-
Notifications
You must be signed in to change notification settings - Fork 13.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Overflow for trait implemented recursively on references #37748
Comments
Triage: still a bug |
I ran into a similar case which I believe is related: #![recursion_limit="10"]
trait Trait {}
struct Foo<X>(X) where for<'a> &'a X: Trait;
impl<X> Foo<X> where for<'a> &'a X: Trait {
fn new(x: X) -> Foo<X> {
Foo(x)
}
}
struct Bar<Y>(Y);
impl<Y> Trait for &Bar<Y> where for<'a> &'a Y: Trait {} This also gives an overflow, but compiles fine with any of the following changes:
I'm kind of surprised that the type of |
Ran into this too now. Unfortunately my project is a rather big backend service using warp. hard to boil down a reproduction. it started to appear wafter upgrading to rust 1.53 |
I ran into a similar issue trying to define custom container types that implement standard vector space operations when the contained type implements them (that for some odd reason—maybe this reason—the standard arrays do not). The following code that tries to implement all ref/noref combinations of multiplication with use std::ops::*;
struct A<T> {
v : T
}
impl<T> Mul<f64> for A<T> where T : Mul<f64> {
type Output = A<<T as Mul<f64>>::Output>;
fn mul(self, w : f64) -> Self::Output { A{ v : self.v * w} }
}
impl<T> Mul<A<T>> for f64 where f64 : Mul<T> {
type Output = A<<f64 as Mul<T>>::Output>;
fn mul(self, x : A<T>) -> Self::Output { A{ v : self * x.v} }
}
impl<'a, T> Mul<f64> for &'a A<T> where &'a T : Mul<f64> {
type Output = A<<&'a T as Mul<f64>>::Output>;
fn mul(self, w : f64) -> Self::Output { A{ v : &(self.v) * w} }
}
// If you remove this
impl<'b, T> Mul<&'b A<T>> for f64 where f64 : Mul<&'b T> {
type Output = A<<f64 as Mul<&'b T>>::Output>;
fn mul(self, x : &'b A<T>) -> Self::Output { A { v : self * &(x.v) } }
}
fn main() {
let t = A{v : 1.0};
let _b = 3.0*&t; // ... and this, then it compiles.
let _c = &t*3.0;
} The code compiles perfectly fine if the last (fourth) If I do complete type annotation for the selection of use std::ops::*;
struct A<T> {
v : T
}
impl<T> Mul<f64> for A<T> where T : Mul<f64> {
type Output = A<<T as Mul<f64>>::Output>;
fn mul(self, w : f64) -> Self::Output { A{ v : <T as Mul<f64>>::mul(self.v, w) } }
}
impl<T> Mul<A<T>> for f64 where f64 : Mul<T> {
type Output = A<<f64 as Mul<T>>::Output>;
fn mul(self, x : A<T>) -> Self::Output { A{ v : <f64 as Mul<T>>::mul(self, x.v) } }
}
impl<'a, T> Mul<f64> for &'a A<T> where &'a T : Mul<f64> {
type Output = A<<&'a T as Mul<f64>>::Output>;
fn mul(self, w : f64) -> Self::Output { A{ v : <&'a T as Mul<f64>>::mul(&(self.v), w)} }
}
impl<'b, T> Mul<&'b A<T>> for f64 where f64 : Mul<&'b T> {
type Output = A<<f64 as Mul<&'b T>>::Output>;
fn mul(self, x : &'b A<T>) -> Self::Output { A { v : <f64 as Mul<&'b T>>::mul(self, &(x.v)) } }
}
fn main() {
let t = A{ v : A{v : 1.0 } };
let _a = <f64 as Mul<&A<A<f64>>>>::mul(3.0, &t); // This explicit typing works
//let _b = 3.0*(&t); // If you uncomment this, the compiler overflows.
let _c = &t*3.0;
} Such is, of course, not practical in daily use. Removing the type annotations in the “daily use ” lines in
Simply based on the error message, without knowing much about the compiler internals, it seems as if it is looking for |
As I posted on stack oveflow, I came up with a few workarounds that do not require major changes to architecture, just adding some level counting to nested structures at the type system level. Maybe they will further help pinpoint the issue, so I'm posting them here as well. Both workrounds are based on a “nesting level counting multiplication trait”. For brevity I only include Version 2This version is very non-invasive with regard to code that should work without the compiler bug. It only implements a “shadow” variant of the original (multiplication) function that counts nesting levels at the type system level. use std::ops::*;
// Our nested data structure
struct A<T> {
v : T
}
// Nesting level counting structs and traits
struct L0 {}
struct Succ<L> { _prev : L }
trait Nested { type Level; }
// Primitives like f64 are at nesting level zero
impl Nested for f64 { type Level = L0; }
// A<T> always increases nesting level
impl<T> Nested for A<T> where T : Nested { type Level=Succ<T::Level>; }
// Nested multiplication trait. Default implementation is standard multiplication.
trait NestedMul<T, L> : Mul<T> + Sized {
fn nested_mul(self, a : T) -> Self::Output { self * a}
}
// Auto-implement it at level 0
impl<'b,S,T> NestedMul<&'b T,L0> for S where T : Nested<Level=L0>, S : Mul<&'b T> + Sized {}
// Special implementation of NestedMul for A, bypassing Mul
impl<'b, T : Nested> NestedMul<&'b A<T>, Succ<T::Level>> for f64 where f64 : NestedMul<&'b T, T::Level> {
fn nested_mul(self, a : &'b A<T>) -> Self::Output { A { v : self.nested_mul(&(a.v)) } }
}
// The “interface” : when A is multiplied in plain code, we pass to level-counting nested
// multiplication to avoid compiler overflow.
//
// Version 1: this would be for all nesting structures. Not allowed by Rust as it involves
// no local structs. Similarly f64 has to be hard-coded here / this whole impl macro-generated
// when generalising to other types.
//
//impl<'b, T : Nested> Mul<&'b T> for f64 where f64 : NestedMul<T, T::Level> {
// type Output=<f64 as Mul<&'b T>>::Output;
// fn mul(self, a : &'b A<T>) -> Self::Output { self.nested_mul(a) }
//}
//
// Version 2: specifically for A<T>. A minor optimisation as bypasses one level of
// nested_mul:
impl<'b, T : Nested> Mul<&'b A<T>> for f64 where f64 : NestedMul<&'b T, T::Level> {
type Output=A<<f64 as Mul<&'b T>>::Output>;
fn mul(self, a : &'b A<T>) -> Self::Output { A { v : self.nested_mul(&(a.v)) } }
}
fn main() {
let t : A<A<f64>> = A{ v : A{v : 1.0 } };
let _b = 3.0*&t;
} Version 1The first version used use std::ops::*;
use std::marker::PhantomData;
// Define type-level integers
struct L0 {}
struct Succ<L> { _prev : L }
type L1 = Succ<L0>;
type L2 = Succ<L1>;
// Our nested data structure that includes the nesting level.
struct A<T,L> {
v : T,
lev : PhantomData<L>
}
// Nested multiplication trait. Default implementation is standard multiplication.
trait NestedMul<T, L> : Mul<T> + Sized {
fn nested_mul(self, a : T) -> Self::Output { self * a }
}
// Implement it for f64 using defaults.
impl<'b,T> NestedMul<T, L0> for f64 where f64 : Mul<T> { }
// Special implementation of NestedMul for A, bypassing Mul
impl<'b,L,T> NestedMul<&'b A<T,Succ<L>>,Succ<L>> for f64 where f64 : NestedMul<&'b T, L> {
fn nested_mul(self, a : &'b A<T,Succ<L>>) -> Self::Output {
A { v : self.nested_mul(&(a.v)), lev : PhantomData }
}
}
// The “interface” : when A is multiplied in plain code, we pass to level-counting nested
// multiplication to avoid compiler overflow.
impl<'b, T, L> Mul<&'b A<T, Succ<L>>> for f64 where f64 : NestedMul<&'b T, L> {
type Output=A<<f64 as Mul<&'b T>>::Output, Succ<L>>;
fn mul(self, a : &'b A<T,Succ<L>>) -> Self::Output {
A { v : self.nested_mul(&(a.v)), lev : PhantomData }
}
}
fn main() {
let t : A<A<f64,L1>,L2> = A{ v : A{v : 1.0, lev : PhantomData }, lev : PhantomData };
let _b = 3.0*&t;
} |
In the original snippet, writing |
I was attempting to implement a wrapper for This is the most minimal reproduction I could find: fn write_wrapped<W>(mut w: W, s: &str, recurse: bool) -> std::fmt::Result
where
W: std::fmt::Write,
{
if recurse {
write_wrapped(&mut w, s, false)
} else {
w.write_str(s)
}
}
fn main() {
let mut s = String::new();
write_wrapped(&mut s, "test", true).unwrap();
println!("{:?}", s);
} It is interesting, because:
However, running the code with the
|
Any progress about this issue? I ran into the same issue also when implementing traits for a newtype: struct Wrap<T>(T);
trait Foo {}
impl Foo for Wrap<usize> {}
// Ok when this impl is commented-out.
impl<T> Foo for Wrap<Vec<T>> where Wrap<T>: Foo {}
fn foo<T>(_: T) where Wrap<T>: Foo {}
fn main() {
foo(0); // won’t compile
}
This does solve the problem, but is apparently less elegant as mentioned. An almost identical version in Haskell works very well: {-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
newtype Wrap t = Wrap t
class Foo a where
method :: a -> Int
method _ = 123
instance Foo (Wrap Bool)
instance (Foo (Wrap t)) => Foo (Wrap [t])
foo :: (Foo (Wrap t)) => t -> ()
foo _ = ()
foo2 :: (Foo (Wrap t)) => t -> Int
foo2 t = method (Wrap [t])
main :: IO ()
main = do
print $ foo True
print $ foo2 True
-- Output:
-- ()
-- 123 |
Consider this snippet [playpen]:
There is no issue when trait implementation for
&'a Vec<T>
is commented-out, even thoughVec
isn't actually used anywhere when callingfoo
method.The text was updated successfully, but these errors were encountered: