Rollup merge of #126575 - fmease:update-lint-type_alias_bounds, r=compiler-errors

Make it crystal clear what lint `type_alias_bounds` actually signifies

This is part of my work on https://github.com/rust-lang/rust/labels/F-lazy_type_alias ([tracking issue](#112792)).

---

To recap, the lint `type_alias_bounds` detects bounds on generic parameters and where clauses on (eager) type aliases. These bounds should've never been allowed because they are currently neither enforced[^1] at usage sites of type aliases nor thoroughly checked for correctness at definition sites due to the way type aliases are represented in the compiler. Allowing them was an oversight.

Explicitly label this as a known limitation of the type checker/system and establish the experimental feature `lazy_type_alias` as its eventual proper solution.

Where this becomes a bit tricky (for me as a rustc dev) are the "secondary effects" of these bounds whose existence I sadly can't deny. As a matter of fact, type alias bounds do play some small roles during type checking. However, after a lot of thinking over the last two weeks I've come to the conclusion (not without second-guessing myself though) that these use cases should not trump the fact that these bounds are currently *inherently broken*. Therefore the lint `type_alias_bounds` should and will continue to flag bounds that may have subordinate uses.

The two *known* secondary effects are:

1. They may enable the use of "shorthand" associated type paths `T::Assoc` (as opposed to fully qualified paths `<T as Trait>::Assoc`) where `T` is a type param bounded by some trait `Trait` which defines that assoc ty.
2. They may affect the default lifetime of trait object types passed as a type argument to the type alias. That concept is called (trait) object lifetime default.

The second one is negligible, no question asked. The first one however is actually "kinda nice" (for writability) and comes up in practice from time to time.

So why don't I just special-case trait bounds that "define" shorthand assoc type paths as originally planned in #125709?

1. Starting to permit even a tiny subset of bounds would already be enough to send a signal to users that bounds in type aliases have been legitimized and that they can expect to see type alias bounds in the wild from now on (proliferation). This would be actively misleading and dangerous because those bounds don't behave at all like one would expect, they are *not real*[^2]!
   1. Let's take `type A<T: Trait> = T::Proj;` for example. Everywhere else in the language `T: Trait` means `T: Trait + Sized`. For type aliases, that's not the case though: `T: Trait` and `T: Trait + ?Sized` for that matter do neither mean `T: Trait + Sized` nor `T: Trait + ?Sized` (for both!). Instead, whether `T` requires `Sized` or not entirely depends on the definition of `Trait`[^2]. Namely, whether or not it is bounded by `Sized`.
   2. Given `type A<T: Trait<AssocA = ()>> = T::AssocB;`, while `X: Trait` gets checked given `A<X>` (by virtue of projection wfchecking post alias expansion[^2]), the associated type constraint `AssocA = ()` gets dropped entirely! While we could choose to warn on such cases, it would inevitably lead to a huge pile of special cases.
   3. While it's common knowledge that the body / aliased type / RHS of an (eager) type alias does not get checked for well-formedness, I'm not sure if people would realize that that extends to bounds as well. Namely, `type A<T: Trait<[u8]>> = T::Proj;` compiles even if `Trait`'s generic parameter requires `Sized`. Of course, at usage sites `[u8]: Sized` would still end up getting checked[^2], so it's not a huge problem if you have full control over `A`. However, imagine that `A` was actually part of a public API and was never used inside the defining crate (not unreasonable). In such a scenario, downstream users would be presented with an impossible to use type alias! Remember, bounds may grow arbitrarily complex and nuanced in practice.
   4. Even if we allowed trait bounds that "define" shorthand assoc type paths, we would still need to continue to warn in cases where the assoc ty comes from a supertrait despite the fact that the shorthand syntax can be used: `type A<T: Sub> = T::Assoc;` does compile given `trait Sub: Super {}` and `trait Super { type Assoc; }`. However, `A<X>` does not enforce `X: Sub`, only `X: Super`[^2]. All that to say, type alias bounds are simply not real and we shouldn't pretend they are!
   5. Summarizing the points above, we would be legitimizing bounds that are completely broken!
2. It's infeasible to implement: Due to the lack of `TypeckResults` in `ItemCtxt` (and a way to propagate it to other parts of the compiler), the resolution of type-dependent paths in non-`Body` items (most notably type aliases) is not recoverable from the HIR alone which would be necessary because the information of whether an associated type path (projection) is a shorthand is only present pre&in-HIR and doesn't survive HIR ty lowering. Of course, I could rerun parts of HIR ty lowering inside the lint `type_alias_bounds` (namely, `probe_single_ty_param_bound_for_assoc_ty` which would need to be exposed or alternatively a stripped-down version of it). This likely has a performance impact and introduces complexity. In short, the "benefits" are not worth the costs.

---

* 3rd commit: Update a diagnostic to avoid suggesting type alias bounds
* 4th commit: Flag type alias bounds even if the RHS contains inherent associated types.
  * I started to allow them at some point in the past which was not correct (see commit for details)
* 5th commit: Allow type alias bounds if the RHS contains const projections and GCEs are enabled
  * (and add a `FIXME(generic_const_exprs)` to be revisited before (M)GCE's stabilization)
  * As a matter of fact type alias bounds are enforced in this case because the contained AnonConsts do get checked for well-formedness and crucially they inherit the generics and predicates of their parent item (here: the type alias)
* Remaining commits: Improve the lint `type_alias_bounds` itself

---

Fixes #125789 (sugg diag fix).
Fixes #125709 (wontfix, acknowledgement, sugg diag applic fix).
Fixes #104918 (sugg diag applic fix).
Fixes #100270 (wontfix, acknowledgement, sugg diag applic fix).
Fixes #94398 (true fix).

r? `@compiler-errors` `@oli-obk`

[^1]: From the perspective of the trait solver.
[^2]: Given `type A<T: Trait> = T::Proj;`, the reason why the trait bound "`T: Trait`" gets *seemingly* enforced at usage sites of the type alias `A` is simply because `A<X>` gets expanded to "`<X as Trait>::Proj`" very early on and it's the *expansion* that gets checked for well-formedness, not the type alias reference.
This commit is contained in:
Trevor Gross 2024-07-26 02:20:28 -04:00 committed by GitHub
commit ceae37188b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 829 additions and 456 deletions

View File

@ -1,4 +1,4 @@
hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$ty_param_name}` hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$qself}`
.label = ambiguous associated {$assoc_kind} `{$assoc_name}` .label = ambiguous associated {$assoc_kind} `{$assoc_name}`
hir_analysis_ambiguous_lifetime_bound = hir_analysis_ambiguous_lifetime_bound =
@ -12,16 +12,21 @@ hir_analysis_assoc_item_is_private = {$kind} `{$name}` is private
.label = private {$kind} .label = private {$kind}
.defined_here_label = the {$kind} is defined here .defined_here_label = the {$kind} is defined here
hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$ty_param_name}` hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$qself}`
hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identically_named -> hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identically_named ->
[true] an [true] an
*[false] a similarly named *[false] a similarly named
} associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}` } associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}`
hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_name}` not found hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_name}` not found
hir_analysis_assoc_item_not_found_other_sugg = `{$ty_param_name}` has the following associated {$assoc_kind} hir_analysis_assoc_item_not_found_other_sugg = `{$qself}` has the following associated {$assoc_kind}
hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg =
consider fully qualifying{$identically_named ->
[true] {""}
*[false] {" "}and renaming
} the associated {$assoc_kind}
hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg = change the associated {$assoc_kind} name to use `{$suggested_name}` from `{$trait_name}` hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg = change the associated {$assoc_kind} name to use `{$suggested_name}` from `{$trait_name}`
hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = and also change the associated {$assoc_kind} name hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = ...and changing the associated {$assoc_kind} name
hir_analysis_assoc_item_not_found_similar_sugg = there is an associated {$assoc_kind} with a similar name hir_analysis_assoc_item_not_found_similar_sugg = there is an associated {$assoc_kind} with a similar name
hir_analysis_assoc_kind_mismatch = expected {$expected}, found {$got} hir_analysis_assoc_kind_mismatch = expected {$expected}, found {$got}

View File

@ -22,7 +22,7 @@ pub struct AmbiguousAssocItem<'a> {
pub span: Span, pub span: Span,
pub assoc_kind: &'static str, pub assoc_kind: &'static str,
pub assoc_name: Ident, pub assoc_name: Ident,
pub ty_param_name: &'a str, pub qself: &'a str,
} }
#[derive(Diagnostic)] #[derive(Diagnostic)]
@ -75,7 +75,7 @@ pub struct AssocItemNotFound<'a> {
pub span: Span, pub span: Span,
pub assoc_name: Ident, pub assoc_name: Ident,
pub assoc_kind: &'static str, pub assoc_kind: &'static str,
pub ty_param_name: &'a str, pub qself: &'a str,
#[subdiagnostic] #[subdiagnostic]
pub label: Option<AssocItemNotFoundLabel<'a>>, pub label: Option<AssocItemNotFoundLabel<'a>>,
#[subdiagnostic] #[subdiagnostic]
@ -126,13 +126,32 @@ pub enum AssocItemNotFoundSugg<'a> {
assoc_kind: &'static str, assoc_kind: &'static str,
suggested_name: Symbol, suggested_name: Symbol,
}, },
#[suggestion(hir_analysis_assoc_item_not_found_other_sugg, code = "{suggested_name}")] #[multipart_suggestion(
hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg,
style = "verbose"
)]
SimilarInOtherTraitQPath {
#[suggestion_part(code = "<")]
lo: Span,
#[suggestion_part(code = " as {trait_ref}>")]
mi: Span,
#[suggestion_part(code = "{suggested_name}")]
hi: Option<Span>,
trait_ref: String,
suggested_name: Symbol,
identically_named: bool,
#[applicability]
applicability: Applicability,
},
#[suggestion(
hir_analysis_assoc_item_not_found_other_sugg,
code = "{suggested_name}",
applicability = "maybe-incorrect"
)]
Other { Other {
#[primary_span] #[primary_span]
span: Span, span: Span,
#[applicability] qself: &'a str,
applicability: Applicability,
ty_param_name: &'a str,
assoc_kind: &'static str, assoc_kind: &'static str,
suggested_name: Symbol, suggested_name: Symbol,
}, },

