From 84a00604470df733f10cefb92c36fcb8034ec616 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Thu, 17 Apr 2025 16:56:36 -0400 Subject: [PATCH] Allow a struct/tuple type literal to implicitly convert into a facet value (#5325) We already had conversion in place to implicitly convert these literals to `type`. Now they can also convert to `FacetType`. This is done by first doing a conversion to `type` and then converting that type value to `FacetType`. --- toolchain/check/convert.cpp | 41 ++++--- .../tuple_and_struct_type_literal.carbon | 100 ++++++++++++++++++ .../testdata/impl/assoc_const_self.carbon | 10 +- 3 files changed, 130 insertions(+), 21 deletions(-) create mode 100644 toolchain/check/testdata/facet/min_prelude/tuple_and_struct_type_literal.carbon diff --git a/toolchain/check/convert.cpp b/toolchain/check/convert.cpp index 8e5b355b565c..d2bd3ace7639 100644 --- a/toolchain/check/convert.cpp +++ b/toolchain/check/convert.cpp @@ -994,7 +994,10 @@ static auto PerformBuiltinConversion( } } - if (target.type_id == SemIR::TypeType::TypeId) { + if (target.type_id == SemIR::TypeType::TypeId || + sem_ir.types().Is(target.type_id)) { + auto type_value_id = SemIR::InstId::None; + // A tuple of types converts to type `type`. // TODO: This should apply even for non-literal tuples. if (auto tuple_literal = value.TryAs()) { @@ -1010,7 +1013,7 @@ static auto PerformBuiltinConversion( // TODO: Should we add this as an instruction? It will contain references // to local InstIds. auto tuple_type_id = GetTupleType(context, type_inst_ids); - return sem_ir.types().GetInstId(tuple_type_id); + type_value_id = sem_ir.types().GetInstId(tuple_type_id); } // `{}` converts to `{} as type`. @@ -1019,23 +1022,33 @@ static auto PerformBuiltinConversion( if (auto struct_literal = value.TryAs(); struct_literal && struct_literal->elements_id == SemIR::InstBlockId::Empty) { - value_id = sem_ir.types().GetInstId(value_type_id); + type_value_id = sem_ir.types().GetInstId(value_type_id); } - // Facet type conversions: a value T of facet type F1 can be implicitly - // converted to facet type F2 if T satisfies the requirements of F2. - // - // TODO: Support this conversion in general. For now we only support it in - // the case where F1 is a facet type and F2 is `type`. - // TODO: Support converting tuple and struct values to facet types, - // combining the above conversions and this one in a single conversion. - if (sem_ir.types().Is(value_type_id)) { - return AddInst( - context, loc_id, - {.type_id = target.type_id, .facet_value_inst_id = value_id}); + if (type_value_id != SemIR::InstId::None) { + if (sem_ir.types().Is(target.type_id)) { + // Use the converted `TypeType` value for converting to a facet. + value_id = type_value_id; + value_type_id = SemIR::TypeType::TypeId; + } else { + // We wanted a `TypeType`, and we've done that. + return type_value_id; + } } } + // FacetType converts to Type by wrapping the facet value in + // FacetAccessType. + if (target.type_id == SemIR::TypeType::TypeId && + sem_ir.types().Is(value_type_id)) { + return AddInst( + context, loc_id, + {.type_id = target.type_id, .facet_value_inst_id = value_id}); + } + + // Type values can convert to facet values, and facet values can convert to + // other facet values, as long as they satisfy the required interfaces of the + // target `FacetType`. if (target.type_id != value_type_id && sem_ir.types().Is(target.type_id) && (sem_ir.types().Is(value_type_id) || diff --git a/toolchain/check/testdata/facet/min_prelude/tuple_and_struct_type_literal.carbon b/toolchain/check/testdata/facet/min_prelude/tuple_and_struct_type_literal.carbon new file mode 100644 index 000000000000..a92b2b88e028 --- /dev/null +++ b/toolchain/check/testdata/facet/min_prelude/tuple_and_struct_type_literal.carbon @@ -0,0 +1,100 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// INCLUDE-FILE: toolchain/testing/min_prelude/convert.carbon +// EXTRA-ARGS: --custom-core --no-dump-sem-ir +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/min_prelude/tuple_and_struct_type_literal.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/min_prelude/tuple_and_struct_type_literal.carbon + +// --- tuple_literal_to_facet_value.carbon +library "[[@TEST_NAME]]"; + +interface Y {} +interface Z { + let X:! Y; +} + +impl () as Y {} + +// The tuple literal converts implicitly to a type, and then to a facet value +// satisfying `Y`. +impl forall [T:! type] T as Z where .X = () {} + +// --- complex_tuple_literal_to_facet_value.carbon +library "[[@TEST_NAME]]"; + +interface Y {} +interface Z { + let X:! Y; +} + +impl ((), {}) as Y {} + +// The tuple literal converts implicitly to a type, and then to a facet value +// satisfying `Y`. +impl forall [T:! type] T as Z where .X = ((), {}) {} + +// --- fail_mismatch_tuple_literal_to_facet_value.carbon +library "[[@TEST_NAME]]"; + +interface Y {} +interface Z { + let X:! Y; +} + +impl ((), {}) as Y {} + +// CHECK:STDERR: fail_mismatch_tuple_literal_to_facet_value.carbon:[[@LINE+4]]:42: error: cannot convert type `({}, {})` into type implementing `Y` [ConversionFailureTypeToFacet] +// CHECK:STDERR: impl forall [T:! type] T as Z where .X = ({}, {}) {} +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: +impl forall [T:! type] T as Z where .X = ({}, {}) {} + +// --- struct_literal_to_facet_value.carbon +library "[[@TEST_NAME]]"; + +interface Y {} +interface Z { + let X:! Y; +} + +impl {} as Y {} + +// The struct literal converts implicitly to a type, and then to a facet value +// satisfying `Y`. +impl forall [T:! type] T as Z where .X = {} {} + +// --- complex_struct_literal_to_facet_value.carbon +library "[[@TEST_NAME]]"; + +interface Y {} +interface Z { + let X:! Y; +} + +impl {.a: (), .b: {}} as Y {} + +// The struct literal converts implicitly to a type, and then to a facet value +// satisfying `Y`. +impl forall [T:! type] T as Z where .X = {.a: (), .b: {}} {} + +// --- fail_mismatch_struct_literal_to_facet_value.carbon +library "[[@TEST_NAME]]"; + +interface Y {} +interface Z { + let X:! Y; +} + +impl {.a: (), .b: {}} as Y {} + +// CHECK:STDERR: fail_mismatch_struct_literal_to_facet_value.carbon:[[@LINE+4]]:42: error: cannot convert type `{.a: {}, .b: {}}` into type implementing `Y` [ConversionFailureTypeToFacet] +// CHECK:STDERR: impl forall [T:! type] T as Z where .X = {.a: {}, .b: {}} {} +// CHECK:STDERR: ^~~~~~~~~~~~~~~~ +// CHECK:STDERR: +impl forall [T:! type] T as Z where .X = {.a: {}, .b: {}} {} diff --git a/toolchain/check/testdata/impl/assoc_const_self.carbon b/toolchain/check/testdata/impl/assoc_const_self.carbon index 497cd5bd4985..cd0682b94800 100644 --- a/toolchain/check/testdata/impl/assoc_const_self.carbon +++ b/toolchain/check/testdata/impl/assoc_const_self.carbon @@ -86,13 +86,10 @@ fn F(T:! I where {} impls Core.ImplicitAs(.Self) and .V = {}); fn CallF() { // TODO: This call should eventually work. - // CHECK:STDERR: fail_todo_constrained_fn.carbon:[[@LINE+10]]:3: error: cannot implicitly convert non-type value of type `{}` into type implementing `I where .(I.V) = {} and...` [ConversionFailureNonTypeToFacet] + // CHECK:STDERR: fail_todo_constrained_fn.carbon:[[@LINE+7]]:3: error: cannot convert type `{}` into type implementing `I where .(I.V) = {} and...` [ConversionFailureTypeToFacet] // CHECK:STDERR: F({}); // CHECK:STDERR: ^~~~~ - // CHECK:STDERR: fail_todo_constrained_fn.carbon:[[@LINE+7]]:3: note: type `{}` does not implement interface `Core.ImplicitAs(I where .(I.V) = {} and...)` [MissingImplInMemberAccessNote] - // CHECK:STDERR: F({}); - // CHECK:STDERR: ^~~~~ - // CHECK:STDERR: fail_todo_constrained_fn.carbon:[[@LINE-10]]:6: note: initializing generic parameter `T` declared here [InitializingGenericParam] + // CHECK:STDERR: fail_todo_constrained_fn.carbon:[[@LINE-7]]:6: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(T:! I where {} impls Core.ImplicitAs(.Self) and .V = {}); // CHECK:STDERR: ^ // CHECK:STDERR: @@ -822,8 +819,7 @@ fn CallF() { // CHECK:STDOUT: fn @CallF() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] -// CHECK:STDOUT: %.loc22_6: %empty_struct_type = struct_literal () -// CHECK:STDOUT: %.loc22_7: %I_where.type = converted %.loc22_6, [concrete = ] +// CHECK:STDOUT: %.loc19: %empty_struct_type = struct_literal () // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: