On Fri, 6 Feb 2026, Jakub Jelinek wrote:

> Hi!
> 
> I had to revert my r16-7102 patch
> https://gcc.gnu.org/pipermail/gcc-patches/2026-January/706424.html
> (first patch in
> https://gcc.gnu.org/pipermail/gcc-patches/2025-December/704097.html )
> in r16-7328 because it regressed the testcase added in r16-7331 for PR123882.
> typedef int T;
> void foo (unsigned long, T[]);
> void foo (unsigned long x, T[restrict x]);
> The ICE was on the array_as_string hack which used qualifiers on
> ARRAY_TYPE for printing and then FE called get_aka_type and errored
> that restrict is not allowed on ARRAY_TYPEs.
> Anyway, guess that would be fixable with further hacks, but when looking
> into that, I've noticed my approach was completely broken, my assumption
> that TYPE_CANONICAL (volatile int) is int is not true, TYPE_CANONICAL
> (volatile int) is itself.  For volatile int[2], C/C++ pushes the cv quals
> to the element type, so it is ARRAY_TYPE of volatile int, and this
> modified ARRAY_TYPE is made a type variant of int[2], but its TYPE_CANONICAL
> is again volatile int[2].
> Furthermore, a lot of places including build_type_attribute_variant call
> build_type_attribute_qual_variant like:
>   return build_type_attribute_qual_variant (ttype, attribute,
>                                           TYPE_QUALS (ttype));
> so pass the quals of the type to it.  If build_type_attribute_qual_variant
> for ARRAY_TYPEs behaves differently between C/C++ (in that case it
> pushes quals to the element type) and other languages, then this will
> do unexpected changes, namely because the ARRAY_TYPE usually (except for
> array_as_string hack) don't contain cv quals,
> build_type_attribute_variant (volatile int[2], may_alias,
>                             TYPE_QUALS (volatile int[2]))
> would create int [2] with may_alias attribute for C/C++.
> 
> Now, the ICE on the testcases was in 2 spots, in get_alias_check
> gcc_checking_assert and in verify_type -fchecking stuff, the assumption
> was that
> TYPE_MAIN_VARIANT (TYPE_CANONICAL (TYPE_MAIN_VARIANT (type)))
> == TYPE_CANONICAL (TYPE_MAIN_VARIANT (type))
> for any type where TYPE_CANONICAL is non-NULL.  The reason why
> this works e.g. for the C/C++ volatile int[2] is that the ARRAY_TYPE
> is variant type of int[2], so the first TYPE_MAIN_VARIANT gets us
> int[2] and its TYPE_CANONICAL is int[2] which is the main variant.
> Now, if TYPE_CANONICAL (volatile int) needs to be itself (but sure with
> typedef volatile int V; TYPE_CANONICAL (V) is volatile int) and I Richi
> tried to change that and it broke everything, my change was wrong and
> we really need
> TYPE_CANONICAL (volatile int[2] __attribute__((may_alias))) to be
> volatile int[2] and we create the attribute variants as distinct types,
> using build_distinct_type_copy, so
> TYPE_MAIN_VARIANT (volatile int[2] __attribute__((may_alias)))
> is itself, then the alias/tree assumption that the
> TYPE_MAIN_VARIANT (TYPE_CANONICAL (TYPE_MAIN_VARIANT (type)))
> == TYPE_CANONICAL (TYPE_MAIN_VARIANT (type))
> can't hold.  We need one further hop, so just guarantee that
> TYPE_CANONICAL (TYPE_MAIN_VARIANT (TYPE_CANONICAL (TYPE_MAIN_VARIANT (type))))
> == TYPE_MAIN_VARIANT (TYPE_CANONICAL (TYPE_MAIN_VARIANT (type))).
> 
> The following patch changes get_alias_set and tree.cc to verify that
> instead.  For get_alias_set the checking checks more than before,
> in particular that for both steps TYPE_CANONICAL is its own TYPE_CANONICAL
> rather than just that !TYPE_STRUCTURAL_EQUALITY_P (i.e. TYPE_CANONICAL
> is non-NULL).  verify_type already checks that though.
> 
> I've tried even
> typedef volatile int A[1] __attribute__((may_alias));
> typedef A __attribute__((btf_type_tag ("foo"))) B;
> B c;
> i.e. cascading more than one type attribute on the ARRAY_TYPE and it
> still works, TYPE_CANONICAL (A) will be volatile int A[1] without
> attribute and TYPE_CANONICAL (B) the same.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> 
> 2026-02-06  Jakub Jelinek  <[email protected]>
> 
>       PR c/101312
>       * alias.cc (get_alias_set): Allow TYPE_CANONICAL (mv) to be
>       not its own TYPE_MAIN_VARIANT, as long as its TYPE_MAIN_VARIANT
>       has TYPE_CANONICAL equal to itself.
>       * tree.cc (verify_type): Likewise.
> 
>       * gcc.dg/pr101312-1.c: New test.
>       * gcc.dg/pr101312-2.c: New test.
> 
> --- gcc/alias.cc.jj   2026-02-05 15:34:14.545990112 +0100
> +++ gcc/alias.cc      2026-02-05 15:54:32.762394480 +0100
> @@ -948,7 +948,12 @@ get_alias_set (tree t)
>    else
>      {
>        t = TYPE_CANONICAL (t);
> -      gcc_checking_assert (!TYPE_STRUCTURAL_EQUALITY_P (t));
> +      gcc_checking_assert (TYPE_CANONICAL (t) == t);
> +      if (t != TYPE_MAIN_VARIANT (t))
> +     {
> +       t = TYPE_MAIN_VARIANT (t);

We already did

  /* Variant qualifiers don't affect the alias set, so get the main
     variant.  */
  t = TYPE_MAIN_VARIANT (t);

at the start.  I'd rather not do this, is this really necessary?
Instead I hoped that at this point (t being a canonical type)

  TYPE_CANONICAL (TYPE_MAIN_VARIANT (t)) == t

so we do never have a complex web with many different canonical
types within the same variant chain (and TYPE_CANONICAL does
not get us to a type that's not in the types variant chain).

So ...

> +       gcc_checking_assert (TYPE_CANONICAL (t) == t);

... just

  +       gcc_checking_assert (TYPE_CANONICAL (TYPE_MAIN_VARIANT (t)) == 
t);

?

> +     }
>      }
>  
>    /* If this is a type with a known alias set, return it.  */
> --- gcc/tree.cc.jj    2026-02-05 15:34:14.546990095 +0100
> +++ gcc/tree.cc       2026-02-05 15:53:47.603157855 +0100
> @@ -14477,10 +14477,25 @@ verify_type (const_tree t)
>      }
>    if (TYPE_MAIN_VARIANT (t) == t && ct && TYPE_MAIN_VARIANT (ct) != ct)
>     {
> -      error ("%<TYPE_CANONICAL%> of main variant is not main variant");
> -      debug_tree (ct);
> -      debug_tree (TYPE_MAIN_VARIANT (ct));
> -      error_found = true;
> +     /* This can happen when build_type_attribute_variant is called on
> +     C/C++ arrays of qualified types.  volatile int[2] is unqualified
> +     ARRAY_TYPE with volatile int element type.
> +     TYPE_CANONICAL (volatile int) is itself and so is
> +     TYPE_CANONICAL (volatile int[2]).  build_type_attribute_qual_variant
> +     creates a distinct type copy (so TYPE_MAIN_VARIANT is itself) and sets
> +     its TYPE_CANONICAL to the unqualified ARRAY_TYPE (so volatile int[2]).
> +     But this is not the TYPE_MAIN_VARIANT, which is int[2].  So, just
> +     verify that TYPE_MAIN_VARIANT (ct) is already the final type we
> +     need.  */
> +      tree mvc = TYPE_MAIN_VARIANT (ct);
> +      if (TYPE_CANONICAL (mvc) != mvc)

But I'm a bit confused now.  So we have const int * being its own
canonical type and a variant of int *.  All fine.  But the canonical
type of  'int *' (a main variant) can now be, say 'const int *'?

So to get at the canonical type for alias purposes we need to do
TYPE_MAIN_VARIANT (TYPE_CANONICAL (TYPE_MAIN_VARIANT (...))) now?
Meaning, we eventually created wrong-code?

But what we now test above is TYPE_CANONICAL (TYPE_MAIN_VARIANT 
(TYPE_CANONICAL (t))) == TYPE_MAIN_VARIANT (TYPE_CANONICAL (t)).

That means that get_alias should swap TYPE_MAIN_VARIANT and
TYPE_CANONICAL punning?  What happens whe TYPE_STRUCTURAL_EQUALITY (t)?
Can we assert that TYPE_STRUCRTURAL_EQUALITY (TYPE_MAIN_VARIANT (t))?

IIRC when the middle-end re-computes TYPE_CANONICAL it does so only
for main variants (and we only stream the TYPE_MAIN_VARIANT pointers,
re-building variant chains at stream-in).  lto/lto-common.cc has

  /* Canonical types are same among all complete variants.  */
  if (TYPE_CANONICAL (TYPE_MAIN_VARIANT (t)))
    TYPE_CANONICAL (t) = TYPE_CANONICAL (TYPE_MAIN_VARIANT (t));
  else
    {
      hashval_t h = hash_canonical_type (TYPE_MAIN_VARIANT (t));
      gimple_register_canonical_type_1 (TYPE_MAIN_VARIANT (t), h);
      TYPE_CANONICAL (t) = TYPE_CANONICAL (TYPE_MAIN_VARIANT (t));

I guess with the old scheme the order of TYPE_CANONICAL/TYPE_MAIN_VARIANT
punning doesn't matter, nor with how LTO re-computes it.  But with
the C/C++ way it would matter unless you iterate one more time.

There is tree-ssa-alias.cc:same_type_for_tbaa which probably needs
updating for this as well (but it's only a missed optimization there
I guess).  Possibly similarly types_equal_for_same_type_for_tbaa_p.

Richard.

> +     {
> +       error ("main variant of %<TYPE_CANONICAL%> of main variant is not"
> +              " its own %<TYPE_CANONICAL%>");
> +       debug_tree (ct);
> +       debug_tree (TYPE_MAIN_VARIANT (ct));
> +       error_found = true;
> +     }
>     }
>  
>  
> --- gcc/testsuite/gcc.dg/pr101312-1.c.jj      2026-02-05 15:59:20.154537761 
> +0100
> +++ gcc/testsuite/gcc.dg/pr101312-1.c 2026-01-28 09:50:17.240497398 +0100
> @@ -0,0 +1,4 @@
> +/* PR c/101312 */
> +/* { dg-do compile } */
> +
> +volatile int a[1] __attribute__((may_alias));
> --- gcc/testsuite/gcc.dg/pr101312-2.c.jj      2026-02-05 15:59:23.324484267 
> +0100
> +++ gcc/testsuite/gcc.dg/pr101312-2.c 2026-01-28 09:50:17.240587703 +0100
> @@ -0,0 +1,5 @@
> +/* PR c/101312 */
> +/* { dg-do compile } */
> +/* { dg-options "-g" } */
> +
> +volatile int a[1] __attribute__((may_alias));
> 
>       Jakub
> 
> 

-- 
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)

Reply via email to