View File

@ -6,7 +6,6 @@ use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::ty::print::PrintTraitRefExt as _;
use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt}; use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt};
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
use rustc_span::{ErrorGuaranteed, Span, Symbol}; use rustc_span::{ErrorGuaranteed, Span, Symbol};
@ -16,9 +15,8 @@ use smallvec::SmallVec;
use crate::bounds::Bounds; use crate::bounds::Bounds;
use crate::errors; use crate::errors;
use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter}; use crate::hir_ty_lowering::HirTyLowerer;
use crate::hir_ty_lowering::{AssocItemQSelf, OnlySelfBounds, PredicateFilter, RegionInferReason};
use super::RegionInferReason;
impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
/// Add a `Sized` bound to the `bounds` if appropriate. /// Add a `Sized` bound to the `bounds` if appropriate.
@ -288,8 +286,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// one that does define it. // one that does define it.
self.probe_single_bound_for_assoc_item( self.probe_single_bound_for_assoc_item(
|| traits::supertraits(tcx, trait_ref), || traits::supertraits(tcx, trait_ref),
trait_ref.skip_binder().print_only_trait_name(), AssocItemQSelf::Trait(trait_ref.def_id()),
None,
assoc_kind, assoc_kind,
constraint.ident, constraint.ident,
path_span, path_span,

View File

@ -3,7 +3,7 @@ use crate::errors::{
ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits, ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits,
}; };
use crate::fluent_generated as fluent; use crate::fluent_generated as fluent;
use crate::hir_ty_lowering::HirTyLowerer; use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer};
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::sorted_map::SortedMap;
use rustc_data_structures::unord::UnordMap; use rustc_data_structures::unord::UnordMap;
@ -11,9 +11,9 @@ use rustc_errors::MultiSpan;
use rustc_errors::{ use rustc_errors::{
codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed,
}; };
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::DefId;
use rustc_hir::{self as hir, Node};
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::query::Key; use rustc_middle::query::Key;
use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _}; use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _};
@ -116,8 +116,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
pub(super) fn complain_about_assoc_item_not_found<I>( pub(super) fn complain_about_assoc_item_not_found<I>(
&self, &self,
all_candidates: impl Fn() -> I, all_candidates: impl Fn() -> I,
ty_param_name: &str, qself: AssocItemQSelf,
ty_param_def_id: Option<LocalDefId>,
assoc_kind: ty::AssocKind, assoc_kind: ty::AssocKind,
assoc_name: Ident, assoc_name: Ident,
span: Span, span: Span,
@ -139,7 +138,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
); );
} }
let assoc_kind_str = super::assoc_kind_str(assoc_kind); let assoc_kind_str = assoc_kind_str(assoc_kind);
let qself_str = qself.to_string(tcx);
// The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a
// valid span, so we point at the whole path segment instead. // valid span, so we point at the whole path segment instead.
@ -149,7 +149,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
span: if is_dummy { span } else { assoc_name.span }, span: if is_dummy { span } else { assoc_name.span },
assoc_name, assoc_name,
assoc_kind: assoc_kind_str, assoc_kind: assoc_kind_str,
ty_param_name, qself: &qself_str,
label: None, label: None,
sugg: None, sugg: None,
}; };
@ -219,19 +219,28 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
suggested_name, suggested_name,
identically_named: suggested_name == assoc_name.name, identically_named: suggested_name == assoc_name.name,
}); });
let hir = tcx.hir(); if let AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span) = qself
if let Some(def_id) = ty_param_def_id // Not using `self.item_def_id()` here as that would yield the opaque type itself if we're
&& let parent = hir.get_parent_item(tcx.local_def_id_to_hir_id(def_id)) // inside an opaque type while we're interested in the overarching type alias (TAIT).
&& let Some(generics) = hir.get_generics(parent.def_id) // FIXME: However, for trait aliases, this incorrectly returns the enclosing module...
&& let item_def_id =
tcx.hir().get_parent_item(tcx.local_def_id_to_hir_id(ty_param_def_id))
// FIXME: ...which obviously won't have any generics.
&& let Some(generics) = tcx.hir().get_generics(item_def_id.def_id)
{ {
if generics.bounds_for_param(def_id).flat_map(|pred| pred.bounds.iter()).any( // FIXME: Suggest adding supertrait bounds if we have a `Self` type param.
|b| match b { // FIXME(trait_alias): Suggest adding `Self: Trait` to
// `trait Alias = where Self::Proj:;` with `trait Trait { type Proj; }`.
if generics
.bounds_for_param(ty_param_def_id)
.flat_map(|pred| pred.bounds.iter())
.any(|b| match b {
hir::GenericBound::Trait(t, ..) => { hir::GenericBound::Trait(t, ..) => {
t.trait_ref.trait_def_id() == Some(best_trait) t.trait_ref.trait_def_id() == Some(best_trait)
} }
_ => false, _ => false,
}, })
) { {
// The type param already has a bound for `trait_name`, we just need to // The type param already has a bound for `trait_name`, we just need to
// change the associated item. // change the associated item.
err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait { err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait {
@ -242,48 +251,60 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
return self.dcx().emit_err(err); return self.dcx().emit_err(err);
} }
let mut err = self.dcx().create_err(err); let trait_args = &ty::GenericArgs::identity_for_item(tcx, best_trait)[1..];
if suggest_constraining_type_param( let mut trait_ref = trait_name.clone();
tcx, let applicability = if let [arg, args @ ..] = trait_args {
generics, use std::fmt::Write;
&mut err, write!(trait_ref, "</* {arg}").unwrap();
&ty_param_name, args.iter().try_for_each(|arg| write!(trait_ref, ", {arg}")).unwrap();
&trait_name, trait_ref += " */>";
None, Applicability::HasPlaceholders
None, } else {
) && suggested_name != assoc_name.name Applicability::MaybeIncorrect
};
let identically_named = suggested_name == assoc_name.name;
if let DefKind::TyAlias = tcx.def_kind(item_def_id)
&& !tcx.type_alias_is_lazy(item_def_id)
{ {
// We suggested constraining a type parameter, but the associated item on it err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTraitQPath {
// was also not an exact match, so we also suggest changing it. lo: ty_param_span.shrink_to_lo(),
err.span_suggestion_verbose( mi: ty_param_span.shrink_to_hi(),
assoc_name.span, hi: (!identically_named).then_some(assoc_name.span),
fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg, trait_ref,
identically_named,
suggested_name, suggested_name,
Applicability::MaybeIncorrect, applicability,
); });
} else {
let mut err = self.dcx().create_err(err);
if suggest_constraining_type_param(
tcx, generics, &mut err, &qself_str, &trait_ref, None, None,
) && !identically_named
{
// We suggested constraining a type parameter, but the associated item on it
// was also not an exact match, so we also suggest changing it.
err.span_suggestion_verbose(
assoc_name.span,
fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg,
suggested_name,
Applicability::MaybeIncorrect,
);
}
return err.emit();
} }
return err.emit();
} }
return self.dcx().emit_err(err); return self.dcx().emit_err(err);
} }
} }
// If we still couldn't find any associated item, and only one associated item exists, // If we still couldn't find any associated item, and only one associated item exists,
// suggests using it. // suggest using it.
if let [candidate_name] = all_candidate_names.as_slice() { if let [candidate_name] = all_candidate_names.as_slice() {
// This should still compile, except on `#![feature(associated_type_defaults)]`
// where it could suggests `type A = Self::A`, thus recursing infinitely.
let applicability =
if assoc_kind == ty::AssocKind::Type && tcx.features().associated_type_defaults {
Applicability::Unspecified
} else {
Applicability::MaybeIncorrect
};
err.sugg = Some(errors::AssocItemNotFoundSugg::Other { err.sugg = Some(errors::AssocItemNotFoundSugg::Other {
span: assoc_name.span, span: assoc_name.span,
applicability, qself: &qself_str,
ty_param_name,
assoc_kind: assoc_kind_str, assoc_kind: assoc_kind_str,
suggested_name: *candidate_name, suggested_name: *candidate_name,
}); });
@ -349,10 +370,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
self.dcx().emit_err(errors::AssocKindMismatch { self.dcx().emit_err(errors::AssocKindMismatch {
span, span,
expected: super::assoc_kind_str(expected), expected: assoc_kind_str(expected),
got: super::assoc_kind_str(got), got: assoc_kind_str(got),
expected_because_label, expected_because_label,
assoc_kind: super::assoc_kind_str(assoc_item.kind), assoc_kind: assoc_kind_str(assoc_item.kind),
def_span: tcx.def_span(assoc_item.def_id), def_span: tcx.def_span(assoc_item.def_id),
bound_on_assoc_const_label, bound_on_assoc_const_label,
wrap_in_braces_sugg, wrap_in_braces_sugg,
@ -746,7 +767,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) { if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) {
let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.trait_ref.hir_ref_id)); let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.trait_ref.hir_ref_id));
in_expr_or_pat = match grandparent { in_expr_or_pat = match grandparent {
Node::Expr(_) | Node::Pat(_) => true, hir::Node::Expr(_) | hir::Node::Pat(_) => true,
_ => false, _ => false,
}; };
match bound.trait_ref.path.segments { match bound.trait_ref.path.segments {
@ -1612,3 +1633,11 @@ fn generics_args_err_extend<'a>(
_ => {} _ => {}
} }
} }
pub(super) fn assoc_kind_str(kind: ty::AssocKind) -> &'static str {
match kind {
ty::AssocKind::Fn => "function",
ty::AssocKind::Const => "constant",
ty::AssocKind::Type => "type",
}
}

View File

