Merge pull request #20761 from rust-lang/rustc-pull

Rustc pull update
This commit is contained in:
Laurențiu Nicola 2025-09-29 04:29:28 +00:00 committed by GitHub
commit 930451e17d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
734 changed files with 9701 additions and 4314 deletions

View file

@ -31,8 +31,8 @@ bootstrapping, the compiler architecture, source code representation, and more.
## [Getting help](https://rustc-dev-guide.rust-lang.org/getting-started.html#asking-questions)
There are many ways you can get help when you're stuck. Rust has many platforms for this:
[internals], [rust-zulip], and [rust-discord]. It is recommended to ask for help on
There are many ways you can get help when you're stuck. Rust has two platforms for this:
[internals] and [rust-zulip]. It is recommended to ask for help on
the [rust-zulip], but any of these platforms are great ways to seek help and even
find a mentor! You can learn more about asking questions and getting help in the
[Asking Questions](https://rustc-dev-guide.rust-lang.org/getting-started.html#asking-questions) chapter of the [rustc-dev-guide].
@ -47,5 +47,4 @@ refer to [this section][contributing-bug-reports] and [open an issue][issue temp
[contributing-bug-reports]: https://rustc-dev-guide.rust-lang.org/contributing.html#bug-reports
[issue template]: https://github.com/rust-lang/rust/issues/new/choose
[internals]: https://internals.rust-lang.org
[rust-discord]: http://discord.gg/rust-lang
[rust-zulip]: https://rust-lang.zulipchat.com

View file

@ -334,8 +334,10 @@ dependencies = [
"anyhow",
"build_helper",
"curl",
"hex",
"indexmap",
"serde",
"sha2",
"toml 0.8.23",
]
@ -5239,9 +5241,9 @@ dependencies = [
[[package]]
name = "stringdex"
version = "0.0.1-alpha9"
version = "0.0.1-alpha10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7081029913fd7d591c0112182aba8c98ae886b4f12edb208130496cd17dc3c15"
checksum = "0fa846a7d509d1828a4f90962dc09810e161abcada7fc6a921e92c168d0811d7"
dependencies = [
"stacker",
]

View file

@ -768,8 +768,7 @@
# make this default to false.
#rust.lld = false in all cases, except on `x86_64-unknown-linux-gnu` as described above, where it is true
# Indicates whether LLD will be used to link Rust crates during bootstrap on
# supported platforms.
# Indicates if we should override the linker used to link Rust crates during bootstrap to be LLD.
# If set to `true` or `"external"`, a global `lld` binary that has to be in $PATH
# will be used.
# If set to `"self-contained"`, rust-lld from the snapshot compiler will be used.
@ -777,7 +776,7 @@
# On MSVC, LLD will not be used if we're cross linking.
#
# Explicitly setting the linker for a target will override this option when targeting MSVC.
#rust.use-lld = false
#rust.bootstrap-override-lld = false
# Indicates whether some LLVM tools, like llvm-objdump, will be made available in the
# sysroot.
@ -950,7 +949,7 @@
# Linker to be used to bootstrap Rust code. Note that the
# default value is platform specific, and if not specified it may also depend on
# what platform is crossing to what platform.
# Setting this will override the `use-lld` option for Rust code when targeting MSVC.
# Setting this will override the `bootstrap-override-lld` option for Rust code when targeting MSVC.
#linker = "cc" (path)
# Should rustc and the standard library be built with split debuginfo? Default

View file

@ -6,6 +6,7 @@
use std::fmt::{self, Display, Formatter};
use std::str::FromStr;
use crate::expand::typetree::TypeTree;
use crate::expand::{Decodable, Encodable, HashStable_Generic};
use crate::{Ty, TyKind};
@ -84,6 +85,8 @@ pub struct AutoDiffItem {
/// The name of the function being generated
pub target: String,
pub attrs: AutoDiffAttrs,
pub inputs: Vec<TypeTree>,
pub output: TypeTree,
}
#[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
@ -275,14 +278,22 @@ impl AutoDiffAttrs {
!matches!(self.mode, DiffMode::Error | DiffMode::Source)
}
pub fn into_item(self, source: String, target: String) -> AutoDiffItem {
AutoDiffItem { source, target, attrs: self }
pub fn into_item(
self,
source: String,
target: String,
inputs: Vec<TypeTree>,
output: TypeTree,
) -> AutoDiffItem {
AutoDiffItem { source, target, inputs, output, attrs: self }
}
}
impl fmt::Display for AutoDiffItem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Differentiating {} -> {}", self.source, self.target)?;
write!(f, " with attributes: {:?}", self.attrs)
write!(f, " with attributes: {:?}", self.attrs)?;
write!(f, " with inputs: {:?}", self.inputs)?;
write!(f, " with output: {:?}", self.output)
}
}

View file

@ -31,6 +31,7 @@ pub enum Kind {
Half,
Float,
Double,
F128,
Unknown,
}

View file

@ -487,26 +487,6 @@ fn expand_format_args<'hir>(
// Generate:
// []
(vec![], ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(&[]))))
} else if argmap.len() == 1 && arguments.len() == 1 {
// Only one argument, so we don't need to make the `args` tuple.
//
// Generate:
// super let args = [<core::fmt::Argument>::new_display(&arg)];
let args = ctx.arena.alloc_from_iter(argmap.iter().map(
|(&(arg_index, ty), &placeholder_span)| {
let arg = &arguments[arg_index];
let placeholder_span =
placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt());
let arg = ctx.lower_expr(&arg.expr);
let ref_arg = ctx.arena.alloc(ctx.expr_ref(arg.span.with_ctxt(macsp.ctxt()), arg));
make_argument(ctx, placeholder_span, ref_arg, ty)
},
));
let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
let args_ident = Ident::new(sym::args, macsp);
let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
let let_statement = ctx.stmt_super_let_pat(macsp, args_pat, Some(args));
(vec![let_statement], ctx.arena.alloc(ctx.expr_ident_mut(macsp, args_ident, args_hir_id)))
} else {
// Generate:
// super let args = (&arg0, &arg1, &…);

View file

@ -183,7 +183,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
gate_doc!(
"experimental" {
cfg => doc_cfg
cfg_hide => doc_cfg_hide
auto_cfg => doc_cfg
masked => doc_masked
notable_trait => doc_notable_trait
}

View file

@ -1,40 +1,4 @@
use std::num::IntErrorKind;
use rustc_hir::limit::Limit;
use super::prelude::*;
use crate::session_diagnostics::LimitInvalid;
impl<S: Stage> AcceptContext<'_, '_, S> {
fn parse_limit_int(&self, nv: &NameValueParser) -> Option<Limit> {
let Some(limit) = nv.value_as_str() else {
self.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
return None;
};
let error_str = match limit.as_str().parse() {
Ok(i) => return Some(Limit::new(i)),
Err(e) => match e.kind() {
IntErrorKind::PosOverflow => "`limit` is too large",
IntErrorKind::Empty => "`limit` must be a non-negative integer",
IntErrorKind::InvalidDigit => "not a valid integer",
IntErrorKind::NegOverflow => {
panic!(
"`limit` should never negatively overflow since we're parsing into a usize and we'd get Empty instead"
)
}
IntErrorKind::Zero => {
panic!("zero is a valid `limit` so should have returned Ok() when parsing")
}
kind => panic!("unimplemented IntErrorKind variant: {:?}", kind),
},
};
self.emit_err(LimitInvalid { span: self.attr_span, value_span: nv.value_span, error_str });
None
}
}
pub(crate) struct CrateNameParser;

View file

@ -0,0 +1,60 @@
use rustc_hir::attrs::{DebugVisualizer, DebuggerVisualizerType};
use super::prelude::*;
pub(crate) struct DebuggerViualizerParser;
impl<S: Stage> CombineAttributeParser<S> for DebuggerViualizerParser {
const PATH: &[Symbol] = &[sym::debugger_visualizer];
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::Mod), Allow(Target::Crate)]);
const TEMPLATE: AttributeTemplate = template!(
List: &[r#"natvis_file = "...", gdb_script_file = "...""#],
"https://doc.rust-lang.org/reference/attributes/debugger.html#the-debugger_visualizer-attribute"
);
type Item = DebugVisualizer;
const CONVERT: ConvertFn<Self::Item> = |v, _| AttributeKind::DebuggerVisualizer(v);
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = Self::Item> + 'c {
let Some(l) = args.list() else {
cx.expected_list(args.span().unwrap_or(cx.attr_span));
return None;
};
let Some(single) = l.single() else {
cx.expected_single_argument(l.span);
return None;
};
let Some(mi) = single.meta_item() else {
cx.expected_name_value(single.span(), None);
return None;
};
let path = mi.path().word_sym();
let visualizer_type = match path {
Some(sym::natvis_file) => DebuggerVisualizerType::Natvis,
Some(sym::gdb_script_file) => DebuggerVisualizerType::GdbPrettyPrinter,
_ => {
cx.expected_specific_argument(
mi.path().span(),
&[sym::natvis_file, sym::gdb_script_file],
);
return None;
}
};
let Some(path) = mi.args().name_value() else {
cx.expected_name_value(single.span(), path);
return None;
};
let Some(path) = path.value_as_str() else {
cx.expected_string_literal(path.value_span, Some(path.value_as_lit()));
return None;
};
Some(DebugVisualizer { span: mi.span(), visualizer_type, path })
}
}

View file

@ -36,6 +36,7 @@ pub(crate) mod cfg_old;
pub(crate) mod codegen_attrs;
pub(crate) mod confusables;
pub(crate) mod crate_level;
pub(crate) mod debugger;
pub(crate) mod deprecation;
pub(crate) mod dummy;
pub(crate) mod inline;

View file

@ -49,3 +49,21 @@ impl<S: Stage> SingleAttributeParser<S> for RustcObjectLifetimeDefaultParser {
Some(AttributeKind::RustcObjectLifetimeDefault)
}
}
pub(crate) struct RustcSimdMonomorphizeLaneLimitParser;
impl<S: Stage> SingleAttributeParser<S> for RustcSimdMonomorphizeLaneLimitParser {
const PATH: &[Symbol] = &[sym::rustc_simd_monomorphize_lane_limit];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let ArgParser::NameValue(nv) = args else {
cx.expected_name_value(cx.attr_span, None);
return None;
};
Some(AttributeKind::RustcSimdMonomorphizeLaneLimit(cx.parse_limit_int(nv)?))
}
}

View file

@ -1,11 +1,15 @@
use std::num::IntErrorKind;
use rustc_ast::LitKind;
use rustc_ast::attr::AttributeExt;
use rustc_feature::is_builtin_attr_name;
use rustc_hir::RustcVersion;
use rustc_hir::limit::Limit;
use rustc_span::{Symbol, sym};
use crate::context::{AcceptContext, Stage};
use crate::parser::ArgParser;
use crate::parser::{ArgParser, NameValueParser};
use crate::session_diagnostics::LimitInvalid;
/// Parse a rustc version number written inside string literal in an attribute,
/// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are
@ -85,3 +89,34 @@ pub(crate) fn parse_single_integer<S: Stage>(
};
Some(num.0)
}
impl<S: Stage> AcceptContext<'_, '_, S> {
pub(crate) fn parse_limit_int(&self, nv: &NameValueParser) -> Option<Limit> {
let Some(limit) = nv.value_as_str() else {
self.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
return None;
};
let error_str = match limit.as_str().parse() {
Ok(i) => return Some(Limit::new(i)),
Err(e) => match e.kind() {
IntErrorKind::PosOverflow => "`limit` is too large",
IntErrorKind::Empty => "`limit` must be a non-negative integer",
IntErrorKind::InvalidDigit => "not a valid integer",
IntErrorKind::NegOverflow => {
panic!(
"`limit` should never negatively overflow since we're parsing into a usize and we'd get Empty instead"
)
}
IntErrorKind::Zero => {
panic!("zero is a valid `limit` so should have returned Ok() when parsing")
}
kind => panic!("unimplemented IntErrorKind variant: {:?}", kind),
},
};
self.emit_err(LimitInvalid { span: self.attr_span, value_span: nv.value_span, error_str });
None
}
}

View file

