diff options
author | John McCall <rjmccall@apple.com> | 2011-04-09 22:50:59 +0000 |
---|---|---|
committer | John McCall <rjmccall@apple.com> | 2011-04-09 22:50:59 +0000 |
commit | a5fc472b353b88be3b4981da946fb01f5a5cc0c6 (patch) | |
tree | 6d7bbf117ee1d4f5e5742e5fa1826461d56059ca | |
parent | 5536daa627b508299007b735a588bf4e88825bb3 (diff) |
Fix a bunch of major problems with __unknown_anytype and properly test
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
-rw-r--r-- | include/clang/Basic/LangOptions.h | 2 | ||||
-rw-r--r-- | include/clang/Basic/Specifiers.h | 1 | ||||
-rw-r--r-- | include/clang/Basic/TokenKinds.def | 7 | ||||
-rw-r--r-- | include/clang/Driver/CC1Options.td | 2 | ||||
-rw-r--r-- | include/clang/Sema/DeclSpec.h | 1 | ||||
-rw-r--r-- | lib/Basic/IdentifierTable.cpp | 5 | ||||
-rw-r--r-- | lib/Frontend/CompilerInvocation.cpp | 3 | ||||
-rw-r--r-- | lib/Parse/ParseDecl.cpp | 4 | ||||
-rw-r--r-- | lib/Sema/DeclSpec.cpp | 1 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 153 | ||||
-rw-r--r-- | lib/Sema/SemaTemplateVariadic.cpp | 1 | ||||
-rw-r--r-- | lib/Sema/SemaType.cpp | 4 | ||||
-rw-r--r-- | test/CodeGenCXX/unknown-anytype.cpp | 57 | ||||
-rw-r--r-- | test/SemaCXX/unknown-anytype.cpp | 21 |
14 files changed, 223 insertions, 39 deletions
diff --git a/include/clang/Basic/LangOptions.h b/include/clang/Basic/LangOptions.h index e2c197f949..beafdba3c9 100644 --- a/include/clang/Basic/LangOptions.h +++ b/include/clang/Basic/LangOptions.h @@ -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; } diff --git a/include/clang/Basic/Specifiers.h b/include/clang/Basic/Specifiers.h index e6b6218100..2f0ad9ffb6 100644 --- a/include/clang/Basic/Specifiers.h +++ b/include/clang/Basic/Specifiers.h @@ -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 }; diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def index ccc8ac79ae..c446d6a626 100644 --- a/include/clang/Basic/TokenKinds.def +++ b/include/clang/Basic/TokenKinds.def @@ -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 diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index f2aba0dfcb..6938991cc9 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -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 diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index 26314c341a..64f4670f36 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -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 diff --git a/lib/Basic/IdentifierTable.cpp b/lib/Basic/IdentifierTable.cpp index cfe8d27927..3d08bf807e 100644 --- a/lib/Basic/IdentifierTable.cpp +++ b/lib/Basic/IdentifierTable.cpp @@ -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 { diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 3d6d9d9afc..00418967fe 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -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); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index d553687837..8674485d5f 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -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: diff --git a/lib/Sema/DeclSpec.cpp b/lib/Sema/DeclSpec.cpp index cad3b58d76..0f20d10b07 100644 --- a/lib/Sema/DeclSpec.cpp +++ b/lib/Sema/DeclSpec.cpp @@ -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!"); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index fbf0a986d5..70d7c7af48 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -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) { diff --git a/lib/Sema/SemaTemplateVariadic.cpp b/lib/Sema/SemaTemplateVariadic.cpp index 0da801c7e3..5c321fd6df 100644 --- a/lib/Sema/SemaTemplateVariadic.cpp +++ b/lib/Sema/SemaTemplateVariadic.cpp @@ -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; } diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 3a19f2f73a..fc2c8f7e1d 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -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 index 0000000000..fdf5fab9cf --- /dev/null +++ b/test/CodeGenCXX/unknown-anytype.cpp @@ -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 index 0000000000..bbf6a9e72f --- /dev/null +++ b/test/SemaCXX/unknown-anytype.cpp @@ -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}} + } +} |