Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 45 additions & 25 deletions include/swift/Sema/CSBindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ struct PotentialBinding {
};

struct LiteralRequirement {
/// The literal protocol.
ProtocolDecl *Protocol;
/// The source of the literal requirement.
Constraint *Source;
/// The default type associated with this literal (if any).
Expand All @@ -164,17 +166,23 @@ struct LiteralRequirement {
/// this points to the source of the binding.
mutable Constraint *CoveredBy = nullptr;

LiteralRequirement(Constraint *source, Type defaultTy, bool isDirect)
: Source(source), DefaultType(defaultTy), IsDirectRequirement(isDirect) {}
LiteralRequirement(ProtocolDecl *protocol, Constraint *source,
Type defaultTy, bool isDirect)
: Protocol(protocol), Source(source), DefaultType(defaultTy),
IsDirectRequirement(isDirect) {}

Constraint *getSource() const { return Source; }

ProtocolDecl *getProtocol() const { return Source->getProtocol(); }
ProtocolDecl *getProtocol() const { return Protocol; }

bool isCovered() const { return bool(CoveredBy); }

bool isDirectRequirement() const { return IsDirectRequirement; }

void setDirectRequirement(bool isDirectRequirement) {
IsDirectRequirement = isDirectRequirement;
}

bool hasDefaultType() const { return bool(DefaultType); }

Type getDefaultType() const {
Expand Down Expand Up @@ -241,6 +249,16 @@ struct PotentialBindings {
llvm::SmallVector<std::pair<TypeVariableType *, Constraint *>, 4> SupertypeOf;
llvm::SmallVector<std::pair<TypeVariableType *, Constraint *>, 4> EquivalentTo;

/// The set of protocol conformance requirements imposed on this type variable.
llvm::SmallVector<Constraint *, 4> Protocols;

/// The set of unique literal protocol requirements placed on this
/// type variable.
llvm::SmallVector<LiteralRequirement, 2> Literals;

/// The set of fallback constraints imposed on this type variable.
llvm::SmallVector<Constraint *, 2> Defaults;

ASTNode AssociatedCodeCompletionToken = ASTNode();

/// Add a potential binding to the list of bindings,
Expand All @@ -256,7 +274,14 @@ struct PotentialBindings {
});
}

private:
ArrayRef<Constraint *> getConformanceRequirements() const {
return Protocols;
}

void inferFromLiteral(ConstraintSystem &CS,
TypeVariableType *TypeVar,
Constraint *literal);

/// Attempt to infer a new binding and other useful information
/// (i.e. whether bindings should be delayed) from the given
/// relational constraint.
Expand All @@ -265,7 +290,6 @@ struct PotentialBindings {
TypeVariableType *TypeVar,
Constraint *constraint);

public:
void infer(ConstraintSystem &CS,
TypeVariableType *TypeVar,
Constraint *constraint);
Expand Down Expand Up @@ -365,18 +389,15 @@ class BindingSet {
public:
swift::SmallSetVector<PotentialBinding, 4> Bindings;

/// The set of protocol conformance requirements placed on this type variable.
llvm::SmallVector<Constraint *, 4> Protocols;

/// The set of unique literal protocol requirements placed on this
/// type variable or inferred transitively through subtype chains.
///
/// Note that ordering is important when it comes to bindings, we'd
/// like to add any "direct" default types first to attempt them
/// before transitive ones.
llvm::SmallMapVector<ProtocolDecl *, LiteralRequirement, 2> Literals;
llvm::SmallVector<LiteralRequirement, 2> Literals;

llvm::SmallDenseMap<CanType, Constraint *, 2> Defaults;
llvm::SmallVector<Constraint *, 2> Defaults;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this not make things slower when there duplicate defaultable constraints?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point, do you mean the same constraint getting added multiple times, or multiple constraints with the same second type? I'll check for both and see if it can happen

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could be both I think, this is the worst case when multiple type variables form an equivalence class and each has a literal requirement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added an assertion to addDefault() to check for the same constraint being added twice. It doesn't trigger when building the stdlib or validation tests, but building swift-build trips it. I'll try to reduce a test case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh:

let s = [[Int: Set<String>]]()                                                                         
let x = s.reduce([:], { x, y in x.merging(y, uniquingKeysWith: +) })    

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are merging as aggressively anymore since the hacks are disabled, i.e. all type variables in 1 + 2 + 3 + … used to be equated by optimizeConstraints.


/// The set of transitive protocol requirements inferred through
/// subtype/conversion/equivalence relations with other type variables.
Expand All @@ -389,8 +410,6 @@ class BindingSet {

BindingSet(const BindingSet &other) = delete;

ConstraintSystem &getConstraintSystem() const { return CS; }

TypeVariableType *getTypeVariable() const { return TypeVar; }

/// Check whether this binding set belongs to a type variable
Expand Down Expand Up @@ -457,7 +476,7 @@ class BindingSet {
// Literal requirements always result in a subtype/supertype
// relationship to a concrete type.
if (llvm::any_of(Literals, [](const auto &literal) {
return literal.second.viableAsBinding();
return literal.viableAsBinding();
}))
return false;

Expand Down Expand Up @@ -494,19 +513,15 @@ class BindingSet {
return hasViableBindings() || isDirectHole();
}

ArrayRef<Constraint *> getConformanceRequirements() const {
return Protocols;
}

unsigned getNumViableLiteralBindings() const;

unsigned getNumViableDefaultableBindings() const {
if (isDirectHole())
return 1;

auto numDefaultable = llvm::count_if(
Defaults, [](const std::pair<CanType, Constraint *> &entry) {
return entry.second->getKind() == ConstraintKind::Defaultable;
Defaults, [](Constraint *constraint) {
return constraint->getKind() == ConstraintKind::Defaultable;
});

// Short-circuit unviable checks if there are no defaultable bindings.
Expand All @@ -518,10 +533,12 @@ class BindingSet {
auto unviable =
llvm::count_if(Bindings, [&](const PotentialBinding &binding) {
auto type = binding.BindingType->getCanonicalType();
auto def = Defaults.find(type);
return def != Defaults.end()
? def->second->getKind() == ConstraintKind::Defaultable
: false;
for (auto *constraint : Defaults) {
if (constraint->getSecondType()->isEqual(type)) {
return constraint->getKind() == ConstraintKind::Defaultable;
}
}
return false;
});

assert(numDefaultable >= unviable);
Expand Down Expand Up @@ -572,6 +589,11 @@ class BindingSet {
/// requirements down the subtype or equivalence chain.
void inferTransitiveProtocolRequirements();

/// Try to coalesce integer and floating point literal protocols
/// if they appear together because the only possible default type that
/// could satisfy both requirements is `Double`.
void coalesceIntegerAndFloatLiteralRequirements();

/// Check whether the given binding set covers any of the literal protocols
/// associated with this type variable. The idea is that if a type variable
/// has a binding like Int and also it has a conformance requirement to
Expand Down Expand Up @@ -607,8 +629,6 @@ class BindingSet {
/// checking.
void addBinding(PotentialBinding binding, bool isTransitive);

void addLiteralRequirement(Constraint *literal);

void addDefault(Constraint *constraint);

StringRef getLiteralBindingKind(LiteralBindingKind K) const {
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Sema/CSTrail.def
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@ GRAPH_NODE_CHANGE(AddedConstraint)
GRAPH_NODE_CHANGE(RemovedConstraint)
GRAPH_NODE_CHANGE(InferredBindings)
GRAPH_NODE_CHANGE(RetractedBindings)
GRAPH_NODE_CHANGE(RetractedLiteral)
GRAPH_NODE_CHANGE(RetractedDelayedBy)
GRAPH_NODE_CHANGE(RetractedProtocol)
GRAPH_NODE_CHANGE(RetractedDefault)

BINDING_RELATION_CHANGE(RetractedAdjacentVar)
BINDING_RELATION_CHANGE(RetractedSubtypeOf)
Expand Down
4 changes: 3 additions & 1 deletion include/swift/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -6196,7 +6196,9 @@ class TypeVarBindingProducer : public BindingProducer<TypeVariableBinding> {
public:
using Element = TypeVariableBinding;

TypeVarBindingProducer(const BindingSet &bindings);
TypeVarBindingProducer(ConstraintSystem &cs,
TypeVariableType *typeVar,
const BindingSet &bindings);

/// Retrieve a set of bindings available in the current state.
ArrayRef<Binding> getCurrentBindings() const { return Bindings; }
Expand Down
Loading