Skip to content

New problem with const_trait_impl #88424

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

Closed
leonardo-m opened this issue Aug 28, 2021 · 10 comments · Fixed by #88558
Closed

New problem with const_trait_impl #88424

leonardo-m opened this issue Aug 28, 2021 · 10 comments · Fixed by #88558
Labels
C-bug Category: This is a bug.

Comments

@leonardo-m
Copy link

This code:

#![feature(const_trait_impl, const_fn_trait_bound, const_panic)]

pub trait Foo {
    fn bar(self) -> usize;
    const MAX: Self;
}
impl const Foo for usize {
    fn bar(self) -> usize { self }
    const MAX: Self = Self::MAX;
}

const fn spam<T: Foo>(n: usize) {
    assert!(n <= T::MAX.bar());
}

fn main() {}

Using:

rustc 1.56.0-nightly (ac50a5335 2021-08-27)
binary: rustc
commit-hash: ac50a53359328a5d7f2f558833e63d59d372e4f7
commit-date: 2021-08-27
host: x86_64-pc-windows-gnu
release: 1.56.0-nightly
LLVM version: 13.0.0

Gives:

error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
  --> ...\test.rs:13:18
   |
13 |     assert!(n <= T::MAX.bar());
   |                  ^^^^^^^^^^^^

I think this used to compile up to the precedent Nightly.

@leonardo-m leonardo-m added the C-bug Category: This is a bug. label Aug 28, 2021
@fee1-dead
Copy link
Member

You need ~const on every bound when you require a const impl in a const context.

#![feature(const_trait_impl, const_fn_trait_bound, const_panic)]

pub trait Foo {
    fn bar(self) -> usize;
    const MAX: Self;
}
impl const Foo for usize {
    fn bar(self) -> usize { self }
    const MAX: Self = Self::MAX;
}

const fn spam<T: ~const Foo>(n: usize) {
    assert!(n <= T::MAX.bar());
}

fn main() {}

@leonardo-m
Copy link
Author

I see. Thank you. So do I convert this into a diagnostic enhancement request asking for a helpful error message (or help message), or do I close this issue down and open another one for that?

@leonardo-m
Copy link
Author

leonardo-m commented Aug 28, 2021

Here I have a problem:

#![feature(const_fn_trait_bound, const_trait_impl)]
#![allow(dead_code)]

const fn foo<T: ~const Default + Copy>() -> T {
    T::default()
}

fn main() {
    const X1: u64 = foo(); // OK
    const X2: (u8, u64) = foo(); // Err
}

That gives me:

error[E0277]: the trait bound `(u8, u64): Default` is not satisfied
  --> ...\test1.rs:10:27
   |
10 |     const X2: (u8, u64) = foo(); // Err
   |                           ^^^ the trait `Default` is not implemented for `(u8, u64)`
   |
note: required by a bound in `foo`
  --> ...\test1.rs:4:17
   |
4  | const fn foo<T: ~const Default + Copy>() -> T {
   |                 ^^^^^^^^^^^^^^ required by this bound in `foo`
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
   |
8  | fn main() where (u8, u64): Default {
   |           ++++++++++++++++++++++++

A little more complex situation:

#![feature(
    const_evaluatable_checked,
    const_fn_trait_bound,
    const_generics,
    const_panic,
    const_trait_impl,
)]

#![allow(incomplete_features)]

pub trait ToFromUsize {
    fn to_usize(self) -> usize;
    fn from_usize(x: usize) -> Self;
    const MAX: Self;
}
impl const ToFromUsize for usize {
    fn to_usize(self) -> usize { self }
    fn from_usize(x: usize) -> Self { x }
    const MAX: Self = Self::MAX;
}

pub const fn assert_nonzero(n: usize) -> usize {
    assert!(n > 0);
    n
}

pub const fn is_contained(n: usize, m: usize) -> usize {
    assert!(n <= m);
    n
}

pub const fn is_representable<Ti: ~const ToFromUsize>(n: usize) -> usize {
    assert!(n <= Ti::MAX.to_usize());
    n
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
#[repr(transparent)]
pub struct Foo<Ti: ToFromUsize + Copy, const N: usize>(Ti)
where [(); assert_nonzero(N)]:,
      [(); is_representable::<Ti>(N - 1)]:;

impl<Ti: ToFromUsize + Copy, const N: usize> Foo<Ti, N>
where [(); assert_nonzero(N)]:,
      [(); is_representable::<Ti>(N - 1)]: {
    pub const fn new(i: Ti) -> Option<Self> {
        if i.to_usize() < N {
            Some(Self(i))
        } else {
            None
        }
    }
}

fn main() {}

That gives:

error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
  --> ...\test2.rs:47:12
   |
47 |         if i.to_usize() < N {
   |            ^^^^^^^^^^^^

@leonardo-m
Copy link
Author

@fee1-dead
Copy link
Member

I think you can just move the bound to the const fn's where clause.

pub const fn new(i: Ti) -> Option<Self> where Ti: ~const ToFromUsize

@leonardo-m
Copy link
Author

I think you can just move the bound to the const fn's where clause.

Unfortunately it gives me a "error: ~const is not allowed here". Followed by "note: only allowed on bounds on traits' associated types and functions, const fns, const impls and its associated functions"

@fee1-dead
Copy link
Member

Hmmm, looks like I will have to allow const fns in inherent impls having ~const bounds in a new PR.

@leonardo-m
Copy link
Author

The latest Nightly solves most of my problems, but I have two further problems. (Tell me if you prefer me to move them to new issues).
Here I don't how to define the const values at the bottom:

#![feature(
    const_fn_trait_bound,
    const_panic,
    const_trait_impl,
    generic_const_exprs,
)]

