Skip to content

Pattern types MVP #107606

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

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 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
3 changes: 3 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2102,6 +2102,9 @@ pub enum TyKind {
Err,
/// Placeholder for a `va_list`.
CVarArgs,
/// Pattern types like `u32 as 1..=`, which is the same as `NonZeroU32`,
/// just as part of the type system.
Comment on lines +2105 to +2106
Copy link

@shua shua Feb 27, 2023

Choose a reason for hiding this comment

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

Not sure if this is supposed to be "is" or "as", but it seems like "is" is used everywhere else

Suggested change
/// Pattern types like `u32 as 1..=`, which is the same as `NonZeroU32`,
/// just as part of the type system.
/// Pattern types like `u32 is 1..=`, which is the same as `NonZeroU32`,
/// just as part of the type system.

Pat(P<Ty>, P<Pat>),
}

impl TyKind {
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,10 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
}
TyKind::Tup(tys) => visit_vec(tys, |ty| vis.visit_ty(ty)),
TyKind::Paren(ty) => vis.visit_ty(ty),
TyKind::Pat(ty, pat) => {
vis.visit_ty(ty);
vis.visit_pat(pat);
}
TyKind::Path(qself, path) => {
vis.visit_qself(qself);
vis.visit_path(path);
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,10 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) {
}
visitor.visit_path(path, typ.id);
}
TyKind::Pat(ty, pat) => {
visitor.visit_ty(ty);
visitor.visit_pat(pat);
}
TyKind::Array(ty, length) => {
visitor.visit_ty(ty);
visitor.visit_anon_const(length)
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1372,14 +1372,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
}
}
TyKind::MacCall(_) => panic!("`TyKind::MacCall` should have been expanded by now"),
TyKind::MacCall(_) => {
span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now")
}
TyKind::CVarArgs => {
self.tcx.sess.delay_span_bug(
t.span,
"`TyKind::CVarArgs` should have been handled elsewhere",
);
hir::TyKind::Err
}
TyKind::Pat(ty, pat) => hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_pat(pat)),
};

hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.lower_node_id(t.id) }
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,9 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
ast::TyKind::TraitObject(_, ast::TraitObjectSyntax::DynStar, ..) => {
gate_feature_post!(&self, dyn_star, ty.span, "dyn* trait objects are unstable");
}
ast::TyKind::Pat(..) => {
gate_feature_post!(&self, pattern_types, ty.span, "pattern types are unstable");
}
_ => {}
}
visit::walk_ty(self, ty)
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,11 @@ impl<'a> State<'a> {
ast::TyKind::CVarArgs => {
self.word("...");
}
ast::TyKind::Pat(ty, pat) => {
self.print_type(ty);
self.word(" is ");
self.print_pat(pat);
}
}
self.end();
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,8 @@ declare_features! (
(active, object_safe_for_dispatch, "1.40.0", Some(43561), None),
/// Allows using `#[optimize(X)]`.
(active, optimize_attribute, "1.34.0", Some(54882), None),
/// Allows using pattern types.
(active, pattern_types, "CURRENT_RUSTC_VERSION", Some(54882), None),
/// Allows `extern "platform-intrinsic" { ... }`.
(active, platform_intrinsics, "1.4.0", Some(27731), None),
/// Allows using `#![plugin(myplugin)]`.
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2682,6 +2682,8 @@ pub enum TyKind<'hir> {
Infer,
/// Placeholder for a type that has failed to be defined.
Err,
/// Pattern types (`u32 as 1..`)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// Pattern types (`u32 as 1..`)
/// Pattern types (`u32 is 1..`)

Copy link

Choose a reason for hiding this comment

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

why not u32 @ 1..?

Pat(&'hir Ty<'hir>, &'hir Pat<'hir>),
}

