Skip to content

Commit aeaa523

Browse files
authored
Merge pull request #69869 from DougGregor/rethrows-parameter-packs
Fix effects checking for rethrowing functions involving parameter packs
2 parents 412a828 + 46f3ba6 commit aeaa523

File tree

2 files changed

+95
-20
lines changed

2 files changed

+95
-20
lines changed

lib/Sema/TypeCheckEffects.cpp

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "swift/AST/ASTWalker.h"
2222
#include "swift/AST/DiagnosticsSema.h"
2323
#include "swift/AST/Effects.h"
24+
#include "swift/AST/GenericEnvironment.h"
2425
#include "swift/AST/Initializer.h"
2526
#include "swift/AST/ParameterList.h"
2627
#include "swift/AST/Pattern.h"
@@ -279,9 +280,10 @@ class AbstractFunction {
279280
TheClosure = closure;
280281
}
281282

282-
explicit AbstractFunction(ParamDecl *parameter)
283-
: TheKind(Kind::Parameter) {
283+
explicit AbstractFunction(ParamDecl *parameter, SubstitutionMap subs)
284+
: TheKind(Kind::Parameter), Substitutions(subs) {
284285
TheParameter = parameter;
286+
285287
}
286288

287289
Kind getKind() const { return TheKind; }
@@ -309,6 +311,24 @@ class AbstractFunction {
309311
llvm_unreachable("bad kind");
310312
}
311313

314+
/// Retrieve the interface type for a parameter based on an index into the
315+
/// substituted parameter type. This
316+
Type getOrigParamInterfaceType(unsigned substIndex) const {
317+
switch (getKind()) {
318+
case Kind::Opaque:
319+
case Kind::Closure:
320+
case Kind::Parameter:
321+
return getType()->castTo<AnyFunctionType>()->getParams()[substIndex]
322+
.getParameterType();
323+
324+
case Kind::Function: {
325+
auto params = getParameterList(static_cast<ValueDecl *>(getFunction()));
326+
auto origIndex = params->getOrigParamIndex(getSubstitutions(), substIndex);
327+
return params->get(origIndex)->getInterfaceType();
328+
}
329+
}
330+
}
331+
312332
bool isAutoClosure() const {
313333
if (getKind() == Kind::Closure)
314334
return isa<AutoClosureExpr>(getClosure());
@@ -386,7 +406,10 @@ class AbstractFunction {
386406
if (auto fn = dyn_cast<AbstractFunctionDecl>(decl)) {
387407
return AbstractFunction(fn, DRE->getDeclRef().getSubstitutions());
388408
} else if (auto param = dyn_cast<ParamDecl>(decl)) {
389-
return AbstractFunction(param);
409+
SubstitutionMap subs;
410+
if (auto genericEnv = param->getDeclContext()->getGenericEnvironmentOfContext())
411+
subs = genericEnv->getForwardingSubstitutionMap();
412+
return AbstractFunction(param, subs);
390413
}
391414

392415
// Closures.
@@ -813,6 +836,8 @@ class Classification {
813836
if (!thrownError || isNeverThrownError(thrownError))
814837
return result;
815838

839+
assert(!thrownError->hasError());
840+
816841
result.ThrowKind = conditionalKind;
817842
result.ThrowReason = reason;
818843
result.ThrownError = thrownError;
@@ -1173,22 +1198,29 @@ class ApplyClassifier {
11731198
// because it only counts for rethrows/reasync purposes if it
11741199
// lines up with a throws/async function parameter in the
11751200
// original type.
1176-
auto *origType = fnRef.getType()->getAs<AnyFunctionType>();
1177-
if (!origType) {
1201+
Type fnInterfaceType = fnRef.getType();
1202+
if (!fnInterfaceType) {
11781203
result.merge(Classification::forInvalidCode());
11791204
return;
11801205
}
11811206

11821207
// Use the most significant result from the arguments.
1183-
auto params = origType->getParams();
1184-
if (params.size() != args->size()) {
1208+
auto *fnSubstType = fnInterfaceType.subst(fnRef.getSubstitutions())
1209+
->getAs<AnyFunctionType>();
1210+
if (!fnSubstType) {
11851211
result.merge(Classification::forInvalidCode());
11861212
return;
11871213
}
11881214

1189-
for (unsigned i = 0, e = params.size(); i < e; ++i) {
1215+
if (fnSubstType->getParams().size() != args->size()) {
1216+
result.merge(Classification::forInvalidCode());
1217+
return;
1218+
}
1219+
1220+
for (unsigned i = 0, e = args->size(); i < e; ++i) {
1221+
Type origParamType = fnRef.getOrigParamInterfaceType(i);
11901222
auto argClassification = classifyArgument(
1191-
args->getExpr(i), params[i].getParameterType(), kind);
1223+
args->getExpr(i), origParamType, fnRef.getSubstitutions(), kind);
11921224

11931225
// Rethrows is untyped, so adjust the thrown error type.
11941226
if (kind == EffectKind::Throws &&
@@ -1284,7 +1316,7 @@ class ApplyClassifier {
12841316
EffectKind kind) {
12851317
switch (fn.getKind()) {
12861318
case AbstractFunction::Opaque: {
1287-
return classifyArgumentByType(fn.getType(),
1319+
return classifyArgumentByType(fn.getType(), fn.getSubstitutions(),
12881320
ConditionalEffectKind::Always, reason,
12891321
kind);
12901322
}
@@ -1323,8 +1355,8 @@ class ApplyClassifier {
13231355
else // otherwise, it throws unconditionally.
13241356
conditional = ConditionalEffectKind::Always;
13251357

1326-
return classifyArgumentByType(param->getTypeInContext(),
1327-
conditional, reason, kind);
1358+
return classifyArgumentByType(
1359+
param->getInterfaceType(), subs, conditional, reason, kind);
13281360
}
13291361

13301362
bool isLocallyDefinedInPolymorphicEffectDeclContext(DeclContext *DC,
@@ -1653,7 +1685,8 @@ class ApplyClassifier {
16531685
}
16541686

16551687
/// Classify an argument being passed to a rethrows/reasync function.
1656-
Classification classifyArgument(Expr *arg, Type paramType, EffectKind kind) {
1688+
Classification classifyArgument(
1689+
Expr *arg, Type paramType, SubstitutionMap subs, EffectKind kind) {
16571690
arg = arg->getValueProvidingExpr();
16581691

16591692
if (auto *defaultArg = dyn_cast<DefaultArgumentExpr>(arg)) {
@@ -1666,7 +1699,7 @@ class ApplyClassifier {
16661699
}
16671700
}
16681701

1669-
return classifyArgumentByType(arg->getType(),
1702+
return classifyArgumentByType(arg->getType(), subs,
16701703
ConditionalEffectKind::Always,
16711704
PotentialEffectReason::forDefaultClosure(),
16721705
kind);
@@ -1691,7 +1724,7 @@ class ApplyClassifier {
16911724
// various tuple operations.
16921725
if (auto paramTupleType = dyn_cast<TupleType>(paramType.getPointer())) {
16931726
if (auto tuple = dyn_cast<TupleExpr>(arg)) {
1694-
return classifyTupleArgument(tuple, paramTupleType, kind);
1727+
return classifyTupleArgument(tuple, paramTupleType, subs, kind);
16951728
}
16961729

16971730
if (paramTupleType->getNumElements() != 1) {
@@ -1700,6 +1733,7 @@ class ApplyClassifier {
17001733
// parameter type included a throwing function type.
17011734
return classifyArgumentByType(
17021735
paramType,
1736+
subs,
17031737
ConditionalEffectKind::Always,
17041738
PotentialEffectReason::forClosure(arg),
17051739
kind);
@@ -1744,6 +1778,7 @@ class ApplyClassifier {
17441778
/// Classify an argument to a rethrows/reasync function that's a tuple literal.
17451779
Classification classifyTupleArgument(TupleExpr *tuple,
17461780
TupleType *paramTupleType,
1781+
SubstitutionMap subs,
17471782
EffectKind kind) {
17481783
if (paramTupleType->getNumElements() != tuple->getNumElements())
17491784
return Classification::forInvalidCode();
@@ -1752,7 +1787,7 @@ class ApplyClassifier {
17521787
for (unsigned i : indices(tuple->getElements())) {
17531788
result.merge(classifyArgument(tuple->getElement(i),
17541789
paramTupleType->getElementType(i),
1755-
kind));
1790+
subs, kind));
17561791
}
17571792
return result;
17581793
}
@@ -1761,7 +1796,8 @@ class ApplyClassifier {
17611796
/// a throws/async function in a way that is permitted to cause a
17621797
/// rethrows/reasync function to throw/async.
17631798
static Classification
1764-
classifyArgumentByType(Type paramType, ConditionalEffectKind conditional,
1799+
classifyArgumentByType(Type paramType, SubstitutionMap subs,
1800+
ConditionalEffectKind conditional,
17651801
PotentialEffectReason reason, EffectKind kind) {
17661802
if (!paramType || paramType->hasError())
17671803
return Classification::forInvalidCode();
@@ -1779,8 +1815,10 @@ class ApplyClassifier {
17791815
return Classification();
17801816

17811817
case EffectKind::Throws:
1782-
if (auto thrownError = fnType->getEffectiveThrownErrorType())
1783-
return Classification::forThrows(*thrownError, conditional, reason);
1818+
if (auto thrownError = fnType->getEffectiveThrownErrorType()) {
1819+
return Classification::forThrows(
1820+
thrownError->subst(subs), conditional, reason);
1821+
}
17841822

17851823
return Classification();
17861824
}
@@ -1792,7 +1830,7 @@ class ApplyClassifier {
17921830

17931831
for (auto eltType : tuple->getElementTypes()) {
17941832
result.merge(
1795-
classifyArgumentByType(eltType, conditional, reason, kind));
1833+
classifyArgumentByType(eltType, subs, conditional, reason, kind));
17961834
}
17971835

17981836
return result;
@@ -2914,6 +2952,14 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
29142952

29152953
auto asyncKind = classification.getConditionalKind(EffectKind::Async);
29162954
E->setNoAsync(asyncKind == ConditionalEffectKind::None);
2955+
} else {
2956+
// HACK: functions can get queued multiple times in
2957+
// definedFunctions, so be sure to be idempotent.
2958+
if (!E->isThrowsSet()) {
2959+
E->setThrows(ThrownErrorDestination());
2960+
}
2961+
2962+
E->setNoAsync(true);
29172963
}
29182964

29192965
// If current apply expression did not type-check, don't attempt

test/decl/func/rethrows.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,3 +659,32 @@ protocol P1 {
659659
func open(p: any P1, s: any Sequence) throws {
660660
_ = p.test(s).map(\.id)
661661
}
662+
663+
// Rethrows checking and parameter packs, oh my.
664+
func rethrowsWithParameterPacks<each Arg>(_ arguments: repeat each Arg, body: () throws -> Void) rethrows { }
665+
666+
enum MyError: Error {
667+
case fail
668+
}
669+
670+
func testRethrowsWithParameterPacks() throws {
671+
try rethrowsWithParameterPacks { throw MyError.fail }
672+
rethrowsWithParameterPacks { }
673+
674+
try rethrowsWithParameterPacks(1) { throw MyError.fail }
675+
rethrowsWithParameterPacks(1) { }
676+
677+
try rethrowsWithParameterPacks(1, "hello") { throw MyError.fail }
678+
rethrowsWithParameterPacks(1, "hello") { }
679+
680+
rethrowsWithParameterPacks { throw MyError.fail }
681+
// expected-error@-1{{call can throw but is not marked with 'try'}}
682+
// expected-note@-2{{call is to 'rethrows' function, but argument function can throw}}
683+
}
684+
685+
// Rethrows checking with the original parameter type providing the cues.
686+
func takesArbitraryAndRethrows<T>(_ value: T, body: () throws -> Void) rethrows { }
687+
688+
func testArbitraryAndRethrows() {
689+
takesArbitraryAndRethrows(throwingFunc) { }
690+
}

0 commit comments

Comments
 (0)