[cfe-dev] Preliminary reinterpret_cast Sema patch

Doug Gregor doug.gregor at gmail.com
Tue Oct 21 09:08:24 PDT 2008


Hi Sebastian,

On Tue, Oct 21, 2008 at 8:25 AM, Sebastian Redl
<sebastian.redl at getdesigned.at> wrote:
> Doug Gregor wrote:
>> On Sun, Oct 19, 2008 at 7:26 AM, Sebastian Redl
>> <sebastian.redl at getdesigned.at> wrote:
>>> +char ***good_const_cast_test(ccvpcvpp var)
>>> +{
>>> +  char ***var2 = const_cast<cppp>(var);
>>> +  char ***const &var3 = static_cast<cpppcr>(var2); // Different bug.
>>> +  char ***&var4 = const_cast<cpppr>(var3);
>>> +  char *** var5 = const_cast<cppp>(var4);
>>>
>>
>> Did you really mean to use var2, var3, and var4 as the expressions in
>> these const_casts? The tests aren't actually casting away any
>> constness here, so I suspect that you want to use "var" as the
>> argument to the const_casts throughout.
>>
>
> No, I mean it exactly like it's there. var3->var4 does cast away constness
> (const& -> &). var2->var3 should be implicit, but that fails, so I made it a
> static_cast. var4->var5 tests the dropping of the reference, not casting
> away constness.

Ah, okay. The initialization of "var3" probably isn't checking what we
want it to check, because the semantics of reference binding aren't
implemented. Still, we're definitely checking *something*, so let's
leave it as is.

>> Also, why create variables like var3 and var4? You can typically
>> express any "this expression has no effect"-type warnings (if that was
>> your intent) by writing, e.g.,
>>
>>  (void)const_cast<cpppr>(var3);
>>
>
> I suppose, for those cases where I don't use the variable.

Now that I'm unconfused about the uses of var2, var3, etc., this
suggestion of mine doesn't matter so much.

> These test array decay and that a function pointer pointer is not
> misidentified as a function pointer, not constness. I like my unit tests to
> test just one thing.

Okay, good.

>>> +/// CheckConstCast - Check that a const_cast<DestType>(SrcExpr) is
>>>
>>
>> The < should be a '<' here.
>>
>
> Doxygen doesn't mind? I know that JavaDoc can get confused.

Ah, right. The right escaping is '\<', according to
http://www.stack.nl/~dimitri/doxygen/commands.html#cmdat

and you'll also need to escape the > with '\>'.

>> Also, I suggest moving the C++ standard quotes down into the body of
>> the function, where the code actually implements that rule. The
>> comment for CheckConstCast itself should point to the section in the
>> standard where const_cast is defined, and give a small example of how
>> const_cast is used.
>>
>
> I like having the entire relevant standard parts summarized at the start of
> the function, but I agree that it results in quite a bit of info
> duplication, because I repeat some of it at the places I actually implement
> it.

I guess it depends on how we read the comments... personally, I want
to get a rough idea of what the function is doing when I read the
function-level comment, then when I'm browsing the code I want to see
the correspondence between the standard and the code.

>>> +  const PointerLikeType *SrcTypeTmp, *DestTypeTmp;
>>>
>>
>> Okay, I know I'm being pretty picky, but I'd really prefer if
>> SrcTypeTmp and DestTypeTmp were only declared locally within the
>> blocks where they are used,
>
> Usually, I do too, but I already had them in this scope because of the loop
> later, and I thought it ugly to have them again, so I just moved the
> declarations up here.

Hrm, yeah. I guess what I don't like here is the use of
PointerLikeType (which, in truth, I don't really like anyway) as a
ReferenceType in some places and as a PointerType in other places.

>>> +    if ((SrcTypeTmp = SrcType->getAsReferenceType()))
>>> +      SrcType = SrcTypeTmp->getPointeeType();
>>>
>>
>> This shouldn't happen, because an expression never has reference type.
>>
>
> Really?

Yeah, check out C++ 5p5, where it talks about the adjustments to the
type of an expression. This is on my short list to fix in Clang.

> What type does "static_cast<int&>(i)" have?

The type is "int", and it's an lvalue.

> What about simply "i",
> where i is declared as "int &i = ...;"? Do they have type int, with lvalue
> possibility?

Yes, exactly.

>>> +  while (SrcType != DestType &&
>>> +    (SrcTypeTmp = SrcType->getAsPointerType()) &&
>>> +    (DestTypeTmp = DestType->getAsPointerType()))
>>>
>>
>> Please put SrcTypeTmp and DestTypeTmp (and their initializations)
>> inside the body of the while loop.
>>
>
> Can't. The loop header also checks if the result of the assignment is null.
> I'd have to write something like:
> while(SrcType != DestType) {
>  const PointerType *SrcTypeTmp = SrcType->getAsPointerType();
>  const PointerType *DestTypeTmp = DestType->getAsPointerType();
>  if(!SrcTypeTmp || !DestTypeTmp) {
>   break;
>  }
>  // ...
> }
> and I actually think my current code is nicer. (Matter of opinion, of
> course. I'll change it if you insist.)

I don't insist.

>>> +  {
>>> +    SrcType = Context.getCanonicalType(SrcTypeTmp->getPointeeType());
>>> +    DestType = Context.getCanonicalType(DestTypeTmp->getPointeeType());
>>> +  }
>>>
>>
>> I'm very surprised to see that you aren't calling getUnqualifiedType
>> inside this loop. Calling getUnqualifiedType on each of SrcType and
>> DestType each time through would allow us to short-circuit the
>> comparison if we're casting, e.g., T1 * * * const * * * to T1 * * * *
>> * *; right now, it'll go through the loop all the way down until it
>> compares the T1's.
>>
>
> I don't see how. It costs us at most one loop iteration more. T1***const* is
> a different type from T1****. T1***const is a different type from T1***
> (that's the wasted iteration). T1** is the same type as T1**.
> The change makes sense, but I don't think it's as bad as you think.

You're right; it's not as bad as I thought.

>>
>> Also: is this going to allow us to const_cast between different
>> address spaces? I'm not sure what the right answer is there.
>>
>
> I don't even know what you mean by different address spaces, or how they are
> implemented. Are you talking about x86-16-type segmented memory or something
> even more extreme?

We can ignore them for now with a FIXME. Address spaces are a C
extension for embedded platforms, which are represented by ASQualType
in Clang.

I *think* that const_cast should not permit one to change the address
space, but my understanding of address spaces is too fuzzy to be sure.

>>  I'm also inclined to diagnose this as an
>> error, unless our reading proves faulty.
>>
>
> Or at least emit a warning or something. I wonder if any real-world code
> actually relies on this quirk of the compilers.

If one compiler gets it wrong, there is code somewhere that depends on
that behavior. It's a corollary to Murphy's Law for compilers :)

Still, if the code is ill-formed we should diagnose it up until the
point where users complain too loudly to ignore.

  - Doug



More information about the cfe-dev mailing list