Skip to content

Commit 2bcf555

Browse files
committed
[Constraint solver] Introduce a *trivial* 'meet' operation for types.
Stub out a simplistic 'meet' operation for types that currently only handles finding the meet of two class types. Use it to optimize the constraint solver ever so slightly: when we see a type variable whose constraints state that it is a supertype of two different concrete types, we attempt to compute their meet and, if we find it, only produce one potential binding that is the meet itself. Note that this is an extremely poor implementation of this concept that is meant to address a specific regression being introduced by the defaulting of collection literal element types. A real implementation would, at the very least: * Implement a proper 'meet' that covers all subtyping in the language. * Distinguish between "I don't know if there is a meet" and "I am absolutely certain that there is no meet". * Collapse the constraints themselves to reduce the number of constraints in the system, rather than just the number of type variable bindings we attempt.
1 parent f3af4ae commit 2bcf555

File tree

4 files changed

+134
-9
lines changed

4 files changed

+134
-9
lines changed

include/swift/AST/Type.h

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,30 @@ class Type {
183183

184184
/// Get the canonical type, or return null if the type is null.
185185
CanType getCanonicalTypeOrNull() const; // in Types.h
186-
186+
187+
/// Computes the meet between two types.
188+
///
189+
/// The meet of two types is the most specific type that is a supertype of
190+
/// both \c type1 and \c type2. For example, given a simple class hierarchy as
191+
/// follows:
192+
///
193+
/// \code
194+
/// class A { }
195+
/// class B : A { }
196+
/// class C : A { }
197+
/// class D { }
198+
/// \endcode
199+
///
200+
/// The meet of B and C is A, the meet of A and B is A. However, there is no
201+
/// meet of D and A (or D and B, or D and C) because there is no common
202+
/// superclass. One would have to jump to an existential (e.g., \c AnyObject)
203+
/// to find a common type.
204+
///
205+
/// \returns the meet of the two types, if there is a concrete type that can
206+
/// express the meet, or a null type if the only meet would be a more-general
207+
/// existential type (e.g., \c Any).
208+
static Type meet(Type type1, Type type2);
209+
187210
private:
188211
// Direct comparison is disabled for types, because they may not be canonical.
189212
void operator==(Type T) const = delete;
@@ -432,6 +455,7 @@ class CanGenericSignature {
432455
return Signature;
433456
}
434457
};
458+
435459
} // end namespace swift
436460

