Merge pull request #20699 from slavapestov/overly-lazy-witness-table-emission
IRGen: Force emission of lazy witness table when conformance descriptor is referenced
diff --git a/include/swift/Sema/IDETypeChecking.h b/include/swift/Sema/IDETypeChecking.h
index 1f2c057..c55dbce 100644
--- a/include/swift/Sema/IDETypeChecking.h
+++ b/include/swift/Sema/IDETypeChecking.h
@@ -98,19 +98,16 @@
Expr *&parsedExpr,
ConcreteDeclRef &referencedDecl);
- /// Typecheck the sequence expression \p parsedExpr for code completion.
+ /// Resolve type of operator function with \c opName appending it to \c LHS.
///
- /// This requires that \p parsedExpr is a SequenceExpr and that it contains:
- /// * ... leading sequence LHS
- /// * UnresolvedDeclRefExpr operator
- /// * CodeCompletionExpr RHS
- ///
- /// On success, returns false, and replaces parsedExpr with the binary
- /// expression corresponding to the operator. The type of the operator and
- /// RHS are also set, but the rest of the expression may not be typed
- ///
- /// The LHS should already be type-checked or this will be very slow.
- bool typeCheckCompletionSequence(DeclContext *DC, Expr *&parsedExpr);
+ /// For \p refKind, use \c DeclRefKind::PostfixOperator for postfix operator,
+ /// or \c DeclRefKind::BinaryOperator for infix operator.
+ /// On success, returns resolved function type of the operator. The LHS should
+ /// already be type-checked. This function guarantees LHS not to be modified.
+ FunctionType *getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS,
+ Identifier opName,
+ DeclRefKind refKind,
+ ConcreteDeclRef &referencedDecl);
/// Typecheck the given expression.
bool typeCheckExpression(DeclContext *DC, Expr *&parsedExpr);
diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp
index 6be2f80..8be5875 100644
--- a/lib/IDE/CodeCompletion.cpp
+++ b/lib/IDE/CodeCompletion.cpp
@@ -3291,35 +3291,15 @@
}
void tryPostfixOperator(Expr *expr, PostfixOperatorDecl *op) {
- auto Ty = expr->getType();
- if (!Ty)
+ ConcreteDeclRef referencedDecl;
+ FunctionType *funcTy = getTypeOfCompletionOperator(
+ const_cast<DeclContext *>(CurrDeclContext), expr, op->getName(),
+ DeclRefKind::PostfixOperator, referencedDecl);
+ if (!funcTy)
return;
- SWIFT_DEFER {
- // Restore type.
- // FIXME: This is workaround for getTypeOfExpressionWithoutApplying()
- // modifies type of 'expr'.
- expr->setType(Ty);
- prepareForRetypechecking(expr);
- };
-
- // We allocate these expressions on the stack because we know they can't
- // escape and there isn't a better way to allocate scratch Expr nodes.
- UnresolvedDeclRefExpr UDRE(op->getName(), DeclRefKind::PostfixOperator,
- DeclNameLoc(expr->getSourceRange().End));
- ParenExpr parenExpr(expr->getSourceRange().Start, expr,
- expr->getSourceRange().End,
- /*hasTrailingClosure=*/false);
- PostfixUnaryExpr opExpr(&UDRE, &parenExpr);
- Expr *tempExpr = &opExpr;
- ConcreteDeclRef referencedDecl;
- if (auto T = getTypeOfCompletionContextExpr(
- CurrDeclContext->getASTContext(),
- const_cast<DeclContext *>(CurrDeclContext),
- CompletionTypeCheckKind::Normal,
- tempExpr,
- referencedDecl))
- addPostfixOperatorCompletion(op, *T);
+ // TODO: Use referencedDecl (FuncDecl) instead of 'op' (OperatorDecl).
+ addPostfixOperatorCompletion(op, funcTy->getResult());
}
void addAssignmentOperator(Type RHSType, Type resultType) {
@@ -3366,169 +3346,67 @@
addTypeAnnotation(builder, resultType);
}
- void tryInfixOperatorCompletion(InfixOperatorDecl *op, SequenceExpr *SE) {
- if (op->getName().str() == "~>")
+ void tryInfixOperatorCompletion(Expr *foldedExpr, InfixOperatorDecl *op) {
+ ConcreteDeclRef referencedDecl;
+ FunctionType *funcTy = getTypeOfCompletionOperator(
+ const_cast<DeclContext *>(CurrDeclContext), foldedExpr, op->getName(),
+ DeclRefKind::BinaryOperator, referencedDecl);
+ if (!funcTy)
return;
- MutableArrayRef<Expr *> sequence = SE->getElements();
- assert(sequence.size() >= 3 && !sequence.back() &&
- !sequence.drop_back(1).back() && "sequence not cleaned up");
- assert((sequence.size() & 1) && "sequence expr ending with operator");
+ Type lhsTy = funcTy->getParams()[0].getPlainType();
+ Type rhsTy = funcTy->getParams()[1].getPlainType();
+ Type resultTy = funcTy->getResult();
- // FIXME: these checks should apply to the LHS of the operator, not the
- // immediately left expression. Move under the type-checking.
- Expr *LHS = sequence.drop_back(2).back();
- if (LHS->getType() && (LHS->getType()->is<MetatypeType>() ||
- LHS->getType()->is<AnyFunctionType>()))
- return;
-
- // Preserve LHS type for restoring it.
- Type LHSTy = LHS->getType();
-
- // We allocate these expressions on the stack because we know they can't
- // escape and there isn't a better way to allocate scratch Expr nodes.
- UnresolvedDeclRefExpr UDRE(op->getName(), DeclRefKind::BinaryOperator,
- DeclNameLoc(LHS->getEndLoc()));
- sequence.drop_back(1).back() = &UDRE;
- CodeCompletionExpr CCE(LHS->getSourceRange());
- sequence.back() = &CCE;
-
- SWIFT_DEFER {
- // Reset sequence.
- SE->setElement(SE->getNumElements() - 1, nullptr);
- SE->setElement(SE->getNumElements() - 2, nullptr);
- LHS->setType(LHSTy);
- prepareForRetypechecking(SE);
-
- for (auto &element : sequence.drop_back(2)) {
- // Unfold expressions for re-typechecking sequence.
- if (auto *assignExpr = dyn_cast_or_null<AssignExpr>(element)) {
- assignExpr->setSrc(nullptr);
- assignExpr->setDest(nullptr);
- } else if (auto *ifExpr = dyn_cast_or_null<IfExpr>(element)) {
- ifExpr->setCondExpr(nullptr);
- ifExpr->setElseExpr(nullptr);
- }
-
- // Reset any references to operators in types, so they are properly
- // handled as operators by sequence folding.
- //
- // FIXME: Would be better to have some kind of 'OperatorRefExpr'?
- if (auto operatorRef = element->getMemberOperatorRef()) {
- operatorRef->setType(nullptr);
- element = operatorRef;
- }
- }
- };
-
- Expr *expr = SE;
- if (!typeCheckCompletionSequence(const_cast<DeclContext *>(CurrDeclContext),
- expr)) {
- if (!LHS->getType() ||
- !LHS->getType()->getRValueType()->getOptionalObjectType()) {
- // Don't complete optional operators on non-optional types.
- // FIXME: can we get the type-checker to disallow these for us?
- if (op->getName().str() == "??")
- return;
- if (auto NT = CCE.getType()->getNominalOrBoundGenericNominal()) {
- if (NT->getName() ==
- CurrDeclContext->getASTContext().Id_OptionalNilComparisonType)
- return;
- }
- }
-
- // If the right-hand side and result type are both type parameters, we're
- // not providing a useful completion.
- if (expr->getType()->isTypeParameter() &&
- CCE.getType()->isTypeParameter())
+ // Don't complete optional operators on non-optional types.
+ if (!lhsTy->getRValueType()->getOptionalObjectType()) {
+ // 'T ?? T'
+ if (op->getName().str() == "??")
return;
-
- addInfixOperatorCompletion(op, expr->getType(), CCE.getType());
+ // 'T == nil'
+ if (auto NT = rhsTy->getNominalOrBoundGenericNominal())
+ if (NT->getName() ==
+ CurrDeclContext->getASTContext().Id_OptionalNilComparisonType)
+ return;
}
+
+ // If the right-hand side and result type are both type parameters, we're
+ // not providing a useful completion.
+ if (resultTy->isTypeParameter() && rhsTy->isTypeParameter())
+ return;
+
+ // TODO: Use referencedDecl (FuncDecl) instead of 'op' (OperatorDecl).
+ addInfixOperatorCompletion(op, funcTy->getResult(),
+ funcTy->getParams()[1].getPlainType());
}
- void flattenBinaryExpr(Expr *expr, SmallVectorImpl<Expr *> &sequence) {
- if (auto binExpr = dyn_cast<BinaryExpr>(expr)) {
- flattenBinaryExpr(binExpr->getArg()->getElement(0), sequence);
- sequence.push_back(binExpr->getFn());
- flattenBinaryExpr(binExpr->getArg()->getElement(1), sequence);
- } else if (auto assignExpr = dyn_cast<AssignExpr>(expr)) {
- flattenBinaryExpr(assignExpr->getDest(), sequence);
- sequence.push_back(assignExpr);
- flattenBinaryExpr(assignExpr->getSrc(), sequence);
- assignExpr->setDest(nullptr);
- assignExpr->setSrc(nullptr);
- } else if (auto ifExpr = dyn_cast<IfExpr>(expr)) {
- flattenBinaryExpr(ifExpr->getCondExpr(), sequence);
- sequence.push_back(ifExpr);
- flattenBinaryExpr(ifExpr->getElseExpr(), sequence);
- ifExpr->setCondExpr(nullptr);
- ifExpr->setElseExpr(nullptr);
- } else if (auto tryExpr = dyn_cast<AnyTryExpr>(expr)) {
- // Strip out try expression. It doesn't affect completion.
- flattenBinaryExpr(tryExpr->getSubExpr(), sequence);
- } else if (auto optEval = dyn_cast<OptionalEvaluationExpr>(expr)){
- // Strip out optional evaluation expression. It doesn't affect completion.
- flattenBinaryExpr(optEval->getSubExpr(), sequence);
- } else {
- sequence.push_back(expr);
- }
- }
+ Expr *typeCheckLeadingSequence(Expr *LHS, ArrayRef<Expr *> leadingSequence) {
+ if (leadingSequence.empty())
+ return LHS;
- void typeCheckLeadingSequence(SmallVectorImpl<Expr *> &sequence) {
-
- // Strip out try and optional evaluation expr because foldSequence() mutates
- // hierarchy of these expressions. They don't affect completion anyway.
- for (auto &element : sequence) {
- if (auto *tryExpr = dyn_cast<AnyTryExpr>(element))
- element = tryExpr->getSubExpr();
- if (auto *optEval = dyn_cast<OptionalEvaluationExpr>(element))
- element = optEval->getSubExpr();
- }
+ assert(leadingSequence.size() % 2 == 0);
+ SmallVector<Expr *, 3> sequence(leadingSequence.begin(),
+ leadingSequence.end());
+ sequence.push_back(LHS);
Expr *expr =
SequenceExpr::create(CurrDeclContext->getASTContext(), sequence);
prepareForRetypechecking(expr);
- // Take advantage of the fact the type-checker leaves the types on the AST.
if (!typeCheckExpression(const_cast<DeclContext *>(CurrDeclContext),
expr)) {
- // Rebuild the sequence from the type-checked version.
- sequence.clear();
- flattenBinaryExpr(expr, sequence);
- return;
+ return expr;
}
-
- // Fall back to just using the immediate LHS.
- auto LHS = sequence.back();
- sequence.clear();
- sequence.push_back(LHS);
+ return LHS;
}
void getOperatorCompletions(Expr *LHS, ArrayRef<Expr *> leadingSequence) {
- std::vector<OperatorDecl *> operators = collectOperators();
+ Expr *foldedExpr = typeCheckLeadingSequence(LHS, leadingSequence);
+ std::vector<OperatorDecl *> operators = collectOperators();
// FIXME: this always chooses the first operator with the given name.
llvm::DenseSet<Identifier> seenPostfixOperators;
llvm::DenseSet<Identifier> seenInfixOperators;
- SmallVector<Expr *, 3> sequence(leadingSequence.begin(),
- leadingSequence.end());
- sequence.push_back(LHS);
- assert((sequence.size() & 1) && "sequence expr ending with operator");
-
- if (sequence.size() > 1)
- typeCheckLeadingSequence(sequence);
-
- // Retrieve typechecked LHS.
- LHS = sequence.back();
-
- // Create a single sequence expression, which we will modify for each
- // operator, filling in the operator and dummy right-hand side.
- sequence.push_back(nullptr); // operator
- sequence.push_back(nullptr); // RHS
- auto *SE = SequenceExpr::create(CurrDeclContext->getASTContext(), sequence);
- prepareForRetypechecking(SE);
-
for (auto op : operators) {
switch (op->getKind()) {
case DeclKind::PrefixOperator:
@@ -3541,7 +3419,7 @@
break;
case DeclKind::InfixOperator:
if (seenInfixOperators.insert(op->getName()).second)
- tryInfixOperatorCompletion(cast<InfixOperatorDecl>(op), SE);
+ tryInfixOperatorCompletion(foldedExpr, cast<InfixOperatorDecl>(op));
break;
default:
llvm_unreachable("unexpected operator kind");
diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp
index 95a7a69..238cac9 100644
--- a/lib/IRGen/GenDecl.cpp
+++ b/lib/IRGen/GenDecl.cpp
@@ -1233,7 +1233,7 @@
sectionName = "swift5_replace";
break;
case llvm::Triple::COFF:
- sectionName = ".sw5repl";
+ sectionName = ".sw5repl$B";
break;
default:
llvm_unreachable("Don't know how to emit field records table for "
diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp
index 16cf4cd..8e7159d 100644
--- a/lib/Sema/CSGen.cpp
+++ b/lib/Sema/CSGen.cpp
@@ -127,14 +127,20 @@
class LinkedExprCollector : public ASTWalker {
llvm::SmallVectorImpl<Expr*> &LinkedExprs;
+ ConstraintSystem &CS;
public:
-
- LinkedExprCollector(llvm::SmallVectorImpl<Expr*> &linkedExprs) :
- LinkedExprs(linkedExprs) {}
-
+ LinkedExprCollector(llvm::SmallVectorImpl<Expr *> &linkedExprs,
+ ConstraintSystem &cs)
+ : LinkedExprs(linkedExprs), CS(cs) {}
+
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
-
+
+ if (CS.shouldReusePrecheckedType() &&
+ !CS.getType(expr)->hasTypeVariable()) {
+ return { false, expr };
+ }
+
// Store top-level binary exprs for further analysis.
if (isa<BinaryExpr>(expr) ||
@@ -195,6 +201,11 @@
LTI(lti), CS(cs) {}
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
+
+ if (CS.shouldReusePrecheckedType() &&
+ !CS.getType(expr)->hasTypeVariable()) {
+ return { false, expr };
+ }
if (isa<IntegerLiteralExpr>(expr)) {
LTI.haveIntLiteral = true;
@@ -934,6 +945,11 @@
CS(cs) {}
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
+
+ if (CS.shouldReusePrecheckedType() &&
+ !CS.getType(expr)->hasTypeVariable()) {
+ return { false, expr };
+ }
if (auto applyExpr = dyn_cast<ApplyExpr>(expr)) {
if (isa<PrefixUnaryExpr>(applyExpr) ||
@@ -3240,6 +3256,12 @@
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
while (true) {
+
+ // If we should reuse pre-checked types, don't sanitize the expression
+ // if it's already type-checked.
+ if (CS.shouldReusePrecheckedType() && expr->getType())
+ return { false, expr };
+
// OpenExistentialExpr contains OpaqueValueExpr in its sub expression.
if (auto OOE = dyn_cast<OpenExistentialExpr>(expr)) {
auto archetypeVal = OOE->getOpaqueValue();
@@ -3409,6 +3431,15 @@
ConstraintWalker(ConstraintGenerator &CG) : CG(CG) { }
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
+
+ if (CG.getConstraintSystem().shouldReusePrecheckedType()) {
+ if (expr->getType()) {
+ assert(!expr->getType()->hasTypeVariable());
+ CG.getConstraintSystem().cacheType(expr);
+ return { false, expr };
+ }
+ }
+
// Note that the subexpression of a #selector expression is
// unevaluated.
if (auto sel = dyn_cast<ObjCSelectorExpr>(expr)) {
@@ -3629,7 +3660,7 @@
SmallVector<Expr *, 16> linkedExprs;
// Collect any linked expressions.
- LinkedExprCollector collector(linkedExprs);
+ LinkedExprCollector collector(linkedExprs, *this);
e->walk(collector);
// Favor types, as appropriate.
diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h
index 93fd294..1b419a6 100644
--- a/lib/Sema/ConstraintSystem.h
+++ b/lib/Sema/ConstraintSystem.h
@@ -823,6 +823,10 @@
/// system is not applied to the expression AST, but the ConstraintSystem is
/// left in-tact.
AllowUnresolvedTypeVariables = 0x10,
+
+ /// If set, constraint system always reuses type of pre-typechecked
+ /// expression, and doesn't dig into its subexpressions.
+ ReusePrecheckedType = 0x20,
};
/// Options that affect the constraint system as a whole.
@@ -1779,17 +1783,21 @@
public:
/// \brief Whether we should attempt to fix problems.
- bool shouldAttemptFixes() {
+ bool shouldAttemptFixes() const {
if (!(Options & ConstraintSystemFlags::AllowFixes))
return false;
return !solverState || solverState->recordFixes;
}
- bool shouldSuppressDiagnostics() {
+ bool shouldSuppressDiagnostics() const {
return Options.contains(ConstraintSystemFlags::SuppressDiagnostics);
}
+ bool shouldReusePrecheckedType() const {
+ return Options.contains(ConstraintSystemFlags::ReusePrecheckedType);
+ }
+
/// \brief Log and record the application of the fix. Return true iff any
/// subsequent solution would be worse than the best known solution.
bool recordFix(ConstraintFix *fix);
diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp
index 1410e8e..4e7365a 100644
--- a/lib/Sema/TypeCheckConstraints.cpp
+++ b/lib/Sema/TypeCheckConstraints.cpp
@@ -2220,57 +2220,26 @@
}
}
-bool TypeChecker::typeCheckCompletionSequence(Expr *&expr, DeclContext *DC) {
- FrontendStatsTracer StatsTracer(Context.Stats, "typecheck-completion-seq", expr);
+static FunctionType *
+getTypeOfCompletionOperatorImpl(TypeChecker &TC, DeclContext *DC, Expr *expr,
+ ConcreteDeclRef &referencedDecl) {
+ ASTContext &Context = TC.Context;
+
+ FrontendStatsTracer StatsTracer(Context.Stats,
+ "typecheck-completion-operator", expr);
PrettyStackTraceExpr stackTrace(Context, "type-checking", expr);
+ ConstraintSystemOptions options;
+ options |= ConstraintSystemFlags::SuppressDiagnostics;
+ options |= ConstraintSystemFlags::ReusePrecheckedType;
+
// Construct a constraint system from this expression.
- ConstraintSystem CS(*this, DC, ConstraintSystemFlags::SuppressDiagnostics);
+ ConstraintSystem CS(TC, DC, options);
+ expr = CS.generateConstraints(expr);
+ if (!expr)
+ return nullptr;
- auto *SE = cast<SequenceExpr>(expr);
- assert(SE->getNumElements() >= 3);
- auto *op = SE->getElement(SE->getNumElements() - 2);
- auto *CCE = cast<CodeCompletionExpr>(SE->getElements().back());
-
- // Resolve the op.
- op = resolveDeclRefExpr(cast<UnresolvedDeclRefExpr>(op), DC);
- SE->setElement(SE->getNumElements() - 2, op);
-
- // Fold the sequence.
- expr = foldSequence(SE, DC);
-
- // Find the code-completion expression and operator again.
- Expr *exprAsBinOp = nullptr;
- while (true) {
- Expr *RHS;
- if (auto *binExpr = dyn_cast<BinaryExpr>(expr))
- RHS = binExpr->getArg()->getElement(1);
- else if (auto *assignExpr = dyn_cast<AssignExpr>(expr))
- RHS = assignExpr->getSrc();
- else if (auto *ifExpr = dyn_cast<IfExpr>(expr))
- RHS = ifExpr->getElseExpr();
- else
- break;
-
- if (RHS == CCE) {
- exprAsBinOp = expr;
- break;
- }
- expr = RHS;
- }
- if (!exprAsBinOp)
- return true;
-
- // Ensure the output expression is up to date.
- assert(exprAsBinOp == expr && isa<BinaryExpr>(expr) && "found wrong expr?");
-
- if (auto generated = CS.generateConstraints(expr)) {
- expr = generated;
- } else {
- return true;
- }
-
- if (getLangOpts().DebugConstraintSolver) {
+ if (TC.getLangOpts().DebugConstraintSolver) {
auto &log = Context.TypeCheckerDebug->getStream();
log << "---Initial constraints for the given expression---\n";
expr->dump(log);
@@ -2281,20 +2250,100 @@
// Attempt to solve the constraint system.
SmallVector<Solution, 4> viable;
if (CS.solve(expr, viable, FreeTypeVariableBinding::Disallow))
- return true;
+ return nullptr;
auto &solution = viable[0];
- if (getLangOpts().DebugConstraintSolver) {
+ if (TC.getLangOpts().DebugConstraintSolver) {
auto &log = Context.TypeCheckerDebug->getStream();
log << "---Solution---\n";
solution.dump(log);
}
- auto &solutionCS = solution.getConstraintSystem();
- expr->setType(solution.simplifyType(solutionCS.getType(expr)));
- auto completionType = solution.simplifyType(solutionCS.getType(CCE));
- CCE->setType(completionType);
- return false;
+ // Fill the results.
+ Expr *opExpr = cast<ApplyExpr>(expr)->getFn();
+ referencedDecl =
+ solution.resolveLocatorToDecl(CS.getConstraintLocator(opExpr));
+
+ // Return '(ArgType[, ArgType]) -> ResultType' as a function type.
+ // We don't use the type of the operator expression because we want the types
+ // of the *arguments* instead of the types of the parameters.
+ Expr *argsExpr = cast<ApplyExpr>(expr)->getArg();
+ SmallVector<FunctionType::Param, 2> argTypes;
+ if (auto *PE = dyn_cast<ParenExpr>(argsExpr)) {
+ argTypes.emplace_back(solution.simplifyType(CS.getType(PE->getSubExpr())));
+ } else if (auto *TE = dyn_cast<TupleExpr>(argsExpr)) {
+ for (auto arg : TE->getElements())
+ argTypes.emplace_back(solution.simplifyType(CS.getType(arg)));
+ }
+
+ return FunctionType::get(argTypes, solution.simplifyType(CS.getType(expr)));
+}
+
+/// \brief Return the type of operator function for specified LHS, or a null
+/// \c Type on error.
+FunctionType *
+TypeChecker::getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS,
+ Identifier opName, DeclRefKind refKind,
+ ConcreteDeclRef &referencedDecl) {
+
+ // For the infix operator, find the actual LHS from pre-folded LHS.
+ if (refKind == DeclRefKind::BinaryOperator)
+ LHS = findLHS(DC, LHS, opName);
+
+ if (!LHS)
+ return nullptr;
+
+ auto LHSTy = LHS->getType();
+
+ // FIXME: 'UnresolvedType' still might be typechecked by an operator.
+ if (!LHSTy || LHSTy->is<UnresolvedType>())
+ return nullptr;
+
+ // Meta types and function types cannot be a operand of operator expressions.
+ if (LHSTy->is<MetatypeType>() || LHSTy->is<AnyFunctionType>())
+ return nullptr;
+
+ auto Loc = LHS->getEndLoc();
+
+ // Build temporary expression to typecheck.
+ // We allocate these expressions on the stack because we know they can't
+ // escape and there isn't a better way to allocate scratch Expr nodes.
+ UnresolvedDeclRefExpr UDRE(opName, refKind, DeclNameLoc(Loc));
+ auto *opExpr = resolveDeclRefExpr(&UDRE, DC);
+
+ switch (refKind) {
+
+ case DeclRefKind::PostfixOperator: {
+ // (postfix_unary_expr
+ // (declref_expr name=<opName>)
+ // (paren_expr
+ // (<LHS>)))
+ ParenExpr Args(SourceLoc(), LHS, SourceLoc(),
+ /*hasTrailingClosure=*/false);
+ PostfixUnaryExpr postfixExpr(opExpr, &Args);
+ return getTypeOfCompletionOperatorImpl(*this, DC, &postfixExpr,
+ referencedDecl);
+ }
+
+ case DeclRefKind::BinaryOperator: {
+ // (binary_expr
+ // (declref_expr name=<opName>)
+ // (tuple_expr
+ // (<LHS>)
+ // (code_completion_expr)))
+ CodeCompletionExpr dummyRHS(Loc);
+ auto Args = TupleExpr::create(
+ Context, SourceLoc(), {LHS, &dummyRHS}, {}, {}, SourceLoc(),
+ /*hasTrailingClosure=*/false, /*isImplicit=*/true);
+ BinaryExpr binaryExpr(opExpr, Args, /*isImplicit=*/true);
+
+ return getTypeOfCompletionOperatorImpl(*this, DC, &binaryExpr,
+ referencedDecl);
+ }
+
+ default:
+ llvm_unreachable("Invalid DeclRefKind for operator completion");
+ }
}
bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer,
diff --git a/lib/Sema/TypeCheckExpr.cpp b/lib/Sema/TypeCheckExpr.cpp
index 15786b8..33e86e1 100644
--- a/lib/Sema/TypeCheckExpr.cpp
+++ b/lib/Sema/TypeCheckExpr.cpp
@@ -190,6 +190,15 @@
return lookupPrecedenceGroupForInfixOperator(DC, binaryExpr->getFn());
}
+ if (auto *DSCE = dyn_cast<DotSyntaxCallExpr>(E)) {
+ return lookupPrecedenceGroupForInfixOperator(DC, DSCE->getFn());
+ }
+
+ if (auto *MRE = dyn_cast<MemberRefExpr>(E)) {
+ Identifier name = MRE->getDecl().getDecl()->getBaseName().getIdentifier();
+ return lookupPrecedenceGroupForOperator(*this, DC, name, MRE->getLoc());
+ }
+
// If E is already an ErrorExpr, then we've diagnosed it as invalid already,
// otherwise emit an error.
if (!isa<ErrorExpr>(E))
@@ -198,6 +207,58 @@
return nullptr;
}
+/// Find LHS as if we append binary operator to existing pre-folded expresion.
+/// Returns found expression, or \c nullptr if the operator is not applicable.
+///
+/// For example, given '(== R (* A B))':
+/// 'findLHS(DC, expr, "+")' returns '(* A B)'.
+/// 'findLHS(DC, expr, "<<")' returns 'B'.
+/// 'findLHS(DC, expr, '==')' returns nullptr.
+Expr *TypeChecker::findLHS(DeclContext *DC, Expr *E, Identifier name) {
+ auto right = lookupPrecedenceGroupForOperator(*this, DC, name, E->getEndLoc());
+ while (true) {
+
+ // Look through implicit conversions.
+ if (auto ICE = dyn_cast<ImplicitConversionExpr>(E)) {
+ E = ICE->getSyntacticSubExpr();
+ continue;
+ }
+ if (auto ACE = dyn_cast<AutoClosureExpr>(E)) {
+ E = ACE->getSingleExpressionBody();
+ continue;
+ }
+
+ auto left = lookupPrecedenceGroupForInfixOperator(DC, E);
+ if (!left)
+ // LHS is not binary expression.
+ return E;
+ switch (Context.associateInfixOperators(left, right)) {
+ case swift::Associativity::None:
+ return nullptr;
+ case swift::Associativity::Left:
+ return E;
+ case swift::Associativity::Right:
+ break;
+ }
+ // Find the RHS of the current binary expr.
+ if (auto *assignExpr = dyn_cast<AssignExpr>(E)) {
+ E = assignExpr->getSrc();
+ } else if (auto *ifExpr = dyn_cast<IfExpr>(E)) {
+ E = ifExpr->getElseExpr();
+ } else if (auto *binaryExpr = dyn_cast<BinaryExpr>(E)) {
+ auto *Args = dyn_cast<TupleExpr>(binaryExpr->getArg());
+ if (!Args || Args->getNumElements() != 2)
+ return nullptr;
+ E = Args->getElement(1);
+ } else {
+ // E.g. 'fn() as Int << 2'.
+ // In this case '<<' has higher precedence than 'as', but the LHS should
+ // be 'fn() as Int' instead of 'Int'.
+ return E;
+ }
+ }
+}
+
// The way we compute isEndOfSequence relies on the assumption that
// the sequence-folding algorithm never recurses with a prefix of the
// entire sequence.
diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp
index 504766f..ba604a8 100644
--- a/lib/Sema/TypeChecker.cpp
+++ b/lib/Sema/TypeChecker.cpp
@@ -840,11 +840,17 @@
referencedDecl);
}
-bool swift::typeCheckCompletionSequence(DeclContext *DC, Expr *&parsedExpr) {
+/// \brief Return the type of operator function for specified LHS, or a null
+/// \c Type on error.
+FunctionType *
+swift::getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS,
+ Identifier opName, DeclRefKind refKind,
+ ConcreteDeclRef &referencedDecl) {
auto &ctx = DC->getASTContext();
DiagnosticSuppression suppression(ctx.Diags);
TypeChecker &TC = createTypeChecker(ctx);
- return TC.typeCheckCompletionSequence(parsedExpr, DC);
+ return TC.getTypeOfCompletionOperator(DC, LHS, opName, refKind,
+ referencedDecl);
}
bool swift::typeCheckExpression(DeclContext *DC, Expr *&parsedExpr) {
diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h
index ff6f076..8ee715b 100644
--- a/lib/Sema/TypeChecker.h
+++ b/lib/Sema/TypeChecker.h
@@ -1404,7 +1404,12 @@
FreeTypeVariableBinding::Disallow,
ExprTypeCheckListener *listener = nullptr);
- bool typeCheckCompletionSequence(Expr *&expr, DeclContext *DC);
+ /// \brief Return the type of operator function for specified LHS, or a null
+ /// \c Type on error.
+ FunctionType *getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS,
+ Identifier opName,
+ DeclRefKind refKind,
+ ConcreteDeclRef &referencedDecl);
/// Check the key-path expression.
///
@@ -1885,6 +1890,10 @@
PrecedenceGroupDecl *lookupPrecedenceGroup(DeclContext *dc, Identifier name,
SourceLoc nameLoc);
+ /// Given an pre-folded expression, find LHS from the expression if a binary
+ /// operator \c name appended to the expression.
+ Expr *findLHS(DeclContext *DC, Expr *E, Identifier name);
+
/// \brief Look up the Bool type in the standard library.
Type lookupBoolType(const DeclContext *dc);
diff --git a/test/IDE/complete_from_stdlib.swift b/test/IDE/complete_from_stdlib.swift
index daf976d..b7aad66 100644
--- a/test/IDE/complete_from_stdlib.swift
+++ b/test/IDE/complete_from_stdlib.swift
@@ -282,7 +282,7 @@
}
// INFIX_EXT_STRING: Begin completions
// INFIX_EXT_STRING-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: + {#String#}[#String#]
-// INFIX_EXT_STRING-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: == {#Bool#}[#Bool#]
// INFIX_EXT_STRING-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: || {#Bool#}[#Bool#]
// INFIX_EXT_STRING-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: && {#Bool#}[#Bool#]
+// INFIX_EXT_STRING-NOT: ==
// INFIX_EXT_STRING: End completions
diff --git a/test/IDE/complete_operators.swift b/test/IDE/complete_operators.swift
index 2e54a61..5b53fb8 100644
--- a/test/IDE/complete_operators.swift
+++ b/test/IDE/complete_operators.swift
@@ -145,15 +145,20 @@
// ===--- Infix operators
+precedencegroup S2PrecedenceGroup {
+ associativity: left
+ lowerThan: ComparisonPrecedence
+ higherThan: AssignmentPrecedence
+}
+precedencegroup S2AssignmentPrecedenceGroup {
+ associativity: none
+ lowerThan: ComparisonPrecedence
+ higherThan: AssignmentPrecedence
+}
+
struct S2 {}
-infix operator ** {
- associativity left
- precedence 123
-}
-infix operator **= {
- associativity none
- precedence 123
-}
+infix operator ** : S2PrecedenceGroup
+infix operator **= : S2AssignmentPrecedenceGroup
func +(x: S2, y: S2) -> S2 { return x }
func **(x: S2, y: Int) -> S2 { return x }
func **=(x: inout S2, y: Int) -> Void { return x }
@@ -364,13 +369,13 @@
x + x == x + x#^EXT_INFIX_2^#
}
// S4_EXT_INFIX: Begin completions
-// S4_EXT_INFIX-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: != {#Bool#}[#Bool#]
// S4_EXT_INFIX-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: + {#S4#}[#S4#]
-// S4_EXT_INFIX-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: == {#Bool#}[#Bool#]
// S4_EXT_INFIX-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: && {#Bool#}[#Bool#]
// S4_EXT_INFIX-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: || {#Bool#}[#Bool#]
// S4_EXT_INFIX: End completions
+// S4_EXT_INFIX-NEG-NOT: !=
+// S4_EXT_INFIX-NEG-NOT: ==
// S4_EXT_INFIX_NEG-NOT: +++
// S4_EXT_INFIX_NEG-NOT: &&&
@@ -425,4 +430,4 @@
// INFIX_AUTOCLOSURE_1-DAG: Decl[InfixOperatorFunction]/CurrModule: [' ']&&&& {#Boolish#}[#Boolish#];
// INFIX_AUTOCLOSURE_1-DAG: Decl[InfixOperatorFunction]/CurrModule: [' ']==== {#Boolish#}[#Boolish#];
// INFIX_AUTOCLOSURE_1-DAG: Decl[InfixOperatorFunction]/CurrModule: [' ']|||| {#Boolish#}[#Boolish#];
-// INFIX_AUTOCLOSURE_1: End completions
\ No newline at end of file
+// INFIX_AUTOCLOSURE_1: End completions
diff --git a/utils/symbolicate-linux-fatal b/utils/symbolicate-linux-fatal
index 8ff5ee5..15c6a81 100755
--- a/utils/symbolicate-linux-fatal
+++ b/utils/symbolicate-linux-fatal
@@ -25,8 +25,10 @@
from __future__ import print_function
import argparse
+import datetime
import os
import subprocess
+import sys
try:
import lldb
@@ -36,10 +38,17 @@
if swift_exec is not None:
site_packages = os.path.join(os.path.dirname(swift_exec),
'../lib/python2.7/site-packages/')
- import sys
sys.path.append(site_packages)
import lldb
+lldb_target = None
+known_memmap = {}
+
+
+def print_with_flush(buff):
+ print(buff)
+ sys.stdout.flush()
+
def process_ldd(lddoutput):
dyn_libs = {}
@@ -53,28 +62,18 @@
return dyn_libs
-def create_lldb_target(binary, memmap):
- lldb_debugger = lldb.SBDebugger.Create()
- lldb_target = lldb_debugger.CreateTarget(binary)
- module = lldb_target.GetModuleAtIndex(0)
+def setup_lldb_target(binary, memmap):
+ global lldb_target
+ if not lldb_target:
+ lldb_debugger = lldb.SBDebugger.Create()
+ lldb_target = lldb_debugger.CreateTarget(binary)
+ module = lldb_target.GetModuleAtIndex(0)
for dynlib_path in memmap:
module = lldb_target.AddModule(
dynlib_path, lldb.LLDB_ARCH_DEFAULT, None, None)
text_section = module.FindSection(".text")
slide = text_section.GetFileAddress() - text_section.GetFileOffset()
lldb_target.SetModuleLoadAddress(module, memmap[dynlib_path] - slide)
- return lldb_target
-
-
-def add_lldb_target_modules(lldb_target, memmap):
- for dynlib_path in memmap:
- module = lldb_target.AddModule(
- dynlib_path, lldb.LLDB_ARCH_DEFAULT, None, None)
- lldb_target.SetModuleLoadAddress(module, memmap[dynlib_path])
-
-
-lldb_target = None
-known_memmap = {}
def check_base_address(dynlib_path, dynlib_baseaddr, memmap):
@@ -116,11 +115,12 @@
frame_fragment, symbol_fragment, line_fragment)
-def process_stack(binary, dyn_libs, stack):
+def get_processed_stack(binary, dyn_libs, stack):
global lldb_target
global known_memmap
+ processed_stack = []
if len(stack) == 0:
- return
+ return processed_stack
memmap = {}
full_stack = []
for line in stack:
@@ -150,20 +150,68 @@
full_stack.append(
{"line": line, "framePC": framePC, "dynlib_fname": dynlib_fname})
- if lldb_target is None:
- lldb_target = create_lldb_target(binary, memmap)
- else:
- add_lldb_target_modules(lldb_target, memmap)
- frame_idx = 0
- for frame in full_stack:
+ setup_lldb_target(binary, memmap)
+
+ for frame_idx, frame in enumerate(full_stack):
frame_addr = frame["framePC"]
dynlib_fname = frame["dynlib_fname"]
try:
sym_line = symbolicate_one(frame_addr, frame_idx, dynlib_fname)
- print(sym_line)
+ processed_stack.append(sym_line)
except Exception:
- print(frame["line"].rstrip())
- frame_idx = frame_idx + 1
+ processed_stack.append(frame["line"].rstrip())
+
+ return processed_stack
+
+
+def is_fatal_error(line):
+ return line.startswith("Fatal error:")
+
+
+def is_stack_trace_header(line):
+ return line.startswith("Current stack trace:")
+
+
+def should_print_previous_line(current_line, previous_line):
+ return is_fatal_error(previous_line) and \
+ not is_stack_trace_header(current_line)
+
+
+def should_print_current_line(current_line, previous_line):
+ return (not is_fatal_error(current_line) and
+ not is_stack_trace_header(current_line)) or \
+ (is_stack_trace_header(current_line) and
+ not is_fatal_error(previous_line))
+
+
+def fatal_error_with_stack_trace_found(current_line, previous_line):
+ return is_stack_trace_header(current_line) and \
+ is_fatal_error(previous_line)
+
+
+def print_stack(fatal_error_header,
+ fatal_error_stack_trace_header,
+ fatal_log_format,
+ processed_stack):
+ if not fatal_error_header:
+ for line in processed_stack:
+ print_with_flush(line)
+ else:
+ # fatal error with a stack trace
+ stack_str = fatal_error_header + fatal_error_stack_trace_header + \
+ '\n'.join(processed_stack)
+ formatted_output = fatal_log_format
+
+ if "%t" in formatted_output:
+ current_time = datetime.datetime.now()
+ time_in_iso_format = \
+ current_time.strftime('%Y-%m-%dT%H:%M:%S,%f%z')
+ formatted_output = \
+ formatted_output.replace("%t", time_in_iso_format)
+ if "%m" in formatted_output:
+ formatted_output = formatted_output.replace("%m", stack_str)
+
+ print_with_flush(formatted_output)
def main():
@@ -175,34 +223,69 @@
parser.add_argument(
"log", nargs='?', type=argparse.FileType("rU"), default="-",
help="Log file for symbolication. Defaults to stdin.")
+ parser.add_argument(
+ "--fatal-log-format", default="%m",
+ help="Format for logging fatal errors. Variable %%t will be "
+ "replaced with current time in ISO 8601 format, variable "
+ "%%m will be replaced with the error message with a full "
+ "stack trace.")
args = parser.parse_args()
binary = args.binary
+ fatal_log_format = args.fatal_log_format
lddoutput = subprocess.check_output(
['ldd', binary], stderr=subprocess.STDOUT)
dyn_libs = process_ldd(lddoutput)
instack = False
+ previous_line = ""
stackidx = 0
stack = []
+ fatal_error_header = ""
+ fatal_error_stack_trace_header = ""
while True:
- line = args.log.readline()
- if not line:
+ current_line = args.log.readline()
+ if not current_line:
break
- if instack and line.startswith(str(stackidx)):
- stack.append(line)
+ if instack and current_line.startswith(str(stackidx)):
+ stack.append(current_line)
stackidx = stackidx + 1
else:
+ processed_stack = get_processed_stack(binary, dyn_libs, stack)
+ print_stack(fatal_error_header,
+ fatal_error_stack_trace_header,
+ fatal_log_format,
+ processed_stack)
+
instack = False
stackidx = 0
- process_stack(binary, dyn_libs, stack)
stack = []
- print(line.rstrip())
- if line.startswith("Current stack trace:"):
- instack = True
- process_stack(binary, dyn_libs, stack)
+ fatal_error_header = ""
+ fatal_error_stack_trace_header = ""
+
+ if is_stack_trace_header(current_line):
+ instack = True
+
+ if should_print_previous_line(current_line, previous_line):
+ print_with_flush(previous_line.rstrip())
+
+ if should_print_current_line(current_line, previous_line):
+ print_with_flush(current_line.rstrip())
+
+ if fatal_error_with_stack_trace_found(current_line, previous_line):
+ fatal_error_header = previous_line
+ fatal_error_stack_trace_header = current_line
+
+ previous_line = current_line
+ if is_fatal_error(previous_line):
+ print_with_flush(previous_line.rstrip())
+ processed_stack = get_processed_stack(binary, dyn_libs, stack)
+ print_stack(fatal_error_header,
+ fatal_error_stack_trace_header,
+ fatal_log_format,
+ processed_stack)
if __name__ == '__main__':
diff --git a/validation-test/IDE/slow_fixed/rdar45511835.swift b/validation-test/IDE/slow_fixed/rdar45511835.swift
new file mode 100644
index 0000000..4f9dd01
--- /dev/null
+++ b/validation-test/IDE/slow_fixed/rdar45511835.swift
@@ -0,0 +1,10 @@
+// RUN: %target-swift-ide-test -code-completion -code-completion-token=COMPLETE -source-filename=%s | %FileCheck %s
+
+// This used to take ~6 min to complete.
+
+func testing() {
+ return (["a"] + [1].map { String($0) })
+ .map { $0 + "b" as String }
+ .filter { $0 != "" } #^COMPLETE^#
+}
+// CHECK: Decl[InfixOperatorFunction]/{{.*}}: [' ']+ {#[String]#}[#[String]#]; name=+ [String]