Add std::meta::source_location_of (std::meta::annotations_of (...)[...]) support.

Annotations on concepts clearly still don't work properly and if I put
the static_asserts into qux in annotations3.C, I'm getting ICEs.
This commit is contained in:
Jakub Jelinek
2025-11-06 15:02:28 +01:00
committed by Marek Polacek
parent 5058e9b05c
commit 8f0eea15c4
8 changed files with 159 additions and 89 deletions

View File

@@ -715,9 +715,14 @@ attribute_takes_identifier_p (const_tree attr_id)
{
const struct attribute_spec *spec = lookup_attribute_spec (attr_id);
if (spec == NULL)
/* Unknown attribute that we'll end up ignoring, return true so we
don't complain about an identifier argument. */
return true;
{
/* Unknown attribute that we'll end up ignoring, return true so we
don't complain about an identifier argument. Except C++
annotations. */
if (c_dialect_cxx () && id_equal (attr_id, "annotation "))
return false;
return true;
}
else if (!strcmp ("mode", spec->name)
|| !strcmp ("format", spec->name)
|| !strcmp ("cleanup", spec->name)

View File

@@ -1472,6 +1472,11 @@ is_late_template_attribute (tree attr, tree decl)
= lookup_attribute_spec (TREE_PURPOSE (attr));
tree arg;
/* Handle all annotations as late, so that they aren't incorrectly
reordered if some have dependent expressions and others don't. */
if (is_attribute_p ("annotation ", name))
return true;
if (!spec)
/* Unknown attribute. */
return false;
@@ -1494,11 +1499,6 @@ is_late_template_attribute (tree attr, tree decl)
if (is_attribute_p ("tls_model", name))
return true;
/* Handle all annotations as late, so that they aren't incorrectly
reordered if some have dependent expressions and others don't. */
if (is_attribute_p ("annotation ", name))
return true;
/* #pragma omp declare simd attribute needs to be always deferred. */
if (flag_openmp
&& is_attribute_p ("omp declare simd", name))
@@ -1755,6 +1755,10 @@ cp_check_const_attributes (tree attributes)
if (cxx_contract_attribute_p (attr))
continue;
/* Annotation arguments are handled in handle_annotation_attribute. */
if (is_attribute_p ("annotation ", get_attribute_name (attr)))
continue;
tree arg;
/* As we implement alignas using gnu::aligned attribute and
alignas argument is a constant expression, force manifestly

View File

@@ -33735,15 +33735,22 @@ cp_parser_annotation_list (cp_parser *parser)
/*strict_p=*/true);
if (annotation == error_mark_node)
break;
auto suppression
= make_temp_override (suppress_location_wrappers, 0);
annotation = maybe_wrap_with_location (annotation, loc);
annotation = build_tree_list (NULL_TREE, annotation);
if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS))
{
cp_lexer_consume_token (parser->lexer);
annotation = make_pack_expansion (annotation);
if (annotation == error_mark_node)
break;
}
attributes = tree_cons (build_tree_list (internal_identifier,
annotation_identifier),
build_tree_list (NULL_TREE, annotation),
attributes);
annotation, attributes);
if (processing_template_decl)
ATTR_IS_DEPENDENT (attributes) = 1;
}
else if (token->type == CPP_NAME
|| token->type == CPP_KEYWORD

View File

@@ -1041,76 +1041,6 @@ maybe_init_meta_operators (location_t loc)
}
}
/* Process std::meta::source_location_of.
Returns: If r represents a value, a type other than a class type or an
enumeration type, the global namespace, or a data member description,
then source_location{}. Otherwise, an implementation-defined
source_location value. */
static tree
eval_source_location_of (location_t loc, tree r, reflect_kind kind,
tree std_source_location)
{
if (!NON_UNION_CLASS_TYPE_P (std_source_location))
{
error_at (loc, "%qT is not a class type", std_source_location);
return error_mark_node;
}
location_t rloc = UNKNOWN_LOCATION;
if (kind == REFLECT_BASE)
{
/* We don't track location_t of the base specifiers, so at least
for now use location_t of the base parent (i.e. the derived
class). */
tree c = r;
while (BINFO_INHERITANCE_CHAIN (c))
c = BINFO_INHERITANCE_CHAIN (c);
r = BINFO_TYPE (c);
}
if (OVERLOAD_TYPE_P (r) || (TYPE_P (r) && typedef_variant_p (r)))
rloc = DECL_SOURCE_LOCATION (TYPE_NAME (r));
else if (DECL_P (r) && r != global_namespace)
rloc = DECL_SOURCE_LOCATION (r);
tree decl = NULL_TREE, field = NULL_TREE;
if (rloc != UNKNOWN_LOCATION)
{
/* Make sure __builtin_source_location (which depends on
std::source_location::__impl) will work without errors. */
tree name = get_identifier ("__impl");
decl = lookup_qualified_name (std_source_location, name);
if (TREE_CODE (decl) != TYPE_DECL)
decl = NULL_TREE;
else
{
name = get_identifier ("__builtin_source_location");
decl = lookup_qualified_name (global_namespace, name);
if (TREE_CODE (decl) != FUNCTION_DECL
|| !fndecl_built_in_p (decl, BUILT_IN_FRONTEND)
|| DECL_FE_FUNCTION_CODE (decl) != CP_BUILT_IN_SOURCE_LOCATION
|| !require_deduced_type (decl, tf_warning_or_error))
decl = NULL_TREE;
}
}
if (decl)
{
field = TYPE_FIELDS (std_source_location);
field = next_aggregate_field (field);
/* Make sure std::source_location has exactly a single non-static
data member (_M_impl in libstdc++, __ptr_ in libc++) with pointer
type. Return {._M_impl = &*.Lsrc_locN}. */
if (field != NULL_TREE
&& POINTER_TYPE_P (TREE_TYPE (field))
&& !next_aggregate_field (DECL_CHAIN (field)))
{
tree call = build_call_nary (TREE_TYPE (TREE_TYPE (decl)), decl, 0);
SET_EXPR_LOCATION (call, rloc);
call = fold_builtin_source_location (call);
return build_constructor_single (std_source_location, field, call);
}
}
return build_constructor (std_source_location, nullptr);
}
/* Process std::meta::is_variable.
Returns: true if r represents a variable. Otherwise, false. */
@@ -2591,6 +2521,78 @@ eval_type_of (location_t loc, const constexpr_ctx *ctx, tree r,
return get_reflection_raw (loc, type_of (r, kind));
}
/* Process std::meta::source_location_of.
Returns: If r represents a value, a type other than a class type or an
enumeration type, the global namespace, or a data member description,
then source_location{}. Otherwise, an implementation-defined
source_location value. */
static tree
eval_source_location_of (location_t loc, tree r, reflect_kind kind,
tree std_source_location)
{
if (!NON_UNION_CLASS_TYPE_P (std_source_location))
{
error_at (loc, "%qT is not a class type", std_source_location);
return error_mark_node;
}
location_t rloc = UNKNOWN_LOCATION;
if (kind == REFLECT_BASE)
{
/* We don't track location_t of the base specifiers, so at least
for now use location_t of the base parent (i.e. the derived
class). */
tree c = r;
while (BINFO_INHERITANCE_CHAIN (c))
c = BINFO_INHERITANCE_CHAIN (c);
r = BINFO_TYPE (c);
}
if (OVERLOAD_TYPE_P (r) || (TYPE_P (r) && typedef_variant_p (r)))
rloc = DECL_SOURCE_LOCATION (TYPE_NAME (r));
else if (DECL_P (r) && r != global_namespace)
rloc = DECL_SOURCE_LOCATION (r);
else if (eval_is_annotation (r) == boolean_true_node)
rloc = EXPR_LOCATION (TREE_VALUE (TREE_VALUE (r)));
tree decl = NULL_TREE, field = NULL_TREE;
if (rloc != UNKNOWN_LOCATION)
{
/* Make sure __builtin_source_location (which depends on
std::source_location::__impl) will work without errors. */
tree name = get_identifier ("__impl");
decl = lookup_qualified_name (std_source_location, name);
if (TREE_CODE (decl) != TYPE_DECL)
decl = NULL_TREE;
else
{
name = get_identifier ("__builtin_source_location");
decl = lookup_qualified_name (global_namespace, name);
if (TREE_CODE (decl) != FUNCTION_DECL
|| !fndecl_built_in_p (decl, BUILT_IN_FRONTEND)
|| DECL_FE_FUNCTION_CODE (decl) != CP_BUILT_IN_SOURCE_LOCATION
|| !require_deduced_type (decl, tf_warning_or_error))
decl = NULL_TREE;
}
}
if (decl)
{
field = TYPE_FIELDS (std_source_location);
field = next_aggregate_field (field);
/* Make sure std::source_location has exactly a single non-static
data member (_M_impl in libstdc++, __ptr_ in libc++) with pointer
type. Return {._M_impl = &*.Lsrc_locN}. */
if (field != NULL_TREE
&& POINTER_TYPE_P (TREE_TYPE (field))
&& !next_aggregate_field (DECL_CHAIN (field)))
{
tree call = build_call_nary (TREE_TYPE (TREE_TYPE (decl)), decl, 0);
SET_EXPR_LOCATION (call, rloc);
call = fold_builtin_source_location (call);
return build_constructor_single (std_source_location, field, call);
}
}
return build_constructor (std_source_location, nullptr);
}
/* If R is (const T &) &foo, get foo. */
static tree
@@ -2665,7 +2667,7 @@ eval_constant_of (location_t loc, const constexpr_ctx *ctx, tree r,
tree fun)
{
if (eval_is_annotation (r) == boolean_true_node)
r = TREE_VALUE (TREE_VALUE (r));
r = tree_strip_any_location_wrapper (TREE_VALUE (TREE_VALUE (r)));
else if (!check_splice_expr (loc, UNKNOWN_LOCATION, r,
/*address_p=*/false,
/*member_access_p=*/false,
@@ -3590,7 +3592,8 @@ eval_display_string_of (location_t loc, const constexpr_ctx *ctx, tree r,
TREE_VEC_ELT (r, 4) == boolean_true_node
? "true" : "false");
else if (eval_is_annotation (r) == boolean_true_node)
pp_printf (&pp, "[[=%E]]", TREE_VALUE (TREE_VALUE (r)));
pp_printf (&pp, "[[=%E]]",
tree_strip_any_location_wrapper (TREE_VALUE (TREE_VALUE (r))));
else
pp_string (&pp, "<unsupported reflection>");
#if __GNUC__ >= 10

View File

@@ -5941,9 +5941,13 @@ handle_annotation_attribute (tree *node, tree ARG_UNUSED (name),
}
if (!processing_template_decl)
{
location_t loc = EXPR_LOCATION (TREE_VALUE (args));
TREE_VALUE (args) = cxx_constant_value (TREE_VALUE (args));
if (TREE_VALUE (args) == error_mark_node)
*no_add_attrs = true;
auto suppression
= make_temp_override (suppress_location_wrappers, 0);
TREE_VALUE (args) = maybe_wrap_with_location (TREE_VALUE (args), loc);
}
ATTR_UNIQUE_VALUE_P (args) = 1;
return NULL_TREE;

View File

@@ -160,3 +160,15 @@ static_assert (annotations_of (bases_of (^^M <J, K, L>, ctx)[4]).size () == 3);
static_assert (type_of (annotations_of (bases_of (^^M <J, K, L>, ctx)[4])[0]) == ^^unsigned);
static_assert (type_of (annotations_of (bases_of (^^M <J, K, L>, ctx)[4])[1]) == ^^const V);
static_assert (type_of (annotations_of (bases_of (^^M <J, K, L>, ctx)[4])[2]) == ^^long long);
template <auto ...V>
consteval auto
qux ()
{
[[=1, =V..., =2]] int an;
return ^^an;
}
static_assert (annotations_of (qux <> ()).size () == 2);
static_assert (annotations_of (qux <3, 4, 5> ()).size () == 5);
static_assert (annotations_of (qux <V { 1, 2, 3 }, V { 2, 3, 4 }> ()).size () == 4);

View File

@@ -69,7 +69,8 @@ struct [[=E ()]] I // { dg-error "annotation does not have structural type" }
[[=E ()]] int I::i4 = 0; // { dg-error "annotation does not have structural type" }
struct J : [[=E ()]] C {}; // { dg-error "annotation does not have structural type" }
template <typename T>
concept K [[=E ()]] = requires { true; }; // { dg-error "annotation does not have structural type" }
concept K [[=E ()]] = requires { true; }; // { dg-error "annotation does not have structural type" "" { xfail *-*-* } }
constexpr bool k = K <int>;
typedef int L [[=E ()]]; // { dg-error "annotation does not have structural type" }
template <typename T>
struct M {};

View File

@@ -32,17 +32,30 @@ static_assert (source_location_of (dealias (^^I)).line () == std::source_locatio
typedef S J;
static_assert (source_location_of (dealias (^^J)).line () == source_location_of (^^S).line ());
static_assert (source_location_of (data_member_spec (^^int, { .name = "_" })).line () == std::source_location ().line ());
constexpr auto ctx = access_context::current ();
struct V {};
struct W : public S, virtual V {};
static_assert (source_location_of (bases_of (^^W, access_context::current ())[0]).line () == std::source_location::current ().line () - 1);
static_assert (source_location_of (bases_of (^^W, access_context::current ())[1]).line () == std::source_location::current ().line () - 2);
struct Y { int a, b, c; };
struct W : [[=1]] public S, [[=2, =Y { 42, 42, 42 }]] virtual V {};
static_assert (source_location_of (bases_of (^^W, ctx)[0]).line () == std::source_location::current ().line () - 1);
static_assert (source_location_of (bases_of (^^W, ctx)[1]).line () == std::source_location::current ().line () - 2);
static_assert (source_location_of (annotations_of (bases_of (^^W, ctx)[0])[0]).line () == std::source_location::current ().line () - 3);
static_assert (source_location_of (annotations_of (bases_of (^^W, ctx)[1])[1]).line () == std::source_location::current ().line () - 4);
struct X : public S,
public V
[[=3]] public V
{
};
static_assert (source_location_of (bases_of (^^X, access_context::current ())[0]).line () == std::source_location::current ().line () - 4);
static_assert (source_location_of (bases_of (^^X, ctx)[0]).line () == std::source_location::current ().line () - 4);
// TODO: we don't track location of base specifiers, so just location of the derived class definition is used.
static_assert (source_location_of (bases_of (^^X, access_context::current ())[1]).line () == std::source_location::current ().line () - 6);
static_assert (source_location_of (bases_of (^^X, ctx)[1]).line () == std::source_location::current ().line () - 6);
static_assert (source_location_of (annotations_of (bases_of (^^X, ctx)[1])[0]).line () == std::source_location::current ().line () - 6);
[[=1]]
[[=2]] extern int an;
[[=Y { 1, 2, 3 }]] extern int an;
[[=3.0f + 4.0f]] int an;
static_assert (source_location_of (annotations_of (^^an)[0]).line () == std::source_location::current ().line () - 4);
static_assert (source_location_of (annotations_of (^^an)[1]).line () == std::source_location::current ().line () - 4);
static_assert (source_location_of (annotations_of (^^an)[2]).line () == std::source_location::current ().line () - 4);
static_assert (source_location_of (annotations_of (^^an)[3]).line () == std::source_location::current ().line () - 4);
#include <string_view>
@@ -77,3 +90,24 @@ bar (int a, int b)
static_assert (std::string_view (source_location_of (d).function_name ())
== std::string_view (source_location_of (^^a).function_name ()));
}
template <int N>
void
qux ()
{
[[=1]]
[[=N + 42]]
[[=Y { N, N + 1, N + 2 }]]
[[=Y { 1, 2, 3 }]] int an;
static_assert (N && source_location_of (annotations_of (^^an)[0]).line () == std::source_location::current ().line () - 4);
static_assert (N && source_location_of (annotations_of (^^an)[1]).line () == std::source_location::current ().line () - 4);
// static_assert (N && source_location_of (annotations_of (^^an)[2]).line () == std::source_location::current ().line () - 4);
// static_assert (N && source_location_of (annotations_of (^^an)[3]).line () == std::source_location::current ().line () - 4);
}
void
plugh ()
{
qux <1> ();
qux <2> ();
}