Skip to content

Opaque type fixes for interesting generic environments #40938

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 4 commits into from
Jan 21, 2022
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
2 changes: 1 addition & 1 deletion include/swift/SIL/AbstractionPattern.h
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,7 @@ class AbstractionPattern {
return true;
}
if (auto archetype = dyn_cast<ArchetypeType>(type)) {
return !isa<OpaqueTypeArchetypeType>(archetype->getRoot());
return !isa<OpaqueTypeArchetypeType>(archetype);
}
return false;
}
Expand Down
13 changes: 13 additions & 0 deletions lib/AST/ASTVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2807,6 +2807,19 @@ class Verifier : public ASTWalker {

unsigned currentDepth = DC->getGenericContextDepth();
if (currentDepth < GTPD->getDepth()) {
// If this is actually an opaque type's generic parameter, we're okay.
if (auto value = dyn_cast_or_null<ValueDecl>(DC->getAsDecl())) {
auto opaqueDecl = dyn_cast<OpaqueTypeDecl>(value);
if (!opaqueDecl)
opaqueDecl = value->getOpaqueResultTypeDecl();
if (opaqueDecl) {
if (GTPD->getDepth() ==
opaqueDecl->getOpaqueGenericParams().front()->getDepth()) {
return;
}
}
}

Out << "GenericTypeParamDecl has incorrect depth\n";
abort();
}
Expand Down
14 changes: 13 additions & 1 deletion lib/AST/GenericEnvironment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,12 +362,24 @@ GenericEnvironment::getOrCreateArchetypeFromInterfaceType(Type depType) {
break;
}

case Kind::Opaque:
case Kind::Opaque: {
// If the anchor type isn't rooted in a generic parameter that
// represents an opaque declaration, then apply the outer substitutions.
// It would be incorrect to build an opaque type archetype here.
auto rootGP = requirements.anchor->getRootGenericParam();
unsigned opaqueDepth =
getOpaqueTypeDecl()->getOpaqueGenericParams().front()->getDepth();
if (rootGP->getDepth() < opaqueDepth) {
result = maybeApplyOpaqueTypeSubstitutions(requirements.anchor);
break;
}

result = OpaqueTypeArchetypeType::getNew(this, requirements.anchor,
requirements.protos, superclass,
requirements.layout);
break;
}
}
}

