public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc/devel/rust/master] Array index access does not need to unsize to a slice for access
@ 2022-08-06 12:10 Thomas Schwinge
  0 siblings, 0 replies; only message in thread
From: Thomas Schwinge @ 2022-08-06 12:10 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:c86ac620c2bdb852b8078f37b898ab40d96ec0b0

commit c86ac620c2bdb852b8078f37b898ab40d96ec0b0
Author: Philip Herron <philip.herron@embecosm.com>
Date:   Fri Aug 5 14:29:19 2022 +0100

    Array index access does not need to unsize to a slice for access
    
    When we define the core code for SliceIndex access its possible for an
    array to be fully coerced into a slice DST and follow the normal slice
    index access which removes support for GCC -Warray-index checks and
    generates unnessecary code for array access.
    
    Fixes #1436

Diff:
---
 gcc/rust/typecheck/rust-hir-type-check-expr.cc   |  63 +++++----
 gcc/testsuite/rust/compile/usize1.rs             |   2 +-
 gcc/testsuite/rust/execute/torture/issue-1436.rs | 172 +++++++++++++++++++++++
 3 files changed, 209 insertions(+), 28 deletions(-)

diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.cc b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
index 9c31284060e..ff1165d093f 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-expr.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
@@ -250,6 +250,35 @@ TypeCheckExpr::visit (HIR::ArrayIndexExpr &expr)
   if (index_expr_ty->get_kind () == TyTy::TypeKind::ERROR)
     return;
 
+  // first attempt to use direct array index logic
+  auto direct_array_expr_ty = array_expr_ty;
+  if (direct_array_expr_ty->get_kind () == TyTy::TypeKind::REF)
+    {
+      // lets try and deref it since rust allows this
+      auto ref = static_cast<TyTy::ReferenceType *> (direct_array_expr_ty);
+      auto base = ref->get_base ();
+      if (base->get_kind () == TyTy::TypeKind::ARRAY)
+	direct_array_expr_ty = base;
+    }
+
+  TyTy::BaseType *size_ty;
+  bool ok = context->lookup_builtin ("usize", &size_ty);
+  rust_assert (ok);
+
+  bool maybe_simple_array_access = index_expr_ty->can_eq (size_ty, false);
+  if (maybe_simple_array_access
+      && direct_array_expr_ty->get_kind () == TyTy::TypeKind::ARRAY)
+    {
+      auto resolved_index_expr = size_ty->unify (index_expr_ty);
+      if (resolved_index_expr->get_kind () == TyTy::TypeKind::ERROR)
+	return;
+
+      TyTy::ArrayType *array_type
+	= static_cast<TyTy::ArrayType *> (direct_array_expr_ty);
+      infered = array_type->get_element_type ()->clone ();
+      return;
+    }
+
   // is this a case of core::ops::index?
   auto lang_item_type = Analysis::RustLangItem::ItemType::INDEX;
   bool operator_overloaded
@@ -266,33 +295,13 @@ TypeCheckExpr::visit (HIR::ArrayIndexExpr &expr)
       return;
     }
 
-  if (array_expr_ty->get_kind () == TyTy::TypeKind::REF)
-    {
-      // lets try and deref it since rust allows this
-      auto ref = static_cast<TyTy::ReferenceType *> (array_expr_ty);
-      auto base = ref->get_base ();
-      if (base->get_kind () == TyTy::TypeKind::ARRAY)
-	array_expr_ty = base;
-    }
-
-  if (array_expr_ty->get_kind () != TyTy::TypeKind::ARRAY)
-    {
-      rust_error_at (expr.get_index_expr ()->get_locus (),
-		     "expected an ArrayType got [%s]",
-		     array_expr_ty->as_string ().c_str ());
-      return;
-    }
-
-  TyTy::BaseType *size_ty;
-  bool ok = context->lookup_builtin ("usize", &size_ty);
-  rust_assert (ok);
-
-  auto resolved_index_expr = size_ty->unify (index_expr_ty);
-  if (resolved_index_expr->get_kind () == TyTy::TypeKind::ERROR)
-    return;
-
-  TyTy::ArrayType *array_type = static_cast<TyTy::ArrayType *> (array_expr_ty);
-  infered = array_type->get_element_type ()->clone ();
+  // error[E0277]: the type `[{integer}]` cannot be indexed by `u32`
+  RichLocation r (expr.get_locus ());
+  r.add_range (expr.get_array_expr ()->get_locus ());
+  r.add_range (expr.get_index_expr ()->get_locus ());
+  rust_error_at (r, "the type %<%s%> cannot be indexed by %<%s%>",
+		 array_expr_ty->get_name ().c_str (),
+		 index_expr_ty->get_name ().c_str ());
 }
 
 void
