Skip to content

Commit

Permalink
Treat associated constants as entities parameterized by Self (#4837)
Browse files Browse the repository at this point in the history
Add a full entity representation for associated constants, and build a
`Generic` object for them. This `Generic` is parameterized by the
enclosing `Self` type, allowing the use of `Self` within the type of the
associated constant to be supported.

When performing impl lookup for an associated constant, produce the type
with the provided self type substituted for its `Self` along with any
generic parameters of the interface.

Split the handling of associated constant declarations into two parts,
corresponding to the code before the `=`, and the code between the `=`
and `;` (if any). The former goes into the generic declaration region;
the latter into the generic definition region. This prepares us to
handle the default value for an associated constant, but for now we're
just storing the information and not actually using it.

Remove the entity type field from `assoc_entity_type`, because it's
almost unused and is an attractive nuisance -- it must necessarily be a
type in the generic scope of the associated constant rather than in the
scope of the instruction (because there is no `Self` anywhere else),
which means that it's hard to substitute into or derive meaning from.

See `toolchain/check/testdata/impl/assoc_const_self.carbon` for tests of
the new functionality; these used to cause the toolchain to crash.
  • Loading branch information
zygoloid authored Jan 25, 2025
1 parent b06fcc9 commit 5f888e1
Show file tree
Hide file tree
Showing 108 changed files with 3,726 additions and 1,680 deletions.
16 changes: 2 additions & 14 deletions toolchain/check/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ cc_library(
"import_cpp.cpp",
"import_ref.cpp",
"inst_block_stack.cpp",
"interface.cpp",
"literal.cpp",
"member_access.cpp",
"merge.cpp",
Expand Down Expand Up @@ -56,6 +57,7 @@ cc_library(
"import_cpp.h",
"import_ref.h",
"inst_block_stack.h",
"interface.h",
"keyword_modifier_set.h",
"literal.h",
"member_access.h",
Expand Down Expand Up @@ -137,7 +139,6 @@ cc_library(
":context",
":dump",
":impl",
":interface",
":pointer_dereference",
":sem_ir_diagnostic_converter",
"//common:check",
Expand Down Expand Up @@ -210,19 +211,6 @@ cc_library(
],
)

cc_library(
name = "interface",
srcs = ["interface.cpp"],
hdrs = ["interface.h"],
deps = [
":context",
"//common:check",
"//toolchain/sem_ir:file",
"//toolchain/sem_ir:inst",
"//toolchain/sem_ir:typed_insts",
],
)

cc_library(
name = "node_stack",
srcs = ["node_stack.cpp"],
Expand Down
19 changes: 15 additions & 4 deletions toolchain/check/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1454,6 +1454,19 @@ auto Context::RequireDefinedType(SemIR::TypeId type_id, SemIR::LocId loc_id,
}
}
// TODO: Finish facet type resolution.
//
// Note that we will need Self to be passed into facet type resolution.
// The `.Self` of a facet type created by `where` will then be bound to the
// provided self type.
//
// For example, in `T:! X where ...`, we will bind the `.Self` of the
// `where` facet type to `T`, and in `(X where ...) where ...`, we will bind
// the inner `.Self` to the outer `.Self`.
//
// If the facet type contains a rewrite, we may have deferred converting the
// rewritten value to the type of the associated constant. That conversion
// should also be performed as part of resolution, and may depend on the
// Self type.
}

return true;
Expand Down Expand Up @@ -1516,11 +1529,9 @@ auto Context::GetTupleType(llvm::ArrayRef<SemIR::TypeId> type_ids)
type_blocks().AddCanonical(type_ids));
}

auto Context::GetAssociatedEntityType(SemIR::TypeId interface_type_id,
SemIR::TypeId entity_type_id)
auto Context::GetAssociatedEntityType(SemIR::TypeId interface_type_id)
-> SemIR::TypeId {
return GetTypeImpl<SemIR::AssociatedEntityType>(*this, interface_type_id,
entity_type_id);
return GetTypeImpl<SemIR::AssociatedEntityType>(*this, interface_type_id);
}

