Skip to content

Commit 4fb2d07

Browse files
jckarterslavapestov
authored andcommitted
Refactor IsBindableVisitor's handling of generic nominals.
In order to correctly handle upper bound requirements arising from conditional conformances, we need to be able to ingest requirements introduced by conditional conformance extensions, which means recursively visiting the parent is no longer possible to do cleanly. It is simpler to substitute the generic type's entire generic context as a whole.
1 parent 8cd8ca0 commit 4fb2d07

File tree

1 file changed

+136
-106
lines changed

1 file changed

+136
-106
lines changed

lib/AST/Type.cpp

Lines changed: 136 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1798,44 +1798,160 @@ class IsBindableVisitor
17981798
}
17991799

18001800
CanType visitDynamicSelfType(DynamicSelfType *orig, CanType subst,
1801-
ArchetypeType *, ArrayRef<ProtocolConformanceRef>) {
1801+
ArchetypeType *upperBound,
1802+
ArrayRef<ProtocolConformanceRef> substConformances) {
18021803
// A "dynamic self" type can be bound to another dynamic self type, or the
18031804
// non-dynamic base class type.
18041805
if (auto dynSubst = dyn_cast<DynamicSelfType>(subst)) {
18051806
if (auto newBase = visit(orig->getSelfType(), dynSubst.getSelfType(),
1806-
nullptr, {})) {
1807+
upperBound, substConformances)) {
18071808
return CanDynamicSelfType::get(newBase, orig->getASTContext())
18081809
->getCanonicalType();
18091810
}
18101811
return CanType();
18111812
}
18121813

18131814
if (auto newNonDynBase = visit(orig->getSelfType(), subst,
1814-
nullptr, {})) {
1815+
upperBound, substConformances)) {
18151816
return newNonDynBase;
18161817
}
18171818
return CanType();
18181819
}
18191820

