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:
Richard Smith
2023-10-18 19:49:45 +00:00
committed by GitHub
co-authored by Jon Ross-Perkins
parent 3b82ea96db
commit b01dfb3f93
25 changed files with 740 additions and 135 deletions
+14 -6
View File
@@ -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;
+13 -1
View File
@@ -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;
+54 -19
View File
@@ -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;
}
+8 -2
View File
@@ -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.
+84 -17
View File
@@ -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
+71 -22
View File
@@ -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);
+34 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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: }
+58
View File
@@ -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: }
+49
View File
@@ -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: }
+47
View File
@@ -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
View File
@@ -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:
+31
View File
@@ -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
View File
@@ -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: }
+7 -9
View File
@@ -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)
+11 -3
View File
@@ -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
View File
@@ -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(); }
+35 -22
View File
@@ -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_ << '}';
}
+13
View File
@@ -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 {