Skip to content

[Sema] Perform availability checks in literals initializers #61805

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
21 changes: 15 additions & 6 deletions lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3254,13 +3254,22 @@ class ExprAvailabilityWalker : public ASTWalker {
maybeDiagStorageAccess(S->getDecl().getDecl(), S->getSourceRange(), DC);
}
}
if (auto *RLE = dyn_cast<RegexLiteralExpr>(E)) {
// Regex literals require both the Regex<Output> type to be available, as
// well as the initializer that is implicitly called.
auto Range = RLE->getSourceRange();
diagnoseDeclRefAvailability(Context.getRegexDecl(), Range);
diagnoseDeclRefAvailability(RLE->getInitializer(), Range);

if (auto *LE = dyn_cast<LiteralExpr>(E)) {
auto Range = LE->getSourceRange();
if (auto *RLE = dyn_cast<RegexLiteralExpr>(LE)) {
// Regex literals require both the Regex<Output> type to be available,
// as well as the initializer that is implicitly called.
diagnoseDeclRefAvailability(Context.getRegexDecl(), Range);
Comment on lines +3261 to +3263
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this is something that ought to be applied to the result type of a literal in general. e.g for:

@available(*, unavailable)
struct S: ExpressibleByIntegerLiteral {
  init(integerLiteral value: Int) {}
}
@available(*, unavailable)
typealias IntegerLiteralType = S

let i = 0

We're implicitly forming a call to S(integerLiteral: 0), but aren't erroring that it's unavailable.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A long term goal of mine is to run availability checks on the transformed AST so that we can catch these issues in a more generalized way. Not sure if that applies directly here but it seems like it might.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah these literal cases are tricky because we don't actually form the call in the AST, we emit them directly in SILGen

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this is something that ought to be applied to the result type of a literal in general.

I'll create an issue to track that case and look at it separately =]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
diagnoseDeclRefAvailability(LE->getInitializer(), Range);
}

if (auto *CE = dyn_cast<CollectionExpr>(E)) {
// Diagnose availability of implicit collection literal initializers.
diagnoseDeclRefAvailability(CE->getInitializer(), CE->getSourceRange());
}

if (auto *EE = dyn_cast<ErasureExpr>(E)) {
maybeDiagParameterizedExistentialErasure(EE, Where);
}
Expand Down
169 changes: 169 additions & 0 deletions test/Sema/availability_literals.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// RUN: %target-typecheck-verify-swift -swift-version 5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love the thoroughness of this test, thanks!


// REQUIRES: OS=macosx

// https://github.com/apple/swift/issues/61564
// ExpressibleByStringLiteral
struct SLD {}
@available(*, deprecated)
extension SLD: ExpressibleByStringLiteral {
init(stringLiteral value: StringLiteralType) {}
}

let _ = SLD(stringLiteral: "") // expected-warning{{'init(stringLiteral:)' is deprecated}}
let _: SLD = "" // expected-warning{{'init(stringLiteral:)' is deprecated}}


struct SLU {}
@available(macOS 100, *)
extension SLU: ExpressibleByStringLiteral {
init(stringLiteral value: StringLiteralType) {}
}

let _ = SLU(stringLiteral: "") // expected-error{{'init(stringLiteral:)' is only available in macOS 100 or newer}} expected-note{{add 'if #available' version check}}
let _: SLU = "" // expected-error{{'init(stringLiteral:)' is only available in macOS 100 or newer}} expected-note{{add 'if #available' version check}}

// ExpressibleByIntegerLiteral
struct ILD {}
@available(*, deprecated)
extension ILD: ExpressibleByIntegerLiteral {
init(integerLiteral value: IntegerLiteralType) {}
}

let _ = ILD(integerLiteral: 1) // expected-warning{{'init(integerLiteral:)' is deprecated}}
let _: ILD = 1 // expected-warning{{'init(integerLiteral:)' is deprecated}}

struct ILU {}

@available(macOS 100, *)
extension ILU: ExpressibleByIntegerLiteral {
init(integerLiteral value: IntegerLiteralType) {}
}

let _ = ILU(integerLiteral: 1) // expected-error{{'init(integerLiteral:)' is only available in macOS 100 or newer}} expected-note{{add 'if #available' version check}}
let _: ILU = 1 // expected-error{{'init(integerLiteral:)' is only available in macOS 100 or newer}} expected-note{{add 'if #available' version check}}

// ExpressibleByNilLiteral
struct NLD {}

@available(*, deprecated)
extension NLD: ExpressibleByNilLiteral {
init(nilLiteral: ()) {}
}

let _: NLD = .init(nilLiteral: ()) // expected-warning{{'init(nilLiteral:)' is deprecated}}
let _: NLD = nil // expected-warning{{'init(nilLiteral:)' is deprecated}}

struct NLU {}

@available(macOS 100, *)
extension NLU: ExpressibleByNilLiteral {
init(nilLiteral: ()) {}
}

let _: NLU = .init(nilLiteral: ()) // expected-error{{'init(nilLiteral:)' is only available in macOS 100 or newer}} expected-note{{add 'if #available' version check}}
let _: NLU = nil // expected-error{{'init(nilLiteral:)' is only available in macOS 100 or newer}} expected-note{{add 'if #available' version check}}

// ExpressibleByBooleanLiteral
struct BLD {}
@available(*, deprecated)
extension BLD: ExpressibleByBooleanLiteral {
init(booleanLiteral value: BooleanLiteralType) {}
}
let _: BLD = .init(booleanLiteral: false) // expected-warning{{'init(booleanLiteral:)' is deprecated}}
let _: BLD = false // expected-warning{{'init(booleanLiteral:)' is deprecated}}

struct BLU {}
@available(macOS 100, *)
extension BLU: ExpressibleByBooleanLiteral {
init(booleanLiteral value: BooleanLiteralType) {}
}
let _: BLU = .init(booleanLiteral: false) // expected-error{{'init(booleanLiteral:)' is only available in macOS 100 or newer}} expected-note{{add 'if #available' version check}}
let _: BLU = false // expected-error{{'init(booleanLiteral:)' is only available in macOS 100 or newer}} expected-note{{add 'if #available' version check}}

// ExpressibleByFloatLiteral
struct FLD {}
@available(*, deprecated)
extension FLD: ExpressibleByFloatLiteral {
init(floatLiteral value: FloatLiteralType) {}
}
let _: FLD = .init(floatLiteral: 0.1) // expected-warning{{'init(floatLiteral:)' is deprecated}}
let _: FLD = 0.1 // expected-warning{{'init(floatLiteral:)' is deprecated}}

struct FLU {}
@available(macOS 100, *)
extension FLU: ExpressibleByFloatLiteral {
init(floatLiteral value: FloatLiteralType) {}
}
let _: FLU = .init(floatLiteral: 0.1) // expected-error{{'init(floatLiteral:)' is only available in macOS 100 or newer}} expected-note{{add 'if #available' version check}}
let _: FLU = 0.1 // expected-error{{'init(floatLiteral:)' is only available in macOS 100 or newer}} expected-note{{add 'if #available' version check}}

// ExpressibleByArrayLiteral
struct ALD {}
@available(*, deprecated)
extension ALD: ExpressibleByArrayLiteral {
init(arrayLiteral elements: Int...) {}
}
let _: ALD = .init(arrayLiteral: 1) // expected-warning{{'init(arrayLiteral:)' is deprecated}}
let _: ALD = [1] // expected-warning{{'init(arrayLiteral:)' is deprecated}}

struct ALU {}
@available(macOS 100, *)
extension ALU: ExpressibleByArrayLiteral {
init(arrayLiteral elements: Int...) {}
}
let _: ALU = .init(arrayLiteral: 1) // expected-error{{'init(arrayLiteral:)' is only available in macOS 100 or newer}} expected-note{{add 'if #available' version check}}
let _: ALU = [1] // expected-error{{'init(arrayLiteral:)' is only available in macOS 100 or newer}} expected-note{{add 'if #available' version check}}

// ExpressibleByDictionaryLiteral
struct DLD {}
@available(*, deprecated)
extension DLD: ExpressibleByDictionaryLiteral {
init(dictionaryLiteral elements: (Int, Int)...) {}
}
let _: DLD = .init(dictionaryLiteral: (1,1)) // expected-warning{{'init(dictionaryLiteral:)' is deprecated}}
let _: DLD = [1: 1] // expected-warning{{'init(dictionaryLiteral:)' is deprecated}}

struct DLU {}
@available(macOS 100, *)
extension DLU: ExpressibleByDictionaryLiteral {
init(dictionaryLiteral elements: (Int, Int)...) {}
}
let _: DLU = .init(dictionaryLiteral: (1,1)) // expected-error{{'init(dictionaryLiteral:)' is only available in macOS 100 or newer}} expected-note{{add 'if #available' version check}}
let _: DLU = [1: 1] // expected-error{{'init(dictionaryLiteral:)' is only available in macOS 100 or newer}} expected-note{{add 'if #available' version check}}

// ExpressibleByUnicodeScalarLiteral
struct USLD {}
@available(*, deprecated)
extension USLD: ExpressibleByUnicodeScalarLiteral {
typealias UnicodeScalarLiteralType = Character
init(unicodeScalarLiteral value: UnicodeScalarLiteralType) {}
}
let _: USLD = .init(unicodeScalarLiteral: "a") // expected-warning{{'init(unicodeScalarLiteral:)' is deprecated}}
let _: USLD = "a" // expected-warning{{'init(unicodeScalarLiteral:)' is deprecated}}

struct USLU {}
@available(macOS 100, *)
extension USLU: ExpressibleByUnicodeScalarLiteral {
typealias UnicodeScalarLiteralType = Character
init(unicodeScalarLiteral value: UnicodeScalarLiteralType) {}
}
let _: USLU = .init(unicodeScalarLiteral: "a") // expected-error{{'init(unicodeScalarLiteral:)' is only available in macOS 100 or newer}} expected-note{{add 'if #available' version check}}
let _: USLU = "a" // expected-error{{'init(unicodeScalarLiteral:)' is only available in macOS 100 or newer}} expected-note{{add 'if #available' version check}}

//ExpressibleByExtendedGraphemeClusterLiteral
struct GCLD {}
@available(*, deprecated)
extension GCLD: ExpressibleByExtendedGraphemeClusterLiteral {
init(extendedGraphemeClusterLiteral value: Character) {}
}
let _: GCLD = .init(extendedGraphemeClusterLiteral: "🇧🇷") // expected-warning{{'init(extendedGraphemeClusterLiteral:)' is deprecated}}
let _: GCLD = "🇧🇷" // expected-warning{{'init(extendedGraphemeClusterLiteral:)' is deprecated}}

struct GCLU {}
@available(macOS 100, *)
extension GCLU: ExpressibleByExtendedGraphemeClusterLiteral {
init(extendedGraphemeClusterLiteral value: Character) {}
}
let _: GCLU = .init(extendedGraphemeClusterLiteral: "🇧🇷") // expected-error{{'init(extendedGraphemeClusterLiteral:)' is only available in macOS 100 or newer}} expected-note{{add 'if #available' version check}}
let _: GCLU = "🇧🇷" // expected-error{{'init(extendedGraphemeClusterLiteral:)' is only available in macOS 100 or newer}} expected-note{{add 'if #available' version check}}