Skip to content

Optional swift_newtype types are @objc if the raw type would be @objc. #4337

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4935,17 +4935,14 @@ Type TypeBase::getSwiftNewtypeUnderlyingType() {
return {};

// Make sure the clang node has swift_newtype attribute
if (!structDecl->getClangNode())
return {};
auto clangNode = structDecl->getClangNode();
if (!clangNode.getAsDecl() ||
!clangNode.castAsDecl()->getAttr<clang::SwiftNewtypeAttr>())
auto clangNode = structDecl->getClangDecl();
if (!clangNode || !clangNode->hasAttr<clang::SwiftNewtypeAttr>())
return {};

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

return {};
Expand Down
15 changes: 11 additions & 4 deletions lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2230,12 +2230,19 @@ getForeignRepresentable(Type type, ForeignLanguage language,
case ForeignLanguage::ObjectiveC:
if (isa<StructDecl>(nominal) || isa<EnumDecl>(nominal)) {
// Optional structs are not representable in (Objective-)C if they
// originally came from C, whether or not they are bridged. If they
// are defined in Swift, they are only representable if they are
// bridged (checked below).
// originally came from C, whether or not they are bridged, unless they
// came from swift_newtype. If they are defined in Swift, they are only
// representable if they are bridged (checked below).
if (wasOptional) {
if (nominal->hasClangNode())
if (nominal->hasClangNode()) {
Type underlyingType =
nominal->getDeclaredType()->getSwiftNewtypeUnderlyingType();
if (underlyingType) {
return getForeignRepresentable(OptionalType::get(underlyingType),
language, dc);
}
return failure();
}
break;
}
}
Expand Down
4 changes: 4 additions & 0 deletions test/ClangModules/Inputs/custom-modules/Newtype.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@import Foundation;

typedef NSString *__nonnull SNTErrorDomain __attribute((swift_newtype(struct)));
typedef NSInteger MyInt __attribute((swift_newtype(struct)));
4 changes: 4 additions & 0 deletions test/ClangModules/Inputs/custom-modules/module.map
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ module MissingHeader {
header "this-header-does-not-exist.h"
}

module Newtype {
header "Newtype.h"
}

module ObjCIRExtras {
header "ObjCIRExtras.h"
export *
Expand Down
8 changes: 8 additions & 0 deletions test/ClangModules/objc_parse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import AppKit
import AVFoundation

import Newtype
import objc_ext
import TestProtocols

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

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

28 changes: 28 additions & 0 deletions test/IRGen/newtype.swift
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,31 @@ public func compareInits() -> Bool {
public func anchor() -> Bool {
return false
}

class ObjCTest {
// CHECK-LABEL: define hidden %0* @_TToFC7newtype8ObjCTest19optionalPassThroughfGSqVSC11ErrorDomain_GSqS1__
// CHECK: [[CASTED:%.+]] = ptrtoint %0* %2 to i{{32|64}}
// CHECK: [[RESULT:%.+]] = call i{{32|64}} @_TFC7newtype8ObjCTest19optionalPassThroughfGSqVSC11ErrorDomain_GSqS1__(i{{32|64}} [[CASTED]], %C7newtype8ObjCTest* {{%.+}})
// CHECK: [[OPAQUE_RESULT:%.+]] = inttoptr i{{32|64}} [[RESULT]] to %0*
// CHECK: ret %0* [[OPAQUE_RESULT]]
// CHECK: {{^}$}}

// OPT-LABEL: define hidden %0* @_TToFC7newtype8ObjCTest19optionalPassThroughfGSqVSC11ErrorDomain_GSqS1__
// OPT: ret %0* %2
// OPT: {{^}$}}
@objc func optionalPassThrough(_ ed: ErrorDomain?) -> ErrorDomain? {
return ed
}

// CHECK-LABEL: define hidden i32 @_TToFC7newtype8ObjCTest18integerPassThroughfVSC5MyIntS1_
// CHECK: [[RESULT:%.+]] = call i32 @_TFC7newtype8ObjCTest18integerPassThroughfVSC5MyIntS1_(i32 %2, %C7newtype8ObjCTest* {{%.+}})
// CHECK: ret i32 [[RESULT]]
// CHECK: {{^}$}}

// OPT-LABEL: define hidden i32 @_TToFC7newtype8ObjCTest18integerPassThroughfVSC5MyIntS1_
// OPT: ret i32 %2
// OPT: {{^}$}}
@objc func integerPassThrough(_ num: MyInt) -> MyInt {
return num
}
}
4 changes: 4 additions & 0 deletions test/PrintAsObjC/newtype.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class TestEnumLike : NSObject {
func takesNewtypeArray(_ a: [EnumLikeStringWrapper]) {}
// CHECK: - (void)takesNewtypeDictionary:(NSDictionary<EnumLikeStringWrapper, EnumLikeStringWrapper> * _Nonnull)a;
func takesNewtypeDictionary(_ a: [EnumLikeStringWrapper: EnumLikeStringWrapper]) {}
// CHECK: - (void)takesNewtypeOptional:(EnumLikeStringWrapper _Nullable)a;
func takesNewtypeOptional(_ a: EnumLikeStringWrapper?) {}
}
// CHECK: @end

Expand All @@ -39,6 +41,8 @@ class TestStructLike : NSObject {
func takesNewtypeArray(_ a: [StructLikeStringWrapper]) {}
// CHECK: - (void)takesNewtypeDictionary:(NSDictionary<StructLikeStringWrapper, StructLikeStringWrapper> * _Nonnull)a;
func takesNewtypeDictionary(_ a: [StructLikeStringWrapper: StructLikeStringWrapper]) {}
// CHECK: - (void)takesNewtypeOptional:(StructLikeStringWrapper _Nullable)a;
func takesNewtypeOptional(_ a: StructLikeStringWrapper?) {}
}
// CHECK: @end

2 changes: 2 additions & 0 deletions test/SILGen/Inputs/usr/include/newtype.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ __attribute((swift_name("ErrorDomain")));
extern const SNTErrorDomain SNTErrTwo;
extern const SNTErrorDomain SNTErrorDomainThree;
extern const SNTErrorDomain SNTFourErrorDomain;

typedef NSInteger MyInt __attribute((swift_newtype(struct)));
14 changes: 14 additions & 0 deletions test/SILGen/newtype.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,17 @@ func getRawValue(ed: ErrorDomain) -> String {
// CHECK-RAW: [[STRING_RESULT:%[0-9]+]] = load [[STRING_RESULT_ADDR]]
// CHECK-RAW: return [[STRING_RESULT]]

class ObjCTest {
// CHECK-RAW-LABEL: sil hidden @_TFC7newtype8ObjCTest19optionalPassThroughfGSqVSC11ErrorDomain_GSqS1__ : $@convention(method) (@owned Optional<ErrorDomain>, @guaranteed ObjCTest) -> @owned Optional<ErrorDomain> {
// CHECK-RAW: sil hidden [thunk] @_TToFC7newtype8ObjCTest19optionalPassThroughfGSqVSC11ErrorDomain_GSqS1__ : $@convention(objc_method) (Optional<ErrorDomain>, ObjCTest) -> Optional<ErrorDomain> {
@objc func optionalPassThrough(_ ed: ErrorDomain?) -> ErrorDomain? {
return ed
}

// CHECK-RAW-LABEL: sil hidden @_TFC7newtype8ObjCTest18integerPassThroughfVSC5MyIntS1_ : $@convention(method) (MyInt, @guaranteed ObjCTest) -> MyInt {
// CHECK-RAW: sil hidden [thunk] @_TToFC7newtype8ObjCTest18integerPassThroughfVSC5MyIntS1_ : $@convention(objc_method) (MyInt, ObjCTest) -> MyInt {
@objc func integerPassThrough(_ ed: MyInt) -> MyInt {
return ed
}
}