Respect resilience when specializing opaque type archetypes

This commit is contained in:
Arnold Schwaighofer
2019-04-24 10:03:57 -07:00
parent 881d9ad2bf
commit a793dfb451
11 changed files with 188 additions and 37 deletions

View File

@@ -111,8 +111,9 @@ public:
/// Replace opaque types in the conforming type with their underlying types, /// Replace opaque types in the conforming type with their underlying types,
/// and resolve opaque conformances to their underlying conformances. /// and resolve opaque conformances to their underlying conformances.
ProtocolConformanceRef substOpaqueTypesWithUnderlyingTypes(Type origType) const; ProtocolConformanceRef substOpaqueTypesWithUnderlyingTypes(
Type origType, ModuleDecl *modulePerformingSubstitution) const;
/// Given a dependent type (expressed in terms of this conformance's /// Given a dependent type (expressed in terms of this conformance's
/// protocol), follow it from the conforming type. /// protocol), follow it from the conforming type.
Type getAssociatedType(Type origType, Type dependentType, Type getAssociatedType(Type origType, Type dependentType,

View File

@@ -177,7 +177,8 @@ public:
/// Replace opaque types in the replacement types in the map with their /// Replace opaque types in the replacement types in the map with their
/// underlying types. Does not change keys. /// underlying types. Does not change keys.
SubstitutionMap substOpaqueTypesWithUnderlyingTypes() const; SubstitutionMap substOpaqueTypesWithUnderlyingTypes(
ModuleDecl *modulePerformingSubstitution) const;
/// Create a substitution map for a protocol conformance. /// Create a substitution map for a protocol conformance.
static SubstitutionMap static SubstitutionMap

View File

@@ -318,7 +318,8 @@ public:
/// Replace opaque types with their underlying types when visible at the given /// Replace opaque types with their underlying types when visible at the given
/// resilience expansion. /// resilience expansion.
Type substOpaqueTypesWithUnderlyingTypes() const; Type substOpaqueTypesWithUnderlyingTypes(
ModuleDecl *modulePerformingSubstitution) const;
bool isPrivateStdlibType(bool treatNonBuiltinProtocolsAsPublic = true) const; bool isPrivateStdlibType(bool treatNonBuiltinProtocolsAsPublic = true) const;

View File

@@ -4827,8 +4827,11 @@ END_CAN_TYPE_WRAPPER(OpaqueTypeArchetypeType, ArchetypeType)
/// to their underlying types. /// to their underlying types.
class ReplaceOpaqueTypesWithUnderlyingTypes { class ReplaceOpaqueTypesWithUnderlyingTypes {
public: public:
ReplaceOpaqueTypesWithUnderlyingTypes() {} ModuleDecl *modulePerformingSubstitution;
ReplaceOpaqueTypesWithUnderlyingTypes(
ModuleDecl *modulePerformingSubstitution)
: modulePerformingSubstitution(modulePerformingSubstitution) {}
/// TypeSubstitutionFn /// TypeSubstitutionFn
Type operator()(SubstitutableType *maybeOpaqueType) const; Type operator()(SubstitutableType *maybeOpaqueType) const;

View File

@@ -142,8 +142,9 @@ ProtocolConformanceRef::subst(Type origType,
} }
ProtocolConformanceRef ProtocolConformanceRef
ProtocolConformanceRef::substOpaqueTypesWithUnderlyingTypes(Type origType) const { ProtocolConformanceRef::substOpaqueTypesWithUnderlyingTypes(
ReplaceOpaqueTypesWithUnderlyingTypes replacer; Type origType, ModuleDecl *modulePerformingSubstitution) const {
ReplaceOpaqueTypesWithUnderlyingTypes replacer(modulePerformingSubstitution);
return subst(origType, replacer, replacer, return subst(origType, replacer, replacer,
SubstFlags::SubstituteOpaqueArchetypes); SubstFlags::SubstituteOpaqueArchetypes);
} }

View File

@@ -492,10 +492,9 @@ SubstitutionMap SubstitutionMap::subst(TypeSubstitutionFn subs,
return SubstitutionMap(genericSig, newSubs, newConformances); return SubstitutionMap(genericSig, newSubs, newConformances);
} }
SubstitutionMap SubstitutionMap SubstitutionMap::substOpaqueTypesWithUnderlyingTypes(
SubstitutionMap::substOpaqueTypesWithUnderlyingTypes() ModuleDecl *modulePerformingSubstitution) const {
const { ReplaceOpaqueTypesWithUnderlyingTypes replacer(modulePerformingSubstitution);
ReplaceOpaqueTypesWithUnderlyingTypes replacer;
return subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes); return subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes);
} }

