Skip to content

[CodeCompletion][Sema] Multiple improvements to prepare for migration of PostfixExprParen to solver-based #42006

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
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
5 changes: 5 additions & 0 deletions include/swift/IDE/ArgumentCompletion.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ class ArgumentTypeCheckCompletionCallback : public TypeCheckCompletionCallback {
/// Whether the surrounding context is async and thus calling async
/// functions is supported.
bool IsInAsyncContext;

/// 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;
};

CodeCompletionExpr *CompletionExpr;
Expand Down
7 changes: 7 additions & 0 deletions include/swift/IDE/TypeCheckCompletionCallback.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ Type getTypeForCompletion(const constraints::Solution &S, ASTNode Node);
/// completion expression \p E.
Type getPatternMatchType(const constraints::Solution &S, Expr *E);

/// Populate \p Result with types of variables that were determined in the
/// solution \p S. This in particular includes parameters of closures that
/// were type-checked with the code completion expression.
void getSolutionSpecificVarTypes(
const constraints::Solution &S,
llvm::SmallDenseMap<const VarDecl *, Type> &Result);

/// Whether the given completion expression is the only expression in its
/// containing closure or function body and its value is implicitly returned.
///
Expand Down
18 changes: 18 additions & 0 deletions include/swift/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -1288,6 +1288,24 @@ class Solution {
/// type variables for their fixed types.
Type simplifyType(Type type) const;

// To aid code completion, we need to attempt to convert type placeholders
// back into underlying generic parameters if possible, since type
// of the code completion expression is used as "expected" (or contextual)
// type so it's helpful to know what requirements it has to filter
// the list of possible member candidates e.g.
//
// \code
// func test<T: P>(_: [T]) {}
//
// test(42.#^MEMBERS^#)
// \code
//
// It's impossible to resolve `T` in this case but code completion
// expression should still have a type of `[T]` instead of `[<<hole>>]`
// because it helps to produce correct contextual member list based on
// a conformance requirement associated with generic parameter `T`.
Type simplifyTypeForCodeCompletion(Type type) const;

/// Coerce the given expression to the given type.
///
/// This operation cannot fail.
Expand Down
21 changes: 12 additions & 9 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3825,17 +3825,20 @@ namespace {
void printAnyFunctionTypeCommon(AnyFunctionType *T, StringRef label,
StringRef name) {
printCommon(label, name);
SILFunctionType::Representation representation =
T->getExtInfo().getSILRepresentation();

if (representation != SILFunctionType::Representation::Thick)
printField("representation",
getSILFunctionTypeRepresentationString(representation));
if (T->hasExtInfo()) {
SILFunctionType::Representation representation =
T->getExtInfo().getSILRepresentation();

printFlag(!T->isNoEscape(), "escaping");
printFlag(T->isSendable(), "Sendable");
printFlag(T->isAsync(), "async");
printFlag(T->isThrowing(), "throws");
if (representation != SILFunctionType::Representation::Thick) {
printField("representation",
getSILFunctionTypeRepresentationString(representation));
}
printFlag(!T->isNoEscape(), "escaping");
printFlag(T->isSendable(), "Sendable");
printFlag(T->isAsync(), "async");
printFlag(T->isThrowing(), "throws");
}

if (Type globalActor = T->getGlobalActor()) {
printField("global_actor", globalActor.getString());
Expand Down
96 changes: 59 additions & 37 deletions lib/IDE/ArgumentCompletion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,34 +104,39 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {

auto *CallLocator = CS.getConstraintLocator(ParentCall);
auto *CalleeLocator = S.getCalleeLocator(CallLocator);
auto SelectedOverload = S.getOverloadChoiceIfAvailable(CalleeLocator);
if (!SelectedOverload) {
return;
}

Type CallBaseTy = SelectedOverload->choice.getBaseType();
if (CallBaseTy) {
CallBaseTy = S.simplifyType(CallBaseTy)->getRValueType();
}
ValueDecl *FuncD = nullptr;
Type FuncTy;
Type CallBaseTy;
// If we are calling a closure in-place there is no overload choice, but we
// still have all the other required information (like the argument's
// expected type) to provide useful code completion results.
if (auto SelectedOverload = S.getOverloadChoiceIfAvailable(CalleeLocator)) {

CallBaseTy = SelectedOverload->choice.getBaseType();
if (CallBaseTy) {
CallBaseTy = S.simplifyType(CallBaseTy)->getRValueType();
}

ValueDecl *FuncD = SelectedOverload->choice.getDeclOrNull();
Type FuncTy = S.simplifyType(SelectedOverload->openedType)->getRValueType();

// For completion as the arg in a call to the implicit [keypath: _] subscript
// the solver can't know what kind of keypath is expected without an actual
// argument (e.g. a KeyPath vs WritableKeyPath) so it ends up as a hole.
// Just assume KeyPath so we show the expected keypath's root type to users
// rather than '_'.
if (SelectedOverload->choice.getKind() ==
OverloadChoiceKind::KeyPathApplication) {
auto Params = FuncTy->getAs<AnyFunctionType>()->getParams();
if (Params.size() == 1 && Params[0].getPlainType()->is<UnresolvedType>()) {
auto *KPDecl = CS.getASTContext().getKeyPathDecl();
Type KPTy =
KPDecl->mapTypeIntoContext(KPDecl->getDeclaredInterfaceType());
Type KPValueTy = KPTy->castTo<BoundGenericType>()->getGenericArgs()[1];
KPTy = BoundGenericType::get(KPDecl, Type(), {CallBaseTy, KPValueTy});
FuncTy = FunctionType::get({Params[0].withType(KPTy)}, KPValueTy);
FuncD = SelectedOverload->choice.getDeclOrNull();
FuncTy = S.simplifyTypeForCodeCompletion(SelectedOverload->openedType);

// For completion as the arg in a call to the implicit [keypath: _]
// subscript the solver can't know what kind of keypath is expected without
// an actual argument (e.g. a KeyPath vs WritableKeyPath) so it ends up as a
// hole. Just assume KeyPath so we show the expected keypath's root type to
// users rather than '_'.
if (SelectedOverload->choice.getKind() ==
OverloadChoiceKind::KeyPathApplication) {
auto Params = FuncTy->getAs<AnyFunctionType>()->getParams();
if (Params.size() == 1 &&
Params[0].getPlainType()->is<UnresolvedType>()) {
auto *KPDecl = CS.getASTContext().getKeyPathDecl();
Type KPTy =
KPDecl->mapTypeIntoContext(KPDecl->getDeclaredInterfaceType());
Type KPValueTy = KPTy->castTo<BoundGenericType>()->getGenericArgs()[1];
KPTy = BoundGenericType::get(KPDecl, Type(), {CallBaseTy, KPValueTy});
FuncTy = FunctionType::get({Params[0].withType(KPTy)}, KPValueTy);
}
}
}

Expand Down Expand Up @@ -193,9 +198,13 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
return;
}

llvm::SmallDenseMap<const VarDecl *, Type> SolutionSpecificVarTypes;
getSolutionSpecificVarTypes(S, SolutionSpecificVarTypes);

Results.push_back({ExpectedTy, isa<SubscriptExpr>(ParentCall), FuncD, FuncTy,
ArgIdx, ParamIdx, std::move(ClaimedParams),
IsNoninitialVariadic, CallBaseTy, HasLabel, IsAsync});
IsNoninitialVariadic, CallBaseTy, HasLabel, IsAsync,
SolutionSpecificVarTypes});
}

void ArgumentTypeCheckCompletionCallback::deliverResults(
Expand Down Expand Up @@ -231,15 +240,27 @@ void ArgumentTypeCheckCompletionCallback::deliverResults(
SemanticContext = SemanticContextKind::CurrentNominal;
}
}
if (Result.IsSubscript) {
assert(SemanticContext != SemanticContextKind::None);
auto *SD = dyn_cast_or_null<SubscriptDecl>(Result.FuncD);
Lookup.addSubscriptCallPattern(Result.FuncTy->getAs<AnyFunctionType>(),
SD, SemanticContext);
} else {
auto *FD = dyn_cast_or_null<AbstractFunctionDecl>(Result.FuncD);
Lookup.addFunctionCallPattern(Result.FuncTy->getAs<AnyFunctionType>(),
FD, SemanticContext);
if (SemanticContext == SemanticContextKind::None && Result.FuncD) {
if (Result.FuncD->getDeclContext()->isTypeContext()) {
SemanticContext = SemanticContextKind::CurrentNominal;
} else if (Result.FuncD->getDeclContext()->isLocalContext()) {
SemanticContext = SemanticContextKind::Local;
} else if (Result.FuncD->getModuleContext() == DC->getParentModule()) {
SemanticContext = SemanticContextKind::CurrentModule;
}
}
if (Result.FuncTy) {
if (auto FuncTy = Result.FuncTy->lookThroughAllOptionalTypes()
->getAs<AnyFunctionType>()) {
if (Result.IsSubscript) {
assert(SemanticContext != SemanticContextKind::None);
auto *SD = dyn_cast_or_null<SubscriptDecl>(Result.FuncD);
Lookup.addSubscriptCallPattern(FuncTy, SD, SemanticContext);
} else {
auto *FD = dyn_cast_or_null<AbstractFunctionDecl>(Result.FuncD);
Lookup.addFunctionCallPattern(FuncTy, FD, SemanticContext);
}
}
}
}
Lookup.setHaveLParen(false);
Expand All @@ -258,6 +279,7 @@ void ArgumentTypeCheckCompletionCallback::deliverResults(
if (shouldPerformGlobalCompletion) {
for (auto &Result : Results) {
ExpectedTypes.push_back(Result.ExpectedType);
Lookup.setSolutionSpecificVarTypes(Result.SolutionSpecificVarTypes);
}
Lookup.setExpectedTypes(ExpectedTypes, false);
bool IsInAsyncContext = llvm::any_of(
Expand Down
8 changes: 5 additions & 3 deletions lib/IDE/CompletionLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -977,13 +977,14 @@ void CompletionLookup::addEffectsSpecifiers(
assert(AFT != nullptr);

// 'async'.
if (forceAsync || (AFD && AFD->hasAsync()) || AFT->isAsync())
if (forceAsync || (AFD && AFD->hasAsync()) ||
(AFT->hasExtInfo() && AFT->isAsync()))
Builder.addAnnotatedAsync();

// 'throws' or 'rethrows'.
if (AFD && AFD->getAttrs().hasAttribute<RethrowsAttr>())
Builder.addAnnotatedRethrows();
else if (AFT->isThrowing())
else if (AFT->hasExtInfo() && AFT->isThrowing())
Builder.addAnnotatedThrows();
}

Expand Down Expand Up @@ -1146,7 +1147,8 @@ void CompletionLookup::addFunctionCallPattern(
else
addTypeAnnotation(Builder, AFT->getResult(), genericSig);

if (!isForCaching() && AFT->isAsync() && !CanCurrDeclContextHandleAsync) {
if (!isForCaching() && AFT->hasExtInfo() && AFT->isAsync() &&
!CanCurrDeclContextHandleAsync) {
Builder.setContextualNotRecommended(
ContextualNotRecommendedReason::InvalidAsyncContext);
}
Expand Down
6 changes: 1 addition & 5 deletions lib/IDE/ExprCompletion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,7 @@ void ExprTypeCheckCompletionCallback::sawSolutionImpl(
bool IsAsync = isContextAsync(S, DC);

llvm::SmallDenseMap<const VarDecl *, Type> SolutionSpecificVarTypes;
for (auto NT : S.nodeTypes) {
if (auto VD = dyn_cast_or_null<VarDecl>(NT.first.dyn_cast<Decl *>())) {
SolutionSpecificVarTypes[VD] = S.simplifyType(NT.second);
}
}
getSolutionSpecificVarTypes(S, SolutionSpecificVarTypes);

addResult(ImplicitReturn, IsAsync, SolutionSpecificVarTypes);
addExpectedType(ExpectedTy);
Expand Down
52 changes: 12 additions & 40 deletions lib/IDE/TypeCheckCompletionCallback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,47 +51,8 @@ Type swift::ide::getTypeForCompletion(const constraints::Solution &S,

Type Result;

// To aid code completion, we need to attempt to convert type placeholders
// back into underlying generic parameters if possible, since type
// of the code completion expression is used as "expected" (or contextual)
// type so it's helpful to know what requirements it has to filter
// the list of possible member candidates e.g.
//
// \code
// func test<T: P>(_: [T]) {}
//
// test(42.#^MEMBERS^#)
// \code
//
// It's impossible to resolve `T` in this case but code completion
// expression should still have a type of `[T]` instead of `[<<hole>>]`
// because it helps to produce correct contextual member list based on
// a conformance requirement associated with generic parameter `T`.
if (isExpr<CodeCompletionExpr>(Node)) {
auto completionTy = S.getType(Node).transform([&](Type type) -> Type {
if (auto *typeVar = type->getAs<TypeVariableType>())
return S.getFixedType(typeVar);
return type;
});

Result = S.simplifyType(completionTy.transform([&](Type type) {
if (auto *placeholder = type->getAs<PlaceholderType>()) {
if (auto *typeVar =
placeholder->getOriginator().dyn_cast<TypeVariableType *>()) {
if (auto *GP = typeVar->getImpl().getGenericParameter()) {
// Code completion depends on generic parameter type being
// represented in terms of `ArchetypeType` since it's easy
// to extract protocol requirements from it.
if (auto *GPD = GP->getDecl())
return GPD->getInnermostDeclContext()->mapTypeIntoContext(GP);
}
}

return Type(CS.getASTContext().TheUnresolvedType);
}

return type;
}));
Result = S.simplifyTypeForCodeCompletion(S.getType(Node));
} else {
Result = S.getResolvedType(Node);
}
Expand Down Expand Up @@ -164,6 +125,17 @@ Type swift::ide::getPatternMatchType(const constraints::Solution &S, Expr *E) {
return nullptr;
}

void swift::ide::getSolutionSpecificVarTypes(
const constraints::Solution &S,
llvm::SmallDenseMap<const VarDecl *, Type> &Result) {
assert(Result.empty());
for (auto NT : S.nodeTypes) {
if (auto VD = dyn_cast_or_null<VarDecl>(NT.first.dyn_cast<Decl *>())) {
Result[VD] = S.simplifyType(NT.second);
}
}
}

bool swift::ide::isImplicitSingleExpressionReturn(ConstraintSystem &CS,
Expr *CompletionExpr) {
Expr *ParentExpr = CS.getParentExpr(CompletionExpr);
Expand Down
8 changes: 6 additions & 2 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1275,6 +1275,9 @@ class CompletionArgumentTracker : public ArgumentFailureTracker {
if (ArgInfo.isBefore(argIdx)) {
return false;
}
if (argIdx == 0 && ArgInfo.completionIdx == 0) {
return false;
}
return ArgumentFailureTracker::extraArgument(argIdx);
}

Expand Down Expand Up @@ -1812,7 +1815,6 @@ static ConstraintSystem::TypeMatchResult matchCallArguments(
auto *argExpr = getArgumentExpr(locator.getAnchor(), argIdx);
if (param.isAutoClosure() && !isSynthesizedArgument(argument)) {
auto &ctx = cs.getASTContext();
auto *fnType = paramTy->castTo<FunctionType>();

// If this is a call to a function with a closure argument and the
// parameter is an autoclosure, let's just increment the score here
Expand All @@ -1832,7 +1834,9 @@ static ConstraintSystem::TypeMatchResult matchCallArguments(
if (ctx.isSwiftVersionAtLeast(5) || !isAutoClosureArgument(argExpr)) {
// In Swift >= 5 mode there is no @autoclosure forwarding,
// so let's match result types.
paramTy = fnType->getResult();
if (auto *fnType = paramTy->getAs<FunctionType>()) {
paramTy = fnType->getResult();
}
} else {
// Matching @autoclosure argument to @autoclosure parameter
// directly would mean introducting a function conversion
Expand Down
42 changes: 42 additions & 0 deletions lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3496,6 +3496,48 @@ Type Solution::simplifyType(Type type) const {
return resolvedType;
}

Type Solution::simplifyTypeForCodeCompletion(Type Ty) const {
auto &CS = getConstraintSystem();

// First, instantiate all type variables that we know, but don't replace
// placeholders by unresolved types.
Ty = CS.simplifyTypeImpl(Ty, [this](TypeVariableType *typeVar) -> Type {
return getFixedType(typeVar);
});

// Next, replace all placeholders by type variables. We know that all type
// variables now in the type originate from placeholders.
Ty = Ty.transform([](Type type) -> Type {
if (auto *placeholder = type->getAs<PlaceholderType>()) {
if (auto *typeVar =
placeholder->getOriginator().dyn_cast<TypeVariableType *>()) {
return typeVar;
}
}

return type;
});

// Replace all type variables (which must come from placeholders) by their
// generic parameters. Because we call into simplifyTypeImpl
Ty = CS.simplifyTypeImpl(Ty, [](TypeVariableType *typeVar) -> Type {
if (auto *GP = typeVar->getImpl().getGenericParameter()) {
// Code completion depends on generic parameter type being
// represented in terms of `ArchetypeType` since it's easy
// to extract protocol requirements from it.
if (auto *GPD = GP->getDecl()) {
return GPD->getInnermostDeclContext()->mapTypeIntoContext(GP);
}
}
return typeVar;
});

// Remove any remaining type variables and placeholders
Ty = simplifyType(Ty);

return Ty->getRValueType();
}

size_t Solution::getTotalMemory() const {
return sizeof(*this) + typeBindings.getMemorySize() +
overloadChoices.getMemorySize() +
Expand Down
Loading