Skip to content

Start using pattern types in libcore #136006

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 4 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
3 changes: 3 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2469,6 +2469,9 @@ pub enum TyPatKind {
/// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`).
Range(Option<P<AnonConst>>, Option<P<AnonConst>>, Spanned<RangeEnd>),

/// A `!null` pattern for raw pointers.
NotNull,

Or(ThinVec<P<TyPat>>),

/// Placeholder for a pattern that wasn't syntactically well formed in some way.
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@ pub fn walk_ty_pat<T: MutVisitor>(vis: &mut T, ty: &mut P<TyPat>) {
visit_opt(end, |c| vis.visit_anon_const(c));
}
TyPatKind::Or(variants) => visit_thin_vec(variants, |p| vis.visit_ty_pat(p)),
TyPatKind::Err(_) => {}
TyPatKind::NotNull | TyPatKind::Err(_) => {}
}
vis.visit_span(span);
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,7 @@ pub fn walk_ty_pat<'a, V: Visitor<'a>>(visitor: &mut V, tp: &'a TyPat) -> V::Res
visit_opt!(visitor, visit_anon_const, end);
}
TyPatKind::Or(variants) => walk_list!(visitor, visit_ty_pat, variants),
TyPatKind::Err(_) => {}
TyPatKind::NotNull | TyPatKind::Err(_) => {}
}
V::Result::output()
}
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_ast_lowering/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
// return inner to be processed in next loop
PatKind::Paren(inner) => pattern = inner,
PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", pattern.span),
PatKind::MacCall(_) => {
panic!("{pattern:#?} shouldn't exist here")
}
PatKind::Err(guar) => break hir::PatKind::Err(*guar),
}
};
Expand Down Expand Up @@ -464,6 +466,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
)
}),
),
TyPatKind::NotNull => hir::TyPatKind::NotNull,
TyPatKind::Or(variants) => {
hir::TyPatKind::Or(self.arena.alloc_from_iter(
variants.iter().map(|pat| self.lower_ty_pat_mut(pat, base_type)),
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1172,6 +1172,7 @@ impl<'a> State<'a> {
self.print_expr_anon_const(end, &[]);
}
}
rustc_ast::TyPatKind::NotNull => self.word("!null"),
rustc_ast::TyPatKind::Or(variants) => {
let mut first = true;
for pat in variants {
Expand Down
28 changes: 17 additions & 11 deletions compiler/rustc_builtin_macros/src/pattern_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,23 @@ fn parse_pat_ty<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P
let ty = parser.parse_ty()?;
parser.expect_keyword(exp!(Is))?;

let pat = pat_to_ty_pat(
cx,
parser
.parse_pat_no_top_guard(
None,
RecoverComma::No,
RecoverColon::No,
CommaRecoveryMode::EitherTupleOrPipe,
)?
.into_inner(),
);
let start = parser.token.span;
let pat = if parser.eat(exp!(Bang)) {
parser.expect_keyword(exp!(Null))?;
ty_pat(TyPatKind::NotNull, start.to(parser.token.span))
} else {
pat_to_ty_pat(
cx,
parser
.parse_pat_no_top_guard(
None,
RecoverComma::No,
RecoverColon::No,
CommaRecoveryMode::EitherTupleOrPipe,
)?
.into_inner(),
)
};

if parser.token != token::Eof {
parser.unexpected()?;
Expand Down
13 changes: 12 additions & 1 deletion compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,18 @@ pub(crate) fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) ->
AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id),
},
ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id),
_ => bug!("debuginfo: unexpected type in type_di_node(): {:?}", t),
ty::Pat(base, _) => return type_di_node(cx, base),
// FIXME(unsafe_binders): impl debug info
ty::UnsafeBinder(_) => unimplemented!(),
ty::Alias(..)
| ty::Param(_)
| ty::Bound(..)
| ty::Infer(_)
| ty::Placeholder(_)
| ty::CoroutineWitness(..)
| ty::Error(_) => {
bug!("debuginfo: unexpected type in type_di_node(): {:?}", t)
}
};

