diff --git a/regression-tests/mixed-lifetime-safety-and-null-contracts.cpp2 b/regression-tests/mixed-lifetime-safety-and-null-contracts.cpp2 index af4149a2d9..454f504753 100644 --- a/regression-tests/mixed-lifetime-safety-and-null-contracts.cpp2 +++ b/regression-tests/mixed-lifetime-safety-and-null-contracts.cpp2 @@ -28,7 +28,7 @@ auto call_my_framework(const char* msg CPP2_SOURCE_LOCATION_PARAM) { std::cout << "from source location: " << loc - << "]\n"; + << "\n"; } exit(0); } diff --git a/regression-tests/test-results/apple-clang-15-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution index b0347698ed..5afc4974e7 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution @@ -1,2 +1,2 @@ sending error to my framework... [dynamic null dereference attempt detected] -from source location: ../../../include/cpp2util.h(673) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = int *&]] +from source location: ../../../include/cpp2util.h(669) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = int *&] diff --git a/regression-tests/test-results/clang-15-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution b/regression-tests/test-results/clang-15-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution index b0347698ed..5afc4974e7 100644 --- a/regression-tests/test-results/clang-15-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution @@ -1,2 +1,2 @@ sending error to my framework... [dynamic null dereference attempt detected] -from source location: ../../../include/cpp2util.h(673) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = int *&]] +from source location: ../../../include/cpp2util.h(669) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = int *&] diff --git a/regression-tests/test-results/clang-18-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution b/regression-tests/test-results/clang-18-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution index 4ec0f5de65..09c49d5ddd 100644 --- a/regression-tests/test-results/clang-18-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution +++ b/regression-tests/test-results/clang-18-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution @@ -1,2 +1,2 @@ sending error to my framework... [dynamic null dereference attempt detected] -from source location: mixed-lifetime-safety-and-null-contracts.cpp2(17) void try_pointer_stuff()] +from source location: mixed-lifetime-safety-and-null-contracts.cpp2(17) void try_pointer_stuff() diff --git a/regression-tests/test-results/clang-18-c++23-libcpp/mixed-lifetime-safety-and-null-contracts.cpp.execution b/regression-tests/test-results/clang-18-c++23-libcpp/mixed-lifetime-safety-and-null-contracts.cpp.execution index 4ec0f5de65..09c49d5ddd 100644 --- a/regression-tests/test-results/clang-18-c++23-libcpp/mixed-lifetime-safety-and-null-contracts.cpp.execution +++ b/regression-tests/test-results/clang-18-c++23-libcpp/mixed-lifetime-safety-and-null-contracts.cpp.execution @@ -1,2 +1,2 @@ sending error to my framework... [dynamic null dereference attempt detected] -from source location: mixed-lifetime-safety-and-null-contracts.cpp2(17) void try_pointer_stuff()] +from source location: mixed-lifetime-safety-and-null-contracts.cpp2(17) void try_pointer_stuff() diff --git a/regression-tests/test-results/gcc-13-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution b/regression-tests/test-results/gcc-13-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution index 4ec0f5de65..09c49d5ddd 100644 --- a/regression-tests/test-results/gcc-13-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution +++ b/regression-tests/test-results/gcc-13-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution @@ -1,2 +1,2 @@ sending error to my framework... [dynamic null dereference attempt detected] -from source location: mixed-lifetime-safety-and-null-contracts.cpp2(17) void try_pointer_stuff()] +from source location: mixed-lifetime-safety-and-null-contracts.cpp2(17) void try_pointer_stuff() diff --git a/regression-tests/test-results/gcc-14-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution b/regression-tests/test-results/gcc-14-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution index 4ec0f5de65..09c49d5ddd 100644 --- a/regression-tests/test-results/gcc-14-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution +++ b/regression-tests/test-results/gcc-14-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution @@ -1,2 +1,2 @@ sending error to my framework... [dynamic null dereference attempt detected] -from source location: mixed-lifetime-safety-and-null-contracts.cpp2(17) void try_pointer_stuff()] +from source location: mixed-lifetime-safety-and-null-contracts.cpp2(17) void try_pointer_stuff() diff --git a/regression-tests/test-results/mixed-lifetime-safety-and-null-contracts.cpp b/regression-tests/test-results/mixed-lifetime-safety-and-null-contracts.cpp index e31f9ca9d8..49f00ac401 100644 --- a/regression-tests/test-results/mixed-lifetime-safety-and-null-contracts.cpp +++ b/regression-tests/test-results/mixed-lifetime-safety-and-null-contracts.cpp @@ -36,7 +36,7 @@ auto call_my_framework(const char* msg CPP2_SOURCE_LOCATION_PARAM) { std::cout << "from source location: " << loc - << "]\n"; + << "\n"; } exit(0); } diff --git a/regression-tests/test-results/msvc-2022-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution b/regression-tests/test-results/msvc-2022-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution index 762c9ea2c4..3d0df16892 100644 --- a/regression-tests/test-results/msvc-2022-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution +++ b/regression-tests/test-results/msvc-2022-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution @@ -1,2 +1,2 @@ sending error to my framework... [dynamic null dereference attempt detected] -from source location: mixed-lifetime-safety-and-null-contracts.cpp2(17) void __cdecl try_pointer_stuff(void)] +from source location: mixed-lifetime-safety-and-null-contracts.cpp2(17) void __cdecl try_pointer_stuff(void) diff --git a/regression-tests/test-results/msvc-2022-c++latest/mixed-lifetime-safety-and-null-contracts.cpp.execution b/regression-tests/test-results/msvc-2022-c++latest/mixed-lifetime-safety-and-null-contracts.cpp.execution index 762c9ea2c4..3d0df16892 100644 --- a/regression-tests/test-results/msvc-2022-c++latest/mixed-lifetime-safety-and-null-contracts.cpp.execution +++ b/regression-tests/test-results/msvc-2022-c++latest/mixed-lifetime-safety-and-null-contracts.cpp.execution @@ -1,2 +1,2 @@ sending error to my framework... [dynamic null dereference attempt detected] -from source location: mixed-lifetime-safety-and-null-contracts.cpp2(17) void __cdecl try_pointer_stuff(void)] +from source location: mixed-lifetime-safety-and-null-contracts.cpp2(17) void __cdecl try_pointer_stuff(void) diff --git a/regression-tests/test-results/pure2-return-tuple-no-identifier-error.cpp2.output b/regression-tests/test-results/pure2-return-tuple-no-identifier-error.cpp2.output index 09e4156404..1be0e56bf7 100644 --- a/regression-tests/test-results/pure2-return-tuple-no-identifier-error.cpp2.output +++ b/regression-tests/test-results/pure2-return-tuple-no-identifier-error.cpp2.output @@ -1,4 +1,4 @@ pure2-return-tuple-no-identifier-error.cpp2... pure2-return-tuple-no-identifier-error.cpp2(1,11): error: expected identifier, not 'int' -pure2-return-tuple-no-identifier-error.cpp2(1,16): error: expected identifier, not 'int' +pure2-return-tuple-no-identifier-error.cpp2(1,14): error: missing function return after -> (at ',') diff --git a/regression-tests/test-results/pure2-return-tuple-no-type-error.cpp2.output b/regression-tests/test-results/pure2-return-tuple-no-type-error.cpp2.output index fb5541abbe..4593cd24b5 100644 --- a/regression-tests/test-results/pure2-return-tuple-no-type-error.cpp2.output +++ b/regression-tests/test-results/pure2-return-tuple-no-type-error.cpp2.output @@ -1,4 +1,4 @@ pure2-return-tuple-no-type-error.cpp2... pure2-return-tuple-no-type-error.cpp2(1,11): error: return parameter 'a' must have a type -pure2-return-tuple-no-type-error.cpp2(1,14): error: return parameter 'b' must have a type +pure2-return-tuple-no-type-error.cpp2(1,12): error: missing function return after -> (at ',') diff --git a/regression-tests/test-results/version b/regression-tests/test-results/version index 4de8ceb0bd..a4467cbd0d 100644 --- a/regression-tests/test-results/version +++ b/regression-tests/test-results/version @@ -1,5 +1,5 @@ -cppfront compiler v0.7.1 Build 9714:0930 +cppfront compiler v0.7.1 Build 9718:1445 Copyright(c) Herb Sutter All rights reserved SPDX-License-Identifier: CC-BY-NC-ND-4.0 diff --git a/source/build.info b/source/build.info index 6840219ea7..80816d5920 100644 --- a/source/build.info +++ b/source/build.info @@ -1 +1 @@ -"9714:0930" \ No newline at end of file +"9718:1445" \ No newline at end of file diff --git a/source/common.h b/source/common.h index 9af809d704..b9b41c1bf3 100644 --- a/source/common.h +++ b/source/common.h @@ -669,6 +669,18 @@ class cmdline_processor } public: + auto flags_starting_with(std::string_view s) + -> std::vector + { + auto ret = std::vector{}; + for (auto const& f : flags) { + if (f.name.starts_with(s)) { + ret.push_back(f.name); + } + } + return ret; + } + auto process_flags() -> void { diff --git a/source/cppfront.cpp b/source/cppfront.cpp index 0c89b6c538..f53b48dcf9 100644 --- a/source/cppfront.cpp +++ b/source/cppfront.cpp @@ -61,6 +61,24 @@ auto main( int exit_status = EXIT_SUCCESS; for (auto const& arg : cmdline.arguments()) { + if ( + arg.text.starts_with("-") + || arg.text.starts_with("/") + ) + { + auto ambiguous = cmdline.flags_starting_with(arg.text.substr(1)); + if (ambiguous.empty()) { + std::cerr << arg.text << " - unknown compiler flag name (try " << arg.text.front() << "help)\n"; + } + else { + std::cerr << arg.text << " - ambiguous compiler flag name, did you mean one of these?\n"; + for (auto a : ambiguous) { + std::cerr << " " << arg.text.front() << a << "\n"; + } + } + return EXIT_FAILURE; + } + cpp2::timer t; t.start(); diff --git a/source/io.h b/source/io.h index 35933d9172..d7b71e1879 100644 --- a/source/io.h +++ b/source/io.h @@ -461,20 +461,30 @@ class braces_tracker // --- Preprocessor matching functions - #if/#else/#endif // Entering an #if - auto found_pre_if() -> void { - assert(std::ssize(preprocessor) > 0); + auto found_pre_if(lineno_t) -> void { preprocessor.push_back({}); } // Encountered an #else - auto found_pre_else() -> void { - assert(std::ssize(preprocessor) > 1); + auto found_pre_else(lineno_t lineno) -> void { + if (std::ssize(preprocessor) < 2) { + errors.emplace_back( + lineno, + "#else does not match a prior #if" + ); + } + preprocessor.back().found_preprocessor_else(); } // Exiting an #endif - auto found_pre_endif() -> void { - assert(std::ssize(preprocessor) > 1); + auto found_pre_endif(lineno_t lineno) -> void { + if (std::ssize(preprocessor) < 2) { + errors.emplace_back( + lineno, + "#endif does not match a prior #if" + ); + } // If the #if/#else/#endif introduced the same net number of braces, // then we will have recorded that number too many open braces, and @@ -779,6 +789,7 @@ auto process_cpp2_line( " after the closing ; or } of a definition, the rest" " of the line cannot begin a /*...*/ comment") ); + return false; } } @@ -807,6 +818,7 @@ auto process_cpp2_line( source_position(lineno, unsafe_narrow(ssize(line))), std::string("line ended before character literal was terminated") ); + return false; } return found_end; @@ -896,11 +908,11 @@ class source { switch (pre) { break;case preprocessor_conditional::pre_if: - braces.found_pre_if(); + braces.found_pre_if( cpp2::unsafe_narrow(std::ssize(lines)) ); break;case preprocessor_conditional::pre_else: - braces.found_pre_else(); + braces.found_pre_else( cpp2::unsafe_narrow(std::ssize(lines)) ); break;case preprocessor_conditional::pre_endif: - braces.found_pre_endif(); + braces.found_pre_endif( cpp2::unsafe_narrow(std::ssize(lines)) ); break;default: assert(false); } diff --git a/source/lex.h b/source/lex.h index 4e9c3fcded..348633b465 100644 --- a/source/lex.h +++ b/source/lex.h @@ -751,6 +751,8 @@ auto lex_line( " short, ushort, int, uint, long, ulong, longlong, ulonglong, longdouble, _schar, _uchar\n" " - see also cpp2util.h > \"Convenience names for integer types\"" ); + + return; } tokens.push_back(last_token); @@ -1002,6 +1004,7 @@ auto lex_line( "invalid universal character name - \\u without { must" " be followed by 4 hexadecimal digits" ); + return 0; } else if ( @@ -1022,6 +1025,7 @@ auto lex_line( if (peek(j + offset) == '}') { ++j; + return j; } else { errors.emplace_back( @@ -1029,8 +1033,8 @@ auto lex_line( "invalid universal character name - \\u{ must" " be followed by hexadecimal digits and a closing }" ); + return 0; } - return j; } else if ( @@ -1052,7 +1056,9 @@ auto lex_line( "invalid universal character name - \\U must" " be followed by 8 hexadecimal digits" ); + return 0; } + return 0; }; @@ -1352,7 +1358,7 @@ auto lex_line( } else { store(1, lexeme::Less); } - // Note: >> and >>= are not source tokens, they are synthesized from > > and > >= where legal + // Note: >= and >> and >>= synthesized from > = and > > and > >= where legal //G '>>=' '>>' '>=' '>' break;case '>': //--------------------------------------------------------- @@ -1362,10 +1368,10 @@ auto lex_line( // if (peek2 == '=') { store(3, lexeme::RightShiftEq); } // else { store(2, lexeme::RightShift); } //} + //else if (peek1 == '=') { store(2, lexeme::GreaterEq); } //else //--------------------------------------------------------- - if (peek1 == '=') { store(2, lexeme::GreaterEq); } - else { store(1, lexeme::Greater); } + { store(1, lexeme::Greater); } //G '++' '+=' '+' break;case '+': @@ -1535,6 +1541,7 @@ auto lex_line( "invalid new-line in raw string delimiter \"" + line.substr(i, 3) + "\" - stray 'R' in program \"" ); + return {}; } } else { store(1, lexeme::Dollar); @@ -1579,6 +1586,7 @@ auto lex_line( source_position(lineno, i), "binary literal cannot be empty (0B must be followed by binary digits)" ); + return {}; } } else if (peek1 == 'x' || peek1 == 'X') { @@ -1592,6 +1600,7 @@ auto lex_line( source_position(lineno, i), "hexadecimal literal cannot be empty (0X must be followed by hexadecimal digits)" ); + return {}; } } } @@ -1658,6 +1667,7 @@ auto lex_line( source_position(lineno, i), "a floating point literal must have at least one digit after the decimal point (can be '.0')" ); + return {}; } while (is_separator_or(is_digit,peek(j))) { ++j; @@ -1725,6 +1735,7 @@ auto lex_line( "invalid new-line in raw string delimiter \"" + line.substr(i, j) + "\" - stray 'R' in program \"" ); + return {}; } } else { @@ -1735,6 +1746,7 @@ auto lex_line( "string literal \"" + line.substr(i+1, j) + "\" is missing its closing \"" ); + return {}; } // At this point we have a string-literal, but it may contain @@ -1782,6 +1794,7 @@ auto lex_line( "character literal '" + line.substr(i+1, j-1) + "' is missing its closing '" ); + return {}; } store(j+1, lexeme::CharacterLiteral); } @@ -1790,6 +1803,7 @@ auto lex_line( source_position(lineno, i), "character literal is empty" ); + return {}; } } @@ -1815,18 +1829,21 @@ auto lex_line( source_position(lineno, i), "'const_cast' is not supported in Cpp2 - the current C++ best practice is to never cast away const, and that is const_cast's only effective use" ); + return {}; } if (tokens.back() == "static_cast") { errors.emplace_back( source_position(lineno, i), "'static_cast(val)' is not supported in Cpp2 - use 'val as T' for safe conversions instead, or if necessary cpp2::unsafe_narrow(val) for a possibly-lossy narrowing conversion" ); + return {}; } if (tokens.back() == "dynamic_cast") { errors.emplace_back( source_position(lineno, i), "'dynamic_cast(pBase)' is not supported in Cpp2 - use 'pBase as *Derived' for safe dynamic conversions instead" ); + return {}; } } @@ -1853,12 +1870,14 @@ auto lex_line( source_position(lineno, i), "'NULL' is not supported in Cpp2 - for a local pointer variable, leave it uninitialized instead, and set it to a non-null value when you have one" ); + return {}; } if (tokens.back() == "delete") { errors.emplace_back( source_position(lineno, i), "'delete' and owning raw pointers are not supported in Cpp2 - use unique.new or shared.new instead in that order (or, in the future, gc.new, but that is not yet implemented)" ); + return {}; } } } @@ -1872,6 +1891,7 @@ auto lex_line( false, true // a noisy fallback error message ); + return {}; } } } diff --git a/source/parse.h b/source/parse.h index d5367ad9be..e7bd2b20e4 100644 --- a/source/parse.h +++ b/source/parse.h @@ -930,6 +930,17 @@ struct postfix_expression_node return ops.empty() && expr->is_id_expression(); } + auto starts_with_function_call_with_n_parameters(int n) const + -> bool + { + return + std::ssize(ops) >= 1 + && ops.front().op->type() == lexeme::LeftParen + && ops.front().expr_list + && std::ssize(ops.front().expr_list->expressions) == n + ; + } + auto is_unqualified_id() const -> bool { @@ -5827,12 +5838,10 @@ class parser auto expr_list = expression_list(open_paren, lexeme::RightParen, inside_initializer); if (!expr_list) { error("unexpected text - ( is not followed by an expression-list"); - next(); return {}; } if (curr().type() != close_paren_type(open_paren->type())) { error("unexpected text - expression-list is not terminated by " + close_text); - next(); return {}; } expr_list->close_paren = &curr(); @@ -5863,14 +5872,12 @@ class parser if (auto obj = std::get_if(&decl->type)) { if ((*obj)->is_wildcard()) { error("an unnamed object at expression scope currently cannot have a deduced type (the reason to create an unnamed object is typically to create a temporary of a named type)"); - next(); return {}; } } else if (auto func = std::get_if(&decl->type)) { if ((*func)->returns.index() == function_type_node::list) { error("an unnamed function at expression scope currently cannot return multiple values"); - next(); return {}; } if ( // check if a single-expression function is followed by an extra second semicolon @@ -5881,13 +5888,11 @@ class parser } if (!(*func)->contracts.empty()) { error("an unnamed function at expression scope currently cannot have contracts"); - next(); return {}; } } else { error("(temporary alpha limitation) an unnamed declaration at expression scope must be a function or an object"); - next(); return {}; } @@ -6157,6 +6162,26 @@ class parser next(); } + // And for assignment-expression we may synthesize >>= from > > = + // which will return a token* == a valid operator for this production + // (possibly a synthesized new token) or nullptr otherwise + else if constexpr (requires{ validate_op(curr(), *peek(1), *peek(2)); }) { + if ( + peek(1) == nullptr + || peek(2) == nullptr + || (t.op = validate_op(curr(), *peek(1), *peek(2))) == nullptr + ) + { + break; + } + // If we didn't consume the next token, we consumed the next three + if (t.op != &curr()) { + next(); + next(); + } + next(); + } + // And it shouldn't be anything else else { assert (!"ICE: validate_op should take one token and return bool, or two tokens and return token const* "); @@ -6269,12 +6294,20 @@ class parser { if (allow_angle_operators) { return binary_expression ( - [](token const& t, token const& next) -> token const* { + [this](token const& t, token const& next) -> token const* { + if ( + t.type() == lexeme::Greater + && next.type() == lexeme::Assignment + && t.position() == source_position{ next.position().lineno, next.position().colno - 1 } + ) + { + generated_tokens->emplace_back(">=", t.position(), lexeme::GreaterEq); + return &generated_tokens->back(); + } if ( t.type() == lexeme::Less || t.type() == lexeme::LessEq - || (t.type() == lexeme::Greater && next.type() != lexeme::GreaterEq) - || t.type() == lexeme::GreaterEq + || t.type() == lexeme::Greater ) { return &t; } @@ -6394,13 +6427,14 @@ class parser if (allow_angle_operators) { ret = binary_expression ( - [this](token const& t, token const& next) -> token const* { + [this](token const& t, token const& next, token const& third) -> token const* { if (is_assignment_operator(t.type())) { return &t; } if ( t.type() == lexeme::Greater - && next.type() == lexeme::GreaterEq + && next.type() == lexeme::Greater + && third.type() == lexeme::Assignment && t.position() == source_position{ next.position().lineno, next.position().colno-1 } ) { @@ -6553,6 +6587,9 @@ class parser { auto n = std::make_unique(); + // Remember current position, because we need to look ahead + auto start_pos = pos; + while ( (curr().type() == lexeme::Keyword && curr() == "const") || curr().type() == lexeme::Multiply @@ -6586,6 +6623,7 @@ class parser } if (curr().type() == lexeme::Multiply) { error("'T*' is not a valid Cpp2 type; use '*T' for a pointer instead", false); + pos = start_pos; // backtrack return {}; } @@ -6847,11 +6885,14 @@ class parser // Reject "std" :: "move" / "forward" assert (term.id->identifier); auto first_uid_was_std = (*term.id->identifier == "std"); - auto first_time_through_loop = true; n->ids.push_back( std::move(term) ); - while (curr().type() == lexeme::Scope) + for ( + auto first_time_through_loop = true; + curr().type() == lexeme::Scope; + first_time_through_loop = false + ) { auto term = qualified_id_node::term{ &curr() }; next(); @@ -6865,17 +6906,11 @@ class parser first_time_through_loop && first_uid_was_std && term.scope_op->type() == lexeme::Scope - ) + && *term.id->identifier == "forward" + ) { - if (*term.id->identifier == "move") { - error("std::move is not needed in Cpp2 - use 'move' parameters/arguments instead", false); - return {}; - } - else if (*term.id->identifier == "forward") { - error("std::forward is not needed in Cpp2 - use 'forward' parameters/arguments instead", false); - return {}; - } - first_time_through_loop = false; + error("std::forward is not needed in Cpp2 - use 'forward' parameters/arguments instead", false); + return {}; } n->ids.push_back( std::move(term) ); } @@ -7116,7 +7151,6 @@ class parser // Final semicolon if (curr().type() != lexeme::Semicolon) { error("missing ; after return"); - next(); return {}; } @@ -7235,7 +7269,6 @@ class parser if (!handle_logical_expression ()) { return {}; } if (curr().type() != lexeme::Semicolon) { error("missing ; after do..while loop condition"); - next(); return {}; } next(); @@ -7254,6 +7287,7 @@ class parser if (curr().type() == lexeme::Comma) { error("iterating over multiple ranges at once is not currently supported"); + return {}; } if (!handle_optional_next_clause()) { return {}; } @@ -7685,7 +7719,6 @@ class parser && peek(1)->type() == lexeme::Comma ) { - next(); error("declaring multiple names at once is not currently supported"); } return {}; @@ -7723,9 +7756,6 @@ class parser n->body_indent = peek(1)->position().colno-1; } - // Remember current position, in case this isn't a valid statement - auto start_pos = pos; - // In the case where this is a declaration initializer with // = { // on the same line, we want to remember our start position @@ -7754,11 +7784,10 @@ class parser auto s = statement(true, source_position{}, true, n.get()); if (!s) { - // Only add error when no specific one already exist + // Only add a general error when no specific one already exists if(!has_error()) { error("invalid statement encountered inside a compound-statement", true); } - pos = start_pos; // backtrack return {}; } n->statements.push_back( std::move(s) ); @@ -7873,12 +7902,18 @@ class parser // And some error checks // + if (n->declaration->is_function()) { + error("a parameter cannot be a function", false); + return {}; + } + if ( n->mod != parameter_declaration_node::modifier::none && !n->declaration->has_name("this") ) { error( "only a 'this' parameter may be declared implicit, virtual, override, or final", false ); + return {}; } if ( @@ -7890,6 +7925,7 @@ class parser ) { error( "a 'this' parameter must be in, inout, out, or move", false ); + return {}; } if ( @@ -7899,6 +7935,7 @@ class parser ) { error( "a 'that' parameter must be in or move", false ); + return {}; } // The only parameter type that could be const-qualified is a 'copy' parameter, because @@ -7943,10 +7980,12 @@ class parser if (tok->type() != lexeme::Identifier) { error("expected identifier, not '" + tok->to_string() + "'", false, tok->position()); + return {}; } else if (n->declaration->has_wildcard_type()) { error("return parameter '" + tok->to_string() + "' must have a type", false, tok->position()); + return {}; } } return n; @@ -8004,6 +8043,7 @@ class parser ) { error("'that' may not be followed by any additional parameters", false); + return {}; } n->parameters.push_back( std::move(param) ); @@ -8237,6 +8277,7 @@ class parser ) { error("only 'forward' and 'move' return passing style are allowed from functions"); + return {}; } next(); if (auto t = type_id()) { @@ -8247,6 +8288,7 @@ class parser auto msg = std::string("'"); msg += to_string_view(pass); error(msg + "' must be followed by a type-id"); + return {}; } } @@ -8450,6 +8492,7 @@ class parser if (curr() == "union") { error("unsafe 'union' is not supported in Cpp2 - write '@union' to apply Cpp2's safe 'union' type metafunction instead, or use std::variant"); + return {}; } // Next is an optional metafunctions clause @@ -8580,6 +8623,14 @@ class parser } } + else { + // Only add a general error when no specific one already exists + if (!has_error()) { + error("syntax error - unknown declaration"); + } + return {}; + } + // If we've already validated that this is a function where the parameter // list is followed by a valid expression-statement, parse that again // (requiring a semicolon as we validated when determining terse_no_equals) @@ -8650,6 +8701,7 @@ class parser // Otherwise, diagnose an error else { error("unexpected semicolon after declaration", {}, {}, {}); + return {}; } } // Otherwise if there isn't one and it was required, diagnose an error @@ -8672,6 +8724,7 @@ class parser if (curr().type() == lexeme::EqualComparison) { if (!n->is_function()) { error("syntax error at '==' - did you mean '='?"); + return {}; } n->is_constexpr = true; } @@ -8735,7 +8788,6 @@ class parser "ill-formed initializer", true, {}, true ); - next(); return {}; } } @@ -8761,7 +8813,7 @@ class parser if (at_a_statement) { error("in this scope, a single-statement function body cannot be immediately followed by a statement - did you forget to put { } braces around a multi-statement function body?", false); - return n; + return {}; } } @@ -9136,7 +9188,7 @@ class parser curr().position(), "a variadic declaration must have a name - did you forget to write a name before '...'?" ); - pos = start_pos; // backtrack + return {}; } auto is_variadic = false; diff --git a/source/sema.h b/source/sema.h index 2f8a0f1587..27169fee00 100644 --- a/source/sema.h +++ b/source/sema.h @@ -1511,6 +1511,18 @@ class sema } } + if ( + n.expr->to_string() == "std::move" + && n.starts_with_function_call_with_n_parameters(1) + ) + { + errors.emplace_back( + n.position(), + "std::move(one_argument) is not needed in Cpp2 - use 'move' parameters/arguments instead" + ); + return false; + } + return true; }