diff --git a/common/fuzzing/carbon.proto b/common/fuzzing/carbon.proto index b60c8dc4c478..4ca448c869bb 100644 --- a/common/fuzzing/carbon.proto +++ b/common/fuzzing/carbon.proto @@ -265,6 +265,12 @@ message WhileStatement { optional BlockStatement body = 2; } +message ForStatement { + optional BindingPattern var_decl = 1; + optional Expression target = 2; + optional BlockStatement body = 3; +} + message MatchClause { optional Pattern pattern = 1; optional Statement statement = 2; @@ -306,6 +312,7 @@ message Statement { AwaitStatement await_statement = 12; BreakStatement break_statement = 13; ContinueStatement continue_statement = 14; + ForStatement for_statement = 15; } } diff --git a/common/fuzzing/proto_to_carbon.cpp b/common/fuzzing/proto_to_carbon.cpp index 7204e6026e0e..07094700307d 100644 --- a/common/fuzzing/proto_to_carbon.cpp +++ b/common/fuzzing/proto_to_carbon.cpp @@ -586,6 +586,16 @@ static auto StatementToCarbon(const Fuzzing::Statement& statement, BlockStatementToCarbon(while_statement.body(), out); break; } + case Fuzzing::Statement::kForStatement: { + const auto& for_statement = statement.for_statement(); + out << "for ("; + BindingPatternToCarbon(for_statement.var_decl(), out); + out << " in "; + ExpressionToCarbon(for_statement.target(), out); + out << ") "; + BlockStatementToCarbon(for_statement.body(), out); + break; + } case Fuzzing::Statement::kMatch: { const auto& match = statement.match(); diff --git a/explorer/ast/ast_rtti.txt b/explorer/ast/ast_rtti.txt index d6cefab3a115..aa4d4698e49f 100644 --- a/explorer/ast/ast_rtti.txt +++ b/explorer/ast/ast_rtti.txt @@ -40,6 +40,7 @@ abstract class Statement : AstNode; class Continuation : Statement; class Run : Statement; class Await : Statement; + class For : Statement; abstract class Expression : AstNode; class BoolTypeLiteral : Expression; class BoolLiteral : Expression; diff --git a/explorer/ast/statement.cpp b/explorer/ast/statement.cpp index 32dfcb36da0f..db6ace0f3129 100644 --- a/explorer/ast/statement.cpp +++ b/explorer/ast/statement.cpp @@ -42,6 +42,13 @@ void Statement::PrintDepth(int depth, llvm::raw_ostream& out) const { while_stmt.body().PrintDepth(depth - 1, out); break; } + case StatementKind::For: { + const auto& for_stmt = cast(*this); + out << "for (" << for_stmt.variable_declaration() << " in " + << for_stmt.loop_target() << ")\n"; + for_stmt.body().PrintDepth(depth - 1, out); + break; + } case StatementKind::Break: out << "break;"; break; diff --git a/explorer/ast/statement.h b/explorer/ast/statement.h index f670eb4c8725..0d96ec2bca9c 100644 --- a/explorer/ast/statement.h +++ b/explorer/ast/statement.h @@ -298,6 +298,38 @@ class While : public Statement { Nonnull body_; }; +class For : public Statement { + public: + For(SourceLocation source_loc, Nonnull variable_declaration, + Nonnull loop_target, Nonnull body) + : Statement(AstNodeKind::For, source_loc), + variable_declaration_(variable_declaration), + loop_target_(loop_target), + body_(body) {} + + static auto classof(const AstNode* node) -> bool { + return InheritsFromFor(node->kind()); + } + + auto variable_declaration() const -> const BindingPattern& { + return *variable_declaration_; + } + auto variable_declaration() -> BindingPattern& { + return *variable_declaration_; + } + + auto loop_target() const -> const Expression& { return *loop_target_; } + auto loop_target() -> Expression& { return *loop_target_; } + + auto body() const -> const Block& { return *body_; } + auto body() -> Block& { return *body_; } + + private: + Nonnull variable_declaration_; + Nonnull loop_target_; + Nonnull body_; +}; + class Break : public Statement { public: explicit Break(SourceLocation source_loc) diff --git a/explorer/fuzzing/ast_to_proto.cpp b/explorer/fuzzing/ast_to_proto.cpp index d5f4795320ae..b93d0f89e59f 100644 --- a/explorer/fuzzing/ast_to_proto.cpp +++ b/explorer/fuzzing/ast_to_proto.cpp @@ -521,6 +521,16 @@ static auto StatementToProto(const Statement& statement) -> Fuzzing::Statement { // Initializes with the default value; there's nothing to set. statement_proto.mutable_continue_statement(); break; + + case StatementKind::For: { + const auto& for_stmt = cast(statement); + auto* for_proto = statement_proto.mutable_for_statement(); + *for_proto->mutable_var_decl() = + BindingPatternToProto(for_stmt.variable_declaration()); + *for_proto->mutable_target() = ExpressionToProto(for_stmt.loop_target()); + *for_proto->mutable_body() = BlockStatementToProto(for_stmt.body()); + break; + } } return statement_proto; } diff --git a/explorer/interpreter/action.h b/explorer/interpreter/action.h index 46ce4115a529..5accfff0c7bc 100644 --- a/explorer/interpreter/action.h +++ b/explorer/interpreter/action.h @@ -116,7 +116,10 @@ class Action { auto results() const -> const std::vector>& { return results_; } - + void ReplaceResult(std::size_t index, Nonnull value) { + CARBON_CHECK(index < results_.size()); + results_[index] = value; + } // Appends `result` to `results`. void AddResult(Nonnull result) { results_.push_back(result); } diff --git a/explorer/interpreter/interpreter.cpp b/explorer/interpreter/interpreter.cpp index 4026b141885f..ea0587a820c0 100644 --- a/explorer/interpreter/interpreter.cpp +++ b/explorer/interpreter/interpreter.cpp @@ -1505,7 +1505,78 @@ auto Interpreter::StepStmt() -> ErrorOr { } } } + case StatementKind::For: { + constexpr int TargetVarPosInResult = 0; + constexpr int CurrentIndexPosInResult = 1; + constexpr int EndIndexPosInResult = 2; + constexpr int LoopVarPosInResult = 3; + if (act.pos() == 0) { + return todo_.Spawn( + std::make_unique(&cast(stmt).loop_target())); + } + if (act.pos() == 1) { + Nonnull source_array = + cast(act.results()[TargetVarPosInResult]); + + auto end_index = static_cast(source_array->elements().size()); + if (end_index == 0) { + return todo_.FinishAction(); + } + act.AddResult(arena_->New(0)); + act.AddResult(arena_->New(end_index)); + return todo_.Spawn(std::make_unique( + &cast(stmt).variable_declaration())); + } + if (act.pos() == 2) { + Nonnull loop_var = + cast( + act.results()[LoopVarPosInResult]); + Nonnull source_array = + cast(act.results()[TargetVarPosInResult]); + + auto start_index = + cast(act.results()[CurrentIndexPosInResult])->value(); + todo_.Initialize(*(loop_var->value_node()), + source_array->elements()[start_index]); + act.ReplaceResult(CurrentIndexPosInResult, + arena_->New(start_index + 1)); + return todo_.Spawn( + std::make_unique(&cast(stmt).body())); + } + if (act.pos() >= 3) { + auto current_index = + cast(act.results()[CurrentIndexPosInResult])->value(); + auto end_index = + cast(act.results()[EndIndexPosInResult])->value(); + + if (current_index < end_index) { + Nonnull source_array = + cast(act.results()[TargetVarPosInResult]); + Nonnull loop_var = + cast( + act.results()[LoopVarPosInResult]); + + CARBON_ASSIGN_OR_RETURN( + Nonnull assigned_array_element, + todo_.ValueOfNode(*(loop_var->value_node()), stmt.source_loc())); + + auto lvalue = cast(assigned_array_element); + CARBON_RETURN_IF_ERROR(heap_.Write( + lvalue->address(), source_array->elements()[current_index], + stmt.source_loc())); + + act.ReplaceResult(CurrentIndexPosInResult, + arena_->New(current_index + 1)); + return todo_.Spawn( + std::make_unique(&cast(stmt).body())); + } + } + return todo_.FinishAction(); + } case StatementKind::While: + // TODO: Rewrite While to use ReplaceResult to store condition result. + // This will remove the inconsistency between the while and for + // loops. if (act.pos() % 2 == 0) { // { { (while (e) s) :: C, E, F} :: S, H} // -> { { e :: (while ([]) s) :: C, E, F} :: S, H} diff --git a/explorer/interpreter/resolve_control_flow.cpp b/explorer/interpreter/resolve_control_flow.cpp index f8db8469f581..b96d38c4d33a 100644 --- a/explorer/interpreter/resolve_control_flow.cpp +++ b/explorer/interpreter/resolve_control_flow.cpp @@ -106,6 +106,11 @@ static auto ResolveControlFlow(Nonnull statement, } return Success(); } + case StatementKind::For: { + CARBON_RETURN_IF_ERROR(ResolveControlFlow(&cast(*statement).body(), + statement, function)); + return Success(); + } case StatementKind::While: CARBON_RETURN_IF_ERROR(ResolveControlFlow(&cast(*statement).body(), statement, function)); diff --git a/explorer/interpreter/resolve_names.cpp b/explorer/interpreter/resolve_names.cpp index 107074255814..94f3ad028d47 100644 --- a/explorer/interpreter/resolve_names.cpp +++ b/explorer/interpreter/resolve_names.cpp @@ -413,6 +413,18 @@ static auto ResolveNames(Statement& statement, StaticScope& enclosing_scope) CARBON_RETURN_IF_ERROR(ResolveNames(while_stmt.body(), enclosing_scope)); break; } + case StatementKind::For: { + StaticScope statement_scope; + statement_scope.AddParent(&enclosing_scope); + auto& for_stmt = cast(statement); + CARBON_RETURN_IF_ERROR( + ResolveNames(for_stmt.loop_target(), statement_scope)); + CARBON_RETURN_IF_ERROR( + ResolveNames(for_stmt.variable_declaration(), statement_scope)); + CARBON_RETURN_IF_ERROR(ResolveNames(for_stmt.body(), statement_scope)); + + break; + } case StatementKind::Match: { auto& match = cast(statement); CARBON_RETURN_IF_ERROR(ResolveNames(match.expression(), enclosing_scope)); diff --git a/explorer/interpreter/resolve_unformed.cpp b/explorer/interpreter/resolve_unformed.cpp index 7e093c9d8523..dc1110be02f6 100644 --- a/explorer/interpreter/resolve_unformed.cpp +++ b/explorer/interpreter/resolve_unformed.cpp @@ -196,6 +196,7 @@ static auto ResolveUnformed( case StatementKind::Continuation: case StatementKind::Run: case StatementKind::Await: + case StatementKind::For: // do nothing break; } diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index 536d2b109f4b..cfdec2d2d7cd 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -2965,6 +2965,29 @@ auto TypeChecker::TypeCheckStmt(Nonnull s, CARBON_RETURN_IF_ERROR(TypeCheckStmt(&while_stmt.body(), impl_scope)); return Success(); } + case StatementKind::For: { + auto& for_stmt = cast(*s); + ImplScope inner_impl_scope; + inner_impl_scope.AddParent(&impl_scope); + + CARBON_RETURN_IF_ERROR( + TypeCheckExp(&for_stmt.loop_target(), inner_impl_scope)); + + const Value& rhs = for_stmt.loop_target().static_type(); + if (rhs.kind() == Value::Kind::StaticArrayType) { + CARBON_RETURN_IF_ERROR( + TypeCheckPattern(&for_stmt.variable_declaration(), + &cast(rhs).element_type(), + inner_impl_scope, ValueCategory::Var)); + + } else { + return CompilationError(for_stmt.source_loc()) + << "expected array type after in, found value of type " << rhs; + } + + CARBON_RETURN_IF_ERROR(TypeCheckStmt(&for_stmt.body(), inner_impl_scope)); + return Success(); + } case StatementKind::Break: case StatementKind::Continue: return Success(); @@ -3163,6 +3186,7 @@ auto TypeChecker::ExpectReturnOnAllPaths( case StatementKind::Assign: case StatementKind::ExpressionStatement: case StatementKind::While: + case StatementKind::For: case StatementKind::Break: case StatementKind::Continue: case StatementKind::VariableDefinition: diff --git a/explorer/syntax/lexer.lpp b/explorer/syntax/lexer.lpp index 3ccfa550b707..6af5bfb4bf37 100644 --- a/explorer/syntax/lexer.lpp +++ b/explorer/syntax/lexer.lpp @@ -64,6 +64,7 @@ EXTERNAL "external" FALSE "false" FN "fn" FN_TYPE "__Fn" +FOR "for" FORALL "forall" GREATER ">" GREATER_EQUAL ">=" @@ -71,6 +72,7 @@ GREATER_GREATER ">>" IF "if" IMPL "impl" IMPORT "import" +IN "in" INTERFACE "interface" IS "is" LEFT_CURLY_BRACE "{" @@ -165,6 +167,7 @@ operand_start [(A-Za-z0-9_\"] {FN_TYPE} { return CARBON_SIMPLE_TOKEN(FN_TYPE); } {FN} { return CARBON_SIMPLE_TOKEN(FN); } {FORALL} { return CARBON_SIMPLE_TOKEN(FORALL); } +{FOR} { return CARBON_SIMPLE_TOKEN(FOR); } {GREATER_EQUAL} { return CARBON_SIMPLE_TOKEN(GREATER_EQUAL); } {GREATER_GREATER} { return CARBON_SIMPLE_TOKEN(GREATER_GREATER); } {GREATER} { return CARBON_SIMPLE_TOKEN(GREATER); } @@ -172,6 +175,7 @@ operand_start [(A-Za-z0-9_\"] {IMPL} { return CARBON_SIMPLE_TOKEN(IMPL); } {IMPORT} { return CARBON_SIMPLE_TOKEN(IMPORT); } {INTERFACE} { return CARBON_SIMPLE_TOKEN(INTERFACE); } +{IN} { return CARBON_SIMPLE_TOKEN(IN); } {IS} { return CARBON_SIMPLE_TOKEN(IS); } {LEFT_CURLY_BRACE} { return CARBON_SIMPLE_TOKEN(LEFT_CURLY_BRACE); } {LEFT_PARENTHESIS} { return CARBON_SIMPLE_TOKEN(LEFT_PARENTHESIS); } diff --git a/explorer/syntax/parser.ypp b/explorer/syntax/parser.ypp index 85e268dc2917..fe8a808b5000 100644 --- a/explorer/syntax/parser.ypp +++ b/explorer/syntax/parser.ypp @@ -228,6 +228,7 @@ FALSE FN FN_TYPE + FOR FORALL GREATER GREATER_EQUAL @@ -235,6 +236,7 @@ IF IMPL IMPORT + IN INTERFACE IS LEFT_CURLY_BRACE @@ -934,6 +936,8 @@ statement: { $$ = arena->New(context.source_loc(), $2); } | AWAIT SEMICOLON { $$ = arena->New(context.source_loc()); } +| FOR LEFT_PARENTHESIS variable_declaration IN type_expression RIGHT_PARENTHESIS block + { $$ = arena->New(context.source_loc(), $3, $5, $7); } ; if_statement: IF LEFT_PARENTHESIS expression RIGHT_PARENTHESIS block optional_else diff --git a/explorer/testdata/basic_syntax/fail_missing_var.carbon b/explorer/testdata/basic_syntax/fail_missing_var.carbon index 26cc5a536f1e..7b209be68ba7 100644 --- a/explorer/testdata/basic_syntax/fail_missing_var.carbon +++ b/explorer/testdata/basic_syntax/fail_missing_var.carbon @@ -12,7 +12,7 @@ package ExplorerTest api; fn Main() -> i32 { // error - // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_missing_var.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting MINUS or PLUS + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_missing_var.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting EQUAL or SEMICOLON x : i32; return 1; } diff --git a/explorer/testdata/basic_syntax/fail_var_named_self.carbon b/explorer/testdata/basic_syntax/fail_var_named_self.carbon index fa74e15b4bc6..79e9e2ded9e7 100644 --- a/explorer/testdata/basic_syntax/fail_var_named_self.carbon +++ b/explorer/testdata/basic_syntax/fail_var_named_self.carbon @@ -14,7 +14,7 @@ fn Main() -> i32 { // Error: can't use keyword `Self` as the name of a variable. // TODO: Current error message is unclear, better would be to say // something like: unexpected `Self`, expecting identifier - // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_var_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting MINUS or PLUS + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_var_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting EQUAL or SEMICOLON var Self : i32 = 0; return Self; } diff --git a/explorer/testdata/for/for_loop.carbon b/explorer/testdata/for/for_loop.carbon new file mode 100644 index 000000000000..657215b46298 --- /dev/null +++ b/explorer/testdata/for/for_loop.carbon @@ -0,0 +1,37 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// RUN: %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s +// CHECK: HALLO WELT +// CHECK: HALLO WELT +// CHECK: HALLO WELT +// CHECK: HALLO WELT +// CHECK: HALLO WELT +// CHECK: HALLO WELT +// CHECK: HALLO WELT +// CHECK: HALLO WELT +// CHECK: result: 8 + +package ExplorerTest api; + + + + +fn Main() -> i32 { + var ar: [i32; 4] = (0, 1,2,3); + var count : i32 = 0; + for( x: i32 in ar){ + Print("HALLO WELT ", x); + count = count +1; + } + for( x: i32 in ar){ + Print("HALLO WELT ", x); + count = count +1; + } + return count; +} diff --git a/explorer/testdata/for/for_loop_auto.carbon b/explorer/testdata/for/for_loop_auto.carbon new file mode 100644 index 000000000000..2b19b78cf439 --- /dev/null +++ b/explorer/testdata/for/for_loop_auto.carbon @@ -0,0 +1,29 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// RUN: %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s +// CHECK: HALLO WELT +// CHECK: HALLO WELT +// CHECK: HALLO WELT +// CHECK: HALLO WELT +// CHECK: result: 4 + +package ExplorerTest api; + + + + +fn Main() -> i32 { + var ar: [i32; 4] = (0, 1,2,3); + var count : i32 = 0; + for( x: auto in ar){ + Print("HALLO WELT ", x); + count = count +1; + } + return count; +} diff --git a/explorer/testdata/for/for_loop_break.carbon b/explorer/testdata/for/for_loop_break.carbon new file mode 100644 index 000000000000..b6d1e1311349 --- /dev/null +++ b/explorer/testdata/for/for_loop_break.carbon @@ -0,0 +1,27 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// RUN: %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s +// CHECK: result: 3 + +package ExplorerTest api; + + + + +fn Main() -> i32 { + var ar: [i32; 4] = (0, 1,2,3); + var count : i32 = 0; + for( x: i32 in ar){ + count = count +x; + if( x == 2){ + break; + } + } + return count; +} diff --git a/explorer/testdata/for/for_loop_continue.carbon b/explorer/testdata/for/for_loop_continue.carbon new file mode 100644 index 000000000000..d4720d7ce494 --- /dev/null +++ b/explorer/testdata/for/for_loop_continue.carbon @@ -0,0 +1,27 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// RUN: %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s +// CHECK: result: 4 + +package ExplorerTest api; + + + + +fn Main() -> i32 { + var ar: [i32; 4] = (0, 1,2,3); + var count : i32 = 0; + for( x: i32 in ar){ + if( x == 2){ + continue; + } + count = count +x; + } + return count; +} diff --git a/explorer/testdata/for/for_loop_empty.carbon b/explorer/testdata/for/for_loop_empty.carbon new file mode 100644 index 000000000000..781be8909897 --- /dev/null +++ b/explorer/testdata/for/for_loop_empty.carbon @@ -0,0 +1,22 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// RUN: %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s +// CHECK: result: 0 + +package ExplorerTest api; + + +fn Main() -> i32 { + var ar: [i32; 0] = () ; + var count : i32 = 0; + for( x: i32 in ar ){ + count = 2; + } + return count; +} diff --git a/explorer/testdata/for/for_loop_nested.carbon b/explorer/testdata/for/for_loop_nested.carbon new file mode 100644 index 000000000000..ce1d381c52ec --- /dev/null +++ b/explorer/testdata/for/for_loop_nested.carbon @@ -0,0 +1,24 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// RUN: %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s +// CHECK: result: 20 + +package ExplorerTest api; + +fn Main() -> i32 { + var ar: [i32; 4] = (0, 1,2,3); + var count : i32 = 0; + for( x: i32 in ar){ + count = count +1; + for( x: i32 in ar){ + count = count +1; + } + } + return count; +} diff --git a/explorer/testdata/let/fail_local_named_self.carbon b/explorer/testdata/let/fail_local_named_self.carbon index 41322c04db97..495f82098741 100644 --- a/explorer/testdata/let/fail_local_named_self.carbon +++ b/explorer/testdata/let/fail_local_named_self.carbon @@ -12,7 +12,7 @@ package ExplorerTest api; fn Main() -> i32 { // Error: Can't use keyword `Self` as the name of a local. - // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/let/fail_local_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting MINUS or PLUS + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/let/fail_local_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting EQUAL let Self: auto = 10; return 0; }