Skip to content

Commit 5df5711

Browse files
authored
[CodeCompletion] Rework getOperatorCompletions() (#20632)
In postfix completion, for operator completion, we do: 1. Type check the operand without applying it, but set the resolved type to the root of the expression. 2. For each possible operators: i. Build temporary binary/postfix expression ii. Perform type checking to see whether the operator is applicable This could be very slow especially if the operand is complex. * Introduce `ReusePrecheckedType` option to constraint system. With this option, CSGen respects pre-stored types in expressions and doesn't take its sub-expressions into account. * Improve type checking performance because type variables aren't generated for sub-expressions of LHS (45511835) * Guarantee that the operand is not modified by the type checker because expression walkers in `CSGen` doesn't walk into the operand. * Introduce `TypeChecker::findLHS()` to find LHS for a infix operator from pre-folded expression. We used to `foldSequence()` temporary `SequenceExpr` and find 'CodeCompletionExpr' for each attempt. * No need to flatten folded expression after initial type-checking. * Save memory of temporary `BinaryExpr` which used to be allocated by `foldSequence()`. * Improve accuracy of the completion. `foldSequence()` recovers invalid combination of operators by `left` associative manner (with diagnostics). This used to cause false-positive results. For instance, `a == b <HERE>` used to suggest `==` operator. `findLHS()` returns `nullptr` for such invalid combination. rdar://problem/45511835 https://bugs.swift.org/browse/SR-9061
1 parent fb52a2e commit 5df5711

File tree

11 files changed

+309
-255
lines changed

11 files changed

+309
-255
lines changed

include/swift/Sema/IDETypeChecking.h

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -98,19 +98,16 @@ namespace swift {
9898
Expr *&parsedExpr,
9999
ConcreteDeclRef &referencedDecl);
100100

101-
/// Typecheck the sequence expression \p parsedExpr for code completion.
101+
/// Resolve type of operator function with \c opName appending it to \c LHS.
102102
///
103-
/// This requires that \p parsedExpr is a SequenceExpr and that it contains:
104-
/// * ... leading sequence LHS
105-
/// * UnresolvedDeclRefExpr operator
106-
/// * CodeCompletionExpr RHS
107-
///
108-
/// On success, returns false, and replaces parsedExpr with the binary
109-
/// expression corresponding to the operator. The type of the operator and
110-
/// RHS are also set, but the rest of the expression may not be typed
111-
///
112-
/// The LHS should already be type-checked or this will be very slow.
113-
bool typeCheckCompletionSequence(DeclContext *DC, Expr *&parsedExpr);
103+
/// For \p refKind, use \c DeclRefKind::PostfixOperator for postfix operator,
104+
/// or \c DeclRefKind::BinaryOperator for infix operator.
105+
/// On success, returns resolved function type of the operator. The LHS should
106+
/// already be type-checked. This function guarantees LHS not to be modified.
107+
FunctionType *getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS,
108+
Identifier opName,
109+
DeclRefKind refKind,
110+
ConcreteDeclRef &referencedDecl);
114111

115112
/// Typecheck the given expression.
116113
bool typeCheckExpression(DeclContext *DC, Expr *&parsedExpr);

lib/IDE/CodeCompletion.cpp

Lines changed: 45 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -3291,35 +3291,15 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
32913291
}
32923292

