Skip to content

Commit 99acd8c

Browse files
committed
[BuilderTransform] Warn about absence of buildLimitedAvailability(_:)
1 parent 04952fd commit 99acd8c

File tree

2 files changed

+123
-1
lines changed

2 files changed

+123
-1
lines changed

lib/Sema/CSClosure.cpp

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818

1919
#include "MiscDiagnostics.h"
2020
#include "TypeChecker.h"
21+
#include "TypeCheckAvailability.h"
2122
#include "swift/Sema/ConstraintSystem.h"
23+
#include "swift/Sema/IDETypeChecking.h"
2224

2325
using namespace swift;
2426
using namespace swift::constraints;
@@ -1688,12 +1690,15 @@ class SyntacticElementSolutionApplication
16881690
};
16891691

16901692
class ResultBuilderRewriter : public SyntacticElementSolutionApplication {
1693+
const AppliedBuilderTransform &Transform;
1694+
16911695
public:
16921696
ResultBuilderRewriter(Solution &solution, AnyFunctionRef context,
16931697
const AppliedBuilderTransform &transform,
16941698
RewriteTargetFn rewriteTarget)
16951699
: SyntacticElementSolutionApplication(
1696-
solution, context, transform.bodyResultType, rewriteTarget) {}
1700+
solution, context, transform.bodyResultType, rewriteTarget),
1701+
Transform(transform) {}
16971702

16981703
bool apply() {
16991704
auto body = visit(context.getBody());
@@ -1767,6 +1772,9 @@ class ResultBuilderRewriter : public SyntacticElementSolutionApplication {
17671772

17681773
NullablePtr<Stmt> transformIf(IfStmt *ifStmt, TypeJoinExpr *join,
17691774
unsigned index) {
1775+
// FIXME: Turn this into a condition once warning is an error.
1776+
(void)diagnoseMissingBuildWithAvailability(ifStmt);
1777+
17701778
auto *joinVar = join->getVar();
17711779

17721780
// First, let's add assignment to the end of `then` branch
@@ -1855,6 +1863,109 @@ class ResultBuilderRewriter : public SyntacticElementSolutionApplication {
18551863
ASTContext &getASTContext() const {
18561864
return context.getAsDeclContext()->getASTContext();
18571865
}
1866+
1867+
private:
1868+
/// Look for a #available condition. If there is one, we need to check
1869+
/// that the resulting type of the "then" doesn't refer to any types that
1870+
/// are unavailable in the enclosing context.
1871+
///
1872+
/// Note that this is for staging in support for buildLimitedAvailability();
1873+
/// the diagnostic is currently a warning, so that existing code that
1874+
/// compiles today will continue to compile. Once result builder types
1875+
/// have had the chance to adopt buildLimitedAvailability(), we'll upgrade
1876+
/// this warning to an error.
1877+
LLVM_NODISCARD
1878+
bool diagnoseMissingBuildWithAvailability(IfStmt *ifStmt) {
1879+
auto findAvailabilityCondition =
1880+
[](StmtCondition stmtCond) -> const StmtConditionElement * {
1881+
for (const auto &cond : stmtCond) {
1882+
switch (cond.getKind()) {
1883+
case StmtConditionElement::CK_Boolean:
1884+
case StmtConditionElement::CK_PatternBinding:
1885+
continue;
1886+
1887+
case StmtConditionElement::CK_Availability:
1888+
return &cond;
1889+
break;
1890+
}
1891+
}
1892+
1893+
return nullptr;
1894+
};
1895+
1896+
auto availabilityCond = findAvailabilityCondition(ifStmt->getCond());
1897+
if (!availabilityCond)
1898+
return false;
1899+
1900+
SourceLoc loc = availabilityCond->getStartLoc();
1901+
Type bodyType;
1902+
if (availabilityCond->getAvailability()->isUnavailability()) {
1903+
BraceStmt *elseBody = nullptr;
1904+
// For #unavailable, we need to check the "else".
1905+
if (auto *innerIf = getAsStmt<IfStmt>(ifStmt->getElseStmt())) {
1906+
elseBody = castToStmt<BraceStmt>(innerIf->getThenStmt());
1907+
} else {
1908+
elseBody = castToStmt<BraceStmt>(ifStmt->getElseStmt());
1909+
}
1910+
1911+
Type elseBodyType =
1912+
solution.simplifyType(solution.getType(elseBody->getLastElement()));
1913+
bodyType = elseBodyType;
1914+
} else {
1915+
auto *thenBody = castToStmt<BraceStmt>(ifStmt->getThenStmt());
1916+
Type thenBodyType =
1917+
solution.simplifyType(solution.getType(thenBody->getLastElement()));
1918+
bodyType = thenBodyType;
1919+
}
1920+
1921+
auto builderType = solution.simplifyType(Transform.builderType);
1922+
1923+
return bodyType.findIf([&](Type type) {
1924+
auto nominal = type->getAnyNominal();
1925+
if (!nominal)
1926+
return false;
1927+
1928+
ExportContext where =
1929+
ExportContext::forFunctionBody(context.getAsDeclContext(), loc);
1930+
if (auto reason =
1931+
TypeChecker::checkDeclarationAvailability(nominal, where)) {
1932+
auto &ctx = getASTContext();
1933+
ctx.Diags.diagnose(loc,
1934+
diag::result_builder_missing_limited_availability,
1935+
builderType);
1936+
1937+
// Add a note to the result builder with a stub for
1938+
// buildLimitedAvailability().
1939+
if (auto builder = builderType->getAnyNominal()) {
1940+
SourceLoc buildInsertionLoc;
1941+
std::string stubIndent;
1942+
Type componentType;
1943+
std::tie(buildInsertionLoc, stubIndent, componentType) =
1944+
determineResultBuilderBuildFixItInfo(builder);
1945+
if (buildInsertionLoc.isValid()) {
1946+
std::string fixItString;
1947+
{
1948+
llvm::raw_string_ostream out(fixItString);
1949+
printResultBuilderBuildFunction(
1950+
builder, componentType,
1951+
ResultBuilderBuildFunction::BuildLimitedAvailability,
1952+
stubIndent, out);
1953+
1954+
builder
1955+
->diagnose(
1956+
diag::result_builder_missing_build_limited_availability,
1957+
builderType)
1958+
.fixItInsert(buildInsertionLoc, fixItString);
1959+
}
1960+
}
1961+
}
1962+
1963+
return true;
1964+
}
1965+
1966+
return false;
1967+
});
1968+
}
18581969
};
18591970
} // namespace
18601971

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1893,6 +1893,17 @@ static bool fixAvailabilityByNarrowingNearbyVersionCheck(
18931893
static void fixAvailabilityByAddingVersionCheck(
18941894
ASTNode NodeToWrap, const VersionRange &RequiredRange,
18951895
SourceRange ReferenceRange, ASTContext &Context) {
1896+
// If this is an implicit variable that wraps an expression,
1897+
// let's point to it's initializer. For example, result builder
1898+
// transform captures expressions into implicit variables.
1899+
if (auto *PB =
1900+
dyn_cast_or_null<PatternBindingDecl>(NodeToWrap.dyn_cast<Decl *>())) {
1901+
if (PB->isImplicit() && PB->getSingleVar()) {
1902+
if (auto *init = PB->getInit(0))
1903+
NodeToWrap = init;
1904+
}
1905+
}
1906+
18961907
SourceRange RangeToWrap = NodeToWrap.getSourceRange();
18971908
if (RangeToWrap.isInvalid())
18981909
return;

0 commit comments

Comments
 (0)