Merge from rustc

This commit is contained in:
Ralf Jung 2024-11-10 10:03:45 +01:00
commit a01f37c7f4
167 changed files with 5562 additions and 2638 deletions

View File

@ -1743,15 +1743,23 @@ pub enum PointerKind {
Box { unpin: bool, global: bool }, Box { unpin: bool, global: bool },
} }
/// Note that this information is advisory only, and backends are free to ignore it. /// Encodes extra information we have about a pointer.
/// It can only be used to encode potential optimizations, but no critical information. /// Note that this information is advisory only, and backends are free to ignore it:
/// if the information is wrong, that can cause UB, but if the information is absent,
/// that must always be okay.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct PointeeInfo { pub struct PointeeInfo {
pub size: Size,
pub align: Align,
/// If this is `None`, then this is a raw pointer, so size and alignment are not guaranteed to /// If this is `None`, then this is a raw pointer, so size and alignment are not guaranteed to
/// be reliable. /// be reliable.
pub safe: Option<PointerKind>, pub safe: Option<PointerKind>,
/// If `safe` is `Some`, then the pointer is either null or dereferenceable for this many bytes.
/// On a function argument, "dereferenceable" here means "dereferenceable for the entire duration
/// of this function call", i.e. it is UB for the memory that this pointer points to to be freed
/// while this function is still running.
/// The size can be zero if the pointer is not dereferenceable.
pub size: Size,
/// If `safe` is `Some`, then the pointer is aligned as indicated.
pub align: Align,
} }
impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> { impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {

View File

@ -48,6 +48,7 @@ pub(crate) fn lower_inline_asm(
| asm::InlineAsmArch::RiscV32 | asm::InlineAsmArch::RiscV32
| asm::InlineAsmArch::RiscV64 | asm::InlineAsmArch::RiscV64
| asm::InlineAsmArch::LoongArch64 | asm::InlineAsmArch::LoongArch64
| asm::InlineAsmArch::S390x
); );
if !is_stable && !self.tcx.features().asm_experimental_arch() { if !is_stable && !self.tcx.features().asm_experimental_arch() {
feature_err( feature_err(

View File

@ -1,10 +1,7 @@
use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::ErrorGuaranteed; use rustc_errors::ErrorGuaranteed;
use rustc_hir::OpaqueTyOrigin;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId; use rustc_hir::def_id::LocalDefId;
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, TyCtxtInferExt as _}; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, TyCtxtInferExt as _};
use rustc_infer::traits::{Obligation, ObligationCause};
use rustc_macros::extension; use rustc_macros::extension;
use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::{ use rustc_middle::ty::{
@ -12,7 +9,6 @@
TypingMode, TypingMode,
}; };
use rustc_span::Span; use rustc_span::Span;
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::ObligationCtxt;
use tracing::{debug, instrument}; use tracing::{debug, instrument};
@ -303,91 +299,7 @@ fn infer_opaque_definition_from_instantiation(
return Ty::new_error(self.tcx, e); return Ty::new_error(self.tcx, e);
} }
// `definition_ty` does not live in of the current inference context, definition_ty
// so lets make sure that we don't accidentally misuse our current `infcx`.
match check_opaque_type_well_formed(
self.tcx,
self.next_trait_solver(),
opaque_type_key.def_id,
instantiated_ty.span,
definition_ty,
) {
Ok(hidden_ty) => hidden_ty,
Err(guar) => Ty::new_error(self.tcx, guar),
}
}
}
/// This logic duplicates most of `check_opaque_meets_bounds`.
/// FIXME(oli-obk): Also do region checks here and then consider removing
/// `check_opaque_meets_bounds` entirely.
fn check_opaque_type_well_formed<'tcx>(
tcx: TyCtxt<'tcx>,
next_trait_solver: bool,
def_id: LocalDefId,
definition_span: Span,
definition_ty: Ty<'tcx>,
) -> Result<Ty<'tcx>, ErrorGuaranteed> {
// Only check this for TAIT. RPIT already supports `tests/ui/impl-trait/nested-return-type2.rs`
// on stable and we'd break that.
let opaque_ty_hir = tcx.hir().expect_opaque_ty(def_id);
let OpaqueTyOrigin::TyAlias { .. } = opaque_ty_hir.origin else {
return Ok(definition_ty);
};
let param_env = tcx.param_env(def_id);
let mut parent_def_id = def_id;
while tcx.def_kind(parent_def_id) == DefKind::OpaqueTy {
parent_def_id = tcx.local_parent(parent_def_id);
}
// FIXME(#132279): This should eventually use the already defined hidden types
// instead. Alternatively we'll entirely remove this function given we also check
// the opaque in `check_opaque_meets_bounds` later.
let infcx = tcx
.infer_ctxt()
.with_next_trait_solver(next_trait_solver)
.build(TypingMode::analysis_in_body(tcx, parent_def_id));
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let identity_args = GenericArgs::identity_for_item(tcx, def_id);
// Require that the hidden type actually fulfills all the bounds of the opaque type, even without
// the bounds that the function supplies.
let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), identity_args);
ocx.eq(&ObligationCause::misc(definition_span, def_id), param_env, opaque_ty, definition_ty)
.map_err(|err| {
infcx
.err_ctxt()
.report_mismatched_types(
&ObligationCause::misc(definition_span, def_id),
param_env,
opaque_ty,
definition_ty,
err,
)
.emit()
})?;
// Require the hidden type to be well-formed with only the generics of the opaque type.
// Defining use functions may have more bounds than the opaque type, which is ok, as long as the
// hidden type is well formed even without those bounds.
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
definition_ty.into(),
)));
ocx.register_obligation(Obligation::misc(tcx, definition_span, def_id, param_env, predicate));
// Check that all obligations are satisfied by the implementation's
// version.
let errors = ocx.select_all_or_error();
// This is fishy, but we check it again in `check_opaque_meets_bounds`.
// Remove once we can prepopulate with known hidden types.
let _ = infcx.take_opaque_types();
if errors.is_empty() {
Ok(definition_ty)
} else {
Err(infcx.err_ctxt().report_fulfillment_errors(errors))
} }
} }

View File

@ -38,7 +38,7 @@ index 42a26ae..5ac1042 100644
@@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
+#![cfg(test)] +#![cfg(test)]
// tidy-alphabetical-start // tidy-alphabetical-start
#![cfg_attr(bootstrap, feature(const_three_way_compare))]
#![cfg_attr(bootstrap, feature(strict_provenance))] #![cfg_attr(bootstrap, feature(strict_provenance))]
#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))]
-- --
2.21.0 (Apple Git-122) 2.21.0 (Apple Git-122)

View File

@ -15,7 +15,7 @@ index 1e336bf..35e6f54 100644
--- a/lib.rs --- a/lib.rs
+++ b/lib.rs +++ b/lib.rs
@@ -2,7 +2,6 @@ @@ -2,7 +2,6 @@
// tidy-alphabetical-start #![cfg_attr(bootstrap, feature(const_three_way_compare))]
#![cfg_attr(bootstrap, feature(strict_provenance))] #![cfg_attr(bootstrap, feature(strict_provenance))]
#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] #![cfg_attr(not(bootstrap), feature(strict_provenance_lints))]
-#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))]

View File

@ -1,4 +1,4 @@
[toolchain] [toolchain]
channel = "nightly-2024-11-02" channel = "nightly-2024-11-09"
components = ["rust-src", "rustc-dev", "llvm-tools"] components = ["rust-src", "rustc-dev", "llvm-tools"]
profile = "minimal" profile = "minimal"

View File

@ -11,5 +11,22 @@ rm -r compiler/rustc_codegen_cranelift/{Cargo.*,src}
cp ../Cargo.* compiler/rustc_codegen_cranelift/ cp ../Cargo.* compiler/rustc_codegen_cranelift/
cp -r ../src compiler/rustc_codegen_cranelift/src cp -r ../src compiler/rustc_codegen_cranelift/src
# FIXME(rust-lang/rust#132719) remove once it doesn't break without this patch
cat <<EOF | git apply -
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 3394f2a84a0..cb980dd4d7c 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -1976,7 +1976,7 @@ fn run(self, builder: &Builder<'_>) -> Compiler {
}
}
- {
+ if builder.config.llvm_enabled(target_compiler.host) && builder.config.llvm_tools_enabled {
// \`llvm-strip\` is used by rustc, which is actually just a symlink to \`llvm-objcopy\`,
// so copy and rename \`llvm-objcopy\`.
let src_exe = exe("llvm-objcopy", target_compiler.host);
EOF
./x.py build --stage 1 library/std ./x.py build --stage 1 library/std
popd popd

View File

@ -3,7 +3,7 @@
use std::borrow::Cow; use std::borrow::Cow;
use rustc_target::abi::call::PassMode; use rustc_target::callconv::PassMode;
use crate::prelude::*; use crate::prelude::*;

View File

@ -10,6 +10,7 @@
use cranelift_codegen::ir::{ArgumentPurpose, SigRef}; use cranelift_codegen::ir::{ArgumentPurpose, SigRef};
use cranelift_codegen::isa::CallConv; use cranelift_codegen::isa::CallConv;
use cranelift_module::ModuleError; use cranelift_module::ModuleError;
use rustc_abi::ExternAbi;
use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization;
use rustc_codegen_ssa::errors::CompilerBuiltinsCannotCall; use rustc_codegen_ssa::errors::CompilerBuiltinsCannotCall;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
@ -18,8 +19,7 @@
use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_session::Session; use rustc_session::Session;
use rustc_span::source_map::Spanned; use rustc_span::source_map::Spanned;
use rustc_target::abi::call::{Conv, FnAbi, PassMode}; use rustc_target::callconv::{Conv, FnAbi, PassMode};
use rustc_target::spec::abi::Abi;
use self::pass_mode::*; use self::pass_mode::*;
pub(crate) use self::returning::codegen_return; pub(crate) use self::returning::codegen_return;
@ -443,7 +443,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
RevealAllLayoutCx(fx.tcx).fn_abi_of_fn_ptr(fn_sig, extra_args) RevealAllLayoutCx(fx.tcx).fn_abi_of_fn_ptr(fn_sig, extra_args)
}; };
let is_cold = if fn_sig.abi() == Abi::RustCold { let is_cold = if fn_sig.abi() == ExternAbi::RustCold {
true true
} else { } else {
instance.is_some_and(|inst| { instance.is_some_and(|inst| {
@ -458,7 +458,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
} }
// Unpack arguments tuple for closures // Unpack arguments tuple for closures
let mut args = if fn_sig.abi() == Abi::RustCall { let mut args = if fn_sig.abi() == ExternAbi::RustCall {
let (self_arg, pack_arg) = match args { let (self_arg, pack_arg) = match args {
[pack_arg] => (None, codegen_call_argument_operand(fx, &pack_arg.node)), [pack_arg] => (None, codegen_call_argument_operand(fx, &pack_arg.node)),
[self_arg, pack_arg] => ( [self_arg, pack_arg] => (

View File

@ -1,8 +1,9 @@
//! Argument passing //! Argument passing
use cranelift_codegen::ir::{ArgumentExtension, ArgumentPurpose}; use cranelift_codegen::ir::{ArgumentExtension, ArgumentPurpose};
use rustc_target::abi::call::{ use rustc_abi::{Reg, RegKind};
ArgAbi, ArgAttributes, ArgExtension as RustcArgExtension, CastTarget, PassMode, Reg, RegKind, use rustc_target::callconv::{
ArgAbi, ArgAttributes, ArgExtension as RustcArgExtension, CastTarget, PassMode,
}; };
use smallvec::{SmallVec, smallvec}; use smallvec::{SmallVec, smallvec};

View File

@ -1,6 +1,6 @@
//! Return value handling //! Return value handling
use rustc_target::abi::call::{ArgAbi, PassMode}; use rustc_target::callconv::{ArgAbi, PassMode};
use smallvec::{SmallVec, smallvec}; use smallvec::{SmallVec, smallvec};
use crate::prelude::*; use crate::prelude::*;

View File

@ -934,7 +934,7 @@ fn is_wide_ptr<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool {
let dst = codegen_operand(fx, dst); let dst = codegen_operand(fx, dst);
let pointee = dst let pointee = dst
.layout() .layout()
.pointee_info_at(fx, rustc_target::abi::Size::ZERO) .pointee_info_at(fx, rustc_abi::Size::ZERO)
.expect("Expected pointer"); .expect("Expected pointer");
let dst = dst.load_scalar(fx); let dst = dst.load_scalar(fx);
let src = codegen_operand(fx, src).load_scalar(fx); let src = codegen_operand(fx, src).load_scalar(fx);

View File

@ -1,13 +1,13 @@
use cranelift_codegen::isa::TargetFrontendConfig; use cranelift_codegen::isa::TargetFrontendConfig;
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
use rustc_abi::{Float, Integer, Primitive};
use rustc_index::IndexVec; use rustc_index::IndexVec;
use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::TypeFoldable;
use rustc_middle::ty::layout::{ use rustc_middle::ty::layout::{
self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers,
}; };
use rustc_span::source_map::Spanned; use rustc_span::source_map::Spanned;
use rustc_target::abi::call::FnAbi; use rustc_target::callconv::FnAbi;
use rustc_target::abi::{Float, Integer, Primitive};
use rustc_target::spec::{HasTargetSpec, Target}; use rustc_target::spec::{HasTargetSpec, Target};
use crate::constant::ConstantCx; use crate::constant::ConstantCx;
@ -162,8 +162,8 @@ pub(crate) fn codegen_icmp_imm(
pub(crate) fn codegen_bitcast(fx: &mut FunctionCx<'_, '_, '_>, dst_ty: Type, val: Value) -> Value { pub(crate) fn codegen_bitcast(fx: &mut FunctionCx<'_, '_, '_>, dst_ty: Type, val: Value) -> Value {
let mut flags = MemFlags::new(); let mut flags = MemFlags::new();
flags.set_endianness(match fx.tcx.data_layout.endian { flags.set_endianness(match fx.tcx.data_layout.endian {
rustc_target::abi::Endian::Big => cranelift_codegen::ir::Endianness::Big, rustc_abi::Endian::Big => cranelift_codegen::ir::Endianness::Big,
rustc_target::abi::Endian::Little => cranelift_codegen::ir::Endianness::Little, rustc_abi::Endian::Little => cranelift_codegen::ir::Endianness::Little,
}); });
fx.bcx.ins().bitcast(dst_ty, flags, val) fx.bcx.ins().bitcast(dst_ty, flags, val)
} }
@ -333,8 +333,8 @@ fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
} }
} }
impl<'tcx> rustc_target::abi::HasDataLayout for FunctionCx<'_, '_, 'tcx> { impl<'tcx> rustc_abi::HasDataLayout for FunctionCx<'_, '_, 'tcx> {
fn data_layout(&self) -> &rustc_target::abi::TargetDataLayout { fn data_layout(&self) -> &rustc_abi::TargetDataLayout {
&self.tcx.data_layout &self.tcx.data_layout
} }
} }
@ -491,8 +491,8 @@ fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
} }
} }
impl<'tcx> rustc_target::abi::HasDataLayout for RevealAllLayoutCx<'tcx> { impl<'tcx> rustc_abi::HasDataLayout for RevealAllLayoutCx<'tcx> {
fn data_layout(&self) -> &rustc_target::abi::TargetDataLayout { fn data_layout(&self) -> &rustc_abi::TargetDataLayout {
&self.0.data_layout &self.0.data_layout
} }
} }

View File

@ -20,7 +20,7 @@
use rustc_hir::def_id::DefIdMap; use rustc_hir::def_id::DefIdMap;
use rustc_session::Session; use rustc_session::Session;
use rustc_span::{FileNameDisplayPreference, SourceFileHash, StableSourceFileId}; use rustc_span::{FileNameDisplayPreference, SourceFileHash, StableSourceFileId};
use rustc_target::abi::call::FnAbi; use rustc_target::callconv::FnAbi;
pub(crate) use self::emit::{DebugReloc, DebugRelocName}; pub(crate) use self::emit::{DebugReloc, DebugRelocName};
pub(crate) use self::types::TypeDebugContext; pub(crate) use self::types::TypeDebugContext;

View File

@ -2,6 +2,7 @@
//! standalone executable. //! standalone executable.
use std::fs::{self, File}; use std::fs::{self, File};
use std::io::BufWriter;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::Arc; use std::sync::Arc;
use std::thread::JoinHandle; use std::thread::JoinHandle;
@ -397,14 +398,19 @@ fn emit_module(
} }
let tmp_file = output_filenames.temp_path(OutputType::Object, Some(&name)); let tmp_file = output_filenames.temp_path(OutputType::Object, Some(&name));
let mut file = match File::create(&tmp_file) { let file = match File::create(&tmp_file) {
Ok(file) => file, Ok(file) => file,
Err(err) => return Err(format!("error creating object file: {}", err)), Err(err) => return Err(format!("error creating object file: {}", err)),
}; };
let mut file = BufWriter::new(file);
if let Err(err) = object.write_stream(&mut file) { if let Err(err) = object.write_stream(&mut file) {
return Err(format!("error writing object file: {}", err)); return Err(format!("error writing object file: {}", err));
} }
let file = match file.into_inner() {
Ok(file) => file,
Err(err) => return Err(format!("error writing object file: {}", err)),
};
prof.artifact_size("object_file", &*name, file.metadata().unwrap().len()); prof.artifact_size("object_file", &*name, file.metadata().unwrap().len());

View File

@ -464,7 +464,7 @@ fn allocate_stack_slots(&mut self) {
let new_slot_fn = |slot_size: &mut Size, reg_class: InlineAsmRegClass| { let new_slot_fn = |slot_size: &mut Size, reg_class: InlineAsmRegClass| {
let reg_size = let reg_size =
reg_class.supported_types(self.arch).iter().map(|(ty, _)| ty.size()).max().unwrap(); reg_class.supported_types(self.arch).iter().map(|(ty, _)| ty.size()).max().unwrap();
let align = rustc_target::abi::Align::from_bytes(reg_size.bytes()).unwrap(); let align = rustc_abi::Align::from_bytes(reg_size.bytes()).unwrap();
let offset = slot_size.align_to(align); let offset = slot_size.align_to(align);
*slot_size = offset + reg_size; *slot_size = offset + reg_size;
offset offset

View File

@ -1,7 +1,7 @@
//! Codegen SIMD intrinsics. //! Codegen SIMD intrinsics.
use cranelift_codegen::ir::immediates::Offset32; use cranelift_codegen::ir::immediates::Offset32;
use rustc_target::abi::Endian; use rustc_abi::Endian;
use super::*; use super::*;
use crate::prelude::*; use crate::prelude::*;

View File

@ -241,6 +241,8 @@ fn join_codegen(
sess: &Session, sess: &Session,
outputs: &OutputFilenames, outputs: &OutputFilenames,
) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) { ) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) {
let _timer = sess.timer("finish_ongoing_codegen");
ongoing_codegen.downcast::<driver::aot::OngoingCodegen>().unwrap().join( ongoing_codegen.downcast::<driver::aot::OngoingCodegen>().unwrap().join(
sess, sess,
outputs, outputs,

View File

@ -2,7 +2,7 @@
//! operations. //! operations.
use cranelift_codegen::ir::immediates::Offset32; use cranelift_codegen::ir::immediates::Offset32;
use rustc_target::abi::Align; use rustc_abi::Align;
use crate::prelude::*; use crate::prelude::*;

View File

@ -64,7 +64,7 @@
use cranelift_codegen::write::{FuncWriter, PlainWriter}; use cranelift_codegen::write::{FuncWriter, PlainWriter};
use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_session::config::{OutputFilenames, OutputType}; use rustc_session::config::{OutputFilenames, OutputType};
use rustc_target::abi::call::FnAbi; use rustc_target::callconv::FnAbi;
use crate::prelude::*; use crate::prelude::*;

View File

@ -1,11 +1,15 @@
use std::collections::hash_map::Entry;
use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext}; use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext};
use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::traits::*;
use rustc_data_structures::fx::FxHashMap;
use rustc_index::Idx; use rustc_index::Idx;
use rustc_index::bit_set::BitSet; use rustc_index::bit_set::BitSet;
use rustc_middle::mir::{Body, SourceScope}; use rustc_middle::mir::{Body, SourceScope};
use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::layout::FnAbiOf;
use rustc_middle::ty::{self, Instance}; use rustc_middle::ty::{self, Instance};
use rustc_session::config::DebugInfo; use rustc_session::config::DebugInfo;
use rustc_span::BytePos;
use super::metadata::file_metadata; use super::metadata::file_metadata;
use super::utils::DIB; use super::utils::DIB;
@ -37,10 +41,20 @@ pub(crate) fn compute_mir_scopes<'ll, 'tcx>(
None None
}; };
let mut instantiated = BitSet::new_empty(mir.source_scopes.len()); let mut instantiated = BitSet::new_empty(mir.source_scopes.len());
let mut discriminators = FxHashMap::default();
// Instantiate all scopes. // Instantiate all scopes.
for idx in 0..mir.source_scopes.len() { for idx in 0..mir.source_scopes.len() {
let scope = SourceScope::new(idx); let scope = SourceScope::new(idx);
make_mir_scope(cx, instance, mir, &variables, debug_context, &mut instantiated, scope); make_mir_scope(
cx,
instance,
mir,
&variables,
debug_context,
&mut instantiated,
&mut discriminators,
scope,
);
} }
assert!(instantiated.count() == mir.source_scopes.len()); assert!(instantiated.count() == mir.source_scopes.len());
} }
@ -52,6 +66,7 @@ fn make_mir_scope<'ll, 'tcx>(
variables: &Option<BitSet<SourceScope>>, variables: &Option<BitSet<SourceScope>>,
debug_context: &mut FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>, debug_context: &mut FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>,
instantiated: &mut BitSet<SourceScope>, instantiated: &mut BitSet<SourceScope>,
discriminators: &mut FxHashMap<BytePos, u32>,
scope: SourceScope, scope: SourceScope,
) { ) {
if instantiated.contains(scope) { if instantiated.contains(scope) {
@ -60,7 +75,16 @@ fn make_mir_scope<'ll, 'tcx>(
let scope_data = &mir.source_scopes[scope]; let scope_data = &mir.source_scopes[scope];
let parent_scope = if let Some(parent) = scope_data.parent_scope { let parent_scope = if let Some(parent) = scope_data.parent_scope {
make_mir_scope(cx, instance, mir, variables, debug_context, instantiated, parent); make_mir_scope(
cx,
instance,
mir,
variables,
debug_context,
instantiated,
discriminators,
parent,
);
debug_context.scopes[parent] debug_context.scopes[parent]
} else { } else {
// The root is the function itself. // The root is the function itself.
@ -117,7 +141,37 @@ fn make_mir_scope<'ll, 'tcx>(
// FIXME(eddyb) this doesn't account for the macro-related // FIXME(eddyb) this doesn't account for the macro-related
// `Span` fixups that `rustc_codegen_ssa::mir::debuginfo` does. // `Span` fixups that `rustc_codegen_ssa::mir::debuginfo` does.
let callsite_scope = parent_scope.adjust_dbg_scope_for_span(cx, callsite_span); let callsite_scope = parent_scope.adjust_dbg_scope_for_span(cx, callsite_span);
cx.dbg_loc(callsite_scope, parent_scope.inlined_at, callsite_span) let loc = cx.dbg_loc(callsite_scope, parent_scope.inlined_at, callsite_span);
// NB: In order to produce proper debug info for variables (particularly
// arguments) in multiply-inline functions, LLVM expects to see a single
// DILocalVariable with multiple different DILocations in the IR. While
// the source information for each DILocation would be identical, their
// inlinedAt attributes will be unique to the particular callsite.
//
// We generate DILocations here based on the callsite's location in the
// source code. A single location in the source code usually can't
// produce multiple distinct calls so this mostly works, until
// proc-macros get involved. A proc-macro can generate multiple calls
// at the same span, which breaks the assumption that we're going to
// produce a unique DILocation for every scope we process here. We
// have to explicitly add discriminators if we see inlines into the
// same source code location.
//
// Note further that we can't key this hashtable on the span itself,
// because these spans could have distinct SyntaxContexts. We have
// to key on exactly what we're giving to LLVM.
match discriminators.entry(callsite_span.lo()) {
Entry::Occupied(mut o) => {
*o.get_mut() += 1;
unsafe { llvm::LLVMRustDILocationCloneWithBaseDiscriminator(loc, *o.get()) }
.expect("Failed to encode discriminator in DILocation")
}
Entry::Vacant(v) => {
v.insert(0);
loc
}
}
}); });
debug_context.scopes[scope] = DebugScope { debug_context.scopes[scope] = DebugScope {

View File

@ -204,7 +204,7 @@ pub enum DLLStorageClass {
DllExport = 2, // Function to be accessible from DLL. DllExport = 2, // Function to be accessible from DLL.
} }
/// Matches LLVMRustAttribute in LLVMWrapper.h /// Must match the layout of `LLVMRustAttributeKind`.
/// Semantically a subset of the C++ enum llvm::Attribute::AttrKind, /// Semantically a subset of the C++ enum llvm::Attribute::AttrKind,
/// though it is not ABI compatible (since it's a C++ enum) /// though it is not ABI compatible (since it's a C++ enum)
#[repr(C)] #[repr(C)]
@ -1766,11 +1766,9 @@ pub fn LLVMRustBuildAtomicStore<'a>(
pub fn LLVMRustGetLastError() -> *const c_char; pub fn LLVMRustGetLastError() -> *const c_char;
/// Prints the timing information collected by `-Ztime-llvm-passes`. /// Prints the timing information collected by `-Ztime-llvm-passes`.
#[expect(improper_ctypes)]
pub(crate) fn LLVMRustPrintPassTimings(OutStr: &RustString); pub(crate) fn LLVMRustPrintPassTimings(OutStr: &RustString);
/// Prints the statistics collected by `-Zprint-codegen-stats`. /// Prints the statistics collected by `-Zprint-codegen-stats`.
#[expect(improper_ctypes)]
pub(crate) fn LLVMRustPrintStatistics(OutStr: &RustString); pub(crate) fn LLVMRustPrintStatistics(OutStr: &RustString);
/// Prepares inline assembly. /// Prepares inline assembly.
@ -1791,7 +1789,6 @@ pub fn LLVMRustInlineAsmVerify(
ConstraintsLen: size_t, ConstraintsLen: size_t,
) -> bool; ) -> bool;
#[allow(improper_ctypes)]
pub(crate) fn LLVMRustCoverageWriteFilenamesToBuffer( pub(crate) fn LLVMRustCoverageWriteFilenamesToBuffer(
Filenames: *const *const c_char, Filenames: *const *const c_char,
FilenamesLen: size_t, FilenamesLen: size_t,
@ -1800,7 +1797,6 @@ pub(crate) fn LLVMRustCoverageWriteFilenamesToBuffer(
BufferOut: &RustString, BufferOut: &RustString,
); );
#[allow(improper_ctypes)]
pub(crate) fn LLVMRustCoverageWriteFunctionMappingsToBuffer( pub(crate) fn LLVMRustCoverageWriteFunctionMappingsToBuffer(
VirtualFileMappingIDs: *const c_uint, VirtualFileMappingIDs: *const c_uint,
NumVirtualFileMappingIDs: size_t, NumVirtualFileMappingIDs: size_t,
@ -1824,13 +1820,10 @@ pub(crate) fn LLVMRustCoverageCreatePGOFuncNameVar(
) -> &Value; ) -> &Value;
pub(crate) fn LLVMRustCoverageHashBytes(Bytes: *const c_char, NumBytes: size_t) -> u64; pub(crate) fn LLVMRustCoverageHashBytes(Bytes: *const c_char, NumBytes: size_t) -> u64;
#[allow(improper_ctypes)]
pub(crate) fn LLVMRustCoverageWriteCovmapSectionNameToString(M: &Module, OutStr: &RustString); pub(crate) fn LLVMRustCoverageWriteCovmapSectionNameToString(M: &Module, OutStr: &RustString);
#[allow(improper_ctypes)]
pub(crate) fn LLVMRustCoverageWriteCovfunSectionNameToString(M: &Module, OutStr: &RustString); pub(crate) fn LLVMRustCoverageWriteCovfunSectionNameToString(M: &Module, OutStr: &RustString);
#[allow(improper_ctypes)]
pub(crate) fn LLVMRustCoverageWriteCovmapVarNameToString(OutStr: &RustString); pub(crate) fn LLVMRustCoverageWriteCovmapVarNameToString(OutStr: &RustString);
pub(crate) fn LLVMRustCoverageMappingVersion() -> u32; pub(crate) fn LLVMRustCoverageMappingVersion() -> u32;
@ -2181,18 +2174,19 @@ pub fn LLVMRustDIBuilderCreateDebugLocation<'a>(
Scope: &'a DIScope, Scope: &'a DIScope,
InlinedAt: Option<&'a DILocation>, InlinedAt: Option<&'a DILocation>,
) -> &'a DILocation; ) -> &'a DILocation;
pub fn LLVMRustDILocationCloneWithBaseDiscriminator<'a>(
Location: &'a DILocation,
BD: c_uint,
) -> Option<&'a DILocation>;
pub fn LLVMRustDIBuilderCreateOpDeref() -> u64; pub fn LLVMRustDIBuilderCreateOpDeref() -> u64;
pub fn LLVMRustDIBuilderCreateOpPlusUconst() -> u64; pub fn LLVMRustDIBuilderCreateOpPlusUconst() -> u64;
pub fn LLVMRustDIBuilderCreateOpLLVMFragment() -> u64; pub fn LLVMRustDIBuilderCreateOpLLVMFragment() -> u64;
#[allow(improper_ctypes)]
pub fn LLVMRustWriteTypeToString(Type: &Type, s: &RustString); pub fn LLVMRustWriteTypeToString(Type: &Type, s: &RustString);
#[allow(improper_ctypes)]
pub fn LLVMRustWriteValueToString(value_ref: &Value, s: &RustString); pub fn LLVMRustWriteValueToString(value_ref: &Value, s: &RustString);
pub fn LLVMRustHasFeature(T: &TargetMachine, s: *const c_char) -> bool; pub fn LLVMRustHasFeature(T: &TargetMachine, s: *const c_char) -> bool;
#[allow(improper_ctypes)]
pub(crate) fn LLVMRustPrintTargetCPUs(TM: &TargetMachine, OutStr: &RustString); pub(crate) fn LLVMRustPrintTargetCPUs(TM: &TargetMachine, OutStr: &RustString);
pub fn LLVMRustGetTargetFeaturesCount(T: &TargetMachine) -> size_t; pub fn LLVMRustGetTargetFeaturesCount(T: &TargetMachine) -> size_t;
pub fn LLVMRustGetTargetFeature( pub fn LLVMRustGetTargetFeature(
@ -2297,10 +2291,8 @@ pub fn LLVMRustArchiveIteratorNext<'a>(
pub fn LLVMRustArchiveIteratorFree<'a>(AIR: &'a mut ArchiveIterator<'a>); pub fn LLVMRustArchiveIteratorFree<'a>(AIR: &'a mut ArchiveIterator<'a>);
pub fn LLVMRustDestroyArchive(AR: &'static mut Archive); pub fn LLVMRustDestroyArchive(AR: &'static mut Archive);
#[allow(improper_ctypes)]
pub fn LLVMRustWriteTwineToString(T: &Twine, s: &RustString); pub fn LLVMRustWriteTwineToString(T: &Twine, s: &RustString);
#[allow(improper_ctypes)]
pub fn LLVMRustUnpackOptimizationDiagnostic<'a>( pub fn LLVMRustUnpackOptimizationDiagnostic<'a>(
DI: &'a DiagnosticInfo, DI: &'a DiagnosticInfo,
pass_name_out: &RustString, pass_name_out: &RustString,
@ -2318,7 +2310,6 @@ pub fn LLVMRustUnpackInlineAsmDiagnostic<'a>(
message_out: &mut Option<&'a Twine>, message_out: &mut Option<&'a Twine>,
); );
#[allow(improper_ctypes)]
pub fn LLVMRustWriteDiagnosticInfoToString(DI: &DiagnosticInfo, s: &RustString); pub fn LLVMRustWriteDiagnosticInfoToString(DI: &DiagnosticInfo, s: &RustString);
pub fn LLVMRustGetDiagInfoKind(DI: &DiagnosticInfo) -> DiagnosticKind; pub fn LLVMRustGetDiagInfoKind(DI: &DiagnosticInfo) -> DiagnosticKind;
@ -2327,7 +2318,6 @@ pub fn LLVMRustGetSMDiagnostic<'a>(
cookie_out: &mut c_uint, cookie_out: &mut c_uint,
) -> &'a SMDiagnostic; ) -> &'a SMDiagnostic;
#[allow(improper_ctypes)]
pub fn LLVMRustUnpackSMDiagnostic( pub fn LLVMRustUnpackSMDiagnostic(
d: &SMDiagnostic, d: &SMDiagnostic,
message_out: &RustString, message_out: &RustString,
@ -2374,7 +2364,6 @@ pub fn LLVMRustWriteImportLibrary(
pub fn LLVMRustModuleBufferLen(p: &ModuleBuffer) -> usize; pub fn LLVMRustModuleBufferLen(p: &ModuleBuffer) -> usize;
pub fn LLVMRustModuleBufferFree(p: &'static mut ModuleBuffer); pub fn LLVMRustModuleBufferFree(p: &'static mut ModuleBuffer);
pub fn LLVMRustModuleCost(M: &Module) -> u64; pub fn LLVMRustModuleCost(M: &Module) -> u64;
#[allow(improper_ctypes)]
pub fn LLVMRustModuleInstructionStats(M: &Module, Str: &RustString); pub fn LLVMRustModuleInstructionStats(M: &Module, Str: &RustString);
pub fn LLVMRustThinLTOBufferCreate( pub fn LLVMRustThinLTOBufferCreate(
@ -2427,7 +2416,6 @@ pub fn LLVMRustLinkerAdd(
bytecode_len: usize, bytecode_len: usize,
) -> bool; ) -> bool;
pub fn LLVMRustLinkerFree<'a>(linker: &'a mut Linker<'a>); pub fn LLVMRustLinkerFree<'a>(linker: &'a mut Linker<'a>);
#[allow(improper_ctypes)]
pub fn LLVMRustComputeLTOCacheKey( pub fn LLVMRustComputeLTOCacheKey(
key_out: &RustString, key_out: &RustString,
mod_id: *const c_char, mod_id: *const c_char,
@ -2450,7 +2438,6 @@ pub fn LLVMRustContextConfigureDiagnosticHandler(
pgo_available: bool, pgo_available: bool,
); );
#[allow(improper_ctypes)]
pub fn LLVMRustGetMangledName(V: &Value, out: &RustString); pub fn LLVMRustGetMangledName(V: &Value, out: &RustString);
pub fn LLVMRustGetElementTypeArgIndex(CallSite: &Value) -> i32; pub fn LLVMRustGetElementTypeArgIndex(CallSite: &Value) -> i32;

View File

@ -1,6 +1,5 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use std::cell::RefCell;
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use std::ops::Deref; use std::ops::Deref;
use std::ptr; use std::ptr;
@ -301,15 +300,11 @@ pub fn set_value_name(value: &Value, name: &[u8]) {
} }
pub fn build_string(f: impl FnOnce(&RustString)) -> Result<String, FromUtf8Error> { pub fn build_string(f: impl FnOnce(&RustString)) -> Result<String, FromUtf8Error> {
let sr = RustString { bytes: RefCell::new(Vec::new()) }; String::from_utf8(RustString::build_byte_buffer(f))
f(&sr);
String::from_utf8(sr.bytes.into_inner())
} }
pub fn build_byte_buffer(f: impl FnOnce(&RustString)) -> Vec<u8> { pub fn build_byte_buffer(f: impl FnOnce(&RustString)) -> Vec<u8> {
let sr = RustString { bytes: RefCell::new(Vec::new()) }; RustString::build_byte_buffer(f)
f(&sr);
sr.bytes.into_inner()
} }
pub fn twine_to_string(tr: &Twine) -> String { pub fn twine_to_string(tr: &Twine) -> String {

View File

@ -228,6 +228,8 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
"x86" "x86"
} else if sess.target.arch == "arm64ec" { } else if sess.target.arch == "arm64ec" {
"aarch64" "aarch64"
} else if sess.target.arch == "sparc64" {
"sparc"
} else { } else {
&*sess.target.arch &*sess.target.arch
}; };
@ -280,6 +282,13 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
// Support for `wide-arithmetic` will first land in LLVM 20 as part of // Support for `wide-arithmetic` will first land in LLVM 20 as part of
// llvm/llvm-project#111598 // llvm/llvm-project#111598
("wasm32" | "wasm64", "wide-arithmetic") if get_version() < (20, 0, 0) => None, ("wasm32" | "wasm64", "wide-arithmetic") if get_version() < (20, 0, 0) => None,
("sparc", "leoncasa") => Some(LLVMFeature::new("hasleoncasa")),
// In LLVM 19, there is no `v8plus` feature and `v9` means "SPARC-V9 instruction available and SPARC-V8+ ABI used".
// https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/llvm/lib/Target/Sparc/MCTargetDesc/SparcELFObjectWriter.cpp#L27-L28
// Before LLVM 19, there is no `v8plus` feature and `v9` means "SPARC-V9 instruction available".
// https://github.com/llvm/llvm-project/blob/llvmorg-18.1.0/llvm/lib/Target/Sparc/MCTargetDesc/SparcELFObjectWriter.cpp#L26
("sparc", "v8plus") if get_version().0 == 19 => Some(LLVMFeature::new("v9")),
("sparc", "v8plus") if get_version().0 < 19 => None,
(_, s) => Some(LLVMFeature::new(s)), (_, s) => Some(LLVMFeature::new(s)),
} }
} }
@ -619,6 +628,8 @@ pub(crate) fn global_llvm_features(
.features .features
.split(',') .split(',')
.filter(|v| !v.is_empty() && backend_feature_name(sess, v).is_some()) .filter(|v| !v.is_empty() && backend_feature_name(sess, v).is_some())
// Drop +v8plus feature introduced in LLVM 20.
.filter(|v| *v != "+v8plus" || get_version() >= (20, 0, 0))
.map(String::from), .map(String::from),
); );

View File

@ -3305,23 +3305,6 @@ fn add_lld_args(
let self_contained_cli = sess.opts.cg.link_self_contained.is_linker_enabled(); let self_contained_cli = sess.opts.cg.link_self_contained.is_linker_enabled();
let self_contained_target = self_contained_components.is_linker_enabled(); let self_contained_target = self_contained_components.is_linker_enabled();
// FIXME: in the future, codegen backends may need to have more control over this process: they
// don't always support all the features the linker expects here, and vice versa. For example,
// at the time of writing this, lld expects a newer style of aarch64 TLS relocations that
// cranelift doesn't implement yet. That in turn can impact whether linking would succeed on
// such a target when using the `cg_clif` backend and lld.
//
// Until interactions between backends and linker features are expressible, we limit target
// specs to opt-in to lld only when we're on the llvm backend, where it's expected to work and
// tested on CI. As usual, the CLI still has precedence over this, so that users and developers
// can still override this default when needed (e.g. for tests).
let uses_llvm_backend =
matches!(sess.opts.unstable_opts.codegen_backend.as_deref(), None | Some("llvm"));
if !uses_llvm_backend && !self_contained_cli && sess.opts.cg.linker_flavor.is_none() {
// We bail if we're not using llvm and lld was not explicitly requested on the CLI.
return;
}
let self_contained_linker = self_contained_cli || self_contained_target; let self_contained_linker = self_contained_cli || self_contained_target;
if self_contained_linker && !sess.opts.cg.link_self_contained.is_linker_disabled() { if self_contained_linker && !sess.opts.cg.link_self_contained.is_linker_disabled() {
let mut linker_path_exists = false; let mut linker_path_exists = false;

View File

@ -212,7 +212,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
"riscv32" => (Architecture::Riscv32, None), "riscv32" => (Architecture::Riscv32, None),
"riscv64" => (Architecture::Riscv64, None), "riscv64" => (Architecture::Riscv64, None),
"sparc" => { "sparc" => {
if sess.target.options.cpu == "v9" { if sess.unstable_target_features.contains(&sym::v8plus) {
// Target uses V8+, aka EM_SPARC32PLUS, aka 64-bit V9 but in 32-bit mode // Target uses V8+, aka EM_SPARC32PLUS, aka 64-bit V9 but in 32-bit mode
(Architecture::Sparc32Plus, None) (Architecture::Sparc32Plus, None)
} else { } else {

View File

@ -25,6 +25,10 @@ const_eval_closure_fndef_not_const =
function defined here, but it is not `const` function defined here, but it is not `const`
const_eval_closure_non_const = const_eval_closure_non_const =
cannot call non-const closure in {const_eval_const_context}s cannot call non-const closure in {const_eval_const_context}s
const_eval_conditionally_const_call =
cannot call conditionally-const {$def_descr} `{$def_path_str}` in {const_eval_const_context}s
const_eval_consider_dereferencing = const_eval_consider_dereferencing =
consider dereferencing here consider dereferencing here

View File

@ -15,7 +15,7 @@
use rustc_middle::mir::*; use rustc_middle::mir::*;
use rustc_middle::span_bug; use rustc_middle::span_bug;
use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TypeVisitableExt}; use rustc_middle::ty::{self, Ty, TypeVisitableExt};
use rustc_mir_dataflow::Analysis; use rustc_mir_dataflow::Analysis;
use rustc_mir_dataflow::impls::MaybeStorageLive; use rustc_mir_dataflow::impls::MaybeStorageLive;
use rustc_mir_dataflow::storage::always_storage_live_locals; use rustc_mir_dataflow::storage::always_storage_live_locals;
@ -361,31 +361,21 @@ fn place_may_escape(&mut self, place: &Place<'_>) -> bool {
!is_transient !is_transient
} }
/// Returns whether there are const-conditions.
fn revalidate_conditional_constness( fn revalidate_conditional_constness(
&mut self, &mut self,
callee: DefId, callee: DefId,
callee_args: ty::GenericArgsRef<'tcx>, callee_args: ty::GenericArgsRef<'tcx>,
call_source: CallSource,
call_span: Span, call_span: Span,
) { ) -> bool {
let tcx = self.tcx; let tcx = self.tcx;
if !tcx.is_conditionally_const(callee) { if !tcx.is_conditionally_const(callee) {
return; return false;
} }
let const_conditions = tcx.const_conditions(callee).instantiate(tcx, callee_args); let const_conditions = tcx.const_conditions(callee).instantiate(tcx, callee_args);
// If there are any const conditions on this fn and `const_trait_impl` if const_conditions.is_empty() {
// is not enabled, simply bail. We shouldn't be able to call conditionally return false;
// const functions on stable.
if !const_conditions.is_empty() && !tcx.features().const_trait_impl() {
self.check_op(ops::FnCallNonConst {
callee,
args: callee_args,
span: call_span,
call_source,
feature: Some(sym::const_trait_impl),
});
return;
} }
let infcx = tcx.infer_ctxt().build(self.body.typing_mode(tcx)); let infcx = tcx.infer_ctxt().build(self.body.typing_mode(tcx));
@ -421,6 +411,8 @@ fn revalidate_conditional_constness(
tcx.dcx() tcx.dcx()
.span_delayed_bug(call_span, "this should have reported a ~const error in HIR"); .span_delayed_bug(call_span, "this should have reported a ~const error in HIR");
} }
true
} }
} }
@ -627,11 +619,11 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location
_ => unreachable!(), _ => unreachable!(),
}; };
let ConstCx { tcx, body, param_env, .. } = *self.ccx; let ConstCx { tcx, body, .. } = *self.ccx;
let fn_ty = func.ty(body, tcx); let fn_ty = func.ty(body, tcx);
let (mut callee, mut fn_args) = match *fn_ty.kind() { let (callee, fn_args) = match *fn_ty.kind() {
ty::FnDef(def_id, fn_args) => (def_id, fn_args), ty::FnDef(def_id, fn_args) => (def_id, fn_args),
ty::FnPtr(..) => { ty::FnPtr(..) => {
@ -645,57 +637,38 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location
} }
}; };
self.revalidate_conditional_constness(callee, fn_args, call_source, *fn_span); let has_const_conditions =
self.revalidate_conditional_constness(callee, fn_args, *fn_span);
let mut is_trait = false;
// Attempting to call a trait method? // Attempting to call a trait method?
if let Some(trait_did) = tcx.trait_of_item(callee) { if let Some(trait_did) = tcx.trait_of_item(callee) {
// We can't determine the actual callee here, so we have to do different checks
// than usual.
trace!("attempting to call a trait method"); trace!("attempting to call a trait method");
let trait_is_const = tcx.is_const_trait(trait_did); let trait_is_const = tcx.is_const_trait(trait_did);
// trait method calls are only permitted when `effects` is enabled.
// typeck ensures the conditions for calling a const trait method are met,
// so we only error if the trait isn't const. We try to resolve the trait
// into the concrete method, and uses that for const stability checks.
// FIXME(const_trait_impl) we might consider moving const stability checks
// to typeck as well.
if tcx.features().const_trait_impl() && trait_is_const {
// This skips the check below that ensures we only call `const fn`.
is_trait = true;
if let Ok(Some(instance)) = if trait_is_const {
Instance::try_resolve(tcx, param_env, callee, fn_args) // Trait calls are always conditionally-const.
&& let InstanceKind::Item(def) = instance.def self.check_op(ops::ConditionallyConstCall { callee, args: fn_args });
{ // FIXME(const_trait_impl): do a more fine-grained check whether this
// Resolve a trait method call to its concrete implementation, which may be in a // particular trait can be const-stably called.
// `const` trait impl. This is only used for the const stability check below, since
// we want to look at the concrete impl's stability.
fn_args = instance.args;
callee = def;
}
} else { } else {
// if the trait is const but the user has not enabled the feature(s), // Not even a const trait.
// suggest them.
let feature = if trait_is_const {
Some(if tcx.features().const_trait_impl() {
sym::effects
} else {
sym::const_trait_impl
})
} else {
None
};
self.check_op(ops::FnCallNonConst { self.check_op(ops::FnCallNonConst {
callee, callee,
args: fn_args, args: fn_args,
span: *fn_span, span: *fn_span,
call_source, call_source,
feature,
}); });
// If we allowed this, we're in miri-unleashed mode, so we might
// as well skip the remaining checks.
return;
} }
// That's all we can check here.
return;
}
// Even if we know the callee, ensure we can use conditionally-const calls.
if has_const_conditions {
self.check_op(ops::ConditionallyConstCall { callee, args: fn_args });
} }
// At this point, we are calling a function, `callee`, whose `DefId` is known... // At this point, we are calling a function, `callee`, whose `DefId` is known...
@ -783,14 +756,12 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location
return; return;
} }
// Trait functions are not `const fn` so we have to skip them here. if !tcx.is_const_fn(callee) {
if !tcx.is_const_fn(callee) && !is_trait {
self.check_op(ops::FnCallNonConst { self.check_op(ops::FnCallNonConst {
callee, callee,
args: fn_args, args: fn_args,
span: *fn_span, span: *fn_span,
call_source, call_source,
feature: None,
}); });
// If we allowed this, we're in miri-unleashed mode, so we might // If we allowed this, we're in miri-unleashed mode, so we might
// as well skip the remaining checks. // as well skip the remaining checks.

View File

@ -70,6 +70,37 @@ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
} }
} }
/// A call to a function that is in a trait, or has trait bounds that make it conditionally-const.
#[derive(Debug)]
pub(crate) struct ConditionallyConstCall<'tcx> {
pub callee: DefId,
pub args: GenericArgsRef<'tcx>,
}
impl<'tcx> NonConstOp<'tcx> for ConditionallyConstCall<'tcx> {
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
// We use the `const_trait_impl` gate for all conditionally-const calls.
Status::Unstable {
gate: sym::const_trait_impl,
safe_to_expose_on_stable: false,
// We don't want the "mark the callee as `#[rustc_const_stable_indirect]`" hint
is_function_call: false,
}
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
ccx.tcx.sess.create_feature_err(
errors::ConditionallyConstCall {
span,
def_path_str: ccx.tcx.def_path_str_with_args(self.callee, self.args),
def_descr: ccx.tcx.def_descr(self.callee),
kind: ccx.const_kind(),
},
sym::const_trait_impl,
)
}
}
/// A function call where the callee is not marked as `const`. /// A function call where the callee is not marked as `const`.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(crate) struct FnCallNonConst<'tcx> { pub(crate) struct FnCallNonConst<'tcx> {
@ -77,7 +108,6 @@ pub(crate) struct FnCallNonConst<'tcx> {
pub args: GenericArgsRef<'tcx>, pub args: GenericArgsRef<'tcx>,
pub span: Span, pub span: Span,
pub call_source: CallSource, pub call_source: CallSource,
pub feature: Option<Symbol>,
} }
impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
@ -85,7 +115,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
#[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::untranslatable_diagnostic)]
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> { fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> {
let FnCallNonConst { callee, args, span, call_source, feature } = *self; let FnCallNonConst { callee, args, span, call_source } = *self;
let ConstCx { tcx, param_env, .. } = *ccx; let ConstCx { tcx, param_env, .. } = *ccx;
let caller = ccx.def_id(); let caller = ccx.def_id();
@ -285,14 +315,6 @@ macro_rules! error {
ccx.const_kind(), ccx.const_kind(),
)); ));
if let Some(feature) = feature {
ccx.tcx.disabled_nightly_features(
&mut err,
Some(ccx.tcx.local_def_id_to_hir_id(caller)),
[(String::new(), feature)],
);
}
if let ConstContext::Static(_) = ccx.const_kind() { if let ConstContext::Static(_) = ccx.const_kind() {
err.note(fluent_generated::const_eval_lazy_lock); err.note(fluent_generated::const_eval_lazy_lock);
} }
@ -398,15 +420,8 @@ fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
let msg = format!("{:#}s are not allowed in {}s", self.0, ccx.const_kind()); let msg = format!("{:#}s are not allowed in {}s", self.0, ccx.const_kind());
if let hir::CoroutineKind::Desugared( if let Status::Unstable { gate, .. } = self.status_in_item(ccx) {
hir::CoroutineDesugaring::Async, ccx.tcx.sess.create_feature_err(errors::UnallowedOpInConstContext { span, msg }, gate)
hir::CoroutineSource::Block,
) = self.0
{
ccx.tcx.sess.create_feature_err(
errors::UnallowedOpInConstContext { span, msg },
sym::const_async_blocks,
)
} else { } else {
ccx.dcx().create_err(errors::UnallowedOpInConstContext { span, msg }) ccx.dcx().create_err(errors::UnallowedOpInConstContext { span, msg })
} }

View File

