From 660d985d12bd66dde21490fefb31c9b7fb22a67e Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 15 Jan 2023 22:39:43 +0100 Subject: [PATCH 01/13] Guarantee the memory layout of `Cell` --- library/core/src/cell.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 129213fde74..c65c30e566a 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -209,6 +209,12 @@ pub use once::OnceCell; /// A mutable memory location. /// +/// # Memory layout +/// +/// `Cell` has the same [memory layout and caveats as +/// `UnsafeCell`](UnsafeCell#memory-layout). In particular, this means that +/// `Cell` has the same in-memory representation as its inner type `T`. +/// /// # Examples /// /// In this example, you can see that `Cell` enables mutation inside an From 4fe232b27770f3e568fa4fa676f8bbd1ff595a6c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 6 Mar 2023 23:30:34 +0000 Subject: [PATCH 02/13] Do not ICE when we have fn pointer Fn obligations with bound vars in the self type --- .../src/traits/select/confirmation.rs | 14 +++++++++++--- .../fn-ptr.classic.stderr | 19 +++++++++++++++++++ tests/ui/higher-rank-trait-bounds/fn-ptr.rs | 14 ++++++++++++++ 3 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 tests/ui/higher-rank-trait-bounds/fn-ptr.classic.stderr create mode 100644 tests/ui/higher-rank-trait-bounds/fn-ptr.rs diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 21c158fd0fd..ee41d840bae 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -601,10 +601,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?obligation, "confirm_fn_pointer_candidate"); let tcx = self.tcx(); - let self_ty = self + + let Some(self_ty) = self .infcx - .shallow_resolve(obligation.self_ty().no_bound_vars()) - .expect("fn pointer should not capture bound vars from predicate"); + .shallow_resolve(obligation.self_ty().no_bound_vars()) else + { + // FIXME: Ideally we'd support `for<'a> fn(&'a ()): Fn(&'a ())`, + // but we do not currently. Luckily, such a bound is not + // particularly useful, so we don't expect users to write + // them often. + return Err(SelectionError::Unimplemented); + }; + let sig = self_ty.fn_sig(tcx); let trait_ref = closure_trait_ref_and_return_type( tcx, diff --git a/tests/ui/higher-rank-trait-bounds/fn-ptr.classic.stderr b/tests/ui/higher-rank-trait-bounds/fn-ptr.classic.stderr new file mode 100644 index 00000000000..9af6bc45c7a --- /dev/null +++ b/tests/ui/higher-rank-trait-bounds/fn-ptr.classic.stderr @@ -0,0 +1,19 @@ +error[E0277]: expected a `Fn<(&'w (),)>` closure, found `fn(&'w ())` + --> $DIR/fn-ptr.rs:12:5 + | +LL | ice(); + | ^^^ expected an `Fn<(&'w (),)>` closure, found `fn(&'w ())` + | + = help: the trait `for<'w> Fn<(&'w (),)>` is not implemented for `fn(&'w ())` +note: required by a bound in `ice` + --> $DIR/fn-ptr.rs:7:25 + | +LL | fn ice() + | --- required by a bound in this function +LL | where +LL | for<'w> fn(&'w ()): Fn(&'w ()), + | ^^^^^^^^^^ required by this bound in `ice` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/higher-rank-trait-bounds/fn-ptr.rs b/tests/ui/higher-rank-trait-bounds/fn-ptr.rs new file mode 100644 index 00000000000..853160f9612 --- /dev/null +++ b/tests/ui/higher-rank-trait-bounds/fn-ptr.rs @@ -0,0 +1,14 @@ +// revisions: classic next +//[next] compile-flags: -Ztrait-solver=next +//[next] check-pass + +fn ice() +where + for<'w> fn(&'w ()): Fn(&'w ()), +{ +} + +fn main() { + ice(); + //[classic]~^ ERROR expected a `Fn<(&'w (),)>` closure, found `fn(&'w ())` +} From 3097453f21668bb5469f8f5ef768de4d225856c6 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 9 Mar 2023 14:36:54 +0100 Subject: [PATCH 03/13] improve readability of winnowing --- .../src/traits/select/mod.rs | 105 +++++++++++------- 1 file changed, 66 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 48c3b3601b4..d28af1c29e9 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -465,14 +465,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if candidates.len() > 1 { let mut i = 0; while i < candidates.len() { - let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| { + let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| { self.candidate_should_be_dropped_in_favor_of( &candidates[i], &candidates[j], needs_infer, - ) + ) == DropVictim::Yes }); - if is_dup { + if should_drop_i { debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len()); candidates.swap_remove(i); } else { @@ -1842,15 +1842,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ProjectionMatchesProjection::No } } +} - /////////////////////////////////////////////////////////////////////////// - // WINNOW - // - // Winnowing is the process of attempting to resolve ambiguity by - // probing further. During the winnowing process, we unify all - // type variables and then we also attempt to evaluate recursive - // bounds to see if they are satisfied. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum DropVictim { + Yes, + No, +} +/// ## Winnowing +/// +/// Winnowing is the process of attempting to resolve ambiguity by +/// probing further. During the winnowing process, we unify all +/// type variables and then we also attempt to evaluate recursive +/// bounds to see if they are satisfied. +impl<'tcx> SelectionContext<'_, 'tcx> { /// Returns `true` if `victim` should be dropped in favor of /// `other`. Generally speaking we will drop duplicate /// candidates and prefer where-clause candidates. @@ -1861,9 +1867,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { victim: &EvaluatedCandidate<'tcx>, other: &EvaluatedCandidate<'tcx>, needs_infer: bool, - ) -> bool { + ) -> DropVictim { if victim.candidate == other.candidate { - return true; + return DropVictim::Yes; } // Check if a bound would previously have been removed when normalizing @@ -1887,11 +1893,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } // FIXME(@jswrenn): this should probably be more sophisticated - (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => false, + (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => DropVictim::No, // (*) - (BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_), _) => true, - (_, BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_)) => false, + (BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_), _) => { + DropVictim::Yes + } + (_, BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_)) => { + DropVictim::No + } (ParamCandidate(other), ParamCandidate(victim)) => { let same_except_bound_vars = other.skip_binder().trait_ref @@ -1905,28 +1915,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // or the current one if tied (they should both evaluate to the same answer). This is // probably best characterized as a "hack", since we might prefer to just do our // best to *not* create essentially duplicate candidates in the first place. - other.bound_vars().len() <= victim.bound_vars().len() + if other.bound_vars().len() <= victim.bound_vars().len() { + DropVictim::Yes + } else { + DropVictim::No + } } else if other.skip_binder().trait_ref == victim.skip_binder().trait_ref && victim.skip_binder().constness == ty::BoundConstness::NotConst && other.skip_binder().polarity == victim.skip_binder().polarity { // Drop otherwise equivalent non-const candidates in favor of const candidates. - true + DropVictim::Yes } else { - false + DropVictim::No } } // Drop otherwise equivalent non-const fn pointer candidates - (FnPointerCandidate { .. }, FnPointerCandidate { is_const: false }) => true, + (FnPointerCandidate { .. }, FnPointerCandidate { is_const: false }) => DropVictim::Yes, - // Global bounds from the where clause should be ignored - // here (see issue #50825). Otherwise, we have a where - // clause so don't go around looking for impls. - // Arbitrarily give param candidates priority - // over projection and object candidates. ( - ParamCandidate(ref cand), + ParamCandidate(ref other_cand), ImplCandidate(..) | ClosureCandidate { .. } | GeneratorCandidate @@ -1939,11 +1948,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | TraitAliasCandidate | ObjectCandidate(_) | ProjectionCandidate(..), - ) => !is_global(cand), - (ObjectCandidate(_) | ProjectionCandidate(..), ParamCandidate(ref cand)) => { + ) => { + if is_global(other_cand) { + DropVictim::No + } else { + // We have a where clause so don't go around looking + // for impls. Arbitrarily give param candidates priority + // over projection and object candidates. + // + // Global bounds from the where clause should be ignored + // here (see issue #50825). + DropVictim::Yes + } + } + (ObjectCandidate(_) | ProjectionCandidate(..), ParamCandidate(ref victim_cand)) => { // Prefer these to a global where-clause bound // (see issue #50825). - is_global(cand) + if is_global(victim_cand) { DropVictim::Yes } else { DropVictim::No } } ( ImplCandidate(_) @@ -1956,18 +1977,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } | TraitAliasCandidate, - ParamCandidate(ref cand), + ParamCandidate(ref victim_cand), ) => { // Prefer these to a global where-clause bound // (see issue #50825). - is_global(cand) && other.evaluation.must_apply_modulo_regions() + if is_global(victim_cand) && other.evaluation.must_apply_modulo_regions() { + DropVictim::Yes + } else { + DropVictim::No + } } (ProjectionCandidate(i, _), ProjectionCandidate(j, _)) | (ObjectCandidate(i), ObjectCandidate(j)) => { // Arbitrarily pick the lower numbered candidate for backwards // compatibility reasons. Don't let this affect inference. - i < j && !needs_infer + if i < j && !needs_infer { DropVictim::Yes } else { DropVictim::No } } (ObjectCandidate(_), ProjectionCandidate(..)) | (ProjectionCandidate(..), ObjectCandidate(_)) => { @@ -1987,7 +2012,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } | TraitAliasCandidate, - ) => true, + ) => DropVictim::Yes, ( ImplCandidate(..) @@ -2001,7 +2026,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinCandidate { .. } | TraitAliasCandidate, ObjectCandidate(_) | ProjectionCandidate(..), - ) => false, + ) => DropVictim::No, (&ImplCandidate(other_def), &ImplCandidate(victim_def)) => { // See if we can toss out `victim` based on specialization. @@ -2014,7 +2039,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let tcx = self.tcx(); if other.evaluation.must_apply_modulo_regions() { if tcx.specializes((other_def, victim_def)) { - return true; + return DropVictim::Yes; } } @@ -2060,13 +2085,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // will then correctly report an inference error, since the // existence of multiple marker trait impls tells us nothing // about which one should actually apply. - !needs_infer + if needs_infer { DropVictim::No } else { DropVictim::Yes } } - Some(_) => true, - None => false, + Some(_) => DropVictim::Yes, + None => DropVictim::No, } } else { - false + DropVictim::No } } @@ -2092,10 +2117,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } | TraitAliasCandidate, - ) => false, + ) => DropVictim::No, } } +} +impl<'tcx> SelectionContext<'_, 'tcx> { fn sized_conditions( &mut self, obligation: &TraitObligation<'tcx>, From d223c26bced5e3d861daa987820b30091198159e Mon Sep 17 00:00:00 2001 From: bohan Date: Thu, 9 Mar 2023 22:42:06 +0800 Subject: [PATCH 04/13] fix(lexer): not skipped whitespace warning for '\x0c' --- compiler/rustc_lexer/src/unescape.rs | 6 +++--- compiler/rustc_parse/locales/en-US.ftl | 2 +- tests/ui/str/str-escape.rs | 22 +++++++++++++++++++++- tests/ui/str/str-escape.stderr | 20 +++++++++++++++----- 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_lexer/src/unescape.rs b/compiler/rustc_lexer/src/unescape.rs index 8507ca9d89e..bb4d91247b8 100644 --- a/compiler/rustc_lexer/src/unescape.rs +++ b/compiler/rustc_lexer/src/unescape.rs @@ -298,10 +298,10 @@ where } let tail = &tail[first_non_space..]; if let Some(c) = tail.chars().nth(0) { - // For error reporting, we would like the span to contain the character that was not - // skipped. The +1 is necessary to account for the leading \ that started the escape. - let end = start + first_non_space + c.len_utf8() + 1; if c.is_whitespace() { + // For error reporting, we would like the span to contain the character that was not + // skipped. The +1 is necessary to account for the leading \ that started the escape. + let end = start + first_non_space + c.len_utf8() + 1; callback(start..end, Err(EscapeError::UnskippedWhitespaceWarning)); } } diff --git a/compiler/rustc_parse/locales/en-US.ftl b/compiler/rustc_parse/locales/en-US.ftl index e76e91fc1b1..d8e816aa115 100644 --- a/compiler/rustc_parse/locales/en-US.ftl +++ b/compiler/rustc_parse/locales/en-US.ftl @@ -710,7 +710,7 @@ parse_zero_chars = empty character literal parse_lone_slash = invalid trailing slash in literal .label = {parse_lone_slash} -parse_unskipped_whitespace = non-ASCII whitespace symbol '{$ch}' is not skipped +parse_unskipped_whitespace = whitespace symbol '{$ch}' is not skipped .label = {parse_unskipped_whitespace} parse_multiple_skipped_lines = multiple lines skipped by escaped newline diff --git a/tests/ui/str/str-escape.rs b/tests/ui/str/str-escape.rs index 0264632fd24..10a72421f24 100644 --- a/tests/ui/str/str-escape.rs +++ b/tests/ui/str/str-escape.rs @@ -1,11 +1,31 @@ // check-pass +// ignore-tidy-tab + fn main() { let s = "\ "; //~^^^ WARNING multiple lines skipped by escaped newline + assert_eq!(s, ""); + let s = "foo\   bar "; - //~^^^ WARNING non-ASCII whitespace symbol '\u{a0}' is not skipped + //~^^^ WARNING whitespace symbol '\u{a0}' is not skipped + assert_eq!(s, "foo  bar\n "); + + let s = "a\ + b"; + assert_eq!(s, "ab"); + + let s = "a\ + b"; + assert_eq!(s, "ab"); + + let s = "a\ + b"; + //~^^ WARNING whitespace symbol '\u{c}' is not skipped + // '\x0c' is ASCII whitespace, but it may not need skipped + // discussion: https://github.com/rust-lang/rust/pull/108403 + assert_eq!(s, "a\x0cb"); } diff --git a/tests/ui/str/str-escape.stderr b/tests/ui/str/str-escape.stderr index b2501f1a214..43b4f7e36f6 100644 --- a/tests/ui/str/str-escape.stderr +++ b/tests/ui/str/str-escape.stderr @@ -1,5 +1,5 @@ warning: multiple lines skipped by escaped newline - --> $DIR/str-escape.rs:3:14 + --> $DIR/str-escape.rs:5:14 | LL | let s = "\ | ______________^ @@ -7,15 +7,25 @@ LL | | LL | | "; | |_____________^ skipping everything up to and including this point -warning: non-ASCII whitespace symbol '\u{a0}' is not skipped - --> $DIR/str-escape.rs:7:17 +warning: whitespace symbol '\u{a0}' is not skipped + --> $DIR/str-escape.rs:11:17 | LL | let s = "foo\ | _________________^ LL | |   bar - | | ^ non-ASCII whitespace symbol '\u{a0}' is not skipped + | | ^ whitespace symbol '\u{a0}' is not skipped | |___| | -warning: 2 warnings emitted +warning: whitespace symbol '\u{c}' is not skipped + --> $DIR/str-escape.rs:25:15 + | +LL | let s = "a\ + | _______________^ +LL | | b"; + | | ^- whitespace symbol '\u{c}' is not skipped + | |____| + | + +warning: 3 warnings emitted From 3aea46979a103fc7415d6e211cc21734196adde5 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 6 Mar 2023 19:45:03 +0000 Subject: [PATCH 05/13] Emit alias-eq when equating numeric var and projection --- compiler/rustc_infer/src/infer/combine.rs | 28 +++++++++++++------ compiler/rustc_middle/src/ty/mod.rs | 2 +- .../ui/traits/new-solver/int-var-alias-eq.rs | 18 ++++++++++++ 3 files changed, 38 insertions(+), 10 deletions(-) create mode 100644 tests/ui/traits/new-solver/int-var-alias-eq.rs diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 33292e871b1..b0613abeb48 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -119,20 +119,30 @@ impl<'tcx> InferCtxt<'tcx> { self.unify_float_variable(!a_is_expected, v_id, v) } + // We don't expect `TyVar` or `Fresh*` vars at this point with lazy norm. + ( + ty::Alias(AliasKind::Projection, _), + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)), + ) + | ( + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)), + ty::Alias(AliasKind::Projection, _), + ) if self.tcx.trait_solver_next() => { + bug!() + } + + (_, ty::Alias(AliasKind::Projection, _)) | (ty::Alias(AliasKind::Projection, _), _) + if self.tcx.trait_solver_next() => + { + relation.register_type_equate_obligation(a, b); + Ok(a) + } + // All other cases of inference are errors (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { Err(TypeError::Sorts(ty::relate::expected_found(relation, a, b))) } - (ty::Alias(AliasKind::Projection, _), _) if self.tcx.trait_solver_next() => { - relation.register_type_equate_obligation(a, b); - Ok(b) - } - (_, ty::Alias(AliasKind::Projection, _)) if self.tcx.trait_solver_next() => { - relation.register_type_equate_obligation(b, a); - Ok(a) - } - _ => ty::relate::super_relate_tys(relation, a, b), } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 8cc8a0573bb..d9bfacd6e3c 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -995,7 +995,7 @@ impl<'tcx> Term<'tcx> { pub fn is_infer(&self) -> bool { match self.unpack() { - TermKind::Ty(ty) => ty.is_ty_or_numeric_infer(), + TermKind::Ty(ty) => ty.is_ty_var(), TermKind::Const(ct) => ct.is_ct_infer(), } } diff --git a/tests/ui/traits/new-solver/int-var-alias-eq.rs b/tests/ui/traits/new-solver/int-var-alias-eq.rs new file mode 100644 index 00000000000..2da387db4a9 --- /dev/null +++ b/tests/ui/traits/new-solver/int-var-alias-eq.rs @@ -0,0 +1,18 @@ +// check-pass +// compile-flags: -Ztrait-solver=next + +// HIR typeck ends up equating `<_#0i as Add>::Output == _#0i`. +// Want to make sure that we emit an alias-eq goal for this, +// instead of treating it as a type error and bailing. + +fn test() { + // fallback + let x = 1 + 2; +} + +fn test2() -> u32 { + // expectation from return ty + 1 + 2 +} + +fn main() {} From 1b6d6f92d1980d7f987fa0fe53cea8d8fb3209eb Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 8 Mar 2023 22:10:24 +0000 Subject: [PATCH 06/13] Fix canonicalizer bug for int/float vars too --- .../src/infer/canonical/canonicalizer.rs | 31 +++++++++++++------ compiler/rustc_infer/src/infer/mod.rs | 22 +++++++++++++ .../src/solve/canonical/canonicalize.rs | 8 ++--- .../canonical-int-var-eq-in-response.rs | 21 +++++++++++++ 4 files changed, 69 insertions(+), 13 deletions(-) create mode 100644 tests/ui/traits/new-solver/canonical-int-var-eq-in-response.rs diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index a43330e2d4e..8ac82653c0e 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -411,15 +411,28 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { } } - ty::Infer(ty::IntVar(_)) => self.canonicalize_ty_var( - CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Int) }, - t, - ), - - ty::Infer(ty::FloatVar(_)) => self.canonicalize_ty_var( - CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Float) }, - t, - ), + ty::Infer(ty::IntVar(vid)) => { + let nt = self.infcx.opportunistic_resolve_int_var(vid); + if nt != t { + return self.fold_ty(nt); + } else { + self.canonicalize_ty_var( + CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Int) }, + t, + ) + } + } + ty::Infer(ty::FloatVar(vid)) => { + let nt = self.infcx.opportunistic_resolve_float_var(vid); + if nt != t { + return self.fold_ty(nt); + } else { + self.canonicalize_ty_var( + CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Float) }, + t, + ) + } + } ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("encountered a fresh type during canonicalization") diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 4a2a5557313..4a834957959 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1363,6 +1363,28 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow_mut().const_unification_table().find(var) } + /// Resolves an int var to a rigid int type, if it was constrained to one, + /// or else the root int var in the unification table. + pub fn opportunistic_resolve_int_var(&self, vid: ty::IntVid) -> Ty<'tcx> { + let mut inner = self.inner.borrow_mut(); + if let Some(value) = inner.int_unification_table().probe_value(vid) { + value.to_type(self.tcx) + } else { + self.tcx.mk_int_var(inner.int_unification_table().find(vid)) + } + } + + /// Resolves a float var to a rigid int type, if it was constrained to one, + /// or else the root float var in the unification table. + pub fn opportunistic_resolve_float_var(&self, vid: ty::FloatVid) -> Ty<'tcx> { + let mut inner = self.inner.borrow_mut(); + if let Some(value) = inner.float_unification_table().probe_value(vid) { + value.to_type(self.tcx) + } else { + self.tcx.mk_float_var(inner.float_unification_table().find(vid)) + } + } + /// Where possible, replaces type/const variables in /// `value` with their final value. Note that region variables /// are unaffected. If a type/const variable has not been unified, it diff --git a/compiler/rustc_trait_selection/src/solve/canonical/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonical/canonicalize.rs index 981a8f45e45..7ee4f332306 100644 --- a/compiler/rustc_trait_selection/src/solve/canonical/canonicalize.rs +++ b/compiler/rustc_trait_selection/src/solve/canonical/canonicalize.rs @@ -278,16 +278,16 @@ impl<'tcx> TypeFolder> for Canonicalizer<'_, 'tcx> { Err(ui) => CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)), } } - ty::Infer(ty::IntVar(_)) => { - let nt = self.infcx.shallow_resolve(t); + ty::Infer(ty::IntVar(vid)) => { + let nt = self.infcx.opportunistic_resolve_int_var(vid); if nt != t { return self.fold_ty(nt); } else { CanonicalVarKind::Ty(CanonicalTyVarKind::Int) } } - ty::Infer(ty::FloatVar(_)) => { - let nt = self.infcx.shallow_resolve(t); + ty::Infer(ty::FloatVar(vid)) => { + let nt = self.infcx.opportunistic_resolve_float_var(vid); if nt != t { return self.fold_ty(nt); } else { diff --git a/tests/ui/traits/new-solver/canonical-int-var-eq-in-response.rs b/tests/ui/traits/new-solver/canonical-int-var-eq-in-response.rs new file mode 100644 index 00000000000..3f7316a2279 --- /dev/null +++ b/tests/ui/traits/new-solver/canonical-int-var-eq-in-response.rs @@ -0,0 +1,21 @@ +// check-pass + +trait Mirror { + type Assoc; +} + +impl Mirror for T { + type Assoc = T; +} + +trait Test {} +impl Test for i64 {} +impl Test for u64 {} + +fn mirror_me(t: T, s: ::Assoc) where ::Assoc: Test {} + +fn main() { + let mut x = 0; + mirror_me(x, 1); + x = 1i64; +} From d4b59a0da25ab72014ee0aaf7f7e647f1682e083 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 9 Mar 2023 17:11:30 +0000 Subject: [PATCH 07/13] Propagate expected return type instead of real return type in check_binop --- compiler/rustc_hir_typeck/src/op.rs | 7 ++-- .../issues/issue-66667-function-cmp-cycle.rs | 3 ++ .../issue-66667-function-cmp-cycle.stderr | 34 ++++++++++++++++--- .../cast-checks-handling-projections.rs | 2 +- .../cast-checks-handling-projections.stderr | 9 ----- 5 files changed, 37 insertions(+), 18 deletions(-) delete mode 100644 tests/ui/typeck/lazy-norm/cast-checks-handling-projections.stderr diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index eecded557a5..37783bc91bb 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -148,10 +148,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_ty, op, ); - self.demand_suptype(expr.span, builtin_return_ty, return_ty); + self.demand_eqtype(expr.span, builtin_return_ty, return_ty); + builtin_return_ty + } else { + return_ty } - - return_ty } } } diff --git a/tests/ui/issues/issue-66667-function-cmp-cycle.rs b/tests/ui/issues/issue-66667-function-cmp-cycle.rs index 7b025be11a0..b4f09fbbb04 100644 --- a/tests/ui/issues/issue-66667-function-cmp-cycle.rs +++ b/tests/ui/issues/issue-66667-function-cmp-cycle.rs @@ -1,16 +1,19 @@ fn first() { second == 1 //~ ERROR binary operation //~^ ERROR mismatched types + //~| ERROR mismatched types } fn second() { first == 1 //~ ERROR binary operation //~^ ERROR mismatched types + //~| ERROR mismatched types } fn bar() { bar == 1 //~ ERROR binary operation //~^ ERROR mismatched types + //~| ERROR mismatched types } fn main() {} diff --git a/tests/ui/issues/issue-66667-function-cmp-cycle.stderr b/tests/ui/issues/issue-66667-function-cmp-cycle.stderr index 887699ef5ce..d9a960ce197 100644 --- a/tests/ui/issues/issue-66667-function-cmp-cycle.stderr +++ b/tests/ui/issues/issue-66667-function-cmp-cycle.stderr @@ -15,8 +15,16 @@ LL | second == 1 = note: expected fn item `fn() {second}` found type `{integer}` +error[E0308]: mismatched types + --> $DIR/issue-66667-function-cmp-cycle.rs:2:5 + | +LL | fn first() { + | - help: try adding a return type: `-> bool` +LL | second == 1 + | ^^^^^^^^^^^ expected `()`, found `bool` + error[E0369]: binary operation `==` cannot be applied to type `fn() {first}` - --> $DIR/issue-66667-function-cmp-cycle.rs:7:11 + --> $DIR/issue-66667-function-cmp-cycle.rs:8:11 | LL | first == 1 | ----- ^^ - {integer} @@ -24,7 +32,7 @@ LL | first == 1 | fn() {first} error[E0308]: mismatched types - --> $DIR/issue-66667-function-cmp-cycle.rs:7:14 + --> $DIR/issue-66667-function-cmp-cycle.rs:8:14 | LL | first == 1 | ^ expected fn item, found integer @@ -32,8 +40,16 @@ LL | first == 1 = note: expected fn item `fn() {first}` found type `{integer}` +error[E0308]: mismatched types + --> $DIR/issue-66667-function-cmp-cycle.rs:8:5 + | +LL | fn second() { + | - help: try adding a return type: `-> bool` +LL | first == 1 + | ^^^^^^^^^^ expected `()`, found `bool` + error[E0369]: binary operation `==` cannot be applied to type `fn() {bar}` - --> $DIR/issue-66667-function-cmp-cycle.rs:12:9 + --> $DIR/issue-66667-function-cmp-cycle.rs:14:9 | LL | bar == 1 | --- ^^ - {integer} @@ -41,7 +57,7 @@ LL | bar == 1 | fn() {bar} error[E0308]: mismatched types - --> $DIR/issue-66667-function-cmp-cycle.rs:12:12 + --> $DIR/issue-66667-function-cmp-cycle.rs:14:12 | LL | bar == 1 | ^ expected fn item, found integer @@ -49,7 +65,15 @@ LL | bar == 1 = note: expected fn item `fn() {bar}` found type `{integer}` -error: aborting due to 6 previous errors +error[E0308]: mismatched types + --> $DIR/issue-66667-function-cmp-cycle.rs:14:5 + | +LL | fn bar() { + | - help: try adding a return type: `-> bool` +LL | bar == 1 + | ^^^^^^^^ expected `()`, found `bool` + +error: aborting due to 9 previous errors Some errors have detailed explanations: E0308, E0369. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/lazy-norm/cast-checks-handling-projections.rs b/tests/ui/typeck/lazy-norm/cast-checks-handling-projections.rs index 5ff567cd07c..3b261062f78 100644 --- a/tests/ui/typeck/lazy-norm/cast-checks-handling-projections.rs +++ b/tests/ui/typeck/lazy-norm/cast-checks-handling-projections.rs @@ -1,5 +1,5 @@ // compile-flags: -Ztrait-solver=next -// known-bug: unknown +// check-pass fn main() { (0u8 + 0u8) as char; diff --git a/tests/ui/typeck/lazy-norm/cast-checks-handling-projections.stderr b/tests/ui/typeck/lazy-norm/cast-checks-handling-projections.stderr deleted file mode 100644 index 6b09ccd5214..00000000000 --- a/tests/ui/typeck/lazy-norm/cast-checks-handling-projections.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0271]: type mismatch resolving `char == ::Output` - --> $DIR/cast-checks-handling-projections.rs:5:5 - | -LL | (0u8 + 0u8) as char; - | ^^^^^^^^^^^ types differ - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0271`. From 9574f39c2dc6312223fe562c1e04e4344eada316 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 9 Mar 2023 20:49:05 +0000 Subject: [PATCH 08/13] Use param's real type in try_eval_lit_or_param --- compiler/rustc_middle/src/ty/consts.rs | 9 ++++++--- .../src/traits/specialize/mod.rs | 6 +++--- tests/ui/const-generics/type_mismatch.rs | 1 + tests/ui/const-generics/type_mismatch.stderr | 18 +++++++++++++++--- tests/ui/const-generics/type_not_in_scope.rs | 1 - .../ui/const-generics/type_not_in_scope.stderr | 14 +++----------- .../bad-const-wf-doesnt-specialize.rs | 12 ++++++++++++ .../bad-const-wf-doesnt-specialize.stderr | 11 +++++++++++ tests/ui/transmutability/issue-101739-1.rs | 2 +- tests/ui/transmutability/issue-101739-1.stderr | 12 +++++++----- 10 files changed, 59 insertions(+), 27 deletions(-) create mode 100644 tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs create mode 100644 tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 5d0cf23280b..42101f6b931 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -135,6 +135,9 @@ impl<'tcx> Const<'tcx> { _, &hir::Path { res: Res::Def(DefKind::ConstParam, def_id), .. }, )) => { + // Use the type from the param's definition, since we can resolve it, + // not the expected parameter type from WithOptConstParam. + let param_ty = tcx.type_of(def_id).subst_identity(); match tcx.named_bound_var(expr.hir_id) { Some(rbv::ResolvedArg::EarlyBound(_)) => { // Find the name and index of the const parameter by indexing the generics of @@ -143,14 +146,14 @@ impl<'tcx> Const<'tcx> { let generics = tcx.generics_of(item_def_id); let index = generics.param_def_id_to_index[&def_id]; let name = tcx.item_name(def_id); - Some(tcx.mk_const(ty::ParamConst::new(index, name), ty)) + Some(tcx.mk_const(ty::ParamConst::new(index, name), param_ty)) } Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => Some(tcx.mk_const( ty::ConstKind::Bound(debruijn, ty::BoundVar::from_u32(index)), - ty, + param_ty, )), Some(rbv::ResolvedArg::Error(guar)) => { - Some(tcx.const_error_with_guaranteed(ty, guar)) + Some(tcx.const_error_with_guaranteed(param_ty, guar)) } arg => bug!("unexpected bound var resolution for {:?}: {arg:?}", expr.hir_id), } diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index d1d6a7a90cf..fcfb60b2603 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -99,10 +99,10 @@ pub fn translate_substs<'tcx>( } fulfill_implication(infcx, param_env, source_trait_ref, target_impl).unwrap_or_else( - |_| { + |()| { bug!( - "When translating substitutions for specialization, the expected \ - specialization failed to hold" + "When translating substitutions from {source_impl:?} to {target_impl:?}, \ + the expected specialization failed to hold" ) }, ) diff --git a/tests/ui/const-generics/type_mismatch.rs b/tests/ui/const-generics/type_mismatch.rs index 4a7534e3713..daa13277be0 100644 --- a/tests/ui/const-generics/type_mismatch.rs +++ b/tests/ui/const-generics/type_mismatch.rs @@ -1,5 +1,6 @@ fn foo() -> [u8; N] { bar::() //~ ERROR mismatched types + //~^ ERROR the constant `N` is not of type `u8` } fn bar() -> [u8; N] {} diff --git a/tests/ui/const-generics/type_mismatch.stderr b/tests/ui/const-generics/type_mismatch.stderr index b28ae8f7e71..6d8955e411e 100644 --- a/tests/ui/const-generics/type_mismatch.stderr +++ b/tests/ui/const-generics/type_mismatch.stderr @@ -1,3 +1,15 @@ +error: the constant `N` is not of type `u8` + --> $DIR/type_mismatch.rs:2:5 + | +LL | bar::() + | ^^^^^^^^ + | +note: required by a bound in `bar` + --> $DIR/type_mismatch.rs:6:8 + | +LL | fn bar() -> [u8; N] {} + | ^^^^^^^^^^^ required by this bound in `bar` + error[E0308]: mismatched types --> $DIR/type_mismatch.rs:2:11 | @@ -5,7 +17,7 @@ LL | bar::() | ^ expected `u8`, found `usize` error[E0308]: mismatched types - --> $DIR/type_mismatch.rs:5:26 + --> $DIR/type_mismatch.rs:6:26 | LL | fn bar() -> [u8; N] {} | --- ^^^^^^^ expected `[u8; N]`, found `()` @@ -13,11 +25,11 @@ LL | fn bar() -> [u8; N] {} | implicitly returns `()` as its body has no tail or `return` expression error[E0308]: mismatched types - --> $DIR/type_mismatch.rs:5:31 + --> $DIR/type_mismatch.rs:6:31 | LL | fn bar() -> [u8; N] {} | ^ expected `usize`, found `u8` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/type_not_in_scope.rs b/tests/ui/const-generics/type_not_in_scope.rs index 5933701808b..917abaed15e 100644 --- a/tests/ui/const-generics/type_not_in_scope.rs +++ b/tests/ui/const-generics/type_not_in_scope.rs @@ -6,6 +6,5 @@ impl X { } fn getn() -> [u8; N] {} //~^ ERROR expected type, found built-in attribute `cfg_attr` -//~| ERROR mismatched types fn main() {} diff --git a/tests/ui/const-generics/type_not_in_scope.stderr b/tests/ui/const-generics/type_not_in_scope.stderr index 5f45550a627..5eb81ca0522 100644 --- a/tests/ui/const-generics/type_not_in_scope.stderr +++ b/tests/ui/const-generics/type_not_in_scope.stderr @@ -10,15 +10,7 @@ error[E0573]: expected type, found built-in attribute `cfg_attr` LL | fn getn() -> [u8; N] {} | ^^^^^^^^ not a type -error[E0308]: mismatched types - --> $DIR/type_not_in_scope.rs:7:33 - | -LL | fn getn() -> [u8; N] {} - | ---- ^^^^^^^ expected `[u8; N]`, found `()` - | | - | implicitly returns `()` as its body has no tail or `return` expression +error: aborting due to 2 previous errors -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0308, E0412, E0573. -For more information about an error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0412, E0573. +For more information about an error, try `rustc --explain E0412`. diff --git a/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs new file mode 100644 index 00000000000..582b480aa25 --- /dev/null +++ b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs @@ -0,0 +1,12 @@ +#![feature(min_specialization)] + +// An impl that has an erroneous const substitution should not specialize one +// that is well-formed. + +struct S; + +impl Copy for S {} +impl Copy for S {} +//~^ ERROR conflicting implementations of trait `Copy` for type `S<_>` + +fn main() {} diff --git a/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr new file mode 100644 index 00000000000..a3906a9a22f --- /dev/null +++ b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr @@ -0,0 +1,11 @@ +error[E0119]: conflicting implementations of trait `Copy` for type `S<_>` + --> $DIR/bad-const-wf-doesnt-specialize.rs:9:1 + | +LL | impl Copy for S {} + | -------------------------------- first implementation here +LL | impl Copy for S {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `S<_>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/transmutability/issue-101739-1.rs b/tests/ui/transmutability/issue-101739-1.rs index bcb8b158edf..2b966609108 100644 --- a/tests/ui/transmutability/issue-101739-1.rs +++ b/tests/ui/transmutability/issue-101739-1.rs @@ -6,7 +6,7 @@ mod assert { pub fn is_transmutable() where Dst: BikeshedIntrinsicFrom, //~ ERROR cannot find type `Dst` in this scope - //~^ ERROR mismatched types + //~^ the constant `ASSUME_ALIGNMENT` is not of type `Assume` { } } diff --git a/tests/ui/transmutability/issue-101739-1.stderr b/tests/ui/transmutability/issue-101739-1.stderr index 7c6b533ef5f..f0fa93722b8 100644 --- a/tests/ui/transmutability/issue-101739-1.stderr +++ b/tests/ui/transmutability/issue-101739-1.stderr @@ -4,13 +4,15 @@ error[E0412]: cannot find type `Dst` in this scope LL | Dst: BikeshedIntrinsicFrom, | ^^^ not found in this scope -error[E0308]: mismatched types - --> $DIR/issue-101739-1.rs:8:50 +error: the constant `ASSUME_ALIGNMENT` is not of type `Assume` + --> $DIR/issue-101739-1.rs:8:14 | LL | Dst: BikeshedIntrinsicFrom, - | ^^^^^^^^^^^^^^^^ expected `Assume`, found `bool` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: required by a bound in `BikeshedIntrinsicFrom` + --> $SRC_DIR/core/src/mem/transmutability.rs:LL:COL error: aborting due to 2 previous errors -Some errors have detailed explanations: E0308, E0412. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0412`. From f7146136d79a4fb78c482797e50724bb4483a337 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 9 Mar 2023 20:52:06 +0000 Subject: [PATCH 09/13] Bail in combine if consts have different types --- compiler/rustc_infer/src/infer/combine.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 33292e871b1..adbd91f436c 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -34,7 +34,6 @@ use rustc_hir::def_id::DefId; use rustc_middle::infer::canonical::OriginalQueryValues; use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; -use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; @@ -161,9 +160,9 @@ impl<'tcx> InferCtxt<'tcx> { // // This probe is probably not strictly necessary but it seems better to be safe and not accidentally find // ourselves with a check to find bugs being required for code to compile because it made inference progress. - self.probe(|_| { + let compatible_types = self.probe(|_| { if a.ty() == b.ty() { - return; + return Ok(()); } // We don't have access to trait solving machinery in `rustc_infer` so the logic for determining if the @@ -173,15 +172,24 @@ impl<'tcx> InferCtxt<'tcx> { (relation.param_env(), a.ty(), b.ty()), &mut OriginalQueryValues::default(), ); - - if let Err(NoSolution) = self.tcx.check_tys_might_be_eq(canonical) { + self.tcx.check_tys_might_be_eq(canonical).map_err(|_| { self.tcx.sess.delay_span_bug( DUMMY_SP, &format!("cannot relate consts of different types (a={:?}, b={:?})", a, b,), - ); - } + ) + }) }); + // If the consts have differing types, just bail with a const error with + // the expected const's type. Specifically, we don't want const infer vars + // to do any type shapeshifting before and after resolution. + if let Err(guar) = compatible_types { + return Ok(self.tcx.const_error_with_guaranteed( + if relation.a_is_expected() { a.ty() } else { b.ty() }, + guar, + )); + } + match (a.kind(), b.kind()) { ( ty::ConstKind::Infer(InferConst::Var(a_vid)), From a30c2c26c08642dfb6b2c56121bea5ca45465c1b Mon Sep 17 00:00:00 2001 From: Ezra Shaw Date: Thu, 9 Mar 2023 20:42:45 +1300 Subject: [PATCH 10/13] feat: implement better error for manual impl of `Fn*` traits --- compiler/rustc_hir_analysis/locales/en-US.ftl | 3 + .../rustc_hir_analysis/src/astconv/errors.rs | 107 ++++++++++++------ .../src/astconv/generics.rs | 12 +- .../rustc_hir_analysis/src/astconv/mod.rs | 13 +-- compiler/rustc_hir_analysis/src/errors.rs | 14 ++- ...-gate-unboxed-closures-manual-impls.stderr | 6 + tests/ui/fn/issue-39259.rs | 13 +++ tests/ui/fn/issue-39259.stderr | 15 +++ tests/ui/lifetimes/issue-95023.stderr | 6 + tests/ui/traits/issue-87558.stderr | 6 + 10 files changed, 143 insertions(+), 52 deletions(-) create mode 100644 tests/ui/fn/issue-39259.rs create mode 100644 tests/ui/fn/issue-39259.stderr diff --git a/compiler/rustc_hir_analysis/locales/en-US.ftl b/compiler/rustc_hir_analysis/locales/en-US.ftl index 3c62529442c..84a6629404b 100644 --- a/compiler/rustc_hir_analysis/locales/en-US.ftl +++ b/compiler/rustc_hir_analysis/locales/en-US.ftl @@ -42,6 +42,9 @@ hir_analysis_assoc_type_binding_not_allowed = associated type bindings are not allowed here .label = associated type not allowed here +hir_analysis_parenthesized_fn_trait_expansion = + parenthesized trait syntax expands to `{$expanded_type}` + hir_analysis_typeof_reserved_keyword_used = `typeof` is a reserved keyword but unimplemented .suggestion = consider replacing `typeof(...)` with an actual type diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index c49e4d9d581..156334fe785 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -1,10 +1,14 @@ use crate::astconv::AstConv; -use crate::errors::{ManualImplementation, MissingTypeParams}; +use crate::errors::{ + AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams, + ParenthesizedFnTraitExpansion, +}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::traits::FulfillmentError; +use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{self, Ty}; use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; @@ -78,43 +82,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Do not suggest the other syntax if we are in trait impl: // the desugaring would contain an associated type constraint. if !is_impl { - let args = trait_segment - .args - .as_ref() - .and_then(|args| args.args.get(0)) - .and_then(|arg| match arg { - hir::GenericArg::Type(ty) => match ty.kind { - hir::TyKind::Tup(t) => t - .iter() - .map(|e| sess.source_map().span_to_snippet(e.span)) - .collect::, _>>() - .map(|a| a.join(", ")), - _ => sess.source_map().span_to_snippet(ty.span), - } - .map(|s| format!("({})", s)) - .ok(), - _ => None, - }) - .unwrap_or_else(|| "()".to_string()); - let ret = trait_segment - .args() - .bindings - .iter() - .find_map(|b| match (b.ident.name == sym::Output, &b.kind) { - (true, hir::TypeBindingKind::Equality { term }) => { - let span = match term { - hir::Term::Ty(ty) => ty.span, - hir::Term::Const(c) => self.tcx().hir().span(c.hir_id), - }; - sess.source_map().span_to_snippet(span).ok() - } - _ => None, - }) - .unwrap_or_else(|| "()".to_string()); err.span_suggestion( span, "use parenthetical notation instead", - format!("{}{} -> {}", trait_segment.ident, args, ret), + fn_trait_to_string(self.tcx(), trait_segment, true), Applicability::MaybeIncorrect, ); } @@ -629,3 +600,69 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { err.emit(); } } + +/// Emits an error regarding forbidden type binding associations +pub fn prohibit_assoc_ty_binding( + tcx: TyCtxt<'_>, + span: Span, + segment: Option<(&hir::PathSegment<'_>, Span)>, +) { + tcx.sess.emit_err(AssocTypeBindingNotAllowed { span, fn_trait_expansion: if let Some((segment, span)) = segment && segment.args().parenthesized { + Some(ParenthesizedFnTraitExpansion { span, expanded_type: fn_trait_to_string(tcx, segment, false) }) + } else { + None + }}); +} + +pub(crate) fn fn_trait_to_string( + tcx: TyCtxt<'_>, + trait_segment: &hir::PathSegment<'_>, + parenthesized: bool, +) -> String { + let args = trait_segment + .args + .as_ref() + .and_then(|args| args.args.get(0)) + .and_then(|arg| match arg { + hir::GenericArg::Type(ty) => match ty.kind { + hir::TyKind::Tup(t) => t + .iter() + .map(|e| tcx.sess.source_map().span_to_snippet(e.span)) + .collect::, _>>() + .map(|a| a.join(", ")), + _ => tcx.sess.source_map().span_to_snippet(ty.span), + } + .map(|s| { + // `s.empty()` checks to see if the type is the unit tuple, if so we don't want a comma + if parenthesized || s.is_empty() { format!("({})", s) } else { format!("({},)", s) } + }) + .ok(), + _ => None, + }) + .unwrap_or_else(|| "()".to_string()); + + let ret = trait_segment + .args() + .bindings + .iter() + .find_map(|b| match (b.ident.name == sym::Output, &b.kind) { + (true, hir::TypeBindingKind::Equality { term }) => { + let span = match term { + hir::Term::Ty(ty) => ty.span, + hir::Term::Const(c) => tcx.hir().span(c.hir_id), + }; + + (span != tcx.hir().span(trait_segment.hir_id)) + .then_some(tcx.sess.source_map().span_to_snippet(span).ok()) + .flatten() + } + _ => None, + }) + .unwrap_or_else(|| "()".to_string()); + + if parenthesized { + format!("{}{} -> {}", trait_segment.ident, args, ret) + } else { + format!("{}<{}, Output={}>", trait_segment.ident, args, ret) + } +} diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs index 7f6518ffd71..2f4963f6bc3 100644 --- a/compiler/rustc_hir_analysis/src/astconv/generics.rs +++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs @@ -1,9 +1,8 @@ use super::IsMethodCall; use crate::astconv::{ - CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, - GenericArgCountResult, GenericArgPosition, + errors::prohibit_assoc_ty_binding, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, + GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition, }; -use crate::errors::AssocTypeBindingNotAllowed; use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs}; use rustc_ast::ast::ParamKindOrd; use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; @@ -433,7 +432,7 @@ pub(crate) fn check_generic_arg_count( (gen_pos != GenericArgPosition::Type || infer_args) && !gen_args.has_lifetime_params(); if gen_pos != GenericArgPosition::Type && let Some(b) = gen_args.bindings.first() { - prohibit_assoc_ty_binding(tcx, b.span); + prohibit_assoc_ty_binding(tcx, b.span, None); } let explicit_late_bound = @@ -589,11 +588,6 @@ pub(crate) fn check_generic_arg_count( } } -/// Emits an error regarding forbidden type binding associations -pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) { - tcx.sess.emit_err(AssocTypeBindingNotAllowed { span }); -} - /// Prohibits explicit lifetime arguments if late-bound lifetime parameters /// are present. This is used both for datatypes and function calls. pub(crate) fn prohibit_explicit_late_bound_lifetimes( diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 899029d98e0..5db404608b3 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -5,9 +5,8 @@ mod errors; pub mod generics; -use crate::astconv::generics::{ - check_generic_arg_count, create_substs_for_generic_args, prohibit_assoc_ty_binding, -}; +use crate::astconv::errors::prohibit_assoc_ty_binding; +use crate::astconv::generics::{check_generic_arg_count, create_substs_for_generic_args}; use crate::bounds::Bounds; use crate::collect::HirPlaceholderCollector; use crate::errors::{ @@ -295,7 +294,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ty::BoundConstness::NotConst, ); if let Some(b) = item_segment.args().bindings.first() { - prohibit_assoc_ty_binding(self.tcx(), b.span); + prohibit_assoc_ty_binding(self.tcx(), b.span, Some((item_segment, span))); } substs @@ -631,7 +630,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ); if let Some(b) = item_segment.args().bindings.first() { - prohibit_assoc_ty_binding(self.tcx(), b.span); + prohibit_assoc_ty_binding(self.tcx(), b.span, Some((item_segment, span))); } args @@ -825,7 +824,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { constness, ); if let Some(b) = trait_segment.args().bindings.first() { - prohibit_assoc_ty_binding(self.tcx(), b.span); + prohibit_assoc_ty_binding(self.tcx(), b.span, Some((trait_segment, span))); } self.tcx().mk_trait_ref(trait_def_id, substs) } @@ -2596,7 +2595,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { for segment in segments { // Only emit the first error to avoid overloading the user with error messages. if let Some(b) = segment.args().bindings.first() { - prohibit_assoc_ty_binding(self.tcx(), b.span); + prohibit_assoc_ty_binding(self.tcx(), b.span, None); return true; } } diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 74fec93d91e..bf2cadddc0d 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -5,7 +5,7 @@ use rustc_errors::{ error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic, MultiSpan, }; -use rustc_macros::Diagnostic; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::Ty; use rustc_span::{symbol::Ident, Span, Symbol}; @@ -129,6 +129,18 @@ pub struct AssocTypeBindingNotAllowed { #[primary_span] #[label] pub span: Span, + + #[subdiagnostic] + pub fn_trait_expansion: Option, +} + +#[derive(Subdiagnostic)] +#[help(hir_analysis_parenthesized_fn_trait_expansion)] +pub struct ParenthesizedFnTraitExpansion { + #[primary_span] + pub span: Span, + + pub expanded_type: String, } #[derive(Diagnostic)] diff --git a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr index f647380ef9b..b1613f638d3 100644 --- a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr +++ b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr @@ -64,6 +64,12 @@ error[E0229]: associated type bindings are not allowed here | LL | impl FnOnce() for Foo1 { | ^^^^^^^^ associated type not allowed here + | +help: parenthesized trait syntax expands to `FnOnce<(), Output=()>` + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:16:6 + | +LL | impl FnOnce() for Foo1 { + | ^^^^^^^^ error[E0658]: the precise format of `Fn`-family traits' type parameters is subject to change --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:23:6 diff --git a/tests/ui/fn/issue-39259.rs b/tests/ui/fn/issue-39259.rs new file mode 100644 index 00000000000..5872f1007b0 --- /dev/null +++ b/tests/ui/fn/issue-39259.rs @@ -0,0 +1,13 @@ +#![feature(fn_traits)] +#![feature(unboxed_closures)] + +struct S; + +impl Fn(u32) -> u32 for S { +//~^ ERROR associated type bindings are not allowed here [E0229] + fn call(&self) -> u32 { + 5 + } +} + +fn main() {} diff --git a/tests/ui/fn/issue-39259.stderr b/tests/ui/fn/issue-39259.stderr new file mode 100644 index 00000000000..b656b76bfe4 --- /dev/null +++ b/tests/ui/fn/issue-39259.stderr @@ -0,0 +1,15 @@ +error[E0229]: associated type bindings are not allowed here + --> $DIR/issue-39259.rs:6:17 + | +LL | impl Fn(u32) -> u32 for S { + | ^^^ associated type not allowed here + | +help: parenthesized trait syntax expands to `Fn<(u32,), Output=u32>` + --> $DIR/issue-39259.rs:6:6 + | +LL | impl Fn(u32) -> u32 for S { + | ^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0229`. diff --git a/tests/ui/lifetimes/issue-95023.stderr b/tests/ui/lifetimes/issue-95023.stderr index 35c3797c77a..5b93eff8614 100644 --- a/tests/ui/lifetimes/issue-95023.stderr +++ b/tests/ui/lifetimes/issue-95023.stderr @@ -25,6 +25,12 @@ error[E0229]: associated type bindings are not allowed here | LL | impl Fn(&isize) for Error { | ^^^^^^^^^^ associated type not allowed here + | +help: parenthesized trait syntax expands to `Fn<(&isize,), Output=()>` + --> $DIR/issue-95023.rs:3:6 + | +LL | impl Fn(&isize) for Error { + | ^^^^^^^^^^ error[E0220]: associated type `B` not found for `Self` --> $DIR/issue-95023.rs:6:44 diff --git a/tests/ui/traits/issue-87558.stderr b/tests/ui/traits/issue-87558.stderr index 494274d8c30..b647f9794bd 100644 --- a/tests/ui/traits/issue-87558.stderr +++ b/tests/ui/traits/issue-87558.stderr @@ -17,6 +17,12 @@ error[E0229]: associated type bindings are not allowed here | LL | impl Fn(&isize) for Error { | ^^^^^^^^^^ associated type not allowed here + | +help: parenthesized trait syntax expands to `Fn<(&isize,), Output=()>` + --> $DIR/issue-87558.rs:3:6 + | +LL | impl Fn(&isize) for Error { + | ^^^^^^^^^^ error: aborting due to 3 previous errors From 14818e28d10f2dfb3e0bddbe3140b2cab7763442 Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 10 Mar 2023 10:49:05 +0100 Subject: [PATCH 11/13] updating comment Co-authored-by: Ruby Lazuli --- compiler/rustc_trait_selection/src/traits/select/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index d28af1c29e9..ba4df2b3720 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1857,7 +1857,7 @@ enum DropVictim { /// type variables and then we also attempt to evaluate recursive /// bounds to see if they are satisfied. impl<'tcx> SelectionContext<'_, 'tcx> { - /// Returns `true` if `victim` should be dropped in favor of + /// Returns `DropVictim::Yes` if `victim` should be dropped in favor of /// `other`. Generally speaking we will drop duplicate /// candidates and prefer where-clause candidates. /// From a8184849919ec577fa80a2fd763769ad2e244fd5 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 10 Mar 2023 11:22:27 +0100 Subject: [PATCH 12/13] Update triagebot rust-analyzer team mention The working group got renamed --- triagebot.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index 403a7087ee1..bbd29b04cc5 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -430,7 +430,7 @@ message = "The Miri subtree was changed" cc = ["@rust-lang/miri"] [mentions."src/tools/rust-analyzer"] -cc = ["@rust-lang/wg-rls-2"] +cc = ["@rust-lang/rust-analyzer"] [mentions."src/tools/rustfmt"] cc = ["@rust-lang/rustfmt"] From ad2bcb5c0e7603092011b6a5ca6328ae6c3fa2c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lanteri=20Thauvin?= Date: Fri, 10 Mar 2023 13:50:51 +0100 Subject: [PATCH 13/13] Forbid `#[target_feature]` on safe default implementations --- compiler/rustc_codegen_ssa/src/target_features.rs | 2 +- .../ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs | 6 ++++++ .../rfcs/rfc-2396-target_feature-11/trait-impl.stderr | 11 ++++++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 576e90ae66b..679dc0866f2 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -442,7 +442,7 @@ fn asm_target_features(tcx: TyCtxt<'_>, did: DefId) -> &FxIndexSet { pub fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) { if let DefKind::AssocFn = tcx.def_kind(id) { let parent_id = tcx.local_parent(id); - if let DefKind::Impl { of_trait: true } = tcx.def_kind(parent_id) { + if let DefKind::Trait | DefKind::Impl { of_trait: true } = tcx.def_kind(parent_id) { tcx.sess .struct_span_err( attr_span, diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs index 7314fa8cced..9108f27b5f7 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs @@ -18,4 +18,10 @@ impl Foo for Bar { unsafe fn unsf_foo(&self) {} } +trait Qux { + #[target_feature(enable = "sse2")] + //~^ ERROR cannot be applied to safe trait method + fn foo(&self) {} +} + fn main() {} diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr index 07d6e090059..eb0f18edd34 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr @@ -1,3 +1,12 @@ +error: `#[target_feature(..)]` cannot be applied to safe trait method + --> $DIR/trait-impl.rs:22:5 + | +LL | #[target_feature(enable = "sse2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be applied to safe trait method +LL | +LL | fn foo(&self) {} + | ------------- not an `unsafe` function + error: `#[target_feature(..)]` cannot be applied to safe trait method --> $DIR/trait-impl.rs:13:5 | @@ -7,5 +16,5 @@ LL | LL | fn foo(&self) {} | ------------- not an `unsafe` function -error: aborting due to previous error +error: aborting due to 2 previous errors