[gcc r17-2251] gccrs: lang: Add lang owned_box and box expression

Arthur Cohen via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:d4a4e68f56968981ec82e6b5a8de86d22a523a06

commit r17-2251-gd4a4e68f56968981ec82e6b5a8de86d22a523a06
Author: Enes Cevik <[email protected]>
Date:   Wed Jun 3 10:18:14 2026 +0300

    gccrs: lang: Add lang owned_box and box expression
    
    This patch implement the lang item `owned_box` and add support box
    expression.
    
    gcc/rust/ChangeLog:
    
            * ast/rust-expr.h (BoxExpr): Add comment description to class.
            * backend/rust-compile-block.h (visit): Add stub for BoxExpr.
            * backend/rust-compile-expr.cc (compile_box_struct): New
            function.
            (compile_box): New function.
            (build_box_inner_ptr): New function.
            (CompileExpr::visit): Implement BoxExpr lowering using
            exchange_malloc and constructor expressions. Handle owned_box
            in FieldAccessExpr and DereferenceExpr.
            (HIRCompileBase::resolve_deref_adjustment): Support owned_box
            in deref adjustments.
            * backend/rust-compile-expr.h (visit): Declare BoxExpr visitor.
            * checks/errors/borrowck/rust-bir-builder-expr-stmt.cc
            (ExprStmtBuilder::visit): Evaluate inner expression and push
            tmp assignment for BoxExpr.
            * checks/errors/borrowck/rust-bir-builder-expr-stmt.h (visit):
            Declare BoxExpr visitor.
            * checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h
            (visit): Mark BoxExpr as unreachable.
            * checks/errors/borrowck/rust-bir-builder-struct.h (visit):
            Likewise.
            * checks/errors/borrowck/rust-function-collector.h (visit):
            Add visit stub for BoxExpr.
            * checks/errors/privacy/rust-privacy-reporter.cc
            (PrivacyReporter::visit): Delegate privacy reporting to inner
            expression of BoxExpr.
            * checks/errors/privacy/rust-privacy-reporter.h (visit):
            Declare BoxExpr visitor.
            * checks/errors/rust-const-checker.cc (ConstChecker::visit):
            Delegate const checking to inner expression.
            * checks/errors/rust-const-checker.h (visit): Declare BoxExpr
            visitor.
            * checks/errors/rust-hir-pattern-analysis.cc
            (PatternChecker::visit): Delegate pattern analysis to inner
            expression.
            * checks/errors/rust-hir-pattern-analysis.h (visit): Declare
            BoxExpr visitor.
            * checks/errors/rust-unsafe-checker.cc (UnsafeChecker::visit):
            Delegate unsafe checking to inner expression.
            * checks/errors/rust-unsafe-checker.h (visit): Declare BoxExpr
            visitor.
            * checks/lints/rust-lint-marklive.cc (MarkLive::visit): Resolve
            inner ADT type for owned_box when marking live fields.
            * checks/lints/rust-lint-marklive.h (visit): Declare BoxExpr
            visitor.
            * hir/rust-ast-lower-expr.cc (ASTLoweringExpr::visit): Translate
            AST BoxExpr to HIR BoxExpr.
            * hir/rust-hir-dump.cc (Dump::visit): Dump HIR BoxExpr.
            * hir/rust-hir-dump.h (visit): Declare BoxExpr visitor.
            * hir/tree/rust-hir-expr-abstract.h (ExprType): Add Box to enum.
            * hir/tree/rust-hir-expr.cc (BoxExpr::BoxExpr): Implement
            constructors for BoxExpr. (BoxExpr::operator=): Implement
            assignment operator.
            * hir/tree/rust-hir-expr.h (class BoxExpr): Define HIR BoxExpr
            node.
            * hir/tree/rust-hir-full-decls.h (class BoxExpr): Forward
            declaration.
            * hir/tree/rust-hir-visitor.cc (DefaultHIRVisitor::walk): Walk
            inner expression of BoxExpr.
            * hir/tree/rust-hir-visitor.h (visit): Add routing for BoxExpr.
            * hir/tree/rust-hir.cc (BoxExpr::to_string): Implement
            to_string.
            (BoxExpr::accept_vis): Implement visitor acceptance.
            * typecheck/rust-autoderef.cc (AutoderefCycle::cycle): Attempt
            autoderef using the owned_box lang item.
            * typecheck/rust-hir-type-check-expr.cc (TypeCheckExpr::visit):
            Implement type checking, validation, and generic substitution
            for BoxExpr. Support owned_box autoderef in field access and
            dereferencing operations.
            * typecheck/rust-tyty.cc (try_get_box_inner_type): New function.
            * typecheck/rust-tyty.h (try_get_box_inner_type): New
            declaration.
            * typecheck/rust-hir-type-check-expr.h (visit): Declare BoxExpr
            visitor.
            * util/rust-lang-item.cc (Rust::LangItem::lang_items): Register
            owned_box to BiMap.
            * util/rust-lang-item.h (LangItem::Kind): Add OWNED_BOX.
    
    gcc/testsuite/ChangeLog:
    
            * rust/compile/box-expr.rs: New test.
            * rust/compile/lang-owned-box-error1.rs: New test.
            * rust/compile/lang-owned-box-error2.rs: New test.
    
    Signed-off-by: Enes Cevik <[email protected]>

