]> granicus.if.org Git - clang/commitdiff
Fix a bunch of major problems with __unknown_anytype and properly test
authorJohn McCall <rjmccall@apple.com>
Sat, 9 Apr 2011 22:50:59 +0000 (22:50 +0000)
committerJohn McCall <rjmccall@apple.com>
Sat, 9 Apr 2011 22:50:59 +0000 (22:50 +0000)
for them.  The only major missing feature is references.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@129234 91177308-0d34-0410-b5e6-96231b3b80d8

14 files changed:
include/clang/Basic/LangOptions.h
include/clang/Basic/Specifiers.h
include/clang/Basic/TokenKinds.def
include/clang/Driver/CC1Options.td
include/clang/Sema/DeclSpec.h
lib/Basic/IdentifierTable.cpp
lib/Frontend/CompilerInvocation.cpp
lib/Parse/ParseDecl.cpp
lib/Sema/DeclSpec.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaTemplateVariadic.cpp
lib/Sema/SemaType.cpp
test/CodeGenCXX/unknown-anytype.cpp [new file with mode: 0644]
test/SemaCXX/unknown-anytype.cpp [new file with mode: 0644]

index e2c197f9495a32dfd20e276fde99afacb59436b7..beafdba3c95dda93281dcb811cc79439a1f74f55 100644 (file)
@@ -113,6 +113,7 @@ public:
   unsigned NoConstantCFStrings : 1;  // Do not do CF strings
   unsigned InlineVisibilityHidden : 1; // Whether inline C++ methods have
                                        // hidden visibility by default.
+  unsigned ParseUnknownAnytype: 1; /// Let the user write __unknown_anytype.
 
   unsigned SpellChecking : 1; // Whether to perform spell-checking for error
                               // recovery.
@@ -223,6 +224,7 @@ public:
     NoBitFieldTypeAlign = 0;
     FakeAddressSpaceMap = 0;
     MRTD = 0;
+    ParseUnknownAnytype = 0;
   }
 
   GCMode getGCMode() const { return (GCMode) GC; }
index e6b6218100ad0d5d615729de188ed7ec2092ff9b..2f0ad9ffb688cbecc198eb2ce22f849b1ebb042e 100644 (file)
@@ -55,6 +55,7 @@ namespace clang {
     TST_typeofExpr,
     TST_decltype,     // C++0x decltype
     TST_auto,         // C++0x auto
+    TST_unknown_anytype, // __unknown_anytype extension
     TST_error         // erroneous type
   };
   
index ccc8ac79aeee2e95ad61f27240d2bb41599022a6..c446d6a62647597900e822a1e0625f8d7592e914 100644 (file)
@@ -38,6 +38,9 @@
 #ifndef OBJC2_AT_KEYWORD
 #define OBJC2_AT_KEYWORD(X)
 #endif
+#ifndef TESTING_KEYWORD
+#define TESTING_KEYWORD(X, L) KEYWORD(X, L)
+#endif
 #ifndef ANNOTATION
 #define ANNOTATION(X) TOK(annot_ ## X)
 #endif
@@ -416,6 +419,9 @@ ALIAS("_pascal"      , __pascal   , KEYBORLAND)
 ALIAS("__char16_t"   , char16_t   , KEYCXX)
 ALIAS("__char32_t"   , char32_t   , KEYCXX)
 
+// Clang-specific keywords enabled only in testing.
+TESTING_KEYWORD(__unknown_anytype , KEYALL)
+
 
 //===----------------------------------------------------------------------===//
 // Objective-C @-preceeded keywords.
@@ -467,6 +473,7 @@ ANNOTATION(template_id)  // annotation for a C++ template-id that names a
 ANNOTATION(pragma_unused)
 
 #undef ANNOTATION
+#undef TESTING_KEYWORD
 #undef OBJC2_AT_KEYWORD
 #undef OBJC1_AT_KEYWORD
 #undef CXX_KEYWORD_OPERATOR
index f2aba0dfcb549db62719a2020bea873808ea0ff0..6938991cc969bd587a00f9c120436188bd81a83c 100644 (file)
@@ -526,6 +526,8 @@ def traditional_cpp : Flag<"-traditional-cpp">,
   HelpText<"Enable some traditional CPP emulation">;
 def ffake_address_space_map : Flag<"-ffake-address-space-map">,
   HelpText<"Use a fake address space map; OpenCL testing purposes only">;
