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 pattern types as the backing logic of rustc_scalar_valid_range attributes #107299

Closed
wants to merge 10 commits into from
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#![feature(negative_impls)]
#![feature(slice_internals)]
#![feature(stmt_expr_attributes)]
#![feature(structural_match)]
#![recursion_limit = "256"]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#![feature(once_cell)]
#![feature(rustc_attrs)]
#![feature(stmt_expr_attributes)]
#![feature(structural_match)]
#![feature(trusted_step)]
#![feature(try_blocks)]
#![recursion_limit = "256"]
Expand Down Expand Up @@ -2103,6 +2104,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
ProjectionElem::Deref => {
let base_ty = place_base.ty(self.body(), self.infcx.tcx).ty;

let base_ty = match *base_ty.kind() {
ty::Pat(inner, _) => inner,
_ => base_ty,
};

// Check the kind of deref to decide
match base_ty.kind() {
ty::Ref(_, _, mutbl) => {
Expand Down
82 changes: 78 additions & 4 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
}
}

#[instrument(level = "trace", skip(self))]
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
self.super_rvalue(rvalue, location);
let rval_ty = rvalue.ty(self.body(), self.tcx());
Expand Down Expand Up @@ -517,14 +518,13 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {

/// Checks that the types internal to the `place` match up with
/// what would be expected.
#[instrument(level = "trace", skip(self), ret)]
fn sanitize_place(
&mut self,
place: &Place<'tcx>,
location: Location,
context: PlaceContext,
) -> PlaceTy<'tcx> {
debug!("sanitize_place: {:?}", place);

let mut place_ty = PlaceTy::from_ty(self.body().local_decls[place.local].ty);

for elem in place.projection.iter() {
Expand Down Expand Up @@ -630,14 +630,14 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
}
}

#[instrument(level = "trace", skip(self), ret)]
fn sanitize_projection(
&mut self,
base: PlaceTy<'tcx>,
pi: PlaceElem<'tcx>,
place: &Place<'tcx>,
location: Location,
) -> PlaceTy<'tcx> {
debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, place);
let tcx = self.tcx();
let base_ty = base.ty;
match pi {
Expand Down Expand Up @@ -742,9 +742,17 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
ProjectionElem::OpaqueCast(ty) => {
let ty = self.sanitize_type(place, ty);
let ty = self.cx.normalize(ty, location);
let compare_ty = match *base.ty.kind() {
ty::Alias(ty::Opaque, _) => base.ty,
ty::Pat(inner, _) => inner,
_ => {
span_mirbug!(self, place, "tried to cast {} to {ty}", base.ty);
base.ty
}
};
self.cx
.eq_types(
base.ty,
compare_ty,
ty,
location.to_locations(),
ConstraintCategory::TypeAnnotation,
Expand Down Expand Up @@ -1310,6 +1318,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.check_operand(discr, term_location);

let switch_ty = discr.ty(body, tcx);
let switch_ty = switch_ty.strip_pattern().unwrap_or(switch_ty);
if !switch_ty.is_integral() && !switch_ty.is_char() && !switch_ty.is_bool() {
span_mirbug!(self, term, "bad SwitchInt discr ty {:?}", switch_ty);
}
Expand Down Expand Up @@ -2107,6 +2116,66 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}
}
CastKind::Patternize => {
let ty_from = op.ty(body, tcx);
let cast_ty_to = CastTy::from_ty(*ty);
match cast_ty_to {
Some(CastTy::Pat(to)) => {
if let Err(terr) = self.eq_types(
ty_from,
to,
location.to_locations(),
ConstraintCategory::Cast,
) {
span_mirbug!(
self,
rvalue,
"relating {:?} with {:?} yields {:?}",
ty_from,
to,
terr
)
}
}
_ => {
span_mirbug!(
self,
rvalue,
"Invalid Patternize cast {ty_from} -> {ty}",
)
}
}
}
CastKind::StripPattern => {
let ty_from = op.ty(body, tcx);
let cast_ty_from = CastTy::from_ty(ty_from);
match cast_ty_from {
Some(CastTy::Pat(from)) => {
if let Err(terr) = self.eq_types(
*ty,
from,
location.to_locations(),
ConstraintCategory::Cast,
) {
span_mirbug!(
self,
rvalue,
"relating {:?} with {:?} yields {:?}",
ty,
from,
terr
)
}
}
_ => {
span_mirbug!(
self,
rvalue,
"Invalid StripPattern cast {ty_from} -> {ty}",
)
}
}
}
CastKind::IntToFloat => {
let ty_from = op.ty(body, tcx);
let cast_ty_from = CastTy::from_ty(ty_from);
Expand Down Expand Up @@ -2427,6 +2496,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
ProjectionElem::Deref => {
let base_ty = Place::ty_from(borrowed_place.local, proj_base, body, tcx).ty;

let base_ty = match *base_ty.kind() {
ty::Pat(inner, _) => inner,
_ => base_ty,
};

debug!("add_reborrow_constraint - base_ty = {:?}", base_ty);
match base_ty.kind() {
ty::Ref(ref_region, _, mutbl) => {
Expand Down
18 changes: 7 additions & 11 deletions compiler/rustc_codegen_cranelift/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -610,17 +610,13 @@ fn codegen_stmt<'tcx>(
}
}
Rvalue::Cast(
CastKind::Pointer(PointerCast::UnsafeFnPointer),
ref operand,
to_ty,
)
| Rvalue::Cast(
CastKind::Pointer(PointerCast::MutToConstPointer),
ref operand,
to_ty,
)
| Rvalue::Cast(
CastKind::Pointer(PointerCast::ArrayToPointer),
CastKind::StripPattern
| CastKind::Patternize
| CastKind::Pointer(
PointerCast::UnsafeFnPointer
| PointerCast::MutToConstPointer
| PointerCast::ArrayToPointer,
),
ref operand,
to_ty,
) => {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,7 @@ pub fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll D
ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id),
// Type parameters from polymorphized functions.
ty::Param(_) => build_param_type_di_node(cx, t),
ty::Pat(inner, _) => return type_di_node(cx, inner),
_ => bug!("debuginfo: unexpected type in type_di_node(): {:?}", t),
};

Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_ssa/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,9 @@ pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
// HACK(eddyb) have to bitcast pointers until LLVM removes pointee types.
(bx.bitcast(lldata, lldata_ty), bx.bitcast(llextra, llextra_ty))
}
(&ty::Pat(a, a_pat), &ty::Pat(b, b_pat)) if a_pat == b_pat => {
unsize_ptr(bx, src, a, b, old_info)
}
_ => bug!("unsize_ptr: called on bad types"),
}
}
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,15 @@ 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);
write!(output, ",{:?}>", pat).unwrap();
} else {
write!(output, "{:?}", t).unwrap();
}
}
ty::Slice(inner_type) => {
if cpp_like_debuginfo {
output.push_str("slice2$<");
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_codegen_ssa/src/mir/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
_ => bug!("{} cannot be cast to a fn ptr", operand.layout.ty),
}
}
mir::CastKind::Pointer(PointerCast::UnsafeFnPointer) => {
mir::CastKind::Patternize
| mir::CastKind::StripPattern
| mir::CastKind::Pointer(PointerCast::UnsafeFnPointer) => {
// This is a no-op at the LLVM level.
operand.val
}
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_const_eval/src/const_eval/valtrees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
Ok(ty::ValTree::Leaf(val.assert_int()))
}

