Skip to content

Commit

Permalink
Add facet type values and an instruction that produces them (#4460)
Browse files Browse the repository at this point in the history
Still to do:
* Represent facet type values in a canonical form
* Produce & consume facet type values instead of interface values
* `type` should be associated with a canonical facet type value
* Support `&` on facet type values
* Type check and enforce requirements in facet types

---------

Co-authored-by: Josh L <josh11b@users.noreply.github.com>
  • Loading branch information
josh11b and josh11b authored Nov 4, 2024
1 parent 607522c commit ea0b0b4
Show file tree
Hide file tree
Showing 18 changed files with 493 additions and 251 deletions.
4 changes: 2 additions & 2 deletions toolchain/check/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1060,8 +1060,8 @@ class TypeCompleter {
template <typename InstT>
requires(
InstT::Kind
.template IsAnyOf<SemIR::AssociatedEntityType, SemIR::FunctionType,
SemIR::GenericClassType,
.template IsAnyOf<SemIR::AssociatedEntityType, SemIR::FacetType,
SemIR::FunctionType, SemIR::GenericClassType,
SemIR::GenericInterfaceType, SemIR::InterfaceType,
SemIR::UnboundElementType, SemIR::WhereExpr>())
auto BuildValueReprForInst(SemIR::TypeId /*type_id*/, InstT /*inst*/) const
Expand Down
47 changes: 43 additions & 4 deletions toolchain/check/eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ class EvalContext {
auto interfaces() -> const ValueStore<SemIR::InterfaceId>& {
return sem_ir().interfaces();
}
auto facet_types() -> CanonicalValueStore<SemIR::FacetTypeId>& {
return sem_ir().facet_types();
}
auto specifics() -> const SemIR::SpecificStore& {
return sem_ir().specifics();
}
Expand Down Expand Up @@ -1106,6 +1109,20 @@ static auto MakeConstantForCall(EvalContext& eval_context, SemIRLoc loc,
return SemIR::ConstantId::NotConstant;
}

// Creates a FacetType constant.
static auto MakeFacetTypeResult(Context& context,
SemIR::TypeId base_facet_type_id,
SemIR::InstBlockId requirement_block_id,
Phase phase) -> SemIR::ConstantId {
SemIR::FacetTypeId facet_type_id = context.sem_ir().facet_types().Add(
SemIR::FacetTypeInfo{.base_facet_type_id = base_facet_type_id,
.requirement_block_id = requirement_block_id});
return MakeConstantResult(context,
SemIR::FacetType{.type_id = SemIR::TypeId::TypeType,
.facet_type_id = facet_type_id},
phase);
}

// Implementation for `TryEvalInst`, wrapping `Context` with `EvalContext`.
static auto TryEvalInstInContext(EvalContext& eval_context,
SemIR::InstId inst_id, SemIR::Inst inst)
Expand Down Expand Up @@ -1287,6 +1304,23 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
Phase::Template);
}

case CARBON_KIND(SemIR::FacetType facet_type): {
SemIR::FacetTypeInfo info =
eval_context.facet_types().Get(facet_type.facet_type_id);
Phase phase = Phase::Template;
SemIR::TypeId base_facet_type_id =
GetConstantValue(eval_context, info.base_facet_type_id, &phase);
// TODO: process & canonicalize requirements
SemIR::InstBlockId requirement_block_id = info.requirement_block_id;
// If nothing changed, can reuse this instruction.
if (base_facet_type_id == info.base_facet_type_id &&
requirement_block_id == info.requirement_block_id) {
return MakeConstantResult(eval_context.context(), inst, phase);
}
return MakeFacetTypeResult(eval_context.context(), base_facet_type_id,
requirement_block_id, phase);
}

case CARBON_KIND(SemIR::InterfaceDecl interface_decl): {
// If the interface has generic parameters, we don't produce an interface
// type, but a callable whose return value is an interface type.
Expand Down Expand Up @@ -1445,10 +1479,15 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
return eval_context.GetConstantValue(typed_inst.facet_id);
}
case CARBON_KIND(SemIR::WhereExpr typed_inst): {
// TODO: This currently ignores the requirements and just produces the
// left-hand type argument to the `where`.
return eval_context.GetConstantValue(
eval_context.insts().Get(typed_inst.period_self_id).type_id());
SemIR::TypeId base_facet_type_id =
eval_context.insts().Get(typed_inst.period_self_id).type_id();
Phase phase = Phase::Template;
base_facet_type_id =
GetConstantValue(eval_context, base_facet_type_id, &phase);
SemIR::InstBlockId requirement_block_id = typed_inst.requirements_id;
// TODO: process & canonicalize requirements
return MakeFacetTypeResult(eval_context.context(), base_facet_type_id,
requirement_block_id, phase);
}

// `not true` -> `false`, `not false` -> `true`.
Expand Down
2 changes: 2 additions & 0 deletions toolchain/check/handle_where.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ auto HandleParseNode(Context& context, Parse::RequirementEqualEqualId node_id)
auto rhs = context.node_stack().PopExpr();
auto lhs = context.node_stack().PopExpr();
// TODO: type check lhs and rhs are comparable
// TODO: require that at least one side uses a designator

// Build up the list of arguments for the `WhereExpr` inst.
context.args_type_info_stack().AddInstId(
Expand All @@ -103,6 +104,7 @@ auto HandleParseNode(Context& context, Parse::RequirementImplsId node_id)
context.emitter().Emit(rhs_node, ImplsOnNonFacetType);
rhs_as_type.inst_id = SemIR::InstId::BuiltinError;
}
// TODO: require that at least one side uses a designator

// Build up the list of arguments for the `WhereExpr` inst.
context.args_type_info_stack().AddInstId(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

interface I { let T:! type; }

// CHECK:STDERR: fail_todo_impl_assoc_const.carbon:[[@LINE+10]]:1: error: semantics TODO: `impl of interface with associated constant` [SemanticsTodo]
// CHECK:STDERR: fail_todo_impl_assoc_const.carbon:[[@LINE+10]]:1: error: semantics TODO: `impl as non-interface` [SemanticsTodo]
// CHECK:STDERR: impl bool as I where .T = bool {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
Expand Down Expand Up @@ -50,6 +50,7 @@ impl bool as I where .T = bool {}
// CHECK:STDOUT: %.6: type = assoc_entity_type %ImplicitAs.type.3, %Convert.type.2 [template]
// CHECK:STDOUT: %.7: %.6 = assoc_entity element0, imports.%import_ref.6 [template]
// CHECK:STDOUT: %.8: %.4 = assoc_entity element0, imports.%import_ref.7 [symbolic]
// CHECK:STDOUT: %.9: type = facet_type <facet-type %I.type+requirements> [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
Expand Down Expand Up @@ -90,7 +91,7 @@ impl bool as I where .T = bool {}
// CHECK:STDOUT: %.loc23_27.2: ref type = temporary_storage
// CHECK:STDOUT: %.loc23_27.3: ref type = temporary %.loc23_27.2, %bool.make_type.loc23_27
// CHECK:STDOUT: %.loc23_27.4: %.1 = converted %bool.make_type.loc23_27, <error> [template = <error>]
// CHECK:STDOUT: %.loc23_16: type = where_expr %.Self [template = constants.%I.type] {
// CHECK:STDOUT: %.loc23_16: type = where_expr %.Self [template = constants.%.9] {
// CHECK:STDOUT: requirement_rewrite %T.ref, <error>
// CHECK:STDOUT: }
// CHECK:STDOUT: }
Expand Down
Loading

0 comments on commit ea0b0b4

Please sign in to comment.