diff --git a/executable_semantics/ast/ast_rtti.txt b/executable_semantics/ast/ast_rtti.txt index a15c2964feae..7ff4206b2080 100644 --- a/executable_semantics/ast/ast_rtti.txt +++ b/executable_semantics/ast/ast_rtti.txt @@ -5,6 +5,7 @@ root class AstNode; abstract class Pattern : AstNode; class AutoPattern : Pattern; + class VarPattern : Pattern; class BindingPattern : Pattern; class TuplePattern : Pattern; class AlternativePattern : Pattern; diff --git a/executable_semantics/ast/declaration.h b/executable_semantics/ast/declaration.h index 338784a4fa7e..b0eb39404875 100644 --- a/executable_semantics/ast/declaration.h +++ b/executable_semantics/ast/declaration.h @@ -251,10 +251,12 @@ class VariableDeclaration : public Declaration { public: VariableDeclaration(SourceLocation source_loc, Nonnull binding, - std::optional> initializer) + std::optional> initializer, + ValueCategory value_category) : Declaration(AstNodeKind::VariableDeclaration, source_loc), binding_(binding), - initializer_(initializer) {} + initializer_(initializer), + value_category_(value_category) {} static auto classof(const AstNode* node) -> bool { return InheritsFromVariableDeclaration(node->kind()); @@ -264,6 +266,7 @@ class VariableDeclaration : public Declaration { auto binding() -> BindingPattern& { return *binding_; } auto initializer() const -> const Expression& { return **initializer_; } auto initializer() -> Expression& { return **initializer_; } + auto value_category() const -> ValueCategory { return value_category_; } bool has_initializer() const { return initializer_.has_value(); } @@ -273,6 +276,7 @@ class VariableDeclaration : public Declaration { // missing name. Nonnull binding_; std::optional> initializer_; + ValueCategory value_category_; }; class InterfaceDeclaration : public Declaration { diff --git a/executable_semantics/ast/pattern.cpp b/executable_semantics/ast/pattern.cpp index fc3ca77c6149..19a4f39a8468 100644 --- a/executable_semantics/ast/pattern.cpp +++ b/executable_semantics/ast/pattern.cpp @@ -48,6 +48,9 @@ void Pattern::Print(llvm::raw_ostream& out) const { case PatternKind::ExpressionPattern: out << cast(*this).expression(); break; + case PatternKind::VarPattern: + out << "var" << cast(*this).pattern(); + break; } } @@ -71,6 +74,9 @@ static void GetBindingsImpl( case PatternKind::AutoPattern: case PatternKind::ExpressionPattern: return; + case PatternKind::VarPattern: + GetBindingsImpl(cast(pattern).pattern(), bindings); + return; } } diff --git a/executable_semantics/ast/pattern.h b/executable_semantics/ast/pattern.h index 46b75364cd58..a16a8d84ff3f 100644 --- a/executable_semantics/ast/pattern.h +++ b/executable_semantics/ast/pattern.h @@ -101,6 +101,24 @@ class AutoPattern : public Pattern { } }; +class VarPattern : public Pattern { + public: + explicit VarPattern(SourceLocation source_loc, Nonnull pattern) + : Pattern(AstNodeKind::VarPattern, source_loc), pattern_(pattern) {} + + static auto classof(const AstNode* node) -> bool { + return InheritsFromVarPattern(node->kind()); + } + + auto pattern() const -> const Pattern& { return *pattern_; } + auto pattern() -> Pattern& { return *pattern_; } + + auto value_category() const -> ValueCategory { return ValueCategory::Var; } + + private: + Nonnull pattern_; +}; + // A pattern that matches a value of a specified type, and optionally binds // a name to it. class BindingPattern : public Pattern { @@ -108,10 +126,12 @@ class BindingPattern : public Pattern { using ImplementsCarbonValueNode = void; BindingPattern(SourceLocation source_loc, std::string name, - Nonnull type) + Nonnull type, + std::optional value_category) : Pattern(AstNodeKind::BindingPattern, source_loc), name_(std::move(name)), - type_(type) {} + type_(type), + value_category_(value_category) {} static auto classof(const AstNode* node) -> bool { return InheritsFromBindingPattern(node->kind()); @@ -126,7 +146,24 @@ class BindingPattern : public Pattern { auto type() const -> const Pattern& { return *type_; } auto type() -> Pattern& { return *type_; } - auto value_category() const -> ValueCategory { return ValueCategory::Var; } + // Returns the value category of this pattern. Can only be called after + // typechecking. + auto value_category() const -> ValueCategory { + return value_category_.value(); + } + + // Returns whether the value category has been set. Should only be called + // during typechecking. + auto has_value_category() const -> bool { + return value_category_.has_value(); + } + + // Sets the value category of the variable being bound. Can only be called + // once during typechecking + void set_value_category(ValueCategory vc) { + CHECK(!value_category_.has_value()); + value_category_ = vc; + } auto constant_value() const -> std::optional> { return std::nullopt; @@ -135,6 +172,7 @@ class BindingPattern : public Pattern { private: std::string name_; Nonnull type_; + std::optional value_category_; }; // A pattern that matches a tuple value field-wise. diff --git a/executable_semantics/ast/statement.h b/executable_semantics/ast/statement.h index de34d89ee3ff..ca0153f3ab16 100644 --- a/executable_semantics/ast/statement.h +++ b/executable_semantics/ast/statement.h @@ -108,10 +108,11 @@ class Assign : public Statement { class VariableDefinition : public Statement { public: VariableDefinition(SourceLocation source_loc, Nonnull pattern, - Nonnull init) + Nonnull init, ValueCategory value_category) : Statement(AstNodeKind::VariableDefinition, source_loc), pattern_(pattern), - init_(init) {} + init_(init), + value_category_(value_category) {} static auto classof(const AstNode* node) -> bool { return InheritsFromVariableDefinition(node->kind()); @@ -121,10 +122,12 @@ class VariableDefinition : public Statement { auto pattern() -> Pattern& { return *pattern_; } auto init() const -> const Expression& { return *init_; } auto init() -> Expression& { return *init_; } + auto value_category() const -> ValueCategory { return value_category_; } private: Nonnull pattern_; Nonnull init_; + ValueCategory value_category_; }; class If : public Statement { diff --git a/executable_semantics/interpreter/interpreter.cpp b/executable_semantics/interpreter/interpreter.cpp index cfd49424c363..c663f474a37b 100644 --- a/executable_semantics/interpreter/interpreter.cpp +++ b/executable_semantics/interpreter/interpreter.cpp @@ -760,6 +760,13 @@ void Interpreter::StepPattern() { } else { return todo_.FinishAction(act.results()[0]); } + case PatternKind::VarPattern: + if (act.pos() == 0) { + return todo_.Spawn(std::make_unique( + &cast(pattern).pattern())); + } else { + return todo_.FinishAction(act.results()[0]); + } } } diff --git a/executable_semantics/interpreter/resolve_names.cpp b/executable_semantics/interpreter/resolve_names.cpp index af854544943d..4c3c30a28768 100644 --- a/executable_semantics/interpreter/resolve_names.cpp +++ b/executable_semantics/interpreter/resolve_names.cpp @@ -180,6 +180,9 @@ static void ResolveNames(Pattern& pattern, StaticScope& enclosing_scope) { break; case PatternKind::AutoPattern: break; + case PatternKind::VarPattern: + ResolveNames(cast(pattern).pattern(), enclosing_scope); + break; } } diff --git a/executable_semantics/interpreter/type_checker.cpp b/executable_semantics/interpreter/type_checker.cpp index 8101884c5c35..7354b0034d4a 100644 --- a/executable_semantics/interpreter/type_checker.cpp +++ b/executable_semantics/interpreter/type_checker.cpp @@ -823,7 +823,7 @@ void TypeChecker::TypeCheckExp(Nonnull e, void TypeChecker::TypeCheckPattern( Nonnull p, std::optional> expected, - const ImplScope& impl_scope) { + const ImplScope& impl_scope, ValueCategory enclosing_value_category) { if (trace_) { llvm::outs() << "checking pattern " << *p; if (expected) { @@ -844,7 +844,8 @@ void TypeChecker::TypeCheckPattern( FATAL_COMPILATION_ERROR(binding.type().source_loc()) << "The type of a binding pattern cannot contain bindings."; } - TypeCheckPattern(&binding.type(), std::nullopt, impl_scope); + TypeCheckPattern(&binding.type(), std::nullopt, impl_scope, + enclosing_value_category); Nonnull type = InterpPattern(&binding.type(), arena_, trace_); if (expected) { @@ -863,6 +864,10 @@ void TypeChecker::TypeCheckPattern( ExpectIsConcreteType(binding.source_loc(), type); binding.set_static_type(type); SetValue(&binding, InterpPattern(&binding, arena_, trace_)); + + if (!binding.has_value_category()) { + binding.set_value_category(enclosing_value_category); + } return; } case PatternKind::TuplePattern: { @@ -882,7 +887,8 @@ void TypeChecker::TypeCheckPattern( if (expected) { expected_field_type = cast(**expected).elements()[i]; } - TypeCheckPattern(field, expected_field_type, impl_scope); + TypeCheckPattern(field, expected_field_type, impl_scope, + enclosing_value_category); field_types.push_back(&field->static_type()); } tuple.set_static_type(arena_->New(std::move(field_types))); @@ -912,7 +918,8 @@ void TypeChecker::TypeCheckPattern( << "'" << alternative.alternative_name() << "' is not an alternative of " << choice_type; } - TypeCheckPattern(&alternative.arguments(), *parameter_types, impl_scope); + TypeCheckPattern(&alternative.arguments(), *parameter_types, impl_scope, + enclosing_value_category); alternative.set_static_type(&choice_type); SetValue(&alternative, InterpPattern(&alternative, arena_, trace_)); return; @@ -924,6 +931,14 @@ void TypeChecker::TypeCheckPattern( SetValue(p, InterpPattern(p, arena_, trace_)); return; } + case PatternKind::VarPattern: + auto& let_var_pattern = cast(*p); + + TypeCheckPattern(&let_var_pattern.pattern(), expected, impl_scope, + let_var_pattern.value_category()); + let_var_pattern.set_static_type(&let_var_pattern.pattern().static_type()); + SetValue(&let_var_pattern, + InterpPattern(&let_var_pattern, arena_, trace_)); } } @@ -939,7 +954,7 @@ void TypeChecker::TypeCheckStmt(Nonnull s, std::vector new_clauses; for (auto& clause : match.clauses()) { TypeCheckPattern(&clause.pattern(), &match.expression().static_type(), - impl_scope); + impl_scope, ValueCategory::Let); TypeCheckStmt(&clause.statement(), impl_scope); } return; @@ -967,7 +982,8 @@ void TypeChecker::TypeCheckStmt(Nonnull s, auto& var = cast(*s); TypeCheckExp(&var.init(), impl_scope); const Value& rhs_ty = var.init().static_type(); - TypeCheckPattern(&var.pattern(), &rhs_ty, impl_scope); + TypeCheckPattern(&var.pattern(), &rhs_ty, impl_scope, + var.value_category()); return; } case StatementKind::Assign: { @@ -1117,10 +1133,12 @@ void TypeChecker::DeclareFunctionDeclaration(Nonnull f, } // Type check the receiver pattern if (f->is_method()) { - TypeCheckPattern(&f->me_pattern(), std::nullopt, impl_scope); + TypeCheckPattern(&f->me_pattern(), std::nullopt, impl_scope, + ValueCategory::Let); } // Type check the parameter pattern - TypeCheckPattern(&f->param_pattern(), std::nullopt, impl_scope); + TypeCheckPattern(&f->param_pattern(), std::nullopt, impl_scope, + ValueCategory::Let); // Create the impl_bindings std::vector> impl_bindings; @@ -1431,7 +1449,8 @@ void TypeChecker::DeclareDeclaration(Nonnull d, } Expression& type = cast(var.binding().type()).expression(); - TypeCheckPattern(&var.binding(), std::nullopt, impl_scope); + TypeCheckPattern(&var.binding(), std::nullopt, impl_scope, + var.value_category()); Nonnull declared_type = InterpExp(&type, arena_, trace_); var.set_static_type(declared_type); break; diff --git a/executable_semantics/interpreter/type_checker.h b/executable_semantics/interpreter/type_checker.h index bae4205e53f5..210f9fe74e52 100644 --- a/executable_semantics/interpreter/type_checker.h +++ b/executable_semantics/interpreter/type_checker.h @@ -50,7 +50,8 @@ class TypeChecker { // nullopt. void TypeCheckPattern(Nonnull p, std::optional> expected, - const ImplScope& impl_scope); + const ImplScope& impl_scope, + ValueCategory enclosing_value_category); // Equivalent to TypeCheckExp, but operates on the AST rooted at `s`. // diff --git a/executable_semantics/syntax/lexer.lpp b/executable_semantics/syntax/lexer.lpp index 462f1666d74b..3d653d6eadd6 100644 --- a/executable_semantics/syntax/lexer.lpp +++ b/executable_semantics/syntax/lexer.lpp @@ -72,6 +72,7 @@ INTERFACE "interface" LEFT_CURLY_BRACE "{" LEFT_PARENTHESIS "(" LEFT_SQUARE_BRACKET "[" +LET "let" LIBRARY "library" MATCH "match" MINUS "-" @@ -173,6 +174,7 @@ string_literal \"([^\\\"\n\v\f\r]|\\.)*\" {LEFT_CURLY_BRACE} { return SIMPLE_TOKEN(LEFT_CURLY_BRACE); } {LEFT_PARENTHESIS} { return SIMPLE_TOKEN(LEFT_PARENTHESIS); } {LEFT_SQUARE_BRACKET} { return SIMPLE_TOKEN(LEFT_SQUARE_BRACKET); } +{LET} { return SIMPLE_TOKEN(LET); } {LIBRARY} { return SIMPLE_TOKEN(LIBRARY); } {MATCH} { return SIMPLE_TOKEN(MATCH); } {MINUS} { return SIMPLE_TOKEN(MINUS); } diff --git a/executable_semantics/syntax/parser.ypp b/executable_semantics/syntax/parser.ypp index 9cd5b9fd2f35..cedd8eaddab0 100644 --- a/executable_semantics/syntax/parser.ypp +++ b/executable_semantics/syntax/parser.ypp @@ -72,6 +72,7 @@ #include "executable_semantics/ast/expression.h" #include "executable_semantics/ast/paren_contents.h" #include "executable_semantics/ast/pattern.h" + #include "executable_semantics/ast/value_category.h" #include "executable_semantics/common/arena.h" #include "executable_semantics/common/nonnull.h" #include "executable_semantics/syntax/bison_wrap.h" @@ -202,6 +203,7 @@ LEFT_CURLY_BRACE LEFT_PARENTHESIS LEFT_SQUARE_BRACKET + LET LIBRARY MATCH MINUS @@ -573,11 +575,16 @@ non_expression_pattern: AUTO { $$ = arena->New(context.source_loc()); } | binding_lhs COLON pattern - { $$ = arena->New(context.source_loc(), $1, $3); } + { + $$ = arena->New(context.source_loc(), $1, $3, + std::nullopt); + } | paren_pattern { $$ = $1; } | postfix_expression tuple_pattern { $$ = arena->New(context.source_loc(), $1, $2); } +| VAR non_expression_pattern + { $$ = arena->New(context.source_loc(), $2); } ; binding_lhs: identifier { $$ = $1; } @@ -642,7 +649,8 @@ clause: { $$ = Match::Clause(arena->New( context.source_loc(), std::string(AnonymousName), - arena->New(context.source_loc())), + arena->New(context.source_loc()), + ValueCategory::Let), $3); } ; @@ -659,7 +667,15 @@ statement: statement_expression EQUAL expression SEMICOLON { $$ = arena->New(context.source_loc(), $1, $3); } | VAR pattern EQUAL expression SEMICOLON - { $$ = arena->New(context.source_loc(), $2, $4); } + { + $$ = arena->New(context.source_loc(), $2, $4, + ValueCategory::Var); + } +| LET pattern EQUAL expression SEMICOLON + { + $$ = arena->New(context.source_loc(), $2, $4, + ValueCategory::Let); + } | statement_expression SEMICOLON { $$ = arena->New(context.source_loc(), $1); } | if_statement @@ -798,7 +814,10 @@ function_declaration: } ; variable_declaration: identifier COLON pattern - { $$ = arena->New(context.source_loc(), $1, $3); } + { + $$ = arena->New(context.source_loc(), $1, $3, + std::nullopt); + } ; alternative: identifier tuple @@ -837,10 +856,18 @@ declaration: | VAR variable_declaration SEMICOLON { $$ = arena->New(context.source_loc(), $2, - std::nullopt); + std::nullopt, ValueCategory::Var); } | VAR variable_declaration EQUAL expression SEMICOLON - { $$ = arena->New(context.source_loc(), $2, $4); } + { + $$ = arena->New(context.source_loc(), $2, $4, + ValueCategory::Var); + } +| LET variable_declaration EQUAL expression SEMICOLON + { + $$ = arena->New(context.source_loc(), $2, $4, + ValueCategory::Let); + } | INTERFACE identifier LEFT_CURLY_BRACE declaration_list RIGHT_CURLY_BRACE { auto ty_ty = arena -> New(context.source_loc()); diff --git a/executable_semantics/testdata/let/fail_function_args.carbon b/executable_semantics/testdata/let/fail_function_args.carbon new file mode 100644 index 000000000000..98f040535e2a --- /dev/null +++ b/executable_semantics/testdata/let/fail_function_args.carbon @@ -0,0 +1,23 @@ +// 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: %{not} %{executable_semantics} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{executable_semantics} --trace %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{executable_semantics} %s +// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/let/fail_function_args.carbon:16: Cannot assign to rvalue 'y' + +package ExecutableSemanticsTest api; + +fn f((var x: i32, y: i32)) -> i32 { + x = 0; + y = 0; + return x - 1; +} + +fn Main() -> i32 { + var t: auto = (1, 2); + return f(t); +} diff --git a/executable_semantics/testdata/let/fail_global_assign.carbon b/executable_semantics/testdata/let/fail_global_assign.carbon new file mode 100644 index 000000000000..abd799586b81 --- /dev/null +++ b/executable_semantics/testdata/let/fail_global_assign.carbon @@ -0,0 +1,19 @@ +// 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: %{not} %{executable_semantics} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{executable_semantics} --trace %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{executable_semantics} %s +// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/let/fail_global_assign.carbon:17: Cannot assign to rvalue 'x' + +package ExecutableSemanticsTest api; + +let x: i32 = 10; + +fn Main() -> i32 { + x = 0; + return 0; +} diff --git a/executable_semantics/testdata/let/fail_local_assign.carbon b/executable_semantics/testdata/let/fail_local_assign.carbon new file mode 100644 index 000000000000..2458faadaaa4 --- /dev/null +++ b/executable_semantics/testdata/let/fail_local_assign.carbon @@ -0,0 +1,18 @@ +// 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: %{not} %{executable_semantics} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{executable_semantics} --trace %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{executable_semantics} %s +// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/let/fail_local_assign.carbon:16: Cannot assign to rvalue 'x' + +package ExecutableSemanticsTest api; + +fn Main() -> i32 { + let x: auto = 10; + x = 0; + return 0; +} diff --git a/executable_semantics/testdata/let/fail_match_choice.carbon b/executable_semantics/testdata/let/fail_match_choice.carbon new file mode 100644 index 000000000000..ebb033e2d5b6 --- /dev/null +++ b/executable_semantics/testdata/let/fail_match_choice.carbon @@ -0,0 +1,33 @@ +// 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: %{not} %{executable_semantics} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{executable_semantics} --trace %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{executable_semantics} %s +// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/let/fail_match_choice.carbon:29: Cannot assign to rvalue 'x' + +package ExecutableSemanticsTest api; + +choice Ints { + None, + One(i32), + Two(i32,i32) +} + +fn Main() -> i32 { + var x: auto = Ints.None(); + var n: auto = 0; + match (x) { + case Ints.One(var x: auto) => + x = 2; + case Ints.None() => + n = n - 1; + case Ints.Two(x: auto, y: auto) => { + x = 0; + } + } + return 0; +} diff --git a/executable_semantics/testdata/let/fail_method_args.carbon b/executable_semantics/testdata/let/fail_method_args.carbon new file mode 100644 index 000000000000..d710ea5fc698 --- /dev/null +++ b/executable_semantics/testdata/let/fail_method_args.carbon @@ -0,0 +1,32 @@ +// 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: %{not} %{executable_semantics} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{executable_semantics} --trace %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{executable_semantics} %s +// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/let/fail_method_args.carbon:21: Cannot assign to rvalue 'x' + +package ExecutableSemanticsTest api; + +class Point { + + fn Origin() -> Point { + return {.x = 0, .y = 0}; + } + + fn SetX[me: Point](x :i32) { + x = 10; + } + + var x: i32; + var y: i32; +} + +fn Main() -> i32 { + var p: Point = Point.Origin(); + p.SetX(42); + return 0; +} diff --git a/executable_semantics/testdata/let/fail_tuple_pattern_let_context.carbon b/executable_semantics/testdata/let/fail_tuple_pattern_let_context.carbon new file mode 100644 index 000000000000..5fde45ee9e2d --- /dev/null +++ b/executable_semantics/testdata/let/fail_tuple_pattern_let_context.carbon @@ -0,0 +1,19 @@ +// 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: %{not} %{executable_semantics} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{executable_semantics} --trace %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{executable_semantics} %s +// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/let/fail_tuple_pattern_let_context.carbon:17: Cannot assign to rvalue 'c' + +package ExecutableSemanticsTest api; + +fn Main() -> i32 { + let (var a: auto, b: auto, c:auto, d: auto) = (1, 2, 3, 4); + a = 0; + c = 0; // should fail + return 0; +} diff --git a/executable_semantics/testdata/let/fail_tuple_pattern_let_context_nested.carbon b/executable_semantics/testdata/let/fail_tuple_pattern_let_context_nested.carbon new file mode 100644 index 000000000000..e5f043e63f27 --- /dev/null +++ b/executable_semantics/testdata/let/fail_tuple_pattern_let_context_nested.carbon @@ -0,0 +1,17 @@ +// 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: %{not} %{executable_semantics} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{executable_semantics} --trace %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{executable_semantics} %s +// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/let/fail_tuple_pattern_let_context_nested.carbon:15: syntax error, unexpected LET + +package ExecutableSemanticsTest api; + +fn Main() -> i32 { + let (var a: auto, b: auto, c:auto, var (d: auto, let e:auto)) = (1, 2, 3, (4, 5)); + return 0; +} diff --git a/executable_semantics/testdata/let/fail_tuple_pattern_let_in_var.carbon b/executable_semantics/testdata/let/fail_tuple_pattern_let_in_var.carbon new file mode 100644 index 000000000000..e6b9ff409110 --- /dev/null +++ b/executable_semantics/testdata/let/fail_tuple_pattern_let_in_var.carbon @@ -0,0 +1,17 @@ +// 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: %{not} %{executable_semantics} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{executable_semantics} --trace %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{executable_semantics} %s +// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/let/fail_tuple_pattern_let_in_var.carbon:15: syntax error, unexpected LET + +package ExecutableSemanticsTest api; + +fn Main() -> i32 { + var (var a: auto, b: auto, let c:auto, d: auto) = (1, 2, 3, 4); + return 0; +} diff --git a/executable_semantics/testdata/let/nested_tuple_pattern.carbon b/executable_semantics/testdata/let/nested_tuple_pattern.carbon new file mode 100644 index 000000000000..84fe7a3940b9 --- /dev/null +++ b/executable_semantics/testdata/let/nested_tuple_pattern.carbon @@ -0,0 +1,28 @@ +// 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: %{executable_semantics} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{executable_semantics} --trace %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{executable_semantics} %s +// CHECK: result: 0 + +package ExecutableSemanticsTest api; + +choice Ints { + None, + One(i32), + Two(i32,i32) +} + +fn Main() -> i32 { + let (var Ints.Two(a1: auto, var a2: auto), ((b: auto, var c:auto), var (d: auto, e:auto))) = (Ints.Two(1, 10), ((2, 3), (4, 5))); + a1 = 0; + a2 = 0; + c = 0; + d = 0; + e = 0; + return 0; +}