@@ -197,6 +197,18 @@ bool ConverterRefCount::IsUnboxedVar(const clang::ValueDecl *decl) const {
197197 return var && !boxed_vars_->contains (var);
198198}
199199
200+ bool ConverterRefCount::IsUnboxedPlace (const clang::Expr *expr) const {
201+ expr = expr->IgnoreParenImpCasts ();
202+ // Fields of unions and references are accessed through pointers.
203+ if (auto *member = clang::dyn_cast<clang::MemberExpr>(expr)) {
204+ return !member->isArrow () && !member->getBase ()->getType ()->isUnionType () &&
205+ !member->getMemberDecl ()->getType ()->isReferenceType () &&
206+ IsUnboxedPlace (member->getBase ());
207+ }
208+ auto *ref = clang::dyn_cast<clang::DeclRefExpr>(expr);
209+ return ref && IsUnboxedVar (ref->getDecl ());
210+ }
211+
200212ConverterRefCount::ConversionKind
201213ConverterRefCount::VarConversionKind (const clang::VarDecl *decl) const {
202214 if (in_function_formals_) {
@@ -1914,7 +1926,7 @@ bool ConverterRefCount::VisitMemberExpr(clang::MemberExpr *expr) {
19141926 return false ;
19151927 }
19161928
1917- if (isAddrOf () && IsArrayFieldPtr (ctx_, expr)) {
1929+ if (isAddrOf () && IsArrayFieldPtr (ctx_, expr) && ! IsUnboxedPlace (expr) ) {
19181930 StrCat (std::format (" array_field_ptr!({}, {})" , ConvertRecordPtr (expr),
19191931 GetNamedDeclAsString (member)));
19201932 computed_expr_type_ = ComputedExprType::FreshPointer;
@@ -1980,6 +1992,13 @@ void ConverterRefCount::ConvertInlineField(clang::MemberExpr *expr) {
19801992 if (isRValue () && !record_ptr_ &&
19811993 (TypeIsCopyable (type) || type->isPointerType () || IsUniquePtr (type) ||
19821994 expr == copied_expr_)) {
1995+ // A pointer in an unboxed struct is used in place, like a pointer
1996+ // variable, and only copied when needed.
1997+ if (type->isPointerType () && IsUnboxedPlace (expr)) {
1998+ StrCat (ReadField (expr, " " ));
1999+ computed_expr_type_ = ComputedExprType::Pointer;
2000+ return ;
2001+ }
19832002 StrCat (ReadField (expr));
19842003 SetFreshType (type);
19852004 return ;
@@ -2034,6 +2053,9 @@ std::string ConverterRefCount::ReadField(clang::MemberExpr *expr,
20342053 if (!TypeIsCopyable (expr->getMemberDecl ()->getType ())) {
20352054 str += copy;
20362055 }
2056+ if (IsUnboxedPlace (expr)) {
2057+ return str;
2058+ }
20372059 return ptr.empty () ? std::format (" {{ {} }}" , str)
20382060 : std::format (" {}.with(|__s| {})" , ptr, str);
20392061}
@@ -2567,10 +2589,20 @@ void ConverterRefCount::ConvertAssignment(clang::Expr *lhs, clang::Expr *rhs,
25672589 auto ptr = pending_deref_.take ();
25682590 auto op = assign_operator;
25692591 op.remove_suffix (1 ); // remove '='
2570- {
2571- PushBrace brace (* this );
2592+ PushBrace brace (* this );
2593+ if (lhs-> getType ()-> isPointerType ()) {
25722594 StrCat (std::format (" let _ptr = {}{};" , ptr, fresh ? " " : " .clone()" ));
25732595 StrCat (std::format (" _ptr.write(_ptr.read() {} {})" , op, rhs_as_string));
2596+ } else {
2597+ // The place is accessed once, after the assigned value is computed,
2598+ // which may access it too.
2599+ if (rhs_as_string != " __rhs" && !rhs->isEvaluatable (ctx_)) {
2600+ StrCat (keyword::kLet , " __rhs" , token::kAssign , rhs_as_string,
2601+ token::kSemiColon );
2602+ rhs_as_string = " __rhs" ;
2603+ }
2604+ StrCat (std::format (" {}.with_mut(|__v| *__v = *__v {} {})" , ptr, op,
2605+ rhs_as_string));
25742606 }
25752607 } else {
25762608 StrCat (lhs_str, assign_operator, rhs_as_string);
0 commit comments