Skip to content
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

Rust beta: "cannot infer type" when compiling bottom crate #96074

Closed
kamulos opened this issue Apr 15, 2022 · 24 comments
Closed

Rust beta: "cannot infer type" when compiling bottom crate #96074

kamulos opened this issue Apr 15, 2022 · 24 comments
Labels
C-bug Category: This is a bug. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@kamulos
Copy link

kamulos commented Apr 15, 2022

Not sure who is correct here, or if this is expected: I just compiled the bottom crate version 0.6.8. This worked with the stable compiler, but failed with the current beta. The error message is the following:

error[E0283]: type annotations needed
   --> src/canvas/dialogs/dd_dialog.rs:385:22
    |
380 | /                     if app_state.dd_err.is_some() {
381 | |                         vec![Constraint::Percentage(100)]
382 | |                     } else {
383 | |                         vec![Constraint::Min(3), Constraint::Length(btn_height)]
384 | |                     }
385 | |                     .as_ref(),
    | |______________________^^^^^^_- this method call resolves to `&T`
    |                        |
    |                        cannot infer type for type parameter `T` declared on the trait `AsRef`
    |
    = note: multiple `impl`s satisfying `Vec<Constraint>: AsRef<_>` found in the `alloc` crate:
            - impl<T, A> AsRef<Vec<T, A>> for Vec<T, A>
              where A: Allocator;
            - impl<T, A> AsRef<[T]> for Vec<T, A>
              where A: Allocator;

error[E0283]: type annotations needed
   --> src/canvas.rs:630:59
    |
630 |                         .constraints(self.row_constraints.as_ref())
    |                                      ---------------------^^^^^^--
    |                                      |                    |
    |                                      |                    cannot infer type for type parameter `T` declared on the trait `AsRef`
    |                                      this method call resolves to `&T`
    |
    = note: multiple `impl`s satisfying `Vec<Constraint>: AsRef<_>` found in the `alloc` crate:
            - impl<T, A> AsRef<Vec<T, A>> for Vec<T, A>
              where A: Allocator;
            - impl<T, A> AsRef<[T]> for Vec<T, A>
              where A: Allocator;

For more information about this error, try `rustc --explain E0283`.
error: could not compile `bottom` due to 2 previous errors

Meta

rustc --version --verbose:

rustc 1.61.0-beta.2 (7c13df853 2022-04-09)
binary: rustc
commit-hash: 7c13df853721b60a03e7c0bb084d2eb1e27a9caa
commit-date: 2022-04-09
host: x86_64-unknown-linux-gnu
release: 1.61.0-beta.2
LLVM version: 14.0.0
@kamulos kamulos added the C-bug Category: This is a bug. label Apr 15, 2022
@Mark-Simulacrum Mark-Simulacrum added this to the 1.61.0 milestone Apr 15, 2022
@Mark-Simulacrum Mark-Simulacrum added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. regression-from-stable-to-beta Performance or correctness regression from stable to beta. labels Apr 15, 2022
@rustbot rustbot added the I-prioritize Issue: Indicates that prioritization has been requested for this issue. label Apr 15, 2022
@ehuss
Copy link
Contributor

ehuss commented Apr 15, 2022

This is almost certainly caused by #95098. cc @shepmaster

A smaller repro:

#[derive(Clone)]
struct Constraint;

fn constraints<C>(constraints: C)
where C: Into<Vec<Constraint>>
{
    let _: Vec<Constraint> = constraints.into();
}

fn main() {
    constraints(vec![Constraint].as_ref());
}

@dtolnay
Copy link
Member

dtolnay commented Apr 16, 2022

This bisects to 93313d1, so indeed #95098.

@apiraino
Copy link
Contributor

Assigning priority as discussed in the Zulip thread of the Prioritization Working Group.

@rustbot label -I-prioritize +P-high

@rustbot rustbot added P-high High priority and removed I-prioritize Issue: Indicates that prioritization has been requested for this issue. labels Apr 20, 2022
@pnkfelix
Copy link
Member

