diff --git a/regression-tests/pure2-bugfix-for-default-constructor-this-initializer-1-error.cpp2 b/regression-tests/pure2-bugfix-for-default-constructor-this-initializer-1-error.cpp2 new file mode 100644 index 0000000000..26a5002297 --- /dev/null +++ b/regression-tests/pure2-bugfix-for-default-constructor-this-initializer-1-error.cpp2 @@ -0,0 +1,10 @@ +base: type = { + public x: i32 = -1; + operator=: (out this) = { } + operator=: (out this, y: i32) = x = y; +} +derived: @ struct type = { + y: i32; + this: base = 42; +} +main: () = { } diff --git a/regression-tests/pure2-bugfix-for-default-constructor-this-initializer-2-error.cpp2 b/regression-tests/pure2-bugfix-for-default-constructor-this-initializer-2-error.cpp2 new file mode 100644 index 0000000000..9d83bc4ce0 --- /dev/null +++ b/regression-tests/pure2-bugfix-for-default-constructor-this-initializer-2-error.cpp2 @@ -0,0 +1,10 @@ +base: type = { + public x: i32 = -1; + operator=: (out this) = { } + operator=: (out this, y: i32) = x = y; +} +derived: @ struct type = { + y: i32 = 2; + this: base; +} +main: () = { } diff --git a/regression-tests/pure2-bugfix-for-default-constructor-this-initializer.cpp2 b/regression-tests/pure2-bugfix-for-default-constructor-this-initializer.cpp2 new file mode 100644 index 0000000000..a4bc6be60b --- /dev/null +++ b/regression-tests/pure2-bugfix-for-default-constructor-this-initializer.cpp2 @@ -0,0 +1,19 @@ +base: type = { + public x: i32 = -1; + operator=: (out this) = { } + operator=: (implicit out this, y: i32) = x = y; +} +derived: type = { + y: i32 = 2; + this: base = 42; +} +derived2: @ struct type = { + y: i32; + this: base; + z: i32 = 3; +} +main: () = { + [[assert: derived().y == 2]] + [[assert: derived().x == 42]] + _ = :derived2 = (1, 2, 3); +} diff --git a/regression-tests/test-results/gcc-13/pure2-bugfix-for-default-constructor-this-initializer.cpp.execution b/regression-tests/test-results/gcc-13/pure2-bugfix-for-default-constructor-this-initializer.cpp.execution new file mode 100644 index 0000000000..e69de29bb2 diff --git a/regression-tests/test-results/gcc-13/pure2-bugfix-for-default-constructor-this-initializer.cpp.output b/regression-tests/test-results/gcc-13/pure2-bugfix-for-default-constructor-this-initializer.cpp.output new file mode 100644 index 0000000000..e69de29bb2 diff --git a/regression-tests/test-results/pure2-bugfix-for-default-constructor-this-initializer-1-error.cpp2.output b/regression-tests/test-results/pure2-bugfix-for-default-constructor-this-initializer-1-error.cpp2.output new file mode 100644 index 0000000000..5b92a1deb9 --- /dev/null +++ b/regression-tests/test-results/pure2-bugfix-for-default-constructor-this-initializer-1-error.cpp2.output @@ -0,0 +1,3 @@ +pure2-bugfix-for-default-constructor-this-initializer-1-error.cpp2... +pure2-bugfix-for-default-constructor-this-initializer-1-error.cpp2(8,3): error: '@struct' scope object can have an initializer if it is ordered after all 'this' objects only + diff --git a/regression-tests/test-results/pure2-bugfix-for-default-constructor-this-initializer-2-error.cpp2.output b/regression-tests/test-results/pure2-bugfix-for-default-constructor-this-initializer-2-error.cpp2.output new file mode 100644 index 0000000000..6025805c16 --- /dev/null +++ b/regression-tests/test-results/pure2-bugfix-for-default-constructor-this-initializer-2-error.cpp2.output @@ -0,0 +1,3 @@ +pure2-bugfix-for-default-constructor-this-initializer-2-error.cpp2... +pure2-bugfix-for-default-constructor-this-initializer-2-error.cpp2(7,3): error: '@struct' scope object can have an initializer if it is ordered after all 'this' objects only + diff --git a/regression-tests/test-results/pure2-bugfix-for-default-constructor-this-initializer.cpp b/regression-tests/test-results/pure2-bugfix-for-default-constructor-this-initializer.cpp new file mode 100644 index 0000000000..e68f068904 --- /dev/null +++ b/regression-tests/test-results/pure2-bugfix-for-default-constructor-this-initializer.cpp @@ -0,0 +1,82 @@ + +#define CPP2_USE_MODULES Yes + +//=== Cpp2 type declarations ==================================================== + + +#include "cpp2util.h" + +#line 1 "pure2-bugfix-for-default-constructor-this-initializer.cpp2" +class base; + +#line 6 "pure2-bugfix-for-default-constructor-this-initializer.cpp2" +class derived; + +#line 10 "pure2-bugfix-for-default-constructor-this-initializer.cpp2" +class derived2; + + +//=== Cpp2 type definitions and function declarations =========================== + +#line 1 "pure2-bugfix-for-default-constructor-this-initializer.cpp2" +class base { + public: cpp2::i32 x {-1}; + public: explicit base(); + public: base(cpp2::in y); +#line 4 "pure2-bugfix-for-default-constructor-this-initializer.cpp2" + public: auto operator=(cpp2::in y) -> base& ; + + public: base(base const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(base const&) -> void = delete; +#line 5 "pure2-bugfix-for-default-constructor-this-initializer.cpp2" +}; + +struct derived_y_as_base { cpp2::i32 y; }; +#line 6 "pure2-bugfix-for-default-constructor-this-initializer.cpp2" +class derived: public derived_y_as_base, public base { +public: explicit derived(); + + public: derived(derived const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(derived const&) -> void = delete; + + +#line 9 "pure2-bugfix-for-default-constructor-this-initializer.cpp2" +}; + +struct derived2_y_as_base { cpp2::i32 y; }; +#line 10 "pure2-bugfix-for-default-constructor-this-initializer.cpp2" +class derived2: public derived2_y_as_base, public base { + +#line 13 "pure2-bugfix-for-default-constructor-this-initializer.cpp2" + public: cpp2::i32 z {3}; +}; +auto main() -> int; + + +//=== Cpp2 function definitions ================================================= + + +#line 3 "pure2-bugfix-for-default-constructor-this-initializer.cpp2" + base::base(){} + base::base(cpp2::in y) + : x{ y } +#line 4 "pure2-bugfix-for-default-constructor-this-initializer.cpp2" + { } +#line 4 "pure2-bugfix-for-default-constructor-this-initializer.cpp2" + auto base::operator=(cpp2::in y) -> base& { + x = y; + return *this; +#line 4 "pure2-bugfix-for-default-constructor-this-initializer.cpp2" + } + + derived::derived() + : derived_y_as_base{ 2 } + , base{ 42 }{} + +#line 15 "pure2-bugfix-for-default-constructor-this-initializer.cpp2" +auto main() -> int{ + cpp2::Default.expects(derived().y==2, ""); + cpp2::Default.expects(derived().x==42, ""); + (void) derived2{1, 2, 3}; +} + diff --git a/regression-tests/test-results/pure2-bugfix-for-default-constructor-this-initializer.cpp2.output b/regression-tests/test-results/pure2-bugfix-for-default-constructor-this-initializer.cpp2.output new file mode 100644 index 0000000000..db9eb78972 --- /dev/null +++ b/regression-tests/test-results/pure2-bugfix-for-default-constructor-this-initializer.cpp2.output @@ -0,0 +1,2 @@ +pure2-bugfix-for-default-constructor-this-initializer.cpp2... ok (all Cpp2, passes safety checks) + diff --git a/source/parse.h b/source/parse.h index 26a76181df..772b217232 100644 --- a/source/parse.h +++ b/source/parse.h @@ -721,7 +721,7 @@ struct postfix_expression_node if (ops.empty()) { return false; } else { - return (ops.front().op->type() == lexeme::Ampersand + return (ops.front().op->type() == lexeme::Ampersand || ops.front().op->type() == lexeme::Tilde); } } @@ -2625,6 +2625,51 @@ struct declaration_node } + struct should_add_default_constructor_res{ + bool value; + source_position pos_of_emitted_base_with_initializer; + }; + auto should_add_default_constructor() const + -> should_add_default_constructor_res + { + if ( + !is_type() + || !initializer + || !initializer->is_compound() + ) + { + return {false, {}}; + } + + source_position pos = {}; + bool found_initializer = false; + bool found_initializer_in_base = false; + for (auto& stmt: std::get(initializer->statement)->statements) + { + auto& decl = std::get(stmt->statement); + assert(decl); + if (decl->is_constructor()) { + return {false, {}}; + } + if (!decl->is_object()) { + continue; + } + if (decl->has_initializer()) { + found_initializer = true; + if (!found_initializer_in_base) { + pos = decl->position(); + } + } + assert(decl->name()); + if (decl->has_name("this") && found_initializer) { + found_initializer_in_base = true; + } + } + + return {found_initializer_in_base, pos}; + } + + auto get_decl_if_type_scope_object_name_before_a_base_type( std::string_view s ) const -> declaration_node const* { @@ -3993,7 +4038,7 @@ class parser // || curr().type() == lexeme::LeftBrace ) { - bool inside_initializer = ( + bool inside_initializer = ( peek(-1) && peek(-1)->type() == lexeme::Assignment ); auto open_paren = &curr(); @@ -4015,12 +4060,12 @@ class parser next(); if ( curr().type() != lexeme::Semicolon - && curr().type() != lexeme::RightParen - && curr().type() != lexeme::RightBracket + && curr().type() != lexeme::RightParen + && curr().type() != lexeme::RightBracket && curr().type() != lexeme::Comma ) { expr_list->inside_initializer = false; - } + } n->expr = std::move(expr_list); return n; } @@ -4374,7 +4419,7 @@ class parser //G shift-expression '<<' additive-expression //G shift-expression '>>' additive-expression //G - auto shift_expression(bool allow_angle_operators = true) + auto shift_expression(bool allow_angle_operators = true) -> auto { if (allow_angle_operators) { @@ -4409,7 +4454,7 @@ class parser //G shift-expression //G compare-expression '<=>' shift-expression //G - auto compare_expression(bool allow_angle_operators = true) + auto compare_expression(bool allow_angle_operators = true) -> auto { return binary_expression ( @@ -4425,7 +4470,7 @@ class parser //G relational-expression '<=' compare-expression //G relational-expression '>=' compare-expression //G - auto relational_expression(bool allow_angle_operators = true) + auto relational_expression(bool allow_angle_operators = true) -> auto { if (allow_angle_operators) { @@ -4457,7 +4502,7 @@ class parser //G equality-expression '==' relational-expression //G equality-expression '!=' relational-expression //G - auto equality_expression(bool allow_angle_operators = true) + auto equality_expression(bool allow_angle_operators = true) -> auto { return binary_expression ( @@ -4470,7 +4515,7 @@ class parser //G equality-expression //G bit-and-expression '&' equality-expression //G - auto bit_and_expression(bool allow_angle_operators = true) + auto bit_and_expression(bool allow_angle_operators = true) -> auto { return binary_expression ( @@ -4483,7 +4528,7 @@ class parser //G bit-and-expression //G bit-xor-expression '^' bit-and-expression //G - auto bit_xor_expression(bool allow_angle_operators = true) + auto bit_xor_expression(bool allow_angle_operators = true) -> auto { return binary_expression ( @@ -4496,7 +4541,7 @@ class parser //G bit-xor-expression //G bit-or-expression '|' bit-xor-expression //G - auto bit_or_expression(bool allow_angle_operators = true) + auto bit_or_expression(bool allow_angle_operators = true) -> auto { return binary_expression ( @@ -4509,7 +4554,7 @@ class parser //G bit-or-expression //G logical-and-expression '&&' bit-or-expression //G - auto logical_and_expression(bool allow_angle_operators = true) + auto logical_and_expression(bool allow_angle_operators = true) -> auto { return binary_expression ( @@ -4524,7 +4569,7 @@ class parser //G logical-and-expression //G logical-or-expression '||' logical-and-expression //G - auto logical_or_expression(bool allow_angle_operators = true) + auto logical_or_expression(bool allow_angle_operators = true) -> auto { return binary_expression ( @@ -4842,7 +4887,7 @@ class parser n->open_angle = curr().position(); next(); - + auto term = unqualified_id_node::term{}; do { @@ -6221,6 +6266,8 @@ class parser auto apply_type_meta_functions( declaration_node& decl ) -> bool; + auto apply_internal_type_metafunctions( declaration_node& decl ) + -> bool; //G unnamed-declaration: @@ -6413,7 +6460,7 @@ class parser } assert (n->is_type()); } - + // Or a function type, declaring a function - and tell the function whether it's in a user-defined type else if (auto t = function_type(n.get(), named)) { @@ -6561,11 +6608,11 @@ class parser ) { auto& type = std::get(n->type); - // object initialized by the address of the curr() object + // object initialized by the address of the curr() object if (peek(1)->type() == lexeme::Ampersand) { type->address_of = &curr(); } - // object initialized by (potentially multiple) dereference of the curr() object + // object initialized by (potentially multiple) dereference of the curr() object else if (peek(1)->type() == lexeme::Multiply) { type->dereference_of = &curr(); for (int i = 1; peek(i)->type() == lexeme::Multiply; ++i) @@ -6599,6 +6646,13 @@ class parser ); return {}; } + if (!apply_internal_type_metafunctions(*n)) { + error( + "error encountered while applying internal type metafunctions", + false, {}, true + ); + return {}; + } } if ( @@ -6770,7 +6824,7 @@ class parser return {}; } if ( - t->is_wildcard() + t->is_wildcard() || ( t->get_token() && t->get_token()->to_string(true) == "auto" ) ) { errors.emplace_back( diff --git a/source/reflect.h b/source/reflect.h index 7f9df45c22..488d6e77bf 100644 --- a/source/reflect.h +++ b/source/reflect.h @@ -32,7 +32,7 @@ class object_declaration; #line 357 "reflect.h2" class type_declaration; -#line 818 "reflect.h2" +#line 822 "reflect.h2" } } @@ -402,7 +402,10 @@ public: type_declaration(type_declaration const& that); // auto add_virtual_destructor(meta::type_declaration& t) -> void; -#line 465 "reflect.h2" +#line 464 "reflect.h2" +auto add_default_constructor(meta::type_declaration& t) -> void; + +#line 470 "reflect.h2" //----------------------------------------------------------------------- // // "... an abstract base class defines an interface ..." @@ -417,7 +420,7 @@ auto add_virtual_destructor(meta::type_declaration& t) -> void; // auto interface(meta::type_declaration& t) -> void; -#line 504 "reflect.h2" +#line 509 "reflect.h2" //----------------------------------------------------------------------- // // "C.35: A base class destructor should be either public and @@ -439,7 +442,7 @@ auto interface(meta::type_declaration& t) -> void; // auto polymorphic_base(meta::type_declaration& t) -> void; -#line 548 "reflect.h2" +#line 553 "reflect.h2" //----------------------------------------------------------------------- // // "... A totally ordered type ... requires operator<=> that @@ -465,7 +468,7 @@ auto ordered_impl( cpp2::in ordering// must be "strong_ordering" etc. ) -> void; -#line 593 "reflect.h2" +#line 598 "reflect.h2" //----------------------------------------------------------------------- // ordered - a totally ordered type // @@ -473,19 +476,19 @@ auto ordered_impl( // auto ordered(meta::type_declaration& t) -> void; -#line 603 "reflect.h2" +#line 608 "reflect.h2" //----------------------------------------------------------------------- // weakly_ordered - a weakly ordered type // auto weakly_ordered(meta::type_declaration& t) -> void; -#line 611 "reflect.h2" +#line 616 "reflect.h2" //----------------------------------------------------------------------- // partially_ordered - a partially ordered type // auto partially_ordered(meta::type_declaration& t) -> void; -#line 620 "reflect.h2" +#line 625 "reflect.h2" //----------------------------------------------------------------------- // // "A value is ... a regular type. It must have all public @@ -504,7 +507,7 @@ auto partially_ordered(meta::type_declaration& t) -> void; // auto copyable(meta::type_declaration& t) -> void; -#line 658 "reflect.h2" +#line 663 "reflect.h2" //----------------------------------------------------------------------- // // basic_value @@ -514,11 +517,11 @@ auto copyable(meta::type_declaration& t) -> void; // auto basic_value(meta::type_declaration& t) -> void; -#line 684 "reflect.h2" +#line 688 "reflect.h2" //----------------------------------------------------------------------- // // "A 'value' is a totally ordered basic_value..." -// +// // -- P0707R4, section 3 // // value - a value type that is totally ordered @@ -527,13 +530,13 @@ auto basic_value(meta::type_declaration& t) -> void; // auto value(meta::type_declaration& t) -> void; -#line 700 "reflect.h2" +#line 704 "reflect.h2" auto weakly_ordered_value(meta::type_declaration& t) -> void; -#line 706 "reflect.h2" +#line 710 "reflect.h2" auto partially_ordered_value(meta::type_declaration& t) -> void; -#line 713 "reflect.h2" +#line 717 "reflect.h2" //----------------------------------------------------------------------- // // "By definition, a `struct` is a `class` in which members @@ -561,7 +564,7 @@ auto partially_ordered_value(meta::type_declaration& t) -> void; // auto cpp2_struct(meta::type_declaration& t) -> void; -#line 756 "reflect.h2" +#line 760 "reflect.h2" /* //----------------------------------------------------------------------- // @@ -621,7 +624,7 @@ basic_enum: (inout t: meta::type_declaration) = } */ -#line 816 "reflect.h2" +#line 820 "reflect.h2" //======================================================================= // Switch to Cpp1 and close subnamespace meta } @@ -688,6 +691,33 @@ auto parser::apply_type_meta_functions( declaration_node& n ) return false; } } + + return true; +} + +auto parser::apply_internal_type_metafunctions( declaration_node& n ) + -> bool +{ + assert(n.is_type()); + + // Get the reflection state ready to pass to the function + auto cs = meta::compiler_services{ &errors, generated_tokens }; + auto rtype = meta::type_declaration{ &n, cs }; + + // Ensure the initializers of members lowered as bases + // are used by injecting the default constructor here. + if (auto res = n.should_add_default_constructor(); res.value) { + if (!n.member_function_generation) { + error( + "'@struct' scope object can have an initializer if it is ordered after all 'this' objects only", + false, + res.pos_of_emitted_base_with_initializer + ); + return false; + } + meta::add_default_constructor( rtype ); + } + return true; } @@ -1077,7 +1107,13 @@ auto add_virtual_destructor(meta::type_declaration& t) -> void "could not add virtual destructor"); } -#line 477 "reflect.h2" +auto add_default_constructor(meta::type_declaration& t) -> void +{ + CPP2_UFCS(require, t, CPP2_UFCS(add_member, t, "operator=: (out this) = { }"), + "could not add default constructor"); +} + +#line 482 "reflect.h2" auto interface(meta::type_declaration& t) -> void { auto has_dtor {false}; @@ -1104,7 +1140,7 @@ auto interface(meta::type_declaration& t) -> void } } -#line 523 "reflect.h2" +#line 528 "reflect.h2" auto polymorphic_base(meta::type_declaration& t) -> void { auto has_dtor {false}; @@ -1129,7 +1165,7 @@ auto polymorphic_base(meta::type_declaration& t) -> void } } -#line 568 "reflect.h2" +#line 573 "reflect.h2" auto ordered_impl( meta::type_declaration& t, cpp2::in ordering @@ -1155,25 +1191,25 @@ auto ordered_impl( } } -#line 598 "reflect.h2" +#line 603 "reflect.h2" auto ordered(meta::type_declaration& t) -> void { ordered_impl(t, "strong_ordering"); } -#line 606 "reflect.h2" +#line 611 "reflect.h2" auto weakly_ordered(meta::type_declaration& t) -> void { ordered_impl(t, "weak_ordering"); } -#line 614 "reflect.h2" +#line 619 "reflect.h2" auto partially_ordered(meta::type_declaration& t) -> void { ordered_impl(t, "partial_ordering"); } -#line 636 "reflect.h2" +#line 641 "reflect.h2" auto copyable(meta::type_declaration& t) -> void { // If the user explicitly wrote any of the copy/move functions, @@ -1196,7 +1232,7 @@ auto copyable(meta::type_declaration& t) -> void }} } -#line 665 "reflect.h2" +#line 670 "reflect.h2" auto basic_value(meta::type_declaration& t) -> void { CPP2_UFCS_0(copyable, t); @@ -1211,12 +1247,11 @@ auto basic_value(meta::type_declaration& t) -> void } if (!(std::move(has_default_ctor))) { - CPP2_UFCS(require, t, CPP2_UFCS(add_member, t, "operator=: (out this) = { }"), - "could not add default constructor"); + add_default_constructor(t); } } -#line 694 "reflect.h2" +#line 698 "reflect.h2" auto value(meta::type_declaration& t) -> void { CPP2_UFCS_0(ordered, t); @@ -1235,7 +1270,7 @@ auto partially_ordered_value(meta::type_declaration& t) -> void CPP2_UFCS_0(basic_value, t); } -#line 738 "reflect.h2" +#line 742 "reflect.h2" auto cpp2_struct(meta::type_declaration& t) -> void { for ( auto& m : CPP2_UFCS_0(get_members, t) ) @@ -1253,7 +1288,7 @@ auto cpp2_struct(meta::type_declaration& t) -> void CPP2_UFCS_0(disable_member_function_generation, t); } -#line 818 "reflect.h2" +#line 822 "reflect.h2" } } diff --git a/source/reflect.h2 b/source/reflect.h2 index 24e9aa7ad0..16cd081e64 100644 --- a/source/reflect.h2 +++ b/source/reflect.h2 @@ -68,7 +68,7 @@ compiler_services: @polymorphic_base @copyable type = // First split this string into source_lines // - (copy newline_pos := source.find('\n')) + (copy newline_pos := source.find('\n')) if source.ssize() > 1 && newline_pos != source.npos { @@ -461,6 +461,11 @@ add_virtual_destructor: (inout t: meta::type_declaration) = "could not add virtual destructor"); } +add_default_constructor: (inout t: meta::type_declaration) = +{ + t.require( t.add_member( "operator=: (out this) = { }"), + "could not add default constructor"); +} //----------------------------------------------------------------------- // @@ -676,15 +681,14 @@ basic_value: (inout t: meta::type_declaration) = } if !has_default_ctor { - t.require( t.add_member( "operator=: (out this) = { }"), - "could not add default constructor"); + add_default_constructor(t); } } //----------------------------------------------------------------------- // // "A 'value' is a totally ordered basic_value..." -// +// // -- P0707R4, section 3 // // value - a value type that is totally ordered @@ -879,6 +883,33 @@ auto parser::apply_type_meta_functions( declaration_node& n ) return false; } } + + return true; +} + +auto parser::apply_internal_type_metafunctions( declaration_node& n ) + -> bool +{ + assert(n.is_type()); + + // Get the reflection state ready to pass to the function + auto cs = meta::compiler_services{ &errors, generated_tokens }; + auto rtype = meta::type_declaration{ &n, cs }; + + // Ensure the initializers of members lowered as bases + // are used by injecting the default constructor here. + if (auto res = n.should_add_default_constructor(); res.value) { + if (!n.member_function_generation) { + error( + "'@struct' scope object can have an initializer if it is ordered after all 'this' objects only", + false, + res.pos_of_emitted_base_with_initializer + ); + return false; + } + meta::add_default_constructor( rtype ); + } + return true; }