Skip to content

[5.1] [ClangImporter] Handle NS_TYPED_ENUMs of NSUIntegers in non-system headers #24349

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
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
138 changes: 75 additions & 63 deletions lib/ClangImporter/ImportType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,8 @@ namespace {
/// The source type is 'void'.
Void,

/// The source type is 'BOOL'.
BOOL,

/// The source type is 'Boolean'.
/// The source type is 'BOOL' or 'Boolean' -- a type mapped to Swift's
/// 'Bool'.
Boolean,

/// The source type is an Objective-C class type bridged to a Swift
Expand Down Expand Up @@ -129,7 +127,6 @@ namespace {
// See also ClangImporter.cpp's canImportAsOptional.
switch (hint) {
case ImportHint::None:
case ImportHint::BOOL:
case ImportHint::Boolean:
case ImportHint::NSUInteger:
case ImportHint::Void:
Expand Down Expand Up @@ -667,24 +664,40 @@ namespace {
if (!decl) return Visit(type->desugar());

Type mappedType = getAdjustedTypeDeclReferenceType(decl);
ImportHint hint = ImportHint::None;

if (getSwiftNewtypeAttr(type->getDecl(), Impl.CurrentVersion)) {
if (isCFTypeDecl(type->getDecl())) {
hint = ImportHint::SwiftNewtypeFromCFPointer;
} else {
return {mappedType, ImportHint::SwiftNewtypeFromCFPointer};
}

auto underlying = Visit(type->getDecl()->getUnderlyingType());
switch (underlying.Hint) {
case ImportHint::None:
case ImportHint::Void:
case ImportHint::Block:
case ImportHint::CFPointer:
case ImportHint::ObjCPointer:
case ImportHint::CFunctionPointer:
case ImportHint::OtherPointer:
case ImportHint::SwiftNewtypeFromCFPointer:
return {mappedType, underlying.Hint};

case ImportHint::Boolean:
case ImportHint::NSUInteger:
// Toss out the special rules for these types; we still want to
// import as a wrapper.
return {mappedType, ImportHint::None};

case ImportHint::ObjCBridged:
// If the underlying type was bridged, the wrapper type is
// only useful in bridged cases.
auto underlying = Visit(type->getDecl()->getUnderlyingType());
if (underlying.Hint == ImportHint::ObjCBridged) {
return { underlying.AbstractType,
ImportHint(ImportHint::ObjCBridged, mappedType) };
}
hint = underlying.Hint;
// only useful in bridged cases. Exit early.
return { underlying.AbstractType,
ImportHint(ImportHint::ObjCBridged, mappedType) };
}
}

// For certain special typedefs, we don't want to use the imported type.
} else if (auto specialKind = Impl.getSpecialTypedefKind(type->getDecl())) {
if (auto specialKind = Impl.getSpecialTypedefKind(type->getDecl())) {
switch (specialKind.getValue()) {
case MappedTypeNameKind::DoNothing:
case MappedTypeNameKind::DefineAndUse:
Expand All @@ -696,8 +709,9 @@ namespace {
break;
}

ImportHint hint = ImportHint::None;
if (type->getDecl()->getName() == "BOOL") {
hint = ImportHint::BOOL;
hint = ImportHint::Boolean;
} else if (type->getDecl()->getName() == "Boolean") {
// FIXME: Darwin only?
hint = ImportHint::Boolean;
Expand All @@ -711,64 +725,62 @@ namespace {
hint = ImportHint::OtherPointer;
}
// Any other interesting mapped types should be hinted here.
return { mappedType, hint };
}

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

// Figure out the bridgeability we would normally use for this typedef.
auto typedefBridgeability =
// Figure out the bridgeability we would normally use for this typedef.
auto typedefBridgeability =
getTypedefBridgeability(type->getDecl(), underlyingType);

// Figure out the typedef we should actually use.
auto underlyingBridgeability = Bridging;
SwiftTypeConverter innerConverter(Impl, AllowNSUIntegerAsInt,
underlyingBridgeability);
auto underlyingResult = innerConverter.Visit(underlyingType);

// If we used different bridgeability than this typedef normally
// would because we're in a non-bridgeable context, and therefore
// the underlying type is different from the mapping of the typedef,
// use the underlying type.
if (underlyingBridgeability != typedefBridgeability &&
!underlyingResult.AbstractType->isEqual(mappedType)) {
return underlyingResult;
}
// Figure out the typedef we should actually use.
auto underlyingBridgeability = Bridging;
SwiftTypeConverter innerConverter(Impl, AllowNSUIntegerAsInt,
underlyingBridgeability);
auto underlyingResult = innerConverter.Visit(underlyingType);

// If we used different bridgeability than this typedef normally
// would because we're in a non-bridgeable context, and therefore
// the underlying type is different from the mapping of the typedef,
// use the underlying type.
if (underlyingBridgeability != typedefBridgeability &&
!underlyingResult.AbstractType->isEqual(mappedType)) {
return underlyingResult;
}

#ifndef NDEBUG
switch (underlyingResult.Hint) {
case ImportHint::Block:
case ImportHint::ObjCBridged:
// Bridging is fine for Objective-C and blocks.
switch (underlyingResult.Hint) {
case ImportHint::Block:
case ImportHint::ObjCBridged:
// Bridging is fine for Objective-C and blocks.
break;
case ImportHint::NSUInteger:
// NSUInteger might be imported as Int rather than UInt depending
// on where the import lives.
if (underlyingResult.AbstractType->getAnyNominal() ==
Impl.SwiftContext.getIntDecl())
break;
case ImportHint::NSUInteger:
// NSUInteger might be imported as Int rather than UInt depending
// on where the import lives.
if (underlyingResult.AbstractType->getAnyNominal() ==
Impl.SwiftContext.getIntDecl())
break;
LLVM_FALLTHROUGH;
default:
if (!underlyingResult.AbstractType->isEqual(mappedType)) {
underlyingResult.AbstractType->dump();
mappedType->dump();
}
assert(underlyingResult.AbstractType->isEqual(mappedType) &&
"typedef without special typedef kind was mapped "
"differently from its underlying type?");
LLVM_FALLTHROUGH;
default:
if (!underlyingResult.AbstractType->isEqual(mappedType)) {
underlyingResult.AbstractType->dump();
mappedType->dump();
}
assert(underlyingResult.AbstractType->isEqual(mappedType) &&
"typedef without special typedef kind was mapped "
"differently from its underlying type?");
}
#endif
hint = underlyingResult.Hint;

// If the imported typealias is unavailable, return the
// underlying type.
if (decl->getAttrs().isUnavailable(Impl.SwiftContext))
mappedType = underlyingResult.AbstractType;
}
// If the imported typealias is unavailable, return the underlying type.
if (decl->getAttrs().isUnavailable(Impl.SwiftContext))
return underlyingResult;

return { mappedType, hint };
return { mappedType, underlyingResult.Hint };
}

#define SUGAR_TYPE(KIND) \
Expand Down Expand Up @@ -1365,8 +1377,8 @@ static ImportedType adjustTypeForConcreteImport(

// Turn BOOL and DarwinBoolean into Bool in contexts that can bridge types
// losslessly.
if ((hint == ImportHint::BOOL || hint == ImportHint::Boolean) &&
bridging == Bridgeability::Full && canBridgeTypes(importKind)) {
if (hint == ImportHint::Boolean && bridging == Bridgeability::Full &&
canBridgeTypes(importKind)) {
return {impl.SwiftContext.getBoolDecl()->getDeclaredType(), false};
}

Expand Down
3 changes: 3 additions & 0 deletions test/ClangImporter/Inputs/custom-modules/Newtype.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,6 @@ typedef NSError *ErrorNewType __attribute((swift_newtype(struct)));

void testErrorDictionary(NSDictionary<NSError *, NSString *> * _Nonnull);
void testErrorDictionaryNewtype(NSDictionary<ErrorNewType, NSString *> * _Nonnull);

typedef NSUInteger NSUIntegerNewType __attribute((swift_newtype(struct)));
extern const NSUIntegerNewType NSUIntegerNewTypeConstant;
5 changes: 5 additions & 0 deletions test/ClangImporter/Inputs/custom-modules/NewtypeSystem.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import Foundation;

typedef NSUInteger NSUIntegerSystemNewType __attribute((swift_newtype(struct)));
extern const NSUIntegerSystemNewType NSUIntegerSystemNewTypeConstant;

6 changes: 5 additions & 1 deletion test/ClangImporter/Inputs/custom-modules/module.map
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ module Newtype {
header "Newtype.h"
}

module NewtypeSystem [system] {
header "NewtypeSystem.h"
}

module ImportAsMember {
export *

Expand Down Expand Up @@ -224,4 +228,4 @@ module ConditionallyFoo {

module ForwardDeclarationsHelper {
header "ForwardDeclarationsHelper.h"
}
}
11 changes: 11 additions & 0 deletions test/ClangImporter/objc_parse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import AppKit
import AVFoundation

import Newtype
import NewtypeSystem
import objc_ext
import TestProtocols
import TypeAndValue
Expand Down Expand Up @@ -683,3 +684,13 @@ func testErrorNewtype() {
testErrorDictionary(3) // expected-error {{cannot convert value of type 'Int' to expected argument type '[AnyHashable : String]'}}
testErrorDictionaryNewtype(3) // expected-error {{cannot convert value of type 'Int' to expected argument type '[AnyHashable : String]'}}
}

func testNSUIntegerNewtype() {
let _: NSUIntegerNewType = NSUIntegerNewType(4)
let _: UInt = NSUIntegerNewType(4).rawValue
let _: NSUIntegerNewType = NSUIntegerNewType.constant

let _: NSUIntegerSystemNewType = NSUIntegerSystemNewType(4)
let _: Int = NSUIntegerSystemNewType(4).rawValue
let _: NSUIntegerSystemNewType = NSUIntegerSystemNewType.constant
}