Skip to content

Commit 97d7470

Browse files
committed
[CSBindings] Avoid inferring duplicate bindings from supertypes
If there is `subtype` relationship between two type variables binding inference algorithm attempts to infer supertype bindings transitively, but it doesn't check whether some of the new types are already included in the set, which leads to duplicate solutions.
1 parent 29268e2 commit 97d7470

File tree

2 files changed

+77
-39
lines changed

2 files changed

+77
-39
lines changed

lib/Sema/CSBindings.cpp

Lines changed: 61 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,66 @@
2121
using namespace swift;
2222
using namespace constraints;
2323

24+
void ConstraintSystem::inferTransitiveSupertypeBindings(
25+
const llvm::SmallDenseMap<TypeVariableType *, PotentialBindings>
26+
&inferredBindings,
27+
PotentialBindings &bindings) {
28+
auto *typeVar = bindings.TypeVar;
29+
30+
llvm::SmallVector<Constraint *, 4> subtypeOf;
31+
// First, let's collect all of the `subtype` constraints associated
32+
// with this type variable.
33+
llvm::copy_if(bindings.Sources, std::back_inserter(subtypeOf),
34+
[&](const Constraint *constraint) -> bool {
35+
if (constraint->getKind() != ConstraintKind::Subtype)
36+
return false;
37+
38+
auto rhs = simplifyType(constraint->getSecondType());
39+
return rhs->getAs<TypeVariableType>() == typeVar;
40+
});
41+
42+
if (subtypeOf.empty())
43+
return;
44+
45+
// We need to make sure that there are no duplicate bindings in the
46+
// set, other we'll produce multiple identical solutions.
47+
llvm::SmallPtrSet<CanType, 4> existingTypes;
48+
for (const auto &binding : bindings.Bindings)
49+
existingTypes.insert(binding.BindingType->getCanonicalType());
50+
51+
for (auto *constraint : subtypeOf) {
52+
auto *tv =
53+
simplifyType(constraint->getFirstType())->getAs<TypeVariableType>();
54+
if (!tv)
55+
continue;
56+
57+
auto relatedBindings = inferredBindings.find(tv);
58+
if (relatedBindings == inferredBindings.end())
59+
continue;
60+
61+
for (auto &binding : relatedBindings->getSecond().Bindings) {
62+
// We need the binding kind for the potential binding to
63+
// either be Exact or Supertypes in order for it to make sense
64+
// to add Supertype bindings based on the relationship between
65+
// our type variables.
66+
if (binding.Kind != AllowedBindingKind::Exact &&
67+
binding.Kind != AllowedBindingKind::Supertypes)
68+
continue;
69+
70+
auto type = binding.BindingType;
71+
72+
if (!existingTypes.insert(type->getCanonicalType()).second)
73+
continue;
74+
75+
if (ConstraintSystem::typeVarOccursInType(typeVar, type))
76+
continue;
77+
78+
bindings.addPotentialBinding(
79+
binding.withSameSource(type, AllowedBindingKind::Supertypes));
80+
}
81+
}
82+
}
83+
2484
Optional<ConstraintSystem::PotentialBindings>
2585
ConstraintSystem::determineBestBindings() {
2686
// Look for potential type variable bindings.
@@ -44,46 +104,8 @@ ConstraintSystem::determineBestBindings() {
44104
continue;
45105

46106
auto &bindings = cachedBindings->getSecond();
47-
// All of the relevant relational constraints associated with
48-
// current type variable should be recored by its potential bindings.
49-
for (auto *constraint : bindings.Sources) {
50-
if (constraint->getKind() != ConstraintKind::Subtype)
51-
continue;
52-
53-
auto lhs = simplifyType(constraint->getFirstType());
54-
auto rhs = simplifyType(constraint->getSecondType());
55-
56-
// We are only interested in 'subtype' constraints which have
57-
// type variable on the left-hand side.
58-
if (rhs->getAs<TypeVariableType>() != typeVar)
59-
continue;
60-
61-
auto *tv = lhs->getAs<TypeVariableType>();
62-
if (!tv)
63-
continue;
64-
65-
auto relatedBindings = cache.find(tv);
66-
if (relatedBindings == cache.end())
67-
continue;
68-
69-
for (auto &binding : relatedBindings->getSecond().Bindings) {
70-
// We need the binding kind for the potential binding to
71-
// either be Exact or Supertypes in order for it to make sense
72-
// to add Supertype bindings based on the relationship between
73-
// our type variables.
74-
if (binding.Kind != AllowedBindingKind::Exact &&
75-
binding.Kind != AllowedBindingKind::Supertypes)
76-
continue;
77-
78-
auto type = binding.BindingType;
79107

80-
if (ConstraintSystem::typeVarOccursInType(typeVar, type))
81-
continue;
82-
83-
bindings.addPotentialBinding(
84-
binding.withSameSource(type, AllowedBindingKind::Supertypes));
85-
}
86-
}
108+
inferTransitiveSupertypeBindings(cache, bindings);
87109

88110
if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) {
89111
auto &log = getASTContext().TypeCheckerDebug->getStream();

lib/Sema/ConstraintSystem.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3761,6 +3761,22 @@ class ConstraintSystem {
37613761
bool &addOptionalSupertypeBindings) const;
37623762
PotentialBindings getPotentialBindings(TypeVariableType *typeVar) const;
37633763

3764+
/// Detect `subtype` relationship between two type variables and
3765+
/// attempt to infer supertype bindings transitively e.g.
3766+
///
3767+
/// Given A <: T1 <: T2 transitively A <: T2
3768+
///
3769+
/// Which gives us a new (superclass A) binding for T2 as well as T1.
3770+
///
3771+
/// \param inferredBindings The set of all bindings inferred for type
3772+
/// variables in the workset.
3773+
/// \param bindings The type variable we aim to infer new supertype
3774+
/// bindings for.
3775+
void inferTransitiveSupertypeBindings(
3776+
const llvm::SmallDenseMap<TypeVariableType *, PotentialBindings>
3777+
&inferredBindings,
3778+
PotentialBindings &bindings);
3779+
37643780
private:
37653781
/// Add a constraint to the constraint system.
37663782
SolutionKind addConstraintImpl(ConstraintKind kind, Type first, Type second,

0 commit comments

Comments
 (0)