@ -55,7 +55,6 @@ use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::wf::object_region_bounds; use rustc_trait_selection::traits::wf::object_region_bounds;
use rustc_trait_selection::traits::{self, ObligationCtxt}; use rustc_trait_selection::traits::{self, ObligationCtxt};
use std::fmt::Display;
use std::slice; use std::slice;
/// A path segment that is semantically allowed to have generic arguments. /// A path segment that is semantically allowed to have generic arguments.
@ -193,6 +192,25 @@ pub trait HirTyLowerer<'tcx> {
} }
} }
/// The "qualified self" of an associated item path.
///
/// For diagnostic purposes only.
enum AssocItemQSelf {
Trait(DefId),
TyParam(LocalDefId, Span),
SelfTyAlias,
}
impl AssocItemQSelf {
fn to_string(&self, tcx: TyCtxt<'_>) -> String {
match *self {
Self::Trait(def_id) => tcx.def_path_str(def_id),
Self::TyParam(def_id, _) => tcx.hir().ty_param_name(def_id).to_string(),
Self::SelfTyAlias => kw::SelfUpper.to_string(),
}
}
}
/// New-typed boolean indicating whether explicit late-bound lifetimes /// New-typed boolean indicating whether explicit late-bound lifetimes
/// are present in a set of generic arguments. /// are present in a set of generic arguments.
/// ///
@ -802,6 +820,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
fn probe_single_ty_param_bound_for_assoc_ty( fn probe_single_ty_param_bound_for_assoc_ty(
&self, &self,
ty_param_def_id: LocalDefId, ty_param_def_id: LocalDefId,
ty_param_span: Span,
assoc_name: Ident, assoc_name: Ident,
span: Span, span: Span,
) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> { ) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> {
@ -811,19 +830,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let predicates = &self.probe_ty_param_bounds(span, ty_param_def_id, assoc_name).predicates; let predicates = &self.probe_ty_param_bounds(span, ty_param_def_id, assoc_name).predicates;
debug!("predicates={:#?}", predicates); debug!("predicates={:#?}", predicates);
let param_name = tcx.hir().ty_param_name(ty_param_def_id);
self.probe_single_bound_for_assoc_item( self.probe_single_bound_for_assoc_item(
|| { || {
traits::transitive_bounds_that_define_assoc_item( let trait_refs = predicates
tcx, .iter()
predicates .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref)));
.iter() traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_name)
.filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref))),
assoc_name,
)
}, },
param_name, AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span),
Some(ty_param_def_id),
ty::AssocKind::Type, ty::AssocKind::Type,
assoc_name, assoc_name,
span, span,
@ -835,12 +849,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
/// ///
/// This fails if there is no such bound in the list of candidates or if there are multiple /// This fails if there is no such bound in the list of candidates or if there are multiple
/// candidates in which case it reports ambiguity. /// candidates in which case it reports ambiguity.
#[instrument(level = "debug", skip(self, all_candidates, ty_param_name, constraint), ret)] #[instrument(level = "debug", skip(self, all_candidates, qself, constraint), ret)]
fn probe_single_bound_for_assoc_item<I>( fn probe_single_bound_for_assoc_item<I>(
&self, &self,
all_candidates: impl Fn() -> I, all_candidates: impl Fn() -> I,
ty_param_name: impl Display, qself: AssocItemQSelf,
ty_param_def_id: Option<LocalDefId>,
assoc_kind: ty::AssocKind, assoc_kind: ty::AssocKind,
assoc_name: Ident, assoc_name: Ident,
span: Span, span: Span,
@ -858,8 +871,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let Some(bound) = matching_candidates.next() else { let Some(bound) = matching_candidates.next() else {
let reported = self.complain_about_assoc_item_not_found( let reported = self.complain_about_assoc_item_not_found(
all_candidates, all_candidates,
&ty_param_name.to_string(), qself,
ty_param_def_id,
assoc_kind, assoc_kind,
assoc_name, assoc_name,
span, span,
@ -872,13 +884,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
if let Some(bound2) = matching_candidates.next() { if let Some(bound2) = matching_candidates.next() {
debug!(?bound2); debug!(?bound2);
let assoc_kind_str = assoc_kind_str(assoc_kind); let assoc_kind_str = errors::assoc_kind_str(assoc_kind);
let ty_param_name = &ty_param_name.to_string(); let qself_str = qself.to_string(tcx);
let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem { let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem {
span, span,
assoc_kind: assoc_kind_str, assoc_kind: assoc_kind_str,
assoc_name, assoc_name,
ty_param_name, qself: &qself_str,
}); });
// Provide a more specific error code index entry for equality bindings. // Provide a more specific error code index entry for equality bindings.
err.code( err.code(
@ -929,7 +941,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
err.span_suggestion_verbose( err.span_suggestion_verbose(
span.with_hi(assoc_name.span.lo()), span.with_hi(assoc_name.span.lo()),
"use fully-qualified syntax to disambiguate", "use fully-qualified syntax to disambiguate",
format!("<{ty_param_name} as {}>::", bound.print_only_trait_path()), format!("<{qself_str} as {}>::", bound.print_only_trait_path()),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
} }
@ -943,7 +955,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
if !where_bounds.is_empty() { if !where_bounds.is_empty() {
err.help(format!( err.help(format!(
"consider introducing a new type parameter `T` and adding `where` constraints:\ "consider introducing a new type parameter `T` and adding `where` constraints:\
\n where\n T: {ty_param_name},\n{}", \n where\n T: {qself_str},\n{}",
where_bounds.join(",\n"), where_bounds.join(",\n"),
)); ));
} }
@ -997,11 +1009,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let tcx = self.tcx(); let tcx = self.tcx();
let assoc_ident = assoc_segment.ident; let assoc_ident = assoc_segment.ident;
let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind {
path.res
} else {
Res::Err
};
// Check if we have an enum variant or an inherent associated type. // Check if we have an enum variant or an inherent associated type.
let mut variant_resolution = None; let mut variant_resolution = None;
@ -1038,6 +1045,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
} }
} }
let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind {
path.res
} else {
Res::Err
};
// Find the type of the associated item, and the trait where the associated // Find the type of the associated item, and the trait where the associated
// item is declared. // item is declared.
let bound = match (&qself_ty.kind(), qself_res) { let bound = match (&qself_ty.kind(), qself_res) {
@ -1056,8 +1069,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
ty::Binder::dummy(trait_ref.instantiate_identity()), ty::Binder::dummy(trait_ref.instantiate_identity()),
) )
}, },
kw::SelfUpper, AssocItemQSelf::SelfTyAlias,
None,
ty::AssocKind::Type, ty::AssocKind::Type,
assoc_ident, assoc_ident,
span, span,
@ -1069,6 +1081,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did), Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did),
) => self.probe_single_ty_param_bound_for_assoc_ty( ) => self.probe_single_ty_param_bound_for_assoc_ty(
param_did.expect_local(), param_did.expect_local(),
qself.span,
assoc_ident, assoc_ident,
span, span,
)?, )?,
@ -2522,11 +2535,3 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
Some(r) Some(r)
} }
} }
fn assoc_kind_str(kind: ty::AssocKind) -> &'static str {
match kind {
ty::AssocKind::Fn => "function",
ty::AssocKind::Const => "constant",
ty::AssocKind::Type => "type",
}
}

View File

@ -144,13 +144,18 @@ lint_builtin_special_module_name_used_main = found module declaration for main.r
lint_builtin_trivial_bounds = {$predicate_kind_name} bound {$predicate} does not depend on any type or lifetime parameters lint_builtin_trivial_bounds = {$predicate_kind_name} bound {$predicate} does not depend on any type or lifetime parameters
lint_builtin_type_alias_bounds_help = use fully disambiguated paths (i.e., `<T as Trait>::Assoc`) to refer to associated types in type aliases lint_builtin_type_alias_bounds_enable_feat_help = add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
lint_builtin_type_alias_bounds_label = will not be checked at usage sites of the type alias
lint_builtin_type_alias_generic_bounds = bounds on generic parameters are not enforced in type aliases lint_builtin_type_alias_bounds_limitation_note = this is a known limitation of the type checker that may be lifted in a future edition.
.suggestion = the bound will not be checked when the type alias is used, and should be removed see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
lint_builtin_type_alias_bounds_param_bounds = bounds on generic parameters in type aliases are not enforced
lint_builtin_type_alias_where_clause = where clauses are not enforced in type aliases .suggestion = remove {$count ->
.suggestion = the clause will not be checked when the type alias is used, and should be removed [one] this bound
*[other] these bounds
}
lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg = fully qualify this associated type
lint_builtin_type_alias_bounds_where_clause = where clauses on type aliases are not enforced
.suggestion = remove this where clause
lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed
lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done

View File

@ -31,12 +31,11 @@ use crate::{
BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents, BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents,
BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,
BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns,
BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds, BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds,
BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause,
BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit,
BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe,
BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
BuiltinWhileTrue, InvalidAsmLabel, SuggestChangingAssocTypes, BuiltinWhileTrue, InvalidAsmLabel,
}, },
EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext, EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext,
}; };
@ -1391,64 +1390,80 @@ declare_lint! {
/// ///
/// ### Explanation /// ### Explanation
/// ///
/// The trait bounds in a type alias are currently ignored, and should not /// Trait and lifetime bounds on generic parameters and in where clauses of
/// be included to avoid confusion. This was previously allowed /// type aliases are not checked at usage sites of the type alias. Moreover,
/// unintentionally; this may become a hard error in the future. /// they are not thoroughly checked for correctness at their definition site
/// either similar to the aliased type.
///
/// This is a known limitation of the type checker that may be lifted in a
/// future edition. Permitting such bounds in light of this was unintentional.
///
/// While these bounds may have secondary effects such as enabling the use of
/// "shorthand" associated type paths[^1] and affecting the default trait
/// object lifetime[^2] of trait object types passed to the type alias, this
/// should not have been allowed until the aforementioned restrictions of the
/// type checker have been lifted.
///
/// Using such bounds is highly discouraged as they are actively misleading.
///
/// [^1]: I.e., paths of the form `T::Assoc` where `T` is a type parameter
/// bounded by trait `Trait` which defines an associated type called `Assoc`
/// as opposed to a fully qualified path of the form `<T as Trait>::Assoc`.
/// [^2]: <https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes>
TYPE_ALIAS_BOUNDS, TYPE_ALIAS_BOUNDS,
Warn, Warn,
"bounds in type aliases are not enforced" "bounds in type aliases are not enforced"
} }
declare_lint_pass!( declare_lint_pass!(TypeAliasBounds => [TYPE_ALIAS_BOUNDS]);
/// Lint for trait and lifetime bounds in type aliases being mostly ignored.
/// They are relevant when using associated types, but otherwise neither checked
/// at definition site nor enforced at use site.
TypeAliasBounds => [TYPE_ALIAS_BOUNDS]
);
impl TypeAliasBounds { impl TypeAliasBounds {
pub(crate) fn is_type_variable_assoc(qpath: &hir::QPath<'_>) -> bool { pub(crate) fn affects_object_lifetime_defaults(pred: &hir::WherePredicate<'_>) -> bool {
match *qpath { // Bounds of the form `T: 'a` with `T` type param affect object lifetime defaults.
hir::QPath::TypeRelative(ty, _) => { if let hir::WherePredicate::BoundPredicate(pred) = pred
// If this is a type variable, we found a `T::Assoc`. && pred.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Outlives(_)))
match ty.kind { && pred.bound_generic_params.is_empty() // indeed, even if absent from the RHS
hir::TyKind::Path(hir::QPath::Resolved(None, path)) => { && pred.bounded_ty.as_generic_param().is_some()
matches!(path.res, Res::Def(DefKind::TyParam, _)) {
} return true;
_ => false,
}
}
hir::QPath::Resolved(..) | hir::QPath::LangItem(..) => false,
} }
false
} }
} }
impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
let hir::ItemKind::TyAlias(hir_ty, type_alias_generics) = &item.kind else { return }; let hir::ItemKind::TyAlias(hir_ty, generics) = item.kind else { return };
// There must not be a where clause.
if generics.predicates.is_empty() {
return;
}
// Bounds of lazy type aliases and TAITs are respected. // Bounds of lazy type aliases and TAITs are respected.
if cx.tcx.type_alias_is_lazy(item.owner_id) { if cx.tcx.type_alias_is_lazy(item.owner_id) {
return; return;
} }
let ty = cx.tcx.type_of(item.owner_id).skip_binder(); // FIXME(generic_const_exprs): Revisit this before stabilization.
if ty.has_inherent_projections() { // See also `tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs`.
// Bounds of type aliases that contain opaque types or inherent projections are let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
// respected. E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`, `type X = if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION)
// Type::Inherent;`. && cx.tcx.features().generic_const_exprs
{
return; return;
} }
// There must not be a where clause // NOTE(inherent_associated_types): While we currently do take some bounds in type
if type_alias_generics.predicates.is_empty() { // aliases into consideration during IAT *selection*, we don't perform full use+def
return; // site wfchecking for such type aliases. Therefore TAB should still trigger.
} // See also `tests/ui/associated-inherent-types/type-alias-bounds.rs`.
let mut where_spans = Vec::new(); let mut where_spans = Vec::new();
let mut inline_spans = Vec::new(); let mut inline_spans = Vec::new();
let mut inline_sugg = Vec::new(); let mut inline_sugg = Vec::new();
for p in type_alias_generics.predicates {
for p in generics.predicates {
let span = p.span(); let span = p.span();
if p.in_where_clause() { if p.in_where_clause() {
where_spans.push(span); where_spans.push(span);
@ -1460,37 +1475,57 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
} }
} }
let mut suggested_changing_assoc_types = false; let mut ty = Some(hir_ty);
if !where_spans.is_empty() { let enable_feat_help = cx.tcx.sess.is_nightly_build();
let sub = (!suggested_changing_assoc_types).then(|| {
suggested_changing_assoc_types = true; if let [.., label_sp] = *where_spans {
SuggestChangingAssocTypes { ty: hir_ty }
});
cx.emit_span_lint( cx.emit_span_lint(
TYPE_ALIAS_BOUNDS, TYPE_ALIAS_BOUNDS,
where_spans, where_spans,
BuiltinTypeAliasWhereClause { BuiltinTypeAliasBounds {
suggestion: type_alias_generics.where_clause_span, in_where_clause: true,
sub, label: label_sp,
enable_feat_help,
suggestions: vec![(generics.where_clause_span, String::new())],
preds: generics.predicates,
ty: ty.take(),
}, },
); );
} }
if let [.., label_sp] = *inline_spans {
if !inline_spans.is_empty() {
let suggestion = BuiltinTypeAliasGenericBoundsSuggestion { suggestions: inline_sugg };
let sub = (!suggested_changing_assoc_types).then(|| {
suggested_changing_assoc_types = true;
SuggestChangingAssocTypes { ty: hir_ty }
});
cx.emit_span_lint( cx.emit_span_lint(
TYPE_ALIAS_BOUNDS, TYPE_ALIAS_BOUNDS,
inline_spans, inline_spans,
BuiltinTypeAliasGenericBounds { suggestion, sub }, BuiltinTypeAliasBounds {
in_where_clause: false,
label: label_sp,
enable_feat_help,
suggestions: inline_sugg,
preds: generics.predicates,
ty,
},
); );
} }
} }
} }
pub(crate) struct ShorthandAssocTyCollector {
pub(crate) qselves: Vec<Span>,
}
impl hir::intravisit::Visitor<'_> for ShorthandAssocTyCollector {
fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, _: Span) {
// Look for "type-parameter shorthand-associated-types". I.e., paths of the
// form `T::Assoc` with `T` type param. These are reliant on trait bounds.
if let hir::QPath::TypeRelative(qself, _) = qpath
&& qself.as_generic_param().is_some()
{
self.qselves.push(qself.span);
}
hir::intravisit::walk_qpath(self, qpath, id)
}
}
declare_lint! { declare_lint! {
/// The `trivial_bounds` lint detects trait bounds that don't depend on /// The `trivial_bounds` lint detects trait bounds that don't depend on
/// any type parameters. /// any type parameters.

View File

@ -2,14 +2,16 @@
#![allow(rustc::untranslatable_diagnostic)] #![allow(rustc::untranslatable_diagnostic)]
use std::num::NonZero; use std::num::NonZero;
use crate::errors::RequestedLevel; use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds};
use crate::errors::{OverruledAttributeSub, RequestedLevel};
use crate::fluent_generated as fluent; use crate::fluent_generated as fluent;
use crate::LateContext;
use rustc_errors::{ use rustc_errors::{
codes::*, Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString, codes::*, Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString,
ElidedLifetimeInPathSubdiag, EmissionGuarantee, LintDiagnostic, MultiSpan, SubdiagMessageOp, ElidedLifetimeInPathSubdiag, EmissionGuarantee, LintDiagnostic, MultiSpan, SubdiagMessageOp,
Subdiagnostic, SuggestionStyle, Subdiagnostic, SuggestionStyle,
}; };
use rustc_hir::{def::Namespace, def_id::DefId}; use rustc_hir::{self as hir, def::Namespace, def_id::DefId};
use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_macros::{LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::{ use rustc_middle::ty::{
inhabitedness::InhabitedPredicate, Clause, PolyExistentialTraitRef, Ty, TyCtxt, inhabitedness::InhabitedPredicate, Clause, PolyExistentialTraitRef, Ty, TyCtxt,
@ -22,10 +24,6 @@ use rustc_span::{
Span, Symbol, Span, Symbol,
}; };
use crate::{
builtin::InitError, builtin::TypeAliasBounds, errors::OverruledAttributeSub, LateContext,
};
// array_into_iter.rs // array_into_iter.rs
#[derive(LintDiagnostic)] #[derive(LintDiagnostic)]
#[diag(lint_shadowed_into_iter)] #[diag(lint_shadowed_into_iter)]
@ -263,62 +261,6 @@ pub struct BuiltinUnreachablePub<'a> {
pub help: Option<()>, pub help: Option<()>,
} }
pub struct SuggestChangingAssocTypes<'a, 'b> {
pub ty: &'a rustc_hir::Ty<'b>,
}
impl<'a, 'b> Subdiagnostic for SuggestChangingAssocTypes<'a, 'b> {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self,
diag: &mut Diag<'_, G>,
_f: &F,
) {
// Access to associates types should use `<T as Bound>::Assoc`, which does not need a
// bound. Let's see if this type does that.
// We use a HIR visitor to walk the type.
use rustc_hir::intravisit::{self, Visitor};
struct WalkAssocTypes<'a, 'b, G: EmissionGuarantee> {
err: &'a mut Diag<'b, G>,
}
impl<'a, 'b, G: EmissionGuarantee> Visitor<'_> for WalkAssocTypes<'a, 'b, G> {
fn visit_qpath(
&mut self,
qpath: &rustc_hir::QPath<'_>,
id: rustc_hir::HirId,
span: Span,
) {
if TypeAliasBounds::is_type_variable_assoc(qpath) {
self.err.span_help(span, fluent::lint_builtin_type_alias_bounds_help);
}
intravisit::walk_qpath(self, qpath, id)
}
}
// Let's go for a walk!
let mut visitor = WalkAssocTypes { err: diag };
visitor.visit_ty(self.ty);
}
}
#[derive(LintDiagnostic)]
#[diag(lint_builtin_type_alias_where_clause)]
pub struct BuiltinTypeAliasWhereClause<'a, 'b> {
#[suggestion(code = "", applicability = "machine-applicable")]
pub suggestion: Span,
#[subdiagnostic]
pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>,
}
#[derive(LintDiagnostic)]
#[diag(lint_builtin_type_alias_generic_bounds)]
pub struct BuiltinTypeAliasGenericBounds<'a, 'b> {
#[subdiagnostic]
pub suggestion: BuiltinTypeAliasGenericBoundsSuggestion,
#[subdiagnostic]
pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>,
}
#[derive(LintDiagnostic)] #[derive(LintDiagnostic)]
#[diag(lint_macro_expr_fragment_specifier_2024_migration)] #[diag(lint_macro_expr_fragment_specifier_2024_migration)]
pub struct MacroExprFragment2024 { pub struct MacroExprFragment2024 {
@ -326,21 +268,72 @@ pub struct MacroExprFragment2024 {
pub suggestion: Span, pub suggestion: Span,
} }
pub struct BuiltinTypeAliasGenericBoundsSuggestion { pub struct BuiltinTypeAliasBounds<'a, 'hir> {
pub in_where_clause: bool,
pub label: Span,
pub enable_feat_help: bool,
pub suggestions: Vec<(Span, String)>, pub suggestions: Vec<(Span, String)>,
pub preds: &'hir [hir::WherePredicate<'hir>],
pub ty: Option<&'a hir::Ty<'hir>>,
} }
impl Subdiagnostic for BuiltinTypeAliasGenericBoundsSuggestion { impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_, '_> {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
self, diag.primary_message(if self.in_where_clause {
diag: &mut Diag<'_, G>, fluent::lint_builtin_type_alias_bounds_where_clause
_f: &F, } else {
) { fluent::lint_builtin_type_alias_bounds_param_bounds
diag.multipart_suggestion( });
fluent::lint_suggestion, diag.span_label(self.label, fluent::lint_builtin_type_alias_bounds_label);
self.suggestions, diag.note(fluent::lint_builtin_type_alias_bounds_limitation_note);
Applicability::MachineApplicable, if self.enable_feat_help {
); diag.help(fluent::lint_builtin_type_alias_bounds_enable_feat_help);
}
// We perform the walk in here instead of in `<TypeAliasBounds as LateLintPass>` to
// avoid doing throwaway work in case the lint ends up getting suppressed.
let mut collector = ShorthandAssocTyCollector { qselves: Vec::new() };
if let Some(ty) = self.ty {
hir::intravisit::Visitor::visit_ty(&mut collector, ty);
}
let affect_object_lifetime_defaults = self
.preds
.iter()
.filter(|pred| pred.in_where_clause() == self.in_where_clause)
.any(|pred| TypeAliasBounds::affects_object_lifetime_defaults(pred));
// If there are any shorthand assoc tys, then the bounds can't be removed automatically.
// The user first needs to fully qualify the assoc tys.
let applicability = if !collector.qselves.is_empty() || affect_object_lifetime_defaults {
Applicability::MaybeIncorrect
} else {
Applicability::MachineApplicable
};
diag.arg("count", self.suggestions.len());
diag.multipart_suggestion(fluent::lint_suggestion, self.suggestions, applicability);
// Suggest fully qualifying paths of the form `T::Assoc` with `T` type param via
// `<T as /* Trait */>::Assoc` to remove their reliance on any type param bounds.
//
// Instead of attempting to figure out the necessary trait ref, just use a
// placeholder. Since we don't record type-dependent resolutions for non-body
// items like type aliases, we can't simply deduce the corresp. trait from
// the HIR path alone without rerunning parts of HIR ty lowering here
// (namely `probe_single_ty_param_bound_for_assoc_ty`) which is infeasible.
//
// (We could employ some simple heuristics but that's likely not worth it).
for qself in collector.qselves {
diag.multipart_suggestion(
fluent::lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg,
vec![
(qself.shrink_to_lo(), "<".into()),
(qself.shrink_to_hi(), " as /* Trait */>".into()),
],
Applicability::HasPlaceholders,
);
}
} }
} }

