Skip to content

Commit 1368bc9

Browse files
committed
SILGen: Rewrite captured local archetypes into primary archetypes
This implements support for autoclosures, closures and local functions nested within a pack iteration for loop. The combination of explicit closure expressions and pack expansion expressions still needs some work. Fixes #66917. Fixes #69947. Fixes rdar://113505724. Fixes rdar://122293832. Fixes rdar://124329076.
1 parent 093f255 commit 1368bc9

12 files changed

+326
-0
lines changed

lib/SILGen/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ add_swift_host_library(swiftSILGen STATIC
2828
SILGenFunction.cpp
2929
SILGenGlobalVariable.cpp
3030
SILGenLazyConformance.cpp
31+
SILGenLocalArchetype.cpp
3132
SILGenLValue.cpp
3233
SILGenPack.cpp
3334
SILGenPattern.cpp

lib/SILGen/SILGen.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -1260,6 +1260,9 @@ void SILGenModule::postEmitFunction(SILDeclRef constant,
12601260
SILFunction *F) {
12611261
emitLazyConformancesForFunction(F);
12621262

1263+
auto sig = Types.getGenericSignatureWithCapturedEnvironments(constant);
1264+
recontextualizeCapturedLocalArchetypes(F, sig);
1265+
12631266
assert(!F->isExternalDeclaration() && "did not emit any function body?!");
12641267
LLVM_DEBUG(llvm::dbgs() << "lowered sil:\n";
12651268
F->print(llvm::dbgs()));

lib/SILGen/SILGen.h

+5
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
625625
/// Emit a property descriptor for the given storage decl if it needs one.
626626
void tryEmitPropertyDescriptor(AbstractStorageDecl *decl);
627627

628+
/// Replace local archetypes captured from outer AST contexts with primary
629+
/// archetypes.
630+
void recontextualizeCapturedLocalArchetypes(
631+
SILFunction *F, GenericSignatureWithCapturedEnvironments sig);
632+
628633
private:
629634
/// The most recent declaration we considered for emission.
630635
SILDeclRef lastEmittedFunction;

lib/SILGen/SILGenLazyConformance.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
12+
//
13+
// This file forces emission of lazily-generated ClangImporter-synthesized
14+
// conformances.
15+
//
16+
//===----------------------------------------------------------------------===//
1217

1318
#include "SILGen.h"
1419
#include "swift/AST/Decl.h"

lib/SILGen/SILGenLocalArchetype.cpp

+149
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
//===--- SILGenLocalArchetype.cpp - Local archetype transform -------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file implements the transformation which rewrites captured local
14+
// archetypes into primary archetypes in the enclosing function's generic
15+
// signature.
16+
//
17+
//===----------------------------------------------------------------------===//
18+
19+
#include "SILGen.h"
20+
#include "swift/AST/LocalArchetypeRequirementCollector.h"
21+
#include "swift/SIL/SILInstruction.h"
22+
#include "swift/SIL/SILCloner.h"
23+
24+
using namespace swift;
25+
using namespace swift::Lowering;
26+
27+
namespace {
28+
29+
class LocalArchetypeTransform : public SILCloner<LocalArchetypeTransform> {
30+
friend class SILCloner<LocalArchetypeTransform>;
31+
friend class SILInstructionVisitor<LocalArchetypeTransform>;
32+
33+
GenericSignatureWithCapturedEnvironments sig;
34+
GenericEnvironment *env;
35+
36+
public:
37+
LocalArchetypeTransform(SILFunction *F,
38+
GenericSignatureWithCapturedEnvironments sig)
39+
: SILCloner(*F), env(sig.genericSig.getGenericEnvironment()) {
40+
41+
assert(!sig.capturedEnvs.empty() && "Why are we doing this?");
42+
43+
// The primary archetypes of the old generic environment map to
44+
// primary archetypes of the new generic environment at the same
45+
// index and depth.
46+
Functor.SubsMap = env->getForwardingSubstitutionMap();
47+
48+
// Local archetypes map to generic parameters at higher depths.
49+
MapLocalArchetypesOutOfContext mapOutOfContext(sig.baseGenericSig,
50+
sig.capturedEnvs);
51+
52+
// For each captured environment...
53+
for (auto *capturedEnv : sig.capturedEnvs) {
54+
// For each introduced generic parameter...
55+
auto localParams = capturedEnv->getGenericSignature()
56+
.getInnermostGenericParams();
57+
for (auto *gp : localParams) {
58+
// Get the local archetype from the captured environment.
59+
auto origArchetypeTy = capturedEnv->mapTypeIntoContext(gp)
60+
->castTo<LocalArchetypeType>();
61+
62+
// Map the local archetype to an interface type in the new generic
63+
// signature.
64+
auto substInterfaceTy = mapOutOfContext(origArchetypeTy);
65+
66+
// Map this interface type into the new generic environment to get
67+
// a primary archetype.
68+
auto substArchetypeTy = env->mapTypeIntoContext(substInterfaceTy)
69+
->castTo<PrimaryArchetypeType>();
70+
71+
// Remember this correspondence.
72+
registerLocalArchetypeRemapping(origArchetypeTy, substArchetypeTy);
73+
}
74+
}
75+
}
76+
77+
void doIt() {
78+
auto &F = getBuilder().getFunction();
79+
80+
// Collect the old basic blocks that we're going to delete.
81+
llvm::SmallVector<SILBasicBlock *, 4> bbs;
82+
for (auto &bb : F)
83+
bbs.push_back(&bb);
84+
85+
// Make F.mapTypeIntoContext() use the new environment.
86+
F.setGenericEnvironment(env);
87+
88+
// Start by cloning the entry block.
89+
auto *origEntryBlock = F.getEntryBlock();
90+
auto *clonedEntryBlock = F.createBasicBlock();
91+
92+
// Clone arguments.
93+
SmallVector<SILValue, 4> entryArgs;
94+
entryArgs.reserve(origEntryBlock->getArguments().size());
95+
for (auto &origArg : origEntryBlock->getArguments()) {
96+
97+
// Remap the argument type into the new generic environment.
98+
SILType mappedType = remapType(origArg->getType());
99+
auto *NewArg = clonedEntryBlock->createFunctionArgument(
100+
mappedType, origArg->getDecl(), true);
101+
NewArg->copyFlags(cast<SILFunctionArgument>(origArg));
102+
entryArgs.push_back(NewArg);
103+
}
104+
105+
// Clone the remaining body.
106+
getBuilder().setInsertionPoint(clonedEntryBlock);
107+
cloneFunctionBody(&F, clonedEntryBlock, entryArgs,
108+
true /*replaceOriginalFunctionInPlace*/);
109+
110+
// Insert the new entry block at the beginning.
111+
F.moveBlockBefore(clonedEntryBlock, F.begin());
112+
113+
// FIXME: This should be a common utility.
114+
115+
// Erase the old basic blocks.
116+
for (auto *bb : bbs) {
117+
for (SILArgument *arg : bb->getArguments()) {
118+
arg->replaceAllUsesWithUndef();
119+
// To appease the ownership verifier, just set to None.
120+
arg->setOwnershipKind(OwnershipKind::None);
121+
}
122+
123+
// Instructions in the dead block may be used by other dead blocks. Replace
124+
// any uses of them with undef values.
125+
while (!bb->empty()) {
126+
// Grab the last instruction in the bb.
127+
auto *inst = &bb->back();
128+
129+
// Replace any still-remaining uses with undef values and erase.
130+
inst->replaceAllUsesOfAllResultsWithUndef();
131+
inst->eraseFromParent();
132+
}
133+
134+
// Finally, erase the basic block itself.
135+
bb->eraseFromParent();
136+
}
137+
}
138+
};
139+
140+
} // end anonymous namespace
141+
142+
void SILGenModule::recontextualizeCapturedLocalArchetypes(
143+
SILFunction *F, GenericSignatureWithCapturedEnvironments sig) {
144+
if (sig.capturedEnvs.empty())
145+
return;
146+
147+
LocalArchetypeTransform(F, sig).doIt();
148+
M.reclaimUnresolvedLocalArchetypeDefinitions();
149+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// RUN: %target-run-simple-swift
2+
3+
func callee<X: P1, T: P2, U: P3, V: P4>(x: X, t: T, u: U, v: V)
4+
-> (any P1, any P2, any P3, any P4) {
5+
return (x, t, u, v)
6+
}
7+
8+
protocol P1 { var f1: Bool { get } }
9+
protocol P2 { var f2: Bool { get } }
10+
protocol P3 { var f3: Bool { get } }
11+
protocol P4 { var f4: Bool { get } }
12+
13+
func packFunction<X, each T, each U, each V>(x: X,
14+
ts: repeat each T,
15+
us: repeat each U,
16+
vs: repeat each V)
17+
where X: P1,
18+
(repeat (each T, each U)): Any,
19+
repeat each T: P2,
20+
repeat each U: P3,
21+
repeat each V: P4 {
22+
23+
var result: [(any P1, any P2, any P3, any P4)] = []
24+
var bools: [Bool] = []
25+
26+
for t in repeat each ts {
27+
for u in repeat each us {
28+
for v in repeat each vs {
29+
30+
// Local function
31+
do {
32+
func localFunc() {
33+
result.append(callee(x: x, t: t, u: u, v: v))
34+
bools.append(true && x.f1 && t.f2 && u.f3 && v.f4)
35+
}
36+
37+
localFunc()
38+
let fn = localFunc
39+
fn()
40+
41+
let closure = { localFunc() }
42+
closure()
43+
}
44+
45+
// Generic local function
46+
do {
47+
func localFunc<Y: P1>(_ y: Y) {
48+
result.append(callee(x: y, t: t, u: u, v: v))
49+
bools.append(true && y.f1 && t.f2 && u.f3 && v.f4)
50+
}
51+
52+
localFunc(x)
53+
let fn: (X) -> () = localFunc
54+
fn(x)
55+
56+
let closure = { localFunc(x) }
57+
closure()
58+
}
59+
}
60+
}
61+
}
62+
63+
assert(result.count == bools.count)
64+
65+
for (r, b) in zip(result, bools) {
66+
assert(b == (r.0.f1 && r.1.f2 && r.2.f3 && r.3.f4))
67+
}
68+
}
69+
70+
struct S1: P1 { var f1: Bool }
71+
struct S2: P2 { var f2: Bool }
72+
struct S3: P3 { var f3: Bool }
73+
struct S4: P4 { var f4: Bool }
74+
75+
packFunction(x: S1(f1: true),
76+
ts: S2(f2: true), S2(f2: true),
77+
us: S3(f3: true), S3(f3: false),
78+
vs: S4(f4: false), S4(f4: false), S4(f4: true), S4(f4: true))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: %target-swift-emit-silgen %s | %FileCheck %s
2+
3+
// CHECK-LABEL: sil hidden [ossa] @$s26element_archetype_captures6calleeyyx_q_q0_tSTRzSTR_7ElementQy_ACRtzr1_lF : $@convention(thin) <T, U, V where T : Sequence, U : Sequence, T.Element == U.Element> (@in_guaranteed T, @in_guaranteed U, @in_guaranteed V) -> () {
4+
5+
func callee<T, U, V>(_: T, _: U, _: V) where T: Sequence, U: Sequence, T.Element == U.Element {}
6+
7+
// CHECK-LABEL: sil hidden [ossa] @$s26element_archetype_captures12packFunction2ts2us2vsyxxQp_q_xQpq0_q0_QptRvzRv_Rv0_STRzSTR_7ElementQy_AFRtzr1_lF : $@convention(thin) <each T, each U, each V where repeat each T : Sequence, repeat each U : Sequence, repeat (each T).Element == (each U).Element> (@pack_guaranteed Pack{repeat each T}, @pack_guaranteed Pack{repeat each U}, @pack_guaranteed Pack{repeat each V}) -> () {
8+
func packFunction<each T, each U, each V>(ts: repeat each T, us: repeat each U, vs: repeat each V)
9+
where repeat each T: Sequence,
10+
repeat each U: Sequence,
11+
repeat (each T).Element == (each U).Element {
12+
for (t, u) in repeat (each ts, each us) {
13+
14+
// // CHECK-LABEL: sil private [ossa] @$s26element_archetype_captures12packFunction2ts2us2vsyxxQp_q_xQpq0_q0_QptRvzRv_Rv0_STRzSTR_7ElementQy_AFRtzr1_lF10middleFuncL_yyRvzRv_Rv0_STRzSTR_AgHRSr1_lF : $@convention(thin) <each T, each U, each V where repeat each T : Sequence, repeat each U : Sequence, repeat (each T).Element == (each U).Element><τ_1_0, τ_1_1 where τ_1_0 : Sequence, τ_1_1 : Sequence, τ_1_0.Element == τ_1_1.Element> (@in_guaranteed (repeat each V), @in_guaranteed τ_1_0, @in_guaranteed τ_1_1) -> () {
15+
func middleFunc() {
16+
for v in repeat each vs {
17+
18+
// CHECK-LABEL: sil private [ossa] @$s26element_archetype_captures12packFunction2ts2us2vsyxxQp_q_xQpq0_q0_QptRvzRv_Rv0_STRzSTR_7ElementQy_AFRtzr1_lF10middleFuncL_yyRvzRv_Rv0_STRzSTR_AgHRSr1_lF05innerK0L_yyRvzRv_Rv0_STRzSTR_AgHRSr1_lF : $@convention(thin) <each T, each U, each V where repeat each T : Sequence, repeat each U : Sequence, repeat (each T).Element == (each U).Element><τ_1_0, τ_1_1 where τ_1_0 : Sequence, τ_1_1 : Sequence, τ_1_0.Element == τ_1_1.Element><τ_2_0> (@in_guaranteed τ_1_0, @in_guaranteed τ_1_1, @in_guaranteed τ_2_0) -> () {
19+
func innerFunc() {
20+
21+
// CHECK: [[FN:%.*]] = function_ref @$s26element_archetype_captures6calleeyyx_q_q0_tSTRzSTR_7ElementQy_ACRtzr1_lF : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2 where τ_0_0 : Sequence, τ_0_1 : Sequence, τ_0_0.Element == τ_0_1.Element> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1, @in_guaranteed τ_0_2) -> ()
22+
// CHECK: apply [[FN]]<τ_1_0, τ_1_1, τ_2_0>(%0, %1, %2) : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2 where τ_0_0 : Sequence, τ_0_1 : Sequence, τ_0_0.Element == τ_0_1.Element> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1, @in_guaranteed τ_0_2) -> ()
23+
callee(t, u, v)
24+
}
25+
26+
innerFunc()
27+
}
28+
}
29+
30+
middleFunc()
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// RUN: %target-swift-frontend -emit-ir %s
2+
3+
public func values<each T>() -> (repeat (each T)?) {
4+
(repeat { () -> (each T)? in
5+
nil
6+
}())
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: %target-swift-frontend -emit-ir %s
2+
3+
class Thing<T> {
4+
var value: T
5+
init(_ value: T) { self.value = value }
6+
7+
func combineThings<each U>(head: Thing<T>, tail: repeat Thing<each U>) {
8+
repeat (each tail).doSomething(each tail) { _ in }
9+
}
10+
11+
func doSomething(_ value: AnyObject, closure: @escaping (T) -> Void) {}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %target-swift-frontend -emit-ir %s
2+
3+
public struct G<T> {
4+
var value: T { fatalError() }
5+
}
6+
7+
public func f<T>(transform: () -> T) {}
8+
9+
public func g<T, each V>(_ v: repeat G<each V>?, transform: (repeat (each V)?) -> T) {
10+
f(transform: { transform(repeat (each v)?.value ?? nil) })
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// RUN: %target-swift-frontend -emit-ir %s
2+
3+
public func withOptionalsAsPointers<T, each Opt>(
4+
_ optional: repeat Optional<each Opt>,
5+
body: (repeat UnsafePointer<each Opt>?) throws -> T
6+
) rethrows -> T {
7+
return try body(repeat (each optional).map { withUnsafePointer(to: $0) { $0 } })
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %target-swift-frontend -emit-ir %s
2+
3+
func withPointerToElements<each T, E, R>(
4+
of tuple: borrowing (repeat each T),
5+
_ body: (UnsafeBufferPointer<E>) -> R
6+
) -> R {
7+
for t in repeat (each T).self {
8+
if t != E.self {
9+
preconditionFailure()
10+
}
11+
}
12+
return withUnsafeBytes(of: tuple) { p in
13+
return body(p.assumingMemoryBound(to: E.self))
14+
}
15+
}

0 commit comments

Comments
 (0)