Skip to content

Commit c996573

Browse files
committed
Sema: Result builder inference always favors explicit attr on storage decl
1 parent 3d40a8c commit c996573

File tree

3 files changed

+81
-28
lines changed

3 files changed

+81
-28
lines changed

lib/Sema/TypeCheckRequestFunctions.cpp

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -201,30 +201,36 @@ AttachedResultBuilderRequest::evaluate(Evaluator &evaluator,
201201
/// Attempt to infer the result builder type for a declaration.
202202
static Type inferResultBuilderType(ValueDecl *decl) {
203203
auto dc = decl->getDeclContext();
204-
if (!dc->isTypeContext() || isa<ProtocolDecl>(dc))
204+
if (isa<ProtocolDecl>(dc))
205205
return Type();
206206

207207
auto funcDecl = dyn_cast<FuncDecl>(decl);
208208
if (!funcDecl || !funcDecl->hasBody() ||
209209
!decl->getDeclContext()->getParentSourceFile())
210210
return Type();
211211

212-
// Check whether there are any return statements in the function's body.
213-
// If there are, the result builder transform will be disabled,
214-
// so don't infer a result builder.
215-
if (!TypeChecker::findReturnStatements(funcDecl).empty())
216-
return Type();
217-
218-
// Only getters can have result builders. When we find one, look at
219-
// the storage declaration for the purposes of witness matching.
220-
auto lookupDecl = decl;
212+
// For a getter, always favor the result builder type of its storage
213+
// declaration if not null. Other accessors are not supported by inference.
221214
if (auto accessor = dyn_cast<AccessorDecl>(funcDecl)) {
222215
if (accessor->getAccessorKind() != AccessorKind::Get)
223216
return Type();
224217

225-
lookupDecl = accessor->getStorage();
218+
if (auto type = accessor->getStorage()->getResultBuilderType()) {
219+
return type;
220+
}
221+
}
222+
223+
// FIXME: We could infer from a dynamically replaced decl in non-type contexts too.
224+
if (!dc->isTypeContext()) {
225+
return Type();
226226
}
227227

228+
// Check whether there are any return statements in the function's body.
229+
// If there are, the result builder transform will be disabled,
230+
// so don't infer a result builder.
231+
if (!TypeChecker::findReturnStatements(funcDecl).empty())
232+
return Type();
233+
228234
// Find all of the potentially inferred result builder types.
229235
struct Match {
230236
enum Kind {
@@ -323,6 +329,13 @@ static Type inferResultBuilderType(ValueDecl *decl) {
323329
}
324330
};
325331

332+
ValueDecl *lookupDecl = nullptr;
333+
if (auto *accessor = dyn_cast<AccessorDecl>(funcDecl)) {
334+
lookupDecl = accessor->getStorage();
335+
} else {
336+
lookupDecl = decl;
337+
}
338+
326339
addConformanceMatches(lookupDecl);
327340

328341
// Look for result builder types inferred through dynamic replacements.

lib/Sema/TypeCheckStmt.cpp

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2124,20 +2124,6 @@ void TypeChecker::typeCheckASTNode(ASTNode &node, DeclContext *DC,
21242124
stmtChecker.typeCheckASTNode(node);
21252125
}
21262126

2127-
static Type getResultBuilderType(FuncDecl *FD) {
2128-
Type builderType = FD->getResultBuilderType();
2129-
2130-
// For getters, fall back on looking on the attribute on the storage.
2131-
if (!builderType) {
2132-
auto accessor = dyn_cast<AccessorDecl>(FD);
2133-
if (accessor && accessor->getAccessorKind() == AccessorKind::Get) {
2134-
builderType = accessor->getStorage()->getResultBuilderType();
2135-
}
2136-
}
2137-
2138-
return builderType;
2139-
}
2140-
21412127
/// Attempts to build an implicit call within the provided constructor
21422128
/// to the provided class's zero-argument super initializer.
21432129
/// @returns nullptr if there was an error and a diagnostic was emitted.
@@ -2617,7 +2603,7 @@ bool TypeCheckASTNodeAtLocRequest::evaluate(
26172603

26182604
// Function builder function doesn't support partial type checking.
26192605
if (auto *func = dyn_cast<FuncDecl>(DC)) {
2620-
if (Type builderType = getResultBuilderType(func)) {
2606+
if (Type builderType = func->getResultBuilderType()) {
26212607
if (func->getBody()) {
26222608
auto optBody =
26232609
TypeChecker::applyResultBuilderBodyTransform(func, builderType);
@@ -2694,7 +2680,7 @@ static void addImplicitReturnIfNeeded(BraceStmt *body, DeclContext *dc) {
26942680

26952681
if (auto *fd = dyn_cast<FuncDecl>(dc)) {
26962682
// Don't apply if we have a result builder, or a Void return type.
2697-
if (getResultBuilderType(fd) || fd->getResultInterfaceType()->isVoid())
2683+
if (fd->getResultBuilderType() || fd->getResultInterfaceType()->isVoid())
26982684
return;
26992685
}
27002686

@@ -2894,7 +2880,7 @@ TypeCheckFunctionBodyRequest::evaluate(Evaluator &eval,
28942880
// produce a type-checked body.
28952881
bool alreadyTypeChecked = false;
28962882
if (auto *func = dyn_cast<FuncDecl>(AFD)) {
2897-
if (Type builderType = getResultBuilderType(func)) {
2883+
if (Type builderType = func->getResultBuilderType()) {
28982884
if (auto optBody =
28992885
TypeChecker::applyResultBuilderBodyTransform(
29002886
func, builderType)) {

test/Constraints/result_builder_infer.swift

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,28 @@ struct TupleMe2: Tupled, Tupled2 {
142142
}
143143
}
144144

145+
protocol Tupled3 {
146+
associatedtype TupleType
147+
148+
var tuple: TupleType { @TupleBuilder get }
149+
}
150+
151+
struct TupleMe3: Tupled3 {
152+
var condition: Bool
153+
154+
// FIXME: Not inferred from getter requirement
155+
// expected-error@+1 {{function declares an opaque return type, but has no return statements in its body from which to infer an underlying type}}
156+
var tuple: some Any {
157+
"hello" // expected-warning{{literal is unused}}
158+
if condition {
159+
"nested" // expected-warning{{literal is unused}}
160+
}
161+
3.14159 // expected-warning{{literal is unused}}
162+
"world" // expected-warning{{literal is unused}}
163+
// expected-note@-1 {{did you mean to return the last expression?}}
164+
}
165+
}
166+
145167
protocol OtherTupled {
146168
associatedtype OtherTupleType
147169

@@ -233,3 +255,35 @@ extension DynamicTupled3: OtherTupled {
233255
0
234256
}
235257
}
258+
259+
do {
260+
@resultBuilder
261+
enum BuildBoolFrom<T> {
262+
static func buildBlock(_ t: T...) -> Bool {}
263+
}
264+
265+
protocol P {
266+
@BuildBoolFrom<String>
267+
var property: Bool { get }
268+
}
269+
270+
struct Conformer1: P {
271+
@BuildBoolFrom<Int>
272+
var property: Bool {
273+
// OK, explicit result builder disables inference through protocol.
274+
1
275+
2
276+
}
277+
}
278+
279+
struct Conformer2: P {
280+
var property: Bool {
281+
@BuildBoolFrom<Int>
282+
get {
283+
// OK, explicit result builder disables inference through protocol.
284+
1
285+
2
286+
}
287+
}
288+
}
289+
}

0 commit comments

Comments
 (0)