Skip to content

Commit 72888ca

Browse files
committed
[ConstraintSystem] NFC: Extract PotentialBindings and auxiliary struct from ConstraintSystem
This opens up a posibility of using `PotentialBindings` in `ConstraintGraphNode` and other places in `ConstraintGraph`.
1 parent b4f28b4 commit 72888ca

File tree

8 files changed

+427
-432
lines changed

8 files changed

+427
-432
lines changed

include/swift/Sema/CSBindings.h

Lines changed: 339 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,32 @@
1818
#ifndef SWIFT_SEMA_CSBINDINGS_H
1919
#define SWIFT_SEMA_CSBINDINGS_H
2020

21+
#include "swift/AST/ASTNode.h"
2122
#include "swift/AST/Type.h"
2223
#include "swift/AST/Types.h"
24+
#include "swift/Basic/Debug.h"
25+
#include "swift/Basic/LLVM.h"
2326
#include "swift/Sema/Constraint.h"
27+
#include "swift/Sema/ConstraintLocator.h"
28+
#include "llvm/ADT/APInt.h"
29+
#include "llvm/ADT/DenseMap.h"
30+
#include "llvm/ADT/MapVector.h"
2431
#include "llvm/ADT/PointerUnion.h"
32+
#include "llvm/ADT/SetVector.h"
33+
#include "llvm/ADT/SmallPtrSet.h"
34+
#include "llvm/ADT/SmallVector.h"
35+
#include "llvm/ADT/TinyPtrVector.h"
36+
#include "llvm/Support/raw_ostream.h"
37+
#include <tuple>
2538