@ -28,6 +28,7 @@ use crate::attributes::crate_level::{
CrateNameParser, MoveSizeLimitParser, NoCoreParser, NoStdParser, PatternComplexityLimitParser,
RecursionLimitParser, RustcCoherenceIsCoreParser, TypeLengthLimitParser,
};
use crate::attributes::debugger::DebuggerViualizerParser;
use crate::attributes::deprecation::DeprecationParser;
use crate::attributes::dummy::DummyParser;
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
@ -53,7 +54,7 @@ use crate::attributes::prototype::CustomMirParser;
use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser};
use crate::attributes::rustc_internal::{
RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart,
RustcObjectLifetimeDefaultParser,
RustcObjectLifetimeDefaultParser, RustcSimdMonomorphizeLaneLimitParser,
};
use crate::attributes::semantics::MayDangleParser;
use crate::attributes::stability::{
@ -163,6 +164,7 @@ attribute_parsers!(
// tidy-alphabetical-start
Combine<AllowConstFnUnstableParser>,
Combine<AllowInternalUnstableParser>,
Combine<DebuggerViualizerParser>,
Combine<ForceTargetFeatureParser>,
Combine<LinkParser>,
Combine<ReprParser>,
@ -198,6 +200,7 @@ attribute_parsers!(
Single<RustcLayoutScalarValidRangeEnd>,
Single<RustcLayoutScalarValidRangeStart>,
Single<RustcObjectLifetimeDefaultParser>,
Single<RustcSimdMonomorphizeLaneLimitParser>,
Single<SanitizeParser>,
Single<ShouldPanicParser>,
Single<SkipDuringMethodDispatchParser>,

View file

@ -85,7 +85,7 @@ impl AllocFnFactory<'_, '_> {
body,
define_opaque: None,
}));
let item = self.cx.item(self.span, self.attrs(), kind);
let item = self.cx.item(self.span, self.attrs(method), kind);
self.cx.stmt_item(self.ty_span, item)
}
@ -100,8 +100,18 @@ impl AllocFnFactory<'_, '_> {
self.cx.expr_call(self.ty_span, method, args)
}
fn attrs(&self) -> AttrVec {
thin_vec![self.cx.attr_word(sym::rustc_std_internal_symbol, self.span)]
fn attrs(&self, method: &AllocatorMethod) -> AttrVec {
let alloc_attr = match method.name {
sym::alloc => sym::rustc_allocator,
sym::dealloc => sym::rustc_deallocator,
sym::realloc => sym::rustc_reallocator,
sym::alloc_zeroed => sym::rustc_allocator_zeroed,
_ => unreachable!("Unknown allocator method!"),
};
thin_vec![
self.cx.attr_word(sym::rustc_std_internal_symbol, self.span),
self.cx.attr_word(alloc_attr, self.span)
]
}
fn arg_ty(&self, input: &AllocatorMethodInput, args: &mut ThinVec<Param>) -> Box<Expr> {

View file

@ -439,7 +439,10 @@ pub(crate) struct FullyMonomorphizedLayoutCx<'tcx>(pub(crate) TyCtxt<'tcx>);
impl<'tcx> LayoutOfHelpers<'tcx> for FullyMonomorphizedLayoutCx<'tcx> {
#[inline]
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err {
if let LayoutError::SizeOverflow(_)
| LayoutError::InvalidSimd { .. }
| LayoutError::ReferencesError(_) = err
{
self.0.sess.dcx().span_fatal(span, err.to_string())
} else {
self.0
@ -458,7 +461,9 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for FullyMonomorphizedLayoutCx<'tcx> {
span: Span,
fn_abi_request: FnAbiRequest<'tcx>,
) -> ! {
if let FnAbiError::Layout(LayoutError::SizeOverflow(_)) = err {
if let FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::InvalidSimd { .. }) =
err
{
self.0.sess.dcx().emit_fatal(Spanned { span, node: err })
} else {
match fn_abi_request {

View file

@ -42,7 +42,10 @@ impl<'tcx> AsmCodegenMethods<'tcx> for GlobalAsmContext<'_, 'tcx> {
impl<'tcx> LayoutOfHelpers<'tcx> for GlobalAsmContext<'_, 'tcx> {
#[inline]
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err {
if let LayoutError::SizeOverflow(_)
| LayoutError::InvalidSimd { .. }
| LayoutError::ReferencesError(_) = err
{
self.tcx.sess.dcx().span_fatal(span, err.to_string())
} else {
self.tcx

View file

@ -1383,6 +1383,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
_src_align: Align,
size: RValue<'gcc>,
flags: MemFlags,
_tt: Option<rustc_ast::expand::typetree::FncTree>, // Autodiff TypeTrees are LLVM-only, ignored in GCC backend
) {
assert!(!flags.contains(MemFlags::NONTEMPORAL), "non-temporal memcpy not supported");
let size = self.intcast(size, self.type_size_t(), false);

View file

@ -529,7 +529,10 @@ impl<'gcc, 'tcx> HasX86AbiOpt for CodegenCx<'gcc, 'tcx> {
impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
#[inline]
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err {
if let LayoutError::SizeOverflow(_)
| LayoutError::InvalidSimd { .. }
| LayoutError::ReferencesError(_) = err
{
self.tcx.dcx().emit_fatal(respan(span, err.into_diagnostic()))
} else {
self.tcx.dcx().emit_fatal(ssa_errors::FailedToGetLayout { span, ty, err })
@ -545,7 +548,9 @@ impl<'gcc, 'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
span: Span,
fn_abi_request: FnAbiRequest<'tcx>,
) -> ! {
if let FnAbiError::Layout(LayoutError::SizeOverflow(_)) = err {
if let FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::InvalidSimd { .. }) =
err
{
self.tcx.dcx().emit_fatal(respan(span, err))
} else {
match fn_abi_request {

View file

@ -770,6 +770,7 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
scratch_align,
bx.const_usize(self.layout.size.bytes()),
MemFlags::empty(),
None,
);
bx.lifetime_end(scratch, scratch_size);

View file

@ -246,6 +246,7 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
scratch_align,
bx.const_usize(copy_bytes),
MemFlags::empty(),
None,
);
bx.lifetime_end(llscratch, scratch_size);
}

View file

@ -563,6 +563,8 @@ fn enable_autodiff_settings(ad: &[config::AutoDiff]) {
config::AutoDiff::Enable => {}
// We handle this below
config::AutoDiff::NoPostopt => {}
// Disables TypeTree generation
config::AutoDiff::NoTT => {}
}
}
// This helps with handling enums for now.

View file

@ -95,8 +95,6 @@ impl Drop for OwnedTargetMachine {
// SAFETY: constructing ensures we have a valid pointer created by
// llvm::LLVMRustCreateTargetMachine OwnedTargetMachine is not copyable so there is no
// double free or use after free.
unsafe {
llvm::LLVMRustDisposeTargetMachine(self.tm_unique.as_ptr());
}
unsafe { llvm::LLVMDisposeTargetMachine(self.tm_unique) };
}
}

View file

@ -2,6 +2,7 @@ use std::borrow::{Borrow, Cow};
use std::ops::Deref;
use std::{iter, ptr};
use rustc_ast::expand::typetree::FncTree;
pub(crate) mod autodiff;
pub(crate) mod gpu_offload;
@ -1107,11 +1108,12 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
src_align: Align,
size: &'ll Value,
flags: MemFlags,
tt: Option<FncTree>,
) {
assert!(!flags.contains(MemFlags::NONTEMPORAL), "non-temporal memcpy not supported");
let size = self.intcast(size, self.type_isize(), false);
let is_volatile = flags.contains(MemFlags::VOLATILE);
unsafe {
let memcpy = unsafe {
llvm::LLVMRustBuildMemCpy(
self.llbuilder,
dst,
@ -1120,7 +1122,16 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
src_align.bytes() as c_uint,
size,
is_volatile,
);
)
};
// TypeTree metadata for memcpy is especially important: when Enzyme encounters
// a memcpy during autodiff, it needs to know the structure of the data being
// copied to properly track derivatives. For example, copying an array of floats
// vs. copying a struct with mixed types requires different derivative handling.
// The TypeTree tells Enzyme exactly what memory layout to expect.
if let Some(tt) = tt {
crate::typetree::add_tt(self.cx().llmod, self.cx().llcx, memcpy, tt);
}
}

View file

@ -1,6 +1,7 @@
use std::ptr;
use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
use rustc_ast::expand::typetree::FncTree;
use rustc_codegen_ssa::common::TypeKind;
use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods};
use rustc_middle::ty::{Instance, PseudoCanonicalInput, TyCtxt, TypingEnv};
@ -294,6 +295,7 @@ pub(crate) fn generate_enzyme_call<'ll, 'tcx>(
fn_args: &[&'ll Value],
attrs: AutoDiffAttrs,
dest: PlaceRef<'tcx, &'ll Value>,
fnc_tree: FncTree,
) {
// We have to pick the name depending on whether we want forward or reverse mode autodiff.
let mut ad_name: String = match attrs.mode {
@ -370,6 +372,10 @@ pub(crate) fn generate_enzyme_call<'ll, 'tcx>(
fn_args,
);
if !fnc_tree.args.is_empty() || !fnc_tree.ret.0.is_empty() {
crate::typetree::add_tt(cx.llmod, cx.llcx, fn_to_diff, fnc_tree);
}
let call = builder.call(enzyme_ty, None, None, ad_fn, &args, None, None);
builder.store_to_place(call, dest.val);

View file

@ -1044,7 +1044,10 @@ impl<'tcx, 'll> HasTypingEnv<'tcx> for CodegenCx<'ll, 'tcx> {
impl<'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'_, 'tcx> {
#[inline]
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err {
if let LayoutError::SizeOverflow(_)
| LayoutError::ReferencesError(_)
| LayoutError::InvalidSimd { .. } = err
{
self.tcx.dcx().emit_fatal(Spanned { span, node: err.into_diagnostic() })
} else {
self.tcx.dcx().emit_fatal(ssa_errors::FailedToGetLayout { span, ty, err })
@ -1061,7 +1064,11 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'_, 'tcx> {
fn_abi_request: FnAbiRequest<'tcx>,
) -> ! {
match err {
FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::Cycle(_)) => {
FnAbiError::Layout(
LayoutError::SizeOverflow(_)
| LayoutError::Cycle(_)
| LayoutError::InvalidSimd { .. },
) => {
self.tcx.dcx().emit_fatal(Spanned { span, node: err });
}
_ => match fn_abi_request {

View file

@ -2,9 +2,9 @@
use rustc_codegen_ssa::base::collect_debugger_visualizers_transitive;
use rustc_codegen_ssa::traits::*;
use rustc_hir::attrs::DebuggerVisualizerType;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::bug;
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerType;
use rustc_session::config::{CrateType, DebugInfo};
use crate::builder::Builder;

View file

@ -117,7 +117,7 @@ fn build_fixed_size_array_di_node<'ll, 'tcx>(
.try_to_target_usize(cx.tcx)
.expect("expected monomorphic const in codegen") as c_longlong;
let subrange = unsafe { llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound) };
let subrange = unsafe { llvm::LLVMDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound) };
let subscripts = &[subrange];
let di_node = unsafe {
@ -477,8 +477,8 @@ pub(crate) fn spanned_type_di_node<'ll, 'tcx>(
ty::CoroutineClosure(..) => build_closure_env_di_node(cx, unique_type_id),
ty::Coroutine(..) => enums::build_coroutine_di_node(cx, unique_type_id),
ty::Adt(def, ..) => match def.adt_kind() {
AdtKind::Struct => build_struct_type_di_node(cx, unique_type_id),
AdtKind::Union => build_union_type_di_node(cx, unique_type_id),
AdtKind::Struct => build_struct_type_di_node(cx, unique_type_id, span),
AdtKind::Union => build_union_type_di_node(cx, unique_type_id, span),
AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id, span),
},
ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id),
@ -1076,6 +1076,7 @@ fn visibility_di_flags<'ll, 'tcx>(
fn build_struct_type_di_node<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
unique_type_id: UniqueTypeId<'tcx>,
span: Span,
) -> DINodeCreationResult<'ll> {
let struct_type = unique_type_id.expect_ty();
let ty::Adt(adt_def, _) = struct_type.kind() else {
@ -1083,7 +1084,7 @@ fn build_struct_type_di_node<'ll, 'tcx>(
};
assert!(adt_def.is_struct());
let containing_scope = get_namespace_for_item(cx, adt_def.did());
let struct_type_and_layout = cx.layout_of(struct_type);
let struct_type_and_layout = cx.spanned_layout_of(struct_type, span);
let variant_def = adt_def.non_enum_variant();
let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(file_metadata_from_def_id(cx, Some(adt_def.did())))
@ -1276,6 +1277,7 @@ fn build_closure_env_di_node<'ll, 'tcx>(
fn build_union_type_di_node<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
unique_type_id: UniqueTypeId<'tcx>,
span: Span,
) -> DINodeCreationResult<'ll> {
let union_type = unique_type_id.expect_ty();
let (union_def_id, variant_def) = match union_type.kind() {
@ -1283,7 +1285,7 @@ fn build_union_type_di_node<'ll, 'tcx>(
_ => bug!("build_union_type_di_node on a non-ADT"),
};
let containing_scope = get_namespace_for_item(cx, union_def_id);
let union_ty_and_layout = cx.layout_of(union_type);
let union_ty_and_layout = cx.spanned_layout_of(union_type, span);
let type_name = compute_debuginfo_type_name(cx.tcx, union_type, false);
let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(file_metadata_from_def_id(cx, Some(union_def_id)))

View file

@ -52,15 +52,6 @@ mod utils;
use self::create_scope_map::compute_mir_scopes;
pub(crate) use self::metadata::build_global_var_di_node;
// FIXME(Zalathar): These `DW_TAG_*` constants are fake values that were
// removed from LLVM in 2015, and are only used by our own `RustWrapper.cpp`
// to decide which C++ API to call. Instead, we should just have two separate
// FFI functions and choose the correct one on the Rust side.
#[allow(non_upper_case_globals)]
const DW_TAG_auto_variable: c_uint = 0x100;
#[allow(non_upper_case_globals)]
const DW_TAG_arg_variable: c_uint = 0x101;
/// A context object for maintaining all state needed by the debuginfo module.
pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> {
llmod: &'ll llvm::Module,
@ -174,35 +165,38 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
if direct_offset.bytes() > 0 {
addr_ops.push(DW_OP_plus_uconst);
addr_ops.push(direct_offset.bytes() as u64);
addr_ops.push(direct_offset.bytes());
}
for &offset in indirect_offsets {
addr_ops.push(DW_OP_deref);
if offset.bytes() > 0 {
addr_ops.push(DW_OP_plus_uconst);
addr_ops.push(offset.bytes() as u64);
addr_ops.push(offset.bytes());
}
}
if let Some(fragment) = fragment {
// `DW_OP_LLVM_fragment` takes as arguments the fragment's
// offset and size, both of them in bits.
addr_ops.push(DW_OP_LLVM_fragment);
addr_ops.push(fragment.start.bits() as u64);
addr_ops.push((fragment.end - fragment.start).bits() as u64);
addr_ops.push(fragment.start.bits());
addr_ops.push((fragment.end - fragment.start).bits());
}
let di_builder = DIB(self.cx());
let addr_expr = unsafe {
llvm::LLVMDIBuilderCreateExpression(di_builder, addr_ops.as_ptr(), addr_ops.len())
};
unsafe {
// FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`.
llvm::LLVMRustDIBuilderInsertDeclareAtEnd(
DIB(self.cx()),
llvm::LLVMDIBuilderInsertDeclareRecordAtEnd(
di_builder,
variable_alloca,
dbg_var,
addr_ops.as_ptr(),
addr_ops.len() as c_uint,
addr_expr,
dbg_loc,
self.llbb(),
);
}
)
};
}
fn set_dbg_loc(&mut self, dbg_loc: &'ll DILocation) {
@ -630,28 +624,39 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
let type_metadata = spanned_type_di_node(self, variable_type, span);
let (argument_index, dwarf_tag) = match variable_kind {
ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable),
LocalVariable => (0, DW_TAG_auto_variable),
};
let align = self.align_of(variable_type);
let name = variable_name.as_str();
unsafe {
llvm::LLVMRustDIBuilderCreateVariable(
DIB(self),
dwarf_tag,
scope_metadata,
name.as_c_char_ptr(),
name.len(),
file_metadata,
loc.line,
type_metadata,
true,
DIFlags::FlagZero,
argument_index,
align.bits() as u32,
)
match variable_kind {
ArgumentVariable(arg_index) => unsafe {
llvm::LLVMDIBuilderCreateParameterVariable(
DIB(self),
scope_metadata,
name.as_ptr(),
name.len(),
arg_index as c_uint,
file_metadata,
loc.line,
type_metadata,
llvm::Bool::TRUE, // (preserve descriptor during optimizations)
DIFlags::FlagZero,
)
},
LocalVariable => unsafe {
llvm::LLVMDIBuilderCreateAutoVariable(
DIB(self),
scope_metadata,
name.as_ptr(),
name.len(),
file_metadata,
loc.line,
type_metadata,
llvm::Bool::TRUE, // (preserve descriptor during optimizations)
DIFlags::FlagZero,
align.bits() as u32,
)
},
}
}
}

View file

@ -28,7 +28,7 @@ pub(crate) fn create_DIArray<'ll>(
builder: &DIBuilder<'ll>,
arr: &[Option<&'ll DIDescriptor>],
) -> &'ll DIArray {
unsafe { llvm::LLVMRustDIBuilderGetOrCreateArray(builder, arr.as_ptr(), arr.len() as u32) }
unsafe { llvm::LLVMDIBuilderGetOrCreateArray(builder, arr.as_ptr(), arr.len()) }
}
#[inline]

View file

@ -1212,6 +1212,9 @@ fn codegen_autodiff<'ll, 'tcx>(
&mut diff_attrs.input_activity,
);
let fnc_tree =
rustc_middle::ty::fnc_typetrees(tcx, fn_source.ty(tcx, TypingEnv::fully_monomorphized()));
// Build body
generate_enzyme_call(
bx,
@ -1222,6 +1225,7 @@ fn codegen_autodiff<'ll, 'tcx>(
&val_arr,
diff_attrs.clone(),
result,
fnc_tree,
);
}

View file

@ -68,6 +68,7 @@ mod llvm_util;
mod mono_item;
mod type_;
mod type_of;
mod typetree;
mod va_arg;
mod value;

View file

@ -3,9 +3,36 @@
use libc::{c_char, c_uint};
use super::MetadataKindId;
use super::ffi::{AttributeKind, BasicBlock, Metadata, Module, Type, Value};
use super::ffi::{AttributeKind, BasicBlock, Context, Metadata, Module, Type, Value};
use crate::llvm::{Bool, Builder};
// TypeTree types
pub(crate) type CTypeTreeRef = *mut EnzymeTypeTree;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub(crate) struct EnzymeTypeTree {
_unused: [u8; 0],
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
#[allow(non_camel_case_types)]
pub(crate) enum CConcreteType {
DT_Anything = 0,
DT_Integer = 1,
DT_Pointer = 2,
DT_Half = 3,
DT_Float = 4,
DT_Double = 5,
DT_Unknown = 6,
DT_FP128 = 9,
}
pub(crate) struct TypeTree {
pub(crate) inner: CTypeTreeRef,
}
#[link(name = "llvm-wrapper", kind = "static")]
unsafe extern "C" {
// Enzyme
@ -68,10 +95,40 @@ pub(crate) mod Enzyme_AD {
use libc::c_void;
use super::{CConcreteType, CTypeTreeRef, Context};
unsafe extern "C" {
pub(crate) fn EnzymeSetCLBool(arg1: *mut ::std::os::raw::c_void, arg2: u8);
pub(crate) fn EnzymeSetCLString(arg1: *mut ::std::os::raw::c_void, arg2: *const c_char);
}
// TypeTree functions
unsafe extern "C" {
pub(crate) fn EnzymeNewTypeTree() -> CTypeTreeRef;
pub(crate) fn EnzymeNewTypeTreeCT(arg1: CConcreteType, ctx: &Context) -> CTypeTreeRef;
pub(crate) fn EnzymeNewTypeTreeTR(arg1: CTypeTreeRef) -> CTypeTreeRef;
pub(crate) fn EnzymeFreeTypeTree(CTT: CTypeTreeRef);
pub(crate) fn EnzymeMergeTypeTree(arg1: CTypeTreeRef, arg2: CTypeTreeRef) -> bool;
pub(crate) fn EnzymeTypeTreeOnlyEq(arg1: CTypeTreeRef, pos: i64);
pub(crate) fn EnzymeTypeTreeData0Eq(arg1: CTypeTreeRef);
pub(crate) fn EnzymeTypeTreeShiftIndiciesEq(
arg1: CTypeTreeRef,
data_layout: *const c_char,
offset: i64,
max_size: i64,
add_offset: u64,
);
pub(crate) fn EnzymeTypeTreeInsertEq(
CTT: CTypeTreeRef,
indices: *const i64,
len: usize,
ct: CConcreteType,
ctx: &Context,
);
pub(crate) fn EnzymeTypeTreeToString(arg1: CTypeTreeRef) -> *const c_char;
pub(crate) fn EnzymeTypeTreeToStringFree(arg1: *const c_char);
}
unsafe extern "C" {
static mut EnzymePrintPerf: c_void;
static mut EnzymePrintActivity: c_void;
@ -141,6 +198,67 @@ pub(crate) use self::Fallback_AD::*;
pub(crate) mod Fallback_AD {
#![allow(unused_variables)]
use libc::c_char;
use super::{CConcreteType, CTypeTreeRef, Context};
// TypeTree function fallbacks
pub(crate) unsafe fn EnzymeNewTypeTree() -> CTypeTreeRef {
unimplemented!()
}
pub(crate) unsafe fn EnzymeNewTypeTreeCT(arg1: CConcreteType, ctx: &Context) -> CTypeTreeRef {
unimplemented!()
}
pub(crate) unsafe fn EnzymeNewTypeTreeTR(arg1: CTypeTreeRef) -> CTypeTreeRef {
unimplemented!()
}
pub(crate) unsafe fn EnzymeFreeTypeTree(CTT: CTypeTreeRef) {
unimplemented!()
}
pub(crate) unsafe fn EnzymeMergeTypeTree(arg1: CTypeTreeRef, arg2: CTypeTreeRef) -> bool {
unimplemented!()
}
pub(crate) unsafe fn EnzymeTypeTreeOnlyEq(arg1: CTypeTreeRef, pos: i64) {
unimplemented!()
}
pub(crate) unsafe fn EnzymeTypeTreeData0Eq(arg1: CTypeTreeRef) {
unimplemented!()
}
pub(crate) unsafe fn EnzymeTypeTreeShiftIndiciesEq(
arg1: CTypeTreeRef,
data_layout: *const c_char,
offset: i64,
max_size: i64,
add_offset: u64,
) {
unimplemented!()
}
pub(crate) unsafe fn EnzymeTypeTreeInsertEq(
CTT: CTypeTreeRef,
indices: *const i64,
len: usize,
ct: CConcreteType,
ctx: &Context,
) {
unimplemented!()
}
pub(crate) unsafe fn EnzymeTypeTreeToString(arg1: CTypeTreeRef) -> *const c_char {
unimplemented!()
}
pub(crate) unsafe fn EnzymeTypeTreeToStringFree(arg1: *const c_char) {
unimplemented!()
}
pub(crate) fn set_inline(val: bool) {
unimplemented!()
}
@ -169,3 +287,89 @@ pub(crate) mod Fallback_AD {
unimplemented!()
}
}
impl TypeTree {
pub(crate) fn new() -> TypeTree {
let inner = unsafe { EnzymeNewTypeTree() };
TypeTree { inner }
}
pub(crate) fn from_type(t: CConcreteType, ctx: &Context) -> TypeTree {
let inner = unsafe { EnzymeNewTypeTreeCT(t, ctx) };
TypeTree { inner }
}
pub(crate) fn merge(self, other: Self) -> Self {
unsafe {
EnzymeMergeTypeTree(self.inner, other.inner);
}
drop(other);
self
}
#[must_use]
pub(crate) fn shift(
self,
layout: &str,
offset: isize,
max_size: isize,
add_offset: usize,
) -> Self {
let layout = std::ffi::CString::new(layout).unwrap();
unsafe {
EnzymeTypeTreeShiftIndiciesEq(
self.inner,
layout.as_ptr(),
offset as i64,
max_size as i64,
add_offset as u64,
);
}
self
}
pub(crate) fn insert(&mut self, indices: &[i64], ct: CConcreteType, ctx: &Context) {
unsafe {
EnzymeTypeTreeInsertEq(self.inner, indices.as_ptr(), indices.len(), ct, ctx);
}
}
}
impl Clone for TypeTree {
fn clone(&self) -> Self {
let inner = unsafe { EnzymeNewTypeTreeTR(self.inner) };
TypeTree { inner }
}
}
impl std::fmt::Display for TypeTree {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let ptr = unsafe { EnzymeTypeTreeToString(self.inner) };
let cstr = unsafe { std::ffi::CStr::from_ptr(ptr) };
match cstr.to_str() {
Ok(x) => write!(f, "{}", x)?,
Err(err) => write!(f, "could not parse: {}", err)?,
}
// delete C string pointer
unsafe {
EnzymeTypeTreeToStringFree(ptr);
}
Ok(())
}
}
impl std::fmt::Debug for TypeTree {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
<Self as std::fmt::Display>::fmt(self, f)
}
}
impl Drop for TypeTree {
fn drop(&mut self) {
unsafe { EnzymeFreeTypeTree(self.inner) }
}
}

View file

@ -25,8 +25,8 @@ use rustc_target::spec::SymbolVisibility;
use super::RustString;
use super::debuginfo::{
DIArray, DIBuilder, DIDerivedType, DIDescriptor, DIEnumerator, DIFile, DIFlags,
DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, DISubrange,
DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, DebugNameTableKind,
DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram,
DITemplateTypeParameter, DIType, DebugEmissionKind, DebugNameTableKind,
};
use crate::llvm;
@ -807,6 +807,8 @@ unsafe extern "C" {
pub(crate) type Metadata;
pub(crate) type BasicBlock;
pub(crate) type Comdat;
/// `&'ll DbgRecord` represents `LLVMDbgRecordRef`.
pub(crate) type DbgRecord;
}
#[repr(C)]
pub(crate) struct Builder<'a>(InvariantOpaque<'a>);
@ -891,7 +893,6 @@ pub(crate) mod debuginfo {
pub(crate) type DIVariable = DIDescriptor;
pub(crate) type DIGlobalVariableExpression = DIDescriptor;
pub(crate) type DIArray = DIDescriptor;
pub(crate) type DISubrange = DIDescriptor;
pub(crate) type DIEnumerator = DIDescriptor;
pub(crate) type DITemplateTypeParameter = DIDescriptor;
@ -1053,6 +1054,8 @@ unsafe extern "C" {
SLen: c_uint,
) -> MetadataKindId;
pub(crate) fn LLVMDisposeTargetMachine(T: ptr::NonNull<TargetMachine>);
// Create modules.
pub(crate) fn LLVMModuleCreateWithNameInContext(
ModuleID: *const c_char,
@ -1990,6 +1993,59 @@ unsafe extern "C" {
Scope: Option<&'ll Metadata>,
AlignInBits: u32, // (optional; default is 0)
) -> &'ll Metadata;
pub(crate) fn LLVMDIBuilderGetOrCreateSubrange<'ll>(
Builder: &DIBuilder<'ll>,
LowerBound: i64,
Count: i64,
) -> &'ll Metadata;
pub(crate) fn LLVMDIBuilderGetOrCreateArray<'ll>(
Builder: &DIBuilder<'ll>,
Data: *const Option<&'ll Metadata>,
NumElements: size_t,
) -> &'ll Metadata;
pub(crate) fn LLVMDIBuilderCreateExpression<'ll>(
Builder: &DIBuilder<'ll>,
Addr: *const u64,
Length: size_t,
) -> &'ll Metadata;
pub(crate) fn LLVMDIBuilderInsertDeclareRecordAtEnd<'ll>(
Builder: &DIBuilder<'ll>,
Storage: &'ll Value,
VarInfo: &'ll Metadata,
Expr: &'ll Metadata,
DebugLoc: &'ll Metadata,
Block: &'ll BasicBlock,
) -> &'ll DbgRecord;
pub(crate) fn LLVMDIBuilderCreateAutoVariable<'ll>(
Builder: &DIBuilder<'ll>,
Scope: &'ll Metadata,
Name: *const c_uchar, // See "PTR_LEN_STR".
NameLen: size_t,
File: &'ll Metadata,
LineNo: c_uint,
Ty: &'ll Metadata,
AlwaysPreserve: llvm::Bool, // "If true, this descriptor will survive optimizations."
Flags: DIFlags,
AlignInBits: u32,
) -> &'ll Metadata;
pub(crate) fn LLVMDIBuilderCreateParameterVariable<'ll>(
Builder: &DIBuilder<'ll>,
Scope: &'ll Metadata,
Name: *const c_uchar, // See "PTR_LEN_STR".
NameLen: size_t,
ArgNo: c_uint,
File: &'ll Metadata,
LineNo: c_uint,
Ty: &'ll Metadata,
AlwaysPreserve: llvm::Bool, // "If true, this descriptor will survive optimizations."
Flags: DIFlags,
) -> &'ll Metadata;
}
#[link(name = "llvm-wrapper", kind = "static")]
@ -2356,43 +2412,6 @@ unsafe extern "C" {
AlignInBits: u32,
) -> &'a DIGlobalVariableExpression;
pub(crate) fn LLVMRustDIBuilderCreateVariable<'a>(
Builder: &DIBuilder<'a>,
Tag: c_uint,
Scope: &'a DIDescriptor,
Name: *const c_char,
NameLen: size_t,
File: &'a DIFile,
LineNo: c_uint,
Ty: &'a DIType,
AlwaysPreserve: bool,
Flags: DIFlags,
ArgNo: c_uint,
AlignInBits: u32,
) -> &'a DIVariable;
pub(crate) fn LLVMRustDIBuilderGetOrCreateSubrange<'a>(
Builder: &DIBuilder<'a>,
Lo: i64,
Count: i64,
) -> &'a DISubrange;
pub(crate) fn LLVMRustDIBuilderGetOrCreateArray<'a>(
Builder: &DIBuilder<'a>,
Ptr: *const Option<&'a DIDescriptor>,
Count: c_uint,
) -> &'a DIArray;
pub(crate) fn LLVMRustDIBuilderInsertDeclareAtEnd<'a>(
Builder: &DIBuilder<'a>,
Val: &'a Value,
VarInfo: &'a DIVariable,
AddrOps: *const u64,
AddrOpsCount: c_uint,
DL: &'a DILocation,
InsertAtEnd: &'a BasicBlock,
);
pub(crate) fn LLVMRustDIBuilderCreateEnumerator<'a>(
Builder: &DIBuilder<'a>,
Name: *const c_char,
@ -2499,7 +2518,6 @@ unsafe extern "C" {
UseWasmEH: bool,
) -> *mut TargetMachine;
pub(crate) fn LLVMRustDisposeTargetMachine(T: *mut TargetMachine);
pub(crate) fn LLVMRustAddLibraryInfo<'a>(
PM: &PassManager<'a>,
M: &'a Module,

View file

@ -0,0 +1,122 @@
use rustc_ast::expand::typetree::FncTree;
#[cfg(feature = "llvm_enzyme")]
use {
crate::attributes,
rustc_ast::expand::typetree::TypeTree as RustTypeTree,
std::ffi::{CString, c_char, c_uint},
};
use crate::llvm::{self, Value};
#[cfg(feature = "llvm_enzyme")]
fn to_enzyme_typetree(
rust_typetree: RustTypeTree,
_data_layout: &str,
llcx: &llvm::Context,
) -> llvm::TypeTree {
let mut enzyme_tt = llvm::TypeTree::new();
process_typetree_recursive(&mut enzyme_tt, &rust_typetree, &[], llcx);
enzyme_tt
}
#[cfg(feature = "llvm_enzyme")]
fn process_typetree_recursive(
enzyme_tt: &mut llvm::TypeTree,
rust_typetree: &RustTypeTree,
parent_indices: &[i64],
llcx: &llvm::Context,
) {
for rust_type in &rust_typetree.0 {
let concrete_type = match rust_type.kind {
rustc_ast::expand::typetree::Kind::Anything => llvm::CConcreteType::DT_Anything,
rustc_ast::expand::typetree::Kind::Integer => llvm::CConcreteType::DT_Integer,
rustc_ast::expand::typetree::Kind::Pointer => llvm::CConcreteType::DT_Pointer,
rustc_ast::expand::typetree::Kind::Half => llvm::CConcreteType::DT_Half,
rustc_ast::expand::typetree::Kind::Float => llvm::CConcreteType::DT_Float,
rustc_ast::expand::typetree::Kind::Double => llvm::CConcreteType::DT_Double,
rustc_ast::expand::typetree::Kind::F128 => llvm::CConcreteType::DT_FP128,
rustc_ast::expand::typetree::Kind::Unknown => llvm::CConcreteType::DT_Unknown,
};
let mut indices = parent_indices.to_vec();
if !parent_indices.is_empty() {
indices.push(rust_type.offset as i64);
} else if rust_type.offset == -1 {
indices.push(-1);
} else {
indices.push(rust_type.offset as i64);
}
enzyme_tt.insert(&indices, concrete_type, llcx);
if rust_type.kind == rustc_ast::expand::typetree::Kind::Pointer
&& !rust_type.child.0.is_empty()
{
process_typetree_recursive(enzyme_tt, &rust_type.child, &indices, llcx);
}
}
}
#[cfg(feature = "llvm_enzyme")]
pub(crate) fn add_tt<'ll>(
llmod: &'ll llvm::Module,
llcx: &'ll llvm::Context,
fn_def: &'ll Value,
tt: FncTree,
) {
let inputs = tt.args;
let ret_tt: RustTypeTree = tt.ret;
let llvm_data_layout: *const c_char = unsafe { llvm::LLVMGetDataLayoutStr(&*llmod) };
let llvm_data_layout =
std::str::from_utf8(unsafe { std::ffi::CStr::from_ptr(llvm_data_layout) }.to_bytes())
.expect("got a non-UTF8 data-layout from LLVM");
let attr_name = "enzyme_type";
let c_attr_name = CString::new(attr_name).unwrap();
for (i, input) in inputs.iter().enumerate() {
unsafe {
let enzyme_tt = to_enzyme_typetree(input.clone(), llvm_data_layout, llcx);
let c_str = llvm::EnzymeTypeTreeToString(enzyme_tt.inner);
let c_str = std::ffi::CStr::from_ptr(c_str);
let attr = llvm::LLVMCreateStringAttribute(
llcx,
c_attr_name.as_ptr(),
c_attr_name.as_bytes().len() as c_uint,
c_str.as_ptr(),
c_str.to_bytes().len() as c_uint,
);
attributes::apply_to_llfn(fn_def, llvm::AttributePlace::Argument(i as u32), &[attr]);
llvm::EnzymeTypeTreeToStringFree(c_str.as_ptr());
}
}
unsafe {
let enzyme_tt = to_enzyme_typetree(ret_tt, llvm_data_layout, llcx);
let c_str = llvm::EnzymeTypeTreeToString(enzyme_tt.inner);
let c_str = std::ffi::CStr::from_ptr(c_str);
let ret_attr = llvm::LLVMCreateStringAttribute(
llcx,
c_attr_name.as_ptr(),
c_attr_name.as_bytes().len() as c_uint,
c_str.as_ptr(),
c_str.to_bytes().len() as c_uint,
);
attributes::apply_to_llfn(fn_def, llvm::AttributePlace::ReturnValue, &[ret_attr]);
llvm::EnzymeTypeTreeToStringFree(c_str.as_ptr());
}
}
#[cfg(not(feature = "llvm_enzyme"))]
pub(crate) fn add_tt<'ll>(
_llmod: &'ll llvm::Module,
_llcx: &'ll llvm::Context,
_fn_def: &'ll Value,
_tt: FncTree,
) {
unimplemented!()
}

View file

@ -738,6 +738,7 @@ fn copy_to_temporary_if_more_aligned<'ll, 'tcx>(
src_align,
bx.const_u32(layout.layout.size().bytes() as u32),
MemFlags::empty(),
None,
);
tmp
} else {

View file

@ -11,12 +11,12 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
use rustc_data_structures::sync::{IntoDynSyncSend, par_map};
use rustc_data_structures::unord::UnordMap;
use rustc_hir::attrs::OptimizeAttr;
use rustc_hir::attrs::{DebuggerVisualizerType, OptimizeAttr};
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_hir::lang_items::LangItem;
use rustc_hir::{ItemId, Target};
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType};
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
use rustc_middle::middle::dependency_format::Dependencies;
use rustc_middle::middle::exported_symbols::{self, SymbolExportKind};
use rustc_middle::middle::lang_items;

View file

@ -1626,6 +1626,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
align,
bx.const_usize(copy_bytes),
MemFlags::empty(),
None,
);
// ...and then load it with the ABI type.
llval = load_cast(bx, cast, llscratch, scratch_align);

View file

@ -30,7 +30,7 @@ fn copy_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
if allow_overlap {
bx.memmove(dst, align, src, align, size, flags);
} else {
bx.memcpy(dst, align, src, align, size, flags);
bx.memcpy(dst, align, src, align, size, flags, None);
}
}

