From: Owen Avery <[email protected]>
Lang items might not already be resolved when an expression depending on
them is resolved.
The new test is a copy of for-loop1.rs in the same directory, but the
main function item is moved towards the top of the file. It still
produces a compiler error after type checking, but that can be resolved
in another patch.
gcc/rust/ChangeLog:
* typecheck/rust-hir-type-check-expr.cc
(TypeCheckExpr::visit (BoxExpr)): Attempt to resolve lang item,
instead of just looking it up.
(TypeCheckExpr::visit (RangeFromToExpr)): Likewise.
(TypeCheckExpr::visit (RangeFromExpr)): Likewise.
(TypeCheckExpr::visit (RangeToExpr)): Likewise.
(TypeCheckExpr::visit (RangeFullExpr)): Likewise.
gcc/testsuite/ChangeLog:
* rust/compile/for-loop3.rs: New test.
Signed-off-by: Owen Avery <[email protected]>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.
Commit on github: https://github.com/Rust-GCC/gccrs/commit/e56b411c5faef5f596efc6c5553c154cbc98f0ef
The commit has NOT been mentioned in any issue.
The commit has been mentioned in the following pull-request(s):
- https://github.com/Rust-GCC/gccrs/pull/4803
.../typecheck/rust-hir-type-check-expr.cc | 25 +-
gcc/testsuite/rust/compile/for-loop3.rs | 549 ++++++++++++++++++
2 files changed, 554 insertions(+), 20 deletions(-)
create mode 100644 gcc/testsuite/rust/compile/for-loop3.rs
diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.cc b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
index aff96db1f..8ba1df7d2 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-expr.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
@@ -197,11 +197,8 @@ TypeCheckExpr::visit (HIR::BoxExpr &expr)
= 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;
+ TyTy::BaseType *item_type = TypeCheckItem::Resolve (*item);
- 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,
@@ -829,10 +826,7 @@ TypeCheckExpr::visit (HIR::RangeFromToExpr &expr)
// look it up and it _must_ be a struct definition
HIR::Item *item = mappings.lookup_defid (respective_lang_item_id).value ();
- TyTy::BaseType *item_type = nullptr;
- bool ok
- = context->lookup_type (item->get_mappings ().get_hirid (), &item_type);
- rust_assert (ok);
+ TyTy::BaseType *item_type = TypeCheckItem::Resolve (*item);
rust_assert (item_type->get_kind () == TyTy::TypeKind::ADT);
TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (item_type);
@@ -880,10 +874,7 @@ TypeCheckExpr::visit (HIR::RangeFromExpr &expr)
// look it up and it _must_ be a struct definition
HIR::Item *item = mappings.lookup_defid (respective_lang_item_id).value ();
- TyTy::BaseType *item_type = nullptr;
- bool ok
- = context->lookup_type (item->get_mappings ().get_hirid (), &item_type);
- rust_assert (ok);
+ TyTy::BaseType *item_type = TypeCheckItem::Resolve (*item);
rust_assert (item_type->get_kind () == TyTy::TypeKind::ADT);
TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (item_type);
@@ -924,10 +915,7 @@ TypeCheckExpr::visit (HIR::RangeToExpr &expr)
// look it up and it _must_ be a struct definition
HIR::Item *item = mappings.lookup_defid (respective_lang_item_id).value ();
- TyTy::BaseType *item_type = nullptr;
- bool ok
- = context->lookup_type (item->get_mappings ().get_hirid (), &item_type);
- rust_assert (ok);
+ TyTy::BaseType *item_type = TypeCheckItem::Resolve (*item);
rust_assert (item_type->get_kind () == TyTy::TypeKind::ADT);
TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (item_type);
@@ -1065,10 +1053,7 @@ TypeCheckExpr::visit (HIR::RangeFullExpr &expr)
// look it up and it _must_ be a struct definition
HIR::Item *item = mappings.lookup_defid (respective_lang_item_id).value ();
- TyTy::BaseType *item_type = nullptr;
- bool ok
- = context->lookup_type (item->get_mappings ().get_hirid (), &item_type);
- rust_assert (ok);
+ TyTy::BaseType *item_type = TypeCheckItem::Resolve (*item);
rust_assert (item_type->is_unit ());
infered = item_type;
diff --git a/gcc/testsuite/rust/compile/for-loop3.rs b/gcc/testsuite/rust/compile/for-loop3.rs
new file mode 100644
index 000000000..562510566
--- /dev/null
+++ b/gcc/testsuite/rust/compile/for-loop3.rs
@@ -0,0 +1,549 @@
+// { dg-additional-options "-frust-compile-until=compilation" }
+// TODO: this should compile fully
+// { dg-output "loop\r*\nloop\r*\n" }
+#![feature(no_core)]
+#![no_core]
+#![feature(intrinsics, lang_items)]
+#![feature(staged_api)]
+
+pub fn main() {
+ let a = 1usize..3usize;
+
+ // TODO: warning is emitted later
+ for i in a { // { d g-warning "unused name" }
+ unsafe { puts("loop\0" as *const str as *const i8); }
+ }
+}
+
+pub use option::Option::{self, None, Some};
+pub use result::Result::{self, Err, Ok};
+
+extern "C" {
+ fn printf(s: *const i8, ...);
+ fn puts(s: *const i8);
+}
+
+mod option {
+ pub enum Option<T> {
+ #[lang = "None"]
+ None,
+ #[lang = "Some"]
+ Some(T),
+ }
+}
+
+mod result {
+ enum Result<T, E> {
+ Ok(T),
+ Err(E),
+ }
+}
+
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "clone"]
+pub trait Clone: Sized {
+ fn clone(&self) -> Self;
+
+ fn clone_from(&mut self, source: &Self) {
+ *self = source.clone()
+ }
+}
+
+mod impls {
+ use super::Clone;
+
+ macro_rules! impl_clone {
+ ($($t:ty)*) => {
+ $(
+ impl Clone for $t {
+ fn clone(&self) -> Self {
+ *self
+ }
+ }
+ )*
+ }
+ }
+
+ impl_clone! {
+ usize u8 u16 u32 u64 // u128
+ isize i8 i16 i32 i64 // i128
+ f32 f64
+ bool char
+ }
+}
+
+#[lang = "copy"]
+pub trait Copy: Clone {
+ // Empty.
+}
+
+mod copy_impls {
+ use super::Copy;
+
+ macro_rules! impl_copy {
+ ($($t:ty)*) => {
+ $(
+ impl Copy for $t {}
+ )*
+ }
+ }
+
+ impl_copy! {
+ usize u8 u16 u32 u64 // u128
+ isize i8 i16 i32 i64 // i128
+ f32 f64
+ bool char
+ }
+}
+
+mod intrinsics {
+ extern "rust-intrinsic" {
+ pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
+ pub fn wrapping_add<T>(a: T, b: T) -> T;
+ pub fn wrapping_sub<T>(a: T, b: T) -> T;
+ pub fn rotate_left<T>(a: T, b: T) -> T;
+ pub fn rotate_right<T>(a: T, b: T) -> T;
+ pub fn offset<T>(ptr: *const T, count: isize) -> *const T;
+ pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
+ pub fn move_val_init<T>(dst: *mut T, src: T);
+ pub fn uninit<T>() -> T;
+ }
+}
+
+mod ptr {
+ #[lang = "const_ptr"]
+ impl<T> *const T {
+ pub unsafe fn offset(self, count: isize) -> *const T {
+ crate::intrinsics::offset(self, count)
+ }
+ }
+
+ #[lang = "mut_ptr"]
+ impl<T> *mut T {
+ pub unsafe fn offset(self, count: isize) -> *mut T {
+ crate::intrinsics::offset(self, count) as *mut T
+ }
+ }
+
+ pub unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
+ let x = x as *mut u8;
+ let y = y as *mut u8;
+ let len = crate::mem::size_of::<T>() * count;
+ swap_nonoverlapping_bytes(x, y, len)
+ }
+
+ pub unsafe fn swap_nonoverlapping_one<T>(x: *mut T, y: *mut T) {
+ // For types smaller than the block optimization below,
+ // just swap directly to avoid pessimizing codegen.
+ if crate::mem::size_of::<T>() < 32 {
+ let z = read(x);
+ crate::intrinsics::copy_nonoverlapping(y, x, 1);
+ write(y, z);
+ } else {
+ swap_nonoverlapping(x, y, 1);
+ }
+ }
+
+ pub unsafe fn write<T>(dst: *mut T, src: T) {
+ crate::intrinsics::move_val_init(&mut *dst, src)
+ }
+
+ pub unsafe fn read<T>(src: *const T) -> T {
+ let mut tmp: T = crate::mem::uninitialized();
+ crate::intrinsics::copy_nonoverlapping(src, &mut tmp, 1);
+ tmp
+ }
+
+ pub unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, len: usize) {
+ struct Block(u64, u64, u64, u64);
+ struct UnalignedBlock(u64, u64, u64, u64);
+
+ let block_size = crate::mem::size_of::<Block>();
+
+ // Loop through x & y, copying them `Block` at a time
+ // The optimizer should unroll the loop fully for most types
+ // N.B. We can't use a for loop as the `range` impl calls `mem::swap` recursively
+ let mut i: usize = 0;
+ while i + block_size <= len {
+ // Create some uninitialized memory as scratch space
+ // Declaring `t` here avoids aligning the stack when this loop is unused
+ let mut t: Block = crate::mem::uninitialized();
+ let t = &mut t as *mut _ as *mut u8;
+ let x = x.offset(i as isize);
+ let y = y.offset(i as isize);
+
+ // Swap a block of bytes of x & y, using t as a temporary buffer
+ // This should be optimized into efficient SIMD operations where available
+ crate::intrinsics::copy_nonoverlapping(x, t, block_size);
+ crate::intrinsics::copy_nonoverlapping(y, x, block_size);
+ crate::intrinsics::copy_nonoverlapping(t, y, block_size);
+ i += block_size;
+ }
+
+ if i < len {
+ // Swap any remaining bytes
+ let mut t: UnalignedBlock = crate::mem::uninitialized();
+ let rem = len - i;
+
+ let t = &mut t as *mut _ as *mut u8;
+ let x = x.offset(i as isize);
+ let y = y.offset(i as isize);
+
+ crate::intrinsics::copy_nonoverlapping(x, t, rem);
+ crate::intrinsics::copy_nonoverlapping(y, x, rem);
+ crate::intrinsics::copy_nonoverlapping(t, y, rem);
+ }
+ }
+}
+
+mod mem {
+ extern "rust-intrinsic" {
+ #[rustc_const_stable(feature = "const_transmute", since = "1.46.0")]
+ pub fn transmute<T, U>(_: T) -> U;
+ #[rustc_const_stable(feature = "const_size_of", since = "1.40.0")]
+ pub fn size_of<T>() -> usize;
+ }
+
+ pub fn swap<T>(x: &mut T, y: &mut T) {
+ unsafe {
+ crate::ptr::swap_nonoverlapping_one(x, y);
+ }
+ }
+
+ pub fn replace<T>(dest: &mut T, mut src: T) -> T {
+ swap(dest, &mut src);
+ src
+ }
+
+ pub unsafe fn uninitialized<T>() -> T {
+ crate::intrinsics::uninit()
+ }
+}
+
+macro_rules! impl_uint {
+ ($($ty:ident = $lang:literal),*) => {
+ $(
+ impl $ty {
+ pub fn wrapping_add(self, rhs: Self) -> Self {
+ unsafe {
+ crate::intrinsics::wrapping_add(self, rhs)
+ }
+ }
+
+ pub fn wrapping_sub(self, rhs: Self) -> Self {
+ unsafe {
+ crate::intrinsics::wrapping_sub(self, rhs)
+ }
+ }
+
+ pub fn rotate_left(self, n: u32) -> Self {
+ unsafe {
+ crate::intrinsics::rotate_left(self, n as Self)
+ }
+ }
+
+ pub fn rotate_right(self, n: u32) -> Self {
+ unsafe {
+ crate::intrinsics::rotate_right(self, n as Self)
+ }
+ }
+
+ pub fn to_le(self) -> Self {
+ #[cfg(target_endian = "little")]
+ {
+ self
+ }
+ }
+
+ pub const fn from_le_bytes(bytes: [u8; crate::mem::size_of::<Self>()]) -> Self {
+ Self::from_le(Self::from_ne_bytes(bytes))
+ }
+
+ pub const fn from_le(x: Self) -> Self {
+ #[cfg(target_endian = "little")]
+ {
+ x
+ }
+ }
+
+ pub const fn from_ne_bytes(bytes: [u8; crate::mem::size_of::<Self>()]) -> Self {
+ unsafe { crate::mem::transmute(bytes) }
+ }
+
+ pub fn checked_add(self, rhs: Self) -> Option<Self> {
+ let (a, b) = self.overflowing_add(rhs);
+ if b {
+ Option::None
+ } else {
+ Option::Some(a)
+ }
+ }
+
+ pub fn overflowing_add(self, rhs: Self) -> (Self, bool) {
+ let (a, b) = unsafe { crate::intrinsics::add_with_overflow(self as $ty, rhs as $ty) };
+ (a as Self, b)
+ }
+ }
+ )*
+ }
+}
+
+impl_uint!(
+ u8 = "u8",
+ u16 = "u16",
+ u32 = "u32",
+ u64 = "u64",
+ usize = "usize"
+);
+
+#[lang = "add"]
+pub trait Add<RHS = Self> {
+ type Output;
+
+ fn add(self, rhs: RHS) -> Self::Output;
+}
+macro_rules! add_impl {
+ ($($t:ty)*) => ($(
+ impl Add for $t {
+ type Output = $t;
+
+ fn add(self, other: $t) -> $t { self + other }
+ }
+ )*)
+}
+
+add_impl! { usize u8 u16 u32 u64 /*isize i8 i16 i32 i64*/ f32 f64 }
+
+#[lang = "sub"]
+pub trait Sub<RHS = Self> {
+ type Output;
+
+ fn sub(self, rhs: RHS) -> Self::Output;
+}
+macro_rules! sub_impl {
+ ($($t:ty)*) => ($(
+ impl Sub for $t {
+ type Output = $t;
+
+ fn sub(self, other: $t) -> $t { self - other }
+ }
+ )*)
+}
+
+sub_impl! { usize u8 u16 u32 u64 /*isize i8 i16 i32 i64*/ f32 f64 }
+
+#[lang = "Range"]
+pub struct Range<Idx> {
+ pub start: Idx,
+ pub end: Idx,
+}
+
+pub trait TryFrom<T>: Sized {
+ /// The type returned in the event of a conversion error.
+ type Error;
+
+ /// Performs the conversion.
+ fn try_from(value: T) -> Result<Self, Self::Error>;
+}
+
+pub trait From<T>: Sized {
+ fn from(_: T) -> Self;
+}
+
+impl<T> From<T> for T {
+ fn from(t: T) -> T {
+ t
+ }
+}
+
+impl<T, U> TryFrom<U> for T
+where
+ T: From<U>,
+{
+ type Error = !;
+
+ fn try_from(value: U) -> Result<Self, Self::Error> {
+ Ok(T::from(value))
+ }
+}
+
+trait Step {
+ /// Returns the number of steps between two step objects. The count is
+ /// inclusive of `start` and exclusive of `end`.
+ ///
+ /// Returns `None` if it is not possible to calculate `steps_between`
+ /// without overflow.
+ fn steps_between(start: &Self, end: &Self) -> Option<usize>;
+
+ /// Replaces this step with `1`, returning itself
+ fn replace_one(&mut self) -> Self;
+
+ /// Replaces this step with `0`, returning itself
+ fn replace_zero(&mut self) -> Self;
+
+ /// Adds one to this step, returning the result
+ fn add_one(&self) -> Self;
+
+ /// Subtracts one to this step, returning the result
+ fn sub_one(&self) -> Self;
+
+ /// Add an usize, returning None on overflow
+ fn add_usize(&self, n: usize) -> Option<Self>;
+}
+
+// These are still macro-generated because the integer literals resolve to different types.
+macro_rules! step_identical_methods {
+ () => {
+ #[inline]
+ fn replace_one(&mut self) -> Self {
+ crate::mem::replace(self, 1)
+ }
+
+ #[inline]
+ fn replace_zero(&mut self) -> Self {
+ crate::mem::replace(self, 0)
+ }
+
+ #[inline]
+ fn add_one(&self) -> Self {
+ Add::add(*self, 1)
+ }
+
+ #[inline]
+ fn sub_one(&self) -> Self {
+ Sub::sub(*self, 1)
+ }
+ };
+}
+
+macro_rules! step_impl_unsigned {
+ ($($t:ty)*) => ($(
+ impl Step for $t {
+ fn steps_between(start: &$t, end: &$t) -> Option<usize> {
+ if *start < *end {
+ // Note: We assume $t <= usize here
+ Option::Some((*end - *start) as usize)
+ } else {
+ Option::Some(0)
+ }
+ }
+
+ fn add_usize(&self, n: usize) -> Option<Self> {
+ match <$t>::try_from(n) {
+ Result::Ok(n_as_t) => self.checked_add(n_as_t),
+ Result::Err(_) => Option::None,
+ }
+ }
+
+ step_identical_methods!();
+ }
+ )*)
+}
+macro_rules! step_impl_signed {
+ ($( [$t:ty : $unsigned:ty] )*) => ($(
+ impl Step for $t {
+ #[inline]
+ #[allow(trivial_numeric_casts)]
+ fn steps_between(start: &$t, end: &$t) -> Option<usize> {
+ if *start < *end {
+ // Note: We assume $t <= isize here
+ // Use .wrapping_sub and cast to usize to compute the
+ // difference that may not fit inside the range of isize.
+ Option::Some((*end as isize).wrapping_sub(*start as isize) as usize)
+ } else {
+ Option::Some(0)
+ }
+ }
+
+ #[inline]
+ #[allow(unreachable_patterns)]
+ fn add_usize(&self, n: usize) -> Option<Self> {
+ match <$unsigned>::try_from(n) {
+ Result::Ok(n_as_unsigned) => {
+ // Wrapping in unsigned space handles cases like
+ // `-120_i8.add_usize(200) == Option::Some(80_i8)`,
+ // even though 200_usize is out of range for i8.
+ let wrapped = (*self as $unsigned).wrapping_add(n_as_unsigned) as $t;
+ if wrapped >= *self {
+ Option::Some(wrapped)
+ } else {
+ Option::None // Addition overflowed
+ }
+ }
+ Result::Err(_) => Option::None,
+ }
+ }
+
+ step_identical_methods!();
+ }
+ )*)
+}
+
+macro_rules! step_impl_no_between {
+ ($($t:ty)*) => ($(
+ impl Step for $t {
+ #[inline]
+ fn steps_between(_start: &Self, _end: &Self) -> Option<usize> {
+ Option::None
+ }
+
+ #[inline]
+ fn add_usize(&self, n: usize) -> Option<Self> {
+ self.checked_add(n as $t)
+ }
+
+ step_identical_methods!();
+ }
+ )*)
+}
+
+step_impl_unsigned!(usize);
+
+pub trait Iterator {
+ type Item;
+
+ #[lang = "next"]
+ fn next(&mut self) -> Option<Self::Item>;
+}
+
+impl<A: Step> Iterator for Range<A> {
+ type Item = A;
+
+ fn next(&mut self) -> Option<A> {
+ if self.start < self.end {
+ // We check for overflow here, even though it can't actually
+ // happen. Adding this check does however help llvm vectorize loops
+ // for some ranges that don't get vectorized otherwise,
+ // and this won't actually result in an extra check in an optimized build.
+ match self.start.add_usize(1) {
+ Option::Some(mut n) => {
+ crate::mem::swap(&mut n, &mut self.start);
+ Option::Some(n)
+ }
+ Option::None => Option::None,
+ }
+ } else {
+ Option::None
+ }
+ }
+}
+
+pub trait IntoIterator {
+ type Item;
+
+ type IntoIter: Iterator<Item = Self::Item>;
+
+ #[lang = "into_iter"]
+ fn into_iter(self) -> Self::IntoIter;
+}
+
+impl<I: Iterator> IntoIterator for I {
+ type Item = I::Item;
+ type IntoIter = I;
+
+ fn into_iter(self) -> I {
+ self
+ }
+}
base-commit: c869502a07460670a86f5e61c7c28672f1bdfdaa
--
2.55.0
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.