Skip to content

[CSDiagnostics] Imporove requirement diagnostics originating in conte… #22701

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 1 commit into from
Feb 19, 2019
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
14 changes: 14 additions & 0 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,14 @@ ValueDecl *RequirementFailure::getDeclRef() const {

auto *anchor = getRawAnchor();
auto *locator = cs.getConstraintLocator(anchor);

if (isFromContextualType()) {
auto type = cs.getContextualType();
assert(type);
auto *alias = dyn_cast<TypeAliasType>(type.getPointer());
return alias ? alias->getDecl() : type->getAnyGeneric();
}

if (auto *AE = dyn_cast<CallExpr>(anchor)) {
assert(isa<TypeExpr>(AE->getFn()));
ConstraintLocatorBuilder ctor(locator);
Expand Down Expand Up @@ -190,6 +198,12 @@ GenericSignature *RequirementFailure::getSignature(ConstraintLocator *locator) {
llvm_unreachable("Type requirement failure should always have signature");
}

bool RequirementFailure::isFromContextualType() const {
auto path = getLocator()->getPath();
assert(!path.empty());
return path.front().getKind() == ConstraintLocator::ContextualType;
}

const DeclContext *RequirementFailure::getRequirementDC() const {
// In case of conditional requirement failure, we don't
// have to guess where the it comes from.
Expand Down
4 changes: 4 additions & 0 deletions lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,10 @@ class RequirementFailure : public FailureDiagnostic {
/// Determine whether this is a conditional requirement failure.
bool isConditional() const { return bool(Conformance); }

/// Check whether this requirement comes from the contextual type
/// that root expression is coerced/converted into.
bool isFromContextualType() const;

/// Retrieve declaration contextual where current
/// requirement has been introduced.
const DeclContext *getRequirementDC() const;
Expand Down
3 changes: 2 additions & 1 deletion lib/Sema/TypeCheckConstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2466,7 +2466,8 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer,
assert(!expr->isSemanticallyInOutExpr());

// Save the locator we're using for the expression.
Locator = cs.getConstraintLocator(expr);
Locator =
cs.getConstraintLocator(expr, ConstraintLocator::ContextualType);

// Collect constraints from the pattern.
initType = cs.generateConstraints(pattern, Locator);
Expand Down
21 changes: 21 additions & 0 deletions test/Constraints/requirement_failures_in_contextual_type.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// RUN: %target-typecheck-verify-swift

struct A<T> {}

extension A where T == Int32 { // expected-note 2 {{where 'T' = 'Int'}}
struct B : ExpressibleByIntegerLiteral { // expected-note {{where 'T' = 'Int'}}
typealias E = Int
typealias IntegerLiteralType = Int

init(integerLiteral: IntegerLiteralType) {}
}

typealias C = Int
}

let _: A<Int>.B = 0
// expected-error@-1 {{referencing struct 'B' on 'A' requires the types 'Int' and 'Int32' be equivalent}}
let _: A<Int>.C = 0
// expected-error@-1 {{referencing type alias 'C' on 'A' requires the types 'Int' and 'Int32' be equivalent}}
let _: A<Int>.B.E = 0
// expected-error@-1 {{referencing type alias 'E' on 'A.B' requires the types 'Int' and 'Int32' be equivalent}}
22 changes: 11 additions & 11 deletions test/Generics/conditional_conformances_literals.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ func arraySameType() {

let _: SameType = arrayWorks
let _: SameType = arrayFails
// expected-error@-1 {{let 'arrayFails' requires the types 'Fails' and 'Works' be equivalent}}
// expected-error@-1 {{protocol 'SameType' requires the types 'Fails' and 'Works' be equivalent}}

let _: SameType = [works] as [Works]
let _: SameType = [fails] as [Fails]
// expected-error@-1 {{generic struct 'Array' requires the types 'Fails' and 'Works' be equivalent}}
// expected-error@-1 {{protocol 'SameType' requires the types 'Fails' and 'Works' be equivalent}}

let _: SameType = [works] as SameType
let _: SameType = [fails] as SameType
Expand All @@ -55,11 +55,11 @@ func dictionarySameType() {

let _: SameType = dictWorks
let _: SameType = dictFails
// expected-error@-1 {{let 'dictFails' requires the types 'Fails' and 'Works' be equivalent}}
// expected-error@-1 {{protocol 'SameType' requires the types 'Fails' and 'Works' be equivalent}}

let _: SameType = [0 : works] as [Int : Works]
let _: SameType = [0 : fails] as [Int : Fails]
// expected-error@-1 {{generic struct 'Dictionary' requires the types 'Fails' and 'Works' be equivalent}}
// expected-error@-1 {{protocol 'SameType' requires the types 'Fails' and 'Works' be equivalent}}

let _: SameType = [0 : works] as SameType
let _: SameType = [0 : fails] as SameType
Expand All @@ -76,15 +76,15 @@ func arrayConforms() {

let _: Conforms = [works]
let _: Conforms = [fails]
// expected-error@-1 {{generic struct 'Array' requires that 'Fails' conform to 'Conforms'}}
// expected-error@-1 {{protocol 'Conforms' requires that 'Fails' conform to 'Conforms'}}

let _: Conforms = arrayWorks
let _: Conforms = arrayFails
// expected-error@-1 {{let 'arrayFails' requires that 'Fails' conform to 'Conforms'}}
// expected-error@-1 {{protocol 'Conforms' requires that 'Fails' conform to 'Conforms'}}

let _: Conforms = [works] as [Works]
let _: Conforms = [fails] as [Fails]
// expected-error@-1 {{eneric struct 'Array' requires that 'Fails' conform to 'Conforms'}}
// expected-error@-1 {{protocol 'Conforms' requires that 'Fails' conform to 'Conforms'}}

let _: Conforms = [works] as Conforms
let _: Conforms = [fails] as Conforms
Expand All @@ -101,15 +101,15 @@ func dictionaryConforms() {

let _: Conforms = [0 : works]
let _: Conforms = [0 : fails]
// expected-error@-1 {{generic struct 'Dictionary' requires that 'Fails' conform to 'Conforms'}}
// expected-error@-1 {{protocol 'Conforms' requires that 'Fails' conform to 'Conforms'}}

let _: Conforms = dictWorks
let _: Conforms = dictFails
// expected-error@-1 {{let 'dictFails' requires that 'Fails' conform to 'Conforms'}}
// expected-error@-1 {{protocol 'Conforms' requires that 'Fails' conform to 'Conforms'}}

let _: Conforms = [0 : works] as [Int : Works]
let _: Conforms = [0 : fails] as [Int : Fails]
// expected-error@-1 {{generic struct 'Dictionary' requires that 'Fails' conform to 'Conforms'}}
// expected-error@-1 {{protocol 'Conforms' requires that 'Fails' conform to 'Conforms'}}

let _: Conforms = [0 : works] as Conforms
let _: Conforms = [0 : fails] as Conforms
Expand All @@ -123,7 +123,7 @@ func dictionaryConforms() {
func combined() {
let _: Conforms = [[0: [1 : [works]]]]
let _: Conforms = [[0: [1 : [fails]]]]
// expected-error@-1 {{generic struct 'Array' requires that 'Fails' conform to 'Conforms'}}
// expected-error@-1 {{protocol 'Conforms' requires that 'Fails' conform to 'Conforms'}}

// Needs self conforming protocols:
let _: Conforms = [[0: [1 : [works]] as Conforms]]
Expand Down