based on the injection point being #95098, isn't this a T-libs-api issue, not a T-compiler issue?

Switching tags under that assumption.

@rustbot label: -T-compiler +T-libs-api

@rustbot rustbot added T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. and removed T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Apr 21, 2022
@dtolnay
Copy link
Member

dtolnay commented Apr 21, 2022

For T-libs-api: I don't expect that we would choose to revert this. @rust-lang/libs-api

However it's not clear to me what type inference limitation makes the type unable to be inferred. Using the minimal repro from @ehuss, we have:

constraints(vec.as_ref());

Rustc can see that .as_ref() refers to core::convert::AsRef<T>::as_ref because there is no as_ref inherent method on Vec or any of the types it transitively derefs to, and AsRef is the only trait in scope with an as_ref method. The signature of AsRef::as_ref is fn as_ref(&self) -> &T and therefore the type of vec.as_ref() is &T for some T, as long as Vec<Constraint>: AsRef<T>.

The possibilities for Vec<Constraint>: AsRef<T> are T = [Constraint] and T = Vec<Constraint>.

Then it sees calling constraints::<&T> requires &T: Into<Vec<Constraint>> due to the where-clause. Previously this was satisfiable only by T = [Constraint] whereas after #95098 it's also satisfiable by T = [Constraint; N].

Is it computationally hard to deduce from this that T = [Constraint]? As someone who doesn't know the type inference implementation in detail, my impression is this seems in line with inference that rustc is able to do successfully in other situations.

@yaahc
Copy link
Member

yaahc commented Apr 21, 2022

However it's not clear to me what type inference limitation makes the type unable to be inferred. Using the minimal repro from @ehuss, we have:

I recall some special cases in candidate assembly for allowing inference to continue when there is only one unambiguous impl, if I had to guess I'd say this is the same issue as #90904.

I'm trying to find the special case, I think this might be it but I'm not sure:

// If there is more than one candidate, first winnow them down
// by considering extra conditions (nested obligations and so
// forth). We don't winnow if there is exactly one
// candidate. This is a relatively minor distinction but it
// can lead to better inference and error-reporting. An
// example would be if there was an impl:
//
// impl<T:Clone> Vec<T> { fn push_clone(...) { ... } }
//
// and we were to see some code `foo.push_clone()` where `boo`
// is a `Vec<Bar>` and `Bar` does not implement `Clone`. If
// we were to winnow, we'd wind up with zero candidates.
// Instead, we select the right impl now but report "`Bar` does
// not implement `Clone`".
if candidates.len() == 1 {
return self.filter_reservation_impls(candidates.pop().unwrap(), stack.obligation);
}

Sadly I don't understand this well enough either to really understand whats going on. I think there's some rule in the compiler (maybe the bit above) that lets the ambiguous type parameter for AsRef to be inferred off of the type of the single candidate for From when there's only one, but as soon as you have two, even if only one of them is actually applicable, it ends up erroring out earlier and never uses the From impls to attempt to select an AsRef impl.

@Mark-Simulacrum
Copy link
Member

This caused a number of regressions in 1.61 beta (per crater run) -- the pattern is pretty much the same in most of them (even so far as constraints being the method, but it looks like copy/paste code, maybe, not dependencies). I think there's enough that we should consider backing this change out for the time being or indefinitely. It's not clear to me that we have a very strong justification for these impls beyond just uniformity, and the breakage isn't trivial (not 100s of crates, I suppose, but not <10 either).

crates.io:

github:

@shepmaster
Copy link
Member

(even so far as constraints being the method, but it looks like copy/paste code, maybe, not dependencies)

Perhaps copy-paste from the tui crate's docs:

let chunks = Layout::default()
    .direction(Direction::Vertical)
    .margin(1)
    .constraints(
        [
            Constraint::Percentage(10),
            Constraint::Percentage(80),
            Constraint::Percentage(10)
        ].as_ref()
    )
    .split(f.size());

