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

Function pointers as const generic arguments #64986

Merged
merged 10 commits into from
Oct 12, 2019
8 changes: 8 additions & 0 deletions src/librustc/mir/interpret/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,14 @@ impl<'tcx> AllocMap<'tcx> {
}
}

/// Panics if the `AllocId` does not refer to a function
pub fn unwrap_fn(&self, id: AllocId) -> Instance<'tcx> {
match self.get(id) {
Some(GlobalAlloc::Function(instance)) => instance,
_ => bug!("expected allocation ID {} to point to a function", id),
}
}

/// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. Trying to
/// call this function twice, even with the same `Allocation` will ICE the compiler.
pub fn set_alloc_id_memory(&mut self, id: AllocId, mem: &'tcx Allocation) {
Expand Down
9 changes: 8 additions & 1 deletion src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2602,7 +2602,14 @@ impl<'tcx> Debug for Constant<'tcx> {
impl<'tcx> Display for Constant<'tcx> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
write!(fmt, "const ")?;
write!(fmt, "{}", self.literal)
// FIXME make the default pretty printing of raw pointers more detailed. Here we output the
// debug representation of raw pointers, so that the raw pointers in the mir dump output are
// detailed and just not '{pointer}'.
if let ty::RawPtr(_) = self.literal.ty.kind {
write!(fmt, "{:?} : {}", self.literal.val, self.literal.ty)
} else {
write!(fmt, "{}", self.literal)
}
}
}

Expand Down
218 changes: 107 additions & 111 deletions src/librustc/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -863,125 +863,121 @@ pub trait PrettyPrinter<'tcx>:
}