if (genericParam)
Expand Down
2 changes: 1 addition & 1 deletion lib/AST/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ ProtocolConformanceRef::subst(Type origType,
// unless we're specifically substituting opaque types.
if (auto origArchetype = origType->getAs<ArchetypeType>()) {
if (!options.contains(SubstFlags::SubstituteOpaqueArchetypes)
&& isa<OpaqueTypeArchetypeType>(origArchetype->getRoot())) {
&& isa<OpaqueTypeArchetypeType>(origArchetype)) {
return *this;
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/AST/SubstitutionMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const {
// If we have an archetype, map out of the context so we can compute a
// conformance access path.
if (auto archetype = dyn_cast<ArchetypeType>(type)) {
if (!isa<OpaqueTypeArchetypeType>(archetype->getRoot())) {
if (!isa<OpaqueTypeArchetypeType>(archetype)) {
type = archetype->getInterfaceType()->getCanonicalType();
}
}
Expand Down
4 changes: 2 additions & 2 deletions lib/AST/TypeWalker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,8 @@ class Traversal : public TypeVisitor<Traversal, bool>
bool visitArchetypeType(ArchetypeType *ty) {
// If the root is an opaque archetype, visit its substitution replacement
// types.
if (auto opaqueRoot = dyn_cast<OpaqueTypeArchetypeType>(ty->getRoot())) {
for (auto arg : opaqueRoot->getSubstitutions().getReplacementTypes()) {
if (auto opaque = dyn_cast<OpaqueTypeArchetypeType>(ty)) {
for (auto arg : opaque->getSubstitutions().getReplacementTypes()) {
if (doIt(arg)) {
return true;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/IRGen/DebugTypeInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class DebugTypeInfo {
bool isContextArchetype() const {
if (auto archetype =
Type->getWithoutSpecifierType()->getAs<ArchetypeType>()) {
return !isa<OpaqueTypeArchetypeType>(archetype->getRoot());
return !isa<OpaqueTypeArchetypeType>(archetype);
}
return false;
}
Expand Down
30 changes: 23 additions & 7 deletions lib/IRGen/GenArchetype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -537,16 +537,32 @@ llvm::Value *irgen::emitOpaqueTypeWitnessTableRef(IRGenFunction &IGF,
ProtocolDecl *protocol) {
auto accessorFn = IGF.IGM.getGetOpaqueTypeConformanceFn();
auto opaqueDecl = archetype->getDecl();

assert(archetype->isRoot() && "Can only follow from the root");

llvm::Value *descriptor = getAddressOfOpaqueTypeDescriptor(IGF, opaqueDecl);

auto foundProtocol = std::find(archetype->getConformsTo().begin(),
archetype->getConformsTo().end(),
protocol);
assert(foundProtocol != archetype->getConformsTo().end());

unsigned index = foundProtocol - archetype->getConformsTo().begin() + 1;
// Compute the index at which this witness table resides.
unsigned index = opaqueDecl->getOpaqueGenericParams().size();
auto opaqueReqs =
opaqueDecl->getOpaqueInterfaceGenericSignature().getRequirements();
bool found = false;
for (const auto &req : opaqueReqs) {
auto reqProto = opaqueTypeRequiresWitnessTable(opaqueDecl, req);
if (!reqProto)
continue;

// Is this requirement the one we're looking for?
if (reqProto == protocol &&
req.getFirstType()->isEqual(archetype->getInterfaceType())) {
found = true;
break;
}

++index;
}

(void)found;
assert(found && "Opaque type does not conform to protocol");
auto indexValue = llvm::ConstantInt::get(IGF.IGM.SizeTy, index);

llvm::CallInst *result = nullptr;
Expand Down
51 changes: 28 additions & 23 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2007,29 +2007,7 @@ namespace {
///
/// When it does, returns the protocol.
ProtocolDecl *requiresWitnessTable(const Requirement &req) const {
// We only care about conformance requirements.
if (req.getKind() != RequirementKind::Conformance)
return nullptr;

// The protocol must require a witness table.
auto proto = req.getProtocolDecl();
if (!Lowering::TypeConverter::protocolRequiresWitnessTable(proto))
return nullptr;

// The type itself must be anchored on one of the generic parameters of
// the opaque type (not an outer context).
Type subject = req.getFirstType();
while (auto depMember = subject->getAs<DependentMemberType>()) {
subject = depMember->getBase();
}

if (auto genericParam = subject->getAs<GenericTypeParamType>()) {
unsigned opaqueDepth = O->getOpaqueGenericParams().front()->getDepth();
if (genericParam->getDepth() == opaqueDepth)
return proto;
}

return nullptr;
return opaqueTypeRequiresWitnessTable(O, req);
}

public:
Expand Down Expand Up @@ -2155,6 +2133,33 @@ namespace {
};
} // end anonymous namespace

ProtocolDecl *irgen::opaqueTypeRequiresWitnessTable(
OpaqueTypeDecl *opaque, const Requirement &req) {
// We only care about conformance requirements.
if (req.getKind() != RequirementKind::Conformance)
return nullptr;

// The protocol must require a witness table.
auto proto = req.getProtocolDecl();
if (!Lowering::TypeConverter::protocolRequiresWitnessTable(proto))
return nullptr;

// The type itself must be anchored on one of the generic parameters of
// the opaque type (not an outer context).
Type subject = req.getFirstType();
while (auto depMember = subject->getAs<DependentMemberType>()) {
subject = depMember->getBase();
}

if (auto genericParam = subject->getAs<GenericTypeParamType>()) {
unsigned opaqueDepth = opaque->getOpaqueGenericParams().front()->getDepth();
if (genericParam->getDepth() == opaqueDepth)
return proto;
}

return nullptr;
}

static void eraseExistingTypeContextDescriptor(IRGenModule &IGM,
NominalTypeDecl *type) {
// We may have emitted a partial type context descriptor with some empty
Expand Down
10 changes: 10 additions & 0 deletions lib/IRGen/GenMeta.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,16 @@ namespace irgen {
llvm::Function *function,
LinkEntity entity,
Size size);

/// Determine whether the given opaque type requires a witness table for the
/// given requirement.
///
/// \returns the protocol when a witness table is required, or \c nullptr
/// if the requirement isn't a conformance requirement or doesn't require a
/// witness table.
ProtocolDecl *opaqueTypeRequiresWitnessTable(
OpaqueTypeDecl *opaque, const Requirement &req);

} // end namespace irgen
} // end namespace swift

Expand Down
2 changes: 1 addition & 1 deletion lib/IRGen/GenReflection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ getRuntimeVersionThatSupportsDemanglingType(CanType type) {
if (type->hasOpaqueArchetype()) {
auto hasOpaqueAssocType = type.findIf([](CanType t) -> bool {
if (auto a = dyn_cast<ArchetypeType>(t)) {
return isa<OpaqueTypeArchetypeType>(a->getRoot()) &&
return isa<OpaqueTypeArchetypeType>(a) &&
a->getInterfaceType()->is<DependentMemberType>();
}
return false;
Expand Down
7 changes: 3 additions & 4 deletions lib/IRGen/Outlining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,10 @@ irgen::getTypeAndGenericSignatureForManglingOutlineFunction(SILType type) {
GenericEnvironment *env = nullptr;
loweredType.findIf([&env](Type t) -> bool {
if (auto arch = t->getAs<ArchetypeType>()) {
auto root = arch->getRoot();
if (!isa<PrimaryArchetypeType>(root) &&
!isa<VariadicSequenceType>(root))
if (!isa<PrimaryArchetypeType>(arch) &&
!isa<VariadicSequenceType>(arch))
return false;
env = root->getGenericEnvironment();
env = arch->getGenericEnvironment();
return true;
}
return false;
Expand Down
9 changes: 4 additions & 5 deletions lib/SIL/Verifier/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,17 @@ static llvm::cl::opt<bool> AllowCriticalEdges("allow-critical-edges",
/// Returns true if A is an opened existential type or is equal to an
/// archetype from F's generic context.
static bool isArchetypeValidInFunction(ArchetypeType *A, const SILFunction *F) {
auto root = A->getRoot();
if (!isa<PrimaryArchetypeType>(root) && !isa<SequenceArchetypeType>(root))
if (!isa<PrimaryArchetypeType>(A) && !isa<SequenceArchetypeType>(A))
return true;
if (isa<OpenedArchetypeType>(root))
if (isa<OpenedArchetypeType>(A))
return true;
if (isa<OpaqueTypeArchetypeType>(root))
if (isa<OpaqueTypeArchetypeType>(A))
return true;

// Ok, we have a primary archetype, make sure it is in the nested generic
// environment of our caller.
if (auto *genericEnv = F->getGenericEnvironment())
if (root->getGenericEnvironment() == genericEnv)
if (A->getGenericEnvironment() == genericEnv)
return true;

return false;
Expand Down
8 changes: 4 additions & 4 deletions lib/SILOptimizer/Utils/Generics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1092,11 +1092,11 @@ shouldBePartiallySpecialized(Type Replacement,
llvm::SmallSetVector<ArchetypeType *, 2> UsedArchetypes;
Replacement.visit([&](Type Ty) {
if (auto Archetype = Ty->getAs<ArchetypeType>()) {
if (auto Primary = dyn_cast<PrimaryArchetypeType>(Archetype->getRoot())) {
if (auto Primary = dyn_cast<PrimaryArchetypeType>(Archetype)) {
UsedArchetypes.insert(Primary);
}

if (auto Seq = dyn_cast<SequenceArchetypeType>(Archetype->getRoot())) {
if (auto Seq = dyn_cast<SequenceArchetypeType>(Archetype)) {
UsedArchetypes.insert(Seq);
}
}
Expand Down Expand Up @@ -1322,11 +1322,11 @@ void FunctionSignaturePartialSpecializer::collectUsedCallerArchetypes(
// Add used generic parameters/archetypes.
Replacement.visit([&](Type Ty) {
if (auto Archetype = Ty->getAs<ArchetypeType>()) {
if (auto Primary = dyn_cast<PrimaryArchetypeType>(Archetype->getRoot())) {
if (auto Primary = dyn_cast<PrimaryArchetypeType>(Archetype)) {
UsedCallerArchetypes.insert(Primary);
}

if (auto Seq = dyn_cast<SequenceArchetypeType>(Archetype->getRoot())) {
if (auto Seq = dyn_cast<SequenceArchetypeType>(Archetype)) {
UsedCallerArchetypes.insert(Seq);
}
}
Expand Down
4 changes: 2 additions & 2 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2849,8 +2849,8 @@ ConstraintSystem::matchDeepEqualityTypes(Type type1, Type type2,
// Handle opaque archetypes.
if (auto arch1 = type1->getAs<ArchetypeType>()) {
auto arch2 = type2->castTo<ArchetypeType>();
auto opaque1 = cast<OpaqueTypeArchetypeType>(arch1->getRoot());
auto opaque2 = cast<OpaqueTypeArchetypeType>(arch2->getRoot());
auto opaque1 = cast<OpaqueTypeArchetypeType>(arch1);
auto opaque2 = cast<OpaqueTypeArchetypeType>(arch2);
assert(opaque1->getDecl() == opaque2->getDecl());
assert(opaque1->getCanonicalInterfaceType(arch1->getInterfaceType())->isEqual(
opaque2->getCanonicalInterfaceType(arch2->getInterfaceType())));
Expand Down
7 changes: 4 additions & 3 deletions lib/Sema/TypeCheckConstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -692,14 +692,15 @@ static Type replaceArchetypesWithTypeVariables(ConstraintSystem &cs,
return found->second;

if (auto archetypeType = dyn_cast<ArchetypeType>(origType)) {
auto root = archetypeType->getRoot();
// We leave opaque types and their nested associated types alone here.
// They're globally available.
if (isa<OpaqueTypeArchetypeType>(root))
if (isa<OpaqueTypeArchetypeType>(archetypeType))
return origType;

auto root = archetypeType->getRoot();
// For other nested types, fail here so the default logic in subst()
// for nested types applies.
else if (root != archetypeType)
if (root != archetypeType)
return Type();

auto locator = cs.getConstraintLocator({});
Expand Down
32 changes: 29 additions & 3 deletions test/Interpreter/named_opaque_result_types.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift -swift-version 5 -Xfrontend -enable-experimental-named-opaque-types %s -o %t/a.out
// RUN: %target-build-swift -g -swift-version 5 -Xfrontend -enable-experimental-named-opaque-types %s -o %t/a.out
// RUN: %target-run %t/a.out | %FileCheck %s

// REQUIRES: executable_test
Expand Down Expand Up @@ -34,8 +34,7 @@ struct X<T, U: Hashable>: P {
func f() -> <
R1: Collection, R2: Collection where R1.Element == T, R2.Element == U
> (R1, R2) {
// FIXME: Use unzipCollection here
return (Array(data.map { $0.0 }), Set(data.map { $0.1 }))
return unzipCollection(data)
}
}

Expand All @@ -62,6 +61,22 @@ if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) {
// CHECK: {{["Hello", "World", "!"]|too old}}
print(x3.1)

// CHECK: Integer loop
// CHECK-NEXT: 1
// CHECK-NEXT: 2
// CHECK-NEXT: 3
print("Integer loop")
for i in x3.0 {
print(i)
}

// CHECK: Hello
// CHECK-NEXT: World
// CHECK-NEXT: !
for index in x3.1.indices {
print(x3.1[index])
}

// CHECK: {{(Array<Int>, Set<String>)|too old}}
let paType = getP_A(X<Int, String>.self)
print(paType)
Expand All @@ -71,6 +86,17 @@ if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) {
print("too old")
print("too old")
print("too old")
print("too old")

print("Integer loop")
print("1")
print("2")
print("3")

print("Hello")
print("World")
print("!")

print("too old")
print("too old")
}