Skip to content

Commit bdc961d

Browse files
committed
[Constraint solver] Do argument label matching during apply simplification.
When simplifying a function application constraint, check the argument labels for that application against the disjunction containing the overload set, disabling any overloads with mis-matching labels. This is staging for several different directions: * Eliminating the argument label matching from performMemberLookup, where it does not belong * More aggressively filtering the overload set when we have some concrete information about argument types * Identifying favored constraints when we have some concrete information about argument types At present, the only easily-visible effect of this change is that we now properly handle argument label matching for non-member functions.
1 parent a11a14a commit bdc961d

File tree

6 files changed

+380
-102
lines changed

6 files changed

+380
-102
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 199 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,47 @@ static Optional<unsigned> scoreParamAndArgNameTypo(StringRef paramName,
9393
return dist;
9494
}
9595

96+
bool constraints::areConservativelyCompatibleArgumentLabels(
97+
OverloadChoice choice,
98+
ArrayRef<Identifier> labels,
99+
bool hasTrailingClosure) {
100+
ValueDecl *decl = nullptr;
101+
Type baseType;
102+
switch (choice.getKind()) {
103+
case OverloadChoiceKind::Decl:
104+
case OverloadChoiceKind::DeclViaBridge:
105+
case OverloadChoiceKind::DeclViaDynamic:
106+
case OverloadChoiceKind::DeclViaUnwrappedOptional:
107+
decl = choice.getDecl();
108+
baseType = choice.getBaseType();
109+
if (baseType)
110+
baseType = baseType->getRValueType();
111+
break;
112+
113+
case OverloadChoiceKind::BaseType:
114+
case OverloadChoiceKind::DynamicMemberLookup:
115+
case OverloadChoiceKind::KeyPathApplication:
116+
case OverloadChoiceKind::TupleIndex:
117+
return true;
118+
}
119+
120+
// This is a member lookup, which generally means that the call arguments
121+
// (if we have any) will apply to the second level of parameters, with
122+
// the member lookup binding the first level. But there are cases where
123+
// we can get an unapplied declaration reference back.
124+
bool hasCurriedSelf;
125+
if (!baseType || baseType->is<ModuleType>()) {
126+
hasCurriedSelf = false;
127+
} else if (baseType->is<AnyMetatypeType>() && decl->isInstanceMember()) {
128+
hasCurriedSelf = false;
129+
} else {
130+
hasCurriedSelf = true;
131+
}
132+
133+
return areConservativelyCompatibleArgumentLabels(
134+
decl, hasCurriedSelf, labels, hasTrailingClosure);
135+
}
136+
96137
bool constraints::
97138
areConservativelyCompatibleArgumentLabels(ValueDecl *decl,
98139
bool hasCurriedSelf,
@@ -3430,31 +3471,6 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
34303471
}
34313472
}
34323473

3433-
/// Determine whether the given declaration has compatible argument
3434-
/// labels.
3435-
auto hasCompatibleArgumentLabels = [&argumentLabels](Type baseObjTy,
3436-
ValueDecl *decl) -> bool {
3437-
if (!argumentLabels)
3438-
return true;
3439-
3440-
// This is a member lookup, which generally means that the call arguments
3441-
// (if we have any) will apply to the second level of parameters, with
3442-
// the member lookup binding the first level. But there are cases where
3443-
// we can get an unapplied declaration reference back.
3444-
bool hasCurriedSelf;
3445-
if (baseObjTy->is<ModuleType>()) {
3446-
hasCurriedSelf = false;
3447-
} else if (baseObjTy->is<AnyMetatypeType>() && decl->isInstanceMember()) {
3448-
hasCurriedSelf = false;
3449-
} else {
3450-
hasCurriedSelf = true;
3451-
}
3452-
3453-
return areConservativelyCompatibleArgumentLabels(decl, hasCurriedSelf,
3454-
argumentLabels->Labels,
3455-
argumentLabels->HasTrailingClosure);
3456-
};
3457-
34583474
// Look for members within the base.
34593475
LookupResult &lookup = lookupMember(instanceTy, memberName);
34603476

@@ -3557,7 +3573,10 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
35573573

