Drop-check regression: overflow while adding drop-check rules for struct

After updating rustc to the latest 1.6.0 nightly my crazy over-generified graphics library fails to build yet again.

This time it is complaining about an overflow while adding drop-check rules.

This is the most important few lines of code (the complete thing is here and here).

On the playground

pub struct DescriptorSet<'a> {
    pub slots: Vec<AttachInfo<'a, Resources>>
}

pub struct Resources;

impl<'a> ResourcesTrait<'a> for Resources {
    type DescriptorSet = descriptor::DescriptorSet<'a>;
    // more definitions here
}

pub enum AttachInfo<'a, R: ResourcesTrait<'a>> {
    Buffer(BufferViewAttachInfo<'a, R>),
    NextDescriptorSet(Arc<'a, R::DescriptorSet>)
}

What I have found out

  • I have tried increasing the recursion limit to 1000 with #![recursion_limit="1000"]
  • The error is because the DescriptorSet struct is recursive. (because R::DescriptorSet == DescriptorSet<'a> there can be an infinite amount of 'next descriptor sets')
  • I think the issue was introduced by implementing non-parametric dropck
  • I think my issue is similar to this test case
  • This reduced example works, so something more must be going on. I forgot to actually use the types, if I add let x = DescriptorSet {slots: Vec::new()}; it then fails

Questions

  • Is DescriptorSet a non-regular type? If so, what does that mean.
  • It seems like this is caused by my type having 'polymorphic recursion'. Is there a way I can make it more concrete?
  • In short is there a 'fix' for the error or is this invalid code that is now correctly rejected.

Full Error

opal_driver_gl/src/device.rs:125:9: 125:65 error: overflow while adding drop-check rules for core::result::Result<alloc::arc::Arc<descriptor::DescriptorSet<'_>>, opal::descriptor::DescriptorSetCreationError> [E0320]
opal_driver_gl/src/device.rs:125         DescriptorSet::new(slots).map(|result| Arc::new(result))
                                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
opal_driver_gl/src/device.rs:125:9: 125:65 note: overflowed on struct core::nonzero::NonZero field #0 type: *const alloc::arc::ArcInner<buffer::Buffer<'_>>
opal_driver_gl/src/device.rs:125         DescriptorSet::new(slots).map(|result| Arc::new(result))
                                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
opal_driver_gl/src/device.rs:125:9: 125:34 error: overflow while adding drop-check rules for core::result::Result<descriptor::DescriptorSet<'_>, opal::descriptor::DescriptorSetCreationError> [E0320]
opal_driver_gl/src/device.rs:125         DescriptorSet::new(slots).map(|result| Arc::new(result))
                                         ^~~~~~~~~~~~~~~~~~~~~~~~~
opal_driver_gl/src/device.rs:125:9: 125:34 note: overflowed on struct core::nonzero::NonZero field #0 type: *const opal::descriptor::AttachInfo<'_, Resources>
opal_driver_gl/src/device.rs:125         DescriptorSet::new(slots).map(|result| Arc::new(result))
                                         ^~~~~~~~~~~~~~~~~~~~~~~~~
opal_driver_gl/src/device.rs:125:48: 125:64 error: overflow while adding drop-check rules for alloc::arc::Arc<descriptor::DescriptorSet<'_>> [E0320]
opal_driver_gl/src/device.rs:125         DescriptorSet::new(slots).map(|result| Arc::new(result))
                                                                                ^~~~~~~~~~~~~~~~
opal_driver_gl/src/device.rs:125:48: 125:64 note: overflowed on struct alloc::arc::ArcInner field `strong` type: core::sync::atomic::AtomicUsize
opal_driver_gl/src/device.rs:125         DescriptorSet::new(slots).map(|result| Arc::new(result))
                                                                                ^~~~~~~~~~~~~~~~
opal_driver_gl/src/descriptor.rs:31:9: 33:11 error: overflow while adding drop-check rules for core::result::Result<descriptor::DescriptorSet<'_>, opal::descriptor::DescriptorSetCreationError> [E0320]
opal_driver_gl/src/descriptor.rs:31         Ok(DescriptorSet {
opal_driver_gl/src/descriptor.rs:32             slots: slots
opal_driver_gl/src/descriptor.rs:33         })
opal_driver_gl/src/descriptor.rs:31:9: 33:11 note: overflowed on struct core::nonzero::NonZero field #0 type: *const opal::descriptor::AttachInfo<'_, Resources>
opal_driver_gl/src/descriptor.rs:31         Ok(DescriptorSet {
opal_driver_gl/src/descriptor.rs:32             slots: slots
opal_driver_gl/src/descriptor.rs:33         })
opal_driver_gl/src/descriptor.rs:31:12: 33:10 error: overflow while adding drop-check rules for descriptor::DescriptorSet<'_> [E0320]
opal_driver_gl/src/descriptor.rs:31         Ok(DescriptorSet {
opal_driver_gl/src/descriptor.rs:32             slots: slots
opal_driver_gl/src/descriptor.rs:33         })
opal_driver_gl/src/descriptor.rs:31:12: 33:10 note: overflowed on enum opal::descriptor::AttachInfo variant Buffer field #0 type: opal::descriptor::BufferViewAttachInfo<'_, Resources>
opal_driver_gl/src/descriptor.rs:31         Ok(DescriptorSet {
opal_driver_gl/src/descriptor.rs:32             slots: slots
opal_driver_gl/src/descriptor.rs:33         })

Thanks if you could assist me.

Reduces to:

use std::sync::Arc;

pub struct DescriptorSet<'a> {
    pub slots: Vec<AttachInfo<'a, Resources>>
}

pub trait ResourcesTrait<'r>: Sized {
    type DescriptorSet: 'r;
}

pub struct Resources;

impl<'a> ResourcesTrait<'a> for Resources {
    type DescriptorSet = DescriptorSet<'a>;
}

pub enum AttachInfo<'a, R: ResourcesTrait<'a>> {
    NextDescriptorSet(Arc<R::DescriptorSet>)
}

fn main() {
    let _x = DescriptorSet {slots: Vec::new()};
}

Looks like a bug to me, although I haven't looked too deeply.

In terms of avoiding the issue... associated types have a tendency to trigger obscure issues.

DescriptorSet isn't an irregular type; that refers to types with recursion that doesn't form a cycle.

Thank you for your analysis. I realised I forgot to actually create a DescriptorSet in the playground example so that is why it seemed to 'work'.

I agree that associated types are pretty buggy, however I can't find a way to eliminate them here.

RawBuffer is the only part that needs the to be generic over Resources. It is defined like this:

pub struct RawBuffer<'r, R: Resources<'r>> {
    pub data: Arc<R::Buffer>,
}

It seems like I could simplify it to this:

pub struct RawBuffer<'r, B: Buffer<'r>> {
    pub data: Arc<B>,
}

However at the moment R::Buffer is not just a simple trait, but actually Buffer<'r, Self> + 'r and thus because of the Self type I need to make it generic over the entire Resources trait.

Filed Regression: overflow while adding drop-check rules · Issue #29844 · rust-lang/rust · GitHub

Thank you for filing the issue. It is good to hear that it's not just my own programming error.
If you like, I could investigate the bug further myself. I've messed around with procedural macros and lint passes a bit and it would be great to understand dropck as well.