@ -472,8 +472,9 @@ fn report_validation_error<'tcx>(
backtrace.print_backtrace(); backtrace.print_backtrace();
let bytes = ecx.print_alloc_bytes_for_diagnostics(alloc_id); let bytes = ecx.print_alloc_bytes_for_diagnostics(alloc_id);
let (size, align, _) = ecx.get_alloc_info(alloc_id); let info = ecx.get_alloc_info(alloc_id);
let raw_bytes = errors::RawBytesNote { size: size.bytes(), align: align.bytes(), bytes }; let raw_bytes =
errors::RawBytesNote { size: info.size.bytes(), align: info.align.bytes(), bytes };
crate::const_eval::report( crate::const_eval::report(
*ecx.tcx, *ecx.tcx,

View File

@ -176,6 +176,16 @@ pub(crate) struct NonConstFmtMacroCall {
pub kind: ConstContext, pub kind: ConstContext,
} }
#[derive(Diagnostic)]
#[diag(const_eval_conditionally_const_call)]
pub(crate) struct ConditionallyConstCall {
#[primary_span]
pub span: Span,
pub def_path_str: String,
pub def_descr: &'static str,
pub kind: ConstContext,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(const_eval_non_const_fn_call, code = E0015)] #[diag(const_eval_non_const_fn_call, code = E0015)]
pub(crate) struct NonConstFnCall { pub(crate) struct NonConstFnCall {

View File

@ -14,10 +14,9 @@
use rustc_abi::{Align, HasDataLayout, Size}; use rustc_abi::{Align, HasDataLayout, Size};
use rustc_ast::Mutability; use rustc_ast::Mutability;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_hir::def::DefKind;
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::mir::display_allocation; use rustc_middle::mir::display_allocation;
use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
use tracing::{debug, instrument, trace}; use tracing::{debug, instrument, trace};
use super::{ use super::{
@ -72,6 +71,21 @@ pub enum AllocKind {
Dead, Dead,
} }
/// Metadata about an `AllocId`.
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct AllocInfo {
pub size: Size,
pub align: Align,
pub kind: AllocKind,
pub mutbl: Mutability,
}
impl AllocInfo {
fn new(size: Size, align: Align, kind: AllocKind, mutbl: Mutability) -> Self {
Self { size, align, kind, mutbl }
}
}
/// The value of a function pointer. /// The value of a function pointer.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum FnVal<'tcx, Other> { pub enum FnVal<'tcx, Other> {
@ -524,17 +538,22 @@ fn is_offset_misaligned(offset: u64, align: Align) -> Option<Misalignment> {
match self.ptr_try_get_alloc_id(ptr, 0) { match self.ptr_try_get_alloc_id(ptr, 0) {
Err(addr) => is_offset_misaligned(addr, align), Err(addr) => is_offset_misaligned(addr, align),
Ok((alloc_id, offset, _prov)) => { Ok((alloc_id, offset, _prov)) => {
let (_size, alloc_align, kind) = self.get_alloc_info(alloc_id); let alloc_info = self.get_alloc_info(alloc_id);
if let Some(misalign) = if let Some(misalign) = M::alignment_check(
M::alignment_check(self, alloc_id, alloc_align, kind, offset, align) self,
{ alloc_id,
alloc_info.align,
alloc_info.kind,
offset,
align,
) {
Some(misalign) Some(misalign)
} else if M::Provenance::OFFSET_IS_ADDR { } else if M::Provenance::OFFSET_IS_ADDR {
is_offset_misaligned(ptr.addr().bytes(), align) is_offset_misaligned(ptr.addr().bytes(), align)
} else { } else {
// Check allocation alignment and offset alignment. // Check allocation alignment and offset alignment.
if alloc_align.bytes() < align.bytes() { if alloc_info.align.bytes() < align.bytes() {
Some(Misalignment { has: alloc_align, required: align }) Some(Misalignment { has: alloc_info.align, required: align })
} else { } else {
is_offset_misaligned(offset.bytes(), align) is_offset_misaligned(offset.bytes(), align)
} }
@ -818,82 +837,45 @@ pub fn is_alloc_live(&self, id: AllocId) -> bool {
/// Obtain the size and alignment of an allocation, even if that allocation has /// Obtain the size and alignment of an allocation, even if that allocation has
/// been deallocated. /// been deallocated.
pub fn get_alloc_info(&self, id: AllocId) -> (Size, Align, AllocKind) { pub fn get_alloc_info(&self, id: AllocId) -> AllocInfo {
// # Regular allocations // # Regular allocations
// Don't use `self.get_raw` here as that will // Don't use `self.get_raw` here as that will
// a) cause cycles in case `id` refers to a static // a) cause cycles in case `id` refers to a static
// b) duplicate a global's allocation in miri // b) duplicate a global's allocation in miri
if let Some((_, alloc)) = self.memory.alloc_map.get(id) { if let Some((_, alloc)) = self.memory.alloc_map.get(id) {
return (alloc.size(), alloc.align, AllocKind::LiveData); return AllocInfo::new(
alloc.size(),
alloc.align,
AllocKind::LiveData,
alloc.mutability,
);
} }
// # Function pointers // # Function pointers
// (both global from `alloc_map` and local from `extra_fn_ptr_map`) // (both global from `alloc_map` and local from `extra_fn_ptr_map`)
if self.get_fn_alloc(id).is_some() { if self.get_fn_alloc(id).is_some() {
return (Size::ZERO, Align::ONE, AllocKind::Function); return AllocInfo::new(Size::ZERO, Align::ONE, AllocKind::Function, Mutability::Not);
} }
// # Statics // # Global allocations
// Can't do this in the match argument, we may get cycle errors since the lock would if let Some(global_alloc) = self.tcx.try_get_global_alloc(id) {
// be held throughout the match. let (size, align) = global_alloc.size_and_align(*self.tcx, self.param_env);
match self.tcx.try_get_global_alloc(id) { let mutbl = global_alloc.mutability(*self.tcx, self.param_env);
Some(GlobalAlloc::Static(def_id)) => { let kind = match global_alloc {
// Thread-local statics do not have a constant address. They *must* be accessed via GlobalAlloc::Static { .. } | GlobalAlloc::Memory { .. } => AllocKind::LiveData,
// `ThreadLocalRef`; we can never have a pointer to them as a regular constant value. GlobalAlloc::Function { .. } => bug!("We already checked function pointers above"),
assert!(!self.tcx.is_thread_local_static(def_id)); GlobalAlloc::VTable { .. } => AllocKind::VTable,
};
let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { return AllocInfo::new(size, align, kind, mutbl);
bug!("GlobalAlloc::Static is not a static")
};
let (size, align) = if nested {
// Nested anonymous statics are untyped, so let's get their
// size and alignment from the allocation itself. This always
// succeeds, as the query is fed at DefId creation time, so no
// evaluation actually occurs.
let alloc = self.tcx.eval_static_initializer(def_id).unwrap();
(alloc.0.size(), alloc.0.align)
} else {
// Use size and align of the type for everything else. We need
// to do that to
// * avoid cycle errors in case of self-referential statics,
// * be able to get information on extern statics.
let ty = self
.tcx
.type_of(def_id)
.no_bound_vars()
.expect("statics should not have generic parameters");
let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap();
assert!(layout.is_sized());
(layout.size, layout.align.abi)
};
(size, align, AllocKind::LiveData)
}
Some(GlobalAlloc::Memory(alloc)) => {
// Need to duplicate the logic here, because the global allocations have
// different associated types than the interpreter-local ones.
let alloc = alloc.inner();
(alloc.size(), alloc.align, AllocKind::LiveData)
}
Some(GlobalAlloc::Function { .. }) => {
bug!("We already checked function pointers above")
}
Some(GlobalAlloc::VTable(..)) => {
// No data to be accessed here. But vtables are pointer-aligned.
return (Size::ZERO, self.tcx.data_layout.pointer_align.abi, AllocKind::VTable);
}
// The rest must be dead.
None => {
// Deallocated pointers are allowed, we should be able to find
// them in the map.
let (size, align) = *self
.memory
.dead_alloc_map
.get(&id)
.expect("deallocated pointers should all be recorded in `dead_alloc_map`");
(size, align, AllocKind::Dead)
}
} }
// # Dead pointers
let (size, align) = *self
.memory
.dead_alloc_map
.get(&id)
.expect("deallocated pointers should all be recorded in `dead_alloc_map`");
AllocInfo::new(size, align, AllocKind::Dead, Mutability::Not)
} }
/// Obtain the size and alignment of a *live* allocation. /// Obtain the size and alignment of a *live* allocation.
@ -902,11 +884,11 @@ fn get_live_alloc_size_and_align(
id: AllocId, id: AllocId,
msg: CheckInAllocMsg, msg: CheckInAllocMsg,
) -> InterpResult<'tcx, (Size, Align)> { ) -> InterpResult<'tcx, (Size, Align)> {
let (size, align, kind) = self.get_alloc_info(id); let info = self.get_alloc_info(id);
if matches!(kind, AllocKind::Dead) { if matches!(info.kind, AllocKind::Dead) {
throw_ub!(PointerUseAfterFree(id, msg)) throw_ub!(PointerUseAfterFree(id, msg))
} }
interp_ok((size, align)) interp_ok((info.size, info.align))
} }
fn get_fn_alloc(&self, id: AllocId) -> Option<FnVal<'tcx, M::ExtraFnVal>> { fn get_fn_alloc(&self, id: AllocId) -> Option<FnVal<'tcx, M::ExtraFnVal>> {
@ -1458,7 +1440,7 @@ pub fn scalar_may_be_null(&self, scalar: Scalar<M::Provenance>) -> InterpResult<
let ptr = scalar.to_pointer(self)?; let ptr = scalar.to_pointer(self)?;
match self.ptr_try_get_alloc_id(ptr, 0) { match self.ptr_try_get_alloc_id(ptr, 0) {
Ok((alloc_id, offset, _)) => { Ok((alloc_id, offset, _)) => {
let (size, _align, _kind) = self.get_alloc_info(alloc_id); let size = self.get_alloc_info(alloc_id).size;
// If the pointer is out-of-bounds, it may be null. // If the pointer is out-of-bounds, it may be null.
// Note that one-past-the-end (offset == size) is still inbounds, and never null. // Note that one-past-the-end (offset == size) is still inbounds, and never null.
offset > size offset > size

View File

@ -31,7 +31,7 @@
}; };
pub(crate) use self::intrinsics::eval_nullary_intrinsic; pub(crate) use self::intrinsics::eval_nullary_intrinsic;
pub use self::machine::{AllocMap, Machine, MayLeak, ReturnAction, compile_time_machine}; pub use self::machine::{AllocMap, Machine, MayLeak, ReturnAction, compile_time_machine};
pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind}; pub use self::memory::{AllocInfo, AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};
use self::operand::Operand; use self::operand::Operand;
pub use self::operand::{ImmTy, Immediate, OpTy}; pub use self::operand::{ImmTy, Immediate, OpTy};
pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable}; pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable};

View File

@ -31,8 +31,8 @@
use super::machine::AllocMap; use super::machine::AllocMap;
use super::{ use super::{
AllocId, AllocKind, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy,
MPlaceTy, Machine, MemPlaceMeta, PlaceTy, Pointer, Projectable, Scalar, ValueVisitor, err_ub, Machine, MemPlaceMeta, PlaceTy, Pointer, Projectable, Scalar, ValueVisitor, err_ub,
format_interp_error, format_interp_error,
}; };
@ -557,9 +557,20 @@ fn check_safe_pointer(
if let Ok((alloc_id, _offset, _prov)) = if let Ok((alloc_id, _offset, _prov)) =
self.ecx.ptr_try_get_alloc_id(place.ptr(), 0) self.ecx.ptr_try_get_alloc_id(place.ptr(), 0)
{ {
if let Some(GlobalAlloc::Static(did)) = // Everything should be already interned.
self.ecx.tcx.try_get_global_alloc(alloc_id) let Some(global_alloc) = self.ecx.tcx.try_get_global_alloc(alloc_id) else {
{ assert!(self.ecx.memory.alloc_map.get(alloc_id).is_none());
// We can't have *any* references to non-existing allocations in const-eval
// as the rest of rustc isn't happy with them... so we throw an error, even
// though for zero-sized references this isn't really UB.
// A potential future alternative would be to resurrect this as a zero-sized allocation
// (which codegen will then compile to an aligned dummy pointer anyway).
throw_validation_failure!(self.path, DanglingPtrUseAfterFree { ptr_kind });
};
let (size, _align) =
global_alloc.size_and_align(*self.ecx.tcx, self.ecx.param_env);
if let GlobalAlloc::Static(did) = global_alloc {
let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else { let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else {
bug!() bug!()
}; };
@ -593,17 +604,6 @@ fn check_safe_pointer(
} }
} }
// Dangling and Mutability check.
let (size, _align, alloc_kind) = self.ecx.get_alloc_info(alloc_id);
if alloc_kind == AllocKind::Dead {
// This can happen for zero-sized references. We can't have *any* references to
// non-existing allocations in const-eval though, interning rejects them all as
// the rest of rustc isn't happy with them... so we throw an error, even though
// this isn't really UB.
// A potential future alternative would be to resurrect this as a zero-sized allocation
// (which codegen will then compile to an aligned dummy pointer anyway).
throw_validation_failure!(self.path, DanglingPtrUseAfterFree { ptr_kind });
}
// If this allocation has size zero, there is no actual mutability here. // If this allocation has size zero, there is no actual mutability here.
if size != Size::ZERO { if size != Size::ZERO {
// Determine whether this pointer expects to be pointing to something mutable. // Determine whether this pointer expects to be pointing to something mutable.
@ -618,7 +618,8 @@ fn check_safe_pointer(
} }
}; };
// Determine what it actually points to. // Determine what it actually points to.
let alloc_actual_mutbl = mutability(self.ecx, alloc_id); let alloc_actual_mutbl =
global_alloc.mutability(*self.ecx.tcx, self.ecx.param_env);
// Mutable pointer to immutable memory is no good. // Mutable pointer to immutable memory is no good.
if ptr_expected_mutbl == Mutability::Mut if ptr_expected_mutbl == Mutability::Mut
&& alloc_actual_mutbl == Mutability::Not && alloc_actual_mutbl == Mutability::Not
@ -842,9 +843,16 @@ fn visit_scalar(
} }
fn in_mutable_memory(&self, val: &PlaceTy<'tcx, M::Provenance>) -> bool { fn in_mutable_memory(&self, val: &PlaceTy<'tcx, M::Provenance>) -> bool {
debug_assert!(self.ctfe_mode.is_some());
if let Some(mplace) = val.as_mplace_or_local().left() { if let Some(mplace) = val.as_mplace_or_local().left() {
if let Some(alloc_id) = mplace.ptr().provenance.and_then(|p| p.get_alloc_id()) { if let Some(alloc_id) = mplace.ptr().provenance.and_then(|p| p.get_alloc_id()) {
mutability(self.ecx, alloc_id).is_mut() let tcx = *self.ecx.tcx;
// Everything must be already interned.
let mutbl = tcx.global_alloc(alloc_id).mutability(tcx, self.ecx.param_env);
if let Some((_, alloc)) = self.ecx.memory.alloc_map.get(alloc_id) {
assert_eq!(alloc.mutability, mutbl);
}
mutbl.is_mut()
} else { } else {
// No memory at all. // No memory at all.
false false
@ -1016,53 +1024,6 @@ fn union_data_range_uncached<'tcx>(
} }
} }
/// Returns whether the allocation is mutable, and whether it's actually a static.
/// For "root" statics we look at the type to account for interior
/// mutability; for nested statics we have no type and directly use the annotated mutability.
fn mutability<'tcx>(ecx: &InterpCx<'tcx, impl Machine<'tcx>>, alloc_id: AllocId) -> Mutability {
// Let's see what kind of memory this points to.
// We're not using `try_global_alloc` since dangling pointers have already been handled.
match ecx.tcx.global_alloc(alloc_id) {
GlobalAlloc::Static(did) => {
let DefKind::Static { safety: _, mutability, nested } = ecx.tcx.def_kind(did) else {
bug!()
};
if nested {
assert!(
ecx.memory.alloc_map.get(alloc_id).is_none(),
"allocations of nested statics are already interned: {alloc_id:?}, {did:?}"
);
// Nested statics in a `static` are never interior mutable,
// so just use the declared mutability.
mutability
} else {
let mutability = match mutability {
Mutability::Not
if !ecx
.tcx
.type_of(did)
.no_bound_vars()
.expect("statics should not have generic parameters")
.is_freeze(*ecx.tcx, ty::ParamEnv::reveal_all()) =>
{
Mutability::Mut
}
_ => mutability,
};
if let Some((_, alloc)) = ecx.memory.alloc_map.get(alloc_id) {
assert_eq!(alloc.mutability, mutability);
}
mutability
}
}
GlobalAlloc::Memory(alloc) => alloc.inner().mutability,
GlobalAlloc::Function { .. } | GlobalAlloc::VTable(..) => {
// These are immutable, we better don't allow mutable pointers here.
Mutability::Not
}
}
}
impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, 'tcx, M> { impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, 'tcx, M> {
type V = PlaceTy<'tcx, M::Provenance>; type V = PlaceTy<'tcx, M::Provenance>;

View File

@ -937,7 +937,7 @@ fn usage(verbose: bool, include_unstable_options: bool, nightly_build: bool) {
let groups = if verbose { config::rustc_optgroups() } else { config::rustc_short_optgroups() }; let groups = if verbose { config::rustc_optgroups() } else { config::rustc_short_optgroups() };
let mut options = getopts::Options::new(); let mut options = getopts::Options::new();
for option in groups.iter().filter(|x| include_unstable_options || x.is_stable()) { for option in groups.iter().filter(|x| include_unstable_options || x.is_stable()) {
(option.apply)(&mut options); option.apply(&mut options);
} }
let message = "Usage: rustc [OPTIONS] INPUT"; let message = "Usage: rustc [OPTIONS] INPUT";
let nightly_help = if nightly_build { let nightly_help = if nightly_build {
@ -1219,7 +1219,7 @@ pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option<geto
let mut options = getopts::Options::new(); let mut options = getopts::Options::new();
let optgroups = config::rustc_optgroups(); let optgroups = config::rustc_optgroups();
for option in &optgroups { for option in &optgroups {
(option.apply)(&mut options); option.apply(&mut options);
} }
let matches = options.parse(args).unwrap_or_else(|e| { let matches = options.parse(args).unwrap_or_else(|e| {
let msg: Option<String> = match e { let msg: Option<String> = match e {
@ -1233,7 +1233,7 @@ pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option<geto
optgroups.iter().find(|option| option.name == opt).map(|option| { optgroups.iter().find(|option| option.name == opt).map(|option| {
// Print the help just for the option in question. // Print the help just for the option in question.
let mut options = getopts::Options::new(); let mut options = getopts::Options::new();
(option.apply)(&mut options); option.apply(&mut options);
// getopt requires us to pass a function for joining an iterator of // getopt requires us to pass a function for joining an iterator of
// strings, even though in this case we expect exactly one string. // strings, even though in this case we expect exactly one string.
options.usage_with_format(|it| { options.usage_with_format(|it| {

View File

@ -336,6 +336,7 @@ pub fn internal(&self, feature: Symbol) -> bool {
(unstable, riscv_target_feature, "1.45.0", Some(44839)), (unstable, riscv_target_feature, "1.45.0", Some(44839)),
(unstable, rtm_target_feature, "1.35.0", Some(44839)), (unstable, rtm_target_feature, "1.35.0", Some(44839)),
(unstable, s390x_target_feature, "1.82.0", Some(44839)), (unstable, s390x_target_feature, "1.82.0", Some(44839)),
(unstable, sparc_target_feature, "CURRENT_RUSTC_VERSION", Some(132783)),
(unstable, sse4a_target_feature, "1.27.0", Some(44839)), (unstable, sse4a_target_feature, "1.27.0", Some(44839)),
(unstable, tbm_target_feature, "1.27.0", Some(44839)), (unstable, tbm_target_feature, "1.27.0", Some(44839)),
(unstable, wasm_target_feature, "1.30.0", Some(44839)), (unstable, wasm_target_feature, "1.30.0", Some(44839)),

View File

@ -5,13 +5,14 @@
use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_errors::MultiSpan; use rustc_errors::MultiSpan;
use rustc_errors::codes::*; use rustc_errors::codes::*;
use rustc_hir::Node;
use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def::{CtorKind, DefKind};
use rustc_hir::{Node, intravisit};
use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
use rustc_infer::traits::Obligation; use rustc_infer::traits::{Obligation, ObligationCauseCode};
use rustc_lint_defs::builtin::{ use rustc_lint_defs::builtin::{
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS, REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS,
}; };
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::resolve_bound_vars::ResolvedArg; use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
use rustc_middle::middle::stability::EvalResult; use rustc_middle::middle::stability::EvalResult;
use rustc_middle::span_bug; use rustc_middle::span_bug;
@ -190,7 +191,7 @@ fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) {
/// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo` /// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo`
/// projections that would result in "inheriting lifetimes". /// projections that would result in "inheriting lifetimes".
fn check_opaque(tcx: TyCtxt<'_>, def_id: LocalDefId) { fn check_opaque(tcx: TyCtxt<'_>, def_id: LocalDefId) {
let hir::OpaqueTy { origin, .. } = tcx.hir().expect_opaque_ty(def_id); let hir::OpaqueTy { origin, .. } = *tcx.hir().expect_opaque_ty(def_id);
// HACK(jynelson): trying to infer the type of `impl trait` breaks documenting // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting
// `async-std` (and `pub async fn` in general). // `async-std` (and `pub async fn` in general).
@ -200,23 +201,20 @@ fn check_opaque(tcx: TyCtxt<'_>, def_id: LocalDefId) {
return; return;
} }
let span = tcx.def_span(def_id);
if tcx.type_of(def_id).instantiate_identity().references_error() { if tcx.type_of(def_id).instantiate_identity().references_error() {
return; return;
} }
if check_opaque_for_cycles(tcx, def_id, span).is_err() { if check_opaque_for_cycles(tcx, def_id).is_err() {
return; return;
} }
let _ = check_opaque_meets_bounds(tcx, def_id, span, origin); let _ = check_opaque_meets_bounds(tcx, def_id, origin);
} }
/// Checks that an opaque type does not contain cycles. /// Checks that an opaque type does not contain cycles.
pub(super) fn check_opaque_for_cycles<'tcx>( pub(super) fn check_opaque_for_cycles<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
def_id: LocalDefId, def_id: LocalDefId,
span: Span,
) -> Result<(), ErrorGuaranteed> { ) -> Result<(), ErrorGuaranteed> {
let args = GenericArgs::identity_for_item(tcx, def_id); let args = GenericArgs::identity_for_item(tcx, def_id);
@ -233,7 +231,7 @@ pub(super) fn check_opaque_for_cycles<'tcx>(
.try_expand_impl_trait_type(def_id.to_def_id(), args, InspectCoroutineFields::No) .try_expand_impl_trait_type(def_id.to_def_id(), args, InspectCoroutineFields::No)
.is_err() .is_err()
{ {
let reported = opaque_type_cycle_error(tcx, def_id, span); let reported = opaque_type_cycle_error(tcx, def_id);
return Err(reported); return Err(reported);
} }
@ -267,10 +265,16 @@ pub(super) fn check_opaque_for_cycles<'tcx>(
fn check_opaque_meets_bounds<'tcx>( fn check_opaque_meets_bounds<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
def_id: LocalDefId, def_id: LocalDefId,
span: Span, origin: hir::OpaqueTyOrigin<LocalDefId>,
origin: &hir::OpaqueTyOrigin<LocalDefId>,
) -> Result<(), ErrorGuaranteed> { ) -> Result<(), ErrorGuaranteed> {
let defining_use_anchor = match *origin { let (span, definition_def_id) =
if let Some((span, def_id)) = best_definition_site_of_opaque(tcx, def_id, origin) {
(span, Some(def_id))
} else {
(tcx.def_span(def_id), None)
};
let defining_use_anchor = match origin {
hir::OpaqueTyOrigin::FnReturn { parent, .. } hir::OpaqueTyOrigin::FnReturn { parent, .. }
| hir::OpaqueTyOrigin::AsyncFn { parent, .. } | hir::OpaqueTyOrigin::AsyncFn { parent, .. }
| hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent, | hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent,
@ -281,7 +285,7 @@ fn check_opaque_meets_bounds<'tcx>(
let infcx = tcx.infer_ctxt().build(TypingMode::analysis_in_body(tcx, defining_use_anchor)); let infcx = tcx.infer_ctxt().build(TypingMode::analysis_in_body(tcx, defining_use_anchor));
let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let args = match *origin { let args = match origin {
hir::OpaqueTyOrigin::FnReturn { parent, .. } hir::OpaqueTyOrigin::FnReturn { parent, .. }
| hir::OpaqueTyOrigin::AsyncFn { parent, .. } | hir::OpaqueTyOrigin::AsyncFn { parent, .. }
| hir::OpaqueTyOrigin::TyAlias { parent, .. } => GenericArgs::identity_for_item( | hir::OpaqueTyOrigin::TyAlias { parent, .. } => GenericArgs::identity_for_item(
@ -306,8 +310,33 @@ fn check_opaque_meets_bounds<'tcx>(
_ => re, _ => re,
}); });
let misc_cause = traits::ObligationCause::misc(span, def_id); // HACK: We eagerly instantiate some bounds to report better errors for them...
// This isn't necessary for correctness, since we register these bounds when
// equating the opaque below, but we should clean this up in the new solver.
for (predicate, pred_span) in
tcx.explicit_item_bounds(def_id).iter_instantiated_copied(tcx, args)
{
let predicate = predicate.fold_with(&mut BottomUpFolder {
tcx,
ty_op: |ty| if ty == opaque_ty { hidden_ty } else { ty },
lt_op: |lt| lt,
ct_op: |ct| ct,
});
ocx.register_obligation(Obligation::new(
tcx,
ObligationCause::new(
span,
def_id,
ObligationCauseCode::OpaqueTypeBound(pred_span, definition_def_id),
),
param_env,
predicate,
));
}
let misc_cause = ObligationCause::misc(span, def_id);
// FIXME: We should just register the item bounds here, rather than equating.
match ocx.eq(&misc_cause, param_env, opaque_ty, hidden_ty) { match ocx.eq(&misc_cause, param_env, opaque_ty, hidden_ty) {
Ok(()) => {} Ok(()) => {}
Err(ty_err) => { Err(ty_err) => {
@ -364,6 +393,97 @@ fn check_opaque_meets_bounds<'tcx>(
} }
} }
fn best_definition_site_of_opaque<'tcx>(
tcx: TyCtxt<'tcx>,
opaque_def_id: LocalDefId,
origin: hir::OpaqueTyOrigin<LocalDefId>,
) -> Option<(Span, LocalDefId)> {
struct TaitConstraintLocator<'tcx> {
opaque_def_id: LocalDefId,
tcx: TyCtxt<'tcx>,
}
impl<'tcx> TaitConstraintLocator<'tcx> {
fn check(&self, item_def_id: LocalDefId) -> ControlFlow<(Span, LocalDefId)> {
if !self.tcx.has_typeck_results(item_def_id) {
return ControlFlow::Continue(());
}
if let Some(hidden_ty) =
self.tcx.mir_borrowck(item_def_id).concrete_opaque_types.get(&self.opaque_def_id)
{
ControlFlow::Break((hidden_ty.span, item_def_id))
} else {
ControlFlow::Continue(())
}
}
}
impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
type NestedFilter = nested_filter::All;
type Result = ControlFlow<(Span, LocalDefId)>;
fn nested_visit_map(&mut self) -> Self::Map {
self.tcx.hir()
}
fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) -> Self::Result {
if let hir::ExprKind::Closure(closure) = ex.kind {
self.check(closure.def_id)?;
}
intravisit::walk_expr(self, ex)
}
fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) -> Self::Result {
self.check(it.owner_id.def_id)?;
intravisit::walk_item(self, it)
}
fn visit_impl_item(&mut self, it: &'tcx hir::ImplItem<'tcx>) -> Self::Result {
self.check(it.owner_id.def_id)?;
intravisit::walk_impl_item(self, it)
}
fn visit_trait_item(&mut self, it: &'tcx hir::TraitItem<'tcx>) -> Self::Result {
self.check(it.owner_id.def_id)?;
intravisit::walk_trait_item(self, it)
}
fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) -> Self::Result {
intravisit::walk_foreign_item(self, it)
}
}
let mut locator = TaitConstraintLocator { tcx, opaque_def_id };
match origin {
hir::OpaqueTyOrigin::FnReturn { parent, .. }
| hir::OpaqueTyOrigin::AsyncFn { parent, .. } => locator.check(parent).break_value(),
hir::OpaqueTyOrigin::TyAlias { parent, in_assoc_ty: true } => {
let impl_def_id = tcx.local_parent(parent);
for assoc in tcx.associated_items(impl_def_id).in_definition_order() {
match assoc.kind {
ty::AssocKind::Const | ty::AssocKind::Fn => {
if let ControlFlow::Break(span) = locator.check(assoc.def_id.expect_local())
{
return Some(span);
}
}
ty::AssocKind::Type => {}
}
}
None
}
hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false, .. } => {
let scope = tcx.hir().get_defining_scope(tcx.local_def_id_to_hir_id(opaque_def_id));
let found = if scope == hir::CRATE_HIR_ID {
tcx.hir().walk_toplevel_module(&mut locator)
} else {
match tcx.hir_node(scope) {
Node::Item(it) => locator.visit_item(it),
Node::ImplItem(it) => locator.visit_impl_item(it),
Node::TraitItem(it) => locator.visit_trait_item(it),
Node::ForeignItem(it) => locator.visit_foreign_item(it),
other => bug!("{:?} is not a valid scope for an opaque type item", other),
}
};
found.break_value()
}
}
}
fn sanity_check_found_hidden_type<'tcx>( fn sanity_check_found_hidden_type<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
key: ty::OpaqueTypeKey<'tcx>, key: ty::OpaqueTypeKey<'tcx>,
@ -1535,11 +1655,8 @@ fn check_type_alias_type_params_are_used<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalD
/// ///
/// If all the return expressions evaluate to `!`, then we explain that the error will go away /// If all the return expressions evaluate to `!`, then we explain that the error will go away
/// after changing it. This can happen when a user uses `panic!()` or similar as a placeholder. /// after changing it. This can happen when a user uses `panic!()` or similar as a placeholder.
fn opaque_type_cycle_error( fn opaque_type_cycle_error(tcx: TyCtxt<'_>, opaque_def_id: LocalDefId) -> ErrorGuaranteed {
tcx: TyCtxt<'_>, let span = tcx.def_span(opaque_def_id);
opaque_def_id: LocalDefId,
span: Span,
) -> ErrorGuaranteed {
let mut err = struct_span_code_err!(tcx.dcx(), span, E0720, "cannot resolve opaque type"); let mut err = struct_span_code_err!(tcx.dcx(), span, E0720, "cannot resolve opaque type");
let mut label = false; let mut label = false;

View File

@ -100,9 +100,9 @@ fn impl_into_iterator_should_be_iterator(
ty: Ty<'tcx>, ty: Ty<'tcx>,
span: Span, span: Span,
unsatisfied_predicates: &Vec<( unsatisfied_predicates: &Vec<(
ty::Predicate<'_>, ty::Predicate<'tcx>,
Option<ty::Predicate<'_>>, Option<ty::Predicate<'tcx>>,
Option<ObligationCause<'_>>, Option<ObligationCause<'tcx>>,
)>, )>,
) -> bool { ) -> bool {
fn predicate_bounds_generic_param<'tcx>( fn predicate_bounds_generic_param<'tcx>(
@ -131,15 +131,17 @@ fn predicate_bounds_generic_param<'tcx>(
} }
} }
fn is_iterator_predicate(predicate: ty::Predicate<'_>, tcx: TyCtxt<'_>) -> bool { let is_iterator_predicate = |predicate: ty::Predicate<'tcx>| -> bool {
if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) = if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) =
predicate.kind().as_ref().skip_binder() predicate.kind().as_ref().skip_binder()
{ {
tcx.is_diagnostic_item(sym::Iterator, trait_pred.trait_ref.def_id) self.tcx.is_diagnostic_item(sym::Iterator, trait_pred.trait_ref.def_id)
// ignore unsatisfied predicates generated from trying to auto-ref ty (#127511)
&& trait_pred.trait_ref.self_ty() == ty
} else { } else {
false false
} }
} };
// Does the `ty` implement `IntoIterator`? // Does the `ty` implement `IntoIterator`?
let Some(into_iterator_trait) = self.tcx.get_diagnostic_item(sym::IntoIterator) else { let Some(into_iterator_trait) = self.tcx.get_diagnostic_item(sym::IntoIterator) else {
@ -164,7 +166,7 @@ fn is_iterator_predicate(predicate: ty::Predicate<'_>, tcx: TyCtxt<'_>) -> bool
generics, generics,
generic_param, generic_param,
self.tcx, self.tcx,
) && is_iterator_predicate(unsatisfied.0, self.tcx) ) && is_iterator_predicate(unsatisfied.0)
{ {
return true; return true;
} }
@ -172,7 +174,7 @@ fn is_iterator_predicate(predicate: ty::Predicate<'_>, tcx: TyCtxt<'_>) -> bool
} }
ty::Slice(..) | ty::Adt(..) | ty::Alias(ty::Opaque, _) => { ty::Slice(..) | ty::Adt(..) | ty::Alias(ty::Opaque, _) => {
for unsatisfied in unsatisfied_predicates.iter() { for unsatisfied in unsatisfied_predicates.iter() {
if is_iterator_predicate(unsatisfied.0, self.tcx) { if is_iterator_predicate(unsatisfied.0) {
return true; return true;
} }
} }

View File

@ -104,6 +104,7 @@ pub fn register_region_obligation_with_cause(
infer::RelateParamBound(cause.span, sup_type, match cause.code().peel_derives() { infer::RelateParamBound(cause.span, sup_type, match cause.code().peel_derives() {
ObligationCauseCode::WhereClause(_, span) ObligationCauseCode::WhereClause(_, span)
| ObligationCauseCode::WhereClauseInExpr(_, span, ..) | ObligationCauseCode::WhereClauseInExpr(_, span, ..)
| ObligationCauseCode::OpaqueTypeBound(span, _)
if !span.is_dummy() => if !span.is_dummy() =>
{ {
Some(*span) Some(*span)

View File

@ -102,7 +102,7 @@ fn new_public_extern_entry<S, I>(locations: I) -> ExternEntry
fn optgroups() -> getopts::Options { fn optgroups() -> getopts::Options {
let mut opts = getopts::Options::new(); let mut opts = getopts::Options::new();
for group in rustc_optgroups() { for group in rustc_optgroups() {
(group.apply)(&mut opts); group.apply(&mut opts);
} }
return opts; return opts;
} }

View File

@ -715,7 +715,17 @@ fn lifetime_or_char(&mut self) -> TokenKind {
self.bump(); self.bump();
self.bump(); self.bump();
self.eat_while(is_id_continue); self.eat_while(is_id_continue);
return RawLifetime; match self.first() {
'\'' => {
// Check if after skipping literal contents we've met a closing
// single quote (which means that user attempted to create a
// string with single quotes).
self.bump();
let kind = Char { terminated: true };
return Literal { kind, suffix_start: self.pos_within_token() };
}
_ => return RawLifetime,
}
} }
// Either a lifetime or a character literal with // Either a lifetime or a character literal with

View File

@ -1,5 +1,9 @@
#include "LLVMWrapper.h" #include "LLVMWrapper.h"
#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/Module.h"
#include "llvm/ProfileData/Coverage/CoverageMapping.h" #include "llvm/ProfileData/Coverage/CoverageMapping.h"
#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
#include "llvm/ProfileData/InstrProf.h" #include "llvm/ProfileData/InstrProf.h"

View File

@ -1,31 +1,12 @@
#ifndef INCLUDED_RUSTC_LLVM_LLVMWRAPPER_H
#define INCLUDED_RUSTC_LLVM_LLVMWRAPPER_H
#include "SuppressLLVMWarnings.h" #include "SuppressLLVMWarnings.h"
#include "llvm-c/BitReader.h" #include "llvm/Config/llvm-config.h" // LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR
#include "llvm-c/Core.h" #include "llvm/Support/raw_ostream.h" // llvm::raw_ostream
#include "llvm-c/Object.h" #include <cstddef> // size_t etc
#include "llvm/ADT/ArrayRef.h" #include <cstdint> // uint64_t etc
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/Lint.h"
#include "llvm/Analysis/Passes.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/Memory.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/Scalar.h"
#define LLVM_VERSION_GE(major, minor) \ #define LLVM_VERSION_GE(major, minor) \
(LLVM_VERSION_MAJOR > (major) || \ (LLVM_VERSION_MAJOR > (major) || \
@ -33,79 +14,17 @@
#define LLVM_VERSION_LT(major, minor) (!LLVM_VERSION_GE((major), (minor))) #define LLVM_VERSION_LT(major, minor) (!LLVM_VERSION_GE((major), (minor)))
#if LLVM_VERSION_GE(20, 0)
#include "llvm/Transforms/Utils/Instrumentation.h"
#else
#include "llvm/Transforms/Instrumentation.h"
#endif
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/Linker/Linker.h"
#include "llvm/TargetParser/Triple.h"
extern "C" void LLVMRustSetLastError(const char *); extern "C" void LLVMRustSetLastError(const char *);
enum class LLVMRustResult { Success, Failure }; enum class LLVMRustResult { Success, Failure };
enum LLVMRustAttribute {
AlwaysInline = 0,
ByVal = 1,
Cold = 2,
InlineHint = 3,
MinSize = 4,
Naked = 5,
NoAlias = 6,
NoCapture = 7,
NoInline = 8,
NonNull = 9,
NoRedZone = 10,
NoReturn = 11,
NoUnwind = 12,
OptimizeForSize = 13,
ReadOnly = 14,
SExt = 15,
StructRet = 16,
UWTable = 17,
ZExt = 18,
InReg = 19,
SanitizeThread = 20,
SanitizeAddress = 21,
SanitizeMemory = 22,
NonLazyBind = 23,
OptimizeNone = 24,
ReadNone = 26,
SanitizeHWAddress = 28,
WillReturn = 29,
StackProtectReq = 30,
StackProtectStrong = 31,
StackProtect = 32,
NoUndef = 33,
SanitizeMemTag = 34,
NoCfCheck = 35,
ShadowCallStack = 36,
AllocSize = 37,
AllocatedPointer = 38,
AllocAlign = 39,
SanitizeSafeStack = 40,
FnRetThunkExtern = 41,
Writable = 42,
DeadOnUnwind = 43,
};
typedef struct OpaqueRustString *RustStringRef; typedef struct OpaqueRustString *RustStringRef;
typedef struct LLVMOpaqueTwine *LLVMTwineRef; typedef struct LLVMOpaqueTwine *LLVMTwineRef;
typedef struct LLVMOpaqueSMDiagnostic *LLVMSMDiagnosticRef; typedef struct LLVMOpaqueSMDiagnostic *LLVMSMDiagnosticRef;
extern "C" void LLVMRustStringWriteImpl(RustStringRef Str, const char *Ptr, extern "C" void LLVMRustStringWriteImpl(RustStringRef buf,
size_t Size); const char *slice_ptr,
size_t slice_len);
class RawRustStringOstream : public llvm::raw_ostream { class RawRustStringOstream : public llvm::raw_ostream {
RustStringRef Str; RustStringRef Str;
@ -126,3 +45,5 @@ public:
flush(); flush();
} }
}; };
#endif // INCLUDED_RUSTC_LLVM_LLVMWRAPPER_H

View File

@ -1,8 +1,10 @@
#include "llvm/Linker/Linker.h"
#include "SuppressLLVMWarnings.h"
#include "LLVMWrapper.h" #include "LLVMWrapper.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/IR/Module.h"
#include "llvm/Linker/Linker.h"
#include "llvm/Support/MemoryBuffer.h"
using namespace llvm; using namespace llvm;
struct RustLinker { struct RustLinker {

View File

@ -1,25 +1,21 @@
#include <stdio.h>
#include <cstddef>
#include <iomanip>
#include <set>
#include <vector>
#include "LLVMWrapper.h" #include "LLVMWrapper.h"
#include "llvm/Analysis/AliasAnalysis.h" #include "llvm-c/Core.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/Lint.h"
#include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/CodeGen/CommandFlags.h" #include "llvm/CodeGen/CommandFlags.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/IR/AssemblyAnnotationWriter.h" #include "llvm/IR/AssemblyAnnotationWriter.h"
#include "llvm/IR/AutoUpgrade.h" #include "llvm/IR/AutoUpgrade.h"
#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/PassManager.h"
#include "llvm/IR/Verifier.h" #include "llvm/IR/Verifier.h"
#include "llvm/LTO/LTO.h" #include "llvm/LTO/LTO.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/TargetRegistry.h" #include "llvm/MC/TargetRegistry.h"
#include "llvm/Object/IRObjectFile.h"
#include "llvm/Object/ObjectFile.h" #include "llvm/Object/ObjectFile.h"
#include "llvm/Passes/PassBuilder.h" #include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h" #include "llvm/Passes/PassPlugin.h"
@ -30,25 +26,28 @@
#include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetMachine.h"
#include "llvm/TargetParser/Host.h" #include "llvm/TargetParser/Host.h"
#include "llvm/Transforms/IPO/AlwaysInliner.h"
#include "llvm/Transforms/IPO/FunctionImport.h" #include "llvm/Transforms/IPO/FunctionImport.h"
#include "llvm/Transforms/IPO/Internalize.h" #include "llvm/Transforms/IPO/Internalize.h"
#include "llvm/Transforms/IPO/LowerTypeTests.h" #include "llvm/Transforms/IPO/LowerTypeTests.h"
#include "llvm/Transforms/IPO/ThinLTOBitcodeWriter.h" #include "llvm/Transforms/IPO/ThinLTOBitcodeWriter.h"
#include "llvm/Transforms/Instrumentation/AddressSanitizer.h" #include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
#include "llvm/Transforms/Instrumentation/DataFlowSanitizer.h" #include "llvm/Transforms/Instrumentation/DataFlowSanitizer.h"
#include "llvm/Transforms/Utils/AddDiscriminators.h"
#include "llvm/Transforms/Utils/FunctionImportUtils.h"
#if LLVM_VERSION_GE(19, 0)
#include "llvm/Support/PGOOptions.h"
#endif
#include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h" #include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h"
#include "llvm/Transforms/Instrumentation/InstrProfiling.h" #include "llvm/Transforms/Instrumentation/InstrProfiling.h"
#include "llvm/Transforms/Instrumentation/MemorySanitizer.h" #include "llvm/Transforms/Instrumentation/MemorySanitizer.h"
#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
#include "llvm/Transforms/Utils.h"
#include "llvm/Transforms/Utils/CanonicalizeAliases.h" #include "llvm/Transforms/Utils/CanonicalizeAliases.h"
#include "llvm/Transforms/Utils/FunctionImportUtils.h"
#include "llvm/Transforms/Utils/NameAnonGlobals.h" #include "llvm/Transforms/Utils/NameAnonGlobals.h"
#include <set>
#include <string>
#include <vector>
// Conditional includes prevent clang-format from fully sorting the list,
// so keep them separate.
#if LLVM_VERSION_GE(19, 0)
#include "llvm/Support/PGOOptions.h"
#endif
using namespace llvm; using namespace llvm;
@ -1624,5 +1623,6 @@ extern "C" void LLVMRustComputeLTOCacheKey(RustStringRef KeyOut,
CfiFunctionDefs, CfiFunctionDecls); CfiFunctionDefs, CfiFunctionDecls);
#endif #endif
LLVMRustStringWriteImpl(KeyOut, Key.c_str(), Key.size()); auto OS = RawRustStringOstream(KeyOut);
OS << Key.str();
} }

View File

@ -1,28 +1,38 @@
#include "LLVMWrapper.h" #include "LLVMWrapper.h"
#include "llvm-c/Core.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h" #include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/Magic.h"
#include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DiagnosticHandler.h" #include "llvm/IR/DiagnosticHandler.h"
#include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/GlobalVariable.h" #include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/Instructions.h" #include "llvm/IR/Instructions.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/IntrinsicsARM.h" #include "llvm/IR/IntrinsicsARM.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/LLVMRemarkStreamer.h" #include "llvm/IR/LLVMRemarkStreamer.h"
#include "llvm/IR/Mangler.h" #include "llvm/IR/Mangler.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Value.h" #include "llvm/IR/Value.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/COFFImportFile.h" #include "llvm/Object/COFFImportFile.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Pass.h"
#include "llvm/Remarks/RemarkFormat.h" #include "llvm/Remarks/RemarkFormat.h"
#include "llvm/Remarks/RemarkSerializer.h" #include "llvm/Remarks/RemarkSerializer.h"
#include "llvm/Remarks/RemarkStreamer.h" #include "llvm/Remarks/RemarkStreamer.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/ModRef.h" #include "llvm/Support/ModRef.h"
#include "llvm/Support/Signals.h" #include "llvm/Support/Signals.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/ToolOutputFile.h"
#include <iostream> #include <iostream>
// for raw `write` in the bad-alloc handler // for raw `write` in the bad-alloc handler
@ -216,94 +226,140 @@ extern "C" LLVMValueRef LLVMRustInsertPrivateGlobal(LLVMModuleRef M,
GlobalValue::PrivateLinkage, nullptr)); GlobalValue::PrivateLinkage, nullptr));
} }
static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) { // Must match the layout of `rustc_codegen_llvm::llvm::ffi::AttributeKind`.
enum class LLVMRustAttributeKind {
AlwaysInline = 0,
ByVal = 1,
Cold = 2,
InlineHint = 3,
MinSize = 4,
Naked = 5,
NoAlias = 6,
NoCapture = 7,
NoInline = 8,
NonNull = 9,
NoRedZone = 10,
NoReturn = 11,
NoUnwind = 12,
OptimizeForSize = 13,
ReadOnly = 14,
SExt = 15,
StructRet = 16,
UWTable = 17,
ZExt = 18,
InReg = 19,
SanitizeThread = 20,
SanitizeAddress = 21,
SanitizeMemory = 22,
NonLazyBind = 23,
OptimizeNone = 24,
ReadNone = 26,
SanitizeHWAddress = 28,
WillReturn = 29,
StackProtectReq = 30,
StackProtectStrong = 31,
StackProtect = 32,
NoUndef = 33,
SanitizeMemTag = 34,
NoCfCheck = 35,
ShadowCallStack = 36,
AllocSize = 37,
AllocatedPointer = 38,
AllocAlign = 39,
SanitizeSafeStack = 40,
FnRetThunkExtern = 41,
Writable = 42,
DeadOnUnwind = 43,
};
static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) {
switch (Kind) { switch (Kind) {
case AlwaysInline: case LLVMRustAttributeKind::AlwaysInline:
return Attribute::AlwaysInline; return Attribute::AlwaysInline;
case ByVal: case LLVMRustAttributeKind::ByVal:
return Attribute::ByVal; return Attribute::ByVal;
case Cold: case LLVMRustAttributeKind::Cold:
return Attribute::Cold; return Attribute::Cold;
case InlineHint: case LLVMRustAttributeKind::InlineHint:
return Attribute::InlineHint; return Attribute::InlineHint;
case MinSize: case LLVMRustAttributeKind::MinSize:
return Attribute::MinSize; return Attribute::MinSize;
case Naked: case LLVMRustAttributeKind::Naked:
return Attribute::Naked; return Attribute::Naked;
case NoAlias: case LLVMRustAttributeKind::NoAlias:
return Attribute::NoAlias; return Attribute::NoAlias;
case NoCapture: case LLVMRustAttributeKind::NoCapture:
return Attribute::NoCapture; return Attribute::NoCapture;
case NoCfCheck: case LLVMRustAttributeKind::NoCfCheck:
return Attribute::NoCfCheck; return Attribute::NoCfCheck;
case NoInline: case LLVMRustAttributeKind::NoInline:
return Attribute::NoInline; return Attribute::NoInline;
case NonNull: case LLVMRustAttributeKind::NonNull:
return Attribute::NonNull; return Attribute::NonNull;
case NoRedZone: case LLVMRustAttributeKind::NoRedZone:
return Attribute::NoRedZone; return Attribute::NoRedZone;
case NoReturn: case LLVMRustAttributeKind::NoReturn:
return Attribute::NoReturn; return Attribute::NoReturn;
case NoUnwind: case LLVMRustAttributeKind::NoUnwind:
return Attribute::NoUnwind; return Attribute::NoUnwind;
case OptimizeForSize: case LLVMRustAttributeKind::OptimizeForSize:
return Attribute::OptimizeForSize; return Attribute::OptimizeForSize;
case ReadOnly: case LLVMRustAttributeKind::ReadOnly:
return Attribute::ReadOnly; return Attribute::ReadOnly;
case SExt: case LLVMRustAttributeKind::SExt:
return Attribute::SExt; return Attribute::SExt;
case StructRet: case LLVMRustAttributeKind::StructRet:
return Attribute::StructRet; return Attribute::StructRet;
case UWTable: case LLVMRustAttributeKind::UWTable:
return Attribute::UWTable; return Attribute::UWTable;
case ZExt: case LLVMRustAttributeKind::ZExt:
return Attribute::ZExt; return Attribute::ZExt;
case InReg: case LLVMRustAttributeKind::InReg:
return Attribute::InReg; return Attribute::InReg;
case SanitizeThread: case LLVMRustAttributeKind::SanitizeThread:
return Attribute::SanitizeThread; return Attribute::SanitizeThread;
case SanitizeAddress: case LLVMRustAttributeKind::SanitizeAddress:
return Attribute::SanitizeAddress; return Attribute::SanitizeAddress;
case SanitizeMemory: case LLVMRustAttributeKind::SanitizeMemory:
return Attribute::SanitizeMemory; return Attribute::SanitizeMemory;
case NonLazyBind: case LLVMRustAttributeKind::NonLazyBind:
return Attribute::NonLazyBind; return Attribute::NonLazyBind;
case OptimizeNone: case LLVMRustAttributeKind::OptimizeNone:
return Attribute::OptimizeNone; return Attribute::OptimizeNone;
case ReadNone: case LLVMRustAttributeKind::ReadNone:
return Attribute::ReadNone; return Attribute::ReadNone;
case SanitizeHWAddress: case LLVMRustAttributeKind::SanitizeHWAddress:
return Attribute::SanitizeHWAddress; return Attribute::SanitizeHWAddress;
case WillReturn: case LLVMRustAttributeKind::WillReturn:
return Attribute::WillReturn; return Attribute::WillReturn;
case StackProtectReq: case LLVMRustAttributeKind::StackProtectReq:
return Attribute::StackProtectReq; return Attribute::StackProtectReq;
case StackProtectStrong: case LLVMRustAttributeKind::StackProtectStrong:
return Attribute::StackProtectStrong; return Attribute::StackProtectStrong;
case StackProtect: case LLVMRustAttributeKind::StackProtect:
return Attribute::StackProtect; return Attribute::StackProtect;
case NoUndef: case LLVMRustAttributeKind::NoUndef:
return Attribute::NoUndef; return Attribute::NoUndef;
case SanitizeMemTag: case LLVMRustAttributeKind::SanitizeMemTag:
return Attribute::SanitizeMemTag; return Attribute::SanitizeMemTag;
case ShadowCallStack: case LLVMRustAttributeKind::ShadowCallStack:
return Attribute::ShadowCallStack; return Attribute::ShadowCallStack;
case AllocSize: case LLVMRustAttributeKind::AllocSize:
return Attribute::AllocSize; return Attribute::AllocSize;
case AllocatedPointer: case LLVMRustAttributeKind::AllocatedPointer:
return Attribute::AllocatedPointer; return Attribute::AllocatedPointer;
case AllocAlign: case LLVMRustAttributeKind::AllocAlign:
return Attribute::AllocAlign; return Attribute::AllocAlign;
case SanitizeSafeStack: case LLVMRustAttributeKind::SanitizeSafeStack:
return Attribute::SafeStack; return Attribute::SafeStack;
case FnRetThunkExtern: case LLVMRustAttributeKind::FnRetThunkExtern:
return Attribute::FnRetThunkExtern; return Attribute::FnRetThunkExtern;
case Writable: case LLVMRustAttributeKind::Writable:
return Attribute::Writable; return Attribute::Writable;
case DeadOnUnwind: case LLVMRustAttributeKind::DeadOnUnwind:
return Attribute::DeadOnUnwind; return Attribute::DeadOnUnwind;
} }
report_fatal_error("bad AttributeKind"); report_fatal_error("bad LLVMRustAttributeKind");
} }
template <typename T> template <typename T>
@ -333,7 +389,7 @@ extern "C" void LLVMRustAddCallSiteAttributes(LLVMValueRef Instr,
} }
extern "C" LLVMAttributeRef extern "C" LLVMAttributeRef
LLVMRustCreateAttrNoValue(LLVMContextRef C, LLVMRustAttribute RustAttr) { LLVMRustCreateAttrNoValue(LLVMContextRef C, LLVMRustAttributeKind RustAttr) {
return wrap(Attribute::get(*unwrap(C), fromRust(RustAttr))); return wrap(Attribute::get(*unwrap(C), fromRust(RustAttr)));
} }
@ -1249,6 +1305,14 @@ LLVMRustDIBuilderCreateDebugLocation(unsigned Line, unsigned Column,
return wrap(Loc); return wrap(Loc);
} }
extern "C" LLVMMetadataRef
LLVMRustDILocationCloneWithBaseDiscriminator(LLVMMetadataRef Location,
unsigned BD) {
DILocation *Loc = unwrapDIPtr<DILocation>(Location);
auto NewLoc = Loc->cloneWithBaseDiscriminator(BD);
return wrap(NewLoc.has_value() ? NewLoc.value() : nullptr);
}
extern "C" uint64_t LLVMRustDIBuilderCreateOpDeref() { extern "C" uint64_t LLVMRustDIBuilderCreateOpDeref() {
return dwarf::DW_OP_deref; return dwarf::DW_OP_deref;
} }
@ -1510,8 +1574,8 @@ LLVMRustUnpackSMDiagnostic(LLVMSMDiagnosticRef DRef, RustStringRef MessageOut,
const SourceMgr &LSM = *D.getSourceMgr(); const SourceMgr &LSM = *D.getSourceMgr();
const MemoryBuffer *LBuf = const MemoryBuffer *LBuf =
LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(D.getLoc())); LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(D.getLoc()));
LLVMRustStringWriteImpl(BufferOut, LBuf->getBufferStart(), auto BufferOS = RawRustStringOstream(BufferOut);
LBuf->getBufferSize()); BufferOS << LBuf->getBuffer();
*LocOut = D.getLoc().getPointer() - LBuf->getBufferStart(); *LocOut = D.getLoc().getPointer() - LBuf->getBufferStart();

View File

@ -8,14 +8,14 @@
// * https://github.com/llvm/llvm-project/blob/ef6d1ec07c693352c4a60dd58db08d2d8558f6ea/llvm/lib/Object/ArchiveWriter.cpp // * https://github.com/llvm/llvm-project/blob/ef6d1ec07c693352c4a60dd58db08d2d8558f6ea/llvm/lib/Object/ArchiveWriter.cpp
#include "LLVMWrapper.h" #include "LLVMWrapper.h"
#include "SuppressLLVMWarnings.h"
#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallString.h"
#include "llvm/IR/LLVMContext.h" #include "llvm/IR/LLVMContext.h"
#include "llvm/Object/COFF.h" #include "llvm/Object/COFF.h"
#include "llvm/Object/COFFImportFile.h" #include "llvm/Object/COFFImportFile.h"
#include "llvm/Object/IRObjectFile.h" #include "llvm/Object/IRObjectFile.h"
#include "llvm/Object/ObjectFile.h" #include "llvm/Object/ObjectFile.h"
#include <llvm/Support/raw_ostream.h> #include "llvm/Support/raw_ostream.h"
using namespace llvm; using namespace llvm;
using namespace llvm::sys; using namespace llvm::sys;

View File

@ -2,42 +2,75 @@
#![allow(internal_features)] #![allow(internal_features)]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)] #![doc(rust_logo)]
#![feature(extern_types)]
#![feature(rustdoc_internals)] #![feature(rustdoc_internals)]
#![warn(unreachable_pub)] #![warn(unreachable_pub)]
// tidy-alphabetical-end // tidy-alphabetical-end
// NOTE: This crate only exists to allow linking on mingw targets.
use std::cell::RefCell; use std::cell::RefCell;
use std::slice; use std::{ptr, slice};
use libc::{c_char, size_t}; use libc::size_t;
#[repr(C)] unsafe extern "C" {
pub struct RustString { /// Opaque type that allows C++ code to write bytes to a Rust-side buffer,
pub bytes: RefCell<Vec<u8>>, /// in conjunction with `RawRustStringOstream`. Use this as `&RustString`
/// (Rust) and `RustStringRef` (C++) in FFI signatures.
pub type RustString;
} }
impl RustString { impl RustString {
pub fn len(&self) -> usize { pub fn build_byte_buffer(closure: impl FnOnce(&Self)) -> Vec<u8> {
self.bytes.borrow().len() let buf = RustStringInner::default();
} closure(buf.as_opaque());
buf.into_inner()
pub fn is_empty(&self) -> bool {
self.bytes.borrow().is_empty()
} }
} }
/// Appending to a Rust string -- used by RawRustStringOstream. /// Underlying implementation of [`RustString`].
///
/// Having two separate types makes it possible to use the opaque [`RustString`]
/// in FFI signatures without `improper_ctypes` warnings. This is a workaround
/// for the fact that there is no way to opt out of `improper_ctypes` when
/// _declaring_ a type (as opposed to using that type).
#[derive(Default)]
struct RustStringInner {
bytes: RefCell<Vec<u8>>,
}
impl RustStringInner {
fn as_opaque(&self) -> &RustString {
let ptr: *const RustStringInner = ptr::from_ref(self);
// We can't use `ptr::cast` here because extern types are `!Sized`.
let ptr = ptr as *const RustString;
unsafe { &*ptr }
}
fn from_opaque(opaque: &RustString) -> &Self {
// SAFETY: A valid `&RustString` must have been created via `as_opaque`.
let ptr: *const RustString = ptr::from_ref(opaque);
let ptr: *const RustStringInner = ptr.cast();
unsafe { &*ptr }
}
fn into_inner(self) -> Vec<u8> {
self.bytes.into_inner()
}
}
/// Appends the contents of a byte slice to a [`RustString`].
///
/// This function is implemented in `rustc_llvm` so that the C++ code in this
/// crate can link to it directly, without an implied link-time dependency on
/// `rustc_codegen_llvm`.
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn LLVMRustStringWriteImpl( pub unsafe extern "C" fn LLVMRustStringWriteImpl(
sr: &RustString, buf: &RustString,
ptr: *const c_char, slice_ptr: *const u8, // Same ABI as `*const c_char`
size: size_t, slice_len: size_t,
) { ) {
let slice = unsafe { slice::from_raw_parts(ptr as *const u8, size) }; let slice = unsafe { slice::from_raw_parts(slice_ptr, slice_len) };
RustStringInner::from_opaque(buf).bytes.borrow_mut().extend_from_slice(slice);
sr.bytes.borrow_mut().extend_from_slice(slice);
} }
/// Initialize targets enabled by the build script via `cfg(llvm_component = "...")`. /// Initialize targets enabled by the build script via `cfg(llvm_component = "...")`.

View File

@ -12,11 +12,12 @@
use std::num::NonZero; use std::num::NonZero;
use std::{fmt, io}; use std::{fmt, io};
use rustc_abi::{AddressSpace, Endian, HasDataLayout}; use rustc_abi::{AddressSpace, Align, Endian, HasDataLayout, Size};
use rustc_ast::LitKind; use rustc_ast::{LitKind, Mutability};
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lock; use rustc_data_structures::sync::Lock;
use rustc_errors::ErrorGuaranteed; use rustc_errors::ErrorGuaranteed;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::print::with_no_trimmed_paths;
@ -45,7 +46,7 @@
pub use self::value::Scalar; pub use self::value::Scalar;
use crate::mir; use crate::mir;
use crate::ty::codec::{TyDecoder, TyEncoder}; use crate::ty::codec::{TyDecoder, TyEncoder};
use crate::ty::{self, Instance, Ty, TyCtxt}; use crate::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
/// Uniquely identifies one of the following: /// Uniquely identifies one of the following:
/// - A constant /// - A constant
@ -310,6 +311,85 @@ pub fn address_space(&self, cx: &impl HasDataLayout) -> AddressSpace {
} }
} }
} }
pub fn mutability(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Mutability {
// Let's see what kind of memory we are.
match self {
GlobalAlloc::Static(did) => {
let DefKind::Static { safety: _, mutability, nested } = tcx.def_kind(did) else {
bug!()
};
if nested {
// Nested statics in a `static` are never interior mutable,
// so just use the declared mutability.
if cfg!(debug_assertions) {
let alloc = tcx.eval_static_initializer(did).unwrap();
assert_eq!(alloc.0.mutability, mutability);
}
mutability
} else {
let mutability = match mutability {
Mutability::Not
if !tcx
.type_of(did)
.no_bound_vars()
.expect("statics should not have generic parameters")
.is_freeze(tcx, param_env) =>
{
Mutability::Mut
}
_ => mutability,
};
mutability
}
}
GlobalAlloc::Memory(alloc) => alloc.inner().mutability,
GlobalAlloc::Function { .. } | GlobalAlloc::VTable(..) => {
// These are immutable.
Mutability::Not
}
}
}
pub fn size_and_align(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> (Size, Align) {
match self {
GlobalAlloc::Static(def_id) => {
let DefKind::Static { nested, .. } = tcx.def_kind(def_id) else {
bug!("GlobalAlloc::Static is not a static")
};
if nested {
// Nested anonymous statics are untyped, so let's get their
// size and alignment from the allocation itself. This always
// succeeds, as the query is fed at DefId creation time, so no
// evaluation actually occurs.
let alloc = tcx.eval_static_initializer(def_id).unwrap();
(alloc.0.size(), alloc.0.align)
} else {
// Use size and align of the type for everything else. We need
// to do that to
// * avoid cycle errors in case of self-referential statics,
// * be able to get information on extern statics.
let ty = tcx
.type_of(def_id)
.no_bound_vars()
.expect("statics should not have generic parameters");
let layout = tcx.layout_of(param_env.and(ty)).unwrap();
assert!(layout.is_sized());
(layout.size, layout.align.abi)
}
}
GlobalAlloc::Memory(alloc) => {
let alloc = alloc.inner();
(alloc.size(), alloc.align)
}
GlobalAlloc::Function { .. } => (Size::ZERO, Align::ONE),
GlobalAlloc::VTable(..) => {
// No data to be accessed here. But vtables are pointer-aligned.
return (Size::ZERO, tcx.data_layout.pointer_align.abi);
}
}
}
} }
pub const CTFE_ALLOC_SALT: usize = 0; pub const CTFE_ALLOC_SALT: usize = 0;

View File

@ -193,6 +193,11 @@ pub enum ObligationCauseCode<'tcx> {
/// The span corresponds to the clause. /// The span corresponds to the clause.
WhereClause(DefId, Span), WhereClause(DefId, Span),
/// Represents a bound for an opaque we are checking the well-formedness of.
/// The def-id corresponds to a specific definition site that we found the
/// hidden type from, if any.
OpaqueTypeBound(Span, Option<LocalDefId>),
/// Like `WhereClause`, but also identifies the expression /// Like `WhereClause`, but also identifies the expression
/// which requires the `where` clause to be proven, and also /// which requires the `where` clause to be proven, and also
/// identifies the index of the predicate in the `predicates_of` /// identifies the index of the predicate in the `predicates_of`

View File

@ -1011,25 +1011,41 @@ fn ty_and_layout_pointee_info_at(
} }
_ => { _ => {
let mut data_variant = match this.variants { let mut data_variant = match &this.variants {
// Within the discriminant field, only the niche itself is // Within the discriminant field, only the niche itself is
// always initialized, so we only check for a pointer at its // always initialized, so we only check for a pointer at its
// offset. // offset.
// //
// If the niche is a pointer, it's either valid (according // Our goal here is to check whether this represents a
// to its type), or null (which the niche field's scalar // "dereferenceable or null" pointer, so we need to ensure
// validity range encodes). This allows using // that there is only one other variant, and it must be null.
// `dereferenceable_or_null` for e.g., `Option<&T>`, and // Below, we will then check whether the pointer is indeed
// this will continue to work as long as we don't start // dereferenceable.
// using more niches than just null (e.g., the first page of
// the address space, or unaligned pointers).
Variants::Multiple { Variants::Multiple {
tag_encoding: TagEncoding::Niche { untagged_variant, .. }, tag_encoding:
TagEncoding::Niche { untagged_variant, niche_variants, niche_start },
tag_field, tag_field,
variants,
.. ..
} if this.fields.offset(tag_field) == offset => { } if variants.len() == 2 && this.fields.offset(*tag_field) == offset => {
Some(this.for_variant(cx, untagged_variant)) let tagged_variant = if untagged_variant.as_u32() == 0 {
VariantIdx::from_u32(1)
} else {
VariantIdx::from_u32(0)
};
assert_eq!(tagged_variant, *niche_variants.start());
if *niche_start == 0 {
// The other variant is encoded as "null", so we can recurse searching for
// a pointer here. This relies on the fact that the codegen backend
// only adds "dereferenceable" if there's also a "nonnull" proof,
// and that null is aligned for all alignments so it's okay to forward
// the pointer's alignment.
Some(this.for_variant(cx, *untagged_variant))
} else {
None
}
} }
Variants::Multiple { .. } => None,
_ => Some(this), _ => Some(this),
}; };

View File

@ -1076,11 +1076,6 @@ pub fn new(caller_bounds: Clauses<'tcx>, reveal: Reveal) -> Self {
ty::ParamEnv { packed: CopyTaggedPtr::new(caller_bounds, ParamTag { reveal }) } ty::ParamEnv { packed: CopyTaggedPtr::new(caller_bounds, ParamTag { reveal }) }
} }
pub fn with_user_facing(mut self) -> Self {
self.packed.set_tag(ParamTag { reveal: Reveal::UserFacing, ..self.packed.tag() });
self
}
/// Returns a new parameter environment with the same clauses, but /// Returns a new parameter environment with the same clauses, but
/// which "reveals" the true results of projections in all cases /// which "reveals" the true results of projections in all cases
/// (even for associated types that are specializable). This is /// (even for associated types that are specializable). This is
@ -1095,6 +1090,12 @@ pub fn with_reveal_all_normalized(self, tcx: TyCtxt<'tcx>) -> Self {
return self; return self;
} }
// No need to reveal opaques with the new solver enabled,
// since we have lazy norm.
if tcx.next_trait_solver_globally() {
return ParamEnv::new(self.caller_bounds(), Reveal::All);
}
ParamEnv::new(tcx.reveal_opaque_types_in_bounds(self.caller_bounds()), Reveal::All) ParamEnv::new(tcx.reveal_opaque_types_in_bounds(self.caller_bounds()), Reveal::All)
} }

View File

@ -1933,6 +1933,7 @@ pub fn primitive_symbol(self) -> Option<Symbol> {
ty::UintTy::U64 => Some(sym::u64), ty::UintTy::U64 => Some(sym::u64),
ty::UintTy::U128 => Some(sym::u128), ty::UintTy::U128 => Some(sym::u128),
}, },
ty::Str => Some(sym::str),
_ => None, _ => None,
} }
} }

