Skip to content

Commit 4d0ab13

Browse files
authored
Merge pull request #34089 from xedin/nil-as-a-hole
[Diagnostics] Diagnose cases when it's impossible to infer type for `nil` literal
2 parents 35d765f + f96d6d3 commit 4d0ab13

File tree

8 files changed

+136
-79
lines changed

8 files changed

+136
-79
lines changed

lib/Sema/CSBindings.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,15 @@ void ConstraintSystem::PotentialBindings::finalize(
368368
PotentiallyIncomplete = true;
369369
}
370370

371+
// Delay resolution of the `nil` literal to a hole until
372+
// the very end to give it a change to be bound to some
373+
// other type, just like code completion expression which
374+
// relies solely on contextual information.
375+
if (locator->directlyAt<NilLiteralExpr>()) {
376+
FullyBound = true;
377+
PotentiallyIncomplete = true;
378+
}
379+
371380
addPotentialBinding(PotentialBinding::forHole(TypeVar, locator));
372381
}
373382

@@ -1230,6 +1239,8 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const {
12301239
cs.increaseScore(SK_Hole);
12311240

12321241
ConstraintFix *fix = nullptr;
1242+
unsigned fixImpact = 1;
1243+
12331244
if (auto *GP = TypeVar->getImpl().getGenericParameter()) {
12341245
// If it is represetative for a key path root, let's emit a more
12351246
// specific diagnostic.
@@ -1268,9 +1279,14 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const {
12681279
return true;
12691280

12701281
fix = SpecifyKeyPathRootType::create(cs, dstLocator);
1282+
} else if (dstLocator->directlyAt<NilLiteralExpr>()) {
1283+
fix = SpecifyContextualTypeForNil::create(cs, dstLocator);
1284+
// This is a dramatic event, it means that there is absolutely
1285+
// no contextual information to resolve type of `nil`.
1286+
fixImpact = 10;
12711287
}
12721288

1273-
if (fix && cs.recordFix(fix))
1289+
if (fix && cs.recordFix(fix, fixImpact))
12741290
return true;
12751291
}
12761292
}

lib/Sema/CSDiagnostics.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6969,3 +6969,43 @@ bool InvalidEmptyKeyPathFailure::diagnoseAsError() {
69696969
emitDiagnostic(diag::expr_swift_keypath_empty);
69706970
return true;
69716971
}
6972+
6973+
bool MissingContextualTypeForNil::diagnoseAsError() {
6974+
auto *expr = castToExpr<NilLiteralExpr>(getAnchor());
6975+
6976+
// If this is a standalone `nil` literal expression e.g.
6977+
// `_ = nil`, let's diagnose it here because solver can't
6978+
// attempt any types for it.
6979+
auto *parentExpr = findParentExpr(expr);
6980+
6981+
while (parentExpr && isa<IdentityExpr>(parentExpr))
6982+
parentExpr = findParentExpr(parentExpr);
6983+
6984+
// In cases like `_ = nil?` AST would have `nil`
6985+
// wrapped in `BindOptionalExpr`.
6986+
if (parentExpr && isa<BindOptionalExpr>(parentExpr))
6987+
parentExpr = findParentExpr(parentExpr);
6988+
6989+
if (parentExpr) {
6990+
// `_ = nil as? ...`
6991+
if (isa<ConditionalCheckedCastExpr>(parentExpr)) {
6992+
emitDiagnostic(diag::conditional_cast_from_nil);
6993+
return true;
6994+
}
6995+
6996+
// `_ = nil!`
6997+
if (isa<ForceValueExpr>(parentExpr)) {
6998+
emitDiagnostic(diag::cannot_force_unwrap_nil_literal);
6999+
return true;
7000+
}
7001+
7002+
// `_ = nil?`
7003+
if (isa<OptionalEvaluationExpr>(parentExpr)) {
7004+
emitDiagnostic(diag::unresolved_nil_literal);
7005+
return true;
7006+
}
7007+
}
7008+
7009+
emitDiagnostic(diag::unresolved_nil_literal);
7010+
return true;
7011+
}

lib/Sema/CSDiagnostics.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2268,6 +2268,23 @@ class InvalidEmptyKeyPathFailure final : public FailureDiagnostic {
22682268
bool diagnoseAsError() override;
22692269
};
22702270