View File

@ -241,10 +241,6 @@ pub trait TypeVisitableExt<I: Interner>: TypeVisitable<I> {
self.has_type_flags(TypeFlags::HAS_ALIAS) self.has_type_flags(TypeFlags::HAS_ALIAS)
} }
fn has_inherent_projections(&self) -> bool {
self.has_type_flags(TypeFlags::HAS_TY_INHERENT)
}
fn has_opaque_types(&self) -> bool { fn has_opaque_types(&self) -> bool {
self.has_type_flags(TypeFlags::HAS_TY_OPAQUE) self.has_type_flags(TypeFlags::HAS_TY_OPAQUE)
} }

View File

@ -1,20 +0,0 @@
//@ compile-flags: --crate-type=lib
//@ check-pass
#![feature(inherent_associated_types)]
#![allow(incomplete_features)]
// Bounds on the self type play a major role in the resolution of inherent associated types (*).
// As a result of that, if a type alias contains any then its bounds have to be respected and the
// lint `type_alias_bounds` should not fire.
#![deny(type_alias_bounds)]
pub type Alias<T: Bound> = (Source<T>::Assoc,);
pub struct Source<T>(T);
pub trait Bound {}
impl<T: Bound> Source<T> {
pub type Assoc = ();
}

View File

@ -0,0 +1,29 @@
//@ compile-flags: --crate-type=lib
//@ check-pass
#![feature(inherent_associated_types)]
#![allow(incomplete_features)]
// FIXME(inherent_associated_types):
// While we currently do take some clauses of the ParamEnv into consideration
// when performing IAT selection, we do not perform full well-formedness checking
// for (eager) type alias definition and usage sites.
//
// Therefore it's *correct* for lint `type_alias_bounds` to fire here despite the
// fact that removing `Bound` from `T` in `Alias` would lead to an error!
//
// Obviously, the present situation isn't ideal and we should fix it in one way
// or another. Either we somehow delay IAT selection until after HIR ty lowering
// to avoid the need to specify any bounds inside (eager) type aliases or we
// force the overarching type alias to be *lazy* (similar to TAITs) which would
// automatically lead to full wfchecking and lint TAB getting suppressed.
pub type Alias<T: Bound> = (Source<T>::Assoc,);
//~^ WARN bounds on generic parameters in type aliases are not enforced
pub struct Source<T>(T);
pub trait Bound {}
impl<T: Bound> Source<T> {
pub type Assoc = ();
}

View File

@ -0,0 +1,16 @@
warning: bounds on generic parameters in type aliases are not enforced
--> $DIR/type-alias-bounds.rs:21:19
|
LL | pub type Alias<T: Bound> = (Source<T>::Assoc,);
| --^^^^^
| | |
| | will not be checked at usage sites of the type alias
| help: remove this bound
|
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
= note: `#[warn(type_alias_bounds)]` on by default
warning: 1 warning emitted

View File

