diff --git a/include/cpp2util.h b/include/cpp2util.h index 1768ee2f5c..7e08fc6f12 100644 --- a/include/cpp2util.h +++ b/include/cpp2util.h @@ -191,7 +191,7 @@ // in our -pure-cpp2 "import std;" simulation mode... if you need this, // use mixed mode (not -pure-cpp2) and #include all the headers you need // including this one - // + // // #include #endif @@ -464,7 +464,7 @@ template auto Typeid() -> decltype(auto) { #ifdef CPP2_NO_RTTI Type.expects( - !"'any' dynamic casting is disabled with -fno-rtti", // more likely to appear on console + !"'any' dynamic casting is disabled with -fno-rtti", // more likely to appear on console "'any' dynamic casting is disabled with -fno-rtti" // make message available to hooked handlers ); #else @@ -854,8 +854,8 @@ auto is( X const& ) -> bool { template< typename C, typename X > requires ( - ( std::is_base_of_v || - ( std::is_polymorphic_v && std::is_polymorphic_v) + ( std::is_base_of_v || + ( std::is_polymorphic_v && std::is_polymorphic_v) ) && !std::is_same_v) auto is( X const& x ) -> bool { return Dynamic_cast(&x) != nullptr; @@ -863,8 +863,8 @@ auto is( X const& x ) -> bool { template< typename C, typename X > requires ( - ( std::is_base_of_v || - ( std::is_polymorphic_v && std::is_polymorphic_v) + ( std::is_base_of_v || + ( std::is_polymorphic_v && std::is_polymorphic_v) ) && !std::is_same_v) auto is( X const* x ) -> bool { return Dynamic_cast(x) != nullptr; @@ -1452,7 +1452,7 @@ inline auto to_string(std::string const& s) -> std::string const& template inline auto to_string(T const& sv) -> std::string - requires (std::is_convertible_v + requires (std::is_convertible_v && !std::is_convertible_v) { return std::string{sv}; diff --git a/regression-tests/test-results/pure2-bugfix-for-requires-clause-unbraced-function-initializer.cpp b/regression-tests/test-results/pure2-bugfix-for-requires-clause-unbraced-function-initializer.cpp index 53b7a823dd..2a31fe922d 100644 --- a/regression-tests/test-results/pure2-bugfix-for-requires-clause-unbraced-function-initializer.cpp +++ b/regression-tests/test-results/pure2-bugfix-for-requires-clause-unbraced-function-initializer.cpp @@ -11,7 +11,10 @@ //=== Cpp2 type definitions and function declarations =========================== #line 1 "pure2-bugfix-for-requires-clause-unbraced-function-initializer.cpp2" -template auto f() -> void; +template auto f() -> void +CPP2_REQUIRES (std::regular) +#line 1 "pure2-bugfix-for-requires-clause-unbraced-function-initializer.cpp2" +; auto main() -> int; diff --git a/source/common.h b/source/common.h index a156da34bc..728dbc0164 100644 --- a/source/common.h +++ b/source/common.h @@ -48,7 +48,7 @@ struct source_line { std::string text; - enum class category { empty, preprocessor, comment, import, cpp1, cpp2, rawstring }; + enum class category { empty, preprocessor, comment, module_directive, module_declaration, import, cpp1, cpp2, rawstring }; category cat; bool all_tokens_are_densely_spaced = true; // to be overridden in lexing if they're not @@ -73,13 +73,15 @@ struct source_line -> std::string { switch (cat) { - break;case category::empty: return "/* */ "; - break;case category::preprocessor: return "/* # */ "; - break;case category::comment: return "/* / */ "; - break;case category::import: return "/* i */ "; - break;case category::cpp1: return "/* 1 */ "; - break;case category::cpp2: return "/* 2 */ "; - break;case category::rawstring: return "/* R */ "; + break;case category::empty: return "/* */ "; + break;case category::preprocessor: return "/* # */ "; + break;case category::comment: return "/* / */ "; + break;case category::module_directive: return "/* m#*/ "; + break;case category::module_declaration: return "/* m */ "; + break;case category::import: return "/* i */ "; + break;case category::cpp1: return "/* 1 */ "; + break;case category::cpp2: return "/* 2 */ "; + break;case category::rawstring: return "/* R */ "; break;default: assert(!"illegal category"); abort(); } } @@ -127,7 +129,7 @@ struct string_parts { string_parts(const std::string& beginseq, const std::string& endseq, - adds_sequences strateg) + adds_sequences strateg) : begin_seq{beginseq} , end_seq{endseq} , strategy{strateg} @@ -144,16 +146,16 @@ struct string_parts { void clear() { parts.clear(); } auto generate() const -> std::string { - - if (parts.empty()) { - return (strategy & on_the_beginning ? begin_seq : std::string{}) - + (strategy & on_the_end ? end_seq : std::string{}); + + if (parts.empty()) { + return (strategy & on_the_beginning ? begin_seq : std::string{}) + + (strategy & on_the_end ? end_seq : std::string{}); } - auto result = std::visit(begin_visit{begin_seq, strategy}, + auto result = std::visit(begin_visit{begin_seq, strategy}, parts.front()); - if (std::ssize(parts) > 1) { + if (std::ssize(parts) > 1) { auto it1 = parts.cbegin(); auto it2 = parts.cbegin()+1; for(;it2 != parts.cend(); ++it1, ++it2) { diff --git a/source/cppfront.cpp b/source/cppfront.cpp index fbd7d49e57..bebd4d2122 100644 --- a/source/cppfront.cpp +++ b/source/cppfront.cpp @@ -197,7 +197,7 @@ class positional_printer std::vector const* pcomments = {}; // Cpp2 comments data source const* psource = {}; parser const* pparser = {}; - + source_position curr_pos = {}; // current (line,col) in output lineno_t generated_pos_line = {}; // current line in generated output int last_line_indentation = {}; @@ -1223,10 +1223,91 @@ class cppfront // Generate a reasonable macroized name auto cpp1_FILENAME = to_upper_and_underbar(cpp1_filename); + lineno_t curr_lineno = 0; + auto hpp_includes = std::string{}; + + auto print_cpp2util_prolog = [&]() { + if (flag_use_source_location) { + printer.print_extra( "#define CPP2_USE_SOURCE_LOCATION Yes\n" ); + } + if (flag_cpp2_only) { + printer.print_extra( "#define CPP2_USE_MODULES Yes\n" ); + } + if (flag_no_exceptions) { + printer.print_extra( "#define CPP2_NO_EXCEPTIONS Yes\n" ); + } + if (flag_no_rtti) { + printer.print_extra( "#define CPP2_NO_RTTI Yes\n" ); + } + }; + + auto print_cpp2util = [&]() { + if (!tokens.get_map().empty()) { + printer.print_extra( "\n#include \"cpp2util.h\"\n\n" ); + } + }; + + auto emit_cpp1_line = [&](auto const& line) { + if ( + source.has_cpp2() + && line.cat == source_line::category::empty + ) + { + ++ret.cpp2_lines; + } + else + { + ++ret.cpp1_lines; + } + + if ( + flag_cpp2_only + && !line.text.empty() + && line.cat != source_line::category::comment + && line.cat != source_line::category::module_directive + && line.cat != source_line::category::module_declaration + && line.cat != source_line::category::import + ) + { + if (line.cat == source_line::category::preprocessor) { + if (!line.text.ends_with(".h2\"")) { + errors.emplace_back( + source_position(curr_lineno, 1), + "pure-cpp2 switch disables the preprocessor, including #include (except of .h2 files) - use import instead (note: 'import std;' is implicit in -pure-cpp2)" + ); + return false; + } + } + else { + errors.emplace_back( + source_position(curr_lineno, 1), + "pure-cpp2 switch disables Cpp1 syntax" + ); + return false; + } + } + + if ( + line.cat == source_line::category::preprocessor + && line.text.ends_with(".h2\"") + ) + { + // Strip off the 2" + auto h_include = line.text.substr(0, line.text.size()-2); + printer.print_cpp1( h_include + "\"", curr_lineno ); + hpp_includes += h_include + "pp\"\n"; + } + else { + printer.print_cpp1( line.text, curr_lineno ); + } + return true; + }; + + auto printed_module_directive = false; //--------------------------------------------------------------------- // Do lowered file prolog - // + // // Only emit extra lines if we actually have Cpp2, because // we want pure-Cpp1 files to pass through with zero changes if (source.has_cpp2()) @@ -1237,23 +1318,47 @@ class cppfront printer.print_extra( "#define " + cpp1_FILENAME+"__CPP2" + "\n\n" ); } - if (flag_use_source_location) { - printer.print_extra( "#define CPP2_USE_SOURCE_LOCATION Yes\n" ); - } - if (flag_cpp2_only) { - printer.print_extra( "#define CPP2_USE_MODULES Yes\n" ); - } - if (flag_no_exceptions) { - printer.print_extra( "#define CPP2_NO_EXCEPTIONS Yes\n" ); + if (source.is_module()) { + if (!source.has_module_directive()) + { + printer.print_extra( "module;\n" ); + printed_module_directive = true; + print_cpp2util_prolog(); + print_cpp2util(); + } + } else { + print_cpp2util_prolog(); } - if (flag_no_rtti) { - printer.print_extra( "#define CPP2_NO_RTTI Yes\n" ); + } + + // Module lines. + for (auto const& line : source.get_module_lines()) + { + // Skip dummy line we added to make 0-vs-1-based offsets readable + if (curr_lineno != 0) + { + assert(line.cat != source_line::category::cpp2); + + if (!emit_cpp1_line(line)) { + return {}; + } + + if ( + !printed_module_directive + && line.cat == source_line::category::module_directive + ) + { + printed_module_directive = true; + if (source.has_cpp2()) { + print_cpp2util_prolog(); + print_cpp2util(); + } + } } + ++curr_lineno; } auto map_iter = tokens.get_map().cbegin(); - auto hpp_includes = std::string{}; - //--------------------------------------------------------------------- // Do phase0_type_decls @@ -1267,9 +1372,8 @@ class cppfront printer.print_extra( "\n//=== Cpp2 type declarations ====================================================\n\n" ); } - if (!tokens.get_map().empty()) - { - printer.print_extra( "\n#include \"cpp2util.h\"\n\n" ); + if (!source.is_module()) { + print_cpp2util(); } for (auto& section : tokens.get_map()) @@ -1284,7 +1388,7 @@ class cppfront } } - + //--------------------------------------------------------------------- // Do phase1_type_defs_func_decls // @@ -1299,67 +1403,24 @@ class cppfront printer.print_extra( "\n//=== Cpp2 type definitions and function declarations ===========================\n\n" ); } - assert (printer.get_phase() == positional_printer::phase1_type_defs_func_decls); - for ( - lineno_t curr_lineno = 0; - auto const& line : source.get_lines() + if ( + !source.has_cpp2() + && source.is_module() ) + { + curr_lineno -= std::ssize(source.get_module_lines()) - 1; + } + + assert (printer.get_phase() == positional_printer::phase1_type_defs_func_decls); + for (auto const& line : source.get_non_module_lines()) { // Skip dummy line we added to make 0-vs-1-based offsets readable if (curr_lineno != 0) { // If it's a Cpp1 line, emit it - if (line.cat != source_line::category::cpp2) - { - if ( - source.has_cpp2() - && line.cat == source_line::category::empty - ) - { - ++ret.cpp2_lines; - } - else - { - ++ret.cpp1_lines; - } - - if ( - flag_cpp2_only - && !line.text.empty() - && line.cat != source_line::category::comment - && line.cat != source_line::category::import - ) - { - if (line.cat == source_line::category::preprocessor) { - if (!line.text.ends_with(".h2\"")) { - errors.emplace_back( - source_position(curr_lineno, 1), - "pure-cpp2 switch disables the preprocessor, including #include (except of .h2 files) - use import instead (note: 'import std;' is implicit in -pure-cpp2)" - ); - return {}; - } - } - else { - errors.emplace_back( - source_position(curr_lineno, 1), - "pure-cpp2 switch disables Cpp1 syntax" - ); - return {}; - } - } - - if ( - line.cat == source_line::category::preprocessor - && line.text.ends_with(".h2\"") - ) - { - // Strip off the 2" - auto h_include = line.text.substr(0, line.text.size()-2); - printer.print_cpp1( h_include + "\"", curr_lineno ); - hpp_includes += h_include + "pp\"\n"; - } - else { - printer.print_cpp1( line.text, curr_lineno ); + if (line.cat != source_line::category::cpp2) { + if (!emit_cpp1_line(line)) { + return {}; } } @@ -1641,7 +1702,7 @@ class cppfront { add_move = false; } - + if ( emitting_move_that_function && *n.identifier == "that" @@ -2253,7 +2314,7 @@ class cppfront return; } else if ( is_literal(tok->type()) || n.expression->expr->is_result_a_temporary_variable() - ) + ) { errors.emplace_back( n.position(), @@ -2545,7 +2606,7 @@ class cppfront ) -> bool { - if (!fun_node) { + if (!fun_node) { return false; } if (addr_cnt > deref_cnt) { @@ -2572,11 +2633,11 @@ class cppfront ) -> bool { - if (!type_id_node) { + if (!type_id_node) { return false; } if (addr_cnt > deref_cnt) { - return true; + return true; } if ( type_id_node->dereference_of ) { @@ -2753,7 +2814,7 @@ class cppfront { auto& unqual = std::get(id->id); assert(unqual); - // TODO: Generalize this: + // TODO: Generalize this: // - we don't recognize pointer types from Cpp1 // - we don't deduce pointer types from parameter_declaration_list_node if ( is_pointer_declaration(unqual->identifier) ) { @@ -4817,7 +4878,7 @@ class cppfront } // If this is a generated declaration (negative source line number), - // add a line break before + // add a line break before if ( printer.get_phase() == printer.phase2_func_defs && n.position().lineno < 1 @@ -4860,6 +4921,8 @@ class cppfront else { printer.print_cpp2("public: ", n.position()); } + } else if (n.is_export()) { + printer.print_cpp2(to_string(n.access) + " ", n.position()); } // Emit template parameters if any @@ -5014,11 +5077,32 @@ class cppfront } } + // Emit export on forward declaration. + if ( + n.is_export() + && n.parent_is_namespace() + ) + { + if ( + printer.get_phase() == printer.phase0_type_decls + || ( + printer.get_phase() == printer.phase1_type_defs_func_decls + && !n.is_type() // Done in phase 0. + ) + ) + { + printer.print_cpp2(to_string(n.access) + " ", n.position()); + } + } // In class definitions, emit the explicit access specifier if there // is one, or default to private for data and public for functions - if (printer.get_phase() == printer.phase1_type_defs_func_decls) + else if (printer.get_phase() == printer.phase1_type_defs_func_decls) { - if (!n.is_default_access()) { + if ( + !n.is_default_access() + && !n.is_export() + ) + { assert (is_in_type); printer.print_cpp2(to_string(n.access) + ": ", n.position()); } @@ -5453,7 +5537,7 @@ class cppfront // A2) This is '(out this, move that)' // and no '(inout this, move that)' was written by the user // (*) and no '(inout this, that)' was written by the user (*) - // + // // (*) This third test is to tie-break M2 and A2 in favor of M2. Both M2 and A2 // can generate a missing '(inout this, move that)', and if we have both // options then we should prefer to use M2 (generate move assignment from diff --git a/source/io.h b/source/io.h index 9bb2b31e16..ac129ab6ca 100644 --- a/source/io.h +++ b/source/io.h @@ -23,6 +23,8 @@ #include #include #include +#include +#include namespace cpp2 { @@ -107,29 +109,35 @@ auto is_preprocessor( //--------------------------------------------------------------------------- -// starts_with_import: returns whether the line starts with "import" +// starts_with_tokens: returns whether the line starts with the tokens // // line current line being processed // -auto starts_with_import(std::string const& line) +auto starts_with_tokens(std::string const& line, std::initializer_list const tokens) -> bool { auto i = 0; - // find first non-whitespace character - if (!move_next(line, i, isspace)) { - return false; - } + for (auto token: tokens) { + // find first non-whitespace character + if (!move_next(line, i, isspace)) { + return false; + } - static constexpr auto import_keyword = std::string_view{"import"}; + // now must begin with the token + if (!std::string_view(line).substr(i).starts_with(token)) { + return false; + } - // the first token must begin with 'import' - if (!std::string_view(line).substr(i).starts_with(import_keyword)) { - return false; + // and not be immediately followed by an _identifier-continue_ + if (is_identifier_continue(line[i + token.size()])) { + return false; + } + + i += token.size(); } - // and not be immediately followed by an _identifier-continue_ - return !is_identifier_continue(line[i + import_keyword.size()]); + return true; } @@ -301,6 +309,9 @@ auto starts_with_identifier_colon(std::string const& line) else if (s.starts_with("private")) { j += 7; } + else if (s.starts_with("export")) { + j += 6; + } while ( j < std::ssize(s) && isspace(s[j]) @@ -623,7 +634,7 @@ auto process_cpp_line( } } } - + break;case '\"': // If this isn't an escaped quote, toggle string literal state if ( @@ -772,8 +783,10 @@ class source { std::vector& errors; std::vector lines; - bool cpp1_found = false; - bool cpp2_found = false; + std::ptrdiff_t module_lines = 0; + bool module_directive_found = false; + bool cpp1_found = false; + bool cpp2_found = false; static const int max_line_len = 90'000; // do not reduce this - I encountered an 80,556-char @@ -796,9 +809,27 @@ class source } + //----------------------------------------------------------------------- + // is_module: Returns true if this file is a module unit + // (note: module, export, and import lines don't count toward Cpp1 or Cpp2) + // + auto is_module() const -> bool { + return module_lines != 0; + } + + + //----------------------------------------------------------------------- + // has_module_directive: Returns true if this file has a module directive + // (note: module, export, and import lines don't count toward Cpp1 or Cpp2) + // + auto has_module_directive() const -> bool { + return module_directive_found; + } + + //----------------------------------------------------------------------- // has_cpp1: Returns true if this file has some Cpp1/preprocessor lines - // (note: import lines don't count toward Cpp1 or Cpp2) + // (note: module, export, and import lines don't count toward Cpp1 or Cpp2) // auto has_cpp1() const -> bool { return cpp1_found; @@ -807,7 +838,7 @@ class source //----------------------------------------------------------------------- // has_cpp2: Returns true if this file has some Cpp2 lines - // (note: import lines don't count toward Cpp1 or Cpp2) + // (note: module, export, and import lines don't count toward Cpp1 or Cpp2) // auto has_cpp2() const -> bool { return cpp2_found; @@ -882,6 +913,11 @@ class source { lines.push_back({ &buf[0], source_line::category::cpp1 }); + auto starts_with_import = [&](auto& text) { + return starts_with_tokens(text, {"import"}) + || starts_with_tokens(text, {"export", "import"}); + }; + // Switch to cpp2 mode if we're not in a comment, not inside nested { }, // and the line starts with "nonwhitespace :" but not "::" // @@ -889,6 +925,7 @@ class source && !in_raw_string_literal && braces.current_depth() < 1 && starts_with_identifier_colon(lines.back().text) + && !starts_with_import(lines.back().text) // For a _module-partition_. ) { cpp2_found= true; @@ -923,12 +960,26 @@ class source } } - // Else still in Cpp1 code, but could be a comment, empty, or import + // Else still in Cpp1 code, but could be a comment, empty, module, export, or import // else { - if (starts_with_import(lines.back().text)) { + if (starts_with_tokens(lines.back().text, {"module", ";"})) { + lines.back().cat = source_line::category::module_directive; + module_directive_found = true; + } + else if ( + starts_with_tokens(lines.back().text, {"module"}) + || starts_with_tokens(lines.back().text, {"export", "module"}) + ) { + lines.back().cat = source_line::category::module_declaration; + module_lines = std::ssize(lines); + } + else if (starts_with_import(lines.back().text)) { lines.back().cat = source_line::category::import; + if (is_module()) { + module_lines = std::ssize(lines); + } } else { auto stats = process_cpp_line( @@ -1002,6 +1053,16 @@ class source return lines; } + auto get_module_lines() const -> std::span + { + return {lines.begin(), lines.begin() + module_lines}; + } + + auto get_non_module_lines() const -> std::span + { + return {lines.begin() + module_lines, lines.end()}; + } + //----------------------------------------------------------------------- // debug_print // diff --git a/source/parse.h b/source/parse.h index fb29ea859f..853b65dada 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); } } @@ -2225,7 +2225,7 @@ struct alias_node }; -enum class accessibility { default_ = 0, public_, protected_, private_ }; +enum class accessibility { default_ = 0, public_, protected_, private_, export_ }; auto to_string(accessibility a) -> std::string @@ -2234,6 +2234,7 @@ auto to_string(accessibility a) break;case accessibility::public_ : return "public"; break;case accessibility::protected_: return "protected"; break;case accessibility::private_ : return "private"; + break;case accessibility::export_ : return "export"; break;default: assert(a == accessibility::default_); } return "default"; @@ -2358,6 +2359,12 @@ struct declaration_node return access == accessibility::private_; } + auto is_export() const + -> bool + { + return access == accessibility::export_; + } + auto is_default_access() const -> bool { @@ -3993,7 +4000,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 +4022,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 +4381,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 +4416,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 +4432,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 +4464,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 +4477,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 +4490,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 +4503,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 +4516,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 +4531,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 +4849,7 @@ class parser n->open_angle = curr().position(); next(); - + auto term = unqualified_id_node::term{}; do { @@ -6413,7 +6420,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 +6568,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) @@ -6770,7 +6777,7 @@ class parser return {}; } if ( - t->is_wildcard() + t->is_wildcard() || ( t->get_token() && t->get_token()->to_string(true) == "auto" ) ) { errors.emplace_back( @@ -6878,6 +6885,10 @@ class parser access = accessibility::private_; next(); } + else if (curr() == "export") { + access = accessibility::export_; + next(); + } // If they wrote an access-specifier, see if they put a ':' // after it out of Cpp1 habit (there's no colon in Cpp2) diff --git a/source/sema.h b/source/sema.h index cfaf589582..0f9b6e907e 100644 --- a/source/sema.h +++ b/source/sema.h @@ -1216,7 +1216,19 @@ class sema } if ( - n.access != accessibility::default_ + n.is_export() + && !n.parent_is_namespace() + ) + { + errors.emplace_back( + n.position(), + "an export declaration is only allowed at namespace scope" + ); + return false; + } + else if ( + !n.is_export() + && !n.is_default_access() && !n.parent_is_type() ) { @@ -1564,7 +1576,7 @@ class sema // Skip type scope (member) variables && !(n.parent_is_type() && n.is_object()) // Skip unnamed variables - && n.identifier + && n.identifier // Skip non-out parameters && ( !inside_parameter_list