@@ -3108,10 +3108,8 @@ get_constraint_error_location (tree t)
/* Emit a diagnostic for a failed trait. */
static void
-diagnose_trait_expr (tree expr, tree args)
+diagnose_trait_expr (location_t loc, tree expr, tree args)
{
- location_t loc = cp_expr_location (expr);
-
/* Build a "fake" version of the instantiated trait, so we can
get the instantiated types from result. */
++processing_template_decl;
@@ -3323,6 +3321,51 @@ diagnose_trait_expr (tree expr, tree args)
}
}
+/* Attempt to detect if this is a standard type trait, defined in terms
+ of a compiler builtin (above). If so, this will allow us to provide
+ slightly more helpful diagnostics.
+
+ However, don't unwrap if the type has been specialized (even if we
+ wouldn't have used said specialization).. */
+
+static void
+maybe_unwrap_standard_trait (tree *expr, tree *args)
+{
+ if (TREE_CODE (*expr) != TEMPLATE_ID_EXPR)
+ return;
+
+ tree templ = TREE_OPERAND (*expr, 0);
+ if (TREE_CODE (templ) != TEMPLATE_DECL
+ || !variable_template_p (templ))
+ return;
+
+ tree gen_tmpl = most_general_template (templ);
+ if (DECL_TEMPLATE_SPECIALIZATIONS (gen_tmpl))
+ return;
+
+ for (tree inst = DECL_TEMPLATE_INSTANTIATIONS (gen_tmpl);
+ inst; inst = TREE_CHAIN (inst))
+ if (DECL_TEMPLATE_SPECIALIZATION (TREE_VALUE (inst)))
+ return;
+
+ tree pattern = DECL_TEMPLATE_RESULT (gen_tmpl);
+ tree initial = DECL_INITIAL (pattern);
+ if (TREE_CODE (initial) != TRAIT_EXPR)
+ return;
+
+ /* At this point we're definitely providing a TRAIT_EXPR, update
+ *expr to point at it and provide remapped *args for it. */
+ tree parms = DECL_INNERMOST_TEMPLATE_PARMS (gen_tmpl);
+ tree targs = TREE_OPERAND (*expr, 1);
+ if (targs)
+ targs = tsubst_template_args (targs, *args, tf_none, NULL_TREE);
+ targs = add_outermost_template_args (templ, targs);
+ targs = coerce_template_parms (parms, targs, templ, tf_none);
+
+ *expr = initial;
+ *args = targs;
+}
+
/* Diagnose a substitution failure in the atomic constraint T using ARGS. */
static void
@@ -3347,10 +3390,11 @@ diagnose_atomic_constraint (tree t, tree args, tree result, sat_info info)
/* Generate better diagnostics for certain kinds of expressions. */
tree expr = ATOMIC_CONSTR_EXPR (t);
STRIP_ANY_LOCATION_WRAPPER (expr);
+ maybe_unwrap_standard_trait (&expr, &args);
switch (TREE_CODE (expr))
{
case TRAIT_EXPR:
- diagnose_trait_expr (expr, args);
+ diagnose_trait_expr (loc, expr, args);
break;
case REQUIRES_EXPR:
gcc_checking_assert (info.diagnose_unsatisfaction_p ());
new file mode 100644
@@ -0,0 +1,31 @@
+// PR c++/117294
+// { dg-do compile { target c++20 } }
+
+template <typename T> constexpr bool normal = __is_constructible(T);
+
+template <typename T> constexpr bool partial_spec = __is_constructible(T);
+template <typename T> constexpr bool partial_spec<T*> = false;
+
+template <typename T> constexpr bool explicit_spec = __is_constructible(T);
+template <> constexpr bool explicit_spec<int> = false;
+
+template <typename T>
+concept test_normal = normal<T>;
+// { dg-message "'void' is not default constructible" "" { target *-*-* } .-1 }
+
+template <typename T>
+concept test_partial_spec = partial_spec<T>;
+// { dg-message "evaluated to .false." "" { target *-*-* } .-1 }
+// { dg-bogus "not default constructible" "" { target *-*-* } .-2 }
+
+template <typename T>
+concept test_explicit_spec = explicit_spec<T>;
+// { dg-message "evaluated to .false." "" { target *-*-* } .-1 }
+// { dg-bogus "not default constructible" "" { target *-*-* } .-2 }
+
+static_assert(test_normal<void>); // { dg-error "assert" }
+static_assert(test_partial_spec<void>); // { dg-error "assert" }
+static_assert(test_explicit_spec<void>); // { dg-error "assert" }
+
+static_assert(test_partial_spec<int*>); // { dg-error "assert" }
+static_assert(test_explicit_spec<int>); // { dg-error "assert" }
Does this approach seem reasonable? I'm pretty sure that the way I've handled the templating here is unideal but I'm not sure what a neat way to do what I'm trying to do here would be; any comments are welcome. -- >8 -- Currently, concept failures of standard type traits just report 'expression X<T> evaluates to false'. However, many type traits are actually defined in terms of compiler builtins; we can do better here. For instance, 'is_constructible_v' could go on to explain why the type is not constructible, or 'is_invocable_v' could list potential candidates. As a first step to supporting that we need to be able to map the standard type traits to the builtins that they use. Rather than adding another list that would need to be kept up-to-date whenever a builtin is added, this patch instead tries to detect any variable template defined directly in terms of a TRAIT_EXPR. To avoid false positives, we ignore any variable templates that have any specialisations (partial or explicit), even if we wouldn't have chosen that specialisation anyway. This shouldn't affect any of the standard library type traits that I could see. The new diagnostics from this patch are not immediately much better; however, it would be relatively straight-forward to update the messages in 'diagnose_trait_expr' to provide these new details. This logic could also perhaps be used by 'diagnose_failing_condition' so that cases like 'static_assert(std::is_constructible_v<T>)' get the same treatment; this patch doesn't attempt to update this yet. PR c++/117294 PR c++/113854 gcc/cp/ChangeLog: * constraint.cc (diagnose_trait_expr): Take location to diagnose at explicitly. (maybe_unwrap_standard_trait): New function. (diagnose_atomic_constraint): Use it; pass in the location of the atomic constraint to diagnose_trait_expr. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/concepts-traits4.C: New test. Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com> --- gcc/cp/constraint.cc | 52 +++++++++++++++++-- gcc/testsuite/g++.dg/cpp2a/concepts-traits4.C | 31 +++++++++++ 2 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-traits4.C