Hi! We incorrectly accept some invalid declare variant cases as if declare variant wasn't there, in particular if a function template has some dependent arguments and variant name lookup fails, because that is during fn_type_unification with complain=tf_none, it just sets it to error_mark_node and doesn't complain further, because it doesn't know the substitution failed (we don't return error_mark_node from tsubst_attribute, just create TREE_LIST with error_mark_node TREE_PURPOSE).
The following patch fixes it by returning error_mark_node in that case, then fn_type_unification caller can see it failed and can redo it with explain_p so that errors are reported. Bootstrapped/regtested on x86_64-linux and i686-linux, committed to trunk. 2024-11-01 Jakub Jelinek <ja...@redhat.com> * pt.cc (tsubst_attribute): For "omp declare variant base" attribute if varid is error_mark_node, set val to error_mark_node rather than creating a TREE_LIST with error_mark_node TREE_PURPOSE. * g++.dg/gomp/declare-variant-10.C: New test. --- gcc/cp/pt.cc.jj 2024-10-25 10:00:29.431768386 +0200 +++ gcc/cp/pt.cc 2024-10-31 10:57:05.855001910 +0100 @@ -12163,7 +12163,10 @@ tsubst_attribute (tree t, tree *decl_p, } OMP_TSS_TRAIT_SELECTORS (tss) = nreverse (selectors); } - val = tree_cons (varid, ctx, chain); + if (varid == error_mark_node) + val = error_mark_node; + else + val = tree_cons (varid, ctx, chain); } /* If the first attribute argument is an identifier, don't pass it through tsubst. Attributes like mode, format, --- gcc/testsuite/g++.dg/gomp/declare-variant-10.C.jj 2024-10-31 11:21:13.962820634 +0100 +++ gcc/testsuite/g++.dg/gomp/declare-variant-10.C 2024-10-31 11:26:59.651011406 +0100 @@ -0,0 +1,56 @@ +// { dg-do compile } + +#pragma omp declare variant (f1) match(user={condition(1)}) // { dg-error "'f1' was not declared in this scope; did you mean 'f2'\\\?" } +void +f2 (int) +{ +} + +void f3 (int); + +#pragma omp declare variant (f3) match(user={condition(1)}) // { dg-error "variant 'void f3\\\(int\\\)' and base 'void f4\\\(long int\\\)' have incompatible types" } +void +f4 (long) +{ +} + +#pragma omp declare variant (f5) match(user={condition(1)}) // { dg-error "there are no arguments to 'f5' that depend on a template parameter, so a declaration of 'f5' must be available" } +template <int N> +void +f6 (int) +{ +} + +template <int N> +void f7 (int); + +#pragma omp declare variant (f7) match(user={condition(1)}) // { dg-error "no matching function for call to 'f7\\\(long int\\\)'" } +template <int N> +void +f8 (long) +{ +} + +#pragma omp declare variant (f9) match(user={condition(1)}) +template <typename T> +void +f10 (T) // { dg-error "'f9' was not declared in this scope; did you mean 'f8'\\\?" } +{ +} + +template <typename T> +void f11 (T, int); + +#pragma omp declare variant (f11) match(user={condition(1)}) // { dg-error "variant 'void f11\\\(T, int\\\) \\\[with T = int\\\]' and base 'void f12\\\(T, long int\\\) \\\[with T = int\\\]' have incompatible types" } +template <typename T> +void +f12 (T, long) +{ +} + +void +test () +{ + f10 (0); // { dg-error "no matching function for call to 'f10\\\(int\\\)'" } + f12 (0, 0L); +} Jakub