Skip to content

Commit c30845f

Browse files
committed
[ConstraintSystem] Detect and diagnose missing generic arguments
Introduce a fix to detect and diagnose situations when omitted generic arguments couldn't be deduced by the solver based on the enclosing context. Example: ```swift struct S<T> { } _ = S() // There is not enough context to deduce `T` ``` Resolves: rdar://problem/51203824
1 parent 636b4ce commit c30845f

24 files changed

+342
-84
lines changed

include/swift/AST/TypeRepr.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,10 @@ class GenericIdentTypeRepr final : public ComponentIdentTypeRepr,
365365
ArrayRef<TypeRepr*> GenericArgs,
366366
SourceRange AngleBrackets);
367367

368+
unsigned getNumGenericArgs() const {
369+
return Bits.GenericIdentTypeRepr.NumGenericArgs;
370+
}
371+
368372
ArrayRef<TypeRepr*> getGenericArgs() const {
369373
return {getTrailingObjects<TypeRepr*>(),
370374
Bits.GenericIdentTypeRepr.NumGenericArgs};

lib/Sema/CSDiagnostics.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,14 +1244,11 @@ class MissingGenericArgumentsFailure final : public FailureDiagnostic {
12441244
public:
12451245
MissingGenericArgumentsFailure(Expr *root, ConstraintSystem &cs,
12461246
TypeRepr *baseType,
1247-
ArrayRef<TypeVariableType *> missingParams,
1247+
ArrayRef<GenericTypeParamType *> missingParams,
12481248
ConstraintLocator *locator)
12491249
: FailureDiagnostic(root, cs, locator), BaseType(baseType) {
12501250
assert(!missingParams.empty());
1251-
llvm::transform(missingParams, std::back_inserter(Parameters),
1252-
[](const TypeVariableType *GP) {
1253-
return GP->getImpl().getGenericParameter();
1254-
});
1251+
Parameters.append(missingParams.begin(), missingParams.end());
12551252
}
12561253

12571254
SourceLoc getLoc() const;

lib/Sema/CSFix.cpp

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,3 +540,110 @@ CollectionElementContextualMismatch::create(ConstraintSystem &cs, Type srcType,
540540
return new (cs.getAllocator())
541541
CollectionElementContextualMismatch(cs, srcType, dstType, locator);
542542
}
543+
544+
bool ExplicitlySpecifyGenericArguments::diagnose(Expr *root,
545+
bool asNote) const {
546+
auto &cs = getConstraintSystem();
547+
548+
bool diagnosed = false;
549+
for (const auto &paramsPerAnchor : Parameters) {
550+
auto anchor = paramsPerAnchor.first;
551+
ArrayRef<GenericTypeParamType *> missingParameters = paramsPerAnchor.second;
552+
MissingGenericArgumentsFailure failure(root, cs,
553+
anchor.dyn_cast<TypeRepr *>(),
554+
missingParameters, getLocator());
555+
diagnosed |= failure.diagnose(asNote);
556+
}
557+
558+
return diagnosed;
559+
}
560+
561+
ExplicitlySpecifyGenericArguments *ExplicitlySpecifyGenericArguments::create(
562+
ConstraintSystem &cs, ArrayRef<GenericTypeParamType *> params,
563+
ConstraintLocator *locator) {
564+
llvm::SmallDenseMap<TypeRepr *, llvm::SmallVector<GenericTypeParamType *, 4>>
565+
typeBasedParams;
566+
567+
if (findArgumentLocations(locator->getAnchor(), params,
568+
[&](TypeRepr *base, GenericTypeParamType *GP) {
569+
typeBasedParams[base].push_back(GP);
570+
}))
571+
return new (cs.getAllocator())
572+
ExplicitlySpecifyGenericArguments(cs, typeBasedParams, locator);
573+
574+
return new (cs.getAllocator())
575+
ExplicitlySpecifyGenericArguments(cs, params, locator);
576+
}
577+
578+
bool ExplicitlySpecifyGenericArguments::findArgumentLocations(
579+
Expr *anchor, ArrayRef<GenericTypeParamType *> genericParams,
580+
llvm::function_ref<void(TypeRepr *, GenericTypeParamType *)> callback) {
581+
using Callback = llvm::function_ref<void(TypeRepr *, GenericTypeParamType *)>;
582+
583+
if (!anchor)
584+
return false;
585+
586+
TypeLoc typeLoc;
587+
if (auto *TE = dyn_cast<TypeExpr>(anchor))
588+
typeLoc = TE->getTypeLoc();
589+
else if (auto *ECE = dyn_cast<ExplicitCastExpr>(anchor))
590+
typeLoc = ECE->getCastTypeLoc();
591+
592+
if (!typeLoc.hasLocation())
593+
return false;
594+
595+
llvm::SmallVector<GenericTypeParamType *, 4> params(genericParams.begin(),
596+
genericParams.end());
597+
598+
struct AssociateMissingParams : public ASTWalker {
599+
llvm::SmallVectorImpl<GenericTypeParamType *> &Params;
600+
Callback Fn;
601+
602+
AssociateMissingParams(SmallVectorImpl<GenericTypeParamType *> &params,
603+
Callback callback)
604+
: Params(params), Fn(callback) {}
605+
606+
bool walkToTypeReprPre(TypeRepr *T) override {
607+
if (Params.empty())
608+
return false;
609+
610+
auto *ident = dyn_cast<ComponentIdentTypeRepr>(T);
611+
if (!ident)
612+
return true;
613+
614+
auto *decl = dyn_cast_or_null<GenericTypeDecl>(ident->getBoundDecl());
615+
if (!decl)
616+
return true;
617+
618+
auto *paramList = decl->getGenericParams();
619+
if (!paramList)
620+
return true;
621+
622+
// There could a situation like `S<S>()`, so we need to be
623+
// careful not to point at first `S` because it has all of
624+
// its generic parameters specified.
625+
if (auto *generic = dyn_cast<GenericIdentTypeRepr>(ident)) {
626+
if (paramList->size() == generic->getNumGenericArgs())
627+
return true;
628+
}
629+
630+
for (auto *candidate : paramList->getParams()) {
631+
auto result =
632+
llvm::find_if(Params, [&](const GenericTypeParamType *param) {
633+
return candidate == param->getDecl();
634+
});
635+
636+
if (result != Params.end()) {
637+
Fn(ident, *result);
638+
Params.erase(result);
639+
}
640+
}
641+
642+
// Keep walking.
643+
return true;
644+
}
645+
} paramAssociator(params, callback);
646+
647+
typeLoc.getTypeRepr()->walk(paramAssociator);
648+
return params.empty();
649+
}

lib/Sema/CSFix.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,11 @@ enum class FixKind : uint8_t {
153153
/// Remove `return` or default last expression of single expression
154154
/// function to `Void` to conform to expected result type.
155155
RemoveReturn,
156+
157+
/// Generic parameters could not be inferred and have to be explicitly
158+
/// specified in the source. This fix groups all of the missing arguments
159+
/// associated with single declaration.
160+
ExplicitlySpecifyGenericArguments,
156161
};
157162

158163
class ConstraintFix {
@@ -917,6 +922,53 @@ class CollectionElementContextualMismatch final : public ContextualMismatch {
917922
ConstraintLocator *locator);
918923
};
919924

925+
class ExplicitlySpecifyGenericArguments final : public ConstraintFix {
926+
using Anchor = llvm::PointerUnion<ConstraintLocator *, TypeRepr *>;
927+
928+
llvm::SmallDenseMap<Anchor, llvm::SmallVector<GenericTypeParamType *, 4>>
929+
Parameters;
930+
931+
ExplicitlySpecifyGenericArguments(ConstraintSystem &cs,
932+
ArrayRef<GenericTypeParamType *> params,
933+
ConstraintLocator *locator)
934+
: ConstraintFix(cs, FixKind::ExplicitlySpecifyGenericArguments, locator) {
935+
assert(!params.empty());
936+
Parameters[locator].append(params.begin(), params.end());
937+
}
938+
939+
ExplicitlySpecifyGenericArguments(
940+
ConstraintSystem &cs,
941+
llvm::SmallDenseMap<TypeRepr *,
942+
llvm::SmallVector<GenericTypeParamType *, 4>> &params,
943+
ConstraintLocator *locator)
944+
: ConstraintFix(cs, FixKind::ExplicitlySpecifyGenericArguments, locator) {
945+
assert(!params.empty());
946+
for (const auto &elt : params) {
947+
Parameters.insert(elt);
948+
}
949+
}
950+
951+
public:
952+
std::string getName() const override {
953+
return "default missing generic argument to `Any`";
954+
}
955+
956+
bool diagnose(Expr *root, bool asNote = false) const override;
957+
958+
static ExplicitlySpecifyGenericArguments *
959+
create(ConstraintSystem &cs, ArrayRef<GenericTypeParamType *> params,
960+
ConstraintLocator *locator);
961+
962+
private:
963+
/// Retrieve representative locations for given generic prameters
964+
/// rooted at the given anchor.
965+
///
966+
/// \returns true if all of the parameters have been covered.
967+
static bool findArgumentLocations(
968+
Expr *anchor, ArrayRef<GenericTypeParamType *> genericParams,
969+
llvm::function_ref<void(TypeRepr *, GenericTypeParamType *)> callback);
970+
};
971+
920972
} // end namespace constraints
921973
} // end namespace swift
922974

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6659,6 +6659,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
66596659
case FixKind::AllowAnyObjectKeyPathRoot:
66606660
case FixKind::TreatKeyPathSubscriptIndexAsHashable:
66616661
case FixKind::AllowInvalidRefInKeyPath:
6662+
case FixKind::ExplicitlySpecifyGenericArguments:
66626663
llvm_unreachable("handled elsewhere");
66636664
}
66646665

