diff --git a/regression-tests/mixed-function-type-argument.cpp2 b/regression-tests/mixed-function-type-argument.cpp2 new file mode 100644 index 0000000000..dc27e54943 --- /dev/null +++ b/regression-tests/mixed-function-type-argument.cpp2 @@ -0,0 +1,21 @@ +#include +main: () = { + _ = alignof(int); + _ = sizeof(int); + _ = typeid(int); + _ = offsetof(t, a); + _ = : (_: T) _ = sizeof(T::value);(std::true_type()); + _ = : (_: T) _ = sizeof(type T::value_type);(std::true_type()); + _ = alignof(const int); + _ = sizeof(const int); + _ = typeid(const int); + _ = alignof(*int); + _ = sizeof(*int); + _ = typeid(*int); +} +#if defined(_MSC_VER) || defined(__GNUC__) +_: int == __builtin_bit_cast(const int, 0); +#endif +t: @struct type = { + a: int; +} diff --git a/regression-tests/pure2-print.cpp2 b/regression-tests/pure2-print.cpp2 index c757e87ef2..1d6a48ddef 100644 --- a/regression-tests/pure2-print.cpp2 +++ b/regression-tests/pure2-print.cpp2 @@ -100,6 +100,8 @@ outer: @print type = { all: (args...: Args) -> bool = (... && args); + sizeof_dependent_type: () = sizeof(type T::value_type); + } main: () = { diff --git a/regression-tests/test-results/mixed-function-type-argument.cpp b/regression-tests/test-results/mixed-function-type-argument.cpp new file mode 100644 index 0000000000..2ae601d66c --- /dev/null +++ b/regression-tests/test-results/mixed-function-type-argument.cpp @@ -0,0 +1,63 @@ + + +//=== Cpp2 type declarations ==================================================== + + +#include "cpp2util.h" + +#line 1 "mixed-function-type-argument.cpp2" + +#line 19 "mixed-function-type-argument.cpp2" +class t; + + +//=== Cpp2 type definitions and function declarations =========================== + +#line 1 "mixed-function-type-argument.cpp2" +#include +auto main() -> int; +#line 16 "mixed-function-type-argument.cpp2" +#if defined(_MSC_VER) || defined(__GNUC__) +int inline constexpr _{ __builtin_bit_cast(int const, 0) }; +#line 18 "mixed-function-type-argument.cpp2" +#endif +class t { + public: int a; + public: t(auto&& a_) +CPP2_REQUIRES_ (std::is_convertible_v&>) ; + +public: auto operator=(auto&& a_) -> t& +CPP2_REQUIRES_ (std::is_convertible_v&>) ; + +#line 21 "mixed-function-type-argument.cpp2" +}; + + +//=== Cpp2 function definitions ================================================= + +#line 1 "mixed-function-type-argument.cpp2" + +#line 2 "mixed-function-type-argument.cpp2" +auto main() -> int{ + static_cast(alignof(int)); + static_cast(sizeof(int)); + static_cast(typeid(int)); + static_cast(offsetof(t, a)); + static_cast([]([[maybe_unused]] T const& unnamed_param_1) -> auto { return static_cast(sizeof(T::value)); }(std::true_type())); + static_cast([]([[maybe_unused]] T const& unnamed_param_1) -> auto { return static_cast(sizeof(typename T::value_type)); }(std::true_type())); + static_cast(alignof(int const)); + static_cast(sizeof(int const)); + static_cast(typeid(int const)); + static_cast(alignof(int*)); + static_cast(sizeof(int*)); + static_cast(typeid(int*)); +} + +t::t(auto&& a_) +requires (std::is_convertible_v&>) + : a{ CPP2_FORWARD(a_) }{} + +auto t::operator=(auto&& a_) -> t& +requires (std::is_convertible_v&>) { + a = CPP2_FORWARD(a_); + return *this;} diff --git a/regression-tests/test-results/mixed-function-type-argument.cpp2.output b/regression-tests/test-results/mixed-function-type-argument.cpp2.output new file mode 100644 index 0000000000..50af9f9bc1 --- /dev/null +++ b/regression-tests/test-results/mixed-function-type-argument.cpp2.output @@ -0,0 +1,2 @@ +mixed-function-type-argument.cpp2... ok (mixed Cpp1/Cpp2, Cpp2 code passes safety checks) + diff --git a/regression-tests/test-results/pure2-print.cpp b/regression-tests/test-results/pure2-print.cpp index 74103903ed..9dab6be659 100644 --- a/regression-tests/test-results/pure2-print.cpp +++ b/regression-tests/test-results/pure2-print.cpp @@ -70,12 +70,15 @@ CPP2_REQUIRES_ (cpp2::impl::cmp_greater_eq(sizeof...(Args),0u)) ; #line 100 "pure2-print.cpp2" public: template [[nodiscard]] static auto all(Args const& ...args) -> bool; + +#line 103 "pure2-print.cpp2" + public: template static auto sizeof_dependent_type() -> void; public: outer() = default; public: outer(outer const&) = delete; /* No 'that' constructor, suppress copy */ public: auto operator=(outer const&) -> void = delete; -#line 103 "pure2-print.cpp2" +#line 105 "pure2-print.cpp2" }; auto main() -> int; @@ -199,7 +202,10 @@ requires (cpp2::impl::cmp_greater_eq(sizeof...(Args),0u)) { template [[nodiscard]] auto outer::all(Args const& ...args) -> bool { return (... && args); } -#line 105 "pure2-print.cpp2" +#line 103 "pure2-print.cpp2" + template auto outer::sizeof_dependent_type() -> void { sizeof(typename T::value_type); } + +#line 107 "pure2-print.cpp2" auto main() -> int{ outer::test(); } diff --git a/regression-tests/test-results/pure2-print.cpp2.output b/regression-tests/test-results/pure2-print.cpp2.output index d523e2f544..70c723bead 100644 --- a/regression-tests/test-results/pure2-print.cpp2.output +++ b/regression-tests/test-results/pure2-print.cpp2.output @@ -146,6 +146,8 @@ outer:/* @print */ type = } all: (in args...: Args, ) -> move bool = (... && args); + + sizeof_dependent_type: () = sizeof(type T::value_type); } ok (all Cpp2, passes safety checks) diff --git a/source/parse.h b/source/parse.h index 44d45c9d68..c218787056 100644 --- a/source/parse.h +++ b/source/parse.h @@ -694,6 +694,8 @@ auto to_string_view(passing_style pass) -> std::string_view { } +struct type_id_node; + struct expression_list_node { token const* open_paren = {}; @@ -701,7 +703,7 @@ struct expression_list_node bool inside_initializer = false; bool default_initializer = false; - struct term { + struct expression_term { passing_style pass = {}; std::unique_ptr expr; @@ -713,7 +715,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; + + auto visit(auto& v, int depth) -> void; + }; + struct term { + enum active { expression=0, type }; + std::variant< + std::unique_ptr, + std::unique_ptr + > argument; + + auto visit(auto& v, int depth) + -> void + { + v.start(*this, depth); + try_visit(argument, v, depth); + try_visit(argument, v, depth); + v.end(*this, depth); + } + }; + std::vector< term > arguments; // API @@ -721,7 +745,7 @@ struct expression_list_node auto is_empty() const -> bool { - return expressions.empty(); + return arguments.empty(); } auto is_fold_expression() const @@ -730,31 +754,16 @@ 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(&x.argument)) { + ret |= (*expr)->expr->is_fold_expression(); + } } return ret; } auto to_string() const - -> std::string - { - auto ret = std::string{}; - - if (open_paren) { - ret += *open_paren; - } - - for (auto& term : expressions) { - ret += term.expr->to_string(); - } - - if (close_paren) { - ret += *close_paren; - } - - return ret; - } + -> std::string; // Internals @@ -771,7 +780,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); @@ -969,7 +978,7 @@ struct postfix_expression_node std::ssize(ops) >= 1 && ops.front().op->type() == lexeme::LeftParen && ops.front().expr_list - && std::ssize(ops.front().expr_list->expressions) == n + && std::ssize(ops.front().expr_list->arguments) == n ; } @@ -1174,7 +1183,6 @@ auto prefix_expression_node::visit(auto& v, int depth) } -struct type_id_node; struct template_args_tag { }; struct template_argument @@ -1513,6 +1521,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 { @@ -2790,6 +2806,32 @@ auto type_id_node::to_string() const } +auto expression_list_node::to_string() const + -> std::string +{ + auto ret = std::string{}; + + if (open_paren) { + ret += *open_paren; + } + + for (auto& term : arguments) { + if (auto expr = std::get_if(&term.argument)) { + ret += (*expr)->expr->to_string(); + } + else if (auto type = std::get_if(&term.argument)) { + ret += (*type)->type->to_string(); + } + } + + if (close_paren) { + ret += *close_paren; + } + + return ret; +} + + struct type_node { token const* type; @@ -4965,18 +5007,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(&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); + } + else if (auto type = std::get_if(&arg.argument)) + { + assert((*type)->type); + if ((*type)->has_disambiguating_type) + { + ret += "type "; + } + ret += pretty_print_visualize(*(*type)->type, indent); } - ret += pretty_print_visualize(*expr.expr, indent); - if (++i < std::ssize(n.expressions)) { + if (++i < std::ssize(n.arguments)) { ret += ", "; } } @@ -6140,7 +6194,7 @@ class parser } n->expression_list_is_fold_expression = expr_list->is_fold_expression(); expr_list->default_initializer = - is_inside_call_expr && std::empty(expr_list->expressions); + is_inside_call_expr && std::empty(expr_list->arguments); n->expr = std::move(expr_list); return n; @@ -6366,10 +6420,7 @@ class parser //G postfix-expression //G prefix-operator prefix-expression //G 'sizeof' '...' ( identifier ')' - //GTODO 'sizeof' '(' type-id ')' - //GTODO 'alignof' '(' type-id ')' //GTODO await-expression - //GTODO throws-expression //G auto prefix_expression() -> std::unique_ptr @@ -6802,7 +6853,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( @@ -6812,7 +6866,7 @@ class parser ) -> std::unique_ptr { - auto pass = passing_style::in; + auto pass = std::optional(); auto n = std::make_unique(); n->open_paren = open_paren; n->inside_initializer = inside_initializer; @@ -6834,16 +6888,60 @@ class parser } }; - consume_optional_passing_style(); - auto x = expression(); + auto add_expr = [&](std::unique_ptr&& x) { + n->arguments.push_back( + {std::make_unique( + 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(); + 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 this is an empty expression_list, we're done - if (!x) { - return n; + 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' + ) + { + consume_optional_passing_style(); + x = expression(); } + // If this wasn't a valid expression... + if (!x) + { + // and it wasn't a valid type id, we're done + 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(); @@ -6853,13 +6951,30 @@ class parser break; } - consume_optional_passing_style(); - 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' + ) + { + consume_optional_passing_style(); + 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; @@ -9738,14 +9853,23 @@ class parse_tree_printer : printing_visitor << static_cast(n.my_statement) << "]\n"; } - auto start(expression_list_node::term const&n, int indent) -> void + auto start(expression_list_node::term const&, int indent) -> void { o << pre(indent) << "expression-list term\n"; + } + + auto start(expression_list_node::expression_term const&n, int indent) -> void + { if (n.pass == passing_style::out) { - o << pre(indent+1) << "out\n"; + o << pre(indent) << "out\n"; } } + auto start(expression_list_node::type_id_term const&, int indent) -> void + { + o << pre(indent) << "type\n"; + } + auto start(expression_list_node const&, int indent) -> void { o << pre(indent) << "expression-list\n"; diff --git a/source/sema.h b/source/sema.h index d1b14e41b5..695a343a1b 100644 --- a/source/sema.h +++ b/source/sema.h @@ -2355,7 +2355,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); } diff --git a/source/to_cpp1.h b/source/to_cpp1.h index 00ac1cdac0..1ae536a4a0 100644 --- a/source/to_cpp1.h +++ b/source/to_cpp1.h @@ -3260,7 +3260,7 @@ class cppfront auto local_args = text_chunks_with_parens_position{{}, i->op->position(), i->op_close->position()}; assert (i->expr_list); - if (!i->expr_list->expressions.empty()) { + if (!i->expr_list->arguments.empty()) { local_args.text_chunks = print_to_text_chunks(*i->expr_list); } @@ -3451,7 +3451,7 @@ class cppfront if ( flag_safe_subscripts && i->op->type() == lexeme::LeftBracket - && std::ssize(i->expr_list->expressions) == 1 + && std::ssize(i->expr_list->arguments) == 1 ) { suffix.emplace_back( ")", i->op->position() ); @@ -3505,10 +3505,14 @@ class cppfront if ( flag_safe_subscripts && i->op->type() == lexeme::LeftBracket - && std::ssize(i->expr_list->expressions) == 1 + && std::ssize(i->expr_list->arguments) == 1 + && i->expr_list->arguments.front().argument.index() == expression_list_node::term::expression ) { - if (auto lit = i->expr_list->expressions.front().expr->get_literal(); + auto& expr = std::get( + i->expr_list->arguments.front().argument + )->expr; + if (auto lit = expr->get_literal(); lit && lit->get_token()->type() == lexeme::DecimalLiteral ) @@ -4086,47 +4090,60 @@ class cppfront } auto first = true; - for (auto const& x : n.expressions) { + for (auto const& arg : n.arguments) { if (!first) { printer.print_cpp2(", ", n.position()); } first = false; - auto is_out = false; + if (auto x_ = std::get_if(&arg.argument)) + { + auto& x = **x_; + auto is_out = false; - if (x.pass != passing_style::in) { - assert( - x.pass == passing_style::out - || x.pass == passing_style::move - || x.pass == passing_style::forward - ); - if (x.pass == passing_style::out) { - is_out = true; - printer.print_cpp2("cpp2::impl::out(&", n.position()); + if (x.pass != passing_style::in) { + assert( + x.pass == passing_style::out + || x.pass == passing_style::move + || x.pass == passing_style::forward + ); + if (x.pass == passing_style::out) { + is_out = true; + printer.print_cpp2("cpp2::impl::out(&", n.position()); + } + else if (x.pass == passing_style::move) { + printer.print_cpp2("std::move(", n.position()); + } } - else if (x.pass == passing_style::move) { - printer.print_cpp2("std::move(", n.position()); + + if (is_out) { + in_non_rvalue_context.push_back(true); } - } - if (is_out) { - in_non_rvalue_context.push_back(true); - } + assert(x.expr); + current_args.push_back( {x.pass} ); + emit(*x.expr); + current_args.pop_back(); - assert(x.expr); - current_args.push_back( {x.pass} ); - emit(*x.expr); - current_args.pop_back(); + if (is_out) { + in_non_rvalue_context.pop_back(); + } - if (is_out) { - in_non_rvalue_context.pop_back(); + if ( + x.pass == passing_style::move + || x.pass == passing_style::out + ) + { + printer.print_cpp2(")", n.position()); + } } - - if ( - x.pass == passing_style::move - || x.pass == passing_style::out - ) + else if (auto x_ = std::get_if(&arg.argument)) { - printer.print_cpp2(")", n.position()); + auto& x = **x_; + if (x.has_disambiguating_type) { + printer.print_cpp2("typename ", n.position()); + } + assert(x.type); + emit(*x.type); } }