Skip to content

Commit 8547b54

Browse files
committed
[PrintAsObjC] Handle imported swift_newtype typedefs.
A typedef with the swift_newtype attribute is imported as a struct wrapping the underlying type instead of just a typealias. If the underlying type is a bridged type (like String), the newtype struct is bridged as well. However, we don't want to use that type when bridging back to Objective-C, because (1) Objective-C header generation is done too late to fill out the _ObjectiveCBridgeable conformance for the type, so if it wasn't type-checked then the conformance won't have the original type in it. (2) There's a perfectly good typedef we should be using anyway. Just use the type as written in Objective-C (instead of crashing). Finishes rdar://problem/26372925.
1 parent 1bdd092 commit 8547b54

File tree

3 files changed

+74
-6
lines changed

3 files changed

+74
-6
lines changed

lib/PrintAsObjC/PrintAsObjC.cpp

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
2626
#include "swift/IDE/CommentConversion.h"
2727
#include "clang/AST/ASTContext.h"
28+
#include "clang/AST/Attr.h"
2829
#include "clang/AST/Decl.h"
2930
#include "clang/AST/DeclObjC.h"
3031
#include "clang/Basic/Module.h"
@@ -786,6 +787,10 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
786787
bool printIfObjCBridgeable(const NominalTypeDecl *nominal,
787788
ArrayRef<Type> typeArgs,
788789
Optional<OptionalTypeKind> optionalKind) {
790+
// Print imported bridgeable decls as their unbridged type.
791+
if (nominal->hasClangNode())
792+
return false;
793+
789794
auto &ctx = nominal->getASTContext();
790795

791796
// Dig out the ObjectiveCBridgeable protocol.
@@ -956,7 +961,8 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
956961
ASTContext &ctx = M.getASTContext();
957962
auto &clangASTContext = ctx.getClangModuleLoader()->getClangASTContext();
958963
clang::QualType clangTy = clangASTContext.getTypeDeclType(clangTypeDecl);
959-
return clangTy->isPointerType();
964+
return clangTy->isPointerType() || clangTy->isBlockPointerType() ||
965+
clangTy->isObjCObjectPointerType();
960966
}
961967

962968
void visitNameAliasType(NameAliasType *aliasTy,
@@ -971,10 +977,8 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
971977
auto *clangTypeDecl = cast<clang::TypeDecl>(alias->getClangDecl());
972978
os << clangTypeDecl->getName();
973979

974-
if (aliasTy->hasReferenceSemantics() ||
975-
isClangPointerType(clangTypeDecl)) {
980+
if (isClangPointerType(clangTypeDecl))
976981
printNullability(optionalKind);
977-
}
978982
return;
979983
}
980984

@@ -1022,6 +1026,11 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
10221026

10231027
maybePrintTagKeyword(SD);
10241028
os << getNameForObjC(SD);
1029+
1030+
// Handle swift_newtype applied to a pointer type.
1031+
if (auto *clangDecl = cast_or_null<clang::TypeDecl>(SD->getClangDecl()))
1032+
if (isClangPointerType(clangDecl))
1033+
printNullability(optionalKind);
10251034
}
10261035

10271036
/// Print a collection element type using Objective-C generics syntax.
@@ -1030,12 +1039,22 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
10301039
void printCollectionElement(Type ty) {
10311040
ASTContext &ctx = M.getASTContext();
10321041

1042+
auto isSwiftNewtype = [&ctx](const StructDecl *SD) -> bool {
1043+
if (!SD)
1044+
return false;
1045+
auto *clangDecl = SD->getClangDecl();
1046+
if (!clangDecl)
1047+
return false;
1048+
return clangDecl->hasAttr<clang::SwiftNewtypeAttr>();
1049+
};
1050+
10331051
// Use the type as bridged to Objective-C unless the element type is itself
1034-
// a collection.
1052+
// an imported type or a collection.
10351053
const StructDecl *SD = ty->getStructOrBoundGenericStruct();
10361054
if (SD != ctx.getArrayDecl() &&
10371055
SD != ctx.getDictionaryDecl() &&
1038-
SD != ctx.getSetDecl()) {
1056+
SD != ctx.getSetDecl() &&
1057+
!isSwiftNewtype(SD)) {
10391058
ty = *ctx.getBridgedToObjC(&M, ty, /*resolver*/nullptr);
10401059
}
10411060

test/PrintAsObjC/Inputs/newtype.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// This file is meant to be used with the mock SDK, not the real one.
2+
#import <Foundation.h>
3+
4+
typedef NSString *StructLikeStringWrapper __attribute__((swift_newtype(struct)));
5+
typedef NSString *EnumLikeStringWrapper __attribute__((swift_newtype(enum)));

test/PrintAsObjC/newtype.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Please keep this file in alphabetical order!
2+
3+
// REQUIRES: objc_interop
4+
5+
// RUN: rm -rf %t && mkdir %t
6+
7+
// FIXME: BEGIN -enable-source-import hackaround
8+
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift
9+
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/CoreGraphics.swift
10+
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/Foundation.swift
11+
// FIXME: END -enable-source-import hackaround
12+
13+
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -import-objc-header %S/Inputs/newtype.h -enable-swift-newtype -emit-module -o %t %s
14+
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -import-objc-header %S/Inputs/newtype.h -enable-swift-newtype -parse-as-library %t/newtype.swiftmodule -parse -emit-objc-header-path %t/newtype.h
15+
16+
// RUN: FileCheck %s < %t/newtype.h
17+
18+
// RUN: %check-in-clang %t/newtype.h
19+
// RUN: %check-in-clang -fno-modules -Qunused-arguments %t/newtype.h
20+
21+
import Foundation
22+
23+
// CHECK-LABEL: @interface TestEnumLike : NSObject
24+
class TestEnumLike : NSObject {
25+
// CHECK: - (void)takesNewtype:(EnumLikeStringWrapper _Nonnull)a;
26+
func takesNewtype(_ a: EnumLikeStringWrapper) {}
27+
// CHECK: - (void)takesNewtypeArray:(NSArray<EnumLikeStringWrapper> * _Nonnull)a;
28+
func takesNewtypeArray(_ a: [EnumLikeStringWrapper]) {}
29+
// CHECK: - (void)takesNewtypeDictionary:(NSDictionary<EnumLikeStringWrapper, EnumLikeStringWrapper> * _Nonnull)a;
30+
func takesNewtypeDictionary(_ a: [EnumLikeStringWrapper: EnumLikeStringWrapper]) {}
31+
}
32+
// CHECK: @end
33+
34+
// CHECK-LABEL: @interface TestStructLike : NSObject
35+
class TestStructLike : NSObject {
36+
// CHECK: - (void)takesNewtype:(StructLikeStringWrapper _Nonnull)a;
37+
func takesNewtype(_ a: StructLikeStringWrapper) {}
38+
// CHECK: - (void)takesNewtypeArray:(NSArray<StructLikeStringWrapper> * _Nonnull)a;
39+
func takesNewtypeArray(_ a: [StructLikeStringWrapper]) {}
40+
// CHECK: - (void)takesNewtypeDictionary:(NSDictionary<StructLikeStringWrapper, StructLikeStringWrapper> * _Nonnull)a;
41+
func takesNewtypeDictionary(_ a: [StructLikeStringWrapper: StructLikeStringWrapper]) {}
42+
}
43+
// CHECK: @end
44+

0 commit comments

Comments
 (0)