Skip to content

Commit

Permalink
where check stage, step 2: SemIR (#4349)
Browse files Browse the repository at this point in the history
The check stage now produces SemIR instructions to represent a `where`
clause. It still does not check types.

---------

Co-authored-by: Josh L <josh11b@users.noreply.github.com>
Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
  • Loading branch information
3 people authored Sep 27, 2024
1 parent 4c9ffb0 commit bdbd107
Show file tree
Hide file tree
Showing 13 changed files with 195 additions and 43 deletions.
10 changes: 6 additions & 4 deletions toolchain/check/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1073,10 +1073,12 @@ class TypeCompleter {
}

template <typename InstT>
requires(InstT::Kind.template IsAnyOf<
SemIR::AssociatedEntityType, SemIR::FunctionType,
SemIR::GenericClassType, SemIR::GenericInterfaceType,
SemIR::InterfaceType, SemIR::UnboundElementType>())
requires(
InstT::Kind
.template IsAnyOf<SemIR::AssociatedEntityType, SemIR::FunctionType,
SemIR::GenericClassType,
SemIR::GenericInterfaceType, SemIR::InterfaceType,
SemIR::UnboundElementType, SemIR::WhereExpr>())
auto BuildValueReprForInst(SemIR::TypeId /*type_id*/, InstT /*inst*/) const
-> SemIR::ValueRepr {
// These types have no runtime operations, so we use an empty value
Expand Down
9 changes: 9 additions & 0 deletions toolchain/check/eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1403,6 +1403,12 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
// here. For now, we model a facet value as just a type.
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());
}

// `not true` -> `false`, `not false` -> `true`.
// All other uses of unary `not` are non-constant.
Expand Down Expand Up @@ -1448,6 +1454,9 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
case SemIR::ImplDecl::Kind:
case SemIR::ImportDecl::Kind:
case SemIR::Param::Kind:
case SemIR::RequirementEquivalent::Kind:
case SemIR::RequirementImpls::Kind:
case SemIR::RequirementRewrite::Kind:
case SemIR::ReturnExpr::Kind:
case SemIR::Return::Kind:
case SemIR::StructLiteral::Kind:
Expand Down
68 changes: 48 additions & 20 deletions toolchain/check/handle_where.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,14 @@

