From 021e83a4f4328f68be34038bc72eed6e1f4f31b5 Mon Sep 17 00:00:00 2001 From: Geoff Romer Date: Tue, 7 Dec 2021 12:27:47 -0800 Subject: [PATCH] Ensure all NamedEntities have static types (#968) --- executable_semantics/ast/declaration.h | 13 ++++ executable_semantics/ast/statement.h | 16 +++++ .../interpreter/type_checker.cpp | 67 +++++++------------ .../interpreter/type_checker.h | 2 +- 4 files changed, 53 insertions(+), 45 deletions(-) diff --git a/executable_semantics/ast/declaration.h b/executable_semantics/ast/declaration.h index d7b7da6de9dc..259f48904b45 100644 --- a/executable_semantics/ast/declaration.h +++ b/executable_semantics/ast/declaration.h @@ -91,9 +91,22 @@ struct GenericBinding : public virtual AstNode, public NamedEntity { auto type() const -> const Expression& { return *type_; } auto type() -> Expression& { return *type_; } + // The static type of the binding. Cannot be called before typechecking. + auto static_type() const -> const Value& { return **static_type_; } + + // Sets the static type of the binding. Can only be called once, during + // typechecking. + void set_static_type(Nonnull type) { static_type_ = type; } + + // Returns whether the static type has been set. Should only be called + // during typechecking: before typechecking it's guaranteed to be false, + // and after typechecking it's guaranteed to be true. + auto has_static_type() const -> bool { return static_type_.has_value(); } + private: std::string name_; Nonnull type_; + std::optional> static_type_; }; // The syntactic representation of a function declaration's return type. diff --git a/executable_semantics/ast/statement.h b/executable_semantics/ast/statement.h index badba400f32b..ae5e5fc0f38f 100644 --- a/executable_semantics/ast/statement.h +++ b/executable_semantics/ast/statement.h @@ -326,9 +326,25 @@ class Continuation : public Statement, public NamedEntity { auto body() const -> const Block& { return *body_; } auto body() -> Block& { return *body_; } + // The static type of the continuation. Cannot be called before typechecking. + // + // This will always be ContinuationType, but we must set it dynamically in + // the typechecker because this code can't depend on ContinuationType. + auto static_type() const -> const Value& { return **static_type_; } + + // Sets the static type of the continuation. Can only be called once, + // during typechecking. + void set_static_type(Nonnull type) { static_type_ = type; } + + // Returns whether the static type has been set. Should only be called + // during typechecking: before typechecking it's guaranteed to be false, + // and after typechecking it's guaranteed to be true. + auto has_static_type() const -> bool { return static_type_.has_value(); } + private: std::string continuation_variable_; Nonnull body_; + std::optional> static_type_; }; // A run statement. diff --git a/executable_semantics/interpreter/type_checker.cpp b/executable_semantics/interpreter/type_checker.cpp index ef490e2d0922..e4fb754d72da 100644 --- a/executable_semantics/interpreter/type_checker.cpp +++ b/executable_semantics/interpreter/type_checker.cpp @@ -25,45 +25,15 @@ using llvm::isa; namespace Carbon { -// Sets the static type of `expression`. Can be called multiple times on +// Sets the static type of `*object`. Can be called multiple times on // the same node, so long as the types are the same on each call. -static void SetStaticType(Nonnull expression, - Nonnull type) { - if (expression->has_static_type()) { - CHECK(TypeEqual(&expression->static_type(), type)); +// T must have static_type, has_static_type, and set_static_type methods. +template +static void SetStaticType(Nonnull object, Nonnull type) { + if (object->has_static_type()) { + CHECK(TypeEqual(&object->static_type(), type)); } else { - expression->set_static_type(type); - } -} - -// Sets the static type of `pattern`. Can be called multiple times on -// the same node, so long as the types are the same on each call. -static void SetStaticType(Nonnull pattern, - Nonnull type) { - if (pattern->has_static_type()) { - CHECK(TypeEqual(&pattern->static_type(), type)); - } else { - pattern->set_static_type(type); - } -} - -// Sets the static type of `definition`. Can be called multiple times on -// the same node, so long as the types are the same on each call. -static void SetStaticType(Nonnull definition, - Nonnull type) { - if (definition->has_static_type()) { - CHECK(TypeEqual(&definition->static_type(), type)); - } else { - definition->set_static_type(type); - } -} - -static void SetStaticType(Nonnull return_term, - Nonnull type) { - if (return_term->has_static_type()) { - CHECK(TypeEqual(&return_term->static_type(), type)); - } else { - return_term->set_static_type(type); + object->set_static_type(type); } } @@ -959,7 +929,8 @@ auto TypeChecker::TypeCheckStmt(Nonnull s, TypeEnv types, case StatementKind::Continuation: { auto& cont = cast(*s); TypeCheckStmt(&cont.body(), types, values); - types.Set(cont.continuation_variable(), arena_->New()); + SetStaticType(&cont, arena_->New()); + types.Set(cont.continuation_variable(), &cont.static_type()); return TCResult(types); } case StatementKind::Run: { @@ -1058,7 +1029,8 @@ auto TypeChecker::TypeCheckFunctionDeclaration(Nonnull f, for (Nonnull deduced : f->deduced_parameters()) { TypeCheckExp(&deduced->type(), types, values); // auto t = interpreter_.InterpExp(values, deduced.type); - types.Set(deduced->name(), arena_->New(deduced->name())); + SetStaticType(deduced, arena_->New(deduced->name())); + types.Set(deduced->name(), &deduced->static_type()); AllocationId a = interpreter_.AllocateValue(*types.Get(deduced->name())); values.Set(deduced->name(), a); } @@ -1109,7 +1081,7 @@ auto TypeChecker::TypeCheckFunctionDeclaration(Nonnull f, return TCResult(types); } -auto TypeChecker::TypeOfClassDecl(const ClassDeclaration& class_decl, +auto TypeChecker::TypeOfClassDecl(ClassDeclaration& class_decl, TypeEnv /*types*/, Env ct_top) -> Nonnull { std::vector fields; @@ -1133,8 +1105,10 @@ auto TypeChecker::TypeOfClassDecl(const ClassDeclaration& class_decl, } } } - return arena_->New(class_decl.name(), std::move(fields), - std::move(methods)); + SetStaticType(&class_decl, + arena_->New( + class_decl.name(), std::move(fields), std::move(methods))); + return &class_decl.static_type(); } static auto GetName(const Declaration& d) -> const std::string& { @@ -1219,7 +1193,7 @@ void TypeChecker::TopLevel(Nonnull d, TypeCheckContext* tops) { } case DeclarationKind::ClassDeclaration: { - const auto& class_decl = cast(*d); + auto& class_decl = cast(*d); auto st = TypeOfClassDecl(class_decl, tops->types, tops->values); AllocationId a = interpreter_.AllocateValue(st); tops->values.Set(class_decl.name(), a); // Is this obsolete? @@ -1228,7 +1202,7 @@ void TypeChecker::TopLevel(Nonnull d, TypeCheckContext* tops) { } case DeclarationKind::ChoiceDeclaration: { - const auto& choice = cast(*d); + auto& choice = cast(*d); std::vector alts; for (Nonnull alternative : choice.alternatives()) { @@ -1237,6 +1211,7 @@ void TypeChecker::TopLevel(Nonnull d, TypeCheckContext* tops) { alts.push_back({.name = alternative->name(), .value = t}); } auto ct = arena_->New(choice.name(), std::move(alts)); + SetStaticType(&choice, ct); AllocationId a = interpreter_.AllocateValue(ct); tops->values.Set(choice.name(), a); // Is this obsolete? tops->types.Set(choice.name(), ct); @@ -1249,9 +1224,13 @@ void TypeChecker::TopLevel(Nonnull d, TypeCheckContext* tops) { // compile-time symbol table. Expression& type = cast(var.binding().type()).expression(); + tops->types = TypeCheckPattern(&var.binding(), tops->types, tops->values, + std::nullopt) + .types; Nonnull declared_type = interpreter_.InterpExp(tops->values, &type); tops->types.Set(*var.binding().name(), declared_type); + SetStaticType(&var, declared_type); break; } } diff --git a/executable_semantics/interpreter/type_checker.h b/executable_semantics/interpreter/type_checker.h index b75134a8969c..e538a332bda6 100644 --- a/executable_semantics/interpreter/type_checker.h +++ b/executable_semantics/interpreter/type_checker.h @@ -94,7 +94,7 @@ class TypeChecker { Nonnull body, TypeEnv types, Env values) -> Match::Clause; - auto TypeOfClassDecl(const ClassDeclaration& class_decl, TypeEnv /*types*/, + auto TypeOfClassDecl(ClassDeclaration& class_decl, TypeEnv /*types*/, Env ct_top) -> Nonnull; auto TopLevel(std::vector>* fs) -> TypeCheckContext;