Diff:
---
 gcc/rust/ast/rust-expr.h                           |   1 +
 gcc/rust/backend/rust-compile-block.h              |   2 +
 gcc/rust/backend/rust-compile-expr.cc              | 171 ++++++++++++++++++++-
 gcc/rust/backend/rust-compile-expr.h               |   1 +
 .../errors/borrowck/rust-bir-builder-expr-stmt.cc  |   9 ++
 .../errors/borrowck/rust-bir-builder-expr-stmt.h   |   1 +
 .../borrowck/rust-bir-builder-lazyboolexpr.h       |   1 +
 .../errors/borrowck/rust-bir-builder-struct.h      |   1 +
 .../errors/borrowck/rust-function-collector.h      |   1 +
 .../checks/errors/privacy/rust-privacy-reporter.cc |   6 +
 .../checks/errors/privacy/rust-privacy-reporter.h  |   1 +
 gcc/rust/checks/errors/rust-const-checker.cc       |   6 +
 gcc/rust/checks/errors/rust-const-checker.h        |   1 +
 .../checks/errors/rust-hir-pattern-analysis.cc     |   6 +
 gcc/rust/checks/errors/rust-hir-pattern-analysis.h |   1 +
 gcc/rust/checks/errors/rust-unsafe-checker.cc      |   6 +
 gcc/rust/checks/errors/rust-unsafe-checker.h       |   1 +
 gcc/rust/checks/lints/rust-lint-marklive.cc        |   7 +
 gcc/rust/checks/lints/rust-lint-marklive.h         |   5 +
 gcc/rust/hir/rust-ast-lower-expr.cc                |  11 +-
 gcc/rust/hir/rust-hir-dump.cc                      |  11 ++
 gcc/rust/hir/rust-hir-dump.h                       |   1 +
 gcc/rust/hir/tree/rust-hir-expr-abstract.h         |   1 +
 gcc/rust/hir/tree/rust-hir-expr.cc                 |  27 ++++
 gcc/rust/hir/tree/rust-hir-expr.h                  |  46 ++++++
 gcc/rust/hir/tree/rust-hir-full-decls.h            |   1 +
 gcc/rust/hir/tree/rust-hir-visitor.cc              |   7 +
 gcc/rust/hir/tree/rust-hir-visitor.h               |   5 +
 gcc/rust/hir/tree/rust-hir.cc                      |  21 +++
 gcc/rust/typecheck/rust-autoderef.cc               |  15 ++
 gcc/rust/typecheck/rust-hir-type-check-expr.cc     |  76 ++++++++-
 gcc/rust/typecheck/rust-hir-type-check-expr.h      |   1 +
 gcc/rust/typecheck/rust-tyty.cc                    |  23 +++
 gcc/rust/typecheck/rust-tyty.h                     |   3 +
 gcc/rust/util/rust-lang-item.cc                    |   1 +
 gcc/rust/util/rust-lang-item.h                     |   3 +-
 gcc/testsuite/rust/compile/box-expr.rs             |  55 +++++++
 .../rust/compile/lang-owned-box-error1.rs          |  15 ++
 .../rust/compile/lang-owned-box-error2.rs          |  12 ++
 39 files changed, 556 insertions(+), 7 deletions(-)

diff --git a/gcc/rust/ast/rust-expr.h b/gcc/rust/ast/rust-expr.h
index 90e4daec413c..921b79800c1f 100644
--- a/gcc/rust/ast/rust-expr.h
+++ b/gcc/rust/ast/rust-expr.h
@@ -3751,6 +3751,7 @@ protected:
   }
 };
 
+// Box expression AST node representation
 class BoxExpr : public ExprWithoutBlock
 {
   std::unique_ptr<Expr> expr;
diff --git a/gcc/rust/backend/rust-compile-block.h b/gcc/rust/backend/rust-compile-block.h
index 680b4fb6c5ea..9d29e8fc504b 100644
--- a/gcc/rust/backend/rust-compile-block.h
+++ b/gcc/rust/backend/rust-compile-block.h
@@ -94,6 +94,7 @@ public:
   void visit (HIR::RangeFullExpr &) override {}
   void visit (HIR::RangeFromToInclExpr &) override {}
   void visit (HIR::RangeToInclExpr &) override {}
+  void visit (HIR::BoxExpr &) override {}
   void visit (HIR::ReturnExpr &) override {}
   void visit (HIR::UnsafeBlockExpr &) override {}
   void visit (HIR::LoopExpr &) override {}
@@ -184,6 +185,7 @@ public:
   void visit (HIR::RangeFullExpr &) override {}
   void visit (HIR::RangeFromToInclExpr &) override {}
   void visit (HIR::RangeToInclExpr &) override {}
+  void visit (HIR::BoxExpr &) override {}
   void visit (HIR::ReturnExpr &) override {}
   void visit (HIR::UnsafeBlockExpr &) override {}
   void visit (HIR::LoopExpr &) override {}
diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc
index 37a9c702ae81..55ff3263a117 100644
--- a/gcc/rust/backend/rust-compile-expr.cc
+++ b/gcc/rust/backend/rust-compile-expr.cc
@@ -45,6 +45,114 @@
 namespace Rust {
 namespace Compile {
 
+namespace {
+tree
+compile_box_struct (Context *ctx, TyTy::BaseType *curr_ty, tree typed_ptr,
+		    location_t locus, int depth = 0)
+{
+  // infinite loop guard
+  rust_assert (depth < 100);
+
+  if (curr_ty->get_kind () == TyTy::TypeKind::POINTER
+      || curr_ty->get_kind () == TyTy::TypeKind::REF)
+    return typed_ptr;
+
+  if (curr_ty->get_kind () == TyTy::TypeKind::ADT)
+    {
+      TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (curr_ty);
+      tree adt_ty_tree = TyTyResolveCompile::compile (ctx, adt);
+      tree size_tree = TYPE_SIZE_UNIT (adt_ty_tree);
+
+      if (integer_zerop (size_tree))
+	return Backend::constructor_expression (adt_ty_tree, false, {}, -1,
+						locus);
+      rust_assert (!adt->is_enum ());
+      rust_assert (adt->number_of_variants () == 1);
+
+      std::vector<tree> args;
+      TyTy::VariantDef *variant = adt->get_variants ().at (0);
+      for (size_t i = 0; i < variant->num_fields (); i++)
+	{
+	  TyTy::StructFieldType *field = variant->get_field_at_index (i);
+	  TyTy::BaseType *field_ty = field->get_field_type ();
+	  tree field_tree
+	    = compile_box_struct (ctx, field_ty, typed_ptr, locus, depth + 1);
+	  args.push_back (field_tree);
+	}
+      return Backend::constructor_expression (adt_ty_tree, false, args, -1,
+					      locus);
+    }
+  if (curr_ty->get_kind () == TyTy::TypeKind::PARAM)
+    {
+      TyTy::ParamType *param_ty = static_cast<TyTy::ParamType *> (curr_ty);
+      TyTy::BaseType *resolved_ty = param_ty->resolve ();
+      rust_assert (resolved_ty != nullptr && resolved_ty != curr_ty);
+      return compile_box_struct (ctx, resolved_ty, typed_ptr, locus, depth + 1);
+    }
+  rust_unreachable ();
+  return error_mark_node;
+};
+
+tree
+compile_box (Context *ctx, TyTy::BaseType *box_tyty, TyTy::BaseType *inner_tyty,
+	     tree inner_expr_tree, location_t locus)
+{
+  tree inner_type_tree = TyTyResolveCompile::compile (ctx, inner_tyty);
+
+  if (!COMPLETE_TYPE_P (inner_type_tree))
+    {
+      rust_sorry_at (locus,
+		     "dynamically sized types in boxes are not supported yet");
+      return error_mark_node;
+    }
+  tree size_tree = TYPE_SIZE_UNIT (inner_type_tree);
+  tree align_tree
+    = build_int_cst (size_type_node, TYPE_ALIGN_UNIT (inner_type_tree));
+
+  DefId exchange_malloc_defid
+    = ctx->get_mappings ().get_lang_item (LangItem::Kind::EXCHANGE_MALLOC,
+					  locus);
+  HIR::Item *item
+    = ctx->get_mappings ().lookup_defid (exchange_malloc_defid).value ();
+
+  tree exchange_malloc_decl = nullptr;
+  ctx->lookup_function_decl (item->get_mappings ().get_hirid (),
+			     &exchange_malloc_decl, exchange_malloc_defid);
+
+  tree raw_ptr = save_expr (build_call_expr_loc (locus, exchange_malloc_decl, 2,
+						 size_tree, align_tree));
+  tree typed_ptr = fold_convert (build_pointer_type (inner_type_tree), raw_ptr);
+
+  tree deref = build1_loc (locus, INDIRECT_REF, inner_type_tree, typed_ptr);
+  tree assignment
+    = build2_loc (locus, MODIFY_EXPR, inner_type_tree, deref, inner_expr_tree);
+
+  tree box_struct = compile_box_struct (ctx, box_tyty, typed_ptr, locus);
+
+  return build2_loc (locus, COMPOUND_EXPR, TREE_TYPE (box_struct), assignment,
+		     box_struct);
+}
+
+tree
+build_box_inner_ptr (tree main_expr, location_t locus)
+{
+  // We continuously extract the first field of the struct until we hit the
+  // actual underlying raw pointer. This handles both simple Box layouts (ptr:
+  // *mut T) and complex ones like this (Box<Unique<NonNull<T>>>). This relies
+  // on the standard library's layout guarantees and will break if a stateful
+  // custom allocator is placed as the first field in the RECORD_TYPE.
+  while (TREE_CODE (TREE_TYPE (main_expr)) == RECORD_TYPE)
+    {
+      tree first_field = TYPE_FIELDS (TREE_TYPE (main_expr));
+      if (first_field == NULL_TREE)
+	break;
+      main_expr = build3_loc (locus, COMPONENT_REF, TREE_TYPE (first_field),
+			      main_expr, first_field, NULL_TREE);
+    }
+  return main_expr;
+}
+} // namespace
+
 CompileExpr::CompileExpr (Context *ctx)
   : HIRCompileBase (ctx), translated (error_mark_node)
 {}
@@ -115,6 +223,26 @@ CompileExpr::visit (HIR::TupleExpr &expr)
 						expr.get_locus ());
 }
 
+void
+CompileExpr::visit (HIR::BoxExpr &expr)
+{
+  TyTy::BaseType *inner_tyty = nullptr;
+  bool ok = ctx->get_tyctx ()->lookup_type (
+    expr.get_expr ().get_mappings ().get_hirid (), &inner_tyty);
+  rust_assert (ok);
+
+  TyTy::BaseType *box_tyty = nullptr;
+  ok = ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
+				       &box_tyty);
+  rust_assert (ok);
+
+  tree inner_expr_tree
+    = save_expr (CompileExpr::Compile (expr.get_expr (), ctx));
+
+  translated = compile_box (ctx, box_tyty, inner_tyty, inner_expr_tree,
+			    expr.get_locus ());
+}
+
 void
 CompileExpr::visit (HIR::ReturnExpr &expr)
 {
@@ -691,7 +819,21 @@ CompileExpr::visit (HIR::FieldAccessExpr &expr)
     }
 
   size_t field_index = 0;
