commit
fb464108ac
573 changed files with 7380 additions and 3030 deletions
19
Cargo.lock
19
Cargo.lock
|
|
@ -441,20 +441,6 @@ dependencies = [
|
|||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_metadata"
|
||||
version = "0.19.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"cargo-platform 0.1.9",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_metadata"
|
||||
version = "0.21.0"
|
||||
|
|
@ -1364,7 +1350,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"askama",
|
||||
"cargo_metadata 0.18.1",
|
||||
"cargo_metadata 0.21.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 1.0.69",
|
||||
|
|
@ -4255,6 +4241,7 @@ dependencies = [
|
|||
"rustc-literal-escaper",
|
||||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_attr_parsing",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_feature",
|
||||
|
|
@ -5369,7 +5356,7 @@ name = "tidy"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"build_helper",
|
||||
"cargo_metadata 0.19.2",
|
||||
"cargo_metadata 0.21.0",
|
||||
"fluent-syntax",
|
||||
"ignore",
|
||||
"miropt-test-tools",
|
||||
|
|
|
|||
|
|
@ -2849,7 +2849,7 @@ impl InlineAsmOperand {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
|
||||
#[derive(Clone, Copy, Encodable, Decodable, Debug, HashStable_Generic, Walkable, PartialEq, Eq)]
|
||||
pub enum AsmMacro {
|
||||
/// The `asm!` macro
|
||||
Asm,
|
||||
|
|
|
|||
|
|
@ -416,12 +416,24 @@ pub enum AttributeKind {
|
|||
/// Represents `#[pointee]`
|
||||
Pointee(Span),
|
||||
|
||||
/// Represents `#[proc_macro]`
|
||||
ProcMacro(Span),
|
||||
|
||||
/// Represents `#[proc_macro_attribute]`
|
||||
ProcMacroAttribute(Span),
|
||||
|
||||
/// Represents `#[proc_macro_derive]`
|
||||
ProcMacroDerive { trait_name: Symbol, helper_attrs: ThinVec<Symbol>, span: Span },
|
||||
|
||||
/// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint).
|
||||
PubTransparent(Span),
|
||||
|
||||
/// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
|
||||
Repr { reprs: ThinVec<(ReprAttr, Span)>, first_span: Span },
|
||||
|
||||
/// Represents `#[rustc_builtin_macro]`.
|
||||
RustcBuiltinMacro { builtin_name: Option<Symbol>, helper_attrs: ThinVec<Symbol>, span: Span },
|
||||
|
||||
/// Represents `#[rustc_layout_scalar_valid_range_end]`.
|
||||
RustcLayoutScalarValidRangeEnd(Box<u128>, Span),
|
||||
|
||||
|
|
|
|||
|
|
@ -61,8 +61,12 @@ impl AttributeKind {
|
|||
PassByValue(..) => Yes,
|
||||
Path(..) => No,
|
||||
Pointee(..) => No,
|
||||
ProcMacro(..) => No,
|
||||
ProcMacroAttribute(..) => No,
|
||||
ProcMacroDerive { .. } => No,
|
||||
PubTransparent(..) => Yes,
|
||||
Repr { .. } => No,
|
||||
RustcBuiltinMacro { .. } => Yes,
|
||||
RustcLayoutScalarValidRangeEnd(..) => Yes,
|
||||
RustcLayoutScalarValidRangeStart(..) => Yes,
|
||||
RustcObjectLifetimeDefault => No,
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ pub(crate) mod must_use;
|
|||
pub(crate) mod no_implicit_prelude;
|
||||
pub(crate) mod non_exhaustive;
|
||||
pub(crate) mod path;
|
||||
pub(crate) mod proc_macro_attrs;
|
||||
pub(crate) mod repr;
|
||||
pub(crate) mod rustc_internal;
|
||||
pub(crate) mod semantics;
|
||||
|
|
|
|||
139
compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs
Normal file
139
compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use crate::attributes::{
|
||||
AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
|
||||
};
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
|
||||
pub(crate) struct ProcMacroParser;
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for ProcMacroParser {
|
||||
const PATH: &[Symbol] = &[sym::proc_macro];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::ProcMacro;
|
||||
}
|
||||
|
||||
pub(crate) struct ProcMacroAttributeParser;
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for ProcMacroAttributeParser {
|
||||
const PATH: &[Symbol] = &[sym::proc_macro_attribute];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::ProcMacroAttribute;
|
||||
}
|
||||
|
||||
pub(crate) struct ProcMacroDeriveParser;
|
||||
impl<S: Stage> SingleAttributeParser<S> for ProcMacroDeriveParser {
|
||||
const PATH: &[Symbol] = &[sym::proc_macro_derive];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const TEMPLATE: AttributeTemplate =
|
||||
template!(List: "TraitName, /*opt*/ attributes(name1, name2, ...)");
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let (trait_name, helper_attrs) = parse_derive_like(cx, args, true)?;
|
||||
Some(AttributeKind::ProcMacroDerive {
|
||||
trait_name: trait_name.expect("Trait name is mandatory, so it is present"),
|
||||
helper_attrs,
|
||||
span: cx.attr_span,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RustcBuiltinMacroParser;
|
||||
impl<S: Stage> SingleAttributeParser<S> for RustcBuiltinMacroParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_builtin_macro];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const TEMPLATE: AttributeTemplate =
|
||||
template!(List: "TraitName, /*opt*/ attributes(name1, name2, ...)");
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let (builtin_name, helper_attrs) = parse_derive_like(cx, args, false)?;
|
||||
Some(AttributeKind::RustcBuiltinMacro { builtin_name, helper_attrs, span: cx.attr_span })
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_derive_like<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser<'_>,
|
||||
trait_name_mandatory: bool,
|
||||
) -> Option<(Option<Symbol>, ThinVec<Symbol>)> {
|
||||
let Some(list) = args.list() else {
|
||||
// For #[rustc_builtin_macro], it is permitted to leave out the trait name
|
||||
if args.no_args().is_ok() && !trait_name_mandatory {
|
||||
return Some((None, ThinVec::new()));
|
||||
}
|
||||
cx.expected_list(cx.attr_span);
|
||||
return None;
|
||||
};
|
||||
let mut items = list.mixed();
|
||||
|
||||
// Parse the name of the trait that is derived.
|
||||
let Some(trait_attr) = items.next() else {
|
||||
cx.expected_at_least_one_argument(list.span);
|
||||
return None;
|
||||
};
|
||||
let Some(trait_attr) = trait_attr.meta_item() else {
|
||||
cx.unexpected_literal(trait_attr.span());
|
||||
return None;
|
||||
};
|
||||
let Some(trait_ident) = trait_attr.path().word() else {
|
||||
cx.expected_identifier(trait_attr.path().span());
|
||||
return None;
|
||||
};
|
||||
if !trait_ident.name.can_be_raw() {
|
||||
cx.expected_identifier(trait_ident.span);
|
||||
return None;
|
||||
}
|
||||
if let Err(e) = trait_attr.args().no_args() {
|
||||
cx.expected_no_args(e);
|
||||
return None;
|
||||
};
|
||||
|
||||
// Parse optional attributes
|
||||
let mut attributes = ThinVec::new();
|
||||
if let Some(attrs) = items.next() {
|
||||
let Some(attr_list) = attrs.meta_item() else {
|
||||
cx.expected_list(attrs.span());
|
||||
return None;
|
||||
};
|
||||
if !attr_list.path().word_is(sym::attributes) {
|
||||
cx.expected_specific_argument(attrs.span(), vec!["attributes"]);
|
||||
return None;
|
||||
}
|
||||
let Some(attr_list) = attr_list.args().list() else {
|
||||
cx.expected_list(attrs.span());
|
||||
return None;
|
||||
};
|
||||
|
||||
// Parse item in `attributes(...)` argument
|
||||
for attr in attr_list.mixed() {
|
||||
let Some(attr) = attr.meta_item() else {
|
||||
cx.expected_identifier(attr.span());
|
||||
return None;
|
||||
};
|
||||
if let Err(e) = attr.args().no_args() {
|
||||
cx.expected_no_args(e);
|
||||
return None;
|
||||
};
|
||||
let Some(ident) = attr.path().word() else {
|
||||
cx.expected_identifier(attr.path().span());
|
||||
return None;
|
||||
};
|
||||
if !ident.name.can_be_raw() {
|
||||
cx.expected_identifier(ident.span);
|
||||
return None;
|
||||
}
|
||||
attributes.push(ident.name);
|
||||
}
|
||||
}
|
||||
|
||||
// If anything else is specified, we should reject it
|
||||
if let Some(next) = items.next() {
|
||||
cx.expected_no_args(next.span());
|
||||
}
|
||||
|
||||
Some((Some(trait_ident.name), attributes))
|
||||
}
|
||||
|
|
@ -38,6 +38,9 @@ use crate::attributes::must_use::MustUseParser;
|
|||
use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser;
|
||||
use crate::attributes::non_exhaustive::NonExhaustiveParser;
|
||||
use crate::attributes::path::PathParser as PathAttributeParser;
|
||||
use crate::attributes::proc_macro_attrs::{
|
||||
ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser,
|
||||
};
|
||||
use crate::attributes::repr::{AlignParser, ReprParser};
|
||||
use crate::attributes::rustc_internal::{
|
||||
RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart,
|
||||
|
|
@ -154,6 +157,8 @@ attribute_parsers!(
|
|||
Single<MustUseParser>,
|
||||
Single<OptimizeParser>,
|
||||
Single<PathAttributeParser>,
|
||||
Single<ProcMacroDeriveParser>,
|
||||
Single<RustcBuiltinMacroParser>,
|
||||
Single<RustcForceInlineParser>,
|
||||
Single<RustcLayoutScalarValidRangeEnd>,
|
||||
Single<RustcLayoutScalarValidRangeStart>,
|
||||
|
|
@ -186,6 +191,8 @@ attribute_parsers!(
|
|||
Single<WithoutArgs<ParenSugarParser>>,
|
||||
Single<WithoutArgs<PassByValueParser>>,
|
||||
Single<WithoutArgs<PointeeParser>>,
|
||||
Single<WithoutArgs<ProcMacroAttributeParser>>,
|
||||
Single<WithoutArgs<ProcMacroParser>>,
|
||||
Single<WithoutArgs<PubTransparentParser>>,
|
||||
Single<WithoutArgs<SpecializationTraitParser>>,
|
||||
Single<WithoutArgs<StdInternalSymbolParser>>,
|
||||
|
|
|
|||
|
|
@ -1290,6 +1290,58 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
span,
|
||||
format!("if `{ty}` implemented `Clone`, you could clone the value"),
|
||||
);
|
||||
} else if let ty::Adt(_, _) = ty.kind()
|
||||
&& let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
|
||||
{
|
||||
// For cases like `Option<NonClone>`, where `Option<T>: Clone` if `T: Clone`, we point
|
||||
// at the types that should be `Clone`.
|
||||
let ocx = ObligationCtxt::new_with_diagnostics(self.infcx);
|
||||
let cause = ObligationCause::misc(expr.span, self.mir_def_id());
|
||||
ocx.register_bound(cause, self.infcx.param_env, ty, clone_trait);
|
||||
let errors = ocx.select_all_or_error();
|
||||
if errors.iter().all(|error| {
|
||||
match error.obligation.predicate.as_clause().and_then(|c| c.as_trait_clause()) {
|
||||
Some(clause) => match clause.self_ty().skip_binder().kind() {
|
||||
ty::Adt(def, _) => def.did().is_local() && clause.def_id() == clone_trait,
|
||||
_ => false,
|
||||
},
|
||||
None => false,
|
||||
}
|
||||
}) {
|
||||
let mut type_spans = vec![];
|
||||
let mut types = FxIndexSet::default();
|
||||
for clause in errors
|
||||
.iter()
|
||||
.filter_map(|e| e.obligation.predicate.as_clause())
|
||||
.filter_map(|c| c.as_trait_clause())
|
||||
{
|
||||
let ty::Adt(def, _) = clause.self_ty().skip_binder().kind() else { continue };
|
||||
type_spans.push(self.infcx.tcx.def_span(def.did()));
|
||||
types.insert(
|
||||
self.infcx
|
||||
.tcx
|
||||
.short_string(clause.self_ty().skip_binder(), &mut err.long_ty_path()),
|
||||
);
|
||||
}
|
||||
let mut span: MultiSpan = type_spans.clone().into();
|
||||
for sp in type_spans {
|
||||
span.push_span_label(sp, "consider implementing `Clone` for this type");
|
||||
}
|
||||
span.push_span_label(expr.span, "you could clone this value");
|
||||
let types: Vec<_> = types.into_iter().collect();
|
||||
let msg = match &types[..] {
|
||||
[only] => format!("`{only}`"),
|
||||
[head @ .., last] => format!(
|
||||
"{} and `{last}`",
|
||||
head.iter().map(|t| format!("`{t}`")).collect::<Vec<_>>().join(", ")
|
||||
),
|
||||
[] => unreachable!(),
|
||||
};
|
||||
err.span_note(
|
||||
span,
|
||||
format!("if {msg} implemented `Clone`, you could clone the value"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
use std::mem;
|
||||
use std::{mem, slice};
|
||||
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::visit::{self, Visitor};
|
||||
use rustc_ast::{self as ast, NodeId, attr};
|
||||
use rustc_ast::{self as ast, HasNodeId, NodeId, attr};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_attr_parsing::AttributeParser;
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_expand::base::{ExtCtxt, ResolverExpand, parse_macro_name_and_helper_attrs};
|
||||
use rustc_expand::base::{ExtCtxt, ResolverExpand};
|
||||
use rustc_expand::expand::{AstFragment, ExpansionConfig};
|
||||
use rustc_feature::Features;
|
||||
use rustc_session::Session;
|
||||
|
|
@ -22,7 +24,7 @@ struct ProcMacroDerive {
|
|||
trait_name: Symbol,
|
||||
function_ident: Ident,
|
||||
span: Span,
|
||||
attrs: Vec<Symbol>,
|
||||
attrs: ThinVec<Symbol>,
|
||||
}
|
||||
|
||||
struct ProcMacroDef {
|
||||
|
|
@ -41,6 +43,7 @@ struct CollectProcMacros<'a> {
|
|||
macros: Vec<ProcMacro>,
|
||||
in_root: bool,
|
||||
dcx: DiagCtxtHandle<'a>,
|
||||
session: &'a Session,
|
||||
source_map: &'a SourceMap,
|
||||
is_proc_macro_crate: bool,
|
||||
is_test_crate: bool,
|
||||
|
|
@ -63,6 +66,7 @@ pub fn inject(
|
|||
macros: Vec::new(),
|
||||
in_root: true,
|
||||
dcx,
|
||||
session: sess,
|
||||
source_map: sess.source_map(),
|
||||
is_proc_macro_crate,
|
||||
is_test_crate,
|
||||
|
|
@ -98,8 +102,18 @@ impl<'a> CollectProcMacros<'a> {
|
|||
function_ident: Ident,
|
||||
attr: &'a ast::Attribute,
|
||||
) {
|
||||
let Some((trait_name, proc_attrs)) =
|
||||
parse_macro_name_and_helper_attrs(self.dcx, attr, "derive")
|
||||
let Some(rustc_hir::Attribute::Parsed(AttributeKind::ProcMacroDerive {
|
||||
trait_name,
|
||||
helper_attrs,
|
||||
..
|
||||
})) = AttributeParser::parse_limited(
|
||||
self.session,
|
||||
slice::from_ref(attr),
|
||||
sym::proc_macro_derive,
|
||||
item.span,
|
||||
item.node_id(),
|
||||
None,
|
||||
)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
|
@ -110,7 +124,7 @@ impl<'a> CollectProcMacros<'a> {
|
|||
span: item.span,
|
||||
trait_name,
|
||||
function_ident,
|
||||
attrs: proc_attrs,
|
||||
attrs: helper_attrs,
|
||||
}));
|
||||
} else {
|
||||
let msg = if !self.in_root {
|
||||
|
|
|
|||
|
|
@ -39,10 +39,7 @@ impl Coords {
|
|||
/// or other expansions), and if it does happen then skipping a span or function is
|
||||
/// better than an ICE or `llvm-cov` failure that the user might have no way to avoid.
|
||||
pub(crate) fn make_coords(source_map: &SourceMap, file: &SourceFile, span: Span) -> Option<Coords> {
|
||||
if span.is_empty() {
|
||||
debug_assert!(false, "can't make coords from empty span: {span:?}");
|
||||
return None;
|
||||
}
|
||||
let span = ensure_non_empty_span(source_map, span)?;
|
||||
|
||||
let lo = span.lo();
|
||||
let hi = span.hi();
|
||||
|
|
@ -73,6 +70,29 @@ pub(crate) fn make_coords(source_map: &SourceMap, file: &SourceFile, span: Span)
|
|||
})
|
||||
}
|
||||
|
||||
fn ensure_non_empty_span(source_map: &SourceMap, span: Span) -> Option<Span> {
|
||||
if !span.is_empty() {
|
||||
return Some(span);
|
||||
}
|
||||
|
||||
// The span is empty, so try to enlarge it to cover an adjacent '{' or '}'.
|
||||
source_map
|
||||
.span_to_source(span, |src, start, end| try {
|
||||
// Adjusting span endpoints by `BytePos(1)` is normally a bug,
|
||||
// but in this case we have specifically checked that the character
|
||||
// we're skipping over is one of two specific ASCII characters, so
|
||||
// adjusting by exactly 1 byte is correct.
|
||||
if src.as_bytes().get(end).copied() == Some(b'{') {
|
||||
Some(span.with_hi(span.hi() + BytePos(1)))
|
||||
} else if start > 0 && src.as_bytes()[start - 1] == b'}' {
|
||||
Some(span.with_lo(span.lo() - BytePos(1)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.ok()?
|
||||
}
|
||||
|
||||
/// If `llvm-cov` sees a source region that is improperly ordered (end < start),
|
||||
/// it will immediately exit with a fatal error. To prevent that from happening,
|
||||
/// discard regions that are improperly ordered, or might be interpreted in a
|
||||
|
|
|
|||
|
|
@ -382,26 +382,16 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
|
|||
let width = size.bits();
|
||||
let llty = self.type_ix(width);
|
||||
match name {
|
||||
sym::ctlz | sym::cttz => {
|
||||
let y = self.const_bool(false);
|
||||
let ret = self.call_intrinsic(
|
||||
format!("llvm.{name}"),
|
||||
&[llty],
|
||||
&[args[0].immediate(), y],
|
||||
);
|
||||
|
||||
self.intcast(ret, result.layout.llvm_type(self), false)
|
||||
}
|
||||
sym::ctlz_nonzero => {
|
||||
let y = self.const_bool(true);
|
||||
sym::ctlz | sym::ctlz_nonzero | sym::cttz | sym::cttz_nonzero => {
|
||||
let y =
|
||||
self.const_bool(name == sym::ctlz_nonzero || name == sym::cttz_nonzero);
|
||||
let llvm_name = if name == sym::ctlz || name == sym::ctlz_nonzero {
|
||||
"llvm.ctlz"
|
||||
} else {
|
||||
"llvm.cttz"
|
||||
};
|
||||
let ret =
|
||||
self.call_intrinsic("llvm.ctlz", &[llty], &[args[0].immediate(), y]);
|
||||
self.intcast(ret, result.layout.llvm_type(self), false)
|
||||
}
|
||||
sym::cttz_nonzero => {
|
||||
let y = self.const_bool(true);
|
||||
let ret =
|
||||
self.call_intrinsic("llvm.cttz", &[llty], &[args[0].immediate(), y]);
|
||||
self.call_intrinsic(llvm_name, &[llty], &[args[0].immediate(), y]);
|
||||
self.intcast(ret, result.layout.llvm_type(self), false)
|
||||
}
|
||||
sym::ctpop => {
|
||||
|
|
|
|||
|
|
@ -405,6 +405,8 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) {
|
|||
("mips64" | "mips64r6", _) => false,
|
||||
// Selection bug <https://github.com/llvm/llvm-project/issues/95471>
|
||||
("nvptx64", _) => false,
|
||||
// Unsupported https://github.com/llvm/llvm-project/issues/121122
|
||||
("amdgpu", _) => false,
|
||||
// ABI bugs <https://github.com/rust-lang/rust/issues/125109> et al. (full
|
||||
// list at <https://github.com/rust-lang/rust/issues/116909>)
|
||||
("powerpc" | "powerpc64", _) => false,
|
||||
|
|
@ -433,6 +435,9 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) {
|
|||
// This rules out anything that doesn't have `long double` = `binary128`; <= 32 bits
|
||||
// (ld is `f64`), anything other than Linux (Windows and MacOS use `f64`), and `x86`
|
||||
// (ld is 80-bit extended precision).
|
||||
//
|
||||
// musl does not implement the symbols required for f128 math at all.
|
||||
_ if target_env == "musl" => false,
|
||||
("x86_64", _) => false,
|
||||
(_, "linux") if target_pointer_width == 64 => true,
|
||||
_ => false,
|
||||
|
|
|
|||
|
|
@ -511,15 +511,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
err.emit();
|
||||
}
|
||||
|
||||
// Any linkage to LLVM intrinsics for now forcibly marks them all as never
|
||||
// unwinds since LLVM sometimes can't handle codegen which `invoke`s
|
||||
// intrinsic functions.
|
||||
if let Some(name) = &codegen_fn_attrs.link_name
|
||||
&& name.as_str().starts_with("llvm.")
|
||||
{
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
|
||||
}
|
||||
|
||||
if let Some(features) = check_tied_features(
|
||||
tcx.sess,
|
||||
&codegen_fn_attrs
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
//! An analysis to determine which locals require allocas and
|
||||
//! which do not.
|
||||
|
||||
use rustc_abi as abi;
|
||||
use rustc_data_structures::graph::dominators::Dominators;
|
||||
use rustc_index::bit_set::DenseBitSet;
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
|
||||
use rustc_middle::mir::{self, DefLocation, Location, TerminatorKind, traversal};
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use tracing::debug;
|
||||
|
||||
|
|
@ -99,63 +100,75 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> LocalAnalyzer<'a, 'b, 'tcx, Bx>
|
|||
context: PlaceContext,
|
||||
location: Location,
|
||||
) {
|
||||
let cx = self.fx.cx;
|
||||
if !place_ref.projection.is_empty() {
|
||||
const COPY_CONTEXT: PlaceContext =
|
||||
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy);
|
||||
|
||||
if let Some((place_base, elem)) = place_ref.last_projection() {
|
||||
let mut base_context = if context.is_mutating_use() {
|
||||
PlaceContext::MutatingUse(MutatingUseContext::Projection)
|
||||
} else {
|
||||
PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
|
||||
};
|
||||
|
||||
// Allow uses of projections that are ZSTs or from scalar fields.
|
||||
let is_consume = matches!(
|
||||
context,
|
||||
PlaceContext::NonMutatingUse(
|
||||
NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
|
||||
)
|
||||
);
|
||||
if is_consume {
|
||||
let base_ty = place_base.ty(self.fx.mir, cx.tcx());
|
||||
let base_ty = self.fx.monomorphize(base_ty);
|
||||
|
||||
// ZSTs don't require any actual memory access.
|
||||
let elem_ty = base_ty.projection_ty(cx.tcx(), self.fx.monomorphize(elem)).ty;
|
||||
let span = self.fx.mir.local_decls[place_ref.local].source_info.span;
|
||||
if cx.spanned_layout_of(elem_ty, span).is_zst() {
|
||||
return;
|
||||
// `PlaceElem::Index` is the only variant that can mention other `Local`s,
|
||||
// so check for those up-front before any potential short-circuits.
|
||||
for elem in place_ref.projection {
|
||||
if let mir::PlaceElem::Index(index_local) = *elem {
|
||||
self.visit_local(index_local, COPY_CONTEXT, location);
|
||||
}
|
||||
}
|
||||
|
||||
if let mir::ProjectionElem::Field(..) = elem {
|
||||
let layout = cx.spanned_layout_of(base_ty.ty, span);
|
||||
if cx.is_backend_immediate(layout) || cx.is_backend_scalar_pair(layout) {
|
||||
// Recurse with the same context, instead of `Projection`,
|
||||
// potentially stopping at non-operand projections,
|
||||
// which would trigger `not_ssa` on locals.
|
||||
base_context = context;
|
||||
// If our local is already memory, nothing can make it *more* memory
|
||||
// so we don't need to bother checking the projections further.
|
||||
if self.locals[place_ref.local] == LocalKind::Memory {
|
||||
return;
|
||||
}
|
||||
|
||||
if place_ref.is_indirect_first_projection() {
|
||||
// If this starts with a `Deref`, we only need to record a read of the
|
||||
// pointer being dereferenced, as all the subsequent projections are
|
||||
// working on a place which is always supported. (And because we're
|
||||
// looking at codegen MIR, it can only happen as the first projection.)
|
||||
self.visit_local(place_ref.local, COPY_CONTEXT, location);
|
||||
return;
|
||||
}
|
||||
|
||||
if context.is_mutating_use() {
|
||||
// If it's a mutating use it doesn't matter what the projections are,
|
||||
// if there are *any* then we need a place to write. (For example,
|
||||
// `_1 = Foo()` works in SSA but `_2.0 = Foo()` does not.)
|
||||
let mut_projection = PlaceContext::MutatingUse(MutatingUseContext::Projection);
|
||||
self.visit_local(place_ref.local, mut_projection, location);
|
||||
return;
|
||||
}
|
||||
|
||||
// Scan through to ensure the only projections are those which
|
||||
// `FunctionCx::maybe_codegen_consume_direct` can handle.
|
||||
let base_ty = self.fx.monomorphized_place_ty(mir::PlaceRef::from(place_ref.local));
|
||||
let mut layout = self.fx.cx.layout_of(base_ty);
|
||||
for elem in place_ref.projection {
|
||||
layout = match *elem {
|
||||
mir::PlaceElem::Field(fidx, ..) => layout.field(self.fx.cx, fidx.as_usize()),
|
||||
mir::PlaceElem::Downcast(_, vidx)
|
||||
if let abi::Variants::Single { index: single_variant } =
|
||||
layout.variants
|
||||
&& vidx == single_variant =>
|
||||
{
|
||||
layout.for_variant(self.fx.cx, vidx)
|
||||
}
|
||||
mir::PlaceElem::Subtype(subtype_ty) => {
|
||||
let subtype_ty = self.fx.monomorphize(subtype_ty);
|
||||
self.fx.cx.layout_of(subtype_ty)
|
||||
}
|
||||
_ => {
|
||||
self.locals[place_ref.local] = LocalKind::Memory;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let mir::ProjectionElem::Deref = elem {
|
||||
// Deref projections typically only read the pointer.
|
||||
base_context = PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy);
|
||||
}
|
||||
|
||||
self.process_place(&place_base, base_context, location);
|
||||
// HACK(eddyb) this emulates the old `visit_projection_elem`, this
|
||||
// entire `visit_place`-like `process_place` method should be rewritten,
|
||||
// now that we have moved to the "slice of projections" representation.
|
||||
if let mir::ProjectionElem::Index(local) = elem {
|
||||
self.visit_local(
|
||||
local,
|
||||
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
|
||||
location,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
self.visit_local(place_ref.local, context, location);
|
||||
debug_assert!(
|
||||
!self.fx.cx.is_backend_ref(layout),
|
||||
"Post-projection {place_ref:?} layout should be non-Ref, but it's {layout:?}",
|
||||
);
|
||||
}
|
||||
|
||||
// Even with supported projections, we still need to have `visit_local`
|
||||
// check for things that can't be done in SSA (like `SharedBorrow`).
|
||||
self.visit_local(place_ref.local, context, location);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -170,11 +183,6 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer
|
|||
|
||||
if let Some(local) = place.as_local() {
|
||||
self.define(local, DefLocation::Assignment(location));
|
||||
if self.locals[local] != LocalKind::Memory {
|
||||
if !self.fx.rvalue_creates_operand(rvalue) {
|
||||
self.locals[local] = LocalKind::Memory;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.visit_place(place, PlaceContext::MutatingUse(MutatingUseContext::Store), location);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -335,13 +335,6 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
|
|||
|
||||
let val = if field.is_zst() {
|
||||
OperandValue::ZeroSized
|
||||
} else if let BackendRepr::SimdVector { .. } = self.layout.backend_repr {
|
||||
// codegen_transmute_operand doesn't support SIMD, but since the previous
|
||||
// check handled ZSTs, the only possible field access into something SIMD
|
||||
// is to the `non_1zst_field` that's the same SIMD. (Other things, even
|
||||
// just padding, would change the wrapper's representation type.)
|
||||
assert_eq!(field.size, self.layout.size);
|
||||
self.val
|
||||
} else if field.size == self.layout.size {
|
||||
assert_eq!(offset.bytes(), 0);
|
||||
fx.codegen_transmute_operand(bx, *self, field)
|
||||
|
|
@ -928,9 +921,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
|
||||
match self.locals[place_ref.local] {
|
||||
LocalRef::Operand(mut o) => {
|
||||
// Moves out of scalar and scalar pair fields are trivial.
|
||||
for elem in place_ref.projection.iter() {
|
||||
match elem {
|
||||
// We only need to handle the projections that
|
||||
// `LocalAnalyzer::process_place` let make it here.
|
||||
for elem in place_ref.projection {
|
||||
match *elem {
|
||||
mir::ProjectionElem::Field(f, _) => {
|
||||
assert!(
|
||||
!o.layout.ty.is_any_ptr(),
|
||||
|
|
@ -939,17 +933,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
);
|
||||
o = o.extract_field(self, bx, f.index());
|
||||
}
|
||||
mir::ProjectionElem::Index(_)
|
||||
| mir::ProjectionElem::ConstantIndex { .. } => {
|
||||
// ZSTs don't require any actual memory access.
|
||||
// FIXME(eddyb) deduplicate this with the identical
|
||||
// checks in `codegen_consume` and `extract_field`.
|
||||
let elem = o.layout.field(bx.cx(), 0);
|
||||
if elem.is_zst() {
|
||||
o = OperandRef::zero_sized(elem);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
mir::PlaceElem::Downcast(_, vidx) => {
|
||||
debug_assert_eq!(
|
||||
o.layout.variants,
|
||||
abi::Variants::Single { index: vidx },
|
||||
);
|
||||
let layout = o.layout.for_variant(bx.cx(), vidx);
|
||||
o = OperandRef { val: o.val, layout }
|
||||
}
|
||||
mir::PlaceElem::Subtype(subtype_ty) => {
|
||||
let subtype_ty = self.monomorphize(subtype_ty);
|
||||
let layout = self.cx.layout_of(subtype_ty);
|
||||
o = OperandRef { val: o.val, layout }
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ use rustc_abi::{self as abi, FIRST_VARIANT};
|
|||
use rustc_middle::ty::adjustment::PointerCoercion;
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
||||
use rustc_middle::{bug, mir};
|
||||
use rustc_middle::{bug, mir, span_bug};
|
||||
use rustc_session::config::OptLevel;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use super::operand::{OperandRef, OperandRefBuilder, OperandValue};
|
||||
use super::place::{PlaceRef, codegen_tag_value};
|
||||
use super::place::{PlaceRef, PlaceValue, codegen_tag_value};
|
||||
use super::{FunctionCx, LocalRef};
|
||||
use crate::common::{IntPredicate, TypeKind};
|
||||
use crate::traits::*;
|
||||
|
|
@ -180,7 +180,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
}
|
||||
|
||||
_ => {
|
||||
assert!(self.rvalue_creates_operand(rvalue));
|
||||
let temp = self.codegen_rvalue_operand(bx, rvalue);
|
||||
temp.val.store(bx, dest);
|
||||
}
|
||||
|
|
@ -218,17 +217,26 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
|
||||
/// Transmutes an `OperandValue` to another `OperandValue`.
|
||||
///
|
||||
/// This is supported only for cases where [`Self::rvalue_creates_operand`]
|
||||
/// returns `true`, and will ICE otherwise. (In particular, anything that
|
||||
/// would need to `alloca` in order to return a `PlaceValue` will ICE,
|
||||
/// expecting those to go via [`Self::codegen_transmute`] instead where
|
||||
/// the destination place is already allocated.)
|
||||
/// This is supported for all cases where the `cast` type is SSA,
|
||||
/// but for non-ZSTs with [`abi::BackendRepr::Memory`] it ICEs.
|
||||
pub(crate) fn codegen_transmute_operand(
|
||||
&mut self,
|
||||
bx: &mut Bx,
|
||||
operand: OperandRef<'tcx, Bx::Value>,
|
||||
cast: TyAndLayout<'tcx>,
|
||||
) -> OperandValue<Bx::Value> {
|
||||
if let abi::BackendRepr::Memory { .. } = cast.backend_repr
|
||||
&& !cast.is_zst()
|
||||
{
|
||||
span_bug!(self.mir.span, "Use `codegen_transmute` to transmute to {cast:?}");
|
||||
}
|
||||
|
||||
// `Layout` is interned, so we can do a cheap check for things that are
|
||||
// exactly the same and thus don't need any handling.
|
||||
if abi::Layout::eq(&operand.layout.layout, &cast.layout) {
|
||||
return operand.val;
|
||||
}
|
||||
|
||||
// Check for transmutes that are always UB.
|
||||
if operand.layout.size != cast.size
|
||||
|| operand.layout.is_uninhabited()
|
||||
|
|
@ -241,11 +249,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
return OperandValue::poison(bx, cast);
|
||||
}
|
||||
|
||||
// To or from pointers takes different methods, so we use this to restrict
|
||||
// the SimdVector case to types which can be `bitcast` between each other.
|
||||
#[inline]
|
||||
fn vector_can_bitcast(x: abi::Scalar) -> bool {
|
||||
matches!(
|
||||
x,
|
||||
abi::Scalar::Initialized {
|
||||
value: abi::Primitive::Int(..) | abi::Primitive::Float(..),
|
||||
..
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
let cx = bx.cx();
|
||||
match (operand.val, operand.layout.backend_repr, cast.backend_repr) {
|
||||
_ if cast.is_zst() => OperandValue::ZeroSized,
|
||||
(_, _, abi::BackendRepr::Memory { .. }) => {
|
||||
bug!("Cannot `codegen_transmute_operand` to non-ZST memory-ABI output {cast:?}");
|
||||
}
|
||||
(OperandValue::Ref(source_place_val), abi::BackendRepr::Memory { .. }, _) => {
|
||||
assert_eq!(source_place_val.llextra, None);
|
||||
// The existing alignment is part of `source_place_val`,
|
||||
|
|
@ -256,16 +275,46 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
OperandValue::Immediate(imm),
|
||||
abi::BackendRepr::Scalar(from_scalar),
|
||||
abi::BackendRepr::Scalar(to_scalar),
|
||||
) => OperandValue::Immediate(transmute_scalar(bx, imm, from_scalar, to_scalar)),
|
||||
) if from_scalar.size(cx) == to_scalar.size(cx) => {
|
||||
OperandValue::Immediate(transmute_scalar(bx, imm, from_scalar, to_scalar))
|
||||
}
|
||||
(
|
||||
OperandValue::Immediate(imm),
|
||||
abi::BackendRepr::SimdVector { element: from_scalar, .. },
|
||||
abi::BackendRepr::SimdVector { element: to_scalar, .. },
|
||||
) if vector_can_bitcast(from_scalar) && vector_can_bitcast(to_scalar) => {
|
||||
let to_backend_ty = bx.cx().immediate_backend_type(cast);
|
||||
OperandValue::Immediate(bx.bitcast(imm, to_backend_ty))
|
||||
}
|
||||
(
|
||||
OperandValue::Pair(imm_a, imm_b),
|
||||
abi::BackendRepr::ScalarPair(in_a, in_b),
|
||||
abi::BackendRepr::ScalarPair(out_a, out_b),
|
||||
) => OperandValue::Pair(
|
||||
transmute_scalar(bx, imm_a, in_a, out_a),
|
||||
transmute_scalar(bx, imm_b, in_b, out_b),
|
||||
),
|
||||
_ => bug!("Cannot `codegen_transmute_operand` {operand:?} to {cast:?}"),
|
||||
) if in_a.size(cx) == out_a.size(cx) && in_b.size(cx) == out_b.size(cx) => {
|
||||
OperandValue::Pair(
|
||||
transmute_scalar(bx, imm_a, in_a, out_a),
|
||||
transmute_scalar(bx, imm_b, in_b, out_b),
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
// For any other potentially-tricky cases, make a temporary instead.
|
||||
// If anything else wants the target local to be in memory this won't
|
||||
// be hit, as `codegen_transmute` will get called directly. Thus this
|
||||
// is only for places where everything else wants the operand form,
|
||||
// and thus it's not worth making those places get it from memory.
|
||||
//
|
||||
// Notably, Scalar ⇌ ScalarPair cases go here to avoid padding
|
||||
// and endianness issues, as do SimdVector ones to avoid worrying
|
||||
// about things like f32x8 ⇌ ptrx4 that would need multiple steps.
|
||||
let align = Ord::max(operand.layout.align.abi, cast.align.abi);
|
||||
let size = Ord::max(operand.layout.size, cast.size);
|
||||
let temp = PlaceValue::alloca(bx, size, align);
|
||||
bx.lifetime_start(temp.llval, size);
|
||||
operand.val.store(bx, temp.with_type(operand.layout));
|
||||
let val = bx.load_operand(temp.with_type(cast)).val;
|
||||
bx.lifetime_end(temp.llval, size);
|
||||
val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -326,8 +375,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
bx: &mut Bx,
|
||||
rvalue: &mir::Rvalue<'tcx>,
|
||||
) -> OperandRef<'tcx, Bx::Value> {
|
||||
assert!(self.rvalue_creates_operand(rvalue), "cannot codegen {rvalue:?} to operand",);
|
||||
|
||||
match *rvalue {
|
||||
mir::Rvalue::Cast(ref kind, ref source, mir_cast_ty) => {
|
||||
let operand = self.codegen_operand(bx, source);
|
||||
|
|
@ -653,8 +700,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
let ty = self.monomorphize(ty);
|
||||
let layout = self.cx.layout_of(ty);
|
||||
|
||||
// `rvalue_creates_operand` has arranged that we only get here if
|
||||
// we can build the aggregate immediate from the field immediates.
|
||||
let mut builder = OperandRefBuilder::new(layout);
|
||||
for (field_idx, field) in fields.iter_enumerated() {
|
||||
let op = self.codegen_operand(bx, field);
|
||||
|
|
@ -955,69 +1000,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
|
||||
OperandValue::Pair(val, of)
|
||||
}
|
||||
|
||||
/// Returns `true` if the `rvalue` can be computed into an [`OperandRef`],
|
||||
/// rather than needing a full `PlaceRef` for the assignment destination.
|
||||
///
|
||||
/// This is used by the [`super::analyze`] code to decide which MIR locals
|
||||
/// can stay as SSA values (as opposed to generating `alloca` slots for them).
|
||||
/// As such, some paths here return `true` even where the specific rvalue
|
||||
/// will not actually take the operand path because the result type is such
|
||||
/// that it always gets an `alloca`, but where it's not worth re-checking the
|
||||
/// layout in this code when the right thing will happen anyway.
|
||||
pub(crate) fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>) -> bool {
|
||||
match *rvalue {
|
||||
mir::Rvalue::Cast(mir::CastKind::Transmute, ref operand, cast_ty) => {
|
||||
let operand_ty = operand.ty(self.mir, self.cx.tcx());
|
||||
let cast_layout = self.cx.layout_of(self.monomorphize(cast_ty));
|
||||
let operand_layout = self.cx.layout_of(self.monomorphize(operand_ty));
|
||||
match (operand_layout.backend_repr, cast_layout.backend_repr) {
|
||||
// When the output will be in memory anyway, just use its place
|
||||
// (instead of the operand path) unless it's the trivial ZST case.
|
||||
(_, abi::BackendRepr::Memory { .. }) => cast_layout.is_zst(),
|
||||
|
||||
// Otherwise (for a non-memory output) if the input is memory
|
||||
// then we can just read the value from the place.
|
||||
(abi::BackendRepr::Memory { .. }, _) => true,
|
||||
|
||||
// When we have scalar immediates, we can only convert things
|
||||
// where the sizes match, to avoid endianness questions.
|
||||
(abi::BackendRepr::Scalar(a), abi::BackendRepr::Scalar(b)) =>
|
||||
a.size(self.cx) == b.size(self.cx),
|
||||
(abi::BackendRepr::ScalarPair(a0, a1), abi::BackendRepr::ScalarPair(b0, b1)) =>
|
||||
a0.size(self.cx) == b0.size(self.cx) && a1.size(self.cx) == b1.size(self.cx),
|
||||
|
||||
// Mixing Scalars and ScalarPairs can get quite complicated when
|
||||
// padding and undef get involved, so leave that to the memory path.
|
||||
(abi::BackendRepr::Scalar(_), abi::BackendRepr::ScalarPair(_, _)) |
|
||||
(abi::BackendRepr::ScalarPair(_, _), abi::BackendRepr::Scalar(_)) => false,
|
||||
|
||||
// SIMD vectors aren't worth the trouble of dealing with complex
|
||||
// cases like from vectors of f32 to vectors of pointers or
|
||||
// from fat pointers to vectors of u16. (See #143194 #110021 ...)
|
||||
(abi::BackendRepr::SimdVector { .. }, _) | (_, abi::BackendRepr::SimdVector { .. }) => false,
|
||||
}
|
||||
}
|
||||
mir::Rvalue::Ref(..) |
|
||||
mir::Rvalue::CopyForDeref(..) |
|
||||
mir::Rvalue::RawPtr(..) |
|
||||
mir::Rvalue::Len(..) |
|
||||
mir::Rvalue::Cast(..) | // (*)
|
||||
mir::Rvalue::ShallowInitBox(..) | // (*)
|
||||
mir::Rvalue::BinaryOp(..) |
|
||||
mir::Rvalue::UnaryOp(..) |
|
||||
mir::Rvalue::Discriminant(..) |
|
||||
mir::Rvalue::NullaryOp(..) |
|
||||
mir::Rvalue::ThreadLocalRef(_) |
|
||||
mir::Rvalue::Use(..) |
|
||||
mir::Rvalue::Repeat(..) | // (*)
|
||||
mir::Rvalue::Aggregate(..) | // (*)
|
||||
mir::Rvalue::WrapUnsafeBinder(..) => // (*)
|
||||
true,
|
||||
}
|
||||
|
||||
// (*) this is only true if the type is suitable
|
||||
}
|
||||
}
|
||||
|
||||
/// Transmutes a single scalar value `imm` from `from_scalar` to `to_scalar`.
|
||||
|
|
|
|||
|
|
@ -128,15 +128,15 @@ const_eval_frame_note_inner = inside {$where_ ->
|
|||
|
||||
const_eval_frame_note_last = the failure occurred here
|
||||
|
||||
const_eval_incompatible_arg_types =
|
||||
calling a function whose parameter #{$arg_idx} has type {$callee_ty} passing argument of type {$caller_ty}
|
||||
|
||||
const_eval_incompatible_calling_conventions =
|
||||
calling a function with calling convention "{$callee_conv}" using calling convention "{$caller_conv}"
|
||||
|
||||
const_eval_incompatible_return_types =
|
||||
calling a function with return type {$callee_ty} passing return place of type {$caller_ty}
|
||||
|
||||
const_eval_incompatible_types =
|
||||
calling a function with argument of type {$callee_ty} passing data of type {$caller_ty}
|
||||
|
||||
const_eval_interior_mutable_borrow_escaping =
|
||||
interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
|
||||
.label = this borrow of an interior mutable value refers to such a temporary
|
||||
|
|
|
|||
|
|
@ -500,7 +500,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
|||
InvalidNichedEnumVariantWritten { .. } => {
|
||||
const_eval_invalid_niched_enum_variant_written
|
||||
}
|
||||
AbiMismatchArgument { .. } => const_eval_incompatible_types,
|
||||
AbiMismatchArgument { .. } => const_eval_incompatible_arg_types,
|
||||
AbiMismatchReturn { .. } => const_eval_incompatible_return_types,
|
||||
}
|
||||
}
|
||||
|
|
@ -625,12 +625,16 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
|||
diag.arg("data_size", info.data_size);
|
||||
}
|
||||
InvalidNichedEnumVariantWritten { enum_ty } => {
|
||||
diag.arg("ty", enum_ty.to_string());
|
||||
diag.arg("ty", enum_ty);
|
||||
}
|
||||
AbiMismatchArgument { caller_ty, callee_ty }
|
||||
| AbiMismatchReturn { caller_ty, callee_ty } => {
|
||||
diag.arg("caller_ty", caller_ty.to_string());
|
||||
diag.arg("callee_ty", callee_ty.to_string());
|
||||
AbiMismatchArgument { arg_idx, caller_ty, callee_ty } => {
|
||||
diag.arg("arg_idx", arg_idx + 1); // adjust for 1-indexed lists in output
|
||||
diag.arg("caller_ty", caller_ty);
|
||||
diag.arg("callee_ty", callee_ty);
|
||||
}
|
||||
AbiMismatchReturn { caller_ty, callee_ty } => {
|
||||
diag.arg("caller_ty", caller_ty);
|
||||
diag.arg("callee_ty", callee_ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -270,6 +270,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
Item = (&'x FnArg<'tcx, M::Provenance>, &'y ArgAbi<'tcx, Ty<'tcx>>),
|
||||
>,
|
||||
callee_abi: &ArgAbi<'tcx, Ty<'tcx>>,
|
||||
callee_arg_idx: usize,
|
||||
callee_arg: &mir::Place<'tcx>,
|
||||
callee_ty: Ty<'tcx>,
|
||||
already_live: bool,
|
||||
|
|
@ -298,6 +299,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// Check compatibility
|
||||
if !self.check_argument_compat(caller_abi, callee_abi)? {
|
||||
throw_ub!(AbiMismatchArgument {
|
||||
arg_idx: callee_arg_idx,
|
||||
caller_ty: caller_abi.layout.ty,
|
||||
callee_ty: callee_abi.layout.ty
|
||||
});
|
||||
|
|
@ -424,7 +426,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// this is a single iterator (that handles `spread_arg`), then
|
||||
// `pass_argument` would be the loop body. It takes care to
|
||||
// not advance `caller_iter` for ignored arguments.
|
||||
let mut callee_args_abis = callee_fn_abi.args.iter();
|
||||
let mut callee_args_abis = callee_fn_abi.args.iter().enumerate();
|
||||
for local in body.args_iter() {
|
||||
// Construct the destination place for this argument. At this point all
|
||||
// locals are still dead, so we cannot construct a `PlaceTy`.
|
||||
|
|
@ -445,10 +447,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
&[mir::ProjectionElem::Field(FieldIdx::from_usize(i), field_ty)],
|
||||
*self.tcx,
|
||||
);
|
||||
let callee_abi = callee_args_abis.next().unwrap();
|
||||
let (idx, callee_abi) = callee_args_abis.next().unwrap();
|
||||
self.pass_argument(
|
||||
&mut caller_args,
|
||||
callee_abi,
|
||||
idx,
|
||||
&dest,
|
||||
field_ty,
|
||||
/* already_live */ true,
|
||||
|
|
@ -456,10 +459,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
} else {
|
||||
// Normal argument. Cannot mark it as live yet, it might be unsized!
|
||||
let callee_abi = callee_args_abis.next().unwrap();
|
||||
let (idx, callee_abi) = callee_args_abis.next().unwrap();
|
||||
self.pass_argument(
|
||||
&mut caller_args,
|
||||
callee_abi,
|
||||
idx,
|
||||
&dest,
|
||||
ty,
|
||||
/* already_live */ false,
|
||||
|
|
|
|||
|
|
@ -551,6 +551,11 @@ impl SelfProfilerRef {
|
|||
pub fn get_self_profiler(&self) -> Option<Arc<SelfProfiler>> {
|
||||
self.profiler.clone()
|
||||
}
|
||||
|
||||
/// Is expensive recording of query keys and/or function arguments enabled?
|
||||
pub fn is_args_recording_enabled(&self) -> bool {
|
||||
self.enabled() && self.event_filter_mask.intersects(EventFilter::ARGS)
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper for recording costly arguments to self-profiling events. Used with
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ thread_local! {
|
|||
pub(crate) fn entrypoint(stream: &MdStream<'_>, buf: &mut Buffer) -> io::Result<()> {
|
||||
#[cfg(not(test))]
|
||||
if let Some((w, _)) = termize::dimensions() {
|
||||
WIDTH.with(|c| c.set(std::cmp::min(w, DEFAULT_COLUMN_WIDTH)));
|
||||
WIDTH.set(std::cmp::min(w, DEFAULT_COLUMN_WIDTH));
|
||||
}
|
||||
write_stream(stream, buf, None, 0)?;
|
||||
buf.write_all(b"\n")
|
||||
|
|
@ -84,7 +84,7 @@ fn write_tt(tt: &MdTree<'_>, buf: &mut Buffer, indent: usize) -> io::Result<()>
|
|||
reset_cursor();
|
||||
}
|
||||
MdTree::HorizontalRule => {
|
||||
(0..WIDTH.with(Cell::get)).for_each(|_| buf.write_all(b"-").unwrap());
|
||||
(0..WIDTH.get()).for_each(|_| buf.write_all(b"-").unwrap());
|
||||
reset_cursor();
|
||||
}
|
||||
MdTree::Heading(n, stream) => {
|
||||
|
|
@ -121,7 +121,7 @@ fn write_tt(tt: &MdTree<'_>, buf: &mut Buffer, indent: usize) -> io::Result<()>
|
|||
|
||||
/// End of that block, just wrap the line
|
||||
fn reset_cursor() {
|
||||
CURSOR.with(|cur| cur.set(0));
|
||||
CURSOR.set(0);
|
||||
}
|
||||
|
||||
/// Change to be generic on Write for testing. If we have a link URL, we don't
|
||||
|
|
@ -144,7 +144,7 @@ fn write_wrapping<B: io::Write>(
|
|||
buf.write_all(ind_ws)?;
|
||||
cur.set(indent);
|
||||
}
|
||||
let ch_count = WIDTH.with(Cell::get) - cur.get();
|
||||
let ch_count = WIDTH.get() - cur.get();
|
||||
let mut iter = to_write.char_indices();
|
||||
let Some((end_idx, _ch)) = iter.nth(ch_count) else {
|
||||
// Write entire line
|
||||
|
|
|
|||
|
|
@ -1,26 +1,8 @@
|
|||
expand_arg_not_attributes =
|
||||
second argument must be `attributes`
|
||||
|
||||
expand_attr_no_arguments =
|
||||
attribute must have either one or two arguments
|
||||
|
||||
expand_attribute_meta_item =
|
||||
attribute must be a meta item, not a literal
|
||||
|
||||
expand_attribute_single_word =
|
||||
attribute must only be a single word
|
||||
|
||||
expand_attributes_on_expressions_experimental =
|
||||
attributes on expressions are experimental
|
||||
.help_outer_doc = `///` is used for outer documentation comments; for a plain comment, use `//`
|
||||
.help_inner_doc = `//!` is used for inner documentation comments; for a plain comment, use `//` by removing the `!` or inserting a space in between them: `// !`
|
||||
|
||||
expand_attributes_wrong_form =
|
||||
attribute must be of form: `attributes(foo, bar)`
|
||||
|
||||
expand_cannot_be_name_of_macro =
|
||||
`{$trait_ident}` cannot be a name of {$macro_type} macro
|
||||
|
||||
expand_collapse_debuginfo_illegal =
|
||||
illegal value for attribute #[collapse_debuginfo(no|external|yes)]
|
||||
|
||||
|
|
@ -71,9 +53,6 @@ expand_glob_delegation_outside_impls =
|
|||
expand_glob_delegation_traitless_qpath =
|
||||
qualified path without a trait in glob delegation
|
||||
|
||||
expand_helper_attribute_name_invalid =
|
||||
`{$name}` cannot be a name of derive helper attribute
|
||||
|
||||
expand_incomplete_parse =
|
||||
macro expansion ignores {$descr} and any tokens following
|
||||
.label = caused by the macro expansion here
|
||||
|
|
@ -165,12 +144,6 @@ expand_mve_unrecognized_var =
|
|||
expand_non_inline_modules_in_proc_macro_input_are_unstable =
|
||||
non-inline modules in proc macro input are unstable
|
||||
|
||||
expand_not_a_meta_item =
|
||||
not a meta item
|
||||
|
||||
expand_only_one_word =
|
||||
must only be one word
|
||||
|
||||
expand_proc_macro_back_compat = using an old version of `{$crate_name}`
|
||||
.note = older versions of the `{$crate_name}` crate no longer compile; please update to `{$crate_name}` v{$fixed_version}, or switch to one of the `{$crate_name}` alternatives
|
||||
|
||||
|
|
|
|||
|
|
@ -861,7 +861,7 @@ impl SyntaxExtension {
|
|||
/// | (unspecified) | no | if-ext | if-ext | yes |
|
||||
/// | external | no | if-ext | if-ext | yes |
|
||||
/// | yes | yes | yes | yes | yes |
|
||||
fn get_collapse_debuginfo(sess: &Session, attrs: &[impl AttributeExt], ext: bool) -> bool {
|
||||
fn get_collapse_debuginfo(sess: &Session, attrs: &[hir::Attribute], ext: bool) -> bool {
|
||||
let flag = sess.opts.cg.collapse_macro_debuginfo;
|
||||
let attr = ast::attr::find_by_name(attrs, sym::collapse_debuginfo)
|
||||
.and_then(|attr| {
|
||||
|
|
@ -872,7 +872,7 @@ impl SyntaxExtension {
|
|||
.ok()
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
if ast::attr::contains_name(attrs, sym::rustc_builtin_macro) {
|
||||
if find_attr!(attrs, AttributeKind::RustcBuiltinMacro { .. }) {
|
||||
CollapseMacroDebuginfo::Yes
|
||||
} else {
|
||||
CollapseMacroDebuginfo::Unspecified
|
||||
|
|
@ -915,16 +915,18 @@ impl SyntaxExtension {
|
|||
let collapse_debuginfo = Self::get_collapse_debuginfo(sess, attrs, !is_local);
|
||||
tracing::debug!(?name, ?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe);
|
||||
|
||||
let (builtin_name, helper_attrs) = ast::attr::find_by_name(attrs, sym::rustc_builtin_macro)
|
||||
.map(|attr| {
|
||||
// Override `helper_attrs` passed above if it's a built-in macro,
|
||||
// marking `proc_macro_derive` macros as built-in is not a realistic use case.
|
||||
parse_macro_name_and_helper_attrs(sess.dcx(), attr, "built-in").map_or_else(
|
||||
|| (Some(name), Vec::new()),
|
||||
|(name, helper_attrs)| (Some(name), helper_attrs),
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| (None, helper_attrs));
|
||||
let (builtin_name, helper_attrs) = match find_attr!(attrs, AttributeKind::RustcBuiltinMacro { builtin_name, helper_attrs, .. } => (builtin_name, helper_attrs))
|
||||
{
|
||||
// Override `helper_attrs` passed above if it's a built-in macro,
|
||||
// marking `proc_macro_derive` macros as built-in is not a realistic use case.
|
||||
Some((Some(name), helper_attrs)) => {
|
||||
(Some(*name), helper_attrs.iter().copied().collect())
|
||||
}
|
||||
Some((None, _)) => (Some(name), Vec::new()),
|
||||
|
||||
// Not a built-in macro
|
||||
None => (None, helper_attrs),
|
||||
};
|
||||
|
||||
let stability = find_attr!(attrs, AttributeKind::Stability { stability, .. } => *stability);
|
||||
|
||||
|
|
@ -1141,7 +1143,7 @@ pub trait ResolverExpand {
|
|||
|
||||
/// Names of specific methods to which glob delegation expands.
|
||||
fn glob_delegation_suffixes(
|
||||
&mut self,
|
||||
&self,
|
||||
trait_def_id: DefId,
|
||||
impl_def_id: LocalDefId,
|
||||
) -> Result<Vec<(Ident, Option<Ident>)>, Indeterminate>;
|
||||
|
|
@ -1224,6 +1226,7 @@ pub struct ExtCtxt<'a> {
|
|||
pub(super) expanded_inert_attrs: MarkedAttrs,
|
||||
/// `-Zmacro-stats` data.
|
||||
pub macro_stats: FxHashMap<(Symbol, MacroKind), MacroStat>,
|
||||
pub nb_macro_errors: usize,
|
||||
}
|
||||
|
||||
impl<'a> ExtCtxt<'a> {
|
||||
|
|
@ -1254,6 +1257,7 @@ impl<'a> ExtCtxt<'a> {
|
|||
expanded_inert_attrs: MarkedAttrs::new(),
|
||||
buffered_early_lint: vec![],
|
||||
macro_stats: Default::default(),
|
||||
nb_macro_errors: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1315,6 +1319,12 @@ impl<'a> ExtCtxt<'a> {
|
|||
self.current_expansion.id.expansion_cause()
|
||||
}
|
||||
|
||||
/// This method increases the internal macro errors count and then call `trace_macros_diag`.
|
||||
pub fn macro_error_and_trace_macros_diag(&mut self) {
|
||||
self.nb_macro_errors += 1;
|
||||
self.trace_macros_diag();
|
||||
}
|
||||
|
||||
pub fn trace_macros_diag(&mut self) {
|
||||
for (span, notes) in self.expansions.iter() {
|
||||
let mut db = self.dcx().create_note(errors::TraceMacro { span: *span });
|
||||
|
|
@ -1382,80 +1392,6 @@ pub fn resolve_path(sess: &Session, path: impl Into<PathBuf>, span: Span) -> PRe
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse_macro_name_and_helper_attrs(
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
attr: &impl AttributeExt,
|
||||
macro_type: &str,
|
||||
) -> Option<(Symbol, Vec<Symbol>)> {
|
||||
// Once we've located the `#[proc_macro_derive]` attribute, verify
|
||||
// that it's of the form `#[proc_macro_derive(Foo)]` or
|
||||
// `#[proc_macro_derive(Foo, attributes(A, ..))]`
|
||||
let list = attr.meta_item_list()?;
|
||||
let ([trait_attr] | [trait_attr, _]) = list.as_slice() else {
|
||||
dcx.emit_err(errors::AttrNoArguments { span: attr.span() });
|
||||
return None;
|
||||
};
|
||||
let Some(trait_attr) = trait_attr.meta_item() else {
|
||||
dcx.emit_err(errors::NotAMetaItem { span: trait_attr.span() });
|
||||
return None;
|
||||
};
|
||||
let trait_ident = match trait_attr.ident() {
|
||||
Some(trait_ident) if trait_attr.is_word() => trait_ident,
|
||||
_ => {
|
||||
dcx.emit_err(errors::OnlyOneWord { span: trait_attr.span });
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
if !trait_ident.name.can_be_raw() {
|
||||
dcx.emit_err(errors::CannotBeNameOfMacro {
|
||||
span: trait_attr.span,
|
||||
trait_ident,
|
||||
macro_type,
|
||||
});
|
||||
}
|
||||
|
||||
let attributes_attr = list.get(1);
|
||||
let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr {
|
||||
if !attr.has_name(sym::attributes) {
|
||||
dcx.emit_err(errors::ArgumentNotAttributes { span: attr.span() });
|
||||
}
|
||||
attr.meta_item_list()
|
||||
.unwrap_or_else(|| {
|
||||
dcx.emit_err(errors::AttributesWrongForm { span: attr.span() });
|
||||
&[]
|
||||
})
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
let Some(attr) = attr.meta_item() else {
|
||||
dcx.emit_err(errors::AttributeMetaItem { span: attr.span() });
|
||||
return None;
|
||||
};
|
||||
|
||||
let ident = match attr.ident() {
|
||||
Some(ident) if attr.is_word() => ident,
|
||||
_ => {
|
||||
dcx.emit_err(errors::AttributeSingleWord { span: attr.span });
|
||||
return None;
|
||||
}
|
||||
};
|
||||
if !ident.name.can_be_raw() {
|
||||
dcx.emit_err(errors::HelperAttributeNameInvalid {
|
||||
span: attr.span,
|
||||
name: ident,
|
||||
});
|
||||
}
|
||||
|
||||
Some(ident.name)
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
Some((trait_ident.name, proc_attrs))
|
||||
}
|
||||
|
||||
/// If this item looks like a specific enums from `rental`, emit a fatal error.
|
||||
/// See #73345 and #83125 for more details.
|
||||
/// FIXME(#73933): Remove this eventually.
|
||||
|
|
|
|||
|
|
@ -78,72 +78,6 @@ pub(crate) struct MacroBodyStability {
|
|||
pub head_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_attr_no_arguments)]
|
||||
pub(crate) struct AttrNoArguments {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_not_a_meta_item)]
|
||||
pub(crate) struct NotAMetaItem {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_only_one_word)]
|
||||
pub(crate) struct OnlyOneWord {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_cannot_be_name_of_macro)]
|
||||
pub(crate) struct CannotBeNameOfMacro<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub trait_ident: Ident,
|
||||
pub macro_type: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_arg_not_attributes)]
|
||||
pub(crate) struct ArgumentNotAttributes {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_attributes_wrong_form)]
|
||||
pub(crate) struct AttributesWrongForm {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_attribute_meta_item)]
|
||||
pub(crate) struct AttributeMetaItem {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_attribute_single_word)]
|
||||
pub(crate) struct AttributeSingleWord {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_helper_attribute_name_invalid)]
|
||||
pub(crate) struct HelperAttributeNameInvalid {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: Ident,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_feature_removed, code = E0557)]
|
||||
#[note]
|
||||
|
|
|
|||
|
|
@ -693,7 +693,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
crate_name: self.cx.ecfg.crate_name,
|
||||
});
|
||||
|
||||
self.cx.trace_macros_diag();
|
||||
self.cx.macro_error_and_trace_macros_diag();
|
||||
guar
|
||||
}
|
||||
|
||||
|
|
@ -707,7 +707,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
) -> ErrorGuaranteed {
|
||||
let guar =
|
||||
self.cx.dcx().emit_err(WrongFragmentKind { span, kind: kind.name(), name: &mac.path });
|
||||
self.cx.trace_macros_diag();
|
||||
self.cx.macro_error_and_trace_macros_diag();
|
||||
guar
|
||||
}
|
||||
|
||||
|
|
@ -1048,7 +1048,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
}
|
||||
annotate_err_with_kind(&mut err, kind, span);
|
||||
let guar = err.emit();
|
||||
self.cx.trace_macros_diag();
|
||||
self.cx.macro_error_and_trace_macros_diag();
|
||||
kind.dummy(span, guar)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -299,6 +299,7 @@ enum EofMatcherPositions {
|
|||
}
|
||||
|
||||
/// Represents the possible results of an attempted parse.
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ParseResult<T, F> {
|
||||
/// Parsed successfully.
|
||||
Success(T),
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@ fn expand_macro<'cx>(
|
|||
// Retry and emit a better error.
|
||||
let (span, guar) =
|
||||
diagnostics::failed_to_match_macro(cx.psess(), sp, def_span, name, arg, rules);
|
||||
cx.trace_macros_diag();
|
||||
cx.macro_error_and_trace_macros_diag();
|
||||
DummyResult::any(span, guar)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1363,6 +1363,17 @@ impl AttributeExt for Attribute {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_proc_macro_attr(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Attribute::Parsed(
|
||||
AttributeKind::ProcMacro(..)
|
||||
| AttributeKind::ProcMacroAttribute(..)
|
||||
| AttributeKind::ProcMacroDerive { .. }
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(fn_delegation): use function delegation instead of manually forwarding
|
||||
|
|
|
|||
|
|
@ -767,7 +767,10 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
|
|||
DefKind::Static { .. } => {
|
||||
check_static_inhabited(tcx, def_id);
|
||||
check_static_linkage(tcx, def_id);
|
||||
res = res.and(wfcheck::check_static_item(tcx, def_id));
|
||||
let ty = tcx.type_of(def_id).instantiate_identity();
|
||||
res = res.and(wfcheck::check_static_item(
|
||||
tcx, def_id, ty, /* should_check_for_sync */ true,
|
||||
));
|
||||
}
|
||||
DefKind::Const => res = res.and(wfcheck::check_const_item(tcx, def_id)),
|
||||
_ => unreachable!(),
|
||||
|
|
|
|||
|
|
@ -1180,12 +1180,13 @@ fn check_item_fn(
|
|||
}
|
||||
|
||||
#[instrument(level = "debug", skip(tcx))]
|
||||
pub(super) fn check_static_item(
|
||||
tcx: TyCtxt<'_>,
|
||||
pub(crate) fn check_static_item<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
item_id: LocalDefId,
|
||||
ty: Ty<'tcx>,
|
||||
should_check_for_sync: bool,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
enter_wf_checking_ctxt(tcx, item_id, |wfcx| {
|
||||
let ty = tcx.type_of(item_id).instantiate_identity();
|
||||
let span = tcx.ty_span(item_id);
|
||||
let item_ty = wfcx.deeply_normalize(span, Some(WellFormedLoc::Ty(item_id)), ty);
|
||||
|
||||
|
|
@ -1212,9 +1213,9 @@ pub(super) fn check_static_item(
|
|||
}
|
||||
|
||||
// Ensure that the end result is `Sync` in a non-thread local `static`.
|
||||
let should_check_for_sync = tcx.static_mutability(item_id.to_def_id())
|
||||
== Some(hir::Mutability::Not)
|
||||
let should_check_for_sync = should_check_for_sync
|
||||
&& !is_foreign_item
|
||||
&& tcx.static_mutability(item_id.to_def_id()) == Some(hir::Mutability::Not)
|
||||
&& !tcx.is_thread_local_static(item_id.to_def_id());
|
||||
|
||||
if should_check_for_sync {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ use rustc_middle::{bug, span_bug};
|
|||
use rustc_span::{DUMMY_SP, Ident, Span};
|
||||
|
||||
use super::{HirPlaceholderCollector, ItemCtxt, bad_placeholder};
|
||||
use crate::check::wfcheck::check_static_item;
|
||||
use crate::errors::TypeofReservedKeywordUsed;
|
||||
use crate::hir_ty_lowering::HirTyLowerer;
|
||||
|
||||
|
|
@ -217,7 +218,15 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
|
|||
"static variable",
|
||||
)
|
||||
} else {
|
||||
icx.lower_ty(ty)
|
||||
let ty = icx.lower_ty(ty);
|
||||
// MIR relies on references to statics being scalars.
|
||||
// Verify that here to avoid ill-formed MIR.
|
||||
// We skip the `Sync` check to avoid cycles for type-alias-impl-trait,
|
||||
// relying on the fact that non-Sync statics don't ICE the rest of the compiler.
|
||||
match check_static_item(tcx, def_id, ty, /* should_check_for_sync */ false) {
|
||||
Ok(()) => ty,
|
||||
Err(guar) => Ty::new_error(tcx, guar),
|
||||
}
|
||||
}
|
||||
}
|
||||
ItemKind::Const(ident, _, ty, body_id) => {
|
||||
|
|
@ -275,7 +284,17 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
|
|||
let args = ty::GenericArgs::identity_for_item(tcx, def_id);
|
||||
Ty::new_fn_def(tcx, def_id.to_def_id(), args)
|
||||
}
|
||||
ForeignItemKind::Static(t, _, _) => icx.lower_ty(t),
|
||||
ForeignItemKind::Static(ty, _, _) => {
|
||||
let ty = icx.lower_ty(ty);
|
||||
// MIR relies on references to statics being scalars.
|
||||
// Verify that here to avoid ill-formed MIR.
|
||||
// We skip the `Sync` check to avoid cycles for type-alias-impl-trait,
|
||||
// relying on the fact that non-Sync statics don't ICE the rest of the compiler.
|
||||
match check_static_item(tcx, def_id, ty, /* should_check_for_sync */ false) {
|
||||
Ok(()) => ty,
|
||||
Err(guar) => Ty::new_error(tcx, guar),
|
||||
}
|
||||
}
|
||||
ForeignItemKind::Type => Ty::new_foreign(tcx, def_id.to_def_id()),
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -264,6 +264,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
err.span_label(within_macro_span, "due to this macro variable");
|
||||
}
|
||||
self.suggest_valid_traits(&mut err, item_name, out_of_scope_traits, true);
|
||||
self.suggest_unwrapping_inner_self(&mut err, source, rcvr_ty, item_name);
|
||||
err.emit()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -208,6 +208,10 @@ fn configure_and_expand(
|
|||
// Expand macros now!
|
||||
let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate));
|
||||
|
||||
if ecx.nb_macro_errors > 0 {
|
||||
sess.dcx().abort_if_errors();
|
||||
}
|
||||
|
||||
// The rest is error reporting and stats
|
||||
|
||||
sess.psess.buffered_lints.with_lock(|buffered_lints: &mut Vec<BufferedEarlyLint>| {
|
||||
|
|
|
|||
|
|
@ -2870,7 +2870,7 @@ impl<'tcx> LateLintPass<'tcx> for AsmLabels {
|
|||
if let hir::Expr {
|
||||
kind:
|
||||
hir::ExprKind::InlineAsm(hir::InlineAsm {
|
||||
asm_macro: AsmMacro::Asm | AsmMacro::NakedAsm,
|
||||
asm_macro: asm_macro @ (AsmMacro::Asm | AsmMacro::NakedAsm),
|
||||
template_strs,
|
||||
options,
|
||||
..
|
||||
|
|
@ -2878,6 +2878,15 @@ impl<'tcx> LateLintPass<'tcx> for AsmLabels {
|
|||
..
|
||||
} = expr
|
||||
{
|
||||
// Non-generic naked functions are allowed to define arbitrary
|
||||
// labels.
|
||||
if *asm_macro == AsmMacro::NakedAsm {
|
||||
let def_id = expr.hir_id.owner.def_id;
|
||||
if !cx.tcx.generics_of(def_id).requires_monomorphization(cx.tcx) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// asm with `options(raw)` does not do replacement with `{` and `}`.
|
||||
let raw = options.contains(InlineAsmOptions::RAW);
|
||||
|
||||
|
|
|
|||
|
|
@ -356,7 +356,16 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>(
|
|||
let store = unerased_lint_store(tcx.sess);
|
||||
|
||||
if store.late_module_passes.is_empty() {
|
||||
late_lint_mod_inner(tcx, module_def_id, context, builtin_lints);
|
||||
// If all builtin lints can be skipped, there is no point in running `late_lint_mod_inner`
|
||||
// at all. This happens often for dependencies built with `--cap-lints=allow`.
|
||||
let dont_need_to_run = tcx.lints_that_dont_need_to_run(());
|
||||
let can_skip_lints = builtin_lints
|
||||
.get_lints()
|
||||
.iter()
|
||||
.all(|lint| dont_need_to_run.contains(&LintId::of(lint)));
|
||||
if !can_skip_lints {
|
||||
late_lint_mod_inner(tcx, module_def_id, context, builtin_lints);
|
||||
}
|
||||
} else {
|
||||
let builtin_lints = Box::new(builtin_lints) as Box<dyn LateLintPass<'tcx>>;
|
||||
let mut binding = store
|
||||
|
|
|
|||
|
|
@ -933,6 +933,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> bool {
|
||||
let feature = if let Some(feature) = lint_id.lint.feature_gate
|
||||
&& !self.features.enabled(feature)
|
||||
&& !span.allows_unstable(feature)
|
||||
{
|
||||
// Lint is behind a feature that is not enabled; eventually return false.
|
||||
feature
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ macro_rules! expand_combined_late_lint_pass_methods {
|
|||
/// Combines multiple lints passes into a single lint pass, at compile time,
|
||||
/// for maximum speed. Each `check_foo` method in `$methods` within this pass
|
||||
/// simply calls `check_foo` once per `$pass`. Compare with
|
||||
/// `LateLintPassObjects`, which is similar, but combines lint passes at
|
||||
/// `RuntimeCombinedLateLintPass`, which is similar, but combines lint passes at
|
||||
/// runtime.
|
||||
#[macro_export]
|
||||
macro_rules! declare_combined_late_lint_pass {
|
||||
|
|
@ -123,10 +123,10 @@ macro_rules! declare_combined_late_lint_pass {
|
|||
#[allow(rustc::lint_pass_impl_without_macro)]
|
||||
impl $crate::LintPass for $name {
|
||||
fn name(&self) -> &'static str {
|
||||
panic!()
|
||||
stringify!($name)
|
||||
}
|
||||
fn get_lints(&self) -> LintVec {
|
||||
panic!()
|
||||
$name::get_lints()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1340,7 +1340,15 @@ impl EarlyLintPass for UnusedParens {
|
|||
self.with_self_ty_parens = false;
|
||||
}
|
||||
ast::TyKind::Ref(_, mut_ty) | ast::TyKind::Ptr(mut_ty) => {
|
||||
self.in_no_bounds_pos.insert(mut_ty.ty.id, NoBoundsException::OneBound);
|
||||
// If this type itself appears in no-bounds position, we propagate its
|
||||
// potentially tighter constraint or risk a false posive (issue 143653).
|
||||
let own_constraint = self.in_no_bounds_pos.get(&ty.id);
|
||||
let constraint = match own_constraint {
|
||||
Some(NoBoundsException::None) => NoBoundsException::None,
|
||||
Some(NoBoundsException::OneBound) => NoBoundsException::OneBound,
|
||||
None => NoBoundsException::OneBound,
|
||||
};
|
||||
self.in_no_bounds_pos.insert(mut_ty.ty.id, constraint);
|
||||
}
|
||||
ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => {
|
||||
for i in 0..bounds.len() {
|
||||
|
|
|
|||
|
|
@ -1610,7 +1610,7 @@ extern "C" void LLVMRustPositionBefore(LLVMBuilderRef B, LLVMValueRef Instr) {
|
|||
|
||||
extern "C" void LLVMRustPositionAfter(LLVMBuilderRef B, LLVMValueRef Instr) {
|
||||
if (auto I = dyn_cast<Instruction>(unwrap<Value>(Instr))) {
|
||||
auto J = I->getNextNonDebugInstruction();
|
||||
auto J = I->getNextNode();
|
||||
unwrap(B)->SetInsertPoint(J);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use std::io::{Read, Seek, Write};
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
use rustc_attr_data_structures::EncodeCrossCrate;
|
||||
use rustc_attr_data_structures::{AttributeKind, EncodeCrossCrate, find_attr};
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::memmap::{Mmap, MmapMut};
|
||||
use rustc_data_structures::sync::{join, par_for_each_in};
|
||||
|
|
@ -1965,18 +1965,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
// Proc-macros may have attributes like `#[allow_internal_unstable]`,
|
||||
// so downstream crates need access to them.
|
||||
let attrs = tcx.hir_attrs(proc_macro);
|
||||
let macro_kind = if ast::attr::contains_name(attrs, sym::proc_macro) {
|
||||
let macro_kind = if find_attr!(attrs, AttributeKind::ProcMacro(..)) {
|
||||
MacroKind::Bang
|
||||
} else if ast::attr::contains_name(attrs, sym::proc_macro_attribute) {
|
||||
} else if find_attr!(attrs, AttributeKind::ProcMacroAttribute(..)) {
|
||||
MacroKind::Attr
|
||||
} else if let Some(attr) = ast::attr::find_by_name(attrs, sym::proc_macro_derive) {
|
||||
// This unwrap chain should have been checked by the proc-macro harness.
|
||||
name = attr.meta_item_list().unwrap()[0]
|
||||
.meta_item()
|
||||
.unwrap()
|
||||
.ident()
|
||||
.unwrap()
|
||||
.name;
|
||||
} else if let Some(trait_name) = find_attr!(attrs, AttributeKind::ProcMacroDerive { trait_name, ..} => trait_name)
|
||||
{
|
||||
name = *trait_name;
|
||||
MacroKind::Derive
|
||||
} else {
|
||||
bug!("Unknown proc-macro type for item {:?}", id);
|
||||
|
|
@ -2124,11 +2119,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
};
|
||||
let def_id = id.owner_id.to_def_id();
|
||||
|
||||
self.tables.defaultness.set_some(def_id.index, tcx.defaultness(def_id));
|
||||
|
||||
if of_trait && let Some(header) = tcx.impl_trait_header(def_id) {
|
||||
record!(self.tables.impl_trait_header[def_id] <- header);
|
||||
|
||||
self.tables.defaultness.set_some(def_id.index, tcx.defaultness(def_id));
|
||||
|
||||
let trait_ref = header.trait_ref.instantiate_identity();
|
||||
let simplified_self_ty = fast_reject::simplify_type(
|
||||
self.tcx,
|
||||
|
|
|
|||
|
|
@ -426,7 +426,12 @@ pub enum UndefinedBehaviorInfo<'tcx> {
|
|||
/// Trying to set discriminant to the niched variant, but the value does not match.
|
||||
InvalidNichedEnumVariantWritten { enum_ty: Ty<'tcx> },
|
||||
/// ABI-incompatible argument types.
|
||||
AbiMismatchArgument { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> },
|
||||
AbiMismatchArgument {
|
||||
/// The index of the argument whose type is wrong.
|
||||
arg_idx: usize,
|
||||
caller_ty: Ty<'tcx>,
|
||||
callee_ty: Ty<'tcx>,
|
||||
},
|
||||
/// ABI-incompatible return types.
|
||||
AbiMismatchReturn { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> },
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1041,11 +1041,13 @@ const NUM_PREINTERNED_TY_VARS: u32 = 100;
|
|||
const NUM_PREINTERNED_FRESH_TYS: u32 = 20;
|
||||
const NUM_PREINTERNED_FRESH_INT_TYS: u32 = 3;
|
||||
const NUM_PREINTERNED_FRESH_FLOAT_TYS: u32 = 3;
|
||||
const NUM_PREINTERNED_ANON_BOUND_TYS_I: u32 = 3;
|
||||
const NUM_PREINTERNED_ANON_BOUND_TYS_V: u32 = 20;
|
||||
|
||||
// This number may seem high, but it is reached in all but the smallest crates.
|
||||
const NUM_PREINTERNED_RE_VARS: u32 = 500;
|
||||
const NUM_PREINTERNED_RE_LATE_BOUNDS_I: u32 = 2;
|
||||
const NUM_PREINTERNED_RE_LATE_BOUNDS_V: u32 = 20;
|
||||
const NUM_PREINTERNED_ANON_RE_BOUNDS_I: u32 = 3;
|
||||
const NUM_PREINTERNED_ANON_RE_BOUNDS_V: u32 = 20;
|
||||
|
||||
pub struct CommonTypes<'tcx> {
|
||||
pub unit: Ty<'tcx>,
|
||||
|
|
@ -1088,6 +1090,11 @@ pub struct CommonTypes<'tcx> {
|
|||
|
||||
/// Pre-interned `Infer(ty::FreshFloatTy(n))` for small values of `n`.
|
||||
pub fresh_float_tys: Vec<Ty<'tcx>>,
|
||||
|
||||
/// Pre-interned values of the form:
|
||||
/// `Bound(DebruijnIndex(i), BoundTy { var: v, kind: BoundTyKind::Anon})`
|
||||
/// for small values of `i` and `v`.
|
||||
pub anon_bound_tys: Vec<Vec<Ty<'tcx>>>,
|
||||
}
|
||||
|
||||
pub struct CommonLifetimes<'tcx> {
|
||||
|
|
@ -1101,9 +1108,9 @@ pub struct CommonLifetimes<'tcx> {
|
|||
pub re_vars: Vec<Region<'tcx>>,
|
||||
|
||||
/// Pre-interned values of the form:
|
||||
/// `ReBound(DebruijnIndex(i), BoundRegion { var: v, kind: BrAnon })`
|
||||
/// `ReBound(DebruijnIndex(i), BoundRegion { var: v, kind: BoundRegionKind::Anon })`
|
||||
/// for small values of `i` and `v`.
|
||||
pub re_late_bounds: Vec<Vec<Region<'tcx>>>,
|
||||
pub anon_re_bounds: Vec<Vec<Region<'tcx>>>,
|
||||
}
|
||||
|
||||
pub struct CommonConsts<'tcx> {
|
||||
|
|
@ -1131,6 +1138,19 @@ impl<'tcx> CommonTypes<'tcx> {
|
|||
let fresh_float_tys: Vec<_> =
|
||||
(0..NUM_PREINTERNED_FRESH_FLOAT_TYS).map(|n| mk(Infer(ty::FreshFloatTy(n)))).collect();
|
||||
|
||||
let anon_bound_tys = (0..NUM_PREINTERNED_ANON_BOUND_TYS_I)
|
||||
.map(|i| {
|
||||
(0..NUM_PREINTERNED_ANON_BOUND_TYS_V)
|
||||
.map(|v| {
|
||||
mk(ty::Bound(
|
||||
ty::DebruijnIndex::from(i),
|
||||
ty::BoundTy { var: ty::BoundVar::from(v), kind: ty::BoundTyKind::Anon },
|
||||
))
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
|
||||
CommonTypes {
|
||||
unit: mk(Tuple(List::empty())),
|
||||
bool: mk(Bool),
|
||||
|
|
@ -1161,6 +1181,7 @@ impl<'tcx> CommonTypes<'tcx> {
|
|||
fresh_tys,
|
||||
fresh_int_tys,
|
||||
fresh_float_tys,
|
||||
anon_bound_tys,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1176,9 +1197,9 @@ impl<'tcx> CommonLifetimes<'tcx> {
|
|||
let re_vars =
|
||||
(0..NUM_PREINTERNED_RE_VARS).map(|n| mk(ty::ReVar(ty::RegionVid::from(n)))).collect();
|
||||
|
||||
let re_late_bounds = (0..NUM_PREINTERNED_RE_LATE_BOUNDS_I)
|
||||
let anon_re_bounds = (0..NUM_PREINTERNED_ANON_RE_BOUNDS_I)
|
||||
.map(|i| {
|
||||
(0..NUM_PREINTERNED_RE_LATE_BOUNDS_V)
|
||||
(0..NUM_PREINTERNED_ANON_RE_BOUNDS_V)
|
||||
.map(|v| {
|
||||
mk(ty::ReBound(
|
||||
ty::DebruijnIndex::from(i),
|
||||
|
|
@ -1196,7 +1217,7 @@ impl<'tcx> CommonLifetimes<'tcx> {
|
|||
re_static: mk(ty::ReStatic),
|
||||
re_erased: mk(ty::ReErased),
|
||||
re_vars,
|
||||
re_late_bounds,
|
||||
anon_re_bounds,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2024,7 +2024,10 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
&& let Some(def_id) = def_id.as_local()
|
||||
&& let outer = self.def_span(def_id).ctxt().outer_expn_data()
|
||||
&& matches!(outer.kind, ExpnKind::Macro(MacroKind::Derive, _))
|
||||
&& self.has_attr(outer.macro_def_id.unwrap(), sym::rustc_builtin_macro)
|
||||
&& find_attr!(
|
||||
self.get_all_attrs(outer.macro_def_id.unwrap()),
|
||||
AttributeKind::RustcBuiltinMacro { .. }
|
||||
)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ macro_rules! define_helper {
|
|||
|
||||
impl $helper {
|
||||
pub fn new() -> $helper {
|
||||
$helper($tl.with(|c| c.replace(true)))
|
||||
$helper($tl.replace(true))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -100,12 +100,12 @@ macro_rules! define_helper {
|
|||
|
||||
impl Drop for $helper {
|
||||
fn drop(&mut self) {
|
||||
$tl.with(|c| c.set(self.0))
|
||||
$tl.set(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn $name() -> bool {
|
||||
$tl.with(|c| c.get())
|
||||
$tl.get()
|
||||
}
|
||||
)+
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ impl<'tcx> Region<'tcx> {
|
|||
) -> Region<'tcx> {
|
||||
// Use a pre-interned one when possible.
|
||||
if let ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon } = bound_region
|
||||
&& let Some(inner) = tcx.lifetimes.re_late_bounds.get(debruijn.as_usize())
|
||||
&& let Some(inner) = tcx.lifetimes.anon_re_bounds.get(debruijn.as_usize())
|
||||
&& let Some(re) = inner.get(var.as_usize()).copied()
|
||||
{
|
||||
re
|
||||
|
|
|
|||
|
|
@ -485,7 +485,15 @@ impl<'tcx> Ty<'tcx> {
|
|||
index: ty::DebruijnIndex,
|
||||
bound_ty: ty::BoundTy,
|
||||
) -> Ty<'tcx> {
|
||||
Ty::new(tcx, Bound(index, bound_ty))
|
||||
// Use a pre-interned one when possible.
|
||||
if let ty::BoundTy { var, kind: ty::BoundTyKind::Anon } = bound_ty
|
||||
&& let Some(inner) = tcx.types.anon_bound_tys.get(index.as_usize())
|
||||
&& let Some(ty) = inner.get(var.as_usize()).copied()
|
||||
{
|
||||
ty
|
||||
} else {
|
||||
Ty::new(tcx, Bound(index, bound_ty))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -86,10 +86,16 @@ mir_build_confused = missing patterns are not covered because `{$variable}` is i
|
|||
|
||||
mir_build_const_continue_bad_const = could not determine the target branch for this `#[const_continue]`
|
||||
.label = this value is too generic
|
||||
.note = the value must be a literal or a monomorphic const
|
||||
|
||||
mir_build_const_continue_missing_value = a `#[const_continue]` must break to a label with a value
|
||||
|
||||
mir_build_const_continue_not_const = could not determine the target branch for this `#[const_continue]`
|
||||
.help = try extracting the expression into a `const` item
|
||||
|
||||
mir_build_const_continue_not_const_const_block = `const` blocks may use generics, and are not evaluated early enough
|
||||
mir_build_const_continue_not_const_const_other = this value must be a literal or a monomorphic const
|
||||
mir_build_const_continue_not_const_constant_parameter = constant parameters may use generics, and are not evaluated early enough
|
||||
|
||||
mir_build_const_continue_unknown_jump_target = the target of this `#[const_continue]` is not statically known
|
||||
.label = this value must be a literal or a monomorphic const
|
||||
|
||||
|
|
|
|||
|
|
@ -100,7 +100,9 @@ use tracing::{debug, instrument};
|
|||
|
||||
use super::matches::BuiltMatchTree;
|
||||
use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG};
|
||||
use crate::errors::{ConstContinueBadConst, ConstContinueUnknownJumpTarget};
|
||||
use crate::errors::{
|
||||
ConstContinueBadConst, ConstContinueNotMonomorphicConst, ConstContinueUnknownJumpTarget,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Scopes<'tcx> {
|
||||
|
|
@ -867,7 +869,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
span_bug!(span, "break value must be a scope")
|
||||
};
|
||||
|
||||
let constant = match &self.thir[value].kind {
|
||||
let expr = &self.thir[value];
|
||||
let constant = match &expr.kind {
|
||||
ExprKind::Adt(box AdtExpr { variant_index, fields, base, .. }) => {
|
||||
assert!(matches!(base, AdtExprBase::None));
|
||||
assert!(fields.is_empty());
|
||||
|
|
@ -887,7 +890,27 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
),
|
||||
}
|
||||
}
|
||||
_ => self.as_constant(&self.thir[value]),
|
||||
|
||||
ExprKind::Literal { .. }
|
||||
| ExprKind::NonHirLiteral { .. }
|
||||
| ExprKind::ZstLiteral { .. }
|
||||
| ExprKind::NamedConst { .. } => self.as_constant(&self.thir[value]),
|
||||
|
||||
other => {
|
||||
use crate::errors::ConstContinueNotMonomorphicConstReason as Reason;
|
||||
|
||||
let span = expr.span;
|
||||
let reason = match other {
|
||||
ExprKind::ConstParam { .. } => Reason::ConstantParameter { span },
|
||||
ExprKind::ConstBlock { .. } => Reason::ConstBlock { span },
|
||||
_ => Reason::Other { span },
|
||||
};
|
||||
|
||||
self.tcx
|
||||
.dcx()
|
||||
.emit_err(ConstContinueNotMonomorphicConst { span: expr.span, reason });
|
||||
return block.unit();
|
||||
}
|
||||
};
|
||||
|
||||
let break_index = self
|
||||
|
|
|
|||
|
|
@ -1213,6 +1213,38 @@ pub(crate) struct LoopMatchArmWithGuard {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(mir_build_const_continue_not_const)]
|
||||
#[help]
|
||||
pub(crate) struct ConstContinueNotMonomorphicConst {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
|
||||
#[subdiagnostic]
|
||||
pub reason: ConstContinueNotMonomorphicConstReason,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum ConstContinueNotMonomorphicConstReason {
|
||||
#[label(mir_build_const_continue_not_const_constant_parameter)]
|
||||
ConstantParameter {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
|
||||
#[label(mir_build_const_continue_not_const_const_block)]
|
||||
ConstBlock {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
|
||||
#[label(mir_build_const_continue_not_const_const_other)]
|
||||
Other {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(mir_build_const_continue_bad_const)]
|
||||
pub(crate) struct ConstContinueBadConst {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span};
|
||||
use rustc_span::{DesugaringKind, ExpnKind, MacroKind, Span};
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
|
||||
|
|
@ -84,18 +83,8 @@ pub(super) fn extract_refined_covspans<'tcx>(
|
|||
// Discard any span that overlaps with a hole.
|
||||
discard_spans_overlapping_holes(&mut covspans, &holes);
|
||||
|
||||
// Discard spans that overlap in unwanted ways.
|
||||
// Perform more refinement steps after holes have been dealt with.
|
||||
let mut covspans = remove_unwanted_overlapping_spans(covspans);
|
||||
|
||||
// For all empty spans, either enlarge them to be non-empty, or discard them.
|
||||
let source_map = tcx.sess.source_map();
|
||||
covspans.retain_mut(|covspan| {
|
||||
let Some(span) = ensure_non_empty_span(source_map, covspan.span) else { return false };
|
||||
covspan.span = span;
|
||||
true
|
||||
});
|
||||
|
||||
// Merge covspans that can be merged.
|
||||
covspans.dedup_by(|b, a| a.merge_if_eligible(b));
|
||||
|
||||
code_mappings.extend(covspans.into_iter().map(|Covspan { span, bcb }| {
|
||||
|
|
@ -241,26 +230,3 @@ fn compare_spans(a: Span, b: Span) -> std::cmp::Ordering {
|
|||
// - Both have the same start and span A extends further right
|
||||
.then_with(|| Ord::cmp(&a.hi(), &b.hi()).reverse())
|
||||
}
|
||||
|
||||
fn ensure_non_empty_span(source_map: &SourceMap, span: Span) -> Option<Span> {
|
||||
if !span.is_empty() {
|
||||
return Some(span);
|
||||
}
|
||||
|
||||
// The span is empty, so try to enlarge it to cover an adjacent '{' or '}'.
|
||||
source_map
|
||||
.span_to_source(span, |src, start, end| try {
|
||||
// Adjusting span endpoints by `BytePos(1)` is normally a bug,
|
||||
// but in this case we have specifically checked that the character
|
||||
// we're skipping over is one of two specific ASCII characters, so
|
||||
// adjusting by exactly 1 byte is correct.
|
||||
if src.as_bytes().get(end).copied() == Some(b'{') {
|
||||
Some(span.with_hi(span.hi() + BytePos(1)))
|
||||
} else if start > 0 && src.as_bytes()[start - 1] == b'}' {
|
||||
Some(span.with_lo(span.lo() - BytePos(1)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.ok()?
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ use rustc_type_ir::inherent::*;
|
|||
use rustc_type_ir::lang_items::TraitSolverLangItem;
|
||||
use rustc_type_ir::solve::SizedTraitKind;
|
||||
use rustc_type_ir::{
|
||||
self as ty, Interner, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt as _,
|
||||
TypeVisitor, TypingMode, Upcast as _, elaborate,
|
||||
self as ty, Interner, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable,
|
||||
TypeVisitableExt as _, TypeVisitor, TypingMode, Upcast as _, elaborate,
|
||||
};
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
|
|
@ -132,6 +132,7 @@ where
|
|||
})
|
||||
.enter(|ecx| {
|
||||
Self::match_assumption(ecx, goal, assumption, |ecx| {
|
||||
ecx.try_evaluate_added_goals()?;
|
||||
source.set(ecx.characterize_param_env_assumption(goal.param_env, assumption)?);
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
})
|
||||
|
|
@ -1069,8 +1070,10 @@ where
|
|||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
} else {
|
||||
} else if ty.has_type_flags(TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_RE_INFER) {
|
||||
ty.super_visit_with(self)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1086,8 +1089,10 @@ where
|
|||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
} else {
|
||||
} else if ct.has_type_flags(TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_RE_INFER) {
|
||||
ct.super_visit_with(self)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ bitflags = "2.4.1"
|
|||
rustc-literal-escaper = "0.0.5"
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_attr_parsing = { path = "../rustc_attr_parsing" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_feature = { path = "../rustc_feature" }
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
//! Meta-syntax validation logic of attributes for post-expansion.
|
||||
|
||||
use std::slice;
|
||||
|
||||
use rustc_ast::token::Delimiter;
|
||||
use rustc_ast::tokenstream::DelimSpan;
|
||||
use rustc_ast::{
|
||||
self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, NodeId,
|
||||
Path, Safety,
|
||||
};
|
||||
use rustc_attr_parsing::{AttributeParser, Late};
|
||||
use rustc_errors::{Applicability, DiagCtxtHandle, FatalError, PResult};
|
||||
use rustc_feature::{AttributeSafety, AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
|
||||
use rustc_session::errors::report_lit_error;
|
||||
|
|
@ -266,67 +269,7 @@ pub fn check_builtin_meta_item(
|
|||
) {
|
||||
if !is_attr_template_compatible(&template, &meta.kind) {
|
||||
// attrs with new parsers are locally validated so excluded here
|
||||
if matches!(
|
||||
name,
|
||||
sym::inline
|
||||
| sym::export_stable
|
||||
| sym::ffi_const
|
||||
| sym::ffi_pure
|
||||
| sym::rustc_std_internal_symbol
|
||||
| sym::may_dangle
|
||||
| sym::rustc_as_ptr
|
||||
| sym::rustc_pub_transparent
|
||||
| sym::rustc_const_stable_indirect
|
||||
| sym::rustc_force_inline
|
||||
| sym::rustc_confusables
|
||||
| sym::rustc_skip_during_method_dispatch
|
||||
| sym::rustc_pass_by_value
|
||||
| sym::rustc_deny_explicit_impl
|
||||
| sym::rustc_do_not_implement_via_object
|
||||
| sym::rustc_coinductive
|
||||
| sym::const_trait
|
||||
| sym::stable
|
||||
| sym::unstable
|
||||
| sym::rustc_allowed_through_unstable_modules
|
||||
| sym::rustc_specialization_trait
|
||||
| sym::rustc_unsafe_specialization_marker
|
||||
| sym::rustc_allow_incoherent_impl
|
||||
| sym::rustc_coherence_is_core
|
||||
| sym::marker
|
||||
| sym::fundamental
|
||||
| sym::rustc_paren_sugar
|
||||
| sym::type_const
|
||||
| sym::repr
|
||||
// FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres
|
||||
// ambiguity
|
||||
| sym::rustc_align
|
||||
| sym::deprecated
|
||||
| sym::optimize
|
||||
| sym::pointee
|
||||
| sym::cold
|
||||
| sym::target_feature
|
||||
| sym::rustc_allow_const_fn_unstable
|
||||
| sym::macro_use
|
||||
| sym::macro_escape
|
||||
| sym::naked
|
||||
| sym::no_mangle
|
||||
| sym::non_exhaustive
|
||||
| sym::omit_gdb_pretty_printer_section
|
||||
| sym::path
|
||||
| sym::ignore
|
||||
| sym::must_use
|
||||
| sym::track_caller
|
||||
| sym::link_name
|
||||
| sym::link_ordinal
|
||||
| sym::export_name
|
||||
| sym::rustc_macro_transparency
|
||||
| sym::link_section
|
||||
| sym::rustc_layout_scalar_valid_range_start
|
||||
| sym::rustc_layout_scalar_valid_range_end
|
||||
| sym::no_implicit_prelude
|
||||
| sym::automatically_derived
|
||||
| sym::coverage
|
||||
) {
|
||||
if AttributeParser::<Late>::is_parsed_attribute(slice::from_ref(&name)) {
|
||||
return;
|
||||
}
|
||||
emit_malformed_attribute(psess, style, meta.span, name, template);
|
||||
|
|
|
|||
|
|
@ -130,6 +130,22 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
for attr in attrs {
|
||||
let mut style = None;
|
||||
match attr {
|
||||
Attribute::Parsed(AttributeKind::ProcMacro(_)) => {
|
||||
self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike)
|
||||
}
|
||||
Attribute::Parsed(AttributeKind::ProcMacroAttribute(_)) => {
|
||||
self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute);
|
||||
}
|
||||
Attribute::Parsed(AttributeKind::ProcMacroDerive { span: attr_span, .. }) => {
|
||||
self.check_generic_attr(
|
||||
hir_id,
|
||||
sym::proc_macro_derive,
|
||||
*attr_span,
|
||||
target,
|
||||
Target::Fn,
|
||||
);
|
||||
self.check_proc_macro(hir_id, target, ProcMacroKind::Derive)
|
||||
}
|
||||
Attribute::Parsed(
|
||||
AttributeKind::SkipDuringMethodDispatch { span: attr_span, .. }
|
||||
| AttributeKind::Coinductive(attr_span)
|
||||
|
|
@ -275,6 +291,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
| AttributeKind::MacroTransparency(_)
|
||||
| AttributeKind::Pointee(..)
|
||||
| AttributeKind::Dummy
|
||||
| AttributeKind::RustcBuiltinMacro { .. }
|
||||
| AttributeKind::OmitGdbPrettyPrinterSection,
|
||||
) => { /* do nothing */ }
|
||||
Attribute::Parsed(AttributeKind::AsPtr(attr_span)) => {
|
||||
|
|
@ -373,16 +390,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
[sym::should_panic, ..] => {
|
||||
self.check_generic_attr_unparsed(hir_id, attr, target, Target::Fn)
|
||||
}
|
||||
[sym::proc_macro, ..] => {
|
||||
self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike)
|
||||
}
|
||||
[sym::proc_macro_attribute, ..] => {
|
||||
self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute);
|
||||
}
|
||||
[sym::proc_macro_derive, ..] => {
|
||||
self.check_generic_attr_unparsed(hir_id, attr, target, Target::Fn);
|
||||
self.check_proc_macro(hir_id, target, ProcMacroKind::Derive)
|
||||
}
|
||||
[sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => {
|
||||
self.check_autodiff(hir_id, attr, span, target)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -950,9 +950,7 @@ impl<Cx: PatCx> Constructor<Cx> {
|
|||
}
|
||||
}
|
||||
Never => write!(f, "!")?,
|
||||
Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => {
|
||||
write!(f, "_ : {:?}", ty)?
|
||||
}
|
||||
Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => write!(f, "_")?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,13 @@ pub trait PatCx: Sized + fmt::Debug {
|
|||
|
||||
fn is_exhaustive_patterns_feature_on(&self) -> bool;
|
||||
|
||||
/// Whether to ensure the non-exhaustiveness witnesses we report for a complete set. This is
|
||||
/// `false` by default to avoid some exponential blowup cases such as
|
||||
/// <https://github.com/rust-lang/rust/issues/118437>.
|
||||
fn exhaustive_witnesses(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// The number of fields for this constructor.
|
||||
fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize;
|
||||
|
||||
|
|
|
|||
|
|
@ -994,7 +994,8 @@ impl<Cx: PatCx> PlaceInfo<Cx> {
|
|||
if !missing_ctors.is_empty() && !report_individual_missing_ctors {
|
||||
// Report `_` as missing.
|
||||
missing_ctors = vec![Constructor::Wildcard];
|
||||
} else if missing_ctors.iter().any(|c| c.is_non_exhaustive()) {
|
||||
} else if missing_ctors.iter().any(|c| c.is_non_exhaustive()) && !cx.exhaustive_witnesses()
|
||||
{
|
||||
// We need to report a `_` anyway, so listing other constructors would be redundant.
|
||||
// `NonExhaustive` is displayed as `_` just like `Wildcard`, but it will be picked
|
||||
// up by diagnostics to add a note about why `_` is required here.
|
||||
|
|
@ -1747,7 +1748,9 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>(
|
|||
// `ctor` is *irrelevant* if there's another constructor in `split_ctors` that matches
|
||||
// strictly fewer rows. In that case we can sometimes skip it. See the top of the file for
|
||||
// details.
|
||||
let ctor_is_relevant = matches!(ctor, Constructor::Missing) || missing_ctors.is_empty();
|
||||
let ctor_is_relevant = matches!(ctor, Constructor::Missing)
|
||||
|| missing_ctors.is_empty()
|
||||
|| mcx.tycx.exhaustive_witnesses();
|
||||
let mut spec_matrix = matrix.specialize_constructor(pcx, &ctor, ctor_is_relevant)?;
|
||||
let mut witnesses = ensure_sufficient_stack(|| {
|
||||
compute_exhaustiveness_and_usefulness(mcx, &mut spec_matrix)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code, unreachable_pub)]
|
||||
use rustc_pattern_analysis::constructor::{
|
||||
Constructor, ConstructorSet, IntRange, MaybeInfiniteInt, RangeEnd, VariantVisibility,
|
||||
};
|
||||
|
|
@ -22,8 +23,10 @@ fn init_tracing() {
|
|||
.try_init();
|
||||
}
|
||||
|
||||
pub(super) const UNIT: Ty = Ty::Tuple(&[]);
|
||||
pub(super) const NEVER: Ty = Ty::Enum(&[]);
|
||||
|
||||
/// A simple set of types.
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub(super) enum Ty {
|
||||
/// Booleans
|
||||
|
|
@ -38,6 +41,8 @@ pub(super) enum Ty {
|
|||
BigStruct { arity: usize, ty: &'static Ty },
|
||||
/// A enum with `arity` variants of type `ty`.
|
||||
BigEnum { arity: usize, ty: &'static Ty },
|
||||
/// Like `Enum` but non-exhaustive.
|
||||
NonExhaustiveEnum(&'static [Ty]),
|
||||
}
|
||||
|
||||
/// The important logic.
|
||||
|
|
@ -47,7 +52,7 @@ impl Ty {
|
|||
match (ctor, *self) {
|
||||
(Struct, Ty::Tuple(tys)) => tys.iter().copied().collect(),
|
||||
(Struct, Ty::BigStruct { arity, ty }) => (0..arity).map(|_| *ty).collect(),
|
||||
(Variant(i), Ty::Enum(tys)) => vec![tys[*i]],
|
||||
(Variant(i), Ty::Enum(tys) | Ty::NonExhaustiveEnum(tys)) => vec![tys[*i]],
|
||||
(Variant(_), Ty::BigEnum { ty, .. }) => vec![*ty],
|
||||
(Bool(..) | IntRange(..) | NonExhaustive | Missing | Wildcard, _) => vec![],
|
||||
_ => panic!("Unexpected ctor {ctor:?} for type {self:?}"),
|
||||
|
|
@ -61,6 +66,7 @@ impl Ty {
|
|||
Ty::Enum(tys) => tys.iter().all(|ty| ty.is_empty()),
|
||||
Ty::BigStruct { arity, ty } => arity != 0 && ty.is_empty(),
|
||||
Ty::BigEnum { arity, ty } => arity == 0 || ty.is_empty(),
|
||||
Ty::NonExhaustiveEnum(..) => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -90,6 +96,19 @@ impl Ty {
|
|||
.collect(),
|
||||
non_exhaustive: false,
|
||||
},
|
||||
Ty::NonExhaustiveEnum(tys) => ConstructorSet::Variants {
|
||||
variants: tys
|
||||
.iter()
|
||||
.map(|ty| {
|
||||
if ty.is_empty() {
|
||||
VariantVisibility::Empty
|
||||
} else {
|
||||
VariantVisibility::Visible
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
non_exhaustive: true,
|
||||
},
|
||||
Ty::BigEnum { arity: 0, .. } => ConstructorSet::NoConstructors,
|
||||
Ty::BigEnum { arity, ty } => {
|
||||
let vis = if ty.is_empty() {
|
||||
|
|
@ -113,7 +132,9 @@ impl Ty {
|
|||
match (*self, ctor) {
|
||||
(Ty::Tuple(..), _) => Ok(()),
|
||||
(Ty::BigStruct { .. }, _) => write!(f, "BigStruct"),
|
||||
(Ty::Enum(..), Constructor::Variant(i)) => write!(f, "Enum::Variant{i}"),
|
||||
(Ty::Enum(..) | Ty::NonExhaustiveEnum(..), Constructor::Variant(i)) => {
|
||||
write!(f, "Enum::Variant{i}")
|
||||
}
|
||||
(Ty::BigEnum { .. }, Constructor::Variant(i)) => write!(f, "BigEnum::Variant{i}"),
|
||||
_ => write!(f, "{:?}::{:?}", self, ctor),
|
||||
}
|
||||
|
|
@ -126,10 +147,11 @@ pub(super) fn compute_match_usefulness<'p>(
|
|||
ty: Ty,
|
||||
scrut_validity: PlaceValidity,
|
||||
complexity_limit: usize,
|
||||
exhaustive_witnesses: bool,
|
||||
) -> Result<UsefulnessReport<'p, Cx>, ()> {
|
||||
init_tracing();
|
||||
rustc_pattern_analysis::usefulness::compute_match_usefulness(
|
||||
&Cx,
|
||||
&Cx { exhaustive_witnesses },
|
||||
arms,
|
||||
ty,
|
||||
scrut_validity,
|
||||
|
|
@ -138,7 +160,9 @@ pub(super) fn compute_match_usefulness<'p>(
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Cx;
|
||||
pub(super) struct Cx {
|
||||
exhaustive_witnesses: bool,
|
||||
}
|
||||
|
||||
/// The context for pattern analysis. Forwards anything interesting to `Ty` methods.
|
||||
impl PatCx for Cx {
|
||||
|
|
@ -153,6 +177,10 @@ impl PatCx for Cx {
|
|||
false
|
||||
}
|
||||
|
||||
fn exhaustive_witnesses(&self) -> bool {
|
||||
self.exhaustive_witnesses
|
||||
}
|
||||
|
||||
fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize {
|
||||
ty.sub_tys(ctor).len()
|
||||
}
|
||||
|
|
@ -219,16 +247,18 @@ macro_rules! pats {
|
|||
// Entrypoint
|
||||
// Parse `type; ..`
|
||||
($ty:expr; $($rest:tt)*) => {{
|
||||
#[allow(unused_imports)]
|
||||
#[allow(unused)]
|
||||
use rustc_pattern_analysis::{
|
||||
constructor::{Constructor, IntRange, MaybeInfiniteInt, RangeEnd},
|
||||
pat::DeconstructedPat,
|
||||
pat::{DeconstructedPat, IndexedPat},
|
||||
};
|
||||
let ty = $ty;
|
||||
// The heart of the macro is designed to push `IndexedPat`s into a `Vec`, so we work around
|
||||
// that.
|
||||
#[allow(unused)]
|
||||
let sub_tys = ::std::iter::repeat(&ty);
|
||||
let mut vec = Vec::new();
|
||||
#[allow(unused)]
|
||||
let mut vec: Vec<IndexedPat<_>> = Vec::new();
|
||||
pats!(@ctor(vec:vec, sub_tys:sub_tys, idx:0) $($rest)*);
|
||||
vec.into_iter().map(|ipat| ipat.pat).collect::<Vec<_>>()
|
||||
}};
|
||||
|
|
@ -263,6 +293,8 @@ macro_rules! pats {
|
|||
let ctor = Constructor::Wildcard;
|
||||
pats!(@pat($($args)*, ctor:ctor) $($rest)*)
|
||||
}};
|
||||
// Nothing
|
||||
(@ctor($($args:tt)*)) => {};
|
||||
|
||||
// Integers and int ranges
|
||||
(@ctor($($args:tt)*) $($start:literal)?..$end:literal $($rest:tt)*) => {{
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ fn check(patterns: &[DeconstructedPat<Cx>], complexity_limit: usize) -> Result<(
|
|||
let ty = *patterns[0].ty();
|
||||
let arms: Vec<_> =
|
||||
patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect();
|
||||
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, complexity_limit)
|
||||
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, complexity_limit, false)
|
||||
.map(|_report| ())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,16 +11,30 @@ use rustc_pattern_analysis::usefulness::PlaceValidity;
|
|||
mod common;
|
||||
|
||||
/// Analyze a match made of these patterns.
|
||||
fn check(patterns: Vec<DeconstructedPat<Cx>>) -> Vec<WitnessPat<Cx>> {
|
||||
let ty = *patterns[0].ty();
|
||||
fn run(
|
||||
ty: Ty,
|
||||
patterns: Vec<DeconstructedPat<Cx>>,
|
||||
exhaustive_witnesses: bool,
|
||||
) -> Vec<WitnessPat<Cx>> {
|
||||
let arms: Vec<_> =
|
||||
patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect();
|
||||
let report =
|
||||
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, usize::MAX)
|
||||
.unwrap();
|
||||
let report = compute_match_usefulness(
|
||||
arms.as_slice(),
|
||||
ty,
|
||||
PlaceValidity::ValidOnly,
|
||||
usize::MAX,
|
||||
exhaustive_witnesses,
|
||||
)
|
||||
.unwrap();
|
||||
report.non_exhaustiveness_witnesses
|
||||
}
|
||||
|
||||
/// Analyze a match made of these patterns. Panics if there are no patterns
|
||||
fn check(patterns: Vec<DeconstructedPat<Cx>>) -> Vec<WitnessPat<Cx>> {
|
||||
let ty = *patterns[0].ty();
|
||||
run(ty, patterns, true)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_exhaustive(patterns: Vec<DeconstructedPat<Cx>>) {
|
||||
let witnesses = check(patterns);
|
||||
|
|
@ -35,6 +49,26 @@ fn assert_non_exhaustive(patterns: Vec<DeconstructedPat<Cx>>) {
|
|||
assert!(!witnesses.is_empty())
|
||||
}
|
||||
|
||||
use WhichWitnesses::*;
|
||||
enum WhichWitnesses {
|
||||
AllOfThem,
|
||||
OnlySome,
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
/// We take the type as input to support empty matches.
|
||||
fn assert_witnesses(
|
||||
which: WhichWitnesses,
|
||||
ty: Ty,
|
||||
patterns: Vec<DeconstructedPat<Cx>>,
|
||||
expected: Vec<&str>,
|
||||
) {
|
||||
let exhaustive_wit = matches!(which, AllOfThem);
|
||||
let witnesses = run(ty, patterns, exhaustive_wit);
|
||||
let witnesses: Vec<_> = witnesses.iter().map(|w| format!("{w:?}")).collect();
|
||||
assert_eq!(witnesses, expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_int_ranges() {
|
||||
let ty = Ty::U8;
|
||||
|
|
@ -59,6 +93,8 @@ fn test_int_ranges() {
|
|||
|
||||
#[test]
|
||||
fn test_nested() {
|
||||
// enum E { A(bool), B(bool) }
|
||||
// ty = (E, E)
|
||||
let ty = Ty::BigStruct { arity: 2, ty: &Ty::BigEnum { arity: 2, ty: &Ty::Bool } };
|
||||
assert_non_exhaustive(pats!(ty;
|
||||
Struct(Variant.0, _),
|
||||
|
|
@ -78,10 +114,74 @@ fn test_nested() {
|
|||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_witnesses() {
|
||||
// TY = Option<bool>
|
||||
const TY: Ty = Ty::Enum(&[Ty::Bool, UNIT]);
|
||||
// ty = (Option<bool>, Option<bool>)
|
||||
let ty = Ty::Tuple(&[TY, TY]);
|
||||
assert_witnesses(AllOfThem, ty, vec![], vec!["(_, _)"]);
|
||||
assert_witnesses(
|
||||
OnlySome,
|
||||
ty,
|
||||
pats!(ty;
|
||||
(Variant.0(false), Variant.0(false)),
|
||||
),
|
||||
vec!["(Enum::Variant1(_), _)"],
|
||||
);
|
||||
assert_witnesses(
|
||||
AllOfThem,
|
||||
ty,
|
||||
pats!(ty;
|
||||
(Variant.0(false), Variant.0(false)),
|
||||
),
|
||||
vec![
|
||||
"(Enum::Variant0(false), Enum::Variant0(true))",
|
||||
"(Enum::Variant0(false), Enum::Variant1(_))",
|
||||
"(Enum::Variant0(true), _)",
|
||||
"(Enum::Variant1(_), _)",
|
||||
],
|
||||
);
|
||||
assert_witnesses(
|
||||
OnlySome,
|
||||
ty,
|
||||
pats!(ty;
|
||||
(_, Variant.0(false)),
|
||||
),
|
||||
vec!["(_, Enum::Variant1(_))"],
|
||||
);
|
||||
assert_witnesses(
|
||||
AllOfThem,
|
||||
ty,
|
||||
pats!(ty;
|
||||
(_, Variant.0(false)),
|
||||
),
|
||||
vec!["(_, Enum::Variant0(true))", "(_, Enum::Variant1(_))"],
|
||||
);
|
||||
|
||||
let ty = Ty::NonExhaustiveEnum(&[UNIT, UNIT, UNIT]);
|
||||
assert_witnesses(
|
||||
OnlySome,
|
||||
ty,
|
||||
pats!(ty;
|
||||
Variant.0,
|
||||
),
|
||||
vec!["_"],
|
||||
);
|
||||
assert_witnesses(
|
||||
AllOfThem,
|
||||
ty,
|
||||
pats!(ty;
|
||||
Variant.0,
|
||||
),
|
||||
vec!["Enum::Variant1(_)", "Enum::Variant2(_)", "_"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty() {
|
||||
// `TY = Result<bool, !>`
|
||||
const TY: Ty = Ty::Enum(&[Ty::Bool, Ty::Enum(&[])]);
|
||||
const TY: Ty = Ty::Enum(&[Ty::Bool, NEVER]);
|
||||
assert_exhaustive(pats!(TY;
|
||||
Variant.0,
|
||||
));
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ fn check(patterns: Vec<DeconstructedPat<Cx>>) -> Vec<Vec<usize>> {
|
|||
let arms: Vec<_> =
|
||||
patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect();
|
||||
let report =
|
||||
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, usize::MAX)
|
||||
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, usize::MAX, false)
|
||||
.unwrap();
|
||||
report.arm_intersections.into_iter().map(|bitset| bitset.iter().collect()).collect()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -720,7 +720,7 @@ fn incremental_verify_ich_failed<Tcx>(
|
|||
static INSIDE_VERIFY_PANIC: Cell<bool> = const { Cell::new(false) };
|
||||
};
|
||||
|
||||
let old_in_panic = INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.replace(true));
|
||||
let old_in_panic = INSIDE_VERIFY_PANIC.replace(true);
|
||||
|
||||
if old_in_panic {
|
||||
tcx.sess().dcx().emit_err(crate::error::Reentrant);
|
||||
|
|
@ -739,7 +739,7 @@ fn incremental_verify_ich_failed<Tcx>(
|
|||
panic!("Found unstable fingerprints for {dep_node:?}: {}", result());
|
||||
}
|
||||
|
||||
INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.set(old_in_panic));
|
||||
INSIDE_VERIFY_PANIC.set(old_in_panic);
|
||||
}
|
||||
|
||||
/// Ensure that either this query has all green inputs or been executed.
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ use rustc_hir::def::{self, *};
|
|||
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
|
||||
use rustc_index::bit_set::DenseBitSet;
|
||||
use rustc_metadata::creader::LoadedMacro;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::metadata::ModChild;
|
||||
use rustc_middle::ty::{Feed, Visibility};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind};
|
||||
use rustc_span::{Ident, Span, Symbol, kw, sym};
|
||||
use thin_vec::ThinVec;
|
||||
|
|
@ -46,30 +46,59 @@ type Res = def::Res<NodeId>;
|
|||
impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
||||
/// Defines `name` in namespace `ns` of module `parent` to be `def` if it is not yet defined;
|
||||
/// otherwise, reports an error.
|
||||
pub(crate) fn define_binding(
|
||||
pub(crate) fn define_binding_local(
|
||||
&mut self,
|
||||
parent: Module<'ra>,
|
||||
ident: Ident,
|
||||
ns: Namespace,
|
||||
binding: NameBinding<'ra>,
|
||||
) {
|
||||
if let Err(old_binding) = self.try_define(parent, ident, ns, binding, false) {
|
||||
if let Err(old_binding) = self.try_define_local(parent, ident, ns, binding, false) {
|
||||
self.report_conflict(parent, ident, ns, old_binding, binding);
|
||||
}
|
||||
}
|
||||
|
||||
fn define(
|
||||
fn define_local(
|
||||
&mut self,
|
||||
parent: Module<'ra>,
|
||||
ident: Ident,
|
||||
ns: Namespace,
|
||||
res: Res,
|
||||
vis: Visibility<impl Into<DefId>>,
|
||||
vis: Visibility,
|
||||
span: Span,
|
||||
expn_id: LocalExpnId,
|
||||
) {
|
||||
let binding = self.arenas.new_res_binding(res, vis.to_def_id(), span, expn_id);
|
||||
self.define_binding(parent, ident, ns, binding)
|
||||
self.define_binding_local(parent, ident, ns, binding);
|
||||
}
|
||||
|
||||
fn define_extern(
|
||||
&self,
|
||||
parent: Module<'ra>,
|
||||
ident: Ident,
|
||||
ns: Namespace,
|
||||
res: Res,
|
||||
vis: Visibility<DefId>,
|
||||
span: Span,
|
||||
expn_id: LocalExpnId,
|
||||
) {
|
||||
let binding = self.arenas.new_res_binding(res, vis, span, expn_id);
|
||||
// Even if underscore names cannot be looked up, we still need to add them to modules,
|
||||
// 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);
|
||||
parent.underscore_disambiguator.get()
|
||||
});
|
||||
if self
|
||||
.resolution_or_default(parent, key)
|
||||
.borrow_mut()
|
||||
.non_glob_binding
|
||||
.replace(binding)
|
||||
.is_some()
|
||||
{
|
||||
span_bug!(span, "an external binding was already defined");
|
||||
}
|
||||
}
|
||||
|
||||
/// Walks up the tree of definitions starting at `def_id`,
|
||||
|
|
@ -192,7 +221,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
visitor.parent_scope.macro_rules
|
||||
}
|
||||
|
||||
pub(crate) fn build_reduced_graph_external(&mut self, module: Module<'ra>) {
|
||||
pub(crate) fn build_reduced_graph_external(&self, module: Module<'ra>) {
|
||||
for child in self.tcx.module_children(module.def_id()) {
|
||||
let parent_scope = ParentScope::module(module, self);
|
||||
self.build_reduced_graph_for_external_crate_res(child, parent_scope)
|
||||
|
|
@ -201,7 +230,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
|
||||
/// Builds the reduced graph for a single item in an external crate.
|
||||
fn build_reduced_graph_for_external_crate_res(
|
||||
&mut self,
|
||||
&self,
|
||||
child: &ModChild,
|
||||
parent_scope: ParentScope<'ra>,
|
||||
) {
|
||||
|
|
@ -232,7 +261,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
_,
|
||||
)
|
||||
| Res::PrimTy(..)
|
||||
| Res::ToolMod => self.define(parent, ident, TypeNS, res, vis, span, expansion),
|
||||
| Res::ToolMod => self.define_extern(parent, ident, TypeNS, res, vis, span, expansion),
|
||||
Res::Def(
|
||||
DefKind::Fn
|
||||
| DefKind::AssocFn
|
||||
|
|
@ -241,9 +270,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
| DefKind::AssocConst
|
||||
| DefKind::Ctor(..),
|
||||
_,
|
||||
) => self.define(parent, ident, ValueNS, res, vis, span, expansion),
|
||||
) => self.define_extern(parent, ident, ValueNS, res, vis, span, expansion),
|
||||
Res::Def(DefKind::Macro(..), _) | Res::NonMacroAttr(..) => {
|
||||
self.define(parent, ident, MacroNS, res, vis, span, expansion)
|
||||
self.define_extern(parent, ident, MacroNS, res, vis, span, expansion)
|
||||
}
|
||||
Res::Def(
|
||||
DefKind::TyParam
|
||||
|
|
@ -452,8 +481,10 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
self.r.per_ns(|this, ns| {
|
||||
if !type_ns_only || ns == TypeNS {
|
||||
let key = BindingKey::new(target, ns);
|
||||
let mut resolution = this.resolution(current_module, key).borrow_mut();
|
||||
resolution.single_imports.insert(import);
|
||||
this.resolution_or_default(current_module, key)
|
||||
.borrow_mut()
|
||||
.single_imports
|
||||
.insert(import);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -709,7 +740,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
let expansion = parent_scope.expansion;
|
||||
|
||||
// Define a name in the type namespace if it is not anonymous.
|
||||
self.r.define(parent, ident, TypeNS, adt_res, adt_vis, adt_span, expansion);
|
||||
self.r.define_local(parent, ident, TypeNS, adt_res, adt_vis, adt_span, expansion);
|
||||
self.r.feed_visibility(feed, adt_vis);
|
||||
let def_id = feed.key();
|
||||
|
||||
|
|
@ -761,7 +792,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
}
|
||||
|
||||
ItemKind::Mod(_, ident, ref mod_kind) => {
|
||||
self.r.define(parent, ident, TypeNS, res, vis, sp, expansion);
|
||||
self.r.define_local(parent, ident, TypeNS, res, vis, sp, expansion);
|
||||
|
||||
if let ast::ModKind::Loaded(_, _, _, Err(_)) = mod_kind {
|
||||
self.r.mods_with_parse_errors.insert(def_id);
|
||||
|
|
@ -780,10 +811,10 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
ItemKind::Const(box ConstItem { ident, .. })
|
||||
| ItemKind::Delegation(box Delegation { ident, .. })
|
||||
| ItemKind::Static(box StaticItem { ident, .. }) => {
|
||||
self.r.define(parent, ident, ValueNS, res, vis, sp, expansion);
|
||||
self.r.define_local(parent, ident, ValueNS, res, vis, sp, expansion);
|
||||
}
|
||||
ItemKind::Fn(box Fn { ident, .. }) => {
|
||||
self.r.define(parent, ident, ValueNS, res, vis, sp, expansion);
|
||||
self.r.define_local(parent, ident, ValueNS, res, vis, sp, expansion);
|
||||
|
||||
// Functions introducing procedural macros reserve a slot
|
||||
// in the macro namespace as well (see #52225).
|
||||
|
|
@ -792,11 +823,11 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
|
||||
// These items live in the type namespace.
|
||||
ItemKind::TyAlias(box TyAlias { ident, .. }) | ItemKind::TraitAlias(ident, ..) => {
|
||||
self.r.define(parent, ident, TypeNS, res, vis, sp, expansion);
|
||||
self.r.define_local(parent, ident, TypeNS, res, vis, sp, expansion);
|
||||
}
|
||||
|
||||
ItemKind::Enum(ident, _, _) | ItemKind::Trait(box ast::Trait { ident, .. }) => {
|
||||
self.r.define(parent, ident, TypeNS, res, vis, sp, expansion);
|
||||
self.r.define_local(parent, ident, TypeNS, res, vis, sp, expansion);
|
||||
|
||||
self.parent_scope.module = self.r.new_local_module(
|
||||
Some(parent),
|
||||
|
|
@ -848,7 +879,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
let feed = self.r.feed(ctor_node_id);
|
||||
let ctor_def_id = feed.key();
|
||||
let ctor_res = self.res(ctor_def_id);
|
||||
self.r.define(parent, ident, ValueNS, ctor_res, ctor_vis, sp, expansion);
|
||||
self.r.define_local(parent, ident, ValueNS, ctor_res, ctor_vis, sp, expansion);
|
||||
self.r.feed_visibility(feed, ctor_vis);
|
||||
// We need the field visibility spans also for the constructor for E0603.
|
||||
self.insert_field_visibilities_local(ctor_def_id.to_def_id(), vdata.fields());
|
||||
|
|
@ -972,7 +1003,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
);
|
||||
}
|
||||
}
|
||||
self.r.define_binding(parent, ident, TypeNS, imported_binding);
|
||||
self.r.define_binding_local(parent, ident, TypeNS, imported_binding);
|
||||
}
|
||||
|
||||
/// Constructs the reduced graph for one foreign item.
|
||||
|
|
@ -989,7 +1020,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
let parent = self.parent_scope.module;
|
||||
let expansion = self.parent_scope.expansion;
|
||||
let vis = self.resolve_visibility(&item.vis);
|
||||
self.r.define(parent, ident, ns, self.res(def_id), vis, item.span, expansion);
|
||||
self.r.define_local(parent, ident, ns, self.res(def_id), vis, item.span, expansion);
|
||||
self.r.feed_visibility(feed, vis);
|
||||
}
|
||||
|
||||
|
|
@ -1072,7 +1103,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
if let Some(span) = import_all {
|
||||
let import = macro_use_import(self, span, false);
|
||||
self.r.potentially_unused_imports.push(import);
|
||||
module.for_each_child(self, |this, ident, ns, binding| {
|
||||
module.for_each_child_mut(self, |this, ident, ns, binding| {
|
||||
if ns == MacroNS {
|
||||
let import = if this.r.is_accessible_from(binding.vis, this.parent_scope.module)
|
||||
{
|
||||
|
|
@ -1237,7 +1268,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
});
|
||||
self.r.import_use_map.insert(import, Used::Other);
|
||||
let import_binding = self.r.import(binding, import);
|
||||
self.r.define_binding(self.r.graph_root, ident, MacroNS, import_binding);
|
||||
self.r.define_binding_local(self.r.graph_root, ident, MacroNS, import_binding);
|
||||
} else {
|
||||
self.r.check_reserved_macro_name(ident, res);
|
||||
self.insert_unused_macro(ident, def_id, item.id);
|
||||
|
|
@ -1265,7 +1296,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
if !vis.is_public() {
|
||||
self.insert_unused_macro(ident, def_id, item.id);
|
||||
}
|
||||
self.r.define(module, ident, MacroNS, res, vis, span, expansion);
|
||||
self.r.define_local(module, ident, MacroNS, res, vis, span, expansion);
|
||||
self.r.feed_visibility(feed, vis);
|
||||
self.parent_scope.macro_rules
|
||||
}
|
||||
|
|
@ -1401,7 +1432,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
if ctxt == AssocCtxt::Trait {
|
||||
let parent = self.parent_scope.module;
|
||||
let expansion = self.parent_scope.expansion;
|
||||
self.r.define(parent, ident, ns, self.res(def_id), vis, item.span, expansion);
|
||||
self.r.define_local(parent, ident, ns, self.res(def_id), vis, item.span, expansion);
|
||||
} else if !matches!(&item.kind, AssocItemKind::Delegation(deleg) if deleg.from_glob)
|
||||
&& ident.name != kw::Underscore
|
||||
{
|
||||
|
|
@ -1489,7 +1520,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
let feed = self.r.feed(variant.id);
|
||||
let def_id = feed.key();
|
||||
let vis = self.resolve_visibility(&variant.vis);
|
||||
self.r.define(parent, ident, TypeNS, self.res(def_id), vis, variant.span, expn_id);
|
||||
self.r.define_local(parent, ident, TypeNS, self.res(def_id), vis, variant.span, expn_id);
|
||||
self.r.feed_visibility(feed, vis);
|
||||
|
||||
// If the variant is marked as non_exhaustive then lower the visibility to within the crate.
|
||||
|
|
@ -1505,7 +1536,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
let feed = self.r.feed(ctor_node_id);
|
||||
let ctor_def_id = feed.key();
|
||||
let ctor_res = self.res(ctor_def_id);
|
||||
self.r.define(parent, ident, ValueNS, ctor_res, ctor_vis, variant.span, expn_id);
|
||||
self.r.define_local(parent, ident, ValueNS, ctor_res, ctor_vis, variant.span, expn_id);
|
||||
self.r.feed_visibility(feed, ctor_vis);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -509,9 +509,7 @@ impl Resolver<'_, '_> {
|
|||
let mut check_redundant_imports = FxIndexSet::default();
|
||||
for module in self.arenas.local_modules().iter() {
|
||||
for (_key, resolution) in self.resolutions(*module).borrow().iter() {
|
||||
let resolution = resolution.borrow();
|
||||
|
||||
if let Some(binding) = resolution.best_binding()
|
||||
if let Some(binding) = resolution.borrow().best_binding()
|
||||
&& let NameBindingKind::Import { import, .. } = binding.kind
|
||||
&& let ImportKind::Single { id, .. } = import.kind
|
||||
{
|
||||
|
|
|
|||
|
|
@ -523,7 +523,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
}
|
||||
|
||||
pub(crate) fn add_module_candidates(
|
||||
&mut self,
|
||||
&self,
|
||||
module: Module<'ra>,
|
||||
names: &mut Vec<TypoSuggestion>,
|
||||
filter_fn: &impl Fn(Res) -> bool,
|
||||
|
|
@ -1150,7 +1150,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
}
|
||||
|
||||
fn lookup_import_candidates_from_module<FilterFn>(
|
||||
&mut self,
|
||||
&self,
|
||||
lookup_ident: Ident,
|
||||
namespace: Namespace,
|
||||
parent_scope: &ParentScope<'ra>,
|
||||
|
|
@ -2659,10 +2659,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
return None;
|
||||
}
|
||||
|
||||
let resolutions = self.resolutions(crate_module).borrow();
|
||||
let binding_key = BindingKey::new(ident, MacroNS);
|
||||
let resolution = resolutions.get(&binding_key)?;
|
||||
let binding = resolution.borrow().binding()?;
|
||||
let binding = self.resolution(crate_module, binding_key)?.binding()?;
|
||||
let Res::Def(DefKind::Macro(MacroKind::Bang), _) = binding.res() else {
|
||||
return None;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -114,9 +114,7 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> {
|
|||
/// including their whole reexport chains.
|
||||
fn set_bindings_effective_visibilities(&mut self, module_id: LocalDefId) {
|
||||
let module = self.r.expect_module(module_id.to_def_id());
|
||||
let resolutions = self.r.resolutions(module);
|
||||
|
||||
for (_, name_resolution) in resolutions.borrow().iter() {
|
||||
for (_, name_resolution) in self.r.resolutions(module).borrow().iter() {
|
||||
let Some(mut binding) = name_resolution.borrow().binding() else {
|
||||
continue;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -848,8 +848,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
};
|
||||
|
||||
let key = BindingKey::new(ident, ns);
|
||||
let resolution =
|
||||
self.resolution(module, key).try_borrow_mut().map_err(|_| (Determined, Weak::No))?; // This happens when there is a cycle of imports.
|
||||
// `try_borrow_mut` is required to ensure exclusive access, even if the resulting binding
|
||||
// doesn't need to be mutable. It will fail when there is a cycle of imports, and without
|
||||
// the exclusive access infinite recursion will crash the compiler with stack overflow.
|
||||
let resolution = &*self
|
||||
.resolution_or_default(module, key)
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| (Determined, Weak::No))?;
|
||||
|
||||
// If the primary binding is unusable, search further and return the shadowed glob
|
||||
// binding if it exists. What we really want here is having two separate scopes in
|
||||
|
|
|
|||
|
|
@ -334,8 +334,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
}
|
||||
|
||||
/// Define the name or return the existing binding if there is a collision.
|
||||
/// `update` indicates if the definition is a redefinition of an existing binding.
|
||||
pub(crate) fn try_define(
|
||||
pub(crate) fn try_define_local(
|
||||
&mut self,
|
||||
module: Module<'ra>,
|
||||
ident: Ident,
|
||||
|
|
@ -353,7 +352,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
module.underscore_disambiguator.update(|d| d + 1);
|
||||
module.underscore_disambiguator.get()
|
||||
});
|
||||
self.update_resolution(module, key, warn_ambiguity, |this, resolution| {
|
||||
self.update_local_resolution(module, key, warn_ambiguity, |this, resolution| {
|
||||
if let Some(old_binding) = resolution.best_binding() {
|
||||
if res == Res::Err && old_binding.res() != Res::Err {
|
||||
// Do not override real bindings with `Res::Err`s from error recovery.
|
||||
|
|
@ -456,7 +455,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
|
||||
// Use `f` to mutate the resolution of the name in the module.
|
||||
// If the resolution becomes a success, define it in the module's glob importers.
|
||||
fn update_resolution<T, F>(
|
||||
fn update_local_resolution<T, F>(
|
||||
&mut self,
|
||||
module: Module<'ra>,
|
||||
key: BindingKey,
|
||||
|
|
@ -469,7 +468,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
// Ensure that `resolution` isn't borrowed when defining in the module's glob importers,
|
||||
// during which the resolution might end up getting re-defined via a glob cycle.
|
||||
let (binding, t, warn_ambiguity) = {
|
||||
let resolution = &mut *self.resolution(module, key).borrow_mut();
|
||||
let resolution = &mut *self.resolution_or_default(module, key).borrow_mut();
|
||||
let old_binding = resolution.binding();
|
||||
|
||||
let t = f(self, resolution);
|
||||
|
|
@ -497,7 +496,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
};
|
||||
if self.is_accessible_from(binding.vis, scope) {
|
||||
let imported_binding = self.import(binding, *import);
|
||||
let _ = self.try_define(
|
||||
let _ = self.try_define_local(
|
||||
import.parent_scope.module,
|
||||
ident,
|
||||
key.ns,
|
||||
|
|
@ -523,11 +522,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
let dummy_binding = self.import(dummy_binding, import);
|
||||
self.per_ns(|this, ns| {
|
||||
let module = import.parent_scope.module;
|
||||
let _ = this.try_define(module, target, ns, dummy_binding, false);
|
||||
let _ = this.try_define_local(module, target, ns, dummy_binding, false);
|
||||
// Don't remove underscores from `single_imports`, they were never added.
|
||||
if target.name != kw::Underscore {
|
||||
let key = BindingKey::new(target, ns);
|
||||
this.update_resolution(module, key, false, |_, resolution| {
|
||||
this.update_local_resolution(module, key, false, |_, resolution| {
|
||||
resolution.single_imports.swap_remove(&import);
|
||||
})
|
||||
}
|
||||
|
|
@ -651,7 +650,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
for module in self.arenas.local_modules().iter() {
|
||||
for (key, resolution) in self.resolutions(*module).borrow().iter() {
|
||||
let resolution = resolution.borrow();
|
||||
|
||||
let Some(binding) = resolution.best_binding() else { continue };
|
||||
|
||||
if let NameBindingKind::Import { import, .. } = binding.kind
|
||||
|
|
@ -903,14 +901,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
}
|
||||
// We need the `target`, `source` can be extracted.
|
||||
let imported_binding = this.import(binding, import);
|
||||
this.define_binding(parent, target, ns, imported_binding);
|
||||
this.define_binding_local(parent, target, ns, imported_binding);
|
||||
PendingBinding::Ready(Some(imported_binding))
|
||||
}
|
||||
Err(Determinacy::Determined) => {
|
||||
// Don't remove underscores from `single_imports`, they were never added.
|
||||
if target.name != kw::Underscore {
|
||||
let key = BindingKey::new(target, ns);
|
||||
this.update_resolution(parent, key, false, |_, resolution| {
|
||||
this.update_local_resolution(parent, key, false, |_, resolution| {
|
||||
resolution.single_imports.swap_remove(&import);
|
||||
});
|
||||
}
|
||||
|
|
@ -1202,41 +1200,39 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
});
|
||||
|
||||
return if all_ns_failed {
|
||||
let resolutions = match module {
|
||||
ModuleOrUniformRoot::Module(module) => Some(self.resolutions(module).borrow()),
|
||||
_ => None,
|
||||
};
|
||||
let resolutions = resolutions.as_ref().into_iter().flat_map(|r| r.iter());
|
||||
let names = resolutions
|
||||
.filter_map(|(BindingKey { ident: i, .. }, resolution)| {
|
||||
if i.name == ident.name {
|
||||
return None;
|
||||
} // Never suggest the same name
|
||||
match *resolution.borrow() {
|
||||
ref resolution
|
||||
if let Some(name_binding) = resolution.best_binding() =>
|
||||
{
|
||||
match name_binding.kind {
|
||||
NameBindingKind::Import { binding, .. } => {
|
||||
match binding.kind {
|
||||
// Never suggest the name that has binding error
|
||||
// i.e., the name that cannot be previously resolved
|
||||
NameBindingKind::Res(Res::Err) => None,
|
||||
_ => Some(i.name),
|
||||
let names = match module {
|
||||
ModuleOrUniformRoot::Module(module) => {
|
||||
self.resolutions(module)
|
||||
.borrow()
|
||||
.iter()
|
||||
.filter_map(|(BindingKey { ident: i, .. }, resolution)| {
|
||||
if i.name == ident.name {
|
||||
return None;
|
||||
} // Never suggest the same name
|
||||
|
||||
let resolution = resolution.borrow();
|
||||
if let Some(name_binding) = resolution.best_binding() {
|
||||
match name_binding.kind {
|
||||
NameBindingKind::Import { binding, .. } => {
|
||||
match binding.kind {
|
||||
// Never suggest the name that has binding error
|
||||
// i.e., the name that cannot be previously resolved
|
||||
NameBindingKind::Res(Res::Err) => None,
|
||||
_ => Some(i.name),
|
||||
}
|
||||
}
|
||||
_ => Some(i.name),
|
||||
}
|
||||
_ => Some(i.name),
|
||||
} else if resolution.single_imports.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(i.name)
|
||||
}
|
||||
}
|
||||
NameResolution { ref single_imports, .. }
|
||||
if single_imports.is_empty() =>
|
||||
{
|
||||
None
|
||||
}
|
||||
_ => Some(i.name),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<Symbol>>();
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
_ => Vec::new(),
|
||||
};
|
||||
|
||||
let lev_suggestion =
|
||||
find_best_match_for_name(&names, ident.name, None).map(|suggestion| {
|
||||
|
|
@ -1517,10 +1513,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
let imported_binding = self.import(binding, import);
|
||||
let warn_ambiguity = self
|
||||
.resolution(import.parent_scope.module, key)
|
||||
.borrow()
|
||||
.binding()
|
||||
.and_then(|r| r.binding())
|
||||
.is_some_and(|binding| binding.warn_ambiguity_recursive());
|
||||
let _ = self.try_define(
|
||||
let _ = self.try_define_local(
|
||||
import.parent_scope.module,
|
||||
key.ident,
|
||||
key.ns,
|
||||
|
|
|
|||
|
|
@ -3449,8 +3449,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
};
|
||||
ident.span.normalize_to_macros_2_0_and_adjust(module.expansion);
|
||||
let key = BindingKey::new(ident, ns);
|
||||
let mut binding =
|
||||
self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.best_binding());
|
||||
let mut binding = self.r.resolution(module, key).and_then(|r| r.best_binding());
|
||||
debug!(?binding);
|
||||
if binding.is_none() {
|
||||
// We could not find the trait item in the correct namespace.
|
||||
|
|
@ -3461,8 +3460,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
_ => ns,
|
||||
};
|
||||
let key = BindingKey::new(ident, ns);
|
||||
binding =
|
||||
self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.best_binding());
|
||||
binding = self.r.resolution(module, key).and_then(|r| r.best_binding());
|
||||
debug!(?binding);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1461,15 +1461,17 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
|
||||
self.resolve_path(mod_path, None, None)
|
||||
{
|
||||
let resolutions = self.r.resolutions(module).borrow();
|
||||
let targets: Vec<_> = resolutions
|
||||
let targets: Vec<_> = self
|
||||
.r
|
||||
.resolutions(module)
|
||||
.borrow()
|
||||
.iter()
|
||||
.filter_map(|(key, resolution)| {
|
||||
resolution
|
||||
.borrow()
|
||||
.best_binding()
|
||||
.map(|binding| binding.res())
|
||||
.and_then(|res| if filter_fn(res) { Some((key, res)) } else { None })
|
||||
.and_then(|res| if filter_fn(res) { Some((*key, res)) } else { None })
|
||||
})
|
||||
.collect();
|
||||
if let [target] = targets.as_slice() {
|
||||
|
|
@ -2300,8 +2302,9 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
return None;
|
||||
}
|
||||
|
||||
let resolutions = self.r.resolutions(*module);
|
||||
let targets = resolutions
|
||||
let targets = self
|
||||
.r
|
||||
.resolutions(*module)
|
||||
.borrow()
|
||||
.iter()
|
||||
.filter_map(|(key, res)| {
|
||||
|
|
@ -2630,7 +2633,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
false
|
||||
}
|
||||
|
||||
fn find_module(&mut self, def_id: DefId) -> Option<(Module<'ra>, ImportSuggestion)> {
|
||||
fn find_module(&self, def_id: DefId) -> Option<(Module<'ra>, ImportSuggestion)> {
|
||||
let mut result = None;
|
||||
let mut seen_modules = FxHashSet::default();
|
||||
let root_did = self.r.graph_root.def_id();
|
||||
|
|
@ -2687,7 +2690,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
result
|
||||
}
|
||||
|
||||
fn collect_enum_ctors(&mut self, def_id: DefId) -> Option<Vec<(Path, DefId, CtorKind)>> {
|
||||
fn collect_enum_ctors(&self, def_id: DefId) -> Option<Vec<(Path, DefId, CtorKind)>> {
|
||||
self.find_module(def_id).map(|(enum_module, enum_import_suggestion)| {
|
||||
let mut variants = Vec::new();
|
||||
enum_module.for_each_child(self.r, |_, ident, _, name_binding| {
|
||||
|
|
@ -2704,7 +2707,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
|
||||
/// Adds a suggestion for using an enum's variant when an enum is used instead.
|
||||
fn suggest_using_enum_variant(
|
||||
&mut self,
|
||||
&self,
|
||||
err: &mut Diag<'_>,
|
||||
source: PathSource<'_, '_, '_>,
|
||||
def_id: DefId,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
#![recursion_limit = "256"]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::cell::{Cell, Ref, RefCell};
|
||||
use std::collections::BTreeSet;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
|
@ -656,11 +656,23 @@ impl<'ra> ModuleData<'ra> {
|
|||
}
|
||||
|
||||
impl<'ra> Module<'ra> {
|
||||
fn for_each_child<'tcx, R, F>(self, resolver: &mut R, mut f: F)
|
||||
where
|
||||
R: AsMut<Resolver<'ra, 'tcx>>,
|
||||
F: FnMut(&mut R, Ident, Namespace, NameBinding<'ra>),
|
||||
{
|
||||
fn for_each_child<'tcx, R: AsRef<Resolver<'ra, 'tcx>>>(
|
||||
self,
|
||||
resolver: &R,
|
||||
mut f: impl FnMut(&R, Ident, Namespace, NameBinding<'ra>),
|
||||
) {
|
||||
for (key, name_resolution) in resolver.as_ref().resolutions(self).borrow().iter() {
|
||||
if let Some(binding) = name_resolution.borrow().best_binding() {
|
||||
f(resolver, key.ident, key.ns, binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn for_each_child_mut<'tcx, R: AsMut<Resolver<'ra, 'tcx>>>(
|
||||
self,
|
||||
resolver: &mut R,
|
||||
mut f: impl FnMut(&mut R, Ident, Namespace, NameBinding<'ra>),
|
||||
) {
|
||||
for (key, name_resolution) in resolver.as_mut().resolutions(self).borrow().iter() {
|
||||
if let Some(binding) = name_resolution.borrow().best_binding() {
|
||||
f(resolver, key.ident, key.ns, binding);
|
||||
|
|
@ -669,10 +681,7 @@ impl<'ra> Module<'ra> {
|
|||
}
|
||||
|
||||
/// This modifies `self` in place. The traits will be stored in `self.traits`.
|
||||
fn ensure_traits<'tcx, R>(self, resolver: &mut R)
|
||||
where
|
||||
R: AsMut<Resolver<'ra, 'tcx>>,
|
||||
{
|
||||
fn ensure_traits<'tcx>(self, resolver: &impl AsRef<Resolver<'ra, 'tcx>>) {
|
||||
let mut traits = self.traits.borrow_mut();
|
||||
if traits.is_none() {
|
||||
let mut collected_traits = Vec::new();
|
||||
|
|
@ -681,7 +690,7 @@ impl<'ra> Module<'ra> {
|
|||
return;
|
||||
}
|
||||
if let Res::Def(DefKind::Trait | DefKind::TraitAlias, def_id) = binding.res() {
|
||||
collected_traits.push((name, binding, r.as_mut().get_module(def_id)))
|
||||
collected_traits.push((name, binding, r.as_ref().get_module(def_id)))
|
||||
}
|
||||
});
|
||||
*traits = Some(collected_traits.into_boxed_slice());
|
||||
|
|
@ -1340,6 +1349,12 @@ impl<'ra, 'tcx> AsMut<Resolver<'ra, 'tcx>> for Resolver<'ra, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'ra, 'tcx> AsRef<Resolver<'ra, 'tcx>> for Resolver<'ra, 'tcx> {
|
||||
fn as_ref(&self) -> &Resolver<'ra, 'tcx> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Resolver<'_, 'tcx> {
|
||||
fn opt_local_def_id(&self, node: NodeId) -> Option<LocalDefId> {
|
||||
self.opt_feed(node).map(|f| f.key())
|
||||
|
|
@ -1861,7 +1876,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
// We don't reject trait aliases (`trait_module == None`) because we don't have access to their
|
||||
// associated items.
|
||||
fn trait_may_have_item(
|
||||
&mut self,
|
||||
&self,
|
||||
trait_module: Option<Module<'ra>>,
|
||||
assoc_item: Option<(Symbol, Namespace)>,
|
||||
) -> bool {
|
||||
|
|
@ -1893,7 +1908,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
import_ids
|
||||
}
|
||||
|
||||
fn resolutions(&mut self, module: Module<'ra>) -> &'ra Resolutions<'ra> {
|
||||
fn resolutions(&self, module: Module<'ra>) -> &'ra Resolutions<'ra> {
|
||||
if module.populate_on_access.get() {
|
||||
module.populate_on_access.set(false);
|
||||
self.build_reduced_graph_external(module);
|
||||
|
|
@ -1902,12 +1917,19 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
}
|
||||
|
||||
fn resolution(
|
||||
&mut self,
|
||||
&self,
|
||||
module: Module<'ra>,
|
||||
key: BindingKey,
|
||||
) -> Option<Ref<'ra, NameResolution<'ra>>> {
|
||||
self.resolutions(module).borrow().get(&key).map(|resolution| resolution.borrow())
|
||||
}
|
||||
|
||||
fn resolution_or_default(
|
||||
&self,
|
||||
module: Module<'ra>,
|
||||
key: BindingKey,
|
||||
) -> &'ra RefCell<NameResolution<'ra>> {
|
||||
*self
|
||||
.resolutions(module)
|
||||
self.resolutions(module)
|
||||
.borrow_mut()
|
||||
.entry(key)
|
||||
.or_insert_with(|| self.arenas.alloc_name_resolution())
|
||||
|
|
|
|||
|
|
@ -511,7 +511,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
|
|||
}
|
||||
|
||||
fn glob_delegation_suffixes(
|
||||
&mut self,
|
||||
&self,
|
||||
trait_def_id: DefId,
|
||||
impl_def_id: LocalDefId,
|
||||
) -> Result<Vec<(Ident, Option<Ident>)>, Indeterminate> {
|
||||
|
|
|
|||
|
|
@ -313,6 +313,7 @@ impl Session {
|
|||
|| self.opts.unstable_opts.query_dep_graph
|
||||
|| self.opts.unstable_opts.dump_mir.is_some()
|
||||
|| self.opts.unstable_opts.unpretty.is_some()
|
||||
|| self.prof.is_args_recording_enabled()
|
||||
|| self.opts.output_types.contains_key(&OutputType::Mir)
|
||||
|| std::env::var_os("RUSTC_LOG").is_some()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ pub(crate) fn target() -> Target {
|
|||
let mut base = base::linux_musl::opts();
|
||||
base.max_atomic_width = Some(128);
|
||||
base.supports_xray = true;
|
||||
base.features = "+v8a".into();
|
||||
base.features = "+v8a,+outline-atomics".into();
|
||||
base.stack_probes = StackProbeType::Inline;
|
||||
base.supported_sanitizers = SanitizerSet::ADDRESS
|
||||
| SanitizerSet::CFI
|
||||
|
|
|
|||
|
|
@ -80,8 +80,11 @@ fn sizedness_constraint_for_ty<'tcx>(
|
|||
|
||||
fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness {
|
||||
match tcx.hir_node_by_def_id(def_id) {
|
||||
hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.defaultness,
|
||||
hir::Node::ImplItem(hir::ImplItem { defaultness, .. })
|
||||
hir::Node::Item(hir::Item {
|
||||
kind: hir::ItemKind::Impl(hir::Impl { defaultness, of_trait: Some(_), .. }),
|
||||
..
|
||||
})
|
||||
| hir::Node::ImplItem(hir::ImplItem { defaultness, .. })
|
||||
| hir::Node::TraitItem(hir::TraitItem { defaultness, .. }) => *defaultness,
|
||||
node => {
|
||||
bug!("`defaultness` called on {:?}", node);
|
||||
|
|
|
|||
|
|
@ -400,8 +400,12 @@ pub fn structurally_relate_tys<I: Interner, R: TypeRelation<I>>(
|
|||
(ty::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => Ok(a),
|
||||
|
||||
(ty::Adt(a_def, a_args), ty::Adt(b_def, b_args)) if a_def == b_def => {
|
||||
let args = relation.relate_item_args(a_def.def_id(), a_args, b_args)?;
|
||||
Ok(Ty::new_adt(cx, a_def, args))
|
||||
Ok(if a_args.is_empty() {
|
||||
a
|
||||
} else {
|
||||
let args = relation.relate_item_args(a_def.def_id(), a_args, b_args)?;
|
||||
if args == a_args { a } else { Ty::new_adt(cx, a_def, args) }
|
||||
})
|
||||
}
|
||||
|
||||
(ty::Foreign(a_id), ty::Foreign(b_id)) if a_id == b_id => Ok(Ty::new_foreign(cx, a_id)),
|
||||
|
|
@ -515,8 +519,12 @@ pub fn structurally_relate_tys<I: Interner, R: TypeRelation<I>>(
|
|||
}
|
||||
|
||||
(ty::FnDef(a_def_id, a_args), ty::FnDef(b_def_id, b_args)) if a_def_id == b_def_id => {
|
||||
let args = relation.relate_item_args(a_def_id, a_args, b_args)?;
|
||||
Ok(Ty::new_fn_def(cx, a_def_id, args))
|
||||
Ok(if a_args.is_empty() {
|
||||
a
|
||||
} else {
|
||||
let args = relation.relate_item_args(a_def_id, a_args, b_args)?;
|
||||
if args == a_args { a } else { Ty::new_fn_def(cx, a_def_id, args) }
|
||||
})
|
||||
}
|
||||
|
||||
(ty::FnPtr(a_sig_tys, a_hdr), ty::FnPtr(b_sig_tys, b_hdr)) => {
|
||||
|
|
|
|||
|
|
@ -78,9 +78,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "dlmalloc"
|
||||
version = "0.2.9"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d01597dde41c0b9da50d5f8c219023d63d8f27f39a27095070fd191fddc83891"
|
||||
checksum = "fa3a2dbee57b69fbb5dbe852fa9c0925697fb0c7fbcb1593e90e5ffaedf13d51"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
|
|
@ -90,11 +90,10 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fortanix-sgx-abi"
|
||||
version = "0.5.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57cafc2274c10fab234f176b25903ce17e690fca7597090d50880e047a0389c5"
|
||||
checksum = "5efc85edd5b83e8394f4371dd0da6859dff63dd387dab8568fece6af4cde6f84"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-core",
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -426,8 +426,10 @@ pub macro debug_assert_matches($($arg:tt)*) {
|
|||
#[macro_export]
|
||||
#[stable(feature = "matches_macro", since = "1.42.0")]
|
||||
#[rustc_diagnostic_item = "matches_macro"]
|
||||
#[allow_internal_unstable(non_exhaustive_omitted_patterns_lint, stmt_expr_attributes)]
|
||||
macro_rules! matches {
|
||||
($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
|
||||
#[allow(non_exhaustive_omitted_patterns)]
|
||||
match $expression {
|
||||
$pattern $(if $guard)? => true,
|
||||
_ => false
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@
|
|||
#![feature(min_specialization)]
|
||||
#![feature(never_type)]
|
||||
#![feature(next_index)]
|
||||
#![feature(non_exhaustive_omitted_patterns_lint)]
|
||||
#![feature(numfmt)]
|
||||
#![feature(pattern)]
|
||||
#![feature(pointer_is_aligned_to)]
|
||||
|
|
|
|||
|
|
@ -213,3 +213,9 @@ fn _expression() {
|
|||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[deny(non_exhaustive_omitted_patterns)]
|
||||
fn _matches_does_not_trigger_non_exhaustive_omitted_patterns_lint(o: core::sync::atomic::Ordering) {
|
||||
// Ordering is a #[non_exhaustive] enum from a separate crate
|
||||
let _m = matches!(o, core::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,10 +63,10 @@ rand = { version = "0.9.0", default-features = false, features = ["alloc"] }
|
|||
rand_xorshift = "0.4.0"
|
||||
|
||||
[target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "xous", all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies]
|
||||
dlmalloc = { version = "0.2.4", features = ['rustc-dep-of-std'] }
|
||||
dlmalloc = { version = "0.2.10", features = ['rustc-dep-of-std'] }
|
||||
|
||||
[target.x86_64-fortanix-unknown-sgx.dependencies]
|
||||
fortanix-sgx-abi = { version = "0.5.0", features = [
|
||||
fortanix-sgx-abi = { version = "0.6.1", features = [
|
||||
'rustc-dep-of-std',
|
||||
], public = true }
|
||||
|
||||
|
|
|
|||
|
|
@ -617,7 +617,7 @@ impl Error for JoinPathsError {
|
|||
/// # Unix
|
||||
///
|
||||
/// - Returns the value of the 'HOME' environment variable if it is set
|
||||
/// (including to an empty string).
|
||||
/// (and not an empty string).
|
||||
/// - Otherwise, it tries to determine the home directory by invoking the `getpwuid_r` function
|
||||
/// using the UID of the current user. An empty home directory field returned from the
|
||||
/// `getpwuid_r` function is considered to be a valid value.
|
||||
|
|
|
|||
|
|
@ -267,7 +267,7 @@ pub fn send(event_set: u64, tcs: Option<Tcs>) -> IoResult<()> {
|
|||
/// Usercall `insecure_time`. See the ABI documentation for more information.
|
||||
#[unstable(feature = "sgx_platform", issue = "56975")]
|
||||
pub fn insecure_time() -> Duration {
|
||||
let t = unsafe { raw::insecure_time() };
|
||||
let t = unsafe { raw::insecure_time().0 };
|
||||
Duration::new(t / 1_000_000_000, (t % 1_000_000_000) as _)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -633,7 +633,10 @@ pub fn temp_dir() -> PathBuf {
|
|||
}
|
||||
|
||||
pub fn home_dir() -> Option<PathBuf> {
|
||||
return crate::env::var_os("HOME").or_else(|| unsafe { fallback() }).map(PathBuf::from);
|
||||
return crate::env::var_os("HOME")
|
||||
.filter(|s| !s.is_empty())
|
||||
.or_else(|| unsafe { fallback() })
|
||||
.map(PathBuf::from);
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
|
|
|
|||
|
|
@ -1399,6 +1399,11 @@ where
|
|||
}
|
||||
|
||||
/// The internal representation of a `Thread` handle
|
||||
///
|
||||
/// We explicitly set the alignment for our guarantee in Thread::into_raw. This
|
||||
/// allows applications to stuff extra metadata bits into the alignment, which
|
||||
/// can be rather useful when working with atomics.
|
||||
#[repr(align(8))]
|
||||
struct Inner {
|
||||
name: Option<ThreadNameString>,
|
||||
id: ThreadId,
|
||||
|
|
@ -1582,7 +1587,8 @@ impl Thread {
|
|||
/// Consumes the `Thread`, returning a raw pointer.
|
||||
///
|
||||
/// To avoid a memory leak the pointer must be converted
|
||||
/// back into a `Thread` using [`Thread::from_raw`].
|
||||
/// back into a `Thread` using [`Thread::from_raw`]. The pointer is
|
||||
/// guaranteed to be aligned to at least 8 bytes.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
|
|||
|
|
@ -191,6 +191,16 @@ unsafe extern "C-unwind" {
|
|||
// #[cfg_attr(test, assert_instr(throw, TAG = 0, ptr = core::ptr::null_mut()))]
|
||||
#[inline]
|
||||
#[unstable(feature = "wasm_exception_handling_intrinsics", issue = "122465")]
|
||||
// FIXME: Since this instruction unwinds, `core` built with `-C panic=unwind`
|
||||
// cannot be linked with `-C panic=abort` programs. But that's not
|
||||
// entirely supported anyway, because runtimes without EH support won't
|
||||
// be able to handle `try` blocks in `-C panic=unwind` crates either.
|
||||
// We ship `-C panic=abort` `core`, so this doesn't affect users
|
||||
// directly. Resolving this will likely require patching out both `try`
|
||||
// and `throw` instructions, at which point we can look into whitelisting
|
||||
// this function in the compiler to allow linking.
|
||||
// See https://github.com/rust-lang/rust/issues/118168.
|
||||
#[allow(ffi_unwind_calls)]
|
||||
pub unsafe fn throw<const TAG: i32>(ptr: *mut u8) -> ! {
|
||||
static_assert!(TAG == 0); // LLVM only supports tag 0 == C++ right now.
|
||||
wasm_throw(TAG, ptr)
|
||||
|
|
|
|||
|
|
@ -34,22 +34,12 @@ pub macro link_dylib {
|
|||
|
||||
#[cfg(feature = "windows_raw_dylib")]
|
||||
pub macro link($($tt:tt)*) {
|
||||
$crate::link_raw_dylib!($($tt)*)
|
||||
$crate::link_raw_dylib!($($tt)*);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "windows_raw_dylib"))]
|
||||
pub macro link {
|
||||
($library:literal $abi:literal $($link_name:literal)? $(#[$doc:meta])? fn $($function:tt)*) => (
|
||||
// Note: the windows-targets crate uses a pre-built Windows.lib import library which we don't
|
||||
// have in this repo. So instead we always link kernel32.lib and add the rest of the import
|
||||
// libraries below by using an empty extern block. This works because extern blocks are not
|
||||
// connected to the library given in the #[link] attribute.
|
||||
#[link(name = "kernel32")]
|
||||
unsafe extern $abi {
|
||||
$(#[link_name=$link_name])?
|
||||
pub fn $($function)*;
|
||||
}
|
||||
)
|
||||
pub macro link($($tt:tt)*) {
|
||||
$crate::link_dylib!($($tt)*);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "windows_raw_dylib"))]
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ const IGNORED_RULES_FOR_STD_AND_RUSTC: &[&str] = &[
|
|||
"too_many_arguments",
|
||||
"needless_lifetimes", // people want to keep the lifetimes
|
||||
"wrong_self_convention",
|
||||
"approx_constant", // libcore is what defines those
|
||||
];
|
||||
|
||||
fn lint_args(builder: &Builder<'_>, config: &LintConfig, ignored_rules: &[&str]) -> Vec<String> {
|
||||
|
|
|
|||
|
|
@ -597,11 +597,6 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car
|
|||
|
||||
let mut features = String::new();
|
||||
|
||||
if stage != 0 && builder.config.default_codegen_backend(target).as_deref() == Some("cranelift")
|
||||
{
|
||||
features += "compiler-builtins-no-f16-f128 ";
|
||||
}
|
||||
|
||||
if builder.no_std(target) == Some(true) {
|
||||
features += " compiler-builtins-mem";
|
||||
if !target.starts_with("bpf") {
|
||||
|
|
|
|||
|
|
@ -3132,7 +3132,11 @@ impl Step for Bootstrap {
|
|||
}
|
||||
|
||||
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||
run.path("src/bootstrap")
|
||||
// Bootstrap tests might not be perfectly self-contained and can depend on the external
|
||||
// environment, submodules that are checked out, etc.
|
||||
// Therefore we only run them by default on CI.
|
||||
let runs_on_ci = run.builder.config.is_running_on_ci;
|
||||
run.path("src/bootstrap").default_condition(runs_on_ci)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig<'_>) {
|
||||
|
|
|
|||
|
|
@ -338,12 +338,6 @@ than building it.
|
|||
|
||||
// Make sure musl-root is valid.
|
||||
if target.contains("musl") && !target.contains("unikraft") {
|
||||
// If this is a native target (host is also musl) and no musl-root is given,
|
||||
// fall back to the system toolchain in /usr before giving up
|
||||
if build.musl_root(*target).is_none() && build.config.is_host_target(*target) {
|
||||
let target = build.config.target_config.entry(*target).or_default();
|
||||
target.musl_root = Some("/usr".into());
|
||||
}
|
||||
match build.musl_libdir(*target) {
|
||||
Some(libdir) => {
|
||||
if fs::metadata(libdir.join("libc.a")).is_err() {
|
||||
|
|
|
|||
|
|
@ -1329,23 +1329,33 @@ impl Build {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the "musl root" for this `target`, if defined
|
||||
/// Returns the "musl root" for this `target`, if defined.
|
||||
///
|
||||
/// If this is a native target (host is also musl) and no musl-root is given,
|
||||
/// it falls back to the system toolchain in /usr.
|
||||
fn musl_root(&self, target: TargetSelection) -> Option<&Path> {
|
||||
self.config
|
||||
let configured_root = self
|
||||
.config
|
||||
.target_config
|
||||
.get(&target)
|
||||
.and_then(|t| t.musl_root.as_ref())
|
||||
.or(self.config.musl_root.as_ref())
|
||||
.map(|p| &**p)
|
||||
.map(|p| &**p);
|
||||
|
||||
if self.config.is_host_target(target) && configured_root.is_none() {
|
||||
Some(Path::new("/usr"))
|
||||
} else {
|
||||
configured_root
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the "musl libdir" for this `target`.
|
||||
fn musl_libdir(&self, target: TargetSelection) -> Option<PathBuf> {
|
||||
let t = self.config.target_config.get(&target)?;
|
||||
if let libdir @ Some(_) = &t.musl_libdir {
|
||||
return libdir.clone();
|
||||
}
|
||||
self.musl_root(target).map(|root| root.join("lib"))
|
||||
self.config
|
||||
.target_config
|
||||
.get(&target)
|
||||
.and_then(|t| t.musl_libdir.clone())
|
||||
.or_else(|| self.musl_root(target).map(|root| root.join("lib")))
|
||||
}
|
||||
|
||||
/// Returns the `lib` directory for the WASI target specified, if
|
||||
|
|
|
|||
|
|
@ -439,7 +439,7 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
|
|||
ChangeInfo {
|
||||
change_id: 143255,
|
||||
severity: ChangeSeverity::Warning,
|
||||
summary: "`llvm.lld` is no longer enabled by default for the dist profile.",
|
||||
summary: "`rust.lld` is no longer enabled by default for the dist profile.",
|
||||
},
|
||||
ChangeInfo {
|
||||
change_id: 143251,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,18 @@
|
|||
//! This module serves two purposes:
|
||||
//! 1. It is part of the `utils` module and used in other parts of bootstrap.
|
||||
//! 2. It is embedded inside bootstrap shims to avoid a dependency on the bootstrap library.
|
||||
//! Therefore, this module should never use any other bootstrap module. This reduces binary
|
||||
//! size and improves compilation time by minimizing linking time.
|
||||
//!
|
||||
//! 1. It is part of the `utils` module and used in other parts of bootstrap.
|
||||
//! 2. It is embedded inside bootstrap shims to avoid a dependency on the bootstrap library.
|
||||
//! Therefore, this module should never use any other bootstrap module. This reduces binary size
|
||||
//! and improves compilation time by minimizing linking time.
|
||||
|
||||
// # Note on tests
|
||||
//
|
||||
// If we were to declare a tests submodule here, the shim binaries that include this module via
|
||||
// `#[path]` would fail to find it, which breaks `./x check bootstrap`. So instead the unit tests
|
||||
// for this module are in `super::tests::shared_helpers_tests`.
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::fs::OpenOptions;
|
||||
|
|
@ -16,10 +20,6 @@ use std::io::Write;
|
|||
use std::process::Command;
|
||||
use std::str::FromStr;
|
||||
|
||||
// If we were to declare a tests submodule here, the shim binaries that include this
|
||||
// module via `#[path]` would fail to find it, which breaks `./x check bootstrap`.
|
||||
// So instead the unit tests for this module are in `super::tests::shared_helpers_tests`.
|
||||
|
||||
/// Returns the environment variable which the dynamic library lookup path
|
||||
/// resides in for this platform.
|
||||
pub fn dylib_path_var() -> &'static str {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ use crate::{Build, Config, Flags, t};
|
|||
|
||||
pub mod git;
|
||||
|
||||
// Note: tests for `shared_helpers` is separate here, as otherwise shim binaries that include the
|
||||
// `shared_helpers` via `#[path]` would fail to find it, breaking `./x check bootstrap`.
|
||||
mod shared_helpers_tests;
|
||||
|
||||
/// Holds temporary state of a bootstrap test.
|
||||
/// Right now it is only used to redirect the build directory of the bootstrap
|
||||
/// invocation, in the future it would be great if we could actually execute
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
//! The `shared_helpers` module can't have its own tests submodule, because that would cause
|
||||
//! problems for the shim binaries that include it via `#[path]`, so instead those unit tests live
|
||||
//! here.
|
||||
//!
|
||||
//! To prevent tidy from complaining about this file not being named `tests.rs`, it lives inside a
|
||||
//! submodule directory named `tests`.
|
||||
|
||||
use crate::utils::shared_helpers::parse_value_from_args;
|
||||
|
||||
#[test]
|
||||
|
|
@ -40,6 +40,7 @@ COPY host-x86_64/pr-check-1/validate-toolstate.sh /scripts/
|
|||
# We disable optimized compiler built-ins because that requires a C toolchain for the target.
|
||||
# We also skip the x86_64-unknown-linux-gnu target as it is well-tested by other jobs.
|
||||
ENV SCRIPT \
|
||||
python3 ../x.py check bootstrap && \
|
||||
/scripts/check-default-config-profiles.sh && \
|
||||
python3 ../x.py build src/tools/build-manifest && \
|
||||
python3 ../x.py test --stage 0 src/tools/compiletest && \
|
||||
|
|
|
|||
|
|
@ -79,7 +79,6 @@ ENV MUSL_TARGETS=x86_64-unknown-linux-musl \
|
|||
CXX_x86_64_unknown_linux_musl=x86_64-linux-musl-g++
|
||||
ENV MUSL_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $MUSL_TARGETS
|
||||
|
||||
COPY host-x86_64/test-various/uefi_qemu_test /uefi_qemu_test
|
||||
ENV UEFI_TARGETS=aarch64-unknown-uefi,i686-unknown-uefi,x86_64-unknown-uefi \
|
||||
CC_aarch64_unknown_uefi=clang-11 \
|
||||
CXX_aarch64_unknown_uefi=clang++-11 \
|
||||
|
|
@ -88,6 +87,8 @@ ENV UEFI_TARGETS=aarch64-unknown-uefi,i686-unknown-uefi,x86_64-unknown-uefi \
|
|||
CC_x86_64_unknown_uefi=clang-11 \
|
||||
CXX_x86_64_unknown_uefi=clang++-11
|
||||
ENV UEFI_SCRIPT python3 /checkout/x.py --stage 2 build --host='' --target $UEFI_TARGETS && \
|
||||
python3 -u /uefi_qemu_test/run.py
|
||||
python3 /checkout/x.py --stage 2 test tests/run-make/uefi-qemu/rmake.rs --target aarch64-unknown-uefi && \
|
||||
python3 /checkout/x.py --stage 2 test tests/run-make/uefi-qemu/rmake.rs --target i686-unknown-uefi && \
|
||||
python3 /checkout/x.py --stage 2 test tests/run-make/uefi-qemu/rmake.rs --target x86_64-unknown-uefi
|
||||
|
||||
ENV SCRIPT $WASM_SCRIPT && $NVPTX_SCRIPT && $MUSL_SCRIPT && $UEFI_SCRIPT
|
||||
|
|
|
|||
|
|
@ -1,140 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
TARGET_AARCH64 = "aarch64-unknown-uefi"
|
||||
TARGET_I686 = "i686-unknown-uefi"
|
||||
TARGET_X86_64 = "x86_64-unknown-uefi"
|
||||
|
||||
|
||||
def run(*cmd, capture=False, check=True, env=None, timeout=None):
|
||||
"""Print and run a command, optionally capturing the output."""
|
||||
cmd = [str(p) for p in cmd]
|
||||
print(" ".join(cmd))
|
||||
return subprocess.run(
|
||||
cmd, capture_output=capture, check=check, env=env, text=True, timeout=timeout
|
||||
)
|
||||
|
||||
|
||||
def build_and_run(tmp_dir, target):
|
||||
if target == TARGET_AARCH64:
|
||||
boot_file_name = "bootaa64.efi"
|
||||
ovmf_dir = Path("/usr/share/AAVMF")
|
||||
ovmf_code = "AAVMF_CODE.fd"
|
||||
ovmf_vars = "AAVMF_VARS.fd"
|
||||
qemu = "qemu-system-aarch64"
|
||||
machine = "virt"
|
||||
cpu = "cortex-a72"
|
||||
elif target == TARGET_I686:
|
||||
boot_file_name = "bootia32.efi"
|
||||
ovmf_dir = Path("/usr/share/OVMF")
|
||||
ovmf_code = "OVMF32_CODE_4M.secboot.fd"
|
||||
ovmf_vars = "OVMF32_VARS_4M.fd"
|
||||
# The i686 target intentionally uses 64-bit qemu; the important
|
||||
# difference is that the OVMF code provides a 32-bit environment.
|
||||
qemu = "qemu-system-x86_64"
|
||||
machine = "q35"
|
||||
cpu = "qemu64"
|
||||
elif target == TARGET_X86_64:
|
||||
boot_file_name = "bootx64.efi"
|
||||
ovmf_dir = Path("/usr/share/OVMF")
|
||||
ovmf_code = "OVMF_CODE.fd"
|
||||
ovmf_vars = "OVMF_VARS.fd"
|
||||
qemu = "qemu-system-x86_64"
|
||||
machine = "q35"
|
||||
cpu = "qemu64"
|
||||
else:
|
||||
raise KeyError("invalid target")
|
||||
|
||||
host_artifacts = Path("/checkout/obj/build/x86_64-unknown-linux-gnu")
|
||||
stage0 = host_artifacts / "stage0/bin"
|
||||
stage2 = host_artifacts / "stage2/bin"
|
||||
|
||||
env = dict(os.environ)
|
||||
env["PATH"] = "{}:{}:{}".format(stage2, stage0, env["PATH"])
|
||||
|
||||
# Copy the test create into `tmp_dir`.
|
||||
test_crate = Path(tmp_dir) / "uefi_qemu_test"
|
||||
shutil.copytree("/uefi_qemu_test", test_crate)
|
||||
|
||||
# Build the UEFI executable.
|
||||
run(
|
||||
"cargo",
|
||||
"build",
|
||||
"--manifest-path",
|
||||
test_crate / "Cargo.toml",
|
||||
"--target",
|
||||
target,
|
||||
env=env,
|
||||
)
|
||||
|
||||
# Create a mock EFI System Partition in a subdirectory.
|
||||
esp = test_crate / "esp"
|
||||
boot = esp / "efi/boot"
|
||||
os.makedirs(boot, exist_ok=True)
|
||||
|
||||
# Copy the executable into the ESP.
|
||||
src_exe_path = test_crate / "target" / target / "debug/uefi_qemu_test.efi"
|
||||
shutil.copy(src_exe_path, boot / boot_file_name)
|
||||
print(src_exe_path, boot / boot_file_name)
|
||||
|
||||
# Select the appropriate EDK2 build.
|
||||
ovmf_code = ovmf_dir / ovmf_code
|
||||
ovmf_vars = ovmf_dir / ovmf_vars
|
||||
|
||||
# Make a writable copy of the vars file. aarch64 doesn't boot
|
||||
# correctly with read-only vars.
|
||||
ovmf_rw_vars = Path(tmp_dir) / "vars.fd"
|
||||
shutil.copy(ovmf_vars, ovmf_rw_vars)
|
||||
|
||||
# Run the executable in QEMU and capture the output.
|
||||
output = run(
|
||||
qemu,
|
||||
"-machine",
|
||||
machine,
|
||||
"-cpu",
|
||||
cpu,
|
||||
"-display",
|
||||
"none",
|
||||
"-serial",
|
||||
"stdio",
|
||||
"-drive",
|
||||
f"if=pflash,format=raw,readonly=on,file={ovmf_code}",
|
||||
"-drive",
|
||||
f"if=pflash,format=raw,readonly=off,file={ovmf_rw_vars}",
|
||||
"-drive",
|
||||
f"format=raw,file=fat:rw:{esp}",
|
||||
capture=True,
|
||||
check=True,
|
||||
# Set a timeout to kill the VM in case something goes wrong.
|
||||
timeout=60,
|
||||
).stdout
|
||||
|
||||
if "Hello World!" in output:
|
||||
print("VM produced expected output")
|
||||
else:
|
||||
print("unexpected VM output:")
|
||||
print("---start---")
|
||||
print(output)
|
||||
print("---end---")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
targets = [TARGET_AARCH64, TARGET_I686, TARGET_X86_64]
|
||||
|
||||
for target in targets:
|
||||
# Create a temporary directory so that we have a writeable
|
||||
# workspace.
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
build_and_run(tmp_dir, target)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -1 +1 @@
|
|||
460259d14de0274b97b8801e08cb2fe5f16fdac5
|
||||
efd420c770bb179537c01063e98cb6990c439654
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue