Skip to content

Commit 406b7a6

Browse files
authored
Merge pull request #65718 from xedin/diagnose-missing-each-in-expr-context
[ConstraintSystem] Detect and diagnose missing 'each' and provide a fix-it
2 parents 61ea49c + e3d1317 commit 406b7a6

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
@@ -5440,6 +5440,9 @@ ERROR(pack_reference_must_be_in_expansion,none,
54405440
ERROR(pack_type_requires_keyword_each,none,
54415441
"type pack %0 must be referenced with 'each'",
54425442
(TypeRepr*))
5443+
ERROR(value_pack_requires_keyword_each,none,
5444+
"value pack %0 must be referenced with 'each'",
5445+
(Type))
54435446
ERROR(tuple_duplicate_label,none,
54445447
"cannot create a tuple with a duplicate element label", ())
54455448
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
@@ -444,6 +444,9 @@ enum class FixKind : uint8_t {
444444

445445
/// Allow value pack expansion without pack references.
446446
AllowValueExpansionWithoutPackReferences,
447+
448+
/// Ignore missing 'each' keyword before value pack reference.
449+
IgnoreMissingEachKeyword,
447450
};
448451

449452
class ConstraintFix {
@@ -3506,6 +3509,33 @@ class AllowValueExpansionWithoutPackReferences final : public ConstraintFix {
35063509
}
35073510
};
35083511

3512+
class IgnoreMissingEachKeyword final : public ConstraintFix {
3513+
Type ValuePackType;
3514+
3515+
IgnoreMissingEachKeyword(ConstraintSystem &cs, Type valuePackTy,
3516+
ConstraintLocator *locator)
3517+
: ConstraintFix(cs, FixKind::IgnoreMissingEachKeyword, locator),
3518+
ValuePackType(valuePackTy) {}
3519+
3520+
public:
3521+
std::string getName() const override {
3522+
return "allow value pack reference without 'each'";
3523+
}
3524+
3525+
bool diagnose(const Solution &solution, bool asNote = false) const override;
3526+
3527+
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
3528+
return diagnose(*commonFixes.front().first);
3529+
}
3530+
3531+
static IgnoreMissingEachKeyword *
3532+
create(ConstraintSystem &cs, Type valuePackTy, ConstraintLocator *locator);
3533+
3534+
static bool classof(const ConstraintFix *fix) {
3535+
return fix->getKind() == FixKind::IgnoreMissingEachKeyword;
3536+
}
3537+
};
3538+
35093539
} // end namespace constraints
35103540
} // end namespace swift
35113541

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
@@ -8965,3 +8965,25 @@ bool ValuePackExpansionWithoutPackReferences::diagnoseAsError() {
89658965
emitDiagnostic(diag::value_expansion_not_variadic);
89668966
return true;
89678967
}
8968+
8969+
bool MissingEachForValuePackReference::diagnoseAsError() {
8970+
bool fixItNeedsParens = false;
8971+
// If 'each' is missing form a base of a member reference
8972+
// it has to be wrapped in parens.
8973+
if (auto anchor = getAsExpr(getAnchor())) {
8974+
fixItNeedsParens = isExpr<UnresolvedDotExpr>(findParentExpr(anchor));
8975+
}
8976+
8977+
{
8978+
auto diagnostic = emitDiagnostic(diag::value_pack_requires_keyword_each, ValuePackType);
8979+
if (fixItNeedsParens) {
8980+
auto range = getSourceRange();
8981+
diagnostic.fixItInsert(range.Start, "(each ")
8982+
.fixItInsertAfter(range.End, ")");
8983+
} else {
8984+
diagnostic.fixItInsert(getLoc(), "each ");
8985+
}
8986+
}
8987+
8988+
return true;
8989+
}

lib/Sema/CSDiagnostics.h

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

3038+
/// Diagnose situations where value pack is referenced without explicit 'each':
3039+
///
3040+
/// \code
3041+
/// func compute<each T>(_: repeat each T) {}
3042+
///
3043+
/// func test<each T>(v: repeat each T) {
3044+
/// repeat compute(v) // should be `repeat compute(each v)`
3045+
/// }
3046+
/// \endcode
3047+
class MissingEachForValuePackReference final : public FailureDiagnostic {
3048+
Type ValuePackType;
3049+
3050+
public:
3051+
MissingEachForValuePackReference(const Solution &solution, Type valuePackTy,
3052+
ConstraintLocator *locator)
3053+
: FailureDiagnostic(solution, locator),
3054+
ValuePackType(resolveType(valuePackTy)) {}
3055+
3056+
bool diagnoseAsError() override;
3057+
};
3058+
30383059
} // end namespace constraints
30393060
} // end namespace swift
30403061

lib/Sema/CSFix.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2776,3 +2776,17 @@ AllowValueExpansionWithoutPackReferences::create(ConstraintSystem &cs,
27762776
return new (cs.getAllocator())
27772777
AllowValueExpansionWithoutPackReferences(cs, locator);
27782778
}
2779+
2780+
bool IgnoreMissingEachKeyword::diagnose(const Solution &solution,
2781+
bool asNote) const {
2782+
MissingEachForValuePackReference failure(solution, ValuePackType,
2783+
getLocator());
2784+
return failure.diagnose(asNote);
2785+
}
2786+
2787+
IgnoreMissingEachKeyword *
2788+
IgnoreMissingEachKeyword::create(ConstraintSystem &cs, Type valuePackTy,
2789+
ConstraintLocator *locator) {
2790+
return new (cs.getAllocator())
2791+
IgnoreMissingEachKeyword(cs, valuePackTy, locator);
2792+
}

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());
@@ -3063,6 +3094,67 @@ namespace {
30633094
return variadicSeq;
30643095
}
30653096

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

30913183
if (expandedPacks.empty()) {
30923184
(void)CS.recordFix(AllowValueExpansionWithoutPackReferences::create(
@@ -3118,23 +3210,8 @@ namespace {
31183210
}
31193211

31203212
Type visitPackElementExpr(PackElementExpr *expr) {
3121-
auto packType = CS.getType(expr->getPackRefExpr());
3122-
3123-
// If 'each t' is written outside of a pack expansion expression, allow the
3124-
// type to bind to a hole. The invalid pack reference will be diagnosed when
3125-
// attempting to bind the type variable for the underlying pack reference to
3126-
// a pack type without TVO_CanBindToPack.
3127-
if (PackElementEnvironments.empty()) {
3128-
return CS.createTypeVariable(CS.getConstraintLocator(expr),
3129-
TVO_CanBindToHole |
3130-
TVO_CanBindToNoEscape);
3131-
}
3132-
3133-
// The type of a PackElementExpr is the opened pack element archetype
3134-
// of the pack reference.
3135-
OpenPackElementType openPackElement(CS, CS.getConstraintLocator(expr),
3136-
PackElementEnvironments.back());
3137-
return openPackElement(packType, /*packRepr*/ nullptr);
3213+
return openPackElement(CS.getType(expr->getPackRefExpr()),
3214+
CS.getConstraintLocator(expr));
31383215
}
31393216

31403217
Type visitMaterializePackExpr(MaterializePackExpr *expr) {

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14786,6 +14786,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1478614786
case FixKind::AddExplicitExistentialCoercion:
1478714787
case FixKind::DestructureTupleToMatchPackExpansionParameter:
1478814788
case FixKind::AllowValueExpansionWithoutPackReferences:
14789+
case FixKind::IgnoreMissingEachKeyword:
1478914790
llvm_unreachable("handled elsewhere");
1479014791
}
1479114792

test/Constraints/pack-expansion-expressions.swift

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

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

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

0 commit comments

Comments
 (0)