Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: accept function type argument #923

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions regression-tests/mixed-function-type-argument.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include <cstddef>
main: () = {
_ = alignof(int);
_ = sizeof(int);
_ = typeid(int);
_ = offsetof(t, a);
_ = :<T> (_: T) _ = sizeof(T::value);(std::true_type());
_ = :<T> (_: T) _ = sizeof(type T::value_type);(std::true_type());
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there intended to be a difference between lines 7 and 8? Or is the type intended to be optional?

If there's no difference, can you show a case where it's required?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The second one needs the disambiguating type.
See https://cpp1.godbolt.org/z/PY53n6Kvf (generated from https://cpp2.godbolt.org/z/KK15vf6Y5):

  • Clang: error: dependent-name 'T::value_type' is parsed as a non-type, but instantiation yields a type.
  • GCC: error: missing 'typename' prior to dependent type name 'integral_constant<bool, true>::value_type'.
  • MSVC accepts.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

D'oh, now I see it. I missed the _type suffix that changed the meaning, sorry.

_ = alignof(const int);
_ = sizeof(const int);
_ = typeid(const int);
_ = alignof(*int);
_ = sizeof(*int);
_ = typeid(*int);
}
t: @struct type = {
a: int;
}
2 changes: 1 addition & 1 deletion regression-tests/pure2-print.cpp2
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ testing_enabled: bool = false;

