Skip to content

Commit 40fe675

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 f9e3e50 commit 40fe675

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
@@ -3122,6 +3122,19 @@ class LoadExpr : public ImplicitConversionExpr {
31223122
static bool classof(const Expr *E) { return E->getKind() == ExprKind::Load; }
31233123
};
31243124

3125+
/// ABISafeConversion - models a type conversion on an l-value that has no
3126+
/// material affect on the ABI of the type, while *preserving* the l-valueness
3127+
/// of the type.
3128+
class ABISafeConversionExpr : public ImplicitConversionExpr {
3129+
public:
3130+
ABISafeConversionExpr(Expr *subExpr, Type type)
3131+
: ImplicitConversionExpr(ExprKind::ABISafeConversion, subExpr, type) {}
3132+
3133+
static bool classof(const Expr *E) {
3134+
return E->getKind() == ExprKind::ABISafeConversion;
3135+
}
3136+
};
3137+
31253138
/// This is a conversion from an expression of UnresolvedType to an arbitrary
31263139
/// other type, and from an arbitrary type to UnresolvedType. This node does
31273140
/// 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
@@ -151,6 +151,7 @@ ABSTRACT_EXPR(Apply, Expr)
151151
EXPR_RANGE(Apply, Call, ConstructorRefCall)
152152
ABSTRACT_EXPR(ImplicitConversion, Expr)
153153
EXPR(Load, ImplicitConversionExpr)
154+
EXPR(ABISafeConversion, ImplicitConversionExpr)
154155
EXPR(DestructureTuple, ImplicitConversionExpr)
155156
EXPR(UnresolvedTypeConversion, ImplicitConversionExpr)
156157
EXPR(FunctionConversion, ImplicitConversionExpr)

lib/AST/ASTDumper.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2265,6 +2265,11 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
22652265
printRec(E->getSubExpr());
22662266
PrintWithColorRAII(OS, ParenthesisColor) << ')';
22672267
}
2268+
void visitABISafeConversionExpr(ABISafeConversionExpr *E) {
2269+
printCommon(E, "abi_safe_conversion_expr") << '\n';
2270+
printRec(E->getSubExpr());
2271+
PrintWithColorRAII(OS, ParenthesisColor) << ')';
2272+
}
22682273
void visitMetatypeConversionExpr(MetatypeConversionExpr *E) {
22692274
printCommon(E, "metatype_conversion_expr") << '\n';
22702275
printRec(E->getSubExpr());

lib/AST/ASTPrinter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4712,6 +4712,9 @@ void PrintAST::visitConstructorRefCallExpr(ConstructorRefCallExpr *expr) {
47124712
}
47134713
}
47144714

4715+
void PrintAST::visitABISafeConversionExpr(ABISafeConversionExpr *expr) {
4716+
}
4717+
47154718
void PrintAST::visitFunctionConversionExpr(FunctionConversionExpr *expr) {
47164719
}
47174720

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) {}
@@ -2317,6 +2328,34 @@ class Verifier : public ASTWalker {
23172328
verifyCheckedBase(E);
23182329
}
23192330

