Skip to content

Commit 6c24bc5

Browse files
committed
[AST][SILGen] model ABI-safe casts of LValues
We needed a way to describe an ABI-safe cast of an address representing an LValue to implement `@preconcurrency` and its injection of casts during accesses of members. This new AST node, `ABISafeConversionExpr` models what is essentially an `unchecked_addr_cast` in SIL when accessing the LVAlue. As of now I simply implemented it and the verification of the node for the concurrency needs to ensure that it's not misused by accident. If it finds use outside of that, feel free to update the verifier.
1 parent e70fbbc commit 6c24bc5

File tree

10 files changed

+120
-14
lines changed

10 files changed

+120
-14
lines changed

include/swift/AST/Expr.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3157,6 +3157,19 @@ class LoadExpr : public ImplicitConversionExpr {
31573157
static bool classof(const Expr *E) { return E->getKind() == ExprKind::Load; }
31583158
};
31593159

3160+
/// ABISafeConversion - models a type conversion on an l-value that has no
3161+
/// material affect on the ABI of the type, while *preserving* the l-valueness
3162+
/// of the type.
3163+
class ABISafeConversionExpr : public ImplicitConversionExpr {
3164+
public:
3165+
ABISafeConversionExpr(Expr *subExpr, Type type)
3166+
: ImplicitConversionExpr(ExprKind::ABISafeConversion, subExpr, type) {}
3167+
3168+
static bool classof(const Expr *E) {
3169+
return E->getKind() == ExprKind::ABISafeConversion;
3170+
}
3171+
};
3172+
31603173
/// This is a conversion from an expression of UnresolvedType to an arbitrary
31613174
/// other type, and from an arbitrary type to UnresolvedType. This node does
31623175
/// not appear in valid code, only in code involving diagnostics.

include/swift/AST/ExprNodes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ ABSTRACT_EXPR(Apply, Expr)
152152
EXPR_RANGE(Apply, Call, ConstructorRefCall)
153153
ABSTRACT_EXPR(ImplicitConversion, Expr)
154154
EXPR(Load, ImplicitConversionExpr)
155+
EXPR(ABISafeConversion, ImplicitConversionExpr)
155156
EXPR(DestructureTuple, ImplicitConversionExpr)
156157
EXPR(UnresolvedTypeConversion, ImplicitConversionExpr)
157158
EXPR(FunctionConversion, ImplicitConversionExpr)

lib/AST/ASTDumper.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2276,6 +2276,11 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
22762276
printRec(E->getSubExpr());
22772277
PrintWithColorRAII(OS, ParenthesisColor) << ')';
22782278
}
2279+
void visitABISafeConversionExpr(ABISafeConversionExpr *E) {
2280+
printCommon(E, "abi_safe_conversion_expr") << '\n';
2281+
printRec(E->getSubExpr());
2282+
PrintWithColorRAII(OS, ParenthesisColor) << ')';
2283+
}
22792284
void visitMetatypeConversionExpr(MetatypeConversionExpr *E) {
22802285
printCommon(E, "metatype_conversion_expr") << '\n';
22812286
printRec(E->getSubExpr());

lib/AST/ASTPrinter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4793,6 +4793,9 @@ void PrintAST::visitConstructorRefCallExpr(ConstructorRefCallExpr *expr) {
47934793
}
47944794
}
47954795

4796+
void PrintAST::visitABISafeConversionExpr(ABISafeConversionExpr *expr) {
4797+
}
4798+
47964799
void PrintAST::visitFunctionConversionExpr(FunctionConversionExpr *expr) {
47974800
}
47984801

lib/AST/ASTVerifier.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,17 @@ class Verifier : public ASTWalker {
240240
pushScope(DC);
241241
}
242242

243+
/// Emit an error message and abort, optionally dumping the expression.
244+
/// \param E if non-null, the expression to dump() followed by a new-line.
245+
void error(llvm::StringRef msg, Expr *E = nullptr) {
246+
Out << msg << "\n";
247+
if (E) {
248+
E->dump(Out);
249+
Out << "\n";
250+
}
251+
abort();
252+
}
253+
243254
public:
244255
Verifier(ModuleDecl *M, DeclContext *DC)
245256
: Verifier(PointerUnion<ModuleDecl *, SourceFile *>(M), DC) {}
@@ -2306,6 +2317,34 @@ class Verifier : public ASTWalker {
23062317
verifyCheckedBase(E);
23072318
}
23082319

