Skip to content

[ConstraintSystem] Change the order in which we visit disjunctions. #19428

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Sep 27, 2018
Merged
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
213 changes: 213 additions & 0 deletions lib/Sema/CSSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1361,6 +1361,215 @@ static Constraint *selectBestBindingDisjunction(
return firstBindDisjunction;
}

// For a given type, determine if it's either a concrete type, the
// result of converting from a concrete type, or a type variable known
// to conform to other types (or the result of converting from such a
// type variable).
static bool havePotentialTypesOrLiteralConformances(Type ty,
ConstraintSystem &cs) {
llvm::SmallSet<TypeVariableType *, 4> visited;
llvm::SmallVector<Type, 4> worklist;
worklist.push_back(ty);

while (!worklist.empty()) {
auto itemTy = worklist.pop_back_val()->getRValueType();

if (!itemTy->is<TypeVariableType>())
return true;

auto tyvar = itemTy->castTo<TypeVariableType>();
if (cs.getFixedType(tyvar))
return true;

auto *rep = cs.getRepresentative(tyvar);

// FIXME: This can happen when we have two type variables that are
// subtypes of each other. We would ideally merge those type
// variables somewhere.
if (visited.count(rep))
continue;

visited.insert(rep);

// Gather all the constraints involving this type variable, and
// then attempt to trace back through each constraint to see if we
// can reach a concrete type or a literal.

llvm::SetVector<Constraint *> constraints;
cs.getConstraintGraph().gatherConstraints(
rep, constraints, ConstraintGraph::GatheringKind::EquivalenceClass);

for (auto *constraint : constraints) {
switch (constraint->getKind()) {
case ConstraintKind::LiteralConformsTo:
return true;

case ConstraintKind::Defaultable:
assert(!constraint->getSecondType()->is<TypeVariableType>());
return true;

case ConstraintKind::Bind:
case ConstraintKind::Equal: {
auto firstTy = constraint->getFirstType();
auto secondTy = constraint->getSecondType();
if (firstTy->is<TypeVariableType>()) {
auto otherRep =
cs.getRepresentative(firstTy->castTo<TypeVariableType>());
if (otherRep->isEqual(rep))
worklist.push_back(secondTy);
}
if (secondTy->is<TypeVariableType>()) {
auto otherRep =
cs.getRepresentative(secondTy->castTo<TypeVariableType>());
if (otherRep->isEqual(rep))
worklist.push_back(constraint->getFirstType());
}
break;
}

case ConstraintKind::Subtype:
case ConstraintKind::OperatorArgumentConversion:
case ConstraintKind::ArgumentConversion:
case ConstraintKind::Conversion:
case ConstraintKind::BridgingConversion:
case ConstraintKind::BindParam: {
auto secondTy = constraint->getSecondType();
if (secondTy->is<TypeVariableType>()) {
auto otherRep =
cs.getRepresentative(secondTy->castTo<TypeVariableType>());
if (otherRep->isEqual(rep))
worklist.push_back(constraint->getFirstType());
}
break;
}

case ConstraintKind::DynamicTypeOf:
case ConstraintKind::EscapableFunctionOf: {
auto firstTy = constraint->getFirstType();
if (firstTy->is<TypeVariableType>()) {
auto otherRep =
cs.getRepresentative(firstTy->castTo<TypeVariableType>());
if (otherRep->isEqual(rep))
worklist.push_back(constraint->getSecondType());
}
break;
}

case ConstraintKind::OptionalObject: {
// Get the underlying object type.
auto secondTy = constraint->getSecondType();
if (secondTy->is<TypeVariableType>()) {
auto otherRep =
cs.getRepresentative(secondTy->castTo<TypeVariableType>());
if (otherRep->isEqual(rep)) {
// See if we can actually determine what the underlying
// type is.
Type fixedTy;
auto firstTy = constraint->getFirstType();
if (!firstTy->is<TypeVariableType>()) {
fixedTy = firstTy;
} else {
fixedTy =
cs.getFixedType(firstTy->castTo<TypeVariableType>());
}
if (fixedTy && fixedTy->getOptionalObjectType())
worklist.push_back(fixedTy->getOptionalObjectType());
}
}
break;
}

case ConstraintKind::KeyPathApplication:
case ConstraintKind::KeyPath: {
auto firstTy = constraint->getFirstType();
if (firstTy->is<TypeVariableType>()) {
auto otherRep =
cs.getRepresentative(firstTy->castTo<TypeVariableType>());
if (otherRep->isEqual(rep))
worklist.push_back(constraint->getThirdType());
}
break;
}

case ConstraintKind::BindToPointerType:
case ConstraintKind::ValueMember:
case ConstraintKind::UnresolvedValueMember:
case ConstraintKind::Disjunction:
case ConstraintKind::CheckedCast:
case ConstraintKind::OpenedExistentialOf:
case ConstraintKind::ApplicableFunction:
case ConstraintKind::BindOverload:
case ConstraintKind::FunctionInput:
case ConstraintKind::FunctionResult:
case ConstraintKind::SelfObjectOfProtocol:
case ConstraintKind::ConformsTo:
break;
}
}
}

return false;
}

