Skip to content

Commit 790b549

Browse files
authored
Merge pull request #38577 from xedin/multi-statement-closure-type-checking
[TypeChecker] Incremental multi-statement closure type-checking (disabled by default)
2 parents d8b2118 + f074305 commit 790b549

32 files changed

+2139
-168
lines changed

include/swift/AST/ASTNode.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#ifndef SWIFT_AST_AST_NODE_H
1818
#define SWIFT_AST_AST_NODE_H
1919

20+
#include "llvm/ADT/ArrayRef.h"
2021
#include "llvm/ADT/PointerUnion.h"
2122
#include "swift/Basic/Debug.h"
2223
#include "swift/AST/TypeAlignments.h"
@@ -35,13 +36,18 @@ namespace swift {
3536
class SourceLoc;
3637
class SourceRange;
3738
class ASTWalker;
39+
class StmtConditionElement;
40+
class CaseLabelItem;
3841
enum class ExprKind : uint8_t;
3942
enum class DeclKind : uint8_t;
4043
enum class PatternKind : uint8_t;
4144
enum class StmtKind;
4245

43-
struct ASTNode : public llvm::PointerUnion<Expr *, Stmt *, Decl *, Pattern *,
44-
TypeRepr *> {
46+
using StmtCondition = llvm::MutableArrayRef<StmtConditionElement>;
47+
48+
struct ASTNode
49+
: public llvm::PointerUnion<Expr *, Stmt *, Decl *, Pattern *, TypeRepr *,
50+
StmtCondition *, CaseLabelItem *> {
4551
// Inherit the constructors from PointerUnion.
4652
using PointerUnion::PointerUnion;
4753

include/swift/AST/Decl.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1805,7 +1805,12 @@ class PatternBindingDecl final : public Decl,
18051805
return getPatternList()[i].getPattern();
18061806
}
18071807

1808-
void setPattern(unsigned i, Pattern *Pat, DeclContext *InitContext);
1808+
void setPattern(unsigned i, Pattern *Pat, DeclContext *InitContext,
1809+
bool isFullyValidated = false);
1810+
1811+
bool isFullyValidated(unsigned i) const {
1812+
return getPatternList()[i].isFullyValidated();
1813+
}
18091814

18101815
DeclContext *getInitContext(unsigned i) const {
18111816
return getPatternList()[i].getInitContext();

include/swift/AST/Stmt.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,10 @@ class LabeledConditionalStmt : public LabeledStmt {
579579
StmtCondition getCond() const { return Cond; }
580580
void setCond(StmtCondition e);
581581

582+
/// FIXME: Find a better way to implement this. Allows conditions to be
583+
/// stored in \c ASTNode.
584+
StmtCondition *getCondPointer() { return &Cond; }
585+
582586
static bool classof(const Stmt *S) {
583587
return S->getKind() >= StmtKind::First_LabeledConditionalStmt &&
584588
S->getKind() <= StmtKind::Last_LabeledConditionalStmt;

include/swift/AST/TypeAlignments.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ namespace swift {
6060
class TypeDecl;
6161
class TypeRepr;
6262
class ValueDecl;
63+
class CaseLabelItem;
6364

6465
/// We frequently use three tag bits on all of these types.
6566
constexpr size_t AttrAlignInBits = 3;
@@ -151,6 +152,8 @@ LLVM_DECLARE_TYPE_ALIGNMENT(swift::AttributeBase, swift::AttrAlignInBits)
151152

152153
LLVM_DECLARE_TYPE_ALIGNMENT(swift::TypeRepr, swift::TypeReprAlignInBits)
153154

155+
LLVM_DECLARE_TYPE_ALIGNMENT(swift::CaseLabelItem, swift::PatternAlignInBits);
156+
154157
static_assert(alignof(void*) >= 2, "pointer alignment is too small");
155158

156159
#endif

include/swift/Basic/LangOptions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,10 @@ namespace swift {
668668
/// parameters of closures.
669669
bool EnableOneWayClosureParameters = false;
670670

671+
/// Enable experimental support for type inference through multi-statement
672+
/// closures.
673+
bool EnableMultiStatementClosureInference = false;
674+
671675
/// See \ref FrontendOptions.PrintFullConvention
672676
bool PrintFullConvention = false;
673677
};

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,10 @@ def experimental_one_way_closure_params :
771771
Flag<["-"], "experimental-one-way-closure-params">,
772772
HelpText<"Enable experimental support for one-way closure parameters">;
773773

774+
def experimental_multi_statement_closures :
775+
Flag<["-"], "experimental-multi-statement-closures">,
776+
HelpText<"Enable experimental support for type inference in multi-statement closures">;
777+
774778
def prebuilt_module_cache_path :
775779
Separate<["-"], "prebuilt-module-cache-path">,
776780
HelpText<"Directory of prebuilt modules for loading module interfaces">;

include/swift/Sema/Constraint.h

Lines changed: 100 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@
1818
#ifndef SWIFT_SEMA_CONSTRAINT_H
1919
#define SWIFT_SEMA_CONSTRAINT_H
2020

21+
#include "swift/AST/ASTNode.h"
2122
#include "swift/AST/FunctionRefKind.h"
2223
#include "swift/AST/Identifier.h"
2324
#include "swift/AST/Type.h"
25+
#include "swift/AST/TypeLoc.h"
2426
#include "swift/Basic/Debug.h"
27+
#include "swift/Sema/ConstraintLocator.h"
2528
#include "swift/Sema/OverloadChoice.h"
2629
#include "llvm/ADT/ArrayRef.h"
2730
#include "llvm/ADT/ilist.h"
@@ -47,6 +50,23 @@ class ConstraintLocator;
4750
class ConstraintSystem;
4851
enum class TrailingClosureMatching;
4952

53+
/// Describes contextual type information about a particular element
54+
/// (expression, statement etc.) within a constraint system.
55+
struct ContextualTypeInfo {
56+
TypeLoc typeLoc;
57+
ContextualTypePurpose purpose;
58+
59+
ContextualTypeInfo() : typeLoc(TypeLoc()), purpose(CTP_Unused) {}
60+
61+
ContextualTypeInfo(Type contextualTy, ContextualTypePurpose purpose)
62+
: typeLoc(TypeLoc::withoutLoc(contextualTy)), purpose(purpose) {}
63+
64+
ContextualTypeInfo(TypeLoc typeLoc, ContextualTypePurpose purpose)
65+
: typeLoc(typeLoc), purpose(purpose) {}
66+
67+
Type getType() const { return typeLoc.getType(); }
68+
};
69+
5070
/// Describes the kind of constraint placed on one or more types.
5171
enum class ConstraintKind : char {
5272
/// The two types must be bound to the same type. This is the only
@@ -129,6 +149,9 @@ enum class ConstraintKind : char {
129149
/// A disjunction constraint that specifies that one or more of the
130150
/// stored constraints must hold.
131151
Disjunction,
152+
/// A conjunction constraint that specifies that all of the stored
153+
/// constraints must hold.
154+
Conjunction,
132155
/// The first type is an optional type whose object type is the second
133156
/// type, preserving lvalue-ness.
134157
OptionalObject,
@@ -192,6 +215,10 @@ enum class ConstraintKind : char {
192215
/// inferred from a conversion, so the check is more relax comparing to
193216
/// `ConformsTo`.
194217
TransitivelyConformsTo,
218+
/// Represents an AST node contained in a body of a closure. It has only
219+
/// one type - type variable representing type of a node, other side is
220+
/// the AST node to infer the type for.
221+
ClosureBodyElement,
195222
};
196223

197224
/// Classification of the different kinds of constraints.
@@ -208,7 +235,13 @@ enum class ConstraintClassification : char {
208235
TypeProperty,
209236

210237
/// A disjunction constraint.
211-
Disjunction
238+
Disjunction,
239+
240+
/// A conjunction constraint.
241+
Conjunction,
242+
243+
/// An element of a closure body.
244+
ClosureElement,
212245
};
213246

214247
/// Specifies a restriction on the kind of conversion that should be
@@ -338,6 +371,10 @@ class Constraint final : public llvm::ilist_node<Constraint>,
338371
/// in its disjunction.
339372
unsigned IsFavored : 1;
340373

374+
/// Whether or not this constraint should be solved in isolation from
375+
/// the rest of the constraint system. Currently only applies to conjunctions.
376+
unsigned IsIsolated : 1;
377+
341378
/// The number of type variables referenced by this constraint.
342379
///
343380
/// The type variables themselves are tail-allocated.
@@ -399,6 +436,13 @@ class Constraint final : public llvm::ilist_node<Constraint>,
399436
/// The DC in which the use appears.
400437
DeclContext *UseDC;
401438
} Overload;
439+
440+
struct {
441+
/// The node itself.
442+
ASTNode Element;
443+
/// Contextual information associated with the element (if any).
444+
ContextualTypeInfo Context;
445+
} ClosureElement;
402446
};
403447

404448
/// The locator that describes where in the expression this
@@ -410,7 +454,7 @@ class Constraint final : public llvm::ilist_node<Constraint>,
410454
void *operator new(size_t) = delete;
411455

412456
Constraint(ConstraintKind kind, ArrayRef<Constraint *> constraints,
413-
ConstraintLocator *locator,
457+
bool isIsolated, ConstraintLocator *locator,
414458
SmallPtrSetImpl<TypeVariableType *> &typeVars);
415459

416460
/// Construct a new constraint.
@@ -450,6 +494,11 @@ class Constraint final : public llvm::ilist_node<Constraint>,
450494
ConstraintLocator *locator,
451495
SmallPtrSetImpl<TypeVariableType *> &typeVars);
452496

497+
/// Construct a closure body element constraint.
498+
Constraint(ASTNode node, ContextualTypeInfo context,
499+
ConstraintLocator *locator,
500+
SmallPtrSetImpl<TypeVariableType *> &typeVars);
501+
453502
/// Retrieve the type variables buffer, for internal mutation.
454503
MutableArrayRef<TypeVariableType *> getTypeVariablesBuffer() {
455504
return { getTrailingObjects<TypeVariableType *>(), NumTypeVariables };
@@ -518,12 +567,31 @@ class Constraint final : public llvm::ilist_node<Constraint>,
518567
RememberChoice_t shouldRememberChoice
519568
= ForgetChoice);
520569

570+
/// Create a new conjunction constraint.
571+
///
572+
/// \param isIsolated - Indicates whether given constraint should be
573+
/// solved in isolation from the rest of the constraint system i.e.
574+
/// by removing all of the unrelated type variables and constraints.
575+
static Constraint *
576+
createConjunction(ConstraintSystem &cs, ArrayRef<Constraint *> constraints,
577+
bool isIsolated, ConstraintLocator *locator,
578+
ArrayRef<TypeVariableType *> referencedVars = {});
579+
521580
/// Create a new Applicable Function constraint.
522581
static Constraint *createApplicableFunction(
523582
ConstraintSystem &cs, Type argumentFnType, Type calleeType,
524583
Optional<TrailingClosureMatching> trailingClosureMatching,
525584
ConstraintLocator *locator);
526585

586+
static Constraint *createClosureBodyElement(ConstraintSystem &cs,
587+
ASTNode node,
588+
ConstraintLocator *locator);
589+
590+
static Constraint *createClosureBodyElement(ConstraintSystem &cs,
591+
ASTNode node,
592+
ContextualTypeInfo context,
593+
ConstraintLocator *locator);
594+
527595
/// Determine the kind of constraint.
528596
ConstraintKind getKind() const { return Kind; }
529597

@@ -629,6 +697,12 @@ class Constraint final : public llvm::ilist_node<Constraint>,
629697

630698
case ConstraintKind::Disjunction:
631699
return ConstraintClassification::Disjunction;
700+
701+
case ConstraintKind::Conjunction:
702+
return ConstraintClassification::Conjunction;
703+
704+
case ConstraintKind::ClosureBodyElement:
705+
return ConstraintClassification::ClosureElement;
632706
}
633707

634708
llvm_unreachable("Unhandled ConstraintKind in switch.");
@@ -640,6 +714,9 @@ class Constraint final : public llvm::ilist_node<Constraint>,
640714
case ConstraintKind::Disjunction:
641715
llvm_unreachable("disjunction constraints have no type operands");
642716

717+
case ConstraintKind::Conjunction:
718+
llvm_unreachable("conjunction constraints have no type operands");
719+
643720
case ConstraintKind::BindOverload:
644721
return Overload.First;
645722

@@ -648,6 +725,9 @@ class Constraint final : public llvm::ilist_node<Constraint>,
648725
case ConstraintKind::ValueWitness:
649726
return Member.First;
650727

728+
case ConstraintKind::ClosureBodyElement:
729+
llvm_unreachable("closure body element constraint has no type operands");
730+
651731
default:
652732
return Types.First;
653733
}
@@ -657,7 +737,9 @@ class Constraint final : public llvm::ilist_node<Constraint>,
657737
Type getSecondType() const {
658738
switch (getKind()) {
659739
case ConstraintKind::Disjunction:
740+
case ConstraintKind::Conjunction:
660741
case ConstraintKind::BindOverload:
742+
case ConstraintKind::ClosureBodyElement:
661743
llvm_unreachable("constraint has no second type");
662744

663745
case ConstraintKind::ValueMember:
@@ -710,7 +792,8 @@ class Constraint final : public llvm::ilist_node<Constraint>,
710792

711793
/// Retrieve the set of constraints in a disjunction.
712794
ArrayRef<Constraint *> getNestedConstraints() const {
713-
assert(Kind == ConstraintKind::Disjunction);
795+
assert(Kind == ConstraintKind::Disjunction ||
796+
Kind == ConstraintKind::Conjunction);
714797
return Nested;
715798
}
716799

@@ -735,6 +818,10 @@ class Constraint final : public llvm::ilist_node<Constraint>,
735818
/// e.g. coercion constraint "as X" which forms a disjunction.
736819
bool isExplicitConversion() const;
737820

821+
/// Determine whether this constraint should be solved in isolation
822+
/// from the rest of the constraint system.
823+
bool isIsolated() const { return IsIsolated; }
824+
738825
/// Whether this is a one-way constraint.
739826
bool isOneWayConstraint() const {
740827
return Kind == ConstraintKind::OneWayEqual ||
@@ -761,6 +848,16 @@ class Constraint final : public llvm::ilist_node<Constraint>,
761848
return Member.UseDC;
762849
}
763850

851+
ASTNode getClosureElement() const {
852+
assert(Kind == ConstraintKind::ClosureBodyElement);
853+
return ClosureElement.Element;
854+
}
855+
856+
ContextualTypeInfo getElementContext() const {
857+
assert(Kind == ConstraintKind::ClosureBodyElement);
858+
return ClosureElement.Context;
859+
}
860+
764861
/// For an applicable function constraint, retrieve the trailing closure
765862
/// matching rule.
766863
Optional<TrailingClosureMatching> getTrailingClosureMatching() const;

include/swift/Sema/ConstraintLocator.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,15 @@ enum ContextualTypePurpose : uint8_t {
7272
///< result type.
7373
CTP_Condition, ///< Condition expression of various statements e.g.
7474
///< `if`, `for`, `while` etc.
75+
CTP_CaseStmt, ///< A single case statement associated with a `switch` or
76+
/// a `do-catch` statement. It has to be convertible
77+
/// to a type of a switch subject or an `Error` type.
7578
CTP_ForEachStmt, ///< "expression/sequence" associated with 'for-in' loop
7679
///< is expected to conform to 'Sequence' protocol.
80+
CTP_ForEachSequence, ///< Sequence expression associated with `for-in` loop,
81+
/// this element acts slightly differently compared to
82+
/// \c CTP_ForEachStmt in a sence that it would
83+
/// produce conformance constraints.
7784
CTP_WrappedProperty, ///< Property type expected to match 'wrappedValue' type
7885
CTP_ComposedPropertyWrapper, ///< Composed wrapper type expected to match
7986
///< former 'wrappedValue' type
@@ -953,6 +960,52 @@ class LocatorPathElt::KeyPathType final
953960
}
954961
};
955962

963+
class LocatorPathElt::ClosureBodyElement final
964+
: public StoredPointerElement<void> {
965+
public:
966+
ClosureBodyElement(ASTNode element)
967+
: StoredPointerElement(PathElementKind::ClosureBodyElement,
968+
element.getOpaqueValue()) {
969+
assert(element);
970+
}
971+
972+
ASTNode getElement() const {
973+
// Unfortunately \c getFromOpaqueValue doesn't produce an ASTNode.
974+
auto node = ASTNode::getFromOpaqueValue(getStoredPointer());
975+
if (auto *expr = node.dyn_cast<Expr *>())
976+
return expr;
977+
978+
if (auto *stmt = node.dyn_cast<Stmt *>())
979+
return stmt;
980+
981+
if (auto *decl = node.dyn_cast<Decl *>())
982+
return decl;
983+
984+
if (auto *pattern = node.dyn_cast<Pattern *>())
985+
return pattern;
986+
987+
if (auto *repr = node.dyn_cast<TypeRepr *>())
988+
return repr;
989+
990+
if (auto *cond = node.dyn_cast<StmtCondition *>())
991+
return cond;
992+
993+
if (auto *caseItem = node.dyn_cast<CaseLabelItem *>())
994+
return caseItem;
995+
996+
llvm_unreachable("unhandled ASTNode element kind");
997+
}
998+
999+
Stmt *asStmt() const {
1000+
auto node = ASTNode::getFromOpaqueValue(getStoredPointer());
1001+
return node.get<Stmt *>();
1002+
}
1003+
1004+
static bool classof(const LocatorPathElt *elt) {
1005+
return elt->getKind() == PathElementKind::ClosureBodyElement;
1006+
}
1007+
};
1008+
9561009
namespace details {
9571010
template <typename CustomPathElement>
9581011
class PathElement {

0 commit comments

Comments
 (0)