-  if (receiver->get_kind () == TyTy::TypeKind::ADT)
+
+  if (auto inner_ty = TyTy::try_get_box_inner_type (receiver))
+    {
+      rust_assert ((*inner_ty)->get_kind () == TyTy::TypeKind::ADT);
+      TyTy::ADTType *inner_adt = static_cast<TyTy::ADTType *> (*inner_ty);
+      TyTy::VariantDef *variant = inner_adt->get_variants ().at (0);
+
+      bool ok = variant->lookup_field (expr.get_field_name ().as_string (),
+				       nullptr, &field_index);
+      rust_assert (ok);
+
+      receiver_ref = build_box_inner_ptr (receiver_ref, expr.get_locus ());
+      receiver_ref = indirect_expression (receiver_ref, expr.get_locus ());
+    }
+  else if (receiver->get_kind () == TyTy::TypeKind::ADT)
     {
       TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (receiver);
       rust_assert (!adt->is_enum ());
@@ -1045,6 +1187,21 @@ CompileExpr::visit (HIR::DereferenceExpr &expr)
 
   tree main_expr = CompileExpr::Compile (expr.get_expr (), ctx);
 
+  // Box<T> Dereference Hook
+  // Typechecker treats 'Box<T>' as a privileged pointer and allows
+  // explicit dereferencing.However, the backend sees Box as a normal
+  // RECORD_TYPE (struct). To solve this, if the type is the 'owned_box'
+  // lang item, we drill down.
+  TyTy::BaseType *base_tyty;
+  if (ctx->get_tyctx ()->lookup_type (
+	expr.get_expr ().get_mappings ().get_hirid (), &base_tyty))
+    {
+      if (TyTy::try_get_box_inner_type (base_tyty))
+	{
+	  main_expr = build_box_inner_ptr (main_expr, expr.get_locus ());
+	}
+    }
+
   // this might be an operator overload situation lets check
   TyTy::FnType *fntype;
   bool is_op_overload = ctx->get_tyctx ()->lookup_operator_overload (
@@ -2253,7 +2410,17 @@ HIRCompileBase::resolve_deref_adjustment (Resolver::Adjustment &adjustment,
 {
   rust_assert (adjustment.is_deref_adjustment ()
 	       || adjustment.is_deref_mut_adjustment ());
-  rust_assert (adjustment.has_operator_overload ());
+  if (!adjustment.has_operator_overload ())
+    {
+      TyTy::BaseType *receiver = adjustment.get_actual ();
+      if (TyTy::try_get_box_inner_type (receiver))
+	{
+	  tree receiver_ref = expression;
+	  receiver_ref = build_box_inner_ptr (receiver_ref, locus);
+	  return indirect_expression (receiver_ref, locus);
+	}
+      rust_assert (false);
+    }
 
   TyTy::FnType *lookup = adjustment.get_deref_operator_fn ();
   TyTy::BaseType *receiver = adjustment.get_actual ();
diff --git a/gcc/rust/backend/rust-compile-expr.h b/gcc/rust/backend/rust-compile-expr.h
index 532e140c9ccf..90e1985c9930 100644
--- a/gcc/rust/backend/rust-compile-expr.h
+++ b/gcc/rust/backend/rust-compile-expr.h
@@ -76,6 +76,7 @@ public:
   void visit (HIR::InlineAsm &expr) override;
   void visit (HIR::LlvmInlineAsm &expr) override;
   void visit (HIR::OffsetOf &expr) override;
+  void visit (HIR::BoxExpr &expr) override;
 
   // TODO
   void visit (HIR::ErrorPropagationExpr &) override {}
diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc
index 0fe585c239a0..b1403609226b 100644
--- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc
+++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc
@@ -506,6 +506,15 @@ ExprStmtBuilder::visit (HIR::RangeToInclExpr &expr)
 	       expr.get_locus ());
 }
 
+void
+ExprStmtBuilder::visit (HIR::BoxExpr &expr)
+{
+  PlaceId result = visit_expr (expr.get_expr ());
+
+  rust_assert (result != INVALID_PLACE);
+  push_tmp_assignment (result, expr.get_locus ());
+}
+
 void
 ExprStmtBuilder::visit (HIR::ReturnExpr &ret)
 {
diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h
index 198f3c8fdb17..8d50b0715f95 100644
--- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h
+++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h
@@ -95,6 +95,7 @@ protected: // Expr
   void visit (HIR::RangeFullExpr &expr) override;
   void visit (HIR::RangeFromToInclExpr &expr) override;
   void visit (HIR::RangeToInclExpr &expr) override;
+  void visit (HIR::BoxExpr &expr) override;
   void visit (HIR::ReturnExpr &ret) override;
   void visit (HIR::UnsafeBlockExpr &expr) override;
   void visit (HIR::LoopExpr &expr) override;
diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h
index e7de77b9f957..d360508efc95 100644
--- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h
+++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h
@@ -240,6 +240,7 @@ protected: // Illegal at this position.
   void visit (HIR::RangeFromToInclExpr &expr) override { rust_unreachable (); }
   void visit (HIR::RangeToInclExpr &expr) override { rust_unreachable (); }
   void visit (HIR::ReturnExpr &expr) override { rust_unreachable (); }
+  void visit (HIR::BoxExpr &expr) override { rust_unreachable (); }
 };
 
 } // namespace BIR
diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h
index 5311ba872f91..7c669e1cc902 100644
--- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h
+++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h
@@ -145,6 +145,7 @@ protected:
   void visit (HIR::RangeFullExpr &expr) override { rust_unreachable (); }
   void visit (HIR::RangeFromToInclExpr &expr) override { rust_unreachable (); }
   void visit (HIR::RangeToInclExpr &expr) override { rust_unreachable (); }
+  void visit (HIR::BoxExpr &expr) override { rust_unreachable (); }
   void visit (HIR::ReturnExpr &expr) override { rust_unreachable (); }
   void visit (HIR::UnsafeBlockExpr &expr) override { rust_unreachable (); }
   void visit (HIR::LoopExpr &expr) override { rust_unreachable (); }
diff --git a/gcc/rust/checks/errors/borrowck/rust-function-collector.h b/gcc/rust/checks/errors/borrowck/rust-function-collector.h
index a076bcaabf26..439f5b37b562 100644
--- a/gcc/rust/checks/errors/borrowck/rust-function-collector.h
+++ b/gcc/rust/checks/errors/borrowck/rust-function-collector.h
@@ -115,6 +115,7 @@ public:
   void visit (HIR::RangeFullExpr &expr) override {}
   void visit (HIR::RangeFromToInclExpr &expr) override {}
   void visit (HIR::RangeToInclExpr &expr) override {}
+  void visit (HIR::BoxExpr &expr) override {}
   void visit (HIR::ReturnExpr &expr) override {}
   void visit (HIR::UnsafeBlockExpr &expr) override {}
   void visit (HIR::LoopExpr &expr) override {}
diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc
index 868c5679b5cf..2b4e87421de5 100644
--- a/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc
+++ b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc
@@ -588,6 +588,12 @@ PrivacyReporter::visit (HIR::RangeToInclExpr &)
   // Not handled yet
 }
 
