Skip to content

Commit 57d504c

Browse files
authored
Merge pull request #60062 from ahoppen/pr/placeholder-for-errors
[CS] Don’t fail constraint generation for ErrorExpr or if type fails to resolve
2 parents d68aa2d + 5ba66aa commit 57d504c

29 files changed

+251
-53
lines changed

include/swift/Sema/CSFix.h

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,13 @@ enum class FixKind : uint8_t {
295295
/// Ignore result builder body if it has `return` statements.
296296
IgnoreResultBuilderWithReturnStmts,
297297

298+
/// Ignore `ErrorExpr` or `ErrorType` during pre-check.
299+
IgnoreInvalidASTNode,
300+
301+
/// Ignore a named pattern whose type we couldn't infer. This issue should
302+
/// already have been diagnosed elsewhere.
303+
IgnoreInvalidNamedPattern,
304+
298305
/// Resolve type of `nil` by providing a contextual type.
299306
SpecifyContextualTypeForNil,
300307

@@ -707,6 +714,26 @@ class ContextualMismatch : public ConstraintFix {
707714

708715
bool diagnose(const Solution &solution, bool asNote = false) const override;
709716

717+
bool coalesceAndDiagnose(const Solution &solution,
718+
ArrayRef<ConstraintFix *> secondaryFixes,
719+
bool asNote = false) const override {
720+
// If the from type or to type is a placeholer type that corresponds to an
721+
// ErrorExpr, the issue has already been diagnosed. There's no need to
722+
// produce another diagnostic for the contextual mismatch complainting that
723+
// a type is not convertible to a placeholder type.
724+
if (auto fromPlaceholder = getFromType()->getAs<PlaceholderType>()) {
725+
if (fromPlaceholder->getOriginator().is<ErrorExpr *>()) {
726+
return true;
727+
}
728+
}
729+
if (auto toPlaceholder = getToType()->getAs<PlaceholderType>()) {
730+
if (toPlaceholder->getOriginator().is<ErrorExpr *>()) {
731+
return true;
732+
}
733+
}
734+
return ConstraintFix::coalesceAndDiagnose(solution, secondaryFixes, asNote);
735+
}
736+
710737
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override;
711738

712739
static ContextualMismatch *create(ConstraintSystem &cs, Type lhs, Type rhs,
@@ -2693,6 +2720,57 @@ class IgnoreResultBuilderWithReturnStmts final
26932720
}
26942721
};
26952722

2723+
class IgnoreInvalidASTNode final : public ConstraintFix {
2724+
IgnoreInvalidASTNode(ConstraintSystem &cs, ConstraintLocator *locator)
2725+
: IgnoreInvalidASTNode(cs, FixKind::IgnoreInvalidASTNode, locator) {}
2726+
2727+
protected:
2728+
IgnoreInvalidASTNode(ConstraintSystem &cs, FixKind kind,
2729+
ConstraintLocator *locator)
2730+
: ConstraintFix(cs, kind, locator) {}
2731+
2732+
public:
2733+
std::string getName() const override { return "ignore invalid AST node"; }
2734+
2735+
bool diagnose(const Solution &solution, bool asNote = false) const override;
2736+
2737+
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
2738+
return diagnose(*commonFixes.front().first);
2739+
}
2740+
2741+
static IgnoreInvalidASTNode *create(ConstraintSystem &cs,
2742+
ConstraintLocator *locator);
2743+
2744+
static bool classof(ConstraintFix *fix) {
2745+
return fix->getKind() == FixKind::IgnoreInvalidASTNode;
2746+
}
2747+
};
2748+
2749+
class IgnoreInvalidNamedPattern final : public ConstraintFix {
2750+
IgnoreInvalidNamedPattern(ConstraintSystem &cs, NamedPattern *pattern,
2751+
ConstraintLocator *locator)
2752+
: ConstraintFix(cs, FixKind::IgnoreInvalidNamedPattern, locator) {}
2753+
2754+
public:
2755+
std::string getName() const override {
2756+
return "specify type for pattern match";
2757+
}
2758+
2759+
bool diagnose(const Solution &solution, bool asNote = false) const override;
2760+
2761+
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
2762+
return diagnose(*commonFixes.front().first);
2763+
}
2764+
2765+
static IgnoreInvalidNamedPattern *create(ConstraintSystem &cs,
2766+
NamedPattern *pattern,
2767+
ConstraintLocator *locator);
2768+
2769+
static bool classof(ConstraintFix *fix) {
2770+
return fix->getKind() == FixKind::IgnoreInvalidNamedPattern;
2771+
}
2772+
};
2773+
26962774
class SpecifyContextualTypeForNil final : public ConstraintFix {
26972775
SpecifyContextualTypeForNil(ConstraintSystem &cs,
26982776
ConstraintLocator *locator)

include/swift/Sema/ConstraintLocatorPathElts.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,9 @@ CUSTOM_LOCATOR_PATH_ELT(SyntacticElement)
239239
/// The element of the pattern binding declaration.
240240
CUSTOM_LOCATOR_PATH_ELT(PatternBindingElement)
241241

242+
/// The variable declared by a named pattern.
243+
SIMPLE_LOCATOR_PATH_ELT(NamedPatternDecl)
244+
242245
#undef LOCATOR_PATH_ELT
243246
#undef CUSTOM_LOCATOR_PATH_ELT
244247
#undef SIMPLE_LOCATOR_PATH_ELT

include/swift/Sema/ConstraintSystem.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,13 @@ T *getAsStmt(ASTNode node) {
640640
return nullptr;
641641
}
642642

643+
template <typename T = Pattern>
644+
T *getAsPattern(ASTNode node) {
645+
if (auto *P = node.dyn_cast<Pattern *>())
646+
return dyn_cast_or_null<T>(P);
647+
return nullptr;
648+
}
649+
643650
SourceLoc getLoc(ASTNode node);
644651
SourceRange getSourceRange(ASTNode node);
645652

lib/Sema/CSBindings.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,16 @@ bool BindingSet::isDelayed() const {
109109
if (locator->directlyAt<NilLiteralExpr>())
110110
return true;
111111

112+
// When inferring the type of a variable in a pattern, delay its resolution
113+
// so that we resolve type variables inside the expression as placeholders
114+
// instead of marking the type of the variable itself as a placeholder. This
115+
// allows us to produce more specific errors because the type variable in
116+
// the expression that introduced the placeholder might be diagnosable using
117+
// fixForHole.
118+
if (locator->isLastElement<LocatorPathElt::NamedPatternDecl>()) {
119+
return true;
120+
}
121+
112122
// It's possible that type of member couldn't be determined,
113123
// and if so it would be beneficial to bind member to a hole
114124
// early to propagate that information down to arguments,
@@ -1979,6 +1989,20 @@ TypeVariableBinding::fixForHole(ConstraintSystem &cs) const {
19791989
return std::make_pair(fix, /*impact=*/(unsigned)10);
19801990
}
19811991

1992+
if (auto pattern = getAsPattern<NamedPattern>(dstLocator->getAnchor())) {
1993+
if (dstLocator->getPath().size() == 1 &&
1994+
dstLocator->isLastElement<LocatorPathElt::NamedPatternDecl>()) {
1995+
// Not being able to infer the type of a variable in a pattern binding
1996+
// decl is more dramatic than anything that could happen inside the
1997+
// expression because we want to preferrably point the diagnostic to a
1998+
// part of the expression that caused us to be unable to infer the
1999+
// variable's type.
2000+
ConstraintFix *fix =
2001+
IgnoreInvalidNamedPattern::create(cs, pattern, dstLocator);
2002+
return std::make_pair(fix, /*impact=*/(unsigned)100);
2003+
}
2004+
}
2005+
19822006
return None;
19832007
}
19842008

lib/Sema/CSFix.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1945,6 +1945,16 @@ IgnoreInvalidResultBuilderBody::create(ConstraintSystem &cs,
19451945
return new (cs.getAllocator()) IgnoreInvalidResultBuilderBody(cs, locator);
19461946
}
19471947

1948+
bool IgnoreInvalidASTNode::diagnose(const Solution &solution,
1949+
bool asNote) const {
1950+
return true; // Already diagnosed by the producer of ErrorExpr or ErrorType.
1951+
}
1952+
1953+
IgnoreInvalidASTNode *IgnoreInvalidASTNode::create(ConstraintSystem &cs,
1954+
ConstraintLocator *locator) {
1955+
return new (cs.getAllocator()) IgnoreInvalidASTNode(cs, locator);
1956+
}
1957+
19481958
bool SpecifyContextualTypeForNil::diagnose(const Solution &solution,
19491959
bool asNote) const {
19501960
MissingContextualTypeForNil failure(solution, getLocator());
@@ -1994,6 +2004,20 @@ IgnoreResultBuilderWithReturnStmts::create(ConstraintSystem &cs, Type builderTy,
19942004
IgnoreResultBuilderWithReturnStmts(cs, builderTy, locator);
19952005
}
19962006

2007+
bool IgnoreInvalidNamedPattern::diagnose(const Solution &solution,
2008+
bool asNote) const {
2009+
// Not being able to infer the type of a pattern should already have been
2010+
// diagnosed on the pattern's initializer or as a structural issue of the AST.
2011+
return true;
2012+
}
2013+
2014+
IgnoreInvalidNamedPattern *
2015+
IgnoreInvalidNamedPattern::create(ConstraintSystem &cs, NamedPattern *pattern,
2016+
ConstraintLocator *locator) {
2017+
return new (cs.getAllocator())
2018+
IgnoreInvalidNamedPattern(cs, pattern, locator);
2019+
}
2020+
19972021
bool SpecifyBaseTypeForOptionalUnresolvedMember::diagnose(
19982022
const Solution &solution, bool asNote) const {
19992023
MemberMissingExplicitBaseTypeFailure failure(solution, MemberName,

lib/Sema/CSGen.cpp

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,11 @@ namespace {
890890
// that member through the base returns a value convertible to the type
891891
// of this expression.
892892
auto baseTy = CS.getType(base);
893+
if (isa<ErrorExpr>(base)) {
894+
return CS.createTypeVariable(
895+
CS.getConstraintLocator(expr, ConstraintLocator::Member),
896+
TVO_CanBindToHole);
897+
}
893898
auto tv = CS.createTypeVariable(
894899
CS.getConstraintLocator(expr, ConstraintLocator::Member),
895900
TVO_CanBindToLValue | TVO_CanBindToNoEscape);
@@ -1068,19 +1073,11 @@ namespace {
10681073
ConstraintSystem &getConstraintSystem() const { return CS; }
10691074

10701075
virtual Type visitErrorExpr(ErrorExpr *E) {
1071-
if (!CS.isForCodeCompletion())
1072-
return nullptr;
1073-
1074-
// For code completion, treat error expressions that don't contain
1075-
// the completion location itself as holes. If an ErrorExpr contains the
1076-
// code completion location, a fallback typecheck is called on the
1077-
// ErrorExpr's OriginalExpr (valid sub-expression) if it had one,
1078-
// independent of the wider expression containing the ErrorExpr, so
1079-
// there's no point attempting to produce a solution for it.
1080-
if (CS.containsCodeCompletionLoc(E))
1081-
return nullptr;
1076+
CS.recordFix(
1077+
IgnoreInvalidASTNode::create(CS, CS.getConstraintLocator(E)));
10821078

1083-
return PlaceholderType::get(CS.getASTContext(), E);
1079+
return CS.createTypeVariable(CS.getConstraintLocator(E),
1080+
TVO_CanBindToHole);
10841081
}
10851082

10861083
virtual Type visitCodeCompletionExpr(CodeCompletionExpr *E) {
@@ -1374,7 +1371,11 @@ namespace {
13741371
const auto result = TypeResolution::resolveContextualType(
13751372
repr, CS.DC, resCtx, genericOpener, placeholderHandler);
13761373
if (result->hasError()) {
1377-
return Type();
1374+
CS.recordFix(
1375+
IgnoreInvalidASTNode::create(CS, CS.getConstraintLocator(locator)));
1376+
1377+
return CS.createTypeVariable(CS.getConstraintLocator(repr),
1378+
TVO_CanBindToHole);
13781379
}
13791380
// Diagnose top-level usages of placeholder types.
13801381
if (isa<PlaceholderTypeRepr>(repr->getWithoutParens())) {
@@ -1600,7 +1601,11 @@ namespace {
16001601

16011602
Type visitUnresolvedSpecializeExpr(UnresolvedSpecializeExpr *expr) {
16021603
auto baseTy = CS.getType(expr->getSubExpr());
1603-
1604+
1605+
if (baseTy->isTypeVariableOrMember()) {
1606+
return baseTy;
1607+
}
1608+
16041609
// We currently only support explicit specialization of generic types.
16051610
// FIXME: We could support explicit function specialization.
16061611
auto &de = CS.getASTContext().Diags;
@@ -2322,8 +2327,10 @@ namespace {
23222327
}
23232328

23242329
if (!varType) {
2325-
varType = CS.createTypeVariable(CS.getConstraintLocator(locator),
2326-
TVO_CanBindToNoEscape);
2330+
varType = CS.createTypeVariable(
2331+
CS.getConstraintLocator(pattern,
2332+
LocatorPathElt::NamedPatternDecl()),
2333+
TVO_CanBindToNoEscape | TVO_CanBindToHole);
23272334

23282335
// If this is either a `weak` declaration or capture e.g.
23292336
// `weak var ...` or `[weak self]`. Let's wrap type variable

lib/Sema/CSSimplify.cpp

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11231,18 +11231,6 @@ ConstraintSystem::simplifyApplicableFnConstraint(
1123111231
// following: $T1 -> $T2.
1123211232
auto func1 = type1->castTo<FunctionType>();
1123311233

11234-
// If a type variable representing "function type" is a hole
11235-
// or it could be bound to some concrete type with a help of
11236-
// a fix, let's propagate holes to the "input" type. Doing so
11237-
// provides more information to upcoming argument and result matching.
11238-
if (shouldAttemptFixes()) {
11239-
if (auto *typeVar = type2->getAs<TypeVariableType>()) {
11240-
auto *locator = typeVar->getImpl().getLocator();
11241-
if (typeVar->isPlaceholder() || hasFixFor(locator))
11242-
recordAnyTypeVarAsPotentialHole(func1);
11243-
}
11244-
}
11245-
1124611234
// Before stripping lvalue-ness and optional types, save the original second
1124711235
// type for handling `func callAsFunction` and `@dynamicCallable`
1124811236
// applications. This supports the following cases:
@@ -11257,6 +11245,29 @@ ConstraintSystem::simplifyApplicableFnConstraint(
1125711245
type2 = getFixedTypeRecursive(type2, flags, /*wantRValue=*/true);
1125811246
auto desugar2 = type2->getDesugaredType();
1125911247

11248+
// If a type variable representing "function type" is a hole
11249+
// or it could be bound to some concrete type with a help of
11250+
// a fix, let's propagate holes to the "input" type. Doing so
11251+
// provides more information to upcoming argument and result matching.
11252+
if (shouldAttemptFixes()) {
11253+
if (auto *typeVar = type2->getAs<TypeVariableType>()) {
11254+
auto *locator = typeVar->getImpl().getLocator();
11255+
if (hasFixFor(locator)) {
11256+
recordAnyTypeVarAsPotentialHole(func1);
11257+
}
11258+
}
11259+
Type underlyingType = desugar2;
11260+
while (auto *MT = underlyingType->getAs<AnyMetatypeType>()) {
11261+
underlyingType = MT->getInstanceType();
11262+
}
11263+
underlyingType =
11264+
getFixedTypeRecursive(underlyingType, flags, /*wantRValue=*/true);
11265+
if (underlyingType->isPlaceholder()) {
11266+
recordAnyTypeVarAsPotentialHole(func1);
11267+
return SolutionKind::Solved;
11268+
}
11269+
}
11270+
1126011271
TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);
1126111272

1126211273
SmallVector<LocatorPathElt, 2> parts;
@@ -12947,6 +12958,12 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1294712958
case FixKind::RenameConflictingPatternVariables: {
1294812959
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
1294912960
}
12961+
case FixKind::IgnoreInvalidASTNode: {
12962+
return recordFix(fix, 10) ? SolutionKind::Error : SolutionKind::Solved;
12963+
}
12964+
case FixKind::IgnoreInvalidNamedPattern: {
12965+
return recordFix(fix, 100) ? SolutionKind::Error : SolutionKind::Solved;
12966+
}
1295012967

1295112968
case FixKind::ExplicitlyConstructRawRepresentable: {
1295212969
// Let's increase impact of this fix for binary operators because

lib/Sema/ConstraintLocator.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const {
9797
case ConstraintLocator::PackType:
9898
case ConstraintLocator::PackElement:
9999
case ConstraintLocator::PatternBindingElement:
100+
case ConstraintLocator::NamedPatternDecl:
100101
return 0;
101102

102103
case ConstraintLocator::FunctionArgument:
@@ -441,6 +442,11 @@ void LocatorPathElt::dump(raw_ostream &out) const {
441442
<< llvm::utostr(patternBindingElt.getIndex());
442443
break;
443444
}
445+
446+
case ConstraintLocator::NamedPatternDecl: {
447+
out << "named pattern decl";
448+
break;
449+
}
444450
}
445451
}
446452

@@ -592,6 +598,12 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const {
592598
out << '@';
593599
expr->getLoc().print(out, *sm);
594600
}
601+
} else if (auto *pattern = anchor.dyn_cast<Pattern *>()) {
602+
out << Pattern::getKindName(pattern->getKind()) << "Pattern";
603+
if (sm) {
604+
out << '@';
605+
pattern->getLoc().print(out, *sm);
606+
}
595607
}
596608

597609
for (auto elt : getPath()) {

lib/Sema/ConstraintSystem.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5256,6 +5256,13 @@ void constraints::simplifyLocator(ASTNode &anchor,
52565256
continue;
52575257
}
52585258

5259+
case ConstraintLocator::NamedPatternDecl: {
5260+
auto pattern = cast<NamedPattern>(anchor.get<Pattern *>());
5261+
anchor = pattern->getDecl();
5262+
path = path.slice(1);
5263+
break;
5264+
}
5265+
52595266
case ConstraintLocator::ImplicitConversion:
52605267
break;
52615268

test/ClangImporter/MixedSource/can_import_objc_idempotent.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
// expected-error@-1 {{cannot find 'CGRect' in scope}}
3131

3232
let (r, s) = square.divided(atDistance: 50, from: .minXEdge)
33-
// expected-error@-1 {{type of expression is ambiguous without more context}}
33+
// expected-error@-1 {{cannot infer contextual base in reference to member 'minXEdge'}}
3434
#endif
3535

3636
#if canImport(MixedWithHeader)

0 commit comments

Comments
 (0)