Skip to content

Commit 6b323f1

Browse files
committed
[BuilderTransform] Verify that builder type has at least one accessible build{Partial}Block
Check accessibility of all build{Partial}Block overloads and diagnose if none of them are as accessible as type, otherwise swift interfaces could end up with invalid result builder declarations when type is public and all builder methods are internal. Resolves: rdar://104384604
1 parent dabec23 commit 6b323f1

File tree

7 files changed

+154
-5
lines changed

7 files changed

+154
-5
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6659,7 +6659,21 @@ ERROR(cannot_declare_computed_var_in_result_builder,none,
66596659
ERROR(cannot_convert_result_builder_result_to_return_type,none,
66606660
"cannot convert result builder result type %0 to return type %1",
66616661
(Type,Type))
6662-
6662+
ERROR(result_builder_buildblock_not_accessible,none,
6663+
"result builder must provide at least one static 'buildBlock' as "
6664+
"accessible as result builder type %0 "
6665+
"(which is %select{private|fileprivate|internal|package|public|open}1)",
6666+
(DeclName, AccessLevel))
6667+
ERROR(result_builder_buildpartialblock_first_not_accessible,none,
6668+
"result builder must provide at least one static 'buildPartialBlock(first:)' "
6669+
"as accessible as result builder type %0 "
6670+
"(which is %select{private|fileprivate|internal|package|public|open}1)",
6671+
(DeclName, AccessLevel))
6672+
ERROR(result_builder_buildpartialblock_accumulated_not_accessible,none,
6673+
"result builder must provide at least one static 'buildPartialBlock(accumulated:next:)' "
6674+
"as accessible as result builder type %0 "
6675+
"(which is %select{private|fileprivate|internal|package|public|open}1)",
6676+
(DeclName, AccessLevel))
66636677

66646678
//------------------------------------------------------------------------------
66656679
// MARK: Tuple Shuffle Diagnostics

lib/Sema/TypeCheckAttr.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3997,6 +3997,69 @@ void AttributeChecker::visitResultBuilderAttr(ResultBuilderAttr *attr) {
39973997
diagnose(member->getLoc(),
39983998
diag::result_builder_buildblock_not_static_method);
39993999
}
4000+
4001+
return;
4002+
}
4003+
4004+
// Let's check whether one or more overloads of buildBlock or
4005+
// buildPartialBlock are as accessible as the builder type itself.
4006+
{
4007+
auto isBuildMethodAsAccessibleAsType = [&](ValueDecl *member) {
4008+
return !isMemberLessAccessibleThanType(nominal, member);
4009+
};
4010+
4011+
bool hasAccessibleBuildBlock =
4012+
llvm::any_of(buildBlockMatches, isBuildMethodAsAccessibleAsType);
4013+
4014+
bool hasAccessibleBuildPartialBlockFirst = false;
4015+
bool hasAccessibleBuildPartialBlockAccumulated = false;
4016+
4017+
if (supportsBuildPartialBlock) {
4018+
DeclName buildPartialBlockFirst(ctx, ctx.Id_buildPartialBlock,
4019+
/*argLabels=*/{ctx.Id_first});
4020+
DeclName buildPartialBlockAccumulated(
4021+
ctx, ctx.Id_buildPartialBlock,
4022+
/*argLabels=*/{ctx.Id_accumulated, ctx.Id_next});
4023+
4024+
buildPartialBlockFirstMatches.clear();
4025+
buildPartialBlockAccumulatedMatches.clear();
4026+
4027+
auto builderType = nominal->getDeclaredType();
4028+
nominal->lookupQualified(builderType, DeclNameRef(buildPartialBlockFirst),
4029+
NL_QualifiedDefault,
4030+
buildPartialBlockFirstMatches);
4031+
nominal->lookupQualified(
4032+
builderType, DeclNameRef(buildPartialBlockAccumulated),
4033+
NL_QualifiedDefault, buildPartialBlockAccumulatedMatches);
4034+
4035+
hasAccessibleBuildPartialBlockFirst = llvm::any_of(
4036+
buildPartialBlockFirstMatches, isBuildMethodAsAccessibleAsType);
4037+
hasAccessibleBuildPartialBlockAccumulated = llvm::any_of(
4038+
buildPartialBlockAccumulatedMatches, isBuildMethodAsAccessibleAsType);
4039+
}
4040+
4041+
if (!hasAccessibleBuildBlock) {
4042+
// No or incomplete `buildPartialBlock` and all overloads of
4043+
// `buildBlock(_:)` are less accessible than the type.
4044+
if (!supportsBuildPartialBlock) {
4045+
diagnose(nominal->getLoc(),
4046+
diag::result_builder_buildblock_not_accessible,
4047+
nominal->getName(), nominal->getFormalAccess());
4048+
} else {
4049+
if (!hasAccessibleBuildPartialBlockFirst) {
4050+
diagnose(nominal->getLoc(),
4051+
diag::result_builder_buildpartialblock_first_not_accessible,
4052+
nominal->getName(), nominal->getFormalAccess());
4053+
}
4054+
4055+
if (!hasAccessibleBuildPartialBlockAccumulated) {
4056+
diagnose(
4057+
nominal->getLoc(),
4058+
diag::result_builder_buildpartialblock_accumulated_not_accessible,
4059+
nominal->getName(), nominal->getFormalAccess());
4060+
}
4061+
}
4062+
}
40004063
}
40014064
}
40024065