View File

@ -1751,6 +1751,7 @@ pub fn reveal_opaque_types_in_bounds<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
val: ty::Clauses<'tcx>, val: ty::Clauses<'tcx>,
) -> ty::Clauses<'tcx> { ) -> ty::Clauses<'tcx> {
assert!(!tcx.next_trait_solver_globally());
let mut visitor = OpaqueTypeExpander { let mut visitor = OpaqueTypeExpander {
seen_opaque_tys: FxHashSet::default(), seen_opaque_tys: FxHashSet::default(),
expanded_cache: FxHashMap::default(), expanded_cache: FxHashMap::default(),

View File

@ -77,6 +77,8 @@ parse_box_syntax_removed_suggestion = use `Box::new()` instead
parse_cannot_be_raw_ident = `{$ident}` cannot be a raw identifier parse_cannot_be_raw_ident = `{$ident}` cannot be a raw identifier
parse_cannot_be_raw_lifetime = `{$ident}` cannot be a raw lifetime
parse_catch_after_try = keyword `catch` cannot follow a `try` block parse_catch_after_try = keyword `catch` cannot follow a `try` block
.help = try using `match` on the result of the `try` block instead .help = try using `match` on the result of the `try` block instead

View File

@ -2018,6 +2018,14 @@ pub(crate) struct CannotBeRawIdent {
pub ident: Symbol, pub ident: Symbol,
} }
#[derive(Diagnostic)]
#[diag(parse_cannot_be_raw_lifetime)]
pub(crate) struct CannotBeRawLifetime {
#[primary_span]
pub span: Span,
pub ident: Symbol,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(parse_keyword_lifetime)] #[diag(parse_keyword_lifetime)]
pub(crate) struct KeywordLifetime { pub(crate) struct KeywordLifetime {

View File

@ -294,15 +294,21 @@ fn next_token(&mut self) -> (Token, bool) {
let prefix_span = self.mk_sp(start, ident_start); let prefix_span = self.mk_sp(start, ident_start);
if prefix_span.at_least_rust_2021() { if prefix_span.at_least_rust_2021() {
let lifetime_name_without_tick = self.str_from(ident_start); let span = self.mk_sp(start, self.pos);
let lifetime_name_without_tick = Symbol::intern(&self.str_from(ident_start));
if !lifetime_name_without_tick.can_be_raw() {
self.dcx().emit_err(errors::CannotBeRawLifetime { span, ident: lifetime_name_without_tick });
}
// Put the `'` back onto the lifetime name. // Put the `'` back onto the lifetime name.
let mut lifetime_name = String::with_capacity(lifetime_name_without_tick.len() + 1); let mut lifetime_name = String::with_capacity(lifetime_name_without_tick.as_str().len() + 1);
lifetime_name.push('\''); lifetime_name.push('\'');
lifetime_name += lifetime_name_without_tick; lifetime_name += lifetime_name_without_tick.as_str();
let sym = Symbol::intern(&lifetime_name); let sym = Symbol::intern(&lifetime_name);
// Make sure we mark this as a raw identifier. // Make sure we mark this as a raw identifier.
self.psess.raw_identifier_spans.push(self.mk_sp(start, self.pos)); self.psess.raw_identifier_spans.push(span);
token::Lifetime(sym, IdentIsRaw::Yes) token::Lifetime(sym, IdentIsRaw::Yes)
} else { } else {

View File

@ -1472,7 +1472,7 @@ fn smart_resolve_context_dependent_help(
}; };
if lhs_span.eq_ctxt(rhs_span) { if lhs_span.eq_ctxt(rhs_span) {
err.span_suggestion( err.span_suggestion_verbose(
lhs_span.between(rhs_span), lhs_span.between(rhs_span),
MESSAGE, MESSAGE,
"::", "::",

View File

@ -12,7 +12,7 @@
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str::{self, FromStr}; use std::str::{self, FromStr};
use std::sync::LazyLock; use std::sync::LazyLock;
use std::{fmt, fs, iter}; use std::{cmp, fmt, fs, iter};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey}; use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey};
@ -1367,13 +1367,38 @@ pub fn build_target_config(early_dcx: &EarlyDiagCtxt, opts: &Options, sysroot: &
} }
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum OptionStability { pub enum OptionStability {
Stable, Stable,
Unstable, Unstable,
} }
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum OptionKind {
/// An option that takes a value, and cannot appear more than once (e.g. `--out-dir`).
///
/// Corresponds to [`getopts::Options::optopt`].
Opt,
/// An option that takes a value, and can appear multiple times (e.g. `--emit`).
///
/// Corresponds to [`getopts::Options::optmulti`].
Multi,
/// An option that does not take a value, and cannot appear more than once (e.g. `--help`).
///
/// Corresponds to [`getopts::Options::optflag`].
/// The `hint` string must be empty.
Flag,
/// An option that does not take a value, and can appear multiple times (e.g. `-O`).
///
/// Corresponds to [`getopts::Options::optflagmulti`].
/// The `hint` string must be empty.
FlagMulti,
}
pub struct RustcOptGroup { pub struct RustcOptGroup {
pub apply: Box<dyn Fn(&mut getopts::Options) -> &mut getopts::Options>, apply: Box<dyn Fn(&mut getopts::Options) -> &mut getopts::Options>,
pub name: &'static str, pub name: &'static str,
stability: OptionStability, stability: OptionStability,
} }
@ -1383,73 +1408,42 @@ pub fn is_stable(&self) -> bool {
self.stability == OptionStability::Stable self.stability == OptionStability::Stable
} }
pub fn stable<F>(name: &'static str, f: F) -> RustcOptGroup pub fn apply(&self, options: &mut getopts::Options) {
where (self.apply)(options);
F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static,
{
RustcOptGroup { name, apply: Box::new(f), stability: OptionStability::Stable }
}
pub fn unstable<F>(name: &'static str, f: F) -> RustcOptGroup
where
F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static,
{
RustcOptGroup { name, apply: Box::new(f), stability: OptionStability::Unstable }
} }
} }
// The `opt` local module holds wrappers around the `getopts` API that pub fn make_opt(
// adds extra rustc-specific metadata to each option; such metadata stability: OptionStability,
// is exposed by . The public kind: OptionKind,
// functions below ending with `_u` are the functions that return short_name: &'static str,
// *unstable* options, i.e., options that are only enabled when the long_name: &'static str,
// user also passes the `-Z unstable-options` debugging flag. desc: &'static str,
mod opt { hint: &'static str,
// The `fn flag*` etc below are written so that we can use them ) -> RustcOptGroup {
// in the future; do not warn about them not being used right now. RustcOptGroup {
#![allow(dead_code)] name: cmp::max_by_key(short_name, long_name, |s| s.len()),
stability,
use super::RustcOptGroup; apply: match kind {
OptionKind::Opt => Box::new(move |opts: &mut getopts::Options| {
type R = RustcOptGroup; opts.optopt(short_name, long_name, desc, hint)
type S = &'static str; }),
OptionKind::Multi => Box::new(move |opts: &mut getopts::Options| {
fn stable<F>(name: S, f: F) -> R opts.optmulti(short_name, long_name, desc, hint)
where }),
F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, OptionKind::Flag => {
{ assert_eq!(hint, "");
RustcOptGroup::stable(name, f) Box::new(move |opts: &mut getopts::Options| {
} opts.optflag(short_name, long_name, desc)
})
fn unstable<F>(name: S, f: F) -> R }
where OptionKind::FlagMulti => {
F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, assert_eq!(hint, "");
{ Box::new(move |opts: &mut getopts::Options| {
RustcOptGroup::unstable(name, f) opts.optflagmulti(short_name, long_name, desc)
} })
}
fn longer(a: S, b: S) -> S { },
if a.len() > b.len() { a } else { b }
}
pub(crate) fn opt_s(a: S, b: S, c: S, d: S) -> R {
stable(longer(a, b), move |opts| opts.optopt(a, b, c, d))
}
pub(crate) fn multi_s(a: S, b: S, c: S, d: S) -> R {
stable(longer(a, b), move |opts| opts.optmulti(a, b, c, d))
}
pub(crate) fn flag_s(a: S, b: S, c: S) -> R {
stable(longer(a, b), move |opts| opts.optflag(a, b, c))
}
pub(crate) fn flagmulti_s(a: S, b: S, c: S) -> R {
stable(longer(a, b), move |opts| opts.optflagmulti(a, b, c))
}
fn opt(a: S, b: S, c: S, d: S) -> R {
unstable(longer(a, b), move |opts| opts.optopt(a, b, c, d))
}
pub(crate) fn multi(a: S, b: S, c: S, d: S) -> R {
unstable(longer(a, b), move |opts| opts.optmulti(a, b, c, d))
} }
} }
@ -1464,46 +1458,60 @@ pub(crate) fn multi(a: S, b: S, c: S, d: S) -> R {
/// including metadata for each option, such as whether the option is /// including metadata for each option, such as whether the option is
/// part of the stable long-term interface for rustc. /// part of the stable long-term interface for rustc.
pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> { pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
use OptionKind::{Flag, FlagMulti, Multi, Opt};
use OptionStability::Stable;
use self::make_opt as opt;
vec![ vec![
opt::flag_s("h", "help", "Display this message"), opt(Stable, Flag, "h", "help", "Display this message", ""),
opt::multi_s("", "cfg", "Configure the compilation environment. opt(
SPEC supports the syntax `NAME[=\"VALUE\"]`.", "SPEC"), Stable,
opt::multi_s("", "check-cfg", "Provide list of expected cfgs for checking", "SPEC"), Multi,
opt::multi_s( "",
"cfg",
"Configure the compilation environment.\n\
SPEC supports the syntax `NAME[=\"VALUE\"]`.",
"SPEC",
),
opt(Stable, Multi, "", "check-cfg", "Provide list of expected cfgs for checking", "SPEC"),
opt(
Stable,
Multi,
"L", "L",
"", "",
"Add a directory to the library search path. The "Add a directory to the library search path. \
optional KIND can be one of dependency, crate, native, The optional KIND can be one of dependency, crate, native, framework, or all (the default).",
framework, or all (the default).",
"[KIND=]PATH", "[KIND=]PATH",
), ),
opt::multi_s( opt(
Stable,
Multi,
"l", "l",
"", "",
"Link the generated crate(s) to the specified native "Link the generated crate(s) to the specified native\n\
library NAME. The optional KIND can be one of library NAME. The optional KIND can be one of\n\
static, framework, or dylib (the default). static, framework, or dylib (the default).\n\
Optional comma separated MODIFIERS (bundle|verbatim|whole-archive|as-needed) Optional comma separated MODIFIERS\n\
may be specified each with a prefix of either '+' to (bundle|verbatim|whole-archive|as-needed)\n\
enable or '-' to disable.", may be specified each with a prefix of either '+' to\n\
enable or '-' to disable.",
"[KIND[:MODIFIERS]=]NAME[:RENAME]", "[KIND[:MODIFIERS]=]NAME[:RENAME]",
), ),
make_crate_type_option(), make_crate_type_option(),
opt::opt_s("", "crate-name", "Specify the name of the crate being built", "NAME"), opt(Stable, Opt, "", "crate-name", "Specify the name of the crate being built", "NAME"),
opt::opt_s( opt(Stable, Opt, "", "edition", &EDITION_STRING, EDITION_NAME_LIST),
"", opt(
"edition", Stable,
&EDITION_STRING, Multi,
EDITION_NAME_LIST,
),
opt::multi_s(
"", "",
"emit", "emit",
"Comma separated list of types of output for \ "Comma separated list of types of output for the compiler to emit",
the compiler to emit",
"[asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info|mir]", "[asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info|mir]",
), ),
opt::multi_s( opt(
Stable,
Multi,
"", "",
"print", "print",
"Compiler information to print on stdout", "Compiler information to print on stdout",
@ -1512,41 +1520,36 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
tls-models|target-spec-json|all-target-specs-json|native-static-libs|\ tls-models|target-spec-json|all-target-specs-json|native-static-libs|\
stack-protector-strategies|link-args|deployment-target]", stack-protector-strategies|link-args|deployment-target]",
), ),
opt::flagmulti_s("g", "", "Equivalent to -C debuginfo=2"), opt(Stable, FlagMulti, "g", "", "Equivalent to -C debuginfo=2", ""),
opt::flagmulti_s("O", "", "Equivalent to -C opt-level=2"), opt(Stable, FlagMulti, "O", "", "Equivalent to -C opt-level=2", ""),
opt::opt_s("o", "", "Write output to <filename>", "FILENAME"), opt(Stable, Opt, "o", "", "Write output to <filename>", "FILENAME"),
opt::opt_s( opt(Stable, Opt, "", "out-dir", "Write output to compiler-chosen filename in <dir>", "DIR"),
"", opt(
"out-dir", Stable,
"Write output to compiler-chosen filename \ Opt,
in <dir>",
"DIR",
),
opt::opt_s(
"", "",
"explain", "explain",
"Provide a detailed explanation of an error \ "Provide a detailed explanation of an error message",
message",
"OPT", "OPT",
), ),
opt::flag_s("", "test", "Build a test harness"), opt(Stable, Flag, "", "test", "Build a test harness", ""),
opt::opt_s("", "target", "Target triple for which the code is compiled", "TARGET"), opt(Stable, Opt, "", "target", "Target triple for which the code is compiled", "TARGET"),
opt::multi_s("A", "allow", "Set lint allowed", "LINT"), opt(Stable, Multi, "A", "allow", "Set lint allowed", "LINT"),
opt::multi_s("W", "warn", "Set lint warnings", "LINT"), opt(Stable, Multi, "W", "warn", "Set lint warnings", "LINT"),
opt::multi_s("", "force-warn", "Set lint force-warn", "LINT"), opt(Stable, Multi, "", "force-warn", "Set lint force-warn", "LINT"),
opt::multi_s("D", "deny", "Set lint denied", "LINT"), opt(Stable, Multi, "D", "deny", "Set lint denied", "LINT"),
opt::multi_s("F", "forbid", "Set lint forbidden", "LINT"), opt(Stable, Multi, "F", "forbid", "Set lint forbidden", "LINT"),
opt::multi_s( opt(
Stable,
Multi,
"", "",
"cap-lints", "cap-lints",
"Set the most restrictive lint level. \ "Set the most restrictive lint level. More restrictive lints are capped at this level",
More restrictive lints are capped at this \
level",
"LEVEL", "LEVEL",
), ),
opt::multi_s("C", "codegen", "Set a codegen option", "OPT[=VALUE]"), opt(Stable, Multi, "C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
opt::flag_s("V", "version", "Print version info and exit"), opt(Stable, Flag, "V", "version", "Print version info and exit", ""),
opt::flag_s("v", "verbose", "Use verbose output"), opt(Stable, Flag, "v", "verbose", "Use verbose output", ""),
] ]
} }
@ -1554,25 +1557,36 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
/// each option, such as whether the option is part of the stable /// each option, such as whether the option is part of the stable
/// long-term interface for rustc. /// long-term interface for rustc.
pub fn rustc_optgroups() -> Vec<RustcOptGroup> { pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
use OptionKind::{Multi, Opt};
use OptionStability::{Stable, Unstable};
use self::make_opt as opt;
let mut opts = rustc_short_optgroups(); let mut opts = rustc_short_optgroups();
// FIXME: none of these descriptions are actually used // FIXME: none of these descriptions are actually used
opts.extend(vec![ opts.extend(vec![
opt::multi_s( opt(
Stable,
Multi,
"", "",
"extern", "extern",
"Specify where an external rust library is located", "Specify where an external rust library is located",
"NAME[=PATH]", "NAME[=PATH]",
), ),
opt::opt_s("", "sysroot", "Override the system root", "PATH"), opt(Stable, Opt, "", "sysroot", "Override the system root", "PATH"),
opt::multi("Z", "", "Set unstable / perma-unstable options", "FLAG"), opt(Unstable, Multi, "Z", "", "Set unstable / perma-unstable options", "FLAG"),
opt::opt_s( opt(
Stable,
Opt,
"", "",
"error-format", "error-format",
"How errors and other messages are produced", "How errors and other messages are produced",
"human|json|short", "human|json|short",
), ),
opt::multi_s("", "json", "Configure the JSON output of the compiler", "CONFIG"), opt(Stable, Multi, "", "json", "Configure the JSON output of the compiler", "CONFIG"),
opt::opt_s( opt(
Stable,
Opt,
"", "",
"color", "color",
"Configure coloring of output: "Configure coloring of output:
@ -1581,19 +1595,23 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
never = never colorize output", never = never colorize output",
"auto|always|never", "auto|always|never",
), ),
opt::opt_s( opt(
Stable,
Opt,
"", "",
"diagnostic-width", "diagnostic-width",
"Inform rustc of the width of the output so that diagnostics can be truncated to fit", "Inform rustc of the width of the output so that diagnostics can be truncated to fit",
"WIDTH", "WIDTH",
), ),
opt::multi_s( opt(
Stable,
Multi,
"", "",
"remap-path-prefix", "remap-path-prefix",
"Remap source names in all output (compiler messages and output files)", "Remap source names in all output (compiler messages and output files)",
"FROM=TO", "FROM=TO",
), ),
opt::multi("", "env-set", "Inject an environment variable", "VAR=VALUE"), opt(Unstable, Multi, "", "env-set", "Inject an environment variable", "VAR=VALUE"),
]); ]);
opts opts
} }
@ -2756,7 +2774,9 @@ fn parse_pretty(early_dcx: &EarlyDiagCtxt, unstable_opts: &UnstableOptions) -> O
} }
pub fn make_crate_type_option() -> RustcOptGroup { pub fn make_crate_type_option() -> RustcOptGroup {
opt::multi_s( make_opt(
OptionStability::Stable,
OptionKind::Multi,
"", "",
"crate-type", "crate-type",
"Comma separated list of types of crates "Comma separated list of types of crates

View File

@ -155,6 +155,7 @@ pub fn feature_warn_issue(
} }
/// Adds the diagnostics for a feature to an existing error. /// Adds the diagnostics for a feature to an existing error.
/// Must be a language feature!
pub fn add_feature_diagnostics<G: EmissionGuarantee>( pub fn add_feature_diagnostics<G: EmissionGuarantee>(
err: &mut Diag<'_, G>, err: &mut Diag<'_, G>,
sess: &Session, sess: &Session,

View File

@ -300,6 +300,7 @@ pub fn is_test_crate(&self) -> bool {
self.opts.test self.opts.test
} }
/// `feature` must be a language feature.
#[track_caller] #[track_caller]
pub fn create_feature_err<'a>(&'a self, err: impl Diagnostic<'a>, feature: Symbol) -> Diag<'a> { pub fn create_feature_err<'a>(&'a self, err: impl Diagnostic<'a>, feature: Symbol) -> Diag<'a> {
let mut err = self.dcx().create_err(err); let mut err = self.dcx().create_err(err);

View File

@ -1867,6 +1867,7 @@
slice_patterns, slice_patterns,
slicing_syntax, slicing_syntax,
soft, soft,
sparc_target_feature,
specialization, specialization,
speed, speed,
spotlight, spotlight,
@ -2109,6 +2110,7 @@
usize_legacy_fn_max_value, usize_legacy_fn_max_value,
usize_legacy_fn_min_value, usize_legacy_fn_min_value,
usize_legacy_mod, usize_legacy_mod,
v8plus,
va_arg, va_arg,
va_copy, va_copy,
va_end, va_end,

View File

@ -143,7 +143,8 @@ pub struct ArgAttributes {
pub regular: ArgAttribute, pub regular: ArgAttribute,
pub arg_ext: ArgExtension, pub arg_ext: ArgExtension,
/// The minimum size of the pointee, guaranteed to be valid for the duration of the whole call /// The minimum size of the pointee, guaranteed to be valid for the duration of the whole call
/// (corresponding to LLVM's dereferenceable and dereferenceable_or_null attributes). /// (corresponding to LLVM's dereferenceable_or_null attributes, i.e., it is okay for this to be
/// set on a null pointer, but all non-null pointers must be dereferenceable).
pub pointee_size: Size, pub pointee_size: Size,
pub pointee_align: Option<Align>, pub pointee_align: Option<Align>,
} }

View File

@ -11,7 +11,7 @@ pub(crate) fn target() -> Target {
std: Some(true), std: Some(true),
}, },
pointer_width: 64, pointer_width: 64,
data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), data_layout: "E-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
arch: "aarch64".into(), arch: "aarch64".into(),
options: TargetOptions { options: TargetOptions {
features: "+v8a,+outline-atomics".into(), features: "+v8a,+outline-atomics".into(),

View File

@ -14,7 +14,7 @@ pub(crate) fn target() -> Target {
std: Some(true), std: Some(true),
}, },
pointer_width: 32, pointer_width: 32,
data_layout: "E-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), data_layout: "E-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
arch: "aarch64".into(), arch: "aarch64".into(),
options: TargetOptions { options: TargetOptions {
abi: "ilp32".into(), abi: "ilp32".into(),

View File

@ -11,7 +11,7 @@ pub(crate) fn target() -> Target {
std: Some(true), std: Some(true),
}, },
pointer_width: 64, pointer_width: 64,
data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), data_layout: "E-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
arch: "aarch64".into(), arch: "aarch64".into(),
options: TargetOptions { options: TargetOptions {
mcount: "__mcount".into(), mcount: "__mcount".into(),

View File

@ -10,7 +10,7 @@ pub(crate) fn target() -> Target {
std: Some(true), std: Some(true),
}, },
pointer_width: 32, pointer_width: 32,
data_layout: "e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
arch: "aarch64".into(), arch: "aarch64".into(),
options: TargetOptions { options: TargetOptions {
abi: "ilp32".into(), abi: "ilp32".into(),

View File

@ -14,6 +14,7 @@ pub(crate) fn target() -> Target {
data_layout: "E-m:e-p:32:32-i64:64-i128:128-f128:64-n32-S64".into(), data_layout: "E-m:e-p:32:32-i64:64-i128:128-f128:64-n32-S64".into(),
arch: "sparc".into(), arch: "sparc".into(),
options: TargetOptions { options: TargetOptions {
features: "+v8plus".into(),
cpu: "v9".into(), cpu: "v9".into(),
endian: Endian::Big, endian: Endian::Big,
late_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[ late_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[

View File

@ -545,6 +545,14 @@ pub fn is_supported(self) -> bool {
// tidy-alphabetical-end // tidy-alphabetical-end
]; ];
const SPARC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
// tidy-alphabetical-start
("leoncasa", Unstable(sym::sparc_target_feature), &[]),
("v8plus", Unstable(sym::sparc_target_feature), &[]),
("v9", Unstable(sym::sparc_target_feature), &[]),
// tidy-alphabetical-end
];
/// When rustdoc is running, provide a list of all known features so that all their respective /// When rustdoc is running, provide a list of all known features so that all their respective
/// primitives may be documented. /// primitives may be documented.
/// ///
@ -563,6 +571,7 @@ pub fn all_rust_features() -> impl Iterator<Item = (&'static str, Stability)> {
.chain(CSKY_FEATURES) .chain(CSKY_FEATURES)
.chain(LOONGARCH_FEATURES) .chain(LOONGARCH_FEATURES)
.chain(IBMZ_FEATURES) .chain(IBMZ_FEATURES)
.chain(SPARC_FEATURES)
.cloned() .cloned()
.map(|(f, s, _)| (f, s)) .map(|(f, s, _)| (f, s))
} }
@ -589,6 +598,7 @@ pub fn rust_target_features(&self) -> &'static [(&'static str, Stability, Implie
"csky" => CSKY_FEATURES, "csky" => CSKY_FEATURES,
"loongarch64" => LOONGARCH_FEATURES, "loongarch64" => LOONGARCH_FEATURES,
"s390x" => IBMZ_FEATURES, "s390x" => IBMZ_FEATURES,
"sparc" | "sparc64" => SPARC_FEATURES,
_ => &[], _ => &[],
} }
} }

View File

@ -2953,6 +2953,22 @@ pub(super) fn note_obligation_cause_code<G: EmissionGuarantee, T>(
// We hold the `DefId` of the item introducing the obligation, but displaying it // We hold the `DefId` of the item introducing the obligation, but displaying it
// doesn't add user usable information. It always point at an associated item. // doesn't add user usable information. It always point at an associated item.
} }
ObligationCauseCode::OpaqueTypeBound(span, definition_def_id) => {
err.span_note(span, "required by a bound in an opaque type");
if let Some(definition_def_id) = definition_def_id
// If there are any stalled coroutine obligations, then this
// error may be due to that, and not because the body has more
// where-clauses.
&& self.tcx.typeck(definition_def_id).coroutine_stalled_predicates.is_empty()
{
// FIXME(compiler-errors): We could probably point to something
// specific here if we tried hard enough...
err.span_note(
tcx.def_span(definition_def_id),
"this definition site has more where clauses than the opaque type",
);
}
}
ObligationCauseCode::Coercion { source, target } => { ObligationCauseCode::Coercion { source, target } => {
let source = let source =
tcx.short_ty_string(self.resolve_vars_if_possible(source), long_ty_file); tcx.short_ty_string(self.resolve_vars_if_possible(source), long_ty_file);

View File

@ -818,10 +818,12 @@ pub fn get_mut(&mut self) -> Option<&mut (dyn error::Error + Send + Sync + 'stat
/// Consumes the `Error`, returning its inner error (if any). /// Consumes the `Error`, returning its inner error (if any).
/// ///
/// If this [`Error`] was constructed via [`new`] then this function will /// If this [`Error`] was constructed via [`new`] or [`other`],
/// return [`Some`], otherwise it will return [`None`]. /// then this function will return [`Some`],
/// otherwise it will return [`None`].
/// ///
/// [`new`]: Error::new /// [`new`]: Error::new
/// [`other`]: Error::other
/// ///
/// # Examples /// # Examples
/// ///

View File

@ -11,6 +11,7 @@ extended = true
# Most users installing from source want to build all parts of the project from source. # Most users installing from source want to build all parts of the project from source.
[llvm] [llvm]
download-ci-llvm = false download-ci-llvm = false
[rust] [rust]
# We have several defaults in bootstrap that depend on whether the channel is `dev` (e.g. `omit-git-hash` and `download-ci-llvm`). # We have several defaults in bootstrap that depend on whether the channel is `dev` (e.g. `omit-git-hash` and `download-ci-llvm`).
# Make sure they don't get set when installing from source. # Make sure they don't get set when installing from source.

View File

@ -8,9 +8,6 @@ bench-stage = 0
[rust] [rust]
# This greatly increases the speed of rebuilds, especially when there are only minor changes. However, it makes the initial build slightly slower. # This greatly increases the speed of rebuilds, especially when there are only minor changes. However, it makes the initial build slightly slower.
incremental = true incremental = true
# Download rustc from CI instead of building it from source.
# For stage > 1 builds, this cuts compile times significantly when there are no changes on "compiler" tree.
download-rustc = "if-unchanged"
# Make the compiler and standard library faster to build, at the expense of a ~20% runtime slowdown. # Make the compiler and standard library faster to build, at the expense of a ~20% runtime slowdown.
lto = "off" lto = "off"

View File

@ -3,11 +3,6 @@
[rust] [rust]
# This greatly increases the speed of rebuilds, especially when there are only minor changes. However, it makes the initial build slightly slower. # This greatly increases the speed of rebuilds, especially when there are only minor changes. However, it makes the initial build slightly slower.
incremental = true incremental = true
# Download rustc from CI instead of building it from source.
# For stage > 1 builds, this cuts compile times significantly when there are no changes on "compiler" tree.
# Using these defaults will download the stage2 compiler (see `download-rustc`
# setting) and the stage2 toolchain should therefore be used for these defaults.
download-rustc = "if-unchanged"
[build] [build]
# Document with the in-tree rustdoc by default, since `download-rustc` makes it quick to compile. # Document with the in-tree rustdoc by default, since `download-rustc` makes it quick to compile.

View File

@ -1665,10 +1665,26 @@ fn get_table(option: &str) -> Result<TomlConfig, toml::de::Error> {
let mut debuginfo_level_tools = None; let mut debuginfo_level_tools = None;
let mut debuginfo_level_tests = None; let mut debuginfo_level_tests = None;
let mut optimize = None; let mut optimize = None;
let mut omit_git_hash = None;
let mut lld_enabled = None; let mut lld_enabled = None;
let mut std_features = None; let mut std_features = None;
let default = config.channel == "dev";
config.omit_git_hash = toml.rust.as_ref().and_then(|r| r.omit_git_hash).unwrap_or(default);
config.rust_info = GitInfo::new(config.omit_git_hash, &config.src);
config.cargo_info = GitInfo::new(config.omit_git_hash, &config.src.join("src/tools/cargo"));
config.rust_analyzer_info =
GitInfo::new(config.omit_git_hash, &config.src.join("src/tools/rust-analyzer"));
config.clippy_info =
GitInfo::new(config.omit_git_hash, &config.src.join("src/tools/clippy"));
config.miri_info = GitInfo::new(config.omit_git_hash, &config.src.join("src/tools/miri"));
config.rustfmt_info =
GitInfo::new(config.omit_git_hash, &config.src.join("src/tools/rustfmt"));
config.enzyme_info =
GitInfo::new(config.omit_git_hash, &config.src.join("src/tools/enzyme"));
config.in_tree_llvm_info = GitInfo::new(false, &config.src.join("src/llvm-project"));
config.in_tree_gcc_info = GitInfo::new(false, &config.src.join("src/gcc"));
let mut is_user_configured_rust_channel = false; let mut is_user_configured_rust_channel = false;
if let Some(rust) = toml.rust { if let Some(rust) = toml.rust {
@ -1699,7 +1715,7 @@ fn get_table(option: &str) -> Result<TomlConfig, toml::de::Error> {
verbose_tests, verbose_tests,
optimize_tests, optimize_tests,
codegen_tests, codegen_tests,
omit_git_hash: omit_git_hash_toml, omit_git_hash: _, // already handled above
dist_src, dist_src,
save_toolstates, save_toolstates,
codegen_backends, codegen_backends,
@ -1750,7 +1766,6 @@ fn get_table(option: &str) -> Result<TomlConfig, toml::de::Error> {
std_features = std_features_toml; std_features = std_features_toml;
optimize = optimize_toml; optimize = optimize_toml;
omit_git_hash = omit_git_hash_toml;
config.rust_new_symbol_mangling = new_symbol_mangling; config.rust_new_symbol_mangling = new_symbol_mangling;
set(&mut config.rust_optimize_tests, optimize_tests); set(&mut config.rust_optimize_tests, optimize_tests);
set(&mut config.codegen_tests, codegen_tests); set(&mut config.codegen_tests, codegen_tests);
@ -1826,24 +1841,6 @@ fn get_table(option: &str) -> Result<TomlConfig, toml::de::Error> {
config.reproducible_artifacts = flags.reproducible_artifact; config.reproducible_artifacts = flags.reproducible_artifact;
// rust_info must be set before is_ci_llvm_available() is called.
let default = config.channel == "dev";
config.omit_git_hash = omit_git_hash.unwrap_or(default);
config.rust_info = GitInfo::new(config.omit_git_hash, &config.src);
config.cargo_info = GitInfo::new(config.omit_git_hash, &config.src.join("src/tools/cargo"));
config.rust_analyzer_info =
GitInfo::new(config.omit_git_hash, &config.src.join("src/tools/rust-analyzer"));
config.clippy_info =
GitInfo::new(config.omit_git_hash, &config.src.join("src/tools/clippy"));
config.miri_info = GitInfo::new(config.omit_git_hash, &config.src.join("src/tools/miri"));
config.rustfmt_info =
GitInfo::new(config.omit_git_hash, &config.src.join("src/tools/rustfmt"));
config.enzyme_info =
GitInfo::new(config.omit_git_hash, &config.src.join("src/tools/enzyme"));
config.in_tree_llvm_info = GitInfo::new(false, &config.src.join("src/llvm-project"));
config.in_tree_gcc_info = GitInfo::new(false, &config.src.join("src/gcc"));
// We need to override `rust.channel` if it's manually specified when using the CI rustc. // We need to override `rust.channel` if it's manually specified when using the CI rustc.
// This is because if the compiler uses a different channel than the one specified in config.toml, // This is because if the compiler uses a different channel than the one specified in config.toml,
// tests may fail due to using a different channel than the one used by the compiler during tests. // tests may fail due to using a different channel than the one used by the compiler during tests.
@ -2760,9 +2757,19 @@ fn download_ci_rustc_commit(
// If `download-rustc` is not set, default to rebuilding. // If `download-rustc` is not set, default to rebuilding.
let if_unchanged = match download_rustc { let if_unchanged = match download_rustc {
None | Some(StringOrBool::Bool(false)) => return None, None => self.rust_info.is_managed_git_subrepository(),
Some(StringOrBool::Bool(false)) => return None,
Some(StringOrBool::Bool(true)) => false, Some(StringOrBool::Bool(true)) => false,
Some(StringOrBool::String(s)) if s == "if-unchanged" => true, Some(StringOrBool::String(s)) if s == "if-unchanged" => {
if !self.rust_info.is_managed_git_subrepository() {
println!(
"ERROR: `download-rustc=if-unchanged` is only compatible with Git managed sources."
);
crate::exit!(1);
}
true
}
Some(StringOrBool::String(other)) => { Some(StringOrBool::String(other)) => {
panic!("unrecognized option for download-rustc: {other}") panic!("unrecognized option for download-rustc: {other}")
} }
@ -2789,7 +2796,7 @@ fn download_ci_rustc_commit(
} }
println!("ERROR: could not find commit hash for downloading rustc"); println!("ERROR: could not find commit hash for downloading rustc");
println!("HELP: maybe your repository history is too shallow?"); println!("HELP: maybe your repository history is too shallow?");
println!("HELP: consider disabling `download-rustc`"); println!("HELP: consider setting `rust.download-rustc=false` in config.toml");
println!("HELP: or fetch enough history to include one upstream commit"); println!("HELP: or fetch enough history to include one upstream commit");
crate::exit!(1); crate::exit!(1);
} }

View File

@ -135,6 +135,7 @@ fn override_toml() {
[rust] [rust]
lto = "off" lto = "off"
deny-warnings = true deny-warnings = true
download-rustc=false
[build] [build]
gdb = "foo" gdb = "foo"
@ -200,6 +201,8 @@ fn override_toml() {
.collect(), .collect(),
"setting dictionary value" "setting dictionary value"
); );
assert!(!config.llvm_from_ci);
assert!(!config.download_rustc());
} }
#[test] #[test]

View File

@ -18,7 +18,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
- MSP430 - MSP430
- M68k - M68k
- CSKY - CSKY
- s390x
- Arm64EC - Arm64EC
- SPARC - SPARC
@ -52,11 +51,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
| M68k | `reg_addr` | `a[0-3]` | `a` | | M68k | `reg_addr` | `a[0-3]` | `a` |
| CSKY | `reg` | `r[0-31]` | `r` | | CSKY | `reg` | `r[0-31]` | `r` |
| CSKY | `freg` | `f[0-31]` | `f` | | CSKY | `freg` | `f[0-31]` | `f` |
| s390x | `reg` | `r[0-10]`, `r[12-14]` | `r` |
| s390x | `reg_addr` | `r[1-10]`, `r[12-14]` | `a` |
| s390x | `freg` | `f[0-15]` | `f` |
| s390x | `vreg` | `v[0-31]` | Only clobbers |
| s390x | `areg` | `a[2-15]` | Only clobbers |
| SPARC | `reg` | `r[2-29]` | `r` | | SPARC | `reg` | `r[2-29]` | `r` |
| SPARC | `yreg` | `y` | Only clobbers | | SPARC | `yreg` | `y` | Only clobbers |
| Arm64EC | `reg` | `x[0-12]`, `x[15-22]`, `x[25-27]`, `x30` | `r` | | Arm64EC | `reg` | `x[0-12]`, `x[15-22]`, `x[25-27]`, `x30` | `r` |
@ -96,10 +90,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
| M68k | `reg_data` | None | `i8`, `i16`, `i32` | | M68k | `reg_data` | None | `i8`, `i16`, `i32` |
| CSKY | `reg` | None | `i8`, `i16`, `i32` | | CSKY | `reg` | None | `i8`, `i16`, `i32` |
| CSKY | `freg` | None | `f32`, | | CSKY | `freg` | None | `f32`, |
| s390x | `reg`, `reg_addr` | None | `i8`, `i16`, `i32`, `i64` |
| s390x | `freg` | None | `f32`, `f64` |
| s390x | `vreg` | N/A | Only clobbers |
| s390x | `areg` | N/A | Only clobbers |
| SPARC | `reg` | None | `i8`, `i16`, `i32`, `i64` (SPARC64 only) | | SPARC | `reg` | None | `i8`, `i16`, `i32`, `i64` (SPARC64 only) |
| SPARC | `yreg` | N/A | Only clobbers | | SPARC | `yreg` | N/A | Only clobbers |
| Arm64EC | `reg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` | | Arm64EC | `reg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` |
@ -159,8 +149,8 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
| Architecture | Unsupported register | Reason | | Architecture | Unsupported register | Reason |
| ------------ | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ------------ | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| All | `sp`, `r15` (s390x), `r14`/`o6` (SPARC) | The stack pointer must be restored to its original value at the end of an asm code block. | | All | `sp`, `r14`/`o6` (SPARC) | The stack pointer must be restored to its original value at the end of an asm code block. |
| All | `fr` (Hexagon), `fp` (PowerPC), `$fp` (MIPS), `Y` (AVR), `r4` (MSP430), `a6` (M68k), `r11` (s390x), `r30`/`i6` (SPARC), `x29` (Arm64EC) | The frame pointer cannot be used as an input or output. | | All | `fr` (Hexagon), `fp` (PowerPC), `$fp` (MIPS), `Y` (AVR), `r4` (MSP430), `a6` (M68k), `r30`/`i6` (SPARC), `x29` (Arm64EC) | The frame pointer cannot be used as an input or output. |
| All | `r19` (Hexagon), `r29` (PowerPC), `r30` (PowerPC), `x19` (Arm64EC) | These are used internally by LLVM as "base pointer" for functions with complex stack frames. | | All | `r19` (Hexagon), `r29` (PowerPC), `r30` (PowerPC), `x19` (Arm64EC) | These are used internally by LLVM as "base pointer" for functions with complex stack frames. |
| MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. | | MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. |
| MIPS | `$1` or `$at` | Reserved for assembler. | | MIPS | `$1` or `$at` | Reserved for assembler. |
@ -181,8 +171,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
| CSKY | `r15` | This is the link register. | | CSKY | `r15` | This is the link register. |
| CSKY | `r[26-30]` | Reserved by its ABI. | | CSKY | `r[26-30]` | Reserved by its ABI. |
| CSKY | `r31` | This is the TLS register. | | CSKY | `r31` | This is the TLS register. |
| s390x | `c[0-15]` | Reserved by the kernel. |
| s390x | `a[0-1]` | Reserved for system use. |
| SPARC | `r0`/`g0` | This is always zero and cannot be used as inputs or outputs. | | SPARC | `r0`/`g0` | This is always zero and cannot be used as inputs or outputs. |
| SPARC | `r1`/`g1` | Used internally by LLVM. | | SPARC | `r1`/`g1` | Used internally by LLVM. |
| SPARC | `r5`/`g5` | Reserved for system. (SPARC32 only) | | SPARC | `r5`/`g5` | Reserved for system. (SPARC32 only) |
@ -206,9 +194,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
| PowerPC | `reg` | None | `0` | None | | PowerPC | `reg` | None | `0` | None |
| PowerPC | `reg_nonzero` | None | `3` | None | | PowerPC | `reg_nonzero` | None | `3` | None |
| PowerPC | `freg` | None | `0` | None | | PowerPC | `freg` | None | `0` | None |
| s390x | `reg` | None | `%r0` | None |
| s390x | `reg_addr` | None | `%r1` | None |
| s390x | `freg` | None | `%f0` | None |
| SPARC | `reg` | None | `%o0` | None | | SPARC | `reg` | None | `%o0` | None |
| CSKY | `reg` | None | `r0` | None | | CSKY | `reg` | None | `r0` | None |
| CSKY | `freg` | None | `f0` | None | | CSKY | `freg` | None | `f0` | None |
@ -232,8 +217,6 @@ These flags registers must be restored upon exiting the asm block if the `preser
- The status register `r2`. - The status register `r2`.
- M68k - M68k
- The condition code register `ccr`. - The condition code register `ccr`.
- s390x
- The condition code register `cc`.
- SPARC - SPARC
- Integer condition codes (`icc` and `xcc`) - Integer condition codes (`icc` and `xcc`)
- Floating-point condition codes (`fcc[0-3]`) - Floating-point condition codes (`fcc[0-3]`)

View File

@ -1,3 +1,12 @@
# This config uses a separate build directory for rust-analyzer,
# so that r-a's checks don't block user `x` commands and vice-verse.
# R-a's build directory is located in `build/rust-analyzer`.
#
# To build rustfmt and proc macro server for r-a run the following command:
# ```
# x b proc-macro-srv-cli rustfmt --stage 0 --build-dir build/rust-analyzer
# ```
[language-server.rust-analyzer.config] [language-server.rust-analyzer.config]
linkedProjects = [ linkedProjects = [
"Cargo.toml", "Cargo.toml",
@ -17,16 +26,18 @@ overrideCommand = [
"x.py", "x.py",
"check", "check",
"--json-output", "--json-output",
"--build-dir",
"build/rust-analyzer",
] ]
[language-server.rust-analyzer.config.rustfmt] [language-server.rust-analyzer.config.rustfmt]
overrideCommand = [ overrideCommand = [
"build-rust-analyzer/host/rustfmt/bin/rustfmt", "build/rust-analyzer/host/rustfmt/bin/rustfmt",
"--edition=2021" "--edition=2021"
] ]
[language-server.rust-analyzer.config.procMacro] [language-server.rust-analyzer.config.procMacro]
server = "build-rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv" server = "build/rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv"
enable = true enable = true
[language-server.rust-analyzer.config.rustc] [language-server.rust-analyzer.config.rustc]
@ -47,4 +58,6 @@ overrideCommand = [
"x.py", "x.py",
"check", "check",
"--json-output", "--json-output",
"--build-dir",
"build/rust-analyzer",
] ]

View File

@ -215,477 +215,482 @@ fn init_logging(early_dcx: &EarlyDiagCtxt) {
} }
fn opts() -> Vec<RustcOptGroup> { fn opts() -> Vec<RustcOptGroup> {
let stable: fn(_, fn(&mut getopts::Options) -> &mut _) -> _ = RustcOptGroup::stable; use rustc_session::config::OptionKind::{Flag, FlagMulti, Multi, Opt};
let unstable: fn(_, fn(&mut getopts::Options) -> &mut _) -> _ = RustcOptGroup::unstable; use rustc_session::config::OptionStability::{Stable, Unstable};
use rustc_session::config::make_opt as opt;
vec![ vec![
stable("h", |o| o.optflagmulti("h", "help", "show this help message")), opt(Stable, FlagMulti, "h", "help", "show this help message", ""),
stable("V", |o| o.optflagmulti("V", "version", "print rustdoc's version")), opt(Stable, FlagMulti, "V", "version", "print rustdoc's version", ""),
stable("v", |o| o.optflagmulti("v", "verbose", "use verbose output")), opt(Stable, FlagMulti, "v", "verbose", "use verbose output", ""),
stable("w", |o| o.optopt("w", "output-format", "the output type to write", "[html]")), opt(Stable, Opt, "w", "output-format", "the output type to write", "[html]"),
stable("output", |o| { opt(
o.optopt( Stable,
"", Opt,
"output", "",
"Which directory to place the output. \ "output",
This option is deprecated, use --out-dir instead.", "Which directory to place the output. This option is deprecated, use --out-dir instead.",
"PATH", "PATH",
) ),
}), opt(Stable, Opt, "o", "out-dir", "which directory to place the output", "PATH"),
stable("o", |o| o.optopt("o", "out-dir", "which directory to place the output", "PATH")), opt(Stable, Opt, "", "crate-name", "specify the name of this crate", "NAME"),
stable("crate-name", |o| {
o.optopt("", "crate-name", "specify the name of this crate", "NAME")
}),
make_crate_type_option(), make_crate_type_option(),
stable("L", |o| { opt(Stable, Multi, "L", "library-path", "directory to add to crate search path", "DIR"),
o.optmulti("L", "library-path", "directory to add to crate search path", "DIR") opt(Stable, Multi, "", "cfg", "pass a --cfg to rustc", ""),
}), opt(Stable, Multi, "", "check-cfg", "pass a --check-cfg to rustc", ""),
stable("cfg", |o| o.optmulti("", "cfg", "pass a --cfg to rustc", "")), opt(Stable, Multi, "", "extern", "pass an --extern to rustc", "NAME[=PATH]"),
stable("check-cfg", |o| o.optmulti("", "check-cfg", "pass a --check-cfg to rustc", "")), opt(
stable("extern", |o| o.optmulti("", "extern", "pass an --extern to rustc", "NAME[=PATH]")), Unstable,
unstable("extern-html-root-url", |o| { Multi,
o.optmulti( "",
"", "extern-html-root-url",
"extern-html-root-url", "base URL to use for dependencies; for example, \
"base URL to use for dependencies; for example, \ \"std=/doc\" links std::vec::Vec to /doc/std/vec/struct.Vec.html",
\"std=/doc\" links std::vec::Vec to /doc/std/vec/struct.Vec.html", "NAME=URL",
"NAME=URL", ),
) opt(
}), Unstable,
unstable("extern-html-root-takes-precedence", |o| { FlagMulti,
o.optflagmulti( "",
"", "extern-html-root-takes-precedence",
"extern-html-root-takes-precedence", "give precedence to `--extern-html-root-url`, not `html_root_url`",
"give precedence to `--extern-html-root-url`, not `html_root_url`", "",
) ),
}), opt(Stable, Multi, "C", "codegen", "pass a codegen option to rustc", "OPT[=VALUE]"),
stable("C", |o| { opt(Stable, FlagMulti, "", "document-private-items", "document private items", ""),
o.optmulti("C", "codegen", "pass a codegen option to rustc", "OPT[=VALUE]") opt(
}), Unstable,
stable("document-private-items", |o| { FlagMulti,
o.optflagmulti("", "document-private-items", "document private items") "",
}), "document-hidden-items",
unstable("document-hidden-items", |o| { "document items that have doc(hidden)",
o.optflagmulti("", "document-hidden-items", "document items that have doc(hidden)") "",
}), ),
stable("test", |o| o.optflagmulti("", "test", "run code examples as tests")), opt(Stable, FlagMulti, "", "test", "run code examples as tests", ""),
stable("test-args", |o| { opt(Stable, Multi, "", "test-args", "arguments to pass to the test runner", "ARGS"),
o.optmulti("", "test-args", "arguments to pass to the test runner", "ARGS") opt(
}), Stable,
stable("test-run-directory", |o| { Opt,
o.optopt( "",
"", "test-run-directory",
"test-run-directory", "The working directory in which to run tests",
"The working directory in which to run tests", "PATH",
"PATH", ),
) opt(Stable, Opt, "", "target", "target triple to document", "TRIPLE"),
}), opt(
stable("target", |o| o.optopt("", "target", "target triple to document", "TRIPLE")), Stable,
stable("markdown-css", |o| { Multi,
o.optmulti( "",
"", "markdown-css",
"markdown-css", "CSS files to include via <link> in a rendered Markdown file",
"CSS files to include via <link> in a rendered Markdown file", "FILES",
"FILES", ),
) opt(
}), Stable,
stable("html-in-header", |o| { Multi,
o.optmulti( "",
"", "html-in-header",
"html-in-header", "files to include inline in the <head> section of a rendered Markdown file \
"files to include inline in the <head> section of a rendered Markdown file \ or generated documentation",
or generated documentation", "FILES",
"FILES", ),
) opt(
}), Stable,
stable("html-before-content", |o| { Multi,
o.optmulti( "",
"", "html-before-content",
"html-before-content", "files to include inline between <body> and the content of a rendered \
"files to include inline between <body> and the content of a rendered \ Markdown file or generated documentation",
Markdown file or generated documentation", "FILES",
"FILES", ),
) opt(
}), Stable,
stable("html-after-content", |o| { Multi,
o.optmulti( "",
"", "html-after-content",
"html-after-content", "files to include inline between the content and </body> of a rendered \
"files to include inline between the content and </body> of a rendered \ Markdown file or generated documentation",
Markdown file or generated documentation", "FILES",
"FILES", ),
) opt(
}), Unstable,
unstable("markdown-before-content", |o| { Multi,
o.optmulti( "",
"", "markdown-before-content",
"markdown-before-content", "files to include inline between <body> and the content of a rendered \
"files to include inline between <body> and the content of a rendered \ Markdown file or generated documentation",
Markdown file or generated documentation", "FILES",
"FILES", ),
) opt(
}), Unstable,
unstable("markdown-after-content", |o| { Multi,
o.optmulti( "",
"", "markdown-after-content",
"markdown-after-content", "files to include inline between the content and </body> of a rendered \
"files to include inline between the content and </body> of a rendered \ Markdown file or generated documentation",
Markdown file or generated documentation", "FILES",
"FILES", ),
) opt(Stable, Opt, "", "markdown-playground-url", "URL to send code snippets to", "URL"),
}), opt(Stable, FlagMulti, "", "markdown-no-toc", "don't include table of contents", ""),
stable("markdown-playground-url", |o| { opt(
o.optopt("", "markdown-playground-url", "URL to send code snippets to", "URL") Stable,
}), Opt,
stable("markdown-no-toc", |o| { "e",
o.optflagmulti("", "markdown-no-toc", "don't include table of contents") "extend-css",
}), "To add some CSS rules with a given file to generate doc with your own theme. \
stable("e", |o| { However, your theme might break if the rustdoc's generated HTML changes, so be careful!",
o.optopt( "PATH",
"e", ),
"extend-css", opt(
"To add some CSS rules with a given file to generate doc with your \ Unstable,
own theme. However, your theme might break if the rustdoc's generated HTML \ Multi,
changes, so be careful!", "Z",
"PATH", "",
) "unstable / perma-unstable options (only on nightly build)",
}), "FLAG",
unstable("Z", |o| { ),
o.optmulti("Z", "", "unstable / perma-unstable options (only on nightly build)", "FLAG") opt(Stable, Opt, "", "sysroot", "Override the system root", "PATH"),
}), opt(
stable("sysroot", |o| o.optopt("", "sysroot", "Override the system root", "PATH")), Unstable,
unstable("playground-url", |o| { Opt,
o.optopt( "",
"", "playground-url",
"playground-url", "URL to send code snippets to, may be reset by --markdown-playground-url \
"URL to send code snippets to, may be reset by --markdown-playground-url \ or `#![doc(html_playground_url=...)]`",
or `#![doc(html_playground_url=...)]`", "URL",
"URL", ),
) opt(
}), Unstable,
unstable("display-doctest-warnings", |o| { FlagMulti,
o.optflagmulti( "",
"", "display-doctest-warnings",
"display-doctest-warnings", "show warnings that originate in doctests",
"show warnings that originate in doctests", "",
) ),
}), opt(
stable("crate-version", |o| { Stable,
o.optopt("", "crate-version", "crate version to print into documentation", "VERSION") Opt,
}), "",
unstable("sort-modules-by-appearance", |o| { "crate-version",
o.optflagmulti( "crate version to print into documentation",
"", "VERSION",
"sort-modules-by-appearance", ),
"sort modules by where they appear in the program, rather than alphabetically", opt(
) Unstable,
}), FlagMulti,
stable("default-theme", |o| { "",
o.optopt( "sort-modules-by-appearance",
"", "sort modules by where they appear in the program, rather than alphabetically",
"default-theme", "",
"Set the default theme. THEME should be the theme name, generally lowercase. \ ),
If an unknown default theme is specified, the builtin default is used. \ opt(
The set of themes, and the rustdoc built-in default, are not stable.", Stable,
"THEME", Opt,
) "",
}), "default-theme",
unstable("default-setting", |o| { "Set the default theme. THEME should be the theme name, generally lowercase. \
o.optmulti( If an unknown default theme is specified, the builtin default is used. \
"", The set of themes, and the rustdoc built-in default, are not stable.",
"default-setting", "THEME",
"Default value for a rustdoc setting (used when \"rustdoc-SETTING\" is absent \ ),
from web browser Local Storage). If VALUE is not supplied, \"true\" is used. \ opt(
Supported SETTINGs and VALUEs are not documented and not stable.", Unstable,
"SETTING[=VALUE]", Multi,
) "",
}), "default-setting",
stable("theme", |o| { "Default value for a rustdoc setting (used when \"rustdoc-SETTING\" is absent \
o.optmulti( from web browser Local Storage). If VALUE is not supplied, \"true\" is used. \
"", Supported SETTINGs and VALUEs are not documented and not stable.",
"theme", "SETTING[=VALUE]",
"additional themes which will be added to the generated docs", ),
"FILES", opt(
) Stable,
}), Multi,
stable("check-theme", |o| { "",
o.optmulti("", "check-theme", "check if given theme is valid", "FILES") "theme",
}), "additional themes which will be added to the generated docs",
unstable("resource-suffix", |o| { "FILES",
o.optopt( ),
"", opt(Stable, Multi, "", "check-theme", "check if given theme is valid", "FILES"),
"resource-suffix", opt(
"suffix to add to CSS and JavaScript files, e.g., \"search-index.js\" will \ Unstable,
become \"search-index-suffix.js\"", Opt,
"PATH", "",
) "resource-suffix",
}), "suffix to add to CSS and JavaScript files, \
stable("edition", |o| { e.g., \"search-index.js\" will become \"search-index-suffix.js\"",
o.optopt( "PATH",
"", ),
"edition", opt(
"edition to use when compiling rust code (default: 2015)", Stable,
"EDITION", Opt,
) "",
}), "edition",
stable("color", |o| { "edition to use when compiling rust code (default: 2015)",
o.optopt( "EDITION",
"", ),
"color", opt(
"Configure coloring of output: Stable,
Opt,
"",
"color",
"Configure coloring of output:
auto = colorize, if output goes to a tty (default); auto = colorize, if output goes to a tty (default);
always = always colorize output; always = always colorize output;
never = never colorize output", never = never colorize output",
"auto|always|never", "auto|always|never",
) ),
}), opt(
stable("error-format", |o| { Stable,
o.optopt( Opt,
"", "",
"error-format", "error-format",
"How errors and other messages are produced", "How errors and other messages are produced",
"human|json|short", "human|json|short",
) ),
}), opt(
stable("diagnostic-width", |o| { Stable,
o.optopt( Opt,
"", "",
"diagnostic-width", "diagnostic-width",
"Provide width of the output for truncated error messages", "Provide width of the output for truncated error messages",
"WIDTH", "WIDTH",
) ),
}), opt(Stable, Opt, "", "json", "Configure the structure of JSON diagnostics", "CONFIG"),
stable("json", |o| { opt(Stable, Multi, "A", "allow", "Set lint allowed", "LINT"),
o.optopt("", "json", "Configure the structure of JSON diagnostics", "CONFIG") opt(Stable, Multi, "W", "warn", "Set lint warnings", "LINT"),
}), opt(Stable, Multi, "", "force-warn", "Set lint force-warn", "LINT"),
stable("allow", |o| o.optmulti("A", "allow", "Set lint allowed", "LINT")), opt(Stable, Multi, "D", "deny", "Set lint denied", "LINT"),
stable("warn", |o| o.optmulti("W", "warn", "Set lint warnings", "LINT")), opt(Stable, Multi, "F", "forbid", "Set lint forbidden", "LINT"),
stable("force-warn", |o| o.optmulti("", "force-warn", "Set lint force-warn", "LINT")), opt(
stable("deny", |o| o.optmulti("D", "deny", "Set lint denied", "LINT")), Stable,
stable("forbid", |o| o.optmulti("F", "forbid", "Set lint forbidden", "LINT")), Multi,
stable("cap-lints", |o| { "",
o.optmulti( "cap-lints",
"", "Set the most restrictive lint level. \
"cap-lints", More restrictive lints are capped at this level. \
"Set the most restrictive lint level. \ By default, it is at `forbid` level.",
More restrictive lints are capped at this \ "LEVEL",
level. By default, it is at `forbid` level.", ),
"LEVEL", opt(Unstable, Opt, "", "index-page", "Markdown file to be used as index page", "PATH"),
) opt(
}), Unstable,
unstable("index-page", |o| { FlagMulti,
o.optopt("", "index-page", "Markdown file to be used as index page", "PATH") "",
}), "enable-index-page",
unstable("enable-index-page", |o| { "To enable generation of the index page",
o.optflagmulti("", "enable-index-page", "To enable generation of the index page") "",
}), ),
unstable("static-root-path", |o| { opt(
o.optopt( Unstable,
"", Opt,
"static-root-path", "",
"Path string to force loading static files from in output pages. \ "static-root-path",
If not set, uses combinations of '../' to reach the documentation root.", "Path string to force loading static files from in output pages. \
"PATH", If not set, uses combinations of '../' to reach the documentation root.",
) "PATH",
}), ),
unstable("persist-doctests", |o| { opt(
o.optopt( Unstable,
"", Opt,
"persist-doctests", "",
"Directory to persist doctest executables into", "persist-doctests",
"PATH", "Directory to persist doctest executables into",
) "PATH",
}), ),
unstable("show-coverage", |o| { opt(
o.optflagmulti( Unstable,
"", FlagMulti,
"show-coverage", "",
"calculate percentage of public items with documentation", "show-coverage",
) "calculate percentage of public items with documentation",
}), "",
unstable("enable-per-target-ignores", |o| { ),
o.optflagmulti( opt(
"", Unstable,
"enable-per-target-ignores", FlagMulti,
"parse ignore-foo for ignoring doctests on a per-target basis", "",
) "enable-per-target-ignores",
}), "parse ignore-foo for ignoring doctests on a per-target basis",
unstable("runtool", |o| { "",
o.optopt( ),
"", opt(
"runtool", Unstable,
"", Opt,
"The tool to run tests with when building for a different target than host", "",
) "runtool",
}), "",
unstable("runtool-arg", |o| { "The tool to run tests with when building for a different target than host",
o.optmulti( ),
"", opt(
"runtool-arg", Unstable,
"", Multi,
"One (of possibly many) arguments to pass to the runtool", "",
) "runtool-arg",
}), "",
unstable("test-builder", |o| { "One (of possibly many) arguments to pass to the runtool",
o.optopt("", "test-builder", "The rustc-like binary to use as the test builder", "PATH") ),
}), opt(
unstable("test-builder-wrapper", |o| { Unstable,
o.optmulti( Opt,
"", "",
"test-builder-wrapper", "test-builder",
"Wrapper program to pass test-builder and arguments", "The rustc-like binary to use as the test builder",
"PATH", "PATH",
) ),
}), opt(
unstable("check", |o| o.optflagmulti("", "check", "Run rustdoc checks")), Unstable,
unstable("generate-redirect-map", |o| { Multi,
o.optflagmulti( "",
"", "test-builder-wrapper",
"generate-redirect-map", "Wrapper program to pass test-builder and arguments",
"Generate JSON file at the top level instead of generating HTML redirection files", "PATH",
) ),
}), opt(Unstable, FlagMulti, "", "check", "Run rustdoc checks", ""),
unstable("emit", |o| { opt(
o.optmulti( Unstable,
"", FlagMulti,
"emit", "",
"Comma separated list of types of output for rustdoc to emit", "generate-redirect-map",
"[unversioned-shared-resources,toolchain-shared-resources,invocation-specific]", "Generate JSON file at the top level instead of generating HTML redirection files",
) "",
}), ),
unstable("no-run", |o| { opt(
o.optflagmulti("", "no-run", "Compile doctests without running them") Unstable,
}), Multi,
unstable("remap-path-prefix", |o| { "",
o.optmulti( "emit",
"", "Comma separated list of types of output for rustdoc to emit",
"remap-path-prefix", "[unversioned-shared-resources,toolchain-shared-resources,invocation-specific]",
"Remap source names in compiler messages", ),
"FROM=TO", opt(Unstable, FlagMulti, "", "no-run", "Compile doctests without running them", ""),
) opt(
}), Unstable,
unstable("show-type-layout", |o| { Multi,
o.optflagmulti("", "show-type-layout", "Include the memory layout of types in the docs") "",
}), "remap-path-prefix",
unstable("nocapture", |o| { "Remap source names in compiler messages",
o.optflag("", "nocapture", "Don't capture stdout and stderr of tests") "FROM=TO",
}), ),
unstable("generate-link-to-definition", |o| { opt(
o.optflag( Unstable,
"", FlagMulti,
"generate-link-to-definition", "",
"Make the identifiers in the HTML source code pages navigable", "show-type-layout",
) "Include the memory layout of types in the docs",
}), "",
unstable("scrape-examples-output-path", |o| { ),
o.optopt( opt(Unstable, Flag, "", "nocapture", "Don't capture stdout and stderr of tests", ""),
"", opt(
"scrape-examples-output-path", Unstable,
"", Flag,
"collect function call information and output at the given path", "",
) "generate-link-to-definition",
}), "Make the identifiers in the HTML source code pages navigable",
unstable("scrape-examples-target-crate", |o| { "",
o.optmulti( ),
"", opt(
"scrape-examples-target-crate", Unstable,
"", Opt,
"collect function call information for functions from the target crate", "",
) "scrape-examples-output-path",
}), "",
unstable("scrape-tests", |o| { "collect function call information and output at the given path",
o.optflag("", "scrape-tests", "Include test code when scraping examples") ),
}), opt(
unstable("with-examples", |o| { Unstable,
o.optmulti( Multi,
"", "",
"with-examples", "scrape-examples-target-crate",
"", "",
"path to function call information (for displaying examples in the documentation)", "collect function call information for functions from the target crate",
) ),
}), opt(Unstable, Flag, "", "scrape-tests", "Include test code when scraping examples", ""),
unstable("merge", |o| { opt(
o.optopt( Unstable,
"", Multi,
"merge", "",
"Controls how rustdoc handles files from previously documented crates in the doc root "with-examples",
none = Do not write cross-crate information to the --out-dir "",
shared = Append current crate's info to files found in the --out-dir "path to function call information (for displaying examples in the documentation)",
finalize = Write current crate's info and --include-parts-dir info to the --out-dir, overwriting conflicting files", ),
"none|shared|finalize", opt(
) Unstable,
}), Opt,
unstable("parts-out-dir", |o| { "",
o.optopt( "merge",
"", "Controls how rustdoc handles files from previously documented crates in the doc root\n\
"parts-out-dir", none = Do not write cross-crate information to the --out-dir\n\
"Writes trait implementations and other info for the current crate to provided path. Only use with --merge=none", shared = Append current crate's info to files found in the --out-dir\n\
"path/to/doc.parts/<crate-name>", finalize = Write current crate's info and --include-parts-dir info to the --out-dir, overwriting conflicting files",
) "none|shared|finalize",
}), ),
unstable("include-parts-dir", |o| { opt(
o.optmulti( Unstable,
"", Opt,
"include-parts-dir", "",
"Includes trait implementations and other crate info from provided path. Only use with --merge=finalize", "parts-out-dir",
"path/to/doc.parts/<crate-name>", "Writes trait implementations and other info for the current crate to provided path. Only use with --merge=none",
) "path/to/doc.parts/<crate-name>",
}), ),
opt(
Unstable,
Multi,
"",
"include-parts-dir",
"Includes trait implementations and other crate info from provided path. Only use with --merge=finalize",
"path/to/doc.parts/<crate-name>",
),
// deprecated / removed options // deprecated / removed options
unstable("disable-minification", |o| o.optflagmulti("", "disable-minification", "removed")), opt(Unstable, FlagMulti, "", "disable-minification", "removed", ""),
stable("plugin-path", |o| { opt(
o.optmulti( Stable,
"", Multi,
"plugin-path", "",
"removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> \ "plugin-path",
for more information", "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
"DIR", "DIR",
) ),
}), opt(
stable("passes", |o| { Stable,
o.optmulti( Multi,
"", "",
"passes", "passes",
"removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> \ "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
for more information", "PASSES",
"PASSES", ),
) opt(
}), Stable,
stable("plugins", |o| { Multi,
o.optmulti( "",
"", "plugins",
"plugins", "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
"removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> \ "PLUGINS",
for more information", ),
"PLUGINS", opt(
) Stable,
}), FlagMulti,
stable("no-default", |o| { "",
o.optflagmulti( "no-defaults",
"", "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
"no-defaults", "",
"removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> \ ),
for more information", opt(
) Stable,
}), Opt,
stable("r", |o| { "r",
o.optopt( "input-format",
"r", "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
"input-format", "[rust]",
"removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> \ ),
for more information", opt(Unstable, Flag, "", "html-no-source", "Disable HTML source code pages generation", ""),
"[rust]",
)
}),
unstable("html-no-source", |o| {
o.optflag("", "html-no-source", "Disable HTML source code pages generation")
}),
] ]
} }
fn usage(argv0: &str) { fn usage(argv0: &str) {
let mut options = getopts::Options::new(); let mut options = getopts::Options::new();
for option in opts() { for option in opts() {
(option.apply)(&mut options); option.apply(&mut options);
} }
println!("{}", options.usage(&format!("{argv0} [options] <input>"))); println!("{}", options.usage(&format!("{argv0} [options] <input>")));
println!(" @path Read newline separated options from `path`\n"); println!(" @path Read newline separated options from `path`\n");
@ -769,7 +774,7 @@ fn main_args(
let mut options = getopts::Options::new(); let mut options = getopts::Options::new();
for option in opts() { for option in opts() {
(option.apply)(&mut options); option.apply(&mut options);
} }
let matches = match options.parse(&args) { let matches = match options.parse(&args) {
Ok(m) => m, Ok(m) => m,

@ -1 +1 @@
Subproject commit 0310497822a7a673a330a5dd068b7aaa579a265e Subproject commit 4a2d8dc636445b276288543882e076f254b3ae95

View File

@ -134,7 +134,7 @@ fn alloc_id_from_addr(&self, addr: u64, size: i64) -> Option<AllocId> {
// entered for addresses that are not the base address, so even zero-sized // entered for addresses that are not the base address, so even zero-sized
// allocations will get recognized at their base address -- but all other // allocations will get recognized at their base address -- but all other
// allocations will *not* be recognized at their "end" address. // allocations will *not* be recognized at their "end" address.
let size = this.get_alloc_info(alloc_id).0; let size = this.get_alloc_info(alloc_id).size;
if offset < size.bytes() { Some(alloc_id) } else { None } if offset < size.bytes() { Some(alloc_id) } else { None }
} }
}?; }?;
@ -157,25 +157,25 @@ fn addr_from_alloc_id_uncached(
) -> InterpResult<'tcx, u64> { ) -> InterpResult<'tcx, u64> {
let this = self.eval_context_ref(); let this = self.eval_context_ref();
let mut rng = this.machine.rng.borrow_mut(); let mut rng = this.machine.rng.borrow_mut();
let (size, align, kind) = this.get_alloc_info(alloc_id); let info = this.get_alloc_info(alloc_id);
// This is either called immediately after allocation (and then cached), or when // This is either called immediately after allocation (and then cached), or when
// adjusting `tcx` pointers (which never get freed). So assert that we are looking // adjusting `tcx` pointers (which never get freed). So assert that we are looking
// at a live allocation. This also ensures that we never re-assign an address to an // at a live allocation. This also ensures that we never re-assign an address to an
// allocation that previously had an address, but then was freed and the address // allocation that previously had an address, but then was freed and the address
// information was removed. // information was removed.
assert!(!matches!(kind, AllocKind::Dead)); assert!(!matches!(info.kind, AllocKind::Dead));
// This allocation does not have a base address yet, pick or reuse one. // This allocation does not have a base address yet, pick or reuse one.
if this.machine.native_lib.is_some() { if this.machine.native_lib.is_some() {
// In native lib mode, we use the "real" address of the bytes for this allocation. // In native lib mode, we use the "real" address of the bytes for this allocation.
// This ensures the interpreted program and native code have the same view of memory. // This ensures the interpreted program and native code have the same view of memory.
let base_ptr = match kind { let base_ptr = match info.kind {
AllocKind::LiveData => { AllocKind::LiveData => {
if this.tcx.try_get_global_alloc(alloc_id).is_some() { if this.tcx.try_get_global_alloc(alloc_id).is_some() {
// For new global allocations, we always pre-allocate the memory to be able use the machine address directly. // For new global allocations, we always pre-allocate the memory to be able use the machine address directly.
let prepared_bytes = MiriAllocBytes::zeroed(size, align) let prepared_bytes = MiriAllocBytes::zeroed(info.size, info.align)
.unwrap_or_else(|| { .unwrap_or_else(|| {
panic!("Miri ran out of memory: cannot create allocation of {size:?} bytes") panic!("Miri ran out of memory: cannot create allocation of {size:?} bytes", size = info.size)
}); });
let ptr = prepared_bytes.as_ptr(); let ptr = prepared_bytes.as_ptr();
// Store prepared allocation space to be picked up for use later. // Store prepared allocation space to be picked up for use later.
@ -203,9 +203,13 @@ fn addr_from_alloc_id_uncached(
return interp_ok(base_ptr.expose_provenance().try_into().unwrap()); return interp_ok(base_ptr.expose_provenance().try_into().unwrap());
} }
// We are not in native lib mode, so we control the addresses ourselves. // We are not in native lib mode, so we control the addresses ourselves.
if let Some((reuse_addr, clock)) = if let Some((reuse_addr, clock)) = global_state.reuse.take_addr(
global_state.reuse.take_addr(&mut *rng, size, align, memory_kind, this.active_thread()) &mut *rng,
{ info.size,
info.align,
memory_kind,
this.active_thread(),
) {
if let Some(clock) = clock { if let Some(clock) = clock {
this.acquire_clock(&clock); this.acquire_clock(&clock);
} }
@ -220,14 +224,14 @@ fn addr_from_alloc_id_uncached(
.next_base_addr .next_base_addr
.checked_add(slack) .checked_add(slack)
.ok_or_else(|| err_exhaust!(AddressSpaceFull))?; .ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
let base_addr = align_addr(base_addr, align.bytes()); let base_addr = align_addr(base_addr, info.align.bytes());
// Remember next base address. If this allocation is zero-sized, leave a gap of at // Remember next base address. If this allocation is zero-sized, leave a gap of at
// least 1 to avoid two allocations having the same base address. (The logic in // least 1 to avoid two allocations having the same base address. (The logic in
// `alloc_id_from_addr` assumes unique addresses, and different function/vtable pointers // `alloc_id_from_addr` assumes unique addresses, and different function/vtable pointers
// need to be distinguishable!) // need to be distinguishable!)
global_state.next_base_addr = base_addr global_state.next_base_addr = base_addr
.checked_add(max(size.bytes(), 1)) .checked_add(max(info.size.bytes(), 1))
.ok_or_else(|| err_exhaust!(AddressSpaceFull))?; .ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
// Even if `Size` didn't overflow, we might still have filled up the address space. // Even if `Size` didn't overflow, we might still have filled up the address space.
if global_state.next_base_addr > this.target_usize_max() { if global_state.next_base_addr > this.target_usize_max() {

View File

@ -363,7 +363,7 @@ fn on_stack_pop(
// If it does exist, then we have the guarantee that the // If it does exist, then we have the guarantee that the
// pointer is readable, and the implicit read access inserted // pointer is readable, and the implicit read access inserted
// will never cause UB on the pointer itself. // will never cause UB on the pointer itself.
let (_, _, kind) = this.get_alloc_info(*alloc_id); let kind = this.get_alloc_info(*alloc_id).kind;
if matches!(kind, AllocKind::LiveData) { if matches!(kind, AllocKind::LiveData) {
let alloc_extra = this.get_alloc_extra(*alloc_id)?; // can still fail for `extern static` let alloc_extra = this.get_alloc_extra(*alloc_id)?; // can still fail for `extern static`
let alloc_borrow_tracker = &alloc_extra.borrow_tracker.as_ref().unwrap(); let alloc_borrow_tracker = &alloc_extra.borrow_tracker.as_ref().unwrap();

View File

@ -626,7 +626,7 @@ fn sb_reborrow(
return interp_ok(()) return interp_ok(())
}; };
let (_size, _align, alloc_kind) = this.get_alloc_info(alloc_id); let alloc_kind = this.get_alloc_info(alloc_id).kind;
match alloc_kind { match alloc_kind {
AllocKind::LiveData => { AllocKind::LiveData => {
// This should have alloc_extra data, but `get_alloc_extra` can still fail // This should have alloc_extra data, but `get_alloc_extra` can still fail
@ -1017,7 +1017,7 @@ fn sb_expose_tag(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx
// Function pointers and dead objects don't have an alloc_extra so we ignore them. // Function pointers and dead objects don't have an alloc_extra so we ignore them.
// This is okay because accessing them is UB anyway, no need for any Stacked Borrows checks. // This is okay because accessing them is UB anyway, no need for any Stacked Borrows checks.
// NOT using `get_alloc_extra_mut` since this might be a read-only allocation! // NOT using `get_alloc_extra_mut` since this might be a read-only allocation!
let (_size, _align, kind) = this.get_alloc_info(alloc_id); let kind = this.get_alloc_info(alloc_id).kind;
match kind { match kind {
AllocKind::LiveData => { AllocKind::LiveData => {
// This should have alloc_extra data, but `get_alloc_extra` can still fail // This should have alloc_extra data, but `get_alloc_extra` can still fail

View File

@ -274,7 +274,7 @@ fn tb_reborrow(
.insert(new_tag, protect); .insert(new_tag, protect);
} }
let alloc_kind = this.get_alloc_info(alloc_id).2; let alloc_kind = this.get_alloc_info(alloc_id).kind;
if !matches!(alloc_kind, AllocKind::LiveData) { if !matches!(alloc_kind, AllocKind::LiveData) {
assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here
// There's not actually any bytes here where accesses could even be tracked. // There's not actually any bytes here where accesses could even be tracked.
@ -538,7 +538,7 @@ fn tb_expose_tag(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx
// Function pointers and dead objects don't have an alloc_extra so we ignore them. // Function pointers and dead objects don't have an alloc_extra so we ignore them.
// This is okay because accessing them is UB anyway, no need for any Tree Borrows checks. // This is okay because accessing them is UB anyway, no need for any Tree Borrows checks.
// NOT using `get_alloc_extra_mut` since this might be a read-only allocation! // NOT using `get_alloc_extra_mut` since this might be a read-only allocation!
let (_size, _align, kind) = this.get_alloc_info(alloc_id); let kind = this.get_alloc_info(alloc_id).kind;
match kind { match kind {
AllocKind::LiveData => { AllocKind::LiveData => {
// This should have alloc_extra data, but `get_alloc_extra` can still fail // This should have alloc_extra data, but `get_alloc_extra` can still fail

View File

@ -1125,10 +1125,10 @@ fn extern_static_pointer(
let Provenance::Concrete { alloc_id, .. } = ptr.provenance else { let Provenance::Concrete { alloc_id, .. } = ptr.provenance else {
panic!("extern_statics cannot contain wildcards") panic!("extern_statics cannot contain wildcards")
}; };
let (shim_size, shim_align, _kind) = ecx.get_alloc_info(alloc_id); let info = ecx.get_alloc_info(alloc_id);
let def_ty = ecx.tcx.type_of(def_id).instantiate_identity(); let def_ty = ecx.tcx.type_of(def_id).instantiate_identity();
let extern_decl_layout = ecx.tcx.layout_of(ty::ParamEnv::empty().and(def_ty)).unwrap(); let extern_decl_layout = ecx.tcx.layout_of(ty::ParamEnv::empty().and(def_ty)).unwrap();
if extern_decl_layout.size != shim_size || extern_decl_layout.align.abi != shim_align { if extern_decl_layout.size != info.size || extern_decl_layout.align.abi != info.align {
throw_unsup_format!( throw_unsup_format!(
"extern static `{link_name}` has been declared as `{krate}::{name}` \ "extern static `{link_name}` has been declared as `{krate}::{name}` \
with a size of {decl_size} bytes and alignment of {decl_align} bytes, \ with a size of {decl_size} bytes and alignment of {decl_align} bytes, \
@ -1138,8 +1138,8 @@ fn extern_static_pointer(
krate = ecx.tcx.crate_name(def_id.krate), krate = ecx.tcx.crate_name(def_id.krate),
decl_size = extern_decl_layout.size.bytes(), decl_size = extern_decl_layout.size.bytes(),
decl_align = extern_decl_layout.align.abi.bytes(), decl_align = extern_decl_layout.align.abi.bytes(),
shim_size = shim_size.bytes(), shim_size = info.size.bytes(),
shim_align = shim_align.bytes(), shim_align = info.align.bytes(),
) )
} }
interp_ok(ptr) interp_ok(ptr)

View File

@ -300,7 +300,7 @@ fn emulate_foreign_item_inner(
let id = this.read_scalar(id)?.to_u64()?; let id = this.read_scalar(id)?.to_u64()?;
let show_unnamed = this.read_scalar(show_unnamed)?.to_bool()?; let show_unnamed = this.read_scalar(show_unnamed)?.to_bool()?;
if let Some(id) = std::num::NonZero::new(id).map(AllocId) if let Some(id) = std::num::NonZero::new(id).map(AllocId)
&& this.get_alloc_info(id).2 == AllocKind::LiveData && this.get_alloc_info(id).kind == AllocKind::LiveData
{ {
this.print_borrow_state(id, show_unnamed)?; this.print_borrow_state(id, show_unnamed)?;
} else { } else {
@ -409,7 +409,7 @@ fn emulate_foreign_item_inner(
); );
} }
if let Ok((alloc_id, offset, ..)) = this.ptr_try_get_alloc_id(ptr, 0) { if let Ok((alloc_id, offset, ..)) = this.ptr_try_get_alloc_id(ptr, 0) {
let (_size, alloc_align, _kind) = this.get_alloc_info(alloc_id); let alloc_align = this.get_alloc_info(alloc_id).align;
// If the newly promised alignment is bigger than the native alignment of this // If the newly promised alignment is bigger than the native alignment of this
// allocation, and bigger than the previously promised alignment, then set it. // allocation, and bigger than the previously promised alignment, then set it.
if align > alloc_align if align > alloc_align

View File

@ -41,6 +41,7 @@ pub mod rfs {
pub use object; pub use object;
pub use regex; pub use regex;
pub use serde_json; pub use serde_json;
pub use similar;
pub use wasmparser; pub use wasmparser;
// tidy-alphabetical-end // tidy-alphabetical-end

View File

@ -4,7 +4,7 @@
//@[s390x] needs-llvm-components: systemz //@[s390x] needs-llvm-components: systemz
//@ compile-flags: -Zmerge-functions=disabled //@ compile-flags: -Zmerge-functions=disabled
#![feature(no_core, lang_items, rustc_attrs, repr_simd, asm_experimental_arch)] #![feature(no_core, lang_items, rustc_attrs, repr_simd)]
#![crate_type = "rlib"] #![crate_type = "rlib"]
#![no_core] #![no_core]
#![allow(asm_sub_register, non_camel_case_types)] #![allow(asm_sub_register, non_camel_case_types)]

View File

@ -3,7 +3,7 @@
//@[s390x] needs-llvm-components: systemz //@[s390x] needs-llvm-components: systemz
#![crate_type = "rlib"] #![crate_type = "rlib"]
#![feature(no_core, rustc_attrs, lang_items, asm_experimental_arch)] #![feature(no_core, rustc_attrs, lang_items)]
#![no_core] #![no_core]
#[lang = "sized"] #[lang = "sized"]

View File

@ -0,0 +1,11 @@
//@ force-host
//@ no-prefer-dynamic
#![crate_type = "proc-macro"]
extern crate proc_macro;
use proc_macro::*;
#[proc_macro]
pub fn square_twice(_item: TokenStream) -> TokenStream {
"(square(env::vars().count() as i32), square(env::vars().count() as i32))".parse().unwrap()
}

View File

@ -0,0 +1,53 @@
//@ min-llvm-version: 19
//@ compile-flags: -Cdebuginfo=2 -Copt-level=0 -Zmir-enable-passes=+Inline
// MSVC is different because of the individual allocas.
//@ ignore-msvc
//@ aux-build:macro_def.rs
// Find the variable.
// CHECK-DAG: ![[#var_dbg:]] = !DILocalVariable(name: "n",{{( arg: 1,)?}} scope: ![[#var_scope:]]
// Find both dbg_declares. These will proceed the variable metadata, of course, so we're looking
// backwards.
// CHECK-DAG: dbg_declare(ptr %n.dbg.spill{{[0-9]}}, ![[#var_dbg]], !DIExpression(), ![[#var_loc2:]])
// CHECK-DAG: dbg_declare(ptr %n.dbg.spill, ![[#var_dbg]], !DIExpression(), ![[#var_loc1:]])
// Find the first location definition, looking forwards again.
// CHECK: ![[#var_loc1]] = !DILocation
// CHECK-SAME: scope: ![[#var_scope:]], inlinedAt: ![[#var_inlinedAt1:]]
// Find the first location's inlinedAt
// NB: If we fail here it's *probably* because we failed to produce two
// different locations and ended up reusing an earlier one.
// CHECK: ![[#var_inlinedAt1]] = !DILocation
// CHECK-SAME: scope: ![[var_inlinedAt1_scope:]]
// Find the second location definition, still looking forwards.
// NB: If we failed to produce two different locations, the test will
// definitely fail by this point (if it hasn't already) because we won't
// be able to find the same line again.
// CHECK: ![[#var_loc2]] = !DILocation
// CHECK-SAME: scope: ![[#var_scope]], inlinedAt: ![[#var_inlinedAt2:]]
// Find the second location's inlinedAt.
// CHECK: ![[#var_inlinedAt2]] = !DILocation
// CHECK-SAME: scope: ![[#var_inlinedAt2_scope:]]
// Finally, check that a discriminator was emitted for the second scope.
// FIXMEkhuey ideally we would check that *either* scope has a discriminator
// but I don't know that it's possible to check that with FileCheck.
// CHECK: ![[#var_inlinedAt2_scope]] = !DILexicalBlockFile
// CHECK-SAME: discriminator: [[#]]
extern crate macro_def;
use std::env;
fn square(n: i32) -> i32 {
n * n
}
fn main() {
let (z1, z2) = macro_def::square_twice!();
println!("{z1} == {z2}");
}

View File

@ -1,5 +1,6 @@
//@ compile-flags: -O -C no-prepopulate-passes //@ compile-flags: -O -C no-prepopulate-passes
#![crate_type = "lib"] #![crate_type = "lib"]
#![feature(rustc_attrs)]
#![feature(dyn_star)] #![feature(dyn_star)]
#![feature(allocator_api)] #![feature(allocator_api)]
@ -143,13 +144,28 @@ pub fn indirect_struct(_: S) {}
#[no_mangle] #[no_mangle]
pub fn borrowed_struct(_: &S) {} pub fn borrowed_struct(_: &S) {}
// CHECK: @option_borrow(ptr noalias noundef readonly align 4 dereferenceable_or_null(4) %x) // CHECK: @option_borrow(ptr noalias noundef readonly align 4 dereferenceable_or_null(4) %_x)
#[no_mangle] #[no_mangle]
pub fn option_borrow(x: Option<&i32>) {} pub fn option_borrow(_x: Option<&i32>) {}
// CHECK: @option_borrow_mut(ptr noalias noundef align 4 dereferenceable_or_null(4) %x) // CHECK: @option_borrow_mut(ptr noalias noundef align 4 dereferenceable_or_null(4) %_x)
#[no_mangle] #[no_mangle]
pub fn option_borrow_mut(x: Option<&mut i32>) {} pub fn option_borrow_mut(_x: Option<&mut i32>) {}
// Function that must NOT have `dereferenceable` or `align`.
#[rustc_layout_scalar_valid_range_start(16)]
pub struct RestrictedAddress(&'static i16);
enum E {
A(RestrictedAddress),
B,
C,
}
// If the `nonnull` ever goes missing, you might have to tweak the
// scalar_valid_range on `RestrictedAddress` to get it back. You
// might even have to add a `rustc_layout_scalar_valid_range_end`.
// CHECK: @nonnull_and_nondereferenceable(ptr noundef nonnull %_x)
#[no_mangle]
pub fn nonnull_and_nondereferenceable(_x: E) {}
// CHECK: @raw_struct(ptr noundef %_1) // CHECK: @raw_struct(ptr noundef %_1)
#[no_mangle] #[no_mangle]

View File

@ -1,6 +1,10 @@
//@ compile-flags: -O -Z merge-functions=disabled --edition=2021 //@ compile-flags: -O -Z merge-functions=disabled --edition=2021
//@ only-x86_64 //@ only-x86_64
// FIXME: Remove the `min-llvm-version`. // FIXME: Remove the `min-llvm-version`.
//@ revisions: NINETEEN TWENTY
//@[NINETEEN] min-llvm-version: 19
//@[NINETEEN] ignore-llvm-version: 20-99
//@[TWENTY] min-llvm-version: 20
//@ min-llvm-version: 19 //@ min-llvm-version: 19
#![crate_type = "lib"] #![crate_type = "lib"]
@ -13,8 +17,11 @@
#[no_mangle] #[no_mangle]
pub fn option_nop_match_32(x: Option<u32>) -> Option<u32> { pub fn option_nop_match_32(x: Option<u32>) -> Option<u32> {
// CHECK: start: // CHECK: start:
// TWENTY-NEXT: %trunc = trunc nuw i32 %0 to i1
// TWENTY-NEXT: %.2 = select i1 %trunc, i32 %1, i32 undef
// CHECK-NEXT: [[REG1:%.*]] = insertvalue { i32, i32 } poison, i32 %0, 0 // CHECK-NEXT: [[REG1:%.*]] = insertvalue { i32, i32 } poison, i32 %0, 0
// CHECK-NEXT: [[REG2:%.*]] = insertvalue { i32, i32 } [[REG1]], i32 %1, 1 // NINETEEN-NEXT: [[REG2:%.*]] = insertvalue { i32, i32 } [[REG1]], i32 %1, 1
// TWENTY-NEXT: [[REG2:%.*]] = insertvalue { i32, i32 } [[REG1]], i32 %.2, 1
// CHECK-NEXT: ret { i32, i32 } [[REG2]] // CHECK-NEXT: ret { i32, i32 } [[REG2]]
match x { match x {
Some(x) => Some(x), Some(x) => Some(x),
@ -26,6 +33,8 @@ pub fn option_nop_match_32(x: Option<u32>) -> Option<u32> {
#[no_mangle] #[no_mangle]
pub fn option_nop_traits_32(x: Option<u32>) -> Option<u32> { pub fn option_nop_traits_32(x: Option<u32>) -> Option<u32> {
// CHECK: start: // CHECK: start:
// TWENTY-NEXT: %trunc = trunc nuw i32 %0 to i1
// TWENTY-NEXT: %.1 = select i1 %trunc, i32 %1, i32 undef
// CHECK-NEXT: insertvalue { i32, i32 } // CHECK-NEXT: insertvalue { i32, i32 }
// CHECK-NEXT: insertvalue { i32, i32 } // CHECK-NEXT: insertvalue { i32, i32 }
// CHECK-NEXT: ret { i32, i32 } // CHECK-NEXT: ret { i32, i32 }

View File

@ -0,0 +1,29 @@
@@ -51,10 +51,27 @@
Set a codegen option
-V, --version Print version info and exit
-v, --verbose Use verbose output
+ --extern NAME[=PATH]
+ Specify where an external rust library is located
+ --sysroot PATH Override the system root
+ --error-format human|json|short
+ How errors and other messages are produced
+ --json CONFIG Configure the JSON output of the compiler
+ --color auto|always|never
+ Configure coloring of output:
+ auto = colorize, if output goes to a tty (default);
+ always = always colorize output;
+ never = never colorize output
+ --diagnostic-width WIDTH
+ Inform rustc of the width of the output so that
+ diagnostics can be truncated to fit
+ --remap-path-prefix FROM=TO
+ Remap source names in all output (compiler messages
+ and output files)
+ @path Read newline separated options from `path`
Additional help:
-C help Print codegen options
-W help Print 'lint' options and default settings
-Z help Print unstable compiler options
- --help -v Print the full set of options rustc accepts

View File

@ -0,0 +1,77 @@
Usage: rustc [OPTIONS] INPUT
Options:
-h, --help Display this message
--cfg SPEC Configure the compilation environment.
SPEC supports the syntax `NAME[="VALUE"]`.
--check-cfg SPEC
Provide list of expected cfgs for checking
-L [KIND=]PATH Add a directory to the library search path. The
optional KIND can be one of dependency, crate, native,
framework, or all (the default).
-l [KIND[:MODIFIERS]=]NAME[:RENAME]
Link the generated crate(s) to the specified native
library NAME. The optional KIND can be one of
static, framework, or dylib (the default).
Optional comma separated MODIFIERS
(bundle|verbatim|whole-archive|as-needed)
may be specified each with a prefix of either '+' to
enable or '-' to disable.
--crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
Comma separated list of types of crates
for the compiler to emit
--crate-name NAME
Specify the name of the crate being built
--edition 2015|2018|2021|2024
Specify which edition of the compiler to use when
compiling code. The default is 2015 and the latest
stable edition is 2021.
--emit [asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info|mir]
Comma separated list of types of output for the
compiler to emit
--print [crate-name|file-names|sysroot|target-libdir|cfg|check-cfg|calling-conventions|target-list|target-cpus|target-features|relocation-models|code-models|tls-models|target-spec-json|all-target-specs-json|native-static-libs|stack-protector-strategies|link-args|deployment-target]
Compiler information to print on stdout
-g Equivalent to -C debuginfo=2
-O Equivalent to -C opt-level=2
-o FILENAME Write output to <filename>
--out-dir DIR Write output to compiler-chosen filename in <dir>
--explain OPT Provide a detailed explanation of an error message
--test Build a test harness
--target TARGET Target triple for which the code is compiled
-A, --allow LINT Set lint allowed
-W, --warn LINT Set lint warnings
--force-warn LINT
Set lint force-warn
-D, --deny LINT Set lint denied
-F, --forbid LINT Set lint forbidden
--cap-lints LEVEL
Set the most restrictive lint level. More restrictive
lints are capped at this level
-C, --codegen OPT[=VALUE]
Set a codegen option
-V, --version Print version info and exit
-v, --verbose Use verbose output
--extern NAME[=PATH]
Specify where an external rust library is located
--sysroot PATH Override the system root
--error-format human|json|short
How errors and other messages are produced
--json CONFIG Configure the JSON output of the compiler
--color auto|always|never
Configure coloring of output:
auto = colorize, if output goes to a tty (default);
always = always colorize output;
never = never colorize output
--diagnostic-width WIDTH
Inform rustc of the width of the output so that
diagnostics can be truncated to fit
--remap-path-prefix FROM=TO
Remap source names in all output (compiler messages
and output files)
@path Read newline separated options from `path`
Additional help:
-C help Print codegen options
-W help Print 'lint' options and default settings
-Z help Print unstable compiler options

View File

@ -0,0 +1,60 @@
Usage: rustc [OPTIONS] INPUT
Options:
-h, --help Display this message
--cfg SPEC Configure the compilation environment.
SPEC supports the syntax `NAME[="VALUE"]`.
--check-cfg SPEC
Provide list of expected cfgs for checking
-L [KIND=]PATH Add a directory to the library search path. The
optional KIND can be one of dependency, crate, native,
framework, or all (the default).
-l [KIND[:MODIFIERS]=]NAME[:RENAME]
Link the generated crate(s) to the specified native
library NAME. The optional KIND can be one of
static, framework, or dylib (the default).
Optional comma separated MODIFIERS
(bundle|verbatim|whole-archive|as-needed)
may be specified each with a prefix of either '+' to
enable or '-' to disable.
--crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
Comma separated list of types of crates
for the compiler to emit
--crate-name NAME
Specify the name of the crate being built
--edition 2015|2018|2021|2024
Specify which edition of the compiler to use when
compiling code. The default is 2015 and the latest
stable edition is 2021.
--emit [asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info|mir]
Comma separated list of types of output for the
compiler to emit
--print [crate-name|file-names|sysroot|target-libdir|cfg|check-cfg|calling-conventions|target-list|target-cpus|target-features|relocation-models|code-models|tls-models|target-spec-json|all-target-specs-json|native-static-libs|stack-protector-strategies|link-args|deployment-target]
Compiler information to print on stdout
-g Equivalent to -C debuginfo=2
-O Equivalent to -C opt-level=2
-o FILENAME Write output to <filename>
--out-dir DIR Write output to compiler-chosen filename in <dir>
--explain OPT Provide a detailed explanation of an error message
--test Build a test harness
--target TARGET Target triple for which the code is compiled
-A, --allow LINT Set lint allowed
-W, --warn LINT Set lint warnings
--force-warn LINT
Set lint force-warn
-D, --deny LINT Set lint denied
-F, --forbid LINT Set lint forbidden
--cap-lints LEVEL
Set the most restrictive lint level. More restrictive
lints are capped at this level
-C, --codegen OPT[=VALUE]
Set a codegen option
-V, --version Print version info and exit
-v, --verbose Use verbose output
Additional help:
-C help Print codegen options
-W help Print 'lint' options and default settings
-Z help Print unstable compiler options
--help -v Print the full set of options rustc accepts

View File

@ -0,0 +1,21 @@
// Tests `rustc --help` and similar invocations against snapshots and each other.
use run_make_support::{bare_rustc, diff, similar};
fn main() {
// `rustc --help`
let help = bare_rustc().arg("--help").run().stdout_utf8();
diff().expected_file("help.stdout").actual_text("(rustc --help)", &help).run();
// `rustc` should be the same as `rustc --help`
let bare = bare_rustc().run().stdout_utf8();
diff().expected_text("(rustc --help)", &help).actual_text("(rustc)", &bare).run();
// `rustc --help -v` should give a similar but longer help message
let help_v = bare_rustc().arg("--help").arg("-v").run().stdout_utf8();
diff().expected_file("help-v.stdout").actual_text("(rustc --help -v)", &help_v).run();
// Check the diff between `rustc --help` and `rustc --help -v`.
let help_v_diff = similar::TextDiff::from_lines(&help, &help_v).unified_diff().to_string();
diff().expected_file("help-v.diff").actual_text("actual", &help_v_diff).run();
}

View File

@ -0,0 +1,78 @@
//@ compile-flags: -Z unstable-options --document-hidden-items --document-private-items
// regression test for https://github.com/rust-lang/rust/issues/90781
#![crate_name = "foo"]
//@ has foo/trait.TPubVis.html
//@ has - '//*[@id="implementors-list"]' 'HidPriv'
//@ has - '//*[@id="implementors-list"]' 'HidPub'
//@ has - '//*[@id="implementors-list"]' 'VisPriv'
//@ has - '//*[@id="implementors-list"]' 'VisPub'
pub trait TPubVis {}
//@ has foo/trait.TPubHidden.html
//@ has - '//*[@id="implementors-list"]' 'HidPriv'
//@ has - '//*[@id="implementors-list"]' 'HidPub'
//@ has - '//*[@id="implementors-list"]' 'VisPriv'
//@ has - '//*[@id="implementors-list"]' 'VisPub'
#[doc(hidden)]
pub trait TPubHidden {}
//@ has foo/trait.TPrivVis.html
//@ has - '//*[@id="implementors-list"]' 'HidPriv'
//@ has - '//*[@id="implementors-list"]' 'HidPub'
//@ has - '//*[@id="implementors-list"]' 'VisPriv'
//@ has - '//*[@id="implementors-list"]' 'VisPub'
trait TPrivVis {}
#[doc(hidden)]
//@ has foo/trait.TPrivHidden.html
//@ has - '//*[@id="impl-TPrivHidden-for-HidPriv"]' 'HidPriv'
//@ has - '//*[@id="impl-TPrivHidden-for-HidPub"]' 'HidPub'
//@ has - '//*[@id="impl-TPrivHidden-for-VisPriv"]' 'VisPriv'
//@ has - '//*[@id="impl-TPrivHidden-for-VisPub"]' 'VisPub'
trait TPrivHidden {}
//@ has foo/struct.VisPub.html
//@ has - '//*[@id="trait-implementations-list"]' 'TPrivHidden'
//@ has - '//*[@id="trait-implementations-list"]' 'TPrivVis'
//@ has - '//*[@id="trait-implementations-list"]' 'TPubHidden'
//@ has - '//*[@id="trait-implementations-list"]' 'TPubVis'
pub struct VisPub;
//@ has foo/struct.VisPriv.html
//@ has - '//*[@id="trait-implementations-list"]' 'TPrivHidden'
//@ has - '//*[@id="trait-implementations-list"]' 'TPrivVis'
//@ has - '//*[@id="trait-implementations-list"]' 'TPubHidden'
//@ has - '//*[@id="trait-implementations-list"]' 'TPubVis'
struct VisPriv;
//@ has foo/struct.HidPub.html
//@ has - '//*[@id="trait-implementations-list"]' 'TPrivHidden'
//@ has - '//*[@id="trait-implementations-list"]' 'TPrivVis'
//@ has - '//*[@id="trait-implementations-list"]' 'TPubHidden'
//@ has - '//*[@id="trait-implementations-list"]' 'TPubVis'
#[doc(hidden)]
pub struct HidPub;
//@ has foo/struct.HidPriv.html
//@ has - '//*[@id="trait-implementations-list"]' 'TPrivHidden'
//@ has - '//*[@id="trait-implementations-list"]' 'TPrivVis'
//@ has - '//*[@id="trait-implementations-list"]' 'TPubHidden'
//@ has - '//*[@id="trait-implementations-list"]' 'TPubVis'
#[doc(hidden)]
struct HidPriv;
macro_rules! implement {
($trait:ident - $($struct:ident)+) => {
$(
impl $trait for $struct {}
)+
}
}
implement!(TPubVis - VisPub VisPriv HidPub HidPriv);
implement!(TPubHidden - VisPub VisPriv HidPub HidPriv);
implement!(TPrivVis - VisPub VisPriv HidPub HidPriv);
implement!(TPrivHidden - VisPub VisPriv HidPub HidPriv);

View File

@ -0,0 +1,43 @@
//@ revisions: sparc sparcv8plus sparc_cpu_v9 sparc_feature_v8plus sparc_cpu_v9_feature_v8plus
//@[sparc] compile-flags: --target sparc-unknown-none-elf
//@[sparc] needs-llvm-components: sparc
//@[sparcv8plus] compile-flags: --target sparc-unknown-linux-gnu
//@[sparcv8plus] needs-llvm-components: sparc
//@[sparc_cpu_v9] compile-flags: --target sparc-unknown-none-elf -C target-cpu=v9
//@[sparc_cpu_v9] needs-llvm-components: sparc
//@[sparc_feature_v8plus] compile-flags: --target sparc-unknown-none-elf -C target-feature=+v8plus
//@[sparc_feature_v8plus] needs-llvm-components: sparc
//@[sparc_cpu_v9_feature_v8plus] compile-flags: --target sparc-unknown-none-elf -C target-cpu=v9 -C target-feature=+v8plus
//@[sparc_cpu_v9_feature_v8plus] needs-llvm-components: sparc
//@ min-llvm-version: 19
#![crate_type = "rlib"]
#![feature(no_core, rustc_attrs, lang_items)]
#![no_core]
#[lang = "sized"]
trait Sized {}
#[lang = "copy"]
trait Copy {}
#[rustc_builtin_macro]
macro_rules! compile_error {
() => {};
}
#[cfg(all(not(target_feature = "v8plus"), not(target_feature = "v9")))]
compile_error!("-v8plus,-v9");
//[sparc]~^ ERROR -v8plus,-v9
// FIXME: sparc_cpu_v9 should be in "-v8plus,+v9" group (fixed in LLVM 20)
#[cfg(all(target_feature = "v8plus", target_feature = "v9"))]
compile_error!("+v8plus,+v9");
//[sparcv8plus,sparc_cpu_v9_feature_v8plus,sparc_cpu_v9]~^ ERROR +v8plus,+v9
// FIXME: should be rejected
#[cfg(all(target_feature = "v8plus", not(target_feature = "v9")))]
compile_error!("+v8plus,-v9 (FIXME)");
//[sparc_feature_v8plus]~^ ERROR +v8plus,-v9 (FIXME)
#[cfg(all(not(target_feature = "v8plus"), target_feature = "v9"))]
compile_error!("-v8plus,+v9");

View File

@ -0,0 +1,8 @@
error: -v8plus,-v9
--> $DIR/sparcv8plus.rs:29:1
|
LL | compile_error!("-v8plus,-v9");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error

Some files were not shown because too many files have changed in this diff Show More