+void
+PrivacyReporter::visit (HIR::BoxExpr &expr)
+{
+  expr.get_expr ().accept_vis (*this);
+}
+
 void
 PrivacyReporter::visit (HIR::ReturnExpr &expr)
 {
diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-reporter.h b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.h
index 07627b1d6a62..d8d37a537873 100644
--- a/gcc/rust/checks/errors/privacy/rust-privacy-reporter.h
+++ b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.h
@@ -127,6 +127,7 @@ types
   virtual void visit (HIR::RangeFullExpr &expr);
   virtual void visit (HIR::RangeFromToInclExpr &expr);
   virtual void visit (HIR::RangeToInclExpr &expr);
+  virtual void visit (HIR::BoxExpr &expr);
   virtual void visit (HIR::ReturnExpr &expr);
   virtual void visit (HIR::UnsafeBlockExpr &expr);
   virtual void visit (HIR::LoopExpr &expr);
diff --git a/gcc/rust/checks/errors/rust-const-checker.cc b/gcc/rust/checks/errors/rust-const-checker.cc
index 39dd7095695d..7cfd81a4aa03 100644
--- a/gcc/rust/checks/errors/rust-const-checker.cc
+++ b/gcc/rust/checks/errors/rust-const-checker.cc
@@ -472,6 +472,12 @@ ConstChecker::visit (RangeToInclExpr &)
   // FIXME: Visit to_expr
 }
 
+void
+ConstChecker::visit (BoxExpr &expr)
+{
+  expr.get_expr ().accept_vis (*this);
+}
+
 void
 ConstChecker::visit (ReturnExpr &expr)
 {
diff --git a/gcc/rust/checks/errors/rust-const-checker.h b/gcc/rust/checks/errors/rust-const-checker.h
index 1dae715d8f3a..d2c9b1bf4317 100644
--- a/gcc/rust/checks/errors/rust-const-checker.h
+++ b/gcc/rust/checks/errors/rust-const-checker.h
@@ -123,6 +123,7 @@ private:
   virtual void visit (RangeFullExpr &expr) override;
   virtual void visit (RangeFromToInclExpr &expr) override;
   virtual void visit (RangeToInclExpr &expr) override;
+  virtual void visit (BoxExpr &expr) override;
   virtual void visit (ReturnExpr &expr) override;
   virtual void visit (UnsafeBlockExpr &expr) override;
   virtual void visit (LoopExpr &expr) override;
diff --git a/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc b/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc
index 460ded1b258d..ed1bd49baa67 100644
--- a/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc
+++ b/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc
@@ -343,6 +343,12 @@ PatternChecker::visit (RangeToInclExpr &expr)
   expr.get_to_expr ().accept_vis (*this);
 }
 
+void
+PatternChecker::visit (BoxExpr &expr)
+{
+  expr.get_expr ().accept_vis (*this);
+}
+
 void
 PatternChecker::visit (ReturnExpr &expr)
 {
diff --git a/gcc/rust/checks/errors/rust-hir-pattern-analysis.h b/gcc/rust/checks/errors/rust-hir-pattern-analysis.h
index 433797ac035b..eba88e306b72 100644
--- a/gcc/rust/checks/errors/rust-hir-pattern-analysis.h
+++ b/gcc/rust/checks/errors/rust-hir-pattern-analysis.h
@@ -97,6 +97,7 @@ private:
   virtual void visit (RangeFullExpr &expr) override;
   virtual void visit (RangeFromToInclExpr &expr) override;
   virtual void visit (RangeToInclExpr &expr) override;
+  virtual void visit (BoxExpr &expr) override;
   virtual void visit (ReturnExpr &expr) override;
   virtual void visit (UnsafeBlockExpr &expr) override;
   virtual void visit (LoopExpr &expr) override;
diff --git a/gcc/rust/checks/errors/rust-unsafe-checker.cc b/gcc/rust/checks/errors/rust-unsafe-checker.cc
index 379cd4c9a381..0868aac3ae50 100644
--- a/gcc/rust/checks/errors/rust-unsafe-checker.cc
+++ b/gcc/rust/checks/errors/rust-unsafe-checker.cc
@@ -573,6 +573,12 @@ UnsafeChecker::visit (RangeToInclExpr &expr)
   expr.get_to_expr ().accept_vis (*this);
 }
 
+void
+UnsafeChecker::visit (BoxExpr &expr)
+{
+  expr.get_expr ().accept_vis (*this);
+}
+
 void
 UnsafeChecker::visit (ReturnExpr &expr)
 {
diff --git a/gcc/rust/checks/errors/rust-unsafe-checker.h b/gcc/rust/checks/errors/rust-unsafe-checker.h
index ee38c7e6649f..c964837ec1d5 100644
--- a/gcc/rust/checks/errors/rust-unsafe-checker.h
+++ b/gcc/rust/checks/errors/rust-unsafe-checker.h
@@ -105,6 +105,7 @@ private:
   virtual void visit (RangeFullExpr &expr) override;
   virtual void visit (RangeFromToInclExpr &expr) override;
   virtual void visit (RangeToInclExpr &expr) override;
+  virtual void visit (BoxExpr &expr) override;
   virtual void visit (ReturnExpr &expr) override;
   virtual void visit (UnsafeBlockExpr &expr) override;
   virtual void visit (LoopExpr &expr) override;
diff --git a/gcc/rust/checks/lints/rust-lint-marklive.cc b/gcc/rust/checks/lints/rust-lint-marklive.cc
index 8fc81b3631fa..796e47de467f 100644
--- a/gcc/rust/checks/lints/rust-lint-marklive.cc
+++ b/gcc/rust/checks/lints/rust-lint-marklive.cc
@@ -28,6 +28,7 @@
 #include "rust-finalized-name-resolution-context.h"
 #include "rust-rib.h"
 #include "rust-system.h"
+#include "rust-tyty.h"
 
 namespace Rust {
 namespace Analysis {
@@ -199,6 +200,12 @@ MarkLive::visit (HIR::FieldAccessExpr &expr)
   if (receiver->get_kind () == TyTy::TypeKind::ADT)
     {
       adt = static_cast<TyTy::ADTType *> (receiver);
+
+      if (auto inner_ty = TyTy::try_get_box_inner_type (receiver))
+	{
+	  rust_assert ((*inner_ty)->get_kind () == TyTy::TypeKind::ADT);
+	  adt = static_cast<TyTy::ADTType *> (*inner_ty);
+	}
     }
   else if (receiver->get_kind () == TyTy::TypeKind::REF)
     {
diff --git a/gcc/rust/checks/lints/rust-lint-marklive.h b/gcc/rust/checks/lints/rust-lint-marklive.h
index 562104f671e3..bea28e55378b 100644
--- a/gcc/rust/checks/lints/rust-lint-marklive.h
+++ b/gcc/rust/checks/lints/rust-lint-marklive.h
@@ -139,6 +139,11 @@ public:
     function.get_definition ().accept_vis (*this);
   }
 
+  void visit (HIR::BoxExpr &expr) override
+  {
+    expr.get_expr ().accept_vis (*this);
+  }
+
   void visit (HIR::ReturnExpr &expr) override
   {
     if (expr.has_return_expr ())
diff --git a/gcc/rust/hir/rust-ast-lower-expr.cc b/gcc/rust/hir/rust-ast-lower-expr.cc
index 47f98f3ad656..f41ba8bcf8cb 100644
--- a/gcc/rust/hir/rust-ast-lower-expr.cc
+++ b/gcc/rust/hir/rust-ast-lower-expr.cc
@@ -192,8 +192,15 @@ ASTLoweringExpr::visit (AST::QualifiedPathInExpression &expr)
 void
 ASTLoweringExpr::visit (AST::BoxExpr &expr)
 {
-  rust_sorry_at (expr.get_locus (),
-		 "box expression syntax is not supported yet");
+  HIR::Expr *box_expr = ASTLoweringExpr::translate (expr.get_boxed_expr ());
+
+  auto crate_num = mappings.get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings.get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated = new HIR::BoxExpr (mapping, expr.get_locus (),
+				 std::unique_ptr<HIR::Expr> (box_expr));
 }
 
 void
diff --git a/gcc/rust/hir/rust-hir-dump.cc b/gcc/rust/hir/rust-hir-dump.cc
index 4e6545d021ff..4228788bcd44 100644
--- a/gcc/rust/hir/rust-hir-dump.cc
+++ b/gcc/rust/hir/rust-hir-dump.cc
@@ -1412,6 +1412,17 @@ Dump::visit (RangeToInclExpr &e)
   end ("RangeToInclExpr");
 }
 
+void
+Dump::visit (BoxExpr &e)
+{
+  begin ("BoxExpr");
+  do_mappings (e.get_mappings ());
+
+  visit_field ("box_expr", e.get_expr ());
+
+  end ("BoxExpr");
+}
+
 void
 Dump::visit (ReturnExpr &e)
 {
diff --git a/gcc/rust/hir/rust-hir-dump.h b/gcc/rust/hir/rust-hir-dump.h
index e97072dfb776..c6496497d2f7 100644
--- a/gcc/rust/hir/rust-hir-dump.h
+++ b/gcc/rust/hir/rust-hir-dump.h
@@ -156,6 +156,7 @@ private:
   virtual void visit (RangeFullExpr &) override;
   virtual void visit (RangeFromToInclExpr &) override;
   virtual void visit (RangeToInclExpr &) override;
+  virtual void visit (BoxExpr &) override;
   virtual void visit (ReturnExpr &) override;
   virtual void visit (UnsafeBlockExpr &) override;
   virtual void visit (LoopExpr &) override;
diff --git a/gcc/rust/hir/tree/rust-hir-expr-abstract.h b/gcc/rust/hir/tree/rust-hir-expr-abstract.h
index a3d77f668856..24fbffac309e 100644
--- a/gcc/rust/hir/tree/rust-hir-expr-abstract.h
+++ b/gcc/rust/hir/tree/rust-hir-expr-abstract.h
@@ -75,6 +75,7 @@ public:
     InlineAsm,
     LlvmInlineAsm,
     OffsetOf,
+    Box,
   };
 
   BaseKind get_hir_kind () override final { return Node::BaseKind::EXPR; }
diff --git a/gcc/rust/hir/tree/rust-hir-expr.cc b/gcc/rust/hir/tree/rust-hir-expr.cc
index fe5d4b7ac8d8..b93309cdc79f 100644
--- a/gcc/rust/hir/tree/rust-hir-expr.cc
+++ b/gcc/rust/hir/tree/rust-hir-expr.cc
@@ -972,6 +972,33 @@ RangeToInclExpr::operator= (RangeToInclExpr const &other)
   return *this;
 }
 
+BoxExpr::BoxExpr (Analysis::NodeMapping mappings, location_t locus,
+		  std::unique_ptr<Expr> expr, AST::AttrVec outer_attribs)
+  : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+    expr (std::move (expr)), locus (locus)
+{
+  rust_assert (this->expr != nullptr);
+}
+
+BoxExpr::BoxExpr (BoxExpr const &other)
+  : ExprWithoutBlock (other), locus (other.locus)
+{
+  rust_assert (other.expr != nullptr);
+  expr = other.expr->clone_expr ();
+}
+
+BoxExpr &
+BoxExpr::operator= (BoxExpr const &other)
+{
+  ExprWithoutBlock::operator= (other);
+
+  rust_assert (other.expr != nullptr);
+  expr = other.expr->clone_expr ();
+  locus = other.locus;
+
+  return *this;
+}
+
 ReturnExpr::ReturnExpr (Analysis::NodeMapping mappings, location_t locus,
 			std::unique_ptr<Expr> returned_expr,
 			AST::AttrVec outer_attribs)
diff --git a/gcc/rust/hir/tree/rust-hir-expr.h b/gcc/rust/hir/tree/rust-hir-expr.h
index ffa2de76e7bf..ad14764eba48 100644
--- a/gcc/rust/hir/tree/rust-hir-expr.h
+++ b/gcc/rust/hir/tree/rust-hir-expr.h
@@ -2267,6 +2267,52 @@ protected:
   }
 };
 