View file

@ -90,7 +90,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let align = pointee_layout.align;
let dst = dst_val.immediate();
let src = src_val.immediate();
bx.memcpy(dst, align, src, align, bytes, crate::MemFlags::empty());
bx.memcpy(dst, align, src, align, bytes, crate::MemFlags::empty(), None);
}
mir::StatementKind::FakeRead(..)
| mir::StatementKind::Retag { .. }

View file

@ -451,6 +451,7 @@ pub trait BuilderMethods<'a, 'tcx>:
src_align: Align,
size: Self::Value,
flags: MemFlags,
tt: Option<rustc_ast::expand::typetree::FncTree>,
);
fn memmove(
&mut self,
@ -507,7 +508,7 @@ pub trait BuilderMethods<'a, 'tcx>:
temp.val.store_with_flags(self, dst.with_type(layout), flags);
} else if !layout.is_zst() {
let bytes = self.const_usize(layout.size.bytes());
self.memcpy(dst.llval, dst.align, src.llval, src.align, bytes, flags);
self.memcpy(dst.llval, dst.align, src.llval, src.align, bytes, flags, None);
}
}

View file

@ -636,6 +636,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
dest,
rustc_apfloat::Round::NearestTiesToEven,
)?,
sym::fmaf16 => self.fma_intrinsic::<Half>(args, dest)?,
sym::fmaf32 => self.fma_intrinsic::<Single>(args, dest)?,
sym::fmaf64 => self.fma_intrinsic::<Double>(args, dest)?,
sym::fmaf128 => self.fma_intrinsic::<Quad>(args, dest)?,
sym::fmuladdf16 => self.float_muladd_intrinsic::<Half>(args, dest)?,
sym::fmuladdf32 => self.float_muladd_intrinsic::<Single>(args, dest)?,
sym::fmuladdf64 => self.float_muladd_intrinsic::<Double>(args, dest)?,
sym::fmuladdf128 => self.float_muladd_intrinsic::<Quad>(args, dest)?,
// Unsupported intrinsic: skip the return_to_block below.
_ => return interp_ok(false),
@ -1035,4 +1043,42 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
self.write_scalar(res, dest)?;
interp_ok(())
}
fn fma_intrinsic<F>(
&mut self,
args: &[OpTy<'tcx, M::Provenance>],
dest: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, ()>
where
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
{
let a: F = self.read_scalar(&args[0])?.to_float()?;
let b: F = self.read_scalar(&args[1])?.to_float()?;
let c: F = self.read_scalar(&args[2])?.to_float()?;
let res = a.mul_add(b, c).value;
let res = self.adjust_nan(res, &[a, b, c]);
self.write_scalar(res, dest)?;
interp_ok(())
}
fn float_muladd_intrinsic<F>(
&mut self,
args: &[OpTy<'tcx, M::Provenance>],
dest: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, ()>
where
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
{
let a: F = self.read_scalar(&args[0])?.to_float()?;
let b: F = self.read_scalar(&args[1])?.to_float()?;
let c: F = self.read_scalar(&args[2])?.to_float()?;
let fuse = M::float_fuse_mul_add(self);
let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
let res = self.adjust_nan(res, &[a, b, c]);
self.write_scalar(res, dest)?;
interp_ok(())
}
}

View file

@ -289,6 +289,9 @@ pub trait Machine<'tcx>: Sized {
a
}
/// Determines whether the `fmuladd` intrinsics fuse the multiply-add or use separate operations.
fn float_fuse_mul_add(_ecx: &mut InterpCx<'tcx, Self>) -> bool;
/// Called before a basic block terminator is executed.
#[inline]
fn before_terminator(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
@ -672,6 +675,11 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
match fn_val {}
}
#[inline(always)]
fn float_fuse_mul_add(_ecx: &mut InterpCx<$tcx, Self>) -> bool {
true
}
#[inline(always)]
fn ub_checks(_ecx: &InterpCx<$tcx, Self>) -> InterpResult<$tcx, bool> {
// We can't look at `tcx.sess` here as that can differ across crates, which can lead to

View file

@ -1,7 +1,6 @@
// tidy-alphabetical-start
#![allow(internal_features)]
#![allow(rustc::diagnostic_outside_of_impl)]
#![cfg_attr(bootstrap, feature(strict_overflow_ops))]
#![doc(rust_logo)]
#![feature(array_try_map)]
#![feature(assert_matches)]

View file

@ -61,7 +61,7 @@ pub(crate) fn const_caller_location_provider(
trace!("const_caller_location: {}:{}:{}", file, line, col);
let mut ecx = mk_eval_cx_to_read_const_val(
tcx,
rustc_span::DUMMY_SP, // FIXME: use a proper span here?
rustc_span::DUMMY_SP, // This interpreter cannot fail, so the span is irrelevant.
ty::TypingEnv::fully_monomorphized(),
CanAccessMutGlobal::No,
);

View file

@ -324,16 +324,16 @@ pub trait BangProcMacro {
impl<F> BangProcMacro for F
where
F: Fn(TokenStream) -> TokenStream,
F: Fn(&mut ExtCtxt<'_>, Span, TokenStream) -> Result<TokenStream, ErrorGuaranteed>,
{
fn expand<'cx>(
&self,
_ecx: &'cx mut ExtCtxt<'_>,
_span: Span,
ecx: &'cx mut ExtCtxt<'_>,
span: Span,
ts: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> {
// FIXME setup implicit context in TLS before calling self.
Ok(self(ts))
self(ecx, span, ts)
}
}
@ -999,17 +999,14 @@ impl SyntaxExtension {
/// A dummy bang macro `foo!()`.
pub fn dummy_bang(edition: Edition) -> SyntaxExtension {
fn expander<'cx>(
cx: &'cx mut ExtCtxt<'_>,
fn expand(
ecx: &mut ExtCtxt<'_>,
span: Span,
_: TokenStream,
) -> MacroExpanderResult<'cx> {
ExpandResult::Ready(DummyResult::any(
span,
cx.dcx().span_delayed_bug(span, "expanded a dummy bang macro"),
))
_ts: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> {
Err(ecx.dcx().span_delayed_bug(span, "expanded a dummy bang macro"))
}
SyntaxExtension::default(SyntaxExtensionKind::LegacyBang(Arc::new(expander)), edition)
SyntaxExtension::default(SyntaxExtensionKind::Bang(Arc::new(expand)), edition)
}
/// A dummy derive macro `#[derive(Foo)]`.

View file

@ -971,7 +971,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
});
}
},
SyntaxExtensionKind::LegacyBang(..) => {
SyntaxExtensionKind::Bang(..) => {
let msg = "expanded a dummy glob delegation";
let guar = self.cx.dcx().span_delayed_bug(span, msg);
return ExpandResult::Ready(fragment_kind.dummy(span, guar));

View file

@ -33,8 +33,8 @@ use super::diagnostics::{FailedMacro, failed_to_match_macro};
use super::macro_parser::{NamedMatches, NamedParseResult};
use super::{SequenceRepetition, diagnostics};
use crate::base::{
AttrProcMacro, DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult,
SyntaxExtension, SyntaxExtensionKind, TTMacroExpander,
AttrProcMacro, BangProcMacro, DummyResult, ExpandResult, ExtCtxt, MacResult,
MacroExpanderResult, SyntaxExtension, SyntaxExtensionKind, TTMacroExpander,
};
use crate::errors;
use crate::expand::{AstFragment, AstFragmentKind, ensure_complete_parse, parse_ast_fragment};
@ -267,16 +267,16 @@ impl AttrProcMacro for MacroRulesMacroExpander {
}
}
struct DummyExpander(ErrorGuaranteed);
struct DummyBang(ErrorGuaranteed);
impl TTMacroExpander for DummyExpander {
impl BangProcMacro for DummyBang {
fn expand<'cx>(
&self,
_: &'cx mut ExtCtxt<'_>,
span: Span,
_: Span,
_: TokenStream,
) -> ExpandResult<Box<dyn MacResult + 'cx>, ()> {
ExpandResult::Ready(DummyResult::any(span, self.0))
) -> Result<TokenStream, ErrorGuaranteed> {
Err(self.0)
}
}
@ -664,7 +664,7 @@ pub fn compile_declarative_macro(
SyntaxExtension::new(sess, kind, span, Vec::new(), edition, ident.name, attrs, is_local)
};
let dummy_syn_ext =
|guar| (mk_syn_ext(SyntaxExtensionKind::LegacyBang(Arc::new(DummyExpander(guar)))), 0);
|guar| (mk_syn_ext(SyntaxExtensionKind::Bang(Arc::new(DummyBang(guar)))), 0);
let macro_rules = macro_def.macro_rules;
let exp_sep = if macro_rules { exp!(Semi) } else { exp!(Comma) };
@ -894,12 +894,12 @@ fn check_redundant_vis_repetition(
seq: &SequenceRepetition,
span: &DelimSpan,
) {
let is_zero_or_one: bool = seq.kleene.op == KleeneOp::ZeroOrOne;
let is_vis = seq.tts.first().map_or(false, |tt| {
matches!(tt, mbe::TokenTree::MetaVarDecl { kind: NonterminalKind::Vis, .. })
});
if is_vis && is_zero_or_one {
if seq.kleene.op == KleeneOp::ZeroOrOne
&& matches!(
seq.tts.first(),
Some(mbe::TokenTree::MetaVarDecl { kind: NonterminalKind::Vis, .. })
)
{
err.note("a `vis` fragment can already be empty");
err.multipart_suggestion(
"remove the `$(` and `)?`",

View file

@ -205,7 +205,7 @@ declare_features! (
(accepted, extended_key_value_attributes, "1.54.0", Some(78835)),
/// Allows using `aapcs`, `efiapi`, `sysv64` and `win64` as calling conventions
/// for functions with varargs.
(accepted, extended_varargs_abi_support, "CURRENT_RUSTC_VERSION", Some(100189)),
(accepted, extended_varargs_abi_support, "1.91.0", Some(100189)),
/// Allows resolving absolute paths as paths from other crates.
(accepted, extern_absolute_paths, "1.30.0", Some(44660)),
/// Allows `extern crate foo as bar;`. This puts `bar` into extern prelude.
@ -400,7 +400,7 @@ declare_features! (
/// Allows use of `&foo[a..b]` as a slicing syntax.
(accepted, slicing_syntax, "1.0.0", None),
/// Allows use of `sse4a` target feature.
(accepted, sse4a_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)),
(accepted, sse4a_target_feature, "1.91.0", Some(44839)),
/// Allows elision of `'static` lifetimes in `static`s and `const`s.
(accepted, static_in_const, "1.17.0", Some(35897)),
/// Allows the definition recursive static items.
@ -414,7 +414,7 @@ declare_features! (
/// Allows the use of `#[target_feature]` on safe functions.
(accepted, target_feature_11, "1.86.0", Some(69098)),
/// Allows use of `tbm` target feature.
(accepted, tbm_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)),
(accepted, tbm_target_feature, "1.91.0", Some(44839)),
/// Allows `fn main()` with return types which implements `Termination` (RFC 1937).
(accepted, termination_trait, "1.26.0", Some(43301)),
/// Allows `#[test]` functions where the return type implements `Termination` (RFC 1937).

View file

@ -1211,6 +1211,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
"the `#[rustc_layout_scalar_valid_range_end]` attribute is just used to enable \
niche optimizations in the standard library",
),
rustc_attr!(
rustc_simd_monomorphize_lane_limit, Normal, template!(NameValueStr: "N"), ErrorFollowing,
EncodeCrossCrate::Yes,
"the `#[rustc_simd_monomorphize_lane_limit]` attribute is just used by std::simd \
for better error messages",
),
rustc_attr!(
rustc_nonnull_optimization_guaranteed, Normal, template!(Word), WarnFollowing,
EncodeCrossCrate::Yes,

View file

@ -101,6 +101,10 @@ declare_features! (
Some("never properly implemented; requires significant design work"), 127655),
/// Allows deriving traits as per `SmartPointer` specification
(removed, derive_smart_pointer, "1.84.0", Some(123430), Some("replaced by `CoercePointee`"), 131284),
/// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.
(removed, doc_auto_cfg, "1.58.0", Some(43781), Some("merged into `doc_cfg`"), 138907),
/// Allows `#[doc(cfg_hide(...))]`.
(removed, doc_cfg_hide, "1.57.0", Some(43781), Some("merged into `doc_cfg`"), 138907),
/// Allows using `#[doc(keyword = "...")]`.
(removed, doc_keyword, "1.58.0", Some(51315),
Some("merged into `#![feature(rustdoc_internals)]`"), 90420),
@ -192,7 +196,7 @@ declare_features! (
(removed, no_debug, "1.43.0", Some(29721), Some("removed due to lack of demand"), 69667),
// Allows the use of `no_sanitize` attribute.
/// The feature was renamed to `sanitize` and the attribute to `#[sanitize(xyz = "on|off")]`
(removed, no_sanitize, "CURRENT_RUSTC_VERSION", Some(39699), Some(r#"renamed to sanitize(xyz = "on|off")"#), 142681),
(removed, no_sanitize, "1.91.0", Some(39699), Some(r#"renamed to sanitize(xyz = "on|off")"#), 142681),
/// Note: this feature was previously recorded in a separate
/// `STABLE_REMOVED` list because it, uniquely, was once stable but was
/// then removed. But there was no utility storing it separately, so now
@ -203,7 +207,7 @@ declare_features! (
(removed, object_safe_for_dispatch, "1.83.0", Some(43561),
Some("renamed to `dyn_compatible_for_dispatch`"), 131511),
/// Allows using `#[omit_gdb_pretty_printer_section]`.
(removed, omit_gdb_pretty_printer_section, "CURRENT_RUSTC_VERSION", None, None, 144738),
(removed, omit_gdb_pretty_printer_section, "1.91.0", None, None, 144738),
/// Allows using `#[on_unimplemented(..)]` on traits.
/// (Moved to `rustc_attrs`.)
(removed, on_unimplemented, "1.40.0", None, None, 65794),

View file

@ -327,7 +327,7 @@ declare_features! (
(unstable, m68k_target_feature, "1.85.0", Some(134328)),
(unstable, mips_target_feature, "1.27.0", Some(44839)),
(unstable, movrs_target_feature, "1.88.0", Some(137976)),
(unstable, nvptx_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)),
(unstable, nvptx_target_feature, "1.91.0", Some(44839)),
(unstable, powerpc_target_feature, "1.27.0", Some(44839)),
(unstable, prfchw_target_feature, "1.78.0", Some(44839)),
(unstable, riscv_target_feature, "1.45.0", Some(44839)),
@ -471,17 +471,13 @@ declare_features! (
/// Allows deref patterns.
(incomplete, deref_patterns, "1.79.0", Some(87121)),
/// Allows deriving the From trait on single-field structs.
(unstable, derive_from, "CURRENT_RUSTC_VERSION", Some(144889)),
/// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.
(unstable, doc_auto_cfg, "1.58.0", Some(43781)),
(unstable, derive_from, "1.91.0", Some(144889)),
/// Allows `#[doc(cfg(...))]`.
(unstable, doc_cfg, "1.21.0", Some(43781)),
/// Allows `#[doc(cfg_hide(...))]`.
(unstable, doc_cfg_hide, "1.57.0", Some(43781)),
/// Allows `#[doc(masked)]`.
(unstable, doc_masked, "1.21.0", Some(44027)),
/// Allows features to allow target_feature to better interact with traits.
(incomplete, effective_target_features, "CURRENT_RUSTC_VERSION", Some(143352)),
(incomplete, effective_target_features, "1.91.0", Some(143352)),
/// Allows the .use postfix syntax `x.use` and use closures `use |x| { ... }`
(incomplete, ergonomic_clones, "1.87.0", Some(132290)),
/// Allows exhaustive pattern matching on types that contain uninhabited types.
@ -554,9 +550,9 @@ declare_features! (
/// Allows fused `loop`/`match` for direct intraprocedural jumps.
(incomplete, loop_match, "1.90.0", Some(132306)),
/// Allow `macro_rules!` attribute rules
(unstable, macro_attr, "CURRENT_RUSTC_VERSION", Some(83527)),
(unstable, macro_attr, "1.91.0", Some(143547)),
/// Allow `macro_rules!` derive rules
(unstable, macro_derive, "CURRENT_RUSTC_VERSION", Some(143549)),
(unstable, macro_derive, "1.91.0", Some(143549)),
/// Give access to additional metadata about declarative macro meta-variables.
(unstable, macro_metavar_expr, "1.61.0", Some(83527)),
/// Provides a way to concatenate identifiers using metavariable expressions.
@ -613,7 +609,7 @@ declare_features! (
(unstable, proc_macro_hygiene, "1.30.0", Some(54727)),
/// Allows the use of raw-dylibs on ELF platforms
(incomplete, raw_dylib_elf, "1.87.0", Some(135694)),
(unstable, reborrow, "CURRENT_RUSTC_VERSION", Some(145612)),
(unstable, reborrow, "1.91.0", Some(145612)),
/// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024.
(incomplete, ref_pat_eat_one_layer_2024, "1.79.0", Some(123076)),
/// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024—structural variant
@ -627,13 +623,13 @@ declare_features! (
/// Allows `extern "rust-cold"`.
(unstable, rust_cold_cc, "1.63.0", Some(97544)),
/// Allows the use of the `sanitize` attribute.
(unstable, sanitize, "CURRENT_RUSTC_VERSION", Some(39699)),
(unstable, sanitize, "1.91.0", Some(39699)),
/// Allows the use of SIMD types in functions declared in `extern` blocks.
(unstable, simd_ffi, "1.0.0", Some(27731)),
/// Allows specialization of implementations (RFC 1210).
(incomplete, specialization, "1.7.0", Some(31844)),
/// Allows using `#[rustc_align_static(...)]` on static items.
(unstable, static_align, "CURRENT_RUSTC_VERSION", Some(146177)),
(unstable, static_align, "1.91.0", Some(146177)),
/// Allows attributes on expressions and non-item statements.
(unstable, stmt_expr_attributes, "1.6.0", Some(15701)),
/// Allows lints part of the strict provenance effort.
@ -645,7 +641,7 @@ declare_features! (
/// Allows subtrait items to shadow supertrait items.
(unstable, supertrait_item_shadowing, "1.86.0", Some(89151)),
/// Allows the use of target_feature when a function is marked inline(always).
(unstable, target_feature_inline_always, "CURRENT_RUSTC_VERSION", Some(145574)),
(unstable, target_feature_inline_always, "1.91.0", Some(145574)),
/// Allows using `#[thread_local]` on `static` items.
(unstable, thread_local, "1.0.0", Some(29594)),
/// Allows defining `trait X = A + B;` alias items.

View file

@ -363,6 +363,20 @@ pub struct LinkEntry {
pub import_name_type: Option<(PeImportNameType, Span)>,
}
#[derive(HashStable_Generic, PrintAttribute)]
#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)]
pub enum DebuggerVisualizerType {
Natvis,
GdbPrettyPrinter,
}
#[derive(Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)]
pub struct DebugVisualizer {
pub span: Span,
pub visualizer_type: DebuggerVisualizerType,
pub path: Symbol,
}
/// Represents parsed *built-in* inert attributes.
///
/// ## Overview
@ -485,7 +499,10 @@ pub enum AttributeKind {
/// Represents `#[custom_mir]`.
CustomMir(Option<(MirDialect, Span)>, Option<(MirPhase, Span)>, Span),
///Represents `#[rustc_deny_explicit_impl]`.
/// Represents `#[debugger_visualizer]`.
DebuggerVisualizer(ThinVec<DebugVisualizer>),
/// Represents `#[rustc_deny_explicit_impl]`.
DenyExplicitImpl(Span),
/// Represents [`#[deprecated]`](https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html#the-deprecated-attribute).
@ -651,6 +668,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_object_lifetime_default]`.
RustcObjectLifetimeDefault,
/// Represents `#[rustc_simd_monomorphize_lane_limit = "N"]`.
RustcSimdMonomorphizeLaneLimit(Limit),
/// Represents `#[sanitize]`
///
/// the on set and off set are distjoint since there's a third option: unset.

View file

@ -37,6 +37,7 @@ impl AttributeKind {
Coverage(..) => No,
CrateName { .. } => No,
CustomMir(_, _, _) => Yes,
DebuggerVisualizer(..) => No,
DenyExplicitImpl(..) => No,
Deprecation { .. } => Yes,
DoNotImplementViaObject(..) => No,
@ -88,6 +89,7 @@ impl AttributeKind {
RustcLayoutScalarValidRangeEnd(..) => Yes,
RustcLayoutScalarValidRangeStart(..) => Yes,
RustcObjectLifetimeDefault => No,
RustcSimdMonomorphizeLaneLimit(..) => Yes, // Affects layout computation, which needs to work cross-crate
Sanitize { .. } => No,
ShouldPanic { .. } => No,
SkipDuringMethodDispatch { .. } => No,

View file

@ -440,6 +440,7 @@ language_item_table! {
// Reborrowing related lang-items
Reborrow, sym::reborrow, reborrow, Target::Trait, GenericRequirement::Exact(0);
CoerceShared, sym::coerce_shared, coerce_shared, Target::Trait, GenericRequirement::Exact(0);
}
/// The requirement imposed on the generics of a lang item

View file

@ -219,6 +219,7 @@ fn should_emit_generic_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError
}
Unknown(..)
| SizeOverflow(..)
| InvalidSimd { .. }
| NormalizationFailure(..)
| ReferencesError(..)
| Cycle(..) => {

View file

@ -1633,8 +1633,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expected: Expectation<'tcx>,
) -> Ty<'tcx> {
let rcvr_t = self.check_expr(rcvr);
// no need to check for bot/err -- callee does that
let rcvr_t = self.structurally_resolve_type(rcvr.span, rcvr_t);
let rcvr_t = self.try_structurally_resolve_type(rcvr.span, rcvr_t);
match self.lookup_method(rcvr_t, segment, segment.ident.span, expr, rcvr, args) {
Ok(method) => {

View file

@ -172,7 +172,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
// Commit the autoderefs by calling `autoderef` again, but this
// time writing the results into the various typeck results.
let mut autoderef = self.autoderef(self.call_expr.span, unadjusted_self_ty);
let Some((ty, n)) = autoderef.nth(pick.autoderefs) else {
let Some((mut target, n)) = autoderef.nth(pick.autoderefs) else {
return Ty::new_error_with_message(
self.tcx,
DUMMY_SP,
@ -182,8 +182,6 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
assert_eq!(n, pick.autoderefs);
let mut adjustments = self.adjust_steps(&autoderef);
let mut target = self.structurally_resolve_type(autoderef.span(), ty);
match pick.autoref_or_ptr_adjustment {
Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => {
let region = self.next_region_var(RegionVariableOrigin::Autoref(self.span));

View file

@ -13,7 +13,7 @@ use rustc_hir::def::DefKind;
use rustc_hir_analysis::autoderef::{self, Autoderef};
use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TyCtxtInferExt};
use rustc_infer::traits::ObligationCauseCode;
use rustc_infer::traits::{ObligationCauseCode, PredicateObligation, query};
use rustc_middle::middle::stability;
use rustc_middle::ty::elaborate::supertrait_def_ids;
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, simplify_type};
@ -30,7 +30,8 @@ use rustc_span::edit_distance::{
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};
use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded;
use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_trait_selection::traits::query::CanonicalTyGoal;
use rustc_trait_selection::solve::Goal;
use rustc_trait_selection::traits::query::CanonicalMethodAutoderefStepsGoal;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use rustc_trait_selection::traits::query::method_autoderef::{
CandidateStep, MethodAutoderefBadTy, MethodAutoderefStepsResult,
@ -389,10 +390,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
OP: FnOnce(ProbeContext<'_, 'tcx>) -> Result<R, MethodError<'tcx>>,
{
let mut orig_values = OriginalQueryValues::default();
let query_input = self.canonicalize_query(
ParamEnvAnd { param_env: self.param_env, value: self_ty },
&mut orig_values,
);
let predefined_opaques_in_body = if self.next_trait_solver() {
self.tcx.mk_predefined_opaques_in_body_from_iter(
self.inner.borrow_mut().opaque_types().iter_opaque_types().map(|(k, v)| (k, v.ty)),
)
} else {
ty::List::empty()
};
let value = query::MethodAutoderefSteps { predefined_opaques_in_body, self_ty };
let query_input = self
.canonicalize_query(ParamEnvAnd { param_env: self.param_env, value }, &mut orig_values);
let steps = match mode {
Mode::MethodCall => self.tcx.method_autoderef_steps(query_input),
@ -403,13 +410,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// special handling for this "trivial case" is a good idea.
let infcx = &self.infcx;
let (ParamEnvAnd { param_env: _, value: self_ty }, var_values) =
let (ParamEnvAnd { param_env: _, value }, var_values) =
infcx.instantiate_canonical(span, &query_input.canonical);
let query::MethodAutoderefSteps { predefined_opaques_in_body: _, self_ty } = value;
debug!(?self_ty, ?query_input, "probe_op: Mode::Path");
MethodAutoderefStepsResult {
steps: infcx.tcx.arena.alloc_from_iter([CandidateStep {
self_ty: self
.make_query_response_ignoring_pending_obligations(var_values, self_ty),
self_ty_is_opaque: false,
autoderefs: 0,
from_unsafe_deref: false,
unsize: false,
@ -479,6 +488,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty::Infer(ty::TyVar(_)) => {
let raw_ptr_call = bad_ty.reached_raw_pointer
&& !self.tcx.features().arbitrary_self_types();
// FIXME: Ideally we'd use the span of the self-expr here,
// not of the method path.
let mut err = self.err_ctxt().emit_inference_failure_err(
self.body_id,
span,
@ -553,12 +564,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(crate) fn method_autoderef_steps<'tcx>(
tcx: TyCtxt<'tcx>,
goal: CanonicalTyGoal<'tcx>,
goal: CanonicalMethodAutoderefStepsGoal<'tcx>,
) -> MethodAutoderefStepsResult<'tcx> {
debug!("method_autoderef_steps({:?})", goal);
let (ref infcx, goal, inference_vars) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal);
let ParamEnvAnd { param_env, value: self_ty } = goal;
let ParamEnvAnd {
param_env,
value: query::MethodAutoderefSteps { predefined_opaques_in_body, self_ty },
} = goal;
for (key, ty) in predefined_opaques_in_body {
let prev =
infcx.register_hidden_type_in_storage(key, ty::OpaqueHiddenType { span: DUMMY_SP, ty });
// It may be possible that two entries in the opaque type storage end up
// with the same key after resolving contained inference variables.
//
// We could put them in the duplicate list but don't have to. The opaques we
// encounter here are already tracked in the caller, so there's no need to
// also store them here. We'd take them out when computing the query response
// and then discard them, as they're already present in the input.
//
// Ideally we'd drop duplicate opaque type definitions when computing
// the canonical input. This is more annoying to implement and may cause a
// perf regression, so we do it inside of the query for now.
if let Some(prev) = prev {
debug!(?key, ?ty, ?prev, "ignore duplicate in `opaque_types_storage`");
}
}
// We accept not-yet-defined opaque types in the autoderef
// chain to support recursive calls. We do error if the final
// infer var is not an opaque.
let self_ty_is_opaque = |ty: Ty<'_>| {
if let &ty::Infer(ty::TyVar(vid)) = ty.kind() {
infcx.has_opaques_with_sub_unified_hidden_type(vid)
} else {
false
}
};
// If arbitrary self types is not enabled, we follow the chain of
// `Deref<Target=T>`. If arbitrary self types is enabled, we instead
@ -593,6 +636,7 @@ pub(crate) fn method_autoderef_steps<'tcx>(
let step = CandidateStep {
self_ty: infcx
.make_query_response_ignoring_pending_obligations(inference_vars, ty),
self_ty_is_opaque: self_ty_is_opaque(ty),
autoderefs: d,
from_unsafe_deref: reached_raw_pointer,
unsize: false,
@ -613,6 +657,7 @@ pub(crate) fn method_autoderef_steps<'tcx>(
let step = CandidateStep {
self_ty: infcx
.make_query_response_ignoring_pending_obligations(inference_vars, ty),
self_ty_is_opaque: self_ty_is_opaque(ty),
autoderefs: d,
from_unsafe_deref: reached_raw_pointer,
unsize: false,
@ -629,7 +674,11 @@ pub(crate) fn method_autoderef_steps<'tcx>(
};
let final_ty = autoderef_via_deref.final_ty();
let opt_bad_ty = match final_ty.kind() {
ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy {
ty::Infer(ty::TyVar(_)) if !self_ty_is_opaque(final_ty) => Some(MethodAutoderefBadTy {
reached_raw_pointer,
ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty),
}),
ty::Error(_) => Some(MethodAutoderefBadTy {
reached_raw_pointer,
ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty),
}),
@ -640,6 +689,7 @@ pub(crate) fn method_autoderef_steps<'tcx>(
inference_vars,
Ty::new_slice(infcx.tcx, *elem_ty),
),
self_ty_is_opaque: false,
autoderefs,
// this could be from an unsafe deref if we had
// a *mut/const [T; N]
@ -655,7 +705,8 @@ pub(crate) fn method_autoderef_steps<'tcx>(
};
debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty);
// Need to empty the opaque types storage before it gets dropped.
let _ = infcx.take_opaque_types();
MethodAutoderefStepsResult {
steps: tcx.arena.alloc_from_iter(steps),
opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)),
@ -1203,7 +1254,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
!step.self_ty.value.references_error() && !step.from_unsafe_deref
})
.find_map(|step| {
let InferOk { value: self_ty, obligations: _ } = self
let InferOk { value: self_ty, obligations: instantiate_self_ty_obligations } = self
.fcx
.probe_instantiate_query_response(
self.span,
@ -1214,7 +1265,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
span_bug!(self.span, "{:?} was applicable but now isn't?", step.self_ty)
});
let by_value_pick = self.pick_by_value_method(step, self_ty, pick_diag_hints);
let by_value_pick = self.pick_by_value_method(
step,
self_ty,
&instantiate_self_ty_obligations,
pick_diag_hints,
);
// Check for shadowing of a by-reference method by a by-value method (see comments on check_for_shadowing)
if let Some(by_value_pick) = by_value_pick {
@ -1225,6 +1281,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
by_value_pick,
step,
self_ty,
&instantiate_self_ty_obligations,
mutbl,
track_unstable_candidates,
) {
@ -1239,6 +1296,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let autoref_pick = self.pick_autorefd_method(
step,
self_ty,
&instantiate_self_ty_obligations,
hir::Mutability::Not,
pick_diag_hints,
None,
@ -1252,6 +1310,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
autoref_pick,
step,
self_ty,
&instantiate_self_ty_obligations,
hir::Mutability::Mut,
track_unstable_candidates,
) {
@ -1288,12 +1347,27 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
self.pick_autorefd_method(
step,
self_ty,
&instantiate_self_ty_obligations,
hir::Mutability::Mut,
pick_diag_hints,
None,
)
.or_else(|| self.pick_const_ptr_method(step, self_ty, pick_diag_hints))
.or_else(|| self.pick_reborrow_pin_method(step, self_ty, pick_diag_hints))
.or_else(|| {
self.pick_const_ptr_method(
step,
self_ty,
&instantiate_self_ty_obligations,
pick_diag_hints,
)
})
.or_else(|| {
self.pick_reborrow_pin_method(
step,
self_ty,
&instantiate_self_ty_obligations,
pick_diag_hints,
)
})
})
}
@ -1317,6 +1391,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
possible_shadower: &Pick<'tcx>,
step: &CandidateStep<'tcx>,
self_ty: Ty<'tcx>,
instantiate_self_ty_obligations: &[PredicateObligation<'tcx>],
mutbl: hir::Mutability,
track_unstable_candidates: bool,
) -> Result<(), MethodError<'tcx>> {
@ -1381,6 +1456,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let potentially_shadowed_pick = self.pick_autorefd_method(
step,
self_ty,
instantiate_self_ty_obligations,
mutbl,
&mut pick_diag_hints,
Some(&pick_constraints),
@ -1407,13 +1483,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
&self,
step: &CandidateStep<'tcx>,
self_ty: Ty<'tcx>,
instantiate_self_ty_obligations: &[PredicateObligation<'tcx>],
pick_diag_hints: &mut PickDiagHints<'_, 'tcx>,
) -> Option<PickResult<'tcx>> {
if step.unsize {
return None;
}
self.pick_method(self_ty, pick_diag_hints, None).map(|r| {
self.pick_method(self_ty, instantiate_self_ty_obligations, pick_diag_hints, None).map(|r| {
r.map(|mut pick| {
pick.autoderefs = step.autoderefs;
@ -1450,6 +1527,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
&self,
step: &CandidateStep<'tcx>,
self_ty: Ty<'tcx>,
instantiate_self_ty_obligations: &[PredicateObligation<'tcx>],
mutbl: hir::Mutability,
pick_diag_hints: &mut PickDiagHints<'_, 'tcx>,
pick_constraints: Option<&PickConstraintsForShadowed>,
@ -1466,7 +1544,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let region = tcx.lifetimes.re_erased;
let autoref_ty = Ty::new_ref(tcx, region, self_ty, mutbl);
self.pick_method(autoref_ty, pick_diag_hints, pick_constraints).map(|r| {
self.pick_method(
autoref_ty,
instantiate_self_ty_obligations,
pick_diag_hints,
pick_constraints,
)
.map(|r| {
r.map(|mut pick| {
pick.autoderefs = step.autoderefs;
pick.autoref_or_ptr_adjustment =
@ -1482,6 +1566,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
&self,
step: &CandidateStep<'tcx>,
self_ty: Ty<'tcx>,
instantiate_self_ty_obligations: &[PredicateObligation<'tcx>],
pick_diag_hints: &mut PickDiagHints<'_, 'tcx>,
) -> Option<PickResult<'tcx>> {
if !self.tcx.features().pin_ergonomics() {
@ -1503,14 +1588,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let region = self.tcx.lifetimes.re_erased;
let autopin_ty = Ty::new_pinned_ref(self.tcx, region, inner_ty, hir::Mutability::Not);
self.pick_method(autopin_ty, pick_diag_hints, None).map(|r| {
r.map(|mut pick| {
pick.autoderefs = step.autoderefs;
pick.autoref_or_ptr_adjustment =
Some(AutorefOrPtrAdjustment::ReborrowPin(hir::Mutability::Not));
pick
})
})
self.pick_method(autopin_ty, instantiate_self_ty_obligations, pick_diag_hints, None).map(
|r| {
r.map(|mut pick| {
pick.autoderefs = step.autoderefs;
pick.autoref_or_ptr_adjustment =
Some(AutorefOrPtrAdjustment::ReborrowPin(hir::Mutability::Not));
pick
})
},
)
}
/// If `self_ty` is `*mut T` then this picks `*const T` methods. The reason why we have a
@ -1520,6 +1607,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
&self,
step: &CandidateStep<'tcx>,
self_ty: Ty<'tcx>,
instantiate_self_ty_obligations: &[PredicateObligation<'tcx>],
pick_diag_hints: &mut PickDiagHints<'_, 'tcx>,
) -> Option<PickResult<'tcx>> {
// Don't convert an unsized reference to ptr
@ -1532,18 +1620,21 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
};
let const_ptr_ty = Ty::new_imm_ptr(self.tcx, ty);
self.pick_method(const_ptr_ty, pick_diag_hints, None).map(|r| {
r.map(|mut pick| {
pick.autoderefs = step.autoderefs;
pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr);
pick
})
})
self.pick_method(const_ptr_ty, instantiate_self_ty_obligations, pick_diag_hints, None).map(
|r| {
r.map(|mut pick| {
pick.autoderefs = step.autoderefs;
pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr);
pick
})
},
)
}
fn pick_method(
&self,
self_ty: Ty<'tcx>,
instantiate_self_ty_obligations: &[PredicateObligation<'tcx>],
pick_diag_hints: &mut PickDiagHints<'_, 'tcx>,
pick_constraints: Option<&PickConstraintsForShadowed>,
) -> Option<PickResult<'tcx>> {
@ -1553,8 +1644,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
[("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)]
{
debug!("searching {} candidates", kind);
let res =
self.consider_candidates(self_ty, candidates, pick_diag_hints, pick_constraints);
let res = self.consider_candidates(
self_ty,
instantiate_self_ty_obligations,
candidates,
pick_diag_hints,
pick_constraints,
);
if let Some(pick) = res {
return Some(pick);
}
@ -1563,6 +1659,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
if self.private_candidate.get().is_none() {
if let Some(Ok(pick)) = self.consider_candidates(
self_ty,
instantiate_self_ty_obligations,
&self.private_candidates,
&mut PickDiagHints {
unstable_candidates: None,
@ -1579,6 +1676,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
fn consider_candidates(
&self,
self_ty: Ty<'tcx>,
instantiate_self_ty_obligations: &[PredicateObligation<'tcx>],
candidates: &[Candidate<'tcx>],
pick_diag_hints: &mut PickDiagHints<'_, 'tcx>,
pick_constraints: Option<&PickConstraintsForShadowed>,
@ -1595,6 +1693,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
probe,
self.consider_probe(
self_ty,
instantiate_self_ty_obligations,
probe,
&mut pick_diag_hints.unsatisfied_predicates,
),
@ -1779,10 +1878,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}
#[instrument(level = "trace", skip(self, possibly_unsatisfied_predicates), ret)]
#[instrument(level = "debug", skip(self, possibly_unsatisfied_predicates), ret)]
fn consider_probe(
&self,
self_ty: Ty<'tcx>,
instantiate_self_ty_obligations: &[PredicateObligation<'tcx>],
probe: &Candidate<'tcx>,
possibly_unsatisfied_predicates: &mut Vec<(
ty::Predicate<'tcx>,
@ -1790,8 +1890,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
Option<ObligationCause<'tcx>>,
)>,
) -> ProbeResult {
debug!("consider_probe: self_ty={:?} probe={:?}", self_ty, probe);
self.probe(|snapshot| {
let outer_universe = self.universe();
@ -1799,6 +1897,21 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let cause = &self.misc(self.span);
let ocx = ObligationCtxt::new_with_diagnostics(self);
// Subtle: we're not *really* instantiating the current self type while
// probing, but instead fully recompute the autoderef steps once we've got
// a final `Pick`. We can't nicely handle these obligations outside of a probe.
//
// We simply handle them for each candidate here for now. That's kinda scuffed
// and ideally we just put them into the `FnCtxt` right away. We need to consider
// them to deal with defining uses in `method_autoderef_steps`.
if self.next_trait_solver() {
ocx.register_obligations(instantiate_self_ty_obligations.iter().cloned());
let errors = ocx.select_where_possible();
if !errors.is_empty() {
unreachable!("unexpected autoderef error {errors:?}");
}
}
let mut trait_predicate = None;
let (mut xform_self_ty, mut xform_ret_ty);
@ -2041,6 +2154,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}
if self.infcx.next_trait_solver() {
if self.should_reject_candidate_due_to_opaque_treated_as_rigid(trait_predicate) {
result = ProbeResult::NoMatch;
}
}
// Previously, method probe used `evaluate_predicate` to determine if a predicate
// was impossible to satisfy. This did a leak check, so we must also do a leak
// check here to prevent backwards-incompatible ambiguity being introduced. See
@ -2054,6 +2173,80 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
})
}
/// Trait candidates for not-yet-defined opaque types are a somewhat hacky.
///
/// We want to only accept trait methods if they were hold even if the
/// opaque types were rigid. To handle this, we both check that for trait
/// candidates the goal were to hold even when treating opaques as rigid,
/// see [OpaqueTypesJank](rustc_trait_selection::solve::OpaqueTypesJank).
///
/// We also check that all opaque types encountered as self types in the
/// autoderef chain don't get constrained when applying the candidate.
/// Importantly, this also handles calling methods taking `&self` on
/// `impl Trait` to reject the "by-self" candidate.
///
/// This needs to happen at the end of `consider_probe` as we need to take
/// all the constraints from that into account.
#[instrument(level = "debug", skip(self), ret)]
fn should_reject_candidate_due_to_opaque_treated_as_rigid(
&self,
trait_predicate: Option<ty::Predicate<'tcx>>,
) -> bool {
// This function is what hacky and doesn't perfectly do what we want it to.
// It's not soundness critical and we should be able to freely improve this
// in the future.
//
// Some concrete edge cases include the fact that `goal_may_hold_opaque_types_jank`
// also fails if there are any constraints opaques which are never used as a self
// type. We also allow where-bounds which are currently ambiguous but end up
// constraining an opaque later on.
// Check whether the trait candidate would not be applicable if the
// opaque type were rigid.
if let Some(predicate) = trait_predicate {
let goal = Goal { param_env: self.param_env, predicate };
if !self.infcx.goal_may_hold_opaque_types_jank(goal) {
return true;
}
}
// Check whether any opaque types in the autoderef chain have been
// constrained.
for step in self.steps {
if step.self_ty_is_opaque {
debug!(?step.autoderefs, ?step.self_ty, "self_type_is_opaque");
let constrained_opaque = self.probe(|_| {
// If we fail to instantiate the self type of this
// step, this part of the deref-chain is no longer
// reachable. In this case we don't care about opaque
// types there.
let Ok(ok) = self.fcx.probe_instantiate_query_response(
self.span,
self.orig_steps_var_values,
&step.self_ty,
) else {
debug!("failed to instantiate self_ty");
return false;
};
let ocx = ObligationCtxt::new(self);
let self_ty = ocx.register_infer_ok_obligations(ok);
if !ocx.select_where_possible().is_empty() {
debug!("failed to prove instantiate self_ty obligations");
return false;
}
!self.resolve_vars_if_possible(self_ty).is_ty_var()
});
if constrained_opaque {
debug!("opaque type has been constrained");
return true;
}
}
}
false
}
/// Sometimes we get in a situation where we have multiple probes that are all impls of the
/// same trait, but we don't know which impl to use. In this case, since in all cases the
/// external interface of the method can be determined from the trait, it's ok not to decide.

View file

@ -1003,8 +1003,10 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Resolver<'cx, 'tcx> {
}
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
debug_assert!(!r.is_bound(), "Should not be resolving bound region.");
self.fcx.tcx.lifetimes.re_erased
match r.kind() {
ty::ReBound(..) => r,
_ => self.fcx.tcx.lifetimes.re_erased,
}
}
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {

View file

@ -130,12 +130,6 @@ impl<I: Idx, T> IntoSliceIdx<I, [T]> for core::range::RangeFrom<I> {
impl<I: Idx, T> IntoSliceIdx<I, [T]> for core::range::RangeInclusive<I> {
type Output = core::range::RangeInclusive<usize>;
#[inline]
#[cfg(bootstrap)]
fn into_slice_idx(self) -> Self::Output {
core::range::RangeInclusive { start: self.start.index(), end: self.end.index() }
}
#[inline]
#[cfg(not(bootstrap))]
fn into_slice_idx(self) -> Self::Output {
core::range::RangeInclusive { start: self.start.index(), last: self.last.index() }
}

View file

@ -85,11 +85,29 @@ impl<'tcx> InferCtxt<'tcx> {
where
T: Debug + TypeFoldable<TyCtxt<'tcx>>,
{
// While we ignore region constraints and pending obligations,
// we do return constrained opaque types to avoid unconstrained
// inference variables in the response. This is important as we want
// to check that opaques in deref steps stay unconstrained.
//
// This doesn't handle the more general case for non-opaques as
// ambiguous `Projection` obligations have same the issue.
let opaque_types = if self.next_trait_solver() {
self.inner
.borrow_mut()
.opaque_type_storage
.iter_opaque_types()
.map(|(k, v)| (k, v.ty))
.collect()
} else {
vec![]
};
self.canonicalize_response(QueryResponse {
var_values: inference_vars,
region_constraints: QueryRegionConstraints::default(),
certainty: Certainty::Proven, // Ambiguities are OK!
opaque_types: vec![],
opaque_types,
value: answer,
})
}

View file

@ -765,7 +765,7 @@ fn test_unstable_options_tracking_hash() {
tracked!(allow_features, Some(vec![String::from("lang_items")]));
tracked!(always_encode_mir, true);
tracked!(assume_incomplete_release, true);
tracked!(autodiff, vec![AutoDiff::Enable]);
tracked!(autodiff, vec![AutoDiff::Enable, AutoDiff::NoTT]);
tracked!(binary_dep_depinfo, true);
tracked!(box_noalias, false);
tracked!(

View file

@ -386,8 +386,8 @@ fn get_codegen_sysroot(
.collect::<Vec<_>>()
.join("\n* ");
let err = format!(
"failed to find a `codegen-backends` folder \
in the sysroot candidates:\n* {candidates}"
"failed to find a `codegen-backends` folder in the sysroot candidates:\n\
* {candidates}"
);
early_dcx.early_fatal(err);
});
@ -396,10 +396,8 @@ fn get_codegen_sysroot(
let d = sysroot.read_dir().unwrap_or_else(|e| {
let err = format!(
"failed to load default codegen backend, couldn't \
read `{}`: {}",
"failed to load default codegen backend, couldn't read `{}`: {e}",
sysroot.display(),
e
);
early_dcx.early_fatal(err);
});

View file

@ -2309,10 +2309,10 @@ declare_lint! {
/// ### Example
///
/// ```rust
/// #![cfg_attr(not(bootstrap), feature(sanitize))]
/// #![feature(sanitize)]
///
/// #[inline(always)]
/// #[cfg_attr(not(bootstrap), sanitize(address = "off"))]
/// #[sanitize(address = "off")]
/// fn x() {}
///
/// fn main() {
@ -4837,16 +4837,13 @@ declare_lint! {
///
/// ### Example
///
#[cfg_attr(not(bootstrap), doc = "```rust,compile_fail")]
#[cfg_attr(bootstrap, doc = "```rust")]
/// ```rust,compile_fail
/// #![doc = in_root!()]
///
/// macro_rules! in_root { () => { "" } }
///
/// fn main() {}
#[cfg_attr(not(bootstrap), doc = "```")]
#[cfg_attr(bootstrap, doc = "```")]
// ^ Needed to avoid tidy warning about odd number of backticks
/// ```
///
/// {{produces}}
///

View file

@ -359,10 +359,6 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
return wrap(TM);
}
extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) {
delete unwrap(TM);
}
// Unfortunately, the LLVM C API doesn't provide a way to create the
// TargetLibraryInfo pass, so we use this method to do so.
extern "C" void LLVMRustAddLibraryInfo(LLVMPassManagerRef PMR, LLVMModuleRef M,
@ -573,25 +569,43 @@ extern "C" LLVMRustResult LLVMRustOptimize(
}
std::optional<PGOOptions> PGOOpt;
#if LLVM_VERSION_LT(22, 0)
auto FS = vfs::getRealFileSystem();
#endif
if (PGOGenPath) {
assert(!PGOUsePath && !PGOSampleUsePath);
PGOOpt = PGOOptions(
#if LLVM_VERSION_GE(22, 0)
PGOGenPath, "", "", "", PGOOptions::IRInstr, PGOOptions::NoCSAction,
#else
PGOGenPath, "", "", "", FS, PGOOptions::IRInstr, PGOOptions::NoCSAction,
#endif
PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling);
} else if (PGOUsePath) {
assert(!PGOSampleUsePath);
PGOOpt = PGOOptions(
#if LLVM_VERSION_GE(22, 0)
PGOUsePath, "", "", "", PGOOptions::IRUse, PGOOptions::NoCSAction,
#else
PGOUsePath, "", "", "", FS, PGOOptions::IRUse, PGOOptions::NoCSAction,
#endif
PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling);
} else if (PGOSampleUsePath) {
PGOOpt =
#if LLVM_VERSION_GE(22, 0)
PGOOptions(PGOSampleUsePath, "", "", "", PGOOptions::SampleUse,
#else
PGOOptions(PGOSampleUsePath, "", "", "", FS, PGOOptions::SampleUse,
#endif
PGOOptions::NoCSAction, PGOOptions::ColdFuncOpt::Default,
DebugInfoForProfiling);
} else if (DebugInfoForProfiling) {
PGOOpt = PGOOptions(
#if LLVM_VERSION_GE(22, 0)
"", "", "", "", PGOOptions::NoAction, PGOOptions::NoCSAction,
#else
"", "", "", "", FS, PGOOptions::NoAction, PGOOptions::NoCSAction,
#endif
PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling);
}

View file

@ -990,14 +990,6 @@ extern "C" void LLVMRustGlobalAddMetadata(LLVMValueRef Global, unsigned Kind,
unwrap<GlobalObject>(Global)->addMetadata(Kind, *unwrap<MDNode>(MD));
}
extern "C" LLVMDIBuilderRef LLVMRustDIBuilderCreate(LLVMModuleRef M) {
return wrap(new DIBuilder(*unwrap(M)));
}
extern "C" void LLVMRustDIBuilderDispose(LLVMDIBuilderRef Builder) {
delete unwrap(Builder);
}
extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateCompileUnit(
LLVMDIBuilderRef Builder, unsigned Lang, LLVMMetadataRef FileRef,
const char *Producer, size_t ProducerLen, bool isOptimized,
@ -1129,51 +1121,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable(
return wrap(VarExpr);
}
extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable(
LLVMDIBuilderRef Builder, unsigned Tag, LLVMMetadataRef Scope,
const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo,
LLVMMetadataRef Ty, bool AlwaysPreserve, LLVMDIFlags Flags, unsigned ArgNo,
uint32_t AlignInBits) {
if (Tag == 0x100) { // DW_TAG_auto_variable
return wrap(unwrap(Builder)->createAutoVariable(
unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen),
unwrapDI<DIFile>(File), LineNo, unwrapDI<DIType>(Ty), AlwaysPreserve,
fromRust(Flags), AlignInBits));
} else {
return wrap(unwrap(Builder)->createParameterVariable(
unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), ArgNo,
unwrapDI<DIFile>(File), LineNo, unwrapDI<DIType>(Ty), AlwaysPreserve,
fromRust(Flags)));
}
}
extern "C" LLVMMetadataRef
LLVMRustDIBuilderGetOrCreateSubrange(LLVMDIBuilderRef Builder, int64_t Lo,
int64_t Count) {
return wrap(unwrap(Builder)->getOrCreateSubrange(Lo, Count));
}
extern "C" LLVMMetadataRef
LLVMRustDIBuilderGetOrCreateArray(LLVMDIBuilderRef Builder,
LLVMMetadataRef *Ptr, unsigned Count) {
Metadata **DataValue = unwrap(Ptr);
return wrap(unwrap(Builder)
->getOrCreateArray(ArrayRef<Metadata *>(DataValue, Count))
.get());
}
extern "C" void
LLVMRustDIBuilderInsertDeclareAtEnd(LLVMDIBuilderRef Builder, LLVMValueRef V,
LLVMMetadataRef VarInfo, uint64_t *AddrOps,
unsigned AddrOpsCount, LLVMMetadataRef DL,
LLVMBasicBlockRef InsertAtEnd) {
unwrap(Builder)->insertDeclare(
unwrap(V), unwrap<DILocalVariable>(VarInfo),
unwrap(Builder)->createExpression(
llvm::ArrayRef<uint64_t>(AddrOps, AddrOpsCount)),
DebugLoc(cast<MDNode>(unwrap(DL))), unwrap(InsertAtEnd));
}
extern "C" LLVMMetadataRef
LLVMRustDIBuilderCreateEnumerator(LLVMDIBuilderRef Builder, const char *Name,
size_t NameLen, const uint64_t Value[2],
@ -1685,6 +1632,14 @@ extern "C" void LLVMRustContextConfigureDiagnosticHandler(
RemarkStreamer(std::move(RemarkStreamer)),
LlvmRemarkStreamer(std::move(LlvmRemarkStreamer)) {}
#if LLVM_VERSION_GE(22, 0)
~RustDiagnosticHandler() {
if (RemarkStreamer) {
RemarkStreamer->releaseSerializer();
}
}
#endif
virtual bool handleDiagnostics(const DiagnosticInfo &DI) override {
// If this diagnostic is one of the optimization remark kinds, we can
// check if it's enabled before emitting it. This can avoid many
@ -1779,9 +1734,14 @@ extern "C" void LLVMRustContextConfigureDiagnosticHandler(
// Do not delete the file after we gather remarks
RemarkFile->keep();
#if LLVM_VERSION_GE(22, 0)
auto RemarkSerializer = remarks::createRemarkSerializer(
llvm::remarks::Format::YAML, RemarkFile->os());
#else
auto RemarkSerializer = remarks::createRemarkSerializer(
llvm::remarks::Format::YAML, remarks::SerializerMode::Separate,
RemarkFile->os());
#endif
if (Error E = RemarkSerializer.takeError()) {
std::string Error = std::string("Cannot create remark serializer: ") +
toString(std::move(E));
@ -1852,3 +1812,15 @@ extern "C" void LLVMRustSetNoSanitizeHWAddress(LLVMValueRef Global) {
MD.NoHWAddress = true;
GV.setSanitizerMetadata(MD);
}
#ifdef ENZYME
extern "C" {
extern llvm::cl::opt<unsigned> EnzymeMaxTypeDepth;
}
extern "C" size_t LLVMRustEnzymeGetMaxTypeDepth() { return EnzymeMaxTypeDepth; }
#else
extern "C" size_t LLVMRustEnzymeGetMaxTypeDepth() {
return 6; // Default fallback depth
}
#endif

View file

@ -5,7 +5,7 @@ use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{
AttrStyle, Attribute, Block, Error, Expr, Ident, Pat, ReturnType, Token, Type, braced,
parenthesized, parse_macro_input, parse_quote, token,
parenthesized, parse_macro_input, token,
};
mod kw {

View file

@ -1555,7 +1555,7 @@ impl<'a> CrateMetadataRef<'a> {
}
#[inline]
fn def_path_hash_to_def_index(self, hash: DefPathHash) -> DefIndex {
fn def_path_hash_to_def_index(self, hash: DefPathHash) -> Option<DefIndex> {
self.def_path_hash_map.def_path_hash_to_def_index(&hash)
}

View file

@ -691,8 +691,8 @@ fn provide_cstore_hooks(providers: &mut Providers) {
.get(&stable_crate_id)
.unwrap_or_else(|| bug!("uninterned StableCrateId: {stable_crate_id:?}"));
assert_ne!(cnum, LOCAL_CRATE);
let def_index = cstore.get_crate_data(cnum).def_path_hash_to_def_index(hash);
DefId { krate: cnum, index: def_index }
let def_index = cstore.get_crate_data(cnum).def_path_hash_to_def_index(hash)?;
Some(DefId { krate: cnum, index: def_index })
};
providers.hooks.expn_hash_to_expn_id = |tcx, cnum, index_guess, hash| {

View file

@ -12,11 +12,12 @@ pub(crate) enum DefPathHashMapRef<'tcx> {
impl DefPathHashMapRef<'_> {
#[inline]
pub(crate) fn def_path_hash_to_def_index(&self, def_path_hash: &DefPathHash) -> DefIndex {
pub(crate) fn def_path_hash_to_def_index(
&self,
def_path_hash: &DefPathHash,
) -> Option<DefIndex> {
match *self {
DefPathHashMapRef::OwnedFromMetadata(ref map) => {
map.get(&def_path_hash.local_hash()).unwrap()
}
DefPathHashMapRef::OwnedFromMetadata(ref map) => map.get(&def_path_hash.local_hash()),
DefPathHashMapRef::BorrowedFromTcx(_) => {
panic!("DefPathHashMap::BorrowedFromTcx variant only exists for serialization")
}

View file

@ -98,6 +98,12 @@ middle_layout_references_error =
middle_layout_size_overflow =
values of the type `{$ty}` are too big for the target architecture
middle_layout_simd_too_many =
the SIMD type `{$ty}` has more elements than the limit {$max_lanes}
middle_layout_simd_zero_length =
the SIMD type `{$ty}` has zero elements
middle_layout_too_generic = the type `{$ty}` does not have a fixed layout
middle_layout_unknown =

View file

@ -109,7 +109,6 @@ macro_rules! arena_types {
rustc_middle::ty::EarlyBinder<'tcx, rustc_middle::ty::Ty<'tcx>>
>,
[] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData<rustc_middle::ty::TyCtxt<'tcx>>,
[] predefined_opaques_in_body: rustc_middle::traits::solve::PredefinedOpaquesData<rustc_middle::ty::TyCtxt<'tcx>>,
[decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap,
[] stripped_cfg_items: rustc_hir::attrs::StrippedCfgItem,
[] mod_child: rustc_middle::metadata::ModChild,

View file

@ -37,7 +37,6 @@ pub(crate) struct OpaqueHiddenTypeMismatch<'tcx> {
pub sub: TypeMismatchReason,
}
// FIXME(autodiff): I should get used somewhere
#[derive(Diagnostic)]
#[diag(middle_unsupported_union)]
pub struct UnsupportedUnion {
@ -143,6 +142,12 @@ pub enum LayoutError<'tcx> {
#[diag(middle_layout_size_overflow)]
Overflow { ty: Ty<'tcx> },
#[diag(middle_layout_simd_too_many)]
SimdTooManyLanes { ty: Ty<'tcx>, max_lanes: u64 },
#[diag(middle_layout_simd_zero_length)]
SimdZeroLength { ty: Ty<'tcx> },
#[diag(middle_layout_normalization_failure)]
NormalizationFailure { ty: Ty<'tcx>, failure_ty: String },

View file

@ -77,7 +77,7 @@ declare_hooks! {
/// session, if it still exists. This is used during incremental compilation to
/// turn a deserialized `DefPathHash` into its current `DefId`.
/// Will fetch a DefId from a DefPathHash for a foreign crate.
hook def_path_hash_to_def_id_extern(hash: DefPathHash, stable_crate_id: StableCrateId) -> DefId;
hook def_path_hash_to_def_id_extern(hash: DefPathHash, stable_crate_id: StableCrateId) -> Option<DefId>;
/// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we
/// can just link to the upstream crate and therefore don't need a mono item.

View file

@ -29,7 +29,6 @@
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::direct_use_of_rustc_type_ir)]
#![allow(rustc::untranslatable_diagnostic)]
#![cfg_attr(bootstrap, feature(round_char_boundary))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(allocator_api)]

View file

@ -1,15 +1,9 @@
use std::path::PathBuf;
use std::sync::Arc;
use rustc_hir::attrs::DebuggerVisualizerType;
use rustc_macros::{Decodable, Encodable, HashStable};
#[derive(HashStable)]
#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)]
pub enum DebuggerVisualizerType {
Natvis,
GdbPrettyPrinter,
}
/// A single debugger visualizer file.
#[derive(HashStable)]
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable)]

View file

@ -124,7 +124,7 @@ use crate::query::plumbing::{
};
use crate::traits::query::{
CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal,
CanonicalPredicateGoal, CanonicalTyGoal, CanonicalTypeOpAscribeUserTypeGoal,
CanonicalMethodAutoderefStepsGoal, CanonicalPredicateGoal, CanonicalTypeOpAscribeUserTypeGoal,
CanonicalTypeOpNormalizeGoal, CanonicalTypeOpProvePredicateGoal, DropckConstraint,
DropckOutlivesResult, MethodAutoderefStepsResult, NoSolution, NormalizationResult,
OutlivesBound,
@ -2559,9 +2559,9 @@ rustc_queries! {
}
query method_autoderef_steps(
goal: CanonicalTyGoal<'tcx>
goal: CanonicalMethodAutoderefStepsGoal<'tcx>
) -> MethodAutoderefStepsResult<'tcx> {
desc { "computing autoderef types for `{}`", goal.canonical.value.value }
desc { "computing autoderef types for `{}`", goal.canonical.value.value.self_ty }
}
/// Used by `-Znext-solver` to compute proof trees.

View file

@ -10,6 +10,7 @@ use rustc_span::Span;
use crate::error::DropCheckOverflow;
use crate::infer::canonical::{Canonical, CanonicalQueryInput, QueryResponse};
use crate::traits::solve;
pub use crate::traits::solve::NoSolution;
use crate::ty::{self, GenericArg, Ty, TyCtxt};
@ -67,7 +68,16 @@ pub mod type_op {
pub type CanonicalAliasGoal<'tcx> =
CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, ty::AliasTy<'tcx>>>;
pub type CanonicalTyGoal<'tcx> = CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>;
pub type CanonicalMethodAutoderefStepsGoal<'tcx> =
CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, MethodAutoderefSteps<'tcx>>>;
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)]
pub struct MethodAutoderefSteps<'tcx> {
/// The list of opaque types currently in the storage.
///
/// Only used by the new solver for now.
pub predefined_opaques_in_body: solve::PredefinedOpaques<'tcx>,
pub self_ty: Ty<'tcx>,
}
pub type CanonicalPredicateGoal<'tcx> =
CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>>;
@ -144,6 +154,7 @@ impl<'tcx> FromIterator<DropckConstraint<'tcx>> for DropckConstraint<'tcx> {
#[derive(Debug, HashStable)]
pub struct CandidateStep<'tcx> {
pub self_ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
pub self_ty_is_opaque: bool,
pub autoderefs: usize,
/// `true` if the type results from a dereference of a raw pointer.
/// when assembling candidates, we include these steps, but not when

View file

@ -4,7 +4,7 @@ use rustc_type_ir as ir;
pub use rustc_type_ir::solve::*;
use crate::ty::{
self, FallibleTypeFolder, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor,
self, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor,
try_visit,
};
@ -15,16 +15,7 @@ pub type CandidateSource<'tcx> = ir::solve::CandidateSource<TyCtxt<'tcx>>;
pub type CanonicalInput<'tcx, P = ty::Predicate<'tcx>> = ir::solve::CanonicalInput<TyCtxt<'tcx>, P>;
pub type CanonicalResponse<'tcx> = ir::solve::CanonicalResponse<TyCtxt<'tcx>>;
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, HashStable)]
pub struct PredefinedOpaques<'tcx>(pub(crate) Interned<'tcx, PredefinedOpaquesData<TyCtxt<'tcx>>>);
impl<'tcx> std::ops::Deref for PredefinedOpaques<'tcx> {
type Target = PredefinedOpaquesData<TyCtxt<'tcx>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub type PredefinedOpaques<'tcx> = &'tcx ty::List<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>;
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, HashStable)]
pub struct ExternalConstraints<'tcx>(
@ -93,35 +84,3 @@ impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> {
self.normalization_nested_goals.visit_with(visitor)
}
}
// FIXME: Having to clone `region_constraints` for folding feels bad and
// probably isn't great wrt performance.
//
// Not sure how to fix this, maybe we should also intern `opaque_types` and
// `region_constraints` here or something.
impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for PredefinedOpaques<'tcx> {
fn try_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>(
self,
folder: &mut F,
) -> Result<Self, F::Error> {
Ok(FallibleTypeFolder::cx(folder).mk_predefined_opaques_in_body(PredefinedOpaquesData {
opaque_types: self
.opaque_types
.iter()
.map(|opaque| opaque.try_fold_with(folder))
.collect::<Result<_, F::Error>>()?,
}))
}
fn fold_with<F: TypeFolder<TyCtxt<'tcx>>>(self, folder: &mut F) -> Self {
TypeFolder::cx(folder).mk_predefined_opaques_in_body(PredefinedOpaquesData {
opaque_types: self.opaque_types.iter().map(|opaque| opaque.fold_with(folder)).collect(),
})
}
}
impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for PredefinedOpaques<'tcx> {
fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
self.opaque_types.visit_with(visitor)
}
}

View file

@ -75,7 +75,7 @@ use crate::thir::Thir;
use crate::traits;
use crate::traits::solve::{
self, CanonicalInput, ExternalConstraints, ExternalConstraintsData, PredefinedOpaques,
PredefinedOpaquesData, QueryResult, inspect,
QueryResult, inspect,
};
use crate::ty::predicate::ExistentialPredicateStableCmpExt as _;
use crate::ty::{
@ -116,7 +116,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
fn mk_predefined_opaques_in_body(
self,
data: PredefinedOpaquesData<Self>,
data: &[(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)],
) -> Self::PredefinedOpaques {
self.mk_predefined_opaques_in_body(data)
}
@ -941,7 +941,7 @@ pub struct CtxtInterners<'tcx> {
layout: InternedSet<'tcx, LayoutData<FieldIdx, VariantIdx>>,
adt_def: InternedSet<'tcx, AdtDefData>,
external_constraints: InternedSet<'tcx, ExternalConstraintsData<TyCtxt<'tcx>>>,
predefined_opaques_in_body: InternedSet<'tcx, PredefinedOpaquesData<TyCtxt<'tcx>>>,
predefined_opaques_in_body: InternedSet<'tcx, List<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>>,
fields: InternedSet<'tcx, List<FieldIdx>>,
local_def_ids: InternedSet<'tcx, List<LocalDefId>>,
captures: InternedSet<'tcx, List<&'tcx ty::CapturedPlace<'tcx>>>,
@ -2012,7 +2012,7 @@ impl<'tcx> TyCtxt<'tcx> {
if stable_crate_id == self.stable_crate_id(LOCAL_CRATE) {
Some(self.untracked.definitions.read().local_def_path_hash_to_def_id(hash)?.to_def_id())
} else {
Some(self.def_path_hash_to_def_id_extern(hash, stable_crate_id))
self.def_path_hash_to_def_id_extern(hash, stable_crate_id)
}
}
@ -2748,8 +2748,6 @@ direct_interners! {
adt_def: pub mk_adt_def_from_data(AdtDefData): AdtDef -> AdtDef<'tcx>,
external_constraints: pub mk_external_constraints(ExternalConstraintsData<TyCtxt<'tcx>>):
ExternalConstraints -> ExternalConstraints<'tcx>,
predefined_opaques_in_body: pub mk_predefined_opaques_in_body(PredefinedOpaquesData<TyCtxt<'tcx>>):
PredefinedOpaques -> PredefinedOpaques<'tcx>,
}
macro_rules! slice_interners {
@ -2786,6 +2784,7 @@ slice_interners!(
offset_of: pub mk_offset_of((VariantIdx, FieldIdx)),
patterns: pub mk_patterns(Pattern<'tcx>),
outlives: pub mk_outlives(ty::ArgOutlivesPredicate<'tcx>),
predefined_opaques_in_body: pub mk_predefined_opaques_in_body((ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)),
);
impl<'tcx> TyCtxt<'tcx> {
@ -3129,6 +3128,14 @@ impl<'tcx> TyCtxt<'tcx> {
T::collect_and_apply(iter, |xs| self.mk_poly_existential_predicates(xs))
}
pub fn mk_predefined_opaques_in_body_from_iter<I, T>(self, iter: I) -> T::Output
where
I: Iterator<Item = T>,
T: CollectAndApply<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>), PredefinedOpaques<'tcx>>,
{
T::collect_and_apply(iter, |xs| self.mk_predefined_opaques_in_body(xs))
}
pub fn mk_clauses_from_iter<I, T>(self, iter: I) -> T::Output
where
I: Iterator<Item = T>,

View file

@ -218,6 +218,15 @@ impl fmt::Display for ValidityRequirement {
}
}
#[derive(Copy, Clone, Debug, HashStable, TyEncodable, TyDecodable)]
pub enum SimdLayoutError {
/// The vector has 0 lanes.
ZeroLength,
/// The vector has more lanes than supported or permitted by
/// #\[rustc_simd_monomorphize_lane_limit\].
TooManyLanes(u64),
}
#[derive(Copy, Clone, Debug, HashStable, TyEncodable, TyDecodable)]
pub enum LayoutError<'tcx> {
/// A type doesn't have a sensible layout.
@ -230,6 +239,8 @@ pub enum LayoutError<'tcx> {
Unknown(Ty<'tcx>),
/// The size of a type exceeds [`TargetDataLayout::obj_size_bound`].
SizeOverflow(Ty<'tcx>),
/// A SIMD vector has invalid layout, such as zero-length or too many lanes.
InvalidSimd { ty: Ty<'tcx>, kind: SimdLayoutError },
/// The layout can vary due to a generic parameter.
///
/// Unlike `Unknown`, this variant is a "soft" error and indicates that the layout
@ -257,6 +268,10 @@ impl<'tcx> LayoutError<'tcx> {
match self {
Unknown(_) => middle_layout_unknown,
SizeOverflow(_) => middle_layout_size_overflow,
InvalidSimd { kind: SimdLayoutError::TooManyLanes(_), .. } => {
middle_layout_simd_too_many
}
InvalidSimd { kind: SimdLayoutError::ZeroLength, .. } => middle_layout_simd_zero_length,
TooGeneric(_) => middle_layout_too_generic,
NormalizationFailure(_, _) => middle_layout_normalization_failure,
Cycle(_) => middle_layout_cycle,
@ -271,6 +286,10 @@ impl<'tcx> LayoutError<'tcx> {
match self {
Unknown(ty) => E::Unknown { ty },
SizeOverflow(ty) => E::Overflow { ty },
InvalidSimd { ty, kind: SimdLayoutError::TooManyLanes(max_lanes) } => {
E::SimdTooManyLanes { ty, max_lanes }
}
InvalidSimd { ty, kind: SimdLayoutError::ZeroLength } => E::SimdZeroLength { ty },
TooGeneric(ty) => E::TooGeneric { ty },
NormalizationFailure(ty, e) => {
E::NormalizationFailure { ty, failure_ty: e.get_type_for_failure() }
@ -293,6 +312,12 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> {
LayoutError::SizeOverflow(ty) => {
write!(f, "values of the type `{ty}` are too big for the target architecture")
}
LayoutError::InvalidSimd { ty, kind: SimdLayoutError::TooManyLanes(max_lanes) } => {
write!(f, "the SIMD type `{ty}` has more elements than the limit {max_lanes}")
}
LayoutError::InvalidSimd { ty, kind: SimdLayoutError::ZeroLength } => {
write!(f, "the SIMD type `{ty}` has zero elements")
}
LayoutError::NormalizationFailure(t, e) => write!(
f,
"unable to determine layout for `{}` because `{}` cannot be normalized",
@ -374,6 +399,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
e @ LayoutError::Cycle(_)
| e @ LayoutError::Unknown(_)
| e @ LayoutError::SizeOverflow(_)
| e @ LayoutError::InvalidSimd { .. }
| e @ LayoutError::NormalizationFailure(..)
| e @ LayoutError::ReferencesError(_),
) => return Err(e),

View file

@ -25,6 +25,7 @@ pub use generic_args::{GenericArgKind, TermKind, *};
pub use generics::*;
pub use intrinsic::IntrinsicDef;
use rustc_abi::{Align, FieldIdx, Integer, IntegerType, ReprFlags, ReprOptions, VariantIdx};
use rustc_ast::expand::typetree::{FncTree, Kind, Type, TypeTree};
use rustc_ast::node_id::NodeMap;
pub use rustc_ast_ir::{Movability, Mutability, try_visit};
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
@ -62,7 +63,7 @@ pub use rustc_type_ir::solve::SizedTraitKind;
pub use rustc_type_ir::*;
#[allow(hidden_glob_reexports, unused_imports)]
use rustc_type_ir::{InferCtxtLike, Interner};
use tracing::{debug, instrument};
use tracing::{debug, instrument, trace};
pub use vtable::*;
use {rustc_ast as ast, rustc_hir as hir};
@ -2216,3 +2217,225 @@ pub struct DestructuredConst<'tcx> {
pub variant: Option<VariantIdx>,
pub fields: &'tcx [ty::Const<'tcx>],
}
/// Generate TypeTree information for autodiff.
/// This function creates TypeTree metadata that describes the memory layout
/// of function parameters and return types for Enzyme autodiff.
pub fn fnc_typetrees<'tcx>(tcx: TyCtxt<'tcx>, fn_ty: Ty<'tcx>) -> FncTree {
// Check if TypeTrees are disabled via NoTT flag
if tcx.sess.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::NoTT) {
return FncTree { args: vec![], ret: TypeTree::new() };
}
// Check if this is actually a function type
if !fn_ty.is_fn() {
return FncTree { args: vec![], ret: TypeTree::new() };
}
// Get the function signature
let fn_sig = fn_ty.fn_sig(tcx);
let sig = tcx.instantiate_bound_regions_with_erased(fn_sig);
// Create TypeTrees for each input parameter
let mut args = vec![];
for ty in sig.inputs().iter() {
let type_tree = typetree_from_ty(tcx, *ty);
args.push(type_tree);
}
// Create TypeTree for return type
let ret = typetree_from_ty(tcx, sig.output());
FncTree { args, ret }
}
/// Generate TypeTree for a specific type.
/// This function analyzes a Rust type and creates appropriate TypeTree metadata.
pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree {
let mut visited = Vec::new();
typetree_from_ty_inner(tcx, ty, 0, &mut visited)
}
/// Maximum recursion depth for TypeTree generation to prevent stack overflow
/// from pathological deeply nested types. Combined with cycle detection.
const MAX_TYPETREE_DEPTH: usize = 6;
/// Internal recursive function for TypeTree generation with cycle detection and depth limiting.
fn typetree_from_ty_inner<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
depth: usize,
visited: &mut Vec<Ty<'tcx>>,
) -> TypeTree {
if depth >= MAX_TYPETREE_DEPTH {
trace!("typetree depth limit {} reached for type: {}", MAX_TYPETREE_DEPTH, ty);
return TypeTree::new();
}
if visited.contains(&ty) {
return TypeTree::new();
}
visited.push(ty);
let result = typetree_from_ty_impl(tcx, ty, depth, visited);
visited.pop();
result
}
/// Implementation of TypeTree generation logic.
fn typetree_from_ty_impl<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
depth: usize,
visited: &mut Vec<Ty<'tcx>>,
) -> TypeTree {
typetree_from_ty_impl_inner(tcx, ty, depth, visited, false)
}
/// Internal implementation with context about whether this is for a reference target.
fn typetree_from_ty_impl_inner<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
depth: usize,
visited: &mut Vec<Ty<'tcx>>,
is_reference_target: bool,
) -> TypeTree {
if ty.is_scalar() {
let (kind, size) = if ty.is_integral() || ty.is_char() || ty.is_bool() {
(Kind::Integer, ty.primitive_size(tcx).bytes_usize())
} else if ty.is_floating_point() {
match ty {
x if x == tcx.types.f16 => (Kind::Half, 2),
x if x == tcx.types.f32 => (Kind::Float, 4),
x if x == tcx.types.f64 => (Kind::Double, 8),
x if x == tcx.types.f128 => (Kind::F128, 16),
_ => (Kind::Integer, 0),
}
} else {
(Kind::Integer, 0)
};
// Use offset 0 for scalars that are direct targets of references (like &f64)
// Use offset -1 for scalars used directly (like function return types)
let offset = if is_reference_target && !ty.is_array() { 0 } else { -1 };
return TypeTree(vec![Type { offset, size, kind, child: TypeTree::new() }]);
}
if ty.is_ref() || ty.is_raw_ptr() || ty.is_box() {
let inner_ty = if let Some(inner) = ty.builtin_deref(true) {
inner
} else {
return TypeTree::new();
};
let child = typetree_from_ty_impl_inner(tcx, inner_ty, depth + 1, visited, true);
return TypeTree(vec![Type {
offset: -1,
size: tcx.data_layout.pointer_size().bytes_usize(),
kind: Kind::Pointer,
child,
}]);
}
if ty.is_array() {
if let ty::Array(element_ty, len_const) = ty.kind() {
let len = len_const.try_to_target_usize(tcx).unwrap_or(0);
if len == 0 {
return TypeTree::new();
}
let element_tree =
typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false);
let mut types = Vec::new();
for elem_type in &element_tree.0 {
types.push(Type {
offset: -1,
size: elem_type.size,
kind: elem_type.kind,
child: elem_type.child.clone(),
});
}
return TypeTree(types);
}
}
if ty.is_slice() {
if let ty::Slice(element_ty) = ty.kind() {
let element_tree =
typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false);
return element_tree;
}
}
if let ty::Tuple(tuple_types) = ty.kind() {
if tuple_types.is_empty() {
return TypeTree::new();
}
let mut types = Vec::new();
let mut current_offset = 0;
for tuple_ty in tuple_types.iter() {
let element_tree =
typetree_from_ty_impl_inner(tcx, tuple_ty, depth + 1, visited, false);
let element_layout = tcx
.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(tuple_ty))
.ok()
.map(|layout| layout.size.bytes_usize())
.unwrap_or(0);
for elem_type in &element_tree.0 {
types.push(Type {
offset: if elem_type.offset == -1 {
current_offset as isize
} else {
current_offset as isize + elem_type.offset
},
size: elem_type.size,
kind: elem_type.kind,
child: elem_type.child.clone(),
});
}
current_offset += element_layout;
}
return TypeTree(types);
}
if let ty::Adt(adt_def, args) = ty.kind() {
if adt_def.is_struct() {
let struct_layout =
tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty));
if let Ok(layout) = struct_layout {
let mut types = Vec::new();
for (field_idx, field_def) in adt_def.all_fields().enumerate() {
let field_ty = field_def.ty(tcx, args);
let field_tree =
typetree_from_ty_impl_inner(tcx, field_ty, depth + 1, visited, false);
let field_offset = layout.fields.offset(field_idx).bytes_usize();
for elem_type in &field_tree.0 {
types.push(Type {
offset: if elem_type.offset == -1 {
field_offset as isize
} else {
field_offset as isize + elem_type.offset
},
size: elem_type.size,
kind: elem_type.kind,
child: elem_type.child.clone(),
});
}
}
return TypeTree(types);
}
}
}
TypeTree::new()
}

View file

@ -3177,8 +3177,7 @@ define_print! {
write!(p, "` can be evaluated")?;
}
ty::ClauseKind::UnstableFeature(symbol) => {
write!(p, "unstable feature: ")?;
write!(p, "`{symbol}`")?;
write!(p, "feature({symbol}) is enabled")?;
}
}
}

View file

@ -796,6 +796,7 @@ macro_rules! list_fold {
list_fold! {
&'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> : mk_poly_existential_predicates,
&'tcx ty::List<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>: mk_predefined_opaques_in_body,
&'tcx ty::List<PlaceElem<'tcx>> : mk_place_elems,
&'tcx ty::List<ty::Pattern<'tcx>> : mk_patterns,
&'tcx ty::List<ty::ArgOutlivesPredicate<'tcx>> : mk_outlives,

View file

@ -8,6 +8,7 @@ use rustc_middle::middle::region;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::*;
use rustc_middle::thir::*;
use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::cast::{CastTy, mir_cast_kind};
use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::{self, Ty, UpvarArgs};
@ -656,6 +657,27 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block.and(rvalue)
}
/// Recursively inspect a THIR expression and probe through unsizing
/// operations that can be const-folded today.
fn check_constness(&self, mut kind: &'a ExprKind<'tcx>) -> bool {
loop {
debug!(?kind, "check_constness");
match kind {
&ExprKind::ValueTypeAscription { source: eid, user_ty: _, user_ty_span: _ }
| &ExprKind::Use { source: eid }
| &ExprKind::PointerCoercion {
cast: PointerCoercion::Unsize,
source: eid,
is_from_as_cast: _,
}
| &ExprKind::Scope { region_scope: _, lint_level: _, value: eid } => {
kind = &self.thir[eid].kind
}
_ => return matches!(Category::of(&kind), Some(Category::Constant)),
}
}
}
fn build_zero_repeat(
&mut self,
mut block: BasicBlock,
@ -666,7 +688,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let this = self;
let value_expr = &this.thir[value];
let elem_ty = value_expr.ty;
if let Some(Category::Constant) = Category::of(&value_expr.kind) {
if this.check_constness(&value_expr.kind) {
// Repeating a const does nothing
} else {
// For a non-const, we may need to generate an appropriate `Drop`

View file

@ -395,12 +395,10 @@ enum NeedsTemporary {
Maybe,
}
///////////////////////////////////////////////////////////////////////////
/// The `BlockAnd` "monad" packages up the new basic block along with a
/// produced value (sometimes just unit, of course). The `unpack!`
/// macro (and methods below) makes working with `BlockAnd` much more
/// convenient.
#[must_use = "if you don't use one of these results, you're leaving a dangling edge"]
struct BlockAnd<T>(BasicBlock, T);
@ -438,9 +436,7 @@ macro_rules! unpack {
}};
}
///////////////////////////////////////////////////////////////////////////
/// the main entry point for building MIR for a function
/// The main entry point for building MIR for a function.
fn construct_fn<'tcx>(
tcx: TyCtxt<'tcx>,
fn_def: LocalDefId,

View file

@ -554,6 +554,21 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
visit::walk_expr(self, &self.thir[arg]);
return;
}
// Secondly, we allow raw borrows of union field accesses. Peel
// any of those off, and recurse normally on the LHS, which should
// reject any unsafe operations within.
let mut peeled = arg;
while let ExprKind::Scope { value: arg, .. } = self.thir[peeled].kind
&& let ExprKind::Field { lhs, name: _, variant_index: _ } = self.thir[arg].kind
&& let ty::Adt(def, _) = &self.thir[lhs].ty.kind()
&& def.is_union()
{
peeled = lhs;
}
visit::walk_expr(self, &self.thir[peeled]);
// And return so we don't recurse directly onto the union field access(es).
return;
}
ExprKind::Deref { arg } => {
if let ExprKind::StaticRef { def_id, .. } | ExprKind::ThreadLocalRef(def_id) =

View file

@ -1275,13 +1275,13 @@ fn report_non_exhaustive_match<'p, 'tcx>(
if ty.is_ptr_sized_integral() {
if ty.inner() == cx.tcx.types.usize {
err.note(format!(
"`{ty}` does not have a fixed maximum value, so half-open ranges are \
necessary to match exhaustively",
"`{ty}::MAX` is not treated as exhaustive, \
so half-open ranges are necessary to match exhaustively",
));
} else if ty.inner() == cx.tcx.types.isize {
err.note(format!(
"`{ty}` does not have fixed minimum and maximum values, so half-open \
ranges are necessary to match exhaustively",
"`{ty}::MIN` and `{ty}::MAX` are not treated as exhaustive, \
so half-open ranges are necessary to match exhaustively",
));
}
} else if ty.inner() == cx.tcx.types.str_ {

View file

@ -84,7 +84,7 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading {
body,
arena,
map: Map::new(tcx, body, Some(MAX_PLACES)),
loop_headers: loop_headers(body),
maybe_loop_headers: maybe_loop_headers(body),
opportunities: Vec::new(),
};
@ -100,7 +100,7 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading {
// Verify that we do not thread through a loop header.
for to in opportunities.iter() {
assert!(to.chain.iter().all(|&block| !finder.loop_headers.contains(block)));
assert!(to.chain.iter().all(|&block| !finder.maybe_loop_headers.contains(block)));
}
OpportunitySet::new(body, opportunities).apply(body);
}
@ -124,7 +124,7 @@ struct TOFinder<'a, 'tcx> {
ecx: InterpCx<'tcx, DummyMachine>,
body: &'a Body<'tcx>,
map: Map<'tcx>,
loop_headers: DenseBitSet<BasicBlock>,
maybe_loop_headers: DenseBitSet<BasicBlock>,
/// We use an arena to avoid cloning the slices when cloning `state`.
arena: &'a DroplessArena,
opportunities: Vec<ThreadingOpportunity>,
@ -190,7 +190,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
#[instrument(level = "trace", skip(self))]
fn start_from_switch(&mut self, bb: BasicBlock) {
let bbdata = &self.body[bb];
if bbdata.is_cleanup || self.loop_headers.contains(bb) {
if bbdata.is_cleanup || self.maybe_loop_headers.contains(bb) {
return;
}
let Some((discr, targets)) = bbdata.terminator().kind.as_switch() else { return };
@ -235,7 +235,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
depth: usize,
) {
// Do not thread through loop headers.
if self.loop_headers.contains(bb) {
if self.maybe_loop_headers.contains(bb) {
return;
}
@ -833,20 +833,28 @@ enum Update {
Decr,
}
/// Compute the set of loop headers in the given body. We define a loop header as a block which has
/// at least a predecessor which it dominates. This definition is only correct for reducible CFGs.
/// But if the CFG is already irreducible, there is no point in trying much harder.
/// is already irreducible.
fn loop_headers(body: &Body<'_>) -> DenseBitSet<BasicBlock> {
let mut loop_headers = DenseBitSet::new_empty(body.basic_blocks.len());
let dominators = body.basic_blocks.dominators();
// Only visit reachable blocks.
for (bb, bbdata) in traversal::preorder(body) {
/// Compute the set of loop headers in the given body. A loop header is usually defined as a block
/// which dominates one of its predecessors. This definition is only correct for reducible CFGs.
/// However, computing dominators is expensive, so we approximate according to the post-order
/// traversal order. A loop header for us is a block which is visited after its predecessor in
/// post-order. This is ok as we mostly need a heuristic.
fn maybe_loop_headers(body: &Body<'_>) -> DenseBitSet<BasicBlock> {
let mut maybe_loop_headers = DenseBitSet::new_empty(body.basic_blocks.len());
let mut visited = DenseBitSet::new_empty(body.basic_blocks.len());
for (bb, bbdata) in traversal::postorder(body) {
// Post-order means we visit successors before the block for acyclic CFGs.
// If the successor is not visited yet, consider it a loop header.
for succ in bbdata.terminator().successors() {
if dominators.dominates(succ, bb) {
loop_headers.insert(succ);
if !visited.contains(succ) {
maybe_loop_headers.insert(succ);
}
}
// Only mark `bb` as visited after we checked the successors, in case we have a self-loop.
// bb1: goto -> bb1;
let _new = visited.insert(bb);
debug_assert!(_new);
}
loop_headers
maybe_loop_headers
}

View file

@ -1,4 +1,5 @@
use rustc_index::{Idx, IndexVec};
use rustc_data_structures::fx::FxHashMap;
use rustc_index::Idx;
use rustc_middle::mir::*;
use rustc_middle::ty::Ty;
use rustc_span::Span;
@ -9,7 +10,7 @@ use tracing::debug;
/// and replacement of terminators, and then apply the queued changes all at
/// once with `apply`. This is useful for MIR transformation passes.
pub(crate) struct MirPatch<'tcx> {
term_patch_map: IndexVec<BasicBlock, Option<TerminatorKind<'tcx>>>,
term_patch_map: FxHashMap<BasicBlock, TerminatorKind<'tcx>>,
new_blocks: Vec<BasicBlockData<'tcx>>,
new_statements: Vec<(Location, StatementKind<'tcx>)>,
new_locals: Vec<LocalDecl<'tcx>>,
@ -22,17 +23,21 @@ pub(crate) struct MirPatch<'tcx> {
terminate_block: Option<(BasicBlock, UnwindTerminateReason)>,
body_span: Span,
next_local: usize,
/// The number of blocks at the start of the transformation. New blocks
/// get appended at the end.
next_block: usize,
}
impl<'tcx> MirPatch<'tcx> {
/// Creates a new, empty patch.
pub(crate) fn new(body: &Body<'tcx>) -> Self {
let mut result = MirPatch {
term_patch_map: IndexVec::from_elem(None, &body.basic_blocks),
term_patch_map: Default::default(),
new_blocks: vec![],
new_statements: vec![],
new_locals: vec![],
next_local: body.local_decls.len(),
next_block: body.basic_blocks.len(),
resume_block: None,
unreachable_cleanup_block: None,
unreachable_no_cleanup_block: None,
@ -141,7 +146,7 @@ impl<'tcx> MirPatch<'tcx> {
/// Has a replacement of this block's terminator been queued in this patch?
pub(crate) fn is_term_patched(&self, bb: BasicBlock) -> bool {
self.term_patch_map[bb].is_some()
self.term_patch_map.contains_key(&bb)
}
/// Universal getter for block data, either it is in 'old' blocks or in patched ones
@ -194,18 +199,17 @@ impl<'tcx> MirPatch<'tcx> {
/// Queues the addition of a new basic block.
pub(crate) fn new_block(&mut self, data: BasicBlockData<'tcx>) -> BasicBlock {
let block = self.term_patch_map.next_index();
let block = BasicBlock::from_usize(self.next_block + self.new_blocks.len());
debug!("MirPatch: new_block: {:?}: {:?}", block, data);
self.new_blocks.push(data);
self.term_patch_map.push(None);
block
}
/// Queues the replacement of a block's terminator.
pub(crate) fn patch_terminator(&mut self, block: BasicBlock, new: TerminatorKind<'tcx>) {
assert!(self.term_patch_map[block].is_none());
assert!(!self.term_patch_map.contains_key(&block));
debug!("MirPatch: patch_terminator({:?}, {:?})", block, new);
self.term_patch_map[block] = Some(new);
self.term_patch_map.insert(block, new);
}
/// Queues the insertion of a statement at a given location. The statement
@ -244,6 +248,7 @@ impl<'tcx> MirPatch<'tcx> {
self.new_blocks.len(),
body.basic_blocks.len()
);
debug_assert_eq!(self.next_block, body.basic_blocks.len());
let bbs = if self.term_patch_map.is_empty() && self.new_blocks.is_empty() {
body.basic_blocks.as_mut_preserves_cfg()
} else {
@ -251,11 +256,12 @@ impl<'tcx> MirPatch<'tcx> {
};
bbs.extend(self.new_blocks);
body.local_decls.extend(self.new_locals);
for (src, patch) in self.term_patch_map.into_iter_enumerated() {
if let Some(patch) = patch {
debug!("MirPatch: patching block {:?}", src);
bbs[src].terminator_mut().kind = patch;
}
// The order in which we patch terminators does not change the result.
#[allow(rustc::potential_query_instability)]
for (src, patch) in self.term_patch_map {
debug!("MirPatch: patching block {:?}", src);
bbs[src].terminator_mut().kind = patch;
}
let mut new_statements = self.new_statements;
@ -273,8 +279,8 @@ impl<'tcx> MirPatch<'tcx> {
}
debug!("MirPatch: adding statement {:?} at loc {:?}+{}", stmt, loc, delta);
loc.statement_index += delta;
let source_info = Self::source_info_for_index(&body[loc.block], loc);
body[loc.block]
let source_info = Self::source_info_for_index(&bbs[loc.block], loc);
bbs[loc.block]
.statements
.insert(loc.statement_index, Statement::new(source_info, stmt));
delta += 1;

View file

@ -25,7 +25,7 @@ use crate::delegate::SolverDelegate;
use crate::resolve::eager_resolve_vars;
use crate::solve::{
CanonicalInput, CanonicalResponse, Certainty, ExternalConstraintsData, Goal,
NestedNormalizationGoals, PredefinedOpaquesData, QueryInput, Response, inspect,
NestedNormalizationGoals, QueryInput, Response, inspect,
};
pub mod canonicalizer;
@ -53,7 +53,7 @@ impl<I: Interner, T> ResponseT<I> for inspect::State<I, T> {
pub(super) fn canonicalize_goal<D, I>(
delegate: &D,
goal: Goal<I, I::Predicate>,
opaque_types: Vec<(ty::OpaqueTypeKey<I>, I::Ty)>,
opaque_types: &[(ty::OpaqueTypeKey<I>, I::Ty)],
) -> (Vec<I::GenericArg>, CanonicalInput<I, I::Predicate>)
where
D: SolverDelegate<Interner = I>,
@ -65,9 +65,7 @@ where
&mut orig_values,
QueryInput {
goal,
predefined_opaques_in_body: delegate
.cx()
.mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }),
predefined_opaques_in_body: delegate.cx().mk_predefined_opaques_in_body(opaque_types),
},
);
let query_input = ty::CanonicalQueryInput { canonical, typing_mode: delegate.typing_mode() };

View file

@ -194,7 +194,7 @@ where
D: SolverDelegate<Interner = I>,
I: Interner,
{
#[instrument(level = "debug", skip(self))]
#[instrument(level = "debug", skip(self), ret)]
fn evaluate_root_goal(
&self,
goal: Goal<I, I::Predicate>,
@ -206,6 +206,7 @@ where
})
}
#[instrument(level = "debug", skip(self), ret)]
fn root_goal_may_hold_opaque_types_jank(
&self,
goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
@ -357,7 +358,7 @@ where
f: impl FnOnce(&mut EvalCtxt<'_, D>, Goal<I, I::Predicate>) -> R,
) -> R {
let (ref delegate, input, var_values) = D::build_with_canonical(cx, &canonical_input);
for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
for (key, ty) in input.predefined_opaques_in_body.iter() {
let prev = delegate.register_hidden_type_in_storage(key, ty, I::Span::dummy());
// It may be possible that two entries in the opaque type storage end up
// with the same key after resolving contained inference variables.
@ -467,7 +468,7 @@ where
let opaque_types = self.delegate.clone_opaque_types_lookup_table();
let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types));
let (orig_values, canonical_goal) = canonicalize_goal(self.delegate, goal, opaque_types);
let (orig_values, canonical_goal) = canonicalize_goal(self.delegate, goal, &opaque_types);
let canonical_result = self.search_graph.evaluate_goal(
self.cx(),
canonical_goal,
@ -548,7 +549,6 @@ where
.canonical
.value
.predefined_opaques_in_body
.opaque_types
.len(),
stalled_vars,
sub_roots,
@ -1557,7 +1557,7 @@ pub(super) fn evaluate_root_goal_for_proof_tree<D: SolverDelegate<Interner = I>,
let opaque_types = delegate.clone_opaque_types_lookup_table();
let (goal, opaque_types) = eager_resolve_vars(delegate, (goal, opaque_types));
let (orig_values, canonical_goal) = canonicalize_goal(delegate, goal, opaque_types);
let (orig_values, canonical_goal) = canonicalize_goal(delegate, goal, &opaque_types);
let (canonical_result, final_revision) =
delegate.cx().evaluate_root_goal_for_proof_tree_raw(canonical_goal);

View file

@ -381,6 +381,7 @@ where
}
/// The result of evaluating a goal.
#[derive_where(Debug; I: Interner)]
pub struct GoalEvaluation<I: Interner> {
/// The goal we've evaluated. This is the input goal, but potentially with its
/// inference variables resolved. This never applies any inference constraints

View file

@ -74,20 +74,28 @@ where
}
}
fn is_initial_provisional_result(
cx: Self::Cx,
kind: PathKind,
input: CanonicalInput<I>,
result: QueryResult<I>,
) -> bool {
Self::initial_provisional_result(cx, kind, input) == result
fn is_initial_provisional_result(result: QueryResult<I>) -> Option<PathKind> {
match result {
Ok(response) => {
if has_no_inference_or_external_constraints(response) {
if response.value.certainty == Certainty::Yes {
return Some(PathKind::Coinductive);
} else if response.value.certainty == Certainty::overflow(false) {
return Some(PathKind::Unknown);
}
}
None
}
Err(NoSolution) => Some(PathKind::Inductive),
}
}
fn on_stack_overflow(cx: I, input: CanonicalInput<I>) -> QueryResult<I> {
fn stack_overflow_result(cx: I, input: CanonicalInput<I>) -> QueryResult<I> {
response_no_constraints(cx, input, Certainty::overflow(true))
}
fn on_fixpoint_overflow(cx: I, input: CanonicalInput<I>) -> QueryResult<I> {
fn fixpoint_overflow_result(cx: I, input: CanonicalInput<I>) -> QueryResult<I> {
response_no_constraints(cx, input, Certainty::overflow(false))
}

View file

@ -93,15 +93,6 @@ passes_dead_codes =
}
} never {$participle}
passes_debug_visualizer_invalid =
invalid argument
.note_1 = expected: `natvis_file = "..."`
.note_2 = OR
.note_3 = expected: `gdb_script_file = "..."`
passes_debug_visualizer_placement =
attribute should be applied to a module
passes_debug_visualizer_unreadable =
couldn't read {$file}: {$error}
@ -149,8 +140,17 @@ passes_doc_attribute_not_attribute =
nonexistent builtin attribute `{$attribute}` used in `#[doc(attribute = "...")]`
.help = only existing builtin attributes are allowed in core/std
passes_doc_cfg_hide_takes_list =
`#[doc(cfg_hide(...))]` takes a list of attributes
passes_doc_auto_cfg_expects_hide_or_show =
only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]`
passes_doc_auto_cfg_hide_show_expects_list =
`#![doc(auto_cfg({$attr_name}(...)))]` expects a list of items
passes_doc_auto_cfg_hide_show_unexpected_item =
`#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/value items
passes_doc_auto_cfg_wrong_literal =
expected boolean for `#[doc(auto_cfg = ...)]`
passes_doc_expect_str =
doc {$attr_name} attribute expects a string: #[doc({$attr_name} = "a")]

View file

@ -10,7 +10,7 @@ use std::collections::hash_map::Entry;
use std::slice;
use rustc_abi::{Align, ExternAbi, Size};
use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, ast};
use rustc_ast::{AttrStyle, LitKind, MetaItem, MetaItemInner, MetaItemKind, ast};
use rustc_attr_parsing::{AttributeParser, Late};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey};
@ -256,6 +256,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::MacroEscape( .. )
| AttributeKind::RustcLayoutScalarValidRangeStart(..)
| AttributeKind::RustcLayoutScalarValidRangeEnd(..)
| AttributeKind::RustcSimdMonomorphizeLaneLimit(..)
| AttributeKind::ExportStable
| AttributeKind::FfiConst(..)
| AttributeKind::UnstableFeatureBound(..)
@ -281,6 +282,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::ObjcClass { .. }
| AttributeKind::ObjcSelector { .. }
| AttributeKind::RustcCoherenceIsCore(..)
| AttributeKind::DebuggerVisualizer(..)
) => { /* do nothing */ }
Attribute::Unparsed(attr_item) => {
style = Some(attr_item.style);
@ -301,7 +303,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
&mut doc_aliases,
),
[sym::no_link, ..] => self.check_no_link(hir_id, attr, span, target),
[sym::debugger_visualizer, ..] => self.check_debugger_visualizer(attr, target),
[sym::rustc_no_implicit_autorefs, ..] => {
self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target)
}
@ -1159,16 +1160,59 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}
/// Check that the `#![doc(cfg_hide(...))]` attribute only contains a list of attributes.
///
fn check_doc_cfg_hide(&self, meta: &MetaItemInner, hir_id: HirId) {
if meta.meta_item_list().is_none() {
self.tcx.emit_node_span_lint(
INVALID_DOC_ATTRIBUTES,
hir_id,
meta.span(),
errors::DocCfgHideTakesList,
);
/// Check that the `#![doc(auto_cfg)]` attribute has the expected input.
fn check_doc_auto_cfg(&self, meta: &MetaItem, hir_id: HirId) {
match &meta.kind {
MetaItemKind::Word => {}
MetaItemKind::NameValue(lit) => {
if !matches!(lit.kind, LitKind::Bool(_)) {
self.tcx.emit_node_span_lint(
INVALID_DOC_ATTRIBUTES,
hir_id,
meta.span,
errors::DocAutoCfgWrongLiteral,
);
}
}
MetaItemKind::List(list) => {
for item in list {
let Some(attr_name @ (sym::hide | sym::show)) = item.name() else {
self.tcx.emit_node_span_lint(
INVALID_DOC_ATTRIBUTES,
hir_id,
meta.span,
errors::DocAutoCfgExpectsHideOrShow,
);
continue;
};
if let Some(list) = item.meta_item_list() {
for item in list {
let valid = item.meta_item().is_some_and(|meta| {
meta.path.segments.len() == 1
&& matches!(
&meta.kind,
MetaItemKind::Word | MetaItemKind::NameValue(_)
)
});
if !valid {
self.tcx.emit_node_span_lint(
INVALID_DOC_ATTRIBUTES,
hir_id,
item.span(),
errors::DocAutoCfgHideShowUnexpectedItem { attr_name },
);
}
}
} else {
self.tcx.emit_node_span_lint(
INVALID_DOC_ATTRIBUTES,
hir_id,
meta.span,
errors::DocAutoCfgHideShowExpectsList { attr_name },
);
}
}
}
}
}
@ -1244,10 +1288,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
self.check_attr_crate_level(attr, style, meta, hir_id);
}
Some(sym::cfg_hide) => {
if self.check_attr_crate_level(attr, style, meta, hir_id) {
self.check_doc_cfg_hide(meta, hir_id);
}
Some(sym::auto_cfg) => {
self.check_doc_auto_cfg(i_meta, hir_id);
}
Some(sym::inline | sym::no_inline) => {
@ -1782,20 +1824,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}
/// Checks if the items on the `#[debugger_visualizer]` attribute are valid.
fn check_debugger_visualizer(&self, attr: &Attribute, target: Target) {
// Here we only check that the #[debugger_visualizer] attribute is attached
// to nothing other than a module. All other checks are done in the
// `debugger_visualizer` query where they need to be done for decoding
// anyway.
match target {
Target::Mod => {}
_ => {
self.dcx().emit_err(errors::DebugVisualizerPlacement { span: attr.span() });
}
}
}
/// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
/// (Allows proc_macro functions)
fn check_rustc_allow_const_fn_unstable(

View file

@ -1,67 +1,60 @@
//! Detecting usage of the `#[debugger_visualizer]` attribute.
use rustc_ast::Attribute;
use rustc_ast::ast::NodeId;
use rustc_ast::{HasNodeId, ItemKind, ast};
use rustc_attr_parsing::AttributeParser;
use rustc_expand::base::resolve_path;
use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType};
use rustc_hir::Attribute;
use rustc_hir::attrs::{AttributeKind, DebugVisualizer};
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
use rustc_middle::query::{LocalCrate, Providers};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_span::sym;
use rustc_span::{DUMMY_SP, Span, sym};
use crate::errors::{DebugVisualizerInvalid, DebugVisualizerUnreadable};
use crate::errors::DebugVisualizerUnreadable;
impl DebuggerVisualizerCollector<'_> {
fn check_for_debugger_visualizer(&mut self, attr: &Attribute) {
if attr.has_name(sym::debugger_visualizer) {
let Some(hints) = attr.meta_item_list() else {
self.sess.dcx().emit_err(DebugVisualizerInvalid { span: attr.span });
return;
};
fn check_for_debugger_visualizer(
&mut self,
attrs: &[ast::Attribute],
span: Span,
node_id: NodeId,
) {
if let Some(Attribute::Parsed(AttributeKind::DebuggerVisualizer(visualizers))) =
AttributeParser::parse_limited(
&self.sess,
attrs,
sym::debugger_visualizer,
span,
node_id,
None,
)
{
for DebugVisualizer { span, visualizer_type, path } in visualizers {
let file = match resolve_path(&self.sess, path.as_str(), span) {
Ok(file) => file,
Err(err) => {
err.emit();
return;
}
};
let [hint] = hints.as_slice() else {
self.sess.dcx().emit_err(DebugVisualizerInvalid { span: attr.span });
return;
};
let Some(meta_item) = hint.meta_item() else {
self.sess.dcx().emit_err(DebugVisualizerInvalid { span: attr.span });
return;
};
let (visualizer_type, visualizer_path) = match (meta_item.name(), meta_item.value_str())
{
(Some(sym::natvis_file), Some(value)) => (DebuggerVisualizerType::Natvis, value),
(Some(sym::gdb_script_file), Some(value)) => {
(DebuggerVisualizerType::GdbPrettyPrinter, value)
}
(_, _) => {
self.sess.dcx().emit_err(DebugVisualizerInvalid { span: meta_item.span });
return;
}
};
let file = match resolve_path(&self.sess, visualizer_path.as_str(), attr.span) {
Ok(file) => file,
Err(err) => {
err.emit();
return;
}
};
match self.sess.source_map().load_binary_file(&file) {
Ok((source, _)) => {
self.visualizers.push(DebuggerVisualizerFile::new(
source,
visualizer_type,
file,
));
}
Err(error) => {
self.sess.dcx().emit_err(DebugVisualizerUnreadable {
span: meta_item.span,
file: &file,
error,
});
match self.sess.source_map().load_binary_file(&file) {
Ok((source, _)) => {
self.visualizers.push(DebuggerVisualizerFile::new(
source,
visualizer_type,
file,
));
}
Err(error) => {
self.sess.dcx().emit_err(DebugVisualizerUnreadable {
span,
file: &file,
error,
});
}
}
}
}
@ -74,9 +67,15 @@ struct DebuggerVisualizerCollector<'a> {
}
impl<'ast> rustc_ast::visit::Visitor<'ast> for DebuggerVisualizerCollector<'_> {
fn visit_attribute(&mut self, attr: &'ast Attribute) {
self.check_for_debugger_visualizer(attr);
rustc_ast::visit::walk_attribute(self, attr);
fn visit_item(&mut self, item: &'ast rustc_ast::Item) -> Self::Result {
if let ItemKind::Mod(..) = item.kind {
self.check_for_debugger_visualizer(&item.attrs, item.span, item.node_id());
}
rustc_ast::visit::walk_item(self, item);
}
fn visit_crate(&mut self, krate: &'ast ast::Crate) -> Self::Result {
self.check_for_debugger_visualizer(&krate.attrs, DUMMY_SP, krate.id);
rustc_ast::visit::walk_crate(self, krate);
}
}

View file

@ -309,8 +309,24 @@ pub(crate) struct DocTestLiteral;
pub(crate) struct DocTestTakesList;
#[derive(LintDiagnostic)]
#[diag(passes_doc_cfg_hide_takes_list)]
pub(crate) struct DocCfgHideTakesList;
#[diag(passes_doc_auto_cfg_wrong_literal)]
pub(crate) struct DocAutoCfgWrongLiteral;
#[derive(LintDiagnostic)]
#[diag(passes_doc_auto_cfg_expects_hide_or_show)]
pub(crate) struct DocAutoCfgExpectsHideOrShow;
#[derive(LintDiagnostic)]
#[diag(passes_doc_auto_cfg_hide_show_expects_list)]
pub(crate) struct DocAutoCfgHideShowExpectsList {
pub attr_name: Symbol,
}
#[derive(LintDiagnostic)]
#[diag(passes_doc_auto_cfg_hide_show_unexpected_item)]
pub(crate) struct DocAutoCfgHideShowUnexpectedItem {
pub attr_name: Symbol,
}
#[derive(LintDiagnostic)]
#[diag(passes_doc_test_unknown_any)]
@ -475,23 +491,6 @@ pub(crate) struct MacroOnlyAttribute {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(passes_debug_visualizer_placement)]
pub(crate) struct DebugVisualizerPlacement {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(passes_debug_visualizer_invalid)]
#[note(passes_note_1)]
#[note(passes_note_2)]
#[note(passes_note_3)]
pub(crate) struct DebugVisualizerInvalid {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(passes_debug_visualizer_unreadable)]
pub(crate) struct DebugVisualizerUnreadable<'a> {

View file

@ -45,7 +45,7 @@ use tracing::debug;
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
////////////////////////////////////////////////////////////////////////////////
/// Generic infrastructure used to implement specific visitors below.
// Generic infrastructure used to implement specific visitors below.
////////////////////////////////////////////////////////////////////////////////
struct LazyDefPathStr<'tcx> {
@ -309,10 +309,7 @@ fn min(vis1: ty::Visibility, vis2: ty::Visibility, tcx: TyCtxt<'_>) -> ty::Visib
if vis1.is_at_least(vis2, tcx) { vis2 } else { vis1 }
}
////////////////////////////////////////////////////////////////////////////////
/// Visitor used to determine impl visibility and reachability.
////////////////////////////////////////////////////////////////////////////////
struct FindMin<'a, 'tcx, VL: VisibilityLike, const SHALLOW: bool> {
tcx: TyCtxt<'tcx>,
effective_visibilities: &'a EffectiveVisibilities,
@ -387,10 +384,7 @@ impl VisibilityLike for EffectiveVisibility {
}
}
////////////////////////////////////////////////////////////////////////////////
/// The embargo visitor, used to determine the exports of the AST.
////////////////////////////////////////////////////////////////////////////////
struct EmbargoVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
@ -849,9 +843,7 @@ impl<'tcx> DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx>
}
}
////////////////////////////////////////////////////////////////////////////////
/// Visitor, used for EffectiveVisibilities table checking
////////////////////////////////////////////////////////////////////////////////
pub struct TestReachabilityVisitor<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
effective_visibilities: &'a EffectiveVisibilities,
@ -909,13 +901,11 @@ impl<'a, 'tcx> TestReachabilityVisitor<'a, 'tcx> {
}
}
//////////////////////////////////////////////////////////////////////////////////////
/// Name privacy visitor, checks privacy and reports violations.
///
/// Most of name privacy checks are performed during the main resolution phase,
/// or later in type checking when field accesses and associated items are resolved.
/// This pass performs remaining checks for fields in struct expressions and patterns.
//////////////////////////////////////////////////////////////////////////////////////
struct NamePrivacyVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>,
@ -1120,12 +1110,10 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> {
}
}
////////////////////////////////////////////////////////////////////////////////////////////
/// Type privacy visitor, checks types for privacy and reports violations.
///
/// Both explicitly written types and inferred types of expressions and patterns are checked.
/// Checks are performed on "semantic" types regardless of names and their hygiene.
////////////////////////////////////////////////////////////////////////////////////////////
struct TypePrivacyVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
module_def_id: LocalModDefId,
@ -1345,13 +1333,11 @@ impl<'tcx> DefIdVisitor<'tcx> for TypePrivacyVisitor<'tcx> {
}
}
///////////////////////////////////////////////////////////////////////////////
/// SearchInterfaceForPrivateItemsVisitor traverses an item's interface and
/// finds any private components in it.
///
/// PrivateItemsInPublicInterfacesVisitor ensures there are no private types
/// and traits in public interfaces.
///////////////////////////////////////////////////////////////////////////////
struct SearchInterfaceForPrivateItemsVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
item_def_id: LocalDefId,

View file

@ -5,7 +5,6 @@
//! unexpanded macros in the fragment are visited and registered.
//! Imports are also considered items and placed into modules here, but not resolved yet.
use std::cell::Cell;
use std::sync::Arc;
use rustc_ast::visit::{self, AssocCtxt, Visitor, WalkItemKind};
@ -35,6 +34,7 @@ use crate::Namespace::{MacroNS, TypeNS, ValueNS};
use crate::def_collector::collect_definitions;
use crate::imports::{ImportData, ImportKind};
use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
use crate::ref_mut::CmCell;
use crate::{
BindingKey, ExternPreludeEntry, Finalize, MacroData, Module, ModuleKind, ModuleOrUniformRoot,
NameBinding, ParentScope, PathResult, ResolutionError, Resolver, Segment, Used,
@ -87,7 +87,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// because they can be fetched by glob imports from those modules, and bring traits
// into scope both directly and through glob imports.
let key = BindingKey::new_disambiguated(ident, ns, || {
parent.underscore_disambiguator.update(|d| d + 1);
// FIXME(batched): Will be fixed in batched resolution.
parent.underscore_disambiguator.update_unchecked(|d| d + 1);
parent.underscore_disambiguator.get()
});
if self
@ -477,7 +478,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
kind,
parent_scope: self.parent_scope,
module_path,
imported_module: Cell::new(None),
imported_module: CmCell::new(None),
span,
use_span: item.span,
use_span_with_attributes: item.span_with_attributes(),
@ -505,7 +506,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
});
}
}
ImportKind::Glob { .. } => current_module.globs.borrow_mut().push(import),
ImportKind::Glob { .. } => current_module.globs.borrow_mut(self.r).push(import),
_ => unreachable!(),
}
}
@ -668,7 +669,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
}
ast::UseTreeKind::Glob => {
if !ast::attr::contains_name(&item.attrs, sym::prelude_import) {
let kind = ImportKind::Glob { max_vis: Cell::new(None), id };
let kind = ImportKind::Glob { max_vis: CmCell::new(None), id };
self.add_import(prefix, kind, use_tree.span, item, root_span, item.id, vis);
} else {
// Resolve the prelude import early.
@ -971,7 +972,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
kind: ImportKind::ExternCrate { source: orig_name, target: ident, id: item.id },
root_id: item.id,
parent_scope: self.parent_scope,
imported_module: Cell::new(module),
imported_module: CmCell::new(module),
has_attributes: !item.attrs.is_empty(),
use_span_with_attributes: item.span_with_attributes(),
use_span: item.span,
@ -1103,7 +1104,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
kind: ImportKind::MacroUse { warn_private },
root_id: item.id,
parent_scope: this.parent_scope,
imported_module: Cell::new(Some(ModuleOrUniformRoot::Module(module))),
imported_module: CmCell::new(Some(ModuleOrUniformRoot::Module(module))),
use_span_with_attributes: item.span_with_attributes(),
has_attributes: !item.attrs.is_empty(),
use_span: item.span,
@ -1196,7 +1197,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
/// directly into its parent scope's module.
fn visit_invoc_in_module(&mut self, id: NodeId) -> MacroRulesScopeRef<'ra> {
let invoc_id = self.visit_invoc(id);
self.parent_scope.module.unexpanded_invocations.borrow_mut().insert(invoc_id);
self.parent_scope.module.unexpanded_invocations.borrow_mut(self.r).insert(invoc_id);
self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Invocation(invoc_id))
}
@ -1274,7 +1275,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
kind: ImportKind::MacroExport,
root_id: item.id,
parent_scope: self.parent_scope,
imported_module: Cell::new(None),
imported_module: CmCell::new(None),
has_attributes: false,
use_span_with_attributes: span,
use_span: span,

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