diff --git a/toolchain/check/cpp/export.cpp b/toolchain/check/cpp/export.cpp index 7b22bd44ef8a..67d0325d4e38 100644 --- a/toolchain/check/cpp/export.cpp +++ b/toolchain/check/cpp/export.cpp @@ -15,7 +15,9 @@ #include "toolchain/check/cpp/import.h" #include "toolchain/check/cpp/location.h" #include "toolchain/check/cpp/type_mapping.h" +#include "toolchain/check/facet_type.h" #include "toolchain/check/function.h" +#include "toolchain/check/generic.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/pattern.h" @@ -288,10 +290,14 @@ namespace { struct FunctionInfo { struct Param { Param(Context& context, SemIR::InstId param_inst_id) - : type_id(ExtractScrutineeType( + : pattern_inst_id(param_inst_id), + type_id(ExtractScrutineeType( context.sem_ir(), context.insts().Get(param_inst_id).type_id())), kind(GetParamPatternKind(context, param_inst_id)) {} + // The parameter's pattern type. + SemIR::InstId pattern_inst_id; + // Type of the parameter's scrutinee. SemIR::TypeId type_id; @@ -810,9 +816,11 @@ auto ExportFunctionSpecializationToCpp( // between specializations. std::string extra_name; - // Create a mapping from Carbon generic parameters to the - // corresponding C++ type in `template_args`. - Map symbolic_to_actual; + // Map the `clang::TemplateArgument`s into Carbon types suitable for + // passing into `MakeSpecific`. + // + // Also initialize `extra_name`. + llvm::SmallVector specific_arg_ids; for (auto [binding_inst_id, clang_template_arg] : llvm::zip(bindings, template_args)) { auto type_expr = @@ -825,37 +833,24 @@ auto ExportFunctionSpecializationToCpp( return false; } - auto binding_const_inst_id = - context.constant_values().GetConstantInstId(binding_inst_id); - symbolic_to_actual.Insert(binding_const_inst_id, type_expr.type_id); - // TODO: this generates a pretty ugly name. extra_name += std::string(llvm::formatv("{}", type_expr.inst_id)); + + auto binding_const_inst_id = + context.constant_values().GetConstantInstId(binding_inst_id); + + specific_arg_ids.push_back(ConvertToValueOfType( + context, loc_id, type_expr.inst_id, + context.insts().Get(binding_const_inst_id).type_id())); } - // Replace symbolic explicit parameters with a concrete Carbon type. - // - // This will only handle simple cases like `x: T`, and not things like - // `x: T*`. Ultimately what we should be doing here is producing a Specific - // for the generic function. See `fail_todo_generic_pointer.carbon`. + // Create a specific, and use that to convert from parameters with + // symbolic types to concrete types. + auto specific_id = MakeSpecific(context, loc_id, target.function.generic_id, + specific_arg_ids); for (auto& param : target.explicit_params) { - auto param_type_inst_id = context.types().GetTypeInstId(param.type_id); - SemIR::InstId symbolic_inst_id = SemIR::InstId::None; - if (auto symbolic_binding = - context.insts().TryGetAs( - param_type_inst_id)) { - symbolic_inst_id = param_type_inst_id; - } else if (auto facet_access_type = - context.insts().TryGetAs( - param_type_inst_id)) { - symbolic_inst_id = facet_access_type->facet_value_inst_id; - } - - if (symbolic_inst_id.has_value()) { - if (auto lookup = symbolic_to_actual.Lookup(symbolic_inst_id)) { - param.type_id = lookup.value(); - } - } + param.type_id = + GetScrutineeTypeInSpecific(context, param.pattern_inst_id, specific_id); } // TODO: handle generic return type. diff --git a/toolchain/check/generic.cpp b/toolchain/check/generic.cpp index 52a408a02052..527477e08cc7 100644 --- a/toolchain/check/generic.cpp +++ b/toolchain/check/generic.cpp @@ -912,4 +912,13 @@ auto DiagnoseImplsOnNonFacetType(Context& context, SemIR::LocId loc_id) context.emitter().Emit(loc_id, ImplsOnNonFacetType); } +auto GetScrutineeTypeInSpecific(const Context& context, + SemIR::InstId pattern_id, + SemIR::SpecificId specific_id) + -> SemIR::TypeId { + const auto& sem_ir = context.sem_ir(); + return ExtractScrutineeType( + sem_ir, SemIR::GetTypeOfInstInSpecific(sem_ir, specific_id, pattern_id)); +} + } // namespace Carbon::Check diff --git a/toolchain/check/generic.h b/toolchain/check/generic.h index 48074a13c18e..623e8a5fe866 100644 --- a/toolchain/check/generic.h +++ b/toolchain/check/generic.h @@ -170,6 +170,14 @@ auto CopySpecificToGeneric(Context& context, SemIR::LocId loc_id, auto DiagnoseImplsOnNonFacetType(Context& context, SemIR::LocId loc_id) -> void; +// Returns the substituted scrutinee type of `pattern_id` in `specific_id`. As +// with `GetTypeOfInstInSpecific`, this does not perform substitution, and it +// accepts `SpecificId::None`, treating it as a request for the value to use +// within the generic itself. +auto GetScrutineeTypeInSpecific(const Context& context, + SemIR::InstId pattern_id, + SemIR::SpecificId specific_id) -> SemIR::TypeId; + } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_GENERIC_H_ diff --git a/toolchain/check/pattern_match.cpp b/toolchain/check/pattern_match.cpp index 6203e855b511..79ca2941d667 100644 --- a/toolchain/check/pattern_match.cpp +++ b/toolchain/check/pattern_match.cpp @@ -17,6 +17,7 @@ #include "toolchain/check/control_flow.h" #include "toolchain/check/convert.h" #include "toolchain/check/eval.h" +#include "toolchain/check/generic.h" #include "toolchain/check/pattern.h" #include "toolchain/check/type.h" #include "toolchain/diagnostics/format_providers.h" @@ -389,19 +390,6 @@ auto MatchContext::DoPreWork(State state, } } -// Returns the substituted scrutinee type of `pattern_id` in `specific_id`. As -// with `GetTypeOfInstInSpecific`, this does not perform substitution, and it -// accepts `SpecificId::None`, treating it as a request for the value to use -// within the generic itself. -static auto GetScrutineeTypeInSpecific(const Context& context, - SemIR::InstId pattern_id, - SemIR::SpecificId specific_id) - -> SemIR::TypeId { - const auto& sem_ir = context.sem_ir(); - return ExtractScrutineeType( - sem_ir, SemIR::GetTypeOfInstInSpecific(sem_ir, specific_id, pattern_id)); -} - auto MatchContext::DoPostWork(State state, SemIR::AnyBindingPattern binding_pattern, WorkItem entry) -> void { diff --git a/toolchain/check/testdata/interop/cpp/function/export/generic.carbon b/toolchain/check/testdata/interop/cpp/function/export/generic.carbon index a70849245fc4..a693d5b5cfcb 100644 --- a/toolchain/check/testdata/interop/cpp/function/export/generic.carbon +++ b/toolchain/check/testdata/interop/cpp/function/export/generic.carbon @@ -68,7 +68,7 @@ void G() { } '''; -// --- fail_todo_generic_pointer.carbon +// --- generic_pointer.carbon library "[[@TEST_NAME]]"; import Cpp; @@ -78,13 +78,6 @@ inline Cpp ''' void G() { int x = 0; int* _Nonnull p = &x; - // CHECK:STDERR: fail_todo_generic_pointer.carbon:[[@LINE+7]]:3: error: no matching function for call to 'F' [CppInteropParseError] - // CHECK:STDERR: 17 | Carbon::F(p); - // CHECK:STDERR: | ^~~~~~~~~ - // CHECK:STDERR: fail_todo_generic_pointer.carbon:[[@LINE-9]]:29: note: candidate template ignored: deduced type 'T * _Nonnull' of 1st parameter does not match adjusted type 'int * _Nonnull' of argument [with T = int] [CppInteropParseNote] - // CHECK:STDERR: 4 | fn F[T: type](unused t: T*) {} - // CHECK:STDERR: | ^ - // CHECK:STDERR: Carbon::F(p); } '''; @@ -248,6 +241,10 @@ void G() { // CHECK:STDOUT: %t: @F.%T.as_type.loc20_15.1 (%T.as_type) = wrapper_binding t, %t.param // CHECK:STDOUT: } // CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %I.facet.loc20_18.1: %I.type = facet_value %A.decl, (constants.%I.impl_witness.6ad) [concrete = constants.%I.facet.64b] +// CHECK:STDOUT: %.loc20_18.1: %I.type = converted %A.decl, %I.facet.loc20_18.1 [concrete = constants.%I.facet.64b] +// CHECK:STDOUT: %I.facet.loc20_18.2: %I.type = facet_value %B.decl, (constants.%I.impl_witness.5b8) [concrete = constants.%I.facet.9f8] +// CHECK:STDOUT: %.loc20_18.2: %I.type = converted %B.decl, %I.facet.loc20_18.2 [concrete = constants.%I.facet.9f8] // CHECK:STDOUT: inline_cpp "void G() {\n Carbon::A a;\n Carbon::B b;\n Carbon::F(a);\n Carbon::F(b);\n}\n" // CHECK:STDOUT: } // CHECK:STDOUT: @@ -514,6 +511,15 @@ void G() { // CHECK:STDOUT: %self.patt.loc6_11.2 => constants.%self.patt.300 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: specific @F(file.%.loc20_18.1) { +// CHECK:STDOUT: %T.patt.loc20_7.2 => constants.%T.patt +// CHECK:STDOUT: %T.loc20_7.1 => constants.%I.facet.64b +// CHECK:STDOUT: %T.as_type.loc20_15.1 => constants.%A +// CHECK:STDOUT: %pattern_type => constants.%pattern_type.9ef +// CHECK:STDOUT: %t.param_patt.loc20_13.2 => constants.%t.param_patt.169 +// CHECK:STDOUT: %t.patt.loc20_13.2 => constants.%t.patt.ee8 +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%I.facet.64b) { // CHECK:STDOUT: %T.patt.loc20_7.2 => constants.%T.patt // CHECK:STDOUT: %T.loc20_7.1 => constants.%I.facet.64b @@ -531,6 +537,15 @@ void G() { // CHECK:STDOUT: %specific_impl_fn.loc21_4.2 => constants.%A.as.I.impl.Doit // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: specific @F(file.%.loc20_18.2) { +// CHECK:STDOUT: %T.patt.loc20_7.2 => constants.%T.patt +// CHECK:STDOUT: %T.loc20_7.1 => constants.%I.facet.9f8 +// CHECK:STDOUT: %T.as_type.loc20_15.1 => constants.%B +// CHECK:STDOUT: %pattern_type => constants.%pattern_type.e39 +// CHECK:STDOUT: %t.param_patt.loc20_13.2 => constants.%t.param_patt.267 +// CHECK:STDOUT: %t.patt.loc20_13.2 => constants.%t.patt.062 +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%I.facet.9f8) { // CHECK:STDOUT: %T.patt.loc20_7.2 => constants.%T.patt // CHECK:STDOUT: %T.loc20_7.1 => constants.%I.facet.9f8