ty::Pat(..) => const_to_valtree_inner(ecx, &ecx.mplace_field(&place, 0).unwrap(), num_nodes),

// Raw pointers are not allowed in type level constants, as we cannot properly test them for
// equality at compile-time (see `ptr_guaranteed_cmp`).
// Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to
Expand Down Expand Up @@ -265,7 +267,7 @@ pub fn valtree_to_const_value<'tcx>(
let (param_env, ty) = param_env_ty.into_parts();
let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);

match ty.kind() {
match *ty.kind() {
ty::FnDef(..) => {
assert!(valtree.unwrap_branch().is_empty());
ConstValue::ZeroSized
Expand All @@ -276,6 +278,7 @@ pub fn valtree_to_const_value<'tcx>(
"ValTrees for Bool, Int, Uint, Float or Char should have the form ValTree::Leaf"
),
},
ty::Pat(ty, _) => valtree_to_const_value(tcx, param_env.and(ty), valtree),
ty::Ref(_, _, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Adt(..) => {
let mut place = match ty.kind() {
ty::Ref(_, inner_ty, _) => {
Expand Down
12 changes: 10 additions & 2 deletions compiler/rustc_const_eval/src/interpret/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
use rustc_middle::mir::CastKind::*;
// FIXME: In which cases should we trigger UB when the source is uninit?
match cast_kind {
Patternize | StripPattern => {
self.copy_op(src, dest, true)?;
}

Pointer(PointerCast::Unsize) => {
let cast_ty = self.layout_of(cast_ty)?;
self.unsize_into(src, cast_ty, dest)?;
Expand Down Expand Up @@ -368,10 +372,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
dest: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx> {
trace!("Unsizing {:?} of type {} into {:?}", *src, src.layout.ty, cast_ty.ty);
match (&src.layout.ty.kind(), &cast_ty.ty.kind()) {
let (src_ty, target_ty) = match (src.layout.ty.kind(), cast_ty.ty.kind()) {
(&ty::Pat(a, pat_a), &ty::Pat(b, pat_b)) if pat_a == pat_b => (a, b),
_ => (src.layout.ty, cast_ty.ty),
};
match (src_ty.kind(), target_ty.kind()) {
(&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(TypeAndMut { ty: c, .. }))
| (&ty::RawPtr(TypeAndMut { ty: s, .. }), &ty::RawPtr(TypeAndMut { ty: c, .. })) => {
self.unsize_into_ptr(src, dest, *s, *c)
self.unsize_into_ptr(src, dest, s, c)
}
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
assert_eq!(def_a, def_b);
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_const_eval/src/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>(
ty::Alias(..) | ty::Param(_) | ty::Placeholder(_) | ty::Infer(_) => {
throw_inval!(TooGeneric)
}
ty::Pat(..) => {
unimplemented!("pattern types need to calculate pattern from their pattern")
Copy link
Member

Choose a reason for hiding this comment

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

I can't parse this panic message... too many patterns?^^

}
ty::Bound(_, _) => bug!("bound ty during ctfe"),
ty::Bool
| ty::Char
Expand Down
30 changes: 25 additions & 5 deletions compiler/rustc_const_eval/src/interpret/validity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,27 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
) -> InterpResult<'tcx, bool> {
// Go over all the primitive types
let ty = value.layout.ty;
match ty.kind() {
match *ty.kind() {
ty::Pat(inner, _) => {
let mut value = value.clone();
value.layout.ty = inner;
// First visit the inner type to report more targetted errors
// if the value is already not valid at the inner type.
self.visit_value(&value)?;
// Then check the extra pattern restrictions.
let scalar = self.read_immediate(&value, "initialized scalar value")?;
match (*scalar, value.layout.abi) {
(Immediate::Scalar(scalar), Abi::Scalar(s))
| (Immediate::ScalarPair(scalar, _), Abi::ScalarPair(s, _)) => {
self.visit_scalar(scalar, s)?
}
other => span_bug!(
self.ecx.cur_span(),
"invalid abi {other:?} for pattern type {ty:?}"
),
}
Comment on lines +501 to +512
Copy link
Member

Choose a reason for hiding this comment

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

Isn't this duplicating the scalar range check after this match?

I assume for now patterns are restricted to things that can be actually represented in the Scalar ABI? If 0 | 5 | 10 is accepted as a pattern then we should properly check it here...

Ok(true)
}
ty::Bool => {
let value = self.read_scalar(value, "a boolean")?;
try_validation!(
Expand Down Expand Up @@ -545,11 +565,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
}
ty::Ref(_, ty, mutbl) => {
if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. }))
&& *mutbl == Mutability::Mut
&& mutbl == Mutability::Mut
{
// A mutable reference inside a const? That does not seem right (except if it is
// a ZST).
let layout = self.ecx.layout_of(*ty)?;
let layout = self.ecx.layout_of(ty)?;
if !layout.is_zst() {
throw_validation_failure!(self.path, { "mutable reference in a `const`" });
}
Expand Down Expand Up @@ -778,7 +798,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
Abi::Scalar(scalar_layout) => {
if !scalar_layout.is_uninit_valid() {
// There is something to check here.
let scalar = self.read_scalar(op, "initiailized scalar value")?;
let scalar = self.read_scalar(op, "initialized scalar value")?;
self.visit_scalar(scalar, scalar_layout)?;
}
}
Expand All @@ -788,7 +808,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
// the other must be init.
if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() {
let (a, b) =
self.read_immediate(op, "initiailized scalar value")?.to_scalar_pair();
self.read_immediate(op, "initialized scalar value")?.to_scalar_pair();
self.visit_scalar(a, a_layout)?;
self.visit_scalar(b, b_layout)?;
}
Expand Down
Loading