Skip to content

Commit d21069b

Browse files
committed
[ConstraintSystem] Propagate holes up to result of optional chaining
If underlying type of an optional chain has been marked as a hole, e.g. due to a reference to an invalid or missing member, let's propagate that information to object type of an optional that represents a result of an optional chain. Resolves: rdar://80941497
1 parent 52e852a commit d21069b

File tree

3 files changed

+37
-2
lines changed

3 files changed

+37
-2
lines changed

lib/Sema/CSBindings.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1929,6 +1929,24 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const {
19291929
type = type->reconstituteSugar(/*recursive=*/false);
19301930
}
19311931

1932+
// If type variable has been marked as a possible hole due to
1933+
// e.g. reference to a missing member. Let's propagate that
1934+
// information to the object type of the optional type it's
1935+
// about to be bound to.
1936+
//
1937+
// In some situations like pattern bindings e.g. `if let x = base?.member`
1938+
// - if `member` doesn't exist, `x` cannot be determined either, which
1939+
// leaves `OptionalEvaluationExpr` representing outer type of `base?.member`
1940+
// without any contextual information, so even though `x` would get
1941+
// bound to result type of the chain, underlying type variable wouldn't
1942+
// be resolved, so we need to propagate holes up the conversion chain.
1943+
if (TypeVar->getImpl().canBindToHole()) {
1944+
if (auto objectTy = type->getOptionalObjectType()) {
1945+
if (auto *typeVar = objectTy->getAs<TypeVariableType>())
1946+
cs.recordPotentialHole(typeVar);
1947+
}
1948+
}
1949+
19321950
ConstraintSystem::TypeMatchOptions options;
19331951

19341952
options |= ConstraintSystem::TMF_GenerateConstraints;

test/Constraints/result_builder_diags.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,3 +797,21 @@ func test_weak_with_nonoptional_type() {
797797
42
798798
}
799799
}
800+
801+
// rdar://80941497 - compiler fails to produce diagnostic when referencing missing member in optional context
802+
func test_missing_member_in_optional_context() {
803+
struct Test {
804+
}
805+
806+
var test: Test? = nil
807+
808+
tuplify(true) { c in
809+
if let prop = test?.prop { // expected-error {{value of type 'Test' has no member 'prop'}}
810+
0
811+
}
812+
813+
if let method = test?.method() { // expected-error {{value of type 'Test' has no member 'method'}}
814+
1
815+
}
816+
}
817+
}

test/expr/unary/keypath/keypath.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,8 +1089,7 @@ func rdar74711236() {
10891089
var s = S()
10901090

10911091
s.arr = {
1092-
// FIXME: Missing member reference is pattern needs a better diagnostic
1093-
if let type = context?.store { // expected-error {{type of expression is ambiguous without more context}}
1092+
if let type = context?.store { // expected-error {{value of type 'Context' has no member 'store'}}
10941093
// `isSupported` should be an invalid declaration to trigger a crash in `map(\.option)`
10951094
let isSupported = context!.supported().contains(type)
10961095
return (isSupported ? [type] : []).map(\.option)

0 commit comments

Comments
 (0)