From b14d9655ca4e1c32cf3a055bd9d4be2f5bf3471f Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 3 Jun 2020 10:26:59 -0700 Subject: [PATCH] [CSGen] Allow `is` patterns to infer type from enclosing context Instead of assuming type of its sub-pattern `is` should be able to infer type from context and then propagate that to the sub-pattern via conversion. This enables support for patterns like `.foo(_ as Foo)` where `is` would have a type different from `_` which gets inferred from associated value of `foo` and converted to `Foo` that becomes a type of `_` pattern. Resolves: rdar://problem/63510989 --- lib/Sema/CSGen.cpp | 12 +++++++++++- test/Constraints/patterns.swift | 17 +++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 1486e63f7f839..3f432cfa3f9c7 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2544,7 +2544,17 @@ namespace { ConstraintKind::CheckedCast, subPatternType, castType, locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); - return setType(subPatternType); + // Allow `is` pattern to infer type from context which is then going + // to be propaged down to its sub-pattern via conversion. This enables + // correct handling of patterns like `_ as Foo` where `_` would + // get a type of `Foo` but `is` pattern enclosing it could still be + // inferred from enclosing context. + auto isType = CS.createTypeVariable(CS.getConstraintLocator(pattern), + TVO_CanBindToNoEscape); + CS.addConstraint( + ConstraintKind::Conversion, subPatternType, isType, + locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); + return setType(isType); } case PatternKind::Bool: diff --git a/test/Constraints/patterns.swift b/test/Constraints/patterns.swift index f184629fa37f4..a3a2de3f5ee24 100644 --- a/test/Constraints/patterns.swift +++ b/test/Constraints/patterns.swift @@ -478,3 +478,20 @@ func rdar_60048356() { } } } + +// rdar://problem/63510989 - valid pattern doesn't type-check +func rdar63510989() { + enum Value : P { + func p() {} + } + + enum E { + case foo(P?) + } + + func test(e: E) { + if case .foo(_ as Value) = e {} // Ok + if case .foo(let v as Value) = e {} // Ok + // expected-warning@-1 {{immutable value 'v' was never used; consider replacing with '_' or removing it}} + } +}