let u8 = self.tcx().types.u8;
if let ty::FnDef(did, substs) = ct.ty.kind {
p!(print_value_path(did, substs));
return Ok(self);
}
if let ConstValue::Unevaluated(did, substs) = ct.val {
match self.tcx().def_kind(did) {
| Some(DefKind::Static)
| Some(DefKind::Const)
| Some(DefKind::AssocConst) => p!(print_value_path(did, substs)),
_ => if did.is_local() {
let span = self.tcx().def_span(did);
if let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span) {
p!(write("{}", snip))

match (ct.val, &ct.ty.kind) {
(_, ty::FnDef(did, substs)) => p!(print_value_path(*did, substs)),
(ConstValue::Unevaluated(did, substs), _) => {
match self.tcx().def_kind(did) {
| Some(DefKind::Static)
| Some(DefKind::Const)
| Some(DefKind::AssocConst) => p!(print_value_path(did, substs)),
_ => if did.is_local() {
let span = self.tcx().def_span(did);
if let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span) {
p!(write("{}", snip))
} else {
p!(write("_: "), print(ct.ty))
}
} else {
p!(write("_: "), print(ct.ty))
}
},
}
},
(ConstValue::Infer(..), _) => p!(write("_: "), print(ct.ty)),
(ConstValue::Param(ParamConst { name, .. }), _) => p!(write("{}", name)),
(ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Bool) =>
p!(write("{}", if data == 0 { "false" } else { "true" })),
(ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Float(ast::FloatTy::F32)) =>
p!(write("{}f32", Single::from_bits(data))),
(ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Float(ast::FloatTy::F64)) =>
p!(write("{}f64", Double::from_bits(data))),
(ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Uint(ui)) => {
let bit_size = Integer::from_attr(&self.tcx(), UnsignedInt(*ui)).size();
let max = truncate(u128::max_value(), bit_size);

if data == max {
p!(write("std::{}::MAX", ui))
} else {
p!(write("_: "), print(ct.ty))
},
}
return Ok(self);
}
if let ConstValue::Infer(..) = ct.val {
p!(write("_: "), print(ct.ty));
return Ok(self);
}
if let ConstValue::Param(ParamConst { name, .. }) = ct.val {
p!(write("{}", name));
return Ok(self);
}
if let ConstValue::Scalar(Scalar::Raw { data, .. }) = ct.val {
match ct.ty.kind {
ty::Bool => {
p!(write("{}", if data == 0 { "false" } else { "true" }));
return Ok(self);
},
ty::Float(ast::FloatTy::F32) => {
p!(write("{}f32", Single::from_bits(data)));
return Ok(self);
},
ty::Float(ast::FloatTy::F64) => {
p!(write("{}f64", Double::from_bits(data)));
return Ok(self);
},
ty::Uint(ui) => {
let bit_size = Integer::from_attr(&self.tcx(), UnsignedInt(ui)).size();
let max = truncate(u128::max_value(), bit_size);
p!(write("{}{}", data, ui))
};
},
(ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Int(i)) => {
let bit_size = Integer::from_attr(&self.tcx(), SignedInt(*i))
.size().bits() as u128;
let min = 1u128 << (bit_size - 1);
let max = min - 1;

let ty = self.tcx().lift(&ct.ty).unwrap();
let size = self.tcx().layout_of(ty::ParamEnv::empty().and(ty))
.unwrap()
.size;
match data {
d if d == min => p!(write("std::{}::MIN", i)),
d if d == max => p!(write("std::{}::MAX", i)),
_ => p!(write("{}{}", sign_extend(data, size) as i128, i))
}
},
(ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Char) =>
p!(write("{:?}", ::std::char::from_u32(data as u32).unwrap())),
(ConstValue::Scalar(_), ty::RawPtr(_)) => p!(write("{{pointer}}")),
(ConstValue::Scalar(Scalar::Ptr(ptr)), ty::FnPtr(_)) => {
let instance = {
let alloc_map = self.tcx().alloc_map.lock();
alloc_map.unwrap_fn(ptr.alloc_id)
};
p!(print_value_path(instance.def_id(), instance.substs));
},
_ => {
let printed = if let ty::Ref(_, ref_ty, _) = ct.ty.kind {
let byte_str = match (ct.val, &ref_ty.kind) {
(ConstValue::Scalar(Scalar::Ptr(ptr)), ty::Array(t, n)) if *t == u8 => {
let n = n.eval_usize(self.tcx(), ty::ParamEnv::empty());
Some(self.tcx()
.alloc_map.lock()
.unwrap_memory(ptr.alloc_id)
.get_bytes(&self.tcx(), ptr, Size::from_bytes(n)).unwrap())
},
(ConstValue::Slice { data, start, end }, ty::Slice(t)) if *t == u8 => {
// The `inspect` here is okay since we checked the bounds, and there are
// no relocations (we have an active slice reference here). We don't use
// this result to affect interpreter execution.
Some(data.inspect_with_undef_and_ptr_outside_interpreter(start..end))
},
_ => None,
};

if data == max {
p!(write("std::{}::MAX", ui))
if let Some(byte_str) = byte_str {
p!(write("b\""));
for &c in byte_str {
for e in std::ascii::escape_default(c) {
self.write_char(e as char)?;
}
}
p!(write("\""));
true
} else if let (ConstValue::Slice { data, start, end }, ty::Str) =
(ct.val, &ref_ty.kind)
{
// The `inspect` here is okay since we checked the bounds, and there are no
// relocations (we have an active `str` reference here). We don't use this
// result to affect interpreter execution.
let slice = data.inspect_with_undef_and_ptr_outside_interpreter(start..end);
let s = ::std::str::from_utf8(slice)
.expect("non utf8 str from miri");
p!(write("{:?}", s));
true
} else {
p!(write("{}{}", data, ui))
};
return Ok(self);
},
ty::Int(i) =>{
let bit_size = Integer::from_attr(&self.tcx(), SignedInt(i))
.size().bits() as u128;
let min = 1u128 << (bit_size - 1);
let max = min - 1;

let ty = self.tcx().lift(&ct.ty).unwrap();
let size = self.tcx().layout_of(ty::ParamEnv::empty().and(ty))
.unwrap()
.size;
match data {
d if d == min => p!(write("std::{}::MIN", i)),
d if d == max => p!(write("std::{}::MAX", i)),
_ => p!(write("{}{}", sign_extend(data, size) as i128, i))
}
return Ok(self);
},
ty::Char => {
p!(write("{:?}", ::std::char::from_u32(data as u32).unwrap()));
return Ok(self);
}
_ => {},
}
}
if let ty::Ref(_, ref_ty, _) = ct.ty.kind {
let byte_str = match (ct.val, &ref_ty.kind) {
(ConstValue::Scalar(Scalar::Ptr(ptr)), ty::Array(t, n)) if *t == u8 => {
let n = n.eval_usize(self.tcx(), ty::ParamEnv::empty());
Some(self.tcx()
.alloc_map.lock()
.unwrap_memory(ptr.alloc_id)
.get_bytes(&self.tcx(), ptr, Size::from_bytes(n)).unwrap())
},
(ConstValue::Slice { data, start, end }, ty::Slice(t)) if *t == u8 => {
// The `inspect` here is okay since we checked the bounds, and there are no
// relocations (we have an active slice reference here). We don't use this
// result to affect interpreter execution.
Some(data.inspect_with_undef_and_ptr_outside_interpreter(start..end))
},
(ConstValue::Slice { data, start, end }, ty::Str) => {
// The `inspect` here is okay since we checked the bounds, and there are no
// relocations (we have an active `str` reference here). We don't use this
// result to affect interpreter execution.
let slice = data.inspect_with_undef_and_ptr_outside_interpreter(start..end);
let s = ::std::str::from_utf8(slice)
.expect("non utf8 str from miri");
p!(write("{:?}", s));
return Ok(self);
},
_ => None,
};
if let Some(byte_str) = byte_str {
p!(write("b\""));
for &c in byte_str {
for e in std::ascii::escape_default(c) {
self.write_char(e as char)?;
false
}
} else {
false
};
if !printed {
// fallback
p!(write("{:?} : ", ct.val), print(ct.ty))
}
p!(write("\""));
return Ok(self);
}
}
p!(write("{:?} : ", ct.val), print(ct.ty));

};
Ok(self)
}
}
Expand Down
58 changes: 30 additions & 28 deletions src/librustc/ty/relate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::hir::def_id::DefId;
use crate::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
use crate::ty::{self, Ty, TyCtxt, TypeFoldable};
use crate::ty::error::{ExpectedFound, TypeError};
use crate::mir::interpret::{ConstValue, get_slice_bytes, Scalar};
use crate::mir::interpret::{ConstValue, get_slice_bytes};
use std::rc::Rc;
use std::iter;
use rustc_target::spec::abi;
Expand Down Expand Up @@ -561,37 +561,39 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
// implement both `PartialEq` and `Eq`, corresponding to
// `structural_match` types.
// FIXME(const_generics): check for `structural_match` synthetic attribute.
match (eagerly_eval(a), eagerly_eval(b)) {
let new_const_val = match (eagerly_eval(a), eagerly_eval(b)) {
(ConstValue::Infer(_), _) | (_, ConstValue::Infer(_)) => {
// The caller should handle these cases!
bug!("var types encountered in super_relate_consts: {:?} {:?}", a, b)
}
(ConstValue::Param(a_p), ConstValue::Param(b_p)) if a_p.index == b_p.index => {
Ok(a)
return Ok(a);
}
(ConstValue::Placeholder(p1), ConstValue::Placeholder(p2)) if p1 == p2 => {
Ok(a)
return Ok(a);
}
(a_val @ ConstValue::Scalar(Scalar::Raw { .. }), b_val @ _)
if a.ty == b.ty && a_val == b_val =>
{
Ok(tcx.mk_const(ty::Const {
val: a_val,
ty: a.ty,
}))
(ConstValue::Scalar(a_val), ConstValue::Scalar(b_val)) if a.ty == b.ty => {
if a_val == b_val {
Ok(ConstValue::Scalar(a_val))
} else if let ty::FnPtr(_) = a.ty.kind {
let alloc_map = tcx.alloc_map.lock();
let a_instance = alloc_map.unwrap_fn(a_val.to_ptr().unwrap().alloc_id);
let b_instance = alloc_map.unwrap_fn(b_val.to_ptr().unwrap().alloc_id);
if a_instance == b_instance {
oli-obk marked this conversation as resolved.
Show resolved Hide resolved
Ok(ConstValue::Scalar(a_val))
} else {
Err(TypeError::ConstMismatch(expected_found(relation, &a, &b)))
}
} else {
Err(TypeError::ConstMismatch(expected_found(relation, &a, &b)))
}
}

// FIXME(const_generics): we should either handle `Scalar::Ptr` or add a comment
// saying that we're not handling it intentionally.

(a_val @ ConstValue::Slice { .. }, b_val @ ConstValue::Slice { .. }) => {
let a_bytes = get_slice_bytes(&tcx, a_val);
let b_bytes = get_slice_bytes(&tcx, b_val);
if a_bytes == b_bytes {
Ok(tcx.mk_const(ty::Const {
val: a_val,
ty: a.ty,
}))
Ok(a_val)
} else {
Err(TypeError::ConstMismatch(expected_found(relation, &a, &b)))
}
Expand All @@ -602,16 +604,16 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
// FIXME(const_generics): this is wrong, as it is a projection
(ConstValue::Unevaluated(a_def_id, a_substs),
ConstValue::Unevaluated(b_def_id, b_substs)) if a_def_id == b_def_id => {
let substs =
relation.relate_with_variance(ty::Variance::Invariant, &a_substs, &b_substs)?;
Ok(tcx.mk_const(ty::Const {
val: ConstValue::Unevaluated(a_def_id, &substs),
ty: a.ty,
}))
}

_ => Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))),
}
let substs =
relation.relate_with_variance(ty::Variance::Invariant, &a_substs, &b_substs)?;
Ok(ConstValue::Unevaluated(a_def_id, &substs))
}
_ => Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))),
};
new_const_val.map(|val| tcx.mk_const(ty::Const {
val,
ty: a.ty,
}))
}

