diff --git a/executable_semantics/ast/expression.h b/executable_semantics/ast/expression.h index 64f947a9c01e..f7e71e25111f 100644 --- a/executable_semantics/ast/expression.h +++ b/executable_semantics/ast/expression.h @@ -24,6 +24,16 @@ class Value; class Expression : public virtual AstNode { public: + // The value category of a Carbon expression indicates whether it evaluates + // to a variable or a value. A variable can be mutated, and can have its + // address taken, whereas a value cannot. + enum class ValueCategory { + // A variable. This roughly corresponds to a C/C++ lvalue. + Var, + // A value. This roughly corresponds to a C/C++ rvalue. + Let, + }; + ~Expression() override = 0; void Print(llvm::raw_ostream& out) const; @@ -51,6 +61,17 @@ class Expression : public virtual AstNode { // and after typechecking it's guaranteed to be true. auto has_static_type() const -> bool { return static_type_.has_value(); } + // The value category of this expression. Cannot be called before + // typechecking. + auto value_category() const -> ValueCategory { return *value_category_; } + + // Sets the value category of this expression. Can be called multiple times, + // but the argument must have the same value each time. + void set_value_category(ValueCategory value_category) { + CHECK(!value_category_.has_value() || value_category == *value_category_); + value_category_ = value_category; + } + protected: // Constructs an Expression representing syntax at the given line number. // `kind` must be the enumerator corresponding to the most-derived type being @@ -59,6 +80,7 @@ class Expression : public virtual AstNode { private: std::optional> static_type_; + std::optional value_category_; }; // A FieldInitializer represents the initialization of a single struct field. diff --git a/executable_semantics/interpreter/interpreter.cpp b/executable_semantics/interpreter/interpreter.cpp index a90a9f24816b..a9d200508e78 100644 --- a/executable_semantics/interpreter/interpreter.cpp +++ b/executable_semantics/interpreter/interpreter.cpp @@ -357,8 +357,7 @@ void Interpreter::StepLvalue() { case ExpressionKind::StringLiteral: case ExpressionKind::StringTypeLiteral: case ExpressionKind::IntrinsicExpression: - FATAL_RUNTIME_ERROR_NO_LINE() - << "Can't treat expression as lvalue: " << exp; + FATAL() << "Can't treat expression as lvalue: " << exp; } } diff --git a/executable_semantics/interpreter/type_checker.cpp b/executable_semantics/interpreter/type_checker.cpp index 0830895269f0..568746893a28 100644 --- a/executable_semantics/interpreter/type_checker.cpp +++ b/executable_semantics/interpreter/type_checker.cpp @@ -449,6 +449,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, << "index " << i << " is out of range for type " << tuple_type; } SetStaticType(&index, tuple_type.elements()[i]); + index.set_value_category(index.aggregate().value_category()); return TCResult(res.types); } default: @@ -464,6 +465,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, arg_types.push_back(&arg->static_type()); } SetStaticType(e, arena_->New(std::move(arg_types))); + e->set_value_category(Expression::ValueCategory::Let); return TCResult(new_types); } case ExpressionKind::StructLiteral: { @@ -477,6 +479,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, arg_types.push_back({arg.name(), &arg.expression().static_type()}); } SetStaticType(e, arena_->New(std::move(arg_types))); + e->set_value_category(Expression::ValueCategory::Let); return TCResult(new_types); } case ExpressionKind::StructTypeLiteral: { @@ -499,6 +502,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, } else { SetStaticType(&struct_type, arena_->New()); } + e->set_value_category(Expression::ValueCategory::Let); return TCResult(new_types); } case ExpressionKind::FieldAccessExpression: { @@ -511,6 +515,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, for (const auto& [field_name, field_type] : struct_type.fields()) { if (access.field() == field_name) { SetStaticType(&access, field_type); + access.set_value_category(access.aggregate().value_category()); return TCResult(res.types); } } @@ -524,6 +529,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, for (auto& field : t_class.fields()) { if (access.field() == field.name) { SetStaticType(&access, field.value); + access.set_value_category(access.aggregate().value_category()); return TCResult(res.types); } } @@ -531,6 +537,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, for (auto& method : t_class.methods()) { if (access.field() == method.name) { SetStaticType(&access, method.value); + access.set_value_category(Expression::ValueCategory::Let); return TCResult(res.types); } } @@ -551,6 +558,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, arena_->New( std::vector>(), *parameter_types, &aggregate_type)); + access.set_value_category(Expression::ValueCategory::Let); return TCResult(res.types); } default: @@ -564,6 +572,9 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, std::optional> type = types.Get(ident.name()); if (type) { SetStaticType(&ident, *type); + // TODO: this should depend on what entity this name resolves to, but + // we don't have access to that information yet. + ident.set_value_category(Expression::ValueCategory::Var); return TCResult(types); } else { FATAL_COMPILATION_ERROR(e->source_loc()) @@ -571,9 +582,11 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, } } case ExpressionKind::IntLiteral: + e->set_value_category(Expression::ValueCategory::Let); SetStaticType(e, arena_->New()); return TCResult(types); case ExpressionKind::BoolLiteral: + e->set_value_category(Expression::ValueCategory::Let); SetStaticType(e, arena_->New()); return TCResult(types); case ExpressionKind::PrimitiveOperatorExpression: { @@ -592,6 +605,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, ExpectExactType(e->source_loc(), "negation", arena_->New(), ts[0]); SetStaticType(&op, arena_->New()); + op.set_value_category(Expression::ValueCategory::Let); return TCResult(new_types); case Operator::Add: ExpectExactType(e->source_loc(), "addition(1)", @@ -599,6 +613,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, ExpectExactType(e->source_loc(), "addition(2)", arena_->New(), ts[1]); SetStaticType(&op, arena_->New()); + op.set_value_category(Expression::ValueCategory::Let); return TCResult(new_types); case Operator::Sub: ExpectExactType(e->source_loc(), "subtraction(1)", @@ -606,6 +621,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, ExpectExactType(e->source_loc(), "subtraction(2)", arena_->New(), ts[1]); SetStaticType(&op, arena_->New()); + op.set_value_category(Expression::ValueCategory::Let); return TCResult(new_types); case Operator::Mul: ExpectExactType(e->source_loc(), "multiplication(1)", @@ -613,6 +629,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, ExpectExactType(e->source_loc(), "multiplication(2)", arena_->New(), ts[1]); SetStaticType(&op, arena_->New()); + op.set_value_category(Expression::ValueCategory::Let); return TCResult(new_types); case Operator::And: ExpectExactType(e->source_loc(), "&&(1)", arena_->New(), @@ -620,6 +637,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, ExpectExactType(e->source_loc(), "&&(2)", arena_->New(), ts[1]); SetStaticType(&op, arena_->New()); + op.set_value_category(Expression::ValueCategory::Let); return TCResult(new_types); case Operator::Or: ExpectExactType(e->source_loc(), "||(1)", arena_->New(), @@ -627,22 +645,27 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, ExpectExactType(e->source_loc(), "||(2)", arena_->New(), ts[1]); SetStaticType(&op, arena_->New()); + op.set_value_category(Expression::ValueCategory::Let); return TCResult(new_types); case Operator::Not: ExpectExactType(e->source_loc(), "!", arena_->New(), ts[0]); SetStaticType(&op, arena_->New()); + op.set_value_category(Expression::ValueCategory::Let); return TCResult(new_types); case Operator::Eq: ExpectExactType(e->source_loc(), "==", ts[0], ts[1]); SetStaticType(&op, arena_->New()); + op.set_value_category(Expression::ValueCategory::Let); return TCResult(new_types); case Operator::Deref: ExpectPointerType(e->source_loc(), "*", ts[0]); SetStaticType(&op, &cast(*ts[0]).type()); + op.set_value_category(Expression::ValueCategory::Var); return TCResult(new_types); case Operator::Ptr: ExpectExactType(e->source_loc(), "*", arena_->New(), ts[0]); SetStaticType(&op, arena_->New()); + op.set_value_category(Expression::ValueCategory::Let); return TCResult(new_types); } break; @@ -677,6 +700,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, &call.argument().static_type()); } SetStaticType(&call, return_type); + call.set_value_category(Expression::ValueCategory::Let); return TCResult(arg_res.types); } default: { @@ -694,10 +718,12 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, ExpectIsConcreteType(fn.return_type().source_loc(), interpreter_.InterpExp(values, &fn.return_type())); SetStaticType(&fn, arena_->New()); + fn.set_value_category(Expression::ValueCategory::Let); return TCResult(types); } case ExpressionKind::StringLiteral: SetStaticType(e, arena_->New()); + e->set_value_category(Expression::ValueCategory::Let); return TCResult(types); case ExpressionKind::IntrinsicExpression: { auto& intrinsic_exp = cast(*e); @@ -712,6 +738,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, arena_->New(), &intrinsic_exp.args().fields()[0]->static_type()); SetStaticType(e, TupleValue::Empty()); + e->set_value_category(Expression::ValueCategory::Let); return TCResult(arg_res.types); } } @@ -720,6 +747,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, case ExpressionKind::StringTypeLiteral: case ExpressionKind::TypeTypeLiteral: case ExpressionKind::ContinuationTypeLiteral: + e->set_value_category(Expression::ValueCategory::Let); SetStaticType(e, arena_->New()); return TCResult(types); } @@ -891,6 +919,10 @@ auto TypeChecker::TypeCheckStmt(Nonnull s, TypeEnv types, auto lhs_res = TypeCheckExp(&assign.lhs(), types, values); ExpectType(s->source_loc(), "assign", &assign.lhs().static_type(), &assign.rhs().static_type()); + if (assign.lhs().value_category() != Expression::ValueCategory::Var) { + FATAL_COMPILATION_ERROR(assign.source_loc()) + << "Cannot assign to rvalue '" << assign.lhs() << "'"; + } return TCResult(lhs_res.types); } case StatementKind::ExpressionStatement: { diff --git a/executable_semantics/testdata/basic_syntax/fail_assign_to_function.carbon b/executable_semantics/testdata/basic_syntax/fail_assign_to_function.carbon new file mode 100644 index 000000000000..95cf86a7625a --- /dev/null +++ b/executable_semantics/testdata/basic_syntax/fail_assign_to_function.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: 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; + +fn F() {} +fn G() {} + +fn Main() -> i32 { + // TODO: this test should not pass. See comments on the handling of + // IdentifierExpression in type_checker.cpp. + F = G; + return 0; +} diff --git a/executable_semantics/testdata/basic_syntax/fail_assign_to_rval.carbon b/executable_semantics/testdata/basic_syntax/fail_assign_to_rval.carbon new file mode 100644 index 000000000000..681ce0d7ed54 --- /dev/null +++ b/executable_semantics/testdata/basic_syntax/fail_assign_to_rval.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/basic_syntax/fail_assign_to_rval.carbon:15: Cannot assign to rvalue '1' + +package ExecutableSemanticsTest api; + +fn Main() -> i32 { + 1 = 0; + return 0; +}