Hi,
The attached patch adds validation to const_cast, to ensure that it only
performs casts that the standard allows. With this, const_cast is
compliant except that it does not reject pointers to (member) functions yet.
Piggybacking are a comment change where a standard reference incorrectly
says C99 instead of C++ and a correction of an assertion that contained
a tautological condition.
Next I'll take a closer look at the beast that makes
int i;
const int &ri = i;
fail to compile :-)
Sebastian
Index: test/SemaCXX/const-cast.cpp
===================================================================
--- test/SemaCXX/const-cast.cpp (revision 0)
+++ test/SemaCXX/const-cast.cpp (revision 0)
@@ -0,0 +1,36 @@
+// RUN: clang -fsyntax-only -verify %s
+
+// See if aliasing can confuse this baby.
+typedef char c;
+typedef c *cp;
+typedef cp *cpp;
+typedef cpp *cppp;
+typedef cppp &cpppr;
+typedef const cppp &cpppcr;
+typedef const char cc;
+typedef cc *ccp;
+typedef volatile ccp ccvp;
+typedef ccvp *ccvpp;
+typedef const volatile ccvpp ccvpcvp;
+typedef ccvpcvp *ccvpcvpp;
+typedef int iar[100];
+typedef iar &iarr;
+
+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);
+ const int ar[100] = {0};
+ int (&rar)[100] = const_cast<iarr>(ar);
+ return var4;
+}
+
+short *bad_const_cast_test(char const *volatile *const volatile *var)
+{
+ char **var2 = const_cast<char**>(var); // expected-error {{invalid
const_cast from 'char const *volatile *const volatile *' to incompatible type
'char **'}}
+ short ***var3 = const_cast<short***>(var); // expected-error {{invalid
const_cast from 'char const *volatile *const volatile *' to incompatible type
'short ***'}}
+ char ***&var4 = const_cast<cpppr>(&var2); // expected-error {{invalid
const_cast from rvalue to reference type 'cpppr'}}
+ char v = const_cast<char>(**var2); // expected-error {{invalid const_cast to
'char', which is not a reference, pointer-to-object, or pointer-to-data-member}}
+ return **var3;
+}
Index: include/clang/Basic/DiagnosticKinds.def
===================================================================
--- include/clang/Basic/DiagnosticKinds.def (revision 56652)
+++ include/clang/Basic/DiagnosticKinds.def (working copy)
@@ -990,6 +990,13 @@
"function-style cast to a builtin type can only take one argument")
DIAG(err_value_init_for_array_type, ERROR,
"array types cannot be value-initialized")
+DIAG(err_bad_const_cast_dest, ERROR,
+ "invalid const_cast to '%0', which is not a reference, pointer-to-object,
"
+ "or pointer-to-data-member")
+DIAG(err_bad_const_cast_rvalue, ERROR,
+ "invalid const_cast from rvalue to reference type '%0'")
+DIAG(err_bad_const_cast_generic, ERROR,
+ "invalid const_cast from '%1' to incompatible type '%0'")
// Temporary
DIAG(err_unsupported_class_constructor, ERROR,
"class constructors are not supported yet")
Index: lib/Sema/SemaExprCXX.cpp
===================================================================
--- lib/Sema/SemaExprCXX.cpp (revision 56652)
+++ lib/Sema/SemaExprCXX.cpp (working copy)
@@ -26,22 +26,105 @@
SourceLocation LParenLoc, ExprTy *E,
SourceLocation RParenLoc) {
CXXCastExpr::Opcode Op;
+ Expr *Ex = (Expr*)E;
+ QualType DestType = QualType::getFromOpaquePtr(Ty);
switch (Kind) {
default: assert(0 && "Unknown C++ cast!");
- case tok::kw_const_cast: Op = CXXCastExpr::ConstCast; break;
- case tok::kw_dynamic_cast: Op = CXXCastExpr::DynamicCast; break;
- case tok::kw_reinterpret_cast: Op = CXXCastExpr::ReinterpretCast; break;
- case tok::kw_static_cast: Op = CXXCastExpr::StaticCast; break;
+ case tok::kw_const_cast:
+ Op = CXXCastExpr::ConstCast;
+ if (!CheckConstCast(OpLoc, Ex, DestType))
+ return ExprResult(true);
+ break;
+ case tok::kw_dynamic_cast:
+ Op = CXXCastExpr::DynamicCast;
+ break;
+ case tok::kw_reinterpret_cast:
+ Op = CXXCastExpr::ReinterpretCast;
+ break;
+ case tok::kw_static_cast:
+ Op = CXXCastExpr::StaticCast;
+ break;
}
- return new CXXCastExpr(Op, QualType::getFromOpaquePtr(Ty), (Expr*)E, OpLoc);
+ return new CXXCastExpr(Op, DestType, Ex, OpLoc);
}
+/// CheckConstCast - Check that a const_cast<DestType>(SrcExpr) is valid.
+/// 5.2.11/3: Both types must be pointers (to objects, void, or data members,
+/// see 5.2.11/5) with the same level of indirection. The final pointee type
+/// must be the same. All cv ([EXT] and r?) types along the way are mutable
+/// by the cast. The result is an rvalue.
+/// 5.2.11/4: If the incoming expression denotes an lvalue, DestType can be a
+/// reference to the underlying type. The result is an lvalue.
+bool
+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;
+ if ((DestTypeTmp = DestType->getAsReferenceType())) {
+ if (SrcExpr->isLvalue(Context) != Expr::LV_Valid) {
+ // Cannot cast non-lvalue to reference type.
+ Diag(OpLoc, diag::err_bad_const_cast_rvalue, OrigDestType.getAsString());
+ return false;
+ }
+
+ // /4: "if a pointer to T can be [cast] to the type pointer to T2"
+ DestType = Context.getPointerType(DestTypeTmp->getPointeeType());
+ if ((SrcTypeTmp = SrcType->getAsReferenceType()))
+ SrcType = SrcTypeTmp->getPointeeType();
+ SrcType = Context.getPointerType(SrcType);
+ } else if (!DestType->isPointerType()) {
+ // Cannot cast to non-pointer, non-reference type.
+ Diag(OpLoc, diag::err_bad_const_cast_dest, OrigDestType.getAsString());
+ return false;
+ }
+ SrcType = Context.getCanonicalType(SrcType);
+
+ // Unwrap the pointers. Ignore qualifiers.
+ while ((SrcTypeTmp = SrcType->getAsPointerType()) &&
+ (DestTypeTmp = DestType->getAsPointerType()))
+ {
+ SrcType = Context.getCanonicalType(SrcTypeTmp->getPointeeType());
+ DestType = Context.getCanonicalType(DestTypeTmp->getPointeeType());
+ }
+
+ // 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.
+ const ConstantArrayType *SrcTypeArr, *DestTypeArr;
+ if ((SrcTypeArr = Context.getAsConstantArrayType(SrcType)) &&
+ (DestTypeArr = Context.getAsConstantArrayType(DestType)))
+ {
+ if (SrcTypeArr->getSize() != DestTypeArr->getSize()) {
+ // Different array sizes.
+ Diag(OpLoc, diag::err_bad_const_cast_generic,
+ OrigDestType.getAsString(), OrigSrcType.getAsString());
+ return false;
+ }
+ SrcType = Context.getCanonicalType(SrcTypeArr->getElementType());
+ DestType = Context.getCanonicalType(SrcTypeArr->getElementType());
+ }
+
+ // Since we're dealing in canonical types, the remainder must be the same.
+ // FIXME: These must not be function or member function types.
+ if(SrcType.getUnqualifiedType() != DestType.getUnqualifiedType()) {
+ // Cast between unrelated types.
+ Diag(OpLoc, diag::err_bad_const_cast_generic,
+ OrigDestType.getAsString(), OrigSrcType.getAsString());
+ return false;
+ }
+
+ return true;
+}
+
/// ActOnCXXBoolLiteral - Parse {true,false} literals.
Action::ExprResult
Sema::ActOnCXXBoolLiteral(SourceLocation OpLoc, tok::TokenKind Kind) {
- assert((Kind != tok::kw_true || Kind != tok::kw_false) &&
+ assert((Kind == tok::kw_true || Kind == tok::kw_false) &&
"Unknown C++ Boolean value!");
return new CXXBoolLiteralExpr(Kind == tok::kw_true, Context.BoolTy, OpLoc);
}
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp (revision 56652)
+++ lib/Sema/SemaExpr.cpp (working copy)
@@ -1703,7 +1703,7 @@
// DeclExpr's (created by ActOnIdentifierExpr), it would mess up the unary
// expressions that surpress this implicit conversion (&, sizeof).
//
- // Suppress this for references: C99 8.5.3p5. FIXME: revisit when references
+ // Suppress this for references: C++ 8.5.3p5. FIXME: revisit when references
// are better understood.
if (!lhsType->isReferenceType())
DefaultFunctionArrayConversion(rExpr);
Index: lib/Sema/Sema.h
===================================================================
--- lib/Sema/Sema.h (revision 56652)
+++ lib/Sema/Sema.h (working copy)
@@ -587,6 +587,9 @@
SourceLocation LParenLoc, ExprTy *E,
SourceLocation RParenLoc);
+ // Helpers for ActOnCXXCasts
+ bool CheckConstCast(SourceLocation OpLoc, Expr *SrcExpr, QualType DestType);
+
//// ActOnCXXThis - Parse 'this' pointer.
virtual ExprResult ActOnCXXThis(SourceLocation ThisLoc);
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits