Просмотр исходного кода

Support member access into the type of `(facet as type)` expressions as being into the facet's type (#6190)

When we do member access `x.F` we attempt to look into the type of `x`
for `F`. If `x` is a facet, we look at its FacetType to find `F`. We
want `facet` and `facet as type` expressions to be generally treated as
equivalent, so we need to get at the "canonical facet value" of `x` in
order to look into its type. Add a new operation that allows us to
preserve the non-canonical `base_id` in the member access for
diagnostics if no conversion to a canonical facet value was needed.
Dana Jansens 6 месяцев назад
Родитель
Сommit
3d592ebe59

+ 4 - 0
toolchain/check/member_access.cpp

@@ -509,6 +509,10 @@ static auto PerformActionHelper(Context& context, SemIR::LocId loc_id,
 
   // Otherwise, handle `x.F` by performing lookup into the type of `x` (where
   // `x` is `base_id`).
+  if (auto facet_value = TryGetCanonicalFacetValue(context, base_id);
+      facet_value.has_value()) {
+    base_id = facet_value;
+  }
   auto base_type_id = context.insts().Get(base_id).type_id();
 
   // Require a complete type explicitly. Materializing a temporary will too, but

+ 4 - 8
toolchain/check/testdata/facet/access.carbon

@@ -113,10 +113,10 @@ fn F(U:! I where .I1 = .Self and .I2 = ()) {
   (U as type) as (I where .I1 = .Self);
 }
 
-// --- fail_todo_access_through_call_once.carbon
+// --- access_through_call_once.carbon
 library "[[@TEST_NAME]]";
 
-// TODO: Merge this test with the one below once they both work.
+// TODO: Merge this test with the one below once it works.
 
 interface I {
   let X:! type;
@@ -128,10 +128,6 @@ fn F2[U:! I](V: U*) {}
 fn F(U:! I where .X = .Self, V: U) {
   // The returned value of `G` type `U` which has access to the methods of `I`.
   U.G()->G();
-  // CHECK:STDERR: fail_todo_access_through_call_once.carbon:[[@LINE+4]]:3: error: type `type` does not support qualified expressions [QualifiedExprUnsupported]
-  // CHECK:STDERR:   (U as type).G()->G();
-  // CHECK:STDERR:   ^~~~~~~~~~~~~
-  // CHECK:STDERR:
   (U as type).G()->G();
 
   // The returned value of type `U` can be used as a value of type `U`.
@@ -166,9 +162,9 @@ fn F(U:! I where .X = .Self, V: U*) {
   // CHECK:STDERR:   ^~~~~~~~~~~~~
   // CHECK:STDERR:
   U.G()->G()->G();
-  // CHECK:STDERR: fail_todo_access_through_call.carbon:[[@LINE+4]]:3: error: type `type` does not support qualified expressions [QualifiedExprUnsupported]
+  // CHECK:STDERR: fail_todo_access_through_call.carbon:[[@LINE+4]]:3: error: type `.(I.X)` does not support qualified expressions [QualifiedExprUnsupported]
   // CHECK:STDERR:   (U as type).G()->G()->G();
-  // CHECK:STDERR:   ^~~~~~~~~~~~~
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
   (U as type).G()->G()->G();
 

+ 4 - 4
toolchain/check/testdata/facet/period_self.carbon

@@ -113,9 +113,9 @@ fn F(U:! I(.Self)) {
   // CHECK:STDERR:
   U.G()->G()->G();
   // Conversion to `type` retains access to all of `U`.
-  // CHECK:STDERR: fail_todo_return_of_type_period_self.carbon:[[@LINE+4]]:3: error: type `type` does not support qualified expressions [QualifiedExprUnsupported]
+  // CHECK:STDERR: fail_todo_return_of_type_period_self.carbon:[[@LINE+4]]:3: error: member name `G` not found [MemberNameNotFound]
   // CHECK:STDERR:   (U as type).G()->G()->G();
-  // CHECK:STDERR:   ^~~~~~~~~~~~~
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
   (U as type).G()->G()->G();
 
@@ -149,9 +149,9 @@ fn F(U:! I(.Self) & J(.Self)) {
   // CHECK:STDERR:   ^~~~~~~~~~
   // CHECK:STDERR:
   U.I1()->J1()->I1()->J1()->I1()->J1();
-  // CHECK:STDERR: fail_todo_return_of_type_period_self_extends_interface.carbon:[[@LINE+4]]:3: error: type `type` does not support qualified expressions [QualifiedExprUnsupported]
+  // CHECK:STDERR: fail_todo_return_of_type_period_self_extends_interface.carbon:[[@LINE+4]]:3: error: member name `J1` not found [MemberNameNotFound]
   // CHECK:STDERR:   (U as type).I1()->J1()->I1()->J1()->I1()->J1();
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
   (U as type).I1()->J1()->I1()->J1()->I1()->J1();
 

+ 15 - 0
toolchain/check/testdata/facet/runtime_value.carbon

@@ -110,6 +110,21 @@ interface Z {
 // CHECK:STDERR:
 fn F(T: Z where .X = (), v: T.X);
 
+// --- fail_todo_member_lookup_in_type_of_runtime_facet_with_value.carbon
+library "[[@TEST_NAME]]";
+
+interface Z {
+  fn G();
+}
+
+fn F(T: Z) {
+  // CHECK:STDERR: fail_todo_member_lookup_in_type_of_runtime_facet_with_value.carbon:[[@LINE+4]]:3: error: cannot evaluate type expression [TypeExprEvaluationFailure]
+  // CHECK:STDERR:   T.G();
+  // CHECK:STDERR:   ^~~
+  // CHECK:STDERR:
+  T.G();
+}
+
 // CHECK:STDOUT: --- facet_value_copy_from_reference.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {

+ 9 - 0
toolchain/check/type.cpp

@@ -242,6 +242,7 @@ auto GetUnboundElementType(Context& context, SemIR::TypeInstId class_type_id,
 auto GetCanonicalFacetOrTypeValue(Context& context, SemIR::InstId inst_id)
     -> SemIR::InstId {
   auto const_inst_id = context.constant_values().GetConstantInstId(inst_id);
+  CARBON_DCHECK(const_inst_id.has_value());
 
   if (auto access =
           context.insts().TryGetAs<SemIR::FacetAccessType>(const_inst_id)) {
@@ -263,4 +264,12 @@ auto GetCanonicalFacetOrTypeValue(Context& context, SemIR::ConstantId const_id)
       context, context.constant_values().GetInstId(const_id)));
 }
 
+auto TryGetCanonicalFacetValue(Context& context, SemIR::InstId inst_id)
+    -> SemIR::InstId {
+  if (context.insts().Get(inst_id).type_id() == SemIR::TypeType::TypeId) {
+    return GetCanonicalFacetOrTypeValue(context, inst_id);
+  }
+  return SemIR::InstId::None;
+}
+
 }  // namespace Carbon::Check

+ 9 - 0
toolchain/check/type.h

@@ -129,6 +129,15 @@ auto GetCanonicalFacetOrTypeValue(Context& context, SemIR::InstId inst_id)
 auto GetCanonicalFacetOrTypeValue(Context& context, SemIR::ConstantId const_id)
     -> SemIR::ConstantId;
 
+// If `inst_id` is a type value which wraps a facet value, return that canonical
+// facet value. Otherwise, return None.
+//
+// In particular, this returns None for non-canonical instructions if no
+// transformation was needed to return a facet value, to preserve source
+// locations in the caller.
+auto TryGetCanonicalFacetValue(Context& context, SemIR::InstId inst_id)
+    -> SemIR::InstId;
+
 }  // namespace Carbon::Check
 
 #endif  // CARBON_TOOLCHAIN_CHECK_TYPE_H_