From 6031b28cb04ba1e667008f4ad48bc85c6046fa58 Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Sat, 22 Apr 2023 11:30:53 -1000 Subject: [PATCH] Allow `else if`, closes #366 Also, reject `=` assignment expressions in `if` conditions. They're already disallowed in loop conditions, and it's an oversight that they were not already disallowed in `if` conditions... the `if` condition is supposed to take *logical-or-expression* (the conditional expression production) like the loop conditions do. --- ...e2-initialization-safety-with-else-if.cpp2 | 25 ++++++++ ...lization-safety-with-else-if.cpp.execution | 1 + ...tialization-safety-with-else-if.cpp.output | 0 ...lization-safety-with-else-if.cpp.execution | 1 + ...tialization-safety-with-else-if.cpp.output | 0 ...lization-safety-with-else-if.cpp.execution | 1 + ...tialization-safety-with-else-if.cpp.output | 1 + ...re2-initialization-safety-with-else-if.cpp | 47 ++++++++++++++ ...ialization-safety-with-else-if.cpp2.output | 2 + source/parse.h | 64 +++++++++++++------ 10 files changed, 123 insertions(+), 19 deletions(-) create mode 100644 regression-tests/pure2-initialization-safety-with-else-if.cpp2 create mode 100644 regression-tests/test-results/clang-12/pure2-initialization-safety-with-else-if.cpp.execution create mode 100644 regression-tests/test-results/clang-12/pure2-initialization-safety-with-else-if.cpp.output create mode 100644 regression-tests/test-results/gcc-10/pure2-initialization-safety-with-else-if.cpp.execution create mode 100644 regression-tests/test-results/gcc-10/pure2-initialization-safety-with-else-if.cpp.output create mode 100644 regression-tests/test-results/msvc-2022/pure2-initialization-safety-with-else-if.cpp.execution create mode 100644 regression-tests/test-results/msvc-2022/pure2-initialization-safety-with-else-if.cpp.output create mode 100644 regression-tests/test-results/pure2-initialization-safety-with-else-if.cpp create mode 100644 regression-tests/test-results/pure2-initialization-safety-with-else-if.cpp2.output diff --git a/regression-tests/pure2-initialization-safety-with-else-if.cpp2 b/regression-tests/pure2-initialization-safety-with-else-if.cpp2 new file mode 100644 index 0000000000..20db09f3c5 --- /dev/null +++ b/regression-tests/pure2-initialization-safety-with-else-if.cpp2 @@ -0,0 +1,25 @@ +main: (args) = { + p : *int; + + a := 1; + b := 2; + c := 3; + d := 4; + + if args.size() == 3 { + p = a&; + } else if true { + if args.size() == 2 { + p = c&; + } else if b > 0 { + p = a&; + } + else { + p = d&; + } + } else { + p = c&; + } + + std::cout << p* << std::endl; +} diff --git a/regression-tests/test-results/clang-12/pure2-initialization-safety-with-else-if.cpp.execution b/regression-tests/test-results/clang-12/pure2-initialization-safety-with-else-if.cpp.execution new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/regression-tests/test-results/clang-12/pure2-initialization-safety-with-else-if.cpp.execution @@ -0,0 +1 @@ +1 diff --git a/regression-tests/test-results/clang-12/pure2-initialization-safety-with-else-if.cpp.output b/regression-tests/test-results/clang-12/pure2-initialization-safety-with-else-if.cpp.output new file mode 100644 index 0000000000..e69de29bb2 diff --git a/regression-tests/test-results/gcc-10/pure2-initialization-safety-with-else-if.cpp.execution b/regression-tests/test-results/gcc-10/pure2-initialization-safety-with-else-if.cpp.execution new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/regression-tests/test-results/gcc-10/pure2-initialization-safety-with-else-if.cpp.execution @@ -0,0 +1 @@ +1 diff --git a/regression-tests/test-results/gcc-10/pure2-initialization-safety-with-else-if.cpp.output b/regression-tests/test-results/gcc-10/pure2-initialization-safety-with-else-if.cpp.output new file mode 100644 index 0000000000..e69de29bb2 diff --git a/regression-tests/test-results/msvc-2022/pure2-initialization-safety-with-else-if.cpp.execution b/regression-tests/test-results/msvc-2022/pure2-initialization-safety-with-else-if.cpp.execution new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/regression-tests/test-results/msvc-2022/pure2-initialization-safety-with-else-if.cpp.execution @@ -0,0 +1 @@ +1 diff --git a/regression-tests/test-results/msvc-2022/pure2-initialization-safety-with-else-if.cpp.output b/regression-tests/test-results/msvc-2022/pure2-initialization-safety-with-else-if.cpp.output new file mode 100644 index 0000000000..67638a3f21 --- /dev/null +++ b/regression-tests/test-results/msvc-2022/pure2-initialization-safety-with-else-if.cpp.output @@ -0,0 +1 @@ +pure2-initialization-safety-with-else-if.cpp diff --git a/regression-tests/test-results/pure2-initialization-safety-with-else-if.cpp b/regression-tests/test-results/pure2-initialization-safety-with-else-if.cpp new file mode 100644 index 0000000000..406dafcf16 --- /dev/null +++ b/regression-tests/test-results/pure2-initialization-safety-with-else-if.cpp @@ -0,0 +1,47 @@ + +#define CPP2_USE_MODULES Yes + +//=== Cpp2 type declarations ==================================================== + + +#include "cpp2util.h" + + + +//=== Cpp2 type definitions and function declarations =========================== + +#line 1 "pure2-initialization-safety-with-else-if.cpp2" +auto main(int const argc_, char const* const* const argv_) -> int; + + +//=== Cpp2 function definitions ================================================= + +#line 1 "pure2-initialization-safety-with-else-if.cpp2" +auto main(int const argc_, char const* const* const argv_) -> int{ + auto args = cpp2::make_args(argc_, argv_); +#line 2 "pure2-initialization-safety-with-else-if.cpp2" + cpp2::deferred_init p; + + auto a {1}; + auto b {2}; + auto c {3}; + auto d {4}; + + if (CPP2_UFCS_0(size, args)==3) { + p.construct(&a); + }else {if (true) { + if (CPP2_UFCS_0(size, args)==2) { + p.construct(&c); + }else {if (cpp2::cmp_greater(std::move(b),0)) { + p.construct(&a); + } + else { + p.construct(&d); + }} + }else { + p.construct(&c); + }} + + std::cout << *cpp2::assert_not_null(std::move(p.value())) << std::endl; +} + diff --git a/regression-tests/test-results/pure2-initialization-safety-with-else-if.cpp2.output b/regression-tests/test-results/pure2-initialization-safety-with-else-if.cpp2.output new file mode 100644 index 0000000000..bbbca572e8 --- /dev/null +++ b/regression-tests/test-results/pure2-initialization-safety-with-else-if.cpp2.output @@ -0,0 +1,2 @@ +pure2-initialization-safety-with-else-if.cpp2... ok (all Cpp2, passes safety checks) + diff --git a/source/parse.h b/source/parse.h index 8b4af5a0d5..0c2ee5602f 100644 --- a/source/parse.h +++ b/source/parse.h @@ -1160,13 +1160,13 @@ struct compound_statement_node struct selection_statement_node { - bool is_constexpr = false; - token const* identifier = {}; - source_position else_pos; - std::unique_ptr expression; - std::unique_ptr true_branch; - std::unique_ptr false_branch; - bool has_source_false_branch = false; + bool is_constexpr = false; + token const* identifier = {}; + source_position else_pos; + std::unique_ptr expression; + std::unique_ptr true_branch; + std::unique_ptr false_branch; + bool has_source_false_branch = false; auto position() const -> source_position @@ -4596,8 +4596,8 @@ class parser //G selection-statement: - //G 'if' 'constexpr'? expression compound-statement - //G 'if' 'constexpr'? expression compound-statement 'else' compound-statement + //G 'if' 'constexpr'? logical-or-expression compound-statement + //G 'if' 'constexpr'? logical-or-expression compound-statement 'else' compound-statement //G auto selection_statement() -> std::unique_ptr @@ -4622,7 +4622,7 @@ class parser next(); } - if (auto e = expression()) { + if (auto e = logical_or_expression()) { n->expression = std::move(e); } else { @@ -4657,12 +4657,16 @@ class parser n->else_pos = curr().position(); next(); - if (curr().type() != lexeme::LeftBrace) { + if ( + curr().type() != lexeme::LeftBrace + && curr() != "if" + ) + { error("an else branch body must be enclosed with { }"); return {}; } - if (auto s = compound_statement()) { + if (auto s = compound_statement( source_position{}, true )) { n->false_branch = std::move(s); n->has_source_false_branch = true; } @@ -5181,16 +5185,25 @@ class parser //G statement-seq statement //G auto compound_statement( - source_position equal_sign = source_position{} + source_position equal_sign = source_position{}, + bool allow_single_unbraced_statement = false ) -> std::unique_ptr { - if (curr().type() != lexeme::LeftBrace) { + bool is_braced = curr().type() == lexeme::LeftBrace; + if ( + !is_braced + && !allow_single_unbraced_statement + ) + { return {}; } auto n = std::make_unique(); - if (peek(1)) { + if (!is_braced) { + n->body_indent = curr().position().colno-1; + } + else if (peek(1)) { n->body_indent = peek(1)->position().colno-1; } @@ -5207,9 +5220,19 @@ class parser else { n->open_brace = curr().position(); } - next(); - while (curr().type() != lexeme::RightBrace) { + if (is_braced) { + next(); + } + + while ( + curr().type() != lexeme::RightBrace + && ( + is_braced + || std::ssize(n->statements) < 1 + ) + ) + { if ( ( is_literal(curr().type()) @@ -5229,8 +5252,11 @@ class parser n->statements.push_back( std::move(s) ); } - n->close_brace = curr().position(); - next(); + if (is_braced) { + assert(curr().type() == lexeme::RightBrace); + n->close_brace = curr().position(); + next(); + } return n; }