Correctly model NRVO returns (#7865)

The AST doesn't really matter here, since it's NRVO, there's no actual
code to generate to return the value. But having a correct AST does
address at least one clang false-positive diagnostic:
```
error: stack memory associated with local variable 'return_storage' is returned
    3 | fn F() -> bool {
      |           ^
```
This commit is contained in:
David Blaikie
2026-09-29 21:06:48 +00:00
committed by GitHub
parent c2d9cde674
commit 99f910f179
2 changed files with 24 additions and 8 deletions
+18 -3
View File
@@ -1254,9 +1254,24 @@ static auto BuildCppToCarbonThunkBody(Context& context,
stmts.push_back(call.get());
if (has_return_value) {
auto* return_stmt = clang::ReturnStmt::Create(
sema.getASTContext(), clang_loc, return_storage_expr.get(),
return_storage_var_decl);
// Return `return_storage` by value. The variable is an NRVO candidate,
// so CodeGen constructs it directly in the return slot and ignores the
// returned expression, but the AST should still model a by-value return
// (a prvalue) rather than returning an lvalue referring to the local.
clang::QualType return_type = return_storage_var_decl->getType();
clang::Expr* return_val_expr = sema.BuildDeclRefExpr(
return_storage_var_decl, return_type, clang::VK_LValue, clang_loc);
// TODO: Lvalue-to-rvalue conversion isn't valid for class types. Those
// would need a (elided) copy/move construction, which may not exist.
if (!return_type->getAsCXXRecordDecl() && !return_type->isNullPtrType()) {
return_val_expr = clang::ImplicitCastExpr::Create(
sema.getASTContext(), return_type, clang::CK_LValueToRValue,
return_val_expr, /*BasePath=*/nullptr, clang::VK_PRValue,
clang::FPOptionsOverride());
}
auto* return_stmt =
clang::ReturnStmt::Create(sema.getASTContext(), clang_loc,
return_val_expr, return_storage_var_decl);
stmts.push_back(return_stmt);
}
}
@@ -35,20 +35,21 @@ fn F(i: i32) -> i32 {
// CHECK:STDOUT: | | | `-ImplicitCastExpr {{0x[a-f0-9]+}} <col:21> 'const int' lvalue <NoOp>
// CHECK:STDOUT: | | | `-DeclRefExpr {{0x[a-f0-9]+}} <col:21> 'int' lvalue Var {{0x[a-f0-9]+}} 'return_storage' 'int'
// CHECK:STDOUT: | | `-ReturnStmt {{0x[a-f0-9]+}} <col:21> nrvo_candidate(Var {{0x[a-f0-9]+}} 'return_storage' 'int')
// CHECK:STDOUT: | | `-DeclRefExpr {{0x[a-f0-9]+}} <col:21> 'int' lvalue Var {{0x[a-f0-9]+}} 'return_storage' 'int'
// CHECK:STDOUT: | | `-ImplicitCastExpr {{0x[a-f0-9]+}} <col:21> 'int' <LValueToRValue>
// CHECK:STDOUT: | | `-DeclRefExpr {{0x[a-f0-9]+}} <col:21> 'int' lvalue Var {{0x[a-f0-9]+}} 'return_storage' 'int'
// CHECK:STDOUT: | |-AlwaysInlineAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit always_inline
// CHECK:STDOUT: | `-InternalLinkageAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit
// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <line:38:1, line:40:1> line:38:5 in thunk_with_args_and_return.carbon hidden G 'int (int)' external-linkage
// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <line:39:1, line:41:1> line:39:5 in thunk_with_args_and_return.carbon hidden G 'int (int)' external-linkage
// CHECK:STDOUT: | |-ParmVarDecl {{0x[a-f0-9]+}} <col:7, col:11> col:11 in thunk_with_args_and_return.carbon hidden used i 'int'
// CHECK:STDOUT: | `-CompoundStmt {{0x[a-f0-9]+}} <col:14, line:40:1>
// CHECK:STDOUT: | `-ReturnStmt {{0x[a-f0-9]+}} <line:39:3, col:21>
// CHECK:STDOUT: | `-CompoundStmt {{0x[a-f0-9]+}} <col:14, line:41:1>
// CHECK:STDOUT: | `-ReturnStmt {{0x[a-f0-9]+}} <line:40:3, col:21>
// CHECK:STDOUT: | `-CallExpr {{0x[a-f0-9]+}} <col:10, col:21> 'int'
// CHECK:STDOUT: | |-ImplicitCastExpr {{0x[a-f0-9]+}} <col:10, col:18> 'int (*)(int)' <FunctionToPointerDecay>
// CHECK:STDOUT: | | `-DeclRefExpr {{0x[a-f0-9]+}} <col:10, col:18> 'int (int)' lvalue Function {{0x[a-f0-9]+}} 'F' 'int (int)'
// CHECK:STDOUT: | | `-NestedNameSpecifier Namespace {{0x[a-f0-9]+}} 'Carbon'
// CHECK:STDOUT: | `-ImplicitCastExpr {{0x[a-f0-9]+}} <col:20> 'int' <LValueToRValue>
// CHECK:STDOUT: | `-DeclRefExpr {{0x[a-f0-9]+}} <col:20> 'int' lvalue ParmVar {{0x[a-f0-9]+}} 'i' 'int'
// CHECK:STDOUT: `-ImportDecl {{0x[a-f0-9]+}} <line:41:4> col:4 in thunk_with_args_and_return.carbon implicit thunk_with_args_and_return.carbon
// CHECK:STDOUT: `-ImportDecl {{0x[a-f0-9]+}} <line:42:4> col:4 in thunk_with_args_and_return.carbon implicit thunk_with_args_and_return.carbon
return i;
}