32933293
void tryPostfixOperator(Expr *expr, PostfixOperatorDecl *op) {
3294-
auto Ty = expr->getType();
3295-
if (!Ty)
3294+
ConcreteDeclRef referencedDecl;
3295+
FunctionType *funcTy = getTypeOfCompletionOperator(
3296+
const_cast<DeclContext *>(CurrDeclContext), expr, op->getName(),
3297+
DeclRefKind::PostfixOperator, referencedDecl);
3298+
if (!funcTy)
32963299
return;
32973300

3298-
SWIFT_DEFER {
3299-
// Restore type.
3300-
// FIXME: This is workaround for getTypeOfExpressionWithoutApplying()
3301-
// modifies type of 'expr'.
3302-
expr->setType(Ty);
3303-
prepareForRetypechecking(expr);
3304-
};
3305-
3306-
// We allocate these expressions on the stack because we know they can't
3307-
// escape and there isn't a better way to allocate scratch Expr nodes.
3308-
UnresolvedDeclRefExpr UDRE(op->getName(), DeclRefKind::PostfixOperator,
3309-
DeclNameLoc(expr->getSourceRange().End));
3310-
ParenExpr parenExpr(expr->getSourceRange().Start, expr,
3311-
expr->getSourceRange().End,
3312-
/*hasTrailingClosure=*/false);
3313-
PostfixUnaryExpr opExpr(&UDRE, &parenExpr);
3314-
Expr *tempExpr = &opExpr;
3315-
ConcreteDeclRef referencedDecl;
3316-
if (auto T = getTypeOfCompletionContextExpr(
3317-
CurrDeclContext->getASTContext(),
3318-
const_cast<DeclContext *>(CurrDeclContext),
3319-
CompletionTypeCheckKind::Normal,
3320-
tempExpr,
3321-
referencedDecl))
3322-
addPostfixOperatorCompletion(op, *T);
3301+
// TODO: Use referencedDecl (FuncDecl) instead of 'op' (OperatorDecl).
3302+
addPostfixOperatorCompletion(op, funcTy->getResult());
33233303
}
33243304

33253305
void addAssignmentOperator(Type RHSType, Type resultType) {
@@ -3366,169 +3346,67 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
33663346
addTypeAnnotation(builder, resultType);
33673347
}
33683348

