Skip to content

Commit fa9c3f3

Browse files
committed
[ConstraintSystem] Track missing members via "holes"
Replace specialized `MissingMembers` tracking with more general constraint system "holes" which simplifies solver logic.
1 parent 8afc560 commit fa9c3f3

File tree

4 files changed

+79
-86
lines changed

4 files changed

+79
-86
lines changed

lib/Sema/CSDiagnostics.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2834,18 +2834,6 @@ bool MissingMemberFailure::diagnoseAsError() {
28342834
if (!anchor || !baseExpr)
28352835
return false;
28362836

2837-
if (auto *typeVar = getBaseType()->getAs<TypeVariableType>()) {
2838-
auto &CS = getConstraintSystem();
2839-
auto *memberLoc = typeVar->getImpl().getLocator();
2840-
// Don't try to diagnose anything besides first missing
2841-
// member in the chain. e.g. `x.foo().bar()` let's make
2842-
// sure to diagnose only `foo()` as missing because we
2843-
// don't really know much about what `bar()` is supposed
2844-
// to be.
2845-
if (CS.MissingMembers.count(memberLoc))
2846-
return false;
2847-
}
2848-
28492837
auto baseType = resolveType(getBaseType())->getWithoutSpecifierType();
28502838

28512839
DeclNameLoc nameLoc(anchor->getStartLoc());

lib/Sema/CSSimplify.cpp

Lines changed: 78 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2177,6 +2177,9 @@ static ConstraintFix *fixPropertyWrapperFailure(
21772177
if (!decl->hasValidSignature() || !type)
21782178
return nullptr;
21792179

2180+
if (baseTy->isEqual(type))
2181+
return nullptr;
2182+
21802183
if (!attemptFix(resolvedOverload, decl, type))
21812184
return nullptr;
21822185

@@ -5143,12 +5146,33 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
51435146
// If the lookup found no hits at all (either viable or unviable), diagnose it
51445147
// as such and try to recover in various ways.
51455148
if (shouldAttemptFixes()) {
5146-
// Let's record missing member in constraint system, this helps to prevent
5147-
// stacking up fixes for the same member, because e.g. if its base was of
5148-
// optional type, we'd re-introduce member constraint with optional stripped
5149-
// off to see if the problem is related to base not being explicitly unwrapped.
5150-
if (!MissingMembers.insert(locator))
5151-
return SolutionKind::Error;
5149+
auto fixMissingMember = [&](Type baseTy, Type memberTy,
5150+
ConstraintLocator *locator) -> SolutionKind {
5151+
// Let's check whether there are any generic parameters
5152+
// associated with base type, we'd have to default them
5153+
// to `Any` and record as potential holes if so.
5154+
baseTy.transform([&](Type type) -> Type {
5155+
if (auto *typeVar = type->getAs<TypeVariableType>()) {
5156+
if (typeVar->getImpl().hasRepresentativeOrFixed())
5157+
return type;
5158+
recordHole(typeVar);
5159+
}
5160+
return type;
5161+
});
5162+
5163+
auto *fix =
5164+
DefineMemberBasedOnUse::create(*this, baseTy, member, locator);
5165+
if (recordFix(fix))
5166+
return SolutionKind::Error;
5167+
5168+
// Allow member type to default to `Any` to make it possible to form
5169+
// solutions when contextual type of the result cannot be deduced e.g.
5170+
// `let _ = x.foo`.
5171+
if (auto *memberTypeVar = memberTy->getAs<TypeVariableType>())
5172+
recordHole(memberTypeVar);
5173+
5174+
return SolutionKind::Solved;
5175+
};
51525176

51535177
if (baseObjTy->getOptionalObjectType()) {
51545178
// If the base type was an optional, look through it.
@@ -5170,6 +5194,18 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
51705194
}
51715195
}
51725196

5197+
// Let's check whether the problem is related to optionality of base
5198+
// type, or there is no member with a given name.
5199+
result =
5200+
performMemberLookup(kind, member, baseObjTy->getOptionalObjectType(),
5201+
functionRefKind, locator,
5202+
/*includeInaccessibleMembers*/ true);
5203+
5204+
// If uwrapped type still couldn't find anything for a given name,
5205+
// let's fallback to a "not such member" fix.
5206+
if (result.ViableCandidates.empty() && result.UnviableCandidates.empty())
5207+
return fixMissingMember(origBaseTy, memberTy, locator);
5208+
51735209
// The result of the member access can either be the expected member type
51745210
// (for '!' or optional members with '?'), or the original member type
51755211
// with one extra level of optionality ('?' with non-optional members).
@@ -5197,26 +5233,27 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
51975233
return SolutionKind::Solved;
51985234
}
51995235

5200-
auto solveWithNewBaseOrName = [&](Type baseType, DeclName memberName,
5201-
bool allowFixes = true) -> SolutionKind {
5202-
// Let's re-enable fixes for this member, because
5203-
// the base or member name has been changed.
5204-
if (allowFixes)
5205-
MissingMembers.remove(locator);
5236+
auto solveWithNewBaseOrName = [&](Type baseType,
5237+
DeclName memberName) -> SolutionKind {
52065238
return simplifyMemberConstraint(kind, baseType, memberName, memberTy,
52075239
useDC, functionRefKind, outerAlternatives,
5208-
flags, locatorB);
5240+
flags | TMF_ApplyingFix, locatorB);
52095241
};
52105242

5243+
// If this member reference is a result of a previous fix, let's not allow
5244+
// any more fixes expect when base is optional, because it could also be
5245+
// an IUO which requires a separate fix.
5246+
if (flags.contains(TMF_ApplyingFix))
5247+
return SolutionKind::Error;
5248+
52115249
// Check if any property wrappers on the base of the member lookup have
52125250
// matching members that we can fall back to, or if the type wraps any
52135251
// properties that have matching members.
52145252
if (auto *fix = fixPropertyWrapperFailure(
52155253
*this, baseTy, locator,
52165254
[&](ResolvedOverloadSetListItem *overload, VarDecl *decl,
52175255
Type newBase) {
5218-
return solveWithNewBaseOrName(newBase, member,
5219-
/*allowFixes=*/false) ==
5256+
return solveWithNewBaseOrName(newBase, member) ==
52205257
SolutionKind::Solved;
52215258
})) {
52225259
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
@@ -5283,38 +5320,20 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
52835320
// fake its presence based on use, that makes it possible to diagnose
52845321
// problems related to member lookup more precisely.
52855322

5286-
auto defaultTypeVariableToAny = [&](TypeVariableType *typeVar) {
5287-
// Recording this generic parameter is a potential "hole" in
5288-
// the constraint system.
5289-
recordHole(typeVar);
5290-
// Default all of the generic parameters found in base to `Any`.
5291-
addConstraint(ConstraintKind::Defaultable, typeVar,
5292-
getASTContext().TheAnyType,
5293-
typeVar->getImpl().getLocator());
5294-
};
5295-
5296-
origBaseTy.transform([&](Type type) -> Type {
5297-
if (auto *typeVar = type->getAs<TypeVariableType>()) {
5298-
if (typeVar->getImpl().hasRepresentativeOrFixed())
5299-
return type;
5300-
5301-
defaultTypeVariableToAny(typeVar);
5323+
// If base type is a "hole" there is no reason to record any
5324+
// more "member not found" fixes for chained member references.
5325+
if (auto *baseType = origBaseTy->getMetatypeInstanceType()
5326+
->getRValueType()
5327+
->getAs<TypeVariableType>()) {
5328+
if (isHole(baseType)) {
5329+
increaseScore(SK_Fix);
5330+
if (auto *memberTypeVar = memberTy->getAs<TypeVariableType>())
5331+
recordHole(memberTypeVar);
5332+
return SolutionKind::Solved;
53025333
}
5303-
return type;
5304-
});
5305-
5306-
auto *fix =
5307-
DefineMemberBasedOnUse::create(*this, origBaseTy, member, locator);
5308-
if (recordFix(fix))
5309-
return SolutionKind::Error;
5310-
5311-
// Allow member type to default to `Any` to make it possible to form
5312-
// solutions when contextual type of the result cannot be deduced e.g.
5313-
// `let _ = x.foo`.
5314-
if (auto *memberTypeVar = memberTy->getAs<TypeVariableType>())
5315-
defaultTypeVariableToAny(memberTypeVar);
5334+
}
53165335

5317-
return SolutionKind::Solved;
5336+
return fixMissingMember(origBaseTy, memberTy, locator);
53185337
}
53195338
return SolutionKind::Error;
53205339
}
@@ -6260,18 +6279,19 @@ ConstraintSystem::simplifyApplicableFnConstraint(
62606279
// Let's check if this member couldn't be found and is fixed
62616280
// to exist based on its usage.
62626281
if (auto *memberTy = type2->getAs<TypeVariableType>()) {
6263-
auto *locator = memberTy->getImpl().getLocator();
6264-
if (MissingMembers.count(locator)) {
6282+
if (isHole(memberTy)) {
62656283
auto *funcTy = type1->castTo<FunctionType>();
6284+
auto *locator = memberTy->getImpl().getLocator();
62666285
// Bind type variable associated with member to a type of argument
62676286
// application, which makes it seem like member exists with the
62686287
// types of the parameters matching argument types exactly.
62696288
addConstraint(ConstraintKind::Bind, memberTy, funcTy, locator);
62706289
// There might be no contextual type for result of the application,
62716290
// in cases like `let _ = x.foo()`, so let's default result to `Any`
62726291
// to make expressions like that type-check.
6273-
addConstraint(ConstraintKind::Defaultable, funcTy->getResult(),
6274-
getASTContext().TheAnyType, locator);
6292+
auto resultTy = funcTy->getResult();
6293+
if (auto *typeVar = resultTy->getAs<TypeVariableType>())
6294+
recordHole(typeVar);
62756295
return SolutionKind::Solved;
62766296
}
62776297
}
@@ -7174,6 +7194,15 @@ bool ConstraintSystem::recordFix(ConstraintFix *fix) {
71747194
return false;
71757195
}
71767196

7197+
void ConstraintSystem::recordHole(TypeVariableType *typeVar) {
7198+
assert(typeVar);
7199+
auto *locator = typeVar->getImpl().getLocator();
7200+
if (Holes.insert(locator)) {
7201+
addConstraint(ConstraintKind::Defaultable, typeVar,
7202+
getASTContext().TheAnyType, locator);
7203+
}
7204+
}
7205+
71777206
ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
71787207
ConstraintFix *fix, Type type1, Type type2, ConstraintKind matchKind,
71797208
TypeMatchOptions flags, ConstraintLocatorBuilder locator) {

lib/Sema/CSSolver.cpp

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -125,12 +125,6 @@ Solution ConstraintSystem::finalize() {
125125
}
126126
solution.Fixes.append(Fixes.begin() + firstFixIndex, Fixes.end());
127127

128-
// Remember all of the missing member references encountered,
129-
// that helps diagnostics to avoid emitting error for each
130-
// member in the chain.
131-
for (auto *member : MissingMembers)
132-
solution.MissingMembers.push_back(member);
133-
134128
// Remember all the disjunction choices we made.
135129
for (auto &choice : DisjunctionChoices) {
136130
// We shouldn't ever register disjunction choices multiple times,
@@ -267,10 +261,6 @@ void ConstraintSystem::applySolution(const Solution &solution) {
267261

268262
// Register any fixes produced along this path.
269263
Fixes.append(solution.Fixes.begin(), solution.Fixes.end());
270-
271-
// Register any missing members encountered along this path.
272-
MissingMembers.insert(solution.MissingMembers.begin(),
273-
solution.MissingMembers.end());
274264
}
275265

276266
/// Restore the type variable bindings to what they were before
@@ -460,7 +450,6 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs)
460450
numDefaultedConstraints = cs.DefaultedConstraints.size();
461451
numAddedNodeTypes = cs.addedNodeTypes.size();
462452
numCheckedConformances = cs.CheckedConformances.size();
463-
numMissingMembers = cs.MissingMembers.size();
464453
numDisabledConstraints = cs.solverState->getNumDisabledConstraints();
465454
numFavoredConstraints = cs.solverState->getNumFavoredConstraints();
466455
numBuilderTransformedClosures = cs.builderTransformedClosures.size();
@@ -529,9 +518,6 @@ ConstraintSystem::SolverScope::~SolverScope() {
529518
// Remove any conformances checked along the current path.
530519
truncate(cs.CheckedConformances, numCheckedConformances);
531520

532-
// Remove any missing members found along the current path.
533-
truncate(cs.MissingMembers, numMissingMembers);
534-
535521
/// Remove any builder transformed closures.
536522
truncate(cs.builderTransformedClosures, numBuilderTransformedClosures);
537523

lib/Sema/ConstraintSystem.h

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -634,9 +634,6 @@ class Solution {
634634
/// The list of fixes that need to be applied to the initial expression
635635
/// to make the solution work.
636636
llvm::SmallVector<ConstraintFix *, 4> Fixes;
637-
/// The list of member references which couldn't be resolved,
638-
/// and had to be assumed based on their use.
639-
llvm::SmallVector<ConstraintLocator *, 4> MissingMembers;
640637

641638
/// The set of disjunction choices used to arrive at this solution,
642639
/// which informs constraint application.
@@ -1103,9 +1100,6 @@ class ConstraintSystem {
11031100
/// parameter which hasn't been explicitly specified.
11041101
llvm::SmallSetVector<ConstraintLocator *, 4> Holes;
11051102

1106-
/// The set of type variables representing types of missing members.
1107-
llvm::SmallSetVector<ConstraintLocator *, 4> MissingMembers;
1108-
11091103
/// The set of remembered disjunction choices used to reach
11101104
/// the current constraint system.
11111105
std::vector<std::pair<ConstraintLocator*, unsigned>>
@@ -1661,8 +1655,6 @@ class ConstraintSystem {
16611655

16621656
unsigned numCheckedConformances;
16631657

1664-
unsigned numMissingMembers;
1665-
16661658
unsigned numDisabledConstraints;
16671659

16681660
unsigned numFavoredConstraints;
@@ -2045,9 +2037,7 @@ class ConstraintSystem {
20452037
/// subsequent solution would be worse than the best known solution.
20462038
bool recordFix(ConstraintFix *fix);
20472039

2048-
void recordHole(TypeVariableType *typeVar) {
2049-
Holes.insert(typeVar->getImpl().getLocator());
2050-
}
2040+
void recordHole(TypeVariableType *typeVar);
20512041

20522042
bool isHole(TypeVariableType *typeVar) const {
20532043
return isHoleAt(typeVar->getImpl().getLocator());

0 commit comments

Comments
 (0)