Skip to content

Commit 3cdc30f

Browse files
committed
[Sema] Support type erasure for dynamic replacement in function
builders.
1 parent 3a361c7 commit 3cdc30f

File tree

6 files changed

+77
-33
lines changed

6 files changed

+77
-33
lines changed

lib/Sema/BuilderTransform.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,10 @@ class BuilderClosureVisitor
210210
return None;
211211

212212
applied.returnExpr = buildVarRef(bodyVar, stmt->getEndLoc());
213+
applied.returnExpr = cs->buildTypeErasedExpr(applied.returnExpr,
214+
dc, applied.bodyResultType,
215+
CTP_ReturnStmt);
216+
213217
applied.returnExpr = cs->generateConstraints(applied.returnExpr, dc);
214218
if (!applied.returnExpr) {
215219
hadError = true;

lib/Sema/CSApply.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7417,6 +7417,7 @@ ExprWalker::rewriteTarget(SolutionApplicationTarget target) {
74177417
return None;
74187418

74197419
result.setFunctionBody(newBody);
7420+
fn.getAbstractFunctionDecl()->setHasSingleExpressionBody(false);
74207421
}
74217422

74227423
// Follow-up tasks.

lib/Sema/CSGen.cpp

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3903,38 +3903,6 @@ static bool generateInitPatternConstraints(
39033903
return false;
39043904
}
39053905

3906-
static Expr *eraseTypeForDynamicReplacement(SolutionApplicationTarget target,
3907-
Expr *expr) {
3908-
auto purpose = target.getExprContextualTypePurpose();
3909-
auto *dc = target.getDeclContext();
3910-
auto *decl = dyn_cast_or_null<ValueDecl>(dc->getAsDecl());
3911-
if (!decl)
3912-
return expr;
3913-
3914-
if (!(purpose == CTP_ReturnStmt || purpose == CTP_ReturnSingleExpr) ||
3915-
!(decl->isDynamic() || decl->getDynamicallyReplacedDecl()))
3916-
return expr;
3917-
3918-
auto *opaque =
3919-
target.getExprContextualType()->getAs<OpaqueTypeArchetypeType>();
3920-
if (!opaque)
3921-
return expr;
3922-
3923-
auto protocols = opaque->getConformsTo();
3924-
if (protocols.size() != 1)
3925-
return expr;
3926-
3927-
auto *attr = protocols.front()->getAttrs().getAttribute<TypeEraserAttr>();
3928-
if (!attr)
3929-
return expr;
3930-
3931-
auto typeEraser = attr->getTypeEraserLoc().getType();
3932-
auto &ctx = dc->getASTContext();
3933-
return CallExpr::createImplicit(ctx,
3934-
TypeExpr::createImplicit(typeEraser, ctx),
3935-
{expr}, {ctx.Id_erasing});
3936-
}
3937-
39383906
bool ConstraintSystem::generateConstraints(
39393907
SolutionApplicationTarget &target,
39403908
FreeTypeVariableBinding allowFreeTypeVariables) {
@@ -3951,7 +3919,9 @@ bool ConstraintSystem::generateConstraints(
39513919
target.setExprConversionType(TypeChecker::getOptionalType(expr->getLoc(), var));
39523920
}
39533921

3954-
expr = eraseTypeForDynamicReplacement(target, expr);
3922+
expr = buildTypeErasedExpr(expr, target.getDeclContext(),
3923+
target.getExprContextualType(),
3924+
target.getExprContextualTypePurpose());
39553925

39563926
// Generate constraints for the main system.
39573927
expr = generateConstraints(expr, target.getDeclContext());

lib/Sema/ConstraintSystem.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4062,6 +4062,36 @@ Expr *ConstraintSystem::buildAutoClosureExpr(Expr *expr,
40624062
return result;
40634063
}
40644064

4065+
Expr *ConstraintSystem::buildTypeErasedExpr(Expr *expr, DeclContext *dc,
4066+
Type contextualType,
4067+
ContextualTypePurpose purpose) {
4068+
if (!(purpose == CTP_ReturnStmt || purpose == CTP_ReturnSingleExpr))
4069+
return expr;
4070+
4071+
auto *decl = dyn_cast_or_null<ValueDecl>(dc->getAsDecl());
4072+
if (!decl ||
4073+
!(decl->isDynamic() || decl->getDynamicallyReplacedDecl()))
4074+
return expr;
4075+
4076+
auto *opaque = contextualType->getAs<OpaqueTypeArchetypeType>();
4077+
if (!opaque)
4078+
return expr;
4079+
4080+
auto protocols = opaque->getConformsTo();
4081+
if (protocols.size() != 1)
4082+
return expr;
4083+
4084+
auto *attr = protocols.front()->getAttrs().getAttribute<TypeEraserAttr>();
4085+
if (!attr)
4086+
return expr;
4087+
4088+
auto typeEraser = attr->getTypeEraserLoc().getType();
4089+
auto &ctx = dc->getASTContext();
4090+
return CallExpr::createImplicit(ctx,
4091+
TypeExpr::createImplicit(typeEraser, ctx),
4092+
{expr}, {ctx.Id_erasing});
4093+
}
4094+
40654095
/// If an UnresolvedDotExpr, SubscriptMember, etc has been resolved by the
40664096
/// constraint system, return the decl that it references.
40674097
ValueDecl *ConstraintSystem::findResolvedMemberRef(ConstraintLocator *locator) {

lib/Sema/ConstraintSystem.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3564,6 +3564,21 @@ class ConstraintSystem {
35643564
/// Given expression represents computed result of the closure.
35653565
Expr *buildAutoClosureExpr(Expr *expr, FunctionType *closureType);
35663566

3567+
/// Builds a type-erased return expression that can be used in dynamic
3568+
/// replacement.
3569+
///
3570+
/// An expression needs type erasure if:
3571+
/// 1. The expression is a return value.
3572+
/// 2. The enclosing function is dynamic or a dynamic replacement.
3573+
/// 3. The enclosing function returns an opaque type.
3574+
/// 4. The opaque type conforms to (exactly) one protocol, and the protocol
3575+
/// has a declared type eraser.
3576+
///
3577+
/// \returns the transformed return expression, or the original expression if
3578+
/// no type erasure is needed.
3579+
Expr *buildTypeErasedExpr(Expr *expr, DeclContext *dc, Type contextualType,
3580+
ContextualTypePurpose purpose);
3581+
35673582
private:
35683583
/// Determines whether or not a given conversion at a given locator requires
35693584
/// the creation of a temporary value that's only valid for a limited scope.

test/Sema/type_eraser.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,27 @@ dynamic func opaque() -> some P {
1616
ConcreteP()
1717
}
1818

19+
class AnyQ: Q {
20+
init<T: Q>(erasing: T) {}
21+
}
22+
@_typeEraser(AnyQ)
23+
protocol Q {}
24+
25+
struct ConcreteQ: Q {}
26+
27+
@_functionBuilder
28+
struct Builder {
29+
static func buildBlock(_ params: Q...) -> ConcreteQ {
30+
return ConcreteQ()
31+
}
32+
}
33+
34+
// CHECK-LABEL: transformFnBody
35+
@Builder
36+
dynamic var transformFnBody: some Q {
37+
// CHECK: return_stmt
38+
// CHECK-NEXT: underlying_to_opaque_expr implicit type='some Q'
39+
// CHECK-NEXT: call_expr implicit type='AnyQ'{{.*}}arg_labels=erasing:
40+
// CHECK: declref_expr implicit type='@lvalue ConcreteQ'
41+
ConcreteQ()
42+
}

0 commit comments

Comments
 (0)