2320+
void verifyChecked(ABISafeConversionExpr *E) {
2321+
PrettyStackTraceExpr debugStack(Ctx, "verify ABISafeConversionExpr", E);
2322+
2323+
auto toType = E->getType();
2324+
auto fromType = E->getSubExpr()->getType();
2325+
2326+
if (!fromType->hasLValueType())
2327+
error("conversion source must be an l-value", E);
2328+
2329+
if (!toType->hasLValueType())
2330+
error("conversion result must be an l-value", E);
2331+
2332+
{
2333+
// At the moment, "ABI Safe" means concurrency features can be stripped.
2334+
// Since we don't know how deeply the stripping is happening, to verify
2335+
// in a fuzzy way, strip everything to see if they're the same type.
2336+
auto strippedFrom = fromType->getRValueType()
2337+
->stripConcurrency(/*recurse*/true,
2338+
/*dropGlobalActor*/true);
2339+
auto strippedTo = toType->getRValueType()
2340+
->stripConcurrency(/*recurse*/true,
2341+
/*dropGlobalActor*/true);
2342+
2343+
if (!strippedFrom->isEqual(strippedTo))
2344+
error("possibly non-ABI safe conversion", E);
2345+
}
2346+
}
2347+
23092348
void verifyChecked(ValueDecl *VD) {
23102349
if (VD->getInterfaceType()->hasError()) {
23112350
Out << "checked decl cannot have error type\n";

lib/AST/Expr.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ ConcreteDeclRef Expr::getReferencedDecl(bool stopAtParenExpr) const {
414414
PASS_THROUGH_REFERENCE(Load, getSubExpr);
415415
NO_REFERENCE(DestructureTuple);
416416
NO_REFERENCE(UnresolvedTypeConversion);
417+
PASS_THROUGH_REFERENCE(ABISafeConversion, getSubExpr);
417418
PASS_THROUGH_REFERENCE(FunctionConversion, getSubExpr);
418419
PASS_THROUGH_REFERENCE(CovariantFunctionConversion, getSubExpr);
419420
PASS_THROUGH_REFERENCE(CovariantReturnConversion, getSubExpr);
@@ -741,6 +742,7 @@ bool Expr::canAppendPostfixExpression(bool appendingPostfixOperator) const {
741742
return false;
742743

743744
case ExprKind::Load:
745+
case ExprKind::ABISafeConversion:
744746
case ExprKind::DestructureTuple:
745747
case ExprKind::UnresolvedTypeConversion:
746748
case ExprKind::FunctionConversion:
@@ -914,6 +916,7 @@ bool Expr::isValidParentOfTypeExpr(Expr *typeExpr) const {
914916
case ExprKind::Load:
915917
case ExprKind::DestructureTuple:
916918
case ExprKind::UnresolvedTypeConversion:
919+
case ExprKind::ABISafeConversion:
917920
case ExprKind::FunctionConversion:
918921
case ExprKind::CovariantFunctionConversion:
919922
case ExprKind::CovariantReturnConversion:

lib/SILGen/LValue.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ class PathComponent {
105105
CoroutineAccessorKind, // coroutine accessor
106106
ValueKind, // random base pointer as an lvalue
107107
PhysicalKeyPathApplicationKind, // applying a key path
108+
ABISafeConversionKind, // unchecked_addr_cast
108109

109110
// Logical LValue kinds
110111
GetterSetterKind, // property or subscript getter/setter

lib/SILGen/SILGenExpr.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,9 @@ namespace {
451451
RValue visitArchetypeToSuperExpr(ArchetypeToSuperExpr *E, SGFContext C);
452452
RValue visitUnresolvedTypeConversionExpr(UnresolvedTypeConversionExpr *E,
453453
SGFContext C);
454+
RValue visitABISafeConversionExpr(ABISafeConversionExpr *E, SGFContext C) {
455+
llvm_unreachable("cannot appear in rvalue");
456+
}
454457
RValue visitFunctionConversionExpr(FunctionConversionExpr *E,
455458
SGFContext C);
456459
RValue visitCovariantFunctionConversionExpr(

lib/SILGen/SILGenLValue.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenLValue
327327
LValueOptions options);
328328
LValue visitMoveExpr(MoveExpr *e, SGFAccessKind accessKind,
329329
LValueOptions options);
330+
LValue visitABISafeConversionExpr(ABISafeConversionExpr *e,
331+
SGFAccessKind accessKind,
332+
LValueOptions options);
330333

331334
// Expressions that wrap lvalues
332335

@@ -2204,6 +2207,29 @@ namespace {
22042207
OS.indent(indent) << "PhysicalKeyPathApplicationComponent\n";
22052208
}
22062209
};
2210+
2211+
/// A physical component which performs an unchecked_addr_cast
2212+
class ABISafeConversionComponent final : public PhysicalPathComponent {
2213+
public:
2214+
ABISafeConversionComponent(LValueTypeData typeData)
2215+
: PhysicalPathComponent(typeData, ABISafeConversionKind,
2216+
/*actorIsolation=*/None) {}
2217+
2218+
ManagedValue project(SILGenFunction &SGF, SILLocation loc,
2219+
ManagedValue base) && override {
2220+
auto toType = SGF.getLoweredType(getTypeData().SubstFormalType)
2221+
.getAddressType();
2222+
2223+
if (base.getType() == toType)
2224+
return base; // nothing to do
2225+
2226+
return SGF.B.createUncheckedAddrCast(loc, base, toType);
2227+
}
2228+
2229+
void dump(raw_ostream &OS, unsigned indent) const override {
2230+
OS.indent(indent) << "ABISafeConversionComponent\n";
2231+
}
2232+
};
22072233
} // end anonymous namespace
22082234

22092235
RValue
@@ -3734,6 +3760,17 @@ LValue SILGenLValue::visitMoveExpr(MoveExpr *e, SGFAccessKind accessKind,
37343760
toAddr->getType().getASTType());
37353761
}
37363762

3763+
LValue SILGenLValue::visitABISafeConversionExpr(ABISafeConversionExpr *e,
3764+
SGFAccessKind accessKind,
3765+
LValueOptions options) {
3766+
LValue lval = visitRec(e->getSubExpr(), accessKind, options);
3767+
auto typeData = getValueTypeData(SGF, accessKind, e);
3768+
3769+
lval.add<ABISafeConversionComponent>(typeData);
3770+
3771+
return lval;
3772+
}
3773+
37373774
/// Emit an lvalue that refers to the given property. This is
37383775
/// designed to work with ManagedValue 'base's that are either +0 or +1.
37393776
LValue SILGenFunction::emitPropertyLValue(SILLocation loc, ManagedValue base,

lib/Sema/CSApply.cpp

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -919,13 +919,22 @@ namespace {
919919

920920
auto &context = cs.getASTContext();
921921

922-
// turn LValues into RValues first
922+
// For an RValue function type, use a standard function conversion.
923+
if (openedType->is<AnyFunctionType>()) {
924+
expr = new (context) FunctionConversionExpr(
925+
expr, getNewType(adjustedOpenedType));
926+
cs.cacheType(expr);
927+
return expr;
928+
}
929+
930+
// For any kind of LValue, use an ABISafeConversion.
923931
if (openedType->hasLValueType()) {
924-
assert(adjustedOpenedType->hasLValueType() && "lvalue-ness mismatch?");
925-
return adjustTypeForDeclReference(cs.coerceToRValue(expr),
926-
openedType->getRValueType(),
927-
adjustedOpenedType->getRValueType(),
928-
getNewType);
932+
assert(adjustedOpenedType->hasLValueType() && "lvalueness mismatch?");
933+
934+
expr = new (context) ABISafeConversionExpr(
935+
expr, getNewType(adjustedOpenedType));
936+
cs.cacheType(expr);
937+
return expr;
929938
}
930939

931940
// If we have an optional type, wrap it up in a monadic '?' and recurse.
@@ -944,14 +953,6 @@ namespace {
944953
return expr;
945954
}
946955

947-
// For a function type, perform a function conversion.
948-
if (openedType->is<AnyFunctionType>()) {
949-
expr = new (context) FunctionConversionExpr(
950-
expr, getNewType(adjustedOpenedType));
951-
cs.cacheType(expr);
952-
return expr;
953-
}
954-
955956
assert(false && "Unhandled adjustment");
956957
return expr;
957958
}

0 commit comments

Comments
 (0)