diff --git a/gcc/testsuite/rust/compile/usize1.rs b/gcc/testsuite/rust/compile/usize1.rs
index c7e746bf449..b1c8fe86473 100644
--- a/gcc/testsuite/rust/compile/usize1.rs
+++ b/gcc/testsuite/rust/compile/usize1.rs
@@ -1,6 +1,6 @@
 fn main() {
     let a = [1, 2, 3];
     let b: u32 = 1;
-    let c = a[b]; // { dg-error "expected .usize. got .u32." }
+    let c = a[b]; // { dg-error "the type ...integer..CAPACITY.. cannot be indexed by .u32." }
                   // { dg-error {failed to type resolve expression} "" { target *-*-* } .-1 }
 }
diff --git a/gcc/testsuite/rust/execute/torture/issue-1436.rs b/gcc/testsuite/rust/execute/torture/issue-1436.rs
new file mode 100644
index 00000000000..32da34e105a
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/issue-1436.rs
@@ -0,0 +1,172 @@
+// { dg-options "-w" }
+// { dg-output "" }
+mod intrinsics {
+    extern "rust-intrinsic" {
+        pub fn offset<T>(ptr: *const T, count: isize) -> *const T;
+    }
+}
+
+mod mem {
+    extern "rust-intrinsic" {
+        fn size_of<T>() -> usize;
+    }
+}
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+struct FatPtr<T> {
+    data: *const T,
+    len: usize,
+}
+
+pub union Repr<T> {
+    rust: *const [T],
+    rust_mut: *mut [T],
+    raw: FatPtr<T>,
+}
+
+pub enum Option<T> {
+    None,
+    Some(T),
+}
+
+#[lang = "Range"]
+pub struct Range<Idx> {
+    pub start: Idx,
+    pub end: Idx,
+}
+
+#[lang = "const_slice_ptr"]
+impl<T> *const [T] {
+    pub const fn len(self) -> usize {
+        let a = unsafe { Repr { rust: self }.raw };
+        a.len
+    }
+
+    pub const fn as_ptr(self) -> *const T {
+        self as *const T
+    }
+}
+
+#[lang = "const_ptr"]
+impl<T> *const T {
+    pub const unsafe fn offset(self, count: isize) -> *const T {
+        unsafe { intrinsics::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
+    }
+}
+
+const fn slice_from_raw_parts<T>(data: *const T, len: usize) -> *const [T] {
+    unsafe {
+        Repr {
+            raw: FatPtr { data, len },
+        }
+        .rust
+    }
+}
+
+#[lang = "index"]
+trait Index<Idx> {
+    type Output;
+
+    fn index(&self, index: Idx) -> &Self::Output;
+}
+
+impl<T> [T] {
+    pub const fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+
+    pub const fn len(&self) -> usize {
+        unsafe { Repr { rust: self }.raw.len }
+    }
+}
+
+pub unsafe trait SliceIndex<T> {
+    type Output;
+
+    fn get(self, slice: &T) -> Option<&Self::Output>;
+
+    unsafe fn get_unchecked(self, slice: *const T) -> *const Self::Output;
+
+    fn index(self, slice: &T) -> &Self::Output;
+}
+
+unsafe impl<T> SliceIndex<[T]> for usize {
+    type Output = T;
+
+    fn get(self, slice: &[T]) -> Option<&T> {
+        unsafe { Option::Some(&*self.get_unchecked(slice)) }
+    }
+
+    unsafe fn get_unchecked(self, slice: *const [T]) -> *const T {
+        // SAFETY: the caller guarantees that `slice` is not dangling, so it
+        // cannot be longer than `isize::MAX`. They also guarantee that
+        // `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
+        // so the call to `add` is safe.
+        unsafe { slice.as_ptr().add(self) }
+    }
+
+    fn index(self, slice: &[T]) -> &T {
+        unsafe { &*self.get_unchecked(slice) }
+    }
+}
+
+unsafe impl<T> SliceIndex<[T]> for Range<usize> {
+    type Output = [T];
+
+    fn get(self, slice: &[T]) -> Option<&[T]> {
+        if self.start > self.end || self.end > slice.len() {
+            Option::None
+        } else {
+            unsafe { Option::Some(&*self.get_unchecked(slice)) }
+        }
+    }
+
+    unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
+        unsafe {
+            let a: *const T = slice.as_ptr();
+            let b: *const T = a.add(self.start);
+            slice_from_raw_parts(b, self.end - self.start)
+        }
+    }
+
+    fn index(self, slice: &[T]) -> &[T] {
+        unsafe { &*self.get_unchecked(slice) }
+    }
+}
+
+impl<T, I> Index<I> for [T]
+where
+    I: SliceIndex<[T]>,
+{
+    type Output = I::Output;
+
+    fn index(&self, index: I) -> &I::Output {
+        unsafe {
+            let a = "slice-index\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        index.index(self)
+    }
+}
+
+fn main() -> i32 {
+    let a = [1, 2, 3, 4, 5];
+    let b = a[1];
+
+    b - 2
+}


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-08-06 12:10 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-06 12:10 [gcc/devel/rust/master] Array index access does not need to unsize to a slice for access Thomas Schwinge

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).