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&lt;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

Reply via email to