#[derive(Debug, HashStable_Generic)]
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_hir/src/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,10 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) {
}
TyKind::Typeof(ref expression) => visitor.visit_anon_const(expression),
TyKind::Infer | TyKind::Err => {}
TyKind::Pat(ty, pat) => {
visitor.visit_ty(ty);
visitor.visit_pat(pat)
}
}
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2961,6 +2961,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// handled specially and will not descend into this routine.
self.ty_infer(None, ast_ty.span)
}
hir::TyKind::Pat(..) => span_bug!(ast_ty.span, "{ast_ty:#?}"),
hir::TyKind::Err => tcx.ty_error(),
};

Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_hir_pretty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,11 @@ impl<'a> State<'a> {
hir::TyKind::Infer => {
self.word("_");
}
hir::TyKind::Pat(ty, pat) => {
self.print_type(ty);
self.word(" is ");
self.print_pat(pat);
}
}
self.end()
}
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_parse/src/parser/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,12 @@ impl<'a> Parser<'a> {
let span = lo.to(self.prev_token.span);
let mut ty = self.mk_ty(span, kind);

if self.eat_keyword(sym::is) {
Copy link
Member

@compiler-errors compiler-errors Feb 2, 2023

Choose a reason for hiding this comment

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

doesn't this change the behavior of macro code like:

macro_rules! foo {
  ($ty:ty) => {};
  (() is $pat:pat) => {};
}

foo!(() is 0..1);

Copy link
Member

Choose a reason for hiding this comment

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

macro rules matches continue being the least forwards compatible thing ever :|

Copy link
Member

Choose a reason for hiding this comment

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

Maybe it's better to expose this through a builtin macro instead of surface syntax, like rustc_pattern_type!() which expands into ast::TyKind::Pat

Copy link
Member

@fmease fmease Feb 3, 2023

Choose a reason for hiding this comment

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

Or builtin#Pat(…) instead of a built-in macro (with builtin# from T-compiler MCP #580).

Copy link
Contributor

Choose a reason for hiding this comment

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

macro rules matches continue being the least forwards compatible thing ever :|

And this is where the editions system can shine.

Copy link
Contributor

Choose a reason for hiding this comment

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

... I'm a little surprised/sad that foo! works at all today. Follow sets and greedy parsing were supposed to handle this -- you're not allowed to follow a :ty fragment with is -- but follow set restrictions are only applied within an arm, and not across arms. Being able to follow a fragment with some token in a simultaneously-taken arm matching the fragment's tokens essentially makes follow sets ineffectual at their job of making fragments forwards compatible. But simultaneously, applying follow set rules between arms would be very restrictive and probably not even solve the problem entirely, since you can attack non-follow-set grammar extensions similarly. I guess you'd need to ensure that the choice of which arm to take is always known to be forwards-compatibly LL(k) via some sort of "leading set," and if the choice isn't LL(k), ensure that the refining match fits within the grammar and follow set of the refined fragment.

i.e., complicated. And still overly restrictive, since we also have the rule for lexically prior arms taking priority over latter arms, so there's only a forwards compatibility concern if the refining arms come after the refined arm. (And until today I thought that macro parsing was greedier than it actually is, preventing foo! from ever matching the second arm here, since the $:ty arm is prior and matched the prefix. Despite probably relying on that not being the case.)

let pat = self.parse_pat_no_top_alt(None)?;
let span = lo.to(self.prev_token.span);
ty = self.mk_ty(span, TyKind::Pat(ty, pat));
}

// Try to recover from use of `+` with incorrect priority.
match allow_plus {
AllowPlus::Yes => self.maybe_recover_from_bad_type_plus(&ty)?,
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_passes/src/hir_stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
TraitObject,
Typeof,
Infer,
Pat,
Err
]
);
Expand Down Expand Up @@ -586,6 +587,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
Never,
Tup,
Path,
Pat,
TraitObject,
ImplTrait,
Paren,
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_save_analysis/src/sig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,10 @@ impl<'hir> Sig for hir::Ty<'hir> {
let item = scx.tcx.hir().item(item_id);
item.make(offset, Some(item_id.hir_id()), scx)
}
hir::TyKind::Typeof(_) | hir::TyKind::Infer | hir::TyKind::Err => Err("Ty"),
hir::TyKind::Pat(..)
| hir::TyKind::Typeof(_)
| hir::TyKind::Infer
| hir::TyKind::Err => Err("Ty"),
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,7 @@ symbols! {
intra_doc_pointers,
intrinsics,
irrefutable_let_patterns,
is,
isa_attribute,
isize,
issue,
Expand Down Expand Up @@ -1076,6 +1077,7 @@ symbols! {
pat_param,
path,
pattern_parentheses,
pattern_types,
phantom_data,
pin,
platform_intrinsics,
Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1609,6 +1609,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
BorrowedRef { lifetime, mutability: m.mutbl, type_: Box::new(clean_ty(m.ty, cx)) }
}
TyKind::Slice(ty) => Slice(Box::new(clean_ty(ty, cx))),
TyKind::Pat(ty, pat) => Type::Pat(Box::new(clean_ty(ty, cx)), format!("{pat:?}").into()),
TyKind::Array(ty, ref length) => {
let length = match length {
hir::ArrayLen::Infer(_, _) => "_".to_string(),
Expand Down
14 changes: 12 additions & 2 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1602,7 +1602,9 @@ pub(crate) enum Type {
///
/// This is mostly Rustdoc's version of [`hir::Path`].
/// It has to be different because Rustdoc's [`PathSegment`] can contain cleaned generics.
Path { path: Path },
Path {
path: Path,
},
/// A `dyn Trait` object: `dyn for<'a> Trait<'a> + Send + 'static`
DynTrait(Vec<PolyTrait>, Option<Lifetime>),
/// A type parameter.
Expand All @@ -1619,10 +1621,15 @@ pub(crate) enum Type {
///
/// The `String` field is a stringified version of the array's length parameter.
Array(Box<Type>, Box<str>),
Pat(Box<Type>, Box<str>),
/// A raw pointer type: `*const i32`, `*mut i32`
RawPointer(Mutability, Box<Type>),
/// A reference type: `&i32`, `&'a mut Foo`
BorrowedRef { lifetime: Option<Lifetime>, mutability: Mutability, type_: Box<Type> },
BorrowedRef {
lifetime: Option<Lifetime>,
mutability: Mutability,
type_: Box<Type>,
},

/// A qualified path to an associated item: `<Type as Trait>::Name`
QPath(Box<QPathData>),
Expand Down Expand Up @@ -1747,6 +1754,7 @@ impl Type {
BareFunction(..) => PrimitiveType::Fn,
Slice(..) => PrimitiveType::Slice,
Array(..) => PrimitiveType::Array,
Type::Pat(..) => PrimitiveType::Pat,
RawPointer(..) => PrimitiveType::RawPointer,
QPath(box QPathData { ref self_type, .. }) => return self_type.inner_def_id(cache),
Generic(_) | Infer | ImplTrait(_) => return None,
Expand Down Expand Up @@ -1798,6 +1806,7 @@ pub(crate) enum PrimitiveType {
Str,
Slice,
Array,
Pat,
Tuple,
Unit,
RawPointer,
Expand Down Expand Up @@ -1945,6 +1954,7 @@ impl PrimitiveType {
Bool => sym::bool,
Char => sym::char,
Array => sym::array,
Pat => sym::pat,
Slice => sym::slice,
Tuple => sym::tuple,
Unit => sym::unit,
Expand Down
4 changes: 4 additions & 0 deletions src/librustdoc/html/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,10 @@ fn fmt_type<'cx>(
write!(f, "]")
}
},
clean::Type::Pat(ref t, ref pat) => {
fmt::Display::fmt(&t.print(cx), f)?;
write!(f, " is {pat}")
}
clean::Array(ref t, ref n) => match **t {
clean::Generic(name) if !f.alternate() => primitive_link(
f,
Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/html/render/search_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ fn get_index_type_id(clean_type: &clean::Type) -> Option<RenderTypeId> {
| clean::ImplTrait(_)
| clean::Tuple(_)
| clean::Slice(_)
| clean::Type::Pat(..)
| clean::Array(_, _)
| clean::QPath { .. }
| clean::Infer => None,
Expand Down
3 changes: 3 additions & 0 deletions src/librustdoc/json/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,9 @@ impl FromWithTcx<clean::Type> for Type {
Tuple(t) => Type::Tuple(t.into_tcx(tcx)),
Slice(t) => Type::Slice(Box::new((*t).into_tcx(tcx))),
Array(t, s) => Type::Array { type_: Box::new((*t).into_tcx(tcx)), len: s.to_string() },
clean::Type::Pat(t, p) => {
Type::Pat { type_: Box::new((*t).into_tcx(tcx)), pat: p.to_string() }
}
ImplTrait(g) => Type::ImplTrait(g.into_tcx(tcx)),
Infer => Type::Infer,
RawPointer(mutability, type_) => Type::RawPointer {
Expand Down
8 changes: 7 additions & 1 deletion src/rustdoc-json-types/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::path::PathBuf;
use serde::{Deserialize, Serialize};

/// rustdoc format-version.
pub const FORMAT_VERSION: u32 = 24;
pub const FORMAT_VERSION: u32 = 25;

/// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information
/// about the language items in the local crate, as well as info about external items to allow
Expand Down Expand Up @@ -565,6 +565,12 @@ pub enum Type {
type_: Box<Type>,
len: String,
},
/// `u32 is 1..`
Pat {
#[serde(rename = "type")]
type_: Box<Type>,
pat: String,
},
/// `impl TraitA + TraitB + ...`
ImplTrait(Vec<GenericBound>),
/// `_`
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/dereference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1010,7 +1010,7 @@ fn binding_ty_auto_deref_stability<'tcx>(
}
},
TyKind::Slice(_) => Position::DerefStable(precedence, false),
TyKind::Array(..) | TyKind::Ptr(_) | TyKind::BareFn(_) => Position::DerefStable(precedence, true),
TyKind::Pat(..) | TyKind::Array(..) | TyKind::Ptr(_) | TyKind::BareFn(_) => Position::DerefStable(precedence, true),
TyKind::Never
| TyKind::Tup(_)
| TyKind::Path(_) => Position::DerefStable(
Expand Down
4 changes: 4 additions & 0 deletions src/tools/clippy/clippy_utils/src/hir_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_ty(ty);
self.hash_array_length(len);
},
TyKind::Pat(ty, pat) => {
self.hash_ty(ty);
self.hash_pat(pat);
},
Comment on lines +949 to +952
Copy link
Member

Choose a reason for hiding this comment

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

Please also update eq_ty

pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {

TyKind::Ptr(ref mut_ty) => {
self.hash_ty(mut_ty.ty);
mut_ty.mutbl.hash(&mut self.s);
Expand Down
5 changes: 5 additions & 0 deletions src/tools/rustfmt/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,11 @@ impl Rewrite for ast::Ty {
self.span,
shape,
),
ast::TyKind::Pat(ref ty, ref pat) => {
let ty = ty.rewrite(context, shape)?;
let pat = pat.rewrite(context, shape)?;
Some(format!("{ty} is {pat}"))
},
Comment on lines +850 to +854
Copy link
Member

Choose a reason for hiding this comment

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

cc @rust-lang/style

This seems quite reasonable and noncontroversial to me, and I'd be surprised if there's any vehement objections to this formatting. However, want to make sure everyone has seen it in case we'd like a chance to discuss this as a team first.

In the event we do want to review as a team to identify and codify the formatting rules, then this can be changed to re-emit the original input contents in the interim via something like:

Suggested change
ast::TyKind::Pat(ref ty, ref pat) => {
let ty = ty.rewrite(context, shape)?;
let pat = pat.rewrite(context, shape)?;
Some(format!("{ty} is {pat}"))
},
ast::TyKind::Pat(..) => Some(context.snippet(self.span).to_owned()),

}
}
}
Expand Down
Loading