namespace Carbon::Check {

auto HandleParseNode(Context& context, Parse::WhereOperandId /*node_id*/)
-> bool {
auto HandleParseNode(Context& context, Parse::WhereOperandId node_id) -> bool {
// The expression at the top of the stack represents a constraint type that
// is being modified by the `where` operator. It would be `MyInterface` in
// `MyInterface where .Member = i32`.
auto [self_node, self_id] = context.node_stack().PopExprWithNodeId();
auto self_type_id = ExprAsType(context, self_node, self_id);
// TODO: Do this instead once `WhereExpr` is ready to consume this:
// context.node_stack().Push(node_id, self_type_id);
context.node_stack().Push(self_node, self_id);
// TODO: Validate that `self_type_id` represents a facet type. Only facet
// types may have `where` restrictions.

// Introduce a name scope so that we can remove the `.Self` entry we are
// adding to name lookup at the end of the `where` expression.
Expand All @@ -43,30 +41,54 @@ auto HandleParseNode(Context& context, Parse::WhereOperandId /*node_id*/)
context.scope_stack().LookupOrAddName(SemIR::NameId::PeriodSelf, inst_id);
// Shouldn't have any names in newly created scope.
CARBON_CHECK(!existing.is_valid());

// Save the `.Self` symbolic binding on the node stack. It will become the
// first argument to the `WhereExpr` instruction.
context.node_stack().Push(node_id, inst_id);

// Going to put each requirement on `args_type_info_stack`, so we can have an
// inst block with the varying number of requirements but keeping other
// instructions on the current inst block from the `inst_block_stack()`.
context.args_type_info_stack().Push();
return true;
}

auto HandleParseNode(Context& context, Parse::RequirementEqualId /*node_id*/)
auto HandleParseNode(Context& context, Parse::RequirementEqualId node_id)
-> bool {
// TODO: Implement
context.node_stack().PopExpr();
context.node_stack().PopExpr();
auto rhs = context.node_stack().PopExpr();
auto lhs = context.node_stack().PopExpr();
// TODO: convert rhs to type of lhs

// Build up the list of arguments for the `WhereExpr` inst.
context.args_type_info_stack().AddInstId(
context.AddInstInNoBlock<SemIR::RequirementRewrite>(
node_id, {.lhs_id = lhs, .rhs_id = rhs}));
return true;
}

auto HandleParseNode(Context& context,
Parse::RequirementEqualEqualId /*node_id*/) -> bool {
// TODO: Implement
context.node_stack().PopExpr();
context.node_stack().PopExpr();
auto HandleParseNode(Context& context, Parse::RequirementEqualEqualId node_id)
-> bool {
auto rhs = context.node_stack().PopExpr();
auto lhs = context.node_stack().PopExpr();
// TODO: type check lhs and rhs are compatible

// Build up the list of arguments for the `WhereExpr` inst.
context.args_type_info_stack().AddInstId(
context.AddInstInNoBlock<SemIR::RequirementEquivalent>(
node_id, {.lhs_id = lhs, .rhs_id = rhs}));
return true;
}

auto HandleParseNode(Context& context, Parse::RequirementImplsId /*node_id*/)
auto HandleParseNode(Context& context, Parse::RequirementImplsId node_id)
-> bool {
// TODO: Implement
context.node_stack().PopExpr();
context.node_stack().PopExpr();
auto rhs = context.node_stack().PopExpr();
auto lhs = context.node_stack().PopExpr();
// TODO: check lhs is a facet and rhs is a facet type

// Build up the list of arguments for the `WhereExpr` inst.
context.args_type_info_stack().AddInstId(
context.AddInstInNoBlock<SemIR::RequirementImpls>(
node_id, {.lhs_id = lhs, .rhs_id = rhs}));
return true;
}

Expand All @@ -76,14 +98,20 @@ auto HandleParseNode(Context& /*context*/, Parse::RequirementAndId /*node_id*/)
return true;
}

auto HandleParseNode(Context& context, Parse::WhereExprId /*node_id*/) -> bool {
auto HandleParseNode(Context& context, Parse::WhereExprId node_id) -> bool {
// Discard the generic region containing `.Self` and the constraints.
// TODO: Decide if we want to build a `Generic` object for this.
DiscardGenericDecl(context);
// Remove `PeriodSelf` from name lookup, undoing the `Push` done for the
// `WhereOperand`.
context.scope_stack().Pop();
// TODO: Output instruction for newly formed restricted constraint type.
SemIR::InstId period_self_id =
context.node_stack().Pop<Parse::NodeKind::WhereOperand>();
SemIR::InstBlockId requirements_id = context.args_type_info_stack().Pop();
context.AddInstAndPush<SemIR::WhereExpr>(
node_id, {.type_id = SemIR::TypeId::TypeType,
.period_self_id = period_self_id,
.requirements_id = requirements_id});
return true;
}

Expand Down
3 changes: 1 addition & 2 deletions toolchain/check/node_stack.h
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ class NodeStack {
case Parse::NodeKind::ShortCircuitOperandOr:
case Parse::NodeKind::StructField:
case Parse::NodeKind::StructTypeField:
case Parse::NodeKind::WhereOperand:
return Id::KindFor<SemIR::InstId>();
case Parse::NodeKind::IfCondition:
case Parse::NodeKind::IfExprIf:
Expand All @@ -449,8 +450,6 @@ class NodeStack {
case Parse::NodeKind::DefaultLibrary:
case Parse::NodeKind::LibraryName:
return Id::KindFor<SemIR::LibraryNameId>();
case Parse::NodeKind::WhereOperand:
return Id::KindFor<SemIR::TypeId>();
case Parse::NodeKind::ArrayExprSemi:
case Parse::NodeKind::BuiltinName:
case Parse::NodeKind::ClassIntroducer:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ impl bool as I where .T = bool {}
// CHECK:STDOUT: %.Self.ref: %.1 = name_ref .Self, %.Self [symbolic = constants.%.Self]
// CHECK:STDOUT: %T.ref: %.2 = name_ref T, @I.%.loc11 [template = constants.%.3]
// CHECK:STDOUT: %bool.make_type.loc16_27: init type = call constants.%Bool() [template = bool]
// CHECK:STDOUT: %.loc16_16: type = where_expr %.Self [template = constants.%.1] {
// CHECK:STDOUT: requirement_rewrite %T.ref, %bool.make_type.loc16_27
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
Expand Down
43 changes: 31 additions & 12 deletions toolchain/check/testdata/where_expr/constraints.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,10 @@ fn NotEmptyStruct() {
// CHECK:STDOUT: %.Self: %.2 = bind_symbolic_name .Self 0 [symbolic = constants.%.Self.1]
// CHECK:STDOUT: %.Self.ref: %.2 = name_ref .Self, %.Self [symbolic = constants.%.Self.1]
// CHECK:STDOUT: %Member.ref: %.3 = name_ref Member, @I.%.loc7 [template = constants.%.4]
// CHECK:STDOUT: %.loc11: %.8 = struct_literal ()
// CHECK:STDOUT: %.loc11_33: %.8 = struct_literal ()
// CHECK:STDOUT: %.loc11_16: type = where_expr %.Self [template = constants.%.2] {
// CHECK:STDOUT: requirement_rewrite %Member.ref, %.loc11_33
// CHECK:STDOUT: }
// CHECK:STDOUT: %T.param: %.2 = param T, runtime_param<invalid>
// CHECK:STDOUT: %T.loc11: %.2 = bind_symbolic_name T 0, %T.param [symbolic = %T.1 (constants.%T)]
// CHECK:STDOUT: }
Expand All @@ -151,7 +154,10 @@ fn NotEmptyStruct() {
// CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [template = constants.%.2]
// CHECK:STDOUT: %.Self: %.2 = bind_symbolic_name .Self 0 [symbolic = constants.%.Self.1]
// CHECK:STDOUT: %.Self.ref: %.2 = name_ref .Self, %.Self [symbolic = constants.%.Self.1]
// CHECK:STDOUT: %.loc13: %.5 = tuple_literal ()
// CHECK:STDOUT: %.loc13_37: %.5 = tuple_literal ()
// CHECK:STDOUT: %.loc13_21: type = where_expr %.Self [template = constants.%.2] {
// CHECK:STDOUT: requirement_equivalent %.Self.ref, %.loc13_37
// CHECK:STDOUT: }
// CHECK:STDOUT: %U.param: %.2 = param U, runtime_param<invalid>
// CHECK:STDOUT: %U.loc13: %.2 = bind_symbolic_name U 0, %U.param [symbolic = %U.1 (constants.%U)]
// CHECK:STDOUT: }
Expand All @@ -162,6 +168,9 @@ fn NotEmptyStruct() {
// CHECK:STDOUT: %.Self: %.1 = bind_symbolic_name .Self 0 [symbolic = constants.%.Self.2]
// CHECK:STDOUT: %.Self.ref: %.1 = name_ref .Self, %.Self [symbolic = constants.%.Self.2]
// CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [template = constants.%.2]
// CHECK:STDOUT: %.loc15: type = where_expr %.Self [template = constants.%.1] {
// CHECK:STDOUT: requirement_impls %.Self.ref, %I.ref
// CHECK:STDOUT: }
// CHECK:STDOUT: %V.param: %.1 = param V, runtime_param<invalid>
// CHECK:STDOUT: %V.loc15: %.1 = bind_symbolic_name V 0, %V.param [symbolic = %V.1 (constants.%V)]
// CHECK:STDOUT: }
Expand All @@ -177,6 +186,10 @@ fn NotEmptyStruct() {
// CHECK:STDOUT: %Member.ref: %.3 = name_ref Member, @I.%.loc7 [template = constants.%.4]
// CHECK:STDOUT: %.Self.ref.loc17_50: %.2 = name_ref .Self, %.Self [symbolic = constants.%.Self.1]
// CHECK:STDOUT: %Second.ref.loc17_50: %.6 = name_ref Second, @I.%.loc8 [template = constants.%.7]
// CHECK:STDOUT: %.loc17: type = where_expr %.Self [template = constants.%.2] {
// CHECK:STDOUT: requirement_impls %Second.ref.loc17_20, %I.ref.loc17_34
// CHECK:STDOUT: requirement_rewrite %Member.ref, %Second.ref.loc17_50
// CHECK:STDOUT: }
// CHECK:STDOUT: %W.param: %.2 = param W, runtime_param<invalid>
// CHECK:STDOUT: %W.loc17: %.2 = bind_symbolic_name W 0, %W.param [symbolic = %W.1 (constants.%W)]
// CHECK:STDOUT: }
Expand Down Expand Up @@ -263,10 +276,10 @@ fn NotEmptyStruct() {
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %import_ref.1 = import_ref Main//state_constraints, inst+3, unloaded
// CHECK:STDOUT: %import_ref.2: type = import_ref Main//state_constraints, inst+7, loaded [template = constants.%.1]
// CHECK:STDOUT: %import_ref.3 = import_ref Main//state_constraints, inst+32, unloaded
// CHECK:STDOUT: %import_ref.4 = import_ref Main//state_constraints, inst+44, unloaded
// CHECK:STDOUT: %import_ref.5 = import_ref Main//state_constraints, inst+57, unloaded
// CHECK:STDOUT: %import_ref.6 = import_ref Main//state_constraints, inst+74, unloaded
// CHECK:STDOUT: %import_ref.3 = import_ref Main//state_constraints, inst+34, unloaded
// CHECK:STDOUT: %import_ref.4 = import_ref Main//state_constraints, inst+48, unloaded
// CHECK:STDOUT: %import_ref.5 = import_ref Main//state_constraints, inst+63, unloaded
// CHECK:STDOUT: %import_ref.6 = import_ref Main//state_constraints, inst+83, unloaded
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [template] {
// CHECK:STDOUT: import Core//prelude
// CHECK:STDOUT: import Core//prelude/operators
Expand Down Expand Up @@ -305,7 +318,10 @@ fn NotEmptyStruct() {
// CHECK:STDOUT: %.Self: %.1 = bind_symbolic_name .Self 0 [symbolic = constants.%.Self]
// CHECK:STDOUT: %.Self.ref: %.1 = name_ref .Self, %.Self [symbolic = constants.%.Self]
// CHECK:STDOUT: %Member.ref: %.3 = name_ref Member, imports.%import_ref.8 [template = constants.%.4]
// CHECK:STDOUT: %.loc8: i32 = int_literal 2 [template = constants.%.5]
// CHECK:STDOUT: %.loc8_39: i32 = int_literal 2 [template = constants.%.5]
// CHECK:STDOUT: %.loc8_23: type = where_expr %.Self [template = constants.%.1] {
// CHECK:STDOUT: requirement_rewrite %Member.ref, %.loc8_39
// CHECK:STDOUT: }
// CHECK:STDOUT: %X.param: %.1 = param X, runtime_param<invalid>
// CHECK:STDOUT: %X.loc8: %.1 = bind_symbolic_name X 0, %X.param [symbolic = %X.1 (constants.%X)]
// CHECK:STDOUT: }
Expand Down Expand Up @@ -371,10 +387,10 @@ fn NotEmptyStruct() {
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %import_ref.1: type = import_ref Main//state_constraints, inst+3, loaded [template = constants.%.3]
// CHECK:STDOUT: %import_ref.2 = import_ref Main//state_constraints, inst+7, unloaded
// CHECK:STDOUT: %import_ref.3 = import_ref Main//state_constraints, inst+32, unloaded
// CHECK:STDOUT: %import_ref.4 = import_ref Main//state_constraints, inst+44, unloaded
// CHECK:STDOUT: %import_ref.5: %Impls.type = import_ref Main//state_constraints, inst+57, loaded [template = constants.%Impls]
// CHECK:STDOUT: %import_ref.6 = import_ref Main//state_constraints, inst+74, unloaded
// CHECK:STDOUT: %import_ref.3 = import_ref Main//state_constraints, inst+34, unloaded
// CHECK:STDOUT: %import_ref.4 = import_ref Main//state_constraints, inst+48, unloaded
// CHECK:STDOUT: %import_ref.5: %Impls.type = import_ref Main//state_constraints, inst+63, loaded [template = constants.%Impls]
// CHECK:STDOUT: %import_ref.6 = import_ref Main//state_constraints, inst+83, unloaded
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [template] {
// CHECK:STDOUT: .ImplicitAs = %import_ref.8
// CHECK:STDOUT: import Core//prelude
Expand Down Expand Up @@ -423,7 +439,10 @@ fn NotEmptyStruct() {
// CHECK:STDOUT: %J.ref: type = name_ref J, imports.%import_ref.1 [template = constants.%.3]
// CHECK:STDOUT: %.Self: %.3 = bind_symbolic_name .Self 0 [symbolic = constants.%.Self]
// CHECK:STDOUT: %.Self.ref: %.3 = name_ref .Self, %.Self [symbolic = constants.%.Self]
// CHECK:STDOUT: %.loc26: %.1 = struct_literal ()
// CHECK:STDOUT: %.loc26_38: %.1 = struct_literal ()
// CHECK:STDOUT: %.loc26_22: type = where_expr %.Self [template = constants.%.3] {
// CHECK:STDOUT: requirement_equivalent %.Self.ref, %.loc26_38
// CHECK:STDOUT: }
// CHECK:STDOUT: %Y.param: %.3 = param Y, runtime_param<invalid>
// CHECK:STDOUT: %Y.loc26: %.3 = bind_symbolic_name Y 0, %Y.param [symbolic = %Y.1 (constants.%Y)]
// CHECK:STDOUT: }
Expand Down
18 changes: 15 additions & 3 deletions toolchain/check/testdata/where_expr/no_prelude/designator.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,10 @@ class D {
// CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [template = constants.%.1]
// CHECK:STDOUT: %.Self: %.1 = bind_symbolic_name .Self 0 [symbolic = constants.%.Self.1]
// CHECK:STDOUT: %.Self.ref: %.1 = name_ref .Self, %.Self [symbolic = constants.%.Self.1]
// CHECK:STDOUT: %.loc8: %.4 = tuple_literal ()
// CHECK:STDOUT: %.loc8_37: %.4 = tuple_literal ()
// CHECK:STDOUT: %.loc8_21: type = where_expr %.Self [template = constants.%.1] {
// CHECK:STDOUT: requirement_equivalent %.Self.ref, %.loc8_37
// CHECK:STDOUT: }
// CHECK:STDOUT: %T.param: %.1 = param T, runtime_param<invalid>
// CHECK:STDOUT: %T.loc8: %.1 = bind_symbolic_name T 0, %T.param [symbolic = %T.1 (constants.%T)]
// CHECK:STDOUT: }
Expand All @@ -133,7 +136,10 @@ class D {
// CHECK:STDOUT: %.Self: %.1 = bind_symbolic_name .Self 0 [symbolic = constants.%.Self.1]
// CHECK:STDOUT: %.Self.ref: %.1 = name_ref .Self, %.Self [symbolic = constants.%.Self.1]
// CHECK:STDOUT: %Member.ref: %.2 = name_ref Member, @I.%.loc5 [template = constants.%.3]
// CHECK:STDOUT: %.loc10: %.5 = struct_literal ()
// CHECK:STDOUT: %.loc10_40: %.5 = struct_literal ()
// CHECK:STDOUT: %.loc10_23: type = where_expr %.Self [template = constants.%.1] {
// CHECK:STDOUT: requirement_rewrite %Member.ref, %.loc10_40
// CHECK:STDOUT: }
// CHECK:STDOUT: %U.param: %.1 = param U, runtime_param<invalid>
// CHECK:STDOUT: %U.loc10: %.1 = bind_symbolic_name U 0, %U.param [symbolic = %U.1 (constants.%U)]
// CHECK:STDOUT: }
Expand All @@ -143,6 +149,9 @@ class D {
// CHECK:STDOUT: %.Self: type = bind_symbolic_name .Self 0 [symbolic = constants.%.Self.2]
// CHECK:STDOUT: %.Self.ref: type = name_ref .Self, %.Self [symbolic = constants.%.Self.2]
// CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [template = constants.%.1]
// CHECK:STDOUT: %.loc12: type = where_expr %.Self [template = type] {
// CHECK:STDOUT: requirement_impls %.Self.ref, %I.ref
// CHECK:STDOUT: }
// CHECK:STDOUT: %V.param: type = param V, runtime_param<invalid>
// CHECK:STDOUT: %V.loc12: type = bind_symbolic_name V 0, %V.param [symbolic = %V.1 (constants.%V)]
// CHECK:STDOUT: }
Expand Down Expand Up @@ -217,7 +226,10 @@ class D {
// CHECK:STDOUT: %.Self: %.1 = bind_symbolic_name .Self 0 [symbolic = constants.%.Self]
// CHECK:STDOUT: %.Self.ref: %.1 = name_ref .Self, %.Self [symbolic = constants.%.Self]
// CHECK:STDOUT: %Mismatch.ref: <error> = name_ref Mismatch, <error> [template = <error>]
// CHECK:STDOUT: %.loc12: %.5 = struct_literal ()
// CHECK:STDOUT: %.loc12_44: %.5 = struct_literal ()
// CHECK:STDOUT: %.loc12_25: type = where_expr %.Self [template = constants.%.1] {
// CHECK:STDOUT: requirement_rewrite %Mismatch.ref, %.loc12_44
// CHECK:STDOUT: }
// CHECK:STDOUT: %W.param: %.1 = param W, runtime_param<invalid>
// CHECK:STDOUT: %W.loc12: %.1 = bind_symbolic_name W 0, %W.param [symbolic = %W.1 (constants.%W)]
// CHECK:STDOUT: }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ fn NotGenericF(U: I where .T = {}) {}
// CHECK:STDOUT: %.Self: %.1 = bind_symbolic_name .Self 0 [symbolic = constants.%.Self]
// CHECK:STDOUT: %.Self.ref: %.1 = name_ref .Self, %.Self [symbolic = constants.%.Self]
// CHECK:STDOUT: %T.ref: %.2 = name_ref T, @I.%.loc11 [template = constants.%.3]
// CHECK:STDOUT: %.loc14: %.5 = struct_literal ()
// CHECK:STDOUT: %.loc14_33: %.5 = struct_literal ()
// CHECK:STDOUT: %.loc14_21: type = where_expr %.Self [template = constants.%.1] {
// CHECK:STDOUT: requirement_rewrite %T.ref, %.loc14_33
// CHECK:STDOUT: }
// CHECK:STDOUT: %U.param: %.1 = param U, runtime_param0
// CHECK:STDOUT: %U: %.1 = bind_name U, %U.param
// CHECK:STDOUT: }
Expand Down
2 changes: 1 addition & 1 deletion toolchain/lower/file_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ template <typename InstT>
requires(InstT::Kind.template IsAnyOf<
SemIR::AssociatedEntityType, SemIR::FunctionType,
SemIR::GenericClassType, SemIR::GenericInterfaceType,
SemIR::InterfaceType, SemIR::UnboundElementType>())
SemIR::InterfaceType, SemIR::UnboundElementType, SemIR::WhereExpr>())
static auto BuildTypeForInst(FileContext& context, InstT /*inst*/)
-> llvm::Type* {
// Return an empty struct as a placeholder.
Expand Down
Loading

0 comments on commit bdbd107

Please sign in to comment.