Skip to content

Commit 0ff871a

Browse files
committed
[ClangImporter] Handle NS_TYPED_ENUMs of NSUIntegers in system headers
The special rule for NSUInteger in system headers was colliding with the behavior of NS_TYPED_ENUM (swift_newtype/swift_wrapper). Let NS_TYPED_ENUM take precedence here. (I also future-proofed this for BOOL and Boolean, but those don't NS_TYPED_ENUM correctly right now for another reason: Bool is bridged to Objective-C via NSNumber, but ObjCBool is not, even though it could be.) rdar://problem/50076612
1 parent f88be05 commit 0ff871a

File tree

5 files changed

+95
-56
lines changed

5 files changed

+95
-56
lines changed

lib/ClangImporter/ImportType.cpp

Lines changed: 71 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -667,24 +667,41 @@ namespace {
667667
if (!decl) return Visit(type->desugar());
668668

669669
Type mappedType = getAdjustedTypeDeclReferenceType(decl);
670-
ImportHint hint = ImportHint::None;
671670

672671
if (getSwiftNewtypeAttr(type->getDecl(), Impl.CurrentVersion)) {
673672
if (isCFTypeDecl(type->getDecl())) {
674-
hint = ImportHint::SwiftNewtypeFromCFPointer;
675-
} else {
673+
return {mappedType, ImportHint::SwiftNewtypeFromCFPointer};
674+
}
675+
676+
auto underlying = Visit(type->getDecl()->getUnderlyingType());
677+
switch (underlying.Hint) {
678+
case ImportHint::None:
679+
case ImportHint::Void:
680+
case ImportHint::Block:
681+
case ImportHint::CFPointer:
682+
case ImportHint::ObjCPointer:
683+
case ImportHint::CFunctionPointer:
684+
case ImportHint::OtherPointer:
685+
case ImportHint::SwiftNewtypeFromCFPointer:
686+
return {mappedType, underlying.Hint};
687+
688+
case ImportHint::BOOL:
689+
case ImportHint::Boolean:
690+
case ImportHint::NSUInteger:
691+
// Toss out the special rules for these types; we still want to
692+
// import as a wrapper.
693+
return {mappedType, ImportHint::None};
694+
695+
case ImportHint::ObjCBridged:
676696
// If the underlying type was bridged, the wrapper type is
677-
// only useful in bridged cases.
678-
auto underlying = Visit(type->getDecl()->getUnderlyingType());
679-
if (underlying.Hint == ImportHint::ObjCBridged) {
680-
return { underlying.AbstractType,
681-
ImportHint(ImportHint::ObjCBridged, mappedType) };
682-
}
683-
hint = underlying.Hint;
697+
// only useful in bridged cases. Exit early.
698+
return { underlying.AbstractType,
699+
ImportHint(ImportHint::ObjCBridged, mappedType) };
684700
}
701+
}
685702

686703
// For certain special typedefs, we don't want to use the imported type.
687-
} else if (auto specialKind = Impl.getSpecialTypedefKind(type->getDecl())) {
704+
if (auto specialKind = Impl.getSpecialTypedefKind(type->getDecl())) {
688705
switch (specialKind.getValue()) {
689706
case MappedTypeNameKind::DoNothing:
690707
case MappedTypeNameKind::DefineAndUse:
@@ -696,6 +713,7 @@ namespace {
696713
break;
697714
}
698715

716+
ImportHint hint = ImportHint::None;
699717
if (type->getDecl()->getName() == "BOOL") {
700718
hint = ImportHint::BOOL;
701719
} else if (type->getDecl()->getName() == "Boolean") {
@@ -711,64 +729,62 @@ namespace {
711729
hint = ImportHint::OtherPointer;
712730
}
713731
// Any other interesting mapped types should be hinted here.
732+
return { mappedType, hint };
733+
}
714734

715735
// Otherwise, recurse on the underlying type. We need to recompute
716736
// the hint, and if the typedef uses different bridgeability than the
717737
// context then we may also need to bypass the typedef.
718-
} else {
719-
auto underlyingType = type->desugar();
738+
auto underlyingType = type->desugar();
720739

721-
// Figure out the bridgeability we would normally use for this typedef.
722-
auto typedefBridgeability =
740+
// Figure out the bridgeability we would normally use for this typedef.
741+
auto typedefBridgeability =
723742
getTypedefBridgeability(type->getDecl(), underlyingType);
724743

725-
// Figure out the typedef we should actually use.
726-
auto underlyingBridgeability = Bridging;
727-
SwiftTypeConverter innerConverter(Impl, AllowNSUIntegerAsInt,
728-
underlyingBridgeability);
729-
auto underlyingResult = innerConverter.Visit(underlyingType);
730-
731-
// If we used different bridgeability than this typedef normally
732-
// would because we're in a non-bridgeable context, and therefore
733-
// the underlying type is different from the mapping of the typedef,
734-
// use the underlying type.
735-
if (underlyingBridgeability != typedefBridgeability &&
736-
!underlyingResult.AbstractType->isEqual(mappedType)) {
737-
return underlyingResult;
738-
}
744+
// Figure out the typedef we should actually use.
745+
auto underlyingBridgeability = Bridging;
746+
SwiftTypeConverter innerConverter(Impl, AllowNSUIntegerAsInt,
747+
underlyingBridgeability);
748+
auto underlyingResult = innerConverter.Visit(underlyingType);
749+
750+
// If we used different bridgeability than this typedef normally
751+
// would because we're in a non-bridgeable context, and therefore
752+
// the underlying type is different from the mapping of the typedef,
753+
// use the underlying type.
754+
if (underlyingBridgeability != typedefBridgeability &&
755+
!underlyingResult.AbstractType->isEqual(mappedType)) {
756+
return underlyingResult;
757+
}
739758

740759
#ifndef NDEBUG
741-
switch (underlyingResult.Hint) {
742-
case ImportHint::Block:
743-
case ImportHint::ObjCBridged:
744-
// Bridging is fine for Objective-C and blocks.
760+
switch (underlyingResult.Hint) {
761+
case ImportHint::Block:
762+
case ImportHint::ObjCBridged:
763+
// Bridging is fine for Objective-C and blocks.
764+
break;
765+
case ImportHint::NSUInteger:
766+
// NSUInteger might be imported as Int rather than UInt depending
767+
// on where the import lives.
768+
if (underlyingResult.AbstractType->getAnyNominal() ==
769+
Impl.SwiftContext.getIntDecl())
745770
break;
746-
case ImportHint::NSUInteger:
747-
// NSUInteger might be imported as Int rather than UInt depending
748-
// on where the import lives.
749-
if (underlyingResult.AbstractType->getAnyNominal() ==
750-
Impl.SwiftContext.getIntDecl())
751-
break;
752-
LLVM_FALLTHROUGH;
753-
default:
754-
if (!underlyingResult.AbstractType->isEqual(mappedType)) {
755-
underlyingResult.AbstractType->dump();
756-
mappedType->dump();
757-
}
758-
assert(underlyingResult.AbstractType->isEqual(mappedType) &&
759-
"typedef without special typedef kind was mapped "
760-
"differently from its underlying type?");
771+
LLVM_FALLTHROUGH;
772+
default:
773+
if (!underlyingResult.AbstractType->isEqual(mappedType)) {
774+
underlyingResult.AbstractType->dump();
775+
mappedType->dump();
761776
}
777+
assert(underlyingResult.AbstractType->isEqual(mappedType) &&
778+
"typedef without special typedef kind was mapped "
779+
"differently from its underlying type?");
780+
}
762781
#endif
763-
hint = underlyingResult.Hint;
764782

765-
// If the imported typealias is unavailable, return the
766-
// underlying type.
767-
if (decl->getAttrs().isUnavailable(Impl.SwiftContext))
768-
mappedType = underlyingResult.AbstractType;
769-
}
783+
// If the imported typealias is unavailable, return the underlying type.
784+
if (decl->getAttrs().isUnavailable(Impl.SwiftContext))
785+
return underlyingResult;
770786

771-
return { mappedType, hint };
787+
return { mappedType, underlyingResult.Hint };
772788
}
773789

774790
#define SUGAR_TYPE(KIND) \

test/ClangImporter/Inputs/custom-modules/Newtype.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,6 @@ typedef NSError *ErrorNewType __attribute((swift_newtype(struct)));
107107

108108
void testErrorDictionary(NSDictionary<NSError *, NSString *> * _Nonnull);
109109
void testErrorDictionaryNewtype(NSDictionary<ErrorNewType, NSString *> * _Nonnull);
110+
111+
typedef NSUInteger NSUIntegerNewType __attribute((swift_newtype(struct)));
112+
extern const NSUIntegerNewType NSUIntegerNewTypeConstant;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@import Foundation;
2+
3+
typedef NSUInteger NSUIntegerSystemNewType __attribute((swift_newtype(struct)));
4+
extern const NSUIntegerSystemNewType NSUIntegerSystemNewTypeConstant;
5+

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ module Newtype {
8787
header "Newtype.h"
8888
}
8989

90+
module NewtypeSystem [system] {
91+
header "NewtypeSystem.h"
92+
}
93+
9094
module ImportAsMember {
9195
export *
9296

@@ -224,4 +228,4 @@ module ConditionallyFoo {
224228

225229
module ForwardDeclarationsHelper {
226230
header "ForwardDeclarationsHelper.h"
227-
}
231+
}

test/ClangImporter/objc_parse.swift

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

88
import Newtype
9+
import NewtypeSystem
910
import objc_ext
1011
import TestProtocols
1112
import TypeAndValue
@@ -683,3 +684,13 @@ func testErrorNewtype() {
683684
testErrorDictionary(3) // expected-error {{cannot convert value of type 'Int' to expected argument type '[AnyHashable : String]'}}
684685
testErrorDictionaryNewtype(3) // expected-error {{cannot convert value of type 'Int' to expected argument type '[AnyHashable : String]'}}
685686
}
687+
688+
func testNSUIntegerNewtype() {
689+
let _: NSUIntegerNewType = NSUIntegerNewType(4)
690+
let _: UInt = NSUIntegerNewType(4).rawValue
691+
let _: NSUIntegerNewType = NSUIntegerNewType.constant
692+
693+
let _: NSUIntegerSystemNewType = NSUIntegerSystemNewType(4)
694+
let _: Int = NSUIntegerSystemNewType(4).rawValue
695+
let _: NSUIntegerSystemNewType = NSUIntegerSystemNewType.constant
696+
}

0 commit comments

Comments
 (0)