{
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_ssa/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ pub(crate) fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
) -> (Bx::Value, Bx::Value) {
debug!("unsize_ptr: {:?} => {:?}", src_ty, dst_ty);
match (src_ty.kind(), dst_ty.kind()) {
(&ty::Pat(a, _), &ty::Pat(b, _)) => unsize_ptr(bx, src, a, b, old_info),
(&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(b, _))
| (&ty::RawPtr(a, _), &ty::RawPtr(b, _)) => {
assert_eq!(bx.cx().type_is_sized(a), old_info.is_none());
Expand Down
13 changes: 13 additions & 0 deletions compiler/rustc_codegen_ssa/src/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
//
// This is also relevant for `Pin<&mut Self>`, where we need to peel the
// `Pin`.

loop {
match *op.layout.ty.kind() {
ty::Ref(..) | ty::RawPtr(..) => break,
ty::Pat(inner, _) => op.layout = bx.layout_of(inner),
_ => {
let (idx, _) = op.layout.non_1zst_field(bx).expect(
"not exactly one non-1-ZST field in a `DispatchFromDyn` type",
);
op = op.extract_field(self, bx, idx);
}
}
}
while !op.layout.ty.is_raw_ptr() && !op.layout.ty.is_ref() {
let (idx, _) = op.layout.non_1zst_field(bx).expect(
"not exactly one non-1-ZST field in a `DispatchFromDyn` type",
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_const_eval/src/interpret/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let (_, field) = layout.non_1zst_field(self).unwrap();
self.unfold_transparent(field, may_unfold)
}
ty::Pat(base, _) => self.layout_of(*base).expect(
"if the layout of a pattern type could be computed, so can the layout of its base",
),
// Not a transparent type, no further unfolding.
_ => layout,
}
Expand Down
14 changes: 14 additions & 0 deletions compiler/rustc_const_eval/src/interpret/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
) -> InterpResult<'tcx> {
trace!("Unsizing {:?} of type {} into {}", *src, src.layout.ty, cast_ty.ty);
match (src.layout.ty.kind(), cast_ty.ty.kind()) {
(&ty::Pat(s, s_pat), &ty::Pat(c, c_pat)) if s_pat == c_pat => {
let mut src = src.clone();
src.layout = self.layout_of(s)?;
let mut dest = dest.clone();
dest.layout = self.layout_of(c)?;
let cast_ty = match *cast_ty.ty.kind() {
ty::Pat(base, pat) if pat == c_pat => self.layout_of(base)?,
_ => span_bug!(
self.cur_span(),
"unsize_into: invalid cast ty for pattern type: {cast_ty:#?}"
),
};
self.unsize_into(&src, cast_ty, &dest)
}
(&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(c, _))
| (&ty::RawPtr(s, _), &ty::RawPtr(c, _)) => self.unsize_into_ptr(src, dest, s, c),
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
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 @@ -1248,6 +1248,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
// Range patterns are precisely reflected into `valid_range` and thus
// handled fully by `visit_scalar` (called below).
ty::PatternKind::Range { .. } => {},
ty::PatternKind::NotNull => {},

// FIXME(pattern_types): check that the value is covered by one of the variants.
// For now, we rely on layout computation setting the scalar's `valid_range` to
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_const_eval/src/interpret/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,12 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized {
);
// ... that contains a `NonNull`... (gladly, only a single field here)
assert_eq!(nonnull_ptr.layout().fields.count(), 1);
let raw_ptr = self.ecx().project_field(&nonnull_ptr, 0)?; // the actual raw ptr
let pat_ty = self.ecx().project_field(&nonnull_ptr, 0)?; // `*mut T is !null`
let base = match *pat_ty.layout().ty.kind() {
ty::Pat(base, _) => self.ecx().layout_of(base)?,
_ => unreachable!(),
};
let raw_ptr = pat_ty.transmute(base, self.ecx())?; // The actual raw pointer
// ... whose only field finally is a raw ptr we can dereference.
self.visit_box(ty, &raw_ptr)?;

Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1813,6 +1813,9 @@ pub enum TyPatKind<'hir> {
/// A range pattern (e.g., `1..=2` or `1..2`).
Range(&'hir ConstArg<'hir>, &'hir ConstArg<'hir>),

/// A pattern that excludes null pointers
NotNull,

/// A list of patterns where only one needs to be satisfied
Or(&'hir [TyPat<'hir>]),

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir/src/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,7 @@ pub fn walk_ty_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v TyPat<'v>)
try_visit!(visitor.visit_const_arg_unambig(upper_bound));
}
TyPatKind::Or(patterns) => walk_list!(visitor, visit_pattern_type_pattern, patterns),
TyPatKind::Err(_) => (),
TyPatKind::NotNull | TyPatKind::Err(_) => (),
}
V::Result::output()
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ hir_analysis_coerce_unsized_field_validity = for `{$ty}` to have a valid impleme

hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures

hir_analysis_coerce_same_pat_kind = only pattern types with the same pattern can be coerced between each other

hir_analysis_coerce_zero = implementing `{$trait_name}` requires a field to be coerced

hir_analysis_coercion_between_struct_same_note = expected coercion between the same definition; expected `{$source_path}`, found `{$target_path}`
Expand Down
39 changes: 33 additions & 6 deletions compiler/rustc_hir_analysis/src/coherence/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,18 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
// in the compiler (in particular, all the call ABI logic) will treat them as repr(transparent)
// even if they do not carry that attribute.
match (source.kind(), target.kind()) {
(&ty::Pat(_, pat_a), &ty::Pat(_, pat_b)) => {
if pat_a != pat_b {
return Err(tcx.dcx().emit_err(errors::CoerceSamePatKind {
span,
trait_name,
pat_a: pat_a.to_string(),
pat_b: pat_b.to_string(),
}));
}
Ok(())
}

(&ty::Ref(r_a, _, mutbl_a), ty::Ref(r_b, _, mutbl_b))
if r_a == *r_b && mutbl_a == *mutbl_b =>
{
Expand Down Expand Up @@ -416,6 +428,18 @@ pub(crate) fn coerce_unsized_info<'tcx>(
(mt_a.ty, mt_b.ty, unsize_trait, None, span)
};
let (source, target, trait_def_id, kind, field_span) = match (source.kind(), target.kind()) {
(&ty::Pat(ty_a, pat_a), &ty::Pat(ty_b, pat_b)) => {
if pat_a != pat_b {
return Err(tcx.dcx().emit_err(errors::CoerceSamePatKind {
span,
trait_name,
pat_a: pat_a.to_string(),
pat_b: pat_b.to_string(),
}));
}
(ty_a, ty_b, coerce_unsized_trait, None, span)
}

(&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => {
infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
Expand Down Expand Up @@ -715,13 +739,16 @@ fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), Err
let impl_span = tcx.def_span(checker.impl_def_id);
let self_ty = tcx.impl_trait_ref(checker.impl_def_id).unwrap().instantiate_identity().self_ty();

let is_permitted_primitive = match *self_ty.kind() {
ty::Adt(def, _) => def.is_box(),
ty::Uint(..) | ty::Int(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) => true,
_ => false,
};
fn is_permitted_primitive(self_ty: Ty<'_>) -> bool {
match *self_ty.kind() {
ty::Adt(def, _) => def.is_box(),
ty::Uint(..) | ty::Int(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) => true,
ty::Pat(base, _) => is_permitted_primitive(base),
_ => false,
}
}

if is_permitted_primitive
if is_permitted_primitive(self_ty)
&& let Ok(layout) = tcx.layout_of(typing_env.as_query_input(self_ty))
&& layout.layout.is_pointer_like(&tcx.data_layout)
{
Expand Down
6 changes: 1 addition & 5 deletions compiler/rustc_hir_analysis/src/coherence/orphan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,8 @@ 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::Pat(..)
| ty::Char
| ty::Int(..)
| ty::Uint(..)
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1280,6 +1280,16 @@ pub(crate) struct CoerceUnsizedNonStruct {
pub trait_name: &'static str,
}

#[derive(Diagnostic)]
#[diag(hir_analysis_coerce_same_pat_kind)]
pub(crate) struct CoerceSamePatKind {
#[primary_span]
pub span: Span,
pub trait_name: &'static str,
pub pat_a: String,
pub pat_b: String,
}

#[derive(Diagnostic)]
#[diag(hir_analysis_coerce_unsized_may, code = E0377)]
pub(crate) struct CoerceSameStruct {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2751,6 +2751,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.span_delayed_bug(ty_span, "invalid base type for range pattern")),
}
}
hir::TyPatKind::NotNull => Ok(ty::PatternKind::NotNull),
hir::TyPatKind::Or(patterns) => {
self.tcx()
.mk_patterns_from_iter(patterns.iter().map(|pat| {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/src/variance/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
self.add_constraints_from_const(current, start, variance);
self.add_constraints_from_const(current, end, variance);
}
ty::PatternKind::NotNull => {}
ty::PatternKind::Or(patterns) => {
for pat in patterns {
self.add_constraints_from_pat(current, variance, pat)
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_hir_pretty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1883,6 +1883,10 @@ impl<'a> State<'a> {
self.word("..=");
self.print_const_arg(end);
}
TyPatKind::NotNull => {
self.word_space("not");
self.word("null");
}
TyPatKind::Or(patterns) => {
self.popen();
let mut first = true;
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_lint/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,7 @@ fn pat_ty_is_known_nonnull<'tcx>(
// to ensure we aren't wrapping over zero.
start > 0 && end >= start
}
ty::PatternKind::NotNull => true,
ty::PatternKind::Or(patterns) => {
patterns.iter().all(|pat| pat_ty_is_known_nonnull(tcx, typing_env, pat))
}
Expand Down Expand Up @@ -1063,7 +1064,9 @@ fn get_nullable_type_from_pat<'tcx>(
pat: ty::Pattern<'tcx>,
) -> Option<Ty<'tcx>> {
match *pat {
ty::PatternKind::Range { .. } => get_nullable_type(tcx, typing_env, base),
ty::PatternKind::NotNull | ty::PatternKind::Range { .. } => {
get_nullable_type(tcx, typing_env, base)
}
ty::PatternKind::Or(patterns) => {
let first = get_nullable_type_from_pat(tcx, typing_env, base, patterns[0])?;
for &pat in &patterns[1..] {
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_middle/src/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ pub enum SelectionCandidate<'tcx> {
/// types generated for a fn pointer type (e.g., `fn(int) -> int`)
FnPointerCandidate,

/// Builtin impl of the `PointerLike` trait.
PointerLikeCandidate,

TraitAliasCandidate,

/// Matching `dyn Trait` with a supertrait of `Trait`. The index is the
Expand Down
11 changes: 10 additions & 1 deletion compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -822,11 +822,20 @@ where
| ty::FnDef(..)
| ty::CoroutineWitness(..)
| ty::Foreign(..)
| ty::Pat(_, _)
| ty::Dynamic(_, _, ty::Dyn) => {
bug!("TyAndLayout::field({:?}): not applicable", this)
}

// May contain wide pointers
ty::Pat(base, pat) => match *pat {
ty::PatternKind::NotNull => {
field_ty_or_layout(TyAndLayout { ty: base, ..this }, cx, i)
}
ty::PatternKind::Range { .. } | ty::PatternKind::Or(_) => {
bug!("TyAndLayout::field({this:?}): only applicable to !null patterns")
}
},

ty::UnsafeBinder(bound_ty) => {
let ty = tcx.instantiate_bound_regions_with_erased(bound_ty.into());
field_ty_or_layout(TyAndLayout { ty, ..this }, cx, i)
Expand Down
Loading
Loading