2020
2121namespace Carbon ::Check {
2222
23+ // Returns true if the given instruction can only be a template argument, and
24+ // not a function argument. We classify arguments as definitely being template
25+ // arguments if they are types or the name of a template or generic.
26+ // TODO: We should also have a way to specify that an argument is a non-type
27+ // template argument.
28+ static auto IsTemplateArg (Context& context, SemIR::InstId arg_id) -> bool {
29+ auto arg_type_id = context.insts ().Get (arg_id).type_id ();
30+ auto arg_type = context.types ().GetAsInst (arg_type_id);
31+ return arg_type
32+ .IsOneOf <SemIR::TypeType, SemIR::FacetType, SemIR::CppTemplateNameType,
33+ SemIR::GenericClassType, SemIR::GenericInterfaceType,
34+ SemIR::GenericNamedConstraintType>();
35+ }
36+
37+ // Splits a call argument list into a list of template arguments followed by a
38+ // list of function arguments. We split the argument list as early as possible,
39+ // subject to the constraint that if an argument is a template argument, it goes
40+ // in the template argument list.
41+ static auto SplitCallArgumentList (Context& context,
42+ llvm::ArrayRef<SemIR::InstId> arg_ids)
43+ -> std::pair<llvm::ArrayRef<SemIR::InstId>, llvm::ArrayRef<SemIR::InstId>> {
44+ for (auto [n, arg_id] : llvm::enumerate (llvm::reverse (arg_ids))) {
45+ if (IsTemplateArg (context, arg_id)) {
46+ return {arg_ids.drop_back (n), arg_ids.take_back (n)};
47+ }
48+ }
49+ // No template arguments found.
50+ return {{}, arg_ids};
51+ }
52+
2353auto PerformCallToCppFunction (Context& context, SemIR::LocId loc_id,
2454 SemIR::CppOverloadSetId overload_set_id,
2555 SemIR::InstId self_id,
2656 llvm::ArrayRef<SemIR::InstId> arg_ids,
2757 bool is_operator_syntax) -> SemIR::InstId {
28- SemIR::InstId callee_id = PerformCppOverloadResolution (
29- context, loc_id, overload_set_id, self_id, arg_ids);
58+ auto [template_arg_ids, function_arg_ids] =
59+ SplitCallArgumentList (context, arg_ids);
60+ auto callee_id =
61+ PerformCppOverloadResolution (context, loc_id, overload_set_id,
62+ template_arg_ids, self_id, function_arg_ids);
3063 SemIR::Callee callee = GetCallee (context.sem_ir (), callee_id);
3164 CARBON_KIND_SWITCH (callee) {
3265 case CARBON_KIND (SemIR::CalleeError _): {
@@ -38,8 +71,8 @@ auto PerformCallToCppFunction(Context& context, SemIR::LocId loc_id,
3871 // Preserve the `self` argument from the original callee.
3972 fn.self_id = self_id;
4073 }
41- return PerformCallToFunction (context, loc_id, callee_id, fn, arg_ids,
42- is_operator_syntax);
74+ return PerformCallToFunction (context, loc_id, callee_id, fn,
75+ function_arg_ids, is_operator_syntax);
4376 }
4477 case CARBON_KIND (SemIR::CalleeCppOverloadSet _): {
4578 CARBON_FATAL (" overloads can't be recursive" );
@@ -79,16 +112,18 @@ static auto MakePlaceholderTemplateArg(Context& context, SemIR::InstId arg_id)
79112static auto ConvertArgToTemplateArg (
80113 Context& context, clang::TemplateDecl* template_decl,
81114 clang::NamedDecl* param_decl, SemIR::InstId arg_id,
82- clang::SmallVector<clang::TemplateArgument>* template_args)
115+ clang::SmallVector<clang::TemplateArgument>* template_args, bool diagnose )
83116 -> std::optional<clang::TemplateArgumentLoc> {
84117 if (isa<clang::TemplateTypeParmDecl>(param_decl)) {
85- auto type = ExprAsType (context, SemIR::LocId (arg_id), arg_id);
118+ auto type = ExprAsType (context, SemIR::LocId (arg_id), arg_id, diagnose );
86119 if (type.type_id == SemIR::ErrorInst::TypeId) {
87120 return std::nullopt ;
88121 }
89122 auto clang_type = MapToCppType (context, type.type_id );
90123 if (clang_type.isNull ()) {
91- context.TODO (arg_id, " unsupported type used as template argument" );
124+ if (diagnose) {
125+ context.TODO (arg_id, " unsupported type used as template argument" );
126+ }
92127 return std::nullopt ;
93128 }
94129 return clang::TemplateArgumentLoc (
@@ -127,6 +162,13 @@ static auto ConvertArgToTemplateArg(
127162 // When evaluating the second template argument, the generic type of
128163 // `T` should be substituted with `i32`.
129164 if (param_type->isInstantiationDependentType ()) {
165+ // If we don't want to diagnose errors, create a SFINAE context so that
166+ // Clang knows to suppress error messages.
167+ std::optional<clang::Sema::SFINAETrap> sfinae;
168+ if (!diagnose) {
169+ sfinae.emplace (context.clang_sema ());
170+ }
171+
130172 clang::Sema::InstantiatingTemplate inst (
131173 context.clang_sema (), clang::SourceLocation (), param_decl, non_type,
132174 *template_args, clang::SourceRange ());
@@ -149,7 +191,7 @@ static auto ConvertArgToTemplateArg(
149191 param_type = context.clang_sema ().CheckNonTypeTemplateParameterType (
150192 param_type, non_type->getLocation ());
151193 }
152- if (param_type.isNull ()) {
194+ if (param_type.isNull () || (sfinae && sfinae-> hasErrorOccurred ()) ) {
153195 return std::nullopt ;
154196 }
155197 }
@@ -164,6 +206,7 @@ static auto ConvertArgToTemplateArg(
164206 {
165207 .kind = ConversionTarget::Value,
166208 .type_id = type_expr.type_id ,
209+ .diagnose = diagnose,
167210 });
168211
169212 if (converted_inst_id == SemIR::ErrorInst::InstId) {
@@ -221,21 +264,21 @@ static auto ConvertArgToTemplateArg(
221264 }
222265
223266 // TODO: Support other types.
224- context.TODO (arg_id,
225- " unsupported argument type for non-type template parameter" );
267+ if (diagnose) {
268+ context.TODO (arg_id,
269+ " unsupported argument type for non-type template parameter" );
270+ }
226271 return std::nullopt ;
227272 }
228273
229274 CARBON_FATAL (" Unknown declaration kind for template parameter" );
230275}
231276
232- // Converts a call argument list into a Clang template argument list for a given
233- // template. Returns true on success, or false if an error was diagnosed.
234- static auto ConvertArgsToTemplateArgs (Context& context,
235- clang::TemplateDecl* template_decl,
236- llvm::ArrayRef<SemIR::InstId> arg_ids,
237- clang::TemplateArgumentListInfo& arg_list)
238- -> bool {
277+ auto ConvertArgsToTemplateArgs (Context& context,
278+ clang::TemplateDecl* template_decl,
279+ llvm::ArrayRef<SemIR::InstId> arg_ids,
280+ clang::TemplateArgumentListInfo& arg_list,
281+ bool diagnose) -> bool {
239282 clang::SmallVector<clang::TemplateArgument> template_args;
240283 for (auto * param_decl : template_decl->getTemplateParameters ()->asArray ()) {
241284 if (arg_ids.empty ()) {
@@ -250,8 +293,9 @@ static auto ConvertArgsToTemplateArgs(Context& context,
250293 param_decl->isTemplateParameterPack () ? std::exchange (arg_ids, {})
251294 : arg_ids.consume_front ();
252295 for (auto arg_id : args_for_param) {
253- if (auto arg = ConvertArgToTemplateArg (context, template_decl, param_decl,
254- arg_id, &template_args)) {
296+ if (auto arg =
297+ ConvertArgToTemplateArg (context, template_decl, param_decl,
298+ arg_id, &template_args, diagnose)) {
255299 arg_list.addArgument (*arg);
256300 template_args.push_back (arg->getArgument ());
257301 } else {
0 commit comments