constraints is

pub fn constraints<C>(mut self, constraints: C) -> Layout
where
    C: Into<Vec<Constraint>>,

@shepmaster
Copy link
Member

shepmaster commented Apr 26, 2022

justification for these impls beyond just uniformity

My original justification was not uniformity, but to allow passing byte strings to impl Into<Vec<u8>> arguments which is somewhat common with FFI code.

Amusingly, the TUI code that fails is improved when the code is changed to avoid the new error (IMO):

// doesn't compile
.constraints(vec![Constraint::Percentage(10)].as_ref())
// compiles
.constraints(vec![Constraint::Percentage(10)].as_slice())
.constraints(vec![Constraint::Percentage(10)])
.constraints([Constraint::Percentage(10)].as_ref())
.constraints(&[Constraint::Percentage(10)])
.constraints([Constraint::Percentage(10)])

@joshtriplett joshtriplett added I-libs-api-nominated Nominated for discussion during a libs-api team meeting. I-compiler-nominated Nominated for discussion during a compiler team meeting. labels Apr 26, 2022
@joshtriplett
Copy link
Member

If we think we can improve inference in the compiler to avoid this, then this does seem worth reverting on beta.

cc @rust-lang/wg-traits @rust-lang/compiler; does this seem fixable through better inference?

@estebank
Copy link
Contributor

If we think we can improve inference in the compiler to avoid this

The only problem I see is that changing inference to get these to work might be doable, but not necessarily in the right way, which scares me because it would necessarily mean having a more lenient version of evaluate_candidate or candidate_should_be_dropped_in_favor_of (which introduces the possibility of more mistakes) and could necessitate an increase in the number of evaluation passes (slowing down compilation for everyone).

I'd still be on favor of delaying landing this until 1.62 to buy us time to evaluate (but t-libs-api has jurisdiction).

@lcnr
Copy link
Contributor

lcnr commented Apr 26, 2022

so my understanding is:

we have two obligations we still have to prove:

  • Vec<Constraint>: AsRef<?T>: has two candidates which may apply
    • Vec<Constraint>: AsRef<Vec<Constraint>>, infers ?T to Vec<Constraint>
    • Vec<Constraint>: AsRef<[T]>, infers ?T to [T]
  • &'_ ?T: Into<Vec<Constraint>>: now also has two candidates
    • &'_ [Constraint]: Into<Vec<Constraint>>, infers ?T to [T]
    • &'_ [Constraint; ?N]: Into<Vec<Constraint>>, infers ?T to [Constraint; ?N]

the core issue here is that both obligations by themselves are rightfully ambiguous and as long as we continue to try and prove obligations by themselves breakage like this is unavoidable.

A possible solution would be:

  • given an ambiguous obligation, for each possibly applying candidate we remember how they would constrain inference variables, storing that in some optional Vec in the InferCtxt.
  • now, when later constraining an inference variable (most notably inside of a snapshot, which is where that would actually matter) we a new check if that Vec is Some for the given inference var. As this point we require the new constraint to unify with at least one of the possibilities in that Vec.

While something like that probably? works I don't think this is something we want. It would add a noticeable amount of complexity to our type inference and will end up causing inference to make some pretty large leaps in places where it is probably not expected.

alternatively we somehow add some preference in the case of ambiguity, but no preference is always correct here. if the preference is incorrect the resulting error will be pretty bad. it will be a worse version of the existing errors mentioning () or i32 due to the inference fallback

@yaahc
Copy link
Member

yaahc commented Apr 26, 2022

👍 for revert and re-evaluate, @shepmaster correct me if I'm wrong but there's no urgency here for landing this asap, correct?

I do think this might fall within the set of changes that we'd potentially just allow breakage on, similar to #75180 where we manually updated all the crates identified by crater.

Additionally, I'm wondering if rust-lang/rfcs#3240 could be extended to cover this case so we could move the breakage to an edition boundary. I'm hoping that might allow for a simpler solution.