+// Box expression HIR node representation
+class BoxExpr : public ExprWithoutBlock
+{
+public:
+  std::unique_ptr<Expr> expr;
+
+  location_t locus;
+
+  std::string to_string () const override;
+
+  // Constructor for BoxExpr.
+  BoxExpr (Analysis::NodeMapping mappings, location_t locus,
+	   std::unique_ptr<Expr> expr,
+	   AST::AttrVec outer_attribs = AST::AttrVec ());
+  // Copy constructor with clone
+  BoxExpr (BoxExpr const &other);
+
+  // Overloaded assignment operator to clone expr pointer
+  BoxExpr &operator= (BoxExpr const &other);
+
+  // move constructors
+  BoxExpr (BoxExpr &&other) = default;
+  BoxExpr &operator= (BoxExpr &&other) = default;
+
+  location_t get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Expr &get_expr () { return *expr; }
+
+  ExprType get_expression_type () const override final { return ExprType::Box; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BoxExpr *clone_expr_impl () const override { return new BoxExpr (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BoxExpr *clone_expr_without_block_impl () const override
+  {
+    return new BoxExpr (*this);
+  }
+};
+
 // Return expression HIR node representation
 class ReturnExpr : public ExprWithoutBlock
 {
diff --git a/gcc/rust/hir/tree/rust-hir-full-decls.h b/gcc/rust/hir/tree/rust-hir-full-decls.h
index dfafee1a35cd..33a1986bccdc 100644
--- a/gcc/rust/hir/tree/rust-hir-full-decls.h
+++ b/gcc/rust/hir/tree/rust-hir-full-decls.h
@@ -106,6 +106,7 @@ class RangeToExpr;
 class RangeFullExpr;
 class RangeFromToInclExpr;
 class RangeToInclExpr;
+class BoxExpr;
 class ReturnExpr;
 class UnsafeBlockExpr;
 class LoopLabel;
diff --git a/gcc/rust/hir/tree/rust-hir-visitor.cc b/gcc/rust/hir/tree/rust-hir-visitor.cc
index db4b2daa83ce..5128ff4e1ca4 100644
--- a/gcc/rust/hir/tree/rust-hir-visitor.cc
+++ b/gcc/rust/hir/tree/rust-hir-visitor.cc
@@ -435,6 +435,13 @@ DefaultHIRVisitor::walk (RangeToInclExpr &expr)
   expr.get_to_expr ().accept_vis (*this);
 }
 
+void
+DefaultHIRVisitor::walk (BoxExpr &expr)
+{
+  visit_outer_attrs (expr);
+  expr.get_expr ().accept_vis (*this);
+}
+
 void
 DefaultHIRVisitor::walk (ReturnExpr &expr)
 {
diff --git a/gcc/rust/hir/tree/rust-hir-visitor.h b/gcc/rust/hir/tree/rust-hir-visitor.h
index e52938222bc4..d39c84fbb29e 100644
--- a/gcc/rust/hir/tree/rust-hir-visitor.h
+++ b/gcc/rust/hir/tree/rust-hir-visitor.h
@@ -76,6 +76,7 @@ public:
   virtual void visit (RangeFullExpr &expr) = 0;
   virtual void visit (RangeFromToInclExpr &expr) = 0;
   virtual void visit (RangeToInclExpr &expr) = 0;
+  virtual void visit (BoxExpr &expr) = 0;
   virtual void visit (ReturnExpr &expr) = 0;
   virtual void visit (UnsafeBlockExpr &expr) = 0;
   virtual void visit (LoopExpr &expr) = 0;
@@ -245,6 +246,7 @@ public:
   virtual void visit (RangeFullExpr &node) override { walk (node); }
   virtual void visit (RangeFromToInclExpr &node) override { walk (node); }
   virtual void visit (RangeToInclExpr &node) override { walk (node); }
+  virtual void visit (BoxExpr &node) override { walk (node); }
   virtual void visit (ReturnExpr &node) override { walk (node); }
   virtual void visit (UnsafeBlockExpr &node) override { walk (node); }
   virtual void visit (LoopExpr &node) override { walk (node); }
@@ -386,6 +388,7 @@ protected:
   virtual void walk (RangeFullExpr &) final;
   virtual void walk (RangeFromToInclExpr &) final;
   virtual void walk (RangeToInclExpr &) final;
+  virtual void walk (BoxExpr &) final;
   virtual void walk (ReturnExpr &) final;
   virtual void walk (UnsafeBlockExpr &) final;
   virtual void walk (LoopExpr &) final;
@@ -527,6 +530,7 @@ public:
   virtual void visit (RangeFullExpr &) override {}
   virtual void visit (RangeFromToInclExpr &) override {}
   virtual void visit (RangeToInclExpr &) override {}
+  virtual void visit (BoxExpr &) override {}
   virtual void visit (ReturnExpr &) override {}
   virtual void visit (UnsafeBlockExpr &) override {}
   virtual void visit (LoopExpr &) override {}
@@ -759,6 +763,7 @@ public:
   virtual void visit (RangeFullExpr &expr) = 0;
   virtual void visit (RangeFromToInclExpr &expr) = 0;
   virtual void visit (RangeToInclExpr &expr) = 0;
+  virtual void visit (BoxExpr &expr) = 0;
   virtual void visit (ReturnExpr &expr) = 0;
   virtual void visit (UnsafeBlockExpr &expr) = 0;
   virtual void visit (LoopExpr &expr) = 0;
diff --git a/gcc/rust/hir/tree/rust-hir.cc b/gcc/rust/hir/tree/rust-hir.cc
index ada104497069..2b9bd5670146 100644
--- a/gcc/rust/hir/tree/rust-hir.cc
+++ b/gcc/rust/hir/tree/rust-hir.cc
@@ -1308,6 +1308,15 @@ BorrowExpr::to_string () const
   return str;
 }
 
+std::string
+BoxExpr::to_string () const
+{
+  std::string str = "box ";
+  rust_assert (expr != nullptr);
+  str += expr->to_string ();
+  return str;
+}
+
 std::string
 ReturnExpr::to_string () const
 {
@@ -4249,6 +4258,12 @@ RangeToInclExpr::accept_vis (HIRFullVisitor &vis)
   vis.visit (*this);
 }
 
+void
+BoxExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
 void
 ReturnExpr::accept_vis (HIRFullVisitor &vis)
 {
@@ -5131,6 +5146,12 @@ RangeToExpr::accept_vis (HIRExpressionVisitor &vis)
   vis.visit (*this);
 }
 
+void
+BoxExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
 void
 ReturnExpr::accept_vis (HIRExpressionVisitor &vis)
 {
diff --git a/gcc/rust/typecheck/rust-autoderef.cc b/gcc/rust/typecheck/rust-autoderef.cc
index 0c20d41c596a..4d323d62818e 100644
--- a/gcc/rust/typecheck/rust-autoderef.cc
+++ b/gcc/rust/typecheck/rust-autoderef.cc
@@ -330,6 +330,21 @@ AutoderefCycle::cycle (TyTy::BaseType *receiver)
       if (autoderef_flag)
 	return false;
 
+      // try owned_box
+      if (auto deref_r = try_get_box_inner_type (r))
+	{
+	  Adjustment box_deref (Adjustment::AdjustmentType::DEREF, r, *deref_r);
+	  adjustments.push_back (box_deref);
+
+	  rust_debug ("autoderef try owned_box: {%s}",
+		      (*deref_r)->debug_str ().c_str ());
+
+	  if (try_autoderefed (*deref_r))
+	    return true;
+
+	  adjustments.pop_back ();
+	}
+
       // try unsize
       Adjustment unsize = Adjuster::try_unsize_type (r);
       if (!unsize.is_error ())
diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.cc b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
index c46338fb297a..8ed04d635fe9 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-expr.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
@@ -184,6 +184,65 @@ TypeCheckExpr::visit (HIR::TupleExpr &expr)
 				 expr.get_locus (), fields);
 }
 
+void
+TypeCheckExpr::visit (HIR::BoxExpr &expr)
+{
+  auto owned_box_defid
+    = mappings.get_lang_item (LangItem::Kind::OWNED_BOX, expr.get_locus ());
+
+  HIR::Item *item = mappings.lookup_defid (owned_box_defid).value ();
+  TyTy::BaseType *item_type = nullptr;
+
+  bool ok
+    = context->lookup_type (item->get_mappings ().get_hirid (), &item_type);
+  rust_assert (ok);
+  if (item_type->get_kind () != TyTy::TypeKind::ADT)
+    {
+      rust_error_at (item->get_locus (), ErrorCode::E0718,
+		     "%qs language item must be applied to a struct",
+		     "owned_box");
+      return;
+    }
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (item_type);
+  if (!adt->is_tuple_struct () && !adt->is_struct_struct ())
+    {
+      rust_error_at (item->get_locus (), ErrorCode::E0718,
+		     "%qs language item must be applied to a struct",
+		     "owned_box");
+      return;
+    }
+
+  // this is at least one generic item
+  if (adt->get_num_substitutions () < 1)
+    {
+      rust_error_at (expr.get_locus (),
+		     "%qs lang item must be applied to a struct with at least "
+		     "1 generic argument",
+		     "owned_box");
+      return;
+    }
+
+  TyTy::BaseType *inner_ty = TypeCheckExpr::Resolve (expr.get_expr ());
+  if (inner_ty->get_kind () == TyTy::TypeKind::ERROR)
+    {
+      infered = inner_ty;
+      return;
+    }
+
+  auto lookup = SubstMapper::InferSubst (adt, expr.get_locus ());
+  rust_assert (lookup->get_kind () == TyTy::TypeKind::ADT);
+  TyTy::ADTType *adt_box = static_cast<TyTy::ADTType *> (lookup);
+
+  TyTy::BaseType *infer = adt_box->get_substs ().at (0).get_param_ty ();
+
+  unify_site (expr.get_mappings ().get_hirid (),
+	      TyTy::TyWithLocation (infer, expr.get_locus ()),
+	      TyTy::TyWithLocation (inner_ty, expr.get_locus ()),
+	      expr.get_locus ());
+
+  infered = adt_box;
+}
+
 void
 TypeCheckExpr::visit (HIR::ReturnExpr &expr)
 {
@@ -1285,6 +1344,12 @@ TypeCheckExpr::visit (HIR::FieldAccessExpr &expr)
 {
   auto struct_base = TypeCheckExpr::Resolve (expr.get_receiver_expr ());
 
+  // Box<T> autoderef
+  if (auto try_struct_base = TyTy::try_get_box_inner_type (struct_base))
+    {
+      struct_base = *try_struct_base;
+    }
+
   // FIXME does this require autoderef here?
   if (struct_base->get_kind () == TyTy::TypeKind::REF)
     {
@@ -1728,14 +1793,21 @@ TypeCheckExpr::visit (HIR::DereferenceExpr &expr)
 
   bool is_valid_type = resolved_base->get_kind () == TyTy::TypeKind::REF
 		       || resolved_base->get_kind () == TyTy::TypeKind::POINTER;
-  if (!is_valid_type)
+
+  auto try_owned_box = TyTy::try_get_box_inner_type (resolved_base);
+
+  if (!is_valid_type && !try_owned_box)
     {
       rust_error_at (expr.get_locus (), "expected reference type got %s",
 		     resolved_base->as_string ().c_str ());
       return;
     }
 
-  if (resolved_base->get_kind () == TyTy::TypeKind::REF)
+  if (try_owned_box)
+    {
+      infered = (*try_owned_box)->clone ();
+    }
+  else if (resolved_base->get_kind () == TyTy::TypeKind::REF)
     {
       TyTy::ReferenceType *ref_base
 	= static_cast<TyTy::ReferenceType *> (resolved_base);
diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.h b/gcc/rust/typecheck/rust-hir-type-check-expr.h
index 03157bd281f5..44e870697ce0 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-expr.h
+++ b/gcc/rust/typecheck/rust-hir-type-check-expr.h
@@ -80,6 +80,7 @@ public:
   void visit (HIR::InlineAsm &expr) override;
   void visit (HIR::LlvmInlineAsm &expr) override;
   void visit (HIR::OffsetOf &expr) override;
+  void visit (HIR::BoxExpr &expr) override;
 
   // TODO
   void visit (HIR::ErrorPropagationExpr &) override {}
diff --git a/gcc/rust/typecheck/rust-tyty.cc b/gcc/rust/typecheck/rust-tyty.cc
index 9ad09a323d06..66f5fc5cb2c9 100644
--- a/gcc/rust/typecheck/rust-tyty.cc
+++ b/gcc/rust/typecheck/rust-tyty.cc
@@ -4538,5 +4538,28 @@ DynamicObjectType::get_object_items () const
   return items;
 }
 
+WARN_UNUSED_RESULT tl::optional<BaseType *>
+try_get_box_inner_type (BaseType *base)
+{
+  if (base->get_kind () != TypeKind::ADT)
+    return tl::nullopt;
+
+  ADTType *adt = static_cast<ADTType *> (base);
+  auto owned_box_lookup
+    = Analysis::Mappings::get ().lookup_lang_item (LangItem::Kind::OWNED_BOX);
+
+  if (owned_box_lookup && adt->get_id () == *owned_box_lookup)
+    {
+      auto args = adt->get_substitution_arguments ();
+      if (!args.is_empty ())
+	{
+	  auto inner = args.get_mappings ().front ().get_tyty ();
+	  rust_assert (inner != nullptr);
+	  return inner;
+	}
+    }
+  return tl::nullopt;
+}
+
 } // namespace TyTy
 } // namespace Rust
diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h
index b27de7422c81..b5e5f02c6782 100644
--- a/gcc/rust/typecheck/rust-tyty.h
+++ b/gcc/rust/typecheck/rust-tyty.h
@@ -1960,6 +1960,9 @@ BaseType::try_as<const SubstitutionRef> () const
   return nullptr;
 }
 
+WARN_UNUSED_RESULT tl::optional<BaseType *>
+try_get_box_inner_type (BaseType *base);
+
 } // namespace TyTy
 } // namespace Rust
 
