Skip to content

Commit 0c8f55e

Browse files
committed
Optional swift_newtype types are @objc if the raw type would be @objc.
This was causing issues where the compiler rejected overrides of imported members as being non-ObjC-compatible, even though the type was exactly the same as what the Clang importer was using. https://bugs.swift.org/browse/SR-2344 (cherry picked from commit 8141363)
1 parent 0219ecd commit 0c8f55e

File tree

9 files changed

+78
-10
lines changed

9 files changed

+78
-10
lines changed

lib/AST/Decl.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4928,17 +4928,14 @@ Type TypeBase::getSwiftNewtypeUnderlyingType() {
49284928
return {};
49294929

49304930
// Make sure the clang node has swift_newtype attribute
4931-
if (!structDecl->getClangNode())
4932-
return {};
4933-
auto clangNode = structDecl->getClangNode();
4934-
if (!clangNode.getAsDecl() ||
4935-
!clangNode.castAsDecl()->getAttr<clang::SwiftNewtypeAttr>())
4931+
auto clangNode = structDecl->getClangDecl();
4932+
if (!clangNode || !clangNode->hasAttr<clang::SwiftNewtypeAttr>())
49364933
return {};
49374934

49384935
// Underlying type is the type of rawValue
49394936
for (auto member : structDecl->getMembers())
49404937
if (auto varDecl = dyn_cast<VarDecl>(member))
4941-
if (varDecl->getName().str() == "rawValue")
4938+
if (varDecl->getName() == getASTContext().Id_rawValue)
49424939
return varDecl->getType();
49434940

49444941
return {};

lib/AST/Type.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2224,12 +2224,19 @@ getForeignRepresentable(Type type, ForeignLanguage language,
22242224
case ForeignLanguage::ObjectiveC:
22252225
if (isa<StructDecl>(nominal) || isa<EnumDecl>(nominal)) {
22262226
// Optional structs are not representable in (Objective-)C if they
2227-
// originally came from C, whether or not they are bridged. If they
2228-
// are defined in Swift, they are only representable if they are
2229-
// bridged (checked below).
2227+
// originally came from C, whether or not they are bridged, unless they
2228+
// came from swift_newtype. If they are defined in Swift, they are only
2229+
// representable if they are bridged (checked below).
22302230
if (wasOptional) {
2231-
if (nominal->hasClangNode())
2231+
if (nominal->hasClangNode()) {
2232+
Type underlyingType =
2233+
nominal->getDeclaredType()->getSwiftNewtypeUnderlyingType();
2234+
if (underlyingType) {
2235+
return getForeignRepresentable(OptionalType::get(underlyingType),
2236+
language, dc);
2237+
}
22322238
return failure();
2239+
}
22332240
break;
22342241
}
22352242
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@import Foundation;
2+
3+
typedef NSString *__nonnull SNTErrorDomain __attribute((swift_newtype(struct)));
4+
typedef NSInteger MyInt __attribute((swift_newtype(struct)));

test/ClangModules/Inputs/custom-modules/module.map

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ module MissingHeader {
6969
header "this-header-does-not-exist.h"
7070
}
7171

72+
module Newtype {
73+
header "Newtype.h"
74+
}
75+
7276
module ObjCIRExtras {
7377
header "ObjCIRExtras.h"
7478
export *

test/ClangModules/objc_parse.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import AppKit
66
import AVFoundation
77

8+
import Newtype
89
import objc_ext
910
import TestProtocols
1011

@@ -599,3 +600,10 @@ func testNSUInteger(_ obj: NSUIntegerTests, uint: UInt, int: Int) {
599600
let _: String = num.uintValue // expected-error {{cannot convert value of type 'UInt' to specified type 'String'}}
600601
}
601602

603+
class NewtypeUser {
604+
@objc func stringNewtype(a: SNTErrorDomain) {}
605+
@objc func stringNewtypeOptional(a: SNTErrorDomain?) {}
606+
@objc func intNewtype(a: MyInt) {}
607+
@objc func intNewtypeOptional(a: MyInt?) {} // expected-error {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}}
608+
}
609+

test/IRGen/newtype.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,31 @@ public func compareInits() -> Bool {
130130
public func anchor() -> Bool {
131131
return false
132132
}
133+
134+
class ObjCTest {
135+
// CHECK-LABEL: define hidden %0* @_TToFC7newtype8ObjCTest19optionalPassThroughfGSqVSC11ErrorDomain_GSqS1__
136+
// CHECK: [[CASTED:%.+]] = ptrtoint %0* %2 to i{{32|64}}
137+
// CHECK: [[RESULT:%.+]] = call i{{32|64}} @_TFC7newtype8ObjCTest19optionalPassThroughfGSqVSC11ErrorDomain_GSqS1__(i{{32|64}} [[CASTED]], %C7newtype8ObjCTest* {{%.+}})
138+
// CHECK: [[OPAQUE_RESULT:%.+]] = inttoptr i{{32|64}} [[RESULT]] to %0*
139+
// CHECK: ret %0* [[OPAQUE_RESULT]]
140+
// CHECK: {{^}$}}
141+
142+
// OPT-LABEL: define hidden %0* @_TToFC7newtype8ObjCTest19optionalPassThroughfGSqVSC11ErrorDomain_GSqS1__
143+
// OPT: ret %0* %2
144+
// OPT: {{^}$}}
145+
@objc func optionalPassThrough(_ ed: ErrorDomain?) -> ErrorDomain? {
146+
return ed
147+
}
148+
149+
// CHECK-LABEL: define hidden i32 @_TToFC7newtype8ObjCTest18integerPassThroughfVSC5MyIntS1_
150+
// CHECK: [[RESULT:%.+]] = call i32 @_TFC7newtype8ObjCTest18integerPassThroughfVSC5MyIntS1_(i32 %2, %C7newtype8ObjCTest* {{%.+}})
151+
// CHECK: ret i32 [[RESULT]]
152+
// CHECK: {{^}$}}
153+
154+
// OPT-LABEL: define hidden i32 @_TToFC7newtype8ObjCTest18integerPassThroughfVSC5MyIntS1_
155+
// OPT: ret i32 %2
156+
// OPT: {{^}$}}
157+
@objc func integerPassThrough(_ num: MyInt) -> MyInt {
158+
return num
159+
}
160+
}

test/PrintAsObjC/newtype.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ class TestEnumLike : NSObject {
2828
func takesNewtypeArray(_ a: [EnumLikeStringWrapper]) {}
2929
// CHECK: - (void)takesNewtypeDictionary:(NSDictionary<EnumLikeStringWrapper, EnumLikeStringWrapper> * _Nonnull)a;
3030
func takesNewtypeDictionary(_ a: [EnumLikeStringWrapper: EnumLikeStringWrapper]) {}
31+
// CHECK: - (void)takesNewtypeOptional:(EnumLikeStringWrapper _Nullable)a;
32+
func takesNewtypeOptional(_ a: EnumLikeStringWrapper?) {}
3133
}
3234
// CHECK: @end
3335

@@ -39,6 +41,8 @@ class TestStructLike : NSObject {
3941
func takesNewtypeArray(_ a: [StructLikeStringWrapper]) {}
4042
// CHECK: - (void)takesNewtypeDictionary:(NSDictionary<StructLikeStringWrapper, StructLikeStringWrapper> * _Nonnull)a;
4143
func takesNewtypeDictionary(_ a: [StructLikeStringWrapper: StructLikeStringWrapper]) {}
44+
// CHECK: - (void)takesNewtypeOptional:(StructLikeStringWrapper _Nullable)a;
45+
func takesNewtypeOptional(_ a: StructLikeStringWrapper?) {}
4246
}
4347
// CHECK: @end
4448

test/SILGen/Inputs/usr/include/newtype.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ __attribute((swift_name("ErrorDomain")));
55
extern const SNTErrorDomain SNTErrTwo;
66
extern const SNTErrorDomain SNTErrorDomainThree;
77
extern const SNTErrorDomain SNTFourErrorDomain;
8+
9+
typedef NSInteger MyInt __attribute((swift_newtype(struct)));

test/SILGen/newtype.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,17 @@ func getRawValue(ed: ErrorDomain) -> String {
3939
// CHECK-RAW: [[STRING_RESULT:%[0-9]+]] = load [[STRING_RESULT_ADDR]]
4040
// CHECK-RAW: return [[STRING_RESULT]]
4141

42+
class ObjCTest {
43+
// CHECK-RAW-LABEL: sil hidden @_TFC7newtype8ObjCTest19optionalPassThroughfGSqVSC11ErrorDomain_GSqS1__ : $@convention(method) (@owned Optional<ErrorDomain>, @guaranteed ObjCTest) -> @owned Optional<ErrorDomain> {
44+
// CHECK-RAW: sil hidden [thunk] @_TToFC7newtype8ObjCTest19optionalPassThroughfGSqVSC11ErrorDomain_GSqS1__ : $@convention(objc_method) (Optional<ErrorDomain>, ObjCTest) -> Optional<ErrorDomain> {
45+
@objc func optionalPassThrough(_ ed: ErrorDomain?) -> ErrorDomain? {
46+
return ed
47+
}
48+
49+
// CHECK-RAW-LABEL: sil hidden @_TFC7newtype8ObjCTest18integerPassThroughfVSC5MyIntS1_ : $@convention(method) (MyInt, @guaranteed ObjCTest) -> MyInt {
50+
// CHECK-RAW: sil hidden [thunk] @_TToFC7newtype8ObjCTest18integerPassThroughfVSC5MyIntS1_ : $@convention(objc_method) (MyInt, ObjCTest) -> MyInt {
51+
@objc func integerPassThrough(_ ed: MyInt) -> MyInt {
52+
return ed
53+
}
54+
}
55+

0 commit comments

Comments
 (0)