Skip to content

Commit 5126215

Browse files
committed
[ConstraintSystem] Detect and diagnose missing 'each' and provide a fix-it
Diagnose situations where value pack is referenced without an explicit 'each': ```swift func compute<each T>(_: repeat each T) {} func test<each T>(v: repeat each T) { repeat compute(v) // should be `repeat compute(each v)` } ``` (cherry picked from commit 31e0a52) (cherry picked from commit 5836490) (cherry picked from commit eee7e18) (cherry picked from commit e3d1317)
1 parent aba8d13 commit 5126215

File tree

10 files changed

+201
-61
lines changed

10 files changed

+201
-61
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5435,6 +5435,9 @@ ERROR(pack_reference_must_be_in_expansion,none,
54355435
ERROR(pack_type_requires_keyword_each,none,
54365436
"type pack %0 must be referenced with 'each'",
54375437
(TypeRepr*))
5438+
ERROR(value_pack_requires_keyword_each,none,
5439+
"value pack %0 must be referenced with 'each'",
5440+
(Type))
54385441
ERROR(tuple_duplicate_label,none,
54395442
"cannot create a tuple with a duplicate element label", ())
54405443
ERROR(multiple_ellipsis_in_tuple,none,

include/swift/AST/Expr.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3627,8 +3627,6 @@ class PackExpansionExpr final : public Expr {
36273627
PatternExpr = patternExpr;
36283628
}
36293629

3630-
void getExpandedPacks(SmallVectorImpl<ASTNode> &packs);
3631-
36323630
GenericEnvironment *getGenericEnvironment() {
36333631
return Environment;
36343632
}

include/swift/Sema/CSFix.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,9 @@ enum class FixKind : uint8_t {
441441

442442
/// Allow value pack expansion without pack references.
443443
AllowValueExpansionWithoutPackReferences,
444+
445+
/// Ignore missing 'each' keyword before value pack reference.
446+
IgnoreMissingEachKeyword,
444447
};
445448

446449
class ConstraintFix {
@@ -3481,6 +3484,33 @@ class AllowValueExpansionWithoutPackReferences final : public ConstraintFix {
34813484
}
34823485
};
34833486

3487+
class IgnoreMissingEachKeyword final : public ConstraintFix {
3488+
Type ValuePackType;
3489+
3490+
IgnoreMissingEachKeyword(ConstraintSystem &cs, Type valuePackTy,
3491+
ConstraintLocator *locator)
3492+
: ConstraintFix(cs, FixKind::IgnoreMissingEachKeyword, locator),
3493+
ValuePackType(valuePackTy) {}
3494+
3495+
public:
3496+
std::string getName() const override {
3497+
return "allow value pack reference without 'each'";
3498+
}
3499+
3500+
bool diagnose(const Solution &solution, bool asNote = false) const override;
3501+
3502+
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
3503+
return diagnose(*commonFixes.front().first);
3504+
}
3505+
3506+
static IgnoreMissingEachKeyword *
3507+
create(ConstraintSystem &cs, Type valuePackTy, ConstraintLocator *locator);
3508+
3509+
static bool classof(const ConstraintFix *fix) {
3510+
return fix->getKind() == FixKind::IgnoreMissingEachKeyword;
3511+
}
3512+
};
3513+
34843514
} // end namespace constraints
34853515
} // end namespace swift
34863516

lib/AST/Expr.cpp

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,46 +1279,6 @@ PackExpansionExpr::create(ASTContext &ctx, SourceLoc repeatLoc,
12791279
implicit, type);
12801280
}
12811281

