diff --git a/toolchain/check/member_access.cpp b/toolchain/check/member_access.cpp index fc1afe6f78ff..ed94ab4eef8b 100644 --- a/toolchain/check/member_access.cpp +++ b/toolchain/check/member_access.cpp @@ -287,8 +287,16 @@ static auto LookupMemberNameInScope(Context& context, SemIR::LocId loc_id, context.types().TryGetAs(type_id)) { if (lookup_in_type_of_base) { SemIR::TypeId base_type_id = context.insts().Get(base_id).type_id(); - if (base_type_id != SemIR::TypeType::SingletonTypeId && - context.types().IsFacetType(base_type_id)) { + if (auto facet_access_type = + context.types().TryGetAs(base_type_id)) { + // Move from the type of a symbolic facet value up in typish-ness to its + // FacetType to find the type to work with. + base_id = facet_access_type->facet_value_inst_id; + base_type_id = context.insts().Get(base_id).type_id(); + } + + if (auto facet_type = + context.types().TryGetAs(base_type_id)) { // Handles `T.F` when `T` is a non-type facet. auto base_as_type = ExprAsType(context, loc_id, base_id); @@ -301,9 +309,8 @@ static auto LookupMemberNameInScope(Context& context, SemIR::LocId loc_id, // First look for `*assoc_interface` in the type of the base. If it is // found, get the witness that the interface is implemented from // `base_id`. - auto facet_type = context.types().GetAs(base_type_id); const auto& facet_type_info = - context.facet_types().Get(facet_type.facet_type_id); + context.facet_types().Get(facet_type->facet_type_id); // Witness that `T` implements the `*assoc_interface`. SemIR::InstId witness_inst_id = SemIR::InstId::None; for (auto base_interface : facet_type_info.impls_constraints) { diff --git a/toolchain/check/name_lookup.cpp b/toolchain/check/name_lookup.cpp index 7482e952ee8a..e84ae392d827 100644 --- a/toolchain/check/name_lookup.cpp +++ b/toolchain/check/name_lookup.cpp @@ -248,6 +248,19 @@ auto AppendLookupScopesForConstant(Context& context, SemIR::LocId loc_id, -> bool { auto base_id = context.constant_values().GetInstId(base_const_id); auto base = context.insts().Get(base_id); + + if (auto base_as_facet_access_type = base.TryAs()) { + // Move from the symbolic facet value up in typish-ness to its FacetType to + // find a lookup scope. + auto facet_type_type_id = + context.insts() + .Get(base_as_facet_access_type->facet_value_inst_id) + .type_id(); + base_const_id = context.types().GetConstantId(facet_type_type_id); + base_id = context.constant_values().GetInstId(base_const_id); + base = context.insts().Get(base_id); + } + if (auto base_as_namespace = base.TryAs()) { scopes->push_back( LookupScope{.name_scope_id = base_as_namespace->name_scope_id, diff --git a/toolchain/check/testdata/facet/no_prelude/access.carbon b/toolchain/check/testdata/facet/no_prelude/access.carbon index de26cc80c652..a3efc091e98d 100644 --- a/toolchain/check/testdata/facet/no_prelude/access.carbon +++ b/toolchain/check/testdata/facet/no_prelude/access.carbon @@ -32,7 +32,7 @@ fn Use(T:! I) -> T { return T.Make(); } -// --- fail_todo_access_assoc_method.carbon +// --- access_assoc_method.carbon library "[[@TEST_NAME]]"; @@ -41,13 +41,21 @@ interface I { } fn Use[T:! I](x: T) -> T { - // CHECK:STDERR: fail_todo_access_assoc_method.carbon:[[@LINE+4]]:10: error: type `T` does not support qualified expressions [QualifiedExprUnsupported] - // CHECK:STDERR: return x.Copy(); - // CHECK:STDERR: ^~~~~~ - // CHECK:STDERR: return x.Copy(); } +// --- access_selfless_method.carbon + +library "[[@TEST_NAME]]"; + +interface I { + fn Hello(); +} + +fn Use[T:! I](x: T){ + x.Hello(); +} + // --- access_assoc_method_indirect.carbon library "[[@TEST_NAME]]"; @@ -270,7 +278,7 @@ fn UseIndirect[T:! I](x: T) -> T { // CHECK:STDOUT: // CHECK:STDOUT: specific @Make(@Use.%I.facet) {} // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_access_assoc_method.carbon +// CHECK:STDOUT: --- access_assoc_method.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] @@ -286,6 +294,10 @@ fn UseIndirect[T:! I](x: T) -> T { // CHECK:STDOUT: %Use.type: type = fn_type @Use [concrete] // CHECK:STDOUT: %Use: %Use.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %T.as_type [symbolic] +// CHECK:STDOUT: %T.as_wit: = facet_access_witness %T [symbolic] +// CHECK:STDOUT: %I.facet: %I.type = facet_value %T.as_type, %T.as_wit [symbolic] +// CHECK:STDOUT: %.4ef: type = fn_type_with_self_type %Copy.type, %I.facet [symbolic] +// CHECK:STDOUT: %impl.elem0: %.4ef = impl_witness_access %T.as_wit, element0 [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -363,11 +375,25 @@ fn UseIndirect[T:! I](x: T) -> T { // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type @Use.%T.as_type.loc8_18.2 (%T.as_type) [symbolic = %require_complete (constants.%require_complete)] +// CHECK:STDOUT: %T.as_wit.loc9_11.2: = facet_access_witness %T.loc8_8.2 [symbolic = %T.as_wit.loc9_11.2 (constants.%T.as_wit)] +// CHECK:STDOUT: %I.facet: %I.type = facet_value %T.as_type.loc8_18.2, %T.as_wit.loc9_11.2 [symbolic = %I.facet (constants.%I.facet)] +// CHECK:STDOUT: %.loc9_11.2: type = fn_type_with_self_type constants.%Copy.type, %I.facet [symbolic = %.loc9_11.2 (constants.%.4ef)] +// CHECK:STDOUT: %impl.elem0.loc9_11.2: @Use.%.loc9_11.2 (%.4ef) = impl_witness_access %T.as_wit.loc9_11.2, element0 [symbolic = %impl.elem0.loc9_11.2 (constants.%impl.elem0)] // CHECK:STDOUT: // CHECK:STDOUT: fn[%T.param_patt: %I.type](%x.param_patt: @Use.%T.as_type.loc8_18.2 (%T.as_type)) -> @Use.%T.as_type.loc8_18.2 (%T.as_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %x.ref: @Use.%T.as_type.loc8_18.2 (%T.as_type) = name_ref x, %x -// CHECK:STDOUT: return +// CHECK:STDOUT: %Copy.ref: %I.assoc_type = name_ref Copy, @I.%assoc0 [concrete = constants.%assoc0] +// CHECK:STDOUT: %T.as_type.loc9: type = facet_access_type constants.%T [symbolic = %T.as_type.loc8_18.2 (constants.%T.as_type)] +// CHECK:STDOUT: %.loc9_11.1: type = converted constants.%T, %T.as_type.loc9 [symbolic = %T.as_type.loc8_18.2 (constants.%T.as_type)] +// CHECK:STDOUT: %T.as_wit.loc9_11.1: = facet_access_witness constants.%T [symbolic = %T.as_wit.loc9_11.2 (constants.%T.as_wit)] +// CHECK:STDOUT: %impl.elem0.loc9_11.1: @Use.%.loc9_11.2 (%.4ef) = impl_witness_access %T.as_wit.loc9_11.1, element0 [symbolic = %impl.elem0.loc9_11.2 (constants.%impl.elem0)] +// CHECK:STDOUT: %bound_method: = bound_method %x.ref, %impl.elem0.loc9_11.1 +// CHECK:STDOUT: %specific_fn: = specific_function %bound_method, @Copy(constants.%I.facet) +// CHECK:STDOUT: %Copy.call: init @Use.%T.as_type.loc8_18.2 (%T.as_type) = call %specific_fn(%x.ref) +// CHECK:STDOUT: %.loc9_18.1: @Use.%T.as_type.loc8_18.2 (%T.as_type) = value_of_initializer %Copy.call +// CHECK:STDOUT: %.loc9_18.2: @Use.%T.as_type.loc8_18.2 (%T.as_type) = converted %Copy.call, %.loc9_18.1 +// CHECK:STDOUT: return %.loc9_18.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: @@ -382,6 +408,113 @@ fn UseIndirect[T:! I](x: T) -> T { // CHECK:STDOUT: %T.as_type.loc8_18.2 => constants.%T.as_type // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: specific @Copy(constants.%I.facet) { +// CHECK:STDOUT: %Self => constants.%I.facet +// CHECK:STDOUT: %Self.as_type.loc5_17.1 => constants.%T.as_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- access_selfless_method.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] +// CHECK:STDOUT: %Self: %I.type = bind_symbolic_name Self, 0 [symbolic] +// CHECK:STDOUT: %Hello.type: type = fn_type @Hello [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %Hello: %Hello.type = struct_value () [concrete] +// CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type %I.type [concrete] +// CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.%Hello.decl [concrete] +// CHECK:STDOUT: %T: %I.type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %T.patt: %I.type = symbolic_binding_pattern T, 0 [symbolic] +// CHECK:STDOUT: %T.as_type: type = facet_access_type %T [symbolic] +// CHECK:STDOUT: %Use.type: type = fn_type @Use [concrete] +// CHECK:STDOUT: %Use: %Use.type = struct_value () [concrete] +// CHECK:STDOUT: %require_complete: = require_complete_type %T.as_type [symbolic] +// CHECK:STDOUT: %T.as_wit: = facet_access_witness %T [symbolic] +// CHECK:STDOUT: %I.facet: %I.type = facet_value %T.as_type, %T.as_wit [symbolic] +// CHECK:STDOUT: %.33b: type = fn_type_with_self_type %Hello.type, %I.facet [symbolic] +// CHECK:STDOUT: %impl.elem0: %.33b = impl_witness_access %T.as_wit, element0 [symbolic] +// CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Hello(%I.facet) [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .I = %I.decl +// CHECK:STDOUT: .Use = %Use.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} +// CHECK:STDOUT: %Use.decl: %Use.type = fn_decl @Use [concrete = constants.%Use] { +// CHECK:STDOUT: %T.patt.loc8_8.1: %I.type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc8_8.2 (constants.%T.patt)] +// CHECK:STDOUT: %T.param_patt: %I.type = value_param_pattern %T.patt.loc8_8.1, runtime_param [symbolic = %T.patt.loc8_8.2 (constants.%T.patt)] +// CHECK:STDOUT: %x.patt: @Use.%T.as_type.loc8_18.2 (%T.as_type) = binding_pattern x +// CHECK:STDOUT: %x.param_patt: @Use.%T.as_type.loc8_18.2 (%T.as_type) = value_param_pattern %x.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %T.param: %I.type = value_param runtime_param +// CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] +// CHECK:STDOUT: %T.loc8_8.1: %I.type = bind_symbolic_name T, 0, %T.param [symbolic = %T.loc8_8.2 (constants.%T)] +// CHECK:STDOUT: %x.param: @Use.%T.as_type.loc8_18.2 (%T.as_type) = value_param runtime_param0 +// CHECK:STDOUT: %.loc8_18.1: type = splice_block %.loc8_18.2 [symbolic = %T.as_type.loc8_18.2 (constants.%T.as_type)] { +// CHECK:STDOUT: %T.ref: %I.type = name_ref T, %T.loc8_8.1 [symbolic = %T.loc8_8.2 (constants.%T)] +// CHECK:STDOUT: %T.as_type.loc8_18.1: type = facet_access_type %T.ref [symbolic = %T.as_type.loc8_18.2 (constants.%T.as_type)] +// CHECK:STDOUT: %.loc8_18.2: type = converted %T.ref, %T.as_type.loc8_18.1 [symbolic = %T.as_type.loc8_18.2 (constants.%T.as_type)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x: @Use.%T.as_type.loc8_18.2 (%T.as_type) = bind_name x, %x.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @I { +// CHECK:STDOUT: %Self: %I.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self] +// CHECK:STDOUT: %Hello.decl: %Hello.type = fn_decl @Hello [concrete = constants.%Hello] {} {} +// CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, %Hello.decl [concrete = constants.%assoc0] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .Hello = %assoc0 +// CHECK:STDOUT: witness = (%Hello.decl) +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @Hello(@I.%Self: %I.type) { +// CHECK:STDOUT: fn(); +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @Use(%T.loc8_8.1: %I.type) { +// CHECK:STDOUT: %T.loc8_8.2: %I.type = bind_symbolic_name T, 0 [symbolic = %T.loc8_8.2 (constants.%T)] +// CHECK:STDOUT: %T.patt.loc8_8.2: %I.type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc8_8.2 (constants.%T.patt)] +// CHECK:STDOUT: %T.as_type.loc8_18.2: type = facet_access_type %T.loc8_8.2 [symbolic = %T.as_type.loc8_18.2 (constants.%T.as_type)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %require_complete: = require_complete_type @Use.%T.as_type.loc8_18.2 (%T.as_type) [symbolic = %require_complete (constants.%require_complete)] +// CHECK:STDOUT: %T.as_wit.loc9_4.2: = facet_access_witness %T.loc8_8.2 [symbolic = %T.as_wit.loc9_4.2 (constants.%T.as_wit)] +// CHECK:STDOUT: %I.facet: %I.type = facet_value %T.as_type.loc8_18.2, %T.as_wit.loc9_4.2 [symbolic = %I.facet (constants.%I.facet)] +// CHECK:STDOUT: %.loc9_4.2: type = fn_type_with_self_type constants.%Hello.type, %I.facet [symbolic = %.loc9_4.2 (constants.%.33b)] +// CHECK:STDOUT: %impl.elem0.loc9_4.2: @Use.%.loc9_4.2 (%.33b) = impl_witness_access %T.as_wit.loc9_4.2, element0 [symbolic = %impl.elem0.loc9_4.2 (constants.%impl.elem0)] +// CHECK:STDOUT: %specific_fn.loc9_4.2: = specific_function %impl.elem0.loc9_4.2, @Hello(%I.facet) [symbolic = %specific_fn.loc9_4.2 (constants.%specific_fn)] +// CHECK:STDOUT: +// CHECK:STDOUT: fn[%T.param_patt: %I.type](%x.param_patt: @Use.%T.as_type.loc8_18.2 (%T.as_type)) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %x.ref: @Use.%T.as_type.loc8_18.2 (%T.as_type) = name_ref x, %x +// CHECK:STDOUT: %Hello.ref: %I.assoc_type = name_ref Hello, @I.%assoc0 [concrete = constants.%assoc0] +// CHECK:STDOUT: %T.as_type.loc9: type = facet_access_type constants.%T [symbolic = %T.as_type.loc8_18.2 (constants.%T.as_type)] +// CHECK:STDOUT: %.loc9_4.1: type = converted constants.%T, %T.as_type.loc9 [symbolic = %T.as_type.loc8_18.2 (constants.%T.as_type)] +// CHECK:STDOUT: %T.as_wit.loc9_4.1: = facet_access_witness constants.%T [symbolic = %T.as_wit.loc9_4.2 (constants.%T.as_wit)] +// CHECK:STDOUT: %impl.elem0.loc9_4.1: @Use.%.loc9_4.2 (%.33b) = impl_witness_access %T.as_wit.loc9_4.1, element0 [symbolic = %impl.elem0.loc9_4.2 (constants.%impl.elem0)] +// CHECK:STDOUT: %specific_fn.loc9_4.1: = specific_function %impl.elem0.loc9_4.1, @Hello(constants.%I.facet) [symbolic = %specific_fn.loc9_4.2 (constants.%specific_fn)] +// CHECK:STDOUT: %Hello.call: init %empty_tuple.type = call %specific_fn.loc9_4.1() +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @Hello(constants.%Self) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @Use(constants.%T) { +// CHECK:STDOUT: %T.loc8_8.2 => constants.%T +// CHECK:STDOUT: %T.patt.loc8_8.2 => constants.%T +// CHECK:STDOUT: %T.as_type.loc8_18.2 => constants.%T.as_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @Hello(constants.%I.facet) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @Hello(@Use.%I.facet) {} +// CHECK:STDOUT: // CHECK:STDOUT: --- access_assoc_method_indirect.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants {