Skip to content

Commit ba92d81

Browse files
authored
Merge pull request #27443 from hborla/ambiguous-generic-parameters
[ConstraintSystem] Extend the ExplicitlySpecifyGenericArguments fix to cover all cases of missing generic parameters.
2 parents 412a0f1 + 33b9d4c commit ba92d81

34 files changed

+249
-372
lines changed

include/swift/AST/Types.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,8 @@ class alignas(1 << TypeAlignInBits) TypeBase {
530530
/// Is this the 'Any' type?
531531
bool isAny();
532532

533+
bool isHole();
534+
533535
/// Does the type have outer parenthesis?
534536
bool hasParenSugar() const { return getKind() == TypeKind::Paren; }
535537

lib/AST/Type.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ bool TypeBase::isAny() {
157157
return isEqual(getASTContext().TheAnyType);
158158
}
159159

160+
bool TypeBase::isHole() {
161+
return isEqual(getASTContext().TheUnresolvedType);
162+
}
163+
160164
bool TypeBase::isAnyClassReferenceType() {
161165
return getCanonicalType().isAnyClassReferenceType();
162166
}

lib/Sema/CSApply.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7455,9 +7455,20 @@ bool ConstraintSystem::applySolutionFixes(Expr *E, const Solution &solution) {
74557455
if (fixes == fixesPerExpr.end())
74567456
return;
74577457

7458-
for (const auto *fix : fixes->second) {
7459-
auto diagnosed = fix->diagnose(root);
7460-
if (fix->isWarning()) {
7458+
// Coalesce fixes with the same locator to avoid duplicating notes.
7459+
llvm::SmallMapVector<ConstraintLocator *,
7460+
llvm::SmallVector<ConstraintFix *, 4>, 4> aggregatedFixes;
7461+
for (auto *fix : fixes->second)
7462+
aggregatedFixes[fix->getLocator()].push_back(fix);
7463+
7464+
for (auto fixesPerLocator : aggregatedFixes) {
7465+
auto fixes = fixesPerLocator.second;
7466+
auto *primaryFix = fixes[0];
7467+
ArrayRef<ConstraintFix *> secondaryFixes{fixes.begin() + 1, fixes.end()};
7468+
7469+
auto *coalescedFix = primaryFix->coalescedWith(secondaryFixes);
7470+
auto diagnosed = coalescedFix->diagnose(root);
7471+
if (coalescedFix->isWarning()) {
74617472
assert(diagnosed && "warnings should always be diagnosed");
74627473
(void)diagnosed;
74637474
} else {

lib/Sema/CSBindings.cpp

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,16 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const {
735735
constraint->getLocator()});
736736
}
737737

738+
// If we don't have any potential bindings, allow generic
739+
// parameters and potential holes to default to `Unresolved`.
740+
if (shouldAttemptFixes() && result.Bindings.empty() &&
741+
(isPotentialHole(typeVar) || result.isGenericParameter())) {
742+
result.IsHole = true;
743+
result.addPotentialBinding({getASTContext().TheUnresolvedType,
744+
AllowedBindingKind::Exact, ConstraintKind::Defaultable, nullptr,
745+
typeVar->getImpl().getLocator()});
746+
}
747+
738748
// Determine if the bindings only constrain the type variable from above with
739749
// an existential type; such a binding is not very helpful because it's
740750
// impossible to enumerate the existential type's subtypes.
@@ -979,31 +989,18 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const {
979989

980990
// If this was from a defaultable binding note that.
981991
if (Binding.isDefaultableBinding()) {
982-
auto *locator = Binding.DefaultableBinding;
983-
// If this default binding comes from a "hole"
984-
// in the constraint system, we have to propagate
985-
// this information and mark this type variable
986-
// as well as mark everything adjacent to it as
987-
// a potential "hole".
988-
//
989-
// Consider this example:
990-
//
991-
// func foo<T: BinaryInteger>(_: T) {}
992-
// foo(.bar) <- Since `.bar` can't be inferred due to
993-
// luck of information about its base type,
994-
// it's member type is going to get defaulted
995-
// to `Any` which has to be propaged to type
996-
// variable associated with `T` and vice versa.
997-
if (cs.shouldAttemptFixes() && cs.isHoleAt(locator)) {
998-
auto &CG = cs.getConstraintGraph();
999-
for (auto *constraint : CG.gatherConstraints(
1000-
TypeVar, ConstraintGraph::GatheringKind::EquivalenceClass)) {
1001-
for (auto *typeVar : constraint->getTypeVariables())
1002-
cs.recordHole(typeVar);
1003-
}
992+
cs.DefaultedConstraints.push_back(Binding.DefaultableBinding);
993+
994+
if (locator->isForGenericParameter() && type->isHole()) {
995+
// Drop `generic parameter '...'` part of the locator to group all of the
996+
// missing generic parameters related to the same path together.
997+
auto path = locator->getPath();
998+
auto genericParam = locator->getGenericParameter();
999+
auto *fix = ExplicitlySpecifyGenericArguments::create(cs, {genericParam},
1000+
cs.getConstraintLocator(locator->getAnchor(), path.drop_back()));
1001+
if (cs.recordFix(fix))
1002+
return true;
10041003
}
1005-
1006-
cs.DefaultedConstraints.push_back(locator);
10071004
}
10081005

10091006
return !cs.failedConstraint && !cs.simplify();

lib/Sema/CSDiag.cpp

Lines changed: 0 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -236,17 +236,6 @@ class FailureDiagnosis :public ASTVisitor<FailureDiagnosis, /*exprresult*/bool>{
236236
/// true.
237237
bool diagnoseAmbiguousMultiStatementClosure(ClosureExpr *closure);
238238

239-
/// Check the associated constraint system to see if it has any opened generic
240-
/// parameters that were not bound to a fixed type. If so, diagnose the
241-
/// problem with an error and return true.
242-
bool diagnoseAmbiguousGenericParameters();
243-
244-
/// Emit an error message about an unbound generic parameter, and emit notes
245-
/// referring to the target of a diagnostic, e.g., the function or parameter
246-
/// being used.
247-
void diagnoseAmbiguousGenericParameter(GenericTypeParamType *paramTy,
248-
Expr *anchor);
249-
250239
/// Produce a diagnostic for a general member-lookup failure (irrespective of
251240
/// the exact expression kind).
252241
bool diagnoseGeneralMemberFailure(Constraint *constraint);
@@ -4529,122 +4518,8 @@ diagnoseAmbiguousMultiStatementClosure(ClosureExpr *closure) {
45294518
return true;
45304519
}
45314520

4532-
/// Check the associated constraint system to see if it has any archetypes
4533-
/// not properly resolved or missing. If so, diagnose the problem with
4534-
/// an error and return true.
4535-
bool FailureDiagnosis::diagnoseAmbiguousGenericParameters() {
4536-
using GenericParameter = std::tuple<GenericTypeParamType *,
4537-
ConstraintLocator *,
4538-
unsigned>;
4539-
4540-
llvm::SmallVector<GenericParameter, 2> unboundParams;
4541-
// Check out all of the type variables lurking in the system. If any free
4542-
// type variables were created when opening generic parameters, diagnose
4543-
// that the generic parameter could not be inferred.
4544-
for (auto tv : CS.getTypeVariables()) {
4545-
auto &impl = tv->getImpl();
4546-
4547-
if (impl.hasRepresentativeOrFixed())
4548-
continue;
4549-
4550-
auto *paramTy = impl.getGenericParameter();
4551-
if (!paramTy)
4552-
continue;
4553-
4554-
// Number of constraints related to particular unbound parameter
4555-
// is significant indicator of the problem, because if there are
4556-
// no constraints associated with it, that means it can't ever be resolved,
4557-
// such helps to diagnose situations like: struct S<A, B> { init(_ a: A) {}}
4558-
// because type B would have no constraints associated with it.
4559-
unsigned numConstraints = 0;
4560-
{
4561-
auto constraints = CS.getConstraintGraph().gatherConstraints(
4562-
tv, ConstraintGraph::GatheringKind::EquivalenceClass,
4563-
[&](Constraint *constraint) -> bool {
4564-
// We are not interested in ConformsTo constraints because
4565-
// we can't derive any concrete type information from them.
4566-
if (constraint->getKind() == ConstraintKind::ConformsTo)
4567-
return false;
4568-
4569-
if (constraint->getKind() == ConstraintKind::Bind) {
4570-
if (auto locator = constraint->getLocator()) {
4571-
auto anchor = locator->getAnchor();
4572-
if (anchor && isa<UnresolvedDotExpr>(anchor))
4573-
return false;
4574-
}
4575-
}
4576-
4577-
return true;
4578-
});
4579-
4580-
numConstraints = constraints.size();
4581-
}
4582-
4583-
auto locator = impl.getLocator();
4584-
unboundParams.emplace_back(paramTy, locator, numConstraints);
4585-
}
4586-
4587-
// We've found unbound generic parameters, let's diagnose
4588-
// based on the number of constraints each one is related to.
4589-
if (!unboundParams.empty()) {
4590-
// Let's prioritize generic parameters that don't have any constraints
4591-
// associated.
4592-
std::stable_sort(unboundParams.begin(), unboundParams.end(),
4593-
[](GenericParameter a, GenericParameter b) {
4594-
return std::get<2>(a) < std::get<2>(b);
4595-
});
4596-
4597-
auto param = unboundParams.front();
4598-
diagnoseAmbiguousGenericParameter(std::get<0>(param),
4599-
std::get<1>(param)->getAnchor());
4600-
return true;
4601-
}
4602-
4603-
return false;
4604-
}
4605-
4606-
/// Emit an error message about an unbound generic parameter existing, and
4607-
/// emit notes referring to the target of a diagnostic, e.g., the function
4608-
/// or parameter being used.
4609-
void FailureDiagnosis::
4610-
diagnoseAmbiguousGenericParameter(GenericTypeParamType *paramTy,
4611-
Expr *anchor) {
4612-
// A very common cause of this diagnostic is a situation where a closure expr
4613-
// has no inferred type, due to being a multiline closure. Check to see if
4614-
// this is the case and (if so), speculatively diagnose that as the problem.
4615-
bool didDiagnose = false;
4616-
expr->forEachChildExpr([&](Expr *subExpr) -> Expr*{
4617-
auto closure = dyn_cast<ClosureExpr>(subExpr);
4618-
if (!didDiagnose && closure)
4619-
didDiagnose = diagnoseAmbiguousMultiStatementClosure(closure);
4620-
4621-
return subExpr;
4622-
});
4623-
4624-
if (didDiagnose) return;
4625-
4626-
4627-
// Otherwise, emit an error message on the expr we have, and emit a note
4628-
// about where the generic parameter came from.
4629-
if (!anchor) {
4630-
auto &tc = CS.getTypeChecker();
4631-
tc.diagnose(expr->getLoc(), diag::unbound_generic_parameter, paramTy);
4632-
return;
4633-
}
4634-
4635-
MissingGenericArgumentsFailure failure(expr, CS, {paramTy},
4636-
CS.getConstraintLocator(anchor));
4637-
failure.diagnoseAsError();
4638-
}
4639-
4640-
46414521
/// Emit an ambiguity diagnostic about the specified expression.
46424522
void FailureDiagnosis::diagnoseAmbiguity(Expr *E) {
4643-
// First, let's try to diagnose any problems related to ambiguous
4644-
// generic parameters present in the constraint system.
4645-
if (diagnoseAmbiguousGenericParameters())
4646-
return;
4647-
46484523
// Unresolved/Anonymous ClosureExprs are common enough that we should give
46494524
// them tailored diagnostics.
46504525
if (auto CE = dyn_cast<ClosureExpr>(E->getValueProvidingExpr())) {

lib/Sema/CSDiagnostics.h

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,23 @@ class FailureDiagnostic {
9898
/// Resolve type variables present in the raw type, if any.
9999
Type resolveType(Type rawType, bool reconstituteSugar = false,
100100
bool wantRValue = true) const {
101-
auto resolvedType = CS.simplifyType(rawType);
102-
103-
if (reconstituteSugar)
104-
resolvedType = resolvedType->reconstituteSugar(/*recursive*/ true);
101+
if (!rawType->hasTypeVariable()) {
102+
if (reconstituteSugar)
103+
rawType = rawType->reconstituteSugar(/*recursive*/ true);
104+
return wantRValue ? rawType->getRValueType() : rawType;
105+
}
105106

106-
return wantRValue ? resolvedType->getRValueType() : resolvedType;
107+
auto &cs = getConstraintSystem();
108+
return cs.simplifyTypeImpl(rawType,
109+
[&](TypeVariableType *typeVar) -> Type {
110+
if (auto fixed = cs.getFixedType(typeVar)) {
111+
auto *genericParam = typeVar->getImpl().getGenericParameter();
112+
if (fixed->isHole() && genericParam)
113+
return genericParam;
114+
return resolveType(fixed, reconstituteSugar, wantRValue);
115+
}
116+
return cs.getRepresentative(typeVar);
117+
});
107118
}
108119

109120
/// Resolve type variables present in the raw type, using generic parameter
@@ -782,12 +793,7 @@ class ContextualFailure : public FailureDiagnostic {
782793

783794
private:
784795
Type resolve(Type rawType) {
785-
auto type = resolveType(rawType)->getWithoutSpecifierType();
786-
if (auto *BGT = type->getAs<BoundGenericType>()) {
787-
if (BGT->hasUnresolvedType())
788-
return BGT->getDecl()->getDeclaredInterfaceType();
789-
}
790-
return type;
796+
return resolveType(rawType)->getWithoutSpecifierType();
791797
}
792798

793799
/// Try to add a fix-it to convert a stored property into a computed

lib/Sema/CSFix.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,25 @@ bool ExplicitlySpecifyGenericArguments::diagnose(Expr *root,
714714
return failure.diagnose(asNote);
715715
}
716716

717+
ConstraintFix *
718+
ExplicitlySpecifyGenericArguments::coalescedWith(ArrayRef<ConstraintFix *> fixes) {
719+
if (fixes.empty())
720+
return this;
721+
722+
auto params = getParameters();
723+
llvm::SmallVector<GenericTypeParamType *, 4> missingParams{params.begin(),
724+
params.end()};
725+
for (auto *otherFix : fixes) {
726+
if (auto *fix = otherFix->getAs<ExplicitlySpecifyGenericArguments>()) {
727+
auto additionalParams = fix->getParameters();
728+
missingParams.append(additionalParams.begin(), additionalParams.end());
729+
}
730+
}
731+
732+
return ExplicitlySpecifyGenericArguments::create(getConstraintSystem(),
733+
missingParams, getLocator());
734+
}
735+
717736
ExplicitlySpecifyGenericArguments *ExplicitlySpecifyGenericArguments::create(
718737
ConstraintSystem &cs, ArrayRef<GenericTypeParamType *> params,
719738
ConstraintLocator *locator) {

lib/Sema/CSFix.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,11 @@ class ConstraintFix {
241241

242242
virtual ~ConstraintFix();
243243

244+
template <typename Fix>
245+
const Fix *getAs() const {
246+
return Fix::classof(this) ? static_cast<const Fix *>(this) : nullptr;
247+
}
248+
244249
FixKind getKind() const { return Kind; }
245250

246251
bool isWarning() const { return IsWarning; }
@@ -251,6 +256,10 @@ class ConstraintFix {
251256
/// root expression and information from constraint system.
252257
virtual bool diagnose(Expr *root, bool asNote = false) const = 0;
253258

259+
virtual ConstraintFix *coalescedWith(ArrayRef<ConstraintFix *> fixes) {
260+
return this;
261+
}
262+
254263
void print(llvm::raw_ostream &Out) const;
255264

256265
SWIFT_DEBUG_DUMP;
@@ -1259,6 +1268,10 @@ class ExplicitlySpecifyGenericArguments final
12591268
}
12601269

12611270
public:
1271+
static bool classof(const ConstraintFix *fix) {
1272+
return fix->getKind() == FixKind::ExplicitlySpecifyGenericArguments;
1273+
}
1274+
12621275
std::string getName() const override {
12631276
return "default missing generic arguments to `Any`";
12641277
}
@@ -1269,6 +1282,8 @@ class ExplicitlySpecifyGenericArguments final
12691282

12701283
bool diagnose(Expr *root, bool asNote = false) const override;
12711284

1285+
ConstraintFix *coalescedWith(ArrayRef<ConstraintFix *> fixes) override;
1286+
12721287
static ExplicitlySpecifyGenericArguments *
12731288
create(ConstraintSystem &cs, ArrayRef<GenericTypeParamType *> params,
12741289
ConstraintLocator *locator);

0 commit comments

Comments
 (0)