2271+
/// Diagnose situations where there is no context to determine a
2272+
/// type of `nil` literal e.g.
2273+
///
2274+
/// \code
2275+
/// let _ = nil
2276+
/// let _ = try nil
2277+
/// let _ = nil!
2278+
/// \endcode
2279+
class MissingContextualTypeForNil final : public FailureDiagnostic {
2280+
public:
2281+
MissingContextualTypeForNil(const Solution &solution,
2282+
ConstraintLocator *locator)
2283+
: FailureDiagnostic(solution, locator) {}
2284+
2285+
bool diagnoseAsError() override;
2286+
};
2287+
22712288
} // end namespace constraints
22722289
} // end namespace swift
22732290

lib/Sema/CSFix.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1603,3 +1603,15 @@ IgnoreInvalidFunctionBuilderBody *IgnoreInvalidFunctionBuilderBody::create(
16031603
return new (cs.getAllocator())
16041604
IgnoreInvalidFunctionBuilderBody(cs, phase, locator);
16051605
}
1606+
1607+
bool SpecifyContextualTypeForNil::diagnose(const Solution &solution,
1608+
bool asNote) const {
1609+
MissingContextualTypeForNil failure(solution, getLocator());
1610+
return failure.diagnose(asNote);
1611+
}
1612+
1613+
SpecifyContextualTypeForNil *
1614+
SpecifyContextualTypeForNil::create(ConstraintSystem &cs,
1615+
ConstraintLocator *locator) {
1616+
return new (cs.getAllocator()) SpecifyContextualTypeForNil(cs, locator);
1617+
}

lib/Sema/CSFix.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,9 @@ enum class FixKind : uint8_t {
279279

280280
/// Ignore function builder body which fails `pre-check` call.
281281
IgnoreInvalidFunctionBuilderBody,
282+
283+
/// Resolve type of `nil` by providing a contextual type.
284+
SpecifyContextualTypeForNil,
282285
};
283286

284287
class ConstraintFix {
@@ -2026,6 +2029,26 @@ class IgnoreInvalidFunctionBuilderBody final : public ConstraintFix {
20262029
create(ConstraintSystem &cs, ErrorInPhase phase, ConstraintLocator *locator);
20272030
};
20282031

2032+
class SpecifyContextualTypeForNil final : public ConstraintFix {
2033+
SpecifyContextualTypeForNil(ConstraintSystem &cs,
2034+
ConstraintLocator *locator)
2035+
: ConstraintFix(cs, FixKind::SpecifyContextualTypeForNil, locator) {}
2036+
2037+
public:
2038+
std::string getName() const override {
2039+
return "specify contextual type to resolve `nil` literal";
2040+
}
2041+
2042+
bool diagnose(const Solution &solution, bool asNote = false) const override;
2043+
2044+
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
2045+
return diagnose(*commonFixes.front().first);
2046+
}
2047+
2048+
static SpecifyContextualTypeForNil *create(ConstraintSystem & cs,
2049+
ConstraintLocator * locator);
2050+
};
2051+
20292052
} // end namespace constraints
20302053
} // end namespace swift
20312054

lib/Sema/CSGen.cpp

Lines changed: 18 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,80 +1015,13 @@ namespace {
10151015
}
10161016

