Skip to content

Commit 08315b1

Browse files
authored
Merge pull request swiftlang#29786 from DougGregor/function-builders-let-decls
Allow initialized let/var declarations in function builders.
2 parents ac71c7c + 2347829 commit 08315b1

File tree

4 files changed

+211
-2
lines changed

4 files changed

+211
-2
lines changed

lib/Sema/BuilderTransform.cpp

Lines changed: 187 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,143 @@ class BuilderClosureVisitor
235235
return nullptr; \
236236
}
237237

238+
/// Provide a type for each variable that occurs within the given pattern,
239+
/// by matching the pattern structurally with its already-computed pattern
240+
/// type. The variables will either get a concrete type (when present in
241+
/// the pattern type) or a fresh type variable bound to that part of the
242+
/// pattern via a one-way constraint.
243+
void bindVariablesInPattern(Pattern *pattern, Type patternType,
244+
ConstraintLocator *locator) {
245+
switch (pattern->getKind()) {
246+
case PatternKind::Paren: {
247+
// Parentheses don't affect the type, but unwrap a paren type if we have
248+
// one.
249+
Type subPatternType;
250+
if (auto parenType = dyn_cast<ParenType>(patternType.getPointer()))
251+
subPatternType = parenType->getUnderlyingType();
252+
else
253+
subPatternType = patternType;
254+
return bindVariablesInPattern(
255+
cast<ParenPattern>(pattern)->getSubPattern(),
256+
subPatternType, locator);
257+
}
258+
259+
case PatternKind::Var:
260+
// Var doesn't affect the type.
261+
return bindVariablesInPattern(cast<VarPattern>(pattern)->getSubPattern(),
262+
patternType, locator);
263+
264+
case PatternKind::Any:
265+
// Nothing to bind.
266+
return;
267+
268+
case PatternKind::Named: {
269+
auto var = cast<NamedPattern>(pattern)->getDecl();
270+
271+
/// Create a fresh type variable to describe the type of the
272+
Type varType = cs->createTypeVariable(locator, TVO_CanBindToNoEscape);
273+
274+
auto ROK = ReferenceOwnership::Strong;
275+
if (auto *OA = var->getAttrs().getAttribute<ReferenceOwnershipAttr>())
276+
ROK = OA->get();
277+
switch (optionalityOf(ROK)) {
278+
case ReferenceOwnershipOptionality::Required:
279+
// FIXME: Can we assert this rather than just checking it.
280+
if (auto optPatternType =
281+
dyn_cast<OptionalType>(patternType.getPointer())) {
282+
// Add a one-way constraint from the type variable to the wrapped
283+
// type of the optional.
284+
cs->addConstraint(
285+
ConstraintKind::OneWayEqual, varType, optPatternType->getBaseType(),
286+
locator);
287+
288+
// Make the variable type optional.
289+
varType = TypeChecker::getOptionalType(var->getLoc(), varType);
290+
break;
291+
}
292+
293+
// Fall through to treat this normally.
294+
LLVM_FALLTHROUGH;
295+
296+
case ReferenceOwnershipOptionality::Allowed:
297+
case ReferenceOwnershipOptionality::Disallowed:
298+
// Add the one-way constraint from the variable type to the pattern
299+
// type.
300+
cs->addConstraint(ConstraintKind::OneWayEqual, varType, patternType,
301+
locator);
302+
break;
303+
}
304+
305+
// Bind the type of the variable.
306+
cs->setType(var, varType);
307+
return;
308+
}
309+
310+
case PatternKind::Typed: {
311+
// Ignore the type itself; it's part of patternType now.
312+
return bindVariablesInPattern(
313+
cast<TypedPattern>(pattern)->getSubPattern(),
314+
patternType, locator);
315+
}
316+
317+
case PatternKind::Tuple: {
318+
auto tuplePat = cast<TuplePattern>(pattern);
319+
auto tupleType = patternType->castTo<TupleType>();
320+
for (unsigned i = 0, e = tuplePat->getNumElements(); i != e; ++i) {
321+
bindVariablesInPattern(tuplePat->getElement(i).getPattern(),
322+
tupleType->getElementType(i), locator);
323+
}
324+
return;
325+
}
326+
327+
// FIXME: Refutable patterns will generate additional constraints.
328+
#define PATTERN(Id, Parent)
329+
#define REFUTABLE_PATTERN(Id, Parent) case PatternKind::Id:
330+
#include "swift/AST/PatternNodes.def"
331+
llvm_unreachable("Refutable patterns are not supported here");
332+
}
333+
334+
llvm_unreachable("Unhandled pattern kind");
335+
}
336+
337+
void visitPatternBindingDecl(PatternBindingDecl *patternBinding) {
338+
// If any of the entries lacks an initializer, don't handle this node.
339+
if (!llvm::all_of(range(patternBinding->getNumPatternEntries()),
340+
[&](unsigned index) {
341+
return patternBinding->isExplicitlyInitialized(index);
342+
})) {
343+
if (!unhandledNode)
344+
unhandledNode = patternBinding;
345+
return;
346+
}
347+
348+
// If we aren't generating constraints, there's nothing to do.
349+
if (!cs)
350+
return;
351+
352+
/// Generate constraints for each pattern binding entry
353+
for (unsigned index : range(patternBinding->getNumPatternEntries())) {
354+
// Type check the pattern.
355+
auto pattern = patternBinding->getPattern(index);
356+
auto contextualPattern = ContextualPattern::forRawPattern(pattern, dc);
357+
Type patternType = TypeChecker::typeCheckPattern(contextualPattern);
358+
359+
// Generate constraints for the initialization.
360+
auto target = SolutionApplicationTarget::forInitialization(
361+
patternBinding->getInit(index), dc, patternType, pattern);
362+
if (cs->generateConstraints(target, FreeTypeVariableBinding::Disallow))
363+
continue;
364+
365+
// Keep track of this binding entry.
366+
applied.patternBindingEntries.insert({{patternBinding, index}, target});
367+
368+
// Bind the variables that occur in the pattern to the corresponding
369+
// type entry for the pattern itself.
370+
bindVariablesInPattern(pattern, cs->getType(pattern),
371+
cs->getConstraintLocator(target.getAsExpr()));
372+
}
373+
}
374+
238375
VarDecl *visitBraceStmt(BraceStmt *braceStmt) {
239376
SmallVector<Expr *, 4> expressions;
240377
auto addChild = [&](VarDecl *childVar) {
@@ -270,6 +407,18 @@ class BuilderClosureVisitor
270407
continue;
271408
}
272409

410+
// Pattern bindings are okay so long as all of the entries are
411+
// initialized.
412+
if (auto patternBinding = dyn_cast<PatternBindingDecl>(decl)) {
413+
visitPatternBindingDecl(patternBinding);
414+
continue;
415+
}
416+
417+
// Ignore variable declarations, because they're always handled within
418+
// their enclosing pattern bindings.
419+
if (isa<VarDecl>(decl))
420+
continue;
421+
273422
if (!unhandledNode)
274423
unhandledNode = decl;
275424

@@ -744,6 +893,26 @@ class BuilderClosureRewriter
744893
elements.push_back(pbd);
745894
}
746895

