mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Implement static name resolution (#958)
This doesn't actually use the results of name resolution, but it does verify that they are present. Also ensures that name resolution and type checking are applied to deduced function parameters and the implicit call to `Main()`. Co-authored-by: Jon Meow <46229924+jonmeow@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
co-authored by
Jon Meow
parent
dc5e62fc7a
commit
17e0a1afb9
@@ -26,6 +26,8 @@ struct AST {
|
||||
std::vector<Nonnull<Declaration*>> declarations;
|
||||
// Names declared at the top level of the file.
|
||||
StaticScope static_scope;
|
||||
// Synthesized call to `Main`. Injected after parsing.
|
||||
std::optional<Nonnull<CallExpression*>> main_call;
|
||||
};
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
@@ -91,6 +91,7 @@ struct GenericBinding : public virtual AstNode, public NamedEntity {
|
||||
|
||||
auto name() const -> const std::string& { return name_; }
|
||||
auto type() const -> const Expression& { return *type_; }
|
||||
auto type() -> Expression& { return *type_; }
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
@@ -201,6 +202,9 @@ class FunctionDeclaration : public Declaration {
|
||||
-> llvm::ArrayRef<Nonnull<const GenericBinding*>> {
|
||||
return deduced_parameters_;
|
||||
}
|
||||
auto deduced_parameters() -> llvm::ArrayRef<Nonnull<GenericBinding*>> {
|
||||
return deduced_parameters_;
|
||||
}
|
||||
auto param_pattern() const -> const TuplePattern& { return *param_pattern_; }
|
||||
auto param_pattern() -> TuplePattern& { return *param_pattern_; }
|
||||
auto return_term() const -> const ReturnTerm& { return return_term_; }
|
||||
@@ -262,6 +266,7 @@ class AlternativeSignature : public virtual AstNode, public NamedEntity {
|
||||
|
||||
auto name() const -> const std::string& { return name_; }
|
||||
auto signature() const -> const Expression& { return *signature_; }
|
||||
auto signature() -> Expression& { return *signature_; }
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
@@ -285,6 +290,9 @@ class ChoiceDeclaration : public Declaration {
|
||||
-> llvm::ArrayRef<Nonnull<const AlternativeSignature*>> {
|
||||
return alternatives_;
|
||||
}
|
||||
auto alternatives() -> llvm::ArrayRef<Nonnull<AlternativeSignature*>> {
|
||||
return alternatives_;
|
||||
}
|
||||
|
||||
// Contains the alternatives.
|
||||
auto static_scope() const -> const StaticScope& { return static_scope_; }
|
||||
@@ -297,6 +305,9 @@ class ChoiceDeclaration : public Declaration {
|
||||
};
|
||||
|
||||
// Global variable definition implements the Declaration concept.
|
||||
//
|
||||
// TODO: this should not inherit from NamedEntity, because names should
|
||||
// always resolve to the underlying binding, not the VariableDeclaration.
|
||||
class VariableDeclaration : public Declaration {
|
||||
public:
|
||||
VariableDeclaration(SourceLocation source_loc,
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
namespace Carbon {
|
||||
|
||||
class Value;
|
||||
class NamedEntity;
|
||||
|
||||
class Expression : public virtual AstNode {
|
||||
public:
|
||||
@@ -129,8 +130,25 @@ 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_; }
|
||||
|
||||
// Sets the value returned by named_entity. Can be called only once,
|
||||
// during name resolution.
|
||||
void set_named_entity(Nonnull<const NamedEntity*> named_entity) {
|
||||
CHECK(!named_entity_.has_value());
|
||||
named_entity_ = named_entity;
|
||||
}
|
||||
|
||||
// Returns true if set_named_entity has been called. Should be used only
|
||||
// for debugging purposes.
|
||||
// TODO: remove this once we no longer need the CHECKs that use it.
|
||||
auto has_named_entity() const -> bool { return named_entity_.has_value(); }
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
std::optional<Nonnull<const NamedEntity*>> named_entity_;
|
||||
};
|
||||
|
||||
class FieldAccessExpression : public Expression {
|
||||
|
||||
@@ -48,7 +48,7 @@ class Member : public virtual AstNode, public NamedEntity {
|
||||
|
||||
class FieldMember : public Member {
|
||||
public:
|
||||
FieldMember(SourceLocation source_loc, Nonnull<const BindingPattern*> binding)
|
||||
FieldMember(SourceLocation source_loc, Nonnull<BindingPattern*> binding)
|
||||
: AstNode(AstNodeKind::FieldMember, source_loc), binding_(binding) {}
|
||||
|
||||
static auto classof(const AstNode* node) -> bool {
|
||||
@@ -56,12 +56,13 @@ class FieldMember : public Member {
|
||||
}
|
||||
|
||||
auto binding() const -> const BindingPattern& { return *binding_; }
|
||||
auto binding() -> BindingPattern& { return *binding_; }
|
||||
|
||||
private:
|
||||
// TODO: split this into a non-optional name and a type, initialized by
|
||||
// a constructor that takes a BindingPattern and handles errors like a
|
||||
// missing name.
|
||||
Nonnull<const BindingPattern*> binding_;
|
||||
Nonnull<BindingPattern*> binding_;
|
||||
};
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
@@ -18,4 +18,37 @@ void StaticScope::Add(std::string name, Nonnull<const NamedEntity*> entity) {
|
||||
}
|
||||
}
|
||||
|
||||
auto StaticScope::Resolve(const std::string& name,
|
||||
SourceLocation source_loc) const
|
||||
-> Nonnull<const NamedEntity*> {
|
||||
std::optional<Nonnull<const NamedEntity*>> result =
|
||||
TryResolve(name, source_loc);
|
||||
if (!result.has_value()) {
|
||||
FATAL_COMPILATION_ERROR(source_loc)
|
||||
<< "'" << name << "' is not declared in this scope";
|
||||
}
|
||||
return *result;
|
||||
}
|
||||
|
||||
auto StaticScope::TryResolve(const std::string& name,
|
||||
SourceLocation source_loc) const
|
||||
-> std::optional<Nonnull<const NamedEntity*>> {
|
||||
auto it = declared_names_.find(name);
|
||||
if (it != declared_names_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
std::optional<Nonnull<const NamedEntity*>> 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();
|
||||
}
|
||||
result = parent_result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
@@ -28,18 +28,38 @@ class NamedEntity : public virtual AstNode {
|
||||
}
|
||||
};
|
||||
|
||||
// The set of declared names in a scope. This is not aware of child scopes, but
|
||||
// does include directions to parent or related scopes for lookup purposes.
|
||||
// Maps the names visible in a given scope to the entities they name.
|
||||
// A scope may have parent scopes, whose names will also be visible in the
|
||||
// child scope.
|
||||
class StaticScope {
|
||||
public:
|
||||
// Defines `name` to be `entity` in this scope, or reports a compilation error
|
||||
// if `name` is already defined in this scope.
|
||||
void Add(std::string name, Nonnull<const NamedEntity*> entity);
|
||||
|
||||
// Make `parent` a parent of this scope.
|
||||
// REQUIRES: `parent` is not already a parent of this scope.
|
||||
void AddParent(Nonnull<StaticScope*> parent) {
|
||||
parent_scopes_.push_back(parent);
|
||||
}
|
||||
|
||||
// Returns the nearest definition of `name` in the ancestor graph of this
|
||||
// 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*>;
|
||||
|
||||
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*>>;
|
||||
|
||||
// Maps locally declared names to their entities.
|
||||
std::unordered_map<std::string, Nonnull<const NamedEntity*>> declared_names_;
|
||||
|
||||
// A list of scopes used for name lookup within this scope.
|
||||
// TODO: This is unused, but is intended for name lookup cross-scope.
|
||||
std::vector<Nonnull<StaticScope*>> parent_scopes_;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user