2331+
void verifyChecked(ABISafeConversionExpr *E) {
2332+
PrettyStackTraceExpr debugStack(Ctx, "verify ABISafeConversionExpr", E);
2333+
2334+
auto toType = E->getType();
2335+
auto fromType = E->getSubExpr()->getType();
2336+
2337+
if (!fromType->hasLValueType())
2338+
error("conversion source must be an l-value", E);
2339+
2340+
if (!toType->hasLValueType())
2341+
error("conversion result must be an l-value", E);
2342+
2343+
{
2344+
// At the moment, "ABI Safe" means concurrency features can be stripped.
2345+
// Since we don't know how deeply the stripping is happening, to verify
2346+
// in a fuzzy way, strip everything to see if they're the same type.
2347+
auto strippedFrom = fromType->getRValueType()
2348+
->stripConcurrency(/*recurse*/true,
2349+
/*dropGlobalActor*/true);
2350+
auto strippedTo = toType->getRValueType()
2351+
->stripConcurrency(/*recurse*/true,
2352+
/*dropGlobalActor*/true);
2353+
2354+
if (!strippedFrom->isEqual(strippedTo))
2355+
error("possibly non-ABI safe conversion", E);
2356+
}
2357+
}
2358+
23202359
void verifyChecked(ValueDecl *VD) {
23212360
if (VD->getInterfaceType()->hasError()) {
23222361
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
@@ -413,6 +413,7 @@ ConcreteDeclRef Expr::getReferencedDecl(bool stopAtParenExpr) const {
413413
PASS_THROUGH_REFERENCE(Load, getSubExpr);
414414
NO_REFERENCE(DestructureTuple);
415415
NO_REFERENCE(UnresolvedTypeConversion);
416+
PASS_THROUGH_REFERENCE(ABISafeConversion, getSubExpr);
416417
PASS_THROUGH_REFERENCE(FunctionConversion, getSubExpr);
417418
PASS_THROUGH_REFERENCE(CovariantFunctionConversion, getSubExpr);
418419
PASS_THROUGH_REFERENCE(CovariantReturnConversion, getSubExpr);
@@ -738,6 +739,7 @@ bool Expr::canAppendPostfixExpression(bool appendingPostfixOperator) const {
738739
return false;
739740

740741
case ExprKind::Load:
742+
case ExprKind::ABISafeConversion:
741743
case ExprKind::DestructureTuple:
742744
case ExprKind::UnresolvedTypeConversion:
743745
case ExprKind::FunctionConversion:
@@ -909,6 +911,7 @@ bool Expr::isValidParentOfTypeExpr(Expr *typeExpr) const {
909911
case ExprKind::Load:
910912
case ExprKind::DestructureTuple:
911913
case ExprKind::UnresolvedTypeConversion:
914+
case ExprKind::ABISafeConversion:
912915
case ExprKind::FunctionConversion:
913916
case ExprKind::CovariantFunctionConversion:
914917
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
@@ -443,6 +443,9 @@ namespace {
443443
RValue visitArchetypeToSuperExpr(ArchetypeToSuperExpr *E, SGFContext C);
444444
RValue visitUnresolvedTypeConversionExpr(UnresolvedTypeConversionExpr *E,
445445
SGFContext C);
446+
RValue visitABISafeConversionExpr(ABISafeConversionExpr *E, SGFContext C) {
447+
llvm_unreachable("cannot appear in rvalue");
448+
}
446449
RValue visitFunctionConversionExpr(FunctionConversionExpr *E,
447450
SGFContext C);
448451
RValue visitCovariantFunctionConversionExpr(

lib/SILGen/SILGenLValue.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenLValue
323323
LValue visitKeyPathApplicationExpr(KeyPathApplicationExpr *e,
324324
SGFAccessKind accessKind,
325325
LValueOptions options);
326+
LValue visitABISafeConversionExpr(ABISafeConversionExpr *e,
327+
SGFAccessKind accessKind,
328+
LValueOptions options);
326329

327330
// Expressions that wrap lvalues
328331

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

22052231
RValue
@@ -3709,6 +3735,17 @@ LValue SILGenLValue::visitInOutExpr(InOutExpr *e, SGFAccessKind accessKind,
37093735
return visitRec(e->getSubExpr(), accessKind, options);
37103736
}
37113737

3738+
LValue SILGenLValue::visitABISafeConversionExpr(ABISafeConversionExpr *e,
3739+
SGFAccessKind accessKind,
3740+
LValueOptions options) {
3741+
LValue lval = visitRec(e->getSubExpr(), accessKind, options);
3742+
auto typeData = getValueTypeData(SGF, accessKind, e);
3743+
3744+
lval.add<ABISafeConversionComponent>(typeData);
3745+
3746+
return lval;
3747+
}
3748+
37123749
/// Emit an lvalue that refers to the given property. This is
37133750
/// designed to work with ManagedValue 'base's that are either +0 or +1.
37143751
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
@@ -913,13 +913,22 @@ namespace {
913913

914914
auto &context = cs.getASTContext();
915915

916-
// turn LValues into RValues first
916+
// For an RValue function type, use a standard function conversion.
917+
if (openedType->is<AnyFunctionType>()) {
918+
expr = new (context) FunctionConversionExpr(
919+
expr, getNewType(adjustedOpenedType));
920+
cs.cacheType(expr);
921+
return expr;
922+
}
923+
924+
// For any kind of LValue, use an ABISafeConversion.
917925
if (openedType->hasLValueType()) {
918-
assert(adjustedOpenedType->hasLValueType() && "lvalue-ness mismatch?");
919-
return adjustTypeForDeclReference(cs.coerceToRValue(expr),
920-
openedType->getRValueType(),
921-
adjustedOpenedType->getRValueType(),
922-
getNewType);
926+
assert(adjustedOpenedType->hasLValueType() && "lvalueness mismatch?");
927+
928+
expr = new (context) ABISafeConversionExpr(
929+
expr, getNewType(adjustedOpenedType));
930+
cs.cacheType(expr);
931+
return expr;
923932
}
924933

925934
// If we have an optional type, wrap it up in a monadic '?' and recurse.
@@ -938,14 +947,6 @@ namespace {
938947
return expr;
939948
}
940949

941-
// For a function type, perform a function conversion.
942-
if (openedType->is<AnyFunctionType>()) {
943-
expr = new (context) FunctionConversionExpr(
944-
expr, getNewType(adjustedOpenedType));
945-
cs.cacheType(expr);
946-
return expr;
947-
}
948-
949950
assert(false && "Unhandled adjustment");
950951
return expr;
951952
}

0 commit comments

Comments
 (0)