diff --git a/src/Changes b/src/Changes index 559372f7b0..f4875e6e70 100644 --- a/src/Changes +++ b/src/Changes @@ -1,5 +1,35 @@ Version 7.1 +10/16/26 [GH #338] +Lambdas that name template parameters in function template declarations + +A lambda in the declaration of a function template that names a template +parameter (in the type of a parameter, in a default template argument, in the +condition of Clang's enable_if attribute, ...) is rescanned when the template +arguments are substituted for the template parameters. The template +parameters of most templates were not visible to that rescan, so the names +were reported as undefined and the template could not be used: + + template + void f(char (*)[sizeof([](T y) { return y; }(T{}))]) {} + void g() { char a[4]; f(&a); } // Was an error: identifier "T" is + // undefined + +The template parameters are now bound to the template arguments for the +rescan of the lambda, and are put back afterwards. The same applies to the +condition of Clang's enable_if attribute on a function template, which is now +rescanned in the instantiation scope of the template. + +This is not done yet for a generic lambda, a lambda with its own template +parameter list, or a lambda that names a template parameter pack. The names +of the template parameters are still reported as undefined in those: + + template + void f(char (*)[sizeof([](auto) { return T(); }(1))]) {} + void g() { char a[4]; f(&a); } // Still an error: identifier "T" is + // undefined + + 10/21/26 [GH #335] Type transformations of qualified function types diff --git a/src/il.c b/src/il.c index 662be45c4e..919316a585 100644 --- a/src/il.c +++ b/src/il.c @@ -20500,6 +20500,223 @@ TRUE. } /* substituted_cast_is_valid */ +static a_boolean lambda_tokens_name_template_param( + a_token_cache_ptr tokens, + a_template_param_ptr tpp) +/* +Return TRUE if one of the identifier tokens cached for a lambda (see +scan_lambda in class_decl.c) names the template parameter tpp. The tokens +keep the spelling of a name and not the entity that it was looked up as, so +an identifier that follows "::", "." or "->" (with "template" between them or +not) is a member or a qualified name (::T, NS::T, x.T, p->T, +G::template T), which is never the template parameter, and is skipped. +*/ +{ + a_boolean found = FALSE; + a_const_token_cache_iterator it; + a_const_token_cache_iterator end = tokens->end(); + a_token_kind prev_kind = tok_error; + a_token_kind prev2_kind = tok_error; + + if (tpp->param_symbol != NULL && tpp->param_symbol->header != NULL) { + for (it = tokens->begin(); it != end && !found; ++it) { + const a_shared_token &tok = *it; + /* The name of a member or of a qualified name follows the qualifier, + possibly with the keyword "template" between them. */ + a_token_kind qualifier_kind = prev_kind == tok_template ? + prev2_kind : prev_kind; + if (tok->is_identifier() && + qualifier_kind != tok_colon_colon && qualifier_kind != tok_period && + qualifier_kind != tok_arrow && + tok->get_symbol_locator()->symbol_header == + tpp->param_symbol->header) { + found = TRUE; + } /* if */ + prev2_kind = prev_kind; + prev_kind = tok->get_kind(); + } /* for */ + } /* if */ + return found; +} /* lambda_tokens_name_template_param */ + + +static a_boolean instantiation_scope_is_active_for_param_list( + a_template_param_ptr template_param_list) +/* +Return TRUE if an instantiation scope for the template whose parameters are +template_param_list is on the scope stack (as for the rescans of a function +template declaration done in template argument deduction and in the +substitution into attributes), so that the names of the template parameters +are found by name lookup. The scope is recognized by its parameter list, so +that the rescan of another template cannot be taken for it. +*/ +{ + a_scope_depth scope_depth; + a_boolean found = FALSE; + + for (scope_depth = depth_scope_stack; scope_depth >= 0; scope_depth--) { + if (scope_is(&scope_stack[scope_depth], sck_template_instantiation) && + scope_stack[scope_depth].template_decl_info != NULL && + scope_stack[scope_depth].template_decl_info->parameters == + template_param_list) { + found = TRUE; + break; + } /* if */ + } /* for */ + return found; +} /* instantiation_scope_is_active_for_param_list */ + + +typedef struct a_saved_template_param_symbol + *a_saved_template_param_symbol_ptr; +typedef struct a_saved_template_param_symbol { + /* The state of a template parameter symbol that + update_template_param_symbols changes. */ + a_symbol_ptr param_symbol; + a_boolean not_visible; + /* When param_symbol->kind == sk_type: */ + a_type_ptr type; + /* When param_symbol->kind == sk_constant: */ + a_constant_ptr + constant; + /* When param_symbol->kind == sk_class_template: */ + a_symbol_ptr argument_template; + a_template_ptr + substituted_param_template; +} a_saved_template_param_symbol; + + +static a_saved_template_param_symbol_ptr save_template_param_symbols( + a_template_param_ptr template_param_list, + size_t *num_symbols) +/* +Save the state of the symbols of the parameters in template_param_list, so +that restore_template_param_symbols can put them back exactly as they were. +Return the saved state (an array that has *num_symbols entries) and release +it with restore_template_param_symbols. +*/ +{ + a_template_param_ptr tpp; + a_saved_template_param_symbol_ptr saved; + size_t i = 0; + + *num_symbols = 0; + for (tpp = template_param_list; tpp != NULL; tpp = tpp->next) { + (*num_symbols)++; + } /* for */ + saved = (a_saved_template_param_symbol_ptr)alloc_general( + *num_symbols * sizeof(a_saved_template_param_symbol)); + for (tpp = template_param_list; tpp != NULL; tpp = tpp->next, i++) { + a_symbol_ptr sym = tpp->param_symbol; + a_saved_template_param_symbol_ptr sp = &saved[i]; + memset((void*)sp, 0, sizeof(a_saved_template_param_symbol)); + sp->param_symbol = sym; + sp->not_visible = sym->template_param_not_visible; + if (sym->kind == (a_symbol_kind)sk_type) { + sp->type = sym->variant.type.ptr; + } else if (sym->kind == (a_symbol_kind)sk_constant) { + sp->constant = sym->variant.constant; + } else { + a_template_symbol_supplement_ptr tssp = sym->variant.template_info; + check_assertion(sym->kind == (a_symbol_kind)sk_class_template); + sp->argument_template = tssp->variant.class_template.argument_template; + sp->substituted_param_template = + tssp->variant.class_template.substituted_param_template; + } /* if */ + } /* for */ + return saved; +} /* save_template_param_symbols */ + + +static void restore_template_param_symbols( + a_saved_template_param_symbol_ptr saved, + size_t count) +/* +Put the template parameter symbols saved by save_template_param_symbols back +as they were, and release the saved state. +*/ +{ + size_t i; + + for (i = 0; i < count; i++) { + a_saved_template_param_symbol_ptr sp = &saved[i]; + a_symbol_ptr sym = sp->param_symbol; + if (sym->kind == (a_symbol_kind)sk_type) { + sym->variant.type.ptr = sp->type; + } else if (sym->kind == (a_symbol_kind)sk_constant) { + sym->variant.constant = sp->constant; + } else { + a_template_symbol_supplement_ptr tssp = sym->variant.template_info; + tssp->variant.class_template.argument_template = + sp->argument_template; + tssp->variant.class_template.substituted_param_template = + sp->substituted_param_template; + } /* if */ + sym->template_param_not_visible = sp->not_visible; + } /* for */ + free_general(saved, count * sizeof(a_saved_template_param_symbol)); +} /* restore_template_param_symbols */ + + +static a_boolean bind_template_params_for_lambda_rescan( + a_lambda_ptr lambda, + a_token_cache_ptr tokens, + a_template_param_ptr template_param_list, + a_template_arg_ptr template_arg_list, + a_saved_template_param_symbol_ptr *p_saved, + size_t *p_num_saved) +/* +If an instantiation scope for template_param_list is active and lambda names +one of the parameters, bind those that have a value in template_arg_list for +the rescan of tokens. *p_saved and *p_num_saved get the state to restore +afterwards (NULL and 0 if nothing is bound). Return FALSE if a named +parameter has no value yet. Generic lambdas and packs are not handled yet. +*/ +{ + a_template_param_ptr tpp; + a_boolean names_a_parameter = FALSE; + a_boolean names_a_pack = FALSE; + a_boolean all_named_have_values = TRUE; + + *p_saved = NULL; + *p_num_saved = 0; + if (!lambda->is_generic && !lambda->has_template_param_list && + instantiation_scope_is_active_for_param_list(template_param_list)) { + for (tpp = template_param_list; tpp != NULL; tpp = tpp->next) { + if (lambda_tokens_name_template_param(tokens, tpp)) { + names_a_parameter = TRUE; + if (tpp->is_pack) names_a_pack = TRUE; + } /* if */ + } /* for */ + } /* if */ + if (names_a_parameter && !names_a_pack) { + a_saved_template_param_symbol_ptr saved; + size_t num_saved; + saved = save_template_param_symbols(template_param_list, &num_saved); + /* Hide every parameter first, so that one that has no value in + template_arg_list stays hidden whatever state its symbol was in. */ + for (tpp = template_param_list; tpp != NULL; tpp = tpp->next) { + tpp->param_symbol->template_param_not_visible = TRUE; + } /* for */ + update_template_param_symbols(template_param_list, template_arg_list, + /*partial_argument_list=*/TRUE); + for (tpp = template_param_list; tpp != NULL; tpp = tpp->next) { + if (tpp->param_symbol->template_param_not_visible && + lambda_tokens_name_template_param(tokens, tpp)) { + all_named_have_values = FALSE; + } /* if */ + } /* for */ + if (all_named_have_values) { + *p_saved = saved; + *p_num_saved = num_saved; + } else { + restore_template_param_symbols(saved, num_saved); + } /* if */ + } /* if */ + return all_named_have_values; +} /* bind_template_params_for_lambda_rescan */ + + static an_expr_node_ptr copy_template_param_lambda_expr( an_expr_node_ptr expr, a_template_arg_ptr template_arg_list, @@ -20521,6 +20738,10 @@ copy_template_param_expr for the parameter descriptions. a_lambda_ptr lambda = expr->variant.init.source.lambda; a_type_ptr closure_class = lambda->closure_class; a_token_cache_ptr tokens; + a_saved_template_param_symbol_ptr + saved_param_symbols = NULL; + size_t num_saved_param_symbols = 0; + a_boolean params_have_decl_scope; /* First, do a preliminary substitution on the lambda's routine type so that any SFINAE-able substitution failure is reported as a deduction failure @@ -20531,10 +20752,22 @@ copy_template_param_expr for the parameter descriptions. copy_error, ctws_state); if (*copy_error) goto done; tokens = cached_lambdas->get(closure_class); + params_have_decl_scope = template_param_list != NULL && + template_param_list->cache.decl_info != NULL && + template_param_list->param_symbol != NULL; if (tokens == NULL) { /* No cached tokens were recorded for this lambda (this can happen in severe error cases). Substitution cannot proceed. */ subst_fail(*copy_error); + } else if (!params_have_decl_scope && template_param_list != NULL && + !bind_template_params_for_lambda_rescan( + lambda, tokens, + template_param_list, + template_arg_list, + &saved_param_symbols, + &num_saved_param_symbols)) { + /* The lambda names a template parameter that has no value yet. */ + subst_fail(*copy_error); } else { an_operand result_operand; an_expr_stack_entry expr_stack_entry; @@ -20554,9 +20787,7 @@ copy_template_param_expr for the parameter descriptions. expr_stack_entry.suppress_diagnostics = TRUE; expr_stack_entry.possible_rescan_context = TRUE; push_lexical_state_stack(); - if (template_param_list != NULL && - template_param_list->cache.decl_info != NULL && - template_param_list->param_symbol != NULL) { + if (params_have_decl_scope) { /* Make the template parameters being substituted visible during the rescan so that names that refer to them can be resolved. The scope is pushed reusing the original declaration scope number so that the @@ -20643,6 +20874,12 @@ copy_template_param_expr for the parameter descriptions. if (pushed_template_decl_scope) { pop_scope(); } /* if */ + if (saved_param_symbols != NULL) { + /* Put back the template parameters that + bind_template_params_for_lambda_rescan bound. */ + restore_template_param_symbols(saved_param_symbols, + num_saved_param_symbols); + } /* if */ pop_lexical_state_stack(); pop_expr_stack(); restore_expr_stack(saved_expr_stack); diff --git a/src/overload.c b/src/overload.c index 57d3973e66..61ba5ca951 100644 --- a/src/overload.c +++ b/src/overload.c @@ -5641,10 +5641,18 @@ is used in the constraint, its evaluation will fail. a_template_ptr templ = rp->assoc_template; check_assertion(templ != NULL); init_ctws_state(&ctws_state); + /* Push an instantiation scope for the template, as the substitution + into the signature does (see the call of + push_instantiation_scope_for_rescan in + wrapup_function_template_argument_deduction's caller), so that the + names of its template parameters are found if the substitution + rescans a lambda in the condition. */ + push_instantiation_scope_for_rescan(symbol_for(templ)); expr = copy_expr_with_substitutions(expr, t_args, templ_params_of(symbol_for(templ)), CTWS_NO_OPTIONS, &failed, &ctws_state); + pop_instantiation_scope_for_rescan(); if (failed) { /* If substitution failed, the function is discarded as a candidate. No need to try another attribute. */ diff --git a/tests/expectations/edg_x86_64/imported.log b/tests/expectations/edg_x86_64/imported.log index 036be42be2..187a82deff 100644 --- a/tests/expectations/edg_x86_64/imported.log +++ b/tests/expectations/edg_x86_64/imported.log @@ -7122,14 +7122,12 @@ gnu/cpp/cpp2a/lambda-generic2.sft.cpp:fn:-DTEST_NUMBER=1 --c++17:FAIL: gnu/cpp/cpp2a/lambda-generic3.sft.cpp:fn:-DTEST_NUMBER=1 --c++17:FAIL: gnu/cpp/cpp2a/lambda-generic7.sft.cpp:fn:-DTEST_NUMBER=1 --c++20:FAIL: gnu/cpp/cpp2a/lambda-targ1.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL: -gnu/cpp/cpp2a/lambda-targ4.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL: gnu/cpp/cpp2a/lambda-targ7.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:ABORT: gnu/cpp/cpp2a/lambda-targ8.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL: gnu/cpp/cpp2a/lambda-this3.sft.cpp:fp:-DTEST_NUMBER=1 --c++17:FAIL: gnu/cpp/cpp2a/lambda-uneval11.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL: gnu/cpp/cpp2a/lambda-uneval13.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL: gnu/cpp/cpp2a/lambda-uneval3.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL: -gnu/cpp/cpp2a/lambda-uneval7.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL: gnu/cpp/cpp2a/lambda-uneval8.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL: gnu/cpp/cpp2a/linkage-spec1.sft.cpp:fn:-DTEST_NUMBER=1 --c++11:FAIL: gnu/cpp/cpp2a/nontype-class17.sft.cpp:fn:-DTEST_NUMBER=1 --c++20:FAIL: diff --git a/tests/tests/changes/.gh-338-lambda-template-param-enable-if.rto/default.1.1.txt b/tests/tests/changes/.gh-338-lambda-template-param-enable-if.rto/default.1.1.txt new file mode 100644 index 0000000000..491d3c91e1 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-enable-if.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --clang --clang_version=210000 --c++20 Test_name.c diff --git a/tests/tests/changes/.gh-338-lambda-template-param-enable-if.rto/default.2.1.txt b/tests/tests/changes/.gh-338-lambda-template-param-enable-if.rto/default.2.1.txt new file mode 100644 index 0000000000..187bef0a38 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-enable-if.rto/default.2.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=2 --clang --clang_version=210000 --c++23 Test_name.c diff --git a/tests/tests/changes/.gh-338-lambda-template-param-generic.rto/default.1.1.txt b/tests/tests/changes/.gh-338-lambda-template-param-generic.rto/default.1.1.txt new file mode 100644 index 0000000000..dd246f1eae --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-generic.rto/default.1.1.txt @@ -0,0 +1,20 @@ +fe_only -DTEST_NUMBER=1 --c++20 Test_name.c +"Test_name.c", line 16: error: identifier "T" is undefined + void f(char (*)[sizeof([](auto x){ return T(x); }(1))]) {} + ^ + detected during instantiation of function "lambda [](auto)->auto [with =int]" at line 17 + +"Test_name.c", line 16: error: identifier "T" is undefined + void f(char (*)[sizeof([](auto x){ return T(x); }(1))]) {} + ^ + detected during instantiation of function "lambda [](auto)->auto [with =int]" at line 17 + +"Test_name.c", line 17: error: no instance of function template "f" matches the argument list + argument types are: (char (*)[4]) + void use() { char a[sizeof(int)]; f(&a); } + ^ +"Test_name.c", line 16: note: function template "f" does not match because argument #1 does not match parameter + void f(char (*)[sizeof([](auto x){ return T(x); }(1))]) {} + ^ + +3 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/changes/.gh-338-lambda-template-param-generic.rto/default.1.matches.txt b/tests/tests/changes/.gh-338-lambda-template-param-generic.rto/default.1.matches.txt new file mode 100644 index 0000000000..95fe5b6ac3 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-generic.rto/default.1.matches.txt @@ -0,0 +1,15 @@ +Matches for "line 16: error: identifier "T" is undefined": + <----------------------------------------------------------------------------- + line 16: error: identifier "T" is undefined + -----------------------------------------------------------------------------> + <----------------------------------------------------------------------------- + line 16: error: identifier "T" is undefined + -----------------------------------------------------------------------------> +Matches for "line 17: error: no instance of function template "f" matches the argument list": + <----------------------------------------------------------------------------- + line 17: error: no instance of function template "f" matches the argument list + -----------------------------------------------------------------------------> +Matches for "\A(?![\s\S]*line (?!1[67]\b)\d+: error)": + <----------------------------------------------------------------------------- + + -----------------------------------------------------------------------------> diff --git a/tests/tests/changes/.gh-338-lambda-template-param-gnu.rto/default.1.1.txt b/tests/tests/changes/.gh-338-lambda-template-param-gnu.rto/default.1.1.txt new file mode 100644 index 0000000000..522f2c3cf6 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-gnu.rto/default.1.1.txt @@ -0,0 +1,18 @@ +fe_only -DTEST_NUMBER=1 --c++20 --gnu_version=999999 Test_name.c +"Test_name.c", line 32: error: no instance of function template "g" matches the argument list + argument types are: (A<2>) + g<1>(A<2>{}); + ^ +"Test_name.c", line 22: note: function template "g" does not match because argument #1 does not match parameter + template void g(A<[]{return N;}()>) {} + ^ + +"Test_name.c", line 33: error: no instance of function template "k" matches the argument list + argument types are: (char (*)[100]) + k<1, int>(&b); + ^ +"Test_name.c", line 24: note: function template "k" does not match because argument #1 does not match parameter + void k(char (*)[([]{ return [] { return N + int(sizeof(T)); }(); }())]) {} + ^ + +2 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/changes/.gh-338-lambda-template-param-gnu.rto/default.1.matches.txt b/tests/tests/changes/.gh-338-lambda-template-param-gnu.rto/default.1.matches.txt new file mode 100644 index 0000000000..31c10bd0af --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-gnu.rto/default.1.matches.txt @@ -0,0 +1,12 @@ +Matches for "line 32: error: no instance of function template "g" matches the argument list": + <----------------------------------------------------------------------------- + line 32: error: no instance of function template "g" matches the argument list + -----------------------------------------------------------------------------> +Matches for "line 33: error: no instance of function template "k" matches the argument list": + <----------------------------------------------------------------------------- + line 33: error: no instance of function template "k" matches the argument list + -----------------------------------------------------------------------------> +Matches for "\A(?![\s\S]*line (?!3[23]\b)\d+: error)": + <----------------------------------------------------------------------------- + + -----------------------------------------------------------------------------> diff --git a/tests/tests/changes/.gh-338-lambda-template-param-incomplete.rto/default.1.1.txt b/tests/tests/changes/.gh-338-lambda-template-param-incomplete.rto/default.1.1.txt new file mode 100644 index 0000000000..d7bbabc0d3 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-incomplete.rto/default.1.1.txt @@ -0,0 +1,13 @@ +fe_only -DTEST_NUMBER=1 --c++20 --gnu_version=999999 Test_name.c +"Test_name.c", line 22: error: identifier "N" is undefined + void g(const char (*)[([]{ return N; })()]); + ^ + +"Test_name.c", line 25: error: more than one instance of overloaded function "g" matches the argument list: + function template "void g(const char (*)[])" (declared at line 22) + function template "void g(const char (*)[])" (declared at line 24) + argument types are: (const char (*)[6]) + void use_g() { g<6>(&"hello"); } + ^ + +2 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/changes/.gh-338-lambda-template-param-incomplete.rto/default.1.matches.txt b/tests/tests/changes/.gh-338-lambda-template-param-incomplete.rto/default.1.matches.txt new file mode 100644 index 0000000000..2d58016fa6 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-incomplete.rto/default.1.matches.txt @@ -0,0 +1,4 @@ +Matches for "line 25: error: more than one instance of overloaded function "g" matches the argument list": + <----------------------------------------------------------------------------- + line 25: error: more than one instance of overloaded function "g" matches the argument list + -----------------------------------------------------------------------------> diff --git a/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.1.1.txt b/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.1.1.txt new file mode 100644 index 0000000000..14a44460a1 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.1.1.txt @@ -0,0 +1,20 @@ +fe_only -DTEST_NUMBER=1 --c++20 Test_name.c +"Test_name.c", line 34: error: identifier "T" is undefined + char (*)[sizeof([](auto x){ return T(x); }(1))]) {} + ^ + detected during instantiation of function "lambda [](auto)->auto [with =int]" at line 35 + +"Test_name.c", line 34: error: identifier "T" is undefined + char (*)[sizeof([](auto x){ return T(x); }(1))]) {} + ^ + detected during instantiation of function "lambda [](auto)->auto [with =int]" at line 35 + +"Test_name.c", line 35: error: no instance of function template "f" matches the argument list + argument types are: (char (*)[4], char (*)[4]) + void use() { char a[sizeof(int)]; f(&a, &a); } + ^ +"Test_name.c", line 33: note: function template "f" does not match because argument #2 does not match parameter + void f(char (*)[sizeof([]{ return T{}; }())], + ^ + +3 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.1.matches.txt b/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.1.matches.txt new file mode 100644 index 0000000000..4890564b4c --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.1.matches.txt @@ -0,0 +1,7 @@ +Matches for "(line 34: error: identifier\s+"T"\s+is\s+undefined|line 39: error: no\s+instance\s+of\s+function\s+template\s+"f"\s+matches\s+the\s+argument\s+list|line 42: error: identifier\s+"Ts"\s+is\s+undefined|line 47: error: identifier\s+"T"\s+is\s+undefined)": + <----------------------------------------------------------------------------- + line 34: error: identifier "T" is undefined + -----------------------------------------------------------------------------> + <----------------------------------------------------------------------------- + line 34: error: identifier "T" is undefined + -----------------------------------------------------------------------------> diff --git a/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.2.1.txt b/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.2.1.txt new file mode 100644 index 0000000000..fc9362b422 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.2.1.txt @@ -0,0 +1,10 @@ +fe_only -DTEST_NUMBER=2 --c++20 Test_name.c +"Test_name.c", line 39: error: no instance of function template "f" matches the argument list + argument types are: (long long, char (*)[8]) + void use() { char a[sizeof(long long)]; f(1LL, &a); } + ^ +"Test_name.c", line 38: note: function template "f" does not match because argument #2 does not match parameter + void f(T x, char (*)[sizeof([](decltype(x) y){ return y; }(x))]) {} + ^ + +1 error detected in the compilation of "Test_name.c". diff --git a/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.2.matches.txt b/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.2.matches.txt new file mode 100644 index 0000000000..9e5e709ad9 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.2.matches.txt @@ -0,0 +1,4 @@ +Matches for "(line 34: error: identifier\s+"T"\s+is\s+undefined|line 39: error: no\s+instance\s+of\s+function\s+template\s+"f"\s+matches\s+the\s+argument\s+list|line 42: error: identifier\s+"Ts"\s+is\s+undefined|line 47: error: identifier\s+"T"\s+is\s+undefined)": + <----------------------------------------------------------------------------- + line 39: error: no instance of function template "f" matches the argument list + -----------------------------------------------------------------------------> diff --git a/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.3.1.txt b/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.3.1.txt new file mode 100644 index 0000000000..52c2562ed6 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.3.1.txt @@ -0,0 +1,18 @@ +fe_only -DTEST_NUMBER=3 --c++20 Test_name.c +"Test_name.c", line 42: error: identifier "Ts" is undefined + void f(char (*)[([]{ return (sizeof(Ts) + ... + 0); }())]) {} + ^ + +"Test_name.c", line 42: error: fold expression does not refer to a parameter pack + void f(char (*)[([]{ return (sizeof(Ts) + ... + 0); }())]) {} + ^ + +"Test_name.c", line 43: error: no instance of function template "f" matches the argument list + argument types are: (char (*)[12]) + void use() { char a[sizeof(int) + sizeof(long long)]; f(&a); } + ^ +"Test_name.c", line 42: note: substituting explicit template arguments "" for function template "f" failed + void f(char (*)[([]{ return (sizeof(Ts) + ... + 0); }())]) {} + ^ + +3 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.3.matches.txt b/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.3.matches.txt new file mode 100644 index 0000000000..5a89863e97 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.3.matches.txt @@ -0,0 +1,4 @@ +Matches for "(line 34: error: identifier\s+"T"\s+is\s+undefined|line 39: error: no\s+instance\s+of\s+function\s+template\s+"f"\s+matches\s+the\s+argument\s+list|line 42: error: identifier\s+"Ts"\s+is\s+undefined|line 47: error: identifier\s+"T"\s+is\s+undefined)": + <----------------------------------------------------------------------------- + line 42: error: identifier "Ts" is undefined + -----------------------------------------------------------------------------> diff --git a/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.4.1.txt b/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.4.1.txt new file mode 100644 index 0000000000..c68cc891f5 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.4.1.txt @@ -0,0 +1,22 @@ +fe_only -DTEST_NUMBER=4 --c++20 Test_name.c +"Test_name.c", line 47: error: identifier "T" is undefined + char (*)[([]{ return sizeof(T) + (sizeof(Ts) + ... + 0); }())]) {} + ^ + +"Test_name.c", line 47: error: identifier "Ts" is undefined + char (*)[([]{ return sizeof(T) + (sizeof(Ts) + ... + 0); }())]) {} + ^ + +"Test_name.c", line 47: error: fold expression does not refer to a parameter pack + char (*)[([]{ return sizeof(T) + (sizeof(Ts) + ... + 0); }())]) {} + ^ + +"Test_name.c", line 48: error: no instance of function template "f" matches the argument list + argument types are: (char (*)[4], char (*)[12]) + void use() { char a[sizeof(int)], b[sizeof(int) + sizeof(long long)]; f(&a, &b); } + ^ +"Test_name.c", line 46: note: substituting explicit template arguments "" for function template "f" failed + void f(char (*)[sizeof([]{ return T{}; }())], + ^ + +4 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.4.matches.txt b/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.4.matches.txt new file mode 100644 index 0000000000..0d5a0c3345 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-limits.rto/default.4.matches.txt @@ -0,0 +1,4 @@ +Matches for "(line 34: error: identifier\s+"T"\s+is\s+undefined|line 39: error: no\s+instance\s+of\s+function\s+template\s+"f"\s+matches\s+the\s+argument\s+list|line 42: error: identifier\s+"Ts"\s+is\s+undefined|line 47: error: identifier\s+"T"\s+is\s+undefined)": + <----------------------------------------------------------------------------- + line 47: error: identifier "T" is undefined + -----------------------------------------------------------------------------> diff --git a/tests/tests/changes/.gh-338-lambda-template-param-mismatch.rto/default.1.1.txt b/tests/tests/changes/.gh-338-lambda-template-param-mismatch.rto/default.1.1.txt new file mode 100644 index 0000000000..e957e4c6a7 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-mismatch.rto/default.1.1.txt @@ -0,0 +1,15 @@ +fe_only -DTEST_NUMBER=1 --c++20 --clang --clang_version=210000 Test_name.c +"Test_name.c", line 24: error: no instance of function template "h" matches the argument list + argument types are: (char (*)[4]) + h(&a); + ^ +"Test_name.c", line 14: note: function template "h" does not match because argument #1 does not match parameter + template void h(char (*)[sizeof([](T y){ return y; }(T{}))]) {} + ^ + +"Test_name.c", line 25: error: no instance of function template "pick" matches the argument list + argument types are: (int) + pick(1); + ^ + +2 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/changes/.gh-338-lambda-template-param-mismatch.rto/default.1.matches.txt b/tests/tests/changes/.gh-338-lambda-template-param-mismatch.rto/default.1.matches.txt new file mode 100644 index 0000000000..5a0491b0ca --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-mismatch.rto/default.1.matches.txt @@ -0,0 +1,12 @@ +Matches for "line 24: error: no instance of function template "h" matches the argument list": + <----------------------------------------------------------------------------- + line 24: error: no instance of function template "h" matches the argument list + -----------------------------------------------------------------------------> +Matches for "line 25: error: no instance of function template "pick" matches the argument list": + <----------------------------------------------------------------------------- + line 25: error: no instance of function template "pick" matches the argument list + -----------------------------------------------------------------------------> +Matches for "\A(?![\s\S]*line (?!2[45]\b)\d+: error)": + <----------------------------------------------------------------------------- + + -----------------------------------------------------------------------------> diff --git a/tests/tests/changes/.gh-338-lambda-template-param-outer.rto/default.1.1.txt b/tests/tests/changes/.gh-338-lambda-template-param-outer.rto/default.1.1.txt new file mode 100644 index 0000000000..0ba0cfe523 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-outer.rto/default.1.1.txt @@ -0,0 +1,10 @@ +fe_only -DTEST_NUMBER=1 --c++20 --gnu_version=999999 Test_name.c +"Test_name.c", line 25: error: no instance of function template "h" matches the argument list + argument types are: (char (*)[7], double) + h<6>(&a, 1.0); + ^ +"Test_name.c", line 22: note: substituting explicit template arguments "<6>" for function template "h" failed + void h(char (*)[([]{ return N + sizeof(U); }())], U) {} + ^ + +1 error detected in the compilation of "Test_name.c". diff --git a/tests/tests/changes/.gh-338-lambda-template-param-outer.rto/default.1.matches.txt b/tests/tests/changes/.gh-338-lambda-template-param-outer.rto/default.1.matches.txt new file mode 100644 index 0000000000..6543c7228a --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-outer.rto/default.1.matches.txt @@ -0,0 +1,8 @@ +Matches for "line 25: error: no instance of function template "h" matches the argument list": + <----------------------------------------------------------------------------- + line 25: error: no instance of function template "h" matches the argument list + -----------------------------------------------------------------------------> +Matches for "\A(?![\s\S]*line (?!25\b)\d+: error)": + <----------------------------------------------------------------------------- + + -----------------------------------------------------------------------------> diff --git a/tests/tests/changes/.gh-338-lambda-template-param-outer.rto/default.2.1.txt b/tests/tests/changes/.gh-338-lambda-template-param-outer.rto/default.2.1.txt new file mode 100644 index 0000000000..a323840c63 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-outer.rto/default.2.1.txt @@ -0,0 +1,10 @@ +fe_only -DTEST_NUMBER=2 --c++20 --clang --clang_version=210000 Test_name.c +"Test_name.c", line 25: error: no instance of function template "h" matches the argument list + argument types are: (char (*)[7], double) + h<6>(&a, 1.0); + ^ +"Test_name.c", line 22: note: substituting explicit template arguments "<6>" for function template "h" failed + void h(char (*)[([]{ return N + sizeof(U); }())], U) {} + ^ + +1 error detected in the compilation of "Test_name.c". diff --git a/tests/tests/changes/.gh-338-lambda-template-param-outer.rto/default.2.matches.txt b/tests/tests/changes/.gh-338-lambda-template-param-outer.rto/default.2.matches.txt new file mode 100644 index 0000000000..6543c7228a --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-outer.rto/default.2.matches.txt @@ -0,0 +1,8 @@ +Matches for "line 25: error: no instance of function template "h" matches the argument list": + <----------------------------------------------------------------------------- + line 25: error: no instance of function template "h" matches the argument list + -----------------------------------------------------------------------------> +Matches for "\A(?![\s\S]*line (?!25\b)\d+: error)": + <----------------------------------------------------------------------------- + + -----------------------------------------------------------------------------> diff --git a/tests/tests/changes/.gh-338-lambda-template-param-overload.rto/default.1.1.txt b/tests/tests/changes/.gh-338-lambda-template-param-overload.rto/default.1.1.txt new file mode 100644 index 0000000000..338dae3ddf --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-overload.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --c++20 --gnu_version=999999 Test_name.c diff --git a/tests/tests/changes/.gh-338-lambda-template-param-overload.rto/default.2.1.txt b/tests/tests/changes/.gh-338-lambda-template-param-overload.rto/default.2.1.txt new file mode 100644 index 0000000000..f60cd1b5e4 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-overload.rto/default.2.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=2 --c++20 --clang --clang_version=210000 Test_name.c diff --git a/tests/tests/changes/.gh-338-lambda-template-param-qualified.rto/default.1.1.txt b/tests/tests/changes/.gh-338-lambda-template-param-qualified.rto/default.1.1.txt new file mode 100644 index 0000000000..338dae3ddf --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-qualified.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --c++20 --gnu_version=999999 Test_name.c diff --git a/tests/tests/changes/.gh-338-lambda-template-param-qualified.rto/default.2.1.txt b/tests/tests/changes/.gh-338-lambda-template-param-qualified.rto/default.2.1.txt new file mode 100644 index 0000000000..f60cd1b5e4 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-qualified.rto/default.2.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=2 --c++20 --clang --clang_version=210000 Test_name.c diff --git a/tests/tests/changes/.gh-338-lambda-template-param-variations-negative.rto/default.1.1.txt b/tests/tests/changes/.gh-338-lambda-template-param-variations-negative.rto/default.1.1.txt new file mode 100644 index 0000000000..1ce2831b0b --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-variations-negative.rto/default.1.1.txt @@ -0,0 +1,30 @@ +fe_only -DTEST_NUMBER=1 --c++20 Test_name.c +"Test_name.c", line 31: error: no instance of function template "named_in_param::f" matches the argument list + argument types are: (char (*)[5]) + void g() { char a[sizeof(int) + 1]; f(&a); } + ^ +"Test_name.c", line 30: note: function template "named_in_param::f" does not match because argument #1 does not match parameter + void f(char (*)[sizeof([](T) { return T(); }(1))]) {} + ^ + +"Test_name.c", line 38: error: no instance of function template "non_generic_lambda::f" matches the argument list + argument types are: (char (*)[5]) + void g() { char a[sizeof(int) + 1]; f(&a); } + ^ +"Test_name.c", line 37: note: function template "non_generic_lambda::f" does not match because argument #1 does not match parameter + void f(char (*)[sizeof([](int) { return T(); }(1))]) {} + ^ + +"Test_name.c", line 44: error: identifier "T" is undefined + void f(char (*)[sizeof([](auto) { return T(); }(1))]) {} + ^ + +"Test_name.c", line 45: error: no instance of function template "generic_lambda::f" matches the argument list + argument types are: (char (*)[4]) + void g() { char a[sizeof(int)]; f(&a); } + ^ +"Test_name.c", line 44: note: function template "generic_lambda::f" does not match because argument #1 does not match parameter + void f(char (*)[sizeof([](auto) { return T(); }(1))]) {} + ^ + +4 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/changes/.gh-338-lambda-template-param-variations-negative.rto/default.1.matches.txt b/tests/tests/changes/.gh-338-lambda-template-param-variations-negative.rto/default.1.matches.txt new file mode 100644 index 0000000000..f430d5719c --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-variations-negative.rto/default.1.matches.txt @@ -0,0 +1,20 @@ +Matches for "line 31: error: no instance of function template "named_in_param::f" matches the argument list": + <----------------------------------------------------------------------------- + line 31: error: no instance of function template "named_in_param::f" matches the argument list + -----------------------------------------------------------------------------> +Matches for "line 38: error: no instance of function template "non_generic_lambda::f" matches the argument list": + <----------------------------------------------------------------------------- + line 38: error: no instance of function template "non_generic_lambda::f" matches the argument list + -----------------------------------------------------------------------------> +Matches for "line 44: error: identifier "T" is undefined": + <----------------------------------------------------------------------------- + line 44: error: identifier "T" is undefined + -----------------------------------------------------------------------------> +Matches for "line 45: error: no instance of function template "generic_lambda::f" matches the argument list": + <----------------------------------------------------------------------------- + line 45: error: no instance of function template "generic_lambda::f" matches the argument list + -----------------------------------------------------------------------------> +Matches for "\A(?![\s\S]*line (?!(31|38|44|45)\b)\d+: error)": + <----------------------------------------------------------------------------- + + -----------------------------------------------------------------------------> diff --git a/tests/tests/changes/.gh-338-lambda-template-param-variations.rto/default.1.1.txt b/tests/tests/changes/.gh-338-lambda-template-param-variations.rto/default.1.1.txt new file mode 100644 index 0000000000..32fc82e916 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-variations.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --c++20 Test_name.c diff --git a/tests/tests/changes/.gh-338-lambda-template-param-variations.rto/default.2.1.txt b/tests/tests/changes/.gh-338-lambda-template-param-variations.rto/default.2.1.txt new file mode 100644 index 0000000000..4f88482471 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param-variations.rto/default.2.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=2 --c++23 Test_name.c diff --git a/tests/tests/changes/.gh-338-lambda-template-param.rto/default.1.1.txt b/tests/tests/changes/.gh-338-lambda-template-param.rto/default.1.1.txt new file mode 100644 index 0000000000..32fc82e916 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --c++20 Test_name.c diff --git a/tests/tests/changes/.gh-338-lambda-template-param.rto/default.2.1.txt b/tests/tests/changes/.gh-338-lambda-template-param.rto/default.2.1.txt new file mode 100644 index 0000000000..4f88482471 --- /dev/null +++ b/tests/tests/changes/.gh-338-lambda-template-param.rto/default.2.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=2 --c++23 Test_name.c diff --git a/tests/tests/changes/gh-338-lambda-template-param-enable-if.sft.cpp b/tests/tests/changes/gh-338-lambda-template-param-enable-if.sft.cpp new file mode 100644 index 0000000000..7b4804c09d --- /dev/null +++ b/tests/tests/changes/gh-338-lambda-template-param-enable-if.sft.cpp @@ -0,0 +1,39 @@ +//type:fp +//options_all:--clang --clang_version=210000 +//options:--c++20:--c++23 +//remark:[GH #338] A lambda in the condition of a Clang enable_if attribute of a function template that names a template parameter +// 10/16/26 [GH #338] +// +// The condition of Clang's enable_if attribute of a function template is +// substituted for the template parameters when the template is a candidate of +// a call. A lambda in the condition is rescanned then, and the names of the +// template parameters that it uses were not found, "identifier "T" is +// undefined" (see gh-338-lambda-template-param). The rescan is done in the +// instantiation scope of the template now, as the substitution into the +// signature is, and the names are found. +// +// The two overloads are selected by the size of the type: the condition is +// evaluated with the arguments of the right instance (a call of each +// overload, repeated, and the other overload is not a candidate). + +template + __attribute__((enable_if(sizeof([]{ return T{}; }()) == sizeof(char), ""))) +int pick(T) { return 1; } +template + __attribute__((enable_if(sizeof([](T v){ return v; }(T{})) == sizeof(int), + ""))) +int pick(T) { return 2; } + +int t1() { return pick('a') + pick(1) + pick('b') + pick(2); } + +// The condition names a private member of the class (a member template that +// is declared in the class): the lambda is rescanned in the access context of +// the template. +class C { + static constexpr int priv = 1; +public: + template + __attribute__((enable_if(sizeof([]{ return priv + T{}; }()) > 0, ""))) + static int f(T) { return 1; } +}; +int t2() { return C::f(1) + C::f('a'); } diff --git a/tests/tests/changes/gh-338-lambda-template-param-generic.sft.cpp b/tests/tests/changes/gh-338-lambda-template-param-generic.sft.cpp new file mode 100644 index 0000000000..7c51a0235a --- /dev/null +++ b/tests/tests/changes/gh-338-lambda-template-param-generic.sft.cpp @@ -0,0 +1,17 @@ +//type:fn +//options_all:--c++20 +//remark:[GH #338] A generic lambda in a function template declaration that names a template parameter is not rescanned with the arguments +//match_regex:line 16: error: identifier "T" is undefined +//match_regex:line 17: error: no instance of function template "f" matches the argument list +//match_regex:\A(?![\s\S]*line (?!1[67]\b)\d+: error) +// 10/16/26 [GH #338] +// +// See gh-338-lambda-template-param. Only a lambda that is not generic and has +// no template parameter list of its own has the template parameters of the +// function template bound to the arguments for the rescan. A generic lambda +// is rescanned as before, so the name of the template parameter is still not +// found here, as it was before the change. (Clang and GCC accept the +// declaration.) This case records the limit of the change. +template +void f(char (*)[sizeof([](auto x){ return T(x); }(1))]) {} +void use() { char a[sizeof(int)]; f(&a); } diff --git a/tests/tests/changes/gh-338-lambda-template-param-gnu.sft.cpp b/tests/tests/changes/gh-338-lambda-template-param-gnu.sft.cpp new file mode 100644 index 0000000000..3b82d4a154 --- /dev/null +++ b/tests/tests/changes/gh-338-lambda-template-param-gnu.sft.cpp @@ -0,0 +1,34 @@ +//type:fn +//options_all:--c++20 --gnu_version=999999 +//remark:[GH #338] A lambda called in a template argument or an array bound of a function template declaration that names a template parameter +//match_regex:line 32: error: no instance of function template "g" matches the argument list +//match_regex:line 33: error: no instance of function template "k" matches the argument list +//match_regex:\A(?![\s\S]*line (?!3[23]\b)\d+: error) +// 10/16/26 [GH #338] +// +// See gh-338-lambda-template-param. The lambda in the declarations below is +// called in a constant expression: a template argument (the case of +// imported/gnu/cpp/cpp2a/lambda-uneval7) and an array bound. Its template +// parameters are bound to the arguments of the instance for the rescan, so the +// two instances of each template have their own parameter types, and an +// argument of the type of the other instance is an error, at the two calls at +// the end only. (The other calls are valid, as for Clang and GCC.) +// +// Without --gnu_version the front end still reports, for the valid calls, +// "template instantiation resulted in unexpected function type": the call of +// the new lambda is not folded to a constant in the type that is built to +// check the instance. That is not changed here. +template struct A { }; +template void g(A<[]{return N;}()>) {} +template +void k(char (*)[([]{ return [] { return N + int(sizeof(T)); }(); }())]) {} +void ok() { + char a[1 + sizeof(int)], b[1 + sizeof(long)], c[4 + sizeof(long)]; + g<1>({}); g<2>({}); g<1>(A<1>{}); g<2>(A<2>{}); + k<1, int>(&a); k<1, long>(&b); k<4, long>(&c); +} +void bad() { + char b[100]; + g<1>(A<2>{}); + k<1, int>(&b); +} diff --git a/tests/tests/changes/gh-338-lambda-template-param-incomplete.sft.cpp b/tests/tests/changes/gh-338-lambda-template-param-incomplete.sft.cpp new file mode 100644 index 0000000000..771aa4eccf --- /dev/null +++ b/tests/tests/changes/gh-338-lambda-template-param-incomplete.sft.cpp @@ -0,0 +1,25 @@ +//type:fn +//options_all:--c++20 --gnu_version=999999 +//remark:[GH #338] A lambda that names a template parameter is rescanned with the arguments that have a value only +//match_regex:line 25: error: more than one instance of overloaded function "g" matches the argument list +// 10/16/26 [GH #338] +// +// See gh-338-lambda-template-param. The list of template arguments that a +// lambda is rescanned with can have arguments that have no value yet (a call +// of a function template whose template arguments are not all deduced, as of +// the two declarations of g below: the lambda is not a deduced context). Only +// the template parameters that have a value are bound for the rescan, the +// others are not visible; binding all of them to the list made the rescan use +// a null constant for N and crash the front end (the case of +// imported/clang/cpp/SemaCXX/lambda-unevaluated). +// +// The two declarations are two function templates (their lambdas are two +// closure types), so the call is ambiguous, as Clang reports. The errors +// "identifier "N" is undefined" at the declarations are not the point of this +// test: they are reported for the rescan of the declarations (they were before +// the change). +template +void g(const char (*)[([]{ return N; })()]); +template +void g(const char (*)[([]{ return N; })()]) {} +void use_g() { g<6>(&"hello"); } diff --git a/tests/tests/changes/gh-338-lambda-template-param-limits.sft.cpp b/tests/tests/changes/gh-338-lambda-template-param-limits.sft.cpp new file mode 100644 index 0000000000..2422d9e61f --- /dev/null +++ b/tests/tests/changes/gh-338-lambda-template-param-limits.sft.cpp @@ -0,0 +1,49 @@ +//type:fn +//options_all:--c++20 +//cases:4 +//remark:[GH #338] What a lambda that names a template parameter cannot do yet +//match_regex:(line 34: error: identifier\s+"T"\s+is\s+undefined|line 39: error: no\s+instance\s+of\s+function\s+template\s+"f"\s+matches\s+the\s+argument\s+list|line 42: error: identifier\s+"Ts"\s+is\s+undefined|line 47: error: identifier\s+"T"\s+is\s+undefined) +// 10/16/26 [GH #338] +// +// See gh-338-lambda-template-param. The limits of the change are pinned here; +// both declarations are valid for Clang and GCC. +// +// Case 1: the template parameters are bound for the rescan of one lambda at a +// time, and only for a lambda that is not generic. The first lambda of the +// signature is rescanned with T bound, and the second (generic) one must not +// see that binding afterwards: it is rescanned as before, and the name T is +// not found. +// +// Case 2: a lambda is rescanned with the template parameters bound only if one +// of its tokens is the name of a template parameter. This one depends on T +// only through decltype of a parameter, so it is rescanned as before and the +// call does not find an instance. +// +// Case 3: a lambda that names a template parameter pack is rescanned as before +// (binding the symbol of a pack is not enough for a pack expansion to find its +// elements, and that is not implemented yet), and the name is not found. +// +// Case 4: the binding of the parameters for the rescan of a lambda that is +// bound does not leak into the next lambda of the declaration: the second +// lambda names a template parameter pack, so it is rescanned as before, and +// the names T and Ts are not found (the first lambda is rescanned with T +// bound). +#if TEST_NUMBER == 1 +template +void f(char (*)[sizeof([]{ return T{}; }())], + char (*)[sizeof([](auto x){ return T(x); }(1))]) {} +void use() { char a[sizeof(int)]; f(&a, &a); } +#elif TEST_NUMBER == 2 +template +void f(T x, char (*)[sizeof([](decltype(x) y){ return y; }(x))]) {} +void use() { char a[sizeof(long long)]; f(1LL, &a); } +#elif TEST_NUMBER == 3 +template +void f(char (*)[([]{ return (sizeof(Ts) + ... + 0); }())]) {} +void use() { char a[sizeof(int) + sizeof(long long)]; f(&a); } +#elif TEST_NUMBER == 4 +template +void f(char (*)[sizeof([]{ return T{}; }())], + char (*)[([]{ return sizeof(T) + (sizeof(Ts) + ... + 0); }())]) {} +void use() { char a[sizeof(int)], b[sizeof(int) + sizeof(long long)]; f(&a, &b); } +#endif diff --git a/tests/tests/changes/gh-338-lambda-template-param-mismatch.sft.cpp b/tests/tests/changes/gh-338-lambda-template-param-mismatch.sft.cpp new file mode 100644 index 0000000000..d0f50f5e28 --- /dev/null +++ b/tests/tests/changes/gh-338-lambda-template-param-mismatch.sft.cpp @@ -0,0 +1,26 @@ +//type:fn +//options_all:--c++20 --clang --clang_version=210000 +//remark:[GH #338] A lambda that names a template parameter: a wrong argument is still an error +//match_regex:line 24: error: no instance of function template "h" matches the argument list +//match_regex:line 25: error: no instance of function template "pick" matches the argument list +//match_regex:\A(?![\s\S]*line (?!2[45]\b)\d+: error) +// 10/16/26 [GH #338] +// +// See gh-338-lambda-template-param. The template parameters are bound to the +// arguments of the instance for the rescan of the lambda: an argument that +// does not match what the lambda computes for those arguments is an error, +// only at the calls below. (The other calls are valid.) The template +// argument case is in gh-338-lambda-template-param-gnu. +template void h(char (*)[sizeof([](T y){ return y; }(T{}))]) {} +template + __attribute__((enable_if(sizeof([]{ return T{}; }()) == sizeof(char), ""))) +int pick(T) { return 1; } +void ok() { + char a[sizeof(int)]; + h(&a); pick('a'); +} +void bad() { + char a[sizeof(int)]; + h(&a); + pick(1); +} diff --git a/tests/tests/changes/gh-338-lambda-template-param-outer.sft.cpp b/tests/tests/changes/gh-338-lambda-template-param-outer.sft.cpp new file mode 100644 index 0000000000..dfd6c20443 --- /dev/null +++ b/tests/tests/changes/gh-338-lambda-template-param-outer.sft.cpp @@ -0,0 +1,26 @@ +//type:fn +//options:--c++20 --gnu_version=999999:--c++20 --clang --clang_version=210000 +//remark:[GH #338] A template parameter that has no value is not looked up as an entity of the same name in an enclosing scope +//match_regex:line 25: error: no instance of function template "h" matches the argument list +//match_regex:\A(?![\s\S]*line (?!25\b)\d+: error) +// 10/16/26 [GH #338] +// +// See gh-338-lambda-template-param. When a template argument is given +// explicitly (h<6>), the arguments of the other template parameters are not +// known yet while the declaration is substituted, and a lambda that names one +// of them (U, below) must not find the global U instead: the parameter that +// has no value makes the copy of the lambda fail, as it did before the lambdas +// could name the template parameters. The bound of the array is 6 + +// sizeof(double) for h<6>(&a, 1.0); the array a has 6 + sizeof(::U) elements, +// so the call is an error, as for Clang and GCC (a rescan that binds the +// global U for U would accept it). (Clang and GCC do not accept the call with +// the array that has 6 + sizeof(double) elements either: the lambda in the +// array bound is not a deduced context for them, and the substituted bound is +// a different expression; that is not what is pinned here.) +struct U { }; +template +void h(char (*)[([]{ return N + sizeof(U); }())], U) {} +void bad() { + char a[6 + sizeof(::U)]; + h<6>(&a, 1.0); +} diff --git a/tests/tests/changes/gh-338-lambda-template-param-overload.sft.cpp b/tests/tests/changes/gh-338-lambda-template-param-overload.sft.cpp new file mode 100644 index 0000000000..c8b7237ccb --- /dev/null +++ b/tests/tests/changes/gh-338-lambda-template-param-overload.sft.cpp @@ -0,0 +1,20 @@ +//type:fp +//options:--c++20 --gnu_version=999999:--c++20 --clang --clang_version=210000 +//remark:[GH #338] The overload that is selected does not depend on a template parameter that has no value yet +// 10/16/26 [GH #338] +// +// See gh-338-lambda-template-param and gh-338-lambda-template-param-outer. +// Clang and GCC call the second h: the first one is not viable, because the +// bound of its array is 6 + sizeof(double) when the argument of U is deduced, +// and the argument is the array a that has 7 elements. A rescan of the lambda +// that finds the global U (a struct that has the size 1) for the template +// parameter U, before the argument of U is deduced, makes the first h viable +// and better (it has no ellipsis), and selects it. +struct U { }; +template +char h(char (*)[([]{ return N + sizeof(U); }())], U) { return 0; } +template +long h(char (*)[7], ...) { return 0; } +char a[7]; +static_assert(sizeof(h<6>(&a, 1.0)) == sizeof(long), + "the second h must be selected"); diff --git a/tests/tests/changes/gh-338-lambda-template-param-qualified.sft.cpp b/tests/tests/changes/gh-338-lambda-template-param-qualified.sft.cpp new file mode 100644 index 0000000000..1ac55bf763 --- /dev/null +++ b/tests/tests/changes/gh-338-lambda-template-param-qualified.sft.cpp @@ -0,0 +1,54 @@ +//type:fp +//options:--c++20 --gnu_version=999999:--c++20 --clang --clang_version=210000 +//remark:[GH #338] A name that is spelled like a template parameter but is a qualified name or a member is not the template parameter +// 10/17/26 [GH #338] +// +// See gh-338-lambda-template-param and gh-338-lambda-template-param-outer. +// When a template argument is given explicitly (f<6>), the arguments of the +// other template parameters are not known yet while the declaration is +// substituted. A lambda that spells the name of one of them (T below) after +// "::", "." or "->" does not name the template parameter: it is a qualified +// name or a member (::T, NS::T, g.T, and G::template T and g.template +// T, where the keyword "template" comes between), and the copy of the +// lambda must not fail because the parameter has no value. The calls are +// valid, and the ones that have an overload that takes "..." select the first +// overload (the second is not viable for the argument), as for Clang and GCC. + +// The global T, by a qualified name. +struct T { char c[3]; }; +template void f1(char (*)[sizeof([]{ return ::T{}; }())], T) {} +void use1() { char a[3]; f1<6>(&a, 1.0); } + +// A member named T. +struct G { int T; }; +template void f2(char (*)[sizeof([]{ return G{}.T; }())], T) {} +void use2() { char a[sizeof(int)]; f2<6>(&a, 1.0); } + +// A namespace member named U, by a qualified name. +namespace NS { struct U { char c[5]; }; } +template void f3(char (*)[sizeof([]{ return NS::U{}; }())], U) {} +void use3() { char a[5]; f3<6>(&a, 1.0); } + +// The overload that is selected. +template char f4(char (*)[sizeof([]{ return ::T{}; }())], T) { return 0; } +template long f4(char (*)[3], ...) { return 0; } +char a4[3]; +static_assert(sizeof(f4<6>(&a4, 1.0)) == sizeof(char), + "the first f4 is viable and better"); + +// A member template named T, after "::template" and after ".template". +struct G1 { template static constexpr int T = 3; }; +template +char f5(char (*)[([]{ return G1::template T; }())], T) { return 0; } +template long f5(char (*)[3], ...) { return 0; } +char a5[3]; +static_assert(sizeof(f5<6>(&a5, 1.0)) == sizeof(char), + "the first f5 is viable and better"); + +struct G2 { template static constexpr int T() { return 3; } }; +template +char f6(char (*)[([]{ return G2{}.template T(); }())], T) { return 0; } +template long f6(char (*)[3], ...) { return 0; } +char a6[3]; +static_assert(sizeof(f6<6>(&a6, 1.0)) == sizeof(char), + "the first f6 is viable and better"); diff --git a/tests/tests/changes/gh-338-lambda-template-param-variations-negative.sft.cpp b/tests/tests/changes/gh-338-lambda-template-param-variations-negative.sft.cpp new file mode 100644 index 0000000000..b5f683fcc3 --- /dev/null +++ b/tests/tests/changes/gh-338-lambda-template-param-variations-negative.sft.cpp @@ -0,0 +1,46 @@ +//type:fn +//options_all:--c++20 +//remark:[GH #338] Variations of a lambda that names a template parameter: an argument of the wrong size, and a generic lambda +//match_regex:line 31: error: no instance of function template "named_in_param::f" matches the argument list +//match_regex:line 38: error: no instance of function template "non_generic_lambda::f" matches the argument list +//match_regex:line 44: error: identifier "T" is undefined +//match_regex:line 45: error: no instance of function template "generic_lambda::f" matches the argument list +//match_regex:\A(?![\s\S]*line (?!(31|38|44|45)\b)\d+: error) +// 10/16/26 [GH #338] +// +// See gh-338-lambda-template-param-variations: the variations from the review +// of the change, as negative cases. +// +// named_in_param and non_generic_lambda: the array does not have the size of +// int. The bound of the parameter is computed by the lambda rescanned with T +// bound to int, so the call has no matching instance, as for Clang and GCC. +// (Before the change, "identifier "T" is undefined" was reported for the +// declarations as well: T was not found when the lambda was rescanned.) +// +// generic_lambda: the parameter of the lambda is auto, and T is named only in +// its body. The template parameters are not bound yet for the rescan of a +// generic lambda (see bind_template_params_for_lambda_rescan in il.c): the +// body of its call operator is scanned when the operator is instantiated, in a +// context that does not include them. So T is not found, and the call is an +// error. (Clang and GCC accept it.) This case records the limit; it moves to +// gh-338-lambda-template-param-variations when generic lambdas are handled. +namespace named_in_param +{ + template + void f(char (*)[sizeof([](T) { return T(); }(1))]) {} + void g() { char a[sizeof(int) + 1]; f(&a); } +} + +namespace non_generic_lambda +{ + template + void f(char (*)[sizeof([](int) { return T(); }(1))]) {} + void g() { char a[sizeof(int) + 1]; f(&a); } +} + +namespace generic_lambda +{ + template + void f(char (*)[sizeof([](auto) { return T(); }(1))]) {} + void g() { char a[sizeof(int)]; f(&a); } +} diff --git a/tests/tests/changes/gh-338-lambda-template-param-variations.sft.cpp b/tests/tests/changes/gh-338-lambda-template-param-variations.sft.cpp new file mode 100644 index 0000000000..a5f21927ea --- /dev/null +++ b/tests/tests/changes/gh-338-lambda-template-param-variations.sft.cpp @@ -0,0 +1,29 @@ +//type:fp +//options:--c++20:--c++23 +//remark:[GH #338] A lambda that names a template parameter in an unnamed parameter or only in its body +// 10/16/26 [GH #338] +// +// See gh-338-lambda-template-param. Variations from the review of the +// change: the lambda names T in the type of its unnamed parameter and in its +// body, or only in its body (its parameter has the type int). Both are +// rescanned with T bound to the argument of each instance, and the calls are +// valid, as for Clang and GCC. (These calls were accepted before the change +// too, although the rescan did not find T. The calls with an array of the +// wrong size, which tell the two apart, and the generic variation are in +// gh-338-lambda-template-param-variations-negative.) + +namespace named_in_param +{ + template + void f(char (*)[sizeof([](T) { return T(); }(1))]) {} + void g() { char a[sizeof(int)]; f(&a); } + void h() { char b[sizeof(long long)]; f(&b); } +} + +namespace non_generic_lambda +{ + template + void f(char (*)[sizeof([](int) { return T(); }(1))]) {} + void g() { char a[sizeof(int)]; f(&a); } + void h() { char b[sizeof(long long)]; f(&b); } +} diff --git a/tests/tests/changes/gh-338-lambda-template-param.sft.cpp b/tests/tests/changes/gh-338-lambda-template-param.sft.cpp new file mode 100644 index 0000000000..1b32d2b0b1 --- /dev/null +++ b/tests/tests/changes/gh-338-lambda-template-param.sft.cpp @@ -0,0 +1,88 @@ +//type:fp +//options:--c++20:--c++23 +//remark:[GH #338] A lambda in a function template declaration that names a template parameter +// 10/16/26 [GH #338] +// +// A lambda in the declaration of a function template (in a parameter type, a +// default template argument, ...) is rescanned from its cached tokens when +// the template arguments are substituted for the template parameters. The +// template parameters of most templates were not visible to the rescan: it +// did not find the names of the template parameters that the lambda uses, +// "identifier "T" is undefined", and the function template could not be +// called. The template parameters are bound to the arguments for the rescan +// of the lambda now, and put back as they were afterwards. +// +// None of the declarations below is an error (as for Clang and GCC). The +// cases that call the lambda in a template argument or in an array bound are +// in gh-338-lambda-template-param-gnu (they need --gnu_version). A wrong +// argument is still an error (gh-338-lambda-template-param-mismatch), and a +// generic lambda is handled as before (gh-338-lambda-template-param-generic). + +// A type parameter in an array bound, and in the parameter of the lambda. +// Three instances: each takes its own argument. +template void h(char (*)[sizeof([](T y){ return y; }(T{}))]) {} +void t2() { + char a[sizeof(char)], b[sizeof(int)], c[sizeof(long)]; + h(&a); h(&b); h(&c); +} + +// A trailing return type: the parameter of the lambda and its body. +template +auto tr(T x) -> decltype([](T y){ return y; }(x)) { return x; } +int t4() { return tr(1) + tr(2); } + +// A requires-clause and a default template argument. +template + requires (sizeof([]{ return T{}; }()) == sizeof(int)) +void rq(T) {} +template +U da() { return U{}; } +void t5() { rq(1); rq(2u); int v = da(); char c = da(); (void)v; (void)c; } + +// The exception specification. +template +void ne(T) noexcept(noexcept([]{ return T{}; }())) {} +void t6() { ne(1); ne('a'); } + +// The template parameters are put back when another instance of the same +// template is deduced while the first is: f checks f (found +// by argument-dependent lookup) in its constraint. (long long, not long: a +// long that has the size of int would make the constraint of f depend +// on itself.) +template struct W { }; +template + requires (sizeof([]{ static_assert(sizeof(T) >= 4); return T{}; }()) >= 4) && + (sizeof(T) > 4 || requires { f(W{}); }) +int f(W) { return sizeof(T); } +int t7() { return f(W{}) + f(W{}); } + +// A lambda that calls a function template whose own declaration has a lambda +// that names its own template parameter. +template +auto gu(U u) -> decltype([](U v){ return v; }(u)) { return u; } +template +auto fu(T x) -> decltype([]{ return gu(T{}); }()) { return gu(x); } +int t8() { return fu(1) + fu(2); } + +// A member template of a class template: the lambda names the template +// parameters of the class and of the member template. +template struct C { + template void n(char (*)[sizeof([]{ return T{} + U{}; }())]) {} + template void r(char (*)[sizeof([](U v){ return v; }(U{}))]) {} +}; +void t9() { + char a[sizeof(int)], b[sizeof(long)]; + C c; + c.n(&a); c.n(&b); c.r(&a); c.r(&b); +} + +// A partial specialization of a class template that has a parameter pack: the +// lambda in its template argument names T. (The pack is not bound.) +template struct B { }; +template struct S { static constexpr int v = 0; }; +template +struct S, Args...> { + static constexpr int v = 1; +}; +static_assert(S, long, char>::v == 1); +static_assert(S, long, char>::v == 0); diff --git a/tests/tests/imported/clang/cpp/SemaCXX/.lambda-unevaluated.rto/default.1.1.txt b/tests/tests/imported/clang/cpp/SemaCXX/.lambda-unevaluated.rto/default.1.1.txt index e86a8f1dcb..93c91e61a7 100644 --- a/tests/tests/imported/clang/cpp/SemaCXX/.lambda-unevaluated.rto/default.1.1.txt +++ b/tests/tests/imported/clang/cpp/SemaCXX/.lambda-unevaluated.rto/default.1.1.txt @@ -32,24 +32,16 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers void use_f() { f({}); } // expected-error {{ambiguous}} ^ -"Test_name.c", line 67: error: identifier "N" is undefined - template void g(const char (*)[([]{ return N; })()]) {} // expected-note {{candidate}} - ^ - "Test_name.c", line 66: error: identifier "N" is undefined template void g(const char (*)[([]{ return N; })()]) {} // expected-note {{candidate}} ^ -"Test_name.c", line 68: error: no instance of overloaded function "PR52073::g" matches the argument list +"Test_name.c", line 68: error: more than one instance of overloaded function "PR52073::g" matches the argument list: + function template "void PR52073::g(const char (*)[])" (declared at line 66) + function template "void PR52073::g(const char (*)[])" (declared at line 67) argument types are: (const char (*)[6]) void use_g() { g<6>(&"hello"); } // expected-error {{ambiguous}} ^ -"Test_name.c", line 67: note: substituting explicit template arguments "<6>" for function template "PR52073::g(const char (*)[])" failed - template void g(const char (*)[([]{ return N; })()]) {} // expected-note {{candidate}} - ^ -"Test_name.c", line 66: note: substituting explicit template arguments "<6>" for function template "PR52073::g(const char (*)[])" failed - template void g(const char (*)[([]{ return N; })()]) {} // expected-note {{candidate}} - ^ "Test_name.c", line 79: error: declaration is incompatible with "void GH51416::A::spam(decltype(()))" (declared at line 75) void A::spam(decltype([] {})) // expected-error{{out-of-line definition of 'spam' does not match}} @@ -226,4 +218,4 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers constexpr auto Ret = ICE()(1); ^ -28 errors detected in the compilation of "Test_name.c". +27 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/imported/clang/cpp/SemaCXX/.lambda-unevaluated.rto/default.2.1.txt b/tests/tests/imported/clang/cpp/SemaCXX/.lambda-unevaluated.rto/default.2.1.txt index 0955c9f228..c8d9a119dd 100644 --- a/tests/tests/imported/clang/cpp/SemaCXX/.lambda-unevaluated.rto/default.2.1.txt +++ b/tests/tests/imported/clang/cpp/SemaCXX/.lambda-unevaluated.rto/default.2.1.txt @@ -32,24 +32,16 @@ fe_only -DTEST_NUMBER=2 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers void use_f() { f({}); } // expected-error {{ambiguous}} ^ -"Test_name.c", line 67: error: identifier "N" is undefined - template void g(const char (*)[([]{ return N; })()]) {} // expected-note {{candidate}} - ^ - "Test_name.c", line 66: error: identifier "N" is undefined template void g(const char (*)[([]{ return N; })()]) {} // expected-note {{candidate}} ^ -"Test_name.c", line 68: error: no instance of overloaded function "PR52073::g" matches the argument list +"Test_name.c", line 68: error: more than one instance of overloaded function "PR52073::g" matches the argument list: + function template "void PR52073::g(const char (*)[])" (declared at line 66) + function template "void PR52073::g(const char (*)[])" (declared at line 67) argument types are: (const char (*)[6]) void use_g() { g<6>(&"hello"); } // expected-error {{ambiguous}} ^ -"Test_name.c", line 67: note: substituting explicit template arguments "<6>" for function template "PR52073::g(const char (*)[])" failed - template void g(const char (*)[([]{ return N; })()]) {} // expected-note {{candidate}} - ^ -"Test_name.c", line 66: note: substituting explicit template arguments "<6>" for function template "PR52073::g(const char (*)[])" failed - template void g(const char (*)[([]{ return N; })()]) {} // expected-note {{candidate}} - ^ "Test_name.c", line 79: error: declaration is incompatible with "void GH51416::A::spam(decltype(()))" (declared at line 75) void A::spam(decltype([] {})) // expected-error{{out-of-line definition of 'spam' does not match}} @@ -150,4 +142,4 @@ fe_only -DTEST_NUMBER=2 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers constexpr auto Ret = ICE()(1); ^ -28 errors detected in the compilation of "Test_name.c". +27 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/imported/gnu/cpp/cpp2a/.lambda-targ4.rto/default.1.1.txt b/tests/tests/imported/gnu/cpp/cpp2a/.lambda-targ4.rto/default.1.1.txt index c37094bcb4..2ed4eaf390 100644 --- a/tests/tests/imported/gnu/cpp/cpp2a/.lambda-targ4.rto/default.1.1.txt +++ b/tests/tests/imported/gnu/cpp/cpp2a/.lambda-targ4.rto/default.1.1.txt @@ -1,6 +1 @@ fe_only -DTEST_NUMBER=1 --gnu_version=999999 -tused -e 200 --no_wrap --no_wrap_diagnostics --c++20 Test_name.c -"Test_name.c", line 8: error: identifier "T" is undefined - template - ^ - -1 error detected in the compilation of "Test_name.c". diff --git a/tests/tests/imported/gnu/cpp/cpp2a/.lambda-uneval7.rto/default.1.1.txt b/tests/tests/imported/gnu/cpp/cpp2a/.lambda-uneval7.rto/default.1.1.txt index 98832623f9..2ed4eaf390 100644 --- a/tests/tests/imported/gnu/cpp/cpp2a/.lambda-uneval7.rto/default.1.1.txt +++ b/tests/tests/imported/gnu/cpp/cpp2a/.lambda-uneval7.rto/default.1.1.txt @@ -1,6 +1 @@ fe_only -DTEST_NUMBER=1 --gnu_version=999999 -tused -e 200 --no_wrap --no_wrap_diagnostics --c++20 Test_name.c -"Test_name.c", line 9: error: identifier "N" is undefined - void g(A<[]{return N;}()>) {} - ^ - -1 error detected in the compilation of "Test_name.c".