Use resolved names in typechecker (#974)

As a byproduct, replace NamedEntity with a type-erasing wrapper NamedEntityView, eliminate virtual inheritance from the AST, and eliminate interfaces from gen_rtti.
This commit is contained in:
Geoff Romer
2021-12-13 16:35:30 -08:00
committed by GitHub
parent fd45b089e9
commit 8e6c209a28
14 changed files with 208 additions and 240 deletions
+1
View File
@@ -93,6 +93,7 @@ cc_library(
deps = [
":ast_node",
":paren_contents",
":static_scope",
"//common:indirect_value",
"//common:ostream",
"//executable_semantics/common:arena",
-38
View File
@@ -35,8 +35,6 @@ namespace Carbon {
// The definitions of `InheritsFromFoo` and `FooKind` are generated from
// ast_rtti.txt, and are implicitly provided by this header.
//
// When inheriting from this class, the inheritance must me marked `virtual`.
//
// TODO: To support generic traversal, add children() method, and ensure that
// all AstNodes are reachable from a root AstNode.
class AstNode {
@@ -75,40 +73,4 @@ class AstNode {
} // namespace Carbon
// Ensure that LLVM casts from AstNode use dynamic_cast, because static_cast
// doesn't work with a virtual base class.
namespace llvm {
template <typename To>
struct cast_convert_val<To, const Carbon::AstNode*, const Carbon::AstNode*> {
using ResultType = typename cast_retty<To, const Carbon::AstNode*>::ret_type;
static auto doit(const Carbon::AstNode* node) -> ResultType {
return dynamic_cast<ResultType>(node);
}
};
template <typename To>
struct cast_convert_val<To, Carbon::AstNode*, Carbon::AstNode*> {
using ResultType = typename cast_retty<To, Carbon::AstNode*>::ret_type;
static auto doit(Carbon::AstNode* node) -> ResultType {
return dynamic_cast<ResultType>(node);
}
};
template <typename To>
struct cast_convert_val<To, const Carbon::AstNode, const Carbon::AstNode> {
using ResultType = typename cast_retty<To, const Carbon::AstNode>::ret_type;
static auto doit(const Carbon::AstNode& node) -> ResultType {
return dynamic_cast<ResultType>(node);
}
};
template <typename To>
struct cast_convert_val<To, Carbon::AstNode, Carbon::AstNode> {
using ResultType = typename cast_retty<To, Carbon::AstNode>::ret_type;
static auto doit(Carbon::AstNode& node) -> ResultType {
return dynamic_cast<ResultType>(node);
}
};
} // namespace llvm
#endif // EXECUTABLE_SEMANTICS_AST_AST_NODE_H_
+6 -7
View File
@@ -3,19 +3,18 @@
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
root class AstNode;
interface class NamedEntity : AstNode;
abstract class Pattern : AstNode;
class AutoPattern : Pattern;
class BindingPattern : Pattern, NamedEntity;
class BindingPattern : Pattern;
class TuplePattern : Pattern;
class AlternativePattern : Pattern;
class ExpressionPattern : Pattern;
abstract class Declaration : AstNode;
class FunctionDeclaration : Declaration, NamedEntity;
class ClassDeclaration : Declaration, NamedEntity;
class ChoiceDeclaration : Declaration, NamedEntity;
class FunctionDeclaration : Declaration;
class ClassDeclaration : Declaration;
class ChoiceDeclaration : Declaration;
class VariableDeclaration : Declaration;
class GenericBinding : AstNode, NamedEntity;
class GenericBinding : AstNode;
class AlternativeSignature : AstNode;
abstract class Statement : AstNode;
class ExpressionStatement : Statement;
@@ -28,7 +27,7 @@ abstract class Statement : AstNode;
class Break : Statement;
class Continue : Statement;
class Match : Statement;
class Continuation : Statement, NamedEntity;
class Continuation : Statement;
class Run : Statement;
class Await : Statement;
abstract class Expression : AstNode;
+20 -11
View File
@@ -29,7 +29,7 @@ namespace Carbon {
// every concrete derived class must have a corresponding enumerator
// in `Kind`; see https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html for
// details.
class Declaration : public virtual AstNode {
class Declaration : public AstNode {
public:
~Declaration() override = 0;
@@ -65,7 +65,8 @@ class Declaration : public virtual AstNode {
// Constructs a Declaration representing syntax at the given line number.
// `kind` must be the enumerator corresponding to the most-derived type being
// constructed.
Declaration() = default;
Declaration(AstNodeKind kind, SourceLocation source_loc)
: AstNode(kind, source_loc) {}
private:
std::optional<Nonnull<const Value*>> static_type_;
@@ -73,8 +74,10 @@ class Declaration : public virtual AstNode {
// TODO: expand the kinds of things that can be deduced parameters.
// For now, only generic parameters are supported.
struct GenericBinding : public virtual AstNode, public NamedEntity {
class GenericBinding : public AstNode {
public:
using ImplementsCarbonNamedEntity = void;
GenericBinding(SourceLocation source_loc, std::string name,
Nonnull<Expression*> type)
: AstNode(AstNodeKind::GenericBinding, source_loc),
@@ -188,14 +191,16 @@ class ReturnTerm {
SourceLocation source_loc_;
};
class FunctionDeclaration : public Declaration, public NamedEntity {
class FunctionDeclaration : public Declaration {
public:
using ImplementsCarbonNamedEntity = void;
FunctionDeclaration(SourceLocation source_loc, std::string name,
std::vector<Nonnull<GenericBinding*>> deduced_params,
Nonnull<TuplePattern*> param_pattern,
ReturnTerm return_term,
std::optional<Nonnull<Block*>> body)
: AstNode(AstNodeKind::FunctionDeclaration, source_loc),
: Declaration(AstNodeKind::FunctionDeclaration, source_loc),
name_(std::move(name)),
deduced_parameters_(std::move(deduced_params)),
param_pattern_(param_pattern),
@@ -231,11 +236,13 @@ class FunctionDeclaration : public Declaration, public NamedEntity {
std::optional<Nonnull<Block*>> body_;
};
class ClassDeclaration : public Declaration, public NamedEntity {
class ClassDeclaration : public Declaration {
public:
using ImplementsCarbonNamedEntity = void;
ClassDeclaration(SourceLocation source_loc, std::string name,
std::vector<Nonnull<Member*>> members)
: AstNode(AstNodeKind::ClassDeclaration, source_loc),
: Declaration(AstNodeKind::ClassDeclaration, source_loc),
name_(std::move(name)),
members_(std::move(members)) {}
@@ -251,7 +258,7 @@ class ClassDeclaration : public Declaration, public NamedEntity {
std::vector<Nonnull<Member*>> members_;
};
class AlternativeSignature : public virtual AstNode {
class AlternativeSignature : public AstNode {
public:
AlternativeSignature(SourceLocation source_loc, std::string name,
Nonnull<Expression*> signature)
@@ -274,11 +281,13 @@ class AlternativeSignature : public virtual AstNode {
Nonnull<Expression*> signature_;
};
class ChoiceDeclaration : public Declaration, public NamedEntity {
class ChoiceDeclaration : public Declaration {
public:
using ImplementsCarbonNamedEntity = void;
ChoiceDeclaration(SourceLocation source_loc, std::string name,
std::vector<Nonnull<AlternativeSignature*>> alternatives)
: AstNode(AstNodeKind::ChoiceDeclaration, source_loc),
: Declaration(AstNodeKind::ChoiceDeclaration, source_loc),
name_(std::move(name)),
alternatives_(std::move(alternatives)) {}
@@ -306,7 +315,7 @@ class VariableDeclaration : public Declaration {
VariableDeclaration(SourceLocation source_loc,
Nonnull<BindingPattern*> binding,
Nonnull<Expression*> initializer)
: AstNode(AstNodeKind::VariableDeclaration, source_loc),
: Declaration(AstNodeKind::VariableDeclaration, source_loc),
binding_(binding),
initializer_(initializer) {}
+30 -28
View File
@@ -14,6 +14,7 @@
#include "executable_semantics/ast/ast_node.h"
#include "executable_semantics/ast/paren_contents.h"
#include "executable_semantics/ast/source_location.h"
#include "executable_semantics/ast/static_scope.h"
#include "executable_semantics/common/arena.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/Compiler.h"
@@ -21,9 +22,8 @@
namespace Carbon {
class Value;
class NamedEntity;
class Expression : public virtual AstNode {
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
@@ -76,7 +76,8 @@ class Expression : public virtual AstNode {
// Constructs an Expression representing syntax at the given line number.
// `kind` must be the enumerator corresponding to the most-derived type being
// constructed.
Expression() = default;
Expression(AstNodeKind kind, SourceLocation source_loc)
: AstNode(kind, source_loc) {}
private:
std::optional<Nonnull<const Value*>> static_type_;
@@ -121,7 +122,7 @@ auto ToString(Operator op) -> std::string_view;
class IdentifierExpression : public Expression {
public:
explicit IdentifierExpression(SourceLocation source_loc, std::string name)
: AstNode(AstNodeKind::IdentifierExpression, source_loc),
: Expression(AstNodeKind::IdentifierExpression, source_loc),
name_(std::move(name)) {}
static auto classof(const AstNode* node) -> bool {
@@ -130,15 +131,15 @@ class IdentifierExpression : public Expression {
auto name() const -> const std::string& { return name_; }
// Returns the NamedEntity this identifier refers to. Cannot be called before
// name resolution.
auto named_entity() const -> const NamedEntity& { return **named_entity_; }
// Returns the NamedEntityView this identifier refers to. Cannot be called
// before name resolution.
auto named_entity() const -> const NamedEntityView& { return *named_entity_; }
// Sets the value returned by named_entity. Can be called only once,
// during name resolution.
void set_named_entity(Nonnull<const NamedEntity*> named_entity) {
void set_named_entity(NamedEntityView named_entity) {
CHECK(!named_entity_.has_value());
named_entity_ = named_entity;
named_entity_ = std::move(named_entity);
}
// Returns true if set_named_entity has been called. Should be used only
@@ -148,7 +149,7 @@ class IdentifierExpression : public Expression {
private:
std::string name_;
std::optional<Nonnull<const NamedEntity*>> named_entity_;
std::optional<NamedEntityView> named_entity_;
};
class FieldAccessExpression : public Expression {
@@ -156,7 +157,7 @@ class FieldAccessExpression : public Expression {
explicit FieldAccessExpression(SourceLocation source_loc,
Nonnull<Expression*> aggregate,
std::string field)
: AstNode(AstNodeKind::FieldAccessExpression, source_loc),
: Expression(AstNodeKind::FieldAccessExpression, source_loc),
aggregate_(aggregate),
field_(std::move(field)) {}
@@ -178,7 +179,7 @@ class IndexExpression : public Expression {
explicit IndexExpression(SourceLocation source_loc,
Nonnull<Expression*> aggregate,
Nonnull<Expression*> offset)
: AstNode(AstNodeKind::IndexExpression, source_loc),
: Expression(AstNodeKind::IndexExpression, source_loc),
aggregate_(aggregate),
offset_(offset) {}
@@ -199,7 +200,7 @@ class IndexExpression : public Expression {
class IntLiteral : public Expression {
public:
explicit IntLiteral(SourceLocation source_loc, int value)
: AstNode(AstNodeKind::IntLiteral, source_loc), value_(value) {}
: Expression(AstNodeKind::IntLiteral, source_loc), value_(value) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromIntLiteral(node->kind());
@@ -214,7 +215,7 @@ class IntLiteral : public Expression {
class BoolLiteral : public Expression {
public:
explicit BoolLiteral(SourceLocation source_loc, bool value)
: AstNode(AstNodeKind::BoolLiteral, source_loc), value_(value) {}
: Expression(AstNodeKind::BoolLiteral, source_loc), value_(value) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromBoolLiteral(node->kind());
@@ -229,7 +230,7 @@ class BoolLiteral : public Expression {
class StringLiteral : public Expression {
public:
explicit StringLiteral(SourceLocation source_loc, std::string value)
: AstNode(AstNodeKind::StringLiteral, source_loc),
: Expression(AstNodeKind::StringLiteral, source_loc),
value_(std::move(value)) {}
static auto classof(const AstNode* node) -> bool {
@@ -245,7 +246,7 @@ class StringLiteral : public Expression {
class StringTypeLiteral : public Expression {
public:
explicit StringTypeLiteral(SourceLocation source_loc)
: AstNode(AstNodeKind::StringTypeLiteral, source_loc) {}
: Expression(AstNodeKind::StringTypeLiteral, source_loc) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromStringTypeLiteral(node->kind());
@@ -259,7 +260,7 @@ class TupleLiteral : public Expression {
explicit TupleLiteral(SourceLocation source_loc,
std::vector<Nonnull<Expression*>> fields)
: AstNode(AstNodeKind::TupleLiteral, source_loc),
: Expression(AstNodeKind::TupleLiteral, source_loc),
fields_(std::move(fields)) {}
static auto classof(const AstNode* node) -> bool {
@@ -285,7 +286,8 @@ class StructLiteral : public Expression {
public:
explicit StructLiteral(SourceLocation loc,
std::vector<FieldInitializer> fields)
: AstNode(AstNodeKind::StructLiteral, loc), fields_(std::move(fields)) {
: Expression(AstNodeKind::StructLiteral, loc),
fields_(std::move(fields)) {
CHECK(!fields_.empty())
<< "`{}` is represented as a StructTypeLiteral, not a StructLiteral.";
}
@@ -311,7 +313,7 @@ class StructTypeLiteral : public Expression {
explicit StructTypeLiteral(SourceLocation loc,
std::vector<FieldInitializer> fields)
: AstNode(AstNodeKind::StructTypeLiteral, loc),
: Expression(AstNodeKind::StructTypeLiteral, loc),
fields_(std::move(fields)) {}
static auto classof(const AstNode* node) -> bool {
@@ -330,7 +332,7 @@ class PrimitiveOperatorExpression : public Expression {
explicit PrimitiveOperatorExpression(
SourceLocation source_loc, Operator op,
std::vector<Nonnull<Expression*>> arguments)
: AstNode(AstNodeKind::PrimitiveOperatorExpression, source_loc),
: Expression(AstNodeKind::PrimitiveOperatorExpression, source_loc),
op_(op),
arguments_(std::move(arguments)) {}
@@ -356,7 +358,7 @@ class CallExpression : public Expression {
explicit CallExpression(SourceLocation source_loc,
Nonnull<Expression*> function,
Nonnull<Expression*> argument)
: AstNode(AstNodeKind::CallExpression, source_loc),
: Expression(AstNodeKind::CallExpression, source_loc),
function_(function),
argument_(argument) {}
@@ -379,7 +381,7 @@ class FunctionTypeLiteral : public Expression {
explicit FunctionTypeLiteral(SourceLocation source_loc,
Nonnull<Expression*> parameter,
Nonnull<Expression*> return_type)
: AstNode(AstNodeKind::FunctionTypeLiteral, source_loc),
: Expression(AstNodeKind::FunctionTypeLiteral, source_loc),
parameter_(parameter),
return_type_(return_type) {}
@@ -400,7 +402,7 @@ class FunctionTypeLiteral : public Expression {
class BoolTypeLiteral : public Expression {
public:
explicit BoolTypeLiteral(SourceLocation source_loc)
: AstNode(AstNodeKind::BoolTypeLiteral, source_loc) {}
: Expression(AstNodeKind::BoolTypeLiteral, source_loc) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromBoolTypeLiteral(node->kind());
@@ -410,7 +412,7 @@ class BoolTypeLiteral : public Expression {
class IntTypeLiteral : public Expression {
public:
explicit IntTypeLiteral(SourceLocation source_loc)
: AstNode(AstNodeKind::IntTypeLiteral, source_loc) {}
: Expression(AstNodeKind::IntTypeLiteral, source_loc) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromIntTypeLiteral(node->kind());
@@ -420,7 +422,7 @@ class IntTypeLiteral : public Expression {
class ContinuationTypeLiteral : public Expression {
public:
explicit ContinuationTypeLiteral(SourceLocation source_loc)
: AstNode(AstNodeKind::ContinuationTypeLiteral, source_loc) {}
: Expression(AstNodeKind::ContinuationTypeLiteral, source_loc) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromContinuationTypeLiteral(node->kind());
@@ -430,7 +432,7 @@ class ContinuationTypeLiteral : public Expression {
class TypeTypeLiteral : public Expression {
public:
explicit TypeTypeLiteral(SourceLocation source_loc)
: AstNode(AstNodeKind::TypeTypeLiteral, source_loc) {}
: Expression(AstNodeKind::TypeTypeLiteral, source_loc) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromTypeTypeLiteral(node->kind());
@@ -446,7 +448,7 @@ class IntrinsicExpression : public Expression {
explicit IntrinsicExpression(std::string_view intrinsic_name,
Nonnull<TupleLiteral*> args,
SourceLocation source_loc)
: AstNode(AstNodeKind::IntrinsicExpression, source_loc),
: Expression(AstNodeKind::IntrinsicExpression, source_loc),
intrinsic_(FindIntrinsic(intrinsic_name, source_loc)),
args_(args) {}
@@ -480,7 +482,7 @@ class UnimplementedExpression : public Expression {
template <typename... Children>
UnimplementedExpression(SourceLocation source_loc, std::string label,
Children... children)
: AstNode(AstNodeKind::UnimplementedExpression, source_loc),
: Expression(AstNodeKind::UnimplementedExpression, source_loc),
label_(std::move(label)) {
AddChildren(children...);
}
+4 -3
View File
@@ -23,7 +23,7 @@ namespace Carbon {
// every concrete derived class must have a corresponding enumerator
// in `Kind`; see https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html for
// details.
class Member : public virtual AstNode {
class Member : public AstNode {
public:
~Member() override = 0;
@@ -43,13 +43,14 @@ class Member : public virtual AstNode {
}
protected:
Member() = default;
Member(AstNodeKind kind, SourceLocation source_loc)
: AstNode(kind, source_loc) {}
};
class FieldMember : public Member {
public:
FieldMember(SourceLocation source_loc, Nonnull<BindingPattern*> binding)
: AstNode(AstNodeKind::FieldMember, source_loc), binding_(binding) {}
: Member(AstNodeKind::FieldMember, source_loc), binding_(binding) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromFieldMember(node->kind());
+1 -1
View File
@@ -89,7 +89,7 @@ static auto RequireFieldAccess(Nonnull<Expression*> alternative)
AlternativePattern::AlternativePattern(SourceLocation source_loc,
Nonnull<Expression*> alternative,
Nonnull<TuplePattern*> arguments)
: AstNode(AstNodeKind::AlternativePattern, source_loc),
: Pattern(AstNodeKind::AlternativePattern, source_loc),
choice_type_(&RequireFieldAccess(alternative).aggregate()),
alternative_name_(RequireFieldAccess(alternative).field()),
arguments_(arguments) {}
+11 -8
View File
@@ -29,7 +29,7 @@ class Value;
// every concrete derived class must have a corresponding enumerator
// in `Kind`; see https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html for
// details.
class Pattern : public virtual AstNode {
class Pattern : public AstNode {
public:
Pattern(const Pattern&) = delete;
auto operator=(const Pattern&) -> Pattern& = delete;
@@ -76,7 +76,8 @@ class Pattern : public virtual AstNode {
// Constructs a Pattern representing syntax at the given line number.
// `kind` must be the enumerator corresponding to the most-derived type being
// constructed.
Pattern() = default;
Pattern(AstNodeKind kind, SourceLocation source_loc)
: AstNode(kind, source_loc) {}
private:
std::optional<Nonnull<const Value*>> static_type_;
@@ -87,7 +88,7 @@ class Pattern : public virtual AstNode {
class AutoPattern : public Pattern {
public:
explicit AutoPattern(SourceLocation source_loc)
: AstNode(AstNodeKind::AutoPattern, source_loc) {}
: Pattern(AstNodeKind::AutoPattern, source_loc) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromAutoPattern(node->kind());
@@ -96,11 +97,13 @@ class AutoPattern : public Pattern {
// A pattern that matches a value of a specified type, and optionally binds
// a name to it.
class BindingPattern : public Pattern, public NamedEntity {
class BindingPattern : public Pattern {
public:
using ImplementsCarbonNamedEntity = void;
BindingPattern(SourceLocation source_loc, std::optional<std::string> name,
Nonnull<Pattern*> type)
: AstNode(AstNodeKind::BindingPattern, source_loc),
: Pattern(AstNodeKind::BindingPattern, source_loc),
name_(std::move(name)),
type_(type) {}
@@ -124,7 +127,7 @@ class BindingPattern : public Pattern, public NamedEntity {
class TuplePattern : public Pattern {
public:
TuplePattern(SourceLocation source_loc, std::vector<Nonnull<Pattern*>> fields)
: AstNode(AstNodeKind::TuplePattern, source_loc),
: Pattern(AstNodeKind::TuplePattern, source_loc),
fields_(std::move(fields)) {}
static auto classof(const AstNode* node) -> bool {
@@ -170,7 +173,7 @@ class AlternativePattern : public Pattern {
Nonnull<Expression*> choice_type,
std::string alternative_name,
Nonnull<TuplePattern*> arguments)
: AstNode(AstNodeKind::AlternativePattern, source_loc),
: Pattern(AstNodeKind::AlternativePattern, source_loc),
choice_type_(choice_type),
alternative_name_(std::move(alternative_name)),
arguments_(arguments) {}
@@ -204,7 +207,7 @@ class AlternativePattern : public Pattern {
class ExpressionPattern : public Pattern {
public:
explicit ExpressionPattern(Nonnull<Expression*> expression)
: AstNode(AstNodeKind::ExpressionPattern, expression->source_loc()),
: Pattern(AstNodeKind::ExpressionPattern, expression->source_loc()),
expression_(expression) {}
static auto classof(const AstNode* node) -> bool {
+19 -16
View File
@@ -21,7 +21,7 @@ namespace Carbon {
class FunctionDeclaration;
class Statement : public virtual AstNode {
class Statement : public AstNode {
public:
~Statement() override = 0;
@@ -39,13 +39,14 @@ class Statement : public virtual AstNode {
}
protected:
Statement() = default;
Statement(AstNodeKind kind, SourceLocation source_loc)
: AstNode(kind, source_loc) {}
};
class Block : public Statement {
public:
Block(SourceLocation source_loc, std::vector<Nonnull<Statement*>> statements)
: AstNode(AstNodeKind::Block, source_loc),
: Statement(AstNodeKind::Block, source_loc),
statements_(std::move(statements)) {}
static auto classof(const AstNode* node) -> bool {
@@ -67,7 +68,7 @@ class ExpressionStatement : public Statement {
public:
ExpressionStatement(SourceLocation source_loc,
Nonnull<Expression*> expression)
: AstNode(AstNodeKind::ExpressionStatement, source_loc),
: Statement(AstNodeKind::ExpressionStatement, source_loc),
expression_(expression) {}
static auto classof(const AstNode* node) -> bool {
@@ -85,7 +86,7 @@ class Assign : public Statement {
public:
Assign(SourceLocation source_loc, Nonnull<Expression*> lhs,
Nonnull<Expression*> rhs)
: AstNode(AstNodeKind::Assign, source_loc), lhs_(lhs), rhs_(rhs) {}
: Statement(AstNodeKind::Assign, source_loc), lhs_(lhs), rhs_(rhs) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromAssign(node->kind());
@@ -105,7 +106,7 @@ class VariableDefinition : public Statement {
public:
VariableDefinition(SourceLocation source_loc, Nonnull<Pattern*> pattern,
Nonnull<Expression*> init)
: AstNode(AstNodeKind::VariableDefinition, source_loc),
: Statement(AstNodeKind::VariableDefinition, source_loc),
pattern_(pattern),
init_(init) {}
@@ -127,7 +128,7 @@ class If : public Statement {
public:
If(SourceLocation source_loc, Nonnull<Expression*> condition,
Nonnull<Block*> then_block, std::optional<Nonnull<Block*>> else_block)
: AstNode(AstNodeKind::If, source_loc),
: Statement(AstNodeKind::If, source_loc),
condition_(condition),
then_block_(then_block),
else_block_(else_block) {}
@@ -157,7 +158,7 @@ class Return : public Statement {
: Return(source_loc, arena->New<TupleLiteral>(source_loc), true) {}
Return(SourceLocation source_loc, Nonnull<Expression*> expression,
bool is_omitted_expression)
: AstNode(AstNodeKind::Return, source_loc),
: Statement(AstNodeKind::Return, source_loc),
expression_(expression),
is_omitted_expression_(is_omitted_expression) {}
@@ -194,7 +195,7 @@ class While : public Statement {
public:
While(SourceLocation source_loc, Nonnull<Expression*> condition,
Nonnull<Block*> body)
: AstNode(AstNodeKind::While, source_loc),
: Statement(AstNodeKind::While, source_loc),
condition_(condition),
body_(body) {}
@@ -215,7 +216,7 @@ class While : public Statement {
class Break : public Statement {
public:
explicit Break(SourceLocation source_loc)
: AstNode(AstNodeKind::Break, source_loc) {}
: Statement(AstNodeKind::Break, source_loc) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromBreak(node->kind());
@@ -242,7 +243,7 @@ class Break : public Statement {
class Continue : public Statement {
public:
explicit Continue(SourceLocation source_loc)
: AstNode(AstNodeKind::Continue, source_loc) {}
: Statement(AstNodeKind::Continue, source_loc) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromContinue(node->kind());
@@ -285,7 +286,7 @@ class Match : public Statement {
Match(SourceLocation source_loc, Nonnull<Expression*> expression,
std::vector<Clause> clauses)
: AstNode(AstNodeKind::Match, source_loc),
: Statement(AstNodeKind::Match, source_loc),
expression_(expression),
clauses_(std::move(clauses)) {}
@@ -308,11 +309,13 @@ class Match : public Statement {
// __continuation <continuation_variable> {
// <body>
// }
class Continuation : public Statement, public NamedEntity {
class Continuation : public Statement {
public:
using ImplementsCarbonNamedEntity = void;
Continuation(SourceLocation source_loc, std::string continuation_variable,
Nonnull<Block*> body)
: AstNode(AstNodeKind::Continuation, source_loc),
: Statement(AstNodeKind::Continuation, source_loc),
continuation_variable_(std::move(continuation_variable)),
body_(body) {}
@@ -353,7 +356,7 @@ class Continuation : public Statement, public NamedEntity {
class Run : public Statement {
public:
Run(SourceLocation source_loc, Nonnull<Expression*> argument)
: AstNode(AstNodeKind::Run, source_loc), argument_(argument) {}
: Statement(AstNodeKind::Run, source_loc), argument_(argument) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromRun(node->kind());
@@ -372,7 +375,7 @@ class Run : public Statement {
class Await : public Statement {
public:
explicit Await(SourceLocation source_loc)
: AstNode(AstNodeKind::Await, source_loc) {}
: Statement(AstNodeKind::Await, source_loc) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromAwait(node->kind());
+10 -13
View File
@@ -8,22 +8,18 @@
namespace Carbon {
NamedEntity::~NamedEntity() = default;
void StaticScope::Add(std::string name, Nonnull<const NamedEntity*> entity) {
void StaticScope::Add(std::string name, NamedEntityView entity) {
auto [it, success] = declared_names_.insert({name, entity});
if (!success && it->second != entity) {
FATAL_COMPILATION_ERROR(entity->source_loc())
FATAL_COMPILATION_ERROR(entity.base().source_loc())
<< "Duplicate name `" << name << "` also found at "
<< it->second->source_loc();
<< it->second.base().source_loc();
}
}
auto StaticScope::Resolve(const std::string& name,
SourceLocation source_loc) const
-> Nonnull<const NamedEntity*> {
std::optional<Nonnull<const NamedEntity*>> result =
TryResolve(name, source_loc);
SourceLocation source_loc) const -> NamedEntityView {
std::optional<NamedEntityView> result = TryResolve(name, source_loc);
if (!result.has_value()) {
FATAL_COMPILATION_ERROR(source_loc) << "could not resolve '" << name << "'";
}
@@ -32,19 +28,20 @@ auto StaticScope::Resolve(const std::string& name,
auto StaticScope::TryResolve(const std::string& name,
SourceLocation source_loc) const
-> std::optional<Nonnull<const NamedEntity*>> {
-> std::optional<NamedEntityView> {
auto it = declared_names_.find(name);
if (it != declared_names_.end()) {
return it->second;
}
std::optional<Nonnull<const NamedEntity*>> result;
std::optional<NamedEntityView> result;
for (Nonnull<const StaticScope*> parent : parent_scopes_) {
auto parent_result = parent->TryResolve(name, source_loc);
if (parent_result.has_value() && result.has_value() &&
*parent_result != *result) {
FATAL_COMPILATION_ERROR(source_loc)
<< "'" << name << "' is ambiguous between " << (*result)->source_loc()
<< " and " << (*parent_result)->source_loc();
<< "'" << name << "' is ambiguous between "
<< result->base().source_loc() << " and "
<< parent_result->base().source_loc();
}
result = parent_result;
}
+56 -10
View File
@@ -5,6 +5,7 @@
#ifndef EXECUTABLE_SEMANTICS_AST_STATIC_SCOPE_H_
#define EXECUTABLE_SEMANTICS_AST_STATIC_SCOPE_H_
#include <functional>
#include <string>
#include <unordered_map>
#include <variant>
@@ -16,16 +17,61 @@
namespace Carbon {
class NamedEntity : public virtual AstNode {
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:
//
// - NodeType is derived from AstNode.
// - node.static_type() is well-formed and has type const Value&.
template <typename T, typename = void>
static constexpr bool ImplementsNamedEntity = false;
template <typename T>
static constexpr bool
ImplementsNamedEntity<T, typename T::ImplementsCarbonNamedEntity> = true;
// Non-owning type-erased wrapper around a const NodeType* `node`, where
// NodeType implements the NamedEntity interface.
class NamedEntityView {
public:
~NamedEntity() override = 0;
template <typename NodeType,
typename = std::enable_if_t<ImplementsNamedEntity<NodeType>>>
// NOLINTNEXTLINE(google-explicit-constructor)
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& {
return llvm::cast<NodeType>(base).static_type();
}) {}
NamedEntity() = default;
NamedEntityView(const NamedEntityView&) = default;
NamedEntityView(NamedEntityView&&) = default;
auto operator=(const NamedEntityView&) -> NamedEntityView& = default;
auto operator=(NamedEntityView&&) -> NamedEntityView& = default;
// TODO: This is unused, but is intended for casts after lookup.
auto kind() const -> NamedEntityKind {
return static_cast<NamedEntityKind>(root_kind());
// Returns `node` as an instance of the base class AstNode.
auto base() const -> const AstNode& { return *base_; }
// Returns node->static_type()
auto static_type() const -> const Value& { return static_type_(*base_); }
friend auto operator==(const NamedEntityView& lhs,
const NamedEntityView& rhs) {
return lhs.base_ == rhs.base_;
}
friend auto operator!=(const NamedEntityView& lhs,
const NamedEntityView& rhs) {
return lhs.base_ != rhs.base_;
}
private:
Nonnull<const AstNode*> base_;
std::function<const Value&(const AstNode&)> static_type_;
};
// Maps the names visible in a given scope to the entities they name.
@@ -35,7 +81,7 @@ class StaticScope {
public:
// Defines `name` to be `entity` in this scope, or reports a compilation error
// if `name` is already defined to be a different entity in this scope.
void Add(std::string name, Nonnull<const NamedEntity*> entity);
void Add(std::string name, NamedEntityView entity);
// Make `parent` a parent of this scope.
// REQUIRES: `parent` is not already a parent of this scope.
@@ -47,17 +93,17 @@ class StaticScope {
// scope, or reports a compilation error at `source_loc` there isn't exactly
// one such definition.
auto Resolve(const std::string& name, SourceLocation source_loc) const
-> Nonnull<const NamedEntity*>;
-> NamedEntityView;
private:
// Equivalent to Resolve, but returns `nullopt` instead of raising an error
// if no definition can be found. Still raises a compilation error if more
// than one definition is found.
auto TryResolve(const std::string& name, SourceLocation source_loc) const
-> std::optional<Nonnull<const NamedEntity*>>;
-> std::optional<NamedEntityView>;
// Maps locally declared names to their entities.
std::unordered_map<std::string, Nonnull<const NamedEntity*>> declared_names_;
std::unordered_map<std::string, NamedEntityView> declared_names_;
// A list of scopes used for name lookup within this scope.
std::vector<Nonnull<StaticScope*>> parent_scopes_;
+35 -92
View File
@@ -2,42 +2,44 @@
"""Generates C++ header to support LLVM-style RTTI for a class hierarchy.
Takes as input a file describing the class hierarchy which can consist of
four different kinds of classes: a *root* class is the base of a class
hierarchy, meaning that it doesn't inherit from any other class. *Abstract* and
*interface* classes are non-root classes that cannot be instantiated, and
*concrete* classes are classes that can be instantiated.
# Background
A non-root class C must inherit from exactly one parent, which can be a root or
abstract class, and can also inherit from any number of interfaces, but each
interface's parent must be an ancestor of C.
A C++ class hierarchy supported by this script consists of *abstract* classes,
which can be inherited from but can't be instantiated, and *concrete* classes,
which can be instantiated but can't be inherited from. Classes can inherit from
at most one other class in the hierarchy; a class that doesn't inherit from
any other class is called a *root* class, and it cannot be concrete.
# Input format
This script's input file declares every class in the hierarchy, and specifies
the parent of each non-root class. The input file consists of comment lines
starting with `#`, whitespace lines, and one `;`-terminated line for each class.
The core of a line is `class` followed by the class name. `class` can be
prefixed with `root` or `abstract` to specify the corresponding kind of class;
if there is no prefix, the class is concrete. If the class is not a root class,
the name is followed by `:` and then the name of the class it inherits from. A
class cannot inherit from a class defined later in the file.
The input file consists of comment lines starting with `#`, whitespace lines,
and one `;`-terminated line for each class. The core of a line is `class`
followed by the class name. `class` can be prefixed with `root`, `abstract`,
or `interface` to specify the corresponding kind of class; if there is no
prefix, the class is concrete. If the class is not a root class, the name is
followed by `:` and then a comma-separated list of the names of the classes
it inherits from. The first entry in the list is the parent, and the others
are interfaces. A class cannot inherit from classes defined later in the file.
For example:
root class R;
abstract class A : R;
interface class I : R;
abstract class B : R, I;
abstract class B : R;
class C : A;
class D : B;
class E : A, I;
class E : A;
For each non-concrete class `Foo`, the generated header file will contain
# Output
For each abstract class `Foo`, the generated header file will contain
`enum class FooKind`, which has an enumerator for each concrete class derived
from `Foo`, with a name that matches the concrete class name.
For each non-root class `Foo` whose root class is `Root`, the generated header
file will also contain a function `bool InheritsFromFoo(RootKind kind)`,
which returns true if the value of `kind` corresponds to a class that is
derived from `Foo`. This function can be used to implement `Foo::classof`.
file will also contain a function `bool InheritsFromFoo(RootKind kind)`, which
returns true if the value of `kind` corresponds to a class that is derived from
`Foo`. This function can be used to implement `Foo::classof`.
All enumerators that represent the same concrete class will have the same
numeric value, so you can use `static_cast` to convert between the enum types
@@ -64,11 +66,9 @@ class Class:
Attributes set at construction:
name: The class name.
kind: The class kind (root, abstract, interface, or concrete)
kind: The class kind (root, abstract, or concrete)
ancestors: A list of Class objects representing the class's ancestors,
starting with the root and ending with the current class's parent.
interfaces: A list of Class objects representing the interfaces the class
inherits from.
_children: A list of Class objects representing the classes that are
derived directly from this one.
@@ -78,18 +78,15 @@ class Class:
id_range (ROOT and ABSTRACT only): A pair such that a Class
object `c` represents a concrete class derived from `self` if and only
if c.id >= self.id_range[0] and c.id < self.id_range[1].
leaf_ids (INTERFACE only): A set containing the IDs of all concrete
classes derived from this interface.
leaves (ROOT only): A list of all concrete classes derived from this one,
indexed by their IDs.
"""
Kind = enum.Enum("Kind", "ROOT ABSTRACT INTERFACE CONCRETE")
Kind = enum.Enum("Kind", "ROOT ABSTRACT CONCRETE")
def __init__(self, name, kind, parent, interfaces):
def __init__(self, name, kind, parent):
self.name = name
self.kind = kind
self.interfaces = interfaces
assert (parent is None) == (kind == Class.Kind.ROOT)
if parent is None:
@@ -102,8 +99,6 @@ class Class:
self.id_range = None
elif self.kind == Class.Kind.ABSTRACT:
self.id_range = None
elif self.kind == Class.Kind.INTERFACE:
self.leaf_ids = set()
else:
self.id = None
@@ -113,9 +108,6 @@ class Class:
if parent:
parent._children.append(self)
for interface in self.interfaces:
interface._children.append(self)
def Parent(self):
"""Returns this Class's parent."""
return self.ancestors[-1]
@@ -130,9 +122,9 @@ class Class:
def _RegisterLeaf(self, leaf):
"""Records that `leaf` is derived from self.
Also recursively updates the parent and interfaces of self. leaf.id must
already be populated, and leaves must be registered in order of ID. This
operation is idempotent."""
Also recursively updates the parent of self. leaf.id must already be
populated, and leaves must be registered in order of ID. This operation
is idempotent."""
already_visited = False
if self.kind == Class.Kind.ROOT:
if leaf.id == len(self.leaves):
@@ -155,17 +147,9 @@ class Class:
assert self.id_range[1] == leaf.id + 1
already_visited = True
elif self.kind == Class.Kind.INTERFACE:
if leaf.id in self.leaf_ids:
already_visited = True
else:
self.leaf_ids.add(leaf.id)
if not already_visited:
if self.kind != Class.Kind.ROOT:
self.Parent()._RegisterLeaf(leaf)
for interface in self.interfaces:
interface._RegisterLeaf(leaf)
def Finalize(self):
"""Populates additional attributes for `self` and derived Classes.
@@ -176,7 +160,7 @@ class Class:
if self.kind == Class.Kind.CONCRETE:
self.id = len(self.Root().leaves)
self._RegisterLeaf(self)
elif self.kind in [Class.Kind.ROOT, Class.Kind.ABSTRACT]:
else:
for child in self._children:
child.Finalize()
@@ -185,7 +169,6 @@ _LINE_PATTERN = r"""(?P<prefix> \w*) \s*
class \s+
(?P<name> \w+)
(?: \s*:\s* (?P<parent> \w+)
(?: , (?P<interfaces> .*) )?
)?
;$"""
@@ -210,8 +193,6 @@ def main():
kind = Class.Kind.ROOT
elif prefix == "abstract":
kind = Class.Kind.ABSTRACT
elif prefix == "interface":
kind = Class.Kind.INTERFACE
else:
sys.exit(f"Unrecognized class prefix '{prefix}' on line {line_num}")
@@ -225,36 +206,14 @@ def main():
sys.exit(f"Unknown class '{parent_name}' on line {line_num}")
if parent.kind == Class.Kind.CONCRETE:
sys.exit(f"{parent.name} cannot be a parent on line {line_num}")
elif parent.kind == Class.Kind.INTERFACE:
if kind != Class.Kind.INTERFACE:
sys.exit(
"Interface cannot be parent of non-interface on"
+ f" line {line_num}"
)
else:
if kind != Class.Kind.ROOT:
sys.exit(
f"Non-root class must have a parent on line {line_num}"
)
interfaces = []
if match_result.group("interfaces"):
for unstripped_name in match_result.group("interfaces").split(","):
interface_name = unstripped_name.strip()
interface = classes[interface_name]
if not interface:
sys.exit(
f"Unknown class '{interface_name}' on line {line_num}"
)
if interface.kind != Class.Kind.INTERFACE:
sys.exit(
f"'{interface_name}' used as interface on"
+ f" line {line_num}"
)
interfaces.append(interface)
classes[match_result.group("name")] = Class(
match_result.group("name"), kind, parent, interfaces
match_result.group("name"), kind, parent
)
for node in classes.values():
@@ -275,16 +234,13 @@ def main():
for node in classes.values():
if node.kind != Class.Kind.CONCRETE:
if node.kind == Class.Kind.INTERFACE:
ids = sorted(node.leaf_ids)
else:
ids = range(node.id_range[0], node.id_range[1])
ids = range(node.id_range[0], node.id_range[1])
print(f"enum class {node.name}Kind {{")
for id in ids:
print(f" {node.Root().leaves[id].name} = {id},")
print("};\n")
if node.kind != Class.Kind.ROOT:
if node.kind in [Class.Kind.ABSTRACT, Class.Kind.CONCRETE]:
print(
f"inline bool InheritsFrom{node.name}({node.Root().name}Kind"
+ " kind) {"
@@ -305,19 +261,6 @@ def main():
+ f"::{range_end}"
)
print(" ;")
elif node.kind == Class.Kind.INTERFACE:
print(" switch(kind) {")
is_empty = True
for id in sorted(node.leaf_ids):
print(
f" case {node.Root().name}Kind::"
+ f"{node.Root().leaves[id].name}:"
)
is_empty = False
if not is_empty:
print(" return true;")
print(" default:")
print(" return false;\n }")
elif node.kind == Class.Kind.CONCRETE:
print(
f" return kind == {node.Root().name}Kind::{node.name};"
@@ -543,19 +543,21 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e, TypeEnv types,
}
case ExpressionKind::IdentifierExpression: {
auto& ident = cast<IdentifierExpression>(*e);
CHECK(ident.has_named_entity()) << "Identifier '" << *e << "' at "
<< e->source_loc() << " was not resolved";
std::optional<Nonnull<const Value*>> 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())
<< "could not find `" << ident.name() << "`";
if (ident.named_entity().base().kind() ==
AstNodeKind::FunctionDeclaration) {
const auto& function =
cast<FunctionDeclaration>(ident.named_entity().base());
if (!function.has_static_type()) {
CHECK(function.return_term().is_auto());
FATAL_COMPILATION_ERROR(ident.source_loc())
<< "Function calls itself, but has a deduced return type";
}
}
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);
return TCResult(types);
}
case ExpressionKind::IntLiteral:
e->set_value_category(Expression::ValueCategory::Let);
@@ -7,7 +7,7 @@
// 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/function/auto_return/fail_direct_recurse.carbon:18: could not find `Recurse`
// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/function/auto_return/fail_direct_recurse.carbon:18: Function calls itself, but has a deduced return type
package ExecutableSemanticsTest api;