mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-06 10:54:49 +01:00
Support for class definitions with static member functions. (#3305)
This includes being able to define a class that was previously forward-declared, and being able to define a member function out-of-line that was previously declared inside a class. No support for fields or methods yet, and a class definition doesn't yet cause the class to be treated as a complete type. --------- Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
This commit is contained in:
co-authored by
Jon Ross-Perkins
parent
3b82ea96db
commit
b01dfb3f93
@@ -89,6 +89,13 @@ auto Context::DiagnoseNameNotFound(Parse::Node parse_node,
|
||||
emitter_->Emit(parse_node, NameNotFound, semantics_ir_->GetString(name_id));
|
||||
}
|
||||
|
||||
auto Context::NoteIncompleteClass(SemIR::ClassDeclaration class_decl,
|
||||
DiagnosticBuilder& builder) -> void {
|
||||
CARBON_DIAGNOSTIC(ClassForwardDeclaredHere, Note,
|
||||
"Class was forward declared here.");
|
||||
builder.Note(class_decl.parse_node, ClassForwardDeclaredHere);
|
||||
}
|
||||
|
||||
auto Context::AddNameToLookup(Parse::Node name_node, SemIR::StringId name_id,
|
||||
SemIR::NodeId target_id) -> void {
|
||||
if (current_scope().names.insert(name_id).second) {
|
||||
@@ -128,7 +135,9 @@ auto Context::LookupName(Parse::Node parse_node, SemIR::StringId name_id,
|
||||
}
|
||||
}
|
||||
|
||||
auto Context::PushScope() -> void { scope_stack_.push_back({}); }
|
||||
auto Context::PushScope(SemIR::NameScopeId scope_id) -> void {
|
||||
scope_stack_.push_back({.scope_id = scope_id});
|
||||
}
|
||||
|
||||
auto Context::PopScope() -> void {
|
||||
auto scope = scope_stack_.pop_back_val();
|
||||
@@ -394,11 +403,10 @@ class TypeCompleter {
|
||||
case SemIR::ClassDeclaration::Kind:
|
||||
// TODO: Support class definitions and complete class types.
|
||||
if (diagnoser_) {
|
||||
CARBON_DIAGNOSTIC(ClassForwardDeclaredHere, Note,
|
||||
"Class was forward declared here.");
|
||||
(*diagnoser_)()
|
||||
.Note(type_node.parse_node(), ClassForwardDeclaredHere)
|
||||
.Emit();
|
||||
auto builder = (*diagnoser_)();
|
||||
context_.NoteIncompleteClass(type_node.As<SemIR::ClassDeclaration>(),
|
||||
builder);
|
||||
builder.Emit();
|
||||
}
|
||||
return false;
|
||||
|
||||
|
||||
@@ -66,12 +66,21 @@ class Context {
|
||||
auto DiagnoseNameNotFound(Parse::Node parse_node, SemIR::StringId name_id)
|
||||
-> void;
|
||||
|
||||
// Adds a note to a diagnostic explaining that a class is incomplete.
|
||||
auto NoteIncompleteClass(SemIR::ClassDeclaration class_decl,
|
||||
DiagnosticBuilder& builder) -> void;
|
||||
|
||||
// Pushes a new scope onto scope_stack_.
|
||||
auto PushScope() -> void;
|
||||
auto PushScope(SemIR::NameScopeId scope_id = SemIR::NameScopeId::Invalid)
|
||||
-> void;
|
||||
|
||||
// Pops the top scope from scope_stack_, cleaning up names from name_lookup_.
|
||||
auto PopScope() -> void;
|
||||
|
||||
auto current_scope_id() const -> SemIR::NameScopeId {
|
||||
return scope_stack_.back().scope_id;
|
||||
}
|
||||
|
||||
// Follows NameReference nodes to find the value named by a given node.
|
||||
auto FollowNameReferences(SemIR::NodeId node_id) -> SemIR::NodeId;
|
||||
|
||||
@@ -232,6 +241,9 @@ class Context {
|
||||
|
||||
// An entry in scope_stack_.
|
||||
struct ScopeStackEntry {
|
||||
// The name scope associated with this entry, if any.
|
||||
SemIR::NameScopeId scope_id;
|
||||
|
||||
// Names which are registered with name_lookup_, and will need to be
|
||||
// deregistered when the scope ends.
|
||||
llvm::DenseSet<SemIR::StringId> names;
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Carbon::Check {
|
||||
auto DeclarationNameStack::Push() -> void {
|
||||
declaration_name_stack_.push_back(
|
||||
{.state = NameContext::State::New,
|
||||
.target_scope_id = SemIR::NameScopeId::Invalid,
|
||||
.target_scope_id = context_->current_scope_id(),
|
||||
.resolved_node_id = SemIR::NodeId::Invalid});
|
||||
}
|
||||
|
||||
@@ -33,29 +33,29 @@ auto DeclarationNameStack::Pop() -> NameContext {
|
||||
return declaration_name_stack_.pop_back_val();
|
||||
}
|
||||
|
||||
auto DeclarationNameStack::AddNameToLookup(NameContext name_context,
|
||||
SemIR::NodeId target_id) -> void {
|
||||
auto DeclarationNameStack::LookupOrAddName(NameContext name_context,
|
||||
SemIR::NodeId target_id)
|
||||
-> SemIR::NodeId {
|
||||
switch (name_context.state) {
|
||||
case NameContext::State::Error:
|
||||
// The name is invalid and a diagnostic has already been emitted.
|
||||
return;
|
||||
return SemIR::NodeId::Invalid;
|
||||
|
||||
case NameContext::State::New:
|
||||
CARBON_FATAL() << "Name is missing, not expected to call AddNameToLookup "
|
||||
"(but that may change based on error handling).";
|
||||
|
||||
case NameContext::State::Resolved:
|
||||
case NameContext::State::ResolvedNonScope: {
|
||||
context_->DiagnoseDuplicateName(name_context.parse_node,
|
||||
name_context.resolved_node_id);
|
||||
return;
|
||||
}
|
||||
case NameContext::State::ResolvedNonScope:
|
||||
return name_context.resolved_node_id;
|
||||
|
||||
case NameContext::State::Unresolved:
|
||||
if (name_context.target_scope_id == SemIR::NameScopeId::Invalid) {
|
||||
context_->AddNameToLookup(name_context.parse_node,
|
||||
name_context.unresolved_name_id, target_id);
|
||||
} else {
|
||||
// TODO: Reject unless the scope is a namespace scope or the name is
|
||||
// unqualified.
|
||||
bool success = context_->semantics_ir().AddNameScopeEntry(
|
||||
name_context.target_scope_id, name_context.unresolved_name_id,
|
||||
target_id);
|
||||
@@ -64,7 +64,15 @@ auto DeclarationNameStack::AddNameToLookup(NameContext name_context,
|
||||
<< name_context.unresolved_name_id << " in "
|
||||
<< name_context.target_scope_id;
|
||||
}
|
||||
return;
|
||||
return SemIR::NodeId::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
auto DeclarationNameStack::AddNameToLookup(NameContext name_context,
|
||||
SemIR::NodeId target_id) -> void {
|
||||
auto existing_node_id = LookupOrAddName(name_context, target_id);
|
||||
if (existing_node_id.is_valid()) {
|
||||
context_->DiagnoseDuplicateName(name_context.parse_node, existing_node_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +126,18 @@ auto DeclarationNameStack::UpdateScopeIfNeeded(NameContext& name_context)
|
||||
auto resolved_node =
|
||||
context_->semantics_ir().GetNode(name_context.resolved_node_id);
|
||||
switch (resolved_node.kind()) {
|
||||
case SemIR::ClassDeclaration::Kind: {
|
||||
auto& class_info = context_->semantics_ir().GetClass(
|
||||
resolved_node.As<SemIR::ClassDeclaration>().class_id);
|
||||
// TODO: Check that the class is complete rather than that it has a scope.
|
||||
if (class_info.scope_id.is_valid()) {
|
||||
name_context.state = NameContext::State::Resolved;
|
||||
name_context.target_scope_id = class_info.scope_id;
|
||||
} else {
|
||||
name_context.state = NameContext::State::ResolvedNonScope;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SemIR::Namespace::Kind:
|
||||
name_context.state = NameContext::State::Resolved;
|
||||
name_context.target_scope_id =
|
||||
@@ -147,16 +167,31 @@ auto DeclarationNameStack::CanResolveQualifier(NameContext& name_context,
|
||||
case NameContext::State::ResolvedNonScope: {
|
||||
// Because more qualifiers were found, we diagnose that the earlier
|
||||
// qualifier didn't resolve to a scoped entity.
|
||||
if (auto class_decl = context_->semantics_ir()
|
||||
.GetNode(name_context.resolved_node_id)
|
||||
.TryAs<SemIR::ClassDeclaration>()) {
|
||||
CARBON_DIAGNOSTIC(QualifiedDeclarationInIncompleteClassScope, Error,
|
||||
"Cannot declare a member of incomplete class `{0}`.",
|
||||
std::string);
|
||||
auto builder = context_->emitter().Build(
|
||||
name_context.parse_node, QualifiedDeclarationInIncompleteClassScope,
|
||||
context_->semantics_ir().StringifyTypeExpression(
|
||||
name_context.resolved_node_id, true));
|
||||
context_->NoteIncompleteClass(*class_decl, builder);
|
||||
builder.Emit();
|
||||
} else {
|
||||
CARBON_DIAGNOSTIC(
|
||||
QualifiedDeclarationInNonScope, Error,
|
||||
"Declaration qualifiers are only allowed for entities "
|
||||
"that provide a scope.");
|
||||
CARBON_DIAGNOSTIC(QualifiedDeclarationNonScopeEntity, Note,
|
||||
"Non-scope entity referenced here.");
|
||||
context_->emitter()
|
||||
.Build(parse_node, QualifiedDeclarationInNonScope)
|
||||
.Note(name_context.parse_node, QualifiedDeclarationNonScopeEntity)
|
||||
.Emit();
|
||||
}
|
||||
name_context.state = NameContext::State::Error;
|
||||
CARBON_DIAGNOSTIC(QualifiedDeclarationInNonScope, Error,
|
||||
"Declaration qualifiers are only allowed for entities "
|
||||
"that provide a scope.");
|
||||
CARBON_DIAGNOSTIC(QualifiedDeclarationNonScopeEntity, Note,
|
||||
"Non-scope entity referenced here.");
|
||||
context_->emitter()
|
||||
.Build(parse_node, QualifiedDeclarationInNonScope)
|
||||
.Note(name_context.parse_node, QualifiedDeclarationNonScopeEntity)
|
||||
.Emit();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,8 +75,9 @@ class DeclarationNameStack {
|
||||
|
||||
State state = State::New;
|
||||
|
||||
// The scope which qualified names are added to. For unqualified names,
|
||||
// this will be Invalid to indicate the current scope should be used.
|
||||
// The scope which qualified names are added to. For unqualified names in
|
||||
// an unnamed scope, this will be Invalid to indicate the current scope
|
||||
// should be used.
|
||||
SemIR::NameScopeId target_scope_id = SemIR::NameScopeId::Invalid;
|
||||
|
||||
// The last parse node used.
|
||||
@@ -117,6 +118,11 @@ class DeclarationNameStack {
|
||||
auto AddNameToLookup(NameContext name_context, SemIR::NodeId target_id)
|
||||
-> void;
|
||||
|
||||
// Adds a name to name lookup, or returns the existing node if this name has
|
||||
// already been declared in this scope.
|
||||
auto LookupOrAddName(NameContext name_context, SemIR::NodeId target_id)
|
||||
-> SemIR::NodeId;
|
||||
|
||||
private:
|
||||
// Returns true if the context is in a state where it can resolve qualifiers.
|
||||
// Updates name_context as needed.
|
||||
|
||||
@@ -17,24 +17,51 @@ auto HandleClassIntroducer(Context& context, Parse::Node parse_node) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
static auto BuildClassDeclaration(Context& context) -> void {
|
||||
static auto BuildClassDeclaration(Context& context)
|
||||
-> std::tuple<SemIR::ClassId, SemIR::NodeId> {
|
||||
auto name_context = context.declaration_name_stack().Pop();
|
||||
|
||||
auto class_keyword =
|
||||
context.node_stack()
|
||||
.PopForSoloParseNode<Parse::NodeKind::ClassIntroducer>();
|
||||
auto decl_block_id = context.node_block_stack().Pop();
|
||||
|
||||
// TODO: Track this somewhere.
|
||||
context.node_block_stack().Pop();
|
||||
// Add the class declaration.
|
||||
auto class_decl =
|
||||
SemIR::ClassDeclaration(class_keyword, SemIR::TypeId::TypeType,
|
||||
SemIR::ClassId::Invalid, decl_block_id);
|
||||
auto class_decl_id = context.AddNode(class_decl);
|
||||
|
||||
auto class_id = context.semantics_ir().AddClass(
|
||||
{.name_id = name_context.state ==
|
||||
DeclarationNameStack::NameContext::State::Unresolved
|
||||
? name_context.unresolved_name_id
|
||||
: SemIR::StringId(SemIR::StringId::InvalidIndex)});
|
||||
auto class_decl_id = context.AddNode(SemIR::ClassDeclaration(
|
||||
class_keyword, SemIR::TypeId::TypeType, class_id));
|
||||
context.declaration_name_stack().AddNameToLookup(name_context, class_decl_id);
|
||||
// Check whether this is a redeclaration.
|
||||
auto existing_id = context.declaration_name_stack().LookupOrAddName(
|
||||
name_context, class_decl_id);
|
||||
if (existing_id.is_valid()) {
|
||||
if (auto existing_class_decl = context.semantics_ir()
|
||||
.GetNode(existing_id)
|
||||
.TryAs<SemIR::ClassDeclaration>()) {
|
||||
// This is a redeclaration of an existing class.
|
||||
class_decl.class_id = existing_class_decl->class_id;
|
||||
} else {
|
||||
// This is a redeclaration of something other than a class.
|
||||
context.DiagnoseDuplicateName(name_context.parse_node, existing_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new class if this isn't a valid redeclaration.
|
||||
if (!class_decl.class_id.is_valid()) {
|
||||
// TODO: If this is an invalid redeclaration of a non-class entity or there
|
||||
// was an error in the qualifier, we will have lost track of the class name
|
||||
// here. We should keep track of it even if the name is invalid.
|
||||
class_decl.class_id = context.semantics_ir().AddClass(
|
||||
{.name_id = name_context.state ==
|
||||
DeclarationNameStack::NameContext::State::Unresolved
|
||||
? name_context.unresolved_name_id
|
||||
: SemIR::StringId(SemIR::StringId::InvalidIndex)});
|
||||
}
|
||||
|
||||
// Write the class ID into the ClassDeclaration.
|
||||
context.semantics_ir().ReplaceNode(class_decl_id, class_decl);
|
||||
|
||||
return {class_decl.class_id, class_decl_id};
|
||||
}
|
||||
|
||||
auto HandleClassDeclaration(Context& context, Parse::Node /*parse_node*/)
|
||||
@@ -45,13 +72,53 @@ auto HandleClassDeclaration(Context& context, Parse::Node /*parse_node*/)
|
||||
|
||||
auto HandleClassDefinitionStart(Context& context, Parse::Node parse_node)
|
||||
-> bool {
|
||||
BuildClassDeclaration(context);
|
||||
// TODO: Introduce `Self`.
|
||||
return context.TODO(parse_node, "HandleClassDefinitionStart");
|
||||
auto [class_id, class_decl_id] = BuildClassDeclaration(context);
|
||||
auto& class_info = context.semantics_ir().GetClass(class_id);
|
||||
|
||||
// Track that this declaration is the definition.
|
||||
if (class_info.definition_id.is_valid()) {
|
||||
CARBON_DIAGNOSTIC(ClassRedefinition, Error, "Redefinition of class {0}.",
|
||||
llvm::StringRef);
|
||||
CARBON_DIAGNOSTIC(ClassPreviousDefinition, Note,
|
||||
"Previous definition was here.");
|
||||
context.emitter()
|
||||
.Build(parse_node, ClassRedefinition,
|
||||
context.semantics_ir().GetString(class_info.name_id))
|
||||
.Note(context.semantics_ir()
|
||||
.GetNode(class_info.definition_id)
|
||||
.parse_node(),
|
||||
ClassPreviousDefinition)
|
||||
.Emit();
|
||||
} else {
|
||||
class_info.definition_id = class_decl_id;
|
||||
class_info.scope_id = context.semantics_ir().AddNameScope();
|
||||
|
||||
// TODO: Introduce `Self`.
|
||||
}
|
||||
|
||||
// Enter the class scope.
|
||||
context.PushScope(class_info.scope_id);
|
||||
context.node_block_stack().Push();
|
||||
|
||||
// TODO: Handle the case where there's control flow in the class body. For
|
||||
// example:
|
||||
//
|
||||
// class C {
|
||||
// var v: if true then i32 else f64;
|
||||
// }
|
||||
//
|
||||
// We may need to track a list of node blocks here, as we do for a function.
|
||||
class_info.body_block_id = context.node_block_stack().PeekOrAdd();
|
||||
return true;
|
||||
}
|
||||
|
||||
auto HandleClassDefinition(Context& context, Parse::Node parse_node) -> bool {
|
||||
return context.TODO(parse_node, "HandleClassDefinition");
|
||||
auto HandleClassDefinition(Context& context, Parse::Node /*parse_node*/)
|
||||
-> bool {
|
||||
context.node_block_stack().Pop();
|
||||
context.PopScope();
|
||||
|
||||
// TODO: Mark the class as a complete type.
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Carbon::Check {
|
||||
// Build a FunctionDeclaration describing the signature of a function. This
|
||||
// handles the common logic shared by function declaration syntax and function
|
||||
// definition syntax.
|
||||
static auto BuildFunctionDeclaration(Context& context)
|
||||
static auto BuildFunctionDeclaration(Context& context, bool is_definition)
|
||||
-> std::pair<SemIR::FunctionId, SemIR::NodeId> {
|
||||
// TODO: This contains the IR block for the parameters and return type. At
|
||||
// present, it's just loose, but it's not strictly required for parameter
|
||||
@@ -57,25 +57,56 @@ static auto BuildFunctionDeclaration(Context& context)
|
||||
context.node_stack()
|
||||
.PopForSoloParseNode<Parse::NodeKind::FunctionIntroducer>();
|
||||
|
||||
// TODO: Support out-of-line definitions, which will have a resolved
|
||||
// name_context. Right now, those become errors in AddNameToLookup.
|
||||
|
||||
// Add the callable.
|
||||
auto function_id = context.semantics_ir().AddFunction(
|
||||
{.name_id = name_context.state ==
|
||||
DeclarationNameStack::NameContext::State::Unresolved
|
||||
? name_context.unresolved_name_id
|
||||
: SemIR::StringId(SemIR::StringId::InvalidIndex),
|
||||
.param_refs_id = param_refs_id,
|
||||
.return_type_id = return_type_id,
|
||||
.return_slot_id = return_slot_id,
|
||||
.body_block_ids = {}});
|
||||
auto decl_id = context.AddNode(SemIR::FunctionDeclaration(
|
||||
// Add the function declaration.
|
||||
auto function_decl = SemIR::FunctionDeclaration(
|
||||
fn_node, context.GetBuiltinType(SemIR::BuiltinKind::FunctionType),
|
||||
function_id));
|
||||
context.declaration_name_stack().AddNameToLookup(name_context, decl_id);
|
||||
SemIR::FunctionId::Invalid);
|
||||
auto function_decl_id = context.AddNode(function_decl);
|
||||
|
||||
if (SemIR::IsEntryPoint(context.semantics_ir(), function_id)) {
|
||||
// Check whether this is a redeclaration.
|
||||
auto existing_id = context.declaration_name_stack().LookupOrAddName(
|
||||
name_context, function_decl_id);
|
||||
if (existing_id.is_valid()) {
|
||||
if (auto existing_function_decl =
|
||||
context.semantics_ir()
|
||||
.GetNode(existing_id)
|
||||
.TryAs<SemIR::FunctionDeclaration>()) {
|
||||
// This is a redeclaration of an existing function.
|
||||
function_decl.function_id = existing_function_decl->function_id;
|
||||
|
||||
// TODO: Check that the signature matches!
|
||||
|
||||
// Track the signature from the definition, so that IDs in the body match
|
||||
// IDs in the signature.
|
||||
if (is_definition) {
|
||||
auto& function_info =
|
||||
context.semantics_ir().GetFunction(function_decl.function_id);
|
||||
function_info.param_refs_id = param_refs_id;
|
||||
function_info.return_type_id = return_type_id;
|
||||
function_info.return_slot_id = return_slot_id;
|
||||
}
|
||||
} else {
|
||||
// This is a redeclaration of something other than a function.
|
||||
context.DiagnoseDuplicateName(name_context.parse_node, existing_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new function if this isn't a valid redeclaration.
|
||||
if (!function_decl.function_id.is_valid()) {
|
||||
function_decl.function_id = context.semantics_ir().AddFunction(
|
||||
{.name_id = name_context.state ==
|
||||
DeclarationNameStack::NameContext::State::Unresolved
|
||||
? name_context.unresolved_name_id
|
||||
: SemIR::StringId(SemIR::StringId::InvalidIndex),
|
||||
.param_refs_id = param_refs_id,
|
||||
.return_type_id = return_type_id,
|
||||
.return_slot_id = return_slot_id});
|
||||
}
|
||||
|
||||
// Write the function ID into the FunctionDeclaration.
|
||||
context.semantics_ir().ReplaceNode(function_decl_id, function_decl);
|
||||
|
||||
if (SemIR::IsEntryPoint(context.semantics_ir(), function_decl.function_id)) {
|
||||
// TODO: Update this once valid signatures for the entry point are decided.
|
||||
if (!context.semantics_ir().GetNodeBlock(param_refs_id).empty() ||
|
||||
(return_slot_id.is_valid() &&
|
||||
@@ -89,12 +120,12 @@ static auto BuildFunctionDeclaration(Context& context)
|
||||
}
|
||||
}
|
||||
|
||||
return {function_id, decl_id};
|
||||
return {function_decl.function_id, function_decl_id};
|
||||
}
|
||||
|
||||
auto HandleFunctionDeclaration(Context& context, Parse::Node /*parse_node*/)
|
||||
-> bool {
|
||||
BuildFunctionDeclaration(context);
|
||||
BuildFunctionDeclaration(context, /*is_definition=*/false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -127,8 +158,26 @@ auto HandleFunctionDefinition(Context& context, Parse::Node parse_node)
|
||||
auto HandleFunctionDefinitionStart(Context& context, Parse::Node parse_node)
|
||||
-> bool {
|
||||
// Process the declaration portion of the function.
|
||||
auto [function_id, decl_id] = BuildFunctionDeclaration(context);
|
||||
const auto& function = context.semantics_ir().GetFunction(function_id);
|
||||
auto [function_id, decl_id] =
|
||||
BuildFunctionDeclaration(context, /*is_definition=*/true);
|
||||
auto& function = context.semantics_ir().GetFunction(function_id);
|
||||
|
||||
// Track that this declaration is the definition.
|
||||
if (function.definition_id.is_valid()) {
|
||||
CARBON_DIAGNOSTIC(FunctionRedefinition, Error,
|
||||
"Redefinition of function {0}.", llvm::StringRef);
|
||||
CARBON_DIAGNOSTIC(FunctionPreviousDefinition, Note,
|
||||
"Previous definition was here.");
|
||||
context.emitter()
|
||||
.Build(parse_node, FunctionRedefinition,
|
||||
context.semantics_ir().GetString(function.name_id))
|
||||
.Note(
|
||||
context.semantics_ir().GetNode(function.definition_id).parse_node(),
|
||||
FunctionPreviousDefinition)
|
||||
.Emit();
|
||||
} else {
|
||||
function.definition_id = decl_id;
|
||||
}
|
||||
|
||||
// Create the function scope and the entry block.
|
||||
context.return_scope_stack().push_back(decl_id);
|
||||
|
||||
@@ -9,19 +9,45 @@
|
||||
|
||||
namespace Carbon::Check {
|
||||
|
||||
// Returns the name scope corresponding to base_id, or nullopt if not a scope.
|
||||
// On invalid scopes, prints a diagnostic and still returns the scope.
|
||||
static auto GetAsNameScope(Context& context, SemIR::NodeId base_id)
|
||||
-> std::optional<SemIR::NameScopeId> {
|
||||
auto base =
|
||||
context.semantics_ir().GetNode(context.FollowNameReferences(base_id));
|
||||
if (auto base_as_namespace = base.TryAs<SemIR::Namespace>()) {
|
||||
return base_as_namespace->name_scope_id;
|
||||
}
|
||||
if (auto base_as_class = base.TryAs<SemIR::ClassDeclaration>()) {
|
||||
auto& class_info = context.semantics_ir().GetClass(base_as_class->class_id);
|
||||
if (!class_info.scope_id.is_valid()) {
|
||||
CARBON_DIAGNOSTIC(QualifiedExpressionInIncompleteClassScope, Error,
|
||||
"Member access into incomplete class `{0}`.",
|
||||
std::string);
|
||||
auto builder = context.emitter().Build(
|
||||
context.semantics_ir().GetNode(base_id).parse_node(),
|
||||
QualifiedExpressionInIncompleteClassScope,
|
||||
context.semantics_ir().StringifyTypeExpression(base_id, true));
|
||||
context.NoteIncompleteClass(*base_as_class, builder);
|
||||
builder.Emit();
|
||||
}
|
||||
return class_info.scope_id;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto HandleMemberAccessExpression(Context& context, Parse::Node parse_node)
|
||||
-> bool {
|
||||
SemIR::StringId name_id = context.node_stack().Pop<Parse::NodeKind::Name>();
|
||||
|
||||
auto base_id = context.node_stack().PopExpression();
|
||||
|
||||
if (auto base_namespace = context.semantics_ir()
|
||||
.GetNode(context.FollowNameReferences(base_id))
|
||||
.TryAs<SemIR::Namespace>()) {
|
||||
// For a namespace, just resolve the name.
|
||||
auto node_id =
|
||||
context.LookupName(parse_node, name_id, base_namespace->name_scope_id,
|
||||
/*print_diagnostics=*/true);
|
||||
// If the base is a name scope, such as a class or namespace, perform lookup
|
||||
// into that scope.
|
||||
if (auto name_scope_id = GetAsNameScope(context, base_id)) {
|
||||
auto node_id = name_scope_id->is_valid()
|
||||
? context.LookupName(parse_node, name_id, *name_scope_id,
|
||||
/*print_diagnostics=*/true)
|
||||
: SemIR::NodeId::BuiltinError;
|
||||
auto node = context.semantics_ir().GetNode(node_id);
|
||||
// TODO: Track that this node was named within `base_id`.
|
||||
context.AddNodeAndPush(
|
||||
|
||||
@@ -20,7 +20,7 @@ var a: [Incomplete; 1];
|
||||
var p: Incomplete* = &a[0];
|
||||
|
||||
// CHECK:STDOUT: file "fail_incomplete_element.carbon" {
|
||||
// CHECK:STDOUT: %Incomplete: type = class_declaration @Incomplete
|
||||
// CHECK:STDOUT: %Incomplete: type = class_declaration @Incomplete, ()
|
||||
// CHECK:STDOUT: %Incomplete.ref.loc15: type = name_reference "Incomplete", %Incomplete
|
||||
// CHECK:STDOUT: %.loc15_21: i32 = int_literal 1
|
||||
// CHECK:STDOUT: %.loc15_22: type = array_type %.loc15_21, Incomplete
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
// NOAUTOUPDATE
|
||||
// SET-CHECK-SUBSET
|
||||
// CHECK:STDERR: Node Push 0: FunctionIntroducer -> <none>
|
||||
// CHECK:STDERR: AddNode: {kind: FunctionDeclaration, arg0: function{{[0-9]+}}, type: type{{[0-9]+}}}
|
||||
// CHECK:STDERR: AddNode: {kind: FunctionDeclaration, arg0: {{.*}}, type: type{{[0-9]+}}}
|
||||
// CHECK:STDERR: node_block_stack_ Push 1
|
||||
// CHECK:STDERR: AddNode: {kind: Return}
|
||||
// CHECK:STDERR: node_block_stack_ Pop 1: block{{[0-9]+}}
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
class Class {
|
||||
fn F(n: i32) -> i32 {
|
||||
return n;
|
||||
}
|
||||
|
||||
fn G(n: i32) -> i32;
|
||||
|
||||
var k: i32;
|
||||
}
|
||||
|
||||
fn Class.G(n: i32) -> i32 {
|
||||
return n;
|
||||
}
|
||||
|
||||
fn Run() -> i32 {
|
||||
return Class.F(4);
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: file "basic.carbon" {
|
||||
// CHECK:STDOUT: %Class: type = class_declaration @Class, ()
|
||||
// CHECK:STDOUT: %G: <function> = fn_decl @G
|
||||
// CHECK:STDOUT: %Run: <function> = fn_decl @Run
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Class {
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F
|
||||
// CHECK:STDOUT: %G: <function> = fn_decl @G
|
||||
// CHECK:STDOUT: %k: ref i32 = var "k"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .F = %F
|
||||
// CHECK:STDOUT: .G = %G
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F(%n: i32) -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %n.ref: i32 = name_reference "n", %n
|
||||
// CHECK:STDOUT: return %n.ref
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @G(%n: i32) -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %n.ref: i32 = name_reference "n", %n
|
||||
// CHECK:STDOUT: return %n.ref
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Run() -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %Class.ref: type = name_reference "Class", package.%Class
|
||||
// CHECK:STDOUT: %F.ref: <function> = name_reference "F", @Class.%F
|
||||
// CHECK:STDOUT: %.loc22_18: i32 = int_literal 4
|
||||
// CHECK:STDOUT: %.loc22_17.1: init i32 = call %F.ref(%.loc22_18)
|
||||
// CHECK:STDOUT: %.loc22_17.2: ref i32 = temporary_storage
|
||||
// CHECK:STDOUT: %.loc22_17.3: ref i32 = temporary %.loc22_17.2, %.loc22_17.1
|
||||
// CHECK:STDOUT: %.loc22_17.4: i32 = bind_value %.loc22_17.3
|
||||
// CHECK:STDOUT: return %.loc22_17.4
|
||||
// CHECK:STDOUT: }
|
||||
+55
-20
@@ -6,10 +6,31 @@
|
||||
|
||||
class Class;
|
||||
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE+6]]:4: ERROR: Cannot declare a member of incomplete class `Class`.
|
||||
// CHECK:STDERR: fn Class.Function() {}
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-5]]:1: Class was forward declared here.
|
||||
// CHECK:STDERR: class Class;
|
||||
// CHECK:STDERR: ^
|
||||
fn Class.Function() {}
|
||||
|
||||
fn CallClassFunction() {
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE+9]]:3: ERROR: Member access into incomplete class `Class`.
|
||||
// CHECK:STDERR: Class.Function();
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-14]]:1: Class was forward declared here.
|
||||
// CHECK:STDERR: class Class;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE+3]]:18: ERROR: Semantics TODO: `Not a callable name`.
|
||||
// CHECK:STDERR: Class.Function();
|
||||
// CHECK:STDERR: ^
|
||||
Class.Function();
|
||||
}
|
||||
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE+6]]:17: ERROR: Variable has incomplete type `Class`.
|
||||
// CHECK:STDERR: var global_var: Class;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-5]]:1: Class was forward declared here.
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-26]]:1: Class was forward declared here.
|
||||
// CHECK:STDERR: class Class;
|
||||
// CHECK:STDERR: ^
|
||||
var global_var: Class;
|
||||
@@ -17,7 +38,7 @@ var global_var: Class;
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE+6]]:24: ERROR: Function returns incomplete type `Class`.
|
||||
// CHECK:STDERR: fn ConvertFromStruct() -> Class { return {}; }
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-13]]:1: Class was forward declared here.
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-34]]:1: Class was forward declared here.
|
||||
// CHECK:STDERR: class Class;
|
||||
// CHECK:STDERR: ^
|
||||
fn ConvertFromStruct() -> Class { return {}; }
|
||||
@@ -31,7 +52,7 @@ fn MemberAccess(p: Class*) -> i32 {
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE+6]]:11: ERROR: Invalid use of incomplete type `Class`.
|
||||
// CHECK:STDERR: return (*p).n;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-27]]:1: Class was forward declared here.
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-48]]:1: Class was forward declared here.
|
||||
// CHECK:STDERR: class Class;
|
||||
// CHECK:STDERR: ^
|
||||
return (*p).n;
|
||||
@@ -40,7 +61,7 @@ fn MemberAccess(p: Class*) -> i32 {
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE+6]]:20: ERROR: Function returns incomplete type `Class`.
|
||||
// CHECK:STDERR: fn Copy(p: Class*) -> Class {
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-36]]:1: Class was forward declared here.
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-57]]:1: Class was forward declared here.
|
||||
// CHECK:STDERR: class Class;
|
||||
// CHECK:STDERR: ^
|
||||
fn Copy(p: Class*) -> Class {
|
||||
@@ -51,7 +72,7 @@ fn Let(p: Class*) {
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE+6]]:10: ERROR: `let` binding has incomplete type `Class`.
|
||||
// CHECK:STDERR: let c: Class = *p;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-47]]:1: Class was forward declared here.
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-68]]:1: Class was forward declared here.
|
||||
// CHECK:STDERR: class Class;
|
||||
// CHECK:STDERR: ^
|
||||
let c: Class = *p;
|
||||
@@ -64,7 +85,7 @@ fn TakeIncomplete(c: Class);
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE+6]]:23: ERROR: Function returns incomplete type `Class`.
|
||||
// CHECK:STDERR: fn ReturnIncomplete() -> Class;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-60]]:1: Class was forward declared here.
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-81]]:1: Class was forward declared here.
|
||||
// CHECK:STDERR: class Class;
|
||||
// CHECK:STDERR: ^
|
||||
fn ReturnIncomplete() -> Class;
|
||||
@@ -73,7 +94,7 @@ fn CallTakeIncomplete(p: Class*) {
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE+9]]:17: ERROR: Forming value of incomplete type `Class`.
|
||||
// CHECK:STDERR: TakeIncomplete(*p);
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-69]]:1: Class was forward declared here.
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-90]]:1: Class was forward declared here.
|
||||
// CHECK:STDERR: class Class;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-19]]:1: Initializing parameter 1 of function declared here.
|
||||
@@ -84,7 +105,7 @@ fn CallTakeIncomplete(p: Class*) {
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE+9]]:17: ERROR: Forming value of incomplete type `Class`.
|
||||
// CHECK:STDERR: TakeIncomplete({});
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-80]]:1: Class was forward declared here.
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-101]]:1: Class was forward declared here.
|
||||
// CHECK:STDERR: class Class;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-30]]:1: Initializing parameter 1 of function declared here.
|
||||
@@ -98,7 +119,9 @@ fn CallReturnIncomplete() {
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: file "fail_incomplete.carbon" {
|
||||
// CHECK:STDOUT: %Class: type = class_declaration @Class
|
||||
// CHECK:STDOUT: %Class: type = class_declaration @Class, ()
|
||||
// CHECK:STDOUT: %.loc15: <function> = fn_decl @.1
|
||||
// CHECK:STDOUT: %CallClassFunction: <function> = fn_decl @CallClassFunction
|
||||
// CHECK:STDOUT: %Class.ref: type = name_reference "Class", %Class
|
||||
// CHECK:STDOUT: %global_var: ref <error> = var "global_var"
|
||||
// CHECK:STDOUT: %ConvertFromStruct: <function> = fn_decl @ConvertFromStruct
|
||||
@@ -113,24 +136,36 @@ fn CallReturnIncomplete() {
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Class;
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @.1() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @CallClassFunction() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %Class.ref: type = name_reference "Class", package.%Class
|
||||
// CHECK:STDOUT: %Function.ref: <error> = name_reference "Function", <error>
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @ConvertFromStruct() -> <error> {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %.loc23_43.1: type = struct_type {}
|
||||
// CHECK:STDOUT: %.loc23_43.2: {} = struct_literal ()
|
||||
// CHECK:STDOUT: %.loc44_43.1: type = struct_type {}
|
||||
// CHECK:STDOUT: %.loc44_43.2: {} = struct_literal ()
|
||||
// CHECK:STDOUT: return <error>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @MemberAccess(%p: Class*) -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %p.ref: Class* = name_reference "p", %p
|
||||
// CHECK:STDOUT: %.loc37: ref Class = dereference %p.ref
|
||||
// CHECK:STDOUT: %.loc58: ref Class = dereference %p.ref
|
||||
// CHECK:STDOUT: return <error>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Copy(%p: Class*) -> <error> {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %p.ref: Class* = name_reference "p", %p
|
||||
// CHECK:STDOUT: %.loc47: ref Class = dereference %p.ref
|
||||
// CHECK:STDOUT: %.loc68: ref Class = dereference %p.ref
|
||||
// CHECK:STDOUT: return <error>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -138,7 +173,7 @@ fn CallReturnIncomplete() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %Class.ref: type = name_reference "Class", package.%Class
|
||||
// CHECK:STDOUT: %p.ref: Class* = name_reference "p", %p
|
||||
// CHECK:STDOUT: %.loc57: ref Class = dereference %p.ref
|
||||
// CHECK:STDOUT: %.loc78: ref Class = dereference %p.ref
|
||||
// CHECK:STDOUT: %c: <error> = bind_name "c", <error>
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
@@ -149,18 +184,18 @@ fn CallReturnIncomplete() {
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @CallTakeIncomplete(%p: Class*) {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %TakeIncomplete.ref.loc82: <function> = name_reference "TakeIncomplete", package.%TakeIncomplete
|
||||
// CHECK:STDOUT: %TakeIncomplete.ref.loc103: <function> = name_reference "TakeIncomplete", package.%TakeIncomplete
|
||||
// CHECK:STDOUT: %p.ref: Class* = name_reference "p", %p
|
||||
// CHECK:STDOUT: %.loc82_18: ref Class = dereference %p.ref
|
||||
// CHECK:STDOUT: %.loc82_17: type = tuple_type ()
|
||||
// CHECK:STDOUT: %TakeIncomplete.ref.loc93: <function> = name_reference "TakeIncomplete", package.%TakeIncomplete
|
||||
// CHECK:STDOUT: %.loc93: {} = struct_literal ()
|
||||
// CHECK:STDOUT: %.loc103_18: ref Class = dereference %p.ref
|
||||
// CHECK:STDOUT: %.loc103_17: type = tuple_type ()
|
||||
// CHECK:STDOUT: %TakeIncomplete.ref.loc114: <function> = name_reference "TakeIncomplete", package.%TakeIncomplete
|
||||
// CHECK:STDOUT: %.loc114: {} = struct_literal ()
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @CallReturnIncomplete() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %ReturnIncomplete.ref: <function> = name_reference "ReturnIncomplete", package.%ReturnIncomplete
|
||||
// CHECK:STDOUT: %.loc97: init <error> = call %ReturnIncomplete.ref()
|
||||
// CHECK:STDOUT: %.loc118: init <error> = call %ReturnIncomplete.ref()
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
class Class {
|
||||
fn F();
|
||||
fn H();
|
||||
}
|
||||
|
||||
// CHECK:STDERR: fail_redefinition.carbon:[[@LINE+6]]:13: ERROR: Redefinition of class Class.
|
||||
// CHECK:STDERR: class Class {
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_redefinition.carbon:[[@LINE-8]]:1: Previous definition was here.
|
||||
// CHECK:STDERR: class Class {
|
||||
// CHECK:STDERR: ^
|
||||
class Class {
|
||||
fn G();
|
||||
fn H();
|
||||
}
|
||||
|
||||
fn Class.F() {}
|
||||
fn Class.G() {}
|
||||
fn Class.H() {}
|
||||
|
||||
// CHECK:STDOUT: file "fail_redefinition.carbon" {
|
||||
// CHECK:STDOUT: %Class.loc7: type = class_declaration @Class, ()
|
||||
// CHECK:STDOUT: %Class.loc18: type = class_declaration @Class, ()
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F
|
||||
// CHECK:STDOUT: %G: <function> = fn_decl @G
|
||||
// CHECK:STDOUT: %H: <function> = fn_decl @H
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Class {
|
||||
// CHECK:STDOUT: %G: <function> = fn_decl @G
|
||||
// CHECK:STDOUT: %H: <function> = fn_decl @H
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .F = <unexpected noderef 9>
|
||||
// CHECK:STDOUT: .H = <unexpected noderef 10>
|
||||
// CHECK:STDOUT: .G = %G
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @H() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @G() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
@@ -0,0 +1,49 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
class Class {
|
||||
fn G() -> i32 {
|
||||
// TODO: This should find the member function `F` even though it's declared
|
||||
// later.
|
||||
// CHECK:STDERR: fail_reorder.carbon:[[@LINE+6]]:17: ERROR: Name `F` not found.
|
||||
// CHECK:STDERR: return Class.F();
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_reorder.carbon:[[@LINE+3]]:20: ERROR: Semantics TODO: `Not a callable name`.
|
||||
// CHECK:STDERR: return Class.F();
|
||||
// CHECK:STDERR: ^
|
||||
return Class.F();
|
||||
}
|
||||
|
||||
fn F() -> i32 {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: file "fail_reorder.carbon" {
|
||||
// CHECK:STDOUT: %Class: type = class_declaration @Class, ()
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Class {
|
||||
// CHECK:STDOUT: %G: <function> = fn_decl @G
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .G = %G
|
||||
// CHECK:STDOUT: .F = %F
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @G() -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %Class.ref: type = name_reference "Class", package.%Class
|
||||
// CHECK:STDOUT: %F.ref: <error> = name_reference "F", <error>
|
||||
// CHECK:STDOUT: return <error>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F() -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %.loc21: i32 = int_literal 1
|
||||
// CHECK:STDOUT: return %.loc21
|
||||
// CHECK:STDOUT: }
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
class Class {
|
||||
fn F() -> i32 {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// TODO: This `F()` should find `Class.F`.
|
||||
fn G() -> i32 {
|
||||
// CHECK:STDERR: fail_scope.carbon:[[@LINE+6]]:12: ERROR: Name `F` not found.
|
||||
// CHECK:STDERR: return F();
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_scope.carbon:[[@LINE+3]]:14: ERROR: Semantics TODO: `Not a callable name`.
|
||||
// CHECK:STDERR: return F();
|
||||
// CHECK:STDERR: ^
|
||||
return F();
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: file "fail_scope.carbon" {
|
||||
// CHECK:STDOUT: %Class: type = class_declaration @Class, ()
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Class {
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F
|
||||
// CHECK:STDOUT: %G: <function> = fn_decl @G
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .F = %F
|
||||
// CHECK:STDOUT: .G = %G
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F() -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %.loc9: i32 = int_literal 1
|
||||
// CHECK:STDOUT: return %.loc9
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @G() -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %F.ref: <error> = name_reference "F", <error>
|
||||
// CHECK:STDOUT: return <error>
|
||||
// CHECK:STDOUT: }
|
||||
+1
-1
@@ -9,7 +9,7 @@ class Class;
|
||||
fn F(p: Class*) -> Class* { return p; }
|
||||
|
||||
// CHECK:STDOUT: file "forward_declared.carbon" {
|
||||
// CHECK:STDOUT: %Class: type = class_declaration @Class
|
||||
// CHECK:STDOUT: %Class: type = class_declaration @Class, ()
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
class Class;
|
||||
|
||||
class Class {
|
||||
fn F();
|
||||
}
|
||||
|
||||
fn Class.F() {}
|
||||
|
||||
// CHECK:STDOUT: file "redeclaration.carbon" {
|
||||
// CHECK:STDOUT: %Class.loc7: type = class_declaration @Class, ()
|
||||
// CHECK:STDOUT: %Class.loc9: type = class_declaration @Class, ()
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Class {
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .F = %F
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
class Class {
|
||||
fn F() -> i32 {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn F() -> i32 {
|
||||
return 2;
|
||||
}
|
||||
|
||||
fn Run() -> i32 {
|
||||
return F() + Class.F();
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: file "scope.carbon" {
|
||||
// CHECK:STDOUT: %Class: type = class_declaration @Class, ()
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F.2
|
||||
// CHECK:STDOUT: %Run: <function> = fn_decl @Run
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Class {
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F.1
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .F = %F
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F.1() -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %.loc9: i32 = int_literal 1
|
||||
// CHECK:STDOUT: return %.loc9
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F.2() -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %.loc14: i32 = int_literal 2
|
||||
// CHECK:STDOUT: return %.loc14
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Run() -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %F.ref.loc18_10: <function> = name_reference "F", package.%F
|
||||
// CHECK:STDOUT: %.loc18_11.1: init i32 = call %F.ref.loc18_10()
|
||||
// CHECK:STDOUT: %Class.ref: type = name_reference "Class", package.%Class
|
||||
// CHECK:STDOUT: %F.ref.loc18_21: <function> = name_reference "F", @Class.%F
|
||||
// CHECK:STDOUT: %.loc18_23.1: init i32 = call %F.ref.loc18_21()
|
||||
// CHECK:STDOUT: %.loc18_11.2: ref i32 = temporary_storage
|
||||
// CHECK:STDOUT: %.loc18_11.3: ref i32 = temporary %.loc18_11.2, %.loc18_11.1
|
||||
// CHECK:STDOUT: %.loc18_11.4: i32 = bind_value %.loc18_11.3
|
||||
// CHECK:STDOUT: %.loc18_23.2: ref i32 = temporary_storage
|
||||
// CHECK:STDOUT: %.loc18_23.3: ref i32 = temporary %.loc18_23.2, %.loc18_23.1
|
||||
// CHECK:STDOUT: %.loc18_23.4: i32 = bind_value %.loc18_23.3
|
||||
// CHECK:STDOUT: %.loc18_14: i32 = add %.loc18_11.4, %.loc18_23.4
|
||||
// CHECK:STDOUT: return %.loc18_14
|
||||
// CHECK:STDOUT: }
|
||||
@@ -9,27 +9,25 @@ namespace Foo;
|
||||
fn Foo.Baz() {
|
||||
}
|
||||
|
||||
// CHECK:STDERR: fail_duplicate.carbon:[[@LINE+6]]:8: ERROR: Duplicate name being declared in the same scope.
|
||||
// CHECK:STDERR: fail_duplicate.carbon:[[@LINE+6]]:14: ERROR: Redefinition of function Baz.
|
||||
// CHECK:STDERR: fn Foo.Baz() {
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_duplicate.carbon:[[@LINE-6]]:1: Name is previously declared here.
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_duplicate.carbon:[[@LINE-6]]:1: Previous definition was here.
|
||||
// CHECK:STDERR: fn Foo.Baz() {
|
||||
// CHECK:STDERR: ^
|
||||
fn Foo.Baz() {
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: file "fail_duplicate.carbon" {
|
||||
// CHECK:STDOUT: %.loc7: <namespace> = namespace {.Baz = %Baz}
|
||||
// CHECK:STDOUT: %Baz: <function> = fn_decl @Baz
|
||||
// CHECK:STDOUT: %.loc18: <function> = fn_decl @.1
|
||||
// CHECK:STDOUT: %.loc7: <namespace> = namespace {.Baz = %Baz.loc9}
|
||||
// CHECK:STDOUT: %Baz.loc9: <function> = fn_decl @Baz
|
||||
// CHECK:STDOUT: %Baz.loc18: <function> = fn_decl @Baz
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Baz() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @.1() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: !.loc19:
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
|
||||
@@ -20,7 +20,7 @@ var s: {.a: Incomplete};
|
||||
var p: Incomplete* = &s.a;
|
||||
|
||||
// CHECK:STDOUT: file "fail_nested_incomplete.carbon" {
|
||||
// CHECK:STDOUT: %Incomplete: type = class_declaration @Incomplete
|
||||
// CHECK:STDOUT: %Incomplete: type = class_declaration @Incomplete, ()
|
||||
// CHECK:STDOUT: %Incomplete.ref.loc15: type = name_reference "Incomplete", %Incomplete
|
||||
// CHECK:STDOUT: %.loc15: type = struct_type {.a: Incomplete}
|
||||
// CHECK:STDOUT: %s: ref <error> = var "s"
|
||||
|
||||
@@ -20,7 +20,7 @@ var t: (i32, Incomplete);
|
||||
var p: Incomplete* = &t[1];
|
||||
|
||||
// CHECK:STDOUT: file "fail_nested_incomplete.carbon" {
|
||||
// CHECK:STDOUT: %Incomplete: type = class_declaration @Incomplete
|
||||
// CHECK:STDOUT: %Incomplete: type = class_declaration @Incomplete, ()
|
||||
// CHECK:STDOUT: %Incomplete.ref.loc15: type = name_reference "Incomplete", %Incomplete
|
||||
// CHECK:STDOUT: %.loc15_24.1: type = tuple_type (type, type)
|
||||
// CHECK:STDOUT: %.loc15_24.2: (type, type) = tuple_literal (i32, %Incomplete.ref.loc15)
|
||||
|
||||
@@ -116,9 +116,13 @@ CARBON_DIAGNOSTIC_KIND(ArrayInitFromExpressionArgCountMismatch)
|
||||
CARBON_DIAGNOSTIC_KIND(AssignmentToNonAssignable)
|
||||
CARBON_DIAGNOSTIC_KIND(BreakOutsideLoop)
|
||||
CARBON_DIAGNOSTIC_KIND(ClassForwardDeclaredHere)
|
||||
CARBON_DIAGNOSTIC_KIND(ClassPreviousDefinition)
|
||||
CARBON_DIAGNOSTIC_KIND(ClassRedefinition)
|
||||
CARBON_DIAGNOSTIC_KIND(ContinueOutsideLoop)
|
||||
CARBON_DIAGNOSTIC_KIND(DereferenceOfNonPointer)
|
||||
CARBON_DIAGNOSTIC_KIND(DereferenceOfType)
|
||||
CARBON_DIAGNOSTIC_KIND(FunctionPreviousDefinition)
|
||||
CARBON_DIAGNOSTIC_KIND(FunctionRedefinition)
|
||||
CARBON_DIAGNOSTIC_KIND(NameNotFound)
|
||||
CARBON_DIAGNOSTIC_KIND(NameDeclarationDuplicate)
|
||||
CARBON_DIAGNOSTIC_KIND(NameDeclarationPrevious)
|
||||
@@ -146,8 +150,10 @@ CARBON_DIAGNOSTIC_KIND(ReturnStatementDisallowExpression)
|
||||
CARBON_DIAGNOSTIC_KIND(ReturnStatementImplicitNote)
|
||||
CARBON_DIAGNOSTIC_KIND(ReturnStatementMissingExpression)
|
||||
CARBON_DIAGNOSTIC_KIND(ImplicitAsConversionFailure)
|
||||
CARBON_DIAGNOSTIC_KIND(QualifiedDeclarationInIncompleteClassScope)
|
||||
CARBON_DIAGNOSTIC_KIND(QualifiedDeclarationInNonScope)
|
||||
CARBON_DIAGNOSTIC_KIND(QualifiedDeclarationNonScopeEntity)
|
||||
CARBON_DIAGNOSTIC_KIND(QualifiedExpressionInIncompleteClassScope)
|
||||
CARBON_DIAGNOSTIC_KIND(QualifiedExpressionUnsupported)
|
||||
CARBON_DIAGNOSTIC_KIND(QualifiedExpressionNameNotFound)
|
||||
CARBON_DIAGNOSTIC_KIND(UseOfNonExpressionAsValue)
|
||||
|
||||
@@ -207,6 +207,7 @@ static auto GetTypePrecedence(NodeKind kind) -> int {
|
||||
case ArrayType::Kind:
|
||||
case Builtin::Kind:
|
||||
case ClassDeclaration::Kind:
|
||||
case NameReference::Kind:
|
||||
case StructType::Kind:
|
||||
case TupleType::Kind:
|
||||
return 0;
|
||||
@@ -238,7 +239,6 @@ static auto GetTypePrecedence(NodeKind kind) -> int {
|
||||
case FunctionDeclaration::Kind:
|
||||
case InitializeFrom::Kind:
|
||||
case IntegerLiteral::Kind:
|
||||
case NameReference::Kind:
|
||||
case Namespace::Kind:
|
||||
case NoOp::Kind:
|
||||
case Parameter::Kind:
|
||||
@@ -268,6 +268,12 @@ static auto GetTypePrecedence(NodeKind kind) -> int {
|
||||
|
||||
auto File::StringifyType(TypeId type_id, bool in_type_context) const
|
||||
-> std::string {
|
||||
return StringifyTypeExpression(GetTypeAllowBuiltinTypes(type_id),
|
||||
in_type_context);
|
||||
}
|
||||
|
||||
auto File::StringifyTypeExpression(NodeId outer_node_id,
|
||||
bool in_type_context) const -> std::string {
|
||||
std::string str;
|
||||
llvm::raw_string_ostream out(str);
|
||||
|
||||
@@ -281,7 +287,6 @@ auto File::StringifyType(TypeId type_id, bool in_type_context) const
|
||||
return {.node_id = node_id, .index = index + 1};
|
||||
}
|
||||
};
|
||||
auto outer_node_id = GetTypeAllowBuiltinTypes(type_id);
|
||||
llvm::SmallVector<Step> steps = {{.node_id = outer_node_id}};
|
||||
|
||||
while (!steps.empty()) {
|
||||
@@ -338,6 +343,10 @@ auto File::StringifyType(TypeId type_id, bool in_type_context) const
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NameReference::Kind: {
|
||||
out << GetString(node.As<NameReference>().name_id);
|
||||
break;
|
||||
}
|
||||
case PointerType::Kind: {
|
||||
if (step.index == 0) {
|
||||
steps.push_back(step.Next());
|
||||
@@ -414,7 +423,6 @@ auto File::StringifyType(TypeId type_id, bool in_type_context) const
|
||||
case FunctionDeclaration::Kind:
|
||||
case InitializeFrom::Kind:
|
||||
case IntegerLiteral::Kind:
|
||||
case NameReference::Kind:
|
||||
case Namespace::Kind:
|
||||
case NoOp::Kind:
|
||||
case Parameter::Kind:
|
||||
|
||||
+21
-1
@@ -35,6 +35,9 @@ struct Function : public Printable<Function> {
|
||||
|
||||
// The function name.
|
||||
StringId name_id;
|
||||
// The definition, if the function has been defined or is currently being
|
||||
// defined. This is a FunctionDeclaration.
|
||||
NodeId definition_id = NodeId::Invalid;
|
||||
// A block containing a single reference node per parameter.
|
||||
NodeBlockId param_refs_id;
|
||||
// The return type. This will be invalid if the return type wasn't specified.
|
||||
@@ -48,7 +51,7 @@ struct Function : public Printable<Function> {
|
||||
// A list of the statically reachable code blocks in the body of the
|
||||
// function, in lexical order. The first block is the entry block. This will
|
||||
// be empty for declarations that don't have a visible definition.
|
||||
llvm::SmallVector<NodeBlockId> body_block_ids;
|
||||
llvm::SmallVector<NodeBlockId> body_block_ids = {};
|
||||
};
|
||||
|
||||
// A class.
|
||||
@@ -60,6 +63,17 @@ struct Class : public Printable<Class> {
|
||||
|
||||
// The class name.
|
||||
StringId name_id;
|
||||
|
||||
// The definition, if the class has been defined or is currently being
|
||||
// defined. This is a ClassDeclaration.
|
||||
NodeId definition_id = NodeId::Invalid;
|
||||
|
||||
// The class scope.
|
||||
NameScopeId scope_id = NameScopeId::Invalid;
|
||||
|
||||
// The first block of the class body.
|
||||
// TODO: Handle control flow in the class body, such as if-expressions.
|
||||
NodeBlockId body_block_id = NodeBlockId::Invalid;
|
||||
};
|
||||
|
||||
// TODO: Replace this with a Rational type, per the design:
|
||||
@@ -428,6 +442,12 @@ class File : public Printable<File> {
|
||||
auto StringifyType(TypeId type_id, bool in_type_context = false) const
|
||||
-> std::string;
|
||||
|
||||
// Same as `StringifyType`, but starting with a node representing a type
|
||||
// expression rather than a canonical type.
|
||||
auto StringifyTypeExpression(NodeId outer_node_id,
|
||||
bool in_type_context = false) const
|
||||
-> std::string;
|
||||
|
||||
auto functions_size() const -> int { return functions_.size(); }
|
||||
auto classes_size() const -> int { return classes_.size(); }
|
||||
auto nodes_size() const -> int { return nodes_.size(); }
|
||||
|
||||
@@ -89,7 +89,8 @@ class NodeNamer {
|
||||
class_info.name_id.is_valid()
|
||||
? semantics_ir.GetString(class_info.name_id).str()
|
||||
: "");
|
||||
// TODO: Handle names declared in the class scope.
|
||||
AddBlockLabel(class_scope, class_info.body_block_id, "class", class_loc);
|
||||
CollectNamesInBlock(class_scope, class_info.body_block_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,9 +510,18 @@ class Formatter {
|
||||
|
||||
out_ << "\nclass ";
|
||||
FormatClassName(id);
|
||||
// TODO: Format class definitions.
|
||||
(void)class_info;
|
||||
out_ << ";\n";
|
||||
|
||||
llvm::SaveAndRestore class_scope(scope_, node_namer_.GetScopeFor(id));
|
||||
|
||||
if (class_info.scope_id.is_valid()) {
|
||||
out_ << " {\n";
|
||||
FormatCodeBlock(class_info.body_block_id);
|
||||
out_ << "\n!members:";
|
||||
FormatNameScope(class_info.scope_id, "", "\n .");
|
||||
out_ << "\n}\n";
|
||||
} else {
|
||||
out_ << ";\n";
|
||||
}
|
||||
}
|
||||
|
||||
auto FormatFunction(FunctionId id) -> void {
|
||||
@@ -572,6 +582,26 @@ class Formatter {
|
||||
}
|
||||
}
|
||||
|
||||
auto FormatNameScope(NameScopeId id, llvm::StringRef separator,
|
||||
llvm::StringRef prefix) -> void {
|
||||
// Name scopes aren't kept in any particular order. Sort the entries before
|
||||
// we print them for stability and consistency.
|
||||
llvm::SmallVector<std::pair<NodeId, StringId>> entries;
|
||||
for (auto [name_id, node_id] : semantics_ir_.GetNameScope(id)) {
|
||||
entries.push_back({node_id, name_id});
|
||||
}
|
||||
llvm::sort(entries,
|
||||
[](auto a, auto b) { return a.first.index < b.first.index; });
|
||||
|
||||
llvm::ListSeparator sep(separator);
|
||||
for (auto [node_id, name_id] : entries) {
|
||||
out_ << sep << prefix;
|
||||
FormatString(name_id);
|
||||
out_ << " = ";
|
||||
FormatNodeName(node_id);
|
||||
}
|
||||
}
|
||||
|
||||
auto FormatInstruction(NodeId node_id) -> void {
|
||||
if (!node_id.is_valid()) {
|
||||
Indent();
|
||||
@@ -788,26 +818,9 @@ class Formatter {
|
||||
|
||||
auto FormatArg(MemberIndex index) -> void { out_ << index; }
|
||||
|
||||
// TODO: Should we be printing scopes inline, or should we have a separate
|
||||
// step to print them like we do for functions?
|
||||
auto FormatArg(NameScopeId id) -> void {
|
||||
// Name scopes aren't kept in any particular order. Sort the entries before
|
||||
// we print them for stability and consistency.
|
||||
std::vector<std::pair<NodeId, StringId>> entries;
|
||||
for (auto [name_id, node_id] : semantics_ir_.GetNameScope(id)) {
|
||||
entries.push_back({node_id, name_id});
|
||||
}
|
||||
llvm::sort(entries,
|
||||
[](auto a, auto b) { return a.first.index < b.first.index; });
|
||||
|
||||
out_ << '{';
|
||||
llvm::ListSeparator sep;
|
||||
for (auto [node_id, name_id] : entries) {
|
||||
out_ << sep << ".";
|
||||
FormatString(name_id);
|
||||
out_ << " = ";
|
||||
FormatNodeName(node_id);
|
||||
}
|
||||
FormatNameScope(id, ", ", ".");
|
||||
out_ << '}';
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,9 @@ constexpr NodeId NodeId::Invalid = NodeId(NodeId::InvalidIndex);
|
||||
|
||||
// The ID of a function.
|
||||
struct FunctionId : public IndexBase, public Printable<FunctionId> {
|
||||
// An explicitly invalid function ID.
|
||||
static const FunctionId Invalid;
|
||||
|
||||
using IndexBase::IndexBase;
|
||||
auto Print(llvm::raw_ostream& out) const -> void {
|
||||
out << "function";
|
||||
@@ -63,8 +66,13 @@ struct FunctionId : public IndexBase, public Printable<FunctionId> {
|
||||
}
|
||||
};
|
||||
|
||||
constexpr FunctionId FunctionId::Invalid = FunctionId(FunctionId::InvalidIndex);
|
||||
|
||||
// The ID of a class.
|
||||
struct ClassId : public IndexBase, public Printable<ClassId> {
|
||||
// An explicitly invalid class ID.
|
||||
static const ClassId Invalid;
|
||||
|
||||
using IndexBase::IndexBase;
|
||||
auto Print(llvm::raw_ostream& out) const -> void {
|
||||
out << "class";
|
||||
@@ -72,6 +80,8 @@ struct ClassId : public IndexBase, public Printable<ClassId> {
|
||||
}
|
||||
};
|
||||
|
||||
constexpr ClassId ClassId::Invalid = ClassId(ClassId::InvalidIndex);
|
||||
|
||||
// The ID of a cross-referenced IR.
|
||||
struct CrossReferenceIRId : public IndexBase,
|
||||
public Printable<CrossReferenceIRId> {
|
||||
@@ -323,6 +333,9 @@ struct Call {
|
||||
|
||||
struct ClassDeclaration {
|
||||
ClassId class_id;
|
||||
// The declaration block, containing the class name's qualifiers and the
|
||||
// class's generic parameters.
|
||||
NodeBlockId decl_block_id;
|
||||
};
|
||||
|
||||
struct ConstType {
|
||||
|
||||
Reference in New Issue
Block a user