#![allow(incomplete_features)]

pub trait ToFromUsize {
    fn to_usize(self) -> usize;
    fn from_usize(x: usize) -> Self;
    const MAX: Self;
}
impl const ToFromUsize for usize {
    fn to_usize(self) -> usize { self }
    fn from_usize(x: usize) -> Self { x }
    const MAX: Self = Self::MAX;
}

pub const fn assert_nonzero(n: usize) -> usize {
    assert!(n > 0);
    n
}

pub const fn is_contained(n: usize, m: usize) -> usize {
    assert!(n <= m);
    n
}

pub const fn is_representable<Ti: ~const ToFromUsize>(n: usize) -> usize {
    assert!(n <= Ti::MAX.to_usize());
    n
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
#[repr(transparent)]
pub struct Foo<Ti: ToFromUsize + Copy, const N: usize>(Ti)
where [(); assert_nonzero(N)]:,
      [(); is_representable::<Ti>(N - 1)]:;

impl<Ti: ToFromUsize + Copy, const N: usize> Foo<Ti, N>
where [(); assert_nonzero(N)]:,
      [(); is_representable::<Ti>(N - 1)]: {

    pub const unsafe fn new_unchecked(i: Ti) -> Self where Ti: ~const ToFromUsize {
        Self(i)
    }

    pub const FIRST: Self = unsafe { Self::new_unchecked(Ti::from_usize(0)) };
    pub const LAST: Self = unsafe { Self::new_unchecked(Ti::from_usize(N - 1)) };
}

fn main() {}

It gives me:

error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
  --> ...\test.rs:50:58
   |
50 |     pub const FIRST: Self = unsafe { Self::new_unchecked(Ti::from_usize(0)) };
   |                                                          ^^^^^^^^^^^^^^^^^

error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
  --> ...\test.rs:51:57
   |
51 |     pub const LAST: Self = unsafe { Self::new_unchecked(Ti::from_usize(N - 1)) };
   |                                                         ^^^^^^^^^^^^^^^^^^^^^

The other problem is an enhancement request. If I remove a "const" from the signature of new_unchecked the compiler doesn't give me much help to see the problem:

#![feature(
    const_fn_trait_bound,
    const_panic,
    const_trait_impl,
    generic_const_exprs,
)]

#![allow(incomplete_features)]

pub trait ToFromUsize {
    fn to_usize(self) -> usize;
    fn from_usize(x: usize) -> Self;
    const MAX: Self;
}
impl const ToFromUsize for usize {
    fn to_usize(self) -> usize { self }
    fn from_usize(x: usize) -> Self { x }
    const MAX: Self = Self::MAX;
}

pub const fn assert_nonzero(n: usize) -> usize {
    assert!(n > 0);
    n
}

pub const fn is_contained(n: usize, m: usize) -> usize {
    assert!(n <= m);
    n
}

pub const fn is_representable<Ti: ~const ToFromUsize>(n: usize) -> usize {
    assert!(n <= Ti::MAX.to_usize());
    n
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
#[repr(transparent)]
pub struct Foo<Ti: ToFromUsize + Copy, const N: usize>(Ti)
where [(); assert_nonzero(N)]:,
      [(); is_representable::<Ti>(N - 1)]:;

impl<Ti: ToFromUsize + Copy, const N: usize> Foo<Ti, N>
where [(); assert_nonzero(N)]:,
      [(); is_representable::<Ti>(N - 1)]: {

    pub unsafe fn new_unchecked(i: Ti) -> Self where Ti: ~const ToFromUsize {
        Self(i)
    }
}

fn main() {}

It gives:

error: `~const` is not allowed here
  --> ...\test2.rs:46:58
   |
46 |     pub unsafe fn new_unchecked(i: Ti) -> Self where Ti: ~const ToFromUsize {
   |                                                          ^^^^^^^^^^^^^^^^^^
   |
   = note: only allowed on bounds on traits' associated types and functions, const fns, const impls and its associated functions

@fee1-dead
Copy link
Member

  1. To accept that would require more design work.
  2. Make a new issue for it. (should have a more helpful message)

@leonardo-m
Copy link
Author

Thank you :)
Opened #89007 for the diagnostic issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants