Handle identifier value category properly. (#986)

This commit is contained in:
Geoff Romer
2021-12-15 13:27:36 -08:00
committed by GitHub
parent daf8729a75
commit c89ce1d570
9 changed files with 99 additions and 48 deletions
+10
View File
@@ -80,6 +80,7 @@ cc_library(
":source_location",
":statement",
":static_scope",
":value_category",
"//common:ostream",
"//executable_semantics/common:nonnull",
"@llvm-project//llvm:Support",
@@ -94,6 +95,7 @@ cc_library(
":ast_node",
":paren_contents",
":static_scope",
":value_category",
"//common:indirect_value",
"//common:ostream",
"//executable_semantics/common:arena",
@@ -146,6 +148,7 @@ cc_library(
":expression",
":source_location",
":static_scope",
":value_category",
"//common:ostream",
"//executable_semantics/common:arena",
"//executable_semantics/common:error",
@@ -171,6 +174,7 @@ cc_library(
deps = [
":ast_node",
":source_location",
":value_category",
"//executable_semantics/common:arena",
"//executable_semantics/common:error",
],
@@ -194,9 +198,15 @@ cc_library(
":expression",
":pattern",
":source_location",
":value_category",
"//common:check",
"//common:ostream",
"//executable_semantics/common:arena",
"@llvm-project//llvm:Support",
],
)
cc_library(
name = "value_category",
hdrs = ["value_category.h"],
)
+9
View File
@@ -15,6 +15,7 @@
#include "executable_semantics/ast/source_location.h"
#include "executable_semantics/ast/statement.h"
#include "executable_semantics/ast/static_scope.h"
#include "executable_semantics/ast/value_category.h"
#include "executable_semantics/common/nonnull.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/Compiler.h"
@@ -106,6 +107,8 @@ class GenericBinding : public AstNode {
// and after typechecking it's guaranteed to be true.
auto has_static_type() const -> bool { return static_type_.has_value(); }
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
private:
std::string name_;
Nonnull<Expression*> type_;
@@ -228,6 +231,8 @@ class FunctionDeclaration : public Declaration {
auto body() const -> std::optional<Nonnull<const Block*>> { return body_; }
auto body() -> std::optional<Nonnull<Block*>> { return body_; }
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
private:
std::string name_;
std::vector<Nonnull<GenericBinding*>> deduced_parameters_;
@@ -253,6 +258,8 @@ class ClassDeclaration : public Declaration {
auto name() const -> const std::string& { return name_; }
auto members() const -> llvm::ArrayRef<Nonnull<Member*>> { return members_; }
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
private:
std::string name_;
std::vector<Nonnull<Member*>> members_;
@@ -304,6 +311,8 @@ class ChoiceDeclaration : public Declaration {
return alternatives_;
}
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
private:
std::string name_;
std::vector<Nonnull<AlternativeSignature*>> alternatives_;
+1 -10
View File
@@ -15,6 +15,7 @@
#include "executable_semantics/ast/paren_contents.h"
#include "executable_semantics/ast/source_location.h"
#include "executable_semantics/ast/static_scope.h"
#include "executable_semantics/ast/value_category.h"
#include "executable_semantics/common/arena.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/Compiler.h"
@@ -25,16 +26,6 @@ class Value;
class Expression : public 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 override;
+2
View File
@@ -118,6 +118,8 @@ class BindingPattern : public Pattern {
auto type() const -> const Pattern& { return *type_; }
auto type() -> Pattern& { return *type_; }
auto value_category() const -> ValueCategory { return ValueCategory::Var; }
private:
std::optional<std::string> name_;
Nonnull<Pattern*> type_;
+3
View File
@@ -13,6 +13,7 @@
#include "executable_semantics/ast/pattern.h"
#include "executable_semantics/ast/source_location.h"
#include "executable_semantics/ast/static_scope.h"
#include "executable_semantics/ast/value_category.h"
#include "executable_semantics/common/arena.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/Compiler.h"
@@ -344,6 +345,8 @@ class Continuation : public Statement {
// and after typechecking it's guaranteed to be true.
auto has_static_type() const -> bool { return static_type_.has_value(); }
auto value_category() const -> ValueCategory { return ValueCategory::Var; }
private:
std::string continuation_variable_;
Nonnull<Block*> body_;
+25 -7
View File
@@ -13,6 +13,7 @@
#include "executable_semantics/ast/ast_node.h"
#include "executable_semantics/ast/source_location.h"
#include "executable_semantics/ast/value_category.h"
#include "executable_semantics/common/nonnull.h"
namespace Carbon {
@@ -20,12 +21,19 @@ namespace Carbon {
class Value;
// True if NodeType::ImplementsCarbonNamedEntity is valid and names a type,
// indicating that NodeType implements the NamedEntity interface. This imposes
// the following requirements on NodeType, where `node` is a const instance of
// NodeType:
// indicating that NodeType implements the NamedEntity interface, which means
// it must define the following methods, with contracts as documented.
#if 0
// Returns the static type of an IdentifierExpression that names *this.
auto static_type() const -> const Value&;
// Returns the value category of an IdentifierExpression that names *this.
auto value_category() const -> ValueCategory;
#endif
// NodeType must be derived from AstNode.
//
// - NodeType is derived from AstNode.
// - node.static_type() is well-formed and has type const Value&.
// TODO: consider turning the above documentation into real code, as sketched
// at https://godbolt.org/z/186oEozhc
template <typename T, typename = void>
static constexpr bool ImplementsNamedEntity = false;
@@ -43,9 +51,13 @@ class NamedEntityView {
NamedEntityView(Nonnull<const NodeType*> node)
// Type-erase NodeType, retaining a pointer to the base class AstNode
// and using std::function to encapsulate the ability to call
// the derived class's static_type.
: base_(node), static_type_([](const AstNode& base) -> const Value& {
// the derived class's methods.
: base_(node),
static_type_([](const AstNode& base) -> const Value& {
return llvm::cast<NodeType>(base).static_type();
}),
value_category_([](const AstNode& base) -> ValueCategory {
return llvm::cast<NodeType>(base).value_category();
}) {}
NamedEntityView(const NamedEntityView&) = default;
@@ -59,6 +71,11 @@ class NamedEntityView {
// Returns node->static_type()
auto static_type() const -> const Value& { return static_type_(*base_); }
// Returns node->value_category()
auto value_category() const -> ValueCategory {
return value_category_(*base_);
}
friend auto operator==(const NamedEntityView& lhs,
const NamedEntityView& rhs) {
return lhs.base_ == rhs.base_;
@@ -72,6 +89,7 @@ class NamedEntityView {
private:
Nonnull<const AstNode*> base_;
std::function<const Value&(const AstNode&)> static_type_;
std::function<ValueCategory(const AstNode&)> value_category_;
};
// Maps the names visible in a given scope to the entities they name.
+22
View File
@@ -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
#ifndef EXECUTABLE_SEMANTICS_AST_VALUE_CATEGORY_H_
#define EXECUTABLE_SEMANTICS_AST_VALUE_CATEGORY_H_
namespace Carbon {
// 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,
};
} // namespace Carbon
#endif // EXECUTABLE_SEMANTICS_AST_VALUE_CATEGORY_H_
@@ -426,7 +426,7 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
arg_types.push_back(&arg->static_type());
}
SetStaticType(e, arena_->New<TupleValue>(std::move(arg_types)));
e->set_value_category(Expression::ValueCategory::Let);
e->set_value_category(ValueCategory::Let);
return;
}
case ExpressionKind::StructLiteral: {
@@ -436,7 +436,7 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
arg_types.push_back({arg.name(), &arg.expression().static_type()});
}
SetStaticType(e, arena_->New<StructType>(std::move(arg_types)));
e->set_value_category(Expression::ValueCategory::Let);
e->set_value_category(ValueCategory::Let);
return;
}
case ExpressionKind::StructTypeLiteral: {
@@ -455,7 +455,7 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
} else {
SetStaticType(&struct_type, arena_->New<TypeType>());
}
e->set_value_category(Expression::ValueCategory::Let);
e->set_value_category(ValueCategory::Let);
return;
}
case ExpressionKind::FieldAccessExpression: {
@@ -490,7 +490,7 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
for (auto& method : t_class.methods()) {
if (access.field() == method.name) {
SetStaticType(&access, method.value);
access.set_value_category(Expression::ValueCategory::Let);
access.set_value_category(ValueCategory::Let);
return;
}
}
@@ -512,7 +512,7 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
arena_->New<FunctionType>(
std::vector<Nonnull<const GenericBinding*>>(),
*parameter_types, &aggregate_type));
access.set_value_category(Expression::ValueCategory::Let);
access.set_value_category(ValueCategory::Let);
return;
}
default:
@@ -534,17 +534,15 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
}
}
SetStaticType(&ident, &ident.named_entity().static_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);
ident.set_value_category(ident.named_entity().value_category());
return;
}
case ExpressionKind::IntLiteral:
e->set_value_category(Expression::ValueCategory::Let);
e->set_value_category(ValueCategory::Let);
SetStaticType(e, arena_->New<IntType>());
return;
case ExpressionKind::BoolLiteral:
e->set_value_category(Expression::ValueCategory::Let);
e->set_value_category(ValueCategory::Let);
SetStaticType(e, arena_->New<BoolType>());
return;
case ExpressionKind::PrimitiveOperatorExpression: {
@@ -559,7 +557,7 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
ExpectExactType(e->source_loc(), "negation", arena_->New<IntType>(),
ts[0]);
SetStaticType(&op, arena_->New<IntType>());
op.set_value_category(Expression::ValueCategory::Let);
op.set_value_category(ValueCategory::Let);
return;
case Operator::Add:
ExpectExactType(e->source_loc(), "addition(1)",
@@ -567,7 +565,7 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
ExpectExactType(e->source_loc(), "addition(2)",
arena_->New<IntType>(), ts[1]);
SetStaticType(&op, arena_->New<IntType>());
op.set_value_category(Expression::ValueCategory::Let);
op.set_value_category(ValueCategory::Let);
return;
case Operator::Sub:
ExpectExactType(e->source_loc(), "subtraction(1)",
@@ -575,7 +573,7 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
ExpectExactType(e->source_loc(), "subtraction(2)",
arena_->New<IntType>(), ts[1]);
SetStaticType(&op, arena_->New<IntType>());
op.set_value_category(Expression::ValueCategory::Let);
op.set_value_category(ValueCategory::Let);
return;
case Operator::Mul:
ExpectExactType(e->source_loc(), "multiplication(1)",
@@ -583,7 +581,7 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
ExpectExactType(e->source_loc(), "multiplication(2)",
arena_->New<IntType>(), ts[1]);
SetStaticType(&op, arena_->New<IntType>());
op.set_value_category(Expression::ValueCategory::Let);
op.set_value_category(ValueCategory::Let);
return;
case Operator::And:
ExpectExactType(e->source_loc(), "&&(1)", arena_->New<BoolType>(),
@@ -591,7 +589,7 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
ExpectExactType(e->source_loc(), "&&(2)", arena_->New<BoolType>(),
ts[1]);
SetStaticType(&op, arena_->New<BoolType>());
op.set_value_category(Expression::ValueCategory::Let);
op.set_value_category(ValueCategory::Let);
return;
case Operator::Or:
ExpectExactType(e->source_loc(), "||(1)", arena_->New<BoolType>(),
@@ -599,27 +597,27 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
ExpectExactType(e->source_loc(), "||(2)", arena_->New<BoolType>(),
ts[1]);
SetStaticType(&op, arena_->New<BoolType>());
op.set_value_category(Expression::ValueCategory::Let);
op.set_value_category(ValueCategory::Let);
return;
case Operator::Not:
ExpectExactType(e->source_loc(), "!", arena_->New<BoolType>(), ts[0]);
SetStaticType(&op, arena_->New<BoolType>());
op.set_value_category(Expression::ValueCategory::Let);
op.set_value_category(ValueCategory::Let);
return;
case Operator::Eq:
ExpectExactType(e->source_loc(), "==", ts[0], ts[1]);
SetStaticType(&op, arena_->New<BoolType>());
op.set_value_category(Expression::ValueCategory::Let);
op.set_value_category(ValueCategory::Let);
return;
case Operator::Deref:
ExpectPointerType(e->source_loc(), "*", ts[0]);
SetStaticType(&op, &cast<PointerType>(*ts[0]).type());
op.set_value_category(Expression::ValueCategory::Var);
op.set_value_category(ValueCategory::Var);
return;
case Operator::Ptr:
ExpectExactType(e->source_loc(), "*", arena_->New<TypeType>(), ts[0]);
SetStaticType(&op, arena_->New<TypeType>());
op.set_value_category(Expression::ValueCategory::Let);
op.set_value_category(ValueCategory::Let);
return;
}
break;
@@ -656,7 +654,7 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
&call.argument().static_type());
}
SetStaticType(&call, return_type);
call.set_value_category(Expression::ValueCategory::Let);
call.set_value_category(ValueCategory::Let);
return;
}
default: {
@@ -674,12 +672,12 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
ExpectIsConcreteType(fn.return_type().source_loc(),
interpreter_.InterpExp(values, &fn.return_type()));
SetStaticType(&fn, arena_->New<TypeType>());
fn.set_value_category(Expression::ValueCategory::Let);
fn.set_value_category(ValueCategory::Let);
return;
}
case ExpressionKind::StringLiteral:
SetStaticType(e, arena_->New<StringType>());
e->set_value_category(Expression::ValueCategory::Let);
e->set_value_category(ValueCategory::Let);
return;
case ExpressionKind::IntrinsicExpression: {
auto& intrinsic_exp = cast<IntrinsicExpression>(*e);
@@ -694,7 +692,7 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
arena_->New<StringType>(),
&intrinsic_exp.args().fields()[0]->static_type());
SetStaticType(e, TupleValue::Empty());
e->set_value_category(Expression::ValueCategory::Let);
e->set_value_category(ValueCategory::Let);
return;
}
}
@@ -703,7 +701,7 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
case ExpressionKind::StringTypeLiteral:
case ExpressionKind::TypeTypeLiteral:
case ExpressionKind::ContinuationTypeLiteral:
e->set_value_category(Expression::ValueCategory::Let);
e->set_value_category(ValueCategory::Let);
SetStaticType(e, arena_->New<TypeType>());
return;
case ExpressionKind::UnimplementedExpression:
@@ -861,7 +859,7 @@ void TypeChecker::TypeCheckStmt(Nonnull<Statement*> s, Env values) {
TypeCheckExp(&assign.lhs(), values);
ExpectType(s->source_loc(), "assign", &assign.lhs().static_type(),
&assign.rhs().static_type());
if (assign.lhs().value_category() != Expression::ValueCategory::Var) {
if (assign.lhs().value_category() != ValueCategory::Var) {
FATAL_COMPILATION_ERROR(assign.source_loc())
<< "Cannot assign to rvalue '" << assign.lhs() << "'";
}
@@ -2,12 +2,12 @@
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{executable_semantics} %s 2>&1 | \
// RUN: %{not} %{executable_semantics} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{executable_semantics} --trace %s 2>&1 | \
// RUN: %{not} %{executable_semantics} --trace %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{executable_semantics} %s
// CHECK: result: 0
// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/basic_syntax/fail_assign_to_function.carbon:18: Cannot assign to rvalue 'F'
package ExecutableSemanticsTest api;
@@ -15,8 +15,6 @@ 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;
}