From 314093f426bbfa9a0da7cfd43089ddb10d64a922 Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Fri, 7 Nov 2025 11:52:35 -0800 Subject: [PATCH] SILGen: add RValue emission for BorrowExpr --- lib/AST/ASTVerifier.cpp | 18 ++++++++++++++++++ lib/SILGen/SILGenExpr.cpp | 19 +++++++++++++++++++ lib/Sema/TypeCheckConstraints.cpp | 10 ++++++++++ lib/Sema/TypeChecker.h | 6 ++++++ test/ASTGen/exprs.swift | 2 +- test/Parse/borrow_expr.swift | 5 ----- 6 files changed, 54 insertions(+), 6 deletions(-) diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index dca0ef13b3bd1..e6af45e501283 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -2508,6 +2508,24 @@ class Verifier : public ASTWalker { verifyCheckedBase(E); } + void verifyChecked(BorrowExpr *E) { + PrettyStackTraceExpr debugStack(Ctx, "verifying BorrowExpr", E); + + auto toType = E->getType(); + auto fromType = E->getSubExpr()->getType(); + + // FIXME: doesStorageProduceLValue should not return false for a 'let', + // so that you can borrow from it. + if (!fromType->hasLValueType()) + error("borrow source must be an l-value", E); + + // Result type can be either l-value or r-value. + // Ensure underlying type matches. + if (fromType->getRValueType()->getCanonicalType() != + toType->getRValueType()->getCanonicalType()) + error("borrow should not be performing a cast", E); + } + void verifyChecked(ABISafeConversionExpr *E) { PrettyStackTraceExpr debugStack(Ctx, "verify ABISafeConversionExpr", E); diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 758d5aa541f99..f91693590f9fe 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -468,6 +468,7 @@ namespace { RValue visitStringLiteralExpr(StringLiteralExpr *E, SGFContext C); RValue visitLoadExpr(LoadExpr *E, SGFContext C); + RValue visitBorrowExpr(BorrowExpr *E, SGFContext C); RValue visitDerivedToBaseExpr(DerivedToBaseExpr *E, SGFContext C); RValue visitMetatypeConversionExpr(MetatypeConversionExpr *E, SGFContext C); @@ -1103,6 +1104,24 @@ RValue RValueEmitter::visitLoadExpr(LoadExpr *E, SGFContext C) { C.withFollowingSideEffects()); } +RValue RValueEmitter::visitBorrowExpr(BorrowExpr *E, SGFContext C_Ignored) { + // NOTE: You should NOT add an evaluation scope here! + // + // The callers of this visitor should have established a scope already that + // encompasses the use of the borrowed RValue that we return. + ASSERT(SGF.isInFormalEvaluationScope() && "emit borrow_expr without scope?"); + + auto accessKind = + SGF.getTypeLowering(E->getType()).isAddress() + ? SGFAccessKind::BorrowedAddressRead + : SGFAccessKind::BorrowedObjectRead; + + LValue lv = SGF.emitLValue(E->getSubExpr(), accessKind); + auto substFormalType = lv.getSubstFormalType(); + ManagedValue mv = SGF.emitBorrowedLValue(E, std::move(lv)); + return RValue(SGF, E, substFormalType, mv); +} + SILValue SILGenFunction::emitTemporaryAllocation(SILLocation loc, SILType ty, HasDynamicLifetime_t dynamic, IsLexical_t isLexical, diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index b3c00f731bbc8..eca04cd5000b0 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -1131,6 +1131,16 @@ bool TypeChecker::checkedCastMaySucceed(Type t1, Type t2, DeclContext *dc) { return (kind != CheckedCastKind::Unresolved); } +Expr * +TypeChecker::addImplicitBorrowExpr(ASTContext &Ctx, Expr *E, + std::function getType, + std::function setType) { + auto objectType = getType(E)->getRValueType(); + auto *BE = BorrowExpr::createImplicit(Ctx, E->getLoc(), E, objectType); + setType(BE, objectType); + return BE; +} + Expr * TypeChecker::addImplicitLoadExpr(ASTContext &Context, Expr *expr, std::function getType, diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index d609cc1725568..1f92c5473b662 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -882,6 +882,12 @@ Expr *addImplicitLoadExpr( std::function setType = [](Expr *E, Type type) { E->setType(type); }); +Expr *addImplicitBorrowExpr( + ASTContext &Context, Expr *expr, + std::function getType = [](Expr *E) { return E->getType(); }, + std::function setType = + [](Expr *E, Type type) { E->setType(type); }); + /// Determine whether the given type either conforms to, or itself an /// existential subtype of, the given protocol. /// diff --git a/test/ASTGen/exprs.swift b/test/ASTGen/exprs.swift index 2fdb4cc22850b..619fc8bd4d7b9 100644 --- a/test/ASTGen/exprs.swift +++ b/test/ASTGen/exprs.swift @@ -83,7 +83,7 @@ func asyncFunc(_ arg: String) async throws -> Int { return 1 } func testUnaryExprs() async throws { - let str = String() + var str = String() let foo = try await asyncFunc(_borrow str) let bar = copy foo let baz = consume foo diff --git a/test/Parse/borrow_expr.swift b/test/Parse/borrow_expr.swift index db6737a684cd6..84f5f28c1778f 100644 --- a/test/Parse/borrow_expr.swift +++ b/test/Parse/borrow_expr.swift @@ -5,11 +5,6 @@ func testGlobal() { let _ = _borrow global } -func testLet() { - let t = String() - let _ = _borrow t -} - func testVar() { var t = String() t = String()