Skip to content

Commit 8470938

Browse files
committed
[Sema] RuntimeMetadata: Add support for mutating methods via a thunk
Instead of passing an unapplied reference to a (either static or instance) method, let's form a thunk and use it to forward arguments to the underlying method invocation. This way it's possible to get a uniform type for all instance methods that always starts with `self` type and supports `mutating` methods but marking `self` as `inout`.
1 parent 28493d0 commit 8470938

File tree

3 files changed

+178
-48
lines changed

3 files changed

+178
-48
lines changed

lib/Sema/TypeCheckRuntimeMetadataAttr.cpp

Lines changed: 150 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,154 @@ ValueDecl::getRuntimeDiscoverableAttributeGenerator(CustomAttr *attr) const {
4646
return std::make_pair(body, init->getType()->mapTypeOutOfContext());
4747
}
4848

49+
static TypeRepr *buildTypeRepr(DeclContext *typeContext) {
50+
assert(typeContext->isTypeContext());
51+
52+
SmallVector<ComponentIdentTypeRepr *, 2> components;
53+
54+
auto &ctx = typeContext->getASTContext();
55+
DeclContext *DC = typeContext;
56+
while (!DC->isModuleContext()) {
57+
auto *NTD = DC->getSelfNominalTypeDecl();
58+
// Only contiguous chains of nominals and extensions thereof.
59+
if (!NTD)
60+
break;
61+
62+
auto *component = new (ctx) SimpleIdentTypeRepr(
63+
/*Loc=*/DeclNameLoc(), NTD->createNameRef());
64+
65+
// Resolve the component right away, instead of
66+
// involving name lookup. This plays well with
67+
// the fact that initializer is anchored on a
68+
// source file.
69+
component->setValue(NTD, NTD->getDeclContext());
70+
71+
components.push_back(component);
72+
DC = NTD->getDeclContext();
73+
}
74+
75+
// Reverse the components to form a valid outer-to-inner name sequence.
76+
std::reverse(components.begin(), components.end());
77+
78+
if (components.size() == 1)
79+
return components.front();
80+
81+
return CompoundIdentTypeRepr::create(ctx, components);
82+
}
83+
84+
/// Synthesizes a closure thunk that forwards all of the arguments
85+
/// to the underlying method. This is required to support mutating
86+
/// methods and create uniform signatures where for instance methods
87+
/// first parameter is always `self` (with or without `inout`).
88+
static ClosureExpr *synthesizeMethodThunk(DeclContext *thunkDC,
89+
NominalTypeDecl *nominal,
90+
FuncDecl *method) {
91+
auto &ctx = method->getASTContext();
92+
93+
// If this is a method, let's form a thunk so that attribute initializer
94+
// gets `([inout] T, Argument, ...) -> Result` signature.
95+
auto *funcParams = method->getParameters();
96+
97+
SmallVector<ParamDecl *, 4> closureParams;
98+
99+
NullablePtr<ParamDecl> selfParam;
100+
if (!method->isStatic()) {
101+
auto *self = ParamDecl::createImplicit(
102+
ctx,
103+
/*argumentName=*/Identifier(),
104+
/*parameterName=*/ctx.Id_self,
105+
/*type=*/nominal->getDeclaredInterfaceType(), thunkDC,
106+
method->isMutating() ? ParamSpecifier::InOut : ParamSpecifier::Default);
107+
108+
// This is very important for the solver, without a type repr
109+
// it would create a type variable and attempt infer the type
110+
// from the body.
111+
self->setTypeRepr(buildTypeRepr(nominal));
112+
113+
closureParams.push_back(self);
114+
selfParam = self;
115+
}
116+
117+
if (funcParams) {
118+
unsigned anonIdx = 0;
119+
for (auto *param : *funcParams) {
120+
auto name = param->getParameterName();
121+
// Cannot leave parameter anonymous because it would be
122+
// referenced in the body.
123+
if (name.empty())
124+
name = ctx.getIdentifier((Twine("$anon") + Twine(anonIdx++)).str());
125+
126+
auto *closureParam = ParamDecl::createImplicit(
127+
ctx,
128+
/*argumentName=*/Identifier(),
129+
/*parameterName=*/name, param->getInterfaceType(), thunkDC,
130+
param->getSpecifier());
131+
closureParam->setTypeRepr(param->getTypeRepr());
132+
133+
closureParams.push_back(closureParam);
134+
}
135+
}
136+
137+
// return self.<func>(<arguments>)
138+
SmallVector<ASTNode, 2> body;
139+
{
140+
NullablePtr<Expr> baseExpr;
141+
if (method->isStatic()) {
142+
baseExpr =
143+
TypeExpr::createImplicit(nominal->getDeclaredInterfaceType(), ctx);
144+
} else {
145+
baseExpr = new (ctx) DeclRefExpr({selfParam.get()}, /*Loc=*/DeclNameLoc(),
146+
/*implicit=*/true);
147+
}
148+
149+
auto *memberRef = new (ctx) MemberRefExpr(
150+
baseExpr.get(), /*dotLoc=*/SourceLoc(), {method}, /*loc=*/DeclNameLoc(),
151+
/*Implicit=*/true);
152+
153+
SmallVector<Argument, 4> arguments;
154+
if (funcParams) {
155+
for (unsigned i = 0, n = funcParams->size(); i != n; ++i) {
156+
const auto *param = funcParams->get(i);
157+
158+
Expr *argExpr = new (ctx)
159+
DeclRefExpr({closureParams[method->isStatic() ? i : i + 1]},
160+
/*Loc=*/DeclNameLoc(),
161+
/*implicit=*/true);
162+
163+
if (param->isInOut()) {
164+
argExpr = new (ctx) InOutExpr(/*operLoc=*/SourceLoc(), argExpr,
165+
Type(), /*implicit=*/true);
166+
}
167+
168+
arguments.push_back(
169+
{/*labelLoc=*/SourceLoc(), param->getArgumentName(), argExpr});
170+
}
171+
}
172+
173+
auto *call = CallExpr::createImplicit(
174+
ctx, memberRef, ArgumentList::createImplicit(ctx, arguments));
175+
176+
body.push_back(new (ctx) ReturnStmt(/*ReturnLoc=*/SourceLoc(), call,
177+
/*implicit=*/true));
178+
}
179+
180+
DeclAttributes attrs;
181+
auto *closure = new (ctx) ClosureExpr(
182+
attrs, /*bracketRange=*/SourceRange(),
183+
/*capturedSelf=*/nullptr, ParameterList::create(ctx, closureParams),
184+
/*asyncLoc=*/SourceLoc(),
185+
/*throwsLoc=*/SourceLoc(),
186+
/*arrowLoc=*/SourceLoc(),
187+
/*inLoc=*/SourceLoc(),
188+
/*explicitResultType=*/nullptr, thunkDC);
189+
190+
closure->setBody(BraceStmt::createImplicit(ctx, body),
191+
/*isSingleExpr=*/true);
192+
closure->setImplicit();
193+
194+
return closure;
195+
}
196+
49197
Expr *SynthesizeRuntimeMetadataAttrGenerator::evaluate(
50198
Evaluator &evaluator, CustomAttr *attr, ValueDecl *attachedTo) const {
51199
auto &ctx = attachedTo->getASTContext();
@@ -77,13 +225,7 @@ Expr *SynthesizeRuntimeMetadataAttrGenerator::evaluate(
77225
DotSelfExpr(metatype, /*dot=*/SourceLoc(), /*self=*/SourceLoc());
78226
} else if (auto *func = dyn_cast<FuncDecl>(attachedTo)) {
79227
if (auto *nominal = func->getDeclContext()->getSelfNominalTypeDecl()) {
80-
auto *baseExpr =
81-
TypeExpr::createImplicit(nominal->getDeclaredInterfaceType(), ctx);
82-
83-
// Form an initializer call passing in the function reference
84-
initArgument = new (ctx) MemberRefExpr(baseExpr, /*dotLoc=*/SourceLoc(),
85-
{func}, /*loc=*/DeclNameLoc(),
86-
/*Implicit=*/true);
228+
initArgument = synthesizeMethodThunk(initContext, nominal, func);
87229
} else {
88230
initArgument = new (ctx)
89231
DeclRefExpr({func}, /*Loc=*/DeclNameLoc(), /*implicit=*/true);
@@ -102,44 +244,7 @@ Expr *SynthesizeRuntimeMetadataAttrGenerator::evaluate(
102244
// Build a type repr for base of the key path, since attribute
103245
// could be attached to an inner type, we need to go up decl
104246
// contexts and add every parent type.
105-
{
106-
SmallVector<ComponentIdentTypeRepr *, 2> baseNameComponents;
107-
108-
auto *DC = var->getDeclContext();
109-
while (!DC->isModuleContext()) {
110-
auto *NTD = DC->getSelfNominalTypeDecl();
111-
// Only contiguous chains of nominals and extensions thereof.
112-
if (!NTD)
113-
break;
114-
115-
auto *component = new (ctx) SimpleIdentTypeRepr(
116-
/*Loc=*/DeclNameLoc(), NTD->createNameRef());
117-
118-
// Resolve the component right away, instead of
119-
// involving name lookup. This plays well with
120-
// the fact that initializer is anchored on a
121-
// source file.
122-
component->setValue(NTD, NTD->getDeclContext());
123-
124-
baseNameComponents.push_back(component);
125-
DC = NTD->getDeclContext();
126-
}
127-
128-
// Reverse the components to form a valid outer-to-inner name sequence.
129-
std::reverse(baseNameComponents.begin(), baseNameComponents.end());
130-
131-
// Set the 'root' of the key path to the newly build base name.
132-
// We cannot do this via `parsedRoot` because it has strict
133-
// rules about leading-dot.
134-
TypeRepr *rootName = nullptr;
135-
if (baseNameComponents.size() == 1) {
136-
rootName = baseNameComponents.front();
137-
} else {
138-
rootName = CompoundIdentTypeRepr::create(ctx, baseNameComponents);
139-
}
140-
141-
keyPath->setRootType(rootName);
142-
}
247+
keyPath->setRootType(buildTypeRepr(var->getDeclContext()));
143248

144249
initArgument = keyPath;
145250
}

test/IRGen/runtime_attributes.swift

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
// CHECK: @"$s18runtime_attributes20attrIsHigherThanFuncyycvpfaAA13FlagWithAvailHF"
4949
// CHECK: @"$s18runtime_attributes9OuterTypeV5InnerV15innerInstFnTestyycvpfaAA07FlagForE7MethodsHF"
5050
// CHECK: @"$s18runtime_attributes9OuterTypeV5InnerV17innerStaticFnTestyycvpZfaAA07FlagForE7MethodsHF"
51+
// CHECK: @"$s18runtime_attributes9OuterTypeV5InnerV07mutableE6FnTest1x_ySS_SiztcvpfaAA07FlagForE7MethodsHF"
52+
// CHECK: @"$s18runtime_attributes9OuterTypeV15outerMutatingFnSiycvpfaAA19FlagForInnerMethodsHF"
5153

5254
// CHECK: @"$s18runtime_attributes4FlagVHa" = internal constant { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } { i32 0, i32 trunc (i64 sub (i64 ptrtoint (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s18runtime_attributes4FlagVMn" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }, { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }* @"$s18runtime_attributes4FlagVHa", i32 0, i32 1) to i64)) to i32), i32 16, {{.*}} @"$s18runtime_attributes4FlagVHa", i32 0, i32 34) to i64)) to i32) }, section "__TEXT, __swift5_rattrs, regular", align 4
5355

@@ -61,7 +63,7 @@
6163

6264
// CHECK: @"$s18runtime_attributes13FlagWithAvailVHa" = internal constant { i32, i32, i32, i32, i32 } { i32 0, i32 trunc (i64 sub (i64 ptrtoint (<{ i32, i32, i32, i32, i32, i32, i32 }>* @"$s18runtime_attributes13FlagWithAvailVMn" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32 }, { i32, i32, i32, i32, i32 }* @"$s18runtime_attributes13FlagWithAvailVHa", i32 0, i32 1) to i64)) to i32), i32 1, i32 trunc (i64 sub (i64 ptrtoint (<{ [3 x i8], i8 }>* @"symbolic yyc" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32 }, { i32, i32, i32, i32, i32 }* @"$s18runtime_attributes13FlagWithAvailVHa", i32 0, i32 3) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (%swift.accessible_function* @"$s18runtime_attributes20attrIsHigherThanFuncyycvpfaAA13FlagWithAvailHF" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32 }, { i32, i32, i32, i32, i32 }* @"$s18runtime_attributes13FlagWithAvailVHa", i32 0, i32 4) to i64)) to i32) }, section "__TEXT, __swift5_rattrs, regular", align 4
6365

64-
// CHECK: @"$s18runtime_attributes19FlagForInnerMethodsVHa" = internal constant { i32, i32, i32, i32, i32, i32, i32 } { i32 0, i32 trunc (i64 sub (i64 ptrtoint (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s18runtime_attributes19FlagForInnerMethodsVMn" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32, i32, i32 }, { i32, i32, i32, i32, i32, i32, i32 }* @"$s18runtime_attributes19FlagForInnerMethodsVHa", i32 0, i32 1) to i64)) to i32), i32 2, i32 trunc (i64 sub (i64 ptrtoint (<{ [3 x i8], i8, i32, [1 x i8], i8 }>* @"symbolic yyc_____c 18runtime_attributes9OuterTypeV5InnerV" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32, i32, i32 }, { i32, i32, i32, i32, i32, i32, i32 }* @"$s18runtime_attributes19FlagForInnerMethodsVHa", i32 0, i32 3) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (%swift.accessible_function* @"$s18runtime_attributes9OuterTypeV5InnerV15innerInstFnTestyycvpfaAA07FlagForE7MethodsHF" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32, i32, i32 }, { i32, i32, i32, i32, i32, i32, i32 }* @"$s18runtime_attributes19FlagForInnerMethodsVHa", i32 0, i32 4) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (<{ [3 x i8], i8, i32, [2 x i8], i8 }>* @"symbolic yyc_____mc 18runtime_attributes9OuterTypeV5InnerV" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32, i32, i32 }, { i32, i32, i32, i32, i32, i32, i32 }* @"$s18runtime_attributes19FlagForInnerMethodsVHa", i32 0, i32 5) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (%swift.accessible_function* @"$s18runtime_attributes9OuterTypeV5InnerV17innerStaticFnTestyycvpZfaAA07FlagForE7MethodsHF" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32, i32, i32 }, { i32, i32, i32, i32, i32, i32, i32 }* @"$s18runtime_attributes19FlagForInnerMethodsVHa", i32 0, i32 6) to i64)) to i32) }, section "__TEXT, __swift5_rattrs, regular", align 4
66+
// CHECK: @"$s18runtime_attributes19FlagForInnerMethodsVHa" = internal constant { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } { i32 0, i32 trunc (i64 sub (i64 ptrtoint (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s18runtime_attributes19FlagForInnerMethodsVMn" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }, { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }* @"$s18runtime_attributes19FlagForInnerMethodsVHa", i32 0, i32 1) to i64)) to i32), i32 4, i32 trunc (i64 sub (i64 ptrtoint (<{ [3 x i8], i8, i32, [1 x i8], i8 }>* @"symbolic yyc_____c 18runtime_attributes9OuterTypeV5InnerV" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }, { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }* @"$s18runtime_attributes19FlagForInnerMethodsVHa", i32 0, i32 3) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (%swift.accessible_function* @"$s18runtime_attributes9OuterTypeV5InnerV15innerInstFnTestyycvpfaAA07FlagForE7MethodsHF" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }, { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }* @"$s18runtime_attributes19FlagForInnerMethodsVHa", i32 0, i32 4) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (<{ [3 x i8], i8, i32, [2 x i8], i8 }>* @"symbolic yyc_____mc 18runtime_attributes9OuterTypeV5InnerV" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }, { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }* @"$s18runtime_attributes19FlagForInnerMethodsVHa", i32 0, i32 5) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (%swift.accessible_function* @"$s18runtime_attributes9OuterTypeV5InnerV17innerStaticFnTestyycvpZfaAA07FlagForE7MethodsHF" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }, { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }* @"$s18runtime_attributes19FlagForInnerMethodsVHa", i32 0, i32 6) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (<{ [9 x i8], i8, i32, [2 x i8], i8 }>* @"symbolic ySS_Siztc_____zc 18runtime_attributes9OuterTypeV5InnerV" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }, { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }* @"$s18runtime_attributes19FlagForInnerMethodsVHa", i32 0, i32 7) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (%swift.accessible_function* @"$s18runtime_attributes9OuterTypeV5InnerV07mutableE6FnTest1x_ySS_SiztcvpfaAA07FlagForE7MethodsHF" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }, { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }* @"$s18runtime_attributes19FlagForInnerMethodsVHa", i32 0, i32 8) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (<{ [4 x i8], i8, i32, [2 x i8], i8 }>* @"symbolic Siyc_____zc 18runtime_attributes9OuterTypeV" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }, { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }* @"$s18runtime_attributes19FlagForInnerMethodsVHa", i32 0, i32 9) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (%swift.accessible_function* @"$s18runtime_attributes9OuterTypeV15outerMutatingFnSiycvpfaAA19FlagForInnerMethodsHF" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }, { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }* @"$s18runtime_attributes19FlagForInnerMethodsVHa", i32 0, i32 10) to i64)) to i32) }, section "__TEXT, __swift5_rattrs, regular", align 4
6567

6668
import RAD
6769

@@ -95,7 +97,7 @@ struct A {
9597
// CHECK-LABEL: define hidden swiftcc void @"$s18runtime_attributes1AV5test1SiycvpZfaAA4Flag"(%T18runtime_attributes4FlagVySiGSg* noalias nocapture sret(%T18runtime_attributes4FlagVySiGSg) %0)
9698
@Flag static func test1() -> Int { 42 }
9799

98-
// CHECK-LABEL: define hidden swiftcc void @"$s18runtime_attributes1AV5test2yycvpfaAA4Flag"(%T18runtime_attributes4FlagVyyycGSg* noalias nocapture sret(%T18runtime_attributes4FlagVyyycGSg) %0)
100+
// CHECK-LABEL: define hidden swiftcc void @"$s18runtime_attributes1AV5test2yycvpfaAA4Flag"(%T18runtime_attributes4FlagVyytGSg* noalias nocapture sret(%T18runtime_attributes4FlagVyytGSg) %0)
99101
@Flag("test2") func test2() {} // Ok
100102

101103
// CHECK-LABEL: define hidden swiftcc void @"$s18runtime_attributes1AV1xSaySiGSgvpfaAA4Flag"(%T18runtime_attributes4FlagVySaySiGSgGSg* noalias nocapture sret(%T18runtime_attributes4FlagVySaySiGSgGSg) %0)
@@ -221,8 +223,10 @@ func test_local_type_with_protocol_conformance() {
221223

222224
@runtimeMetadata
223225
struct FlagForInnerMethods<Result> {
224-
init<T>(attachedTo: (T) -> () -> Result) {}
225226
init(attachedTo: () -> Result) {}
227+
init<T>(attachedTo: (T) -> Result) {}
228+
init<T>(attachedTo: (inout T) -> Result) {}
229+
init<T>(attachedTo: (inout T, String, inout Int) -> Result) {}
226230
}
227231

228232
struct OuterType {
@@ -231,5 +235,13 @@ struct OuterType {
231235
@FlagForInnerMethods func innerInstFnTest() {}
232236
// CHECK-LABEL: define hidden swiftcc void @"$s18runtime_attributes9OuterTypeV5InnerV17innerStaticFnTestyycvpZfaAA07FlagForE7Methods"(%T18runtime_attributes19FlagForInnerMethodsVyytGSg* noalias nocapture sret(%T18runtime_attributes19FlagForInnerMethodsVyytGSg) %0)
233237
@FlagForInnerMethods static func innerStaticFnTest() {}
238+
239+
// CHECK-LABEL: define hidden swiftcc void @"$s18runtime_attributes9OuterTypeV5InnerV07mutableE6FnTest1x_ySS_SiztcvpfaAA07FlagForE7Methods"(%T18runtime_attributes19FlagForInnerMethodsVyytGSg* noalias nocapture sret(%T18runtime_attributes19FlagForInnerMethodsVyytGSg) %0)
240+
@FlagForInnerMethods mutating func mutableInnerFnTest(x: String, _ y: inout Int) {}
234241
}
235242
}
243+
244+
extension OuterType {
245+
// CHECK-LABEL: define hidden swiftcc void @"$s18runtime_attributes9OuterTypeV15outerMutatingFnSiycvpfaAA19FlagForInnerMethods"(%T18runtime_attributes19FlagForInnerMethodsVySiGSg* noalias nocapture sret(%T18runtime_attributes19FlagForInnerMethodsVySiGSg) %0)
246+
@FlagForInnerMethods mutating func outerMutatingFn() -> Int { 42 }
247+
}

test/type/runtime_discoverable_attrs.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,16 @@ extension AttrWithInitsInExts {
172172
@AttrWithInitsInExts
173173
struct TestAttrWithExts { // Ok
174174
}
175+
176+
@runtimeMetadata
177+
struct FlagForMutating {
178+
init<T, Result>(attachedTo: (inout T) -> Result) {}
179+
init<T, Result>(attachedTo: (inout T, String, inout Int, (String, [Int])) -> Result) {}
180+
}
181+
182+
struct TestMutatingMethods {
183+
@FlagForMutating mutating func noArgs() -> Int { 42 } // Ok
184+
@FlagForMutating mutating func yesArgs(_: String, x: inout Int, _ data: (String, [Int])) -> (q: String, a: Int) {
185+
(q: "", a: 42)
186+
}
187+
}

0 commit comments

Comments
 (0)