Skip to content

Commit 02a676b

Browse files
authored
Merge pull request swiftlang#29672 from xedin/eliminate-leftover-contextual-diagnostics
[Diagnostics] Fix all of the edge cases and remove `diagnoseContextualConversionError`
2 parents 94784f1 + 17f7598 commit 02a676b

18 files changed

+233
-199
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -195,12 +195,6 @@ class FailureDiagnosis :public ASTVisitor<FailureDiagnosis, /*exprresult*/bool>{
195195
/// Emit an ambiguity diagnostic about the specified expression.
196196
void diagnoseAmbiguity(Expr *E);
197197

198-
/// Attempt to produce a diagnostic for a mismatch between an expression's
199-
/// type and its assumed contextual type.
200-
bool diagnoseContextualConversionError(Expr *expr, Type contextualType,
201-
ContextualTypePurpose CTP,
202-
Type suggestedType = Type());
203-
204198
private:
205199
/// Validate potential contextual type for type-checking one of the
206200
/// sub-expressions, usually correct/valid types are the ones which
@@ -500,51 +494,6 @@ DeclContext *FailureDiagnosis::findDeclContext(Expr *subExpr) const {
500494
return finder.DC;
501495
}
502496

503-
bool FailureDiagnosis::diagnoseContextualConversionError(
504-
Expr *expr, Type contextualType, ContextualTypePurpose CTP,
505-
Type suggestedType) {
506-
// If the constraint system has a contextual type, then we can test to see if
507-
// this is the problem that prevents us from solving the system.
508-
if (!contextualType)
509-
return false;
510-
511-
// Try re-type-checking the expression without the contextual type to see if
512-
// it can work without it. If so, the contextual type is the problem. We
513-
// force a recheck, because "expr" is likely in our table with the extra
514-
// contextual constraint that we know we are relaxing.
515-
TCCOptions options = TCC_ForceRecheck;
516-
if (contextualType->is<InOutType>())
517-
options |= TCC_AllowLValue;
518-
519-
auto *recheckedExpr = typeCheckChildIndependently(expr, options);
520-
auto exprType = recheckedExpr ? CS.getType(recheckedExpr) : Type();
521-
522-
// If there is a suggested type and re-typecheck failed, let's use it.
523-
if (!exprType)
524-
exprType = suggestedType;
525-
526-
// If it failed and diagnosed something, then we're done.
527-
if (!exprType)
528-
return CS.getASTContext().Diags.hadAnyError();
529-
530-
// If we don't have a type for the expression, then we cannot use it in
531-
// conversion constraint diagnostic generation. If the types match, then it
532-
// must not be the contextual type that is the problem.
533-
if (isUnresolvedOrTypeVarType(exprType) || exprType->isEqual(contextualType))
534-
return false;
535-
536-
// Don't attempt fixits if we have an unsolved type variable, since
537-
// the recovery path's recursion into the type checker via typeCheckCast()
538-
// will confuse matters.
539-
if (exprType->hasTypeVariable())
540-
return false;
541-
542-
ContextualFailure failure(
543-
CS, CTP, exprType, contextualType,
544-
CS.getConstraintLocator(expr, LocatorPathElt::ContextualType()));
545-
return failure.diagnoseAsError();
546-
}
547-
548497
//===----------------------------------------------------------------------===//
549498
// Diagnose assigning variable to itself.
550499
//===----------------------------------------------------------------------===//
@@ -1256,13 +1205,6 @@ void ConstraintSystem::diagnoseFailureFor(SolutionApplicationTarget target) {
12561205
if (diagnosis.diagnoseExprFailure())
12571206
return;
12581207

1259-
// If this is a contextual conversion problem, dig out some information.
1260-
if (diagnosis.diagnoseContextualConversionError(
1261-
expr,
1262-
getContextualType(expr),
1263-
getContextualTypePurpose(expr)))
1264-
return;
1265-
12661208
// If no one could find a problem with this expression or constraint system,
12671209
// then it must be well-formed... but is ambiguous. Handle this by diagnostic
12681210
// various cases that come up.

lib/Sema/CSDiagnostics.cpp

Lines changed: 17 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1891,9 +1891,6 @@ bool ContextualFailure::diagnoseAsError() {
18911891
if (diagnoseMissingFunctionCall())
18921892
return true;
18931893

1894-
if (diagnoseConversionToDictionary())
1895-
return true;
1896-
18971894
// Special case of some common conversions involving Swift.String
18981895
// indexes, catching cases where people attempt to index them with an integer.
18991896
if (isIntegerToStringIndexConversion()) {
@@ -1958,6 +1955,17 @@ bool ContextualFailure::diagnoseAsError() {
19581955
if (diagnoseYieldByReferenceMismatch())
19591956
return true;
19601957

1958+
if (isa<OptionalTryExpr>(anchor) || isa<OptionalEvaluationExpr>(anchor)) {
1959+
auto objectType = fromType->getOptionalObjectType();
1960+
if (objectType->isEqual(toType)) {
1961+
auto &cs = getConstraintSystem();
1962+
MissingOptionalUnwrapFailure failure(cs, getType(anchor), toType,
1963+
cs.getConstraintLocator(anchor));
1964+
if (failure.diagnoseAsError())
1965+
return true;
1966+
}
1967+
}
1968+
19611969
if (CTP == CTP_ForEachStmt) {
19621970
if (fromType->isAnyExistentialType()) {
19631971
emitDiagnostic(anchor->getLoc(), diag::type_cannot_conform,
@@ -2392,61 +2400,6 @@ bool ContextualFailure::diagnoseConversionToBool() const {
23922400
return false;
23932401
}
23942402

2395-
bool ContextualFailure::isInvalidDictionaryConversion(
2396-
ConstraintSystem &cs, Expr *anchor, Type contextualType) {
2397-
auto *arrayExpr = dyn_cast<ArrayExpr>(anchor);
2398-
if (!arrayExpr)
2399-
return false;
2400-
2401-
auto type = contextualType->lookThroughAllOptionalTypes();
2402-
if (!conformsToKnownProtocol(
2403-
cs, type, KnownProtocolKind::ExpressibleByDictionaryLiteral))
2404-
return false;
2405-
2406-
return (arrayExpr->getNumElements() & 1) == 0;
2407-
}
2408-
2409-
bool ContextualFailure::diagnoseConversionToDictionary() const {
2410-
auto &cs = getConstraintSystem();
2411-
auto toType = getToType()->lookThroughAllOptionalTypes();
2412-
2413-
if (!isInvalidDictionaryConversion(cs, getAnchor(), toType))
2414-
return false;
2415-
2416-
auto *arrayExpr = cast<ArrayExpr>(getAnchor());
2417-
2418-
// If the contextual type conforms to ExpressibleByDictionaryLiteral and
2419-
// this is an empty array, then they meant "[:]".
2420-
auto numElements = arrayExpr->getNumElements();
2421-
if (numElements == 0) {
2422-
emitDiagnostic(arrayExpr->getStartLoc(),
2423-
diag::should_use_empty_dictionary_literal)
2424-
.fixItInsert(arrayExpr->getEndLoc(), ":");
2425-
return true;
2426-
}
2427-
2428-
// If the contextual type conforms to ExpressibleByDictionaryLiteral, then
2429-
// they wrote "x = [1,2]" but probably meant "x = [1:2]".
2430-
bool isIniting = getContextualTypePurpose() == CTP_Initialization;
2431-
emitDiagnostic(arrayExpr->getStartLoc(), diag::should_use_dictionary_literal,
2432-
toType, isIniting);
2433-
2434-
auto diagnostic =
2435-
emitDiagnostic(arrayExpr->getStartLoc(), diag::meant_dictionary_lit);
2436-
2437-
// Change every other comma into a colon, only if the number
2438-
// of commas present matches the number of elements, because
2439-
// otherwise it might a structural problem with the expression
2440-
// e.g. ["a""b": 1].
2441-
const auto commaLocs = arrayExpr->getCommaLocs();
2442-
if (commaLocs.size() == numElements - 1) {
2443-
for (unsigned i = 0, e = numElements / 2; i != e; ++i)
2444-
diagnostic.fixItReplace(commaLocs[i * 2], ":");
2445-
}
2446-
2447-
return true;
2448-
}
2449-
24502403
bool ContextualFailure::diagnoseThrowsTypeMismatch() const {
24512404
// If this is conversion failure due to a return statement with an argument
24522405
// that cannot be coerced to the result type of the function, emit a
@@ -4089,7 +4042,12 @@ bool MissingArgumentsFailure::diagnoseClosure(ClosureExpr *closure) {
40894042
if (locator->isForContextualType()) {
40904043
funcType = cs.getContextualType(locator->getAnchor())->getAs<FunctionType>();
40914044
} else if (auto info = cs.getFunctionArgApplyInfo(locator)) {
4092-
funcType = info->getParamType()->getAs<FunctionType>();
4045+
auto paramType = info->getParamType();
4046+
// Drop a single layer of optionality because argument could get injected
4047+
// into optional and that doesn't contribute to the problem.
4048+
if (auto objectType = paramType->getOptionalObjectType())
4049+
paramType = objectType;
4050+
funcType = paramType->getAs<FunctionType>();
40934051
} else if (locator->isLastElement<LocatorPathElt::ClosureResult>()) {
40944052
// Based on the locator we know this this is something like this:
40954053
// `let _: () -> ((Int) -> Void) = { return {} }`.
@@ -4841,24 +4799,6 @@ bool CollectionElementContextualFailure::diagnoseAsError() {
48414799
auto *anchor = getAnchor();
48424800
auto *locator = getLocator();
48434801

4844-
// Check whether this is situation like `let _: [String: Int] = ["A", 0]`
4845-
// which attempts to convert an array into a dictionary. We have a tailored
4846-
// contextual diagnostic for that, so no need to diagnose element mismatches
4847-
// as well.
4848-
auto &cs = getConstraintSystem();
4849-
auto *rawAnchor = getRawAnchor();
4850-
if (llvm::any_of(cs.getFixes(), [&](const ConstraintFix *fix) -> bool {
4851-
auto *locator = fix->getLocator();
4852-
if (!(fix->getKind() == FixKind::ContextualMismatch &&
4853-
locator->getAnchor() == rawAnchor))
4854-
return false;
4855-
4856-
auto *mismatch = static_cast<const ContextualMismatch *>(fix);
4857-
return isInvalidDictionaryConversion(cs, rawAnchor,
4858-
mismatch->getToType());
4859-
}))
4860-
return false;
4861-
48624802
auto eltType = getFromType();
48634803
auto contextualType = getToType();
48644804

lib/Sema/CSDiagnostics.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -566,10 +566,6 @@ class ContextualFailure : public FailureDiagnostic {
566566
/// Produce a specialized diagnostic if this is an invalid conversion to Bool.
567567
bool diagnoseConversionToBool() const;
568568

569-
/// Produce a specialized diagnostic if this is an attempt to initialize
570-
/// or convert an array literal to a dictionary e.g. `let _: [String: Int] = ["A", 0]`
571-
bool diagnoseConversionToDictionary() const;
572-
573569
/// Produce a specialized diagnostic if this is an attempt to throw
574570
/// something with doesn't conform to `Error`.
575571
bool diagnoseThrowsTypeMismatch() const;
@@ -625,11 +621,6 @@ class ContextualFailure : public FailureDiagnostic {
625621
/// protocol
626622
bool tryProtocolConformanceFixIt(InFlightDiagnostic &diagnostic) const;
627623

628-
/// Check whether this contextual failure represents an invalid
629-
/// conversion from array literal to dictionary.
630-
static bool isInvalidDictionaryConversion(ConstraintSystem &cs, Expr *anchor,
631-
Type contextualType);
632-
633624
private:
634625
Type resolve(Type rawType) const {
635626
return resolveType(rawType)->getWithoutSpecifierType();

lib/Sema/CSFix.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -262,11 +262,15 @@ ContextualMismatch *ContextualMismatch::create(ConstraintSystem &cs, Type lhs,
262262
/// and the contextual type.
263263
static Optional<std::tuple<ContextualTypePurpose, Type, Type>>
264264
getStructuralTypeContext(ConstraintSystem &cs, ConstraintLocator *locator) {
265-
if (auto contextualType = cs.getContextualType(locator->getAnchor())) {
266-
if (auto *anchor = simplifyLocatorToAnchor(locator))
267-
return std::make_tuple(cs.getContextualTypePurpose(locator->getAnchor()),
268-
cs.getType(anchor),
269-
contextualType);
265+
if (locator->findLast<LocatorPathElt::ContextualType>()) {
266+
assert(locator->isLastElement<LocatorPathElt::ContextualType>() ||
267+
locator->isLastElement<LocatorPathElt::FunctionArgument>());
268+
269+
auto *anchor = locator->getAnchor();
270+
auto contextualType = cs.getContextualType(anchor);
271+
auto exprType = cs.getType(anchor);
272+
return std::make_tuple(cs.getContextualTypePurpose(anchor), exprType,
273+
contextualType);
270274
} else if (auto argApplyInfo = cs.getFunctionArgApplyInfo(locator)) {
271275
return std::make_tuple(CTP_CallArgument,
272276
argApplyInfo->getArgType(),

lib/Sema/CSGen.cpp

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1852,7 +1852,56 @@ namespace {
18521852

18531853
return contextualArrayType;
18541854
}
1855-
1855+
1856+
// Produce a specialized diagnostic if this is an attempt to initialize
1857+
// or convert an array literal to a dictionary e.g.
1858+
// `let _: [String: Int] = ["A", 0]`
1859+
auto isDictionaryContextualType = [&](Type contextualType) -> bool {
1860+
if (!contextualType)
1861+
return false;
1862+
1863+
auto type = contextualType->lookThroughAllOptionalTypes();
1864+
if (conformsToKnownProtocol(
1865+
CS, type, KnownProtocolKind::ExpressibleByArrayLiteral))
1866+
return false;
1867+
1868+
return conformsToKnownProtocol(
1869+
CS, type, KnownProtocolKind::ExpressibleByDictionaryLiteral);
1870+
};
1871+
1872+
if (isDictionaryContextualType(contextualType)) {
1873+
auto &DE = CS.getASTContext().Diags;
1874+
auto numElements = expr->getNumElements();
1875+
1876+
if (numElements == 0) {
1877+
DE.diagnose(expr->getStartLoc(),
1878+
diag::should_use_empty_dictionary_literal)
1879+
.fixItInsert(expr->getEndLoc(), ":");
1880+
return nullptr;
1881+
}
1882+
1883+
bool isIniting =
1884+
CS.getContextualTypePurpose(expr) == CTP_Initialization;
1885+
DE.diagnose(expr->getStartLoc(), diag::should_use_dictionary_literal,
1886+
contextualType->lookThroughAllOptionalTypes(), isIniting);
1887+
1888+
auto diagnostic =
1889+
DE.diagnose(expr->getStartLoc(), diag::meant_dictionary_lit);
1890+
1891+
// If there is an even number of elements in the array, let's produce
1892+
// a fix-it which suggests to replace "," with ":" to form a dictionary
1893+
// literal.
1894+
if ((numElements & 1) == 0) {
1895+
const auto commaLocs = expr->getCommaLocs();
1896+
if (commaLocs.size() == numElements - 1) {
1897+
for (unsigned i = 0, e = numElements / 2; i != e; ++i)
1898+
diagnostic.fixItReplace(commaLocs[i * 2], ":");
1899+
}
1900+
}
1901+
1902+
return nullptr;
1903+
}
1904+
18561905
auto arrayTy = CS.createTypeVariable(locator,
18571906
TVO_PrefersSubtypeBinding |
18581907
TVO_CanBindToNoEscape);

0 commit comments

Comments
 (0)