lib/Sema/CSStep.cpp

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,58 @@ StepResult ComponentStep::take(bool prevFailed) {
275275
// If there are no disjunctions or type variables to bind
276276
// we can't solve this system unless we have free type variables
277277
// allowed in the solution.
278-
if (!CS.solverState->allowsFreeTypeVariables() && CS.hasFreeTypeVariables())
279-
return done(/*isSuccess=*/false);
278+
if (!CS.solverState->allowsFreeTypeVariables() && CS.hasFreeTypeVariables()) {
279+
if (!CS.shouldAttemptFixes())
280+
return done(/*isSuccess=*/false);
281+
282+
// Let's see if all of the free type variables are associated with
283+
// generic parameters and if so, let's default them to `Any` and continue
284+
// solving so we can properly diagnose the problem later by suggesting
285+
// to explictly specify them.
286+
287+
llvm::SmallDenseMap<ConstraintLocator *,
288+
llvm::SmallVector<GenericTypeParamType *, 4>>
289+
defaultableGenericParams;
290+
291+
for (auto *typeVar : CS.getTypeVariables()) {
292+
if (typeVar->getImpl().hasRepresentativeOrFixed())
293+
continue;
294+
295+
// If this free type variable is not a generic parameter
296+
// we are done.
297+
auto *locator = typeVar->getImpl().getLocator();
298+
299+
auto *anchor = locator->getAnchor();
300+
if (!(anchor && locator->isForGenericParameter()))
301+
return done(/*isSuccess=*/false);
302+
303+
// Increment the score for every missing generic argument
304+
// to make ranking of the solutions with different number
305+
// of generic arguments easier.
306+
CS.increaseScore(ScoreKind::SK_Fix);
307+
// Default argument to `Any`.
308+
CS.assignFixedType(typeVar, CS.getASTContext().TheAnyType);
309+
// Note that this generic argument has been given a default value.
310+
CS.DefaultedConstraints.push_back(locator);
311+
312+
auto path = locator->getPath();
313+
// Let's drop `generic parameter '...'` part of the locator to
314+
// group all of the missing generic parameters related to the
315+
// same path together.
316+
defaultableGenericParams[CS.getConstraintLocator(anchor, path.drop_back(),
317+
/*summaryFlags=*/0)]
318+
.push_back(locator->getGenericParameter());
319+
}
320+
321+
for (const auto &missing : defaultableGenericParams) {
322+
auto *locator = missing.first;
323+
auto &missingParams = missing.second;
324+
auto *fix =
325+
ExplicitlySpecifyGenericArguments::create(CS, missingParams, locator);
326+
if (CS.recordFix(fix))
327+
return done(/*isSuccess=*/false);
328+
}
329+
}
280330

281331
// If this solution is worse than the best solution we've seen so far,
282332
// skip it.

lib/Sema/ConstraintSystem.cpp

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,9 @@ void ConstraintSystem::incrementLeafScopes() {
100100

101101
bool ConstraintSystem::hasFreeTypeVariables() {
102102
// Look for any free type variables.
103-
for (auto tv : TypeVariables) {
104-
if (!tv->getImpl().hasRepresentativeOrFixed()) {
105-
return true;
106-
}
107-
}
108-
109-
return false;
103+
return llvm::any_of(TypeVariables, [](const TypeVariableType *typeVar) {
104+
return !typeVar->getImpl().hasRepresentativeOrFixed();
105+
});
110106
}
111107

112108
void ConstraintSystem::addTypeVariable(TypeVariableType *typeVar) {

test/Constraints/array_literal.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func longArray() {
102102
var _=["1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1"]
103103
}
104104

105-
[1,2].map // expected-error {{expression type '((Int) throws -> _) throws -> [_]' is ambiguous without more context}}
105+
[1,2].map // expected-error {{generic parameter 'T' could not be inferred}}
106106

107107

108108
// <rdar://problem/25563498> Type checker crash assigning array literal to type conforming to ArrayProtocol

test/Constraints/bridging.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,9 @@ func takesDictionary<K, V>(_ p: Dictionary<K, V>) {} // expected-note {{in call
223223
func takesArray<T>(_ t: Array<T>) {} // expected-note {{in call to function 'takesArray'}}
224224
func rdar19695671() {
225225
takesSet(NSSet() as! Set) // expected-error{{generic parameter 'T' could not be inferred}}
226-
takesDictionary(NSDictionary() as! Dictionary) // expected-error{{generic parameter 'K' could not be inferred}}
226+
takesDictionary(NSDictionary() as! Dictionary)
227+
// expected-error@-1 {{generic parameter 'K' could not be inferred}}
228+
// expected-error@-2 {{generic parameter 'V' could not be inferred}}
227229
takesArray(NSArray() as! Array) // expected-error{{generic parameter 'T' could not be inferred}}
228230
}
229231

test/Constraints/closures.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,7 @@ func rdar37790062() {
716716
}
717717

718718
// <rdar://problem/39489003>
719-
typealias KeyedItem<K, T> = (key: K, value: T)
719+
typealias KeyedItem<K, T> = (key: K, value: T) // expected-note {{'T' declared as parameter to type 'KeyedItem'}}
720720

721721
protocol Node {
722722
associatedtype T
@@ -730,8 +730,7 @@ extension Node {
730730
func getChild(for key:K)->(key: K, value: T) {
731731
return children.first(where: { (item:KeyedItem) -> Bool in
732732
return item.key == key
733-
// expected-error@-1 {{binary operator '==' cannot be applied to operands of type '_' and 'Self.K'}}
734-
// expected-note@-2 {{overloads for '==' exist with these partially matching parameter lists:}}
733+
// expected-error@-1 {{generic parameter 'T' could not be inferred}}
735734
})!
736735
}
737736
}
@@ -814,13 +813,15 @@ func rdar_40537960() {
814813
init(_: P_40537960) {}
815814
}
816815

817-
struct A<T: Collection, P: P_40537960> {
816+
struct A<T: Collection, P: P_40537960> { // expected-note {{'P' declared as parameter to type 'A'}}
818817
typealias Data = T.Element
819818
init(_: T, fn: (Data) -> R<P>) {}
820819
}
821820

822821
var arr: [S] = []
823-
_ = A(arr, fn: { L($0.v) }) // expected-error {{cannot convert value of type 'L' to closure result type 'R<T>'}}
822+
_ = A(arr, fn: { L($0.v) }) // expected-error {{cannot convert value of type 'L' to closure result type 'R<Any>'}}
823+
// expected-error@-1 {{generic parameter 'P' could not be inferred}}
824+
// expected-note@-2 {{explicitly specify the generic arguments to fix this issue}} {{8-8=<[S], <#P: P_40537960#>>}}
824825
}
825826

826827
// rdar://problem/45659733

test/Constraints/diagnostics.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -903,7 +903,7 @@ func test2208() {
903903

904904
// SR-2164: Erroneous diagnostic when unable to infer generic type
905905

906-
struct SR_2164<A, B> { // expected-note 3 {{'B' declared as parameter to type 'SR_2164'}} expected-note 2 {{'A' declared as parameter to type 'SR_2164'}} expected-note * {{generic type 'SR_2164' declared here}}
906+
struct SR_2164<A, B> { // expected-note 4 {{'B' declared as parameter to type 'SR_2164'}} expected-note 2 {{'A' declared as parameter to type 'SR_2164'}} expected-note * {{generic type 'SR_2164' declared here}}
907907
init(a: A) {}
908908
init(b: B) {}
909909
init(c: Int) {}
@@ -921,7 +921,10 @@ struct SR_2164_Dict<A: Hashable, B> { // expected-note {{'B' declared as paramet
921921

922922
SR_2164(a: 0) // expected-error {{generic parameter 'B' could not be inferred}} expected-note {{explicitly specify the generic arguments to fix this issue}}
923923
SR_2164(b: 1) // expected-error {{generic parameter 'A' could not be inferred}} expected-note {{explicitly specify the generic arguments to fix this issue}}
924-
SR_2164(c: 2) // expected-error {{generic parameter 'A' could not be inferred}} expected-note {{explicitly specify the generic arguments to fix this issue}}
924+
SR_2164(c: 2)
925+
// expected-error@-1 {{generic parameter 'A' could not be inferred}}
926+
// expected-error@-2 {{generic parameter 'B' could not be inferred}}
927+
// expected-note@-3 {{explicitly specify the generic arguments to fix this issue}} {{8-8=<Any, Any>}}
925928
SR_2164(3) // expected-error {{generic parameter 'B' could not be inferred}} expected-note {{explicitly specify the generic arguments to fix this issue}}
926929
SR_2164_Array([4]) // expected-error {{generic parameter 'B' could not be inferred}} expected-note {{explicitly specify the generic arguments to fix this issue}}
927930
SR_2164(e: 5) // expected-error {{generic parameter 'B' could not be inferred}} expected-note {{explicitly specify the generic arguments to fix this issue}}

0 commit comments

Comments
 (0)