mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Add typechecking of type declarations (#984)
Also explicitly model the types of expressions whose values are class or choice types, and make those be the static_type of the type declaration. This ensures that the static_type of a NamedEntity consistently corresponds to the static type of an IdentifierExpression using that name.
This commit is contained in:
@@ -392,6 +392,8 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
|
||||
case Value::Kind::ContinuationValue:
|
||||
case Value::Kind::StringType:
|
||||
case Value::Kind::StringValue:
|
||||
case Value::Kind::TypeOfClassType:
|
||||
case Value::Kind::TypeOfChoiceType:
|
||||
// TODO: add `CHECK(TypeEqual(type, value->dynamic_type()))`, once we
|
||||
// have Value::dynamic_type.
|
||||
return value;
|
||||
|
||||
@@ -102,6 +102,8 @@ static auto IsConcreteType(Nonnull<const Value*> value) -> bool {
|
||||
case Value::Kind::ContinuationType:
|
||||
case Value::Kind::VariableType:
|
||||
case Value::Kind::StringType:
|
||||
case Value::Kind::TypeOfClassType:
|
||||
case Value::Kind::TypeOfChoiceType:
|
||||
return true;
|
||||
case Value::Kind::AutoType:
|
||||
// `auto` isn't a concrete type, it's a pattern that matches types.
|
||||
@@ -310,6 +312,8 @@ void TypeChecker::ArgumentDeduction(
|
||||
case Value::Kind::BoolType:
|
||||
case Value::Kind::TypeType:
|
||||
case Value::Kind::StringType:
|
||||
case Value::Kind::TypeOfClassType:
|
||||
case Value::Kind::TypeOfChoiceType:
|
||||
ExpectType(source_loc, "argument deduction", param, arg);
|
||||
return;
|
||||
// The rest of these cases should never happen.
|
||||
@@ -374,6 +378,8 @@ auto TypeChecker::Substitute(
|
||||
case Value::Kind::ChoiceType:
|
||||
case Value::Kind::ContinuationType:
|
||||
case Value::Kind::StringType:
|
||||
case Value::Kind::TypeOfClassType:
|
||||
case Value::Kind::TypeOfChoiceType:
|
||||
return type;
|
||||
// The rest of these cases should never happen.
|
||||
case Value::Kind::IntValue:
|
||||
@@ -512,8 +518,9 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e, TypeEnv types,
|
||||
<< "class " << t_class.name() << " does not have a field named "
|
||||
<< access.field();
|
||||
}
|
||||
case Value::Kind::ChoiceType: {
|
||||
const auto& choice = cast<ChoiceType>(aggregate_type);
|
||||
case Value::Kind::TypeOfChoiceType: {
|
||||
const ChoiceType& choice =
|
||||
cast<TypeOfChoiceType>(aggregate_type).choice_type();
|
||||
std::optional<Nonnull<const Value*>> parameter_types =
|
||||
choice.FindAlternative(access.field());
|
||||
if (!parameter_types.has_value()) {
|
||||
@@ -803,27 +810,30 @@ auto TypeChecker::TypeCheckPattern(
|
||||
}
|
||||
case PatternKind::AlternativePattern: {
|
||||
auto& alternative = cast<AlternativePattern>(*p);
|
||||
Nonnull<const Value*> choice_type =
|
||||
interpreter_.InterpExp(values, &alternative.choice_type());
|
||||
if (choice_type->kind() != Value::Kind::ChoiceType) {
|
||||
TypeCheckExp(&alternative.choice_type(), types, values);
|
||||
if (alternative.choice_type().static_type().kind() !=
|
||||
Value::Kind::TypeOfChoiceType) {
|
||||
FATAL_COMPILATION_ERROR(alternative.source_loc())
|
||||
<< "alternative pattern does not name a choice type.";
|
||||
}
|
||||
if (expected) {
|
||||
ExpectExactType(alternative.source_loc(), "alternative pattern",
|
||||
*expected, choice_type);
|
||||
*expected, &alternative.choice_type().static_type());
|
||||
}
|
||||
const ChoiceType& choice_type =
|
||||
cast<TypeOfChoiceType>(alternative.choice_type().static_type())
|
||||
.choice_type();
|
||||
std::optional<Nonnull<const Value*>> parameter_types =
|
||||
cast<ChoiceType>(*choice_type)
|
||||
cast<ChoiceType>(choice_type)
|
||||
.FindAlternative(alternative.alternative_name());
|
||||
if (parameter_types == std::nullopt) {
|
||||
FATAL_COMPILATION_ERROR(alternative.source_loc())
|
||||
<< "'" << alternative.alternative_name()
|
||||
<< "' is not an alternative of " << *choice_type;
|
||||
<< "' is not an alternative of " << choice_type;
|
||||
}
|
||||
TCResult arg_results = TypeCheckPattern(&alternative.arguments(), types,
|
||||
values, *parameter_types);
|
||||
SetStaticType(&alternative, choice_type);
|
||||
SetStaticType(&alternative, &choice_type);
|
||||
SetValue(&alternative, interpreter_.InterpPattern(values, &alternative));
|
||||
return TCResult(arg_results.types);
|
||||
}
|
||||
@@ -1080,34 +1090,45 @@ auto TypeChecker::TypeCheckFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
|
||||
return TCResult(types);
|
||||
}
|
||||
|
||||
auto TypeChecker::TypeOfClassDecl(ClassDeclaration& class_decl,
|
||||
TypeEnv /*types*/, Env ct_top)
|
||||
-> Nonnull<const Value*> {
|
||||
auto TypeChecker::TypeCheckClassDeclaration(
|
||||
Nonnull<ClassDeclaration*> class_decl, TypeEnv types, Env ct_top)
|
||||
-> TCResult {
|
||||
std::vector<NamedValue> fields;
|
||||
std::vector<NamedValue> methods;
|
||||
for (Nonnull<const Member*> m : class_decl.members()) {
|
||||
for (Nonnull<Member*> m : class_decl->members()) {
|
||||
switch (m->kind()) {
|
||||
case MemberKind::FieldMember: {
|
||||
const BindingPattern& binding = cast<FieldMember>(*m).binding();
|
||||
BindingPattern& binding = cast<FieldMember>(*m).binding();
|
||||
if (!binding.name().has_value()) {
|
||||
FATAL_COMPILATION_ERROR(binding.source_loc())
|
||||
<< "Struct members must have names";
|
||||
}
|
||||
const auto* binding_type = dyn_cast<ExpressionPattern>(&binding.type());
|
||||
if (binding_type == nullptr) {
|
||||
FATAL_COMPILATION_ERROR(binding.source_loc())
|
||||
<< "Struct members must have explicit types";
|
||||
}
|
||||
auto type = interpreter_.InterpExp(ct_top, &binding_type->expression());
|
||||
fields.push_back({.name = *binding.name(), .value = type});
|
||||
types = TypeCheckPattern(&binding, types, ct_top, std::nullopt).types;
|
||||
fields.push_back(
|
||||
{.name = *binding.name(), .value = &binding.static_type()});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
SetStaticType(&class_decl,
|
||||
arena_->New<NominalClassType>(
|
||||
class_decl.name(), std::move(fields), std::move(methods)));
|
||||
return &class_decl.static_type();
|
||||
SetStaticType(
|
||||
class_decl,
|
||||
arena_->New<TypeOfClassType>(arena_->New<NominalClassType>(
|
||||
class_decl->name(), std::move(fields), std::move(methods))));
|
||||
return TCResult(types);
|
||||
}
|
||||
|
||||
auto TypeChecker::TypeCheckChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice,
|
||||
TypeEnv types, Env ct_top)
|
||||
-> TCResult {
|
||||
std::vector<NamedValue> alternatives;
|
||||
for (Nonnull<AlternativeSignature*> alternative : choice->alternatives()) {
|
||||
types = TypeCheckExp(&alternative->signature(), types, ct_top).types;
|
||||
auto signature = interpreter_.InterpExp(ct_top, &alternative->signature());
|
||||
alternatives.push_back({.name = alternative->name(), .value = signature});
|
||||
}
|
||||
auto ct = arena_->New<ChoiceType>(choice->name(), std::move(alternatives));
|
||||
SetStaticType(choice, arena_->New<TypeOfChoiceType>(ct));
|
||||
return TCResult(types);
|
||||
}
|
||||
|
||||
static auto GetName(const Declaration& d) -> const std::string& {
|
||||
@@ -1150,13 +1171,11 @@ void TypeChecker::TypeCheckDeclaration(Nonnull<Declaration*> d,
|
||||
values, /*check_body=*/true);
|
||||
return;
|
||||
case DeclarationKind::ClassDeclaration:
|
||||
// TODO
|
||||
TypeCheckClassDeclaration(&cast<ClassDeclaration>(*d), types, values);
|
||||
return;
|
||||
|
||||
case DeclarationKind::ChoiceDeclaration:
|
||||
// TODO
|
||||
TypeCheckChoiceDeclaration(&cast<ChoiceDeclaration>(*d), types, values);
|
||||
return;
|
||||
|
||||
case DeclarationKind::VariableDeclaration: {
|
||||
auto& var = cast<VariableDeclaration>(*d);
|
||||
// Signals a type error if the initializing expression does not have
|
||||
@@ -1193,27 +1212,23 @@ void TypeChecker::TopLevel(Nonnull<Declaration*> d, TypeCheckContext* tops) {
|
||||
|
||||
case DeclarationKind::ClassDeclaration: {
|
||||
auto& class_decl = cast<ClassDeclaration>(*d);
|
||||
auto st = TypeOfClassDecl(class_decl, tops->types, tops->values);
|
||||
AllocationId a = interpreter_.AllocateValue(st);
|
||||
TypeCheckClassDeclaration(&class_decl, tops->types, tops->values);
|
||||
const auto& type = cast<TypeOfClassType>(class_decl.static_type());
|
||||
const NominalClassType& value = type.class_type();
|
||||
AllocationId a = interpreter_.AllocateValue(&value);
|
||||
tops->values.Set(class_decl.name(), a); // Is this obsolete?
|
||||
tops->types.Set(class_decl.name(), st);
|
||||
tops->types.Set(class_decl.name(), &type);
|
||||
break;
|
||||
}
|
||||
|
||||
case DeclarationKind::ChoiceDeclaration: {
|
||||
auto& choice = cast<ChoiceDeclaration>(*d);
|
||||
std::vector<NamedValue> alts;
|
||||
for (Nonnull<const AlternativeSignature*> alternative :
|
||||
choice.alternatives()) {
|
||||
auto t =
|
||||
interpreter_.InterpExp(tops->values, &alternative->signature());
|
||||
alts.push_back({.name = alternative->name(), .value = t});
|
||||
}
|
||||
auto ct = arena_->New<ChoiceType>(choice.name(), std::move(alts));
|
||||
SetStaticType(&choice, ct);
|
||||
AllocationId a = interpreter_.AllocateValue(ct);
|
||||
TypeCheckChoiceDeclaration(&choice, tops->types, tops->values);
|
||||
const auto& type = cast<TypeOfChoiceType>(choice.static_type());
|
||||
const ChoiceType& value = type.choice_type();
|
||||
AllocationId a = interpreter_.AllocateValue(&value);
|
||||
tops->values.Set(choice.name(), a); // Is this obsolete?
|
||||
tops->types.Set(choice.name(), ct);
|
||||
tops->types.Set(choice.name(), &type);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -96,8 +96,13 @@ class TypeChecker {
|
||||
Nonnull<Statement*> body, TypeEnv types, Env values)
|
||||
-> Match::Clause;
|
||||
|
||||
auto TypeOfClassDecl(ClassDeclaration& class_decl, TypeEnv /*types*/,
|
||||
Env ct_top) -> Nonnull<const Value*>;
|
||||
// Equivalent to TypeCheckExp, but operates on the AST rooted at class_decl.
|
||||
auto TypeCheckClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
|
||||
TypeEnv types, Env ct_top) -> TCResult;
|
||||
|
||||
// Equivalent to TypeCheckExp, but operates on the AST rooted at choice_decl.
|
||||
auto TypeCheckChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice,
|
||||
TypeEnv types, Env ct_top) -> TCResult;
|
||||
|
||||
auto TopLevel(std::vector<Nonnull<Declaration*>>* fs) -> TypeCheckContext;
|
||||
void TopLevel(Nonnull<Declaration*> d, TypeCheckContext* tops);
|
||||
|
||||
@@ -255,6 +255,14 @@ void Value::Print(llvm::raw_ostream& out) const {
|
||||
out.write_escaped(cast<StringValue>(*this).value());
|
||||
out << "\"";
|
||||
break;
|
||||
case Value::Kind::TypeOfClassType:
|
||||
out << "typeof(" << cast<TypeOfClassType>(*this).class_type().name()
|
||||
<< ")";
|
||||
break;
|
||||
case Value::Kind::TypeOfChoiceType:
|
||||
out << "typeof(" << cast<TypeOfChoiceType>(*this).choice_type().name()
|
||||
<< ")";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,6 +358,12 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2) -> bool {
|
||||
case Value::Kind::VariableType:
|
||||
return &cast<VariableType>(*t1).binding() ==
|
||||
&cast<VariableType>(*t2).binding();
|
||||
case Value::Kind::TypeOfClassType:
|
||||
return TypeEqual(&cast<TypeOfClassType>(*t1).class_type(),
|
||||
&cast<TypeOfClassType>(*t2).class_type());
|
||||
case Value::Kind::TypeOfChoiceType:
|
||||
return TypeEqual(&cast<TypeOfChoiceType>(*t1).choice_type(),
|
||||
&cast<TypeOfChoiceType>(*t2).choice_type());
|
||||
default:
|
||||
FATAL() << "TypeEqual used to compare non-type values\n"
|
||||
<< *t1 << "\n"
|
||||
@@ -420,6 +434,8 @@ auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2,
|
||||
case Value::Kind::ContinuationType:
|
||||
case Value::Kind::VariableType:
|
||||
case Value::Kind::StringType:
|
||||
case Value::Kind::TypeOfClassType:
|
||||
case Value::Kind::TypeOfChoiceType:
|
||||
return TypeEqual(v1, v2);
|
||||
case Value::Kind::NominalClassValue:
|
||||
case Value::Kind::AlternativeValue:
|
||||
|
||||
@@ -58,6 +58,8 @@ class Value {
|
||||
ContinuationValue, // A first-class continuation value.
|
||||
StringType,
|
||||
StringValue,
|
||||
TypeOfClassType,
|
||||
TypeOfChoiceType,
|
||||
};
|
||||
|
||||
Value(const Value&) = delete;
|
||||
@@ -560,6 +562,44 @@ class StringValue : public Value {
|
||||
std::string value_;
|
||||
};
|
||||
|
||||
// The type of an expression whose value is a class type. Currently there is no
|
||||
// way to explicitly name such a type in Carbon code, but we are tentatively
|
||||
// using `typeof(ClassName)` as the debug-printing format, in anticipation of
|
||||
// something like that becoming valid Carbon syntax.
|
||||
class TypeOfClassType : public Value {
|
||||
public:
|
||||
explicit TypeOfClassType(Nonnull<const NominalClassType*> class_type)
|
||||
: Value(Kind::TypeOfClassType), class_type_(class_type) {}
|
||||
|
||||
static auto classof(const Value* value) -> bool {
|
||||
return value->kind() == Kind::TypeOfClassType;
|
||||
}
|
||||
|
||||
auto class_type() const -> const NominalClassType& { return *class_type_; }
|
||||
|
||||
private:
|
||||
Nonnull<const NominalClassType*> class_type_;
|
||||
};
|
||||
|
||||
// The type of an expression whose value is a choice type. Currently there is no
|
||||
// way to explicitly name such a type in Carbon code, but we are tentatively
|
||||
// using `typeof(ChoiceName)` as the debug-printing format, in anticipation of
|
||||
// something like that becoming valid Carbon syntax.
|
||||
class TypeOfChoiceType : public Value {
|
||||
public:
|
||||
explicit TypeOfChoiceType(Nonnull<const ChoiceType*> choice_type)
|
||||
: Value(Kind::TypeOfChoiceType), choice_type_(choice_type) {}
|
||||
|
||||
static auto classof(const Value* value) -> bool {
|
||||
return value->kind() == Kind::TypeOfChoiceType;
|
||||
}
|
||||
|
||||
auto choice_type() const -> const ChoiceType& { return *choice_type_; }
|
||||
|
||||
private:
|
||||
Nonnull<const ChoiceType*> choice_type_;
|
||||
};
|
||||
|
||||
auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2) -> bool;
|
||||
auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2,
|
||||
SourceLocation source_loc) -> bool;
|
||||
|
||||
Reference in New Issue
Block a user