Skip to content

[Sema] [SR-4347] Improve inference of optional supertypes #8339

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 5 commits into from
Apr 6, 2017
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
38 changes: 23 additions & 15 deletions lib/Sema/CSSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1173,22 +1173,30 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
// If we're supposed to add optional supertype bindings, do so now.
if (addOptionalSupertypeBindings) {
for (unsigned i : indices(result.Bindings)) {
// Only interested in supertype bindings.
auto &binding = result.Bindings[i];
if (binding.Kind != AllowedBindingKind::Supertypes) continue;

// If the type doesn't conform to ExpressibleByNilLiteral,
// produce an optional of that type as a potential binding. We
// overwrite the binding in place because the non-optional type
// will fail to type-check against the nil-literal conformance.
auto nominalBindingDecl = binding.BindingType->getAnyNominal();
if (!nominalBindingDecl) continue;
SmallVector<ProtocolConformance *, 2> conformances;
if (!nominalBindingDecl->lookupConformance(
cs.DC->getParentModule(),
cs.getASTContext().getProtocol(
KnownProtocolKind::ExpressibleByNilLiteral),
conformances)) {
bool wrapInOptional = false;

if (binding.Kind == AllowedBindingKind::Supertypes) {
// If the type doesn't conform to ExpressibleByNilLiteral,
// produce an optional of that type as a potential binding. We
// overwrite the binding in place because the non-optional type
// will fail to type-check against the nil-literal conformance.
auto nominalBindingDecl = binding.BindingType->getAnyNominal();
bool conformsToExprByNilLiteral = false;
if (nominalBindingDecl) {
SmallVector<ProtocolConformance *, 2> conformances;
conformsToExprByNilLiteral = nominalBindingDecl->lookupConformance(
cs.DC->getParentModule(),
cs.getASTContext().getProtocol(
KnownProtocolKind::ExpressibleByNilLiteral),
conformances);
}
wrapInOptional = !conformsToExprByNilLiteral;
} else if (binding.isDefaultableBinding() && binding.BindingType->isAny()) {
wrapInOptional = true;
}

if (wrapInOptional) {
binding.BindingType = OptionalType::get(binding.BindingType);
}
}
Expand Down
99 changes: 90 additions & 9 deletions test/Constraints/array_literal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,32 +121,47 @@ class A { }
class B : A { }
class C : A { }

/// Check for defaulting the element type to 'Any'.
/// Check for defaulting the element type to 'Any' / 'Any?'.
func defaultToAny(i: Int, s: String) {
let a1 = [1, "a", 3.5]
// expected-error@-1{{heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional}}
let _: Int = a1 // expected-error{{value of type '[Any]'}}

let a2: Array = [1, "a", 3.5]
// expected-error@-1{{heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional}}

let _: Int = a2 // expected-error{{value of type '[Any]'}}

let a3 = []

let a3 = [1, "a", nil, 3.5]
// expected-error@-1{{heterogeneous collection literal could only be inferred to '[Any?]'; add explicit type annotation if this is intentional}}
let _: Int = a3 // expected-error{{value of type '[Any?]'}}

let a4: Array = [1, "a", nil, 3.5]
// expected-error@-1{{heterogeneous collection literal could only be inferred to '[Any?]'; add explicit type annotation if this is intentional}}
let _: Int = a4 // expected-error{{value of type '[Any?]'}}

let a5 = []
// expected-error@-1{{empty collection literal requires an explicit type}}

let _: Int = a3 // expected-error{{value of type '[Any]'}}
let _: Int = a5 // expected-error{{value of type '[Any]'}}

let _: [Any] = [1, "a", 3.5]
let _: [Any] = [1, "a", [3.5, 3.7, 3.9]]
let _: [Any] = [1, "a", [3.5, "b", 3]]

let _: [Any?] = [1, "a", nil, 3.5]
let _: [Any?] = [1, "a", nil, [3.5, 3.7, 3.9]]
let _: [Any?] = [1, "a", nil, [3.5, "b", nil]]

let a4 = [B(), C()]
let _: Int = a4 // expected-error{{value of type '[A]'}}
let a6 = [B(), C()]
let _: Int = a6 // expected-error{{value of type '[A]'}}
}

/// Check handling of 'nil'.
func joinWithNil(s: String) {
protocol Proto1 {}
protocol Proto2 {}
struct Nilable: ExpressibleByNilLiteral {
init(nilLiteral: ()) {}
}
func joinWithNil<T>(s: String, a: Any, t: T, m: T.Type, p: Proto1 & Proto2, arr: [Int], opt: Int?, iou: Int!, n: Nilable) {
let a1 = [s, nil]
let _: Int = a1 // expected-error{{value of type '[String?]'}}

Expand All @@ -158,6 +173,72 @@ func joinWithNil(s: String) {

let a4 = [nil, "hello"]
let _: Int = a4 // expected-error{{value of type '[String?]'}}

let a5 = [(s, s), nil]
let _: Int = a5 // expected-error{{value of type '[(String, String)?]'}}

let a6 = [nil, (s, s)]
let _: Int = a6 // expected-error{{value of type '[(String, String)?]'}}

let a7 = [("hello", "world"), nil]
let _: Int = a7 // expected-error{{value of type '[(String, String)?]'}}

let a8 = [nil, ("hello", "world")]
let _: Int = a8 // expected-error{{value of type '[(String, String)?]'}}

let a9 = [{ $0 * 2 }, nil]
let _: Int = a9 // expected-error{{value of type '[((Int) -> Int)?]'}}

let a10 = [nil, { $0 * 2 }]
let _: Int = a10 // expected-error{{value of type '[((Int) -> Int)?]'}}

let a11 = [a, nil]
let _: Int = a11 // expected-error{{value of type '[Any?]'}}

let a12 = [nil, a]
let _: Int = a12 // expected-error{{value of type '[Any?]'}}

let a13 = [t, nil]
let _: Int = a13 // expected-error{{value of type '[T?]'}}

let a14 = [nil, t]
let _: Int = a14 // expected-error{{value of type '[T?]'}}

let a15 = [m, nil]
let _: Int = a15 // expected-error{{value of type '[T.Type?]'}}

let a16 = [nil, m]
let _: Int = a16 // expected-error{{value of type '[T.Type?]'}}

let a17 = [p, nil]
let _: Int = a17 // expected-error{{value of type '[(Proto1 & Proto2)?]'}}

let a18 = [nil, p]
let _: Int = a18 // expected-error{{value of type '[(Proto1 & Proto2)?]'}}

let a19 = [arr, nil]
let _: Int = a19 // expected-error{{value of type '[[Int]?]'}}

let a20 = [nil, arr]
let _: Int = a20 // expected-error{{value of type '[[Int]?]'}}

let a21 = [opt, nil]
let _: Int = a21 // expected-error{{value of type '[Int?]'}}

let a22 = [nil, opt]
let _: Int = a22 // expected-error{{value of type '[Int?]'}}

let a23 = [iou, nil]
let _: Int = a23 // expected-error{{value of type '[Int?]'}}

let a24 = [nil, iou]
let _: Int = a24 // expected-error{{value of type '[Int?]'}}

let a25 = [n, nil]
let _: Int = a25 // expected-error{{value of type '[Nilable]'}}

let a26 = [nil, n]
let _: Int = a26 // expected-error{{value of type '[Nilable]'}}
}

struct OptionSetLike : ExpressibleByArrayLiteral {
Expand Down
5 changes: 5 additions & 0 deletions test/Constraints/dictionary_literal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ func testDefaultExistentials() {
// expected-error@-1{{heterogeneous collection literal could only be inferred to 'Dictionary<String, Any>'; add explicit type annotation if this is intentional}}{{46-46= as Dictionary<String, Any>}}

let _: [String : Any] = ["a" : 1, "b" : 2.5, "c" : "hello"]

let _ = ["a" : 1, "b" : nil, "c" : "hello"]
// expected-error@-1{{heterogeneous collection literal could only be inferred to 'Dictionary<String, Any?>' (aka 'Dictionary<String, Optional<Any>>'); add explicit type annotation if this is intentional}}{{46-46= as Dictionary<String, Any?>}}

let _: [String : Any?] = ["a" : 1, "b" : nil, "c" : "hello"]

let d2 = [:]
// expected-error@-1{{empty collection literal requires an explicit type}}
Expand Down
51 changes: 50 additions & 1 deletion test/Constraints/optional.swift
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,12 @@ func testVoidOptional() {
voidOptional(optNoop)
}

func testTernaryWithNil(b: Bool, s: String, i: Int) {
protocol Proto1 {}
protocol Proto2 {}
struct Nilable: ExpressibleByNilLiteral {
init(nilLiteral: ()) {}
}
func testTernaryWithNil<T>(b: Bool, s: String, i: Int, a: Any, t: T, m: T.Type, p: Proto1 & Proto2, arr: [Int], opt: Int?, iou: Int!, n: Nilable) {
let t1 = b ? s : nil
let _: Double = t1 // expected-error{{value of type 'String?'}}
let t2 = b ? nil : i
Expand All @@ -129,6 +134,50 @@ func testTernaryWithNil(b: Bool, s: String, i: Int) {
let _: Double = t3 // expected-error{{value of type 'String?'}}
let t4 = b ? nil : 1
let _: Double = t4 // expected-error{{value of type 'Int?'}}
let t5 = b ? (s, i) : nil
let _: Double = t5 // expected-error{{value of type '(String, Int)?}}
let t6 = b ? nil : (i, s)
let _: Double = t6 // expected-error{{value of type '(Int, String)?}}
let t7 = b ? ("hello", 1) : nil
let _: Double = t7 // expected-error{{value of type '(String, Int)?}}
let t8 = b ? nil : (1, "hello")
let _: Double = t8 // expected-error{{value of type '(Int, String)?}}
let t9 = b ? { $0 * 2 } : nil
let _: Double = t9 // expected-error{{value of type '((Int) -> Int)?}}
let t10 = b ? nil : { $0 * 2 }
let _: Double = t10 // expected-error{{value of type '((Int) -> Int)?}}
let t11 = b ? a : nil
let _: Double = t11 // expected-error{{value of type 'Any?'}}
let t12 = b ? nil : a
let _: Double = t12 // expected-error{{value of type 'Any?'}}
let t13 = b ? t : nil
let _: Double = t13 // expected-error{{value of type 'T?'}}
let t14 = b ? nil : t
let _: Double = t14 // expected-error{{value of type 'T?'}}
let t15 = b ? m : nil
let _: Double = t15 // expected-error{{value of type 'T.Type?'}}
let t16 = b ? nil : m
let _: Double = t16 // expected-error{{value of type 'T.Type?'}}
let t17 = b ? p : nil
let _: Double = t17 // expected-error{{value of type '(Proto1 & Proto2)?'}}
let t18 = b ? nil : p
let _: Double = t18 // expected-error{{value of type '(Proto1 & Proto2)?'}}
let t19 = b ? arr : nil
let _: Double = t19 // expected-error{{value of type '[Int]?'}}
let t20 = b ? nil : arr
let _: Double = t20 // expected-error{{value of type '[Int]?'}}
let t21 = b ? opt : nil
let _: Double = t21 // expected-error{{value of type 'Int?'}}
let t22 = b ? nil : opt
let _: Double = t22 // expected-error{{value of type 'Int?'}}
let t23 = b ? iou : nil
let _: Double = t23 // expected-error{{value of type 'Int?'}}
let t24 = b ? nil : iou
let _: Double = t24 // expected-error{{value of type 'Int?'}}
let t25 = b ? n : nil
let _: Double = t25 // expected-error{{value of type 'Nilable'}}
let t26 = b ? nil : n
let _: Double = t26 // expected-error{{value of type 'Nilable'}}
}

// inference with IUOs
Expand Down