Skip to content

Commit

Permalink
Merge pull request #15489 from xedin/clean-literal-bindings
Browse files Browse the repository at this point in the history
[CSBindings] Cleanup literal binding inference
  • Loading branch information
xedin authored Mar 28, 2018
2 parents f715282 + df1cb55 commit 31907d4
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 42 deletions.
78 changes: 43 additions & 35 deletions lib/Sema/CSBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,10 @@ void ConstraintSystem::PotentialBindings::addPotentialBinding(
// check whether we can combine it with another
// supertype binding by computing the 'join' of the types.
if (binding.Kind == AllowedBindingKind::Supertypes &&
!binding.BindingType->hasTypeVariable() && !binding.DefaultedProtocol &&
!binding.isDefaultableBinding() && allowJoinMeet) {
!binding.BindingType->hasTypeVariable() &&
!binding.BindingType->hasUnboundGenericType() &&
!binding.DefaultedProtocol && !binding.isDefaultableBinding() &&
allowJoinMeet) {
if (lastSupertypeIndex) {
auto &lastBinding = Bindings[*lastSupertypeIndex];
auto lastType = lastBinding.BindingType->getWithoutSpecifierType();
Expand All @@ -194,6 +196,9 @@ void ConstraintSystem::PotentialBindings::addPotentialBinding(
lastSupertypeIndex = Bindings.size();
}

if (auto *literalProtocol = binding.DefaultedProtocol)
foundLiteralBinding(literalProtocol);

Bindings.push_back(std::move(binding));
}

Expand Down Expand Up @@ -357,8 +362,8 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) {

// Consider each of the constraints related to this type variable.
llvm::SmallPtrSet<CanType, 4> exactTypes;
llvm::SmallPtrSet<ProtocolDecl *, 4> literalProtocols;
SmallVector<Constraint *, 2> defaultableConstraints;
SmallVector<PotentialBinding, 4> literalBindings;
bool addOptionalSupertypeBindings = false;
auto &tc = getTypeChecker();
bool hasNonDependentMemberRelationalConstraints = false;
Expand Down Expand Up @@ -478,19 +483,16 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) {
if (!defaultType)
continue;

// Note that we have a literal constraint with this protocol.
literalProtocols.insert(constraint->getProtocol());
hasNonDependentMemberRelationalConstraints = true;

// Handle unspecialized types directly.
if (!defaultType->hasUnboundGenericType()) {
if (!exactTypes.insert(defaultType->getCanonicalType()).second)
continue;

result.foundLiteralBinding(constraint->getProtocol());
result.addPotentialBinding({defaultType, AllowedBindingKind::Subtypes,
constraint->getKind(),
constraint->getProtocol()});
literalBindings.push_back({defaultType, AllowedBindingKind::Subtypes,
constraint->getKind(),
constraint->getProtocol()});
continue;
}

Expand All @@ -514,11 +516,10 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) {
}

if (!matched) {
result.foundLiteralBinding(constraint->getProtocol());
exactTypes.insert(defaultType->getCanonicalType());
result.addPotentialBinding({defaultType, AllowedBindingKind::Subtypes,
constraint->getKind(),
constraint->getProtocol()});
literalBindings.push_back({defaultType, AllowedBindingKind::Subtypes,
constraint->getKind(),
constraint->getProtocol()});
}

break;
Expand Down Expand Up @@ -575,14 +576,11 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) {
// binding that provides a type that conforms to that literal protocol. In
// such cases, remove the default binding suggestion because the existing
// suggestion is better.
if (!literalProtocols.empty()) {
if (!literalBindings.empty()) {
SmallPtrSet<ProtocolDecl *, 5> coveredLiteralProtocols;
for (auto &binding : result.Bindings) {
// Skip defaulted-protocol constraints.
if (binding.DefaultedProtocol)
continue;

Type testType;

switch (binding.Kind) {
case AllowedBindingKind::Exact:
testType = binding.BindingType;
Expand All @@ -594,16 +592,32 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) {
break;
}

// Attempting to check conformance of the type variable,
// or unresolved type is invalid since it would result
// in lose of viable literal bindings because that check
// always returns trivial conformance.
if (testType->isTypeVariableOrMember() || testType->is<UnresolvedType>())
continue;

// Check each non-covered literal protocol to determine which ones
// might be covered by non-defaulted bindings.
bool updatedBindingType = false;
for (auto proto : literalProtocols) {
for (auto &literalBinding : literalBindings) {
auto *protocol = literalBinding.DefaultedProtocol;

assert(protocol);

// Has already been covered by one of the bindings.
if (coveredLiteralProtocols.count(protocol))
continue;

do {
// If the type conforms to this protocol, we're covered.
if (tc.conformsToProtocol(
testType, proto, DC,
(ConformanceCheckFlags::InExpression|
ConformanceCheckFlags::SkipConditionalRequirements))) {
coveredLiteralProtocols.insert(proto);
testType, protocol, DC,
(ConformanceCheckFlags::InExpression |
ConformanceCheckFlags::SkipConditionalRequirements))) {
coveredLiteralProtocols.insert(protocol);
break;
}

Expand All @@ -627,17 +641,11 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) {
binding.BindingType = testType;
}

// For any literal type that has been covered, remove the default literal
// type.
if (!coveredLiteralProtocols.empty()) {
result.Bindings.erase(
std::remove_if(result.Bindings.begin(), result.Bindings.end(),
[&](PotentialBinding &binding) {
return binding.DefaultedProtocol &&
coveredLiteralProtocols.count(
*binding.DefaultedProtocol) > 0;
}),
result.Bindings.end());
for (auto &literalBinding : literalBindings) {
auto *protocol = literalBinding.DefaultedProtocol;
// For any literal type that has been covered, skip them.
if (coveredLiteralProtocols.count(protocol) == 0)
result.addPotentialBinding(std::move(literalBinding));
}
}

Expand All @@ -649,7 +657,7 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) {

++result.NumDefaultableBindings;
result.addPotentialBinding({type, AllowedBindingKind::Exact,
constraint->getKind(), None,
constraint->getKind(), nullptr,
constraint->getLocator()});
}

Expand Down
4 changes: 2 additions & 2 deletions lib/Sema/CSSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -708,8 +708,8 @@ bool ConstraintSystem::tryTypeVariableBindings(
// If we have a protocol with a default type, look for alternative
// types to the default.
if (tryCount == 0 && binding.DefaultedProtocol) {
KnownProtocolKind knownKind
= *((*binding.DefaultedProtocol)->getKnownProtocolKind());
KnownProtocolKind knownKind =
*(binding.DefaultedProtocol->getKnownProtocolKind());
for (auto altType : getAlternativeLiteralTypes(knownKind)) {
if (exploredTypes.insert(altType->getCanonicalType()).second)
newBindings.push_back({altType, AllowedBindingKind::Subtypes,
Expand Down
6 changes: 3 additions & 3 deletions lib/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -2696,15 +2696,15 @@ class ConstraintSystem {
ConstraintKind BindingSource;

/// The defaulted protocol associated with this binding.
Optional<ProtocolDecl *> DefaultedProtocol;
ProtocolDecl *DefaultedProtocol;

/// If this is a binding that comes from a \c Defaultable constraint,
/// the locator of that constraint.
ConstraintLocator *DefaultableBinding = nullptr;

PotentialBinding(Type type, AllowedBindingKind kind,
ConstraintKind bindingSource,
Optional<ProtocolDecl *> defaultedProtocol = None,
ProtocolDecl *defaultedProtocol = nullptr,
ConstraintLocator *defaultableBinding = nullptr)
: BindingType(type), Kind(kind), BindingSource(bindingSource),
DefaultedProtocol(defaultedProtocol),
Expand Down Expand Up @@ -2839,7 +2839,7 @@ class ConstraintSystem {
}
if (binding.DefaultedProtocol)
out << "(default from "
<< (*binding.DefaultedProtocol)->getName() << ") ";
<< binding.DefaultedProtocol->getName() << ") ";
out << type.getString();
},
[&]() { out << "; "; });
Expand Down
14 changes: 14 additions & 0 deletions test/Constraints/rdar38535743.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify %s
// REQUIRES: objc_interop

import Foundation

protocol P {}

class C {
init<T: NSObject>(values: [T]) where T: P {}
}

func foo<T: NSObject>(value: T) where T: P {
_ = C(values: [value]) // Ok
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %scale-test --invert-result --begin 1 --end 4 --step 1 --select incrementScopeCounter %s
// RUN: %scale-test --begin 1 --end 10 --step 1 --select incrementScopeCounter %s
// REQUIRES: OS=macosx
// REQUIRES: asserts

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %scale-test --invert-result --begin 7 --end 12 --step 1 --select incrementScopeCounter %s
// RUN: %scale-test --begin 1 --end 15 --step 1 --select incrementScopeCounter %s
// REQUIRES: OS=macosx
// REQUIRES: asserts

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// RUN: %scale-test --invert-result --begin 1 --end 5 --step 1 --select incrementScopeCounter %s
// REQUIRES: OS=macosx
// REQUIRES: asserts
// REQUIRES: rdar38963783

func t(_ x: Int?) -> Int {
return (x ?? 0)
Expand Down

0 comments on commit 31907d4

Please sign in to comment.