Skip to content

[SILGen] Recognize swift_newtype-ed CF foreign class types #2463

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
May 10, 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
4 changes: 4 additions & 0 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,10 @@ class alignas(1 << TypeAlignInBits) TypeBase {
return getAnyOptionalObjectType(ignored);
}

// Return type underlying type of a swift_newtype annotated imported struct;
// otherwise, return the null type.
Type getSwiftNewtypeUnderlyingType();

/// Return the type T after looking through all of the optional or
/// implicitly-unwrapped optional types.
Type lookThroughAllAnyOptionalTypes();
Expand Down
22 changes: 22 additions & 0 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4655,3 +4655,25 @@ void InfixOperatorDecl::collectOperatorKeywordRanges(SmallVectorImpl
bool FuncDecl::isDeferBody() const {
return getName() == getASTContext().getIdentifier("$defer");
}

Type TypeBase::getSwiftNewtypeUnderlyingType() {
auto structDecl = getStructOrBoundGenericStruct();
if (!structDecl)
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>())
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")
return varDecl->getType();

return {};
}
17 changes: 1 addition & 16 deletions lib/ClangImporter/ImportType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1005,21 +1005,6 @@ static bool isNSString(Type type) {
return false;
}

static Type findNewtypeUnderlyingType(Type newtype) {
auto structDecl = newtype->getStructOrBoundGenericStruct();
assert(structDecl && structDecl->getClangNode() &&
structDecl->getClangNode().getAsDecl() &&
structDecl->getClangNode()
.castAsDecl()
->getAttr<clang::SwiftNewtypeAttr>() &&
"Called on a non-swift_newtype decl");
for (auto member : structDecl->getMembers())
if (auto varDecl = dyn_cast<VarDecl>(member))
if (varDecl->getName().str() == "rawValue")
return varDecl->getType();
assert(0 && "no rawValue variable member");
}

static Type adjustTypeForConcreteImport(ClangImporter::Implementation &impl,
clang::QualType clangType,
Type importedType,
Expand Down Expand Up @@ -1205,7 +1190,7 @@ static Type adjustTypeForConcreteImport(ClangImporter::Implementation &impl,
// then we have to for it to be unmanaged
if (hint == ImportHint::SwiftNewtypeFromCFPointer &&
!isCFAudited(importKind)) {
auto underlyingType = findNewtypeUnderlyingType(importedType);
auto underlyingType = importedType->getSwiftNewtypeUnderlyingType();
if (underlyingType)
importedType = getUnmanagedType(impl, underlyingType);
}
Expand Down
14 changes: 13 additions & 1 deletion lib/SIL/SILFunctionType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -404,8 +404,20 @@ enum class ConventionsKind : uint8_t {
if (substTy->getClassOrBoundGenericClass()
&& substTy->getClassOrBoundGenericClass()->isForeign())
return false;
// swift_newtype-ed CF type as foreign class
if (auto typedefTy = clangTy->getAs<clang::TypedefType>()) {
if (typedefTy->getDecl()->getAttr<clang::SwiftNewtypeAttr>()) {
// Make sure that we actually made the struct during import
if (auto underlyingType =
substTy->getSwiftNewtypeUnderlyingType()) {
if (underlyingType->getClassOrBoundGenericClass() &&
underlyingType->getClassOrBoundGenericClass()->isForeign())
return false;
}
}
}
}

return true;
}
return false;
Expand Down
28 changes: 26 additions & 2 deletions test/IDE/Inputs/custom-modules/Newtype.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,34 @@ typedef CFStringRef CFNewType __attribute((swift_newtype(struct)));
// CF audited
_Pragma("clang arc_cf_code_audited begin")
extern const CFNewType MyCFNewTypeValue;
CFNewType FooAudited(void);
extern CFNewType FooAudited(void);
_Pragma("clang arc_cf_code_audited end")
extern const CFNewType MyCFNewTypeValueUnauditedButConst;

// un-audited CFStringRef
extern CFNewType MyCFNewTypeValueUnaudited;
CFNewType FooUnaudited(void);
extern CFNewType FooUnaudited(void);

// Tests to show identical calling convention / binary representation for
// new_type and non-new_type
typedef CFStringRef MyABINewType __attribute((swift_newtype(struct)));
typedef CFStringRef MyABIOldType;
_Pragma("clang arc_cf_code_audited begin")
extern const MyABINewType kMyABINewTypeGlobal;
extern const MyABIOldType kMyABIOldTypeGlobal;
extern MyABINewType getMyABINewType(void);
extern MyABIOldType getMyABIOldType(void);
extern void takeMyABINewType(MyABINewType);
extern void takeMyABIOldType(MyABIOldType);

extern void takeMyABINewTypeNonNull(__nonnull MyABINewType);
extern void takeMyABIOldTypeNonNull(__nonnull MyABIOldType);
_Pragma("clang arc_cf_code_audited end")

typedef NSString *MyABINewTypeNS __attribute((swift_newtype(struct)));
typedef NSString *MyABIOldTypeNS;
extern MyABINewTypeNS getMyABINewTypeNS(void);
extern MyABIOldTypeNS getMyABIOldTypeNS(void);
extern void takeMyABINewTypeNonNullNS(__nonnull MyABINewTypeNS);
extern void takeMyABIOldTypeNonNullNS(__nonnull MyABIOldTypeNS);

33 changes: 33 additions & 0 deletions test/IRGen/newtype.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,36 @@ public func getUnmanagedCFNewType(useVar: Bool) -> Unmanaged<CFString> {
// witness table.
public func hasArrayOfClosedEnums(closed: [ClosedEnum]) {
}

// CHECK-LABEL: _TF7newtype11compareABIsFT_T_
public func compareABIs() {
let new = getMyABINewType()
let old = getMyABIOldType()
takeMyABINewType(new)
takeMyABIOldType(old)

takeMyABINewTypeNonNull(new!)
takeMyABIOldTypeNonNull(old!)

let newNS = getMyABINewTypeNS()
let oldNS = getMyABIOldTypeNS()
takeMyABINewTypeNonNullNS(newNS!)
takeMyABIOldTypeNonNullNS(oldNS!)

// Make sure that the calling conventions align correctly, that is we don't
// have double-indirection or anything else like that
// CHECK: declare %struct.__CFString* @getMyABINewType() #0
// CHECK: declare %struct.__CFString* @getMyABIOldType() #0
//
// CHECK: declare void @takeMyABINewType(%struct.__CFString*) #0
// CHECK: declare void @takeMyABIOldType(%struct.__CFString*) #0
//
// CHECK: declare void @takeMyABINewTypeNonNull(%struct.__CFString*) #0
// CHECK: declare void @takeMyABIOldTypeNonNull(%struct.__CFString*) #0
//
// CHECK: declare %0* @getMyABINewTypeNS() #0
// CHECK: declare %0* @getMyABIOldTypeNS() #0
//
// CHECK: declare void @takeMyABINewTypeNonNullNS(%0*) #0
// CHECK: declare void @takeMyABIOldTypeNonNullNS(%0*) #0
}