Skip to content

Commit 85c7228

Browse files
jckarterslavapestov
authored andcommitted
Have IsBindableVisitor consider conditional conformances.
The upper bound on a nominal type's generic argument can have more requirements imposed on it than those written in the original nominal type's generic signature, depending on the requirements imposed on the nominal type itself, thanks to conditional conformances. IsBindableVisitor failed to take this into account when visiting the bindings of generic type arguments. This could cause Type::isBindableTo to provide a false positive for a substituted type that doesn't satisfy conditional conformances, but more importantly, SIL type lowering uses the same visitor to extract an upper bound generic signature for substituted SIL function types, and if it doesn't preserve requirements from conditional conformances on nominal types in that signature, it can end up building an incorrect substitution map. Fix this by passing down the upper bound from generic arguments even when visiting nominal types, and using those upper bounds to check for conditional conformances that add requirements to generic arguments while visiting them.
1 parent 4fb2d07 commit 85c7228

File tree

2 files changed

+189
-5
lines changed

2 files changed

+189
-5
lines changed

lib/AST/Type.cpp

Lines changed: 136 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
//
1515
//===----------------------------------------------------------------------===//
1616

17+
#define DEBUG_TYPE "ast-types"
18+
1719
#include "swift/AST/Types.h"
1820
#include "ForeignRepresentationInfo.h"
1921
#include "swift/AST/ASTContext.h"
@@ -39,6 +41,7 @@
3941
#include "llvm/ADT/SmallString.h"
4042
#include "llvm/ADT/STLExtras.h"
4143
#include "llvm/Support/Compiler.h"
44+
#include "llvm/Support/Debug.h"
4245
#include "llvm/Support/raw_ostream.h"
4346
#include <algorithm>
4447
#include <functional>
@@ -1829,6 +1832,15 @@ class IsBindableVisitor
18291832
assert(origType->getAnyNominal() == decl
18301833
&& substType->getAnyNominal() == decl);
18311834

1835+
LLVM_DEBUG(llvm::dbgs() << "\n---\nTesting bindability of:\n";
1836+
origType->print(llvm::dbgs());
1837+
llvm::dbgs() << "\nto subst type:\n";
1838+
substType->print(llvm::dbgs());
1839+
if (upperBound) {
1840+
llvm::dbgs() << "\nwith upper bound archetype:\n";
1841+
upperBound->print(llvm::dbgs());
1842+
});
1843+
18321844
auto *moduleDecl = decl->getParentModule();
18331845
auto origSubMap = origType->getContextSubstitutionMap(
18341846
moduleDecl, decl, decl->getGenericEnvironment());
@@ -1841,7 +1853,125 @@ class IsBindableVisitor
18411853
llvm::DenseMap<Type, Type> newParamsMap;
18421854
bool didChange = false;
18431855