View File

@@ -2477,9 +2477,17 @@ Type ReplaceOpaqueTypesWithUnderlyingTypes::operator()(
auto archetypeAndRoot = getArchetypeAndRootOpaqueArchetype(maybeOpaqueType); auto archetypeAndRoot = getArchetypeAndRootOpaqueArchetype(maybeOpaqueType);
if (!archetypeAndRoot) if (!archetypeAndRoot)
return maybeOpaqueType; return maybeOpaqueType;
auto archetype = archetypeAndRoot->first; auto archetype = archetypeAndRoot->first;
auto opaqueRoot = archetypeAndRoot->second; auto opaqueRoot = archetypeAndRoot->second;
// Don't replace opaque types from resilient modules.
auto namingDecl = opaqueRoot->getDecl()->getNamingDecl();
auto module = namingDecl->getModuleContext();
if (module->isResilient() && module != modulePerformingSubstitution) {
return maybeOpaqueType;
}
auto subs = opaqueRoot->getDecl()->getUnderlyingTypeSubstitutions(); auto subs = opaqueRoot->getDecl()->getUnderlyingTypeSubstitutions();
// TODO: Check the resilience expansion, and handle opaque types with // TODO: Check the resilience expansion, and handle opaque types with
// unknown underlying types. For now, all opaque types are always // unknown underlying types. For now, all opaque types are always
@@ -2496,7 +2504,8 @@ Type ReplaceOpaqueTypesWithUnderlyingTypes::operator()(
// If the type still contains opaque types, recur. // If the type still contains opaque types, recur.
if (substTy->hasOpaqueArchetype()) { if (substTy->hasOpaqueArchetype()) {
return substTy.substOpaqueTypesWithUnderlyingTypes(); return substTy.substOpaqueTypesWithUnderlyingTypes(
modulePerformingSubstitution);
} }
return substTy; return substTy;
} }
@@ -2516,6 +2525,14 @@ ReplaceOpaqueTypesWithUnderlyingTypes::operator()(CanType maybeOpaqueType,
auto archetype = archetypeAndRoot->first; auto archetype = archetypeAndRoot->first;
auto opaqueRoot = archetypeAndRoot->second; auto opaqueRoot = archetypeAndRoot->second;
// Don't replace opaque types from resilient modules.
auto namingDecl = opaqueRoot->getDecl()->getNamingDecl();
auto module = namingDecl->getModuleContext();
if (module->isResilient() && module != modulePerformingSubstitution) {
return abstractRef;
}
auto subs = opaqueRoot->getDecl()->getUnderlyingTypeSubstitutions(); auto subs = opaqueRoot->getDecl()->getUnderlyingTypeSubstitutions();
assert(subs.hasValue()); assert(subs.hasValue());
@@ -2534,13 +2551,15 @@ ReplaceOpaqueTypesWithUnderlyingTypes::operator()(CanType maybeOpaqueType,
// If the type still contains opaque types, recur. // If the type still contains opaque types, recur.
if (substTy->hasOpaqueArchetype()) { if (substTy->hasOpaqueArchetype()) {
return substRef.substOpaqueTypesWithUnderlyingTypes(substTy); return substRef.substOpaqueTypesWithUnderlyingTypes(
substTy, modulePerformingSubstitution);
} }
return substRef; return substRef;
} }
Type Type::substOpaqueTypesWithUnderlyingTypes() const { Type Type::substOpaqueTypesWithUnderlyingTypes(
ReplaceOpaqueTypesWithUnderlyingTypes replacer; ModuleDecl *modulePerformingSubstitution) const {
ReplaceOpaqueTypesWithUnderlyingTypes replacer(modulePerformingSubstitution);
return subst(replacer, replacer, return subst(replacer, replacer,
SubstFlags::SubstituteOpaqueArchetypes SubstFlags::SubstituteOpaqueArchetypes
// TODO(opaque): Currently lowered types always get opaque types // TODO(opaque): Currently lowered types always get opaque types

View File

@@ -20,6 +20,7 @@ class OpaqueSpecializerCloner
SILBasicBlock *entryBlock; SILBasicBlock *entryBlock;
SILBasicBlock *cloneFromBlock; SILBasicBlock *cloneFromBlock;
ModuleDecl *currentModule;
public: public:
friend class SILCloner<OpaqueSpecializerCloner>; friend class SILCloner<OpaqueSpecializerCloner>;
@@ -30,6 +31,7 @@ public:
true /*ReplacingOpaqueArchetypes*/) { true /*ReplacingOpaqueArchetypes*/) {
entryBlock = fun.getEntryBlock(); entryBlock = fun.getEntryBlock();
cloneFromBlock = entryBlock->split(entryBlock->begin()); cloneFromBlock = entryBlock->split(entryBlock->begin());
currentModule = fun.getModule().getSwiftModule();
} }
void clone(); void clone();
@@ -208,21 +210,23 @@ protected:
} }
protected: protected:
SILType remapType(SILType Ty) { SILType remapType(SILType Ty) {
SILType &Sty = TypeCache[Ty]; SILType &Sty = TypeCache[Ty];
if (!Sty) { if (Sty)
// Apply the opaque types substitution. return Sty;
ReplaceOpaqueTypesWithUnderlyingTypes replacer;
Sty = Ty.subst(Original.getModule(), SubsMap) // Apply the opaque types substitution.
.subst(Original.getModule(), replacer, replacer, ReplaceOpaqueTypesWithUnderlyingTypes replacer(currentModule);
CanGenericSignature(), true); Sty = Ty.subst(Original.getModule(), SubsMap)
} .subst(Original.getModule(), replacer, replacer,
CanGenericSignature(), true);
return Sty; return Sty;
} }
CanType remapASTType(CanType ty) { CanType remapASTType(CanType ty) {
// Apply the opaque types substitution. // Apply the opaque types substitution.
ReplaceOpaqueTypesWithUnderlyingTypes replacer; ReplaceOpaqueTypesWithUnderlyingTypes replacer(currentModule);
return SuperTy::remapASTType(ty) return SuperTy::remapASTType(ty)
.subst(replacer, replacer, .subst(replacer, replacer,
SubstFlags::SubstituteOpaqueArchetypes | SubstFlags::SubstituteOpaqueArchetypes |
@@ -233,7 +237,7 @@ protected:
ProtocolConformanceRef remapConformance(Type type, ProtocolConformanceRef remapConformance(Type type,
ProtocolConformanceRef conf) { ProtocolConformanceRef conf) {
// Apply the opaque types substitution. // Apply the opaque types substitution.
ReplaceOpaqueTypesWithUnderlyingTypes replacer; ReplaceOpaqueTypesWithUnderlyingTypes replacer(currentModule);
return SuperTy::remapConformance(type, conf) return SuperTy::remapConformance(type, conf)
.subst(type, replacer, replacer, .subst(type, replacer, replacer,
SubstFlags::SubstituteOpaqueArchetypes | SubstFlags::SubstituteOpaqueArchetypes |
@@ -242,7 +246,7 @@ protected:
SubstitutionMap remapSubstitutionMap(SubstitutionMap Subs) { SubstitutionMap remapSubstitutionMap(SubstitutionMap Subs) {
// Apply the opaque types substitution. // Apply the opaque types substitution.
ReplaceOpaqueTypesWithUnderlyingTypes replacer; ReplaceOpaqueTypesWithUnderlyingTypes replacer(currentModule);
return SuperTy::remapSubstitutionMap(Subs).subst( return SuperTy::remapSubstitutionMap(Subs).subst(
replacer, replacer, replacer, replacer,
SubstFlags::SubstituteOpaqueArchetypes | SubstFlags::AllowLoweredTypes); SubstFlags::SubstituteOpaqueArchetypes | SubstFlags::AllowLoweredTypes);
@@ -295,13 +299,24 @@ void OpaqueSpecializerCloner::insertOpaqueToConcreteAddressCasts(
namespace { namespace {
class OpaqueArchetypeSpecializer : public SILFunctionTransform { class OpaqueArchetypeSpecializer : public SILFunctionTransform {
void run() override { void run() override {
auto *currentModule = getFunction()->getModule().getSwiftModule();
auto opaqueArchetypeWouldChange = [=](CanType ty) -> bool {
if (!ty->hasOpaqueArchetype())
return false;
ReplaceOpaqueTypesWithUnderlyingTypes replacer(currentModule);
return ty.subst(replacer, replacer,
SubstFlags::SubstituteOpaqueArchetypes |
SubstFlags::AllowLoweredTypes)
->getCanonicalType() != ty;
};
// Look for opaque type archetypes. // Look for opaque type archetypes.
bool foundOpaqueArchetype = false; bool foundOpaqueArchetype = false;
for (auto &BB : *getFunction()) { for (auto &BB : *getFunction()) {
for (auto &inst : BB) { for (auto &inst : BB) {
for (auto &opd : inst.getAllOperands()) { for (auto &opd : inst.getAllOperands()) {
if (!opd.get()->getType().getASTType()->hasOpaqueArchetype()) if (!opaqueArchetypeWouldChange(opd.get()->getType().getASTType()))
continue; continue;
foundOpaqueArchetype = true; foundOpaqueArchetype = true;
break; break;
@@ -309,8 +324,8 @@ class OpaqueArchetypeSpecializer : public SILFunctionTransform {
if (foundOpaqueArchetype) if (foundOpaqueArchetype)
break; break;
auto *allocStack = dyn_cast<AllocStackInst>(&inst); auto *allocStack = dyn_cast<AllocStackInst>(&inst);
if (!allocStack || if (!allocStack || !opaqueArchetypeWouldChange(
!allocStack->getElementType().getASTType()->hasOpaqueArchetype()) allocStack->getElementType().getASTType()))
continue; continue;
foundOpaqueArchetype = true; foundOpaqueArchetype = true;
break; break;
@@ -322,10 +337,8 @@ class OpaqueArchetypeSpecializer : public SILFunctionTransform {
OpaqueSpecializerCloner s(subsMap, *getFunction()); OpaqueSpecializerCloner s(subsMap, *getFunction());
s.clone(); s.clone();
removeUnreachableBlocks(*getFunction()); removeUnreachableBlocks(*getFunction());
}
if (foundOpaqueArchetype)
invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody); invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody);
}
} }
}; };
} // end anonymous namespace } // end anonymous namespace

View File

@@ -2,6 +2,7 @@
// RUN: %target-swift-frontend %S/Inputs/specialize_opaque_type_archetypes_2.swift -module-name External -emit-module -emit-module-path %t/External.swiftmodule // RUN: %target-swift-frontend %S/Inputs/specialize_opaque_type_archetypes_2.swift -module-name External -emit-module -emit-module-path %t/External.swiftmodule
// RUN: %target-swift-frontend %S/Inputs/specialize_opaque_type_archetypes_3.swift -enable-library-evolution -module-name External2 -emit-module -emit-module-path %t/External2.swiftmodule // RUN: %target-swift-frontend %S/Inputs/specialize_opaque_type_archetypes_3.swift -enable-library-evolution -module-name External2 -emit-module -emit-module-path %t/External2.swiftmodule
// RUN: %target-swift-frontend -I %t -module-name A -enforce-exclusivity=checked -Osize -emit-sil -sil-verify-all %s | %FileCheck %s // RUN: %target-swift-frontend -I %t -module-name A -enforce-exclusivity=checked -Osize -emit-sil -sil-verify-all %s | %FileCheck %s
// RUN: %target-swift-frontend -I %t -module-name A -enforce-exclusivity=checked -enable-library-evolution -Osize -emit-sil -sil-verify-all %s | %FileCheck %s
import External import External
import External2 import External2
@@ -112,12 +113,14 @@ public func useExternal() {
useP(e.myValue2()) useP(e.myValue2())
} }
// This should change once opaque types support resilience. // Call to a resilient function should not be specialized.
// CHECK-LABEL: sil @$s1A20useExternalResilientyyF // CHECK-LABEL: sil @$s1A20useExternalResilientyyF
// CHECK: // function_ref Int64.myValue3() // CHECK: [[RES:%.*]] = alloc_stack $@_opaqueReturnTypeOf("$s9External217externalResilientQryF", 0)
// CHECK: [[FUN:%.*]] = function_ref @$ss5Int64V9External2E8myValue3AByF // CHECK: [[FUN:%.*]] = function_ref @$s9External217externalResilientQryF : $@convention(thin) () -> @out @_opaqueReturnTypeOf("$s9External217externalResilientQryF", 0)
// CHECK: apply [[FUN]] // CHECK: apply [[FUN]]([[RES]])
// CHECK: witness_method
// CHECK: return
public func useExternalResilient() { public func useExternalResilient() {
let e = externalResilient() let e = externalResilient()
useP(e.myValue3()) useP(e.myValue3())
@@ -207,6 +210,35 @@ public func usePair() {
useP(x.second.myValue()) useP(x.second.myValue())
} }
struct MyInt64 : ExternalP2 {
var x = Int64(0)
public func myValue3() -> Int64 {
return x + 3
}
}
func nonResilient() -> some ExternalP2 {
return MyInt64()
}
// CHECK-LABEL: sil @$s1A019usePairResilientNonC0yyF : $@convention(thin) () -> ()
// CHECK: alloc_stack $Pair<MyInt64, @_opaqueReturnTypeOf("$s9External217externalResilientQryF", 0)
// CHECK: cond_fail
// CHECK: [[FIRST_MYVALUE3:%.*]] = struct $Int64
// CHECK: [[USEP:%.*]] = function_ref @$s1A4usePyyxAA1PRzlFs5Int64V_Tg5
// CHECK: apply [[USEP]]([[FIRST_MYVALUE3]])
// CHECK: [[MYVALUE_WITNESS:%.*]] = witness_method $@_opaqueReturnTypeOf("$s9External217externalResilientQryF"
// CHECK: [[SECOND_MYVALUE3:%.*]] = apply [[MYVALUE_WITNESS]]
// CHECK: apply [[USEP]]([[SECOND_MYVALUE3]])
// CHECK: return
public func usePairResilientNonResilient() {
var x = Pair(first: nonResilient(), second: externalResilient())
useP(x.first.myValue3())
useP(x.second.myValue3())
}
public protocol P3 { public protocol P3 {
associatedtype AT associatedtype AT
func foo() -> AT func foo() -> AT

View File

@@ -0,0 +1,59 @@
public protocol P {
func getValue() -> Int
}
extension Int : P {
public func getValue() -> Int {
return self + 1
}
}
#if !BEFORE
public struct Pair : P {
var x = 0
var y = 1
public func getValue() -> Int {
return y
}
}
#endif
public func resilientFunction() -> some P {
#if BEFORE
return Int(5)
#else
return Pair()
#endif
}
public func expectedResult() -> Int {
return resilientFunction().getValue()
}
public func expectedSize() -> Int {
return MemoryLayout.size(ofValue: resilientFunction())
}
public struct Container {
public init() {}
public var property : some P {
get {
#if BEFORE
return Int(5)
#else
return Pair()
#endif
}
}
public func expectedResult() -> Int {
return property.getValue()
}
public func expectedSize() -> Int {
return MemoryLayout.size(ofValue: property)
}
}

View File

@@ -0,0 +1,22 @@
// RUN: %target-resilience-test
// REQUIRES: executable_test
// Use swift-version 4.
// UNSUPPORTED: swift_test_mode_optimize_none_with_implicit_dynamic
import opaque_archetypes_change_underlying_type
import StdlibUnittest
var OpaqueArchetypes = TestSuite("OpaqueArchetypes")
OpaqueArchetypes.test("test1") {
let o = resilientFunction()
expectEqual(o.getValue(), expectedResult())
expectEqual(MemoryLayout.size(ofValue: o), expectedSize())
let c = Container()
expectEqual(c.property.getValue(), c.expectedResult())
expectEqual(MemoryLayout.size(ofValue: c.property), c.expectedSize())
}
runAllTests()