diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 4ecd56dfa40b6..f7556f5d3dae4 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -17,7 +17,6 @@ use rustc_type_ir::{ }; use tracing::{debug, instrument}; -use super::trait_goals::TraitGoalProvenVia; use super::{has_only_region_constraints, inspect}; use crate::delegate::SolverDelegate; use crate::solve::inspect::ProbeKind; @@ -361,6 +360,7 @@ pub(super) enum AssembleCandidatesFrom { /// user-written and built-in impls. We only expect `ParamEnv` and `AliasBound` /// candidates to be assembled. EnvAndBounds, + Impl, } impl AssembleCandidatesFrom { @@ -368,6 +368,7 @@ impl AssembleCandidatesFrom { match self { AssembleCandidatesFrom::All => true, AssembleCandidatesFrom::EnvAndBounds => false, + AssembleCandidatesFrom::Impl => true, } } } @@ -424,11 +425,14 @@ where return (candidates, failed_candidate_info); } - self.assemble_alias_bound_candidates(goal, &mut candidates); - self.assemble_param_env_candidates(goal, &mut candidates, &mut failed_candidate_info); - match assemble_from { AssembleCandidatesFrom::All => { + self.assemble_alias_bound_candidates(goal, &mut candidates); + self.assemble_param_env_candidates( + goal, + &mut candidates, + &mut failed_candidate_info, + ); self.assemble_builtin_impl_candidates(goal, &mut candidates); // For performance we only assemble impls if there are no candidates // which would shadow them. This is necessary to avoid hangs in rayon, @@ -455,6 +459,12 @@ where } } AssembleCandidatesFrom::EnvAndBounds => { + self.assemble_alias_bound_candidates(goal, &mut candidates); + self.assemble_param_env_candidates( + goal, + &mut candidates, + &mut failed_candidate_info, + ); // This is somewhat inconsistent and may make #57893 slightly easier to exploit. // However, it matches the behavior of the old solver. See // `tests/ui/traits/next-solver/normalization-shadowing/use_object_if_empty_env.rs`. @@ -464,6 +474,10 @@ where self.assemble_object_bound_candidates(goal, &mut candidates); } } + AssembleCandidatesFrom::Impl => { + self.assemble_builtin_impl_candidates(goal, &mut candidates); + self.assemble_impl_candidates(goal, &mut candidates); + } } (candidates, failed_candidate_info) @@ -1095,112 +1109,6 @@ where } } - /// Assemble and merge candidates for goals which are related to an underlying trait - /// goal. Right now, this is normalizes-to and host effect goals. - /// - /// We sadly can't simply take all possible candidates for normalization goals - /// and check whether they result in the same constraints. We want to make sure - /// that trying to normalize an alias doesn't result in constraints which aren't - /// otherwise required. - /// - /// Most notably, when proving a trait goal by via a where-bound, we should not - /// normalize via impls which have stricter region constraints than the where-bound: - /// - /// ```rust - /// trait Trait<'a> { - /// type Assoc; - /// } - /// - /// impl<'a, T: 'a> Trait<'a> for T { - /// type Assoc = u32; - /// } - /// - /// fn with_bound<'a, T: Trait<'a>>(_value: T::Assoc) {} - /// ``` - /// - /// The where-bound of `with_bound` doesn't specify the associated type, so we would - /// only be able to normalize `>::Assoc` by using the impl. This impl - /// adds a `T: 'a` bound however, which would result in a region error. Given that the - /// user explicitly wrote that `T: Trait<'a>` holds, this is undesirable and we instead - /// treat the alias as rigid. - /// - /// See trait-system-refactor-initiative#124 for more details. - #[instrument(level = "debug", skip_all, fields(proven_via, goal), ret)] - pub(super) fn assemble_and_merge_candidates>( - &mut self, - proven_via: Option, - goal: Goal, - inject_forced_ambiguity_candidate: impl FnOnce(&mut EvalCtxt<'_, D>) -> Option>, - inject_normalize_to_rigid_candidate: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, - ) -> QueryResult { - let Some(proven_via) = proven_via else { - // We don't care about overflow. If proving the trait goal overflowed, then - // it's enough to report an overflow error for that, we don't also have to - // overflow during normalization. - // - // We use `forced_ambiguity` here over `make_ambiguous_response_no_constraints` - // because the former will also record a built-in candidate in the inspector. - return self.forced_ambiguity(MaybeCause::Ambiguity).map(|cand| cand.result); - }; - - match proven_via { - TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => { - // Even when a trait bound has been proven using a where-bound, we - // still need to consider alias-bounds for normalization, see - // `tests/ui/next-solver/alias-bound-shadowed-by-env.rs`. - let (mut candidates, _) = self - .assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::EnvAndBounds); - debug!(?candidates); - - // If the trait goal has been proven by using the environment, we want to treat - // aliases as rigid if there are no applicable projection bounds in the environment. - if candidates.is_empty() { - return inject_normalize_to_rigid_candidate(self); - } - - // If we're normalizing an GAT, we bail if using a where-bound would constrain - // its generic arguments. - if let Some(result) = inject_forced_ambiguity_candidate(self) { - return result; - } - - // We still need to prefer where-bounds over alias-bounds however. - // See `tests/ui/winnowing/norm-where-bound-gt-alias-bound.rs`. - if candidates.iter().any(|c| matches!(c.source, CandidateSource::ParamEnv(_))) { - candidates.retain(|c| matches!(c.source, CandidateSource::ParamEnv(_))); - } - - if let Some((response, _)) = self.try_merge_candidates(&candidates) { - Ok(response) - } else { - self.flounder(&candidates) - } - } - TraitGoalProvenVia::Misc => { - let (mut candidates, _) = - self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All); - - // Prefer "orphaned" param-env normalization predicates, which are used - // (for example, and ideally only) when proving item bounds for an impl. - if candidates.iter().any(|c| matches!(c.source, CandidateSource::ParamEnv(_))) { - candidates.retain(|c| matches!(c.source, CandidateSource::ParamEnv(_))); - } - - // We drop specialized impls to allow normalization via a final impl here. In case - // the specializing impl has different inference constraints from the specialized - // impl, proving the trait goal is already ambiguous, so we never get here. This - // means we can just ignore inference constraints and don't have to special-case - // constraining the normalized-to `term`. - self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates); - if let Some((response, _)) = self.try_merge_candidates(&candidates) { - Ok(response) - } else { - self.flounder(&candidates) - } - } - } - } - /// Compute whether a param-env assumption is global or non-global after normalizing it. /// /// This is necessary because, for example, given: diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 2cb79a0219f6e..3c7fa5d522090 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -7,13 +7,15 @@ use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::solve::inspect::ProbeKind; use rustc_type_ir::solve::{AliasBoundKind, SizedTraitKind}; use rustc_type_ir::{self as ty, Interner, TypingMode, elaborate}; -use tracing::instrument; +use tracing::{debug, instrument}; use super::assembly::{Candidate, structural_traits}; use crate::delegate::SolverDelegate; +use crate::solve::assembly::{AllowInferenceConstraints, AssembleCandidatesFrom}; +use crate::solve::trait_goals::TraitGoalProvenVia; use crate::solve::{ - BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, NoSolution, - QueryResult, assembly, + BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause, + NoSolution, QueryResult, assembly, }; impl assembly::GoalKind for ty::HostEffectPredicate @@ -446,6 +448,61 @@ where goal.with(ecx.cx(), goal.predicate.trait_ref); ecx.compute_trait_goal(trait_goal) })?; - self.assemble_and_merge_candidates(proven_via, goal, |_ecx| None, |_ecx| Err(NoSolution)) + self.assemble_and_merge_candidates(proven_via, goal) + } + + #[instrument(level = "debug", skip(self), ret)] + fn assemble_and_merge_candidates( + &mut self, + proven_via: Option, + goal: Goal>, + ) -> QueryResult { + let Some(proven_via) = proven_via else { + // We don't care about overflow. If proving the trait goal overflowed, then + // it's enough to report an overflow error for that, we don't also have to + // overflow during normalization. + // + // We use `forced_ambiguity` here over `make_ambiguous_response_no_constraints` + // because the former will also record a built-in candidate in the inspector. + return self.forced_ambiguity(MaybeCause::Ambiguity).map(|cand| cand.result); + }; + + match proven_via { + TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => { + let (mut candidates, _) = self + .assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::EnvAndBounds); + debug!(?candidates); + + if candidates.iter().any(|c| matches!(c.source, CandidateSource::ParamEnv(_))) { + candidates.retain(|c| matches!(c.source, CandidateSource::ParamEnv(_))); + } + + if let Some((response, _)) = self.try_merge_candidates(&candidates) { + Ok(response) + } else { + self.flounder(&candidates) + } + } + TraitGoalProvenVia::Misc => { + let (mut candidates, _) = + self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All); + + if candidates.iter().any(|c| matches!(c.source, CandidateSource::ParamEnv(_))) { + candidates.retain(|c| matches!(c.source, CandidateSource::ParamEnv(_))); + } + + // We drop specialized impls to allow normalization via a final impl here. In case + // the specializing impl has different inference constraints from the specialized + // impl, proving the trait goal is already ambiguous, so we never get here. This + // means we can just ignore inference constraints and don't have to special-case + // constraining the normalized-to `term`. + self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates); + if let Some((response, _)) = self.try_merge_candidates(&candidates) { + Ok(response) + } else { + self.flounder(&candidates) + } + } + } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 794bda7726a4a..66dd312da8f64 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -2,6 +2,7 @@ mod anon_const; mod free_alias; mod inherent; mod opaque_types; +mod projection; use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; @@ -13,7 +14,6 @@ use tracing::instrument; use crate::delegate::SolverDelegate; use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes}; use crate::solve::assembly::{self, Candidate}; -use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause, NoSolution, QueryResult, @@ -33,55 +33,7 @@ where let cx = self.cx(); match goal.predicate.alias.kind(cx) { ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => { - let trait_ref = goal.predicate.alias.trait_ref(cx); - let (_, proven_via) = - self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| { - let trait_goal: Goal> = goal.with(cx, trait_ref); - ecx.compute_trait_goal(trait_goal) - })?; - self.assemble_and_merge_candidates( - proven_via, - goal, - |ecx| { - // FIXME(generic_associated_types): Addresses aggressive inference in #92917. - // - // If this type is a GAT with currently unconstrained arguments, we do not - // want to normalize it via a candidate which only applies for a specific - // instantiation. We could otherwise keep the GAT as rigid and succeed this way. - // See tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs. - // - // This only avoids normalization if a GAT argument is fully unconstrained. - // This is quite arbitrary but fixing it causes some ambiguity, see #125196. - for arg in goal.predicate.alias.own_args(cx).iter() { - let Some(term) = arg.as_term() else { - continue; - }; - match ecx.structurally_normalize_term(goal.param_env, term) { - Ok(term) => { - if term.is_infer() { - return Some( - ecx.evaluate_added_goals_and_make_canonical_response( - Certainty::AMBIGUOUS, - ), - ); - } - } - Err(NoSolution) => return Some(Err(NoSolution)), - } - } - - None - }, - |ecx| { - ecx.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| { - this.structurally_instantiate_normalizes_to_term( - goal, - goal.predicate.alias, - ); - this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - }, - ) + self.normalize_trait_associated_term(goal) } ty::AliasTermKind::InherentTy | ty::AliasTermKind::InherentConst => { self.normalize_inherent_associated_term(goal) diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/projection.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/projection.rs new file mode 100644 index 0000000000000..222e5b3ff6968 --- /dev/null +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/projection.rs @@ -0,0 +1,167 @@ +//! Computes a normalizes-to (projection) goal for trait associated types and consts. +//! +//! We sadly can't simply take all possible candidates for normalization goals +//! and check whether they result in the same constraints. We want to make sure +//! that trying to normalize an alias doesn't result in constraints which aren't +//! otherwise required. +//! +//! Most notably, when proving a trait goal by via a where-bound, we should not +//! normalize via impls which have stricter region constraints than the where-bound: +//! +//! ```rust +//! trait Trait<'a> { +//! type Assoc; +//! } +//! +//! impl<'a, T: 'a> Trait<'a> for T { +//! type Assoc = u32; +//! } +//! +//! fn with_bound<'a, T: Trait<'a>>(_value: T::Assoc) {} +//! ``` +//! +//! The where-bound of `with_bound` doesn't specify the associated type, so we would +//! only be able to normalize `>::Assoc` by using the impl. This impl +//! adds a `T: 'a` bound however, which would result in a region error. Given that the +//! user explicitly wrote that `T: Trait<'a>` holds, this is undesirable and we instead +//! treat the alias as rigid. +//! +//! See trait-system-refactor-initiative#124 for more details. +use rustc_type_ir::inherent::*; +use rustc_type_ir::{self as ty, Interner}; +use tracing::{debug, instrument}; + +use crate::delegate::SolverDelegate; +use crate::solve::assembly::{AllowInferenceConstraints, AssembleCandidatesFrom}; +use crate::solve::inspect::ProbeKind; +use crate::solve::trait_goals::TraitGoalProvenVia; +use crate::solve::{ + CandidateSource, Certainty, EvalCtxt, Goal, MaybeCause, NoSolution, QueryResult, +}; + +impl EvalCtxt<'_, D> +where + D: SolverDelegate, + I: Interner, +{ + pub(super) fn normalize_trait_associated_term( + &mut self, + goal: Goal>, + ) -> QueryResult { + // Fast path via preferred env, alias bound and object candidates. + if let Some(result) = self.assemble_and_merge_env_candidates(goal) { + return result; + } + + // Decides whether we need to assemble impl candidates. + // We only assemble them if the trait goal is also proved via impl candidates. + let cx = self.cx(); + let trait_ref = goal.predicate.alias.trait_ref(cx); + let (_, proven_via) = self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| { + let trait_goal: Goal> = goal.with(cx, trait_ref); + ecx.compute_trait_goal(trait_goal) + })?; + if let Some(proven_via) = proven_via { + match proven_via { + TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => { + // If the trait goal has been proven by using the environment, we want to treat + // aliases as rigid if there are no applicable projection bounds in the environment. + self.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| { + this.structurally_instantiate_normalizes_to_term( + goal, + goal.predicate.alias, + ); + this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + TraitGoalProvenVia::Misc => self.assemble_and_merge_impl_candidates(goal), + } + } else { + // We don't care about overflow. If proving the trait goal overflowed, then + // it's enough to report an overflow error for that, we don't also have to + // overflow during normalization. + // + // We use `forced_ambiguity` here over `make_ambiguous_response_no_constraints` + // because the former will also record a built-in candidate in the inspector. + self.forced_ambiguity(MaybeCause::Ambiguity).map(|cand| cand.result) + } + } + + #[instrument(level = "debug", skip(self), ret)] + fn assemble_and_merge_env_candidates( + &mut self, + goal: Goal>, + ) -> Option> { + // Even when a trait bound has been proven using a where-bound, we + // still need to consider alias-bounds for normalization, see + // `tests/ui/next-solver/alias-bound-shadowed-by-env.rs`. + let (mut candidates, _) = + self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::EnvAndBounds); + debug!(?candidates); + + if candidates.is_empty() { + return None; + } + + // If we're normalizing an GAT, we bail if using a where-bound would constrain + // its generic arguments. + // FIXME(generic_associated_types): Addresses aggressive inference in #92917. + // + // If this type is a GAT with currently unconstrained arguments, we do not + // want to normalize it via a candidate which only applies for a specific + // instantiation. We could otherwise keep the GAT as rigid and succeed this way. + // See tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs. + // + // This only avoids normalization if a GAT argument is fully unconstrained. + // This is quite arbitrary but fixing it causes some ambiguity, see #125196. + for arg in goal.predicate.alias.own_args(self.cx()).iter() { + let Some(term) = arg.as_term() else { + continue; + }; + match self.structurally_normalize_term(goal.param_env, term) { + Ok(term) => { + if term.is_infer() { + return Some(self.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + )); + } + } + Err(NoSolution) => return Some(Err(NoSolution)), + } + } + + // We still need to prefer where-bounds over alias-bounds however. + // See `tests/ui/winnowing/norm-where-bound-gt-alias-bound.rs`. + if candidates.iter().any(|c| matches!(c.source, CandidateSource::ParamEnv(_))) { + candidates.retain(|c| matches!(c.source, CandidateSource::ParamEnv(_))); + } + + let result = if let Some((response, _)) = self.try_merge_candidates(&candidates) { + Ok(response) + } else { + self.flounder(&candidates) + }; + Some(result) + } + + #[instrument(level = "debug", skip(self), ret)] + fn assemble_and_merge_impl_candidates( + &mut self, + goal: Goal>, + ) -> QueryResult { + let (mut candidates, _) = + self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::Impl); + + // We drop specialized impls to allow normalization via a final impl here. In case + // the specializing impl has different inference constraints from the specialized + // impl, proving the trait goal is already ambiguous, so we never get here. This + // means we can just ignore inference constraints and don't have to special-case + // constraining the normalized-to `term`. + self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates); + if let Some((response, _)) = self.try_merge_candidates(&candidates) { + Ok(response) + } else { + self.flounder(&candidates) + } + } +} diff --git a/tests/ui/traits/next-solver/normalization-shadowing/normalizes_to_ignores_unnormalizable_candidate.rs b/tests/ui/traits/next-solver/normalization-shadowing/normalizes_to_ignores_unnormalizable_candidate.rs index e66d1c485f899..1c5a654772d40 100644 --- a/tests/ui/traits/next-solver/normalization-shadowing/normalizes_to_ignores_unnormalizable_candidate.rs +++ b/tests/ui/traits/next-solver/normalization-shadowing/normalizes_to_ignores_unnormalizable_candidate.rs @@ -1,3 +1,4 @@ +//@ check-pass //@ compile-flags: -Znext-solver // Checks whether the new solver is smart enough to infer `?0 = U` when solving: @@ -5,7 +6,9 @@ // with `normalizes-to( as Trait>::Assoc, u8)` in the paramenv even when // there is a separate `Vec: Trait` bound in the paramenv. // -// We currently intentionally do not guide inference this way. +// Since we skip proving the trait goal in normalizes-to goal now, the normalizes-to +// goal can successfully resolve the infer var via param env. +// This causes the stalled ambiguous trait goal to succeed as well. trait Trait { type Assoc; @@ -23,7 +26,6 @@ where Vec: Trait, { foo(unconstrained()) - //~^ ERROR type annotations needed } fn main() {} diff --git a/tests/ui/traits/next-solver/normalization-shadowing/normalizes_to_ignores_unnormalizable_candidate.stderr b/tests/ui/traits/next-solver/normalization-shadowing/normalizes_to_ignores_unnormalizable_candidate.stderr deleted file mode 100644 index 36d281e11dd7a..0000000000000 --- a/tests/ui/traits/next-solver/normalization-shadowing/normalizes_to_ignores_unnormalizable_candidate.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error[E0283]: type annotations needed - --> $DIR/normalizes_to_ignores_unnormalizable_candidate.rs:25:5 - | -LL | foo(unconstrained()) - | ^^^ --------------- type must be known at this point - | | - | cannot infer type of the type parameter `T` declared on the function `foo` - | - = note: cannot satisfy `Vec<_>: Trait` -note: required by a bound in `foo` - --> $DIR/normalizes_to_ignores_unnormalizable_candidate.rs:14:11 - | -LL | fn foo>(x: T) {} - | ^^^^^^^^^^^^^^^^^ required by this bound in `foo` -help: consider specifying the generic argument - | -LL | foo::>(unconstrained()) - | ++++++++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/unconstrained-projection-normalization-2.next.stderr b/tests/ui/traits/unconstrained-projection-normalization-2.next.stderr index 2da655afa935c..b79a3ff7df07a 100644 --- a/tests/ui/traits/unconstrained-projection-normalization-2.next.stderr +++ b/tests/ui/traits/unconstrained-projection-normalization-2.next.stderr @@ -1,9 +1,3 @@ -error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates - --> $DIR/unconstrained-projection-normalization-2.rs:14:6 - | -LL | impl Every for Thing { - | ^ unconstrained type parameter - error[E0277]: the size for values of type `T` cannot be known at compilation time --> $DIR/unconstrained-projection-normalization-2.rs:16:18 | @@ -28,6 +22,12 @@ help: consider relaxing the implicit `Sized` restriction LL | type Assoc: ?Sized; | ++++++++ +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/unconstrained-projection-normalization-2.rs:14:6 + | +LL | impl Every for Thing { + | ^ unconstrained type parameter + error[E0282]: type annotations needed --> $DIR/unconstrained-projection-normalization-2.rs:20:11 | diff --git a/tests/ui/traits/unconstrained-projection-normalization.next.stderr b/tests/ui/traits/unconstrained-projection-normalization.next.stderr index c52e8dd68aa87..fade0b46f6691 100644 --- a/tests/ui/traits/unconstrained-projection-normalization.next.stderr +++ b/tests/ui/traits/unconstrained-projection-normalization.next.stderr @@ -1,9 +1,3 @@ -error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates - --> $DIR/unconstrained-projection-normalization.rs:13:6 - | -LL | impl Every for Thing { - | ^ unconstrained type parameter - error[E0277]: the size for values of type `T` cannot be known at compilation time --> $DIR/unconstrained-projection-normalization.rs:15:18 | @@ -28,6 +22,12 @@ help: consider relaxing the implicit `Sized` restriction LL | type Assoc: ?Sized; | ++++++++ +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/unconstrained-projection-normalization.rs:13:6 + | +LL | impl Every for Thing { + | ^ unconstrained type parameter + error: aborting due to 2 previous errors Some errors have detailed explanations: E0207, E0277.