1282-
void PackExpansionExpr::getExpandedPacks(SmallVectorImpl<ASTNode> &packs) {
1283-
struct PackCollector : public ASTWalker {
1284-
llvm::SmallVector<ASTNode, 2> packs;
1285-
1286-
/// Walk everything that's available.
1287-
MacroWalking getMacroWalkingBehavior() const override {
1288-
return MacroWalking::ArgumentsAndExpansion;
1289-
}
1290-
1291-
virtual PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
1292-
// Don't walk into nested pack expansions
1293-
if (isa<PackExpansionExpr>(E)) {
1294-
return Action::SkipChildren(E);
1295-
}
1296-
1297-
if (isa<PackElementExpr>(E)) {
1298-
packs.push_back(E);
1299-
}
1300-
1301-
return Action::Continue(E);
1302-
}
1303-
1304-
virtual PreWalkAction walkToTypeReprPre(TypeRepr *T) override {
1305-
// Don't walk into nested pack expansions
1306-
if (isa<PackExpansionTypeRepr>(T)) {
1307-
return Action::SkipChildren();
1308-
}
1309-
1310-
if (isa<PackElementTypeRepr>(T)) {
1311-
packs.push_back(T);
1312-
}
1313-
1314-
return Action::Continue();
1315-
}
1316-
} packCollector;
1317-
1318-
getPatternExpr()->walk(packCollector);
1319-
packs.append(packCollector.packs.begin(), packCollector.packs.end());
1320-
}
1321-
13221282
PackElementExpr *
13231283
PackElementExpr::create(ASTContext &ctx, SourceLoc eachLoc, Expr *packRefExpr,
13241284
bool implicit, Type type) {

lib/Sema/CSDiagnostics.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8904,3 +8904,25 @@ bool ValuePackExpansionWithoutPackReferences::diagnoseAsError() {
89048904
emitDiagnostic(diag::value_expansion_not_variadic);
89058905
return true;
89068906
}
8907+
8908+
bool MissingEachForValuePackReference::diagnoseAsError() {
8909+
bool fixItNeedsParens = false;
8910+
// If 'each' is missing form a base of a member reference
8911+
// it has to be wrapped in parens.
8912+
if (auto anchor = getAsExpr(getAnchor())) {
8913+
fixItNeedsParens = isExpr<UnresolvedDotExpr>(findParentExpr(anchor));
8914+
}
8915+
8916+
{
8917+
auto diagnostic = emitDiagnostic(diag::value_pack_requires_keyword_each, ValuePackType);
8918+
if (fixItNeedsParens) {
8919+
auto range = getSourceRange();
8920+
diagnostic.fixItInsert(range.Start, "(each ")
8921+
.fixItInsertAfter(range.End, ")");
8922+
} else {
8923+
diagnostic.fixItInsert(getLoc(), "each ");
8924+
}
8925+
}
8926+
8927+
return true;
8928+
}

lib/Sema/CSDiagnostics.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3017,6 +3017,27 @@ class ValuePackExpansionWithoutPackReferences final : public FailureDiagnostic {
30173017
bool diagnoseAsError() override;
30183018
};
30193019

3020+
/// Diagnose situations where value pack is referenced without explicit 'each':
3021+
///
3022+
/// \code
3023+
/// func compute<each T>(_: repeat each T) {}
3024+
///
3025+
/// func test<each T>(v: repeat each T) {
3026+
/// repeat compute(v) // should be `repeat compute(each v)`
3027+
/// }
3028+
/// \endcode
3029+
class MissingEachForValuePackReference final : public FailureDiagnostic {
3030+
Type ValuePackType;
3031+
3032+
public:
3033+
MissingEachForValuePackReference(const Solution &solution, Type valuePackTy,
3034+
ConstraintLocator *locator)
3035+
: FailureDiagnostic(solution, locator),
3036+
ValuePackType(resolveType(valuePackTy)) {}
3037+
3038+
bool diagnoseAsError() override;
3039+
};
3040+
30203041
} // end namespace constraints
30213042
} // end namespace swift
30223043

lib/Sema/CSFix.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2769,3 +2769,17 @@ AllowValueExpansionWithoutPackReferences::create(ConstraintSystem &cs,
27692769
return new (cs.getAllocator())
27702770
AllowValueExpansionWithoutPackReferences(cs, locator);
27712771
}
2772+
2773+
bool IgnoreMissingEachKeyword::diagnose(const Solution &solution,
2774+
bool asNote) const {
2775+
MissingEachForValuePackReference failure(solution, ValuePackType,
2776+
getLocator());
2777+
return failure.diagnose(asNote);
2778+
}
2779+
2780+
IgnoreMissingEachKeyword *
2781+
IgnoreMissingEachKeyword::create(ConstraintSystem &cs, Type valuePackTy,
2782+
ConstraintLocator *locator) {
2783+
return new (cs.getAllocator())
2784+
IgnoreMissingEachKeyword(cs, valuePackTy, locator);
2785+
}