@estebank
Copy link
Contributor

estebank commented Apr 26, 2022

Looking at candidate_should_be_dropped_in_favor_of, it seems like right place for rust-lang/rfcs#3240 (or similar) mechanism to be implemented.

Edit:

alternatively we somehow add some preference in the case of ambiguity, but no preference is always correct here

I was gonna make a point about how the inference resolver should strive to be as repeatable and clean as possible (while at the same time me thinking that UX trumps theoretical purity), but then I find this, and realize that's already gone out the window 😅

if let Some(self_ty1) = self.issue33140_self_ty(def_id1) {
if let Some(self_ty2) = self.issue33140_self_ty(def_id2) {
if self_ty1 == self_ty2 {
debug!(
"impls_are_allowed_to_overlap({:?}, {:?}) - issue #33140 HACK",
def_id1, def_id2
);
return Some(ImplOverlapKind::Issue33140);

/// If `def_id` is an issue 33140 hack impl, returns its self type; otherwise, returns `None`.
///
/// See [`ty::ImplOverlapKind::Issue33140`] for more details.
fn issue33140_self_ty(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Ty<'_>> {
debug!("issue33140_self_ty({:?})", def_id);
let trait_ref = tcx
.impl_trait_ref(def_id)
.unwrap_or_else(|| bug!("issue33140_self_ty called on inherent impl {:?}", def_id));
debug!("issue33140_self_ty({:?}), trait-ref={:?}", def_id, trait_ref);
let is_marker_like = tcx.impl_polarity(def_id) == ty::ImplPolarity::Positive
&& tcx.associated_item_def_ids(trait_ref.def_id).is_empty();
// Check whether these impls would be ok for a marker trait.
if !is_marker_like {
debug!("issue33140_self_ty - not marker-like!");
return None;
}
// impl must be `impl Trait for dyn Marker1 + Marker2 + ...`
if trait_ref.substs.len() != 1 {
debug!("issue33140_self_ty - impl has substs!");
return None;
}
let predicates = tcx.predicates_of(def_id);
if predicates.parent.is_some() || !predicates.predicates.is_empty() {
debug!("issue33140_self_ty - impl has predicates {:?}!", predicates);
return None;
}
let self_ty = trait_ref.self_ty();
let self_ty_matches = match self_ty.kind() {
ty::Dynamic(ref data, re) if re.is_static() => data.principal().is_none(),
_ => false,
};
if self_ty_matches {
debug!("issue33140_self_ty - MATCHES!");
Some(self_ty)
} else {
debug!("issue33140_self_ty - non-matching self type");
None
}
}

@jackh726
Copy link
Member

(fair warning, I've only skimmed this thread)

Interestingly, I think this might be a similar "problem" to the one that gets discussed with Chalk with the SLG vs recursive solver (some background: https://rust-lang.github.io/chalk/book/recursive.html#recursion-and-completeness). Without going into specific details, I think committing to a "smarter" inference here has practical effects in an eventual more-formal solver.

I wouldn't want to apply an ad-hoc solution to this problem to the trait solver without thinking about how we implement this in the future. Also given that there are ongoing efforts to define that, I think reverting for now is a good option.

@shepmaster
Copy link
Member

there's no urgency here for landing this asap

None that I know of.

The only downside is that I used the original PR as a way to show someone "look how easy it is to submit changes to Rust" 😉

estebank added a commit to estebank/rust that referenced this issue Apr 26, 2022
…e without reverting rust-lang#95098

 rust-lang#95098 introduces new `From` impls for `Vec`, which can break existing
 code that relies on inference when calling `.as_ref()`. This change
 explicitly carves out a bias in the inference machinery to keep
 existing code compiling, while still maintaining the new `From` impls.

 Reported in rust-lang#96074.
@estebank
Copy link
Contributor

I came up with the following unprincipled hack: #96446

Doing anything like this would in effect turn both Vec and AsRef into lang_items. I didn't see the mechanism that rust-lang/rfcs#3240 proposes for setting the edition on the impl, but whatever does that (an attribute?) would be something that'd have to be able to express this kind of check.

@joshtriplett joshtriplett added beta-nominated Nominated for backporting to the compiler in the beta channel. and removed beta-nominated Nominated for backporting to the compiler in the beta channel. labels Apr 27, 2022
@joshtriplett
Copy link
Member

We discussed this in today's @rust-lang/libs meeting.

Could we get a revert PR, please? We can then mark that as beta-nominated/beta-accepted.

@shepmaster
Copy link
Member

Opened #96489

ClementTsang added a commit to ClementTsang/bottom that referenced this issue Apr 27, 2022
For context, see:
- #708
- rust-lang/rust#96074

This changes the calls from `as_ref()`, which was causing problems,
to just `as_slice()` which works fine.
ClementTsang added a commit to ClementTsang/bottom that referenced this issue Apr 27, 2022
For context, see:
- #708
- rust-lang/rust#96074

This changes the calls from `as_ref()`, which was causing type
inference issues, to just `as_slice()` which works fine.
ClementTsang added a commit to ClementTsang/bottom that referenced this issue Apr 27, 2022
For context, see:
- #708
- rust-lang/rust#96074

This changes the calls from `as_ref()`, which was causing type
inference issues, to just `as_slice()` which works fine.
ClementTsang added a commit to ClementTsang/bottom that referenced this issue Apr 27, 2022
For context, see:
- #708
- rust-lang/rust#96074

This changes the calls from `as_ref()`, which was causing type
inference issues, to just `as_slice()` which works fine.
ClementTsang added a commit to ClementTsang/bottom that referenced this issue Apr 27, 2022
For context, see:
- #708
- rust-lang/rust#96074

This changes the calls from `as_ref()`, which was causing type
inference issues, to just `as_slice()` which works fine.
ClementTsang added a commit to ClementTsang/bottom that referenced this issue Apr 27, 2022
This changes various as_ref() calls as needed in order for bottom to successfully build in Rust beta 1.61, as they were causing type inference issues. These calls were either removed or changed to an alternative that does build (e.g. as_slice()).

Functionally, there should be no change.

For context, see:
- #708
- rust-lang/rust#96074
@jplatte
Copy link
Contributor

jplatte commented Apr 28, 2022

I think making inference even 'smarter' is just going to lead to more code that implicitly relies on certain impls not existing, which will ultimately prohibit more impls from being added because some ambiguities just cannot be resolved cleanly.

I would much rather see existing code that relies on there's-only-one-impl-for cleverness start raising future-incompat errors. Maybe we could even have impl Add<char> for String and friends at some point then.

@pnkfelix
Copy link
Member

pnkfelix commented May 5, 2022

Visiting this issue during T-compiler triage meeting.

We believe the beta backport of PR #96489 addresses the issue this was nominated for.

@rustbot label: -I-nominated-compiler

@apiraino
Copy link
Contributor

apiraino commented May 5, 2022

:)

@rustbot label -I-compiler-nominated

@rustbot rustbot removed the I-compiler-nominated Nominated for discussion during a compiler team meeting. label May 5, 2022
@Mark-Simulacrum Mark-Simulacrum removed the regression-from-stable-to-beta Performance or correctness regression from stable to beta. label May 15, 2022
@Mark-Simulacrum Mark-Simulacrum removed this from the 1.61.0 milestone May 15, 2022
@Mark-Simulacrum
Copy link
Member

Clearing milestone as we have landed #96489 on master (1.62+) and backported it to 1.61, so this is no longer a regression.

@m-ou-se m-ou-se removed P-high High priority I-libs-api-nominated Nominated for discussion during a libs-api team meeting. labels May 18, 2022
@Enselic
Copy link
Member

Enselic commented Mar 16, 2024

Triage: The revert is done and consensus seems to be to not change inference. Closing.

@Enselic Enselic closed this as completed Mar 16, 2024
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. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests