Skip to content

Introduce impl restrictions to AST, lower to rustc_middle #141754

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use rustc_data_structures::tagged_ptr::Tag;
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
pub use rustc_span::AttrId;
use rustc_span::source_map::{Spanned, respan};
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
use thin_vec::{ThinVec, thin_vec};

pub use crate::format::*;
Expand Down Expand Up @@ -3316,6 +3316,42 @@ impl VisibilityKind {
}
}

#[derive(Clone, Encodable, Decodable, Debug)]
pub struct Restriction {
pub kind: RestrictionKind,
pub span: Span,
pub tokens: Option<LazyAttrTokenStream>,
}

#[derive(Clone, Encodable, Decodable, Debug)]
pub enum RestrictionKind {
Unrestricted,
Restricted { path: P<Path>, id: NodeId, shorthand: bool },
Implied,
Comment on lines +3328 to +3330
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the difference between Unrestricted and Implied?

As far as I can see from the parser, Implied seems to mean no impl at all, while Unrestricted seems to mean impl without parentheses or anything else, is that correct?

Looking at the RFC I can't seems to find any reference to just impl for restrictions, it's always supposed to be followed but parentheses1.

And in ty::Restriction there is only Restricted and Unrestricted (your Implied).

What's the reasoning for that? Shouldn't we just have Restricted and Unrestricted? Like so?

Suggested change
Unrestricted,
Restricted { path: P<Path>, id: NodeId, shorthand: bool },
Implied,
/// No restriction.
Unrestricted,
/// Restricted, either `kw(crate)`, `kw(self)`, `kw(super)`, `kw(in path)`
Restricted { path: P<Path>, id: NodeId, shorthand: bool },

EDIT: Looking at the previous PR, it seems to be for the visiblity, which where pub alone is valid, https://github.com/rust-lang/rust/pull/106074/files#diff-00567f9be31678eec46651e6157f0eaaa47d1f344eb61b8a63f87edfa7bf6b14R2799-R2811.

Let's defer any merging with visiblity for a future PR.

Footnotes

  1. https://rust-lang.github.io/rfcs/3323-restrictions.html#syntax

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While it will ultimately be used to parse visibility, this is also intended to reduce friction for any future restrictions that are added and have keyword-only syntax permitted.

As to the difference between Unrestricted and Implied, Unrestricted is an explicit keyword-only modifier, while Implied is "figure this out some other way". The reason Implied doesn't exist later on is that it is determined what the appropriate level is. In reality this means unrestricted, as if something is inaccessible (from visibility) then we don't care about the impl restriction.

For both impl and mut unrestricted isn't permitted (at least for now), but being able to parse this is plausibly useful to provide improved diagnostics.

Of course, if you feel strongly about this, I can leave it out, but it'll almost certainly be re-introduced in the future.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking back at the original PR, I missed one bit that needed to be carried over that would reject Unrestricted during parsing. The final bit about it being re-introduced later even if left out now remains true, so I think it's reasonable to leave it in regardless.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's reasonable to leave it in regardless.

I don't feel strongly about it, so if you prefer to leave it, then leave it; but please add doc-comments explaining what each Restriction variant represent.

}

impl Restriction {
pub fn unrestricted() -> Self {
Restriction { kind: RestrictionKind::Unrestricted, span: DUMMY_SP, tokens: None }
}

pub fn restricted(path: P<Path>, id: NodeId, shorthand: bool) -> Self {
Restriction {
kind: RestrictionKind::Restricted { path, id, shorthand },
span: DUMMY_SP,
tokens: None,
}
}

pub fn implied() -> Self {
Restriction { kind: RestrictionKind::Implied, span: DUMMY_SP, tokens: None }
}
Comment on lines +3334 to +3348
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the previous PR, https://github.com/rust-lang/rust/pull/106074/files#diff-68dedde73c3f169dfeeed73fc362bede2bb414477a495b8b2fb626fc5e91dd19R172 seems to be the only place where a Span is not given, and it's for mut restriction, let's deal with that later.

Could you change the methods so take Span directly, that should avoid the use of DUMMY_SP and should avoid accidental omition of the span.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do. Passing DUMMY_SP explicitly isn't a bad idea.


pub fn with_span(self, span: Span) -> Self {
Restriction { span, ..self }
}
}

