From 5169982e438426ca74d40a3622571cf23e4b85b3 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 6 Sep 2016 15:57:40 -0700 Subject: [PATCH 1/2] [Type checker] Throwing methods must return an object type in the @objc thunk to be @objc. A throwing method can only be exposed to Objective-C if we can map the return type convention, either because the return type is Void or because it is something that is bridged to an object type (and can therefore be nil to indicate error). Our predicate for checking "bridged to an object type" didn't account for value types that are exposed to (or come from) C, and therefore aren't actually bridged to object types in the Objective-C thunk, meaning they cannot be optional. An existing hack dealt with the largest class of these---types like Int and Bool that can be dynamically bridged to NSNumber---but generalize this by checking exactly how the result type is going to be represented in Objective-C, rejecting '@objc' for cases where the result type won't be an object type. Fixes rdar://problem/28035614 for real. (cherry picked from commit cba7eb960d4a092df482387ecfbf7f2a002f4d00) --- lib/Sema/TypeCheckType.cpp | 33 ++++++++++----------------------- test/attr/attr_objc.swift | 22 +++++++++++++++++++++- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index d36f5338bb4c4..27464da166ce0 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -2903,31 +2903,18 @@ bool TypeChecker::isCIntegerType(const DeclContext *DC, Type T) { /// Determines whether the given type is bridged to an Objective-C class type. static bool isBridgedToObjectiveCClass(DeclContext *dc, Type type) { - // Simple case: bridgeable object types. - if (type->isBridgeableObjectType()) - return true; - - // Any bridges to AnyObject. - if (type->isAny()) - return true; - - // Determine whether this type is bridged to Objective-C. - ASTContext &ctx = type->getASTContext(); - Optional bridged = ctx.getBridgedToObjC(dc, type, - ctx.getLazyResolver()); - if (!bridged) + switch (type->getForeignRepresentableIn(ForeignLanguage::ObjectiveC, dc) + .first) { + case ForeignRepresentableKind::Trivial: + case ForeignRepresentableKind::None: return false; - // Check whether we're bridging to a class. - auto classDecl = (*bridged)->getClassOrBoundGenericClass(); - if (!classDecl) - return false; - - // Allow anything that isn't bridged to NSNumber. - // FIXME: This feels like a hack, but we don't have the right predicate - // anywhere. - return classDecl->getName().str() - != ctx.getSwiftName(KnownFoundationEntity::NSNumber); + case ForeignRepresentableKind::Object: + case ForeignRepresentableKind::Bridged: + case ForeignRepresentableKind::BridgedError: + case ForeignRepresentableKind::StaticBridged: + return true; + } } bool TypeChecker::isRepresentableInObjC( diff --git a/test/attr/attr_objc.swift b/test/attr/attr_objc.swift index 89f0599fd07d7..244d6b6d00933 100644 --- a/test/attr/attr_objc.swift +++ b/test/attr/attr_objc.swift @@ -11,7 +11,9 @@ struct PlainStruct {} enum PlainEnum {} protocol PlainProtocol {} // expected-note {{protocol 'PlainProtocol' declared here}} -enum ErrorEnum : Error { } +enum ErrorEnum : Error { + case failed +} @objc class Class_ObjC1 {} @@ -1978,6 +1980,24 @@ class ClassThrows1 { // CHECK: @objc init(degrees: Double) throws // CHECK-DUMP: constructor_decl "init(degrees:)"{{.*}}foreign_error=NilResult,unowned,param=1,paramtype=Optional>> init(degrees: Double) throws { } + + // CHECK: {{^}} func methodReturnsBridgedValueType() throws -> NSRange + func methodReturnsBridgedValueType() throws -> NSRange { return NSRange() } + + @objc func methodReturnsBridgedValueType2() throws -> NSRange { + return NSRange() + } + // expected-error@-3{{throwing method cannot be marked @objc because it returns a value of type 'NSRange' (aka '_NSRange'); return 'Void' or a type that bridges to an Objective-C class}} + + // CHECK: {{^}} @objc func methodReturnsError() throws -> Error + func methodReturnsError() throws -> Error { return ErrorEnum.failed } + + // CHECK: @objc func methodReturnStaticBridged() throws -> ((Int) -> (Int) -> Int) + func methodReturnStaticBridged() throws -> ((Int) -> (Int) -> Int) { + func add(x: Int) -> (Int) -> Int { + return { x + $0 } + } + } } // CHECK-DUMP-LABEL: class_decl "SubclassImplicitClassThrows1" From 6a51859080f3d9f4fd9322f376615986df6f8961 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 6 Sep 2016 20:59:57 -0700 Subject: [PATCH 2/2] [Type checker] Minor cleanups for @objc inference from witnesses. (cherry picked from commit 48204538206865830b3c2a635a02aa9d90675e01) --- lib/Sema/TypeCheckProtocol.cpp | 2 +- test/attr/attr_objc.swift | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 2829fc7063d45..c79e5f28cf70a 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -4853,6 +4853,7 @@ TypeChecker::findWitnessedObjCRequirements(const ValueDecl *witness, // We only care about Objective-C protocols. if (!proto->isObjC()) continue; + Optional conformance; for (auto req : proto->lookupDirect(name, true)) { // Skip anything in a protocol extension. if (req->getDeclContext() != proto) continue; @@ -4861,7 +4862,6 @@ TypeChecker::findWitnessedObjCRequirements(const ValueDecl *witness, if (isa(req)) continue; // Dig out the conformance. - Optional conformance; if (!conformance.hasValue()) { SmallVector conformances; nominal->lookupConformance(dc->getParentModule(), proto, diff --git a/test/attr/attr_objc.swift b/test/attr/attr_objc.swift index 244d6b6d00933..ef37b0983d9a6 100644 --- a/test/attr/attr_objc.swift +++ b/test/attr/attr_objc.swift @@ -2147,15 +2147,14 @@ extension ClassInfersFromProtocol3 { func method1(value: String) { } } +// Inference for subclasses. class SuperclassImplementsProtocol : InferFromProtocol { } -// Note: no inference for subclasses class SubclassInfersFromProtocol1 : SuperclassImplementsProtocol { // CHECK: {{^}} @objc func method1(value: Int) func method1(value: Int) { } } -// Note: no inference for subclasses class SubclassInfersFromProtocol2 : SuperclassImplementsProtocol { } @@ -2163,4 +2162,3 @@ extension SubclassInfersFromProtocol2 { // CHECK: {{^}} @objc dynamic func method1(value: Int) func method1(value: Int) { } } -