diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index 963a53fadf51c..1511965adda5b 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -324,27 +324,11 @@ class BuilderClosureVisitor CONTROL_FLOW_STMT(Yield) CONTROL_FLOW_STMT(Defer) - /// Whether we can handle all of the conditions for this statement. - static bool canHandleStmtConditions(StmtCondition condition) { - for (const auto &element : condition) { - switch (element.getKind()) { - case StmtConditionElement::CK_Boolean: - continue; - - case StmtConditionElement::CK_PatternBinding: - case StmtConditionElement::CK_Availability: - return false; - } - } - - return true; - } - static bool isBuildableIfChainRecursive(IfStmt *ifStmt, unsigned &numPayloads, bool &isOptional) { // Check whether we can handle the conditional. - if (!canHandleStmtConditions(ifStmt->getCond())) + if (!ConstraintSystem::canGenerateConstraints(ifStmt->getCond())) return false; // The 'then' clause contributes a payload. @@ -470,38 +454,12 @@ class BuilderClosureVisitor payloadIndex + 1, numPayloads, isOptional); } - // Condition must convert to Bool. - // FIXME: This should be folded into constraint generation for conditions. - auto boolDecl = ctx.getBoolDecl(); - if (!boolDecl) { + // Generate constraints for the conditions. + if (cs->generateConstraints(ifStmt->getCond(), dc)) { hadError = true; return nullptr; } - // Generate constraints for the conditions. - for (const auto &condElement : ifStmt->getCond()) { - switch (condElement.getKind()) { - case StmtConditionElement::CK_Boolean: { - Expr *condExpr = condElement.getBoolean(); - condExpr = cs->generateConstraints(condExpr, dc); - if (!condExpr) { - hadError = true; - return nullptr; - } - - cs->addConstraint(ConstraintKind::Conversion, - cs->getType(condExpr), - boolDecl->getDeclaredType(), - cs->getConstraintLocator(condExpr)); - continue; - } - - case StmtConditionElement::CK_PatternBinding: - case StmtConditionElement::CK_Availability: - llvm_unreachable("unhandled statement condition"); - } - } - // The operand should have optional type if we had optional results, // so we just need to call `buildIf` now, since we're at the top level. if (isOptional && isTopLevel) { @@ -873,6 +831,9 @@ class BuilderClosureRewriter auto condition = ifStmt->getCond(); for (auto &condElement : condition) { switch (condElement.getKind()) { + case StmtConditionElement::CK_Availability: + continue; + case StmtConditionElement::CK_Boolean: { auto condExpr = condElement.getBoolean(); auto finalCondExpr = rewriteExpr(condExpr); @@ -888,7 +849,6 @@ class BuilderClosureRewriter } case StmtConditionElement::CK_PatternBinding: - case StmtConditionElement::CK_Availability: llvm_unreachable("unhandled statement condition"); } } diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 78402bbf3e69d..69946ab363743 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3798,6 +3798,57 @@ Type ConstraintSystem::generateConstraints(Pattern *pattern, return cg.getTypeForPattern(pattern, locator); } +bool ConstraintSystem::canGenerateConstraints(StmtCondition condition) { + for (const auto &element : condition) { + switch (element.getKind()) { + case StmtConditionElement::CK_Availability: + case StmtConditionElement::CK_Boolean: + continue; + + case StmtConditionElement::CK_PatternBinding: + return false; + } + } + + return true; +} + +bool ConstraintSystem::generateConstraints(StmtCondition condition, + DeclContext *dc) { + // FIXME: This should be folded into constraint generation for conditions. + auto boolDecl = getASTContext().getBoolDecl(); + if (!boolDecl) { + return true; + } + + for (const auto &condElement : condition) { + switch (condElement.getKind()) { + case StmtConditionElement::CK_Availability: + // Nothing to do here. + continue; + + case StmtConditionElement::CK_Boolean: { + Expr *condExpr = condElement.getBoolean(); + condExpr = generateConstraints(condExpr, dc); + if (!condExpr) { + return true; + } + + addConstraint(ConstraintKind::Conversion, + getType(condExpr), + boolDecl->getDeclaredType(), + getConstraintLocator(condExpr)); + continue; + } + + case StmtConditionElement::CK_PatternBinding: + llvm_unreachable("unhandled statement condition"); + } + } + + return false; +} + void ConstraintSystem::optimizeConstraints(Expr *e) { if (getASTContext().TypeCheckerOpts.DisableConstraintSolverPerformanceHacks) return; diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index ff516c05099a7..f30ed8a7395eb 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -3065,6 +3065,16 @@ class ConstraintSystem { /// \returns a possibly-sanitized initializer, or null if an error occurred. Type generateConstraints(Pattern *P, ConstraintLocatorBuilder locator); + /// Determines whether we can generate constraints for this statement + /// condition. + static bool canGenerateConstraints(StmtCondition condition); + + /// Generate constraints for a statement condition. + /// + /// \returns true if there was an error in constraint generation, false + /// if generation succeeded. + bool generateConstraints(StmtCondition condition, DeclContext *dc); + /// Generate constraints for a given set of overload choices. /// /// \param constraints The container of generated constraint choices. diff --git a/test/Constraints/function_builder_availability.swift b/test/Constraints/function_builder_availability.swift new file mode 100644 index 0000000000000..ac63adc4a7f7f --- /dev/null +++ b/test/Constraints/function_builder_availability.swift @@ -0,0 +1,74 @@ +// RUN: %target-typecheck-verify-swift -target x86_64-apple-macosx10.50 + +// REQUIRES: OS=macosx + +enum Either { + case first(T) + case second(U) +} + +@_functionBuilder +struct TupleBuilder { + static func buildBlock(_ t1: T1) -> (T1) { + return (t1) + } + + static func buildBlock(_ t1: T1, _ t2: T2) -> (T1, T2) { + return (t1, t2) + } + + static func buildBlock(_ t1: T1, _ t2: T2, _ t3: T3) + -> (T1, T2, T3) { + return (t1, t2, t3) + } + + static func buildBlock(_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4) + -> (T1, T2, T3, T4) { + return (t1, t2, t3, t4) + } + + static func buildBlock( + _ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4, _ t5: T5 + ) -> (T1, T2, T3, T4, T5) { + return (t1, t2, t3, t4, t5) + } + + static func buildDo(_ value: T) -> T { return value } + static func buildIf(_ value: T?) -> T? { return value } + + static func buildEither(first value: T) -> Either { + return .first(value) + } + static func buildEither(second value: U) -> Either { + return .second(value) + } +} + +func tuplify(_ cond: Bool, @TupleBuilder body: (Bool) -> T) { + print(body(cond)) +} + +@available(OSX, introduced: 10.9) +func globalFuncAvailableOn10_9() -> Int { return 9 } + +@available(OSX, introduced: 10.51) +func globalFuncAvailableOn10_51() -> Int { return 10 } + +@available(OSX, introduced: 10.52) +func globalFuncAvailableOn10_52() -> Int { return 11 } + +tuplify(true) { cond in + globalFuncAvailableOn10_9() + if #available(OSX 10.51, *) { + globalFuncAvailableOn10_51() + tuplify(false) { cond2 in + if cond, #available(OSX 10.52, *) { + cond2 + globalFuncAvailableOn10_52() + } else { + globalFuncAvailableOn10_52() // expected-error{{'globalFuncAvailableOn10_52()' is only available in macOS 10.52 or newer}} + // expected-note@-1{{add 'if #available' version check}} + } + } + } +}