/// Field definition in a struct, variant or union.
///
/// E.g., `bar: usize` as in `struct Foo { bar: usize }`.
Expand Down Expand Up @@ -3494,6 +3530,7 @@ impl Default for FnHeader {

#[derive(Clone, Encodable, Decodable, Debug)]
pub struct Trait {
pub impl_restriction: Restriction,
pub safety: Safety,
pub is_auto: IsAuto,
Comment on lines +3533 to 3535
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the synctax changes from the RFC, impl restrictions are supposed to be after safety (and auto?), so let's keep them in their order of appearance in the source code.

Suggested change
pub impl_restriction: Restriction,
pub safety: Safety,
pub is_auto: IsAuto,
pub safety: Safety,
pub is_auto: IsAuto,
pub impl_restriction: Restriction,

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Argh…you're right, though I do think pub impl(crate) unsafe auto is the right order ultimately. I'll update it per the RFC while noting that it's an unresolved question.

pub ident: Ident,
Expand Down
20 changes: 16 additions & 4 deletions compiler/rustc_ast/src/ast_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use crate::ptr::P;
use crate::tokenstream::LazyAttrTokenStream;
use crate::{
Arm, AssocItem, AttrItem, AttrKind, AttrVec, Attribute, Block, Crate, Expr, ExprField,
FieldDef, ForeignItem, GenericParam, Item, NodeId, Param, Pat, PatField, Path, Stmt, StmtKind,
Ty, Variant, Visibility, WherePredicate,
FieldDef, ForeignItem, GenericParam, Item, NodeId, Param, Pat, PatField, Path, Restriction,
Stmt, StmtKind, Ty, Variant, Visibility, WherePredicate,
};

/// A trait for AST nodes having an ID.
Expand Down Expand Up @@ -98,7 +98,19 @@ macro_rules! impl_has_tokens_none {
};
}

impl_has_tokens!(AssocItem, AttrItem, Block, Expr, ForeignItem, Item, Pat, Path, Ty, Visibility);
impl_has_tokens!(
AssocItem,
AttrItem,
Block,
Expr,
ForeignItem,
Item,
Pat,
Path,
Restriction,
Ty,
Visibility
);
impl_has_tokens_none!(
Arm,
ExprField,
Expand Down Expand Up @@ -243,7 +255,7 @@ impl_has_attrs!(
Variant,
WherePredicate,
);
impl_has_attrs_none!(Attribute, AttrItem, Block, Pat, Path, Ty, Visibility);
impl_has_attrs_none!(Attribute, AttrItem, Block, Pat, Path, Restriction, Ty, Visibility);

impl<T: HasAttrs> HasAttrs for P<T> {
const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS;
Expand Down
16 changes: 16 additions & 0 deletions compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,10 @@ pub trait MutVisitor: Sized {
walk_vis(self, vis);
}

fn visit_restriction(&mut self, restriction: &mut Restriction) {
walk_restriction(self, restriction);
}

fn visit_id(&mut self, _id: &mut NodeId) {
// Do nothing.
}
Expand Down Expand Up @@ -1405,6 +1409,18 @@ fn walk_vis<T: MutVisitor>(vis: &mut T, visibility: &mut Visibility) {
vis.visit_span(span);
}

fn walk_restriction<T: MutVisitor>(vis: &mut T, restriction: &mut Restriction) {
let Restriction { kind, span, tokens: _ } = restriction;
match kind {
RestrictionKind::Unrestricted | RestrictionKind::Implied => {}
RestrictionKind::Restricted { path, id, shorthand: _ } => {
vis.visit_id(id);
vis.visit_path(path);
}
}
vis.visit_span(span);
}

fn walk_capture_by<T: MutVisitor>(vis: &mut T, capture_by: &mut CaptureBy) {
match capture_by {
CaptureBy::Ref => {}
Expand Down
28 changes: 27 additions & 1 deletion compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,9 @@ pub trait Visitor<'ast>: Sized {
fn visit_vis(&mut self, vis: &'ast Visibility) -> Self::Result {
walk_vis(self, vis)
}
fn visit_restriction(&mut self, restriction: &'ast Restriction) -> Self::Result {
walk_restriction(self, restriction)
}
fn visit_fn_ret_ty(&mut self, ret_ty: &'ast FnRetTy) -> Self::Result {
walk_fn_ret_ty(self, ret_ty)
}
Expand Down Expand Up @@ -554,7 +557,16 @@ macro_rules! common_visitor_and_walkers {
<V as Visitor<$lt>>::Result::output()
)?
}
ItemKind::Trait(box Trait { safety, is_auto: _, ident, generics, bounds, items }) => {
ItemKind::Trait(box Trait {
impl_restriction,
safety,
is_auto: _,
ident,
generics,
bounds,
items,
}) => {
try_visit!(vis.visit_restriction(impl_restriction));
try_visit!(visit_safety(vis, safety));
try_visit!(vis.visit_ident(ident));
try_visit!(vis.visit_generics(generics));
Expand Down Expand Up @@ -1516,6 +1528,20 @@ pub fn walk_vis<'a, V: Visitor<'a>>(visitor: &mut V, vis: &'a Visibility) -> V::
V::Result::output()
}

pub fn walk_restriction<'a, V: Visitor<'a>>(
visitor: &mut V,
restriction: &'a Restriction,
) -> V::Result {
let Restriction { kind, span: _, tokens: _ } = restriction;
match kind {
RestrictionKind::Unrestricted | RestrictionKind::Implied => {}
RestrictionKind::Restricted { path, id, shorthand: _ } => {
try_visit!(visitor.visit_path(path, *id));
}
}
V::Result::output()
}

pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute) -> V::Result {
let Attribute { kind, id: _, style: _, span: _ } = attr;
match kind {
Expand Down
10 changes: 9 additions & 1 deletion compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
items: new_impl_items,
}))
}
ItemKind::Trait(box Trait { is_auto, safety, ident, generics, bounds, items }) => {
ItemKind::Trait(box Trait {
impl_restriction: _,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
impl_restriction: _,
// FIXME(impl_restrictions): lower to HIR
impl_restriction: _,

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the benefit of this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just a nit. To make it explicit that it's not yet handle but should be. You can leave it if you prefer.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant what's the benefit of lowering it, not of the comment. Given that it's not handled by anything in HIR, I don't see why it needs to be present. Though perhaps I'm missing something obvious here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For linting purpose it maybe be good to lower the span, like is done with the visibility span on Item. Otherwise I agree that lowering the Restriction it-self is not useful.

is_auto,
safety,
ident,
generics,
bounds,
items,
}) => {
let ident = self.lower_ident(*ident);
let (generics, (safety, items, bounds)) = self.lower_generics(
generics,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(where_clause_attrs, "attributes in `where` clause are unstable");
gate_all!(super_let, "`super let` is experimental");
gate_all!(frontmatter, "frontmatters are experimental");
gate_all!(impl_restriction, "impl restrictions are experimental");

if !visitor.features.never_patterns() {
if let Some(spans) = spans.get(&sym::never_patterns) {
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1071,6 +1071,10 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
Self::to_string(|s| s.print_visibility(v))
}

fn restriction_to_string(&self, kw: &'static str, r: &ast::Restriction) -> String {
Self::to_string(|s| s.print_restriction(kw, r))
}

fn block_to_string(&self, blk: &ast::Block) -> String {
Self::to_string(|s| {
let (cb, ib) = s.head("");
Expand Down
18 changes: 18 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ impl<'a> State<'a> {
self.bclose(item.span, empty, cb);
}
ast::ItemKind::Trait(box ast::Trait {
impl_restriction,
safety,
is_auto,
ident,
Expand All @@ -366,6 +367,7 @@ impl<'a> State<'a> {
}) => {
let (cb, ib) = self.head("");
self.print_visibility(&item.vis);
self.print_restriction("impl", impl_restriction);
self.print_safety(*safety);
self.print_is_auto(*is_auto);
Comment on lines +370 to 372
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be re-ordered.

Suggested change
self.print_restriction("impl", impl_restriction);
self.print_safety(*safety);
self.print_is_auto(*is_auto);
self.print_safety(*safety);
self.print_is_auto(*is_auto);
self.print_restriction("impl", impl_restriction);

self.word_nbsp("trait");
Expand Down Expand Up @@ -473,6 +475,22 @@ impl<'a> State<'a> {
}
}

// FIXME(jhpratt) make `kw` into a const generic once permitted
pub(crate) fn print_restriction(&mut self, kw: &'static str, restriction: &ast::Restriction) {
match restriction.kind {
ast::RestrictionKind::Unrestricted => self.word_nbsp(kw),
ast::RestrictionKind::Restricted { ref path, shorthand, id: _ } => {
let path = Self::to_string(|s| s.print_path(path, false, 0));
if shorthand {
self.word_nbsp(format!("{kw}({path})"))
} else {
self.word_nbsp(format!("{kw}(in {path})"))
}
}
ast::RestrictionKind::Implied => {}
}
}

fn print_defaultness(&mut self, defaultness: ast::Defaultness) {
if let ast::Defaultness::Default(_) = defaultness {
self.word_nbsp("default");
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,8 @@ declare_features! (
(unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)),
/// Allows `if let` guard in match arms.
(unstable, if_let_guard, "1.47.0", Some(51114)),
/// Allows `impl(crate) trait Foo` restrictions
(unstable, impl_restriction, "CURRENT_RUSTC_VERSION", Some(105077)),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The feature should be marked incomplete for now.

Suggested change
(unstable, impl_restriction, "CURRENT_RUSTC_VERSION", Some(105077)),
(incomplete, impl_restriction, "CURRENT_RUSTC_VERSION", Some(105077)),

/// Allows `impl Trait` to be used inside associated types (RFC 2515).
(unstable, impl_trait_in_assoc_type, "1.70.0", Some(63063)),
/// Allows `impl Trait` in bindings (`let`).
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_middle/src/hir/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1179,14 +1179,15 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh {
}
tcx.sess.opts.dep_tracking_hash(true).hash_stable(&mut hcx, &mut stable_hasher);
tcx.stable_crate_id(LOCAL_CRATE).hash_stable(&mut hcx, &mut stable_hasher);
// Hash visibility information since it does not appear in HIR.
// Hash visibility and restriction information since it does not appear in HIR.
// FIXME: Figure out how to remove `visibilities_for_hashing` by hashing visibilities on
// the fly in the resolver, storing only their accumulated hash in `ResolverGlobalCtxt`,
// and combining it with other hashes here.
resolutions.visibilities_for_hashing.hash_stable(&mut hcx, &mut stable_hasher);
with_metavar_spans(|mspans| {
mspans.freeze_and_get_read_spans().hash_stable(&mut hcx, &mut stable_hasher);
});
resolutions.impl_restrictions.hash_stable(&mut hcx, &mut stable_hasher);
stable_hasher.finish()
});

Expand Down
30 changes: 30 additions & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ pub struct ResolverOutputs {
#[derive(Debug)]
pub struct ResolverGlobalCtxt {
pub visibilities_for_hashing: Vec<(LocalDefId, Visibility)>,
pub impl_restrictions: FxIndexMap<LocalDefId, Restriction>,
/// Item with a given `LocalDefId` was defined during macro expansion with ID `ExpnId`.
pub expn_that_defined: FxHashMap<LocalDefId, ExpnId>,
pub effective_visibilities: EffectiveVisibilities,
Expand Down Expand Up @@ -322,6 +323,35 @@ impl Visibility {
}
}

#[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, Encodable, Decodable, HashStable)]
pub enum Restriction {
/// The restriction does not affect the item.
Unrestricted,
/// The restriction only applies outside of this path.
Restricted(DefId, Span),
}

impl Restriction {
/// Returns `true` if the behavior is allowed/unrestricted in the given module. A value of
/// `false` indicates that the behavior is prohibited.
pub fn is_allowed_in(self, module: DefId, tcx: TyCtxt<'_>) -> bool {
let restricted_to = match self {
Restriction::Unrestricted => return true,
Restriction::Restricted(module, _) => module,
};

tcx.is_descendant_of(module, restricted_to.into())
}

/// Obtain the [`Span`] of the restriction. If unrestricted, an empty span is returned.
pub fn span(&self) -> Span {
match self {
Restriction::Unrestricted => DUMMY_SP,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we instead return an Option<Span>? That would make it clear that there might not a span for some restrictions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only used in one location (<ImplOfRestrictedTraitVisitor as Visitor>::visit_item), so I'm unsure of the benefit gained here. A span is ultimately needed, and DUMMY_SP seems like the most logical choice.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A span is ultimately needed

Is it really needed? If it's Unrestricted, is_allowed_in should only report true so you never call the span method with an Unrestricted restriction. Maybe a panic! instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The initial PR had a bug! call but it was requested to change it to what's currently present: #106074 (comment)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what #106074 (comment) suggested was to have a (not the) dummy span, probably the same empty span the parser uses and have Restriction::Unrestricted be Restriction::Unrestricted { span: Span}; which I would be okay with.

Restriction::Restricted(_, span) => *span,
}
}
}

#[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, TyEncodable, TyDecodable, HashStable)]
#[derive(TypeFoldable, TypeVisitable)]
pub struct ClosureSizeProfileData<'tcx> {
Expand Down
20 changes: 20 additions & 0 deletions compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,13 @@ parse_inclusive_range_no_end = inclusive range with no end
parse_incorrect_parens_trait_bounds = incorrect parentheses around trait bounds
parse_incorrect_parens_trait_bounds_sugg = fix the parentheses

parse_incorrect_restriction = incorrect {parse_restriction_noun} restriction
.help = some possible {parse_restriction_noun} restrictions are:
`{$keyword}(crate)`: {parse_restriction_adjective} only in the current crate
`{$keyword}(super)`: {parse_restriction_adjective} only in the current module's parent
`{$keyword}(in path::to::module)`: {parse_restriction_adjective} only in the specified path
Comment on lines +381 to +384
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to also mention kw(self)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. I'll need to special-case pub later on, but that's not a concern for now.

.suggestion = make this {parse_restriction_adjective} only to module `{$path}` with `in`

parse_incorrect_semicolon =
expected item, found `;`
.suggestion = remove this semicolon
Expand Down Expand Up @@ -783,6 +790,18 @@ parse_reserved_string = invalid string literal
.note = unprefixed guarded string literals are reserved for future use since Rust 2024
.suggestion_whitespace = consider inserting whitespace here

# internal use only
parse_restriction_adjective = { $keyword ->
[impl] implementable
*[DEFAULT_IS_BUG] BUG
}

# internal use only
parse_restriction_noun = { $keyword ->
[impl] impl
*[DEFAULT_IS_BUG] BUG
}

parse_return_types_use_thin_arrow = return types are denoted using `->`
.suggestion = use `->` instead

Expand Down Expand Up @@ -853,6 +872,7 @@ parse_trailing_vert_not_allowed = a trailing `|` is not allowed in an or-pattern
parse_trait_alias_cannot_be_auto = trait aliases cannot be `auto`
parse_trait_alias_cannot_be_unsafe = trait aliases cannot be `unsafe`

parse_trait_alias_cannot_have_impl_restriction = trait alias cannot have `impl` restriction
parse_transpose_dyn_or_impl = `for<...>` expected after `{$kw}`, not before
.suggestion = move `{$kw}` before the `for<...>`

Expand Down
Loading
Loading