impl<'tcx> Relate<'tcx> for &'tcx ty::List<ty::ExistentialPredicate<'tcx>> {
Expand Down
17 changes: 9 additions & 8 deletions src/librustc_mir/monomorphize/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1265,7 +1265,14 @@ fn collect_const<'tcx>(
) {
debug!("visiting const {:?}", constant);

match constant.val {
let param_env = ty::ParamEnv::reveal_all();
let substituted_constant = tcx.subst_and_normalize_erasing_regions(
param_substs,
param_env,
&constant,
);

match substituted_constant.val {
ConstValue::Scalar(Scalar::Ptr(ptr)) =>
collect_miri(tcx, ptr.alloc_id, output),
ConstValue::Slice { data: alloc, start: _, end: _ } |
Expand All @@ -1275,12 +1282,6 @@ fn collect_const<'tcx>(
}
}
ConstValue::Unevaluated(def_id, substs) => {
let param_env = ty::ParamEnv::reveal_all();
let substs = tcx.subst_and_normalize_erasing_regions(
param_substs,
param_env,
&substs,
);
let instance = ty::Instance::resolve(tcx,
param_env,
def_id,
Expand All @@ -1297,7 +1298,7 @@ fn collect_const<'tcx>(
tcx.def_span(def_id), "collection encountered polymorphic constant",
),
}
}
},
_ => {},
}
}
Loading