437461
namespace llvm {

lib/AST/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ add_swift_library(swiftAST STATIC
3838
SourceEntityWalker.cpp
3939
Substitution.cpp
4040
Type.cpp
41+
TypeJoinMeet.cpp
4142
TypeRefinementContext.cpp
4243
TypeRepr.cpp
4344
TypeWalker.cpp

lib/AST/TypeJoinMeet.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//===--- TypeJoinMeet.cpp - Swift Type "Join" and "Meet" -----------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file implements the "meet" operation for types (and, eventually,
14+
// "join").
15+
//
16+
//===----------------------------------------------------------------------===//
17+
#include "swift/AST/ASTContext.h"
18+
#include "swift/AST/Type.h"
19+
#include "swift/AST/Types.h"
20+
#include "llvm/ADT/SmallPtrSet.h"
21+
using namespace swift;
22+
23+
Type Type::meet(Type type1, Type type2) {
24+
assert(!type1->hasTypeVariable() && !type2->hasTypeVariable() &&
25+
"Cannot compute meet of types involving type variables");
26+
27+
// FIXME: This algorithm is woefully incomplete, and is only currently used
28+
// for optimizing away extra exploratory work in the constraint solver. It
29+
// should eventually encompass all of the subtyping rules of the language.
30+
31+
// If the types are equivalent, the meet is obvious.
32+
if (type1->isEqual(type2))
33+
return type1;
34+
35+
// If both are class types or opaque types that potentially have superclasses,
36+
// find the common superclass.
37+
if (type1->mayHaveSuperclass() && type2->mayHaveSuperclass()) {
38+
ASTContext &ctx = type1->getASTContext();
39+
LazyResolver *resolver = ctx.getLazyResolver();
40+
41+
/// Walk the superclasses of type1 looking for type2. Record them for our
42+
/// second step.
43+
llvm::SmallPtrSet<CanType, 8> superclassesOfType1;
44+
CanType canType2 = type2->getCanonicalType();
45+
for (Type super1 = type1; super1; super1 = super1->getSuperclass(resolver)){
46+
CanType canSuper1 = super1->getCanonicalType();
47+
48+
// If we have found the second type, we're done.
49+
if (canSuper1 == canType2) return super1;
50+
51+
superclassesOfType1.insert(canSuper1);
52+
}
53+
54+
// Look through the superclasses of type2 to determine if any were also
55+
// superclasses of type1.
56+
for (Type super2 = type2; super2; super2 = super2->getSuperclass(resolver)){
57+
CanType canSuper2 = super2->getCanonicalType();
58+
59+
// If we found the first type, we're done.
60+
if (superclassesOfType1.count(canSuper2)) return super2;
61+
}
62+
63+
// There is no common superclass; we're done.
64+
return nullptr;
65+
}
66+
67+
// The meet can only be an existential.
68+
return nullptr;
69+
}
70+

lib/Sema/CSSolver.cpp

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -692,8 +692,38 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
692692
llvm::SmallPtrSet<Constraint *, 4> visitedConstraints;
693693
cs.getConstraintGraph().gatherConstraints(typeVar, constraints);
694694

695-
// Consider each of the constraints related to this type variable.
696695
PotentialBindings result;
696+
Optional<unsigned> lastSupertypeIndex;
697+
698+
// Local function to add a potential binding to the list of bindings,
699+
// coalescing supertype bounds when we are able to compute the meet.
700+
auto addPotentialBinding = [&](PotentialBinding binding) {
701+
// If this is a non-defaulted supertype binding, check whether we can
702+
// combine it with another supertype binding by computing the 'meet' of the
703+
// types.
704+
if (binding.Kind == AllowedBindingKind::Supertypes &&
705+
!binding.BindingType->hasTypeVariable() &&
706+
!binding.DefaultedProtocol &&
707+
!binding.IsDefaultableBinding) {
708+
if (lastSupertypeIndex) {
709+
// Can we compute a meet?
710+
auto &lastBinding = result.Bindings[*lastSupertypeIndex];
711+
if (auto meet =
712+
Type::meet(lastBinding.BindingType, binding.BindingType)) {
713+
// Replace the last supertype binding with the meet. We're done.
714+
lastBinding.BindingType = meet;
715+
return;
716+
}
717+
}
718+
719+
// Record this as the most recent supertype index.
720+
lastSupertypeIndex = result.Bindings.size();
721+
}
722+
723+
result.Bindings.push_back(std::move(binding));
724+
};
725+
726+
// Consider each of the constraints related to this type variable.
697727
llvm::SmallPtrSet<CanType, 4> exactTypes;
698728
llvm::SmallPtrSet<ProtocolDecl *, 4> literalProtocols;
699729
SmallVector<Constraint *, 2> defaultableConstraints;
@@ -767,8 +797,8 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
767797
continue;
768798

769799
result.foundLiteralBinding(constraint->getProtocol());
770-
result.Bindings.push_back({defaultType, AllowedBindingKind::Subtypes,
771-
constraint->getProtocol()});
800+
addPotentialBinding({defaultType, AllowedBindingKind::Subtypes,
801+
constraint->getProtocol()});
772802
continue;
773803
}
774804

@@ -794,8 +824,8 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
794824
if (!matched) {
795825
result.foundLiteralBinding(constraint->getProtocol());
796826
exactTypes.insert(defaultType->getCanonicalType());
797-
result.Bindings.push_back({defaultType, AllowedBindingKind::Subtypes,
798-
constraint->getProtocol()});
827+
addPotentialBinding({defaultType, AllowedBindingKind::Subtypes,
828+
constraint->getProtocol()});
799829
}
800830

801831
continue;
@@ -935,10 +965,10 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
935965
}
936966

937967
if (exactTypes.insert(type->getCanonicalType()).second)
938-
result.Bindings.push_back({type, kind, None});
968+
addPotentialBinding({type, kind, None});
939969
if (alternateType &&
940970
exactTypes.insert(alternateType->getCanonicalType()).second)
941-
result.Bindings.push_back({alternateType, kind, None});
971+
addPotentialBinding({alternateType, kind, None});
942972
}
943973

944974
// If we have any literal constraints, check whether there is already a
@@ -1016,7 +1046,7 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
10161046
continue;
10171047

10181048
++result.NumDefaultableBindings;
1019-
result.Bindings.push_back({type, AllowedBindingKind::Exact, None, true});
1049+
addPotentialBinding({type, AllowedBindingKind::Exact, None, true});
10201050
}
10211051

10221052
// Determine if the bindings only constrain the type variable from above with

0 commit comments

Comments
 (0)