Skip to content

Commit 5169982

Browse files
committed
[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 cba7eb9)
1 parent 893ee41 commit 5169982

File tree

2 files changed

+31
-24
lines changed

2 files changed

+31
-24
lines changed

lib/Sema/TypeCheckType.cpp

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2903,31 +2903,18 @@ bool TypeChecker::isCIntegerType(const DeclContext *DC, Type T) {
29032903

29042904
/// Determines whether the given type is bridged to an Objective-C class type.
29052905
static bool isBridgedToObjectiveCClass(DeclContext *dc, Type type) {
2906-
// Simple case: bridgeable object types.
2907-
if (type->isBridgeableObjectType())
2908-
return true;
2909-
2910-
// Any bridges to AnyObject.
2911-
if (type->isAny())
2912-
return true;
2913-
2914-
// Determine whether this type is bridged to Objective-C.
2915-
ASTContext &ctx = type->getASTContext();
2916-
Optional<Type> bridged = ctx.getBridgedToObjC(dc, type,
2917-
ctx.getLazyResolver());
2918-
if (!bridged)
2906+
switch (type->getForeignRepresentableIn(ForeignLanguage::ObjectiveC, dc)
2907+
.first) {
2908+
case ForeignRepresentableKind::Trivial:
2909+
case ForeignRepresentableKind::None:
29192910
return false;
29202911

2921-
// Check whether we're bridging to a class.
2922-
auto classDecl = (*bridged)->getClassOrBoundGenericClass();
2923-
if (!classDecl)
2924-
return false;
2925-
2926-
// Allow anything that isn't bridged to NSNumber.
2927-
// FIXME: This feels like a hack, but we don't have the right predicate
2928-
// anywhere.
2929-
return classDecl->getName().str()
2930-
!= ctx.getSwiftName(KnownFoundationEntity::NSNumber);
2912+
case ForeignRepresentableKind::Object:
2913+
case ForeignRepresentableKind::Bridged:
2914+
case ForeignRepresentableKind::BridgedError:
2915+
case ForeignRepresentableKind::StaticBridged:
2916+
return true;
2917+
}
29312918
}
29322919

29332920
bool TypeChecker::isRepresentableInObjC(

test/attr/attr_objc.swift

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ struct PlainStruct {}
1111
enum PlainEnum {}
1212
protocol PlainProtocol {} // expected-note {{protocol 'PlainProtocol' declared here}}
1313

14-
enum ErrorEnum : Error { }
14+
enum ErrorEnum : Error {
15+
case failed
16+
}
1517

1618
@objc class Class_ObjC1 {}
1719

@@ -1978,6 +1980,24 @@ class ClassThrows1 {
19781980
// CHECK: @objc init(degrees: Double) throws
19791981
// CHECK-DUMP: constructor_decl "init(degrees:)"{{.*}}foreign_error=NilResult,unowned,param=1,paramtype=Optional<AutoreleasingUnsafeMutablePointer<Optional<NSError>>>
19801982
init(degrees: Double) throws { }
1983+
1984+
// CHECK: {{^}} func methodReturnsBridgedValueType() throws -> NSRange
1985+
func methodReturnsBridgedValueType() throws -> NSRange { return NSRange() }
1986+
1987+
@objc func methodReturnsBridgedValueType2() throws -> NSRange {
1988+
return NSRange()
1989+
}
1990+
// 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}}
1991+
1992+
// CHECK: {{^}} @objc func methodReturnsError() throws -> Error
1993+
func methodReturnsError() throws -> Error { return ErrorEnum.failed }
1994+
1995+
// CHECK: @objc func methodReturnStaticBridged() throws -> ((Int) -> (Int) -> Int)
1996+
func methodReturnStaticBridged() throws -> ((Int) -> (Int) -> Int) {
1997+
func add(x: Int) -> (Int) -> Int {
1998+
return { x + $0 }
1999+
}
2000+
}
19812001
}
19822002

19832003
// CHECK-DUMP-LABEL: class_decl "SubclassImplicitClassThrows1"

0 commit comments

Comments
 (0)