Skip to content

Commit

Permalink
Hack: Force UIEdgeInsets.zero to always come from the SDK
Browse files Browse the repository at this point in the history
...instead of relying on the one in the overlay in pre-4.2 versions of
Swift. This caused crashes in deserialization, which (deliberately)
doesn't respect availability.

There are three changes here:

- Remove UIEdgeInsets.zero and UIOffset.zero from the UIKit overlay.
- Always use the 4.2 name for UIEdgeInsetsZero and UIOffsetZero from
  the underlying UIKit framework. (This is the nested name.)
- Ignore the unavailability messages for those two constants in
  pre-4.2 Swift, since we're now relying on them being present.

The latter two, the compiler changes, can go away once UIKit's API
notes no longer specify different pre-4.2 behavior, but meanwhile we
need to keep compatibility with the SDKs released in Xcode 10b1.

https://bugs.swift.org/browse/SR-7879
  • Loading branch information
jrose-apple committed Jun 12, 2018
1 parent d6bf8e3 commit a467bea
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 16 deletions.
20 changes: 20 additions & 0 deletions lib/ClangImporter/ImportDecl.cpp
Expand Up @@ -7341,6 +7341,21 @@ getSwiftNameFromClangName(StringRef replacement) {
return SwiftContext.AllocateCopy(StringRef(renamed));
}

bool importer::isSpecialUIKitStructZeroProperty(const clang::NamedDecl *decl) {
// FIXME: Once UIKit removes the "nonswift" availability in their versioned
// API notes, this workaround can go away.
auto *constant = dyn_cast<clang::VarDecl>(decl);
if (!constant)
return false;

clang::DeclarationName name = constant->getDeclName();
const clang::IdentifierInfo *ident = name.getAsIdentifierInfo();
if (!ident)
return false;

return ident->isStr("UIEdgeInsetsZero") || ident->isStr("UIOffsetZero");
}

/// Import Clang attributes as Swift attributes.
void ClangImporter::Implementation::importAttributes(
const clang::NamedDecl *ClangDecl,
Expand Down Expand Up @@ -7405,6 +7420,11 @@ void ClangImporter::Implementation::importAttributes(

// Is this our special "availability(swift, unavailable)" attribute?
if (Platform == "swift") {
// FIXME: Until Apple gets a chance to update UIKit's API notes, ignore
// the Swift-unavailability for certain properties.
if (isSpecialUIKitStructZeroProperty(ClangDecl))
continue;

auto replacement = avail->getReplacement();
StringRef swiftReplacement = "";
if (!replacement.empty())
Expand Down
6 changes: 6 additions & 0 deletions lib/ClangImporter/ImportName.cpp
Expand Up @@ -616,6 +616,12 @@ findSwiftNameAttr(const clang::Decl *decl, ImportNameVersion version) {

// Handle versioned API notes for Swift 3 and later. This is the common case.
if (version > ImportNameVersion::swift2()) {
// FIXME: Until Apple gets a chance to update UIKit's API notes, always use
// the new name for certain properties.
if (auto *namedDecl = dyn_cast<clang::NamedDecl>(decl))
if (importer::isSpecialUIKitStructZeroProperty(namedDecl))
version = ImportNameVersion::swift4_2();

const auto *activeAttr = decl->getAttr<clang::SwiftNameAttr>();
const clang::SwiftNameAttr *result = activeAttr;
clang::VersionTuple bestSoFar;
Expand Down
4 changes: 4 additions & 0 deletions lib/ClangImporter/ImporterImpl.h
Expand Up @@ -1383,6 +1383,10 @@ namespace importer {
/// Whether we should suppress the import of the given Clang declaration.
bool shouldSuppressDeclImport(const clang::Decl *decl);

/// Identifies certain UIKit constants that used to have overlay equivalents,
/// but are now renamed using the swift_name attribute.
bool isSpecialUIKitStructZeroProperty(const clang::NamedDecl *decl);

/// Finds a particular kind of nominal by looking through typealiases.
template <typename T>
static T *dynCastIgnoringCompatibilityAlias(Decl *D) {
Expand Down
16 changes: 0 additions & 16 deletions stdlib/public/SDK/UIKit/UIKit.swift
Expand Up @@ -21,22 +21,6 @@ import _SwiftUIKitOverlayShims
// UIGeometry
//===----------------------------------------------------------------------===//

extension UIEdgeInsets {
@available(swift, obsoleted: 4.2) // in 4.2 it is provided by the apinotes
public static var zero: UIEdgeInsets {
@_transparent // @fragile
get { return UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0) }
}
}

extension UIOffset {
@available(swift, obsoleted: 4.2) // in 4.2 it is provided by the apinotes
public static var zero: UIOffset {
@_transparent // @fragile
get { return UIOffset(horizontal: 0.0, vertical: 0.0) }
}
}

extension UIEdgeInsets : Equatable {
@_transparent // @fragile
public static func == (lhs: UIEdgeInsets, rhs: UIEdgeInsets) -> Bool {
Expand Down
10 changes: 10 additions & 0 deletions validation-test/Serialization/SR7879.swift
@@ -0,0 +1,10 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift -emit-module-path %t/main4.swiftmodule -swift-version 4 %s
// RUN: %target-build-swift -emit-module-path %t/main4_2.swiftmodule -swift-version 4.2 %s

// REQUIRES: OS=ios

import UIKit

public func testInsets(_: UIEdgeInsets = .zero) {}
public func testOffsets(_: UIOffset = .zero) {}

0 comments on commit a467bea

Please sign in to comment.