1821+
/// Handle a nominal type with generic parameters anywhere in its context.
1822+
/// \c origType and \c substType must already have been established to be
1823+
/// instantiations of the same \c NominalTypeDecl.
1824+
CanType handleGenericNominalType(NominalTypeDecl *decl,
1825+
CanType origType,
1826+
CanType substType,
1827+
ArchetypeType *upperBound,
1828+
ArrayRef<ProtocolConformanceRef> substConformances) {
1829+
assert(origType->getAnyNominal() == decl
1830+
&& substType->getAnyNominal() == decl);
1831+
1832+
auto *moduleDecl = decl->getParentModule();
1833+
auto origSubMap = origType->getContextSubstitutionMap(
1834+
moduleDecl, decl, decl->getGenericEnvironment());
1835+
auto substSubMap = substType->getContextSubstitutionMap(
1836+
moduleDecl, decl, decl->getGenericEnvironment());
1837+
1838+
auto genericSig = decl->getGenericSignature();
1839+
1840+
SmallVector<Type, 4> newParams;
1841+
llvm::DenseMap<Type, Type> newParamsMap;
1842+
bool didChange = false;
1843+
1844+
for (auto gpTy : genericSig.getGenericParams()) {
1845+
auto gp = gpTy->getCanonicalType();
1846+
1847+
auto orig = gp.subst(origSubMap)->getCanonicalType();
1848+
auto subst = gp.subst(substSubMap)->getCanonicalType();
1849+
1850+
// The new type is upper-bounded by the constraints the nominal type
1851+
// requires. The substitution operation may be interested in transforming
1852+
// the substituted type's conformances to these protocols.
1853+
//
1854+
// FIXME: The upperBound on the nominal type itself may impose additional
1855+
// requirements on the type parameters due to conditional conformances.
1856+
// These are currently not considered, leading to invalid generic signatures
1857+
// built during SILGen.
1858+
auto paramUpperBound = decl->mapTypeIntoContext(gp)
1859+
->getAs<ArchetypeType>();
1860+
SmallVector<ProtocolConformanceRef, 4> paramSubstConformances;
1861+
if (paramUpperBound) {
1862+
for (auto proto : paramUpperBound->getConformsTo()) {
1863+
auto conformance = substSubMap.lookupConformance(gp->getCanonicalType(),
1864+
proto);
1865+
if (!conformance)
1866+
return CanType();
1867+
paramSubstConformances.push_back(conformance);
1868+
}
1869+
}
1870+
1871+
auto newParam = visit(orig, subst, paramUpperBound,
1872+
paramSubstConformances);
1873+
if (!newParam)
1874+
return CanType();
1875+
1876+
newParams.push_back(newParam);
1877+
newParamsMap.insert({gpTy, newParam});
1878+
didChange |= (newParam != subst);
1879+
}
1880+
1881+
SmallVector<ProtocolConformanceRef, 4> newConformances;
1882+
1883+
// Collect conformances for the new substitutions, and verify that they don't
1884+
// invalidate the binding to the original type.
1885+
for (const auto &req : genericSig.getRequirements()) {
1886+
if (req.getKind() != RequirementKind::Conformance) continue;
1887+
1888+
auto canTy = req.getFirstType()->getCanonicalType();
1889+
1890+
// Verify the generic requirements, if the subst type is bound to
1891+
// concrete type.
1892+
auto *proto = req.getProtocolDecl();
1893+
if (!canTy.subst(substSubMap)->isTypeParameter()) {
1894+
auto origConf = origSubMap.lookupConformance(canTy, proto);
1895+
auto substConf = substSubMap.lookupConformance(canTy, proto);
1896+
1897+
if (origConf.isConcrete()) {
1898+
// A generic argument may inherit a concrete conformance from a class
1899+
// constraint, which could still be bound to a type parameter we don't
1900+
// know more about.
1901+
if (origConf.getConcrete()->getType()->is<ArchetypeType>())
1902+
continue;
1903+
1904+
if (!substConf.isConcrete())
1905+
return CanType();
1906+
if (origConf.getConcrete()->getRootConformance()
1907+
!= substConf.getConcrete()->getRootConformance())
1908+
return CanType();
1909+
}
1910+
}
1911+
1912+
// Gather the conformances for the new binding type, if the type changed.
1913+
if (didChange) {
1914+
auto newSubstTy = newParamsMap.find(req.getFirstType());
1915+
assert(newSubstTy != newParamsMap.end());
1916+
1917+
if (newSubstTy->second->isTypeParameter()) {
1918+
newConformances.push_back(ProtocolConformanceRef(proto));
1919+
} else {
1920+
auto newConformance
1921+
= moduleDecl->lookupConformance(newSubstTy->second, proto);
1922+
if (!newConformance)
1923+
return CanType();
1924+
newConformances.push_back(newConformance);
1925+
}
1926+
}
1927+
}
1928+
1929+
if (!didChange)
1930+
return substType;
1931+
1932+
// Build the new substituted generic type.
1933+
auto newSubMap = SubstitutionMap::get(genericSig,
1934+
newParams,
1935+
newConformances);
1936+
return decl->getDeclaredInterfaceType().subst(newSubMap)
1937+
->getCanonicalType();
1938+
}
1939+
18201940
CanType visitNominalType(NominalType *nom, CanType subst,
1821-
ArchetypeType*, ArrayRef<ProtocolConformanceRef>) {
1941+
ArchetypeType* upperBound,
1942+
ArrayRef<ProtocolConformanceRef> substConformances) {
18221943
if (auto substNom = dyn_cast<NominalType>(subst)) {
1944+
auto nomDecl = nom->getDecl();
18231945
if (nom->getDecl() != substNom->getDecl())
18241946
return CanType();
18251947

1826-
// Same decl should always either have or not have a parent.
1827-
assert((bool)nom->getParent() == (bool)substNom->getParent());
1828-
1829-
if (nom->getParent()) {
1830-
auto substParent = visit(nom->getParent()->getCanonicalType(),
1831-
substNom->getParent()->getCanonicalType(),
1832-
nullptr, {});
1833-
if (substParent == substNom.getParent())
1834-
return subst;
1835-
return NominalType::get(nom->getDecl(), substParent,
1836-
nom->getASTContext())
1837-
->getCanonicalType();
1948+
// If the type is generic (because it's a nested type in a generic context),
1949+
// process the generic type bindings.
1950+
if (!isa<ProtocolDecl>(nomDecl) && nomDecl->isGenericContext()) {
1951+
return handleGenericNominalType(nomDecl, CanType(nom), subst,
1952+
upperBound, substConformances);
18381953
}
1954+
// Otherwise, the nongeneric nominal types trivially match.
18391955
return subst;
18401956
}
18411957
return CanType();
@@ -2055,7 +2171,8 @@ class IsBindableVisitor
20552171
}
20562172

20572173
CanType visitBoundGenericType(BoundGenericType *bgt, CanType subst,
2058-
ArchetypeType *, ArrayRef<ProtocolConformanceRef>) {
2174+
ArchetypeType *upperBound,
2175+
ArrayRef<ProtocolConformanceRef> substConformances) {
20592176
auto substBGT = dyn_cast<BoundGenericType>(subst);
20602177
if (!substBGT)
20612178
return CanType();
@@ -2065,95 +2182,8 @@ class IsBindableVisitor
20652182

20662183
auto *decl = bgt->getDecl();
20672184

2068-
auto *moduleDecl = decl->getParentModule();
2069-
auto origSubMap = bgt->getContextSubstitutionMap(
2070-
moduleDecl, decl, decl->getGenericEnvironment());
2071-
auto substSubMap = substBGT->getContextSubstitutionMap(
2072-
moduleDecl, decl, decl->getGenericEnvironment());
2073-
2074-
auto genericSig = decl->getGenericSignature();
2075-
2076-
// Same decl should always either have or not have a parent.
2077-
assert((bool)bgt->getParent() == (bool)substBGT->getParent());
2078-
CanType newParent;
2079-
if (bgt->getParent()) {
2080-
newParent = visit(bgt->getParent()->getCanonicalType(),
2081-
substBGT.getParent(),
2082-
nullptr, {});
2083-
if (!newParent)
2084-
return CanType();
2085-
}
2086-
2087-
SmallVector<Type, 4> newParams;
2088-
bool didChange = newParent != substBGT.getParent();
2089-
2090-
auto depthStart =
2091-
genericSig.getGenericParams().size() - bgt->getGenericArgs().size();
2092-
for (auto i : indices(bgt->getGenericArgs())) {
2093-
auto orig = bgt->getGenericArgs()[i]->getCanonicalType();
2094-
auto subst = substBGT.getGenericArgs()[i];
2095-
auto gp = genericSig.getGenericParams()[depthStart + i];
2096-
2097-
// The new type is upper-bounded by the constraints the nominal type
2098-
// requires. The substitution operation may be interested in transforming
2099-
// the substituted type's conformances to these protocols.
2100-
auto upperBoundArchetype = decl->mapTypeIntoContext(gp)
2101-
->getAs<ArchetypeType>();
2102-
SmallVector<ProtocolConformanceRef, 4> substConformances;
2103-
if (upperBoundArchetype) {
2104-
for (auto proto : upperBoundArchetype->getConformsTo()) {
2105-
auto conformance = substSubMap.lookupConformance(gp->getCanonicalType(),
2106-
proto);
2107-
if (!conformance)
2108-
return CanType();
2109-
substConformances.push_back(conformance);
2110-
}
2111-
}
2112-
2113-
auto newParam = visit(orig, subst, upperBoundArchetype,
2114-
substConformances);
2115-
if (!newParam)
2116-
return CanType();
2117-
2118-
newParams.push_back(newParam);
2119-
didChange |= (newParam != subst);
2120-
}
2121-
2122-
for (const auto &req : genericSig.getRequirements()) {
2123-
if (req.getKind() != RequirementKind::Conformance) continue;
2124-
2125-
auto canTy = req.getFirstType()->getCanonicalType();
2126-
2127-
// If the substituted type is an interface type, we can't verify the
2128-
// generic requirements.
2129-
if (canTy.subst(substSubMap)->isTypeParameter())
2130-
continue;
2131-
2132-
auto *proto = req.getProtocolDecl();
2133-
auto origConf = origSubMap.lookupConformance(canTy, proto);
2134-
auto substConf = substSubMap.lookupConformance(canTy, proto);
2135-
2136-
if (origConf.isConcrete()) {
2137-
// A generic argument may inherit a concrete conformance from a class
2138-
// constraint, which could still be bound to a type parameter we don't
2139-
// know more about.
2140-
if (origConf.getConcrete()->getType()->is<ArchetypeType>())
2141-
continue;
2142-
2143-
if (!substConf.isConcrete())
2144-
return CanType();
2145-
if (origConf.getConcrete()->getRootConformance()
2146-
!= substConf.getConcrete()->getRootConformance())
2147-
return CanType();
2148-
}
2149-
}
2150-
2151-
if (!didChange)
2152-
return subst;
2153-
2154-
return BoundGenericType::get(substBGT->getDecl(),
2155-
newParent, newParams)
2156-
->getCanonicalType();
2185+
return handleGenericNominalType(decl, CanType(bgt), subst,
2186+
upperBound, substConformances);
21572187
}
21582188
};
21592189
}

0 commit comments

Comments
 (0)