diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -277,6 +277,9 @@ LANGOPT(ObjCWeakRuntime , 1, 0, "__weak support in the ARC runtime") LANGOPT(ObjCWeak , 1, 0, "Objective-C __weak in ARC and MRC files") LANGOPT(ObjCSubscriptingLegacyRuntime , 1, 0, "Subscripting support in legacy ObjectiveC runtime") +BENIGN_LANGOPT(CompatibilityQualifiedIdBlockParamTypeChecking, 1, 0, + "compatibility mode for type checking block parameters " + "involving qualified id types") LANGOPT(CFProtectionBranch , 1, 0, "Control-Flow Branch Protection enabled") LANGOPT(FakeAddressSpaceMap , 1, 0, "OpenCL fake address space map") ENUM_LANGOPT(AddressSpaceMapMangling , AddrSpaceMapMangling, 2, ASMM_Target, "OpenCL address space map mangling mode") diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -815,6 +815,9 @@ HelpText<"Use a signed type for wchar_t">; def fno_signed_wchar : Flag<["-"], "fno-signed-wchar">, HelpText<"Use an unsigned type for wchar_t">; +def fcompatibility_qualified_id_block_param_type_checking : Flag<["-"], "fcompatibility-qualified-id-block-type-checking">, + HelpText<"Allow using blocks with parameters of more specific type than " + "the type system guarantees when a parameter is qualified id">; // FIXME: Remove these entirely once functionality/tests have been excised. def fobjc_gc_only : Flag<["-"], "fobjc-gc-only">, Group, diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -8499,10 +8499,18 @@ RHSOPT->isObjCQualifiedIdType()); } - if (LHSOPT->isObjCQualifiedIdType() || RHSOPT->isObjCQualifiedIdType()) - return finish(ObjCQualifiedIdTypesAreCompatible( - (BlockReturnType ? LHSOPT : RHSOPT), - (BlockReturnType ? RHSOPT : LHSOPT), false)); + if (LHSOPT->isObjCQualifiedIdType() || RHSOPT->isObjCQualifiedIdType()) { + if (getLangOpts().CompatibilityQualifiedIdBlockParamTypeChecking) + // Use for block parameters previous type checking for compatibility. + return finish(ObjCQualifiedIdTypesAreCompatible(LHSOPT, RHSOPT, false) || + // Or corrected type checking as in non-compat mode. + (!BlockReturnType && + ObjCQualifiedIdTypesAreCompatible(RHSOPT, LHSOPT, false))); + else + return finish(ObjCQualifiedIdTypesAreCompatible( + (BlockReturnType ? LHSOPT : RHSOPT), + (BlockReturnType ? RHSOPT : LHSOPT), false)); + } const ObjCInterfaceType* LHS = LHSOPT->getInterfaceType(); const ObjCInterfaceType* RHS = RHSOPT->getInterfaceType(); diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -2374,6 +2374,10 @@ OS << "-target-sdk-version=" << SDKInfo->getVersion(); CC1Args.push_back(DriverArgs.MakeArgString(OS.str())); } + + // Enable compatibility mode for NSItemProviderCompletionHandler in + // Foundation/NSItemProvider.h. + CC1Args.push_back("-fcompatibility-qualified-id-block-type-checking"); } DerivedArgList * diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3371,6 +3371,9 @@ } Opts.BranchTargetEnforcement = Args.hasArg(OPT_mbranch_target_enforce); + + Opts.CompatibilityQualifiedIdBlockParamTypeChecking = + Args.hasArg(OPT_fcompatibility_qualified_id_block_param_type_checking); } static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) { diff --git a/clang/test/Driver/darwin-objc-options.m b/clang/test/Driver/darwin-objc-options.m --- a/clang/test/Driver/darwin-objc-options.m +++ b/clang/test/Driver/darwin-objc-options.m @@ -40,3 +40,9 @@ // Don't crash with an unexpected target triple. // RUN: %clang -target i386-apple-ios7 -S -### %s + +// Add -fcompatibility-qualified-id-block-type-checking only on Darwin. +// RUN: %clang -target x86_64-apple-darwin10 -### %s 2>&1 | FileCheck --check-prefix=DARWIN_COMPATIBILITY %s +// RUN: %clang -target x86_64-linux-gnu -### %s 2>&1 | FileCheck --check-prefix=OTHER_COMPATIBILITY %s +// DARWIN_COMPATIBILITY: -fcompatibility-qualified-id-block-type-checking +// OTHER_COMPATIBILITY-NOT: -fcompatibility-qualified-id-block-type-checking diff --git a/clang/test/SemaObjC/block-type-safety.m b/clang/test/SemaObjC/block-type-safety.m --- a/clang/test/SemaObjC/block-type-safety.m +++ b/clang/test/SemaObjC/block-type-safety.m @@ -1,4 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -verify -fblocks -Wno-objc-root-class %s +// RUN: %clang_cc1 -fsyntax-only -verify -fblocks -Wno-objc-root-class \ +// RUN: -fcompatibility-qualified-id-block-type-checking -DCOMPATIBILITY_QUALIFIED_ID_TYPE_CHECKING=1 %s // test for block type safety. @interface Super @end @@ -132,6 +134,7 @@ @interface NSAllArray (FooConformance) @end +#ifndef COMPATIBILITY_QUALIFIED_ID_TYPE_CHECKING int test5() { // Returned value is used outside of a block, so error on changing // a return type to a more general than expected. @@ -149,6 +152,25 @@ blockWithParam = genericBlockWithParam; return 0; } +#else +// In Apple SDK APIs using NSItemProviderCompletionHandler require to work with +// blocks that have parameters more specific than in method signatures. As +// explained in non-compatibility test above, it is not safe in general. But +// to keep existing code working we support a compatibility mode that uses +// previous type checking. +int test5() { + NSAllArray *(^block)(id); + id (^genericBlock)(id); + genericBlock = block; + block = genericBlock; // expected-error {{incompatible block pointer types assigning to 'NSAllArray *(^)(id)' from 'id (^)(id)'}} + + void (^blockWithParam)(NSAllArray *); + void (^genericBlockWithParam)(id); + genericBlockWithParam = blockWithParam; + blockWithParam = genericBlockWithParam; + return 0; +} +#endif // rdar://10798770 typedef int NSInteger;