Re: [PATCH gcc/* 2/2] gcc: stop using 'int' to represent sets of qualifiers

Jason Merrill <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
On 8/18/26 7:15 AM, Arsen Arsenović wrote:
> Jason Merrill <[email protected]> writes:
> 
>>> -      && TYPE_QUALS (element_type))
>>> +      && TYPE_QUALS (element_type) != qualifier_set {})
>>
>> And how about an explicit operator bool so we don't need these changes?
> 
> I seem to recall that adding an implicit conversion operator would've
> changed the meaning of some existing code, but I am not 100% confident
> that my memory is right.  (That certainly happened when I added an
> operator& as a shorthand for 'has'; I may be confusing those two)
> 
> I'll see if I can find any issue with adding an implicit conversion
> operator.
> 
> Adding an explicit one would just mean needing to wrap these places in
> 'bool (...)'.

It actually wouldn't; in boolean contexts like conditions and && the 
expression is "contextually converted to bool" 
(https://eel.is/c++draft/conv.general#4), which uses an explicit 
conversion operator.

> Or, we could settle on using 'nonempty_p' for all these cases.
> 
>>> -    unqual_elt = c_build_qualified_type (elt, KEEP_QUAL_ADDR_SPACE (quals));
>>> +    unqual_elt = c_build_qualified_type (elt,
>>> +					 quals.without (TYPE_QUAL_ALL));
>> The quals.without (TYPE_QUAL_ALL) pattern seems awkward, especially given the
>> semi-ambiguity of "ALL".  How about a without_cv member function?
> 
> Yes, "ALL" is ambiguous.  I couldn't think of a better term.
> 
> This only occurred once, which is why I didn't add 'without_cv'.
> 
> But, I can add it.

It seems useful; wanting the cv-unqualified type comes up a lot.

>>> +  /* Construct an empty qualifier set with the generic address space.  Such a
>>> +     qualifier set corresponds to unqualified types.  */
>>> +  qualifier_set () = default;
>>> +  static_assert (cv_qualifier {} == TYPE_UNQUALIFIED
>>> +		 && addr_space_t {} == ADDR_SPACE_GENERIC,
>>> +		 "We want the trivial default constructor to use those vals");
>>
>> Is there a reason we need the default constructor to be trivial rather than
>> explicitly setting those values?  If so that should be documented in the
>> comment.
> 
> I did that originally because of the unions inside c_declarator and
> cp_declarator.
> 
> Those get used in both FEs as well as libcc1 and initialized via memset.
> 
> That could all be fixed up, but it ended up being an additional large
> change when I tried to do so.

Fair enough; please add it to the comment, then.

>>> +  /* Returns true if qualifiers in SUBSET can be replaced with qualifiers in
>>> +     THIS safely.
>>> +
>>> +     In general, this means that an object qualified per SUBSET can be used as
>>> +     if it was qualified per this qualifier set (e.g. 'T' as 'const T', or
>>> +     'const T' as 'const volatile AS1 T', presuming that AS1 is a superset of
>>> +     the generic address space).
>>> +
>>> +     If NOP_ONLY, return 'true' iff a pointer with a pointee qualified via
>>> +     SUBSET can be converted into a pointer with a pointee qualified via THIS
>>> +     (i.e. if a NOP_EXPR conversion would be valid).  In particular, this means
>>> +     address space mismatches are forbidden.  */
>>> +  bool can_qualify (qualifier_set subset, bool nop_only = false) const;
>>
>> This name and the first paragraph are confusing.  What does "safely" mean?  If
>> it means there's a C++ standard conversion, let's say that.
> 
> That's what the second paragraph expands on.
> 
> I didn't refer to standard conversions because a single qualifier set
> alone doesn't suffice to decide whether a conversion is allowed (whether
> 'T * cv *' can be converted to 'const T * cv *' depends on 'cv' rather
> than just the qualification of the innermost T).

Sure, but doesn't that same argument apply to the second paragraph as it 
is?  In cases where there isn't a standard conversion, it's specifically 
because such a conversion would not be safe.

And we're only dealing with one set of qualifiers here, we could frame 
it in terms of whether cv1 int* can convert to cv2 int* without 
referring to an arbitrary type T.

>> How about "superset_of"?
> 
> Hmm, a superset does not necessarily mean that THIS can be used to
> qualify an object previously qualified as SUBSET (particularly when
> NOP_ONLY, or when the sets differ by _Atomic qualification), so it has
> different semantics.

And yet you call the parameter "SUBSET" :)

Maybe "compatible_with" (in a sense similar to C++ reference-compatible)?

>> Also "nop_only" seems like it should be called pointee or
>> (not_)toplevel or some such that describes the semantics rather than
>> the representation.
> 
> Yeah, 'pointee' could work.
> 
> The semantics that I was going for is that "if not NOP_ONLY, then
> replacing SUBSET with *this might require some work other than
> reinterpretation; if NOP_ONLY, then reinterpretation is safe" which is,
> indeed, phrased in terms of representation.
> 
> I can't think of any case other than pointee types where the compiler
> can't insert the correct conversion sequence (and where it, thus,
> requires reinterpretation), so that name may be okay.
> 
>>> +/* Documented next to declaration in tree.h.  */
>>
>> In GCC we generally document by the definition.
> 
> Yes, but I thought it'd be awkward to outline the documentation for just
> two members (given that the rest are just inlined in the struct body),
> so I figured that leaving the documentation next to the declaration and
> a note here would work.
> 
> No strong feeling either way, I can move the documentation above the
> definition, too.

Please; it's easy for me to jump to the definition of a symbol, but to 
find the declaration I need to do a text search.

Jason
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.