Skip to content

nalgebra depends on incomplete guidance from env during method selection #161

Closed
rust-lang/rust
#136928
@lcnr

Description

@lcnr

the following snippet is ambig with the new solver and compiles with old

use num_traits::Zero;
use nalgebra::{DimName, Scalar, U1, Matrix};
use nalgebra::DefaultAllocator;
use nalgebra::allocator::Allocator;

type Alias<T, D> = Matrix<T, D, U1, <DefaultAllocator as Allocator<D>>::Buffer<T>>;
fn foo<T: Scalar + Zero, D: DimName>() -> Matrix<T, D, U1, <DefaultAllocator as Allocator<D>>::Buffer<T>>
where
    DefaultAllocator: Allocator<D>,
{
    Matrix::<_, _, U1, <DefaultAllocator as Allocator<_>>::Buffer<T>>::from_element(T::zero()) // works
    // Alias::<T, _>::from_element(T::zero()) // error
}

minimizing this to not depend on nalgebra results in the following

trait Constrain<T> {
    type Assoc;
}
impl<T> Constrain<T> for () {
    type Assoc = ();
}
struct Foo<T, U = <() as Constrain<T>>::Assoc>(T, U);

impl<T: Copy> Foo<T> {
    fn foo() {}
}
struct B;
impl Foo<B> {
    fn foo() {}
}

type Alias<T> = Foo<T>;
fn via_guidance<T: Copy>()
where
    (): Constrain<T>,
{
    Alias::foo();
}

this errors with the following with the new solver

error[E0034]: multiple applicable items in scope
  --> src/lib.rs:26:10
   |
26 |     Foo::foo();
   |          ^^^ multiple `foo` found
   |
note: candidate #1 is defined in an impl for the type `Foo<B>`
  --> src/lib.rs:17:5
   |
17 |     fn foo() {}
   |     ^^^^^^^^
note: candidate #2 is defined in an impl for the type `Foo<T>`
  --> src/lib.rs:13:5
   |
13 |     fn foo() {}
   |     ^^^^^^^^

This compiles in the old solver as Alias expands to Foo<?x, <() as Constrain<?x>>::Assoc>. Normalizing <() as Constrain<?x>>::Assoc constrains ?x to T, at which point method selection has a unique candidate.

In the new solver we never try to normalize <() as Constrain<?x>>::Assoc, causing ?x to remain ambiguous at this point, resulting in an ambiguity error during method selection.

Subtle: using Foo instead of Alias errors on stable as well, as it gets lowered to Foo<?x, ?y> instead without using the default argument. Foo::<_> also gets lowered to Foo<?x, ?y>. We only use default arguments if infer_args is set to false during ast lowering in the lowerer. I don't understand why that is the case tbh 🤷

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Status

done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions