Skip to content

Commit

Permalink
AST/SIL: Fix problems if protocol requirement signature makes Self : …
Browse files Browse the repository at this point in the history
…P conformance implicit

Fixes assertion failures in SILGen and the optimizer with this
exotic setup:

protocol P {
  associatedtype T : Q
}

protocol Q {
  func requirement<U : P>(u: U) where U.T == Self
}

Here, we only have a U : P conformance, and not Self : Q,
because Self : Q is available as U.T : Q.

There were three problems here:

- The SIL verifier was too strict in verifying the generic signature.
  All that matters is we can get the Self parameter conformance, not
  that it's the first requirement, etc.

- GenericSignature::getSubstitutionMap() had a TODO concerning handling
  of same-type constraints -- this is the first test-case I've found
  that triggered the problem.

- GenericEnvironment::getSubstitutionMap() incorrectly ignored
  same-type constraints where one of the two types was a generic
  parameter.

Fixes <https://bugs.swift.org/browse/SR-3321>.
  • Loading branch information
slavapestov committed Jan 4, 2017
1 parent b47eff1 commit 3cbc08c
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 31 deletions.
46 changes: 25 additions & 21 deletions lib/AST/GenericEnvironment.cpp
Expand Up @@ -369,33 +369,37 @@ getSubstitutionMap(ModuleDecl *mod,
if (reqt.getKind() != RequirementKind::SameType)
continue;

auto first = reqt.getFirstType()->getAs<DependentMemberType>();
auto second = reqt.getSecondType()->getAs<DependentMemberType>();

if (!first || !second)
continue;
auto first = reqt.getFirstType();
auto second = reqt.getSecondType();

auto archetype = mapTypeIntoContext(mod, first)->getAs<ArchetypeType>();
if (!archetype)
continue;

auto firstBase = first->getBase();
auto secondBase = second->getBase();

auto firstBaseArchetype = mapTypeIntoContext(mod, firstBase)->getAs<ArchetypeType>();
auto secondBaseArchetype = mapTypeIntoContext(mod, secondBase)->getAs<ArchetypeType>();

if (!firstBaseArchetype || !secondBaseArchetype)
continue;
#ifndef NDEBUG
auto secondArchetype = mapTypeIntoContext(mod, second)->getAs<ArchetypeType>();
assert(secondArchetype == archetype);
#endif

if (auto *firstMemTy = first->getAs<DependentMemberType>()) {
auto parent = mapTypeIntoContext(mod, firstMemTy->getBase())
->getAs<ArchetypeType>();
if (parent && archetype->getParent() != parent) {
result.addParent(CanType(archetype),
CanType(parent),
firstMemTy->getAssocType());
}
}

if (archetype->getParent() != firstBaseArchetype)
result.addParent(CanType(archetype),
CanType(firstBaseArchetype),
first->getAssocType());
if (archetype->getParent() != secondBaseArchetype)
result.addParent(CanType(archetype),
CanType(secondBaseArchetype),
second->getAssocType());
if (auto *secondMemTy = second->getAs<DependentMemberType>()) {
auto parent = mapTypeIntoContext(mod, secondMemTy->getBase())
->getAs<ArchetypeType>();
if (parent && archetype->getParent() != parent) {
result.addParent(CanType(archetype),
CanType(parent),
secondMemTy->getAssocType());
}
}
}

assert(subs.empty() && "did not use all substitutions?!");
Expand Down
23 changes: 22 additions & 1 deletion lib/AST/GenericSignature.cpp
Expand Up @@ -275,7 +275,28 @@ GenericSignature::getSubstitutionMap(ArrayRef<Substitution> subs,
result.addConformances(canTy, sub.getConformances());
}

// TODO: same-type constraints
for (auto reqt : getRequirements()) {
if (reqt.getKind() != RequirementKind::SameType)
continue;

auto first = reqt.getFirstType();
auto second = reqt.getSecondType();

if (!first->isTypeParameter() || !second->isTypeParameter())
continue;

if (auto *firstMemTy = first->getAs<DependentMemberType>()) {
result.addParent(second->getCanonicalType(),
firstMemTy->getBase()->getCanonicalType(),
firstMemTy->getAssocType());
}

if (auto *secondMemTy = second->getAs<DependentMemberType>()) {
result.addParent(first->getCanonicalType(),
secondMemTy->getBase()->getCanonicalType(),
secondMemTy->getAssocType());
}
}

assert(subs.empty() && "did not use all substitutions?!");
}
Expand Down
21 changes: 12 additions & 9 deletions lib/SIL/SILVerifier.cpp
Expand Up @@ -1935,18 +1935,21 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
require(methodType->isPolymorphic(),
"result of witness_method must be polymorphic");

auto selfGenericParam
= methodType->getGenericSignature()->getGenericParams()[0];
auto genericSig = methodType->getGenericSignature();

auto selfGenericParam = genericSig->getGenericParams()[0];
require(selfGenericParam->getDepth() == 0
&& selfGenericParam->getIndex() == 0,
"method should be polymorphic on Self parameter at depth 0 index 0");
auto selfRequirement
= methodType->getGenericSignature()->getRequirements()[0];
require(selfRequirement.getKind() == RequirementKind::Conformance
&& selfRequirement.getFirstType()->isEqual(selfGenericParam)
&& selfRequirement.getSecondType()->getAs<ProtocolType>()
->getDecl() == protocol,
"method's Self parameter should be constrained by protocol");
auto selfRequirement = genericSig->getRequirements()[0];
require(selfRequirement.getKind() == RequirementKind::Conformance,
"first requirement should be conformance requirement");
auto conformsTo = genericSig->getConformsTo(selfGenericParam,
*F.getModule().getSwiftModule());
require(conformsTo.size() == 1,
"requirement Self parameter must conform to exactly one protocol");
require(conformsTo[0] == protocol,
"requirement Self parameter should be constrained by protocol");

auto lookupType = AMI->getLookupType();
if (getOpenedArchetypeOf(lookupType)) {
Expand Down
37 changes: 37 additions & 0 deletions validation-test/compiler_crashers_2_fixed/0059-sr3321.swift
@@ -0,0 +1,37 @@
// RUN: %target-swift-frontend %s -emit-ir
// RUN: %target-swift-frontend %s -emit-ir -O

protocol ControllerB {
associatedtype T: Controller
}

protocol Controller {
associatedtype T

func shouldSelect<S: ControllerB>(_ a: T, b: S) where S.T == Self
}

struct ControllerAImpl {}

struct ControllerImpl : Controller {
typealias T = ControllerAImpl

func shouldSelect<S : ControllerB>(_ a: ControllerAImpl, b: S) where S.T == ControllerImpl {}
}

struct ControllerBImpl1 : ControllerB {
typealias T = ControllerImpl
}

struct ControllerBImpl2<C : Controller> : ControllerB {
typealias T = C
}

extension Controller {
func didSelect<S: ControllerB>(_ a: T, b: S) where S.T == Self {
shouldSelect(a, b: b)
}
}

ControllerImpl().didSelect(ControllerAImpl(), b: ControllerBImpl1())
ControllerImpl().didSelect(ControllerAImpl(), b: ControllerBImpl2<ControllerImpl>())

0 comments on commit 3cbc08c

Please sign in to comment.