10171017
Type visitNilLiteralExpr(NilLiteralExpr *expr) {
1018-
auto &DE = CS.getASTContext().Diags;
1019-
// If this is a standalone `nil` literal expression e.g.
1020-
// `_ = nil`, let's diagnose it here because solver can't
1021-
// attempt any types for it.
1022-
auto *parentExpr = CS.getParentExpr(expr);
1023-
bool hasContextualType = bool(CS.getContextualType(expr));
1024-
1025-
while (parentExpr) {
1026-
if (!isa<IdentityExpr>(parentExpr))
1027-
break;
1028-
1029-
// If there is a parent, use it, otherwise we need
1030-
// to check whether the last parent node in the chain
1031-
// had a contextual type associated with it because
1032-
// in situations like:
1033-
//
1034-
// \code
1035-
// func foo() -> Int? {
1036-
// return (nil)
1037-
// }
1038-
// \endcode
1039-
//
1040-
// parentheses around `nil` are significant.
1041-
if (auto *nextParent = CS.getParentExpr(parentExpr)) {
1042-
parentExpr = nextParent;
1043-
} else {
1044-
hasContextualType |= bool(CS.getContextualType(parentExpr));
1045-
// Since current expression is an identity expr
1046-
// and there are no more parents, let's pretend
1047-
// that `nil` don't have a parent since parens
1048-
// are not semantically significant for further checks.
1049-
parentExpr = nullptr;
1050-
}
1051-
}
1052-
1053-
// In cases like `_ = nil?` AST would have `nil`
1054-
// wrapped in `BindOptionalExpr`.
1055-
if (parentExpr && isa<BindOptionalExpr>(parentExpr))
1056-
parentExpr = CS.getParentExpr(parentExpr);
1057-
1058-
if (parentExpr) {
1059-
// `_ = nil as? ...`
1060-
if (isa<ConditionalCheckedCastExpr>(parentExpr)) {
1061-
DE.diagnose(expr->getLoc(), diag::conditional_cast_from_nil);
1062-
return Type();
1063-
}
1064-
1065-
// `_ = nil!`
1066-
if (isa<ForceValueExpr>(parentExpr)) {
1067-
DE.diagnose(expr->getLoc(), diag::cannot_force_unwrap_nil_literal);
1068-
return Type();
1069-
}
1070-
1071-
// `_ = nil?`
1072-
if (isa<OptionalEvaluationExpr>(parentExpr)) {
1073-
DE.diagnose(expr->getLoc(), diag::unresolved_nil_literal);
1074-
return Type();
1075-
}
1018+
auto literalTy = visitLiteralExpr(expr);
1019+
// Allow `nil` to be a hole so we can diagnose it via a fix
1020+
// if it turns out that there is no contextual information.
1021+
if (auto *typeVar = literalTy->getAs<TypeVariableType>())
1022+
CS.recordPotentialHole(typeVar);
10761023

1077-
// `_ = nil`
1078-
if (auto *assignment = dyn_cast<AssignExpr>(parentExpr)) {
1079-
if (isa<DiscardAssignmentExpr>(assignment->getDest())) {
1080-
DE.diagnose(expr->getLoc(), diag::unresolved_nil_literal);
1081-
return Type();
1082-
}
1083-
}
1084-
}
1085-
1086-
if (!parentExpr && !hasContextualType) {
1087-
DE.diagnose(expr->getLoc(), diag::unresolved_nil_literal);
1088-
return Type();
1089-
}
1090-
1091-
return visitLiteralExpr(expr);
1024+
return literalTy;
10921025
}
10931026

10941027
Type visitFloatLiteralExpr(FloatLiteralExpr *expr) {
@@ -3042,11 +2975,19 @@ namespace {
30422975
TVO_PrefersSubtypeBinding |
30432976
TVO_CanBindToLValue |
30442977
TVO_CanBindToNoEscape);
3045-
2978+
2979+
auto *valueExpr = expr->getSubExpr();
2980+
// It's invalid to force unwrap `nil` literal e.g. `_ = nil!` or
2981+
// `_ = (try nil)!` and similar constructs.
2982+
if (auto *nilLiteral = dyn_cast<NilLiteralExpr>(
2983+
valueExpr->getSemanticsProvidingExpr())) {
2984+
CS.recordFix(SpecifyContextualTypeForNil::create(
2985+
CS, CS.getConstraintLocator(nilLiteral)));
2986+
}
2987+
30462988
// The result is the object type of the optional subexpression.
3047-
CS.addConstraint(ConstraintKind::OptionalObject,
3048-
CS.getType(expr->getSubExpr()), objectTy,
3049-
locator);
2989+
CS.addConstraint(ConstraintKind::OptionalObject, CS.getType(valueExpr),
2990+
objectTy, locator);
30502991
return objectTy;
30512992
}
30522993

lib/Sema/CSSimplify.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5893,6 +5893,12 @@ ConstraintSystem::simplifyOptionalObjectConstraint(
58935893
}
58945894

58955895

5896+
if (optTy->isHole()) {
5897+
if (auto *typeVar = second->getAs<TypeVariableType>())
5898+
recordPotentialHole(typeVar);
5899+
return SolutionKind::Solved;
5900+
}
5901+
58965902
Type objectTy = optTy->getOptionalObjectType();
58975903
// If the base type is not optional, let's attempt a fix (if possible)
58985904
// and assume that `!` is just not there.
@@ -10072,7 +10078,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1007210078
case FixKind::SpecifyKeyPathRootType:
1007310079
case FixKind::SpecifyLabelToAssociateTrailingClosure:
1007410080
case FixKind::AllowKeyPathWithoutComponents:
10075-
case FixKind::IgnoreInvalidFunctionBuilderBody: {
10081+
case FixKind::IgnoreInvalidFunctionBuilderBody:
10082+
case FixKind::SpecifyContextualTypeForNil: {
1007610083
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
1007710084
}
1007810085

test/Constraints/function_builder_diags.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,7 @@ struct TestConstraintGenerationErrors {
440440

441441
@TupleBuilder var nilWithoutContext: String {
442442
let a = nil // expected-error {{'nil' requires a contextual type}}
443+
""
443444
}
444445

445446
func buildTupleClosure() {

0 commit comments

Comments
 (0)