+def funknown_anytype : Flag<"-funknown-anytype">,
+  HelpText<"Enable parser support for the __unknown_anytype type; for testing purposes only">;
 
 //===----------------------------------------------------------------------===//
 // Header Search Options
index 26314c341aa251e66ed92748fad4a171e04d0af1..64f4670f36ed9197fdd86fcd4897c4832ab87a2d 100644 (file)
@@ -250,6 +250,7 @@ public:
   static const TST TST_typeofExpr = clang::TST_typeofExpr;
   static const TST TST_decltype = clang::TST_decltype;
   static const TST TST_auto = clang::TST_auto;
+  static const TST TST_unknown_anytype = clang::TST_unknown_anytype;
   static const TST TST_error = clang::TST_error;
 
   // type-qualifiers
index cfe8d27927d24733ecb9076b809a48f941d74156..3d08bf807ebccdc1edbf2737ef9f594a3be386a8 100644 (file)
@@ -162,7 +162,12 @@ void IdentifierTable::AddKeywords(const LangOptions &LangOpts) {
 #define OBJC2_AT_KEYWORD(NAME) \
   if (LangOpts.ObjC2)          \
     AddObjCKeyword(llvm::StringRef(#NAME), tok::objc_##NAME, *this);
+#define TESTING_KEYWORD(NAME, FLAGS)
 #include "clang/Basic/TokenKinds.def"
+
+  if (LangOpts.ParseUnknownAnytype)
+    AddKeyword("__unknown_anytype", tok::kw___unknown_anytype, KEYALL,
+               LangOpts, *this);
 }
 
 tok::PPKeywordKind IdentifierInfo::getPPKeywordID() const {
index 3d6d9d9afcbaf7ea312f5e4b2a8fc2387c36abc1..00418967fefab1379cf77d048569f8c26800dff4 100644 (file)
@@ -684,6 +684,8 @@ static void LangOptsToArgs(const LangOptions &Opts,
   }
   if (Opts.FakeAddressSpaceMap)
     Res.push_back("-ffake-address-space-map");
+  if (Opts.ParseUnknownAnytype)
+    Res.push_back("-funknown-anytype");
 }
 
 static void PreprocessorOptsToArgs(const PreprocessorOptions &Opts,
@@ -1507,6 +1509,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
   Opts.OptimizeSize = 0;
   Opts.MRTD = Args.hasArg(OPT_mrtd);
   Opts.FakeAddressSpaceMap = Args.hasArg(OPT_ffake_address_space_map);
+  Opts.ParseUnknownAnytype = Args.hasArg(OPT_funknown_anytype);
 
   // FIXME: Eliminate this dependency.
   unsigned Opt = getOptimizationLevel(Args, IK, Diags);
index d553687837e6c547ff594b4e23692d63031e79f4..8674485d5f84f91fee5e7dddbb5dc9788b860127 100644 (file)
@@ -1720,6 +1720,10 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
     case tok::kw___pixel:
       isInvalid = DS.SetTypeAltiVecPixel(true, Loc, PrevSpec, DiagID);
       break;
+    case tok::kw___unknown_anytype:
+      isInvalid = DS.SetTypeSpecType(TST_unknown_anytype, Loc,
+                                     PrevSpec, DiagID);
+      break;
 
     // class-specifier:
     case tok::kw_class:
index cad3b58d7646dd3be4d1a34e641f1f0e98fc9e62..0f20d10b076a2053a3a65c439da8d110cf7c5be1 100644 (file)
@@ -309,6 +309,7 @@ const char *DeclSpec::getSpecifierName(DeclSpec::TST T) {
   case DeclSpec::TST_typeofExpr:  return "typeof";
   case DeclSpec::TST_auto:        return "auto";
   case DeclSpec::TST_decltype:    return "(decltype)";
+  case DeclSpec::TST_unknown_anytype: return "__unknown_anytype";
   case DeclSpec::TST_error:       return "(error)";
   }
   llvm_unreachable("Unknown typespec!");
index fbf0a986d5f95db73a9a9014969250ff1b5ad463..70d7c7af4846c7675847027c878a7aa204c3560c 100644 (file)
@@ -4720,6 +4720,11 @@ Sema::ActOnCUDAExecConfigExpr(Scope *S, SourceLocation LLLLoc,
 /// yielding a value of unknown-any type.
 static ExprResult rebuildUnknownAnyFunction(Sema &S, Expr *fn,
                                             Expr **args, unsigned numArgs) {
+  // Strip an lvalue-to-rvalue conversion off.
+  if (ImplicitCastExpr *ice = dyn_cast<ImplicitCastExpr>(fn))
+    if (ice->getCastKind() == CK_LValueToRValue)
+      fn = ice->getSubExpr();
+
   // Build a simple function type exactly matching the arguments.
   llvm::SmallVector<QualType, 8> argTypes;
   argTypes.reserve(numArgs);
@@ -4818,6 +4823,7 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
       ExprResult rewrite = rebuildUnknownAnyFunction(*this, Fn, Args, NumArgs);
       if (rewrite.isInvalid()) return ExprError();
       Fn = rewrite.take();
+      TheCall->setCallee(Fn);
       NDecl = FDecl = 0;
       goto retry;
     }
@@ -10138,7 +10144,7 @@ ExprResult Sema::ActOnBooleanCondition(Scope *S, SourceLocation Loc,
 
 namespace {
   struct RebuildUnknownAnyExpr
-    : StmtVisitor<RebuildUnknownAnyExpr, Expr*> {
+    : StmtVisitor<RebuildUnknownAnyExpr, ExprResult> {
 
     Sema &S;
 
@@ -10148,57 +10154,123 @@ namespace {
     RebuildUnknownAnyExpr(Sema &S, QualType castType)
       : S(S), DestType(castType) {}
 
-    Expr *VisitStmt(Stmt *S) {
+    ExprResult VisitStmt(Stmt *S) {
       llvm_unreachable("unexpected expression kind!");
-      return 0;
+      return ExprError();
+    }
+
+    ExprResult VisitCallExpr(CallExpr *call) {
+      Expr *callee = call->getCallee();
+
+      bool wasBlock;
+      QualType type = callee->getType();
+      if (const PointerType *ptr = type->getAs<PointerType>()) {
+        type = ptr->getPointeeType();
+        wasBlock = false;
+      } else {
+        type = type->castAs<BlockPointerType>()->getPointeeType();
+        wasBlock = true;
+      }
+      const FunctionType *fnType = type->castAs<FunctionType>();
+
+      // Verify that this is a legal result type of a function.
+      if (DestType->isArrayType() || DestType->isFunctionType()) {
+        unsigned diagID = diag::err_func_returning_array_function;
+        if (wasBlock) diagID = diag::err_block_returning_array_function;
+
+        S.Diag(call->getExprLoc(), diagID)
+          << DestType->isFunctionType() << DestType;
+        return ExprError();
+      }
+
+      // Otherwise, go ahead and set DestType as the call's result.
+      call->setType(DestType.getNonLValueExprType(S.Context));
+      call->setValueKind(Expr::getValueKindForType(DestType));
+      assert(call->getObjectKind() == OK_Ordinary);
+
+      // Rebuild the function type, replacing the result type with DestType.
+      if (const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(fnType))
+        DestType = S.Context.getFunctionType(DestType,
+                                             proto->arg_type_begin(),
+                                             proto->getNumArgs(),
+                                             proto->getExtProtoInfo());
+      else
+        DestType = S.Context.getFunctionNoProtoType(DestType,
+                                                    fnType->getExtInfo());
+
+      // Rebuild the appropriate pointer-to-function type.
+      if (wasBlock)
+        DestType = S.Context.getBlockPointerType(DestType);
+      else
+        DestType = S.Context.getPointerType(DestType);
+
+      // Finally, we can recurse.
+      ExprResult calleeResult = Visit(callee);
+      if (!calleeResult.isUsable()) return ExprError();
+      call->setCallee(calleeResult.take());
+
+      // Bind a temporary if necessary.
+      return S.MaybeBindToTemporary(call);
     }
 
-    Expr *VisitCallExpr(CallExpr *call) {
-      call->setCallee(Visit(call->getCallee()));
-      return call;
+    /// Rebuild an expression which simply semantically wraps another
+    /// expression which it shares the type and value kind of.
+    template <class T> ExprResult rebuildSugarExpr(T *expr) {
+      ExprResult subResult = Visit(expr->getSubExpr());
+      if (!subResult.isUsable()) return ExprError();
+      Expr *subExpr = subResult.take();
+      expr->setSubExpr(subExpr);
+      expr->setType(subExpr->getType());
+      expr->setValueKind(subExpr->getValueKind());
+      assert(expr->getObjectKind() == OK_Ordinary);
+      return expr;
     }
 
-    Expr *VisitParenExpr(ParenExpr *paren) {
-      paren->setSubExpr(Visit(paren->getSubExpr()));
-      return paren;
+    ExprResult VisitParenExpr(ParenExpr *paren) {
+      return rebuildSugarExpr(paren);
     }
 
-    Expr *VisitUnaryExtension(UnaryOperator *op) {
-      op->setSubExpr(Visit(op->getSubExpr()));
-      return op;
+    ExprResult VisitUnaryExtension(UnaryOperator *op) {
+      return rebuildSugarExpr(op);
     }
 
-    Expr *VisitImplicitCastExpr(ImplicitCastExpr *ice) {
-      // If this isn't an inner resolution, just recurse down.
-      if (ice->getCastKind() != CK_ResolveUnknownAnyType) {
-        assert(ice->getCastKind() == CK_FunctionToPointerDecay);
-        ice->setSubExpr(Visit(ice->getSubExpr()));
-        return ice;
-      }
+    ExprResult VisitImplicitCastExpr(ImplicitCastExpr *ice) {
+      // Rebuild an inner resolution by stripping it and propagating
+      // the new type down.
+      if (ice->getCastKind() == CK_ResolveUnknownAnyType)
+        return Visit(ice->getSubExpr());
+
+      // The only other case we should be able to get here is a
+      // function-to-pointer decay.
+      assert(ice->getCastKind() == CK_FunctionToPointerDecay);
+      ice->setType(DestType);
+      assert(ice->getValueKind() == VK_RValue);
+      assert(ice->getObjectKind() == OK_Ordinary);
 
-      QualType type = ice->getType();
-      assert(type.getUnqualifiedType() == type);
+      // Rebuild the sub-expression as the pointee (function) type.
+      DestType = DestType->castAs<PointerType>()->getPointeeType();
 
-      // The only time it should be possible for this to appear
-      // internally to an unknown-any expression is when handling a call.
-      const FunctionProtoType *proto = type->castAs<FunctionProtoType>();
-      DestType = S.Context.getFunctionType(DestType,
-                                           proto->arg_type_begin(),
-                                           proto->getNumArgs(),
-                                           proto->getExtProtoInfo());
+      ExprResult result = Visit(ice->getSubExpr());
+      if (!result.isUsable()) return ExprError();
 
-      // Strip the resolve cast when recursively rebuilding.
-      return Visit(ice->getSubExpr());
+      ice->setSubExpr(result.take());
+      return S.Owned(ice);
     }
 
-    Expr *VisitDeclRefExpr(DeclRefExpr *ref) {
+    ExprResult VisitDeclRefExpr(DeclRefExpr *ref) {
       ExprValueKind valueKind = VK_LValue;
-      if (!S.getLangOptions().CPlusPlus && DestType->isFunctionType())
+      if (S.getLangOptions().CPlusPlus) {
+        // FIXME: if the value was resolved as a reference type, we
+        // should really remember that somehow, or else we'll be
+        // missing a load.
+        DestType = DestType.getNonReferenceType();
+      } else if (DestType->isFunctionType()) {
         valueKind = VK_RValue;
+      }
 
-      return ImplicitCastExpr::Create(S.Context, DestType,
-                                      CK_ResolveUnknownAnyType,
-                                      ref, 0, valueKind);
+      return S.Owned(ImplicitCastExpr::Create(S.Context, DestType,
+                                              CK_ResolveUnknownAnyType,
+                                              ref, 0, valueKind));
     }
   };
 }
@@ -10208,12 +10280,15 @@ namespace {
 ExprResult Sema::checkUnknownAnyCast(SourceRange typeRange, QualType castType,
                                      Expr *castExpr, CastKind &castKind,
                                      ExprValueKind &VK, CXXCastPath &path) {
-  VK = Expr::getValueKindForType(castType);
-
   // Rewrite the casted expression from scratch.
-  castExpr = RebuildUnknownAnyExpr(*this, castType).Visit(castExpr);
+  ExprResult result = RebuildUnknownAnyExpr(*this, castType).Visit(castExpr);
+  if (!result.isUsable()) return ExprError();
+
+  castExpr = result.take();
+  VK = castExpr->getValueKind();
+  castKind = CK_NoOp;
 
-  return CheckCastTypes(typeRange, castType, castExpr, castKind, VK, path);
+  return castExpr;
 }
 
 static ExprResult diagnoseUnknownAnyExpr(Sema &S, Expr *e) {
index 0da801c7e343ce2154d1bc738cda1bfb13353305..5c321fd6df5c1dbbc675ec81307a947a158de4b4 100644 (file)
@@ -649,6 +649,7 @@ bool Sema::containsUnexpandedParameterPacks(Declarator &D) {
   case TST_struct:
   case TST_class:
   case TST_auto:
+  case TST_unknown_anytype:
   case TST_error:
     break;
   }
index 3a19f2f73a2b63f84c6225614f62503c5db4551d..fc2c8f7e1dde9a0d7867ddf58b03c4a1ce8248b7 100644 (file)
@@ -841,6 +841,10 @@ static QualType ConvertDeclSpecToType(Sema &S, TypeProcessingState &state) {
     break;
   }
 
+  case DeclSpec::TST_unknown_anytype:
+    Result = Context.UnknownAnyTy;
+    break;
+
   case DeclSpec::TST_error:
     Result = Context.IntTy;
     declarator.setInvalidType(true);
diff --git a/test/CodeGenCXX/unknown-anytype.cpp b/test/CodeGenCXX/unknown-anytype.cpp
new file mode 100644 (file)
index 0000000..fdf5fab
--- /dev/null
@@ -0,0 +1,57 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -funknown-anytype -emit-llvm -o - %s | FileCheck %s
+
+int test0() {
+  extern __unknown_anytype test0_any;
+  // CHECK: load i32* @test0_any
+  return (int) test0_any;
+}
+
+int test1() {
+  extern __unknown_anytype test1_any;
+  // CHECK: call i32 @test1_any()
+  return (int) test1_any();
+}
+
+float test2() {
+  extern __unknown_anytype test2_any;
+  // CHECK: call float @test2_any(float {{[^,]+}})
+  return (float) test2_any(0.5f);
+}
+
+float test3() {
+  extern __unknown_anytype test3_any;
+  // CHECK: call float @test3_any(i32 5)
+  return ((float(int)) test3_any)(5);
+}
+
+namespace test4 {
+  extern __unknown_anytype test4_any1;
+  extern __unknown_anytype test4_any2;
+
+  int test() {
+    // CHECK: load i32* @_ZN5test410test4_any1E
+    // CHECK: call i32 @_ZN5test410test4_any2E
+    return (int) test4_any1 + (int) test4_any2();
+  }
+}
+
+void test5() {
+  extern __unknown_anytype test5_any;
+  // CHECK: call void @test5_any()
+  return (void) test5_any();
+}
+
+long test6() {
+  extern __unknown_anytype test6_any(float *);
+  // CHECK: call i64 @_Z9test6_anyPf(float* null)
+  return (long) test6_any(0);
+}
+
+struct Test7 {
+  ~Test7();
+};
+Test7 test7() {
+  extern __unknown_anytype test7_any;
+  // CHECK: call void @test7_any({{%.*}}* sret {{%.*}}, i32 5)
+  return (Test7) test7_any(5);
+}
diff --git a/test/SemaCXX/unknown-anytype.cpp b/test/SemaCXX/unknown-anytype.cpp
new file mode 100644 (file)
index 0000000..bbf6a9e
--- /dev/null
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -funknown-anytype -fsyntax-only -verify %s
+
+namespace test0 {
+  extern __unknown_anytype test0;
+  extern __unknown_anytype test1();
+  extern __unknown_anytype test2(int);
+}
+
+namespace test1 {
+  extern __unknown_anytype foo;
+  int test() {
+    // TODO: it would be great if the 'cannot initialize' errors
+    // turned into something more interesting.  It's just a matter of
+    // making sure that these locations check for placeholder types
+    // properly.
+
+    int x = foo; // expected-error {{cannot initialize}}
+    int y = 0 + foo; // expected-error {{no known type for 'foo'; must explicitly cast this expression to use it}}
+    return foo; // expected-error {{cannot initialize}}
+  }
+}