Updated patch. Supports arrays and function pointers the same way as GCC (as near as I can determine).

What it does not support is array decay. This fails:

const int ar[100];
int *pi = const_cast<int*>(ar);

This is because lvalue-to-rvalue conversion is not performed on the argument (would lead to array decay). Is a generic facility for this in the semantic checker somewhere? It's really indispensable for C++ semantic checking.

The test case will now fail because I've put the above in it. I think it's better to have a failing test than to know there's a missing feature but eventually forget about it.

Sebastian
Index: test/SemaCXX/const-cast.cpp
===================================================================
--- test/SemaCXX/const-cast.cpp (revision 0)
+++ test/SemaCXX/const-cast.cpp (revision 0)
@@ -0,0 +1,46 @@
+// 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;
+typedef int (*f)(int);
+
+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);
+  int *pi = const_cast<int*>(ar); // Array decay?
+  f fp = 0;
+  f *fpp = const_cast<f*>(&fp);
+  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}}
+  const int *ar[100] = {0};
+  // Not even lenient g++ accepts this.
+  int *(*rar)[100] = const_cast<int *(*)[100]>(&ar); // expected-error 
{{invalid const_cast from 'int const *(*)[100]' to incompatible type 'int 
*(*)[100]'}}
+  f fp1 = 0;
+  // Direct casting of function pointers not allowed.
+  f fp2 = const_cast<f>(fp1); // expected-error {{invalid const_cast to 'f', 
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,114 @@
                     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);
+  }
+  if (!DestType->isPointerType()) {
+    // Cannot cast to non-pointer, non-reference type.
+    Diag(OpLoc, diag::err_bad_const_cast_dest, OrigDestType.getAsString());
+    return false;
+  }
+  if (DestType->isFunctionPointerType()) {
+    // Cannot cast direct function pointers.
+    Diag(OpLoc, diag::err_bad_const_cast_dest, OrigDestType.getAsString());
+    return false;
+  }
+  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()))
+  {
+    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.
+  // No more than one level, though, so don't embed this in the unwrap loop
+  // above.
+  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(DestTypeArr->getElementType());
+  }
+
+  // Since we're dealing in canonical types, the remainder must be the same.
+  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