//===--- CSClosure.cpp - Closures -----------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file implements constraint generation and solution application for // closures. It provides part of the implementation of the ConstraintSystem // class. // //===----------------------------------------------------------------------===// #include "TypeChecker.h" #include "swift/Sema/ConstraintSystem.h" using namespace swift; using namespace swift::constraints; namespace { /// Find any type variable references inside of an AST node. class TypeVariableRefFinder : public ASTWalker { ConstraintSystem &CS; ASTNode Parent; llvm::SmallSetVector referencedVars; public: TypeVariableRefFinder(ConstraintSystem &cs, ASTNode parent) : CS(cs), Parent(parent) {} std::pair walkToExprPre(Expr *expr) override { if (auto *DRE = dyn_cast(expr)) { if (auto type = CS.getTypeIfAvailable(DRE->getDecl())) { if (auto *typeVar = type->getAs()) referencedVars.insert(typeVar); } } return {true, expr}; } std::pair walkToStmtPre(Stmt *stmt) override { // Return statements have to reference outside result type // since all of them are joined by it if it's not specified // explicitly. if (isa(stmt)) { if (auto *closure = getAsExpr(Parent)) { auto resultTy = CS.getClosureType(closure)->getResult(); if (auto *typeVar = resultTy->getAs()) referencedVars.insert(typeVar); } } return {true, stmt}; } ArrayRef getReferencedVars() const { return referencedVars.getArrayRef(); } }; // MARK: Constraint generation /// Check whether it makes sense to convert this element into a constrant. static bool isViableElement(ASTNode element) { if (auto *decl = element.dyn_cast()) { // - Ignore variable declarations, they are handled by pattern bindings; // - Ignore #if, the chosen children should appear in the // surrounding context; // - Skip #warning and #error, they are handled during solution // application. if (isa(decl) || isa(decl) || isa(decl)) return false; } if (auto *stmt = element.dyn_cast()) { // Empty brace statements are now viable because they do not require // inference. if (auto *braceStmt = dyn_cast(stmt)) { return braceStmt->getNumElements() > 0; } } return true; } static void createConjunctionForBody(ConstraintSystem &cs, BraceStmt *body, ASTNode parent, ConstraintLocator *locator) { bool isIsolated = false; SmallVector referencedVars; // If this is a top level closure, it has to be isolated. if (auto *closure = getAsExpr(parent)) { isIsolated = !isa(closure->getParent()); referencedVars.push_back(cs.getType(closure)->castTo()); } SmallVector elements; for (auto element : body->getElements()) { if (!isViableElement(element)) continue; TypeVariableRefFinder refFinder(cs, parent); // If conjunction would require isolation, each element // has to bring any of the referenced outer context // type variables into scope. if (isIsolated) element.walk(refFinder); auto *elementLoc = cs.getConstraintLocator( locator, LocatorPathElt::ClosureBodyElement(element)); elements.push_back(Constraint::createClosureBodyElement( cs, element, elementLoc, refFinder.getReferencedVars())); } cs.addUnsolvedConstraint(Constraint::createConjunction( cs, elements, isIsolated, locator, referencedVars)); } static void createNonIsolatedConjunction( ConstraintSystem &cs, ArrayRef> elements, ConstraintLocator *locator) { SmallVector constraints; for (const auto &entry : elements) { ASTNode element = entry.first; ConstraintLocator *elementLoc = entry.second; if (!isViableElement(element)) continue; constraints.push_back(Constraint::createClosureBodyElement( cs, element, elementLoc)); } cs.addUnsolvedConstraint(Constraint::createConjunction( cs, constraints, /*isIsolated=*/false, locator)); } /// Statement visitor that generates constraints for a given closure body. class ClosureConstraintGenerator : public StmtVisitor { friend StmtVisitor; ConstraintSystem &cs; ClosureExpr *closure; ConstraintLocator *locator; public: /// Whether an error was encountered while generating constraints. bool hadError = false; ClosureConstraintGenerator(ConstraintSystem &cs, ClosureExpr *closure, ConstraintLocator *locator) : cs(cs), closure(closure), locator(locator) {} private: void visitDecl(Decl *decl) { if (isSupportedMultiStatementClosure()) { if (auto patternBinding = dyn_cast(decl)) { SolutionApplicationTarget target(patternBinding); if (cs.generateConstraints(target, FreeTypeVariableBinding::Disallow)) hadError = true; return; } } // Just ignore #if; the chosen children should appear in the // surrounding context. This isn't good for source tools but it // at least works. if (isa(decl)) return; // Skip #warning/#error; we'll handle them when applying the closure. if (isa(decl)) return; // Ignore variable declarations, because they're always handled within // their enclosing pattern bindings. if (isa(decl)) return; llvm_unreachable("Unimplemented case for closure body"); } void visitIfStmt(IfStmt *ifStmt) { if (!isSupportedMultiStatementClosure()) llvm_unreachable("Unsupported statement: If"); SmallVector> elements; // Condition { auto *condLoc = cs.getConstraintLocator(locator, ConstraintLocator::Condition); elements.push_back({ifStmt->getCondPointer(), condLoc}); } // Then Branch { auto *thenLoc = cs.getConstraintLocator( locator, LocatorPathElt::TernaryBranch(/*then=*/true)); elements.push_back({ifStmt->getThenStmt(), thenLoc}); } // Else Branch (if any). if (auto *elseStmt = ifStmt->getElseStmt()) { auto *elseLoc = cs.getConstraintLocator( locator, LocatorPathElt::TernaryBranch(/*then=*/false)); elements.push_back({ifStmt->getElseStmt(), elseLoc}); } createNonIsolatedConjunction(cs, elements, locator); } void visitBraceStmt(BraceStmt *braceStmt) { if (isSupportedMultiStatementClosure()) { ASTNode parent; if (locator->getPath().empty()) { parent = locator->getAnchor(); } else { auto parentElt = locator->findLast(); assert(parentElt && "body has to have an anchor statement"); parent = parentElt->getElement(); } createConjunctionForBody(cs, braceStmt, parent, locator); return; } for (auto node : braceStmt->getElements()) { if (auto expr = node.dyn_cast()) { auto generatedExpr = cs.generateConstraints( expr, closure, /*isInputExpression=*/false); if (!generatedExpr) { hadError = true; } } else if (auto stmt = node.dyn_cast()) { visit(stmt); } else { visitDecl(node.get()); } } } void visitReturnStmt(ReturnStmt *returnStmt) { auto expr = returnStmt->getResult(); // FIXME: Implies Void return? if (!expr) return; // FIXME: Use SolutionApplicationTarget? expr = cs.generateConstraints(expr, closure, /*isInputExpression=*/false); if (!expr) { hadError = true; return; } // FIXME: Locator should point at the return statement? bool hasReturn = hasExplicitResult(closure); cs.addConstraint(ConstraintKind::Conversion, cs.getType(expr), cs.getClosureType(closure)->getResult(), cs.getConstraintLocator( closure, LocatorPathElt::ClosureBody(hasReturn))); } bool isSupportedMultiStatementClosure() const { return !closure->hasSingleExpressionBody() && shouldTypeCheckInEnclosingExpression(closure); } #define UNSUPPORTED_STMT(STMT) void visit##STMT##Stmt(STMT##Stmt *) { \ llvm_unreachable("Unsupported statement kind " #STMT); \ } UNSUPPORTED_STMT(Yield) UNSUPPORTED_STMT(Defer) UNSUPPORTED_STMT(Guard) UNSUPPORTED_STMT(While) UNSUPPORTED_STMT(Do) UNSUPPORTED_STMT(DoCatch) UNSUPPORTED_STMT(RepeatWhile) UNSUPPORTED_STMT(ForEach) UNSUPPORTED_STMT(Switch) UNSUPPORTED_STMT(Case) UNSUPPORTED_STMT(Break) UNSUPPORTED_STMT(Continue) UNSUPPORTED_STMT(Fallthrough) UNSUPPORTED_STMT(Fail) UNSUPPORTED_STMT(Throw) UNSUPPORTED_STMT(PoundAssert) #undef UNSUPPORTED_STMT }; } bool ConstraintSystem::generateConstraints(ClosureExpr *closure) { auto &ctx = closure->getASTContext(); if (shouldTypeCheckInEnclosingExpression(closure)) { ClosureConstraintGenerator generator(*this, closure, getConstraintLocator(closure)); generator.visit(closure->getBody()); if (closure->hasSingleExpressionBody()) return generator.hadError; } // If this closure has an empty body and no explicit result type // let's bind result type to `Void` since that's the only type empty body // can produce. Otherwise, if (multi-statement) closure doesn't have // an explicit result (no `return` statements) let's default it to `Void`. if (!hasExplicitResult(closure)) { auto constraintKind = (closure->hasEmptyBody() && !closure->hasExplicitResultType()) ? ConstraintKind::Bind : ConstraintKind::Defaultable; addConstraint( constraintKind, getClosureType(closure)->getResult(), ctx.TheEmptyTupleType, getConstraintLocator(closure, ConstraintLocator::ClosureResult)); } return false; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyClosureBodyElementConstraint( ASTNode element, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { auto *closure = castToExpr(locator.getAnchor()); ClosureConstraintGenerator generator(*this, closure, getConstraintLocator(locator)); if (auto *expr = element.dyn_cast()) { if (!generateConstraints(expr, closure, /*isInputExpression=*/false)) return SolutionKind::Error; } else if (auto *stmt = element.dyn_cast()) { generator.visit(stmt); } else if (auto *cond = element.dyn_cast()) { if (generateConstraints(*cond, closure)) return SolutionKind::Error; } else { generator.visit(element.get()); } return generator.hadError ? SolutionKind::Error : SolutionKind::Solved; } // MARK: Solution application namespace { /// Statement visitor that applies constraints for a given closure body. class ClosureConstraintApplication : public StmtVisitor { friend StmtVisitor; Solution &solution; ClosureExpr *closure; Type resultType; RewriteTargetFn rewriteTarget; bool isSingleExpression; public: /// Whether an error was encountered while generating constraints. bool hadError = false; ClosureConstraintApplication( Solution &solution, ClosureExpr *closure, Type resultType, RewriteTargetFn rewriteTarget) : solution(solution), closure(closure), resultType(resultType), rewriteTarget(rewriteTarget), isSingleExpression(closure->hasSingleExpressionBody()) { } private: /// Rewrite an expression without any particularly special context. Expr *rewriteExpr(Expr *expr) { auto result = rewriteTarget( SolutionApplicationTarget(expr, closure, CTP_Unused, Type(), /*isDiscarded=*/false)); if (result) return result->getAsExpr(); return nullptr; } void visitDecl(Decl *decl) { if (isa(decl)) return; // Variable declaration would be handled by a pattern binding. if (isa(decl)) return; // Generate constraints for pattern binding declarations. if (auto patternBinding = dyn_cast(decl)) { SolutionApplicationTarget target(patternBinding); if (!rewriteTarget(target)) hadError = true; return; } TypeChecker::typeCheckDecl(decl); } ASTNode visitIfStmt(IfStmt *ifStmt) { // Rewrite the condition. if (auto condition = rewriteTarget( SolutionApplicationTarget(ifStmt->getCond(), closure))) ifStmt->setCond(*condition->getAsStmtCondition()); else hadError = true; ifStmt->setThenStmt(visit(ifStmt->getThenStmt()).get()); if (auto elseStmt = ifStmt->getElseStmt()) { ifStmt->setElseStmt(visit(elseStmt).get()); } return ifStmt; } ASTNode visitBraceStmt(BraceStmt *braceStmt) { for (auto &node : braceStmt->getElements()) { if (auto expr = node.dyn_cast()) { // Rewrite the expression. if (auto rewrittenExpr = rewriteExpr(expr)) node = rewrittenExpr; else hadError = true; } else if (auto stmt = node.dyn_cast()) { node = visit(stmt); } else { visitDecl(node.get()); } } return braceStmt; } ASTNode visitReturnStmt(ReturnStmt *returnStmt) { auto resultExpr = returnStmt->getResult(); if (!resultExpr) return returnStmt; enum { convertToResult, coerceToVoid, coerceFromNever, } mode; auto resultExprType = solution.simplifyType(solution.getType(resultExpr))->getRValueType(); // A closure with a non-void return expression can coerce to a closure // that returns Void. if (resultType->isVoid() && !resultExprType->isVoid()) { mode = coerceToVoid; // A single-expression closure with a Never expression type // coerces to any other function type. } else if (isSingleExpression && resultExprType->isUninhabited()) { mode = coerceFromNever; // Normal rule is to coerce to the return expression to the closure type. } else { mode = convertToResult; } SolutionApplicationTarget resultTarget( resultExpr, closure, mode == convertToResult ? CTP_ReturnStmt : CTP_Unused, mode == convertToResult ? resultType : Type(), /*isDiscarded=*/false); if (auto newResultTarget = rewriteTarget(resultTarget)) resultExpr = newResultTarget->getAsExpr(); switch (mode) { case convertToResult: // Record the coerced expression. returnStmt->setResult(resultExpr); return returnStmt; case coerceToVoid: { // Evaluate the expression, then produce a return statement that // returns nothing. TypeChecker::checkIgnoredExpr(resultExpr); auto &ctx = solution.getConstraintSystem().getASTContext(); auto newReturnStmt = new (ctx) ReturnStmt( returnStmt->getStartLoc(), nullptr, /*implicit=*/true); ASTNode elements[2] = { resultExpr, newReturnStmt }; return BraceStmt::create(ctx, returnStmt->getStartLoc(), elements, returnStmt->getEndLoc(), /*implicit*/ true); } case coerceFromNever: // Replace the return statement with its expression, so that the // expression is evaluated directly. This only works because coercion // from never is limited to single-expression closures. return resultExpr; } return returnStmt; } #define UNSUPPORTED_STMT(STMT) ASTNode visit##STMT##Stmt(STMT##Stmt *) { \ llvm_unreachable("Unsupported statement kind " #STMT); \ } UNSUPPORTED_STMT(Yield) UNSUPPORTED_STMT(Defer) UNSUPPORTED_STMT(Guard) UNSUPPORTED_STMT(While) UNSUPPORTED_STMT(Do) UNSUPPORTED_STMT(DoCatch) UNSUPPORTED_STMT(RepeatWhile) UNSUPPORTED_STMT(ForEach) UNSUPPORTED_STMT(Switch) UNSUPPORTED_STMT(Case) UNSUPPORTED_STMT(Break) UNSUPPORTED_STMT(Continue) UNSUPPORTED_STMT(Fallthrough) UNSUPPORTED_STMT(Fail) UNSUPPORTED_STMT(Throw) UNSUPPORTED_STMT(PoundAssert) #undef UNSUPPORTED_STMT }; } SolutionApplicationToFunctionResult ConstraintSystem::applySolution( Solution &solution, AnyFunctionRef fn, DeclContext *¤tDC, RewriteTargetFn rewriteTarget) { auto &cs = solution.getConstraintSystem(); auto closure = dyn_cast_or_null(fn.getAbstractClosureExpr()); FunctionType *closureFnType = nullptr; if (closure) { // Update the closure's type. auto closureType = solution.simplifyType(cs.getType(closure)); cs.setType(closure, closureType); // Coerce the parameter types. closureFnType = closureType->castTo(); auto *params = closure->getParameters(); TypeChecker::coerceParameterListToType(params, closureFnType); // Coerce the result type, if it was written explicitly. if (closure->hasExplicitResultType()) { closure->setExplicitResultType(closureFnType->getResult()); } } // Enter the context of the function before performing any additional // transformations. llvm::SaveAndRestore savedDC(currentDC, fn.getAsDeclContext()); // Apply the result builder transform, if there is one. if (auto transform = solution.getAppliedBuilderTransform(fn)) { // Apply the result builder to the closure. We want to be in the // context of the closure for subsequent transforms. auto newBody = applyResultBuilderTransform( solution, *transform, fn.getBody(), fn.getAsDeclContext(), [&](SolutionApplicationTarget target) { auto resultTarget = rewriteTarget(target); if (resultTarget) { if (auto expr = resultTarget->getAsExpr()) solution.setExprTypes(expr); } return resultTarget; }); if (!newBody) return SolutionApplicationToFunctionResult::Failure; fn.setTypecheckedBody(newBody, /*isSingleExpression=*/false); if (closure) { solution.setExprTypes(closure); } return SolutionApplicationToFunctionResult::Success; } assert(closure && "Can only get here with a closure at the moment"); // If this closure is checked as part of the enclosing expression, handle // that now. if (shouldTypeCheckInEnclosingExpression(closure)) { ClosureConstraintApplication application( solution, closure, closureFnType->getResult(), rewriteTarget); application.visit(fn.getBody()); closure->setBodyState(ClosureExpr::BodyState::TypeCheckedWithSignature); return SolutionApplicationToFunctionResult::Success; } // Otherwise, we need to delay type checking of the closure until later. solution.setExprTypes(closure); closure->setBodyState(ClosureExpr::BodyState::ReadyForTypeChecking); return SolutionApplicationToFunctionResult::Delay; }