Skip to content

[CodeCompletion] Fix ASAN failure when completing in parameter packs #65837

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions include/swift/IDE/ArgumentCompletion.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ class ArgumentTypeCheckCompletionCallback : public TypeCheckCompletionCallback {
Type ExpectedCallType;
/// True if this is a subscript rather than a function call.
bool IsSubscript;
/// The reference to the FuncDecl or SubscriptDecl associated with the call.
ConcreteDeclRef FuncDeclRef;
/// The FuncDecl or SubscriptDecl associated with the call.
ValueDecl *FuncD;
/// The type of the function being called.
AnyFunctionType *FuncTy;
/// The index of the argument containing the completion location
Expand All @@ -50,14 +50,18 @@ class ArgumentTypeCheckCompletionCallback : public TypeCheckCompletionCallback {
/// Whether the surrounding context is async and thus calling async
/// functions is supported.
bool IsInAsyncContext;
/// A bitfield to mark whether the parameter at a given index is optional.
/// Parameters can be optional if they have a default argument or belong to
/// a parameter pack.
/// Indices are based on the parameters in \c FuncTy. Note that the number
/// of parameters in \c FuncTy and \c FuncD is different when a parameter
/// pack has been exploded.
llvm::BitVector DeclParamIsOptional;

/// Types of variables that were determined in the solution that produced
/// this result. This in particular includes parameters of closures that
/// were type-checked with the code completion expression.
llvm::SmallDenseMap<const VarDecl *, Type> SolutionSpecificVarTypes;

/// Return the \c FuncDecl or \c SubscriptDecl associated with this call.
ValueDecl *getFuncD() const { return FuncDeclRef.getDecl(); }
};

CodeCompletionExpr *CompletionExpr;
Expand Down
80 changes: 42 additions & 38 deletions lib/IDE/ArgumentCompletion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,25 +47,8 @@ bool ArgumentTypeCheckCompletionCallback::addPossibleParams(
break;
}

// We work with the parameter from the function type and the declaration
// because they contain different information that we need.
//
// Since not all function types are backed by declarations (e.g. closure
// paramters), `DeclParam` might be `nullptr`.
const AnyFunctionType::Param *TypeParam = &ParamsToPass[Idx];
const ParamDecl *DeclParam = nullptr;
if (Res.FuncDeclRef) {
DeclParam = getParameterAt(Res.FuncDeclRef, Idx);
}

bool Required = true;
if (DeclParam && DeclParam->isDefaultArgument()) {
Required = false;
} else if (DeclParam && DeclParam->getType()->is<PackExpansionType>()) {
Required = false;
} else if (TypeParam->isVariadic()) {
Required = false;
}
bool Required = !Res.DeclParamIsOptional[Idx];

if (TypeParam->hasLabel() && !(IsCompletion && Res.IsNoninitialVariadic)) {
// Suggest parameter label if parameter has label, we are completing in it
Expand Down Expand Up @@ -225,7 +208,7 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {

// If this is a duplicate of any other result, ignore this solution.
if (llvm::any_of(Results, [&](const Result &R) {
return R.FuncDeclRef == Info.ValueRef &&
return R.FuncD == Info.getValue() &&
nullableTypesEqual(R.FuncTy, Info.ValueTy) &&
nullableTypesEqual(R.BaseType, Info.BaseTy) &&
R.ParamIdx == ParamIdx &&
Expand All @@ -241,11 +224,34 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
if (Info.ValueTy) {
FuncTy = Info.ValueTy->lookThroughAllOptionalTypes()->getAs<AnyFunctionType>();
}

// Determine which parameters are optional. We need to do this in
// `sawSolutionImpl` because it accesses the substitution map in
// `Info.ValueRef`. This substitution map might contain type variables that
// are allocated in the constraint system's arena and are freed once we reach
// `deliverResults`.
llvm::BitVector DeclParamIsOptional;
if (FuncTy) {
ArrayRef<AnyFunctionType::Param> ParamsToPass = FuncTy->getParams();
for (auto Idx : range(0, ParamsToPass.size())) {
bool Optional = false;
if (Info.ValueRef) {
if (const ParamDecl *DeclParam = getParameterAt(Info.ValueRef, Idx)) {
Optional |= DeclParam->isDefaultArgument();
Optional |= DeclParam->getType()->is<PackExpansionType>();
}
}
const AnyFunctionType::Param *TypeParam = &ParamsToPass[Idx];
Optional |= TypeParam->isVariadic();
DeclParamIsOptional.push_back(Optional);
}
}

Results.push_back({ExpectedTy, ExpectedCallType,
isa<SubscriptExpr>(ParentCall), Info.ValueRef, FuncTy,
isa<SubscriptExpr>(ParentCall), Info.getValue(), FuncTy,
ArgIdx, ParamIdx, std::move(ClaimedParams),
IsNoninitialVariadic, Info.BaseTy, HasLabel, IsAsync,
SolutionSpecificVarTypes});
DeclParamIsOptional, SolutionSpecificVarTypes});
}

void ArgumentTypeCheckCompletionCallback::computeShadowedDecls(
Expand All @@ -254,25 +260,25 @@ void ArgumentTypeCheckCompletionCallback::computeShadowedDecls(
auto &ResultA = Results[i];
for (size_t j = i + 1; j < Results.size(); ++j) {
auto &ResultB = Results[j];
if (!ResultA.getFuncD() || !ResultB.getFuncD() || !ResultA.FuncTy ||
if (!ResultA.FuncD || !ResultB.FuncD || !ResultA.FuncTy ||
!ResultB.FuncTy) {
continue;
}
if (ResultA.getFuncD()->getName() != ResultB.getFuncD()->getName()) {
if (ResultA.FuncD->getName() != ResultB.FuncD->getName()) {
continue;
}
if (!ResultA.FuncTy->isEqual(ResultB.FuncTy)) {
continue;
}
ProtocolDecl *inProtocolExtensionA =
ResultA.getFuncD()->getDeclContext()->getExtendedProtocolDecl();
ResultA.FuncD->getDeclContext()->getExtendedProtocolDecl();
ProtocolDecl *inProtocolExtensionB =
ResultB.getFuncD()->getDeclContext()->getExtendedProtocolDecl();
ResultB.FuncD->getDeclContext()->getExtendedProtocolDecl();

if (inProtocolExtensionA && !inProtocolExtensionB) {
ShadowedDecls.insert(ResultA.getFuncD());
ShadowedDecls.insert(ResultA.FuncD);
} else if (!inProtocolExtensionA && inProtocolExtensionB) {
ShadowedDecls.insert(ResultB.getFuncD());
ShadowedDecls.insert(ResultB.FuncD);
}
}
}
Expand Down Expand Up @@ -313,37 +319,35 @@ void ArgumentTypeCheckCompletionCallback::deliverResults(
}
if ((BaseNominal = BaseTy->getAnyNominal())) {
SemanticContext = SemanticContextKind::CurrentNominal;
if (Result.getFuncD() &&
Result.getFuncD()->getDeclContext()->getSelfNominalTypeDecl() !=
if (Result.FuncD &&
Result.FuncD->getDeclContext()->getSelfNominalTypeDecl() !=
BaseNominal) {
SemanticContext = SemanticContextKind::Super;
}
} else if (BaseTy->is<TupleType>() || BaseTy->is<SubstitutableType>()) {
SemanticContext = SemanticContextKind::CurrentNominal;
}
}
if (SemanticContext == SemanticContextKind::None && Result.getFuncD()) {
if (Result.getFuncD()->getDeclContext()->isTypeContext()) {
if (SemanticContext == SemanticContextKind::None && Result.FuncD) {
if (Result.FuncD->getDeclContext()->isTypeContext()) {
SemanticContext = SemanticContextKind::CurrentNominal;
} else if (Result.getFuncD()->getDeclContext()->isLocalContext()) {
} else if (Result.FuncD->getDeclContext()->isLocalContext()) {
SemanticContext = SemanticContextKind::Local;
} else if (Result.getFuncD()->getModuleContext() ==
DC->getParentModule()) {
} else if (Result.FuncD->getModuleContext() == DC->getParentModule()) {
SemanticContext = SemanticContextKind::CurrentModule;
}
}
if (Result.FuncTy) {
if (auto FuncTy = Result.FuncTy) {
if (ShadowedDecls.count(Result.getFuncD()) == 0) {
if (ShadowedDecls.count(Result.FuncD) == 0) {
// Don't show call pattern completions if the function is
// overridden.
if (Result.IsSubscript) {
assert(SemanticContext != SemanticContextKind::None);
auto *SD = dyn_cast_or_null<SubscriptDecl>(Result.getFuncD());
auto *SD = dyn_cast_or_null<SubscriptDecl>(Result.FuncD);
Lookup.addSubscriptCallPattern(FuncTy, SD, SemanticContext);
} else {
auto *FD =
dyn_cast_or_null<AbstractFunctionDecl>(Result.getFuncD());
auto *FD = dyn_cast_or_null<AbstractFunctionDecl>(Result.FuncD);
Lookup.addFunctionCallPattern(FuncTy, FD, SemanticContext);
}
}
Expand Down