@ -1,147 +1,159 @@
warning: where clauses are not enforced in type aliases warning: where clauses on type aliases are not enforced
--> $DIR/type-alias.rs:3:25 --> $DIR/type-alias.rs:3:25
| |
LL | type _TaWhere1<T> where T: Iterator<Item: Copy> = T; LL | type _TaWhere1<T> where T: Iterator<Item: Copy> = T;
| ^^^^^^^^^^^^^^^^^^^^^^^ | ------^^^^^^^^^^^^^^^^^^^^^^^
| | |
| | will not be checked at usage sites of the type alias
| help: remove this where clause
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
= note: `#[warn(type_alias_bounds)]` on by default = note: `#[warn(type_alias_bounds)]` on by default
help: the clause will not be checked when the type alias is used, and should be removed
|
LL - type _TaWhere1<T> where T: Iterator<Item: Copy> = T;
LL + type _TaWhere1<T> = T;
|
warning: where clauses are not enforced in type aliases warning: where clauses on type aliases are not enforced
--> $DIR/type-alias.rs:4:25 --> $DIR/type-alias.rs:4:25
| |
LL | type _TaWhere2<T> where T: Iterator<Item: 'static> = T; LL | type _TaWhere2<T> where T: Iterator<Item: 'static> = T;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ------^^^^^^^^^^^^^^^^^^^^^^^^^^
| | | |
help: the clause will not be checked when the type alias is used, and should be removed | | will not be checked at usage sites of the type alias
| | help: remove this where clause
LL - type _TaWhere2<T> where T: Iterator<Item: 'static> = T;
LL + type _TaWhere2<T> = T;
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
warning: where clauses are not enforced in type aliases warning: where clauses on type aliases are not enforced
--> $DIR/type-alias.rs:5:25 --> $DIR/type-alias.rs:5:25
| |
LL | type _TaWhere3<T> where T: Iterator<Item: 'static> = T; LL | type _TaWhere3<T> where T: Iterator<Item: 'static> = T;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ------^^^^^^^^^^^^^^^^^^^^^^^^^^
| | | |
help: the clause will not be checked when the type alias is used, and should be removed | | will not be checked at usage sites of the type alias
| | help: remove this where clause
LL - type _TaWhere3<T> where T: Iterator<Item: 'static> = T;
LL + type _TaWhere3<T> = T;
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
warning: where clauses are not enforced in type aliases warning: where clauses on type aliases are not enforced
--> $DIR/type-alias.rs:6:25 --> $DIR/type-alias.rs:6:25
| |
LL | type _TaWhere4<T> where T: Iterator<Item: 'static + Copy + Send> = T; LL | type _TaWhere4<T> where T: Iterator<Item: 'static + Copy + Send> = T;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| | | |
help: the clause will not be checked when the type alias is used, and should be removed | | will not be checked at usage sites of the type alias
| | help: remove this where clause
LL - type _TaWhere4<T> where T: Iterator<Item: 'static + Copy + Send> = T;
LL + type _TaWhere4<T> = T;
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
warning: where clauses are not enforced in type aliases warning: where clauses on type aliases are not enforced
--> $DIR/type-alias.rs:7:25 --> $DIR/type-alias.rs:7:25
| |
LL | type _TaWhere5<T> where T: Iterator<Item: for<'a> Into<&'a u8>> = T; LL | type _TaWhere5<T> where T: Iterator<Item: for<'a> Into<&'a u8>> = T;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| | | |
help: the clause will not be checked when the type alias is used, and should be removed | | will not be checked at usage sites of the type alias
| | help: remove this where clause
LL - type _TaWhere5<T> where T: Iterator<Item: for<'a> Into<&'a u8>> = T;
LL + type _TaWhere5<T> = T;
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
warning: where clauses are not enforced in type aliases warning: where clauses on type aliases are not enforced
--> $DIR/type-alias.rs:8:25 --> $DIR/type-alias.rs:8:25
| |
LL | type _TaWhere6<T> where T: Iterator<Item: Iterator<Item: Copy>> = T; LL | type _TaWhere6<T> where T: Iterator<Item: Iterator<Item: Copy>> = T;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| | | |
help: the clause will not be checked when the type alias is used, and should be removed | | will not be checked at usage sites of the type alias
| | help: remove this where clause
LL - type _TaWhere6<T> where T: Iterator<Item: Iterator<Item: Copy>> = T;
LL + type _TaWhere6<T> = T;
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
warning: bounds on generic parameters are not enforced in type aliases warning: bounds on generic parameters in type aliases are not enforced
--> $DIR/type-alias.rs:10:20 --> $DIR/type-alias.rs:10:20
| |
LL | type _TaInline1<T: Iterator<Item: Copy>> = T; LL | type _TaInline1<T: Iterator<Item: Copy>> = T;
| ^^^^^^^^^^^^^^^^^^^^ | --^^^^^^^^^^^^^^^^^^^^
| | | |
help: the bound will not be checked when the type alias is used, and should be removed | | will not be checked at usage sites of the type alias
| | help: remove this bound
LL - type _TaInline1<T: Iterator<Item: Copy>> = T;
LL + type _TaInline1<T> = T;
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
warning: bounds on generic parameters are not enforced in type aliases warning: bounds on generic parameters in type aliases are not enforced
--> $DIR/type-alias.rs:11:20 --> $DIR/type-alias.rs:11:20
| |
LL | type _TaInline2<T: Iterator<Item: 'static>> = T; LL | type _TaInline2<T: Iterator<Item: 'static>> = T;
| ^^^^^^^^^^^^^^^^^^^^^^^ | --^^^^^^^^^^^^^^^^^^^^^^^
| | | |
help: the bound will not be checked when the type alias is used, and should be removed | | will not be checked at usage sites of the type alias
| | help: remove this bound
LL - type _TaInline2<T: Iterator<Item: 'static>> = T;
LL + type _TaInline2<T> = T;
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
warning: bounds on generic parameters are not enforced in type aliases warning: bounds on generic parameters in type aliases are not enforced
--> $DIR/type-alias.rs:12:20 --> $DIR/type-alias.rs:12:20
| |
LL | type _TaInline3<T: Iterator<Item: 'static>> = T; LL | type _TaInline3<T: Iterator<Item: 'static>> = T;
| ^^^^^^^^^^^^^^^^^^^^^^^ | --^^^^^^^^^^^^^^^^^^^^^^^
| | | |
help: the bound will not be checked when the type alias is used, and should be removed | | will not be checked at usage sites of the type alias
| | help: remove this bound
LL - type _TaInline3<T: Iterator<Item: 'static>> = T;
LL + type _TaInline3<T> = T;
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
warning: bounds on generic parameters are not enforced in type aliases warning: bounds on generic parameters in type aliases are not enforced
--> $DIR/type-alias.rs:13:20 --> $DIR/type-alias.rs:13:20
| |
LL | type _TaInline4<T: Iterator<Item: 'static + Copy + Send>> = T; LL | type _TaInline4<T: Iterator<Item: 'static + Copy + Send>> = T;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| | | |
help: the bound will not be checked when the type alias is used, and should be removed | | will not be checked at usage sites of the type alias
| | help: remove this bound
LL - type _TaInline4<T: Iterator<Item: 'static + Copy + Send>> = T;
LL + type _TaInline4<T> = T;
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
warning: bounds on generic parameters are not enforced in type aliases warning: bounds on generic parameters in type aliases are not enforced
--> $DIR/type-alias.rs:14:20 --> $DIR/type-alias.rs:14:20
| |
LL | type _TaInline5<T: Iterator<Item: for<'a> Into<&'a u8>>> = T; LL | type _TaInline5<T: Iterator<Item: for<'a> Into<&'a u8>>> = T;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| | | |
help: the bound will not be checked when the type alias is used, and should be removed | | will not be checked at usage sites of the type alias
| | help: remove this bound
LL - type _TaInline5<T: Iterator<Item: for<'a> Into<&'a u8>>> = T;
LL + type _TaInline5<T> = T;
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
warning: bounds on generic parameters are not enforced in type aliases warning: bounds on generic parameters in type aliases are not enforced
--> $DIR/type-alias.rs:15:20 --> $DIR/type-alias.rs:15:20
| |
LL | type _TaInline6<T: Iterator<Item: Iterator<Item: Copy>>> = T; LL | type _TaInline6<T: Iterator<Item: Iterator<Item: Copy>>> = T;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| | | |
help: the bound will not be checked when the type alias is used, and should be removed | | will not be checked at usage sites of the type alias
| | help: remove this bound
LL - type _TaInline6<T: Iterator<Item: Iterator<Item: Copy>>> = T;
LL + type _TaInline6<T> = T;
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
warning: 12 warnings emitted warning: 12 warnings emitted

View File

