mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 15:23:46 +00:00
Auto merge of #3042 - rust-lang:rustup-2023-08-29, r=RalfJung
Automatic sync from rustc
This commit is contained in:
commit
d9c11c65ee
@ -236,7 +236,7 @@ enum ImplTraitContext {
|
||||
ReturnPositionOpaqueTy {
|
||||
/// Origin: Either OpaqueTyOrigin::FnReturn or OpaqueTyOrigin::AsyncFn,
|
||||
origin: hir::OpaqueTyOrigin,
|
||||
in_trait: bool,
|
||||
fn_kind: FnDeclKind,
|
||||
},
|
||||
/// Impl trait in type aliases.
|
||||
TypeAliasesOpaqueTy { in_assoc_ty: bool },
|
||||
@ -312,7 +312,7 @@ impl std::fmt::Display for ImplTraitPosition {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
enum FnDeclKind {
|
||||
Fn,
|
||||
Inherent,
|
||||
@ -1401,13 +1401,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
TyKind::ImplTrait(def_node_id, bounds) => {
|
||||
let span = t.span;
|
||||
match itctx {
|
||||
ImplTraitContext::ReturnPositionOpaqueTy { origin, in_trait } => self
|
||||
ImplTraitContext::ReturnPositionOpaqueTy { origin, fn_kind } => self
|
||||
.lower_opaque_impl_trait(
|
||||
span,
|
||||
*origin,
|
||||
*def_node_id,
|
||||
bounds,
|
||||
*in_trait,
|
||||
Some(*fn_kind),
|
||||
itctx,
|
||||
),
|
||||
&ImplTraitContext::TypeAliasesOpaqueTy { in_assoc_ty } => self
|
||||
@ -1416,7 +1416,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
hir::OpaqueTyOrigin::TyAlias { in_assoc_ty },
|
||||
*def_node_id,
|
||||
bounds,
|
||||
false,
|
||||
None,
|
||||
itctx,
|
||||
),
|
||||
ImplTraitContext::Universal => {
|
||||
@ -1523,7 +1523,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
origin: hir::OpaqueTyOrigin,
|
||||
opaque_ty_node_id: NodeId,
|
||||
bounds: &GenericBounds,
|
||||
in_trait: bool,
|
||||
fn_kind: Option<FnDeclKind>,
|
||||
itctx: &ImplTraitContext,
|
||||
) -> hir::TyKind<'hir> {
|
||||
// Make sure we know that some funky desugaring has been going on here.
|
||||
@ -1540,10 +1540,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
Vec::new()
|
||||
}
|
||||
hir::OpaqueTyOrigin::FnReturn(..) => {
|
||||
// in fn return position, like the `fn test<'a>() -> impl Debug + 'a`
|
||||
// example, we only need to duplicate lifetimes that appear in the
|
||||
// bounds, since those are the only ones that are captured by the opaque.
|
||||
lifetime_collector::lifetimes_in_bounds(&self.resolver, bounds)
|
||||
if let FnDeclKind::Impl | FnDeclKind::Trait =
|
||||
fn_kind.expect("expected RPITs to be lowered with a FnKind")
|
||||
{
|
||||
// return-position impl trait in trait was decided to capture all
|
||||
// in-scope lifetimes, which we collect for all opaques during resolution.
|
||||
self.resolver
|
||||
.take_extra_lifetime_params(opaque_ty_node_id)
|
||||
.into_iter()
|
||||
.map(|(ident, id, _)| Lifetime { id, ident })
|
||||
.collect()
|
||||
} else {
|
||||
// in fn return position, like the `fn test<'a>() -> impl Debug + 'a`
|
||||
// example, we only need to duplicate lifetimes that appear in the
|
||||
// bounds, since those are the only ones that are captured by the opaque.
|
||||
lifetime_collector::lifetimes_in_bounds(&self.resolver, bounds)
|
||||
}
|
||||
}
|
||||
hir::OpaqueTyOrigin::AsyncFn(..) => {
|
||||
unreachable!("should be using `lower_async_fn_ret_ty`")
|
||||
@ -1554,7 +1566,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
self.lower_opaque_inner(
|
||||
opaque_ty_node_id,
|
||||
origin,
|
||||
in_trait,
|
||||
matches!(fn_kind, Some(FnDeclKind::Trait)),
|
||||
captured_lifetimes_to_duplicate,
|
||||
span,
|
||||
opaque_ty_span,
|
||||
@ -1802,12 +1814,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
}
|
||||
|
||||
let fn_def_id = self.local_def_id(fn_node_id);
|
||||
self.lower_async_fn_ret_ty(
|
||||
&decl.output,
|
||||
fn_def_id,
|
||||
ret_id,
|
||||
matches!(kind, FnDeclKind::Trait),
|
||||
)
|
||||
self.lower_async_fn_ret_ty(&decl.output, fn_def_id, ret_id, kind)
|
||||
} else {
|
||||
match &decl.output {
|
||||
FnRetTy::Ty(ty) => {
|
||||
@ -1815,7 +1822,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let fn_def_id = self.local_def_id(fn_node_id);
|
||||
ImplTraitContext::ReturnPositionOpaqueTy {
|
||||
origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
|
||||
in_trait: matches!(kind, FnDeclKind::Trait),
|
||||
fn_kind: kind,
|
||||
}
|
||||
} else {
|
||||
let position = match kind {
|
||||
@ -1883,7 +1890,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
output: &FnRetTy,
|
||||
fn_def_id: LocalDefId,
|
||||
opaque_ty_node_id: NodeId,
|
||||
in_trait: bool,
|
||||
fn_kind: FnDeclKind,
|
||||
) -> hir::FnRetTy<'hir> {
|
||||
let span = self.lower_span(output.span());
|
||||
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, None);
|
||||
@ -1898,7 +1905,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let opaque_ty_ref = self.lower_opaque_inner(
|
||||
opaque_ty_node_id,
|
||||
hir::OpaqueTyOrigin::AsyncFn(fn_def_id),
|
||||
in_trait,
|
||||
matches!(fn_kind, FnDeclKind::Trait),
|
||||
captured_lifetimes,
|
||||
span,
|
||||
opaque_ty_span,
|
||||
@ -1906,7 +1913,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let future_bound = this.lower_async_fn_output_type_to_future_bound(
|
||||
output,
|
||||
span,
|
||||
if in_trait && !this.tcx.features().return_position_impl_trait_in_trait {
|
||||
if let FnDeclKind::Trait = fn_kind
|
||||
&& !this.tcx.features().return_position_impl_trait_in_trait
|
||||
{
|
||||
ImplTraitContext::FeatureGated(
|
||||
ImplTraitPosition::TraitReturn,
|
||||
sym::return_position_impl_trait_in_trait,
|
||||
@ -1914,7 +1923,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
} else {
|
||||
ImplTraitContext::ReturnPositionOpaqueTy {
|
||||
origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
|
||||
in_trait,
|
||||
fn_kind,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -217,7 +217,7 @@ pub fn expand_include_bytes(
|
||||
};
|
||||
match cx.source_map().load_binary_file(&file) {
|
||||
Ok(bytes) => {
|
||||
let expr = cx.expr(sp, ast::ExprKind::IncludedBytes(bytes.into()));
|
||||
let expr = cx.expr(sp, ast::ExprKind::IncludedBytes(bytes));
|
||||
base::MacEager::expr(expr)
|
||||
}
|
||||
Err(e) => {
|
||||
|
@ -39,7 +39,7 @@ fn clif_sig_from_fn_abi<'tcx>(
|
||||
pub(crate) fn conv_to_call_conv(sess: &Session, c: Conv, default_call_conv: CallConv) -> CallConv {
|
||||
match c {
|
||||
Conv::Rust | Conv::C => default_call_conv,
|
||||
Conv::RustCold => CallConv::Cold,
|
||||
Conv::Cold | Conv::PreserveMost | Conv::PreserveAll => CallConv::Cold,
|
||||
Conv::X86_64SysV => CallConv::SystemV,
|
||||
Conv::X86_64Win64 => CallConv::WindowsFastcall,
|
||||
|
||||
|
@ -125,8 +125,8 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
||||
PassMode::Ignore => continue,
|
||||
PassMode::Direct(_) => arg.layout.immediate_gcc_type(cx),
|
||||
PassMode::Pair(..) => {
|
||||
argument_tys.push(arg.layout.scalar_pair_element_gcc_type(cx, 0, true));
|
||||
argument_tys.push(arg.layout.scalar_pair_element_gcc_type(cx, 1, true));
|
||||
argument_tys.push(arg.layout.scalar_pair_element_gcc_type(cx, 0));
|
||||
argument_tys.push(arg.layout.scalar_pair_element_gcc_type(cx, 1));
|
||||
continue;
|
||||
}
|
||||
PassMode::Indirect { extra_attrs: Some(_), .. } => {
|
||||
|
@ -821,7 +821,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
||||
|
||||
let mut load = |i, scalar: &abi::Scalar, align| {
|
||||
let llptr = self.struct_gep(pair_type, place.llval, i as u64);
|
||||
let llty = place.layout.scalar_pair_element_gcc_type(self, i, false);
|
||||
let llty = place.layout.scalar_pair_element_gcc_type(self, i);
|
||||
let load = self.load(llty, llptr, align);
|
||||
scalar_load_metadata(self, load, scalar);
|
||||
if scalar.is_bool() { self.trunc(load, self.type_i1()) } else { load }
|
||||
|
@ -55,7 +55,7 @@ impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
||||
_fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
|
||||
_llfn: RValue<'gcc>,
|
||||
_mir: &mir::Body<'tcx>,
|
||||
) -> Option<FunctionDebugContext<'tcx, Self::DIScope, Self::DILocation>> {
|
||||
) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>> {
|
||||
// TODO(antoyo)
|
||||
None
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use gccjit::{Struct, Type};
|
||||
use crate::rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
|
||||
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_target::abi::{self, Abi, Align, F32, F64, FieldsShape, Int, Integer, Pointer, PointeeInfo, Size, TyAbiInterface, Variants};
|
||||
use rustc_target::abi::call::{CastTarget, FnAbi, Reg};
|
||||
@ -74,8 +74,8 @@ fn uncached_gcc_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout
|
||||
Abi::ScalarPair(..) => {
|
||||
return cx.type_struct(
|
||||
&[
|
||||
layout.scalar_pair_element_gcc_type(cx, 0, false),
|
||||
layout.scalar_pair_element_gcc_type(cx, 1, false),
|
||||
layout.scalar_pair_element_gcc_type(cx, 0),
|
||||
layout.scalar_pair_element_gcc_type(cx, 1),
|
||||
],
|
||||
false,
|
||||
);
|
||||
@ -150,7 +150,7 @@ pub trait LayoutGccExt<'tcx> {
|
||||
fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>;
|
||||
fn immediate_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>;
|
||||
fn scalar_gcc_type_at<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, scalar: &abi::Scalar, offset: Size) -> Type<'gcc>;
|
||||
fn scalar_pair_element_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, index: usize, immediate: bool) -> Type<'gcc>;
|
||||
fn scalar_pair_element_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, index: usize) -> Type<'gcc>;
|
||||
fn gcc_field_index(&self, index: usize) -> u64;
|
||||
fn pointee_info_at<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, offset: Size) -> Option<PointeeInfo>;
|
||||
}
|
||||
@ -182,23 +182,16 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
|
||||
/// of that field's type - this is useful for taking the address of
|
||||
/// that field and ensuring the struct has the right alignment.
|
||||
fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> {
|
||||
// This must produce the same result for `repr(transparent)` wrappers as for the inner type!
|
||||
// In other words, this should generally not look at the type at all, but only at the
|
||||
// layout.
|
||||
if let Abi::Scalar(ref scalar) = self.abi {
|
||||
// Use a different cache for scalars because pointers to DSTs
|
||||
// can be either fat or thin (data pointers of fat pointers).
|
||||
if let Some(&ty) = cx.scalar_types.borrow().get(&self.ty) {
|
||||
return ty;
|
||||
}
|
||||
let ty =
|
||||
match *self.ty.kind() {
|
||||
ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => {
|
||||
cx.type_ptr_to(cx.layout_of(ty).gcc_type(cx))
|
||||
}
|
||||
ty::Adt(def, _) if def.is_box() => {
|
||||
cx.type_ptr_to(cx.layout_of(self.ty.boxed_ty()).gcc_type(cx))
|
||||
}
|
||||
ty::FnPtr(sig) => cx.fn_ptr_backend_type(&cx.fn_abi_of_fn_ptr(sig, ty::List::empty())),
|
||||
_ => self.scalar_gcc_type_at(cx, scalar, Size::ZERO),
|
||||
};
|
||||
let ty = self.scalar_gcc_type_at(cx, scalar, Size::ZERO);
|
||||
cx.scalar_types.borrow_mut().insert(self.ty, ty);
|
||||
return ty;
|
||||
}
|
||||
@ -272,23 +265,10 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn scalar_pair_element_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, index: usize, immediate: bool) -> Type<'gcc> {
|
||||
// TODO(antoyo): remove llvm hack:
|
||||
// HACK(eddyb) special-case fat pointers until LLVM removes
|
||||
// pointee types, to avoid bitcasting every `OperandRef::deref`.
|
||||
match self.ty.kind() {
|
||||
ty::Ref(..) | ty::RawPtr(_) => {
|
||||
return self.field(cx, index).gcc_type(cx);
|
||||
}
|
||||
// only wide pointer boxes are handled as pointers
|
||||
// thin pointer boxes with scalar allocators are handled by the general logic below
|
||||
ty::Adt(def, args) if def.is_box() && cx.layout_of(args.type_at(1)).is_zst() => {
|
||||
let ptr_ty = Ty::new_mut_ptr(cx.tcx,self.ty.boxed_ty());
|
||||
return cx.layout_of(ptr_ty).scalar_pair_element_gcc_type(cx, index, immediate);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
fn scalar_pair_element_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, index: usize) -> Type<'gcc> {
|
||||
// This must produce the same result for `repr(transparent)` wrappers as for the inner type!
|
||||
// In other words, this should generally not look at the type at all, but only at the
|
||||
// layout.
|
||||
let (a, b) = match self.abi {
|
||||
Abi::ScalarPair(ref a, ref b) => (a, b),
|
||||
_ => bug!("TyAndLayout::scalar_pair_element_llty({:?}): not applicable", self),
|
||||
@ -367,8 +347,8 @@ impl<'gcc, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
||||
layout.gcc_field_index(index)
|
||||
}
|
||||
|
||||
fn scalar_pair_element_backend_type(&self, layout: TyAndLayout<'tcx>, index: usize, immediate: bool) -> Type<'gcc> {
|
||||
layout.scalar_pair_element_gcc_type(self, index, immediate)
|
||||
fn scalar_pair_element_backend_type(&self, layout: TyAndLayout<'tcx>, index: usize, _immediate: bool) -> Type<'gcc> {
|
||||
layout.scalar_pair_element_gcc_type(self, index)
|
||||
}
|
||||
|
||||
fn cast_backend_type(&self, ty: &CastTarget) -> Type<'gcc> {
|
||||
|
@ -571,7 +571,9 @@ impl From<Conv> for llvm::CallConv {
|
||||
Conv::C | Conv::Rust | Conv::CCmseNonSecureCall | Conv::RiscvInterrupt { .. } => {
|
||||
llvm::CCallConv
|
||||
}
|
||||
Conv::RustCold => llvm::ColdCallConv,
|
||||
Conv::Cold => llvm::ColdCallConv,
|
||||
Conv::PreserveMost => llvm::PreserveMost,
|
||||
Conv::PreserveAll => llvm::PreserveAll,
|
||||
Conv::AmdGpuKernel => llvm::AmdGpuKernel,
|
||||
Conv::AvrInterrupt => llvm::AvrInterrupt,
|
||||
Conv::AvrNonBlockingInterrupt => llvm::AvrNonBlockingInterrupt,
|
||||
|
@ -20,7 +20,7 @@ pub fn compute_mir_scopes<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
instance: Instance<'tcx>,
|
||||
mir: &Body<'tcx>,
|
||||
debug_context: &mut FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>,
|
||||
debug_context: &mut FunctionDebugContext<&'ll DIScope, &'ll DILocation>,
|
||||
) {
|
||||
// Find all scopes with variables defined in them.
|
||||
let variables = if cx.sess().opts.debuginfo == DebugInfo::Full {
|
||||
@ -51,7 +51,7 @@ fn make_mir_scope<'ll, 'tcx>(
|
||||
instance: Instance<'tcx>,
|
||||
mir: &Body<'tcx>,
|
||||
variables: &Option<BitSet<SourceScope>>,
|
||||
debug_context: &mut FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>,
|
||||
debug_context: &mut FunctionDebugContext<&'ll DIScope, &'ll DILocation>,
|
||||
instantiated: &mut BitSet<SourceScope>,
|
||||
scope: SourceScope,
|
||||
) {
|
||||
@ -84,6 +84,7 @@ fn make_mir_scope<'ll, 'tcx>(
|
||||
}
|
||||
|
||||
let loc = cx.lookup_debug_loc(scope_data.span.lo());
|
||||
let file_metadata = file_metadata(cx, &loc.file);
|
||||
|
||||
let dbg_scope = match scope_data.inlined {
|
||||
Some((callee, _)) => {
|
||||
@ -94,26 +95,18 @@ fn make_mir_scope<'ll, 'tcx>(
|
||||
ty::ParamEnv::reveal_all(),
|
||||
ty::EarlyBinder::bind(callee),
|
||||
);
|
||||
debug_context.inlined_function_scopes.entry(callee).or_insert_with(|| {
|
||||
let callee_fn_abi = cx.fn_abi_of_instance(callee, ty::List::empty());
|
||||
cx.dbg_scope_fn(callee, callee_fn_abi, None)
|
||||
})
|
||||
}
|
||||
None => {
|
||||
let file_metadata = file_metadata(cx, &loc.file);
|
||||
debug_context
|
||||
.lexical_blocks
|
||||
.entry((parent_scope.dbg_scope, loc.line, loc.col, file_metadata))
|
||||
.or_insert_with(|| unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateLexicalBlock(
|
||||
DIB(cx),
|
||||
parent_scope.dbg_scope,
|
||||
file_metadata,
|
||||
loc.line,
|
||||
loc.col,
|
||||
)
|
||||
})
|
||||
let callee_fn_abi = cx.fn_abi_of_instance(callee, ty::List::empty());
|
||||
cx.dbg_scope_fn(callee, callee_fn_abi, None)
|
||||
}
|
||||
None => unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateLexicalBlock(
|
||||
DIB(cx),
|
||||
parent_scope.dbg_scope,
|
||||
file_metadata,
|
||||
loc.line,
|
||||
loc.col,
|
||||
)
|
||||
},
|
||||
};
|
||||
|
||||
let inlined_at = scope_data.inlined.map(|(_, callsite_span)| {
|
||||
|
@ -5,7 +5,7 @@ use rustc_codegen_ssa::mir::debuginfo::VariableKind::*;
|
||||
use self::metadata::{file_metadata, type_di_node};
|
||||
use self::metadata::{UNKNOWN_COLUMN_NUMBER, UNKNOWN_LINE_NUMBER};
|
||||
use self::namespace::mangled_name_of_instance;
|
||||
use self::utils::{create_DIArray, debug_context, is_node_local_to_unit, DIB};
|
||||
use self::utils::{create_DIArray, is_node_local_to_unit, DIB};
|
||||
|
||||
use crate::abi::FnAbi;
|
||||
use crate::builder::Builder;
|
||||
@ -67,8 +67,6 @@ pub struct CodegenUnitDebugContext<'ll, 'tcx> {
|
||||
type_map: metadata::TypeMap<'ll, 'tcx>,
|
||||
namespace_map: RefCell<DefIdMap<&'ll DIScope>>,
|
||||
recursion_marker_type: OnceCell<&'ll DIType>,
|
||||
/// Maps a variable (name, scope, kind (argument or local), span) to its debug information.
|
||||
variables: RefCell<FxHashMap<(Symbol, &'ll DIScope, VariableKind, Span), &'ll DIVariable>>,
|
||||
}
|
||||
|
||||
impl Drop for CodegenUnitDebugContext<'_, '_> {
|
||||
@ -93,7 +91,6 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
|
||||
type_map: Default::default(),
|
||||
namespace_map: RefCell::new(Default::default()),
|
||||
recursion_marker_type: OnceCell::new(),
|
||||
variables: RefCell::new(Default::default()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -295,7 +292,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
|
||||
llfn: &'ll Value,
|
||||
mir: &mir::Body<'tcx>,
|
||||
) -> Option<FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>> {
|
||||
) -> Option<FunctionDebugContext<&'ll DIScope, &'ll DILocation>> {
|
||||
if self.sess().opts.debuginfo == DebugInfo::None {
|
||||
return None;
|
||||
}
|
||||
@ -307,11 +304,8 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
file_start_pos: BytePos(0),
|
||||
file_end_pos: BytePos(0),
|
||||
};
|
||||
let mut fn_debug_context = FunctionDebugContext {
|
||||
scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes),
|
||||
inlined_function_scopes: Default::default(),
|
||||
lexical_blocks: Default::default(),
|
||||
};
|
||||
let mut fn_debug_context =
|
||||
FunctionDebugContext { scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes) };
|
||||
|
||||
// Fill in all the scopes, with the information from the MIR body.
|
||||
compute_mir_scopes(self, instance, mir, &mut fn_debug_context);
|
||||
@ -612,39 +606,33 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
variable_kind: VariableKind,
|
||||
span: Span,
|
||||
) -> &'ll DIVariable {
|
||||
debug_context(self)
|
||||
.variables
|
||||
.borrow_mut()
|
||||
.entry((variable_name, scope_metadata, variable_kind, span))
|
||||
.or_insert_with(|| {
|
||||
let loc = self.lookup_debug_loc(span.lo());
|
||||
let file_metadata = file_metadata(self, &loc.file);
|
||||
let loc = self.lookup_debug_loc(span.lo());
|
||||
let file_metadata = file_metadata(self, &loc.file);
|
||||
|
||||
let type_metadata = type_di_node(self, variable_type);
|
||||
let type_metadata = type_di_node(self, variable_type);
|
||||
|
||||
let (argument_index, dwarf_tag) = match variable_kind {
|
||||
ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable),
|
||||
LocalVariable => (0, DW_TAG_auto_variable),
|
||||
};
|
||||
let align = self.align_of(variable_type);
|
||||
let (argument_index, dwarf_tag) = match variable_kind {
|
||||
ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable),
|
||||
LocalVariable => (0, DW_TAG_auto_variable),
|
||||
};
|
||||
let align = self.align_of(variable_type);
|
||||
|
||||
let name = variable_name.as_str();
|
||||
unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateVariable(
|
||||
DIB(self),
|
||||
dwarf_tag,
|
||||
scope_metadata,
|
||||
name.as_ptr().cast(),
|
||||
name.len(),
|
||||
file_metadata,
|
||||
loc.line,
|
||||
type_metadata,
|
||||
true,
|
||||
DIFlags::FlagZero,
|
||||
argument_index,
|
||||
align.bytes() as u32,
|
||||
)
|
||||
}
|
||||
})
|
||||
let name = variable_name.as_str();
|
||||
unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateVariable(
|
||||
DIB(self),
|
||||
dwarf_tag,
|
||||
scope_metadata,
|
||||
name.as_ptr().cast(),
|
||||
name.len(),
|
||||
file_metadata,
|
||||
loc.line,
|
||||
type_metadata,
|
||||
true,
|
||||
DIFlags::FlagZero,
|
||||
argument_index,
|
||||
align.bytes() as u32,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,12 +83,17 @@ pub enum LLVMModFlagBehavior {
|
||||
// Consts for the LLVM CallConv type, pre-cast to usize.
|
||||
|
||||
/// LLVM CallingConv::ID. Should we wrap this?
|
||||
///
|
||||
/// See <https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/IR/CallingConv.h>
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
#[repr(C)]
|
||||
pub enum CallConv {
|
||||
CCallConv = 0,
|
||||
FastCallConv = 8,
|
||||
ColdCallConv = 9,
|
||||
PreserveMost = 14,
|
||||
PreserveAll = 15,
|
||||
Tail = 18,
|
||||
X86StdcallCallConv = 64,
|
||||
X86FastcallCallConv = 65,
|
||||
ArmAapcsCallConv = 67,
|
||||
|
@ -3,7 +3,7 @@ use crate::context::TypeLowering;
|
||||
use crate::type_::Type;
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
|
||||
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
|
||||
use rustc_target::abi::HasDataLayout;
|
||||
@ -215,20 +215,16 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
|
||||
/// of that field's type - this is useful for taking the address of
|
||||
/// that field and ensuring the struct has the right alignment.
|
||||
fn llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> &'a Type {
|
||||
// This must produce the same result for `repr(transparent)` wrappers as for the inner type!
|
||||
// In other words, this should generally not look at the type at all, but only at the
|
||||
// layout.
|
||||
if let Abi::Scalar(scalar) = self.abi {
|
||||
// Use a different cache for scalars because pointers to DSTs
|
||||
// can be either fat or thin (data pointers of fat pointers).
|
||||
if let Some(&llty) = cx.scalar_lltypes.borrow().get(&self.ty) {
|
||||
return llty;
|
||||
}
|
||||
let llty = match *self.ty.kind() {
|
||||
ty::Ref(..) | ty::RawPtr(_) => cx.type_ptr(),
|
||||
ty::Adt(def, _) if def.is_box() => cx.type_ptr(),
|
||||
ty::FnPtr(sig) => {
|
||||
cx.fn_ptr_backend_type(cx.fn_abi_of_fn_ptr(sig, ty::List::empty()))
|
||||
}
|
||||
_ => self.scalar_llvm_type_at(cx, scalar),
|
||||
};
|
||||
let llty = self.scalar_llvm_type_at(cx, scalar);
|
||||
cx.scalar_lltypes.borrow_mut().insert(self.ty, llty);
|
||||
return llty;
|
||||
}
|
||||
@ -303,27 +299,9 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
|
||||
index: usize,
|
||||
immediate: bool,
|
||||
) -> &'a Type {
|
||||
// HACK(eddyb) special-case fat pointers until LLVM removes
|
||||
// pointee types, to avoid bitcasting every `OperandRef::deref`.
|
||||
match *self.ty.kind() {
|
||||
ty::Ref(..) | ty::RawPtr(_) => {
|
||||
return self.field(cx, index).llvm_type(cx);
|
||||
}
|
||||
// only wide pointer boxes are handled as pointers
|
||||
// thin pointer boxes with scalar allocators are handled by the general logic below
|
||||
ty::Adt(def, args) if def.is_box() && cx.layout_of(args.type_at(1)).is_zst() => {
|
||||
let ptr_ty = Ty::new_mut_ptr(cx.tcx, self.ty.boxed_ty());
|
||||
return cx.layout_of(ptr_ty).scalar_pair_element_llvm_type(cx, index, immediate);
|
||||
}
|
||||
// `dyn* Trait` has the same ABI as `*mut dyn Trait`
|
||||
ty::Dynamic(bounds, region, ty::DynStar) => {
|
||||
let ptr_ty =
|
||||
Ty::new_mut_ptr(cx.tcx, Ty::new_dynamic(cx.tcx, bounds, region, ty::Dyn));
|
||||
return cx.layout_of(ptr_ty).scalar_pair_element_llvm_type(cx, index, immediate);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// This must produce the same result for `repr(transparent)` wrappers as for the inner type!
|
||||
// In other words, this should generally not look at the type at all, but only at the
|
||||
// layout.
|
||||
let Abi::ScalarPair(a, b) = self.abi else {
|
||||
bug!("TyAndLayout::scalar_pair_element_llty({:?}): not applicable", self);
|
||||
};
|
||||
@ -405,7 +383,11 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
|
||||
|
||||
// Vectors, even for non-power-of-two sizes, have the same layout as
|
||||
// arrays but don't count as aggregate types
|
||||
// While LLVM theoretically supports non-power-of-two sizes, and they
|
||||
// often work fine, sometimes x86-isel deals with them horribly
|
||||
// (see #115212) so for now only use power-of-two ones.
|
||||
if let FieldsShape::Array { count, .. } = self.layout.fields()
|
||||
&& count.is_power_of_two()
|
||||
&& let element = self.field(cx, 0)
|
||||
&& element.ty.is_integral()
|
||||
{
|
||||
|
@ -1,12 +1,10 @@
|
||||
use crate::traits::*;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
|
||||
use rustc_middle::ty::Instance;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_session::config::DebugInfo;
|
||||
use rustc_span::symbol::{kw, Symbol};
|
||||
@ -19,19 +17,11 @@ use super::{FunctionCx, LocalRef};
|
||||
|
||||
use std::ops::Range;
|
||||
|
||||
pub struct FunctionDebugContext<'tcx, S, L> {
|
||||
/// Maps from source code to the corresponding debug info scope.
|
||||
pub struct FunctionDebugContext<S, L> {
|
||||
pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
|
||||
|
||||
/// Maps from a given inlined function to its debug info declaration.
|
||||
pub inlined_function_scopes: FxHashMap<Instance<'tcx>, S>,
|
||||
|
||||
/// Maps from a lexical block (parent scope, line, column, file) to its debug info declaration.
|
||||
/// This is particularily useful if the parent scope is an inlined function.
|
||||
pub lexical_blocks: FxHashMap<(S, u32, u32, S), S>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum VariableKind {
|
||||
ArgumentVariable(usize /*index*/),
|
||||
LocalVariable,
|
||||
@ -445,9 +435,23 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
bx.store(place.llval, alloca.llval, alloca.align);
|
||||
|
||||
// Point the debug info to `*alloca` for the current variable
|
||||
bx.dbg_var_addr(dbg_var, dbg_loc, alloca.llval, Size::ZERO, &[Size::ZERO], None);
|
||||
bx.dbg_var_addr(
|
||||
dbg_var,
|
||||
dbg_loc,
|
||||
alloca.llval,
|
||||
Size::ZERO,
|
||||
&[Size::ZERO],
|
||||
var.fragment,
|
||||
);
|
||||
} else {
|
||||
bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, &indirect_offsets, None);
|
||||
bx.dbg_var_addr(
|
||||
dbg_var,
|
||||
dbg_loc,
|
||||
base.llval,
|
||||
direct_offset,
|
||||
&indirect_offsets,
|
||||
var.fragment,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -570,17 +574,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
}
|
||||
|
||||
let place = fragment.contents;
|
||||
let fragment = if fragment_layout.size == Size::ZERO {
|
||||
// Fragment is a ZST, so does not represent anything.
|
||||
continue;
|
||||
} else if fragment_layout.size == var_layout.size {
|
||||
// Fragment covers entire variable, so as far as
|
||||
// DWARF is concerned, it's not really a fragment.
|
||||
None
|
||||
} else {
|
||||
Some(fragment_start..fragment_start + fragment_layout.size)
|
||||
};
|
||||
|
||||
per_local[place.local].push(PerLocalVarDebugInfo {
|
||||
name: var.name,
|
||||
source_info: var.source_info,
|
||||
dbg_var,
|
||||
fragment: if fragment_layout.size == var_layout.size {
|
||||
// Fragment covers entire variable, so as far as
|
||||
// DWARF is concerned, it's not really a fragment.
|
||||
None
|
||||
} else {
|
||||
Some(fragment_start..fragment_start + fragment_layout.size)
|
||||
},
|
||||
fragment,
|
||||
projection: place.projection,
|
||||
});
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
|
||||
|
||||
mir: &'tcx mir::Body<'tcx>,
|
||||
|
||||
debug_context: Option<FunctionDebugContext<'tcx, Bx::DIScope, Bx::DILocation>>,
|
||||
debug_context: Option<FunctionDebugContext<Bx::DIScope, Bx::DILocation>>,
|
||||
|
||||
llfn: Bx::Function,
|
||||
|
||||
|
@ -26,7 +26,7 @@ pub trait DebugInfoMethods<'tcx>: BackendTypes {
|
||||
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
|
||||
llfn: Self::Function,
|
||||
mir: &mir::Body<'tcx>,
|
||||
) -> Option<FunctionDebugContext<'tcx, Self::DIScope, Self::DILocation>>;
|
||||
) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>>;
|
||||
|
||||
// FIXME(eddyb) find a common convention for all of the debuginfo-related
|
||||
// names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.).
|
||||
|
@ -255,6 +255,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
caller_abi: &ArgAbi<'tcx, Ty<'tcx>>,
|
||||
callee_abi: &ArgAbi<'tcx, Ty<'tcx>>,
|
||||
) -> bool {
|
||||
let primitive_abi_compat = |a1: abi::Primitive, a2: abi::Primitive| -> bool {
|
||||
match (a1, a2) {
|
||||
// For integers, ignore the sign.
|
||||
(abi::Primitive::Int(int_ty1, _sign1), abi::Primitive::Int(int_ty2, _sign2)) => {
|
||||
int_ty1 == int_ty2
|
||||
}
|
||||
// For everything else we require full equality.
|
||||
_ => a1 == a2,
|
||||
}
|
||||
};
|
||||
// Heuristic for type comparison.
|
||||
let layout_compat = || {
|
||||
if caller_abi.layout.ty == callee_abi.layout.ty {
|
||||
@ -267,28 +277,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
// then who knows what happens.
|
||||
return false;
|
||||
}
|
||||
if caller_abi.layout.size != callee_abi.layout.size
|
||||
|| caller_abi.layout.align.abi != callee_abi.layout.align.abi
|
||||
{
|
||||
// This cannot go well...
|
||||
return false;
|
||||
}
|
||||
// The rest *should* be okay, but we are extra conservative.
|
||||
// This is tricky. Some ABIs split aggregates up into multiple registers etc, so we have
|
||||
// to be super careful here. For the scalar ABIs we conveniently already have all the
|
||||
// newtypes unwrapped etc, so in those cases we can just compare the scalar components.
|
||||
// Everything else we just reject for now.
|
||||
match (caller_abi.layout.abi, callee_abi.layout.abi) {
|
||||
// Different valid ranges are okay (once we enforce validity,
|
||||
// that will take care to make it UB to leave the range, just
|
||||
// like for transmute).
|
||||
// Different valid ranges are okay (the validity check will complain if this leads
|
||||
// to invalid transmutes).
|
||||
(abi::Abi::Scalar(caller), abi::Abi::Scalar(callee)) => {
|
||||
caller.primitive() == callee.primitive()
|
||||
primitive_abi_compat(caller.primitive(), callee.primitive())
|
||||
}
|
||||
(
|
||||
abi::Abi::ScalarPair(caller1, caller2),
|
||||
abi::Abi::ScalarPair(callee1, callee2),
|
||||
) => {
|
||||
caller1.primitive() == callee1.primitive()
|
||||
&& caller2.primitive() == callee2.primitive()
|
||||
primitive_abi_compat(caller1.primitive(), callee1.primitive())
|
||||
&& primitive_abi_compat(caller2.primitive(), callee2.primitive())
|
||||
}
|
||||
// Be conservative
|
||||
// Be conservative.
|
||||
_ => false,
|
||||
}
|
||||
};
|
||||
@ -309,7 +315,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
return true;
|
||||
};
|
||||
let mode_compat = || match (&caller_abi.mode, &callee_abi.mode) {
|
||||
(PassMode::Ignore, PassMode::Ignore) => true,
|
||||
(PassMode::Ignore, PassMode::Ignore) => true, // can still be reached for the return type
|
||||
(PassMode::Direct(a1), PassMode::Direct(a2)) => arg_attr_compat(a1, a2),
|
||||
(PassMode::Pair(a1, b1), PassMode::Pair(a2, b2)) => {
|
||||
arg_attr_compat(a1, a2) && arg_attr_compat(b1, b2)
|
||||
@ -326,7 +332,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
_ => false,
|
||||
};
|
||||
|
||||
// We have to check both. `layout_compat` is needed to reject e.g. `i32` vs `f32`,
|
||||
// which is not reflected in `PassMode`. `mode_compat` is needed to reject `u8` vs `bool`,
|
||||
// which have the same `abi::Primitive` but different `arg_ext`.
|
||||
if layout_compat() && mode_compat() {
|
||||
// Something went very wrong if our checks don't even imply that the layout is the same.
|
||||
assert!(
|
||||
caller_abi.layout.size == callee_abi.layout.size
|
||||
&& caller_abi.layout.align.abi == callee_abi.layout.align.abi
|
||||
);
|
||||
return true;
|
||||
}
|
||||
trace!(
|
||||
|
@ -20,6 +20,8 @@ use rustc_mir_dataflow::{Analysis, ResultsCursor};
|
||||
use rustc_target::abi::{Size, FIRST_VARIANT};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use crate::util::is_within_packed;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
enum EdgeKind {
|
||||
Unwind,
|
||||
@ -93,6 +95,7 @@ impl<'tcx> MirPass<'tcx> for Validator {
|
||||
cfg_checker.visit_body(body);
|
||||
cfg_checker.check_cleanup_control_flow();
|
||||
|
||||
// Also run the TypeChecker.
|
||||
for (location, msg) in validate_types(tcx, self.mir_phase, param_env, body) {
|
||||
cfg_checker.fail(location, msg);
|
||||
}
|
||||
@ -427,14 +430,34 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
|
||||
self.check_unwind_edge(location, *unwind);
|
||||
|
||||
// The call destination place and Operand::Move place used as an argument might be
|
||||
// passed by a reference to the callee. Consequently they must be non-overlapping.
|
||||
// Currently this simply checks for duplicate places.
|
||||
// passed by a reference to the callee. Consequently they must be non-overlapping
|
||||
// and cannot be packed. Currently this simply checks for duplicate places.
|
||||
self.place_cache.clear();
|
||||
self.place_cache.insert(destination.as_ref());
|
||||
if is_within_packed(self.tcx, &self.body.local_decls, *destination).is_some() {
|
||||
// This is bad! The callee will expect the memory to be aligned.
|
||||
self.fail(
|
||||
location,
|
||||
format!(
|
||||
"encountered packed place in `Call` terminator destination: {:?}",
|
||||
terminator.kind,
|
||||
),
|
||||
);
|
||||
}
|
||||
let mut has_duplicates = false;
|
||||
for arg in args {
|
||||
if let Operand::Move(place) = arg {
|
||||
has_duplicates |= !self.place_cache.insert(place.as_ref());
|
||||
if is_within_packed(self.tcx, &self.body.local_decls, *place).is_some() {
|
||||
// This is bad! The callee will expect the memory to be aligned.
|
||||
self.fail(
|
||||
location,
|
||||
format!(
|
||||
"encountered `Move` of a packed place in `Call` terminator: {:?}",
|
||||
terminator.kind,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -442,7 +465,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
|
||||
self.fail(
|
||||
location,
|
||||
format!(
|
||||
"encountered overlapping memory in `Call` terminator: {:?}",
|
||||
"encountered overlapping memory in `Move` arguments to `Call` terminator: {:?}",
|
||||
terminator.kind,
|
||||
),
|
||||
);
|
||||
@ -541,6 +564,8 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A faster version of the validation pass that only checks those things which may break when apply
|
||||
/// generic substitutions.
|
||||
pub fn validate_types<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
mir_phase: MirPhase,
|
||||
|
@ -34,6 +34,7 @@ where
|
||||
false
|
||||
}
|
||||
_ => {
|
||||
// We cannot figure out the layout. Conservatively assume that this is disaligned.
|
||||
debug!("is_disaligned({:?}) - true", place);
|
||||
true
|
||||
}
|
||||
|
@ -20,7 +20,6 @@
|
||||
//! the field `next_edge`). Each of those fields is an array that should
|
||||
//! be indexed by the direction (see the type `Direction`).
|
||||
|
||||
use crate::snapshot_vec::{SnapshotVec, SnapshotVecDelegate};
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use std::fmt::Debug;
|
||||
|
||||
@ -28,8 +27,8 @@ use std::fmt::Debug;
|
||||
mod tests;
|
||||
|
||||
pub struct Graph<N, E> {
|
||||
nodes: SnapshotVec<Node<N>>,
|
||||
edges: SnapshotVec<Edge<E>>,
|
||||
nodes: Vec<Node<N>>,
|
||||
edges: Vec<Edge<E>>,
|
||||
}
|
||||
|
||||
pub struct Node<N> {
|
||||
@ -45,20 +44,6 @@ pub struct Edge<E> {
|
||||
pub data: E,
|
||||
}
|
||||
|
||||
impl<N> SnapshotVecDelegate for Node<N> {
|
||||
type Value = Node<N>;
|
||||
type Undo = ();
|
||||
|
||||
fn reverse(_: &mut Vec<Node<N>>, _: ()) {}
|
||||
}
|
||||
|
||||
impl<N> SnapshotVecDelegate for Edge<N> {
|
||||
type Value = Edge<N>;
|
||||
type Undo = ();
|
||||
|
||||
fn reverse(_: &mut Vec<Edge<N>>, _: ()) {}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub struct NodeIndex(pub usize);
|
||||
|
||||
@ -86,11 +71,11 @@ impl NodeIndex {
|
||||
|
||||
impl<N: Debug, E: Debug> Graph<N, E> {
|
||||
pub fn new() -> Graph<N, E> {
|
||||
Graph { nodes: SnapshotVec::new(), edges: SnapshotVec::new() }
|
||||
Graph { nodes: Vec::new(), edges: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn with_capacity(nodes: usize, edges: usize) -> Graph<N, E> {
|
||||
Graph { nodes: SnapshotVec::with_capacity(nodes), edges: SnapshotVec::with_capacity(edges) }
|
||||
Graph { nodes: Vec::with_capacity(nodes), edges: Vec::with_capacity(edges) }
|
||||
}
|
||||
|
||||
// # Simple accessors
|
||||
|
@ -516,7 +516,8 @@ E0793: include_str!("./error_codes/E0793.md"),
|
||||
E0794: include_str!("./error_codes/E0794.md"),
|
||||
}
|
||||
|
||||
// Undocumented removed error codes. Note that many removed error codes are documented.
|
||||
// Undocumented removed error codes. Note that many removed error codes are kept in the list above
|
||||
// and marked as no-longer emitted with a note in the markdown file (see E0001 for an example).
|
||||
// E0006, // merged with E0005
|
||||
// E0008, // cannot bind by-move into a pattern guard
|
||||
// E0019, // merged into E0015
|
||||
|
@ -813,7 +813,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||
rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word), WarnFollowing),
|
||||
rustc_attr!(TEST, rustc_strict_coherence, Normal, template!(Word), WarnFollowing),
|
||||
rustc_attr!(TEST, rustc_variance, Normal, template!(Word), WarnFollowing),
|
||||
rustc_attr!(TEST, rustc_variance_of_opaques, Normal, template!(Word), WarnFollowing),
|
||||
rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ..."), WarnFollowing),
|
||||
rustc_attr!(TEST, rustc_abi, Normal, template!(List: "field1, field2, ..."), WarnFollowing),
|
||||
rustc_attr!(TEST, rustc_regions, Normal, template!(Word), WarnFollowing),
|
||||
rustc_attr!(
|
||||
TEST, rustc_error, Normal,
|
||||
|
@ -56,6 +56,7 @@ pub fn provide(providers: &mut Providers) {
|
||||
resolve_bound_vars::provide(providers);
|
||||
*providers = Providers {
|
||||
type_of: type_of::type_of,
|
||||
type_of_opaque: type_of::type_of_opaque,
|
||||
item_bounds: item_bounds::item_bounds,
|
||||
explicit_item_bounds: item_bounds::explicit_item_bounds,
|
||||
generics_of: generics_of::generics_of,
|
||||
|
@ -1,7 +1,8 @@
|
||||
use rustc_errors::{Applicability, StashKey};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::HirId;
|
||||
use rustc_middle::query::plumbing::CyclePlaceholder;
|
||||
use rustc_middle::ty::print::with_forced_trimmed_paths;
|
||||
use rustc_middle::ty::util::IntTypeExt;
|
||||
use rustc_middle::ty::{self, ImplTraitInTraitData, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
|
||||
@ -388,86 +389,62 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
|
||||
}
|
||||
},
|
||||
|
||||
Node::Item(item) => {
|
||||
match item.kind {
|
||||
ItemKind::Static(ty, .., body_id) => {
|
||||
if is_suggestable_infer_ty(ty) {
|
||||
infer_placeholder_type(
|
||||
tcx,
|
||||
def_id,
|
||||
body_id,
|
||||
ty.span,
|
||||
item.ident,
|
||||
"static variable",
|
||||
)
|
||||
} else {
|
||||
icx.to_ty(ty)
|
||||
}
|
||||
}
|
||||
ItemKind::Const(ty, _, body_id) => {
|
||||
if is_suggestable_infer_ty(ty) {
|
||||
infer_placeholder_type(
|
||||
tcx, def_id, body_id, ty.span, item.ident, "constant",
|
||||
)
|
||||
} else {
|
||||
icx.to_ty(ty)
|
||||
}
|
||||
}
|
||||
ItemKind::TyAlias(self_ty, _) => icx.to_ty(self_ty),
|
||||
ItemKind::Impl(hir::Impl { self_ty, .. }) => match self_ty.find_self_aliases() {
|
||||
spans if spans.len() > 0 => {
|
||||
let guar = tcx.sess.emit_err(crate::errors::SelfInImplSelf {
|
||||
span: spans.into(),
|
||||
note: (),
|
||||
});
|
||||
Ty::new_error(tcx, guar)
|
||||
}
|
||||
_ => icx.to_ty(*self_ty),
|
||||
},
|
||||
ItemKind::Fn(..) => {
|
||||
let args = ty::GenericArgs::identity_for_item(tcx, def_id);
|
||||
Ty::new_fn_def(tcx, def_id.to_def_id(), args)
|
||||
}
|
||||
ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) => {
|
||||
let def = tcx.adt_def(def_id);
|
||||
let args = ty::GenericArgs::identity_for_item(tcx, def_id);
|
||||
Ty::new_adt(tcx, def, args)
|
||||
}
|
||||
ItemKind::OpaqueTy(OpaqueTy {
|
||||
origin: hir::OpaqueTyOrigin::TyAlias { .. },
|
||||
..
|
||||
}) => opaque::find_opaque_ty_constraints_for_tait(tcx, def_id),
|
||||
// Opaque types desugared from `impl Trait`.
|
||||
ItemKind::OpaqueTy(&OpaqueTy {
|
||||
origin:
|
||||
hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner),
|
||||
in_trait,
|
||||
..
|
||||
}) => {
|
||||
if in_trait && !tcx.defaultness(owner).has_value() {
|
||||
span_bug!(
|
||||
tcx.def_span(def_id),
|
||||
"tried to get type of this RPITIT with no definition"
|
||||
);
|
||||
}
|
||||
opaque::find_opaque_ty_constraints_for_rpit(tcx, def_id, owner)
|
||||
}
|
||||
ItemKind::Trait(..)
|
||||
| ItemKind::TraitAlias(..)
|
||||
| ItemKind::Macro(..)
|
||||
| ItemKind::Mod(..)
|
||||
| ItemKind::ForeignMod { .. }
|
||||
| ItemKind::GlobalAsm(..)
|
||||
| ItemKind::ExternCrate(..)
|
||||
| ItemKind::Use(..) => {
|
||||
span_bug!(
|
||||
item.span,
|
||||
"compute_type_of_item: unexpected item type: {:?}",
|
||||
item.kind
|
||||
);
|
||||
Node::Item(item) => match item.kind {
|
||||
ItemKind::Static(ty, .., body_id) => {
|
||||
if is_suggestable_infer_ty(ty) {
|
||||
infer_placeholder_type(
|
||||
tcx,
|
||||
def_id,
|
||||
body_id,
|
||||
ty.span,
|
||||
item.ident,
|
||||
"static variable",
|
||||
)
|
||||
} else {
|
||||
icx.to_ty(ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
ItemKind::Const(ty, _, body_id) => {
|
||||
if is_suggestable_infer_ty(ty) {
|
||||
infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident, "constant")
|
||||
} else {
|
||||
icx.to_ty(ty)
|
||||
}
|
||||
}
|
||||
ItemKind::TyAlias(self_ty, _) => icx.to_ty(self_ty),
|
||||
ItemKind::Impl(hir::Impl { self_ty, .. }) => match self_ty.find_self_aliases() {
|
||||
spans if spans.len() > 0 => {
|
||||
let guar = tcx
|
||||
.sess
|
||||
.emit_err(crate::errors::SelfInImplSelf { span: spans.into(), note: () });
|
||||
Ty::new_error(tcx, guar)
|
||||
}
|
||||
_ => icx.to_ty(*self_ty),
|
||||
},
|
||||
ItemKind::Fn(..) => {
|
||||
let args = ty::GenericArgs::identity_for_item(tcx, def_id);
|
||||
Ty::new_fn_def(tcx, def_id.to_def_id(), args)
|
||||
}
|
||||
ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) => {
|
||||
let def = tcx.adt_def(def_id);
|
||||
let args = ty::GenericArgs::identity_for_item(tcx, def_id);
|
||||
Ty::new_adt(tcx, def, args)
|
||||
}
|
||||
ItemKind::OpaqueTy(..) => tcx.type_of_opaque(def_id).map_or_else(
|
||||
|CyclePlaceholder(guar)| Ty::new_error(tcx, guar),
|
||||
|ty| ty.instantiate_identity(),
|
||||
),
|
||||
ItemKind::Trait(..)
|
||||
| ItemKind::TraitAlias(..)
|
||||
| ItemKind::Macro(..)
|
||||
| ItemKind::Mod(..)
|
||||
| ItemKind::ForeignMod { .. }
|
||||
| ItemKind::GlobalAsm(..)
|
||||
| ItemKind::ExternCrate(..)
|
||||
| ItemKind::Use(..) => {
|
||||
span_bug!(item.span, "compute_type_of_item: unexpected item type: {:?}", item.kind);
|
||||
}
|
||||
},
|
||||
|
||||
Node::ForeignItem(foreign_item) => match foreign_item.kind {
|
||||
ForeignItemKind::Fn(..) => {
|
||||
@ -514,6 +491,51 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
|
||||
ty::EarlyBinder::bind(output)
|
||||
}
|
||||
|
||||
pub(super) fn type_of_opaque(
|
||||
tcx: TyCtxt<'_>,
|
||||
def_id: DefId,
|
||||
) -> Result<ty::EarlyBinder<Ty<'_>>, CyclePlaceholder> {
|
||||
if let Some(def_id) = def_id.as_local() {
|
||||
use rustc_hir::*;
|
||||
|
||||
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
|
||||
Ok(ty::EarlyBinder::bind(match tcx.hir().get(hir_id) {
|
||||
Node::Item(item) => match item.kind {
|
||||
ItemKind::OpaqueTy(OpaqueTy {
|
||||
origin: hir::OpaqueTyOrigin::TyAlias { .. },
|
||||
..
|
||||
}) => opaque::find_opaque_ty_constraints_for_tait(tcx, def_id),
|
||||
// Opaque types desugared from `impl Trait`.
|
||||
ItemKind::OpaqueTy(&OpaqueTy {
|
||||
origin:
|
||||
hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner),
|
||||
in_trait,
|
||||
..
|
||||
}) => {
|
||||
if in_trait && !tcx.defaultness(owner).has_value() {
|
||||
span_bug!(
|
||||
tcx.def_span(def_id),
|
||||
"tried to get type of this RPITIT with no definition"
|
||||
);
|
||||
}
|
||||
opaque::find_opaque_ty_constraints_for_rpit(tcx, def_id, owner)
|
||||
}
|
||||
_ => {
|
||||
span_bug!(item.span, "type_of_opaque: unexpected item type: {:?}", item.kind);
|
||||
}
|
||||
},
|
||||
|
||||
x => {
|
||||
bug!("unexpected sort of node in type_of_opaque(): {:?}", x);
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
// Foreign opaque type will go through the foreign provider
|
||||
// and load the type from metadata.
|
||||
Ok(tcx.type_of(def_id))
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_placeholder_type<'a>(
|
||||
tcx: TyCtxt<'a>,
|
||||
def_id: LocalDefId,
|
||||
|
@ -1,9 +1,24 @@
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::CRATE_DEF_ID;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use crate::errors;
|
||||
|
||||
pub fn test_variance(tcx: TyCtxt<'_>) {
|
||||
if tcx.has_attr(CRATE_DEF_ID, sym::rustc_variance_of_opaques) {
|
||||
for id in tcx.hir().items() {
|
||||
if matches!(tcx.def_kind(id.owner_id), DefKind::OpaqueTy) {
|
||||
let variances_of = tcx.variances_of(id.owner_id);
|
||||
|
||||
tcx.sess.emit_err(errors::VariancesOf {
|
||||
span: tcx.def_span(id.owner_id),
|
||||
variances_of: format!("{variances_of:?}"),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For unit testing: check for a special "rustc_variance"
|
||||
// attribute and report an error with various results if found.
|
||||
for id in tcx.hir().items() {
|
||||
|
@ -231,7 +231,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let inferred_sig = self.normalize(
|
||||
span,
|
||||
self.deduce_sig_from_projection(
|
||||
Some(span),
|
||||
Some(span),
|
||||
bound_predicate.rebind(proj_predicate),
|
||||
),
|
||||
);
|
||||
|
@ -20,10 +20,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
let hir = self.tcx.hir();
|
||||
let hir::Node::Expr(expr) = hir.get(hir_id) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let Some(unsubstituted_pred) = self
|
||||
.tcx
|
||||
@ -47,6 +43,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
let direct_param = if let ty::ClauseKind::Trait(pred) = unsubstituted_pred.kind().skip_binder()
|
||||
&& let ty = pred.trait_ref.self_ty()
|
||||
&& let ty::Param(_param) = ty.kind()
|
||||
&& let Some(arg) = predicate_args.get(0)
|
||||
&& let ty::GenericArgKind::Type(arg_ty) = arg.unpack()
|
||||
&& arg_ty == ty
|
||||
{
|
||||
Some(*arg)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let find_param_matching = |matches: &dyn Fn(ty::ParamTerm) -> bool| {
|
||||
predicate_args.iter().find_map(|arg| {
|
||||
arg.walk().find_map(|arg| {
|
||||
@ -96,54 +103,83 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.find_ambiguous_parameter_in(def_id, error.root_obligation.predicate);
|
||||
}
|
||||
|
||||
if self.closure_span_overlaps_error(error, expr.span) {
|
||||
return false;
|
||||
}
|
||||
let hir = self.tcx.hir();
|
||||
let (expr, qpath) = match hir.get(hir_id) {
|
||||
hir::Node::Expr(expr) => {
|
||||
if self.closure_span_overlaps_error(error, expr.span) {
|
||||
return false;
|
||||
}
|
||||
let qpath =
|
||||
if let hir::ExprKind::Path(qpath) = expr.kind { Some(qpath) } else { None };
|
||||
|
||||
match &expr.kind {
|
||||
hir::ExprKind::Path(qpath) => {
|
||||
if let hir::Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::Call(callee, args),
|
||||
hir_id: call_hir_id,
|
||||
span: call_span,
|
||||
..
|
||||
}) = hir.get_parent(expr.hir_id)
|
||||
&& callee.hir_id == expr.hir_id
|
||||
{
|
||||
if self.closure_span_overlaps_error(error, *call_span) {
|
||||
return false;
|
||||
}
|
||||
(Some(*expr), qpath)
|
||||
}
|
||||
hir::Node::Ty(hir::Ty { kind: hir::TyKind::Path(qpath), .. }) => (None, Some(*qpath)),
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
for param in
|
||||
[param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
{
|
||||
if self.blame_specific_arg_if_possible(
|
||||
error,
|
||||
def_id,
|
||||
param,
|
||||
*call_hir_id,
|
||||
callee.span,
|
||||
None,
|
||||
args,
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if let Some(qpath) = qpath {
|
||||
if let Some(param) = direct_param {
|
||||
if self.point_at_path_if_possible(error, def_id, param, &qpath) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if let hir::Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::Call(callee, args),
|
||||
hir_id: call_hir_id,
|
||||
span: call_span,
|
||||
..
|
||||
}) = hir.get_parent(hir_id)
|
||||
&& callee.hir_id == hir_id
|
||||
{
|
||||
if self.closure_span_overlaps_error(error, *call_span) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
|
||||
for param in
|
||||
[param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
{
|
||||
if self.point_at_path_if_possible(error, def_id, param, qpath) {
|
||||
if self.blame_specific_arg_if_possible(
|
||||
error,
|
||||
def_id,
|
||||
param,
|
||||
*call_hir_id,
|
||||
callee.span,
|
||||
None,
|
||||
args,
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
hir::ExprKind::MethodCall(segment, receiver, args, ..) => {
|
||||
|
||||
for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
{
|
||||
if self.point_at_path_if_possible(error, def_id, param, &qpath) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match expr.map(|e| e.kind) {
|
||||
Some(hir::ExprKind::MethodCall(segment, receiver, args, ..)) => {
|
||||
if let Some(param) = direct_param
|
||||
&& self.point_at_generic_if_possible(error, def_id, param, segment)
|
||||
{
|
||||
error.obligation.cause.map_code(|parent_code| {
|
||||
ObligationCauseCode::FunctionArgumentObligation {
|
||||
arg_hir_id: receiver.hir_id,
|
||||
call_hir_id: hir_id,
|
||||
parent_code,
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
@ -175,7 +211,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
hir::ExprKind::Struct(qpath, fields, ..) => {
|
||||
Some(hir::ExprKind::Struct(qpath, fields, ..)) => {
|
||||
if let Res::Def(DefKind::Struct | DefKind::Variant, variant_def_id) =
|
||||
self.typeck_results.borrow().qpath_res(qpath, hir_id)
|
||||
{
|
||||
@ -200,9 +236,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
for param in [
|
||||
direct_param,
|
||||
param_to_point_at,
|
||||
fallback_param_to_point_at,
|
||||
self_param_to_point_at,
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
{
|
||||
if self.point_at_path_if_possible(error, def_id, param, qpath) {
|
||||
return true;
|
||||
@ -434,7 +475,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively searches for the most-specific blamable expression.
|
||||
* Recursively searches for the most-specific blameable expression.
|
||||
* For example, if you have a chain of constraints like:
|
||||
* - want `Vec<i32>: Copy`
|
||||
* - because `Option<Vec<i32>>: Copy` needs `Vec<i32>: Copy` because `impl <T: Copy> Copy for Option<T>`
|
||||
|
@ -317,7 +317,18 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
|
||||
|
||||
fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span) {
|
||||
// FIXME: normalization and escaping regions
|
||||
let ty = if !ty.has_escaping_bound_vars() { self.normalize(span, ty) } else { ty };
|
||||
let ty = if !ty.has_escaping_bound_vars() {
|
||||
if let ty::Alias(
|
||||
ty::AliasKind::Projection | ty::AliasKind::Weak,
|
||||
ty::AliasTy { args, def_id, .. },
|
||||
) = ty.kind()
|
||||
{
|
||||
self.add_required_obligations_for_hir(span, *def_id, args, hir_id);
|
||||
}
|
||||
self.normalize(span, ty)
|
||||
} else {
|
||||
ty
|
||||
};
|
||||
self.write_ty(hir_id, ty)
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,6 @@ use rustc_data_structures::graph::implementation::{
|
||||
use rustc_data_structures::intern::Interned;
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_middle::ty::fold::TypeFoldable;
|
||||
use rustc_middle::ty::PlaceholderRegion;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{ReEarlyBound, ReErased, ReError, ReFree, ReStatic};
|
||||
use rustc_middle::ty::{ReLateBound, RePlaceholder, ReVar};
|
||||
@ -173,38 +172,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the LUb of a given region and the empty region
|
||||
fn lub_empty(&self, a_region: Region<'tcx>) -> Result<Region<'tcx>, PlaceholderRegion> {
|
||||
match *a_region {
|
||||
ReLateBound(..) | ReErased => {
|
||||
bug!("cannot relate region: {:?}", a_region);
|
||||
}
|
||||
|
||||
ReVar(v_id) => {
|
||||
span_bug!(
|
||||
self.var_infos[v_id].origin.span(),
|
||||
"lub invoked with non-concrete regions: {:?}",
|
||||
a_region,
|
||||
);
|
||||
}
|
||||
|
||||
ReStatic => {
|
||||
// nothing lives longer than `'static`
|
||||
Ok(self.tcx().lifetimes.re_static)
|
||||
}
|
||||
|
||||
ReError(_) => Ok(a_region),
|
||||
|
||||
ReEarlyBound(_) | ReFree(_) => {
|
||||
// All empty regions are less than early-bound, free,
|
||||
// and scope regions.
|
||||
Ok(a_region)
|
||||
}
|
||||
|
||||
RePlaceholder(placeholder) => Err(placeholder),
|
||||
}
|
||||
}
|
||||
|
||||
fn expansion(&self, var_values: &mut LexicalRegionResolutions<'tcx>) {
|
||||
// In the first pass, we expand region vids according to constraints we
|
||||
// have previously found. In the second pass, we loop through the region
|
||||
@ -247,27 +214,25 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
|
||||
true
|
||||
}
|
||||
VarValue::Value(cur_region) => {
|
||||
let lub = match self.lub_empty(cur_region) {
|
||||
Ok(r) => r,
|
||||
// If the empty and placeholder regions are in the same universe,
|
||||
// then the LUB is the Placeholder region (which is the cur_region).
|
||||
// If they are not in the same universe, the LUB is the Static lifetime.
|
||||
Err(placeholder) if a_universe == placeholder.universe => {
|
||||
cur_region
|
||||
match *cur_region {
|
||||
// If this empty region is from a universe that can name the
|
||||
// placeholder universe, then the LUB is the Placeholder region
|
||||
// (which is the cur_region). Otherwise, the LUB is the Static
|
||||
// lifetime.
|
||||
RePlaceholder(placeholder)
|
||||
if !a_universe.can_name(placeholder.universe) =>
|
||||
{
|
||||
let lub = self.tcx().lifetimes.re_static;
|
||||
debug!(
|
||||
"Expanding value of {:?} from {:?} to {:?}",
|
||||
b_vid, cur_region, lub
|
||||
);
|
||||
|
||||
*b_data = VarValue::Value(lub);
|
||||
true
|
||||
}
|
||||
Err(_) => self.tcx().lifetimes.re_static,
|
||||
};
|
||||
|
||||
if lub == cur_region {
|
||||
false
|
||||
} else {
|
||||
debug!(
|
||||
"Expanding value of {:?} from {:?} to {:?}",
|
||||
b_vid, cur_region, lub
|
||||
);
|
||||
|
||||
*b_data = VarValue::Value(lub);
|
||||
true
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -341,15 +306,19 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
|
||||
|
||||
match *b_data {
|
||||
VarValue::Empty(empty_ui) => {
|
||||
let lub = match self.lub_empty(a_region) {
|
||||
Ok(r) => r,
|
||||
// If this empty region is from a universe that can
|
||||
// name the placeholder, then the placeholder is
|
||||
// larger; otherwise, the only ancestor is `'static`.
|
||||
Err(placeholder) if empty_ui.can_name(placeholder.universe) => {
|
||||
ty::Region::new_placeholder(self.tcx(), placeholder)
|
||||
let lub = match *a_region {
|
||||
RePlaceholder(placeholder) => {
|
||||
// If this empty region is from a universe that can
|
||||
// name the placeholder, then the placeholder is
|
||||
// larger; otherwise, the only ancestor is `'static`.
|
||||
if empty_ui.can_name(placeholder.universe) {
|
||||
ty::Region::new_placeholder(self.tcx(), placeholder)
|
||||
} else {
|
||||
self.tcx().lifetimes.re_static
|
||||
}
|
||||
}
|
||||
Err(_) => self.tcx().lifetimes.re_static,
|
||||
|
||||
_ => a_region,
|
||||
};
|
||||
|
||||
debug!("Expanding value of {:?} from empty lifetime to {:?}", b_vid, lub);
|
||||
|
@ -22,7 +22,7 @@ use rustc_middle::query::{ExternProviders, Providers};
|
||||
use rustc_middle::ty::{self, GlobalCtxt, RegisteredTools, TyCtxt};
|
||||
use rustc_mir_build as mir_build;
|
||||
use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str, validate_attr};
|
||||
use rustc_passes::{self, hir_stats, layout_test};
|
||||
use rustc_passes::{self, abi_test, hir_stats, layout_test};
|
||||
use rustc_plugin_impl as plugin;
|
||||
use rustc_resolve::Resolver;
|
||||
use rustc_session::code_stats::VTableSizeInfo;
|
||||
@ -818,6 +818,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
|
||||
}
|
||||
|
||||
sess.time("layout_testing", || layout_test::test_layout(tcx));
|
||||
sess.time("abi_testing", || abi_test::test_abi(tcx));
|
||||
|
||||
// Avoid overwhelming user with errors if borrow checking failed.
|
||||
// I'm not sure how helpful this is, to be honest, but it avoids a
|
||||
|
@ -807,6 +807,7 @@ fn test_unstable_options_tracking_hash() {
|
||||
tracked!(no_jump_tables, true);
|
||||
tracked!(no_link, true);
|
||||
tracked!(no_profiler_runtime, true);
|
||||
tracked!(no_trait_vptr, true);
|
||||
tracked!(no_unique_section_names, true);
|
||||
tracked!(oom, OomStrategy::Panic);
|
||||
tracked!(osx_rpath_install_name, true);
|
||||
@ -821,7 +822,7 @@ fn test_unstable_options_tracking_hash() {
|
||||
tracked!(profile_emit, Some(PathBuf::from("abc")));
|
||||
tracked!(profile_sample_use, Some(PathBuf::from("abc")));
|
||||
tracked!(profiler_runtime, "abc".to_string());
|
||||
tracked!(relax_elf_relocations, Some(false));
|
||||
tracked!(relax_elf_relocations, Some(true));
|
||||
tracked!(relro_level, Some(RelroLevel::Full));
|
||||
tracked!(remap_cwd_prefix, Some(PathBuf::from("abc")));
|
||||
tracked!(report_delayed_bugs, true);
|
||||
|
@ -321,13 +321,13 @@ extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM,
|
||||
PrintBackendInfo Print,
|
||||
void* Out) {
|
||||
const TargetMachine *Target = unwrap(TM);
|
||||
const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
|
||||
const Triple::ArchType HostArch = Triple(sys::getDefaultTargetTriple()).getArch();
|
||||
const Triple::ArchType TargetArch = Target->getTargetTriple().getArch();
|
||||
|
||||
std::ostringstream Buf;
|
||||
|
||||
#if LLVM_VERSION_GE(17, 0)
|
||||
const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
|
||||
const ArrayRef<SubtargetSubTypeKV> CPUTable = MCInfo->getAllProcessorDescriptions();
|
||||
#else
|
||||
Buf << "Full target CPU help is not supported by this LLVM version.\n\n";
|
||||
|
@ -347,6 +347,13 @@ impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for Symbol {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for [u8] {
|
||||
fn encode(&self, e: &mut EncodeContext<'a, 'tcx>) {
|
||||
Encoder::emit_usize(e, self.len());
|
||||
e.emit_raw_bytes(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> TyEncoder for EncodeContext<'a, 'tcx> {
|
||||
const CLEAR_CROSS_CRATE: bool = true;
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::mir;
|
||||
use crate::query::CyclePlaceholder;
|
||||
use crate::traits;
|
||||
use crate::ty::{self, Ty};
|
||||
use std::mem::{size_of, transmute_copy, MaybeUninit};
|
||||
@ -142,6 +143,10 @@ impl EraseType for Result<&'_ ty::List<Ty<'_>>, ty::util::AlwaysRequiresDrop> {
|
||||
[u8; size_of::<Result<&'static ty::List<Ty<'static>>, ty::util::AlwaysRequiresDrop>>()];
|
||||
}
|
||||
|
||||
impl EraseType for Result<ty::EarlyBinder<Ty<'_>>, CyclePlaceholder> {
|
||||
type Result = [u8; size_of::<Result<ty::EarlyBinder<Ty<'_>>, CyclePlaceholder>>()];
|
||||
}
|
||||
|
||||
impl<T> EraseType for Option<&'_ T> {
|
||||
type Result = [u8; size_of::<Option<&'static ()>>()];
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ use crate::mir::interpret::{
|
||||
use crate::mir::interpret::{LitToConstError, LitToConstInput};
|
||||
use crate::mir::mono::CodegenUnit;
|
||||
use crate::query::erase::{erase, restore, Erase};
|
||||
use crate::query::plumbing::{query_ensure, query_get_at, DynamicQuery};
|
||||
use crate::query::plumbing::{query_ensure, query_get_at, CyclePlaceholder, DynamicQuery};
|
||||
use crate::thir;
|
||||
use crate::traits::query::{
|
||||
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal,
|
||||
@ -243,6 +243,16 @@ rustc_queries! {
|
||||
feedable
|
||||
}
|
||||
|
||||
/// Specialized instance of `type_of` that detects cycles that are due to
|
||||
/// revealing opaque because of an auto trait bound. Unless `CyclePlaceholder` needs
|
||||
/// to be handled separately, call `type_of` instead.
|
||||
query type_of_opaque(key: DefId) -> Result<ty::EarlyBinder<Ty<'tcx>>, CyclePlaceholder> {
|
||||
desc { |tcx|
|
||||
"computing type of opaque `{path}`",
|
||||
path = tcx.def_path_str(key),
|
||||
}
|
||||
}
|
||||
|
||||
query collect_return_position_impl_trait_in_trait_tys(key: DefId)
|
||||
-> Result<&'tcx FxHashMap<DefId, ty::EarlyBinder<Ty<'tcx>>>, ErrorGuaranteed>
|
||||
{
|
||||
|
@ -19,7 +19,7 @@ use rustc_query_system::dep_graph::SerializedDepNodeIndex;
|
||||
pub(crate) use rustc_query_system::query::QueryJobId;
|
||||
use rustc_query_system::query::*;
|
||||
use rustc_query_system::HandleCycleError;
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
|
||||
use std::ops::Deref;
|
||||
|
||||
pub struct QueryKeyStringCache {
|
||||
@ -52,7 +52,8 @@ pub struct DynamicQuery<'tcx, C: QueryCache> {
|
||||
pub loadable_from_disk:
|
||||
fn(tcx: TyCtxt<'tcx>, key: &C::Key, index: SerializedDepNodeIndex) -> bool,
|
||||
pub hash_result: HashResult<C::Value>,
|
||||
pub value_from_cycle_error: fn(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo<DepKind>]) -> C::Value,
|
||||
pub value_from_cycle_error:
|
||||
fn(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo<DepKind>], guar: ErrorGuaranteed) -> C::Value,
|
||||
pub format_value: fn(&C::Value) -> String,
|
||||
}
|
||||
|
||||
@ -629,3 +630,6 @@ impl<'tcx> TyCtxtAt<'tcx> {
|
||||
.unwrap_or_else(|| bug!("def_kind: unsupported node: {:?}", def_id))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, HashStable)]
|
||||
pub struct CyclePlaceholder(pub ErrorGuaranteed);
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::dep_graph::DepKind;
|
||||
use crate::query::plumbing::CyclePlaceholder;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
|
||||
use rustc_hir as hir;
|
||||
@ -8,20 +9,38 @@ use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_query_system::query::QueryInfo;
|
||||
use rustc_query_system::Value;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{ErrorGuaranteed, Span};
|
||||
|
||||
use std::fmt::Write;
|
||||
|
||||
impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for Ty<'_> {
|
||||
fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo<DepKind>]) -> Self {
|
||||
fn from_cycle_error(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
_: &[QueryInfo<DepKind>],
|
||||
guar: ErrorGuaranteed,
|
||||
) -> Self {
|
||||
// SAFETY: This is never called when `Self` is not `Ty<'tcx>`.
|
||||
// FIXME: Represent the above fact in the trait system somehow.
|
||||
unsafe { std::mem::transmute::<Ty<'tcx>, Ty<'_>>(Ty::new_misc_error(tcx)) }
|
||||
unsafe { std::mem::transmute::<Ty<'tcx>, Ty<'_>>(Ty::new_error(tcx, guar)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for Result<ty::EarlyBinder<Ty<'_>>, CyclePlaceholder> {
|
||||
fn from_cycle_error(
|
||||
_tcx: TyCtxt<'tcx>,
|
||||
_: &[QueryInfo<DepKind>],
|
||||
guar: ErrorGuaranteed,
|
||||
) -> Self {
|
||||
Err(CyclePlaceholder(guar))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::SymbolName<'_> {
|
||||
fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo<DepKind>]) -> Self {
|
||||
fn from_cycle_error(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
_: &[QueryInfo<DepKind>],
|
||||
_guar: ErrorGuaranteed,
|
||||
) -> Self {
|
||||
// SAFETY: This is never called when `Self` is not `SymbolName<'tcx>`.
|
||||
// FIXME: Represent the above fact in the trait system somehow.
|
||||
unsafe {
|
||||
@ -33,8 +52,12 @@ impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::SymbolName<'_> {
|
||||
}
|
||||
|
||||
impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::Binder<'_, ty::FnSig<'_>> {
|
||||
fn from_cycle_error(tcx: TyCtxt<'tcx>, stack: &[QueryInfo<DepKind>]) -> Self {
|
||||
let err = Ty::new_misc_error(tcx);
|
||||
fn from_cycle_error(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
stack: &[QueryInfo<DepKind>],
|
||||
guar: ErrorGuaranteed,
|
||||
) -> Self {
|
||||
let err = Ty::new_error(tcx, guar);
|
||||
|
||||
let arity = if let Some(frame) = stack.get(0)
|
||||
&& frame.query.dep_kind == DepKind::fn_sig
|
||||
@ -63,7 +86,11 @@ impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::Binder<'_, ty::FnSig<'_>> {
|
||||
}
|
||||
|
||||
impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for Representability {
|
||||
fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo<DepKind>]) -> Self {
|
||||
fn from_cycle_error(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
cycle: &[QueryInfo<DepKind>],
|
||||
_guar: ErrorGuaranteed,
|
||||
) -> Self {
|
||||
let mut item_and_field_ids = Vec::new();
|
||||
let mut representable_ids = FxHashSet::default();
|
||||
for info in cycle {
|
||||
@ -95,22 +122,35 @@ impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for Representability {
|
||||
}
|
||||
|
||||
impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::EarlyBinder<Ty<'_>> {
|
||||
fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo<DepKind>]) -> Self {
|
||||
ty::EarlyBinder::bind(Ty::from_cycle_error(tcx, cycle))
|
||||
fn from_cycle_error(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
cycle: &[QueryInfo<DepKind>],
|
||||
guar: ErrorGuaranteed,
|
||||
) -> Self {
|
||||
ty::EarlyBinder::bind(Ty::from_cycle_error(tcx, cycle, guar))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::EarlyBinder<ty::Binder<'_, ty::FnSig<'_>>> {
|
||||
fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo<DepKind>]) -> Self {
|
||||
ty::EarlyBinder::bind(ty::Binder::from_cycle_error(tcx, cycle))
|
||||
fn from_cycle_error(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
cycle: &[QueryInfo<DepKind>],
|
||||
guar: ErrorGuaranteed,
|
||||
) -> Self {
|
||||
ty::EarlyBinder::bind(ty::Binder::from_cycle_error(tcx, cycle, guar))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T> Value<TyCtxt<'tcx>, DepKind> for Result<T, &'_ ty::layout::LayoutError<'_>> {
|
||||
fn from_cycle_error(_tcx: TyCtxt<'tcx>, _cycle: &[QueryInfo<DepKind>]) -> Self {
|
||||
fn from_cycle_error(
|
||||
_tcx: TyCtxt<'tcx>,
|
||||
_cycle: &[QueryInfo<DepKind>],
|
||||
_guar: ErrorGuaranteed,
|
||||
) -> Self {
|
||||
// tcx.arena.alloc cannot be used because we are not allowed to use &'tcx LayoutError under
|
||||
// min_specialization. Since this is an error path anyways, leaking doesn't matter (and really,
|
||||
// tcx.arena.alloc is pretty much equal to leaking).
|
||||
// FIXME: `Cycle` should carry the ErrorGuaranteed
|
||||
Err(Box::leak(Box::new(ty::layout::LayoutError::Cycle)))
|
||||
}
|
||||
}
|
||||
|
@ -590,6 +590,8 @@ struct MirUsedCollector<'a, 'tcx> {
|
||||
body: &'a mir::Body<'tcx>,
|
||||
output: &'a mut MonoItems<'tcx>,
|
||||
instance: Instance<'tcx>,
|
||||
/// Spans for move size lints already emitted. Helps avoid duplicate lints.
|
||||
move_size_spans: Vec<Span>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
|
||||
@ -604,6 +606,45 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
|
||||
ty::EarlyBinder::bind(value),
|
||||
)
|
||||
}
|
||||
|
||||
fn check_move_size(&mut self, limit: usize, operand: &mir::Operand<'tcx>, location: Location) {
|
||||
let limit = Size::from_bytes(limit);
|
||||
let ty = operand.ty(self.body, self.tcx);
|
||||
let ty = self.monomorphize(ty);
|
||||
let Ok(layout) = self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)) else { return };
|
||||
if layout.size <= limit {
|
||||
return;
|
||||
}
|
||||
debug!(?layout);
|
||||
let source_info = self.body.source_info(location);
|
||||
debug!(?source_info);
|
||||
for span in &self.move_size_spans {
|
||||
if span.overlaps(source_info.span) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let lint_root = source_info.scope.lint_root(&self.body.source_scopes);
|
||||
debug!(?lint_root);
|
||||
let Some(lint_root) = lint_root else {
|
||||
// This happens when the issue is in a function from a foreign crate that
|
||||
// we monomorphized in the current crate. We can't get a `HirId` for things
|
||||
// in other crates.
|
||||
// FIXME: Find out where to report the lint on. Maybe simply crate-level lint root
|
||||
// but correct span? This would make the lint at least accept crate-level lint attributes.
|
||||
return;
|
||||
};
|
||||
self.tcx.emit_spanned_lint(
|
||||
LARGE_ASSIGNMENTS,
|
||||
lint_root,
|
||||
source_info.span,
|
||||
LargeAssignmentsLint {
|
||||
span: source_info.span,
|
||||
size: layout.size.bytes(),
|
||||
limit: limit.bytes(),
|
||||
},
|
||||
);
|
||||
self.move_size_spans.push(source_info.span);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
|
||||
@ -803,40 +844,9 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
|
||||
|
||||
fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) {
|
||||
self.super_operand(operand, location);
|
||||
let limit = self.tcx.move_size_limit().0;
|
||||
if limit == 0 {
|
||||
return;
|
||||
}
|
||||
let limit = Size::from_bytes(limit);
|
||||
let ty = operand.ty(self.body, self.tcx);
|
||||
let ty = self.monomorphize(ty);
|
||||
let layout = self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty));
|
||||
if let Ok(layout) = layout {
|
||||
if layout.size > limit {
|
||||
debug!(?layout);
|
||||
let source_info = self.body.source_info(location);
|
||||
debug!(?source_info);
|
||||
let lint_root = source_info.scope.lint_root(&self.body.source_scopes);
|
||||
debug!(?lint_root);
|
||||
let Some(lint_root) = lint_root else {
|
||||
// This happens when the issue is in a function from a foreign crate that
|
||||
// we monomorphized in the current crate. We can't get a `HirId` for things
|
||||
// in other crates.
|
||||
// FIXME: Find out where to report the lint on. Maybe simply crate-level lint root
|
||||
// but correct span? This would make the lint at least accept crate-level lint attributes.
|
||||
return;
|
||||
};
|
||||
self.tcx.emit_spanned_lint(
|
||||
LARGE_ASSIGNMENTS,
|
||||
lint_root,
|
||||
source_info.span,
|
||||
LargeAssignmentsLint {
|
||||
span: source_info.span,
|
||||
size: layout.size.bytes(),
|
||||
limit: limit.bytes(),
|
||||
},
|
||||
)
|
||||
}
|
||||
let move_size_limit = self.tcx.move_size_limit().0;
|
||||
if move_size_limit > 0 {
|
||||
self.check_move_size(move_size_limit, operand, location);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1363,7 +1373,8 @@ fn collect_used_items<'tcx>(
|
||||
output: &mut MonoItems<'tcx>,
|
||||
) {
|
||||
let body = tcx.instance_mir(instance.def);
|
||||
MirUsedCollector { tcx, body: &body, output, instance }.visit_body(&body);
|
||||
MirUsedCollector { tcx, body: &body, output, instance, move_size_spans: vec![] }
|
||||
.visit_body(&body);
|
||||
}
|
||||
|
||||
#[instrument(skip(tcx, output), level = "debug")]
|
||||
|
@ -7,6 +7,9 @@
|
||||
passes_abi =
|
||||
abi: {$abi}
|
||||
|
||||
passes_abi_of =
|
||||
fn_abi_of_instance({$fn_name}) = {$fn_abi}
|
||||
|
||||
passes_align =
|
||||
align: {$align}
|
||||
|
||||
|
93
compiler/rustc_passes/src/abi_test.rs
Normal file
93
compiler/rustc_passes/src/abi_test.rs
Normal file
@ -0,0 +1,93 @@
|
||||
use rustc_ast::Attribute;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::ty::layout::{FnAbiError, LayoutError};
|
||||
use rustc_middle::ty::{self, GenericArgs, Instance, TyCtxt};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use crate::errors::{AbiOf, UnrecognizedField};
|
||||
|
||||
pub fn test_abi(tcx: TyCtxt<'_>) {
|
||||
if !tcx.features().rustc_attrs {
|
||||
// if the `rustc_attrs` feature is not enabled, don't bother testing ABI
|
||||
return;
|
||||
}
|
||||
for id in tcx.hir().items() {
|
||||
match tcx.def_kind(id.owner_id) {
|
||||
DefKind::Fn => {
|
||||
for attr in tcx.get_attrs(id.owner_id, sym::rustc_abi) {
|
||||
dump_abi_of(tcx, id.owner_id.def_id.into(), attr);
|
||||
}
|
||||
}
|
||||
DefKind::Impl { .. } => {
|
||||
// To find associated functions we need to go into the child items here.
|
||||
for &id in tcx.associated_item_def_ids(id.owner_id) {
|
||||
if matches!(tcx.def_kind(id), DefKind::AssocFn) {
|
||||
for attr in tcx.get_attrs(id, sym::rustc_abi) {
|
||||
dump_abi_of(tcx, id, attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dump_abi_of(tcx: TyCtxt<'_>, item_def_id: DefId, attr: &Attribute) {
|
||||
let param_env = tcx.param_env(item_def_id);
|
||||
let args = GenericArgs::identity_for_item(tcx, item_def_id);
|
||||
let instance = match Instance::resolve(tcx, param_env, item_def_id, args) {
|
||||
Ok(Some(instance)) => instance,
|
||||
Ok(None) => {
|
||||
// Not sure what to do here, but `LayoutError::Unknown` seems reasonable?
|
||||
let ty = tcx.type_of(item_def_id).instantiate_identity();
|
||||
tcx.sess.emit_fatal(Spanned {
|
||||
node: LayoutError::Unknown(ty).into_diagnostic(),
|
||||
|
||||
span: tcx.def_span(item_def_id),
|
||||
});
|
||||
}
|
||||
Err(_guaranteed) => return,
|
||||
};
|
||||
match tcx.fn_abi_of_instance(param_env.and((instance, /* extra_args */ ty::List::empty()))) {
|
||||
Ok(abi) => {
|
||||
// Check out the `#[rustc_abi(..)]` attribute to tell what to dump.
|
||||
// The `..` are the names of fields to dump.
|
||||
let meta_items = attr.meta_item_list().unwrap_or_default();
|
||||
for meta_item in meta_items {
|
||||
match meta_item.name_or_empty() {
|
||||
sym::debug => {
|
||||
let fn_name = tcx.item_name(item_def_id);
|
||||
tcx.sess.emit_err(AbiOf {
|
||||
span: tcx.def_span(item_def_id),
|
||||
fn_name,
|
||||
fn_abi: format!("{:#?}", abi),
|
||||
});
|
||||
}
|
||||
|
||||
name => {
|
||||
tcx.sess.emit_err(UnrecognizedField { span: meta_item.span(), name });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(FnAbiError::Layout(layout_error)) => {
|
||||
tcx.sess.emit_fatal(Spanned {
|
||||
node: layout_error.into_diagnostic(),
|
||||
span: tcx.def_span(item_def_id),
|
||||
});
|
||||
}
|
||||
Err(FnAbiError::AdjustForForeignAbi(e)) => {
|
||||
// Sadly there seems to be no `into_diagnostic` for this case... and I am not sure if
|
||||
// this can even be reached. Anyway this is a perma-unstable debug attribute, an ICE
|
||||
// isn't the worst thing. Also this matches what codegen does.
|
||||
span_bug!(
|
||||
tcx.def_span(item_def_id),
|
||||
"error computing fn_abi_of_instance, cannot adjust for foreign ABI: {e:?}",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -913,6 +913,15 @@ pub struct LayoutOf {
|
||||
pub ty_layout: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_abi_of)]
|
||||
pub struct AbiOf {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub fn_name: Symbol,
|
||||
pub fn_abi: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_unrecognized_field)]
|
||||
pub struct UnrecognizedField {
|
||||
|
@ -11,16 +11,17 @@ use rustc_target::abi::{HasDataLayout, TargetDataLayout};
|
||||
use crate::errors::{Abi, Align, HomogeneousAggregate, LayoutOf, Size, UnrecognizedField};
|
||||
|
||||
pub fn test_layout(tcx: TyCtxt<'_>) {
|
||||
if tcx.features().rustc_attrs {
|
||||
if !tcx.features().rustc_attrs {
|
||||
// if the `rustc_attrs` feature is not enabled, don't bother testing layout
|
||||
for id in tcx.hir().items() {
|
||||
if matches!(
|
||||
tcx.def_kind(id.owner_id),
|
||||
DefKind::TyAlias { .. } | DefKind::Enum | DefKind::Struct | DefKind::Union
|
||||
) {
|
||||
for attr in tcx.get_attrs(id.owner_id, sym::rustc_layout) {
|
||||
dump_layout_of(tcx, id.owner_id.def_id, attr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
for id in tcx.hir().items() {
|
||||
if matches!(
|
||||
tcx.def_kind(id.owner_id),
|
||||
DefKind::TyAlias { .. } | DefKind::Enum | DefKind::Struct | DefKind::Union
|
||||
) {
|
||||
for attr in tcx.get_attrs(id.owner_id, sym::rustc_layout) {
|
||||
dump_layout_of(tcx, id.owner_id.def_id, attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
|
||||
use rustc_fluent_macro::fluent_messages;
|
||||
use rustc_middle::query::Providers;
|
||||
|
||||
pub mod abi_test;
|
||||
mod check_attr;
|
||||
mod check_const;
|
||||
pub mod dead;
|
||||
|
@ -41,7 +41,7 @@ use rustc_query_system::query::{
|
||||
};
|
||||
use rustc_query_system::HandleCycleError;
|
||||
use rustc_query_system::Value;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{ErrorGuaranteed, Span};
|
||||
|
||||
#[macro_use]
|
||||
mod plumbing;
|
||||
@ -146,8 +146,9 @@ where
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
cycle: &[QueryInfo<DepKind>],
|
||||
guar: ErrorGuaranteed,
|
||||
) -> Self::Value {
|
||||
(self.dynamic.value_from_cycle_error)(tcx, cycle)
|
||||
(self.dynamic.value_from_cycle_error)(tcx, cycle, guar)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -605,8 +605,8 @@ macro_rules! define_queries {
|
||||
} {
|
||||
|_tcx, _key, _prev_index, _index| None
|
||||
}),
|
||||
value_from_cycle_error: |tcx, cycle| {
|
||||
let result: queries::$name::Value<'tcx> = Value::from_cycle_error(tcx, cycle);
|
||||
value_from_cycle_error: |tcx, cycle, guar| {
|
||||
let result: queries::$name::Value<'tcx> = Value::from_cycle_error(tcx, cycle, guar);
|
||||
erase(result)
|
||||
},
|
||||
loadable_from_disk: |_tcx, _key, _index| {
|
||||
|
@ -8,6 +8,7 @@ use crate::query::DepNodeIndex;
|
||||
use crate::query::{QueryContext, QueryInfo, QueryState};
|
||||
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_span::ErrorGuaranteed;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
|
||||
@ -57,6 +58,7 @@ pub trait QueryConfig<Qcx: QueryContext>: Copy {
|
||||
self,
|
||||
tcx: Qcx::DepContext,
|
||||
cycle: &[QueryInfo<Qcx::DepKind>],
|
||||
guar: ErrorGuaranteed,
|
||||
) -> Self::Value;
|
||||
|
||||
fn anon(self) -> bool;
|
||||
|
@ -148,8 +148,8 @@ where
|
||||
use HandleCycleError::*;
|
||||
match query.handle_cycle_error() {
|
||||
Error => {
|
||||
error.emit();
|
||||
query.value_from_cycle_error(*qcx.dep_context(), &cycle_error.cycle)
|
||||
let guar = error.emit();
|
||||
query.value_from_cycle_error(*qcx.dep_context(), &cycle_error.cycle, guar)
|
||||
}
|
||||
Fatal => {
|
||||
error.emit();
|
||||
@ -157,8 +157,8 @@ where
|
||||
unreachable!()
|
||||
}
|
||||
DelayBug => {
|
||||
error.delay_as_bug();
|
||||
query.value_from_cycle_error(*qcx.dep_context(), &cycle_error.cycle)
|
||||
let guar = error.delay_as_bug();
|
||||
query.value_from_cycle_error(*qcx.dep_context(), &cycle_error.cycle, guar)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -300,7 +300,18 @@ where
|
||||
match result {
|
||||
Ok(()) => {
|
||||
let Some((v, index)) = query.query_cache(qcx).lookup(&key) else {
|
||||
cold_path(|| panic!("value must be in cache after waiting"))
|
||||
cold_path(|| {
|
||||
// We didn't find the query result in the query cache. Check if it was
|
||||
// poisoned due to a panic instead.
|
||||
let lock = query.query_state(qcx).active.get_shard_by_value(&key).lock();
|
||||
match lock.get(&key) {
|
||||
// The query we waited on panicked. Continue unwinding here.
|
||||
Some(QueryResult::Poisoned) => FatalError.raise(),
|
||||
_ => panic!(
|
||||
"query result must in the cache or the query must be poisoned after a wait"
|
||||
),
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
qcx.dep_context().profiler().query_cache_hit(index.into());
|
||||
|
@ -1,12 +1,14 @@
|
||||
use rustc_span::ErrorGuaranteed;
|
||||
|
||||
use crate::dep_graph::{DepContext, DepKind};
|
||||
use crate::query::QueryInfo;
|
||||
|
||||
pub trait Value<Tcx: DepContext, D: DepKind>: Sized {
|
||||
fn from_cycle_error(tcx: Tcx, cycle: &[QueryInfo<D>]) -> Self;
|
||||
fn from_cycle_error(tcx: Tcx, cycle: &[QueryInfo<D>], guar: ErrorGuaranteed) -> Self;
|
||||
}
|
||||
|
||||
impl<Tcx: DepContext, T, D: DepKind> Value<Tcx, D> for T {
|
||||
default fn from_cycle_error(tcx: Tcx, cycle: &[QueryInfo<D>]) -> T {
|
||||
default fn from_cycle_error(tcx: Tcx, cycle: &[QueryInfo<D>], _guar: ErrorGuaranteed) -> T {
|
||||
tcx.sess().abort_if_errors();
|
||||
// Ideally we would use `bug!` here. But bug! is only defined in rustc_middle, and it's
|
||||
// non-trivial to define it earlier.
|
||||
|
@ -772,9 +772,10 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
|
||||
self.r.record_partial_res(ty.id, PartialRes::new(res));
|
||||
visit::walk_ty(self, ty)
|
||||
}
|
||||
TyKind::ImplTrait(..) => {
|
||||
TyKind::ImplTrait(node_id, _) => {
|
||||
let candidates = self.lifetime_elision_candidates.take();
|
||||
visit::walk_ty(self, ty);
|
||||
self.record_lifetime_params_for_impl_trait(*node_id);
|
||||
self.lifetime_elision_candidates = candidates;
|
||||
}
|
||||
TyKind::TraitObject(bounds, ..) => {
|
||||
@ -909,8 +910,8 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
|
||||
&sig.decl.output,
|
||||
);
|
||||
|
||||
if let Some((async_node_id, span)) = sig.header.asyncness.opt_return_id() {
|
||||
this.record_lifetime_params_for_impl_trait(async_node_id, span);
|
||||
if let Some((async_node_id, _)) = sig.header.asyncness.opt_return_id() {
|
||||
this.record_lifetime_params_for_impl_trait(async_node_id);
|
||||
}
|
||||
},
|
||||
);
|
||||
@ -951,8 +952,8 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
|
||||
&declaration.output,
|
||||
);
|
||||
|
||||
if let Some((async_node_id, span)) = async_node_id {
|
||||
this.record_lifetime_params_for_impl_trait(async_node_id, span);
|
||||
if let Some((async_node_id, _)) = async_node_id {
|
||||
this.record_lifetime_params_for_impl_trait(async_node_id);
|
||||
}
|
||||
},
|
||||
);
|
||||
@ -4367,7 +4368,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
||||
/// We include all lifetime parameters, either named or "Fresh".
|
||||
/// The order of those parameters does not matter, as long as it is
|
||||
/// deterministic.
|
||||
fn record_lifetime_params_for_impl_trait(&mut self, impl_trait_node_id: NodeId, span: Span) {
|
||||
fn record_lifetime_params_for_impl_trait(&mut self, impl_trait_node_id: NodeId) {
|
||||
let mut extra_lifetime_params = vec![];
|
||||
|
||||
for rib in self.lifetime_ribs.iter().rev() {
|
||||
@ -4380,14 +4381,10 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
||||
extra_lifetime_params.extend(earlier_fresh);
|
||||
}
|
||||
}
|
||||
LifetimeRibKind::Generics { .. } => {}
|
||||
_ => {
|
||||
// We are in a function definition. We should only find `Generics`
|
||||
// and `AnonymousCreateParameter` inside the innermost `Item`.
|
||||
span_bug!(span, "unexpected rib kind: {:?}", rib.kind)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
self.r.extra_lifetime_params_map.insert(impl_trait_node_id, extra_lifetime_params);
|
||||
}
|
||||
|
||||
|
@ -1631,6 +1631,8 @@ options! {
|
||||
"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"),
|
||||
no_trait_vptr: bool = (false, parse_no_flag, [TRACKED],
|
||||
"disable generation of trait vptr in vtable for upcasting"),
|
||||
no_unique_section_names: bool = (false, parse_bool, [TRACKED],
|
||||
"do not use unique names for text and data sections when -Z function-sections is used"),
|
||||
normalize_docs: bool = (false, parse_bool, [TRACKED],
|
||||
|
@ -21,6 +21,8 @@
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(round_char_boundary)]
|
||||
#![feature(read_buf)]
|
||||
#![feature(new_uninit)]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(internal_features)]
|
||||
|
@ -24,6 +24,8 @@ use std::sync::atomic::Ordering;
|
||||
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::BorrowedBuf;
|
||||
use std::io::Read;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
@ -101,10 +103,13 @@ pub trait FileLoader {
|
||||
fn file_exists(&self, path: &Path) -> bool;
|
||||
|
||||
/// Read the contents of a UTF-8 file into memory.
|
||||
/// This function must return a String because we normalize
|
||||
/// source files, which may require resizing.
|
||||
fn read_file(&self, path: &Path) -> io::Result<String>;
|
||||
|
||||
/// Read the contents of a potentially non-UTF-8 file into memory.
|
||||
fn read_binary_file(&self, path: &Path) -> io::Result<Vec<u8>>;
|
||||
/// We don't normalize binary files, so we can start in an Lrc.
|
||||
fn read_binary_file(&self, path: &Path) -> io::Result<Lrc<[u8]>>;
|
||||
}
|
||||
|
||||
/// A FileLoader that uses std::fs to load real files.
|
||||
@ -119,8 +124,16 @@ impl FileLoader for RealFileLoader {
|
||||
fs::read_to_string(path)
|
||||
}
|
||||
|
||||
fn read_binary_file(&self, path: &Path) -> io::Result<Vec<u8>> {
|
||||
fs::read(path)
|
||||
fn read_binary_file(&self, path: &Path) -> io::Result<Lrc<[u8]>> {
|
||||
let mut file = fs::File::open(path)?;
|
||||
let len = file.metadata()?.len();
|
||||
|
||||
let mut bytes = Lrc::new_uninit_slice(len as usize);
|
||||
let mut buf = BorrowedBuf::from(Lrc::get_mut(&mut bytes).unwrap());
|
||||
file.read_buf_exact(buf.unfilled())?;
|
||||
// SAFETY: If the read_buf_exact call returns Ok(()), then we have
|
||||
// read len bytes and initialized the buffer.
|
||||
Ok(unsafe { bytes.assume_init() })
|
||||
}
|
||||
}
|
||||
|
||||
@ -228,7 +241,7 @@ impl SourceMap {
|
||||
///
|
||||
/// Unlike `load_file`, guarantees that no normalization like BOM-removal
|
||||
/// takes place.
|
||||
pub fn load_binary_file(&self, path: &Path) -> io::Result<Vec<u8>> {
|
||||
pub fn load_binary_file(&self, path: &Path) -> io::Result<Lrc<[u8]>> {
|
||||
let bytes = self.file_loader.read_binary_file(path)?;
|
||||
|
||||
// We need to add file to the `SourceMap`, so that it is present
|
||||
|
@ -1281,6 +1281,7 @@ symbols! {
|
||||
rust_eh_catch_typeinfo,
|
||||
rust_eh_personality,
|
||||
rustc,
|
||||
rustc_abi,
|
||||
rustc_allocator,
|
||||
rustc_allocator_zeroed,
|
||||
rustc_allow_const_fn_unstable,
|
||||
@ -1365,6 +1366,7 @@ symbols! {
|
||||
rustc_trivial_field_reads,
|
||||
rustc_unsafe_specialization_marker,
|
||||
rustc_variance,
|
||||
rustc_variance_of_opaques,
|
||||
rustdoc,
|
||||
rustdoc_internals,
|
||||
rustdoc_missing_doc_code_examples,
|
||||
|
@ -579,10 +579,9 @@ pub enum Conv {
|
||||
C,
|
||||
Rust,
|
||||
|
||||
/// For things unlikely to be called, where smaller caller codegen is
|
||||
/// preferred over raw speed.
|
||||
/// Stronger than just `#[cold]` because `fn` pointers might be incompatible.
|
||||
RustCold,
|
||||
Cold,
|
||||
PreserveMost,
|
||||
PreserveAll,
|
||||
|
||||
// Target-specific calling conventions.
|
||||
ArmAapcs,
|
||||
@ -605,9 +604,7 @@ pub enum Conv {
|
||||
AvrInterrupt,
|
||||
AvrNonBlockingInterrupt,
|
||||
|
||||
RiscvInterrupt {
|
||||
kind: RiscvInterruptKind,
|
||||
},
|
||||
RiscvInterrupt { kind: RiscvInterruptKind },
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
|
||||
|
@ -96,7 +96,9 @@ impl ToJson for crate::abi::call::Conv {
|
||||
let s = match self {
|
||||
Self::C => "C",
|
||||
Self::Rust => "Rust",
|
||||
Self::RustCold => "RustCold",
|
||||
Self::Cold => "Cold",
|
||||
Self::PreserveMost => "PreserveMost",
|
||||
Self::PreserveAll => "PreserveAll",
|
||||
Self::ArmAapcs => "ArmAapcs",
|
||||
Self::CCmseNonSecureCall => "CCmseNonSecureCall",
|
||||
Self::Msp430Intr => "Msp430Intr",
|
||||
|
@ -14,15 +14,33 @@ pub enum Abi {
|
||||
// hashing tests. These are used in many places, so giving them stable values reduces test
|
||||
// churn. The specific values are meaningless.
|
||||
Rust,
|
||||
C { unwind: bool },
|
||||
Cdecl { unwind: bool },
|
||||
Stdcall { unwind: bool },
|
||||
Fastcall { unwind: bool },
|
||||
Vectorcall { unwind: bool },
|
||||
Thiscall { unwind: bool },
|
||||
Aapcs { unwind: bool },
|
||||
Win64 { unwind: bool },
|
||||
SysV64 { unwind: bool },
|
||||
C {
|
||||
unwind: bool,
|
||||
},
|
||||
Cdecl {
|
||||
unwind: bool,
|
||||
},
|
||||
Stdcall {
|
||||
unwind: bool,
|
||||
},
|
||||
Fastcall {
|
||||
unwind: bool,
|
||||
},
|
||||
Vectorcall {
|
||||
unwind: bool,
|
||||
},
|
||||
Thiscall {
|
||||
unwind: bool,
|
||||
},
|
||||
Aapcs {
|
||||
unwind: bool,
|
||||
},
|
||||
Win64 {
|
||||
unwind: bool,
|
||||
},
|
||||
SysV64 {
|
||||
unwind: bool,
|
||||
},
|
||||
PtxKernel,
|
||||
Msp430Interrupt,
|
||||
X86Interrupt,
|
||||
@ -32,11 +50,16 @@ pub enum Abi {
|
||||
AvrNonBlockingInterrupt,
|
||||
CCmseNonSecureCall,
|
||||
Wasm,
|
||||
System { unwind: bool },
|
||||
System {
|
||||
unwind: bool,
|
||||
},
|
||||
RustIntrinsic,
|
||||
RustCall,
|
||||
PlatformIntrinsic,
|
||||
Unadjusted,
|
||||
/// For things unlikely to be called, where reducing register pressure in
|
||||
/// `extern "Rust"` callers is worth paying extra cost in the callee.
|
||||
/// Stronger than just `#[cold]` because `fn` pointers might be incompatible.
|
||||
RustCold,
|
||||
RiscvInterruptM,
|
||||
RiscvInterruptS,
|
||||
|
@ -2216,7 +2216,7 @@ impl Default for TargetOptions {
|
||||
mcount: "mcount".into(),
|
||||
llvm_mcount_intrinsic: None,
|
||||
llvm_abiname: "".into(),
|
||||
relax_elf_relocations: true,
|
||||
relax_elf_relocations: false,
|
||||
llvm_args: cvs![],
|
||||
use_ctors_section: false,
|
||||
eh_frame_header: true,
|
||||
@ -2276,6 +2276,13 @@ impl Target {
|
||||
Abi::Vectorcall { .. } if ["x86", "x86_64"].contains(&&self.arch[..]) => abi,
|
||||
Abi::Fastcall { unwind } | Abi::Vectorcall { unwind } => Abi::C { unwind },
|
||||
|
||||
// The Windows x64 calling convention we use for `extern "Rust"`
|
||||
// <https://learn.microsoft.com/en-us/cpp/build/x64-software-conventions#register-volatility-and-preservation>
|
||||
// expects the callee to save `xmm6` through `xmm15`, but `PreserveMost`
|
||||
// (that we use by default for `extern "rust-cold"`) doesn't save any of those.
|
||||
// So to avoid bloating callers, just use the Rust convention here.
|
||||
Abi::RustCold if self.is_like_windows && self.arch == "x86_64" => Abi::Rust,
|
||||
|
||||
abi => abi,
|
||||
}
|
||||
}
|
||||
|
@ -69,6 +69,7 @@ pub fn target() -> Target {
|
||||
position_independent_executables: true,
|
||||
pre_link_args,
|
||||
override_export_symbols: Some(EXPORT_SYMBOLS.iter().cloned().map(Cow::from).collect()),
|
||||
relax_elf_relocations: true,
|
||||
..Default::default()
|
||||
};
|
||||
Target {
|
||||
|
@ -2743,12 +2743,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
}
|
||||
ObligationCauseCode::BindingObligation(item_def_id, span)
|
||||
| ObligationCauseCode::ExprBindingObligation(item_def_id, span, ..) => {
|
||||
if self.tcx.is_diagnostic_item(sym::Send, item_def_id)
|
||||
|| self.tcx.lang_items().sync_trait() == Some(item_def_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let item_name = tcx.def_path_str(item_def_id);
|
||||
let short_item_name = with_forced_trimmed_paths!(tcx.def_path_str(item_def_id));
|
||||
let mut multispan = MultiSpan::from(span);
|
||||
|
@ -548,7 +548,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
obligation.cause.span,
|
||||
"GATs in trait object shouldn't have been considered",
|
||||
);
|
||||
return Err(SelectionError::Unimplemented);
|
||||
return Err(SelectionError::TraitNotObjectSafe(trait_predicate.trait_ref.def_id));
|
||||
}
|
||||
|
||||
// This maybe belongs in wf, but that can't (doesn't) handle
|
||||
|
@ -2346,14 +2346,15 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
||||
}
|
||||
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
|
||||
let ty = self.tcx().type_of(def_id);
|
||||
if ty.skip_binder().references_error() {
|
||||
return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id));
|
||||
}
|
||||
// We can resolve the `impl Trait` to its concrete type,
|
||||
// which enforces a DAG between the functions requiring
|
||||
// the auto trait bounds in question.
|
||||
t.rebind(vec![ty.instantiate(self.tcx(), args)])
|
||||
match self.tcx().type_of_opaque(def_id) {
|
||||
Ok(ty) => t.rebind(vec![ty.instantiate(self.tcx(), args)]),
|
||||
Err(_) => {
|
||||
return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -472,17 +472,11 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti
|
||||
let mut types_without_default_bounds = FxIndexSet::default();
|
||||
let sized_trait = tcx.lang_items().sized_trait();
|
||||
|
||||
if !args.is_empty() {
|
||||
let arg_names = args.iter().map(|k| k.to_string()).filter(|k| k != "'_").collect::<Vec<_>>();
|
||||
if !arg_names.is_empty() {
|
||||
types_without_default_bounds.extend(args.types());
|
||||
w.push('<');
|
||||
w.push_str(
|
||||
&args
|
||||
.iter()
|
||||
.map(|k| k.to_string())
|
||||
.filter(|k| k != "'_")
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
);
|
||||
w.push_str(&arg_names.join(", "));
|
||||
w.push('>');
|
||||
}
|
||||
|
||||
|
@ -152,7 +152,7 @@ fn prepare_vtable_segments_inner<'tcx, T>(
|
||||
while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() {
|
||||
segment_visitor(VtblSegment::TraitOwnEntries {
|
||||
trait_ref: inner_most_trait_ref,
|
||||
emit_vptr,
|
||||
emit_vptr: emit_vptr && !tcx.sess.opts.unstable_opts.no_trait_vptr,
|
||||
})?;
|
||||
|
||||
// If we've emitted (fed to `segment_visitor`) a trait that has methods present in the vtable,
|
||||
|
@ -172,7 +172,10 @@ fn conv_from_spec_abi(tcx: TyCtxt<'_>, abi: SpecAbi) -> Conv {
|
||||
use rustc_target::spec::abi::Abi::*;
|
||||
match tcx.sess.target.adjust_abi(abi) {
|
||||
RustIntrinsic | PlatformIntrinsic | Rust | RustCall => Conv::Rust,
|
||||
RustCold => Conv::RustCold,
|
||||
|
||||
// This is intentionally not using `Conv::Cold`, as that has to preserve
|
||||
// even SIMD registers, which is generally not a good trade-off.
|
||||
RustCold => Conv::PreserveMost,
|
||||
|
||||
// It's the ABI's job to select this, not ours.
|
||||
System { .. } => bug!("system abi should be selected elsewhere"),
|
||||
|
@ -343,18 +343,31 @@ extern "Rust" {
|
||||
fn __rust_alloc_error_handler(size: usize, align: usize) -> !;
|
||||
}
|
||||
|
||||
/// Abort on memory allocation error or failure.
|
||||
/// Signal a memory allocation error.
|
||||
///
|
||||
/// Callers of memory allocation APIs wishing to abort computation
|
||||
/// Callers of memory allocation APIs wishing to cease execution
|
||||
/// in response to an allocation error are encouraged to call this function,
|
||||
/// rather than directly invoking `panic!` or similar.
|
||||
/// rather than directly invoking [`panic!`] or similar.
|
||||
///
|
||||
/// The default behavior of this function is to print a message to standard error
|
||||
/// and abort the process.
|
||||
/// It can be replaced with [`set_alloc_error_hook`] and [`take_alloc_error_hook`].
|
||||
/// This function is guaranteed to diverge (not return normally with a value), but depending on
|
||||
/// global configuration, it may either panic (resulting in unwinding or aborting as per
|
||||
/// configuration for all panics), or abort the process (with no unwinding).
|
||||
///
|
||||
/// The default behavior is:
|
||||
///
|
||||
/// * If the binary links against `std` (typically the case), then
|
||||
/// print a message to standard error and abort the process.
|
||||
/// This behavior can be replaced with [`set_alloc_error_hook`] and [`take_alloc_error_hook`].
|
||||
/// Future versions of Rust may panic by default instead.
|
||||
///
|
||||
/// * If the binary does not link against `std` (all of its crates are marked
|
||||
/// [`#![no_std]`][no_std]), then call [`panic!`] with a message.
|
||||
/// [The panic handler] applies as to any panic.
|
||||
///
|
||||
/// [`set_alloc_error_hook`]: ../../std/alloc/fn.set_alloc_error_hook.html
|
||||
/// [`take_alloc_error_hook`]: ../../std/alloc/fn.take_alloc_error_hook.html
|
||||
/// [The panic handler]: https://doc.rust-lang.org/reference/runtime.html#the-panic_handler-attribute
|
||||
/// [no_std]: https://doc.rust-lang.org/reference/names/preludes.html#the-no_std-attribute
|
||||
#[stable(feature = "global_alloc", since = "1.28.0")]
|
||||
#[rustc_const_unstable(feature = "const_alloc_error", issue = "92523")]
|
||||
#[cfg(all(not(no_global_oom_handling), not(test)))]
|
||||
@ -395,9 +408,10 @@ pub mod __alloc_error_handler {
|
||||
if unsafe { __rust_alloc_error_handler_should_panic != 0 } {
|
||||
panic!("memory allocation of {size} bytes failed")
|
||||
} else {
|
||||
core::panicking::panic_nounwind_fmt(format_args!(
|
||||
"memory allocation of {size} bytes failed"
|
||||
))
|
||||
core::panicking::panic_nounwind_fmt(
|
||||
format_args!("memory allocation of {size} bytes failed"),
|
||||
/* force_no_backtrace */ false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,10 +79,12 @@ macro_rules! vec {
|
||||
///
|
||||
/// The first argument `format!` receives is a format string. This must be a string
|
||||
/// literal. The power of the formatting string is in the `{}`s contained.
|
||||
///
|
||||
/// Additional parameters passed to `format!` replace the `{}`s within the
|
||||
/// formatting string in the order given unless named or positional parameters
|
||||
/// are used; see [`std::fmt`] for more information.
|
||||
/// are used.
|
||||
///
|
||||
/// See [the formatting syntax documentation in `std::fmt`](../std/fmt/index.html)
|
||||
/// for details.
|
||||
///
|
||||
/// A common use for `format!` is concatenation and interpolation of strings.
|
||||
/// The same convention is used with [`print!`] and [`write!`] macros,
|
||||
@ -91,7 +93,6 @@ macro_rules! vec {
|
||||
/// To convert a single value to a string, use the [`to_string`] method. This
|
||||
/// will use the [`Display`] formatting trait.
|
||||
///
|
||||
/// [`std::fmt`]: ../std/fmt/index.html
|
||||
/// [`print!`]: ../std/macro.print.html
|
||||
/// [`write!`]: core::write
|
||||
/// [`to_string`]: crate::string::ToString
|
||||
|
@ -849,7 +849,8 @@ pub(crate) mod builtin {
|
||||
/// assert_eq!(display, debug);
|
||||
/// ```
|
||||
///
|
||||
/// For more information, see the documentation in [`std::fmt`].
|
||||
/// See [the formatting documentation in `std::fmt`](../std/fmt/index.html)
|
||||
/// for details of the macro argument syntax, and further information.
|
||||
///
|
||||
/// [`Display`]: crate::fmt::Display
|
||||
/// [`Debug`]: crate::fmt::Debug
|
||||
|
@ -8,8 +8,8 @@ tests. `panic!` is closely tied with the `unwrap` method of both
|
||||
[`Option`][ounwrap] and [`Result`][runwrap] enums. Both implementations call
|
||||
`panic!` when they are set to [`None`] or [`Err`] variants.
|
||||
|
||||
When using `panic!()` you can specify a string payload, that is built using
|
||||
the [`format!`] syntax. That payload is used when injecting the panic into
|
||||
When using `panic!()` you can specify a string payload that is built using
|
||||
[formatting syntax]. That payload is used when injecting the panic into
|
||||
the calling Rust thread, causing the thread to panic entirely.
|
||||
|
||||
The behavior of the default `std` hook, i.e. the code that runs directly
|
||||
@ -18,6 +18,7 @@ after the panic is invoked, is to print the message payload to
|
||||
call. You can override the panic hook using [`std::panic::set_hook()`].
|
||||
Inside the hook a panic can be accessed as a `&dyn Any + Send`,
|
||||
which contains either a `&str` or `String` for regular `panic!()` invocations.
|
||||
(Whether a particular invocation contains the payload at type `&str` or `String` is unspecified and can change.)
|
||||
To panic with a value of another other type, [`panic_any`] can be used.
|
||||
|
||||
See also the macro [`compile_error!`], for raising errors during compilation.
|
||||
@ -55,7 +56,7 @@ For more detailed information about error handling check out the [book] or the
|
||||
[`panic_any`]: ../std/panic/fn.panic_any.html
|
||||
[`Box`]: ../std/boxed/struct.Box.html
|
||||
[`Any`]: crate::any::Any
|
||||
[`format!`]: ../std/macro.format.html
|
||||
[`format!` syntax]: ../std/fmt/index.html
|
||||
[book]: ../book/ch09-00-error-handling.html
|
||||
[`std::result`]: ../std/result/index.html
|
||||
|
||||
@ -64,6 +65,29 @@ For more detailed information about error handling check out the [book] or the
|
||||
If the main thread panics it will terminate all your threads and end your
|
||||
program with code `101`.
|
||||
|
||||
# Editions
|
||||
|
||||
Behavior of the panic macros changed over editions.
|
||||
|
||||
## 2021 and later
|
||||
|
||||
In Rust 2021 and later, `panic!` always requires a format string and
|
||||
the applicable format arguments, and is the same in `core` and `std`.
|
||||
Use [`std::panic::panic_any(x)`](../std/panic/fn.panic_any.html) to
|
||||
panic with an arbitrary payload.
|
||||
|
||||
## 2018 and 2015
|
||||
|
||||
In Rust Editions prior to 2021, `std::panic!(x)` with a single
|
||||
argument directly uses that argument as a payload.
|
||||
This is true even if the argument is a string literal.
|
||||
For example, `panic!("problem: {reason}")` panics with a
|
||||
payload of literally `"problem: {reason}"` (a `&'static str`).
|
||||
|
||||
`core::panic!(x)` with a single argument requires that `x` be `&str`,
|
||||
but otherwise behaves like `std::panic!`. In particular, the string
|
||||
need not be a literal, and is not interpreted as a format string.
|
||||
|
||||
# Examples
|
||||
|
||||
```should_panic
|
||||
|
@ -76,11 +76,8 @@ macro marker_impls {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "Send")]
|
||||
#[rustc_on_unimplemented(
|
||||
on(_Self = "std::rc::Rc<T, A>", note = "use `std::sync::Arc` instead of `std::rc::Rc`"),
|
||||
message = "`{Self}` cannot be sent between threads safely",
|
||||
label = "`{Self}` cannot be sent between threads safely",
|
||||
note = "consider using `std::sync::Arc<{Self}>`; for more information visit \
|
||||
<https://doc.rust-lang.org/book/ch16-03-shared-state.html>"
|
||||
label = "`{Self}` cannot be sent between threads safely"
|
||||
)]
|
||||
pub unsafe auto trait Send {
|
||||
// empty.
|
||||
@ -631,11 +628,8 @@ impl<T: ?Sized> Copy for &T {}
|
||||
any(_Self = "core::cell::RefCell<T>", _Self = "std::cell::RefCell<T>"),
|
||||
note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead",
|
||||
),
|
||||
on(_Self = "std::rc::Rc<T, A>", note = "use `std::sync::Arc` instead of `std::rc::Rc`"),
|
||||
message = "`{Self}` cannot be shared between threads safely",
|
||||
label = "`{Self}` cannot be shared between threads safely",
|
||||
note = "consider using `std::sync::Arc<{Self}>`; for more information visit \
|
||||
<https://doc.rust-lang.org/book/ch16-03-shared-state.html>"
|
||||
label = "`{Self}` cannot be shared between threads safely"
|
||||
)]
|
||||
pub unsafe auto trait Sync {
|
||||
// FIXME(estebank): once support to add notes in `rustc_on_unimplemented`
|
||||
|
@ -1856,13 +1856,7 @@ impl fmt::Display for Ipv6Addr {
|
||||
if f.precision().is_none() && f.width().is_none() {
|
||||
let segments = self.segments();
|
||||
|
||||
// Special case for :: and ::1; otherwise they get written with the
|
||||
// IPv4 formatter
|
||||
if self.is_unspecified() {
|
||||
f.write_str("::")
|
||||
} else if self.is_loopback() {
|
||||
f.write_str("::1")
|
||||
} else if let Some(ipv4) = self.to_ipv4_mapped() {
|
||||
if let Some(ipv4) = self.to_ipv4_mapped() {
|
||||
write!(f, "::ffff:{}", ipv4)
|
||||
} else {
|
||||
#[derive(Copy, Clone, Default)]
|
||||
|
@ -277,6 +277,14 @@ pub mod consts {
|
||||
#[stable(feature = "tau_constant", since = "1.47.0")]
|
||||
pub const TAU: f32 = 6.28318530717958647692528676655900577_f32;
|
||||
|
||||
/// The golden ratio (φ)
|
||||
#[unstable(feature = "more_float_constants", issue = "103883")]
|
||||
pub const PHI: f32 = 1.618033988749894848204586834365638118_f32;
|
||||
|
||||
/// The Euler-Mascheroni constant (γ)
|
||||
#[unstable(feature = "more_float_constants", issue = "103883")]
|
||||
pub const EGAMMA: f32 = 0.577215664901532860606512090082402431_f32;
|
||||
|
||||
/// π/2
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub const FRAC_PI_2: f32 = 1.57079632679489661923132169163975144_f32;
|
||||
@ -301,6 +309,10 @@ pub mod consts {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub const FRAC_1_PI: f32 = 0.318309886183790671537767526745028724_f32;
|
||||
|
||||
/// 1/sqrt(π)
|
||||
#[unstable(feature = "more_float_constants", issue = "103883")]
|
||||
pub const FRAC_1_SQRT_PI: f32 = 0.564189583547756286948079451560772586_f32;
|
||||
|
||||
/// 2/π
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub const FRAC_2_PI: f32 = 0.636619772367581343075535053490057448_f32;
|
||||
@ -317,6 +329,14 @@ pub mod consts {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub const FRAC_1_SQRT_2: f32 = 0.707106781186547524400844362104849039_f32;
|
||||
|
||||
/// sqrt(3)
|
||||
#[unstable(feature = "more_float_constants", issue = "103883")]
|
||||
pub const SQRT_3: f32 = 1.732050807568877293527446341505872367_f32;
|
||||
|
||||
/// 1/sqrt(3)
|
||||
#[unstable(feature = "more_float_constants", issue = "103883")]
|
||||
pub const FRAC_1_SQRT_3: f32 = 0.577350269189625764509148780501957456_f32;
|
||||
|
||||
/// Euler's number (e)
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub const E: f32 = 2.71828182845904523536028747135266250_f32;
|
||||
|
@ -277,6 +277,14 @@ pub mod consts {
|
||||
#[stable(feature = "tau_constant", since = "1.47.0")]
|
||||
pub const TAU: f64 = 6.28318530717958647692528676655900577_f64;
|
||||
|
||||
/// The golden ratio (φ)
|
||||
#[unstable(feature = "more_float_constants", issue = "103883")]
|
||||
pub const PHI: f64 = 1.618033988749894848204586834365638118_f64;
|
||||
|
||||
/// The Euler-Mascheroni constant (γ)
|
||||
#[unstable(feature = "more_float_constants", issue = "103883")]
|
||||
pub const EGAMMA: f64 = 0.577215664901532860606512090082402431_f64;
|
||||
|
||||
/// π/2
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub const FRAC_PI_2: f64 = 1.57079632679489661923132169163975144_f64;
|
||||
@ -301,6 +309,10 @@ pub mod consts {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub const FRAC_1_PI: f64 = 0.318309886183790671537767526745028724_f64;
|
||||
|
||||
/// 1/sqrt(π)
|
||||
#[unstable(feature = "more_float_constants", issue = "103883")]
|
||||
pub const FRAC_1_SQRT_PI: f64 = 0.564189583547756286948079451560772586_f64;
|
||||
|
||||
/// 2/π
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub const FRAC_2_PI: f64 = 0.636619772367581343075535053490057448_f64;
|
||||
@ -317,6 +329,14 @@ pub mod consts {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub const FRAC_1_SQRT_2: f64 = 0.707106781186547524400844362104849039_f64;
|
||||
|
||||
/// sqrt(3)
|
||||
#[unstable(feature = "more_float_constants", issue = "103883")]
|
||||
pub const SQRT_3: f64 = 1.732050807568877293527446341505872367_f64;
|
||||
|
||||
/// 1/sqrt(3)
|
||||
#[unstable(feature = "more_float_constants", issue = "103883")]
|
||||
pub const FRAC_1_SQRT_3: f64 = 0.577350269189625764509148780501957456_f64;
|
||||
|
||||
/// Euler's number (e)
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub const E: f64 = 2.71828182845904523536028747135266250_f64;
|
||||
|
@ -217,8 +217,13 @@ pub trait Drop {
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Given that a [`panic!`] will call `drop` as it unwinds, any [`panic!`]
|
||||
/// in a `drop` implementation will likely abort.
|
||||
/// Implementations should generally avoid [`panic!`]ing, because `drop()` may itself be called
|
||||
/// during unwinding due to a panic, and if the `drop()` panics in that situation (a “double
|
||||
/// panic”), this will likely abort the program. It is possible to check [`panicking()`] first,
|
||||
/// which may be desirable for a `Drop` implementation that is reporting a bug of the kind
|
||||
/// “you didn't finish using this before it was dropped”; but most types should simply clean up
|
||||
/// their owned allocations or other resources and return normally from `drop()`, regardless of
|
||||
/// what state they are in.
|
||||
///
|
||||
/// Note that even if this panics, the value is considered to be dropped;
|
||||
/// you must not cause `drop` to be called again. This is normally automatically
|
||||
@ -227,6 +232,7 @@ pub trait Drop {
|
||||
///
|
||||
/// [E0040]: ../../error_codes/E0040.html
|
||||
/// [`panic!`]: crate::panic!
|
||||
/// [`panicking()`]: ../../std/thread/fn.panicking.html
|
||||
/// [`mem::drop`]: drop
|
||||
/// [`ptr::drop_in_place`]: crate::ptr::drop_in_place
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
|
@ -28,6 +28,7 @@ pub struct PanicInfo<'a> {
|
||||
message: Option<&'a fmt::Arguments<'a>>,
|
||||
location: &'a Location<'a>,
|
||||
can_unwind: bool,
|
||||
force_no_backtrace: bool,
|
||||
}
|
||||
|
||||
impl<'a> PanicInfo<'a> {
|
||||
@ -42,9 +43,10 @@ impl<'a> PanicInfo<'a> {
|
||||
message: Option<&'a fmt::Arguments<'a>>,
|
||||
location: &'a Location<'a>,
|
||||
can_unwind: bool,
|
||||
force_no_backtrace: bool,
|
||||
) -> Self {
|
||||
struct NoPayload;
|
||||
PanicInfo { location, message, payload: &NoPayload, can_unwind }
|
||||
PanicInfo { location, message, payload: &NoPayload, can_unwind, force_no_backtrace }
|
||||
}
|
||||
|
||||
#[unstable(
|
||||
@ -141,6 +143,17 @@ impl<'a> PanicInfo<'a> {
|
||||
pub fn can_unwind(&self) -> bool {
|
||||
self.can_unwind
|
||||
}
|
||||
|
||||
#[unstable(
|
||||
feature = "panic_internals",
|
||||
reason = "internal details of the implementation of the `panic!` and related macros",
|
||||
issue = "none"
|
||||
)]
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub fn force_no_backtrace(&self) -> bool {
|
||||
self.force_no_backtrace
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "panic_hook_display", since = "1.26.0")]
|
||||
|
@ -61,7 +61,12 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
|
||||
fn panic_impl(pi: &PanicInfo<'_>) -> !;
|
||||
}
|
||||
|
||||
let pi = PanicInfo::internal_constructor(Some(&fmt), Location::caller(), true);
|
||||
let pi = PanicInfo::internal_constructor(
|
||||
Some(&fmt),
|
||||
Location::caller(),
|
||||
/* can_unwind */ true,
|
||||
/* force_no_backtrace */ false,
|
||||
);
|
||||
|
||||
// SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call.
|
||||
unsafe { panic_impl(&pi) }
|
||||
@ -77,7 +82,7 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
|
||||
// and unwinds anyway, we will hit the "unwinding out of nounwind function" guard,
|
||||
// which causes a "panic in a function that cannot unwind".
|
||||
#[rustc_nounwind]
|
||||
pub fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>) -> ! {
|
||||
pub fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: bool) -> ! {
|
||||
if cfg!(feature = "panic_immediate_abort") {
|
||||
super::intrinsics::abort()
|
||||
}
|
||||
@ -90,7 +95,12 @@ pub fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>) -> ! {
|
||||
}
|
||||
|
||||
// PanicInfo with the `can_unwind` flag set to false forces an abort.
|
||||
let pi = PanicInfo::internal_constructor(Some(&fmt), Location::caller(), false);
|
||||
let pi = PanicInfo::internal_constructor(
|
||||
Some(&fmt),
|
||||
Location::caller(),
|
||||
/* can_unwind */ false,
|
||||
force_no_backtrace,
|
||||
);
|
||||
|
||||
// SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call.
|
||||
unsafe { panic_impl(&pi) }
|
||||
@ -123,7 +133,15 @@ pub const fn panic(expr: &'static str) -> ! {
|
||||
#[lang = "panic_nounwind"] // needed by codegen for non-unwinding panics
|
||||
#[rustc_nounwind]
|
||||
pub fn panic_nounwind(expr: &'static str) -> ! {
|
||||
panic_nounwind_fmt(fmt::Arguments::new_const(&[expr]));
|
||||
panic_nounwind_fmt(fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ false);
|
||||
}
|
||||
|
||||
/// Like `panic_nounwind`, but also inhibits showing a backtrace.
|
||||
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
|
||||
#[cfg_attr(feature = "panic_immediate_abort", inline)]
|
||||
#[rustc_nounwind]
|
||||
pub fn panic_nounwind_nobacktrace(expr: &'static str) -> ! {
|
||||
panic_nounwind_fmt(fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ true);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -172,9 +190,12 @@ fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! {
|
||||
super::intrinsics::abort()
|
||||
}
|
||||
|
||||
panic_nounwind_fmt(format_args!(
|
||||
"misaligned pointer dereference: address must be a multiple of {required:#x} but is {found:#x}"
|
||||
))
|
||||
panic_nounwind_fmt(
|
||||
format_args!(
|
||||
"misaligned pointer dereference: address must be a multiple of {required:#x} but is {found:#x}"
|
||||
),
|
||||
/* force_no_backtrace */ false,
|
||||
)
|
||||
}
|
||||
|
||||
/// Panic because we cannot unwind out of a function.
|
||||
@ -205,7 +226,7 @@ fn panic_cannot_unwind() -> ! {
|
||||
#[rustc_nounwind]
|
||||
fn panic_in_cleanup() -> ! {
|
||||
// Keep the text in sync with `UnwindTerminateReason::as_str` in `rustc_middle`.
|
||||
panic_nounwind("panic in a destructor during cleanup")
|
||||
panic_nounwind_nobacktrace("panic in a destructor during cleanup")
|
||||
}
|
||||
|
||||
/// This function is used instead of panic_fmt in const eval.
|
||||
|
@ -656,10 +656,10 @@ impl Duration {
|
||||
#[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")]
|
||||
pub const fn checked_div(self, rhs: u32) -> Option<Duration> {
|
||||
if rhs != 0 {
|
||||
let secs = self.secs / (rhs as u64);
|
||||
let carry = self.secs - secs * (rhs as u64);
|
||||
let extra_nanos = carry * (NANOS_PER_SEC as u64) / (rhs as u64);
|
||||
let nanos = self.nanos.0 / rhs + (extra_nanos as u32);
|
||||
let (secs, extra_secs) = (self.secs / (rhs as u64), self.secs % (rhs as u64));
|
||||
let (mut nanos, extra_nanos) = (self.nanos.0 / rhs, self.nanos.0 % rhs);
|
||||
nanos +=
|
||||
((extra_secs * (NANOS_PER_SEC as u64) + extra_nanos as u64) / (rhs as u64)) as u32;
|
||||
debug_assert!(nanos < NANOS_PER_SEC);
|
||||
Some(Duration::new(secs, nanos))
|
||||
} else {
|
||||
|
@ -170,6 +170,7 @@ fn saturating_mul() {
|
||||
fn div() {
|
||||
assert_eq!(Duration::new(0, 1) / 2, Duration::new(0, 0));
|
||||
assert_eq!(Duration::new(1, 1) / 3, Duration::new(0, 333_333_333));
|
||||
assert_eq!(Duration::new(1, 1) / 7, Duration::new(0, 142_857_143));
|
||||
assert_eq!(Duration::new(99, 999_999_000) / 100, Duration::new(0, 999_999_990));
|
||||
}
|
||||
|
||||
|
@ -290,15 +290,29 @@ static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());
|
||||
|
||||
/// Registers a custom allocation error hook, replacing any that was previously registered.
|
||||
///
|
||||
/// The allocation error hook is invoked when an infallible memory allocation fails, before
|
||||
/// the runtime aborts. The default hook prints a message to standard error,
|
||||
/// but this behavior can be customized with the [`set_alloc_error_hook`] and
|
||||
/// [`take_alloc_error_hook`] functions.
|
||||
/// The allocation error hook is invoked when an infallible memory allocation fails — that is,
|
||||
/// as a consequence of calling [`handle_alloc_error`] — before the runtime aborts.
|
||||
///
|
||||
/// The hook is provided with a `Layout` struct which contains information
|
||||
/// The allocation error hook is a global resource. [`take_alloc_error_hook`] may be used to
|
||||
/// retrieve a previously registered hook and wrap or discard it.
|
||||
///
|
||||
/// # What the provided `hook` function should expect
|
||||
///
|
||||
/// The hook function is provided with a [`Layout`] struct which contains information
|
||||
/// about the allocation that failed.
|
||||
///
|
||||
/// The allocation error hook is a global resource.
|
||||
/// The hook function may choose to panic or abort; in the event that it returns normally, this
|
||||
/// will cause an immediate abort.
|
||||
///
|
||||
/// Since [`take_alloc_error_hook`] is a safe function that allows retrieving the hook, the hook
|
||||
/// function must be _sound_ to call even if no memory allocations were attempted.
|
||||
///
|
||||
/// # The default hook
|
||||
///
|
||||
/// The default hook, used if [`set_alloc_error_hook`] is never called, prints a message to
|
||||
/// standard error (and then returns, causing the runtime to abort the process).
|
||||
/// Compiler options may cause it to panic instead, and the default behavior may be changed
|
||||
/// to panicking in future versions of Rust.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -41,6 +41,9 @@ macro_rules! panic {
|
||||
/// Use `print!` only for the primary output of your program. Use
|
||||
/// [`eprint!`] instead to print error and progress messages.
|
||||
///
|
||||
/// See [the formatting documentation in `std::fmt`](../std/fmt/index.html)
|
||||
/// for details of the macro argument syntax.
|
||||
///
|
||||
/// [flush]: crate::io::Write::flush
|
||||
/// [`println!`]: crate::println
|
||||
/// [`eprint!`]: crate::eprint
|
||||
@ -103,6 +106,9 @@ macro_rules! print {
|
||||
/// Use `println!` only for the primary output of your program. Use
|
||||
/// [`eprintln!`] instead to print error and progress messages.
|
||||
///
|
||||
/// See [the formatting documentation in `std::fmt`](../std/fmt/index.html)
|
||||
/// for details of the macro argument syntax.
|
||||
///
|
||||
/// [`std::fmt`]: crate::fmt
|
||||
/// [`eprintln!`]: crate::eprintln
|
||||
/// [lock]: crate::io::Stdout
|
||||
@ -150,6 +156,9 @@ macro_rules! println {
|
||||
/// [`io::stderr`]: crate::io::stderr
|
||||
/// [`io::stdout`]: crate::io::stdout
|
||||
///
|
||||
/// See [the formatting documentation in `std::fmt`](../std/fmt/index.html)
|
||||
/// for details of the macro argument syntax.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if writing to `io::stderr` fails.
|
||||
@ -181,6 +190,9 @@ macro_rules! eprint {
|
||||
/// Use `eprintln!` only for error and progress messages. Use `println!`
|
||||
/// instead for the primary output of your program.
|
||||
///
|
||||
/// See [the formatting documentation in `std::fmt`](../std/fmt/index.html)
|
||||
/// for details of the macro argument syntax.
|
||||
///
|
||||
/// [`io::stderr`]: crate::io::stderr
|
||||
/// [`io::stdout`]: crate::io::stdout
|
||||
/// [`println!`]: crate::println
|
||||
|
@ -662,7 +662,7 @@ fn test_send_vectored_fds_unix_stream() {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
|
||||
fn test_send_vectored_with_ancillary_to_unix_datagram() {
|
||||
|
@ -192,6 +192,66 @@ pub trait CommandExt: Sealed {
|
||||
/// ```
|
||||
#[unstable(feature = "windows_process_extensions_async_pipes", issue = "98289")]
|
||||
fn async_pipes(&mut self, always_async: bool) -> &mut process::Command;
|
||||
|
||||
/// Sets a raw attribute on the command, providing extended configuration options for Windows processes.
|
||||
///
|
||||
/// This method allows you to specify custom attributes for a child process on Windows systems using raw attribute values.
|
||||
/// Raw attributes provide extended configurability for process creation, but their usage can be complex and potentially unsafe.
|
||||
///
|
||||
/// The `attribute` parameter specifies the raw attribute to be set, while the `value` parameter holds the value associated with that attribute.
|
||||
/// Please refer to the [`windows-rs`](https://microsoft.github.io/windows-docs-rs/doc/windows/) documentation or the [`Win32 API documentation`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute) for detailed information about available attributes and their meanings.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The maximum number of raw attributes is the value of [`u32::MAX`].
|
||||
/// If this limit is exceeded, the call to [`process::Command::spawn`] will return an `Error` indicating that the maximum number of attributes has been exceeded.
|
||||
/// # Safety
|
||||
///
|
||||
/// The usage of raw attributes is potentially unsafe and should be done with caution. Incorrect attribute values or improper configuration can lead to unexpected behavior or errors.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// The following example demonstrates how to create a child process with a specific parent process ID using a raw attribute.
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(windows_process_extensions_raw_attribute)]
|
||||
/// use std::os::windows::{process::CommandExt, io::AsRawHandle};
|
||||
/// use std::process::Command;
|
||||
///
|
||||
/// # struct ProcessDropGuard(std::process::Child);
|
||||
/// # impl Drop for ProcessDropGuard {
|
||||
/// # fn drop(&mut self) {
|
||||
/// # let _ = self.0.kill();
|
||||
/// # }
|
||||
/// # }
|
||||
///
|
||||
/// let parent = Command::new("cmd").spawn()?;
|
||||
///
|
||||
/// let mut child_cmd = Command::new("cmd");
|
||||
///
|
||||
/// const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000;
|
||||
///
|
||||
/// unsafe {
|
||||
/// child_cmd.raw_attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, parent.as_raw_handle() as isize);
|
||||
/// }
|
||||
/// #
|
||||
/// # let parent = ProcessDropGuard(parent);
|
||||
///
|
||||
/// let mut child = child_cmd.spawn()?;
|
||||
///
|
||||
/// # child.kill()?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
///
|
||||
/// # Safety Note
|
||||
///
|
||||
/// Remember that improper use of raw attributes can lead to undefined behavior or security vulnerabilities. Always consult the documentation and ensure proper attribute values are used.
|
||||
#[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")]
|
||||
unsafe fn raw_attribute<T: Copy + Send + Sync + 'static>(
|
||||
&mut self,
|
||||
attribute: usize,
|
||||
value: T,
|
||||
) -> &mut process::Command;
|
||||
}
|
||||
|
||||
#[stable(feature = "windows_process_extensions", since = "1.16.0")]
|
||||
@ -219,6 +279,15 @@ impl CommandExt for process::Command {
|
||||
let _ = always_async;
|
||||
self
|
||||
}
|
||||
|
||||
unsafe fn raw_attribute<T: Copy + Send + Sync + 'static>(
|
||||
&mut self,
|
||||
attribute: usize,
|
||||
value: T,
|
||||
) -> &mut process::Command {
|
||||
self.as_inner_mut().raw_attribute(attribute, value);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "windows_process_extensions_main_thread_handle", issue = "96723")]
|
||||
|
@ -246,7 +246,9 @@ fn default_hook(info: &PanicInfo<'_>) {
|
||||
pub fn panic_hook_with_disk_dump(info: &PanicInfo<'_>, path: Option<&crate::path::Path>) {
|
||||
// If this is a double panic, make sure that we print a backtrace
|
||||
// for this panic. Otherwise only print it if logging is enabled.
|
||||
let backtrace = if panic_count::get_count() >= 2 {
|
||||
let backtrace = if info.force_no_backtrace() {
|
||||
None
|
||||
} else if panic_count::get_count() >= 2 {
|
||||
BacktraceStyle::full()
|
||||
} else {
|
||||
crate::panic::get_backtrace_style()
|
||||
@ -294,7 +296,7 @@ pub fn panic_hook_with_disk_dump(info: &PanicInfo<'_>, path: Option<&crate::path
|
||||
}
|
||||
}
|
||||
}
|
||||
// If backtraces aren't supported, do nothing.
|
||||
// If backtraces aren't supported or are forced-off, do nothing.
|
||||
None => {}
|
||||
}
|
||||
};
|
||||
@ -615,14 +617,23 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
|
||||
let loc = info.location().unwrap(); // The current implementation always returns Some
|
||||
let msg = info.message().unwrap(); // The current implementation always returns Some
|
||||
crate::sys_common::backtrace::__rust_end_short_backtrace(move || {
|
||||
// FIXME: can we just pass `info` along rather than taking it apart here, only to have
|
||||
// `rust_panic_with_hook` construct a new `PanicInfo`?
|
||||
if let Some(msg) = msg.as_str() {
|
||||
rust_panic_with_hook(&mut StrPanicPayload(msg), info.message(), loc, info.can_unwind());
|
||||
rust_panic_with_hook(
|
||||
&mut StrPanicPayload(msg),
|
||||
info.message(),
|
||||
loc,
|
||||
info.can_unwind(),
|
||||
info.force_no_backtrace(),
|
||||
);
|
||||
} else {
|
||||
rust_panic_with_hook(
|
||||
&mut PanicPayload::new(msg),
|
||||
info.message(),
|
||||
loc,
|
||||
info.can_unwind(),
|
||||
info.force_no_backtrace(),
|
||||
);
|
||||
}
|
||||
})
|
||||
@ -647,7 +658,13 @@ pub const fn begin_panic<M: Any + Send>(msg: M) -> ! {
|
||||
|
||||
let loc = Location::caller();
|
||||
return crate::sys_common::backtrace::__rust_end_short_backtrace(move || {
|
||||
rust_panic_with_hook(&mut PanicPayload::new(msg), None, loc, true)
|
||||
rust_panic_with_hook(
|
||||
&mut PanicPayload::new(msg),
|
||||
None,
|
||||
loc,
|
||||
/* can_unwind */ true,
|
||||
/* force_no_backtrace */ false,
|
||||
)
|
||||
});
|
||||
|
||||
struct PanicPayload<A> {
|
||||
@ -693,6 +710,7 @@ fn rust_panic_with_hook(
|
||||
message: Option<&fmt::Arguments<'_>>,
|
||||
location: &Location<'_>,
|
||||
can_unwind: bool,
|
||||
force_no_backtrace: bool,
|
||||
) -> ! {
|
||||
let must_abort = panic_count::increase(true);
|
||||
|
||||
@ -707,14 +725,20 @@ fn rust_panic_with_hook(
|
||||
panic_count::MustAbort::AlwaysAbort => {
|
||||
// Unfortunately, this does not print a backtrace, because creating
|
||||
// a `Backtrace` will allocate, which we must to avoid here.
|
||||
let panicinfo = PanicInfo::internal_constructor(message, location, can_unwind);
|
||||
let panicinfo = PanicInfo::internal_constructor(
|
||||
message,
|
||||
location,
|
||||
can_unwind,
|
||||
force_no_backtrace,
|
||||
);
|
||||
rtprintpanic!("{panicinfo}\npanicked after panic::always_abort(), aborting.\n");
|
||||
}
|
||||
}
|
||||
crate::sys::abort_internal();
|
||||
}
|
||||
|
||||
let mut info = PanicInfo::internal_constructor(message, location, can_unwind);
|
||||
let mut info =
|
||||
PanicInfo::internal_constructor(message, location, can_unwind, force_no_backtrace);
|
||||
let hook = HOOK.read().unwrap_or_else(PoisonError::into_inner);
|
||||
match *hook {
|
||||
// Some platforms (like wasm) know that printing to stderr won't ever actually
|
||||
|
@ -434,6 +434,91 @@ fn test_creation_flags() {
|
||||
assert!(events > 0);
|
||||
}
|
||||
|
||||
/// Tests proc thread attributes by spawning a process with a custom parent process,
|
||||
/// then comparing the parent process ID with the expected parent process ID.
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn test_proc_thread_attributes() {
|
||||
use crate::mem;
|
||||
use crate::os::windows::io::AsRawHandle;
|
||||
use crate::os::windows::process::CommandExt;
|
||||
use crate::sys::c::{CloseHandle, BOOL, HANDLE};
|
||||
use crate::sys::cvt;
|
||||
|
||||
#[repr(C)]
|
||||
#[allow(non_snake_case)]
|
||||
struct PROCESSENTRY32W {
|
||||
dwSize: u32,
|
||||
cntUsage: u32,
|
||||
th32ProcessID: u32,
|
||||
th32DefaultHeapID: usize,
|
||||
th32ModuleID: u32,
|
||||
cntThreads: u32,
|
||||
th32ParentProcessID: u32,
|
||||
pcPriClassBase: i32,
|
||||
dwFlags: u32,
|
||||
szExeFile: [u16; 260],
|
||||
}
|
||||
|
||||
extern "system" {
|
||||
fn CreateToolhelp32Snapshot(dwflags: u32, th32processid: u32) -> HANDLE;
|
||||
fn Process32First(hsnapshot: HANDLE, lppe: *mut PROCESSENTRY32W) -> BOOL;
|
||||
fn Process32Next(hsnapshot: HANDLE, lppe: *mut PROCESSENTRY32W) -> BOOL;
|
||||
}
|
||||
|
||||
const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000;
|
||||
const TH32CS_SNAPPROCESS: u32 = 0x00000002;
|
||||
|
||||
struct ProcessDropGuard(crate::process::Child);
|
||||
|
||||
impl Drop for ProcessDropGuard {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.0.kill();
|
||||
}
|
||||
}
|
||||
|
||||
let parent = ProcessDropGuard(Command::new("cmd").spawn().unwrap());
|
||||
|
||||
let mut child_cmd = Command::new("cmd");
|
||||
|
||||
unsafe {
|
||||
child_cmd
|
||||
.raw_attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, parent.0.as_raw_handle() as isize);
|
||||
}
|
||||
|
||||
let child = ProcessDropGuard(child_cmd.spawn().unwrap());
|
||||
|
||||
let h_snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) };
|
||||
|
||||
let mut process_entry = PROCESSENTRY32W {
|
||||
dwSize: mem::size_of::<PROCESSENTRY32W>() as u32,
|
||||
cntUsage: 0,
|
||||
th32ProcessID: 0,
|
||||
th32DefaultHeapID: 0,
|
||||
th32ModuleID: 0,
|
||||
cntThreads: 0,
|
||||
th32ParentProcessID: 0,
|
||||
pcPriClassBase: 0,
|
||||
dwFlags: 0,
|
||||
szExeFile: [0; 260],
|
||||
};
|
||||
|
||||
unsafe { cvt(Process32First(h_snapshot, &mut process_entry as *mut _)) }.unwrap();
|
||||
|
||||
loop {
|
||||
if child.0.id() == process_entry.th32ProcessID {
|
||||
break;
|
||||
}
|
||||
unsafe { cvt(Process32Next(h_snapshot, &mut process_entry as *mut _)) }.unwrap();
|
||||
}
|
||||
|
||||
unsafe { cvt(CloseHandle(h_snapshot)) }.unwrap();
|
||||
|
||||
assert_eq!(parent.0.id(), process_entry.th32ParentProcessID);
|
||||
|
||||
drop(child)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_command_implements_send_sync() {
|
||||
fn take_send_sync_type<T: Send + Sync>(_: T) {}
|
||||
|
@ -101,7 +101,6 @@ pub extern "C" fn __rust_abort() {
|
||||
// SAFETY: must be called only once during runtime initialization.
|
||||
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
||||
pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) {
|
||||
let _ = net::init();
|
||||
args::init(argc, argv);
|
||||
}
|
||||
|
||||
|
@ -33,13 +33,7 @@ pub fn cvt_gai(err: i32) -> io::Result<()> {
|
||||
))
|
||||
}
|
||||
|
||||
/// Checks whether the HermitCore's socket interface has been started already, and
|
||||
/// if not, starts it.
|
||||
pub fn init() {
|
||||
if unsafe { netc::network_init() } < 0 {
|
||||
panic!("Unable to initialize network interface");
|
||||
}
|
||||
}
|
||||
pub fn init() {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Socket(FileDesc);
|
||||
|
@ -86,7 +86,11 @@ cfg_if::cfg_if! {
|
||||
} else if #[cfg(target_os = "wasi")] {
|
||||
#[inline]
|
||||
unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
|
||||
libc::aligned_alloc(layout.align(), layout.size()) as *mut u8
|
||||
// C11 aligned_alloc requires that the size be a multiple of the alignment.
|
||||
// Layout already checks that the size rounded up doesn't overflow isize::MAX.
|
||||
let align = layout.align();
|
||||
let size = layout.size().next_multiple_of(align);
|
||||
libc::aligned_alloc(align, size) as *mut u8
|
||||
}
|
||||
} else {
|
||||
#[inline]
|
||||
|
@ -2510,6 +2510,7 @@ Windows.Win32.System.Threading.CreateProcessW
|
||||
Windows.Win32.System.Threading.CreateThread
|
||||
Windows.Win32.System.Threading.DEBUG_ONLY_THIS_PROCESS
|
||||
Windows.Win32.System.Threading.DEBUG_PROCESS
|
||||
Windows.Win32.System.Threading.DeleteProcThreadAttributeList
|
||||
Windows.Win32.System.Threading.DETACHED_PROCESS
|
||||
Windows.Win32.System.Threading.ExitProcess
|
||||
Windows.Win32.System.Threading.EXTENDED_STARTUPINFO_PRESENT
|
||||
@ -2524,8 +2525,10 @@ Windows.Win32.System.Threading.INFINITE
|
||||
Windows.Win32.System.Threading.INHERIT_CALLER_PRIORITY
|
||||
Windows.Win32.System.Threading.INHERIT_PARENT_AFFINITY
|
||||
Windows.Win32.System.Threading.INIT_ONCE_INIT_FAILED
|
||||
Windows.Win32.System.Threading.InitializeProcThreadAttributeList
|
||||
Windows.Win32.System.Threading.InitOnceBeginInitialize
|
||||
Windows.Win32.System.Threading.InitOnceComplete
|
||||
Windows.Win32.System.Threading.LPPROC_THREAD_ATTRIBUTE_LIST
|
||||
Windows.Win32.System.Threading.LPTHREAD_START_ROUTINE
|
||||
Windows.Win32.System.Threading.NORMAL_PRIORITY_CLASS
|
||||
Windows.Win32.System.Threading.OpenProcessToken
|
||||
@ -2561,6 +2564,7 @@ Windows.Win32.System.Threading.STARTF_USEPOSITION
|
||||
Windows.Win32.System.Threading.STARTF_USESHOWWINDOW
|
||||
Windows.Win32.System.Threading.STARTF_USESIZE
|
||||
Windows.Win32.System.Threading.STARTF_USESTDHANDLES
|
||||
Windows.Win32.System.Threading.STARTUPINFOEXW
|
||||
Windows.Win32.System.Threading.STARTUPINFOW
|
||||
Windows.Win32.System.Threading.STARTUPINFOW_FLAGS
|
||||
Windows.Win32.System.Threading.SwitchToThread
|
||||
@ -2575,6 +2579,7 @@ Windows.Win32.System.Threading.TlsGetValue
|
||||
Windows.Win32.System.Threading.TlsSetValue
|
||||
Windows.Win32.System.Threading.TryAcquireSRWLockExclusive
|
||||
Windows.Win32.System.Threading.TryAcquireSRWLockShared
|
||||
Windows.Win32.System.Threading.UpdateProcThreadAttribute
|
||||
Windows.Win32.System.Threading.WaitForMultipleObjects
|
||||
Windows.Win32.System.Threading.WaitForSingleObject
|
||||
Windows.Win32.System.Threading.WakeAllConditionVariable
|
||||
|
@ -155,6 +155,10 @@ extern "system" {
|
||||
pub fn DeleteFileW(lpfilename: PCWSTR) -> BOOL;
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn DeleteProcThreadAttributeList(lpattributelist: LPPROC_THREAD_ATTRIBUTE_LIST) -> ();
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn DeviceIoControl(
|
||||
hdevice: HANDLE,
|
||||
@ -371,6 +375,15 @@ extern "system" {
|
||||
) -> BOOL;
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn InitializeProcThreadAttributeList(
|
||||
lpattributelist: LPPROC_THREAD_ATTRIBUTE_LIST,
|
||||
dwattributecount: u32,
|
||||
dwflags: u32,
|
||||
lpsize: *mut usize,
|
||||
) -> BOOL;
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn MoveFileExW(
|
||||
lpexistingfilename: PCWSTR,
|
||||
@ -543,6 +556,18 @@ extern "system" {
|
||||
pub fn TryAcquireSRWLockShared(srwlock: *mut RTL_SRWLOCK) -> BOOLEAN;
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn UpdateProcThreadAttribute(
|
||||
lpattributelist: LPPROC_THREAD_ATTRIBUTE_LIST,
|
||||
dwflags: u32,
|
||||
attribute: usize,
|
||||
lpvalue: *const ::core::ffi::c_void,
|
||||
cbsize: usize,
|
||||
lppreviousvalue: *mut ::core::ffi::c_void,
|
||||
lpreturnsize: *const usize,
|
||||
) -> BOOL;
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn WaitForMultipleObjects(
|
||||
ncount: u32,
|
||||
@ -3567,6 +3592,7 @@ pub type LPOVERLAPPED_COMPLETION_ROUTINE = ::core::option::Option<
|
||||
lpoverlapped: *mut OVERLAPPED,
|
||||
) -> (),
|
||||
>;
|
||||
pub type LPPROC_THREAD_ATTRIBUTE_LIST = *mut ::core::ffi::c_void;
|
||||
pub type LPPROGRESS_ROUTINE = ::core::option::Option<
|
||||
unsafe extern "system" fn(
|
||||
totalfilesize: i64,
|
||||
@ -3833,6 +3859,17 @@ pub const STARTF_USESHOWWINDOW: STARTUPINFOW_FLAGS = 1u32;
|
||||
pub const STARTF_USESIZE: STARTUPINFOW_FLAGS = 2u32;
|
||||
pub const STARTF_USESTDHANDLES: STARTUPINFOW_FLAGS = 256u32;
|
||||
#[repr(C)]
|
||||
pub struct STARTUPINFOEXW {
|
||||
pub StartupInfo: STARTUPINFOW,
|
||||
pub lpAttributeList: LPPROC_THREAD_ATTRIBUTE_LIST,
|
||||
}
|
||||
impl ::core::marker::Copy for STARTUPINFOEXW {}
|
||||
impl ::core::clone::Clone for STARTUPINFOEXW {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
#[repr(C)]
|
||||
pub struct STARTUPINFOW {
|
||||
pub cb: u32,
|
||||
pub lpReserved: PWSTR,
|
||||
|
@ -11,6 +11,7 @@ use crate::ffi::{OsStr, OsString};
|
||||
use crate::fmt;
|
||||
use crate::io::{self, Error, ErrorKind};
|
||||
use crate::mem;
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::num::NonZeroI32;
|
||||
use crate::os::windows::ffi::{OsStrExt, OsStringExt};
|
||||
use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle};
|
||||
@ -166,6 +167,7 @@ pub struct Command {
|
||||
stdout: Option<Stdio>,
|
||||
stderr: Option<Stdio>,
|
||||
force_quotes_enabled: bool,
|
||||
proc_thread_attributes: BTreeMap<usize, ProcThreadAttributeValue>,
|
||||
}
|
||||
|
||||
pub enum Stdio {
|
||||
@ -195,6 +197,7 @@ impl Command {
|
||||
stdout: None,
|
||||
stderr: None,
|
||||
force_quotes_enabled: false,
|
||||
proc_thread_attributes: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,6 +248,17 @@ impl Command {
|
||||
self.cwd.as_ref().map(|cwd| Path::new(cwd))
|
||||
}
|
||||
|
||||
pub unsafe fn raw_attribute<T: Copy + Send + Sync + 'static>(
|
||||
&mut self,
|
||||
attribute: usize,
|
||||
value: T,
|
||||
) {
|
||||
self.proc_thread_attributes.insert(
|
||||
attribute,
|
||||
ProcThreadAttributeValue { size: mem::size_of::<T>(), data: Box::new(value) },
|
||||
);
|
||||
}
|
||||
|
||||
pub fn spawn(
|
||||
&mut self,
|
||||
default: Stdio,
|
||||
@ -308,7 +322,6 @@ impl Command {
|
||||
let stderr = stderr.to_handle(c::STD_ERROR_HANDLE, &mut pipes.stderr)?;
|
||||
|
||||
let mut si = zeroed_startupinfo();
|
||||
si.cb = mem::size_of::<c::STARTUPINFOW>() as c::DWORD;
|
||||
|
||||
// If at least one of stdin, stdout or stderr are set (i.e. are non null)
|
||||
// then set the `hStd` fields in `STARTUPINFO`.
|
||||
@ -322,6 +335,27 @@ impl Command {
|
||||
si.hStdError = stderr.as_raw_handle();
|
||||
}
|
||||
|
||||
let si_ptr: *mut c::STARTUPINFOW;
|
||||
|
||||
let mut proc_thread_attribute_list;
|
||||
let mut si_ex;
|
||||
|
||||
if !self.proc_thread_attributes.is_empty() {
|
||||
si.cb = mem::size_of::<c::STARTUPINFOEXW>() as u32;
|
||||
flags |= c::EXTENDED_STARTUPINFO_PRESENT;
|
||||
|
||||
proc_thread_attribute_list =
|
||||
make_proc_thread_attribute_list(&self.proc_thread_attributes)?;
|
||||
si_ex = c::STARTUPINFOEXW {
|
||||
StartupInfo: si,
|
||||
lpAttributeList: proc_thread_attribute_list.0.as_mut_ptr() as _,
|
||||
};
|
||||
si_ptr = &mut si_ex as *mut _ as _;
|
||||
} else {
|
||||
si.cb = mem::size_of::<c::STARTUPINFOW> as c::DWORD;
|
||||
si_ptr = &mut si as *mut _ as _;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
cvt(c::CreateProcessW(
|
||||
program.as_ptr(),
|
||||
@ -332,7 +366,7 @@ impl Command {
|
||||
flags,
|
||||
envp,
|
||||
dirp,
|
||||
&si,
|
||||
si_ptr,
|
||||
&mut pi,
|
||||
))
|
||||
}?;
|
||||
@ -831,6 +865,80 @@ fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec<u16>)> {
|
||||
}
|
||||
}
|
||||
|
||||
struct ProcThreadAttributeList(Box<[MaybeUninit<u8>]>);
|
||||
|
||||
impl Drop for ProcThreadAttributeList {
|
||||
fn drop(&mut self) {
|
||||
let lp_attribute_list = self.0.as_mut_ptr() as _;
|
||||
unsafe { c::DeleteProcThreadAttributeList(lp_attribute_list) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper around the value data to be used as a Process Thread Attribute.
|
||||
struct ProcThreadAttributeValue {
|
||||
data: Box<dyn Send + Sync>,
|
||||
size: usize,
|
||||
}
|
||||
|
||||
fn make_proc_thread_attribute_list(
|
||||
attributes: &BTreeMap<usize, ProcThreadAttributeValue>,
|
||||
) -> io::Result<ProcThreadAttributeList> {
|
||||
// To initialize our ProcThreadAttributeList, we need to determine
|
||||
// how many bytes to allocate for it. The Windows API simplifies this process
|
||||
// by allowing us to call `InitializeProcThreadAttributeList` with
|
||||
// a null pointer to retrieve the required size.
|
||||
let mut required_size = 0;
|
||||
let Ok(attribute_count) = attributes.len().try_into() else {
|
||||
return Err(io::const_io_error!(
|
||||
ErrorKind::InvalidInput,
|
||||
"maximum number of ProcThreadAttributes exceeded",
|
||||
));
|
||||
};
|
||||
unsafe {
|
||||
c::InitializeProcThreadAttributeList(
|
||||
ptr::null_mut(),
|
||||
attribute_count,
|
||||
0,
|
||||
&mut required_size,
|
||||
)
|
||||
};
|
||||
|
||||
let mut proc_thread_attribute_list = ProcThreadAttributeList(
|
||||
vec![MaybeUninit::uninit(); required_size as usize].into_boxed_slice(),
|
||||
);
|
||||
|
||||
// Once we've allocated the necessary memory, it's safe to invoke
|
||||
// `InitializeProcThreadAttributeList` to properly initialize the list.
|
||||
cvt(unsafe {
|
||||
c::InitializeProcThreadAttributeList(
|
||||
proc_thread_attribute_list.0.as_mut_ptr() as *mut _,
|
||||
attribute_count,
|
||||
0,
|
||||
&mut required_size,
|
||||
)
|
||||
})?;
|
||||
|
||||
// # Add our attributes to the buffer.
|
||||
// It's theoretically possible for the attribute count to exceed a u32 value.
|
||||
// Therefore, we ensure that we don't add more attributes than the buffer was initialized for.
|
||||
for (&attribute, value) in attributes.iter().take(attribute_count as usize) {
|
||||
let value_ptr = &*value.data as *const (dyn Send + Sync) as _;
|
||||
cvt(unsafe {
|
||||
c::UpdateProcThreadAttribute(
|
||||
proc_thread_attribute_list.0.as_mut_ptr() as _,
|
||||
0,
|
||||
attribute,
|
||||
value_ptr,
|
||||
value.size,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
)
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(proc_thread_attribute_list)
|
||||
}
|
||||
|
||||
pub struct CommandArgs<'a> {
|
||||
iter: crate::slice::Iter<'a, Arg>,
|
||||
}
|
||||
|
@ -176,6 +176,14 @@ pub struct Instant(time::Instant);
|
||||
/// The size of a `SystemTime` struct may vary depending on the target operating
|
||||
/// system.
|
||||
///
|
||||
/// A `SystemTime` does not count leap seconds.
|
||||
/// `SystemTime::now()`'s behaviour around a leap second
|
||||
/// is the same as the operating system's wall clock.
|
||||
/// The precise behaviour near a leap second
|
||||
/// (e.g. whether the clock appears to run slow or fast, or stop, or jump)
|
||||
/// depends on platform and configuration,
|
||||
/// so should not be relied on.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```no_run
|
||||
@ -461,6 +469,9 @@ impl fmt::Debug for Instant {
|
||||
impl SystemTime {
|
||||
/// An anchor in time which can be used to create new `SystemTime` instances or
|
||||
/// learn about where in time a `SystemTime` lies.
|
||||
//
|
||||
// NOTE! this documentation is duplicated, here and in std::time::UNIX_EPOCH.
|
||||
// The two copies are not quite identical, because of the difference in naming.
|
||||
///
|
||||
/// This constant is defined to be "1970-01-01 00:00:00 UTC" on all systems with
|
||||
/// respect to the system clock. Using `duration_since` on an existing
|
||||
@ -468,6 +479,11 @@ impl SystemTime {
|
||||
/// measurement lies, and using `UNIX_EPOCH + duration` can be used to create a
|
||||
/// `SystemTime` instance to represent another fixed point in time.
|
||||
///
|
||||
/// `duration_since(UNIX_EPOCH).unwrap().as_secs()` returns
|
||||
/// the number of non-leap seconds since the start of 1970 UTC.
|
||||
/// This is a POSIX `time_t` (as a `u64`),
|
||||
/// and is the same time representation as used in many Internet protocols.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
@ -617,6 +633,9 @@ impl fmt::Debug for SystemTime {
|
||||
|
||||
/// An anchor in time which can be used to create new `SystemTime` instances or
|
||||
/// learn about where in time a `SystemTime` lies.
|
||||
//
|
||||
// NOTE! this documentation is duplicated, here and in SystemTime::UNIX_EPOCH.
|
||||
// The two copies are not quite identical, because of the difference in naming.
|
||||
///
|
||||
/// This constant is defined to be "1970-01-01 00:00:00 UTC" on all systems with
|
||||
/// respect to the system clock. Using `duration_since` on an existing
|
||||
@ -624,6 +643,11 @@ impl fmt::Debug for SystemTime {
|
||||
/// measurement lies, and using `UNIX_EPOCH + duration` can be used to create a
|
||||
/// [`SystemTime`] instance to represent another fixed point in time.
|
||||
///
|
||||
/// `duration_since(UNIX_EPOCH).unwrap().as_secs()` returns
|
||||
/// the number of non-leap seconds since the start of 1970 UTC.
|
||||
/// This is a POSIX `time_t` (as a `u64`),
|
||||
/// and is the same time representation as used in many Internet protocols.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
|
@ -353,10 +353,17 @@ pub struct RustAnalyzer {
|
||||
impl Step for RustAnalyzer {
|
||||
type Output = ();
|
||||
const ONLY_HOSTS: bool = true;
|
||||
const DEFAULT: bool = false;
|
||||
const DEFAULT: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||
run.path("src/tools/rust-analyzer")
|
||||
let builder = run.builder;
|
||||
run.path("src/tools/rust-analyzer").default_condition(
|
||||
builder
|
||||
.config
|
||||
.tools
|
||||
.as_ref()
|
||||
.map_or(true, |tools| tools.iter().any(|tool| tool == "rust-analyzer")),
|
||||
)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig<'_>) {
|
||||
|
@ -35,7 +35,7 @@ fn download_ci_llvm() {
|
||||
));
|
||||
}
|
||||
|
||||
// FIXME(ozkanonur): extend scope of the test
|
||||
// FIXME(onur-ozkan): extend scope of the test
|
||||
// refs:
|
||||
// - https://github.com/rust-lang/rust/issues/109120
|
||||
// - https://github.com/rust-lang/rust/pull/109162#issuecomment-1496782487
|
||||
|
@ -64,9 +64,8 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then
|
||||
|
||||
docker --version >> $hash_key
|
||||
|
||||
# Include cache version. Currently it is needed to bust Docker
|
||||
# cache key after opting in into the old Docker build backend.
|
||||
echo "1" >> $hash_key
|
||||
# Include cache version. Can be used to manually bust the Docker cache.
|
||||
echo "2" >> $hash_key
|
||||
|
||||
cksum=$(sha512sum $hash_key | \
|
||||
awk '{print $1}')
|
||||
@ -78,6 +77,10 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then
|
||||
set +e
|
||||
retry curl --max-time 600 -y 30 -Y 10 --connect-timeout 30 -f -L -C - \
|
||||
-o /tmp/rustci_docker_cache "$url"
|
||||
|
||||
docker_archive_hash=$(sha512sum /tmp/rustci_docker_cache | awk '{print $1}')
|
||||
echo "Downloaded archive hash: ${docker_archive_hash}"
|
||||
|
||||
echo "Loading images into docker"
|
||||
# docker load sometimes hangs in the CI, so time out after 10 minutes with TERM,
|
||||
# KILL after 12 minutes
|
||||
@ -115,8 +118,10 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then
|
||||
digest=$(docker inspect rust-ci --format '{{.Id}}')
|
||||
echo "Built container $digest"
|
||||
if ! grep -q "$digest" <(echo "$loaded_images"); then
|
||||
echo "Uploading finished image to $url"
|
||||
echo "Uploading finished image $digest to $url"
|
||||
set +e
|
||||
# Print image history for easier debugging of layer SHAs
|
||||
docker history rust-ci
|
||||
docker history -q rust-ci | \
|
||||
grep -v missing | \
|
||||
xargs docker save | \
|
||||
@ -131,6 +136,7 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then
|
||||
mkdir -p "$dist"
|
||||
echo "$url" >"$info"
|
||||
echo "$digest" >>"$info"
|
||||
cat "$info"
|
||||
fi
|
||||
elif [ -f "$docker_dir/disabled/$image/Dockerfile" ]; then
|
||||
if isCI; then
|
||||
|
@ -313,7 +313,7 @@ target | std | host | notes
|
||||
[`riscv64-linux-android`](platform-support/android.md) | | | RISC-V 64-bit Android
|
||||
`s390x-unknown-linux-musl` | | | S390x Linux (kernel 3.2, MUSL)
|
||||
`sparc-unknown-linux-gnu` | ✓ | | 32-bit SPARC Linux
|
||||
[`sparc-unknown-none-elf`](./platform-support/sparc-unknown-none-elf.md) | * | Bare 32-bit SPARC V7+
|
||||
[`sparc-unknown-none-elf`](./platform-support/sparc-unknown-none-elf.md) | * | | Bare 32-bit SPARC V7+
|
||||
[`sparc64-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/sparc64
|
||||
[`sparc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/sparc64
|
||||
`thumbv4t-none-eabi` | * | | Thumb-mode Bare ARMv4T
|
||||
|
@ -400,7 +400,12 @@ constructs. For example, a macro use `foo!(a, b, c)` can be parsed like a
|
||||
function call (ignoring the `!`), so format it using the rules for function
|
||||
calls.
|
||||
|
||||
### Special case macros
|
||||
The style guide defines specific formatting for particular macros in the
|
||||
language or standard library. The style guide does not define formatting for
|
||||
any third-party macros, even if similar to those in the language or standard
|
||||
library.
|
||||
|
||||
### Format string macros
|
||||
|
||||
For macros which take a format string, if all other arguments are *small*,
|
||||
format the arguments before the format string on a single line if they fit, and
|
||||
|
@ -55,9 +55,9 @@ RUST_GDBGUI="${RUST_GDBGUI:-gdbgui}"
|
||||
|
||||
# These arguments get passed through to GDB and make it load the
|
||||
# Rust pretty printers.
|
||||
GDB_ARGS="--directory=\"$GDB_PYTHON_MODULE_DIRECTORY\"" \
|
||||
"-iex \"add-auto-load-safe-path $GDB_PYTHON_MODULE_DIRECTORY\"" \
|
||||
"-iex \"set substitute-path /rustc/$RUSTC_COMMIT_HASH $RUSTC_SYSROOT/lib/rustlib/src/rust\""
|
||||
GDB_ARGS="--directory=\"$GDB_PYTHON_MODULE_DIRECTORY\" \
|
||||
-iex \"add-auto-load-safe-path $GDB_PYTHON_MODULE_DIRECTORY\" \
|
||||
-iex \"set substitute-path /rustc/$RUSTC_COMMIT_HASH $RUSTC_SYSROOT/lib/rustlib/src/rust\""
|
||||
|
||||
# Finally we execute gdbgui.
|
||||
PYTHONPATH="$PYTHONPATH:$GDB_PYTHON_MODULE_DIRECTORY" \
|
||||
|
@ -31,7 +31,7 @@ Original by Dempfi (https://github.com/dempfi/ayu)
|
||||
--codeblock-error-color: rgba(255, 0, 0, .5);
|
||||
--codeblock-ignore-hover-color: rgb(255, 142, 0);
|
||||
--codeblock-ignore-color: rgba(255, 142, 0, .6);
|
||||
--warning-border-color: rgb(255, 142, 0);
|
||||
--warning-border-color: #ff8e00;
|
||||
--type-link-color: #ffa0a5;
|
||||
--trait-link-color: #39afd7;
|
||||
--assoc-item-link-color: #39afd7;
|
||||
@ -96,8 +96,8 @@ Original by Dempfi (https://github.com/dempfi/ayu)
|
||||
--codeblock-link-background: #333;
|
||||
--scrape-example-toggle-line-background: #999;
|
||||
--scrape-example-toggle-line-hover-background: #c5c5c5;
|
||||
--scrape-example-code-line-highlight: rgb(91, 59, 1);
|
||||
--scrape-example-code-line-highlight-focus: rgb(124, 75, 15);
|
||||
--scrape-example-code-line-highlight: #5b3b01;
|
||||
--scrape-example-code-line-highlight-focus: #7c4b0f;
|
||||
--scrape-example-help-border-color: #aaa;
|
||||
--scrape-example-help-color: #eee;
|
||||
--scrape-example-help-hover-border-color: #fff;
|
||||
|
@ -26,7 +26,7 @@
|
||||
--codeblock-error-color: rgba(255, 0, 0, .5);
|
||||
--codeblock-ignore-hover-color: rgb(255, 142, 0);
|
||||
--codeblock-ignore-color: rgba(255, 142, 0, .6);
|
||||
--warning-border-color: rgb(255, 142, 0);
|
||||
--warning-border-color: #ff8e00;
|
||||
--type-link-color: #2dbfb8;
|
||||
--trait-link-color: #b78cf2;
|
||||
--assoc-item-link-color: #d2991d;
|
||||
@ -69,7 +69,7 @@
|
||||
--test-arrow-color: #dedede;
|
||||
--test-arrow-background-color: rgba(78, 139, 202, 0.2);
|
||||
--test-arrow-hover-color: #dedede;
|
||||
--test-arrow-hover-background-color: rgb(78, 139, 202);
|
||||
--test-arrow-hover-background-color: #4e8bca;
|
||||
--target-background-color: #494a3d;
|
||||
--target-border-color: #bb7410;
|
||||
--kbd-color: #000;
|
||||
@ -87,12 +87,12 @@
|
||||
--crate-search-hover-border: #2196f3;
|
||||
--src-sidebar-background-selected: #333;
|
||||
--src-sidebar-background-hover: #444;
|
||||
--table-alt-row-background-color: #2A2A2A;
|
||||
--table-alt-row-background-color: #2a2a2a;
|
||||
--codeblock-link-background: #333;
|
||||
--scrape-example-toggle-line-background: #999;
|
||||
--scrape-example-toggle-line-hover-background: #c5c5c5;
|
||||
--scrape-example-code-line-highlight: rgb(91, 59, 1);
|
||||
--scrape-example-code-line-highlight-focus: rgb(124, 75, 15);
|
||||
--scrape-example-code-line-highlight: #5b3b01;
|
||||
--scrape-example-code-line-highlight-focus: #7c4b0f;
|
||||
--scrape-example-help-border-color: #aaa;
|
||||
--scrape-example-help-color: #eee;
|
||||
--scrape-example-help-hover-border-color: #fff;
|
||||
|
@ -5,9 +5,9 @@
|
||||
--settings-input-border-color: #717171;
|
||||
--settings-button-color: #000;
|
||||
--settings-button-border-focus: #717171;
|
||||
--sidebar-background-color: #F5F5F5;
|
||||
--sidebar-background-color-hover: #E0E0E0;
|
||||
--code-block-background-color: #F5F5F5;
|
||||
--sidebar-background-color: #f5f5f5;
|
||||
--sidebar-background-color-hover: #e0e0e0;
|
||||
--code-block-background-color: #f5f5f5;
|
||||
--scrollbar-track-background-color: #dcdcdc;
|
||||
--scrollbar-thumb-background-color: rgba(36, 37, 39, 0.6);
|
||||
--scrollbar-color: rgba(36, 37, 39, 0.6) #d9d9d9;
|
||||
@ -26,7 +26,7 @@
|
||||
--codeblock-error-color: rgba(255, 0, 0, .5);
|
||||
--codeblock-ignore-hover-color: rgb(255, 142, 0);
|
||||
--codeblock-ignore-color: rgba(255, 142, 0, .6);
|
||||
--warning-border-color: rgb(255, 142, 0);
|
||||
--warning-border-color: #ff8e00;
|
||||
--type-link-color: #ad378a;
|
||||
--trait-link-color: #6e4fc9;
|
||||
--assoc-item-link-color: #3873ad;
|
||||
@ -47,7 +47,7 @@
|
||||
--search-tab-button-not-selected-border-top-color: #e6e6e6;
|
||||
--search-tab-button-not-selected-background: #e6e6e6;
|
||||
--search-tab-button-selected-border-top-color: #0089ff;
|
||||
--search-tab-button-selected-background: #ffffff;
|
||||
--search-tab-button-selected-background: #fff;
|
||||
--stab-background-color: #fff5d6;
|
||||
--stab-code-color: #000;
|
||||
--code-highlight-kw-color: #8959a8;
|
||||
@ -84,7 +84,7 @@
|
||||
--crate-search-hover-border: #717171;
|
||||
--src-sidebar-background-selected: #fff;
|
||||
--src-sidebar-background-hover: #e0e0e0;
|
||||
--table-alt-row-background-color: #F5F5F5;
|
||||
--table-alt-row-background-color: #f5f5f5;
|
||||
--codeblock-link-background: #eee;
|
||||
--scrape-example-toggle-line-background: #ccc;
|
||||
--scrape-example-toggle-line-hover-background: #999;
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 2cc50bc0b63ad20da193e002ba11d391af0104b7
|
||||
Subproject commit 925280f028db3a322935e040719a0754703947cf
|
@ -1 +1 @@
|
||||
9334ec93541fd6963a3bfa2d2d09e3e33ac93131
|
||||
f3284dc3ad9254236d296daa1285dd273b492b01
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user