Skip to content

Commit e13fe5b

Browse files
authored
Merge pull request #65771 from ahoppen/ahoppen/5.9/complete-parameter-pack-arg
[5.9][CodeCompletion] Fix a crash when completing an argument to a function taking a parameter pack
2 parents 71f1993 + 0cccac3 commit e13fe5b

File tree

6 files changed

+60
-23
lines changed

6 files changed

+60
-23
lines changed

include/swift/IDE/ArgumentCompletion.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ class ArgumentTypeCheckCompletionCallback : public TypeCheckCompletionCallback {
5050
/// Whether the surrounding context is async and thus calling async
5151
/// functions is supported.
5252
bool IsInAsyncContext;
53+
/// A bitfield to mark whether the parameter at a given index is optional.
54+
/// Parameters can be optional if they have a default argument or belong to
55+
/// a parameter pack.
56+
/// Indicies are based on the parameters in \c FuncTy. Note that the number
57+
/// of parameters in \c FuncTy and \c FuncD is different when a parameter
58+
/// pack has been exploded.
59+
std::vector<bool> DeclParamIsOptional;
5360

5461
/// Types of variables that were determined in the solution that produced
5562
/// this result. This in particular includes parameters of closures that

include/swift/IDE/SelectedOverloadInfo.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@ using namespace swift::constraints;
2525
/// \c SelectedOverload.
2626
struct SelectedOverloadInfo {
2727
/// The function that is being called or the value that is being accessed.
28-
ValueDecl *Value = nullptr;
28+
ConcreteDeclRef ValueRef;
2929
/// For a function, type of the called function itself (not its result type),
3030
/// for an arbitrary value the type of that value.
3131
Type ValueTy;
3232
/// The type on which the overload is being accessed. \c null if it does not
3333
/// have a base type, e.g. for a free function.
3434
Type BaseTy;
35+
36+
ValueDecl *getValue() const { return ValueRef.getDecl(); }
3537
};
3638

3739
/// Extract additional information about the overload that is being called by

lib/IDE/ArgumentCompletion.cpp

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,6 @@ bool ArgumentTypeCheckCompletionCallback::addPossibleParams(
3838

3939
ArrayRef<AnyFunctionType::Param> ParamsToPass = Res.FuncTy->getParams();
4040

41-
ParameterList *PL = nullptr;
42-
if (Res.FuncD) {
43-
PL = swift::getParameterList(Res.FuncD);
44-
}
45-
assert(!PL || PL->size() == ParamsToPass.size());
46-
4741
bool ShowGlobalCompletions = false;
4842
for (auto Idx : range(*Res.ParamIdx, ParamsToPass.size())) {
4943
bool IsCompletion = (Idx == Res.ParamIdx);
@@ -53,22 +47,21 @@ bool ArgumentTypeCheckCompletionCallback::addPossibleParams(
5347
break;
5448
}
5549

56-
const AnyFunctionType::Param *P = &ParamsToPass[Idx];
57-
bool Required =
58-
!(PL && PL->get(Idx)->isDefaultArgument()) && !P->isVariadic();
50+
const AnyFunctionType::Param *TypeParam = &ParamsToPass[Idx];
51+
bool Required = !Res.DeclParamIsOptional[Idx];
5952

60-
if (P->hasLabel() && !(IsCompletion && Res.IsNoninitialVariadic)) {
53+
if (TypeParam->hasLabel() && !(IsCompletion && Res.IsNoninitialVariadic)) {
6154
// Suggest parameter label if parameter has label, we are completing in it
6255
// and it is not a variadic parameter that already has arguments
63-
PossibleParamInfo PP(P, Required);
56+
PossibleParamInfo PP(TypeParam, Required);
6457
if (!llvm::is_contained(Params, PP)) {
6558
Params.push_back(std::move(PP));
6659
}
6760
} else {
6861
// We have a parameter that doesn't require a label. Suggest global
6962
// results for that type.
7063
ShowGlobalCompletions = true;
71-
Types.push_back(P->getPlainType());
64+
Types.push_back(TypeParam->getPlainType());
7265
}
7366
if (Required) {
7467
// The user should only be suggested the first required param. Stop.
@@ -157,7 +150,7 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
157150
auto *CalleeLocator = S.getCalleeLocator(CallLocator);
158151

159152
auto Info = getSelectedOverloadInfo(S, CalleeLocator);
160-
if (Info.Value && Info.Value->shouldHideFromEditor()) {
153+
if (Info.getValue() && Info.getValue()->shouldHideFromEditor()) {
161154
return;
162155
}
163156
// Disallow invalid initializer references
@@ -215,7 +208,7 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
215208

216209
// If this is a duplicate of any other result, ignore this solution.
217210
if (llvm::any_of(Results, [&](const Result &R) {
218-
return R.FuncD == Info.Value &&
211+
return R.FuncD == Info.getValue() &&
219212
nullableTypesEqual(R.FuncTy, Info.ValueTy) &&
220213
nullableTypesEqual(R.BaseType, Info.BaseTy) &&
221214
R.ParamIdx == ParamIdx &&
@@ -231,10 +224,34 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
231224
if (Info.ValueTy) {
232225
FuncTy = Info.ValueTy->lookThroughAllOptionalTypes()->getAs<AnyFunctionType>();
233226
}
227+
228+
// Determine which parameters is optional. We need to do this in
229+
// `sawSolutionImpl` because it accesses the substitution map in
230+
// `Info.ValueRef`. This substitution map might contain tyep variables that
231+
// are allocated in the constraint system's arena and are freed once we reach
232+
// `deliverResults`.
233+
std::vector<bool> DeclParamIsOptional;
234+
if (FuncTy) {
235+
ArrayRef<AnyFunctionType::Param> ParamsToPass = FuncTy->getParams();
236+
for (auto Idx : range(0, ParamsToPass.size())) {
237+
bool Optional = false;
238+
if (Info.ValueRef) {
239+
if (const ParamDecl *DeclParam = getParameterAt(Info.ValueRef, Idx)) {
240+
Optional |= DeclParam->isDefaultArgument();
241+
Optional |= DeclParam->getType()->is<PackExpansionType>();
242+
}
243+
}
244+
const AnyFunctionType::Param *TypeParam = &ParamsToPass[Idx];
245+
Optional |= TypeParam->isVariadic();
246+
DeclParamIsOptional.push_back(Optional);
247+
}
248+
}
249+
234250
Results.push_back({ExpectedTy, ExpectedCallType,
235-
isa<SubscriptExpr>(ParentCall), Info.Value, FuncTy, ArgIdx,
236-
ParamIdx, std::move(ClaimedParams), IsNoninitialVariadic,
237-
Info.BaseTy, HasLabel, IsAsync, SolutionSpecificVarTypes});
251+
isa<SubscriptExpr>(ParentCall), Info.getValue(), FuncTy,
252+
ArgIdx, ParamIdx, std::move(ClaimedParams),
253+
IsNoninitialVariadic, Info.BaseTy, HasLabel, IsAsync,
254+
DeclParamIsOptional, SolutionSpecificVarTypes});
238255
}
239256

240257
void ArgumentTypeCheckCompletionCallback::computeShadowedDecls(
@@ -243,7 +260,8 @@ void ArgumentTypeCheckCompletionCallback::computeShadowedDecls(
243260
auto &ResultA = Results[i];
244261
for (size_t j = i + 1; j < Results.size(); ++j) {
245262
auto &ResultB = Results[j];
246-
if (!ResultA.FuncD || !ResultB.FuncD || !ResultA.FuncTy || !ResultB.FuncTy) {
263+
if (!ResultA.FuncD || !ResultB.FuncD || !ResultA.FuncTy ||
264+
!ResultB.FuncTy) {
247265
continue;
248266
}
249267
if (ResultA.FuncD->getName() != ResultB.FuncD->getName()) {

lib/IDE/CursorInfo.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ class CursorInfoTypeCheckSolutionCallback : public TypeCheckCompletionCallback {
311311
auto Locator = CS.getConstraintLocator(ResolveExpr);
312312
auto CalleeLocator = S.getCalleeLocator(Locator);
313313
auto OverloadInfo = getSelectedOverloadInfo(S, CalleeLocator);
314-
if (!OverloadInfo.Value) {
314+
if (!OverloadInfo.ValueRef) {
315315
// We could not resolve the referenced declaration. Skip the solution.
316316
return;
317317
}
@@ -322,11 +322,12 @@ class CursorInfoTypeCheckSolutionCallback : public TypeCheckCompletionCallback {
322322
if (auto BaseExpr =
323323
simplifyLocatorToAnchor(BaseLocator).dyn_cast<Expr *>()) {
324324
IsDynamicRef =
325-
ide::isDynamicRef(BaseExpr, OverloadInfo.Value,
325+
ide::isDynamicRef(BaseExpr, OverloadInfo.getValue(),
326326
[&S](Expr *E) { return S.getResolvedType(E); });
327327
}
328328

329-
Results.push_back({OverloadInfo.BaseTy, IsDynamicRef, OverloadInfo.Value});
329+
Results.push_back(
330+
{OverloadInfo.BaseTy, IsDynamicRef, OverloadInfo.getValue()});
330331
}
331332

332333
public:

lib/IDE/SelectedOverloadInfo.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ swift::ide::getSelectedOverloadInfo(const Solution &S,
4040
Result.BaseTy = nullptr;
4141
}
4242

43-
Result.Value = SelectedOverload->choice.getDeclOrNull();
43+
if (auto ReferencedDecl = SelectedOverload->choice.getDeclOrNull()) {
44+
Result.ValueRef = S.resolveConcreteDeclRef(ReferencedDecl, CalleeLocator);
45+
}
4446
Result.ValueTy =
4547
S.simplifyTypeForCodeCompletion(SelectedOverload->adjustedOpenedType);
4648

test/IDE/complete_call_arg.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,3 +1303,10 @@ func testMacroArg() {
13031303
// MACRO_CALL_ARG-DAG: Literal[Boolean]/None: true[#Bool#]; name=true
13041304
// MACRO_CALL_ARG: End completions
13051305
}
1306+
1307+
func testParameterPack(intArray: [Int]) {
1308+
func myZip<each S>(_ sequence: repeat each S, otherParam: Int) where repeat each S: Sequence {}
1309+
myZip([1], #^PARAMETER_PACK_ARG^#)
1310+
// PARAMETER_PACK_ARG: Pattern/Local/Flair[ArgLabels]: {#otherParam: Int#}[#Int#]; name=otherParam:
1311+
// PARAMETER_PACK_ARG: Decl[LocalVar]/Local/TypeRelation[Convertible]: intArray[#[Int]#]; name=intArray
1312+
}

0 commit comments

Comments
 (0)