Skip to content

Commit

Permalink
Distinguish between whether an entity has its own parameter lists and…
Browse files Browse the repository at this point in the history
… whether it is generic. (#4191)

It's actually possible to get into all four combinations of having
parameter lists versus being generic:

- An entity nested within a generic, such as a member class, can be
generic even if it has no parameters.

- As a corner case, an entity with an *empty* parameter list has
parameter lists, but isn't a generic because it doesn't have any generic
parameters.
  • Loading branch information
zygoloid authored Aug 6, 2024
1 parent 8a8c227 commit f6ff5b1
Show file tree
Hide file tree
Showing 11 changed files with 606 additions and 69 deletions.
17 changes: 9 additions & 8 deletions toolchain/check/eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1109,7 +1109,7 @@ static auto MakeConstantForCall(EvalContext& eval_context, SemIRLoc loc,
eval_context.insts().Get(call.callee_id).type_id());
CARBON_KIND_SWITCH(type_inst) {
case CARBON_KIND(SemIR::GenericClassType generic_class): {
auto specific_id = MakeSpecific(
auto specific_id = MakeSpecificIfGeneric(
eval_context.context(),
eval_context.classes().Get(generic_class.class_id).generic_id,
call.args_id);
Expand All @@ -1121,11 +1121,12 @@ static auto MakeConstantForCall(EvalContext& eval_context, SemIRLoc loc,
phase);
}
case CARBON_KIND(SemIR::GenericInterfaceType generic_interface): {
auto specific_id = MakeSpecific(eval_context.context(),
eval_context.interfaces()
.Get(generic_interface.interface_id)
.generic_id,
call.args_id);
auto specific_id =
MakeSpecificIfGeneric(eval_context.context(),
eval_context.interfaces()
.Get(generic_interface.interface_id)
.generic_id,
call.args_id);
return MakeConstantResult(
eval_context.context(),
SemIR::InterfaceType{.type_id = call.type_id,
Expand Down Expand Up @@ -1286,7 +1287,7 @@ auto TryEvalInstInContext(EvalContext& eval_context, SemIR::InstId inst_id,
case CARBON_KIND(SemIR::ClassDecl class_decl): {
// If the class has generic parameters, we don't produce a class type, but
// a callable whose return value is a class type.
if (eval_context.classes().Get(class_decl.class_id).is_generic()) {
if (eval_context.classes().Get(class_decl.class_id).has_parameters()) {
return TransformIfFieldsAreConstant(
eval_context, class_decl,
[&](SemIR::ClassDecl result) {
Expand All @@ -1309,7 +1310,7 @@ auto TryEvalInstInContext(EvalContext& eval_context, SemIR::InstId inst_id,
// type, but a callable whose return value is an interface type.
if (eval_context.interfaces()
.Get(interface_decl.interface_id)
.is_generic()) {
.has_parameters()) {
return TransformIfFieldsAreConstant(
eval_context, interface_decl,
[&](SemIR::InterfaceDecl result) {
Expand Down
9 changes: 9 additions & 0 deletions toolchain/check/generic.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ auto RebuildGenericEvalBlock(Context& context, SemIR::GenericId generic_id,
auto MakeSpecific(Context& context, SemIR::GenericId generic_id,
SemIR::InstBlockId args_id) -> SemIR::SpecificId;

// Builds a new specific if the given generic is valid. Otherwise returns an
// invalid specific.
inline auto MakeSpecificIfGeneric(Context& context, SemIR::GenericId generic_id,
SemIR::InstBlockId args_id)
-> SemIR::SpecificId {
return generic_id.is_valid() ? MakeSpecific(context, generic_id, args_id)
: SemIR::SpecificId::Invalid;
}

// Builds the specific that describes how the generic should refer to itself.
// For example, for a generic `G(T:! type)`, this is the specific `G(T)`. For an
// invalid `generic_id`, returns an invalid specific ID.
Expand Down
20 changes: 8 additions & 12 deletions toolchain/check/handle_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
// here. We should keep track of it even if the name is invalid.
class_info.generic_id = FinishGenericDecl(context, class_decl_id);
class_decl.class_id = context.classes().Add(class_info);
if (class_info.is_generic()) {
if (class_info.has_parameters()) {
class_decl.type_id = context.GetGenericClassType(class_decl.class_id);
}
} else {
Expand All @@ -251,17 +251,13 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
// TODO: Form this as part of building the definition, not as part of the
// declaration.
auto& class_info = context.classes().Get(class_decl.class_id);
if (class_info.is_generic()) {
auto specific_id =
context.generics().GetSelfSpecific(class_info.generic_id);
class_info.self_type_id = context.GetTypeIdForTypeConstant(
TryEvalInst(context, SemIR::InstId::Invalid,
SemIR::ClassType{.type_id = SemIR::TypeId::TypeType,
.class_id = class_decl.class_id,
.specific_id = specific_id}));
} else {
class_info.self_type_id = context.GetTypeIdForTypeInst(class_decl_id);
}
auto specific_id =
context.generics().GetSelfSpecific(class_info.generic_id);
class_info.self_type_id = context.GetTypeIdForTypeConstant(
TryEvalInst(context, SemIR::InstId::Invalid,
SemIR::ClassType{.type_id = SemIR::TypeId::TypeType,
.class_id = class_decl.class_id,
.specific_id = specific_id}));
}

if (!is_definition && context.IsImplFile() && !is_extern) {
Expand Down
31 changes: 14 additions & 17 deletions toolchain/check/handle_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ static auto BuildInterfaceDecl(Context& context,
.param_refs_id = name.params_id,
.decl_id = interface_decl_id}};
interface_decl.interface_id = context.interfaces().Add(interface_info);
if (interface_info.is_generic()) {
if (interface_info.has_parameters()) {
interface_decl.type_id =
context.GetGenericInterfaceType(interface_decl.interface_id);
}
Expand Down Expand Up @@ -141,10 +141,9 @@ auto HandleParseNode(Context& context,
interface_info.parent_scope_id);
}

// Enter the interface scope.
context.scope_stack().Push(
interface_decl_id, interface_info.scope_id,
context.generics().GetSelfSpecific(interface_info.generic_id));
auto self_specific_id =
context.generics().GetSelfSpecific(interface_info.generic_id);

StartGenericDefinition(context);

context.inst_block_stack().Push();
Expand All @@ -155,18 +154,12 @@ auto HandleParseNode(Context& context,

// Declare and introduce `Self`.
if (!interface_info.is_defined()) {
SemIR::TypeId self_type_id = SemIR::TypeId::Invalid;
if (interface_info.is_generic()) {
auto specific_id =
context.generics().GetSelfSpecific(interface_info.generic_id);
self_type_id = context.GetTypeIdForTypeConstant(
TryEvalInst(context, SemIR::InstId::Invalid,
SemIR::InterfaceType{.type_id = SemIR::TypeId::TypeType,
.interface_id = interface_id,
.specific_id = specific_id}));
} else {
self_type_id = context.GetTypeIdForTypeInst(interface_decl_id);
}
auto interface_type =
SemIR::InterfaceType{.type_id = SemIR::TypeId::TypeType,
.interface_id = interface_id,
.specific_id = self_specific_id};
SemIR::TypeId self_type_id = context.GetTypeIdForTypeConstant(
TryEvalInst(context, SemIR::InstId::Invalid, interface_type));

// We model `Self` as a symbolic binding whose type is the interface.
// Because there is no equivalent non-symbolic value, we use `Invalid` as
Expand All @@ -185,6 +178,10 @@ auto HandleParseNode(Context& context,
interface_info.self_param_id);
}

// Enter the interface scope.
context.scope_stack().Push(interface_decl_id, interface_info.scope_id,
self_specific_id);

// TODO: Handle the case where there's control flow in the interface body. For
// example:
//
Expand Down
4 changes: 2 additions & 2 deletions toolchain/check/import_ref.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1137,7 +1137,7 @@ class ImportRefResolver {
{.self_type_id = SemIR::TypeId::Invalid,
.inheritance_kind = import_class.inheritance_kind}});

if (import_class.is_generic()) {
if (import_class.has_parameters()) {
class_decl.type_id = context_.GetGenericClassType(class_decl.class_id);
}

Expand Down Expand Up @@ -1498,7 +1498,7 @@ class ImportRefResolver {
{GetIncompleteLocalEntityBase(interface_decl_id, import_interface),
{}});

if (import_interface.is_generic()) {
if (import_interface.has_parameters()) {
interface_decl.type_id =
context_.GetGenericInterfaceType(interface_decl.interface_id);
}
Expand Down
Loading

0 comments on commit f6ff5b1

Please sign in to comment.