[cfe-dev] Preliminary reinterpret_cast Sema patch
Sebastian Redl
sebastian.redl at getdesigned.at
Tue Oct 21 08:25:50 PDT 2008
Doug Gregor wrote:
> Hi Sebastian,
>
> Thanks for the update; comments follow.
>
> 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.
> 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.
>> + const int ar[100] = {0};
>> + int (&rar)[100] = const_cast<iarr>(ar);
>> + int *pi = const_cast<int*>(ar); // Array decay?
>> + f fp = 0;
>> + f *fpp = const_cast<f*>(&fp);
>>
>
> There doesn't seem to be any "const" involved here. I suggest adding, e.g.,
>
> f const * fp2 = 0;
> f* fpp2 = const_cast<f*>(fp2);
>
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.
> Note that these expected-errors don't match the actual error messages
> we get, so -verify fails.
>
Yeah, I changed the messages in the last reinterpret_cast update and
forgot to update the test case.
> Please put a "// C++ casts" before all of the C++ cast-specific diagnostics.
>
OK.
>> +/// 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.
> A couple comments about the comments... please put "C++" prior to the
> section/paragraph you are citing and use the "p" instead of the "/",
> e.g., "C++ 5.2.11p4", so that the specification reference script can
> pick up the references.
>
OK.
> 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.
>
>> +void
>> +Sema::CheckConstCast(SourceLocation OpLoc, Expr *&SrcExpr, QualType
>> DestType)
>> +{
>> + QualType OrigDestType = DestType, OrigSrcType = SrcExpr->getType();
>> +
>> + DestType = Context.getCanonicalType(DestType);
>> + QualType SrcType = SrcExpr->getType();
>> + 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.
>> + if (SrcExpr->isLvalue(Context) != Expr::LV_Valid) {
>> + // Cannot cast non-lvalue to reference type.
>> + Diag(OpLoc, diag::err_bad_cxx_cast_rvalue,
>> + "const_cast", OrigDestType.getAsString());
>> + return;
>> + }
>> +
>> + // /4: "if a pointer to T can be [cast] to the type pointer to T2"
>>
>
> Here's a case where citing all of p4 (and using the full spec
> reference, [C++ 5.2.10p4)
>
As I said, duplication.
>
>> + if ((SrcTypeTmp = SrcType->getAsReferenceType()))
>> + SrcType = SrcTypeTmp->getPointeeType();
>>
>
> This shouldn't happen, because an expression never has reference type.
>
Really? What type does "static_cast<int&>(i)" have? What about simply
"i", where i is declared as "int &i = ...;"? Do they have type int, with
lvalue possibility?
> Here's another case where citing the relevant part of p1 would help the reader.
>
OK.
> Please note in the comment here that if DestType was a reference type
> originally, it is now a pointer type.
>
OK.
>> + }
>> + if (DestType->isFunctionPointerType()) {
>> + // Cannot cast direct function pointers.
>> + Diag(OpLoc, diag::err_bad_const_cast_dest, OrigDestType.getAsString());
>> + return;
>> + }
>> + SrcType = Context.getCanonicalType(SrcType);
>> +
>> + // Unwrap the pointers. Ignore qualifiers. Terminate early if the types
>> are
>> + // completely equal.
>> + 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.)
>
>> + {
>> + 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.
> 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?
> Finally, once SrcType and DestType are both canonical types (which is
> guaranteed before we even enter the loop), all of the types we get via
> getPointeeType will already be canonical. So, you don't need to call
> getCanonicalType in the loop.
>
I wasn't aware of this guarantee - in fact, I was somehow under the
impression that getCanonicalType only canonicalized the first level.
Can't remember why I thought that, but I know that I very deliberately
added these additional calls. I'll try it without them.
>
>> + // If we end up with constant arrays of equal size, unwrap those too. A
>> cast
>> + // from const int [N] to int (&)[N] is invalid by my reading of the
>> + // standard, but g++ accepts it even with -ansi -pedantic.
>> + // No more than one level, though, so don't embed this in the unwrap loop
>> + // above.
>>
>
> How interesting... EDG also accepts this const_cast, but the standard
> says absolutely nothing about being able to const_cast away qualifiers
> on the element types of arrays. I'm curious whether MSVC diagnoses
> this as an error or not.
I can try it tomorrow.
> 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.
> Thanks again for this patch; it's in very good shape already. I'll
> follow up with a review of the reinterpret_cast implementation later.
>
Thank you for the review.
Sebastian
More information about the cfe-dev
mailing list