outer: @print type = {

object_alias: <T> T requires true == 42;
object_alias: <T> T requires true == sizeof(type T::value_type);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this just to exercise type for this pretty-print self-test, or is type now intended to be required here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The former.
This change is intended to exercise the type keyword in the pretty-print.


mytype: final type =
{
Expand Down
45 changes: 45 additions & 0 deletions regression-tests/test-results/mixed-function-type-argument.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@


//=== Cpp2 type declarations ====================================================


#include "cpp2util.h"

#line 1 "mixed-function-type-argument.cpp2"

#line 16 "mixed-function-type-argument.cpp2"
class t;


//=== Cpp2 type definitions and function declarations ===========================

#line 1 "mixed-function-type-argument.cpp2"
#include <cstddef>
auto main() -> int;

#line 16 "mixed-function-type-argument.cpp2"
class t {
public: int a;
};


//=== Cpp2 function definitions =================================================

#line 1 "mixed-function-type-argument.cpp2"

#line 2 "mixed-function-type-argument.cpp2"
auto main() -> int{
static_cast<void>(alignof(int));
static_cast<void>(sizeof(int));
static_cast<void>(typeid(int));
static_cast<void>(offsetof(t, a));
static_cast<void>([]<typename T>([[maybe_unused]] T const& unnamed_param_1) mutable -> auto { return static_cast<void>(sizeof(T::value)); }(std::true_type()));
static_cast<void>([]<typename T>([[maybe_unused]] T const& unnamed_param_1) mutable -> auto { return static_cast<void>(sizeof(typename T::value_type)); }(std::true_type()));
static_cast<void>(alignof(int const));
static_cast<void>(sizeof(int const));
static_cast<void>(typeid(int const));
static_cast<void>(alignof(int*));
static_cast<void>(sizeof(int*));
static_cast<void>(typeid(int*));
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mixed-function-type-argument.cpp2... ok (mixed Cpp1/Cpp2, Cpp2 code passes safety checks)

2 changes: 1 addition & 1 deletion regression-tests/test-results/pure2-print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ bool testing_enabled {false};

#line 8 "pure2-print.cpp2"
template<typename T>
requires (true) inline CPP2_CONSTEXPR T outer::object_alias = 42;
requires (true) inline CPP2_CONSTEXPR T outer::object_alias = sizeof(typename T::value_type);
#line 9 "pure2-print.cpp2"

#line 12 "pure2-print.cpp2"
Expand Down
2 changes: 1 addition & 1 deletion regression-tests/test-results/pure2-print.cpp2.output
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ pure2-print.cpp2...

outer:/* @print */ type =
{
object_alias: <T: type> T requires (true) == 42;
object_alias: <T: type> T requires (true) == sizeof(type T::value_type);
mytype: final type =
{
protected f:() -> move int = 42;
Expand Down
166 changes: 134 additions & 32 deletions source/parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -619,13 +619,15 @@ auto to_string_view(passing_style pass) -> std::string_view {
}


struct type_id_node;

struct expression_list_node
{
token const* open_paren = {};
token const* close_paren = {};
bool inside_initializer = false;

struct term {
struct expression_term {
passing_style pass = {};
std::unique_ptr<expression_node> expr;

Expand All @@ -637,7 +639,29 @@ struct expression_list_node
v.end(*this, depth);
}
};
std::vector< term > expressions;
struct type_id_term {
bool has_disambiguating_type = {};
std::unique_ptr<type_id_node> type;

auto visit(auto& v, int depth) -> void;
};
struct term {
enum active { expression=0, type };
std::variant<
std::unique_ptr<expression_term>,
std::unique_ptr<type_id_term>
> argument;

auto visit(auto& v, int depth)
-> void
{
v.start(*this, depth);
try_visit<expression>(argument, v, depth);
try_visit<type >(argument, v, depth);
v.end(*this, depth);
}
};
std::vector< term > arguments;


// API
Expand All @@ -648,8 +672,10 @@ struct expression_list_node
// This is a fold-expression if any subexpression
// has an identifier named "..."
auto ret = false;
for (auto& x : expressions) {
ret |= x.expr->is_fold_expression();
for (auto& x : arguments) {
if (auto expr = std::get_if<term::expression>(&x.argument)) {
ret |= (*expr)->expr->is_fold_expression();
}
}
return ret;
}
Expand All @@ -669,7 +695,7 @@ struct expression_list_node
-> void
{
v.start(*this, depth);
for (auto& x : expressions) {
for (auto& x : arguments) {
x.visit(v, depth+1);
}
v.end(*this, depth);
Expand Down Expand Up @@ -998,7 +1024,6 @@ auto prefix_expression_node::visit(auto& v, int depth)
}


struct type_id_node;
struct template_args_tag { };

struct template_argument
Expand Down Expand Up @@ -1320,6 +1345,14 @@ auto template_argument::to_string() const
return {};
}

auto expression_list_node::type_id_term::visit(auto& v, int depth) -> void
{
v.start(*this, depth);
assert(type);
type->visit(v, depth+1);
v.end(*this, depth);
}


struct is_as_expression_node
{
Expand Down Expand Up @@ -4423,18 +4456,30 @@ auto pretty_print_visualize(expression_list_node const& n, int indent)

auto ret = n.open_paren->to_string();

for (auto i = 0; auto& expr : n.expressions) {
assert(expr.expr);
if (
expr.pass == passing_style::out
|| expr.pass == passing_style::move
|| expr.pass == passing_style::forward
)
for (auto i = 0; auto& arg : n.arguments) {
if (auto expr = std::get_if<expression_list_node::term::expression>(&arg.argument))
{
ret += to_string_view(expr.pass) + std::string{" "};
assert((*expr)->expr);
if (
(*expr)->pass == passing_style::out
|| (*expr)->pass == passing_style::move
|| (*expr)->pass == passing_style::forward
)
{
ret += to_string_view((*expr)->pass) + std::string{" "};
}
ret += pretty_print_visualize(*(*expr)->expr, indent);
}
ret += pretty_print_visualize(*expr.expr, indent);
if (++i < std::ssize(n.expressions)) {
else if (auto type = std::get_if<expression_list_node::term::type>(&arg.argument))
{
assert((*type)->type);
if ((*type)->has_disambiguating_type)
{
ret += "type ";
}
ret += pretty_print_visualize(*(*type)->type, indent);
}
if (++i < std::ssize(n.arguments)) {
ret += ", ";
}
}
Expand Down Expand Up @@ -4962,7 +5007,7 @@ auto pretty_print_visualize(declaration_node const& n, int indent, bool include_

auto metafunctions = std::string{};
{
auto as_comment =
auto as_comment =
!n.metafunctions.empty()
&& !include_metafunctions_list;
if (as_comment) {
Expand Down Expand Up @@ -5800,9 +5845,6 @@ class parser
//G postfix-expression
//G prefix-operator prefix-expression
//GTODO await-expression
//GTODO 'sizeof' '(' type-id ')'
//GTODO 'sizeof' '...' ( identifier ')'
//GTODO 'alignof' '(' type-id ')'
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, these should go anyway as they already work without a special grammar. And throws-expression too, since throw(...) works (and I'm using that in reflect.h2's report_violation function).

//GTODO throws-expression
//G
auto prefix_expression()
Expand All @@ -5825,8 +5867,6 @@ class parser
error("prefix '++var' is not valid Cpp2; use postfix 'var++' instead", false);
break; case lexeme::MinusMinus:
error("prefix '--var' is not valid Cpp2; use postfix 'var--' instead", false);
break; case lexeme::Multiply:
error("prefix '*ptr' dereference is not valid Cpp2; use postfix 'ptr*' instead", false);
JohelEGP marked this conversation as resolved.
Show resolved Hide resolved
break; case lexeme::Ampersand:
error("prefix '&var' address-of is not valid Cpp2; use postfix 'var&' instead", false);
break; case lexeme::Tilde:
Expand Down Expand Up @@ -6207,7 +6247,10 @@ class parser
}

//G expression-list:
//G 'type' type-id
//G 'const' type-id
//G parameter-direction? expression
//G type-id
//G expression-list ',' parameter-direction? expression
//G
auto expression_list(
Expand All @@ -6216,11 +6259,39 @@ class parser
)
-> std::unique_ptr<expression_list_node>
{
auto pass = passing_style::in;
auto pass = std::optional<passing_style>();
auto n = std::make_unique<expression_list_node>();
n->open_paren = open_paren;
n->inside_initializer = inside_initializer;

auto add_expr = [&](std::unique_ptr<expression_node>&& x) {
n->arguments.push_back(
{std::make_unique<expression_list_node::expression_term>(
expression_list_node::expression_term{
pass.value_or(passing_style::in),
std::move(x)
}
)}
);
pass.reset();
};
auto parses_type_id = [&]() -> bool {
if (pass.has_value()) {
return false;
}
auto res = std::make_unique<expression_list_node::type_id_term>();
next((res->has_disambiguating_type = curr() == "type"));
if ((res->type = type_id()))
{
n->arguments.push_back( {std::move(res)} );
return true;
}
if (curr().type() == lexeme::Multiply) {
error("prefix '*ptr' dereference is not valid Cpp2; use postfix 'ptr*' instead", false);
}
return false;
};

if (auto dir = to_passing_style(curr());
(
dir == passing_style::out
Expand All @@ -6234,15 +6305,29 @@ class parser
pass = dir;
next();
}
auto x = expression();
decltype(expression()) x = {};
// If it doesn't start with * or const (which can only be a type id),
// try parsing it as an expression
if (
curr().type() != lexeme::Multiply // '*'
&& curr() != "const" // 'const'
&& curr() != "type" // 'type'
)
{
x = expression();
}

// If this is an empty expression_list, we're done
if (!x) {
return n;
if (!x)
{
if (!parses_type_id()) {
return n;
}
}

// Otherwise remember the first expression
n->expressions.push_back( { pass, std::move(x) } );
else {
add_expr( std::move(x) );
}
// and see if there are more...
while (curr().type() == lexeme::Comma) {
next();
Expand All @@ -6256,12 +6341,29 @@ class parser
pass = dir;
next();
}
auto expr = expression();
if (!expr) {
decltype(expression()) expr = {};
// If it doesn't start with * or const (which can only be a type id),
// try parsing it as an expression
if (
curr().type() != lexeme::Multiply // '*'
&& curr() != "const" // 'const'
&& curr() != "type" // 'type'
)
{
expr = expression();
}
if (
!expr
&& !parses_type_id()
)
{
error("invalid text in expression list", true, {}, true);
return {};
}
n->expressions.push_back( { pass, std::move(expr) } );
else
{
add_expr( std::move(expr) );
}
}
return n;
}
Expand Down Expand Up @@ -8996,7 +9098,7 @@ class parse_tree_printer : printing_visitor
<< static_cast<void const*>(n.my_statement) << "]\n";
}

auto start(expression_list_node::term const&n, int indent) -> void
auto start(expression_list_node::expression_term const&n, int indent) -> void
JohelEGP marked this conversation as resolved.
Show resolved Hide resolved
{
o << pre(indent) << "expression-list term\n";
if (n.pass == passing_style::out) {
Expand Down
2 changes: 1 addition & 1 deletion source/sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -1690,7 +1690,7 @@ class sema
inside_out_parameter = {};
}

auto start(expression_list_node::term const&n, int) -> void
auto start(expression_list_node::expression_term const&n, int) -> void
{
is_out_expression = (n.pass == passing_style::out);
}
Expand Down
Loading