diff --git a/gcc/rust/util/rust-lang-item.cc b/gcc/rust/util/rust-lang-item.cc
index 2f7704546575..91284e27b088 100644
--- a/gcc/rust/util/rust-lang-item.cc
+++ b/gcc/rust/util/rust-lang-item.cc
@@ -125,6 +125,7 @@ const BiMap<std::string, LangItem::Kind> Rust::LangItem::lang_items = {{
   {"manually_drop", Kind::MANUALLY_DROP},
 
   {"exchange_malloc", Kind::EXCHANGE_MALLOC},
+  {"owned_box", Kind::OWNED_BOX},
 }};
 
 tl::optional<LangItem::Kind>
diff --git a/gcc/rust/util/rust-lang-item.h b/gcc/rust/util/rust-lang-item.h
index 75f4090b97de..d96fa9e6a789 100644
--- a/gcc/rust/util/rust-lang-item.h
+++ b/gcc/rust/util/rust-lang-item.h
@@ -160,7 +160,8 @@ public:
 
     MANUALLY_DROP,
 
-    EXCHANGE_MALLOC
+    EXCHANGE_MALLOC,
+    OWNED_BOX,
   };
 
   static const BiMap<std::string, Kind> lang_items;
diff --git a/gcc/testsuite/rust/compile/box-expr.rs b/gcc/testsuite/rust/compile/box-expr.rs
new file mode 100644
index 000000000000..979977658195
--- /dev/null
+++ b/gcc/testsuite/rust/compile/box-expr.rs
@@ -0,0 +1,55 @@
+#![no_core]
+#![feature(no_core, lang_items, box_syntax)]
+
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "phantom_data"]
+pub struct PhantomData<T: ?Sized>;
+
+pub struct NonNull<T: ?Sized> {
+    _pointer: *const T,
+}
+
+pub struct Unique<T: ?Sized> {
+    _pointer: NonNull<T>,
+    _marker: PhantomData<T>,
+}
+
+pub struct Global;
+
+#[lang = "exchange_malloc"]
+fn _exchange_malloc() -> *mut u8 {
+    0 as *mut u8
+}
+
+#[lang = "owned_box"]
+pub struct Box<T, A = Global>(Unique<T>, A);
+
+impl<T> Box<T> {
+    pub fn new(x: T) -> Self {
+        box x
+    }
+}
+
+struct NonCopyStruct {
+    id: i32,
+}
+
+impl NonCopyStruct {
+    fn get_id(&self) -> i32 {
+        self.id
+    }
+}
+
+fn main() {
+    let my_box: Box<NonCopyStruct> = Box::new(NonCopyStruct { id: 42 });
+    let _moved_val = *my_box;
+
+
+    let my_box2: Box<NonCopyStruct> = Box::new(NonCopyStruct { id: 100 });
+
+    let _val_id = my_box2.id;
+
+    let _val_method_id = my_box2.get_id();
+}
diff --git a/gcc/testsuite/rust/compile/lang-owned-box-error1.rs b/gcc/testsuite/rust/compile/lang-owned-box-error1.rs
new file mode 100644
index 000000000000..54258ddacb3e
--- /dev/null
+++ b/gcc/testsuite/rust/compile/lang-owned-box-error1.rs
@@ -0,0 +1,15 @@
+#![feature(no_core, lang_items, box_syntax)]
+#![no_core]
+
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "owned_box"]
+pub enum EnumBox<T> { // { dg-error "language item must be applied to a struct" }
+    Some(T),
+    None,
+}
+
+fn _foo() {
+    let _ = box 5;
+}
diff --git a/gcc/testsuite/rust/compile/lang-owned-box-error2.rs b/gcc/testsuite/rust/compile/lang-owned-box-error2.rs
new file mode 100644
index 000000000000..8a57dfa690f6
--- /dev/null
+++ b/gcc/testsuite/rust/compile/lang-owned-box-error2.rs
@@ -0,0 +1,12 @@
+#![feature(no_core, lang_items, box_syntax)]
+#![no_core]
+
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "owned_box"]
+pub struct BadBox;
+
+fn _foo() {
+    let _ = box 5; // { dg-error "lang item must be applied to a struct with at least 1 generic argument" }
+}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.