diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc index 55ff3263a11..4bcd11da416 100644 --- a/gcc/rust/backend/rust-compile-expr.cc +++ b/gcc/rust/backend/rust-compile-expr.cc @@ -769,10 +769,22 @@ CompileExpr::visit (HIR::StructExprStructFields &struct_expr) if (!adt->is_enum ()) { - translated - = Backend::constructor_expression (compiled_adt_type, adt->is_enum (), - arguments, union_disriminator, - struct_expr.get_locus ()); + auto repr_kind = adt->get_repr_options ().repr_kind; + if (repr_kind == TyTy::ADTType::ReprKind::TRANSPARENT) + { + translated + = fold_build1_loc (struct_expr.get_locus (), VIEW_CONVERT_EXPR, + compiled_adt_type, arguments.front ()); + } + else + { + translated + = Backend::constructor_expression (compiled_adt_type, + adt->is_enum (), arguments, + union_disriminator, + struct_expr.get_locus ()); + } + return; } @@ -843,6 +855,15 @@ CompileExpr::visit (HIR::FieldAccessExpr &expr) bool ok = variant->lookup_field (expr.get_field_name ().as_string (), nullptr, &field_index); rust_assert (ok); + + auto repr_kind = adt->get_repr_options ().repr_kind; + if (repr_kind == TyTy::ADTType::ReprKind::TRANSPARENT) + { + translated + = compile_transparent_field_access (variant, expr.get_locus (), + receiver_ref); + return; + } } else if (receiver->get_kind () == TyTy::TypeKind::REF) { @@ -859,16 +880,12 @@ CompileExpr::visit (HIR::FieldAccessExpr &expr) nullptr, &field_index); rust_assert (ok); - // TODO this check is only used for CStr, test again when we support - // compilation of #[repr(transparent)] structs - if (RS_DST_FLAG_P (TREE_TYPE (receiver_ref))) + auto repr_kind = adt->get_repr_options ().repr_kind; + if (repr_kind == TyTy::ADTType::ReprKind::TRANSPARENT) { - const TyTy::StructFieldType *field - = variant->get_field_at_index (field_index); - tree field_type - = TyTyResolveCompile::compile (ctx, field->get_field_type ()); - translated = fold_build1_loc (expr.get_locus (), VIEW_CONVERT_EXPR, - field_type, receiver_ref); + translated + = compile_transparent_field_access (variant, expr.get_locus (), + receiver_ref); return; } else @@ -2114,6 +2131,16 @@ CompileExpr::compile_c_string_literal (const HIR::LiteralExpr &expr, expr.get_locus ()); } +tree +CompileExpr::compile_transparent_field_access (TyTy::VariantDef *variant, + location_t locus, + tree source_expr) +{ + const TyTy::StructFieldType *field = variant->get_field_at_index (0); + tree field_type = TyTyResolveCompile::compile (ctx, field->get_field_type ()); + return fold_build1_loc (locus, VIEW_CONVERT_EXPR, field_type, source_expr); +} + tree CompileExpr::type_cast_expression (tree type_to_cast_to, tree expr_tree, location_t location) diff --git a/gcc/rust/backend/rust-compile-expr.h b/gcc/rust/backend/rust-compile-expr.h index 90e1985c993..ad5c483221a 100644 --- a/gcc/rust/backend/rust-compile-expr.h +++ b/gcc/rust/backend/rust-compile-expr.h @@ -145,6 +145,9 @@ class CompileExpr : private HIRCompileBase, protected HIR::HIRExpressionVisitor const TyTy::ArrayType &array_tyty, tree array_type, HIR::ArrayElemsCopied &elems); + tree compile_transparent_field_access (TyTy::VariantDef *variant, + location_t locus, tree source_expr); + protected: tree generate_closure_function (HIR::ClosureExpr &expr, TyTy::ClosureType &closure_tyty, diff --git a/gcc/rust/backend/rust-compile-type.cc b/gcc/rust/backend/rust-compile-type.cc index e71d404f1fa..01f80da52a4 100644 --- a/gcc/rust/backend/rust-compile-type.cc +++ b/gcc/rust/backend/rust-compile-type.cc @@ -295,7 +295,35 @@ void TyTyResolveCompile::visit (const TyTy::ADTType &type) { tree type_record = error_mark_node; - if (!type.is_enum ()) + + TyTy::ADTType::ReprOptions repr = type.get_repr_options (); + if (repr.repr_kind == TyTy::ADTType::ReprKind::TRANSPARENT) + { + rust_assert (type.number_of_variants () == 1); + TyTy::VariantDef &variant = *type.get_variants ().at (0); + + rust_assert (variant.num_fields () <= 1); + if (variant.num_fields () == 0) + { + // 0-field transparent repr + // Rustonomicon states that transparent structs should have a single + // non-zero-sized field, but rustc compiles one with 0 fields happily + // without errors, so not sure what's the correct treatment. + // + // For now, treat it as a unit struct + type_record = Backend::struct_type ({}); + } + else + { + // single field transparent repr + const TyTy::StructFieldType *field = variant.get_field_at_index (0); + type_record + = TyTyResolveCompile::compile (ctx, field->get_field_type ()); + } + } + + // compilation of non-transparent ADTs below + else if (!type.is_enum ()) { rust_assert (type.number_of_variants () == 1); @@ -442,22 +470,24 @@ TyTyResolveCompile::visit (const TyTy::ADTType &type) // TODO: "packed" should only narrow type alignment and "align" should only // widen it. Do we need to check and enforce this here, or is it taken care of // later on in the gcc middle-end? - TyTy::ADTType::ReprOptions repr = type.get_repr_options (); - if (repr.pack) + if (repr.repr_kind != TyTy::ADTType::ReprKind::TRANSPARENT) { - TYPE_PACKED (type_record) = 1; - if (repr.pack > 1) + if (repr.pack) + { + TYPE_PACKED (type_record) = 1; + if (repr.pack > 1) + { + SET_TYPE_ALIGN (type_record, repr.pack * 8); + TYPE_USER_ALIGN (type_record) = 1; + } + } + else if (repr.align) { - SET_TYPE_ALIGN (type_record, repr.pack * 8); + SET_TYPE_ALIGN (type_record, repr.align * 8); TYPE_USER_ALIGN (type_record) = 1; } + layout_type (type_record); } - else if (repr.align) - { - SET_TYPE_ALIGN (type_record, repr.align * 8); - TYPE_USER_ALIGN (type_record) = 1; - } - layout_type (type_record); std::string named_struct_str = type.get_ident ().path.get () + type.subst_as_string (); diff --git a/gcc/rust/typecheck/rust-hir-type-check-base.cc b/gcc/rust/typecheck/rust-hir-type-check-base.cc index 8b6b4d6f543..f966f002aa2 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-base.cc +++ b/gcc/rust/typecheck/rust-hir-type-check-base.cc @@ -517,12 +517,21 @@ TypeCheckBase::parse_repr_options (const AST::AttrVec &attrs, location_t locus) bool is_align = false; bool is_c = false; bool is_integer = false; + bool is_transparent = false; unsigned char value = 1; if (oparen == std::string::npos) { + if (inline_option.compare ("align") == 0) + { + rust_error_at (attr.get_locus (), ErrorCode::E0589, + "invalid % attribute: % " + "needs an argument"); + delete meta_items; + break; + } + is_pack = inline_option.compare ("packed") == 0; - is_align = inline_option.compare ("align") == 0; is_c = inline_option.compare ("C") == 0; is_integer = (inline_option.compare ("isize") == 0 || inline_option.compare ("i8") == 0 @@ -536,6 +545,7 @@ TypeCheckBase::parse_repr_options (const AST::AttrVec &attrs, location_t locus) || inline_option.compare ("u32") == 0 || inline_option.compare ("u64") == 0 || inline_option.compare ("u128") == 0); + is_transparent = inline_option.compare ("transparent") == 0; } else @@ -554,13 +564,26 @@ TypeCheckBase::parse_repr_options (const AST::AttrVec &attrs, location_t locus) value = strtoul (value_str.c_str () + 1, NULL, 10); } - if (is_pack) + if (is_transparent) + { + if (is_pack || is_align || is_c || is_integer) + rust_error_at ( + locus, ErrorCode::E0692, + "transparent struct cannot have other repr hints"); + + repr.repr_kind = TyTy::ADTType::ReprKind::TRANSPARENT; + } + else if (is_pack) { repr.repr_kind = TyTy::ADTType::ReprKind::PACKED; repr.pack = value; } else if (is_align) { + if (value == 0 || (value & (value - 1)) != 0) + rust_error_at ( + attr.get_locus (), ErrorCode::E0589, + "invalid % attribute: not a power of two"); repr.repr_kind = TyTy::ADTType::ReprKind::ALIGN; repr.align = value; } @@ -574,9 +597,15 @@ TypeCheckBase::parse_repr_options (const AST::AttrVec &attrs, location_t locus) bool ok = context->lookup_builtin (inline_option, &repr.repr); if (!ok) { - rust_error_at (attr.get_locus (), "Invalid repr type"); + rust_error_at (attr.get_locus (), ErrorCode::E0552, + "unrecognized representation hint"); } } + else + { + rust_error_at (attr.get_locus (), ErrorCode::E0552, + "unrecognized representation hint"); + } delete meta_items; diff --git a/gcc/rust/typecheck/rust-hir-type-check-item.cc b/gcc/rust/typecheck/rust-hir-type-check-item.cc index 065615a23b0..ac0eeaf277e 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-item.cc +++ b/gcc/rust/typecheck/rust-hir-type-check-item.cc @@ -339,6 +339,19 @@ TypeCheckItem::visit (HIR::StructStruct &struct_decl) ResolveWhereClauseItem::Resolve (*where_clause_item, region_constraints); } + // Process #[repr(X)] attribute, if any + const AST::AttrVec &attrs = struct_decl.get_outer_attrs (); + TyTy::ADTType::ReprOptions repr + = parse_repr_options (attrs, struct_decl.get_locus ()); + if (repr.repr_kind == TyTy::ADTType::ReprKind::TRANSPARENT + && struct_decl.get_fields ().size () > 1) + { + rust_error_at (struct_decl.get_locus (), ErrorCode::E0690, + "transparent struct needs at most one field with " + "non-trivial size or alignment, but has %lu", + (unsigned long) struct_decl.get_fields ().size ()); + } + std::vector fields; for (auto &field : struct_decl.get_fields ()) { @@ -379,11 +392,6 @@ TypeCheckItem::visit (HIR::StructStruct &struct_decl) struct_decl.get_identifier ().as_string (), ident, variant_type, tl::nullopt, std::move (fields))); - // Process #[repr(X)] attribute, if any - const AST::AttrVec &attrs = struct_decl.get_outer_attrs (); - TyTy::ADTType::ReprOptions repr - = parse_repr_options (attrs, struct_decl.get_locus ()); - auto *type = new TyTy::ADTType ( struct_decl.get_mappings ().get_defid (), struct_decl.get_mappings ().get_hirid (), diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h index b5e5f02c678..f645f673f86 100644 --- a/gcc/rust/typecheck/rust-tyty.h +++ b/gcc/rust/typecheck/rust-tyty.h @@ -910,7 +910,7 @@ class ADTType : public BaseType, public SubstitutionRef INT, ALIGN, PACKED, - // TRANSPARENT, + TRANSPARENT, // SIMD, // ... }; diff --git a/gcc/testsuite/rust/compile/c_string_null_byte_check.rs b/gcc/testsuite/rust/compile/c_string_null_byte_check.rs index 040ba9ad468..89a4bcdcaef 100644 --- a/gcc/testsuite/rust/compile/c_string_null_byte_check.rs +++ b/gcc/testsuite/rust/compile/c_string_null_byte_check.rs @@ -5,6 +5,7 @@ type c_char = u8; #[lang = "CStr"] +#[repr(transparent)] pub struct CStr { inner: [c_char] } diff --git a/gcc/testsuite/rust/compile/invalid_repr_hint.rs b/gcc/testsuite/rust/compile/invalid_repr_hint.rs new file mode 100644 index 00000000000..5e1af4d1c53 --- /dev/null +++ b/gcc/testsuite/rust/compile/invalid_repr_hint.rs @@ -0,0 +1,15 @@ +#![feature(no_core)] +#![no_core] + +#[repr(InvalidRepr)] // { dg-error "unrecognized representation hint" } +struct Foo { + x: i32, +} + +#[repr(align)] // { dg-error "invalid .repr.align.. attribute: .align. needs an argument" } +struct Bar { + x: i32, +} + +#[repr(align(3))] // { dg-error "invalid .repr.align.. attribute: not a power of two" } +struct Baz {} diff --git a/gcc/testsuite/rust/compile/repr_transparent_fields.rs b/gcc/testsuite/rust/compile/repr_transparent_fields.rs new file mode 100644 index 00000000000..19002138696 --- /dev/null +++ b/gcc/testsuite/rust/compile/repr_transparent_fields.rs @@ -0,0 +1,16 @@ +#![feature(no_core)] +#![no_core] + +#[repr(transparent)] +struct Foo { // { dg-error "transparent struct needs at most one field with non-trivial size or alignment, but has 2" } + foo: i32, + bar: i32 +} + +#[repr(transparent)] +struct Bar {} + +#[repr(transparent)] +struct Baz { + foo: i32 +} diff --git a/gcc/testsuite/rust/compile/repr_transparent_fields2.rs b/gcc/testsuite/rust/compile/repr_transparent_fields2.rs new file mode 100644 index 00000000000..611cd851c40 --- /dev/null +++ b/gcc/testsuite/rust/compile/repr_transparent_fields2.rs @@ -0,0 +1,21 @@ +// { dg-additional-options "-fdump-tree-gimple" } +#![feature(no_core)] +#![no_core] + +struct NonTransparent { + foo: i32 +} + +#[repr(transparent)] +struct Transparent { + foo: i32 +} + +fn main () -> i32 { + // { dg-final { scan-tree-dump-times {(?n)my_obj . 42;$} 1 gimple } } + let mut my_obj = Transparent { foo: 42 }; + // { dg-final { scan-tree-dump-times {(?n)my_obj2.foo . 40;$} 1 gimple } } + let my_obj2 = NonTransparent { foo: 40 }; + my_obj.foo -= 2; + my_obj.foo - my_obj2.foo +} \ No newline at end of file diff --git a/gcc/testsuite/rust/compile/transparent_struct_deref.rs b/gcc/testsuite/rust/compile/transparent_struct_deref.rs new file mode 100644 index 00000000000..97ca9301781 --- /dev/null +++ b/gcc/testsuite/rust/compile/transparent_struct_deref.rs @@ -0,0 +1,43 @@ +#![feature(no_core, intrinsics, staged_api, lang_items)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} + +// below's helper code copied from issue-1232.rs +extern "rust-intrinsic" { + #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] + fn offset(dst: *const T, offset: isize) -> *const T; +} + +#[lang = "const_ptr"] +impl *const T { + pub const unsafe fn offset(self, count: isize) -> *const T { + unsafe { offset(self, count) } + } + + pub const unsafe fn add(self, count: usize) -> Self { + unsafe { self.offset(count as isize) } + } + + pub const fn as_ptr(self) -> *const T { + self as *const T + } +} + +#[repr(transparent)] +pub struct Foo { + inner: i32 +} + +impl Foo { + pub const fn to_ptr(&self) -> *const i32 { + &self.inner as *const i32 + } +} + +pub fn main() -> i32 { + let a = Foo { inner: 67 }; + let val = unsafe { a.to_ptr() }; + unsafe { *val - 67 } +} diff --git a/gcc/testsuite/rust/execute/torture/c_string.rs b/gcc/testsuite/rust/execute/torture/c_string.rs index 5aef4db0c21..9f4cd5c036a 100644 --- a/gcc/testsuite/rust/execute/torture/c_string.rs +++ b/gcc/testsuite/rust/execute/torture/c_string.rs @@ -10,6 +10,7 @@ extern "C" { type c_char = u8; #[lang = "CStr"] +#[repr(transparent)] pub struct CStr { inner: [c_char] } diff --git a/gcc/testsuite/rust/execute/torture/c_string_ensure_null_term.rs b/gcc/testsuite/rust/execute/torture/c_string_ensure_null_term.rs index 26e0fed2173..60da8a1dd9e 100644 --- a/gcc/testsuite/rust/execute/torture/c_string_ensure_null_term.rs +++ b/gcc/testsuite/rust/execute/torture/c_string_ensure_null_term.rs @@ -33,6 +33,7 @@ extern "C" { type c_char = u8; #[lang = "CStr"] +#[repr(transparent)] pub struct CStr { inner: [c_char] }