Skip to content

[Diagnostics] Port diagnostics for array/dictionary literals #29166

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 4 commits into from
Jan 14, 2020
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
1 change: 0 additions & 1 deletion docs/TypeChecker.rst
Original file line number Diff line number Diff line change
Expand Up @@ -992,4 +992,3 @@ The things in the queue yet to be ported are:
- ``diagnoseParameterErrors``
- ``diagnoseSimpleErrors``

- Diagnostics related to array/dictionary literals: ``visit{Array, Dictionary}Expr``.
121 changes: 0 additions & 121 deletions lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,6 @@ class FailureDiagnosis :public ASTVisitor<FailureDiagnosis, /*exprresult*/bool>{
bool visitTryExpr(TryExpr *E);

bool visitUnresolvedDotExpr(UnresolvedDotExpr *UDE);
bool visitArrayExpr(ArrayExpr *E);
bool visitDictionaryExpr(DictionaryExpr *E);
bool visitObjectLiteralExpr(ObjectLiteralExpr *E);

bool visitApplyExpr(ApplyExpr *AE);
Expand Down Expand Up @@ -1724,125 +1722,6 @@ visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E) {
return false;
}

bool FailureDiagnosis::visitArrayExpr(ArrayExpr *E) {
// If we had a contextual type, then it either conforms to
// ExpressibleByArrayLiteral or it is an invalid contextual type.
auto contextualType = CS.getContextualType();
if (!contextualType) {
return false;
}

// If our contextual type is an optional, look through them, because we're
// surely initializing whatever is inside.
contextualType = contextualType->lookThroughAllOptionalTypes();

// Validate that the contextual type conforms to ExpressibleByArrayLiteral and
// figure out what the contextual element type is in place.
auto ALC =
TypeChecker::getProtocol(CS.getASTContext(), E->getLoc(),
KnownProtocolKind::ExpressibleByArrayLiteral);
if (!ALC)
return visitExpr(E);

// Check to see if the contextual type conforms.
auto Conformance = TypeChecker::conformsToProtocol(
contextualType, ALC, CS.DC, ConformanceCheckFlags::InExpression);
if (Conformance) {
Type contextualElementType =
Conformance
.getTypeWitnessByName(contextualType,
CS.getASTContext().Id_ArrayLiteralElement)
->getDesugaredType();

// Type check each of the subexpressions in place, passing down the contextual
// type information if we have it.
for (auto elt : E->getElements()) {
if (typeCheckChildIndependently(elt, contextualElementType,
CTP_ArrayElement) == nullptr) {
return true;
}
}

return false;
}

ContextualFailure failure(CS, CS.getType(E), contextualType,
CS.getConstraintLocator(E));
if (failure.diagnoseConversionToDictionary())
return true;

// If that didn't turn up an issue, then we don't know what to do.
// TODO: When a contextual type is missing, we could try to diagnose cases
// where the element types mismatch... but theoretically they should type
// unify to Any, so that could never happen?
return false;
}

bool FailureDiagnosis::visitDictionaryExpr(DictionaryExpr *E) {
Type contextualKeyType, contextualValueType;
auto keyTypePurpose = CTP_Unused, valueTypePurpose = CTP_Unused;

// If we had a contextual type, then it either conforms to
// ExpressibleByDictionaryLiteral or it is an invalid contextual type.
if (auto contextualType = CS.getContextualType()) {
// If our contextual type is an optional, look through them, because we're
// surely initializing whatever is inside.
contextualType = contextualType->lookThroughAllOptionalTypes();

auto DLC = TypeChecker::getProtocol(
CS.getASTContext(), E->getLoc(),
KnownProtocolKind::ExpressibleByDictionaryLiteral);
if (!DLC) return visitExpr(E);

// Validate the contextual type conforms to ExpressibleByDictionaryLiteral
// and figure out what the contextual Key/Value types are in place.
auto Conformance = TypeChecker::conformsToProtocol(
contextualType, DLC, CS.DC, ConformanceCheckFlags::InExpression);
if (Conformance.isInvalid()) {
diagnose(E->getStartLoc(), diag::type_is_not_dictionary, contextualType)
.highlight(E->getSourceRange());
return true;
}

contextualKeyType =
Conformance
.getTypeWitnessByName(contextualType, CS.getASTContext().Id_Key)
->getDesugaredType();

contextualValueType =
Conformance
.getTypeWitnessByName(contextualType, CS.getASTContext().Id_Value)
->getDesugaredType();

assert(contextualKeyType && contextualValueType &&
"Could not find Key/Value DictionaryLiteral associated types from"
" contextual type conformance");

keyTypePurpose = CTP_DictionaryKey;
valueTypePurpose = CTP_DictionaryValue;
}

// Type check each of the subexpressions in place, passing down the contextual
// type information if we have it.
for (auto elt : E->getElements()) {
auto TE = dyn_cast<TupleExpr>(elt);
if (!TE || TE->getNumElements() != 2) continue;

if (!typeCheckChildIndependently(TE->getElement(0),
contextualKeyType, keyTypePurpose))
return true;
if (!typeCheckChildIndependently(TE->getElement(1),
contextualValueType, valueTypePurpose))
return true;
}

// If that didn't turn up an issue, then we don't know what to do.
// TODO: When a contextual type is missing, we could try to diagnose cases
// where the element types mismatch. There is no Any equivalent since they
// keys need to be hashable.
return false;
}

/// When an object literal fails to typecheck because its protocol's
/// corresponding default type has not been set in the global namespace (e.g.
/// _ColorLiteralType), suggest that the user import the appropriate module for
Expand Down
13 changes: 13 additions & 0 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,19 @@ bool GenericArgumentsMismatchFailure::diagnoseAsError() {
break;
}

case ConstraintLocator::TupleElement: {
auto *anchor = getRawAnchor();

if (isa<ArrayExpr>(anchor)) {
diagnostic = getDiagnosticFor(CTP_ArrayElement);
} else if (isa<DictionaryExpr>(anchor)) {
auto eltLoc = last.castTo<LocatorPathElt::TupleElement>();
diagnostic = getDiagnosticFor(
eltLoc.getIndex() == 0 ? CTP_DictionaryKey : CTP_DictionaryValue);
}
break;
}

default:
return false;
}
Expand Down
11 changes: 11 additions & 0 deletions test/Constraints/generics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -835,3 +835,14 @@ func sr_11491(_ value: [String]) {
arr.insert(value)
// expected-error@-1 {{cannot convert value of type '[String]' to expected argument type 'String'}}
}

func test_dictionary_with_generic_mismatch_in_key_or_value() {
struct S<T> : Hashable {}
// expected-note@-1 2 {{arguments to generic parameter 'T' ('Int' and 'Bool') are expected to be equal}}

let _: [Int: S<Bool>] = [0: S<Bool>(), 1: S<Int>()]
// expected-error@-1 {{cannot convert value of type 'S<Int>' to expected dictionary value type 'S<Bool>'}}

let _: [S<Bool>: Int] = [S<Int>(): 42]
// expected-error@-1 {{cannot convert value of type 'S<Int>' to expected dictionary key type 'S<Bool>'}}
}