896+
/// Produce a final type-checked pattern binding.
897+
void finishPatternBindingDecl(PatternBindingDecl *patternBinding) {
898+
for (unsigned index : range(patternBinding->getNumPatternEntries())) {
899+
// Find the solution application target for this.
900+
auto knownTarget =
901+
builderTransform.patternBindingEntries.find({patternBinding, index});
902+
assert(knownTarget != builderTransform.patternBindingEntries.end());
903+
904+
// Rewrite the target.
905+
auto resultTarget = rewriteTarget(knownTarget->second);
906+
if (!resultTarget)
907+
continue;
908+
909+
patternBinding->setPattern(
910+
index, resultTarget->getInitializationPattern(),
911+
resultTarget->getDeclContext());
912+
patternBinding->setInit(index, resultTarget->getAsExpr());
913+
}
914+
}
915+
747916
public:
748917
BuilderClosureRewriter(
749918
const Solution &solution,
@@ -811,12 +980,29 @@ class BuilderClosureRewriter
811980
auto decl = node.get<Decl *>();
812981

813982
// Skip #if declarations.
814-
if (isa<IfConfigDecl>(decl))
983+
if (isa<IfConfigDecl>(decl)) {
984+
newElements.push_back(decl);
815985
continue;
986+
}
816987

817988
// Diagnose #warning / #error during application.
818989
if (auto poundDiag = dyn_cast<PoundDiagnosticDecl>(decl)) {
819990
TypeChecker::typeCheckDecl(poundDiag);
991+
newElements.push_back(decl);
992+
continue;
993+
}
994+
995+
// Skip variable declarations; they're always part of a pattern
996+
// binding.
997+
if (isa<VarDecl>(decl)) {
998+
newElements.push_back(decl);
999+
continue;
1000+
}
1001+
1002+
// Handle pattern bindings.
1003+
if (auto patternBinding = dyn_cast<PatternBindingDecl>(decl)) {
1004+
finishPatternBindingDecl(patternBinding);
1005+
newElements.push_back(decl);
8201006
continue;
8211007
}
8221008

lib/Sema/ConstraintSystem.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,13 @@ struct AppliedBuilderTransform {
697697

698698
/// The return expression, capturing the last value to be emitted.
699699
Expr *returnExpr = nullptr;
700+
701+
using PatternEntry = std::pair<const PatternBindingDecl *, unsigned>;
702+
703+
/// Mapping from specific pattern binding entries to the solution application
704+
/// targets capturing their initialization.
705+
llvm::DenseMap<PatternEntry, SolutionApplicationTarget>
706+
patternBindingEntries;
700707
};
701708

702709
/// Describes the fixed score of a solution to the constraint system.

test/Constraints/function_builder.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,3 +476,18 @@ func testIfConditions(cond: Bool, c1: Bool, i1: Int, i2: Int) {
476476
testIfConditions(cond: true, c1: true, i1: 1, i2: 1)
477477
// CHECK: testIfConditions
478478
// CHECK-SAME: hello
479+
480+
// Use a "let" declaration within a function builder.
481+
tuplify(true) { c in
482+
"testLetDeclarations"
483+
let (a, b) = (c, c && true)
484+
if a == b {
485+
"hello"
486+
b
487+
}
488+
a
489+
}
490+
// CHECK: testLetDeclarations"
491+
// CHECK-SAME: hello
492+
// CHECK-SAME: true
493+

test/Constraints/function_builder_diags.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ func testDiags() {
8383
// Declarations
8484
tuplify(true) { _ in
8585
17
86-
let x = 17 // expected-error{{closure containing a declaration cannot be used with function builder 'TupleBuilder'}}
86+
let x = 17
87+
let y: Int // expected-error{{closure containing a declaration cannot be used with function builder 'TupleBuilder'}}
8788
x + 25
8889
}
8990

0 commit comments

Comments
 (0)