2639
namespace swift {
2740

41+
class DeclContext;
2842
class ProtocolDecl;
2943

3044
namespace constraints {
3145

32-
class ConstraintLocator;
46+
class ConstraintSystem;
3347

3448
namespace inference {
3549

@@ -135,6 +149,330 @@ struct PotentialBinding {
135149
}
136150
};
137151

152+
struct LiteralRequirement {
153+
/// The source of the literal requirement.
154+
Constraint *Source;
155+
/// The default type associated with this literal (if any).
156+
Type DefaultType;
157+
/// Determines whether this literal is a direct requirement
158+
/// of the current type variable.
159+
bool IsDirectRequirement;
160+
161+
/// If the literal is covered by existing type binding,
162+
/// this points to the source of the binding.
163+
mutable Constraint *CoveredBy = nullptr;
164+
165+
LiteralRequirement(Constraint *source, Type defaultTy, bool isDirect)
166+
: Source(source), DefaultType(defaultTy), IsDirectRequirement(isDirect) {}
167+
168+
Constraint *getSource() const { return Source; }
169+
170+
ProtocolDecl *getProtocol() const { return Source->getProtocol(); }
171+
172+
bool isCovered() const { return bool(CoveredBy); }
173+
174+
bool isDirectRequirement() const { return IsDirectRequirement; }
175+
176+
bool hasDefaultType() const { return bool(DefaultType); }
177+
178+
Type getDefaultType() const {
179+
assert(hasDefaultType());
180+
return DefaultType;
181+
}
182+
183+
void setCoveredBy(Constraint *coveredBy) {
184+
assert(!isCovered());
185+
CoveredBy = coveredBy;
186+
}
187+
188+
bool isCoveredBy(Type type, DeclContext *useDC) const;
189+
190+
/// Determines whether literal protocol associated with this
191+
/// meta-information is viable for inclusion as a defaultable binding.
192+
bool viableAsBinding() const { return !isCovered() && hasDefaultType(); }
193+
};
194+
195+
struct PotentialBindings {
196+
using BindingScore =
197+
std::tuple<bool, bool, bool, bool, bool, unsigned char, int>;
198+
199+
/// The constraint system this type variable and its bindings belong to.
200+
ConstraintSystem &CS;
201+
202+
TypeVariableType *TypeVar;
203+
204+
/// The set of potential bindings.
205+
llvm::SmallSetVector<PotentialBinding, 4> Bindings;
206+
207+
/// The set of protocol requirements placed on this type variable.
208+
llvm::SmallVector<Constraint *, 4> Protocols;
209+
210+
/// The set of transitive protocol requirements inferred through
211+
/// subtype/conversion/equivalence relations with other type variables.
212+
llvm::Optional<llvm::SmallPtrSet<Constraint *, 4>> TransitiveProtocols;
213+
214+
/// The set of unique literal protocol requirements placed on this
215+
/// type variable or inferred transitively through subtype chains.
216+
///
217+
/// Note that ordering is important when it comes to bindings, we'd
218+
/// like to add any "direct" default types first to attempt them
219+
/// before transitive ones.
220+
llvm::SmallMapVector<ProtocolDecl *, LiteralRequirement, 2> Literals;
221+
222+
/// The set of constraints which would be used to infer default types.
223+
llvm::SmallDenseMap<CanType, Constraint *, 2> Defaults;
224+
225+
/// The set of constraints which delay attempting this type variable.
226+
llvm::TinyPtrVector<Constraint *> DelayedBy;
227+
228+
/// The set of type variables adjacent to the current one.
229+
///
230+
/// Type variables contained here are either related through the
231+
/// bindings (contained in the binding type e.g. `Foo<$T0>`), or
232+
/// reachable through subtype/conversion relationship e.g.
233+
/// `$T0 subtype of $T1` or `$T0 arg conversion $T1`.
234+
llvm::SmallPtrSet<TypeVariableType *, 2> AdjacentVars;
235+
236+
ASTNode AssociatedCodeCompletionToken = ASTNode();
237+
238+
/// A set of all not-yet-resolved type variables this type variable
239+
/// is a subtype of, supertype of or is equivalent to. This is used
240+
/// to determine ordering inside of a chain of subtypes to help infer
241+
/// transitive bindings and protocol requirements.
242+
llvm::SmallMapVector<TypeVariableType *, Constraint *, 4> SubtypeOf;
243+
llvm::SmallMapVector<TypeVariableType *, Constraint *, 4> SupertypeOf;
244+
llvm::SmallMapVector<TypeVariableType *, Constraint *, 4> EquivalentTo;
245+
246+
PotentialBindings(ConstraintSystem &cs, TypeVariableType *typeVar)
247+
: CS(cs), TypeVar(typeVar) {}
248+
249+
/// Determine whether the set of bindings is non-empty.
250+
explicit operator bool() const {
251+
return !Bindings.empty() || getNumViableLiteralBindings() > 0 ||
252+
!Defaults.empty() || isDirectHole();
253+
}
254+
255+
/// Determines whether this type variable could be `nil`,
256+
/// which means that all of its bindings should be optional.
257+
bool canBeNil() const;
258+
259+
/// Determine whether attempting this type variable should be
260+
/// delayed until the rest of the constraint system is considered
261+
/// "fully bound" meaning constraints, which affect completeness
262+
/// of the binding set, for this type variable such as - member
263+
/// constraint, disjunction, function application etc. - are simplified.
264+
///
265+
/// Note that in some situations i.e. when there are no more
266+
/// disjunctions or type variables left to attempt, it's still
267+
/// okay to attempt "delayed" type variable to make forward progress.
268+
bool isDelayed() const;
269+
270+
/// Whether the bindings of this type involve other type variables,
271+
/// or the type variable itself is adjacent to other type variables
272+
/// that could become valid bindings in the future.
273+
bool involvesTypeVariables() const;
274+
275+
/// Whether the bindings represent (potentially) incomplete set,
276+
/// there is no way to say with absolute certainty if that's the
277+
/// case, but that could happen when certain constraints like
278+
/// `bind param` are present in the system.
279+
bool isPotentiallyIncomplete() const;
280+
281+
/// If this type variable doesn't have any viable bindings, or
282+
/// if there is only one binding and it's a hole type, consider
283+
/// this type variable to be a hole in a constraint system
284+
/// regardless of where hole type originated.
285+
bool isHole() const {
286+
if (isDirectHole())
287+
return true;
288+
289+
if (Bindings.size() != 1)
290+
return false;
291+
292+
const auto &binding = Bindings.front();
293+
return binding.BindingType->is<HoleType>();
294+
}
295+
296+
/// Determines whether the only possible binding for this type variable
297+
/// would be a hole type. This is different from `isHole` method because
298+
/// type variable could also acquire a hole type transitively if one
299+
/// of the type variables in its subtype/equivalence chain has been
300+
/// bound to a hole type.
301+
bool isDirectHole() const;
302+
303+
/// Determine if the bindings only constrain the type variable from above
304+
/// with an existential type; such a binding is not very helpful because
305+
/// it's impossible to enumerate the existential type's subtypes.
306+
bool isSubtypeOfExistentialType() const {
307+
if (Bindings.empty())
308+
return false;
309+
310+
return llvm::all_of(Bindings, [](const PotentialBinding &binding) {
311+
return binding.BindingType->isExistentialType() &&
312+
binding.Kind == AllowedBindingKind::Subtypes;
313+
});
314+
}
315+
316+
unsigned getNumViableLiteralBindings() const;
317+
318+
unsigned getNumViableDefaultableBindings() const {
319+
if (isDirectHole())
320+
return 1;
321+
322+
auto numDefaultable = llvm::count_if(
323+
Defaults, [](const std::pair<CanType, Constraint *> &entry) {
324+
return entry.second->getKind() == ConstraintKind::Defaultable;
325+
});
326+
327+
// Short-circuit unviable checks if there are no defaultable bindings.
328+
if (numDefaultable == 0)
329+
return 0;
330+
331+
// Defaultable constraint is unviable if its type is covered by
332+
// an existing direct or transitive binding.
333+
auto unviable =
334+
llvm::count_if(Bindings, [&](const PotentialBinding &binding) {
335+
auto type = binding.BindingType->getCanonicalType();
336+
auto def = Defaults.find(type);
337+
return def != Defaults.end()
338+
? def->second->getKind() == ConstraintKind::Defaultable
339+
: false;
340+
});
341+
342+
assert(numDefaultable >= unviable);
343+
return numDefaultable - unviable;
344+
}
345+
346+
static BindingScore formBindingScore(const PotentialBindings &b);
347+
348+
/// Compare two sets of bindings, where \c x < y indicates that
349+
/// \c x is a better set of bindings that \c y.
350+
friend bool operator<(const PotentialBindings &x,
351+
const PotentialBindings &y) {
352+
auto xScore = formBindingScore(x);
353+
auto yScore = formBindingScore(y);
354+
355+
if (xScore < yScore)
356+
return true;
357+
358+
if (yScore < xScore)
359+
return false;
360+
361+
auto xDefaults = x.getNumViableDefaultableBindings();
362+
auto yDefaults = y.getNumViableDefaultableBindings();
363+
364+
// If there is a difference in number of default types,
365+
// prioritize bindings with fewer of them.
366+
if (xDefaults != yDefaults)
367+
return xDefaults < yDefaults;
368+
369+
// If neither type variable is a "hole" let's check whether
370+
// there is a subtype relationship between them and prefer
371+
// type variable which represents superclass first in order
372+
// for "subtype" type variable to attempt more bindings later.
373+
// This is required because algorithm can't currently infer
374+
// bindings for subtype transitively through superclass ones.
375+
if (!(std::get<0>(xScore) && std::get<0>(yScore))) {
376+
if (x.isSubtypeOf(y.TypeVar))
377+
return false;
378+
379+
if (y.isSubtypeOf(x.TypeVar))
380+
return true;
381+
}
382+
383+
// As a last resort, let's check if the bindings are
384+
// potentially incomplete, and if so, let's de-prioritize them.
385+
return x.isPotentiallyIncomplete() < y.isPotentiallyIncomplete();
386+
}
387+
388+
LiteralBindingKind getLiteralKind() const;
389+
390+
void addDefault(Constraint *constraint);
391+
392+
void addLiteral(Constraint *constraint);
393+
394+
/// Determines whether the given literal protocol is "covered"
395+
/// by the given binding - type of the binding could either be
396+
/// equal (in canonical sense) to the protocol's default type,
397+
/// or conform to a protocol.
398+
///
399+
/// \param literal The literal protocol requirement to check.
400+
///
401+
/// \param binding The binding to check for coverage.
402+
///
403+
/// \param canBeNil The flag that determines whether given type
404+
/// variable requires all of its bindings to be optional.
405+
///
406+
/// \returns a pair of bool and a type:
407+
/// - bool, true if binding covers given literal protocol;
408+
/// - type, non-null if binding type has to be adjusted
409+
/// to cover given literal protocol;
410+
std::pair<bool, Type> isLiteralCoveredBy(const LiteralRequirement &literal,
411+
const PotentialBinding &binding,
412+
bool canBeNil) const;
413+
414+
/// Add a potential binding to the list of bindings,
415+
/// coalescing supertype bounds when we are able to compute the meet.
416+
///
417+
/// \returns true if this binding has been added to the set,
418+
/// false otherwise (e.g. because binding with this type is
419+
/// already in the set).
420+
bool addPotentialBinding(PotentialBinding binding, bool allowJoinMeet = true);
421+
422+
/// Check if this binding is viable for inclusion in the set.
423+
bool isViable(PotentialBinding &binding) const;
424+
425+
bool isGenericParameter() const;
426+
427+
bool isSubtypeOf(TypeVariableType *typeVar) const {
428+
auto result = SubtypeOf.find(typeVar);
429+
if (result == SubtypeOf.end())
430+
return false;
431+
432+
auto *constraint = result->second;
433+
return constraint->getKind() == ConstraintKind::Subtype;
434+
}
435+
436+
/// Check if this binding is favored over a disjunction e.g.
437+
/// if it has only concrete types or would resolve a closure.
438+
bool favoredOverDisjunction(Constraint *disjunction) const;
439+
440+
private:
441+
/// Detect `subtype` relationship between two type variables and
442+
/// attempt to infer supertype bindings transitively e.g.
443+
///
444+
/// Given A <: T1 <: T2 transitively A <: T2
445+
///
446+
/// Which gives us a new (superclass A) binding for T2 as well as T1.
447+
///
448+
/// \param inferredBindings The set of all bindings inferred for type
449+
/// variables in the workset.
450+
void inferTransitiveBindings(
451+
const llvm::SmallDenseMap<TypeVariableType *, PotentialBindings>
452+
&inferredBindings);
453+
454+
/// Detect subtype, conversion or equivalence relationship
455+
/// between two type variables and attempt to propagate protocol
456+
/// requirements down the subtype or equivalence chain.
457+
void inferTransitiveProtocolRequirements(
458+
llvm::SmallDenseMap<TypeVariableType *, PotentialBindings>
459+
&inferredBindings);
460+
461+
public:
462+
bool infer(Constraint *constraint);
463+
464+
/// Finalize binding computation for this type variable by
465+
/// inferring bindings from context e.g. transitive bindings.
466+
void finalize(llvm::SmallDenseMap<TypeVariableType *, PotentialBindings>
467+
&inferredBindings);
468+
469+
void dump(llvm::raw_ostream &out,
470+
unsigned indent = 0) const LLVM_ATTRIBUTE_USED;
471+
472+
void dump(TypeVariableType *typeVar, llvm::raw_ostream &out,
473+
unsigned indent = 0) const LLVM_ATTRIBUTE_USED;
474+
};
475+
138476
} // end namespace inference
139477

140478
} // end namespace constraints

0 commit comments

Comments
 (0)