35583574
// If the argument labels for this result are incompatible with
35593575
// the call site, skip it.
3560-
if (!hasCompatibleArgumentLabels(baseObjTy, decl)) {
3576+
if (argumentLabels &&
3577+
!areConservativelyCompatibleArgumentLabels(
3578+
candidate, argumentLabels->Labels,
3579+
argumentLabels->HasTrailingClosure)) {
35613580
labelMismatch = true;
35623581
result.addUnviable(candidate, MemberLookupResult::UR_LabelMismatch);
35633582
return;
@@ -4768,6 +4787,145 @@ ConstraintSystem::simplifyKeyPathApplicationConstraint(
47684787
return unsolved();
47694788
}
47704789

4790+
Type ConstraintSystem::simplifyAppliedOverloads(
4791+
Type fnType,
4792+
const FunctionType *argFnType,
4793+
Optional<ArgumentLabelState> argumentLabels,
4794+
ConstraintLocatorBuilder locator) {
4795+
auto fnTypeVar = fnType->getAs<TypeVariableType>();
4796+
if (!fnTypeVar)
4797+
return fnType;
4798+
4799+
// Always work on the representation.
4800+
fnTypeVar = getRepresentative(fnTypeVar);
4801+
4802+
// Dig out the disjunction that describes this overload.
4803+
auto disjunction = getUnboundBindOverloadDisjunction(fnTypeVar);
4804+
if (!disjunction) return fnType;
4805+
4806+
/// The common result type amongst all function overloads.
4807+
Optional<Type> commonResultType;
4808+
auto updateCommonResultType = [&](Type choiceType) {
4809+
auto markFailure = [&] {
4810+
commonResultType = Type();
4811+
};
4812+
4813+
auto choiceFnType = choiceType->getAs<FunctionType>();
4814+
if (!choiceFnType)
4815+
return markFailure();
4816+
4817+
// For now, don't attempt to establish a common result type when there
4818+
// are type parameters.
4819+
Type choiceResultType = choiceFnType->getResult();
4820+
if (choiceResultType->hasTypeParameter())
4821+
return markFailure();
4822+
4823+
// If we haven't seen a common result type yet, record what we found.
4824+
if (!commonResultType) {
4825+
commonResultType = choiceResultType;
4826+
return;
4827+
}
4828+
4829+
// If we already failed, we're done.
4830+
if (commonResultType->isNull())
4831+
return;
4832+
4833+
// If we found something different, fail.
4834+
if (!commonResultType.getValue()->isEqual(choiceResultType))
4835+
return markFailure();
4836+
};
4837+
4838+
// Consider each of the constraints in the disjunction.
4839+
retry_after_fail:
4840+
bool skippedAnyConstraints = false;
4841+
bool labelMismatch = false;
4842+
auto filterResult =
4843+
filterDisjunctions(disjunction, /*restoreOnFail=*/shouldAttemptFixes(),
4844+
[&](Constraint *constraint) {
4845+
// We must have bind-overload constraints.
4846+
// FIXME: This isn't entirely true.
4847+
if (constraint->getKind() != ConstraintKind::BindOverload) {
4848+
skippedAnyConstraints = true;
4849+
return true;
4850+
}
4851+
4852+
// We must be binding the type variable (or a type variable
4853+
// equivalent to it).
4854+
auto boundTypeVar = constraint->getFirstType()->getAs<TypeVariableType>();
4855+
if (!boundTypeVar || getRepresentative(boundTypeVar) != fnTypeVar) {
4856+
skippedAnyConstraints = true;
4857+
return true;
4858+
}
4859+
4860+
auto choice = constraint->getOverloadChoice();
4861+
4862+
// Determine whether the argument labels we have conflict with those of
4863+
// this overload choice.
4864+
if (argumentLabels &&
4865+
!areConservativelyCompatibleArgumentLabels(
4866+
choice, argumentLabels->Labels,
4867+
argumentLabels->HasTrailingClosure)) {
4868+
labelMismatch = true;
4869+
return false;
4870+
}
4871+
4872+
// Determine the type that this choice will have.
4873+
Type choiceType =
4874+
getEffectiveOverloadType(choice, /*allowMembers=*/true,
4875+
constraint->getOverloadUseDC());
4876+
if (!choiceType) {
4877+
skippedAnyConstraints = true;
4878+
return true;
4879+
}
4880+
4881+
// If we have a function type, we can compute a common result type.
4882+
updateCommonResultType(choiceType);
4883+
return true;
4884+
});
4885+
4886+
switch (filterResult) {
4887+
case SolutionKind::Error:
4888+
if (labelMismatch && shouldAttemptFixes()) {
4889+
argumentLabels = None;
4890+
goto retry_after_fail;
4891+
}
4892+
4893+
return Type();
4894+
4895+
case SolutionKind::Solved:
4896+
// We should now have a type for the one remaining overload.
4897+
fnType = getFixedTypeRecursive(fnType, /*wantRValue=*/true);
4898+
break;
4899+
4900+
case SolutionKind::Unsolved:
4901+
break;
4902+
}
4903+
4904+
4905+
// If there was a constraint that we couldn't reason about, don't use the
4906+
// results of any common-type computations.
4907+
if (skippedAnyConstraints)
4908+
return fnType;
4909+
4910+
// If we have a common result type, bind the expected result type to it.
4911+
if (commonResultType && *commonResultType) {
4912+
ASTContext &ctx = getASTContext();
4913+
if (ctx.LangOpts.DebugConstraintSolver) {
4914+
auto &log = ctx.TypeCheckerDebug->getStream();
4915+
log.indent(solverState ? solverState->depth * 2 + 2 : 0)
4916+
<< "(common result type for $T" << fnTypeVar->getID() << " is "
4917+
<< commonResultType->getString()
4918+
<< ")\n";
4919+
}
4920+
4921+
// FIXME: Could also rewrite fnType to include this result type.
4922+
addConstraint(ConstraintKind::Bind, argFnType->getResult(),
4923+
*commonResultType, locator);
4924+
}
4925+
4926+
return fnType;
4927+
}
4928+
47714929
ConstraintSystem::SolutionKind
47724930
ConstraintSystem::simplifyApplicableFnConstraint(
47734931
Type type1,
@@ -4821,23 +4979,16 @@ ConstraintSystem::simplifyApplicableFnConstraint(
48214979

48224980
};
48234981

4824-
// If the right-hand side is a type variable, try to find a common result
4825-
// type in the overload set.
4982+
// If the right-hand side is a type variable, try to simplify the overload
4983+
// set.
48264984
if (auto typeVar = desugar2->getAs<TypeVariableType>()) {
4827-
auto choices = getUnboundBindOverloads(typeVar);
4828-
if (Type resultType = findCommonResultType(choices)) {
4829-
ASTContext &ctx = getASTContext();
4830-
if (ctx.LangOpts.DebugConstraintSolver) {
4831-
auto &log = ctx.TypeCheckerDebug->getStream();
4832-
log.indent(solverState ? solverState->depth * 2 + 2 : 0)
4833-
<< "(common result type for $T" << typeVar->getID() << " is "
4834-
<< resultType.getString()
4835-
<< ")\n";
4836-
}
4985+
auto argumentLabels = getArgumentLabels(*this, locator);
4986+
Type newType2 =
4987+
simplifyAppliedOverloads(type2, func1, argumentLabels, locator);
4988+
if (!newType2)
4989+
return SolutionKind::Error;
48374990

4838-
addConstraint(ConstraintKind::Bind, func1->getResult(), resultType,
4839-
locator);
4840-
}
4991+
desugar2 = newType2->getDesugaredType();
48414992
}
48424993

48434994
// If right-hand side is a type variable, the constraint is unsolved.
@@ -4992,7 +5143,7 @@ lookupDynamicCallableMethods(Type type, ConstraintSystem &CS,
49925143
/// Returns the @dynamicCallable required methods (if they exist) implemented
49935144
/// by a type.
49945145
/// This function may be slow for deep class hierarchies and multiple protocol
4995-
/// conformances, but it is invoked only after other constraint simplification
5146+
/// conformances, but it is invoked only after other constraint simplification
49965147
/// rules fail.
49975148
static DynamicCallableMethods
49985149
getDynamicCallableMethods(Type type, ConstraintSystem &CS,
@@ -6170,11 +6321,13 @@ void ConstraintSystem::simplifyDisjunctionChoice(Constraint *choice) {
61706321
case ConstraintSystem::SolutionKind::Error:
61716322
if (!failedConstraint)
61726323
failedConstraint = choice;
6173-
solverState->retireConstraint(choice);
6324+
if (solverState)
6325+
solverState->retireConstraint(choice);
61746326
break;
61756327

61766328
case ConstraintSystem::SolutionKind::Solved:
6177-
solverState->retireConstraint(choice);
6329+
if (solverState)
6330+
solverState->retireConstraint(choice);
61786331
break;
61796332

61806333
case ConstraintSystem::SolutionKind::Unsolved:
@@ -6184,5 +6337,6 @@ void ConstraintSystem::simplifyDisjunctionChoice(Constraint *choice) {
61846337
}
61856338

61866339
// Record this as a generated constraint.
6187-
solverState->addGeneratedConstraint(choice);
6340+
if (solverState)
6341+
solverState->addGeneratedConstraint(choice);
61886342
}

lib/Sema/CSSolver.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,8 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs)
423423
numDefaultedConstraints = cs.DefaultedConstraints.size();
424424
numCheckedConformances = cs.CheckedConformances.size();
425425
numMissingMembers = cs.MissingMembers.size();
426+
numDisabledConstraints = cs.solverState->getNumDisabledConstraints();
427+
426428
PreviousScore = cs.CurrentScore;
427429

428430
cs.solverState->registerScope(this);
@@ -1289,6 +1291,85 @@ void ConstraintSystem::collectDisjunctions(
12891291
}
12901292
}
12911293

1294+
ConstraintSystem::SolutionKind
1295+
ConstraintSystem::filterDisjunctions(
1296+
Constraint *disjunction, bool restoreOnFail,
1297+
llvm::function_ref<bool(Constraint *)> pred) {
1298+
assert(disjunction->getKind() == ConstraintKind::Disjunction);
1299+
1300+
SmallVector<Constraint *, 4> constraintsToRestoreOnFail;
1301+
unsigned choiceIdx = 0;
1302+
unsigned numEnabledTerms = 0;
1303+
ASTContext &ctx = getASTContext();
1304+
for (unsigned constraintIdx : indices(disjunction->getNestedConstraints())) {
1305+
auto constraint = disjunction->getNestedConstraints()[constraintIdx];
1306+
1307+
// Skip already-disabled constraints.
1308+
if (constraint->isDisabled())
1309+
continue;
1310+
1311+
if (pred(constraint)) {
1312+
++numEnabledTerms;
1313+
choiceIdx = constraintIdx;
1314+
continue;
1315+
}
1316+
1317+
if (ctx.LangOpts.DebugConstraintSolver) {
1318+
auto &log = ctx.TypeCheckerDebug->getStream();
1319+
log.indent(solverState ? solverState->depth * 2 + 2 : 0)
1320+
<< "(disabled disjunction term ";
1321+
constraint->print(log, &ctx.SourceMgr);
1322+
log << ")\n";
1323+
}
1324+
1325+
if (restoreOnFail)
1326+
constraintsToRestoreOnFail.push_back(constraint);
1327+
1328+
if (solverState)
1329+
solverState->disableContraint(constraint);
1330+
else
1331+
constraint->setDisabled();
1332+
}
1333+
1334+
switch (numEnabledTerms) {
1335+
case 0:
1336+
for (auto constraint : constraintsToRestoreOnFail) {
1337+
constraint->setEnabled();
1338+
}
1339+
return SolutionKind::Error;
1340+
1341+
case 1: {
1342+
// Only a single constraint remains. Retire the disjunction and make
1343+
// the remaining constraint active.
1344+
1345+
// Retire the disjunction. It's been solved.
1346+
retireConstraint(disjunction);
1347+
1348+
// Note the choice we made and simplify it. This introduces the
1349+
// new constraint into the system.
1350+
auto choice = disjunction->getNestedConstraints()[choiceIdx];
1351+
if (disjunction->shouldRememberChoice()) {
1352+
recordDisjunctionChoice(disjunction->getLocator(), choiceIdx);
1353+
}
1354+
1355+
if (ctx.LangOpts.DebugConstraintSolver) {
1356+
auto &log = ctx.TypeCheckerDebug->getStream();
1357+
log.indent(solverState ? solverState->depth * 2 + 2 : 0)
1358+
<< "(introducing single enabled disjunction term ";
1359+
choice->print(log, &ctx.SourceMgr);
1360+
log << ")\n";
1361+
}
1362+
1363+
simplifyDisjunctionChoice(choice);
1364+
1365+
return failedConstraint ? SolutionKind::Unsolved : SolutionKind::Solved;
1366+
}
1367+
1368+
default:
1369+
return SolutionKind::Unsolved;
1370+
}
1371+
}
1372+
12921373
// Attempt to find a disjunction of bind constraints where all options
12931374
// in the disjunction are binding the same type variable.
12941375
//

0 commit comments

Comments
 (0)