From 9221682c0b6f006f6a8387a88b97a6a9d6e41fcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johel=20Ernesto=20Guerrero=20Pe=C3=B1a?= Date: Thu, 3 Aug 2023 00:08:11 -0400 Subject: [PATCH 1/6] feat: allow exporting declarations --- source/cppfront.cpp | 49 +++++++++++++++++++++++++++++++------------ source/parse.h | 51 +++++++++++++++++++++++++++------------------ source/sema.h | 16 ++++++++++++-- 3 files changed, 81 insertions(+), 35 deletions(-) diff --git a/source/cppfront.cpp b/source/cppfront.cpp index fbd7d49e57..070ab8eb22 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 = {}; @@ -1226,7 +1226,7 @@ class cppfront //--------------------------------------------------------------------- // 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()) @@ -1284,7 +1284,7 @@ class cppfront } } - + //--------------------------------------------------------------------- // Do phase1_type_defs_func_decls // @@ -1641,7 +1641,7 @@ class cppfront { add_move = false; } - + if ( emitting_move_that_function && *n.identifier == "that" @@ -2253,7 +2253,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 +2545,7 @@ class cppfront ) -> bool { - if (!fun_node) { + if (!fun_node) { return false; } if (addr_cnt > deref_cnt) { @@ -2572,11 +2572,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 +2753,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 +4817,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 +4860,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 +5016,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 +5476,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/parse.h b/source/parse.h index fb29ea859f..1fdf3861af 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 From 674c862144ba03d5fda22a417cc18039b2b1c6d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johel=20Ernesto=20Guerrero=20Pe=C3=B1a?= Date: Thu, 3 Aug 2023 01:21:08 -0400 Subject: [PATCH 2/6] feat: allow authoring module units --- ...s-clause-unbraced-function-initializer.cpp | 5 +- source/common.h | 32 +-- source/cppfront.cpp | 191 +++++++++++------- source/io.h | 91 +++++++-- 4 files changed, 214 insertions(+), 105 deletions(-) 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 070ab8eb22..2a7ed77af5 100644 --- a/source/cppfront.cpp +++ b/source/cppfront.cpp @@ -1223,6 +1223,70 @@ 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 = [&]() { + 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; + }; //--------------------------------------------------------------------- // Do lowered file prolog @@ -1237,23 +1301,62 @@ 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" ); + 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 printed_module_directive = false; + + 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; + 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 +1370,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()) @@ -1300,66 +1402,15 @@ class cppfront } assert (printer.get_phase() == positional_printer::phase1_type_defs_func_decls); - for ( - lineno_t curr_lineno = 0; - auto const& line : source.get_lines() - ) + 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 {}; } } diff --git a/source/io.h b/source/io.h index 9bb2b31e16..5669f20c37 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; @@ -923,11 +954,23 @@ 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 (!is_module() && starts_with_tokens(lines.back().text, {"module", ";"})) { + lines.back().cat = source_line::category::module_directive; + module_directive_found = true; + } + else if (!is_module() && starts_with_tokens(lines.back().text, {"module"})) { + lines.back().cat = source_line::category::module_declaration; + module_lines = lines.size(); + } + else if (!is_module() && starts_with_tokens(lines.back().text, {"export", "module"})) { + lines.back().cat = source_line::category::module_declaration; + module_lines = lines.size(); + } + else if (starts_with_tokens(lines.back().text, {"import"})) { lines.back().cat = source_line::category::import; } else { @@ -1002,6 +1045,16 @@ class source return lines; } + auto get_non_module_lines() const -> std::span + { + return std::span{lines.begin() + module_lines, lines.end()}; + } + + auto get_module_lines() const -> std::span + { + return std::span{lines.begin(), lines.begin() + module_lines}; + } + //----------------------------------------------------------------------- // debug_print // From 69d1de77ab3df6de68b7854576d32dfe3f817232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johel=20Ernesto=20Guerrero=20Pe=C3=B1a?= Date: Thu, 3 Aug 2023 20:38:02 -0400 Subject: [PATCH 3/6] refactor: various simplifications and clean ups --- include/cpp2util.h | 14 +++++++------- source/io.h | 22 ++++++++++++---------- source/parse.h | 2 +- 3 files changed, 20 insertions(+), 18 deletions(-) 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/source/io.h b/source/io.h index 5669f20c37..354672fd62 100644 --- a/source/io.h +++ b/source/io.h @@ -962,13 +962,15 @@ class source lines.back().cat = source_line::category::module_directive; module_directive_found = true; } - else if (!is_module() && starts_with_tokens(lines.back().text, {"module"})) { - lines.back().cat = source_line::category::module_declaration; - module_lines = lines.size(); - } - else if (!is_module() && starts_with_tokens(lines.back().text, {"export", "module"})) { + else if ( + !is_module() + && ( + 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 = lines.size(); + module_lines = std::ssize(lines); } else if (starts_with_tokens(lines.back().text, {"import"})) { lines.back().cat = source_line::category::import; @@ -1045,14 +1047,14 @@ class source return lines; } - auto get_non_module_lines() const -> std::span + auto get_module_lines() const -> std::span { - return std::span{lines.begin() + module_lines, lines.end()}; + return {lines.begin(), lines.begin() + module_lines}; } - auto get_module_lines() const -> std::span + auto get_non_module_lines() const -> std::span { - return std::span{lines.begin(), lines.begin() + module_lines}; + return {lines.begin() + module_lines, lines.end()}; } //----------------------------------------------------------------------- diff --git a/source/parse.h b/source/parse.h index 1fdf3861af..853b65dada 100644 --- a/source/parse.h +++ b/source/parse.h @@ -2234,7 +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;case accessibility::export_ : return "export"; break;default: assert(a == accessibility::default_); } return "default"; From 9cc78c7e28ba6b21d86f8bc1c5eaa8c39e1f7fd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johel=20Ernesto=20Guerrero=20Pe=C3=B1a?= Date: Fri, 4 Aug 2023 06:44:31 -0400 Subject: [PATCH 4/6] fix: passthrough Cpp1 code with only module lines --- source/cppfront.cpp | 76 +++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/source/cppfront.cpp b/source/cppfront.cpp index 2a7ed77af5..bebd4d2122 100644 --- a/source/cppfront.cpp +++ b/source/cppfront.cpp @@ -1226,6 +1226,21 @@ class cppfront 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" ); @@ -1288,6 +1303,8 @@ class cppfront return true; }; + auto printed_module_directive = false; + //--------------------------------------------------------------------- // Do lowered file prolog // @@ -1301,23 +1318,6 @@ class cppfront printer.print_extra( "#define " + cpp1_FILENAME+"__CPP2" + "\n\n" ); } - 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 printed_module_directive = false; - if (source.is_module()) { if (!source.has_module_directive()) { @@ -1329,31 +1329,33 @@ class cppfront } else { print_cpp2util_prolog(); } + } - // Module lines. - for (auto const& line : source.get_module_lines()) + // 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) { - // Skip dummy line we added to make 0-vs-1-based offsets readable - if (curr_lineno != 0) - { - assert(line.cat != source_line::category::cpp2); + assert(line.cat != source_line::category::cpp2); - if (!emit_cpp1_line(line)) { - return {}; - } + if (!emit_cpp1_line(line)) { + return {}; + } - if ( - !printed_module_directive - && line.cat == source_line::category::module_directive - ) - { - printed_module_directive = true; + 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; } + ++curr_lineno; } auto map_iter = tokens.get_map().cbegin(); @@ -1401,6 +1403,14 @@ class cppfront printer.print_extra( "\n//=== Cpp2 type definitions and function declarations ===========================\n\n" ); } + 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()) { From 7e521eb812ce41f7517fc538dda98e3663d6a0b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johel=20Ernesto=20Guerrero=20Pe=C3=B1a?= Date: Fri, 4 Aug 2023 07:04:34 -0400 Subject: [PATCH 5/6] fix: count import lines as part of module lines --- source/io.h | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/source/io.h b/source/io.h index 354672fd62..bc525857c1 100644 --- a/source/io.h +++ b/source/io.h @@ -958,22 +958,25 @@ class source // else { - if (!is_module() && starts_with_tokens(lines.back().text, {"module", ";"})) { + if (starts_with_tokens(lines.back().text, {"module", ";"})) { lines.back().cat = source_line::category::module_directive; module_directive_found = true; } else if ( - !is_module() - && ( - starts_with_tokens(lines.back().text, {"module"}) - || starts_with_tokens(lines.back().text, {"export", "module"}) - ) + 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_tokens(lines.back().text, {"import"})) { + else if ( + starts_with_tokens(lines.back().text, {"import"}) + || starts_with_tokens(lines.back().text, {"export", "import"}) + ) { lines.back().cat = source_line::category::import; + if (is_module()) { + module_lines = std::ssize(lines); + } } else { auto stats = process_cpp_line( From c1ff963f04399c3cc3cdde6991da62ff15420138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johel=20Ernesto=20Guerrero=20Pe=C3=B1a?= Date: Fri, 4 Aug 2023 20:21:53 -0400 Subject: [PATCH 6/6] fix(parse): recognize import of a module partition --- source/io.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/source/io.h b/source/io.h index bc525857c1..ac129ab6ca 100644 --- a/source/io.h +++ b/source/io.h @@ -913,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 "::" // @@ -920,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; @@ -969,10 +975,7 @@ class source lines.back().cat = source_line::category::module_declaration; module_lines = std::ssize(lines); } - else if ( - starts_with_tokens(lines.back().text, {"import"}) - || starts_with_tokens(lines.back().text, {"export", "import"}) - ) { + else if (starts_with_import(lines.back().text)) { lines.back().cat = source_line::category::import; if (is_module()) { module_lines = std::ssize(lines);