// Check to see if we know something about the types of all arguments
// in the given function type.
static bool haveTypeInformationForAllArguments(AnyFunctionType *fnType,
ConstraintSystem &cs) {
return llvm::all_of(fnType->getParams(), [&](AnyFunctionType::Param param) {
return havePotentialTypesOrLiteralConformances(param.getPlainType(), cs);
});
}

// Given a type variable representing the RHS of an ApplicableFunction
// constraint, attempt to find the disjunction of bind overloads
// associated with it. This may return null in cases where have not
// yet created a disjunction because we need to resolve a base type,
// e.g.: [1].map{ ... } does not have a disjunction until we decide on
// a type for [1].
static Constraint *getUnboundBindOverloadDisjunction(TypeVariableType *tyvar,
ConstraintSystem &cs) {
auto *rep = cs.getRepresentative(tyvar);
assert(!cs.getFixedType(rep));

llvm::SetVector<Constraint *> disjunctions;
cs.getConstraintGraph().gatherConstraints(
rep, disjunctions, ConstraintGraph::GatheringKind::EquivalenceClass,
[](Constraint *match) {
return match->getKind() == ConstraintKind::Disjunction &&
match->getNestedConstraints().front()->getKind() ==
ConstraintKind::BindOverload;
});

if (disjunctions.empty())
return nullptr;

return disjunctions[0];
}

// Find a disjunction associated with an ApplicableFunction constraint
// where we have some information about all of the types of in the
// function application (even if we only know something about what the
// types conform to and not actually a concrete type).
Constraint *ConstraintSystem::selectApplyDisjunction() {
for (auto &constraint : InactiveConstraints) {
if (constraint.getKind() != ConstraintKind::ApplicableFunction)
continue;

auto *applicable = &constraint;
if (haveTypeInformationForAllArguments(
applicable->getFirstType()->castTo<AnyFunctionType>(), *this)) {
auto *tyvar = applicable->getSecondType()->castTo<TypeVariableType>();

// If we have created the disjunction for this apply, find it.
auto *disjunction = getUnboundBindOverloadDisjunction(tyvar, *this);
if (disjunction)
return disjunction;
}
}

return nullptr;
}

void ConstraintSystem::partitionDisjunction(
ArrayRef<Constraint *> Choices, SmallVectorImpl<unsigned> &Ordering,
SmallVectorImpl<unsigned> &PartitionBeginning) {
Expand All @@ -1386,6 +1595,10 @@ Constraint *ConstraintSystem::selectDisjunction() {
if (auto *disjunction = selectBestBindingDisjunction(*this, disjunctions))
return disjunction;

if (getASTContext().isSwiftVersionAtLeast(5))
if (auto *disjunction = selectApplyDisjunction())
return disjunction;

// Pick the disjunction with the smallest number of active choices.
auto minDisjunction =
std::min_element(disjunctions.begin(), disjunctions.end(),
Expand Down
2 changes: 2 additions & 0 deletions lib/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -2986,6 +2986,8 @@ class ConstraintSystem {
/// \returns The selected disjunction.
Constraint *selectDisjunction();

Constraint *selectApplyDisjunction();

bool simplifyForConstraintPropagation();
void collectNeighboringBindOverloadDisjunctions(
llvm::SetVector<Constraint *> &neighbors);
Expand Down
6 changes: 0 additions & 6 deletions test/Misc/expression_too_complex_4.swift

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -swift-version 4
// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -swift-version 5
// REQUIRES: tools-release,no_asserts

let _ = 1 | UInt32(0) << 0 | UInt32(1) << 1 | UInt32(2) << 2 | UInt32(3) << 3 | UInt32(4) << 4
// expected-error@-1 {{reasonable time}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// RUN: %target-typecheck-verify-swift -swift-version 4 -solver-expression-time-threshold=1 -swift-version 5
// REQUIRES: tools-release,no_asserts

func test(_ i: Int, _ j: Int) -> Int {
return 1 + (((i >> 1) + (i >> 2) + (i >> 3) + (i >> 4) << 1) << 1) & 0x40 +
1 + (((i >> 1) + (i >> 2) + (i >> 3) + (i >> 4) << 1) << 1) & 0x40 +
1 + (((i >> 1) + (i >> 2) + (i >> 3) + (i >> 4) << 1) << 1) & 0x40
// expected-error@-1 {{reasonable time}}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -swift-version 5
// REQUIRES: tools-release,no_asserts

_ = [1, 3, 5, 7, 11].filter{ $0 == 1 || $0 == 3 || $0 == 11 || $0 == 1 || $0 == 3 || $0 == 11 } == [ 1, 3, 11 ]
// expected-error@-1 {{reasonable time}}