Skip to content
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

Implement minimal, internal-only pattern types in the type system #120131

Merged
merged 7 commits into from
Apr 8, 2024
Merged
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
3 changes: 3 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2152,6 +2152,9 @@ pub enum TyKind {
MacCall(P<MacCall>),
/// Placeholder for a `va_list`.
CVarArgs,
/// Pattern types like `pattern_type!(u32 is 1..=)`, which is the same as `NonZeroU32`,
/// just as part of the type system.
Pat(P<Ty>, P<Pat>),
/// Sometimes we need a dummy value when no error has occurred.
Dummy,
/// Placeholder for a kind that has failed to be defined.
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 @@ -502,6 +502,10 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
}
TyKind::Tup(tys) => visit_thin_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 @@ -446,6 +446,10 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result {
}
try_visit!(visitor.visit_path(path, typ.id));
}
TyKind::Pat(ty, pat) => {
try_visit!(visitor.visit_ty(ty));
try_visit!(visitor.visit_pat(pat));
}
TyKind::Array(ty, length) => {
try_visit!(visitor.visit_ty(ty));
try_visit!(visitor.visit_anon_const(length));
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_ast_lowering/src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,4 +381,8 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
ArrayLen::Body(..) => intravisit::walk_array_len(self, len),
}
}

fn visit_pattern_type_pattern(&mut self, p: &'hir hir::Pat<'hir>) {
self.visit_pat(p)
}
}
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 @@ -1463,7 +1463,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
}
}
TyKind::MacCall(_) => panic!("`TyKind::MacCall` should have been expanded by now"),
TyKind::Pat(ty, pat) => hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_pat(pat)),
TyKind::MacCall(_) => {
span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now")
}
TyKind::CVarArgs => {
let guar = self.dcx().span_delayed_bug(
t.span,
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 @@ -332,6 +332,9 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
ast::TyKind::Never => {
gate!(&self, never_type, ty.span, "the `!` type is experimental");
}
ast::TyKind::Pat(..) => {
gate!(&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 @@ -1188,6 +1188,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_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1606,6 +1606,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
| ty::Foreign(_)
| ty::Str
| ty::Array(_, _)
| ty::Pat(_, _)
| ty::Slice(_)
| ty::FnDef(_, _)
| ty::FnPtr(_)
Expand Down Expand Up @@ -1648,6 +1649,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
| ty::Foreign(_)
| ty::Str
| ty::Array(_, _)
| ty::Pat(_, _)
| ty::Slice(_)
| ty::RawPtr(_, _)
| ty::Ref(_, _, _)
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_builtin_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ mod format;
mod format_foreign;
mod global_allocator;
mod log_syntax;
mod pattern_type;
mod source_util;
mod test;
mod trace_macros;
Expand Down Expand Up @@ -95,6 +96,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
log_syntax: log_syntax::expand_log_syntax,
module_path: source_util::expand_mod,
option_env: env::expand_option_env,
pattern_type: pattern_type::expand,
std_panic: edition_panic::expand_panic,
stringify: source_util::expand_stringify,
trace_macros: trace_macros::expand_trace_macros,
Expand Down
29 changes: 29 additions & 0 deletions compiler/rustc_builtin_macros/src/pattern_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use rustc_ast::{ast, ptr::P, tokenstream::TokenStream, Pat, Ty};
use rustc_errors::PResult;
use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult};
use rustc_span::{sym, Span};

pub fn expand<'cx>(
cx: &'cx mut ExtCtxt<'_>,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
let (ty, pat) = match parse_pat_ty(cx, tts) {
Ok(parsed) => parsed,
Err(err) => {
return ExpandResult::Ready(DummyResult::any(sp, err.emit()));
}
};

ExpandResult::Ready(base::MacEager::ty(cx.ty(sp, ast::TyKind::Pat(ty, pat))))
}

fn parse_pat_ty<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P<Ty>, P<Pat>)> {
let mut parser = cx.new_parser_from_tts(stream);

let ty = parser.parse_ty()?;
parser.eat_keyword(sym::is);
let pat = parser.parse_pat_no_top_alt(None, None)?;

Ok((ty, pat))
}
10 changes: 10 additions & 0 deletions compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,16 @@ fn push_debuginfo_type_name<'tcx>(
}
}
}
ty::Pat(inner_type, pat) => {
if cpp_like_debuginfo {
output.push_str("pat$<");
push_debuginfo_type_name(tcx, inner_type, true, output, visited);
// FIXME(wg-debugging): implement CPP like printing for patterns.
write!(output, ",{:?}>", pat).unwrap();
oli-obk marked this conversation as resolved.
Show resolved Hide resolved
} else {
write!(output, "{:?}", t).unwrap();
}
}
ty::Slice(inner_type) => {
if cpp_like_debuginfo {
output.push_str("slice2$<");
Expand Down
16 changes: 14 additions & 2 deletions compiler/rustc_const_eval/src/const_eval/valtrees.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_middle::mir;
use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
Expand Down Expand Up @@ -98,6 +99,16 @@ fn const_to_valtree_inner<'tcx>(
Ok(ty::ValTree::Leaf(val.assert_int()))
}

ty::Pat(base, ..) => {
let mut place = place.clone();
// The valtree of the base type is the same as the valtree of the pattern type.
// Since the returned valtree does not contain the type or layout, we can just
// switch to the base type.
place.layout = ecx.layout_of(*base).unwrap();
oli-obk marked this conversation as resolved.
Show resolved Hide resolved
ensure_sufficient_stack(|| const_to_valtree_inner(ecx, &place, num_nodes))
},


ty::RawPtr(_, _) => {
// Not all raw pointers are allowed, as we cannot properly test them for
// equality at compile-time (see `ptr_guaranteed_cmp`).
Expand Down Expand Up @@ -273,7 +284,7 @@ pub fn valtree_to_const_value<'tcx>(

let (param_env, ty) = param_env_ty.into_parts();

match ty.kind() {
match *ty.kind() {
ty::FnDef(..) => {
assert!(valtree.unwrap_branch().is_empty());
mir::ConstValue::ZeroSized
Expand All @@ -286,10 +297,11 @@ pub fn valtree_to_const_value<'tcx>(
),
}
}
ty::Pat(ty, _) => valtree_to_const_value(tcx, param_env.and(ty), valtree),
ty::Ref(_, inner_ty, _) => {
let mut ecx =
mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No);
let imm = valtree_to_ref(&mut ecx, valtree, *inner_ty);
let imm = valtree_to_ref(&mut ecx, valtree, inner_ty);
let imm = ImmTy::from_immediate(imm, tcx.layout_of(param_env_ty).unwrap());
op_to_const(&ecx, &imm.into(), /* for diagnostics */ false)
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_const_eval/src/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {

ty::Tuple(tys) => tys.last().iter().all(|ty| is_very_trivially_sized(**ty)),

ty::Pat(ty, ..) => is_very_trivially_sized(*ty),

// We don't want to do any queries, so there is not much we can do with ADTs.
ty::Adt(..) => false,

Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_const_eval/src/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>(
ty::Alias(..) | ty::Param(_) | ty::Placeholder(_) | ty::Infer(_) => {
throw_inval!(TooGeneric)
}
ty::Pat(_, pat) => match **pat {
ty::PatternKind::Range { .. } => ConstValue::from_target_usize(0u64, &tcx),
// Future pattern kinds may have more variants
},
ty::Bound(_, _) => bug!("bound ty during ctfe"),
ty::Bool
| ty::Char
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_const_eval/src/interpret/validity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
| ty::Str
| ty::Dynamic(..)
| ty::Closure(..)
| ty::Pat(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..) => Ok(false),
// Some types only occur during typechecking, they have no layout.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_const_eval/src/util/type_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
| ty::Uint(_)
| ty::Float(_)
| ty::Str
| ty::Pat(_, _)
| ty::Array(_, _)
| ty::Slice(_)
| ty::RawPtr(_, _)
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 @@ -215,6 +215,8 @@ declare_features! (
(internal, omit_gdb_pretty_printer_section, "1.5.0", None),
/// Set the maximum pattern complexity allowed (not limited by default).
(internal, pattern_complexity, "1.78.0", None),
/// Allows using pattern types.
(internal, pattern_types, "CURRENT_RUSTC_VERSION", Some(54882)),
/// Allows using `#[prelude_import]` on glob `use` items.
(internal, prelude_import, "1.2.0", None),
/// Used to identify crates that contain the profiler runtime.
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 @@ -2624,6 +2624,8 @@ pub enum TyKind<'hir> {
Infer,
/// Placeholder for a type that has failed to be defined.
Err(rustc_span::ErrorGuaranteed),
/// Pattern types (`pattern_type!(u32 is 1..)`)
Pat(&'hir Ty<'hir>, &'hir Pat<'hir>),
}

#[derive(Debug, Clone, Copy, HashStable_Generic)]
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_hir/src/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,11 @@ pub trait Visitor<'v>: Sized {
fn visit_ty(&mut self, t: &'v Ty<'v>) -> Self::Result {
walk_ty(self, t)
}
fn visit_pattern_type_pattern(&mut self, _p: &'v Pat<'v>) {
// Do nothing. Only a few visitors need to know the details of the pattern type,
// and they opt into it. All other visitors will just choke on our fake patterns
// because they aren't in a body.
}
fn visit_generic_param(&mut self, p: &'v GenericParam<'v>) -> Self::Result {
walk_generic_param(self, p)
}
Expand Down Expand Up @@ -882,6 +887,10 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul
TyKind::AnonAdt(item_id) => {
try_visit!(visitor.visit_nested_item(item_id));
}
TyKind::Pat(ty, pat) => {
try_visit!(visitor.visit_ty(ty));
try_visit!(visitor.visit_pattern_type_pattern(pat));
}
}
V::Result::output()
}
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,9 @@ hir_analysis_pass_to_variadic_function = can't pass `{$ty}` to variadic function
.suggestion = cast the value to `{$cast_ty}`
.help = cast the value to `{$cast_ty}`
hir_analysis_pattern_type_non_const_range = "range patterns must have constant range start and end"
hir_analysis_pattern_type_wild_pat = "wildcard patterns are not permitted for pattern types"
Copy link
Member

Choose a reason for hiding this comment

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

why error on wildcards. is it just so that you dont have to support it as a PatternKind in

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yea. I can keep ICEing, but I used wildcard patterns for tests before implementing range patterns

.label = "this type is the same as the inner type without a pattern"
hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind}
.label = not allowed in type signatures
Expand Down
8 changes: 7 additions & 1 deletion compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,12 @@ impl<'tcx> InherentCollect<'tcx> {
let id = id.owner_id.def_id;
let item_span = self.tcx.def_span(id);
let self_ty = self.tcx.type_of(id).instantiate_identity();
let self_ty = self.tcx.peel_off_weak_alias_tys(self_ty);
let mut self_ty = self.tcx.peel_off_weak_alias_tys(self_ty);
// We allow impls on pattern types exactly when we allow impls on the base type.
// FIXME(pattern_types): Figure out the exact coherence rules we want here.
while let ty::Pat(base, _) = *self_ty.kind() {
self_ty = base;
}
oli-obk marked this conversation as resolved.
Show resolved Hide resolved
match *self_ty.kind() {
ty::Adt(def, _) => self.check_def_id(id, self_ty, def.did()),
ty::Foreign(did) => self.check_def_id(id, self_ty, did),
Expand All @@ -154,6 +159,7 @@ impl<'tcx> InherentCollect<'tcx> {
ty::Dynamic(..) => {
Err(self.tcx.dcx().emit_err(errors::InherentDyn { span: item_span }))
}
ty::Pat(_, _) => unreachable!(),
ty::Bool
| ty::Char
| ty::Int(_)
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_hir_analysis/src/coherence/orphan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@ pub(crate) fn orphan_check_impl(
(LocalImpl::Disallow { problematic_kind }, NonlocalImpl::DisallowOther)
}

ty::Pat(..) => (
LocalImpl::Disallow { problematic_kind: "pattern type" },
NonlocalImpl::DisallowOther,
),

ty::Bool
| ty::Char
| ty::Int(..)
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use rustc_errors::{
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::Ty;
use rustc_span::{symbol::Ident, Span, Symbol};
mod pattern_types;
pub use pattern_types::*;

#[derive(Diagnostic)]
#[diag(hir_analysis_ambiguous_assoc_item)]
Expand Down Expand Up @@ -1629,3 +1631,10 @@ pub struct OpaqueCapturesHigherRankedLifetime {
pub decl_span: Span,
pub bad_place: &'static str,
}

#[derive(Diagnostic)]
#[diag(hir_analysis_pattern_type_non_const_range)]
pub struct NonConstRange {
#[primary_span]
pub span: Span,
}
9 changes: 9 additions & 0 deletions compiler/rustc_hir_analysis/src/errors/pattern_types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use rustc_macros::Diagnostic;
use rustc_span::Span;

#[derive(Diagnostic)]
#[diag(hir_analysis_pattern_type_wild_pat)]
pub struct WildPatTy {
#[primary_span]
pub span: Span,
}
Loading
Loading