mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-08 09:38:26 +03:00
Merge from rustc
This commit is contained in:
@@ -429,6 +429,8 @@ Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> nils <48135649+Nilstrieb
|
||||
Nif Ward <nif.ward@gmail.com>
|
||||
Nika Layzell <nika@thelayzells.com> <michael@thelayzells.com>
|
||||
NODA Kai <nodakai@gmail.com>
|
||||
Oğuz Ağcayazı <oguz.agcayazi@gmail.com> <oguz.agcayazi@gmail.com>
|
||||
Oğuz Ağcayazı <oguz.agcayazi@gmail.com> <ouz.agz@gmail.com>
|
||||
oliver <16816606+o752d@users.noreply.github.com>
|
||||
Oliver Middleton <olliemail27@gmail.com> <ollie27@users.noreply.github.com>
|
||||
Oliver Scherer <oli-obk@users.noreply.github.com> <git-spam-no-reply9815368754983@oli-obk.de>
|
||||
|
||||
+7
-11
@@ -6030,19 +6030,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-bindgen"
|
||||
version = "0.49.0"
|
||||
version = "0.51.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6935fb09b84ee57929ae92518b475f5dfdfbeb87c5334756acc28ee8e202b60"
|
||||
checksum = "bc1f16b778125675feee0d15d6dd9f6af0e3ac52b3233d63a10aa39230c1cd75"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"rayon",
|
||||
"syn 2.0.29",
|
||||
"windows-metadata",
|
||||
"windows-tokens",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-metadata"
|
||||
version = "0.49.0"
|
||||
version = "0.51.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f5bca94a32bf1e6a376522b6601275a3b611ee885ec0f1b6a05f17e8cfd3385"
|
||||
checksum = "753135d996f9da437c0b31dbde3032489a61708361929bcc07d4fba0b161000e"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
@@ -6092,12 +6094,6 @@ dependencies = [
|
||||
"windows_x86_64_msvc 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-tokens"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b34c9a3b28cb41db7385546f7f9a8179348dffc89923dde66857b1ba5312f6b4"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.2"
|
||||
|
||||
@@ -245,7 +245,7 @@ fn suggest_static_lifetime_for_gat_from_hrtb(
|
||||
let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }, _) = bound else { return; };
|
||||
diag.span_note(
|
||||
*trait_span,
|
||||
format!("due to current limitations in the borrow checker, this implies a `'static` lifetime")
|
||||
"due to current limitations in the borrow checker, this implies a `'static` lifetime"
|
||||
);
|
||||
let Some(generics_fn) = hir.get_generics(self.body.source.def_id().expect_local()) else { return; };
|
||||
let Def(_, trait_res_defid) = trait_ref.path.res else { return; };
|
||||
@@ -277,7 +277,7 @@ fn suggest_static_lifetime_for_gat_from_hrtb(
|
||||
if suggestions.len() > 0 {
|
||||
suggestions.dedup();
|
||||
diag.multipart_suggestion_verbose(
|
||||
format!("consider restricting the type parameter to the `'static` lifetime"),
|
||||
"consider restricting the type parameter to the `'static` lifetime",
|
||||
suggestions,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
|
||||
@@ -875,7 +875,7 @@ pub(crate) fn codegen_place<'tcx>(
|
||||
PlaceElem::Deref => {
|
||||
cplace = cplace.place_deref(fx);
|
||||
}
|
||||
PlaceElem::OpaqueCast(ty) => cplace = cplace.place_opaque_cast(fx, ty),
|
||||
PlaceElem::OpaqueCast(ty) => bug!("encountered OpaqueCast({ty}) in codegen"),
|
||||
PlaceElem::Field(field, _ty) => {
|
||||
cplace = cplace.place_field(fx, field);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ fn report_simd_type_validation_error(
|
||||
pub(super) fn codegen_simd_intrinsic_call<'tcx>(
|
||||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||
intrinsic: Symbol,
|
||||
_args: GenericArgsRef<'tcx>,
|
||||
generic_args: GenericArgsRef<'tcx>,
|
||||
args: &[mir::Operand<'tcx>],
|
||||
ret: CPlace<'tcx>,
|
||||
target: BasicBlock,
|
||||
@@ -117,6 +117,54 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
|
||||
});
|
||||
}
|
||||
|
||||
// simd_shuffle_generic<T, U, const I: &[u32]>(x: T, y: T) -> U
|
||||
sym::simd_shuffle_generic => {
|
||||
let [x, y] = args else {
|
||||
bug!("wrong number of args for intrinsic {intrinsic}");
|
||||
};
|
||||
let x = codegen_operand(fx, x);
|
||||
let y = codegen_operand(fx, y);
|
||||
|
||||
if !x.layout().ty.is_simd() {
|
||||
report_simd_type_validation_error(fx, intrinsic, span, x.layout().ty);
|
||||
return;
|
||||
}
|
||||
|
||||
let idx = generic_args[2]
|
||||
.expect_const()
|
||||
.eval(fx.tcx, ty::ParamEnv::reveal_all(), Some(span))
|
||||
.unwrap()
|
||||
.unwrap_branch();
|
||||
|
||||
assert_eq!(x.layout(), y.layout());
|
||||
let layout = x.layout();
|
||||
|
||||
let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
|
||||
let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);
|
||||
|
||||
assert_eq!(lane_ty, ret_lane_ty);
|
||||
assert_eq!(idx.len() as u64, ret_lane_count);
|
||||
|
||||
let total_len = lane_count * 2;
|
||||
|
||||
let indexes =
|
||||
idx.iter().map(|idx| idx.unwrap_leaf().try_to_u16().unwrap()).collect::<Vec<u16>>();
|
||||
|
||||
for &idx in &indexes {
|
||||
assert!(u64::from(idx) < total_len, "idx {} out of range 0..{}", idx, total_len);
|
||||
}
|
||||
|
||||
for (out_idx, in_idx) in indexes.into_iter().enumerate() {
|
||||
let in_lane = if u64::from(in_idx) < lane_count {
|
||||
x.value_lane(fx, in_idx.into())
|
||||
} else {
|
||||
y.value_lane(fx, u64::from(in_idx) - lane_count)
|
||||
};
|
||||
let out_lane = ret.place_lane(fx, u64::try_from(out_idx).unwrap());
|
||||
out_lane.write_cvalue(fx, in_lane);
|
||||
}
|
||||
}
|
||||
|
||||
// simd_shuffle<T, I, U>(x: T, y: T, idx: I) -> U
|
||||
sym::simd_shuffle => {
|
||||
let (x, y, idx) = match args {
|
||||
|
||||
@@ -674,14 +674,6 @@ fn transmute_scalar<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn place_opaque_cast(
|
||||
self,
|
||||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> CPlace<'tcx> {
|
||||
CPlace { inner: self.inner, layout: fx.layout_of(ty) }
|
||||
}
|
||||
|
||||
pub(crate) fn place_field(
|
||||
self,
|
||||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||
|
||||
@@ -37,6 +37,8 @@ codegen_llvm_lto_disallowed = lto can only be run for executables, cdylibs and s
|
||||
|
||||
codegen_llvm_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto`
|
||||
|
||||
codegen_llvm_lto_proc_macro = lto cannot be used for `proc-macro` crate type without `-Zdylib-lto`
|
||||
|
||||
codegen_llvm_missing_features =
|
||||
add the missing features in a `target_feature` attribute
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
self, bitcode_section_name, save_temp_bitcode, CodegenDiagnosticsStage, DiagnosticHandlers,
|
||||
};
|
||||
use crate::errors::{
|
||||
DynamicLinkingWithLTO, LlvmError, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib,
|
||||
DynamicLinkingWithLTO, LlvmError, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib, LtoProcMacro,
|
||||
};
|
||||
use crate::llvm::{self, build_string};
|
||||
use crate::{LlvmCodegenBackend, ModuleLlvm};
|
||||
@@ -36,8 +36,12 @@
|
||||
|
||||
pub fn crate_type_allows_lto(crate_type: CrateType) -> bool {
|
||||
match crate_type {
|
||||
CrateType::Executable | CrateType::Dylib | CrateType::Staticlib | CrateType::Cdylib => true,
|
||||
CrateType::Rlib | CrateType::ProcMacro => false,
|
||||
CrateType::Executable
|
||||
| CrateType::Dylib
|
||||
| CrateType::Staticlib
|
||||
| CrateType::Cdylib
|
||||
| CrateType::ProcMacro => true,
|
||||
CrateType::Rlib => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +91,11 @@ fn prepare_lto(
|
||||
diag_handler.emit_err(LtoDylib);
|
||||
return Err(FatalError);
|
||||
}
|
||||
} else if *crate_type == CrateType::ProcMacro {
|
||||
if !cgcx.opts.unstable_opts.dylib_lto {
|
||||
diag_handler.emit_err(LtoProcMacro);
|
||||
return Err(FatalError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -138,6 +138,10 @@ fn into_diagnostic(self, sess: &'_ Handler) -> DiagnosticBuilder<'_, ErrorGuaran
|
||||
#[diag(codegen_llvm_lto_dylib)]
|
||||
pub(crate) struct LtoDylib;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_llvm_lto_proc_macro)]
|
||||
pub(crate) struct LtoProcMacro;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_llvm_lto_bitcode_from_rlib)]
|
||||
pub(crate) struct LtoBitcodeFromRlib {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, LayoutOf};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::{self, GenericArgsRef, Ty};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::{sym, symbol::kw, Span, Symbol};
|
||||
use rustc_target::abi::{self, Align, HasDataLayout, Primitive};
|
||||
@@ -376,7 +376,9 @@ fn codegen_intrinsic_call(
|
||||
}
|
||||
|
||||
_ if name.as_str().starts_with("simd_") => {
|
||||
match generic_simd_intrinsic(self, name, callee_ty, args, ret_ty, llret_ty, span) {
|
||||
match generic_simd_intrinsic(
|
||||
self, name, callee_ty, fn_args, args, ret_ty, llret_ty, span,
|
||||
) {
|
||||
Ok(llval) => llval,
|
||||
Err(()) => return,
|
||||
}
|
||||
@@ -911,6 +913,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
|
||||
bx: &mut Builder<'_, 'll, 'tcx>,
|
||||
name: Symbol,
|
||||
callee_ty: Ty<'tcx>,
|
||||
fn_args: GenericArgsRef<'tcx>,
|
||||
args: &[OperandRef<'tcx, &'ll Value>],
|
||||
ret_ty: Ty<'tcx>,
|
||||
llret_ty: &'ll Type,
|
||||
@@ -1030,6 +1033,56 @@ macro_rules! require_simd {
|
||||
));
|
||||
}
|
||||
|
||||
if name == sym::simd_shuffle_generic {
|
||||
let idx = fn_args[2]
|
||||
.expect_const()
|
||||
.eval(tcx, ty::ParamEnv::reveal_all(), Some(span))
|
||||
.unwrap()
|
||||
.unwrap_branch();
|
||||
let n = idx.len() as u64;
|
||||
|
||||
require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty });
|
||||
let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx());
|
||||
require!(
|
||||
out_len == n,
|
||||
InvalidMonomorphization::ReturnLength { span, name, in_len: n, ret_ty, out_len }
|
||||
);
|
||||
require!(
|
||||
in_elem == out_ty,
|
||||
InvalidMonomorphization::ReturnElement { span, name, in_elem, in_ty, ret_ty, out_ty }
|
||||
);
|
||||
|
||||
let total_len = in_len * 2;
|
||||
|
||||
let indices: Option<Vec<_>> = idx
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(arg_idx, val)| {
|
||||
let idx = val.unwrap_leaf().try_to_i32().unwrap();
|
||||
if idx >= i32::try_from(total_len).unwrap() {
|
||||
bx.sess().emit_err(InvalidMonomorphization::ShuffleIndexOutOfBounds {
|
||||
span,
|
||||
name,
|
||||
arg_idx: arg_idx as u64,
|
||||
total_len: total_len.into(),
|
||||
});
|
||||
None
|
||||
} else {
|
||||
Some(bx.const_i32(idx))
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let Some(indices) = indices else {
|
||||
return Ok(bx.const_null(llret_ty));
|
||||
};
|
||||
|
||||
return Ok(bx.shuffle_vector(
|
||||
args[0].immediate(),
|
||||
args[1].immediate(),
|
||||
bx.const_vector(&indices),
|
||||
));
|
||||
}
|
||||
|
||||
if name == sym::simd_shuffle {
|
||||
// Make sure this is actually an array, since typeck only checks the length-suffixed
|
||||
// version of this intrinsic.
|
||||
|
||||
@@ -46,8 +46,6 @@ codegen_ssa_ignoring_output = ignoring -o because multiple .{$extension} files w
|
||||
codegen_ssa_illegal_link_ordinal_format = illegal ordinal format in `link_ordinal`
|
||||
.note = an unsuffixed integer value, e.g., `1`, is expected
|
||||
|
||||
codegen_ssa_incompatible_linking_modifiers = link modifiers combination `+bundle,+whole-archive` is unstable when generating rlibs
|
||||
|
||||
codegen_ssa_insufficient_vs_code_product = VS Code is a different product, and is not sufficient.
|
||||
|
||||
codegen_ssa_invalid_link_ordinal_nargs = incorrect number of arguments to `#[link_ordinal]`
|
||||
|
||||
@@ -365,15 +365,9 @@ fn link_rlib<'a>(
|
||||
// loaded from the libraries found here and then encode that into the
|
||||
// metadata of the rlib we're generating somehow.
|
||||
for lib in codegen_results.crate_info.used_libraries.iter() {
|
||||
let NativeLibKind::Static { bundle: None | Some(true), whole_archive } = lib.kind else {
|
||||
let NativeLibKind::Static { bundle: None | Some(true), .. } = lib.kind else {
|
||||
continue;
|
||||
};
|
||||
if whole_archive == Some(true)
|
||||
&& flavor == RlibFlavor::Normal
|
||||
&& !codegen_results.crate_info.feature_packed_bundled_libs
|
||||
{
|
||||
sess.emit_err(errors::IncompatibleLinkingModifiers);
|
||||
}
|
||||
if flavor == RlibFlavor::Normal && let Some(filename) = lib.filename {
|
||||
let path = find_native_static_library(filename.as_str(), true, &lib_search_paths, sess);
|
||||
let src = read(path).map_err(|e| sess.emit_fatal(errors::ReadFileError {message: e }))?;
|
||||
|
||||
@@ -181,7 +181,7 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
old_info
|
||||
}
|
||||
}
|
||||
(_, &ty::Dynamic(ref data, _, _)) => meth::get_vtable(cx, source, data.principal()),
|
||||
(_, ty::Dynamic(data, _, _)) => meth::get_vtable(cx, source, data.principal()),
|
||||
_ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target),
|
||||
}
|
||||
}
|
||||
@@ -857,7 +857,6 @@ pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo {
|
||||
dependency_formats: tcx.dependency_formats(()).clone(),
|
||||
windows_subsystem,
|
||||
natvis_debugger_visualizers: Default::default(),
|
||||
feature_packed_bundled_libs: tcx.features().packed_bundled_libs,
|
||||
};
|
||||
let crates = tcx.crates(());
|
||||
|
||||
|
||||
@@ -107,10 +107,6 @@ pub struct CreateTempDir {
|
||||
pub error: Error,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_incompatible_linking_modifiers)]
|
||||
pub struct IncompatibleLinkingModifiers;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_add_native_library)]
|
||||
pub struct AddNativeLibrary {
|
||||
|
||||
@@ -164,7 +164,6 @@ pub struct CrateInfo {
|
||||
pub dependency_formats: Lrc<Dependencies>,
|
||||
pub windows_subsystem: Option<String>,
|
||||
pub natvis_debugger_visualizers: BTreeSet<DebuggerVisualizerFile>,
|
||||
pub feature_packed_bundled_libs: bool, // unstable feature flag.
|
||||
}
|
||||
|
||||
#[derive(Encodable, Decodable)]
|
||||
|
||||
@@ -463,7 +463,9 @@ pub fn codegen_place(
|
||||
mir::ProjectionElem::Field(ref field, _) => {
|
||||
cg_base.project_field(bx, field.index())
|
||||
}
|
||||
mir::ProjectionElem::OpaqueCast(ty) => cg_base.project_type(bx, ty),
|
||||
mir::ProjectionElem::OpaqueCast(ty) => {
|
||||
bug!("encountered OpaqueCast({ty}) in codegen")
|
||||
}
|
||||
mir::ProjectionElem::Index(index) => {
|
||||
let index = &mir::Operand::Copy(mir::Place::from(index));
|
||||
let index = self.codegen_operand(bx, index);
|
||||
|
||||
@@ -316,7 +316,9 @@ pub fn project<P>(&self, base: &P, proj_elem: mir::PlaceElem<'tcx>) -> InterpRes
|
||||
{
|
||||
use rustc_middle::mir::ProjectionElem::*;
|
||||
Ok(match proj_elem {
|
||||
OpaqueCast(ty) => base.transmute(self.layout_of(ty)?, self)?,
|
||||
OpaqueCast(ty) => {
|
||||
span_bug!(self.cur_span(), "OpaqueCast({ty}) encountered after borrowck")
|
||||
}
|
||||
Field(field, _) => self.project_field(base, field.index())?,
|
||||
Downcast(_, variant) => self.project_downcast(base, variant)?,
|
||||
Deref => self.deref_pointer(&base.to_op(self)?)?.into(),
|
||||
|
||||
@@ -633,6 +633,14 @@ fn visit_projection_elem(
|
||||
location: Location,
|
||||
) {
|
||||
match elem {
|
||||
ProjectionElem::OpaqueCast(ty)
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) =>
|
||||
{
|
||||
self.fail(
|
||||
location,
|
||||
format!("explicit opaque type cast to `{ty}` after `RevealAll`"),
|
||||
)
|
||||
}
|
||||
ProjectionElem::Index(index) => {
|
||||
let index_ty = self.body.local_decls[index].ty;
|
||||
if index_ty != self.tcx.types.usize {
|
||||
|
||||
@@ -5,7 +5,7 @@ or `Self` that references lifetimes from a parent scope.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,edition2018
|
||||
```ignore,edition2018
|
||||
struct S<'a>(&'a i32);
|
||||
|
||||
impl<'a> S<'a> {
|
||||
|
||||
@@ -197,6 +197,8 @@ macro_rules! declare_features {
|
||||
/// + `impl<I:Iterator> Iterator for &mut Iterator`
|
||||
/// + `impl Debug for Foo<'_>`
|
||||
(accepted, impl_header_lifetime_elision, "1.31.0", Some(15872), None),
|
||||
/// Allows referencing `Self` and projections in impl-trait.
|
||||
(accepted, impl_trait_projections, "CURRENT_RUSTC_VERSION", Some(103532), None),
|
||||
/// Allows using `a..=b` and `..=b` as inclusive range syntaxes.
|
||||
(accepted, inclusive_range_syntax, "1.26.0", Some(28237), None),
|
||||
/// Allows inferring outlives requirements (RFC 2093).
|
||||
@@ -267,6 +269,8 @@ macro_rules! declare_features {
|
||||
(accepted, non_modrs_mods, "1.30.0", Some(44660), None),
|
||||
/// Allows the use of or-patterns (e.g., `0 | 1`).
|
||||
(accepted, or_patterns, "1.53.0", Some(54883), None),
|
||||
/// Allows using `+bundle,+whole-archive` link modifiers with native libs.
|
||||
(accepted, packed_bundled_libs, "CURRENT_RUSTC_VERSION", Some(108081), None),
|
||||
/// Allows annotating functions conforming to `fn(&PanicInfo) -> !` with `#[panic_handler]`.
|
||||
/// This defines the behavior of panics.
|
||||
(accepted, panic_handler, "1.30.0", Some(44489), None),
|
||||
|
||||
@@ -241,8 +241,6 @@ pub fn set(&self, features: &mut Features, span: Span) {
|
||||
(active, linkage, "1.0.0", Some(29603), None),
|
||||
/// Allows declaring with `#![needs_panic_runtime]` that a panic runtime is needed.
|
||||
(internal, needs_panic_runtime, "1.10.0", Some(32837), None),
|
||||
/// Allows using `+bundled,+whole-archive` native libs.
|
||||
(active, packed_bundled_libs, "1.69.0", Some(108081), None),
|
||||
/// Allows using the `#![panic_runtime]` attribute.
|
||||
(internal, panic_runtime, "1.10.0", Some(32837), None),
|
||||
/// Allows `extern "platform-intrinsic" { ... }`.
|
||||
@@ -472,8 +470,6 @@ pub fn set(&self, features: &mut Features, span: Span) {
|
||||
(active, impl_trait_in_assoc_type, "1.70.0", Some(63063), None),
|
||||
/// Allows `impl Trait` as output type in `Fn` traits in return position of functions.
|
||||
(active, impl_trait_in_fn_trait_return, "1.64.0", Some(99697), None),
|
||||
/// Allows referencing `Self` and projections in impl-trait.
|
||||
(active, impl_trait_projections, "1.67.0", Some(103532), None),
|
||||
/// Allows using imported `main` function
|
||||
(active, imported_main, "1.53.0", Some(28937), None),
|
||||
/// Allows associated types in inherent impls.
|
||||
|
||||
@@ -38,6 +38,17 @@ hir_analysis_cast_thin_pointer_to_fat_pointer = cannot cast thin pointer `{$expr
|
||||
hir_analysis_closure_implicit_hrtb = implicit types in closure signatures are forbidden when `for<...>` is present
|
||||
.label = `for<...>` is here
|
||||
|
||||
hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures
|
||||
|
||||
hir_analysis_coerce_unsized_multi = implementing the trait `CoerceUnsized` requires multiple coercions
|
||||
.note = `CoerceUnsized` may only be implemented for a coercion between structures with one field being coerced
|
||||
.coercions_note = currently, {$number} fields need coercions: {$coercions}
|
||||
.label = requires multiple coercions
|
||||
|
||||
hir_analysis_coercion_between_struct_same_note = expected coercion between the same definition; expected `{$source_path}`, found `{$target_path}`
|
||||
|
||||
hir_analysis_coercion_between_struct_single_note = expected a single field to be coerced, none found
|
||||
|
||||
hir_analysis_const_bound_for_non_const_trait =
|
||||
~const can only be applied to `#[const_trait]` traits
|
||||
|
||||
@@ -61,6 +72,15 @@ hir_analysis_copy_impl_on_type_with_dtor =
|
||||
the trait `Copy` cannot be implemented for this type; the type has a destructor
|
||||
.label = `Copy` not allowed on types with destructors
|
||||
|
||||
hir_analysis_dispatch_from_dyn_multi = implementing the `DispatchFromDyn` trait requires multiple coercions
|
||||
.note = the trait `DispatchFromDyn` may only be implemented for a coercion between structures with a single field being coerced
|
||||
.coercions_note = currently, {$number} fields need coercions: {$coercions}
|
||||
|
||||
hir_analysis_dispatch_from_dyn_repr = structs implementing `DispatchFromDyn` may not have `#[repr(packed)]` or `#[repr(C)]`
|
||||
|
||||
hir_analysis_dispatch_from_dyn_zst = the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment, and nothing else
|
||||
.note = extra field `{$name}` of type `{$ty}` is not allowed
|
||||
|
||||
hir_analysis_drop_impl_negative = negative `Drop` impls are not supported
|
||||
|
||||
hir_analysis_drop_impl_on_wrong_item =
|
||||
@@ -232,6 +252,8 @@ hir_analysis_pass_to_variadic_function = can't pass `{$ty}` to variadic function
|
||||
hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind}
|
||||
.label = not allowed in type signatures
|
||||
|
||||
hir_analysis_requires_note = the `{$trait_name}` impl for `{$ty}` requires that `{$error_predicate}`
|
||||
|
||||
hir_analysis_return_type_notation_conflicting_bound =
|
||||
ambiguous associated function `{$assoc_name}` for `{$ty_name}`
|
||||
.note = `{$assoc_name}` is declared in two supertraits: `{$first_bound}` and `{$second_bound}`
|
||||
@@ -299,6 +321,9 @@ hir_analysis_too_large_static = extern static is too large for the current archi
|
||||
hir_analysis_track_caller_on_main = `main` function is not allowed to be `#[track_caller]`
|
||||
.suggestion = remove this annotation
|
||||
|
||||
hir_analysis_trait_cannot_impl_for_ty = the trait `{$trait_name}` cannot be implemented for this type
|
||||
.label = this field does not implement `{$trait_name}`
|
||||
|
||||
hir_analysis_trait_object_declared_with_no_traits =
|
||||
at least one trait is required for an object type
|
||||
.alias_span = this alias does not contain a trait
|
||||
|
||||
@@ -5,18 +5,15 @@
|
||||
use super::compare_impl_item::{compare_impl_method, compare_impl_ty};
|
||||
use super::*;
|
||||
use rustc_attr as attr;
|
||||
use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan};
|
||||
use rustc_errors::{ErrorGuaranteed, MultiSpan};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||
use rustc_hir::def::{CtorKind, DefKind};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::{ItemKind, Node, PathSegment};
|
||||
use rustc_infer::infer::opaque_types::ConstrainOpaqueTypeRegionVisitor;
|
||||
use rustc_hir::Node;
|
||||
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
||||
use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
|
||||
use rustc_infer::traits::{Obligation, TraitEngineExt as _};
|
||||
use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::middle::stability::EvalResult;
|
||||
use rustc_middle::traits::{DefiningAnchor, ObligationCauseCode};
|
||||
use rustc_middle::ty::fold::BottomUpFolder;
|
||||
@@ -218,9 +215,6 @@ fn check_opaque(tcx: TyCtxt<'_>, id: hir::ItemId) {
|
||||
let args = GenericArgs::identity_for_item(tcx, item.owner_id);
|
||||
let span = tcx.def_span(item.owner_id.def_id);
|
||||
|
||||
if !tcx.features().impl_trait_projections {
|
||||
check_opaque_for_inheriting_lifetimes(tcx, item.owner_id.def_id, span);
|
||||
}
|
||||
if tcx.type_of(item.owner_id.def_id).instantiate_identity().references_error() {
|
||||
return;
|
||||
}
|
||||
@@ -231,129 +225,6 @@ fn check_opaque(tcx: TyCtxt<'_>, id: hir::ItemId) {
|
||||
let _ = check_opaque_meets_bounds(tcx, item.owner_id.def_id, span, &origin);
|
||||
}
|
||||
|
||||
/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result
|
||||
/// in "inheriting lifetimes".
|
||||
#[instrument(level = "debug", skip(tcx, span))]
|
||||
pub(super) fn check_opaque_for_inheriting_lifetimes(
|
||||
tcx: TyCtxt<'_>,
|
||||
def_id: LocalDefId,
|
||||
span: Span,
|
||||
) {
|
||||
let item = tcx.hir().expect_item(def_id);
|
||||
debug!(?item, ?span);
|
||||
|
||||
struct ProhibitOpaqueVisitor<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
opaque_identity_ty: Ty<'tcx>,
|
||||
parent_count: u32,
|
||||
references_parent_regions: bool,
|
||||
selftys: Vec<(Span, Option<String>)>,
|
||||
}
|
||||
|
||||
impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for ProhibitOpaqueVisitor<'tcx> {
|
||||
type BreakTy = Ty<'tcx>;
|
||||
|
||||
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
debug!(?t, "root_visit_ty");
|
||||
if t == self.opaque_identity_ty {
|
||||
ControlFlow::Continue(())
|
||||
} else {
|
||||
t.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
|
||||
tcx: self.tcx,
|
||||
op: |region| {
|
||||
if let ty::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = *region
|
||||
&& index < self.parent_count
|
||||
{
|
||||
self.references_parent_regions= true;
|
||||
}
|
||||
},
|
||||
});
|
||||
if self.references_parent_regions {
|
||||
ControlFlow::Break(t)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for ProhibitOpaqueVisitor<'tcx> {
|
||||
type NestedFilter = nested_filter::OnlyBodies;
|
||||
|
||||
fn nested_visit_map(&mut self) -> Self::Map {
|
||||
self.tcx.hir()
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
|
||||
match arg.kind {
|
||||
hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments {
|
||||
[PathSegment { res: Res::SelfTyParam { .. }, .. }] => {
|
||||
let impl_ty_name = None;
|
||||
self.selftys.push((path.span, impl_ty_name));
|
||||
}
|
||||
[PathSegment { res: Res::SelfTyAlias { alias_to: def_id, .. }, .. }] => {
|
||||
let impl_ty_name = Some(self.tcx.def_path_str(*def_id));
|
||||
self.selftys.push((path.span, impl_ty_name));
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
hir::intravisit::walk_ty(self, arg);
|
||||
}
|
||||
}
|
||||
|
||||
if let ItemKind::OpaqueTy(&hir::OpaqueTy {
|
||||
origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..),
|
||||
..
|
||||
}) = item.kind
|
||||
{
|
||||
let args = GenericArgs::identity_for_item(tcx, def_id);
|
||||
let opaque_identity_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args);
|
||||
let mut visitor = ProhibitOpaqueVisitor {
|
||||
opaque_identity_ty,
|
||||
parent_count: tcx.generics_of(def_id).parent_count as u32,
|
||||
references_parent_regions: false,
|
||||
tcx,
|
||||
selftys: vec![],
|
||||
};
|
||||
let prohibit_opaque = tcx
|
||||
.explicit_item_bounds(def_id)
|
||||
.instantiate_identity_iter_copied()
|
||||
.try_for_each(|(predicate, _)| predicate.visit_with(&mut visitor));
|
||||
|
||||
if let Some(ty) = prohibit_opaque.break_value() {
|
||||
visitor.visit_item(&item);
|
||||
let is_async = match item.kind {
|
||||
ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => {
|
||||
matches!(origin, hir::OpaqueTyOrigin::AsyncFn(..))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let mut err = feature_err(
|
||||
&tcx.sess.parse_sess,
|
||||
sym::impl_trait_projections,
|
||||
span,
|
||||
format!(
|
||||
"`{}` return type cannot contain a projection or `Self` that references \
|
||||
lifetimes from a parent scope",
|
||||
if is_async { "async fn" } else { "impl Trait" },
|
||||
),
|
||||
);
|
||||
for (span, name) in visitor.selftys {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"consider spelling out the type instead",
|
||||
name.unwrap_or_else(|| format!("{ty:?}")),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that an opaque type does not contain cycles.
|
||||
pub(super) fn check_opaque_for_cycles<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
||||
@@ -20,6 +20,7 @@ fn equate_intrinsic_type<'tcx>(
|
||||
it: &hir::ForeignItem<'_>,
|
||||
n_tps: usize,
|
||||
n_lts: usize,
|
||||
n_cts: usize,
|
||||
sig: ty::PolyFnSig<'tcx>,
|
||||
) {
|
||||
let (own_counts, span) = match &it.kind {
|
||||
@@ -51,7 +52,7 @@ fn equate_intrinsic_type<'tcx>(
|
||||
|
||||
if gen_count_ok(own_counts.lifetimes, n_lts, "lifetime")
|
||||
&& gen_count_ok(own_counts.types, n_tps, "type")
|
||||
&& gen_count_ok(own_counts.consts, 0, "const")
|
||||
&& gen_count_ok(own_counts.consts, n_cts, "const")
|
||||
{
|
||||
let it_def_id = it.owner_id.def_id;
|
||||
check_function_signature(
|
||||
@@ -489,7 +490,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
|
||||
};
|
||||
let sig = tcx.mk_fn_sig(inputs, output, false, unsafety, Abi::RustIntrinsic);
|
||||
let sig = ty::Binder::bind_with_vars(sig, bound_vars);
|
||||
equate_intrinsic_type(tcx, it, n_tps, n_lts, sig)
|
||||
equate_intrinsic_type(tcx, it, n_tps, n_lts, 0, sig)
|
||||
}
|
||||
|
||||
/// Type-check `extern "platform-intrinsic" { ... }` functions.
|
||||
@@ -501,9 +502,9 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>)
|
||||
|
||||
let name = it.ident.name;
|
||||
|
||||
let (n_tps, inputs, output) = match name {
|
||||
let (n_tps, n_cts, inputs, output) = match name {
|
||||
sym::simd_eq | sym::simd_ne | sym::simd_lt | sym::simd_le | sym::simd_gt | sym::simd_ge => {
|
||||
(2, vec![param(0), param(0)], param(1))
|
||||
(2, 0, vec![param(0), param(0)], param(1))
|
||||
}
|
||||
sym::simd_add
|
||||
| sym::simd_sub
|
||||
@@ -519,8 +520,8 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>)
|
||||
| sym::simd_fmax
|
||||
| sym::simd_fpow
|
||||
| sym::simd_saturating_add
|
||||
| sym::simd_saturating_sub => (1, vec![param(0), param(0)], param(0)),
|
||||
sym::simd_arith_offset => (2, vec![param(0), param(1)], param(0)),
|
||||
| sym::simd_saturating_sub => (1, 0, vec![param(0), param(0)], param(0)),
|
||||
sym::simd_arith_offset => (2, 0, vec![param(0), param(1)], param(0)),
|
||||
sym::simd_neg
|
||||
| sym::simd_bswap
|
||||
| sym::simd_bitreverse
|
||||
@@ -538,25 +539,25 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>)
|
||||
| sym::simd_ceil
|
||||
| sym::simd_floor
|
||||
| sym::simd_round
|
||||
| sym::simd_trunc => (1, vec![param(0)], param(0)),
|
||||
sym::simd_fpowi => (1, vec![param(0), tcx.types.i32], param(0)),
|
||||
sym::simd_fma => (1, vec![param(0), param(0), param(0)], param(0)),
|
||||
sym::simd_gather => (3, vec![param(0), param(1), param(2)], param(0)),
|
||||
sym::simd_scatter => (3, vec![param(0), param(1), param(2)], Ty::new_unit(tcx)),
|
||||
sym::simd_insert => (2, vec![param(0), tcx.types.u32, param(1)], param(0)),
|
||||
sym::simd_extract => (2, vec![param(0), tcx.types.u32], param(1)),
|
||||
| sym::simd_trunc => (1, 0, vec![param(0)], param(0)),
|
||||
sym::simd_fpowi => (1, 0, vec![param(0), tcx.types.i32], param(0)),
|
||||
sym::simd_fma => (1, 0, vec![param(0), param(0), param(0)], param(0)),
|
||||
sym::simd_gather => (3, 0, vec![param(0), param(1), param(2)], param(0)),
|
||||
sym::simd_scatter => (3, 0, vec![param(0), param(1), param(2)], Ty::new_unit(tcx)),
|
||||
sym::simd_insert => (2, 0, vec![param(0), tcx.types.u32, param(1)], param(0)),
|
||||
sym::simd_extract => (2, 0, vec![param(0), tcx.types.u32], param(1)),
|
||||
sym::simd_cast
|
||||
| sym::simd_as
|
||||
| sym::simd_cast_ptr
|
||||
| sym::simd_expose_addr
|
||||
| sym::simd_from_exposed_addr => (2, vec![param(0)], param(1)),
|
||||
sym::simd_bitmask => (2, vec![param(0)], param(1)),
|
||||
| sym::simd_from_exposed_addr => (2, 0, vec![param(0)], param(1)),
|
||||
sym::simd_bitmask => (2, 0, vec![param(0)], param(1)),
|
||||
sym::simd_select | sym::simd_select_bitmask => {
|
||||
(2, vec![param(0), param(1), param(1)], param(1))
|
||||
(2, 0, vec![param(0), param(1), param(1)], param(1))
|
||||
}
|
||||
sym::simd_reduce_all | sym::simd_reduce_any => (1, vec![param(0)], tcx.types.bool),
|
||||
sym::simd_reduce_all | sym::simd_reduce_any => (1, 0, vec![param(0)], tcx.types.bool),
|
||||
sym::simd_reduce_add_ordered | sym::simd_reduce_mul_ordered => {
|
||||
(2, vec![param(0), param(1)], param(1))
|
||||
(2, 0, vec![param(0), param(1)], param(1))
|
||||
}
|
||||
sym::simd_reduce_add_unordered
|
||||
| sym::simd_reduce_mul_unordered
|
||||
@@ -566,8 +567,9 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>)
|
||||
| sym::simd_reduce_min
|
||||
| sym::simd_reduce_max
|
||||
| sym::simd_reduce_min_nanless
|
||||
| sym::simd_reduce_max_nanless => (2, vec![param(0)], param(1)),
|
||||
sym::simd_shuffle => (3, vec![param(0), param(0), param(1)], param(2)),
|
||||
| sym::simd_reduce_max_nanless => (2, 0, vec![param(0)], param(1)),
|
||||
sym::simd_shuffle => (3, 0, vec![param(0), param(0), param(1)], param(2)),
|
||||
sym::simd_shuffle_generic => (2, 1, vec![param(0), param(0)], param(1)),
|
||||
_ => {
|
||||
let msg = format!("unrecognized platform-specific intrinsic function: `{name}`");
|
||||
tcx.sess.struct_span_err(it.span, msg).emit();
|
||||
@@ -577,5 +579,5 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>)
|
||||
|
||||
let sig = tcx.mk_fn_sig(inputs, output, false, hir::Unsafety::Unsafe, Abi::PlatformIntrinsic);
|
||||
let sig = ty::Binder::dummy(sig);
|
||||
equate_intrinsic_type(tcx, it, n_tps, 0, sig)
|
||||
equate_intrinsic_type(tcx, it, n_tps, 0, n_cts, sig)
|
||||
}
|
||||
|
||||
@@ -329,41 +329,52 @@ fn bounds_from_generic_predicates<'tcx>(
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let generics = if types.is_empty() {
|
||||
"".to_string()
|
||||
} else {
|
||||
format!(
|
||||
"<{}>",
|
||||
types
|
||||
.keys()
|
||||
.filter_map(|t| match t.kind() {
|
||||
ty::Param(_) => Some(t.to_string()),
|
||||
// Avoid suggesting the following:
|
||||
// fn foo<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as Trait>::Bar: Other {}
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
)
|
||||
};
|
||||
|
||||
let mut where_clauses = vec![];
|
||||
let mut types_str = vec![];
|
||||
for (ty, bounds) in types {
|
||||
where_clauses
|
||||
.extend(bounds.into_iter().map(|bound| format!("{}: {}", ty, tcx.def_path_str(bound))));
|
||||
}
|
||||
for projection in &projections {
|
||||
let p = projection.skip_binder();
|
||||
// FIXME: this is not currently supported syntax, we should be looking at the `types` and
|
||||
// insert the associated types where they correspond, but for now let's be "lazy" and
|
||||
// propose this instead of the following valid resugaring:
|
||||
// `T: Trait, Trait::Assoc = K` → `T: Trait<Assoc = K>`
|
||||
where_clauses.push(format!("{} = {}", tcx.def_path_str(p.projection_ty.def_id), p.term));
|
||||
if let ty::Param(_) = ty.kind() {
|
||||
let mut bounds_str = vec![];
|
||||
for bound in bounds {
|
||||
let mut projections_str = vec![];
|
||||
for projection in &projections {
|
||||
let p = projection.skip_binder();
|
||||
let alias_ty = p.projection_ty;
|
||||
if bound == tcx.parent(alias_ty.def_id) && alias_ty.self_ty() == ty {
|
||||
let name = tcx.item_name(alias_ty.def_id);
|
||||
projections_str.push(format!("{} = {}", name, p.term));
|
||||
}
|
||||
}
|
||||
let bound_def_path = tcx.def_path_str(bound);
|
||||
if projections_str.is_empty() {
|
||||
where_clauses.push(format!("{}: {}", ty, bound_def_path));
|
||||
} else {
|
||||
bounds_str.push(format!("{}<{}>", bound_def_path, projections_str.join(", ")));
|
||||
}
|
||||
}
|
||||
if bounds_str.is_empty() {
|
||||
types_str.push(ty.to_string());
|
||||
} else {
|
||||
types_str.push(format!("{}: {}", ty, bounds_str.join(" + ")));
|
||||
}
|
||||
} else {
|
||||
// Avoid suggesting the following:
|
||||
// fn foo<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as Trait>::Bar: Other {}
|
||||
where_clauses.extend(
|
||||
bounds.into_iter().map(|bound| format!("{}: {}", ty, tcx.def_path_str(bound))),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let generics =
|
||||
if types_str.is_empty() { "".to_string() } else { format!("<{}>", types_str.join(", ")) };
|
||||
|
||||
let where_clauses = if where_clauses.is_empty() {
|
||||
String::new()
|
||||
"".to_string()
|
||||
} else {
|
||||
format!(" where {}", where_clauses.join(", "))
|
||||
};
|
||||
|
||||
(generics, where_clauses)
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
|
||||
use rustc_trait_selection::traits::misc::{
|
||||
type_allowed_to_implement_const_param_ty, ConstParamTyImplementationError,
|
||||
};
|
||||
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::{
|
||||
@@ -865,43 +868,65 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
|
||||
);
|
||||
});
|
||||
} else {
|
||||
let err_ty_str;
|
||||
let mut is_ptr = true;
|
||||
|
||||
let err = match ty.kind() {
|
||||
let diag = match ty.kind() {
|
||||
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => None,
|
||||
ty::FnPtr(_) => Some("function pointers"),
|
||||
ty::RawPtr(_) => Some("raw pointers"),
|
||||
_ => {
|
||||
is_ptr = false;
|
||||
err_ty_str = format!("`{ty}`");
|
||||
Some(err_ty_str.as_str())
|
||||
}
|
||||
ty::FnPtr(_) => Some(tcx.sess.struct_span_err(
|
||||
hir_ty.span,
|
||||
"using function pointers as const generic parameters is forbidden",
|
||||
)),
|
||||
ty::RawPtr(_) => Some(tcx.sess.struct_span_err(
|
||||
hir_ty.span,
|
||||
"using raw pointers as const generic parameters is forbidden",
|
||||
)),
|
||||
_ => Some(tcx.sess.struct_span_err(
|
||||
hir_ty.span,
|
||||
format!("`{}` is forbidden as the type of a const generic parameter", ty),
|
||||
)),
|
||||
};
|
||||
|
||||
if let Some(unsupported_type) = err {
|
||||
if is_ptr {
|
||||
tcx.sess.span_err(
|
||||
hir_ty.span,
|
||||
format!(
|
||||
"using {unsupported_type} as const generic parameters is forbidden",
|
||||
),
|
||||
);
|
||||
} else {
|
||||
let mut err = tcx.sess.struct_span_err(
|
||||
hir_ty.span,
|
||||
format!(
|
||||
"{unsupported_type} is forbidden as the type of a const generic parameter",
|
||||
),
|
||||
);
|
||||
err.note("the only supported types are integers, `bool` and `char`");
|
||||
if tcx.sess.is_nightly_build() {
|
||||
err.help(
|
||||
"more complex types are supported with `#![feature(adt_const_params)]`",
|
||||
);
|
||||
if let Some(mut diag) = diag {
|
||||
diag.note("the only supported types are integers, `bool` and `char`");
|
||||
|
||||
let cause = ObligationCause::misc(hir_ty.span, param.def_id);
|
||||
let may_suggest_feature = match type_allowed_to_implement_const_param_ty(
|
||||
tcx,
|
||||
tcx.param_env(param.def_id),
|
||||
ty,
|
||||
cause,
|
||||
) {
|
||||
// Can never implement `ConstParamTy`, don't suggest anything.
|
||||
Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => false,
|
||||
// May be able to implement `ConstParamTy`. Only emit the feature help
|
||||
// if the type is local, since the user may be able to fix the local type.
|
||||
Err(ConstParamTyImplementationError::InfrigingFields(..)) => {
|
||||
fn ty_is_local(ty: Ty<'_>) -> bool {
|
||||
match ty.kind() {
|
||||
ty::Adt(adt_def, ..) => adt_def.did().is_local(),
|
||||
// Arrays and slices use the inner type's `ConstParamTy`.
|
||||
ty::Array(ty, ..) => ty_is_local(*ty),
|
||||
ty::Slice(ty) => ty_is_local(*ty),
|
||||
// `&` references use the inner type's `ConstParamTy`.
|
||||
// `&mut` are not supported.
|
||||
ty::Ref(_, ty, ast::Mutability::Not) => ty_is_local(*ty),
|
||||
// Say that a tuple is local if any of its components are local.
|
||||
// This is not strictly correct, but it's likely that the user can fix the local component.
|
||||
ty::Tuple(tys) => tys.iter().any(|ty| ty_is_local(ty)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
ty_is_local(ty)
|
||||
}
|
||||
err.emit();
|
||||
// Implments `ConstParamTy`, suggest adding the feature to enable.
|
||||
Ok(..) => true,
|
||||
};
|
||||
if may_suggest_feature && tcx.sess.is_nightly_build() {
|
||||
diag.help(
|
||||
"add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types",
|
||||
);
|
||||
}
|
||||
|
||||
diag.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
//! Check properties that are required by built-in traits and set
|
||||
//! up data structures required by type-checking/codegen.
|
||||
|
||||
use crate::errors::{
|
||||
ConstParamTyImplOnNonAdt, CopyImplOnNonAdt, CopyImplOnTypeWithDtor, DropImplOnWrongItem,
|
||||
};
|
||||
use crate::errors;
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::{struct_span_err, ErrorGuaranteed, MultiSpan};
|
||||
use rustc_errors::{ErrorGuaranteed, MultiSpan};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
@@ -65,7 +64,7 @@ fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
|
||||
|
||||
let impl_ = tcx.hir().expect_item(impl_did).expect_impl();
|
||||
|
||||
tcx.sess.emit_err(DropImplOnWrongItem { span: impl_.self_ty.span });
|
||||
tcx.sess.emit_err(errors::DropImplOnWrongItem { span: impl_.self_ty.span });
|
||||
}
|
||||
|
||||
fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
|
||||
@@ -91,10 +90,10 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
|
||||
infringing_fields_error(tcx, fields, LangItem::Copy, impl_did, span);
|
||||
}
|
||||
Err(CopyImplementationError::NotAnAdt) => {
|
||||
tcx.sess.emit_err(CopyImplOnNonAdt { span });
|
||||
tcx.sess.emit_err(errors::CopyImplOnNonAdt { span });
|
||||
}
|
||||
Err(CopyImplementationError::HasDestructor) => {
|
||||
tcx.sess.emit_err(CopyImplOnTypeWithDtor { span });
|
||||
tcx.sess.emit_err(errors::CopyImplOnTypeWithDtor { span });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,7 +116,7 @@ fn visit_implementation_of_const_param_ty(tcx: TyCtxt<'_>, impl_did: LocalDefId)
|
||||
infringing_fields_error(tcx, fields, LangItem::ConstParamTy, impl_did, span);
|
||||
}
|
||||
Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => {
|
||||
tcx.sess.emit_err(ConstParamTyImplOnNonAdt { span });
|
||||
tcx.sess.emit_err(errors::ConstParamTyImplOnNonAdt { span });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -152,8 +151,6 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef
|
||||
|
||||
let param_env = tcx.param_env(impl_did);
|
||||
|
||||
let create_err = |msg: &str| struct_span_err!(tcx.sess, span, E0378, "{}", msg);
|
||||
|
||||
let infcx = tcx.infer_ctxt().build();
|
||||
let cause = ObligationCause::misc(span, impl_did);
|
||||
|
||||
@@ -176,22 +173,19 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef
|
||||
let source_path = tcx.def_path_str(def_a.did());
|
||||
let target_path = tcx.def_path_str(def_b.did());
|
||||
|
||||
create_err(&format!(
|
||||
"the trait `DispatchFromDyn` may only be implemented \
|
||||
for a coercion between structures with the same \
|
||||
definition; expected `{source_path}`, found `{target_path}`",
|
||||
))
|
||||
.emit();
|
||||
tcx.sess.emit_err(errors::DispatchFromDynCoercion {
|
||||
span,
|
||||
trait_name: "DispatchFromDyn",
|
||||
note: true,
|
||||
source_path,
|
||||
target_path,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if def_a.repr().c() || def_a.repr().packed() {
|
||||
create_err(
|
||||
"structs implementing `DispatchFromDyn` may not have \
|
||||
`#[repr(packed)]` or `#[repr(C)]`",
|
||||
)
|
||||
.emit();
|
||||
tcx.sess.emit_err(errors::DispatchFromDynRepr { span });
|
||||
}
|
||||
|
||||
let fields = &def_a.non_enum_variant().fields;
|
||||
@@ -213,16 +207,11 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef
|
||||
infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, ty_a, ty_b)
|
||||
{
|
||||
if ok.obligations.is_empty() {
|
||||
create_err(
|
||||
"the trait `DispatchFromDyn` may only be implemented \
|
||||
for structs containing the field being coerced, \
|
||||
ZST fields with 1 byte alignment, and nothing else",
|
||||
)
|
||||
.note(format!(
|
||||
"extra field `{}` of type `{}` is not allowed",
|
||||
field.name, ty_a,
|
||||
))
|
||||
.emit();
|
||||
tcx.sess.emit_err(errors::DispatchFromDynZST {
|
||||
span,
|
||||
name: field.name,
|
||||
ty: ty_a,
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -233,36 +222,29 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if coerced_fields.is_empty() {
|
||||
create_err(
|
||||
"the trait `DispatchFromDyn` may only be implemented \
|
||||
for a coercion between structures with a single field \
|
||||
being coerced, none found",
|
||||
)
|
||||
.emit();
|
||||
tcx.sess.emit_err(errors::DispatchFromDynSingle {
|
||||
span,
|
||||
trait_name: "DispatchFromDyn",
|
||||
note: true,
|
||||
});
|
||||
} else if coerced_fields.len() > 1 {
|
||||
create_err("implementing the `DispatchFromDyn` trait requires multiple coercions")
|
||||
.note(
|
||||
"the trait `DispatchFromDyn` may only be implemented \
|
||||
for a coercion between structures with a single field \
|
||||
being coerced",
|
||||
)
|
||||
.note(format!(
|
||||
"currently, {} fields need coercions: {}",
|
||||
coerced_fields.len(),
|
||||
coerced_fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
format!(
|
||||
"`{}` (`{}` to `{}`)",
|
||||
field.name,
|
||||
field.ty(tcx, args_a),
|
||||
field.ty(tcx, args_b),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
))
|
||||
.emit();
|
||||
tcx.sess.emit_err(errors::DispatchFromDynMulti {
|
||||
span,
|
||||
coercions_note: true,
|
||||
number: coerced_fields.len(),
|
||||
coercions: coerced_fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
format!(
|
||||
"`{}` (`{}` to `{}`)",
|
||||
field.name,
|
||||
field.ty(tcx, args_a),
|
||||
field.ty(tcx, args_b),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
});
|
||||
} else {
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
for field in coerced_fields {
|
||||
@@ -288,11 +270,7 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
create_err(
|
||||
"the trait `DispatchFromDyn` may only be implemented \
|
||||
for a coercion between structures",
|
||||
)
|
||||
.emit();
|
||||
tcx.sess.emit_err(errors::CoerceUnsizedMay { span, trait_name: "DispatchFromDyn" });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -359,17 +337,13 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe
|
||||
if def_a != def_b {
|
||||
let source_path = tcx.def_path_str(def_a.did());
|
||||
let target_path = tcx.def_path_str(def_b.did());
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
tcx.sess.emit_err(errors::DispatchFromDynSame {
|
||||
span,
|
||||
E0377,
|
||||
"the trait `CoerceUnsized` may only be implemented \
|
||||
for a coercion between structures with the same \
|
||||
definition; expected `{}`, found `{}`",
|
||||
trait_name: "CoerceUnsized",
|
||||
note: true,
|
||||
source_path,
|
||||
target_path
|
||||
)
|
||||
.emit();
|
||||
target_path,
|
||||
});
|
||||
return err_info;
|
||||
}
|
||||
|
||||
@@ -445,15 +419,11 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if diff_fields.is_empty() {
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
tcx.sess.emit_err(errors::CoerceUnsizedOneField {
|
||||
span,
|
||||
E0374,
|
||||
"the trait `CoerceUnsized` may only be implemented \
|
||||
for a coercion between structures with one field \
|
||||
being coerced, none found"
|
||||
)
|
||||
.emit();
|
||||
trait_name: "CoerceUnsized",
|
||||
note: true,
|
||||
});
|
||||
return err_info;
|
||||
} else if diff_fields.len() > 1 {
|
||||
let item = tcx.hir().expect_item(impl_did);
|
||||
@@ -463,29 +433,17 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe
|
||||
tcx.def_span(impl_did)
|
||||
};
|
||||
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
tcx.sess.emit_err(errors::CoerceUnsizedMulti {
|
||||
span,
|
||||
E0375,
|
||||
"implementing the trait \
|
||||
`CoerceUnsized` requires multiple \
|
||||
coercions"
|
||||
)
|
||||
.note(
|
||||
"`CoerceUnsized` may only be implemented for \
|
||||
a coercion between structures with one field being coerced",
|
||||
)
|
||||
.note(format!(
|
||||
"currently, {} fields need coercions: {}",
|
||||
diff_fields.len(),
|
||||
diff_fields
|
||||
coercions_note: true,
|
||||
number: diff_fields.len(),
|
||||
coercions: diff_fields
|
||||
.iter()
|
||||
.map(|&(i, a, b)| { format!("`{}` (`{}` to `{}`)", fields[i].name, a, b) })
|
||||
.map(|&(i, a, b)| format!("`{}` (`{}` to `{}`)", fields[i].name, a, b))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
))
|
||||
.span_label(span, "requires multiple coercions")
|
||||
.emit();
|
||||
.join(", "),
|
||||
});
|
||||
|
||||
return err_info;
|
||||
}
|
||||
|
||||
@@ -495,14 +453,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe
|
||||
}
|
||||
|
||||
_ => {
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
span,
|
||||
E0376,
|
||||
"the trait `CoerceUnsized` may only be implemented \
|
||||
for a coercion between structures"
|
||||
)
|
||||
.emit();
|
||||
tcx.sess.emit_err(errors::DispatchFromDynStruct { span, trait_name: "CoerceUnsized" });
|
||||
return err_info;
|
||||
}
|
||||
};
|
||||
@@ -540,13 +491,6 @@ fn infringing_fields_error(
|
||||
|
||||
let trait_name = tcx.def_path_str(trait_did);
|
||||
|
||||
let mut err = struct_span_err!(
|
||||
tcx.sess,
|
||||
impl_span,
|
||||
E0204,
|
||||
"the trait `{trait_name}` cannot be implemented for this type"
|
||||
);
|
||||
|
||||
// We'll try to suggest constraining type parameters to fulfill the requirements of
|
||||
// their `Copy` implementation.
|
||||
let mut errors: BTreeMap<_, Vec<_>> = Default::default();
|
||||
@@ -554,14 +498,15 @@ fn infringing_fields_error(
|
||||
|
||||
let mut seen_tys = FxHashSet::default();
|
||||
|
||||
let mut label_spans = Vec::new();
|
||||
|
||||
for (field, ty, reason) in fields {
|
||||
// Only report an error once per type.
|
||||
if !seen_tys.insert(ty) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let field_span = tcx.def_span(field.did);
|
||||
err.span_label(field_span, format!("this field does not implement `{trait_name}`"));
|
||||
label_spans.push(tcx.def_span(field.did));
|
||||
|
||||
match reason {
|
||||
InfringingFieldsReason::Fulfill(fulfillment_errors) => {
|
||||
@@ -625,13 +570,24 @@ fn infringing_fields_error(
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut notes = Vec::new();
|
||||
for ((ty, error_predicate), spans) in errors {
|
||||
let span: MultiSpan = spans.into();
|
||||
err.span_note(
|
||||
notes.push(errors::ImplForTyRequires {
|
||||
span,
|
||||
format!("the `{trait_name}` impl for `{ty}` requires that `{error_predicate}`"),
|
||||
);
|
||||
error_predicate,
|
||||
trait_name: trait_name.clone(),
|
||||
ty,
|
||||
});
|
||||
}
|
||||
|
||||
let mut err = tcx.sess.create_err(errors::TraitCannotImplForTy {
|
||||
span: impl_span,
|
||||
trait_name,
|
||||
label_spans,
|
||||
notes,
|
||||
});
|
||||
|
||||
suggest_constraining_type_params(
|
||||
tcx,
|
||||
tcx.hir().get_generics(impl_did).expect("impls always have generics"),
|
||||
|
||||
@@ -1374,7 +1374,7 @@ fn impl_trait_ref(
|
||||
// make astconv happy.
|
||||
let mut path_segments = ast_trait_ref.path.segments.to_vec();
|
||||
let last_segment = path_segments.len() - 1;
|
||||
let mut args = path_segments[last_segment].args().clone();
|
||||
let mut args = *path_segments[last_segment].args();
|
||||
let last_arg = args.args.len() - 1;
|
||||
assert!(matches!(args.args[last_arg], hir::GenericArg::Const(anon_const) if tcx.has_attr(anon_const.value.def_id, sym::rustc_host)));
|
||||
args.args = &args.args[..args.args.len() - 1];
|
||||
|
||||
@@ -963,6 +963,25 @@ pub struct InherentTyOutside {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_coerce_unsized_may, code = "E0378")]
|
||||
pub struct DispatchFromDynCoercion<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub trait_name: &'a str,
|
||||
#[note(hir_analysis_coercion_between_struct_same_note)]
|
||||
pub note: bool,
|
||||
pub source_path: String,
|
||||
pub target_path: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_dispatch_from_dyn_repr, code = "E0378")]
|
||||
pub struct DispatchFromDynRepr {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_inherent_ty_outside_relevant, code = "E0390")]
|
||||
#[help]
|
||||
@@ -1025,3 +1044,108 @@ pub struct InherentNominal {
|
||||
#[label]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_dispatch_from_dyn_zst, code = "E0378")]
|
||||
#[note]
|
||||
pub struct DispatchFromDynZST<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: Symbol,
|
||||
pub ty: Ty<'a>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_coerce_unsized_may, code = "E0378")]
|
||||
pub struct DispatchFromDynSingle<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub trait_name: &'a str,
|
||||
#[note(hir_analysis_coercion_between_struct_single_note)]
|
||||
pub note: bool,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_dispatch_from_dyn_multi, code = "E0378")]
|
||||
#[note]
|
||||
pub struct DispatchFromDynMulti {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[note(hir_analysis_coercions_note)]
|
||||
pub coercions_note: bool,
|
||||
pub number: usize,
|
||||
pub coercions: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_coerce_unsized_may, code = "E0376")]
|
||||
pub struct DispatchFromDynStruct<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub trait_name: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_coerce_unsized_may, code = "E0377")]
|
||||
pub struct DispatchFromDynSame<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub trait_name: &'a str,
|
||||
#[note(hir_analysis_coercion_between_struct_same_note)]
|
||||
pub note: bool,
|
||||
pub source_path: String,
|
||||
pub target_path: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_coerce_unsized_may, code = "E0374")]
|
||||
pub struct CoerceUnsizedOneField<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub trait_name: &'a str,
|
||||
#[note(hir_analysis_coercion_between_struct_single_note)]
|
||||
pub note: bool,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_coerce_unsized_multi, code = "E0375")]
|
||||
#[note]
|
||||
pub struct CoerceUnsizedMulti {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
#[note(hir_analysis_coercions_note)]
|
||||
pub coercions_note: bool,
|
||||
pub number: usize,
|
||||
pub coercions: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_coerce_unsized_may, code = "E0378")]
|
||||
pub struct CoerceUnsizedMay<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub trait_name: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_trait_cannot_impl_for_ty, code = "E0204")]
|
||||
pub struct TraitCannotImplForTy {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub trait_name: String,
|
||||
#[label]
|
||||
pub label_spans: Vec<Span>,
|
||||
#[subdiagnostic]
|
||||
pub notes: Vec<ImplForTyRequires>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[note(hir_analysis_requires_note)]
|
||||
pub struct ImplForTyRequires {
|
||||
#[primary_span]
|
||||
pub span: MultiSpan,
|
||||
pub error_predicate: String,
|
||||
pub trait_name: String,
|
||||
pub ty: String,
|
||||
}
|
||||
|
||||
@@ -644,7 +644,7 @@ fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
|
||||
if self.can_eq(self.param_env, ty, expected) {
|
||||
err.span_label(
|
||||
ex.span,
|
||||
format!("expected because of this `break`"),
|
||||
"expected because of this `break`",
|
||||
);
|
||||
exit = true;
|
||||
}
|
||||
|
||||
@@ -621,7 +621,7 @@ trait defining them",
|
||||
{
|
||||
diag.span_label(
|
||||
item.span,
|
||||
format!("associated type is `default` and may be overridden"),
|
||||
"associated type is `default` and may be overridden",
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1316,6 +1316,40 @@ pub fn get_associated_type(
|
||||
})
|
||||
}
|
||||
|
||||
/// If the given expression is a local binding, find the initializer expression.
|
||||
/// If that initializer expression is another local binding, find its initializer again.
|
||||
///
|
||||
/// This process repeats as long as possible (but usually no more than once).
|
||||
/// Type-check adjustments are not taken in account in this function.
|
||||
///
|
||||
/// Examples:
|
||||
/// ```
|
||||
/// let abc = 1;
|
||||
/// let def = abc + 2;
|
||||
/// // ^^^^^^^ output
|
||||
/// let def = def;
|
||||
/// dbg!(def);
|
||||
/// // ^^^ input
|
||||
/// ```
|
||||
pub fn expr_or_init<'a>(&self, mut expr: &'a hir::Expr<'tcx>) -> &'a hir::Expr<'tcx> {
|
||||
expr = expr.peel_blocks();
|
||||
|
||||
while let hir::ExprKind::Path(ref qpath) = expr.kind
|
||||
&& let Some(parent_node) = match self.qpath_res(qpath, expr.hir_id) {
|
||||
Res::Local(hir_id) => self.tcx.hir().find_parent(hir_id),
|
||||
_ => None,
|
||||
}
|
||||
&& let Some(init) = match parent_node {
|
||||
hir::Node::Expr(expr) => Some(expr),
|
||||
hir::Node::Local(hir::Local { init, .. }) => *init,
|
||||
_ => None
|
||||
}
|
||||
{
|
||||
expr = init.peel_blocks();
|
||||
}
|
||||
expr
|
||||
}
|
||||
|
||||
/// If the given expression is a local binding, find the initializer expression.
|
||||
/// If that initializer expression is another local or **outside** (`const`/`static`)
|
||||
/// binding, find its initializer again.
|
||||
@@ -1338,7 +1372,10 @@ pub fn get_associated_type(
|
||||
/// dbg!(def);
|
||||
/// // ^^^ input
|
||||
/// ```
|
||||
pub fn expr_or_init<'a>(&self, mut expr: &'a hir::Expr<'tcx>) -> &'a hir::Expr<'tcx> {
|
||||
pub fn expr_or_init_with_outside_body<'a>(
|
||||
&self,
|
||||
mut expr: &'a hir::Expr<'tcx>,
|
||||
) -> &'a hir::Expr<'tcx> {
|
||||
expr = expr.peel_blocks();
|
||||
|
||||
while let hir::ExprKind::Path(ref qpath) = expr.kind
|
||||
|
||||
@@ -5,19 +5,18 @@
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::layout::LayoutError;
|
||||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
||||
use rustc_session::lint::{lint_array, LintArray};
|
||||
use rustc_span::{sym, Span, Symbol};
|
||||
use rustc_target::abi::FIRST_VARIANT;
|
||||
|
||||
use crate::lints::{BuiltinClashingExtern, BuiltinClashingExternSub};
|
||||
use crate::types;
|
||||
use crate::{types, LintVec};
|
||||
|
||||
pub(crate) fn provide(providers: &mut Providers) {
|
||||
*providers = Providers { clashing_extern_declarations, ..*providers };
|
||||
}
|
||||
|
||||
pub(crate) fn get_lints() -> LintArray {
|
||||
lint_array!(CLASHING_EXTERN_DECLARATIONS)
|
||||
pub(crate) fn get_lints() -> LintVec {
|
||||
vec![CLASHING_EXTERN_DECLARATIONS]
|
||||
}
|
||||
|
||||
fn clashing_extern_declarations(tcx: TyCtxt<'_>, (): ()) {
|
||||
|
||||
@@ -84,9 +84,9 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
)
|
||||
};
|
||||
|
||||
let mut init = cx.expr_or_init(arg);
|
||||
let mut init = cx.expr_or_init_with_outside_body(arg);
|
||||
while let ExprKind::AddrOf(.., inner) = init.kind {
|
||||
init = cx.expr_or_init(inner);
|
||||
init = cx.expr_or_init_with_outside_body(inner);
|
||||
}
|
||||
match init.kind {
|
||||
ExprKind::Lit(Spanned { node: lit, .. }) => {
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
pub use passes::{EarlyLintPass, LateLintPass};
|
||||
pub use rustc_session::lint::Level::{self, *};
|
||||
pub use rustc_session::lint::{BufferedEarlyLint, FutureIncompatibleInfo, Lint, LintId};
|
||||
pub use rustc_session::lint::{LintArray, LintPass};
|
||||
pub use rustc_session::lint::{LintPass, LintVec};
|
||||
|
||||
fluent_messages! { "../messages.ftl" }
|
||||
|
||||
@@ -196,7 +196,7 @@ fn lint_mod(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
|
||||
BoxPointers: BoxPointers,
|
||||
PathStatements: PathStatements,
|
||||
LetUnderscore: LetUnderscore,
|
||||
InvalidReferenceCasting: InvalidReferenceCasting::default(),
|
||||
InvalidReferenceCasting: InvalidReferenceCasting,
|
||||
// Depends on referenced function signatures in expressions
|
||||
UnusedResults: UnusedResults,
|
||||
NonUpperCaseGlobals: NonUpperCaseGlobals,
|
||||
|
||||
@@ -98,6 +98,12 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, cx.param_env, did, args) else { return };
|
||||
// (Re)check that it implements the noop diagnostic.
|
||||
let Some(name) = cx.tcx.get_diagnostic_name(i.def_id()) else { return };
|
||||
if !matches!(
|
||||
name,
|
||||
sym::noop_method_borrow | sym::noop_method_clone | sym::noop_method_deref
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
let receiver_ty = cx.typeck_results().expr_ty(receiver);
|
||||
let expr_ty = cx.typeck_results().expr_ty_adjusted(expr);
|
||||
|
||||
@@ -111,7 +111,7 @@ impl $name {
|
||||
}
|
||||
}
|
||||
|
||||
$v fn get_lints() -> $crate::LintArray {
|
||||
$v fn get_lints() -> $crate::LintVec {
|
||||
let mut lints = Vec::new();
|
||||
$(lints.extend_from_slice(&$pass::get_lints());)*
|
||||
lints
|
||||
@@ -226,7 +226,7 @@ impl $name {
|
||||
}
|
||||
}
|
||||
|
||||
$v fn get_lints() -> $crate::LintArray {
|
||||
$v fn get_lints() -> $crate::LintVec {
|
||||
let mut lints = Vec::new();
|
||||
$(lints.extend_from_slice(&$pass::get_lints());)*
|
||||
lints
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, QPath, StmtKind, UnOp};
|
||||
use rustc_hir::{Expr, ExprKind, UnOp};
|
||||
use rustc_middle::ty::{self, TypeAndMut};
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_span::sym;
|
||||
|
||||
use crate::{lints::InvalidReferenceCastingDiag, LateContext, LateLintPass, LintContext};
|
||||
|
||||
@@ -34,38 +33,18 @@
|
||||
"casts of `&T` to `&mut T` without interior mutability"
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InvalidReferenceCasting {
|
||||
casted: FxHashMap<HirId, Span>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(InvalidReferenceCasting => [INVALID_REFERENCE_CASTING]);
|
||||
declare_lint_pass!(InvalidReferenceCasting => [INVALID_REFERENCE_CASTING]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting {
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx rustc_hir::Stmt<'tcx>) {
|
||||
let StmtKind::Local(local) = stmt.kind else {
|
||||
return;
|
||||
};
|
||||
let Local { init: Some(init), els: None, .. } = local else {
|
||||
return;
|
||||
};
|
||||
|
||||
if is_cast_from_const_to_mut(cx, init) {
|
||||
self.casted.insert(local.pat.hir_id, init.span);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
let Some((is_assignment, e)) = is_operation_we_care_about(cx, expr) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let orig_cast = if is_cast_from_const_to_mut(cx, e) {
|
||||
None
|
||||
} else if let ExprKind::Path(QPath::Resolved(_, path)) = e.kind
|
||||
&& let Res::Local(hir_id) = &path.res
|
||||
&& let Some(orig_cast) = self.casted.get(hir_id) {
|
||||
Some(*orig_cast)
|
||||
let init = cx.expr_or_init(e);
|
||||
|
||||
let orig_cast = if is_cast_from_const_to_mut(cx, init) {
|
||||
if init.span != e.span { Some(init.span) } else { None }
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
@@ -125,99 +104,51 @@ fn ptr_write<'tcx>(
|
||||
deref_assign_or_addr_of(e).or_else(|| ptr_write(cx, e))
|
||||
}
|
||||
|
||||
fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
|
||||
let e = e.peel_blocks();
|
||||
fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, orig_expr: &'tcx Expr<'tcx>) -> bool {
|
||||
let mut need_check_freeze = false;
|
||||
let mut e = orig_expr;
|
||||
|
||||
fn from_casts<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'tcx>,
|
||||
need_check_freeze: &mut bool,
|
||||
) -> Option<&'tcx Expr<'tcx>> {
|
||||
// <expr> as *mut ...
|
||||
let mut e = if let ExprKind::Cast(e, t) = e.kind
|
||||
&& let ty::RawPtr(TypeAndMut { mutbl: Mutability::Mut, .. }) = cx.typeck_results().node_type(t.hir_id).kind() {
|
||||
e
|
||||
// <expr>.cast_mut()
|
||||
let end_ty = cx.typeck_results().node_type(orig_expr.hir_id);
|
||||
|
||||
// Bail out early if the end type is **not** a mutable pointer.
|
||||
if !matches!(end_ty.kind(), ty::RawPtr(TypeAndMut { ty: _, mutbl: Mutability::Mut })) {
|
||||
return false;
|
||||
}
|
||||
|
||||
loop {
|
||||
e = e.peel_blocks();
|
||||
// <expr> as ...
|
||||
e = if let ExprKind::Cast(expr, _) = e.kind {
|
||||
expr
|
||||
// <expr>.cast(), <expr>.cast_mut() or <expr>.cast_const()
|
||||
} else if let ExprKind::MethodCall(_, expr, [], _) = e.kind
|
||||
&& let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
|
||||
&& cx.tcx.is_diagnostic_item(sym::ptr_cast_mut, def_id) {
|
||||
&& matches!(
|
||||
cx.tcx.get_diagnostic_name(def_id),
|
||||
Some(sym::ptr_cast | sym::const_ptr_cast | sym::ptr_cast_mut | sym::ptr_cast_const)
|
||||
)
|
||||
{
|
||||
expr
|
||||
// UnsafeCell::raw_get(<expr>)
|
||||
// ptr::from_ref(<expr>), UnsafeCell::raw_get(<expr>) or mem::transmute<_, _>(<expr>)
|
||||
} else if let ExprKind::Call(path, [arg]) = e.kind
|
||||
&& let ExprKind::Path(ref qpath) = path.kind
|
||||
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
|
||||
&& cx.tcx.is_diagnostic_item(sym::unsafe_cell_raw_get, def_id)
|
||||
&& matches!(
|
||||
cx.tcx.get_diagnostic_name(def_id),
|
||||
Some(sym::ptr_from_ref | sym::unsafe_cell_raw_get | sym::transmute)
|
||||
)
|
||||
{
|
||||
*need_check_freeze = true;
|
||||
if cx.tcx.is_diagnostic_item(sym::unsafe_cell_raw_get, def_id) {
|
||||
need_check_freeze = true;
|
||||
}
|
||||
arg
|
||||
} else {
|
||||
return None;
|
||||
break;
|
||||
};
|
||||
|
||||
let mut had_at_least_one_cast = false;
|
||||
loop {
|
||||
e = e.peel_blocks();
|
||||
// <expr> as *mut/const ... or <expr> as <uint>
|
||||
e = if let ExprKind::Cast(expr, t) = e.kind
|
||||
&& matches!(cx.typeck_results().node_type(t.hir_id).kind(), ty::RawPtr(_) | ty::Uint(_)) {
|
||||
had_at_least_one_cast = true;
|
||||
expr
|
||||
// <expr>.cast(), <expr>.cast_mut() or <expr>.cast_const()
|
||||
} else if let ExprKind::MethodCall(_, expr, [], _) = e.kind
|
||||
&& let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
|
||||
&& matches!(
|
||||
cx.tcx.get_diagnostic_name(def_id),
|
||||
Some(sym::ptr_cast | sym::const_ptr_cast | sym::ptr_cast_mut | sym::ptr_cast_const)
|
||||
)
|
||||
{
|
||||
had_at_least_one_cast = true;
|
||||
expr
|
||||
// ptr::from_ref(<expr>) or UnsafeCell::raw_get(<expr>)
|
||||
} else if let ExprKind::Call(path, [arg]) = e.kind
|
||||
&& let ExprKind::Path(ref qpath) = path.kind
|
||||
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
|
||||
&& matches!(
|
||||
cx.tcx.get_diagnostic_name(def_id),
|
||||
Some(sym::ptr_from_ref | sym::unsafe_cell_raw_get)
|
||||
)
|
||||
{
|
||||
if cx.tcx.is_diagnostic_item(sym::unsafe_cell_raw_get, def_id) {
|
||||
*need_check_freeze = true;
|
||||
}
|
||||
return Some(arg);
|
||||
} else if had_at_least_one_cast {
|
||||
return Some(e);
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn from_transmute<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'tcx>,
|
||||
) -> Option<&'tcx Expr<'tcx>> {
|
||||
// mem::transmute::<_, *mut _>(<expr>)
|
||||
if let ExprKind::Call(path, [arg]) = e.kind
|
||||
&& let ExprKind::Path(ref qpath) = path.kind
|
||||
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
|
||||
&& cx.tcx.is_diagnostic_item(sym::transmute, def_id)
|
||||
&& let ty::RawPtr(TypeAndMut { mutbl: Mutability::Mut, .. }) = cx.typeck_results().node_type(e.hir_id).kind() {
|
||||
Some(arg)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
let mut need_check_freeze = false;
|
||||
let Some(e) = from_casts(cx, e, &mut need_check_freeze).or_else(|| from_transmute(cx, e))
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let e = e.peel_blocks();
|
||||
let node_type = cx.typeck_results().node_type(e.hir_id);
|
||||
if let ty::Ref(_, inner_ty, Mutability::Not) = node_type.kind() {
|
||||
let start_ty = cx.typeck_results().node_type(e.hir_id);
|
||||
if let ty::Ref(_, inner_ty, Mutability::Not) = start_ty.kind() {
|
||||
// If an UnsafeCell method is involved we need to additionaly check the
|
||||
// inner type for the presence of the Freeze trait (ie does NOT contain
|
||||
// an UnsafeCell), since in that case we would incorrectly lint on valid casts.
|
||||
|
||||
@@ -785,16 +785,7 @@ macro_rules! declare_tool_lint {
|
||||
);
|
||||
}
|
||||
|
||||
/// Declares a static `LintArray` and return it as an expression.
|
||||
#[macro_export]
|
||||
macro_rules! lint_array {
|
||||
($( $lint:expr ),* ,) => { lint_array!( $($lint),* ) };
|
||||
($( $lint:expr ),*) => {{
|
||||
vec![$($lint),*]
|
||||
}}
|
||||
}
|
||||
|
||||
pub type LintArray = Vec<&'static Lint>;
|
||||
pub type LintVec = Vec<&'static Lint>;
|
||||
|
||||
pub trait LintPass {
|
||||
fn name(&self) -> &'static str;
|
||||
@@ -808,7 +799,7 @@ impl $crate::LintPass for $ty {
|
||||
fn name(&self) -> &'static str { stringify!($ty) }
|
||||
}
|
||||
impl $ty {
|
||||
pub fn get_lints() -> $crate::LintArray { $crate::lint_array!($($lint),*) }
|
||||
pub fn get_lints() -> $crate::LintVec { vec![$($lint),*] }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -63,11 +63,14 @@ pub fn predecessors(&self) -> &Predecessors {
|
||||
}
|
||||
|
||||
/// Returns basic blocks in a reverse postorder.
|
||||
///
|
||||
/// See [`traversal::reverse_postorder`]'s docs to learn what is preorder traversal.
|
||||
///
|
||||
/// [`traversal::reverse_postorder`]: crate::mir::traversal::reverse_postorder
|
||||
#[inline]
|
||||
pub fn reverse_postorder(&self) -> &[BasicBlock] {
|
||||
self.cache.reverse_postorder.get_or_init(|| {
|
||||
let mut rpo: Vec<_> =
|
||||
Postorder::new(&self.basic_blocks, START_BLOCK).map(|(bb, _)| bb).collect();
|
||||
let mut rpo: Vec<_> = Postorder::new(&self.basic_blocks, START_BLOCK).collect();
|
||||
rpo.reverse();
|
||||
rpo
|
||||
})
|
||||
|
||||
@@ -1146,10 +1146,10 @@ fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) ->
|
||||
ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => {
|
||||
write!(fmt, "[-{offset:?} of {min_length:?}]")?;
|
||||
}
|
||||
ProjectionElem::Subslice { from, to, from_end: true } if to == 0 => {
|
||||
ProjectionElem::Subslice { from, to: 0, from_end: true } => {
|
||||
write!(fmt, "[{from:?}:]")?;
|
||||
}
|
||||
ProjectionElem::Subslice { from, to, from_end: true } if from == 0 => {
|
||||
ProjectionElem::Subslice { from: 0, to, from_end: true } => {
|
||||
write!(fmt, "[:-{to:?}]")?;
|
||||
}
|
||||
ProjectionElem::Subslice { from, to, from_end: true } => {
|
||||
|
||||
@@ -139,6 +139,7 @@ pub enum RuntimePhase {
|
||||
/// * [`TerminatorKind::Yield`]
|
||||
/// * [`TerminatorKind::GeneratorDrop`]
|
||||
/// * [`Rvalue::Aggregate`] for any `AggregateKind` except `Array`
|
||||
/// * [`PlaceElem::OpaqueCast`]
|
||||
///
|
||||
/// And the following variants are allowed:
|
||||
/// * [`StatementKind::Retag`]
|
||||
|
||||
@@ -41,6 +41,12 @@ pub fn new(body: &'a Body<'tcx>, root: BasicBlock) -> Preorder<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Preorder traversal of a graph.
|
||||
///
|
||||
/// This function creates an iterator over the `Body`'s basic blocks, that
|
||||
/// returns basic blocks in a preorder.
|
||||
///
|
||||
/// See [`Preorder`]'s docs to learn what is preorder traversal.
|
||||
pub fn preorder<'a, 'tcx>(body: &'a Body<'tcx>) -> Preorder<'a, 'tcx> {
|
||||
Preorder::new(body, START_BLOCK)
|
||||
}
|
||||
@@ -178,7 +184,7 @@ fn traverse_successor(&mut self) {
|
||||
// When we yield `C` and call `traverse_successor`, we push `B` to the stack, but
|
||||
// since we've already visited `E`, that child isn't added to the stack. The last
|
||||
// two iterations yield `B` and finally `A` for a final traversal of [E, D, C, B, A]
|
||||
while let Some(&mut (_, ref mut iter)) = self.visit_stack.last_mut() && let Some(bb) = iter.next_back() {
|
||||
while let Some(bb) = self.visit_stack.last_mut().and_then(|(_, iter)| iter.next_back()) {
|
||||
if self.visited.insert(bb) {
|
||||
if let Some(term) = &self.basic_blocks[bb].terminator {
|
||||
self.visit_stack.push((bb, term.successors()));
|
||||
@@ -188,16 +194,14 @@ fn traverse_successor(&mut self) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> {
|
||||
type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
|
||||
impl<'tcx> Iterator for Postorder<'_, 'tcx> {
|
||||
type Item = BasicBlock;
|
||||
|
||||
fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
|
||||
let next = self.visit_stack.pop();
|
||||
if next.is_some() {
|
||||
self.traverse_successor();
|
||||
}
|
||||
fn next(&mut self) -> Option<BasicBlock> {
|
||||
let (bb, _) = self.visit_stack.pop()?;
|
||||
self.traverse_successor();
|
||||
|
||||
next.map(|(bb, _)| (bb, &self.basic_blocks[bb]))
|
||||
Some(bb)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
@@ -215,10 +219,14 @@ fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an iterator over the `Body`'s basic blocks, that:
|
||||
/// Postorder traversal of a graph.
|
||||
///
|
||||
/// This function creates an iterator over the `Body`'s basic blocks, that:
|
||||
/// - returns basic blocks in a postorder,
|
||||
/// - traverses the `BasicBlocks` CFG cache's reverse postorder backwards, and does not cache the
|
||||
/// postorder itself.
|
||||
///
|
||||
/// See [`Postorder`]'s docs to learn what is postorder traversal.
|
||||
pub fn postorder<'a, 'tcx>(
|
||||
body: &'a Body<'tcx>,
|
||||
) -> impl Iterator<Item = (BasicBlock, &'a BasicBlockData<'tcx>)> + ExactSizeIterator + DoubleEndedIterator
|
||||
@@ -226,7 +234,28 @@ pub fn postorder<'a, 'tcx>(
|
||||
reverse_postorder(body).rev()
|
||||
}
|
||||
|
||||
/// Reverse postorder traversal of a graph
|
||||
/// Returns an iterator over all basic blocks reachable from the `START_BLOCK` in no particular
|
||||
/// order.
|
||||
///
|
||||
/// This is clearer than writing `preorder` in cases where the order doesn't matter.
|
||||
pub fn reachable<'a, 'tcx>(
|
||||
body: &'a Body<'tcx>,
|
||||
) -> impl 'a + Iterator<Item = (BasicBlock, &'a BasicBlockData<'tcx>)> {
|
||||
preorder(body)
|
||||
}
|
||||
|
||||
/// Returns a `BitSet` containing all basic blocks reachable from the `START_BLOCK`.
|
||||
pub fn reachable_as_bitset(body: &Body<'_>) -> BitSet<BasicBlock> {
|
||||
let mut iter = preorder(body);
|
||||
iter.by_ref().for_each(drop);
|
||||
iter.visited
|
||||
}
|
||||
|
||||
/// Reverse postorder traversal of a graph.
|
||||
///
|
||||
/// This function creates an iterator over the `Body`'s basic blocks, that:
|
||||
/// - returns basic blocks in a reverse postorder,
|
||||
/// - makes use of the `BasicBlocks` CFG cache's reverse postorder.
|
||||
///
|
||||
/// Reverse postorder is the reverse order of a postorder traversal.
|
||||
/// This is different to a preorder traversal and represents a natural
|
||||
@@ -246,65 +275,6 @@ pub fn postorder<'a, 'tcx>(
|
||||
/// A reverse postorder traversal of this graph is either `A B C D` or `A C B D`
|
||||
/// Note that for a graph containing no loops (i.e., A DAG), this is equivalent to
|
||||
/// a topological sort.
|
||||
///
|
||||
/// Construction of a `ReversePostorder` traversal requires doing a full
|
||||
/// postorder traversal of the graph, therefore this traversal should be
|
||||
/// constructed as few times as possible. Use the `reset` method to be able
|
||||
/// to re-use the traversal
|
||||
#[derive(Clone)]
|
||||
pub struct ReversePostorder<'a, 'tcx> {
|
||||
body: &'a Body<'tcx>,
|
||||
blocks: Vec<BasicBlock>,
|
||||
idx: usize,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ReversePostorder<'a, 'tcx> {
|
||||
pub fn new(body: &'a Body<'tcx>, root: BasicBlock) -> ReversePostorder<'a, 'tcx> {
|
||||
let blocks: Vec<_> = Postorder::new(&body.basic_blocks, root).map(|(bb, _)| bb).collect();
|
||||
let len = blocks.len();
|
||||
ReversePostorder { body, blocks, idx: len }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Iterator for ReversePostorder<'a, 'tcx> {
|
||||
type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
|
||||
|
||||
fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
|
||||
if self.idx == 0 {
|
||||
return None;
|
||||
}
|
||||
self.idx -= 1;
|
||||
|
||||
self.blocks.get(self.idx).map(|&bb| (bb, &self.body[bb]))
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(self.idx, Some(self.idx))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ExactSizeIterator for ReversePostorder<'a, 'tcx> {}
|
||||
|
||||
/// Returns an iterator over all basic blocks reachable from the `START_BLOCK` in no particular
|
||||
/// order.
|
||||
///
|
||||
/// This is clearer than writing `preorder` in cases where the order doesn't matter.
|
||||
pub fn reachable<'a, 'tcx>(
|
||||
body: &'a Body<'tcx>,
|
||||
) -> impl 'a + Iterator<Item = (BasicBlock, &'a BasicBlockData<'tcx>)> {
|
||||
preorder(body)
|
||||
}
|
||||
|
||||
/// Returns a `BitSet` containing all basic blocks reachable from the `START_BLOCK`.
|
||||
pub fn reachable_as_bitset(body: &Body<'_>) -> BitSet<BasicBlock> {
|
||||
let mut iter = preorder(body);
|
||||
(&mut iter).for_each(drop);
|
||||
iter.visited
|
||||
}
|
||||
|
||||
/// Creates an iterator over the `Body`'s basic blocks, that:
|
||||
/// - returns basic blocks in a reverse postorder,
|
||||
/// - makes use of the `BasicBlocks` CFG cache's reverse postorder.
|
||||
pub fn reverse_postorder<'a, 'tcx>(
|
||||
body: &'a Body<'tcx>,
|
||||
) -> impl Iterator<Item = (BasicBlock, &'a BasicBlockData<'tcx>)> + ExactSizeIterator + DoubleEndedIterator
|
||||
|
||||
@@ -740,9 +740,7 @@ fn non_exhaustive_match<'p, 'tcx>(
|
||||
));
|
||||
}
|
||||
} else if ty == cx.tcx.types.str_ {
|
||||
err.note(format!(
|
||||
"`&str` cannot be matched exhaustively, so a wildcard `_` is necessary",
|
||||
));
|
||||
err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary");
|
||||
} else if cx.is_foreign_non_exhaustive_enum(ty) {
|
||||
err.note(format!("`{ty}` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively"));
|
||||
}
|
||||
|
||||
@@ -194,6 +194,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
|
||||
D: DropElaborator<'b, 'tcx>,
|
||||
'tcx: 'b,
|
||||
{
|
||||
#[instrument(level = "trace", skip(self), ret)]
|
||||
fn place_ty(&self, place: Place<'tcx>) -> Ty<'tcx> {
|
||||
place.ty(self.elaborator.body(), self.tcx()).ty
|
||||
}
|
||||
@@ -220,11 +221,9 @@ fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
//
|
||||
// FIXME: I think we should just control the flags externally,
|
||||
// and then we do not need this machinery.
|
||||
#[instrument(level = "debug")]
|
||||
pub fn elaborate_drop(&mut self, bb: BasicBlock) {
|
||||
debug!("elaborate_drop({:?}, {:?})", bb, self);
|
||||
let style = self.elaborator.drop_style(self.path, DropFlagMode::Deep);
|
||||
debug!("elaborate_drop({:?}, {:?}): live - {:?}", bb, self, style);
|
||||
match style {
|
||||
match self.elaborator.drop_style(self.path, DropFlagMode::Deep) {
|
||||
DropStyle::Dead => {
|
||||
self.elaborator
|
||||
.patch()
|
||||
|
||||
@@ -763,7 +763,7 @@ pub(super) fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span>
|
||||
// and `_1` is the `Place` for `somenum`.
|
||||
//
|
||||
// If and when the Issue is resolved, remove this special case match pattern:
|
||||
StatementKind::FakeRead(box (cause, _)) if cause == FakeReadCause::ForGuardBinding => None,
|
||||
StatementKind::FakeRead(box (FakeReadCause::ForGuardBinding, _)) => None,
|
||||
|
||||
// Retain spans from all other statements
|
||||
StatementKind::FakeRead(box (_, _)) // Not including `ForGuardBinding`
|
||||
|
||||
@@ -170,6 +170,7 @@ fn param_env(&self) -> ty::ParamEnv<'tcx> {
|
||||
self.ctxt.param_env()
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle {
|
||||
let ((maybe_live, maybe_dead), multipart) = match mode {
|
||||
DropFlagMode::Shallow => (self.ctxt.init_data.maybe_live_dead(path), false),
|
||||
|
||||
@@ -54,11 +54,8 @@ fn candidate<'tcx>(
|
||||
let layout = tcx.layout_of(param_env.and(ty)).ok()?;
|
||||
let variants = match &layout.variants {
|
||||
Variants::Single { .. } => return None,
|
||||
Variants::Multiple { tag_encoding, .. }
|
||||
if matches!(tag_encoding, TagEncoding::Niche { .. }) =>
|
||||
{
|
||||
return None;
|
||||
}
|
||||
Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, .. } => return None,
|
||||
|
||||
Variants::Multiple { variants, .. } if variants.len() <= 1 => return None,
|
||||
Variants::Multiple { variants, .. } => variants,
|
||||
};
|
||||
|
||||
@@ -480,6 +480,7 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
let passes: &[&dyn MirPass<'tcx>] = &[
|
||||
// These next passes must be executed together
|
||||
&add_call_guards::CriticalCallEdges,
|
||||
&reveal_all::RevealAll, // has to be done before drop elaboration, since we need to drop opaque types, too.
|
||||
&elaborate_drops::ElaborateDrops,
|
||||
// This will remove extraneous landing pads which are no longer
|
||||
// necessary as well as well as forcing any call in a non-unwinding
|
||||
@@ -526,7 +527,6 @@ fn o1<T>(x: T) -> WithMinOptLevel<T> {
|
||||
body,
|
||||
&[
|
||||
&check_alignment::CheckAlignment,
|
||||
&reveal_all::RevealAll, // has to be done before inlining, since inlined code is in RevealAll mode.
|
||||
&lower_slice_len::LowerSliceLenCalls, // has to be done before inlining, otherwise actual call will be almost always inlined. Also simple, so can just do first
|
||||
&unreachable_prop::UnreachablePropagation,
|
||||
&uninhabited_enum_branching::UninhabitedEnumBranching,
|
||||
|
||||
@@ -8,16 +8,7 @@
|
||||
pub struct RevealAll;
|
||||
|
||||
impl<'tcx> MirPass<'tcx> for RevealAll {
|
||||
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
|
||||
sess.mir_opt_level() >= 3 || super::inline::Inline.is_enabled(sess)
|
||||
}
|
||||
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
// Do not apply this transformation to generators.
|
||||
if body.generator.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
|
||||
RevealAllVisitor { tcx, param_env }.visit_body_preserves_cfg(body);
|
||||
}
|
||||
@@ -34,6 +25,29 @@ fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_place(
|
||||
&mut self,
|
||||
place: &mut Place<'tcx>,
|
||||
_context: PlaceContext,
|
||||
_location: Location,
|
||||
) {
|
||||
// Performance optimization: don't reintern if there is no `OpaqueCast` to remove.
|
||||
if place.projection.iter().all(|elem| !matches!(elem, ProjectionElem::OpaqueCast(_))) {
|
||||
return;
|
||||
}
|
||||
// `OpaqueCast` projections are only needed if there are opaque types on which projections are performed.
|
||||
// After the `RevealAll` pass, all opaque types are replaced with their hidden types, so we don't need these
|
||||
// projections anymore.
|
||||
place.projection = self.tcx.mk_place_elems(
|
||||
&place
|
||||
.projection
|
||||
.into_iter()
|
||||
.filter(|elem| !matches!(elem, ProjectionElem::OpaqueCast(_)))
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_constant(&mut self, constant: &mut ConstOperand<'tcx>, _: Location) {
|
||||
// We have to use `try_normalize_erasing_regions` here, since it's
|
||||
|
||||
@@ -78,14 +78,10 @@ pub fn new<'tcx>(body: &Body<'tcx>) -> SsaLocals {
|
||||
visitor.assignments[local] = Set1::One(LocationExtended::Arg);
|
||||
}
|
||||
|
||||
if body.basic_blocks.len() > 2 {
|
||||
for (bb, data) in traversal::reverse_postorder(body) {
|
||||
visitor.visit_basic_block_data(bb, data);
|
||||
}
|
||||
} else {
|
||||
for (bb, data) in body.basic_blocks.iter_enumerated() {
|
||||
visitor.visit_basic_block_data(bb, data);
|
||||
}
|
||||
// For SSA assignments, a RPO visit will see the assignment before it sees any use.
|
||||
// We only visit reachable nodes: computing `dominates` on an unreachable node ICEs.
|
||||
for (bb, data) in traversal::reverse_postorder(body) {
|
||||
visitor.visit_basic_block_data(bb, data);
|
||||
}
|
||||
|
||||
for var_debug_info in &body.var_debug_info {
|
||||
|
||||
@@ -509,7 +509,7 @@ parse_maybe_fn_typo_with_impl = you might have meant to write `impl` instead of
|
||||
|
||||
parse_maybe_recover_from_bad_qpath_stage_2 =
|
||||
missing angle brackets in associated item path
|
||||
.suggestion = try: `{$ty}`
|
||||
.suggestion = types that don't start with an identifier need to be surrounded with angle brackets in qualified paths
|
||||
|
||||
parse_maybe_recover_from_bad_type_plus =
|
||||
expected a path on the left-hand side of `+`, not `{$ty}`
|
||||
|
||||
@@ -59,9 +59,18 @@ pub(crate) enum BadTypePlusSub {
|
||||
#[diag(parse_maybe_recover_from_bad_qpath_stage_2)]
|
||||
pub(crate) struct BadQPathStage2 {
|
||||
#[primary_span]
|
||||
#[suggestion(code = "", applicability = "maybe-incorrect")]
|
||||
pub span: Span,
|
||||
pub ty: String,
|
||||
#[subdiagnostic]
|
||||
pub wrap: WrapType,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
|
||||
pub(crate) struct WrapType {
|
||||
#[suggestion_part(code = "<")]
|
||||
pub lo: Span,
|
||||
#[suggestion_part(code = ">")]
|
||||
pub hi: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens,
|
||||
StructLiteralNeedingParensSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma,
|
||||
TernaryOperator, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
|
||||
UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead,
|
||||
UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType,
|
||||
};
|
||||
|
||||
use crate::fluent_generated as fluent;
|
||||
@@ -1589,10 +1589,9 @@ pub(super) fn maybe_recover_from_bad_qpath_stage_2<T: RecoverQPath>(
|
||||
self.parse_path_segments(&mut path.segments, T::PATH_STYLE, None)?;
|
||||
path.span = ty_span.to(self.prev_token.span);
|
||||
|
||||
let ty_str = self.span_to_snippet(ty_span).unwrap_or_else(|_| pprust::ty_to_string(&ty));
|
||||
self.sess.emit_err(BadQPathStage2 {
|
||||
span: path.span,
|
||||
ty: format!("<{}>::{}", ty_str, pprust::path_to_string(&path)),
|
||||
span: ty_span,
|
||||
wrap: WrapType { lo: ty_span.shrink_to_lo(), hi: ty_span.shrink_to_hi() },
|
||||
});
|
||||
|
||||
let path_span = ty_span.shrink_to_hi(); // Use an empty path since `position == 0`.
|
||||
|
||||
@@ -4140,6 +4140,12 @@ fn resolve_anon_const_manual(
|
||||
});
|
||||
}
|
||||
|
||||
fn resolve_expr_field(&mut self, f: &'ast ExprField, e: &'ast Expr) {
|
||||
self.resolve_expr(&f.expr, Some(e));
|
||||
self.visit_ident(f.ident);
|
||||
walk_list!(self, visit_attribute, f.attrs.iter());
|
||||
}
|
||||
|
||||
fn resolve_expr(&mut self, expr: &'ast Expr, parent: Option<&'ast Expr>) {
|
||||
// First, record candidate traits for this expression if it could
|
||||
// result in the invocation of a method call.
|
||||
@@ -4155,7 +4161,19 @@ fn resolve_expr(&mut self, expr: &'ast Expr, parent: Option<&'ast Expr>) {
|
||||
|
||||
ExprKind::Struct(ref se) => {
|
||||
self.smart_resolve_path(expr.id, &se.qself, &se.path, PathSource::Struct);
|
||||
visit::walk_expr(self, expr);
|
||||
// This is the same as `visit::walk_expr(self, expr);`, but we want to pass the
|
||||
// parent in for accurate suggestions when encountering `Foo { bar }` that should
|
||||
// have been `Foo { bar: self.bar }`.
|
||||
if let Some(qself) = &se.qself {
|
||||
self.visit_ty(&qself.ty);
|
||||
}
|
||||
self.visit_path(&se.path, expr.id);
|
||||
walk_list!(self, resolve_expr_field, &se.fields, expr);
|
||||
match &se.rest {
|
||||
StructRest::Base(expr) => self.visit_expr(expr),
|
||||
StructRest::Rest(_span) => {}
|
||||
StructRest::None => {}
|
||||
}
|
||||
}
|
||||
|
||||
ExprKind::Break(Some(label), _) | ExprKind::Continue(Some(label)) => {
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
/// A field or associated item from self type suggested in case of resolution failure.
|
||||
enum AssocSuggestion {
|
||||
Field,
|
||||
Field(Span),
|
||||
MethodWithSelf { called: bool },
|
||||
AssocFn { called: bool },
|
||||
AssocType,
|
||||
@@ -51,7 +51,7 @@ enum AssocSuggestion {
|
||||
impl AssocSuggestion {
|
||||
fn action(&self) -> &'static str {
|
||||
match self {
|
||||
AssocSuggestion::Field => "use the available field",
|
||||
AssocSuggestion::Field(_) => "use the available field",
|
||||
AssocSuggestion::MethodWithSelf { called: true } => {
|
||||
"call the method with the fully-qualified path"
|
||||
}
|
||||
@@ -186,7 +186,7 @@ fn make_base_error(
|
||||
fallback_label: format!("not a {expected}"),
|
||||
span,
|
||||
span_label: match res {
|
||||
Res::Def(kind, def_id) if kind == DefKind::TyParam => {
|
||||
Res::Def(DefKind::TyParam, def_id) => {
|
||||
Some((self.r.def_span(def_id), "found this type parameter"))
|
||||
}
|
||||
_ => None,
|
||||
@@ -215,7 +215,8 @@ fn make_base_error(
|
||||
}
|
||||
} else {
|
||||
let mut span_label = None;
|
||||
let item_span = path.last().unwrap().ident.span;
|
||||
let item_ident = path.last().unwrap().ident;
|
||||
let item_span = item_ident.span;
|
||||
let (mod_prefix, mod_str, module, suggestion) = if path.len() == 1 {
|
||||
debug!(?self.diagnostic_metadata.current_impl_items);
|
||||
debug!(?self.diagnostic_metadata.current_function);
|
||||
@@ -231,9 +232,35 @@ fn make_base_error(
|
||||
})
|
||||
{
|
||||
let sp = item_span.shrink_to_lo();
|
||||
|
||||
// Account for `Foo { field }` when suggesting `self.field` so we result on
|
||||
// `Foo { field: self.field }`.
|
||||
let field = match source {
|
||||
PathSource::Expr(Some(Expr { kind: ExprKind::Struct(expr), .. })) => {
|
||||
expr.fields.iter().find(|f| f.ident == item_ident)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let pre = if let Some(field) = field && field.is_shorthand {
|
||||
format!("{item_ident}: ")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
// Ensure we provide a structured suggestion for an assoc fn only for
|
||||
// expressions that are actually a fn call.
|
||||
let is_call = match field {
|
||||
Some(ast::ExprField { expr, .. }) => {
|
||||
matches!(expr.kind, ExprKind::Call(..))
|
||||
}
|
||||
_ => matches!(
|
||||
source,
|
||||
PathSource::Expr(Some(Expr { kind: ExprKind::Call(..), ..})),
|
||||
),
|
||||
};
|
||||
|
||||
match &item.kind {
|
||||
AssocItemKind::Fn(fn_)
|
||||
if !sig.decl.has_self() && fn_.sig.decl.has_self() => {
|
||||
if (!sig.decl.has_self() || !is_call) && fn_.sig.decl.has_self() => {
|
||||
// Ensure that we only suggest `self.` if `self` is available,
|
||||
// you can't call `fn foo(&self)` from `fn bar()` (#115992).
|
||||
// We also want to mention that the method exists.
|
||||
@@ -243,20 +270,28 @@ fn make_base_error(
|
||||
));
|
||||
None
|
||||
}
|
||||
AssocItemKind::Fn(fn_)
|
||||
if !fn_.sig.decl.has_self() && !is_call => {
|
||||
span_label = Some((
|
||||
item.ident.span,
|
||||
"an associated function by that name is available on `Self` here",
|
||||
));
|
||||
None
|
||||
}
|
||||
AssocItemKind::Fn(fn_) if fn_.sig.decl.has_self() => Some((
|
||||
sp,
|
||||
"consider using the method on `Self`",
|
||||
"self.".to_string(),
|
||||
format!("{pre}self."),
|
||||
)),
|
||||
AssocItemKind::Fn(_) => Some((
|
||||
sp,
|
||||
"consider using the associated function on `Self`",
|
||||
"Self::".to_string(),
|
||||
format!("{pre}Self::"),
|
||||
)),
|
||||
AssocItemKind::Const(..) => Some((
|
||||
sp,
|
||||
"consider using the associated constant on `Self`",
|
||||
"Self::".to_string(),
|
||||
format!("{pre}Self::"),
|
||||
)),
|
||||
_ => None
|
||||
}
|
||||
@@ -621,17 +656,30 @@ fn try_lookup_name_relaxed(
|
||||
self.lookup_assoc_candidate(ident, ns, is_expected, source.is_call())
|
||||
{
|
||||
let self_is_available = self.self_value_is_available(path[0].ident.span);
|
||||
// Account for `Foo { field }` when suggesting `self.field` so we result on
|
||||
// `Foo { field: self.field }`.
|
||||
let pre = match source {
|
||||
PathSource::Expr(Some(Expr { kind: ExprKind::Struct(expr), .. }))
|
||||
if expr
|
||||
.fields
|
||||
.iter()
|
||||
.any(|f| f.ident == path[0].ident && f.is_shorthand) =>
|
||||
{
|
||||
format!("{path_str}: ")
|
||||
}
|
||||
_ => String::new(),
|
||||
};
|
||||
match candidate {
|
||||
AssocSuggestion::Field => {
|
||||
AssocSuggestion::Field(field_span) => {
|
||||
if self_is_available {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
err.span_suggestion_verbose(
|
||||
span.shrink_to_lo(),
|
||||
"you might have meant to use the available field",
|
||||
format!("self.{path_str}"),
|
||||
format!("{pre}self."),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
err.span_label(span, "a field by this name exists in `Self`");
|
||||
err.span_label(field_span, "a field by that name exists in `Self`");
|
||||
}
|
||||
}
|
||||
AssocSuggestion::MethodWithSelf { called } if self_is_available => {
|
||||
@@ -640,10 +688,10 @@ fn try_lookup_name_relaxed(
|
||||
} else {
|
||||
"you might have meant to refer to the method"
|
||||
};
|
||||
err.span_suggestion(
|
||||
span,
|
||||
err.span_suggestion_verbose(
|
||||
span.shrink_to_lo(),
|
||||
msg,
|
||||
format!("self.{path_str}"),
|
||||
"self.".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
@@ -651,10 +699,10 @@ fn try_lookup_name_relaxed(
|
||||
| AssocSuggestion::AssocFn { .. }
|
||||
| AssocSuggestion::AssocConst
|
||||
| AssocSuggestion::AssocType => {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
err.span_suggestion_verbose(
|
||||
span.shrink_to_lo(),
|
||||
format!("you might have meant to {}", candidate.action()),
|
||||
format!("Self::{path_str}"),
|
||||
"Self::".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
@@ -1667,11 +1715,11 @@ fn extract_node_id(t: &Ty) -> Option<NodeId> {
|
||||
resolution.full_res()
|
||||
{
|
||||
if let Some(field_ids) = self.r.field_def_ids(did) {
|
||||
if field_ids
|
||||
if let Some(field_id) = field_ids
|
||||
.iter()
|
||||
.any(|&field_id| ident.name == self.r.tcx.item_name(field_id))
|
||||
.find(|&&field_id| ident.name == self.r.tcx.item_name(field_id))
|
||||
{
|
||||
return Some(AssocSuggestion::Field);
|
||||
return Some(AssocSuggestion::Field(self.r.def_span(*field_id)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,10 @@ pub fn impl_def(&mut self, did: DefId) -> stable_mir::ty::ImplDef {
|
||||
stable_mir::ty::ImplDef(self.create_def_id(did))
|
||||
}
|
||||
|
||||
pub fn region_def(&mut self, did: DefId) -> stable_mir::ty::RegionDef {
|
||||
stable_mir::ty::RegionDef(self.create_def_id(did))
|
||||
}
|
||||
|
||||
pub fn prov(&mut self, aid: AllocId) -> stable_mir::ty::Prov {
|
||||
stable_mir::ty::Prov(self.create_alloc_id(aid))
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
//!
|
||||
//! For now, we are developing everything inside `rustc`, thus, we keep this module private.
|
||||
|
||||
use hir::def::DefKind;
|
||||
use crate::rustc_smir::hir::def::DefKind;
|
||||
use crate::rustc_smir::stable_mir::ty::{BoundRegion, EarlyBoundRegion, Region};
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::{alloc_range, AllocId};
|
||||
@@ -1500,9 +1501,39 @@ fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
|
||||
impl<'tcx> Stable<'tcx> for ty::Region<'tcx> {
|
||||
type T = stable_mir::ty::Region;
|
||||
|
||||
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
|
||||
// FIXME: add a real implementation of stable regions
|
||||
opaque(self)
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
Region { kind: self.kind().stable(tables) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for ty::RegionKind<'tcx> {
|
||||
type T = stable_mir::ty::RegionKind;
|
||||
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
use stable_mir::ty::RegionKind;
|
||||
match self {
|
||||
ty::ReEarlyBound(early_reg) => RegionKind::ReEarlyBound(EarlyBoundRegion {
|
||||
def_id: tables.region_def(early_reg.def_id),
|
||||
index: early_reg.index,
|
||||
name: early_reg.name.to_string(),
|
||||
}),
|
||||
ty::ReLateBound(db_index, bound_reg) => RegionKind::ReLateBound(
|
||||
db_index.as_u32(),
|
||||
BoundRegion { var: bound_reg.var.as_u32(), kind: bound_reg.kind.stable(tables) },
|
||||
),
|
||||
ty::ReStatic => RegionKind::ReStatic,
|
||||
ty::RePlaceholder(place_holder) => {
|
||||
RegionKind::RePlaceholder(stable_mir::ty::Placeholder {
|
||||
universe: place_holder.universe.as_u32(),
|
||||
bound: BoundRegion {
|
||||
var: place_holder.bound.var.as_u32(),
|
||||
kind: place_holder.bound.kind.stable(tables),
|
||||
},
|
||||
})
|
||||
}
|
||||
ty::ReErased => RegionKind::ReErased,
|
||||
_ => unreachable!("{self:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1753,7 +1753,7 @@ pub fn original_relative_byte_pos(&self, pos: BytePos) -> RelativeBytePos {
|
||||
// is recorded.
|
||||
let diff = match self.normalized_pos.binary_search_by(|np| np.pos.cmp(&pos)) {
|
||||
Ok(i) => self.normalized_pos[i].diff,
|
||||
Err(i) if i == 0 => 0,
|
||||
Err(0) => 0,
|
||||
Err(i) => self.normalized_pos[i - 1].diff,
|
||||
};
|
||||
|
||||
@@ -1775,7 +1775,7 @@ pub fn normalized_byte_pos(&self, offset: u32) -> BytePos {
|
||||
.binary_search_by(|np| (np.pos.0 + np.diff).cmp(&(self.start_pos.0 + offset)))
|
||||
{
|
||||
Ok(i) => self.normalized_pos[i].diff,
|
||||
Err(i) if i == 0 => 0,
|
||||
Err(0) => 0,
|
||||
Err(i) => self.normalized_pos[i - 1].diff,
|
||||
};
|
||||
|
||||
|
||||
@@ -1465,6 +1465,7 @@
|
||||
simd_shl,
|
||||
simd_shr,
|
||||
simd_shuffle,
|
||||
simd_shuffle_generic,
|
||||
simd_sub,
|
||||
simd_trunc,
|
||||
simd_xor,
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
//! This module both handles the global cache which stores "finished" goals,
|
||||
//! and the provisional cache which contains partially computed goals.
|
||||
//!
|
||||
//! The provisional cache is necessary when dealing with coinductive cycles.
|
||||
//!
|
||||
//! For more information about the provisional cache and coinduction in general,
|
||||
//! check out the relevant section of the rustc-dev-guide.
|
||||
//!
|
||||
//! FIXME(@lcnr): Write that section, feel free to ping me if you need help here
|
||||
//! before then or if I still haven't done that before January 2023.
|
||||
use super::StackDepth;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::traits::solve::{CanonicalInput, QueryResult};
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
pub struct EntryIndex {}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(super) struct ProvisionalEntry<'tcx> {
|
||||
/// In case we have a coinductive cycle, this is the
|
||||
/// the current provisional result of this goal.
|
||||
///
|
||||
/// This starts out as `None` for all goals and gets to some
|
||||
/// when the goal gets popped from the stack or we rerun evaluation
|
||||
/// for this goal to reach a fixpoint.
|
||||
pub(super) response: Option<QueryResult<'tcx>>,
|
||||
/// In case of a cycle, the position of deepest stack entry involved
|
||||
/// in that cycle. This is monotonically decreasing in the stack as all
|
||||
/// elements between the current stack element in the deepest stack entry
|
||||
/// involved have to also be involved in that cycle.
|
||||
///
|
||||
/// We can only move entries to the global cache once we're complete done
|
||||
/// with the cycle. If this entry has not been involved in a cycle,
|
||||
/// this is just its own depth.
|
||||
pub(super) depth: StackDepth,
|
||||
|
||||
/// The goal for this entry. Should always be equal to the corresponding goal
|
||||
/// in the lookup table.
|
||||
pub(super) input: CanonicalInput<'tcx>,
|
||||
}
|
||||
|
||||
pub(super) struct ProvisionalCache<'tcx> {
|
||||
pub(super) entries: IndexVec<EntryIndex, ProvisionalEntry<'tcx>>,
|
||||
// FIXME: This is only used to quickly check whether a given goal
|
||||
// is in the cache. We should experiment with using something like
|
||||
// `SsoHashSet` here because in most cases there are only a few entries.
|
||||
pub(super) lookup_table: FxHashMap<CanonicalInput<'tcx>, EntryIndex>,
|
||||
}
|
||||
|
||||
impl<'tcx> ProvisionalCache<'tcx> {
|
||||
pub(super) fn empty() -> ProvisionalCache<'tcx> {
|
||||
ProvisionalCache { entries: Default::default(), lookup_table: Default::default() }
|
||||
}
|
||||
|
||||
pub(super) fn is_empty(&self) -> bool {
|
||||
self.entries.is_empty() && self.lookup_table.is_empty()
|
||||
}
|
||||
|
||||
/// Adds a dependency from the current leaf to `target` in the cache
|
||||
/// to prevent us from moving any goals which depend on the current leaf
|
||||
/// to the global cache while we're still computing `target`.
|
||||
///
|
||||
/// Its important to note that `target` may already be part of a different cycle.
|
||||
/// In this case we have to ensure that we also depend on all other goals
|
||||
/// in the existing cycle in addition to the potentially direct cycle with `target`.
|
||||
pub(super) fn add_dependency_of_leaf_on(&mut self, target: EntryIndex) {
|
||||
let depth = self.entries[target].depth;
|
||||
for provisional_entry in &mut self.entries.raw[target.index()..] {
|
||||
// The depth of `target` is the position of the deepest goal in the stack
|
||||
// on which `target` depends. That goal is the `root` of this cycle.
|
||||
//
|
||||
// Any entry which was added after `target` is either on the stack itself
|
||||
// at which point its depth is definitely at least as high as the depth of
|
||||
// `root`. If it's not on the stack itself it has to depend on a goal
|
||||
// between `root` and `leaf`. If it were to depend on a goal deeper in the
|
||||
// stack than `root`, then `root` would also depend on that goal, at which
|
||||
// point `root` wouldn't be the root anymore.
|
||||
debug_assert!(provisional_entry.depth >= depth);
|
||||
provisional_entry.depth = depth;
|
||||
}
|
||||
|
||||
// We only update entries which were added after `target` as no other
|
||||
// entry should have a higher depth.
|
||||
//
|
||||
// Any entry which previously had a higher depth than target has to
|
||||
// be between `target` and `root`. Because of this we would have updated
|
||||
// its depth when calling `add_dependency_of_leaf_on(root)` for `target`.
|
||||
if cfg!(debug_assertions) {
|
||||
self.entries.iter().all(|e| e.depth <= depth);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn depth(&self, entry_index: EntryIndex) -> StackDepth {
|
||||
self.entries[entry_index].depth
|
||||
}
|
||||
|
||||
pub(super) fn provisional_result(&self, entry_index: EntryIndex) -> Option<QueryResult<'tcx>> {
|
||||
self.entries[entry_index].response
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
mod cache;
|
||||
|
||||
use self::cache::ProvisionalEntry;
|
||||
use super::inspect;
|
||||
use super::inspect::ProofTreeBuilder;
|
||||
use super::SolverMode;
|
||||
use cache::ProvisionalCache;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_index::Idx;
|
||||
use rustc_index::IndexVec;
|
||||
@@ -27,8 +24,14 @@ struct StackEntry<'tcx> {
|
||||
// The maximum depth reached by this stack entry, only up-to date
|
||||
// for the top of the stack and lazily updated for the rest.
|
||||
reached_depth: StackDepth,
|
||||
// In case of a cycle, the depth of the root.
|
||||
cycle_root_depth: StackDepth,
|
||||
|
||||
encountered_overflow: bool,
|
||||
has_been_used: bool,
|
||||
/// Starts out as `None` and gets set when rerunning this
|
||||
/// goal in case we encounter a cycle.
|
||||
provisional_result: Option<QueryResult<'tcx>>,
|
||||
|
||||
/// We put only the root goal of a coinductive cycle into the global cache.
|
||||
///
|
||||
@@ -47,7 +50,7 @@ pub(super) struct SearchGraph<'tcx> {
|
||||
///
|
||||
/// An element is *deeper* in the stack if its index is *lower*.
|
||||
stack: IndexVec<StackDepth, StackEntry<'tcx>>,
|
||||
provisional_cache: ProvisionalCache<'tcx>,
|
||||
stack_entries: FxHashMap<CanonicalInput<'tcx>, StackDepth>,
|
||||
}
|
||||
|
||||
impl<'tcx> SearchGraph<'tcx> {
|
||||
@@ -56,7 +59,7 @@ pub(super) fn new(tcx: TyCtxt<'tcx>, mode: SolverMode) -> SearchGraph<'tcx> {
|
||||
mode,
|
||||
local_overflow_limit: tcx.recursion_limit().0.checked_ilog2().unwrap_or(0) as usize,
|
||||
stack: Default::default(),
|
||||
provisional_cache: ProvisionalCache::empty(),
|
||||
stack_entries: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +88,7 @@ fn on_cache_hit(&mut self, additional_depth: usize, encountered_overflow: bool)
|
||||
/// would cause us to not track overflow and recursion depth correctly.
|
||||
fn pop_stack(&mut self) -> StackEntry<'tcx> {
|
||||
let elem = self.stack.pop().unwrap();
|
||||
assert!(self.stack_entries.remove(&elem.input).is_some());
|
||||
if let Some(last) = self.stack.raw.last_mut() {
|
||||
last.reached_depth = last.reached_depth.max(elem.reached_depth);
|
||||
last.encountered_overflow |= elem.encountered_overflow;
|
||||
@@ -104,22 +108,17 @@ pub(super) fn global_cache(&self, tcx: TyCtxt<'tcx>) -> &'tcx EvaluationCache<'t
|
||||
}
|
||||
|
||||
pub(super) fn is_empty(&self) -> bool {
|
||||
self.stack.is_empty() && self.provisional_cache.is_empty()
|
||||
self.stack.is_empty()
|
||||
}
|
||||
|
||||
/// Whether we're currently in a cycle. This should only be used
|
||||
/// for debug assertions.
|
||||
pub(super) fn in_cycle(&self) -> bool {
|
||||
if let Some(stack_depth) = self.stack.last_index() {
|
||||
// Either the current goal on the stack is the root of a cycle...
|
||||
if self.stack[stack_depth].has_been_used {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ...or it depends on a goal with a lower depth.
|
||||
let current_goal = self.stack[stack_depth].input;
|
||||
let entry_index = self.provisional_cache.lookup_table[¤t_goal];
|
||||
self.provisional_cache.entries[entry_index].depth != stack_depth
|
||||
// Either the current goal on the stack is the root of a cycle
|
||||
// or it depends on a goal with a lower depth.
|
||||
self.stack[stack_depth].has_been_used
|
||||
|| self.stack[stack_depth].cycle_root_depth != stack_depth
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@@ -211,9 +210,8 @@ pub(super) fn with_new_goal(
|
||||
}
|
||||
}
|
||||
|
||||
// Look at the provisional cache to detect cycles.
|
||||
let cache = &mut self.provisional_cache;
|
||||
match cache.lookup_table.entry(input) {
|
||||
// Check whether we're in a cycle.
|
||||
match self.stack_entries.entry(input) {
|
||||
// No entry, we push this goal on the stack and try to prove it.
|
||||
Entry::Vacant(v) => {
|
||||
let depth = self.stack.next_index();
|
||||
@@ -221,14 +219,14 @@ pub(super) fn with_new_goal(
|
||||
input,
|
||||
available_depth,
|
||||
reached_depth: depth,
|
||||
cycle_root_depth: depth,
|
||||
encountered_overflow: false,
|
||||
has_been_used: false,
|
||||
provisional_result: None,
|
||||
cycle_participants: Default::default(),
|
||||
};
|
||||
assert_eq!(self.stack.push(entry), depth);
|
||||
let entry_index =
|
||||
cache.entries.push(ProvisionalEntry { response: None, depth, input });
|
||||
v.insert(entry_index);
|
||||
v.insert(depth);
|
||||
}
|
||||
// We have a nested goal which relies on a goal `root` deeper in the stack.
|
||||
//
|
||||
@@ -239,41 +237,50 @@ pub(super) fn with_new_goal(
|
||||
//
|
||||
// Finally we can return either the provisional response for that goal if we have a
|
||||
// coinductive cycle or an ambiguous result if the cycle is inductive.
|
||||
Entry::Occupied(entry_index) => {
|
||||
Entry::Occupied(entry) => {
|
||||
inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::CacheHit(
|
||||
CacheHit::Provisional,
|
||||
));
|
||||
|
||||
let entry_index = *entry_index.get();
|
||||
let stack_depth = cache.depth(entry_index);
|
||||
let stack_depth = *entry.get();
|
||||
debug!("encountered cycle with depth {stack_depth:?}");
|
||||
|
||||
cache.add_dependency_of_leaf_on(entry_index);
|
||||
let mut iter = self.stack.iter_mut();
|
||||
let root = iter.nth(stack_depth.as_usize()).unwrap();
|
||||
for e in iter {
|
||||
root.cycle_participants.insert(e.input);
|
||||
// We start by updating the root depth of all cycle participants, and
|
||||
// add all cycle participants to the root.
|
||||
let root_depth = self.stack[stack_depth].cycle_root_depth;
|
||||
let (prev, participants) = self.stack.raw.split_at_mut(stack_depth.as_usize() + 1);
|
||||
let root = &mut prev[root_depth.as_usize()];
|
||||
for entry in participants {
|
||||
debug_assert!(entry.cycle_root_depth >= root_depth);
|
||||
entry.cycle_root_depth = root_depth;
|
||||
root.cycle_participants.insert(entry.input);
|
||||
// FIXME(@lcnr): I believe that this line is needed as we could
|
||||
// otherwise access a cache entry for the root of a cycle while
|
||||
// computing the result for a cycle participant. This can result
|
||||
// in unstable results due to incompleteness.
|
||||
//
|
||||
// However, a test for this would be an even more complex version of
|
||||
// tests/ui/traits/new-solver/coinduction/incompleteness-unstable-result.rs.
|
||||
// I did not bother to write such a test and we have no regression test
|
||||
// for this. It would be good to have such a test :)
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
root.cycle_participants.extend(entry.cycle_participants.drain());
|
||||
}
|
||||
|
||||
// If we're in a cycle, we have to retry proving the current goal
|
||||
// until we reach a fixpoint.
|
||||
// If we're in a cycle, we have to retry proving the cycle head
|
||||
// until we reach a fixpoint. It is not enough to simply retry the
|
||||
// `root` goal of this cycle.
|
||||
//
|
||||
// See tests/ui/traits/new-solver/cycles/fixpoint-rerun-all-cycle-heads.rs
|
||||
// for an example.
|
||||
self.stack[stack_depth].has_been_used = true;
|
||||
return if let Some(result) = cache.provisional_result(entry_index) {
|
||||
return if let Some(result) = self.stack[stack_depth].provisional_result {
|
||||
result
|
||||
} else {
|
||||
// If we don't have a provisional result yet, the goal has to
|
||||
// still be on the stack.
|
||||
let mut goal_on_stack = false;
|
||||
let mut is_coinductive = true;
|
||||
for entry in self.stack.raw[stack_depth.index()..]
|
||||
// If we don't have a provisional result yet we're in the first iteration,
|
||||
// so we start with no constraints.
|
||||
let is_coinductive = self.stack.raw[stack_depth.index()..]
|
||||
.iter()
|
||||
.skip_while(|entry| entry.input != input)
|
||||
{
|
||||
goal_on_stack = true;
|
||||
is_coinductive &= entry.input.value.goal.predicate.is_coinductive(tcx);
|
||||
}
|
||||
debug_assert!(goal_on_stack);
|
||||
|
||||
.all(|entry| entry.input.value.goal.predicate.is_coinductive(tcx));
|
||||
if is_coinductive {
|
||||
Self::response_no_constraints(tcx, input, Certainty::Yes)
|
||||
} else {
|
||||
@@ -294,40 +301,25 @@ pub(super) fn with_new_goal(
|
||||
// of the previous iteration is equal to the final result, at which
|
||||
// point we are done.
|
||||
for _ in 0..self.local_overflow_limit() {
|
||||
let response = prove_goal(self, inspect);
|
||||
let result = prove_goal(self, inspect);
|
||||
|
||||
// Check whether the current goal is the root of a cycle and whether
|
||||
// we have to rerun because its provisional result differed from the
|
||||
// final result.
|
||||
//
|
||||
// Also update the response for this goal stored in the provisional
|
||||
// cache.
|
||||
let stack_entry = self.pop_stack();
|
||||
debug_assert_eq!(stack_entry.input, input);
|
||||
let cache = &mut self.provisional_cache;
|
||||
let provisional_entry_index =
|
||||
*cache.lookup_table.get(&stack_entry.input).unwrap();
|
||||
let provisional_entry = &mut cache.entries[provisional_entry_index];
|
||||
if stack_entry.has_been_used
|
||||
&& provisional_entry.response.map_or(true, |r| r != response)
|
||||
&& stack_entry.provisional_result.map_or(true, |r| r != result)
|
||||
{
|
||||
// If so, update the provisional result for this goal and remove
|
||||
// all entries whose result depends on this goal from the provisional
|
||||
// cache...
|
||||
//
|
||||
// That's not completely correct, as a nested goal can also only
|
||||
// depend on a goal which is lower in the stack so it doesn't
|
||||
// actually depend on the current goal. This should be fairly
|
||||
// rare and is hopefully not relevant for performance.
|
||||
provisional_entry.response = Some(response);
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
cache.lookup_table.retain(|_key, index| *index <= provisional_entry_index);
|
||||
cache.entries.truncate(provisional_entry_index.index() + 1);
|
||||
|
||||
// ...and finally push our goal back on the stack and reevaluate it.
|
||||
self.stack.push(StackEntry { has_been_used: false, ..stack_entry });
|
||||
// If so, update its provisional result and reevaluate it.
|
||||
let depth = self.stack.push(StackEntry {
|
||||
has_been_used: false,
|
||||
provisional_result: Some(result),
|
||||
..stack_entry
|
||||
});
|
||||
assert_eq!(self.stack_entries.insert(input, depth), None);
|
||||
} else {
|
||||
return (stack_entry, response);
|
||||
return (stack_entry, result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,17 +335,7 @@ pub(super) fn with_new_goal(
|
||||
//
|
||||
// It is not possible for any nested goal to depend on something deeper on the
|
||||
// stack, as this would have also updated the depth of the current goal.
|
||||
let cache = &mut self.provisional_cache;
|
||||
let provisional_entry_index = *cache.lookup_table.get(&input).unwrap();
|
||||
let provisional_entry = &mut cache.entries[provisional_entry_index];
|
||||
let depth = provisional_entry.depth;
|
||||
if depth == self.stack.next_index() {
|
||||
for (i, entry) in cache.entries.drain_enumerated(provisional_entry_index.index()..) {
|
||||
let actual_index = cache.lookup_table.remove(&entry.input);
|
||||
debug_assert_eq!(Some(i), actual_index);
|
||||
debug_assert!(entry.depth == depth);
|
||||
}
|
||||
|
||||
if final_entry.cycle_root_depth == self.stack.next_index() {
|
||||
// When encountering a cycle, both inductive and coinductive, we only
|
||||
// move the root into the global cache. We also store all other cycle
|
||||
// participants involved.
|
||||
@@ -371,8 +353,6 @@ pub(super) fn with_new_goal(
|
||||
dep_node,
|
||||
result,
|
||||
)
|
||||
} else {
|
||||
provisional_entry.response = Some(result);
|
||||
}
|
||||
|
||||
result
|
||||
|
||||
@@ -3211,7 +3211,7 @@ fn report_opaque_type_auto_trait_leakage(
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
let name = match self.tcx.opaque_type_origin(def_id.expect_local()) {
|
||||
hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => {
|
||||
format!("opaque type")
|
||||
"opaque type".to_string()
|
||||
}
|
||||
hir::OpaqueTyOrigin::TyAlias { .. } => {
|
||||
format!("`{}`", self.tcx.def_path_debug_str(def_id))
|
||||
|
||||
@@ -4,17 +4,20 @@
|
||||
|
||||
use super::ty::{
|
||||
Allocation, Binder, Const, ConstDef, ConstantKind, ExistentialPredicate, FnSig, GenericArgKind,
|
||||
GenericArgs, Promoted, RigidTy, TermKind, Ty, TyKind, UnevaluatedConst,
|
||||
GenericArgs, Promoted, Region, RigidTy, TermKind, Ty, TyKind, UnevaluatedConst,
|
||||
};
|
||||
|
||||
pub trait Folder: Sized {
|
||||
type Break;
|
||||
fn visit_ty(&mut self, ty: &Ty) -> ControlFlow<Self::Break, Ty> {
|
||||
fn fold_ty(&mut self, ty: &Ty) -> ControlFlow<Self::Break, Ty> {
|
||||
ty.super_fold(self)
|
||||
}
|
||||
fn fold_const(&mut self, c: &Const) -> ControlFlow<Self::Break, Const> {
|
||||
c.super_fold(self)
|
||||
}
|
||||
fn fold_reg(&mut self, reg: &Region) -> ControlFlow<Self::Break, Region> {
|
||||
reg.super_fold(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Foldable: Sized + Clone {
|
||||
@@ -26,7 +29,7 @@ fn fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
|
||||
|
||||
impl Foldable for Ty {
|
||||
fn fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
|
||||
folder.visit_ty(self)
|
||||
folder.fold_ty(self)
|
||||
}
|
||||
fn super_fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
|
||||
let mut kind = self.kind();
|
||||
@@ -81,7 +84,7 @@ fn super_fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
|
||||
|
||||
impl Foldable for ConstDef {
|
||||
fn super_fold<V: Folder>(&self, _folder: &mut V) -> ControlFlow<V::Break, Self> {
|
||||
ControlFlow::Continue(self.clone())
|
||||
ControlFlow::Continue(*self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +99,7 @@ fn super_fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
|
||||
|
||||
impl Foldable for Promoted {
|
||||
fn super_fold<V: Folder>(&self, _folder: &mut V) -> ControlFlow<V::Break, Self> {
|
||||
ControlFlow::Continue(self.clone())
|
||||
ControlFlow::Continue(*self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +109,15 @@ fn super_fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Foldable for Region {
|
||||
fn fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
|
||||
folder.fold_reg(self)
|
||||
}
|
||||
fn super_fold<V: Folder>(&self, _: &mut V) -> ControlFlow<V::Break, Self> {
|
||||
ControlFlow::Continue(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Foldable for GenericArgKind {
|
||||
fn super_fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
|
||||
let mut this = self.clone();
|
||||
@@ -136,7 +148,10 @@ fn super_fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
|
||||
}
|
||||
RigidTy::Slice(inner) => *inner = inner.fold(folder)?,
|
||||
RigidTy::RawPtr(ty, _) => *ty = ty.fold(folder)?,
|
||||
RigidTy::Ref(_, ty, _) => *ty = ty.fold(folder)?,
|
||||
RigidTy::Ref(reg, ty, _) => {
|
||||
*reg = reg.fold(folder)?;
|
||||
*ty = ty.fold(folder)?
|
||||
}
|
||||
RigidTy::FnDef(_, args) => *args = args.fold(folder)?,
|
||||
RigidTy::FnPtr(sig) => *sig = sig.fold(folder)?,
|
||||
RigidTy::Closure(_, args) => *args = args.fold(folder)?,
|
||||
@@ -214,7 +229,7 @@ pub enum Never {}
|
||||
impl Folder for GenericArgs {
|
||||
type Break = Never;
|
||||
|
||||
fn visit_ty(&mut self, ty: &Ty) -> ControlFlow<Self::Break, Ty> {
|
||||
fn fold_ty(&mut self, ty: &Ty) -> ControlFlow<Self::Break, Ty> {
|
||||
ControlFlow::Continue(match ty.kind() {
|
||||
TyKind::Param(p) => self[p],
|
||||
_ => *ty,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::{
|
||||
mir::Safety,
|
||||
mir::{Body, Mutability},
|
||||
with, AllocId, DefId,
|
||||
with, AllocId, DefId, Symbol,
|
||||
};
|
||||
use crate::Opaque;
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
@@ -34,7 +34,46 @@ pub struct Const {
|
||||
}
|
||||
|
||||
type Ident = Opaque;
|
||||
pub type Region = Opaque;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Region {
|
||||
pub kind: RegionKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RegionKind {
|
||||
ReEarlyBound(EarlyBoundRegion),
|
||||
ReLateBound(DebruijnIndex, BoundRegion),
|
||||
ReStatic,
|
||||
RePlaceholder(Placeholder<BoundRegion>),
|
||||
ReErased,
|
||||
}
|
||||
|
||||
pub(crate) type DebruijnIndex = u32;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EarlyBoundRegion {
|
||||
pub def_id: RegionDef,
|
||||
pub index: u32,
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
pub(crate) type BoundVar = u32;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BoundRegion {
|
||||
pub var: BoundVar,
|
||||
pub kind: BoundRegionKind,
|
||||
}
|
||||
|
||||
pub(crate) type UniverseIndex = u32;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Placeholder<T> {
|
||||
pub universe: UniverseIndex,
|
||||
pub bound: T,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Span(pub usize);
|
||||
|
||||
@@ -152,6 +191,9 @@ pub fn body(&self) -> Body {
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct ImplDef(pub DefId);
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct RegionDef(pub DefId);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GenericArgs(pub Vec<GenericArgKind>);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
use super::ty::{
|
||||
Allocation, Binder, Const, ConstDef, ExistentialPredicate, FnSig, GenericArgKind, GenericArgs,
|
||||
Promoted, RigidTy, TermKind, Ty, UnevaluatedConst,
|
||||
Promoted, Region, RigidTy, TermKind, Ty, UnevaluatedConst,
|
||||
};
|
||||
|
||||
pub trait Visitor: Sized {
|
||||
@@ -15,6 +15,9 @@ fn visit_ty(&mut self, ty: &Ty) -> ControlFlow<Self::Break> {
|
||||
fn visit_const(&mut self, c: &Const) -> ControlFlow<Self::Break> {
|
||||
c.super_visit(self)
|
||||
}
|
||||
fn visit_reg(&mut self, reg: &Region) -> ControlFlow<Self::Break> {
|
||||
reg.super_visit(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Visitable {
|
||||
@@ -101,6 +104,16 @@ fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Visitable for Region {
|
||||
fn visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
|
||||
visitor.visit_reg(self)
|
||||
}
|
||||
|
||||
fn super_visit<V: Visitor>(&self, _: &mut V) -> ControlFlow<V::Break> {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Visitable for GenericArgKind {
|
||||
fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
|
||||
match self {
|
||||
@@ -128,7 +141,10 @@ fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
|
||||
}
|
||||
RigidTy::Slice(inner) => inner.visit(visitor),
|
||||
RigidTy::RawPtr(ty, _) => ty.visit(visitor),
|
||||
RigidTy::Ref(_, ty, _) => ty.visit(visitor),
|
||||
RigidTy::Ref(reg, ty, _) => {
|
||||
reg.visit(visitor);
|
||||
ty.visit(visitor)
|
||||
}
|
||||
RigidTy::FnDef(_, args) => args.visit(visitor),
|
||||
RigidTy::FnPtr(sig) => sig.visit(visitor),
|
||||
RigidTy::Closure(_, args) => args.visit(visitor),
|
||||
|
||||
@@ -2408,6 +2408,27 @@ fn from(t: T) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[stable(feature = "shared_from_array", since = "CURRENT_RUSTC_VERSION")]
|
||||
impl<T, const N: usize> From<[T; N]> for Rc<[T]> {
|
||||
/// Converts a [`[T; N]`](prim@array) into an `Rc<[T]>`.
|
||||
///
|
||||
/// The conversion moves the array into a newly allocated `Rc`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use std::rc::Rc;
|
||||
/// let original: [i32; 3] = [1, 2, 3];
|
||||
/// let shared: Rc<[i32]> = Rc::from(original);
|
||||
/// assert_eq!(&[1, 2, 3], &shared[..]);
|
||||
/// ```
|
||||
#[inline]
|
||||
fn from(v: [T; N]) -> Rc<[T]> {
|
||||
Rc::<[T; N]>::from(v)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[stable(feature = "shared_from_slice", since = "1.21.0")]
|
||||
impl<T: Clone> From<&[T]> for Rc<[T]> {
|
||||
|
||||
@@ -1618,7 +1618,7 @@ pub fn downgrade(this: &Self) -> Weak<T, A>
|
||||
#[must_use]
|
||||
#[stable(feature = "arc_counts", since = "1.15.0")]
|
||||
pub fn weak_count(this: &Self) -> usize {
|
||||
let cnt = this.inner().weak.load(Acquire);
|
||||
let cnt = this.inner().weak.load(Relaxed);
|
||||
// If the weak count is currently locked, the value of the
|
||||
// count was 0 just before taking the lock.
|
||||
if cnt == usize::MAX { 0 } else { cnt - 1 }
|
||||
@@ -1648,7 +1648,7 @@ pub fn weak_count(this: &Self) -> usize {
|
||||
#[must_use]
|
||||
#[stable(feature = "arc_counts", since = "1.15.0")]
|
||||
pub fn strong_count(this: &Self) -> usize {
|
||||
this.inner().strong.load(Acquire)
|
||||
this.inner().strong.load(Relaxed)
|
||||
}
|
||||
|
||||
/// Increments the strong reference count on the `Arc<T>` associated with the
|
||||
@@ -2803,7 +2803,7 @@ fn checked_increment(n: usize) -> Option<usize> {
|
||||
#[must_use]
|
||||
#[stable(feature = "weak_counts", since = "1.41.0")]
|
||||
pub fn strong_count(&self) -> usize {
|
||||
if let Some(inner) = self.inner() { inner.strong.load(Acquire) } else { 0 }
|
||||
if let Some(inner) = self.inner() { inner.strong.load(Relaxed) } else { 0 }
|
||||
}
|
||||
|
||||
/// Gets an approximation of the number of `Weak` pointers pointing to this
|
||||
@@ -2822,7 +2822,7 @@ pub fn strong_count(&self) -> usize {
|
||||
pub fn weak_count(&self) -> usize {
|
||||
if let Some(inner) = self.inner() {
|
||||
let weak = inner.weak.load(Acquire);
|
||||
let strong = inner.strong.load(Acquire);
|
||||
let strong = inner.strong.load(Relaxed);
|
||||
if strong == 0 {
|
||||
0
|
||||
} else {
|
||||
@@ -3269,6 +3269,27 @@ fn from(t: T) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[stable(feature = "shared_from_array", since = "CURRENT_RUSTC_VERSION")]
|
||||
impl<T, const N: usize> From<[T; N]> for Arc<[T]> {
|
||||
/// Converts a [`[T; N]`](prim@array) into an `Arc<[T]>`.
|
||||
///
|
||||
/// The conversion moves the array into a newly allocated `Arc`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use std::sync::Arc;
|
||||
/// let original: [i32; 3] = [1, 2, 3];
|
||||
/// let shared: Arc<[i32]> = Arc::from(original);
|
||||
/// assert_eq!(&[1, 2, 3], &shared[..]);
|
||||
/// ```
|
||||
#[inline]
|
||||
fn from(v: [T; N]) -> Arc<[T]> {
|
||||
Arc::<[T; N]>::from(v)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[stable(feature = "shared_from_slice", since = "1.21.0")]
|
||||
impl<T: Clone> From<&[T]> for Arc<[T]> {
|
||||
|
||||
@@ -3154,6 +3154,36 @@ fn from(s: &mut [T]) -> Vec<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[stable(feature = "vec_from_array_ref", since = "CURRENT_RUSTC_VERSION")]
|
||||
impl<T: Clone, const N: usize> From<&[T; N]> for Vec<T> {
|
||||
/// Allocate a `Vec<T>` and fill it by cloning `s`'s items.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// assert_eq!(Vec::from(&[1, 2, 3]), vec![1, 2, 3]);
|
||||
/// ```
|
||||
fn from(s: &[T; N]) -> Vec<T> {
|
||||
Self::from(s.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[stable(feature = "vec_from_array_ref", since = "CURRENT_RUSTC_VERSION")]
|
||||
impl<T: Clone, const N: usize> From<&mut [T; N]> for Vec<T> {
|
||||
/// Allocate a `Vec<T>` and fill it by cloning `s`'s items.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// assert_eq!(Vec::from(&mut [1, 2, 3]), vec![1, 2, 3]);
|
||||
/// ```
|
||||
fn from(s: &mut [T; N]) -> Vec<T> {
|
||||
Self::from(s.as_mut_slice())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[stable(feature = "vec_from_array", since = "1.44.0")]
|
||||
impl<T, const N: usize> From<[T; N]> for Vec<T> {
|
||||
|
||||
@@ -2562,3 +2562,13 @@ unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
|
||||
// Ensure all ZSTs have been freed.
|
||||
assert!(alloc.state.borrow().0.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_from_array_ref() {
|
||||
assert_eq!(Vec::from(&[1, 2, 3]), vec![1, 2, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_from_array_mut_ref() {
|
||||
assert_eq!(Vec::from(&mut [1, 2, 3]), vec![1, 2, 3]);
|
||||
}
|
||||
|
||||
@@ -291,9 +291,9 @@ fn ne(&self, other: &Rhs) -> bool {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_diagnostic_item = "Eq"]
|
||||
pub trait Eq: PartialEq<Self> {
|
||||
// this method is used solely by #[deriving] to assert
|
||||
// that every component of a type implements #[deriving]
|
||||
// itself, the current deriving infrastructure means doing this
|
||||
// this method is used solely by #[derive(Eq)] to assert
|
||||
// that every component of a type implements `Eq`
|
||||
// itself. The current deriving infrastructure means doing this
|
||||
// assertion without using a method on this trait is nearly
|
||||
// impossible.
|
||||
//
|
||||
|
||||
@@ -112,9 +112,9 @@ pub trait Write {
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an instance of [`Error`] on error.
|
||||
/// This function will return an instance of [`std::fmt::Error`][Error] on error.
|
||||
///
|
||||
/// The purpose of std::fmt::Error is to abort the formatting operation when the underlying
|
||||
/// The purpose of that error is to abort the formatting operation when the underlying
|
||||
/// destination encounters some error preventing it from accepting more text; it should
|
||||
/// generally be propagated rather than handled, at least when implementing formatting traits.
|
||||
///
|
||||
|
||||
@@ -178,6 +178,7 @@
|
||||
#![feature(ip)]
|
||||
#![feature(ip_bits)]
|
||||
#![feature(is_ascii_octdigit)]
|
||||
#![feature(isqrt)]
|
||||
#![feature(maybe_uninit_uninit_array)]
|
||||
#![feature(ptr_alignment_type)]
|
||||
#![feature(ptr_metadata)]
|
||||
|
||||
@@ -898,6 +898,30 @@ pub const fn checked_pow(self, mut exp: u32) -> Option<Self> {
|
||||
acc.checked_mul(base)
|
||||
}
|
||||
|
||||
/// Returns the square root of the number, rounded down.
|
||||
///
|
||||
/// Returns `None` if `self` is negative.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
/// ```
|
||||
/// #![feature(isqrt)]
|
||||
#[doc = concat!("assert_eq!(10", stringify!($SelfT), ".checked_isqrt(), Some(3));")]
|
||||
/// ```
|
||||
#[unstable(feature = "isqrt", issue = "116226")]
|
||||
#[rustc_const_unstable(feature = "isqrt", issue = "116226")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
pub const fn checked_isqrt(self) -> Option<Self> {
|
||||
if self < 0 {
|
||||
None
|
||||
} else {
|
||||
Some((self as $UnsignedT).isqrt() as Self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Saturating integer addition. Computes `self + rhs`, saturating at the numeric
|
||||
/// bounds instead of overflowing.
|
||||
///
|
||||
@@ -2061,6 +2085,36 @@ pub const fn pow(self, mut exp: u32) -> Self {
|
||||
acc * base
|
||||
}
|
||||
|
||||
/// Returns the square root of the number, rounded down.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if `self` is negative.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
/// ```
|
||||
/// #![feature(isqrt)]
|
||||
#[doc = concat!("assert_eq!(10", stringify!($SelfT), ".isqrt(), 3);")]
|
||||
/// ```
|
||||
#[unstable(feature = "isqrt", issue = "116226")]
|
||||
#[rustc_const_unstable(feature = "isqrt", issue = "116226")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
pub const fn isqrt(self) -> Self {
|
||||
// I would like to implement it as
|
||||
// ```
|
||||
// self.checked_isqrt().expect("argument of integer square root must be non-negative")
|
||||
// ```
|
||||
// but `expect` is not yet stable as a `const fn`.
|
||||
match self.checked_isqrt() {
|
||||
Some(sqrt) => sqrt,
|
||||
None => panic!("argument of integer square root must be non-negative"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the quotient of Euclidean division of `self` by `rhs`.
|
||||
///
|
||||
/// This computes the integer `q` such that `self = q * rhs + r`, with
|
||||
|
||||
@@ -1995,6 +1995,54 @@ pub const fn pow(self, mut exp: u32) -> Self {
|
||||
acc * base
|
||||
}
|
||||
|
||||
/// Returns the square root of the number, rounded down.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
/// ```
|
||||
/// #![feature(isqrt)]
|
||||
#[doc = concat!("assert_eq!(10", stringify!($SelfT), ".isqrt(), 3);")]
|
||||
/// ```
|
||||
#[unstable(feature = "isqrt", issue = "116226")]
|
||||
#[rustc_const_unstable(feature = "isqrt", issue = "116226")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
pub const fn isqrt(self) -> Self {
|
||||
if self < 2 {
|
||||
return self;
|
||||
}
|
||||
|
||||
// The algorithm is based on the one presented in
|
||||
// <https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_(base_2)>
|
||||
// which cites as source the following C code:
|
||||
// <https://web.archive.org/web/20120306040058/http://medialab.freaknet.org/martin/src/sqrt/sqrt.c>.
|
||||
|
||||
let mut op = self;
|
||||
let mut res = 0;
|
||||
let mut one = 1 << (self.ilog2() & !1);
|
||||
|
||||
while one != 0 {
|
||||
if op >= res + one {
|
||||
op -= res + one;
|
||||
res = (res >> 1) + one;
|
||||
} else {
|
||||
res >>= 1;
|
||||
}
|
||||
one >>= 2;
|
||||
}
|
||||
|
||||
// SAFETY: the result is positive and fits in an integer with half as many bits.
|
||||
// Inform the optimizer about it.
|
||||
unsafe {
|
||||
intrinsics::assume(0 < res);
|
||||
intrinsics::assume(res < 1 << (Self::BITS / 2));
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
/// Performs Euclidean division.
|
||||
///
|
||||
/// Since, for the positive integers, all common
|
||||
|
||||
@@ -607,7 +607,16 @@ pub fn mask(self, mask: usize) -> *const T {
|
||||
/// Calculates the distance between two pointers. The returned value is in
|
||||
/// units of T: the distance in bytes divided by `mem::size_of::<T>()`.
|
||||
///
|
||||
/// This function is the inverse of [`offset`].
|
||||
/// This is equivalent to `(self as isize - origin as isize) / (mem::size_of::<T>() as isize)`,
|
||||
/// except that it has a lot more opportunities for UB, in exchange for the compiler
|
||||
/// better understanding what you are doing.
|
||||
///
|
||||
/// The primary motivation of this method is for computing the `len` of an array/slice
|
||||
/// of `T` that you are currently representing as a "start" and "end" pointer
|
||||
/// (and "end" is "one past the end" of the array).
|
||||
/// In that case, `end.offset_from(start)` gets you the length of the array.
|
||||
///
|
||||
/// All of the following safety requirements are trivially satisfied for this usecase.
|
||||
///
|
||||
/// [`offset`]: #method.offset
|
||||
///
|
||||
@@ -616,7 +625,7 @@ pub fn mask(self, mask: usize) -> *const T {
|
||||
/// If any of the following conditions are violated, the result is Undefined
|
||||
/// Behavior:
|
||||
///
|
||||
/// * Both the starting and other pointer must be either in bounds or one
|
||||
/// * Both `self` and `origin` must be either in bounds or one
|
||||
/// byte past the end of the same [allocated object].
|
||||
///
|
||||
/// * Both pointers must be *derived from* a pointer to the same object.
|
||||
@@ -646,6 +655,14 @@ pub fn mask(self, mask: usize) -> *const T {
|
||||
/// (Note that [`offset`] and [`add`] also have a similar limitation and hence cannot be used on
|
||||
/// such large allocations either.)
|
||||
///
|
||||
/// The requirement for pointers to be derived from the same allocated object is primarily
|
||||
/// needed for `const`-compatibility: the distance between pointers into *different* allocated
|
||||
/// objects is not known at compile-time. However, the requirement also exists at
|
||||
/// runtime and may be exploited by optimizations. If you wish to compute the difference between
|
||||
/// pointers that are not guaranteed to be from the same allocation, use `(self as isize -
|
||||
/// origin as isize) / mem::size_of::<T>()`.
|
||||
// FIXME: recommend `addr()` instead of `as usize` once that is stable.
|
||||
///
|
||||
/// [`add`]: #method.add
|
||||
/// [allocated object]: crate::ptr#allocated-object
|
||||
///
|
||||
@@ -703,7 +720,7 @@ pub fn mask(self, mask: usize) -> *const T {
|
||||
/// units of **bytes**.
|
||||
///
|
||||
/// This is purely a convenience for casting to a `u8` pointer and
|
||||
/// using [offset_from][pointer::offset_from] on it. See that method for
|
||||
/// using [`offset_from`][pointer::offset_from] on it. See that method for
|
||||
/// documentation and safety requirements.
|
||||
///
|
||||
/// For non-`Sized` pointees this operation considers only the data pointers,
|
||||
|
||||
@@ -781,7 +781,16 @@ pub const fn guaranteed_ne(self, other: *mut T) -> Option<bool>
|
||||
/// Calculates the distance between two pointers. The returned value is in
|
||||
/// units of T: the distance in bytes divided by `mem::size_of::<T>()`.
|
||||
///
|
||||
/// This function is the inverse of [`offset`].
|
||||
/// This is equivalent to `(self as isize - origin as isize) / (mem::size_of::<T>() as isize)`,
|
||||
/// except that it has a lot more opportunities for UB, in exchange for the compiler
|
||||
/// better understanding what you are doing.
|
||||
///
|
||||
/// The primary motivation of this method is for computing the `len` of an array/slice
|
||||
/// of `T` that you are currently representing as a "start" and "end" pointer
|
||||
/// (and "end" is "one past the end" of the array).
|
||||
/// In that case, `end.offset_from(start)` gets you the length of the array.
|
||||
///
|
||||
/// All of the following safety requirements are trivially satisfied for this usecase.
|
||||
///
|
||||
/// [`offset`]: pointer#method.offset-1
|
||||
///
|
||||
@@ -790,7 +799,7 @@ pub const fn guaranteed_ne(self, other: *mut T) -> Option<bool>
|
||||
/// If any of the following conditions are violated, the result is Undefined
|
||||
/// Behavior:
|
||||
///
|
||||
/// * Both the starting and other pointer must be either in bounds or one
|
||||
/// * Both `self` and `origin` must be either in bounds or one
|
||||
/// byte past the end of the same [allocated object].
|
||||
///
|
||||
/// * Both pointers must be *derived from* a pointer to the same object.
|
||||
@@ -820,6 +829,14 @@ pub const fn guaranteed_ne(self, other: *mut T) -> Option<bool>
|
||||
/// (Note that [`offset`] and [`add`] also have a similar limitation and hence cannot be used on
|
||||
/// such large allocations either.)
|
||||
///
|
||||
/// The requirement for pointers to be derived from the same allocated object is primarily
|
||||
/// needed for `const`-compatibility: the distance between pointers into *different* allocated
|
||||
/// objects is not known at compile-time. However, the requirement also exists at
|
||||
/// runtime and may be exploited by optimizations. If you wish to compute the difference between
|
||||
/// pointers that are not guaranteed to be from the same allocation, use `(self as isize -
|
||||
/// origin as isize) / mem::size_of::<T>()`.
|
||||
// FIXME: recommend `addr()` instead of `as usize` once that is stable.
|
||||
///
|
||||
/// [`add`]: #method.add
|
||||
/// [allocated object]: crate::ptr#allocated-object
|
||||
///
|
||||
@@ -875,7 +892,7 @@ pub const fn guaranteed_ne(self, other: *mut T) -> Option<bool>
|
||||
/// units of **bytes**.
|
||||
///
|
||||
/// This is purely a convenience for casting to a `u8` pointer and
|
||||
/// using [offset_from][pointer::offset_from] on it. See that method for
|
||||
/// using [`offset_from`][pointer::offset_from] on it. See that method for
|
||||
/// documentation and safety requirements.
|
||||
///
|
||||
/// For non-`Sized` pointees this operation considers only the data pointers,
|
||||
|
||||
@@ -3410,7 +3410,7 @@ pub fn rotate_left(&mut self, mid: usize) {
|
||||
/// assert_eq!(a, ['e', 'f', 'a', 'b', 'c', 'd']);
|
||||
/// ```
|
||||
///
|
||||
/// Rotate a subslice:
|
||||
/// Rotating a subslice:
|
||||
///
|
||||
/// ```
|
||||
/// let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
use core::fmt;
|
||||
use core::future::Future;
|
||||
use core::marker::Tuple;
|
||||
use core::ops::{Generator, GeneratorState};
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
@@ -168,10 +170,52 @@ fn from(t: T) -> Self {
|
||||
}
|
||||
|
||||
#[unstable(feature = "exclusive_wrapper", issue = "98407")]
|
||||
impl<T: Future + ?Sized> Future for Exclusive<T> {
|
||||
impl<F, Args> FnOnce<Args> for Exclusive<F>
|
||||
where
|
||||
F: FnOnce<Args>,
|
||||
Args: Tuple,
|
||||
{
|
||||
type Output = F::Output;
|
||||
|
||||
extern "rust-call" fn call_once(self, args: Args) -> Self::Output {
|
||||
self.into_inner().call_once(args)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "exclusive_wrapper", issue = "98407")]
|
||||
impl<F, Args> FnMut<Args> for Exclusive<F>
|
||||
where
|
||||
F: FnMut<Args>,
|
||||
Args: Tuple,
|
||||
{
|
||||
extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output {
|
||||
self.get_mut().call_mut(args)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "exclusive_wrapper", issue = "98407")]
|
||||
impl<T> Future for Exclusive<T>
|
||||
where
|
||||
T: Future + ?Sized,
|
||||
{
|
||||
type Output = T::Output;
|
||||
|
||||
#[inline]
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
self.get_pin_mut().poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "generator_trait", issue = "43122")] // also #98407
|
||||
impl<R, G> Generator<R> for Exclusive<G>
|
||||
where
|
||||
G: Generator<R> + ?Sized,
|
||||
{
|
||||
type Yield = G::Yield;
|
||||
type Return = G::Return;
|
||||
|
||||
#[inline]
|
||||
fn resume(self: Pin<&mut Self>, arg: R) -> GeneratorState<Self::Yield, Self::Return> {
|
||||
G::resume(self.get_pin_mut(), arg)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
#![feature(min_specialization)]
|
||||
#![feature(numfmt)]
|
||||
#![feature(num_midpoint)]
|
||||
#![feature(isqrt)]
|
||||
#![feature(step_trait)]
|
||||
#![feature(str_internals)]
|
||||
#![feature(std_internals)]
|
||||
|
||||
@@ -290,6 +290,38 @@ fn test_pow() {
|
||||
assert_eq!(r.saturating_pow(0), 1 as $T);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_isqrt() {
|
||||
assert_eq!($T::MIN.checked_isqrt(), None);
|
||||
assert_eq!((-1 as $T).checked_isqrt(), None);
|
||||
assert_eq!((0 as $T).isqrt(), 0 as $T);
|
||||
assert_eq!((1 as $T).isqrt(), 1 as $T);
|
||||
assert_eq!((2 as $T).isqrt(), 1 as $T);
|
||||
assert_eq!((99 as $T).isqrt(), 9 as $T);
|
||||
assert_eq!((100 as $T).isqrt(), 10 as $T);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))] // Miri is too slow
|
||||
#[test]
|
||||
fn test_lots_of_isqrt() {
|
||||
let n_max: $T = (1024 * 1024).min($T::MAX as u128) as $T;
|
||||
for n in 0..=n_max {
|
||||
let isqrt: $T = n.isqrt();
|
||||
|
||||
assert!(isqrt.pow(2) <= n);
|
||||
let (square, overflow) = (isqrt + 1).overflowing_pow(2);
|
||||
assert!(overflow || square > n);
|
||||
}
|
||||
|
||||
for n in ($T::MAX - 127)..=$T::MAX {
|
||||
let isqrt: $T = n.isqrt();
|
||||
|
||||
assert!(isqrt.pow(2) <= n);
|
||||
let (square, overflow) = (isqrt + 1).overflowing_pow(2);
|
||||
assert!(overflow || square > n);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_floor() {
|
||||
let a: $T = 8;
|
||||
|
||||
@@ -206,6 +206,35 @@ fn test_pow() {
|
||||
assert_eq!(r.saturating_pow(2), MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_isqrt() {
|
||||
assert_eq!((0 as $T).isqrt(), 0 as $T);
|
||||
assert_eq!((1 as $T).isqrt(), 1 as $T);
|
||||
assert_eq!((2 as $T).isqrt(), 1 as $T);
|
||||
assert_eq!((99 as $T).isqrt(), 9 as $T);
|
||||
assert_eq!((100 as $T).isqrt(), 10 as $T);
|
||||
assert_eq!($T::MAX.isqrt(), (1 << ($T::BITS / 2)) - 1);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))] // Miri is too slow
|
||||
#[test]
|
||||
fn test_lots_of_isqrt() {
|
||||
let n_max: $T = (1024 * 1024).min($T::MAX as u128) as $T;
|
||||
for n in 0..=n_max {
|
||||
let isqrt: $T = n.isqrt();
|
||||
|
||||
assert!(isqrt.pow(2) <= n);
|
||||
assert!(isqrt + 1 == (1 as $T) << ($T::BITS / 2) || (isqrt + 1).pow(2) > n);
|
||||
}
|
||||
|
||||
for n in ($T::MAX - 255)..=$T::MAX {
|
||||
let isqrt: $T = n.isqrt();
|
||||
|
||||
assert!(isqrt.pow(2) <= n);
|
||||
assert!(isqrt + 1 == (1 as $T) << ($T::BITS / 2) || (isqrt + 1).pow(2) > n);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_floor() {
|
||||
assert_eq!((8 as $T).div_floor(3), 2);
|
||||
|
||||
@@ -434,6 +434,20 @@ fn from(child_stdin: crate::process::ChildStdin) -> OwnedFd {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `ChildStdin` from the provided `OwnedFd`.
|
||||
///
|
||||
/// The provided file descriptor must point to a pipe
|
||||
/// with the `CLOEXEC` flag set.
|
||||
#[stable(feature = "child_stream_from_fd", since = "CURRENT_RUSTC_VERSION")]
|
||||
impl From<OwnedFd> for process::ChildStdin {
|
||||
#[inline]
|
||||
fn from(fd: OwnedFd) -> process::ChildStdin {
|
||||
let fd = sys::fd::FileDesc::from_inner(fd);
|
||||
let pipe = sys::pipe::AnonPipe::from_inner(fd);
|
||||
process::ChildStdin::from_inner(pipe)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "io_safety", since = "1.63.0")]
|
||||
impl AsFd for crate::process::ChildStdout {
|
||||
#[inline]
|
||||
@@ -450,6 +464,20 @@ fn from(child_stdout: crate::process::ChildStdout) -> OwnedFd {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `ChildStdout` from the provided `OwnedFd`.
|
||||
///
|
||||
/// The provided file descriptor must point to a pipe
|
||||
/// with the `CLOEXEC` flag set.
|
||||
#[stable(feature = "child_stream_from_fd", since = "CURRENT_RUSTC_VERSION")]
|
||||
impl From<OwnedFd> for process::ChildStdout {
|
||||
#[inline]
|
||||
fn from(fd: OwnedFd) -> process::ChildStdout {
|
||||
let fd = sys::fd::FileDesc::from_inner(fd);
|
||||
let pipe = sys::pipe::AnonPipe::from_inner(fd);
|
||||
process::ChildStdout::from_inner(pipe)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "io_safety", since = "1.63.0")]
|
||||
impl AsFd for crate::process::ChildStderr {
|
||||
#[inline]
|
||||
@@ -466,6 +494,20 @@ fn from(child_stderr: crate::process::ChildStderr) -> OwnedFd {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `ChildStderr` from the provided `OwnedFd`.
|
||||
///
|
||||
/// The provided file descriptor must point to a pipe
|
||||
/// with the `CLOEXEC` flag set.
|
||||
#[stable(feature = "child_stream_from_fd", since = "CURRENT_RUSTC_VERSION")]
|
||||
impl From<OwnedFd> for process::ChildStderr {
|
||||
#[inline]
|
||||
fn from(fd: OwnedFd) -> process::ChildStderr {
|
||||
let fd = sys::fd::FileDesc::from_inner(fd);
|
||||
let pipe = sys::pipe::AnonPipe::from_inner(fd);
|
||||
process::ChildStderr::from_inner(pipe)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the OS-assigned process identifier associated with this process's parent.
|
||||
#[must_use]
|
||||
#[stable(feature = "unix_ppid", since = "1.27.0")]
|
||||
|
||||
@@ -116,7 +116,7 @@ pub fn try_clone_to_owned(&self) -> io::Result<OwnedSocket> {
|
||||
let mut info = unsafe { mem::zeroed::<sys::c::WSAPROTOCOL_INFOW>() };
|
||||
let result = unsafe {
|
||||
sys::c::WSADuplicateSocketW(
|
||||
self.as_raw_socket(),
|
||||
self.as_raw_socket() as sys::c::SOCKET,
|
||||
sys::c::GetCurrentProcessId(),
|
||||
&mut info,
|
||||
)
|
||||
@@ -134,7 +134,7 @@ pub fn try_clone_to_owned(&self) -> io::Result<OwnedSocket> {
|
||||
};
|
||||
|
||||
if socket != sys::c::INVALID_SOCKET {
|
||||
unsafe { Ok(OwnedSocket::from_raw_socket(socket)) }
|
||||
unsafe { Ok(OwnedSocket::from_raw_socket(socket as RawSocket)) }
|
||||
} else {
|
||||
let error = unsafe { sys::c::WSAGetLastError() };
|
||||
|
||||
@@ -158,7 +158,7 @@ pub fn try_clone_to_owned(&self) -> io::Result<OwnedSocket> {
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let socket = OwnedSocket::from_raw_socket(socket);
|
||||
let socket = OwnedSocket::from_raw_socket(socket as RawSocket);
|
||||
socket.set_no_inherit()?;
|
||||
Ok(socket)
|
||||
}
|
||||
@@ -211,7 +211,7 @@ impl Drop for OwnedSocket {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let _ = sys::c::closesocket(self.socket);
|
||||
let _ = sys::c::closesocket(self.socket as sys::c::SOCKET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +106,45 @@ fn into_raw_handle(self) -> RawHandle {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `ChildStdin` from the provided `OwnedHandle`.
|
||||
///
|
||||
/// The provided handle must be asynchronous, as reading and
|
||||
/// writing from and to it is implemented using asynchronous APIs.
|
||||
#[stable(feature = "child_stream_from_fd", since = "CURRENT_RUSTC_VERSION")]
|
||||
impl From<OwnedHandle> for process::ChildStdin {
|
||||
fn from(handle: OwnedHandle) -> process::ChildStdin {
|
||||
let handle = sys::handle::Handle::from_inner(handle);
|
||||
let pipe = sys::pipe::AnonPipe::from_inner(handle);
|
||||
process::ChildStdin::from_inner(pipe)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `ChildStdout` from the provided `OwnedHandle`.
|
||||
///
|
||||
/// The provided handle must be asynchronous, as reading and
|
||||
/// writing from and to it is implemented using asynchronous APIs.
|
||||
#[stable(feature = "child_stream_from_fd", since = "CURRENT_RUSTC_VERSION")]
|
||||
impl From<OwnedHandle> for process::ChildStdout {
|
||||
fn from(handle: OwnedHandle) -> process::ChildStdout {
|
||||
let handle = sys::handle::Handle::from_inner(handle);
|
||||
let pipe = sys::pipe::AnonPipe::from_inner(handle);
|
||||
process::ChildStdout::from_inner(pipe)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `ChildStderr` from the provided `OwnedHandle`.
|
||||
///
|
||||
/// The provided handle must be asynchronous, as reading and
|
||||
/// writing from and to it is implemented using asynchronous APIs.
|
||||
#[stable(feature = "child_stream_from_fd", since = "CURRENT_RUSTC_VERSION")]
|
||||
impl From<OwnedHandle> for process::ChildStderr {
|
||||
fn from(handle: OwnedHandle) -> process::ChildStderr {
|
||||
let handle = sys::handle::Handle::from_inner(handle);
|
||||
let pipe = sys::pipe::AnonPipe::from_inner(handle);
|
||||
process::ChildStderr::from_inner(pipe)
|
||||
}
|
||||
}
|
||||
|
||||
/// Windows-specific extensions to [`process::ExitStatus`].
|
||||
///
|
||||
/// This trait is sealed: it cannot be implemented outside the standard library.
|
||||
|
||||
+72
-74
@@ -12,9 +12,9 @@
|
||||
//! use std::process::Command;
|
||||
//!
|
||||
//! let output = Command::new("echo")
|
||||
//! .arg("Hello world")
|
||||
//! .output()
|
||||
//! .expect("Failed to execute command");
|
||||
//! .arg("Hello world")
|
||||
//! .output()
|
||||
//! .expect("Failed to execute command");
|
||||
//!
|
||||
//! assert_eq!(b"Hello world\n", output.stdout.as_slice());
|
||||
//! ```
|
||||
@@ -154,12 +154,11 @@
|
||||
/// use std::process::Command;
|
||||
///
|
||||
/// let mut child = Command::new("/bin/cat")
|
||||
/// .arg("file.txt")
|
||||
/// .spawn()
|
||||
/// .expect("failed to execute child");
|
||||
/// .arg("file.txt")
|
||||
/// .spawn()
|
||||
/// .expect("failed to execute child");
|
||||
///
|
||||
/// let ecode = child.wait()
|
||||
/// .expect("failed to wait on child");
|
||||
/// let ecode = child.wait().expect("failed to wait on child");
|
||||
///
|
||||
/// assert!(ecode.success());
|
||||
/// ```
|
||||
@@ -481,15 +480,15 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
///
|
||||
/// let output = if cfg!(target_os = "windows") {
|
||||
/// Command::new("cmd")
|
||||
/// .args(["/C", "echo hello"])
|
||||
/// .output()
|
||||
/// .expect("failed to execute process")
|
||||
/// .args(["/C", "echo hello"])
|
||||
/// .output()
|
||||
/// .expect("failed to execute process")
|
||||
/// } else {
|
||||
/// Command::new("sh")
|
||||
/// .arg("-c")
|
||||
/// .arg("echo hello")
|
||||
/// .output()
|
||||
/// .expect("failed to execute process")
|
||||
/// .arg("-c")
|
||||
/// .arg("echo hello")
|
||||
/// .output()
|
||||
/// .expect("failed to execute process")
|
||||
/// };
|
||||
///
|
||||
/// let hello = output.stdout;
|
||||
@@ -502,8 +501,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// use std::process::Command;
|
||||
///
|
||||
/// let mut echo_hello = Command::new("sh");
|
||||
/// echo_hello.arg("-c")
|
||||
/// .arg("echo hello");
|
||||
/// echo_hello.arg("-c").arg("echo hello");
|
||||
/// let hello_1 = echo_hello.output().expect("failed to execute process");
|
||||
/// let hello_2 = echo_hello.output().expect("failed to execute process");
|
||||
/// ```
|
||||
@@ -576,8 +574,8 @@ impl Command {
|
||||
/// use std::process::Command;
|
||||
///
|
||||
/// Command::new("sh")
|
||||
/// .spawn()
|
||||
/// .expect("sh command failed to start");
|
||||
/// .spawn()
|
||||
/// .expect("sh command failed to start");
|
||||
/// ```
|
||||
#[stable(feature = "process", since = "1.0.0")]
|
||||
pub fn new<S: AsRef<OsStr>>(program: S) -> Command {
|
||||
@@ -620,10 +618,10 @@ pub fn new<S: AsRef<OsStr>>(program: S) -> Command {
|
||||
/// use std::process::Command;
|
||||
///
|
||||
/// Command::new("ls")
|
||||
/// .arg("-l")
|
||||
/// .arg("-a")
|
||||
/// .spawn()
|
||||
/// .expect("ls command failed to start");
|
||||
/// .arg("-l")
|
||||
/// .arg("-a")
|
||||
/// .spawn()
|
||||
/// .expect("ls command failed to start");
|
||||
/// ```
|
||||
#[stable(feature = "process", since = "1.0.0")]
|
||||
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Command {
|
||||
@@ -650,9 +648,9 @@ pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Command {
|
||||
/// use std::process::Command;
|
||||
///
|
||||
/// Command::new("ls")
|
||||
/// .args(["-l", "-a"])
|
||||
/// .spawn()
|
||||
/// .expect("ls command failed to start");
|
||||
/// .args(["-l", "-a"])
|
||||
/// .spawn()
|
||||
/// .expect("ls command failed to start");
|
||||
/// ```
|
||||
#[stable(feature = "process", since = "1.0.0")]
|
||||
pub fn args<I, S>(&mut self, args: I) -> &mut Command
|
||||
@@ -688,9 +686,9 @@ pub fn args<I, S>(&mut self, args: I) -> &mut Command
|
||||
/// use std::process::Command;
|
||||
///
|
||||
/// Command::new("ls")
|
||||
/// .env("PATH", "/bin")
|
||||
/// .spawn()
|
||||
/// .expect("ls command failed to start");
|
||||
/// .env("PATH", "/bin")
|
||||
/// .spawn()
|
||||
/// .expect("ls command failed to start");
|
||||
/// ```
|
||||
#[stable(feature = "process", since = "1.0.0")]
|
||||
pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Command
|
||||
@@ -731,12 +729,12 @@ pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Command
|
||||
/// ).collect();
|
||||
///
|
||||
/// Command::new("printenv")
|
||||
/// .stdin(Stdio::null())
|
||||
/// .stdout(Stdio::inherit())
|
||||
/// .env_clear()
|
||||
/// .envs(&filtered_env)
|
||||
/// .spawn()
|
||||
/// .expect("printenv failed to start");
|
||||
/// .stdin(Stdio::null())
|
||||
/// .stdout(Stdio::inherit())
|
||||
/// .env_clear()
|
||||
/// .envs(&filtered_env)
|
||||
/// .spawn()
|
||||
/// .expect("printenv failed to start");
|
||||
/// ```
|
||||
#[stable(feature = "command_envs", since = "1.19.0")]
|
||||
pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Command
|
||||
@@ -772,9 +770,9 @@ pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Command
|
||||
/// use std::process::Command;
|
||||
///
|
||||
/// Command::new("ls")
|
||||
/// .env_remove("PATH")
|
||||
/// .spawn()
|
||||
/// .expect("ls command failed to start");
|
||||
/// .env_remove("PATH")
|
||||
/// .spawn()
|
||||
/// .expect("ls command failed to start");
|
||||
/// ```
|
||||
#[stable(feature = "process", since = "1.0.0")]
|
||||
pub fn env_remove<K: AsRef<OsStr>>(&mut self, key: K) -> &mut Command {
|
||||
@@ -802,9 +800,9 @@ pub fn env_remove<K: AsRef<OsStr>>(&mut self, key: K) -> &mut Command {
|
||||
/// use std::process::Command;
|
||||
///
|
||||
/// Command::new("ls")
|
||||
/// .env_clear()
|
||||
/// .spawn()
|
||||
/// .expect("ls command failed to start");
|
||||
/// .env_clear()
|
||||
/// .spawn()
|
||||
/// .expect("ls command failed to start");
|
||||
/// ```
|
||||
#[stable(feature = "process", since = "1.0.0")]
|
||||
pub fn env_clear(&mut self) -> &mut Command {
|
||||
@@ -830,9 +828,9 @@ pub fn env_clear(&mut self) -> &mut Command {
|
||||
/// use std::process::Command;
|
||||
///
|
||||
/// Command::new("ls")
|
||||
/// .current_dir("/bin")
|
||||
/// .spawn()
|
||||
/// .expect("ls command failed to start");
|
||||
/// .current_dir("/bin")
|
||||
/// .spawn()
|
||||
/// .expect("ls command failed to start");
|
||||
/// ```
|
||||
///
|
||||
/// [`canonicalize`]: crate::fs::canonicalize
|
||||
@@ -861,9 +859,9 @@ pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Command {
|
||||
/// use std::process::{Command, Stdio};
|
||||
///
|
||||
/// Command::new("ls")
|
||||
/// .stdin(Stdio::null())
|
||||
/// .spawn()
|
||||
/// .expect("ls command failed to start");
|
||||
/// .stdin(Stdio::null())
|
||||
/// .spawn()
|
||||
/// .expect("ls command failed to start");
|
||||
/// ```
|
||||
#[stable(feature = "process", since = "1.0.0")]
|
||||
pub fn stdin<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command {
|
||||
@@ -890,9 +888,9 @@ pub fn stdin<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command {
|
||||
/// use std::process::{Command, Stdio};
|
||||
///
|
||||
/// Command::new("ls")
|
||||
/// .stdout(Stdio::null())
|
||||
/// .spawn()
|
||||
/// .expect("ls command failed to start");
|
||||
/// .stdout(Stdio::null())
|
||||
/// .spawn()
|
||||
/// .expect("ls command failed to start");
|
||||
/// ```
|
||||
#[stable(feature = "process", since = "1.0.0")]
|
||||
pub fn stdout<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command {
|
||||
@@ -919,9 +917,9 @@ pub fn stdout<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command {
|
||||
/// use std::process::{Command, Stdio};
|
||||
///
|
||||
/// Command::new("ls")
|
||||
/// .stderr(Stdio::null())
|
||||
/// .spawn()
|
||||
/// .expect("ls command failed to start");
|
||||
/// .stderr(Stdio::null())
|
||||
/// .spawn()
|
||||
/// .expect("ls command failed to start");
|
||||
/// ```
|
||||
#[stable(feature = "process", since = "1.0.0")]
|
||||
pub fn stderr<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command {
|
||||
@@ -941,8 +939,8 @@ pub fn stderr<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command {
|
||||
/// use std::process::Command;
|
||||
///
|
||||
/// Command::new("ls")
|
||||
/// .spawn()
|
||||
/// .expect("ls command failed to start");
|
||||
/// .spawn()
|
||||
/// .expect("ls command failed to start");
|
||||
/// ```
|
||||
#[stable(feature = "process", since = "1.0.0")]
|
||||
pub fn spawn(&mut self) -> io::Result<Child> {
|
||||
@@ -963,9 +961,9 @@ pub fn spawn(&mut self) -> io::Result<Child> {
|
||||
/// use std::process::Command;
|
||||
/// use std::io::{self, Write};
|
||||
/// let output = Command::new("/bin/cat")
|
||||
/// .arg("file.txt")
|
||||
/// .output()
|
||||
/// .expect("failed to execute process");
|
||||
/// .arg("file.txt")
|
||||
/// .output()
|
||||
/// .expect("failed to execute process");
|
||||
///
|
||||
/// println!("status: {}", output.status);
|
||||
/// io::stdout().write_all(&output.stdout).unwrap();
|
||||
@@ -990,9 +988,9 @@ pub fn output(&mut self) -> io::Result<Output> {
|
||||
/// use std::process::Command;
|
||||
///
|
||||
/// let status = Command::new("/bin/cat")
|
||||
/// .arg("file.txt")
|
||||
/// .status()
|
||||
/// .expect("failed to execute process");
|
||||
/// .arg("file.txt")
|
||||
/// .status()
|
||||
/// .expect("failed to execute process");
|
||||
///
|
||||
/// println!("process finished with: {status}");
|
||||
///
|
||||
@@ -1618,9 +1616,9 @@ impl ExitStatus {
|
||||
/// use std::process::Command;
|
||||
///
|
||||
/// let status = Command::new("ls")
|
||||
/// .arg("/dev/nonexistent")
|
||||
/// .status()
|
||||
/// .expect("ls could not be executed");
|
||||
/// .arg("/dev/nonexistent")
|
||||
/// .status()
|
||||
/// .expect("ls could not be executed");
|
||||
///
|
||||
/// println!("ls: {status}");
|
||||
/// status.exit_ok().expect_err("/dev/nonexistent could be listed!");
|
||||
@@ -1640,9 +1638,9 @@ pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
|
||||
/// use std::process::Command;
|
||||
///
|
||||
/// let status = Command::new("mkdir")
|
||||
/// .arg("projects")
|
||||
/// .status()
|
||||
/// .expect("failed to execute mkdir");
|
||||
/// .arg("projects")
|
||||
/// .status()
|
||||
/// .expect("failed to execute mkdir");
|
||||
///
|
||||
/// if status.success() {
|
||||
/// println!("'projects/' directory created");
|
||||
@@ -1673,13 +1671,13 @@ pub fn success(&self) -> bool {
|
||||
/// use std::process::Command;
|
||||
///
|
||||
/// let status = Command::new("mkdir")
|
||||
/// .arg("projects")
|
||||
/// .status()
|
||||
/// .expect("failed to execute mkdir");
|
||||
/// .arg("projects")
|
||||
/// .status()
|
||||
/// .expect("failed to execute mkdir");
|
||||
///
|
||||
/// match status.code() {
|
||||
/// Some(code) => println!("Exited with status code: {code}"),
|
||||
/// None => println!("Process terminated by signal")
|
||||
/// None => println!("Process terminated by signal")
|
||||
/// }
|
||||
/// ```
|
||||
#[must_use]
|
||||
@@ -1809,9 +1807,9 @@ pub fn into_status(&self) -> ExitStatus {
|
||||
}
|
||||
|
||||
#[unstable(feature = "exit_status_error", issue = "84908")]
|
||||
impl Into<ExitStatus> for ExitStatusError {
|
||||
fn into(self) -> ExitStatus {
|
||||
ExitStatus(self.0.into())
|
||||
impl From<ExitStatusError> for ExitStatus {
|
||||
fn from(error: ExitStatusError) -> Self {
|
||||
Self(error.0.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
|
||||
use crate::sys::fd::FileDesc;
|
||||
use crate::sys::{cvt, cvt_r};
|
||||
use crate::sys_common::IntoInner;
|
||||
use crate::sys_common::{FromInner, IntoInner};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Anonymous pipes
|
||||
@@ -159,3 +159,9 @@ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
|
||||
Self(FromRawFd::from_raw_fd(raw_fd))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromInner<FileDesc> for AnonPipe {
|
||||
fn from_inner(fd: FileDesc) -> Self {
|
||||
Self(fd)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,10 +46,6 @@
|
||||
pub use LINGER as linger;
|
||||
pub use TIMEVAL as timeval;
|
||||
|
||||
pub type CONDITION_VARIABLE = RTL_CONDITION_VARIABLE;
|
||||
pub type SRWLOCK = RTL_SRWLOCK;
|
||||
pub type INIT_ONCE = RTL_RUN_ONCE;
|
||||
|
||||
pub const CONDITION_VARIABLE_INIT: CONDITION_VARIABLE = CONDITION_VARIABLE { Ptr: ptr::null_mut() };
|
||||
pub const SRWLOCK_INIT: SRWLOCK = SRWLOCK { Ptr: ptr::null_mut() };
|
||||
pub const INIT_ONCE_STATIC_INIT: INIT_ONCE = INIT_ONCE { Ptr: ptr::null_mut() };
|
||||
@@ -224,7 +220,7 @@ pub struct in6_addr {
|
||||
) -> BOOL {
|
||||
windows_sys::ReadFileEx(
|
||||
hFile.as_raw_handle(),
|
||||
lpBuffer,
|
||||
lpBuffer.cast::<u8>(),
|
||||
nNumberOfBytesToRead,
|
||||
lpOverlapped,
|
||||
lpCompletionRoutine,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
--out windows_sys.rs
|
||||
--config flatten std
|
||||
--filter
|
||||
// tidy-alphabetical-start
|
||||
Windows.Wdk.Storage.FileSystem.FILE_COMPLETE_IF_OPLOCKED
|
||||
Windows.Wdk.Storage.FileSystem.FILE_CONTAINS_EXTENDED_CREATE_INFORMATION
|
||||
@@ -2108,7 +2111,6 @@ Windows.Win32.Networking.WinSock.WSABASEERR
|
||||
Windows.Win32.Networking.WinSock.WSABUF
|
||||
Windows.Win32.Networking.WinSock.WSACleanup
|
||||
Windows.Win32.Networking.WinSock.WSADATA
|
||||
Windows.Win32.Networking.WinSock.WSADATA
|
||||
Windows.Win32.Networking.WinSock.WSADuplicateSocketW
|
||||
Windows.Win32.Networking.WinSock.WSAEACCES
|
||||
Windows.Win32.Networking.WinSock.WSAEADDRINUSE
|
||||
@@ -2328,7 +2330,6 @@ Windows.Win32.Storage.FileSystem.FileStandardInfo
|
||||
Windows.Win32.Storage.FileSystem.FileStorageInfo
|
||||
Windows.Win32.Storage.FileSystem.FileStreamInfo
|
||||
Windows.Win32.Storage.FileSystem.FindClose
|
||||
Windows.Win32.Storage.FileSystem.FindFileHandle
|
||||
Windows.Win32.Storage.FileSystem.FindFirstFileW
|
||||
Windows.Win32.Storage.FileSystem.FindNextFileW
|
||||
Windows.Win32.Storage.FileSystem.FlushFileBuffers
|
||||
@@ -2420,8 +2421,6 @@ Windows.Win32.System.Console.STD_OUTPUT_HANDLE
|
||||
Windows.Win32.System.Console.WriteConsoleW
|
||||
Windows.Win32.System.Diagnostics.Debug.ARM64_NT_NEON128
|
||||
Windows.Win32.System.Diagnostics.Debug.CONTEXT
|
||||
Windows.Win32.System.Diagnostics.Debug.CONTEXT
|
||||
Windows.Win32.System.Diagnostics.Debug.CONTEXT
|
||||
Windows.Win32.System.Diagnostics.Debug.EXCEPTION_RECORD
|
||||
Windows.Win32.System.Diagnostics.Debug.FACILITY_CODE
|
||||
Windows.Win32.System.Diagnostics.Debug.FACILITY_NT_BIT
|
||||
@@ -2435,7 +2434,6 @@ Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_OPTIONS
|
||||
Windows.Win32.System.Diagnostics.Debug.FormatMessageW
|
||||
Windows.Win32.System.Diagnostics.Debug.M128A
|
||||
Windows.Win32.System.Diagnostics.Debug.XSAVE_FORMAT
|
||||
Windows.Win32.System.Diagnostics.Debug.XSAVE_FORMAT
|
||||
Windows.Win32.System.Environment.FreeEnvironmentStringsW
|
||||
Windows.Win32.System.Environment.GetCommandLineW
|
||||
Windows.Win32.System.Environment.GetCurrentDirectoryW
|
||||
@@ -2456,7 +2454,6 @@ Windows.Win32.System.Kernel.ExceptionContinueExecution
|
||||
Windows.Win32.System.Kernel.ExceptionContinueSearch
|
||||
Windows.Win32.System.Kernel.ExceptionNestedException
|
||||
Windows.Win32.System.Kernel.FLOATING_SAVE_AREA
|
||||
Windows.Win32.System.Kernel.FLOATING_SAVE_AREA
|
||||
Windows.Win32.System.Kernel.OBJ_DONT_REPARSE
|
||||
Windows.Win32.System.LibraryLoader.GetModuleFileNameW
|
||||
Windows.Win32.System.LibraryLoader.GetModuleHandleA
|
||||
@@ -2482,6 +2479,7 @@ Windows.Win32.System.SystemInformation.GetSystemTimeAsFileTime
|
||||
Windows.Win32.System.SystemInformation.GetWindowsDirectoryW
|
||||
Windows.Win32.System.SystemInformation.PROCESSOR_ARCHITECTURE
|
||||
Windows.Win32.System.SystemInformation.SYSTEM_INFO
|
||||
Windows.Win32.System.SystemServices.ALL_PROCESSOR_GROUPS
|
||||
Windows.Win32.System.SystemServices.DLL_PROCESS_DETACH
|
||||
Windows.Win32.System.SystemServices.DLL_THREAD_DETACH
|
||||
Windows.Win32.System.SystemServices.EXCEPTION_MAXIMUM_PARAMETERS
|
||||
@@ -2514,6 +2512,7 @@ Windows.Win32.System.Threading.DeleteProcThreadAttributeList
|
||||
Windows.Win32.System.Threading.DETACHED_PROCESS
|
||||
Windows.Win32.System.Threading.ExitProcess
|
||||
Windows.Win32.System.Threading.EXTENDED_STARTUPINFO_PRESENT
|
||||
Windows.Win32.System.Threading.GetActiveProcessorCount
|
||||
Windows.Win32.System.Threading.GetCurrentProcess
|
||||
Windows.Win32.System.Threading.GetCurrentProcessId
|
||||
Windows.Win32.System.Threading.GetCurrentThread
|
||||
@@ -2542,9 +2541,6 @@ Windows.Win32.System.Threading.PROFILE_USER
|
||||
Windows.Win32.System.Threading.REALTIME_PRIORITY_CLASS
|
||||
Windows.Win32.System.Threading.ReleaseSRWLockExclusive
|
||||
Windows.Win32.System.Threading.ReleaseSRWLockShared
|
||||
Windows.Win32.System.Threading.RTL_CONDITION_VARIABLE
|
||||
Windows.Win32.System.Threading.RTL_RUN_ONCE
|
||||
Windows.Win32.System.Threading.RTL_SRWLOCK
|
||||
Windows.Win32.System.Threading.SetThreadStackGuarantee
|
||||
Windows.Win32.System.Threading.Sleep
|
||||
Windows.Win32.System.Threading.SleepConditionVariableSRW
|
||||
@@ -2584,8 +2580,6 @@ Windows.Win32.System.Threading.WaitForMultipleObjects
|
||||
Windows.Win32.System.Threading.WaitForSingleObject
|
||||
Windows.Win32.System.Threading.WakeAllConditionVariable
|
||||
Windows.Win32.System.Threading.WakeConditionVariable
|
||||
Windows.Win32.System.WindowsProgramming.IO_STATUS_BLOCK
|
||||
Windows.Win32.System.WindowsProgramming.OBJECT_ATTRIBUTES
|
||||
Windows.Win32.System.WindowsProgramming.PROGRESS_CONTINUE
|
||||
Windows.Win32.UI.Shell.GetUserProfileDirectoryW
|
||||
// tidy-alphabetical-end
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// regenerate the bindings.
|
||||
//
|
||||
// ignore-tidy-filelength
|
||||
// Bindings generated by `windows-bindgen` 0.49.0
|
||||
// Bindings generated by `windows-bindgen` 0.51.1
|
||||
|
||||
#![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)]
|
||||
#[link(name = "advapi32")]
|
||||
@@ -32,11 +32,11 @@ pub fn BCryptGenRandom(
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn AcquireSRWLockExclusive(srwlock: *mut RTL_SRWLOCK) -> ();
|
||||
pub fn AcquireSRWLockExclusive(srwlock: *mut SRWLOCK) -> ();
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn AcquireSRWLockShared(srwlock: *mut RTL_SRWLOCK) -> ();
|
||||
pub fn AcquireSRWLockShared(srwlock: *mut SRWLOCK) -> ();
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
@@ -189,18 +189,15 @@ pub fn DuplicateHandle(
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn FindClose(hfindfile: FindFileHandle) -> BOOL;
|
||||
pub fn FindClose(hfindfile: HANDLE) -> BOOL;
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn FindFirstFileW(
|
||||
lpfilename: PCWSTR,
|
||||
lpfindfiledata: *mut WIN32_FIND_DATAW,
|
||||
) -> FindFileHandle;
|
||||
pub fn FindFirstFileW(lpfilename: PCWSTR, lpfindfiledata: *mut WIN32_FIND_DATAW) -> HANDLE;
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn FindNextFileW(hfindfile: FindFileHandle, lpfindfiledata: *mut WIN32_FIND_DATAW) -> BOOL;
|
||||
pub fn FindNextFileW(hfindfile: HANDLE, lpfindfiledata: *mut WIN32_FIND_DATAW) -> BOOL;
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
@@ -223,6 +220,10 @@ pub fn FormatMessageW(
|
||||
pub fn FreeEnvironmentStringsW(penv: PCWSTR) -> BOOL;
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn GetActiveProcessorCount(groupnumber: u16) -> u32;
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn GetCommandLineW() -> PCWSTR;
|
||||
}
|
||||
@@ -360,7 +361,7 @@ pub fn GetOverlappedResult(
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn InitOnceBeginInitialize(
|
||||
lpinitonce: *mut RTL_RUN_ONCE,
|
||||
lpinitonce: *mut INIT_ONCE,
|
||||
dwflags: u32,
|
||||
fpending: *mut BOOL,
|
||||
lpcontext: *mut *mut ::core::ffi::c_void,
|
||||
@@ -369,7 +370,7 @@ pub fn InitOnceBeginInitialize(
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn InitOnceComplete(
|
||||
lpinitonce: *mut RTL_RUN_ONCE,
|
||||
lpinitonce: *mut INIT_ONCE,
|
||||
dwflags: u32,
|
||||
lpcontext: *const ::core::ffi::c_void,
|
||||
) -> BOOL;
|
||||
@@ -424,7 +425,7 @@ pub fn ReadConsoleW(
|
||||
extern "system" {
|
||||
pub fn ReadFile(
|
||||
hfile: HANDLE,
|
||||
lpbuffer: *mut ::core::ffi::c_void,
|
||||
lpbuffer: *mut u8,
|
||||
nnumberofbytestoread: u32,
|
||||
lpnumberofbytesread: *mut u32,
|
||||
lpoverlapped: *mut OVERLAPPED,
|
||||
@@ -434,7 +435,7 @@ pub fn ReadFile(
|
||||
extern "system" {
|
||||
pub fn ReadFileEx(
|
||||
hfile: HANDLE,
|
||||
lpbuffer: *mut ::core::ffi::c_void,
|
||||
lpbuffer: *mut u8,
|
||||
nnumberofbytestoread: u32,
|
||||
lpoverlapped: *mut OVERLAPPED,
|
||||
lpcompletionroutine: LPOVERLAPPED_COMPLETION_ROUTINE,
|
||||
@@ -442,11 +443,11 @@ pub fn ReadFileEx(
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn ReleaseSRWLockExclusive(srwlock: *mut RTL_SRWLOCK) -> ();
|
||||
pub fn ReleaseSRWLockExclusive(srwlock: *mut SRWLOCK) -> ();
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn ReleaseSRWLockShared(srwlock: *mut RTL_SRWLOCK) -> ();
|
||||
pub fn ReleaseSRWLockShared(srwlock: *mut SRWLOCK) -> ();
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
@@ -513,8 +514,8 @@ pub fn SetFileTime(
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn SleepConditionVariableSRW(
|
||||
conditionvariable: *mut RTL_CONDITION_VARIABLE,
|
||||
srwlock: *mut RTL_SRWLOCK,
|
||||
conditionvariable: *mut CONDITION_VARIABLE,
|
||||
srwlock: *mut SRWLOCK,
|
||||
dwmilliseconds: u32,
|
||||
flags: u32,
|
||||
) -> BOOL;
|
||||
@@ -549,11 +550,11 @@ pub fn SleepConditionVariableSRW(
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn TryAcquireSRWLockExclusive(srwlock: *mut RTL_SRWLOCK) -> BOOLEAN;
|
||||
pub fn TryAcquireSRWLockExclusive(srwlock: *mut SRWLOCK) -> BOOLEAN;
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn TryAcquireSRWLockShared(srwlock: *mut RTL_SRWLOCK) -> BOOLEAN;
|
||||
pub fn TryAcquireSRWLockShared(srwlock: *mut SRWLOCK) -> BOOLEAN;
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
@@ -574,19 +575,19 @@ pub fn WaitForMultipleObjects(
|
||||
lphandles: *const HANDLE,
|
||||
bwaitall: BOOL,
|
||||
dwmilliseconds: u32,
|
||||
) -> WIN32_ERROR;
|
||||
) -> WAIT_EVENT;
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn WaitForSingleObject(hhandle: HANDLE, dwmilliseconds: u32) -> WIN32_ERROR;
|
||||
pub fn WaitForSingleObject(hhandle: HANDLE, dwmilliseconds: u32) -> WAIT_EVENT;
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn WakeAllConditionVariable(conditionvariable: *mut RTL_CONDITION_VARIABLE) -> ();
|
||||
pub fn WakeAllConditionVariable(conditionvariable: *mut CONDITION_VARIABLE) -> ();
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
pub fn WakeConditionVariable(conditionvariable: *mut RTL_CONDITION_VARIABLE) -> ();
|
||||
pub fn WakeConditionVariable(conditionvariable: *mut CONDITION_VARIABLE) -> ();
|
||||
}
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
@@ -847,6 +848,7 @@ fn clone(&self) -> Self {
|
||||
pub const AF_INET: ADDRESS_FAMILY = 2u16;
|
||||
pub const AF_INET6: ADDRESS_FAMILY = 23u16;
|
||||
pub const AF_UNSPEC: ADDRESS_FAMILY = 0u16;
|
||||
pub const ALL_PROCESSOR_GROUPS: u32 = 65535u32;
|
||||
#[repr(C)]
|
||||
pub union ARM64_NT_NEON128 {
|
||||
pub Anonymous: ARM64_NT_NEON128_0,
|
||||
@@ -899,7 +901,17 @@ fn clone(&self) -> Self {
|
||||
}
|
||||
pub const CALLBACK_CHUNK_FINISHED: LPPROGRESS_ROUTINE_CALLBACK_REASON = 0u32;
|
||||
pub const CALLBACK_STREAM_SWITCH: LPPROGRESS_ROUTINE_CALLBACK_REASON = 1u32;
|
||||
pub type COMPARESTRING_RESULT = u32;
|
||||
pub type COMPARESTRING_RESULT = i32;
|
||||
#[repr(C)]
|
||||
pub struct CONDITION_VARIABLE {
|
||||
pub Ptr: *mut ::core::ffi::c_void,
|
||||
}
|
||||
impl ::core::marker::Copy for CONDITION_VARIABLE {}
|
||||
impl ::core::clone::Clone for CONDITION_VARIABLE {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
pub type CONSOLE_MODE = u32;
|
||||
#[repr(C)]
|
||||
pub struct CONSOLE_READCONSOLE_CONTROL {
|
||||
@@ -917,7 +929,7 @@ fn clone(&self) -> Self {
|
||||
#[repr(C)]
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub struct CONTEXT {
|
||||
pub ContextFlags: u32,
|
||||
pub ContextFlags: CONTEXT_FLAGS,
|
||||
pub Cpsr: u32,
|
||||
pub Anonymous: CONTEXT_0,
|
||||
pub Sp: u64,
|
||||
@@ -1004,7 +1016,7 @@ pub struct CONTEXT {
|
||||
pub P4Home: u64,
|
||||
pub P5Home: u64,
|
||||
pub P6Home: u64,
|
||||
pub ContextFlags: u32,
|
||||
pub ContextFlags: CONTEXT_FLAGS,
|
||||
pub MxCsr: u32,
|
||||
pub SegCs: u16,
|
||||
pub SegDs: u16,
|
||||
@@ -1100,7 +1112,7 @@ fn clone(&self) -> Self {
|
||||
#[repr(C)]
|
||||
#[cfg(target_arch = "x86")]
|
||||
pub struct CONTEXT {
|
||||
pub ContextFlags: u32,
|
||||
pub ContextFlags: CONTEXT_FLAGS,
|
||||
pub Dr0: u32,
|
||||
pub Dr1: u32,
|
||||
pub Dr2: u32,
|
||||
@@ -1134,6 +1146,7 @@ fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
pub type CONTEXT_FLAGS = u32;
|
||||
pub const CP_UTF8: u32 = 65001u32;
|
||||
pub const CREATE_ALWAYS: FILE_CREATION_DISPOSITION = 2u32;
|
||||
pub const CREATE_BREAKAWAY_FROM_JOB: PROCESS_CREATION_FLAGS = 16777216u32;
|
||||
@@ -1151,9 +1164,9 @@ fn clone(&self) -> Self {
|
||||
pub const CREATE_SHARED_WOW_VDM: PROCESS_CREATION_FLAGS = 4096u32;
|
||||
pub const CREATE_SUSPENDED: PROCESS_CREATION_FLAGS = 4u32;
|
||||
pub const CREATE_UNICODE_ENVIRONMENT: PROCESS_CREATION_FLAGS = 1024u32;
|
||||
pub const CSTR_EQUAL: COMPARESTRING_RESULT = 2u32;
|
||||
pub const CSTR_GREATER_THAN: COMPARESTRING_RESULT = 3u32;
|
||||
pub const CSTR_LESS_THAN: COMPARESTRING_RESULT = 1u32;
|
||||
pub const CSTR_EQUAL: COMPARESTRING_RESULT = 2i32;
|
||||
pub const CSTR_GREATER_THAN: COMPARESTRING_RESULT = 3i32;
|
||||
pub const CSTR_LESS_THAN: COMPARESTRING_RESULT = 1i32;
|
||||
pub const DEBUG_ONLY_THIS_PROCESS: PROCESS_CREATION_FLAGS = 2u32;
|
||||
pub const DEBUG_PROCESS: PROCESS_CREATION_FLAGS = 1u32;
|
||||
pub const DELETE: FILE_ACCESS_RIGHTS = 65536u32;
|
||||
@@ -3369,7 +3382,6 @@ fn clone(&self) -> Self {
|
||||
pub const FileStandardInfo: FILE_INFO_BY_HANDLE_CLASS = 1i32;
|
||||
pub const FileStorageInfo: FILE_INFO_BY_HANDLE_CLASS = 16i32;
|
||||
pub const FileStreamInfo: FILE_INFO_BY_HANDLE_CLASS = 7i32;
|
||||
pub type FindFileHandle = *mut ::core::ffi::c_void;
|
||||
pub type GENERIC_ACCESS_RIGHTS = u32;
|
||||
pub const GENERIC_ALL: GENERIC_ACCESS_RIGHTS = 268435456u32;
|
||||
pub const GENERIC_EXECUTE: GENERIC_ACCESS_RIGHTS = 536870912u32;
|
||||
@@ -3383,6 +3395,12 @@ pub struct GUID {
|
||||
pub data3: u16,
|
||||
pub data4: [u8; 8],
|
||||
}
|
||||
impl ::core::marker::Copy for GUID {}
|
||||
impl ::core::clone::Clone for GUID {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
impl GUID {
|
||||
pub const fn from_u128(uuid: u128) -> Self {
|
||||
Self {
|
||||
@@ -3393,12 +3411,6 @@ pub const fn from_u128(uuid: u128) -> Self {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ::core::marker::Copy for GUID {}
|
||||
impl ::core::clone::Clone for GUID {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
pub type HANDLE = *mut ::core::ffi::c_void;
|
||||
pub type HANDLE_FLAGS = u32;
|
||||
pub const HANDLE_FLAG_INHERIT: HANDLE_FLAGS = 1u32;
|
||||
@@ -3431,6 +3443,16 @@ fn clone(&self) -> Self {
|
||||
pub const INFINITE: u32 = 4294967295u32;
|
||||
pub const INHERIT_CALLER_PRIORITY: PROCESS_CREATION_FLAGS = 131072u32;
|
||||
pub const INHERIT_PARENT_AFFINITY: PROCESS_CREATION_FLAGS = 65536u32;
|
||||
#[repr(C)]
|
||||
pub union INIT_ONCE {
|
||||
pub Ptr: *mut ::core::ffi::c_void,
|
||||
}
|
||||
impl ::core::marker::Copy for INIT_ONCE {}
|
||||
impl ::core::clone::Clone for INIT_ONCE {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
pub const INIT_ONCE_INIT_FAILED: u32 = 4u32;
|
||||
pub const INVALID_FILE_ATTRIBUTES: u32 = 4294967295u32;
|
||||
pub const INVALID_HANDLE_VALUE: HANDLE = ::core::ptr::invalid_mut(-1i32 as _);
|
||||
@@ -3659,10 +3681,10 @@ fn clone(&self) -> Self {
|
||||
pub struct OBJECT_ATTRIBUTES {
|
||||
pub Length: u32,
|
||||
pub RootDirectory: HANDLE,
|
||||
pub ObjectName: *mut UNICODE_STRING,
|
||||
pub ObjectName: *const UNICODE_STRING,
|
||||
pub Attributes: u32,
|
||||
pub SecurityDescriptor: *mut ::core::ffi::c_void,
|
||||
pub SecurityQualityOfService: *mut ::core::ffi::c_void,
|
||||
pub SecurityDescriptor: *const ::core::ffi::c_void,
|
||||
pub SecurityQualityOfService: *const ::core::ffi::c_void,
|
||||
}
|
||||
impl ::core::marker::Copy for OBJECT_ATTRIBUTES {}
|
||||
impl ::core::clone::Clone for OBJECT_ATTRIBUTES {
|
||||
@@ -3712,8 +3734,8 @@ fn clone(&self) -> Self {
|
||||
pub type PCWSTR = *const u16;
|
||||
pub type PIO_APC_ROUTINE = ::core::option::Option<
|
||||
unsafe extern "system" fn(
|
||||
apccontext: *const ::core::ffi::c_void,
|
||||
iostatusblock: *const IO_STATUS_BLOCK,
|
||||
apccontext: *mut ::core::ffi::c_void,
|
||||
iostatusblock: *mut IO_STATUS_BLOCK,
|
||||
reserved: u32,
|
||||
) -> (),
|
||||
>;
|
||||
@@ -3755,36 +3777,6 @@ fn clone(&self) -> Self {
|
||||
pub type PWSTR = *mut u16;
|
||||
pub const READ_CONTROL: FILE_ACCESS_RIGHTS = 131072u32;
|
||||
pub const REALTIME_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 256u32;
|
||||
#[repr(C)]
|
||||
pub struct RTL_CONDITION_VARIABLE {
|
||||
pub Ptr: *mut ::core::ffi::c_void,
|
||||
}
|
||||
impl ::core::marker::Copy for RTL_CONDITION_VARIABLE {}
|
||||
impl ::core::clone::Clone for RTL_CONDITION_VARIABLE {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
#[repr(C)]
|
||||
pub union RTL_RUN_ONCE {
|
||||
pub Ptr: *mut ::core::ffi::c_void,
|
||||
}
|
||||
impl ::core::marker::Copy for RTL_RUN_ONCE {}
|
||||
impl ::core::clone::Clone for RTL_RUN_ONCE {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
#[repr(C)]
|
||||
pub struct RTL_SRWLOCK {
|
||||
pub Ptr: *mut ::core::ffi::c_void,
|
||||
}
|
||||
impl ::core::marker::Copy for RTL_SRWLOCK {}
|
||||
impl ::core::clone::Clone for RTL_SRWLOCK {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
pub const SD_BOTH: WINSOCK_SHUTDOWN_HOW = 2i32;
|
||||
pub const SD_RECEIVE: WINSOCK_SHUTDOWN_HOW = 0i32;
|
||||
pub const SD_SEND: WINSOCK_SHUTDOWN_HOW = 1i32;
|
||||
@@ -3821,10 +3813,7 @@ fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
pub type SOCKET = u32;
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
pub type SOCKET = u64;
|
||||
pub type SOCKET = usize;
|
||||
pub const SOCKET_ERROR: i32 = -1i32;
|
||||
pub const SOCK_DGRAM: WINSOCK_SOCKET_TYPE = 2i32;
|
||||
pub const SOCK_RAW: WINSOCK_SOCKET_TYPE = 3i32;
|
||||
@@ -3838,6 +3827,16 @@ fn clone(&self) -> Self {
|
||||
pub const SO_RCVTIMEO: i32 = 4102i32;
|
||||
pub const SO_SNDTIMEO: i32 = 4101i32;
|
||||
pub const SPECIFIC_RIGHTS_ALL: FILE_ACCESS_RIGHTS = 65535u32;
|
||||
#[repr(C)]
|
||||
pub struct SRWLOCK {
|
||||
pub Ptr: *mut ::core::ffi::c_void,
|
||||
}
|
||||
impl ::core::marker::Copy for SRWLOCK {}
|
||||
impl ::core::clone::Clone for SRWLOCK {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
pub const STACK_SIZE_PARAM_IS_A_RESERVATION: THREAD_CREATION_FLAGS = 65536u32;
|
||||
pub const STANDARD_RIGHTS_ALL: FILE_ACCESS_RIGHTS = 2031616u32;
|
||||
pub const STANDARD_RIGHTS_EXECUTE: FILE_ACCESS_RIGHTS = 131072u32;
|
||||
@@ -4008,12 +4007,13 @@ fn clone(&self) -> Self {
|
||||
pub const VOLUME_NAME_DOS: GETFINALPATHNAMEBYHANDLE_FLAGS = 0u32;
|
||||
pub const VOLUME_NAME_GUID: GETFINALPATHNAMEBYHANDLE_FLAGS = 1u32;
|
||||
pub const VOLUME_NAME_NONE: GETFINALPATHNAMEBYHANDLE_FLAGS = 4u32;
|
||||
pub const WAIT_ABANDONED: WIN32_ERROR = 128u32;
|
||||
pub const WAIT_ABANDONED_0: WIN32_ERROR = 128u32;
|
||||
pub const WAIT_FAILED: WIN32_ERROR = 4294967295u32;
|
||||
pub const WAIT_IO_COMPLETION: WIN32_ERROR = 192u32;
|
||||
pub const WAIT_OBJECT_0: WIN32_ERROR = 0u32;
|
||||
pub const WAIT_TIMEOUT: WIN32_ERROR = 258u32;
|
||||
pub const WAIT_ABANDONED: WAIT_EVENT = 128u32;
|
||||
pub const WAIT_ABANDONED_0: WAIT_EVENT = 128u32;
|
||||
pub type WAIT_EVENT = u32;
|
||||
pub const WAIT_FAILED: WAIT_EVENT = 4294967295u32;
|
||||
pub const WAIT_IO_COMPLETION: WAIT_EVENT = 192u32;
|
||||
pub const WAIT_OBJECT_0: WAIT_EVENT = 0u32;
|
||||
pub const WAIT_TIMEOUT: WAIT_EVENT = 258u32;
|
||||
pub const WC_ERR_INVALID_CHARS: u32 = 128u32;
|
||||
pub type WIN32_ERROR = u32;
|
||||
#[repr(C)]
|
||||
|
||||
@@ -143,13 +143,8 @@ pub unsafe fn read_overlapped(
|
||||
) -> io::Result<Option<usize>> {
|
||||
let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD;
|
||||
let mut amt = 0;
|
||||
let res = cvt(c::ReadFile(
|
||||
self.as_raw_handle(),
|
||||
buf.as_ptr() as c::LPVOID,
|
||||
len,
|
||||
&mut amt,
|
||||
overlapped,
|
||||
));
|
||||
let res =
|
||||
cvt(c::ReadFile(self.as_raw_handle(), buf.as_mut_ptr(), len, &mut amt, overlapped));
|
||||
match res {
|
||||
Ok(_) => Ok(Some(amt as usize)),
|
||||
Err(e) => {
|
||||
|
||||
@@ -117,7 +117,7 @@ pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> {
|
||||
};
|
||||
|
||||
if socket != c::INVALID_SOCKET {
|
||||
unsafe { Ok(Self::from_raw_socket(socket)) }
|
||||
unsafe { Ok(Self::from_raw(socket)) }
|
||||
} else {
|
||||
let error = unsafe { c::WSAGetLastError() };
|
||||
|
||||
@@ -133,7 +133,7 @@ pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> {
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let socket = Self::from_raw_socket(socket);
|
||||
let socket = Self::from_raw(socket);
|
||||
socket.0.set_no_inherit()?;
|
||||
Ok(socket)
|
||||
}
|
||||
@@ -144,7 +144,7 @@ pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Resul
|
||||
self.set_nonblocking(true)?;
|
||||
let result = {
|
||||
let (addr, len) = addr.into_inner();
|
||||
let result = unsafe { c::connect(self.as_raw_socket(), addr.as_ptr(), len) };
|
||||
let result = unsafe { c::connect(self.as_raw(), addr.as_ptr(), len) };
|
||||
cvt(result).map(drop)
|
||||
};
|
||||
self.set_nonblocking(false)?;
|
||||
@@ -170,7 +170,7 @@ pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Resul
|
||||
let fds = {
|
||||
let mut fds = unsafe { mem::zeroed::<c::fd_set>() };
|
||||
fds.fd_count = 1;
|
||||
fds.fd_array[0] = self.as_raw_socket();
|
||||
fds.fd_array[0] = self.as_raw();
|
||||
fds
|
||||
};
|
||||
|
||||
@@ -202,11 +202,11 @@ pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Resul
|
||||
}
|
||||
|
||||
pub fn accept(&self, storage: *mut c::SOCKADDR, len: *mut c_int) -> io::Result<Socket> {
|
||||
let socket = unsafe { c::accept(self.as_raw_socket(), storage, len) };
|
||||
let socket = unsafe { c::accept(self.as_raw(), storage, len) };
|
||||
|
||||
match socket {
|
||||
c::INVALID_SOCKET => Err(last_error()),
|
||||
_ => unsafe { Ok(Self::from_raw_socket(socket)) },
|
||||
_ => unsafe { Ok(Self::from_raw(socket)) },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,9 +218,8 @@ fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Resu
|
||||
// On unix when a socket is shut down all further reads return 0, so we
|
||||
// do the same on windows to map a shut down socket to returning EOF.
|
||||
let length = cmp::min(buf.capacity(), i32::MAX as usize) as i32;
|
||||
let result = unsafe {
|
||||
c::recv(self.as_raw_socket(), buf.as_mut().as_mut_ptr() as *mut _, length, flags)
|
||||
};
|
||||
let result =
|
||||
unsafe { c::recv(self.as_raw(), buf.as_mut().as_mut_ptr() as *mut _, length, flags) };
|
||||
|
||||
match result {
|
||||
c::SOCKET_ERROR => {
|
||||
@@ -257,7 +256,7 @@ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
let mut flags = 0;
|
||||
let result = unsafe {
|
||||
c::WSARecv(
|
||||
self.as_raw_socket(),
|
||||
self.as_raw(),
|
||||
bufs.as_mut_ptr() as *mut c::WSABUF,
|
||||
length,
|
||||
&mut nread,
|
||||
@@ -305,7 +304,7 @@ fn recv_from_with_flags(
|
||||
// do the same on windows to map a shut down socket to returning EOF.
|
||||
let result = unsafe {
|
||||
c::recvfrom(
|
||||
self.as_raw_socket(),
|
||||
self.as_raw(),
|
||||
buf.as_mut_ptr() as *mut _,
|
||||
length,
|
||||
flags,
|
||||
@@ -341,7 +340,7 @@ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let mut nwritten = 0;
|
||||
let result = unsafe {
|
||||
c::WSASend(
|
||||
self.as_raw_socket(),
|
||||
self.as_raw(),
|
||||
bufs.as_ptr() as *const c::WSABUF as *mut _,
|
||||
length,
|
||||
&mut nwritten,
|
||||
@@ -392,14 +391,14 @@ pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
|
||||
Shutdown::Read => c::SD_RECEIVE,
|
||||
Shutdown::Both => c::SD_BOTH,
|
||||
};
|
||||
let result = unsafe { c::shutdown(self.as_raw_socket(), how) };
|
||||
let result = unsafe { c::shutdown(self.as_raw(), how) };
|
||||
cvt(result).map(drop)
|
||||
}
|
||||
|
||||
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
|
||||
let mut nonblocking = nonblocking as c_ulong;
|
||||
let result =
|
||||
unsafe { c::ioctlsocket(self.as_raw_socket(), c::FIONBIO as c_int, &mut nonblocking) };
|
||||
unsafe { c::ioctlsocket(self.as_raw(), c::FIONBIO as c_int, &mut nonblocking) };
|
||||
cvt(result).map(drop)
|
||||
}
|
||||
|
||||
@@ -433,8 +432,15 @@ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
|
||||
}
|
||||
|
||||
// This is used by sys_common code to abstract over Windows and Unix.
|
||||
pub fn as_raw(&self) -> RawSocket {
|
||||
self.as_inner().as_raw_socket()
|
||||
pub fn as_raw(&self) -> c::SOCKET {
|
||||
debug_assert_eq!(mem::size_of::<c::SOCKET>(), mem::size_of::<RawSocket>());
|
||||
debug_assert_eq!(mem::align_of::<c::SOCKET>(), mem::align_of::<RawSocket>());
|
||||
self.as_inner().as_raw_socket() as c::SOCKET
|
||||
}
|
||||
pub unsafe fn from_raw(raw: c::SOCKET) -> Self {
|
||||
debug_assert_eq!(mem::size_of::<c::SOCKET>(), mem::size_of::<RawSocket>());
|
||||
debug_assert_eq!(mem::align_of::<c::SOCKET>(), mem::align_of::<RawSocket>());
|
||||
Self::from_raw_socket(raw as RawSocket)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
use crate::sys::fs::{File, OpenOptions};
|
||||
use crate::sys::handle::Handle;
|
||||
use crate::sys::hashmap_random_keys;
|
||||
use crate::sys_common::IntoInner;
|
||||
use crate::sys_common::{FromInner, IntoInner};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Anonymous pipes
|
||||
@@ -28,6 +28,12 @@ fn into_inner(self) -> Handle {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromInner<Handle> for AnonPipe {
|
||||
fn from_inner(inner: Handle) -> AnonPipe {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Pipes {
|
||||
pub ours: AnonPipe,
|
||||
pub theirs: AnonPipe,
|
||||
|
||||
+41
-49
@@ -41,6 +41,10 @@ def get_cpus():
|
||||
return 1
|
||||
|
||||
|
||||
def eprint(*args, **kwargs):
|
||||
kwargs["file"] = sys.stderr
|
||||
print(*args, **kwargs)
|
||||
|
||||
|
||||
def get(base, url, path, checksums, verbose=False):
|
||||
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
|
||||
@@ -57,23 +61,23 @@ def get(base, url, path, checksums, verbose=False):
|
||||
if os.path.exists(path):
|
||||
if verify(path, sha256, False):
|
||||
if verbose:
|
||||
print("using already-download file", path, file=sys.stderr)
|
||||
eprint("using already-download file", path)
|
||||
return
|
||||
else:
|
||||
if verbose:
|
||||
print("ignoring already-download file",
|
||||
path, "due to failed verification", file=sys.stderr)
|
||||
eprint("ignoring already-download file",
|
||||
path, "due to failed verification")
|
||||
os.unlink(path)
|
||||
download(temp_path, "{}/{}".format(base, url), True, verbose)
|
||||
if not verify(temp_path, sha256, verbose):
|
||||
raise RuntimeError("failed verification")
|
||||
if verbose:
|
||||
print("moving {} to {}".format(temp_path, path), file=sys.stderr)
|
||||
eprint("moving {} to {}".format(temp_path, path))
|
||||
shutil.move(temp_path, path)
|
||||
finally:
|
||||
if os.path.isfile(temp_path):
|
||||
if verbose:
|
||||
print("removing", temp_path, file=sys.stderr)
|
||||
eprint("removing", temp_path)
|
||||
os.unlink(temp_path)
|
||||
|
||||
|
||||
@@ -83,7 +87,7 @@ def download(path, url, probably_big, verbose):
|
||||
_download(path, url, probably_big, verbose, True)
|
||||
return
|
||||
except RuntimeError:
|
||||
print("\nspurious failure, trying again", file=sys.stderr)
|
||||
eprint("\nspurious failure, trying again")
|
||||
_download(path, url, probably_big, verbose, False)
|
||||
|
||||
|
||||
@@ -94,7 +98,7 @@ def _download(path, url, probably_big, verbose, exception):
|
||||
# - If we are on win32 fallback to powershell
|
||||
# - Otherwise raise the error if appropriate
|
||||
if probably_big or verbose:
|
||||
print("downloading {}".format(url), file=sys.stderr)
|
||||
eprint("downloading {}".format(url))
|
||||
|
||||
try:
|
||||
if (probably_big or verbose) and "GITHUB_ACTIONS" not in os.environ:
|
||||
@@ -129,20 +133,20 @@ def _download(path, url, probably_big, verbose, exception):
|
||||
def verify(path, expected, verbose):
|
||||
"""Check if the sha256 sum of the given path is valid"""
|
||||
if verbose:
|
||||
print("verifying", path, file=sys.stderr)
|
||||
eprint("verifying", path)
|
||||
with open(path, "rb") as source:
|
||||
found = hashlib.sha256(source.read()).hexdigest()
|
||||
verified = found == expected
|
||||
if not verified:
|
||||
print("invalid checksum:\n"
|
||||
eprint("invalid checksum:\n"
|
||||
" found: {}\n"
|
||||
" expected: {}".format(found, expected), file=sys.stderr)
|
||||
" expected: {}".format(found, expected))
|
||||
return verified
|
||||
|
||||
|
||||
def unpack(tarball, tarball_suffix, dst, verbose=False, match=None):
|
||||
"""Unpack the given tarball file"""
|
||||
print("extracting", tarball, file=sys.stderr)
|
||||
eprint("extracting", tarball)
|
||||
fname = os.path.basename(tarball).replace(tarball_suffix, "")
|
||||
with contextlib.closing(tarfile.open(tarball)) as tar:
|
||||
for member in tar.getnames():
|
||||
@@ -155,7 +159,7 @@ def unpack(tarball, tarball_suffix, dst, verbose=False, match=None):
|
||||
|
||||
dst_path = os.path.join(dst, name)
|
||||
if verbose:
|
||||
print(" extracting", member, file=sys.stderr)
|
||||
eprint(" extracting", member)
|
||||
tar.extract(member, dst)
|
||||
src_path = os.path.join(dst, member)
|
||||
if os.path.isdir(src_path) and os.path.exists(dst_path):
|
||||
@@ -167,7 +171,7 @@ def unpack(tarball, tarball_suffix, dst, verbose=False, match=None):
|
||||
def run(args, verbose=False, exception=False, is_bootstrap=False, **kwargs):
|
||||
"""Run a child program in a new process"""
|
||||
if verbose:
|
||||
print("running: " + ' '.join(args), file=sys.stderr)
|
||||
eprint("running: " + ' '.join(args))
|
||||
sys.stdout.flush()
|
||||
# Ensure that the .exe is used on Windows just in case a Linux ELF has been
|
||||
# compiled in the same directory.
|
||||
@@ -207,8 +211,8 @@ def require(cmd, exit=True, exception=False):
|
||||
if exception:
|
||||
raise
|
||||
elif exit:
|
||||
print("error: unable to run `{}`: {}".format(' '.join(cmd), exc), file=sys.stderr)
|
||||
print("Please make sure it's installed and in the path.", file=sys.stderr)
|
||||
eprint("error: unable to run `{}`: {}".format(' '.join(cmd), exc))
|
||||
eprint("Please make sure it's installed and in the path.")
|
||||
sys.exit(1)
|
||||
return None
|
||||
|
||||
@@ -239,14 +243,12 @@ def default_build_triple(verbose):
|
||||
host = next(x for x in version.split('\n') if x.startswith("host: "))
|
||||
triple = host.split("host: ")[1]
|
||||
if verbose:
|
||||
print("detected default triple {} from pre-installed rustc".format(triple),
|
||||
file=sys.stderr)
|
||||
eprint("detected default triple {} from pre-installed rustc".format(triple))
|
||||
return triple
|
||||
except Exception as e:
|
||||
if verbose:
|
||||
print("pre-installed rustc not detected: {}".format(e),
|
||||
file=sys.stderr)
|
||||
print("falling back to auto-detect", file=sys.stderr)
|
||||
eprint("pre-installed rustc not detected: {}".format(e))
|
||||
eprint("falling back to auto-detect")
|
||||
|
||||
required = not platform_is_win32()
|
||||
uname = require(["uname", "-smp"], exit=required)
|
||||
@@ -672,15 +674,14 @@ class RustBuild(object):
|
||||
if not is_nixos:
|
||||
in_nix_shell = os.getenv('IN_NIX_SHELL')
|
||||
if in_nix_shell:
|
||||
print("The IN_NIX_SHELL environment variable is `{}`;".format(in_nix_shell),
|
||||
"you may need to set `patch-binaries-for-nix=true` in config.toml",
|
||||
file=sys.stderr)
|
||||
eprint("The IN_NIX_SHELL environment variable is `{}`;".format(in_nix_shell),
|
||||
"you may need to set `patch-binaries-for-nix=true` in config.toml")
|
||||
|
||||
return is_nixos
|
||||
|
||||
answer = self._should_fix_bins_and_dylibs = get_answer()
|
||||
if answer:
|
||||
print("info: You seem to be using Nix.", file=sys.stderr)
|
||||
eprint("info: You seem to be using Nix.")
|
||||
return answer
|
||||
|
||||
def fix_bin_or_dylib(self, fname):
|
||||
@@ -693,7 +694,7 @@ class RustBuild(object):
|
||||
Please see https://nixos.org/patchelf.html for more information
|
||||
"""
|
||||
assert self._should_fix_bins_and_dylibs is True
|
||||
print("attempting to patch", fname, file=sys.stderr)
|
||||
eprint("attempting to patch", fname)
|
||||
|
||||
# Only build `.nix-deps` once.
|
||||
nix_deps_dir = self.nix_deps_dir
|
||||
@@ -726,7 +727,7 @@ class RustBuild(object):
|
||||
"nix-build", "-E", nix_expr, "-o", nix_deps_dir,
|
||||
])
|
||||
except subprocess.CalledProcessError as reason:
|
||||
print("warning: failed to call nix-build:", reason, file=sys.stderr)
|
||||
eprint("warning: failed to call nix-build:", reason)
|
||||
return
|
||||
self.nix_deps_dir = nix_deps_dir
|
||||
|
||||
@@ -746,7 +747,7 @@ class RustBuild(object):
|
||||
try:
|
||||
subprocess.check_output([patchelf] + patchelf_args + [fname])
|
||||
except subprocess.CalledProcessError as reason:
|
||||
print("warning: failed to call patchelf:", reason, file=sys.stderr)
|
||||
eprint("warning: failed to call patchelf:", reason)
|
||||
return
|
||||
|
||||
def rustc_stamp(self):
|
||||
@@ -888,7 +889,7 @@ class RustBuild(object):
|
||||
if "GITHUB_ACTIONS" in env:
|
||||
print("::group::Building bootstrap")
|
||||
else:
|
||||
print("Building bootstrap", file=sys.stderr)
|
||||
eprint("Building bootstrap")
|
||||
|
||||
args = self.build_bootstrap_cmd(env)
|
||||
# Run this from the source directory so cargo finds .cargo/config
|
||||
@@ -997,12 +998,9 @@ class RustBuild(object):
|
||||
if 'SUDO_USER' in os.environ and not self.use_vendored_sources:
|
||||
if os.getuid() == 0:
|
||||
self.use_vendored_sources = True
|
||||
print('info: looks like you\'re trying to run this command as root',
|
||||
file=sys.stderr)
|
||||
print(' and so in order to preserve your $HOME this will now',
|
||||
file=sys.stderr)
|
||||
print(' use vendored sources by default.',
|
||||
file=sys.stderr)
|
||||
eprint('info: looks like you\'re trying to run this command as root')
|
||||
eprint(' and so in order to preserve your $HOME this will now')
|
||||
eprint(' use vendored sources by default.')
|
||||
|
||||
cargo_dir = os.path.join(self.rust_root, '.cargo')
|
||||
if self.use_vendored_sources:
|
||||
@@ -1012,18 +1010,14 @@ class RustBuild(object):
|
||||
"--sync ./src/tools/rust-analyzer/Cargo.toml " \
|
||||
"--sync ./compiler/rustc_codegen_cranelift/Cargo.toml " \
|
||||
"--sync ./src/bootstrap/Cargo.toml "
|
||||
print('error: vendoring required, but vendor directory does not exist.',
|
||||
file=sys.stderr)
|
||||
print(' Run `cargo vendor {}` to initialize the '
|
||||
'vendor directory.'.format(sync_dirs),
|
||||
file=sys.stderr)
|
||||
print('Alternatively, use the pre-vendored `rustc-src` dist component.',
|
||||
file=sys.stderr)
|
||||
eprint('error: vendoring required, but vendor directory does not exist.')
|
||||
eprint(' Run `cargo vendor {}` to initialize the '
|
||||
'vendor directory.'.format(sync_dirs))
|
||||
eprint('Alternatively, use the pre-vendored `rustc-src` dist component.')
|
||||
raise Exception("{} not found".format(vendor_dir))
|
||||
|
||||
if not os.path.exists(cargo_dir):
|
||||
print('error: vendoring required, but .cargo/config does not exist.',
|
||||
file=sys.stderr)
|
||||
eprint('error: vendoring required, but .cargo/config does not exist.')
|
||||
raise Exception("{} not found".format(cargo_dir))
|
||||
else:
|
||||
if os.path.exists(cargo_dir):
|
||||
@@ -1117,10 +1111,9 @@ def main():
|
||||
# If the user is asking for help, let them know that the whole download-and-build
|
||||
# process has to happen before anything is printed out.
|
||||
if help_triggered:
|
||||
print(
|
||||
eprint(
|
||||
"info: Downloading and building bootstrap before processing --help command.\n"
|
||||
" See src/bootstrap/README.md for help with common commands."
|
||||
, file=sys.stderr)
|
||||
" See src/bootstrap/README.md for help with common commands.")
|
||||
|
||||
exit_code = 0
|
||||
success_word = "successfully"
|
||||
@@ -1131,12 +1124,11 @@ def main():
|
||||
exit_code = error.code
|
||||
else:
|
||||
exit_code = 1
|
||||
print(error, file=sys.stderr)
|
||||
eprint(error)
|
||||
success_word = "unsuccessfully"
|
||||
|
||||
if not help_triggered:
|
||||
print("Build completed", success_word, "in", format_build_time(time() - start_time),
|
||||
file=sys.stderr)
|
||||
eprint("Build completed", success_word, "in", format_build_time(time() - start_time))
|
||||
sys.exit(exit_code)
|
||||
|
||||
|
||||
|
||||
@@ -45,6 +45,23 @@ fn change_drive(s: &str) -> Option<String> {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_dir_writable_for_user(dir: &PathBuf) -> bool {
|
||||
let tmp_file = dir.join(".tmp");
|
||||
match fs::File::create(&tmp_file) {
|
||||
Ok(_) => {
|
||||
fs::remove_file(tmp_file).unwrap();
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == std::io::ErrorKind::PermissionDenied {
|
||||
false
|
||||
} else {
|
||||
panic!("Failed the write access check for the current user. {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn install_sh(
|
||||
builder: &Builder<'_>,
|
||||
package: &str,
|
||||
@@ -56,6 +73,17 @@ fn install_sh(
|
||||
|
||||
let prefix = default_path(&builder.config.prefix, "/usr/local");
|
||||
let sysconfdir = prefix.join(default_path(&builder.config.sysconfdir, "/etc"));
|
||||
|
||||
// Sanity check for the user write access on prefix and sysconfdir
|
||||
assert!(
|
||||
is_dir_writable_for_user(&prefix),
|
||||
"User doesn't have write access on `install.prefix` path in the `config.toml`.",
|
||||
);
|
||||
assert!(
|
||||
is_dir_writable_for_user(&sysconfdir),
|
||||
"User doesn't have write access on `install.sysconfdir` path in `config.toml`."
|
||||
);
|
||||
|
||||
let datadir = prefix.join(default_path(&builder.config.datadir, "share"));
|
||||
let docdir = prefix.join(default_path(&builder.config.docdir, "share/doc/rust"));
|
||||
let mandir = prefix.join(default_path(&builder.config.mandir, "share/man"));
|
||||
@@ -92,6 +120,9 @@ fn prepare_dir(mut path: PathBuf) -> String {
|
||||
// More information on the environment variable is available here:
|
||||
// https://www.gnu.org/prep/standards/html_node/DESTDIR.html
|
||||
if let Some(destdir) = env::var_os("DESTDIR").map(PathBuf::from) {
|
||||
// Sanity check for the user write access on DESTDIR
|
||||
assert!(is_dir_writable_for_user(&destdir), "User doesn't have write access on DESTDIR.");
|
||||
|
||||
let without_destdir = path.clone();
|
||||
path = destdir;
|
||||
// Custom .join() which ignores disk roots.
|
||||
|
||||
@@ -271,7 +271,7 @@ For targets: `loongarch64-unknown-linux-gnu`
|
||||
- Operating System > Linux kernel version = 5.19.16
|
||||
- Binary utilities > Version of binutils = 2.40
|
||||
- C-library > glibc version = 2.36
|
||||
- C compiler > gcc version = 13.1.0
|
||||
- C compiler > gcc version = 13.2.0
|
||||
- C compiler > C++ = ENABLE -- to cross compile LLVM
|
||||
|
||||
### `mips-linux-gnu.defconfig`
|
||||
@@ -407,7 +407,7 @@ For targets: `riscv64-unknown-linux-gnu`
|
||||
- Target options > Bitness = 64-bit
|
||||
- Operating System > Target OS = linux
|
||||
- Operating System > Linux kernel version = 4.20.17
|
||||
- Binary utilities > Version of binutils = 2.32
|
||||
- Binary utilities > Version of binutils = 2.36.1
|
||||
- C-library > glibc version = 2.29
|
||||
- C compiler > gcc version = 8.5.0
|
||||
- C compiler > C++ = ENABLE -- to cross compile LLVM
|
||||
|
||||
@@ -3,9 +3,8 @@ FROM ubuntu:22.04
|
||||
COPY scripts/cross-apt-packages.sh /scripts/
|
||||
RUN sh /scripts/cross-apt-packages.sh
|
||||
|
||||
# The latest released version does not support LoongArch.
|
||||
COPY scripts/crosstool-ng-git.sh /scripts/
|
||||
RUN sh /scripts/crosstool-ng-git.sh
|
||||
COPY scripts/crosstool-ng.sh /scripts/
|
||||
RUN sh /scripts/crosstool-ng.sh
|
||||
|
||||
COPY scripts/rustbuild-setup.sh /scripts/
|
||||
RUN sh /scripts/rustbuild-setup.sh
|
||||
|
||||
@@ -128,6 +128,8 @@ ENV TARGETS=$TARGETS,armv7-unknown-linux-gnueabi
|
||||
ENV TARGETS=$TARGETS,armv7-unknown-linux-musleabi
|
||||
ENV TARGETS=$TARGETS,i686-unknown-freebsd
|
||||
ENV TARGETS=$TARGETS,x86_64-unknown-none
|
||||
ENV TARGETS=$TARGETS,loongarch64-unknown-none
|
||||
ENV TARGETS=$TARGETS,loongarch64-unknown-none-softfloat
|
||||
ENV TARGETS=$TARGETS,aarch64-unknown-uefi
|
||||
ENV TARGETS=$TARGETS,i686-unknown-uefi
|
||||
ENV TARGETS=$TARGETS,x86_64-unknown-uefi
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -ex
|
||||
|
||||
URL=https://github.com/crosstool-ng/crosstool-ng
|
||||
REV=227d99d7f3115f3a078595a580d2b307dcd23e93
|
||||
|
||||
mkdir crosstool-ng
|
||||
cd crosstool-ng
|
||||
git init
|
||||
git fetch --depth=1 ${URL} ${REV}
|
||||
git reset --hard FETCH_HEAD
|
||||
./bootstrap
|
||||
./configure --prefix=/usr/local
|
||||
make -j$(nproc)
|
||||
make install
|
||||
cd ..
|
||||
rm -rf crosstool-ng
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -ex
|
||||
|
||||
CT_NG=1.25.0
|
||||
CT_NG=1.26.0
|
||||
|
||||
url="https://github.com/crosstool-ng/crosstool-ng/archive/crosstool-ng-$CT_NG.tar.gz"
|
||||
curl -Lf $url | tar xzf -
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user