Skip to content
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

[Sema] Fix issue 65330 Unhelpful error when missing contextually requ… #65610

Merged
merged 1 commit into from
May 6, 2023
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
61 changes: 60 additions & 1 deletion lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2577,7 +2577,6 @@ bool ContextualFailure::diagnoseAsError() {
}
return false;
}

case ConstraintLocator::UnresolvedMemberChainResult: {
auto &solution = getSolution();

Expand Down Expand Up @@ -3528,6 +3527,66 @@ ContextualFailure::getDiagnosticFor(ContextualTypePurpose context,
return None;
}

bool NonClassTypeToAnyObjectConversionFailure::diagnoseAsError() {
auto locator = getLocator();
if (locator->isForContextualType()) {
return ContextualFailure::diagnoseAsError();
}

auto fromType = getFromType();
auto toType = getToType();
assert(fromType);
assert(toType);
if (locator->isLastElement<LocatorPathElt::ApplyArgToParam>()) {
ArgumentMismatchFailure failure(getSolution(), fromType, toType, locator);
return failure.diagnoseAsError();
}

Optional<Diag<Type, Type>> diagnostic;

bool forProtocol = toType->isConstraintType();
auto rawAnchor = getRawAnchor();

if (isExpr<ArrayExpr>(rawAnchor)) {
diagnostic = forProtocol ? diag::cannot_convert_array_element_protocol
: diag::cannot_convert_array_element;
} else if (isExpr<DictionaryExpr>(rawAnchor)) {
auto lastElem = locator->getLastElementAs<LocatorPathElt::TupleElement>();
if (lastElem && lastElem->getIndex() == 0) {
diagnostic = forProtocol ? diag::cannot_convert_dict_key_protocol
: diag::cannot_convert_dict_key;
} else {
diagnostic = forProtocol ? diag::cannot_convert_dict_value_protocol
: diag::cannot_convert_dict_value;
}
} else if (toType->isAnyObject()) {
diagnostic = diag::cannot_convert_initializer_value_anyobject;
}

if (diagnostic.hasValue()) {
emitDiagnostic(*diagnostic, fromType, toType);
return true;
}

return false;
}

bool NonClassTypeToAnyObjectConversionFailure::diagnoseAsNote() {
auto *locator = getLocator();

if (locator->isForContextualType()) {
return ContextualFailure::diagnoseAsNote();
}

if (locator->isLastElement<LocatorPathElt::ApplyArgToParam>()) {
ArgumentMismatchFailure failure(getSolution(), getFromType(), getToType(),
getLocator());
return failure.diagnoseAsNote();
}

return false;
}

bool TupleContextualFailure::diagnoseAsError() {
Diag<Type, Type> diagnostic;
auto purpose = getContextualTypePurpose();
Expand Down
13 changes: 13 additions & 0 deletions lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,19 @@ class ContextualFailure : public FailureDiagnostic {
}
};

class NonClassTypeToAnyObjectConversionFailure final
: public ContextualFailure {

public:
NonClassTypeToAnyObjectConversionFailure(const Solution &solution, Type lhs,
Type rhs, ConstraintLocator *locator)
: ContextualFailure(solution, lhs, rhs, locator, FixBehavior::Error) {}

bool diagnoseAsError() override;

bool diagnoseAsNote() override;
};

/// Diagnose errors related to using an array literal where a
/// dictionary is expected.
class ArrayLiteralToDictionaryConversionFailure final : public ContextualFailure {
Expand Down
14 changes: 3 additions & 11 deletions lib/Sema/CSFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1934,18 +1934,10 @@ bool AllowNonClassTypeToConvertToAnyObject::diagnose(const Solution &solution,
bool asNote) const {
auto *locator = getLocator();

if (locator->isForContextualType()) {
ContextualFailure failure(solution, getFromType(), getToType(), locator);
return failure.diagnose(asNote);
}
NonClassTypeToAnyObjectConversionFailure failure(solution, getFromType(),
getToType(), locator);

if (locator->isLastElement<LocatorPathElt::ApplyArgToParam>()) {
ArgumentMismatchFailure failure(solution, getFromType(), getToType(),
locator);
return failure.diagnose(asNote);
}

return false;
return failure.diagnose(asNote);
}

AllowNonClassTypeToConvertToAnyObject *
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// RUN: %target-typecheck-verify-swift

let _: AnyObject = "a" // expected-error {{value of type 'String' expected to be instance of class or class-constrained type}}

let _: [AnyObject] = ["a"] // expected-error {{cannot convert value of type 'String' to expected element type 'AnyObject'}}
let _: [String: AnyObject] = ["a": "a"] // expected-error {{cannot convert value of type 'String' to expected dictionary value type 'AnyObject'}}
let _: [AnyObject: String] = ["a": "a"] // expected-error {{type 'AnyObject' does not conform to protocol 'Hashable'}}
// expected-error@-1 {{cannot convert value of type 'String' to expected dictionary key type 'AnyObject'}}
let _: (AnyObject, Void) = ("a", ()) // expected-error {{value of type 'String' expected to be instance of class or class-constrained type}}
12 changes: 4 additions & 8 deletions test/Sema/generic-subscript.swift
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
// RUN: %target-typecheck-verify-swift

protocol P {
subscript<Value>(x: Value) -> Int // expected-note {{protocol requires subscript with type '<Value> (Value) -> Int'; do you want to add a stub?}}
{
subscript<Value>(x: Value) -> Int { // expected-note {{protocol requires subscript with type '<Value> (Value) -> Int'; do you want to add a stub?}}
get
}
}

struct S: P // expected-error {{type 'S' does not conform to protocol 'P'}}
{
subscript<Value>(x: Int) -> Value // expected-note {{candidate has non-matching type '<Value> (Int) -> Value'}}
{
struct S : P { // expected-error {{type 'S' does not conform to protocol 'P'}}
subscript<Value>(x: Int) -> Value { // expected-note {{candidate has non-matching type '<Value> (Int) -> Value'}}
} // expected-error {{missing return in subscript expected to return 'Value'}}
}

Expand All @@ -21,8 +18,7 @@ struct S2: P {
}

protocol P2 {
subscript(x: Int) -> Int
{
subscript(x: Int) -> Int {
get
}
}
Expand Down