test/IDE/print_swift_module.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public func returnsAlias() -> Alias<Int> {
2929

3030
@resultBuilder
3131
public struct BridgeBuilder {
32-
static func buildBlock(_: Any...) {}
32+
public static func buildBlock(_: Any...) {}
3333
}
3434

3535
public struct City {

test/NameLookup/Inputs/custom_attrs_A.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ public struct Wrapper<Value> {
99

1010
@resultBuilder
1111
public struct Builder {
12-
static func buildBlock<T>(_ component: T) -> T { component }
12+
public static func buildBlock<T>(_ component: T) -> T { component }
1313
}

test/NameLookup/Inputs/custom_attrs_B.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ public struct Wrapper<Value> {
99

1010
@resultBuilder
1111
public struct Builder {
12-
static func buildBlock<T>(_ component: T) -> T { component }
12+
public static func buildBlock<T>(_ component: T) -> T { component }
1313
}

test/Serialization/Inputs/def_macros.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public struct TestMacroArgTypechecking {
2727

2828
@resultBuilder
2929
public struct Builder {
30-
static func buildBlock(_: Int...) -> Void {}
30+
public static func buildBlock(_: Int...) -> Void {}
3131
}
3232
@freestanding(expression)
3333
public macro macroWithBuilderArgs(@Builder _: () -> Void) = #externalMacro(module: "A", type: "B")

test/attr/attr_result_builder.swift

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,75 @@
44
struct MyBuilder {
55
static func buildBlock(_: Any...) -> Any { }
66
}
7+
8+
// rdar://104384604 - empty result builder in swiftinterface file
9+
@resultBuilder
10+
public struct TestInvalidBuildBlock1 {
11+
// expected-error@-1 {{result builder must provide at least one static 'buildBlock' as accessible as result builder type 'TestInvalidBuildBlock1' (which is public)}}
12+
static func buildBlock(_: Int) -> Int { 42 }
13+
}
14+
15+
@resultBuilder
16+
public struct TestInvalidBuildBlock2 { // Ok
17+
static func buildBlock(_: Int) -> Int { 42 }
18+
public static func buildBlock(_: String) -> String { "" }
19+
}
20+
21+
@resultBuilder
22+
public struct TestInvalidBuildPartialBlockFirst1 {
23+
// expected-error@-1 {{result builder must provide at least one static 'buildPartialBlock(first:)' as accessible as result builder type 'TestInvalidBuildPartialBlockFirst1' (which is public)}}
24+
static func buildPartialBlock(first: Int) -> Int { first }
25+
public static func buildPartialBlock(accumulated: Int, next: Int) -> Int { accumulated + next }
26+
}
27+
28+
@resultBuilder
29+
public struct TestInvalidBuildPartialBlockFirst2 { // Ok
30+
static func buildPartialBlock(first: Int) -> Int { first }
31+
public static func buildPartialBlock<T>(first: T) -> T { first }
32+
public static func buildPartialBlock(accumulated: Int, next: Int) -> Int { accumulated + next }
33+
}
34+
35+
@resultBuilder
36+
public struct TestInvalidBuildPartialBlockAccumulated1 {
37+
// expected-error@-1 {{result builder must provide at least one static 'buildPartialBlock(accumulated:next:)' as accessible as result builder type 'TestInvalidBuildPartialBlockAccumulated1' (which is public)}}
38+
public static func buildPartialBlock(first: Int) -> Int { first }
39+
private static func buildPartialBlock(accumulated: Int, next: Int) -> Int { accumulated + next }
40+
}
41+
42+
@resultBuilder
43+
public struct TestInvalidBuildPartialBlockAccumulated2 { // Ok
44+
public static func buildPartialBlock<T>(first: T) -> T { first }
45+
public static func buildPartialBlock<T>(accumulated: T, next: T) -> T { fatalError() }
46+
47+
private static func buildPartialBlock(accumulated: Int, next: Int) -> Int { accumulated + next }
48+
}
49+
50+
@resultBuilder
51+
public struct TestBuildPartialBlock1 { // Ok
52+
public static func buildPartialBlock(first: Int) -> Int { first }
53+
public static func buildPartialBlock(accumulated: Int, next: Int) -> Int { accumulated + next }
54+
}
55+
56+
@resultBuilder
57+
public struct TestBuildPartialBlock2 { // Ok
58+
private static func buildBlock(_: Int) -> Int { 42 }
59+
60+
public static func buildPartialBlock(first: Int) -> Int { first }
61+
public static func buildPartialBlock(accumulated: Int, next: Int) -> Int { accumulated + next }
62+
}
63+
64+
@resultBuilder
65+
public struct TestBuildPartialBlock3 { // Ok
66+
public static func buildBlock(_: Int) -> Int { 42 }
67+
68+
fileprivate static func buildPartialBlock(first: Int) -> Int { first }
69+
fileprivate static func buildPartialBlock(accumulated: Int, next: Int) -> Int { accumulated + next }
70+
}
71+
72+
@resultBuilder
73+
public struct TestBuildPartialBlock4 { // Ok
74+
public static func buildBlock(_: Int) -> Int { 42 }
75+
76+
public static func buildPartialBlock(first: Int) -> Int { first }
77+
public static func buildPartialBlock(accumulated: Int, next: Int) -> Int { accumulated + next }
78+
}

0 commit comments

Comments
 (0)