3369-
void tryInfixOperatorCompletion(InfixOperatorDecl *op, SequenceExpr *SE) {
3370-
if (op->getName().str() == "~>")
3371-
return;
3372-
3373-
MutableArrayRef<Expr *> sequence = SE->getElements();
3374-
assert(sequence.size() >= 3 && !sequence.back() &&
3375-
!sequence.drop_back(1).back() && "sequence not cleaned up");
3376-
assert((sequence.size() & 1) && "sequence expr ending with operator");
3377-
3378-
// FIXME: these checks should apply to the LHS of the operator, not the
3379-
// immediately left expression. Move under the type-checking.
3380-
Expr *LHS = sequence.drop_back(2).back();
3381-
if (LHS->getType() && (LHS->getType()->is<MetatypeType>() ||
3382-
LHS->getType()->is<AnyFunctionType>()))
3349+
void tryInfixOperatorCompletion(Expr *foldedExpr, InfixOperatorDecl *op) {
3350+
ConcreteDeclRef referencedDecl;
3351+
FunctionType *funcTy = getTypeOfCompletionOperator(
3352+
const_cast<DeclContext *>(CurrDeclContext), foldedExpr, op->getName(),
3353+
DeclRefKind::BinaryOperator, referencedDecl);
3354+
if (!funcTy)
33833355
return;
33843356

3385-
// Preserve LHS type for restoring it.
3386-
Type LHSTy = LHS->getType();
3387-
3388-
// We allocate these expressions on the stack because we know they can't
3389-
// escape and there isn't a better way to allocate scratch Expr nodes.
3390-
UnresolvedDeclRefExpr UDRE(op->getName(), DeclRefKind::BinaryOperator,
3391-
DeclNameLoc(LHS->getEndLoc()));
3392-
sequence.drop_back(1).back() = &UDRE;
3393-
CodeCompletionExpr CCE(LHS->getSourceRange());
3394-
sequence.back() = &CCE;
3395-
3396-
SWIFT_DEFER {
3397-
// Reset sequence.
3398-
SE->setElement(SE->getNumElements() - 1, nullptr);
3399-
SE->setElement(SE->getNumElements() - 2, nullptr);
3400-
LHS->setType(LHSTy);
3401-
prepareForRetypechecking(SE);
3402-
3403-
for (auto &element : sequence.drop_back(2)) {
3404-
// Unfold expressions for re-typechecking sequence.
3405-
if (auto *assignExpr = dyn_cast_or_null<AssignExpr>(element)) {
3406-
assignExpr->setSrc(nullptr);
3407-
assignExpr->setDest(nullptr);
3408-
} else if (auto *ifExpr = dyn_cast_or_null<IfExpr>(element)) {
3409-
ifExpr->setCondExpr(nullptr);
3410-
ifExpr->setElseExpr(nullptr);
3411-
}
3412-
3413-
// Reset any references to operators in types, so they are properly
3414-
// handled as operators by sequence folding.
3415-
//
3416-
// FIXME: Would be better to have some kind of 'OperatorRefExpr'?
3417-
if (auto operatorRef = element->getMemberOperatorRef()) {
3418-
operatorRef->setType(nullptr);
3419-
element = operatorRef;
3420-
}
3421-
}
3422-
};
3357+
Type lhsTy = funcTy->getParams()[0].getPlainType();
3358+
Type rhsTy = funcTy->getParams()[1].getPlainType();
3359+
Type resultTy = funcTy->getResult();
34233360

3424-
Expr *expr = SE;
3425-
if (!typeCheckCompletionSequence(const_cast<DeclContext *>(CurrDeclContext),
3426-
expr)) {
3427-
if (!LHS->getType() ||
3428-
!LHS->getType()->getRValueType()->getOptionalObjectType()) {
3429-
// Don't complete optional operators on non-optional types.
3430-
// FIXME: can we get the type-checker to disallow these for us?
3431-
if (op->getName().str() == "??")
3361+
// Don't complete optional operators on non-optional types.
3362+
if (!lhsTy->getRValueType()->getOptionalObjectType()) {
3363+
// 'T ?? T'
3364+
if (op->getName().str() == "??")
3365+
return;
3366+
// 'T == nil'
3367+
if (auto NT = rhsTy->getNominalOrBoundGenericNominal())
3368+
if (NT->getName() ==
3369+
CurrDeclContext->getASTContext().Id_OptionalNilComparisonType)
34323370
return;
3433-
if (auto NT = CCE.getType()->getNominalOrBoundGenericNominal()) {
3434-
if (NT->getName() ==
3435-
CurrDeclContext->getASTContext().Id_OptionalNilComparisonType)
3436-
return;
3437-
}
3438-
}
3371+
}
34393372

3440-
// If the right-hand side and result type are both type parameters, we're
3441-
// not providing a useful completion.
3442-
if (expr->getType()->isTypeParameter() &&
3443-
CCE.getType()->isTypeParameter())
3444-
return;
3373+
// If the right-hand side and result type are both type parameters, we're
3374+
// not providing a useful completion.
3375+
if (resultTy->isTypeParameter() && rhsTy->isTypeParameter())
3376+
return;
34453377

3446-
addInfixOperatorCompletion(op, expr->getType(), CCE.getType());
3447-
}
3448-
}
3449-
3450-
void flattenBinaryExpr(Expr *expr, SmallVectorImpl<Expr *> &sequence) {
3451-
if (auto binExpr = dyn_cast<BinaryExpr>(expr)) {
3452-
flattenBinaryExpr(binExpr->getArg()->getElement(0), sequence);
3453-
sequence.push_back(binExpr->getFn());
3454-
flattenBinaryExpr(binExpr->getArg()->getElement(1), sequence);
3455-
} else if (auto assignExpr = dyn_cast<AssignExpr>(expr)) {
3456-
flattenBinaryExpr(assignExpr->getDest(), sequence);
3457-
sequence.push_back(assignExpr);
3458-
flattenBinaryExpr(assignExpr->getSrc(), sequence);
3459-
assignExpr->setDest(nullptr);
3460-
assignExpr->setSrc(nullptr);
3461-
} else if (auto ifExpr = dyn_cast<IfExpr>(expr)) {
3462-
flattenBinaryExpr(ifExpr->getCondExpr(), sequence);
3463-
sequence.push_back(ifExpr);
3464-
flattenBinaryExpr(ifExpr->getElseExpr(), sequence);
3465-
ifExpr->setCondExpr(nullptr);
3466-
ifExpr->setElseExpr(nullptr);
3467-
} else if (auto tryExpr = dyn_cast<AnyTryExpr>(expr)) {
3468-
// Strip out try expression. It doesn't affect completion.
3469-
flattenBinaryExpr(tryExpr->getSubExpr(), sequence);
3470-
} else if (auto optEval = dyn_cast<OptionalEvaluationExpr>(expr)){
3471-
// Strip out optional evaluation expression. It doesn't affect completion.
3472-
flattenBinaryExpr(optEval->getSubExpr(), sequence);
3473-
} else {
3474-
sequence.push_back(expr);
3475-
}
3378+
// TODO: Use referencedDecl (FuncDecl) instead of 'op' (OperatorDecl).
3379+
addInfixOperatorCompletion(op, funcTy->getResult(),
3380+
funcTy->getParams()[1].getPlainType());
34763381
}
34773382

3478-
void typeCheckLeadingSequence(SmallVectorImpl<Expr *> &sequence) {
3383+
Expr *typeCheckLeadingSequence(Expr *LHS, ArrayRef<Expr *> leadingSequence) {
3384+
if (leadingSequence.empty())
3385+
return LHS;
34793386

3480-
// Strip out try and optional evaluation expr because foldSequence() mutates
3481-
// hierarchy of these expressions. They don't affect completion anyway.
3482-
for (auto &element : sequence) {
3483-
if (auto *tryExpr = dyn_cast<AnyTryExpr>(element))
3484-
element = tryExpr->getSubExpr();
3485-
if (auto *optEval = dyn_cast<OptionalEvaluationExpr>(element))
3486-
element = optEval->getSubExpr();
3487-
}
3387+
assert(leadingSequence.size() % 2 == 0);
3388+
SmallVector<Expr *, 3> sequence(leadingSequence.begin(),
3389+
leadingSequence.end());
3390+
sequence.push_back(LHS);
34883391

34893392
Expr *expr =
34903393
SequenceExpr::create(CurrDeclContext->getASTContext(), sequence);
34913394
prepareForRetypechecking(expr);
3492-
// Take advantage of the fact the type-checker leaves the types on the AST.
34933395
if (!typeCheckExpression(const_cast<DeclContext *>(CurrDeclContext),
34943396
expr)) {
3495-
// Rebuild the sequence from the type-checked version.
3496-
sequence.clear();
3497-
flattenBinaryExpr(expr, sequence);
3498-
return;
3397+
return expr;
34993398
}
3500-
3501-
// Fall back to just using the immediate LHS.
3502-
auto LHS = sequence.back();
3503-
sequence.clear();
3504-
sequence.push_back(LHS);
3399+
return LHS;
35053400
}
35063401

35073402
void getOperatorCompletions(Expr *LHS, ArrayRef<Expr *> leadingSequence) {
3508-
std::vector<OperatorDecl *> operators = collectOperators();
3403+
Expr *foldedExpr = typeCheckLeadingSequence(LHS, leadingSequence);
35093404

3405+
std::vector<OperatorDecl *> operators = collectOperators();
35103406
// FIXME: this always chooses the first operator with the given name.
35113407
llvm::DenseSet<Identifier> seenPostfixOperators;
35123408
llvm::DenseSet<Identifier> seenInfixOperators;
35133409

3514-
SmallVector<Expr *, 3> sequence(leadingSequence.begin(),
3515-
leadingSequence.end());
3516-
sequence.push_back(LHS);
3517-
assert((sequence.size() & 1) && "sequence expr ending with operator");
3518-
3519-
if (sequence.size() > 1)
3520-
typeCheckLeadingSequence(sequence);
3521-
3522-
// Retrieve typechecked LHS.
3523-
LHS = sequence.back();
3524-
3525-
// Create a single sequence expression, which we will modify for each
3526-
// operator, filling in the operator and dummy right-hand side.
3527-
sequence.push_back(nullptr); // operator
3528-
sequence.push_back(nullptr); // RHS
3529-
auto *SE = SequenceExpr::create(CurrDeclContext->getASTContext(), sequence);
3530-
prepareForRetypechecking(SE);
3531-
35323410
for (auto op : operators) {
35333411
switch (op->getKind()) {
35343412
case DeclKind::PrefixOperator:
@@ -3541,7 +3419,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
35413419
break;
35423420
case DeclKind::InfixOperator:
35433421
if (seenInfixOperators.insert(op->getName()).second)
3544-
tryInfixOperatorCompletion(cast<InfixOperatorDecl>(op), SE);
3422+
tryInfixOperatorCompletion(foldedExpr, cast<InfixOperatorDecl>(op));
35453423
break;
35463424
default:
35473425
llvm_unreachable("unexpected operator kind");

lib/Sema/CSGen.cpp

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,14 +127,20 @@ namespace {
127127
class LinkedExprCollector : public ASTWalker {
128128

129129
llvm::SmallVectorImpl<Expr*> &LinkedExprs;
130+
ConstraintSystem &CS;
130131

131132
public:
132-
133-
LinkedExprCollector(llvm::SmallVectorImpl<Expr*> &linkedExprs) :
134-
LinkedExprs(linkedExprs) {}
135-
133+
LinkedExprCollector(llvm::SmallVectorImpl<Expr *> &linkedExprs,
134+
ConstraintSystem &cs)
135+
: LinkedExprs(linkedExprs), CS(cs) {}
136+
136137
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
137-
138+
139+
if (CS.shouldReusePrecheckedType() &&
140+
!CS.getType(expr)->hasTypeVariable()) {
141+
return { false, expr };
142+
}
143+
138144
// Store top-level binary exprs for further analysis.
139145
if (isa<BinaryExpr>(expr) ||
140146

@@ -195,6 +201,11 @@ namespace {
195201
LTI(lti), CS(cs) {}
196202

197203
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
204+
205+
if (CS.shouldReusePrecheckedType() &&
206+
!CS.getType(expr)->hasTypeVariable()) {
207+
return { false, expr };
208+
}
198209

199210
if (isa<IntegerLiteralExpr>(expr)) {
200211
LTI.haveIntLiteral = true;
@@ -934,6 +945,11 @@ namespace {
934945
CS(cs) {}
935946

936947
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
948+
949+
if (CS.shouldReusePrecheckedType() &&
950+
!CS.getType(expr)->hasTypeVariable()) {
951+
return { false, expr };
952+
}
937953

938954
if (auto applyExpr = dyn_cast<ApplyExpr>(expr)) {
939955
if (isa<PrefixUnaryExpr>(applyExpr) ||
@@ -3240,6 +3256,12 @@ namespace {
32403256

32413257
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
32423258
while (true) {
3259+
3260+
// If we should reuse pre-checked types, don't sanitize the expression
3261+
// if it's already type-checked.
3262+
if (CS.shouldReusePrecheckedType() && expr->getType())
3263+
return { false, expr };
3264+
32433265
// OpenExistentialExpr contains OpaqueValueExpr in its sub expression.
32443266
if (auto OOE = dyn_cast<OpenExistentialExpr>(expr)) {
32453267
auto archetypeVal = OOE->getOpaqueValue();
@@ -3409,6 +3431,15 @@ namespace {
34093431
ConstraintWalker(ConstraintGenerator &CG) : CG(CG) { }
34103432

34113433
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
3434+
3435+
if (CG.getConstraintSystem().shouldReusePrecheckedType()) {
3436+
if (expr->getType()) {
3437+
assert(!expr->getType()->hasTypeVariable());
3438+
CG.getConstraintSystem().cacheType(expr);
3439+
return { false, expr };
3440+
}
3441+
}
3442+
34123443
// Note that the subexpression of a #selector expression is
34133444
// unevaluated.
34143445
if (auto sel = dyn_cast<ObjCSelectorExpr>(expr)) {
@@ -3629,7 +3660,7 @@ void ConstraintSystem::optimizeConstraints(Expr *e) {
36293660
SmallVector<Expr *, 16> linkedExprs;
36303661

36313662
// Collect any linked expressions.
3632-
LinkedExprCollector collector(linkedExprs);
3663+
LinkedExprCollector collector(linkedExprs, *this);
36333664
e->walk(collector);
36343665

36353666
// Favor types, as appropriate.

0 commit comments

Comments
 (0)