@ -0,0 +1,63 @@
error[E0277]: the trait bound `String: Copy` is not satisfied
--> $DIR/type-alias-bounds.rs:23:12
|
LL | let _: AliasConstUnused<String>;
| ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
|
note: required by a bound in `ct_unused_0::AliasConstUnused`
--> $DIR/type-alias-bounds.rs:20:30
|
LL | type AliasConstUnused<T: Copy> = (T, I32<{ DATA }>);
| ^^^^ required by this bound in `AliasConstUnused`
error[E0277]: the trait bound `String: Copy` is not satisfied
--> $DIR/type-alias-bounds.rs:31:12
|
LL | let _: AliasConstUnused;
| ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
|
note: required by a bound in `ct_unused_1::AliasConstUnused`
--> $DIR/type-alias-bounds.rs:29:41
|
LL | type AliasConstUnused where String: Copy = I32<{ 0; 0 }>;
| ^^^^ required by this bound in `AliasConstUnused`
error[E0277]: the trait bound `String: Copy` is not satisfied
--> $DIR/type-alias-bounds.rs:39:12
|
LL | let _: AliasFnUnused<String>;
| ^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
|
note: required by a bound in `AliasFnUnused`
--> $DIR/type-alias-bounds.rs:36:27
|
LL | type AliasFnUnused<T: Copy> = (T, I32<{ code() }>);
| ^^^^ required by this bound in `AliasFnUnused`
error[E0277]: the trait bound `String: Copy` is not satisfied
--> $DIR/type-alias-bounds.rs:57:12
|
LL | let _: AliasAssocConstUsed<String>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
|
note: required by a bound in `AliasAssocConstUsed`
--> $DIR/type-alias-bounds.rs:55:41
|
LL | type AliasAssocConstUsed<T: Trait + Copy> = I32<{ T::DATA }>;
| ^^^^ required by this bound in `AliasAssocConstUsed`
error[E0277]: the trait bound `String: Copy` is not satisfied
--> $DIR/type-alias-bounds.rs:65:12
|
LL | let _: AliasFnUsed<String>;
| ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
|
note: required by a bound in `AliasFnUsed`
--> $DIR/type-alias-bounds.rs:62:33
|
LL | type AliasFnUsed<T: Trait + Copy> = I32<{ code::<T>() }>;
| ^^^^ required by this bound in `AliasFnUsed`
error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0277`.

View File

@ -0,0 +1,71 @@
//@ revisions: pos neg
//@[pos] check-pass
#![feature(generic_const_exprs)]
#![feature(trivial_bounds)] // only used in test case `ct_unused_1`
#![allow(incomplete_features)]
// FIXME(generic_const_exprs): Revisit this before stabilization.
// Check that we don't emit the lint `type_alias_bounds` for (eager) type aliases
// whose RHS contains a const projection (aka uneval'ed const).
// Since anon consts inherit the parent generics and predicates and we effectively
// check them before and after instantiaton for well-formedness, the type alias
// bounds are in every sense "enforced".
// Note that the test cases whose name ends in "unused" just demonstrate that this
// holds even if the const projections don't "visibly" capture any generics and/or
// predicates.
#![deny(type_alias_bounds)]
fn ct_unused_0() {
type AliasConstUnused<T: Copy> = (T, I32<{ DATA }>);
const DATA: i32 = 0;
#[cfg(neg)]
let _: AliasConstUnused<String>;
//[neg]~^ ERROR the trait bound `String: Copy` is not satisfied
}
fn ct_unused_1() {
#[allow(trivial_bounds)]
type AliasConstUnused where String: Copy = I32<{ 0; 0 }>;
#[cfg(neg)]
let _: AliasConstUnused;
//[neg]~^ ERROR the trait bound `String: Copy` is not satisfied
}
fn fn_unused() {
type AliasFnUnused<T: Copy> = (T, I32<{ code() }>);
const fn code() -> i32 { 0 }
#[cfg(neg)]
let _: AliasFnUnused<String>;
//[neg]~^ ERROR the trait bound `String: Copy` is not satisfied
}
trait Trait {
type Proj;
const DATA: i32;
}
impl Trait for String {
type Proj = i32;
const DATA: i32 = 0;
}
// Regression test for issue #94398.
fn assoc_ct_used() {
type AliasAssocConstUsed<T: Trait + Copy> = I32<{ T::DATA }>;
#[cfg(neg)]
let _: AliasAssocConstUsed<String>;
//[neg]~^ ERROR the trait bound `String: Copy` is not satisfied
}
fn fn_used() {
type AliasFnUsed<T: Trait + Copy> = I32<{ code::<T>() }>;
const fn code<T: Trait>() -> i32 { T::DATA }
#[cfg(neg)]
let _: AliasFnUsed<String>;
//[neg]~^ ERROR the trait bound `String: Copy` is not satisfied
}
struct I32<const N: i32>;
fn main() {}

View File

@ -39,7 +39,7 @@ mod traits {
pub trait PubTr {} pub trait PubTr {}
pub type Alias<T: PrivTr> = T; //~ ERROR trait `traits::PrivTr` is more private than the item `traits::Alias` pub type Alias<T: PrivTr> = T; //~ ERROR trait `traits::PrivTr` is more private than the item `traits::Alias`
//~^ WARNING bounds on generic parameters are not enforced in type aliases //~^ WARNING bounds on generic parameters in type aliases are not enforced
pub trait Tr1: PrivTr {} //~ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr1` pub trait Tr1: PrivTr {} //~ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr1`
pub trait Tr2<T: PrivTr> {} //~ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr2` pub trait Tr2<T: PrivTr> {} //~ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr2`
pub trait Tr3 { pub trait Tr3 {
@ -58,7 +58,7 @@ mod traits_where {
pub type Alias<T> where T: PrivTr = T; pub type Alias<T> where T: PrivTr = T;
//~^ ERROR trait `traits_where::PrivTr` is more private than the item `traits_where::Alias` //~^ ERROR trait `traits_where::PrivTr` is more private than the item `traits_where::Alias`
//~| WARNING where clauses are not enforced in type aliases //~| WARNING where clauses on type aliases are not enforced
pub trait Tr2<T> where T: PrivTr {} pub trait Tr2<T> where T: PrivTr {}
//~^ ERROR trait `traits_where::PrivTr` is more private than the item `traits_where::Tr2` //~^ ERROR trait `traits_where::PrivTr` is more private than the item `traits_where::Tr2`
pub trait Tr3 { pub trait Tr3 {

View File

@ -395,30 +395,32 @@ note: but type `Priv2` is only usable at visibility `pub(self)`
LL | struct Priv2; LL | struct Priv2;
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
warning: bounds on generic parameters are not enforced in type aliases warning: bounds on generic parameters in type aliases are not enforced
--> $DIR/private-in-public-warn.rs:41:23 --> $DIR/private-in-public-warn.rs:41:23
| |
LL | pub type Alias<T: PrivTr> = T; LL | pub type Alias<T: PrivTr> = T;
| ^^^^^^ | --^^^^^^
| | |
| | will not be checked at usage sites of the type alias
| help: remove this bound
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
= note: `#[warn(type_alias_bounds)]` on by default = note: `#[warn(type_alias_bounds)]` on by default
help: the bound will not be checked when the type alias is used, and should be removed
|
LL - pub type Alias<T: PrivTr> = T;
LL + pub type Alias<T> = T;
|
warning: where clauses are not enforced in type aliases warning: where clauses on type aliases are not enforced
--> $DIR/private-in-public-warn.rs:59:29 --> $DIR/private-in-public-warn.rs:59:29
| |
LL | pub type Alias<T> where T: PrivTr = T; LL | pub type Alias<T> where T: PrivTr = T;
| ^^^^^^^^^ | ------^^^^^^^^^
| | | |
help: the clause will not be checked when the type alias is used, and should be removed | | will not be checked at usage sites of the type alias
| | help: remove this where clause
LL - pub type Alias<T> where T: PrivTr = T;
LL + pub type Alias<T> = T;
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
error: aborting due to 34 previous errors; 2 warnings emitted error: aborting due to 34 previous errors; 2 warnings emitted

View File

@ -19,7 +19,7 @@ help: consider further restricting type parameter `T`
| |
LL | T::Baa: std::fmt::Debug, T: Foo LL | T::Baa: std::fmt::Debug, T: Foo
| ~~~~~~~~ | ~~~~~~~~
help: and also change the associated type name help: ...and changing the associated type name
| |
LL | T::Bar: std::fmt::Debug, LL | T::Bar: std::fmt::Debug,
| ~~~ | ~~~

View File

@ -24,18 +24,19 @@ warning: trait bound i32: Foo does not depend on any type or lifetime parameters
LL | union U where i32: Foo { f: i32 } LL | union U where i32: Foo { f: i32 }
| ^^^ | ^^^
warning: where clauses are not enforced in type aliases warning: where clauses on type aliases are not enforced
--> $DIR/trivial-bounds-inconsistent.rs:22:14 --> $DIR/trivial-bounds-inconsistent.rs:22:14
| |
LL | type Y where i32: Foo = (); LL | type Y where i32: Foo = ();
| ^^^^^^^^ | ------^^^^^^^^
| | |
| | will not be checked at usage sites of the type alias
| help: remove this where clause
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
= note: `#[warn(type_alias_bounds)]` on by default = note: `#[warn(type_alias_bounds)]` on by default
help: the clause will not be checked when the type alias is used, and should be removed
|
LL - type Y where i32: Foo = ();
LL + type Y = ();
|
warning: trait bound i32: Foo does not depend on any type or lifetime parameters warning: trait bound i32: Foo does not depend on any type or lifetime parameters
--> $DIR/trivial-bounds-inconsistent.rs:22:19 --> $DIR/trivial-bounds-inconsistent.rs:22:19

View File

@ -0,0 +1,36 @@
error[E0220]: associated type `Assoc` not found for `T`
--> $DIR/unresolved-assoc-ty-suggest-trait.rs:9:22
|
LL | type AssocOf<T> = T::Assoc;
| ^^^^^ there is an associated type `Assoc` in the trait `Trait`
|
help: consider fully qualifying the associated type
|
LL | type AssocOf<T> = <T as Trait>::Assoc;
| + +++++++++
error[E0220]: associated type `Assok` not found for `T`
--> $DIR/unresolved-assoc-ty-suggest-trait.rs:13:22
|
LL | type AssokOf<T> = T::Assok;
| ^^^^^ there is a similarly named associated type `Assoc` in the trait `Trait`
|
help: consider fully qualifying and renaming the associated type
|
LL | type AssokOf<T> = <T as Trait>::Assoc;
| + +++++++++ ~~~~~
error[E0220]: associated type `Proj` not found for `T`
--> $DIR/unresolved-assoc-ty-suggest-trait.rs:22:21
|
LL | type ProjOf<T> = T::Proj;
| ^^^^ there is an associated type `Proj` in the trait `Parametrized`
|
help: consider fully qualifying the associated type
|
LL | type ProjOf<T> = <T as Parametrized</* 'a, T, N */>>::Proj;
| + ++++++++++++++++++++++++++++++++
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0220`.

View File

@ -0,0 +1,40 @@
error[E0220]: associated type `Assoc` not found for `T`
--> $DIR/unresolved-assoc-ty-suggest-trait.rs:9:22
|
LL | type AssocOf<T> = T::Assoc;
| ^^^^^ there is an associated type `Assoc` in the trait `Trait`
|
help: consider restricting type parameter `T`
|
LL | type AssocOf<T: Trait> = T::Assoc;
| +++++++
error[E0220]: associated type `Assok` not found for `T`
--> $DIR/unresolved-assoc-ty-suggest-trait.rs:13:22
|
LL | type AssokOf<T> = T::Assok;
| ^^^^^ there is a similarly named associated type `Assoc` in the trait `Trait`
|
help: consider restricting type parameter `T`
|
LL | type AssokOf<T: Trait> = T::Assok;
| +++++++
help: ...and changing the associated type name
|
LL | type AssokOf<T> = T::Assoc;
| ~~~~~
error[E0220]: associated type `Proj` not found for `T`
--> $DIR/unresolved-assoc-ty-suggest-trait.rs:22:21
|
LL | type ProjOf<T> = T::Proj;
| ^^^^ there is an associated type `Proj` in the trait `Parametrized`
|
help: consider restricting type parameter `T`
|
LL | type ProjOf<T: Parametrized</* 'a, T, N */>> = T::Proj;
| ++++++++++++++++++++++++++++++
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0220`.

View File

@ -0,0 +1,26 @@
// Ensure that we don't suggest *type alias bounds* for **eager** type aliases.
// issue: rust-lang/rust#125789
//@ revisions: eager lazy
#![cfg_attr(lazy, feature(lazy_type_alias), allow(incomplete_features))]
trait Trait { type Assoc; }
type AssocOf<T> = T::Assoc; //~ ERROR associated type `Assoc` not found for `T`
//[eager]~^ HELP consider fully qualifying the associated type
//[lazy]~| HELP consider restricting type parameter `T`
type AssokOf<T> = T::Assok; //~ ERROR associated type `Assok` not found for `T`
//[eager]~^ HELP consider fully qualifying and renaming the associated type
//[lazy]~| HELP consider restricting type parameter `T`
//[lazy]~| HELP and changing the associated type name
trait Parametrized<'a, T, const N: usize> {
type Proj;
}
type ProjOf<T> = T::Proj; //~ ERROR associated type `Proj` not found for `T`
//[eager]~^ HELP consider fully qualifying the associated type
//[lazy]~| HELP consider restricting type parameter `T`
fn main() {}

View File

@ -3,6 +3,6 @@
//@ check-pass //@ check-pass
pub type T<P: Send + Send + Send> = P; pub type T<P: Send + Send + Send> = P;
//~^ WARN bounds on generic parameters are not enforced in type aliases //~^ WARN bounds on generic parameters in type aliases are not enforced
fn main() {} fn main() {}

View File

@ -1,15 +1,16 @@
warning: bounds on generic parameters are not enforced in type aliases warning: bounds on generic parameters in type aliases are not enforced
--> $DIR/issue-67690-type-alias-bound-diagnostic-crash.rs:5:15 --> $DIR/issue-67690-type-alias-bound-diagnostic-crash.rs:5:15
| |
LL | pub type T<P: Send + Send + Send> = P; LL | pub type T<P: Send + Send + Send> = P;
| ^^^^ ^^^^ ^^^^ | --^^^^---^^^^---^^^^
| | |
| | will not be checked at usage sites of the type alias
| help: remove this bound
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
= note: `#[warn(type_alias_bounds)]` on by default = note: `#[warn(type_alias_bounds)]` on by default
help: the bound will not be checked when the type alias is used, and should be removed
|
LL - pub type T<P: Send + Send + Send> = P;
LL + pub type T<P> = P;
|
warning: 1 warning emitted warning: 1 warning emitted

View File

@ -6,15 +6,15 @@
use std::rc::Rc; use std::rc::Rc;
type SVec<T: Send + Send> = Vec<T>; type SVec<T: Send + Send> = Vec<T>;
//~^ WARN bounds on generic parameters are not enforced in type aliases [type_alias_bounds] //~^ WARN bounds on generic parameters in type aliases are not enforced [type_alias_bounds]
type S2Vec<T> where T: Send = Vec<T>; type S2Vec<T> where T: Send = Vec<T>;
//~^ WARN where clauses are not enforced in type aliases [type_alias_bounds] //~^ WARN where clauses on type aliases are not enforced [type_alias_bounds]
type VVec<'b, 'a: 'b + 'b> = (&'b u32, Vec<&'a i32>); type VVec<'b, 'a: 'b + 'b> = (&'b u32, Vec<&'a i32>);
//~^ WARN bounds on generic parameters are not enforced in type aliases [type_alias_bounds] //~^ WARN bounds on generic parameters in type aliases are not enforced [type_alias_bounds]
type WVec<'b, T: 'b + 'b> = (&'b u32, Vec<T>); type WVec<'b, T: 'b + 'b> = (&'b u32, Vec<T>);
//~^ WARN bounds on generic parameters are not enforced in type aliases [type_alias_bounds] //~^ WARN bounds on generic parameters in type aliases are not enforced [type_alias_bounds]
type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec<T>); type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec<T>);
//~^ WARN where clauses are not enforced in type aliases [type_alias_bounds] //~^ WARN where clauses on type aliases are not enforced [type_alias_bounds]
static STATIC: u32 = 0; static STATIC: u32 = 0;
@ -42,10 +42,11 @@ fn foo<'a>(y: &'a i32) {
struct Sendable<T: Send>(T); struct Sendable<T: Send>(T);
type MySendable<T> = Sendable<T>; // no error here! type MySendable<T> = Sendable<T>; // no error here!
// However, bounds *are* taken into account when accessing associated types // Bounds on type params do enable shorthand type alias paths.
// However, that doesn't actually mean that they are properly enforced.
trait Bound { type Assoc; } trait Bound { type Assoc; }
type T1<U: Bound> = U::Assoc; //~ WARN not enforced in type aliases type T1<U: Bound> = U::Assoc; //~ WARN are not enforced
type T2<U> where U: Bound = U::Assoc; //~ WARN not enforced in type aliases type T2<U> where U: Bound = U::Assoc; //~ WARN are not enforced
// This errors: // This errors:
// `type T3<U> = U::Assoc;` // `type T3<U> = U::Assoc;`
@ -53,7 +54,7 @@ type T2<U> where U: Bound = U::Assoc; //~ WARN not enforced in type aliases
type T4<U> = <U as Bound>::Assoc; type T4<U> = <U as Bound>::Assoc;
// Make sure the help about associated types is not shown incorrectly // Make sure the help about associated types is not shown incorrectly
type T5<U: Bound> = <U as Bound>::Assoc; //~ WARN not enforced in type aliases type T5<U: Bound> = <U as Bound>::Assoc; //~ WARN are not enforced
type T6<U: Bound> = ::std::vec::Vec<U>; //~ WARN not enforced in type aliases type T6<U: Bound> = ::std::vec::Vec<U>; //~ WARN are not enforced
fn main() {} fn main() {}

View File

@ -1,121 +1,132 @@
warning: bounds on generic parameters are not enforced in type aliases warning: bounds on generic parameters in type aliases are not enforced
--> $DIR/type-alias-bounds.rs:8:14 --> $DIR/type-alias-bounds.rs:8:14
| |
LL | type SVec<T: Send + Send> = Vec<T>; LL | type SVec<T: Send + Send> = Vec<T>;
| ^^^^ ^^^^ | --^^^^---^^^^
| | |
| | will not be checked at usage sites of the type alias
| help: remove this bound
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
= note: `#[warn(type_alias_bounds)]` on by default = note: `#[warn(type_alias_bounds)]` on by default
help: the bound will not be checked when the type alias is used, and should be removed
|
LL - type SVec<T: Send + Send> = Vec<T>;
LL + type SVec<T> = Vec<T>;
|
warning: where clauses are not enforced in type aliases warning: where clauses on type aliases are not enforced
--> $DIR/type-alias-bounds.rs:10:21 --> $DIR/type-alias-bounds.rs:10:21
| |
LL | type S2Vec<T> where T: Send = Vec<T>; LL | type S2Vec<T> where T: Send = Vec<T>;
| ^^^^^^^ | ------^^^^^^^
| | | |
help: the clause will not be checked when the type alias is used, and should be removed | | will not be checked at usage sites of the type alias
| | help: remove this where clause
LL - type S2Vec<T> where T: Send = Vec<T>;
LL + type S2Vec<T> = Vec<T>;
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
warning: bounds on generic parameters are not enforced in type aliases warning: bounds on generic parameters in type aliases are not enforced
--> $DIR/type-alias-bounds.rs:12:19 --> $DIR/type-alias-bounds.rs:12:19
| |
LL | type VVec<'b, 'a: 'b + 'b> = (&'b u32, Vec<&'a i32>); LL | type VVec<'b, 'a: 'b + 'b> = (&'b u32, Vec<&'a i32>);
| ^^ ^^ | --^^---^^
| | | |
help: the bound will not be checked when the type alias is used, and should be removed | | will not be checked at usage sites of the type alias
| | help: remove this bound
LL - type VVec<'b, 'a: 'b + 'b> = (&'b u32, Vec<&'a i32>);
LL + type VVec<'b, 'a> = (&'b u32, Vec<&'a i32>);
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
warning: bounds on generic parameters are not enforced in type aliases warning: bounds on generic parameters in type aliases are not enforced
--> $DIR/type-alias-bounds.rs:14:18 --> $DIR/type-alias-bounds.rs:14:18
| |
LL | type WVec<'b, T: 'b + 'b> = (&'b u32, Vec<T>); LL | type WVec<'b, T: 'b + 'b> = (&'b u32, Vec<T>);
| ^^ ^^ | --^^---^^
| | | |
help: the bound will not be checked when the type alias is used, and should be removed | | will not be checked at usage sites of the type alias
| | help: remove this bound
LL - type WVec<'b, T: 'b + 'b> = (&'b u32, Vec<T>);
LL + type WVec<'b, T> = (&'b u32, Vec<T>);
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
warning: where clauses are not enforced in type aliases warning: where clauses on type aliases are not enforced
--> $DIR/type-alias-bounds.rs:16:25 --> $DIR/type-alias-bounds.rs:16:25
| |
LL | type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec<T>); LL | type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec<T>);
| ^^^^^ ^^^^^ | ------^^^^^--^^^^^
| | | |
help: the clause will not be checked when the type alias is used, and should be removed | | will not be checked at usage sites of the type alias
| | help: remove this where clause
LL - type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec<T>);
LL + type W2Vec<'b, T> = (&'b u32, Vec<T>);
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
warning: bounds on generic parameters are not enforced in type aliases warning: bounds on generic parameters in type aliases are not enforced
--> $DIR/type-alias-bounds.rs:47:12 --> $DIR/type-alias-bounds.rs:48:12
| |
LL | type T1<U: Bound> = U::Assoc; LL | type T1<U: Bound> = U::Assoc;
| ^^^^^ | ^^^^^ will not be checked at usage sites of the type alias
| |
help: use fully disambiguated paths (i.e., `<T as Trait>::Assoc`) to refer to associated types in type aliases = note: this is a known limitation of the type checker that may be lifted in a future edition.
--> $DIR/type-alias-bounds.rs:47:21 see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
| = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
LL | type T1<U: Bound> = U::Assoc; help: remove this bound
| ^^^^^^^^
help: the bound will not be checked when the type alias is used, and should be removed
| |
LL - type T1<U: Bound> = U::Assoc; LL - type T1<U: Bound> = U::Assoc;
LL + type T1<U> = U::Assoc; LL + type T1<U> = U::Assoc;
| |
help: fully qualify this associated type
|
LL | type T1<U: Bound> = <U as /* Trait */>::Assoc;
| + +++++++++++++++
warning: where clauses are not enforced in type aliases warning: where clauses on type aliases are not enforced
--> $DIR/type-alias-bounds.rs:48:18 --> $DIR/type-alias-bounds.rs:49:18
| |
LL | type T2<U> where U: Bound = U::Assoc; LL | type T2<U> where U: Bound = U::Assoc;
| ^^^^^^^^ | ^^^^^^^^ will not be checked at usage sites of the type alias
| |
help: use fully disambiguated paths (i.e., `<T as Trait>::Assoc`) to refer to associated types in type aliases = note: this is a known limitation of the type checker that may be lifted in a future edition.
--> $DIR/type-alias-bounds.rs:48:29 see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
| = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
LL | type T2<U> where U: Bound = U::Assoc; help: remove this where clause
| ^^^^^^^^
help: the clause will not be checked when the type alias is used, and should be removed
| |
LL - type T2<U> where U: Bound = U::Assoc; LL - type T2<U> where U: Bound = U::Assoc;
LL + type T2<U> = U::Assoc; LL + type T2<U> = U::Assoc;
| |
help: fully qualify this associated type
|
LL | type T2<U> where U: Bound = <U as /* Trait */>::Assoc;
| + +++++++++++++++
warning: bounds on generic parameters are not enforced in type aliases warning: bounds on generic parameters in type aliases are not enforced
--> $DIR/type-alias-bounds.rs:56:12
|
LL | type T5<U: Bound> = <U as Bound>::Assoc;
| ^^^^^
|
help: the bound will not be checked when the type alias is used, and should be removed
|
LL - type T5<U: Bound> = <U as Bound>::Assoc;
LL + type T5<U> = <U as Bound>::Assoc;
|
warning: bounds on generic parameters are not enforced in type aliases
--> $DIR/type-alias-bounds.rs:57:12 --> $DIR/type-alias-bounds.rs:57:12
| |
LL | type T5<U: Bound> = <U as Bound>::Assoc;
| --^^^^^
| | |
| | will not be checked at usage sites of the type alias
| help: remove this bound
|
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
warning: bounds on generic parameters in type aliases are not enforced
--> $DIR/type-alias-bounds.rs:58:12
|
LL | type T6<U: Bound> = ::std::vec::Vec<U>; LL | type T6<U: Bound> = ::std::vec::Vec<U>;
| ^^^^^ | --^^^^^
| | | |
help: the bound will not be checked when the type alias is used, and should be removed | | will not be checked at usage sites of the type alias
| | help: remove this bound
LL - type T6<U: Bound> = ::std::vec::Vec<U>;
LL + type T6<U> = ::std::vec::Vec<U>;
| |
= note: this is a known limitation of the type checker that may be lifted in a future edition.
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
= help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
warning: 9 warnings emitted warning: 9 warnings emitted