Re-use std::sealed::Sealed in os/linux/process.
This uses `std::sealed::Sealed` in `std::os::linux::process` instead of defining new `Sealed` traits there.
[[package]]
name = "cargo"
-version = "0.56.0"
+version = "0.57.0"
dependencies = [
"anyhow",
"atty",
[[package]]
name = "cargo-util"
-version = "0.1.0"
+version = "0.1.1"
dependencies = [
"anyhow",
"core-foundation",
[[package]]
name = "mdbook"
-version = "0.4.11"
+version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ee73932975c44c485e541416d7c30abb31a053af7e49682f6e856f1e4d6ab2a"
+checksum = "0651782b4cc514c3f98c0acf9b5af1101a735bbe1ac6852bb1a90cb91bdf0ed4"
dependencies = [
"ammonia",
"anyhow",
"libc",
"object 0.25.2",
"pathdiff",
+ "regex",
"rustc_apfloat",
"rustc_ast",
"rustc_attr",
then: &Block,
else_opt: Option<&Expr>,
) -> hir::ExprKind<'hir> {
- macro_rules! make_if {
- ($opt:expr) => {{
- let cond = self.lower_expr(cond);
- let then_expr = self.lower_block_expr(then);
- hir::ExprKind::If(cond, self.arena.alloc(then_expr), $opt)
- }};
- }
- if let Some(rslt) = else_opt {
- make_if!(Some(self.lower_expr(rslt)))
- } else {
- make_if!(None)
- }
+ let cond = self.lower_expr(cond);
+ let then = self.arena.alloc(self.lower_block_expr(then));
+ let els = else_opt.map(|els| self.lower_expr(els));
+ hir::ExprKind::If(cond, then, els)
}
fn lower_expr_if_let(
Size,
}
-#[derive(Copy, Clone, PartialEq)]
-pub enum UnwindAttr {
- Allowed,
- Aborts,
-}
-
-/// Determine what `#[unwind]` attribute is present in `attrs`, if any.
-pub fn find_unwind_attr(sess: &Session, attrs: &[Attribute]) -> Option<UnwindAttr> {
- attrs.iter().fold(None, |ia, attr| {
- if sess.check_name(attr, sym::unwind) {
- if let Some(meta) = attr.meta() {
- if let MetaItemKind::List(items) = meta.kind {
- if items.len() == 1 {
- if items[0].has_name(sym::allowed) {
- return Some(UnwindAttr::Allowed);
- } else if items[0].has_name(sym::aborts) {
- return Some(UnwindAttr::Aborts);
- }
- }
-
- struct_span_err!(
- sess.diagnostic(),
- attr.span,
- E0633,
- "malformed `unwind` attribute input"
- )
- .span_label(attr.span, "invalid argument")
- .span_suggestions(
- attr.span,
- "the allowed arguments are `allowed` and `aborts`",
- (vec!["allowed", "aborts"])
- .into_iter()
- .map(|s| format!("#[unwind({})]", s)),
- Applicability::MachineApplicable,
- )
- .emit();
- }
- }
- }
-
- ia
- })
-}
-
/// Represents the following attributes:
///
/// - `#[stable]`
.bcx
.ins()
.iconst(fx.pointer_type, len.eval_usize(fx.tcx, ParamEnv::reveal_all()) as i64),
- (&ty::Dynamic(..), &ty::Dynamic(..)) => {
- // For now, upcasts are limited to changes in marker
- // traits, and hence never actually require an actual
- // change to the vtable.
- old_info.expect("unsized_info: missing old info for trait upcast")
+ (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
+ let old_info =
+ old_info.expect("unsized_info: missing old info for trait upcasting coercion");
+ if data_a.principal_def_id() == data_b.principal_def_id() {
+ return old_info;
+ }
+ // trait upcasting coercion
+
+ // if both of the two `principal`s are `None`, this function would have returned early above.
+ // and if one of the two `principal`s is `None`, typechecking would have rejected this case.
+ let principal_a = data_a
+ .principal()
+ .expect("unsized_info: missing principal trait for trait upcasting coercion");
+ let principal_b = data_b
+ .principal()
+ .expect("unsized_info: missing principal trait for trait upcasting coercion");
+
+ let vptr_entry_idx = fx.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((
+ principal_a.with_self_ty(fx.tcx, source),
+ principal_b.with_self_ty(fx.tcx, source),
+ ));
+
+ if let Some(entry_idx) = vptr_entry_idx {
+ let entry_idx = u32::try_from(entry_idx).unwrap();
+ let entry_offset = entry_idx * fx.pointer_type.bytes();
+ let vptr_ptr = Pointer::new(old_info).offset_i64(fx, entry_offset.into()).load(
+ fx,
+ fx.pointer_type,
+ crate::vtable::vtable_memflags(),
+ );
+ vptr_ptr
+ } else {
+ old_info
+ }
}
(_, &ty::Dynamic(ref data, ..)) => crate::vtable::get_vtable(fx, source, data.principal()),
_ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target),
}
}
-/// Coerce `src` to `dst_ty`. `src_ty` must be a thin pointer.
-fn unsize_thin_ptr<'tcx>(
+/// Coerce `src` to `dst_ty`.
+fn unsize_ptr<'tcx>(
fx: &mut FunctionCx<'_, '_, 'tcx>,
src: Value,
src_layout: TyAndLayout<'tcx>,
dst_layout: TyAndLayout<'tcx>,
+ old_info: Option<Value>,
) -> (Value, Value) {
match (&src_layout.ty.kind(), &dst_layout.ty.kind()) {
(&ty::Ref(_, a, _), &ty::Ref(_, b, _))
| (&ty::Ref(_, a, _), &ty::RawPtr(ty::TypeAndMut { ty: b, .. }))
| (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => {
assert!(!fx.layout_of(a).is_unsized());
- (src, unsized_info(fx, a, b, None))
+ (src, unsized_info(fx, a, b, old_info))
}
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => {
let (a, b) = (src_layout.ty.boxed_ty(), dst_layout.ty.boxed_ty());
assert!(!fx.layout_of(a).is_unsized());
- (src, unsized_info(fx, a, b, None))
+ (src, unsized_info(fx, a, b, old_info))
}
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
assert_eq!(def_a, def_b);
+ if src_layout == dst_layout {
+ return (src, old_info.unwrap());
+ }
+
let mut result = None;
for i in 0..src_layout.fields.count() {
let src_f = src_layout.field(fx, i);
let dst_f = dst_layout.field(fx, i);
assert_ne!(src_f.ty, dst_f.ty);
assert_eq!(result, None);
- result = Some(unsize_thin_ptr(fx, src, src_f, dst_f));
+ result = Some(unsize_ptr(fx, src, src_f, dst_f, old_info));
}
result.unwrap()
}
- _ => bug!("unsize_thin_ptr: called on bad types"),
+ _ => bug!("unsize_ptr: called on bad types"),
}
}
let mut coerce_ptr = || {
let (base, info) =
if fx.layout_of(src.layout().ty.builtin_deref(true).unwrap().ty).is_unsized() {
- // fat-ptr to fat-ptr unsize preserves the vtable
- // i.e., &'a fmt::Debug+Send => &'a fmt::Debug
- src.load_scalar_pair(fx)
+ let (old_base, old_info) = src.load_scalar_pair(fx);
+ unsize_ptr(fx, old_base, src.layout(), dst.layout(), Some(old_info))
} else {
let base = src.load_scalar(fx);
- unsize_thin_ptr(fx, base, src.layout(), dst.layout())
+ unsize_ptr(fx, base, src.layout(), dst.layout(), None)
};
dst.write_cvalue(fx, CValue::by_val_pair(base, info, dst.layout()));
};
use crate::constant::data_id_for_alloc_id;
use crate::prelude::*;
-fn vtable_memflags() -> MemFlags {
+pub(crate) fn vtable_memflags() -> MemFlags {
let mut flags = MemFlags::trusted(); // A vtable access is always aligned and will never trap.
flags.set_readonly(); // A vtable is always read-only.
flags
OperandValue::Immediate(self.to_immediate(llval, place.layout))
} else if let abi::Abi::ScalarPair(ref a, ref b) = place.layout.abi {
let b_offset = a.value.size(self).align_to(b.value.align(self).abi);
+ let pair_ty = place.layout.llvm_type(self);
let mut load = |i, scalar: &abi::Scalar, align| {
- let llptr = self.struct_gep(place.llval, i as u64);
+ let llptr = self.struct_gep(pair_ty, place.llval, i as u64);
let llty = place.layout.scalar_pair_element_llvm_type(self, i, false);
let load = self.load(llty, llptr, align);
scalar_load_metadata(self, load, scalar);
.val
.store(&mut body_bx, PlaceRef::new_sized_aligned(current, cg_elem.layout, align));
- let next = body_bx.inbounds_gep(current, &[self.const_usize(1)]);
+ let next = body_bx.inbounds_gep(
+ self.backend_type(cg_elem.layout),
+ current,
+ &[self.const_usize(1)],
+ );
body_bx.br(header_bx.llbb());
header_bx.add_incoming_to_phi(current, next, body_bx.llbb());
}
}
- fn gep(&mut self, ptr: &'ll Value, indices: &[&'ll Value]) -> &'ll Value {
+ fn gep(&mut self, ty: &'ll Type, ptr: &'ll Value, indices: &[&'ll Value]) -> &'ll Value {
unsafe {
- llvm::LLVMBuildGEP(
+ llvm::LLVMBuildGEP2(
self.llbuilder,
+ ty,
ptr,
indices.as_ptr(),
indices.len() as c_uint,
}
}
- fn inbounds_gep(&mut self, ptr: &'ll Value, indices: &[&'ll Value]) -> &'ll Value {
+ fn inbounds_gep(
+ &mut self,
+ ty: &'ll Type,
+ ptr: &'ll Value,
+ indices: &[&'ll Value],
+ ) -> &'ll Value {
unsafe {
- llvm::LLVMBuildInBoundsGEP(
+ llvm::LLVMBuildInBoundsGEP2(
self.llbuilder,
+ ty,
ptr,
indices.as_ptr(),
indices.len() as c_uint,
}
}
- fn struct_gep(&mut self, ptr: &'ll Value, idx: u64) -> &'ll Value {
+ fn struct_gep(&mut self, ty: &'ll Type, ptr: &'ll Value, idx: u64) -> &'ll Value {
assert_eq!(idx as c_uint as u64, idx);
- unsafe { llvm::LLVMBuildStructGEP(self.llbuilder, ptr, idx as c_uint, UNNAMED) }
+ unsafe { llvm::LLVMBuildStructGEP2(self.llbuilder, ty, ptr, idx as c_uint, UNNAMED) }
}
/* Casts */
}
};
let llval = unsafe {
- llvm::LLVMConstInBoundsGEP(
+ llvm::LLVMRustConstInBoundsGEP2(
+ self.type_i8(),
self.const_bitcast(base_addr, self.type_i8p_ext(base_addr_space)),
&self.const_usize(offset.bytes()),
1,
let base_addr = self.static_addr_of(init, alloc.align, None);
let llval = unsafe {
- llvm::LLVMConstInBoundsGEP(
+ llvm::LLVMRustConstInBoundsGEP2(
+ self.type_i8(),
self.const_bitcast(base_addr, self.type_i8p()),
&self.const_usize(offset.bytes()),
1,
/// .debug_gdb_scripts global is referenced, so it isn't removed by the linker.
pub fn insert_reference_to_gdb_debug_scripts_section_global(bx: &mut Builder<'_, '_, '_>) {
if needs_gdb_debug_scripts_section(bx) {
- let gdb_debug_scripts_section = get_or_insert_gdb_debug_scripts_section_global(bx);
+ let gdb_debug_scripts_section =
+ bx.const_bitcast(get_or_insert_gdb_debug_scripts_section_global(bx), bx.type_i8p());
// Load just the first byte as that's all that's necessary to force
// LLVM to keep around the reference to the global.
- let indices = [bx.const_i32(0), bx.const_i32(0)];
- let element = bx.inbounds_gep(gdb_debug_scripts_section, &indices);
- let volative_load_instruction = bx.volatile_load(bx.type_i8(), element);
+ let volative_load_instruction = bx.volatile_load(bx.type_i8(), gdb_debug_scripts_section);
unsafe {
llvm::LLVMSetAlignment(volative_load_instruction, 1);
}
// create an alloca and pass a pointer to that.
let ptr_align = bx.tcx().data_layout.pointer_align.abi;
let i8_align = bx.tcx().data_layout.i8_align.abi;
- let catch_data =
- catch.alloca(bx.type_struct(&[bx.type_i8p(), bx.type_bool()], false), ptr_align);
- let catch_data_0 = catch.inbounds_gep(catch_data, &[bx.const_usize(0), bx.const_usize(0)]);
+ let catch_data_type = bx.type_struct(&[bx.type_i8p(), bx.type_bool()], false);
+ let catch_data = catch.alloca(catch_data_type, ptr_align);
+ let catch_data_0 = catch.inbounds_gep(
+ catch_data_type,
+ catch_data,
+ &[bx.const_usize(0), bx.const_usize(0)],
+ );
catch.store(ptr, catch_data_0, ptr_align);
- let catch_data_1 = catch.inbounds_gep(catch_data, &[bx.const_usize(0), bx.const_usize(1)]);
+ let catch_data_1 = catch.inbounds_gep(
+ catch_data_type,
+ catch_data,
+ &[bx.const_usize(0), bx.const_usize(1)],
+ );
catch.store(is_rust_panic, catch_data_1, i8_align);
let catch_data = catch.bitcast(catch_data, bx.type_i8p());
pub fn LLVMConstVector(ScalarConstantVals: *const &Value, Size: c_uint) -> &Value;
// Constant expressions
- pub fn LLVMConstInBoundsGEP(
+ pub fn LLVMRustConstInBoundsGEP2(
+ ty: &'a Type,
ConstantVal: &'a Value,
ConstantIndices: *const &'a Value,
NumIndices: c_uint,
pub fn LLVMBuildStore(B: &Builder<'a>, Val: &'a Value, Ptr: &'a Value) -> &'a Value;
- pub fn LLVMBuildGEP(
+ pub fn LLVMBuildGEP2(
B: &Builder<'a>,
+ Ty: &'a Type,
Pointer: &'a Value,
Indices: *const &'a Value,
NumIndices: c_uint,
Name: *const c_char,
) -> &'a Value;
- pub fn LLVMBuildInBoundsGEP(
+ pub fn LLVMBuildInBoundsGEP2(
B: &Builder<'a>,
+ Ty: &'a Type,
Pointer: &'a Value,
Indices: *const &'a Value,
NumIndices: c_uint,
Name: *const c_char,
) -> &'a Value;
- pub fn LLVMBuildStructGEP(
+ pub fn LLVMBuildStructGEP2(
B: &Builder<'a>,
+ Ty: &'a Type,
Pointer: &'a Value,
Idx: c_uint,
Name: *const c_char,
let aligned_size = size.align_to(slot_size).bytes() as i32;
let full_direct_size = bx.cx().const_i32(aligned_size);
- let next = bx.inbounds_gep(addr, &[full_direct_size]);
+ let next = bx.inbounds_gep(bx.type_i8(), addr, &[full_direct_size]);
bx.store(next, va_list_addr, bx.tcx().data_layout.pointer_align.abi);
if size.bytes() < slot_size.bytes() && bx.tcx().sess.target.endian == Endian::Big {
let adjusted_size = bx.cx().const_i32((slot_size.bytes() - size.bytes()) as i32);
- let adjusted = bx.inbounds_gep(addr, &[adjusted_size]);
+ let adjusted = bx.inbounds_gep(bx.type_i8(), addr, &[adjusted_size]);
(bx.bitcast(adjusted, bx.cx().type_ptr_to(llty)), addr_align)
} else {
(bx.bitcast(addr, bx.cx().type_ptr_to(llty)), addr_align)
// Implementation of the AAPCS64 calling convention for va_args see
// https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst
let va_list_addr = list.immediate();
+ let va_list_ty = list.deref(bx.cx).layout.llvm_type(bx);
let layout = bx.cx.layout_of(target_ty);
let mut maybe_reg = bx.build_sibling_block("va_arg.maybe_reg");
let gr_type = target_ty.is_any_ptr() || target_ty.is_integral();
let (reg_off, reg_top_index, slot_size) = if gr_type {
- let gr_offs = bx.struct_gep(va_list_addr, 7);
+ let gr_offs = bx.struct_gep(va_list_ty, va_list_addr, 7);
let nreg = (layout.size.bytes() + 7) / 8;
(gr_offs, 3, nreg * 8)
} else {
- let vr_off = bx.struct_gep(va_list_addr, 9);
+ let vr_off = bx.struct_gep(va_list_ty, va_list_addr, 9);
let nreg = (layout.size.bytes() + 15) / 16;
(vr_off, 5, nreg * 16)
};
maybe_reg.cond_br(use_stack, &on_stack.llbb(), &in_reg.llbb());
let top_type = bx.type_i8p();
- let top = in_reg.struct_gep(va_list_addr, reg_top_index);
+ let top = in_reg.struct_gep(va_list_ty, va_list_addr, reg_top_index);
let top = in_reg.load(top_type, top, bx.tcx().data_layout.pointer_align.abi);
// reg_value = *(@top + reg_off_v);
- let mut reg_addr = in_reg.gep(top, &[reg_off_v]);
+ let mut reg_addr = in_reg.gep(bx.type_i8(), top, &[reg_off_v]);
if bx.tcx().sess.target.endian == Endian::Big && layout.size.bytes() != slot_size {
// On big-endian systems the value is right-aligned in its slot.
let offset = bx.const_i32((slot_size - layout.size.bytes()) as i32);
- reg_addr = in_reg.gep(reg_addr, &[offset]);
+ reg_addr = in_reg.gep(bx.type_i8(), reg_addr, &[offset]);
}
let reg_type = layout.llvm_type(bx);
let reg_addr = in_reg.bitcast(reg_addr, bx.cx.type_ptr_to(reg_type));
tempfile = "3.2"
pathdiff = "0.2.0"
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
+regex = "1.4"
rustc_serialize = { path = "../rustc_serialize" }
rustc_ast = { path = "../rustc_ast" }
use object::elf;
use object::write::Object;
use object::{Architecture, BinaryFormat, Endianness, FileFlags, SectionFlags, SectionKind};
+use regex::Regex;
use tempfile::Builder as TempFileBuilder;
use std::ffi::OsString;
// Invoke the system linker
info!("{:?}", &cmd);
let retry_on_segfault = env::var("RUSTC_RETRY_LINKER_ON_SEGFAULT").is_ok();
+ let unknown_arg_regex =
+ Regex::new(r"(unknown|unrecognized) (command line )?(option|argument)").unwrap();
let mut prog;
let mut i = 0;
loop {
out.extend(&output.stdout);
let out = String::from_utf8_lossy(&out);
- // Check to see if the link failed with "unrecognized command line option:
- // '-no-pie'" for gcc or "unknown argument: '-no-pie'" for clang. If so,
- // reperform the link step without the -no-pie option. This is safe because
- // if the linker doesn't support -no-pie then it should not default to
- // linking executables as pie. Different versions of gcc seem to use
- // different quotes in the error message so don't check for them.
+ // Check to see if the link failed with an error message that indicates it
+ // doesn't recognize the -no-pie option. If so, reperform the link step
+ // without it. This is safe because if the linker doesn't support -no-pie
+ // then it should not default to linking executables as pie. Different
+ // versions of gcc seem to use different quotes in the error message so
+ // don't check for them.
if sess.target.linker_is_gnu
&& flavor != LinkerFlavor::Ld
- && (out.contains("unrecognized command line option")
- || out.contains("unknown argument"))
+ && unknown_arg_regex.is_match(&out)
&& out.contains("-no-pie")
&& cmd.get_args().iter().any(|e| e.to_string_lossy() == "-no-pie")
{
// Fallback from '-static-pie' to '-static' in that case.
if sess.target.linker_is_gnu
&& flavor != LinkerFlavor::Ld
- && (out.contains("unrecognized command line option")
- || out.contains("unknown argument"))
+ && unknown_arg_regex.is_match(&out)
&& (out.contains("-static-pie") || out.contains("--no-dynamic-linker"))
&& cmd.get_args().iter().any(|e| e.to_string_lossy() == "-static-pie")
{
use rustc_middle::middle::lang_items;
use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem};
use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout};
-use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
use rustc_session::cgu_reuse_tracker::CguReuse;
use rustc_span::symbol::sym;
use rustc_target::abi::{Align, LayoutOf, VariantIdx};
+use std::convert::TryFrom;
use std::ops::{Deref, DerefMut};
use std::time::{Duration, Instant};
///
/// The `old_info` argument is a bit odd. It is intended for use in an upcast,
/// where the new vtable for an object will be derived from the old one.
-pub fn unsized_info<'tcx, Cx: CodegenMethods<'tcx>>(
- cx: &Cx,
+pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+ bx: &mut Bx,
source: Ty<'tcx>,
target: Ty<'tcx>,
- old_info: Option<Cx::Value>,
-) -> Cx::Value {
+ old_info: Option<Bx::Value>,
+) -> Bx::Value {
+ let cx = bx.cx();
let (source, target) =
- cx.tcx().struct_lockstep_tails_erasing_lifetimes(source, target, cx.param_env());
+ cx.tcx().struct_lockstep_tails_erasing_lifetimes(source, target, bx.param_env());
match (source.kind(), target.kind()) {
(&ty::Array(_, len), &ty::Slice(_)) => {
cx.const_usize(len.eval_usize(cx.tcx(), ty::ParamEnv::reveal_all()))
}
- (&ty::Dynamic(..), &ty::Dynamic(..)) => {
- // For now, upcasts are limited to changes in marker
- // traits, and hence never actually require an actual
- // change to the vtable.
- old_info.expect("unsized_info: missing old info for trait upcast")
+ (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
+ let old_info =
+ old_info.expect("unsized_info: missing old info for trait upcasting coercion");
+ if data_a.principal_def_id() == data_b.principal_def_id() {
+ return old_info;
+ }
+
+ // trait upcasting coercion
+
+ // if both of the two `principal`s are `None`, this function would have returned early above.
+ // and if one of the two `principal`s is `None`, typechecking would have rejected this case.
+ let principal_a = data_a
+ .principal()
+ .expect("unsized_info: missing principal trait for trait upcasting coercion");
+ let principal_b = data_b
+ .principal()
+ .expect("unsized_info: missing principal trait for trait upcasting coercion");
+
+ let vptr_entry_idx = cx.tcx().vtable_trait_upcasting_coercion_new_vptr_slot((
+ principal_a.with_self_ty(cx.tcx(), source),
+ principal_b.with_self_ty(cx.tcx(), source),
+ ));
+
+ if let Some(entry_idx) = vptr_entry_idx {
+ let ptr_ty = cx.type_i8p();
+ let ptr_align = cx.tcx().data_layout.pointer_align.abi;
+ let llvtable = bx.pointercast(old_info, bx.type_ptr_to(ptr_ty));
+ let gep = bx.inbounds_gep(
+ ptr_ty,
+ llvtable,
+ &[bx.const_usize(u64::try_from(entry_idx).unwrap())],
+ );
+ let new_vptr = bx.load(ptr_ty, gep, ptr_align);
+ bx.nonnull_metadata(new_vptr);
+ // Vtable loads are invariant.
+ bx.set_invariant_load(new_vptr);
+ new_vptr
+ } else {
+ old_info
+ }
}
(_, &ty::Dynamic(ref data, ..)) => {
- let vtable_ptr = cx.layout_of(cx.tcx().mk_mut_ptr(target)).field(cx, FAT_PTR_EXTRA);
- cx.const_ptrcast(
- meth::get_vtable(cx, source, data.principal()),
- cx.backend_type(vtable_ptr),
- )
+ let vtable_ptr_ty = cx.scalar_pair_element_backend_type(
+ cx.layout_of(cx.tcx().mk_mut_ptr(target)),
+ 1,
+ true,
+ );
+ cx.const_ptrcast(meth::get_vtable(cx, source, data.principal()), vtable_ptr_ty)
}
_ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target),
}
}
-/// Coerces `src` to `dst_ty`. `src_ty` must be a thin pointer.
-pub fn unsize_thin_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+/// Coerces `src` to `dst_ty`. `src_ty` must be a pointer.
+pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
bx: &mut Bx,
src: Bx::Value,
src_ty: Ty<'tcx>,
dst_ty: Ty<'tcx>,
+ old_info: Option<Bx::Value>,
) -> (Bx::Value, Bx::Value) {
- debug!("unsize_thin_ptr: {:?} => {:?}", src_ty, dst_ty);
+ debug!("unsize_ptr: {:?} => {:?}", src_ty, dst_ty);
match (src_ty.kind(), dst_ty.kind()) {
(&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. }))
| (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => {
- assert!(bx.cx().type_is_sized(a));
+ assert_eq!(bx.cx().type_is_sized(a), old_info.is_none());
let ptr_ty = bx.cx().type_ptr_to(bx.cx().backend_type(bx.cx().layout_of(b)));
- (bx.pointercast(src, ptr_ty), unsized_info(bx.cx(), a, b, None))
+ (bx.pointercast(src, ptr_ty), unsized_info(bx, a, b, old_info))
}
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
assert_eq!(def_a, def_b);
-
let src_layout = bx.cx().layout_of(src_ty);
let dst_layout = bx.cx().layout_of(dst_ty);
+ if src_ty == dst_ty {
+ return (src, old_info.unwrap());
+ }
let mut result = None;
for i in 0..src_layout.fields.count() {
let src_f = src_layout.field(bx.cx(), i);
let dst_f = dst_layout.field(bx.cx(), i);
assert_ne!(src_f.ty, dst_f.ty);
assert_eq!(result, None);
- result = Some(unsize_thin_ptr(bx, src, src_f.ty, dst_f.ty));
+ result = Some(unsize_ptr(bx, src, src_f.ty, dst_f.ty, old_info));
}
let (lldata, llextra) = result.unwrap();
+ let lldata_ty = bx.cx().scalar_pair_element_backend_type(dst_layout, 0, true);
+ let llextra_ty = bx.cx().scalar_pair_element_backend_type(dst_layout, 1, true);
// HACK(eddyb) have to bitcast pointers until LLVM removes pointee types.
- // FIXME(eddyb) move these out of this `match` arm, so they're always
- // applied, uniformly, no matter the source/destination types.
- (
- bx.bitcast(lldata, bx.cx().scalar_pair_element_backend_type(dst_layout, 0, true)),
- bx.bitcast(llextra, bx.cx().scalar_pair_element_backend_type(dst_layout, 1, true)),
- )
+ (bx.bitcast(lldata, lldata_ty), bx.bitcast(llextra, llextra_ty))
}
- _ => bug!("unsize_thin_ptr: called on bad types"),
+ _ => bug!("unsize_ptr: called on bad types"),
}
}
match (src_ty.kind(), dst_ty.kind()) {
(&ty::Ref(..), &ty::Ref(..) | &ty::RawPtr(..)) | (&ty::RawPtr(..), &ty::RawPtr(..)) => {
let (base, info) = match bx.load_operand(src).val {
- OperandValue::Pair(base, info) => {
- // fat-ptr to fat-ptr unsize preserves the vtable
- // i.e., &'a fmt::Debug+Send => &'a fmt::Debug
- // So we need to pointercast the base to ensure
- // the types match up.
- // FIXME(eddyb) use `scalar_pair_element_backend_type` here,
- // like `unsize_thin_ptr` does.
- let thin_ptr = dst.layout.field(bx.cx(), FAT_PTR_ADDR);
- (bx.pointercast(base, bx.cx().backend_type(thin_ptr)), info)
- }
- OperandValue::Immediate(base) => unsize_thin_ptr(bx, base, src_ty, dst_ty),
+ OperandValue::Pair(base, info) => unsize_ptr(bx, base, src_ty, dst_ty, Some(info)),
+ OperandValue::Immediate(base) => unsize_ptr(bx, base, src_ty, dst_ty, None),
OperandValue::Ref(..) => bug!(),
};
OperandValue::Pair(base, info).store(bx, dst);
let llty = bx.fn_ptr_backend_type(fn_abi);
let llvtable = bx.pointercast(llvtable, bx.type_ptr_to(llty));
let ptr_align = bx.tcx().data_layout.pointer_align.abi;
- let gep = bx.inbounds_gep(llvtable, &[bx.const_usize(self.0)]);
+ let gep = bx.inbounds_gep(llty, llvtable, &[bx.const_usize(self.0)]);
let ptr = bx.load(llty, gep, ptr_align);
bx.nonnull_metadata(ptr);
// Vtable loads are invariant.
let llty = bx.type_isize();
let llvtable = bx.pointercast(llvtable, bx.type_ptr_to(llty));
let usize_align = bx.tcx().data_layout.pointer_align.abi;
- let gep = bx.inbounds_gep(llvtable, &[bx.const_usize(self.0)]);
+ let gep = bx.inbounds_gep(llty, llvtable, &[bx.const_usize(self.0)]);
let ptr = bx.load(llty, gep, usize_align);
// Vtable loads are invariant.
bx.set_invariant_load(ptr);
OperandRef::from_const(bx, value, ret_ty).immediate_or_packed_pair(bx)
}
sym::offset => {
+ let ty = substs.type_at(0);
+ let layout = bx.layout_of(ty);
let ptr = args[0].immediate();
let offset = args[1].immediate();
- bx.inbounds_gep(ptr, &[offset])
+ bx.inbounds_gep(bx.backend_type(layout), ptr, &[offset])
}
sym::arith_offset => {
+ let ty = substs.type_at(0);
+ let layout = bx.layout_of(ty);
let ptr = args[0].immediate();
let offset = args[1].immediate();
- bx.gep(ptr, &[offset])
+ bx.gep(bx.backend_type(layout), ptr, &[offset])
}
sym::copy => {
copy_intrinsic(
Abi::ScalarPair(ref a, ref b) => (a, b),
_ => bug!("store_with_flags: invalid ScalarPair layout: {:#?}", dest.layout),
};
+ let ty = bx.backend_type(dest.layout);
let b_offset = a_scalar.value.size(bx).align_to(b_scalar.value.align(bx).abi);
- let llptr = bx.struct_gep(dest.llval, 0);
+ let llptr = bx.struct_gep(ty, dest.llval, 0);
let val = bx.from_immediate(a);
let align = dest.align;
bx.store_with_flags(val, llptr, align, flags);
- let llptr = bx.struct_gep(dest.llval, 1);
+ let llptr = bx.struct_gep(ty, dest.llval, 1);
let val = bx.from_immediate(b);
let align = dest.align.restrict_for_offset(b_offset);
bx.store_with_flags(val, llptr, align, flags);
if offset == a.value.size(bx.cx()).align_to(b.value.align(bx.cx()).abi) =>
{
// Offset matches second field.
- bx.struct_gep(self.llval, 1)
+ let ty = bx.backend_type(self.layout);
+ bx.struct_gep(ty, self.llval, 1)
}
Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. } if field.is_zst() => {
// ZST fields are not included in Scalar, ScalarPair, and Vector layouts, so manually offset the pointer.
let byte_ptr = bx.pointercast(self.llval, bx.cx().type_i8p());
- bx.gep(byte_ptr, &[bx.const_usize(offset.bytes())])
+ bx.gep(bx.cx().type_i8(), byte_ptr, &[bx.const_usize(offset.bytes())])
}
Abi::Scalar(_) | Abi::ScalarPair(..) => {
// All fields of Scalar and ScalarPair layouts must have been handled by this point.
self.layout
);
}
- _ => bx.struct_gep(self.llval, bx.cx().backend_field_index(self.layout, ix)),
+ _ => {
+ let ty = bx.backend_type(self.layout);
+ bx.struct_gep(ty, self.llval, bx.cx().backend_field_index(self.layout, ix))
+ }
};
PlaceRef {
// HACK(eddyb): have to bitcast pointers until LLVM removes pointee types.
// Cast and adjust pointer.
let byte_ptr = bx.pointercast(self.llval, bx.cx().type_i8p());
- let byte_ptr = bx.gep(byte_ptr, &[offset]);
+ let byte_ptr = bx.gep(bx.cx().type_i8(), byte_ptr, &[offset]);
// Finally, cast back to the type expected.
let ll_fty = bx.cx().backend_type(field);
};
PlaceRef {
- llval: bx.inbounds_gep(self.llval, &[bx.cx().const_usize(0), llindex]),
+ llval: bx.inbounds_gep(
+ bx.cx().backend_type(self.layout),
+ self.llval,
+ &[bx.cx().const_usize(0), llindex],
+ ),
llextra: None,
layout,
align: self.align.restrict_for_offset(offset),
}
mir::CastKind::Pointer(PointerCast::Unsize) => {
assert!(bx.cx().is_backend_scalar_pair(cast));
- match operand.val {
+ let (lldata, llextra) = match operand.val {
OperandValue::Pair(lldata, llextra) => {
// unsize from a fat pointer -- this is a
- // "trait-object-to-supertrait" coercion, for
- // example, `&'a fmt::Debug + Send => &'a fmt::Debug`.
-
- // HACK(eddyb) have to bitcast pointers
- // until LLVM removes pointee types.
- let lldata = bx.pointercast(
- lldata,
- bx.cx().scalar_pair_element_backend_type(cast, 0, true),
- );
- OperandValue::Pair(lldata, llextra)
+ // "trait-object-to-supertrait" coercion.
+ (lldata, Some(llextra))
}
OperandValue::Immediate(lldata) => {
// "standard" unsize
- let (lldata, llextra) = base::unsize_thin_ptr(
- &mut bx,
- lldata,
- operand.layout.ty,
- cast.ty,
- );
- OperandValue::Pair(lldata, llextra)
+ (lldata, None)
}
OperandValue::Ref(..) => {
bug!("by-ref operand {:?} in `codegen_rvalue_operand`", operand);
}
- }
+ };
+ let (lldata, llextra) =
+ base::unsize_ptr(&mut bx, lldata, operand.layout.ty, cast.ty, llextra);
+ OperandValue::Pair(lldata, llextra)
}
mir::CastKind::Pointer(PointerCast::MutToConstPointer)
| mir::CastKind::Misc
mir::BinOp::BitOr => bx.or(lhs, rhs),
mir::BinOp::BitAnd => bx.and(lhs, rhs),
mir::BinOp::BitXor => bx.xor(lhs, rhs),
- mir::BinOp::Offset => bx.inbounds_gep(lhs, &[rhs]),
+ mir::BinOp::Offset => {
+ let pointee_type = input_ty
+ .builtin_deref(true)
+ .unwrap_or_else(|| bug!("deref of non-pointer {:?}", input_ty))
+ .ty;
+ let llty = bx.cx().backend_type(bx.cx().layout_of(pointee_type));
+ bx.inbounds_gep(llty, lhs, &[rhs])
+ }
mir::BinOp::Shl => common::build_unchecked_lshift(bx, lhs, rhs),
mir::BinOp::Shr => common::build_unchecked_rshift(bx, input_ty, lhs, rhs),
mir::BinOp::Ne
("sve", Some(sym::aarch64_target_feature)),
// FEAT_CRC
("crc", Some(sym::aarch64_target_feature)),
- // Cryptographic extension
- ("crypto", Some(sym::aarch64_target_feature)),
// FEAT_RAS
("ras", Some(sym::aarch64_target_feature)),
// FEAT_LSE
size: Size,
);
- fn gep(&mut self, ptr: Self::Value, indices: &[Self::Value]) -> Self::Value;
- fn inbounds_gep(&mut self, ptr: Self::Value, indices: &[Self::Value]) -> Self::Value;
- fn struct_gep(&mut self, ptr: Self::Value, idx: u64) -> Self::Value;
+ fn gep(&mut self, ty: Self::Type, ptr: Self::Value, indices: &[Self::Value]) -> Self::Value;
+ fn inbounds_gep(
+ &mut self,
+ ty: Self::Type,
+ ptr: Self::Value,
+ indices: &[Self::Value],
+ ) -> Self::Value;
+ fn struct_gep(&mut self, ty: Self::Type, ptr: Self::Value, idx: u64) -> Self::Value;
fn trunc(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
fn sext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
E0622: include_str!("./error_codes/E0622.md"),
E0623: include_str!("./error_codes/E0623.md"),
E0624: include_str!("./error_codes/E0624.md"),
+E0625: include_str!("./error_codes/E0625.md"),
E0626: include_str!("./error_codes/E0626.md"),
E0627: include_str!("./error_codes/E0627.md"),
E0628: include_str!("./error_codes/E0628.md"),
// E0611, // merged into E0616
// E0612, // merged into E0609
// E0613, // Removed (merged with E0609)
- E0625, // thread-local statics cannot be accessed at compile-time
// E0629, // missing 'feature' (rustc_const_unstable)
// E0630, // rustc_const_unstable attribute must be paired with stable/unstable
// attribute
--- /dev/null
+A compile-time const variable is referring to a thread-local static variable.
+
+Erroneous code example:
+
+```compile_fail,E0625
+#![feature(thread_local)]
+
+#[thread_local]
+static X: usize = 12;
+
+const Y: usize = 2 * X;
+```
+
+Static and const variables can refer to other const variables but a const
+variable cannot refer to a thread-local static variable. In this example,
+`Y` cannot refer to `X`. To fix this, the value can be extracted as a const
+and then used:
+
+```
+#![feature(thread_local)]
+
+const C: usize = 12;
+
+#[thread_local]
+static X: usize = C;
+
+const Y: usize = 2 * C;
+```
+#### Note: this error code is no longer emitted by the compiler.
+
The `unwind` attribute was malformed.
Erroneous code example:
-```compile_fail,E0633
+```compile_fail
#![feature(unwind_attributes)]
#[unwind()] // error: expected one argument
// |
for pos in 0..=line_len {
draw_col_separator(buffer, line_offset + pos + 1, width_offset - 2);
- buffer.putc(line_offset + pos + 1, width_offset - 2, '|', Style::LineNumber);
}
// Write the horizontal lines for multiline annotations
let buffer_msg_line_offset = buffer.num_lines();
// Add spacing line
- draw_col_separator(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1);
+ draw_col_separator_no_space(
+ &mut buffer,
+ buffer_msg_line_offset,
+ max_line_num_len + 1,
+ );
// Then, the secondary file indicator
buffer.prepend(buffer_msg_line_offset + 1, "::: ", Style::LineNumber);
deduplicated_warn_count: usize,
future_breakage_diagnostics: Vec<Diagnostic>,
+
+ /// If set to `true`, no warning or error will be emitted.
+ quiet: bool,
}
/// A key denoting where from a diagnostic was stashed.
emitted_diagnostics: Default::default(),
stashed_diagnostics: Default::default(),
future_breakage_diagnostics: Vec::new(),
+ quiet: false,
}),
}
}
+ pub fn with_disabled_diagnostic<T, F: FnOnce() -> T>(&self, f: F) -> T {
+ let prev = self.inner.borrow_mut().quiet;
+ self.inner.borrow_mut().quiet = true;
+ let ret = f();
+ self.inner.borrow_mut().quiet = prev;
+ ret
+ }
+
// This is here to not allow mutation of flags;
// as of this writing it's only used in tests in librustc_middle.
pub fn can_emit_warnings(&self) -> bool {
}
fn emit_diagnostic(&mut self, diagnostic: &Diagnostic) {
- if diagnostic.cancelled() {
+ if diagnostic.cancelled() || self.quiet {
return;
}
}
fn delay_as_bug(&mut self, diagnostic: Diagnostic) {
+ if self.quiet {
+ return;
+ }
if self.flags.report_delayed_bugs {
self.emit_diagnostic(&diagnostic);
}
use crate::base::{ExtCtxt, ResolverExpand};
use rustc_ast as ast;
-use rustc_ast::token::{self, Nonterminal, NtIdent, TokenKind};
+use rustc_ast::token::{self, Nonterminal, NtIdent};
use rustc_ast::tokenstream::{self, CanSynthesizeMissingTokens};
use rustc_ast::tokenstream::{DelimSpan, Spacing::*, TokenStream, TreeAndSpacing};
use rustc_ast_pretty::pprust;
impl server::Literal for Rustc<'_> {
fn from_str(&mut self, s: &str) -> Result<Self::Literal, ()> {
- let override_span = None;
- let stream = parse_stream_from_source_str(
- FileName::proc_macro_source_code(s),
- s.to_owned(),
- self.sess,
- override_span,
- );
- if stream.len() != 1 {
- return Err(());
- }
- let tree = stream.into_trees().next().unwrap();
- let token = match tree {
- tokenstream::TokenTree::Token(token) => token,
- tokenstream::TokenTree::Delimited { .. } => return Err(()),
+ let name = FileName::proc_macro_source_code(s);
+ let mut parser = rustc_parse::new_parser_from_source_str(self.sess, name, s.to_owned());
+
+ let first_span = parser.token.span.data();
+ let minus_present = parser.eat(&token::BinOp(token::Minus));
+
+ let lit_span = parser.token.span.data();
+ let mut lit = match parser.token.kind {
+ token::Literal(lit) => lit,
+ _ => return Err(()),
};
- let span_data = token.span.data();
- if (span_data.hi.0 - span_data.lo.0) as usize != s.len() {
- // There is a comment or whitespace adjacent to the literal.
+
+ // Check no comment or whitespace surrounding the (possibly negative)
+ // literal, or more tokens after it.
+ if (lit_span.hi.0 - first_span.lo.0) as usize != s.len() {
return Err(());
}
- let lit = match token.kind {
- TokenKind::Literal(lit) => lit,
- _ => return Err(()),
- };
+
+ if minus_present {
+ // If minus is present, check no comment or whitespace in between it
+ // and the literal token.
+ if first_span.hi.0 != lit_span.lo.0 {
+ return Err(());
+ }
+
+ // Check literal is a kind we allow to be negated in a proc macro token.
+ match lit.kind {
+ token::LitKind::Bool
+ | token::LitKind::Byte
+ | token::LitKind::Char
+ | token::LitKind::Str
+ | token::LitKind::StrRaw(_)
+ | token::LitKind::ByteStr
+ | token::LitKind::ByteStrRaw(_)
+ | token::LitKind::Err => return Err(()),
+ token::LitKind::Integer | token::LitKind::Float => {}
+ }
+
+ // Synthesize a new symbol that includes the minus sign.
+ let symbol = Symbol::intern(&s[..1 + lit.symbol.len()]);
+ lit = token::Lit::new(lit.kind, symbol, lit.suffix);
+ }
+
Ok(Literal { lit, span: self.call_site })
}
+ fn to_string(&mut self, literal: &Self::Literal) -> String {
+ literal.lit.to_string()
+ }
fn debug_kind(&mut self, literal: &Self::Literal) -> String {
format!("{:?}", literal.lit.kind)
}
/// Allows `extern "platform-intrinsic" { ... }`.
(active, platform_intrinsics, "1.4.0", Some(27731), None),
- /// Allows `#[unwind(..)]`.
- ///
- /// Permits specifying whether a function should permit unwinding or abort on unwind.
- (active, unwind_attributes, "1.4.0", Some(58760), None),
-
/// Allows attributes on expressions and non-item statements.
(active, stmt_expr_attributes, "1.6.0", Some(15701), None),
/// Trait upcasting is casting, e.g., `dyn Foo -> dyn Bar` where `Foo: Bar`.
(incomplete, trait_upcasting, "1.56.0", Some(65991), None),
+ /// Allows explicit generic arguments specification with `impl Trait` present.
+ (active, explicit_generic_args_with_impl_trait, "1.56.0", Some(83701), None),
+
// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------
),
gated!(panic_runtime, AssumedUsed, template!(Word), experimental!(panic_runtime)),
gated!(needs_panic_runtime, AssumedUsed, template!(Word), experimental!(needs_panic_runtime)),
- gated!(
- unwind, AssumedUsed, template!(List: "allowed|aborts"), unwind_attributes,
- experimental!(unwind),
- ),
gated!(
compiler_builtins, AssumedUsed, template!(Word),
"the `#[compiler_builtins]` attribute is used to identify the `compiler_builtins` crate \
(removed, min_type_alias_impl_trait, "1.56.0", Some(63063), None,
Some("removed in favor of full type_alias_impl_trait")),
+ /// Allows `#[unwind(..)]`.
+ ///
+ /// Permits specifying whether a function should permit unwinding or abort on unwind.
+ (removed, unwind_attributes, "1.56.0", Some(58760), None, Some("use the C-unwind ABI instead")),
+
// -------------------------------------------------------------------------
// feature-group-end: removed features
// -------------------------------------------------------------------------
subst::{GenericArgKind, Subst, SubstsRef},
Region, Ty, TyCtxt, TypeFoldable,
};
-use rustc_span::{sym, BytePos, DesugaringKind, Pos, Span};
+use rustc_span::{sym, BytePos, DesugaringKind, MultiSpan, Pos, Span};
use rustc_target::spec::abi;
use std::ops::ControlFlow;
use std::{cmp, fmt, iter};
prefix: &str,
region: ty::Region<'tcx>,
suffix: &str,
+ alt_span: Option<Span>,
) {
let (description, span) = match *region {
ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic => {
- msg_span_from_free_region(tcx, region)
+ msg_span_from_free_region(tcx, region, alt_span)
}
- ty::ReEmpty(ty::UniverseIndex::ROOT) => ("the empty lifetime".to_owned(), None),
+ ty::ReEmpty(ty::UniverseIndex::ROOT) => ("the empty lifetime".to_owned(), alt_span),
// uh oh, hope no user ever sees THIS
- ty::ReEmpty(ui) => (format!("the empty lifetime in universe {:?}", ui), None),
+ ty::ReEmpty(ui) => (format!("the empty lifetime in universe {:?}", ui), alt_span),
ty::RePlaceholder(_) => return,
// We shouldn't really be having unification failures with ReVar
// and ReLateBound though.
ty::ReVar(_) | ty::ReLateBound(..) | ty::ReErased => {
- (format!("lifetime {:?}", region), None)
+ (format!("lifetime {:?}", region), alt_span)
}
};
region: ty::Region<'tcx>,
suffix: &str,
) {
- let (description, span) = msg_span_from_free_region(tcx, region);
+ let (description, span) = msg_span_from_free_region(tcx, region, None);
emit_msg_span(err, prefix, description, span, suffix);
}
fn msg_span_from_free_region(
tcx: TyCtxt<'tcx>,
region: ty::Region<'tcx>,
+ alt_span: Option<Span>,
) -> (String, Option<Span>) {
match *region {
ty::ReEarlyBound(_) | ty::ReFree(_) => {
msg_span_from_early_bound_and_free_regions(tcx, region)
}
- ty::ReStatic => ("the static lifetime".to_owned(), None),
- ty::ReEmpty(ty::UniverseIndex::ROOT) => ("an empty lifetime".to_owned(), None),
- ty::ReEmpty(ui) => (format!("an empty lifetime in universe {:?}", ui), None),
+ ty::ReStatic => ("the static lifetime".to_owned(), alt_span),
+ ty::ReEmpty(ty::UniverseIndex::ROOT) => ("an empty lifetime".to_owned(), alt_span),
+ ty::ReEmpty(ui) => (format!("an empty lifetime in universe {:?}", ui), alt_span),
_ => bug!("{:?}", region),
}
}
&format!("hidden type `{}` captures ", hidden_ty),
hidden_region,
"",
+ None,
);
}
}
let count = values.len();
let kind = key.descr();
let mut returned_async_output_error = false;
- for sp in values {
- err.span_label(
- *sp,
- format!(
- "{}{}{} {}{}",
- if sp.is_desugaring(DesugaringKind::Async)
- && !returned_async_output_error
- {
- "checked the `Output` of this `async fn`, "
- } else if count == 1 {
- "the "
- } else {
- ""
- },
- if count > 1 { "one of the " } else { "" },
- target,
- kind,
- pluralize!(count),
- ),
- );
- if sp.is_desugaring(DesugaringKind::Async)
- && returned_async_output_error == false
- {
- err.note("while checking the return type of the `async fn`");
+ for &sp in values {
+ if sp.is_desugaring(DesugaringKind::Async) && !returned_async_output_error {
+ if &[sp] != err.span.primary_spans() {
+ let mut span: MultiSpan = sp.into();
+ span.push_span_label(
+ sp,
+ format!(
+ "checked the `Output` of this `async fn`, {}{} {}{}",
+ if count > 1 { "one of the " } else { "" },
+ target,
+ kind,
+ pluralize!(count),
+ ),
+ );
+ err.span_note(
+ span,
+ "while checking the return type of the `async fn`",
+ );
+ } else {
+ err.span_label(
+ sp,
+ format!(
+ "checked the `Output` of this `async fn`, {}{} {}{}",
+ if count > 1 { "one of the " } else { "" },
+ target,
+ kind,
+ pluralize!(count),
+ ),
+ );
+ err.note("while checking the return type of the `async fn`");
+ }
returned_async_output_error = true;
+ } else {
+ err.span_label(
+ sp,
+ format!(
+ "{}{} {}{}",
+ if count == 1 { "the " } else { "one of the " },
+ target,
+ kind,
+ pluralize!(count),
+ ),
+ );
}
}
}
&format!("{} must be valid for ", labeled_user_string),
sub,
"...",
+ None,
);
- if let Some(infer::RelateParamBound(_, t)) = origin {
+ if let Some(infer::RelateParamBound(_, t, _)) = origin {
let return_impl_trait = self
.in_progress_typeck_results
.map(|typeck_results| typeck_results.borrow().hir_owner)
"first, the lifetime cannot outlive ",
sup_region,
"...",
+ None,
);
debug!("report_sub_sup_conflict: var_origin={:?}", var_origin);
"...but the lifetime must also be valid for ",
sub_region,
"...",
+ None,
);
err.span_note(
sup_trace.cause.span,
"but, the lifetime must be valid for ",
sub_region,
"...",
+ None,
);
self.note_region_origin(&mut err, &sub_origin);
if let (UnderspecifiedArgKind::Const { .. }, Some(parent_data)) =
(&arg_data.kind, &arg_data.parent)
{
- let has_impl_trait =
- self.tcx.generics_of(parent_data.def_id).params.iter().any(|param| {
- matches!(
- param.kind,
- ty::GenericParamDefKind::Type {
- synthetic: Some(
- hir::SyntheticTyParamKind::ImplTrait
- | hir::SyntheticTyParamKind::FromAttr,
- ),
- ..
- }
- )
- });
-
// (#83606): Do not emit a suggestion if the parent has an `impl Trait`
// as an argument otherwise it will cause the E0282 error.
- if !has_impl_trait {
+ if !self.tcx.generics_of(parent_data.def_id).has_impl_trait()
+ || self.tcx.features().explicit_generic_args_with_impl_trait
+ {
err.span_suggestion_verbose(
span,
"consider specifying the const argument",
let borrow = typeck_results.borrow();
if let Some((DefKind::AssocFn, did)) = borrow.type_dependent_def(e.hir_id) {
let generics = self.tcx.generics_of(did);
- if !generics.params.is_empty() {
+ if !generics.params.is_empty() && !generics.has_impl_trait() {
err.span_suggestion_verbose(
segment.ident.span.shrink_to_hi(),
&format!(
multi_span
.push_span_label(binding_span, "introduces a `'static` lifetime requirement".into());
err.span_note(multi_span, "because this has an unmet lifetime requirement");
- note_and_explain_region(self.tcx(), &mut err, "", sup, "...");
+ note_and_explain_region(self.tcx(), &mut err, "", sup, "...", Some(binding_span));
if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) {
// If an impl is local, then maybe this isn't what they want. Try to
// be as helpful as possible with implicit lifetimes.
),
);
}
- infer::RelateParamBound(span, t) => {
+ infer::RelateParamBound(span, t, opt_span) => {
label_or_note(
span,
&format!(
- "...so that the type `{}` will meet its required lifetime bounds",
- self.ty_to_string(t)
+ "...so that the type `{}` will meet its required lifetime bounds{}",
+ self.ty_to_string(t),
+ if opt_span.is_some() { "..." } else { "" },
),
);
+ if let Some(span) = opt_span {
+ err.span_note(span, "...that is required by this bound");
+ }
}
infer::RelateRegionParamBound(span) => {
label_or_note(
"",
sup,
" doesn't meet the lifetime requirements",
+ None,
);
}
(_, ty::RePlaceholder(_)) => {
"the required lifetime does not necessarily outlive ",
sub,
"",
+ None,
);
}
_ => {
- note_and_explain_region(self.tcx, &mut err, "", sup, "...");
+ note_and_explain_region(self.tcx, &mut err, "", sup, "...", None);
note_and_explain_region(
self.tcx,
&mut err,
"...does not necessarily outlive ",
sub,
"",
+ None,
);
}
}
"...the reference is valid for ",
sub,
"...",
+ None,
);
note_and_explain_region(
self.tcx,
"...but the borrowed content is only valid for ",
sup,
"",
+ None,
);
err
}
"...the borrowed pointer is valid for ",
sub,
"...",
+ None,
);
note_and_explain_region(
self.tcx,
&format!("...but `{}` is only valid for ", var_name),
sup,
"",
+ None,
);
err
}
"lifetime of the source pointer does not outlive lifetime bound of the \
object type"
);
- note_and_explain_region(self.tcx, &mut err, "object type is valid for ", sub, "");
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "object type is valid for ",
+ sub,
+ "",
+ None,
+ );
note_and_explain_region(
self.tcx,
&mut err,
"source pointer is only valid for ",
sup,
"",
+ None,
);
err
}
- infer::RelateParamBound(span, ty) => {
+ infer::RelateParamBound(span, ty, opt_span) => {
let mut err = struct_span_err!(
self.tcx.sess,
span,
self.ty_to_string(ty)
);
match *sub {
- ty::ReStatic => {
- note_and_explain_region(self.tcx, &mut err, "type must satisfy ", sub, "")
- }
- _ => note_and_explain_region(self.tcx, &mut err, "type must outlive ", sub, ""),
+ ty::ReStatic => note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "type must satisfy ",
+ sub,
+ if opt_span.is_some() { " as required by this binding" } else { "" },
+ opt_span,
+ ),
+ _ => note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "type must outlive ",
+ sub,
+ if opt_span.is_some() { " as required by this binding" } else { "" },
+ opt_span,
+ ),
}
err
}
"lifetime parameter instantiated with ",
sup,
"",
+ None,
);
note_and_explain_region(
self.tcx,
"but lifetime parameter must outlive ",
sub,
"",
+ None,
);
err
}
"the return value is only valid for ",
sup,
"",
+ None,
);
err
}
"a value of type `{}` is borrowed for too long",
self.ty_to_string(ty)
);
- note_and_explain_region(self.tcx, &mut err, "the type is valid for ", sub, "");
- note_and_explain_region(self.tcx, &mut err, "but the borrow lasts for ", sup, "");
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "the type is valid for ",
+ sub,
+ "",
+ None,
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "but the borrow lasts for ",
+ sup,
+ "",
+ None,
+ );
err
}
infer::ReferenceOutlivesReferent(ty, span) => {
"in type `{}`, reference has a longer lifetime than the data it references",
self.ty_to_string(ty)
);
- note_and_explain_region(self.tcx, &mut err, "the pointer is valid for ", sub, "");
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "the pointer is valid for ",
+ sub,
+ "",
+ None,
+ );
note_and_explain_region(
self.tcx,
&mut err,
"but the referenced data is only valid for ",
sup,
"",
+ None,
);
err
}
/// Some type parameter was instantiated with the given type,
/// and that type must outlive some region.
- RelateParamBound(Span, Ty<'tcx>),
+ RelateParamBound(Span, Ty<'tcx>, Option<Span>),
/// The given region parameter was instantiated with a region
/// that must outlive some other region.
match *self {
Subtype(ref a) => a.span(),
RelateObjectBound(a) => a,
- RelateParamBound(a, _) => a,
+ RelateParamBound(a, ..) => a,
RelateRegionParamBound(a) => a,
Reborrow(a) => a,
ReborrowUpvar(a, _) => a,
use crate::infer::{
self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, UndoLog, VerifyBound,
};
-use crate::traits::ObligationCause;
+use crate::traits::{ObligationCause, ObligationCauseCode};
use rustc_middle::ty::outlives::Component;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, Region, Ty, TyCtxt, TypeFoldable};
cause: &ObligationCause<'tcx>,
) {
let origin = SubregionOrigin::from_obligation_cause(cause, || {
- infer::RelateParamBound(cause.span, sup_type)
+ infer::RelateParamBound(
+ cause.span,
+ sup_type,
+ match cause.code.peel_derives() {
+ ObligationCauseCode::BindingObligation(_, span) => Some(*span),
+ _ => None,
+ },
+ )
});
self.register_region_obligation(
tracked!(new_llvm_pass_manager, Some(true));
tracked!(no_generate_arange_section, true);
tracked!(no_link, true);
+ tracked!(no_profiler_runtime, true);
tracked!(osx_rpath_install_name, true);
tracked!(panic_abort_tests, true);
tracked!(plt, Some(true));
tracked!(print_fuel, Some("abc".to_string()));
tracked!(profile, true);
tracked!(profile_emit, Some(PathBuf::from("abc")));
- tracked!(profiler_runtime, None);
+ tracked!(profiler_runtime, "abc".to_string());
tracked!(relax_elf_relocations, Some(true));
tracked!(relro_level, Some(RelroLevel::Full));
tracked!(simulate_remapped_rust_src_base, Some(PathBuf::from("/rustc/abc")));
use rustc_feature::{GateIssue, Stability};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet};
+use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID};
use rustc_hir::{ForeignItemKind, GenericParamKind, PatKind};
use rustc_hir::{HirId, Node};
use rustc_index::vec::Idx;
fn check_missing_docs_attrs(
&self,
cx: &LateContext<'_>,
- id: hir::HirId,
+ def_id: LocalDefId,
sp: Span,
article: &'static str,
desc: &'static str,
// Only check publicly-visible items, using the result from the privacy pass.
// It's an option so the crate root can also use this function (it doesn't
// have a `NodeId`).
- if id != hir::CRATE_HIR_ID {
- if !cx.access_levels.is_exported(id) {
+ if def_id != CRATE_DEF_ID {
+ if !cx.access_levels.is_exported(def_id) {
return;
}
}
- let attrs = cx.tcx.hir().attrs(id);
+ let attrs = cx.tcx.get_attrs(def_id.to_def_id());
let has_doc = attrs.iter().any(|a| has_doc(cx.sess(), a));
if !has_doc {
cx.struct_span_lint(
}
fn check_crate(&mut self, cx: &LateContext<'_>, krate: &hir::Crate<'_>) {
- self.check_missing_docs_attrs(cx, hir::CRATE_HIR_ID, krate.module().inner, "the", "crate");
+ self.check_missing_docs_attrs(cx, CRATE_DEF_ID, krate.module().inner, "the", "crate");
for macro_def in krate.exported_macros() {
// Non exported macros should be skipped, since `missing_docs` only
// applies to externally visible items.
- if !cx.access_levels.is_exported(macro_def.hir_id()) {
+ if !cx.access_levels.is_exported(macro_def.def_id) {
continue;
}
let (article, desc) = cx.tcx.article_and_description(it.def_id.to_def_id());
- self.check_missing_docs_attrs(cx, it.hir_id(), it.span, article, desc);
+ self.check_missing_docs_attrs(cx, it.def_id, it.span, article, desc);
}
fn check_trait_item(&mut self, cx: &LateContext<'_>, trait_item: &hir::TraitItem<'_>) {
let (article, desc) = cx.tcx.article_and_description(trait_item.def_id.to_def_id());
- self.check_missing_docs_attrs(cx, trait_item.hir_id(), trait_item.span, article, desc);
+ self.check_missing_docs_attrs(cx, trait_item.def_id, trait_item.span, article, desc);
}
fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
}
let (article, desc) = cx.tcx.article_and_description(impl_item.def_id.to_def_id());
- self.check_missing_docs_attrs(cx, impl_item.hir_id(), impl_item.span, article, desc);
+ self.check_missing_docs_attrs(cx, impl_item.def_id, impl_item.span, article, desc);
}
fn check_foreign_item(&mut self, cx: &LateContext<'_>, foreign_item: &hir::ForeignItem<'_>) {
let (article, desc) = cx.tcx.article_and_description(foreign_item.def_id.to_def_id());
- self.check_missing_docs_attrs(cx, foreign_item.hir_id(), foreign_item.span, article, desc);
+ self.check_missing_docs_attrs(cx, foreign_item.def_id, foreign_item.span, article, desc);
}
fn check_field_def(&mut self, cx: &LateContext<'_>, sf: &hir::FieldDef<'_>) {
if !sf.is_positional() {
- self.check_missing_docs_attrs(cx, sf.hir_id, sf.span, "a", "struct field")
+ let def_id = cx.tcx.hir().local_def_id(sf.hir_id);
+ self.check_missing_docs_attrs(cx, def_id, sf.span, "a", "struct field")
}
}
fn check_variant(&mut self, cx: &LateContext<'_>, v: &hir::Variant<'_>) {
- self.check_missing_docs_attrs(cx, v.id, v.span, "a", "variant");
+ self.check_missing_docs_attrs(cx, cx.tcx.hir().local_def_id(v.id), v.span, "a", "variant");
}
}
impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
- if !cx.access_levels.is_reachable(item.hir_id()) {
+ if !cx.access_levels.is_reachable(item.def_id) {
return;
}
let (def, ty) = match item.kind {
impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations {
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
- if !cx.access_levels.is_reachable(item.hir_id()) {
+ if !cx.access_levels.is_reachable(item.def_id) {
return;
}
&self,
cx: &LateContext<'_>,
what: &str,
- id: hir::HirId,
+ def_id: LocalDefId,
vis: &hir::Visibility<'_>,
span: Span,
exportable: bool,
) {
let mut applicability = Applicability::MachineApplicable;
match vis.node {
- hir::VisibilityKind::Public if !cx.access_levels.is_reachable(id) => {
+ hir::VisibilityKind::Public if !cx.access_levels.is_reachable(def_id) => {
if span.from_expansion() {
applicability = Applicability::MaybeIncorrect;
}
impl<'tcx> LateLintPass<'tcx> for UnreachablePub {
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
- self.perform_lint(cx, "item", item.hir_id(), &item.vis, item.span, true);
+ self.perform_lint(cx, "item", item.def_id, &item.vis, item.span, true);
}
fn check_foreign_item(&mut self, cx: &LateContext<'_>, foreign_item: &hir::ForeignItem<'tcx>) {
self.perform_lint(
cx,
"item",
- foreign_item.hir_id(),
+ foreign_item.def_id,
&foreign_item.vis,
foreign_item.span,
true,
}
fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) {
- self.perform_lint(cx, "field", field.hir_id, &field.vis, field.span, false);
+ let def_id = cx.tcx.hir().local_def_id(field.hir_id);
+ self.perform_lint(cx, "field", def_id, &field.vis, field.span, false);
}
fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
- self.perform_lint(cx, "item", impl_item.hir_id(), &impl_item.vis, impl_item.span, false);
+ self.perform_lint(cx, "item", impl_item.def_id, &impl_item.vis, impl_item.span, false);
}
}
db.note(&format!("to ignore the value produced by the macro, add a semicolon after the invocation of `{name}`"));
}
}
+ BuiltinLintDiagnostics::BreakWithLabelAndLoop(span) => {
+ db.multipart_suggestion(
+ "wrap this expression in parentheses",
+ vec![(span.shrink_to_lo(), "(".to_string()),
+ (span.shrink_to_hi(), ")".to_string())],
+ Applicability::MachineApplicable
+ );
+ }
}
// Rewrap `db`, and pass control to the user.
decorate(LintDiagnosticBuilder::new(db));
/// The asm block must not contain any operands other than `const` and
/// `sym`. Additionally, naked function should specify a non-Rust ABI.
///
+ /// Naked functions cannot be inlined. All forms of the `inline` attribute
+ /// are prohibited.
+ ///
/// While other definitions of naked functions were previously accepted,
/// they are unsupported and might not work reliably. This is a
/// [future-incompatible] lint that will transition into hard error in
RUST_2021_PRELUDE_COLLISIONS,
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
UNSUPPORTED_CALLING_CONVENTIONS,
+ BREAK_WITH_LABEL_AND_LOOP,
]
}
reference: "issue #00000 <https://github.com/rust-lang/rust/issues/00000>",
};
}
+
+declare_lint! {
+ /// The `break_with_label_and_loop` lint detects labeled `break` expressions with
+ /// an unlabeled loop as their value expression.
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// 'label: loop {
+ /// break 'label loop { break 42; };
+ /// };
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// In Rust, loops can have a label, and `break` expressions can refer to that label to
+ /// break out of specific loops (and not necessarily the innermost one). `break` expressions
+ /// can also carry a value expression, which can be another loop. A labeled `break` with an
+ /// unlabeled loop as its value expression is easy to confuse with an unlabeled break with
+ /// a labeled loop and is thus discouraged (but allowed for compatibility); use parentheses
+ /// around the loop expression to silence this warning. Unlabeled `break` expressions with
+ /// labeled loops yield a hard error, which can also be silenced by wrapping the expression
+ /// in parentheses.
+ pub BREAK_WITH_LABEL_AND_LOOP,
+ Warn,
+ "`break` expression with label and unlabeled loop as value expression"
+}
OrPatternsBackCompat(Span, String),
ReservedPrefix(Span),
TrailingMacro(bool, Ident),
+ BreakWithLabelAndLoop(Span),
}
/// Lints that are buffered up early on in the `Session` before the
LLVMSetLinkage(V, fromRust(RustLinkage));
}
+extern "C" LLVMValueRef LLVMRustConstInBoundsGEP2(LLVMTypeRef Ty,
+ LLVMValueRef ConstantVal,
+ LLVMValueRef *ConstantIndices,
+ unsigned NumIndices) {
+ ArrayRef<Constant *> IdxList(unwrap<Constant>(ConstantIndices, NumIndices),
+ NumIndices);
+ Constant *Val = unwrap<Constant>(ConstantVal);
+ return wrap(ConstantExpr::getInBoundsGetElementPtr(unwrap(Ty), Val, IdxList));
+}
+
// Returns true if both high and low were successfully set. Fails in case constant wasn’t any of
// the common sizes (1, 8, 16, 32, 64, 128 bits)
extern "C" bool LLVMRustConstInt128Get(LLVMValueRef CV, bool sext, uint64_t *high, uint64_t *low)
}
fn inject_profiler_runtime(&mut self, krate: &ast::Crate) {
- let profiler_runtime = &self.sess.opts.debugging_opts.profiler_runtime;
-
- if !(profiler_runtime.is_some()
- && (self.sess.instrument_coverage()
+ if self.sess.opts.debugging_opts.no_profiler_runtime
+ || !(self.sess.instrument_coverage()
|| self.sess.opts.debugging_opts.profile
- || self.sess.opts.cg.profile_generate.enabled()))
+ || self.sess.opts.cg.profile_generate.enabled())
{
return;
}
info!("loading profiler");
- let name = Symbol::intern(profiler_runtime.as_ref().unwrap());
+ let name = Symbol::intern(&self.sess.opts.debugging_opts.profiler_runtime);
if name == sym::profiler_builtins && self.sess.contains_name(&krate.attrs, sym::no_core) {
self.sess.err(
"`profiler_builtins` crate (required by compiler options) \
if sess.is_nightly_build() {
err.help("consider building the standard library from source with `cargo build -Zbuild-std`");
}
- } else if Some(crate_name)
- == sess.opts.debugging_opts.profiler_runtime.as_deref().map(Symbol::intern)
+ } else if crate_name
+ == Symbol::intern(&sess.opts.debugging_opts.profiler_runtime)
{
err.note(&"the compiler may have been built without the profiler runtime");
}
self.body_const_context(self.local_def_id(self.enclosing_body_owner(hir_id))).is_some()
}
- /// Whether `hir_id` corresponds to a `mod` or a crate.
- pub fn is_hir_id_module(&self, hir_id: HirId) -> bool {
- matches!(
- self.get(hir_id),
- Node::Item(Item { kind: ItemKind::Mod(_), .. }) | Node::Crate(..)
- )
- }
-
/// Retrieves the `HirId` for `id`'s enclosing method, unless there's a
/// `while` or `loop` before reaching it, as block tail returns are not
/// available in them.
/// `#[rustc_allocator]`: a hint to LLVM that the pointer returned from this
/// function is never null.
const ALLOCATOR = 1 << 1;
- /// `#[unwind]`: an indicator that this function may unwind despite what
- /// its ABI signature may otherwise imply.
- const UNWIND = 1 << 2;
- /// `#[rust_allocator_nounwind]`, an indicator that an imported FFI
- /// function will never unwind. Probably obsolete by recent changes with
- /// #[unwind], but hasn't been removed/migrated yet
- const RUSTC_ALLOCATOR_NOUNWIND = 1 << 3;
+ /// An indicator that function will never unwind. Will become obsolete
+ /// once C-unwind is fully stabilized.
+ const NEVER_UNWIND = 1 << 3;
/// `#[naked]`: an indicator to LLVM that no function prologue/epilogue
/// should be generated.
const NAKED = 1 << 4;
//! which are available for use externally when compiled as a library.
use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::HirId;
use rustc_macros::HashStable;
-use std::fmt;
+use rustc_span::def_id::LocalDefId;
use std::hash::Hash;
/// Represents the levels of accessibility an item can have.
}
/// Holds a map of accessibility levels for reachable HIR nodes.
-#[derive(Clone)]
-pub struct AccessLevels<Id = HirId> {
+#[derive(Debug)]
+pub struct AccessLevels<Id = LocalDefId> {
pub map: FxHashMap<Id, AccessLevel>,
}
}
}
-impl<Id: Hash + Eq> Default for AccessLevels<Id> {
+impl<Id> Default for AccessLevels<Id> {
fn default() -> Self {
AccessLevels { map: Default::default() }
}
}
-
-impl<Id: Hash + Eq + fmt::Debug> fmt::Debug for AccessLevels<Id> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt::Debug::fmt(&self.map, f)
- }
-}
use rustc_feature::GateIssue;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
-use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX};
+use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_INDEX};
use rustc_hir::{self, HirId};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE};
pub attr: Deprecation,
/// The `DefId` where the attr was originally attached. `None` for non-local
/// `DefId`'s.
- origin: Option<HirId>,
+ origin: Option<LocalDefId>,
}
impl DeprecationEntry {
- pub fn local(attr: Deprecation, id: HirId) -> DeprecationEntry {
- DeprecationEntry { attr, origin: Some(id) }
+ pub fn local(attr: Deprecation, def_id: LocalDefId) -> DeprecationEntry {
+ DeprecationEntry { attr, origin: Some(def_id) }
}
pub fn external(attr: Deprecation) -> DeprecationEntry {
pub struct Index<'tcx> {
/// This is mostly a cache, except the stabilities of local items
/// are filled by the annotator.
- pub stab_map: FxHashMap<HirId, &'tcx Stability>,
- pub const_stab_map: FxHashMap<HirId, &'tcx ConstStability>,
- pub depr_map: FxHashMap<HirId, DeprecationEntry>,
+ pub stab_map: FxHashMap<LocalDefId, &'tcx Stability>,
+ pub const_stab_map: FxHashMap<LocalDefId, &'tcx ConstStability>,
+ pub depr_map: FxHashMap<LocalDefId, DeprecationEntry>,
/// Maps for each crate whether it is part of the staged API.
pub staged_api: FxHashMap<CrateNum, bool>,
}
impl<'tcx> Index<'tcx> {
- pub fn local_stability(&self, id: HirId) -> Option<&'tcx Stability> {
- self.stab_map.get(&id).cloned()
+ pub fn local_stability(&self, def_id: LocalDefId) -> Option<&'tcx Stability> {
+ self.stab_map.get(&def_id).copied()
}
- pub fn local_const_stability(&self, id: HirId) -> Option<&'tcx ConstStability> {
- self.const_stab_map.get(&id).cloned()
+ pub fn local_const_stability(&self, def_id: LocalDefId) -> Option<&'tcx ConstStability> {
+ self.const_stab_map.get(&def_id).copied()
}
- pub fn local_deprecation_entry(&self, id: HirId) -> Option<DeprecationEntry> {
- self.depr_map.get(&id).cloned()
+ pub fn local_deprecation_entry(&self, def_id: LocalDefId) -> Option<DeprecationEntry> {
+ self.depr_map.get(&def_id).cloned()
}
}
use rustc_target::abi::{Align, HasDataLayout, Size};
use super::{
- read_target_uint, write_target_uint, AllocId, InterpError, InterpResult, Pointer,
+ read_target_uint, write_target_uint, AllocId, InterpError, InterpResult, Pointer, Provenance,
ResourceExhaustionInfo, Scalar, ScalarMaybeUninit, UndefinedBehaviorInfo, UninitBytesAccess,
UnsupportedOpInfo,
};
pub enum AllocError {
/// Encountered a pointer where we needed raw bytes.
ReadPointerAsBytes,
+ /// Partially overwriting a pointer.
+ PartialPointerOverwrite(Size),
/// Using uninitialized data where it is not allowed.
InvalidUninitBytes(Option<UninitBytesAccess>),
}
impl AllocError {
pub fn to_interp_error<'tcx>(self, alloc_id: AllocId) -> InterpError<'tcx> {
+ use AllocError::*;
match self {
- AllocError::ReadPointerAsBytes => {
- InterpError::Unsupported(UnsupportedOpInfo::ReadPointerAsBytes)
- }
- AllocError::InvalidUninitBytes(info) => InterpError::UndefinedBehavior(
+ ReadPointerAsBytes => InterpError::Unsupported(UnsupportedOpInfo::ReadPointerAsBytes),
+ PartialPointerOverwrite(offset) => InterpError::Unsupported(
+ UnsupportedOpInfo::PartialPointerOverwrite(Pointer::new(alloc_id, offset)),
+ ),
+ InvalidUninitBytes(info) => InterpError::UndefinedBehavior(
UndefinedBehaviorInfo::InvalidUninitBytes(info.map(|b| (alloc_id, b))),
),
}
}
/// Byte accessors.
-impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
+impl<Tag: Provenance, Extra> Allocation<Tag, Extra> {
/// The last argument controls whether we error out when there are uninitialized
/// or pointer bytes. You should never call this, call `get_bytes` or
/// `get_bytes_with_uninit_and_ptr` instead,
/// It is the caller's responsibility to check bounds and alignment beforehand.
/// Most likely, you want to use the `PlaceTy` and `OperandTy`-based methods
/// on `InterpCx` instead.
- pub fn get_bytes_mut(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> &mut [u8] {
+ pub fn get_bytes_mut(
+ &mut self,
+ cx: &impl HasDataLayout,
+ range: AllocRange,
+ ) -> AllocResult<&mut [u8]> {
self.mark_init(range, true);
- self.clear_relocations(cx, range);
+ self.clear_relocations(cx, range)?;
- &mut self.bytes[range.start.bytes_usize()..range.end().bytes_usize()]
+ Ok(&mut self.bytes[range.start.bytes_usize()..range.end().bytes_usize()])
}
/// A raw pointer variant of `get_bytes_mut` that avoids invalidating existing aliases into this memory.
- pub fn get_bytes_mut_ptr(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> *mut [u8] {
+ pub fn get_bytes_mut_ptr(
+ &mut self,
+ cx: &impl HasDataLayout,
+ range: AllocRange,
+ ) -> AllocResult<*mut [u8]> {
self.mark_init(range, true);
- // This also clears relocations that just overlap with the written range. So writing to some
- // byte can de-initialize its neighbors! See
- // <https://github.com/rust-lang/rust/issues/87184> for details.
- self.clear_relocations(cx, range);
+ self.clear_relocations(cx, range)?;
assert!(range.end().bytes_usize() <= self.bytes.len()); // need to do our own bounds-check
let begin_ptr = self.bytes.as_mut_ptr().wrapping_add(range.start.bytes_usize());
let len = range.end().bytes_usize() - range.start.bytes_usize();
- ptr::slice_from_raw_parts_mut(begin_ptr, len)
+ Ok(ptr::slice_from_raw_parts_mut(begin_ptr, len))
}
}
/// Reading and writing.
-impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
+impl<Tag: Provenance, Extra> Allocation<Tag, Extra> {
/// Validates that `ptr.offset` and `ptr.offset + size` do not point to the middle of a
/// relocation. If `allow_uninit_and_ptr` is `false`, also enforces that the memory in the
/// given range contains neither relocations nor uninitialized bytes.
};
let endian = cx.data_layout().endian;
- let dst = self.get_bytes_mut(cx, range);
+ let dst = self.get_bytes_mut(cx, range)?;
write_target_uint(endian, dst, bytes).unwrap();
// See if we have to also write a relocation.
/// uninitialized. This is a somewhat odd "spooky action at a distance",
/// but it allows strictly more code to run than if we would just error
/// immediately in that case.
- fn clear_relocations(&mut self, cx: &impl HasDataLayout, range: AllocRange) {
+ fn clear_relocations(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult
+ where
+ Tag: Provenance,
+ {
// Find the start and end of the given range and its outermost relocations.
let (first, last) = {
// Find all relocations overlapping the given range.
let relocations = self.get_relocations(cx, range);
if relocations.is_empty() {
- return;
+ return Ok(());
}
(
let start = range.start;
let end = range.end();
- // Mark parts of the outermost relocations as uninitialized if they partially fall outside the
- // given range.
+ // We need to handle clearing the relocations from parts of a pointer. See
+ // <https://github.com/rust-lang/rust/issues/87184> for details.
if first < start {
+ if Tag::ERR_ON_PARTIAL_PTR_OVERWRITE {
+ return Err(AllocError::PartialPointerOverwrite(first));
+ }
self.init_mask.set_range(first, start, false);
}
if last > end {
+ if Tag::ERR_ON_PARTIAL_PTR_OVERWRITE {
+ return Err(AllocError::PartialPointerOverwrite(
+ last - cx.data_layout().pointer_size,
+ ));
+ }
self.init_mask.set_range(end, last, false);
}
// Forget all the relocations.
self.relocations.0.remove_range(first..last);
+
+ Ok(())
}
/// Errors if there are relocations overlapping with the edges of the
Unsupported(String),
/// Encountered a pointer where we needed raw bytes.
ReadPointerAsBytes,
+ /// Overwriting parts of a pointer; the resulting state cannot be represented in our
+ /// `Allocation` data structure.
+ PartialPointerOverwrite(Pointer<AllocId>),
//
// The variants below are only reachable from CTFE/const prop, miri will never emit them.
//
use UnsupportedOpInfo::*;
match self {
Unsupported(ref msg) => write!(f, "{}", msg),
- ReadExternStatic(did) => write!(f, "cannot read from extern static ({:?})", did),
- ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes",),
+ ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes"),
+ PartialPointerOverwrite(ptr) => {
+ write!(f, "unable to overwrite parts of a pointer in memory at {:?}", ptr)
+ }
ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({:?})", did),
+ ReadExternStatic(did) => write!(f, "cannot read from extern static ({:?})", did),
}
}
}
/// If `false`, ptr-to-int casts are not supported. The offset *must* be relative in that case.
const OFFSET_IS_ADDR: bool;
+ /// We also use this trait to control whether to abort execution when a pointer is being partially overwritten
+ /// (this avoids a separate trait in `allocation.rs` just for this purpose).
+ const ERR_ON_PARTIAL_PTR_OVERWRITE: bool;
+
/// Determines how a pointer should be printed.
fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result
where
// so ptr-to-int casts are not possible (since we do not know the global physical offset).
const OFFSET_IS_ADDR: bool = false;
+ // For now, do not allow this, so that we keep our options open.
+ const ERR_ON_PARTIAL_PTR_OVERWRITE: bool = true;
+
fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Forward `alternate` flag to `alloc_id` printing.
if f.alternate() {
/// consider it in borrowck. We don't want to accept programs which
/// pass borrowck only when `panic=abort` or some assertions are disabled
/// due to release vs. debug mode builds. This needs to be an `Option` because
- /// of the `remove_noop_landing_pads` and `no_landing_pads` passes.
+ /// of the `remove_noop_landing_pads` and `abort_unwinding_calls` passes.
unwind: Option<BasicBlock>,
},
desc { |tcx| "finding all vtable entries for trait {}", tcx.def_path_str(key.def_id()) }
}
+ query vtable_trait_upcasting_coercion_new_vptr_slot(key: (ty::PolyTraitRef<'tcx>, ty::PolyTraitRef<'tcx>)) -> Option<usize> {
+ desc { |tcx| "finding the slot within vtable for trait {} vtable ptr during trait upcasting coercion from {} vtable",
+ tcx.def_path_str(key.1.def_id()), tcx.def_path_str(key.0.def_id()) }
+ }
+
query codegen_fulfill_obligation(
key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)
) -> Result<ImplSource<'tcx, ()>, ErrorReported> {
tcx.arena.alloc(tcx.resolutions(()).glob_map.get(&id).cloned().unwrap_or_default())
};
- providers.lookup_stability = |tcx, id| {
- let id = tcx.hir().local_def_id_to_hir_id(id.expect_local());
- tcx.stability().local_stability(id)
- };
- providers.lookup_const_stability = |tcx, id| {
- let id = tcx.hir().local_def_id_to_hir_id(id.expect_local());
- tcx.stability().local_const_stability(id)
- };
- providers.lookup_deprecation_entry = |tcx, id| {
- let id = tcx.hir().local_def_id_to_hir_id(id.expect_local());
- tcx.stability().local_deprecation_entry(id)
- };
+ providers.lookup_stability = |tcx, id| tcx.stability().local_stability(id.expect_local());
+ providers.lookup_const_stability =
+ |tcx, id| tcx.stability().local_const_stability(id.expect_local());
+ providers.lookup_deprecation_entry =
+ |tcx, id| tcx.stability().local_deprecation_entry(id.expect_local());
providers.extern_mod_stmt_cnum =
|tcx, id| tcx.resolutions(()).extern_crate_map.get(&id).cloned();
providers.output_filenames = |tcx, ()| tcx.output_filenames.clone();
_ => bug!("expected const parameter, but found another generic parameter"),
}
}
+
+ /// Returns `true` if `params` has `impl Trait`.
+ pub fn has_impl_trait(&'tcx self) -> bool {
+ self.params.iter().any(|param| {
+ matches!(
+ param.kind,
+ ty::GenericParamDefKind::Type {
+ synthetic: Some(
+ hir::SyntheticTyParamKind::ImplTrait | hir::SyntheticTyParamKind::FromAttr,
+ ),
+ ..
+ }
+ )
+ })
+ }
}
/// Bounds on generics.
fn adjust_for_abi(&mut self, cx: &C, abi: SpecAbi);
}
+/// Calculates whether a function's ABI can unwind or not.
+///
+/// This takes two primary parameters:
+///
+/// * `codegen_fn_attr_flags` - these are flags calculated as part of the
+/// codegen attrs for a defined function. For function pointers this set of
+/// flags is the empty set. This is only applicable for Rust-defined
+/// functions, and generally isn't needed except for small optimizations where
+/// we try to say a function which otherwise might look like it could unwind
+/// doesn't actually unwind (such as for intrinsics and such).
+///
+/// * `abi` - this is the ABI that the function is defined with. This is the
+/// primary factor for determining whether a function can unwind or not.
+///
+/// Note that in this case unwinding is not necessarily panicking in Rust. Rust
+/// panics are implemented with unwinds on most platform (when
+/// `-Cpanic=unwind`), but this also accounts for `-Cpanic=abort` build modes.
+/// Notably unwinding is disallowed for more non-Rust ABIs unless it's
+/// specifically in the name (e.g. `"C-unwind"`). Unwinding within each ABI is
+/// defined for each ABI individually, but it always corresponds to some form of
+/// stack-based unwinding (the exact mechanism of which varies
+/// platform-by-platform).
+///
+/// Rust functions are classfied whether or not they can unwind based on the
+/// active "panic strategy". In other words Rust functions are considered to
+/// unwind in `-Cpanic=unwind` mode and cannot unwind in `-Cpanic=abort` mode.
+/// Note that Rust supports intermingling panic=abort and panic=unwind code, but
+/// only if the final panic mode is panic=abort. In this scenario any code
+/// previously compiled assuming that a function can unwind is still correct, it
+/// just never happens to actually unwind at runtime.
+///
+/// This function's answer to whether or not a function can unwind is quite
+/// impactful throughout the compiler. This affects things like:
+///
+/// * Calling a function which can't unwind means codegen simply ignores any
+/// associated unwinding cleanup.
+/// * Calling a function which can unwind from a function which can't unwind
+/// causes the `abort_unwinding_calls` MIR pass to insert a landing pad that
+/// aborts the process.
+/// * This affects whether functions have the LLVM `nounwind` attribute, which
+/// affects various optimizations and codegen.
+///
+/// FIXME: this is actually buggy with respect to Rust functions. Rust functions
+/// compiled with `-Cpanic=unwind` and referenced from another crate compiled
+/// with `-Cpanic=abort` will look like they can't unwind when in fact they
+/// might (from a foreign exception or similar).
pub fn fn_can_unwind(
- panic_strategy: PanicStrategy,
+ tcx: TyCtxt<'tcx>,
codegen_fn_attr_flags: CodegenFnAttrFlags,
- call_conv: Conv,
abi: SpecAbi,
) -> bool {
- if panic_strategy != PanicStrategy::Unwind {
- // In panic=abort mode we assume nothing can unwind anywhere, so
- // optimize based on this!
- false
- } else if codegen_fn_attr_flags.contains(CodegenFnAttrFlags::UNWIND) {
- // If a specific #[unwind] attribute is present, use that.
- true
- } else if codegen_fn_attr_flags.contains(CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND) {
- // Special attribute for allocator functions, which can't unwind.
- false
- } else {
- if call_conv == Conv::Rust {
- // Any Rust method (or `extern "Rust" fn` or `extern
- // "rust-call" fn`) is explicitly allowed to unwind
- // (unless it has no-unwind attribute, handled above).
- true
- } else {
- // Anything else is either:
- //
- // 1. A foreign item using a non-Rust ABI (like `extern "C" { fn foo(); }`), or
- //
- // 2. A Rust item using a non-Rust ABI (like `extern "C" fn foo() { ... }`).
- //
- // In both of these cases, we should refer to the ABI to determine whether or not we
- // should unwind. See Rust RFC 2945 for more information on this behavior, here:
- // https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md
- use SpecAbi::*;
- match abi {
- C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => {
- unwind
- }
- Cdecl
- | Fastcall
- | Vectorcall
- | Aapcs
- | Win64
- | SysV64
- | PtxKernel
- | Msp430Interrupt
- | X86Interrupt
- | AmdGpuKernel
- | EfiApi
- | AvrInterrupt
- | AvrNonBlockingInterrupt
- | CCmseNonSecureCall
- | Wasm
- | RustIntrinsic
- | PlatformIntrinsic
- | Unadjusted => false,
- // In the `if` above, we checked for functions with the Rust calling convention.
- Rust | RustCall => unreachable!(),
- }
+ // Special attribute for functions which can't unwind.
+ if codegen_fn_attr_flags.contains(CodegenFnAttrFlags::NEVER_UNWIND) {
+ return false;
+ }
+
+ // Otherwise if this isn't special then unwinding is generally determined by
+ // the ABI of the itself. ABIs like `C` have variants which also
+ // specifically allow unwinding (`C-unwind`), but not all platform-specific
+ // ABIs have such an option. Otherwise the only other thing here is Rust
+ // itself, and those ABIs are determined by the panic strategy configured
+ // for this compilation.
+ //
+ // Unfortunately at this time there's also another caveat. Rust [RFC
+ // 2945][rfc] has been accepted and is in the process of being implemented
+ // and stabilized. In this interim state we need to deal with historical
+ // rustc behavior as well as plan for future rustc behavior.
+ //
+ // Historically functions declared with `extern "C"` were marked at the
+ // codegen layer as `nounwind`. This happened regardless of `panic=unwind`
+ // or not. This is UB for functions in `panic=unwind` mode that then
+ // actually panic and unwind. Note that this behavior is true for both
+ // externally declared functions as well as Rust-defined function.
+ //
+ // To fix this UB rustc would like to change in the future to catch unwinds
+ // from function calls that may unwind within a Rust-defined `extern "C"`
+ // function and forcibly abort the process, thereby respecting the
+ // `nounwind` attribut emitted for `extern "C"`. This behavior change isn't
+ // ready to roll out, so determining whether or not the `C` family of ABIs
+ // unwinds is conditional not only on their definition but also whether the
+ // `#![feature(c_unwind)]` feature gate is active.
+ //
+ // Note that this means that unlike historical compilers rustc now, by
+ // default, unconditionally thinks that the `C` ABI may unwind. This will
+ // prevent some optimization opportunities, however, so we try to scope this
+ // change and only assume that `C` unwinds with `panic=unwind` (as opposed
+ // to `panic=abort`).
+ //
+ // Eventually the check against `c_unwind` here will ideally get removed and
+ // this'll be a little cleaner as it'll be a straightforward check of the
+ // ABI.
+ //
+ // [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md
+ use SpecAbi::*;
+ match abi {
+ C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => {
+ unwind
+ || (!tcx.features().c_unwind && tcx.sess.panic_strategy() == PanicStrategy::Unwind)
}
+ Cdecl
+ | Fastcall
+ | Vectorcall
+ | Aapcs
+ | Win64
+ | SysV64
+ | PtxKernel
+ | Msp430Interrupt
+ | X86Interrupt
+ | AmdGpuKernel
+ | EfiApi
+ | AvrInterrupt
+ | AvrNonBlockingInterrupt
+ | CCmseNonSecureCall
+ | Wasm
+ | RustIntrinsic
+ | PlatformIntrinsic
+ | Unadjusted => false,
+ Rust | RustCall => tcx.sess.panic_strategy() == PanicStrategy::Unwind,
}
}
}
}
-pub fn fn_ptr_codegen_fn_attr_flags() -> CodegenFnAttrFlags {
- // Assume that fn pointers may always unwind
- CodegenFnAttrFlags::UNWIND
-}
-
impl<'tcx, C> FnAbiExt<'tcx, C> for call::FnAbi<'tcx, Ty<'tcx>>
where
C: LayoutOf<Ty = Ty<'tcx>, TyAndLayout = TyAndLayout<'tcx>>
+ HasParamEnv<'tcx>,
{
fn of_fn_ptr(cx: &C, sig: ty::PolyFnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self {
- call::FnAbi::new_internal(cx, sig, extra_args, None, fn_ptr_codegen_fn_attr_flags(), false)
+ call::FnAbi::new_internal(cx, sig, extra_args, None, CodegenFnAttrFlags::empty(), false)
}
fn of_instance(cx: &C, instance: ty::Instance<'tcx>, extra_args: &[Ty<'tcx>]) -> Self {
c_variadic: sig.c_variadic,
fixed_count: inputs.len(),
conv,
- can_unwind: fn_can_unwind(
- cx.tcx().sess.panic_strategy(),
- codegen_fn_attr_flags,
- conv,
- sig.abi,
- ),
+ can_unwind: fn_can_unwind(cx.tcx(), codegen_fn_attr_flags, sig.abi),
};
fn_abi.adjust_for_abi(cx, sig.abi);
debug!("FnAbi::new_internal = {:?}", fn_abi);
GenericArgKind::Type(t1) => {
// we don't actually use this for anything, but
// the `TypeOutlives` code needs an origin.
- let origin = infer::RelateParamBound(DUMMY_SP, t1);
+ let origin = infer::RelateParamBound(DUMMY_SP, t1, None);
TypeOutlives::new(
&mut *self,
Immediate::new_slice(ptr, length.eval_usize(*self.tcx, self.param_env), self);
self.write_immediate(val, dest)
}
- (&ty::Dynamic(..), &ty::Dynamic(..)) => {
- // For now, upcasts are limited to changes in marker
- // traits, and hence never actually require an actual
- // change to the vtable.
+ (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
let val = self.read_immediate(src)?;
- self.write_immediate(*val, dest)
+ if data_a.principal_def_id() == data_b.principal_def_id() {
+ return self.write_immediate(*val, dest);
+ }
+ // trait upcasting coercion
+ let principal_a = data_a.principal().expect(
+ "unsize_into_ptr: missing principal trait for trait upcasting coercion",
+ );
+ let principal_b = data_b.principal().expect(
+ "unsize_into_ptr: missing principal trait for trait upcasting coercion",
+ );
+
+ let vptr_entry_idx = self.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((
+ principal_a.with_self_ty(*self.tcx, src_pointee_ty),
+ principal_b.with_self_ty(*self.tcx, src_pointee_ty),
+ ));
+
+ if let Some(entry_idx) = vptr_entry_idx {
+ let entry_idx = u64::try_from(entry_idx).unwrap();
+ let (old_data, old_vptr) = val.to_scalar_pair()?;
+ let old_vptr = self.scalar_to_ptr(old_vptr);
+ let new_vptr = self
+ .read_new_vtable_after_trait_upcasting_from_vtable(old_vptr, entry_idx)?;
+ self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)
+ } else {
+ self.write_immediate(*val, dest)
+ }
}
(_, &ty::Dynamic(ref data, _)) => {
// Initial cast from sized to dyn trait
}
/// Reading and writing.
-impl<'tcx, 'a, Tag: Copy, Extra> AllocRefMut<'a, 'tcx, Tag, Extra> {
+impl<'tcx, 'a, Tag: Provenance, Extra> AllocRefMut<'a, 'tcx, Tag, Extra> {
pub fn write_scalar(
&mut self,
range: AllocRange,
}
}
-impl<'tcx, 'a, Tag: Copy, Extra> AllocRef<'a, 'tcx, Tag, Extra> {
+impl<'tcx, 'a, Tag: Provenance, Extra> AllocRef<'a, 'tcx, Tag, Extra> {
pub fn read_scalar(&self, range: AllocRange) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> {
Ok(self
.alloc
// Side-step AllocRef and directly access the underlying bytes more efficiently.
// (We are staying inside the bounds here so all is good.)
- let bytes = alloc_ref.alloc.get_bytes_mut(&alloc_ref.tcx, alloc_ref.range);
+ let alloc_id = alloc_ref.alloc_id;
+ let bytes = alloc_ref
+ .alloc
+ .get_bytes_mut(&alloc_ref.tcx, alloc_ref.range)
+ .map_err(move |e| e.to_interp_error(alloc_id))?;
// `zip` would stop when the first iterator ends; we want to definitely
// cover all of `bytes`.
for dest in bytes {
let (dest_alloc, extra) = self.get_raw_mut(dest_alloc_id)?;
let dest_range = alloc_range(dest_offset, size * num_copies);
M::memory_written(extra, &mut dest_alloc.extra, dest.provenance, dest_range)?;
- let dest_bytes = dest_alloc.get_bytes_mut_ptr(&tcx, dest_range).as_mut_ptr();
+ let dest_bytes = dest_alloc
+ .get_bytes_mut_ptr(&tcx, dest_range)
+ .map_err(|e| e.to_interp_error(dest_alloc_id))?
+ .as_mut_ptr();
if compressed.no_bytes_init() {
// Fast path: If all bytes are `uninit` then there is nothing to copy. The target range
Immediate::ScalarPair(val.into(), Scalar::from_machine_usize(len, cx).into())
}
- pub fn new_dyn_trait(val: Scalar<Tag>, vtable: Pointer<Tag>, cx: &impl HasDataLayout) -> Self {
- Immediate::ScalarPair(val.into(), ScalarMaybeUninit::from_pointer(vtable, cx))
+ pub fn new_dyn_trait(
+ val: Scalar<Tag>,
+ vtable: Pointer<Option<Tag>>,
+ cx: &impl HasDataLayout,
+ ) -> Self {
+ Immediate::ScalarPair(val.into(), ScalarMaybeUninit::from_maybe_pointer(vtable, cx))
}
#[inline]
pub fn to_scalar_or_uninit(self) -> ScalarMaybeUninit<Tag> {
match self {
Immediate::Scalar(val) => val,
- Immediate::ScalarPair(..) => bug!("Got a wide pointer where a scalar was expected"),
+ Immediate::ScalarPair(..) => bug!("Got a scalar pair where a scalar was expected"),
}
}
pub fn to_scalar(self) -> InterpResult<'tcx, Scalar<Tag>> {
self.to_scalar_or_uninit().check_init()
}
+
+ #[inline]
+ pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar<Tag>, Scalar<Tag>)> {
+ match self {
+ Immediate::ScalarPair(val1, val2) => Ok((val1.check_init()?, val2.check_init()?)),
+ Immediate::Scalar(..) => {
+ bug!("Got a scalar where a scalar pair was expected")
+ }
+ }
+ }
}
// ScalarPair needs a type to interpret, so we often have an immediate and a type together
let ptr = self.global_base_pointer(Pointer::new(id, offset))?;
Operand::Indirect(MemPlace::from_ptr(ptr.into(), layout.align.abi))
}
- ConstValue::Scalar(x) => Operand::Immediate(tag_scalar(x.into())?.into()),
+ ConstValue::Scalar(x) => Operand::Immediate(tag_scalar(x)?.into()),
ConstValue::Slice { data, start, end } => {
// We rely on mutability being set correctly in `data` to prevent writes
// where none should happen.
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
fn fn_can_unwind(&self, attrs: CodegenFnAttrFlags, abi: Abi) -> bool {
- layout::fn_can_unwind(
- self.tcx.sess.panic_strategy(),
- attrs,
- layout::conv_from_spec_abi(*self.tcx, abi),
- abi,
- )
+ layout::fn_can_unwind(*self.tcx, attrs, abi)
}
pub(super) fn eval_terminator(
ty::FnPtr(sig) => {
let caller_abi = sig.abi();
let fn_ptr = self.read_pointer(&func)?;
- let fn_val = self.memory.get_fn(fn_ptr.into())?;
+ let fn_val = self.memory.get_fn(fn_ptr)?;
(
fn_val,
caller_abi,
- self.fn_can_unwind(layout::fn_ptr_codegen_fn_attr_flags(), caller_abi),
+ self.fn_can_unwind(CodegenFnAttrFlags::empty(), caller_abi),
)
}
ty::FnDef(def_id, substs) => {
&mut self,
ty: Ty<'tcx>,
poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
- ) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
+ ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> {
trace!("get_vtable(trait_ref={:?})", poly_trait_ref);
let (ty, poly_trait_ref) = self.tcx.erase_regions((ty, poly_trait_ref));
let vtable_ptr = self.memory.global_base_pointer(Pointer::from(vtable_allocation))?;
- Ok(vtable_ptr)
+ Ok(vtable_ptr.into())
}
/// Resolves the function at the specified slot in the provided
}
Ok((Size::from_bytes(size), align))
}
+
+ pub fn read_new_vtable_after_trait_upcasting_from_vtable(
+ &self,
+ vtable: Pointer<Option<M::PointerTag>>,
+ idx: u64,
+ ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> {
+ let pointer_size = self.pointer_size();
+
+ let vtable_slot = vtable.offset(pointer_size * idx, self)?;
+ let new_vtable = self
+ .memory
+ .get(vtable_slot, pointer_size, self.tcx.data_layout.pointer_align.abi)?
+ .expect("cannot be a ZST");
+
+ let new_vtable = self.scalar_to_ptr(new_vtable.read_ptr_sized(Size::ZERO)?.check_init()?);
+
+ Ok(new_vtable)
+ }
}
use std::iter;
use crate::transform::{
- add_call_guards, add_moves_for_packed_drops, no_landing_pads, remove_noop_landing_pads,
+ abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, remove_noop_landing_pads,
run_passes, simplify,
};
use crate::util::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle};
MirPhase::Const,
&[&[
&add_moves_for_packed_drops::AddMovesForPackedDrops,
- &no_landing_pads::NoLandingPads,
&remove_noop_landing_pads::RemoveNoopLandingPads,
&simplify::SimplifyCfg::new("make_shim"),
&add_call_guards::CriticalCallEdges,
+ &abort_unwinding_calls::AbortUnwindingCalls,
]],
);
--- /dev/null
+use crate::transform::MirPass;
+use rustc_hir::def::DefKind;
+use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
+use rustc_middle::mir::*;
+use rustc_middle::ty::layout;
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_target::spec::abi::Abi;
+
+/// A pass that runs which is targeted at ensuring that codegen guarantees about
+/// unwinding are upheld for compilations of panic=abort programs.
+///
+/// When compiling with panic=abort codegen backends generally want to assume
+/// that all Rust-defined functions do not unwind, and it's UB if they actually
+/// do unwind. Foreign functions, however, can be declared as "may unwind" via
+/// their ABI (e.g. `extern "C-unwind"`). To uphold the guarantees that
+/// Rust-defined functions never unwind a well-behaved Rust program needs to
+/// catch unwinding from foreign functions and force them to abort.
+///
+/// This pass walks over all functions calls which may possibly unwind,
+/// and if any are found sets their cleanup to a block that aborts the process.
+/// This forces all unwinds, in panic=abort mode happening in foreign code, to
+/// trigger a process abort.
+#[derive(PartialEq)]
+pub struct AbortUnwindingCalls;
+
+impl<'tcx> MirPass<'tcx> for AbortUnwindingCalls {
+ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+ let def_id = body.source.def_id();
+ let kind = tcx.def_kind(def_id);
+
+ // We don't simplify the MIR of constants at this time because that
+ // namely results in a cyclic query when we call `tcx.type_of` below.
+ let is_function = match kind {
+ DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true,
+ _ => tcx.is_closure(def_id),
+ };
+ if !is_function {
+ return;
+ }
+
+ // This pass only runs on functions which themselves cannot unwind,
+ // forcibly changing the body of the function to structurally provide
+ // this guarantee by aborting on an unwind. If this function can unwind,
+ // then there's nothing to do because it already should work correctly.
+ //
+ // Here we test for this function itself whether its ABI allows
+ // unwinding or not.
+ let body_flags = tcx.codegen_fn_attrs(def_id).flags;
+ let body_ty = tcx.type_of(def_id);
+ let body_abi = match body_ty.kind() {
+ ty::FnDef(..) => body_ty.fn_sig(tcx).abi(),
+ ty::Closure(..) => Abi::RustCall,
+ ty::Generator(..) => Abi::Rust,
+ _ => span_bug!(body.span, "unexpected body ty: {:?}", body_ty),
+ };
+ let body_can_unwind = layout::fn_can_unwind(tcx, body_flags, body_abi);
+
+ // Look in this function body for any basic blocks which are terminated
+ // with a function call, and whose function we're calling may unwind.
+ // This will filter to functions with `extern "C-unwind"` ABIs, for
+ // example.
+ let mut calls_to_terminate = Vec::new();
+ let mut cleanups_to_remove = Vec::new();
+ for (id, block) in body.basic_blocks().iter_enumerated() {
+ if block.is_cleanup {
+ continue;
+ }
+ let terminator = match &block.terminator {
+ Some(terminator) => terminator,
+ None => continue,
+ };
+ let span = terminator.source_info.span;
+
+ let call_can_unwind = match &terminator.kind {
+ TerminatorKind::Call { func, .. } => {
+ let ty = func.ty(body, tcx);
+ let sig = ty.fn_sig(tcx);
+ let flags = match ty.kind() {
+ ty::FnPtr(_) => CodegenFnAttrFlags::empty(),
+ ty::FnDef(def_id, _) => tcx.codegen_fn_attrs(*def_id).flags,
+ _ => span_bug!(span, "invalid callee of type {:?}", ty),
+ };
+ layout::fn_can_unwind(tcx, flags, sig.abi())
+ }
+ TerminatorKind::Drop { .. }
+ | TerminatorKind::DropAndReplace { .. }
+ | TerminatorKind::Assert { .. }
+ | TerminatorKind::FalseUnwind { .. } => {
+ layout::fn_can_unwind(tcx, CodegenFnAttrFlags::empty(), Abi::Rust)
+ }
+ _ => continue,
+ };
+
+ // If this function call can't unwind, then there's no need for it
+ // to have a landing pad. This means that we can remove any cleanup
+ // registered for it.
+ if !call_can_unwind {
+ cleanups_to_remove.push(id);
+ continue;
+ }
+
+ // Otherwise if this function can unwind, then if the outer function
+ // can also unwind there's nothing to do. If the outer function
+ // can't unwind, however, we need to change the landing pad for this
+ // function call to one that aborts.
+ if !body_can_unwind {
+ calls_to_terminate.push(id);
+ }
+ }
+
+ // For call instructions which need to be terminated, we insert a
+ // singular basic block which simply terminates, and then configure the
+ // `cleanup` attribute for all calls we found to this basic block we
+ // insert which means that any unwinding that happens in the functions
+ // will force an abort of the process.
+ if !calls_to_terminate.is_empty() {
+ let bb = BasicBlockData {
+ statements: Vec::new(),
+ is_cleanup: true,
+ terminator: Some(Terminator {
+ source_info: SourceInfo::outermost(body.span),
+ kind: TerminatorKind::Abort,
+ }),
+ };
+ let abort_bb = body.basic_blocks_mut().push(bb);
+
+ for bb in calls_to_terminate {
+ let cleanup = body.basic_blocks_mut()[bb].terminator_mut().unwind_mut().unwrap();
+ *cleanup = Some(abort_bb);
+ }
+ }
+
+ for id in cleanups_to_remove {
+ let cleanup = body.basic_blocks_mut()[id].terminator_mut().unwind_mut().unwrap();
+ *cleanup = None;
+ }
+
+ // We may have invalidated some `cleanup` blocks so clean those up now.
+ super::simplify::remove_dead_blocks(tcx, body);
+ }
+}
MaybeBorrowedLocals, MaybeLiveLocals, MaybeRequiresStorage, MaybeStorageLive,
};
use crate::dataflow::{self, Analysis};
-use crate::transform::no_landing_pads::no_landing_pads;
use crate::transform::simplify;
use crate::transform::MirPass;
use crate::util::dump_mir;
)
}
- no_landing_pads(tcx, &mut body);
-
// Make sure we remove dead blocks to remove
// unrelated code from the resume part of the function
simplify::remove_dead_blocks(tcx, &mut body);
make_generator_state_argument_indirect(tcx, body);
make_generator_state_argument_pinned(tcx, body);
- no_landing_pads(tcx, body);
-
// Make sure we remove dead blocks to remove
// unrelated code from the drop part of the function
simplify::remove_dead_blocks(tcx, body);
use rustc_span::{Span, Symbol};
use std::borrow::Cow;
+pub mod abort_unwinding_calls;
pub mod add_call_guards;
pub mod add_moves_for_packed_drops;
pub mod add_retag;
pub mod lower_slice_len;
pub mod match_branches;
pub mod multiple_return_terminators;
-pub mod no_landing_pads;
pub mod nrvo;
pub mod promote_consts;
pub mod remove_noop_landing_pads;
let post_borrowck_cleanup: &[&dyn MirPass<'tcx>] = &[
// Remove all things only needed by analysis
- &no_landing_pads::NoLandingPads,
&simplify_branches::SimplifyBranches::new("initial"),
&remove_noop_landing_pads::RemoveNoopLandingPads,
&cleanup_post_borrowck::CleanupNonCodegenStatements,
// These next passes must be executed together
&add_call_guards::CriticalCallEdges,
&elaborate_drops::ElaborateDrops,
- &no_landing_pads::NoLandingPads,
+ // This will remove extraneous landing pads which are no longer
+ // necessary as well as well as forcing any call in a non-unwinding
+ // function calling a possibly-unwinding function to abort the process.
+ &abort_unwinding_calls::AbortUnwindingCalls,
// AddMovesForPackedDrops needs to run after drop
// elaboration.
&add_moves_for_packed_drops::AddMovesForPackedDrops,
+++ /dev/null
-//! This pass removes the unwind branch of all the terminators when the no-landing-pads option is
-//! specified.
-
-use crate::transform::MirPass;
-use rustc_middle::mir::*;
-use rustc_middle::ty::TyCtxt;
-use rustc_target::spec::PanicStrategy;
-
-pub struct NoLandingPads;
-
-impl<'tcx> MirPass<'tcx> for NoLandingPads {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
- no_landing_pads(tcx, body)
- }
-}
-
-pub fn no_landing_pads<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
- if tcx.sess.panic_strategy() != PanicStrategy::Abort {
- return;
- }
-
- for block in body.basic_blocks_mut() {
- let terminator = block.terminator_mut();
- if let Some(unwind) = terminator.kind.unwind_mut() {
- unwind.take();
- }
- }
-}
return None;
};
let branch_value_scalar = branch_value.literal.try_to_scalar()?;
- Some((branch_value_scalar.into(), branch_value_ty, *to_switch_on))
+ Some((branch_value_scalar, branch_value_ty, *to_switch_on))
}
_ => None,
}
use crate::build::expr::as_place::PlaceBuilder;
use crate::build::scope::DropKind;
use crate::thir::pattern::pat_from_hir;
-use rustc_attr::{self as attr, UnwindAttr};
use rustc_errors::ErrorReported;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_span::symbol::{kw, sym};
use rustc_span::Span;
use rustc_target::spec::abi::Abi;
-use rustc_target::spec::PanicStrategy;
use super::lints;
}};
}
-fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, abi: Abi) -> bool {
- // Validate `#[unwind]` syntax regardless of platform-specific panic strategy.
- let attrs = &tcx.get_attrs(fn_def_id.to_def_id());
- let unwind_attr = attr::find_unwind_attr(&tcx.sess, attrs);
-
- // We never unwind, so it's not relevant to stop an unwind.
- if tcx.sess.panic_strategy() != PanicStrategy::Unwind {
- return false;
- }
-
- match unwind_attr {
- // If an `#[unwind]` attribute was found, we should adhere to it.
- Some(UnwindAttr::Allowed) => false,
- Some(UnwindAttr::Aborts) => true,
- // If no attribute was found and the panic strategy is `unwind`, then we should examine
- // the function's ABI string to determine whether it should abort upon panic.
- None if tcx.features().c_unwind => {
- use Abi::*;
- match abi {
- // In the case of ABI's that have an `-unwind` equivalent, check whether the ABI
- // permits unwinding. If so, we should not abort. Otherwise, we should.
- C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => {
- !unwind
- }
- // Rust and `rust-call` functions are allowed to unwind, and should not abort.
- Rust | RustCall => false,
- // Other ABI's should abort.
- Cdecl
- | Fastcall
- | Vectorcall
- | Aapcs
- | Win64
- | SysV64
- | PtxKernel
- | Msp430Interrupt
- | X86Interrupt
- | AmdGpuKernel
- | EfiApi
- | AvrInterrupt
- | AvrNonBlockingInterrupt
- | CCmseNonSecureCall
- | Wasm
- | RustIntrinsic
- | PlatformIntrinsic
- | Unadjusted => true,
- }
- }
- // If the `c_unwind` feature gate is not active, follow the behavior that was in place
- // prior to #76570. This is a special case: some functions have a C ABI but are meant to
- // unwind anyway. Don't stop them.
- None => false, // FIXME(#58794); should be `!(abi == Abi::Rust || abi == Abi::RustCall)`
- }
-}
-
///////////////////////////////////////////////////////////////////////////
/// the main entry point for building MIR for a function
}));
let source_info = builder.source_info(fn_end);
builder.cfg.terminate(return_block, source_info, TerminatorKind::Return);
- let should_abort = should_abort_on_panic(tcx, fn_def.did, abi);
- builder.build_drop_trees(should_abort);
+ builder.build_drop_trees();
return_block.unit()
}));
let source_info = builder.source_info(span);
builder.cfg.terminate(block, source_info, TerminatorKind::Return);
- builder.build_drop_trees(false);
+ builder.build_drop_trees();
builder.finish()
}
}
/// Build the unwind and generator drop trees.
- crate fn build_drop_trees(&mut self, should_abort: bool) {
+ crate fn build_drop_trees(&mut self) {
if self.generator_kind.is_some() {
- self.build_generator_drop_trees(should_abort);
+ self.build_generator_drop_trees();
} else {
Self::build_unwind_tree(
&mut self.cfg,
&mut self.scopes.unwind_drops,
self.fn_span,
- should_abort,
&mut None,
);
}
}
- fn build_generator_drop_trees(&mut self, should_abort: bool) {
+ fn build_generator_drop_trees(&mut self) {
// Build the drop tree for dropping the generator while it's suspended.
let drops = &mut self.scopes.generator_drops;
let cfg = &mut self.cfg;
// Build the drop tree for unwinding in the normal control flow paths.
let resume_block = &mut None;
let unwind_drops = &mut self.scopes.unwind_drops;
- Self::build_unwind_tree(cfg, unwind_drops, fn_span, should_abort, resume_block);
+ Self::build_unwind_tree(cfg, unwind_drops, fn_span, resume_block);
// Build the drop tree for unwinding when dropping a suspended
// generator.
drops.entry_points.push((drop_data.1, blocks[drop_idx].unwrap()));
}
}
- Self::build_unwind_tree(cfg, drops, fn_span, should_abort, resume_block);
+ Self::build_unwind_tree(cfg, drops, fn_span, resume_block);
}
fn build_unwind_tree(
cfg: &mut CFG<'tcx>,
drops: &mut DropTree,
fn_span: Span,
- should_abort: bool,
resume_block: &mut Option<BasicBlock>,
) {
let mut blocks = IndexVec::from_elem(None, &drops.drops);
blocks[ROOT_NODE] = *resume_block;
drops.build_mir::<Unwind>(cfg, &mut blocks);
if let (None, Some(resume)) = (*resume_block, blocks[ROOT_NODE]) {
- // `TerminatorKind::Abort` is used for `#[unwind(aborts)]`
- // functions.
- let terminator =
- if should_abort { TerminatorKind::Abort } else { TerminatorKind::Resume };
-
- cfg.terminate(resume, SourceInfo::outermost(fn_span), terminator);
+ cfg.terminate(resume, SourceInfo::outermost(fn_span), TerminatorKind::Resume);
*resume_block = blocks[ROOT_NODE];
}
return; // we have already visited everything by now
}
}
- ExprKind::Borrow { borrow_kind, arg } => match borrow_kind {
- BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => {
- if !self.thir[arg]
- .ty
- .is_freeze(self.tcx.at(self.thir[arg].span), self.param_env)
- {
- let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
- visit::walk_expr(&mut visitor, expr);
- if visitor.found {
- self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField);
+ ExprKind::Borrow { borrow_kind, arg } => {
+ let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
+ visit::walk_expr(&mut visitor, expr);
+ if visitor.found {
+ match borrow_kind {
+ BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique
+ if !self.thir[arg]
+ .ty
+ .is_freeze(self.tcx.at(self.thir[arg].span), self.param_env) =>
+ {
+ self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField)
}
+ BorrowKind::Mut { .. } => {
+ self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField)
+ }
+ BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => {}
}
}
- BorrowKind::Mut { .. } => {
- let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
- visit::walk_expr(&mut visitor, expr);
- if visitor.found {
- self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField);
- }
- }
- },
+ }
_ => {}
}
visit::walk_expr(self, expr);
// Closures are handled by their owner, if it has a body
if tcx.is_closure(def.did.to_def_id()) {
- let owner = tcx.hir().local_def_id_to_hir_id(def.did).owner;
- let owner_hir_id = tcx.hir().local_def_id_to_hir_id(owner);
-
- if tcx.hir().maybe_body_owned_by(owner_hir_id).is_some() {
- tcx.ensure().thir_check_unsafety(owner);
- return;
- }
+ let hir = tcx.hir();
+ let owner = hir.enclosing_body_owner(hir.local_def_id_to_hir_id(def.did));
+ tcx.ensure().thir_check_unsafety(hir.local_def_id(owner));
+ return;
}
let (thir, expr) = tcx.thir_body(def);
EscapeError::NonAsciiCharInByte => {
assert!(mode.is_bytes());
let (c, span) = last_char();
- handler
- .struct_span_err(span, "non-ASCII character in byte constant")
- .span_label(span, "byte constant must be ASCII")
- .span_suggestion(
+ let mut err = handler.struct_span_err(span, "non-ASCII character in byte constant");
+ err.span_label(span, "byte constant must be ASCII");
+ if (c as u32) <= 0xFF {
+ err.span_suggestion(
span,
- "use a \\xHH escape for a non-ASCII byte",
+ &format!(
+ "if you meant to use the unicode code point for '{}', use a \\xHH escape",
+ c
+ ),
format!("\\x{:X}", c as u32),
- Applicability::MachineApplicable,
- )
- .emit();
+ Applicability::MaybeIncorrect,
+ );
+ } else if matches!(mode, Mode::Byte) {
+ err.span_label(span, "this multibyte character does not fit into a single byte");
+ } else if matches!(mode, Mode::ByteStr) {
+ let mut utf8 = String::new();
+ utf8.push(c);
+ err.span_suggestion(
+ span,
+ &format!(
+ "if you meant to use the UTF-8 encoding of '{}', use \\xHH escapes",
+ c
+ ),
+ utf8.as_bytes()
+ .iter()
+ .map(|b: &u8| format!("\\x{:X}", *b))
+ .fold("".to_string(), |a, c| a + &c),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ err.emit();
}
EscapeError::NonAsciiCharInByteString => {
assert!(mode.is_bytes());
use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
use rustc_ast_pretty::pprust;
use rustc_errors::{Applicability, DiagnosticBuilder, PResult};
+use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
+use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_span::edition::LATEST_STABLE_EDITION;
use rustc_span::source_map::{self, Span, Spanned};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
self.maybe_recover_from_bad_qpath(expr, true)
}
- /// Parse `"('label ":")? break expr?`.
+ /// Parse `"break" (('label (:? expr)?) | expr?)` with `"break"` token already eaten.
+ /// If the label is followed immediately by a `:` token, the label and `:` are
+ /// parsed as part of the expression (i.e. a labeled loop). The language team has
+ /// decided in #87026 to require parentheses as a visual aid to avoid confusion if
+ /// the break expression of an unlabeled break is a labeled loop (as in
+ /// `break 'lbl: loop {}`); a labeled break with an unlabeled loop as its value
+ /// expression only gets a warning for compatibility reasons; and a labeled break
+ /// with a labeled loop does not even get a warning because there is no ambiguity.
fn parse_break_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
let lo = self.prev_token.span;
- let label = self.eat_label();
- let kind = if self.token != token::OpenDelim(token::Brace)
+ let mut label = self.eat_label();
+ let kind = if label.is_some() && self.token == token::Colon {
+ // The value expression can be a labeled loop, see issue #86948, e.g.:
+ // `loop { break 'label: loop { break 'label 42; }; }`
+ let lexpr = self.parse_labeled_expr(label.take().unwrap(), AttrVec::new(), true)?;
+ self.struct_span_err(
+ lexpr.span,
+ "parentheses are required around this expression to avoid confusion with a labeled break expression",
+ )
+ .multipart_suggestion(
+ "wrap the expression in parentheses",
+ vec![
+ (lexpr.span.shrink_to_lo(), "(".to_string()),
+ (lexpr.span.shrink_to_hi(), ")".to_string()),
+ ],
+ Applicability::MachineApplicable,
+ )
+ .emit();
+ Some(lexpr)
+ } else if self.token != token::OpenDelim(token::Brace)
|| !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
{
- self.parse_expr_opt()?
+ let expr = self.parse_expr_opt()?;
+ if let Some(ref expr) = expr {
+ if label.is_some()
+ && matches!(
+ expr.kind,
+ ExprKind::While(_, _, None)
+ | ExprKind::ForLoop(_, _, _, None)
+ | ExprKind::Loop(_, None)
+ | ExprKind::Block(_, None)
+ )
+ {
+ self.sess.buffer_lint_with_diagnostic(
+ BREAK_WITH_LABEL_AND_LOOP,
+ lo.to(expr.span),
+ ast::CRATE_NODE_ID,
+ "this labeled break expression is easy to confuse with an unlabeled break with a labeled value expression",
+ BuiltinLintDiagnostics::BreakWithLabelAndLoop(expr.span),
+ );
+ }
+ }
+ expr
} else {
None
};
// the AST for typechecking.
err.span_label(ident.span, "while parsing this `fn`");
err.emit();
- (Vec::new(), None)
} else {
return Err(err);
}
- } else {
- unreachable!()
}
+ (Vec::new(), None)
};
attrs.extend(inner_attrs);
Ok(body)
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res};
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::{Node, PatKind, TyKind};
use rustc_middle::middle::privacy;
use rustc_middle::ty::{self, DefIdTree, TyCtxt};
use rustc_session::lint;
-
use rustc_span::symbol::{sym, Symbol};
// Any local node that may call something in its body block should be
// explored. For example, if it's a live Node::Item that is a
// function, then we should explore its block to check for codes that
// may need to be marked as live.
-fn should_explore(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
+fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
matches!(
- tcx.hir().find(hir_id),
+ tcx.hir().find(tcx.hir().local_def_id_to_hir_id(def_id)),
Some(
Node::Item(..)
| Node::ImplItem(..)
| Node::TraitItem(..)
| Node::Variant(..)
| Node::AnonConst(..)
- | Node::Pat(..),
)
)
}
struct MarkSymbolVisitor<'tcx> {
- worklist: Vec<hir::HirId>,
+ worklist: Vec<LocalDefId>,
tcx: TyCtxt<'tcx>,
maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>,
- live_symbols: FxHashSet<hir::HirId>,
+ live_symbols: FxHashSet<LocalDefId>,
repr_has_repr_c: bool,
in_pat: bool,
inherited_pub_visibility: bool,
pub_visibility: bool,
ignore_variant_stack: Vec<DefId>,
// maps from tuple struct constructors to tuple struct items
- struct_constructors: FxHashMap<hir::HirId, hir::HirId>,
+ struct_constructors: FxHashMap<LocalDefId, LocalDefId>,
}
impl<'tcx> MarkSymbolVisitor<'tcx> {
fn check_def_id(&mut self, def_id: DefId) {
if let Some(def_id) = def_id.as_local() {
- let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
- if should_explore(self.tcx, hir_id) || self.struct_constructors.contains_key(&hir_id) {
- self.worklist.push(hir_id);
+ if should_explore(self.tcx, def_id) || self.struct_constructors.contains_key(&def_id) {
+ self.worklist.push(def_id);
}
- self.live_symbols.insert(hir_id);
+ self.live_symbols.insert(def_id);
}
}
fn insert_def_id(&mut self, def_id: DefId) {
if let Some(def_id) = def_id.as_local() {
- let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
- debug_assert!(!should_explore(self.tcx, hir_id));
- self.live_symbols.insert(hir_id);
+ debug_assert!(!should_explore(self.tcx, def_id));
+ self.live_symbols.insert(def_id);
}
}
// in the case of tuple struct constructors we want to check the item, not the generated
// tuple struct constructor function
- let id = self.struct_constructors.get(&id).cloned().unwrap_or(id);
+ let id = self.struct_constructors.get(&id).copied().unwrap_or(id);
- if let Some(node) = self.tcx.hir().find(id) {
+ if let Some(node) = self.tcx.hir().find(self.tcx.hir().local_def_id_to_hir_id(id)) {
self.live_symbols.insert(id);
self.visit_node(node);
}
let live_fields = def.fields().iter().filter(|f| {
has_repr_c || (pub_visibility && (inherited_pub_visibility || f.vis.node.is_pub()))
});
- self.live_symbols.extend(live_fields.map(|f| f.hir_id));
+ let hir = self.tcx.hir();
+ self.live_symbols.extend(live_fields.map(|f| hir.local_def_id(f.hir_id)));
intravisit::walk_struct_def(self, def);
}
}
fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) {
- self.live_symbols.insert(c.hir_id);
+ self.live_symbols.insert(self.tcx.hir().local_def_id(c.hir_id));
intravisit::walk_anon_const(self, c);
}
}
// 2) We are not sure to be live or not
// * Implementations of traits and trait methods
struct LifeSeeder<'k, 'tcx> {
- worklist: Vec<hir::HirId>,
+ worklist: Vec<LocalDefId>,
krate: &'k hir::Crate<'k>,
tcx: TyCtxt<'tcx>,
// see `MarkSymbolVisitor::struct_constructors`
- struct_constructors: FxHashMap<hir::HirId, hir::HirId>,
+ struct_constructors: FxHashMap<LocalDefId, LocalDefId>,
}
impl<'v, 'k, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'k, 'tcx> {
fn visit_item(&mut self, item: &hir::Item<'_>) {
let allow_dead_code = has_allow_dead_code_or_lang_attr(self.tcx, item.hir_id());
if allow_dead_code {
- self.worklist.push(item.hir_id());
+ self.worklist.push(item.def_id);
}
match item.kind {
hir::ItemKind::Enum(ref enum_def, _) => {
+ let hir = self.tcx.hir();
if allow_dead_code {
- self.worklist.extend(enum_def.variants.iter().map(|variant| variant.id));
+ self.worklist.extend(
+ enum_def.variants.iter().map(|variant| hir.local_def_id(variant.id)),
+ );
}
for variant in enum_def.variants {
if let Some(ctor_hir_id) = variant.data.ctor_hir_id() {
- self.struct_constructors.insert(ctor_hir_id, variant.id);
+ self.struct_constructors
+ .insert(hir.local_def_id(ctor_hir_id), hir.local_def_id(variant.id));
}
}
}
hir::ItemKind::Impl(hir::Impl { ref of_trait, items, .. }) => {
if of_trait.is_some() {
- self.worklist.push(item.hir_id());
+ self.worklist.push(item.def_id);
}
for impl_item_ref in items {
let impl_item = self.krate.impl_item(impl_item_ref.id);
if of_trait.is_some()
|| has_allow_dead_code_or_lang_attr(self.tcx, impl_item.hir_id())
{
- self.worklist.push(impl_item_ref.id.hir_id());
+ self.worklist.push(impl_item_ref.id.def_id);
}
}
}
hir::ItemKind::Struct(ref variant_data, _) => {
if let Some(ctor_hir_id) = variant_data.ctor_hir_id() {
- self.struct_constructors.insert(ctor_hir_id, item.hir_id());
+ self.struct_constructors
+ .insert(self.tcx.hir().local_def_id(ctor_hir_id), item.def_id);
}
}
_ => (),
if matches!(trait_item.kind, Const(_, Some(_)) | Fn(_, hir::TraitFn::Provided(_)))
&& has_allow_dead_code_or_lang_attr(self.tcx, trait_item.hir_id())
{
- self.worklist.push(trait_item.hir_id());
+ self.worklist.push(trait_item.def_id);
}
}
if matches!(foreign_item.kind, Static(..) | Fn(..))
&& has_allow_dead_code_or_lang_attr(self.tcx, foreign_item.hir_id())
{
- self.worklist.push(foreign_item.hir_id());
+ self.worklist.push(foreign_item.def_id);
}
}
}
tcx: TyCtxt<'tcx>,
access_levels: &privacy::AccessLevels,
krate: &hir::Crate<'_>,
-) -> (Vec<hir::HirId>, FxHashMap<hir::HirId, hir::HirId>) {
+) -> (Vec<LocalDefId>, FxHashMap<LocalDefId, LocalDefId>) {
let worklist = access_levels
.map
.iter()
if level >= privacy::AccessLevel::Reachable { Some(id) } else { None }
},
)
- .chain(
- // Seed entry point
- tcx.entry_fn(()).and_then(|(def_id, _)| {
- def_id.as_local().map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id))
- }),
- )
+ // Seed entry point
+ .chain(tcx.entry_fn(()).and_then(|(def_id, _)| def_id.as_local()))
.collect::<Vec<_>>();
// Seed implemented trait items
tcx: TyCtxt<'tcx>,
access_levels: &privacy::AccessLevels,
krate: &hir::Crate<'_>,
-) -> FxHashSet<hir::HirId> {
+) -> FxHashSet<LocalDefId> {
let (worklist, struct_constructors) = create_and_seed_worklist(tcx, access_levels, krate);
let mut symbol_visitor = MarkSymbolVisitor {
worklist,
struct DeadVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
- live_symbols: FxHashSet<hir::HirId>,
+ live_symbols: FxHashSet<LocalDefId>,
}
impl DeadVisitor<'tcx> {
| hir::ItemKind::Struct(..)
| hir::ItemKind::Union(..)
);
- should_warn && !self.symbol_is_live(item.hir_id())
+ should_warn && !self.symbol_is_live(item.def_id)
}
fn should_warn_about_field(&mut self, field: &hir::FieldDef<'_>) -> bool {
- let field_type = self.tcx.type_of(self.tcx.hir().local_def_id(field.hir_id));
+ let def_id = self.tcx.hir().local_def_id(field.hir_id);
+ let field_type = self.tcx.type_of(def_id);
!field.is_positional()
- && !self.symbol_is_live(field.hir_id)
+ && !self.symbol_is_live(def_id)
&& !field_type.is_phantom_data()
&& !has_allow_dead_code_or_lang_attr(self.tcx, field.hir_id)
}
fn should_warn_about_variant(&mut self, variant: &hir::Variant<'_>) -> bool {
- !self.symbol_is_live(variant.id) && !has_allow_dead_code_or_lang_attr(self.tcx, variant.id)
+ let def_id = self.tcx.hir().local_def_id(variant.id);
+ !self.symbol_is_live(def_id) && !has_allow_dead_code_or_lang_attr(self.tcx, variant.id)
}
fn should_warn_about_foreign_item(&mut self, fi: &hir::ForeignItem<'_>) -> bool {
- !self.symbol_is_live(fi.hir_id())
- && !has_allow_dead_code_or_lang_attr(self.tcx, fi.hir_id())
+ !self.symbol_is_live(fi.def_id) && !has_allow_dead_code_or_lang_attr(self.tcx, fi.hir_id())
}
// id := HIR id of an item's definition.
- fn symbol_is_live(&mut self, id: hir::HirId) -> bool {
- if self.live_symbols.contains(&id) {
+ fn symbol_is_live(&mut self, def_id: LocalDefId) -> bool {
+ if self.live_symbols.contains(&def_id) {
return true;
}
// If it's a type whose items are live, then it's live, too.
// This is done to handle the case where, for example, the static
// method of a private type is used, but the type itself is never
// called directly.
- let def_id = self.tcx.hir().local_def_id(id);
let inherent_impls = self.tcx.inherent_impls(def_id);
for &impl_did in inherent_impls.iter() {
for item_did in self.tcx.associated_item_def_ids(impl_did) {
- if let Some(did) = item_did.as_local() {
- let item_hir_id = self.tcx.hir().local_def_id_to_hir_id(did);
- if self.live_symbols.contains(&item_hir_id) {
+ if let Some(def_id) = item_did.as_local() {
+ if self.live_symbols.contains(&def_id) {
return true;
}
}
fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
match impl_item.kind {
hir::ImplItemKind::Const(_, body_id) => {
- if !self.symbol_is_live(impl_item.hir_id()) {
+ if !self.symbol_is_live(impl_item.def_id) {
self.warn_dead_code(
impl_item.hir_id(),
impl_item.span,
self.visit_nested_body(body_id)
}
hir::ImplItemKind::Fn(_, body_id) => {
- if !self.symbol_is_live(impl_item.hir_id()) {
+ if !self.symbol_is_live(impl_item.def_id) {
// FIXME(66095): Because impl_item.span is annotated with things
// like expansion data, and ident.span isn't, we use the
// def_span method if it's part of a macro invocation
}
}
+ // Don't run unused pass for #[naked]
+ if self.tcx.has_attr(def_id, sym::naked) {
+ return;
+ }
+
if let Some(captures) = maps.tcx.typeck(local_def_id).closure_min_captures.get(&def_id) {
for &var_hir_id in captures.keys() {
let var_name = maps.tcx.hir().name(var_hir_id);
//! Checks validity of naked functions.
-use rustc_ast::InlineAsmOptions;
+use rustc_ast::{Attribute, InlineAsmOptions};
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{ErasedMap, FnKind, NestedVisitorMap, Visitor};
check_no_patterns(self.tcx, body.params);
check_no_parameters_use(self.tcx, body);
check_asm(self.tcx, hir_id, body, span);
+ check_inline(self.tcx, hir_id, attrs);
}
}
}
+/// Check that the function isn't inlined.
+fn check_inline(tcx: TyCtxt<'_>, hir_id: HirId, attrs: &[Attribute]) {
+ for attr in attrs.iter().filter(|attr| attr.has_name(sym::inline)) {
+ tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, attr.span, |lint| {
+ lint.build("naked functions cannot be inlined").emit();
+ });
+ }
+}
+
/// Checks that function uses non-Rust ABI.
fn check_abi(tcx: TyCtxt<'_>, hir_id: HirId, abi: Abi, fn_ident_span: Span) {
if abi == Abi::Rust {
if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref trait_ref), ref items, .. }) =
item.kind
{
- if !self.access_levels.is_reachable(item.hir_id()) {
+ if !self.access_levels.is_reachable(item.def_id) {
// FIXME(#53488) remove `let`
let tcx = self.tcx;
self.worklist.extend(items.iter().map(|ii_ref| ii_ref.id.def_id));
// If other crates link to us, they're going to expect to be able to
// use the lang items, so we need to be sure to mark them as
// exported.
- reachable_context
- .worklist
- .extend(access_levels.map.iter().map(|(id, _)| tcx.hir().local_def_id(*id)));
+ reachable_context.worklist.extend(access_levels.map.keys());
for item in tcx.lang_items().items().iter() {
if let Some(def_id) = *item {
if let Some(def_id) = def_id.as_local() {
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
+use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE};
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{FieldDef, Generics, HirId, Item, TraitRef, Ty, TyKind, Variant};
use rustc_middle::hir::map::Map;
// If the node is a function, `fn_sig` is its signature
fn annotate<F>(
&mut self,
- hir_id: HirId,
+ def_id: LocalDefId,
item_sp: Span,
fn_sig: Option<&'tcx hir::FnSig<'tcx>>,
kind: AnnotationKind,
) where
F: FnOnce(&mut Self),
{
- let attrs = self.tcx.hir().attrs(hir_id);
- debug!("annotate(id = {:?}, attrs = {:?})", hir_id, attrs);
+ let attrs = self.tcx.get_attrs(def_id.to_def_id());
+ debug!("annotate(id = {:?}, attrs = {:?})", def_id, attrs);
let mut did_error = false;
if !self.tcx.features().staged_api {
- did_error = self.forbid_staged_api_attrs(hir_id, attrs, inherit_deprecation.clone());
+ did_error = self.forbid_staged_api_attrs(def_id, attrs, inherit_deprecation.clone());
}
let depr = if did_error { None } else { attr::find_deprecation(&self.tcx.sess, attrs) };
is_deprecated = true;
if kind == AnnotationKind::Prohibited || kind == AnnotationKind::DeprecationProhibited {
+ let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
self.tcx.struct_span_lint_hir(USELESS_DEPRECATED, hir_id, *span, |lint| {
lint.build("this `#[deprecated]` annotation has no effect")
.span_suggestion_short(
}
// `Deprecation` is just two pointers, no need to intern it
- let depr_entry = DeprecationEntry::local(depr.clone(), hir_id);
- self.index.depr_map.insert(hir_id, depr_entry);
+ let depr_entry = DeprecationEntry::local(depr.clone(), def_id);
+ self.index.depr_map.insert(def_id, depr_entry);
} else if let Some(parent_depr) = self.parent_depr.clone() {
if inherit_deprecation.yes() {
is_deprecated = true;
- info!("tagging child {:?} as deprecated from parent", hir_id);
- self.index.depr_map.insert(hir_id, parent_depr);
+ info!("tagging child {:?} as deprecated from parent", def_id);
+ self.index.depr_map.insert(def_id, parent_depr);
}
}
}
} else {
self.recurse_with_stability_attrs(
- depr.map(|(d, _)| DeprecationEntry::local(d, hir_id)),
+ depr.map(|(d, _)| DeprecationEntry::local(d, def_id)),
None,
None,
visit_children,
let const_stab = const_stab.map(|(const_stab, const_span_node)| {
let const_stab = self.tcx.intern_const_stability(const_stab);
- self.index.const_stab_map.insert(hir_id, const_stab);
+ self.index.const_stab_map.insert(def_id, const_stab);
const_span = Some(const_span_node);
const_stab
});
&& !fn_sig.header.is_const()
{
if !self.in_trait_impl
- || (self.in_trait_impl && !self.tcx.is_const_fn_raw(hir_id.owner.to_def_id()))
+ || (self.in_trait_impl && !self.tcx.is_const_fn_raw(def_id.to_def_id()))
{
missing_const_err(&self.tcx.sess, fn_sig.span, const_span);
}
debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab);
if let Some(parent) = self.parent_const_stab {
if parent.level.is_unstable() {
- self.index.const_stab_map.insert(hir_id, parent);
+ self.index.const_stab_map.insert(def_id, parent);
}
}
}
}
}
- self.index.stab_map.insert(hir_id, stab);
+ self.index.stab_map.insert(def_id, stab);
stab
});
if inherit_deprecation.yes() && stab.level.is_unstable()
|| inherit_from_parent.yes()
{
- self.index.stab_map.insert(hir_id, stab);
+ self.index.stab_map.insert(def_id, stab);
}
}
}
self.recurse_with_stability_attrs(
- depr.map(|(d, _)| DeprecationEntry::local(d, hir_id)),
+ depr.map(|(d, _)| DeprecationEntry::local(d, def_id)),
stab,
if inherit_const_stability.yes() { const_stab } else { None },
visit_children,
// returns true if an error occurred, used to suppress some spurious errors
fn forbid_staged_api_attrs(
&mut self,
- hir_id: HirId,
+ def_id: LocalDefId,
attrs: &[Attribute],
inherit_deprecation: InheritDeprecation,
) -> bool {
// -Zforce-unstable-if-unmarked is set.
if let Some(stab) = self.parent_stab {
if inherit_deprecation.yes() && stab.level.is_unstable() {
- self.index.stab_map.insert(hir_id, stab);
+ self.index.stab_map.insert(def_id, stab);
}
}
hir::ItemKind::Struct(ref sd, _) => {
if let Some(ctor_hir_id) = sd.ctor_hir_id() {
self.annotate(
- ctor_hir_id,
+ self.tcx.hir().local_def_id(ctor_hir_id),
i.span,
None,
AnnotationKind::Required,
}
self.annotate(
- i.hir_id(),
+ i.def_id,
i.span,
fn_sig,
kind,
};
self.annotate(
- ti.hir_id(),
+ ti.def_id,
ti.span,
fn_sig,
AnnotationKind::Required,
};
self.annotate(
- ii.hir_id(),
+ ii.def_id,
ii.span,
fn_sig,
kind,
fn visit_variant(&mut self, var: &'tcx Variant<'tcx>, g: &'tcx Generics<'tcx>, item_id: HirId) {
self.annotate(
- var.id,
+ self.tcx.hir().local_def_id(var.id),
var.span,
None,
AnnotationKind::Required,
|v| {
if let Some(ctor_hir_id) = var.data.ctor_hir_id() {
v.annotate(
- ctor_hir_id,
+ v.tcx.hir().local_def_id(ctor_hir_id),
var.span,
None,
AnnotationKind::Required,
fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) {
self.annotate(
- s.hir_id,
+ self.tcx.hir().local_def_id(s.hir_id),
s.span,
None,
AnnotationKind::Required,
fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) {
self.annotate(
- i.hir_id(),
+ i.def_id,
i.span,
None,
AnnotationKind::Required,
fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) {
self.annotate(
- md.hir_id(),
+ md.def_id,
md.span,
None,
AnnotationKind::Required,
};
self.annotate(
- p.hir_id,
+ self.tcx.hir().local_def_id(p.hir_id),
p.span,
None,
kind,
}
impl<'tcx> MissingStabilityAnnotations<'tcx> {
- fn check_missing_stability(&self, hir_id: HirId, span: Span) {
- let stab = self.tcx.stability().local_stability(hir_id);
- let is_error =
- !self.tcx.sess.opts.test && stab.is_none() && self.access_levels.is_reachable(hir_id);
- if is_error {
- let def_id = self.tcx.hir().local_def_id(hir_id);
+ fn check_missing_stability(&self, def_id: LocalDefId, span: Span) {
+ let stab = self.tcx.stability().local_stability(def_id);
+ if !self.tcx.sess.opts.test && stab.is_none() && self.access_levels.is_reachable(def_id) {
let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id());
self.tcx.sess.span_err(span, &format!("{} has missing stability attribute", descr));
}
}
- fn check_missing_const_stability(&self, hir_id: HirId, span: Span) {
+ fn check_missing_const_stability(&self, def_id: LocalDefId, span: Span) {
let stab_map = self.tcx.stability();
- let stab = stab_map.local_stability(hir_id);
+ let stab = stab_map.local_stability(def_id);
if stab.map_or(false, |stab| stab.level.is_stable()) {
- let const_stab = stab_map.local_const_stability(hir_id);
+ let const_stab = stab_map.local_const_stability(def_id);
if const_stab.is_none() {
self.tcx.sess.span_err(
span,
hir::ItemKind::Impl(hir::Impl { of_trait: None, .. })
| hir::ItemKind::ForeignMod { .. }
) {
- self.check_missing_stability(i.hir_id(), i.span);
+ self.check_missing_stability(i.def_id, i.span);
}
// Ensure `const fn` that are `stable` have one of `rustc_const_unstable` or
if self.tcx.features().staged_api
&& matches!(&i.kind, hir::ItemKind::Fn(sig, ..) if sig.header.is_const())
{
- self.check_missing_const_stability(i.hir_id(), i.span);
+ self.check_missing_const_stability(i.def_id, i.span);
}
intravisit::walk_item(self, i)
}
fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
- self.check_missing_stability(ti.hir_id(), ti.span);
+ self.check_missing_stability(ti.def_id, ti.span);
intravisit::walk_trait_item(self, ti);
}
fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
let impl_def_id = self.tcx.hir().local_def_id(self.tcx.hir().get_parent_item(ii.hir_id()));
if self.tcx.impl_trait_ref(impl_def_id).is_none() {
- self.check_missing_stability(ii.hir_id(), ii.span);
+ self.check_missing_stability(ii.def_id, ii.span);
}
intravisit::walk_impl_item(self, ii);
}
fn visit_variant(&mut self, var: &'tcx Variant<'tcx>, g: &'tcx Generics<'tcx>, item_id: HirId) {
- self.check_missing_stability(var.id, var.span);
+ self.check_missing_stability(self.tcx.hir().local_def_id(var.id), var.span);
intravisit::walk_variant(self, var, g, item_id);
}
fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) {
- self.check_missing_stability(s.hir_id, s.span);
+ self.check_missing_stability(self.tcx.hir().local_def_id(s.hir_id), s.span);
intravisit::walk_field_def(self, s);
}
fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) {
- self.check_missing_stability(i.hir_id(), i.span);
+ self.check_missing_stability(i.def_id, i.span);
intravisit::walk_foreign_item(self, i);
}
fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) {
- self.check_missing_stability(md.hir_id(), md.span);
+ self.check_missing_stability(md.def_id, md.span);
}
// Note that we don't need to `check_missing_stability` for default generic parameters,
}
annotator.annotate(
- hir::CRATE_HIR_ID,
+ CRATE_DEF_ID,
krate.module().inner,
None,
AnnotationKind::Required,
if tcx.stability().staged_api[&LOCAL_CRATE] {
let krate = tcx.hir().krate();
let mut missing = MissingStabilityAnnotations { tcx, access_levels };
- missing.check_missing_stability(hir::CRATE_HIR_ID, krate.module().inner);
+ missing.check_missing_stability(CRATE_DEF_ID, krate.module().inner);
intravisit::walk_crate(&mut missing, krate);
krate.visit_all_item_likes(&mut missing.as_deep_visitor());
}
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
+use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE};
use rustc_hir::intravisit::{self, DeepVisitor, NestedVisitorMap, Visitor};
use rustc_hir::{AssocItemKind, HirIdSet, Node, PatKind};
use rustc_middle::bug;
fn new_min(find: &FindMin<'_, '_, Self>, def_id: DefId) -> Self {
cmp::min(
if let Some(def_id) = def_id.as_local() {
- let hir_id = find.tcx.hir().local_def_id_to_hir_id(def_id);
- find.access_levels.map.get(&hir_id).cloned()
+ find.access_levels.map.get(&def_id).copied()
} else {
Self::MAX
},
/// pub macro m() {
/// n::p::f()
/// }
- macro_reachable: FxHashSet<(hir::HirId, DefId)>,
+ macro_reachable: FxHashSet<(LocalDefId, LocalDefId)>,
/// Previous accessibility level; `None` means unreachable.
prev_level: Option<AccessLevel>,
/// Has something changed in the level map?
}
impl EmbargoVisitor<'tcx> {
- fn get(&self, id: hir::HirId) -> Option<AccessLevel> {
- self.access_levels.map.get(&id).cloned()
+ fn get(&self, def_id: LocalDefId) -> Option<AccessLevel> {
+ self.access_levels.map.get(&def_id).copied()
}
/// Updates node level and returns the updated level.
- fn update(&mut self, id: hir::HirId, level: Option<AccessLevel>) -> Option<AccessLevel> {
- let old_level = self.get(id);
+ fn update(&mut self, def_id: LocalDefId, level: Option<AccessLevel>) -> Option<AccessLevel> {
+ let old_level = self.get(def_id);
// Accessibility levels can only grow.
if level > old_level {
- self.access_levels.map.insert(id, level.unwrap());
+ self.access_levels.map.insert(def_id, level.unwrap());
self.changed = true;
level
} else {
/// Updates the item as being reachable through a macro defined in the given
/// module. Returns `true` if the level has changed.
- fn update_macro_reachable(&mut self, reachable_mod: hir::HirId, defining_mod: DefId) -> bool {
- if self.macro_reachable.insert((reachable_mod, defining_mod)) {
- self.update_macro_reachable_mod(reachable_mod, defining_mod);
+ fn update_macro_reachable(
+ &mut self,
+ module_def_id: LocalDefId,
+ defining_mod: LocalDefId,
+ ) -> bool {
+ if self.macro_reachable.insert((module_def_id, defining_mod)) {
+ self.update_macro_reachable_mod(module_def_id, defining_mod);
true
} else {
false
}
}
- fn update_macro_reachable_mod(&mut self, reachable_mod: hir::HirId, defining_mod: DefId) {
- let module_def_id = self.tcx.hir().local_def_id(reachable_mod);
+ fn update_macro_reachable_mod(&mut self, module_def_id: LocalDefId, defining_mod: LocalDefId) {
let module = self.tcx.hir().get_module(module_def_id).0;
for item_id in module.item_ids {
let def_kind = self.tcx.def_kind(item_id.def_id);
let vis = self.tcx.visibility(item_id.def_id);
- self.update_macro_reachable_def(item_id.hir_id(), def_kind, vis, defining_mod);
+ self.update_macro_reachable_def(item_id.def_id, def_kind, vis, defining_mod);
}
if let Some(exports) = self.tcx.module_exports(module_def_id) {
for export in exports {
- if export.vis.is_accessible_from(defining_mod, self.tcx) {
+ if export.vis.is_accessible_from(defining_mod.to_def_id(), self.tcx) {
if let Res::Def(def_kind, def_id) = export.res {
if let Some(def_id) = def_id.as_local() {
- let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
let vis = self.tcx.visibility(def_id.to_def_id());
- self.update_macro_reachable_def(hir_id, def_kind, vis, defining_mod);
+ self.update_macro_reachable_def(def_id, def_kind, vis, defining_mod);
}
}
}
fn update_macro_reachable_def(
&mut self,
- hir_id: hir::HirId,
+ def_id: LocalDefId,
def_kind: DefKind,
vis: ty::Visibility,
- module: DefId,
+ module: LocalDefId,
) {
let level = Some(AccessLevel::Reachable);
if let ty::Visibility::Public = vis {
- self.update(hir_id, level);
+ self.update(def_id, level);
}
match def_kind {
// No type privacy, so can be directly marked as reachable.
| DefKind::Static
| DefKind::TraitAlias
| DefKind::TyAlias => {
- if vis.is_accessible_from(module, self.tcx) {
- self.update(hir_id, level);
+ if vis.is_accessible_from(module.to_def_id(), self.tcx) {
+ self.update(def_id, level);
}
}
// hygiene these don't need to be marked reachable. The contents of
// the module, however may be reachable.
DefKind::Mod => {
- if vis.is_accessible_from(module, self.tcx) {
- self.update_macro_reachable(hir_id, module);
+ if vis.is_accessible_from(module.to_def_id(), self.tcx) {
+ self.update_macro_reachable(def_id, module);
}
}
DefKind::Struct | DefKind::Union => {
- // While structs and unions have type privacy, their fields do
- // not.
+ // While structs and unions have type privacy, their fields do not.
if let ty::Visibility::Public = vis {
- let item = self.tcx.hir().expect_item(hir_id);
+ let item =
+ self.tcx.hir().expect_item(self.tcx.hir().local_def_id_to_hir_id(def_id));
if let hir::ItemKind::Struct(ref struct_def, _)
| hir::ItemKind::Union(ref struct_def, _) = item.kind
{
for field in struct_def.fields() {
let field_vis =
self.tcx.visibility(self.tcx.hir().local_def_id(field.hir_id));
- if field_vis.is_accessible_from(module, self.tcx) {
+ if field_vis.is_accessible_from(module.to_def_id(), self.tcx) {
self.reach(field.hir_id, level).ty();
}
}
continue;
}
if let hir::ItemKind::Use(..) = item.kind {
- self.update(item.hir_id(), Some(AccessLevel::Exported));
+ self.update(item.def_id, Some(AccessLevel::Exported));
}
}
}
};
// Update level of the item itself.
- let item_level = self.update(item.hir_id(), inherited_item_level);
+ let item_level = self.update(item.def_id, inherited_item_level);
// Update levels of nested things.
match item.kind {
hir::ItemKind::Enum(ref def, _) => {
for variant in def.variants {
- let variant_level = self.update(variant.id, item_level);
+ let variant_level =
+ self.update(self.tcx.hir().local_def_id(variant.id), item_level);
if let Some(ctor_hir_id) = variant.data.ctor_hir_id() {
- self.update(ctor_hir_id, item_level);
+ self.update(self.tcx.hir().local_def_id(ctor_hir_id), item_level);
}
for field in variant.data.fields() {
- self.update(field.hir_id, variant_level);
+ self.update(self.tcx.hir().local_def_id(field.hir_id), variant_level);
}
}
}
hir::ItemKind::Impl(ref impl_) => {
for impl_item_ref in impl_.items {
if impl_.of_trait.is_some() || impl_item_ref.vis.node.is_pub() {
- self.update(impl_item_ref.id.hir_id(), item_level);
+ self.update(impl_item_ref.id.def_id, item_level);
}
}
}
hir::ItemKind::Trait(.., trait_item_refs) => {
for trait_item_ref in trait_item_refs {
- self.update(trait_item_ref.id.hir_id(), item_level);
+ self.update(trait_item_ref.id.def_id, item_level);
}
}
hir::ItemKind::Struct(ref def, _) | hir::ItemKind::Union(ref def, _) => {
if let Some(ctor_hir_id) = def.ctor_hir_id() {
- self.update(ctor_hir_id, item_level);
+ self.update(self.tcx.hir().local_def_id(ctor_hir_id), item_level);
}
for field in def.fields() {
if field.vis.node.is_pub() {
- self.update(field.hir_id, item_level);
+ self.update(self.tcx.hir().local_def_id(field.hir_id), item_level);
}
}
}
hir::ItemKind::ForeignMod { items, .. } => {
for foreign_item in items {
if foreign_item.vis.node.is_pub() {
- self.update(foreign_item.id.hir_id(), item_level);
+ self.update(foreign_item.id.def_id, item_level);
}
}
}
self.reach(item.hir_id(), item_level).generics().predicates().ty().trait_ref();
for impl_item_ref in impl_.items {
- let impl_item_level = self.get(impl_item_ref.id.hir_id());
+ let impl_item_level = self.get(impl_item_ref.id.def_id);
if impl_item_level.is_some() {
self.reach(impl_item_ref.id.hir_id(), impl_item_level)
.generics()
self.reach(item.hir_id(), item_level).generics().predicates();
}
for variant in def.variants {
- let variant_level = self.get(variant.id);
+ let variant_level = self.get(self.tcx.hir().local_def_id(variant.id));
if variant_level.is_some() {
for field in variant.data.fields() {
self.reach(field.hir_id, variant_level).ty();
}
// Corner case: if the variant is reachable, but its
// enum is not, make the enum reachable as well.
- self.update(item.hir_id(), variant_level);
+ self.update(item.def_id, variant_level);
}
}
}
// Visit everything, but foreign items have their own levels.
hir::ItemKind::ForeignMod { items, .. } => {
for foreign_item in items {
- let foreign_item_level = self.get(foreign_item.id.hir_id());
+ let foreign_item_level = self.get(foreign_item.id.def_id);
if foreign_item_level.is_some() {
self.reach(foreign_item.id.hir_id(), foreign_item_level)
.generics()
if item_level.is_some() {
self.reach(item.hir_id(), item_level).generics().predicates();
for field in struct_def.fields() {
- let field_level = self.get(field.hir_id);
+ let field_level = self.get(self.tcx.hir().local_def_id(field.hir_id));
if field_level.is_some() {
self.reach(field.hir_id, field_level).ty();
}
if export.vis == ty::Visibility::Public {
if let Some(def_id) = export.res.opt_def_id() {
if let Some(def_id) = def_id.as_local() {
- let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
- self.update(hir_id, Some(AccessLevel::Exported));
+ self.update(def_id, Some(AccessLevel::Exported));
}
}
}
// `#[macro_export]`-ed `macro_rules!` are `Public` since they
// ignore their containing path to always appear at the crate root.
if md.ast.macro_rules {
- self.update(md.hir_id(), Some(AccessLevel::Public));
+ self.update(md.def_id, Some(AccessLevel::Public));
}
return;
}
- let macro_module_def_id = ty::DefIdTree::parent(self.tcx, md.def_id.to_def_id()).unwrap();
- let hir_id = macro_module_def_id
- .as_local()
- .map(|def_id| self.tcx.hir().local_def_id_to_hir_id(def_id));
- let mut module_id = match hir_id {
- Some(module_id) if self.tcx.hir().is_hir_id_module(module_id) => module_id,
- // `module_id` doesn't correspond to a `mod`, return early (#63164, #65252).
- _ => return,
- };
- let level = if md.vis.node.is_pub() { self.get(module_id) } else { None };
- let new_level = self.update(md.hir_id(), level);
+ let macro_module_def_id =
+ ty::DefIdTree::parent(self.tcx, md.def_id.to_def_id()).unwrap().expect_local();
+ if self.tcx.hir().opt_def_kind(macro_module_def_id) != Some(DefKind::Mod) {
+ // The macro's parent doesn't correspond to a `mod`, return early (#63164, #65252).
+ return;
+ }
+
+ let level = if md.vis.node.is_pub() { self.get(macro_module_def_id) } else { None };
+ let new_level = self.update(md.def_id, level);
if new_level.is_none() {
return;
}
+ // Since we are starting from an externally visible module,
+ // all the parents in the loop below are also guaranteed to be modules.
+ let mut module_def_id = macro_module_def_id;
loop {
- let changed_reachability = self.update_macro_reachable(module_id, macro_module_def_id);
- if changed_reachability || module_id == hir::CRATE_HIR_ID {
+ let changed_reachability =
+ self.update_macro_reachable(module_def_id, macro_module_def_id);
+ if changed_reachability || module_def_id == CRATE_DEF_ID {
break;
}
- module_id = self.tcx.hir().get_parent_node(module_id);
+ module_def_id =
+ ty::DefIdTree::parent(self.tcx, module_def_id.to_def_id()).unwrap().expect_local();
}
}
}
if let (ty::Visibility::Public, _) | (_, Some(AccessLevel::ReachableFromImplTrait)) =
(self.tcx().visibility(def_id.to_def_id()), self.access_level)
{
- let hir_id = self.ev.tcx.hir().local_def_id_to_hir_id(def_id);
- self.ev.update(hir_id, self.access_level);
+ self.ev.update(def_id, self.access_level);
}
}
ControlFlow::CONTINUE
}
}
- fn trait_is_public(&self, trait_id: hir::HirId) -> bool {
+ fn trait_is_public(&self, trait_id: LocalDefId) -> bool {
// FIXME: this would preferably be using `exported_items`, but all
// traits are exported currently (see `EmbargoVisitor.exported_trait`).
self.access_levels.is_public(trait_id)
}
}
- fn item_is_public(&self, id: &hir::HirId, vis: &hir::Visibility<'_>) -> bool {
- self.access_levels.is_reachable(*id) || vis.node.is_pub()
+ fn item_is_public(&self, def_id: LocalDefId, vis: &hir::Visibility<'_>) -> bool {
+ self.access_levels.is_reachable(def_id) || vis.node.is_pub()
}
}
hir::ItemKind::ForeignMod { .. } => {}
hir::ItemKind::Trait(.., ref bounds, _) => {
- if !self.trait_is_public(item.hir_id()) {
+ if !self.trait_is_public(item.def_id) {
return;
}
let not_private_trait = impl_.of_trait.as_ref().map_or(
true, // no trait counts as public trait
|tr| {
- let did = tr.path.res.def_id();
-
- if let Some(did) = did.as_local() {
- self.trait_is_public(self.tcx.hir().local_def_id_to_hir_id(did))
+ if let Some(def_id) = tr.path.res.def_id().as_local() {
+ self.trait_is_public(def_id)
} else {
true // external traits must be public
}
let impl_item = self.tcx.hir().impl_item(impl_item_ref.id);
match impl_item.kind {
hir::ImplItemKind::Const(..) | hir::ImplItemKind::Fn(..) => {
- self.access_levels.is_reachable(impl_item_ref.id.hir_id())
+ self.access_levels.is_reachable(impl_item_ref.id.def_id)
}
hir::ImplItemKind::TyAlias(_) => false,
}
let impl_item = self.tcx.hir().impl_item(impl_item_ref.id);
match impl_item.kind {
hir::ImplItemKind::Const(..) | hir::ImplItemKind::Fn(..)
- if self.item_is_public(
- &impl_item.hir_id(),
- &impl_item.vis,
- ) =>
+ if self
+ .item_is_public(impl_item.def_id, &impl_item.vis) =>
{
intravisit::walk_impl_item(self, impl_item)
}
// methods will be visible as `Public::foo`.
let mut found_pub_static = false;
for impl_item_ref in impl_.items {
- if self.item_is_public(&impl_item_ref.id.hir_id(), &impl_item_ref.vis) {
+ if self.item_is_public(impl_item_ref.id.def_id, &impl_item_ref.vis) {
let impl_item = self.tcx.hir().impl_item(impl_item_ref.id);
match impl_item_ref.kind {
AssocItemKind::Const => {
hir::ItemKind::TyAlias(..) => return,
// Not at all public, so we don't care.
- _ if !self.item_is_public(&item.hir_id(), &item.vis) => {
+ _ if !self.item_is_public(item.def_id, &item.vis) => {
return;
}
}
fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
- if self.access_levels.is_reachable(item.hir_id()) {
+ if self.access_levels.is_reachable(item.def_id) {
intravisit::walk_foreign_item(self, item)
}
}
g: &'tcx hir::Generics<'tcx>,
item_id: hir::HirId,
) {
- if self.access_levels.is_reachable(v.id) {
+ if self.access_levels.is_reachable(self.tcx.hir().local_def_id(v.id)) {
self.in_variant = true;
intravisit::walk_variant(self, v, g, item_id);
self.in_variant = false;
break;
}
}
- visitor.update(hir::CRATE_HIR_ID, Some(AccessLevel::Public));
+ visitor.update(CRATE_DEF_ID, Some(AccessLevel::Public));
tcx.arena.alloc(visitor.access_levels)
}
}
}
+impl<'tcx> Key for (ty::PolyTraitRef<'tcx>, ty::PolyTraitRef<'tcx>) {
+ #[inline(always)]
+ fn query_crate_is_local(&self) -> bool {
+ self.0.def_id().krate == LOCAL_CRATE
+ }
+ fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
+ tcx.def_span(self.0.def_id())
+ }
+}
+
impl<'tcx> Key for GenericArg<'tcx> {
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
&mut self,
sig: &'tcx hir::FnSig<'tcx>,
body: Option<hir::BodyId>,
- hir_id: hir::HirId,
+ def_id: LocalDefId,
ident: Ident,
generics: &'tcx hir::Generics<'tcx>,
vis: &hir::Visibility<'tcx>,
span: Span,
) {
- debug!("process_method: {}:{}", hir_id, ident);
+ debug!("process_method: {:?}:{}", def_id, ident);
let map = &self.tcx.hir();
- self.nest_typeck_results(map.local_def_id(hir_id), |v| {
+ let hir_id = map.local_def_id_to_hir_id(def_id);
+ self.nest_typeck_results(def_id, |v| {
if let Some(mut method_data) = v.save_ctxt.get_method_data(hir_id, ident, span) {
if let Some(body) = body {
v.process_formals(map.body(body).params, &method_data.qualname);
fn_to_string(sig.decl, sig.header, Some(ident.name), generics, vis, &[], None);
method_data.sig = sig::method_signature(hir_id, ident, generics, sig, &v.save_ctxt);
- v.dumper.dump_def(&access_from_vis!(v.save_ctxt, vis, hir_id), method_data);
+ v.dumper.dump_def(&access_from_vis!(v.save_ctxt, vis, def_id), method_data);
}
// walk arg and return types
) {
let field_data = self.save_ctxt.get_field_data(field, parent_id);
if let Some(field_data) = field_data {
- self.dumper.dump_def(&access_from!(self.save_ctxt, field, field.hir_id), field_data);
+ self.dumper.dump_def(
+ &access_from!(self.save_ctxt, field, self.tcx.hir().local_def_id(field.hir_id)),
+ field_data,
+ );
}
}
v.process_formals(body.params, &fn_data.qualname);
v.process_generic_params(ty_params, &fn_data.qualname, item.hir_id());
- v.dumper.dump_def(&access_from!(v.save_ctxt, item, item.hir_id()), fn_data);
+ v.dumper.dump_def(&access_from!(v.save_ctxt, item, item.def_id), fn_data);
}
for arg in decl.inputs {
self.nest_typeck_results(item.def_id, |v| {
if let Some(var_data) = v.save_ctxt.get_item_data(item) {
down_cast_data!(var_data, DefData, item.span);
- v.dumper.dump_def(&access_from!(v.save_ctxt, item, item.hir_id()), var_data);
+ v.dumper.dump_def(&access_from!(v.save_ctxt, item, item.def_id), var_data);
}
v.visit_ty(&typ);
v.visit_expr(expr);
fn process_assoc_const(
&mut self,
- hir_id: hir::HirId,
+ def_id: LocalDefId,
ident: Ident,
typ: &'tcx hir::Ty<'tcx>,
expr: Option<&'tcx hir::Expr<'tcx>>,
vis: &hir::Visibility<'tcx>,
attrs: &'tcx [ast::Attribute],
) {
- let qualname =
- format!("::{}", self.tcx.def_path_str(self.tcx.hir().local_def_id(hir_id).to_def_id()));
+ let qualname = format!("::{}", self.tcx.def_path_str(def_id.to_def_id()));
if !self.span.filter_generated(ident.span) {
+ let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
let sig = sig::assoc_const_signature(hir_id, ident.name, typ, expr, &self.save_ctxt);
let span = self.span_from_span(ident.span);
self.dumper.dump_def(
- &access_from_vis!(self.save_ctxt, vis, hir_id),
+ &access_from_vis!(self.save_ctxt, vis, def_id),
Def {
kind: DefKind::Const,
id: id_from_hir_id(hir_id, &self.save_ctxt),
}
// walk type and init value
- self.nest_typeck_results(self.tcx.hir().local_def_id(hir_id), |v| {
+ self.nest_typeck_results(def_id, |v| {
v.visit_ty(typ);
if let Some(expr) = expr {
v.visit_expr(expr);
let span = self.span_from_span(item.ident.span);
let attrs = self.tcx.hir().attrs(item.hir_id());
self.dumper.dump_def(
- &access_from!(self.save_ctxt, item, item.hir_id()),
+ &access_from!(self.save_ctxt, item, item.def_id),
Def {
kind,
id: id_from_def_id(item.def_id.to_def_id()),
};
down_cast_data!(enum_data, DefData, item.span);
- let access = access_from!(self.save_ctxt, item, item.hir_id());
+ let access = access_from!(self.save_ctxt, item, item.def_id);
for variant in enum_definition.variants {
let name = variant.ident.name.to_string();
methods.iter().map(|i| id_from_def_id(i.id.def_id.to_def_id())).collect();
let attrs = self.tcx.hir().attrs(item.hir_id());
self.dumper.dump_def(
- &access_from!(self.save_ctxt, item, item.hir_id()),
+ &access_from!(self.save_ctxt, item, item.def_id),
Def {
kind: DefKind::Trait,
id,
fn process_mod(&mut self, item: &'tcx hir::Item<'tcx>) {
if let Some(mod_data) = self.save_ctxt.get_item_data(item) {
down_cast_data!(mod_data, DefData, item.span);
- self.dumper.dump_def(&access_from!(self.save_ctxt, item, item.hir_id()), mod_data);
+ self.dumper.dump_def(&access_from!(self.save_ctxt, item, item.def_id), mod_data);
}
}
let respan = respan(vis_span, hir::VisibilityKind::Public);
let attrs = self.tcx.hir().attrs(trait_item.hir_id());
self.process_assoc_const(
- trait_item.hir_id(),
+ trait_item.def_id,
trait_item.ident,
&ty,
body,
self.process_method(
sig,
body,
- trait_item.hir_id(),
+ trait_item.def_id,
trait_item.ident,
&trait_item.generics,
&respan,
let body = self.tcx.hir().body(body);
let attrs = self.tcx.hir().attrs(impl_item.hir_id());
self.process_assoc_const(
- impl_item.hir_id(),
+ impl_item.def_id,
impl_item.ident,
&ty,
Some(&body.value),
self.process_method(
sig,
Some(body),
- impl_item.hir_id(),
+ impl_item.def_id,
impl_item.ident,
&impl_item.generics,
&impl_item.vis,
hir::ItemKind::Use(path, hir::UseKind::Single) => {
let sub_span = path.segments.last().unwrap().ident.span;
if !self.span.filter_generated(sub_span) {
- let access = access_from!(self.save_ctxt, item, item.hir_id());
+ let access = access_from!(self.save_ctxt, item, item.def_id);
let ref_id = self.lookup_def_id(item.hir_id()).map(id_from_def_id);
let span = self.span_from_span(sub_span);
let parent =
// we don't want to track anyway, since it's probably macro-internal `use`
if let Some(sub_span) = self.span.sub_span_of_star(item.span) {
if !self.span.filter_generated(item.span) {
- let access = access_from!(self.save_ctxt, item, item.hir_id());
+ let access = access_from!(self.save_ctxt, item, item.def_id);
let span = self.span_from_span(sub_span);
let parent =
self.save_ctxt.tcx.parent(item.def_id.to_def_id()).map(id_from_def_id);
let attrs = self.tcx.hir().attrs(item.hir_id());
self.dumper.dump_def(
- &access_from!(self.save_ctxt, item, item.hir_id()),
+ &access_from!(self.save_ctxt, item, item.def_id),
Def {
kind: DefKind::Type,
id,
}
fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
- let access = access_from!(self.save_ctxt, item, item.hir_id());
+ let access = access_from!(self.save_ctxt, item, item.def_id);
match item.kind {
hir::ForeignItemKind::Fn(decl, _, ref generics) => {
"compile without linking"),
no_parallel_llvm: bool = (false, parse_no_flag, [UNTRACKED],
"run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO)"),
+ no_profiler_runtime: bool = (false, parse_no_flag, [TRACKED],
+ "prevent automatic injection of the profiler_builtins crate"),
normalize_docs: bool = (false, parse_bool, [TRACKED],
"normalize associated items in rustdoc when generating documentation"),
osx_rpath_install_name: bool = (false, parse_bool, [TRACKED],
profile_emit: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
"file path to emit profiling data at runtime when using 'profile' \
(default based on relative source path)"),
- profiler_runtime: Option<String> = (Some(String::from("profiler_builtins")), parse_opt_string, [TRACKED],
- "name of the profiler runtime crate to automatically inject, or None to disable"),
+ profiler_runtime: String = (String::from("profiler_builtins"), parse_string, [TRACKED],
+ "name of the profiler runtime crate to automatically inject (default: `profiler_builtins`)"),
query_dep_graph: bool = (false, parse_bool, [UNTRACKED],
"enable queries of the dependency graph for regression testing (default: no)"),
query_stats: bool = (false, parse_bool, [UNTRACKED],
&self.parse_sess.span_diagnostic
}
+ pub fn with_disabled_diagnostic<T, F: FnOnce() -> T>(&self, f: F) -> T {
+ self.parse_sess.span_diagnostic.with_disabled_diagnostic(f)
+ }
+
/// Analogous to calling methods on the given `DiagnosticBuilder`, but
/// deduplicates on lint ID, span (if any), and message for this `Session`
fn diag_once<'a, 'b>(
expected,
expf32,
expf64,
+ explicit_generic_args_with_impl_trait,
export_name,
expr,
extended_key_value_attributes,
self.0.as_u32()
}
+ pub fn len(self) -> usize {
+ with_interner(|interner| interner.get(self).len())
+ }
+
pub fn is_empty(self) -> bool {
self == kw::Empty
}
let generics = self.tcx.generics_of(*def_id);
if generics.params.iter().any(|p| p.name != kw::SelfUpper)
&& !snippet.ends_with('>')
+ && !generics.has_impl_trait()
+ && !self.tcx.fn_trait_kind_from_lang_item(*def_id).is_some()
{
// FIXME: To avoid spurious suggestions in functions where type arguments
// where already supplied, we check the snippet to make sure it doesn't
}
/// Find slot offset for trait vptr within vtable entries of another trait
-/// FIXME: This function is not yet used. Remove `#[allow(dead_code)]` when it's used in upcoming pr.
-#[allow(dead_code)]
-fn vtable_trait_vptr_slot_offset<'tcx>(
+pub fn vtable_trait_upcasting_coercion_new_vptr_slot(
tcx: TyCtxt<'tcx>,
key: (
- ty::PolyTraitRef<'tcx>, // trait_to_be_found
- ty::PolyTraitRef<'tcx>, // trait_owning_vtable
+ ty::PolyTraitRef<'tcx>, // trait owning vtable
+ ty::PolyTraitRef<'tcx>, // super trait ref
),
) -> Option<usize> {
- let (trait_to_be_found, trait_owning_vtable) = key;
+ let (trait_owning_vtable, super_trait_ref) = key;
+ let super_trait_did = super_trait_ref.def_id();
+ // FIXME: take substsref part into account here after upcasting coercion allows the same def_id occur
+ // multiple times.
let vtable_segment_callback = {
let mut vptr_offset = 0;
}
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
vptr_offset += util::count_own_vtable_entries(tcx, trait_ref);
- if trait_ref == trait_to_be_found {
+ if trait_ref.def_id() == super_trait_did {
if emit_vptr {
return ControlFlow::Break(Some(vptr_offset));
} else {
specializes: specialize::specializes,
codegen_fulfill_obligation: codegen::codegen_fulfill_obligation,
vtable_entries,
+ vtable_trait_upcasting_coercion_new_vptr_slot,
subst_and_check_impossible_predicates,
mir_abstract_const: |tcx, def_id| {
let def_id = def_id.expect_local();
let default_counts = gen_params.own_defaults();
let param_counts = gen_params.own_counts();
- let named_type_param_count = param_counts.types - has_self as usize;
+
+ // Subtracting from param count to ensure type params synthesized from `impl Trait`
+ // cannot be explictly specified even with `explicit_generic_args_with_impl_trait`
+ // feature enabled.
+ let synth_type_param_count = if tcx.features().explicit_generic_args_with_impl_trait {
+ gen_params
+ .params
+ .iter()
+ .filter(|param| {
+ matches!(
+ param.kind,
+ ty::GenericParamDefKind::Type {
+ synthetic: Some(
+ hir::SyntheticTyParamKind::ImplTrait
+ | hir::SyntheticTyParamKind::FromAttr
+ ),
+ ..
+ }
+ )
+ })
+ .count()
+ } else {
+ 0
+ };
+ let named_type_param_count =
+ param_counts.types - has_self as usize - synth_type_param_count;
let infer_lifetimes =
gen_pos != GenericArgPosition::Type && !gen_args.has_lifetime_params();
seg: &hir::PathSegment<'_>,
generics: &ty::Generics,
) -> bool {
- let explicit = !seg.infer_args;
- let impl_trait = generics.params.iter().any(|param| {
- matches!(
- param.kind,
- ty::GenericParamDefKind::Type {
- synthetic: Some(
- hir::SyntheticTyParamKind::ImplTrait | hir::SyntheticTyParamKind::FromAttr,
- ),
- ..
- }
- )
- });
+ if seg.infer_args || tcx.features().explicit_generic_args_with_impl_trait {
+ return false;
+ }
+
+ let impl_trait = generics.has_impl_trait();
- if explicit && impl_trait {
+ if impl_trait {
let spans = seg
.args()
.args
StatementAsExpression,
};
-macro_rules! create_maybe_get_coercion_reason {
- ($fn_name:ident, $node:expr) => {
- pub(crate) fn $fn_name(&self, hir_id: hir::HirId, sp: Span) -> Option<(Span, String)> {
- let node = $node(self.tcx.hir(), hir_id);
- if let hir::Node::Block(block) = node {
- // check that the body's parent is an fn
- let parent = self.tcx.hir().get(
- self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(block.hir_id)),
- );
- if let (
- Some(expr),
- hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }),
- ) = (&block.expr, parent)
- {
- // check that the `if` expr without `else` is the fn body's expr
- if expr.span == sp {
- return self.get_fn_decl(hir_id).and_then(|(fn_decl, _)| {
- let span = fn_decl.output.span();
- let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok()?;
- Some((
- span,
- format!("expected `{}` because of this return type", snippet),
- ))
- });
- }
- }
- }
- if let hir::Node::Local(hir::Local { ty: Some(_), pat, .. }) = node {
- return Some((pat.span, "expected because of this assignment".to_string()));
- }
- None
- }
- };
-}
-
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn check_match(
&self,
expr.span,
&arms[0].body,
&mut coercion,
- |hir_id, span| self.maybe_get_coercion_reason(hir_id, span),
+ |hir_id, span| self.coercion_reason_match(hir_id, span),
) {
tcx.ty_error()
} else {
error
}
- create_maybe_get_coercion_reason!(
- maybe_get_coercion_reason,
- |hir: rustc_middle::hir::map::Map<'a>, id| {
- let arm_id = hir.get_parent_node(id);
- let match_id = hir.get_parent_node(arm_id);
- let containing_id = hir.get_parent_node(match_id);
- hir.get(containing_id)
- }
- );
+ pub(crate) fn coercion_reason_if(
+ &self,
+ hir_id: hir::HirId,
+ span: Span,
+ ) -> Option<(Span, String)> {
+ self.coercion_reason_inner(hir_id, span, 1)
+ }
- create_maybe_get_coercion_reason!(
- maybe_get_coercion_reason_if,
- |hir: rustc_middle::hir::map::Map<'a>, id| {
- let rslt = hir.get_parent_node(hir.get_parent_node(id));
- hir.get(rslt)
+ pub(crate) fn coercion_reason_match(
+ &self,
+ hir_id: hir::HirId,
+ span: Span,
+ ) -> Option<(Span, String)> {
+ self.coercion_reason_inner(hir_id, span, 2)
+ }
+
+ fn coercion_reason_inner(
+ &self,
+ hir_id: hir::HirId,
+ span: Span,
+ parent_index: usize,
+ ) -> Option<(Span, String)> {
+ let hir = self.tcx.hir();
+ let mut parent_iter = hir.parent_iter(hir_id);
+ let (_, node) = parent_iter.nth(parent_index)?;
+ match node {
+ hir::Node::Block(block) => {
+ let expr = block.expr?;
+ // check that the body's parent is an fn
+ let (_, parent) = parent_iter.nth(1)?;
+ if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) = parent {
+ // check that the `if` expr without `else` is the fn body's expr
+ if expr.span == span {
+ let (fn_decl, _) = self.get_fn_decl(hir_id)?;
+ let span = fn_decl.output.span();
+ let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok()?;
+ return Some((
+ span,
+ format!("expected `{}` because of this return type", snippet),
+ ));
+ }
+ }
+ None
+ }
+ hir::Node::Local(hir::Local { ty: Some(_), pat, .. }) => {
+ Some((pat.span, "expected because of this assignment".to_string()))
+ }
+ _ => None,
}
- );
+ }
pub(crate) fn if_cause(
&self,
match e.kind() {
ty::Param(_) => (), // pass struct<T>(T, T, T, T) through, let monomorphization catch errors
ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_) => (), // struct(u8, u8, u8, u8) is ok
+ ty::Array(t, _) if matches!(t.kind(), ty::Param(_)) => (), // pass struct<T>([T; N]) through, let monomorphization catch errors
ty::Array(t, _clen)
if matches!(
t.kind(),
self.diverges.set(cond_diverges | then_diverges & else_diverges);
} else {
self.if_fallback_coercion(sp, then_expr, &mut coerce, |hir_id, span| {
- self.maybe_get_coercion_reason_if(hir_id, span)
+ self.coercion_reason_if(hir_id, span)
});
// If the condition is false we can't diverge.
use rustc_span::source_map::{Span, Spanned};
use rustc_span::symbol::Ident;
use rustc_span::{BytePos, DUMMY_SP};
+use rustc_trait_selection::autoderef::Autoderef;
use rustc_trait_selection::traits::{ObligationCause, Pattern};
use ty::VariantDef;
// The expected type must be an array or slice, but was neither, so error.
_ => {
if !expected.references_error() {
- self.error_expected_array_or_slice(span, expected);
+ self.error_expected_array_or_slice(span, expected, ti);
}
let err = self.tcx.ty_error();
(err, Some(err), err)
.emit();
}
- fn error_expected_array_or_slice(&self, span: Span, expected_ty: Ty<'tcx>) {
+ fn error_expected_array_or_slice(&self, span: Span, expected_ty: Ty<'tcx>, ti: TopInfo<'tcx>) {
let mut err = struct_span_err!(
self.tcx.sess,
span,
if let ty::Array(..) | ty::Slice(..) = ty.kind() {
err.help("the semantics of slice patterns changed recently; see issue #62254");
}
+ } else if Autoderef::new(&self.infcx, self.param_env, self.body_id, span, expected_ty, span)
+ .any(|(ty, _)| matches!(ty.kind(), ty::Slice(..)))
+ {
+ if let (Some(span), true) = (ti.span, ti.origin_expr) {
+ if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+ err.span_suggestion(
+ span,
+ "consider slicing here",
+ format!("{}[..]", snippet),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
}
err.span_label(span, format!("pattern cannot match with input type `{}`", expected_ty));
err.emit();
codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD;
} else if tcx.sess.check_name(attr, sym::rustc_allocator) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR;
- } else if tcx.sess.check_name(attr, sym::unwind) {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::UNWIND;
} else if tcx.sess.check_name(attr, sym::ffi_returns_twice) {
if tcx.is_foreign_item(id) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_RETURNS_TWICE;
.emit();
}
} else if tcx.sess.check_name(attr, sym::rustc_allocator_nounwind) {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND;
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
} else if tcx.sess.check_name(attr, sym::naked) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED;
} else if tcx.sess.check_name(attr, sym::no_mangle) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
}
+ // Any linkage to LLVM intrinsics for now forcibly marks them all as never
+ // unwinds since LLVM sometimes can't handle codegen which `invoke`s
+ // intrinsic functions.
+ if let Some(name) = &codegen_fn_attrs.link_name {
+ if name.as_str().starts_with("llvm.") {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
+ }
+ }
+
codegen_fn_attrs
}
}
/// Create a zero-size type similar to a closure type, but named.
-#[unstable(feature = "std_internals", issue = "none")]
macro_rules! impl_fn_for_zst {
($(
$( #[$attr: meta] )*
test(attr(allow(dead_code, deprecated, unused_variables, unused_mut)))
)]
#![no_core]
+//
+// Lints:
+#![deny(rust_2021_incompatible_or_patterns)]
+#![deny(unsafe_op_in_unsafe_fn)]
#![warn(deprecated_in_future)]
-#![warn(missing_docs)]
#![warn(missing_debug_implementations)]
+#![warn(missing_docs)]
#![allow(explicit_outlives_requirements)]
-#![feature(rustc_allow_const_fn_unstable)]
-#![feature(allow_internal_unstable)]
-#![feature(arbitrary_self_types)]
-#![feature(asm)]
-#![feature(bool_to_option)]
-#![feature(cfg_target_has_atomic)]
-#![feature(const_heap)]
+//
+// Library features for const fns:
+#![feature(const_align_of_val)]
#![feature(const_alloc_layout)]
#![feature(const_arguments_as_str)]
#![feature(const_assert_type)]
-#![feature(const_discriminant)]
+#![feature(const_caller_location)]
#![feature(const_cell_into_inner)]
-#![feature(const_intrinsic_copy)]
-#![feature(const_intrinsic_forget)]
-#![feature(const_float_classify)]
+#![feature(const_discriminant)]
#![feature(const_float_bits_conv)]
-#![feature(const_int_unchecked_arith)]
+#![feature(const_float_classify)]
+#![feature(const_heap)]
#![feature(const_inherent_unchecked_arith)]
-#![feature(const_mut_refs)]
-#![feature(const_refs_to_cell)]
-#![feature(const_panic)]
-#![feature(const_pin)]
-#![cfg_attr(bootstrap, feature(const_fn_union))]
-#![feature(const_impl_trait)]
-#![feature(const_fn_floating_point_arithmetic)]
-#![feature(const_fn_fn_ptr_basics)]
-#![feature(const_fn_trait_bound)]
+#![feature(const_int_unchecked_arith)]
+#![feature(const_intrinsic_copy)]
+#![feature(const_intrinsic_forget)]
+#![feature(const_likely)]
+#![feature(const_maybe_uninit_as_ptr)]
+#![feature(const_maybe_uninit_assume_init)]
#![feature(const_option)]
-#![feature(const_precise_live_drops)]
+#![feature(const_pin)]
#![feature(const_ptr_offset)]
#![feature(const_ptr_offset_from)]
#![feature(const_ptr_read)]
#![feature(const_ptr_write)]
#![feature(const_raw_ptr_comparison)]
-#![feature(const_raw_ptr_deref)]
+#![feature(const_size_of_val)]
#![feature(const_slice_from_raw_parts)]
#![feature(const_slice_ptr_len)]
-#![feature(const_size_of_val)]
#![feature(const_swap)]
-#![feature(const_align_of_val)]
#![feature(const_type_id)]
#![feature(const_type_name)]
-#![feature(const_likely)]
#![feature(const_unreachable_unchecked)]
-#![feature(const_maybe_uninit_assume_init)]
-#![feature(const_maybe_uninit_as_ptr)]
-#![feature(custom_inner_attributes)]
+#![feature(duration_consts_2)]
+#![feature(ptr_metadata)]
+#![feature(slice_ptr_get)]
+#![feature(variant_count)]
+//
+// Language features:
+#![feature(abi_unadjusted)]
+#![feature(allow_internal_unstable)]
+#![feature(asm)]
+#![feature(associated_type_bounds)]
+#![feature(auto_traits)]
+#![feature(cfg_target_has_atomic)]
+#![feature(const_fn_floating_point_arithmetic)]
+#![feature(const_fn_fn_ptr_basics)]
+#![feature(const_fn_trait_bound)]
+#![cfg_attr(bootstrap, feature(const_fn_transmute))]
+#![cfg_attr(bootstrap, feature(const_fn_union))]
+#![feature(const_impl_trait)]
+#![feature(const_mut_refs)]
+#![feature(const_panic)]
+#![feature(const_precise_live_drops)]
+#![feature(const_raw_ptr_deref)]
+#![feature(const_refs_to_cell)]
#![feature(decl_macro)]
#![feature(doc_cfg)]
#![feature(doc_notable_trait)]
-#![feature(duration_consts_2)]
+#![feature(exhaustive_patterns)]
#![feature(extern_types)]
#![feature(fundamental)]
#![feature(intra_doc_pointers)]
#![feature(lang_items)]
#![feature(link_llvm_intrinsics)]
#![feature(llvm_asm)]
+#![feature(min_specialization)]
#![feature(negative_impls)]
#![feature(never_type)]
-#![feature(nll)]
-#![feature(exhaustive_patterns)]
#![feature(no_core)]
-#![feature(auto_traits)]
-#![feature(pin_deref_mut)]
+#![feature(no_coverage)] // rust-lang/rust#84605
+#![feature(no_niche)] // rust-lang/rust#68303
+#![feature(platform_intrinsics)]
#![feature(prelude_import)]
-#![feature(ptr_metadata)]
-#![feature(repr_simd, platform_intrinsics)]
+#![feature(repr_simd)]
+#![feature(rustc_allow_const_fn_unstable)]
#![feature(rustc_attrs)]
#![feature(simd_ffi)]
-#![feature(min_specialization)]
#![feature(staged_api)]
-#![feature(std_internals)]
#![feature(stmt_expr_attributes)]
-#![feature(str_split_as_str)]
-#![feature(str_split_inclusive_as_str)]
-#![feature(char_indices_offset)]
#![feature(trait_alias)]
#![feature(transparent_unions)]
#![feature(try_blocks)]
#![feature(unboxed_closures)]
#![feature(unsized_fn_params)]
-#![feature(unwind_attributes)]
-#![feature(variant_count)]
-#![feature(tbm_target_feature)]
-#![feature(sse4a_target_feature)]
-#![feature(arm_target_feature)]
-#![feature(powerpc_target_feature)]
-#![feature(mips_target_feature)]
+//
+// Target features:
#![feature(aarch64_target_feature)]
-#![feature(wasm_target_feature)]
+#![feature(adx_target_feature)]
+#![feature(arm_target_feature)]
#![feature(avx512_target_feature)]
#![feature(cmpxchg16b_target_feature)]
-#![feature(rtm_target_feature)]
#![feature(f16c_target_feature)]
#![feature(hexagon_target_feature)]
-#![cfg_attr(bootstrap, feature(const_fn_transmute))]
-#![feature(abi_unadjusted)]
-#![feature(adx_target_feature)]
-#![feature(associated_type_bounds)]
-#![feature(const_caller_location)]
-#![feature(slice_ptr_get)]
-#![feature(no_niche)] // rust-lang/rust#68303
-#![feature(no_coverage)] // rust-lang/rust#84605
-#![deny(unsafe_op_in_unsafe_fn)]
-#![deny(rust_2021_incompatible_or_patterns)]
+#![feature(mips_target_feature)]
+#![feature(powerpc_target_feature)]
+#![feature(rtm_target_feature)]
+#![feature(sse4a_target_feature)]
+#![feature(tbm_target_feature)]
+#![feature(wasm_target_feature)]
// allow using `core::` in intra-doc links
#[allow(unused_extern_crates)]
/// ```
#[stable(feature = "num_wrapping", since = "1.2.0")]
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_neg(self) -> Self {
- self.overflowing_neg().0
+ (0 as $SelfT).wrapping_sub(self)
}
/// Panic-free bitwise shift-left; yields `self << mask(rhs)`, where `mask` removes
use self::ParseIntError as PIE;
assert!(
- radix >= 2 && radix <= 36,
+ (2..=36).contains(&radix),
"from_str_radix_int: must lie in the range `[2, 36]` - found {}",
radix
);
/// ```
#[stable(feature = "num_wrapping", since = "1.2.0")]
#[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_neg(self) -> Self {
- self.overflowing_neg().0
+ (0 as $SelfT).wrapping_sub(self)
}
/// Panic-free bitwise shift-left; yields `self << mask(rhs)`,
//! assert_eq!(res, ["error!", "error!", "foo", "error!", "bar"]);
//! ```
//!
+//! ## Comparison operators
+//!
+//! If `T` implements [`PartialOrd`] then [`Option<T>`] will derive its
+//! [`PartialOrd`] implementation. With this order, [`None`] compares as
+//! less than any [`Some`], and two [`Some`] compare the same way as their
+//! contained values would in `T`. If `T` also implements
+//! [`Ord`], then so does [`Option<T>`].
+//!
+//! ```
+//! assert!(None < Some(0));
+//! assert!(Some(0) < Some(1));
+//! ```
+//!
//! ## Iterating over `Option`
//!
//! An [`Option`] can be iterated over. This can be helpful if you need an
/// enables more aggressive compiler optimizations.
///
/// [`wrapping_add`]: #method.wrapping_add
+ /// [allocated object]: crate::ptr#allocated-object
///
/// # Examples
///
//! [`and_then`]: Result::and_then
//! [`or_else`]: Result::or_else
//!
+//! ## Comparison operators
+//!
+//! If `T` and `E` both implement [`PartialOrd`] then [`Result<T, E>`] will
+//! derive its [`PartialOrd`] implementation. With this order, an [`Ok`]
+//! compares as less than any [`Err`], while two [`Ok`] or two [`Err`]
+//! compare as their contained values would in `T` or `E` respectively. If `T`
+//! and `E` both also implement [`Ord`], then so does [`Result<T, E>`].
+//!
+//! ```
+//! assert!(Ok(1) < Err(0));
+//! let x: Result<i32, ()> = Ok(0);
+//! let y = Ok(1);
+//! assert!(x < y);
+//! let x: Result<(), i32> = Err(0);
+//! let y = Err(1);
+//! assert!(x < y);
+//! ```
+//!
//! ## Iterating over `Result`
//!
//! A [`Result`] can be iterated over. This can be helpful if you need an
}
}
+ #[inline]
fn advance_by(&mut self, n: usize) -> Result<(), usize> {
- let advance = cmp::min(n, len!(self));
+ let advance = cmp::min(len!(self), n);
// SAFETY: By construction, `advance` does not exceed `self.len()`.
unsafe { self.post_inc_start(advance as isize) };
if advance == n { Ok(()) } else { Err(advance) }
#[inline]
fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
- let advance = cmp::min(n, len!(self));
+ let advance = cmp::min(len!(self), n);
// SAFETY: By construction, `advance` does not exceed `self.len()`.
unsafe { self.pre_dec_end(advance as isize) };
if advance == n { Ok(()) } else { Err(advance) }
--- /dev/null
+use crate::pin::Pin;
+
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+/// A stream that was created from iterator.
+///
+/// This stream is created by the [`from_iter`] function.
+/// See it documentation for more.
+///
+/// [`from_iter`]: fn.from_iter.html
+#[unstable(feature = "stream_from_iter", issue = "81798")]
+#[derive(Clone, Debug)]
+pub struct FromIter<I> {
+ iter: I,
+}
+
+#[unstable(feature = "stream_from_iter", issue = "81798")]
+impl<I> Unpin for FromIter<I> {}
+
+/// Converts an iterator into a stream.
+#[unstable(feature = "stream_from_iter", issue = "81798")]
+pub fn from_iter<I: IntoIterator>(iter: I) -> FromIter<I::IntoIter> {
+ FromIter { iter: iter.into_iter() }
+}
+
+#[unstable(feature = "stream_from_iter", issue = "81798")]
+impl<I: Iterator> Stream for FromIter<I> {
+ type Item = I::Item;
+
+ fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+ Poll::Ready(self.iter.next())
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+}
//! warning: unused result that must be used: streams do nothing unless polled
//! ```
+mod from_iter;
mod stream;
+pub use from_iter::{from_iter, FromIter};
pub use stream::Stream;
#![feature(panic_unwind)]
#![feature(staged_api)]
#![feature(std_internals)]
-#![feature(unwind_attributes)]
#![feature(abi_thiscall)]
#![feature(rustc_attrs)]
#![panic_runtime]
#![feature(panic_runtime)]
+#![feature(c_unwind)]
// `real_imp` is unused with Miri, so silence warnings.
#![cfg_attr(miri, allow(dead_code))]
// Entry point for raising an exception, just delegates to the platform-specific
// implementation.
#[rustc_std_internal_symbol]
-#[unwind(allowed)]
-pub unsafe extern "C" fn __rust_start_panic(payload: *mut &mut dyn BoxMeUp) -> u32 {
+pub unsafe extern "C-unwind" fn __rust_start_panic(payload: *mut &mut dyn BoxMeUp) -> u32 {
let payload = Box::from_raw((*payload).take_box());
imp::panic(payload)
// support capturing exceptions with std::exception_ptr, which we can't support
// because Box<dyn Any> isn't clonable.
macro_rules! define_cleanup {
- ($abi:tt) => {
+ ($abi:tt $abi2:tt) => {
unsafe extern $abi fn exception_cleanup(e: *mut Exception) {
if let Exception { data: Some(b) } = e.read() {
drop(b);
super::__rust_drop_panic();
}
}
- #[unwind(allowed)]
- unsafe extern $abi fn exception_copy(_dest: *mut Exception,
+ unsafe extern $abi2 fn exception_copy(_dest: *mut Exception,
_src: *mut Exception)
-> *mut Exception {
panic!("Rust panics cannot be copied");
}
cfg_if::cfg_if! {
if #[cfg(target_arch = "x86")] {
- define_cleanup!("thiscall");
+ define_cleanup!("thiscall" "thiscall-unwind");
} else {
- define_cleanup!("C");
+ define_cleanup!("C" "C-unwind");
}
}
ptr!(exception_copy) as u32,
);
- extern "system" {
- #[unwind(allowed)]
+ extern "system-unwind" {
fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !;
}
fn drop($self: $S::Literal);
fn clone($self: &$S::Literal) -> $S::Literal;
fn from_str(s: &str) -> Result<$S::Literal, ()>;
+ fn to_string($self: &$S::Literal) -> String;
fn debug_kind($self: &$S::Literal) -> String;
fn symbol($self: &$S::Literal) -> String;
fn suffix($self: &$S::Literal) -> Option<String>;
/// Gets the starting line/column in the source file for this span.
#[unstable(feature = "proc_macro_span", issue = "54725")]
pub fn start(&self) -> LineColumn {
- self.0.start()
+ self.0.start().add_1_to_column()
}
/// Gets the ending line/column in the source file for this span.
#[unstable(feature = "proc_macro_span", issue = "54725")]
pub fn end(&self) -> LineColumn {
- self.0.end()
+ self.0.end().add_1_to_column()
}
/// Creates a new span encompassing `self` and `other`.
/// The 1-indexed line in the source file on which the span starts or ends (inclusive).
#[unstable(feature = "proc_macro_span", issue = "54725")]
pub line: usize,
- /// The 0-indexed column (in UTF-8 characters) in the source file on which
- /// the span starts or ends (inclusive).
+ /// The 1-indexed column (number of bytes in UTF-8 encoding) in the source
+ /// file on which the span starts or ends (inclusive).
#[unstable(feature = "proc_macro_span", issue = "54725")]
pub column: usize,
}
+impl LineColumn {
+ fn add_1_to_column(self) -> Self {
+ LineColumn { line: self.line, column: self.column + 1 }
+ }
+}
+
#[unstable(feature = "proc_macro_span", issue = "54725")]
impl !Send for LineColumn {}
#[unstable(feature = "proc_macro_span", issue = "54725")]
#[stable(feature = "proc_macro_lib", since = "1.15.0")]
impl ToString for Literal {
fn to_string(&self) -> String {
- TokenStream::from(TokenTree::from(self.clone())).to_string()
+ self.0.to_string()
}
}
#[unstable(feature = "once_cell", issue = "74465")]
impl<T> Default for SyncOnceCell<T> {
+ /// Creates a new empty cell.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// #![feature(once_cell)]
+ ///
+ /// use std::lazy::SyncOnceCell;
+ ///
+ /// fn main() {
+ /// assert_eq!(SyncOnceCell::<()>::new(), SyncOnceCell::default());
+ /// }
+ /// ```
fn default() -> SyncOnceCell<T> {
SyncOnceCell::new()
}
#[unstable(feature = "once_cell", issue = "74465")]
impl<T> From<T> for SyncOnceCell<T> {
+ /// Create a new cell with its contents set to `value`.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// #![feature(once_cell)]
+ ///
+ /// use std::lazy::SyncOnceCell;
+ ///
+ /// # fn main() -> Result<(), i32> {
+ /// let a = SyncOnceCell::from(3);
+ /// let b = SyncOnceCell::new();
+ /// b.set(3)?;
+ /// assert_eq!(a, b);
+ /// Ok(())
+ /// # }
+ /// ```
fn from(value: T) -> Self {
let cell = Self::new();
match cell.set(value) {
#![feature(auto_traits)]
#![feature(bench_black_box)]
#![feature(box_syntax)]
+#![feature(c_unwind)]
#![feature(c_variadic)]
#![feature(cfg_accessible)]
#![feature(cfg_eval)]
#![feature(try_reserve)]
#![feature(unboxed_closures)]
#![feature(unsafe_cell_raw_get)]
-#![feature(unwind_attributes)]
#![feature(unwrap_infallible)]
#![feature(vec_into_raw_parts)]
#![feature(vec_spare_capacity)]
/// IPv6 addresses are defined as 128-bit integers in [IETF RFC 4291].
/// They are usually represented as eight 16-bit segments.
///
-/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses.
-///
/// The size of an `Ipv6Addr` struct may vary depending on the target operating
/// system.
///
/// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
///
+/// # Embedding IPv4 Addresses
+///
+/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses.
+///
+/// To assist in the transition from IPv4 to IPv6 two types of IPv6 addresses that embed an IPv4 address were defined:
+/// IPv4-compatible and IPv4-mapped addresses. Of these IPv4-compatible addresses have been officially deprecated.
+///
+/// Both types of addresses are not assigned any special meaning by this implementation,
+/// other than what the relevant standards prescribe. This means that an address like `::ffff:127.0.0.1`,
+/// while representing an IPv4 loopback address, is not itself an IPv6 loopback address; only `::1` is.
+/// To handle these so called "IPv4-in-IPv6" addresses, they have to first be converted to their canonical IPv4 address.
+///
+/// ### IPv4-Compatible IPv6 Addresses
+///
+/// IPv4-compatible IPv6 addresses are defined in [IETF RFC 4291 Section 2.5.5.1], and have been officially deprecated.
+/// The RFC describes the format of an "IPv4-Compatible IPv6 address" as follows:
+///
+/// ```text
+/// | 80 bits | 16 | 32 bits |
+/// +--------------------------------------+--------------------------+
+/// |0000..............................0000|0000| IPv4 address |
+/// +--------------------------------------+----+---------------------+
+/// ```
+/// So `::a.b.c.d` would be an IPv4-compatible IPv6 address representing the IPv4 address `a.b.c.d`.
+///
+/// To convert from an IPv4 address to an IPv4-compatible IPv6 address, use [`Ipv4Addr::to_ipv6_compatible`].
+/// Use [`Ipv6Addr::to_ipv4`] to convert an IPv4-compatible IPv6 address to the canonical IPv4 address.
+///
+/// [IETF RFC 4291 Section 2.5.5.1]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.1
+///
+/// ### IPv4-Mapped IPv6 Addresses
+///
+/// IPv4-mapped IPv6 addresses are defined in [IETF RFC 4291 Section 2.5.5.2].
+/// The RFC describes the format of an "IPv4-Mapped IPv6 address" as follows:
+///
+/// ```text
+/// | 80 bits | 16 | 32 bits |
+/// +--------------------------------------+--------------------------+
+/// |0000..............................0000|FFFF| IPv4 address |
+/// +--------------------------------------+----+---------------------+
+/// ```
+/// So `::ffff:a.b.c.d` would be an IPv4-mapped IPv6 address representing the IPv4 address `a.b.c.d`.
+///
+/// To convert from an IPv4 address to an IPv4-mapped IPv6 address, use [`Ipv4Addr::to_ipv6_mapped`].
+/// Use [`Ipv6Addr::to_ipv4`] to convert an IPv4-mapped IPv6 address to the canonical IPv4 address.
+///
+/// [IETF RFC 4291 Section 2.5.5.2]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2
+///
/// # Textual representation
///
/// `Ipv6Addr` provides a [`FromStr`] implementation. There are many ways to represent
pub const fn is_ipv6(&self) -> bool {
matches!(self, IpAddr::V6(_))
}
+
+ /// Converts this address to an `IpAddr::V4` if it is a IPv4-mapped IPv6 addresses, otherwise it
+ /// return `self` as-is.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ip)]
+ /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+ ///
+ /// assert_eq!(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)).to_canonical().is_loopback(), true);
+ /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1)).is_loopback(), false);
+ /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1)).to_canonical().is_loopback(), true);
+ /// ```
+ #[inline]
+ #[rustc_const_unstable(feature = "const_ip", issue = "76205")]
+ #[unstable(feature = "ip", issue = "27709")]
+ pub const fn to_canonical(&self) -> IpAddr {
+ match self {
+ &v4 @ IpAddr::V4(_) => v4,
+ IpAddr::V6(v6) => v6.to_canonical(),
+ }
+ }
}
impl Ipv4Addr {
}
}
- /// Converts this address to an IPv4-compatible [`IPv6` address].
+ /// Converts this address to an [IPv4-compatible] [`IPv6` address].
///
/// `a.b.c.d` becomes `::a.b.c.d`
///
- /// This isn't typically the method you want; these addresses don't typically
- /// function on modern systems. Use `to_ipv6_mapped` instead.
+ /// Note that IPv4-compatible addresses have been officially deprecated.
+ /// If you don't explicitly need an IPv4-compatible address for legacy reasons, consider using `to_ipv6_mapped` instead.
///
+ /// [IPv4-compatible]: Ipv6Addr#ipv4-compatible-ipv6-addresses
/// [`IPv6` address]: Ipv6Addr
///
/// # Examples
}
}
- /// Converts this address to an IPv4-mapped [`IPv6` address].
+ /// Converts this address to an [IPv4-mapped] [`IPv6` address].
///
/// `a.b.c.d` becomes `::ffff:a.b.c.d`
///
+ /// [IPv4-mapped]: Ipv6Addr#ipv4-mapped-ipv6-addresses
/// [`IPv6` address]: Ipv6Addr
///
/// # Examples
u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::UNSPECIFIED.octets())
}
- /// Returns [`true`] if this is a loopback address (::1).
+ /// Returns [`true`] if this is the [loopback address] (`::1`),
+ /// as defined in [IETF RFC 4291 section 2.5.3].
///
- /// This property is defined in [IETF RFC 4291].
+ /// Contrary to IPv4, in IPv6 there is only one loopback address.
///
- /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
+ /// [loopback address]: Ipv6Addr::LOCALHOST
+ /// [IETF RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3
///
/// # Examples
///
(self.segments()[0] & 0xff00) == 0xff00
}
- /// Converts this address to an [`IPv4` address] if it's an "IPv4-mapped IPv6 address"
- /// defined in [IETF RFC 4291 section 2.5.5.2], otherwise returns [`None`].
+ /// Converts this address to an [`IPv4` address] if it's an [IPv4-mapped] address,
+ /// as defined in [IETF RFC 4291 section 2.5.5.2], otherwise returns [`None`].
///
/// `::ffff:a.b.c.d` becomes `a.b.c.d`.
/// All addresses *not* starting with `::ffff` will return `None`.
///
/// [`IPv4` address]: Ipv4Addr
+ /// [IPv4-mapped]: Ipv6Addr
/// [IETF RFC 4291 section 2.5.5.2]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2
///
/// # Examples
}
}
- /// Converts this address to an [`IPv4` address]. Returns [`None`] if this address is
- /// neither IPv4-compatible or IPv4-mapped.
+ /// Converts this address to an [`IPv4` address] if it is either
+ /// an [IPv4-compatible] address as defined in [IETF RFC 4291 section 2.5.5.1],
+ /// or an [IPv4-mapped] address as defined in [IETF RFC 4291 section 2.5.5.2],
+ /// otherwise returns [`None`].
///
/// `::a.b.c.d` and `::ffff:a.b.c.d` become `a.b.c.d`
+ /// All addresses *not* starting with either all zeroes or `::ffff` will return `None`.
///
- /// [`IPv4` address]: Ipv4Addr
+ /// [IPv4 address]: Ipv4Addr
+ /// [IPv4-compatible]: Ipv6Addr#ipv4-compatible-ipv6-addresses
+ /// [IPv4-mapped]: Ipv6Addr#ipv4-mapped-ipv6-addresses
+ /// [IETF RFC 4291 section 2.5.5.1]: https://tools.ietf.org/html/rfc4291#section-2.5.5.1
+ /// [IETF RFC 4291 section 2.5.5.2]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2
///
/// # Examples
///
}
}
+ /// Converts this address to an `IpAddr::V4` if it is a IPv4-mapped addresses, otherwise it
+ /// returns self wrapped in a `IpAddr::V6`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ip)]
+ /// use std::net::Ipv6Addr;
+ ///
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1).is_loopback(), false);
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1).to_canonical().is_loopback(), true);
+ /// ```
+ #[inline]
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
+ #[unstable(feature = "ip", issue = "27709")]
+ pub const fn to_canonical(&self) -> IpAddr {
+ if let Some(mapped) = self.to_ipv4_mapped() {
+ return IpAddr::V4(mapped);
+ }
+ IpAddr::V6(*self)
+ }
+
/// Returns the sixteen eight-bit integers the IPv6 address consists of.
///
/// ```
#[allow(improper_ctypes)]
extern "C" {
fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static);
+}
+#[allow(improper_ctypes)]
+extern "C-unwind" {
/// `payload` is passed through another layer of raw pointers as `&mut dyn Trait` is not
/// FFI-safe. `BoxMeUp` lazily performs allocation only when needed (this avoids allocations
/// when using the "abort" panic runtime).
- #[unwind(allowed)]
fn __rust_start_panic(payload: *mut &mut dyn BoxMeUp) -> u32;
}
/// Entry point of panics from the libcore crate (`panic_impl` lang item).
#[cfg_attr(not(test), panic_handler)]
-#[unwind(allowed)]
pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
struct PanicPayload<'a> {
inner: &'a fmt::Arguments<'a>,
}
}
+ #[cfg(target_os = "haiku")]
+ pub fn set_name(name: &CStr) {
+ unsafe {
+ let thread_self = libc::find_thread(ptr::null_mut());
+ libc::rename_thread(thread_self, name.as_ptr());
+ }
+ }
+
#[cfg(any(
target_env = "newlib",
- target_os = "haiku",
target_os = "l4re",
target_os = "emscripten",
target_os = "redox",
target_os = "vxworks"
))]
pub fn set_name(_name: &CStr) {
- // Newlib, Haiku, Emscripten, and VxWorks have no way to set a thread name.
+ // Newlib, Emscripten, and VxWorks have no way to set a thread name.
}
pub fn sleep(dur: Duration) {
fn arm_linux() {
println!("neon: {}", is_arm_feature_detected!("neon"));
println!("pmull: {}", is_arm_feature_detected!("pmull"));
+ println!("crypto: {}", is_arm_feature_detected!("crypto"));
+ println!("crc: {}", is_arm_feature_detected!("crc"));
+ println!("aes: {}", is_arm_feature_detected!("aes"));
+ println!("sha2: {}", is_arm_feature_detected!("sha2"));
}
#[test]
#[cfg(all(target_arch = "aarch64", any(target_os = "linux", target_os = "android")))]
fn aarch64_linux() {
- println!("fp: {}", is_aarch64_feature_detected!("fp"));
- println!("fp16: {}", is_aarch64_feature_detected!("fp16"));
println!("neon: {}", is_aarch64_feature_detected!("neon"));
println!("asimd: {}", is_aarch64_feature_detected!("asimd"));
+ println!("pmull: {}", is_aarch64_feature_detected!("pmull"));
+ println!("fp: {}", is_aarch64_feature_detected!("fp"));
+ println!("fp16: {}", is_aarch64_feature_detected!("fp16"));
println!("sve: {}", is_aarch64_feature_detected!("sve"));
println!("crc: {}", is_aarch64_feature_detected!("crc"));
println!("lse: {}", is_aarch64_feature_detected!("lse"));
+ println!("lse2: {}", is_aarch64_feature_detected!("lse2"));
println!("rdm: {}", is_aarch64_feature_detected!("rdm"));
println!("rcpc: {}", is_aarch64_feature_detected!("rcpc"));
+ println!("rcpc2: {}", is_aarch64_feature_detected!("rcpc2"));
println!("dotprod: {}", is_aarch64_feature_detected!("dotprod"));
println!("tme: {}", is_aarch64_feature_detected!("tme"));
+ println!("fhm: {}", is_aarch64_feature_detected!("fhm"));
+ println!("dit: {}", is_aarch64_feature_detected!("dit"));
+ println!("flagm: {}", is_aarch64_feature_detected!("flagm"));
+ println!("ssbs: {}", is_aarch64_feature_detected!("ssbs"));
+ println!("sb: {}", is_aarch64_feature_detected!("sb"));
+ println!("pauth: {}", is_aarch64_feature_detected!("pauth"));
+ println!("dpb: {}", is_aarch64_feature_detected!("dpb"));
+ println!("dpb2: {}", is_aarch64_feature_detected!("dpb2"));
+ println!("sve2: {}", is_aarch64_feature_detected!("sve2"));
+ println!("sve2-aes: {}", is_aarch64_feature_detected!("sve2-aes"));
+ println!("sve2-sm4: {}", is_aarch64_feature_detected!("sve2-sm4"));
+ println!("sve2-sha3: {}", is_aarch64_feature_detected!("sve2-sha3"));
+ println!("sve2-bitperm: {}", is_aarch64_feature_detected!("sve2-bitperm"));
+ println!("frintts: {}", is_aarch64_feature_detected!("frintts"));
+ println!("i8mm: {}", is_aarch64_feature_detected!("i8mm"));
+ println!("f32mm: {}", is_aarch64_feature_detected!("f32mm"));
+ println!("f64mm: {}", is_aarch64_feature_detected!("f64mm"));
+ println!("bf16: {}", is_aarch64_feature_detected!("bf16"));
+ println!("rand: {}", is_aarch64_feature_detected!("rand"));
+ println!("bti: {}", is_aarch64_feature_detected!("bti"));
+ println!("mte: {}", is_aarch64_feature_detected!("mte"));
+ println!("jsconv: {}", is_aarch64_feature_detected!("jsconv"));
+ println!("fcma: {}", is_aarch64_feature_detected!("fcma"));
+ println!("aes: {}", is_aarch64_feature_detected!("aes"));
+ println!("sha2: {}", is_aarch64_feature_detected!("sha2"));
+ println!("sha3: {}", is_aarch64_feature_detected!("sha3"));
+ println!("sm4: {}", is_aarch64_feature_detected!("sm4"));
}
#[test]
-Subproject commit 3001c75a1d2a81d2a76bef139c69387cb2ebb820
+Subproject commit c158cfd38e20d855f5d6ca8a5a101eefb82604a8
#![feature(link_cfg)]
#![feature(nll)]
#![feature(staged_api)]
-#![feature(unwind_attributes)]
#![feature(static_nobundle)]
+#![feature(c_unwind)]
#![cfg_attr(not(target_env = "msvc"), feature(libc))]
cfg_if::cfg_if! {
all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux")),
link(name = "unwind", kind = "static")
)]
-extern "C" {
- #[unwind(allowed)]
+extern "C-unwind" {
pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !;
+}
+extern "C" {
pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception);
pub fn _Unwind_GetLanguageSpecificData(ctx: *mut _Unwind_Context) -> *mut c_void;
pub fn _Unwind_GetRegionStart(ctx: *mut _Unwind_Context) -> _Unwind_Ptr;
#[cfg_attr(all(feature = "llvm-libunwind",
any(target_os = "fuchsia", target_os = "linux")),
link(name = "unwind", kind = "static"))]
- extern "C" {
- #[unwind(allowed)]
+ extern "C-unwind" {
pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
+ }
+ extern "C" {
pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn,
trace_argument: *mut c_void)
-> _Unwind_Reason_Code;
#[cfg_attr(all(feature = "llvm-libunwind",
any(target_os = "fuchsia", target_os = "linux")),
link(name = "unwind", kind = "static"))]
- extern "C" {
- #[unwind(allowed)]
+ extern "C-unwind" {
pub fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
}
# LLVM more often than necessary.
#
# This git command finds that commit SHA, looking for bors-authored
- # merges that modified src/llvm-project.
+ # merges that modified src/llvm-project or other relevant version
+ # stamp files.
#
# This works even in a repository that has not yet initialized
# submodules.
"git", "rev-parse", "--show-toplevel",
]).decode(sys.getdefaultencoding()).strip()
llvm_sha = subprocess.check_output([
- "git", "log", "--author=bors", "--format=%H", "-n1",
- "--no-patch", "--first-parent",
+ "git", "rev-list", "--author=bors@rust-lang.org", "-n1",
+ "--merges", "--first-parent", "HEAD",
"--",
"{}/src/llvm-project".format(top_level),
"{}/src/bootstrap/download-ci-llvm-stamp".format(top_level),
# Look for a version to compare to based on the current commit.
# Only commits merged by bors will have CI artifacts.
- merge_base = ["git", "log", "--author=bors", "--pretty=%H", "-n1"]
+ merge_base = [
+ "git", "rev-list", "--author=bors@rust-lang.org", "-n1",
+ "--merges", "--first-parent", "HEAD"
+ ]
commit = subprocess.check_output(merge_base, universal_newlines=True).strip()
# Warn if there were changes to the compiler or standard library since the ancestor commit.
--- /dev/null
+# `explicit_generic_args_with_impl_trait`
+
+The tracking issue for this feature is: [#83701]
+
+[#83701]: https://github.com/rust-lang/rust/issues/83701
+
+------------------------
+
+The `explicit_generic_args_with_impl_trait` feature gate lets you specify generic arguments even
+when `impl Trait` is used in argument position.
+
+A simple example is:
+
+```rust
+#![feature(explicit_generic_args_with_impl_trait)]
+
+fn foo<T: ?Sized>(_f: impl AsRef<T>) {}
+
+fn main() {
+ foo::<str>("".to_string());
+}
+```
+
+This is currently rejected:
+
+```text
+error[E0632]: cannot provide explicit generic arguments when `impl Trait` is used in argument position
+ --> src/main.rs:6:11
+ |
+6 | foo::<str>("".to_string());
+ | ^^^ explicit generic argument not allowed
+
+```
+
+However it would compile if `explicit_generic_args_with_impl_trait` is enabled.
+
+Note that the synthetic type parameters from `impl Trait` are still implicit and you
+cannot explicitly specify these:
+
+```rust,compile_fail
+#![feature(explicit_generic_args_with_impl_trait)]
+
+fn foo<T: ?Sized>(_f: impl AsRef<T>) {}
+fn bar<T: ?Sized, F: AsRef<T>>(_f: F) {}
+
+fn main() {
+ bar::<str, _>("".to_string()); // Okay
+ bar::<str, String>("".to_string()); // Okay
+
+ foo::<str>("".to_string()); // Okay
+ foo::<str, String>("".to_string()); // Error, you cannot specify `impl Trait` explicitly
+}
+```
--- /dev/null
+# `trait_upcasting`
+
+The tracking issue for this feature is: [#65991]
+
+[#65991]: https://github.com/rust-lang/rust/issues/65991
+
+------------------------
+
+The `trait_upcasting` feature adds support for trait upcasting coercion. This allows a
+trait object of type `dyn Bar` to be cast to a trait object of type `dyn Foo`
+so long as `Bar: Foo`.
+
+```rust,edition2018
+#![feature(trait_upcasting)]
+#![allow(incomplete_features)]
+
+trait Foo {}
+
+trait Bar: Foo {}
+
+impl Foo for i32 {}
+
+impl<T: Foo + ?Sized> Bar for T {}
+
+let bar: &dyn Bar = &123;
+let foo: &dyn Foo = bar;
+```
This will decrement the `{0}` register value from 10 to 3, then add 2 and store it in `a`.
-This example show a few thing:
+This example shows a few things:
First that the same number can be used as a label multiple times in the same inline block.
visibility: Inherited,
def_id: ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id },
kind: box ImplItem(Impl {
- span: Span::from_rustc_span(self.cx.tcx.def_span(impl_def_id)),
+ span: Span::new(self.cx.tcx.def_span(impl_def_id)),
unsafety: hir::Unsafety::Normal,
generics: (
self.cx.tcx.generics_of(impl_def_id),
}
}
- let span = clean::Span::from_rustc_span(cx.tcx.def_span(did));
+ let span = clean::Span::new(cx.tcx.def_span(did));
clean::Module { items, span }
}
// determine if we should display the inner contents or
// the outer `mod` item for the source code.
- let span = Span::from_rustc_span({
+
+ let span = Span::new({
let where_outer = self.where_outer(cx.tcx);
let sm = cx.sess().source_map();
let outer = sm.lookup_char_pos(where_outer.lo());
if let Some(((_, trait_did, name), rhs)) =
proj.as_ref().and_then(|(lhs, rhs)| Some((lhs.projection()?, rhs)))
{
- impl_trait_proj.entry(param_idx).or_default().push((
- trait_did.into(),
- name,
- rhs,
- ));
+ impl_trait_proj
+ .entry(param_idx)
+ .or_default()
+ .push((trait_did, name, rhs));
}
return None;
rustc_data_structures::static_assert_size!(Item, 56);
crate fn rustc_span(def_id: DefId, tcx: TyCtxt<'_>) -> Span {
- Span::from_rustc_span(def_id.as_local().map_or_else(
+ Span::new(def_id.as_local().map_or_else(
|| tcx.def_span(def_id),
|local| {
let hir = tcx.hir();
impl Type {
fn inner_def_id(&self, cache: Option<&Cache>) -> Option<DefId> {
let t: PrimitiveType = match *self {
- ResolvedPath { did, .. } => return Some(did.into()),
+ ResolvedPath { did, .. } => return Some(did),
DynTrait(ref bounds, _) => return bounds[0].trait_.inner_def_id(cache),
Primitive(p) => return cache.and_then(|c| c.primitive_locations.get(&p).cloned()),
BorrowedRef { type_: box Generic(..), .. } => PrimitiveType::Reference,
crate struct Span(rustc_span::Span);
impl Span {
- crate fn from_rustc_span(sp: rustc_span::Span) -> Self {
- // Get the macro invocation instead of the definition,
- // in case the span is result of a macro expansion.
- // (See rust-lang/rust#39726)
+ /// Wraps a [`rustc_span::Span`]. In case this span is the result of a macro expansion, the
+ /// span will be updated to point to the macro invocation instead of the macro definition.
+ ///
+ /// (See rust-lang/rust#39726)
+ crate fn new(sp: rustc_span::Span) -> Self {
Self(sp.source_callsite())
}
crate show_type_layout: bool,
crate unstable_features: rustc_feature::UnstableFeatures,
crate emit: Vec<EmitType>,
+ /// If `true`, HTML source pages will generate links for items to their definition.
+ crate generate_link_to_definition: bool,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
let generate_redirect_map = matches.opt_present("generate-redirect-map");
let show_type_layout = matches.opt_present("show-type-layout");
let nocapture = matches.opt_present("nocapture");
+ let generate_link_to_definition = matches.opt_present("generate-link-to-definition");
+
+ if generate_link_to_definition && (show_coverage || output_format != OutputFormat::Html) {
+ diag.struct_err(
+ "--generate-link-to-definition option can only be used with HTML output format",
+ )
+ .emit();
+ return Err(1);
+ }
let (lint_opts, describe_lints, lint_cap) =
get_cmd_lint_options(matches, error_format, &debugging_opts);
crate_name.as_deref(),
),
emit,
+ generate_link_to_definition,
},
crate_name,
output_format,
});
rustc_passes::stability::check_unused_or_stable_features(tcx);
- let access_levels = tcx.privacy_access_levels(());
- // Convert from a HirId set to a DefId set since we don't always have easy access
- // to the map from defid -> hirid
let access_levels = AccessLevels {
- map: access_levels
- .map
- .iter()
- .map(|(&k, &v)| (tcx.hir().local_def_id(k).to_def_id(), v))
- .collect(),
+ map: tcx.privacy_access_levels(()).map.iter().map(|(k, v)| (k.to_def_id(), *v)).collect(),
};
let mut ctxt = DocContext {
if i.blanket_impl.is_none() {
self.cache
.implementors
- .entry(did.into())
+ .entry(did)
.or_default()
.push(Impl { impl_item: item.clone() });
}
NotInExternalCache,
}
-crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<String>), HrefError> {
+crate fn href_with_root_path(
+ did: DefId,
+ cx: &Context<'_>,
+ root_path: Option<&str>,
+) -> Result<(String, ItemType, Vec<String>), HrefError> {
let cache = &cx.cache();
let relative_to = &cx.current;
fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] {
return Err(HrefError::Private);
}
+ let mut is_remote = false;
let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) {
Some(&(ref fqp, shortty)) => (fqp, shortty, {
let module_fqp = to_module_fqp(shortty, fqp);
shortty,
match cache.extern_locations[&did.krate] {
ExternalLocation::Remote(ref s) => {
+ is_remote = true;
let s = s.trim_end_matches('/');
let mut s = vec![s];
s.extend(module_fqp[..].iter().map(String::as_str));
}
}
};
+ if !is_remote {
+ if let Some(root_path) = root_path {
+ let root = root_path.trim_end_matches('/');
+ url_parts.insert(0, root);
+ }
+ }
let last = &fqp.last().unwrap()[..];
let filename;
match shortty {
Ok((url_parts.join("/"), shortty, fqp.to_vec()))
}
+crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<String>), HrefError> {
+ href_with_root_path(did, cx, None)
+}
+
/// Both paths should only be modules.
/// This is because modules get their own directories; that is, `std::vec` and `std::vec::Vec` will
/// both need `../iter/trait.Iterator.html` to get at the iterator trait.
text: &'a str,
cx: &'cx Context<'_>,
) -> impl fmt::Display + 'a {
- let parts = href(did.into(), cx);
+ let parts = href(did, cx);
display_fn(move |f| {
if let Ok((url, short_ty, fqp)) = parts {
write!(
// everything comes in as a fully resolved QPath (hard to
// look at).
box clean::ResolvedPath { did, .. } => {
- match href(did.into(), cx) {
+ match href(did, cx) {
Ok((ref url, _, ref path)) if !f.alternate() => {
write!(
f,
//! Use the `render_with_highlighting` to highlight some rust code.
use crate::html::escape::Escape;
+use crate::html::render::Context;
-use std::fmt::Display;
+use std::fmt::{Display, Write};
use std::iter::Peekable;
use rustc_lexer::{LiteralKind, TokenKind};
use rustc_span::edition::Edition;
use rustc_span::symbol::Symbol;
-
-use super::format::Buffer;
+use rustc_span::{BytePos, Span, DUMMY_SP};
+
+use super::format::{self, Buffer};
+use super::render::LinkFromSrc;
+
+/// This type is needed in case we want to render links on items to allow to go to their definition.
+crate struct ContextInfo<'a, 'b, 'c> {
+ crate context: &'a Context<'b>,
+ /// This span contains the current file we're going through.
+ crate file_span: Span,
+ /// This field is used to know "how far" from the top of the directory we are to link to either
+ /// documentation pages or other source pages.
+ crate root_path: &'c str,
+}
/// Highlights `src`, returning the HTML output.
crate fn render_with_highlighting(
tooltip: Option<(Option<Edition>, &str)>,
edition: Edition,
extra_content: Option<Buffer>,
+ context_info: Option<ContextInfo<'_, '_, '_>>,
) {
debug!("highlighting: ================\n{}\n==============", src);
if let Some((edition_info, class)) = tooltip {
}
write_header(out, class, extra_content);
- write_code(out, &src, edition);
+ write_code(out, &src, edition, context_info);
write_footer(out, playground_button);
}
}
}
-fn write_code(out: &mut Buffer, src: &str, edition: Edition) {
+/// Convert the given `src` source code into HTML by adding classes for highlighting.
+///
+/// This code is used to render code blocks (in the documentation) as well as the source code pages.
+///
+/// Some explanations on the last arguments:
+///
+/// In case we are rendering a code block and not a source code file, `context_info` will be `None`.
+/// To put it more simply: if `context_info` is `None`, the code won't try to generate links to an
+/// item definition.
+///
+/// More explanations about spans and how we use them here are provided in the
+fn write_code(
+ out: &mut Buffer,
+ src: &str,
+ edition: Edition,
+ context_info: Option<ContextInfo<'_, '_, '_>>,
+) {
// This replace allows to fix how the code source with DOS backline characters is displayed.
let src = src.replace("\r\n", "\n");
- Classifier::new(&src, edition).highlight(&mut |highlight| {
- match highlight {
- Highlight::Token { text, class } => string(out, Escape(text), class),
- Highlight::EnterSpan { class } => enter_span(out, class),
- Highlight::ExitSpan => exit_span(out),
- };
- });
+ Classifier::new(&src, edition, context_info.as_ref().map(|c| c.file_span).unwrap_or(DUMMY_SP))
+ .highlight(&mut |highlight| {
+ match highlight {
+ Highlight::Token { text, class } => string(out, Escape(text), class, &context_info),
+ Highlight::EnterSpan { class } => enter_span(out, class),
+ Highlight::ExitSpan => exit_span(out),
+ };
+ });
}
fn write_footer(out: &mut Buffer, playground_button: Option<&str>) {
KeyWord,
// Keywords that do pointer/reference stuff.
RefKeyWord,
- Self_,
+ Self_(Span),
Op,
Macro,
MacroNonTerminal,
String,
Number,
Bool,
- Ident,
+ Ident(Span),
Lifetime,
PreludeTy,
PreludeVal,
Class::Attribute => "attribute",
Class::KeyWord => "kw",
Class::RefKeyWord => "kw-2",
- Class::Self_ => "self",
+ Class::Self_(_) => "self",
Class::Op => "op",
Class::Macro => "macro",
Class::MacroNonTerminal => "macro-nonterminal",
Class::String => "string",
Class::Number => "number",
Class::Bool => "bool-val",
- Class::Ident => "ident",
+ Class::Ident(_) => "ident",
Class::Lifetime => "lifetime",
Class::PreludeTy => "prelude-ty",
Class::PreludeVal => "prelude-val",
Class::QuestionMark => "question-mark",
}
}
+
+ /// In case this is an item which can be converted into a link to a definition, it'll contain
+ /// a "span" (a tuple representing `(lo, hi)` equivalent of `Span`).
+ fn get_span(self) -> Option<Span> {
+ match self {
+ Self::Ident(sp) | Self::Self_(sp) => Some(sp),
+ _ => None,
+ }
+ }
}
enum Highlight<'a> {
}
}
-fn get_real_ident_class(text: &str, edition: Edition) -> Class {
- match text {
+/// Classifies into identifier class; returns `None` if this is a non-keyword identifier.
+fn get_real_ident_class(text: &str, edition: Edition, allow_path_keywords: bool) -> Option<Class> {
+ let ignore: &[&str] =
+ if allow_path_keywords { &["self", "Self", "super", "crate"] } else { &["self", "Self"] };
+ if ignore.iter().any(|k| *k == text) {
+ return None;
+ }
+ Some(match text {
"ref" | "mut" => Class::RefKeyWord,
- "self" | "Self" => Class::Self_,
"false" | "true" => Class::Bool,
_ if Symbol::intern(text).is_reserved(|| edition) => Class::KeyWord,
- _ => Class::Ident,
- }
+ _ => return None,
+ })
}
/// Processes program tokens, classifying strings of text by highlighting
in_macro_nonterminal: bool,
edition: Edition,
byte_pos: u32,
+ file_span: Span,
src: &'a str,
}
impl<'a> Classifier<'a> {
- fn new(src: &str, edition: Edition) -> Classifier<'_> {
+ /// Takes as argument the source code to HTML-ify, the rust edition to use and the source code
+ /// file span which will be used later on by the `span_correspondance_map`.
+ fn new(src: &str, edition: Edition, file_span: Span) -> Classifier<'_> {
let tokens = TokenIter { src }.peekable();
Classifier {
tokens,
in_macro_nonterminal: false,
edition,
byte_pos: 0,
+ file_span,
src,
}
}
+ /// Convenient wrapper to create a [`Span`] from a position in the file.
+ fn new_span(&self, lo: u32, text: &str) -> Span {
+ let hi = lo + text.len() as u32;
+ let file_lo = self.file_span.lo();
+ self.file_span.with_lo(file_lo + BytePos(lo)).with_hi(file_lo + BytePos(hi))
+ }
+
/// Concatenate colons and idents as one when possible.
fn get_full_ident_path(&mut self) -> Vec<(TokenKind, usize, usize)> {
let start = self.byte_pos as usize;
if has_ident {
return vec![(TokenKind::Ident, start, pos), (TokenKind::Colon, pos, pos + nb)];
} else {
- return vec![(TokenKind::Colon, pos, pos + nb)];
+ return vec![(TokenKind::Colon, start, pos + nb)];
}
}
- if let Some((Class::Ident, text)) = self.tokens.peek().map(|(token, text)| {
+ if let Some((None, text)) = self.tokens.peek().map(|(token, text)| {
if *token == TokenKind::Ident {
- let class = get_real_ident_class(text, edition);
+ let class = get_real_ident_class(text, edition, true);
(class, text)
} else {
// Doesn't matter which Class we put in here...
- (Class::Comment, text)
+ (Some(Class::Comment), text)
}
}) {
// We only "add" the colon if there is an ident behind.
} else if nb > 0 && has_ident {
return vec![(TokenKind::Ident, start, pos), (TokenKind::Colon, pos, pos + nb)];
} else if nb > 0 {
- return vec![(TokenKind::Colon, pos, pos + nb)];
+ return vec![(TokenKind::Colon, start, start + nb)];
} else if has_ident {
return vec![(TokenKind::Ident, start, pos)];
} else {
}
}
- /// Wraps the tokens iteration to ensure that the byte_pos is always correct.
- fn next(&mut self) -> Option<(TokenKind, &'a str)> {
+ /// Wraps the tokens iteration to ensure that the `byte_pos` is always correct.
+ ///
+ /// It returns the token's kind, the token as a string and its byte position in the source
+ /// string.
+ fn next(&mut self) -> Option<(TokenKind, &'a str, u32)> {
if let Some((kind, text)) = self.tokens.next() {
+ let before = self.byte_pos;
self.byte_pos += text.len() as u32;
- Some((kind, text))
+ Some((kind, text, before))
} else {
None
}
.unwrap_or(false)
{
let tokens = self.get_full_ident_path();
- for (token, start, end) in tokens {
- let text = &self.src[start..end];
- self.advance(token, text, sink);
+ for (token, start, end) in &tokens {
+ let text = &self.src[*start..*end];
+ self.advance(*token, text, sink, *start as u32);
self.byte_pos += text.len() as u32;
}
+ if !tokens.is_empty() {
+ continue;
+ }
}
- if let Some((token, text)) = self.next() {
- self.advance(token, text, sink);
+ if let Some((token, text, before)) = self.next() {
+ self.advance(token, text, sink, before);
} else {
break;
}
}
}
- /// Single step of highlighting. This will classify `token`, but maybe also
- /// a couple of following ones as well.
- fn advance(&mut self, token: TokenKind, text: &'a str, sink: &mut dyn FnMut(Highlight<'a>)) {
+ /// Single step of highlighting. This will classify `token`, but maybe also a couple of
+ /// following ones as well.
+ ///
+ /// `before` is the position of the given token in the `source` string and is used as "lo" byte
+ /// in case we want to try to generate a link for this token using the
+ /// `span_correspondance_map`.
+ fn advance(
+ &mut self,
+ token: TokenKind,
+ text: &'a str,
+ sink: &mut dyn FnMut(Highlight<'a>),
+ before: u32,
+ ) {
let lookahead = self.peek();
let no_highlight = |sink: &mut dyn FnMut(_)| sink(Highlight::Token { text, class: None });
let class = match token {
sink(Highlight::Token { text, class: None });
return;
}
- TokenKind::Ident => match get_real_ident_class(text, self.edition) {
- Class::Ident => match text {
+ TokenKind::Ident => match get_real_ident_class(text, self.edition, false) {
+ None => match text {
"Option" | "Result" => Class::PreludeTy,
"Some" | "None" | "Ok" | "Err" => Class::PreludeVal,
_ if self.in_macro_nonterminal => {
self.in_macro_nonterminal = false;
Class::MacroNonTerminal
}
- _ => Class::Ident,
+ "self" | "Self" => Class::Self_(self.new_span(before, text)),
+ _ => Class::Ident(self.new_span(before, text)),
},
- c => c,
+ Some(c) => c,
},
- TokenKind::RawIdent | TokenKind::UnknownPrefix => Class::Ident,
+ TokenKind::RawIdent | TokenKind::UnknownPrefix => {
+ Class::Ident(self.new_span(before, text))
+ }
TokenKind::Lifetime { .. } => Class::Lifetime,
};
// Anything that didn't return above is the simple case where we the
/// enter_span(Foo), string("text", None), exit_span()
/// string("text", Foo)
/// ```
+///
/// The latter can be thought of as a shorthand for the former, which is more
/// flexible.
-fn string<T: Display>(out: &mut Buffer, text: T, klass: Option<Class>) {
- match klass {
- None => write!(out, "{}", text),
- Some(klass) => write!(out, "<span class=\"{}\">{}</span>", klass.as_html(), text),
+///
+/// Note that if `context` is not `None` and that the given `klass` contains a `Span`, the function
+/// will then try to find this `span` in the `span_correspondance_map`. If found, it'll then
+/// generate a link for this element (which corresponds to where its definition is located).
+fn string<T: Display>(
+ out: &mut Buffer,
+ text: T,
+ klass: Option<Class>,
+ context_info: &Option<ContextInfo<'_, '_, '_>>,
+) {
+ let klass = match klass {
+ None => return write!(out, "{}", text),
+ Some(klass) => klass,
+ };
+ let def_span = match klass.get_span() {
+ Some(d) => d,
+ None => {
+ write!(out, "<span class=\"{}\">{}</span>", klass.as_html(), text);
+ return;
+ }
+ };
+ let mut text_s = text.to_string();
+ if text_s.contains("::") {
+ text_s = text_s.split("::").intersperse("::").fold(String::new(), |mut path, t| {
+ match t {
+ "self" | "Self" => write!(
+ &mut path,
+ "<span class=\"{}\">{}</span>",
+ Class::Self_(DUMMY_SP).as_html(),
+ t
+ ),
+ "crate" | "super" => {
+ write!(&mut path, "<span class=\"{}\">{}</span>", Class::KeyWord.as_html(), t)
+ }
+ t => write!(&mut path, "{}", t),
+ }
+ .expect("Failed to build source HTML path");
+ path
+ });
+ }
+ if let Some(context_info) = context_info {
+ if let Some(href) =
+ context_info.context.shared.span_correspondance_map.get(&def_span).and_then(|href| {
+ let context = context_info.context;
+ // FIXME: later on, it'd be nice to provide two links (if possible) for all items:
+ // one to the documentation page and one to the source definition.
+ // FIXME: currently, external items only generate a link to their documentation,
+ // a link to their definition can be generated using this:
+ // https://github.com/rust-lang/rust/blob/60f1a2fc4b535ead9c85ce085fdce49b1b097531/src/librustdoc/html/render/context.rs#L315-L338
+ match href {
+ LinkFromSrc::Local(span) => context
+ .href_from_span(*span)
+ .map(|s| format!("{}{}", context_info.root_path, s)),
+ LinkFromSrc::External(def_id) => {
+ format::href_with_root_path(*def_id, context, Some(context_info.root_path))
+ .ok()
+ .map(|(url, _, _)| url)
+ }
+ }
+ })
+ {
+ write!(out, "<a class=\"{}\" href=\"{}\">{}</a>", klass.as_html(), href, text_s);
+ return;
+ }
}
+ write!(out, "<span class=\"{}\">{}</span>", klass.as_html(), text_s);
}
#[cfg(test)]
--- /dev/null
+<span class="kw">use</span> <span class="ident"><span class="kw">crate</span>::a::foo</span>;
+<span class="kw">use</span> <span class="ident"><span class="self">self</span>::whatever</span>;
+<span class="kw">let</span> <span class="ident">x</span> <span class="op">=</span> <span class="ident"><span class="kw">super</span>::b::foo</span>;
+<span class="kw">let</span> <span class="ident">y</span> <span class="op">=</span> <span class="ident"><span class="self">Self</span>::whatever</span>;
\ No newline at end of file
<span class="macro">assert!</span>(<span class="self">self</span>.<span class="ident">length</span> <span class="op"><</span> <span class="ident">N</span> <span class="op">&&</span> <span class="ident">index</span> <span class="op"><</span><span class="op">=</span> <span class="self">self</span>.<span class="ident">length</span>);
<span class="ident">::std::env::var</span>(<span class="string">"gateau"</span>).<span class="ident">is_ok</span>();
<span class="attribute">#[<span class="ident">rustfmt::skip</span>]</span>
- <span class="kw">let</span> <span class="ident">s</span>:<span class="ident">std</span><span class="ident">::path::PathBuf</span> <span class="op">=</span> <span class="ident">std::path::PathBuf::new</span>();
+ <span class="kw">let</span> <span class="ident">s</span>:<span class="ident">std::path::PathBuf</span> <span class="op">=</span> <span class="ident">std::path::PathBuf::new</span>();
<span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">s</span> <span class="op">=</span> <span class="ident">String::new</span>();
<span class="kw">match</span> <span class="kw-2">&</span><span class="ident">s</span> {
let src = include_str!("fixtures/sample.rs");
let html = {
let mut out = Buffer::new();
- write_code(&mut out, src, Edition::Edition2018);
+ write_code(&mut out, src, Edition::Edition2018, None);
format!("{}<pre><code>{}</code></pre>\n", STYLE, out.into_inner())
};
expect_file!["fixtures/sample.html"].assert_eq(&html);
println!(\"foo\");\r\n\
}\r\n";
let mut html = Buffer::new();
- write_code(&mut html, src, Edition::Edition2018);
+ write_code(&mut html, src, Edition::Edition2018, None);
expect_file!["fixtures/dos_line.html"].assert_eq(&html.into_inner());
});
}
+
+#[test]
+fn test_keyword_highlight() {
+ create_default_session_globals_then(|| {
+ let src = "use crate::a::foo;
+use self::whatever;
+let x = super::b::foo;
+let y = Self::whatever;";
+
+ let mut html = Buffer::new();
+ write_code(&mut html, src, Edition::Edition2018, None);
+ expect_file!["fixtures/highlight.html"].assert_eq(&html.into_inner());
+ });
+}
tooltip,
edition,
None,
+ None,
);
Some(Event::Html(s.into_inner().into()))
}
name: item.name.unwrap().to_string(),
path: fqp[..fqp.len() - 1].join("::"),
desc,
- parent: Some(did.into()),
+ parent: Some(did),
parent_idx: None,
search_type: get_index_search_type(&item, tcx),
aliases: item.attrs.get_doc_aliases(),
use super::cache::{build_index, ExternalLocation};
use super::print_item::{full_path, item_path, print_item};
use super::write_shared::write_shared;
-use super::{print_sidebar, settings, AllTypes, NameDoc, StylePath, BASIC_KEYWORDS};
+use super::{
+ collect_spans_and_sources, print_sidebar, settings, AllTypes, LinkFromSrc, NameDoc, StylePath,
+ BASIC_KEYWORDS,
+};
use crate::clean;
use crate::clean::ExternalCrate;
pub(crate) current: Vec<String>,
/// The current destination folder of where HTML artifacts should be placed.
/// This changes as the context descends into the module hierarchy.
- pub(super) dst: PathBuf,
+ crate dst: PathBuf,
/// A flag, which when `true`, will render pages which redirect to the
/// real location of an item. This is used to allow external links to
/// publicly reused items to redirect to the right location.
/// Issue for improving the situation: [#82381][]
///
/// [#82381]: https://github.com/rust-lang/rust/issues/82381
- pub(super) shared: Rc<SharedContext<'tcx>>,
+ crate shared: Rc<SharedContext<'tcx>>,
/// The [`Cache`] used during rendering.
///
/// Ideally the cache would be in [`SharedContext`], but it's mutated
/// It's immutable once in `Context`, so it's not as bad that it's not in
/// `SharedContext`.
// FIXME: move `cache` to `SharedContext`
- pub(super) cache: Rc<Cache>,
+ crate cache: Rc<Cache>,
+ /// This flag indicates whether `[src]` links should be generated or not. If
+ /// the source files are present in the html rendering, then this will be
+ /// `true`.
+ crate include_sources: bool,
}
// `Context` is cloned a lot, so we don't want the size to grow unexpectedly.
/// This describes the layout of each page, and is not modified after
/// creation of the context (contains info like the favicon and added html).
crate layout: layout::Layout,
- /// This flag indicates whether `[src]` links should be generated or not. If
- /// the source files are present in the html rendering, then this will be
- /// `true`.
- crate include_sources: bool,
/// The local file sources we've emitted and their respective url-paths.
crate local_sources: FxHashMap<PathBuf, String>,
/// Show the memory layout of types in the docs.
redirections: Option<RefCell<FxHashMap<String, String>>>,
pub(crate) templates: tera::Tera,
+
+ /// Correspondance map used to link types used in the source code pages to allow to click on
+ /// links to jump to the type's definition.
+ crate span_correspondance_map: FxHashMap<rustc_span::Span, LinkFromSrc>,
}
impl SharedContext<'_> {
/// may happen, for example, with externally inlined items where the source
/// of their crate documentation isn't known.
pub(super) fn src_href(&self, item: &clean::Item) -> Option<String> {
- if item.span(self.tcx()).is_dummy() {
+ self.href_from_span(item.span(self.tcx()))
+ }
+
+ crate fn href_from_span(&self, span: clean::Span) -> Option<String> {
+ if span.is_dummy() {
return None;
}
let mut root = self.root_path();
let mut path = String::new();
- let cnum = item.span(self.tcx()).cnum(self.sess());
+ let cnum = span.cnum(self.sess());
// We can safely ignore synthetic `SourceFile`s.
- let file = match item.span(self.tcx()).filename(self.sess()) {
+ let file = match span.filename(self.sess()) {
FileName::Real(ref path) => path.local_path_if_available().to_path_buf(),
_ => return None,
};
(&*symbol, &path)
};
- let loline = item.span(self.tcx()).lo(self.sess()).line;
- let hiline = item.span(self.tcx()).hi(self.sess()).line;
+ let loline = span.lo(self.sess()).line;
+ let hiline = span.hi(self.sess()).line;
let lines =
if loline == hiline { loline.to_string() } else { format!("{}-{}", loline, hiline) };
Some(format!(
const RUN_ON_MODULE: bool = true;
fn init(
- mut krate: clean::Crate,
+ krate: clean::Crate,
options: RenderOptions,
- mut cache: Cache,
+ cache: Cache,
tcx: TyCtxt<'tcx>,
) -> Result<(Self, clean::Crate), Error> {
// need to save a copy of the options for rendering the index page
unstable_features,
generate_redirect_map,
show_type_layout,
+ generate_link_to_definition,
..
} = options;
_ => {}
}
}
+
+ let (mut krate, local_sources, matches) = collect_spans_and_sources(
+ tcx,
+ krate,
+ &src_root,
+ include_sources,
+ generate_link_to_definition,
+ );
+
let (sender, receiver) = channel();
let mut scx = SharedContext {
tcx,
collapsed: krate.collapsed,
src_root,
- include_sources,
- local_sources: Default::default(),
+ local_sources,
issue_tracker_base_url,
layout,
created_dirs: Default::default(),
redirections: if generate_redirect_map { Some(Default::default()) } else { None },
show_type_layout,
templates,
+ span_correspondance_map: matches,
};
// Add the default themes to the `Vec` of stylepaths
let dst = output;
scx.ensure_dir(&dst)?;
- if emit_crate {
- krate = sources::render(&dst, &mut scx, krate)?;
- }
-
- // Build our search index
- let index = build_index(&krate, &mut cache, tcx);
let mut cx = Context {
current: Vec::new(),
id_map: RefCell::new(id_map),
shared: Rc::new(scx),
cache: Rc::new(cache),
+ include_sources,
};
+ if emit_crate {
+ krate = sources::render(&mut cx, krate)?;
+ }
+
+ // Build our search index
+ let index = build_index(&krate, Rc::get_mut(&mut cx.cache).unwrap(), tcx);
+
// Write shared runs within a flock; disable thread dispatching of IO temporarily.
Rc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true);
write_shared(&cx, &krate, index, &md_opts)?;
id_map: RefCell::new(IdMap::new()),
shared: Rc::clone(&self.shared),
cache: Rc::clone(&self.cache),
+ include_sources: self.include_sources,
}
}
mod context;
mod print_item;
+mod span_map;
mod write_shared;
crate use context::*;
+crate use span_map::{collect_spans_and_sources, LinkFromSrc};
use std::collections::VecDeque;
use std::default::Default;
// [src] link in the downstream documentation will actually come back to
// this page, and this link will be auto-clicked. The `id` attribute is
// used to find the link to auto-click.
- if cx.shared.include_sources && !item.is_primitive() {
+ if cx.include_sources && !item.is_primitive() {
write_srclink(cx, item, buf);
}
None,
it.span(cx.tcx()).inner().edition(),
None,
+ None,
);
});
document(w, cx, it, None)
--- /dev/null
+use crate::clean;
+use crate::html::sources;
+
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_hir::{ExprKind, GenericParam, GenericParamKind, HirId, Mod, Node};
+use rustc_middle::ty::TyCtxt;
+use rustc_span::Span;
+
+use std::path::{Path, PathBuf};
+
+/// This enum allows us to store two different kinds of information:
+///
+/// In case the `span` definition comes from the same crate, we can simply get the `span` and use
+/// it as is.
+///
+/// Otherwise, we store the definition `DefId` and will generate a link to the documentation page
+/// instead of the source code directly.
+#[derive(Debug)]
+crate enum LinkFromSrc {
+ Local(clean::Span),
+ External(DefId),
+}
+
+/// This function will do at most two things:
+///
+/// 1. Generate a `span` correspondance map which links an item `span` to its definition `span`.
+/// 2. Collect the source code files.
+///
+/// It returns the `krate`, the source code files and the `span` correspondance map.
+///
+/// Note about the `span` correspondance map: the keys are actually `(lo, hi)` of `span`s. We don't
+/// need the `span` context later on, only their position, so instead of keep a whole `Span`, we
+/// only keep the `lo` and `hi`.
+crate fn collect_spans_and_sources(
+ tcx: TyCtxt<'_>,
+ krate: clean::Crate,
+ src_root: &Path,
+ include_sources: bool,
+ generate_link_to_definition: bool,
+) -> (clean::Crate, FxHashMap<PathBuf, String>, FxHashMap<Span, LinkFromSrc>) {
+ let mut visitor = SpanMapVisitor { tcx, matches: FxHashMap::default() };
+
+ if include_sources {
+ if generate_link_to_definition {
+ intravisit::walk_crate(&mut visitor, tcx.hir().krate());
+ }
+ let (krate, sources) = sources::collect_local_sources(tcx, src_root, krate);
+ (krate, sources, visitor.matches)
+ } else {
+ (krate, Default::default(), Default::default())
+ }
+}
+
+struct SpanMapVisitor<'tcx> {
+ crate tcx: TyCtxt<'tcx>,
+ crate matches: FxHashMap<Span, LinkFromSrc>,
+}
+
+impl<'tcx> SpanMapVisitor<'tcx> {
+ /// This function is where we handle `hir::Path` elements and add them into the "span map".
+ fn handle_path(&mut self, path: &rustc_hir::Path<'_>, path_span: Option<Span>) {
+ let info = match path.res {
+ // FIXME: For now, we only handle `DefKind` if it's not `DefKind::TyParam` or
+ // `DefKind::Macro`. Would be nice to support them too alongside the other `DefKind`
+ // (such as primitive types!).
+ Res::Def(kind, def_id) if kind != DefKind::TyParam => {
+ if matches!(kind, DefKind::Macro(_)) {
+ return;
+ }
+ Some(def_id)
+ }
+ Res::Local(_) => None,
+ Res::Err => return,
+ _ => return,
+ };
+ if let Some(span) = self.tcx.hir().res_span(path.res) {
+ self.matches.insert(
+ path_span.unwrap_or_else(|| path.span),
+ LinkFromSrc::Local(clean::Span::new(span)),
+ );
+ } else if let Some(def_id) = info {
+ self.matches
+ .insert(path_span.unwrap_or_else(|| path.span), LinkFromSrc::External(def_id));
+ }
+ }
+}
+
+impl Visitor<'tcx> for SpanMapVisitor<'tcx> {
+ type Map = rustc_middle::hir::map::Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::All(self.tcx.hir())
+ }
+
+ fn visit_generic_param(&mut self, p: &'tcx GenericParam<'tcx>) {
+ if !matches!(p.kind, GenericParamKind::Type { .. }) {
+ return;
+ }
+ for bound in p.bounds {
+ if let Some(trait_ref) = bound.trait_ref() {
+ self.handle_path(&trait_ref.path, None);
+ }
+ }
+ }
+
+ fn visit_path(&mut self, path: &'tcx rustc_hir::Path<'tcx>, _id: HirId) {
+ self.handle_path(path, None);
+ intravisit::walk_path(self, path);
+ }
+
+ fn visit_mod(&mut self, m: &'tcx Mod<'tcx>, span: Span, id: HirId) {
+ // To make the difference between "mod foo {}" and "mod foo;". In case we "import" another
+ // file, we want to link to it. Otherwise no need to create a link.
+ if !span.overlaps(m.inner) {
+ // Now that we confirmed it's a file import, we want to get the span for the module
+ // name only and not all the "mod foo;".
+ if let Some(node) = self.tcx.hir().find(id) {
+ match node {
+ Node::Item(item) => {
+ self.matches
+ .insert(item.ident.span, LinkFromSrc::Local(clean::Span::new(m.inner)));
+ }
+ _ => {}
+ }
+ }
+ }
+ intravisit::walk_mod(self, m, id);
+ }
+
+ fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) {
+ match expr.kind {
+ ExprKind::MethodCall(segment, method_span, _, _) => {
+ if let Some(hir_id) = segment.hir_id {
+ let hir = self.tcx.hir();
+ let body_id = hir.enclosing_body_owner(hir_id);
+ let typeck_results = self.tcx.sess.with_disabled_diagnostic(|| {
+ self.tcx.typeck_body(
+ hir.maybe_body_owned_by(body_id).expect("a body which isn't a body"),
+ )
+ });
+ if let Some(def_id) = typeck_results.type_dependent_def_id(expr.hir_id) {
+ self.matches.insert(
+ method_span,
+ match hir.span_if_local(def_id) {
+ Some(span) => LinkFromSrc::Local(clean::Span::new(span)),
+ None => LinkFromSrc::External(def_id),
+ },
+ );
+ }
+ }
+ }
+ _ => {}
+ }
+ intravisit::walk_expr(self, expr);
+ }
+
+ fn visit_use(&mut self, path: &'tcx rustc_hir::Path<'tcx>, id: HirId) {
+ self.handle_path(path, None);
+ intravisit::walk_use(self, path, id);
+ }
+}
cx.write_shared(SharedResource::InvocationSpecific { basename: p }, content, &options.emit)
};
+ fn add_background_image_to_css(
+ cx: &Context<'_>,
+ css: &mut String,
+ rule: &str,
+ file: &'static str,
+ ) {
+ css.push_str(&format!(
+ "{} {{ background-image: url({}); }}",
+ rule,
+ SharedResource::ToolchainSpecific { basename: file }
+ .path(cx)
+ .file_name()
+ .unwrap()
+ .to_str()
+ .unwrap()
+ ))
+ }
+
+ // Add all the static files. These may already exist, but we just
+ // overwrite them anyway to make sure that they're fresh and up-to-date.
+ let mut rustdoc_css = static_files::RUSTDOC_CSS.to_owned();
+ add_background_image_to_css(
+ cx,
+ &mut rustdoc_css,
+ "details.undocumented[open] > summary::before, \
+ details.rustdoc-toggle[open] > summary::before, \
+ details.rustdoc-toggle[open] > summary.hideme::before",
+ "toggle-minus.svg",
+ );
+ add_background_image_to_css(
+ cx,
+ &mut rustdoc_css,
+ "details.undocumented > summary::before, details.rustdoc-toggle > summary::before",
+ "toggle-plus.svg",
+ );
+ write_minify("rustdoc.css", &rustdoc_css)?;
+
// Add all the static files. These may already exist, but we just
// overwrite them anyway to make sure that they're fresh and up-to-date.
- write_minify("rustdoc.css", static_files::RUSTDOC_CSS)?;
write_minify("settings.css", static_files::SETTINGS_CSS)?;
write_minify("noscript.css", static_files::NOSCRIPT_CSS)?;
write_toolchain("wheel.svg", static_files::WHEEL_SVG)?;
write_toolchain("clipboard.svg", static_files::CLIPBOARD_SVG)?;
write_toolchain("down-arrow.svg", static_files::DOWN_ARROW_SVG)?;
+ write_toolchain("toggle-minus.svg", static_files::TOGGLE_MINUS_PNG)?;
+ write_toolchain("toggle-plus.svg", static_files::TOGGLE_PLUS_PNG)?;
let mut themes: Vec<&String> = themes.iter().collect();
themes.sort();
write_minify("search.js", static_files::SEARCH_JS)?;
write_minify("settings.js", static_files::SETTINGS_JS)?;
- if cx.shared.include_sources {
+ if cx.include_sources {
write_minify("source-script.js", static_files::sidebar::SOURCE_SCRIPT)?;
}
}
}
- if cx.shared.include_sources {
+ if cx.include_sources {
let mut hierarchy = Hierarchy::new(OsString::new());
for source in cx
.shared
use crate::html::format::Buffer;
use crate::html::highlight;
use crate::html::layout;
-use crate::html::render::{SharedContext, BASIC_KEYWORDS};
+use crate::html::render::{Context, BASIC_KEYWORDS};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::LOCAL_CRATE;
+use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_span::edition::Edition;
use rustc_span::source_map::FileName;
use std::fs;
use std::path::{Component, Path, PathBuf};
-crate fn render(
- dst: &Path,
- scx: &mut SharedContext<'_>,
- krate: clean::Crate,
-) -> Result<clean::Crate, Error> {
+crate fn render(cx: &mut Context<'_>, krate: clean::Crate) -> Result<clean::Crate, Error> {
info!("emitting source files");
- let dst = dst.join("src").join(&*krate.name.as_str());
- scx.ensure_dir(&dst)?;
- let mut folder = SourceCollector { dst, scx };
+ let dst = cx.dst.join("src").join(&*krate.name.as_str());
+ cx.shared.ensure_dir(&dst)?;
+ let mut folder = SourceCollector { dst, cx, emitted_local_sources: FxHashSet::default() };
Ok(folder.fold_crate(krate))
}
+crate fn collect_local_sources<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ src_root: &Path,
+ krate: clean::Crate,
+) -> (clean::Crate, FxHashMap<PathBuf, String>) {
+ let mut lsc = LocalSourcesCollector { tcx, local_sources: FxHashMap::default(), src_root };
+
+ let krate = lsc.fold_crate(krate);
+ (krate, lsc.local_sources)
+}
+
+struct LocalSourcesCollector<'a, 'tcx> {
+ tcx: TyCtxt<'tcx>,
+ local_sources: FxHashMap<PathBuf, String>,
+ src_root: &'a Path,
+}
+
+fn is_real_and_local(span: clean::Span, sess: &Session) -> bool {
+ span.filename(sess).is_real() && span.cnum(sess) == LOCAL_CRATE
+}
+
+impl LocalSourcesCollector<'_, '_> {
+ fn add_local_source(&mut self, item: &clean::Item) {
+ let sess = self.tcx.sess;
+ let span = item.span(self.tcx);
+ // skip all synthetic "files"
+ if !is_real_and_local(span, sess) {
+ return;
+ }
+ let filename = span.filename(sess);
+ let p = match filename {
+ FileName::Real(ref file) => match file.local_path() {
+ Some(p) => p.to_path_buf(),
+ _ => return,
+ },
+ _ => return,
+ };
+ if self.local_sources.contains_key(&*p) {
+ // We've already emitted this source
+ return;
+ }
+
+ let mut href = String::new();
+ clean_path(&self.src_root, &p, false, |component| {
+ href.push_str(&component.to_string_lossy());
+ href.push('/');
+ });
+
+ let src_fname = p.file_name().expect("source has no filename").to_os_string();
+ let mut fname = src_fname.clone();
+ fname.push(".html");
+ href.push_str(&fname.to_string_lossy());
+ self.local_sources.insert(p, href);
+ }
+}
+
+impl DocFolder for LocalSourcesCollector<'_, '_> {
+ fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
+ self.add_local_source(&item);
+
+ // FIXME: if `include_sources` isn't set and DocFolder didn't require consuming the crate by value,
+ // we could return None here without having to walk the rest of the crate.
+ Some(self.fold_item_recur(item))
+ }
+}
+
/// Helper struct to render all source code to HTML pages
struct SourceCollector<'a, 'tcx> {
- scx: &'a mut SharedContext<'tcx>,
+ cx: &'a mut Context<'tcx>,
/// Root destination to place all HTML output into
dst: PathBuf,
+ emitted_local_sources: FxHashSet<PathBuf>,
}
impl DocFolder for SourceCollector<'_, '_> {
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
+ let tcx = self.cx.tcx();
+ let span = item.span(tcx);
+ let sess = tcx.sess;
+
// If we're not rendering sources, there's nothing to do.
// If we're including source files, and we haven't seen this file yet,
// then we need to render it out to the filesystem.
- if self.scx.include_sources
- // skip all synthetic "files"
- && item.span(self.scx.tcx).filename(self.sess()).is_real()
- // skip non-local files
- && item.span(self.scx.tcx).cnum(self.sess()) == LOCAL_CRATE
- {
- let filename = item.span(self.scx.tcx).filename(self.sess());
+ if self.cx.include_sources && is_real_and_local(span, sess) {
+ let filename = span.filename(sess);
+ let span = span.inner();
+ let pos = sess.source_map().lookup_source_file(span.lo());
+ let file_span = span.with_lo(pos.start_pos).with_hi(pos.end_pos);
// If it turns out that we couldn't read this file, then we probably
// can't read any of the files (generating html output from json or
// something like that), so just don't include sources for the
// entire crate. The other option is maintaining this mapping on a
// per-file basis, but that's probably not worth it...
- self.scx.include_sources = match self.emit_source(&filename) {
+ self.cx.include_sources = match self.emit_source(&filename, file_span) {
Ok(()) => true,
Err(e) => {
- self.scx.tcx.sess.span_err(
- item.span(self.scx.tcx).inner(),
+ self.cx.shared.tcx.sess.span_err(
+ span,
&format!(
"failed to render source code for `{}`: {}",
filename.prefer_local(),
- e
+ e,
),
);
false
}
impl SourceCollector<'_, 'tcx> {
- fn sess(&self) -> &'tcx Session {
- &self.scx.tcx.sess
- }
-
/// Renders the given filename into its corresponding HTML source file.
- fn emit_source(&mut self, filename: &FileName) -> Result<(), Error> {
+ fn emit_source(
+ &mut self,
+ filename: &FileName,
+ file_span: rustc_span::Span,
+ ) -> Result<(), Error> {
let p = match *filename {
FileName::Real(ref file) => {
if let Some(local_path) = file.local_path() {
}
_ => return Ok(()),
};
- if self.scx.local_sources.contains_key(&*p) {
+ if self.emitted_local_sources.contains(&*p) {
// We've already emitted this source
return Ok(());
}
// Create the intermediate directories
let mut cur = self.dst.clone();
let mut root_path = String::from("../../");
- let mut href = String::new();
- clean_path(&self.scx.src_root, &p, false, |component| {
+ clean_path(&self.cx.shared.src_root, &p, false, |component| {
cur.push(component);
root_path.push_str("../");
- href.push_str(&component.to_string_lossy());
- href.push('/');
});
- self.scx.ensure_dir(&cur)?;
+
+ self.cx.shared.ensure_dir(&cur)?;
let src_fname = p.file_name().expect("source has no filename").to_os_string();
let mut fname = src_fname.clone();
fname.push(".html");
cur.push(&fname);
- href.push_str(&fname.to_string_lossy());
let title = format!("{} - source", src_fname.to_string_lossy());
let desc = format!("Source of the Rust file `{}`.", filename.prefer_remapped());
title: &title,
css_class: "source",
root_path: &root_path,
- static_root_path: self.scx.static_root_path.as_deref(),
+ static_root_path: self.cx.shared.static_root_path.as_deref(),
description: &desc,
keywords: BASIC_KEYWORDS,
- resource_suffix: &self.scx.resource_suffix,
- extra_scripts: &[&format!("source-files{}", self.scx.resource_suffix)],
- static_extra_scripts: &[&format!("source-script{}", self.scx.resource_suffix)],
+ resource_suffix: &self.cx.shared.resource_suffix,
+ extra_scripts: &[&format!("source-files{}", self.cx.shared.resource_suffix)],
+ static_extra_scripts: &[&format!("source-script{}", self.cx.shared.resource_suffix)],
};
let v = layout::render(
- &self.scx.templates,
- &self.scx.layout,
+ &self.cx.shared.templates,
+ &self.cx.shared.layout,
&page,
"",
- |buf: &mut _| print_src(buf, contents, self.scx.edition()),
- &self.scx.style_files,
+ |buf: &mut _| {
+ print_src(buf, contents, self.cx.shared.edition(), file_span, &self.cx, &root_path)
+ },
+ &self.cx.shared.style_files,
);
- self.scx.fs.write(&cur, v.as_bytes())?;
- self.scx.local_sources.insert(p, href);
+ self.cx.shared.fs.write(&cur, v.as_bytes())?;
+ self.emitted_local_sources.insert(p);
Ok(())
}
}
/// Wrapper struct to render the source code of a file. This will do things like
/// adding line numbers to the left-hand side.
-fn print_src(buf: &mut Buffer, s: &str, edition: Edition) {
+fn print_src(
+ buf: &mut Buffer,
+ s: &str,
+ edition: Edition,
+ file_span: rustc_span::Span,
+ context: &Context<'_>,
+ root_path: &str,
+) {
let lines = s.lines().count();
let mut line_numbers = Buffer::empty_from(buf);
let mut cols = 0;
writeln!(line_numbers, "<span id=\"{0}\">{0:1$}</span>", i, cols);
}
line_numbers.write_str("</pre>");
- highlight::render_with_highlighting(s, buf, None, None, None, edition, Some(line_numbers));
+ highlight::render_with_highlighting(
+ s,
+ buf,
+ None,
+ None,
+ None,
+ edition,
+ Some(line_numbers),
+ Some(highlight::ContextInfo { context, file_span, root_path }),
+ );
}
border-bottom-left-radius: 5px;
}
+.example-wrap > pre.rust a:hover {
+ text-decoration: underline;
+}
+
.rustdoc:not(.source) .example-wrap > pre:not(.line-number) {
width: 100%;
overflow-x: auto;
}
details.rustdoc-toggle > summary::before {
- content: "[+]";
- font-weight: 300;
- font-size: 0.8em;
- letter-spacing: 1px;
+ content: "";
cursor: pointer;
+ width: 17px;
+ height: max(17px, 1.1em);
+ background-repeat: no-repeat;
+ background-position: top left;
+ display: inline-block;
+ vertical-align: middle;
+ opacity: .5;
+}
+
+/* Screen readers see the text version at the end the line.
+ Visual readers see the icon at the start of the line, but small and transparent. */
+details.rustdoc-toggle > summary::after {
+ content: "Expand";
+ overflow: hidden;
+ width: 0;
+ height: 0;
+ position: absolute;
+}
+
+details.rustdoc-toggle > summary.hideme::after {
+ /* "hideme" toggles already have a description when they're contracted */
+ content: "";
+}
+
+details.rustdoc-toggle > summary:focus::before,
+details.rustdoc-toggle > summary:hover::before {
+ opacity: 1;
}
details.rustdoc-toggle.top-doc > summary,
display: none;
}
-details.rustdoc-toggle[open] > summary::before {
- content: "[−]";
- display: inline;
+details.rustdoc-toggle[open] > summary::before,
+details.rustdoc-toggle[open] > summary.hideme::before {
+ width: 17px;
+ height: max(17px, 1.1em);
+ background-repeat: no-repeat;
+ background-position: top left;
+ display: inline-block;
+ content: "";
+}
+
+details.rustdoc-toggle[open] > summary::after,
+details.rustdoc-toggle[open] > summary.hideme::after {
+ content: "Collapse";
}
details.undocumented > summary::before {
- content: "[+] Show hidden undocumented items";
+ padding-left: 17px;
+ height: max(17px, 1.1em);
+ background-repeat: no-repeat;
+ background-position: top left;
+ content: "Show hidden undocumented items";
cursor: pointer;
font-size: 16px;
font-weight: 300;
+ opacity: .5;
+}
+
+details.undocumented > summary:focus::before,
+details.undocumented > summary:hover::before {
+ opacity: 1;
}
details.undocumented[open] > summary::before {
- content: "[−] Hide undocumented items";
+ padding-left: 17px;
+ height: max(17px, 1.1em);
+ background-repeat: no-repeat
+ background-position: top left;
+ content: "Hide undocumented items";
}
/* Media Queries */
color: #999;
}
+details.rustdoc-toggle > summary::before,
+details.undocumented > summary::before {
+ filter: invert(100%);
+}
+
#crate-search {
color: #c5c5c5;
background-color: #141920;
color: #999;
}
+details.rustdoc-toggle > summary::before,
+details.undocumented > summary::before {
+ filter: invert(100%);
+}
+
#crate-search {
color: #111;
background-color: #f0f0f0;
--- /dev/null
+<svg width="17" height="17" shape-rendering="crispEdges" stroke="#000" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5 2.5H2.5v12H5m7-12h2.5v12H12M5 8.5h7"/></svg>
\ No newline at end of file
--- /dev/null
+<svg width="17" height="17" shape-rendering="crispEdges" stroke="#000" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5 2.5H2.5v12H5m7-12h2.5v12H12M5 8.5h7M8.5 12V8.625v0V5"/></svg>
\ No newline at end of file
/// The file contents of `down-arrow.svg`, the icon used for the crate choice combobox.
crate static DOWN_ARROW_SVG: &[u8] = include_bytes!("static/images/down-arrow.svg");
+/// The file contents of `toggle-minus.svg`, the icon used for opened toggles.
+crate static TOGGLE_MINUS_PNG: &[u8] = include_bytes!("static/images/toggle-minus.svg");
+
+/// The file contents of `toggle-plus.svg`, the icon used for closed toggles.
+crate static TOGGLE_PLUS_PNG: &[u8] = include_bytes!("static/images/toggle-plus.svg");
+
/// The contents of `COPYRIGHT.txt`, the license listing for files distributed with documentation
/// output.
crate static COPYRIGHT: &[u8] = include_bytes!("static/COPYRIGHT.txt");
unstable("nocapture", |o| {
o.optflag("", "nocapture", "Don't capture stdout and stderr of tests")
}),
+ unstable("generate-link-to-definition", |o| {
+ o.optflag(
+ "",
+ "generate-link-to-definition",
+ "Make the identifiers in the HTML source code pages navigable",
+ )
+ }),
]
}
) -> Result<(Res, Option<String>), ErrorKind<'path>> {
let tcx = self.cx.tcx;
let no_res = || ResolutionFailure::NotResolved {
- module_id: module_id.into(),
+ module_id: module_id,
partial_res: None,
unresolved: path_str.into(),
};
// but the disambiguator logic expects the associated item.
// Store the kind in a side channel so that only the disambiguator logic looks at it.
if let Some((kind, id)) = side_channel {
- self.kind_side_channel.set(Some((kind, id.into())));
+ self.kind_side_channel.set(Some((kind, id)));
}
Ok((res, Some(fragment)))
};
item.def_id.expect_def_id().as_local().map(|src_id| (src_id, dst_id))
})
{
- let hir_src = self.cx.tcx.hir().local_def_id_to_hir_id(src_id);
- let hir_dst = self.cx.tcx.hir().local_def_id_to_hir_id(dst_id);
-
- if self.cx.tcx.privacy_access_levels(()).is_exported(hir_src)
- && !self.cx.tcx.privacy_access_levels(()).is_exported(hir_dst)
+ if self.cx.tcx.privacy_access_levels(()).is_exported(src_id)
+ && !self.cx.tcx.privacy_access_levels(()).is_exported(dst_id)
{
privacy_error(self.cx, &diag_info, &path_str);
}
// doesn't allow statements like `use str::trim;`, making this a (hopefully)
// valid omission. See https://github.com/rust-lang/rust/pull/80660#discussion_r551585677
// for discussion on the matter.
- verify(kind, id.into())?;
+ verify(kind, id)?;
// FIXME: it would be nice to check that the feature gate was enabled in the original crate, not just ignore it altogether.
// However I'm not sure how to check that across crates.
Some(ItemLink { link: ori_link.link, link_text, did: None, fragment })
}
Res::Def(kind, id) => {
- verify(kind, id.into())?;
+ verify(kind, id)?;
let id = clean::register_res(self.cx, rustc_hir::def::Res::Def(kind, id));
- Some(ItemLink { link: ori_link.link, link_text, did: Some(id.into()), fragment })
+ Some(ItemLink { link: ori_link.link, link_text, did: Some(id), fragment })
}
}
}
name = start;
for ns in [TypeNS, ValueNS, MacroNS] {
if let Some(res) =
- collector.check_full_res(ns, &start, module_id.into(), &None)
+ collector.check_full_res(ns, &start, module_id, &None)
{
debug!("found partial_res={:?}", res);
*partial_res = Some(res);
// FIXME(eddyb) is this `doc(hidden)` check needed?
if !cx.tcx.get_attrs(def_id).lists(sym::doc).has_word(sym::hidden) {
- let impls = get_auto_trait_and_blanket_impls(cx, def_id.into());
+ let impls = get_auto_trait_and_blanket_impls(cx, def_id);
new_items.extend(impls.filter(|i| cx.inlined.insert(i.def_id)));
}
});
} else {
// All items need to be handled here in case someone wishes to link
// to them with intra-doc links
- self.cx.cache.access_levels.map.insert(did.into(), AccessLevel::Public);
+ self.cx.cache.access_levels.map.insert(did, AccessLevel::Public);
}
}
}
None => return false,
};
- let is_private = !self.cx.cache.access_levels.is_public(res_did.into());
+ let is_private = !self.cx.cache.access_levels.is_public(res_did);
let is_hidden = inherits_doc_hidden(self.cx.tcx, res_hir_id);
// Only inline if requested or if the item would otherwise be stripped.
#![crate_type = "lib"]
#![feature(c_variadic)]
-#![feature(unwind_attributes)]
+#![feature(c_unwind)]
#![no_std]
use core::ffi::VaList;
fn foreign_c_variadic_1(_: VaList, ...);
}
-#[unwind(aborts)] // FIXME(#58794)
pub unsafe extern "C" fn use_foreign_c_variadic_0() {
// Ensure that we correctly call foreign C-variadic functions.
// CHECK: call void (i32, ...) @foreign_c_variadic_0([[PARAM:i32( signext)?]] 0)
// Ensure that we do not remove the `va_list` passed to the foreign function when
// removing the "spoofed" `VaListImpl` that is used by Rust defined C-variadics.
-#[unwind(aborts)] // FIXME(#58794)
pub unsafe extern "C" fn use_foreign_c_variadic_1_0(ap: VaList) {
// CHECK: call void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap)
foreign_c_variadic_1(ap);
}
-#[unwind(aborts)] // FIXME(#58794)
pub unsafe extern "C" fn use_foreign_c_variadic_1_1(ap: VaList) {
// CHECK: call void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 42)
foreign_c_variadic_1(ap, 42i32);
}
-#[unwind(aborts)] // FIXME(#58794)
pub unsafe extern "C" fn use_foreign_c_variadic_1_2(ap: VaList) {
// CHECK: call void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 2, [[PARAM]] 42)
foreign_c_variadic_1(ap, 2i32, 42i32);
}
-#[unwind(aborts)] // FIXME(#58794)
pub unsafe extern "C" fn use_foreign_c_variadic_1_3(ap: VaList) {
// CHECK: call void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 2, [[PARAM]] 42, [[PARAM]] 0)
foreign_c_variadic_1(ap, 2i32, 42i32, 0i32);
// ignore-riscv64 FIXME
#![crate_type = "lib"]
+#![feature(c_unwind)]
extern "C" {
fn bar();
// We need a function which is normally called through the PLT.
extern "C" {
- // CHECK: Function Attrs: nounwind nonlazybind
+ // CHECK: Function Attrs:{{.*}}nonlazybind
fn getenv(name: *const u8) -> *mut u8;
}
+++ /dev/null
-// compile-flags: -C panic=abort -O
-
-#![crate_type = "lib"]
-#![feature(unwind_attributes, core_intrinsics)]
-
-extern "C" {
- #[unwind(allow)]
- fn bar(data: *mut u8);
-}
-extern "Rust" {
- fn catch(data: *mut u8, exception: *mut u8);
-}
-
-// CHECK-LABEL: @foo
-#[no_mangle]
-pub unsafe fn foo() -> i32 {
- // CHECK: call void @bar
- // CHECK: ret i32 0
- std::intrinsics::r#try(|x| bar(x), 0 as *mut u8, |x, y| catch(x, y))
-}
-// compile-flags: -C panic=abort -C opt-level=0
+// compile-flags: -C panic=abort
-// Test that `nounwind` atributes are applied to `C-unwind` extern functions when the
-// code is compiled with `panic=abort`. We disable optimizations above to prevent LLVM from
-// inferring the attribute.
+// Test that `nounwind` atributes are not applied to `C-unwind` extern functions
+// even when the code is compiled with `panic=abort`.
#![crate_type = "lib"]
#![feature(c_unwind)]
-// CHECK: @rust_item_that_can_unwind() unnamed_addr #0 {
+extern "C-unwind" {
+ fn may_unwind();
+}
+
+// CHECK: @rust_item_that_can_unwind() unnamed_addr #0
#[no_mangle]
-pub extern "C-unwind" fn rust_item_that_can_unwind() {
+pub unsafe extern "C-unwind" fn rust_item_that_can_unwind() {
+ may_unwind();
}
// Now, make sure that the LLVM attributes for this functions are correct. First, make
// sure that the first item is correctly marked with the `nounwind` attribute:
//
-// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} }
+// CHECK-NOT: attributes #0 = { {{.*}}nounwind{{.*}} }
--- /dev/null
+// compile-flags: -C opt-level=0 -Cpanic=abort
+// ignore-wasm32-bare compiled with panic=abort by default
+
+#![crate_type = "lib"]
+
+// We disable optimizations to prevent LLVM from infering the attribute.
+
+// CHECK: Function Attrs:{{.*}}nounwind
+// CHECK-NEXT: @foo
+#[no_mangle]
+pub extern "C" fn foo() {}
+
+// CHECK: Function Attrs:{{.*}}nounwind
+// CHECK-NEXT: @bar
+#[no_mangle]
+pub fn bar() {}
--- /dev/null
+// compile-flags: -C opt-level=0
+// ignore-wasm32-bare compiled with panic=abort by default
+
+#![crate_type = "lib"]
+
+// We disable optimizations to prevent LLVM from infering the attribute.
+
+extern "C" {
+ fn bar();
+}
+
+// CHECK-NOT: Function Attrs:{{.*}}nounwind
+pub unsafe extern "C" fn foo() {
+ bar();
+}
+
+// Note that this test will get removed when `C-unwind` is fully stabilized
--- /dev/null
+// compile-flags: -C opt-level=0 -Cpanic=abort
+// ignore-wasm32-bare compiled with panic=abort by default
+
+#![crate_type = "lib"]
+#![feature(c_unwind)]
+
+// We disable optimizations to prevent LLVM from infering the attribute.
+
+// CHECK: Function Attrs:{{.*}}nounwind
+// CHECK-NEXT: @foo
+#[no_mangle]
+pub extern "C" fn foo() {}
+
+// CHECK: Function Attrs:{{.*}}nounwind
+// CHECK-NEXT: @bar
+#[no_mangle]
+pub fn bar() {}
--- /dev/null
+// compile-flags: -C panic=abort
+
+#![crate_type = "lib"]
+#![feature(c_unwind)]
+
+extern "C-unwind" {
+ fn bar();
+}
+
+// CHECK: Function Attrs:{{.*}}nounwind
+// CHECK-NEXT: define{{.*}}void @foo
+// CHECK: call void @llvm.trap()
+#[no_mangle]
+pub unsafe extern "C" fn foo() {
+ bar();
+}
// ignore-wasm32-bare compiled with panic=abort by default
#![crate_type = "lib"]
-#![feature(unwind_attributes)]
+#![feature(c_unwind)]
// Make sure these all do *not* get the attribute.
// We disable optimizations to prevent LLVM from infering the attribute.
// CHECK-NOT: nounwind
// "C" ABI
-// pub extern fn foo() {} // FIXME right now we don't abort-on-panic but add `nounwind` nevertheless
-#[unwind(allowed)]
-pub extern "C" fn foo_allowed() {}
+pub extern "C-unwind" fn foo_unwind() {}
// "Rust"
// (`extern "Rust"` could be removed as all `fn` get it implicitly; we leave it in for clarity.)
-pub extern "Rust" fn bar() {}
-#[unwind(allowed)]
-pub extern "Rust" fn bar_allowed() {}
+pub fn bar() {}
// ignore-wasm32-bare compiled with panic=abort by default
#![crate_type = "lib"]
-#![feature(unwind_attributes)]
+#![feature(c_unwind)]
extern "C" {
-// CHECK: Function Attrs:{{.*}}nounwind
-// CHECK-NEXT: declare{{.*}}void @extern_fn
+ // CHECK: Function Attrs:{{.*}}nounwind
+ // CHECK-NEXT: declare{{.*}}void @extern_fn
fn extern_fn();
-// CHECK-NOT: Function Attrs:{{.*}}nounwind
-// CHECK: declare{{.*}}void @unwinding_extern_fn
- #[unwind(allowed)]
- fn unwinding_extern_fn();
-// CHECK-NOT: nounwind
-// CHECK: declare{{.*}}void @aborting_extern_fn
- #[unwind(aborts)]
- fn aborting_extern_fn(); // FIXME: we want to have the attribute here
}
-extern "Rust" {
-// CHECK-NOT: nounwind
-// CHECK: declare{{.*}}void @rust_extern_fn
- fn rust_extern_fn();
-// CHECK-NOT: nounwind
-// CHECK: declare{{.*}}void @rust_unwinding_extern_fn
- #[unwind(allowed)]
- fn rust_unwinding_extern_fn();
-// CHECK-NOT: nounwind
-// CHECK: declare{{.*}}void @rust_aborting_extern_fn
- #[unwind(aborts)]
- fn rust_aborting_extern_fn(); // FIXME: we want to have the attribute here
+extern "C-unwind" {
+ // CHECK-NOT: nounwind
+ // CHECK: declare{{.*}}void @c_unwind_extern_fn
+ fn c_unwind_extern_fn();
}
pub unsafe fn force_declare() {
extern_fn();
- unwinding_extern_fn();
- aborting_extern_fn();
- rust_extern_fn();
- rust_unwinding_extern_fn();
- rust_aborting_extern_fn();
+ c_unwind_extern_fn();
}
+ // MIR for `main` after SimplifyCfg-early-opt
fn main() -> () {
- let mut _0: (); // return place in scope 0 at $DIR/simplify_cfg.rs:5:11: 5:11
- let mut _1: (); // in scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2
- let mut _2: bool; // in scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17
- let mut _3: !; // in scope 0 at $DIR/simplify_cfg.rs:7:18: 9:10
+ let mut _0: (); // return place in scope 0 at $DIR/simplify_cfg.rs:7:11: 7:11
+ let mut _1: (); // in scope 0 at $DIR/simplify_cfg.rs:7:1: 13:2
+ let mut _2: bool; // in scope 0 at $DIR/simplify_cfg.rs:9:12: 9:17
+ let mut _3: !; // in scope 0 at $DIR/simplify_cfg.rs:9:18: 11:10
bb0: {
-- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6
+- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:8:5: 12:6
- }
-
- bb1: {
- StorageLive(_2); // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17
-- _2 = bar() -> bb2; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17
-+ _2 = bar() -> bb1; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17
+ StorageLive(_2); // scope 0 at $DIR/simplify_cfg.rs:9:12: 9:17
+- _2 = bar() -> [return: bb2, unwind: bb5]; // scope 0 at $DIR/simplify_cfg.rs:9:12: 9:17
++ _2 = bar() -> [return: bb1, unwind: bb4]; // scope 0 at $DIR/simplify_cfg.rs:9:12: 9:17
// mir::Constant
- // + span: $DIR/simplify_cfg.rs:7:12: 7:15
+ // + span: $DIR/simplify_cfg.rs:9:12: 9:15
// + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar(<ZST>)) }
}
- bb2: {
-- switchInt(move _2) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10
+- switchInt(move _2) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/simplify_cfg.rs:9:9: 11:10
+ bb1: {
-+ switchInt(move _2) -> [false: bb3, otherwise: bb2]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10
++ switchInt(move _2) -> [false: bb3, otherwise: bb2]; // scope 0 at $DIR/simplify_cfg.rs:9:9: 11:10
}
- bb3: {
+ bb2: {
- _0 = const (); // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18
- StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:9:9: 9:10
- return; // scope 0 at $DIR/simplify_cfg.rs:11:2: 11:2
+ _0 = const (); // scope 0 at $DIR/simplify_cfg.rs:10:13: 10:18
+ StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:11:9: 11:10
+ return; // scope 0 at $DIR/simplify_cfg.rs:13:2: 13:2
}
- bb4: {
+ bb3: {
- _1 = const (); // scope 0 at $DIR/simplify_cfg.rs:9:10: 9:10
- StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:9:9: 9:10
- goto -> bb0; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6
-- }
--
+ _1 = const (); // scope 0 at $DIR/simplify_cfg.rs:11:10: 11:10
+ StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:11:9: 11:10
+ goto -> bb0; // scope 0 at $DIR/simplify_cfg.rs:8:5: 12:6
+ }
+
- bb5 (cleanup): {
-- resume; // scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2
++ bb4 (cleanup): {
+ resume; // scope 0 at $DIR/simplify_cfg.rs:7:1: 13:2
}
}
+ // MIR for `main` after SimplifyCfg-initial
fn main() -> () {
- let mut _0: (); // return place in scope 0 at $DIR/simplify_cfg.rs:5:11: 5:11
- let mut _1: (); // in scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2
- let mut _2: bool; // in scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17
- let mut _3: !; // in scope 0 at $DIR/simplify_cfg.rs:7:18: 9:10
+ let mut _0: (); // return place in scope 0 at $DIR/simplify_cfg.rs:7:11: 7:11
+ let mut _1: (); // in scope 0 at $DIR/simplify_cfg.rs:7:1: 13:2
+ let mut _2: bool; // in scope 0 at $DIR/simplify_cfg.rs:9:12: 9:17
+ let mut _3: !; // in scope 0 at $DIR/simplify_cfg.rs:9:18: 11:10
bb0: {
-- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6
-+ falseUnwind -> [real: bb1, cleanup: bb5]; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6
+- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:8:5: 12:6
++ falseUnwind -> [real: bb1, cleanup: bb5]; // scope 0 at $DIR/simplify_cfg.rs:8:5: 12:6
}
bb1: {
-- falseUnwind -> [real: bb2, cleanup: bb10]; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6
+- falseUnwind -> [real: bb2, cleanup: bb10]; // scope 0 at $DIR/simplify_cfg.rs:8:5: 12:6
- }
-
- bb2: {
- StorageLive(_2); // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17
-- _2 = bar() -> [return: bb3, unwind: bb10]; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17
-+ _2 = bar() -> [return: bb2, unwind: bb5]; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17
+ StorageLive(_2); // scope 0 at $DIR/simplify_cfg.rs:9:12: 9:17
+- _2 = bar() -> [return: bb3, unwind: bb10]; // scope 0 at $DIR/simplify_cfg.rs:9:12: 9:17
++ _2 = bar() -> [return: bb2, unwind: bb5]; // scope 0 at $DIR/simplify_cfg.rs:9:12: 9:17
// mir::Constant
- // + span: $DIR/simplify_cfg.rs:7:12: 7:15
+ // + span: $DIR/simplify_cfg.rs:9:12: 9:15
// + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar(<ZST>)) }
}
- bb3: {
-- switchInt(move _2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10
+- switchInt(move _2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/simplify_cfg.rs:9:9: 11:10
+ bb2: {
-+ switchInt(move _2) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10
++ switchInt(move _2) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/simplify_cfg.rs:9:9: 11:10
}
- bb4: {
+ bb3: {
- _0 = const (); // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18
-- goto -> bb9; // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18
-+ StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:9:9: 9:10
-+ return; // scope 0 at $DIR/simplify_cfg.rs:11:2: 11:2
+ _0 = const (); // scope 0 at $DIR/simplify_cfg.rs:10:13: 10:18
+- goto -> bb9; // scope 0 at $DIR/simplify_cfg.rs:10:13: 10:18
++ StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:11:9: 11:10
++ return; // scope 0 at $DIR/simplify_cfg.rs:13:2: 13:2
}
- bb5: {
+ bb4: {
- _1 = const (); // scope 0 at $DIR/simplify_cfg.rs:9:10: 9:10
-- goto -> bb8; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10
+ _1 = const (); // scope 0 at $DIR/simplify_cfg.rs:11:10: 11:10
+- goto -> bb8; // scope 0 at $DIR/simplify_cfg.rs:9:9: 11:10
- }
-
- bb6: {
-- unreachable; // scope 0 at $DIR/simplify_cfg.rs:7:18: 9:10
+- unreachable; // scope 0 at $DIR/simplify_cfg.rs:9:18: 11:10
- }
-
- bb7: {
-- goto -> bb8; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10
+- goto -> bb8; // scope 0 at $DIR/simplify_cfg.rs:9:9: 11:10
- }
-
- bb8: {
- StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:9:9: 9:10
-- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6
-+ goto -> bb0; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6
+ StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:11:9: 11:10
+- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:8:5: 12:6
++ goto -> bb0; // scope 0 at $DIR/simplify_cfg.rs:8:5: 12:6
}
- bb9: {
-- StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:9:9: 9:10
-- return; // scope 0 at $DIR/simplify_cfg.rs:11:2: 11:2
+- StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:11:9: 11:10
+- return; // scope 0 at $DIR/simplify_cfg.rs:13:2: 13:2
- }
-
- bb10 (cleanup): {
+ bb5 (cleanup): {
- resume; // scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2
+ resume; // scope 0 at $DIR/simplify_cfg.rs:7:1: 13:2
}
}
// Test that the goto chain starting from bb0 is collapsed.
+// compile-flags: -Cpanic=abort
+// no-prefer-dynamic
// EMIT_MIR simplify_cfg.main.SimplifyCfg-initial.diff
// EMIT_MIR simplify_cfg.main.SimplifyCfg-early-opt.diff
- 1| |#![feature(unwind_attributes)]
+ 1| |#![feature(c_unwind)]
2| |#![allow(unused_assignments)]
3| |
- 4| |#[unwind(aborts)]
- 5| 12|fn might_abort(should_abort: bool) {
- 6| 12| if should_abort {
- 7| 0| println!("aborting...");
- 8| 0| panic!("panics and aborts");
- 9| 12| } else {
- 10| 12| println!("Don't Panic");
- 11| 12| }
- 12| 12|}
- 13| |
- 14| 1|fn main() -> Result<(), u8> {
- 15| 1| let mut countdown = 10;
- 16| 11| while countdown > 0 {
- 17| 10| if countdown < 5 {
- 18| 4| might_abort(false);
- 19| 6| }
- 20| | // See discussion (below the `Notes` section) on coverage results for the closing brace.
- 21| 10| if countdown < 5 { might_abort(false); } // Counts for different regions on one line.
+ 4| 12|extern "C" fn might_abort(should_abort: bool) {
+ 5| 12| if should_abort {
+ 6| 0| println!("aborting...");
+ 7| 0| panic!("panics and aborts");
+ 8| 12| } else {
+ 9| 12| println!("Don't Panic");
+ 10| 12| }
+ 11| 12|}
+ 12| |
+ 13| 1|fn main() -> Result<(), u8> {
+ 14| 1| let mut countdown = 10;
+ 15| 11| while countdown > 0 {
+ 16| 10| if countdown < 5 {
+ 17| 4| might_abort(false);
+ 18| 6| }
+ 19| | // See discussion (below the `Notes` section) on coverage results for the closing brace.
+ 20| 10| if countdown < 5 { might_abort(false); } // Counts for different regions on one line.
^4 ^6
- 22| | // For the following example, the closing brace is the last character on the line.
- 23| | // This shows the character after the closing brace is highlighted, even if that next
- 24| | // character is a newline.
- 25| 10| if countdown < 5 { might_abort(false); }
+ 21| | // For the following example, the closing brace is the last character on the line.
+ 22| | // This shows the character after the closing brace is highlighted, even if that next
+ 23| | // character is a newline.
+ 24| 10| if countdown < 5 { might_abort(false); }
^4 ^6
- 26| 10| countdown -= 1;
- 27| | }
- 28| 1| Ok(())
- 29| 1|}
- 30| |
- 31| |// Notes:
- 32| |// 1. Compare this program and its coverage results to those of the similar tests
- 33| |// `panic_unwind.rs` and `try_error_result.rs`.
- 34| |// 2. This test confirms the coverage generated when a program includes `TerminatorKind::Abort`.
- 35| |// 3. The test does not invoke the abort. By executing to a successful completion, the coverage
- 36| |// results show where the program did and did not execute.
- 37| |// 4. If the program actually aborted, the coverage counters would not be saved (which "works as
- 38| |// intended"). Coverage results would show no executed coverage regions.
- 39| |// 6. If `should_abort` is `true` and the program aborts, the program exits with a `132` status
- 40| |// (on Linux at least).
- 41| |
- 42| |/*
- 43| |
- 44| |Expect the following coverage results:
- 45| |
- 46| |```text
- 47| | 16| 11| while countdown > 0 {
- 48| | 17| 10| if countdown < 5 {
- 49| | 18| 4| might_abort(false);
- 50| | 19| 6| }
- 51| |```
- 52| |
- 53| |This is actually correct.
- 54| |
- 55| |The condition `countdown < 5` executed 10 times (10 loop iterations).
- 56| |
- 57| |It evaluated to `true` 4 times, and executed the `might_abort()` call.
- 58| |
- 59| |It skipped the body of the `might_abort()` call 6 times. If an `if` does not include an explicit
- 60| |`else`, the coverage implementation injects a counter, at the character immediately after the `if`s
- 61| |closing brace, to count the "implicit" `else`. This is the only way to capture the coverage of the
- 62| |non-true condition.
- 63| |
- 64| |As another example of why this is important, say the condition was `countdown < 50`, which is always
- 65| |`true`. In that case, we wouldn't have a test for what happens if `might_abort()` is not called.
- 66| |The closing brace would have a count of `0`, highlighting the missed coverage.
- 67| |*/
+ 25| 10| countdown -= 1;
+ 26| | }
+ 27| 1| Ok(())
+ 28| 1|}
+ 29| |
+ 30| |// Notes:
+ 31| |// 1. Compare this program and its coverage results to those of the similar tests
+ 32| |// `panic_unwind.rs` and `try_error_result.rs`.
+ 33| |// 2. This test confirms the coverage generated when a program includes `TerminatorKind::Abort`.
+ 34| |// 3. The test does not invoke the abort. By executing to a successful completion, the coverage
+ 35| |// results show where the program did and did not execute.
+ 36| |// 4. If the program actually aborted, the coverage counters would not be saved (which "works as
+ 37| |// intended"). Coverage results would show no executed coverage regions.
+ 38| |// 6. If `should_abort` is `true` and the program aborts, the program exits with a `132` status
+ 39| |// (on Linux at least).
+ 40| |
+ 41| |/*
+ 42| |
+ 43| |Expect the following coverage results:
+ 44| |
+ 45| |```text
+ 46| | 16| 11| while countdown > 0 {
+ 47| | 17| 10| if countdown < 5 {
+ 48| | 18| 4| might_abort(false);
+ 49| | 19| 6| }
+ 50| |```
+ 51| |
+ 52| |This is actually correct.
+ 53| |
+ 54| |The condition `countdown < 5` executed 10 times (10 loop iterations).
+ 55| |
+ 56| |It evaluated to `true` 4 times, and executed the `might_abort()` call.
+ 57| |
+ 58| |It skipped the body of the `might_abort()` call 6 times. If an `if` does not include an explicit
+ 59| |`else`, the coverage implementation injects a counter, at the character immediately after the `if`s
+ 60| |closing brace, to count the "implicit" `else`. This is the only way to capture the coverage of the
+ 61| |non-true condition.
+ 62| |
+ 63| |As another example of why this is important, say the condition was `countdown < 50`, which is always
+ 64| |`true`. In that case, we wouldn't have a test for what happens if `might_abort()` is not called.
+ 65| |The closing brace would have a count of `0`, highlighting the missed coverage.
+ 66| |*/
18| 2| println!("BOOM times {}!!!", self.strength);
19| 2| }
------------------
- | <generics::Firework<i32> as core::ops::drop::Drop>::drop:
+ | <generics::Firework<f64> as core::ops::drop::Drop>::drop:
| 17| 1| fn drop(&mut self) {
| 18| 1| println!("BOOM times {}!!!", self.strength);
| 19| 1| }
------------------
- | <generics::Firework<f64> as core::ops::drop::Drop>::drop:
+ | <generics::Firework<i32> as core::ops::drop::Drop>::drop:
| 17| 1| fn drop(&mut self) {
| 18| 1| println!("BOOM times {}!!!", self.strength);
| 19| 1| }
18| 2| println!("used_only_from_bin_crate_generic_function with {:?}", arg);
19| 2|}
------------------
- | used_crate::used_only_from_bin_crate_generic_function::<&str>:
+ | used_crate::used_only_from_bin_crate_generic_function::<&alloc::vec::Vec<i32>>:
| 17| 1|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) {
| 18| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg);
| 19| 1|}
------------------
- | used_crate::used_only_from_bin_crate_generic_function::<&alloc::vec::Vec<i32>>:
+ | used_crate::used_only_from_bin_crate_generic_function::<&str>:
| 17| 1|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) {
| 18| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg);
| 19| 1|}
-#![feature(unwind_attributes)]
+#![feature(c_unwind)]
#![allow(unused_assignments)]
-#[unwind(aborts)]
-fn might_abort(should_abort: bool) {
+extern "C" fn might_abort(should_abort: bool) {
if should_abort {
println!("aborting...");
panic!("panics and aborts");
// For linking libstdc++ on MinGW
#![cfg_attr(all(windows, target_env = "gnu"), feature(static_nobundle))]
-#![feature(unwind_attributes)]
+#![feature(c_unwind)]
use std::panic::{catch_unwind, AssertUnwindSafe};
extern "C" {
fn test_cxx_exception();
+}
- #[unwind(allowed)]
- fn cxx_catch_callback(cb: extern "C" fn(), ok: *mut bool);
+extern "C-unwind" {
+ fn cxx_catch_callback(cb: extern "C-unwind" fn(), ok: *mut bool);
}
#[no_mangle]
-#[unwind(allowed)]
-extern "C" fn rust_catch_callback(cb: extern "C" fn(), rust_ok: &mut bool) {
+extern "C-unwind" fn rust_catch_callback(cb: extern "C-unwind" fn(), rust_ok: &mut bool) {
let _drop = DropCheck(rust_ok);
cb();
unreachable!("should have unwound instead of returned");
}
fn test_rust_panic() {
- #[unwind(allowed)]
- extern "C" fn callback() {
+ extern "C-unwind" fn callback() {
println!("throwing rust panic");
panic!(1234i32);
}
--- /dev/null
+// This test purpose is to check that the "--generate-link-to-definition"
+// option can only be used on nightly.
+
+// compile-flags: --generate-link-to-definition
+
+pub fn f() {}
--- /dev/null
+error: the `-Z unstable-options` flag must also be passed to enable the flag `generate-link-to-definition`
+
--- /dev/null
+// This test purpose is to check that the "--generate-link-to-definition"
+// option can only be used with HTML generation.
+
+// compile-flags: -Zunstable-options --generate-link-to-definition --output-format json
+
+pub fn f() {}
--- /dev/null
+error: --generate-link-to-definition option can only be used with HTML output format
+
--- /dev/null
+// This test purpose is to check that the "--generate-link-to-definition"
+// option can only be used with HTML generation.
+
+// compile-flags: -Zunstable-options --generate-link-to-definition --show-coverage
+
+pub fn f() {}
--- /dev/null
+error: --generate-link-to-definition option can only be used with HTML output format
+
--- /dev/null
+//! just some other file. :)
+
+use crate::Foo;
+
+pub struct Bar {
+ field: Foo,
+}
+
+pub struct Bar2 {
+ field: crate::Foo,
+}
+
+pub mod sub {
+ pub trait Trait {
+ fn tadam() {}
+ }
+}
--- /dev/null
+pub struct SourceCode;
--- /dev/null
+// compile-flags: -Zunstable-options --generate-link-to-definition
+// aux-build:source_code.rs
+// build-aux-docs
+
+#![crate_name = "foo"]
+
+extern crate source_code;
+
+// @has 'src/foo/check-source-code-urls-to-def.rs.html'
+
+// @has - '//a[@href="../../src/foo/auxiliary/source-code-bar.rs.html#1-17"]' 'bar'
+#[path = "auxiliary/source-code-bar.rs"]
+pub mod bar;
+
+// @count - '//a[@href="../../src/foo/auxiliary/source-code-bar.rs.html#5-7"]' 4
+use bar::Bar;
+// @has - '//a[@href="../../src/foo/auxiliary/source-code-bar.rs.html#13-17"]' 'self'
+// @has - '//a[@href="../../src/foo/auxiliary/source-code-bar.rs.html#14-16"]' 'Trait'
+use bar::sub::{self, Trait};
+
+pub struct Foo;
+
+impl Foo {
+ fn hello(&self) {}
+}
+
+fn babar() {}
+
+// @has - '//a/@href' '/struct.String.html'
+// @count - '//a[@href="../../src/foo/check-source-code-urls-to-def.rs.html#21"]' 5
+// @has - '//a[@href="../../source_code/struct.SourceCode.html"]' 'source_code::SourceCode'
+pub fn foo(a: u32, b: &str, c: String, d: Foo, e: bar::Bar, f: source_code::SourceCode) {
+ let x = 12;
+ let y: Foo = Foo;
+ let z: Bar = bar::Bar { field: Foo };
+ babar();
+ // @has - '//a[@href="../../src/foo/check-source-code-urls-to-def.rs.html#24"]' 'hello'
+ y.hello();
+}
+
+// @has - '//a[@href="../../src/foo/auxiliary/source-code-bar.rs.html#14-16"]' 'bar::sub::Trait'
+// @has - '//a[@href="../../src/foo/auxiliary/source-code-bar.rs.html#14-16"]' 'Trait'
+pub fn foo2<T: bar::sub::Trait, V: Trait>(t: &T, v: &V) {
+}
--- /dev/null
+// check-pass
+// only-x86_64
+#![feature(asm)]
+#![feature(naked_functions)]
+#![crate_type = "lib"]
+
+#[naked]
+pub extern "C" fn naked(p: char) -> u128 {
+ //~^ WARN uses type `char`
+ //~| WARN uses type `u128`
+ unsafe { asm!("", options(noreturn)); }
+}
--- /dev/null
+warning: `extern` fn uses type `char`, which is not FFI-safe
+ --> $DIR/naked-functions-ffi.rs:8:28
+ |
+LL | pub extern "C" fn naked(p: char) -> u128 {
+ | ^^^^ not FFI-safe
+ |
+ = note: `#[warn(improper_ctypes_definitions)]` on by default
+ = help: consider using `u32` or `libc::wchar_t` instead
+ = note: the `char` type has no C equivalent
+
+warning: `extern` fn uses type `u128`, which is not FFI-safe
+ --> $DIR/naked-functions-ffi.rs:8:37
+ |
+LL | pub extern "C" fn naked(p: char) -> u128 {
+ | ^^^^ not FFI-safe
+ |
+ = note: 128-bit integers don't currently have a known stable ABI
+
+warning: 2 warnings emitted
+
--- /dev/null
+// only-x86_64
+#![deny(unused)]
+#![feature(asm)]
+#![feature(naked_functions)]
+#![crate_type = "lib"]
+
+pub trait Trait {
+ extern "sysv64" fn trait_associated(a: usize, b: usize) -> usize;
+ extern "sysv64" fn trait_method(&self, a: usize, b: usize) -> usize;
+}
+
+pub mod normal {
+ pub extern "sysv64" fn function(a: usize, b: usize) -> usize {
+ //~^ ERROR unused variable: `a`
+ //~| ERROR unused variable: `b`
+ unsafe { asm!("", options(noreturn)); }
+ }
+
+ pub struct Normal;
+
+ impl Normal {
+ pub extern "sysv64" fn associated(a: usize, b: usize) -> usize {
+ //~^ ERROR unused variable: `a`
+ //~| ERROR unused variable: `b`
+ unsafe { asm!("", options(noreturn)); }
+ }
+
+ pub extern "sysv64" fn method(&self, a: usize, b: usize) -> usize {
+ //~^ ERROR unused variable: `a`
+ //~| ERROR unused variable: `b`
+ unsafe { asm!("", options(noreturn)); }
+ }
+ }
+
+ impl super::Trait for Normal {
+ extern "sysv64" fn trait_associated(a: usize, b: usize) -> usize {
+ //~^ ERROR unused variable: `a`
+ //~| ERROR unused variable: `b`
+ unsafe { asm!("", options(noreturn)); }
+ }
+
+ extern "sysv64" fn trait_method(&self, a: usize, b: usize) -> usize {
+ //~^ ERROR unused variable: `a`
+ //~| ERROR unused variable: `b`
+ unsafe { asm!("", options(noreturn)); }
+ }
+ }
+}
+
+pub mod naked {
+ #[naked]
+ pub extern "sysv64" fn function(a: usize, b: usize) -> usize {
+ unsafe { asm!("", options(noreturn)); }
+ }
+
+ pub struct Naked;
+
+ impl Naked {
+ #[naked]
+ pub extern "sysv64" fn associated(a: usize, b: usize) -> usize {
+ unsafe { asm!("", options(noreturn)); }
+ }
+
+ #[naked]
+ pub extern "sysv64" fn method(&self, a: usize, b: usize) -> usize {
+ unsafe { asm!("", options(noreturn)); }
+ }
+ }
+
+ impl super::Trait for Naked {
+ #[naked]
+ extern "sysv64" fn trait_associated(a: usize, b: usize) -> usize {
+ unsafe { asm!("", options(noreturn)); }
+ }
+
+ #[naked]
+ extern "sysv64" fn trait_method(&self, a: usize, b: usize) -> usize {
+ unsafe { asm!("", options(noreturn)); }
+ }
+ }
+}
--- /dev/null
+error: unused variable: `a`
+ --> $DIR/naked-functions-unused.rs:13:37
+ |
+LL | pub extern "sysv64" fn function(a: usize, b: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: the lint level is defined here
+ --> $DIR/naked-functions-unused.rs:2:9
+ |
+LL | #![deny(unused)]
+ | ^^^^^^
+ = note: `#[deny(unused_variables)]` implied by `#[deny(unused)]`
+
+error: unused variable: `b`
+ --> $DIR/naked-functions-unused.rs:13:47
+ |
+LL | pub extern "sysv64" fn function(a: usize, b: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_b`
+
+error: unused variable: `a`
+ --> $DIR/naked-functions-unused.rs:22:43
+ |
+LL | pub extern "sysv64" fn associated(a: usize, b: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+
+error: unused variable: `b`
+ --> $DIR/naked-functions-unused.rs:22:53
+ |
+LL | pub extern "sysv64" fn associated(a: usize, b: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_b`
+
+error: unused variable: `a`
+ --> $DIR/naked-functions-unused.rs:28:46
+ |
+LL | pub extern "sysv64" fn method(&self, a: usize, b: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+
+error: unused variable: `b`
+ --> $DIR/naked-functions-unused.rs:28:56
+ |
+LL | pub extern "sysv64" fn method(&self, a: usize, b: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_b`
+
+error: unused variable: `a`
+ --> $DIR/naked-functions-unused.rs:36:45
+ |
+LL | extern "sysv64" fn trait_associated(a: usize, b: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+
+error: unused variable: `b`
+ --> $DIR/naked-functions-unused.rs:36:55
+ |
+LL | extern "sysv64" fn trait_associated(a: usize, b: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_b`
+
+error: unused variable: `a`
+ --> $DIR/naked-functions-unused.rs:42:48
+ |
+LL | extern "sysv64" fn trait_method(&self, a: usize, b: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+
+error: unused variable: `b`
+ --> $DIR/naked-functions-unused.rs:42:58
+ |
+LL | extern "sysv64" fn trait_method(&self, a: usize, b: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_b`
+
+error: aborting due to 10 previous errors
+
pub unsafe extern "C" fn valid_att_syntax() {
asm!("", options(noreturn, att_syntax));
}
+
+#[naked]
+pub unsafe extern "C" fn inline_none() {
+ asm!("", options(noreturn));
+}
+
+#[naked]
+#[inline]
+//~^ WARN naked functions cannot be inlined
+//~| WARN this was previously accepted
+pub unsafe extern "C" fn inline_hint() {
+ asm!("", options(noreturn));
+}
+
+#[naked]
+#[inline(always)]
+//~^ WARN naked functions cannot be inlined
+//~| WARN this was previously accepted
+pub unsafe extern "C" fn inline_always() {
+ asm!("", options(noreturn));
+}
+
+#[naked]
+#[inline(never)]
+//~^ WARN naked functions cannot be inlined
+//~| WARN this was previously accepted
+pub unsafe extern "C" fn inline_never() {
+ asm!("", options(noreturn));
+}
+
+#[naked]
+#[inline]
+//~^ WARN naked functions cannot be inlined
+//~| WARN this was previously accepted
+#[inline(always)]
+//~^ WARN naked functions cannot be inlined
+//~| WARN this was previously accepted
+#[inline(never)]
+//~^ WARN naked functions cannot be inlined
+//~| WARN this was previously accepted
+pub unsafe extern "C" fn inline_all() {
+ asm!("", options(noreturn));
+}
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408>
-error: aborting due to 8 previous errors; 19 warnings emitted
+warning: naked functions cannot be inlined
+ --> $DIR/naked-functions.rs:177:1
+ |
+LL | #[inline]
+ | ^^^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408>
+
+warning: naked functions cannot be inlined
+ --> $DIR/naked-functions.rs:185:1
+ |
+LL | #[inline(always)]
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408>
+
+warning: naked functions cannot be inlined
+ --> $DIR/naked-functions.rs:193:1
+ |
+LL | #[inline(never)]
+ | ^^^^^^^^^^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408>
+
+warning: naked functions cannot be inlined
+ --> $DIR/naked-functions.rs:201:1
+ |
+LL | #[inline]
+ | ^^^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408>
+
+warning: naked functions cannot be inlined
+ --> $DIR/naked-functions.rs:204:1
+ |
+LL | #[inline(always)]
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408>
+
+warning: naked functions cannot be inlined
+ --> $DIR/naked-functions.rs:207:1
+ |
+LL | #[inline(never)]
+ | ^^^^^^^^^^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408>
+
+error: aborting due to 8 previous errors; 25 warnings emitted
|
LL | type C: Clone + Iterator<Item: Send + Iterator<Item: for<'a> Lam<&'a u8, App: Debug>> + Sync>;
| ^^^^ `<<Self as Case1>::C as Iterator>::Item` cannot be sent between threads safely
- |
+ |
::: $SRC_DIR/core/src/marker.rs:LL:COL
|
LL | pub unsafe auto trait Send {
|
LL | type C: Clone + Iterator<Item: Send + Iterator<Item: for<'a> Lam<&'a u8, App: Debug>> + Sync>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `<<Self as Case1>::C as Iterator>::Item` is not an iterator
- |
+ |
::: $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | pub trait Iterator {
|
LL | type C: Clone + Iterator<Item: Send + Iterator<Item: for<'a> Lam<&'a u8, App: Debug>> + Sync>;
| ^^^^ `<<Self as Case1>::C as Iterator>::Item` cannot be shared between threads safely
- |
+ |
::: $SRC_DIR/core/src/marker.rs:LL:COL
|
LL | pub unsafe auto trait Sync {
|
LL | type A: Iterator<Item: Debug>;
| ^^^^^ `<<Self as Case1>::A as Iterator>::Item` cannot be formatted using `{:?}` because it doesn't implement `Debug`
- |
+ |
::: $SRC_DIR/core/src/fmt/mod.rs:LL:COL
|
LL | pub trait Debug {
|
LL | pub trait Foo { type Out: Baz<Assoc: Default>; }
| ^^^^^^^ the trait `Default` is not implemented for `<<Self as Foo>::Out as Baz>::Assoc`
- |
+ |
::: $SRC_DIR/core/src/default.rs:LL:COL
|
LL | pub trait Default: Sized {
|
LL | type Ty = Vec<[u8]>;
| ^^^^^^^^^ doesn't have a size known at compile-time
- |
+ |
::: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
|
LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
|
LL | trait ArithmeticOps: Add<Output=Self> + Sub<Output=Self> + Mul<Output=Self> + Div<Output=Self> {}
| ^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
- |
+ |
::: $SRC_DIR/core/src/ops/arith.rs:LL:COL
|
LL | pub trait Add<Rhs = Self> {
error[E0308]: mismatched types
--> $DIR/dont-suggest-missing-await.rs:14:18
|
-LL | async fn make_u32() -> u32 {
- | --- checked the `Output` of this `async fn`, found opaque type
-...
LL | take_u32(x)
| ^ expected `u32`, found opaque type
|
- = note: while checking the return type of the `async fn`
+note: while checking the return type of the `async fn`
+ --> $DIR/dont-suggest-missing-await.rs:7:24
+ |
+LL | async fn make_u32() -> u32 {
+ | ^^^ checked the `Output` of this `async fn`, found opaque type
= note: expected type `u32`
found opaque type `impl Future`
help: consider `await`ing on the `Future`
error[E0308]: mismatched types
--> $DIR/generator-desc.rs:12:16
|
-LL | async fn one() {}
- | - checked the `Output` of this `async fn`, expected opaque type
-LL | async fn two() {}
- | - checked the `Output` of this `async fn`, found opaque type
-...
LL | fun(one(), two());
| ^^^^^ expected opaque type, found a different opaque type
|
- = note: while checking the return type of the `async fn`
- = note: while checking the return type of the `async fn`
+note: while checking the return type of the `async fn`
+ --> $DIR/generator-desc.rs:5:16
+ |
+LL | async fn one() {}
+ | ^ checked the `Output` of this `async fn`, expected opaque type
+note: while checking the return type of the `async fn`
+ --> $DIR/generator-desc.rs:6:16
+ |
+LL | async fn two() {}
+ | ^ checked the `Output` of this `async fn`, found opaque type
= note: expected opaque type `impl Future` (opaque type at <$DIR/generator-desc.rs:5:16>)
found opaque type `impl Future` (opaque type at <$DIR/generator-desc.rs:6:16>)
= help: consider `await`ing on both `Future`s
| -- ^^^^^^^^^^^^^^^ expected `async` closure body, found a different `async` closure body
| |
| the expected `async` closure body
- |
+ |
::: $SRC_DIR/core/src/future/mod.rs:LL:COL
|
LL | pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
async fn tuple() -> Tuple {
//~^ NOTE checked the `Output` of this `async fn`, expected opaque type
+ //~| NOTE while checking the return type of the `async fn`
+ //~| NOTE in this expansion of desugaring of `async` block or function
Tuple(1i32)
}
Tuple(_) => {} //~ ERROR mismatched types
//~^ NOTE expected opaque type, found struct `Tuple`
//~| NOTE expected opaque type `impl Future`
- //~| NOTE while checking the return type of the `async fn`
}
}
| ^^^^^^
error[E0277]: the `?` operator can only be applied to values that implement `Try`
- --> $DIR/issue-61076.rs:65:5
+ --> $DIR/issue-61076.rs:67:5
|
LL | t?;
| ^^ the `?` operator cannot be applied to type `T`
| ^^^^^^
error[E0609]: no field `0` on type `impl Future`
- --> $DIR/issue-61076.rs:76:26
+ --> $DIR/issue-61076.rs:78:26
|
LL | let _: i32 = tuple().0;
| ^ field not available in `impl Future`, but it is available in its `Output`
| ^^^^^^
error[E0609]: no field `a` on type `impl Future`
- --> $DIR/issue-61076.rs:80:28
+ --> $DIR/issue-61076.rs:82:28
|
LL | let _: i32 = struct_().a;
| ^ field not available in `impl Future`, but it is available in its `Output`
| ^^^^^^
error[E0599]: no method named `method` found for opaque type `impl Future` in the current scope
- --> $DIR/issue-61076.rs:84:15
+ --> $DIR/issue-61076.rs:86:15
|
LL | struct_().method();
| ^^^^^^ method not found in `impl Future`
| ^^^^^^
error[E0308]: mismatched types
- --> $DIR/issue-61076.rs:92:9
+ --> $DIR/issue-61076.rs:94:9
|
-LL | async fn tuple() -> Tuple {
- | ----- checked the `Output` of this `async fn`, expected opaque type
-...
LL | Tuple(_) => {}
| ^^^^^^^^ expected opaque type, found struct `Tuple`
|
- = note: while checking the return type of the `async fn`
+note: while checking the return type of the `async fn`
+ --> $DIR/issue-61076.rs:58:21
+ |
+LL | async fn tuple() -> Tuple {
+ | ^^^^^ checked the `Output` of this `async fn`, expected opaque type
= note: expected opaque type `impl Future`
found struct `Tuple`
help: consider `await`ing on the `Future`
|
LL | let mut f = File::open(path.to_str())?;
| ^^^^^^^^^^^^^ the trait `AsRef<Path>` is not implemented for `Option<&str>`
- |
+ |
::: $SRC_DIR/std/src/fs.rs:LL:COL
|
LL | pub fn open<P: AsRef<Path>>(path: P) -> io::Result<File> {
...
LL | g(issue_67893::run())
| ^ `MutexGuard<'_, ()>` cannot be sent between threads safely
- |
+ |
::: $DIR/auxiliary/issue_67893.rs:7:20
|
LL | pub async fn run() {
...
LL | self.sleep.poll(cx)
| ^^^^ method not found in `Sleep`
- |
+ |
::: $SRC_DIR/core/src/future/future.rs:LL:COL
|
LL | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
error[E0308]: mismatched types
--> $DIR/suggest-missing-await-closure.rs:16:18
|
-LL | async fn make_u32() -> u32 {
- | --- checked the `Output` of this `async fn`, found opaque type
-...
LL | take_u32(x)
| ^ expected `u32`, found opaque type
|
- = note: while checking the return type of the `async fn`
+note: while checking the return type of the `async fn`
+ --> $DIR/suggest-missing-await-closure.rs:8:24
+ |
+LL | async fn make_u32() -> u32 {
+ | ^^^ checked the `Output` of this `async fn`, found opaque type
= note: expected type `u32`
found opaque type `impl Future`
help: consider `await`ing on the `Future`
error[E0308]: mismatched types
--> $DIR/suggest-missing-await.rs:12:14
|
-LL | async fn make_u32() -> u32 {
- | --- checked the `Output` of this `async fn`, found opaque type
-...
LL | take_u32(x)
| ^ expected `u32`, found opaque type
|
- = note: while checking the return type of the `async fn`
+note: while checking the return type of the `async fn`
+ --> $DIR/suggest-missing-await.rs:5:24
+ |
+LL | async fn make_u32() -> u32 {
+ | ^^^ checked the `Output` of this `async fn`, found opaque type
= note: expected type `u32`
found opaque type `impl Future`
help: consider `await`ing on the `Future`
error[E0308]: mismatched types
--> $DIR/suggest-missing-await.rs:22:5
|
-LL | async fn dummy() {}
- | - checked the `Output` of this `async fn`, found opaque type
-...
LL | dummy()
| ^^^^^^^ expected `()`, found opaque type
|
- = note: while checking the return type of the `async fn`
+note: while checking the return type of the `async fn`
+ --> $DIR/suggest-missing-await.rs:18:18
+ |
+LL | async fn dummy() {}
+ | ^ checked the `Output` of this `async fn`, found opaque type
= note: expected unit type `()`
found opaque type `impl Future`
help: consider `await`ing on the `Future`
--> $DIR/key-value-non-ascii.rs:3:19
|
LL | #[rustc_dummy = b"ffi.rs"]
- | ^
- | |
- | byte constant must be ASCII
- | help: use a \xHH escape for a non-ASCII byte: `\xFB03`
+ | ^ byte constant must be ASCII
+ |
+help: if you meant to use the UTF-8 encoding of 'ffi', use \xHH escapes
+ |
+LL | #[rustc_dummy = b"/xEF/xAC/x83.rs"]
+ | ^^^^^^^^^^^^
error: aborting due to previous error
| |
| move occurs because `D` has type `A`, which does not implement the `Copy` trait
| help: consider borrowing here: `&$c`
- |
+ |
::: $DIR/move-error-snippets.rs:21:1
|
LL | sss!();
|
LL | const SIZE: usize = core::mem::size_of::<Self>();
| ^^^^ doesn't have a size known at compile-time
- |
+ |
::: $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | pub const fn size_of<T>() -> usize {
|
LL | const SIZE: usize = core::mem::size_of::<Self>();
| ^^^^ doesn't have a size known at compile-time
- |
+ |
::: $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | pub const fn size_of<T>() -> usize {
|
LL | const SIZE: usize = core::mem::size_of::<Self>();
| ^^^^ doesn't have a size known at compile-time
- |
+ |
::: $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | pub const fn size_of<T>() -> usize {
|
LL | const SIZE: usize = core::mem::size_of::<Self>();
| ^^^^ doesn't have a size known at compile-time
- |
+ |
::: $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | pub const fn size_of<T>() -> usize {
|
LL | const SIZE: usize = core::mem::size_of::<Self>();
| ^^^^ doesn't have a size known at compile-time
- |
+ |
::: $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | pub const fn size_of<T>() -> usize {
|
LL | impl <T:Sync+'static> RequiresRequiresShareAndSend for X<T> { }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `T` cannot be sent between threads safely
- |
+ |
::: $DIR/auxiliary/trait_superkinds_in_metadata.rs:7:58
|
LL | pub trait RequiresRequiresShareAndSend : RequiresShare + Send { }
|
LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
| ^
- |
+ |
::: $SRC_DIR/core/src/ops/function.rs:LL:COL
|
LL | pub trait Fn<Args>: FnMut<Args> {
|
LL | let t = thread::spawn(|| {
| ^^^^^^^^^^^^^ `std::sync::mpsc::Receiver<()>` cannot be shared between threads safely
- |
+ |
::: $SRC_DIR/std/src/thread/mod.rs:LL:COL
|
LL | F: Send + 'static,
|
LL | thread::spawn(|| tx.send(()).unwrap());
| ^^^^^^^^^^^^^ `Sender<()>` cannot be shared between threads safely
- |
+ |
::: $SRC_DIR/std/src/thread/mod.rs:LL:COL
|
LL | F: Send + 'static,
| - this type parameter needs to be `std::marker::Sized`
LL | if std::mem::size_of::<T>() == 0 {
| ^ doesn't have a size known at compile-time
- |
+ |
::: $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | pub const fn size_of<T>() -> usize {
|
LL | let _ = const_evaluatable_lib::test1::<T>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
+ |
::: $DIR/auxiliary/const_evaluatable_lib.rs:6:10
|
LL | [u8; std::mem::size_of::<T>() - 1]: Sized,
|
LL | let _ = const_evaluatable_lib::test1::<T>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
+ |
::: $DIR/auxiliary/const_evaluatable_lib.rs:4:27
|
LL | pub fn test1<T>() -> [u8; std::mem::size_of::<T>() - 1]
|
LL | let _ = const_evaluatable_lib::test1::<T>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
+ |
::: $DIR/auxiliary/const_evaluatable_lib.rs:6:10
|
LL | [u8; std::mem::size_of::<T>() - 1]: Sized,
|
LL | let _ = const_evaluatable_lib::test1::<T>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
+ |
::: $DIR/auxiliary/const_evaluatable_lib.rs:4:27
|
LL | pub fn test1<T>() -> [u8; std::mem::size_of::<T>() - 1]
| |
| memory access failed: alloc7 has size 4, so pointer to 4 bytes starting at offset 4 is out-of-bounds
| inside `copy_nonoverlapping::<u32>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL
- |
+ |
::: $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
LL | copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
| --------------------------------------------- inside `std::ptr::read::<u32>` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL
- |
+ |
::: $DIR/out_of_bounds_read.rs:13:33
|
LL | const _READ: u32 = unsafe { ptr::read(PAST_END_PTR) };
| |
| memory access failed: alloc7 has size 4, so pointer to 4 bytes starting at offset 4 is out-of-bounds
| inside `copy_nonoverlapping::<u32>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL
- |
+ |
::: $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
LL | copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
| --------------------------------------------- inside `std::ptr::read::<u32>` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL
- |
+ |
::: $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { read(self) }
| ---------- inside `ptr::const_ptr::<impl *const u32>::read` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
- |
+ |
::: $DIR/out_of_bounds_read.rs:14:39
|
LL | const _CONST_READ: u32 = unsafe { PAST_END_PTR.read() };
| |
| memory access failed: alloc7 has size 4, so pointer to 4 bytes starting at offset 4 is out-of-bounds
| inside `copy_nonoverlapping::<u32>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL
- |
+ |
::: $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
LL | copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
| --------------------------------------------- inside `std::ptr::read::<u32>` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL
- |
+ |
::: $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL
|
LL | unsafe { read(self) }
| ---------- inside `ptr::mut_ptr::<impl *mut u32>::read` at $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL
- |
+ |
::: $DIR/out_of_bounds_read.rs:15:37
|
LL | const _MUT_READ: u32 = unsafe { (PAST_END_PTR as *mut u32).read() };
--- /dev/null
+// Test for the behavior described in <https://github.com/rust-lang/rust/issues/87184>.
+#![feature(const_mut_refs, const_raw_ptr_deref)]
+
+const PARTIAL_OVERWRITE: () = {
+ let mut p = &42;
+ unsafe {
+ let ptr: *mut _ = &mut p;
+ *(ptr as *mut u8) = 123; //~ ERROR any use of this value
+ //~| unable to overwrite parts of a pointer
+ //~| WARN previously accepted
+ }
+ let x = *p;
+};
+
+fn main() {}
--- /dev/null
+error: any use of this value will cause an error
+ --> $DIR/partial_ptr_overwrite.rs:8:9
+ |
+LL | / const PARTIAL_OVERWRITE: () = {
+LL | | let mut p = &42;
+LL | | unsafe {
+LL | | let ptr: *mut _ = &mut p;
+LL | | *(ptr as *mut u8) = 123;
+ | | ^^^^^^^^^^^^^^^^^^^^^^^ unable to overwrite parts of a pointer in memory at alloc4
+... |
+LL | | let x = *p;
+LL | | };
+ | |__-
+ |
+ = note: `#[deny(const_err)]` on by default
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
+
+error: aborting due to previous error
+
-#![feature(unwind_attributes, const_panic)]
+#![feature(c_unwind, const_panic, const_extern_fn)]
-#[unwind(aborts)]
-const fn foo() {
+const extern "C" fn foo() {
panic!() //~ ERROR evaluation of constant value failed
}
const _: () = foo();
-// Ensure that the CTFE engine handles calls to `#[unwind(aborts)]` gracefully
+// Ensure that the CTFE engine handles calls to `extern "C"` aborting gracefully
fn main() {
let _ = foo();
error[E0080]: evaluation of constant value failed
- --> $DIR/unwind-abort.rs:5:5
+ --> $DIR/unwind-abort.rs:4:5
|
LL | panic!()
| ^^^^^^^^
| |
- | the evaluated program panicked at 'explicit panic', $DIR/unwind-abort.rs:5:5
+ | the evaluated program panicked at 'explicit panic', $DIR/unwind-abort.rs:4:5
| inside `foo` at $SRC_DIR/std/src/panic.rs:LL:COL
...
LL | const _: () = foo();
- | ----- inside `_` at $DIR/unwind-abort.rs:8:15
+ | ----- inside `_` at $DIR/unwind-abort.rs:7:15
|
= note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
| |
| entering unreachable code
| inside `unreachable_unchecked` at $SRC_DIR/core/src/hint.rs:LL:COL
- |
+ |
::: $DIR/const_unsafe_unreachable_ub.rs:7:18
|
LL | false => std::hint::unreachable_unchecked(),
| |
| calling non-const function `<Vec<i32> as Drop>::drop`
| inside `std::ptr::drop_in_place::<Vec<i32>> - shim(Some(Vec<i32>))` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL
- |
+ |
::: $DIR/drop.rs:18:1
|
LL | };
| |
| 0x2a is not a valid pointer
| inside `ptr::const_ptr::<impl *const u8>::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
- |
+ |
::: $DIR/offset_from_ub.rs:24:14
|
LL | unsafe { (42 as *const u8).offset_from(&5u8) as usize }
| |
| overflowing in-bounds pointer arithmetic
| inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
- |
+ |
::: $DIR/offset_ub.rs:8:46
|
LL | pub const BEFORE_START: *const u8 = unsafe { (&0u8 as *const u8).offset(-1) };
| |
| pointer arithmetic failed: allocN has size 1, so pointer to 2 bytes starting at offset 0 is out-of-bounds
| inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
- |
+ |
::: $DIR/offset_ub.rs:9:43
|
LL | pub const AFTER_END: *const u8 = unsafe { (&0u8 as *const u8).offset(2) };
| |
| pointer arithmetic failed: allocN has size 100, so pointer to 101 bytes starting at offset 0 is out-of-bounds
| inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
- |
+ |
::: $DIR/offset_ub.rs:10:45
|
LL | pub const AFTER_ARRAY: *const u8 = unsafe { [0u8; 100].as_ptr().offset(101) };
| |
| overflowing in-bounds pointer arithmetic
| inside `ptr::const_ptr::<impl *const u16>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
- |
+ |
::: $DIR/offset_ub.rs:12:43
|
LL | pub const OVERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MAX) };
| |
| overflowing in-bounds pointer arithmetic
| inside `ptr::const_ptr::<impl *const u16>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
- |
+ |
::: $DIR/offset_ub.rs:13:44
|
LL | pub const UNDERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MIN) };
| |
| overflowing in-bounds pointer arithmetic
| inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
- |
+ |
::: $DIR/offset_ub.rs:14:56
|
LL | pub const OVERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (usize::MAX as *const u8).offset(2) };
| |
| overflowing in-bounds pointer arithmetic
| inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
- |
+ |
::: $DIR/offset_ub.rs:15:57
|
LL | pub const UNDERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (1 as *const u8).offset(-2) };
| |
| pointer arithmetic failed: allocN has size 1, so pointer to 2 bytes starting at offset -4 is out-of-bounds
| inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
- |
+ |
::: $DIR/offset_ub.rs:16:49
|
LL | pub const NEGATIVE_OFFSET: *const u8 = unsafe { [0u8; 1].as_ptr().wrapping_offset(-2).offset(-2) };
| |
| pointer arithmetic failed: allocN has size 0, so pointer to 1 byte starting at offset 0 is out-of-bounds
| inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
- |
+ |
::: $DIR/offset_ub.rs:18:50
|
LL | pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) };
| |
| 0x1 is not a valid pointer
| inside `ptr::mut_ptr::<impl *mut u8>::offset` at $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL
- |
+ |
::: $DIR/offset_ub.rs:19:42
|
LL | pub const DANGLING: *const u8 = unsafe { ptr::NonNull::<u8>::dangling().as_ptr().offset(4) };
| |
| pointer arithmetic failed: 0x0 is not a valid pointer
| inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
- |
+ |
::: $DIR/offset_ub.rs:22:50
|
LL | pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::<u8>().offset(0) };
| |
| 0x7f..f is not a valid pointer
| inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
- |
+ |
::: $DIR/offset_ub.rs:25:47
|
LL | pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) };
| |
| pointer arithmetic failed: alloc3 has size $WORD, so pointer to $TWO_WORDS bytes starting at offset 0 is out-of-bounds
| inside `ptr::const_ptr::<impl *const usize>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
- |
+ |
::: $DIR/ptr_comparisons.rs:61:34
|
LL | const _: *const usize = unsafe { (FOO as *const usize).offset(2) };
// check-pass
-#![feature(unwind_attributes, const_panic)]
+#![feature(c_unwind, const_panic, const_extern_fn)]
-// `#[unwind(aborts)]` is okay for a `const fn`. We don't unwind in const-eval anyways.
-#[unwind(aborts)]
-const fn foo() {
+// We don't unwind in const-eval anyways.
+const extern "C" fn foo() {
panic!()
}
|
LL | _
| ^
- |
+ |
::: $DIR/main.rs:5:5
|
LL | underscore!();
|
LL | _
| ^ `_` not allowed here
- |
+ |
::: $DIR/main.rs:5:5
|
LL | underscore!();
...
LL | x: Error
| ^^^^^^^^ the trait `Eq` is not implemented for `Error`
- |
+ |
::: $SRC_DIR/core/src/cmp.rs:LL:COL
|
LL | pub struct AssertParamIsEq<T: Eq + ?Sized> {
...
LL | Error
| ^^^^^ the trait `Eq` is not implemented for `Error`
- |
+ |
::: $SRC_DIR/core/src/cmp.rs:LL:COL
|
LL | pub struct AssertParamIsEq<T: Eq + ?Sized> {
LL | struct Struct {
LL | x: Error
| ^^^^^^^^ the trait `Eq` is not implemented for `Error`
- |
+ |
::: $SRC_DIR/core/src/cmp.rs:LL:COL
|
LL | pub struct AssertParamIsEq<T: Eq + ?Sized> {
LL | struct Struct(
LL | Error
| ^^^^^ the trait `Eq` is not implemented for `Error`
- |
+ |
::: $SRC_DIR/core/src/cmp.rs:LL:COL
|
LL | pub struct AssertParamIsEq<T: Eq + ?Sized> {
...
LL | x: Error
| ^^^^^^^^ the trait `Hash` is not implemented for `Error`
- |
+ |
::: $SRC_DIR/core/src/hash/mod.rs:LL:COL
|
LL | fn hash<H: Hasher>(&self, state: &mut H);
...
LL | Error
| ^^^^^ the trait `Hash` is not implemented for `Error`
- |
+ |
::: $SRC_DIR/core/src/hash/mod.rs:LL:COL
|
LL | fn hash<H: Hasher>(&self, state: &mut H);
LL | struct Struct {
LL | x: Error
| ^^^^^^^^ the trait `Hash` is not implemented for `Error`
- |
+ |
::: $SRC_DIR/core/src/hash/mod.rs:LL:COL
|
LL | fn hash<H: Hasher>(&self, state: &mut H);
LL | struct Struct(
LL | Error
| ^^^^^ the trait `Hash` is not implemented for `Error`
- |
+ |
::: $SRC_DIR/core/src/hash/mod.rs:LL:COL
|
LL | fn hash<H: Hasher>(&self, state: &mut H);
|
LL | #[derive(Eqr)]
| ^^^ help: a derive macro with a similar name exists: `Eq`
- |
+ |
::: $SRC_DIR/core/src/cmp.rs:LL:COL
|
LL | pub macro Eq($item:item) {
|
LL | #[derive(Eqr)]
| ^^^ help: a derive macro with a similar name exists: `Eq`
- |
+ |
::: $SRC_DIR/core/src/cmp.rs:LL:COL
|
LL | pub macro Eq($item:item) {
|
LL | ($i: ident) => ($i)
| ^ expected one of `move`, `|`, or `||`
- |
+ |
::: $DIR/edition-keywords-2018-2015-parsing.rs:24:8
|
LL | if passes_ident!(async) == 1 {}
|
LL | ($i: ident) => ($i)
| ^ expected one of `move`, `|`, or `||`
- |
+ |
::: $DIR/edition-keywords-2018-2018-parsing.rs:24:8
|
LL | if passes_ident!(async) == 1 {}
...
LL | let e1 = Empty1;
| ^^^^^^
- |
+ |
::: $DIR/auxiliary/empty-struct.rs:2:1
|
LL | pub struct XEmpty2;
...
LL | let e1 = Empty1();
| ^^^^^^^^
- |
+ |
::: $DIR/auxiliary/empty-struct.rs:2:1
|
LL | pub struct XEmpty2;
|
LL | let xe1 = XEmpty1;
| ^^^^^^^
- |
+ |
::: $DIR/auxiliary/empty-struct.rs:1:1
|
LL | pub struct XEmpty1 {}
|
LL | let xe1 = XEmpty1();
| ^^^^^^^^^
- |
+ |
::: $DIR/auxiliary/empty-struct.rs:1:1
|
LL | pub struct XEmpty1 {}
|
LL | XE::XEmpty3 => ()
| ^^^^^^^^^^^
- |
+ |
::: $DIR/auxiliary/empty-struct.rs:6:5
|
LL | XEmpty3 {},
...
LL | Empty1() => ()
| ^^^^^^^^
- |
+ |
::: $DIR/auxiliary/empty-struct.rs:3:1
|
LL | pub struct XEmpty6();
|
LL | XEmpty1() => ()
| ^^^^^^^^^
- |
+ |
::: $DIR/auxiliary/empty-struct.rs:1:1
|
LL | pub struct XEmpty1 {}
...
LL | Empty1(..) => ()
| ^^^^^^^^^^
- |
+ |
::: $DIR/auxiliary/empty-struct.rs:3:1
|
LL | pub struct XEmpty6();
|
LL | XEmpty1(..) => ()
| ^^^^^^^^^^^
- |
+ |
::: $DIR/auxiliary/empty-struct.rs:1:1
|
LL | pub struct XEmpty1 {}
|
LL | XE::XEmpty3() => ()
| ^^^^^^^^^^^^^
- |
+ |
::: $DIR/auxiliary/empty-struct.rs:6:5
|
LL | XEmpty3 {},
|
LL | XE::XEmpty3(..) => ()
| ^^^^^^^^^^^^^^^
- |
+ |
::: $DIR/auxiliary/empty-struct.rs:6:5
|
LL | XEmpty3 {},
|
LL | XE::XEmpty5 => (),
| ^^^^^^^^^^^
- |
+ |
::: $DIR/auxiliary/empty-struct.rs:7:5
|
LL | XEmpty4,
...
LL | Empty2() => ()
| ^^^^^^^^
- |
+ |
::: $DIR/auxiliary/empty-struct.rs:3:1
|
LL | pub struct XEmpty6();
|
LL | XEmpty2() => ()
| ^^^^^^^^^
- |
+ |
::: $DIR/auxiliary/empty-struct.rs:2:1
|
LL | pub struct XEmpty2;
...
LL | Empty2(..) => ()
| ^^^^^^^^^^
- |
+ |
::: $DIR/auxiliary/empty-struct.rs:3:1
|
LL | pub struct XEmpty6();
|
LL | XEmpty2(..) => ()
| ^^^^^^^^^^^
- |
+ |
::: $DIR/auxiliary/empty-struct.rs:2:1
|
LL | pub struct XEmpty2;
|
LL | XE::XEmpty4() => (),
| ^^^^^^^^^^^^^
- |
+ |
::: $DIR/auxiliary/empty-struct.rs:7:5
|
LL | XEmpty4,
|
LL | XE::XEmpty4(..) => (),
| ^^^^^^^^^^^^^^^
- |
+ |
::: $DIR/auxiliary/empty-struct.rs:7:5
|
LL | XEmpty4,
|
LL | match x { }
| ^ patterns `None` and `Some(_)` not covered
- |
+ |
::: $SRC_DIR/core/src/option.rs:LL:COL
|
LL | None,
|
LL | let Some(y) = x;
| ^^^^^^^ pattern `None` not covered
- |
+ |
::: $SRC_DIR/core/src/option.rs:LL:COL
|
LL | None,
|
LL | for Some(x) in xs {}
| ^^^^^^^ pattern `None` not covered
- |
+ |
::: $SRC_DIR/core/src/option.rs:LL:COL
|
LL | None,
|
LL | r.pub_crate();
| ^^^^^^^^^ private associated function
- |
+ |
::: $DIR/auxiliary/pub-and-stability.rs:114:9
|
LL | pub(crate) fn pub_crate(&self) -> i32 { self.d_priv }
|
LL | r.pub_mod();
| ^^^^^^^ private associated function
- |
+ |
::: $DIR/auxiliary/pub-and-stability.rs:116:9
|
LL | pub(in m) fn pub_mod(&self) -> i32 { self.d_priv }
|
LL | r.private();
| ^^^^^^^ private associated function
- |
+ |
::: $DIR/auxiliary/pub-and-stability.rs:118:9
|
LL | fn private(&self) -> i32 { self.d_priv }
|
LL | t.pub_crate();
| ^^^^^^^^^ private associated function
- |
+ |
::: $DIR/auxiliary/pub-and-stability.rs:129:9
|
LL | pub(crate) fn pub_crate(&self) -> i32 { self.0 }
|
LL | t.pub_mod();
| ^^^^^^^ private associated function
- |
+ |
::: $DIR/auxiliary/pub-and-stability.rs:130:9
|
LL | pub(in m) fn pub_mod(&self) -> i32 { self.0 }
|
LL | t.private();
| ^^^^^^^ private associated function
- |
+ |
::: $DIR/auxiliary/pub-and-stability.rs:131:9
|
LL | fn private(&self) -> i32 { self.0 }
|
LL | type A: Iterator<Item: Copy>;
| ^^^^ the trait `Copy` is not implemented for `<<Self as _Tr3>::A as Iterator>::Item`
- |
+ |
::: $SRC_DIR/core/src/marker.rs:LL:COL
|
LL | pub trait Copy: Clone {
|
LL | let Ok(_x) = foo();
| ^^^^^^ pattern `Err(_)` not covered
- |
+ |
::: $SRC_DIR/core/src/result.rs:LL:COL
|
LL | Err(#[stable(feature = "rust1", since = "1.0.0")] E),
LL | struct Foo<U> {
| - help: consider adding an explicit lifetime bound...: `U: 'static`
LL | bar: Bar<U>
- | ^^^^^^ ...so that the type `U` will meet its required lifetime bounds
+ | ^^^^^^ ...so that the type `U` will meet its required lifetime bounds...
+ |
+note: ...that is required by this bound
+ --> $DIR/feature-gate-infer_static_outlives_requirements.rs:7:15
+ |
+LL | struct Bar<T: 'static> {
+ | ^^^^^^^
error: aborting due to previous error
+++ /dev/null
-// ignore-wasm32-bare compiled with panic=abort by default
-// compile-flags: -C no-prepopulate-passes -Cpasses=name-anon-globals
-
-#![crate_type = "lib"]
-
-extern "C" {
- // CHECK: Function Attrs: nounwind
- // CHECK-NEXT: declare void @extern_fn
- fn extern_fn();
- // CHECK-NOT: Function Attrs: nounwind
- // CHECK: declare void @unwinding_extern_fn
- #[unwind(allowed)] //~ ERROR the `#[unwind]` attribute is an experimental feature
- fn unwinding_extern_fn();
-}
-
-pub unsafe fn force_declare() {
- extern_fn();
- unwinding_extern_fn();
-}
+++ /dev/null
-error[E0658]: the `#[unwind]` attribute is an experimental feature
- --> $DIR/feature-gate-unwind-attributes.rs:12:5
- |
-LL | #[unwind(allowed)]
- | ^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #58760 <https://github.com/rust-lang/rust/issues/58760> for more information
- = help: add `#![feature(unwind_attributes)]` to the crate attributes to enable
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0658`.
|
LL | Pin::new(&mut gen).resume(());
| ^^^^^^ doesn't have a size known at compile-time
- |
+ |
::: $SRC_DIR/core/src/ops/generator.rs:LL:COL
|
LL | pub enum GeneratorState<Y, R> {
|
LL | type Bar = ();
| ^^^^^^^^^^^^^^ the trait `AsRef<()>` is not implemented for `()`
- |
+ |
::: $DIR/auxiliary/foo_defn.rs:6:15
|
LL | type Bar: AsRef<()>;
... |
LL | |
LL | | }
- | |_^ ...so that the type `T` will meet its required lifetime bounds
+ | |_^ ...so that the type `T` will meet its required lifetime bounds...
+ |
+note: ...that is required by this bound
+ --> $DIR/issue-86483.rs:7:16
+ |
+LL | for<'a> T: 'a,
+ | ^^
error[E0311]: the parameter type `T` may not live long enough
--> $DIR/issue-86483.rs:9:5
| - help: consider adding an explicit lifetime bound...: `T: 'a`
...
LL | type Ice<'v>: IntoIterator<Item = &'v T>;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds...
+ |
+note: ...that is required by this bound
+ --> $DIR/issue-86483.rs:7:16
+ |
+LL | for<'a> T: 'a,
+ | ^^
error[E0309]: the parameter type `T` may not live long enough
--> $DIR/issue-86483.rs:9:32
LL | type Item<'a> = &'b ();
| ^^^^^^^^^^^^^^^^^^^^^^^
|
-note: type must outlive the lifetime `'a` as defined on the associated item at 8:15
+note: type must outlive the lifetime `'a` as defined on the associated item at 8:15 as required by this binding
--> $DIR/unsatisfied-outlives-bound.rs:8:15
|
LL | type Item<'a> = &'b ();
LL | type Item<'a> = &'a ();
| ^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: type must satisfy the static lifetime
+note: type must satisfy the static lifetime as required by this binding
+ --> $DIR/unsatisfied-outlives-bound.rs:13:20
+ |
+LL | type Item<'a>: 'static;
+ | ^^^^^^^
error: aborting due to 2 previous errors
--- /dev/null
+#![feature(explicit_generic_args_with_impl_trait)]
+
+fn foo<T: ?Sized>(_f: impl AsRef<T>) {}
+
+fn main() {
+ foo::<str, String>("".to_string()); //~ ERROR E0107
+}
--- /dev/null
+error[E0107]: this function takes 1 generic argument but 2 generic arguments were supplied
+ --> $DIR/explicit-generic-args-for-impl.rs:6:5
+ |
+LL | foo::<str, String>("".to_string());
+ | ^^^ ------ help: remove this generic argument
+ | |
+ | expected 1 generic argument
+ |
+note: function defined here, with 1 generic parameter: `T`
+ --> $DIR/explicit-generic-args-for-impl.rs:3:4
+ |
+LL | fn foo<T: ?Sized>(_f: impl AsRef<T>) {}
+ | ^^^ -
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0107`.
--- /dev/null
+// check-pass
+
+#![feature(explicit_generic_args_with_impl_trait)]
+
+fn foo<T: ?Sized>(_f: impl AsRef<T>) {}
+
+fn main() {
+ foo::<str>("".to_string());
+}
--- /dev/null
+// gate-test-explicit_generic_args_with_impl_trait
+
+fn foo<T: ?Sized>(_f: impl AsRef<T>) {}
+
+fn main() {
+ foo::<str>("".to_string()); //~ ERROR E0632
+}
--- /dev/null
+error[E0632]: cannot provide explicit generic arguments when `impl Trait` is used in argument position
+ --> $DIR/feature-gate.rs:6:11
+ |
+LL | foo::<str>("".to_string());
+ | ^^^ explicit generic argument not allowed
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0632`.
--- /dev/null
+// check-pass
+
+#![feature(explicit_generic_args_with_impl_trait)]
+
+fn f<T: ?Sized>(_: impl AsRef<T>, _: impl AsRef<T>) {}
+
+fn main() {
+ f::<[u8]>("a", b"a");
+}
--- /dev/null
+#![feature(explicit_generic_args_with_impl_trait)]
+
+fn f<T: ?Sized, U: ?Sized>(_: impl AsRef<T>, _: impl AsRef<U>) {}
+
+fn main() {
+ f::<[u8]>("a", b"a");
+ //~^ ERROR: this function takes 2 generic arguments but 1 generic argument was supplied
+}
--- /dev/null
+error[E0107]: this function takes 2 generic arguments but 1 generic argument was supplied
+ --> $DIR/not-enough-args.rs:6:5
+ |
+LL | f::<[u8]>("a", b"a");
+ | ^ ---- supplied 1 generic argument
+ | |
+ | expected 2 generic arguments
+ |
+note: function defined here, with 2 generic parameters: `T`, `U`
+ --> $DIR/not-enough-args.rs:3:4
+ |
+LL | fn f<T: ?Sized, U: ?Sized>(_: impl AsRef<T>, _: impl AsRef<U>) {}
+ | ^ - -
+help: add missing generic argument
+ |
+LL | f::<[u8], U>("a", b"a");
+ | ^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0107`.
|
LL | fn hash(&self, hasher: &mut impl Hasher) {}
| ^^^^^^^^^^^ expected generic parameter, found `impl Trait`
- |
+ |
::: $SRC_DIR/core/src/hash/mod.rs:LL:COL
|
LL | fn hash<H: Hasher>(&self, state: &mut H);
| - ^^^^^^^^^^^ expected opaque type, found a different opaque type
| |
| expected due to this
- |
+ |
::: $SRC_DIR/core/src/future/mod.rs:LL:COL
|
LL | pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
|
LL | 1i32.method();
| ^^^^^^ method not found in `i32`
- |
+ |
::: $DIR/auxiliary/no_method_suggested_traits.rs:8:12
|
LL | fn method(&self) {}
|
LL | fn deref(&self) -> &dyn Trait {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&Struct) -> &dyn Trait`
- |
+ |
::: $SRC_DIR/core/src/ops/deref.rs:LL:COL
|
LL | fn deref(&self) -> &Self::Target;
--- /dev/null
+// Regression test of #86162.
+
+fn foo(x: impl Clone) {}
+fn gen<T>() -> T { todo!() }
+
+fn main() {
+ foo(gen()); //<- Do not suggest `foo::<impl Clone>()`!
+ //~^ ERROR: type annotations needed
+}
--- /dev/null
+error[E0283]: type annotations needed
+ --> $DIR/issue-86162-1.rs:7:5
+ |
+LL | fn foo(x: impl Clone) {}
+ | ----- required by this bound in `foo`
+...
+LL | foo(gen()); //<- Do not suggest `foo::<impl Clone>()`!
+ | ^^^ cannot infer type for type parameter `impl Clone` declared on the function `foo`
+ |
+ = note: cannot satisfy `_: Clone`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0283`.
--- /dev/null
+// Regression test of #86162.
+
+fn gen<T>() -> T { todo!() }
+
+struct Foo;
+
+impl Foo {
+ fn bar(x: impl Clone) {}
+}
+
+fn main() {
+ Foo::bar(gen()); //<- Do not suggest `Foo::bar::<impl Clone>()`!
+ //~^ ERROR: type annotations needed
+}
--- /dev/null
+error[E0283]: type annotations needed
+ --> $DIR/issue-86162-2.rs:12:5
+ |
+LL | fn bar(x: impl Clone) {}
+ | ----- required by this bound in `Foo::bar`
+...
+LL | Foo::bar(gen()); //<- Do not suggest `Foo::bar::<impl Clone>()`!
+ | ^^^^^^^^ cannot infer type for type parameter `impl Clone` declared on the associated function `bar`
+ |
+ = note: cannot satisfy `_: Clone`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0283`.
|
LL | catch_unwind(|| { x.set(23); });
| ^^^^^^^^^^^^ `UnsafeCell<i32>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
- |
+ |
::: $SRC_DIR/std/src/panic.rs:LL:COL
|
LL | pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
LL | fn new() -> NoResult<MyEnum, String> {
| ^^^^^^^^^^^^^^^^^^^^^^^^
- |
+ |
::: $SRC_DIR/core/src/result.rs:LL:COL
|
LL | pub enum Result<T, E> {
|
LL | fn newer() -> NoResult<foo::MyEnum, String> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
+ |
::: $SRC_DIR/core/src/result.rs:LL:COL
|
LL | pub enum Result<T, E> {
|
LL | fn iceman(c: Vec<[i32]>) {}
| ^^^^^^^^^^ doesn't have a size known at compile-time
- |
+ |
::: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
|
LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
| ---- in this derive macro expansion
LL | struct Foo(Bar);
| ^^^ the trait `Hash` is not implemented for `Bar`
- |
+ |
::: $SRC_DIR/core/src/hash/mod.rs:LL:COL
|
LL | fn hash<H: Hasher>(&self, state: &mut H);
|
LL | Foo::foo(&f);
| ^^^ private associated function
- |
+ |
::: $DIR/auxiliary/issue-21202.rs:4:9
|
LL | fn foo(&self) { }
|
LL | None @ _ => {}
| ^^^^ cannot be named the same as a unit variant
- |
+ |
::: $SRC_DIR/std/src/prelude/mod.rs:LL:COL
|
LL | pub use super::v1::*;
|
LL | .collect();
| ^^^^^^^ method cannot be called on `Cloned<TakeWhile<&mut std::vec::IntoIter<u8>, [closure@$DIR/issue-31173.rs:6:39: 9:6]>>` due to unsatisfied trait bounds
- |
+ |
::: $SRC_DIR/core/src/iter/adapters/cloned.rs:LL:COL
|
LL | pub struct Cloned<I> {
| -------------------- doesn't satisfy `_: Iterator`
- |
+ |
::: $SRC_DIR/core/src/iter/adapters/take_while.rs:LL:COL
|
LL | pub struct TakeWhile<I, P> {
|
LL | ().a();
| ^ method not found in `()`
- |
+ |
::: $DIR/auxiliary/xcrate-issue-43189-a.rs:5:8
|
LL | fn a(&self) {}
LL | let x = foo::<&'a u32>();
| ^^^^^^^^^^^^^^
|
- = note: type must satisfy the static lifetime
+note: type must satisfy the static lifetime as required by this binding
+ --> $DIR/issue-54943.rs:1:11
+ |
+LL | fn foo<T: 'static>() { }
+ | ^^^^^^^
error: aborting due to previous error
|
LL | reexported_trait::FooStruct.trait_method();
| ^^^^^^^^^^^^ method not found in `FooStruct`
- |
+ |
::: $DIR/auxiliary/reexported-trait.rs:3:12
|
LL | fn trait_method(&self) {
|
LL | reexported_trait::FooStruct.trait_method_b();
| ^^^^^^^^^^^^^^ method not found in `FooStruct`
- |
+ |
::: $DIR/auxiliary/reexported-trait.rs:7:12
|
LL | fn trait_method_b(&self) {
|
LL | foo((), drop)
| ^^^^ doesn't have a size known at compile-time
- |
+ |
::: $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | pub fn drop<T>(_x: T) {}
|
LL | let _ = Struct::<A>::new().clone();
| ^^^^^ method cannot be called on `Struct<A>` due to unsatisfied trait bounds
- |
+ |
::: $DIR/auxiliary/issue-69725.rs:2:1
|
LL | pub struct Struct<A>(A);
|
LL | impl Fo {
| ^^ help: a trait with a similar name exists: `Fn`
- |
+ |
::: $SRC_DIR/core/src/ops/function.rs:LL:COL
|
LL | pub trait Fn<Args>: FnMut<Args> {
|
LL | _func: F,
| ^
- |
+ |
::: $SRC_DIR/core/src/ops/function.rs:LL:COL
|
LL | pub trait Fn<Args>: FnMut<Args> {
LL | assert_send::<&'a (dyn Dummy + Sync)>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: type must satisfy the static lifetime
+note: type must satisfy the static lifetime as required by this binding
+ --> $DIR/kindck-send-object1.rs:5:23
+ |
+LL | fn assert_send<T:Send+'static>() { }
+ | ^^^^^^^
error[E0277]: `(dyn Dummy + 'a)` cannot be sent between threads safely
--> $DIR/kindck-send-object1.rs:29:5
|
LL | intrinsics::size_of::<T>()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ inside `std::mem::size_of::<[u8; SIZE]>` at $SRC_DIR/core/src/mem/mod.rs:LL:COL
- |
+ |
::: $DIR/issue-55878.rs:7:26
|
LL | println!("Size: {}", std::mem::size_of::<[u8; u64::MAX as usize]>());
...
LL | ping!();
| -------- in this macro invocation (#1)
- |
+ |
::: $DIR/auxiliary/ping.rs:5:1
|
LL | / macro_rules! ping {
...
LL | deep!();
| -------- in this macro invocation (#1)
- |
+ |
::: $DIR/auxiliary/ping.rs:5:1
|
LL | / macro_rules! ping {
|
LL | printlx!("oh noes!");
| ^^^^^^^ help: a macro with a similar name exists: `println`
- |
+ |
::: $SRC_DIR/std/src/macros.rs:LL:COL
|
LL | macro_rules! println {
|
LL | inline!();
| ^^^^^^ help: a macro with a similar name exists: `line`
- |
+ |
::: $SRC_DIR/core/src/macros/mod.rs:LL:COL
|
LL | macro_rules! line {
|
LL | macro_two!();
| ^^^^^^^^^ help: a macro with a similar name exists: `macro_one`
- |
+ |
::: $DIR/auxiliary/two_macros.rs:2:1
|
LL | macro_rules! macro_one { () => ("one") }
|
LL | #[derive(Copy(Bad))]
| ^^^^ the trait `Clone` is not implemented for `Test1`
- |
+ |
::: $SRC_DIR/core/src/marker.rs:LL:COL
|
LL | pub trait Copy: Clone {
|
LL | #[derive(Copy="bad")]
| ^^^^ the trait `Clone` is not implemented for `Test2`
- |
+ |
::: $SRC_DIR/core/src/marker.rs:LL:COL
|
LL | pub trait Copy: Clone {
+++ /dev/null
-#![feature(unwind_attributes)]
-
-#[unwind] //~ ERROR malformed `unwind` attribute
-extern "C" fn f1() {}
-
-#[unwind = ""] //~ ERROR malformed `unwind` attribute
-extern "C" fn f2() {}
-
-fn main() {}
+++ /dev/null
-error: malformed `unwind` attribute input
- --> $DIR/malformed-unwind-1.rs:3:1
- |
-LL | #[unwind]
- | ^^^^^^^^^ help: must be of the form: `#[unwind(allowed|aborts)]`
-
-error: malformed `unwind` attribute input
- --> $DIR/malformed-unwind-1.rs:6:1
- |
-LL | #[unwind = ""]
- | ^^^^^^^^^^^^^^ help: must be of the form: `#[unwind(allowed|aborts)]`
-
-error: aborting due to 2 previous errors
-
+++ /dev/null
-#![feature(unwind_attributes)]
-
-#[unwind(allowed, aborts)]
-//~^ ERROR malformed `unwind` attribute
-extern "C" fn f1() {}
-
-#[unwind(unsupported)]
-//~^ ERROR malformed `unwind` attribute
-extern "C" fn f2() {}
-
-fn main() {}
+++ /dev/null
-error[E0633]: malformed `unwind` attribute input
- --> $DIR/malformed-unwind-2.rs:3:1
- |
-LL | #[unwind(allowed, aborts)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid argument
- |
-help: the allowed arguments are `allowed` and `aborts`
- |
-LL | #[unwind(allowed)]
- |
-LL | #[unwind(aborts)]
- |
-
-error[E0633]: malformed `unwind` attribute input
- --> $DIR/malformed-unwind-2.rs:7:1
- |
-LL | #[unwind(unsupported)]
- | ^^^^^^^^^^^^^^^^^^^^^^ invalid argument
- |
-help: the allowed arguments are `allowed` and `aborts`
- |
-LL | #[unwind(allowed)]
- |
-LL | #[unwind(aborts)]
- |
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0633`.
| |
| size_of called on unsized type `dyn Debug`
| inside `std::mem::size_of::<dyn Debug>` at $SRC_DIR/core/src/mem/mod.rs:LL:COL
- |
+ |
::: $DIR/issue-80742.rs:23:10
|
LL | [u8; size_of::<T>() + 1]: ,
...
LL | let dst = Inline::<dyn Debug>::new(0);
| ^^^ function or associated item cannot be called on `Inline<dyn Debug>` due to unsatisfied trait bounds
- |
+ |
::: $SRC_DIR/core/src/fmt/mod.rs:LL:COL
|
LL | pub trait Debug {
| |
| size_of called on unsized type `dyn Debug`
| inside `std::mem::size_of::<dyn Debug>` at $SRC_DIR/core/src/mem/mod.rs:LL:COL
- |
+ |
::: $DIR/issue-80742.rs:15:10
|
LL | [u8; size_of::<T>() + 1]: ,
| |
| doesn't satisfy `<_ as FnOnce<(&&str,)>>::Output = bool`
| doesn't satisfy `_: FnMut<(&&str,)>`
- |
+ |
::: $SRC_DIR/core/src/iter/adapters/filter.rs:LL:COL
|
LL | pub struct Filter<I, P> {
|
LL | check(xm1::S);
| ^^^^^^
- |
+ |
::: $DIR/auxiliary/namespace-mix.rs:3:5
|
LL | pub struct TS();
|
LL | check(xm7::V);
| ^^^^^^
- |
+ |
::: $DIR/auxiliary/namespace-mix.rs:6:9
|
LL | V {},
| ^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<T as MyTrait<'a>>::Output: 'a`...
- = note: ...so that the type `<T as MyTrait<'a>>::Output` will meet its required lifetime bounds
+ = note: ...so that the type `<T as MyTrait<'a>>::Output` will meet its required lifetime bounds...
+note: ...that is required by this bound
+ --> $DIR/projection-where-clause-env-wrong-bound.rs:29:8
+ |
+LL | T: 'a,
+ | ^^
error: aborting due to previous error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<T as MyTrait<'a>>::Output: 'a`...
- = note: ...so that the type `<T as MyTrait<'a>>::Output` will meet its required lifetime bounds
+ = note: ...so that the type `<T as MyTrait<'a>>::Output` will meet its required lifetime bounds...
+note: ...that is required by this bound
+ --> $DIR/projection-where-clause-env-wrong-lifetime.rs:20:8
+ |
+LL | T: 'a,
+ | ^^
error: aborting due to previous error
LL | | println!("{:?}", y);
LL | | });
| |_____- within this `[closure@$DIR/no-send-res-ports.rs:25:19: 29:6]`
- |
+ |
::: $SRC_DIR/std/src/thread/mod.rs:LL:COL
|
LL | F: Send + 'static,
// run-pass
#![allow(unused_must_use)]
-#![feature(unwind_attributes)]
+#![feature(c_unwind)]
#![feature(panic_always_abort)]
// Since we mark some ABIs as "nounwind" to LLVM, we must make sure that
// we never unwind through them.
// ignore-emscripten no processes
// ignore-sgx no processes
-use std::{env, panic};
-use std::io::prelude::*;
use std::io;
+use std::io::prelude::*;
use std::process::{exit, Command, Stdio};
use std::sync::{Arc, Barrier};
use std::thread;
+use std::{env, panic};
-#[unwind(aborts)] // FIXME(#58794) should work even without the attribute
extern "C" fn panic_in_ffi() {
panic!("Test");
}
-#[unwind(aborts)]
-extern "Rust" fn panic_in_rust_abi() {
- panic!("TestRust");
-}
-
fn should_have_aborted() {
io::stdout().write(b"This should never be printed.\n");
let _ = io::stdout().flush();
}
fn test() {
- let _ = panic::catch_unwind(|| { panic_in_ffi(); });
- should_have_aborted();
-}
-
-fn testrust() {
- let _ = panic::catch_unwind(|| { panic_in_rust_abi(); });
+ let _ = panic::catch_unwind(|| {
+ panic_in_ffi();
+ });
should_have_aborted();
}
fn test_always_abort() {
panic::always_abort();
- let _ = panic::catch_unwind(|| { panic!(); });
+ let _ = panic::catch_unwind(|| {
+ panic!();
+ });
should_have_aborted();
}
let barrier = Arc::new(Barrier::new(2));
let thr = {
let barrier = barrier.clone();
- thread::spawn(move ||{
+ thread::spawn(move || {
barrier.wait();
panic!("in thread");
})
fn main() {
let tests: &[(_, fn())] = &[
("test", test),
- ("testrust", testrust),
("test_always_abort", test_always_abort),
("test_always_abort_thread", test_always_abort_thread),
];
let args: Vec<String> = env::args().collect();
if args.len() > 1 {
// This is inside the self-executed command.
- for (a,f) in tests {
- if &args[1] == a { return f() }
+ for (a, f) in tests {
+ if &args[1] == a {
+ return f();
+ }
}
bomb_out_but_not_abort("bad test");
}
let execute_self_expecting_abort = |arg| {
let mut p = Command::new(&args[0])
- .stdout(Stdio::piped())
- .stdin(Stdio::piped())
- .arg(arg).spawn().unwrap();
+ .stdout(Stdio::piped())
+ .stdin(Stdio::piped())
+ .arg(arg)
+ .spawn()
+ .unwrap();
let status = p.wait().unwrap();
assert!(!status.success());
// Any reasonable platform can distinguish a process which
assert_ne!(status.code(), Some(1));
};
- for (a,_f) in tests {
+ for (a, _f) in tests {
execute_self_expecting_abort(a);
}
}
--> $DIR/byte-literals.rs:10:7
|
LL | b'é';
- | ^
- | |
- | byte constant must be ASCII
- | help: use a \xHH escape for a non-ASCII byte: `\xE9`
+ | ^ byte constant must be ASCII
+ |
+help: if you meant to use the unicode code point for 'é', use a \xHH escape
+ |
+LL | b'\xE9';
+ | ^^^^
error[E0763]: unterminated byte constant
--> $DIR/byte-literals.rs:11:6
--> $DIR/byte-string-literals.rs:6:7
|
LL | b"é";
- | ^
- | |
- | byte constant must be ASCII
- | help: use a \xHH escape for a non-ASCII byte: `\xE9`
+ | ^ byte constant must be ASCII
+ |
+help: if you meant to use the unicode code point for 'é', use a \xHH escape
+ |
+LL | b"\xE9";
+ | ^^^^
error: raw byte string must be ASCII
--> $DIR/byte-string-literals.rs:7:10
LL |
LL | fn main() {}
| ^^ unexpected token
- |
+ |
::: $SRC_DIR/core/src/macros/mod.rs:LL:COL
|
LL | ($left:expr, $right:expr $(,)?) => ({
--- /dev/null
+struct Foo {}
+
+impl Foo {
+ pub fn bar()
+ //~^ ERROR: expected `;`, found `}`
+ //~| ERROR: associated function in `impl` without body
+}
+
+fn main() {}
--- /dev/null
+error: expected `;`, found `}`
+ --> $DIR/issue-87635.rs:4:17
+ |
+LL | pub fn bar()
+ | ^ help: add `;` here
+...
+LL | }
+ | - unexpected token
+
+error: associated function in `impl` without body
+ --> $DIR/issue-87635.rs:4:5
+ |
+LL | pub fn bar()
+ | ^^^^^^^^^^^-
+ | |
+ | help: provide a definition for the function: `{ <body> }`
+
+error: aborting due to 2 previous errors
+
+#![allow(unused, dead_code)]
+
fn foo() -> u32 {
return 'label: loop { break 'label 42; };
}
fn bar() -> u32 {
loop { break 'label: loop { break 'label 42; }; }
- //~^ ERROR expected identifier, found keyword `loop`
- //~| ERROR expected type, found keyword `loop`
+ //~^ ERROR: parentheses are required around this expression to avoid confusion
+ //~| HELP: wrap the expression in parentheses
+}
+
+fn baz() -> u32 {
+ 'label: loop {
+ break 'label
+ //~^ WARNING: this labeled break expression is easy to confuse with an unlabeled break
+ loop { break 42; };
+ //~^ HELP: wrap this expression in parentheses
+ };
+
+ 'label2: loop {
+ break 'label2 'inner: loop { break 42; };
+ // no warnings or errors here
+ }
}
pub fn main() {
- foo();
+ // Regression test for issue #86948, as resolved in #87026:
+ let a = 'first_loop: loop {
+ break 'first_loop 1;
+ };
+ let b = loop {
+ break 'inner_loop: loop {
+ //~^ ERROR: parentheses are required around this expression to avoid confusion
+ //~| HELP: wrap the expression in parentheses
+ break 'inner_loop 1;
+ };
+ };
}
-error: expected identifier, found keyword `loop`
- --> $DIR/lifetime_starts_expressions.rs:6:26
+error: parentheses are required around this expression to avoid confusion with a labeled break expression
+ --> $DIR/lifetime_starts_expressions.rs:8:18
|
LL | loop { break 'label: loop { break 'label 42; }; }
- | ^^^^ expected identifier, found keyword
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-help: you can escape reserved keywords to use them as identifiers
+help: wrap the expression in parentheses
|
-LL | loop { break 'label: r#loop { break 'label 42; }; }
- | ^^^^^^
+LL | loop { break ('label: loop { break 'label 42; }); }
+ | ^ ^
-error: expected type, found keyword `loop`
- --> $DIR/lifetime_starts_expressions.rs:6:26
+error: parentheses are required around this expression to avoid confusion with a labeled break expression
+ --> $DIR/lifetime_starts_expressions.rs:33:15
|
-LL | loop { break 'label: loop { break 'label 42; }; }
- | - ^^^^ expected type
- | |
- | help: maybe write a path separator here: `::`
+LL | break 'inner_loop: loop {
+ | _______________^
+LL | |
+LL | |
+LL | | break 'inner_loop 1;
+LL | | };
+ | |_________^
+ |
+help: wrap the expression in parentheses
+ |
+LL | break ('inner_loop: loop {
+LL |
+LL |
+LL | break 'inner_loop 1;
+LL | });
+ |
+
+warning: this labeled break expression is easy to confuse with an unlabeled break with a labeled value expression
+ --> $DIR/lifetime_starts_expressions.rs:15:9
+ |
+LL | / break 'label
+LL | |
+LL | | loop { break 42; };
+ | |______________________________^
+ |
+ = note: `#[warn(break_with_label_and_loop)]` on by default
+help: wrap this expression in parentheses
|
- = note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `<expr>: <type>`
+LL | (loop { break 42; });
+ | ^ ^
-error: aborting due to 2 previous errors
+error: aborting due to 2 previous errors; 1 warning emitted
|
LL | match Some(Some(North)) {
| ^^^^^^^^^^^^^^^^^ pattern `Some(Some(West))` not covered
- |
+ |
::: $SRC_DIR/core/src/option.rs:LL:COL
|
LL | Some(#[stable(feature = "rust1", since = "1.0.0")] T),
|
LL | match private::DATA {
| ^^^^^^^^^^^^^ pattern `Some(Private { misc: true, .. })` not covered
- |
+ |
::: $SRC_DIR/core/src/option.rs:LL:COL
|
LL | Some(#[stable(feature = "rust1", since = "1.0.0")] T),
|
LL | match Some(10) {
| ^^^^^^^^ pattern `Some(_)` not covered
- |
+ |
::: $SRC_DIR/core/src/option.rs:LL:COL
|
LL | Some(#[stable(feature = "rust1", since = "1.0.0")] T),
|
LL | let a = other::A(());
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:1:14
|
LL | pub struct A(());
|
LL | let b = other::B(2);
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:2:14
|
LL | pub struct B(isize);
|
LL | let c = other::C(2, 3);
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:3:14
|
LL | pub struct C(pub isize, isize);
|
LL | let other::A(()) = a;
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:1:14
|
LL | pub struct A(());
|
LL | let other::A(_) = a;
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:1:14
|
LL | pub struct A(());
|
LL | match a { other::A(()) => {} }
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:1:14
|
LL | pub struct A(());
|
LL | match a { other::A(_) => {} }
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:1:14
|
LL | pub struct A(());
|
LL | let other::B(_) = b;
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:2:14
|
LL | pub struct B(isize);
|
LL | let other::B(_b) = b;
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:2:14
|
LL | pub struct B(isize);
|
LL | match b { other::B(_) => {} }
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:2:14
|
LL | pub struct B(isize);
|
LL | match b { other::B(_b) => {} }
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:2:14
|
LL | pub struct B(isize);
|
LL | match b { other::B(1) => {}
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:2:14
|
LL | pub struct B(isize);
|
LL | other::B(_) => {} }
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:2:14
|
LL | pub struct B(isize);
|
LL | let other::C(_, _) = c;
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:3:14
|
LL | pub struct C(pub isize, isize);
|
LL | let other::C(_a, _) = c;
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:3:14
|
LL | pub struct C(pub isize, isize);
|
LL | let other::C(_, _b) = c;
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:3:14
|
LL | pub struct C(pub isize, isize);
|
LL | let other::C(_a, _b) = c;
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:3:14
|
LL | pub struct C(pub isize, isize);
|
LL | match c { other::C(_, _) => {} }
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:3:14
|
LL | pub struct C(pub isize, isize);
|
LL | match c { other::C(_a, _) => {} }
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:3:14
|
LL | pub struct C(pub isize, isize);
|
LL | match c { other::C(_, _b) => {} }
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:3:14
|
LL | pub struct C(pub isize, isize);
|
LL | match c { other::C(_a, _b) => {} }
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:3:14
|
LL | pub struct C(pub isize, isize);
|
LL | let a2 = other::A;
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:1:14
|
LL | pub struct A(());
|
LL | let b2 = other::B;
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:2:14
|
LL | pub struct B(isize);
|
LL | let c2 = other::C;
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy_tuple_struct.rs:3:14
|
LL | pub struct C(pub isize, isize);
|
LL | nyan.nap();
| ^^^ private associated function
- |
+ |
::: $DIR/auxiliary/cci_class_5.rs:8:9
|
LL | fn nap(&self) {}
|
LL | u.g();
| ^ private associated function
- |
+ |
::: $DIR/auxiliary/pub_restricted.rs:14:5
|
LL | pub(crate) fn g(&self) {}
|
LL | u.h();
| ^ private associated function
- |
+ |
::: $DIR/auxiliary/pub_restricted.rs:15:5
|
LL | crate fn h(&self) {}
use proc_macro::Literal;
pub fn test() {
+ test_display_literal();
test_parse_literal();
}
+fn test_display_literal() {
+ assert_eq!(Literal::isize_unsuffixed(-10).to_string(), "-10");
+ assert_eq!(Literal::isize_suffixed(-10).to_string(), "-10isize");
+}
+
fn test_parse_literal() {
assert_eq!("1".parse::<Literal>().unwrap().to_string(), "1");
assert_eq!("1.0".parse::<Literal>().unwrap().to_string(), "1.0");
assert_eq!("b\"\"".parse::<Literal>().unwrap().to_string(), "b\"\"");
assert_eq!("r##\"\"##".parse::<Literal>().unwrap().to_string(), "r##\"\"##");
assert_eq!("10ulong".parse::<Literal>().unwrap().to_string(), "10ulong");
+ assert_eq!("-10ulong".parse::<Literal>().unwrap().to_string(), "-10ulong");
+ assert!("true".parse::<Literal>().is_err());
+ assert!(".8".parse::<Literal>().is_err());
assert!("0 1".parse::<Literal>().is_err());
assert!("'a".parse::<Literal>().is_err());
assert!(" 0".parse::<Literal>().is_err());
assert!("/* comment */0".parse::<Literal>().is_err());
assert!("0/* comment */".parse::<Literal>().is_err());
assert!("0// comment".parse::<Literal>().is_err());
+ assert!("- 10".parse::<Literal>().is_err());
+ assert!("-'x'".parse::<Literal>().is_err());
}
--- /dev/null
+// force-host
+// no-prefer-dynamic
+
+#![feature(proc_macro_diagnostic, proc_macro_span)]
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::{TokenStream, TokenTree, Span};
+
+fn lit_span(tt: TokenTree) -> (Span, String) {
+ match tt {
+ TokenTree::Literal(..) |
+ TokenTree::Group(..) => (tt.span(), tt.to_string().trim().into()),
+ _ => panic!("expected a literal in token tree, got: {:?}", tt)
+ }
+}
+
+#[proc_macro]
+pub fn assert_span_pos(input: TokenStream) -> TokenStream {
+ let mut tokens = input.into_iter();
+ let (sp1, str1) = lit_span(tokens.next().expect("first argument"));
+ let _ = tokens.next();
+ let (_sp2, str2) = lit_span(tokens.next().expect("second argument"));
+
+ let line: usize = str1.parse().unwrap();
+ let col: usize = str2.parse().unwrap();
+
+ let sp1s = sp1.start();
+ if (line, col) != (sp1s.line, sp1s.column) {
+ let msg = format!("line/column mismatch: ({}, {}) != ({}, {})", line, col,
+ sp1s.line, sp1s.column);
+ sp1.error(msg).emit();
+ }
+
+ "".parse().unwrap()
+}
|
LL | #[my_macro] struct One($name);
| ^^^^^
- |
+ |
::: $DIR/group-compat-hack.rs:27:5
|
LL | impl_macros!(Foo);
|
LL | #[my_macro] struct One($name);
| ^^^^^
- |
+ |
::: $DIR/group-compat-hack.rs:44:5
|
LL | impl_macros!(Foo);
|
LL | #[my_macro] struct Two($name);
| ^^^^^
- |
+ |
::: $DIR/group-compat-hack.rs:46:5
|
LL | arrays!(Foo);
|
LL | #[my_macro] struct Three($T);
| ^^
- |
+ |
::: $DIR/group-compat-hack.rs:55:5
|
LL | tuple_from_req!(Foo);
|
LL | #[my_macro] struct Three($T);
| ^^
- |
+ |
::: $DIR/group-compat-hack.rs:63:5
|
LL | tuple_from_req!(Foo);
|
LL | #[my_macro] struct One($name);
| ^^^^^
- |
+ |
::: $DIR/group-compat-hack.rs:27:5
|
LL | impl_macros!(Foo);
|
LL | #[my_macro] struct One($name);
| ^^^^^
- |
+ |
::: $DIR/group-compat-hack.rs:44:5
|
LL | impl_macros!(Foo);
|
LL | #[my_macro] struct Two($name);
| ^^^^^
- |
+ |
::: $DIR/group-compat-hack.rs:46:5
|
LL | arrays!(Foo);
|
LL | #[my_macro] struct Three($T);
| ^^
- |
+ |
::: $DIR/group-compat-hack.rs:55:5
|
LL | tuple_from_req!(Foo);
|
LL | #[my_macro] struct Three($T);
| ^^
- |
+ |
::: $DIR/group-compat-hack.rs:63:5
|
LL | tuple_from_req!(Foo);
...
LL | one!("hello", "world");
| ----------------------- in this macro invocation
- |
+ |
::: $SRC_DIR/core/src/result.rs:LL:COL
|
LL | Ok(#[stable(feature = "rust1", since = "1.0.0")] T),
...
LL | two!("yay", "rust");
| -------------------- in this macro invocation
- |
+ |
::: $SRC_DIR/core/src/result.rs:LL:COL
|
LL | Ok(#[stable(feature = "rust1", since = "1.0.0")] T),
...
LL | three!("hip", "hop");
| --------------------- in this macro invocation
- |
+ |
::: $SRC_DIR/core/src/result.rs:LL:COL
|
LL | Ok(#[stable(feature = "rust1", since = "1.0.0")] T),
|
LL | bang_proc_macrp!();
| ^^^^^^^^^^^^^^^ help: a macro with a similar name exists: `bang_proc_macro`
- |
+ |
::: $DIR/auxiliary/test-macros.rs:15:1
|
LL | pub fn empty(_: TokenStream) -> TokenStream {
|
LL | #[derive(Dlona)]
| ^^^^^ help: a derive macro with a similar name exists: `Clona`
- |
+ |
::: $DIR/auxiliary/derive-clona.rs:11:1
|
LL | pub fn derive_clonea(input: TokenStream) -> TokenStream {
|
LL | #[derive(Dlona)]
| ^^^^^ help: a derive macro with a similar name exists: `Clona`
- |
+ |
::: $DIR/auxiliary/derive-clona.rs:11:1
|
LL | pub fn derive_clonea(input: TokenStream) -> TokenStream {
|
LL | #[derive(Dlone)]
| ^^^^^ help: a derive macro with a similar name exists: `Clone`
- |
+ |
::: $SRC_DIR/core/src/clone.rs:LL:COL
|
LL | pub macro Clone($item:item) {
|
LL | #[derive(Dlone)]
| ^^^^^ help: a derive macro with a similar name exists: `Clone`
- |
+ |
::: $SRC_DIR/core/src/clone.rs:LL:COL
|
LL | pub macro Clone($item:item) {
|
LL | #[attr_proc_macra]
| ^^^^^^^^^^^^^^^ help: an attribute macro with a similar name exists: `attr_proc_macro`
- |
+ |
::: $DIR/auxiliary/test-macros.rs:20:1
|
LL | pub fn empty_attr(_: TokenStream, _: TokenStream) -> TokenStream {
|
LL | #[derive(FooWithLongNan)]
| ^^^^^^^^^^^^^^ help: a derive macro with a similar name exists: `FooWithLongName`
- |
+ |
::: $DIR/auxiliary/derive-foo.rs:11:1
|
LL | pub fn derive_foo(input: TokenStream) -> TokenStream {
|
LL | #[derive(FooWithLongNan)]
| ^^^^^^^^^^^^^^ help: a derive macro with a similar name exists: `FooWithLongName`
- |
+ |
::: $DIR/auxiliary/derive-foo.rs:11:1
|
LL | pub fn derive_foo(input: TokenStream) -> TokenStream {
--- /dev/null
+// aux-build:assert-span-pos.rs
+// ignore-tidy-tab
+extern crate assert_span_pos;
+
+assert_span_pos::assert_span_pos!(5, 35);
+
+// Test space indentation
+ assert_span_pos::assert_span_pos!(8, 39);
+// Test tab indentation
+ assert_span_pos::assert_span_pos!(10, 36);
+
+// Two tests to ensure the promise of the docs that the column is the number
+// of UTF-8 bytes instead of some other number like number of code points.
+
+// Test that multi byte UTF-8 characters indeed count as multiple bytes
+/*🌈*/assert_span_pos::assert_span_pos!(16, 40);
+// Test with a complete grapheme cluster
+/*🏳️🌈*/assert_span_pos::assert_span_pos!(18, 43);
+
+// Test that the macro actually emits an error on a mismatch:
+assert_span_pos::assert_span_pos!(0, 35); //~ ERROR line/column mismatch: (0, 35) != (21, 35)
+assert_span_pos::assert_span_pos!(22, 0); //~ ERROR line/column mismatch: (22, 0) != (22, 35)
+
+fn main() {}
--- /dev/null
+error: line/column mismatch: (0, 35) != (21, 35)
+ --> $DIR/span-absolute-posititions.rs:21:35
+ |
+LL | assert_span_pos::assert_span_pos!(0, 35);
+ | ^
+
+error: line/column mismatch: (22, 0) != (22, 35)
+ --> $DIR/span-absolute-posititions.rs:22:35
+ |
+LL | assert_span_pos::assert_span_pos!(22, 0);
+ | ^^
+
+error: aborting due to 2 previous errors
+
...
LL | field: MissingType
| ^^^^^^^^^^^ not found in this scope
- |
+ |
::: $DIR/span-from-proc-macro.rs:8:1
|
LL | #[error_from_attribute]
...
LL | Variant(OtherMissingType)
| ^^^^^^^^^^^^^^^^ not found in this scope
- |
+ |
::: $DIR/span-from-proc-macro.rs:11:10
|
LL | #[derive(ErrorFromDerive)]
LL | custom_quote::custom_quote! {
LL | my_ident
| ^^^^^^^^ not found in this scope
- |
+ |
::: $DIR/span-from-proc-macro.rs:16:5
|
LL | other_error_from_bang!();
...
LL | pub fn error_from_bang(_input: TokenStream) -> TokenStream {
| ---------------------------------------------------------- in this expansion of `error_from_bang!`
- |
+ |
::: $DIR/span-from-proc-macro.rs:15:5
|
LL | error_from_bang!();
|
LL | let range = *arr..;
| ^^^^^^ doesn't have a size known at compile-time
- |
+ |
::: $SRC_DIR/core/src/ops/range.rs:LL:COL
|
LL | pub struct RangeFrom<Idx> {
|
LL | let Ok(x) = res;
| ^^^^^ pattern `Err(_)` not covered
- |
+ |
::: $SRC_DIR/core/src/result.rs:LL:COL
|
LL | Err(#[stable(feature = "rust1", since = "1.0.0")] E),
LL | type Value = &'a i32;
| ^^^^^^^^^^^^^^^^^^^^^
|
- = note: type must satisfy the static lifetime
+note: type must satisfy the static lifetime as required by this binding
+ --> $DIR/regions-assoc-type-region-bound-in-trait-not-met.rs:5:17
+ |
+LL | type Value: 'a;
+ | ^^
error[E0477]: the type `&'a i32` does not fulfill the required lifetime
--> $DIR/regions-assoc-type-region-bound-in-trait-not-met.rs:20:5
LL | type Value = &'a i32;
| ^^^^^^^^^^^^^^^^^^^^^
|
-note: type must outlive the lifetime `'b` as defined on the impl at 19:10
+note: type must outlive the lifetime `'b` as defined on the impl at 19:10 as required by this binding
--> $DIR/regions-assoc-type-region-bound-in-trait-not-met.rs:19:10
|
LL | impl<'a, 'b> Foo<'b> for &'a i64 {
LL | type Value = &'a i32;
| ^^^^^^^^^^^^^^^^^^^^^
|
- = note: type must satisfy the static lifetime
+note: type must satisfy the static lifetime as required by this binding
+ --> $DIR/regions-assoc-type-static-bound-in-trait-not-met.rs:5:17
+ |
+LL | type Value: 'static;
+ | ^^^^^^^
error: aborting due to previous error
LL | assert_send::<&'a isize>();
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: type must satisfy the static lifetime
+note: type must satisfy the static lifetime as required by this binding
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:6:18
+ |
+LL | fn assert_send<T:'static>() { }
+ | ^^^^^^^
error[E0477]: the type `&'a str` does not fulfill the required lifetime
--> $DIR/regions-bounded-by-trait-requiring-static.rs:26:5
LL | assert_send::<&'a str>();
| ^^^^^^^^^^^^^^^^^^^^^^
|
- = note: type must satisfy the static lifetime
+note: type must satisfy the static lifetime as required by this binding
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:6:18
+ |
+LL | fn assert_send<T:'static>() { }
+ | ^^^^^^^
error[E0477]: the type `&'a [isize]` does not fulfill the required lifetime
--> $DIR/regions-bounded-by-trait-requiring-static.rs:30:5
LL | assert_send::<&'a [isize]>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: type must satisfy the static lifetime
+note: type must satisfy the static lifetime as required by this binding
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:6:18
+ |
+LL | fn assert_send<T:'static>() { }
+ | ^^^^^^^
error[E0477]: the type `Box<&'a isize>` does not fulfill the required lifetime
--> $DIR/regions-bounded-by-trait-requiring-static.rs:44:5
LL | assert_send::<Box<&'a isize>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: type must satisfy the static lifetime
+note: type must satisfy the static lifetime as required by this binding
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:6:18
+ |
+LL | fn assert_send<T:'static>() { }
+ | ^^^^^^^
error[E0477]: the type `*const &'a isize` does not fulfill the required lifetime
--> $DIR/regions-bounded-by-trait-requiring-static.rs:55:5
LL | assert_send::<*const &'a isize>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: type must satisfy the static lifetime
+note: type must satisfy the static lifetime as required by this binding
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:6:18
+ |
+LL | fn assert_send<T:'static>() { }
+ | ^^^^^^^
error[E0477]: the type `*mut &'a isize` does not fulfill the required lifetime
--> $DIR/regions-bounded-by-trait-requiring-static.rs:59:5
LL | assert_send::<*mut &'a isize>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: type must satisfy the static lifetime
+note: type must satisfy the static lifetime as required by this binding
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:6:18
+ |
+LL | fn assert_send<T:'static>() { }
+ | ^^^^^^^
error: aborting due to 6 previous errors
| - help: consider adding an explicit lifetime bound...: `T: 'static`
LL | // oh dear!
LL | box B(&*v) as Box<X>
- | ^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+ | ^^^^^^ ...so that the type `T` will meet its required lifetime bounds...
+ |
+note: ...that is required by this bound
+ --> $DIR/regions-close-object-into-object-5.rs:9:17
+ |
+LL | struct B<'a, T: 'a>(&'a (A<T> + 'a));
+ | ^^
error[E0310]: the parameter type `T` may not live long enough
--> $DIR/regions-close-object-into-object-5.rs:17:11
| ^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `Self: 'a`...
- = note: ...so that the type `Self` will meet its required lifetime bounds
+ = note: ...so that the type `Self` will meet its required lifetime bounds...
+note: ...that is required by this bound
+ --> $DIR/regions-infer-bound-from-trait-self.rs:12:21
+ |
+LL | fn check_bound<'a,A:'a>(x: Inv<'a>, a: A) { }
+ | ^^
error: aborting due to previous error
LL | fn bar1<'a,A>(x: Inv<'a>, a: A) {
| - help: consider adding an explicit lifetime bound...: `A: 'a`
LL | check_bound(x, a)
- | ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds
+ | ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds...
+ |
+note: ...that is required by this bound
+ --> $DIR/regions-infer-bound-from-trait.rs:12:21
+ |
+LL | fn check_bound<'a,A:'a>(x: Inv<'a>, a: A) { }
+ | ^^
error[E0309]: the parameter type `A` may not live long enough
--> $DIR/regions-infer-bound-from-trait.rs:37:5
LL | fn bar2<'a,'b,A:Is<'b>>(x: Inv<'a>, y: Inv<'b>, a: A) {
| -- help: consider adding an explicit lifetime bound...: `A: 'a +`
LL | check_bound(x, a)
- | ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds
+ | ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds...
+ |
+note: ...that is required by this bound
+ --> $DIR/regions-infer-bound-from-trait.rs:12:21
+ |
+LL | fn check_bound<'a,A:'a>(x: Inv<'a>, a: A) { }
+ | ^^
error: aborting due to 2 previous errors
|
LL | let homura = issue_19452_aux::Homura::Madoka;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `issue_19452_aux::Homura::Madoka { /* fields */ }`
- |
+ |
::: $DIR/auxiliary/issue-19452-aux.rs:2:5
|
LL | Madoka { age: u32 }
|
LL | type B = Opiton<u8>; // Misspelled type name from the prelude.
| ^^^^^^ help: an enum with a similar name exists: `Option`
- |
+ |
::: $SRC_DIR/core/src/option.rs:LL:COL
|
LL | pub enum Option<T> {
|
LL | xcrate::m::S;
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy-struct-ctor.rs:2:18
|
LL | pub struct S(u8);
|
LL | xcrate::m::n::Z;
| ^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/privacy-struct-ctor.rs:5:28
|
LL | pub(in m) struct Z(pub(in m::n) u8);
LL | | "0".parse()
LL | | }
| |_^ `main` can only return types that implement `Termination`
- |
+ |
::: $SRC_DIR/test/src/lib.rs:LL:COL
|
LL | pub fn assert_test_result<T: Termination>(result: T) {
|
LL | let ts_explicit = structs::TupleStruct(640, 480);
| ^^^^^^^^^^^ private tuple struct constructor
- |
+ |
::: $DIR/auxiliary/structs.rs:11:24
|
LL | pub struct TupleStruct(pub u16, pub u16);
|
LL | match x {}
| ^ patterns `Tuple(_)` and `Struct { .. }` not covered
- |
+ |
::: $DIR/auxiliary/uninhabited.rs:17:23
|
LL | #[non_exhaustive] Tuple(!),
|
LL | match x {}
| ^ patterns `Tuple(_)` and `Struct { .. }` not covered
- |
+ |
::: $DIR/auxiliary/uninhabited.rs:17:23
|
LL | #[non_exhaustive] Tuple(!),
LL | struct Foo<U> {
| - help: consider adding an explicit lifetime bound...: `U: 'static`
LL | bar: Bar<U>
- | ^^^^^^ ...so that the type `U` will meet its required lifetime bounds
+ | ^^^^^^ ...so that the type `U` will meet its required lifetime bounds...
+ |
+note: ...that is required by this bound
+ --> $DIR/dont-infer-static.rs:10:15
+ |
+LL | struct Bar<T: 'static> {
+ | ^^^^^^^
error: aborting due to previous error
LL | impl<'a, T> Trait<'a, T> for u32 {
| - help: consider adding an explicit lifetime bound...: `T: 'a`
LL | type Out = RefOk<'a, T>;
- | ^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds...
+ |
+note: ...that is required by this bound
+ --> $DIR/regions-struct-not-wf.rs:16:20
+ |
+LL | struct RefOk<'a, T:'a> {
+ | ^^
error[E0491]: in type `&'a &'b T`, reference has a longer lifetime than the data it references
--> $DIR/regions-struct-not-wf.rs:25:5
|
LL | let _: u32 = 3u8.try_into().unwrap();
| ^^^^^^^^ method not found in `u8`
- |
+ |
::: $SRC_DIR/core/src/convert/mod.rs:LL:COL
|
LL | fn try_into(self) -> Result<T, Self::Error>;
#[repr(simd)]
#[derive(Copy, Clone)]
-struct S<const N: usize>([f32; N]);
+struct A<const N: usize>([f32; N]);
+
+#[repr(simd)]
+#[derive(Copy, Clone)]
+struct B<T>([T; 4]);
+
+#[repr(simd)]
+#[derive(Copy, Clone)]
+struct C<T, const N: usize>([T; N]);
extern "platform-intrinsic" {
}
}
-impl ops::Add for S<4> {
+impl ops::Add for A<4> {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ unsafe { simd_add(self, rhs) }
+ }
+}
+
+impl ops::Add for B<f32> {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ unsafe { simd_add(self, rhs) }
+ }
+}
+
+impl ops::Add for C<f32, 4> {
type Output = Self;
fn add(self, rhs: Self) -> Self {
pub fn main() {
- let lr = f32x4(1.0f32, 2.0f32, 3.0f32, 4.0f32);
+ let x = [1.0f32, 2.0f32, 3.0f32, 4.0f32];
+ let y = [2.0f32, 4.0f32, 6.0f32, 8.0f32];
// lame-o
- let f32x4(x, y, z, w) = add(lr, lr);
- assert_eq!(x, 2.0f32);
- assert_eq!(y, 4.0f32);
- assert_eq!(z, 6.0f32);
- assert_eq!(w, 8.0f32);
-
- let lr2 = S::<4>([1.0f32, 2.0f32, 3.0f32, 4.0f32]);
- let [x, y, z, w] = add(lr2, lr2).0;
- assert_eq!(x, 2.0f32);
- assert_eq!(y, 4.0f32);
- assert_eq!(z, 6.0f32);
- assert_eq!(w, 8.0f32);
+ let a = f32x4(1.0f32, 2.0f32, 3.0f32, 4.0f32);
+ let f32x4(a0, a1, a2, a3) = add(a, a);
+ assert_eq!(a0, 2.0f32);
+ assert_eq!(a1, 4.0f32);
+ assert_eq!(a2, 6.0f32);
+ assert_eq!(a3, 8.0f32);
+
+ let a = A(x);
+ assert_eq!(add(a, a).0, y);
+
+ let b = B(x);
+ assert_eq!(add(b, b).0, y);
+
+ let c = C(x);
+ assert_eq!(add(c, c).0, y);
}
--- /dev/null
+// build-fail
+
+#![feature(repr_simd)]
+
+struct E;
+
+// error-pattern:monomorphising SIMD type `S<E>` with a non-primitive-scalar (integer/float/pointer) element type `E`
+
+#[repr(simd)]
+struct S<T>([T; 4]);
+
+fn main() {
+ let _v: Option<S<E>> = None;
+}
--- /dev/null
+error: monomorphising SIMD type `S<E>` with a non-primitive-scalar (integer/float/pointer) element type `E`
+
+error: aborting due to previous error
+
| | ^^^^^ expected one of `!` or `::`
LL | | }
| |_________- in this expansion of `transitive_dep_two::parse_error!`
- |
+ |
::: $DIR/transitive-dep-span.rs:13:1
|
LL | transitive_dep_two::parse_error!();
|
LL | #[tests]
| ^^^^^ help: an attribute macro with a similar name exists: `test`
- |
+ |
::: $SRC_DIR/core/src/macros/mod.rs:LL:COL
|
LL | pub macro test($item:item) {
|
LL | impl result {
| ^^^^^^ help: an enum with a similar name exists: `Result`
- |
+ |
::: $SRC_DIR/core/src/result.rs:LL:COL
|
LL | pub enum Result<T, E> {
LL | | 42
LL | | }
| |_____^ expected struct `Pin`, found opaque type
- |
+ |
::: $SRC_DIR/core/src/future/mod.rs:LL:COL
|
LL | pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
|
LL | t.min().unwrap()
| ^^^
- |
+ |
::: $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | Self: Sized,
|
LL | h.finish()
| ^^^^^^ method not found in `DefaultHasher`
- |
+ |
::: $SRC_DIR/core/src/hash/mod.rs:LL:COL
|
LL | fn finish(&self) -> u64;
| | ^^^^^^^^^^^^^^^^^^^ expected `()`, found opaque type
LL | | }
| |_____- `match` arms have incompatible types
- |
- ::: $DIR/auxiliary/issue-81839.rs:6:49
|
-LL | pub async fn answer_str(&self, _s: &str) -> Test {
- | ---- checked the `Output` of this `async fn`, found opaque type
+note: while checking the return type of the `async fn`
+ --> $DIR/auxiliary/issue-81839.rs:6:49
|
- = note: while checking the return type of the `async fn`
+LL | pub async fn answer_str(&self, _s: &str) -> Test {
+ | ^^^^ checked the `Output` of this `async fn`, found opaque type
= note: expected type `()`
found opaque type `impl Future`
error[E0529]: expected an array or slice, found `Vec<i32>`
--> $DIR/match-ergonomics.rs:8:9
|
+LL | match x {
+ | - help: consider slicing here: `x[..]`
LL | [&v] => {},
| ^^^^ pattern cannot match with input type `Vec<i32>`
error[E0529]: expected an array or slice, found `Vec<i32>`
--> $DIR/match-ergonomics.rs:20:9
|
+LL | match x {
+ | - help: consider slicing here: `x[..]`
LL | [v] => {},
| ^^^ pattern cannot match with input type `Vec<i32>`
}
async fn async_dummy() {} //~ NOTE checked the `Output` of this `async fn`, found opaque type
+//~| NOTE while checking the return type of the `async fn`
+//~| NOTE in this expansion of desugaring of `async` block or function
async fn async_dummy2() {} //~ NOTE checked the `Output` of this `async fn`, found opaque type
//~| NOTE checked the `Output` of this `async fn`, found opaque type
+//~| NOTE while checking the return type of the `async fn`
+//~| NOTE in this expansion of desugaring of `async` block or function
+//~| NOTE while checking the return type of the `async fn`
+//~| NOTE in this expansion of desugaring of `async` block or function
async fn async_extra_semicolon_same() {
let _ = match true { //~ NOTE `match` arms have incompatible types
false => async_dummy(), //~ ERROR `match` arms have incompatible types
//~^ NOTE expected `()`, found opaque type
//~| NOTE expected type `()`
- //~| NOTE while checking the return type of the `async fn`
//~| HELP consider `await`ing on the `Future`
};
}
false => async_dummy2(), //~ ERROR `match` arms have incompatible types
//~^ NOTE expected `()`, found opaque type
//~| NOTE expected type `()`
- //~| NOTE while checking the return type of the `async fn`
//~| HELP consider `await`ing on the `Future`
};
}
//~^ NOTE expected opaque type, found a different opaque type
//~| NOTE expected type `impl Future`
//~| NOTE distinct uses of `impl Trait` result in different opaque types
- //~| NOTE while checking the return type of the `async fn`
};
}
error[E0308]: `match` arms have incompatible types
- --> $DIR/match-prev-arm-needing-semi.rs:26:18
+ --> $DIR/match-prev-arm-needing-semi.rs:32:18
|
-LL | async fn async_dummy() {}
- | - checked the `Output` of this `async fn`, found opaque type
-...
LL | let _ = match true {
| _____________-
LL | | true => {
LL | | };
| |_____- `match` arms have incompatible types
|
- = note: while checking the return type of the `async fn`
+note: while checking the return type of the `async fn`
+ --> $DIR/match-prev-arm-needing-semi.rs:16:24
+ |
+LL | async fn async_dummy() {}
+ | ^ checked the `Output` of this `async fn`, found opaque type
= note: expected type `()`
found opaque type `impl Future`
help: consider `await`ing on the `Future`
| --
error[E0308]: `match` arms have incompatible types
- --> $DIR/match-prev-arm-needing-semi.rs:40:18
+ --> $DIR/match-prev-arm-needing-semi.rs:45:18
|
-LL | async fn async_dummy2() {}
- | - checked the `Output` of this `async fn`, found opaque type
-...
LL | let _ = match true {
| _____________-
LL | | true => {
LL | | };
| |_____- `match` arms have incompatible types
|
- = note: while checking the return type of the `async fn`
+note: while checking the return type of the `async fn`
+ --> $DIR/match-prev-arm-needing-semi.rs:19:25
+ |
+LL | async fn async_dummy2() {}
+ | ^ checked the `Output` of this `async fn`, found opaque type
= note: expected type `()`
found opaque type `impl Future`
help: consider `await`ing on the `Future`
|
error[E0308]: `match` arms have incompatible types
- --> $DIR/match-prev-arm-needing-semi.rs:52:18
+ --> $DIR/match-prev-arm-needing-semi.rs:56:18
|
-LL | async fn async_dummy2() {}
- | - checked the `Output` of this `async fn`, found opaque type
-...
LL | let _ = match true {
| _____________-
LL | | true => async_dummy(),
LL | | };
| |_____- `match` arms have incompatible types
|
- = note: while checking the return type of the `async fn`
+note: while checking the return type of the `async fn`
+ --> $DIR/match-prev-arm-needing-semi.rs:19:25
+ |
+LL | async fn async_dummy2() {}
+ | ^ checked the `Output` of this `async fn`, found opaque type
= note: expected type `impl Future` (opaque type at <$DIR/match-prev-arm-needing-semi.rs:16:24>)
- found opaque type `impl Future` (opaque type at <$DIR/match-prev-arm-needing-semi.rs:17:25>)
+ found opaque type `impl Future` (opaque type at <$DIR/match-prev-arm-needing-semi.rs:19:25>)
= note: distinct uses of `impl Trait` result in different opaque types
help: consider `await`ing on both `Future`s
|
--- /dev/null
+// Regression test for #87397.
+
+fn main() {
+ b'µ';
+ //~^ ERROR: non-ASCII character in byte constant
+ //~| HELP: if you meant to use the unicode code point for 'µ', use a \xHH escape
+ //~| NOTE: byte constant must be ASCII
+
+ b'字';
+ //~^ ERROR: non-ASCII character in byte constant
+ //~| NOTE: this multibyte character does not fit into a single byte
+ //~| NOTE: byte constant must be ASCII
+
+ b"字";
+ //~^ ERROR: non-ASCII character in byte constant
+ //~| HELP: if you meant to use the UTF-8 encoding of '字', use \xHH escapes
+ //~| NOTE: byte constant must be ASCII
+}
--- /dev/null
+error: non-ASCII character in byte constant
+ --> $DIR/multibyte-escapes.rs:4:7
+ |
+LL | b'µ';
+ | ^ byte constant must be ASCII
+ |
+help: if you meant to use the unicode code point for 'µ', use a \xHH escape
+ |
+LL | b'\xB5';
+ | ^^^^
+
+error: non-ASCII character in byte constant
+ --> $DIR/multibyte-escapes.rs:9:7
+ |
+LL | b'字';
+ | ^^
+ | |
+ | byte constant must be ASCII
+ | this multibyte character does not fit into a single byte
+
+error: non-ASCII character in byte constant
+ --> $DIR/multibyte-escapes.rs:14:7
+ |
+LL | b"字";
+ | ^^ byte constant must be ASCII
+ |
+help: if you meant to use the UTF-8 encoding of '字', use \xHH escapes
+ |
+LL | b"\xE5\xAD\x97";
+ | ^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
|
LL | let fp = BufWriter::new(fp);
| ^^^^^^^^^^^^^^^^^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write`
- |
+ |
::: $SRC_DIR/std/src/io/buffered/bufwriter.rs:LL:COL
|
LL | pub struct BufWriter<W: Write> {
|
LL | writeln!(fp, "hello world").unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ method cannot be called on `BufWriter<&dyn std::io::Write>` due to unsatisfied trait bounds
- |
+ |
::: $SRC_DIR/std/src/io/buffered/bufwriter.rs:LL:COL
|
LL | pub struct BufWriter<W: Write> {
--- /dev/null
+// Regression test for #87017.
+
+// run-rustfix
+
+fn main() {
+ fn foo() -> Vec<i32> { vec![1, 2, 3] }
+
+ if let [_, _, _] = foo()[..] {}
+ //~^ ERROR: expected an array or slice
+ //~| HELP: consider slicing here
+
+ if let [] = &foo()[..] {}
+ //~^ ERROR: expected an array or slice
+ //~| HELP: consider slicing here
+
+ if let [] = foo()[..] {}
+ //~^ ERROR: expected an array or slice
+ //~| HELP: consider slicing here
+
+ let v = vec![];
+ match &v[..] {
+ //~^ HELP: consider slicing here
+ [5] => {}
+ //~^ ERROR: expected an array or slice
+ _ => {}
+ }
+}
--- /dev/null
+// Regression test for #87017.
+
+// run-rustfix
+
+fn main() {
+ fn foo() -> Vec<i32> { vec![1, 2, 3] }
+
+ if let [_, _, _] = foo() {}
+ //~^ ERROR: expected an array or slice
+ //~| HELP: consider slicing here
+
+ if let [] = &foo() {}
+ //~^ ERROR: expected an array or slice
+ //~| HELP: consider slicing here
+
+ if let [] = foo() {}
+ //~^ ERROR: expected an array or slice
+ //~| HELP: consider slicing here
+
+ let v = vec![];
+ match &v {
+ //~^ HELP: consider slicing here
+ [5] => {}
+ //~^ ERROR: expected an array or slice
+ _ => {}
+ }
+}
--- /dev/null
+error[E0529]: expected an array or slice, found `Vec<i32>`
+ --> $DIR/pattern-slice-vec.rs:8:12
+ |
+LL | if let [_, _, _] = foo() {}
+ | ^^^^^^^^^ ----- help: consider slicing here: `foo()[..]`
+ | |
+ | pattern cannot match with input type `Vec<i32>`
+
+error[E0529]: expected an array or slice, found `Vec<i32>`
+ --> $DIR/pattern-slice-vec.rs:12:12
+ |
+LL | if let [] = &foo() {}
+ | ^^ ------ help: consider slicing here: `&foo()[..]`
+ | |
+ | pattern cannot match with input type `Vec<i32>`
+
+error[E0529]: expected an array or slice, found `Vec<i32>`
+ --> $DIR/pattern-slice-vec.rs:16:12
+ |
+LL | if let [] = foo() {}
+ | ^^ ----- help: consider slicing here: `foo()[..]`
+ | |
+ | pattern cannot match with input type `Vec<i32>`
+
+error[E0529]: expected an array or slice, found `Vec<_>`
+ --> $DIR/pattern-slice-vec.rs:23:9
+ |
+LL | match &v {
+ | -- help: consider slicing here: `&v[..]`
+LL |
+LL | [5] => {}
+ | ^^^ pattern cannot match with input type `Vec<_>`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0529`.
|
LL | stream_reader.read_until(b'\n', &mut buffer).expect("Reading into buffer failed");
| ^^^^^^^^^^ method cannot be called on `BufReader<&T>` due to unsatisfied trait bounds
- |
+ |
::: $SRC_DIR/std/src/io/buffered/bufreader.rs:LL:COL
|
LL | pub struct BufReader<R> {
//~| NOTE ...so that the type `impl Debug` will meet its required lifetime bounds
}
-fn bar(d: impl Debug + 'static) {
+fn bar(d: impl Debug + 'static) { //~ NOTE ...that is required by this bound
println!("{:?}", d)
}
//~| NOTE ...so that the type `impl Debug` will meet its required lifetime bounds
}
-fn bar(d: impl Debug + 'static) {
+fn bar(d: impl Debug + 'static) { //~ NOTE ...that is required by this bound
println!("{:?}", d)
}
| ---------- help: consider adding an explicit lifetime bound...: `impl Debug + 'static`
LL |
LL | bar(d);
- | ^^^ ...so that the type `impl Debug` will meet its required lifetime bounds
+ | ^^^ ...so that the type `impl Debug` will meet its required lifetime bounds...
+ |
+note: ...that is required by this bound
+ --> $DIR/suggest-impl-trait-lifetime.rs:12:24
+ |
+LL | fn bar(d: impl Debug + 'static) {
+ | ^^^^^^^
error: aborting due to previous error
error: aborting due to 5 previous errors
+For more information about this error, try `rustc --explain E0625`.
error: aborting due to 5 previous errors
-Some errors have detailed explanations: E0013, E0133, E0658.
+Some errors have detailed explanations: E0013, E0133, E0625, E0658.
For more information about an error, try `rustc --explain E0013`.
|
LL | struct Foo<T> where T: Bar, <T as Bar>::Baz: String {
| ^^^^^^ not a trait
- |
+ |
::: $SRC_DIR/alloc/src/string.rs:LL:COL
|
LL | pub trait ToString {
|
LL | struct Qux<'a, T> where T: Bar, <&'a T as Bar>::Baz: String {
| ^^^^^^ not a trait
- |
+ |
::: $SRC_DIR/alloc/src/string.rs:LL:COL
|
LL | pub trait ToString {
|
LL | fn foo<T: Bar>(_: T) where <T as Bar>::Baz: String {
| ^^^^^^ not a trait
- |
+ |
::: $SRC_DIR/alloc/src/string.rs:LL:COL
|
LL | pub trait ToString {
|
LL | fn qux<'a, T: Bar>(_: &'a T) where <&'a T as Bar>::Baz: String {
| ^^^^^^ not a trait
- |
+ |
::: $SRC_DIR/alloc/src/string.rs:LL:COL
|
LL | pub trait ToString {
|
LL | let x: Vec<dyn Trait + Sized> = Vec::new();
| ^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
- |
+ |
::: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
|
LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
|
LL | let x: Vec<dyn Trait + Sized> = Vec::new();
| ^^^ doesn't have a size known at compile-time
- |
+ |
::: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
|
LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
|
LL | fn explode(x: Foo<usize>) {}
| ^^^^^^^^^^ the trait `Trait` is not implemented for `usize`
- |
+ |
::: $DIR/auxiliary/on_structs_and_enums_xc.rs:5:18
|
LL | pub struct Foo<T:Trait> {
|
LL | fn kaboom(y: Bar<f32>) {}
| ^^^^^^^^ the trait `Trait` is not implemented for `f32`
- |
+ |
::: $DIR/auxiliary/on_structs_and_enums_xc.rs:9:16
|
LL | pub enum Bar<T:Trait> {
|
LL | let bar: Bar<f64> = return;
| ^^^^^^^^ the trait `Trait` is not implemented for `f64`
- |
+ |
::: $DIR/auxiliary/on_structs_and_enums_xc.rs:9:16
|
LL | pub enum Bar<T:Trait> {
|
LL | a::try_foo(foo);
| ^^^ the trait `main::a::Bar` is not implemented for `Foo`
- |
+ |
::: $DIR/auxiliary/crate_a1.rs:3:24
|
LL | pub fn try_foo(x: impl Bar) {}
|
LL | a::try_foo(implements_no_traits);
| ^^^^^^^^^^^^^^^^^^^^ the trait `main::a::Bar` is not implemented for `DoesNotImplementTrait`
- |
+ |
::: $DIR/auxiliary/crate_a1.rs:3:24
|
LL | pub fn try_foo(x: impl Bar) {}
|
LL | a::try_foo(other_variant_implements_mismatched_trait);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `main::a::Bar` is not implemented for `ImplementsWrongTraitConditionally<isize>`
- |
+ |
::: $DIR/auxiliary/crate_a1.rs:3:24
|
LL | pub fn try_foo(x: impl Bar) {}
|
LL | a::try_foo(other_variant_implements_correct_trait);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `main::a::Bar` is not implemented for `ImplementsTraitForUsize<isize>`
- |
+ |
::: $DIR/auxiliary/crate_a1.rs:3:24
|
LL | pub fn try_foo(x: impl Bar) {}
--- /dev/null
+// Regression test for the invalid suggestion in #85735 (the
+// underlying issue #21974 still exists here).
+
+trait Foo {}
+impl<'a, 'b, T> Foo for T
+where
+ T: FnMut(&'a ()),
+ //~^ ERROR: type annotations needed [E0283]
+ T: FnMut(&'b ()),
+{
+}
+
+fn main() {}
--- /dev/null
+error[E0283]: type annotations needed
+ --> $DIR/issue-85735.rs:7:8
+ |
+LL | T: FnMut(&'a ()),
+ | ^^^^^^^^^^^^^ cannot infer type for type parameter `T`
+ |
+ ::: $SRC_DIR/core/src/ops/function.rs:LL:COL
+ |
+LL | pub trait FnMut<Args>: FnOnce<Args> {
+ | ----------------------------------- required by this bound in `FnMut`
+ |
+ = note: cannot satisfy `T: FnMut<(&'a (),)>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0283`.
|
LL | iso(left, right)
| ^^^
- |
+ |
::: $SRC_DIR/core/src/option.rs:LL:COL
|
LL | pub enum Option<T> {
| |
| the trait `ToSocketAddrs` is not implemented for `NoToSocketAddrs`
| help: consider adding dereference here: `&*bad`
- |
+ |
::: $SRC_DIR/std/src/net/tcp.rs:LL:COL
|
LL | pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
LL | // suggest a where-clause, if needed
LL | mem::size_of::<U>();
| ^ doesn't have a size known at compile-time
- |
+ |
::: $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | pub const fn size_of<T>() -> usize {
...
LL | mem::size_of::<Misc<U>>();
| ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
- |
+ |
::: $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | pub const fn size_of<T>() -> usize {
|
LL | mem::size_of::<[T]>();
| ^^^ doesn't have a size known at compile-time
- |
+ |
::: $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | pub const fn size_of<T>() -> usize {
|
LL | mem::size_of::<[&U]>();
| ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
- |
+ |
::: $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | pub const fn size_of<T>() -> usize {
--- /dev/null
+// run-pass
+
+#![feature(trait_upcasting)]
+#![allow(incomplete_features)]
+
+trait Foo: PartialEq<i32> + std::fmt::Debug + Send + Sync {
+ fn a(&self) -> i32 {
+ 10
+ }
+
+ fn z(&self) -> i32 {
+ 11
+ }
+
+ fn y(&self) -> i32 {
+ 12
+ }
+}
+
+trait Bar: Foo {
+ fn b(&self) -> i32 {
+ 20
+ }
+
+ fn w(&self) -> i32 {
+ 21
+ }
+}
+
+trait Baz: Bar {
+ fn c(&self) -> i32 {
+ 30
+ }
+}
+
+impl Foo for i32 {
+ fn a(&self) -> i32 {
+ 100
+ }
+}
+
+impl Bar for i32 {
+ fn b(&self) -> i32 {
+ 200
+ }
+}
+
+impl Baz for i32 {
+ fn c(&self) -> i32 {
+ 300
+ }
+}
+
+fn main() {
+ let baz: &dyn Baz = &1;
+ let _: &dyn std::fmt::Debug = baz;
+ assert_eq!(*baz, 1);
+ assert_eq!(baz.a(), 100);
+ assert_eq!(baz.b(), 200);
+ assert_eq!(baz.c(), 300);
+ assert_eq!(baz.z(), 11);
+ assert_eq!(baz.y(), 12);
+ assert_eq!(baz.w(), 21);
+
+ let bar: &dyn Bar = baz;
+ let _: &dyn std::fmt::Debug = bar;
+ assert_eq!(*bar, 1);
+ assert_eq!(bar.a(), 100);
+ assert_eq!(bar.b(), 200);
+ assert_eq!(bar.z(), 11);
+ assert_eq!(bar.y(), 12);
+ assert_eq!(bar.w(), 21);
+
+ let foo: &dyn Foo = baz;
+ let _: &dyn std::fmt::Debug = foo;
+ assert_eq!(*foo, 1);
+ assert_eq!(foo.a(), 100);
+ assert_eq!(foo.z(), 11);
+ assert_eq!(foo.y(), 12);
+
+ let foo: &dyn Foo = bar;
+ let _: &dyn std::fmt::Debug = foo;
+ assert_eq!(*foo, 1);
+ assert_eq!(foo.a(), 100);
+ assert_eq!(foo.z(), 11);
+ assert_eq!(foo.y(), 12);
+}
--- /dev/null
+trait A: B + A {}
+//~^ ERROR cycle detected when computing the super predicates of `A` [E0391]
+
+trait B {}
+
+impl A for () {}
+
+impl B for () {}
+
+fn main() {
+ let a: Box<dyn A> = Box::new(());
+ let _b: Box<dyn B> = a;
+}
--- /dev/null
+error[E0391]: cycle detected when computing the super predicates of `A`
+ --> $DIR/cyclic-trait-resolution.rs:1:1
+ |
+LL | trait A: B + A {}
+ | ^^^^^^^^^^^^^^
+ |
+note: ...which requires computing the super traits of `A`...
+ --> $DIR/cyclic-trait-resolution.rs:1:14
+ |
+LL | trait A: B + A {}
+ | ^
+ = note: ...which again requires computing the super predicates of `A`, completing the cycle
+note: cycle used when collecting item types in top-level module
+ --> $DIR/cyclic-trait-resolution.rs:1:1
+ |
+LL | trait A: B + A {}
+ | ^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0391`.
--- /dev/null
+// run-pass
+
+#![feature(trait_upcasting)]
+#![allow(incomplete_features)]
+
+trait Foo: PartialEq<i32> + std::fmt::Debug + Send + Sync {
+ fn a(&self) -> i32 {
+ 10
+ }
+
+ fn z(&self) -> i32 {
+ 11
+ }
+
+ fn y(&self) -> i32 {
+ 12
+ }
+}
+
+trait Bar1: Foo {
+ fn b(&self) -> i32 {
+ 20
+ }
+
+ fn w(&self) -> i32 {
+ 21
+ }
+}
+
+trait Bar2: Foo {
+ fn c(&self) -> i32 {
+ 30
+ }
+
+ fn v(&self) -> i32 {
+ 31
+ }
+}
+
+trait Baz: Bar1 + Bar2 {
+ fn d(&self) -> i32 {
+ 40
+ }
+}
+
+impl Foo for i32 {
+ fn a(&self) -> i32 {
+ 100
+ }
+}
+
+impl Bar1 for i32 {
+ fn b(&self) -> i32 {
+ 200
+ }
+}
+
+impl Bar2 for i32 {
+ fn c(&self) -> i32 {
+ 300
+ }
+}
+
+impl Baz for i32 {
+ fn d(&self) -> i32 {
+ 400
+ }
+}
+
+fn main() {
+ let baz: &dyn Baz = &1;
+ let _: &dyn std::fmt::Debug = baz;
+ assert_eq!(*baz, 1);
+ assert_eq!(baz.a(), 100);
+ assert_eq!(baz.b(), 200);
+ assert_eq!(baz.c(), 300);
+ assert_eq!(baz.d(), 400);
+ assert_eq!(baz.z(), 11);
+ assert_eq!(baz.y(), 12);
+ assert_eq!(baz.w(), 21);
+ assert_eq!(baz.v(), 31);
+
+ let bar1: &dyn Bar1 = baz;
+ let _: &dyn std::fmt::Debug = bar1;
+ assert_eq!(*bar1, 1);
+ assert_eq!(bar1.a(), 100);
+ assert_eq!(bar1.b(), 200);
+ assert_eq!(bar1.z(), 11);
+ assert_eq!(bar1.y(), 12);
+ assert_eq!(bar1.w(), 21);
+
+ let bar2: &dyn Bar2 = baz;
+ let _: &dyn std::fmt::Debug = bar2;
+ assert_eq!(*bar2, 1);
+ assert_eq!(bar2.a(), 100);
+ assert_eq!(bar2.c(), 300);
+ assert_eq!(bar2.z(), 11);
+ assert_eq!(bar2.y(), 12);
+ assert_eq!(bar2.v(), 31);
+
+ let foo: &dyn Foo = baz;
+ let _: &dyn std::fmt::Debug = foo;
+ assert_eq!(*foo, 1);
+ assert_eq!(foo.a(), 100);
+
+ let foo: &dyn Foo = bar1;
+ let _: &dyn std::fmt::Debug = foo;
+ assert_eq!(*foo, 1);
+ assert_eq!(foo.a(), 100);
+
+ let foo: &dyn Foo = bar2;
+ let _: &dyn std::fmt::Debug = foo;
+ assert_eq!(*foo, 1);
+ assert_eq!(foo.a(), 100);
+}
--- /dev/null
+#![feature(trait_upcasting)]
+#![allow(incomplete_features)]
+
+trait Foo {
+ fn a(&self) -> i32 {
+ 10
+ }
+
+ fn z(&self) -> i32 {
+ 11
+ }
+
+ fn y(&self) -> i32 {
+ 12
+ }
+}
+
+trait Bar {
+ fn b(&self) -> i32 {
+ 20
+ }
+
+ fn w(&self) -> i32 {
+ 21
+ }
+}
+
+trait Baz {
+ fn c(&self) -> i32 {
+ 30
+ }
+}
+
+impl Foo for i32 {
+ fn a(&self) -> i32 {
+ 100
+ }
+}
+
+impl Bar for i32 {
+ fn b(&self) -> i32 {
+ 200
+ }
+}
+
+impl Baz for i32 {
+ fn c(&self) -> i32 {
+ 300
+ }
+}
+
+fn main() {
+ let baz: &dyn Baz = &1;
+ let _: &dyn std::fmt::Debug = baz;
+ //~^ ERROR mismatched types [E0308]
+ let _: &dyn Send = baz;
+ //~^ ERROR mismatched types [E0308]
+ let _: &dyn Sync = baz;
+ //~^ ERROR mismatched types [E0308]
+
+ let bar: &dyn Bar = baz;
+ //~^ ERROR mismatched types [E0308]
+ let _: &dyn std::fmt::Debug = bar;
+ //~^ ERROR mismatched types [E0308]
+ let _: &dyn Send = bar;
+ //~^ ERROR mismatched types [E0308]
+ let _: &dyn Sync = bar;
+ //~^ ERROR mismatched types [E0308]
+
+ let foo: &dyn Foo = baz;
+ //~^ ERROR mismatched types [E0308]
+ let _: &dyn std::fmt::Debug = foo;
+ //~^ ERROR mismatched types [E0308]
+ let _: &dyn Send = foo;
+ //~^ ERROR mismatched types [E0308]
+ let _: &dyn Sync = foo;
+ //~^ ERROR mismatched types [E0308]
+
+ let foo: &dyn Foo = bar;
+ //~^ ERROR mismatched types [E0308]
+ let _: &dyn std::fmt::Debug = foo;
+ //~^ ERROR mismatched types [E0308]
+ let _: &dyn Send = foo;
+ //~^ ERROR mismatched types [E0308]
+ let _: &dyn Sync = foo;
+ //~^ ERROR mismatched types [E0308]
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/invalid-upcast.rs:54:35
+ |
+LL | let _: &dyn std::fmt::Debug = baz;
+ | -------------------- ^^^ expected trait `Debug`, found trait `Baz`
+ | |
+ | expected due to this
+ |
+ = note: expected reference `&dyn Debug`
+ found reference `&dyn Baz`
+
+error[E0308]: mismatched types
+ --> $DIR/invalid-upcast.rs:56:24
+ |
+LL | let _: &dyn Send = baz;
+ | --------- ^^^ expected trait `Send`, found trait `Baz`
+ | |
+ | expected due to this
+ |
+ = note: expected reference `&dyn Send`
+ found reference `&dyn Baz`
+
+error[E0308]: mismatched types
+ --> $DIR/invalid-upcast.rs:58:24
+ |
+LL | let _: &dyn Sync = baz;
+ | --------- ^^^ expected trait `Sync`, found trait `Baz`
+ | |
+ | expected due to this
+ |
+ = note: expected reference `&dyn Sync`
+ found reference `&dyn Baz`
+
+error[E0308]: mismatched types
+ --> $DIR/invalid-upcast.rs:61:25
+ |
+LL | let bar: &dyn Bar = baz;
+ | -------- ^^^ expected trait `Bar`, found trait `Baz`
+ | |
+ | expected due to this
+ |
+ = note: expected reference `&dyn Bar`
+ found reference `&dyn Baz`
+
+error[E0308]: mismatched types
+ --> $DIR/invalid-upcast.rs:63:35
+ |
+LL | let _: &dyn std::fmt::Debug = bar;
+ | -------------------- ^^^ expected trait `Debug`, found trait `Bar`
+ | |
+ | expected due to this
+ |
+ = note: expected reference `&dyn Debug`
+ found reference `&dyn Bar`
+
+error[E0308]: mismatched types
+ --> $DIR/invalid-upcast.rs:65:24
+ |
+LL | let _: &dyn Send = bar;
+ | --------- ^^^ expected trait `Send`, found trait `Bar`
+ | |
+ | expected due to this
+ |
+ = note: expected reference `&dyn Send`
+ found reference `&dyn Bar`
+
+error[E0308]: mismatched types
+ --> $DIR/invalid-upcast.rs:67:24
+ |
+LL | let _: &dyn Sync = bar;
+ | --------- ^^^ expected trait `Sync`, found trait `Bar`
+ | |
+ | expected due to this
+ |
+ = note: expected reference `&dyn Sync`
+ found reference `&dyn Bar`
+
+error[E0308]: mismatched types
+ --> $DIR/invalid-upcast.rs:70:25
+ |
+LL | let foo: &dyn Foo = baz;
+ | -------- ^^^ expected trait `Foo`, found trait `Baz`
+ | |
+ | expected due to this
+ |
+ = note: expected reference `&dyn Foo`
+ found reference `&dyn Baz`
+
+error[E0308]: mismatched types
+ --> $DIR/invalid-upcast.rs:72:35
+ |
+LL | let _: &dyn std::fmt::Debug = foo;
+ | -------------------- ^^^ expected trait `Debug`, found trait `Foo`
+ | |
+ | expected due to this
+ |
+ = note: expected reference `&dyn Debug`
+ found reference `&dyn Foo`
+
+error[E0308]: mismatched types
+ --> $DIR/invalid-upcast.rs:74:24
+ |
+LL | let _: &dyn Send = foo;
+ | --------- ^^^ expected trait `Send`, found trait `Foo`
+ | |
+ | expected due to this
+ |
+ = note: expected reference `&dyn Send`
+ found reference `&dyn Foo`
+
+error[E0308]: mismatched types
+ --> $DIR/invalid-upcast.rs:76:24
+ |
+LL | let _: &dyn Sync = foo;
+ | --------- ^^^ expected trait `Sync`, found trait `Foo`
+ | |
+ | expected due to this
+ |
+ = note: expected reference `&dyn Sync`
+ found reference `&dyn Foo`
+
+error[E0308]: mismatched types
+ --> $DIR/invalid-upcast.rs:79:25
+ |
+LL | let foo: &dyn Foo = bar;
+ | -------- ^^^ expected trait `Foo`, found trait `Bar`
+ | |
+ | expected due to this
+ |
+ = note: expected reference `&dyn Foo`
+ found reference `&dyn Bar`
+
+error[E0308]: mismatched types
+ --> $DIR/invalid-upcast.rs:81:35
+ |
+LL | let _: &dyn std::fmt::Debug = foo;
+ | -------------------- ^^^ expected trait `Debug`, found trait `Foo`
+ | |
+ | expected due to this
+ |
+ = note: expected reference `&dyn Debug`
+ found reference `&dyn Foo`
+
+error[E0308]: mismatched types
+ --> $DIR/invalid-upcast.rs:83:24
+ |
+LL | let _: &dyn Send = foo;
+ | --------- ^^^ expected trait `Send`, found trait `Foo`
+ | |
+ | expected due to this
+ |
+ = note: expected reference `&dyn Send`
+ found reference `&dyn Foo`
+
+error[E0308]: mismatched types
+ --> $DIR/invalid-upcast.rs:85:24
+ |
+LL | let _: &dyn Sync = foo;
+ | --------- ^^^ expected trait `Sync`, found trait `Foo`
+ | |
+ | expected due to this
+ |
+ = note: expected reference `&dyn Sync`
+ found reference `&dyn Foo`
+
+error: aborting due to 15 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// run-pass
+// ignore-compare-mode-nll
+
+#![feature(trait_upcasting)]
+#![allow(incomplete_features)]
+
+trait Foo: PartialEq<i32> + std::fmt::Debug + Send + Sync {
+ fn a(&self) -> i32 {
+ 10
+ }
+
+ fn z(&self) -> i32 {
+ 11
+ }
+
+ fn y(&self) -> i32 {
+ 12
+ }
+}
+
+trait Bar: Foo {
+ fn b(&self) -> i32 {
+ 20
+ }
+
+ fn w(&self) -> i32 {
+ 21
+ }
+}
+
+trait Baz: Bar {
+ fn c(&self) -> i32 {
+ 30
+ }
+}
+
+impl Foo for i32 {
+ fn a(&self) -> i32 {
+ 100
+ }
+}
+
+impl Bar for i32 {
+ fn b(&self) -> i32 {
+ 200
+ }
+}
+
+impl Baz for i32 {
+ fn c(&self) -> i32 {
+ 300
+ }
+}
+
+// Note: upcast lifetime means a shorter lifetime.
+fn upcast_baz<'a: 'b, 'b, T>(v: Box<dyn Baz + 'a>, _l: &'b T) -> Box<dyn Baz + 'b> {
+ v
+}
+fn upcast_bar<'a: 'b, 'b, T>(v: Box<dyn Bar + 'a>, _l: &'b T) -> Box<dyn Bar + 'b> {
+ v
+}
+fn upcast_foo<'a: 'b, 'b, T>(v: Box<dyn Foo + 'a>, _l: &'b T) -> Box<dyn Foo + 'b> {
+ v
+}
+
+fn main() {
+ let v = Box::new(1);
+ let l = &(); // dummy lifetime (shorter than `baz`)
+
+ let baz: Box<dyn Baz> = v.clone();
+ let u = upcast_baz(baz, &l);
+ assert_eq!(*u, 1);
+ assert_eq!(u.a(), 100);
+ assert_eq!(u.b(), 200);
+ assert_eq!(u.c(), 300);
+
+ let baz: Box<dyn Baz> = v.clone();
+ let bar: Box<dyn Bar> = baz;
+ let u = upcast_bar(bar, &l);
+ assert_eq!(*u, 1);
+ assert_eq!(u.a(), 100);
+ assert_eq!(u.b(), 200);
+
+ let baz: Box<dyn Baz> = v.clone();
+ let foo: Box<dyn Foo> = baz;
+ let u = upcast_foo(foo, &l);
+ assert_eq!(*u, 1);
+ assert_eq!(u.a(), 100);
+
+ let baz: Box<dyn Baz> = v.clone();
+ let bar: Box<dyn Bar> = baz;
+ let foo: Box<dyn Foo> = bar;
+ let u = upcast_foo(foo, &l);
+ assert_eq!(*u, 1);
+ assert_eq!(u.a(), 100);
+}
--- /dev/null
+// run-pass
+
+#![feature(trait_upcasting)]
+#![allow(incomplete_features)]
+
+trait A {
+ fn foo_a(&self);
+}
+
+trait B {
+ fn foo_b(&self);
+}
+
+trait C: A + B {
+ fn foo_c(&self);
+}
+
+struct S(i32);
+
+impl A for S {
+ fn foo_a(&self) {
+ unreachable!();
+ }
+}
+
+impl B for S {
+ fn foo_b(&self) {
+ assert_eq!(42, self.0);
+ }
+}
+
+impl C for S {
+ fn foo_c(&self) {
+ unreachable!();
+ }
+}
+
+fn invoke_inner(b: &dyn B) {
+ b.foo_b();
+}
+
+fn invoke_outer(c: &dyn C) {
+ invoke_inner(c);
+}
+
+fn main() {
+ let s = S(42);
+ invoke_outer(&s);
+}
--- /dev/null
+// run-pass
+
+#![feature(trait_upcasting)]
+#![allow(incomplete_features)]
+
+use std::rc::Rc;
+use std::sync::Arc;
+
+trait Foo: PartialEq<i32> + std::fmt::Debug + Send + Sync {
+ fn a(&self) -> i32 {
+ 10
+ }
+
+ fn z(&self) -> i32 {
+ 11
+ }
+
+ fn y(&self) -> i32 {
+ 12
+ }
+}
+
+trait Bar: Foo {
+ fn b(&self) -> i32 {
+ 20
+ }
+
+ fn w(&self) -> i32 {
+ 21
+ }
+}
+
+trait Baz: Bar {
+ fn c(&self) -> i32 {
+ 30
+ }
+}
+
+impl Foo for i32 {
+ fn a(&self) -> i32 {
+ 100
+ }
+}
+
+impl Bar for i32 {
+ fn b(&self) -> i32 {
+ 200
+ }
+}
+
+impl Baz for i32 {
+ fn c(&self) -> i32 {
+ 300
+ }
+}
+
+fn test_box() {
+ let v = Box::new(1);
+
+ let baz: Box<dyn Baz> = v.clone();
+ assert_eq!(*baz, 1);
+ assert_eq!(baz.a(), 100);
+ assert_eq!(baz.b(), 200);
+ assert_eq!(baz.c(), 300);
+ assert_eq!(baz.z(), 11);
+ assert_eq!(baz.y(), 12);
+ assert_eq!(baz.w(), 21);
+
+ let baz: Box<dyn Baz> = v.clone();
+ let bar: Box<dyn Bar> = baz;
+ assert_eq!(*bar, 1);
+ assert_eq!(bar.a(), 100);
+ assert_eq!(bar.b(), 200);
+ assert_eq!(bar.z(), 11);
+ assert_eq!(bar.y(), 12);
+ assert_eq!(bar.w(), 21);
+
+ let baz: Box<dyn Baz> = v.clone();
+ let foo: Box<dyn Foo> = baz;
+ assert_eq!(*foo, 1);
+ assert_eq!(foo.a(), 100);
+ assert_eq!(foo.z(), 11);
+ assert_eq!(foo.y(), 12);
+
+ let baz: Box<dyn Baz> = v.clone();
+ let bar: Box<dyn Bar> = baz;
+ let foo: Box<dyn Foo> = bar;
+ assert_eq!(*foo, 1);
+ assert_eq!(foo.a(), 100);
+ assert_eq!(foo.z(), 11);
+ assert_eq!(foo.y(), 12);
+}
+
+fn test_rc() {
+ let v = Rc::new(1);
+
+ let baz: Rc<dyn Baz> = v.clone();
+ assert_eq!(*baz, 1);
+ assert_eq!(baz.a(), 100);
+ assert_eq!(baz.b(), 200);
+ assert_eq!(baz.c(), 300);
+ assert_eq!(baz.z(), 11);
+ assert_eq!(baz.y(), 12);
+ assert_eq!(baz.w(), 21);
+
+ let baz: Rc<dyn Baz> = v.clone();
+ let bar: Rc<dyn Bar> = baz;
+ assert_eq!(*bar, 1);
+ assert_eq!(bar.a(), 100);
+ assert_eq!(bar.b(), 200);
+ assert_eq!(bar.z(), 11);
+ assert_eq!(bar.y(), 12);
+ assert_eq!(bar.w(), 21);
+
+ let baz: Rc<dyn Baz> = v.clone();
+ let foo: Rc<dyn Foo> = baz;
+ assert_eq!(*foo, 1);
+ assert_eq!(foo.a(), 100);
+ assert_eq!(foo.z(), 11);
+ assert_eq!(foo.y(), 12);
+
+ let baz: Rc<dyn Baz> = v.clone();
+ let bar: Rc<dyn Bar> = baz;
+ let foo: Rc<dyn Foo> = bar;
+ assert_eq!(*foo, 1);
+ assert_eq!(foo.a(), 100);
+ assert_eq!(foo.z(), 11);
+ assert_eq!(foo.y(), 12);
+ assert_eq!(foo.z(), 11);
+ assert_eq!(foo.y(), 12);
+}
+
+fn test_arc() {
+ let v = Arc::new(1);
+
+ let baz: Arc<dyn Baz> = v.clone();
+ assert_eq!(*baz, 1);
+ assert_eq!(baz.a(), 100);
+ assert_eq!(baz.b(), 200);
+ assert_eq!(baz.c(), 300);
+ assert_eq!(baz.z(), 11);
+ assert_eq!(baz.y(), 12);
+ assert_eq!(baz.w(), 21);
+
+ let baz: Arc<dyn Baz> = v.clone();
+ let bar: Arc<dyn Bar> = baz;
+ assert_eq!(*bar, 1);
+ assert_eq!(bar.a(), 100);
+ assert_eq!(bar.b(), 200);
+ assert_eq!(bar.z(), 11);
+ assert_eq!(bar.y(), 12);
+ assert_eq!(bar.w(), 21);
+
+ let baz: Arc<dyn Baz> = v.clone();
+ let foo: Arc<dyn Foo> = baz;
+ assert_eq!(*foo, 1);
+ assert_eq!(foo.a(), 100);
+ assert_eq!(foo.z(), 11);
+ assert_eq!(foo.y(), 12);
+
+ let baz: Arc<dyn Baz> = v.clone();
+ let bar: Arc<dyn Bar> = baz;
+ let foo: Arc<dyn Foo> = bar;
+ assert_eq!(*foo, 1);
+ assert_eq!(foo.a(), 100);
+ assert_eq!(foo.z(), 11);
+ assert_eq!(foo.y(), 12);
+}
+
+fn main() {
+ test_box();
+ test_rc();
+ test_arc();
+}
--- /dev/null
+#![feature(trait_upcasting)]
+#![allow(incomplete_features)]
+
+trait Foo: PartialEq<i32> + std::fmt::Debug + Send + Sync {
+ fn a(&self) -> i32 {
+ 10
+ }
+
+ fn z(&self) -> i32 {
+ 11
+ }
+
+ fn y(&self) -> i32 {
+ 12
+ }
+}
+
+trait Bar: Foo {
+ fn b(&self) -> i32 {
+ 20
+ }
+
+ fn w(&self) -> i32 {
+ 21
+ }
+}
+
+trait Baz: Bar {
+ fn c(&self) -> i32 {
+ 30
+ }
+}
+
+impl Foo for i32 {
+ fn a(&self) -> i32 {
+ 100
+ }
+}
+
+impl Bar for i32 {
+ fn b(&self) -> i32 {
+ 200
+ }
+}
+
+impl Baz for i32 {
+ fn c(&self) -> i32 {
+ 300
+ }
+}
+
+fn main() {
+ let baz: &dyn Baz = &1;
+
+ let bar: &dyn Bar = baz;
+ bar.c();
+ //~^ ERROR no method named `c` found for reference `&dyn Bar` in the current scope [E0599]
+
+ let foo: &dyn Foo = baz;
+ foo.b();
+ //~^ ERROR no method named `b` found for reference `&dyn Foo` in the current scope [E0599]
+ foo.c();
+ //~^ ERROR no method named `c` found for reference `&dyn Foo` in the current scope [E0599]
+
+ let foo: &dyn Foo = bar;
+ foo.b();
+ //~^ ERROR no method named `b` found for reference `&dyn Foo` in the current scope [E0599]
+ foo.c();
+ //~^ ERROR no method named `c` found for reference `&dyn Foo` in the current scope [E0599]
+}
--- /dev/null
+error[E0599]: no method named `c` found for reference `&dyn Bar` in the current scope
+ --> $DIR/subtrait-method.rs:56:9
+ |
+LL | bar.c();
+ | ^ help: there is an associated function with a similar name: `a`
+ |
+ = help: items from traits can only be used if the trait is implemented and in scope
+note: `Baz` defines an item `c`, perhaps you need to implement it
+ --> $DIR/subtrait-method.rs:28:1
+ |
+LL | trait Baz: Bar {
+ | ^^^^^^^^^^^^^^
+
+error[E0599]: no method named `b` found for reference `&dyn Foo` in the current scope
+ --> $DIR/subtrait-method.rs:60:9
+ |
+LL | foo.b();
+ | ^ help: there is an associated function with a similar name: `a`
+ |
+ = help: items from traits can only be used if the trait is implemented and in scope
+note: `Bar` defines an item `b`, perhaps you need to implement it
+ --> $DIR/subtrait-method.rs:18:1
+ |
+LL | trait Bar: Foo {
+ | ^^^^^^^^^^^^^^
+
+error[E0599]: no method named `c` found for reference `&dyn Foo` in the current scope
+ --> $DIR/subtrait-method.rs:62:9
+ |
+LL | foo.c();
+ | ^ help: there is an associated function with a similar name: `a`
+ |
+ = help: items from traits can only be used if the trait is implemented and in scope
+note: `Baz` defines an item `c`, perhaps you need to implement it
+ --> $DIR/subtrait-method.rs:28:1
+ |
+LL | trait Baz: Bar {
+ | ^^^^^^^^^^^^^^
+
+error[E0599]: no method named `b` found for reference `&dyn Foo` in the current scope
+ --> $DIR/subtrait-method.rs:66:9
+ |
+LL | foo.b();
+ | ^ help: there is an associated function with a similar name: `a`
+ |
+ = help: items from traits can only be used if the trait is implemented and in scope
+note: `Bar` defines an item `b`, perhaps you need to implement it
+ --> $DIR/subtrait-method.rs:18:1
+ |
+LL | trait Bar: Foo {
+ | ^^^^^^^^^^^^^^
+
+error[E0599]: no method named `c` found for reference `&dyn Foo` in the current scope
+ --> $DIR/subtrait-method.rs:68:9
+ |
+LL | foo.c();
+ | ^ help: there is an associated function with a similar name: `a`
+ |
+ = help: items from traits can only be used if the trait is implemented and in scope
+note: `Baz` defines an item `c`, perhaps you need to implement it
+ --> $DIR/subtrait-method.rs:28:1
+ |
+LL | trait Baz: Bar {
+ | ^^^^^^^^^^^^^^
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0599`.
|
LL | impl F {
| ^ help: a trait with a similar name exists: `Fn`
- |
+ |
::: $SRC_DIR/core/src/ops/function.rs:LL:COL
|
LL | pub trait Fn<Args>: FnMut<Args> {
|
LL | let _ = match x {
| ^ pattern `Err(_)` not covered
- |
+ |
::: $SRC_DIR/core/src/result.rs:LL:COL
|
LL | Err(#[stable(feature = "rust1", since = "1.0.0")] E),
|
LL | let _ = match x {
| ^ pattern `Err(_)` not covered
- |
+ |
::: $SRC_DIR/core/src/result.rs:LL:COL
|
LL | Err(#[stable(feature = "rust1", since = "1.0.0")] E),
|
LL | let Ok(x) = x;
| ^^^^^ pattern `Err(_)` not covered
- |
+ |
::: $SRC_DIR/core/src/result.rs:LL:COL
|
LL | Err(#[stable(feature = "rust1", since = "1.0.0")] E),
|
LL | #[derive(Clone)]
| ^^^^^ the trait `Copy` is not implemented for `U1`
- |
+ |
::: $SRC_DIR/core/src/clone.rs:LL:COL
|
LL | pub struct AssertParamIsCopy<T: Copy + ?Sized> {
|
LL | #[derive(Clone)]
| ^^^^^ the trait `Copy` is not implemented for `U1`
- |
+ |
::: $SRC_DIR/core/src/clone.rs:LL:COL
|
LL | pub struct AssertParamIsCopy<T: Copy + ?Sized> {
LL | union U2 {
LL | a: PartialEqNotEq,
| ^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `PartialEqNotEq`
- |
+ |
::: $SRC_DIR/core/src/cmp.rs:LL:COL
|
LL | pub struct AssertParamIsEq<T: Eq + ?Sized> {
LL | union U2 {
LL | a: PartialEqNotEq,
| ^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `PartialEqNotEq`
- |
+ |
::: $SRC_DIR/core/src/cmp.rs:LL:COL
|
LL | pub struct AssertParamIsEq<T: Eq + ?Sized> {
// run-pass
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
#![allow(dead_code)]
#![allow(unused_variables)]
+++ /dev/null
-warning: unnecessary `unsafe` block
- --> $DIR/union-drop.rs:27:9
- |
-LL | unsafe { CHECK += 1; }
- | ^^^^^^ unnecessary `unsafe` block
- |
- = note: `#[warn(unused_unsafe)]` on by default
-
-warning: unnecessary `unsafe` block
- --> $DIR/union-drop.rs:33:9
- |
-LL | unsafe { CHECK += 1; }
- | ^^^^^^ unnecessary `unsafe` block
-
-warning: unnecessary `unsafe` block
- --> $DIR/union-drop.rs:40:5
- |
-LL | unsafe {
- | ^^^^^^ unnecessary `unsafe` block
-
-warning: 3 warnings emitted
-
...
LL | let _z = y.clone();
| ^^^^^ method cannot be called on `Box<dyn Foo>` due to unsatisfied trait bounds
- |
+ |
::: $SRC_DIR/alloc/src/boxed.rs:LL:COL
|
LL | / pub struct Box<
...
LL | let _j = i.clone();
| ^^^^^ method cannot be called on `Box<R>` due to unsatisfied trait bounds
- |
+ |
::: $SRC_DIR/alloc/src/boxed.rs:LL:COL
|
LL | / pub struct Box<
--- /dev/null
+// Regression test for #87414.
+
+// check-pass
+// compile-flags: -Zthir-unsafeck
+
+fn bad<T>() -> Box<dyn Iterator<Item = [(); { |x: u32| { x }; 4 }]>> { todo!() }
+
+fn foo() -> [(); { |x: u32| { x }; 4 }] { todo!() }
+fn bar() { let _: [(); { |x: u32| { x }; 4 }]; }
+
+// This one should not cause any errors either:
+unsafe fn unsf() {}
+fn bad2<T>() -> Box<dyn Iterator<Item = [(); { unsafe { || { unsf() } }; 4 }]>> { todo!() }
+
+fn main() {}
--- /dev/null
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+ --> $DIR/union-assignop.rs:20:5
+ |
+LL | foo.a += 5;
+ | ^^^^^^^^^^ access to union field
+ |
+ = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+ --> $DIR/union-assignop.rs:21:5
+ |
+LL | foo.b += Dropping;
+ | ^^^^^ access to union field
+ |
+ = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: assignment to union field that might need dropping is unsafe and requires unsafe function or block
+ --> $DIR/union-assignop.rs:22:5
+ |
+LL | foo.b = Dropping;
+ | ^^^^^^^^^^^^^^^^ assignment to union field that might need dropping
+ |
+ = note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+ --> $DIR/union-assignop.rs:23:5
+ |
+LL | foo.a;
+ | ^^^^^ access to union field
+ |
+ = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+ --> $DIR/union-assignop.rs:25:5
+ |
+LL | foo.b;
+ | ^^^^^ access to union field
+ |
+ = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+ --> $DIR/union-assignop.rs:27:13
+ |
+LL | foo.b = foo.b;
+ | ^^^^^ access to union field
+ |
+ = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: assignment to union field that might need dropping is unsafe and requires unsafe function or block
+ --> $DIR/union-assignop.rs:27:5
+ |
+LL | foo.b = foo.b;
+ | ^^^^^^^^^^^^^ assignment to union field that might need dropping
+ |
+ = note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0133`.
--- /dev/null
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
+#![feature(untagged_unions)]
+
+use std::ops::AddAssign;
+
+struct Dropping;
+impl AddAssign for Dropping {
+ fn add_assign(&mut self, _: Self) {}
+}
+
+union Foo {
+ a: u8, // non-dropping
+ b: Dropping, // treated as dropping
+}
+
+fn main() {
+ let mut foo = Foo { a: 42 };
+ foo.a += 5; //~ ERROR access to union field is unsafe
+ foo.b += Dropping; //~ ERROR access to union field is unsafe
+ foo.b = Dropping; //~ ERROR assignment to union field that might need dropping is unsafe
+ foo.a; //~ ERROR access to union field is unsafe
+ let foo = Foo { a: 42 };
+ foo.b; //~ ERROR access to union field is unsafe
+ let mut foo = Foo { a: 42 };
+ foo.b = foo.b;
+ //~^ ERROR access to union field is unsafe
+ //~| ERROR assignment to union field that might need dropping
+}
--- /dev/null
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+ --> $DIR/union-assignop.rs:20:5
+ |
+LL | foo.a += 5;
+ | ^^^^^ access to union field
+ |
+ = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+ --> $DIR/union-assignop.rs:21:5
+ |
+LL | foo.b += Dropping;
+ | ^^^^^ access to union field
+ |
+ = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: assignment to union field that might need dropping is unsafe and requires unsafe function or block
+ --> $DIR/union-assignop.rs:22:5
+ |
+LL | foo.b = Dropping;
+ | ^^^^^^^^^^^^^^^^ assignment to union field that might need dropping
+ |
+ = note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+ --> $DIR/union-assignop.rs:23:5
+ |
+LL | foo.a;
+ | ^^^^^ access to union field
+ |
+ = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+ --> $DIR/union-assignop.rs:25:5
+ |
+LL | foo.b;
+ | ^^^^^ access to union field
+ |
+ = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: assignment to union field that might need dropping is unsafe and requires unsafe function or block
+ --> $DIR/union-assignop.rs:27:5
+ |
+LL | foo.b = foo.b;
+ | ^^^^^^^^^^^^^ assignment to union field that might need dropping
+ |
+ = note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+ --> $DIR/union-assignop.rs:27:13
+ |
+LL | foo.b = foo.b;
+ | ^^^^^ access to union field
+ |
+ = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0133`.
|
LL | impl Foo for Option<[u8]> {}
| ^^^^^^^^^^^^ doesn't have a size known at compile-time
- |
+ |
::: $SRC_DIR/core/src/option.rs:LL:COL
|
LL | pub enum Option<T> {
|
LL | let _ = xc_private_method_lib::Struct::static_meth_struct();
| ^^^^^^^^^^^^^^^^^^ private associated function
- |
+ |
::: $DIR/auxiliary/xc-private-method-lib.rs:8:5
|
LL | fn static_meth_struct() -> Struct {
|
LL | let _ = xc_private_method_lib::Enum::static_meth_enum();
| ^^^^^^^^^^^^^^^^ private associated function
- |
+ |
::: $DIR/auxiliary/xc-private-method-lib.rs:23:5
|
LL | fn static_meth_enum() -> Enum {
|
LL | let _ = xc_private_method_lib::Struct{ x: 10 }.meth_struct();
| ^^^^^^^^^^^ private associated function
- |
+ |
::: $DIR/auxiliary/xc-private-method-lib.rs:12:5
|
LL | fn meth_struct(&self) -> isize {
|
LL | let _ = xc_private_method_lib::Enum::Variant1(20).meth_enum();
| ^^^^^^^^^ private associated function
- |
+ |
::: $DIR/auxiliary/xc-private-method-lib.rs:27:5
|
LL | fn meth_enum(&self) -> isize {
|
LL | let _ = xcrate_unit_struct::StructWithFields;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `xcrate_unit_struct::StructWithFields { foo: val }`
- |
+ |
::: $DIR/auxiliary/xcrate_unit_struct.rs:20:1
|
LL | pub struct StructWithFields {
static TARGETS: &[&str] = &[
"aarch64-apple-darwin",
"aarch64-apple-ios",
+ "aarch64-apple-ios-sim",
"aarch64-fuchsia",
"aarch64-linux-android",
"aarch64-pc-windows-msvc",
-Subproject commit d21c22870e58499d6c31f1bef3bf1255eb021666
+Subproject commit cc17afbb0067b1f57d8882640f63b2168d5b7624
use rustc_parse::parser::ForceCollect;
use rustc_session::parse::ParseSess;
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::LocalDefId;
use rustc_span::edition::Edition;
use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span};
use rustc_span::{sym, FileName, Pos};
fpu.visit_expr(&body.value);
lint_for_missing_headers(
cx,
- item.hir_id(),
+ item.def_id,
item.span,
sig,
headers,
let headers = check_attrs(cx, &self.valid_idents, attrs);
if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
if !in_external_macro(cx.tcx.sess, item.span) {
- lint_for_missing_headers(cx, item.hir_id(), item.span, sig, headers, None, None);
+ lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, None, None);
}
}
}
fpu.visit_expr(&body.value);
lint_for_missing_headers(
cx,
- item.hir_id(),
+ item.def_id,
item.span,
sig,
headers,
fn lint_for_missing_headers<'tcx>(
cx: &LateContext<'tcx>,
- hir_id: hir::HirId,
+ def_id: LocalDefId,
span: impl Into<MultiSpan> + Copy,
sig: &hir::FnSig<'_>,
headers: DocHeaders,
body_id: Option<hir::BodyId>,
panic_span: Option<Span>,
) {
- if !cx.access_levels.is_exported(hir_id) {
+ if !cx.access_levels.is_exported(def_id) {
return; // Private functions do not require doc comments
}
if !headers.safety && sig.header.unsafety == hir::Unsafety::Unsafe {
);
}
if !headers.errors {
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) {
span_lint(
cx,
}
}
if let ItemKind::Enum(ref def, _) = item.kind {
- if !(self.avoid_breaking_exported_api && cx.access_levels.is_exported(item.hir_id())) {
+ if !(self.avoid_breaking_exported_api && cx.access_levels.is_exported(item.def_id)) {
check_variant(cx, self.threshold, def, &item_name, item_name_chars, item.span);
}
}
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
if_chain! {
if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind;
- if cx.access_levels.is_exported(item.hir_id());
+ if cx.access_levels.is_exported(item.def_id);
let attrs = cx.tcx.hir().attrs(item.hir_id());
if !attrs.iter().any(|a| a.has_name(sym::non_exhaustive));
then {
use rustc_ast::ast::Attribute;
use rustc_errors::Applicability;
-use rustc_hir::def_id::DefIdSet;
+use rustc_hir::def_id::{DefIdSet, LocalDefId};
use rustc_hir::{self as hir, def::Res, intravisit, QPath};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::{
let attrs = cx.tcx.hir().attrs(item.hir_id());
let attr = must_use_attr(attrs);
if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind {
- let is_public = cx.access_levels.is_exported(item.hir_id());
+ let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
if let Some(attr) = attr {
check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
sig.decl,
cx.tcx.hir().body(*body_id),
item.span,
- item.hir_id(),
+ item.def_id,
item.span.with_hi(sig.decl.output.span().hi()),
"this function could have a `#[must_use]` attribute",
);
pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind {
- let is_public = cx.access_levels.is_exported(item.hir_id());
+ let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
let attrs = cx.tcx.hir().attrs(item.hir_id());
let attr = must_use_attr(attrs);
sig.decl,
cx.tcx.hir().body(*body_id),
item.span,
- item.hir_id(),
+ item.def_id,
item.span.with_hi(sig.decl.output.span().hi()),
"this method could have a `#[must_use]` attribute",
);
pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind {
- let is_public = cx.access_levels.is_exported(item.hir_id());
+ let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
let attrs = cx.tcx.hir().attrs(item.hir_id());
sig.decl,
body,
item.span,
- item.hir_id(),
+ item.def_id,
item.span.with_hi(sig.decl.output.span().hi()),
"this method could have a `#[must_use]` attribute",
);
decl: &'tcx hir::FnDecl<'_>,
body: &'tcx hir::Body<'_>,
item_span: Span,
- item_id: hir::HirId,
+ item_id: LocalDefId,
fn_span: Span,
msg: &str,
) {
|| in_external_macro(cx.sess(), item_span)
|| returns_unit(decl)
|| !cx.access_levels.is_exported(item_id)
- || is_must_use_ty(cx, return_ty(cx, item_id))
+ || is_must_use_ty(cx, return_ty(cx, cx.tcx.hir().local_def_id_to_hir_id(item_id)))
{
return;
}
use rustc_hir::{self as hir, intravisit, HirIdSet};
use rustc_lint::LateContext;
use rustc_middle::{hir::map::Map, ty};
+use rustc_span::def_id::LocalDefId;
use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::type_is_unsafe_function;
intravisit::FnKind::Closure => return,
};
- check_raw_ptr(cx, unsafety, decl, body, hir_id);
+ check_raw_ptr(cx, unsafety, decl, body, cx.tcx.hir().local_def_id(hir_id));
}
pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind {
let body = cx.tcx.hir().body(eid);
- check_raw_ptr(cx, sig.header.unsafety, sig.decl, body, item.hir_id());
+ check_raw_ptr(cx, sig.header.unsafety, sig.decl, body, item.def_id);
}
}
unsafety: hir::Unsafety,
decl: &'tcx hir::FnDecl<'tcx>,
body: &'tcx hir::Body<'tcx>,
- hir_id: hir::HirId,
+ def_id: LocalDefId,
) {
let expr = &body.value;
- if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(hir_id) {
+ if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(def_id) {
let raw_ptrs = iter_input_pats(decl, body)
.zip(decl.inputs.iter())
.filter_map(|(arg, ty)| raw_ptr_arg(arg, ty))
pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind {
- let is_public = cx.access_levels.is_exported(item.hir_id());
+ let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
if is_public {
check_result_unit_err(cx, sig.decl, item.span, fn_header_span);
pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
if let hir::ImplItemKind::Fn(ref sig, _) = item.kind {
- let is_public = cx.access_levels.is_exported(item.hir_id());
+ let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
if is_public && trait_ref_of_method(cx, item.hir_id()).is_none() {
check_result_unit_err(cx, sig.decl, item.span, fn_header_span);
pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
- let is_public = cx.access_levels.is_exported(item.hir_id());
+ let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
if is_public {
check_result_unit_err(cx, sig.decl, item.span, fn_header_span);
}
}
- if !cx.access_levels.is_exported(item.hir_id()) {
+ if !cx.access_levels.is_exported(item.def_id) {
return;
}
if item.ident.name == sym::len;
if let ImplItemKind::Fn(sig, _) = &item.kind;
if sig.decl.implicit_self.has_implicit_self();
- if cx.access_levels.is_exported(item.hir_id());
+ if cx.access_levels.is_exported(item.def_id);
if matches!(sig.decl.output, FnRetTy::Return(_));
if let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id());
if imp.of_trait.is_none();
}
}
- if cx.access_levels.is_exported(visited_trait.hir_id())
+ if cx.access_levels.is_exported(visited_trait.def_id)
&& trait_items.iter().any(|i| is_named_self(cx, i, sym::len))
{
let mut current_and_super_traits = DefIdSet::default();
None,
None,
),
- Some(is_empty)
- if !cx
- .access_levels
- .is_exported(cx.tcx.hir().local_def_id_to_hir_id(is_empty.def_id.expect_local())) =>
- {
+ Some(is_empty) if !cx.access_levels.is_exported(is_empty.def_id.expect_local()) => {
(
format!(
"{} `{}` has a public `len` method, but a private `is_empty` method",
then {
// if this impl block implements a trait, lint in trait definition instead
- if !implements_trait && cx.access_levels.is_exported(impl_item.hir_id()) {
+ if !implements_trait && cx.access_levels.is_exported(impl_item.def_id) {
// check missing trait implementations
for method_config in &TRAIT_METHODS {
if name == method_config.method_name &&
if sig.decl.implicit_self.has_implicit_self()
&& !(self.avoid_breaking_exported_api
- && cx.access_levels.is_exported(impl_item.hir_id()))
+ && cx.access_levels.is_exported(impl_item.def_id))
{
wrong_self_convention::check(
cx,
return;
}
- if !cx.access_levels.is_exported(it.hir_id()) {
+ if !cx.access_levels.is_exported(it.def_id) {
return;
}
match it.kind {
}
// If the item being implemented is not exported, then we don't need #[inline]
- if !cx.access_levels.is_exported(impl_item.hir_id()) {
+ if !cx.access_levels.is_exported(impl_item.def_id) {
return;
}
};
if let Some(trait_def_id) = trait_def_id {
- if trait_def_id.is_local() && !cx.access_levels.is_exported(impl_item.hir_id()) {
+ if trait_def_id.is_local() && !cx.access_levels.is_exported(impl_item.def_id) {
// If a trait is being implemented for an item, and the
// trait is not exported, we don't need #[inline]
return;
if_chain! {
if sig.decl.inputs.is_empty();
if name == sym::new;
- if cx.access_levels.is_reachable(id);
+ if cx.access_levels.is_reachable(impl_item.def_id);
let self_def_id = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id));
let self_ty = cx.tcx.type_of(self_def_id);
if TyS::same_type(self_ty, return_ty(cx, id));
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::LocalDefId;
use rustc_span::{sym, Span};
use rustc_target::abi::LayoutOf;
use rustc_target::spec::abi::Abi;
}
}
- fn check_poly_fn(&mut self, cx: &LateContext<'tcx>, hir_id: HirId, decl: &FnDecl<'_>, span: Option<Span>) {
- if self.avoid_breaking_exported_api && cx.access_levels.is_exported(hir_id) {
+ fn check_poly_fn(&mut self, cx: &LateContext<'tcx>, def_id: LocalDefId, decl: &FnDecl<'_>, span: Option<Span>) {
+ if self.avoid_breaking_exported_api && cx.access_levels.is_exported(def_id) {
return;
}
- let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
- let fn_sig = cx.tcx.fn_sig(fn_def_id);
+ let fn_sig = cx.tcx.fn_sig(def_id);
let fn_sig = cx.tcx.erase_late_bound_regions(fn_sig);
let fn_body = cx.enclosing_body.map(|id| cx.tcx.hir().body(id));
}
if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind {
- self.check_poly_fn(cx, item.hir_id(), &*method_sig.decl, None);
+ self.check_poly_fn(cx, item.def_id, &*method_sig.decl, None);
}
}
}
}
- self.check_poly_fn(cx, hir_id, decl, Some(span));
+ self.check_poly_fn(cx, cx.tcx.hir().local_def_id(hir_id), decl, Some(span));
}
}
impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if let VisibilityKind::Crate { .. } = item.vis.node {
- if !cx.access_levels.is_exported(item.hir_id()) {
+ if !cx.access_levels.is_exported(item.def_id) {
if let Some(false) = self.is_exported.last() {
let span = item.span.with_hi(item.ident.span.hi());
let descr = cx.tcx.def_kind(item.def_id).descr(item.def_id.to_def_id());
}
if let ItemKind::Mod { .. } = item.kind {
- self.is_exported.push(cx.access_levels.is_exported(item.hir_id()));
+ self.is_exported.push(cx.access_levels.is_exported(item.def_id));
}
}
// Abort if public function/method or closure.
match fn_kind {
FnKind::ItemFn(..) | FnKind::Method(..) => {
- if self.avoid_breaking_exported_api && cx.access_levels.is_exported(hir_id) {
+ let def_id = cx.tcx.hir().local_def_id(hir_id);
+ if self.avoid_breaking_exported_api && cx.access_levels.is_exported(def_id) {
return;
}
},
fn check_item(&mut self, cx: &LateContext<'_>, it: &Item<'_>) {
// do not lint public items or in macros
if in_external_macro(cx.sess(), it.span)
- || (self.avoid_breaking_exported_api && cx.access_levels.is_exported(it.hir_id()))
+ || (self.avoid_breaking_exported_api && cx.access_levels.is_exported(it.def_id))
{
return;
}
-Subproject commit 453e3ef7dab5aad6450bee09b2c02de94c5b18cb
+Subproject commit 99ec9c1707aad74b4a4a6d301f27fb1c19733f58
env_logger = "0.7.1"
[dependencies.mdbook]
-version = "0.4.11"
+version = "0.4.12"
default-features = false
features = ["search"]