SIL: Fix lowering for 'var's whose types contain local archetypes

A mutable 'var' becomes a SILBoxType, and we need to plumb the
correct generic signature through here too.

Fixes https://github.com/apple/swift/issues/71921.
This commit is contained in:
Slava Pestov
2024-06-13 23:14:41 -04:00
parent 61d00cacd6
commit e4d6108e11
5 changed files with 168 additions and 52 deletions

View File

@@ -1253,13 +1253,25 @@ public:
SILDeclRef constant,
CanAnyFunctionType origInterfaceType);
/// Get the boxed interface type to use for a capture of the given decl.
/// Get the interface type for a box that holds a mutable local 'var',
/// substituted for a closure that captures some superset of the local
/// environments captured by the 'var'.
CanSILBoxType
getInterfaceBoxTypeForCapture(ValueDecl *captured,
CanType loweredInterfaceType,
CanType loweredContextType,
GenericSignature genericSig,
ArrayRef<GenericEnvironment *> capturedEnvs,
bool isMutable);
/// Get the boxed contextual type to use for a capture of the given decl
/// in the given generic environment.
/// Get the interface type for a box that holds a mutable local 'var',
/// given that the interface type of the 'var' might capture local
/// archetypes.
CanSILBoxType
getInterfaceBoxTypeForCapture(ValueDecl *captured,
CanType loweredContextType,
bool isMutable);
/// Get the contextual type for a box that holds a mutable local 'var'.
CanSILBoxType
getContextBoxTypeForCapture(ValueDecl *captured,
CanType loweredContextType,

View File

@@ -2036,13 +2036,12 @@ lowerCaptureContextParameters(TypeConverter &TC, SILDeclRef function,
assert(!type->hasLocalArchetype() ||
(genericSig && origGenericSig &&
!genericSig->isEqual(origGenericSig)));
type = mapTypeOutOfContext(type);
auto canType = type->getReducedType(
auto interfaceType = mapTypeOutOfContext(type)->getReducedType(
genericSig ? genericSig : origGenericSig);
auto &loweredTL =
TC.getTypeLowering(AbstractionPattern(genericSig, canType), canType,
expansion);
TC.getTypeLowering(AbstractionPattern(genericSig, interfaceType),
interfaceType, expansion);
auto loweredTy = loweredTL.getLoweredType();
switch (TC.getDeclCaptureKind(capture, expansion)) {
case CaptureKind::Constant: {
@@ -2065,12 +2064,13 @@ lowerCaptureContextParameters(TypeConverter &TC, SILDeclRef function,
// The type in the box is lowered in the minimal context.
auto minimalLoweredTy =
TC.getTypeLowering(AbstractionPattern(genericSig, canType), canType,
TC.getTypeLowering(AbstractionPattern(type), type,
TypeExpansionContext::minimal())
.getLoweredType();
// Lvalues are captured as a box that owns the captured value.
auto boxTy = TC.getInterfaceBoxTypeForCapture(
varDecl, minimalLoweredTy.getASTType(),
genericSig, capturedEnvs,
/*mutable*/ true);
auto convention = ParameterConvention::Direct_Guaranteed;
auto param = SILParameterInfo(boxTy, convention, options);
@@ -2084,12 +2084,13 @@ lowerCaptureContextParameters(TypeConverter &TC, SILDeclRef function,
// The type in the box is lowered in the minimal context.
auto minimalLoweredTy =
TC.getTypeLowering(AbstractionPattern(genericSig, canType), canType,
TC.getTypeLowering(AbstractionPattern(type), type,
TypeExpansionContext::minimal())
.getLoweredType();
// Lvalues are captured as a box that owns the captured value.
auto boxTy = TC.getInterfaceBoxTypeForCapture(
varDecl, minimalLoweredTy.getASTType(),
genericSig, capturedEnvs,
/*mutable*/ false);
auto convention = ParameterConvention::Direct_Guaranteed;
auto param = SILParameterInfo(boxTy, convention, options);

View File

@@ -32,6 +32,7 @@
#include "swift/AST/SourceFile.h"
#include "swift/AST/TypeDifferenceVisitor.h"
#include "swift/AST/Types.h"
#include "swift/Basic/LLVMExtras.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/SIL/AbstractionPatternGenerators.h"
#include "swift/SIL/PrettyStackTrace.h"
@@ -4886,14 +4887,93 @@ TypeConverter::checkFunctionForABIDifferences(SILModule &M,
return ABIDifference::CompatibleRepresentation;
}
static void findCapturedEnvironments(
Type type,
SmallSetVector<GenericEnvironment *, 2> &boxCapturedEnvs) {
type.visit([&](Type t) {
if (auto *archetypeTy = t->getAs<LocalArchetypeType>()) {
boxCapturedEnvs.insert(archetypeTy->getGenericEnvironment());
}
});
}
CanSILBoxType
TypeConverter::getInterfaceBoxTypeForCapture(ValueDecl *captured,
CanType loweredInterfaceType,
CanType loweredContextType,
GenericSignature genericSig,
ArrayRef<GenericEnvironment *> capturedEnvs,
bool isMutable) {
auto boxType = getInterfaceBoxTypeForCapture(captured,
loweredContextType,
isMutable);
LLVM_DEBUG(llvm::dbgs() << "Generic signature of closure: "
<< genericSig << "\n";);
LLVM_DEBUG(llvm::dbgs() << "Box type: "
<< boxType << "\n";);
auto &C = M.getASTContext();
auto signature = getCanonicalSignatureOrNull(
auto baseGenericSig = getCanonicalSignatureOrNull(
captured->getDeclContext()->getGenericSignatureOfContext());
SmallSetVector<GenericEnvironment *, 2> boxCapturedEnvs;
findCapturedEnvironments(loweredContextType, boxCapturedEnvs);
return cast<SILBoxType>(Type(boxType).subst(
[&](SubstitutableType *t) -> Type {
auto *paramTy = cast<GenericTypeParamType>(t);
// Depth of first captured local archetype in box generic signature.
unsigned depth = baseGenericSig.getNextDepth();
// Is this a captured local archetype?
if (paramTy->getDepth() >= depth) {
// Get the environment.
auto *genericEnv = boxCapturedEnvs[paramTy->getDepth() - depth];
// Find this environment in the captured environments of our
// closure.
auto found = std::find(capturedEnvs.begin(), capturedEnvs.end(),
genericEnv);
assert(found != capturedEnvs.end());
unsigned capturedEnvIndex = found - capturedEnvs.begin();
// Remap the depth. This is necessary because the 'var' box might
// capture a subset of the captured environments of the closure.
return GenericTypeParamType::get(
/*isParameterPack=*/false,
genericSig.getNextDepth() - capturedEnvs.size() + capturedEnvIndex,
paramTy->getIndex(),
C);
}
return paramTy;
},
MakeAbstractConformanceForGenericType(),
SubstFlags::PreservePackExpansionLevel |
SubstFlags::AllowLoweredTypes)->getCanonicalType());
}
CanSILBoxType
TypeConverter::getInterfaceBoxTypeForCapture(ValueDecl *captured,
CanType loweredContextType,
bool isMutable) {
auto &C = M.getASTContext();
auto baseGenericSig = getCanonicalSignatureOrNull(
captured->getDeclContext()->getGenericSignatureOfContext());
SmallSetVector<GenericEnvironment *, 2> boxCapturedEnvs;
findCapturedEnvironments(loweredContextType, boxCapturedEnvs);
MapLocalArchetypesOutOfContext mapOutOfContext(baseGenericSig,
boxCapturedEnvs.getArrayRef());
auto loweredInterfaceType = loweredContextType.subst(
mapOutOfContext,
MakeAbstractConformanceForGenericType(),
SubstFlags::PreservePackExpansionLevel |
SubstFlags::AllowLoweredTypes)->getCanonicalType();
// If the type is not dependent at all, we can form a concrete box layout.
// We don't need to capture the generic environment.
if (!loweredInterfaceType->hasTypeParameter()) {
@@ -4902,39 +4982,23 @@ TypeConverter::getInterfaceBoxTypeForCapture(ValueDecl *captured,
/*captures generics*/ false);
return SILBoxType::get(C, layout, {});
}
auto boxGenericSig = buildGenericSignatureWithCapturedEnvironments(
M.getASTContext(), baseGenericSig,
boxCapturedEnvs.getArrayRef()).getCanonicalSignature();
// Otherwise, the layout needs to capture the generic environment of its
// originating scope.
// TODO: We could conceivably minimize the captured generic environment to
// only the parts used by the captured variable.
auto layout = SILLayout::get(C, signature,
auto layout = SILLayout::get(C, boxGenericSig,
SILField(loweredInterfaceType, isMutable),
/*captures generics*/ false);
// Instantiate the layout with identity substitutions.
auto subMap = signature->getIdentitySubstitutionMap();
auto subMap = boxGenericSig->getIdentitySubstitutionMap();
auto boxTy = SILBoxType::get(C, layout, subMap);
#ifndef NDEBUG
auto loweredContextType = loweredInterfaceType;
auto contextBoxTy = boxTy;
if (signature) {
auto env = signature.getGenericEnvironment();
loweredContextType = env->mapTypeIntoContext(loweredContextType)
->getCanonicalType();
contextBoxTy = cast<SILBoxType>(
env->mapTypeIntoContext(contextBoxTy)
->getCanonicalType());
}
auto ty = getSILBoxFieldType(TypeExpansionContext::minimal(), contextBoxTy,
*this, 0);
assert(contextBoxTy->getLayout()->getFields().size() == 1 &&
ty.getRawASTType() == loweredContextType &&
"box field type doesn't match capture!");
#endif
return boxTy;
return SILBoxType::get(C, layout, subMap);
}
CanSILBoxType
@@ -4942,24 +5006,20 @@ TypeConverter::getContextBoxTypeForCapture(ValueDecl *captured,
CanType loweredContextType,
GenericEnvironment *env,
bool isMutable) {
CanType loweredInterfaceType = loweredContextType;
if (env) {
auto homeSig = captured->getDeclContext()
->getGenericSignatureOfContext();
loweredInterfaceType =
loweredInterfaceType->mapTypeOutOfContext()
->getReducedType(homeSig);
}
SmallSetVector<GenericEnvironment *, 2> boxCapturedEnvs;
findCapturedEnvironments(loweredContextType, boxCapturedEnvs);
auto boxType = getInterfaceBoxTypeForCapture(captured,
loweredInterfaceType,
loweredContextType,
isMutable);
if (env)
boxType = cast<SILBoxType>(
env->mapTypeIntoContext(boxType)
->getCanonicalType());
return boxType;
MapIntoLocalArchetypeContext mapIntoContext(env, boxCapturedEnvs.getArrayRef());
return cast<SILBoxType>(
Type(boxType).subst(mapIntoContext,
LookUpConformanceInModule(&M),
SubstFlags::PreservePackExpansionLevel |
SubstFlags::AllowLoweredTypes)->getCanonicalType());
}
CanSILBoxType TypeConverter::getBoxTypeForEnumElement(

View File

@@ -41,3 +41,25 @@ public func anotherPackFunction<each T>(_ ts: repeat each T) {
}
}
}
public func varCaptures<each T, each U>(ts: repeat each T, us: repeat each U) {
for t in repeat each ts {
for u in repeat each us {
var both = (t, u)
both = (t, u)
let capture_both = { both = (t, u) }
capture_both()
var just_u = u
just_u = u
let capture_u = { _ = t; just_u = u }
capture_u()
var just_t = t
just_t = t
let capture_t = { just_t = t; _ = u }
capture_t()
}
}
}

View File

@@ -0,0 +1,21 @@
// RUN: %target-swift-frontend -emit-ir %s -disable-availability-checking
public protocol Signal {
mutating func process() -> Float
}
public struct Mixer<each Source: Signal> {
public var sources: (repeat each Source)
public mutating func process() -> Float {
var result: Float = 0
self.sources = (repeat ({
var signal = $0
result += signal.process()
return signal
}(each sources)))
return result
}
}