lib/Sema/CSGen.cpp

Lines changed: 95 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,23 @@ namespace {
10891089
return outputTy;
10901090
}
10911091

1092+
Type openPackElement(Type packType, ConstraintLocator *locator) {
1093+
// If 'each t' is written outside of a pack expansion expression, allow the
1094+
// type to bind to a hole. The invalid pack reference will be diagnosed when
1095+
// attempting to bind the type variable for the underlying pack reference to
1096+
// a pack type without TVO_CanBindToPack.
1097+
if (PackElementEnvironments.empty()) {
1098+
return CS.createTypeVariable(locator,
1099+
TVO_CanBindToHole | TVO_CanBindToNoEscape);
1100+
}
1101+
1102+
// The type of a PackElementExpr is the opened pack element archetype
1103+
// of the pack reference.
1104+
OpenPackElementType openPackElement(CS, locator,
1105+
PackElementEnvironments.back());
1106+
return openPackElement(packType, /*packRepr*/ nullptr);
1107+
}
1108+
10921109
public:
10931110
ConstraintGenerator(ConstraintSystem &CS, DeclContext *DC)
10941111
: CS(CS), CurDC(DC ? DC : CS.DC), CurrPhase(CS.getPhase()) {
@@ -1407,6 +1424,20 @@ namespace {
14071424
return invalidateReference();
14081425
}
14091426

1427+
// value packs cannot be referenced without `each` immediately
1428+
// preceding them.
1429+
if (auto *expansion = knownType->getAs<PackExpansionType>()) {
1430+
if (!PackElementEnvironments.empty() &&
1431+
!isExpr<PackElementExpr>(CS.getParentExpr(E))) {
1432+
auto packType = expansion->getPatternType();
1433+
(void)CS.recordFix(
1434+
IgnoreMissingEachKeyword::create(CS, packType, locator));
1435+
auto eltType = openPackElement(packType, locator);
1436+
CS.setType(E, eltType);
1437+
return eltType;
1438+
}
1439+
}
1440+
14101441
if (!knownType->hasPlaceholder()) {
14111442
// Set the favored type for this expression to the known type.
14121443
CS.setFavoredType(E, knownType.getPointer());
@@ -3057,6 +3088,67 @@ namespace {
30573088
return variadicSeq;
30583089
}
30593090

3091+
void collectExpandedPacks(PackExpansionExpr *expansion,
3092+
SmallVectorImpl<ASTNode> &packs) {
3093+
struct PackCollector : public ASTWalker {
3094+
private:
3095+
ConstraintSystem &CS;
3096+
SmallVectorImpl<ASTNode> &Packs;
3097+
3098+
public:
3099+
PackCollector(ConstraintSystem &cs, SmallVectorImpl<ASTNode> &packs)
3100+
: CS(cs), Packs(packs) {}
3101+
3102+
/// Walk everything that's available.
3103+
MacroWalking getMacroWalkingBehavior() const override {
3104+
return MacroWalking::ArgumentsAndExpansion;
3105+
}
3106+
3107+
virtual PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
3108+
// Don't walk into nested pack expansions
3109+
if (isa<PackExpansionExpr>(E)) {
3110+
return Action::SkipChildren(E);
3111+
}
3112+
3113+
if (isa<PackElementExpr>(E)) {
3114+
Packs.push_back(E);
3115+
}
3116+
3117+
if (auto *declRef = dyn_cast<DeclRefExpr>(E)) {
3118+
auto type = CS.getTypeIfAvailable(declRef);
3119+
if (!type)
3120+
return Action::Continue(E);
3121+
3122+
if (type->is<ElementArchetypeType>() &&
3123+
CS.hasFixFor(CS.getConstraintLocator(declRef),
3124+
FixKind::IgnoreMissingEachKeyword)) {
3125+
Packs.push_back(PackElementExpr::create(CS.getASTContext(),
3126+
/*eachLoc=*/SourceLoc(),
3127+
declRef,
3128+
/*implicit=*/true));
3129+
}
3130+
}
3131+
3132+
return Action::Continue(E);
3133+
}
3134+
3135+
virtual PreWalkAction walkToTypeReprPre(TypeRepr *T) override {
3136+
// Don't walk into nested pack expansions
3137+
if (isa<PackExpansionTypeRepr>(T)) {
3138+
return Action::SkipChildren();
3139+
}
3140+
3141+
if (isa<PackElementTypeRepr>(T)) {
3142+
Packs.push_back(T);
3143+
}
3144+
3145+
return Action::Continue();
3146+
}
3147+
} packCollector(CS, packs);
3148+
3149+
expansion->getPatternExpr()->walk(packCollector);
3150+
}
3151+
30603152
Type visitPackExpansionExpr(PackExpansionExpr *expr) {
30613153
assert(PackElementEnvironments.back() == expr);
30623154
PackElementEnvironments.pop_back();
@@ -3080,7 +3172,7 @@ namespace {
30803172
// Generate ShapeOf constraints between all packs expanded by this
30813173
// pack expansion expression through the shape type variable.
30823174
SmallVector<ASTNode, 2> expandedPacks;
3083-
expr->getExpandedPacks(expandedPacks);
3175+
collectExpandedPacks(expr, expandedPacks);
30843176

30853177
if (expandedPacks.empty()) {
30863178
(void)CS.recordFix(AllowValueExpansionWithoutPackReferences::create(
@@ -3112,23 +3204,8 @@ namespace {
31123204
}
31133205

31143206
Type visitPackElementExpr(PackElementExpr *expr) {
3115-
auto packType = CS.getType(expr->getPackRefExpr());
3116-
3117-
// If 'each t' is written outside of a pack expansion expression, allow the
3118-
// type to bind to a hole. The invalid pack reference will be diagnosed when
3119-
// attempting to bind the type variable for the underlying pack reference to
3120-
// a pack type without TVO_CanBindToPack.
3121-
if (PackElementEnvironments.empty()) {
3122-
return CS.createTypeVariable(CS.getConstraintLocator(expr),
3123-
TVO_CanBindToHole |
3124-
TVO_CanBindToNoEscape);
3125-
}
3126-
3127-
// The type of a PackElementExpr is the opened pack element archetype
3128-
// of the pack reference.
3129-
OpenPackElementType openPackElement(CS, CS.getConstraintLocator(expr),
3130-
PackElementEnvironments.back());
3131-
return openPackElement(packType, /*packRepr*/ nullptr);
3207+
return openPackElement(CS.getType(expr->getPackRefExpr()),
3208+
CS.getConstraintLocator(expr));
31323209
}
31333210

31343211
Type visitMaterializePackExpr(MaterializePackExpr *expr) {

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14822,6 +14822,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1482214822
case FixKind::AddExplicitExistentialCoercion:
1482314823
case FixKind::DestructureTupleToMatchPackExpansionParameter:
1482414824
case FixKind::AllowValueExpansionWithoutPackReferences:
14825+
case FixKind::IgnoreMissingEachKeyword:
1482514826
llvm_unreachable("handled elsewhere");
1482614827
}
1482714828

test/Constraints/pack-expansion-expressions.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,8 @@ do {
454454

455455
func test_misplaced_each<each T: P>(_ value: repeat each T) -> (repeat each T.A) {
456456
return (repeat each value.makeA())
457-
// expected-error@-1 {{pack reference 'each T' can only appear in pack expansion}}
457+
// expected-error@-1 {{value pack 'each T' must be referenced with 'each'}} {{25-25=(each }} {{30-30=)}}
458+
// expected-error@-2 {{pack expansion requires that '()' and 'each T' have the same shape}}
458459
}
459460
}
460461

@@ -480,3 +481,16 @@ do {
480481
// expected-error@-1:25 {{value pack expansion must contain at least one pack reference}}
481482
}
482483
}
484+
485+
// missing 'each' keyword before value pack references
486+
do {
487+
func overloaded<each U>(_: String, _: repeat each U) -> Int { 42 }
488+
func overloaded<each T>(_: Int, _ b: repeat each T) -> (repeat each T) {
489+
fatalError()
490+
}
491+
492+
func test<each T>(v: repeat each T) {
493+
_ = (repeat overloaded(42, v)) // expected-error {{value pack 'each T' must be referenced with 'each'}} {{32-32=each }}
494+
_ = (repeat overloaded(42, each v)) // Ok
495+
}
496+
}

0 commit comments

Comments
 (0)