1844-
for (auto gpTy : genericSig.getGenericParams()) {
1856+
// The upper bounds for the nominal type's arguments may depend on the
1857+
// upper bounds imposed on the nominal type itself, if conditional
1858+
// conformances are involved. For instance, if we're looking at:
1859+
//
1860+
// protocol P {}
1861+
// struct A<T: P> {}
1862+
// struct B<U> {}
1863+
// extension B: P where U: P {}
1864+
//
1865+
// and visiting `A<B<V>>`, then `B<V>` has an upper bound of `_: P` because
1866+
// of A's generic type constraint. In order to stay within this upper bound,
1867+
// `V` must also have `_: P` as its upper bound, in order to satisfy the
1868+
// constraint on `B<V>`. To handle this correctly, ingest requirements
1869+
// from any extension declarations providing conformances required by the
1870+
// upper bound.
1871+
auto upperBoundGenericSig = genericSig;
1872+
auto upperBoundSubstMap = substSubMap;
1873+
1874+
LLVM_DEBUG(llvm::dbgs() << "\nNominal type generic signature:\n";
1875+
upperBoundGenericSig.print(llvm::dbgs());
1876+
upperBoundSubstMap.dump(llvm::dbgs()));
1877+
1878+
if (upperBound && !upperBound->getConformsTo().empty()) {
1879+
// Start with the set of requirements from the nominal type.
1880+
SmallVector<Requirement, 4> addedRequirements;
1881+
1882+
llvm::DenseMap<std::pair<CanType, ProtocolDecl*>, ProtocolConformanceRef> addedConformances;
1883+
1884+
for (auto proto : upperBound->getConformsTo()) {
1885+
// Find the DeclContext providing the conformance for the type.
1886+
auto nomConformance = moduleDecl->lookupConformance(substType, proto);
1887+
if (!nomConformance)
1888+
return CanType();
1889+
if (nomConformance.isAbstract())
1890+
continue;
1891+
auto conformanceContext = nomConformance.getConcrete()->getDeclContext();
1892+
1893+
LLVM_DEBUG(llvm::dbgs() << "\nFound extension conformance for "
1894+
<< proto->getName()
1895+
<< " in context:\n";
1896+
conformanceContext->printContext(llvm::dbgs()));
1897+
1898+
auto conformanceSig = conformanceContext->getGenericSignatureOfContext();
1899+
// TODO: Conformance on generalized generic extensions could conceivably
1900+
// have totally different generic signatures.
1901+
assert(conformanceSig.getGenericParams().size()
1902+
== genericSig.getGenericParams().size()
1903+
&& "generalized generic extension not handled properly");
1904+
1905+
// Collect requirements from the conformance not satisfied by the
1906+
// original declaration.
1907+
for (auto reqt : conformanceSig->requirementsNotSatisfiedBy(genericSig)) {
1908+
LLVM_DEBUG(llvm::dbgs() << "\n- adds requirement\n";
1909+
reqt.dump(llvm::dbgs()));
1910+
1911+
addedRequirements.push_back(reqt);
1912+
1913+
// Collect the matching conformance for the substituted type.
1914+
// TODO: Look this up using the upperBoundSubstConformances
1915+
if (reqt.getKind() == RequirementKind::Conformance) {
1916+
auto proto = reqt.getSecondType()->castTo<ProtocolType>()->getDecl();
1917+
auto substTy = reqt.getFirstType().subst(substSubMap);
1918+
ProtocolConformanceRef substConformance;
1919+
if (substTy->isTypeParameter()) {
1920+
substConformance = ProtocolConformanceRef(proto);
1921+
} else {
1922+
substConformance = moduleDecl->lookupConformance(substTy, proto);
1923+
}
1924+
1925+
LLVM_DEBUG(llvm::dbgs() << "\n` adds conformance for subst type\n";
1926+
substTy->print(llvm::dbgs());
1927+
substConformance.dump(llvm::dbgs()));
1928+
1929+
auto key = std::make_pair(reqt.getFirstType()->getCanonicalType(),
1930+
proto);
1931+
1932+
addedConformances.insert({key, substConformance});
1933+
}
1934+
}
1935+
}
1936+
1937+
// Build the generic signature with the additional collected requirements.
1938+
if (!addedRequirements.empty()) {
1939+
upperBoundGenericSig = evaluateOrDefault(
1940+
decl->getASTContext().evaluator,
1941+
AbstractGenericSignatureRequest{
1942+
upperBoundGenericSig.getPointer(),
1943+
/*genericParams=*/{ },
1944+
std::move(addedRequirements)},
1945+
nullptr);
1946+
upperBoundSubstMap = SubstitutionMap::get(upperBoundGenericSig,
1947+
[&](SubstitutableType *t) -> Type {
1948+
// Type substitutions remain the same as the original substitution
1949+
// map.
1950+
return Type(t).subst(substSubMap);
1951+
},
1952+
[&](CanType dependentType,
1953+
Type conformingReplacementType,
1954+
ProtocolDecl *conformedProtocol) -> ProtocolConformanceRef {
1955+
// Check whether we added this conformance.
1956+
auto added = addedConformances.find({dependentType,
1957+
conformedProtocol});
1958+
if (added != addedConformances.end()) {
1959+
return added->second;
1960+
}
1961+
// Otherwise, use the conformance from the original map.
1962+
1963+
return substSubMap.lookupConformance(dependentType, conformedProtocol);
1964+
});
1965+
1966+
LLVM_DEBUG(llvm::dbgs() << "\nGeneric signature with conditional reqts:\n";
1967+
upperBoundGenericSig.print(llvm::dbgs());
1968+
upperBoundSubstMap.dump(llvm::dbgs()));
1969+
}
1970+
}
1971+
1972+
auto upperBoundGenericEnv = upperBoundGenericSig.getGenericEnvironment();
1973+
1974+
for (auto gpTy : upperBoundGenericSig.getGenericParams()) {
18451975
auto gp = gpTy->getCanonicalType();
18461976

18471977
auto orig = gp.subst(origSubMap)->getCanonicalType();
@@ -1855,13 +1985,14 @@ class IsBindableVisitor
18551985
// requirements on the type parameters due to conditional conformances.
18561986
// These are currently not considered, leading to invalid generic signatures
18571987
// built during SILGen.
1858-
auto paramUpperBound = decl->mapTypeIntoContext(gp)
1859-
->getAs<ArchetypeType>();
1988+
auto paramUpperBound =
1989+
GenericEnvironment::mapTypeIntoContext(upperBoundGenericEnv, gp)
1990+
->getAs<ArchetypeType>();
18601991
SmallVector<ProtocolConformanceRef, 4> paramSubstConformances;
18611992
if (paramUpperBound) {
18621993
for (auto proto : paramUpperBound->getConformsTo()) {
1863-
auto conformance = substSubMap.lookupConformance(gp->getCanonicalType(),
1864-
proto);
1994+
auto conformance = upperBoundSubstMap.lookupConformance(gp->getCanonicalType(),
1995+
proto);
18651996
if (!conformance)
18661997
return CanType();
18671998
paramSubstConformances.push_back(conformance);
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// RUN: %target-swift-emit-silgen %s | %FileCheck %s
2+
3+
enum E<T : P> {
4+
case a(T.X)
5+
}
6+
7+
struct S<T> {}
8+
9+
protocol P {
10+
associatedtype X
11+
}
12+
13+
extension S : P where T : P {
14+
typealias X = T.X
15+
}
16+
17+
func foo<T : P>(_ x: E<S<T>>) {
18+
// Ensure that the lowered substituted SIL function type for `() -> E<S<T>>`
19+
// preserves the T: P constraint necessary to allow for `S<T>` to substitute
20+
// into `E<T: P>`
21+
bar({ return x })
22+
}
23+
24+
// CHECK-LABEL: {{^}}sil {{.*}} @${{.*}}3bar
25+
// CHECK-SAME: @substituted <τ_0_0 where τ_0_0 : P> () -> @out E<S<τ_0_0>> for <T>
26+
func bar<T: P>(_: () -> E<S<T>>) {}
27+
28+
// ---
29+
30+
protocol Q: P {
31+
associatedtype Y
32+
}
33+
34+
enum E2<T: Q> {
35+
case a(T.Y)
36+
}
37+
38+
struct S2<T: P>: P {
39+
typealias X = T.X
40+
}
41+
42+
extension S2: Q where T: Q {
43+
typealias Y = T.Y
44+
}
45+
46+
func foo2<T : Q>(_ x: E2<S2<T>>) {
47+
bar2({ return x })
48+
}
49+
50+
// CHECK-LABEL: {{^}}sil {{.*}} @${{.*}}4bar2
51+
// CHECK-SAME: @substituted <τ_0_0 where τ_0_0 : Q> () -> @out E2<S2<τ_0_0>> for <T>
52+
func bar2<T: Q>(_: () -> E2<S2<T>>) {}
53+

0 commit comments

Comments
 (0)