auto Context::GetSingletonType(SemIR::InstId singleton_id) -> SemIR::TypeId {
Expand Down
14 changes: 11 additions & 3 deletions toolchain/check/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -467,9 +467,14 @@ class Context {

// TODO: Consider moving these `Get*Type` functions to a separate class.

// Gets the type for the name of an associated entity.
auto GetAssociatedEntityType(SemIR::TypeId interface_type_id,
SemIR::TypeId entity_type_id) -> SemIR::TypeId;
// Gets the type to use for an unbound associated entity declared in this
// interface. For example, this is the type of `I.T` after
// `interface I { let T:! type; }`.
// The name of the interface is used for diagnostics.
// TODO: Should we use a different type for each such entity, or the same type
// for all associated entities?
auto GetAssociatedEntityType(SemIR::TypeId interface_type_id)
-> SemIR::TypeId;

// Gets a singleton type. The returned type will be complete. Requires that
// `singleton_id` is already validated to be a singleton.
Expand Down Expand Up @@ -628,6 +633,9 @@ class Context {
auto interfaces() -> ValueStore<SemIR::InterfaceId>& {
return sem_ir().interfaces();
}
auto associated_constants() -> ValueStore<SemIR::AssociatedConstantId>& {
return sem_ir().associated_constants();
}
auto facet_types() -> CanonicalValueStore<SemIR::FacetTypeId>& {
return sem_ir().facet_types();
}
Expand Down
3 changes: 1 addition & 2 deletions toolchain/check/eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1568,8 +1568,7 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
&SemIR::AssociatedEntity::type_id);
case SemIR::AssociatedEntityType::Kind:
return RebuildIfFieldsAreConstant(
eval_context, inst, &SemIR::AssociatedEntityType::interface_type_id,
&SemIR::AssociatedEntityType::entity_type_id);
eval_context, inst, &SemIR::AssociatedEntityType::interface_type_id);
case SemIR::BoundMethod::Kind:
return RebuildIfFieldsAreConstant(eval_context, inst,
&SemIR::BoundMethod::type_id,
Expand Down
5 changes: 5 additions & 0 deletions toolchain/check/generic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "toolchain/check/eval.h"
#include "toolchain/check/generic_region_stack.h"
#include "toolchain/check/subst.h"
#include "toolchain/sem_ir/generic.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/inst.h"
#include "toolchain/sem_ir/typed_insts.h"
Expand Down Expand Up @@ -506,6 +507,10 @@ auto GetInstForSpecific(Context& context, SemIR::SpecificId specific_id)
.callee_id = generic.decl_id,
.specific_id = specific_id}));
}
case SemIR::AssociatedConstantDecl::Kind: {
// TODO: We don't have a good instruction to use here.
return generic.decl_id;
}
default: {
CARBON_FATAL("Unknown kind for generic declaration {0}", decl);
}
Expand Down
1 change: 1 addition & 0 deletions toolchain/check/generic.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ auto ResolveSpecificDefinition(Context& context, SemIRLoc loc,
SemIR::SpecificId specific_id) -> bool;

// Returns an instruction describing the entity named by the given specific.
// This is used to name the entity in diagnostics.
auto GetInstForSpecific(Context& context, SemIR::SpecificId specific_id)
-> SemIR::InstId;

Expand Down
26 changes: 13 additions & 13 deletions toolchain/check/handle_binding_pattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,23 +159,23 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
return context.emitter().Build(type_node, IncompleteTypeInAssociatedDecl,
cast_type_id);
});
context.entity_names().Add(

SemIR::AssociatedConstantDecl assoc_const_decl = {
.type_id = cast_type_id,
.assoc_const_id = SemIR::AssociatedConstantId::None,
.decl_block_id = SemIR::InstBlockId::None};
auto decl_id = context.AddPlaceholderInstInNoBlock(SemIR::LocIdAndInst(
context.parse_tree().As<Parse::CompileTimeBindingPatternId>(node_id),
assoc_const_decl));
assoc_const_decl.assoc_const_id = context.associated_constants().Add(
{.name_id = name_id,
.parent_scope_id = context.scope_stack().PeekNameScopeId(),
.bind_index = SemIR::CompileTimeBindIndex::None});
.decl_id = decl_id,
.generic_id = SemIR::GenericId::None,
.default_value_id = SemIR::InstId::None});
context.ReplaceInstBeforeConstantUse(decl_id, assoc_const_decl);

SemIR::InstId decl_id = context.AddInst<SemIR::AssociatedConstantDecl>(
context.parse_tree().As<Parse::CompileTimeBindingPatternId>(node_id),
{cast_type_id, name_id});
context.node_stack().Push(node_id, decl_id);

// Add an associated entity name to the interface scope.
auto assoc_id = BuildAssociatedEntity(
context, parent_interface_decl->interface_id, decl_id);
auto name_context =
context.decl_name_stack().MakeUnqualifiedName(node_id, name_id);
context.decl_name_stack().AddNameOrDiagnose(
name_context, assoc_id, introducer.modifier_set.GetAccessKind());
return true;
}

Expand Down
Loading

0 comments on commit 5f888e1

Please sign in to comment.