[[package]]
name = "object"
-version = "0.28.1"
+version = "0.28.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ce8b38d41f9f3618fc23f908faae61510f8d8ce2d99cbe910641e8f1971f084"
+checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424"
dependencies = [
"crc32fast",
"flate2",
"itertools",
"jobserver",
"libc",
- "object 0.28.1",
+ "object 0.28.4",
"pathdiff",
"regex",
"rustc_apfloat",
dependencies = [
"gimli 0.26.1",
"hashbrown 0.11.2",
- "object 0.28.1",
+ "object 0.28.4",
"tracing",
]
ItemKind::ForeignMod(ref foreign_module) => {
walk_list!(visitor, visit_foreign_item, &foreign_module.items);
}
- ItemKind::GlobalAsm(ref asm) => walk_inline_asm(visitor, asm),
+ ItemKind::GlobalAsm(ref asm) => visitor.visit_inline_asm(asm),
ItemKind::TyAlias(box TyAlias { ref generics, ref bounds, ref ty, .. }) => {
visitor.visit_generics(generics);
walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
}
ExprKind::MacCall(ref mac) => visitor.visit_mac_call(mac),
ExprKind::Paren(ref subexpression) => visitor.visit_expr(subexpression),
- ExprKind::InlineAsm(ref asm) => walk_inline_asm(visitor, asm),
+ ExprKind::InlineAsm(ref asm) => visitor.visit_inline_asm(asm),
ExprKind::Yield(ref optional_expression) => {
walk_list!(visitor, visit_expr, optional_expression);
}
fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> {
let pat = self.lower_pat(&arm.pat);
let guard = arm.guard.as_ref().map(|cond| {
- if let ExprKind::Let(ref pat, ref scrutinee, _) = cond.kind {
- hir::Guard::IfLet(self.lower_pat(pat), self.lower_expr(scrutinee))
+ if let ExprKind::Let(ref pat, ref scrutinee, span) = cond.kind {
+ hir::Guard::IfLet(self.arena.alloc(hir::Let {
+ hir_id: self.next_id(),
+ span: self.lower_span(span),
+ pat: self.lower_pat(pat),
+ ty: None,
+ init: self.lower_expr(scrutinee),
+ }))
} else {
hir::Guard::If(self.lower_expr(cond))
}
}
}
- // Check for unstable modifiers on `#[link(..)]` attribute
- if attr.has_name(sym::link) {
- for nested_meta in attr.meta_item_list().unwrap_or_default() {
- if nested_meta.has_name(sym::modifiers) {
- if let Some(modifiers) = nested_meta.value_str() {
- for modifier in modifiers.as_str().split(',') {
- if let Some(modifier) = modifier.strip_prefix(&['+', '-']) {
- macro_rules! gate_modifier { ($($name:literal => $feature:ident)*) => {
- $(if modifier == $name {
- let msg = concat!("`#[link(modifiers=\"", $name, "\")]` is unstable");
- gate_feature_post!(
- self,
- $feature,
- nested_meta.name_value_literal_span().unwrap(),
- msg
- );
- })*
- }}
-
- gate_modifier!(
- "bundle" => native_link_modifiers_bundle
- "verbatim" => native_link_modifiers_verbatim
- "as-needed" => native_link_modifiers_as_needed
- );
- }
- }
- }
- }
- }
- }
-
// Emit errors for non-staged-api crates.
if !self.features.staged_api {
if attr.has_name(sym::rustc_deprecated)
/// structure layout, `packed` to remove padding, and `transparent` to delegate representation
/// concerns to the only non-ZST field.
pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
- use ReprAttr::*;
+ if attr.has_name(sym::repr) { parse_repr_attr(sess, attr) } else { Vec::new() }
+}
+pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
+ assert!(attr.has_name(sym::repr), "expected `#[repr(..)]`, found: {:?}", attr);
+ use ReprAttr::*;
let mut acc = Vec::new();
let diagnostic = &sess.parse_sess.span_diagnostic;
- if attr.has_name(sym::repr) {
- if let Some(items) = attr.meta_item_list() {
- for item in items {
- let mut recognised = false;
- if item.is_word() {
- let hint = match item.name_or_empty() {
- sym::C => Some(ReprC),
- sym::packed => Some(ReprPacked(1)),
- sym::simd => Some(ReprSimd),
- sym::transparent => Some(ReprTransparent),
- sym::no_niche => Some(ReprNoNiche),
- sym::align => {
- let mut err = struct_span_err!(
- diagnostic,
- item.span(),
- E0589,
- "invalid `repr(align)` attribute: `align` needs an argument"
- );
- err.span_suggestion(
- item.span(),
- "supply an argument here",
- "align(...)".to_string(),
- Applicability::HasPlaceholders,
- );
- err.emit();
- recognised = true;
- None
- }
- name => int_type_of_word(name).map(ReprInt),
- };
- if let Some(h) = hint {
+ if let Some(items) = attr.meta_item_list() {
+ for item in items {
+ let mut recognised = false;
+ if item.is_word() {
+ let hint = match item.name_or_empty() {
+ sym::C => Some(ReprC),
+ sym::packed => Some(ReprPacked(1)),
+ sym::simd => Some(ReprSimd),
+ sym::transparent => Some(ReprTransparent),
+ sym::no_niche => Some(ReprNoNiche),
+ sym::align => {
+ let mut err = struct_span_err!(
+ diagnostic,
+ item.span(),
+ E0589,
+ "invalid `repr(align)` attribute: `align` needs an argument"
+ );
+ err.span_suggestion(
+ item.span(),
+ "supply an argument here",
+ "align(...)".to_string(),
+ Applicability::HasPlaceholders,
+ );
+ err.emit();
recognised = true;
- acc.push(h);
+ None
}
- } else if let Some((name, value)) = item.name_value_literal() {
- let mut literal_error = None;
- if name == sym::align {
- recognised = true;
- match parse_alignment(&value.kind) {
- Ok(literal) => acc.push(ReprAlign(literal)),
- Err(message) => literal_error = Some(message),
- };
- } else if name == sym::packed {
- recognised = true;
- match parse_alignment(&value.kind) {
- Ok(literal) => acc.push(ReprPacked(literal)),
- Err(message) => literal_error = Some(message),
- };
- } else if matches!(name, sym::C | sym::simd | sym::transparent | sym::no_niche)
- || int_type_of_word(name).is_some()
- {
- recognised = true;
- struct_span_err!(
+ name => int_type_of_word(name).map(ReprInt),
+ };
+
+ if let Some(h) = hint {
+ recognised = true;
+ acc.push(h);
+ }
+ } else if let Some((name, value)) = item.name_value_literal() {
+ let mut literal_error = None;
+ if name == sym::align {
+ recognised = true;
+ match parse_alignment(&value.kind) {
+ Ok(literal) => acc.push(ReprAlign(literal)),
+ Err(message) => literal_error = Some(message),
+ };
+ } else if name == sym::packed {
+ recognised = true;
+ match parse_alignment(&value.kind) {
+ Ok(literal) => acc.push(ReprPacked(literal)),
+ Err(message) => literal_error = Some(message),
+ };
+ } else if matches!(name, sym::C | sym::simd | sym::transparent | sym::no_niche)
+ || int_type_of_word(name).is_some()
+ {
+ recognised = true;
+ struct_span_err!(
diagnostic,
item.span(),
E0552,
"invalid representation hint: `{}` does not take a parenthesized argument list",
name.to_ident_string(),
).emit();
- }
- if let Some(literal_error) = literal_error {
- struct_span_err!(
+ }
+ if let Some(literal_error) = literal_error {
+ struct_span_err!(
+ diagnostic,
+ item.span(),
+ E0589,
+ "invalid `repr({})` attribute: {}",
+ name.to_ident_string(),
+ literal_error
+ )
+ .emit();
+ }
+ } else if let Some(meta_item) = item.meta_item() {
+ if let MetaItemKind::NameValue(ref value) = meta_item.kind {
+ if meta_item.has_name(sym::align) || meta_item.has_name(sym::packed) {
+ let name = meta_item.name_or_empty().to_ident_string();
+ recognised = true;
+ let mut err = struct_span_err!(
diagnostic,
item.span(),
- E0589,
- "invalid `repr({})` attribute: {}",
- name.to_ident_string(),
- literal_error
- )
- .emit();
- }
- } else if let Some(meta_item) = item.meta_item() {
- if let MetaItemKind::NameValue(ref value) = meta_item.kind {
- if meta_item.has_name(sym::align) || meta_item.has_name(sym::packed) {
- let name = meta_item.name_or_empty().to_ident_string();
- recognised = true;
- let mut err = struct_span_err!(
- diagnostic,
- item.span(),
- E0693,
- "incorrect `repr({})` attribute format",
- name,
- );
- match value.kind {
- ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => {
- err.span_suggestion(
- item.span(),
- "use parentheses instead",
- format!("{}({})", name, int),
- Applicability::MachineApplicable,
- );
- }
- ast::LitKind::Str(s, _) => {
- err.span_suggestion(
- item.span(),
- "use parentheses instead",
- format!("{}({})", name, s),
- Applicability::MachineApplicable,
- );
- }
- _ => {}
+ E0693,
+ "incorrect `repr({})` attribute format",
+ name,
+ );
+ match value.kind {
+ ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => {
+ err.span_suggestion(
+ item.span(),
+ "use parentheses instead",
+ format!("{}({})", name, int),
+ Applicability::MachineApplicable,
+ );
}
- err.emit();
- } else {
- if matches!(
- meta_item.name_or_empty(),
- sym::C | sym::simd | sym::transparent | sym::no_niche
- ) || int_type_of_word(meta_item.name_or_empty()).is_some()
- {
- recognised = true;
- struct_span_err!(
- diagnostic,
- meta_item.span,
- E0552,
- "invalid representation hint: `{}` does not take a value",
- meta_item.name_or_empty().to_ident_string(),
- )
- .emit();
+ ast::LitKind::Str(s, _) => {
+ err.span_suggestion(
+ item.span(),
+ "use parentheses instead",
+ format!("{}({})", name, s),
+ Applicability::MachineApplicable,
+ );
}
+ _ => {}
}
- } else if let MetaItemKind::List(_) = meta_item.kind {
- if meta_item.has_name(sym::align) {
+ err.emit();
+ } else {
+ if matches!(
+ meta_item.name_or_empty(),
+ sym::C | sym::simd | sym::transparent | sym::no_niche
+ ) || int_type_of_word(meta_item.name_or_empty()).is_some()
+ {
recognised = true;
struct_span_err!(
diagnostic,
meta_item.span,
- E0693,
- "incorrect `repr(align)` attribute format: \
- `align` takes exactly one argument in parentheses"
+ E0552,
+ "invalid representation hint: `{}` does not take a value",
+ meta_item.name_or_empty().to_ident_string(),
)
.emit();
- } else if meta_item.has_name(sym::packed) {
- recognised = true;
- struct_span_err!(
- diagnostic,
- meta_item.span,
- E0552,
- "incorrect `repr(packed)` attribute format: \
+ }
+ }
+ } else if let MetaItemKind::List(_) = meta_item.kind {
+ if meta_item.has_name(sym::align) {
+ recognised = true;
+ struct_span_err!(
+ diagnostic,
+ meta_item.span,
+ E0693,
+ "incorrect `repr(align)` attribute format: \
+ `align` takes exactly one argument in parentheses"
+ )
+ .emit();
+ } else if meta_item.has_name(sym::packed) {
+ recognised = true;
+ struct_span_err!(
+ diagnostic,
+ meta_item.span,
+ E0552,
+ "incorrect `repr(packed)` attribute format: \
`packed` takes exactly one parenthesized argument, \
or no parentheses at all"
- )
- .emit();
- } else if matches!(
- meta_item.name_or_empty(),
- sym::C | sym::simd | sym::transparent | sym::no_niche
- ) || int_type_of_word(meta_item.name_or_empty()).is_some()
- {
- recognised = true;
- struct_span_err!(
+ )
+ .emit();
+ } else if matches!(
+ meta_item.name_or_empty(),
+ sym::C | sym::simd | sym::transparent | sym::no_niche
+ ) || int_type_of_word(meta_item.name_or_empty()).is_some()
+ {
+ recognised = true;
+ struct_span_err!(
diagnostic,
meta_item.span,
E0552,
"invalid representation hint: `{}` does not take a parenthesized argument list",
meta_item.name_or_empty().to_ident_string(),
).emit();
- }
}
}
- if !recognised {
- // Not a word we recognize. This will be caught and reported by
- // the `check_mod_attrs` pass, but this pass doesn't always run
- // (e.g. if we only pretty-print the source), so we have to gate
- // the `delay_span_bug` call as follows:
- if sess.opts.pretty.map_or(true, |pp| pp.needs_analysis()) {
- diagnostic.delay_span_bug(item.span(), "unrecognized representation hint");
- }
+ }
+ if !recognised {
+ // Not a word we recognize. This will be caught and reported by
+ // the `check_mod_attrs` pass, but this pass doesn't always run
+ // (e.g. if we only pretty-print the source), so we have to gate
+ // the `delay_span_bug` call as follows:
+ if sess.opts.pretty.map_or(true, |pp| pp.needs_analysis()) {
+ diagnostic.delay_span_bug(item.span(), "unrecognized representation hint");
}
}
}
// Add successor BBs to the work list, if necessary.
let bb_data = &self.body[bb];
debug_assert!(hi == bb_data.statements.len());
- for &succ_bb in bb_data.terminator().successors() {
+ for succ_bb in bb_data.terminator().successors() {
if !self.visited.insert(succ_bb) {
if succ_bb == location.block && first_lo > 0 {
// `succ_bb` has been seen before. If it wasn't
FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
};
-use rustc_middle::ty::{self, subst::Subst, suggest_constraining_type_params, PredicateKind, Ty};
+use rustc_middle::ty::{
+ self, subst::Subst, suggest_constraining_type_params, EarlyBinder, PredicateKind, Ty,
+};
use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
use rustc_span::symbol::sym;
use rustc_span::{BytePos, Span};
let find_fn_kind_from_did = |predicates: &[(ty::Predicate<'tcx>, Span)], substs| {
predicates.iter().find_map(|(pred, _)| {
let pred = if let Some(substs) = substs {
- pred.subst(tcx, substs).kind().skip_binder()
+ EarlyBinder(*pred).subst(tcx, substs).kind().skip_binder()
} else {
pred.kind().skip_binder()
};
block
.terminator()
.successors()
- .map(|bb| Location { statement_index: 0, block: *bb })
+ .map(|bb| Location { statement_index: 0, block: bb })
.filter(|s| visited_locations.insert(*s))
.map(|s| {
if self.is_back_edge(location, s) {
}
} else {
for bb in block.terminator().successors() {
- let successor = Location { statement_index: 0, block: *bb };
+ let successor = Location { statement_index: 0, block: bb };
if !visited_locations.contains(&successor)
&& self.find_loop_head_dfs(successor, loop_head, visited_locations)
block_data
.terminator()
.successors()
- .filter(|&bb| Some(&Some(*bb)) != block_data.terminator().unwind())
- .map(|&bb| Location { statement_index: 0, block: bb }),
+ .filter(|&bb| Some(&Some(bb)) != block_data.terminator().unwind())
+ .map(|bb| Location { statement_index: 0, block: bb }),
);
}
}
// We are at the terminator of an init that has a panic path,
// and where the init should not happen on panic
- for &successor in block_data.terminator().successors() {
+ for successor in block_data.terminator().successors() {
if body[successor].is_cleanup {
continue;
}
-use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::vec_map::VecMap;
use rustc_hir::def_id::DefId;
use rustc_hir::OpaqueTyOrigin;
use rustc_infer::infer::InferCtxt;
-use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, TyCtxt, TypeFoldable};
-use rustc_span::Span;
use rustc_trait_selection::opaque_types::InferCtxtExt;
use super::RegionInferenceContext;
let opaque_type_key =
OpaqueTypeKey { def_id: opaque_type_key.def_id, substs: universal_substs };
- let remapped_type = infcx.infer_opaque_definition_from_instantiation(
+ let ty = infcx.infer_opaque_definition_from_instantiation(
opaque_type_key,
universal_concrete_type,
origin,
);
- let ty = if check_opaque_type_parameter_valid(
- infcx.tcx,
- opaque_type_key,
- origin,
- concrete_type.span,
- ) {
- remapped_type
- } else {
- infcx.tcx.ty_error()
- };
// Sometimes two opaque types are the same only after we remap the generic parameters
// back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to `(X, Y)`
// and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we only know that
})
}
}
-
-fn check_opaque_type_parameter_valid(
- tcx: TyCtxt<'_>,
- opaque_type_key: OpaqueTypeKey<'_>,
- origin: OpaqueTyOrigin,
- span: Span,
-) -> bool {
- match origin {
- // No need to check return position impl trait (RPIT)
- // because for type and const parameters they are correct
- // by construction: we convert
- //
- // fn foo<P0..Pn>() -> impl Trait
- //
- // into
- //
- // type Foo<P0...Pn>
- // fn foo<P0..Pn>() -> Foo<P0...Pn>.
- //
- // For lifetime parameters we convert
- //
- // fn foo<'l0..'ln>() -> impl Trait<'l0..'lm>
- //
- // into
- //
- // type foo::<'p0..'pn>::Foo<'q0..'qm>
- // fn foo<l0..'ln>() -> foo::<'static..'static>::Foo<'l0..'lm>.
- //
- // which would error here on all of the `'static` args.
- OpaqueTyOrigin::FnReturn(..) | OpaqueTyOrigin::AsyncFn(..) => return true,
- // Check these
- OpaqueTyOrigin::TyAlias => {}
- }
- let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
- let mut seen_params: FxHashMap<_, Vec<_>> = FxHashMap::default();
- for (i, arg) in opaque_type_key.substs.iter().enumerate() {
- let arg_is_param = match arg.unpack() {
- GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
- GenericArgKind::Lifetime(lt) if lt.is_static() => {
- tcx.sess
- .struct_span_err(span, "non-defining opaque type use in defining scope")
- .span_label(
- tcx.def_span(opaque_generics.param_at(i, tcx).def_id),
- "cannot use static lifetime; use a bound lifetime \
- instead or remove the lifetime parameter from the \
- opaque type",
- )
- .emit();
- return false;
- }
- GenericArgKind::Lifetime(lt) => {
- matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_))
- }
- GenericArgKind::Const(ct) => matches!(ct.val(), ty::ConstKind::Param(_)),
- };
-
- if arg_is_param {
- seen_params.entry(arg).or_default().push(i);
- } else {
- // Prevent `fn foo() -> Foo<u32>` from being defining.
- let opaque_param = opaque_generics.param_at(i, tcx);
- tcx.sess
- .struct_span_err(span, "non-defining opaque type use in defining scope")
- .span_note(
- tcx.def_span(opaque_param.def_id),
- &format!(
- "used non-generic {} `{}` for generic parameter",
- opaque_param.kind.descr(),
- arg,
- ),
- )
- .emit();
- return false;
- }
- }
-
- for (_, indices) in seen_params {
- if indices.len() > 1 {
- let descr = opaque_generics.param_at(indices[0], tcx).kind.descr();
- let spans: Vec<_> = indices
- .into_iter()
- .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id))
- .collect();
- tcx.sess
- .struct_span_err(span, "non-defining opaque type use in defining scope")
- .span_note(spans, &format!("{} used multiple times", descr))
- .emit();
- return false;
- }
- }
- true
-}
.infcx
.tcx
.mk_region(ty::ReVar(self.infcx.next_nll_region_var(FR).to_region_vid()));
- let va_list_ty =
- self.infcx.tcx.type_of(va_list_did).subst(self.infcx.tcx, &[region.into()]);
+ let va_list_ty = self
+ .infcx
+ .tcx
+ .bound_type_of(va_list_did)
+ .subst(self.infcx.tcx, &[region.into()]);
unnormalized_input_tys = self.infcx.tcx.mk_type_list(
unnormalized_input_tys.iter().copied().chain(iter::once(va_list_ty)),
timeout-minutes: 10
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Install rustfmt
run: |
TARGET_TRIPLE: aarch64-unknown-linux-gnu
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Cache cargo installed crates
uses: actions/cache@v2
timeout-minutes: 60
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
#- name: Cache cargo installed crates
# uses: actions/cache@v2
timeout-minutes: 60
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Cache cargo installed crates
uses: actions/cache@v2
sed -i 's/cranelift-jit = { version = "\w*.\w*.\w*", optional = true }/cranelift-jit = { git = "https:\/\/github.com\/bytecodealliance\/wasmtime.git", optional = true }/' Cargo.toml
sed -i 's/cranelift-object = "\w*.\w*.\w*"/cranelift-object = { git = "https:\/\/github.com\/bytecodealliance\/wasmtime.git" }/' Cargo.toml
- sed -i 's/gimli = { version = "0.25.0", default-features = false, features = \["write"\]}/gimli = { version = "0.26.1", default-features = false, features = ["write"] }/' Cargo.toml
+ sed -i 's/object = { version = "0.27.0"/object = { version = "0.28.0"/' Cargo.toml
cat Cargo.toml
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Cache cargo installed crates
uses: actions/cache@v2
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Cache cargo installed crates
uses: actions/cache@v2
"rust-analyzer.assist.importEnforceGranularity": true,
"rust-analyzer.assist.importPrefix": "crate",
"rust-analyzer.cargo.runBuildScripts": true,
- "rust-analyzer.cargo.features": ["unstable-features"]
+ "rust-analyzer.cargo.features": ["unstable-features"],
"rust-analyzer.linkedProjects": [
"./Cargo.toml",
//"./build_sysroot/sysroot_src/src/libstd/Cargo.toml",
jit = ["cranelift-jit", "libloading"]
inline_asm = []
-# Disable optimizations and debuginfo of build scripts and some of the heavy build deps, as the
-# execution time of build scripts is so fast that optimizing them slows down the total build time.
-[profile.release.build-override]
-opt-level = 0
-debug = false
-
-[profile.release.package.cranelift-codegen-meta]
-opt-level = 0
-debug = false
-
[package.metadata.rust-analyzer]
rustc_private = true
[[package]]
name = "hashbrown"
-version = "0.12.0"
+version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758"
+checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-alloc",
[[package]]
name = "libc"
-version = "0.2.124"
+version = "0.2.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50"
+checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b"
dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "memchr"
-version = "2.4.1"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
_ => unreachable!(),
}
- // Set the rpath to make the cg_clif executable find librustc_codegen_cranelift without changing
- // LD_LIBRARY_PATH
- if cfg!(unix) {
- if cfg!(target_os = "macos") {
- rustflags += " -Csplit-debuginfo=unpacked \
- -Clink-arg=-Wl,-rpath,@loader_path/../lib \
- -Zosx-rpath-install-name";
- } else {
- rustflags += " -Clink-arg=-Wl,-rpath=$ORIGIN/../lib ";
- }
- }
-
cmd.env("RUSTFLAGS", rustflags);
eprintln!("[BUILD] rustc_codegen_cranelift");
-use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{self, Command};
fs::create_dir_all(target_dir.join("lib")).unwrap();
// Copy the backend
- for file in ["cg_clif", "cg_clif_build_sysroot"] {
- try_hard_link(
- cg_clif_build_dir.join(get_file_name(file, "bin")),
- target_dir.join("bin").join(get_file_name(file, "bin")),
- );
- }
-
let cg_clif_dylib = get_file_name("rustc_codegen_cranelift", "dylib");
- try_hard_link(
- cg_clif_build_dir.join(&cg_clif_dylib),
- target_dir
- .join(if cfg!(windows) {
- // Windows doesn't have rpath support, so the cg_clif dylib needs to be next to the
- // binaries.
- "bin"
- } else {
- "lib"
- })
- .join(cg_clif_dylib),
- );
-
- // Build and copy cargo wrapper
- let mut build_cargo_wrapper_cmd = Command::new("rustc");
- build_cargo_wrapper_cmd
- .arg("scripts/cargo-clif.rs")
- .arg("-o")
- .arg(target_dir.join("cargo-clif"))
- .arg("-g");
- spawn_and_wait(build_cargo_wrapper_cmd);
+ let cg_clif_dylib_path = target_dir
+ .join(if cfg!(windows) {
+ // Windows doesn't have rpath support, so the cg_clif dylib needs to be next to the
+ // binaries.
+ "bin"
+ } else {
+ "lib"
+ })
+ .join(&cg_clif_dylib);
+ try_hard_link(cg_clif_build_dir.join(cg_clif_dylib), &cg_clif_dylib_path);
+
+ // Build and copy rustc and cargo wrappers
+ for wrapper in ["rustc-clif", "cargo-clif"] {
+ let mut build_cargo_wrapper_cmd = Command::new("rustc");
+ build_cargo_wrapper_cmd
+ .arg(PathBuf::from("scripts").join(format!("{wrapper}.rs")))
+ .arg("-o")
+ .arg(target_dir.join(wrapper))
+ .arg("-g");
+ spawn_and_wait(build_cargo_wrapper_cmd);
+ }
let default_sysroot = super::rustc_info::get_default_sysroot();
}
}
SysrootKind::Clif => {
- build_clif_sysroot_for_triple(channel, target_dir, host_triple, None);
+ build_clif_sysroot_for_triple(
+ channel,
+ target_dir,
+ host_triple,
+ &cg_clif_dylib_path,
+ None,
+ );
if host_triple != target_triple {
// When cross-compiling it is often necessary to manually pick the right linker
} else {
None
};
- build_clif_sysroot_for_triple(channel, target_dir, target_triple, linker);
+ build_clif_sysroot_for_triple(
+ channel,
+ target_dir,
+ target_triple,
+ &cg_clif_dylib_path,
+ linker,
+ );
}
// Copy std for the host to the lib dir. This is necessary for the jit mode to find
// libstd.
for file in fs::read_dir(host_rustlib_lib).unwrap() {
let file = file.unwrap().path();
- if file.file_name().unwrap().to_str().unwrap().contains("std-") {
+ let filename = file.file_name().unwrap().to_str().unwrap();
+ if filename.contains("std-") && !filename.contains(".rlib") {
try_hard_link(&file, target_dir.join("lib").join(file.file_name().unwrap()));
}
}
channel: &str,
target_dir: &Path,
triple: &str,
+ cg_clif_dylib_path: &Path,
linker: Option<&str>,
) {
match fs::read_to_string(Path::new("build_sysroot").join("rustc_version")) {
let build_dir = Path::new("build_sysroot").join("target").join(triple).join(channel);
if !super::config::get_bool("keep_sysroot") {
- // Cleanup the target dir with the exception of build scripts and the incremental cache
- for dir in ["build", "deps", "examples", "native"] {
- if build_dir.join(dir).exists() {
- fs::remove_dir_all(build_dir.join(dir)).unwrap();
- }
+ // Cleanup the deps dir, but keep build scripts and the incremental cache for faster
+ // recompilation as they are not affected by changes in cg_clif.
+ if build_dir.join("deps").exists() {
+ fs::remove_dir_all(build_dir.join("deps")).unwrap();
}
}
// Build sysroot
let mut build_cmd = Command::new("cargo");
build_cmd.arg("build").arg("--target").arg(triple).current_dir("build_sysroot");
- let mut rustflags = "--clif -Zforce-unstable-if-unmarked".to_string();
+ let mut rustflags = "-Zforce-unstable-if-unmarked -Cpanic=abort".to_string();
+ rustflags.push_str(&format!(" -Zcodegen-backend={}", cg_clif_dylib_path.to_str().unwrap()));
if channel == "release" {
build_cmd.arg("--release");
rustflags.push_str(" -Zmir-opt-level=3");
write!(rustflags, " -Clinker={}", linker).unwrap();
}
build_cmd.env("RUSTFLAGS", rustflags);
- build_cmd.env(
- "RUSTC",
- env::current_dir().unwrap().join(target_dir).join("bin").join("cg_clif_build_sysroot"),
- );
build_cmd.env("__CARGO_DEFAULT_LIB_METADATA", "cg_clif");
spawn_and_wait(build_cmd);
arg => arg_error!("Unexpected argument {}", arg),
}
}
+ target_dir = std::env::current_dir().unwrap().join(target_dir);
let host_triple = if let Ok(host_triple) = std::env::var("HOST_TRIPLE") {
host_triple
> You should prefer using the Cargo method.
```bash
-$ $cg_clif_dir/build/bin/cg_clif my_crate.rs
+$ $cg_clif_dir/build/rustc-clif my_crate.rs
```
## Jit mode
or
```bash
-$ $cg_clif_dir/build/bin/cg_clif -Zunstable-features -Cllvm-args=mode=jit -Cprefer-dynamic my_crate.rs
+$ $cg_clif_dir/build/rustc-clif -Zunstable-features -Cllvm-args=mode=jit -Cprefer-dynamic my_crate.rs
```
There is also an experimental lazy jit mode. In this mode functions are only compiled once they are
```bash
function jit_naked() {
- echo "$@" | $cg_clif_dir/build/bin/cg_clif - -Zunstable-features -Cllvm-args=mode=jit -Cprefer-dynamic
+ echo "$@" | $cg_clif_dir/build/rustc-clif - -Zunstable-features -Cllvm-args=mode=jit -Cprefer-dynamic
}
function jit() {
-#[cfg(target_has_atomic_load_store = "128")]
-#[unstable(feature = "integer_atomics", issue = "32976")]
-impl RefUnwindSafe for crate::sync::atomic::AtomicI128 {}
-
+
#[cfg(target_has_atomic_load_store = "ptr")]
#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")]
@@ -235,9 +232,6 @@ impl RefUnwindSafe for crate::sync::atomic::AtomicU32 {}
-#[cfg(target_has_atomic_load_store = "128")]
-#[unstable(feature = "integer_atomics", issue = "32976")]
-impl RefUnwindSafe for crate::sync::atomic::AtomicU128 {}
-
+
#[cfg(target_has_atomic_load_store = "8")]
#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")]
diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs
index d9de37e..8293fce 100644
--- a/library/core/src/sync/atomic.rs
+++ b/library/core/src/sync/atomic.rs
-@@ -2234,44 +2234,6 @@ atomic_int! {
+@@ -2234,46 +2234,6 @@ atomic_int! {
"AtomicU64::new(0)",
u64 AtomicU64 ATOMIC_U64_INIT
}
- unstable(feature = "integer_atomics", issue = "32976"),
- rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"),
- unstable(feature = "integer_atomics", issue = "32976"),
+- cfg_attr(not(test), rustc_diagnostic_item = "AtomicI128"),
- "i128",
- "#![feature(integer_atomics)]\n\n",
- atomic_min, atomic_max,
- unstable(feature = "integer_atomics", issue = "32976"),
- rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"),
- unstable(feature = "integer_atomics", issue = "32976"),
+- cfg_attr(not(test), rustc_diagnostic_item = "AtomicU128"),
- "u128",
- "#![feature(integer_atomics)]\n\n",
- atomic_umin, atomic_umax,
#[cfg(target_has_atomic = "ptr")]
assert_eq!(align_of::<AtomicUsize>(), size_of::<AtomicUsize>());
#[cfg(target_has_atomic = "ptr")]
---
+--
2.26.2.7.g19db9cfb68
[toolchain]
-channel = "nightly-2022-04-21"
+channel = "nightly-2022-05-15"
components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
use std::process::Command;
fn main() {
- if env::var("RUSTC_WRAPPER").map_or(false, |wrapper| wrapper.contains("sccache")) {
- eprintln!(
- "\x1b[1;93m=== Warning: Unsetting RUSTC_WRAPPER to prevent interference with sccache ===\x1b[0m"
- );
- env::remove_var("RUSTC_WRAPPER");
- }
-
let sysroot = PathBuf::from(env::current_exe().unwrap().parent().unwrap());
- env::set_var("RUSTC", sysroot.join("bin/cg_clif".to_string() + env::consts::EXE_SUFFIX));
-
- let mut rustdoc_flags = env::var("RUSTDOCFLAGS").unwrap_or(String::new());
- rustdoc_flags.push_str(" -Cpanic=abort -Zpanic-abort-tests -Zcodegen-backend=");
- rustdoc_flags.push_str(
+ let mut rustflags = String::new();
+ rustflags.push_str(" -Cpanic=abort -Zpanic-abort-tests -Zcodegen-backend=");
+ rustflags.push_str(
sysroot
.join(if cfg!(windows) { "bin" } else { "lib" })
.join(
.to_str()
.unwrap(),
);
- rustdoc_flags.push_str(" --sysroot ");
- rustdoc_flags.push_str(sysroot.to_str().unwrap());
- env::set_var("RUSTDOCFLAGS", rustdoc_flags);
+ rustflags.push_str(" --sysroot ");
+ rustflags.push_str(sysroot.to_str().unwrap());
+ env::set_var("RUSTFLAGS", env::var("RUSTFLAGS").unwrap_or(String::new()) + &rustflags);
+ env::set_var("RUSTDOCFLAGS", env::var("RUSTDOCFLAGS").unwrap_or(String::new()) + &rustflags);
// Ensure that the right toolchain is used
env::set_var("RUSTUP_TOOLCHAIN", env!("RUSTUP_TOOLCHAIN"));
.chain(env::args().skip(2))
.chain([
"--".to_string(),
- "-Zunstable-features".to_string(),
+ "-Zunstable-options".to_string(),
"-Cllvm-args=mode=jit".to_string(),
])
.collect()
.chain(env::args().skip(2))
.chain([
"--".to_string(),
- "-Zunstable-features".to_string(),
+ "-Zunstable-options".to_string(),
"-Cllvm-args=mode=jit-lazy".to_string(),
])
.collect()
+++ /dev/null
-# Note to people running shellcheck: this file should only be sourced, not executed directly.
-
-set -e
-
-export LD_LIBRARY_PATH="$(rustc --print sysroot)/lib:$LD_LIBRARY_PATH"
-export DYLD_LIBRARY_PATH="$(rustc --print sysroot)/lib:$DYLD_LIBRARY_PATH"
+++ /dev/null
-# Note to people running shellcheck: this file should only be sourced, not executed directly.
-
-# Various env vars that should only be set for the build system
-
-set -e
-
-export CG_CLIF_DISPLAY_CG_TIME=1
-export CG_CLIF_DISABLE_INCR_CACHE=1
-
-export HOST_TRIPLE=$(rustc -vV | grep host | cut -d: -f2 | tr -d " ")
-export TARGET_TRIPLE=${TARGET_TRIPLE:-$HOST_TRIPLE}
-
-export RUN_WRAPPER=''
-export JIT_SUPPORTED=1
-if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then
- export JIT_SUPPORTED=0
- if [[ "$TARGET_TRIPLE" == "aarch64-unknown-linux-gnu" ]]; then
- # We are cross-compiling for aarch64. Use the correct linker and run tests in qemu.
- export RUSTFLAGS='-Clinker=aarch64-linux-gnu-gcc '$RUSTFLAGS
- export RUN_WRAPPER='qemu-aarch64 -L /usr/aarch64-linux-gnu'
- elif [[ "$TARGET_TRIPLE" == "x86_64-pc-windows-gnu" ]]; then
- # We are cross-compiling for Windows. Run tests in wine.
- export RUN_WRAPPER='wine'
- else
- echo "Unknown non-native platform"
- fi
-fi
-
-# FIXME fix `#[linkage = "extern_weak"]` without this
-if [[ "$(uname)" == 'Darwin' ]]; then
- export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-undefined -Clink-arg=dynamic_lookup"
-fi
#![forbid(unsafe_code)]/* This line is ignored by bash
# This block is ignored by rustc
pushd $(dirname "$0")/../
-source scripts/config.sh
-RUSTC="$(pwd)/build/bin/cg_clif"
+RUSTC="$(pwd)/build/rustc-clif"
popd
PROFILE=$1 OUTPUT=$2 exec $RUSTC -Zunstable-options -Cllvm-args=mode=jit -Cprefer-dynamic $0
#*/
--- /dev/null
+use std::env;
+use std::ffi::OsString;
+#[cfg(unix)]
+use std::os::unix::process::CommandExt;
+use std::path::PathBuf;
+use std::process::Command;
+
+fn main() {
+ let sysroot = PathBuf::from(env::current_exe().unwrap().parent().unwrap());
+
+ let cg_clif_dylib_path = sysroot.join(if cfg!(windows) { "bin" } else { "lib" }).join(
+ env::consts::DLL_PREFIX.to_string() + "rustc_codegen_cranelift" + env::consts::DLL_SUFFIX,
+ );
+
+ let mut args = std::env::args_os().skip(1).collect::<Vec<_>>();
+ args.push(OsString::from("-Cpanic=abort"));
+ args.push(OsString::from("-Zpanic-abort-tests"));
+ let mut codegen_backend_arg = OsString::from("-Zcodegen-backend=");
+ codegen_backend_arg.push(cg_clif_dylib_path);
+ args.push(codegen_backend_arg);
+ if !args.contains(&OsString::from("--sysroot")) {
+ args.push(OsString::from("--sysroot"));
+ args.push(OsString::from(sysroot.to_str().unwrap()));
+ }
+
+ // Ensure that the right toolchain is used
+ env::set_var("RUSTUP_TOOLCHAIN", env!("RUSTUP_TOOLCHAIN"));
+
+ #[cfg(unix)]
+ Command::new("rustc").args(args).exec();
+
+ #[cfg(not(unix))]
+ std::process::exit(
+ Command::new("rustc").args(args).spawn().unwrap().wait().unwrap().code().unwrap_or(1),
+ );
+}
set -e
./y.rs build --no-unstable-features
-source scripts/config.sh
echo "[SETUP] Rust fork"
git clone https://github.com/rust-lang/rust.git || true
[dev-dependencies]
rand = "0.7"
rand_xorshift = "0.2"
-diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
-index 887d27fd6dca4..2c2239f2b83d1 100644
---- a/src/tools/compiletest/src/header.rs
-+++ b/src/tools/compiletest/src/header.rs
-@@ -806,8 +806,8 @@ pub fn make_test_description<R: Read>(
- cfg: Option<&str>,
- ) -> test::TestDesc {
- let mut ignore = false;
- #[cfg(not(bootstrap))]
-- let ignore_message: Option<String> = None;
-+ let ignore_message: Option<&str> = None;
- let mut should_fail = false;
-
- let rustc_has_profiler_support = env::var_os("RUSTC_PROFILER_SUPPORT").is_some();
-
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 8431aa7b818..a3ff7e68ce5 100644
--- a/src/tools/compiletest/src/runtest.rs
ninja = false
[build]
-rustc = "$(pwd)/../build/bin/cg_clif"
+rustc = "$(pwd)/../build/rustc-clif"
cargo = "$(rustup which cargo)"
full-bootstrap = true
local-rebuild = true
rm src/test/incremental/spike-neg2.rs # same
rm src/test/ui/issues/issue-74564-if-expr-stack-overflow.rs # gives a stackoverflow before the backend runs
rm src/test/ui/mir/ssa-analysis-regression-50041.rs # produces ICE
+rm src/test/ui/type-alias-impl-trait/assoc-projection-ice.rs # produces ICE
rm src/test/ui/simd/intrinsic/generic-reduction-pass.rs # simd_reduce_add_unordered doesn't accept an accumulator for integer vectors
-rm src/test/ui/rfc-2091-track-caller/intrinsic-wrapper.rs # wrong result from `Location::caller()`
-
# bugs in the test suite
# ======================
rm src/test/ui/backtrace.rs # TODO warning
set -e
-source scripts/config.sh
-source scripts/ext_config.sh
-export RUSTC=false # ensure that cg_llvm isn't accidentally used
-MY_RUSTC="$(pwd)/build/bin/cg_clif $RUSTFLAGS -L crate=target/out --out-dir target/out -Cdebuginfo=2"
+export CG_CLIF_DISPLAY_CG_TIME=1
+export CG_CLIF_DISABLE_INCR_CACHE=1
+
+export HOST_TRIPLE=$(rustc -vV | grep host | cut -d: -f2 | tr -d " ")
+export TARGET_TRIPLE=${TARGET_TRIPLE:-$HOST_TRIPLE}
+
+export RUN_WRAPPER=''
+
+case "$TARGET_TRIPLE" in
+ x86_64*)
+ export JIT_SUPPORTED=1
+ ;;
+ *)
+ export JIT_SUPPORTED=0
+ ;;
+esac
+
+if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then
+ export JIT_SUPPORTED=0
+ if [[ "$TARGET_TRIPLE" == "aarch64-unknown-linux-gnu" ]]; then
+ # We are cross-compiling for aarch64. Use the correct linker and run tests in qemu.
+ export RUSTFLAGS='-Clinker=aarch64-linux-gnu-gcc '$RUSTFLAGS
+ export RUN_WRAPPER='qemu-aarch64 -L /usr/aarch64-linux-gnu'
+ elif [[ "$TARGET_TRIPLE" == "x86_64-pc-windows-gnu" ]]; then
+ # We are cross-compiling for Windows. Run tests in wine.
+ export RUN_WRAPPER='wine'
+ else
+ echo "Unknown non-native platform"
+ fi
+fi
+
+# FIXME fix `#[linkage = "extern_weak"]` without this
+if [[ "$(uname)" == 'Darwin' ]]; then
+ export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-undefined -Clink-arg=dynamic_lookup"
+fi
+
+MY_RUSTC="$(pwd)/build/rustc-clif $RUSTFLAGS -L crate=target/out --out-dir target/out -Cdebuginfo=2"
function no_sysroot_tests() {
echo "[BUILD] mini_core"
$MY_RUSTC example/issue-91827-extern-types.rs --crate-name issue_91827_extern_types --crate-type bin --target "$TARGET_TRIPLE"
$RUN_WRAPPER ./target/out/issue_91827_extern_types
- echo "[AOT] alloc_system"
+ echo "[BUILD] alloc_system"
$MY_RUSTC example/alloc_system.rs --crate-type lib --target "$TARGET_TRIPLE"
echo "[AOT] alloc_example"
echo "[JIT] std_example (skipped)"
fi
- echo "[AOT] dst_field_align"
- $MY_RUSTC example/dst-field-align.rs --crate-name dst_field_align --crate-type bin --target "$TARGET_TRIPLE"
- $RUN_WRAPPER ./target/out/dst_field_align || (echo $?; false)
-
echo "[AOT] std_example"
$MY_RUSTC example/std_example.rs --crate-type bin --target "$TARGET_TRIPLE"
$RUN_WRAPPER ./target/out/std_example arg
+ echo "[AOT] dst_field_align"
+ $MY_RUSTC example/dst-field-align.rs --crate-name dst_field_align --crate-type bin --target "$TARGET_TRIPLE"
+ $RUN_WRAPPER ./target/out/dst_field_align
+
echo "[AOT] subslice-patterns-const-eval"
$MY_RUSTC example/subslice-patterns-const-eval.rs --crate-type bin -Cpanic=abort --target "$TARGET_TRIPLE"
$RUN_WRAPPER ./target/out/subslice-patterns-const-eval
if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then
echo "[BENCH COMPILE] ebobby/simple-raytracer"
hyperfine --runs "${RUN_RUNS:-10}" --warmup 1 --prepare "../build/cargo-clif clean" \
- "RUSTC=rustc RUSTFLAGS='' cargo build" \
+ "RUSTFLAGS='' cargo build" \
"../build/cargo-clif build"
echo "[BENCH RUN] ebobby/simple-raytracer"
pub(crate) fn codegen_terminator_call<'tcx>(
fx: &mut FunctionCx<'_, '_, 'tcx>,
- span: Span,
+ source_info: mir::SourceInfo,
func: &Operand<'tcx>,
args: &[Operand<'tcx>],
mir_dest: Option<(Place<'tcx>, BasicBlock)>,
match instance.def {
InstanceDef::Intrinsic(_) => {
- crate::intrinsics::codegen_intrinsic_call(fx, instance, args, destination, span);
+ crate::intrinsics::codegen_intrinsic_call(
+ fx,
+ instance,
+ args,
+ destination,
+ source_info,
+ );
return;
}
InstanceDef::DropGlue(_, None) => {
// Pass the caller location for `#[track_caller]`.
if instance.map(|inst| inst.def.requires_caller_location(fx.tcx)).unwrap_or(false) {
- let caller_location = fx.get_caller_location(span);
+ let caller_location = fx.get_caller_location(source_info);
args.push(CallArgument { value: caller_location, is_owned: false });
}
// FIXME find a cleaner way to support varargs
if fn_sig.c_variadic {
if !matches!(fn_sig.abi, Abi::C { .. }) {
- fx.tcx
- .sess
- .span_fatal(span, &format!("Variadic call for non-C abi {:?}", fn_sig.abi));
+ fx.tcx.sess.span_fatal(
+ source_info.span,
+ &format!("Variadic call for non-C abi {:?}", fn_sig.abi),
+ );
}
let sig_ref = fx.bcx.func.dfg.call_signature(call_inst).unwrap();
let abi_params = call_args
let ty = fx.bcx.func.dfg.value_type(arg);
if !ty.is_int() {
// FIXME set %al to upperbound on float args once floats are supported
- fx.tcx
- .sess
- .span_fatal(span, &format!("Non int ty {:?} for variadic call", ty));
+ fx.tcx.sess.span_fatal(
+ source_info.span,
+ &format!("Non int ty {:?} for variadic call", ty),
+ );
}
AbiParam::new(ty)
})
pub(crate) fn codegen_drop<'tcx>(
fx: &mut FunctionCx<'_, '_, 'tcx>,
- span: Span,
+ source_info: mir::SourceInfo,
drop_place: CPlace<'tcx>,
) {
let ty = drop_place.layout().ty;
if drop_instance.def.requires_caller_location(fx.tcx) {
// Pass the caller location for `#[track_caller]`.
- let caller_location = fx.get_caller_location(span);
+ let caller_location = fx.get_caller_location(source_info);
call_args.extend(
adjust_arg_for_abi(fx, caller_location, &fn_abi.args[1], false).into_iter(),
);
AssertKind::BoundsCheck { ref len, ref index } => {
let len = codegen_operand(fx, len).load_scalar(fx);
let index = codegen_operand(fx, index).load_scalar(fx);
- let location = fx.get_caller_location(source_info.span).load_scalar(fx);
+ let location = fx.get_caller_location(source_info).load_scalar(fx);
codegen_panic_inner(
fx,
}
_ => {
let msg_str = msg.description();
- codegen_panic(fx, msg_str, source_info.span);
+ codegen_panic(fx, msg_str, source_info);
}
}
}
from_hir_call: _,
} => {
fx.tcx.sess.time("codegen call", || {
- crate::abi::codegen_terminator_call(fx, *fn_span, func, args, *destination)
+ crate::abi::codegen_terminator_call(
+ fx,
+ mir::SourceInfo { span: *fn_span, ..source_info },
+ func,
+ args,
+ *destination,
+ )
});
}
TerminatorKind::InlineAsm {
}
TerminatorKind::Drop { place, target, unwind: _ } => {
let drop_place = codegen_place(fx, *place);
- crate::abi::codegen_drop(fx, source_info.span, drop_place);
+ crate::abi::codegen_drop(fx, source_info, drop_place);
let target_block = fx.get_block(*target);
fx.bcx.ins().jump(target_block, &[]);
fx.set_debug_loc(stmt.source_info);
- #[cfg(disabled)]
+ #[cfg(any())] // This is never true
match &stmt.kind {
StatementKind::StorageLive(..) | StatementKind::StorageDead(..) => {} // Those are not very useful
_ => {
}
}
-pub(crate) fn codegen_panic<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, msg_str: &str, span: Span) {
- let location = fx.get_caller_location(span).load_scalar(fx);
+pub(crate) fn codegen_panic<'tcx>(
+ fx: &mut FunctionCx<'_, '_, 'tcx>,
+ msg_str: &str,
+ source_info: mir::SourceInfo,
+) {
+ let location = fx.get_caller_location(source_info).load_scalar(fx);
let msg_ptr = fx.anonymous_str(msg_str);
let msg_len = fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(msg_str.len()).unwrap());
let args = [msg_ptr, msg_len, location];
- codegen_panic_inner(fx, rustc_hir::LangItem::Panic, &args, span);
+ codegen_panic_inner(fx, rustc_hir::LangItem::Panic, &args, source_info.span);
}
pub(crate) fn codegen_panic_inner<'tcx>(
+++ /dev/null
-#![feature(rustc_private)]
-#![warn(rust_2018_idioms)]
-#![warn(unused_lifetimes)]
-#![warn(unreachable_pub)]
-
-extern crate rustc_data_structures;
-extern crate rustc_driver;
-extern crate rustc_interface;
-extern crate rustc_session;
-extern crate rustc_target;
-
-use std::panic;
-
-use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
-use rustc_interface::interface;
-use rustc_session::config::{ErrorOutputType, TrimmedDefPaths};
-use rustc_session::early_error;
-use rustc_target::spec::PanicStrategy;
-
-// FIXME use std::lazy::SyncLazy once it stabilizes
-use once_cell::sync::Lazy;
-
-const BUG_REPORT_URL: &str = "https://github.com/bjorn3/rustc_codegen_cranelift/issues/new";
-
-static DEFAULT_HOOK: Lazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> =
- Lazy::new(|| {
- let hook = panic::take_hook();
- panic::set_hook(Box::new(|info| {
- // Invoke the default handler, which prints the actual panic message and optionally a backtrace
- (*DEFAULT_HOOK)(info);
-
- // Separate the output with an empty line
- eprintln!();
-
- // Print the ICE message
- rustc_driver::report_ice(info, BUG_REPORT_URL);
- }));
- hook
- });
-
-#[derive(Default)]
-pub struct CraneliftPassesCallbacks {
- time_passes: bool,
-}
-
-impl rustc_driver::Callbacks for CraneliftPassesCallbacks {
- fn config(&mut self, config: &mut interface::Config) {
- // If a --prints=... option has been given, we don't print the "total"
- // time because it will mess up the --prints output. See #64339.
- self.time_passes = config.opts.prints.is_empty()
- && (config.opts.debugging_opts.time_passes || config.opts.debugging_opts.time);
-
- config.opts.cg.panic = Some(PanicStrategy::Abort);
- config.opts.debugging_opts.panic_abort_tests = true;
- config.opts.maybe_sysroot = Some(config.opts.maybe_sysroot.clone().unwrap_or_else(|| {
- std::env::current_exe().unwrap().parent().unwrap().parent().unwrap().to_owned()
- }));
-
- config.opts.trimmed_def_paths = TrimmedDefPaths::GoodPath;
- }
-}
-
-fn main() {
- let start_time = std::time::Instant::now();
- let start_rss = get_resident_set_size();
- rustc_driver::init_rustc_env_logger();
- let mut callbacks = CraneliftPassesCallbacks::default();
- Lazy::force(&DEFAULT_HOOK); // Install ice hook
- let exit_code = rustc_driver::catch_with_exit_code(|| {
- let args = std::env::args_os()
- .enumerate()
- .map(|(i, arg)| {
- arg.into_string().unwrap_or_else(|arg| {
- early_error(
- ErrorOutputType::default(),
- &format!("Argument {} is not valid Unicode: {:?}", i, arg),
- )
- })
- })
- .collect::<Vec<_>>();
- let mut run_compiler = rustc_driver::RunCompiler::new(&args, &mut callbacks);
- run_compiler.set_make_codegen_backend(Some(Box::new(move |_| {
- Box::new(rustc_codegen_cranelift::CraneliftCodegenBackend { config: None })
- })));
- run_compiler.run()
- });
-
- if callbacks.time_passes {
- let end_rss = get_resident_set_size();
- print_time_passes_entry("total", start_time.elapsed(), start_rss, end_rss);
- }
-
- std::process::exit(exit_code)
-}
+++ /dev/null
-//! The only difference between this and cg_clif.rs is that this binary defaults to using cg_llvm
-//! instead of cg_clif and requires `--clif` to use cg_clif and that this binary doesn't have JIT
-//! support.
-//! This is necessary as with Cargo `RUSTC` applies to both target crates and host crates. The host
-//! crates must be built with cg_llvm as we are currently building a sysroot for cg_clif.
-//! `RUSTFLAGS` however is only applied to target crates, so `--clif` would only be passed to the
-//! target crates.
-
-#![feature(rustc_private)]
-#![warn(rust_2018_idioms)]
-#![warn(unused_lifetimes)]
-#![warn(unreachable_pub)]
-
-extern crate rustc_driver;
-extern crate rustc_interface;
-extern crate rustc_session;
-extern crate rustc_target;
-
-use std::path::PathBuf;
-
-use rustc_interface::interface;
-use rustc_session::config::ErrorOutputType;
-use rustc_session::early_error;
-use rustc_target::spec::PanicStrategy;
-
-fn find_sysroot() -> String {
- // Taken from https://github.com/Manishearth/rust-clippy/pull/911.
- let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
- let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
- match (home, toolchain) {
- (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain),
- _ => option_env!("RUST_SYSROOT")
- .expect("need to specify RUST_SYSROOT env var or use rustup or multirust")
- .to_owned(),
- }
-}
-
-pub struct CraneliftPassesCallbacks {
- use_clif: bool,
-}
-
-impl rustc_driver::Callbacks for CraneliftPassesCallbacks {
- fn config(&mut self, config: &mut interface::Config) {
- if !self.use_clif {
- config.opts.maybe_sysroot = Some(PathBuf::from(find_sysroot()));
- return;
- }
-
- config.opts.cg.panic = Some(PanicStrategy::Abort);
- config.opts.debugging_opts.panic_abort_tests = true;
- config.opts.maybe_sysroot =
- Some(std::env::current_exe().unwrap().parent().unwrap().parent().unwrap().to_owned());
- }
-}
-
-fn main() {
- rustc_driver::init_rustc_env_logger();
- rustc_driver::install_ice_hook();
- let exit_code = rustc_driver::catch_with_exit_code(|| {
- let mut use_clif = false;
-
- let args = std::env::args_os()
- .enumerate()
- .map(|(i, arg)| {
- arg.into_string().unwrap_or_else(|arg| {
- early_error(
- ErrorOutputType::default(),
- &format!("Argument {} is not valid Unicode: {:?}", i, arg),
- )
- })
- })
- .filter(|arg| {
- if arg == "--clif" {
- use_clif = true;
- false
- } else {
- true
- }
- })
- .collect::<Vec<_>>();
-
- let mut callbacks = CraneliftPassesCallbacks { use_clif };
-
- let mut run_compiler = rustc_driver::RunCompiler::new(&args, &mut callbacks);
- if use_clif {
- run_compiler.set_make_codegen_backend(Some(Box::new(move |_| {
- Box::new(rustc_codegen_cranelift::CraneliftCodegenBackend { config: None })
- })));
- }
- run_compiler.run()
- });
- std::process::exit(exit_code)
-}
self.bcx.set_srcloc(SourceLoc::new(index as u32));
}
- pub(crate) fn get_caller_location(&mut self, span: Span) -> CValue<'tcx> {
- if let Some(loc) = self.caller_location {
- // `#[track_caller]` is used; return caller location instead of current location.
- return loc;
+ // Note: must be kept in sync with get_caller_location from cg_ssa
+ pub(crate) fn get_caller_location(&mut self, mut source_info: mir::SourceInfo) -> CValue<'tcx> {
+ let span_to_caller_location = |fx: &mut FunctionCx<'_, '_, 'tcx>, span: Span| {
+ let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
+ let caller = fx.tcx.sess.source_map().lookup_char_pos(topmost.lo());
+ let const_loc = fx.tcx.const_caller_location((
+ rustc_span::symbol::Symbol::intern(
+ &caller.file.name.prefer_remapped().to_string_lossy(),
+ ),
+ caller.line as u32,
+ caller.col_display as u32 + 1,
+ ));
+ crate::constant::codegen_const_value(fx, const_loc, fx.tcx.caller_location_ty())
+ };
+
+ // Walk up the `SourceScope`s, in case some of them are from MIR inlining.
+ // If so, the starting `source_info.span` is in the innermost inlined
+ // function, and will be replaced with outer callsite spans as long
+ // as the inlined functions were `#[track_caller]`.
+ loop {
+ let scope_data = &self.mir.source_scopes[source_info.scope];
+
+ if let Some((callee, callsite_span)) = scope_data.inlined {
+ // Stop inside the most nested non-`#[track_caller]` function,
+ // before ever reaching its caller (which is irrelevant).
+ if !callee.def.requires_caller_location(self.tcx) {
+ return span_to_caller_location(self, source_info.span);
+ }
+ source_info.span = callsite_span;
+ }
+
+ // Skip past all of the parents with `inlined: None`.
+ match scope_data.inlined_parent_scope {
+ Some(parent) => source_info.scope = parent,
+ None => break,
+ }
}
- let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
- let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
- let const_loc = self.tcx.const_caller_location((
- rustc_span::symbol::Symbol::intern(
- &caller.file.name.prefer_remapped().to_string_lossy(),
- ),
- caller.line as u32,
- caller.col_display as u32 + 1,
- ));
- crate::constant::codegen_const_value(self, const_loc, self.tcx.caller_location_ty())
+ // No inlined `SourceScope`s, or all of them were `#[track_caller]`.
+ self.caller_location.unwrap_or_else(|| span_to_caller_location(self, source_info.span))
}
pub(crate) fn anonymous_str(&mut self, msg: &str) -> Value {
jit_builder.hotswap(hotswap);
crate::compiler_builtins::register_functions_for_jit(&mut jit_builder);
jit_builder.symbols(imported_symbols);
+ jit_builder.symbol("__clif_jit_fn", clif_jit_fn as *const u8);
let mut jit_module = JITModule::new(jit_builder);
let mut cx = crate::CodegenCx::new(
}
}
-#[no_mangle]
-extern "C" fn __clif_jit_fn(
+extern "C" fn clif_jit_fn(
instance_ptr: *const Instance<'static>,
trampoline_ptr: *const u8,
) -> *const u8 {
instance: Instance<'tcx>,
args: &[mir::Operand<'tcx>],
destination: Option<(CPlace<'tcx>, BasicBlock)>,
- span: Span,
+ source_info: mir::SourceInfo,
) {
let intrinsic = fx.tcx.item_name(instance.def_id());
let substs = instance.substs;
fx.bcx.ins().trap(TrapCode::User(0));
}
sym::transmute => {
- crate::base::codegen_panic(fx, "Transmuting to uninhabited type.", span);
+ crate::base::codegen_panic(fx, "Transmuting to uninhabited type.", source_info);
}
_ => unimplemented!("unsupported instrinsic {}", intrinsic),
}
};
if intrinsic.as_str().starts_with("simd_") {
- self::simd::codegen_simd_intrinsic_call(fx, intrinsic, substs, args, ret, span);
+ self::simd::codegen_simd_intrinsic_call(fx, intrinsic, substs, args, ret, source_info.span);
let ret_block = fx.get_block(destination.expect("SIMD intrinsics don't diverge").1);
fx.bcx.ins().jump(ret_block, &[]);
} else if codegen_float_intrinsic_call(fx, intrinsic, args, ret) {
substs,
args,
ret,
- span,
+ source_info,
destination,
);
}
substs: SubstsRef<'tcx>,
args: &[mir::Operand<'tcx>],
ret: CPlace<'tcx>,
- span: Span,
+ source_info: mir::SourceInfo,
destination: Option<(CPlace<'tcx>, BasicBlock)>,
) {
let usize_layout = fx.layout_of(fx.tcx.types.usize);
intrinsic_match! {
fx, intrinsic, args,
_ => {
- fx.tcx.sess.span_fatal(span, &format!("unsupported intrinsic {}", intrinsic));
+ fx.tcx.sess.span_fatal(source_info.span, &format!("unsupported intrinsic {}", intrinsic));
};
assume, (c _a) {};
crate::base::codegen_panic(
fx,
&format!("attempted to instantiate uninhabited type `{}`", layout.ty),
- span,
+ source_info,
)
});
return;
crate::base::codegen_panic(
fx,
&format!("attempted to zero-initialize type `{}`, which is invalid", layout.ty),
- span,
+ source_info,
);
});
return;
crate::base::codegen_panic(
fx,
&format!("attempted to leave type `{}` uninitialized, which is invalid", layout.ty),
- span,
+ source_info,
)
});
return;
ret.write_cvalue(fx, val);
};
- ptr_offset_from, (v ptr, v base) {
+ ptr_offset_from | ptr_offset_from_unsigned, (v ptr, v base) {
let ty = substs.type_at(0);
- let isize_layout = fx.layout_of(fx.tcx.types.isize);
let pointee_size: u64 = fx.layout_of(ty).size.bytes();
- let diff = fx.bcx.ins().isub(ptr, base);
+ let diff_bytes = fx.bcx.ins().isub(ptr, base);
// FIXME this can be an exact division.
- let val = CValue::by_val(fx.bcx.ins().sdiv_imm(diff, pointee_size as i64), isize_layout);
+ let val = if intrinsic == sym::ptr_offset_from_unsigned {
+ let usize_layout = fx.layout_of(fx.tcx.types.usize);
+ // Because diff_bytes ULE isize::MAX, this would be fine as signed,
+ // but unsigned is slightly easier to codegen, so might as well.
+ CValue::by_val(fx.bcx.ins().udiv_imm(diff_bytes, pointee_size as i64), usize_layout)
+ } else {
+ let isize_layout = fx.layout_of(fx.tcx.types.isize);
+ CValue::by_val(fx.bcx.ins().sdiv_imm(diff_bytes, pointee_size as i64), isize_layout)
+ };
ret.write_cvalue(fx, val);
};
};
caller_location, () {
- let caller_location = fx.get_caller_location(span);
+ let caller_location = fx.get_caller_location(source_info);
ret.write_cvalue(fx, caller_location);
};
fx.bcx.ins().jump(ret_block, &[]);
return;
} else {
- fx.tcx.sess.span_fatal(span, "128bit atomics not yet supported");
+ fx.tcx.sess.span_fatal(source_info.span, "128bit atomics not yet supported");
}
}
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
_ => {
- report_atomic_type_validation_error(fx, intrinsic, span, ty);
+ report_atomic_type_validation_error(fx, intrinsic, source_info.span, ty);
return;
}
}
fx.bcx.ins().jump(ret_block, &[]);
return;
} else {
- fx.tcx.sess.span_fatal(span, "128bit atomics not yet supported");
+ fx.tcx.sess.span_fatal(source_info.span, "128bit atomics not yet supported");
}
}
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
_ => {
- report_atomic_type_validation_error(fx, intrinsic, span, ty);
+ report_atomic_type_validation_error(fx, intrinsic, source_info.span, ty);
return;
}
}
match layout.ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
_ => {
- report_atomic_type_validation_error(fx, intrinsic, span, layout.ty);
+ report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
return;
}
}
match layout.ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
_ => {
- report_atomic_type_validation_error(fx, intrinsic, span, layout.ty);
+ report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
return;
}
}
match layout.ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
_ => {
- report_atomic_type_validation_error(fx, intrinsic, span, layout.ty);
+ report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
return;
}
}
match layout.ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
_ => {
- report_atomic_type_validation_error(fx, intrinsic, span, layout.ty);
+ report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
return;
}
}
match layout.ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
_ => {
- report_atomic_type_validation_error(fx, intrinsic, span, layout.ty);
+ report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
return;
}
}
match layout.ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
_ => {
- report_atomic_type_validation_error(fx, intrinsic, span, layout.ty);
+ report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
return;
}
}
match layout.ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
_ => {
- report_atomic_type_validation_error(fx, intrinsic, span, layout.ty);
+ report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
return;
}
}
match layout.ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
_ => {
- report_atomic_type_validation_error(fx, intrinsic, span, layout.ty);
+ report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
return;
}
}
match layout.ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
_ => {
- report_atomic_type_validation_error(fx, intrinsic, span, layout.ty);
+ report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
return;
}
}
match layout.ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
_ => {
- report_atomic_type_validation_error(fx, intrinsic, span, layout.ty);
+ report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
return;
}
}
match layout.ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
_ => {
- report_atomic_type_validation_error(fx, intrinsic, span, layout.ty);
+ report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
return;
}
}
match layout.ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
_ => {
- report_atomic_type_validation_error(fx, intrinsic, span, layout.ty);
+ report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
return;
}
}
}
}
}
+ (ty::Array(a, _), ty::Array(b, _)) => assert_assignable(fx, *a, *b),
_ => {
assert_eq!(
from_ty, to_ty,
InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg0) => unimplemented!(),
InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(),
InlineAsmRegClass::X86(
- X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg,
+ X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg | X86InlineAsmRegClass::tmm_reg,
) => unreachable!("clobber-only"),
InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
bug!("GCC backend does not support SPIR-V")
InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg) => unimplemented!(),
InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => cx.type_i16(),
InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg0) => cx.type_i16(),
+ InlineAsmRegClass::X86(X86InlineAsmRegClass::tmm_reg) => unimplemented!(),
InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(),
InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
bug!("LLVM backend does not support SPIR-V")
},
InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => None,
InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg0) => None,
- InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg) => {
+ InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg | X86InlineAsmRegClass::tmm_reg) => {
unreachable!("clobber-only")
}
InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(),
InlineAsmRegClass::X86(
X86InlineAsmRegClass::x87_reg
| X86InlineAsmRegClass::mmx_reg
- | X86InlineAsmRegClass::kreg0,
+ | X86InlineAsmRegClass::kreg0
+ | X86InlineAsmRegClass::tmm_reg,
) => unreachable!("clobber-only"),
InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => "r",
InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => "r",
InlineAsmRegClass::X86(
X86InlineAsmRegClass::x87_reg
| X86InlineAsmRegClass::mmx_reg
- | X86InlineAsmRegClass::kreg0,
+ | X86InlineAsmRegClass::kreg0
+ | X86InlineAsmRegClass::tmm_reg,
) => {
unreachable!("clobber-only")
}
InlineAsmRegClass::X86(
X86InlineAsmRegClass::x87_reg
| X86InlineAsmRegClass::mmx_reg
- | X86InlineAsmRegClass::kreg0,
+ | X86InlineAsmRegClass::kreg0
+ | X86InlineAsmRegClass::tmm_reg,
) => {
unreachable!("clobber-only")
}
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::config::OptLevel;
+use rustc_span::symbol::sym;
use rustc_target::spec::abi::Abi;
use rustc_target::spec::{FramePointer, SanitizerSet, StackProbeType, StackProtector};
use smallvec::SmallVec;
) {
let span = cx
.tcx
- .get_attrs(instance.def_id())
- .iter()
- .find(|a| a.has_name(rustc_span::sym::target_feature))
+ .get_attr(instance.def_id(), sym::target_feature)
.map_or_else(|| cx.tcx.def_span(instance.def_id()), |a| a.span);
let msg = format!(
"the target features {} must all be either enabled or disabled together",
};
let target = &self.config.sess.target;
- let mingw_gnu_toolchain =
- target.vendor == "pc" && target.os == "windows" && target.env == "gnu";
+ let mingw_gnu_toolchain = target.vendor == "pc"
+ && target.os == "windows"
+ && target.env == "gnu"
+ && target.abi.is_empty();
let import_name_and_ordinal_vector: Vec<(String, Option<u16>)> = dll_imports
.iter()
use rustc_middle::ty::{self, AdtKind, Instance, ParamEnv, Ty, TyCtxt, COMMON_VTABLE_ENTRIES};
use rustc_session::config::{self, DebugInfo};
use rustc_span::symbol::Symbol;
+use rustc_span::FileName;
use rustc_span::FileNameDisplayPreference;
-use rustc_span::{self, SourceFile, SourceFileHash};
+use rustc_span::{self, SourceFile};
use rustc_target::abi::{Align, Size};
use smallvec::smallvec;
use tracing::debug;
use libc::{c_longlong, c_uint};
use std::borrow::Cow;
-use std::collections::hash_map::Entry;
use std::fmt::{self, Write};
use std::hash::{Hash, Hasher};
use std::iter;
use std::path::{Path, PathBuf};
use std::ptr;
+use tracing::instrument;
impl PartialEq for llvm::Metadata {
fn eq(&self, other: &Self) -> bool {
}
pub fn file_metadata<'ll>(cx: &CodegenCx<'ll, '_>, source_file: &SourceFile) -> &'ll DIFile {
- debug!("file_metadata: file_name: {:?}", source_file.name);
-
- let hash = Some(&source_file.src_hash);
- let file_name = Some(source_file.name.prefer_remapped().to_string());
- let directory = if source_file.is_real_file() && !source_file.is_imported() {
- Some(
- cx.sess()
- .opts
- .working_dir
- .to_string_lossy(FileNameDisplayPreference::Remapped)
- .to_string(),
- )
- } else {
- // If the path comes from an upstream crate we assume it has been made
- // independent of the compiler's working directory one way or another.
- None
- };
- file_metadata_raw(cx, file_name, directory, hash)
-}
-
-pub fn unknown_file_metadata<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll DIFile {
- file_metadata_raw(cx, None, None, None)
-}
-
-fn file_metadata_raw<'ll>(
- cx: &CodegenCx<'ll, '_>,
- file_name: Option<String>,
- directory: Option<String>,
- hash: Option<&SourceFileHash>,
-) -> &'ll DIFile {
- let key = (file_name, directory);
-
- match debug_context(cx).created_files.borrow_mut().entry(key) {
- Entry::Occupied(o) => o.get(),
- Entry::Vacant(v) => {
- let (file_name, directory) = v.key();
- debug!("file_metadata: file_name: {:?}, directory: {:?}", file_name, directory);
-
- let file_name = file_name.as_deref().unwrap_or("<unknown>");
- let directory = directory.as_deref().unwrap_or("");
-
- let (hash_kind, hash_value) = match hash {
- Some(hash) => {
- let kind = match hash.kind {
- rustc_span::SourceFileHashAlgorithm::Md5 => llvm::ChecksumKind::MD5,
- rustc_span::SourceFileHashAlgorithm::Sha1 => llvm::ChecksumKind::SHA1,
- rustc_span::SourceFileHashAlgorithm::Sha256 => llvm::ChecksumKind::SHA256,
- };
- (kind, hex_encode(hash.hash_bytes()))
+ let cache_key = Some((source_file.name_hash, source_file.src_hash));
+ return debug_context(cx)
+ .created_files
+ .borrow_mut()
+ .entry(cache_key)
+ .or_insert_with(|| alloc_new_file_metadata(cx, source_file));
+
+ #[instrument(skip(cx, source_file), level = "debug")]
+ fn alloc_new_file_metadata<'ll>(
+ cx: &CodegenCx<'ll, '_>,
+ source_file: &SourceFile,
+ ) -> &'ll DIFile {
+ debug!(?source_file.name);
+
+ let (directory, file_name) = match &source_file.name {
+ FileName::Real(filename) => {
+ let working_directory = &cx.sess().opts.working_dir;
+ debug!(?working_directory);
+
+ let filename = cx
+ .sess()
+ .source_map()
+ .path_mapping()
+ .to_embeddable_absolute_path(filename.clone(), working_directory);
+
+ // Construct the absolute path of the file
+ let abs_path = filename.remapped_path_if_available();
+ debug!(?abs_path);
+
+ if let Ok(rel_path) =
+ abs_path.strip_prefix(working_directory.remapped_path_if_available())
+ {
+ // If the compiler's working directory (which also is the DW_AT_comp_dir of
+ // the compilation unit) is a prefix of the path we are about to emit, then
+ // only emit the part relative to the working directory.
+ // Because of path remapping we sometimes see strange things here: `abs_path`
+ // might actually look like a relative path
+ // (e.g. `<crate-name-and-version>/src/lib.rs`), so if we emit it without
+ // taking the working directory into account, downstream tooling will
+ // interpret it as `<working-directory>/<crate-name-and-version>/src/lib.rs`,
+ // which makes no sense. Usually in such cases the working directory will also
+ // be remapped to `<crate-name-and-version>` or some other prefix of the path
+ // we are remapping, so we end up with
+ // `<crate-name-and-version>/<crate-name-and-version>/src/lib.rs`.
+ // By moving the working directory portion into the `directory` part of the
+ // DIFile, we allow LLVM to emit just the relative path for DWARF, while
+ // still emitting the correct absolute path for CodeView.
+ (
+ working_directory.to_string_lossy(FileNameDisplayPreference::Remapped),
+ rel_path.to_string_lossy().into_owned(),
+ )
+ } else {
+ ("".into(), abs_path.to_string_lossy().into_owned())
}
- None => (llvm::ChecksumKind::None, String::new()),
- };
+ }
+ other => ("".into(), other.prefer_remapped().to_string_lossy().into_owned()),
+ };
- let file_metadata = unsafe {
- llvm::LLVMRustDIBuilderCreateFile(
- DIB(cx),
- file_name.as_ptr().cast(),
- file_name.len(),
- directory.as_ptr().cast(),
- directory.len(),
- hash_kind,
- hash_value.as_ptr().cast(),
- hash_value.len(),
- )
- };
+ let hash_kind = match source_file.src_hash.kind {
+ rustc_span::SourceFileHashAlgorithm::Md5 => llvm::ChecksumKind::MD5,
+ rustc_span::SourceFileHashAlgorithm::Sha1 => llvm::ChecksumKind::SHA1,
+ rustc_span::SourceFileHashAlgorithm::Sha256 => llvm::ChecksumKind::SHA256,
+ };
+ let hash_value = hex_encode(source_file.src_hash.hash_bytes());
- v.insert(file_metadata);
- file_metadata
+ unsafe {
+ llvm::LLVMRustDIBuilderCreateFile(
+ DIB(cx),
+ file_name.as_ptr().cast(),
+ file_name.len(),
+ directory.as_ptr().cast(),
+ directory.len(),
+ hash_kind,
+ hash_value.as_ptr().cast(),
+ hash_value.len(),
+ )
}
}
}
+pub fn unknown_file_metadata<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll DIFile {
+ debug_context(cx).created_files.borrow_mut().entry(None).or_insert_with(|| unsafe {
+ let file_name = "<unknown>";
+ let directory = "";
+ let hash_value = "";
+
+ llvm::LLVMRustDIBuilderCreateFile(
+ DIB(cx),
+ file_name.as_ptr().cast(),
+ file_name.len(),
+ directory.as_ptr().cast(),
+ directory.len(),
+ llvm::ChecksumKind::None,
+ hash_value.as_ptr().cast(),
+ hash_value.len(),
+ )
+ })
+}
+
trait MsvcBasicName {
fn msvc_basic_name(self) -> &'static str;
}
use rustc_session::config::{self, DebugInfo};
use rustc_session::Session;
use rustc_span::symbol::Symbol;
-use rustc_span::{self, BytePos, Pos, SourceFile, SourceFileAndLine, Span};
+use rustc_span::{self, BytePos, Pos, SourceFile, SourceFileAndLine, SourceFileHash, Span};
use rustc_target::abi::Size;
use libc::c_uint;
llcontext: &'ll llvm::Context,
llmod: &'ll llvm::Module,
builder: &'ll mut DIBuilder<'ll>,
- created_files: RefCell<FxHashMap<(Option<String>, Option<String>), &'ll DIFile>>,
+ created_files: RefCell<FxHashMap<Option<(u128, SourceFileHash)>, &'ll DIFile>>,
type_map: metadata::TypeMap<'ll, 'tcx>,
namespace_map: RefCell<DefIdMap<&'ll DIScope>>,
span: Span,
) -> Result<&'ll Value, ()> {
// macros for error handling:
+ #[cfg_attr(not(bootstrap), allow(unused_macro_rules))]
macro_rules! emit_error {
($msg: tt) => {
emit_error!($msg, )
span: Span,
args: &[OperandRef<'tcx, &'ll Value>],
) -> Result<&'ll Value, ()> {
+ #[cfg_attr(not(bootstrap), allow(unused_macro_rules))]
macro_rules! emit_error {
($msg: tt) => {
emit_error!($msg, )
rustc_session = { path = "../rustc_session" }
[dependencies.object]
-version = "0.28.0"
+version = "0.28.4"
default-features = false
features = ["read_core", "elf", "macho", "pe", "unaligned", "archive", "write"]
use rustc_session::cstore::MetadataLoader;
use rustc_session::Session;
use rustc_target::abi::Endian;
-use rustc_target::spec::Target;
+use rustc_target::spec::{RelocModel, Target};
use crate::METADATA_FILENAME;
let mut file = write::Object::new(binary_format, architecture, endianness);
match architecture {
Architecture::Mips => {
- // copied from `mipsel-linux-gnu-gcc foo.c -c` and
- // inspecting the resulting `e_flags` field.
- let e_flags = elf::EF_MIPS_CPIC
- | elf::EF_MIPS_PIC
- | if sess.target.options.cpu.contains("r6") {
- elf::EF_MIPS_ARCH_32R6 | elf::EF_MIPS_NAN2008
- } else {
- elf::EF_MIPS_ARCH_32R2
- };
+ let arch = match sess.target.options.cpu.as_ref() {
+ "mips1" => elf::EF_MIPS_ARCH_1,
+ "mips2" => elf::EF_MIPS_ARCH_2,
+ "mips3" => elf::EF_MIPS_ARCH_3,
+ "mips4" => elf::EF_MIPS_ARCH_4,
+ "mips5" => elf::EF_MIPS_ARCH_5,
+ s if s.contains("r6") => elf::EF_MIPS_ARCH_32R6,
+ _ => elf::EF_MIPS_ARCH_32R2,
+ };
+ // The only ABI LLVM supports for 32-bit MIPS CPUs is o32.
+ let mut e_flags = elf::EF_MIPS_CPIC | elf::EF_MIPS_ABI_O32 | arch;
+ if sess.target.options.relocation_model != RelocModel::Static {
+ e_flags |= elf::EF_MIPS_PIC;
+ }
+ if sess.target.options.cpu.contains("r6") {
+ e_flags |= elf::EF_MIPS_NAN2008;
+ }
file.flags = FileFlags::Elf { e_flags };
}
Architecture::Mips64 => {
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
+use rustc_data_structures::sync::par_iter;
#[cfg(parallel_compiler)]
-use rustc_data_structures::sync::{par_iter, ParallelIterator};
+use rustc_data_structures::sync::ParallelIterator;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_hir::lang_items::LangItem;
second_half.iter().rev().interleave(first_half).copied().collect()
};
+ // Calculate the CGU reuse
+ let cgu_reuse = tcx.sess.time("find_cgu_reuse", || {
+ codegen_units.iter().map(|cgu| determine_cgu_reuse(tcx, &cgu)).collect::<Vec<_>>()
+ });
+
+ let mut total_codegen_time = Duration::new(0, 0);
+ let start_rss = tcx.sess.time_passes().then(|| get_resident_set_size());
+
// The non-parallel compiler can only translate codegen units to LLVM IR
// on a single thread, leading to a staircase effect where the N LLVM
// threads have to wait on the single codegen threads to generate work
// This likely is a temporary measure. Once we don't have to support the
// non-parallel compiler anymore, we can compile CGUs end-to-end in
// parallel and get rid of the complicated scheduling logic.
- #[cfg(parallel_compiler)]
- let pre_compile_cgus = |cgu_reuse: &[CguReuse]| {
+ let mut pre_compiled_cgus = if cfg!(parallel_compiler) {
tcx.sess.time("compile_first_CGU_batch", || {
// Try to find one CGU to compile per thread.
let cgus: Vec<_> = cgu_reuse
})
.collect();
- (pre_compiled_cgus, start_time.elapsed())
+ total_codegen_time += start_time.elapsed();
+
+ pre_compiled_cgus
})
+ } else {
+ FxHashMap::default()
};
- #[cfg(not(parallel_compiler))]
- let pre_compile_cgus = |_: &[CguReuse]| (FxHashMap::default(), Duration::new(0, 0));
-
- let mut cgu_reuse = Vec::new();
- let mut pre_compiled_cgus: Option<FxHashMap<usize, _>> = None;
- let mut total_codegen_time = Duration::new(0, 0);
- let start_rss = tcx.sess.time_passes().then(|| get_resident_set_size());
-
for (i, cgu) in codegen_units.iter().enumerate() {
ongoing_codegen.wait_for_signal_to_codegen_item();
ongoing_codegen.check_for_errors(tcx.sess);
- // Do some setup work in the first iteration
- if pre_compiled_cgus.is_none() {
- // Calculate the CGU reuse
- cgu_reuse = tcx.sess.time("find_cgu_reuse", || {
- codegen_units.iter().map(|cgu| determine_cgu_reuse(tcx, &cgu)).collect()
- });
- // Pre compile some CGUs
- let (compiled_cgus, codegen_time) = pre_compile_cgus(&cgu_reuse);
- pre_compiled_cgus = Some(compiled_cgus);
- total_codegen_time += codegen_time;
- }
-
let cgu_reuse = cgu_reuse[i];
tcx.sess.cgu_reuse_tracker.set_actual_reuse(cgu.name().as_str(), cgu_reuse);
match cgu_reuse {
CguReuse::No => {
- let (module, cost) =
- if let Some(cgu) = pre_compiled_cgus.as_mut().unwrap().remove(&i) {
- cgu
- } else {
- let start_time = Instant::now();
- let module = backend.compile_codegen_unit(tcx, cgu.name());
- total_codegen_time += start_time.elapsed();
- module
- };
+ let (module, cost) = if let Some(cgu) = pre_compiled_cgus.remove(&i) {
+ cgu
+ } else {
+ let start_time = Instant::now();
+ let module = backend.compile_codegen_unit(tcx, cgu.name());
+ total_codegen_time += start_time.elapsed();
+ module
+ };
// This will unwind if there are errors, which triggers our `AbortCodegenOnDrop`
// guard. Unfortunately, just skipping the `submit_codegened_module_to_llvm` makes
// compilation hang on post-monomorphization errors.
bb, data, result[bb], funclet
);
- for &succ in data.terminator().successors() {
+ for succ in data.terminator().successors() {
let kind = result[succ];
debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}", funclet, succ, kind);
match kind {
}
}
- sym::ptr_offset_from => {
+ sym::ptr_offset_from | sym::ptr_offset_from_unsigned => {
let ty = substs.type_at(0);
let pointee_size = bx.layout_of(ty).size;
- // This is the same sequence that Clang emits for pointer subtraction.
- // It can be neither `nsw` nor `nuw` because the input is treated as
- // unsigned but then the output is treated as signed, so neither works.
let a = args[0].immediate();
let b = args[1].immediate();
let a = bx.ptrtoint(a, bx.type_isize());
let b = bx.ptrtoint(b, bx.type_isize());
- let d = bx.sub(a, b);
let pointee_size = bx.const_usize(pointee_size.bytes());
- // this is where the signed magic happens (notice the `s` in `exactsdiv`)
- bx.exactsdiv(d, pointee_size)
+ if name == sym::ptr_offset_from {
+ // This is the same sequence that Clang emits for pointer subtraction.
+ // It can be neither `nsw` nor `nuw` because the input is treated as
+ // unsigned but then the output is treated as signed, so neither works.
+ let d = bx.sub(a, b);
+ // this is where the signed magic happens (notice the `s` in `exactsdiv`)
+ bx.exactsdiv(d, pointee_size)
+ } else {
+ // The `_unsigned` version knows the relative ordering of the pointers,
+ // so can use `sub nuw` and `udiv exact` instead of dealing in signed.
+ let d = bx.unchecked_usub(a, b);
+ bx.exactudiv(d, pointee_size)
+ }
}
_ => {
use rustc_middle::traits::Reveal;
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_middle::ty::{self, subst::Subst, TyCtxt};
+use rustc_middle::ty::{self, subst::Subst, EarlyBinder, TyCtxt};
use rustc_span::source_map::Span;
use rustc_target::abi::{self, Abi};
use std::borrow::Cow;
"Unexpected DefKind: {:?}",
ecx.tcx.def_kind(cid.instance.def_id())
);
- let layout = ecx.layout_of(body.return_ty().subst(tcx, cid.instance.substs))?;
+ let layout = ecx.layout_of(EarlyBinder(body.return_ty()).subst(tcx, cid.instance.substs))?;
assert!(!layout.is_unsized());
let ret = ecx.allocate(layout, MemoryKind::Stack)?;
} else {
// It is guaranteed that any non-slice scalar pair is actually ByRef here.
// When we come back from raw const eval, we are always by-ref. The only way our op here is
- // by-val is if we are in destructure_const, i.e., if this is (a field of) something that we
+ // by-val is if we are in destructure_mir_constant, i.e., if this is (a field of) something that we
// "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or
// structs containing such.
op.try_as_mplace()
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{DefIdTree, TyCtxt};
use rustc_span::symbol::Symbol;
-use rustc_target::spec::abi::Abi;
/// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it
pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Symbol> {
hir::Node::ForeignItem(hir::ForeignItem { kind: hir::ForeignItemKind::Fn(..), .. }) => {
// Intrinsics use `rustc_const_{un,}stable` attributes to indicate constness. All other
// foreign items cannot be evaluated at compile-time.
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
- let is_const = if let Abi::RustIntrinsic | Abi::PlatformIntrinsic =
- tcx.hir().get_foreign_abi(hir_id)
- {
+ let is_const = if tcx.is_intrinsic(def_id) {
tcx.lookup_const_stability(def_id).is_some()
} else {
false
use crate::interpret::{
self, compile_time_machine, AllocId, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult,
- OpTy, PlaceTy, Scalar, StackPopUnwind,
+ OpTy, PlaceTy, Pointer, Scalar, StackPopUnwind,
};
use super::error::*;
Ok(())
}
+ #[inline(always)]
+ fn expose_ptr(
+ _ecx: &mut InterpCx<'mir, 'tcx, Self>,
+ _ptr: Pointer<AllocId>,
+ ) -> InterpResult<'tcx> {
+ Err(ConstEvalErrKind::NeedsRfc("exposing pointers".to_string()).into())
+ }
+
#[inline(always)]
fn init_frame_extra(
ecx: &mut InterpCx<'mir, 'tcx, Self>,
use rustc_hir::Mutability;
use rustc_middle::mir;
+use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};
pub use eval_queries::*;
pub use fn_queries::*;
pub use machine::*;
-pub(crate) use valtrees::{const_to_valtree, valtree_to_const_value};
+pub(crate) use valtrees::{const_to_valtree_inner, valtree_to_const_value};
pub(crate) fn const_caller_location(
tcx: TyCtxt<'_>,
ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr, &tcx))
}
+// We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes.
+const VALTREE_MAX_NODES: usize = 1000;
+
+pub(crate) enum ValTreeCreationError {
+ NodesOverflow,
+ NonSupportedType,
+ Other,
+}
+pub(crate) type ValTreeCreationResult<'tcx> = Result<ty::ValTree<'tcx>, ValTreeCreationError>;
+
+/// Evaluates a constant and turns it into a type-level constant value.
+pub(crate) fn eval_to_valtree<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ cid: GlobalId<'tcx>,
+) -> EvalToValTreeResult<'tcx> {
+ let const_alloc = tcx.eval_to_allocation_raw(param_env.and(cid))?;
+ let ecx = mk_eval_cx(
+ tcx, DUMMY_SP, param_env,
+ // It is absolutely crucial for soundness that
+ // we do not read from static items or other mutable memory.
+ false,
+ );
+ let place = ecx.raw_const_to_mplace(const_alloc).unwrap();
+ debug!(?place);
+
+ let mut num_nodes = 0;
+ let valtree_result = const_to_valtree_inner(&ecx, &place, &mut num_nodes);
+
+ match valtree_result {
+ Ok(valtree) => Ok(Some(valtree)),
+ Err(err) => {
+ let did = cid.instance.def_id();
+ let s = cid.display(tcx);
+ match err {
+ ValTreeCreationError::NodesOverflow => {
+ let msg = format!("maximum number of nodes exceeded in constant {}", &s);
+ let mut diag = match tcx.hir().span_if_local(did) {
+ Some(span) => tcx.sess.struct_span_err(span, &msg),
+ None => tcx.sess.struct_err(&msg),
+ };
+ diag.emit();
+
+ Ok(None)
+ }
+ ValTreeCreationError::NonSupportedType | ValTreeCreationError::Other => Ok(None),
+ }
+ }
+ }
+}
+
/// This function should never fail for validated constants. However, it is also invoked from the
/// pretty printer which might attempt to format invalid constants and in that case it might fail.
pub(crate) fn try_destructure_const<'tcx>(
trace!("destructure_const: {:?}", val);
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
let op = ecx.const_to_op(val, None)?;
-
// We go to `usize` as we cannot allocate anything bigger anyway.
let (field_count, variant, down) = match val.ty().kind() {
ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op),
ty::Tuple(substs) => (substs.len(), None, op),
_ => bug!("cannot destructure constant {:?}", val),
};
-
let fields = (0..field_count)
.map(|i| {
let field_op = ecx.operand_field(&down, i)?;
})
.collect::<InterpResult<'tcx, Vec<_>>>()?;
let fields = tcx.arena.alloc_from_iter(fields);
-
Ok(mir::DestructuredConst { variant, fields })
}
+#[instrument(skip(tcx), level = "debug")]
+pub(crate) fn try_destructure_mir_constant<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ val: mir::ConstantKind<'tcx>,
+) -> InterpResult<'tcx, mir::DestructuredMirConstant<'tcx>> {
+ trace!("destructure_mir_constant: {:?}", val);
+ let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
+ let op = ecx.mir_const_to_op(&val, None)?;
+
+ // We go to `usize` as we cannot allocate anything bigger anyway.
+ let (field_count, variant, down) = match val.ty().kind() {
+ ty::Array(_, len) => (len.eval_usize(tcx, param_env) as usize, None, op),
+ ty::Adt(def, _) if def.variants().is_empty() => {
+ throw_ub!(Unreachable)
+ }
+ ty::Adt(def, _) => {
+ let variant = ecx.read_discriminant(&op).unwrap().1;
+ let down = ecx.operand_downcast(&op, variant).unwrap();
+ (def.variants()[variant].fields.len(), Some(variant), down)
+ }
+ ty::Tuple(substs) => (substs.len(), None, op),
+ _ => bug!("cannot destructure mir constant {:?}", val),
+ };
+
+ let fields_iter = (0..field_count)
+ .map(|i| {
+ let field_op = ecx.operand_field(&down, i)?;
+ let val = op_to_const(&ecx, &field_op);
+ Ok(mir::ConstantKind::Val(val, field_op.layout.ty))
+ })
+ .collect::<InterpResult<'tcx, Vec<_>>>()?;
+ let fields = tcx.arena.alloc_from_iter(fields_iter);
+
+ Ok(mir::DestructuredMirConstant { variant, fields })
+}
+
#[instrument(skip(tcx), level = "debug")]
pub(crate) fn deref_const<'tcx>(
tcx: TyCtxt<'tcx>,
tcx.mk_const(ty::ConstS { val: ty::ConstKind::Value(op_to_const(&ecx, &mplace.into())), ty })
}
+
+#[instrument(skip(tcx), level = "debug")]
+pub(crate) fn deref_mir_constant<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ val: mir::ConstantKind<'tcx>,
+) -> mir::ConstantKind<'tcx> {
+ let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
+ let op = ecx.mir_const_to_op(&val, None).unwrap();
+ let mplace = ecx.deref_operand(&op).unwrap();
+ if let Some(alloc_id) = mplace.ptr.provenance {
+ assert_eq!(
+ tcx.get_global_alloc(alloc_id).unwrap().unwrap_memory().0.0.mutability,
+ Mutability::Not,
+ "deref_const cannot be used with mutable allocations as \
+ that could allow pattern matching to observe mutable statics",
+ );
+ }
+
+ let ty = match mplace.meta {
+ MemPlaceMeta::None => mplace.layout.ty,
+ MemPlaceMeta::Poison => bug!("poison metadata in `deref_const`: {:#?}", mplace),
+ // In case of unsized types, figure out the real type behind.
+ MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() {
+ ty::Str => bug!("there's no sized equivalent of a `str`"),
+ ty::Slice(elem_ty) => tcx.mk_array(*elem_ty, scalar.to_machine_usize(&tcx).unwrap()),
+ _ => bug!(
+ "type {} should not have metadata, but had {:?}",
+ mplace.layout.ty,
+ mplace.meta
+ ),
+ },
+ };
+
+ mir::ConstantKind::Val(op_to_const(&ecx, &mplace.into()), ty)
+}
use super::eval_queries::{mk_eval_cx, op_to_const};
use super::machine::CompileTimeEvalContext;
+use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES};
use crate::interpret::{
intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta,
MemoryKind, PlaceTy, Scalar, ScalarMaybeUninit,
};
-use rustc_middle::mir::interpret::ConstAlloc;
-use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
use rustc_span::source_map::DUMMY_SP;
use rustc_target::abi::{Align, VariantIdx};
use crate::interpret::MPlaceTy;
use crate::interpret::Value;
-
-/// Convert an evaluated constant to a type level constant
-#[instrument(skip(tcx), level = "debug")]
-pub(crate) fn const_to_valtree<'tcx>(
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- raw: ConstAlloc<'tcx>,
-) -> Option<ty::ValTree<'tcx>> {
- let ecx = mk_eval_cx(
- tcx, DUMMY_SP, param_env,
- // It is absolutely crucial for soundness that
- // we do not read from static items or other mutable memory.
- false,
- );
- let place = ecx.raw_const_to_mplace(raw).unwrap();
- const_to_valtree_inner(&ecx, &place)
-}
+use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
#[instrument(skip(ecx), level = "debug")]
fn branches<'tcx>(
place: &MPlaceTy<'tcx>,
n: usize,
variant: Option<VariantIdx>,
-) -> Option<ty::ValTree<'tcx>> {
+ num_nodes: &mut usize,
+) -> ValTreeCreationResult<'tcx> {
let place = match variant {
Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(),
None => *place,
let variant = variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32()))));
debug!(?place, ?variant);
- let fields = (0..n).map(|i| {
+ let mut fields = Vec::with_capacity(n);
+ for i in 0..n {
let field = ecx.mplace_field(&place, i).unwrap();
- const_to_valtree_inner(ecx, &field)
- });
- // For enums, we preped their variant index before the variant's fields so we can figure out
+ let valtree = const_to_valtree_inner(ecx, &field, num_nodes)?;
+ fields.push(Some(valtree));
+ }
+
+ // For enums, we prepend their variant index before the variant's fields so we can figure out
// the variant again when just seeing a valtree.
- let branches = variant.into_iter().chain(fields);
- Some(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?)))
+ let branches = variant
+ .into_iter()
+ .chain(fields.into_iter())
+ .collect::<Option<Vec<_>>>()
+ .expect("should have already checked for errors in ValTree creation");
+
+ // Have to account for ZSTs here
+ if branches.len() == 0 {
+ *num_nodes += 1;
+ }
+
+ Ok(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches)))
}
#[instrument(skip(ecx), level = "debug")]
fn slice_branches<'tcx>(
ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
place: &MPlaceTy<'tcx>,
-) -> Option<ty::ValTree<'tcx>> {
- let n = place.len(&ecx.tcx.tcx).expect(&format!("expected to use len of place {:?}", place));
- let branches = (0..n).map(|i| {
+ num_nodes: &mut usize,
+) -> ValTreeCreationResult<'tcx> {
+ let n = place
+ .len(&ecx.tcx.tcx)
+ .unwrap_or_else(|_| panic!("expected to use len of place {:?}", place));
+
+ let mut elems = Vec::with_capacity(n as usize);
+ for i in 0..n {
let place_elem = ecx.mplace_index(place, i).unwrap();
- const_to_valtree_inner(ecx, &place_elem)
- });
+ let valtree = const_to_valtree_inner(ecx, &place_elem, num_nodes)?;
+ elems.push(valtree);
+ }
- Some(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?)))
+ Ok(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(elems)))
}
#[instrument(skip(ecx), level = "debug")]
-fn const_to_valtree_inner<'tcx>(
+pub(crate) fn const_to_valtree_inner<'tcx>(
ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
place: &MPlaceTy<'tcx>,
-) -> Option<ty::ValTree<'tcx>> {
- match place.layout.ty.kind() {
- ty::FnDef(..) => Some(ty::ValTree::zst()),
+ num_nodes: &mut usize,
+) -> ValTreeCreationResult<'tcx> {
+ if *num_nodes >= VALTREE_MAX_NODES {
+ return Err(ValTreeCreationError::NodesOverflow);
+ }
+
+ let ty = place.layout.ty;
+ debug!("ty kind: {:?}", ty.kind());
+
+ match ty.kind() {
+ ty::FnDef(..) => {
+ *num_nodes += 1;
+ Ok(ty::ValTree::zst())
+ }
ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
- let val = ecx.read_immediate(&place.into()).unwrap();
+ let Ok(val) = ecx.read_immediate(&place.into()) else {
+ return Err(ValTreeCreationError::Other);
+ };
let val = val.to_scalar().unwrap();
- Some(ty::ValTree::Leaf(val.assert_int()))
+ *num_nodes += 1;
+
+ Ok(ty::ValTree::Leaf(val.assert_int()))
}
// Raw pointers are not allowed in type level constants, as we cannot properly test them for
// equality at compile-time (see `ptr_guaranteed_eq`/`_ne`).
// Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to
// agree with runtime equality tests.
- ty::FnPtr(_) | ty::RawPtr(_) => None,
+ ty::FnPtr(_) | ty::RawPtr(_) => Err(ValTreeCreationError::NonSupportedType),
ty::Ref(_, _, _) => {
- let derefd_place = ecx.deref_operand(&place.into()).unwrap_or_else(|e| bug!("couldn't deref {:?}, error: {:?}", place, e));
+ let Ok(derefd_place)= ecx.deref_operand(&place.into()) else {
+ return Err(ValTreeCreationError::Other);
+ };
debug!(?derefd_place);
- const_to_valtree_inner(ecx, &derefd_place)
+ const_to_valtree_inner(ecx, &derefd_place, num_nodes)
}
ty::Str | ty::Slice(_) | ty::Array(_, _) => {
- let valtree = slice_branches(ecx, place);
- debug!(?valtree);
-
- valtree
+ slice_branches(ecx, place, num_nodes)
}
// Trait objects are not allowed in type level constants, as we have no concept for
// resolving their backing type, even if we can do that at const eval time. We may
// hypothetically be able to allow `dyn StructuralEq` trait objects in the future,
// but it is unclear if this is useful.
- ty::Dynamic(..) => None,
+ ty::Dynamic(..) => Err(ValTreeCreationError::NonSupportedType),
- ty::Tuple(substs) => branches(ecx, place, substs.len(), None),
+ ty::Tuple(elem_tys) => {
+ branches(ecx, place, elem_tys.len(), None, num_nodes)
+ }
ty::Adt(def, _) => {
if def.is_union() {
- return None
+ return Err(ValTreeCreationError::NonSupportedType);
} else if def.variants().is_empty() {
bug!("uninhabited types should have errored and never gotten converted to valtree")
}
- let variant = ecx.read_discriminant(&place.into()).unwrap().1;
-
- branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant))
+ let Ok((_, variant)) = ecx.read_discriminant(&place.into()) else {
+ return Err(ValTreeCreationError::Other);
+ };
+ branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes)
}
ty::Never
// FIXME(oli-obk): we can probably encode closures just like structs
| ty::Closure(..)
| ty::Generator(..)
- | ty::GeneratorWitness(..) => None,
+ | ty::GeneratorWitness(..) => Err(ValTreeCreationError::NonSupportedType),
}
}
.unwrap();
debug!(?ptr);
- let mut place = MPlaceTy::from_aligned_ptr(ptr.into(), layout);
- place.meta = MemPlaceMeta::Meta(Scalar::from_u64(num_elems as u64));
+ let place = MPlaceTy::from_aligned_ptr_with_meta(
+ ptr.into(),
+ layout,
+ MemPlaceMeta::Meta(Scalar::from_u64(num_elems as u64)),
+ );
debug!(?place);
place
/// Converts a `ValTree` to a `ConstValue`, which is needed after mir
/// construction has finished.
-// FIXME Merge `valtree_to_const_value` and `fill_place_recursively` into one function
+// FIXME Merge `valtree_to_const_value` and `valtree_into_mplace` into one function
#[instrument(skip(tcx), level = "debug")]
pub fn valtree_to_const_value<'tcx>(
tcx: TyCtxt<'tcx>,
- param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
+ ty: Ty<'tcx>,
valtree: ty::ValTree<'tcx>,
) -> ConstValue<'tcx> {
// Basic idea: We directly construct `Scalar` values from trivial `ValTree`s
// create inner `MPlace`s which are filled recursively.
// FIXME Does this need an example?
- let (param_env, ty) = param_env_ty.into_parts();
- let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
+ let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::empty(), false);
+ let param_env_ty = ty::ParamEnv::empty().and(ty);
match ty.kind() {
ty::FnDef(..) => {
};
debug!(?place);
- fill_place_recursively(&mut ecx, &mut place, valtree);
+ valtree_into_mplace(&mut ecx, &mut place, valtree);
dump_place(&ecx, place.into());
intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap();
// FIXME Needs a better/correct name
#[instrument(skip(ecx), level = "debug")]
-fn fill_place_recursively<'tcx>(
+fn valtree_into_mplace<'tcx>(
ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>,
place: &mut MPlaceTy<'tcx>,
valtree: ty::ValTree<'tcx>,
let mut pointee_place = create_pointee_place(ecx, *inner_ty, valtree);
debug!(?pointee_place);
- fill_place_recursively(ecx, &mut pointee_place, valtree);
+ valtree_into_mplace(ecx, &mut pointee_place, valtree);
dump_place(ecx, pointee_place.into());
intern_const_alloc_recursive(ecx, InternKind::Constant, &pointee_place).unwrap();
};
debug!(?place_inner);
- fill_place_recursively(ecx, &mut place_inner, *inner_valtree);
+ valtree_into_mplace(ecx, &mut place_inner, *inner_valtree);
dump_place(&ecx, place_inner.into());
}
}
pub fn misc_cast(
- &self,
+ &mut self,
src: &ImmTy<'tcx, M::PointerTag>,
cast_ty: Ty<'tcx>,
) -> InterpResult<'tcx, Immediate<M::PointerTag>> {
if let Some(discr) = src.layout.ty.discriminant_for_variant(*self.tcx, index) {
assert!(src.layout.is_zst());
let discr_layout = self.layout_of(discr.ty)?;
- return Ok(self.cast_from_int_like(discr.val, discr_layout, cast_ty).into());
+
+ let scalar = Scalar::from_uint(discr.val, discr_layout.layout.size());
+ return Ok(self.cast_from_int_like(scalar, discr_layout, cast_ty)?.into());
}
}
Variants::Multiple { .. } => {}
}
// # The remaining source values are scalar and "int-like".
+ let scalar = src.to_scalar()?;
+
+ // If we are casting from a pointer to something
+ // that is not a pointer, mark the pointer as exposed
+ if src.layout.ty.is_any_ptr() && !cast_ty.is_any_ptr() {
+ let ptr = self.scalar_to_ptr(scalar)?;
+
+ match ptr.into_pointer_or_addr() {
+ Ok(ptr) => {
+ M::expose_ptr(self, ptr)?;
+ }
+ Err(_) => {
+ // do nothing, exposing an invalid pointer
+ // has no meaning
+ }
+ };
+ }
- // For all remaining casts, we either
- // (a) cast a raw ptr to usize, or
- // (b) cast from an integer-like (including bool, char, enums).
- // In both cases we want the bits.
- let bits = src.to_scalar()?.to_bits(src.layout.size)?;
- Ok(self.cast_from_int_like(bits, src.layout, cast_ty).into())
+ Ok(self.cast_from_int_like(scalar, src.layout, cast_ty)?.into())
}
- fn cast_from_int_like(
+ pub fn cast_from_int_like(
&self,
- v: u128, // raw bits (there is no ScalarTy so we separate data+layout)
+ scalar: Scalar<M::PointerTag>, // input value (there is no ScalarTy so we separate data+layout)
src_layout: TyAndLayout<'tcx>,
cast_ty: Ty<'tcx>,
- ) -> Scalar<M::PointerTag> {
+ ) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
// Let's make sure v is sign-extended *if* it has a signed type.
let signed = src_layout.abi.is_signed(); // Also asserts that abi is `Scalar`.
+
+ let v = scalar.to_bits(src_layout.size)?;
let v = if signed { self.sign_extend(v, src_layout) } else { v };
trace!("cast_from_scalar: {}, {} -> {}", v, src_layout.ty, cast_ty);
use rustc_middle::ty::TyKind::*;
- match *cast_ty.kind() {
- Int(_) | Uint(_) | RawPtr(_) => {
+
+ Ok(match *cast_ty.kind() {
+ Int(_) | Uint(_) => {
let size = match *cast_ty.kind() {
Int(t) => Integer::from_int_ty(self, t).size(),
Uint(t) => Integer::from_uint_ty(self, t).size(),
- RawPtr(_) => self.pointer_size(),
_ => bug!(),
};
let v = size.truncate(v);
Scalar::from_uint(v, size)
}
+ RawPtr(_) => {
+ assert!(src_layout.ty.is_integral());
+
+ let size = self.pointer_size();
+ let addr = u64::try_from(size.truncate(v)).unwrap();
+
+ let ptr = M::ptr_from_addr_cast(&self, addr);
+ if addr == 0 {
+ assert!(ptr.provenance.is_none(), "null pointer can never have an AllocId");
+ }
+ Scalar::from_maybe_pointer(ptr, self)
+ }
+
Float(FloatTy::F32) if signed => Scalar::from_f32(Single::from_i128(v as i128).value),
Float(FloatTy::F64) if signed => Scalar::from_f64(Double::from_i128(v as i128).value),
Float(FloatTy::F32) => Scalar::from_f32(Single::from_u128(v).value),
// Casts to bool are not permitted by rustc, no need to handle them here.
_ => span_bug!(self.cur_span(), "invalid int to {:?} cast", cast_ty),
- }
+ })
}
fn cast_from_float<F>(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar<M::PointerTag>
trace!(
"deallocating local {:?}: {:?}",
local,
- self.dump_alloc(ptr.provenance.unwrap().get_alloc_id())
+ // Locals always have a `alloc_id` (they are never the result of a int2ptr).
+ self.dump_alloc(ptr.provenance.unwrap().get_alloc_id().unwrap())
);
self.deallocate_ptr(ptr, None, MemoryKind::Stack)?;
};
}
}
- write!(fmt, ": {:?}", self.ecx.dump_allocs(allocs))
+ write!(
+ fmt,
+ ": {:?}",
+ self.ecx.dump_allocs(allocs.into_iter().filter_map(|x| x).collect())
+ )
}
- Place::Ptr(mplace) => match mplace.ptr.provenance.map(Provenance::get_alloc_id) {
+ Place::Ptr(mplace) => match mplace.ptr.provenance.and_then(Provenance::get_alloc_id) {
Some(alloc_id) => write!(
fmt,
"by align({}) ref {:?}: {:?}",
let offset_ptr = ptr.wrapping_signed_offset(offset_bytes, self);
self.write_pointer(offset_ptr, dest)?;
}
- sym::ptr_offset_from => {
+ sym::ptr_offset_from | sym::ptr_offset_from_unsigned => {
let a = self.read_pointer(&args[0])?;
let b = self.read_pointer(&args[1])?;
// Both are pointers. They must be into the same allocation.
if a_alloc_id != b_alloc_id {
throw_ub_format!(
- "ptr_offset_from cannot compute offset of pointers into different \
- allocations.",
+ "{} cannot compute offset of pointers into different allocations.",
+ intrinsic_name,
);
}
// And they must both be valid for zero-sized accesses ("in-bounds or one past the end").
CheckInAllocMsg::OffsetFromTest,
)?;
+ if intrinsic_name == sym::ptr_offset_from_unsigned && a_offset < b_offset {
+ throw_ub_format!(
+ "{} cannot compute a negative offset, but {} < {}",
+ intrinsic_name,
+ a_offset.bytes(),
+ b_offset.bytes(),
+ );
+ }
+
// Compute offset.
let usize_layout = self.layout_of(self.tcx.types.usize)?;
let isize_layout = self.layout_of(self.tcx.types.isize)?;
- let a_offset = ImmTy::from_uint(a_offset.bytes(), usize_layout);
- let b_offset = ImmTy::from_uint(b_offset.bytes(), usize_layout);
- let (val, _overflowed, _ty) =
+ let ret_layout = if intrinsic_name == sym::ptr_offset_from {
+ isize_layout
+ } else {
+ usize_layout
+ };
+
+ // The subtraction is always done in `isize` to enforce
+ // the "no more than `isize::MAX` apart" requirement.
+ let a_offset = ImmTy::from_uint(a_offset.bytes(), isize_layout);
+ let b_offset = ImmTy::from_uint(b_offset.bytes(), isize_layout);
+ let (val, overflowed, _ty) =
self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?;
+ if overflowed {
+ throw_ub_format!("Pointers were too far apart for {}", intrinsic_name);
+ }
+
let pointee_layout = self.layout_of(substs.type_at(0))?;
- let val = ImmTy::from_scalar(val, isize_layout);
- let size = ImmTy::from_int(pointee_layout.size.bytes(), isize_layout);
+ // This re-interprets an isize at ret_layout, but we already checked
+ // that if ret_layout is usize, then the result must be non-negative.
+ let val = ImmTy::from_scalar(val, ret_layout);
+ let size = ImmTy::from_int(pointee_layout.size.bytes(), ret_layout);
self.exact_div(&val, &size, dest)?;
}
}
// Allocate memory for `CallerLocation` struct.
let loc_ty = self
.tcx
- .type_of(self.tcx.require_lang_item(LangItem::PanicLocation, None))
+ .bound_type_of(self.tcx.require_lang_item(LangItem::PanicLocation, None))
.subst(*self.tcx, self.tcx.mk_substs([self.tcx.lifetimes.re_erased.into()].iter()));
let loc_layout = self.layout_of(loc_ty).unwrap();
// This can fail if rustc runs out of memory right here. Trying to emit an error would be
) -> Pointer<Self::PointerTag>;
/// "Int-to-pointer cast"
- fn ptr_from_addr(
+ fn ptr_from_addr_cast(
ecx: &InterpCx<'mir, 'tcx, Self>,
addr: u64,
) -> Pointer<Option<Self::PointerTag>>;
+ // FIXME: Transmuting an integer to a pointer should just always return a `None`
+ // provenance, but that causes problems with function pointers in Miri.
+ /// Hook for returning a pointer from a transmute-like operation on an addr.
+ fn ptr_from_addr_transmute(
+ ecx: &InterpCx<'mir, 'tcx, Self>,
+ addr: u64,
+ ) -> Pointer<Option<Self::PointerTag>>;
+
+ /// Marks a pointer as exposed, allowing it's provenance
+ /// to be recovered. "Pointer-to-int cast"
+ fn expose_ptr(
+ ecx: &mut InterpCx<'mir, 'tcx, Self>,
+ ptr: Pointer<Self::PointerTag>,
+ ) -> InterpResult<'tcx>;
+
/// Convert a pointer with provenance into an allocation-offset pair
/// and extra provenance info.
///
/// The returned `AllocId` must be the same as `ptr.provenance.get_alloc_id()`.
+ ///
+ /// When this fails, that means the pointer does not point to a live allocation.
fn ptr_get_alloc(
ecx: &InterpCx<'mir, 'tcx, Self>,
ptr: Pointer<Self::PointerTag>,
- ) -> (AllocId, Size, Self::TagExtra);
+ ) -> Option<(AllocId, Size, Self::TagExtra)>;
/// Called to initialize the "extra" state of an allocation and make the pointers
/// it contains (in relocations) tagged. The way we construct allocations is
}
#[inline(always)]
- fn ptr_from_addr(_ecx: &InterpCx<$mir, $tcx, Self>, addr: u64) -> Pointer<Option<AllocId>> {
+ fn ptr_from_addr_transmute(
+ _ecx: &InterpCx<$mir, $tcx, Self>,
+ addr: u64,
+ ) -> Pointer<Option<AllocId>> {
+ Pointer::new(None, Size::from_bytes(addr))
+ }
+
+ #[inline(always)]
+ fn ptr_from_addr_cast(
+ _ecx: &InterpCx<$mir, $tcx, Self>,
+ addr: u64,
+ ) -> Pointer<Option<AllocId>> {
Pointer::new(None, Size::from_bytes(addr))
}
fn ptr_get_alloc(
_ecx: &InterpCx<$mir, $tcx, Self>,
ptr: Pointer<AllocId>,
- ) -> (AllocId, Size, Self::TagExtra) {
+ ) -> Option<(AllocId, Size, Self::TagExtra)> {
// We know `offset` is relative to the allocation, so we can use `into_parts`.
let (alloc_id, offset) = ptr.into_parts();
- (alloc_id, offset, ())
+ Some((alloc_id, offset, ()))
}
}
if reachable.insert(id) {
// This is a new allocation, add its relocations to `todo`.
if let Some((_, alloc)) = self.memory.alloc_map.get(id) {
- todo.extend(alloc.relocations().values().map(|tag| tag.get_alloc_id()));
+ todo.extend(
+ alloc.relocations().values().filter_map(|tag| tag.get_alloc_id()),
+ );
}
}
}
allocs_to_print: &mut VecDeque<AllocId>,
alloc: &Allocation<Tag, Extra>,
) -> std::fmt::Result {
- for alloc_id in alloc.relocations().values().map(|tag| tag.get_alloc_id()) {
+ for alloc_id in alloc.relocations().values().filter_map(|tag| tag.get_alloc_id()) {
allocs_to_print.push_back(alloc_id);
}
write!(fmt, "{}", display_allocation(tcx, alloc))
Err(ptr) => ptr.into(),
Ok(bits) => {
let addr = u64::try_from(bits).unwrap();
- let ptr = M::ptr_from_addr(&self, addr);
+ let ptr = M::ptr_from_addr_transmute(&self, addr);
if addr == 0 {
assert!(ptr.provenance.is_none(), "null pointer can never have an AllocId");
}
ptr: Pointer<Option<M::PointerTag>>,
) -> Result<(AllocId, Size, M::TagExtra), u64> {
match ptr.into_pointer_or_addr() {
- Ok(ptr) => {
- let (alloc_id, offset, extra) = M::ptr_get_alloc(self, ptr);
- Ok((alloc_id, offset, extra))
- }
+ Ok(ptr) => match M::ptr_get_alloc(self, ptr) {
+ Some((alloc_id, offset, extra)) => Ok((alloc_id, offset, extra)),
+ None => {
+ assert!(M::PointerTag::OFFSET_IS_ADDR);
+ let (_, addr) = ptr.into_parts();
+ Err(addr.bytes())
+ }
+ },
Err(addr) => Err(addr.bytes()),
}
}
// Figure out which discriminant and variant this corresponds to.
Ok(match *tag_encoding {
TagEncoding::Direct => {
+ let scalar = tag_val.to_scalar()?;
// Generate a specific error if `tag_val` is not an integer.
// (`tag_bits` itself is only used for error messages below.)
- let tag_bits = tag_val
- .to_scalar()?
+ let tag_bits = scalar
.try_to_int()
.map_err(|dbg_val| err_ub!(InvalidTag(dbg_val)))?
.assert_bits(tag_layout.size);
// Cast bits from tag layout to discriminant layout.
- // After the checks we did above, this cannot fail.
+ // After the checks we did above, this cannot fail, as
+ // discriminants are int-like.
let discr_val =
- self.misc_cast(&tag_val, discr_layout.ty).unwrap().to_scalar().unwrap();
+ self.cast_from_int_like(scalar, tag_val.layout, discr_layout.ty).unwrap();
let discr_bits = discr_val.assert_bits(discr_layout.size);
// Convert discriminant to variant index, and catch invalid discriminants.
let index = match *op.layout.ty.kind() {
}
}
-impl<'tcx, Tag: Provenance> std::ops::DerefMut for MPlaceTy<'tcx, Tag> {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.mplace
- }
-}
-
impl<'tcx, Tag: Provenance> From<MPlaceTy<'tcx, Tag>> for PlaceTy<'tcx, Tag> {
#[inline(always)]
fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self {
MPlaceTy { mplace: MemPlace::from_ptr(ptr, layout.align.abi), layout }
}
+ #[inline]
+ pub fn from_aligned_ptr_with_meta(
+ ptr: Pointer<Option<Tag>>,
+ layout: TyAndLayout<'tcx>,
+ meta: MemPlaceMeta<Tag>,
+ ) -> Self {
+ let mut mplace = MemPlace::from_ptr(ptr, layout.align.abi);
+ mplace.meta = meta;
+
+ MPlaceTy { mplace, layout }
+ }
+
#[inline]
pub(crate) fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
if self.layout.is_unsized() {
/// Project into an mplace
#[instrument(skip(self), level = "debug")]
- pub(crate) fn mplace_projection(
+ pub(super) fn mplace_projection(
&self,
base: &MPlaceTy<'tcx, M::PointerTag>,
proj_elem: mir::PlaceElem<'tcx>,
};
match instance.def {
- ty::InstanceDef::Intrinsic(..) => {
- assert!(caller_abi == Abi::RustIntrinsic || caller_abi == Abi::PlatformIntrinsic);
+ ty::InstanceDef::Intrinsic(def_id) => {
+ assert!(self.tcx.is_intrinsic(def_id));
// caller_fn_abi is not relevant here, we interpret the arguments directly for each intrinsic.
M::call_intrinsic(self, instance, args, ret, unwind)
}
self.path,
err_ub!(AlignmentCheckFailed { required, has }) =>
{
- "an unaligned {} (required {} byte alignment but found {})",
- kind,
+ "an unaligned {kind} (required {} byte alignment but found {})",
required.bytes(),
has.bytes()
},
err_ub!(DanglingIntPointer(0, _)) =>
- { "a null {}", kind },
+ { "a null {kind}" },
err_ub!(DanglingIntPointer(i, _)) =>
- { "a dangling {} (address 0x{:x} is unallocated)", kind, i },
+ { "a dangling {kind} (address 0x{i:x} is unallocated)" },
err_ub!(PointerOutOfBounds { .. }) =>
- { "a dangling {} (going beyond the bounds of its allocation)", kind },
+ { "a dangling {kind} (going beyond the bounds of its allocation)" },
// This cannot happen during const-eval (because interning already detects
// dangling pointers), but it can happen in Miri.
err_ub!(PointerUseAfterFree(..)) =>
- { "a dangling {} (use-after-free)", kind },
+ { "a dangling {kind} (use-after-free)" },
);
+ // Do not allow pointers to uninhabited types.
+ if place.layout.abi.is_uninhabited() {
+ throw_validation_failure!(self.path,
+ { "a {kind} pointing to uninhabited type {}", place.layout.ty }
+ )
+ }
// Recursive checking
if let Some(ref mut ref_tracking) = self.ref_tracking {
// Proceed recursively even for ZST, no reason to skip them!
pub mod util;
use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::ParamEnv;
pub fn provide(providers: &mut Providers) {
const_eval::provide(providers);
providers.eval_to_const_value_raw = const_eval::eval_to_const_value_raw_provider;
providers.eval_to_allocation_raw = const_eval::eval_to_allocation_raw_provider;
providers.const_caller_location = const_eval::const_caller_location;
- providers.try_destructure_const = |tcx, param_env_and_value| {
- let (param_env, value) = param_env_and_value.into_parts();
- const_eval::try_destructure_const(tcx, param_env, value).ok()
+ providers.try_destructure_const = |tcx, param_env_and_val| {
+ let (param_env, c) = param_env_and_val.into_parts();
+ const_eval::try_destructure_const(tcx, param_env, c).ok()
};
- providers.const_to_valtree = |tcx, param_env_and_value| {
+ providers.eval_to_valtree = |tcx, param_env_and_value| {
let (param_env, raw) = param_env_and_value.into_parts();
- const_eval::const_to_valtree(tcx, param_env, raw)
+ const_eval::eval_to_valtree(tcx, param_env, raw)
};
- providers.valtree_to_const_val = |tcx, (ty, valtree)| {
- const_eval::valtree_to_const_value(tcx, ParamEnv::empty().and(ty), valtree)
+ providers.try_destructure_mir_constant = |tcx, param_env_and_value| {
+ let (param_env, value) = param_env_and_value.into_parts();
+ const_eval::try_destructure_mir_constant(tcx, param_env, value).ok()
};
+ providers.valtree_to_const_val =
+ |tcx, (ty, valtree)| const_eval::valtree_to_const_value(tcx, ty, valtree);
providers.deref_const = |tcx, param_env_and_value| {
let (param_env, value) = param_env_and_value.into_parts();
const_eval::deref_const(tcx, param_env, value)
};
+ providers.deref_mir_constant = |tcx, param_env_and_value| {
+ let (param_env, value) = param_env_and_value.into_parts();
+ const_eval::deref_mir_constant(tcx, param_env, value)
+ };
}
Status::Unstable(gate) if self.tcx.features().enabled(gate) => {
let unstable_in_stable = self.ccx.is_const_stable_const_fn()
- && !super::rustc_allow_const_fn_unstable(
- self.tcx,
- self.def_id().to_def_id(),
- gate,
- );
+ && !super::rustc_allow_const_fn_unstable(self.tcx, self.def_id(), gate);
if unstable_in_stable {
emit_unstable_in_stable_error(self.ccx, span, gate);
}
#[instrument(level = "debug", skip(self))]
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
- use rustc_target::spec::abi::Abi::RustIntrinsic;
-
self.super_terminator(terminator, location);
match &terminator.kind {
TerminatorKind::Call { func, args, fn_span, from_hir_call, .. } => {
let ConstCx { tcx, body, param_env, .. } = *self.ccx;
- let caller = self.def_id().to_def_id();
+ let caller = self.def_id();
let fn_ty = func.ty(body, tcx);
// trait.
let callee_trait = tcx.trait_of_item(callee);
if callee_trait.is_some()
- && tcx.has_attr(caller, sym::default_method_body_is_const)
+ && tcx.has_attr(caller.to_def_id(), sym::default_method_body_is_const)
&& callee_trait == tcx.trait_of_item(caller)
// Can only call methods when it's `<Self as TheTrait>::f`.
&& tcx.types.self_param == substs.type_at(0)
return;
}
- let is_intrinsic = tcx.fn_sig(callee).abi() == RustIntrinsic;
+ let is_intrinsic = tcx.is_intrinsic(callee);
if !tcx.is_const_fn_raw(callee) {
if tcx.trait_of_item(callee).is_some() {
}
}
-pub fn rustc_allow_const_fn_unstable(tcx: TyCtxt<'_>, def_id: DefId, feature_gate: Symbol) -> bool {
- let attrs = tcx.get_attrs(def_id);
+pub fn rustc_allow_const_fn_unstable(
+ tcx: TyCtxt<'_>,
+ def_id: LocalDefId,
+ feature_gate: Symbol,
+) -> bool {
+ let attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(def_id));
attr::rustc_allow_const_fn_unstable(&tcx.sess, attrs).any(|name| name == feature_gate)
}
//! Concrete error types for all operations which may be invalid in a certain const context.
+use hir::def_id::LocalDefId;
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
ccx: &ConstCx<'_, 'tcx>,
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
- ccx.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn")
+ ccx.tcx.sess.struct_span_err(
+ span,
+ &format!("function pointer calls are not allowed in {}s", ccx.const_kind()),
+ )
}
}
/// A function call where the callee is not marked as `const`.
#[derive(Debug, Clone, Copy)]
pub struct FnCallNonConst<'tcx> {
- pub caller: DefId,
+ pub caller: LocalDefId,
pub callee: DefId,
pub substs: SubstsRef<'tcx>,
pub span: Span,
match self_ty.kind() {
Param(param_ty) => {
debug!(?param_ty);
- if let Some(generics) = caller
- .as_local()
- .map(|id| tcx.hir().local_def_id_to_hir_id(id))
- .map(|id| tcx.hir().get(id))
- .as_ref()
- .and_then(|node| node.generics())
- {
+ let caller_hir_id = tcx.hir().local_def_id_to_hir_id(caller);
+ if let Some(generics) = tcx.hir().get(caller_hir_id).generics() {
let constraint = with_no_trimmed_paths!(format!(
"~const {}",
trait_ref.print_only_trait_path()
//! Validates the MIR to ensure that invariants are upheld.
+use rustc_data_structures::fx::FxHashSet;
use rustc_index::bit_set::BitSet;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::mir::interpret::Scalar;
}
}
let all_len = self.place_cache.len();
- self.place_cache.sort_unstable();
- self.place_cache.dedup();
+ let mut dedup = FxHashSet::default();
+ self.place_cache.retain(|p| dedup.insert(*p));
let has_duplicates = all_len != self.place_cache.len();
if has_duplicates {
self.fail(
+Some linking kinds are target-specific and not supported on all platforms.
+
Linking with `kind=framework` is only supported when targeting macOS,
as frameworks are specific to that operating system.
+Similarly, `kind=raw-dylib` is only supported when targeting Windows-like
+platforms.
+
Erroneous code example:
```ignore (should-compile_fail-but-cannot-doctest-conditionally-without-macos)
* static
* dylib
* framework
+* raw-dylib
parser-maybe-report-ambiguous-plus =
ambiguous `+` in a type
.suggestion = use parentheses to disambiguate
+
+parser-maybe-recover-from-bad-type-plus =
+ expected a path on the left-hand side of `+`, not `{$ty}`
+
+parser-add-paren = try adding parentheses
+
+parser-forgot-paren = perhaps you forgot parentheses?
+
+parser-expect-path = expected a path
cannot provide explicit generic arguments when `impl Trait` is used in argument position
.label = explicit generic argument not allowed
.note = see issue #83701 <https://github.com/rust-lang/rust/issues/83701> for more information
+ .help = add `#![feature(explicit_generic_args_with_impl_trait)]` to the crate attributes to enable
-typeck-explicit-generic-args-with-impl-trait-feature =
- add `#![feature(explicit_generic_args_with_impl_trait)]` to the crate attributes to enable
+typeck-missing-type-params =
+ the type {$parameterCount ->
+ [one] parameter
+ *[other] parameters
+ } {$parameters} must be explicitly specified
+ .label = type {$parameterCount ->
+ [one] parameter
+ *[other] parameters
+ } {$parameters} must be specified for this
+ .suggestion = set the type {$parameterCount ->
+ [one] parameter
+ *[other] parameters
+ } to the desired {$parameterCount ->
+ [one] type
+ *[other] types
+ }
+ .no-suggestion-label = missing {$parameterCount ->
+ [one] reference
+ *[other] references
+ } to {$parameters}
+ .note = because of the default `Self` reference, type parameters must be specified on object types
+
+typeck-manual-implementation =
+ manual implementations of `{$trait_name}` are experimental
+ .label = manual implementations of `{$trait_name}` are experimental
+ .help = add `#![feature(unboxed_closures)]` to the crate attributes to enable
+
+typeck-substs-on-overridden-impl = could not resolve substs on overridden impl
pub fn set_arg(
&mut self,
name: impl Into<Cow<'static, str>>,
- arg: DiagnosticArgValue<'static>,
+ arg: impl IntoDiagnosticArg,
) -> &mut Self {
- self.args.push((name.into(), arg));
+ self.args.push((name.into(), arg.into_diagnostic_arg()));
self
}
-use crate::diagnostic::DiagnosticArgValue;
+use crate::diagnostic::IntoDiagnosticArg;
use crate::{Diagnostic, DiagnosticId, DiagnosticMessage, DiagnosticStyledString, ErrorGuaranteed};
use crate::{Handler, Level, MultiSpan, StashKey};
use rustc_lint_defs::Applicability;
forward!(pub fn set_arg(
&mut self,
name: impl Into<Cow<'static, str>>,
- arg: DiagnosticArgValue<'static>,
+ arg: impl IntoDiagnosticArg,
) -> &mut Self);
forward!(pub fn subdiagnostic(
force: bool,
) -> Result<Lrc<SyntaxExtension>, Indeterminate>;
+ fn record_macro_rule_usage(&mut self, mac_id: NodeId, rule_index: usize);
+
fn check_unused_macros(&mut self);
// Resolver interfaces for specific built-in macros.
use crate::mbe::{KleeneOp, TokenTree};
use rustc_ast::token::{self, DocComment, Nonterminal, NonterminalKind, Token};
+use rustc_lint_defs::pluralize;
use rustc_parse::parser::{NtOrTt, Parser};
use rustc_span::symbol::MacroRulesNormalizedIdent;
use rustc_span::Span;
self.macro_name,
match self.next_mps.len() {
0 => format!("built-in NTs {}.", nts),
- 1 => format!("built-in NTs {} or 1 other option.", nts),
- n => format!("built-in NTs {} or {} other options.", nts, n),
+ n => format!("built-in NTs {} or {n} other option{s}.", nts, s = pluralize!(n)),
}
),
)
}
struct MacroRulesMacroExpander {
+ node_id: NodeId,
name: Ident,
span: Span,
transparency: Transparency,
lhses: Vec<Vec<MatcherLoc>>,
rhses: Vec<mbe::TokenTree>,
valid: bool,
- is_local: bool,
}
impl TTMacroExpander for MacroRulesMacroExpander {
if !self.valid {
return DummyResult::any(sp);
}
- generic_extension(
+ expand_macro(
cx,
sp,
self.span,
+ self.node_id,
self.name,
self.transparency,
input,
&self.lhses,
&self.rhses,
- self.is_local,
)
}
}
cx_expansions.entry(sp).or_default().push(message);
}
-/// Given `lhses` and `rhses`, this is the new macro we create
-fn generic_extension<'cx, 'tt>(
+/// Expands the rules based macro defined by `lhses` and `rhses` for a given
+/// input `arg`.
+fn expand_macro<'cx, 'tt>(
cx: &'cx mut ExtCtxt<'_>,
sp: Span,
def_span: Span,
+ node_id: NodeId,
name: Ident,
transparency: Transparency,
arg: TokenStream,
lhses: &'tt [Vec<MatcherLoc>],
rhses: &'tt [mbe::TokenTree],
- is_local: bool,
) -> Box<dyn MacResult + 'cx> {
let sess = &cx.sess.parse_sess;
+ // Macros defined in the current crate have a real node id,
+ // whereas macros from an external crate have a dummy id.
+ let is_local = node_id != DUMMY_NODE_ID;
if cx.trace_macros() {
let msg = format!("expanding `{}! {{ {} }}`", name, pprust::tts_to_string(&arg));
let mut p = Parser::new(sess, tts, false, None);
p.last_type_ascription = cx.current_expansion.prior_type_ascription;
+ if is_local {
+ cx.resolver.record_macro_rule_usage(node_id, i);
+ }
+
// Let the context choose how to interpret the result.
// Weird, but useful for X-macros.
return Box::new(ParserAnyMacro {
features: &Features,
def: &ast::Item,
edition: Edition,
-) -> SyntaxExtension {
+) -> (SyntaxExtension, Vec<Span>) {
debug!("compile_declarative_macro: {:?}", def);
let mk_syn_ext = |expander| {
SyntaxExtension::new(
&def.attrs,
)
};
+ let dummy_syn_ext = || (mk_syn_ext(Box::new(macro_rules_dummy_expander)), Vec::new());
let diag = &sess.parse_sess.span_diagnostic;
let lhs_nm = Ident::new(sym::lhs, def.span);
let s = parse_failure_msg(&token);
let sp = token.span.substitute_dummy(def.span);
sess.parse_sess.span_diagnostic.struct_span_err(sp, &s).span_label(sp, msg).emit();
- return mk_syn_ext(Box::new(macro_rules_dummy_expander));
+ return dummy_syn_ext();
}
Error(sp, msg) => {
sess.parse_sess
.span_diagnostic
.struct_span_err(sp.substitute_dummy(def.span), &msg)
.emit();
- return mk_syn_ext(Box::new(macro_rules_dummy_expander));
+ return dummy_syn_ext();
}
ErrorReported => {
- return mk_syn_ext(Box::new(macro_rules_dummy_expander));
+ return dummy_syn_ext();
}
};
None => {}
}
+ // Compute the spans of the macro rules
+ // We only take the span of the lhs here,
+ // so that the spans of created warnings are smaller.
+ let rule_spans = if def.id != DUMMY_NODE_ID {
+ lhses.iter().map(|lhs| lhs.span()).collect::<Vec<_>>()
+ } else {
+ Vec::new()
+ };
+
// Convert the lhses into `MatcherLoc` form, which is better for doing the
// actual matching. Unless the matcher is invalid.
let lhses = if valid {
vec![]
};
- mk_syn_ext(Box::new(MacroRulesMacroExpander {
+ let expander = Box::new(MacroRulesMacroExpander {
name: def.ident,
span: def.span,
+ node_id: def.id,
transparency,
lhses,
rhses,
valid,
- // Macros defined in the current crate have a real node id,
- // whereas macros from an external crate have a dummy id.
- is_local: def.id != DUMMY_NODE_ID,
- }))
+ });
+ (mk_syn_ext(expander), rule_spans)
}
fn check_lhs_nt_follows(sess: &ParseSess, def: &ast::Item, lhs: &mbe::TokenTree) -> bool {
FutureWarnPreceding,
}
+/// A conveniece macro to deal with `$($expr)?`.
+macro_rules! or_default {
+ ($default:expr,) => {
+ $default
+ };
+ ($default:expr, $next:expr) => {
+ $next
+ };
+}
+
/// A convenience macro for constructing attribute templates.
/// E.g., `template!(Word, List: "description")` means that the attribute
/// supports forms `#[attr]` and `#[attr(description)]`.
}
macro_rules! ungated {
- ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr $(,)?) => {
+ ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr $(, @only_local: $only_local:expr)? $(,)?) => {
BuiltinAttribute {
name: sym::$attr,
+ only_local: or_default!(false, $($only_local)?),
type_: $typ,
template: $tpl,
gate: Ungated,
}
macro_rules! gated {
- ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $gate:ident, $msg:expr $(,)?) => {
+ ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr $(, @only_local: $only_local:expr)?, $gate:ident, $msg:expr $(,)?) => {
BuiltinAttribute {
name: sym::$attr,
+ only_local: or_default!(false, $($only_local)?),
type_: $typ,
template: $tpl,
duplicates: $duplicates,
gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)),
}
};
- ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $msg:expr $(,)?) => {
+ ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr $(, @only_local: $only_local:expr)?, $msg:expr $(,)?) => {
BuiltinAttribute {
name: sym::$attr,
+ only_local: or_default!(false, $($only_local)?),
type_: $typ,
template: $tpl,
duplicates: $duplicates,
}
macro_rules! rustc_attr {
- (TEST, $attr:ident, $typ:expr, $tpl:expr, $duplicate:expr $(,)?) => {
+ (TEST, $attr:ident, $typ:expr, $tpl:expr, $duplicate:expr $(, @only_local: $only_local:expr)? $(,)?) => {
rustc_attr!(
$attr,
$typ,
$tpl,
$duplicate,
+ $(@only_local: $only_local,)?
concat!(
"the `#[",
stringify!($attr),
),
)
};
- ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $msg:expr $(,)?) => {
+ ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr $(, @only_local: $only_local:expr)?, $msg:expr $(,)?) => {
BuiltinAttribute {
name: sym::$attr,
+ only_local: or_default!(false, $($only_local)?),
type_: $typ,
template: $tpl,
duplicates: $duplicates,
pub struct BuiltinAttribute {
pub name: Symbol,
+ /// Whether this attribute is only used in the local crate.
+ ///
+ /// If so, it is not encoded in the crate metadata.
+ pub only_local: bool,
pub type_: AttributeType,
pub template: AttributeTemplate,
pub duplicates: AttributeDuplicates,
ungated!(must_use, Normal, template!(Word, NameValueStr: "reason"), FutureWarnFollowing),
gated!(
must_not_suspend, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing,
- must_not_suspend, experimental!(must_not_suspend)
+ experimental!(must_not_suspend)
),
ungated!(
deprecated, Normal,
ungated!(repr, Normal, template!(List: "C"), DuplicatesOk),
ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
- ungated!(no_mangle, Normal, template!(Word), WarnFollowing),
- ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing),
+ ungated!(no_mangle, Normal, template!(Word), WarnFollowing, @only_local: true),
+ ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, @only_local: true),
// Limits:
ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing),
ungated!(panic_handler, Normal, template!(Word), WarnFollowing), // RFC 2070
// Code generation:
- ungated!(inline, Normal, template!(Word, List: "always|never"), FutureWarnFollowing),
- ungated!(cold, Normal, template!(Word), WarnFollowing),
+ ungated!(inline, Normal, template!(Word, List: "always|never"), FutureWarnFollowing, @only_local: true),
+ ungated!(cold, Normal, template!(Word), WarnFollowing, @only_local: true),
ungated!(no_builtins, CrateLevel, template!(Word), WarnFollowing),
ungated!(target_feature, Normal, template!(List: r#"enable = "name""#), DuplicatesOk),
ungated!(track_caller, Normal, template!(Word), WarnFollowing),
),
// Linking:
- gated!(naked, Normal, template!(Word), WarnFollowing, naked_functions, experimental!(naked)),
+ gated!(naked, Normal, template!(Word), WarnFollowing, @only_local: true, naked_functions, experimental!(naked)),
gated!(
link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, raw_dylib,
experimental!(link_ordinal)
// Plugins:
BuiltinAttribute {
name: sym::plugin,
+ only_local: false,
type_: CrateLevel,
template: template!(List: "name"),
duplicates: DuplicatesOk,
),
// DuplicatesOk since it has its own validation
ungated!(
- stable, Normal, template!(List: r#"feature = "name", since = "version""#), DuplicatesOk
+ stable, Normal, template!(List: r#"feature = "name", since = "version""#), DuplicatesOk,
),
ungated!(
unstable, Normal,
// ==========================================================================
gated!(
- linkage, Normal, template!(NameValueStr: "external|internal|..."), ErrorPreceding,
+ linkage, Normal, template!(NameValueStr: "external|internal|..."), ErrorPreceding, @only_local: true,
"the `linkage` attribute is experimental and not portable across platforms",
),
rustc_attr!(
- rustc_std_internal_symbol, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE
+ rustc_std_internal_symbol, Normal, template!(Word), WarnFollowing, @only_local: true, INTERNAL_UNSTABLE
),
// ==========================================================================
// Internal attributes, Misc:
// ==========================================================================
gated!(
- lang, Normal, template!(NameValueStr: "name"), DuplicatesOk, lang_items,
+ lang, Normal, template!(NameValueStr: "name"), DuplicatesOk, @only_local: true, lang_items,
"language items are subject to change",
),
rustc_attr!(
"#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference."
),
rustc_attr!(
- rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing,
+ rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, @only_local: true,
"#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`."
),
rustc_attr!(
- rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing,
+ rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: true,
"#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl."
),
rustc_attr!(
),
BuiltinAttribute {
name: sym::rustc_diagnostic_item,
+ // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`.
+ only_local: false,
type_: Normal,
template: template!(NameValueStr: "name"),
duplicates: ErrorFollowing,
"unboxed_closures are still evolving",
),
rustc_attr!(
- rustc_inherit_overflow_checks, Normal, template!(Word), WarnFollowing,
+ rustc_inherit_overflow_checks, Normal, template!(Word), WarnFollowing, @only_local: true,
"the `#[rustc_inherit_overflow_checks]` attribute is just used to control \
overflow checking behavior of several libcore functions that are inlined \
across crates and will never be stable",
BUILTIN_ATTRIBUTE_MAP.get(&name).is_some()
}
+pub fn is_builtin_only_local(name: Symbol) -> bool {
+ BUILTIN_ATTRIBUTE_MAP.get(&name).map_or(false, |attr| attr.only_local)
+}
+
pub static BUILTIN_ATTRIBUTE_MAP: SyncLazy<FxHashMap<Symbol, &BuiltinAttribute>> =
SyncLazy::new(|| {
let mut map = FxHashMap::default();
pub use active::{Features, ACTIVE_FEATURES, INCOMPATIBLE_FEATURES};
pub use builtin_attrs::AttributeDuplicates;
pub use builtin_attrs::{
- deprecated_attributes, find_gated_cfg, is_builtin_attr_name, AttributeGate, AttributeTemplate,
- AttributeType, BuiltinAttribute, GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP,
+ deprecated_attributes, find_gated_cfg, is_builtin_attr_name, is_builtin_only_local,
+ AttributeGate, AttributeTemplate, AttributeType, BuiltinAttribute, GatedCfg,
+ BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP,
};
pub use removed::{REMOVED_FEATURES, STABLE_REMOVED_FEATURES};
//! use with [Graphviz](https://www.graphviz.org/) by walking a labeled
//! graph. (Graphviz can then automatically lay out the nodes and edges
//! of the graph, and also optionally render the graph as an image or
-//! other [output formats](
-//! https://www.graphviz.org/content/output-formats), such as SVG.)
+//! other [output formats](https://www.graphviz.org/docs/outputs), such as SVG.)
//!
//! Rather than impose some particular graph data structure on clients,
//! this library exposes two traits that clients can implement on their
//! own structs before handing them over to the rendering function.
//!
//! Note: This library does not yet provide access to the full
-//! expressiveness of the [DOT language](
-//! https://www.graphviz.org/doc/info/lang.html). For example, there are
-//! many [attributes](https://www.graphviz.org/content/attrs) related to
-//! providing layout hints (e.g., left-to-right versus top-down, which
+//! expressiveness of the [DOT language](https://www.graphviz.org/doc/info/lang.html).
+//! For example, there are many [attributes](https://www.graphviz.org/doc/info/attrs.html)
+//! related to providing layout hints (e.g., left-to-right versus top-down, which
//! algorithm to use, etc). The current intention of this library is to
//! emit a human-readable .dot file with very regular structure suitable
//! for easy post-processing.
LabelStr(Cow<'a, str>),
/// This kind of label uses the graphviz label escString type:
- /// <https://www.graphviz.org/content/attrs#kescString>
+ /// <https://www.graphviz.org/docs/attr-types/escString>
///
/// Occurrences of backslashes (`\`) are not escaped; instead they
/// are interpreted as initiating an escString escape sequence.
/// printed exactly as given, but between `<` and `>`. **No
/// escaping is performed.**
///
- /// [html]: https://www.graphviz.org/content/node-shapes#html
+ /// [html]: https://www.graphviz.org/doc/info/shapes.html#html
HtmlStr(Cow<'a, str>),
}
/// The style for a node or edge.
-/// See <https://www.graphviz.org/doc/info/attrs.html#k:style> for descriptions.
+/// See <https://www.graphviz.org/docs/attr-types/style/> for descriptions.
/// Note that some of these are not valid for edges.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Style {
/// Maps `n` to one of the [graphviz `shape` names][1]. If `None`
/// is returned, no `shape` attribute is specified.
///
- /// [1]: https://www.graphviz.org/content/node-shapes
+ /// [1]: https://www.graphviz.org/doc/info/shapes.html
fn node_shape(&'a self, _node: &Self::Node) -> Option<LabelText<'a>> {
None
}
// they are treated specially by the `def_path` function.
/// The crate root (marker).
CrateRoot,
- // Catch-all for random `DefId` things like `DUMMY_NODE_ID`.
- Misc,
// Different kinds of items and item-like things:
/// An impl.
Impl,
/// An `extern` block.
ForeignMod,
+ /// A `use` item.
+ Use,
+ /// A global asm item.
+ GlobalAsm,
/// Something in the type namespace.
TypeNs(Symbol),
/// Something in the value namespace.
match *self {
TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) => Some(name),
- Impl | ForeignMod | CrateRoot | Misc | ClosureExpr | Ctor | AnonConst | ImplTrait => {
- None
- }
+ Impl | ForeignMod | CrateRoot | Use | GlobalAsm | ClosureExpr | Ctor | AnonConst
+ | ImplTrait => None,
}
}
CrateRoot => DefPathDataName::Anon { namespace: kw::Crate },
Impl => DefPathDataName::Anon { namespace: kw::Impl },
ForeignMod => DefPathDataName::Anon { namespace: kw::Extern },
- Misc => DefPathDataName::Anon { namespace: sym::misc },
+ Use => DefPathDataName::Anon { namespace: kw::Use },
+ GlobalAsm => DefPathDataName::Anon { namespace: sym::global_asm },
ClosureExpr => DefPathDataName::Anon { namespace: sym::closure },
Ctor => DefPathDataName::Anon { namespace: sym::constructor },
AnonConst => DefPathDataName::Anon { namespace: sym::constant },
#[derive(Debug, HashStable_Generic)]
pub enum Guard<'hir> {
If(&'hir Expr<'hir>),
- // FIXME use hir::Let for this.
- IfLet(&'hir Pat<'hir>, &'hir Expr<'hir>),
+ IfLet(&'hir Let<'hir>),
}
#[derive(Debug, HashStable_Generic)]
//! HIR walker for walking the contents of nodes.
//!
-//! **For an overview of the visitor strategy, see the docs on the
-//! `super::itemlikevisit::ItemLikeVisitor` trait.**
+//! Here are the three available patterns for the visitor strategy,
+//! in roughly the order of desirability:
+//!
+//! 1. **Shallow visit**: Get a simple callback for every item (or item-like thing) in the HIR.
+//! - Example: find all items with a `#[foo]` attribute on them.
+//! - How: Use the `hir_crate_items` or `hir_module_items` query to traverse over item-like ids
+//! (ItemId, TraitItemId, etc.) and use tcx.def_kind and `tcx.hir().item*(id)` to filter and
+//! access actual item-like thing, respectively.
+//! - Pro: Efficient; just walks the lists of item ids and gives users control whether to access
+//! the hir_owners themselves or not.
+//! - Con: Don't get information about nesting
+//! - Con: Don't have methods for specific bits of HIR, like "on
+//! every expr, do this".
+//! 2. **Deep visit**: Want to scan for specific kinds of HIR nodes within
+//! an item, but don't care about how item-like things are nested
+//! within one another.
+//! - Example: Examine each expression to look for its type and do some check or other.
+//! - How: Implement `intravisit::Visitor` and override the `NestedFilter` type to
+//! `nested_filter::OnlyBodies` (and implement `nested_visit_map`), and use
+//! `tcx.hir().deep_visit_all_item_likes(&mut visitor)`. Within your
+//! `intravisit::Visitor` impl, implement methods like `visit_expr()` (don't forget to invoke
+//! `intravisit::walk_expr()` to keep walking the subparts).
+//! - Pro: Visitor methods for any kind of HIR node, not just item-like things.
+//! - Pro: Integrates well into dependency tracking.
+//! - Con: Don't get information about nesting between items
+//! 3. **Nested visit**: Want to visit the whole HIR and you care about the nesting between
+//! item-like things.
+//! - Example: Lifetime resolution, which wants to bring lifetimes declared on the
+//! impl into scope while visiting the impl-items, and then back out again.
+//! - How: Implement `intravisit::Visitor` and override the `NestedFilter` type to
+//! `nested_filter::All` (and implement `nested_visit_map`). Walk your crate with
+//! `tcx.hir().walk_toplevel_module(visitor)` invoked on `tcx.hir().krate()`.
+//! - Pro: Visitor methods for any kind of HIR node, not just item-like things.
+//! - Pro: Preserves nesting information
+//! - Con: Does not integrate well into dependency tracking.
//!
//! If you have decided to use this visitor, here are some general
//! notes on how to do so:
//! example generator inference, and possibly also HIR borrowck.
use crate::hir::*;
-use crate::itemlikevisit::{ItemLikeVisitor, ParItemLikeVisitor};
+use crate::itemlikevisit::ParItemLikeVisitor;
use rustc_ast::walk_list;
use rustc_ast::{Attribute, Label};
use rustc_span::symbol::{Ident, Symbol};
use rustc_span::Span;
-pub struct DeepVisitor<'v, V> {
- visitor: &'v mut V,
-}
-
-impl<'v, V> DeepVisitor<'v, V> {
- pub fn new(base: &'v mut V) -> Self {
- DeepVisitor { visitor: base }
- }
-}
-
-impl<'v, 'hir, V> ItemLikeVisitor<'hir> for DeepVisitor<'v, V>
-where
- V: Visitor<'hir>,
-{
- fn visit_item(&mut self, item: &'hir Item<'hir>) {
- self.visitor.visit_item(item);
- }
-
- fn visit_trait_item(&mut self, trait_item: &'hir TraitItem<'hir>) {
- self.visitor.visit_trait_item(trait_item);
- }
-
- fn visit_impl_item(&mut self, impl_item: &'hir ImplItem<'hir>) {
- self.visitor.visit_impl_item(impl_item);
- }
-
- fn visit_foreign_item(&mut self, foreign_item: &'hir ForeignItem<'hir>) {
- self.visitor.visit_foreign_item(foreign_item);
- }
-}
-
pub trait IntoVisitor<'hir> {
type Visitor: Visitor<'hir>;
fn into_visitor(&self) -> Self::Visitor;
walk_body(self, b);
}
- /// When invoking `visit_all_item_likes()`, you need to supply an
- /// item-like visitor. This method converts an "intra-visit"
- /// visitor into an item-like visitor that walks the entire tree.
- /// If you use this, you probably don't want to process the
- /// contents of nested item-like things, since the outer loop will
- /// visit them as well.
- fn as_deep_visitor(&mut self) -> DeepVisitor<'_, Self> {
- DeepVisitor::new(self)
- }
-
///////////////////////////////////////////////////////////////////////////
fn visit_id(&mut self, _hir_id: HirId) {
if let Some(ref g) = arm.guard {
match g {
Guard::If(ref e) => visitor.visit_expr(e),
- Guard::IfLet(ref pat, ref e) => {
- visitor.visit_pat(pat);
- visitor.visit_expr(e);
+ Guard::IfLet(ref l) => {
+ visitor.visit_let_expr(l);
}
}
}
use super::{ForeignItem, ImplItem, Item, TraitItem};
-/// The "item-like visitor" defines only the top-level methods
-/// that can be invoked by `Crate::visit_all_item_likes()`. Whether
-/// this trait is the right one to implement will depend on the
-/// overall pattern you need. Here are the three available patterns,
-/// in roughly the order of desirability:
-///
-/// 1. **Shallow visit**: Get a simple callback for every item (or item-like thing) in the HIR.
-/// - Example: find all items with a `#[foo]` attribute on them.
-/// - How: Implement `ItemLikeVisitor` and call `tcx.hir().visit_all_item_likes()`.
-/// - Pro: Efficient; just walks the lists of item-like things, not the nodes themselves.
-/// - Con: Don't get information about nesting
-/// - Con: Don't have methods for specific bits of HIR, like "on
-/// every expr, do this".
-/// 2. **Deep visit**: Want to scan for specific kinds of HIR nodes within
-/// an item, but don't care about how item-like things are nested
-/// within one another.
-/// - Example: Examine each expression to look for its type and do some check or other.
-/// - How: Implement `intravisit::Visitor` and override the `NestedFilter` type to
-/// `nested_filter::OnlyBodies` (and implement `nested_visit_map`), and use
-/// `tcx.hir().visit_all_item_likes(&mut visitor.as_deep_visitor())`. Within your
-/// `intravisit::Visitor` impl, implement methods like `visit_expr()` (don't forget to invoke
-/// `intravisit::walk_expr()` to keep walking the subparts).
-/// - Pro: Visitor methods for any kind of HIR node, not just item-like things.
-/// - Pro: Integrates well into dependency tracking.
-/// - Con: Don't get information about nesting between items
-/// 3. **Nested visit**: Want to visit the whole HIR and you care about the nesting between
-/// item-like things.
-/// - Example: Lifetime resolution, which wants to bring lifetimes declared on the
-/// impl into scope while visiting the impl-items, and then back out again.
-/// - How: Implement `intravisit::Visitor` and override the `NestedFilter` type to
-/// `nested_filter::All` (and implement `nested_visit_map`). Walk your crate with
-/// `tcx.hir().walk_toplevel_module(visitor)` invoked on `tcx.hir().krate()`.
-/// - Pro: Visitor methods for any kind of HIR node, not just item-like things.
-/// - Pro: Preserves nesting information
-/// - Con: Does not integrate well into dependency tracking.
-///
-/// Note: the methods of `ItemLikeVisitor` intentionally have no
-/// defaults, so that as we expand the list of item-like things, we
-/// revisit the various visitors to see if they need to change. This
-/// is harder to do with `intravisit::Visitor`, so when you add a new
-/// `visit_nested_foo()` method, it is recommended that you search for
-/// existing `fn visit_nested` methods to see where changes are
-/// needed.
-pub trait ItemLikeVisitor<'hir> {
- fn visit_item(&mut self, item: &'hir Item<'hir>);
- fn visit_trait_item(&mut self, trait_item: &'hir TraitItem<'hir>);
- fn visit_impl_item(&mut self, impl_item: &'hir ImplItem<'hir>);
- fn visit_foreign_item(&mut self, foreign_item: &'hir ForeignItem<'hir>);
-}
-
/// A parallel variant of `ItemLikeVisitor`.
pub trait ParItemLikeVisitor<'hir> {
fn visit_item(&self, item: &'hir Item<'hir>);
self.print_expr(&e);
self.space();
}
- hir::Guard::IfLet(pat, e) => {
+ hir::Guard::IfLet(hir::Let { pat, ty, init, .. }) => {
self.word_nbsp("if");
- self.word_nbsp("let");
- self.print_pat(&pat);
- self.space();
- self.word_space("=");
- self.print_expr(&e);
- self.space();
+ self.print_let(pat, *ty, init);
}
}
}
let mut visitor =
IfThisChanged { tcx, if_this_changed: vec![], then_this_would_need: vec![] };
visitor.process_attrs(hir::CRATE_HIR_ID);
- tcx.hir().visit_all_item_likes(&mut visitor.as_deep_visitor());
+ tcx.hir().deep_visit_all_item_likes(&mut visitor);
(visitor.if_this_changed, visitor.then_this_would_need)
};
impl<'tcx> DirtyCleanVisitor<'tcx> {
/// Possibly "deserialize" the attribute into a clean/dirty assertion
fn assertion_maybe(&mut self, item_id: LocalDefId, attr: &Attribute) -> Option<Assertion> {
- if !attr.has_name(sym::rustc_clean) {
- // skip: not rustc_clean/dirty
- return None;
- }
+ assert!(attr.has_name(sym::rustc_clean));
if !check_config(self.tcx, attr) {
// skip: not the correct `cfg=`
return None;
fn check_item(&mut self, item_id: LocalDefId) {
let item_span = self.tcx.def_span(item_id.to_def_id());
let def_path_hash = self.tcx.def_path_hash(item_id.to_def_id());
- for attr in self.tcx.get_attrs(item_id.to_def_id()).iter() {
+ for attr in self.tcx.get_attrs(item_id.to_def_id(), sym::rustc_clean) {
let Some(assertion) = self.assertion_maybe(item_id, attr) else {
continue;
};
/// Returns true if we increased the number of elements present.
pub fn insert_range(&mut self, range: impl RangeBounds<I> + Clone) -> bool {
let start = inclusive_start(range.clone());
- let Some(mut end) = inclusive_end(self.domain, range) else {
+ let Some(end) = inclusive_end(self.domain, range) else {
// empty range
return false;
};
return false;
}
- loop {
- // This condition looks a bit weird, but actually makes sense.
- //
- // if r.0 == end + 1, then we're actually adjacent, so we want to
- // continue to the next range. We're looking here for the first
- // range which starts *non-adjacently* to our end.
- let next = self.map.partition_point(|r| r.0 <= end + 1);
- if let Some(last) = next.checked_sub(1) {
- let (prev_start, prev_end) = &mut self.map[last];
- if *prev_end + 1 >= start {
- // If the start for the inserted range is adjacent to the
- // end of the previous, we can extend the previous range.
- if start < *prev_start {
- // Our range starts before the one we found. We'll need
- // to *remove* it, and then try again.
- //
- // FIXME: This is not so efficient; we may need to
- // recurse a bunch of times here. Instead, it's probably
- // better to do something like drain_filter(...) on the
- // map to be able to delete or modify all the ranges in
- // start..=end and then potentially re-insert a new
- // range.
- end = std::cmp::max(end, *prev_end);
- self.map.remove(last);
- } else {
- // We overlap with the previous range, increase it to
- // include us.
- //
- // Make sure we're actually going to *increase* it though --
- // it may be that end is just inside the previously existing
- // set.
- return if end > *prev_end {
- *prev_end = end;
- true
- } else {
- false
- };
+ // This condition looks a bit weird, but actually makes sense.
+ //
+ // if r.0 == end + 1, then we're actually adjacent, so we want to
+ // continue to the next range. We're looking here for the first
+ // range which starts *non-adjacently* to our end.
+ let next = self.map.partition_point(|r| r.0 <= end + 1);
+ if let Some(right) = next.checked_sub(1) {
+ let (prev_start, prev_end) = self.map[right];
+ if prev_end + 1 >= start {
+ // If the start for the inserted range is adjacent to the
+ // end of the previous, we can extend the previous range.
+ if start < prev_start {
+ // The first range which ends *non-adjacently* to our start.
+ // And we can ensure that left <= right.
+ let left = self.map.partition_point(|l| l.1 + 1 < start);
+ let min = std::cmp::min(self.map[left].0, start);
+ let max = std::cmp::max(prev_end, end);
+ self.map[right] = (min, max);
+ if left != right {
+ self.map.drain(left..right);
}
- } else {
- // Otherwise, we don't overlap, so just insert
- self.map.insert(last + 1, (start, end));
return true;
- }
- } else {
- if self.map.is_empty() {
- // Quite common in practice, and expensive to call memcpy
- // with length zero.
- self.map.push((start, end));
} else {
- self.map.insert(next, (start, end));
+ // We overlap with the previous range, increase it to
+ // include us.
+ //
+ // Make sure we're actually going to *increase* it though --
+ // it may be that end is just inside the previously existing
+ // set.
+ return if end > prev_end {
+ self.map[right].1 = end;
+ true
+ } else {
+ false
+ };
}
+ } else {
+ // Otherwise, we don't overlap, so just insert
+ self.map.insert(right + 1, (start, end));
return true;
}
+ } else {
+ if self.map.is_empty() {
+ // Quite common in practice, and expensive to call memcpy
+ // with length zero.
+ self.map.push((start, end));
+ } else {
+ self.map.insert(next, (start, end));
+ }
+ return true;
}
}
fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>);
}
-pub trait RelateResultCompare<'tcx, T> {
- fn compare<F>(&self, t: T, f: F) -> RelateResult<'tcx, T>
- where
- F: FnOnce() -> TypeError<'tcx>;
-}
-
-impl<'tcx, T: Clone + PartialEq> RelateResultCompare<'tcx, T> for RelateResult<'tcx, T> {
- fn compare<F>(&self, t: T, f: F) -> RelateResult<'tcx, T>
- where
- F: FnOnce() -> TypeError<'tcx>,
- {
- self.clone().and_then(|s| if s == t { self.clone() } else { Err(f()) })
- }
-}
-
pub fn const_unification_error<'tcx>(
a_is_expected: bool,
(a, b): (ty::Const<'tcx>, ty::Const<'tcx>),
self,
error::TypeError,
subst::{GenericArgKind, Subst, SubstsRef},
- Binder, List, Region, Ty, TyCtxt, TypeFoldable,
+ Binder, EarlyBinder, List, Region, Ty, TyCtxt, TypeFoldable,
};
use rustc_span::{sym, BytePos, DesugaringKind, Pos, Span};
use rustc_target::spec::abi;
for (def_id, actual) in iter::zip(default_params, substs.iter().rev()) {
match actual.unpack() {
GenericArgKind::Const(c) => {
- if self.tcx.const_param_default(def_id).subst(self.tcx, substs) != c {
+ if EarlyBinder(self.tcx.const_param_default(def_id)).subst(self.tcx, substs)
+ != c
+ {
break;
}
}
GenericArgKind::Type(ty) => {
- if self.tcx.type_of(def_id).subst(self.tcx, substs) != ty {
+ if self.tcx.bound_type_of(def_id).subst(self.tcx, substs) != ty {
break;
}
}
}
(ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
- let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1);
- let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2);
+ let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1);
+ let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2);
let mut values = self.cmp_fn_sig(&sig1, &sig2);
let path1 = format!(" {{{}}}", self.tcx.def_path_str_with_substs(*did1, substs1));
let path2 = format!(" {{{}}}", self.tcx.def_path_str_with_substs(*did2, substs2));
}
(ty::FnDef(did1, substs1), ty::FnPtr(sig2)) => {
- let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1);
+ let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1);
let mut values = self.cmp_fn_sig(&sig1, sig2);
values.0.push_highlighted(format!(
" {{{}}}",
}
(ty::FnPtr(sig1), ty::FnDef(did2, substs2)) => {
- let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2);
+ let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2);
let mut values = self.cmp_fn_sig(sig1, &sig2);
values.1.push_normal(format!(
" {{{}}}",
// Future::Output
let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
- let bounds = self.tcx.explicit_item_bounds(*def_id);
+ let bounds = self.tcx.bound_explicit_item_bounds(*def_id);
- for (predicate, _) in bounds {
+ for predicate in bounds.transpose_iter().map(|e| e.map_bound(|(p, _)| *p)) {
let predicate = predicate.subst(self.tcx, substs);
let output = predicate
.kind()
{
let mut eraser = TypeParamEraser(self.tcx);
let candidate_len = impl_candidates.len();
- let mut suggestions: Vec<_> = impl_candidates.iter().map(|candidate| {
+ let mut suggestions: Vec<_> = impl_candidates.iter().filter_map(|candidate| {
let trait_item = self.tcx
.associated_items(candidate.def_id)
.find_by_name_and_kind(
ty::AssocKind::Fn,
candidate.def_id
);
+ if trait_item.is_none() {
+ return None;
+ }
let prefix = if let Some(trait_item) = trait_item
&& let Some(trait_m) = trait_item.def_id.as_local()
&& let hir::TraitItemKind::Fn(fn_, _) = &self.tcx.hir().trait_item(hir::TraitItemId { def_id: trait_m }).kind
""
};
let candidate = candidate.super_fold_with(&mut eraser);
- vec![
+ Some(vec![
(expr.span.shrink_to_lo(), format!("{}::{}({}", candidate, segment.ident, prefix)),
if exprs.len() == 1 {
(expr.span.shrink_to_hi().with_hi(e.span.hi()), ")".to_string())
} else {
(expr.span.shrink_to_hi().with_hi(exprs[1].span.lo()), ", ".to_string())
},
- ]
+ ])
}).collect();
suggestions.sort_by(|a, b| a[0].1.cmp(&b[0].1));
- err.multipart_suggestions(
- &format!(
- "use the fully qualified path for the potential candidate{}",
- pluralize!(candidate_len),
- ),
- suggestions.into_iter(),
- Applicability::MaybeIncorrect,
- );
+ if !suggestions.is_empty() {
+ err.multipart_suggestions(
+ &format!(
+ "use the fully qualified path for the potential candidate{}",
+ pluralize!(candidate_len),
+ ),
+ suggestions.into_iter(),
+ Applicability::MaybeIncorrect,
+ );
+ }
}
// Suggest specifying type params or point out the return type of the call:
//
delegate: &'me mut D,
- /// After we generalize this type, we are going to relative it to
+ /// After we generalize this type, we are going to relate it to
/// some other type. What will be the variance at this point?
ambient_variance: ty::Variance,
obligations = self.at(&cause, param_env).eq(prev, hidden_ty)?.obligations;
}
- let item_bounds = tcx.explicit_item_bounds(def_id);
+ let item_bounds = tcx.bound_explicit_item_bounds(def_id);
- for (predicate, _) in item_bounds {
+ for predicate in item_bounds.transpose_iter().map(|e| e.map_bound(|(p, _)| *p)) {
debug!(?predicate);
let predicate = predicate.subst(tcx, substs);
use rustc_data_structures::sso::SsoHashSet;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt};
/// The `TypeOutlives` struct has the job of "lowering" a `T: 'a`
/// obligation into a series of `'a: 'b` constraints and "verifys", as
debug!("projection_bounds(projection_ty={:?})", projection_ty);
let tcx = self.tcx;
self.region_bounds_declared_on_associated_item(projection_ty.item_def_id)
- .map(move |r| r.subst(tcx, projection_ty.substs))
+ .map(move |r| EarlyBinder(r).subst(tcx, projection_ty.substs))
}
/// Given the `DefId` of an associated item, returns any region
}
}
-impl TraitObligation<'_> {
+impl<'tcx> TraitObligation<'tcx> {
/// Returns `true` if the trait predicate is considered `const` in its ParamEnv.
pub fn is_const(&self) -> bool {
match (self.predicate.skip_binder().constness, self.param_env.constness()) {
_ => false,
}
}
+
+ pub fn derived_cause(
+ &self,
+ variant: impl FnOnce(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>,
+ ) -> ObligationCause<'tcx> {
+ self.cause.clone().derived_cause(self.predicate, variant)
+ }
}
// `PredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger.
/// an error.
fn check_for_rustc_errors_attr(tcx: TyCtxt<'_>) {
let Some((def_id, _)) = tcx.entry_fn(()) else { return };
-
- let attrs = &*tcx.get_attrs(def_id);
- let attrs = attrs.iter().filter(|attr| attr.has_name(sym::rustc_error));
- for attr in attrs {
+ for attr in tcx.get_attrs(def_id, sym::rustc_error) {
match attr.meta_item_list() {
// Check if there is a `#[rustc_error(delay_span_bug_from_inside_query)]`.
Some(list)
}
}
- let attrs = cx.tcx.get_attrs(def_id.to_def_id());
+ let attrs = cx.tcx.hir().attrs(cx.tcx.hir().local_def_id_to_hir_id(def_id));
let has_doc = attrs.iter().any(has_doc);
if !has_doc {
cx.struct_span_lint(
impl<'tcx> LateLintPass<'tcx> for MutableTransmutes {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
- use rustc_target::spec::abi::Abi::RustIntrinsic;
if let Some((&ty::Ref(_, _, from_mt), &ty::Ref(_, _, to_mt))) =
get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (ty1.kind(), ty2.kind()))
{
}
fn def_id_is_transmute(cx: &LateContext<'_>, def_id: DefId) -> bool {
- cx.tcx.fn_sig(def_id).abi() == RustIntrinsic
- && cx.tcx.item_name(def_id) == sym::transmute
+ cx.tcx.is_intrinsic(def_id) && cx.tcx.item_name(def_id) == sym::transmute
}
}
}
// bottleneck, this does just fine.
(
overridden_link_name,
- tcx.get_attrs(fi.def_id.to_def_id())
- .iter()
- .find(|at| at.has_name(sym::link_name))
- .unwrap()
- .span,
+ tcx.get_attr(fi.def_id.to_def_id(), sym::link_name).unwrap().span,
)
})
{
PATH_STATEMENTS,
UNUSED_ATTRIBUTES,
UNUSED_MACROS,
+ UNUSED_MACRO_RULES,
UNUSED_ALLOCATION,
UNUSED_DOC_COMMENTS,
UNUSED_EXTERN_CRATES,
}
crate fn nonnull_optimization_guaranteed<'tcx>(tcx: TyCtxt<'tcx>, def: ty::AdtDef<'tcx>) -> bool {
- tcx.get_attrs(def.did()).iter().any(|a| a.has_name(sym::rustc_nonnull_optimization_guaranteed))
+ tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed)
}
/// `repr(transparent)` structs can have a single non-ZST field, this function returns that
descr_pre_path: &str,
descr_post_path: &str,
) -> bool {
- for attr in cx.tcx.get_attrs(def_id).iter() {
- if attr.has_name(sym::must_use) {
- cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
- let msg = format!(
- "unused {}`{}`{} that must be used",
- descr_pre_path,
- cx.tcx.def_path_str(def_id),
- descr_post_path
- );
- let mut err = lint.build(&msg);
- // check for #[must_use = "..."]
- if let Some(note) = attr.value_str() {
- err.note(note.as_str());
- }
- err.emit();
- });
- return true;
- }
+ if let Some(attr) = cx.tcx.get_attr(def_id, sym::must_use) {
+ cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
+ let msg = format!(
+ "unused {}`{}`{} that must be used",
+ descr_pre_path,
+ cx.tcx.def_path_str(def_id),
+ descr_post_path
+ );
+ let mut err = lint.build(&msg);
+ // check for #[must_use = "..."]
+ if let Some(note) = attr.value_str() {
+ err.note(note.as_str());
+ }
+ err.emit();
+ });
+ true
+ } else {
+ false
}
- false
}
}
}
declare_lint! {
/// The `unused_macros` lint detects macros that were not used.
///
+ /// Note that this lint is distinct from the `unused_macro_rules` lint,
+ /// which checks for single rules that never match of an otherwise used
+ /// macro, and thus never expand.
+ ///
/// ### Example
///
/// ```rust
"detects macros that were not used"
}
+declare_lint! {
+ /// The `unused_macro_rules` lint detects macro rules that were not used.
+ ///
+ /// Note that the lint is distinct from the `unused_macros` lint, which
+ /// fires if the entire macro is never called, while this lint fires for
+ /// single unused rules of the macro that is otherwise used.
+ /// `unused_macro_rules` fires only if `unused_macros` wouldn't fire.
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// #[warn(unused_macro_rules)]
+ /// macro_rules! unused_empty {
+ /// (hello) => { println!("Hello, world!") }; // This rule is unused
+ /// () => { println!("empty") }; // This rule is used
+ /// }
+ ///
+ /// fn main() {
+ /// unused_empty!(hello);
+ /// }
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Unused macro rules may signal a mistake or unfinished code. Furthermore,
+ /// they slow down compilation. Right now, silencing the warning is not
+ /// supported on a single rule level, so you have to add an allow to the
+ /// entire macro definition.
+ ///
+ /// If you intended to export the macro to make it
+ /// available outside of the crate, use the [`macro_export` attribute].
+ ///
+ /// [`macro_export` attribute]: https://doc.rust-lang.org/reference/macros-by-example.html#path-based-scope
+ pub UNUSED_MACRO_RULES,
+ Allow,
+ "detects macro rules that were not used"
+}
+
declare_lint! {
/// The `warnings` lint allows you to change the level of other
/// lints which produce warnings.
OVERLAPPING_RANGE_ENDPOINTS,
BINDINGS_WITH_VARIANT_NAME,
UNUSED_MACROS,
+ UNUSED_MACRO_RULES,
WARNINGS,
UNUSED_FEATURES,
STABLE_FEATURES,
let stdcppname = if target.contains("openbsd") {
if target.contains("sparc64") { "estdc++" } else { "c++" }
- } else if target.contains("freebsd") {
- "c++"
- } else if target.contains("darwin") {
+ } else if target.contains("darwin")
+ || target.contains("freebsd")
+ || target.contains("windows-gnullvm")
+ {
"c++"
} else if target.contains("netbsd") && llvm_static_stdcpp.is_some() {
// NetBSD uses a separate library when relocation is required
// Libstdc++ depends on pthread which Rust doesn't link on MinGW
// since nothing else requires it.
- if target.contains("windows-gnu") {
+ if target.ends_with("windows-gnu") {
println!("cargo:rustc-link-lib=static:-bundle=pthread");
}
}
SessionDiagnosticDeriveError,
};
use crate::diagnostics::utils::{
- report_error_if_not_applied_to_span, type_matches_path, Applicability, FieldInfo, FieldInnerTy,
- HasFieldMap, SetOnce,
+ report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path,
+ Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce,
};
-use proc_macro2::TokenStream;
+use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use std::collections::HashMap;
use std::str::FromStr;
quote! {
#diag.set_arg(
stringify!(#ident),
- #field_binding.into_diagnostic_arg()
+ #field_binding
);
}
} else {
) -> Result<TokenStream, SessionDiagnosticDeriveError> {
let diag = &self.diag;
- let name = attr.path.segments.last().unwrap().ident.to_string();
+ let ident = &attr.path.segments.last().unwrap().ident;
+ let name = ident.to_string();
let name = name.as_str();
let meta = attr.parse_meta()?;
#diag.set_span(#binding);
})
}
- "label" | "note" | "help" => {
+ "label" => {
report_error_if_not_applied_to_span(attr, &info)?;
- Ok(self.add_subdiagnostic(binding, name, name))
+ Ok(self.add_spanned_subdiagnostic(binding, ident, name))
+ }
+ "note" | "help" => {
+ if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
+ Ok(self.add_spanned_subdiagnostic(binding, ident, name))
+ } else if type_is_unit(&info.ty) {
+ Ok(self.add_subdiagnostic(ident, name))
+ } else {
+ report_type_error(attr, "`Span` or `()`")?;
+ }
}
"subdiagnostic" => Ok(quote! { #diag.subdiagnostic(#binding); }),
_ => throw_invalid_attr!(attr, &meta, |diag| {
}),
},
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(ref s), .. }) => match name {
- "label" | "note" | "help" => {
+ "label" => {
report_error_if_not_applied_to_span(attr, &info)?;
- Ok(self.add_subdiagnostic(binding, name, &s.value()))
+ Ok(self.add_spanned_subdiagnostic(binding, ident, &s.value()))
+ }
+ "note" | "help" => {
+ if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
+ Ok(self.add_spanned_subdiagnostic(binding, ident, &s.value()))
+ } else if type_is_unit(&info.ty) {
+ Ok(self.add_subdiagnostic(ident, &s.value()))
+ } else {
+ report_type_error(attr, "`Span` or `()`")?;
+ }
}
_ => throw_invalid_attr!(attr, &meta, |diag| {
diag.help("only `label`, `note` and `help` are valid field attributes")
}
}
- /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug and
- /// `fluent_attr_identifier`.
- fn add_subdiagnostic(
+ /// Adds a spanned subdiagnostic by generating a `diag.span_$kind` call with the current slug
+ /// and `fluent_attr_identifier`.
+ fn add_spanned_subdiagnostic(
&self,
field_binding: TokenStream,
- kind: &str,
+ kind: &Ident,
fluent_attr_identifier: &str,
) -> TokenStream {
let diag = &self.diag;
}
}
+ /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug
+ /// and `fluent_attr_identifier`.
+ fn add_subdiagnostic(&self, kind: &Ident, fluent_attr_identifier: &str) -> TokenStream {
+ let diag = &self.diag;
+ let slug = self.slug.as_ref().map(|(slug, _)| slug.as_str()).unwrap_or("missing-slug");
+ quote! {
+ #diag.#kind(rustc_errors::DiagnosticMessage::fluent_attr(#slug, #fluent_attr_identifier));
+ }
+ }
+
fn span_and_applicability_of_ty(
&self,
info: FieldInfo<'_>,
let generated = quote! {
#diag.set_arg(
stringify!(#ident),
- #binding.into_diagnostic_arg()
+ #binding
);
};
use quote::{format_ident, quote, ToTokens};
use std::collections::BTreeSet;
use std::str::FromStr;
-use syn::{spanned::Spanned, Attribute, Meta, Type, Visibility};
+use syn::{spanned::Spanned, Attribute, Meta, Type, TypeTuple, Visibility};
use synstructure::BindingInfo;
/// Checks whether the type name of `ty` matches `name`.
}
}
-/// Reports an error if the field's type is not `Applicability`.
+/// Checks whether the type `ty` is `()`.
+pub(crate) fn type_is_unit(ty: &Type) -> bool {
+ if let Type::Tuple(TypeTuple { elems, .. }) = ty { elems.is_empty() } else { false }
+}
+
+/// Reports a type error for field with `attr`.
+pub(crate) fn report_type_error(
+ attr: &Attribute,
+ ty_name: &str,
+) -> Result<!, SessionDiagnosticDeriveError> {
+ let name = attr.path.segments.last().unwrap().ident.to_string();
+ let meta = attr.parse_meta()?;
+
+ throw_span_err!(
+ attr.span().unwrap(),
+ &format!(
+ "the `#[{}{}]` attribute can only be applied to fields of type {}",
+ name,
+ match meta {
+ Meta::Path(_) => "",
+ Meta::NameValue(_) => " = ...",
+ Meta::List(_) => "(...)",
+ },
+ ty_name
+ )
+ );
+}
+
+/// Reports an error if the field's type does not match `path`.
fn report_error_if_not_applied_to_ty(
attr: &Attribute,
info: &FieldInfo<'_>,
ty_name: &str,
) -> Result<(), SessionDiagnosticDeriveError> {
if !type_matches_path(&info.ty, path) {
- let name = attr.path.segments.last().unwrap().ident.to_string();
- let name = name.as_str();
- let meta = attr.parse_meta()?;
-
- throw_span_err!(
- attr.span().unwrap(),
- &format!(
- "the `#[{}{}]` attribute can only be applied to fields of type `{}`",
- name,
- match meta {
- Meta::Path(_) => "",
- Meta::NameValue(_) => " = ...",
- Meta::List(_) => "(...)",
- },
- ty_name
- )
- );
+ report_type_error(attr, ty_name)?;
}
Ok(())
attr,
info,
&["rustc_errors", "Applicability"],
- "Applicability",
+ "`Applicability`",
)
}
attr: &Attribute,
info: &FieldInfo<'_>,
) -> Result<(), SessionDiagnosticDeriveError> {
- report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "Span")
+ report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "`Span`")
}
/// Inner type of a field and type of wrapper.
#![feature(allow_internal_unstable)]
#![feature(let_else)]
+#![feature(never_type)]
#![feature(proc_macro_diagnostic)]
#![allow(rustc::default_hash_types)]
#![recursion_limit = "128"]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(crate_visibility_modifier)]
+#![feature(decl_macro)]
#![feature(drain_filter)]
#![feature(generators)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(nll)]
#![feature(once_cell)]
-use rustc_ast::CRATE_NODE_ID;
+use rustc_ast::{NestedMetaItem, CRATE_NODE_ID};
use rustc_attr as attr;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::struct_span_err;
use rustc_session::parse::feature_err;
use rustc_session::utils::NativeLibKind;
use rustc_session::Session;
-use rustc_span::symbol::{kw, sym, Symbol};
-use rustc_span::Span;
+use rustc_span::symbol::{sym, Symbol};
use rustc_target::spec::abi::Abi;
crate fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> {
// Process all of the #[link(..)]-style arguments
let sess = &self.tcx.sess;
+ let features = self.tcx.features();
for m in self.tcx.hir().attrs(it.hir_id()).iter().filter(|a| a.has_name(sym::link)) {
let Some(items) = m.meta_item_list() else {
continue;
};
- let mut lib = NativeLib {
- name: None,
- kind: NativeLibKind::Unspecified,
- cfg: None,
- foreign_module: Some(it.def_id.to_def_id()),
- wasm_import_module: None,
- verbatim: None,
- dll_imports: Vec::new(),
- };
- let mut kind_specified = false;
+ let mut name = None;
+ let mut kind = None;
+ let mut modifiers = None;
+ let mut cfg = None;
+ let mut wasm_import_module = None;
for item in items.iter() {
- if item.has_name(sym::kind) {
- kind_specified = true;
- let Some(kind) = item.value_str() else {
- continue; // skip like historical compilers
- };
- lib.kind = match kind.as_str() {
- "static" => NativeLibKind::Static { bundle: None, whole_archive: None },
- "static-nobundle" => {
- sess.struct_span_warn(
- item.span(),
- "library kind `static-nobundle` has been superseded by specifying \
- modifier `-bundle` with library kind `static`",
- )
- .emit();
- if !self.tcx.features().static_nobundle {
- feature_err(
- &self.tcx.sess.parse_sess,
- sym::static_nobundle,
- item.span(),
- "kind=\"static-nobundle\" is unstable",
- )
- .emit();
- }
- NativeLibKind::Static { bundle: Some(false), whole_archive: None }
+ match item.name_or_empty() {
+ sym::name => {
+ if name.is_some() {
+ let msg = "multiple `name` arguments in a single `#[link]` attribute";
+ sess.span_err(item.span(), msg);
+ continue;
}
- "dylib" => NativeLibKind::Dylib { as_needed: None },
- "framework" => NativeLibKind::Framework { as_needed: None },
- "raw-dylib" => NativeLibKind::RawDylib,
- k => {
- struct_span_err!(sess, item.span(), E0458, "unknown kind: `{}`", k)
- .span_label(item.span(), "unknown kind")
- .span_label(m.span, "")
+ let Some(link_name) = item.value_str() else {
+ let msg = "link name must be of the form `name = \"string\"`";
+ sess.span_err(item.span(), msg);
+ continue;
+ };
+ let span = item.name_value_literal_span().unwrap();
+ if link_name.is_empty() {
+ struct_span_err!(sess, span, E0454, "link name must not be empty")
+ .span_label(span, "empty link name")
.emit();
- NativeLibKind::Unspecified
}
- };
- } else if item.has_name(sym::name) {
- lib.name = item.value_str();
- } else if item.has_name(sym::cfg) {
- let Some(cfg) = item.meta_item_list() else {
- continue; // skip like historical compilers
- };
- if cfg.is_empty() {
- sess.span_err(item.span(), "`cfg()` must have an argument");
- } else if let cfg @ Some(..) = cfg[0].meta_item() {
- lib.cfg = cfg.cloned();
- } else {
- sess.span_err(cfg[0].span(), "invalid argument for `cfg(..)`");
+ name = Some((link_name, span));
}
- } else if item.has_name(sym::wasm_import_module) {
- match item.value_str() {
- Some(s) => lib.wasm_import_module = Some(s),
- None => {
- let msg = "must be of the form `#[link(wasm_import_module = \"...\")]`";
+ sym::kind => {
+ if kind.is_some() {
+ let msg = "multiple `kind` arguments in a single `#[link]` attribute";
sess.span_err(item.span(), msg);
+ continue;
}
- }
- } else {
- // currently, like past compilers, ignore unknown
- // directives here.
- }
- }
-
- // Do this outside the above loop so we don't depend on modifiers coming
- // after kinds
- let mut modifiers_count = 0;
- for item in items.iter().filter(|item| item.has_name(sym::modifiers)) {
- if let Some(modifiers) = item.value_str() {
- modifiers_count += 1;
- let span = item.name_value_literal_span().unwrap();
- let mut has_duplicate_modifiers = false;
- for modifier in modifiers.as_str().split(',') {
- let (modifier, value) = match modifier.strip_prefix(&['+', '-']) {
- Some(m) => (m, modifier.starts_with('+')),
- None => {
- // Note: this error also excludes the case with empty modifier
- // string, like `modifiers = ""`.
- sess.span_err(
- span,
- "invalid linking modifier syntax, expected '+' or '-' prefix \
- before one of: bundle, verbatim, whole-archive, as-needed",
- );
- continue;
- }
+ let Some(link_kind) = item.value_str() else {
+ let msg = "link kind must be of the form `kind = \"string\"`";
+ sess.span_err(item.span(), msg);
+ continue;
};
- match (modifier, &mut lib.kind) {
- ("bundle", NativeLibKind::Static { bundle, .. }) => {
- if bundle.is_some() {
- has_duplicate_modifiers = true;
- }
- *bundle = Some(value);
- }
- ("bundle", _) => {
- sess.span_err(
+ let span = item.name_value_literal_span().unwrap();
+ let link_kind = match link_kind.as_str() {
+ "static" => NativeLibKind::Static { bundle: None, whole_archive: None },
+ "static-nobundle" => {
+ sess.struct_span_warn(
span,
- "bundle linking modifier is only compatible with \
- `static` linking kind",
- );
- }
-
- ("verbatim", _) => {
- if lib.verbatim.is_some() {
- has_duplicate_modifiers = true;
+ "link kind `static-nobundle` has been superseded by specifying \
+ modifier `-bundle` with link kind `static`",
+ )
+ .emit();
+ if !features.static_nobundle {
+ feature_err(
+ &sess.parse_sess,
+ sym::static_nobundle,
+ span,
+ "link kind `static-nobundle` is unstable",
+ )
+ .emit();
}
- lib.verbatim = Some(value);
+ NativeLibKind::Static { bundle: Some(false), whole_archive: None }
}
-
- ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
- if whole_archive.is_some() {
- has_duplicate_modifiers = true;
+ "dylib" => NativeLibKind::Dylib { as_needed: None },
+ "framework" => {
+ if !sess.target.is_like_osx {
+ struct_span_err!(
+ sess,
+ span,
+ E0455,
+ "link kind `framework` is only supported on Apple targets"
+ )
+ .emit();
}
- *whole_archive = Some(value);
+ NativeLibKind::Framework { as_needed: None }
}
- ("whole-archive", _) => {
- sess.span_err(
- span,
- "whole-archive linking modifier is only compatible with \
- `static` linking kind",
- );
- }
-
- ("as-needed", NativeLibKind::Dylib { as_needed })
- | ("as-needed", NativeLibKind::Framework { as_needed }) => {
- if as_needed.is_some() {
- has_duplicate_modifiers = true;
+ "raw-dylib" => {
+ if !sess.target.is_like_windows {
+ struct_span_err!(
+ sess,
+ span,
+ E0455,
+ "link kind `raw-dylib` is only supported on Windows targets"
+ )
+ .emit();
+ } else if !features.raw_dylib {
+ feature_err(
+ &sess.parse_sess,
+ sym::raw_dylib,
+ span,
+ "link kind `raw-dylib` is unstable",
+ )
+ .emit();
}
- *as_needed = Some(value);
+ NativeLibKind::RawDylib
}
- ("as-needed", _) => {
- sess.span_err(
- span,
- "as-needed linking modifier is only compatible with \
- `dylib` and `framework` linking kinds",
- );
- }
-
- _ => {
- sess.span_err(
- span,
- &format!(
- "unrecognized linking modifier `{}`, expected one \
- of: bundle, verbatim, whole-archive, as-needed",
- modifier
- ),
+ kind => {
+ let msg = format!(
+ "unknown link kind `{kind}`, expected one of: \
+ static, dylib, framework, raw-dylib"
);
+ struct_span_err!(sess, span, E0458, "{}", msg)
+ .span_label(span, "unknown link kind")
+ .emit();
+ continue;
}
+ };
+ kind = Some(link_kind);
+ }
+ sym::modifiers => {
+ if modifiers.is_some() {
+ let msg =
+ "multiple `modifiers` arguments in a single `#[link]` attribute";
+ sess.span_err(item.span(), msg);
+ continue;
+ }
+ let Some(link_modifiers) = item.value_str() else {
+ let msg = "link modifiers must be of the form `modifiers = \"string\"`";
+ sess.span_err(item.span(), msg);
+ continue;
+ };
+ modifiers = Some((link_modifiers, item.name_value_literal_span().unwrap()));
+ }
+ sym::cfg => {
+ if cfg.is_some() {
+ let msg = "multiple `cfg` arguments in a single `#[link]` attribute";
+ sess.span_err(item.span(), msg);
+ continue;
+ }
+ let Some(link_cfg) = item.meta_item_list() else {
+ let msg = "link cfg must be of the form `cfg(/* predicate */)`";
+ sess.span_err(item.span(), msg);
+ continue;
+ };
+ let [NestedMetaItem::MetaItem(link_cfg)] = link_cfg else {
+ let msg = "link cfg must have a single predicate argument";
+ sess.span_err(item.span(), msg);
+ continue;
+ };
+ if !features.link_cfg {
+ feature_err(
+ &sess.parse_sess,
+ sym::link_cfg,
+ item.span(),
+ "link cfg is unstable",
+ )
+ .emit();
+ }
+ cfg = Some(link_cfg.clone());
+ }
+ sym::wasm_import_module => {
+ if wasm_import_module.is_some() {
+ let msg = "multiple `wasm_import_module` arguments \
+ in a single `#[link]` attribute";
+ sess.span_err(item.span(), msg);
+ continue;
}
+ let Some(link_wasm_import_module) = item.value_str() else {
+ let msg = "wasm import module must be of the form \
+ `wasm_import_module = \"string\"`";
+ sess.span_err(item.span(), msg);
+ continue;
+ };
+ wasm_import_module = Some((link_wasm_import_module, item.span()));
}
- if has_duplicate_modifiers {
- let msg =
- "same modifier is used multiple times in a single `modifiers` argument";
+ _ => {
+ let msg = "unexpected `#[link]` argument, expected one of: \
+ name, kind, modifiers, cfg, wasm_import_module";
sess.span_err(item.span(), msg);
}
- } else {
- let msg = "must be of the form `#[link(modifiers = \"...\")]`";
- sess.span_err(item.span(), msg);
}
}
- if modifiers_count > 1 {
- let msg = "multiple `modifiers` arguments in a single `#[link]` attribute";
- sess.span_err(m.span, msg);
+ // Do this outside the above loop so we don't depend on modifiers coming after kinds
+ let mut verbatim = None;
+ if let Some((modifiers, span)) = modifiers {
+ for modifier in modifiers.as_str().split(',') {
+ let (modifier, value) = match modifier.strip_prefix(&['+', '-']) {
+ Some(m) => (m, modifier.starts_with('+')),
+ None => {
+ sess.span_err(
+ span,
+ "invalid linking modifier syntax, expected '+' or '-' prefix \
+ before one of: bundle, verbatim, whole-archive, as-needed",
+ );
+ continue;
+ }
+ };
+
+ macro report_unstable_modifier($feature: ident) {
+ if !features.$feature {
+ feature_err(
+ &sess.parse_sess,
+ sym::$feature,
+ span,
+ &format!("linking modifier `{modifier}` is unstable"),
+ )
+ .emit();
+ }
+ }
+ let assign_modifier = |dst: &mut Option<bool>| {
+ if dst.is_some() {
+ let msg = format!(
+ "multiple `{modifier}` modifiers in a single `modifiers` argument"
+ );
+ sess.span_err(span, &msg);
+ } else {
+ *dst = Some(value);
+ }
+ };
+ match (modifier, &mut kind) {
+ ("bundle", Some(NativeLibKind::Static { bundle, .. })) => {
+ report_unstable_modifier!(native_link_modifiers_bundle);
+ assign_modifier(bundle)
+ }
+ ("bundle", _) => {
+ sess.span_err(
+ span,
+ "linking modifier `bundle` is only compatible with \
+ `static` linking kind",
+ );
+ }
+
+ ("verbatim", _) => {
+ report_unstable_modifier!(native_link_modifiers_verbatim);
+ assign_modifier(&mut verbatim)
+ }
+
+ ("whole-archive", Some(NativeLibKind::Static { whole_archive, .. })) => {
+ assign_modifier(whole_archive)
+ }
+ ("whole-archive", _) => {
+ sess.span_err(
+ span,
+ "linking modifier `whole-archive` is only compatible with \
+ `static` linking kind",
+ );
+ }
+
+ ("as-needed", Some(NativeLibKind::Dylib { as_needed }))
+ | ("as-needed", Some(NativeLibKind::Framework { as_needed })) => {
+ report_unstable_modifier!(native_link_modifiers_as_needed);
+ assign_modifier(as_needed)
+ }
+ ("as-needed", _) => {
+ sess.span_err(
+ span,
+ "linking modifier `as-needed` is only compatible with \
+ `dylib` and `framework` linking kinds",
+ );
+ }
+
+ _ => {
+ sess.span_err(
+ span,
+ format!(
+ "unknown linking modifier `{modifier}`, expected one of: \
+ bundle, verbatim, whole-archive, as-needed"
+ ),
+ );
+ }
+ }
+ }
}
- // In general we require #[link(name = "...")] but we allow
- // #[link(wasm_import_module = "...")] without the `name`.
- let requires_name = kind_specified || lib.wasm_import_module.is_none();
- if lib.name.is_none() && requires_name {
+ if let Some((_, span)) = wasm_import_module {
+ if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() {
+ let msg = "`wasm_import_module` is incompatible with \
+ other arguments in `#[link]` attributes";
+ sess.span_err(span, msg);
+ }
+ } else if name.is_none() {
struct_span_err!(
sess,
m.span,
E0459,
- "`#[link(...)]` specified without \
- `name = \"foo\"`"
+ "`#[link]` attribute requires a `name = \"string\"` argument"
)
.span_label(m.span, "missing `name` argument")
.emit();
}
- if lib.kind == NativeLibKind::RawDylib {
- lib.dll_imports.extend(
+ let dll_imports = match kind {
+ Some(NativeLibKind::RawDylib) => {
+ if let Some((name, span)) = name && name.as_str().contains('\0') {
+ sess.span_err(
+ span,
+ "link name must not contain NUL characters if link kind is `raw-dylib`",
+ );
+ }
foreign_mod_items
.iter()
- .map(|child_item| self.build_dll_import(abi, child_item)),
- );
- }
-
- self.register_native_lib(Some(m.span), lib);
- }
- }
-
- fn register_native_lib(&mut self, span: Option<Span>, lib: NativeLib) {
- if lib.name.as_ref().map_or(false, |&s| s == kw::Empty) {
- match span {
- Some(span) => {
- struct_span_err!(
- self.tcx.sess,
- span,
- E0454,
- "`#[link(name = \"\")]` given with empty name"
- )
- .span_label(span, "empty name given")
- .emit();
- }
- None => {
- self.tcx.sess.err("empty library name given via `-l`");
- }
- }
- return;
- }
- let is_osx = self.tcx.sess.target.is_like_osx;
- if matches!(lib.kind, NativeLibKind::Framework { .. }) && !is_osx {
- let msg = "native frameworks are only available on macOS targets";
- match span {
- Some(span) => {
- struct_span_err!(self.tcx.sess, span, E0455, "{}", msg).emit();
- }
- None => {
- self.tcx.sess.err(msg);
+ .map(|child_item| self.build_dll_import(abi, child_item))
+ .collect()
}
- }
- }
- if lib.cfg.is_some() && !self.tcx.features().link_cfg {
- feature_err(
- &self.tcx.sess.parse_sess,
- sym::link_cfg,
- span.unwrap(),
- "kind=\"link_cfg\" is unstable",
- )
- .emit();
- }
- // this just unwraps lib.name; we already established that it isn't empty above.
- if let (NativeLibKind::RawDylib, Some(lib_name)) = (lib.kind, lib.name) {
- let Some(span) = span else {
- bug!("raw-dylib libraries are not supported on the command line");
+ _ => Vec::new(),
};
-
- if !self.tcx.sess.target.options.is_like_windows {
- self.tcx.sess.span_fatal(
- span,
- "`#[link(...)]` with `kind = \"raw-dylib\"` only supported on Windows",
- );
- }
-
- if lib_name.as_str().contains('\0') {
- self.tcx.sess.span_err(span, "library name may not contain NUL characters");
- }
-
- if !self.tcx.features().raw_dylib {
- feature_err(
- &self.tcx.sess.parse_sess,
- sym::raw_dylib,
- span,
- "kind=\"raw-dylib\" is unstable",
- )
- .emit();
- }
+ self.libs.push(NativeLib {
+ name: name.map(|(name, _)| name),
+ kind: kind.unwrap_or(NativeLibKind::Unspecified),
+ cfg,
+ foreign_module: Some(it.def_id.to_def_id()),
+ wasm_import_module: wasm_import_module.map(|(name, _)| name),
+ verbatim,
+ dll_imports,
+ });
}
-
- self.libs.push(lib);
}
// Process libs passed on the command line
// First, check for errors
let mut renames = FxHashSet::default();
for lib in &self.tcx.sess.opts.libs {
+ if let NativeLibKind::Framework { .. } = lib.kind && !self.tcx.sess.target.is_like_osx {
+ // Cannot check this when parsing options because the target is not yet available.
+ self.tcx.sess.err("library kind `framework` is only supported on Apple targets");
+ }
if let Some(ref new_name) = lib.new_name {
let any_duplicate = self
.libs
.filter_map(|lib| lib.name.as_ref())
.any(|n| n.as_str() == lib.name);
if new_name.is_empty() {
- self.tcx.sess.err(&format!(
+ self.tcx.sess.err(format!(
"an empty renaming target was specified for library `{}`",
lib.name
));
} else if !any_duplicate {
- self.tcx.sess.err(&format!(
+ self.tcx.sess.err(format!(
"renaming of the library `{}` was specified, \
however this crate contains no `#[link(...)]` \
attributes referencing this library",
lib.name
));
} else if !renames.insert(&lib.name) {
- self.tcx.sess.err(&format!(
+ self.tcx.sess.err(format!(
"multiple renamings were \
specified for library `{}`",
lib.name
if existing.is_empty() {
// Add if not found
let new_name: Option<&str> = passed_lib.new_name.as_deref();
- let lib = NativeLib {
+ self.libs.push(NativeLib {
name: Some(Symbol::intern(new_name.unwrap_or(&passed_lib.name))),
kind: passed_lib.kind,
cfg: None,
wasm_import_module: None,
verbatim: passed_lib.verbatim,
dll_imports: Vec::new(),
- };
- self.register_native_lib(None, lib);
+ });
} else {
// Move all existing libraries with the same name to the
// end of the command line.
fn get_may_have_doc_links(self, index: DefIndex) -> bool {
self.root.tables.may_have_doc_links.get(self, index).is_some()
}
+
+ fn get_is_intrinsic(self, index: DefIndex) -> bool {
+ self.root.tables.is_intrinsic.get(self, index).is_some()
+ }
}
impl CrateMetadata {
tcx.arena.alloc_slice(&result)
}
defined_lib_features => { cdata.get_lib_features(tcx) }
+ is_intrinsic => { cdata.get_is_intrinsic(def_id.index) }
defined_lang_items => { cdata.get_lang_items(tcx) }
diagnostic_items => { cdata.get_diagnostic_items() }
missing_lang_items => { cdata.get_missing_lang_items(tcx) }
};
use rustc_hir::definitions::DefPathData;
use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::lang_items;
use rustc_hir::{AnonConst, GenericParamKind};
use rustc_index::bit_set::GrowableBitSet;
use rustc_serialize::{opaque, Encodable, Encoder};
use rustc_session::config::CrateType;
use rustc_session::cstore::{ForeignModule, LinkagePreference, NativeLib};
+use rustc_span::hygiene::{ExpnIndex, HygieneEncodeContext, MacroKind};
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{
self, DebuggerVisualizerFile, ExternalSource, FileName, SourceFile, Span, SyntaxContext,
};
-use rustc_span::{
- hygiene::{ExpnIndex, HygieneEncodeContext, MacroKind},
- RealFileName,
-};
use rustc_target::abi::VariantIdx;
use std::hash::Hash;
use std::num::NonZeroUsize;
-use std::path::Path;
use tracing::{debug, trace};
pub(super) struct EncodeContext<'a, 'tcx> {
return;
}
- self.tcx.hir().visit_all_item_likes(&mut self.as_deep_visitor());
+ self.tcx.hir().deep_visit_all_item_likes(self);
}
fn encode_def_path_table(&mut self) {
// is done.
let required_source_files = self.required_source_files.take().unwrap();
+ let working_directory = &self.tcx.sess.opts.working_dir;
+
let adapted = all_source_files
.iter()
.enumerate()
(!source_file.is_imported() || self.is_proc_macro)
})
.map(|(_, source_file)| {
- let mut adapted = match source_file.name {
- FileName::Real(ref realname) => {
- let mut adapted = (**source_file).clone();
- adapted.name = FileName::Real(match realname {
- RealFileName::LocalPath(path_to_file) => {
- // Prepend path of working directory onto potentially
- // relative paths, because they could become relative
- // to a wrong directory.
- // We include `working_dir` as part of the crate hash,
- // so it's okay for us to use it as part of the encoded
- // metadata.
- let working_dir = &self.tcx.sess.opts.working_dir;
- match working_dir {
- RealFileName::LocalPath(absolute) => {
- // Although neither working_dir or the file name were subject
- // to path remapping, the concatenation between the two may
- // be. Hence we need to do a remapping here.
- let joined = Path::new(absolute).join(path_to_file);
- let (joined, remapped) =
- source_map.path_mapping().map_prefix(joined);
- if remapped {
- RealFileName::Remapped {
- local_path: None,
- virtual_name: joined,
- }
- } else {
- RealFileName::LocalPath(joined)
- }
- }
- RealFileName::Remapped { local_path: _, virtual_name } => {
- // If working_dir has been remapped, then we emit
- // Remapped variant as the expanded path won't be valid
- RealFileName::Remapped {
- local_path: None,
- virtual_name: Path::new(virtual_name)
- .join(path_to_file),
- }
- }
- }
- }
- RealFileName::Remapped { local_path: _, virtual_name } => {
- RealFileName::Remapped {
- // We do not want any local path to be exported into metadata
- local_path: None,
- virtual_name: virtual_name.clone(),
- }
- }
- });
- adapted.name_hash = {
- let mut hasher: StableHasher = StableHasher::new();
- adapted.name.hash(&mut hasher);
- hasher.finish::<u128>()
- };
- Lrc::new(adapted)
+ // At export time we expand all source file paths to absolute paths because
+ // downstream compilation sessions can have a different compiler working
+ // directory, so relative paths from this or any other upstream crate
+ // won't be valid anymore.
+ //
+ // At this point we also erase the actual on-disk path and only keep
+ // the remapped version -- as is necessary for reproducible builds.
+ match source_file.name {
+ FileName::Real(ref original_file_name) => {
+ let adapted_file_name =
+ source_map.path_mapping().to_embeddable_absolute_path(
+ original_file_name.clone(),
+ working_directory,
+ );
+
+ if adapted_file_name != *original_file_name {
+ let mut adapted: SourceFile = (**source_file).clone();
+ adapted.name = FileName::Real(adapted_file_name);
+ adapted.name_hash = {
+ let mut hasher: StableHasher = StableHasher::new();
+ adapted.name.hash(&mut hasher);
+ hasher.finish::<u128>()
+ };
+ Lrc::new(adapted)
+ } else {
+ // Nothing to adapt
+ source_file.clone()
+ }
}
-
// expanded code, not from a file
_ => source_file.clone(),
- };
-
+ }
+ })
+ .map(|mut source_file| {
// We're serializing this `SourceFile` into our crate metadata,
// so mark it as coming from this crate.
// This also ensures that we don't try to deserialize the
// dependencies aren't loaded when we deserialize a proc-macro,
// trying to remap the `CrateNum` would fail.
if self.is_proc_macro {
- Lrc::make_mut(&mut adapted).cnum = LOCAL_CRATE;
+ Lrc::make_mut(&mut source_file).cnum = LOCAL_CRATE;
}
- adapted
+ source_file
})
.collect::<Vec<_>>();
}
impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
- fn encode_attrs(&mut self, def_id: DefId) {
- let attrs = self.tcx.get_attrs(def_id);
- record!(self.tables.attributes[def_id] <- attrs);
- if attrs.iter().any(|attr| attr.may_have_doc_links()) {
- self.tables.may_have_doc_links.set(def_id.index, ());
+ fn encode_attrs(&mut self, def_id: LocalDefId) {
+ let mut attrs = self
+ .tcx
+ .hir()
+ .attrs(self.tcx.hir().local_def_id_to_hir_id(def_id))
+ .iter()
+ .filter(|attr| !rustc_feature::is_builtin_only_local(attr.name_or_empty()));
+
+ record!(self.tables.attributes[def_id.to_def_id()] <- attrs.clone());
+ if attrs.any(|attr| attr.may_have_doc_links()) {
+ self.tables.may_have_doc_links.set(def_id.local_def_index, ());
}
}
let Some(def_kind) = def_kind else { continue };
self.tables.opt_def_kind.set(def_id.index, def_kind);
record!(self.tables.def_span[def_id] <- tcx.def_span(def_id));
- self.encode_attrs(def_id);
+ self.encode_attrs(local_id);
record!(self.tables.expn_that_defined[def_id] <- self.tcx.expn_that_defined(def_id));
if def_kind.has_codegen_attrs() {
record!(self.tables.codegen_fn_attrs[def_id] <- self.tcx.codegen_fn_attrs(def_id));
}
if impl_item.kind == ty::AssocKind::Fn {
record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
+ if tcx.is_intrinsic(def_id) {
+ self.tables.is_intrinsic.set(def_id.index, ());
+ }
}
}
}
if let hir::ItemKind::Fn(..) = item.kind {
record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
+ if tcx.is_intrinsic(def_id) {
+ self.tables.is_intrinsic.set(def_id.index, ());
+ }
}
if let hir::ItemKind::Impl { .. } = item.kind {
if let Some(trait_ref) = self.tcx.impl_trait_ref(def_id) {
self.tables.opt_def_kind.set(LOCAL_CRATE.as_def_id().index, DefKind::Mod);
record!(self.tables.def_span[LOCAL_CRATE.as_def_id()] <- tcx.def_span(LOCAL_CRATE.as_def_id()));
- self.encode_attrs(LOCAL_CRATE.as_def_id());
+ self.encode_attrs(LOCAL_CRATE.as_def_id().expect_local());
record!(self.tables.visibility[LOCAL_CRATE.as_def_id()] <- tcx.visibility(LOCAL_CRATE.as_def_id()));
if let Some(stability) = stability {
record!(self.tables.lookup_stability[LOCAL_CRATE.as_def_id()] <- stability);
let def_id = id.to_def_id();
self.tables.opt_def_kind.set(def_id.index, DefKind::Macro(macro_kind));
record!(self.tables.kind[def_id] <- EntryKind::ProcMacro(macro_kind));
- self.encode_attrs(def_id);
+ self.encode_attrs(id);
record!(self.tables.def_keys[def_id] <- def_key);
record!(self.tables.def_ident_span[def_id] <- span);
record!(self.tables.def_span[def_id] <- span);
self.encode_item_type(def_id);
if let hir::ForeignItemKind::Fn(..) = nitem.kind {
record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
+ if tcx.is_intrinsic(def_id) {
+ self.tables.is_intrinsic.set(def_id.index, ());
+ }
}
}
}
traits_in_crate: |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
- #[derive(Default)]
- struct TraitsVisitor {
- traits: Vec<DefId>,
- }
- impl ItemLikeVisitor<'_> for TraitsVisitor {
- fn visit_item(&mut self, item: &hir::Item<'_>) {
- if let hir::ItemKind::Trait(..) | hir::ItemKind::TraitAlias(..) = item.kind {
- self.traits.push(item.def_id.to_def_id());
- }
+ let mut traits = Vec::new();
+ for id in tcx.hir().items() {
+ if matches!(tcx.def_kind(id.def_id), DefKind::Trait | DefKind::TraitAlias) {
+ traits.push(id.def_id.to_def_id())
}
- fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {}
- fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {}
- fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {}
}
- let mut visitor = TraitsVisitor::default();
- tcx.hir().visit_all_item_likes(&mut visitor);
// Bring everything into deterministic order.
- visitor.traits.sort_by_cached_key(|&def_id| tcx.def_path_hash(def_id));
- tcx.arena.alloc_slice(&visitor.traits)
+ traits.sort_by_cached_key(|&def_id| tcx.def_path_hash(def_id));
+ tcx.arena.alloc_slice(&traits)
},
..*providers
impl_parent: Table<DefIndex, RawDefId>,
impl_polarity: Table<DefIndex, ty::ImplPolarity>,
impl_constness: Table<DefIndex, hir::Constness>,
+ is_intrinsic: Table<DefIndex, ()>,
impl_defaultness: Table<DefIndex, hir::Defaultness>,
// FIXME(eddyb) perhaps compute this on the fly if cheap enough?
coerce_unsized_info: Table<DefIndex, Lazy!(ty::adjustment::CoerceUnsizedInfo)>,
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::*;
use rustc_index::vec::Idx;
use rustc_middle::hir::nested_filter;
self.tcx.hir_crate_items(()).items.iter().copied()
}
+ pub fn module_items(self, module: LocalDefId) -> impl Iterator<Item = ItemId> + 'hir {
+ self.tcx.hir_module_items(module).items()
+ }
+
pub fn par_for_each_item(self, f: impl Fn(ItemId) + Sync + Send) {
par_for_each_in(&self.tcx.hir_crate_items(()).items[..], |id| f(*id));
}
}
/// Visits all items in the crate in some deterministic (but
- /// unspecified) order. If you just need to process every item,
- /// but don't care about nesting, this method is the best choice.
+ /// unspecified) order. If you need to process every item,
+ /// and care about nesting -- usually because your algorithm
+ /// follows lexical scoping rules -- then this method is the best choice.
+ /// If you don't care about nesting, you should use the `tcx.hir_crate_items()` query
+ /// or `items()` instead.
///
- /// If you do care about nesting -- usually because your algorithm
- /// follows lexical scoping rules -- then you want a different
- /// approach. You should override `visit_nested_item` in your
- /// visitor and then call `intravisit::walk_crate` instead.
- pub fn visit_all_item_likes<V>(self, visitor: &mut V)
+ /// Please see the notes in `intravisit.rs` for more information.
+ pub fn deep_visit_all_item_likes<V>(self, visitor: &mut V)
where
- V: itemlikevisit::ItemLikeVisitor<'hir>,
+ V: Visitor<'hir>,
{
let krate = self.krate();
for owner in krate.owners.iter().filter_map(|i| i.as_owner()) {
})
}
- pub fn visit_item_likes_in_module<V>(self, module: LocalDefId, visitor: &mut V)
+ /// If you don't care about nesting, you should use the
+ /// `tcx.hir_module_items()` query or `module_items()` instead.
+ /// Please see notes in `deep_visit_all_item_likes`.
+ pub fn deep_visit_item_likes_in_module<V>(self, module: LocalDefId, visitor: &mut V)
where
- V: ItemLikeVisitor<'hir>,
+ V: Visitor<'hir>,
{
let module = self.tcx.hir_module_items(module);
}
}
- pub fn for_each_module(self, f: impl Fn(LocalDefId)) {
+ pub fn for_each_module(self, mut f: impl FnMut(LocalDefId)) {
let crate_items = self.tcx.hir_crate_items(());
for module in crate_items.submodules.iter() {
f(*module)
/// constant arguments of types, e.g. in `let _: [(); /* HERE */];`.
///
/// **This is the most common choice.** A very common pattern is
-/// to use `visit_all_item_likes()` as an outer loop,
+/// to use `deep_visit_all_item_likes()` as an outer loop,
/// and to have the visitor that visits the contents of each item
/// using this setting.
pub struct OnlyBodies(());
#![feature(get_mut_unchecked)]
#![feature(if_let_guard)]
#![feature(map_first_last)]
+#![feature(negative_impls)]
#![feature(never_type)]
#![feature(extern_types)]
#![feature(new_uninit)]
let terminator = body[source].terminator();
let labels = terminator.kind.fmt_successor_labels();
- for (&target, label) in terminator.successors().zip(labels) {
+ for (target, label) in terminator.successors().zip(labels) {
let src = node(def_id, source);
let trg = node(def_id, target);
edges.push(Edge::new(src, trg, label.to_string()));
use super::{AllocId, ConstAlloc, Pointer, Scalar};
use crate::mir::interpret::ConstValue;
-use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty};
+use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty, ValTree};
use rustc_data_structures::sync::Lock;
use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorGuaranteed};
pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
+pub type EvalToValTreeResult<'tcx> = Result<Option<ValTree<'tcx>>, ErrorHandled>;
pub fn struct_error<'tcx>(
tcx: TyCtxtAt<'tcx>,
pub use self::error::{
struct_error, CheckInAllocMsg, ErrorHandled, EvalToAllocationRawResult, EvalToConstValueResult,
- InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo, MachineStopType,
- ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo, UninitBytesAccess,
- UnsupportedOpInfo,
+ EvalToValTreeResult, InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo,
+ MachineStopType, ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo,
+ UninitBytesAccess, UnsupportedOpInfo,
};
pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar, ScalarMaybeUninit};
where
Self: Sized;
- /// Provenance must always be able to identify the allocation this ptr points to.
+ /// If `OFFSET_IS_ADDR == false`, provenance must always be able to
+ /// identify the allocation this ptr points to (i.e., this must return `Some`).
+ /// Otherwise this function is best-effort (but must agree with `Machine::ptr_get_alloc`).
/// (Identifying the offset in that allocation, however, is harder -- use `Memory::ptr_get_alloc` for that.)
- fn get_alloc_id(self) -> AllocId;
+ fn get_alloc_id(self) -> Option<AllocId>;
}
impl Provenance for AllocId {
Ok(())
}
- fn get_alloc_id(self) -> AllocId {
- self
+ fn get_alloc_id(self) -> Option<AllocId> {
+ Some(self)
}
}
Ok(self.global_alloc(raw_const.alloc_id).unwrap_memory())
}
- /// Destructure a constant ADT or array into its variant index and its field values.
+ /// Destructure a type-level constant ADT or array into its variant index and its field values.
+ /// Panics if the destructuring fails, use `try_destructure_const` for fallible version.
pub fn destructure_const(
self,
param_env_and_val: ty::ParamEnvAnd<'tcx, ty::Const<'tcx>>,
) -> mir::DestructuredConst<'tcx> {
self.try_destructure_const(param_env_and_val).unwrap()
}
+
+ /// Destructure a mir constant ADT or array into its variant index and its field values.
+ /// Panics if the destructuring fails, use `try_destructure_const` for fallible version.
+ pub fn destructure_mir_constant(
+ self,
+ param_env: ty::ParamEnv<'tcx>,
+ constant: mir::ConstantKind<'tcx>,
+ ) -> mir::DestructuredMirConstant<'tcx> {
+ self.try_destructure_mir_constant(param_env.and(constant)).unwrap()
+ }
}
} else {
// We know `offset` is relative, since `OFFSET_IS_ADDR == false`.
let (tag, offset) = ptr.into_parts();
- Err(Scalar::Ptr(Pointer::new(tag.get_alloc_id(), offset), sz))
+ // Because `OFFSET_IS_ADDR == false`, this unwrap can never fail.
+ Err(Scalar::Ptr(Pointer::new(tag.get_alloc_id().unwrap(), offset), sz))
}
}
}
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html
use crate::mir::coverage::{CodeRegion, CoverageKind};
-use crate::mir::interpret::{ConstAllocation, ConstValue, GlobalAlloc, Scalar};
+use crate::mir::interpret::{ConstAllocation, ConstValue, GlobalAlloc, LitToConstInput, Scalar};
use crate::mir::visit::MirVisitable;
use crate::ty::adjustment::PointerCast;
use crate::ty::codec::{TyDecoder, TyEncoder};
/// Type for MIR `Assert` terminator error messages.
pub type AssertMessage<'tcx> = AssertKind<Operand<'tcx>>;
-// FIXME: Change `Successors` to `impl Iterator<Item = BasicBlock>`.
-#[allow(rustc::pass_by_value)]
-pub type Successors<'a> =
- iter::Chain<option::IntoIter<&'a BasicBlock>, slice::Iter<'a, BasicBlock>>;
+pub type Successors<'a> = impl Iterator<Item = BasicBlock> + 'a;
pub type SuccessorsMut<'a> =
iter::Chain<option::IntoIter<&'a mut BasicBlock>, slice::IterMut<'a, BasicBlock>>;
}
}
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct PlaceRef<'tcx> {
pub local: Local,
pub projection: &'tcx [PlaceElem<'tcx>],
}
+// Once we stop implementing `Ord` for `DefId`,
+// this impl will be unnecessary. Until then, we'll
+// leave this impl in place to prevent re-adding a
+// dependnecy on the `Ord` impl for `DefId`
+impl<'tcx> !PartialOrd for PlaceRef<'tcx> {}
+
impl<'tcx> Place<'tcx> {
// FIXME change this to a const fn by also making List::empty a const fn.
pub fn return_place() -> Place<'tcx> {
substs: SubstsRef<'tcx>,
span: Span,
) -> Self {
- let ty = tcx.type_of(def_id).subst(tcx, substs);
+ let ty = tcx.bound_type_of(def_id).subst(tcx, substs);
Operand::Constant(Box::new(Constant {
span,
user_ty: None,
Self::from_opt_const_arg_anon_const(tcx, ty::WithOptConstParam::unknown(def_id), param_env)
}
+ #[instrument(skip(tcx), level = "debug")]
+ pub fn from_inline_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+ let body_id = match tcx.hir().get(hir_id) {
+ hir::Node::AnonConst(ac) => ac.body,
+ _ => span_bug!(
+ tcx.def_span(def_id.to_def_id()),
+ "from_inline_const can only process anonymous constants"
+ ),
+ };
+ let expr = &tcx.hir().body(body_id).value;
+ let ty = tcx.typeck(def_id).node_type(hir_id);
+
+ let lit_input = match expr.kind {
+ hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }),
+ hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => match expr.kind {
+ hir::ExprKind::Lit(ref lit) => {
+ Some(LitToConstInput { lit: &lit.node, ty, neg: true })
+ }
+ _ => None,
+ },
+ _ => None,
+ };
+ if let Some(lit_input) = lit_input {
+ // If an error occurred, ignore that it's a literal and leave reporting the error up to
+ // mir.
+ match tcx.at(expr.span).lit_to_mir_constant(lit_input) {
+ Ok(c) => return c,
+ Err(_) => {}
+ }
+ }
+
+ let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id());
+ let parent_substs =
+ tcx.erase_regions(InternalSubsts::identity_for_item(tcx, typeck_root_def_id));
+ let substs =
+ ty::InlineConstSubsts::new(tcx, ty::InlineConstSubstsParts { parent_substs, ty })
+ .substs;
+ let uneval_const = tcx.mk_const(ty::ConstS {
+ val: ty::ConstKind::Unevaluated(ty::Unevaluated {
+ def: ty::WithOptConstParam::unknown(def_id).to_global(),
+ substs,
+ promoted: None,
+ }),
+ ty,
+ });
+ debug!(?uneval_const);
+ debug_assert!(!uneval_const.has_free_regions());
+
+ Self::Ty(uneval_const)
+ }
+
#[instrument(skip(tcx), level = "debug")]
fn from_opt_const_arg_anon_const(
tcx: TyCtxt<'tcx>,
impl<'tcx> graph::WithSuccessors for Body<'tcx> {
#[inline]
fn successors(&self, node: Self::Node) -> <Self as GraphSuccessors<'_>>::Iter {
- self.basic_blocks[node].terminator().successors().cloned()
+ self.basic_blocks[node].terminator().successors()
}
}
impl<'a, 'b> graph::GraphSuccessors<'b> for Body<'a> {
type Item = BasicBlock;
- type Iter = iter::Cloned<Successors<'b>>;
+ type Iter = Successors<'b>;
}
impl<'tcx, 'graph> graph::GraphPredecessors<'graph> for Body<'tcx> {
// get terminator's targets and apply the statement to all of them.
if loc.statement_index > body[loc.block].statements.len() {
let term = body[loc.block].terminator();
- let successors = term.successors().clone();
-
- for i in successors {
+ for i in term.successors() {
stmts_and_targets
.push((Statement { source_info, kind: stmt.clone() }, i.clone()));
}
let mut preds = IndexVec::from_elem(SmallVec::new(), basic_blocks);
for (bb, data) in basic_blocks.iter_enumerated() {
if let Some(term) = &data.terminator {
- for &succ in term.successors() {
+ for succ in term.successors() {
preds[succ].push(bb);
}
}
//! Values computed by queries that use MIR.
-use crate::mir::{Body, Promoted};
+use crate::mir::{self, Body, Promoted};
use crate::ty::{self, OpaqueHiddenType, Ty, TyCtxt};
use rustc_data_structures::stable_map::FxHashMap;
use rustc_data_structures::vec_map::VecMap;
Region(ty::RegionVid),
}
-/// The constituent parts of an ADT or array.
+/// The constituent parts of a type level constant of kind ADT or array.
#[derive(Copy, Clone, Debug, HashStable)]
pub struct DestructuredConst<'tcx> {
pub variant: Option<VariantIdx>,
pub fields: &'tcx [ty::Const<'tcx>],
}
+/// The constituent parts of a mir constant of kind ADT or array.
+#[derive(Copy, Clone, Debug, HashStable)]
+pub struct DestructuredMirConstant<'tcx> {
+ pub variant: Option<VariantIdx>,
+ pub fields: &'tcx [mir::ConstantKind<'tcx>],
+}
+
/// Coverage information summarized from a MIR if instrumented for source code coverage (see
/// compiler option `-Cinstrument-coverage`). This information is generated by the
/// `InstrumentCoverage` MIR pass and can be retrieved via the `coverageinfo` query.
Rvalue::Aggregate(ref ak, ref ops) => match **ak {
AggregateKind::Array(ty) => tcx.mk_array(ty, ops.len() as u64),
AggregateKind::Tuple => tcx.mk_tup(ops.iter().map(|op| op.ty(local_decls, tcx))),
- AggregateKind::Adt(did, _, substs, _, _) => tcx.type_of(did).subst(tcx, substs),
+ AggregateKind::Adt(did, _, substs, _, _) => {
+ tcx.bound_type_of(did).subst(tcx, substs)
+ }
AggregateKind::Closure(did, substs) => tcx.mk_closure(did, substs),
AggregateKind::Generator(did, substs, movability) => {
tcx.mk_generator(did, substs, movability)
| Return
| Unreachable
| Call { destination: None, cleanup: None, .. }
- | InlineAsm { destination: None, cleanup: None, .. } => None.into_iter().chain(&[]),
- Goto { target: ref t }
- | Call { destination: None, cleanup: Some(ref t), .. }
- | Call { destination: Some((_, ref t)), cleanup: None, .. }
- | Yield { resume: ref t, drop: None, .. }
- | DropAndReplace { target: ref t, unwind: None, .. }
- | Drop { target: ref t, unwind: None, .. }
- | Assert { target: ref t, cleanup: None, .. }
- | FalseUnwind { real_target: ref t, unwind: None }
- | InlineAsm { destination: Some(ref t), cleanup: None, .. }
- | InlineAsm { destination: None, cleanup: Some(ref t), .. } => {
- Some(t).into_iter().chain(&[])
+ | InlineAsm { destination: None, cleanup: None, .. } => {
+ None.into_iter().chain((&[]).into_iter().copied())
}
- Call { destination: Some((_, ref t)), cleanup: Some(ref u), .. }
- | Yield { resume: ref t, drop: Some(ref u), .. }
- | DropAndReplace { target: ref t, unwind: Some(ref u), .. }
- | Drop { target: ref t, unwind: Some(ref u), .. }
- | Assert { target: ref t, cleanup: Some(ref u), .. }
- | FalseUnwind { real_target: ref t, unwind: Some(ref u) }
- | InlineAsm { destination: Some(ref t), cleanup: Some(ref u), .. } => {
- Some(t).into_iter().chain(slice::from_ref(u))
+ Goto { target: t }
+ | Call { destination: None, cleanup: Some(t), .. }
+ | Call { destination: Some((_, t)), cleanup: None, .. }
+ | Yield { resume: t, drop: None, .. }
+ | DropAndReplace { target: t, unwind: None, .. }
+ | Drop { target: t, unwind: None, .. }
+ | Assert { target: t, cleanup: None, .. }
+ | FalseUnwind { real_target: t, unwind: None }
+ | InlineAsm { destination: Some(t), cleanup: None, .. }
+ | InlineAsm { destination: None, cleanup: Some(t), .. } => {
+ Some(t).into_iter().chain((&[]).into_iter().copied())
}
- SwitchInt { ref targets, .. } => None.into_iter().chain(&targets.targets),
- FalseEdge { ref real_target, ref imaginary_target } => {
- Some(real_target).into_iter().chain(slice::from_ref(imaginary_target))
+ Call { destination: Some((_, t)), cleanup: Some(ref u), .. }
+ | Yield { resume: t, drop: Some(ref u), .. }
+ | DropAndReplace { target: t, unwind: Some(ref u), .. }
+ | Drop { target: t, unwind: Some(ref u), .. }
+ | Assert { target: t, cleanup: Some(ref u), .. }
+ | FalseUnwind { real_target: t, unwind: Some(ref u) }
+ | InlineAsm { destination: Some(t), cleanup: Some(ref u), .. } => {
+ Some(t).into_iter().chain(slice::from_ref(u).into_iter().copied())
}
+ SwitchInt { ref targets, .. } => {
+ None.into_iter().chain(targets.targets.iter().copied())
+ }
+ FalseEdge { real_target, ref imaginary_target } => Some(real_target)
+ .into_iter()
+ .chain(slice::from_ref(imaginary_target).into_iter().copied()),
}
}
// two iterations yield `C` and finally `A` for a final traversal of [E, D, B, C, A]
loop {
let bb = if let Some(&mut (_, ref mut iter)) = self.visit_stack.last_mut() {
- if let Some(&bb) = iter.next() {
+ if let Some(bb) = iter.next() {
bb
} else {
break;
// for best performance, we want to use an iterator rather
// than a for-loop, to avoid calling `body::Body::invalidate` for
// each basic block.
+ #[cfg_attr(not(bootstrap), allow(unused_macro_rules))]
macro_rules! basic_blocks {
(mut) => (body.basic_blocks_mut().iter_enumerated_mut());
() => (body.basic_blocks().iter_enumerated());
self.visit_local_decl(local, & $($mutability)? body.local_decls[local]);
}
+ #[cfg_attr(not(bootstrap), allow(unused_macro_rules))]
macro_rules! type_annotations {
(mut) => (body.user_type_annotations.iter_enumerated_mut());
() => (body.user_type_annotations.iter_enumerated());
body: &$($mutability)? Body<'tcx>,
location: Location
) {
+ #[cfg_attr(not(bootstrap), allow(unused_macro_rules))]
macro_rules! basic_blocks {
(mut) => (body.basic_blocks_mut());
() => (body.basic_blocks());
cache_on_disk_if { true }
}
- /// Convert an evaluated constant to a type level constant or
+ /// Evaluate a constant and convert it to a type level constant or
/// return `None` if that is not possible.
- query const_to_valtree(
- key: ty::ParamEnvAnd<'tcx, ConstAlloc<'tcx>>
- ) -> Option<ty::ValTree<'tcx>> {
- desc { "destructure constant" }
+ query eval_to_valtree(
+ key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>
+ ) -> EvalToValTreeResult<'tcx> {
+ desc { "evaluate type-level constant" }
remap_env_constness
}
/// field values or return `None` if constant is invalid.
///
/// Use infallible `TyCtxt::destructure_const` when you know that constant is valid.
- query try_destructure_const(
- key: ty::ParamEnvAnd<'tcx, ty::Const<'tcx>>
- ) -> Option<mir::DestructuredConst<'tcx>> {
- desc { "destructure constant" }
+ query try_destructure_const(key: ty::ParamEnvAnd<'tcx, ty::Const<'tcx>>) -> Option<mir::DestructuredConst<'tcx>> {
+ desc { "destructure type level constant"}
+ }
+
+ /// Tries to destructure an `mir::ConstantKind` ADT or array into its variant index
+ /// and its field values.
+ query try_destructure_mir_constant(key: ty::ParamEnvAnd<'tcx, mir::ConstantKind<'tcx>>) -> Option<mir::DestructuredMirConstant<'tcx>> {
+ desc { "destructure mir constant"}
remap_env_constness
}
remap_env_constness
}
+ /// Dereference a constant reference or raw pointer and turn the result into a constant
+ /// again.
+ query deref_mir_constant(
+ key: ty::ParamEnvAnd<'tcx, mir::ConstantKind<'tcx>>
+ ) -> mir::ConstantKind<'tcx> {
+ desc { "deref constant" }
+ remap_env_constness
+ }
+
query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> ConstValue<'tcx> {
desc { "get a &core::panic::Location referring to a span" }
}
desc { "converting literal to const" }
}
+ query lit_to_mir_constant(key: LitToConstInput<'tcx>) -> Result<mir::ConstantKind<'tcx>, LitToConstError> {
+ desc { "converting literal to mir constant" }
+ }
+
query check_match(key: DefId) {
desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) }
cache_on_disk_if { key.is_local() }
desc { |tcx| "checking whether `{}` is `doc(hidden)`", tcx.def_path_str(def_id) }
}
+ /// Returns the attributes on the item at `def_id`.
+ ///
+ /// Do not use this directly, use `tcx.get_attrs` instead.
query item_attrs(def_id: DefId) -> &'tcx [ast::Attribute] {
desc { |tcx| "collecting attributes of `{}`", tcx.def_path_str(def_id) }
separate_provide_extern
query codegen_fulfill_obligation(
key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)
- ) -> Result<&'tcx ImplSource<'tcx, ()>, ErrorGuaranteed> {
+ ) -> Result<&'tcx ImplSource<'tcx, ()>, traits::CodegenObligationError> {
cache_on_disk_if { true }
desc { |tcx|
"checking if `{}` fulfills its obligations",
desc { "calculating the lib features defined in a crate" }
separate_provide_extern
}
+ /// Whether the function is an intrinsic
+ query is_intrinsic(def_id: DefId) -> bool {
+ desc { |tcx| "is_intrinsic({})", tcx.def_path_str(def_id) }
+ separate_provide_extern
+ }
/// Returns the lang items defined in another crate by loading it from metadata.
query get_lang_items(_: ()) -> LanguageItems {
storage(ArenaCacheSelector<'tcx>)
/// * Opaque constants, that must not be matched structurally. So anything that does not derive
/// `PartialEq` and `Eq`.
Constant {
- value: ty::Const<'tcx>,
+ value: mir::ConstantKind<'tcx>,
},
Range(PatRange<'tcx>),
#[derive(Copy, Clone, Debug, PartialEq, HashStable)]
pub struct PatRange<'tcx> {
- pub lo: ty::Const<'tcx>,
- pub hi: ty::Const<'tcx>,
+ pub lo: mir::ConstantKind<'tcx>,
+ pub hi: mir::ConstantKind<'tcx>,
pub end: RangeEnd,
}
/// let x: <() as Assoc>::Output = true;
/// }
/// ```
+ ///
+ /// We also do not reveal the hidden type of opaque types during
+ /// type-checking.
UserFacing,
/// At codegen time, all monomorphic projections will succeed.
/// information.
pub body_id: hir::HirId,
- /// `None` for `MISC_OBLIGATION_CAUSE_CODE` (a common case, occurs ~60% of
- /// the time). `Some` otherwise.
- code: Option<Lrc<ObligationCauseCode<'tcx>>>,
+ code: InternedObligationCauseCode<'tcx>,
}
// This custom hash function speeds up hashing for `Obligation` deduplication
body_id: hir::HirId,
code: ObligationCauseCode<'tcx>,
) -> ObligationCause<'tcx> {
- ObligationCause {
- span,
- body_id,
- code: if code == MISC_OBLIGATION_CAUSE_CODE { None } else { Some(Lrc::new(code)) },
- }
+ ObligationCause { span, body_id, code: code.into() }
}
pub fn misc(span: Span, body_id: hir::HirId) -> ObligationCause<'tcx> {
#[inline(always)]
pub fn dummy() -> ObligationCause<'tcx> {
- ObligationCause { span: DUMMY_SP, body_id: hir::CRATE_HIR_ID, code: None }
+ ObligationCause::dummy_with_span(DUMMY_SP)
}
+ #[inline(always)]
pub fn dummy_with_span(span: Span) -> ObligationCause<'tcx> {
- ObligationCause { span, body_id: hir::CRATE_HIR_ID, code: None }
- }
-
- pub fn make_mut_code(&mut self) -> &mut ObligationCauseCode<'tcx> {
- Lrc::make_mut(self.code.get_or_insert_with(|| Lrc::new(MISC_OBLIGATION_CAUSE_CODE)))
+ ObligationCause { span, body_id: hir::CRATE_HIR_ID, code: Default::default() }
}
pub fn span(&self, tcx: TyCtxt<'tcx>) -> Span {
#[inline]
pub fn code(&self) -> &ObligationCauseCode<'tcx> {
- self.code.as_deref().unwrap_or(&MISC_OBLIGATION_CAUSE_CODE)
+ &self.code
}
- pub fn clone_code(&self) -> Lrc<ObligationCauseCode<'tcx>> {
- match &self.code {
- Some(code) => code.clone(),
- None => Lrc::new(MISC_OBLIGATION_CAUSE_CODE),
- }
+ pub fn map_code(
+ &mut self,
+ f: impl FnOnce(InternedObligationCauseCode<'tcx>) -> ObligationCauseCode<'tcx>,
+ ) {
+ self.code = f(std::mem::take(&mut self.code)).into();
+ }
+
+ pub fn derived_cause(
+ mut self,
+ parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
+ variant: impl FnOnce(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>,
+ ) -> ObligationCause<'tcx> {
+ /*!
+ * Creates a cause for obligations that are derived from
+ * `obligation` by a recursive search (e.g., for a builtin
+ * bound, or eventually a `auto trait Foo`). If `obligation`
+ * is itself a derived obligation, this is just a clone, but
+ * otherwise we create a "derived obligation" cause so as to
+ * keep track of the original root obligation for error
+ * reporting.
+ */
+
+ // NOTE(flaper87): As of now, it keeps track of the whole error
+ // chain. Ideally, we should have a way to configure this either
+ // by using -Z verbose or just a CLI argument.
+ self.code =
+ variant(DerivedObligationCause { parent_trait_pred, parent_code: self.code }).into();
+ self
}
}
pub substs: SubstsRef<'tcx>,
}
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift, Default)]
+pub struct InternedObligationCauseCode<'tcx> {
+ /// `None` for `MISC_OBLIGATION_CAUSE_CODE` (a common case, occurs ~60% of
+ /// the time). `Some` otherwise.
+ code: Option<Lrc<ObligationCauseCode<'tcx>>>,
+}
+
+impl<'tcx> ObligationCauseCode<'tcx> {
+ #[inline(always)]
+ fn into(self) -> InternedObligationCauseCode<'tcx> {
+ InternedObligationCauseCode {
+ code: if let MISC_OBLIGATION_CAUSE_CODE = self { None } else { Some(Lrc::new(self)) },
+ }
+ }
+}
+
+impl<'tcx> std::ops::Deref for InternedObligationCauseCode<'tcx> {
+ type Target = ObligationCauseCode<'tcx>;
+
+ fn deref(&self) -> &Self::Target {
+ self.code.as_deref().unwrap_or(&MISC_OBLIGATION_CAUSE_CODE)
+ }
+}
+
#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)]
pub enum ObligationCauseCode<'tcx> {
/// Not well classified or should be obvious from the span.
/// The node of the function call.
call_hir_id: hir::HirId,
/// The obligation introduced by this argument.
- parent_code: Lrc<ObligationCauseCode<'tcx>>,
+ parent_code: InternedObligationCauseCode<'tcx>,
},
/// Error derived when matching traits/impls; see ObligationCause for more details
pub span: Span,
}
-impl ObligationCauseCode<'_> {
+impl<'tcx> ObligationCauseCode<'tcx> {
// Return the base obligation, ignoring derived obligations.
pub fn peel_derives(&self) -> &Self {
let mut base_cause = self;
- loop {
- match base_cause {
- BuiltinDerivedObligation(DerivedObligationCause { parent_code, .. })
- | DerivedObligation(DerivedObligationCause { parent_code, .. })
- | FunctionArgumentObligation { parent_code, .. } => {
- base_cause = &parent_code;
- }
- ImplDerivedObligation(obligation_cause) => {
- base_cause = &*obligation_cause.derived.parent_code;
- }
- _ => break,
- }
+ while let Some((parent_code, _)) = base_cause.parent() {
+ base_cause = parent_code;
}
base_cause
}
+
+ pub fn parent(&self) -> Option<(&Self, Option<ty::PolyTraitPredicate<'tcx>>)> {
+ match self {
+ FunctionArgumentObligation { parent_code, .. } => Some((parent_code, None)),
+ BuiltinDerivedObligation(derived)
+ | DerivedObligation(derived)
+ | ImplDerivedObligation(box ImplDerivedObligationCause { derived, .. }) => {
+ Some((&derived.parent_code, Some(derived.parent_trait_pred)))
+ }
+ _ => None,
+ }
+ }
}
// `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger.
pub parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
/// The parent trait had this cause.
- pub parent_code: Lrc<ObligationCauseCode<'tcx>>,
+ pub parent_code: InternedObligationCauseCode<'tcx>,
}
#[derive(Clone, Debug, TypeFoldable, Lift)]
/// the method's receiver (`self` argument) can't be dispatched on
UndispatchableReceiver,
}
+
+/// These are the error cases for `codegen_fulfill_obligation`.
+#[derive(Copy, Clone, Debug, Hash, HashStable, Encodable, Decodable)]
+pub enum CodegenObligationError {
+ /// Ambiguity can happen when monomorphizing during trans
+ /// expands to some humongous type that never occurred
+ /// statically -- this humongous type can then overflow,
+ /// leading to an ambiguous result. So report this as an
+ /// overflow bug, since I believe this is the only case
+ /// where ambiguity can result.
+ Ambiguity,
+ /// This can trigger when we probe for the source of a `'static` lifetime requirement
+ /// on a trait object: `impl Foo for dyn Trait {}` has an implicit `'static` bound.
+ /// This can also trigger when we have a global bound that is not actually satisfied,
+ /// but was included during typeck due to the trivial_bounds feature.
+ Unimplemented,
+ FulfillmentError,
+}
flags |= AdtFlags::HAS_CTOR;
}
- let attrs = tcx.get_attrs(did);
- if tcx.sess.contains_name(&attrs, sym::fundamental) {
+ if tcx.has_attr(did, sym::fundamental) {
flags |= AdtFlags::IS_FUNDAMENTAL;
}
if Some(did) == tcx.lang_items().phantom_data() {
/// `rustc_layout_scalar_valid_range` attribute.
// FIXME(eddyb) this is an awkward spot for this method, maybe move it?
pub fn layout_scalar_valid_range(self, def_id: DefId) -> (Bound<u128>, Bound<u128>) {
- let attrs = self.get_attrs(def_id);
let get = |name| {
- let Some(attr) = attrs.iter().find(|a| a.has_name(name)) else {
+ let Some(attr) = self.get_attr(def_id, name) else {
return Bound::Unbounded;
};
debug!("layout_scalar_valid_range: attr={:?}", attr);
pub fn caller_location_ty(self) -> Ty<'tcx> {
self.mk_imm_ref(
self.lifetimes.re_static,
- self.type_of(self.require_lang_item(LangItem::PanicLocation, None))
+ self.bound_type_of(self.require_lang_item(LangItem::PanicLocation, None))
.subst(self, self.mk_substs([self.lifetimes.re_static.into()].iter())),
)
}
ty_param.into()
} else {
assert!(has_default);
- self.type_of(param.def_id).subst(self, substs).into()
+ self.bound_type_of(param.def_id).subst(self, substs).into()
}
}
});
}
}
TargetFeatureCast(def_id) => {
- let attrs = self.get_attrs(*def_id);
- let target_spans = attrs
- .iter()
- .filter(|attr| attr.has_name(sym::target_feature))
- .map(|attr| attr.span);
+ let target_spans =
+ self.get_attrs(*def_id, sym::target_feature).map(|attr| attr.span);
diag.note(
"functions with `#[target_feature]` can only be coerced to `unsafe` function pointers"
);
use crate::middle::resolve_lifetime::ObjectLifetimeDefault;
use crate::ty;
use crate::ty::subst::{Subst, SubstsRef};
+use crate::ty::EarlyBinder;
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::DefId;
substs: SubstsRef<'tcx>,
) -> InstantiatedPredicates<'tcx> {
InstantiatedPredicates {
- predicates: self.predicates.iter().map(|(p, _)| p.subst(tcx, substs)).collect(),
+ predicates: self
+ .predicates
+ .iter()
+ .map(|(p, _)| EarlyBinder(*p).subst(tcx, substs))
+ .collect(),
spans: self.predicates.iter().map(|(_, sp)| *sp).collect(),
}
}
if let Some(def_id) = self.parent {
tcx.predicates_of(def_id).instantiate_into(tcx, instantiated, substs);
}
- instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| p.subst(tcx, substs)));
+ instantiated
+ .predicates
+ .extend(self.predicates.iter().map(|(p, _)| EarlyBinder(*p).subst(tcx, substs)));
instantiated.spans.extend(self.predicates.iter().map(|(_, sp)| *sp));
}
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use crate::ty::print::{FmtPrinter, Printer};
use crate::ty::subst::{InternalSubsts, Subst};
-use crate::ty::{self, SubstsRef, Ty, TyCtxt, TypeFoldable};
+use crate::ty::{self, EarlyBinder, SubstsRef, Ty, TyCtxt, TypeFoldable};
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def::Namespace;
use rustc_hir::def_id::{CrateNum, DefId};
use rustc_hir::lang_items::LangItem;
use rustc_macros::HashStable;
use rustc_middle::ty::normalize_erasing_regions::NormalizationError;
+use rustc_span::Symbol;
use std::fmt;
}
#[inline]
- pub fn attrs(&self, tcx: TyCtxt<'tcx>) -> ty::Attributes<'tcx> {
- tcx.get_attrs(self.def_id())
+ pub fn get_attrs(&self, tcx: TyCtxt<'tcx>, attr: Symbol) -> ty::Attributes<'tcx> {
+ tcx.get_attrs(self.def_id(), attr)
}
/// Returns `true` if the LLVM version of this instance is unconditionally
where
T: TypeFoldable<'tcx> + Copy,
{
- if let Some(substs) = self.substs_for_mir_body() { v.subst(tcx, substs) } else { *v }
+ if let Some(substs) = self.substs_for_mir_body() {
+ EarlyBinder(*v).subst(tcx, substs)
+ } else {
+ *v
+ }
}
#[inline(always)]
use crate::mir::{GeneratorLayout, GeneratorSavedLocal};
use crate::ty::normalize_erasing_regions::NormalizationError;
use crate::ty::subst::Subst;
-use crate::ty::{self, subst::SubstsRef, ReprOptions, Ty, TyCtxt, TypeFoldable};
+use crate::ty::{self, subst::SubstsRef, EarlyBinder, ReprOptions, Ty, TyCtxt, TypeFoldable};
use rustc_ast as ast;
use rustc_attr as attr;
use rustc_hir as hir;
) -> Result<Layout<'tcx>, LayoutError<'tcx>> {
use SavedLocalEligibility::*;
let tcx = self.tcx;
- let subst_field = |ty: Ty<'tcx>| ty.subst(tcx, substs);
+ let subst_field = |ty: Ty<'tcx>| EarlyBinder(ty).subst(tcx, substs);
let Some(info) = tcx.generator_layout(def_id) else {
return Err(LayoutError::Unknown(ty));
// track of a polymorphization `ParamEnv` to allow normalizing later.
let mut sig = match *ty.kind() {
ty::FnDef(def_id, substs) => tcx
- .normalize_erasing_regions(tcx.param_env(def_id), tcx.fn_sig(def_id))
+ .normalize_erasing_regions(tcx.param_env(def_id), tcx.bound_fn_sig(def_id))
.subst(tcx, substs),
_ => unreachable!(),
};
return false;
}
+ // With `-C panic=abort`, all non-FFI functions are required to not unwind.
+ //
+ // Note that this is true regardless ABI specified on the function -- a `extern "C-unwind"`
+ // function defined in Rust is also required to abort.
+ if tcx.sess.panic_strategy() == PanicStrategy::Abort && !tcx.is_foreign_item(did) {
+ return false;
+ }
+
// With -Z panic-in-drop=abort, drop_in_place never unwinds.
//
// This is not part of `codegen_fn_attrs` as it can differ between crates
pub use self::BorrowKind::*;
pub use self::IntVarValue::*;
pub use self::Variance::*;
-pub use adt::*;
-pub use assoc::*;
-pub use generics::*;
-use rustc_data_structures::fingerprint::Fingerprint;
-pub use vtable::*;
-
use crate::metadata::ModChild;
use crate::middle::privacy::AccessLevels;
use crate::mir::{Body, GeneratorLayout};
use crate::ty::fast_reject::SimplifiedType;
use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef};
use crate::ty::util::Discr;
+pub use adt::*;
+pub use assoc::*;
+pub use generics::*;
use rustc_ast as ast;
use rustc_attr as attr;
+use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_data_structures::intern::{Interned, WithStableHash};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::Span;
use rustc_target::abi::Align;
+pub use vtable::*;
use std::fmt::Debug;
use std::hash::Hash;
pub use self::sty::TyKind::*;
pub use self::sty::{
Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVar, BoundVariableKind,
- CanonicalPolyFnSig, ClosureSubsts, ClosureSubstsParts, ConstVid, EarlyBoundRegion,
+ CanonicalPolyFnSig, ClosureSubsts, ClosureSubstsParts, ConstVid, EarlyBinder, EarlyBoundRegion,
ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, FreeRegion, GenSig,
GeneratorSubsts, GeneratorSubstsParts, InlineConstSubsts, InlineConstSubstsParts, ParamConst,
ParamTy, PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, PolyGenSig,
let shifted_pred =
tcx.shift_bound_var_indices(trait_bound_vars.len(), bound_pred.skip_binder());
// 2) Self: Bar1<'a, '^0.1> -> T: Bar1<'^0.0, '^0.1>
- let new = shifted_pred.subst(tcx, trait_ref.skip_binder().substs);
+ let new = EarlyBinder(shifted_pred).subst(tcx, trait_ref.skip_binder().substs);
// 3) ['x] + ['b] -> ['x, 'b]
let bound_vars =
tcx.mk_bound_variable_kinds(trait_bound_vars.iter().chain(pred_bound_vars));
field_shuffle_seed ^= user_seed;
}
- for attr in tcx.get_attrs(did).iter() {
- for r in attr::find_repr_attrs(&tcx.sess, attr) {
+ for attr in tcx.get_attrs(did, sym::repr) {
+ for r in attr::parse_repr_attr(&tcx.sess, attr) {
flags.insert(match r {
attr::ReprC => ReprFlags::IS_C,
attr::ReprPacked(pack) => {
/// Returns the type of this field. The resulting type is not normalized. The `subst` is
/// typically obtained via the second field of [`TyKind::Adt`].
pub fn ty(&self, tcx: TyCtxt<'tcx>, subst: SubstsRef<'tcx>) -> Ty<'tcx> {
- tcx.type_of(self.did).subst(tcx, subst)
+ tcx.bound_type_of(self.did).subst(tcx, subst)
}
/// Computes the `Ident` of this variant by looking up the `Span`
}
}
-pub type Attributes<'tcx> = &'tcx [ast::Attribute];
-
+pub type Attributes<'tcx> = impl Iterator<Item = &'tcx ast::Attribute>;
#[derive(Debug, PartialEq, Eq)]
pub enum ImplOverlapKind {
/// These impls are always allowed to overlap.
.filter(|item| item.kind == AssocKind::Fn && item.defaultness.has_value())
}
- fn opt_item_name(self, def_id: DefId) -> Option<Symbol> {
+ /// Look up the name of a definition across crates. This does not look at HIR.
+ pub fn opt_item_name(self, def_id: DefId) -> Option<Symbol> {
if let Some(cnum) = def_id.as_crate_root() {
Some(self.crate_name(cnum))
} else {
/// Look up the name of a definition across crates. This does not look at HIR.
///
- /// When possible, this function should be used for cross-crate lookups over
- /// [`opt_item_name`] to avoid invalidating the incremental cache. If you
- /// need to handle items without a name, or HIR items that will not be
- /// serialized cross-crate, or if you need the span of the item, use
+ /// This method will ICE if the corresponding item does not have a name. In these cases, use
/// [`opt_item_name`] instead.
///
/// [`opt_item_name`]: Self::opt_item_name
pub fn item_name(self, id: DefId) -> Symbol {
- // Look at cross-crate items first to avoid invalidating the incremental cache
- // unless we have to.
self.opt_item_name(id).unwrap_or_else(|| {
bug!("item_name: no name for {:?}", self.def_path(id));
})
}
}
- /// Gets the attributes of a definition.
- pub fn get_attrs(self, did: DefId) -> Attributes<'tcx> {
+ // FIXME(@lcnr): Remove this function.
+ pub fn get_attrs_unchecked(self, did: DefId) -> &'tcx [ast::Attribute] {
if let Some(did) = did.as_local() {
self.hir().attrs(self.hir().local_def_id_to_hir_id(did))
} else {
}
}
+ /// Gets all attributes with the given name.
+ pub fn get_attrs(self, did: DefId, attr: Symbol) -> ty::Attributes<'tcx> {
+ let filter_fn = move |a: &&ast::Attribute| a.has_name(attr);
+ if let Some(did) = did.as_local() {
+ self.hir().attrs(self.hir().local_def_id_to_hir_id(did)).iter().filter(filter_fn)
+ } else if cfg!(debug_assertions) && rustc_feature::is_builtin_only_local(attr) {
+ bug!("tried to access the `only_local` attribute `{}` from an extern crate", attr);
+ } else {
+ self.item_attrs(did).iter().filter(filter_fn)
+ }
+ }
+
+ pub fn get_attr(self, did: DefId, attr: Symbol) -> Option<&'tcx ast::Attribute> {
+ self.get_attrs(did, attr).next()
+ }
+
/// Determines whether an item is annotated with an attribute.
pub fn has_attr(self, did: DefId, attr: Symbol) -> bool {
- self.sess.contains_name(&self.get_attrs(did), attr)
+ if cfg!(debug_assertions) && !did.is_local() && rustc_feature::is_builtin_only_local(attr) {
+ bug!("tried to access the `only_local` attribute `{}` from an extern crate", attr);
+ } else {
+ self.get_attrs(did, attr).next().is_some()
+ }
}
/// Returns `true` if this is an `auto trait`.
use crate::traits::query::NoSolution;
use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder};
use crate::ty::subst::{Subst, SubstsRef};
-use crate::ty::{self, Ty, TyCtxt};
+use crate::ty::{self, EarlyBinder, Ty, TyCtxt};
#[derive(Debug, Copy, Clone, HashStable, TyEncodable, TyDecodable)]
pub enum NormalizationError<'tcx> {
param_env={:?})",
param_substs, value, param_env,
);
- let substituted = value.subst(self, param_substs);
+ let substituted = EarlyBinder(value).subst(self, param_substs);
self.normalize_erasing_regions(param_env, substituted)
}
param_env={:?})",
param_substs, value, param_env,
);
- let substituted = value.subst(self, param_substs);
+ let substituted = EarlyBinder(value).subst(self, param_substs);
self.try_normalize_erasing_regions(param_env, substituted)
}
}
DefPathData::Impl => {
let generics = self.tcx().generics_of(def_id);
- let mut self_ty = self.tcx().type_of(def_id);
- let mut impl_trait_ref = self.tcx().impl_trait_ref(def_id);
- if substs.len() >= generics.count() {
- self_ty = self_ty.subst(self.tcx(), substs);
- impl_trait_ref = impl_trait_ref.subst(self.tcx(), substs);
- }
+ let self_ty = self.tcx().bound_type_of(def_id);
+ let impl_trait_ref = self.tcx().bound_impl_trait_ref(def_id);
+ let (self_ty, impl_trait_ref) = if substs.len() >= generics.count() {
+ (
+ self_ty.subst(self.tcx(), substs),
+ impl_trait_ref.map(|i| i.subst(self.tcx(), substs)),
+ )
+ } else {
+ (self_ty.0, impl_trait_ref.map(|i| i.0))
+ };
self.print_impl_path(def_id, substs, self_ty, impl_trait_ref)
}
has_default
&& substs[param.index as usize]
== GenericArg::from(
- self.tcx().type_of(param.def_id).subst(self.tcx(), substs),
+ self.tcx().bound_type_of(param.def_id).subst(self.tcx(), substs),
)
}
ty::GenericParamDefKind::Const { has_default } => {
p!(")")
}
ty::FnDef(def_id, substs) => {
- let sig = self.tcx().fn_sig(def_id).subst(self.tcx(), substs);
+ let sig = self.tcx().bound_fn_sig(def_id).subst(self.tcx(), substs);
p!(print(sig), " {{", print_value_path(def_id, substs), "}}");
}
ty::FnPtr(ref bare_fn) => p!(print(bare_fn)),
// Grab the "TraitA + TraitB" from `impl TraitA + TraitB`,
// by looking up the projections associated with the def_id.
- let bounds = self.tcx().explicit_item_bounds(def_id);
+ let bounds = self.tcx().bound_explicit_item_bounds(def_id);
let mut traits = BTreeMap::new();
let mut fn_traits = BTreeMap::new();
let mut is_sized = false;
- for (predicate, _) in bounds {
+ for predicate in bounds.transpose_iter().map(|e| e.map_bound(|(p, _)| *p)) {
let predicate = predicate.subst(self.tcx(), substs);
let bound_predicate = predicate.kind();
use crate::middle::stability::{self, DeprecationEntry};
use crate::mir;
use crate::mir::interpret::GlobalId;
-use crate::mir::interpret::{ConstAlloc, LitToConstError, LitToConstInput};
-use crate::mir::interpret::{ConstValue, EvalToAllocationRawResult, EvalToConstValueResult};
+use crate::mir::interpret::{
+ ConstValue, EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult,
+};
+use crate::mir::interpret::{LitToConstError, LitToConstInput};
use crate::mir::mono::CodegenUnit;
use crate::thir;
use crate::traits::query::{
let params = iter::zip(a_subst, b_subst).enumerate().map(|(i, (a, b))| {
let variance = variances[i];
let variance_info = if variance == ty::Invariant {
- let ty = *cached_ty.get_or_insert_with(|| tcx.type_of(ty_def_id).subst(tcx, a_subst));
+ let ty =
+ *cached_ty.get_or_insert_with(|| tcx.bound_type_of(ty_def_id).subst(tcx, a_subst));
ty::VarianceDiagInfo::Invariant { ty, param_index: i.try_into().unwrap() }
} else {
ty::VarianceDiagInfo::default()
}
}
+impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::EarlyBinder<T> {
+ fn try_super_fold_with<F: FallibleTypeFolder<'tcx>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ self.try_map_bound(|ty| ty.try_fold_with(folder))
+ }
+
+ fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> {
+ self.try_map_bound(|ty| ty.try_fold_with(folder))
+ }
+
+ fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+ self.as_ref().0.visit_with(visitor)
+ }
+
+ fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+ self.as_ref().0.visit_with(visitor)
+ }
+}
+
impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::Binder<'tcx, T> {
fn try_super_fold_with<F: FallibleTypeFolder<'tcx>>(
self,
) -> impl Iterator<Item = impl Iterator<Item = Ty<'tcx>> + Captures<'tcx>> {
let layout = tcx.generator_layout(def_id).unwrap();
layout.variant_fields.iter().map(move |variant| {
- variant.iter().map(move |field| layout.field_tys[*field].subst(tcx, self.substs))
+ variant
+ .iter()
+ .map(move |field| EarlyBinder(layout.field_tys[*field]).subst(tcx, self.substs))
})
}
}
}
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+#[derive(Encodable, Decodable, HashStable)]
+pub struct EarlyBinder<T>(pub T);
+
+impl<T> EarlyBinder<T> {
+ pub fn as_ref(&self) -> EarlyBinder<&T> {
+ EarlyBinder(&self.0)
+ }
+
+ pub fn map_bound_ref<F, U>(&self, f: F) -> EarlyBinder<U>
+ where
+ F: FnOnce(&T) -> U,
+ {
+ self.as_ref().map_bound(f)
+ }
+
+ pub fn map_bound<F, U>(self, f: F) -> EarlyBinder<U>
+ where
+ F: FnOnce(T) -> U,
+ {
+ let value = f(self.0);
+ EarlyBinder(value)
+ }
+
+ pub fn try_map_bound<F, U, E>(self, f: F) -> Result<EarlyBinder<U>, E>
+ where
+ F: FnOnce(T) -> Result<U, E>,
+ {
+ let value = f(self.0)?;
+ Ok(EarlyBinder(value))
+ }
+}
+
+impl<T> EarlyBinder<Option<T>> {
+ pub fn transpose(self) -> Option<EarlyBinder<T>> {
+ self.0.map(|v| EarlyBinder(v))
+ }
+}
+
+impl<T, U> EarlyBinder<(T, U)> {
+ pub fn transpose_tuple2(self) -> (EarlyBinder<T>, EarlyBinder<U>) {
+ (EarlyBinder(self.0.0), EarlyBinder(self.0.1))
+ }
+}
+
+pub struct EarlyBinderIter<T> {
+ t: T,
+}
+
+impl<T: IntoIterator> EarlyBinder<T> {
+ pub fn transpose_iter(self) -> EarlyBinderIter<T::IntoIter> {
+ EarlyBinderIter { t: self.0.into_iter() }
+ }
+}
+
+impl<T: Iterator> Iterator for EarlyBinderIter<T> {
+ type Item = EarlyBinder<T::Item>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.t.next().map(|i| EarlyBinder(i))
+ }
+}
+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
#[derive(HashStable)]
pub enum BoundVariableKind {
pub fn fn_sig(self, tcx: TyCtxt<'tcx>) -> PolyFnSig<'tcx> {
match self.kind() {
- FnDef(def_id, substs) => tcx.fn_sig(*def_id).subst(tcx, substs),
+ FnDef(def_id, substs) => tcx.bound_fn_sig(*def_id).subst(tcx, substs),
FnPtr(f) => *f,
Error(_) => {
// ignore errors (#54954)
ty::Str | ty::Slice(_) => (tcx.types.usize, false),
ty::Dynamic(..) => {
let dyn_metadata = tcx.lang_items().dyn_metadata().unwrap();
- (tcx.type_of(dyn_metadata).subst(tcx, &[tail.into()]), false)
+ (tcx.bound_type_of(dyn_metadata).subst(tcx, &[tail.into()]), false)
},
// type parameters only have unit metadata if they're sized, so return true
use crate::ty::codec::{TyDecoder, TyEncoder};
use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeVisitor};
use crate::ty::sty::{ClosureSubsts, GeneratorSubsts, InlineConstSubsts};
-use crate::ty::{self, Lift, List, ParamConst, Ty, TyCtxt};
+use crate::ty::{self, EarlyBinder, Lift, List, ParamConst, Ty, TyCtxt};
use rustc_data_structures::intern::{Interned, WithStableHash};
use rustc_hir::def_id::DefId;
}
// Just call `foo.subst(tcx, substs)` to perform a substitution across `foo`.
+#[rustc_on_unimplemented(message = "Calling `subst` must now be done through an `EarlyBinder`")]
pub trait Subst<'tcx>: Sized {
- fn subst(self, tcx: TyCtxt<'tcx>, substs: &[GenericArg<'tcx>]) -> Self;
+ type Inner;
+
+ fn subst(self, tcx: TyCtxt<'tcx>, substs: &[GenericArg<'tcx>]) -> Self::Inner;
}
-impl<'tcx, T: TypeFoldable<'tcx>> Subst<'tcx> for T {
- fn subst(self, tcx: TyCtxt<'tcx>, substs: &[GenericArg<'tcx>]) -> T {
+impl<'tcx, T: TypeFoldable<'tcx>> Subst<'tcx> for EarlyBinder<T> {
+ type Inner = T;
+
+ fn subst(self, tcx: TyCtxt<'tcx>, substs: &[GenericArg<'tcx>]) -> Self::Inner {
let mut folder = SubstFolder { tcx, substs, binders_passed: 0 };
- self.fold_with(&mut folder)
+ self.0.fold_with(&mut folder)
}
}
use crate::ty::query::TyCtxtAt;
use crate::ty::subst::{GenericArgKind, Subst, SubstsRef};
use crate::ty::{
- self, Const, DebruijnIndex, DefIdTree, List, ReEarlyBound, Ty, TyCtxt, TyKind::*, TypeFoldable,
+ self, DebruijnIndex, DefIdTree, EarlyBinder, List, ReEarlyBound, Ty, TyCtxt, TyKind::*,
+ TypeFoldable,
};
use rustc_apfloat::Float as _;
use rustc_ast as ast;
use rustc_macros::HashStable;
use rustc_span::{sym, DUMMY_SP};
use rustc_target::abi::{Integer, Size, TargetDataLayout};
+use rustc_target::spec::abi::Abi;
use smallvec::SmallVec;
use std::{fmt, iter};
trace!(?expanded_type);
if visitor.found_recursion { Err(expanded_type) } else { Ok(expanded_type) }
}
+
+ pub fn bound_type_of(self, def_id: DefId) -> EarlyBinder<Ty<'tcx>> {
+ EarlyBinder(self.type_of(def_id))
+ }
+
+ pub fn bound_fn_sig(self, def_id: DefId) -> EarlyBinder<ty::PolyFnSig<'tcx>> {
+ EarlyBinder(self.fn_sig(def_id))
+ }
+
+ pub fn bound_impl_trait_ref(self, def_id: DefId) -> Option<EarlyBinder<ty::TraitRef<'tcx>>> {
+ self.impl_trait_ref(def_id).map(|i| EarlyBinder(i))
+ }
+
+ pub fn bound_explicit_item_bounds(
+ self,
+ def_id: DefId,
+ ) -> EarlyBinder<&'tcx [(ty::Predicate<'tcx>, rustc_span::Span)]> {
+ EarlyBinder(self.explicit_item_bounds(def_id))
+ }
+
+ pub fn bound_item_bounds(
+ self,
+ def_id: DefId,
+ ) -> EarlyBinder<&'tcx ty::List<ty::Predicate<'tcx>>> {
+ EarlyBinder(self.item_bounds(def_id))
+ }
}
struct OpaqueTypeExpander<'tcx> {
let expanded_ty = match self.expanded_cache.get(&(def_id, substs)) {
Some(expanded_ty) => *expanded_ty,
None => {
- let generic_ty = self.tcx.type_of(def_id);
+ let generic_ty = self.tcx.bound_type_of(def_id);
let concrete_ty = generic_ty.subst(self.tcx, substs);
let expanded_ty = self.fold_ty(concrete_ty);
self.expanded_cache.insert((def_id, substs), expanded_ty);
impl<'tcx> Ty<'tcx> {
/// Returns the maximum value for the given numeric type (including `char`s)
/// or returns `None` if the type is not numeric.
- pub fn numeric_max_val(self, tcx: TyCtxt<'tcx>) -> Option<Const<'tcx>> {
+ pub fn numeric_max_val(self, tcx: TyCtxt<'tcx>) -> Option<ty::Const<'tcx>> {
let val = match self.kind() {
ty::Int(_) | ty::Uint(_) => {
let (size, signed) = int_size_and_signed(tcx, self);
}),
_ => None,
};
- val.map(|v| Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self)))
+
+ val.map(|v| ty::Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self)))
}
/// Returns the minimum value for the given numeric type (including `char`s)
/// or returns `None` if the type is not numeric.
- pub fn numeric_min_val(self, tcx: TyCtxt<'tcx>) -> Option<Const<'tcx>> {
+ pub fn numeric_min_val(self, tcx: TyCtxt<'tcx>) -> Option<ty::Const<'tcx>> {
let val = match self.kind() {
ty::Int(_) | ty::Uint(_) => {
let (size, signed) = int_size_and_signed(tcx, self);
}),
_ => None,
};
- val.map(|v| Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self)))
+
+ val.map(|v| ty::Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self)))
}
/// Checks whether values of this type `T` are *moved* or *copied*
/// Determines whether an item is annotated with `doc(hidden)`.
pub fn is_doc_hidden(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
- tcx.get_attrs(def_id)
- .iter()
- .filter_map(|attr| if attr.has_name(sym::doc) { attr.meta_item_list() } else { None })
+ tcx.get_attrs(def_id, sym::doc)
+ .filter_map(|attr| attr.meta_item_list())
.any(|items| items.iter().any(|item| item.has_name(sym::hidden)))
}
+/// Determines whether an item is an intrinsic by Abi.
+pub fn is_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+ matches!(tcx.fn_sig(def_id).abi(), Abi::RustIntrinsic | Abi::PlatformIntrinsic)
+}
+
pub fn provide(providers: &mut ty::query::Providers) {
- *providers = ty::query::Providers { normalize_opaque_types, is_doc_hidden, ..*providers }
+ *providers =
+ ty::query::Providers { normalize_opaque_types, is_doc_hidden, is_intrinsic, ..*providers }
}
//! See docs in build/expr/mod.rs
-use crate::build::Builder;
-use crate::thir::constant::parse_float;
-use rustc_ast as ast;
+use crate::build::{lit_to_mir_constant, Builder};
use rustc_hir::def_id::DefId;
-use rustc_middle::mir::interpret::Allocation;
use rustc_middle::mir::interpret::{ConstValue, LitToConstError, LitToConstInput, Scalar};
use rustc_middle::mir::*;
use rustc_middle::thir::*;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt};
-use rustc_target::abi::Size;
impl<'a, 'tcx> Builder<'a, 'tcx> {
/// Compile `expr`, yielding a compile-time constant. Assumes that
}
}
}
-
-#[instrument(skip(tcx, lit_input))]
-fn lit_to_mir_constant<'tcx>(
- tcx: TyCtxt<'tcx>,
- lit_input: LitToConstInput<'tcx>,
-) -> Result<ConstantKind<'tcx>, LitToConstError> {
- let LitToConstInput { lit, ty, neg } = lit_input;
- let trunc = |n| {
- let param_ty = ty::ParamEnv::reveal_all().and(ty);
- let width = tcx.layout_of(param_ty).map_err(|_| LitToConstError::Reported)?.size;
- trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
- let result = width.truncate(n);
- trace!("trunc result: {}", result);
- Ok(ConstValue::Scalar(Scalar::from_uint(result, width)))
- };
-
- let value = match (lit, &ty.kind()) {
- (ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => {
- let s = s.as_str();
- let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes());
- let allocation = tcx.intern_const_alloc(allocation);
- ConstValue::Slice { data: allocation, start: 0, end: s.len() }
- }
- (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _))
- if matches!(inner_ty.kind(), ty::Slice(_)) =>
- {
- let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]);
- let allocation = tcx.intern_const_alloc(allocation);
- ConstValue::Slice { data: allocation, start: 0, end: data.len() }
- }
- (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => {
- let id = tcx.allocate_bytes(data);
- ConstValue::Scalar(Scalar::from_pointer(id.into(), &tcx))
- }
- (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => {
- ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1)))
- }
- (ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => {
- trunc(if neg { (*n as i128).overflowing_neg().0 as u128 } else { *n })?
- }
- (ast::LitKind::Float(n, _), ty::Float(fty)) => {
- parse_float(*n, *fty, neg).ok_or(LitToConstError::Reported)?
- }
- (ast::LitKind::Bool(b), ty::Bool) => ConstValue::Scalar(Scalar::from_bool(*b)),
- (ast::LitKind::Char(c), ty::Char) => ConstValue::Scalar(Scalar::from_char(*c)),
- (ast::LitKind::Err(_), _) => return Err(LitToConstError::Reported),
- _ => return Err(LitToConstError::TypeError),
- };
-
- Ok(ConstantKind::Val(value, ty))
-}
use crate::build::{BlockAnd, BlockAndExtension, Builder};
use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode};
use rustc_data_structures::{
- fx::{FxIndexMap, FxIndexSet},
+ fx::{FxHashSet, FxIndexMap, FxIndexSet},
stack::ensure_sufficient_stack,
};
use rustc_hir::HirId;
///
/// For `bool` we always generate two edges, one for `true` and one for
/// `false`.
- options: FxIndexMap<ty::Const<'tcx>, u128>,
+ options: FxIndexMap<ConstantKind<'tcx>, u128>,
},
/// Test for equality with value, possibly after an unsizing coercion to
/// `ty`,
Eq {
- value: ty::Const<'tcx>,
+ value: ConstantKind<'tcx>,
// Integer types are handled by `SwitchInt`, and constants with ADT
// types are converted back into patterns, so this can only be `&str`,
// `&[T]`, `f32` or `f64`.
all_fake_borrows.push(place.as_ref());
}
- all_fake_borrows.dedup();
+ // Deduplicate
+ let mut dedup = FxHashSet::default();
+ all_fake_borrows.retain(|b| dedup.insert(*b));
debug!("add_fake_borrows all_fake_borrows = {:?}", all_fake_borrows);
_ => (None, 0),
};
if let Some((min, max, sz)) = range {
- if let (Some(lo), Some(hi)) =
- (lo.val().try_to_bits(sz), hi.val().try_to_bits(sz))
- {
+ if let (Some(lo), Some(hi)) = (lo.try_to_bits(sz), hi.try_to_bits(sz)) {
// We want to compare ranges numerically, but the order of the bitwise
// representation of signed integers does not match their numeric order.
// Thus, to correct the ordering, we need to shift the range of signed
test_place: &PlaceBuilder<'tcx>,
candidate: &Candidate<'pat, 'tcx>,
switch_ty: Ty<'tcx>,
- options: &mut FxIndexMap<ty::Const<'tcx>, u128>,
+ options: &mut FxIndexMap<ConstantKind<'tcx>, u128>,
) -> bool {
let Some(match_pair) = candidate.match_pairs.iter().find(|mp| mp.place == *test_place) else {
return false;
block: BasicBlock,
make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
source_info: SourceInfo,
- value: ty::Const<'tcx>,
+ value: ConstantKind<'tcx>,
place: Place<'tcx>,
mut ty: Ty<'tcx>,
) {
span_bug!(match_pair.pattern.span, "simplifyable pattern found: {:?}", match_pair.pattern)
}
- fn const_range_contains(&self, range: PatRange<'tcx>, value: ty::Const<'tcx>) -> Option<bool> {
+ fn const_range_contains(
+ &self,
+ range: PatRange<'tcx>,
+ value: ConstantKind<'tcx>,
+ ) -> Option<bool> {
use std::cmp::Ordering::*;
let tcx = self.tcx;
fn values_not_contained_in_range(
&self,
range: PatRange<'tcx>,
- options: &FxIndexMap<ty::Const<'tcx>, u128>,
+ options: &FxIndexMap<ConstantKind<'tcx>, u128>,
) -> Option<bool> {
for &val in options.keys() {
if self.const_range_contains(range, val)? {
.find(|item| item.kind == ty::AssocKind::Fn)
.expect("trait method not found");
- let method_ty = tcx.type_of(item.def_id);
+ let method_ty = tcx.bound_type_of(item.def_id);
let method_ty = method_ty.subst(tcx, substs);
ConstantKind::zero_sized(method_ty)
use crate::build::Builder;
-use rustc_middle::mir;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, Ty};
use rustc_span::{Span, DUMMY_SP};
/// Convenience function for creating a literal operand, one
/// without any user type annotation.
- crate fn literal_operand(
- &mut self,
- span: Span,
- literal: mir::ConstantKind<'tcx>,
- ) -> Operand<'tcx> {
+ crate fn literal_operand(&mut self, span: Span, literal: ConstantKind<'tcx>) -> Operand<'tcx> {
let constant = Box::new(Constant { span, user_ty: None, literal });
Operand::Constant(constant)
}
use crate::build;
use crate::build::expr::as_place::PlaceBuilder;
use crate::build::scope::DropKind;
+use crate::thir::constant::parse_float;
use crate::thir::pattern::pat_from_hir;
+use rustc_ast as ast;
use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_middle::hir::place::PlaceBase as HirPlaceBase;
use rustc_middle::middle::region;
+use rustc_middle::mir::interpret::Allocation;
+use rustc_middle::mir::interpret::{ConstValue, LitToConstError, LitToConstInput, Scalar};
use rustc_middle::mir::*;
use rustc_middle::thir::{BindingMode, Expr, ExprId, LintLevel, PatKind, Thir};
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeckResults};
use rustc_span::symbol::sym;
use rustc_span::Span;
+use rustc_target::abi::Size;
use rustc_target::spec::abi::Abi;
use super::lints;
let ty = if fn_sig.c_variadic && index == fn_sig.inputs().len() {
let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(arg.span));
- tcx.type_of(va_list_did).subst(tcx, &[tcx.lifetimes.re_erased.into()])
+ tcx.bound_type_of(va_list_did).subst(tcx, &[tcx.lifetimes.re_erased.into()])
} else {
fn_sig.inputs()[index]
};
})
}
+#[instrument(skip(tcx, lit_input))]
+pub(crate) fn lit_to_mir_constant<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ lit_input: LitToConstInput<'tcx>,
+) -> Result<ConstantKind<'tcx>, LitToConstError> {
+ let LitToConstInput { lit, ty, neg } = lit_input;
+ let trunc = |n| {
+ let param_ty = ty::ParamEnv::reveal_all().and(ty);
+ let width = tcx.layout_of(param_ty).map_err(|_| LitToConstError::Reported)?.size;
+ trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
+ let result = width.truncate(n);
+ trace!("trunc result: {}", result);
+ Ok(ConstValue::Scalar(Scalar::from_uint(result, width)))
+ };
+
+ let value = match (lit, &ty.kind()) {
+ (ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => {
+ let s = s.as_str();
+ let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes());
+ let allocation = tcx.intern_const_alloc(allocation);
+ ConstValue::Slice { data: allocation, start: 0, end: s.len() }
+ }
+ (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _))
+ if matches!(inner_ty.kind(), ty::Slice(_)) =>
+ {
+ let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]);
+ let allocation = tcx.intern_const_alloc(allocation);
+ ConstValue::Slice { data: allocation, start: 0, end: data.len() }
+ }
+ (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => {
+ let id = tcx.allocate_bytes(data);
+ ConstValue::Scalar(Scalar::from_pointer(id.into(), &tcx))
+ }
+ (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => {
+ ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1)))
+ }
+ (ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => {
+ trunc(if neg { (*n as i128).overflowing_neg().0 as u128 } else { *n })?
+ }
+ (ast::LitKind::Float(n, _), ty::Float(fty)) => {
+ parse_float(*n, *fty, neg).ok_or(LitToConstError::Reported)?
+ }
+ (ast::LitKind::Bool(b), ty::Bool) => ConstValue::Scalar(Scalar::from_bool(*b)),
+ (ast::LitKind::Char(c), ty::Char) => ConstValue::Scalar(Scalar::from_char(*c)),
+ (ast::LitKind::Err(_), _) => return Err(LitToConstError::Reported),
+ _ => return Err(LitToConstError::TypeError),
+ };
+
+ Ok(ConstantKind::Val(value, ty))
+}
+
///////////////////////////////////////////////////////////////////////////
// BuildMir -- walks a crate, looking for fn items and methods to build MIR from
} else {
None
};
- debug!("fn_id {:?} has attrs {:?}", fn_def, tcx.get_attrs(fn_def.did.to_def_id()));
let mut body = builder.finish();
body.spread_arg = spread_arg;
pub fn provide(providers: &mut Providers) {
providers.check_match = thir::pattern::check_match;
providers.lit_to_const = thir::constant::lit_to_const;
+ providers.lit_to_mir_constant = build::lit_to_mir_constant;
providers.mir_built = build::mir_built;
providers.thir_check_unsafety = check_unsafety::thir_check_unsafety;
providers.thir_check_unsafety_for_const_arg = check_unsafety::thir_check_unsafety_for_const_arg;
pattern: self.pattern_from_hir(&arm.pat),
guard: arm.guard.as_ref().map(|g| match g {
hir::Guard::If(ref e) => Guard::If(self.mirror_expr(e)),
- hir::Guard::IfLet(ref pat, ref e) => {
- Guard::IfLet(self.pattern_from_hir(pat), self.mirror_expr(e))
+ hir::Guard::IfLet(ref l) => {
+ Guard::IfLet(self.pattern_from_hir(l.pat), self.mirror_expr(l.init))
}
}),
body: self.mirror_expr(arm.body),
use crate::thir::pattern::pat_from_hir;
use crate::thir::util::UserAnnotatedTyHelpers;
+use rustc_ast as ast;
use rustc_data_structures::steal::Steal;
use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::HirId;
use rustc_hir::Node;
use rustc_middle::middle::region;
+use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
+use rustc_middle::mir::ConstantKind;
use rustc_middle::thir::*;
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::Span;
crate fn thir_body<'tcx>(
}
}
+ #[instrument(skip(self), level = "debug")]
+ crate fn const_eval_literal(
+ &mut self,
+ lit: &'tcx ast::LitKind,
+ ty: Ty<'tcx>,
+ sp: Span,
+ neg: bool,
+ ) -> ConstantKind<'tcx> {
+ match self.tcx.at(sp).lit_to_mir_constant(LitToConstInput { lit, ty, neg }) {
+ Ok(c) => c,
+ Err(LitToConstError::Reported) => {
+ // create a dummy value and continue compiling
+ ConstantKind::Ty(self.tcx.const_error(ty))
+ }
+ Err(LitToConstError::TypeError) => bug!("const_eval_literal: had type error"),
+ }
+ }
+
crate fn pattern_from_hir(&mut self, p: &hir::Pat<'_>) -> Pat<'tcx> {
let p = match self.tcx.hir().get(p.hir_id) {
Node::Pat(p) | Node::Binding(p) => p,
for arm in hir_arms {
// Check the arm for some things unrelated to exhaustiveness.
self.check_patterns(&arm.pat, Refutable);
- if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard {
- self.check_patterns(pat, Refutable);
- let tpat = self.lower_pattern(&mut cx, pat, &mut false);
- self.check_let_reachability(&mut cx, pat.hir_id, tpat, tpat.span());
+ if let Some(hir::Guard::IfLet(ref let_expr)) = arm.guard {
+ self.check_patterns(let_expr.pat, Refutable);
+ let tpat = self.lower_pattern(&mut cx, let_expr.pat, &mut false);
+ self.check_let_reachability(&mut cx, let_expr.pat.hir_id, tpat, tpat.span());
}
}
match parent_node {
hir::Node::Arm(hir::Arm {
- guard: Some(hir::Guard::IfLet(&hir::Pat { hir_id, .. }, _)),
+ guard: Some(hir::Guard::IfLet(&hir::Let { pat: hir::Pat { hir_id, .. }, .. })),
..
- }) if Some(hir_id) == pat_id => {
+ }) if Some(*hir_id) == pat_id => {
return LetSource::IfLetGuard;
}
hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Let(..), span, .. }) => {
use rustc_hir as hir;
use rustc_index::vec::Idx;
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
-use rustc_middle::mir::Field;
+use rustc_middle::mir::{self, Field};
use rustc_middle::thir::{FieldPat, Pat, PatKind};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
#[instrument(level = "debug", skip(self))]
pub(super) fn const_to_pat(
&self,
- cv: ty::Const<'tcx>,
+ cv: mir::ConstantKind<'tcx>,
id: hir::HirId,
span: Span,
mir_structural_match_violation: bool,
ty.is_structural_eq_shallow(self.infcx.tcx)
}
- fn to_pat(&mut self, cv: ty::Const<'tcx>, mir_structural_match_violation: bool) -> Pat<'tcx> {
+ fn to_pat(
+ &mut self,
+ cv: mir::ConstantKind<'tcx>,
+ mir_structural_match_violation: bool,
+ ) -> Pat<'tcx> {
trace!(self.treat_byte_string_as_slice);
// This method is just a wrapper handling a validity check; the heavy lifting is
// performed by the recursive `recur` method, which is not meant to be
fn field_pats(
&self,
- vals: impl Iterator<Item = ty::Const<'tcx>>,
+ vals: impl Iterator<Item = mir::ConstantKind<'tcx>>,
) -> Result<Vec<FieldPat<'tcx>>, FallbackToConstRef> {
vals.enumerate()
.map(|(idx, val)| {
}
// Recursive helper for `to_pat`; invoke that (instead of calling this directly).
+ #[instrument(skip(self), level = "debug")]
fn recur(
&self,
- cv: ty::Const<'tcx>,
+ cv: mir::ConstantKind<'tcx>,
mir_structural_match_violation: bool,
) -> Result<Pat<'tcx>, FallbackToConstRef> {
let id = self.id;
PatKind::Wild
}
ty::Adt(adt_def, substs) if adt_def.is_enum() => {
- let destructured = tcx.destructure_const(param_env.and(cv));
+ let destructured = tcx.destructure_mir_constant(param_env, cv);
PatKind::Variant {
adt_def: *adt_def,
substs,
}
}
ty::Tuple(_) | ty::Adt(_, _) => {
- let destructured = tcx.destructure_const(param_env.and(cv));
+ let destructured = tcx.destructure_mir_constant(param_env, cv);
PatKind::Leaf { subpatterns: self.field_pats(destructured.fields.iter().copied())? }
}
ty::Array(..) => PatKind::Array {
prefix: tcx
- .destructure_const(param_env.and(cv))
+ .destructure_mir_constant(param_env, cv)
.fields
.iter()
.map(|val| self.recur(*val, false))
// arrays.
ty::Array(..) if !self.treat_byte_string_as_slice => {
let old = self.behind_reference.replace(true);
- let array = tcx.deref_const(self.param_env.and(cv));
+ let array = tcx.deref_mir_constant(self.param_env.and(cv));
let val = PatKind::Deref {
subpattern: Pat {
kind: Box::new(PatKind::Array {
prefix: tcx
- .destructure_const(param_env.and(array))
+ .destructure_mir_constant(param_env, array)
.fields
.iter()
.map(|val| self.recur(*val, false))
// pattern.
ty::Slice(elem_ty) => {
let old = self.behind_reference.replace(true);
- let array = tcx.deref_const(self.param_env.and(cv));
+ let array = tcx.deref_mir_constant(self.param_env.and(cv));
let val = PatKind::Deref {
subpattern: Pat {
kind: Box::new(PatKind::Slice {
prefix: tcx
- .destructure_const(param_env.and(array))
+ .destructure_mir_constant(param_env, array)
.fields
.iter()
.map(|val| self.recur(*val, false))
// we fall back to a const pattern. If we do not do this, we may end up with
// a !structural-match constant that is not of reference type, which makes it
// very hard to invoke `PartialEq::eq` on it as a fallback.
- let val = match self.recur(tcx.deref_const(self.param_env.and(cv)), false) {
+ let val = match self.recur(tcx.deref_mir_constant(self.param_env.and(cv)), false) {
Ok(subpattern) => PatKind::Deref { subpattern },
Err(_) => PatKind::Constant { value: cv },
};
use rustc_index::vec::Idx;
use rustc_hir::{HirId, RangeEnd};
-use rustc_middle::mir::Field;
+use rustc_middle::mir::{self, Field};
use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange};
use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef};
}
#[inline]
- fn from_const<'tcx>(
+ fn from_constant<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- value: ty::Const<'tcx>,
+ value: mir::ConstantKind<'tcx>,
) -> Option<IntRange> {
let ty = value.ty();
if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, ty) {
let val = (|| {
- if let ty::ConstKind::Value(ConstValue::Scalar(scalar)) = value.val() {
- // For this specific pattern we can skip a lot of effort and go
- // straight to the result, after doing a bit of checking. (We
- // could remove this branch and just fall through, which
- // is more general but much slower.)
- if let Ok(bits) = scalar.to_bits_or_ptr_internal(target_size).unwrap() {
- return Some(bits);
+ match value {
+ mir::ConstantKind::Val(ConstValue::Scalar(scalar), _) => {
+ // For this specific pattern we can skip a lot of effort and go
+ // straight to the result, after doing a bit of checking. (We
+ // could remove this branch and just fall through, which
+ // is more general but much slower.)
+ if let Ok(Ok(bits)) = scalar.to_bits_or_ptr_internal(target_size) {
+ return Some(bits);
+ } else {
+ return None;
+ }
}
+ mir::ConstantKind::Ty(c) => match c.val() {
+ ty::ConstKind::Value(_) => bug!(
+ "encountered ConstValue in mir::ConstantKind::Ty, whereas this is expected to be in ConstantKind::Val"
+ ),
+ _ => {}
+ },
+ _ => {}
}
+
// This is a more general form of the previous case.
value.try_eval_bits(tcx, param_env, ty)
})()?;
let (lo, hi) = (lo ^ bias, hi ^ bias);
let env = ty::ParamEnv::empty().and(ty);
- let lo_const = ty::Const::from_bits(tcx, lo, env);
- let hi_const = ty::Const::from_bits(tcx, hi, env);
+ let lo_const = mir::ConstantKind::from_bits(tcx, lo, env);
+ let hi_const = mir::ConstantKind::from_bits(tcx, hi, env);
let kind = if lo == hi {
PatKind::Constant { value: lo_const }
/// Ranges of integer literal values (`2`, `2..=5` or `2..5`).
IntRange(IntRange),
/// Ranges of floating-point literal values (`2.0..=5.2`).
- FloatRange(ty::Const<'tcx>, ty::Const<'tcx>, RangeEnd),
+ FloatRange(mir::ConstantKind<'tcx>, mir::ConstantKind<'tcx>, RangeEnd),
/// String literals. Strings are not quite the same as `&[u8]` so we treat them separately.
- Str(ty::Const<'tcx>),
+ Str(mir::ConstantKind<'tcx>),
/// Array and slice patterns.
Slice(Slice),
/// Constants that must not be matched structurally. They are treated as black
}
}
PatKind::Constant { value } => {
- if let Some(int_range) = IntRange::from_const(cx.tcx, cx.param_env, *value) {
+ if let Some(int_range) = IntRange::from_constant(cx.tcx, cx.param_env, *value) {
ctor = IntRange(int_range);
fields = Fields::empty();
} else {
use rustc_index::vec::Idx;
use rustc_middle::mir::interpret::{get_slice_bytes, ConstValue};
use rustc_middle::mir::interpret::{ErrorHandled, LitToConstError, LitToConstInput};
-use rustc_middle::mir::UserTypeProjection;
+use rustc_middle::mir::{self, UserTypeProjection};
use rustc_middle::mir::{BorrowKind, Field, Mutability};
use rustc_middle::thir::{Ascription, BindingMode, FieldPat, Pat, PatKind, PatRange, PatTyProj};
use rustc_middle::ty::subst::{GenericArg, SubstsRef};
fn lower_pattern_range(
&mut self,
ty: Ty<'tcx>,
- lo: ty::Const<'tcx>,
- hi: ty::Const<'tcx>,
+ lo: mir::ConstantKind<'tcx>,
+ hi: mir::ConstantKind<'tcx>,
end: RangeEnd,
span: Span,
) -> PatKind<'tcx> {
ty: Ty<'tcx>,
lo: Option<&PatKind<'tcx>>,
hi: Option<&PatKind<'tcx>>,
- ) -> Option<(ty::Const<'tcx>, ty::Const<'tcx>)> {
+ ) -> Option<(mir::ConstantKind<'tcx>, mir::ConstantKind<'tcx>)> {
match (lo, hi) {
(Some(PatKind::Constant { value: lo }), Some(PatKind::Constant { value: hi })) => {
Some((*lo, *hi))
}
(Some(PatKind::Constant { value: lo }), None) => {
- Some((*lo, ty.numeric_max_val(self.tcx)?))
+ let hi = ty.numeric_max_val(self.tcx)?;
+ Some((*lo, hi.into()))
}
(None, Some(PatKind::Constant { value: hi })) => {
- Some((ty.numeric_min_val(self.tcx)?, *hi))
+ let lo = ty.numeric_min_val(self.tcx)?;
+ Some((lo.into(), *hi))
}
_ => None,
}
/// Takes a HIR Path. If the path is a constant, evaluates it and feeds
/// it to `const_to_pat`. Any other path (like enum variants without fields)
/// is converted to the corresponding pattern via `lower_variant_or_leaf`.
+ #[instrument(skip(self), level = "debug")]
fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) -> Pat<'tcx> {
let ty = self.typeck_results.node_type(id);
let res = self.typeck_results.qpath_res(qpath, id);
debug!("mir_structural_match_violation({:?}) -> {}", qpath, mir_structural_match_violation);
match self.tcx.const_eval_instance(param_env_reveal_all, instance, Some(span)) {
- Ok(value) => {
- let const_ = ty::Const::from_value(self.tcx, value, ty);
+ Ok(literal) => {
+ let const_ = mir::ConstantKind::Val(literal, ty);
let pattern = self.const_to_pat(const_, id, span, mir_structural_match_violation);
if !is_associated_const {
span: Span,
) -> PatKind<'tcx> {
let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id);
- let value = ty::Const::from_inline_const(self.tcx, anon_const_def_id);
+ let value = mir::ConstantKind::from_inline_const(self.tcx, anon_const_def_id);
// Evaluate early like we do in `lower_path`.
let value = value.eval(self.tcx, self.param_env);
- match value.val() {
- ConstKind::Param(_) => {
- self.errors.push(PatternError::ConstParamInPattern(span));
- return PatKind::Wild;
- }
- ConstKind::Unevaluated(_) => {
- // If we land here it means the const can't be evaluated because it's `TooGeneric`.
- self.tcx.sess.span_err(span, "constant pattern depends on a generic parameter");
- return PatKind::Wild;
+ match value {
+ mir::ConstantKind::Ty(c) => {
+ match c.val() {
+ ConstKind::Param(_) => {
+ self.errors.push(PatternError::ConstParamInPattern(span));
+ return PatKind::Wild;
+ }
+ ConstKind::Unevaluated(_) => {
+ // If we land here it means the const can't be evaluated because it's `TooGeneric`.
+ self.tcx
+ .sess
+ .span_err(span, "constant pattern depends on a generic parameter");
+ return PatKind::Wild;
+ }
+ _ => bug!("Expected either ConstKind::Param or ConstKind::Unevaluated"),
+ }
}
- _ => (),
+ mir::ConstantKind::Val(_, _) => *self.const_to_pat(value, id, span, false).kind,
}
-
- *self.const_to_pat(value, id, span, false).kind
}
/// Converts literals, paths and negation of literals to patterns.
let lit_input =
LitToConstInput { lit: &lit.node, ty: self.typeck_results.expr_ty(expr), neg };
- match self.tcx.at(expr.span).lit_to_const(lit_input) {
+ match self.tcx.at(expr.span).lit_to_mir_constant(lit_input) {
Ok(constant) => *self.const_to_pat(constant, expr.hir_id, lit.span, false).kind,
Err(LitToConstError::Reported) => PatKind::Wild,
Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"),
#[instrument(skip(tcx), level = "debug")]
crate fn compare_const_vals<'tcx>(
tcx: TyCtxt<'tcx>,
- a: ty::Const<'tcx>,
- b: ty::Const<'tcx>,
+ a: mir::ConstantKind<'tcx>,
+ b: mir::ConstantKind<'tcx>,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
) -> Option<Ordering> {
return fallback();
}
- // Early return for equal constants (so e.g. references to ZSTs can be compared, even if they
- // are just integer addresses).
- if a.val() == b.val() {
+ if a == b {
return from_bool(true);
}
}
if let ty::Str = ty.kind() && let (
- ty::ConstKind::Value(a_val @ ConstValue::Slice { .. }),
- ty::ConstKind::Value(b_val @ ConstValue::Slice { .. }),
- ) = (a.val(), b.val())
+ Some(a_val @ ConstValue::Slice { .. }),
+ Some(b_val @ ConstValue::Slice { .. }),
+ ) = (a.try_val(), b.try_val())
{
let a_bytes = get_slice_bytes(&tcx, a_val);
let b_bytes = get_slice_bytes(&tcx, b_val);
impl RustcMirAttrs {
fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Result<Self, ()> {
- let attrs = tcx.get_attrs(def_id);
-
let mut result = Ok(());
let mut ret = RustcMirAttrs::default();
- let rustc_mir_attrs = attrs
- .iter()
- .filter(|attr| attr.has_name(sym::rustc_mir))
+ let rustc_mir_attrs = tcx
+ .get_attrs(def_id, sym::rustc_mir)
.flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));
for attr in rustc_mir_attrs {
}
fn target(&self, edge: &Self::Edge) -> Self::Node {
- self.body[edge.source].terminator().successors().nth(edge.index).copied().unwrap()
+ self.body[edge.source].terminator().successors().nth(edge.index).unwrap()
}
}
#[macro_use]
extern crate rustc_middle;
-use rustc_ast::{self as ast, MetaItem};
-use rustc_middle::ty;
-use rustc_session::Session;
+use rustc_ast::MetaItem;
+use rustc_hir::def_id::DefId;
+use rustc_middle::ty::{self, TyCtxt};
use rustc_span::symbol::{sym, Symbol};
pub use self::drop_flag_effects::{
pub param_env: ty::ParamEnv<'tcx>,
}
-pub fn has_rustc_mir_with(
- _sess: &Session,
- attrs: &[ast::Attribute],
- name: Symbol,
-) -> Option<MetaItem> {
- for attr in attrs {
- if attr.has_name(sym::rustc_mir) {
- let items = attr.meta_item_list();
- for item in items.iter().flat_map(|l| l.iter()) {
- match item.meta_item() {
- Some(mi) if mi.has_name(name) => return Some(mi.clone()),
- _ => continue,
- }
+pub fn has_rustc_mir_with(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Option<MetaItem> {
+ for attr in tcx.get_attrs(def_id, sym::rustc_mir) {
+ let items = attr.meta_item_list();
+ for item in items.iter().flat_map(|l| l.iter()) {
+ match item.meta_item() {
+ Some(mi) if mi.has_name(name) => return Some(mi.clone()),
+ _ => continue,
}
}
}
-use rustc_ast::ast;
use rustc_span::symbol::sym;
use rustc_span::Span;
-use rustc_target::spec::abi::Abi;
use rustc_index::bit_set::BitSet;
use rustc_middle::mir::MirPass;
debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
}
- let attributes = tcx.get_attrs(def_id);
let param_env = tcx.param_env(def_id);
let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap();
let mdpe = MoveDataParamEnv { move_data, param_env };
- let sess = &tcx.sess;
- if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_maybe_init).is_some() {
+ if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() {
let flow_inits = MaybeInitializedPlaces::new(tcx, body, &mdpe)
.into_engine(tcx, body)
.iterate_to_fixpoint();
- sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_inits);
+ sanity_check_via_rustc_peek(tcx, body, &flow_inits);
}
- if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_maybe_uninit).is_some() {
+ if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_uninit).is_some() {
let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &mdpe)
.into_engine(tcx, body)
.iterate_to_fixpoint();
- sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_uninits);
+ sanity_check_via_rustc_peek(tcx, body, &flow_uninits);
}
- if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_definite_init).is_some() {
+ if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_definite_init).is_some() {
let flow_def_inits = DefinitelyInitializedPlaces::new(tcx, body, &mdpe)
.into_engine(tcx, body)
.iterate_to_fixpoint();
- sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_def_inits);
+ sanity_check_via_rustc_peek(tcx, body, &flow_def_inits);
}
- if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_liveness).is_some() {
+ if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() {
let flow_liveness = MaybeLiveLocals.into_engine(tcx, body).iterate_to_fixpoint();
- sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_liveness);
+ sanity_check_via_rustc_peek(tcx, body, &flow_liveness);
}
- if has_rustc_mir_with(sess, &attributes, sym::stop_after_dataflow).is_some() {
+ if has_rustc_mir_with(tcx, def_id, sym::stop_after_dataflow).is_some() {
tcx.sess.fatal("stop_after_dataflow ended compilation");
}
}
pub fn sanity_check_via_rustc_peek<'tcx, A>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
- _attributes: &[ast::Attribute],
results: &Results<'tcx, A>,
) where
A: RustcPeekAt<'tcx>,
&terminator.kind
{
if let ty::FnDef(def_id, substs) = *func.literal.ty().kind() {
- let sig = tcx.fn_sig(def_id);
let name = tcx.item_name(def_id);
- if sig.abi() != Abi::RustIntrinsic || name != sym::rustc_peek {
+ if !tcx.is_intrinsic(def_id) || name != sym::rustc_peek {
return None;
}
};
use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
use rustc_middle::ty::subst::{InternalSubsts, Subst};
-use rustc_middle::ty::{self, ConstKind, Instance, ParamEnv, Ty, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{
+ self, ConstKind, EarlyBinder, Instance, ParamEnv, Ty, TyCtxt, TypeFoldable,
+};
use rustc_span::{def_id::DefId, Span};
use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout};
use rustc_target::spec::abi::Abi;
use rustc_const_eval::interpret::{
self, compile_time_machine, AllocId, ConstAllocation, ConstValue, CtfeValidationMode, Frame,
ImmTy, Immediate, InterpCx, InterpResult, LocalState, LocalValue, MemPlace, MemoryKind, OpTy,
- Operand as InterpOperand, PlaceTy, Scalar, ScalarMaybeUninit, StackPopCleanup, StackPopUnwind,
+ Operand as InterpOperand, PlaceTy, Pointer, Scalar, ScalarMaybeUninit, StackPopCleanup,
+ StackPopUnwind,
};
/// The maximum number of bytes that we'll allocate space for a local or the return value.
Ok(())
}
+ #[inline(always)]
+ fn expose_ptr(
+ _ecx: &mut InterpCx<'mir, 'tcx, Self>,
+ _ptr: Pointer<AllocId>,
+ ) -> InterpResult<'tcx> {
+ throw_machine_stop_str!("exposing pointers isn't supported in ConstProp")
+ }
+
#[inline(always)]
fn init_frame_extra(
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
);
let ret = ecx
- .layout_of(body.return_ty().subst(tcx, substs))
+ .layout_of(EarlyBinder(body.return_ty()).subst(tcx, substs))
.ok()
// Don't bother allocating memory for ZST types which have no values
// or for large values.
use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
use rustc_middle::ty::subst::{InternalSubsts, Subst};
use rustc_middle::ty::{
- self, ConstInt, ConstKind, Instance, ParamEnv, ScalarInt, Ty, TyCtxt, TypeFoldable,
+ self, ConstInt, ConstKind, EarlyBinder, Instance, ParamEnv, ScalarInt, Ty, TyCtxt, TypeFoldable,
};
use rustc_session::lint;
use rustc_span::{def_id::DefId, Span};
use rustc_const_eval::const_eval::ConstEvalErr;
use rustc_const_eval::interpret::{
self, compile_time_machine, AllocId, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult,
- LocalState, LocalValue, MemPlace, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Scalar,
- ScalarMaybeUninit, StackPopCleanup, StackPopUnwind,
+ LocalState, LocalValue, MemPlace, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Pointer,
+ Scalar, ScalarMaybeUninit, StackPopCleanup, StackPopUnwind,
};
/// The maximum number of bytes that we'll allocate space for a local or the return value.
Ok(())
}
+ #[inline(always)]
+ fn expose_ptr(
+ _ecx: &mut InterpCx<'mir, 'tcx, Self>,
+ _ptr: Pointer<AllocId>,
+ ) -> InterpResult<'tcx> {
+ throw_machine_stop_str!("exposing pointers isn't supported in ConstProp")
+ }
+
#[inline(always)]
fn init_frame_extra(
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
);
let ret = ecx
- .layout_of(body.return_ty().subst(tcx, substs))
+ .layout_of(EarlyBinder(body.return_ty()).subst(tcx, substs))
.ok()
// Don't bother allocating memory for ZST types which have no values
// or for large values.
edge_labels.retain(|label| label != "unreachable");
let edge_counters = from_terminator
.successors()
- .map(|&successor_bb| graphviz_data.get_edge_counter(from_bcb, successor_bb));
+ .map(|successor_bb| graphviz_data.get_edge_counter(from_bcb, successor_bb));
iter::zip(&edge_labels, edge_counters)
.map(|(label, some_counter)| {
if let Some(counter) = some_counter {
body: &'tcx &'a mir::Body<'tcx>,
term_kind: &'tcx TerminatorKind<'tcx>,
) -> Box<dyn Iterator<Item = BasicBlock> + 'a> {
- let mut successors = term_kind.successors();
Box::new(
match &term_kind {
// SwitchInt successors are never unwind, and all of them should be traversed.
- TerminatorKind::SwitchInt { .. } => successors,
+ TerminatorKind::SwitchInt { ref targets, .. } => {
+ None.into_iter().chain(targets.all_targets().into_iter().copied())
+ }
// For all other kinds, return only the first successor, if any, and ignore unwinds.
// NOTE: `chain(&[])` is required to coerce the `option::iter` (from
// `next().into_iter()`) into the `mir::Successors` aliased type.
- _ => successors.next().into_iter().chain(&[]),
+ _ => term_kind.successors().next().into_iter().chain((&[]).into_iter().copied()),
}
- .copied()
.filter(move |&successor| body[successor].terminator().kind != TerminatorKind::Unreachable),
)
}
use rustc_middle::ty::{
self,
subst::{GenericArgKind, Subst, SubstsRef},
- PredicateKind, Ty, TyCtxt,
+ EarlyBinder, PredicateKind, Ty, TyCtxt,
};
use rustc_session::lint::builtin::FUNCTION_ITEM_REFERENCES;
use rustc_span::{symbol::sym, Span};
// If the inner type matches the type bound by `Pointer`
if inner_ty == bound_ty {
// Do a substitution using the parameters from the callsite
- let subst_ty = inner_ty.subst(self.tcx, substs_ref);
+ let subst_ty = EarlyBinder(inner_ty).subst(self.tcx, substs_ref);
if let Some((fn_id, fn_substs)) =
FunctionItemRefChecker::is_fn_ref(subst_ty)
{
assert_eq!(self.state_adt_ref.variant(idx).fields.len(), 1);
let ty = self
.tcx
- .type_of(self.state_adt_ref.variant(idx).fields[0].did)
+ .bound_type_of(self.state_adt_ref.variant(idx).fields[0].did)
.subst(self.tcx, self.state_substs);
expand_aggregate(
Place::return_place(),
return None;
}
- let fn_sig = self.tcx.fn_sig(def_id).subst(self.tcx, substs);
+ let fn_sig = self.tcx.bound_fn_sig(def_id).subst(self.tcx, substs);
return Some(CallSite {
callee,
}
}
// Don't give intrinsics the extra penalty for calls
- let f = tcx.fn_sig(def_id);
- if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic {
+ if tcx.is_intrinsic(def_id) {
cost += INSTR_COST;
} else {
cost += CALL_PENALTY;
}
if !is_drop {
- for &succ in term.successors() {
+ for succ in term.successors() {
work_list.push(succ);
}
}
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::sso::SsoHashSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::mir::TerminatorKind;
) -> bool {
trace!(%caller);
for &(callee, substs) in tcx.mir_inliner_callees(caller.def) {
- let substs = caller.subst_mir_and_normalize_erasing_regions(tcx, param_env, substs);
+ let Ok(substs) = caller.try_subst_mir_and_normalize_erasing_regions(tcx, param_env, substs) else {
+ trace!(?caller, ?param_env, ?substs, "cannot normalize, skipping");
+ continue;
+ };
let Some(callee) = ty::Instance::resolve(tcx, param_env, callee, substs).unwrap() else {
trace!(?callee, "cannot resolve, skipping");
continue;
// Functions from other crates and MIR shims
_ => tcx.instance_mir(instance),
};
- let mut calls = SsoHashSet::new();
+ let mut calls = FxIndexSet::default();
for bb_data in body.basic_blocks() {
let terminator = bb_data.terminator();
if let TerminatorKind::Call { func, .. } = &terminator.kind {
intravisit::walk_struct_def(self, v)
}
}
- tcx.hir().visit_all_item_likes(&mut GatherCtors { tcx, set: &mut set }.as_deep_visitor());
+ tcx.hir().deep_visit_all_item_likes(&mut GatherCtors { tcx, set: &mut set });
set
}
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
-use rustc_target::spec::abi::Abi;
pub struct LowerIntrinsics;
func_ty: Ty<'tcx>,
) -> Option<(Symbol, SubstsRef<'tcx>)> {
if let ty::FnDef(def_id, substs) = *func_ty.kind() {
- let fn_sig = func_ty.fn_sig(tcx);
- if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = fn_sig.abi() {
+ if tcx.is_intrinsic(def_id) {
return Some((tcx.item_name(def_id), substs));
}
}
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. } => {
- terminator.successors().all(|&succ| nop_landing_pads.contains(succ))
+ terminator.successors().all(|succ| nop_landing_pads.contains(succ))
}
TerminatorKind::GeneratorDrop
| TerminatorKind::Yield { .. }
use rustc_middle::mir::*;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::subst::{InternalSubsts, Subst};
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt};
use rustc_target::abi::VariantIdx;
use rustc_index::vec::{Idx, IndexVec};
// of this function. Is this intentional?
if let Some(ty::Generator(gen_def_id, substs, _)) = ty.map(Ty::kind) {
let body = tcx.optimized_mir(*gen_def_id).generator_drop().unwrap();
- let body = body.clone().subst(tcx, substs);
+ let body = EarlyBinder(body.clone()).subst(tcx, substs);
debug!("make_shim({:?}) = {:?}", instance, body);
return body;
}
} else {
InternalSubsts::identity_for_item(tcx, def_id)
};
- let sig = tcx.fn_sig(def_id).subst(tcx, substs);
+ let sig = tcx.bound_fn_sig(def_id).subst(tcx, substs);
let sig = tcx.erase_late_bound_regions(sig);
let span = tcx.def_span(def_id);
// otherwise going to be TySelf and we can't index
// or access fields of a Place of type TySelf.
let substs = tcx.mk_substs_trait(self_ty, &[]);
- let sig = tcx.fn_sig(def_id).subst(tcx, substs);
+ let sig = tcx.bound_fn_sig(def_id).subst(tcx, substs);
let sig = tcx.erase_late_bound_regions(sig);
let span = tcx.def_span(def_id);
assert_eq!(sig_substs.is_some(), !instance.has_polymorphic_mir_body());
if let Some(sig_substs) = sig_substs {
- sig = sig.subst(tcx, sig_substs);
+ sig = EarlyBinder(sig).subst(tcx, sig_substs);
}
if let CallKind::Indirect(fnty) = call_kind {
for (_, data) in traversal::preorder(body) {
if let Some(ref term) = data.terminator {
- for &tgt in term.successors() {
+ for tgt in term.successors() {
pred_count[tgt] += 1;
}
}
};
let first_succ = {
- if let Some(&first_succ) = terminator.successors().next() {
- if terminator.successors().all(|s| *s == first_succ) {
+ if let Some(first_succ) = terminator.successors().next() {
+ if terminator.successors().all(|s| s == first_succ) {
let count = terminator.successors().count();
self.pred_count[first_succ] -= (count - 1) as u32;
first_succ
unused_parameters: &FiniteBitSet<u32>,
) {
let base_def_id = tcx.typeck_root_def_id(def_id);
- if !tcx.get_attrs(base_def_id).iter().any(|a| a.has_name(sym::rustc_polymorphize_error)) {
+ if !tcx.has_attr(base_def_id, sym::rustc_polymorphize_error) {
return;
}
use rustc_errors::{
Applicability, DiagnosticBuilder, DiagnosticMessage, Handler, MultiSpan, PResult,
};
-use rustc_macros::SessionDiagnostic;
+use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, Ident};
use rustc_span::{Span, SpanSnippetError, DUMMY_SP};
pub span: Span,
}
+#[derive(SessionDiagnostic)]
+#[error(code = "E0178", slug = "parser-maybe-recover-from-bad-type-plus")]
+struct BadTypePlus<'a> {
+ pub ty: String,
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub sub: BadTypePlusSub<'a>,
+}
+
+#[derive(SessionSubdiagnostic, Clone, Copy)]
+pub enum BadTypePlusSub<'a> {
+ #[suggestion(
+ slug = "parser-add-paren",
+ code = "{sum_with_parens}",
+ applicability = "machine-applicable"
+ )]
+ AddParen {
+ sum_with_parens: &'a str,
+ #[primary_span]
+ span: Span,
+ },
+ #[label(slug = "parser-forgot-paren")]
+ ForgotParen {
+ #[primary_span]
+ span: Span,
+ },
+ #[label(slug = "parser-expect-path")]
+ ExpectPath {
+ #[primary_span]
+ span: Span,
+ },
+}
+
// SnapshotParser is used to create a snapshot of the parser
// without causing duplicate errors being emitted when the `Parser`
// is dropped.
let bounds = self.parse_generic_bounds(None)?;
let sum_span = ty.span.to(self.prev_token.span);
- let mut err = struct_span_err!(
- self.sess.span_diagnostic,
- sum_span,
- E0178,
- "expected a path on the left-hand side of `+`, not `{}`",
- pprust::ty_to_string(ty)
- );
+ let sum_with_parens: String;
- match ty.kind {
+ let sub = match ty.kind {
TyKind::Rptr(ref lifetime, ref mut_ty) => {
- let sum_with_parens = pprust::to_string(|s| {
+ sum_with_parens = pprust::to_string(|s| {
s.s.word("&");
s.print_opt_lifetime(lifetime);
s.print_mutability(mut_ty.mutbl, false);
s.print_type_bounds(" +", &bounds);
s.pclose()
});
- err.span_suggestion(
- sum_span,
- "try adding parentheses",
- sum_with_parens,
- Applicability::MachineApplicable,
- );
- }
- TyKind::Ptr(..) | TyKind::BareFn(..) => {
- err.span_label(sum_span, "perhaps you forgot parentheses?");
- }
- _ => {
- err.span_label(sum_span, "expected a path");
+
+ BadTypePlusSub::AddParen { sum_with_parens: &sum_with_parens, span: sum_span }
}
- }
- err.emit();
+ TyKind::Ptr(..) | TyKind::BareFn(..) => BadTypePlusSub::ForgotParen { span: sum_span },
+ _ => BadTypePlusSub::ExpectPath { span: sum_span },
+ };
+
+ self.sess.emit_err(BadTypePlus { ty: pprust::ty_to_string(ty), span: sum_span, sub });
+
Ok(())
}
use rustc_session::parse::feature_err;
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::{Span, DUMMY_SP};
+use rustc_target::spec::abi::Abi;
use std::collections::hash_map::Entry;
pub(crate) fn target_from_impl_item<'tcx>(
/// Checks if `#[link]` is applied to an item other than a foreign module.
fn check_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
- match target {
- Target::ForeignMod => {}
- _ => {
- self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
- let mut diag = lint.build("attribute should be applied to an `extern` block");
- diag.warn(
- "this was previously accepted by the compiler but is \
- being phased out; it will become a hard error in \
- a future release!",
- );
+ if target == Target::ForeignMod
+ && let hir::Node::Item(item) = self.tcx.hir().get(hir_id)
+ && let Item { kind: ItemKind::ForeignMod { abi, .. }, .. } = item
+ && !matches!(abi, Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic)
+ {
+ return;
+ }
- diag.span_label(span, "not an `extern` block");
- diag.emit();
- });
+ self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+ let mut diag =
+ lint.build("attribute should be applied to an `extern` block with non-Rust ABI");
+ diag.warn(
+ "this was previously accepted by the compiler but is \
+ being phased out; it will become a hard error in \
+ a future release!",
+ );
+ if target != Target::ForeignMod {
+ diag.span_label(span, "not an `extern` block");
}
- }
+ diag.emit();
+ });
}
/// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
let check_attr_visitor = &mut CheckAttrVisitor { tcx };
- tcx.hir().visit_item_likes_in_module(module_def_id, &mut check_attr_visitor.as_deep_visitor());
+ tcx.hir().deep_visit_item_likes_in_module(module_def_id, check_attr_visitor);
if module_def_id.is_top_level_module() {
check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None);
check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs());
fn check_mod_const_bodies(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
let mut vis = CheckConstVisitor::new(tcx);
- tcx.hir().visit_item_likes_in_module(module_def_id, &mut vis.as_deep_visitor());
- tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckConstTraitVisitor::new(tcx));
+ tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut vis);
}
pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers { check_mod_const_bodies, ..*providers };
}
-struct CheckConstTraitVisitor<'tcx> {
- tcx: TyCtxt<'tcx>,
-}
-
-impl<'tcx> CheckConstTraitVisitor<'tcx> {
- fn new(tcx: TyCtxt<'tcx>) -> Self {
- CheckConstTraitVisitor { tcx }
- }
-}
-
-impl<'tcx> hir::itemlikevisit::ItemLikeVisitor<'tcx> for CheckConstTraitVisitor<'tcx> {
- /// check for const trait impls, and errors if the impl uses provided/default functions
- /// of the trait being implemented; as those provided functions can be non-const.
- fn visit_item<'hir>(&mut self, item: &'hir hir::Item<'hir>) {
- let _: Option<_> = try {
- if let hir::ItemKind::Impl(ref imp) = item.kind && let hir::Constness::Const = imp.constness {
- let trait_def_id = imp.of_trait.as_ref()?.trait_def_id()?;
- let ancestors = self
- .tcx
- .trait_def(trait_def_id)
- .ancestors(self.tcx, item.def_id.to_def_id())
- .ok()?;
- let mut to_implement = Vec::new();
-
- for trait_item in self.tcx.associated_items(trait_def_id).in_definition_order()
+fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
+ let _: Option<_> = try {
+ if let hir::ItemKind::Impl(ref imp) = item.kind && let hir::Constness::Const = imp.constness {
+ let trait_def_id = imp.of_trait.as_ref()?.trait_def_id()?;
+ let ancestors = tcx
+ .trait_def(trait_def_id)
+ .ancestors(tcx, item.def_id.to_def_id())
+ .ok()?;
+ let mut to_implement = Vec::new();
+
+ for trait_item in tcx.associated_items(trait_def_id).in_definition_order()
+ {
+ if let ty::AssocItem {
+ kind: ty::AssocKind::Fn,
+ defaultness,
+ def_id: trait_item_id,
+ ..
+ } = *trait_item
+ {
+ // we can ignore functions that do not have default bodies:
+ // if those are unimplemented it will be caught by typeck.
+ if !defaultness.has_value()
+ || tcx
+ .has_attr(trait_item_id, sym::default_method_body_is_const)
{
- if let ty::AssocItem {
- kind: ty::AssocKind::Fn,
- defaultness,
- def_id: trait_item_id,
- ..
- } = *trait_item
- {
- // we can ignore functions that do not have default bodies:
- // if those are unimplemented it will be caught by typeck.
- if !defaultness.has_value()
- || self
- .tcx
- .has_attr(trait_item_id, sym::default_method_body_is_const)
- {
- continue;
- }
-
- let is_implemented = ancestors
- .leaf_def(self.tcx, trait_item_id)
- .map(|node_item| !node_item.defining_node.is_from_trait())
- .unwrap_or(false);
-
- if !is_implemented {
- to_implement.push(self.tcx.item_name(trait_item_id).to_string());
- }
- }
+ continue;
}
- // all nonconst trait functions (not marked with #[default_method_body_is_const])
- // must be implemented
- if !to_implement.is_empty() {
- self.tcx
- .sess
- .struct_span_err(
- item.span,
- "const trait implementations may not use non-const default functions",
- )
- .note(&format!("`{}` not implemented", to_implement.join("`, `")))
- .emit();
+ let is_implemented = ancestors
+ .leaf_def(tcx, trait_item_id)
+ .map(|node_item| !node_item.defining_node.is_from_trait())
+ .unwrap_or(false);
+
+ if !is_implemented {
+ to_implement.push(trait_item_id);
}
+ }
}
- };
- }
-
- fn visit_trait_item<'hir>(&mut self, _: &'hir hir::TraitItem<'hir>) {}
-
- fn visit_impl_item<'hir>(&mut self, _: &'hir hir::ImplItem<'hir>) {}
- fn visit_foreign_item<'hir>(&mut self, _: &'hir hir::ForeignItem<'hir>) {}
+ // all nonconst trait functions (not marked with #[default_method_body_is_const])
+ // must be implemented
+ if !to_implement.is_empty() {
+ let not_implemented = to_implement
+ .into_iter()
+ .map(|did| tcx.item_name(did).to_string())
+ .collect::<Vec<_>>()
+ .join("`, `");
+ tcx
+ .sess
+ .struct_span_err(
+ item.span,
+ "const trait implementations may not use non-const default functions",
+ )
+ .note(&format!("`{}` not implemented", not_implemented))
+ .emit();
+ }
+ }
+ };
}
#[derive(Copy, Clone)]
// If `def_id` is `None`, we don't need to consider stability attributes.
let def_id = match def_id {
- Some(x) => x.to_def_id(),
+ Some(x) => x,
None => return true,
};
// If this crate is not using stability attributes, or this function is not claiming to be a
// stable `const fn`, that is all that is required.
- if !tcx.features().staged_api || tcx.has_attr(def_id, sym::rustc_const_unstable) {
+ if !tcx.features().staged_api
+ || tcx.has_attr(def_id.to_def_id(), sym::rustc_const_unstable)
+ {
return true;
}
// However, we cannot allow stable `const fn`s to use unstable features without an explicit
// opt-in via `rustc_allow_const_fn_unstable`.
- attr::rustc_allow_const_fn_unstable(&tcx.sess, &tcx.get_attrs(def_id))
- .any(|name| name == feature_gate)
+ let attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(def_id));
+ attr::rustc_allow_const_fn_unstable(&tcx.sess, attrs).any(|name| name == feature_gate)
};
match required_gates {
self.tcx.hir()
}
+ fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
+ intravisit::walk_item(self, item);
+ check_item(self.tcx, item);
+ }
+
fn visit_anon_const(&mut self, anon: &'tcx hir::AnonConst) {
let kind = Some(hir::ConstContext::Const);
self.recurse_into(kind, None, |this| intravisit::walk_anon_const(this, anon));
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::{Node, PatKind, TyKind};
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
tcx.lint_level_at_node(lint::builtin::DEAD_CODE, id).0 == lint::Allow
}
-// This visitor seeds items that
+// These check_* functions seeds items that
// 1) We want to explicitly consider as live:
// * Item annotated with #[allow(dead_code)]
// - This is done so that if we want to suppress warnings for a
// or
// 2) We are not sure to be live or not
// * Implementations of traits and trait methods
-struct LifeSeeder<'tcx> {
- worklist: Vec<LocalDefId>,
+fn check_item<'tcx>(
tcx: TyCtxt<'tcx>,
- // see `MarkSymbolVisitor::struct_constructors`
- struct_constructors: FxHashMap<LocalDefId, LocalDefId>,
-}
-
-impl<'v, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'tcx> {
- fn visit_item(&mut self, item: &hir::Item<'_>) {
- let allow_dead_code = has_allow_dead_code_or_lang_attr(self.tcx, item.hir_id());
- if allow_dead_code {
- self.worklist.push(item.def_id);
- }
- match item.kind {
- hir::ItemKind::Enum(ref enum_def, _) => {
- let hir = self.tcx.hir();
+ worklist: &mut Vec<LocalDefId>,
+ struct_constructors: &mut FxHashMap<LocalDefId, LocalDefId>,
+ id: hir::ItemId,
+) {
+ let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, id.hir_id());
+ if allow_dead_code {
+ worklist.push(id.def_id);
+ }
+
+ match tcx.def_kind(id.def_id) {
+ DefKind::Enum => {
+ let item = tcx.hir().item(id);
+ if let hir::ItemKind::Enum(ref enum_def, _) = item.kind {
+ let hir = tcx.hir();
if allow_dead_code {
- self.worklist.extend(
+ worklist.extend(
enum_def.variants.iter().map(|variant| hir.local_def_id(variant.id)),
);
}
for variant in enum_def.variants {
if let Some(ctor_hir_id) = variant.data.ctor_hir_id() {
- self.struct_constructors
+ struct_constructors
.insert(hir.local_def_id(ctor_hir_id), hir.local_def_id(variant.id));
}
}
}
- hir::ItemKind::Impl(hir::Impl { ref of_trait, items, .. }) => {
- if of_trait.is_some() {
- self.worklist.push(item.def_id);
- }
- for impl_item_ref in *items {
- let impl_item = self.tcx.hir().impl_item(impl_item_ref.id);
- if of_trait.is_some()
- || has_allow_dead_code_or_lang_attr(self.tcx, impl_item.hir_id())
- {
- self.worklist.push(impl_item_ref.id.def_id);
- }
+ }
+ DefKind::Impl => {
+ let of_trait = tcx.impl_trait_ref(id.def_id);
+
+ if of_trait.is_some() {
+ worklist.push(id.def_id);
+ }
+
+ // get DefIds from another query
+ let local_def_ids = tcx
+ .associated_item_def_ids(id.def_id)
+ .iter()
+ .filter_map(|def_id| def_id.as_local());
+
+ // And we access the Map here to get HirId from LocalDefId
+ for id in local_def_ids {
+ if of_trait.is_some()
+ || has_allow_dead_code_or_lang_attr(tcx, tcx.hir().local_def_id_to_hir_id(id))
+ {
+ worklist.push(id);
}
}
- hir::ItemKind::Struct(ref variant_data, _) => {
+ }
+ DefKind::Struct => {
+ let item = tcx.hir().item(id);
+ if let hir::ItemKind::Struct(ref variant_data, _) = item.kind {
if let Some(ctor_hir_id) = variant_data.ctor_hir_id() {
- self.struct_constructors
- .insert(self.tcx.hir().local_def_id(ctor_hir_id), item.def_id);
+ struct_constructors.insert(tcx.hir().local_def_id(ctor_hir_id), item.def_id);
}
}
- hir::ItemKind::GlobalAsm(_) => {
- // global_asm! is always live.
- self.worklist.push(item.def_id);
- }
- _ => (),
}
+ DefKind::GlobalAsm => {
+ // global_asm! is always live.
+ worklist.push(id.def_id);
+ }
+ _ => {}
}
+}
- fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) {
- use hir::TraitItemKind::{Const, Fn};
+fn check_trait_item<'tcx>(tcx: TyCtxt<'tcx>, worklist: &mut Vec<LocalDefId>, id: hir::TraitItemId) {
+ use hir::TraitItemKind::{Const, Fn};
+ if matches!(tcx.def_kind(id.def_id), DefKind::AssocConst | DefKind::AssocFn) {
+ let trait_item = tcx.hir().trait_item(id);
if matches!(trait_item.kind, Const(_, Some(_)) | Fn(_, hir::TraitFn::Provided(_)))
- && has_allow_dead_code_or_lang_attr(self.tcx, trait_item.hir_id())
+ && has_allow_dead_code_or_lang_attr(tcx, trait_item.hir_id())
{
- self.worklist.push(trait_item.def_id);
+ worklist.push(trait_item.def_id);
}
}
+}
- fn visit_impl_item(&mut self, _item: &hir::ImplItem<'_>) {
- // ignore: we are handling this in `visit_item` above
- }
-
- fn visit_foreign_item(&mut self, foreign_item: &hir::ForeignItem<'_>) {
- use hir::ForeignItemKind::{Fn, Static};
- if matches!(foreign_item.kind, Static(..) | Fn(..))
- && has_allow_dead_code_or_lang_attr(self.tcx, foreign_item.hir_id())
- {
- self.worklist.push(foreign_item.def_id);
- }
+fn check_foreign_item<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ worklist: &mut Vec<LocalDefId>,
+ id: hir::ForeignItemId,
+) {
+ if matches!(tcx.def_kind(id.def_id), DefKind::Static(_) | DefKind::Fn)
+ && has_allow_dead_code_or_lang_attr(tcx, id.hir_id())
+ {
+ worklist.push(id.def_id);
}
}
tcx: TyCtxt<'tcx>,
) -> (Vec<LocalDefId>, FxHashMap<LocalDefId, LocalDefId>) {
let access_levels = &tcx.privacy_access_levels(());
- let worklist = access_levels
+ // see `MarkSymbolVisitor::struct_constructors`
+ let mut struct_constructors = Default::default();
+ let mut worklist = access_levels
.map
.iter()
.filter_map(
.chain(tcx.entry_fn(()).and_then(|(def_id, _)| def_id.as_local()))
.collect::<Vec<_>>();
- // Seed implemented trait items
- let mut life_seeder = LifeSeeder { worklist, tcx, struct_constructors: Default::default() };
- tcx.hir().visit_all_item_likes(&mut life_seeder);
+ let crate_items = tcx.hir_crate_items(());
+ for id in crate_items.items() {
+ check_item(tcx, &mut worklist, &mut struct_constructors, id);
+ }
+
+ for id in crate_items.trait_items() {
+ check_trait_item(tcx, &mut worklist, id);
+ }
+
+ for id in crate_items.foreign_items() {
+ check_foreign_item(tcx, &mut worklist, id);
+ }
- (life_seeder.worklist, life_seeder.struct_constructors)
+ (worklist, struct_constructors)
}
fn live_symbols_and_ignored_derived_traits<'tcx>(
use rustc_expand::base::resolve_path;
use rustc_hir as hir;
use rustc_hir::def_id::CrateNum;
-use rustc_hir::itemlikevisit::ItemLikeVisitor;
-use rustc_hir::{HirId, Target};
+use rustc_hir::HirId;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::LOCAL_CRATE;
use std::sync::Arc;
-struct DebuggerVisualizerCollector<'tcx> {
- debugger_visualizers: FxHashSet<DebuggerVisualizerFile>,
+fn check_for_debugger_visualizer<'tcx>(
tcx: TyCtxt<'tcx>,
-}
-
-impl<'v, 'tcx> ItemLikeVisitor<'v> for DebuggerVisualizerCollector<'tcx> {
- fn visit_item(&mut self, item: &hir::Item<'_>) {
- let target = Target::from_item(item);
- match target {
- Target::Mod => {
- self.check_for_debugger_visualizer(item.hir_id());
- }
- _ => {}
- }
- }
-
- fn visit_trait_item(&mut self, _: &hir::TraitItem<'_>) {}
-
- fn visit_impl_item(&mut self, _: &hir::ImplItem<'_>) {}
-
- fn visit_foreign_item(&mut self, _: &hir::ForeignItem<'_>) {}
-}
-
-impl<'tcx> DebuggerVisualizerCollector<'tcx> {
- fn new(tcx: TyCtxt<'tcx>) -> DebuggerVisualizerCollector<'tcx> {
- DebuggerVisualizerCollector { tcx, debugger_visualizers: FxHashSet::default() }
- }
-
- fn check_for_debugger_visualizer(&mut self, hir_id: HirId) {
- let attrs = self.tcx.hir().attrs(hir_id);
- for attr in attrs {
- if attr.has_name(sym::debugger_visualizer) {
- let list = match attr.meta_item_list() {
- Some(list) => list,
+ hir_id: HirId,
+ debugger_visualizers: &mut FxHashSet<DebuggerVisualizerFile>,
+) {
+ let attrs = tcx.hir().attrs(hir_id);
+ for attr in attrs {
+ if attr.has_name(sym::debugger_visualizer) {
+ let list = match attr.meta_item_list() {
+ Some(list) => list,
+ _ => continue,
+ };
+
+ let meta_item = match list.len() {
+ 1 => match list[0].meta_item() {
+ Some(meta_item) => meta_item,
_ => continue,
- };
-
- let meta_item = match list.len() {
- 1 => match list[0].meta_item() {
- Some(meta_item) => meta_item,
- _ => continue,
- },
- _ => continue,
- };
-
- let file = match (meta_item.name_or_empty(), meta_item.value_str()) {
- (sym::natvis_file, Some(value)) => {
- match resolve_path(&self.tcx.sess.parse_sess, value.as_str(), attr.span) {
- Ok(file) => file,
- Err(mut err) => {
- err.emit();
- continue;
- }
+ },
+ _ => continue,
+ };
+
+ let file = match (meta_item.name_or_empty(), meta_item.value_str()) {
+ (sym::natvis_file, Some(value)) => {
+ match resolve_path(&tcx.sess.parse_sess, value.as_str(), attr.span) {
+ Ok(file) => file,
+ Err(mut err) => {
+ err.emit();
+ continue;
}
}
- (_, _) => continue,
+ }
+ (_, _) => continue,
+ };
+
+ if file.is_file() {
+ let contents = match std::fs::read(&file) {
+ Ok(contents) => contents,
+ Err(err) => {
+ tcx.sess
+ .struct_span_err(
+ attr.span,
+ &format!(
+ "Unable to read contents of file `{}`. {}",
+ file.display(),
+ err
+ ),
+ )
+ .emit();
+ continue;
+ }
};
- if file.is_file() {
- let contents = match std::fs::read(&file) {
- Ok(contents) => contents,
- Err(err) => {
- self.tcx
- .sess
- .struct_span_err(
- attr.span,
- &format!(
- "Unable to read contents of file `{}`. {}",
- file.display(),
- err
- ),
- )
- .emit();
- continue;
- }
- };
-
- self.debugger_visualizers.insert(DebuggerVisualizerFile::new(
- Arc::from(contents),
- DebuggerVisualizerType::Natvis,
- ));
- } else {
- self.tcx
- .sess
- .struct_span_err(
- attr.span,
- &format!("{} is not a valid file", file.display()),
- )
- .emit();
- }
+ debugger_visualizers.insert(DebuggerVisualizerFile::new(
+ Arc::from(contents),
+ DebuggerVisualizerType::Natvis,
+ ));
+ } else {
+ tcx.sess
+ .struct_span_err(attr.span, &format!("{} is not a valid file", file.display()))
+ .emit();
}
}
}
assert_eq!(cnum, LOCAL_CRATE);
// Initialize the collector.
- let mut collector = DebuggerVisualizerCollector::new(tcx);
+ let mut debugger_visualizers = FxHashSet::default();
// Collect debugger visualizers in this crate.
- tcx.hir().visit_all_item_likes(&mut collector);
+ tcx.hir().for_each_module(|id| {
+ check_for_debugger_visualizer(
+ tcx,
+ tcx.hir().local_def_id_to_hir_id(id),
+ &mut debugger_visualizers,
+ )
+ });
// Collect debugger visualizers on the crate attributes.
- collector.check_for_debugger_visualizer(CRATE_HIR_ID);
+ check_for_debugger_visualizer(tcx, CRATE_HIR_ID, &mut debugger_visualizers);
// Extract out the found debugger_visualizer items.
- let DebuggerVisualizerCollector { debugger_visualizers, .. } = collector;
-
let mut visualizers = debugger_visualizers.into_iter().collect::<Vec<_>>();
// Sort the visualizers so we always get a deterministic query result.
//! * Compiler internal types like `Ty` and `TyCtxt`
use rustc_ast as ast;
-use rustc_hir as hir;
use rustc_hir::diagnostic_items::DiagnosticItems;
-use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
use rustc_span::symbol::{sym, Symbol};
-struct DiagnosticItemCollector<'tcx> {
+fn observe_item<'tcx>(
tcx: TyCtxt<'tcx>,
- diagnostic_items: DiagnosticItems,
-}
-
-impl<'v, 'tcx> ItemLikeVisitor<'v> for DiagnosticItemCollector<'tcx> {
- fn visit_item(&mut self, item: &hir::Item<'_>) {
- self.observe_item(item.def_id);
- }
-
- fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) {
- self.observe_item(trait_item.def_id);
- }
-
- fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) {
- self.observe_item(impl_item.def_id);
- }
-
- fn visit_foreign_item(&mut self, foreign_item: &hir::ForeignItem<'_>) {
- self.observe_item(foreign_item.def_id);
- }
-}
-
-impl<'tcx> DiagnosticItemCollector<'tcx> {
- fn new(tcx: TyCtxt<'tcx>) -> DiagnosticItemCollector<'tcx> {
- DiagnosticItemCollector { tcx, diagnostic_items: DiagnosticItems::default() }
- }
-
- fn observe_item(&mut self, def_id: LocalDefId) {
- let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
- let attrs = self.tcx.hir().attrs(hir_id);
- if let Some(name) = extract(attrs) {
- // insert into our table
- collect_item(self.tcx, &mut self.diagnostic_items, name, def_id.to_def_id());
- }
+ diagnostic_items: &mut DiagnosticItems,
+ def_id: LocalDefId,
+) {
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+ let attrs = tcx.hir().attrs(hir_id);
+ if let Some(name) = extract(attrs) {
+ // insert into our table
+ collect_item(tcx, diagnostic_items, name, def_id.to_def_id());
}
}
}
}
-/// Extract the first `rustc_diagnostic_item = "$name"` out of a list of attributes.p
+/// Extract the first `rustc_diagnostic_item = "$name"` out of a list of attributes.
fn extract(attrs: &[ast::Attribute]) -> Option<Symbol> {
attrs.iter().find_map(|attr| {
if attr.has_name(sym::rustc_diagnostic_item) { attr.value_str() } else { None }
assert_eq!(cnum, LOCAL_CRATE);
// Initialize the collector.
- let mut collector = DiagnosticItemCollector::new(tcx);
+ let mut diagnostic_items = DiagnosticItems::default();
// Collect diagnostic items in this crate.
- tcx.hir().visit_all_item_likes(&mut collector);
+ let crate_items = tcx.hir_crate_items(());
+
+ for id in crate_items.items() {
+ observe_item(tcx, &mut diagnostic_items, id.def_id);
+ }
+
+ for id in crate_items.trait_items() {
+ observe_item(tcx, &mut diagnostic_items, id.def_id);
+ }
+
+ for id in crate_items.impl_items() {
+ observe_item(tcx, &mut diagnostic_items, id.def_id);
+ }
+
+ for id in crate_items.foreign_items() {
+ observe_item(tcx, &mut diagnostic_items, id.def_id);
+ }
- collector.diagnostic_items
+ diagnostic_items
}
/// Traverse and collect all the diagnostic items in all crates.
use rustc_ast::entry::EntryPointType;
use rustc_errors::struct_span_err;
+use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
-use rustc_hir::itemlikevisit::ItemLikeVisitor;
-use rustc_hir::{ForeignItem, ImplItem, Item, ItemKind, Node, TraitItem, CRATE_HIR_ID};
+use rustc_hir::{ItemId, Node, CRATE_HIR_ID};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{DefIdTree, TyCtxt};
use rustc_session::config::{CrateType, EntryFnType};
non_main_fns: Vec<Span>,
}
-impl<'tcx> ItemLikeVisitor<'tcx> for EntryContext<'tcx> {
- fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
- let at_root = self.tcx.opt_local_parent(item.def_id) == Some(CRATE_DEF_ID);
- find_item(item, self, at_root);
- }
-
- fn visit_trait_item(&mut self, _trait_item: &'tcx TraitItem<'tcx>) {
- // Entry fn is never a trait item.
- }
-
- fn visit_impl_item(&mut self, _impl_item: &'tcx ImplItem<'tcx>) {
- // Entry fn is never a trait item.
- }
-
- fn visit_foreign_item(&mut self, _: &'tcx ForeignItem<'tcx>) {
- // Entry fn is never a foreign item.
- }
-}
-
fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> {
let any_exe = tcx.sess.crate_types().iter().any(|ty| *ty == CrateType::Executable);
if !any_exe {
let mut ctxt =
EntryContext { tcx, attr_main_fn: None, start_fn: None, non_main_fns: Vec::new() };
- tcx.hir().visit_all_item_likes(&mut ctxt);
+ for id in tcx.hir().items() {
+ find_item(id, &mut ctxt);
+ }
configure_main(tcx, &ctxt)
}
// Beware, this is duplicated in `librustc_builtin_macros/test_harness.rs`
// (with `ast::Item`), so make sure to keep them in sync.
-fn entry_point_type(ctxt: &EntryContext<'_>, item: &Item<'_>, at_root: bool) -> EntryPointType {
- let attrs = ctxt.tcx.hir().attrs(item.hir_id());
+// A small optimization was added so that hir::Item is fetched only when needed.
+// An equivalent optimization was not applied to the duplicated code in test_harness.rs.
+fn entry_point_type(ctxt: &EntryContext<'_>, id: ItemId, at_root: bool) -> EntryPointType {
+ let attrs = ctxt.tcx.hir().attrs(id.hir_id());
if ctxt.tcx.sess.contains_name(attrs, sym::start) {
EntryPointType::Start
} else if ctxt.tcx.sess.contains_name(attrs, sym::rustc_main) {
EntryPointType::MainAttr
- } else if item.ident.name == sym::main {
- if at_root {
- // This is a top-level function so can be `main`.
- EntryPointType::MainNamed
+ } else {
+ if let Some(name) = ctxt.tcx.opt_item_name(id.def_id.to_def_id())
+ && name == sym::main {
+ if at_root {
+ // This is a top-level function so can be `main`.
+ EntryPointType::MainNamed
+ } else {
+ EntryPointType::OtherMain
+ }
} else {
- EntryPointType::OtherMain
+ EntryPointType::None
}
- } else {
- EntryPointType::None
}
}
.emit();
}
-fn find_item(item: &Item<'_>, ctxt: &mut EntryContext<'_>, at_root: bool) {
- match entry_point_type(ctxt, item, at_root) {
+fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
+ let at_root = ctxt.tcx.opt_local_parent(id.def_id) == Some(CRATE_DEF_ID);
+
+ match entry_point_type(ctxt, id, at_root) {
EntryPointType::None => (),
- _ if !matches!(item.kind, ItemKind::Fn(..)) => {
- let attrs = ctxt.tcx.hir().attrs(item.hir_id());
+ _ if !matches!(ctxt.tcx.def_kind(id.def_id), DefKind::Fn) => {
+ let attrs = ctxt.tcx.hir().attrs(id.hir_id());
if let Some(attr) = ctxt.tcx.sess.find_by_name(attrs, sym::start) {
throw_attr_err(&ctxt.tcx.sess, attr.span, "start");
}
}
EntryPointType::MainNamed => (),
EntryPointType::OtherMain => {
- ctxt.non_main_fns.push(item.span);
+ ctxt.non_main_fns.push(ctxt.tcx.def_span(id.def_id));
}
EntryPointType::MainAttr => {
if ctxt.attr_main_fn.is_none() {
- ctxt.attr_main_fn = Some((item.def_id, item.span));
+ ctxt.attr_main_fn = Some((id.def_id, ctxt.tcx.def_span(id.def_id.to_def_id())));
} else {
struct_span_err!(
ctxt.tcx.sess,
- item.span,
+ ctxt.tcx.def_span(id.def_id.to_def_id()),
E0137,
"multiple functions with a `#[main]` attribute"
)
- .span_label(item.span, "additional `#[main]` function")
+ .span_label(
+ ctxt.tcx.def_span(id.def_id.to_def_id()),
+ "additional `#[main]` function",
+ )
.span_label(ctxt.attr_main_fn.unwrap().1, "first `#[main]` function")
.emit();
}
}
EntryPointType::Start => {
if ctxt.start_fn.is_none() {
- ctxt.start_fn = Some((item.def_id, item.span));
+ ctxt.start_fn = Some((id.def_id, ctxt.tcx.def_span(id.def_id.to_def_id())));
} else {
- struct_span_err!(ctxt.tcx.sess, item.span, E0138, "multiple `start` functions")
- .span_label(ctxt.start_fn.unwrap().1, "previous `#[start]` function here")
- .span_label(item.span, "multiple `start` functions")
- .emit();
+ struct_span_err!(
+ ctxt.tcx.sess,
+ ctxt.tcx.def_span(id.def_id.to_def_id()),
+ E0138,
+ "multiple `start` functions"
+ )
+ .span_label(ctxt.start_fn.unwrap().1, "previous `#[start]` function here")
+ .span_label(ctxt.tcx.def_span(id.def_id.to_def_id()), "multiple `start` functions")
+ .emit();
}
}
}
use rustc_hir as hir;
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
use rustc_hir::intravisit;
-use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::{HirId, ItemLocalId};
use rustc_middle::hir::map::Map;
use rustc_middle::hir::nested_filter;
let hir_map = tcx.hir();
hir_map.par_for_each_module(|module_id| {
- hir_map
- .visit_item_likes_in_module(module_id, &mut OuterVisitor { hir_map, errors: &errors })
+ let mut v = HirIdValidator {
+ hir_map,
+ owner: None,
+ hir_ids_seen: Default::default(),
+ errors: &errors,
+ };
+
+ tcx.hir().deep_visit_item_likes_in_module(module_id, &mut v);
});
let errors = errors.into_inner();
errors: &'a Lock<Vec<String>>,
}
-struct OuterVisitor<'a, 'hir> {
- hir_map: Map<'hir>,
- errors: &'a Lock<Vec<String>>,
-}
-
-impl<'a, 'hir> OuterVisitor<'a, 'hir> {
- fn new_inner_visitor(&self, hir_map: Map<'hir>) -> HirIdValidator<'a, 'hir> {
+impl<'a, 'hir> HirIdValidator<'a, 'hir> {
+ fn new_visitor(&self, hir_map: Map<'hir>) -> HirIdValidator<'a, 'hir> {
HirIdValidator {
hir_map,
owner: None,
errors: self.errors,
}
}
-}
-
-impl<'a, 'hir> ItemLikeVisitor<'hir> for OuterVisitor<'a, 'hir> {
- fn visit_item(&mut self, i: &'hir hir::Item<'hir>) {
- let mut inner_visitor = self.new_inner_visitor(self.hir_map);
- inner_visitor.check(i.def_id, |this| intravisit::walk_item(this, i));
- }
-
- fn visit_trait_item(&mut self, i: &'hir hir::TraitItem<'hir>) {
- let mut inner_visitor = self.new_inner_visitor(self.hir_map);
- inner_visitor.check(i.def_id, |this| intravisit::walk_trait_item(this, i));
- }
-
- fn visit_impl_item(&mut self, i: &'hir hir::ImplItem<'hir>) {
- let mut inner_visitor = self.new_inner_visitor(self.hir_map);
- inner_visitor.check(i.def_id, |this| intravisit::walk_impl_item(this, i));
- }
-
- fn visit_foreign_item(&mut self, i: &'hir hir::ForeignItem<'hir>) {
- let mut inner_visitor = self.new_inner_visitor(self.hir_map);
- inner_visitor.check(i.def_id, |this| intravisit::walk_foreign_item(this, i));
- }
-}
-impl<'a, 'hir> HirIdValidator<'a, 'hir> {
#[cold]
#[inline(never)]
fn error(&self, f: impl FnOnce() -> String) {
self.hir_map
}
+ fn visit_item(&mut self, i: &'hir hir::Item<'hir>) {
+ let mut inner_visitor = self.new_visitor(self.hir_map);
+ inner_visitor.check(i.def_id, |this| intravisit::walk_item(this, i));
+ }
+
fn visit_id(&mut self, hir_id: HirId) {
let owner = self.owner.expect("no owner");
self.hir_ids_seen.insert(hir_id.local_id);
}
- fn visit_impl_item_ref(&mut self, _: &'hir hir::ImplItemRef) {
- // Explicitly do nothing here. ImplItemRefs contain hir::Visibility
- // values that actually belong to an ImplItem instead of the ItemKind::Impl
- // we are currently in. So for those it's correct that they have a
- // different owner.
+ fn visit_foreign_item(&mut self, i: &'hir hir::ForeignItem<'hir>) {
+ let mut inner_visitor = self.new_visitor(self.hir_map);
+ inner_visitor.check(i.def_id, |this| intravisit::walk_foreign_item(this, i));
+ }
+
+ fn visit_trait_item(&mut self, i: &'hir hir::TraitItem<'hir>) {
+ let mut inner_visitor = self.new_visitor(self.hir_map);
+ inner_visitor.check(i.def_id, |this| intravisit::walk_trait_item(this, i));
}
- fn visit_foreign_item_ref(&mut self, _: &'hir hir::ForeignItemRef) {
- // Explicitly do nothing here. ForeignItemRefs contain hir::Visibility
- // values that actually belong to an ForeignItem instead of the ItemKind::ForeignMod
- // we are currently in. So for those it's correct that they have a
- // different owner.
+ fn visit_impl_item(&mut self, i: &'hir hir::ImplItem<'hir>) {
+ let mut inner_visitor = self.new_visitor(self.hir_map);
+ inner_visitor.check(i.def_id, |this| intravisit::walk_impl_item(this, i));
}
}
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
use rustc_target::abi::{Pointer, VariantIdx};
use rustc_target::asm::{InlineAsmRegOrRegClass, InlineAsmType};
-use rustc_target::spec::abi::Abi::RustIntrinsic;
fn check_mod_intrinsics(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
- tcx.hir().visit_item_likes_in_module(module_def_id, &mut ItemVisitor { tcx }.as_deep_visitor());
+ tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut ItemVisitor { tcx });
}
pub fn provide(providers: &mut Providers) {
impl<'tcx> ExprVisitor<'tcx> {
fn def_id_is_transmute(&self, def_id: DefId) -> bool {
- self.tcx.fn_sig(def_id).abi() == RustIntrinsic
- && self.tcx.item_name(def_id) == sym::transmute
+ self.tcx.is_intrinsic(def_id) && self.tcx.item_name(def_id) == sym::transmute
}
fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>) {
use rustc_ast::Attribute;
-use rustc_hir as hir;
+use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
-use rustc_hir::itemlikevisit::ItemLikeVisitor;
-use rustc_hir::ItemKind;
use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout};
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
use rustc_span::symbol::sym;
pub fn test_layout(tcx: TyCtxt<'_>) {
if tcx.features().rustc_attrs {
// if the `rustc_attrs` feature is not enabled, don't bother testing layout
- tcx.hir().visit_all_item_likes(&mut LayoutTest { tcx });
- }
-}
-
-struct LayoutTest<'tcx> {
- tcx: TyCtxt<'tcx>,
-}
-
-impl<'tcx> ItemLikeVisitor<'tcx> for LayoutTest<'tcx> {
- fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
- match item.kind {
- ItemKind::TyAlias(..)
- | ItemKind::Enum(..)
- | ItemKind::Struct(..)
- | ItemKind::Union(..) => {
- for attr in self.tcx.get_attrs(item.def_id.to_def_id()).iter() {
- if attr.has_name(sym::rustc_layout) {
- self.dump_layout_of(item.def_id, item, attr);
- }
+ for id in tcx.hir().items() {
+ if matches!(
+ tcx.def_kind(id.def_id),
+ DefKind::TyAlias | DefKind::Enum | DefKind::Struct | DefKind::Union
+ ) {
+ for attr in tcx.get_attrs(id.def_id.to_def_id(), sym::rustc_layout) {
+ dump_layout_of(tcx, id.def_id, attr);
}
}
- _ => {}
}
}
-
- fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem<'tcx>) {}
- fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {}
- fn visit_foreign_item(&mut self, _: &'tcx hir::ForeignItem<'tcx>) {}
}
-impl<'tcx> LayoutTest<'tcx> {
- fn dump_layout_of(&self, item_def_id: LocalDefId, item: &hir::Item<'tcx>, attr: &Attribute) {
- let tcx = self.tcx;
- let param_env = self.tcx.param_env(item_def_id);
- let ty = self.tcx.type_of(item_def_id);
- match self.tcx.layout_of(param_env.and(ty)) {
- Ok(ty_layout) => {
- // Check out the `#[rustc_layout(..)]` attribute to tell what to dump.
- // The `..` are the names of fields to dump.
- let meta_items = attr.meta_item_list().unwrap_or_default();
- for meta_item in meta_items {
- match meta_item.name_or_empty() {
- sym::abi => {
- self.tcx.sess.span_err(item.span, &format!("abi: {:?}", ty_layout.abi));
- }
+fn dump_layout_of<'tcx>(tcx: TyCtxt<'tcx>, item_def_id: LocalDefId, attr: &Attribute) {
+ let tcx = tcx;
+ let param_env = tcx.param_env(item_def_id);
+ let ty = tcx.type_of(item_def_id);
+ match tcx.layout_of(param_env.and(ty)) {
+ Ok(ty_layout) => {
+ // Check out the `#[rustc_layout(..)]` attribute to tell what to dump.
+ // The `..` are the names of fields to dump.
+ let meta_items = attr.meta_item_list().unwrap_or_default();
+ for meta_item in meta_items {
+ match meta_item.name_or_empty() {
+ sym::abi => {
+ tcx.sess.span_err(
+ tcx.def_span(item_def_id.to_def_id()),
+ &format!("abi: {:?}", ty_layout.abi),
+ );
+ }
- sym::align => {
- self.tcx
- .sess
- .span_err(item.span, &format!("align: {:?}", ty_layout.align));
- }
+ sym::align => {
+ tcx.sess.span_err(
+ tcx.def_span(item_def_id.to_def_id()),
+ &format!("align: {:?}", ty_layout.align),
+ );
+ }
- sym::size => {
- self.tcx
- .sess
- .span_err(item.span, &format!("size: {:?}", ty_layout.size));
- }
+ sym::size => {
+ tcx.sess.span_err(
+ tcx.def_span(item_def_id.to_def_id()),
+ &format!("size: {:?}", ty_layout.size),
+ );
+ }
- sym::homogeneous_aggregate => {
- self.tcx.sess.span_err(
- item.span,
- &format!(
- "homogeneous_aggregate: {:?}",
- ty_layout
- .homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env }),
- ),
- );
- }
+ sym::homogeneous_aggregate => {
+ tcx.sess.span_err(
+ tcx.def_span(item_def_id.to_def_id()),
+ &format!(
+ "homogeneous_aggregate: {:?}",
+ ty_layout.homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env }),
+ ),
+ );
+ }
- sym::debug => {
- let normalized_ty = self.tcx.normalize_erasing_regions(
- param_env.with_reveal_all_normalized(self.tcx),
- ty,
- );
- self.tcx.sess.span_err(
- item.span,
- &format!("layout_of({:?}) = {:#?}", normalized_ty, *ty_layout),
- );
- }
+ sym::debug => {
+ let normalized_ty = tcx.normalize_erasing_regions(
+ param_env.with_reveal_all_normalized(tcx),
+ ty,
+ );
+ tcx.sess.span_err(
+ tcx.def_span(item_def_id.to_def_id()),
+ &format!("layout_of({:?}) = {:#?}", normalized_ty, *ty_layout),
+ );
+ }
- name => {
- self.tcx.sess.span_err(
- meta_item.span(),
- &format!("unrecognized field name `{}`", name),
- );
- }
+ name => {
+ tcx.sess.span_err(
+ meta_item.span(),
+ &format!("unrecognized field name `{}`", name),
+ );
}
}
}
+ }
- Err(layout_error) => {
- self.tcx.sess.span_err(item.span, &format!("layout error: {:?}", layout_error));
- }
+ Err(layout_error) => {
+ tcx.sess.span_err(
+ tcx.def_span(item_def_id.to_def_id()),
+ &format!("layout error: {:?}", layout_error),
+ );
}
}
}
}
fn check_mod_liveness(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
- tcx.hir().visit_item_likes_in_module(module_def_id, &mut IrMaps::new(tcx).as_deep_visitor());
+ tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut IrMaps::new(tcx));
}
pub fn provide(providers: &mut Providers) {
fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
self.add_from_pat(&arm.pat);
- if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard {
- self.add_from_pat(pat);
+ if let Some(hir::Guard::IfLet(ref let_expr)) = arm.guard {
+ self.add_from_pat(let_expr.pat);
}
intravisit::walk_arm(self, arm);
}
let guard_succ = arm.guard.as_ref().map_or(body_succ, |g| match g {
hir::Guard::If(e) => self.propagate_through_expr(e, body_succ),
- hir::Guard::IfLet(pat, e) => {
- let let_bind = self.define_bindings_in_pat(pat, body_succ);
- self.propagate_through_expr(e, let_bind)
+ hir::Guard::IfLet(let_expr) => {
+ let let_bind = self.define_bindings_in_pat(let_expr.pat, body_succ);
+ self.propagate_through_expr(let_expr.init, let_bind)
}
});
let arm_succ = self.define_bindings_in_pat(&arm.pat, guard_succ);
}
fn check_mod_loops(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
- tcx.hir().visit_item_likes_in_module(
+ tcx.hir().deep_visit_item_likes_in_module(
module_def_id,
- &mut CheckLoopVisitor { sess: &tcx.sess, hir_map: tcx.hir(), cx: Normal }.as_deep_visitor(),
+ &mut CheckLoopVisitor { sess: &tcx.sess, hir_map: tcx.hir(), cx: Normal },
);
}
use rustc_target::spec::abi::Abi;
fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
- tcx.hir().visit_item_likes_in_module(
- module_def_id,
- &mut CheckNakedFunctions { tcx }.as_deep_visitor(),
- );
+ tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut CheckNakedFunctions { tcx });
}
crate fn provide(providers: &mut Providers) {
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::Node;
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
use rustc_middle::middle::privacy;
}
}
-// Some methods from non-exported (completely private) trait impls still have to be
-// reachable if they are called from inlinable code. Generally, it's not known until
-// monomorphization if a specific trait impl item can be reachable or not. So, we
-// conservatively mark all of them as reachable.
-// FIXME: One possible strategy for pruning the reachable set is to avoid marking impl
-// items of non-exported traits (or maybe all local traits?) unless their respective
-// trait items are used from inlinable code through method call syntax or UFCS, or their
-// trait is a lang item.
-struct CollectPrivateImplItemsVisitor<'a, 'tcx> {
+fn check_item<'tcx>(
tcx: TyCtxt<'tcx>,
- access_levels: &'a privacy::AccessLevels,
- worklist: &'a mut Vec<LocalDefId>,
-}
+ id: hir::ItemId,
+ worklist: &mut Vec<LocalDefId>,
+ access_levels: &privacy::AccessLevels,
+) {
+ if has_custom_linkage(tcx, id.def_id) {
+ worklist.push(id.def_id);
+ }
-impl CollectPrivateImplItemsVisitor<'_, '_> {
- fn push_to_worklist_if_has_custom_linkage(&mut self, def_id: LocalDefId) {
- // Anything which has custom linkage gets thrown on the worklist no
- // matter where it is in the crate, along with "special std symbols"
- // which are currently akin to allocator symbols.
- if self.tcx.def_kind(def_id).has_codegen_attrs() {
- let codegen_attrs = self.tcx.codegen_fn_attrs(def_id);
- if codegen_attrs.contains_extern_indicator()
- || codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
- // FIXME(nbdd0121): `#[used]` are marked as reachable here so it's picked up by
- // `linked_symbols` in cg_ssa. They won't be exported in binary or cdylib due to their
- // `SymbolExportLevel::Rust` export level but may end up being exported in dylibs.
- || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED)
- || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
- {
- self.worklist.push(def_id);
- }
- }
+ if !matches!(tcx.def_kind(id.def_id), DefKind::Impl) {
+ return;
}
-}
-impl<'a, 'tcx> ItemLikeVisitor<'tcx> for CollectPrivateImplItemsVisitor<'a, 'tcx> {
- fn visit_item(&mut self, item: &hir::Item<'_>) {
- self.push_to_worklist_if_has_custom_linkage(item.def_id);
-
- // We need only trait impls here, not inherent impls, and only non-exported ones
- if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref trait_ref), ref items, .. }) =
- item.kind
- {
- if !self.access_levels.is_reachable(item.def_id) {
- // FIXME(#53488) remove `let`
- let tcx = self.tcx;
- self.worklist.extend(items.iter().map(|ii_ref| ii_ref.id.def_id));
-
- let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res else {
- unreachable!();
- };
+ // We need only trait impls here, not inherent impls, and only non-exported ones
+ let item = tcx.hir().item(id);
+ if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref trait_ref), ref items, .. }) =
+ item.kind
+ {
+ if !access_levels.is_reachable(item.def_id) {
+ // FIXME(#53488) remove `let`
+ let tcx = tcx;
+ worklist.extend(items.iter().map(|ii_ref| ii_ref.id.def_id));
- if !trait_def_id.is_local() {
- return;
- }
+ let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res else {
+ unreachable!();
+ };
- self.worklist.extend(
- tcx.provided_trait_methods(trait_def_id)
- .map(|assoc| assoc.def_id.expect_local()),
- );
+ if !trait_def_id.is_local() {
+ return;
}
- }
- }
-
- fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {}
- fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) {
- self.push_to_worklist_if_has_custom_linkage(impl_item.def_id);
+ worklist.extend(
+ tcx.provided_trait_methods(trait_def_id).map(|assoc| assoc.def_id.expect_local()),
+ );
+ }
}
+}
- fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {
- // We never export foreign functions as they have no body to export.
+fn has_custom_linkage<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool {
+ // Anything which has custom linkage gets thrown on the worklist no
+ // matter where it is in the crate, along with "special std symbols"
+ // which are currently akin to allocator symbols.
+ if !tcx.def_kind(def_id).has_codegen_attrs() {
+ return false;
}
+ let codegen_attrs = tcx.codegen_fn_attrs(def_id);
+ codegen_attrs.contains_extern_indicator()
+ || codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
+ // FIXME(nbdd0121): `#[used]` are marked as reachable here so it's picked up by
+ // `linked_symbols` in cg_ssa. They won't be exported in binary or cdylib due to their
+ // `SymbolExportLevel::Rust` export level but may end up being exported in dylibs.
+ || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED)
+ || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
}
fn reachable_set<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> FxHashSet<LocalDefId> {
}
}
{
- let mut collect_private_impl_items = CollectPrivateImplItemsVisitor {
- tcx,
- access_levels,
- worklist: &mut reachable_context.worklist,
- };
- tcx.hir().visit_all_item_likes(&mut collect_private_impl_items);
+ // Some methods from non-exported (completely private) trait impls still have to be
+ // reachable if they are called from inlinable code. Generally, it's not known until
+ // monomorphization if a specific trait impl item can be reachable or not. So, we
+ // conservatively mark all of them as reachable.
+ // FIXME: One possible strategy for pruning the reachable set is to avoid marking impl
+ // items of non-exported traits (or maybe all local traits?) unless their respective
+ // trait items are used from inlinable code through method call syntax or UFCS, or their
+ // trait is a lang item.
+ let crate_items = tcx.hir_crate_items(());
+
+ for id in crate_items.items() {
+ check_item(tcx, id, &mut reachable_context.worklist, access_levels);
+ }
+
+ for id in crate_items.impl_items() {
+ if has_custom_linkage(tcx, id.def_id) {
+ reachable_context.worklist.push(id.def_id);
+ }
+ }
}
// Step 2: Mark all symbols that the symbols on the worklist touch.
) where
F: FnOnce(&mut Self),
{
- let attrs = self.tcx.get_attrs(def_id.to_def_id());
+ let attrs = self.tcx.hir().attrs(self.tcx.hir().local_def_id_to_hir_id(def_id));
debug!("annotate(id = {:?}, attrs = {:?})", def_id, attrs);
let depr = attr::find_deprecation(&self.tcx.sess, attrs);
/// Cross-references the feature names of unstable APIs with enabled
/// features and possibly prints errors.
fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
- tcx.hir().visit_item_likes_in_module(module_def_id, &mut Checker { tcx }.as_deep_visitor());
+ tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut Checker { tcx });
}
pub(crate) fn provide(providers: &mut Providers) {
let mut missing = MissingStabilityAnnotations { tcx, access_levels };
missing.check_missing_stability(CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID));
tcx.hir().walk_toplevel_module(&mut missing);
- tcx.hir().visit_all_item_likes(&mut missing.as_deep_visitor());
+ tcx.hir().deep_visit_all_item_likes(&mut missing);
}
let declared_lang_features = &tcx.features().declared_lang_features;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID};
-use rustc_hir::intravisit::{self, DeepVisitor, Visitor};
-use rustc_hir::{AssocItemKind, HirIdSet, Node, PatKind};
+use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::{AssocItemKind, HirIdSet, ItemId, Node, PatKind};
use rustc_middle::bug;
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::privacy::{AccessLevel, AccessLevels};
}
// Corner case: if the variant is reachable, but its
// enum is not, make the enum reachable as well.
- self.update(item.def_id, variant_level);
+ self.reach(item.def_id, variant_level).ty();
+ }
+ if let Some(hir_id) = variant.data.ctor_hir_id() {
+ let ctor_def_id = self.tcx.hir().local_def_id(hir_id);
+ let ctor_level = self.get(ctor_def_id);
+ if ctor_level.is_some() {
+ self.reach(item.def_id, ctor_level).ty();
+ }
}
}
}
}
}
}
+ if let Some(hir_id) = struct_def.ctor_hir_id() {
+ let ctor_def_id = self.tcx.hir().local_def_id(hir_id);
+ let ctor_level = self.get(ctor_def_id);
+ if ctor_level.is_some() {
+ self.reach(item.def_id, ctor_level).ty();
+ }
+ }
}
}
}
}
-struct PrivateItemsInPublicInterfacesVisitor<'tcx> {
+struct PrivateItemsInPublicInterfacesChecker<'tcx> {
tcx: TyCtxt<'tcx>,
old_error_set_ancestry: LocalDefIdSet,
}
-impl<'tcx> PrivateItemsInPublicInterfacesVisitor<'tcx> {
+impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx> {
fn check(
&self,
def_id: LocalDefId,
check.ty();
}
}
-}
-
-impl<'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'tcx> {
- type NestedFilter = nested_filter::OnlyBodies;
- fn nested_visit_map(&mut self) -> Self::Map {
- self.tcx.hir()
- }
-
- fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
+ pub fn check_item(&mut self, id: ItemId) {
let tcx = self.tcx;
- let item_visibility = tcx.visibility(item.def_id);
+ let item_visibility = tcx.visibility(id.def_id);
+ let def_kind = tcx.def_kind(id.def_id);
- match item.kind {
- // Crates are always public.
- hir::ItemKind::ExternCrate(..) => {}
- // All nested items are checked by `visit_item`.
- hir::ItemKind::Mod(..) => {}
- // Checked in resolve.
- hir::ItemKind::Use(..) => {}
- // No subitems.
- hir::ItemKind::Macro(..) | hir::ItemKind::GlobalAsm(..) => {}
- // Subitems of these items have inherited publicity.
- hir::ItemKind::Const(..)
- | hir::ItemKind::Static(..)
- | hir::ItemKind::Fn(..)
- | hir::ItemKind::TyAlias(..) => {
- self.check(item.def_id, item_visibility).generics().predicates().ty();
+ match def_kind {
+ DefKind::Const | DefKind::Static(_) | DefKind::Fn | DefKind::TyAlias => {
+ self.check(id.def_id, item_visibility).generics().predicates().ty();
}
- hir::ItemKind::OpaqueTy(..) => {
+ DefKind::OpaqueTy => {
// `ty()` for opaque types is the underlying type,
// it's not a part of interface, so we skip it.
- self.check(item.def_id, item_visibility).generics().bounds();
+ self.check(id.def_id, item_visibility).generics().bounds();
}
- hir::ItemKind::Trait(.., trait_item_refs) => {
- self.check(item.def_id, item_visibility).generics().predicates();
+ DefKind::Trait => {
+ let item = tcx.hir().item(id);
+ if let hir::ItemKind::Trait(.., trait_item_refs) = item.kind {
+ self.check(item.def_id, item_visibility).generics().predicates();
- for trait_item_ref in trait_item_refs {
- self.check_assoc_item(
- trait_item_ref.id.def_id,
- trait_item_ref.kind,
- trait_item_ref.defaultness,
- item_visibility,
- );
-
- if let AssocItemKind::Type = trait_item_ref.kind {
- self.check(trait_item_ref.id.def_id, item_visibility).bounds();
+ for trait_item_ref in trait_item_refs {
+ self.check_assoc_item(
+ trait_item_ref.id.def_id,
+ trait_item_ref.kind,
+ trait_item_ref.defaultness,
+ item_visibility,
+ );
+
+ if let AssocItemKind::Type = trait_item_ref.kind {
+ self.check(trait_item_ref.id.def_id, item_visibility).bounds();
+ }
}
}
}
- hir::ItemKind::TraitAlias(..) => {
- self.check(item.def_id, item_visibility).generics().predicates();
+ DefKind::TraitAlias => {
+ self.check(id.def_id, item_visibility).generics().predicates();
}
- hir::ItemKind::Enum(ref def, _) => {
- self.check(item.def_id, item_visibility).generics().predicates();
+ DefKind::Enum => {
+ let item = tcx.hir().item(id);
+ if let hir::ItemKind::Enum(ref def, _) = item.kind {
+ self.check(item.def_id, item_visibility).generics().predicates();
- for variant in def.variants {
- for field in variant.data.fields() {
- self.check(self.tcx.hir().local_def_id(field.hir_id), item_visibility).ty();
+ for variant in def.variants {
+ for field in variant.data.fields() {
+ self.check(self.tcx.hir().local_def_id(field.hir_id), item_visibility)
+ .ty();
+ }
}
}
}
// Subitems of foreign modules have their own publicity.
- hir::ItemKind::ForeignMod { items, .. } => {
- for foreign_item in items {
- let vis = tcx.visibility(foreign_item.id.def_id);
- self.check(foreign_item.id.def_id, vis).generics().predicates().ty();
+ DefKind::ForeignMod => {
+ let item = tcx.hir().item(id);
+ if let hir::ItemKind::ForeignMod { items, .. } = item.kind {
+ for foreign_item in items {
+ let vis = tcx.visibility(foreign_item.id.def_id);
+ self.check(foreign_item.id.def_id, vis).generics().predicates().ty();
+ }
}
}
// Subitems of structs and unions have their own publicity.
- hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => {
- self.check(item.def_id, item_visibility).generics().predicates();
+ DefKind::Struct | DefKind::Union => {
+ let item = tcx.hir().item(id);
+ if let hir::ItemKind::Struct(ref struct_def, _)
+ | hir::ItemKind::Union(ref struct_def, _) = item.kind
+ {
+ self.check(item.def_id, item_visibility).generics().predicates();
- for field in struct_def.fields() {
- let def_id = tcx.hir().local_def_id(field.hir_id);
- let field_visibility = tcx.visibility(def_id);
- self.check(def_id, min(item_visibility, field_visibility, tcx)).ty();
+ for field in struct_def.fields() {
+ let def_id = tcx.hir().local_def_id(field.hir_id);
+ let field_visibility = tcx.visibility(def_id);
+ self.check(def_id, min(item_visibility, field_visibility, tcx)).ty();
+ }
}
}
// An inherent impl is public when its type is public
// Subitems of inherent impls have their own publicity.
// A trait impl is public when both its type and its trait are public
// Subitems of trait impls have inherited publicity.
- hir::ItemKind::Impl(ref impl_) => {
- let impl_vis = ty::Visibility::of_impl(item.def_id, tcx, &Default::default());
- // check that private components do not appear in the generics or predicates of inherent impls
- // this check is intentionally NOT performed for impls of traits, per #90586
- if impl_.of_trait.is_none() {
- self.check(item.def_id, impl_vis).generics().predicates();
- }
- for impl_item_ref in impl_.items {
- let impl_item_vis = if impl_.of_trait.is_none() {
- min(tcx.visibility(impl_item_ref.id.def_id), impl_vis, tcx)
- } else {
- impl_vis
- };
- self.check_assoc_item(
- impl_item_ref.id.def_id,
- impl_item_ref.kind,
- impl_item_ref.defaultness,
- impl_item_vis,
- );
+ DefKind::Impl => {
+ let item = tcx.hir().item(id);
+ if let hir::ItemKind::Impl(ref impl_) = item.kind {
+ let impl_vis = ty::Visibility::of_impl(item.def_id, tcx, &Default::default());
+ // check that private components do not appear in the generics or predicates of inherent impls
+ // this check is intentionally NOT performed for impls of traits, per #90586
+ if impl_.of_trait.is_none() {
+ self.check(item.def_id, impl_vis).generics().predicates();
+ }
+ for impl_item_ref in impl_.items {
+ let impl_item_vis = if impl_.of_trait.is_none() {
+ min(tcx.visibility(impl_item_ref.id.def_id), impl_vis, tcx)
+ } else {
+ impl_vis
+ };
+ self.check_assoc_item(
+ impl_item_ref.id.def_id,
+ impl_item_ref.kind,
+ impl_item_ref.defaultness,
+ impl_item_vis,
+ );
+ }
}
}
+ _ => {}
}
}
}
}
// Check for private types and traits in public interfaces.
- let mut visitor = PrivateItemsInPublicInterfacesVisitor {
+ let mut checker = PrivateItemsInPublicInterfacesChecker {
tcx,
// Only definition IDs are ever searched in `old_error_set_ancestry`,
// so we can filter away all non-definition IDs at this point.
.filter_map(|hir_id| tcx.hir().opt_local_def_id(hir_id))
.collect(),
};
- tcx.hir().visit_all_item_likes(&mut DeepVisitor::new(&mut visitor));
+
+ for id in tcx.hir().items() {
+ checker.check_item(id);
+ }
}
edition = "2021"
[lib]
-test = false
doctest = false
[dependencies]
}
let ext = Lrc::new(match self.cstore().load_macro_untracked(def_id, &self.session) {
- LoadedMacro::MacroDef(item, edition) => self.compile_macro(&item, edition),
+ LoadedMacro::MacroDef(item, edition) => self.compile_macro(&item, edition).0,
LoadedMacro::ProcMacro(ext) => ext,
});
// Mark the given macro as unused unless its name starts with `_`.
// Macro uses will remove items from this set, and the remaining
// items will be reported as `unused_macros`.
- fn insert_unused_macro(&mut self, ident: Ident, def_id: LocalDefId, node_id: NodeId) {
+ fn insert_unused_macro(
+ &mut self,
+ ident: Ident,
+ def_id: LocalDefId,
+ node_id: NodeId,
+ rule_spans: &[Span],
+ ) {
if !ident.as_str().starts_with('_') {
self.r.unused_macros.insert(def_id, (node_id, ident));
+ for (rule_i, rule_span) in rule_spans.iter().enumerate() {
+ self.r.unused_macro_rules.insert((def_id, rule_i), (ident, *rule_span));
+ }
}
}
let parent_scope = self.parent_scope;
let expansion = parent_scope.expansion;
let def_id = self.r.local_def_id(item.id);
- let (ext, ident, span, macro_rules) = match &item.kind {
+ let (ext, ident, span, macro_rules, rule_spans) = match &item.kind {
ItemKind::MacroDef(def) => {
- let ext = Lrc::new(self.r.compile_macro(item, self.r.session.edition()));
- (ext, item.ident, item.span, def.macro_rules)
+ let (ext, rule_spans) = self.r.compile_macro(item, self.r.session.edition());
+ let ext = Lrc::new(ext);
+ (ext, item.ident, item.span, def.macro_rules, rule_spans)
}
ItemKind::Fn(..) => match self.proc_macro_stub(item) {
Some((macro_kind, ident, span)) => {
self.r.proc_macro_stubs.insert(def_id);
- (self.r.dummy_ext(macro_kind), ident, span, false)
+ (self.r.dummy_ext(macro_kind), ident, span, false, Vec::new())
}
None => return parent_scope.macro_rules,
},
};
let binding = (res, vis, span, expansion).to_name_binding(self.r.arenas);
self.r.set_binding_parent_module(binding, parent_scope.module);
- self.r.all_macro_rules.insert(ident.name, res);
if is_macro_export {
let module = self.r.graph_root;
self.r.define(module, ident, MacroNS, (res, vis, span, expansion, IsMacroExport));
} else {
self.r.check_reserved_macro_name(ident, res);
- self.insert_unused_macro(ident, def_id, item.id);
+ self.insert_unused_macro(ident, def_id, item.id, &rule_spans);
}
self.r.visibilities.insert(def_id, vis);
let scope = self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding(
_ => self.resolve_visibility(&item.vis),
};
if vis != ty::Visibility::Public {
- self.insert_unused_macro(ident, def_id, item.id);
+ self.insert_unused_macro(ident, def_id, item.id, &rule_spans);
}
self.r.define(module, ident, MacroNS, (res, vis, span, expansion));
self.r.visibilities.insert(def_id, vis);
"remove the unused import"
};
- let parent_module = visitor.r.get_nearest_non_block_module(
- visitor.r.local_def_id(unused.use_tree_id).to_def_id(),
- );
- let test_module_span = match module_to_string(parent_module) {
- Some(module)
- if module == "test"
- || module == "tests"
- || module.starts_with("test_")
- || module.starts_with("tests_")
- || module.ends_with("_test")
- || module.ends_with("_tests") =>
- {
- Some(parent_module.span)
+ // If we are in the `--test` mode, suppress a help that adds the `#[cfg(test)]`
+ // attribute; however, if not, suggest adding the attribute. There is no way to
+ // retrieve attributes here because we do not have a `TyCtxt` yet.
+ let test_module_span = if visitor.r.session.opts.test {
+ None
+ } else {
+ let parent_module = visitor.r.get_nearest_non_block_module(
+ visitor.r.local_def_id(unused.use_tree_id).to_def_id(),
+ );
+ match module_to_string(parent_module) {
+ Some(module)
+ if module == "test"
+ || module == "tests"
+ || module.starts_with("test_")
+ || module.starts_with("tests_")
+ || module.ends_with("_test")
+ || module.ends_with("_tests") =>
+ {
+ Some(parent_module.span)
+ }
+ _ => None,
}
- _ => None,
};
visitor.r.lint_buffer.buffer_lint_with_diagnostic(
visit::walk_item(self, i);
return self.visit_macro_invoc(i.id);
}
- ItemKind::GlobalAsm(..) => DefPathData::Misc,
+ ItemKind::GlobalAsm(..) => DefPathData::GlobalAsm,
ItemKind::Use(..) => {
return visit::walk_item(self, i);
}
}
fn visit_use_tree(&mut self, use_tree: &'a UseTree, id: NodeId, _nested: bool) {
- self.create_def(id, DefPathData::Misc, use_tree.span);
+ self.create_def(id, DefPathData::Use, use_tree.span);
match use_tree.kind {
UseTreeKind::Simple(_, id1, id2) => {
- self.create_def(id1, DefPathData::Misc, use_tree.prefix.span);
- self.create_def(id2, DefPathData::Misc, use_tree.prefix.span);
+ self.create_def(id1, DefPathData::Use, use_tree.prefix.span);
+ self.create_def(id2, DefPathData::Use, use_tree.prefix.span);
}
UseTreeKind::Glob => (),
UseTreeKind::Nested(..) => {}
use crate::{ParentScope, PathResult, ResolutionError, Resolver, Scope, ScopeSet};
use crate::{Segment, UseError};
+#[cfg(test)]
+mod tests;
+
type Res = def::Res<ast::NodeId>;
/// A vector of spans and replacements, a message and applicability.
// import or other generated ones
!s.from_expansion()
}
+
+/// Convert the given number into the corresponding ordinal
+crate fn ordinalize(v: usize) -> String {
+ let suffix = match ((11..=13).contains(&(v % 100)), v % 10) {
+ (false, 1) => "st",
+ (false, 2) => "nd",
+ (false, 3) => "rd",
+ _ => "th",
+ };
+ format!("{v}{suffix}")
+}
--- /dev/null
+use super::ordinalize;
+
+#[test]
+fn test_ordinalize() {
+ assert_eq!(ordinalize(1), "1st");
+ assert_eq!(ordinalize(2), "2nd");
+ assert_eq!(ordinalize(3), "3rd");
+ assert_eq!(ordinalize(4), "4th");
+ assert_eq!(ordinalize(5), "5th");
+ // ...
+ assert_eq!(ordinalize(10), "10th");
+ assert_eq!(ordinalize(11), "11th");
+ assert_eq!(ordinalize(12), "12th");
+ assert_eq!(ordinalize(13), "13th");
+ assert_eq!(ordinalize(14), "14th");
+ // ...
+ assert_eq!(ordinalize(20), "20th");
+ assert_eq!(ordinalize(21), "21st");
+ assert_eq!(ordinalize(22), "22nd");
+ assert_eq!(ordinalize(23), "23rd");
+ assert_eq!(ordinalize(24), "24th");
+ // ...
+ assert_eq!(ordinalize(30), "30th");
+ assert_eq!(ordinalize(31), "31st");
+ assert_eq!(ordinalize(32), "32nd");
+ assert_eq!(ordinalize(33), "33rd");
+ assert_eq!(ordinalize(34), "34th");
+ // ...
+ assert_eq!(ordinalize(7010), "7010th");
+ assert_eq!(ordinalize(7011), "7011th");
+ assert_eq!(ordinalize(7012), "7012th");
+ assert_eq!(ordinalize(7013), "7013th");
+ assert_eq!(ordinalize(7014), "7014th");
+ // ...
+ assert_eq!(ordinalize(7020), "7020th");
+ assert_eq!(ordinalize(7021), "7021st");
+ assert_eq!(ordinalize(7022), "7022nd");
+ assert_eq!(ordinalize(7023), "7023rd");
+ assert_eq!(ordinalize(7024), "7024th");
+}
| AssocItemRibKind
| ModuleRibKind(..)
| MacroDefinition(..)
+ | InlineAsmSymRibKind
| ForwardGenericParamBanRibKind => {
// Nothing to do. Continue.
continue;
}
return Res::Err;
}
- InlineAsmSymRibKind => {
- let features = self.session.features_untracked();
- if !features.generic_const_exprs {
- if let Some(span) = finalize {
- self.report_error(
- span,
- ResolutionError::ParamInNonTrivialAnonConst {
- name: rib_ident.name,
- is_type: true,
- },
- );
- }
- return Res::Err;
- }
- continue;
- }
};
if let Some(span) = finalize {
| AssocItemRibKind
| ModuleRibKind(..)
| MacroDefinition(..)
+ | InlineAsmSymRibKind
| ForwardGenericParamBanRibKind => continue,
ConstantItemRibKind(trivial, _) => {
}
return Res::Err;
}
- InlineAsmSymRibKind => {
- let features = self.session.features_untracked();
- if !features.generic_const_exprs {
- if let Some(span) = finalize {
- self.report_error(
- span,
- ResolutionError::ParamInNonTrivialAnonConst {
- name: rib_ident.name,
- is_type: false,
- },
- );
- }
- return Res::Err;
- }
- continue;
- }
};
// This was an attempt to use a const parameter outside its scope.
self.diagnostic_metadata.current_where_predicate = previous_value;
}
+ fn visit_inline_asm(&mut self, asm: &'ast InlineAsm) {
+ for (op, _) in &asm.operands {
+ match op {
+ InlineAsmOperand::In { expr, .. }
+ | InlineAsmOperand::Out { expr: Some(expr), .. }
+ | InlineAsmOperand::InOut { expr, .. } => self.visit_expr(expr),
+ InlineAsmOperand::Out { expr: None, .. } => {}
+ InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
+ self.visit_expr(in_expr);
+ if let Some(out_expr) = out_expr {
+ self.visit_expr(out_expr);
+ }
+ }
+ InlineAsmOperand::Const { anon_const, .. } => {
+ // Although this is `DefKind::AnonConst`, it is allowed to reference outer
+ // generic parameters like an inline const.
+ self.resolve_inline_const(anon_const);
+ }
+ InlineAsmOperand::Sym { sym } => self.visit_inline_asm_sym(sym),
+ }
+ }
+ }
+
fn visit_inline_asm_sym(&mut self, sym: &'ast InlineAsmSym) {
// This is similar to the code for AnonConst.
self.with_rib(ValueNS, InlineAsmSymRibKind, |this| {
}
self.uninsert_lifetime_on_error(lifetime, def.unwrap());
}
+ if let hir::Node::Item(hir::Item {
+ kind: hir::ItemKind::OpaqueTy { .. }, ..
+ }) = self.tcx.hir().get(parent_id)
+ {
+ if !self.trait_definition_only {
+ let mut err = self.tcx.sess.struct_span_err(
+ lifetime.span,
+ "higher kinded lifetime bounds on nested opaque types are not supported yet",
+ );
+ err.span_note(self.tcx.def_span(def_id), "lifetime declared here");
+ err.emit();
+ }
+ self.uninsert_lifetime_on_error(lifetime, def.unwrap());
+ }
}
// We want to start our early-bound indices at the end of the parent scope,
let parent_def_id = self.tcx.parent(def_id);
if let Some(def_id) = parent_def_id.as_local() {
// lifetimes in `derive` expansions don't count (Issue #53738)
- if self
- .tcx
- .get_attrs(def_id.to_def_id())
- .iter()
- .any(|attr| attr.has_name(sym::automatically_derived))
- {
+ if self.tcx.has_attr(def_id.to_def_id(), sym::automatically_derived) {
continue;
}
use smallvec::{smallvec, SmallVec};
use std::cell::{Cell, RefCell};
use std::collections::BTreeSet;
-use std::{cmp, fmt, mem, ptr};
+use std::{cmp, fmt, ptr};
use tracing::debug;
use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion};
registered_attrs: FxHashSet<Ident>,
registered_tools: RegisteredTools,
macro_use_prelude: FxHashMap<Symbol, &'a NameBinding<'a>>,
- /// FIXME: The only user of this is a doc link resolution hack for rustdoc.
- all_macro_rules: FxHashMap<Symbol, Res>,
macro_map: FxHashMap<DefId, Lrc<SyntaxExtension>>,
dummy_ext_bang: Lrc<SyntaxExtension>,
dummy_ext_derive: Lrc<SyntaxExtension>,
local_macro_def_scopes: FxHashMap<LocalDefId, Module<'a>>,
ast_transform_scopes: FxHashMap<LocalExpnId, Module<'a>>,
unused_macros: FxHashMap<LocalDefId, (NodeId, Ident)>,
+ unused_macro_rules: FxHashMap<(LocalDefId, usize), (Ident, Span)>,
proc_macro_stubs: FxHashSet<LocalDefId>,
/// Traces collected during macro resolution and validated when it's complete.
single_segment_macro_resolutions:
registered_attrs,
registered_tools,
macro_use_prelude: FxHashMap::default(),
- all_macro_rules: Default::default(),
macro_map: FxHashMap::default(),
dummy_ext_bang: Lrc::new(SyntaxExtension::dummy_bang(session.edition())),
dummy_ext_derive: Lrc::new(SyntaxExtension::dummy_derive(session.edition())),
potentially_unused_imports: Vec::new(),
struct_constructors: Default::default(),
unused_macros: Default::default(),
+ unused_macro_rules: Default::default(),
proc_macro_stubs: Default::default(),
single_segment_macro_resolutions: Default::default(),
multi_segment_macro_resolutions: Default::default(),
}
}
- // For rustdoc.
- pub fn take_all_macro_rules(&mut self) -> FxHashMap<Symbol, Res> {
- mem::take(&mut self.all_macro_rules)
- }
-
/// For rustdoc.
/// For local modules returns only reexports, for external modules returns all children.
pub fn module_children_or_reexports(&self, def_id: DefId) -> Vec<ModChild> {
}
/// For rustdoc.
- pub fn macro_rules_scope(&self, def_id: LocalDefId) -> MacroRulesScopeRef<'a> {
- *self.macro_rules_scopes.get(&def_id).expect("not a `macro_rules` item")
+ pub fn macro_rules_scope(&self, def_id: LocalDefId) -> (MacroRulesScopeRef<'a>, Res) {
+ let scope = *self.macro_rules_scopes.get(&def_id).expect("not a `macro_rules` item");
+ match scope.get() {
+ MacroRulesScope::Binding(mb) => (scope, mb.binding.res()),
+ _ => unreachable!(),
+ }
}
/// Retrieves the span of the given `DefId` if `DefId` is in the local crate.
use rustc_hir::def_id::{CrateNum, LocalDefId};
use rustc_middle::middle::stability;
use rustc_middle::ty::RegisteredTools;
-use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE, UNUSED_MACROS};
+use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE};
+use rustc_session::lint::builtin::{UNUSED_MACROS, UNUSED_MACRO_RULES};
use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_session::parse::feature_err;
use rustc_session::Session;
Ok(ext)
}
+ fn record_macro_rule_usage(&mut self, id: NodeId, rule_i: usize) {
+ let did = self.local_def_id(id);
+ self.unused_macro_rules.remove(&(did, rule_i));
+ }
+
fn check_unused_macros(&mut self) {
for (_, &(node_id, ident)) in self.unused_macros.iter() {
self.lint_buffer.buffer_lint(
&format!("unused macro definition: `{}`", ident.as_str()),
);
}
+ for (&(def_id, arm_i), &(ident, rule_span)) in self.unused_macro_rules.iter() {
+ if self.unused_macros.contains_key(&def_id) {
+ // We already lint the entire macro as unused
+ continue;
+ }
+ let node_id = self.def_id_to_node_id[def_id];
+ self.lint_buffer.buffer_lint(
+ UNUSED_MACRO_RULES,
+ node_id,
+ rule_span,
+ &format!(
+ "{} rule of macro `{}` is never used",
+ crate::diagnostics::ordinalize(arm_i + 1),
+ ident.as_str()
+ ),
+ );
+ }
}
fn has_derive_copy(&self, expn_id: LocalExpnId) -> bool {
}
}
- /// Compile the macro into a `SyntaxExtension` and possibly replace
- /// its expander to a pre-defined one for built-in macros.
- crate fn compile_macro(&mut self, item: &ast::Item, edition: Edition) -> SyntaxExtension {
- let mut result = compile_declarative_macro(
+ /// Compile the macro into a `SyntaxExtension` and its rule spans.
+ ///
+ /// Possibly replace its expander to a pre-defined one for built-in macros.
+ crate fn compile_macro(
+ &mut self,
+ item: &ast::Item,
+ edition: Edition,
+ ) -> (SyntaxExtension, Vec<Span>) {
+ let (mut result, mut rule_spans) = compile_declarative_macro(
&self.session,
self.session.features_untracked(),
item,
match mem::replace(builtin_macro, BuiltinMacroState::AlreadySeen(item.span)) {
BuiltinMacroState::NotYetSeen(ext) => {
result.kind = ext;
+ rule_spans = Vec::new();
if item.id != ast::DUMMY_NODE_ID {
self.builtin_macro_kinds
.insert(self.local_def_id(item.id), result.macro_kind());
}
}
- result
+ (result, rule_spans)
}
}
variant: &'tcx ty::VariantDef,
rest: Option<&'tcx hir::Expr<'tcx>>,
) {
- if let Some(struct_lit_data) = self.save_ctxt.get_expr_data(ex) {
+ if let Some(_ex_res_data) = self.save_ctxt.get_expr_data(ex) {
if let hir::QPath::Resolved(_, path) = path {
self.write_sub_paths_truncated(path);
}
- down_cast_data!(struct_lit_data, RefData, ex.span);
+ // For MyEnum::MyVariant, get_expr_data gives us MyEnum, not MyVariant.
+ // For recording the span's ref id, we want MyVariant.
if !generated_code(ex.span) {
- self.dumper.dump_ref(struct_lit_data);
+ let sub_span = path.last_segment_span();
+ let span = self.save_ctxt.span_from_span(sub_span);
+ let reff =
+ Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(variant.def_id) };
+ self.dumper.dump_ref(reff);
}
for field in fields {
};
let kind = match kind {
- "dylib" => NativeLibKind::Dylib { as_needed: None },
- "framework" => NativeLibKind::Framework { as_needed: None },
"static" => NativeLibKind::Static { bundle: None, whole_archive: None },
"static-nobundle" => {
early_warn(
error_format,
"library kind `static-nobundle` has been superseded by specifying \
- `-bundle` on library kind `static`. Try `static:-bundle`",
+ modifier `-bundle` with library kind `static`. Try `static:-bundle`",
);
- if modifiers.is_some() {
- early_error(
- error_format,
- "linking modifier can't be used with library kind `static-nobundle`",
- )
- }
if !nightly_options::match_is_nightly_build(matches) {
early_error(
error_format,
- "library kind `static-nobundle` are currently unstable and only accepted on \
- the nightly compiler",
+ "library kind `static-nobundle` is unstable \
+ and only accepted on the nightly compiler",
);
}
NativeLibKind::Static { bundle: Some(false), whole_archive: None }
}
- s => early_error(
+ "dylib" => NativeLibKind::Dylib { as_needed: None },
+ "framework" => NativeLibKind::Framework { as_needed: None },
+ _ => early_error(
error_format,
- &format!("unknown library kind `{s}`, expected one of dylib, framework, or static"),
+ &format!("unknown library kind `{kind}`, expected one of: static, dylib, framework"),
),
};
match modifiers {
error_format: ErrorOutputType,
matches: &getopts::Matches,
) -> (NativeLibKind, Option<bool>) {
- let report_unstable_modifier = |modifier| {
- if !nightly_options::is_unstable_enabled(matches) {
- let why = if nightly_options::match_is_nightly_build(matches) {
- " and only accepted on the nightly compiler"
- } else {
- ", the `-Z unstable-options` flag must also be passed to use it"
- };
- early_error(
- error_format,
- &format!("{modifier} linking modifier is currently unstable{why}"),
- )
- }
- };
-
- let mut has_duplicate_modifiers = false;
let mut verbatim = None;
for modifier in modifiers.split(',') {
let (modifier, value) = match modifier.strip_prefix(&['+', '-']) {
None => early_error(
error_format,
"invalid linking modifier syntax, expected '+' or '-' prefix \
- before one of: bundle, verbatim, whole-archive, as-needed",
+ before one of: bundle, verbatim, whole-archive, as-needed",
),
};
+ let report_unstable_modifier = || {
+ if !nightly_options::is_unstable_enabled(matches) {
+ let why = if nightly_options::match_is_nightly_build(matches) {
+ " and only accepted on the nightly compiler"
+ } else {
+ ", the `-Z unstable-options` flag must also be passed to use it"
+ };
+ early_error(
+ error_format,
+ &format!("linking modifier `{modifier}` is unstable{why}"),
+ )
+ }
+ };
+ let assign_modifier = |dst: &mut Option<bool>| {
+ if dst.is_some() {
+ let msg = format!("multiple `{modifier}` modifiers in a single `-l` option");
+ early_error(error_format, &msg)
+ } else {
+ *dst = Some(value);
+ }
+ };
match (modifier, &mut kind) {
("bundle", NativeLibKind::Static { bundle, .. }) => {
- report_unstable_modifier(modifier);
- if bundle.is_some() {
- has_duplicate_modifiers = true;
- }
- *bundle = Some(value);
+ report_unstable_modifier();
+ assign_modifier(bundle)
}
("bundle", _) => early_error(
error_format,
- "bundle linking modifier is only compatible with \
- `static` linking kind",
+ "linking modifier `bundle` is only compatible with `static` linking kind",
),
("verbatim", _) => {
- report_unstable_modifier(modifier);
- if verbatim.is_some() {
- has_duplicate_modifiers = true;
- }
- verbatim = Some(value);
+ report_unstable_modifier();
+ assign_modifier(&mut verbatim)
}
("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
- if whole_archive.is_some() {
- has_duplicate_modifiers = true;
- }
- *whole_archive = Some(value);
+ assign_modifier(whole_archive)
}
("whole-archive", _) => early_error(
error_format,
- "whole-archive linking modifier is only compatible with \
- `static` linking kind",
+ "linking modifier `whole-archive` is only compatible with `static` linking kind",
),
("as-needed", NativeLibKind::Dylib { as_needed })
| ("as-needed", NativeLibKind::Framework { as_needed }) => {
- report_unstable_modifier(modifier);
- if as_needed.is_some() {
- has_duplicate_modifiers = true;
- }
- *as_needed = Some(value);
+ report_unstable_modifier();
+ assign_modifier(as_needed)
}
("as-needed", _) => early_error(
error_format,
- "as-needed linking modifier is only compatible with \
- `dylib` and `framework` linking kinds",
+ "linking modifier `as-needed` is only compatible with \
+ `dylib` and `framework` linking kinds",
),
// Note: this error also excludes the case with empty modifier
_ => early_error(
error_format,
&format!(
- "unrecognized linking modifier `{modifier}`, expected one \
- of: bundle, verbatim, whole-archive, as-needed"
+ "unknown linking modifier `{modifier}`, expected one \
+ of: bundle, verbatim, whole-archive, as-needed"
),
),
}
}
- if has_duplicate_modifiers {
- report_unstable_modifier("duplicating")
- }
(kind, verbatim)
}
None => (name, None),
Some((name, new_name)) => (name.to_string(), Some(new_name.to_owned())),
};
+ if name.is_empty() {
+ early_error(error_format, "library name must not be empty");
+ }
NativeLib { name, new_name, kind, verbatim }
})
.collect()
}
}
-impl FileNameDisplay<'_> {
- pub fn to_string_lossy(&self) -> Cow<'_, str> {
+impl<'a> FileNameDisplay<'a> {
+ pub fn to_string_lossy(&self) -> Cow<'a, str> {
match self.inner {
FileName::Real(ref inner) => inner.to_string_lossy(self.display_pref),
_ => Cow::from(format!("{}", self)),
}
/// The hash of the on-disk source file used for debug info.
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
#[derive(HashStable_Generic, Encodable, Decodable)]
pub struct SourceFileHash {
pub kind: SourceFileHashAlgorithm,
/// The return value is the remapped path and a boolean indicating whether
/// the path was affected by the mapping.
pub fn map_prefix(&self, path: PathBuf) -> (PathBuf, bool) {
- // NOTE: We are iterating over the mapping entries from last to first
- // because entries specified later on the command line should
- // take precedence.
- for &(ref from, ref to) in self.mapping.iter().rev() {
- if let Ok(rest) = path.strip_prefix(from) {
- let remapped = if rest.as_os_str().is_empty() {
- // This is subtle, joining an empty path onto e.g. `foo/bar` will
- // result in `foo/bar/`, that is, there'll be an additional directory
- // separator at the end. This can lead to duplicated directory separators
- // in remapped paths down the line.
- // So, if we have an exact match, we just return that without a call
- // to `Path::join()`.
- to.clone()
- } else {
- to.join(rest)
- };
+ if path.as_os_str().is_empty() {
+ // Exit early if the path is empty and therefore there's nothing to remap.
+ // This is mostly to reduce spam for `RUSTC_LOG=[remap_path_prefix]`.
+ return (path, false);
+ }
- return (remapped, true);
+ return remap_path_prefix(&self.mapping, path);
+
+ #[instrument(level = "debug", skip(mapping))]
+ fn remap_path_prefix(mapping: &[(PathBuf, PathBuf)], path: PathBuf) -> (PathBuf, bool) {
+ // NOTE: We are iterating over the mapping entries from last to first
+ // because entries specified later on the command line should
+ // take precedence.
+ for &(ref from, ref to) in mapping.iter().rev() {
+ debug!("Trying to apply {:?} => {:?}", from, to);
+
+ if let Ok(rest) = path.strip_prefix(from) {
+ let remapped = if rest.as_os_str().is_empty() {
+ // This is subtle, joining an empty path onto e.g. `foo/bar` will
+ // result in `foo/bar/`, that is, there'll be an additional directory
+ // separator at the end. This can lead to duplicated directory separators
+ // in remapped paths down the line.
+ // So, if we have an exact match, we just return that without a call
+ // to `Path::join()`.
+ to.clone()
+ } else {
+ to.join(rest)
+ };
+ debug!("Match - remapped {:?} => {:?}", path, remapped);
+
+ return (remapped, true);
+ } else {
+ debug!("No match - prefix {:?} does not match {:?}", from, path);
+ }
}
- }
- (path, false)
+ debug!("Path {:?} was not remapped", path);
+ (path, false)
+ }
}
fn map_filename_prefix(&self, file: &FileName) -> (FileName, bool) {
other => (other.clone(), false),
}
}
+
+ /// Expand a relative path to an absolute path with remapping taken into account.
+ /// Use this when absolute paths are required (e.g. debuginfo or crate metadata).
+ ///
+ /// The resulting `RealFileName` will have its `local_path` portion erased if
+ /// possible (i.e. if there's also a remapped path).
+ pub fn to_embeddable_absolute_path(
+ &self,
+ file_path: RealFileName,
+ working_directory: &RealFileName,
+ ) -> RealFileName {
+ match file_path {
+ // Anything that's already remapped we don't modify, except for erasing
+ // the `local_path` portion.
+ RealFileName::Remapped { local_path: _, virtual_name } => {
+ RealFileName::Remapped {
+ // We do not want any local path to be exported into metadata
+ local_path: None,
+ // We use the remapped name verbatim, even if it looks like a relative
+ // path. The assumption is that the user doesn't want us to further
+ // process paths that have gone through remapping.
+ virtual_name,
+ }
+ }
+
+ RealFileName::LocalPath(unmapped_file_path) => {
+ // If no remapping has been applied yet, try to do so
+ let (new_path, was_remapped) = self.map_prefix(unmapped_file_path);
+ if was_remapped {
+ // It was remapped, so don't modify further
+ return RealFileName::Remapped { local_path: None, virtual_name: new_path };
+ }
+
+ if new_path.is_absolute() {
+ // No remapping has applied to this path and it is absolute,
+ // so the working directory cannot influence it either, so
+ // we are done.
+ return RealFileName::LocalPath(new_path);
+ }
+
+ debug_assert!(new_path.is_relative());
+ let unmapped_file_path_rel = new_path;
+
+ match working_directory {
+ RealFileName::LocalPath(unmapped_working_dir_abs) => {
+ let file_path_abs = unmapped_working_dir_abs.join(unmapped_file_path_rel);
+
+ // Although neither `working_directory` nor the file name were subject
+ // to path remapping, the concatenation between the two may be. Hence
+ // we need to do a remapping here.
+ let (file_path_abs, was_remapped) = self.map_prefix(file_path_abs);
+ if was_remapped {
+ RealFileName::Remapped {
+ // Erase the actual path
+ local_path: None,
+ virtual_name: file_path_abs,
+ }
+ } else {
+ // No kind of remapping applied to this path, so
+ // we leave it as it is.
+ RealFileName::LocalPath(file_path_abs)
+ }
+ }
+ RealFileName::Remapped {
+ local_path: _,
+ virtual_name: remapped_working_dir_abs,
+ } => {
+ // If working_directory has been remapped, then we emit
+ // Remapped variant as the expanded path won't be valid
+ RealFileName::Remapped {
+ local_path: None,
+ virtual_name: Path::new(remapped_working_dir_abs)
+ .join(unmapped_file_path_rel),
+ }
+ }
+ }
+ }
+ }
+ }
}
}
}
-fn map_path_prefix(mapping: &FilePathMapping, path: &str) -> String {
+// Takes a unix-style path and returns a platform specific path.
+fn path(p: &str) -> PathBuf {
+ path_str(p).into()
+}
+
+// Takes a unix-style path and returns a platform specific path.
+fn path_str(p: &str) -> String {
+ #[cfg(not(windows))]
+ {
+ return p.into();
+ }
+
+ #[cfg(windows)]
+ {
+ let mut path = p.replace('/', "\\");
+ if let Some(rest) = path.strip_prefix('\\') {
+ path = ["X:\\", rest].concat();
+ }
+
+ path
+ }
+}
+
+fn map_path_prefix(mapping: &FilePathMapping, p: &str) -> String {
// It's important that we convert to a string here because that's what
// later stages do too (e.g. in the backend), and comparing `Path` values
// won't catch some differences at the string level, e.g. "abc" and "abc/"
// compare as equal.
- mapping.map_prefix(path.into()).0.to_string_lossy().to_string()
+ mapping.map_prefix(path(p)).0.to_string_lossy().to_string()
}
-#[cfg(unix)]
#[test]
fn path_prefix_remapping() {
// Relative to relative
{
- let mapping = &FilePathMapping::new(vec![("abc/def".into(), "foo".into())]);
+ let mapping = &FilePathMapping::new(vec![(path("abc/def"), path("foo"))]);
- assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), "foo/src/main.rs");
- assert_eq!(map_path_prefix(mapping, "abc/def"), "foo");
+ assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), path_str("foo/src/main.rs"));
+ assert_eq!(map_path_prefix(mapping, "abc/def"), path_str("foo"));
}
// Relative to absolute
{
- let mapping = &FilePathMapping::new(vec![("abc/def".into(), "/foo".into())]);
+ let mapping = &FilePathMapping::new(vec![(path("abc/def"), path("/foo"))]);
- assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), "/foo/src/main.rs");
- assert_eq!(map_path_prefix(mapping, "abc/def"), "/foo");
+ assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), path_str("/foo/src/main.rs"));
+ assert_eq!(map_path_prefix(mapping, "abc/def"), path_str("/foo"));
}
// Absolute to relative
{
- let mapping = &FilePathMapping::new(vec![("/abc/def".into(), "foo".into())]);
+ let mapping = &FilePathMapping::new(vec![(path("/abc/def"), path("foo"))]);
- assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), "foo/src/main.rs");
- assert_eq!(map_path_prefix(mapping, "/abc/def"), "foo");
+ assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), path_str("foo/src/main.rs"));
+ assert_eq!(map_path_prefix(mapping, "/abc/def"), path_str("foo"));
}
// Absolute to absolute
{
- let mapping = &FilePathMapping::new(vec![("/abc/def".into(), "/foo".into())]);
+ let mapping = &FilePathMapping::new(vec![(path("/abc/def"), path("/foo"))]);
- assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), "/foo/src/main.rs");
- assert_eq!(map_path_prefix(mapping, "/abc/def"), "/foo");
+ assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), path_str("/foo/src/main.rs"));
+ assert_eq!(map_path_prefix(mapping, "/abc/def"), path_str("/foo"));
}
}
-#[cfg(windows)]
#[test]
-fn path_prefix_remapping_from_relative2() {
- // Relative to relative
- {
- let mapping = &FilePathMapping::new(vec![("abc\\def".into(), "foo".into())]);
+fn path_prefix_remapping_expand_to_absolute() {
+ // "virtual" working directory is relative path
+ let mapping =
+ &FilePathMapping::new(vec![(path("/foo"), path("FOO")), (path("/bar"), path("BAR"))]);
+ let working_directory = path("/foo");
+ let working_directory = RealFileName::Remapped {
+ local_path: Some(working_directory.clone()),
+ virtual_name: mapping.map_prefix(working_directory).0,
+ };
+
+ assert_eq!(working_directory.remapped_path_if_available(), path("FOO"));
+
+ // Unmapped absolute path
+ assert_eq!(
+ mapping.to_embeddable_absolute_path(
+ RealFileName::LocalPath(path("/foo/src/main.rs")),
+ &working_directory
+ ),
+ RealFileName::Remapped { local_path: None, virtual_name: path("FOO/src/main.rs") }
+ );
- assert_eq!(map_path_prefix(mapping, "abc\\def\\src\\main.rs"), "foo\\src\\main.rs");
- assert_eq!(map_path_prefix(mapping, "abc\\def"), "foo");
- }
+ // Unmapped absolute path with unrelated working directory
+ assert_eq!(
+ mapping.to_embeddable_absolute_path(
+ RealFileName::LocalPath(path("/bar/src/main.rs")),
+ &working_directory
+ ),
+ RealFileName::Remapped { local_path: None, virtual_name: path("BAR/src/main.rs") }
+ );
- // Relative to absolute
- {
- let mapping = &FilePathMapping::new(vec![("abc\\def".into(), "X:\\foo".into())]);
+ // Unmapped absolute path that does not match any prefix
+ assert_eq!(
+ mapping.to_embeddable_absolute_path(
+ RealFileName::LocalPath(path("/quux/src/main.rs")),
+ &working_directory
+ ),
+ RealFileName::LocalPath(path("/quux/src/main.rs")),
+ );
- assert_eq!(map_path_prefix(mapping, "abc\\def\\src\\main.rs"), "X:\\foo\\src\\main.rs");
- assert_eq!(map_path_prefix(mapping, "abc\\def"), "X:\\foo");
- }
+ // Unmapped relative path
+ assert_eq!(
+ mapping.to_embeddable_absolute_path(
+ RealFileName::LocalPath(path("src/main.rs")),
+ &working_directory
+ ),
+ RealFileName::Remapped { local_path: None, virtual_name: path("FOO/src/main.rs") }
+ );
- // Absolute to relative
- {
- let mapping = &FilePathMapping::new(vec![("X:\\abc\\def".into(), "foo".into())]);
+ // Unmapped relative path with `./`
+ assert_eq!(
+ mapping.to_embeddable_absolute_path(
+ RealFileName::LocalPath(path("./src/main.rs")),
+ &working_directory
+ ),
+ RealFileName::Remapped { local_path: None, virtual_name: path("FOO/src/main.rs") }
+ );
- assert_eq!(map_path_prefix(mapping, "X:\\abc\\def\\src\\main.rs"), "foo\\src\\main.rs");
- assert_eq!(map_path_prefix(mapping, "X:\\abc\\def"), "foo");
- }
+ // Unmapped relative path that does not match any prefix
+ assert_eq!(
+ mapping.to_embeddable_absolute_path(
+ RealFileName::LocalPath(path("quux/src/main.rs")),
+ &RealFileName::LocalPath(path("/abc")),
+ ),
+ RealFileName::LocalPath(path("/abc/quux/src/main.rs")),
+ );
- // Absolute to absolute
- {
- let mapping = &FilePathMapping::new(vec![("X:\\abc\\def".into(), "X:\\foo".into())]);
+ // Already remapped absolute path
+ assert_eq!(
+ mapping.to_embeddable_absolute_path(
+ RealFileName::Remapped {
+ local_path: Some(path("/foo/src/main.rs")),
+ virtual_name: path("FOO/src/main.rs"),
+ },
+ &working_directory
+ ),
+ RealFileName::Remapped { local_path: None, virtual_name: path("FOO/src/main.rs") }
+ );
- assert_eq!(map_path_prefix(mapping, "X:\\abc\\def\\src\\main.rs"), "X:\\foo\\src\\main.rs");
- assert_eq!(map_path_prefix(mapping, "X:\\abc\\def"), "X:\\foo");
- }
+ // Already remapped absolute path, with unrelated working directory
+ assert_eq!(
+ mapping.to_embeddable_absolute_path(
+ RealFileName::Remapped {
+ local_path: Some(path("/bar/src/main.rs")),
+ virtual_name: path("BAR/src/main.rs"),
+ },
+ &working_directory
+ ),
+ RealFileName::Remapped { local_path: None, virtual_name: path("BAR/src/main.rs") }
+ );
+
+ // Already remapped relative path
+ assert_eq!(
+ mapping.to_embeddable_absolute_path(
+ RealFileName::Remapped { local_path: None, virtual_name: path("XYZ/src/main.rs") },
+ &working_directory
+ ),
+ RealFileName::Remapped { local_path: None, virtual_name: path("XYZ/src/main.rs") }
+ );
}
ptr_null,
ptr_null_mut,
ptr_offset_from,
+ ptr_offset_from_unsigned,
pub_macro_rules,
pub_restricted,
pure,
thread_local_macro,
thumb2,
thumb_mode: "thumb-mode",
+ tmm_reg,
todo_macro,
tool_attributes,
tool_lints,
impl SymbolNamesTest<'_> {
fn process_attrs(&mut self, def_id: LocalDefId) {
let tcx = self.tcx;
- for attr in tcx.get_attrs(def_id.to_def_id()).iter() {
- if attr.has_name(SYMBOL_NAME) {
- let def_id = def_id.to_def_id();
- let instance = Instance::new(
- def_id,
- tcx.erase_regions(InternalSubsts::identity_for_item(tcx, def_id)),
- );
- let mangled = tcx.symbol_name(instance);
- tcx.sess.span_err(attr.span, &format!("symbol-name({})", mangled));
- if let Ok(demangling) = rustc_demangle::try_demangle(mangled.name) {
- tcx.sess.span_err(attr.span, &format!("demangling({})", demangling));
- tcx.sess.span_err(attr.span, &format!("demangling-alt({:#})", demangling));
- }
- } else if attr.has_name(DEF_PATH) {
- let path = with_no_trimmed_paths!(tcx.def_path_str(def_id.to_def_id()));
- tcx.sess.span_err(attr.span, &format!("def-path({})", path));
+ // The formatting of `tag({})` is chosen so that tests can elect
+ // to test the entirety of the string, if they choose, or else just
+ // some subset.
+ for attr in tcx.get_attrs(def_id.to_def_id(), SYMBOL_NAME) {
+ let def_id = def_id.to_def_id();
+ let instance = Instance::new(
+ def_id,
+ tcx.erase_regions(InternalSubsts::identity_for_item(tcx, def_id)),
+ );
+ let mangled = tcx.symbol_name(instance);
+ tcx.sess.span_err(attr.span, &format!("symbol-name({})", mangled));
+ if let Ok(demangling) = rustc_demangle::try_demangle(mangled.name) {
+ tcx.sess.span_err(attr.span, &format!("demangling({})", demangling));
+ tcx.sess.span_err(attr.span, &format!("demangling-alt({:#})", demangling));
}
+ }
- // (*) The formatting of `tag({})` is chosen so that tests can elect
- // to test the entirety of the string, if they choose, or else just
- // some subset.
+ for attr in tcx.get_attrs(def_id.to_def_id(), DEF_PATH) {
+ let path = with_no_trimmed_paths!(tcx.def_path_str(def_id.to_def_id()));
+ tcx.sess.span_err(attr.span, &format!("def-path({})", path));
}
}
}
use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::print::{Print, Printer};
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
-use rustc_middle::ty::{self, FloatTy, Instance, IntTy, Ty, TyCtxt, TypeFoldable, UintTy};
+use rustc_middle::ty::{
+ self, EarlyBinder, FloatTy, Instance, IntTy, Ty, TyCtxt, TypeFoldable, UintTy,
+};
use rustc_span::symbol::kw;
use rustc_target::abi::call::FnAbi;
use rustc_target::abi::Integer;
let mut param_env = self.tcx.param_env_reveal_all_normalized(impl_def_id);
if !substs.is_empty() {
- param_env = param_env.subst(self.tcx, substs);
+ param_env = EarlyBinder(param_env).subst(self.tcx, substs);
}
match &mut impl_trait_ref {
// These should never show up as `path_append` arguments.
DefPathData::CrateRoot
- | DefPathData::Misc
+ | DefPathData::Use
+ | DefPathData::GlobalAsm
| DefPathData::Impl
| DefPathData::MacroNs(_)
| DefPathData::LifetimeNs(_) => {
mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7,
st0, st1, st2, st3, st4, st5, st6, st7,
+ tmm0, tmm1, tmm2, tmm3, tmm4, tmm5, tmm6, tmm7,
}
},
InlineAsmClobberAbi::X86_64Win => clobbered_regs! {
mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7,
st0, st1, st2, st3, st4, st5, st6, st7,
+ tmm0, tmm1, tmm2, tmm3, tmm4, tmm5, tmm6, tmm7,
}
},
InlineAsmClobberAbi::AArch64 => clobbered_regs! {
kreg0,
mmx_reg,
x87_reg,
+ tmm_reg,
}
}
Self::xmm_reg | Self::ymm_reg | Self::zmm_reg => &['x', 'y', 'z'],
Self::kreg | Self::kreg0 => &[],
Self::mmx_reg | Self::x87_reg => &[],
+ Self::tmm_reg => &[],
}
}
},
Self::kreg | Self::kreg0 => None,
Self::mmx_reg | Self::x87_reg => None,
+ Self::tmm_reg => None,
}
}
Self::zmm_reg => Some(('z', "zmm0")),
Self::kreg | Self::kreg0 => None,
Self::mmx_reg | Self::x87_reg => None,
+ Self::tmm_reg => None,
}
}
},
Self::kreg0 => &[],
Self::mmx_reg | Self::x87_reg => &[],
+ Self::tmm_reg => &[],
}
}
}
st5: x87_reg = ["st(5)"],
st6: x87_reg = ["st(6)"],
st7: x87_reg = ["st(7)"],
+ tmm0: tmm_reg = ["tmm0"] % x86_64_only,
+ tmm1: tmm_reg = ["tmm1"] % x86_64_only,
+ tmm2: tmm_reg = ["tmm2"] % x86_64_only,
+ tmm3: tmm_reg = ["tmm3"] % x86_64_only,
+ tmm4: tmm_reg = ["tmm4"] % x86_64_only,
+ tmm5: tmm_reg = ["tmm5"] % x86_64_only,
+ tmm6: tmm_reg = ["tmm6"] % x86_64_only,
+ tmm7: tmm_reg = ["tmm7"] % x86_64_only,
#error = ["bp", "bpl", "ebp", "rbp"] =>
"the frame pointer cannot be used as an operand for inline asm",
#error = ["sp", "spl", "esp", "rsp"] =>
--- /dev/null
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::windows_gnullvm_base::opts();
+ base.max_atomic_width = Some(64);
+ base.features = "+neon,+fp-armv8".into();
+ base.linker = Some("aarch64-w64-mingw32-clang".into());
+
+ Target {
+ llvm_target: "aarch64-pc-windows-gnu".into(),
+ pointer_width: 64,
+ data_layout: "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: base,
+ }
+}
mod vxworks_base;
mod wasm_base;
mod windows_gnu_base;
+mod windows_gnullvm_base;
mod windows_msvc_base;
mod windows_uwp_gnu_base;
mod windows_uwp_msvc_base;
("i686-uwp-windows-gnu", i686_uwp_windows_gnu),
("x86_64-uwp-windows-gnu", x86_64_uwp_windows_gnu),
+ ("aarch64-pc-windows-gnullvm", aarch64_pc_windows_gnullvm),
+ ("x86_64-pc-windows-gnullvm", x86_64_pc_windows_gnullvm),
+
("aarch64-pc-windows-msvc", aarch64_pc_windows_msvc),
("aarch64-uwp-windows-msvc", aarch64_uwp_windows_msvc),
("x86_64-pc-windows-msvc", x86_64_pc_windows_msvc),
--- /dev/null
+use crate::spec::{cvs, LinkArgs, LinkerFlavor, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+ let pre_link_args = LinkArgs::from([(
+ LinkerFlavor::Gcc,
+ vec![
+ // We cannot use `-nodefaultlibs` because compiler-rt has to be passed
+ // as a path since it's not added to linker search path by the default.
+ // There were attemts to make it behave like libgcc (so one can just use -l<name>)
+ // but LLVM maintainers rejected it: https://reviews.llvm.org/D51440
+ "-nolibc".into(),
+ "--unwindlib=none".into(),
+ ],
+ )]);
+ let late_link_args = LinkArgs::from([(
+ LinkerFlavor::Gcc,
+ // Order of `late_link_args*` does not matter with LLD.
+ vec![
+ "-lmingw32".into(),
+ "-lmingwex".into(),
+ "-lmsvcrt".into(),
+ "-lkernel32".into(),
+ "-luser32".into(),
+ ],
+ )]);
+
+ TargetOptions {
+ os: "windows".into(),
+ env: "gnu".into(),
+ vendor: "pc".into(),
+ abi: "llvm".into(),
+ linker: Some("clang".into()),
+ dynamic_linking: true,
+ executables: true,
+ dll_prefix: "".into(),
+ dll_suffix: ".dll".into(),
+ exe_suffix: ".exe".into(),
+ families: cvs!["windows"],
+ is_like_windows: true,
+ allows_weak_linkage: false,
+ pre_link_args,
+ late_link_args,
+ abi_return_struct_as_int: true,
+ emit_debug_gdb_scripts: false,
+ requires_uwtable: true,
+ eh_frame_header: false,
+ no_default_libraries: false,
+ has_thread_local: true,
+
+ ..Default::default()
+ }
+}
--- /dev/null
+use crate::spec::{LinkerFlavor, Target};
+
+pub fn target() -> Target {
+ let mut base = super::windows_gnullvm_base::opts();
+ base.cpu = "x86-64".into();
+ let gcc_pre_link_args = base.pre_link_args.entry(LinkerFlavor::Gcc).or_default();
+ gcc_pre_link_args.push("-m64".into());
+ base.max_atomic_width = Some(64);
+ base.linker = Some("x86_64-w64-mingw32-clang".into());
+
+ Target {
+ llvm_target: "x86_64-pc-windows-gnu".into(),
+ pointer_width: 64,
+ data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
));
debug!(?definition_ty);
+ if !check_opaque_type_parameter_valid(
+ self.tcx,
+ opaque_type_key,
+ origin,
+ instantiated_ty.span,
+ ) {
+ return self.tcx.ty_error();
+ }
+
// Only check this for TAIT. RPIT already supports `src/test/ui/impl-trait/nested-return-type2.rs`
// on stable and we'd break that.
if let OpaqueTyOrigin::TyAlias = origin {
}
}
+fn check_opaque_type_parameter_valid(
+ tcx: TyCtxt<'_>,
+ opaque_type_key: OpaqueTypeKey<'_>,
+ origin: OpaqueTyOrigin,
+ span: Span,
+) -> bool {
+ match origin {
+ // No need to check return position impl trait (RPIT)
+ // because for type and const parameters they are correct
+ // by construction: we convert
+ //
+ // fn foo<P0..Pn>() -> impl Trait
+ //
+ // into
+ //
+ // type Foo<P0...Pn>
+ // fn foo<P0..Pn>() -> Foo<P0...Pn>.
+ //
+ // For lifetime parameters we convert
+ //
+ // fn foo<'l0..'ln>() -> impl Trait<'l0..'lm>
+ //
+ // into
+ //
+ // type foo::<'p0..'pn>::Foo<'q0..'qm>
+ // fn foo<l0..'ln>() -> foo::<'static..'static>::Foo<'l0..'lm>.
+ //
+ // which would error here on all of the `'static` args.
+ OpaqueTyOrigin::FnReturn(..) | OpaqueTyOrigin::AsyncFn(..) => return true,
+ // Check these
+ OpaqueTyOrigin::TyAlias => {}
+ }
+ let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
+ let mut seen_params: FxHashMap<_, Vec<_>> = FxHashMap::default();
+ for (i, arg) in opaque_type_key.substs.iter().enumerate() {
+ let arg_is_param = match arg.unpack() {
+ GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
+ GenericArgKind::Lifetime(lt) if lt.is_static() => {
+ tcx.sess
+ .struct_span_err(span, "non-defining opaque type use in defining scope")
+ .span_label(
+ tcx.def_span(opaque_generics.param_at(i, tcx).def_id),
+ "cannot use static lifetime; use a bound lifetime \
+ instead or remove the lifetime parameter from the \
+ opaque type",
+ )
+ .emit();
+ return false;
+ }
+ GenericArgKind::Lifetime(lt) => {
+ matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_))
+ }
+ GenericArgKind::Const(ct) => matches!(ct.val(), ty::ConstKind::Param(_)),
+ };
+
+ if arg_is_param {
+ seen_params.entry(arg).or_default().push(i);
+ } else {
+ // Prevent `fn foo() -> Foo<u32>` from being defining.
+ let opaque_param = opaque_generics.param_at(i, tcx);
+ tcx.sess
+ .struct_span_err(span, "non-defining opaque type use in defining scope")
+ .span_note(
+ tcx.def_span(opaque_param.def_id),
+ &format!(
+ "used non-generic {} `{}` for generic parameter",
+ opaque_param.kind.descr(),
+ arg,
+ ),
+ )
+ .emit();
+ return false;
+ }
+ }
+
+ for (_, indices) in seen_params {
+ if indices.len() > 1 {
+ let descr = opaque_generics.param_at(indices[0], tcx).kind.descr();
+ let spans: Vec<_> = indices
+ .into_iter()
+ .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id))
+ .collect();
+ tcx.sess
+ .struct_span_err(span, "non-defining opaque type use in defining scope")
+ .span_note(spans, &format!("{} used multiple times", descr))
+ .emit();
+ return false;
+ }
+ }
+ true
+}
+
struct ReverseMapper<'tcx> {
tcx: TyCtxt<'tcx>,
// seems likely that they should eventually be merged into more
// general routines.
-use crate::infer::{InferCtxt, TyCtxtInferExt};
+use crate::infer::TyCtxtInferExt;
use crate::traits::{
FulfillmentContext, ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine,
Unimplemented,
};
-use rustc_errors::ErrorGuaranteed;
-use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::traits::CodegenObligationError;
use rustc_middle::ty::{self, TyCtxt};
/// Attempts to resolve an obligation to an `ImplSource`. The result is
pub fn codegen_fulfill_obligation<'tcx>(
tcx: TyCtxt<'tcx>,
(param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>),
-) -> Result<&'tcx ImplSource<'tcx, ()>, ErrorGuaranteed> {
+) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> {
// Remove any references to regions; this helps improve caching.
let trait_ref = tcx.erase_regions(trait_ref);
// We expect the input to be fully normalized.
let selection = match selcx.select(&obligation) {
Ok(Some(selection)) => selection,
- Ok(None) => {
- // Ambiguity can happen when monomorphizing during trans
- // expands to some humongous type that never occurred
- // statically -- this humongous type can then overflow,
- // leading to an ambiguous result. So report this as an
- // overflow bug, since I believe this is the only case
- // where ambiguity can result.
- let reported = infcx.tcx.sess.delay_span_bug(
- rustc_span::DUMMY_SP,
- &format!(
- "encountered ambiguity selecting `{:?}` during codegen, presuming due to \
- overflow or prior type error",
- trait_ref
- ),
- );
- return Err(reported);
- }
- Err(Unimplemented) => {
- // This can trigger when we probe for the source of a `'static` lifetime requirement
- // on a trait object: `impl Foo for dyn Trait {}` has an implicit `'static` bound.
- // This can also trigger when we have a global bound that is not actually satisfied,
- // but was included during typeck due to the trivial_bounds feature.
- let guar = infcx.tcx.sess.delay_span_bug(
- rustc_span::DUMMY_SP,
- &format!(
- "Encountered error `Unimplemented` selecting `{:?}` during codegen",
- trait_ref
- ),
- );
- return Err(guar);
- }
+ Ok(None) => return Err(CodegenObligationError::Ambiguity),
+ Err(Unimplemented) => return Err(CodegenObligationError::Unimplemented),
Err(e) => {
bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref)
}
let impl_source = selection.map(|predicate| {
fulfill_cx.register_predicate_obligation(&infcx, predicate);
});
- let impl_source = drain_fulfillment_cx_or_panic(&infcx, &mut fulfill_cx, impl_source);
+
+ // In principle, we only need to do this so long as `impl_source`
+ // contains unbound type parameters. It could be a slight
+ // optimization to stop iterating early.
+ let errors = fulfill_cx.select_all_or_error(&infcx);
+ if !errors.is_empty() {
+ return Err(CodegenObligationError::FulfillmentError);
+ }
+
+ let impl_source = infcx.resolve_vars_if_possible(impl_source);
+ let impl_source = infcx.tcx.erase_regions(impl_source);
// Opaque types may have gotten their hidden types constrained, but we can ignore them safely
// as they will get constrained elsewhere, too.
Ok(&*tcx.arena.alloc(impl_source))
})
}
-
-// # Global Cache
-
-/// Finishes processes any obligations that remain in the
-/// fulfillment context, and then returns the result with all type
-/// variables removed and regions erased. Because this is intended
-/// for use outside of type inference, if any errors occur,
-/// it will panic. It is used during normalization and other cases
-/// where processing the obligations in `fulfill_cx` may cause
-/// type inference variables that appear in `result` to be
-/// unified, and hence we need to process those obligations to get
-/// the complete picture of the type.
-fn drain_fulfillment_cx_or_panic<'tcx, T>(
- infcx: &InferCtxt<'_, 'tcx>,
- fulfill_cx: &mut FulfillmentContext<'tcx>,
- result: T,
-) -> T
-where
- T: TypeFoldable<'tcx>,
-{
- debug!("drain_fulfillment_cx_or_panic()");
-
- // In principle, we only need to do this so long as `result`
- // contains unbound type parameters. It could be a slight
- // optimization to stop iterating early.
- let errors = fulfill_cx.select_all_or_error(infcx);
- if !errors.is_empty() {
- infcx.tcx.sess.delay_span_bug(
- rustc_span::DUMMY_SP,
- &format!(
- "Encountered errors `{:?}` resolving bounds outside of type inference",
- errors
- ),
- );
- }
-
- let result = infcx.resolve_vars_if_possible(result);
- infcx.tcx.erase_regions(result)
-}
let header = ty::ImplHeader {
impl_def_id,
- self_ty: tcx.type_of(impl_def_id).subst(tcx, impl_substs),
- trait_ref: tcx.impl_trait_ref(impl_def_id).subst(tcx, impl_substs),
+ self_ty: tcx.bound_type_of(impl_def_id).subst(tcx, impl_substs),
+ trait_ref: tcx.bound_impl_trait_ref(impl_def_id).map(|i| i.subst(tcx, impl_substs)),
predicates: tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs).predicates,
};
.substs
.types()
.flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate))
- .find(|ty| ty_is_local_constructor(*ty, in_crate));
+ .find(|&ty| ty_is_local_constructor(tcx, ty, in_crate));
debug!("orphan_check_trait_ref: uncovered ty local_type: `{:?}`", local_type);
ty: Ty<'tcx>,
in_crate: InCrate,
) -> Vec<Ty<'tcx>> {
- if ty_is_local_constructor(ty, in_crate) {
+ if ty_is_local_constructor(tcx, ty, in_crate) {
Vec::new()
} else {
match fundamental_ty_inner_tys(tcx, ty) {
}
}
-fn ty_is_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> bool {
+fn ty_is_local_constructor(tcx: TyCtxt<'_>, ty: Ty<'_>, in_crate: InCrate) -> bool {
debug!("ty_is_local_constructor({:?})", ty);
match *ty.kind() {
false
}
- ty::Closure(..) => {
- // Similar to the `Opaque` case (#83613).
- false
- }
-
ty::Dynamic(ref tt, ..) => {
if let Some(principal) = tt.principal() {
def_id_is_local(principal.def_id(), in_crate)
ty::Error(_) => true,
- ty::Generator(..) | ty::GeneratorWitness(..) => {
- bug!("ty_is_local invoked on unexpected type: {:?}", ty)
+ // These variants should never appear during coherence checking because they
+ // cannot be named directly.
+ //
+ // They could be indirectly used through an opaque type. While using opaque types
+ // in impls causes an error, this path can still be hit afterwards.
+ //
+ // See `test/ui/coherence/coherence-with-closure.rs` for an example where this
+ // could happens.
+ ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) => {
+ tcx.sess.delay_span_bug(
+ DUMMY_SP,
+ format!("ty_is_local invoked on closure or generator: {:?}", ty),
+ );
+ true
}
}
}
use rustc_middle::thir;
use rustc_middle::thir::abstract_const::{self, Node, NodeId, NotConstEvaluatable};
use rustc_middle::ty::subst::{Subst, SubstsRef};
-use rustc_middle::ty::{self, DelaySpanBugEmitted, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{self, DelaySpanBugEmitted, EarlyBinder, TyCtxt, TypeFoldable};
use rustc_session::lint;
use rustc_span::def_id::LocalDefId;
use rustc_span::Span;
pub fn root(self, tcx: TyCtxt<'tcx>) -> Node<'tcx> {
let node = self.inner.last().copied().unwrap();
match node {
- Node::Leaf(leaf) => Node::Leaf(leaf.subst(tcx, self.substs)),
- Node::Cast(kind, operand, ty) => Node::Cast(kind, operand, ty.subst(tcx, self.substs)),
+ Node::Leaf(leaf) => Node::Leaf(EarlyBinder(leaf).subst(tcx, self.substs)),
+ Node::Cast(kind, operand, ty) => {
+ Node::Cast(kind, operand, EarlyBinder(ty).subst(tcx, self.substs))
+ }
// Don't perform substitution on the following as they can't directly contain generic params
Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => node,
}
pub mod suggestions;
use super::{
- DerivedObligationCause, EvaluationResult, FulfillmentContext, FulfillmentError,
- FulfillmentErrorCode, ImplDerivedObligationCause, MismatchedProjectionTypes, Obligation,
- ObligationCause, ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote,
- OutputTypeParameterMismatch, Overflow, PredicateObligation, SelectionContext, SelectionError,
- TraitNotObjectSafe,
+ EvaluationResult, FulfillmentContext, FulfillmentError, FulfillmentErrorCode,
+ MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode,
+ OnUnimplementedDirective, OnUnimplementedNote, OutputTypeParameterMismatch, Overflow,
+ PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe,
};
use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
let mut code = obligation.cause.code();
let mut trait_pred = trait_predicate;
let mut peeled = false;
- loop {
- match &*code {
- ObligationCauseCode::FunctionArgumentObligation {
- parent_code,
- ..
- } => {
- code = &parent_code;
- }
- ObligationCauseCode::ImplDerivedObligation(
- box ImplDerivedObligationCause {
- derived:
- DerivedObligationCause {
- parent_code,
- parent_trait_pred,
- },
- ..
- },
- )
- | ObligationCauseCode::BuiltinDerivedObligation(
- DerivedObligationCause {
- parent_code,
- parent_trait_pred,
- },
- )
- | ObligationCauseCode::DerivedObligation(
- DerivedObligationCause {
- parent_code,
- parent_trait_pred,
- },
- ) => {
- peeled = true;
- code = &parent_code;
- trait_pred = *parent_trait_pred;
- }
- _ => break,
- };
+ while let Some((parent_code, parent_trait_pred)) = code.parent() {
+ code = parent_code;
+ if let Some(parent_trait_pred) = parent_trait_pred {
+ trait_pred = parent_trait_pred;
+ peeled = true;
+ }
}
let def_id = trait_pred.def_id();
// Mention *all* the `impl`s for the *top most* obligation, the
fn mk_trait_obligation_with_new_self_ty(
&self,
param_env: ty::ParamEnv<'tcx>,
- trait_ref: ty::PolyTraitPredicate<'tcx>,
- new_self_ty: Ty<'tcx>,
+ trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>,
) -> PredicateObligation<'tcx>;
fn maybe_report_ambiguity(
fn mk_trait_obligation_with_new_self_ty(
&self,
param_env: ty::ParamEnv<'tcx>,
- trait_ref: ty::PolyTraitPredicate<'tcx>,
- new_self_ty: Ty<'tcx>,
+ trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>,
) -> PredicateObligation<'tcx> {
- assert!(!new_self_ty.has_escaping_bound_vars());
-
- let trait_pred = trait_ref.map_bound_ref(|tr| ty::TraitPredicate {
+ let trait_pred = trait_ref_and_ty.map_bound_ref(|(tr, new_self_ty)| ty::TraitPredicate {
trait_ref: ty::TraitRef {
- substs: self.tcx.mk_substs_trait(new_self_ty, &tr.trait_ref.substs[1..]),
+ substs: self.tcx.mk_substs_trait(*new_self_ty, &tr.trait_ref.substs[1..]),
..tr.trait_ref
},
..*tr
self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| {
let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id);
- let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs);
+ let impl_trait_ref = tcx.bound_impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs);
let impl_self_ty = impl_trait_ref.self_ty();
use super::{
- DerivedObligationCause, EvaluationResult, ImplDerivedObligationCause, Obligation,
- ObligationCause, ObligationCauseCode, PredicateObligation, SelectionContext,
+ EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation,
+ SelectionContext,
};
use crate::autoderef::Autoderef;
let span = obligation.cause.span;
let mut real_trait_pred = trait_pred;
let mut code = obligation.cause.code();
- loop {
- match &code {
- ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => {
- code = &parent_code;
- }
- ObligationCauseCode::ImplDerivedObligation(box ImplDerivedObligationCause {
- derived: DerivedObligationCause { parent_code, parent_trait_pred },
- ..
- })
- | ObligationCauseCode::BuiltinDerivedObligation(DerivedObligationCause {
- parent_code,
- parent_trait_pred,
- })
- | ObligationCauseCode::DerivedObligation(DerivedObligationCause {
- parent_code,
- parent_trait_pred,
- }) => {
- code = &parent_code;
- real_trait_pred = *parent_trait_pred;
- }
- _ => break,
- };
- let Some(real_ty) = real_trait_pred.self_ty().no_bound_vars() else {
- continue;
- };
+ while let Some((parent_code, parent_trait_pred)) = code.parent() {
+ code = parent_code;
+ if let Some(parent_trait_pred) = parent_trait_pred {
+ real_trait_pred = parent_trait_pred;
+ }
+
+ // Skipping binder here, remapping below
+ let real_ty = real_trait_pred.self_ty().skip_binder();
if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() {
let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span);
if let Some(steps) = autoderef.find_map(|(ty, steps)| {
// Re-add the `&`
let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl });
- let obligation =
- self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred, ty);
+
+ // Remapping bound vars here
+ let real_trait_pred_and_ty =
+ real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, ty));
+ let obligation = self
+ .mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred_and_ty);
Some(steps).filter(|_| self.predicate_may_hold(&obligation))
}) {
if steps > 0 {
}
} else if real_trait_pred != trait_pred {
// This branch addresses #87437.
+
+ // Remapping bound vars here
+ let real_trait_pred_and_base_ty =
+ real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, base_ty));
let obligation = self.mk_trait_obligation_with_new_self_ty(
param_env,
- real_trait_pred,
- base_ty,
+ real_trait_pred_and_base_ty,
);
if self.predicate_may_hold(&obligation) {
err.span_suggestion_verbose(
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool {
- let Some(self_ty) = trait_pred.self_ty().no_bound_vars() else {
- return false;
- };
+ // Skipping binder here, remapping below
+ let self_ty = trait_pred.self_ty().skip_binder();
let (def_id, output_ty, callable) = match *self_ty.kind() {
ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig().output(), "closure"),
};
let msg = format!("use parentheses to call the {}", callable);
- // `mk_trait_obligation_with_new_self_ty` only works for types with no escaping bound
- // variables, so bail out if we have any.
- let Some(output_ty) = output_ty.no_bound_vars() else {
- return false;
- };
+ // "We should really create a single list of bound vars from the combined vars
+ // from the predicate and function, but instead we just liberate the function bound vars"
+ let output_ty = self.tcx.liberate_late_bound_regions(def_id, output_ty);
+
+ // Remapping bound vars here
+ let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, output_ty));
let new_obligation =
- self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred, output_ty);
+ self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self);
match self.evaluate_obligation(&new_obligation) {
Ok(
let param_env = obligation.param_env;
// Try to apply the original trait binding obligation by borrowing.
- let mut try_borrowing = |old_pred: ty::PolyTraitPredicate<'tcx>,
- blacklist: &[DefId]|
- -> bool {
- if blacklist.contains(&old_pred.def_id()) {
- return false;
- }
-
- // This is a quick fix to resolve an ICE (#96223).
- // This change should probably be deeper.
- // As suggested by @jackh726, `mk_trait_obligation_with_new_self_ty` could take a `Binder<(TraitRef, Ty)>
- // instead of `Binder<Ty>` leading to some changes to its call places.
- let Some(orig_ty) = old_pred.self_ty().no_bound_vars() else {
- return false;
- };
- let mk_result = |new_ty| {
- let obligation =
- self.mk_trait_obligation_with_new_self_ty(param_env, old_pred, new_ty);
- self.predicate_must_hold_modulo_regions(&obligation)
- };
- let imm_result = mk_result(self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, orig_ty));
- let mut_result = mk_result(self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, orig_ty));
-
- if imm_result || mut_result {
- if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
- // We have a very specific type of error, where just borrowing this argument
- // might solve the problem. In cases like this, the important part is the
- // original type obligation, not the last one that failed, which is arbitrary.
- // Because of this, we modify the error to refer to the original obligation and
- // return early in the caller.
-
- let msg = format!(
- "the trait bound `{}: {}` is not satisfied",
- orig_ty,
- old_pred.print_modifiers_and_trait_path(),
- );
- if has_custom_message {
- err.note(&msg);
- } else {
- err.message =
- vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)];
- }
- if snippet.starts_with('&') {
- // This is already a literal borrow and the obligation is failing
- // somewhere else in the obligation chain. Do not suggest non-sense.
- return false;
- }
- err.span_label(
- span,
- &format!(
- "expected an implementor of trait `{}`",
- old_pred.print_modifiers_and_trait_path(),
- ),
- );
+ let mut try_borrowing =
+ |old_pred: ty::PolyTraitPredicate<'tcx>, blacklist: &[DefId]| -> bool {
+ if blacklist.contains(&old_pred.def_id()) {
+ return false;
+ }
+ // We map bounds to `&T` and `&mut T`
+ let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| {
+ (
+ trait_pred,
+ self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
+ )
+ });
+ let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| {
+ (
+ trait_pred,
+ self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
+ )
+ });
- // This if is to prevent a special edge-case
- if matches!(
- span.ctxt().outer_expn_data().kind,
- ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop)
- ) {
- // We don't want a borrowing suggestion on the fields in structs,
- // ```
- // struct Foo {
- // the_foos: Vec<Foo>
- // }
- // ```
-
- if imm_result && mut_result {
- err.span_suggestions(
- span.shrink_to_lo(),
- "consider borrowing here",
- ["&".to_string(), "&mut ".to_string()].into_iter(),
- Applicability::MaybeIncorrect,
- );
+ let mk_result = |trait_pred_and_new_ty| {
+ let obligation =
+ self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty);
+ self.predicate_must_hold_modulo_regions(&obligation)
+ };
+ let imm_result = mk_result(trait_pred_and_imm_ref);
+ let mut_result = mk_result(trait_pred_and_mut_ref);
+
+ if imm_result || mut_result {
+ if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+ // We have a very specific type of error, where just borrowing this argument
+ // might solve the problem. In cases like this, the important part is the
+ // original type obligation, not the last one that failed, which is arbitrary.
+ // Because of this, we modify the error to refer to the original obligation and
+ // return early in the caller.
+
+ let msg = format!("the trait bound `{}` is not satisfied", old_pred);
+ if has_custom_message {
+ err.note(&msg);
} else {
- err.span_suggestion_verbose(
- span.shrink_to_lo(),
- &format!(
- "consider{} borrowing here",
- if mut_result { " mutably" } else { "" }
- ),
- format!("&{}", if mut_result { "mut " } else { "" }),
- Applicability::MaybeIncorrect,
- );
+ err.message =
+ vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)];
+ }
+ if snippet.starts_with('&') {
+ // This is already a literal borrow and the obligation is failing
+ // somewhere else in the obligation chain. Do not suggest non-sense.
+ return false;
}
+ err.span_label(
+ span,
+ &format!(
+ "expected an implementor of trait `{}`",
+ old_pred.print_modifiers_and_trait_path(),
+ ),
+ );
+
+ // This if is to prevent a special edge-case
+ if matches!(
+ span.ctxt().outer_expn_data().kind,
+ ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop)
+ ) {
+ // We don't want a borrowing suggestion on the fields in structs,
+ // ```
+ // struct Foo {
+ // the_foos: Vec<Foo>
+ // }
+ // ```
+
+ if imm_result && mut_result {
+ err.span_suggestions(
+ span.shrink_to_lo(),
+ "consider borrowing here",
+ ["&".to_string(), "&mut ".to_string()].into_iter(),
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ err.span_suggestion_verbose(
+ span.shrink_to_lo(),
+ &format!(
+ "consider{} borrowing here",
+ if mut_result { " mutably" } else { "" }
+ ),
+ format!("&{}", if mut_result { "mut " } else { "" }),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ return true;
}
- return true;
}
- }
- return false;
- };
+ return false;
+ };
if let ObligationCauseCode::ImplDerivedObligation(cause) = &*code {
try_borrowing(cause.derived.parent_trait_pred, &[])
return false;
}
- let Some(mut suggested_ty) = trait_pred.self_ty().no_bound_vars() else {
- return false;
- };
+ // Skipping binder here, remapping below
+ let mut suggested_ty = trait_pred.self_ty().skip_binder();
for refs_remaining in 0..refs_number {
let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else {
};
suggested_ty = *inner_ty;
+ // Remapping bound vars here
+ let trait_pred_and_suggested_ty =
+ trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty));
+
let new_obligation = self.mk_trait_obligation_with_new_self_ty(
obligation.param_env,
- trait_pred,
- suggested_ty,
+ trait_pred_and_suggested_ty,
);
if self.predicate_may_hold(&new_obligation) {
return;
}
+ // Skipping binder here, remapping below
if let ty::Ref(region, t_type, mutability) = *trait_pred.skip_binder().self_ty().kind()
{
- if region.is_late_bound() || t_type.has_escaping_bound_vars() {
- // Avoid debug assertion in `mk_obligation_for_def_id`.
- //
- // If the self type has escaping bound vars then it's not
- // going to be the type of an expression, so the suggestion
- // probably won't apply anyway.
- return;
- }
-
let suggested_ty = match mutability {
hir::Mutability::Mut => self.tcx.mk_imm_ref(region, t_type),
hir::Mutability::Not => self.tcx.mk_mut_ref(region, t_type),
};
+ // Remapping bound vars here
+ let trait_pred_and_suggested_ty =
+ trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty));
+
let new_obligation = self.mk_trait_obligation_with_new_self_ty(
obligation.param_env,
- trait_pred,
- suggested_ty,
+ trait_pred_and_suggested_ty,
);
let suggested_ty_would_satisfy_obligation = self
.evaluate_obligation_no_overflow(&new_obligation)
// Only suggest this if the expression behind the semicolon implements the predicate
&& let Some(typeck_results) = self.in_progress_typeck_results
&& let Some(ty) = typeck_results.borrow().expr_ty_opt(expr)
- && self.predicate_may_hold(&self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred, ty))
+ && self.predicate_may_hold(&self.mk_trait_obligation_with_new_self_ty(
+ obligation.param_env, trait_pred.map_bound(|trait_pred| (trait_pred, ty))
+ ))
{
err.span_label(
expr.span,
debug!("maybe_note_obligation_cause_for_async_await: code={:?}", code);
match code {
ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => {
- next_code = Some(parent_code.as_ref());
+ next_code = Some(parent_code);
}
ObligationCauseCode::ImplDerivedObligation(cause) => {
let ty = cause.derived.parent_trait_pred.skip_binder().self_ty();
_ => {}
}
- next_code = Some(cause.derived.parent_code.as_ref());
+ next_code = Some(&cause.derived.parent_code);
}
ObligationCauseCode::DerivedObligation(derived_obligation)
| ObligationCauseCode::BuiltinDerivedObligation(derived_obligation) => {
_ => {}
}
- next_code = Some(derived_obligation.parent_code.as_ref());
+ next_code = Some(&derived_obligation.parent_code);
}
_ => break,
}
let is_upvar_tys_infer_tuple = if !matches!(ty.kind(), ty::Tuple(..)) {
false
} else {
- if let ObligationCauseCode::BuiltinDerivedObligation(ref data) =
- *data.parent_code
+ if let ObligationCauseCode::BuiltinDerivedObligation(data) = &*data.parent_code
{
let parent_trait_ref =
self.resolve_vars_if_possible(data.parent_trait_pred);
err,
&parent_predicate,
param_env,
- &cause_code.peel_derives(),
+ cause_code.peel_derives(),
obligated_types,
seen_requirements,
)
);
let try_obligation = self.mk_trait_obligation_with_new_self_ty(
obligation.param_env,
- trait_pred,
- normalized_ty.ty().unwrap(),
+ trait_pred.map_bound(|trait_pred| (trait_pred, normalized_ty.ty().unwrap())),
);
debug!("suggest_await_before_try: try_trait_obligation {:?}", try_obligation);
if self.predicate_may_hold(&try_obligation)
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::subst::{GenericArg, InternalSubsts, Subst};
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor};
+use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt, TypeFoldable, TypeVisitor};
use rustc_middle::ty::{Predicate, ToPredicate};
use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY;
use rustc_span::symbol::Symbol;
if param.index == 0 { self_ty.into() } else { tcx.mk_param_from_def(param) }
});
- let result = receiver_ty.subst(tcx, substs);
+ let result = EarlyBinder(receiver_ty).subst(tcx, substs);
debug!(
"receiver_for_self_ty({:?}, {:?}, {:?}) = {:?}",
receiver_ty, self_ty, method_def_id, result
}
pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
- let attrs = tcx.get_attrs(item_def_id);
-
- let Some(attr) = tcx.sess.find_by_name(&attrs, sym::rustc_on_unimplemented) else {
+ let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) else {
return Ok(None);
};
use rustc_middle::traits::select::OverflowError;
use rustc_middle::ty::fold::{MaxUniverse, TypeFoldable, TypeFolder};
use rustc_middle::ty::subst::Subst;
-use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt};
+use rustc_middle::ty::{self, EarlyBinder, Term, ToPredicate, Ty, TyCtxt};
use rustc_span::symbol::sym;
use std::collections::BTreeMap;
}
let substs = substs.super_fold_with(self);
- let generic_ty = self.tcx().type_of(def_id);
+ let generic_ty = self.tcx().bound_type_of(def_id);
let concrete_ty = generic_ty.subst(self.tcx(), substs);
self.depth += 1;
let folded_ty = self.fold_ty(concrete_ty);
// Check whether the self-type is itself a projection.
// If so, extract what we know from the trait and try to come up with a good answer.
let bounds = match *obligation.predicate.self_ty().kind() {
- ty::Projection(ref data) => tcx.item_bounds(data.item_def_id).subst(tcx, data.substs),
- ty::Opaque(def_id, substs) => tcx.item_bounds(def_id).subst(tcx, substs),
+ ty::Projection(ref data) => tcx.bound_item_bounds(data.item_def_id).subst(tcx, data.substs),
+ ty::Opaque(def_id, substs) => tcx.bound_item_bounds(def_id).subst(tcx, substs),
ty::Infer(ty::TyVar(_)) => {
// If the self-type is an inference variable, then it MAY wind up
// being a projected type, so induce an ambiguity.
Progress { term: err.into(), obligations: nested }
} else {
assoc_ty_own_obligations(selcx, obligation, &mut nested);
- Progress { term: term.subst(tcx, substs), obligations: nested }
+ Progress { term: EarlyBinder(term).subst(tcx, substs), obligations: nested }
}
}
self.infcx.report_overflow_error(&obligation, true);
}
- let generic_ty = self.tcx().type_of(def_id);
+ let generic_ty = self.tcx().bound_type_of(def_id);
let concrete_ty = generic_ty.subst(self.tcx(), substs);
self.anon_depth += 1;
if concrete_ty == ty {
use rustc_infer::infer::InferOk;
use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef};
-use rustc_middle::ty::{self, GenericParamDefKind, Ty};
+use rustc_middle::ty::{self, EarlyBinder, GenericParamDefKind, Ty};
use rustc_middle::ty::{ToPolyTraitRef, ToPredicate};
use rustc_span::def_id::DefId;
use crate::traits::project::{normalize_with_depth, normalize_with_depth_to};
-use crate::traits::select::TraitObligationExt;
use crate::traits::util::{self, closure_trait_ref_and_return_type, predicate_for_trait_def};
use crate::traits::{
- BuiltinDerivedObligation, DerivedObligationCause, ImplDerivedObligation,
- ImplDerivedObligationCause, ImplSource, ImplSourceAutoImplData, ImplSourceBuiltinData,
- ImplSourceClosureData, ImplSourceConstDestructData, ImplSourceDiscriminantKindData,
- ImplSourceFnPointerData, ImplSourceGeneratorData, ImplSourceObjectData, ImplSourcePointeeData,
- ImplSourceTraitAliasData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized,
- ObjectCastObligation, Obligation, ObligationCause, OutputTypeParameterMismatch,
- PredicateObligation, Selection, SelectionError, TraitNotObjectSafe, TraitObligation,
- Unimplemented, VtblSegment,
+ BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource,
+ ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData,
+ ImplSourceConstDestructData, ImplSourceDiscriminantKindData, ImplSourceFnPointerData,
+ ImplSourceGeneratorData, ImplSourceObjectData, ImplSourcePointeeData, ImplSourceTraitAliasData,
+ ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized, ObjectCastObligation,
+ Obligation, ObligationCause, OutputTypeParameterMismatch, PredicateObligation, Selection,
+ SelectionError, TraitNotObjectSafe, TraitObligation, Unimplemented, VtblSegment,
};
use super::BuiltinImplConditions;
_ => bug!("projection candidate for unexpected type: {:?}", placeholder_self_ty),
};
- let candidate_predicate = tcx.item_bounds(def_id)[idx].subst(tcx, substs);
+ let candidate_predicate =
+ tcx.bound_item_bounds(def_id).map_bound(|i| i[idx]).subst(tcx, substs);
let candidate = candidate_predicate
.to_opt_poly_trait_pred()
.expect("projection candidate is not a trait predicate")
// This maybe belongs in wf, but that can't (doesn't) handle
// higher-ranked things.
// Prevent, e.g., `dyn Iterator<Item = str>`.
- for bound in self.tcx().item_bounds(assoc_type) {
+ for bound in self.tcx().bound_item_bounds(assoc_type).transpose_iter() {
let subst_bound =
if defs.count() == 0 {
bound.subst(tcx, trait_predicate.trait_ref.substs)
substs.extend(trait_predicate.trait_ref.substs.iter());
let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> =
smallvec::SmallVec::with_capacity(
- bound.kind().bound_vars().len() + defs.count(),
+ bound.0.kind().bound_vars().len() + defs.count(),
);
- bound_vars.extend(bound.kind().bound_vars().into_iter());
+ bound_vars.extend(bound.0.kind().bound_vars().into_iter());
InternalSubsts::fill_single(&mut substs, defs, &mut |param, _| match param
.kind
{
let assoc_ty_substs = tcx.intern_substs(&substs);
let bound_vars = tcx.mk_bound_variable_kinds(bound_vars.into_iter());
- let bound = bound.kind().skip_binder().subst(tcx, assoc_ty_substs);
+ let bound =
+ EarlyBinder(bound.0.kind().skip_binder()).subst(tcx, assoc_ty_substs);
tcx.mk_predicate(ty::Binder::bind_with_vars(bound, bound_vars))
};
let normalized_bound = normalize_with_depth_to(
// The last field of the structure has to exist and contain type/const parameters.
let (tail_field, prefix_fields) =
def.non_enum_variant().fields.split_last().ok_or(Unimplemented)?;
- let tail_field_ty = tcx.type_of(tail_field.did);
+ let tail_field_ty = tcx.bound_type_of(tail_field.did);
let mut unsizing_params = GrowableBitSet::new_empty();
- for arg in tail_field_ty.walk() {
+ for arg in tail_field_ty.0.walk() {
if let Some(i) = maybe_unsizing_param_idx(arg) {
unsizing_params.insert(i);
}
let substs = self.rematch_impl(impl_def_id, &new_obligation);
debug!(?substs, "impl substs");
- let derived = DerivedObligationCause {
- parent_trait_pred: obligation.predicate,
- parent_code: obligation.cause.clone_code(),
- };
- let derived_code = ImplDerivedObligation(Box::new(ImplDerivedObligationCause {
- derived,
- impl_def_id,
- span: obligation.cause.span,
- }));
-
- let cause = ObligationCause::new(
- obligation.cause.span,
- obligation.cause.body_id,
- derived_code,
- );
+ let cause = obligation.derived_cause(|derived| {
+ ImplDerivedObligation(Box::new(ImplDerivedObligationCause {
+ derived,
+ impl_def_id,
+ span: obligation.cause.span,
+ }))
+ });
ensure_sufficient_stack(|| {
self.vtable_impl(
impl_def_id,
use super::util::{closure_trait_ref_and_return_type, predicate_for_trait_def};
use super::wf;
use super::{
- DerivedObligationCause, ErrorReporting, ImplDerivedObligation, ImplDerivedObligationCause,
- Normalized, Obligation, ObligationCause, ObligationCauseCode, Overflow, PredicateObligation,
- Selection, SelectionError, SelectionResult, TraitObligation, TraitQueryMode,
+ ErrorReporting, ImplDerivedObligation, ImplDerivedObligationCause, Normalized, Obligation,
+ ObligationCause, ObligationCauseCode, Overflow, PredicateObligation, Selection, SelectionError,
+ SelectionResult, TraitObligation, TraitQueryMode,
};
use crate::infer::{InferCtxt, InferOk, TypeFreshener};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::relate::TypeRelation;
use rustc_middle::ty::subst::{GenericArgKind, Subst, SubstsRef};
-use rustc_middle::ty::{self, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate};
+use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate};
use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable};
use rustc_span::symbol::sym;
/// require themselves.
freshener: TypeFreshener<'cx, 'tcx>,
- /// If `true`, indicates that the evaluation should be conservative
- /// and consider the possibility of types outside this crate.
+ /// During coherence we have to assume that other crates may add
+ /// additional impls which we currently don't know about.
+ ///
+ /// To deal with this evaluation should be conservative
+ /// and consider the possibility of impls from outside this crate.
/// This comes up primarily when resolving ambiguity. Imagine
/// there is some trait reference `$0: Bar` where `$0` is an
/// inference variable. If `intercrate` is true, then we can never
/// say for sure that this reference is not implemented, even if
/// there are *no impls at all for `Bar`*, because `$0` could be
/// bound to some type that in a downstream crate that implements
- /// `Bar`. This is the suitable mode for coherence. Elsewhere,
- /// though, we set this to false, because we are only interested
- /// in types that the user could actually have written --- in
- /// other words, we consider `$0: Bar` to be unimplemented if
+ /// `Bar`.
+ ///
+ /// Outside of coherence we set this to false because we are only
+ /// interested in types that the user could actually have written.
+ /// In other words, we consider `$0: Bar` to be unimplemented if
/// there is no type that the user could *actually name* that
/// would satisfy it. This avoids crippling inference, basically.
intercrate: bool,
-
+ /// If `intercrate` is set, we remember predicates which were
+ /// considered ambiguous because of impls potentially added in other crates.
+ /// This is used in coherence to give improved diagnostics.
+ /// We don't do his until we detect a coherence error because it can
+ /// lead to false overflow results (#47139) and because always
+ /// computing it may negatively impact performance.
intercrate_ambiguity_causes: Option<Vec<IntercrateAmbiguityCause>>,
/// The mode that trait queries run in, which informs our error handling
}
}
- /// Enables tracking of intercrate ambiguity causes. These are
- /// used in coherence to give improved diagnostics. We don't do
- /// this until we detect a coherence error because it can lead to
- /// false overflow results (#47139) and because it costs
- /// computation time.
+ /// Enables tracking of intercrate ambiguity causes. See
+ /// the documentation of [`Self::intercrate_ambiguity_causes`] for more.
pub fn enable_tracking_intercrate_ambiguity_causes(&mut self) {
assert!(self.intercrate);
assert!(self.intercrate_ambiguity_causes.is_none());
if let ImplCandidate(def_id) = candidate {
if let ty::ImplPolarity::Reservation = tcx.impl_polarity(def_id) {
if let Some(intercrate_ambiguity_clauses) = &mut self.intercrate_ambiguity_causes {
- let attrs = tcx.get_attrs(def_id);
- let attr = tcx.sess.find_by_name(&attrs, sym::rustc_reservation_impl);
- let value = attr.and_then(|a| a.value_str());
+ let value = tcx
+ .get_attr(def_id, sym::rustc_reservation_impl)
+ .and_then(|a| a.value_str());
if let Some(value) = value {
debug!(
"filter_reservation_impls: \
);
}
};
- let bounds = tcx.item_bounds(def_id).subst(tcx, substs);
+ let bounds = tcx.bound_item_bounds(def_id).subst(tcx, substs);
// The bounds returned by `item_bounds` may contain duplicates after
// normalization, so try to deduplicate when possible to avoid
ty::Adt(def, substs) => {
let sized_crit = def.sized_constraint(self.tcx());
// (*) binder moved here
- Where(
- obligation.predicate.rebind({
- sized_crit.iter().map(|ty| ty.subst(self.tcx(), substs)).collect()
- }),
- )
+ Where(obligation.predicate.rebind({
+ sized_crit.iter().map(|ty| EarlyBinder(*ty).subst(self.tcx(), substs)).collect()
+ }))
}
ty::Projection(_) | ty::Param(_) | ty::Opaque(..) => None,
// We can resolve the `impl Trait` to its concrete type,
// which enforces a DAG between the functions requiring
// the auto trait bounds in question.
- t.rebind(vec![self.tcx().type_of(def_id).subst(self.tcx(), substs)])
+ t.rebind(vec![self.tcx().bound_type_of(def_id).subst(self.tcx(), substs)])
}
}
}
impl_def_id: DefId,
obligation: &TraitObligation<'tcx>,
) -> Result<Normalized<'tcx, SubstsRef<'tcx>>, ()> {
- let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap();
+ let impl_trait_ref = self.tcx().bound_impl_trait_ref(impl_def_id).unwrap();
// Before we create the substitutions and everything, first
// consider a "quick reject". This avoids creating more types
// and so forth that we need to.
- if self.fast_reject_trait_refs(obligation, &impl_trait_ref) {
+ if self.fast_reject_trait_refs(obligation, &impl_trait_ref.0) {
return Err(());
}
debug!(?predicates);
assert_eq!(predicates.parent, None);
let mut obligations = Vec::with_capacity(predicates.predicates.len());
- let parent_code = cause.clone_code();
for (predicate, span) in predicates.predicates {
let span = *span;
- let derived =
- DerivedObligationCause { parent_trait_pred, parent_code: parent_code.clone() };
- let code = ImplDerivedObligation(Box::new(ImplDerivedObligationCause {
- derived,
- impl_def_id: def_id,
- span,
- }));
- let cause = ObligationCause::new(cause.span, cause.body_id, code);
+ let cause = cause.clone().derived_cause(parent_trait_pred, |derived| {
+ ImplDerivedObligation(Box::new(ImplDerivedObligationCause {
+ derived,
+ impl_def_id: def_id,
+ span,
+ }))
+ });
let predicate = normalize_with_depth_to(
self,
param_env,
cause.clone(),
recursion_depth,
- predicate.subst(tcx, substs),
+ EarlyBinder(*predicate).subst(tcx, substs),
&mut obligations,
);
obligations.push(Obligation { cause, recursion_depth, param_env, predicate });
}
}
-trait TraitObligationExt<'tcx> {
- fn derived_cause(
- &self,
- variant: fn(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>,
- ) -> ObligationCause<'tcx>;
-}
-
-impl<'tcx> TraitObligationExt<'tcx> for TraitObligation<'tcx> {
- fn derived_cause(
- &self,
- variant: fn(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>,
- ) -> ObligationCause<'tcx> {
- /*!
- * Creates a cause for obligations that are derived from
- * `obligation` by a recursive search (e.g., for a builtin
- * bound, or eventually a `auto trait Foo`). If `obligation`
- * is itself a derived obligation, this is just a clone, but
- * otherwise we create a "derived obligation" cause so as to
- * keep track of the original root obligation for error
- * reporting.
- */
-
- let obligation = self;
-
- // NOTE(flaper87): As of now, it keeps track of the whole error
- // chain. Ideally, we should have a way to configure this either
- // by using -Z verbose or just a CLI argument.
- let derived_cause = DerivedObligationCause {
- parent_trait_pred: obligation.predicate,
- parent_code: obligation.cause.clone_code(),
- };
- let derived_code = variant(derived_cause);
- ObligationCause::new(obligation.cause.span, obligation.cause.body_id, derived_code)
- }
-}
-
impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> {
fn list(&'o self) -> TraitObligationStackList<'o, 'tcx> {
TraitObligationStackList::with(self)
param_env, source_impl, source_substs, target_node
);
let source_trait_ref =
- infcx.tcx.impl_trait_ref(source_impl).unwrap().subst(infcx.tcx, &source_substs);
+ infcx.tcx.bound_impl_trait_ref(source_impl).unwrap().subst(infcx.tcx, &source_substs);
// translate the Self and Param parts of the substitution, since those
// vary across impls
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef};
-use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{self, EarlyBinder, ImplSubject, ToPredicate, Ty, TyCtxt, TypeFoldable};
use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext};
pub use rustc_infer::traits::{self, util::*};
impl_substs: SubstsRef<'tcx>,
) -> (ImplSubject<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) {
let subject = selcx.tcx().impl_subject(impl_def_id);
- let subject = subject.subst(selcx.tcx(), impl_substs);
+ let subject = EarlyBinder(subject).subst(selcx.tcx(), impl_substs);
let Normalized { value: subject, obligations: normalization_obligations1 } =
super::normalize(selcx, param_env, ObligationCause::dummy(), subject);
let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs);
debug!("compute_trait_ref obligations {:?}", obligations);
- let cause = self.cause(traits::MiscObligation);
let param_env = self.param_env;
let depth = self.recursion_depth;
let item = self.item;
- let extend = |obligation: traits::PredicateObligation<'tcx>| {
- let mut cause = cause.clone();
- if let Some(parent_trait_pred) = obligation.predicate.to_opt_poly_trait_pred() {
- let derived_cause = traits::DerivedObligationCause {
+ let extend = |traits::PredicateObligation { predicate, mut cause, .. }| {
+ if let Some(parent_trait_pred) = predicate.to_opt_poly_trait_pred() {
+ cause = cause.derived_cause(
parent_trait_pred,
- parent_code: obligation.cause.clone_code(),
- };
- *cause.make_mut_code() =
- traits::ObligationCauseCode::DerivedObligation(derived_cause);
+ traits::ObligationCauseCode::DerivedObligation,
+ );
}
extend_cause_with_original_assoc_item_obligation(
- tcx,
- trait_ref,
- item,
- &mut cause,
- obligation.predicate,
+ tcx, trait_ref, item, &mut cause, predicate,
);
- traits::Obligation::with_depth(cause, depth, param_env, obligation.predicate)
+ traits::Obligation::with_depth(cause, depth, param_env, predicate)
};
if let Elaborate::All = elaborate {
})
.filter(|(_, arg)| !arg.has_escaping_bound_vars())
.map(|(i, arg)| {
- let mut new_cause = cause.clone();
+ let mut cause = traits::ObligationCause::misc(self.span, self.body_id);
// The first subst is the self ty - use the correct span for it.
if i == 0 {
if let Some(hir::ItemKind::Impl(hir::Impl { self_ty, .. })) =
item.map(|i| &i.kind)
{
- new_cause.span = self_ty.span;
+ cause.span = self_ty.span;
}
}
traits::Obligation::with_depth(
- new_cause,
+ cause,
depth,
param_env,
ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)).to_predicate(tcx),
// generators don't take arguments.
}
- ty::Closure(_, substs) => {
+ ty::Closure(did, substs) => {
// Only check the upvar types for WF, not the rest
// of the types within. This is needed because we
// capture the signature and it may not be WF
// probably always be WF, because it should be
// shorthand for something like `where(T: 'a) {
// fn(&'a T) }`, as discussed in #25860.
- //
- // Note that we are also skipping the generic
- // types. This is consistent with the `outlives`
- // code, but anyway doesn't matter: within the fn
+ walker.skip_current_subtree(); // subtree handled below
+ // FIXME(eddyb) add the type to `walker` instead of recursing.
+ self.compute(substs.as_closure().tupled_upvars_ty().into());
+ // Note that we cannot skip the generic types
+ // types. Normally, within the fn
// body where they are created, the generics will
// always be WF, and outside of that fn body we
// are not directly inspecting closure types
// anyway, except via auto trait matching (which
// only inspects the upvar types).
- walker.skip_current_subtree(); // subtree handled below
- // FIXME(eddyb) add the type to `walker` instead of recursing.
- self.compute(substs.as_closure().tupled_upvars_ty().into());
+ // But when a closure is part of a type-alias-impl-trait
+ // then the function that created the defining site may
+ // have had more bounds available than the type alias
+ // specifies. This may cause us to have a closure in the
+ // hidden type that is not actually well formed and
+ // can cause compiler crashes when the user abuses unsafe
+ // code to procure such a closure.
+ // See src/test/ui/type-alias-impl-trait/wf_check_closures.rs
+ let obligations = self.nominal_obligations(did, substs);
+ self.out.extend(obligations);
}
ty::FnPtr(_) => {
use rustc_middle::traits::ChalkRustInterner as RustInterner;
use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
-use rustc_middle::ty::{self, AssocItemContainer, AssocKind, Ty, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{
+ self, AssocItemContainer, AssocKind, EarlyBinder, Ty, TyCtxt, TypeFoldable,
+};
use rustc_ast::ast;
use rustc_attr as attr;
let predicates = self.interner.tcx.predicates_defined_on(def_id).predicates;
predicates
.iter()
- .map(|(wc, _)| wc.subst(self.interner.tcx, bound_vars))
+ .map(|(wc, _)| EarlyBinder(*wc).subst(self.interner.tcx, bound_vars))
.filter_map(|wc| LowerInto::<
Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>
>::lower_into(wc, self.interner)).collect()
.tcx
.explicit_item_bounds(def_id)
.iter()
- .map(|(bound, _)| bound.subst(self.interner.tcx, &bound_vars))
+ .map(|(bound, _)| EarlyBinder(*bound).subst(self.interner.tcx, &bound_vars))
.filter_map(|bound| LowerInto::<Option<_>>::lower_into(bound, self.interner))
.collect()
}
let (inputs_and_output, iobinders, _) = crate::chalk::lowering::collect_bound_vars(
self.interner,
self.interner.tcx,
- sig.inputs_and_output().subst(self.interner.tcx, bound_vars),
+ EarlyBinder(sig.inputs_and_output()).subst(self.interner.tcx, bound_vars),
);
let argument_types = inputs_and_output[..inputs_and_output.len() - 1]
.iter()
- .map(|t| t.subst(self.interner.tcx, &bound_vars).lower_into(self.interner))
+ .map(|t| {
+ EarlyBinder(*t).subst(self.interner.tcx, &bound_vars).lower_into(self.interner)
+ })
.collect();
- let return_type = inputs_and_output[inputs_and_output.len() - 1]
+ let return_type = EarlyBinder(inputs_and_output[inputs_and_output.len() - 1])
.subst(self.interner.tcx, &bound_vars)
.lower_into(self.interner);
let bound_vars = bound_vars_for_item(self.interner.tcx, def_id);
let binders = binders_for(self.interner, bound_vars);
- let trait_ref = self.interner.tcx.impl_trait_ref(def_id).expect("not an impl");
+ let trait_ref = self.interner.tcx.bound_impl_trait_ref(def_id).expect("not an impl");
let trait_ref = trait_ref.subst(self.interner.tcx, bound_vars);
let where_clauses = self.where_clauses_for(def_id, bound_vars);
let all_impls = self.interner.tcx.all_impls(def_id);
let matched_impls = all_impls.filter(|impl_def_id| {
use chalk_ir::could_match::CouldMatch;
- let trait_ref = self.interner.tcx.impl_trait_ref(*impl_def_id).unwrap();
+ let trait_ref = self.interner.tcx.bound_impl_trait_ref(*impl_def_id).unwrap();
let bound_vars = bound_vars_for_item(self.interner.tcx, *impl_def_id);
- let self_ty = trait_ref.self_ty();
+ let self_ty = trait_ref.map_bound(|t| t.self_ty());
let self_ty = self_ty.subst(self.interner.tcx, bound_vars);
let lowered_ty = self_ty.lower_into(self.interner);
let ty = self
.interner
.tcx
- .type_of(def_id)
+ .bound_type_of(def_id)
.subst(self.interner.tcx, bound_vars)
.lower_into(self.interner);
.tcx
.explicit_item_bounds(opaque_ty_id.0)
.iter()
- .map(|(bound, _)| bound.subst(self.interner.tcx, &bound_vars))
+ .map(|(bound, _)| EarlyBinder(*bound).subst(self.interner.tcx, &bound_vars))
.map(|bound| {
bound.fold_with(&mut ReplaceOpaqueTyFolder {
tcx: self.interner.tcx,
use rustc_infer::traits::TraitEngineExt as _;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::subst::{InternalSubsts, Subst};
-use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt};
+use rustc_middle::ty::{self, EarlyBinder, ParamEnvAnd, Ty, TyCtxt};
use rustc_span::source_map::{Span, DUMMY_SP};
use rustc_trait_selection::traits::query::dropck_outlives::trivial_dropck_outlives;
use rustc_trait_selection::traits::query::dropck_outlives::{
tcx.at(span).adt_dtorck_constraint(def.did())?;
// FIXME: we can try to recursively `dtorck_constraint_on_ty`
// there, but that needs some way to handle cycles.
- constraints.dtorck_types.extend(dtorck_types.iter().map(|t| t.subst(tcx, substs)));
- constraints.outlives.extend(outlives.iter().map(|t| t.subst(tcx, substs)));
- constraints.overflows.extend(overflows.iter().map(|t| t.subst(tcx, substs)));
+ constraints
+ .dtorck_types
+ .extend(dtorck_types.iter().map(|t| EarlyBinder(*t).subst(tcx, substs)));
+ constraints
+ .outlives
+ .extend(outlives.iter().map(|t| EarlyBinder(*t).subst(tcx, substs)));
+ constraints
+ .overflows
+ .extend(overflows.iter().map(|t| EarlyBinder(*t).subst(tcx, substs)));
}
// Objects must be alive in order for their destructor
use rustc_infer::traits::TraitEngineExt as _;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::subst::{GenericArg, Subst, UserSelfTy, UserSubsts};
-use rustc_middle::ty::{self, FnSig, Lift, PolyFnSig, Ty, TyCtxt, TypeFoldable, Variance};
+use rustc_middle::ty::{
+ self, EarlyBinder, FnSig, Lift, PolyFnSig, Ty, TyCtxt, TypeFoldable, Variance,
+};
use rustc_middle::ty::{ParamEnv, ParamEnvAnd, Predicate, ToPredicate};
use rustc_span::{Span, DUMMY_SP};
use rustc_trait_selection::infer::InferCtxtBuilderExt;
where
T: TypeFoldable<'tcx>,
{
- value.subst(self.tcx(), substs)
+ EarlyBinder(value).subst(self.tcx(), substs)
}
fn relate_mir_and_user_ty(
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_infer::infer::TyCtxtInferExt;
+use rustc_middle::traits::CodegenObligationError;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, Binder, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitor};
use rustc_span::{sym, DUMMY_SP};
-use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits;
use traits::{translate_substs, Reveal};
let item_type = tcx.subst_and_normalize_erasing_regions(substs, param_env, ty);
let def = match *item_type.kind() {
- ty::FnDef(..)
- if {
- let f = item_type.fn_sig(tcx);
- f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic
- } =>
- {
+ ty::FnDef(def_id, ..) if tcx.is_intrinsic(def_id) => {
debug!(" => intrinsic");
ty::InstanceDef::Intrinsic(def.did)
}
let mut bound_vars_collector = BoundVarsCollector::new();
trait_ref.visit_with(&mut bound_vars_collector);
let trait_binder = ty::Binder::bind_with_vars(trait_ref, bound_vars_collector.into_vars(tcx));
- let vtbl = tcx.codegen_fulfill_obligation((param_env, trait_binder))?;
+ let vtbl = match tcx.codegen_fulfill_obligation((param_env, trait_binder)) {
+ Ok(vtbl) => vtbl,
+ Err(CodegenObligationError::Ambiguity) => {
+ let reported = tcx.sess.delay_span_bug(
+ tcx.def_span(trait_item_id),
+ &format!(
+ "encountered ambiguity selecting `{:?}` during codegen, presuming due to \
+ overflow or prior type error",
+ trait_binder
+ ),
+ );
+ return Err(reported);
+ }
+ Err(CodegenObligationError::Unimplemented) => return Ok(None),
+ Err(CodegenObligationError::FulfillmentError) => return Ok(None),
+ };
// Now that we know which impl is being used, we can dispatch to
// the actual function:
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::util::{needs_drop_components, AlwaysRequiresDrop};
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt};
use rustc_session::Limit;
use rustc_span::{sym, DUMMY_SP};
match subty.kind() {
ty::Adt(adt_id, subst) => {
for subty in tcx.adt_drop_tys(adt_id.did())? {
- vec.push(subty.subst(tcx, subst));
+ vec.push(EarlyBinder(subty).subst(tcx, subst));
}
}
_ => vec.push(subty),
Ok(Vec::new())
} else {
let field_tys = adt_def.all_fields().map(|field| {
- let r = tcx.type_of(field.did).subst(tcx, substs);
+ let r = tcx.bound_type_of(field.did).subst(tcx, substs);
debug!("drop_tys_helper: Subst into {:?} with {:?} gettng {:?}", field, substs, r);
r
});
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::subst::Subst;
-use rustc_middle::ty::{self, Binder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt};
+use rustc_middle::ty::{
+ self, Binder, EarlyBinder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt,
+};
use rustc_span::{sym, Span};
use rustc_trait_selection::traits;
debug!("sized_constraint_for_ty({:?}) intermediate = {:?}", ty, adt_tys);
adt_tys
.iter()
- .map(|ty| ty.subst(tcx, substs))
+ .map(|ty| EarlyBinder(*ty).subst(tcx, substs))
.flat_map(|ty| sized_constraint_for_ty(tcx, adtdef, ty))
.collect()
}
// one uninhabited field.
def.variants().iter().all(|var| {
var.fields.iter().any(|field| {
- let ty = tcx.type_of(field.did).subst(tcx, substs);
+ let ty = tcx.bound_type_of(field.did).subst(tcx, substs);
tcx.conservative_is_privately_uninhabited(param_env.and(ty))
})
})
use crate::astconv::AstConv;
+use crate::errors::{ManualImplementation, MissingTypeParams};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{pluralize, struct_span_err, Applicability, ErrorGuaranteed};
use rustc_hir as hir;
if missing_type_params.is_empty() {
return;
}
- let display =
- missing_type_params.iter().map(|n| format!("`{}`", n)).collect::<Vec<_>>().join(", ");
- let mut err = struct_span_err!(
- self.tcx().sess,
+
+ self.tcx().sess.emit_err(MissingTypeParams {
span,
- E0393,
- "the type parameter{} {} must be explicitly specified",
- pluralize!(missing_type_params.len()),
- display,
- );
- err.span_label(
- self.tcx().def_span(def_id),
- &format!(
- "type parameter{} {} must be specified for this",
- pluralize!(missing_type_params.len()),
- display,
- ),
- );
- let mut suggested = false;
- if let (Ok(snippet), true) = (
- self.tcx().sess.source_map().span_to_snippet(span),
- // Don't suggest setting the type params if there are some already: the order is
- // tricky to get right and the user will already know what the syntax is.
+ def_span: self.tcx().def_span(def_id),
+ missing_type_params,
empty_generic_args,
- ) {
- if snippet.ends_with('>') {
- // The user wrote `Trait<'a, T>` or similar. To provide an accurate suggestion
- // we would have to preserve the right order. For now, as clearly the user is
- // aware of the syntax, we do nothing.
- } else {
- // The user wrote `Iterator`, so we don't have a type we can suggest, but at
- // least we can clue them to the correct syntax `Iterator<Type>`.
- err.span_suggestion(
- span,
- &format!(
- "set the type parameter{plural} to the desired type{plural}",
- plural = pluralize!(missing_type_params.len()),
- ),
- format!("{}<{}>", snippet, missing_type_params.join(", ")),
- Applicability::HasPlaceholders,
- );
- suggested = true;
- }
- }
- if !suggested {
- err.span_label(
- span,
- format!(
- "missing reference{} to {}",
- pluralize!(missing_type_params.len()),
- display,
- ),
- );
- }
- err.note(
- "because of the default `Self` reference, type parameters must be \
- specified on object types",
- );
- err.emit();
+ });
}
/// When the code is using the `Fn` traits directly, instead of the `Fn(A) -> B` syntax, emit
if is_impl {
let trait_name = self.tcx().def_path_str(trait_def_id);
- struct_span_err!(
- self.tcx().sess,
- span,
- E0183,
- "manual implementations of `{}` are experimental",
- trait_name,
- )
- .span_label(
- span,
- format!("manual implementations of `{}` are experimental", trait_name),
- )
- .help("add `#![feature(unboxed_closures)]` to the crate attributes to enable")
- .emit();
+ self.tcx().sess.emit_err(ManualImplementation { span, trait_name });
}
}
AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch,
GenericArgCountResult, GenericArgPosition,
};
-use crate::errors::{
- AssocTypeBindingNotAllowed, ExplicitGenericArgsWithImplTrait,
- ExplicitGenericArgsWithImplTraitFeature,
-};
+use crate::errors::{AssocTypeBindingNotAllowed, ExplicitGenericArgsWithImplTrait};
use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs};
use rustc_ast::ast::ParamKindOrd;
use rustc_errors::{struct_span_err, Applicability, Diagnostic, MultiSpan};
})
.collect::<Vec<_>>();
- let mut err = tcx.sess.create_err(ExplicitGenericArgsWithImplTrait { spans });
- if tcx.sess.is_nightly_build() {
- err.subdiagnostic(ExplicitGenericArgsWithImplTraitFeature);
- }
- err.emit();
+ tcx.sess.emit_err(ExplicitGenericArgsWithImplTrait {
+ spans,
+ is_nightly_build: tcx.sess.is_nightly_build().then_some(()),
+ });
}
impl_trait
use rustc_hir::{GenericArg, GenericArgs};
use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, Subst, SubstsRef};
use rustc_middle::ty::GenericParamDefKind;
-use rustc_middle::ty::{self, Const, DefIdTree, Ty, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{self, Const, DefIdTree, EarlyBinder, Ty, TyCtxt, TypeFoldable};
use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS};
use rustc_span::edition::Edition;
use rustc_span::lev_distance::find_best_match_for_name;
self.astconv
.normalize_ty(
self.span,
- tcx.at(self.span).type_of(param.def_id).subst(tcx, substs),
+ EarlyBinder(tcx.at(self.span).type_of(param.def_id))
+ .subst(tcx, substs),
)
.into()
}
GenericParamDefKind::Const { has_default } => {
let ty = tcx.at(self.span).type_of(param.def_id);
if !infer_args && has_default {
- tcx.const_param_default(param.def_id).subst(tcx, substs.unwrap()).into()
+ EarlyBinder(tcx.const_param_default(param.def_id))
+ .subst(tcx, substs.unwrap())
+ .into()
} else {
if infer_args {
self.astconv.ct_infer(ty, Some(param), self.span).into()
item_segment: &hir::PathSegment<'_>,
) -> Ty<'tcx> {
let substs = self.ast_path_substs_for_ty(span, did, item_segment);
- self.normalize_ty(span, self.tcx().at(span).type_of(did).subst(self.tcx(), substs))
+ self.normalize_ty(
+ span,
+ EarlyBinder(self.tcx().at(span).type_of(did)).subst(self.tcx(), substs),
+ )
}
fn conv_object_ty_poly_trait_ref(
true,
None,
);
- self.normalize_ty(span, tcx.at(span).type_of(def_id).subst(tcx, substs))
+ EarlyBinder(self.normalize_ty(span, tcx.at(span).type_of(def_id)))
+ .subst(tcx, substs)
}
hir::TyKind::Array(ref ty, ref length) => {
let length = match length {
trait_ref.def_id,
)?;
- let fn_sig = tcx.fn_sig(assoc.def_id).subst(
+ let fn_sig = tcx.bound_fn_sig(assoc.def_id).subst(
tcx,
trait_ref.substs.extend_to(tcx, assoc.def_id, |param, _| tcx.mk_param_from_def(param)),
);
hir::Guard::If(e) => {
self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {});
}
- hir::Guard::IfLet(pat, e) => {
- let scrutinee_ty = self.demand_scrutinee_type(
- e,
- pat.contains_explicit_ref_binding(),
- false,
- );
- self.check_pat_top(&pat, scrutinee_ty, None, true);
+ hir::Guard::IfLet(l) => {
+ self.check_expr_let(l);
}
};
}
) -> Ty<'tcx> {
let (fn_sig, def_id) = match *callee_ty.kind() {
ty::FnDef(def_id, subst) => {
- let fn_sig = self.tcx.fn_sig(def_id).subst(self.tcx, subst);
+ let fn_sig = self.tcx.bound_fn_sig(def_id).subst(self.tcx, subst);
// Unit testing: function items annotated with
// `#[rustc_evaluate_where_clauses]` trigger special output
let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(span));
let region = fcx.next_region_var(RegionVariableOrigin::MiscVariable(span));
- Some(tcx.type_of(va_list_did).subst(tcx, &[region.into()]))
+ Some(tcx.bound_type_of(va_list_did).subst(tcx, &[region.into()]))
} else {
None
};
span: Span,
origin: &hir::OpaqueTyOrigin,
) {
- let hidden_type = tcx.type_of(def_id).subst(tcx, substs);
+ let hidden_type = tcx.bound_type_of(def_id.to_def_id()).subst(tcx, substs);
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
let defining_use_anchor = match *origin {
if let Some(missing_items) = must_implement_one_of {
let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span);
let attr_span = tcx
- .get_attrs(impl_trait_ref.def_id)
- .iter()
- .find(|attr| attr.has_name(sym::rustc_must_implement_one_of))
+ .get_attr(impl_trait_ref.def_id, sym::rustc_must_implement_one_of)
.map(|attr| attr.span);
missing_items_must_implement_one_of_err(tcx, impl_span, missing_items, attr_span);
pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) {
let repr = def.repr();
if repr.packed() {
- for attr in tcx.get_attrs(def.did()).iter() {
- for r in attr::find_repr_attrs(&tcx.sess, attr) {
+ for attr in tcx.get_attrs(def.did(), sym::repr) {
+ for r in attr::parse_repr_attr(&tcx.sess, attr) {
if let attr::ReprPacked(pack) = r
- && let Some(repr_pack) = repr.pack
- && pack as u64 != repr_pack.bytes()
- {
- struct_span_err!(
- tcx.sess,
- sp,
- E0634,
- "type has conflicting packed representation hints"
- )
- .emit();
- }
+ && let Some(repr_pack) = repr.pack
+ && pack as u64 != repr_pack.bytes()
+ {
+ struct_span_err!(
+ tcx.sess,
+ sp,
+ E0634,
+ "type has conflicting packed representation hints"
+ )
+ .emit();
+ }
}
}
if repr.align.is_some() {
def.destructor(tcx); // force the destructor to be evaluated
if vs.is_empty() {
- let attributes = tcx.get_attrs(def_id.to_def_id());
- if let Some(attr) = tcx.sess.find_by_name(&attributes, sym::repr) {
+ if let Some(attr) = tcx.get_attr(def_id.to_def_id(), sym::repr) {
struct_span_err!(
tcx.sess,
attr.span,
) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
match *expected_ty.kind() {
ty::Opaque(def_id, substs) => {
- let bounds = self.tcx.explicit_item_bounds(def_id);
- let sig = bounds.iter().find_map(|(pred, span)| match pred.kind().skip_binder() {
- ty::PredicateKind::Projection(proj_predicate) => self
- .deduce_sig_from_projection(
- Some(*span),
- pred.kind().rebind(proj_predicate.subst(self.tcx, substs)),
- ),
- _ => None,
- });
+ let bounds = self.tcx.bound_explicit_item_bounds(def_id);
+ let sig = bounds
+ .transpose_iter()
+ .map(|e| e.map_bound(|e| *e).transpose_tuple2())
+ .find_map(|(pred, span)| match pred.0.kind().skip_binder() {
+ ty::PredicateKind::Projection(proj_predicate) => self
+ .deduce_sig_from_projection(
+ Some(span.0),
+ pred.0.kind().rebind(
+ pred.map_bound(|_| proj_predicate).subst(self.tcx, substs),
+ ),
+ ),
+ _ => None,
+ });
let kind = bounds
- .iter()
- .filter_map(|(pred, _)| match pred.kind().skip_binder() {
+ .transpose_iter()
+ .map(|e| e.map_bound(|e| *e).transpose_tuple2())
+ .filter_map(|(pred, _)| match pred.0.kind().skip_binder() {
ty::PredicateKind::Trait(tp) => {
self.tcx.fn_trait_kind_from_lang_item(tp.def_id())
}
),
};
- let item_bounds = self.tcx.explicit_item_bounds(def_id);
+ let item_bounds = self.tcx.bound_explicit_item_bounds(def_id);
// Search for a pending obligation like
//
//
// where R is the return type we are expecting. This type `T`
// will be our output.
- let output_ty = item_bounds.iter().find_map(|&(predicate, span)| {
- let bound_predicate = predicate.subst(self.tcx, substs).kind();
- if let ty::PredicateKind::Projection(proj_predicate) = bound_predicate.skip_binder() {
- self.deduce_future_output_from_projection(
- span,
- bound_predicate.rebind(proj_predicate),
- )
- } else {
- None
- }
- });
+ let output_ty = item_bounds
+ .transpose_iter()
+ .map(|e| e.map_bound(|e| *e).transpose_tuple2())
+ .find_map(|(predicate, span)| {
+ let bound_predicate = predicate.subst(self.tcx, substs).kind();
+ if let ty::PredicateKind::Projection(proj_predicate) = bound_predicate.skip_binder()
+ {
+ self.deduce_future_output_from_projection(
+ span.0,
+ bound_predicate.rebind(proj_predicate),
+ )
+ } else {
+ None
+ }
+ });
debug!("deduce_future_output_from_obligations: output_ty={:?}", output_ty);
output_ty
use rustc_span::symbol::sym;
use rustc_span::{self, BytePos, DesugaringKind, Span};
use rustc_target::spec::abi::Abi;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
+use rustc_trait_selection::infer::InferCtxtExt as _;
+use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
use smallvec::{smallvec, SmallVec};
)];
let mut has_unsized_tuple_coercion = false;
- let mut has_trait_upcasting_coercion = false;
+ let mut has_trait_upcasting_coercion = None;
// Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
// emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
&& data_a.principal_def_id() != data_b.principal_def_id()
{
debug!("coerce_unsized: found trait upcasting coercion");
- has_trait_upcasting_coercion = true;
+ has_trait_upcasting_coercion = Some((self_ty, unsize_ty));
}
if let ty::Tuple(..) = unsize_ty.kind() {
debug!("coerce_unsized: found unsized tuple coercion");
.emit();
}
- if has_trait_upcasting_coercion && !self.tcx().features().trait_upcasting {
- feature_err(
+ if let Some((sub, sup)) = has_trait_upcasting_coercion
+ && !self.tcx().features().trait_upcasting
+ {
+ // Renders better when we erase regions, since they're not really the point here.
+ let (sub, sup) = self.tcx.erase_regions((sub, sup));
+ let mut err = feature_err(
&self.tcx.sess.parse_sess,
sym::trait_upcasting,
self.cause.span,
- "trait upcasting coercion is experimental",
- )
- .emit();
+ &format!("cannot cast `{sub}` to `{sup}`, trait upcasting coercion is experimental"),
+ );
+ err.note(&format!("required when coercing `{source}` into `{target}`"));
+ err.emit();
}
Ok(coercion)
match b.kind() {
ty::FnPtr(b_sig) => {
let a_sig = a.fn_sig(self.tcx);
- // Intrinsics are not coercible to function pointers
- if a_sig.abi() == Abi::RustIntrinsic || a_sig.abi() == Abi::PlatformIntrinsic {
- return Err(TypeError::IntrinsicCast);
- }
+ if let ty::FnDef(def_id, _) = *a.kind() {
+ // Intrinsics are not coercible to function pointers
+ if self.tcx.is_intrinsic(def_id) {
+ return Err(TypeError::IntrinsicCast);
+ }
- // Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396).
- if let ty::FnDef(def_id, _) = *a.kind()
- && b_sig.unsafety() == hir::Unsafety::Normal
- && !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty()
- {
- return Err(TypeError::TargetFeatureCast(def_id));
+ // Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396).
+
+ if b_sig.unsafety() == hir::Unsafety::Normal
+ && !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty()
+ {
+ return Err(TypeError::TargetFeatureCast(def_id));
+ }
}
let InferOk { value: a_sig, obligations: o1 } =
.find_map(|(ty, steps)| self.probe(|_| coerce.unify(ty, target)).ok().map(|_| steps))
}
+ /// Given a type, this function will calculate and return the type given
+ /// for `<Ty as Deref>::Target` only if `Ty` also implements `DerefMut`.
+ ///
+ /// This function is for diagnostics only, since it does not register
+ /// trait or region sub-obligations. (presumably we could, but it's not
+ /// particularly important for diagnostics...)
+ pub fn deref_once_mutably_for_diagnostic(&self, expr_ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
+ self.autoderef(rustc_span::DUMMY_SP, expr_ty).nth(1).and_then(|(deref_ty, _)| {
+ self.infcx
+ .type_implements_trait(
+ self.infcx.tcx.lang_items().deref_mut_trait()?,
+ expr_ty,
+ ty::List::empty(),
+ self.param_env,
+ )
+ .may_apply()
+ .then(|| deref_ty)
+ })
+ }
+
/// Given some expressions, their known unified type and another expression,
/// tries to unify the types, potentially inserting coercions on any of the
/// provided expressions and returns their LUB (aka "common supertype").
let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig));
debug!("compare_impl_method: impl_fty={:?}", impl_fty);
- // First liberate late bound regions and subst placeholders
- let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, tcx.fn_sig(trait_m.def_id));
- let trait_sig = trait_sig.subst(tcx, trait_to_placeholder_substs);
+ let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs);
+ let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig);
let trait_sig =
inh.normalize_associated_types_in(impl_m_span, impl_m_hir_id, param_env, trait_sig);
// Add the resulting inputs and output as well-formed.
// Compute placeholder form of impl and trait const tys.
let impl_ty = tcx.type_of(impl_c.def_id);
- let trait_ty = tcx.type_of(trait_c.def_id).subst(tcx, trait_to_impl_substs);
+ let trait_ty = tcx.bound_type_of(trait_c.def_id).subst(tcx, trait_to_impl_substs);
let mut cause = ObligationCause::new(
impl_c_span,
impl_c_hir_id,
};
let obligations = tcx
- .explicit_item_bounds(trait_ty.def_id)
- .iter()
- .map(|&(bound, span)| {
+ .bound_explicit_item_bounds(trait_ty.def_id)
+ .transpose_iter()
+ .map(|e| e.map_bound(|e| *e).transpose_tuple2())
+ .map(|(bound, span)| {
debug!(?bound);
let concrete_ty_bound = bound.subst(tcx, rebased_substs);
debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound);
- traits::Obligation::new(mk_cause(span), param_env, concrete_ty_bound)
+ traits::Obligation::new(mk_cause(span.0), param_env, concrete_ty_bound)
})
.collect();
debug!("check_type_bounds: item_bounds={:?}", obligations);
self.has_only_self_parameter(m)
&& self
.tcx
- .get_attrs(m.def_id)
- .iter()
// This special internal attribute is used to permit
// "identity-like" conversion methods to be suggested here.
//
//
// FIXME? Other potential candidate methods: `as_ref` and
// `as_mut`?
- .any(|a| a.has_name(sym::rustc_conversion_suggestion))
+ .has_attr(m.def_id, sym::rustc_conversion_suggestion)
});
methods
};
if let Some(hir::Node::Expr(hir::Expr {
- kind: hir::ExprKind::Assign(left_expr, ..),
+ kind: hir::ExprKind::Assign(..),
..
})) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id))
{
if mutability == hir::Mutability::Mut {
- // Found the following case:
- // fn foo(opt: &mut Option<String>){ opt = None }
- // --- ^^^^
- // | |
- // consider dereferencing here: `*opt` |
- // expected mutable reference, found enum `Option`
- if sm.span_to_snippet(left_expr.span).is_ok() {
- return Some((
- left_expr.span.shrink_to_lo(),
- "consider dereferencing here to assign to the mutable \
- borrowed piece of memory"
- .to_string(),
- "*".to_string(),
- Applicability::MachineApplicable,
- true,
- ));
- }
+ // Suppressing this diagnostic, we'll properly print it in `check_expr_assign`
+ return None;
}
}
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
use rustc_middle::ty::subst::{Subst, SubstsRef};
-use rustc_middle::ty::{self, Predicate, Ty, TyCtxt};
+use rustc_middle::ty::{self, EarlyBinder, Predicate, Ty, TyCtxt};
use rustc_span::Span;
use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
use rustc_trait_selection::traits::query::dropck_outlives::AtExt;
let drop_impl_span = tcx.def_span(drop_impl_did);
let fresh_impl_substs =
infcx.fresh_substs_for_item(drop_impl_span, drop_impl_did.to_def_id());
- let fresh_impl_self_ty = drop_impl_ty.subst(tcx, fresh_impl_substs);
+ let fresh_impl_self_ty = EarlyBinder(drop_impl_ty).subst(tcx, fresh_impl_substs);
let cause = &ObligationCause::misc(drop_impl_span, drop_impl_hir_id);
match infcx.at(cause, impl_param_env).eq(named_type, fresh_impl_self_ty) {
use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{BytePos, Pos};
+use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::{self, ObligationCauseCode};
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
lhs: &'tcx hir::Expr<'tcx>,
err_code: &'static str,
op_span: Span,
+ adjust_err: impl FnOnce(&mut DiagnosticBuilder<'tcx, ErrorGuaranteed>),
) {
if lhs.is_syntactic_place_expr() {
return;
);
});
+ adjust_err(&mut err);
+
err.emit();
}
return self.tcx.ty_error();
}
- self.check_lhs_assignable(lhs, "E0070", span);
-
let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace);
- let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty, Some(lhs));
+
+ let suggest_deref_binop = |err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+ rhs_ty: Ty<'tcx>| {
+ if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
+ // Can only assign if the type is sized, so if `DerefMut` yields a type that is
+ // unsized, do not suggest dereferencing it.
+ let lhs_deref_ty_is_sized = self
+ .infcx
+ .type_implements_trait(
+ self.tcx.lang_items().sized_trait().unwrap(),
+ lhs_deref_ty,
+ ty::List::empty(),
+ self.param_env,
+ )
+ .may_apply();
+ if lhs_deref_ty_is_sized && self.can_coerce(rhs_ty, lhs_deref_ty) {
+ err.span_suggestion_verbose(
+ lhs.span.shrink_to_lo(),
+ "consider dereferencing here to assign to the mutably borrowed value",
+ "*".to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ };
+
+ self.check_lhs_assignable(lhs, "E0070", span, |err| {
+ let rhs_ty = self.check_expr(&rhs);
+ suggest_deref_binop(err, rhs_ty);
+ });
+
+ // This is (basically) inlined `check_expr_coercable_to_type`, but we want
+ // to suggest an additional fixup here in `suggest_deref_binop`.
+ let rhs_ty = self.check_expr_with_hint(&rhs, lhs_ty);
+ if let (_, Some(mut diag)) =
+ self.demand_coerce_diag(rhs, rhs_ty, lhs_ty, Some(lhs), AllowTwoPhase::No)
+ {
+ suggest_deref_binop(&mut diag, rhs_ty);
+ diag.emit();
+ }
self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized);
}
}
- fn check_expr_let(&self, let_expr: &'tcx hir::Let<'tcx>) -> Ty<'tcx> {
+ pub(super) fn check_expr_let(&self, let_expr: &'tcx hir::Let<'tcx>) -> Ty<'tcx> {
// for let statements, this is done in check_stmt
let init = let_expr.init;
self.warn_if_unreachable(init.hir_id, init.span, "block in `let` expression");
self.check_expr_asm_operand(out_expr, false);
}
}
- hir::InlineAsmOperand::Const { anon_const }
- | hir::InlineAsmOperand::SymFn { anon_const } => {
- self.to_const(anon_const);
- }
+ // `AnonConst`s have their own body and is type-checked separately.
+ // As they don't flow into the type system we don't need them to
+ // be well-formed.
+ hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymFn { .. } => {}
hir::InlineAsmOperand::SymStatic { .. } => {}
}
}
self, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSelfTy, UserSubsts,
};
use rustc_middle::ty::{
- self, AdtKind, CanonicalUserType, DefIdTree, GenericParamDefKind, ToPolyTraitRef, ToPredicate,
- Ty, UserType,
+ self, AdtKind, CanonicalUserType, DefIdTree, EarlyBinder, GenericParamDefKind, ToPolyTraitRef,
+ ToPredicate, Ty, UserType,
};
use rustc_session::lint;
use rustc_span::hygiene::DesugaringKind;
T: TypeFoldable<'tcx>,
{
debug!("instantiate_type_scheme(value={:?}, substs={:?})", value, substs);
- let value = value.subst(self.tcx, substs);
+ let value = EarlyBinder(value).subst(self.tcx, substs);
let result = self.normalize_associated_types_in(span, value);
debug!("instantiate_type_scheme = {:?}", result);
result
let def_kind = self.tcx.def_kind(def_id);
let item_ty = if let DefKind::Variant = def_kind {
- self.tcx.type_of(self.tcx.parent(def_id))
+ self.tcx.bound_type_of(self.tcx.parent(def_id))
} else {
- self.tcx.type_of(def_id)
+ self.tcx.bound_type_of(def_id)
};
let substs = self.infcx.fresh_substs_for_item(span, def_id);
let ty = item_ty.subst(self.tcx, substs);
) {
let (sig, did, substs) = match (&expected.kind(), &found.kind()) {
(ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
- let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1);
- let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2);
+ let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1);
+ let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2);
if sig1 != sig2 {
return;
}
(sig1, *did1, substs1)
}
(ty::FnDef(did, substs), ty::FnPtr(sig2)) => {
- let sig1 = self.tcx.fn_sig(*did).subst(self.tcx, substs);
+ let sig1 = self.tcx.bound_fn_sig(*did).subst(self.tcx, substs);
if sig1 != *sig2 {
return;
}
// If we have a default, then we it doesn't matter that we're not
// inferring the type arguments: we provide the default where any
// is missing.
- let default = tcx.type_of(param.def_id);
+ let default = tcx.bound_type_of(param.def_id);
self.fcx
.normalize_ty(self.span, default.subst(tcx, substs.unwrap()))
.into()
}
GenericParamDefKind::Const { has_default } => {
if !infer_args && has_default {
- tcx.const_param_default(param.def_id).subst(tcx, substs.unwrap()).into()
+ EarlyBinder(tcx.const_param_default(param.def_id))
+ .subst(tcx, substs.unwrap())
+ .into()
} else {
self.fcx.var_for_def(self.span, param)
}
use crate::structured_errors::StructuredDiagnostic;
use rustc_ast as ast;
-use rustc_data_structures::sync::Lrc;
use rustc_errors::{Applicability, Diagnostic, DiagnosticId, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res};
// Peel derived obligation, because it's the type that originally
// started this inference chain that matters, not the one we wound
// up with at the end.
- fn unpeel_to_top(
- mut code: Lrc<ObligationCauseCode<'_>>,
- ) -> Lrc<ObligationCauseCode<'_>> {
- let mut result_code = code.clone();
+ fn unpeel_to_top<'a, 'tcx>(
+ mut code: &'a ObligationCauseCode<'tcx>,
+ ) -> &'a ObligationCauseCode<'tcx> {
+ let mut result_code = code;
loop {
- let parent = match &*code {
- ObligationCauseCode::ImplDerivedObligation(c) => {
- c.derived.parent_code.clone()
- }
+ let parent = match code {
+ ObligationCauseCode::ImplDerivedObligation(c) => &c.derived.parent_code,
ObligationCauseCode::BuiltinDerivedObligation(c)
- | ObligationCauseCode::DerivedObligation(c) => c.parent_code.clone(),
- _ => break,
+ | ObligationCauseCode::DerivedObligation(c) => &c.parent_code,
+ _ => break result_code,
};
- result_code = std::mem::replace(&mut code, parent);
+ (result_code, code) = (code, parent);
}
- result_code
}
- let self_: ty::subst::GenericArg<'_> = match &*unpeel_to_top(error.obligation.cause.clone_code()) {
+ let self_: ty::subst::GenericArg<'_> = match unpeel_to_top(error.obligation.cause.code()) {
ObligationCauseCode::BuiltinDerivedObligation(code) |
ObligationCauseCode::DerivedObligation(code) => {
code.parent_trait_pred.self_ty().skip_binder().into()
// We make sure that only *one* argument matches the obligation failure
// and we assign the obligation's span to its expression's.
error.obligation.cause.span = args[ref_in].span;
- let parent_code = error.obligation.cause.clone_code();
- *error.obligation.cause.make_mut_code() =
+ error.obligation.cause.map_code(|parent_code| {
ObligationCauseCode::FunctionArgumentObligation {
arg_hir_id: args[ref_in].hir_id,
call_hir_id: expr.hir_id,
parent_code,
- };
+ }
+ });
} else if error.obligation.cause.span == call_sp {
// Make function calls point at the callee, not the whole thing.
if let hir::ExprKind::Call(callee, _) = expr.kind {
Guard::If(ref e) => {
self.visit_expr(e);
}
- Guard::IfLet(ref pat, ref e) => {
- self.visit_pat(pat);
- self.visit_expr(e);
+ Guard::IfLet(ref l) => {
+ self.visit_let_expr(l);
}
}
hir_id: HirId,
data: SuspendCheckData<'_, '_>,
) -> bool {
- for attr in tcx.get_attrs(def_id).iter() {
- if attr.has_name(sym::must_not_suspend) {
- tcx.struct_span_lint_hir(
- rustc_session::lint::builtin::MUST_NOT_SUSPEND,
- hir_id,
- data.source_span,
- |lint| {
- let msg = format!(
- "{}`{}`{} held across a suspend point, but should not be",
- data.descr_pre,
- tcx.def_path_str(def_id),
- data.descr_post,
- );
- let mut err = lint.build(&msg);
+ if let Some(attr) = tcx.get_attr(def_id, sym::must_not_suspend) {
+ tcx.struct_span_lint_hir(
+ rustc_session::lint::builtin::MUST_NOT_SUSPEND,
+ hir_id,
+ data.source_span,
+ |lint| {
+ let msg = format!(
+ "{}`{}`{} held across a suspend point, but should not be",
+ data.descr_pre,
+ tcx.def_path_str(def_id),
+ data.descr_post,
+ );
+ let mut err = lint.build(&msg);
- // add span pointing to the offending yield/await
- err.span_label(data.yield_span, "the value is held across this suspend point");
+ // add span pointing to the offending yield/await
+ err.span_label(data.yield_span, "the value is held across this suspend point");
- // Add optional reason note
- if let Some(note) = attr.value_str() {
- // FIXME(guswynn): consider formatting this better
- err.span_note(data.source_span, note.as_str());
- }
+ // Add optional reason note
+ if let Some(note) = attr.value_str() {
+ // FIXME(guswynn): consider formatting this better
+ err.span_note(data.source_span, note.as_str());
+ }
- // Add some quick suggestions on what to do
- // FIXME: can `drop` work as a suggestion here as well?
- err.span_help(
- data.source_span,
- "consider using a block (`{ ... }`) \
- to shrink the value's scope, ending before the suspend point",
- );
+ // Add some quick suggestions on what to do
+ // FIXME: can `drop` work as a suggestion here as well?
+ err.span_help(
+ data.source_span,
+ "consider using a block (`{ ... }`) \
+ to shrink the value's scope, ending before the suspend point",
+ );
- err.emit();
- },
- );
+ err.emit();
+ },
+ );
- return true;
- }
+ true
+ } else {
+ false
}
- false
}
// B -> C and E -> F are added implicitly due to the traversal order.
match guard {
Some(Guard::If(expr)) => self.visit_expr(expr),
- Some(Guard::IfLet(pat, expr)) => {
- self.visit_pat(pat);
- self.visit_expr(expr);
+ Some(Guard::IfLet(let_expr)) => {
+ self.visit_let_expr(let_expr);
}
None => (),
}
diag_expr_id: HirId,
) {
debug!("mutate {assignee_place:?}; diag_expr_id={diag_expr_id:?}");
+
+ if assignee_place.place.base == PlaceBase::Rvalue
+ && assignee_place.place.projections.is_empty()
+ {
+ // Assigning to an Rvalue is illegal unless done through a dereference. We would have
+ // already gotten a type error, so we will just return here.
+ return;
+ }
+
// If the type being assigned needs dropped, then the mutation counts as a borrow
// since it is essentially doing `Drop::drop(&mut x); x = new_value;`.
if assignee_place.place.base_ty.needs_drop(self.tcx, self.param_env) {
ty::INNERMOST,
ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BrEnv },
));
- let va_list_ty = tcx.type_of(did).subst(tcx, &[region.into()]);
+ let va_list_ty = tcx.bound_type_of(did).subst(tcx, &[region.into()]);
(tcx.mk_ref(env_region, ty::TypeAndMut { ty: va_list_ty, mutbl }), va_list_ty)
})
};
sym::ptr_offset_from => {
(1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.isize)
}
+ sym::ptr_offset_from_unsigned => {
+ (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.usize)
+ }
sym::unchecked_div | sym::unchecked_rem | sym::exact_div => {
(1, vec![param(0), param(0)], param(0))
}
debug!("method_predicates after subst = {:?}", method_predicates);
- let sig = self.tcx.fn_sig(def_id);
+ let sig = self.tcx.bound_fn_sig(def_id);
let sig = sig.subst(self.tcx, all_substs);
debug!("type scheme substituted, sig={:?}", sig);
// N.B., instantiate late-bound regions first so that
// `instantiate_type_scheme` can normalize associated types that
// may reference those regions.
- let fn_sig = tcx.fn_sig(def_id);
+ let fn_sig = tcx.bound_fn_sig(def_id);
let fn_sig = fn_sig.subst(self.tcx, substs);
let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig).0;
use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
use rustc_middle::ty::GenericParamDefKind;
-use rustc_middle::ty::{self, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{self, EarlyBinder, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable};
use rustc_session::lint;
use rustc_span::def_id::LocalDefId;
use rustc_span::lev_distance::{
}
let (impl_ty, impl_substs) = self.impl_ty_and_substs(impl_def_id);
- let impl_ty = impl_ty.subst(self.tcx, impl_substs);
+ let impl_ty = EarlyBinder(impl_ty).subst(self.tcx, impl_substs);
debug!("impl_ty: {:?}", impl_ty);
) -> bool {
match method.kind {
ty::AssocKind::Fn => {
- let fty = self.tcx.fn_sig(method.def_id);
+ let fty = self.tcx.bound_fn_sig(method.def_id);
self.probe(|_| {
let substs = self.fresh_substs_for_item(self.span, method.def_id);
let fty = fty.subst(self.tcx, substs);
#[instrument(level = "debug", skip(self))]
fn xform_method_sig(&self, method: DefId, substs: SubstsRef<'tcx>) -> ty::FnSig<'tcx> {
- let fn_sig = self.tcx.fn_sig(method);
+ let fn_sig = self.tcx.bound_fn_sig(method);
debug!(?fn_sig);
assert!(!substs.has_escaping_bound_vars());
return_ty
};
- self.check_lhs_assignable(lhs, "E0067", op.span);
+ self.check_lhs_assignable(lhs, "E0067", op.span, |err| {
+ if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
+ if self
+ .lookup_op_method(
+ lhs_deref_ty,
+ Some(rhs_ty),
+ Some(rhs),
+ Op::Binary(op, IsAssign::Yes),
+ )
+ .is_ok()
+ {
+ // Suppress this error, since we already emitted
+ // a deref suggestion in check_overloaded_binop
+ err.delay_as_bug();
+ }
+ }
+ });
ty
}
(err, missing_trait, use_output)
}
};
- if let Ref(_, rty, _) = lhs_ty.kind() {
- if self.infcx.type_is_copy_modulo_regions(self.param_env, *rty, lhs_expr.span)
- && self
- .lookup_op_method(
- *rty,
- Some(rhs_ty),
- Some(rhs_expr),
- Op::Binary(op, is_assign),
- )
- .is_ok()
+
+ let mut suggest_deref_binop = |lhs_deref_ty: Ty<'tcx>| {
+ if self
+ .lookup_op_method(
+ lhs_deref_ty,
+ Some(rhs_ty),
+ Some(rhs_expr),
+ Op::Binary(op, is_assign),
+ )
+ .is_ok()
{
if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) {
let msg = &format!(
IsAssign::Yes => "=",
IsAssign::No => "",
},
- rty.peel_refs(),
+ lhs_deref_ty.peel_refs(),
lstring,
);
err.span_suggestion_verbose(
);
}
}
+ };
+
+ // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest
+ // `a += b` => `*a += b` if a is a mut ref.
+ if is_assign == IsAssign::Yes
+ && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
+ suggest_deref_binop(lhs_deref_ty);
+ } else if is_assign == IsAssign::No
+ && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind() {
+ if self.infcx.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty, lhs_expr.span) {
+ suggest_deref_binop(*lhs_deref_ty);
+ }
}
if let Some(missing_trait) = missing_trait {
let mut visitor = TypeParamVisitor(vec![]);
use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst};
use rustc_middle::ty::trait_def::TraitSpecializationKind;
use rustc_middle::ty::{
- self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitor,
+ self, AdtKind, EarlyBinder, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable,
+ TypeVisitor,
};
use rustc_session::parse::feature_err;
use rustc_span::symbol::{sym, Ident, Symbol};
}
let mut param_count = CountParams::default();
let has_region = pred.visit_with(&mut param_count).is_break();
- let substituted_pred = pred.subst(tcx, substs);
+ let substituted_pred = EarlyBinder(pred).subst(tcx, substs);
// Don't check non-defaulted params, dependent defaults (including lifetimes)
// or preds with multiple params.
if substituted_pred.has_param_types_or_consts()
tcx.struct_span_lint_hir(lint, id, span, |lint| {
// Removal suggestion span needs to include attributes (Issue #54400)
let span_with_attrs = tcx
- .get_attrs(extern_crate.def_id)
+ .hir()
+ .attrs(id)
.iter()
.map(|attr| attr.span)
.fold(span, |acc, attr_span| acc.to(attr_span));
continue;
}
+ let id = tcx.hir().local_def_id_to_hir_id(def_id);
// If the extern crate has any attributes, they may have funky
// semantics we can't faithfully represent using `use` (most
// notably `#[macro_use]`). Ignore it.
- if !tcx.get_attrs(extern_crate.def_id).is_empty() {
+ if !tcx.hir().attrs(id).is_empty() {
continue;
}
- let id = tcx.hir().local_def_id_to_hir_id(def_id);
tcx.struct_span_lint_hir(lint, id, extern_crate.span, |lint| {
// Otherwise, we can convert it into a `use` of some kind.
let base_replacement = match extern_crate.orig_name {
// Main entry point
fn collect_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
- tcx.hir().visit_item_likes_in_module(
- module_def_id,
- &mut CollectItemTypesVisitor { tcx }.as_deep_visitor(),
- );
+ tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut CollectItemTypesVisitor { tcx });
}
pub fn provide(providers: &mut Providers) {
ty::trait_def::TraitSpecializationKind::None
};
let must_implement_one_of = tcx
- .get_attrs(def_id)
- .iter()
- .find(|attr| attr.has_name(sym::rustc_must_implement_one_of))
+ .get_attr(def_id, sym::rustc_must_implement_one_of)
// Check that there are at least 2 arguments of `#[rustc_must_implement_one_of]`
// and that they are all identifiers
.and_then(|attr| match attr.meta_item_list() {
Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) => {
Some(tcx.typeck_root_def_id(def_id))
}
+ // Exclude `GlobalAsm` here which cannot have generics.
+ Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. })
+ if asm.operands.iter().any(|(op, _op_sp)| match op {
+ hir::InlineAsmOperand::Const { anon_const }
+ | hir::InlineAsmOperand::SymFn { anon_const } => {
+ anon_const.hir_id == hir_id
+ }
+ _ => false,
+ }) =>
+ {
+ Some(parent_def_id.to_def_id())
+ }
_ => None,
}
}
//! Errors emitted by typeck.
-use rustc_errors::Applicability;
+use rustc_errors::{
+ error_code, Applicability, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed,
+};
use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
use rustc_middle::ty::Ty;
+use rustc_session::{parse::ParseSess, SessionDiagnostic};
use rustc_span::{symbol::Ident, Span, Symbol};
#[derive(SessionDiagnostic)]
#[primary_span]
#[label]
pub spans: Vec<Span>,
+ #[help]
+ pub is_nightly_build: Option<()>,
}
-#[derive(SessionSubdiagnostic)]
-#[help(slug = "typeck-explicit-generic-args-with-impl-trait-feature")]
-pub struct ExplicitGenericArgsWithImplTraitFeature;
+pub struct MissingTypeParams {
+ pub span: Span,
+ pub def_span: Span,
+ pub missing_type_params: Vec<String>,
+ pub empty_generic_args: bool,
+}
+
+// Manual implementation of `SessionDiagnostic` to be able to call `span_to_snippet`.
+impl<'a> SessionDiagnostic<'a> for MissingTypeParams {
+ fn into_diagnostic(self, sess: &'a ParseSess) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+ static SLUG: &'static str = "typeck-missing-type-params";
+ let mut err = sess.span_diagnostic.struct_span_err_with_code(
+ self.span,
+ DiagnosticMessage::fluent(SLUG),
+ error_code!(E0393),
+ );
+ err.set_arg("parameterCount", self.missing_type_params.len());
+ err.set_arg(
+ "parameters",
+ self.missing_type_params
+ .iter()
+ .map(|n| format!("`{}`", n))
+ .collect::<Vec<_>>()
+ .join(", "),
+ );
+
+ err.span_label(self.def_span, DiagnosticMessage::fluent_attr(SLUG, "label"));
+
+ let mut suggested = false;
+ if let (Ok(snippet), true) = (
+ sess.source_map().span_to_snippet(self.span),
+ // Don't suggest setting the type params if there are some already: the order is
+ // tricky to get right and the user will already know what the syntax is.
+ self.empty_generic_args,
+ ) {
+ if snippet.ends_with('>') {
+ // The user wrote `Trait<'a, T>` or similar. To provide an accurate suggestion
+ // we would have to preserve the right order. For now, as clearly the user is
+ // aware of the syntax, we do nothing.
+ } else {
+ // The user wrote `Iterator`, so we don't have a type we can suggest, but at
+ // least we can clue them to the correct syntax `Iterator<Type>`.
+ err.span_suggestion(
+ self.span,
+ DiagnosticMessage::fluent_attr(SLUG, "suggestion"),
+ format!("{}<{}>", snippet, self.missing_type_params.join(", ")),
+ Applicability::HasPlaceholders,
+ );
+ suggested = true;
+ }
+ }
+ if !suggested {
+ err.span_label(self.span, DiagnosticMessage::fluent_attr(SLUG, "no-suggestion-label"));
+ }
+
+ err.note(DiagnosticMessage::fluent_attr(SLUG, "note"));
+ err
+ }
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0183", slug = "typeck-manual-implementation")]
+#[help]
+pub struct ManualImplementation {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ pub trait_name: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(slug = "typeck-substs-on-overridden-impl")]
+pub struct SubstsOnOverriddenImpl {
+ #[primary_span]
+ pub span: Span,
+}
if let Some(hir::Guard::If(e)) = arm.guard {
self.consume_expr(e)
- } else if let Some(hir::Guard::IfLet(_, ref e)) = arm.guard {
- self.consume_expr(e)
+ } else if let Some(hir::Guard::IfLet(ref l)) = arm.guard {
+ self.consume_expr(l.init)
}
self.consume_expr(arm.body);
//! on traits with methods can.
use crate::constrained_generic_params as cgp;
+use crate::errors::SubstsOnOverriddenImpl;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def_id::{DefId, LocalDefId};
let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty());
infcx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env, RegionckMode::default());
let Ok(impl2_substs) = infcx.fully_resolve(impl2_substs) else {
- tcx.sess.struct_span_err(span, "could not resolve substs on overridden impl").emit();
+ tcx.sess.emit_err(SubstsOnOverriddenImpl { span });
return None;
};
Some((impl1_substs, impl2_substs))
error = true;
}
- for attr in tcx.get_attrs(main_def_id) {
- if attr.has_name(sym::track_caller) {
- tcx.sess
- .struct_span_err(
- attr.span,
- "`main` function is not allowed to be `#[track_caller]`",
- )
- .span_label(main_span, "`main` function is not allowed to be `#[track_caller]`")
- .emit();
- error = true;
- }
+ for attr in tcx.get_attrs(main_def_id, sym::track_caller) {
+ tcx.sess
+ .struct_span_err(attr.span, "`main` function is not allowed to be `#[track_caller]`")
+ .span_label(main_span, "`main` function is not allowed to be `#[track_caller]`")
+ .emit();
+ error = true;
}
if error {
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt};
use rustc_span::Span;
use super::explicit::ExplicitPredicatesMap;
// `unsubstituted_predicate` is `U: 'b` in the
// example above. So apply the substitution to
// get `T: 'a` (or `predicate`):
- let predicate = unsubstituted_predicate.subst(tcx, substs);
+ let predicate = EarlyBinder(*unsubstituted_predicate).subst(tcx, substs);
insert_outlives_predicate(
tcx,
predicate.0,
continue;
}
- let predicate = outlives_predicate.subst(tcx, substs);
+ let predicate = EarlyBinder(*outlives_predicate).subst(tcx, substs);
debug!("predicate = {:?}", &predicate);
insert_outlives_predicate(tcx, predicate.0, predicate.1, span, required_predicates);
}
#[cfg(all(not(no_global_oom_handling), test))]
pub use std::alloc::handle_alloc_error;
-#[cfg(all(not(no_global_oom_handling), not(any(target_os = "hermit", test))))]
+#[cfg(all(not(no_global_oom_handling), not(test)))]
#[doc(hidden)]
#[allow(unused_attributes)]
#[unstable(feature = "alloc_internals", issue = "none")]
#![feature(pattern)]
#![feature(ptr_internals)]
#![feature(ptr_metadata)]
+#![feature(ptr_sub_ptr)]
#![feature(receiver_trait)]
#![feature(set_ptr_value)]
#![feature(slice_group_by)]
// `slice::into_vec` function which is only available with cfg(test)
// NB see the slice::hack module in slice.rs for more information
#[cfg(all(not(no_global_oom_handling), test))]
+#[cfg_attr(not(bootstrap), allow(unused_macro_rules))]
macro_rules! vec {
() => (
$crate::vec::Vec::new()
/// Like `new`, but parameterized over the choice of allocator for
/// the returned `RawVec`.
- #[rustc_allow_const_fn_unstable(const_fn)]
pub const fn new_in(alloc: A) -> Self {
// `cap: 0` means "unallocated". zero-sized types are ignored.
Self { ptr: Unique::dangling(), cap: 0, alloc }
fn drop(&mut self) {
// `T` is not a zero-sized type, and these are pointers into a slice's elements.
unsafe {
- let len = self.end.offset_from(self.start) as usize;
+ let len = self.end.sub_ptr(self.start);
ptr::copy_nonoverlapping(self.start, self.dest, len);
}
}
// it from the original vec but also avoid creating a &mut to the front since that could
// invalidate raw pointers to it which some unsafe code might rely on.
let vec_ptr = vec.as_mut().as_mut_ptr();
- let drop_offset = drop_ptr.offset_from(vec_ptr) as usize;
+ let drop_offset = drop_ptr.sub_ptr(vec_ptr);
let to_drop = ptr::slice_from_raw_parts_mut(vec_ptr.add(drop_offset), drop_len);
ptr::drop_in_place(to_drop);
}
let sink =
self.try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(end)).unwrap();
// iteration succeeded, don't drop head
- unsafe { ManuallyDrop::new(sink).dst.offset_from(dst_buf) as usize }
+ unsafe { ManuallyDrop::new(sink).dst.sub_ptr(dst_buf) }
}
}
impl<T> InPlaceDrop<T> {
fn len(&self) -> usize {
- unsafe { self.dst.offset_from(self.inner) as usize }
+ unsafe { self.dst.sub_ptr(self.inner) }
}
}
let exact = if mem::size_of::<T>() == 0 {
self.end.addr().wrapping_sub(self.ptr.addr())
} else {
- unsafe { self.end.offset_from(self.ptr) as usize }
+ unsafe { self.end.sub_ptr(self.ptr) }
};
(exact, Some(exact))
}
///
/// If this returns a non-null pointer, then ownership of the memory block
/// referenced by `ptr` has been transferred to this allocator.
- /// The memory may or may not have been deallocated,
- /// and should be considered unusable (unless of course it was
- /// transferred back to the caller again via the return value of
- /// this method). The new memory block is allocated with `layout`, but
- /// with the `size` updated to `new_size`. This new layout should be
+ /// The memory may or may not have been deallocated, and should be
+ /// considered unusable. The new memory block is allocated with `layout`,
+ /// but with the `size` updated to `new_size`. This new layout should be
/// used when deallocating the new memory block with `dealloc`. The range
/// `0..min(layout.size(), new_size)` of the new memory block is
/// guaranteed to have the same values as the original block.
///
/// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been
/// transferred to this allocator. The memory may or may not have been freed, and should be
- /// considered unusable unless it was transferred back to the caller again via the return value
- /// of this method.
+ /// considered unusable.
///
/// If this method returns `Err`, then ownership of the memory block has not been transferred to
/// this allocator, and the contents of the memory block are unaltered.
///
/// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been
/// transferred to this allocator. The memory may or may not have been freed, and should be
- /// considered unusable unless it was transferred back to the caller again via the return value
- /// of this method.
+ /// considered unusable.
///
/// If this method returns `Err`, then ownership of the memory block has not been transferred to
/// this allocator, and the contents of the memory block are unaltered.
Fold: FnMut(Acc, Self::Item) -> Acc,
{
let data = &mut self.data;
- self.alive.by_ref().fold(init, |acc, idx| {
+ iter::ByRefSized(&mut self.alive).fold(init, |acc, idx| {
// SAFETY: idx is obtained by folding over the `alive` range, which implies the
// value is currently considered alive but as the range is being consumed each value
// we read here will only be read once and then considered dead.
})
}
+ #[inline]
+ fn rfold<Acc, Fold>(mut self, init: Acc, mut rfold: Fold) -> Acc
+ where
+ Fold: FnMut(Acc, Self::Item) -> Acc,
+ {
+ let data = &mut self.data;
+ iter::ByRefSized(&mut self.alive).rfold(init, |acc, idx| {
+ // SAFETY: idx is obtained by folding over the `alive` range, which implies the
+ // value is currently considered alive but as the range is being consumed each value
+ // we read here will only be read once and then considered dead.
+ rfold(acc, unsafe { data.get_unchecked(idx).assume_init_read() })
+ })
+ }
+
fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
let len = self.len();
use crate::intrinsics;
-/// Informs the compiler that this point in the code is not reachable, enabling
-/// further optimizations.
+/// Informs the compiler that the site which is calling this function is not
+/// reachable, possibly enabling further optimizations.
///
/// # Safety
///
-/// Reaching this function is completely *undefined behavior* (UB). In
-/// particular, the compiler assumes that all UB must never happen, and
-/// therefore will eliminate all branches that reach to a call to
-/// `unreachable_unchecked()`.
+/// Reaching this function is *Undefined Behavior*.
///
-/// Like all instances of UB, if this assumption turns out to be wrong, i.e., the
-/// `unreachable_unchecked()` call is actually reachable among all possible
-/// control flow, the compiler will apply the wrong optimization strategy, and
-/// may sometimes even corrupt seemingly unrelated code, causing
-/// difficult-to-debug problems.
+/// As the compiler assumes that all forms of Undefined Behavior can never
+/// happen, it will eliminate all branches in the surrounding code that it can
+/// determine will invariably lead to a call to `unreachable_unchecked()`.
///
-/// Use this function only when you can prove that the code will never call it.
-/// Otherwise, consider using the [`unreachable!`] macro, which does not allow
-/// optimizations but will panic when executed.
+/// If the assumptions embedded in using this function turn out to be wrong -
+/// that is, if the site which is calling `unreachable_unchecked()` is actually
+/// reachable at runtime - the compiler may have generated nonsensical machine
+/// instructions for this situation, including in seemingly unrelated code,
+/// causing difficult-to-debug problems.
///
-/// # Example
+/// Use this function sparingly. Consider using the [`unreachable!`] macro,
+/// which may prevent some optimizations but will safely panic in case it is
+/// actually reached at runtime. Benchmark your code to find out if using
+/// `unreachable_unchecked()` comes with a performance benefit.
+///
+/// # Examples
+///
+/// `unreachable_unchecked()` can be used in situations where the compiler
+/// can't prove invariants that were previously established. Such situations
+/// have a higher chance of occuring if those invariants are upheld by
+/// external code that the compiler can't analyze.
+/// ```
+/// fn prepare_inputs(divisors: &mut Vec<u32>) {
+/// // Note to future-self when making changes: The invariant established
+/// // here is NOT checked in `do_computation()`; if this changes, you HAVE
+/// // to change `do_computation()`.
+/// divisors.retain(|divisor| *divisor != 0)
+/// }
+///
+/// /// # Safety
+/// /// All elements of `divisor` must be non-zero.
+/// unsafe fn do_computation(i: u32, divisors: &[u32]) -> u32 {
+/// divisors.iter().fold(i, |acc, divisor| {
+/// // Convince the compiler that a division by zero can't happen here
+/// // and a check is not needed below.
+/// if *divisor == 0 {
+/// // Safety: `divisor` can't be zero because of `prepare_inputs`,
+/// // but the compiler does not know about this. We *promise*
+/// // that we always call `prepare_inputs`.
+/// std::hint::unreachable_unchecked()
+/// }
+/// // The compiler would normally introduce a check here that prevents
+/// // a division by zero. However, if `divisor` was zero, the branch
+/// // above would reach what we explicitly marked as unreachable.
+/// // The compiler concludes that `divisor` can't be zero at this point
+/// // and removes the - now proven useless - check.
+/// acc / divisor
+/// })
+/// }
+///
+/// let mut divisors = vec![2, 0, 4];
+/// prepare_inputs(&mut divisors);
+/// let result = unsafe {
+/// // Safety: prepare_inputs() guarantees that divisors is non-zero
+/// do_computation(100, &divisors)
+/// };
+/// assert_eq!(result, 12);
+///
+/// ```
+///
+/// While using `unreachable_unchecked()` is perfectly sound in the following
+/// example, the compiler is able to prove that a division by zero is not
+/// possible. Benchmarking reveals that `unreachable_unchecked()` provides
+/// no benefit over using [`unreachable!`], while the latter does not introduce
+/// the possibility of Undefined Behavior.
///
/// ```
/// fn div_1(a: u32, b: u32) -> u32 {
#[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "92980")]
pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize;
+ /// See documentation of `<*const T>::sub_ptr` for details.
+ #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "92980")]
+ #[cfg(not(bootstrap))]
+ pub fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize;
+
/// See documentation of `<*const T>::guaranteed_eq` for details.
///
/// Note that, unlike most intrinsics, this is safe to call;
{
called_in_const.call_once(arg)
}
+
+/// Bootstrap polyfill
+#[cfg(bootstrap)]
+pub const unsafe fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize {
+ // SAFETY: we have stricter preconditions than `ptr_offset_from`, so can
+ // call it, and its output has to be positive, so we can just cast.
+ unsafe { ptr_offset_from(ptr, base) as _ }
+}
impl<I: Iterator> Iterator for ByRefSized<'_, I> {
type Item = I::Item;
+ #[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
+ #[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
+ #[inline]
fn advance_by(&mut self, n: usize) -> Result<(), usize> {
self.0.advance_by(n)
}
+ #[inline]
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.0.nth(n)
}
+ #[inline]
fn fold<B, F>(self, init: B, f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
self.0.fold(init, f)
}
+ #[inline]
fn try_fold<B, F, R>(&mut self, init: B, f: F) -> R
where
F: FnMut(B, Self::Item) -> R,
self.0.try_fold(init, f)
}
}
+
+impl<I: DoubleEndedIterator> DoubleEndedIterator for ByRefSized<'_, I> {
+ #[inline]
+ fn next_back(&mut self) -> Option<Self::Item> {
+ self.0.next_back()
+ }
+
+ #[inline]
+ fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
+ self.0.advance_back_by(n)
+ }
+
+ #[inline]
+ fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
+ self.0.nth_back(n)
+ }
+
+ #[inline]
+ fn rfold<B, F>(self, init: B, f: F) -> B
+ where
+ F: FnMut(B, Self::Item) -> B,
+ {
+ self.0.rfold(init, f)
+ }
+
+ #[inline]
+ fn try_rfold<B, F, R>(&mut self, init: B, f: F) -> R
+ where
+ F: FnMut(B, Self::Item) -> R,
+ R: Try<Output = B>,
+ {
+ self.0.try_rfold(init, f)
+ }
+}
#![feature(const_option)]
#![feature(const_option_ext)]
#![feature(const_pin)]
+#![feature(const_ptr_sub_ptr)]
#![feature(const_replace)]
#![feature(const_ptr_as_ref)]
#![feature(const_ptr_is_null)]
}
/// Check if self starts with u with a case-insensitive comparison.
- fn eq_ignore_case(&self, u: &[u8]) -> bool {
+ fn starts_with_ignore_case(&self, u: &[u8]) -> bool {
debug_assert!(self.as_ref().len() >= u.len());
let iter = self.as_ref().iter().zip(u.iter());
let d = iter.fold(0, |i, (&x, &y)| i | (x ^ y));
/// Parse a partial representation of a special, non-finite float.
fn parse_partial_inf_nan<F: RawFloat>(s: &[u8]) -> Option<(F, usize)> {
fn parse_inf_rest(s: &[u8]) -> usize {
- if s.len() >= 8 && s[3..].as_ref().eq_ignore_case(b"inity") { 8 } else { 3 }
+ if s.len() >= 8 && s[3..].as_ref().starts_with_ignore_case(b"inity") { 8 } else { 3 }
}
if s.len() >= 3 {
- if s.eq_ignore_case(b"nan") {
+ if s.starts_with_ignore_case(b"nan") {
return Some((F::NAN, 3));
- } else if s.eq_ignore_case(b"inf") {
+ } else if s.starts_with_ignore_case(b"inf") {
return Some((F::INFINITY, parse_inf_rest(s)));
}
}
#[$const_new_unchecked_stability]
#[must_use]
#[inline]
- #[rustc_allow_const_fn_unstable(const_fn_fn_ptr_basics)] // required by assert_unsafe_precondition
pub const unsafe fn new_unchecked(n: $Int) -> Self {
// SAFETY: this is guaranteed to be safe by the caller.
unsafe {
where
T: ~const Default,
{
- #[rustc_allow_const_fn_unstable(const_fn_trait_bound)]
const fn default<T: ~const Default>() -> T {
T::default()
}
unsafe { intrinsics::offset(self, count) }
}
+ /// Calculates the offset from a pointer in bytes.
+ ///
+ /// `count` is in units of **bytes**.
+ ///
+ /// This is purely a convenience for casting to a `u8` pointer and
+ /// using [offset][pointer::offset] on it. See that method for documentation
+ /// and safety requirements.
+ ///
+ /// For non-`Sized` pointees this operation changes only the data pointer,
+ /// leaving the metadata untouched.
+ #[must_use]
+ #[inline(always)]
+ #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+ #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+ pub const unsafe fn byte_offset(self, count: isize) -> Self {
+ // SAFETY: the caller must uphold the safety contract for `offset`.
+ let this = unsafe { self.cast::<u8>().offset(count).cast::<()>() };
+ from_raw_parts::<T>(this, metadata(self))
+ }
+
/// Calculates the offset from a pointer using wrapping arithmetic.
///
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
unsafe { intrinsics::arith_offset(self, count) }
}
+ /// Calculates the offset from a pointer in bytes using wrapping arithmetic.
+ ///
+ /// `count` is in units of **bytes**.
+ ///
+ /// This is purely a convenience for casting to a `u8` pointer and
+ /// using [wrapping_offset][pointer::wrapping_offset] on it. See that method
+ /// for documentation.
+ ///
+ /// For non-`Sized` pointees this operation changes only the data pointer,
+ /// leaving the metadata untouched.
+ #[must_use]
+ #[inline(always)]
+ #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+ #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+ pub const fn wrapping_byte_offset(self, count: isize) -> Self {
+ from_raw_parts::<T>(self.cast::<u8>().wrapping_offset(count).cast::<()>(), metadata(self))
+ }
+
/// Calculates the distance between two pointers. The returned value is in
/// units of T: the distance in bytes divided by `mem::size_of::<T>()`.
///
unsafe { intrinsics::ptr_offset_from(self, origin) }
}
+ /// Calculates the distance between two pointers. The returned value is in
+ /// units of **bytes**.
+ ///
+ /// This is purely a convenience for casting to a `u8` pointer and
+ /// using [offset_from][pointer::offset_from] on it. See that method for
+ /// documentation and safety requirements.
+ ///
+ /// For non-`Sized` pointees this operation considers only the data pointers,
+ /// ignoring the metadata.
+ #[inline(always)]
+ #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+ #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+ pub const unsafe fn byte_offset_from(self, origin: *const T) -> isize {
+ // SAFETY: the caller must uphold the safety contract for `offset_from`.
+ unsafe { self.cast::<u8>().offset_from(origin.cast::<u8>()) }
+ }
+
+ /// Calculates the distance between two pointers, *where it's known that
+ /// `self` is equal to or greater than `origin`*. The returned value is in
+ /// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
+ ///
+ /// This computes the same value that [`offset_from`](#method.offset_from)
+ /// would compute, but with the added precondition that that the offset is
+ /// guaranteed to be non-negative. This method is equivalent to
+ /// `usize::from(self.offset_from(origin)).unwrap_unchecked()`,
+ /// but it provides slightly more information to the optimizer, which can
+ /// sometimes allow it to optimize slightly better with some backends.
+ ///
+ /// This method can be though of as recovering the `count` that was passed
+ /// to [`add`](#method.add) (or, with the parameters in the other order,
+ /// to [`sub`](#method.sub)). The following are all equivalent, assuming
+ /// that their safety preconditions are met:
+ /// ```rust
+ /// # #![feature(ptr_sub_ptr)]
+ /// # unsafe fn blah(ptr: *const i32, origin: *const i32, count: usize) -> bool {
+ /// ptr.sub_ptr(origin) == count
+ /// # &&
+ /// origin.add(count) == ptr
+ /// # &&
+ /// ptr.sub(count) == origin
+ /// # }
+ /// ```
+ ///
+ /// # Safety
+ ///
+ /// - The distance between the pointers must be non-negative (`self >= origin`)
+ ///
+ /// - *All* the safety conditions of [`offset_from`](#method.offset_from)
+ /// apply to this method as well; see it for the full details.
+ ///
+ /// Importantly, despite the return type of this method being able to represent
+ /// a larger offset, it's still *not permitted* to pass pointers which differ
+ /// by more than `isize::MAX` *bytes*. As such, the result of this method will
+ /// always be less than or equal to `isize::MAX as usize`.
+ ///
+ /// # Panics
+ ///
+ /// This function panics if `T` is a Zero-Sized Type ("ZST").
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ptr_sub_ptr)]
+ ///
+ /// let a = [0; 5];
+ /// let ptr1: *const i32 = &a[1];
+ /// let ptr2: *const i32 = &a[3];
+ /// unsafe {
+ /// assert_eq!(ptr2.sub_ptr(ptr1), 2);
+ /// assert_eq!(ptr1.add(2), ptr2);
+ /// assert_eq!(ptr2.sub(2), ptr1);
+ /// assert_eq!(ptr2.sub_ptr(ptr2), 0);
+ /// }
+ ///
+ /// // This would be incorrect, as the pointers are not correctly ordered:
+ /// // ptr1.offset_from(ptr2)
+ /// ```
+ #[unstable(feature = "ptr_sub_ptr", issue = "95892")]
+ #[rustc_const_unstable(feature = "const_ptr_sub_ptr", issue = "95892")]
+ #[inline]
+ pub const unsafe fn sub_ptr(self, origin: *const T) -> usize
+ where
+ T: Sized,
+ {
+ // SAFETY: The comparison has no side-effects, and the intrinsic
+ // does this check internally in the CTFE implementation.
+ unsafe { assert_unsafe_precondition!(self >= origin) };
+
+ let pointee_size = mem::size_of::<T>();
+ assert!(0 < pointee_size && pointee_size <= isize::MAX as usize);
+ // SAFETY: the caller must uphold the safety contract for `ptr_offset_from_unsigned`.
+ unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) }
+ }
+
/// Returns whether two pointers are guaranteed to be equal.
///
/// At runtime this function behaves like `self == other`.
unsafe { self.offset(count as isize) }
}
+ /// Calculates the offset from a pointer in bytes (convenience for `.byte_offset(count as isize)`).
+ ///
+ /// `count` is in units of bytes.
+ ///
+ /// This is purely a convenience for casting to a `u8` pointer and
+ /// using [add][pointer::add] on it. See that method for documentation
+ /// and safety requirements.
+ ///
+ /// For non-`Sized` pointees this operation changes only the data pointer,
+ /// leaving the metadata untouched.
+ #[must_use]
+ #[inline(always)]
+ #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+ #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+ pub const unsafe fn byte_add(self, count: usize) -> Self {
+ // SAFETY: the caller must uphold the safety contract for `add`.
+ let this = unsafe { self.cast::<u8>().add(count).cast::<()>() };
+ from_raw_parts::<T>(this, metadata(self))
+ }
+
/// Calculates the offset from a pointer (convenience for
/// `.offset((count as isize).wrapping_neg())`).
///
unsafe { self.offset((count as isize).wrapping_neg()) }
}
+ /// Calculates the offset from a pointer in bytes (convenience for
+ /// `.byte_offset((count as isize).wrapping_neg())`).
+ ///
+ /// `count` is in units of bytes.
+ ///
+ /// This is purely a convenience for casting to a `u8` pointer and
+ /// using [sub][pointer::sub] on it. See that method for documentation
+ /// and safety requirements.
+ ///
+ /// For non-`Sized` pointees this operation changes only the data pointer,
+ /// leaving the metadata untouched.
+ #[must_use]
+ #[inline(always)]
+ #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+ #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+ pub const unsafe fn byte_sub(self, count: usize) -> Self {
+ // SAFETY: the caller must uphold the safety contract for `sub`.
+ let this = unsafe { self.cast::<u8>().sub(count).cast::<()>() };
+ from_raw_parts::<T>(this, metadata(self))
+ }
+
/// Calculates the offset from a pointer using wrapping arithmetic.
/// (convenience for `.wrapping_offset(count as isize)`)
///
self.wrapping_offset(count as isize)
}
+ /// Calculates the offset from a pointer in bytes using wrapping arithmetic.
+ /// (convenience for `.wrapping_byte_offset(count as isize)`)
+ ///
+ /// `count` is in units of bytes.
+ ///
+ /// This is purely a convenience for casting to a `u8` pointer and
+ /// using [wrapping_add][pointer::wrapping_add] on it. See that method for documentation.
+ ///
+ /// For non-`Sized` pointees this operation changes only the data pointer,
+ /// leaving the metadata untouched.
+ #[must_use]
+ #[inline(always)]
+ #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+ #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+ pub const fn wrapping_byte_add(self, count: usize) -> Self {
+ from_raw_parts::<T>(self.cast::<u8>().wrapping_add(count).cast::<()>(), metadata(self))
+ }
+
/// Calculates the offset from a pointer using wrapping arithmetic.
/// (convenience for `.wrapping_offset((count as isize).wrapping_neg())`)
///
self.wrapping_offset((count as isize).wrapping_neg())
}
+ /// Calculates the offset from a pointer in bytes using wrapping arithmetic.
+ /// (convenience for `.wrapping_offset((count as isize).wrapping_neg())`)
+ ///
+ /// `count` is in units of bytes.
+ ///
+ /// This is purely a convenience for casting to a `u8` pointer and
+ /// using [wrapping_sub][pointer::wrapping_sub] on it. See that method for documentation.
+ ///
+ /// For non-`Sized` pointees this operation changes only the data pointer,
+ /// leaving the metadata untouched.
+ #[must_use]
+ #[inline(always)]
+ #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+ #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+ pub const fn wrapping_byte_sub(self, count: usize) -> Self {
+ from_raw_parts::<T>(self.cast::<u8>().wrapping_sub(count).cast::<()>(), metadata(self))
+ }
+
/// Reads the value from `self` without moving it. This leaves the
/// memory in `self` unchanged.
///
}
// SAFETY:
- // It is permisseble for `align_offset` to always return `usize::MAX`,
+ // It is permissible for `align_offset` to always return `usize::MAX`,
// algorithm correctness can not depend on `align_offset` returning non-max values.
//
// As such the behaviour can't change after replacing `align_offset` with `usize::MAX`, only performance can.
unsafe { intrinsics::const_eval_select((self, align), ctfe_impl, rt_impl) }
}
+
+ /// Returns whether the pointer is properly aligned for `T`.
+ #[must_use]
+ #[inline]
+ #[unstable(feature = "pointer_is_aligned", issue = "96284")]
+ pub fn is_aligned(self) -> bool
+ where
+ T: Sized,
+ {
+ self.is_aligned_to(core::mem::align_of::<T>())
+ }
+
+ /// Returns whether the pointer is aligned to `align`.
+ ///
+ /// For non-`Sized` pointees this operation considers only the data pointer,
+ /// ignoring the metadata.
+ ///
+ /// # Panics
+ ///
+ /// The function panics if `align` is not a power-of-two (this includes 0).
+ #[must_use]
+ #[inline]
+ #[unstable(feature = "pointer_is_aligned", issue = "96284")]
+ pub fn is_aligned_to(self, align: usize) -> bool {
+ if !align.is_power_of_two() {
+ panic!("is_aligned_to: align is not a power-of-two");
+ }
+
+ // SAFETY: `is_power_of_two()` will return `false` for zero.
+ unsafe { core::intrinsics::assume(align != 0) };
+
+ // Cast is needed for `T: !Sized`
+ self.cast::<u8>().addr() % align == 0
+ }
}
impl<T> *const [T] {
unsafe { intrinsics::offset(self, count) as *mut T }
}
+ /// Calculates the offset from a pointer in bytes.
+ ///
+ /// `count` is in units of **bytes**.
+ ///
+ /// This is purely a convenience for casting to a `u8` pointer and
+ /// using [offset][pointer::offset] on it. See that method for documentation
+ /// and safety requirements.
+ ///
+ /// For non-`Sized` pointees this operation changes only the data pointer,
+ /// leaving the metadata untouched.
+ #[must_use]
+ #[inline(always)]
+ #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+ #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+ pub const unsafe fn byte_offset(self, count: isize) -> Self {
+ // SAFETY: the caller must uphold the safety contract for `offset`.
+ let this = unsafe { self.cast::<u8>().offset(count).cast::<()>() };
+ from_raw_parts_mut::<T>(this, metadata(self))
+ }
+
/// Calculates the offset from a pointer using wrapping arithmetic.
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
/// offset of `3 * size_of::<T>()` bytes.
unsafe { intrinsics::arith_offset(self, count) as *mut T }
}
+ /// Calculates the offset from a pointer in bytes using wrapping arithmetic.
+ ///
+ /// `count` is in units of **bytes**.
+ ///
+ /// This is purely a convenience for casting to a `u8` pointer and
+ /// using [wrapping_offset][pointer::wrapping_offset] on it. See that method
+ /// for documentation.
+ ///
+ /// For non-`Sized` pointees this operation changes only the data pointer,
+ /// leaving the metadata untouched.
+ #[must_use]
+ #[inline(always)]
+ #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+ #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+ pub const fn wrapping_byte_offset(self, count: isize) -> Self {
+ from_raw_parts_mut::<T>(
+ self.cast::<u8>().wrapping_offset(count).cast::<()>(),
+ metadata(self),
+ )
+ }
+
/// Returns `None` if the pointer is null, or else returns a unique reference to
/// the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_mut`]
/// must be used instead.
unsafe { (self as *const T).offset_from(origin) }
}
+ /// Calculates the distance between two pointers. The returned value is in
+ /// units of **bytes**.
+ ///
+ /// This is purely a convenience for casting to a `u8` pointer and
+ /// using [offset_from][pointer::offset_from] on it. See that method for
+ /// documentation and safety requirements.
+ ///
+ /// For non-`Sized` pointees this operation considers only the data pointers,
+ /// ignoring the metadata.
+ #[inline(always)]
+ #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+ #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+ pub const unsafe fn byte_offset_from(self, origin: *const T) -> isize {
+ // SAFETY: the caller must uphold the safety contract for `offset_from`.
+ unsafe { self.cast::<u8>().offset_from(origin.cast::<u8>()) }
+ }
+
+ /// Calculates the distance between two pointers, *where it's known that
+ /// `self` is equal to or greater than `origin`*. The returned value is in
+ /// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
+ ///
+ /// This computes the same value that [`offset_from`](#method.offset_from)
+ /// would compute, but with the added precondition that that the offset is
+ /// guaranteed to be non-negative. This method is equivalent to
+ /// `usize::from(self.offset_from(origin)).unwrap_unchecked()`,
+ /// but it provides slightly more information to the optimizer, which can
+ /// sometimes allow it to optimize slightly better with some backends.
+ ///
+ /// This method can be though of as recovering the `count` that was passed
+ /// to [`add`](#method.add) (or, with the parameters in the other order,
+ /// to [`sub`](#method.sub)). The following are all equivalent, assuming
+ /// that their safety preconditions are met:
+ /// ```rust
+ /// # #![feature(ptr_sub_ptr)]
+ /// # unsafe fn blah(ptr: *mut i32, origin: *mut i32, count: usize) -> bool {
+ /// ptr.sub_ptr(origin) == count
+ /// # &&
+ /// origin.add(count) == ptr
+ /// # &&
+ /// ptr.sub(count) == origin
+ /// # }
+ /// ```
+ ///
+ /// # Safety
+ ///
+ /// - The distance between the pointers must be non-negative (`self >= origin`)
+ ///
+ /// - *All* the safety conditions of [`offset_from`](#method.offset_from)
+ /// apply to this method as well; see it for the full details.
+ ///
+ /// Importantly, despite the return type of this method being able to represent
+ /// a larger offset, it's still *not permitted* to pass pointers which differ
+ /// by more than `isize::MAX` *bytes*. As such, the result of this method will
+ /// always be less than or equal to `isize::MAX as usize`.
+ ///
+ /// # Panics
+ ///
+ /// This function panics if `T` is a Zero-Sized Type ("ZST").
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ptr_sub_ptr)]
+ ///
+ /// let mut a = [0; 5];
+ /// let p: *mut i32 = a.as_mut_ptr();
+ /// unsafe {
+ /// let ptr1: *mut i32 = p.add(1);
+ /// let ptr2: *mut i32 = p.add(3);
+ ///
+ /// assert_eq!(ptr2.sub_ptr(ptr1), 2);
+ /// assert_eq!(ptr1.add(2), ptr2);
+ /// assert_eq!(ptr2.sub(2), ptr1);
+ /// assert_eq!(ptr2.sub_ptr(ptr2), 0);
+ /// }
+ ///
+ /// // This would be incorrect, as the pointers are not correctly ordered:
+ /// // ptr1.offset_from(ptr2)
+ #[unstable(feature = "ptr_sub_ptr", issue = "95892")]
+ #[rustc_const_unstable(feature = "const_ptr_sub_ptr", issue = "95892")]
+ #[inline]
+ pub const unsafe fn sub_ptr(self, origin: *const T) -> usize
+ where
+ T: Sized,
+ {
+ // SAFETY: the caller must uphold the safety contract for `sub_ptr`.
+ unsafe { (self as *const T).sub_ptr(origin) }
+ }
+
/// Calculates the offset from a pointer (convenience for `.offset(count as isize)`).
///
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
unsafe { self.offset(count as isize) }
}
+ /// Calculates the offset from a pointer in bytes (convenience for `.byte_offset(count as isize)`).
+ ///
+ /// `count` is in units of bytes.
+ ///
+ /// This is purely a convenience for casting to a `u8` pointer and
+ /// using [add][pointer::add] on it. See that method for documentation
+ /// and safety requirements.
+ ///
+ /// For non-`Sized` pointees this operation changes only the data pointer,
+ /// leaving the metadata untouched.
+ #[must_use]
+ #[inline(always)]
+ #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+ #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+ pub const unsafe fn byte_add(self, count: usize) -> Self {
+ // SAFETY: the caller must uphold the safety contract for `add`.
+ let this = unsafe { self.cast::<u8>().add(count).cast::<()>() };
+ from_raw_parts_mut::<T>(this, metadata(self))
+ }
+
/// Calculates the offset from a pointer (convenience for
/// `.offset((count as isize).wrapping_neg())`).
///
unsafe { self.offset((count as isize).wrapping_neg()) }
}
+ /// Calculates the offset from a pointer in bytes (convenience for
+ /// `.byte_offset((count as isize).wrapping_neg())`).
+ ///
+ /// `count` is in units of bytes.
+ ///
+ /// This is purely a convenience for casting to a `u8` pointer and
+ /// using [sub][pointer::sub] on it. See that method for documentation
+ /// and safety requirements.
+ ///
+ /// For non-`Sized` pointees this operation changes only the data pointer,
+ /// leaving the metadata untouched.
+ #[must_use]
+ #[inline(always)]
+ #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+ #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+ pub const unsafe fn byte_sub(self, count: usize) -> Self {
+ // SAFETY: the caller must uphold the safety contract for `sub`.
+ let this = unsafe { self.cast::<u8>().sub(count).cast::<()>() };
+ from_raw_parts_mut::<T>(this, metadata(self))
+ }
+
/// Calculates the offset from a pointer using wrapping arithmetic.
/// (convenience for `.wrapping_offset(count as isize)`)
///
self.wrapping_offset(count as isize)
}
+ /// Calculates the offset from a pointer in bytes using wrapping arithmetic.
+ /// (convenience for `.wrapping_byte_offset(count as isize)`)
+ ///
+ /// `count` is in units of bytes.
+ ///
+ /// This is purely a convenience for casting to a `u8` pointer and
+ /// using [wrapping_add][pointer::wrapping_add] on it. See that method for documentation.
+ ///
+ /// For non-`Sized` pointees this operation changes only the data pointer,
+ /// leaving the metadata untouched.
+ #[must_use]
+ #[inline(always)]
+ #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+ #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+ pub const fn wrapping_byte_add(self, count: usize) -> Self {
+ from_raw_parts_mut::<T>(self.cast::<u8>().wrapping_add(count).cast::<()>(), metadata(self))
+ }
+
/// Calculates the offset from a pointer using wrapping arithmetic.
/// (convenience for `.wrapping_offset((count as isize).wrapping_neg())`)
///
self.wrapping_offset((count as isize).wrapping_neg())
}
+ /// Calculates the offset from a pointer in bytes using wrapping arithmetic.
+ /// (convenience for `.wrapping_offset((count as isize).wrapping_neg())`)
+ ///
+ /// `count` is in units of bytes.
+ ///
+ /// This is purely a convenience for casting to a `u8` pointer and
+ /// using [wrapping_sub][pointer::wrapping_sub] on it. See that method for documentation.
+ ///
+ /// For non-`Sized` pointees this operation changes only the data pointer,
+ /// leaving the metadata untouched.
+ #[must_use]
+ #[inline(always)]
+ #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+ #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+ pub const fn wrapping_byte_sub(self, count: usize) -> Self {
+ from_raw_parts_mut::<T>(self.cast::<u8>().wrapping_sub(count).cast::<()>(), metadata(self))
+ }
+
/// Reads the value from `self` without moving it. This leaves the
/// memory in `self` unchanged.
///
}
// SAFETY:
- // It is permisseble for `align_offset` to always return `usize::MAX`,
+ // It is permissible for `align_offset` to always return `usize::MAX`,
// algorithm correctness can not depend on `align_offset` returning non-max values.
//
// As such the behaviour can't change after replacing `align_offset` with `usize::MAX`, only performance can.
unsafe { intrinsics::const_eval_select((self, align), ctfe_impl, rt_impl) }
}
+
+ /// Returns whether the pointer is properly aligned for `T`.
+ #[must_use]
+ #[inline]
+ #[unstable(feature = "pointer_is_aligned", issue = "96284")]
+ pub fn is_aligned(self) -> bool
+ where
+ T: Sized,
+ {
+ self.is_aligned_to(core::mem::align_of::<T>())
+ }
+
+ /// Returns whether the pointer is aligned to `align`.
+ ///
+ /// For non-`Sized` pointees this operation considers only the data pointer,
+ /// ignoring the metadata.
+ ///
+ /// # Panics
+ ///
+ /// The function panics if `align` is not a power-of-two (this includes 0).
+ #[must_use]
+ #[inline]
+ #[unstable(feature = "pointer_is_aligned", issue = "96284")]
+ pub fn is_aligned_to(self, align: usize) -> bool {
+ if !align.is_power_of_two() {
+ panic!("is_aligned_to: align is not a power-of-two");
+ }
+
+ // SAFETY: `is_power_of_two()` will return `false` for zero.
+ unsafe { core::intrinsics::assume(align != 0) };
+
+ // Cast is needed for `T: !Sized`
+ self.cast::<u8>().addr() % align == 0
+ }
}
impl<T> *mut [T] {
#[unstable(feature = "slice_from_ptr_range", issue = "89792")]
pub unsafe fn from_ptr_range<'a, T>(range: Range<*const T>) -> &'a [T] {
// SAFETY: the caller must uphold the safety contract for `from_ptr_range`.
- unsafe { from_raw_parts(range.start, range.end.offset_from(range.start) as usize) }
+ unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) }
}
/// Performs the same functionality as [`from_ptr_range`], except that a
#[unstable(feature = "slice_from_ptr_range", issue = "89792")]
pub unsafe fn from_mut_ptr_range<'a, T>(range: Range<*mut T>) -> &'a mut [T] {
// SAFETY: the caller must uphold the safety contract for `from_mut_ptr_range`.
- unsafe { from_raw_parts_mut(range.start, range.end.offset_from(range.start) as usize) }
+ unsafe { from_raw_parts_mut(range.start, range.end.sub_ptr(range.start)) }
}
#[rustc_promotable]
#[stable(feature = "futures_api", since = "1.36.0")]
#[rustc_const_stable(feature = "futures_api", since = "1.36.0")]
- #[rustc_allow_const_fn_unstable(const_fn_fn_ptr_basics)]
pub const fn new(
clone: unsafe fn(*const ()) -> RawWaker,
wake: unsafe fn(*const ()),
assert!(!(mut3 == array3));
assert!(mut3 != array3);
}
+
+#[test]
+fn array_into_iter_fold() {
+ // Strings to help MIRI catch if we double-free or something
+ let a = ["Aa".to_string(), "Bb".to_string(), "Cc".to_string()];
+ let mut s = "s".to_string();
+ a.into_iter().for_each(|b| s += &b);
+ assert_eq!(s, "sAaBbCc");
+
+ let a = [1, 2, 3, 4, 5, 6];
+ let mut it = a.into_iter();
+ it.advance_by(1).unwrap();
+ it.advance_back_by(2).unwrap();
+ let s = it.fold(10, |a, b| 10 * a + b);
+ assert_eq!(s, 10234);
+}
+
+#[test]
+fn array_into_iter_rfold() {
+ // Strings to help MIRI catch if we double-free or something
+ let a = ["Aa".to_string(), "Bb".to_string(), "Cc".to_string()];
+ let mut s = "s".to_string();
+ a.into_iter().rev().for_each(|b| s += &b);
+ assert_eq!(s, "sCcBbAa");
+
+ let a = [1, 2, 3, 4, 5, 6];
+ let mut it = a.into_iter();
+ it.advance_by(1).unwrap();
+ it.advance_back_by(2).unwrap();
+ let s = it.rfold(10, |a, b| 10 * a + b);
+ assert_eq!(s, 10432);
+}
}
impl Client<fn(crate::TokenStream) -> crate::TokenStream> {
- #[rustc_allow_const_fn_unstable(const_fn)]
pub const fn expand1(f: fn(crate::TokenStream) -> crate::TokenStream) -> Self {
extern "C" fn run(
bridge: Bridge<'_>,
}
impl Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream> {
- #[rustc_allow_const_fn_unstable(const_fn)]
pub const fn expand2(
f: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream,
) -> Self {
}
}
- #[rustc_allow_const_fn_unstable(const_fn)]
pub const fn custom_derive(
trait_name: &'static str,
attributes: &'static [&'static str],
ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) }
}
- #[rustc_allow_const_fn_unstable(const_fn)]
pub const fn attr(
name: &'static str,
expand: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream,
ProcMacro::Attr { name, client: Client::expand2(expand) }
}
- #[rustc_allow_const_fn_unstable(const_fn)]
pub const fn bang(
name: &'static str,
expand: fn(crate::TokenStream) -> crate::TokenStream,
pub struct ScopedCell<T: LambdaL>(Cell<<T as ApplyL<'static>>::Out>);
impl<T: LambdaL> ScopedCell<T> {
- #[rustc_allow_const_fn_unstable(const_fn)]
pub const fn new(value: <T as ApplyL<'static>>::Out) -> Self {
ScopedCell(Cell::new(value))
}
/// On all platforms, the newline is the LINE FEED character (`\n`/`U+000A`) alone
/// (no additional CARRIAGE RETURN (`\r`/`U+000D`)).
///
-/// Use the [`format!`] syntax to write data to the standard output.
+/// This macro uses the same syntax as [`format!`], but writes to the standard output instead.
/// See [`std::fmt`] for more information.
///
/// Use `println!` only for the primary output of your program. Use
// 32-bit c_int. Below is -2, in two's complement, but that only works out
// because c_int is 32 bits.
#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
+#[rustc_nonnull_optimization_guaranteed]
#[unstable(feature = "io_safety", issue = "87074")]
pub struct BorrowedFd<'fd> {
fd: RawFd,
// 32-bit c_int. Below is -2, in two's complement, but that only works out
// because c_int is 32 bits.
#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
+#[rustc_nonnull_optimization_guaranteed]
#[unstable(feature = "io_safety", issue = "87074")]
pub struct OwnedFd {
fd: RawFd,
assert_eq!(stdin_as_file.as_fd().as_raw_fd(), raw_fd);
assert_eq!(Into::<OwnedFd>::into(stdin_as_file).into_raw_fd(), raw_fd);
}
+
+#[cfg(any(unix, target_os = "wasi"))]
+#[test]
+fn test_niche_optimizations() {
+ use crate::mem::size_of;
+ #[cfg(unix)]
+ use crate::os::unix::io::{BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
+ #[cfg(target_os = "wasi")]
+ use crate::os::wasi::io::{BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
+
+ assert_eq!(size_of::<Option<OwnedFd>>(), size_of::<RawFd>());
+ assert_eq!(size_of::<Option<BorrowedFd<'static>>>(), size_of::<RawFd>());
+ unsafe {
+ assert_eq!(OwnedFd::from_raw_fd(RawFd::MIN).into_raw_fd(), RawFd::MIN);
+ assert_eq!(OwnedFd::from_raw_fd(RawFd::MAX).into_raw_fd(), RawFd::MAX);
+ assert_eq!(Some(OwnedFd::from_raw_fd(RawFd::MIN)).unwrap().into_raw_fd(), RawFd::MIN);
+ assert_eq!(Some(OwnedFd::from_raw_fd(RawFd::MAX)).unwrap().into_raw_fd(), RawFd::MAX);
+ }
+}
/// so it can be used in FFI in places where a handle is passed as an argument,
/// it is not captured or consumed.
///
-/// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is
-/// sometimes a valid handle value. See [here] for the full story.
+/// Note that it *may* have the value `-1`, which in `BorrowedHandle` always
+/// represents a valid handle value, such as [the current process handle], and
+/// not `INVALID_HANDLE_VALUE`, despite the two having the same value. See
+/// [here] for the full story.
///
/// And, it *may* have the value `NULL` (0), which can occur when consoles are
/// detached from processes, or when `windows_subsystem` is used.
/// handle, which is then borrowed under the same lifetime.
///
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
+/// [the current process handle]: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocess#remarks
#[derive(Copy, Clone)]
#[repr(transparent)]
#[unstable(feature = "io_safety", issue = "87074")]
///
/// This closes the handle on drop.
///
-/// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is
-/// sometimes a valid handle value. See [here] for the full story.
+/// Note that it *may* have the value `-1`, which in `OwnedHandle` always
+/// represents a valid handle value, such as [the current process handle], and
+/// not `INVALID_HANDLE_VALUE`, despite the two having the same value. See
+/// [here] for the full story.
///
/// And, it *may* have the value `NULL` (0), which can occur when consoles are
/// detached from processes, or when `windows_subsystem` is used.
/// [`RegCloseKey`]: https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regclosekey
///
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
+/// [the current process handle]: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocess#remarks
#[repr(transparent)]
#[unstable(feature = "io_safety", issue = "87074")]
pub struct OwnedHandle {
/// `NULL`. This ensures that such FFI calls cannot start using the handle without
/// checking for `NULL` first.
///
-/// This type considers any value other than `NULL` to be valid, including `INVALID_HANDLE_VALUE`.
-/// This is because APIs that use `NULL` as their sentry value don't treat `INVALID_HANDLE_VALUE`
-/// as special.
+/// This type may hold any handle value that [`OwnedHandle`] may hold. As with `OwnedHandle`, when
+/// it holds `-1`, that value is interpreted as a valid handle value, such as
+/// [the current process handle], and not `INVALID_HANDLE_VALUE`.
///
-/// If this holds a valid handle, it will close the handle on drop.
+/// If this holds a non-null handle, it will close the handle on drop.
+///
+/// [the current process handle]: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocess#remarks
#[repr(transparent)]
#[unstable(feature = "io_safety", issue = "87074")]
#[derive(Debug)]
/// `INVALID_HANDLE_VALUE`. This ensures that such FFI calls cannot start using the handle without
/// checking for `INVALID_HANDLE_VALUE` first.
///
-/// This type considers any value other than `INVALID_HANDLE_VALUE` to be valid, including `NULL`.
-/// This is because APIs that use `INVALID_HANDLE_VALUE` as their sentry value may return `NULL`
-/// under `windows_subsystem = "windows"` or other situations where I/O devices are detached.
+/// This type may hold any handle value that [`OwnedHandle`] may hold, except that when it holds
+/// `-1`, that value is interpreted to mean `INVALID_HANDLE_VALUE`.
///
-/// If this holds a valid handle, it will close the handle on drop.
+/// If holds a handle other than `INVALID_HANDLE_VALUE`, it will close the handle on drop.
#[repr(transparent)]
#[unstable(feature = "io_safety", issue = "87074")]
#[derive(Debug)]
})?;
unsafe { Ok(Self::from_raw_handle(ret)) }
}
-
- /// Allow child processes to inherit the handle.
- pub(crate) fn set_inheritable(&self) -> io::Result<()> {
- cvt(unsafe {
- c::SetHandleInformation(
- self.as_raw_handle(),
- c::HANDLE_FLAG_INHERIT,
- c::HANDLE_FLAG_INHERIT,
- )
- })?;
- Ok(())
- }
}
impl TryFrom<HandleOrInvalid> for OwnedHandle {
pub use raw::*;
#[unstable(feature = "io_safety", issue = "87074")]
pub use socket::*;
+
+#[cfg(test)]
+mod tests;
/// raw handle to the caller, and the handle is only guaranteed
/// to be valid while the original object has not yet been destroyed.
///
+ /// This function may return null, such as when called on [`Stdin`],
+ /// [`Stdout`], or [`Stderr`] when the console is detached.
+ ///
/// However, borrowing is not strictly required. See [`AsHandle::as_handle`]
/// for an API which strictly borrows a handle.
+ ///
+ /// [`Stdin`]: io::Stdin
+ /// [`Stdout`]: io::Stdout
+ /// [`Stderr`]: io::Stderr
#[stable(feature = "rust1", since = "1.0.0")]
fn as_raw_handle(&self) -> RawHandle;
}
use crate::mem::forget;
use crate::sys;
use crate::sys::c;
+#[cfg(not(target_vendor = "uwp"))]
use crate::sys::cvt;
/// A borrowed socket.
target_pointer_width = "64",
rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE)
)]
+#[rustc_nonnull_optimization_guaranteed]
#[unstable(feature = "io_safety", issue = "87074")]
pub struct BorrowedSocket<'socket> {
socket: RawSocket,
target_pointer_width = "64",
rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE)
)]
+#[rustc_nonnull_optimization_guaranteed]
#[unstable(feature = "io_safety", issue = "87074")]
pub struct OwnedSocket {
socket: RawSocket,
--- /dev/null
+#[test]
+fn test_niche_optimizations_socket() {
+ use crate::mem::size_of;
+ use crate::os::windows::io::{
+ BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket,
+ };
+
+ assert_eq!(size_of::<Option<OwnedSocket>>(), size_of::<RawSocket>());
+ assert_eq!(size_of::<Option<BorrowedSocket<'static>>>(), size_of::<RawSocket>(),);
+ unsafe {
+ #[cfg(target_pointer_width = "32")]
+ let (min, max) = (i32::MIN as u32, i32::MAX as u32);
+ #[cfg(target_pointer_width = "64")]
+ let (min, max) = (i64::MIN as u64, i64::MAX as u64);
+
+ assert_eq!(OwnedSocket::from_raw_socket(min).into_raw_socket(), min);
+ assert_eq!(OwnedSocket::from_raw_socket(max).into_raw_socket(), max);
+ assert_eq!(Some(OwnedSocket::from_raw_socket(min)).unwrap().into_raw_socket(), min);
+ assert_eq!(Some(OwnedSocket::from_raw_socket(max)).unwrap().into_raw_socket(), max);
+ }
+}
use crate::sync::Arc;
use core::hint::black_box;
+#[allow(unknown_lints, unused_macro_rules)]
macro_rules! t (
($path:expr, iter: $iter:expr) => (
{
/// return the same codes (but will also `eprintln!` the error).
#[stable(feature = "process_exitcode", since = "1.61.0")]
pub const FAILURE: ExitCode = ExitCode(imp::ExitCode::FAILURE);
+
+ /// Exit the current process with the given `ExitCode`.
+ ///
+ /// Note that this has the same caveats as [`process::exit()`][exit], namely that this function
+ /// terminates the process immediately, so no destructors on the current stack or any other
+ /// thread's stack will be run. If a clean shutdown is needed, it is recommended to simply
+ /// return this ExitCode from the `main` function, as demonstrated in the [type
+ /// documentation](#examples).
+ ///
+ /// # Differences from `process::exit()`
+ ///
+ /// `process::exit()` accepts any `i32` value as the exit code for the process; however, there
+ /// are platforms that only use a subset of that value (see [`process::exit` platform-specific
+ /// behavior][exit#platform-specific-behavior]). `ExitCode` exists because of this; only
+ /// `ExitCode`s that are supported by a majority of our platforms can be created, so those
+ /// problems don't exist (as much) with this method.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(exitcode_exit_method)]
+ /// # use std::process::ExitCode;
+ /// # use std::fmt;
+ /// # enum UhOhError { GenericProblem, Specific, WithCode { exit_code: ExitCode, _x: () } }
+ /// # impl fmt::Display for UhOhError {
+ /// # fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result { unimplemented!() }
+ /// # }
+ /// // there's no way to gracefully recover from an UhOhError, so we just
+ /// // print a message and exit
+ /// fn handle_unrecoverable_error(err: UhOhError) -> ! {
+ /// eprintln!("UH OH! {err}");
+ /// let code = match err {
+ /// UhOhError::GenericProblem => ExitCode::FAILURE,
+ /// UhOhError::Specific => ExitCode::from(3),
+ /// UhOhError::WithCode { exit_code, .. } => exit_code,
+ /// };
+ /// code.exit_process()
+ /// }
+ /// ```
+ #[unstable(feature = "exitcode_exit_method", issue = "97100")]
+ pub fn exit_process(self) -> ! {
+ exit(self.to_i32())
+ }
}
impl ExitCode {
/// process, no destructors on the current stack or any other thread's stack
/// will be run. If a clean shutdown is needed it is recommended to only call
/// this function at a known point where there are no more destructors left
-/// to run.
+/// to run; or, preferably, simply return a type implementing [`Termination`]
+/// (such as [`ExitCode`] or `Result`) from the `main` function and avoid this
+/// function altogether:
+///
+/// ```
+/// # use std::io::Error as MyError;
+/// fn main() -> Result<(), MyError> {
+/// // ...
+/// Ok(())
+/// }
+/// ```
///
/// ## Platform-specific behavior
///
/// will be visible to a parent process inspecting the exit code. On most
/// Unix-like platforms, only the eight least-significant bits are considered.
///
-/// # Examples
-///
-/// Due to this function’s behavior regarding destructors, a conventional way
-/// to use the function is to extract the actual computation to another
-/// function and compute the exit code from its return value:
-///
-/// ```
-/// fn run_app() -> Result<(), ()> {
-/// // Application logic here
-/// Ok(())
-/// }
-///
-/// fn main() {
-/// std::process::exit(match run_app() {
-/// Ok(_) => 0,
-/// Err(err) => {
-/// eprintln!("error: {err:?}");
-/// 1
-/// }
-/// });
-/// }
-/// ```
-///
-/// Due to [platform-specific behavior], the exit code for this example will be
-/// `0` on Linux, but `256` on Windows:
+/// For example, the exit code for this example will be `0` on Linux, but `256`
+/// on Windows:
///
/// ```no_run
/// use std::process;
///
/// process::exit(0x0100);
/// ```
-///
-/// [platform-specific behavior]: #platform-specific-behavior
#[stable(feature = "rust1", since = "1.0.0")]
pub fn exit(code: i32) -> ! {
crate::rt::cleanup();
//
// Overflows are rounded up to an infinite timeout (None).
let timespec = timeout
- .and_then(|d| Some(Timespec::now(libc::CLOCK_MONOTONIC).checked_add_duration(&d)?))
+ .and_then(|d| Timespec::now(libc::CLOCK_MONOTONIC).checked_add_duration(&d))
.and_then(|t| t.to_timespec());
loop {
#[cfg(target_os = "openbsd")]
pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
+ use super::time::Timespec;
use crate::ptr::{null, null_mut};
- let timespec = timeout.and_then(|d| {
- Some(libc::timespec {
- // Sleep forever if the timeout is longer than fits in a timespec.
- tv_sec: d.as_secs().try_into().ok()?,
- // This conversion never truncates, as subsec_nanos is always <1e9.
- tv_nsec: d.subsec_nanos() as _,
- })
- });
+
+ // Overflows are rounded up to an infinite timeout (None).
+ let timespec = timeout
+ .and_then(|d| Timespec::zero().checked_add_duration(&d))
+ .and_then(|t| t.to_timespec());
let r = unsafe {
libc::futex(
}
impl Timespec {
- const fn zero() -> Timespec {
+ pub const fn zero() -> Timespec {
Timespec { tv_sec: 0, tv_nsec: 0 }
}
Some(Timespec::new(secs, nsec as i64))
}
+ #[allow(dead_code)]
pub fn to_timespec(&self) -> Option<libc::timespec> {
Some(libc::timespec {
tv_sec: self.tv_sec.try_into().ok()?,
#[link(name = "advapi32")]
extern "system" {
+ // Forbidden when targeting UWP
+ #[link_name = "SystemFunction036"]
+ pub fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: ULONG) -> BOOLEAN;
+
// Allowed but unused by UWP
pub fn OpenProcessToken(
ProcessHandle: HANDLE,
bWaitAll: BOOL,
dwMilliseconds: DWORD,
) -> DWORD;
- pub fn CreatePipe(
- hReadPipe: *mut HANDLE,
- hWritePipe: *mut HANDLE,
- lpPipeAttributes: *const SECURITY_ATTRIBUTES,
- nSize: DWORD,
- ) -> BOOL;
pub fn CreateNamedPipeW(
lpName: LPCWSTR,
dwOpenMode: DWORD,
Ok(Self(self.0.duplicate(access, inherit, options)?))
}
- pub(crate) fn set_inheritable(&self) -> io::Result<()> {
- self.0.set_inheritable()
- }
-
/// Performs a synchronous read.
///
/// If the handle is opened for asynchronous I/O then this abort the process.
// Anonymous pipes
////////////////////////////////////////////////////////////////////////////////
-// A 64kb pipe capacity is the same as a typical Linux default.
-const PIPE_BUFFER_CAPACITY: u32 = 64 * 1024;
-
-pub enum AnonPipe {
- Sync(Handle),
- Async(Handle),
+pub struct AnonPipe {
+ inner: Handle,
}
impl IntoInner<Handle> for AnonPipe {
fn into_inner(self) -> Handle {
- match self {
- Self::Sync(handle) => handle,
- Self::Async(handle) => handle,
- }
+ self.inner
}
}
pub ours: AnonPipe,
pub theirs: AnonPipe,
}
-impl Pipes {
- /// Create a new pair of pipes where both pipes are synchronous.
- ///
- /// These must not be used asynchronously.
- pub fn new_synchronous(
- ours_readable: bool,
- their_handle_inheritable: bool,
- ) -> io::Result<Self> {
- unsafe {
- // If `CreatePipe` succeeds, these will be our pipes.
- let mut read = ptr::null_mut();
- let mut write = ptr::null_mut();
-
- if c::CreatePipe(&mut read, &mut write, ptr::null(), PIPE_BUFFER_CAPACITY) == 0 {
- Err(io::Error::last_os_error())
- } else {
- let (ours, theirs) = if ours_readable { (read, write) } else { (write, read) };
- let ours = Handle::from_raw_handle(ours);
- let theirs = Handle::from_raw_handle(theirs);
-
- if their_handle_inheritable {
- theirs.set_inheritable()?;
- }
-
- Ok(Pipes { ours: AnonPipe::Sync(ours), theirs: AnonPipe::Sync(theirs) })
- }
- }
- }
-}
/// Although this looks similar to `anon_pipe` in the Unix module it's actually
/// subtly different. Here we'll return two pipes in the `Pipes` return value,
/// with `OVERLAPPED` instances, but also works out ok if it's only ever used
/// once at a time (which we do indeed guarantee).
pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Result<Pipes> {
+ // A 64kb pipe capacity is the same as a typical Linux default.
+ const PIPE_BUFFER_CAPACITY: u32 = 64 * 1024;
+
// Note that we specifically do *not* use `CreatePipe` here because
// unfortunately the anonymous pipes returned do not support overlapped
// operations. Instead, we create a "hopefully unique" name and create a
};
opts.security_attributes(&mut sa);
let theirs = File::open(Path::new(&name), &opts)?;
- let theirs = AnonPipe::Sync(theirs.into_inner());
+ let theirs = AnonPipe { inner: theirs.into_inner() };
- Ok(Pipes { ours: AnonPipe::Async(ours), theirs })
+ Ok(Pipes {
+ ours: AnonPipe { inner: ours },
+ theirs: AnonPipe { inner: theirs.into_inner() },
+ })
}
}
/// This is achieved by creating a new set of pipes and spawning a thread that
/// relays messages between the source and the synchronous pipe.
pub fn spawn_pipe_relay(
- source: &Handle,
+ source: &AnonPipe,
ours_readable: bool,
their_handle_inheritable: bool,
) -> io::Result<AnonPipe> {
// We need this handle to live for the lifetime of the thread spawned below.
- let source = AnonPipe::Async(source.duplicate(0, true, c::DUPLICATE_SAME_ACCESS)?);
+ let source = source.duplicate()?;
// create a new pair of anon pipes.
let Pipes { theirs, ours } = anon_pipe(ours_readable, their_handle_inheritable)?;
impl AnonPipe {
pub fn handle(&self) -> &Handle {
- match self {
- Self::Async(ref handle) => handle,
- Self::Sync(ref handle) => handle,
- }
+ &self.inner
}
pub fn into_handle(self) -> Handle {
- self.into_inner()
+ self.inner
+ }
+ fn duplicate(&self) -> io::Result<Self> {
+ self.inner.duplicate(0, false, c::DUPLICATE_SAME_ACCESS).map(|inner| AnonPipe { inner })
}
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
let result = unsafe {
let len = crate::cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD;
- match self {
- Self::Sync(ref handle) => handle.read(buf),
- Self::Async(_) => {
- self.alertable_io_internal(c::ReadFileEx, buf.as_mut_ptr() as _, len)
- }
- }
+ self.alertable_io_internal(c::ReadFileEx, buf.as_mut_ptr() as _, len)
};
match result {
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
- io::default_read_vectored(|buf| self.read(buf), bufs)
+ self.inner.read_vectored(bufs)
}
#[inline]
pub fn is_read_vectored(&self) -> bool {
- false
+ self.inner.is_read_vectored()
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
unsafe {
let len = crate::cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD;
- match self {
- Self::Sync(ref handle) => handle.write(buf),
- Self::Async(_) => {
- self.alertable_io_internal(c::WriteFileEx, buf.as_ptr() as _, len)
- }
- }
+ self.alertable_io_internal(c::WriteFileEx, buf.as_ptr() as _, len)
}
}
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
- io::default_write_vectored(|buf| self.write(buf), bufs)
+ self.inner.write_vectored(bufs)
}
#[inline]
pub fn is_write_vectored(&self) -> bool {
- false
+ self.inner.is_write_vectored()
}
/// Synchronizes asynchronous reads or writes using our anonymous pipe.
// Asynchronous read of the pipe.
// If successful, `callback` will be called once it completes.
- let result = io(self.handle().as_handle(), buf, len, &mut overlapped, callback);
+ let result = io(self.inner.as_handle(), buf, len, &mut overlapped, callback);
if result == c::FALSE {
// We can return here because the call failed.
// After this we must not return until the I/O completes.
use crate::sys::fs::{File, OpenOptions};
use crate::sys::handle::Handle;
use crate::sys::path;
-use crate::sys::pipe::{self, AnonPipe, Pipes};
+use crate::sys::pipe::{self, AnonPipe};
use crate::sys::stdio;
use crate::sys_common::mutex::StaticMutex;
use crate::sys_common::process::{CommandEnv, CommandEnvs};
Inherit,
Null,
MakePipe,
- AsyncPipe(Handle),
+ Pipe(AnonPipe),
Handle(Handle),
}
},
Stdio::MakePipe => {
- // Handles that are passed to a child process must be synchronous
- // because they will be read synchronously (see #95759).
- // Therefore we prefer to make both ends of a pipe synchronous
- // just in case our end of the pipe is passed to another process.
- //
- // However, we may need to read from both the child's stdout and
- // stderr simultaneously when waiting for output. This requires
- // async reads so as to avoid blocking either pipe.
- //
- // The solution used here is to make handles synchronous
- // except for our side of the stdout and sterr pipes.
- // If our side of those pipes do end up being given to another
- // process then we use a "pipe relay" to synchronize access
- // (see `Stdio::AsyncPipe` below).
- let pipes = if stdio_id == c::STD_INPUT_HANDLE {
- // For stdin both sides of the pipe are synchronous.
- Pipes::new_synchronous(false, true)?
- } else {
- // For stdout/stderr our side of the pipe is async and their side is synchronous.
- pipe::anon_pipe(true, true)?
- };
+ let ours_readable = stdio_id != c::STD_INPUT_HANDLE;
+ let pipes = pipe::anon_pipe(ours_readable, true)?;
*pipe = Some(pipes.ours);
Ok(pipes.theirs.into_handle())
}
- Stdio::AsyncPipe(ref source) => {
- // We need to synchronize asynchronous pipes by using a pipe relay.
+ Stdio::Pipe(ref source) => {
let ours_readable = stdio_id != c::STD_INPUT_HANDLE;
pipe::spawn_pipe_relay(source, ours_readable, true).map(AnonPipe::into_handle)
}
impl From<AnonPipe> for Stdio {
fn from(pipe: AnonPipe) -> Stdio {
- // Note that it's very important we don't give async handles to child processes.
- // Therefore if the pipe is asynchronous we must have a way to turn it synchronous.
- // See #95759.
- match pipe {
- AnonPipe::Sync(handle) => Stdio::Handle(handle),
- AnonPipe::Async(handle) => Stdio::AsyncPipe(handle),
- }
+ Stdio::Pipe(pipe)
}
}
use crate::io;
+use crate::lazy;
use crate::mem;
use crate::sys::c;
+/// The kinds of HashMap RNG that may be available
+#[derive(Clone, Copy, Debug, PartialEq)]
+enum HashMapRng {
+ Preferred,
+ Fallback,
+}
+
pub fn hashmap_random_keys() -> (u64, u64) {
+ match get_hashmap_rng() {
+ HashMapRng::Preferred => {
+ preferred_rng().expect("couldn't generate random bytes with preferred RNG")
+ }
+ HashMapRng::Fallback => {
+ fallback_rng().expect("couldn't generate random bytes with fallback RNG")
+ }
+ }
+}
+
+/// Returns the HashMap RNG that should be used
+///
+/// Panics if they are both broken
+fn get_hashmap_rng() -> HashMapRng {
+ // Assume that if the preferred RNG is broken the first time we use it, it likely means
+ // that: the DLL has failed to load, there is no point to calling it over-and-over again,
+ // and we should cache the result
+ static VALUE: lazy::SyncOnceCell<HashMapRng> = lazy::SyncOnceCell::new();
+ *VALUE.get_or_init(choose_hashmap_rng)
+}
+
+/// Test whether we should use the preferred or fallback RNG
+///
+/// If the preferred RNG is successful, we choose it. Otherwise, if the fallback RNG is successful,
+/// we choose that
+///
+/// Panics if both the preferred and the fallback RNG are both non-functional
+fn choose_hashmap_rng() -> HashMapRng {
+ let preferred_error = match preferred_rng() {
+ Ok(_) => return HashMapRng::Preferred,
+ Err(e) => e,
+ };
+
+ match fallback_rng() {
+ Ok(_) => return HashMapRng::Fallback,
+ Err(fallback_error) => panic!(
+ "preferred RNG broken: `{}`, fallback RNG broken: `{}`",
+ preferred_error, fallback_error
+ ),
+ }
+}
+
+/// Generate random numbers using the preferred RNG function (BCryptGenRandom)
+fn preferred_rng() -> Result<(u64, u64), io::Error> {
use crate::ptr;
let mut v = (0, 0);
c::BCRYPT_USE_SYSTEM_PREFERRED_RNG,
)
};
- if ret != 0 {
- panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
- }
- return v;
+
+ if ret == 0 { Ok(v) } else { Err(io::Error::last_os_error()) }
+}
+
+/// Generate random numbers using the fallback RNG function (RtlGenRandom)
+#[cfg(not(target_vendor = "uwp"))]
+fn fallback_rng() -> Result<(u64, u64), io::Error> {
+ let mut v = (0, 0);
+ let ret =
+ unsafe { c::RtlGenRandom(&mut v as *mut _ as *mut u8, mem::size_of_val(&v) as c::ULONG) };
+
+ if ret != 0 { Ok(v) } else { Err(io::Error::last_os_error()) }
+}
+
+/// We can't use RtlGenRandom with UWP, so there is no fallback
+#[cfg(target_vendor = "uwp")]
+fn fallback_rng() -> Result<(u64, u64), io::Error> {
+ Err(io::const_io_error!(io::ErrorKind::Unsupported, "RtlGenRandom() not supported on UWP"))
}
-Subproject commit d215afe9d1cf79c5edb0dfd3cdf4c50aca1f1760
+Subproject commit 28335054b1f417175ab5005cf1d9cf7937737930
println!("cargo:rustc-link-lib=gcc_s");
} else if target.contains("dragonfly") {
println!("cargo:rustc-link-lib=gcc_pic");
- } else if target.contains("pc-windows-gnu") {
+ } else if target.ends_with("pc-windows-gnu") {
// This is handled in the target spec with late_link_args_[static|dynamic]
} else if target.contains("uwp-windows-gnu") {
println!("cargo:rustc-link-lib=unwind");
#![feature(nll)]
#![feature(staged_api)]
#![feature(c_unwind)]
+#![feature(cfg_target_abi)]
#![cfg_attr(not(target_env = "msvc"), feature(libc))]
cfg_if::cfg_if! {
#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
#[link(name = "unwind", kind = "static", modifiers = "-bundle")]
extern "C" {}
+
+#[cfg(all(target_os = "windows", target_env = "gnu", target_abi = "llvm"))]
+#[link(name = "unwind", kind = "static", modifiers = "-bundle")]
+extern "C" {}
def _download(path, url, probably_big, verbose, exception, help_on_error=None):
+ # Try to use curl (potentially available on win32
+ # https://devblogs.microsoft.com/commandline/tar-and-curl-come-to-windows/)
+ # If an error occurs:
+ # - If we are on win32 fallback to powershell
+ # - Otherwise raise the error if appropriate
if probably_big or verbose:
print("downloading {}".format(url))
- # see https://serverfault.com/questions/301128/how-to-download
- if sys.platform == 'win32':
- run(["PowerShell.exe", "/nologo", "-Command",
- "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
- "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')".format(url, path)],
- verbose=verbose,
- exception=exception)
- else:
+
+ platform_is_win32 = sys.platform == 'win32'
+ try:
if probably_big or verbose:
option = "-#"
else:
option = "-s"
- require(["curl", "--version"])
+ # If curl is not present on Win32, we shoud not sys.exit
+ # but raise `CalledProcessError` or `OSError` instead
+ require(["curl", "--version"], exception=platform_is_win32)
run(["curl", option,
"-L", # Follow redirect.
"-y", "30", "-Y", "10", # timeout if speed is < 10 bytes/sec for > 30 seconds
"--connect-timeout", "30", # timeout if cannot connect within 30 seconds
"--retry", "3", "-Sf", "-o", path, url],
verbose=verbose,
- exception=exception,
+ exception=True, # Will raise RuntimeError on failure
help_on_error=help_on_error)
+ except (subprocess.CalledProcessError, OSError, RuntimeError):
+ # see http://serverfault.com/questions/301128/how-to-download
+ if platform_is_win32:
+ run(["PowerShell.exe", "/nologo", "-Command",
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
+ "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')".format(url, path)],
+ verbose=verbose,
+ exception=exception)
+ # Check if the RuntimeError raised by run(curl) should be silenced
+ elif verbose or exception:
+ raise
def verify(path, expected, verbose):
sys.exit(err)
-def require(cmd, exit=True):
+def require(cmd, exit=True, exception=False):
'''Run a command, returning its output.
On error,
- If `exit` is `True`, exit the process.
- Otherwise, return None.'''
+ If `exception` is `True`, raise the error
+ Otherwise If `exit` is `True`, exit the process
+ Else return None.'''
try:
return subprocess.check_output(cmd).strip()
except (subprocess.CalledProcessError, OSError) as exc:
- if not exit:
- return None
- print("error: unable to run `{}`: {}".format(' '.join(cmd), exc))
- print("Please make sure it's installed and in the path.")
- sys.exit(1)
+ if exception:
+ raise
+ elif exit:
+ print("error: unable to run `{}`: {}".format(' '.join(cmd), exc))
+ print("Please make sure it's installed and in the path.")
+ sys.exit(1)
+ return None
+
def format_build_time(duration):
// backtrace, core_simd, std_float, ...), those dependencies have their own features
// but cargo isn't involved in the #[path] and so cannot pass the complete list of
// features, so for that reason we don't enable checking of features for std.
- if mode != Mode::Std {
- cargo.arg("-Zcheck-cfg-features");
- }
+ //
+ // FIXME: Re-enable this after the beta bump as apperently rustc-perf doesn't use the
+ // beta cargo. See https://github.com/rust-lang/rust/pull/96984#issuecomment-1126678773
+ // #[cfg(not(bootstrap))]
+ // if mode != Mode::Std {
+ // cargo.arg("-Zcheck-cfg-features"); // -Zcheck-cfg=features after bump
+ // }
// Enable cfg checking of well known names/values
rustflags
config.stage = 0;
config.cmd = Subcommand::Test {
paths: vec!["library/std".into()],
+ skip: vec![],
test_args: vec![],
rustc_args: vec![],
fail_fast: true,
let mut config = configure(&["A"], &["A"]);
config.cmd = Subcommand::Test {
paths: vec![],
+ skip: vec![],
test_args: vec![],
rustc_args: vec![],
fail_fast: true,
}
if target == "x86_64-fortanix-unknown-sgx"
+ || target.contains("pc-windows-gnullvm")
|| builder.config.llvm_libunwind == LlvmLibunwind::InTree
&& (target.contains("linux") || target.contains("fuchsia"))
{
DependencyType::TargetSelfContained,
);
}
- } else if target.contains("windows-gnu") {
+ } else if target.ends_with("windows-gnu") {
for obj in ["crt2.o", "dllcrt2.o"].iter() {
let src = compiler_file(builder, builder.cc(target), target, CLang::C, obj);
let target = libdir_self_contained.join(obj);
fn run(self, builder: &Builder<'_>) -> Vec<(PathBuf, DependencyType)> {
let for_compiler = self.compiler;
let target = self.target;
- if !target.contains("windows-gnu") {
+ if !target.ends_with("windows-gnu") {
return vec![];
}
/// without any extra installed software (e.g., we bundle gcc, libraries, etc).
fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
let host = self.host;
- if !host.contains("pc-windows-gnu") {
+ if !host.ends_with("pc-windows-gnu") {
return None;
}
// anything requiring us to distribute a license, but it's likely the
// install will *also* include the rust-mingw package, which also needs
// licenses, so to be safe we just include it here in all MinGW packages.
- if host.contains("pc-windows-gnu") {
+ if host.ends_with("pc-windows-gnu") {
make_win_dist(tarball.image_dir(), &tmpdir(builder), host, builder);
tarball.add_dir(builder.src.join("src/etc/third-party"), "share/doc");
}
tarballs.push(builder.ensure(Rustc { compiler: builder.compiler(stage, target) }));
tarballs.push(builder.ensure(Std { compiler, target }).expect("missing std"));
- if target.contains("windows-gnu") {
+ if target.ends_with("windows-gnu") {
tarballs.push(builder.ensure(Mingw { host: target }).expect("missing mingw"));
}
prepare(tool);
}
}
- if target.contains("windows-gnu") {
+ if target.ends_with("windows-gnu") {
prepare("rust-mingw");
}
.arg("-t")
.arg(etc.join("msi/remove-duplicates.xsl")),
);
- if target.contains("windows-gnu") {
+ if target.ends_with("windows-gnu") {
builder.run(
Command::new(&heat)
.current_dir(&exe)
if built_tools.contains("miri") {
cmd.arg("-dMiriDir=miri");
}
- if target.contains("windows-gnu") {
+ if target.ends_with("windows-gnu") {
cmd.arg("-dGccDir=rust-mingw");
}
builder.run(&mut cmd);
}
candle("AnalysisGroup.wxs".as_ref());
- if target.contains("windows-gnu") {
+ if target.ends_with("windows-gnu") {
candle("GccGroup.wxs".as_ref());
}
cmd.arg("MiriGroup.wixobj");
}
- if target.contains("windows-gnu") {
+ if target.ends_with("windows-gnu") {
cmd.arg("GccGroup.wixobj");
}
// ICE57 wrongly complains about the shortcuts
.env("CFG_BUILD", target.triple)
.env("CFG_CHANNEL", &builder.config.channel);
- if target.contains("windows-gnu") {
+ if target.contains("windows-gnullvm") {
+ cmd.env("CFG_MINGW", "1").env("CFG_ABI", "LLVM");
+ } else if target.contains("windows-gnu") {
cmd.env("CFG_MINGW", "1").env("CFG_ABI", "GNU");
} else {
cmd.env("CFG_MINGW", "0").env("CFG_ABI", "MSVC");
"llvm-cov",
"llvm-dwp",
"llvm-nm",
+ "llvm-dwarfdump",
] {
tarball.add_file(src_bindir.join(exe(bin, target)), "bin", 0o755);
}
compare_mode: Option<String>,
pass: Option<String>,
run: Option<String>,
+ skip: Vec<String>,
test_args: Vec<String>,
rustc_args: Vec<String>,
fail_fast: bool,
match subcommand {
Kind::Test => {
opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
+ opts.optmulti("", "skip", "skips tests matching SUBSTRING, if supported by test tool. May be passed multiple times", "SUBSTRING");
opts.optmulti(
"",
"test-args",
compare_mode: matches.opt_str("compare-mode"),
pass: matches.opt_str("pass"),
run: matches.opt_str("run"),
+ skip: matches.opt_strs("skip"),
test_args: matches.opt_strs("test-args"),
rustc_args: matches.opt_strs("rustc-args"),
fail_fast: !matches.opt_present("no-fail-fast"),
}
pub fn test_args(&self) -> Vec<&str> {
+ let mut args = vec![];
+
+ match *self {
+ Subcommand::Test { ref skip, .. } => {
+ for s in skip {
+ args.push("--skip");
+ args.push(s.as_str());
+ }
+ }
+ _ => (),
+ };
+
match *self {
Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
- test_args.iter().flat_map(|s| s.split_whitespace()).collect()
+ args.extend(test_args.iter().flat_map(|s| s.split_whitespace()))
}
- _ => Vec::new(),
+ _ => (),
}
+
+ args
}
pub fn rustc_args(&self) -> Vec<&str> {
use crate::builder::Kind;
use crate::config::{LlvmLibunwind, TargetSelection};
use crate::util::{
- exe, libdir, mtime, output, run, run_suppressed, t, try_run, try_run_suppressed, CiEnv,
+ check_run, exe, libdir, mtime, output, run, run_suppressed, t, try_run, try_run_suppressed,
+ CiEnv,
};
mod builder;
try_run_suppressed(cmd)
}
+ /// Runs a command, printing out nice contextual information if it fails.
+ /// Returns false if do not execute at all, otherwise returns its
+ /// `status.success()`.
+ fn check_run(&self, cmd: &mut Command) -> bool {
+ if self.config.dry_run {
+ return true;
+ }
+ self.verbose(&format!("running: {:?}", cmd));
+ check_run(cmd, self.is_verbose())
+ }
+
pub fn is_verbose(&self) -> bool {
self.verbosity > 0
}
let llvm_lib = llvm_root.join("lib");
for entry in t!(fs::read_dir(&llvm_lib)) {
let lib = t!(entry).path();
- if lib.ends_with(".so") {
+ if lib.extension().map_or(false, |ext| ext == "so") {
fix_bin_or_dylib(builder, &lib);
}
}
entries
};
patchelf.args(&[OsString::from("--set-rpath"), rpath_entries]);
- if !fname.ends_with(".so") {
+ if !fname.extension().map_or(false, |ext| ext == "so") {
// Finally, set the corret .interp for binaries
let dynamic_linker_path = nix_deps_dir.join("nix-support/dynamic-linker");
// FIXME: can we support utf8 here? `args` doesn't accept Vec<u8>, only OsString ...
fn download_with_retries(builder: &Builder<'_>, tempfile: &str, url: &str) {
println!("downloading {}", url);
-
- // FIXME: check if curl is installed instead of skipping straight to powershell
- if builder.build.build.contains("windows-msvc") {
- for _ in 0..3 {
- if builder.try_run(Command::new("PowerShell.exe").args(&[
- "/nologo",
- "-Command",
- "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
- &format!(
- "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')",
- url, tempfile
- ),
- ])) {
- return;
+ // Try curl. If that fails and we are on windows, fallback to PowerShell.
+ if !builder.check_run(Command::new("curl").args(&[
+ "-#",
+ "-y",
+ "30",
+ "-Y",
+ "10", // timeout if speed is < 10 bytes/sec for > 30 seconds
+ "--connect-timeout",
+ "30", // timeout if cannot connect within 30 seconds
+ "--retry",
+ "3",
+ "-Sf",
+ "-o",
+ tempfile,
+ url,
+ ])) {
+ if builder.build.build.contains("windows-msvc") {
+ println!("Fallback to PowerShell");
+ for _ in 0..3 {
+ if builder.try_run(Command::new("PowerShell.exe").args(&[
+ "/nologo",
+ "-Command",
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
+ &format!(
+ "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')",
+ url, tempfile
+ ),
+ ])) {
+ return;
+ }
+ println!("\nspurious failure, trying again");
}
- println!("\nspurious failure, trying again");
}
- } else {
- builder.run(Command::new("curl").args(&[
- "-#",
- "-y",
- "30",
- "-Y",
- "10", // timeout if speed is < 10 bytes/sec for > 30 seconds
- "--connect-timeout",
- "30", // timeout if cannot connect within 30 seconds
- "--retry",
- "3",
- "-Sf",
- "-o",
- tempfile,
- url,
- ]));
+ std::process::exit(1);
}
}
cfg.define("__LIBUNWIND_IS_NATIVE_ONLY", None);
cfg.define("NDEBUG", None);
}
+ if self.target.contains("windows") {
+ cfg.define("_LIBUNWIND_HIDE_SYMBOLS", "1");
+ cfg.define("_LIBUNWIND_IS_NATIVE_ONLY", "1");
+ }
}
cc_cfg.compiler(builder.cc(self.target));
status.success()
}
+pub fn check_run(cmd: &mut Command, print_cmd_on_fail: bool) -> bool {
+ let status = match cmd.status() {
+ Ok(status) => status,
+ Err(e) => {
+ println!("failed to execute command: {:?}\nerror: {}", cmd, e);
+ return false;
+ }
+ };
+ if !status.success() && print_cmd_on_fail {
+ println!(
+ "\n\ncommand did not execute successfully: {:?}\n\
+ expected success, got: {}\n\n",
+ cmd, status
+ );
+ }
+ status.success()
+}
+
pub fn run_suppressed(cmd: &mut Command) {
if !try_run_suppressed(cmd) {
std::process::exit(1);
set -ex
-# Originally from https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/clang+llvm-11.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
-curl https://ci-mirrors.rust-lang.org/rustc/2021-01-14-clang%2Bllvm-11.0.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz | \
+# Originally from https://github.com/llvm/llvm-project/releases/download/llvmorg-14.0.0/clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz
+curl https://ci-mirrors.rust-lang.org/rustc/2022-05-10-clang%2Bllvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz | \
tar xJf -
-export PATH=`pwd`/clang+llvm-11.0.1-x86_64-linux-gnu-ubuntu-16.04/bin:$PATH
+bin="$PWD/clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04/bin"
git clone https://github.com/WebAssembly/wasi-libc
cd wasi-libc
-git reset --hard ad5133410f66b93a2381db5b542aad5e0964db96
-make -j$(nproc) INSTALL_DIR=/wasm32-wasi install
+git reset --hard 9886d3d6200fcc3726329966860fc058707406cd
+make -j$(nproc) \
+ CC="$bin/clang" \
+ NM="$bin/llvm-nm" \
+ AR="$bin/llvm-ar" \
+ INSTALL_DIR=/wasm32-wasi \
+ install
cd ..
rm -rf wasi-libc
-0.9.0
\ No newline at end of file
+0.9.3
\ No newline at end of file
# Here we're profiling the `rustc` frontend, so we also include `Check`.
# The benchmark set includes various stress tests that put the frontend under pressure.
-gather_profiles "Check,Debug,Opt" "All" \
+# The profile data is written into a single filepath that is being repeatedly merged when each
+# rustc invocation ends. Empirically, this can result in some profiling data being lost.
+# That's why we override the profile path to include the PID. This will produce many more profiling
+# files, but the resulting profile will produce a slightly faster rustc binary.
+LLVM_PROFILE_FILE=/tmp/rustc-pgo/default_%m_%p.profraw gather_profiles "Check,Debug,Opt" "All" \
"externs,ctfe-stress-4,cargo-0.60.0,token-stream-stress,match-stress,tuple-stress"
# Merge the profile data we gathered
fi
deploy_url="s3://${DEPLOY_BUCKET}/${deploy_dir}/$(ciCommit)"
-retry aws s3 cp --no-progress --recursive --acl public-read "${upload_dir}" "${deploy_url}"
+retry aws s3 cp --storage-class INTELLIGENT_TIERING \
+ --no-progress --recursive --acl public-read "${upload_dir}" "${deploy_url}"
- [Platform Support](platform-support.md)
- [Template for target-specific documentation](platform-support/TEMPLATE.md)
- [aarch64-apple-ios-sim](platform-support/aarch64-apple-ios-sim.md)
- - [aarch64-unknown-none-hermitkernel](platform-support/aarch64-unknown-none-hermitkernel.md)
- [armv7-unknown-linux-uclibceabi](platform-support/armv7-unknown-linux-uclibceabi.md)
- [armv7-unknown-linux-uclibceabihf](platform-support/armv7-unknown-linux-uclibceabihf.md)
- [\*-kmc-solid_\*](platform-support/kmc-solid.md)
- [m68k-unknown-linux-gnu](platform-support/m68k-unknown-linux-gnu.md)
- [mips64-openwrt-linux-musl](platform-support/mips64-openwrt-linux-musl.md)
+ - [nvptx64-nvidia-cuda](platform-support/nvptx64-nvidia-cuda.md)
+ - [*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md)
- [*-unknown-openbsd](platform-support/openbsd.md)
- [wasm64-unknown-unknown](platform-support/wasm64-unknown-unknown.md)
- [x86_64-unknown-none](platform-support/x86_64-unknown-none.md)
If the kind is specified, then linking modifiers can be attached to it.
Modifiers are specified as a comma-delimited string with each modifier prefixed with
either a `+` or `-` to indicate that the modifier is enabled or disabled, respectively.
-The last boolean value specified for a given modifier wins. \
+Specifying multiple `modifiers` arguments in a single `link` attribute,
+or multiple identical modifiers in the same `modifiers` argument is not currently supported. \
Example: `-l static:+whole-archive=mylib`.
The kind of library and the modifiers can also be specified in a [`#[link]`
`aarch64-apple-ios-macabi` | ? | | Apple Catalyst on ARM64
`aarch64-apple-tvos` | * | | ARM64 tvOS
[`aarch64-kmc-solid_asp3`](platform-support/kmc-solid.md) | ✓ | | ARM64 SOLID with TOPPERS/ASP3
+[`aarch64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ |
`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD
`aarch64-unknown-hermit` | ✓ | | ARM64 HermitCore
`aarch64-unknown-uefi` | * | | ARM64 UEFI
[`wasm64-unknown-unknown`](platform-support/wasm64-unknown-unknown.md) | ? | | WebAssembly
`x86_64-apple-ios-macabi` | ✓ | | Apple Catalyst on x86_64
`x86_64-apple-tvos` | * | | x86 64-bit tvOS
+[`x86_64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ |
`x86_64-pc-windows-msvc` | * | | 64-bit Windows XP support
`x86_64-sun-solaris` | ? | | Deprecated target for 64-bit Solaris 10/11, illumos
`x86_64-unknown-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD
--- /dev/null
+# `nvptx64-nvidia-cuda`
+
+**Tier: 2**
+
+This is the target meant for deploying code for Nvidia® accelerators based on their CUDA
+platform.
+
+## Target maintainers
+
+- Riccardo D'Ambrosio, https://github.com/RDambrosio016
+- Kjetil Kjeka, https://github.com/kjetilkjeka
+
+<!-- FIXME: fill this out
+
+## Requirements
+
+Does the target support host tools, or only cross-compilation? Does the target
+support std, or alloc (either with a default allocator, or if the user supplies
+an allocator)?
+
+Document the expectations of binaries built for the target. Do they assume
+specific minimum features beyond the baseline of the CPU/environment/etc? What
+version of the OS or environment do they expect?
+
+Are there notable `#[target_feature(...)]` or `-C target-feature=` values that
+programs may wish to use?
+
+What calling convention does `extern "C"` use on the target?
+
+What format do binaries use by default? ELF, PE, something else?
+
+## Building the target
+
+If Rust doesn't build the target by default, how can users build it? Can users
+just add it to the `target` list in `config.toml`?
+
+## Building Rust programs
+
+Rust does not yet ship pre-compiled artifacts for this target. To compile for
+this target, you will either need to build Rust with the target enabled (see
+"Building the target" above), or build your own copy of `core` by using
+`build-std` or similar.
+
+## Testing
+
+Does the target support running binaries, or do binaries have varying
+expectations that prevent having a standard way to run them? If users can run
+binaries, can they do so in some common emulator, or do they need native
+hardware? Does the target support running the Rust testsuite?
+
+## Cross-compilation toolchains and C code
+
+Does the target support C code? If so, what toolchain target should users use
+to build compatible C code? (This may match the target triple, or it may be a
+toolchain for a different target triple, potentially with specific options or
+caveats.)
+
+-->
--- /dev/null
+# \*-pc-windows-gnullvm
+
+**Tier: 3**
+
+Windows targets similar to `*-pc-windows-gnu` but using UCRT as the runtime and various LLVM tools/libraries instead of GCC/Binutils.
+
+Target triples avaiable so far:
+- `aarch64-pc-windows-gnullvm`
+- `x86_64-pc-windows-gnullvm`
+
+## Target maintainers
+
+- [@mati865](https://github.com/mati865)
+
+## Requirements
+
+The easiest way to obtain these targets is cross-compilation but native build from `x86_64-pc-windows-gnu` is possible with few hacks which I don't recommend.
+Std support is expected to be on pair with `*-pc-windows-gnu`.
+
+Binaries for this target should be at least on pair with `*-pc-windows-gnu` in terms of requirements and functionality.
+
+Those targets follow Windows calling convention for `extern "C"`.
+
+Like with any other Windows target created binaries are in PE format.
+
+## Building the target
+
+For cross-compilation I recommend using [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) toolchain, one change that seems necessary beside configuring corss compilers is disabling experimental `m86k` target. Otherwise LLVM build fails with `multiple definition ...` errors.
+Native bootstrapping builds require rather fragile hacks until host artifacts are avaiable so I won't describe them here.
+
+## Building Rust programs
+
+Rust does not yet ship pre-compiled artifacts for this target. To compile for
+this target, you will either need to build Rust with the target enabled (see
+"Building the target" above), or build your own copy of `core` by using
+`build-std` or similar.
+
+## Testing
+
+Created binaries work fine on Windows or Wine using native hardware. Testing AArch64 on x86_64 is problematic though and requires spending some time with QEMU.
+Once these targets bootstrap themselves on native hardware they should pass Rust testsuite.
+
+## Cross-compilation toolchains and C code
+
+Compatible C code can be built with Clang's `aarch64-pc-windows-gnu` and `x86_64-pc-windows-gnu` targets as long as LLVM based C toolchains are used.
+Those include:
+- [llvm-mingw](https://github.com/mstorsjo/llvm-mingw)
+- [MSYS2 with CLANG* environment](https://www.msys2.org/docs/environments)
## The Theme Picker and Search Interface
When viewing `rustdoc`'s output in a browser with JavaScript enabled,
-a dynamic interface appears at the top of the page.
-To the left is the theme picker, denoted with a paint-brush icon,
-and the search interface, help screen, and options appear to the right of that.
-
-### The Theme Picker
-
-Clicking on the theme picker provides a list of themes -
-by default `ayu`, `light`, and `dark` -
-which are available for viewing.
+a dynamic interface appears at the top of the page composed of the search
+interface, help screen, and options.
### The Search Interface
followed by a colon (such as `mod:`) to restrict the results to just that
kind of item. (The available items are listed in the help popup.)
+### Changing displayed theme
+
+You can change the displayed theme by opening the settings menu (the gear
+icon in the upper right) and then pick a new one from there.
+
### Shortcuts
Pressing `S` while focused elsewhere on the page will move focus to the
search bar, and pressing `?` shows the help screen,
which includes all these shortcuts and more.
-Pressing `T` focuses the theme picker.
When the search results are focused,
the left and right arrows move between tabs and the up and down arrows move
A good example of using this feature to create a dark theme is documented [on
this blog]. Just remember, dark theme is already included in the rustdoc output
-by clicking on the paintbrush. Adding additional options to the themes are
-as easy as creating a custom theme `.css` file and using the following syntax:
+by clicking on the gear icon in the upper right. Adding additional options to the
+themes are as easy as creating a custom theme `.css` file and using the following
+syntax:
```bash
rustdoc --theme awesome.css src/lib.rs
To enable a sanitizer compile with `-Zsanitizer=address`,`-Zsanitizer=cfi`,
`-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, `-Zsanitizer=memory`,
-`-Zsanitizer=memtag`, or `-Zsanitizer=thread`.
+`-Zsanitizer=memtag`, or `-Zsanitizer=thread`. You might also need the `--target` and `build-std` flags. Example:
+```shell
+$ RUSTFLAGS=-Zsanitizer=address cargo build -Zbuild-std --target x86_64-unknown-linux-gnu
+```
# AddressSanitizer
atty = "0.2"
pulldown-cmark = { version = "0.9", default-features = false }
minifier = "0.0.43"
-rayon = "1.5.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
smallvec = "1.6.1"
default-features = false
features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"]
+[target.'cfg(windows)'.dependencies]
+rayon = "1.5.1"
+
[dev-dependencies]
expect-test = "1.0"
impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
crate fn get_blanket_impls(&mut self, item_def_id: DefId) -> Vec<Item> {
let param_env = self.cx.tcx.param_env(item_def_id);
- let ty = self.cx.tcx.type_of(item_def_id);
+ let ty = self.cx.tcx.bound_type_of(item_def_id);
trace!("get_blanket_impls({:?})", ty);
let mut impls = Vec::new();
self.cx.with_all_traits(|cx, all_traits| {
for &trait_def_id in all_traits {
if !cx.cache.access_levels.is_public(trait_def_id)
- || cx.generated_synthetics.get(&(ty, trait_def_id)).is_some()
+ || cx.generated_synthetics.get(&(ty.0, trait_def_id)).is_some()
{
continue;
}
trait_def_id,
impl_def_id
);
- let trait_ref = cx.tcx.impl_trait_ref(impl_def_id).unwrap();
- let is_param = matches!(trait_ref.self_ty().kind(), ty::Param(_));
+ let trait_ref = cx.tcx.bound_impl_trait_ref(impl_def_id).unwrap();
+ let is_param = matches!(trait_ref.0.self_ty().kind(), ty::Param(_));
let may_apply = is_param && cx.tcx.infer_ctxt().enter(|infcx| {
let substs = infcx.fresh_substs_for_item(DUMMY_SP, item_def_id);
let ty = ty.subst(infcx.tcx, substs);
- let param_env = param_env.subst(infcx.tcx, substs);
+ let param_env = EarlyBinder(param_env).subst(infcx.tcx, substs);
let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
let trait_ref = trait_ref.subst(infcx.tcx, impl_substs);
continue;
}
- cx.generated_synthetics.insert((ty, trait_def_id));
+ cx.generated_synthetics.insert((ty.0, trait_def_id));
impls.push(Item {
name: None,
),
// FIXME(eddyb) compute both `trait_` and `for_` from
// the post-inference `trait_ref`, as it's more accurate.
- trait_: Some(trait_ref.clean(cx)),
- for_: ty.clean(cx),
+ trait_: Some(trait_ref.0.clean(cx)),
+ for_: ty.0.clean(cx),
items: cx.tcx
.associated_items(impl_def_id)
.in_definition_order()
.map(|x| x.clean(cx))
.collect::<Vec<_>>(),
polarity: ty::ImplPolarity::Positive,
- kind: ImplKind::Blanket(box trait_ref.self_ty().clean(cx)),
+ kind: ImplKind::Blanket(box trait_ref.0.self_ty().clean(cx)),
}),
cfg: None,
});
use crate::core::DocContext;
use crate::formats::item_type::ItemType;
-type Attrs<'hir> = rustc_middle::ty::Attributes<'hir>;
+type Attrs<'hir> = &'hir [ast::Attribute];
/// Attempt to inline a definition into this AST.
///
}
crate fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> Attrs<'hir> {
- cx.tcx.get_attrs(did)
+ cx.tcx.get_attrs_unchecked(did)
}
/// Record an external fully qualified name in the external_paths cache.
let trait_ = clean::TraitWithExtraInfo {
trait_,
- is_notable: clean::utils::has_doc_flag(cx.tcx.get_attrs(did), sym::notable_trait),
+ is_notable: clean::utils::has_doc_flag(cx.tcx, did, sym::notable_trait),
};
cx.external_traits.borrow_mut().insert(did, trait_);
cx.active_extern_traits.remove(&did);
use rustc_middle::middle::resolve_lifetime as rl;
use rustc_middle::ty::fold::TypeFolder;
use rustc_middle::ty::subst::{InternalSubsts, Subst};
-use rustc_middle::ty::{self, AdtKind, DefIdTree, Lift, Ty, TyCtxt};
+use rustc_middle::ty::{self, AdtKind, DefIdTree, EarlyBinder, Lift, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_span::hygiene::{AstPass, MacroKind};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
.tcx
.explicit_item_bounds(def_id)
.iter()
- .map(|(bound, _)| bound.subst(cx.tcx, substs))
+ .map(|(bound, _)| EarlyBinder(*bound).subst(cx.tcx, substs))
.collect::<Vec<_>>();
let mut regions = vec![];
let mut has_sized = false;
// Failing that, see if there's an attribute specifying where to find this
// external crate
let did = self.crate_num.as_def_id();
- tcx.get_attrs(did)
- .lists(sym::doc)
+ tcx.get_attrs(did, sym::doc)
+ .flat_map(|attr| attr.meta_item_list().unwrap_or_default())
.filter(|a| a.has_name(sym::html_root_url))
.filter_map(|a| a.value_str())
.map(to_remote)
let as_keyword = |res: Res<!>| {
if let Res::Def(DefKind::Mod, def_id) = res {
- let attrs = tcx.get_attrs(def_id);
let mut keyword = None;
- for attr in attrs.lists(sym::doc) {
- if attr.has_name(sym::keyword) {
- if let Some(v) = attr.value_str() {
+ let meta_items = tcx
+ .get_attrs(def_id, sym::doc)
+ .flat_map(|attr| attr.meta_item_list().unwrap_or_default());
+ for meta in meta_items {
+ if meta.has_name(sym::keyword) {
+ if let Some(v) = meta.value_str() {
keyword = Some(v);
break;
}
// rendering by delegating everything to a hash map.
let as_primitive = |res: Res<!>| {
if let Res::Def(DefKind::Mod, def_id) = res {
- let attrs = tcx.get_attrs(def_id);
let mut prim = None;
- for attr in attrs.lists(sym::doc) {
- if let Some(v) = attr.value_str() {
- if attr.has_name(sym::primitive) {
+ let meta_items = tcx
+ .get_attrs(def_id, sym::doc)
+ .flat_map(|attr| attr.meta_item_list().unwrap_or_default());
+ for meta in meta_items {
+ if let Some(v) = meta.value_str() {
+ if meta.has_name(sym::primitive) {
prim = PrimitiveType::from_symbol(v);
if prim.is_some() {
break;
}
crate fn inner_docs(&self, tcx: TyCtxt<'_>) -> bool {
- self.item_id.as_def_id().map(|did| tcx.get_attrs(did).inner_docs()).unwrap_or(false)
+ self.item_id
+ .as_def_id()
+ .map(|did| tcx.get_attrs_unchecked(did).inner_docs())
+ .unwrap_or(false)
}
crate fn span(&self, tcx: TyCtxt<'_>) -> Span {
kind: ItemKind,
cx: &mut DocContext<'_>,
) -> Item {
- let ast_attrs = cx.tcx.get_attrs(def_id);
+ let ast_attrs = cx.tcx.get_attrs_unchecked(def_id);
Self::from_def_id_and_attrs_and_parts(
def_id,
///
/// This function exists because it runs on `hir::Attributes` whereas the other is a
/// `clean::Attributes` method.
-crate fn has_doc_flag(attrs: ty::Attributes<'_>, flag: Symbol) -> bool {
- attrs.iter().any(|attr| {
- attr.has_name(sym::doc)
- && attr.meta_item_list().map_or(false, |l| rustc_attr::list_contains_name(&l, flag))
+crate fn has_doc_flag(tcx: TyCtxt<'_>, did: DefId, flag: Symbol) -> bool {
+ tcx.get_attrs(did, sym::doc).any(|attr| {
+ attr.meta_item_list().map_or(false, |l| rustc_attr::list_contains_name(&l, flag))
})
}
where
E: PathError,
{
- if !self.sync_only && cfg!(windows) {
+ #[cfg(windows)]
+ if !self.sync_only {
// A possible future enhancement after more detailed profiling would
// be to create the file sync so errors are reported eagerly.
let sender = self.errors.clone().expect("can't write after closing");
} else {
fs::write(&path, contents).map_err(|e| E::new(e, path))?;
}
+
+ #[cfg(not(windows))]
+ fs::write(&path, contents).map_err(|e| E::new(e, path))?;
+
Ok(())
}
}
// used in tera template files).
map.insert("mainThemeStyle".into(), 1);
map.insert("themeStyle".into(), 1);
- map.insert("theme-picker".into(), 1);
- map.insert("theme-choices".into(), 1);
map.insert("settings-menu".into(), 1);
map.insert("help-button".into(), 1);
map.insert("main-content".into(), 1);
|buf: &mut Buffer| {
write!(
buf,
- "<script defer src=\"{}settings{}.js\"></script>",
- page.static_root_path.unwrap_or(""),
- page.resource_suffix
+ "<div class=\"main-heading\">\
+ <h1 class=\"fqn\">\
+ <span class=\"in-band\">Rustdoc settings</span>\
+ </h1>\
+ <span class=\"out-of-band\">\
+ <a id=\"back\" href=\"javascript:void(0)\" onclick=\"history.back();\">\
+ Back\
+ </a>\
+ </span>\
+ </div>\
+ <link rel=\"stylesheet\" type=\"text/css\" \
+ href=\"{root_path}settings{suffix}.css\">\
+ <script defer src=\"{root_path}settings{suffix}.js\"></script>",
+ root_path = page.static_root_path.unwrap_or(""),
+ suffix = page.resource_suffix,
)
},
&self.shared.style_files,
clean::ImportItem(ref import) => {
let (stab, stab_tags) = if let Some(import_def_id) = import.source.did {
- let ast_attrs = cx.tcx().get_attrs(import_def_id);
+ let ast_attrs = cx.tcx().get_attrs_unchecked(import_def_id);
let import_attrs = Box::new(clean::Attributes::from_ast(ast_attrs, None));
// Just need an item with the correct def_id and attrs
write_toolchain("favicon-16x16.png", static_files::RUST_FAVICON_PNG_16)?;
write_toolchain("favicon-32x32.png", static_files::RUST_FAVICON_PNG_32)?;
}
- write_toolchain("brush.svg", static_files::BRUSH_SVG)?;
write_toolchain("wheel.svg", static_files::WHEEL_SVG)?;
write_toolchain("clipboard.svg", static_files::CLIPBOARD_SVG)?;
write_toolchain("down-arrow.svg", static_files::DOWN_ARROW_SVG)?;
));
all_sources.sort();
Ok(format!(
- "var N = null;var sourcesIndex = {{}};\n{}\ncreateSourceSidebar();\n",
+ "var sourcesIndex = {{}};\n{}\ncreateSourceSidebar();\n",
all_sources.join("\n")
)
.into_bytes())
write_crate("search-index.js", &|| {
let mut v = String::from("var searchIndex = JSON.parse('{\\\n");
v.push_str(&all_indexes.join(",\\\n"));
- v.push_str("\\\n}');\nif (window.initSearch) {window.initSearch(searchIndex)};");
+ v.push_str(
+ r#"\
+}');
+if (typeof window !== 'undefined' && window.initSearch) {window.initSearch(searchIndex)};
+if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
+"#,
+ );
Ok(v.into_bytes())
})?;
"no-var": ["error"],
"prefer-const": ["error"],
"prefer-arrow-callback": ["error"],
+ "brace-style": [
+ "error",
+ "1tbs",
+ { "allowSingleLine": false }
+ ],
}
};
/* The search bar and related controls don't work without JS */
display: none;
}
-
-#theme-picker {
- display: none;
-}
margin-left: 0.25em;
padding-left: 0.3125em;
padding-right: 23px;
- border: 0;
+ border: 1px solid;
border-radius: 4px;
outline: none;
cursor: pointer;
margin-bottom: 6px;
}
-.theme-picker {
- position: absolute;
- left: -38px;
- top: 4px;
-}
-
-.theme-picker button {
- outline: none;
-}
-
#settings-menu, #help-button {
margin-left: 4px;
outline: none;
}
-#theme-picker, #copy-path {
+#copy-path {
height: 34px;
}
-#theme-picker, #settings-menu, #help-button, #copy-path {
+#settings-menu > a, #help-button, #copy-path {
padding: 5px;
width: 33px;
border: 1px solid;
border-radius: 2px;
cursor: pointer;
}
+#settings-menu {
+ padding: 0;
+}
+#settings-menu > a {
+ padding: 5px;
+ width: 100%;
+ height: 100%;
+ display: block;
+}
@keyframes rotating {
from {
transform: rotate(360deg);
}
}
-#settings-menu.rotate img {
+#settings-menu.rotate > a img {
animation: rotating 2s linear infinite;
}
+#settings-menu #settings {
+ position: absolute;
+ right: 0;
+ z-index: 1;
+ display: block;
+ margin-top: 7px;
+ border-radius: 3px;
+ border: 1px solid;
+}
+#settings-menu #settings .setting-line {
+ margin: 0.6em;
+}
+/* This rule is to draw the little arrow connecting the settings menu to the gear icon. */
+#settings-menu #settings::before {
+ content: '';
+ position: absolute;
+ right: 11px;
+ border: solid;
+ border-width: 1px 1px 0 0;
+ display: inline-block;
+ padding: 4px;
+ transform: rotate(-45deg);
+ top: -5px;
+}
#help-button {
font-family: "Fira Sans", Arial, sans-serif;
margin-left: 32px;
}
- /* Space is at a premium on mobile, so remove the theme-picker icon. */
- #theme-picker {
- display: none;
- width: 0;
- }
-
.content {
margin-left: 0px;
}
#crate-search {
border-radius: 4px;
- border: 0;
}
.docblock {
position: absolute;
}
-.select-wrapper {
- float: right;
- position: relative;
- height: 27px;
- min-width: 25%;
-}
-
-.select-wrapper select {
- appearance: none;
- -moz-appearance: none;
- -webkit-appearance: none;
- background: none;
- border: 2px solid #ccc;
- padding-right: 28px;
- width: 100%;
-}
-
-.select-wrapper img {
- pointer-events: none;
- position: absolute;
- right: 0;
- bottom: 0;
- background: #ccc;
- height: 100%;
- width: 28px;
- padding: 0px 4px;
-}
-
-.select-wrapper select option {
- color: initial;
-}
-
.slider {
position: absolute;
cursor: pointer;
right: 0;
bottom: 0;
background-color: #ccc;
- -webkit-transition: .3s;
transition: .3s;
}
left: 4px;
bottom: 4px;
background-color: white;
- -webkit-transition: .3s;
transition: .3s;
}
}
input:checked + .slider:before {
- -webkit-transform: translateX(19px);
- -ms-transform: translateX(19px);
transform: translateX(19px);
}
/* General structure and fonts */
-body {
+body, #settings-menu #settings, #settings-menu #settings::before {
background-color: #0f1419;
color: #c5c5c5;
}
#crate-search, .search-input {
background-color: #141920;
- border-color: #424c57;
- color: #c5c5c5;
+ /* Without the `!important`, the border-color is ignored for `<select>`... */
+ border-color: #424c57 !important;
}
.search-input {
box-shadow: inset 0 -1px 0 #5c6773;
}
-#theme-picker, #settings-menu, #help-button {
+#settings-menu > a, #help-button {
border-color: #5c6773;
background-color: #0f1419;
color: #fff;
}
-#theme-picker > img, #settings-menu > img {
+#settings-menu > a img {
filter: invert(100);
}
+#settings-menu #settings, #settings-menu #settings::before {
+ border-color: #5c6773;
+}
+
#copy-path {
color: #fff;
}
filter: invert(100%);
}
-#theme-picker:hover, #theme-picker:focus,
-#settings-menu:hover, #settings-menu:focus,
+#settings-menu > a:hover, #settings-menu > a:focus,
#help-button:hover, #help-button:focus {
border-color: #e0e0e0;
}
background-color: rgba(110, 110, 110, 0.33);
}
-@media (max-width: 700px) {
- #theme-picker {
- background: #0f1419;
- }
-}
-
.search-results .result-name span.alias {
color: #c5c5c5;
}
-body {
+body, #settings-menu #settings, #settings-menu #settings::before {
background-color: #353535;
color: #ddd;
}
#crate-search, .search-input {
color: #111;
background-color: #f0f0f0;
- border-color: #000;
+ /* Without the `!important`, the border-color is ignored for `<select>`... */
+ border-color: #f0f0f0 !important;
}
.search-input {
box-shadow: inset 0 -1px 0 #c6cbd1;
}
-#theme-picker, #settings-menu, #help-button {
+#settings-menu > a, #help-button {
border-color: #e0e0e0;
background: #f0f0f0;
color: #000;
}
-#theme-picker:hover, #theme-picker:focus,
-#settings-menu:hover, #settings-menu:focus,
+#settings-menu > a:hover, #settings-menu > a:focus,
#help-button:hover, #help-button:focus {
border-color: #ffb900;
}
+#settings-menu #settings, #settings-menu #settings::before {
+ border-color: #d2d2d2;
+}
+
#copy-path {
color: #999;
}
background-color: #4e4e4e;
}
-@media (max-width: 700px) {
- #theme-picker {
- background: #f0f0f0;
- }
-}
-
.search-results .result-name span.alias {
color: #fff;
}
/* General structure and fonts */
-body {
+body, #settings-menu #settings, #settings-menu #settings::before {
background-color: white;
color: black;
}
}
#crate-search, .search-input {
- color: #555;
background-color: white;
- border-color: #e0e0e0;
+ /* Without the `!important`, the border-color is ignored for `<select>`... */
+ border-color: #e0e0e0 !important;
}
.search-input:focus {
box-shadow: inset 0 -1px 0 #c6cbd1;
}
-#theme-picker, #settings-menu, #help-button {
+#settings-menu > a, #help-button {
border-color: #e0e0e0;
background-color: #fff;
}
-#theme-picker:hover, #theme-picker:focus,
-#settings-menu:hover, #settings-menu:focus,
+#settings-menu > a:hover, #settings-menu > a:focus,
#help-button:hover, #help-button:focus {
border-color: #717171;
}
+#settings-menu #settings, #settings-menu #settings::before {
+ border-color: #DDDDDD;
+}
+
#copy-path {
color: #999;
}
background-color: #eee;
}
-@media (max-width: 700px) {
- #theme-picker {
- background: #fff;
- }
-}
-
.search-results .result-name span.alias {
color: #000;
}
// Local js definitions:
/* global addClass, getSettingValue, hasClass, searchState */
/* global onEach, onEachLazy, removeClass */
-/* global switchTheme, useSystemTheme */
"use strict";
return String.fromCharCode(c);
}
-const THEME_PICKER_ELEMENT_ID = "theme-picker";
-const THEMES_ELEMENT_ID = "theme-choices";
const MAIN_ID = "main-content";
const SETTINGS_BUTTON_ID = "settings-menu";
const ALTERNATIVE_DISPLAY_ID = "alternative-display";
const NOT_DISPLAYED_ID = "not-displayed";
-function getThemesElement() {
- return document.getElementById(THEMES_ELEMENT_ID);
-}
-
-function getThemePickerElement() {
- return document.getElementById(THEME_PICKER_ELEMENT_ID);
-}
-
function getSettingsButton() {
return document.getElementById(SETTINGS_BUTTON_ID);
}
return window.location.href.split("?")[0].split("#")[0];
}
-function showThemeButtonState() {
- const themePicker = getThemePickerElement();
- const themeChoices = getThemesElement();
-
- themeChoices.style.display = "block";
- themePicker.style.borderBottomRightRadius = "0";
- themePicker.style.borderBottomLeftRadius = "0";
-}
-
-function hideThemeButtonState() {
- const themePicker = getThemePickerElement();
- const themeChoices = getThemesElement();
-
- themeChoices.style.display = "none";
- themePicker.style.borderBottomRightRadius = "3px";
- themePicker.style.borderBottomLeftRadius = "3px";
-}
-
window.hideSettings = () => {
// Does nothing by default.
};
-// Set up the theme picker list.
-(function () {
- if (!document.location.href.startsWith("file:///")) {
- return;
- }
- const themeChoices = getThemesElement();
- const themePicker = getThemePickerElement();
- const availableThemes = getVar("themes").split(",");
-
- removeClass(themeChoices.parentElement, "hidden");
-
- function switchThemeButtonState() {
- if (themeChoices.style.display === "block") {
- hideThemeButtonState();
- } else {
- showThemeButtonState();
- }
- }
-
- function handleThemeButtonsBlur(e) {
- const active = document.activeElement;
- const related = e.relatedTarget;
-
- if (active.id !== THEME_PICKER_ELEMENT_ID &&
- (!active.parentNode || active.parentNode.id !== THEMES_ELEMENT_ID) &&
- (!related ||
- (related.id !== THEME_PICKER_ELEMENT_ID &&
- (!related.parentNode || related.parentNode.id !== THEMES_ELEMENT_ID)))) {
- hideThemeButtonState();
- }
- }
-
- themePicker.onclick = switchThemeButtonState;
- themePicker.onblur = handleThemeButtonsBlur;
- availableThemes.forEach(item => {
- const but = document.createElement("button");
- but.textContent = item;
- but.onclick = () => {
- switchTheme(window.currentTheme, window.mainTheme, item, true);
- useSystemTheme(false);
- };
- but.onblur = handleThemeButtonsBlur;
- themeChoices.appendChild(but);
- });
-}());
-
/**
* This function inserts `newNode` after `referenceNode`. It doesn't work if `referenceNode`
* doesn't have a parent node.
ev.preventDefault();
}
searchState.defocus();
- hideThemeButtonState();
+ window.hideSettings();
}
const disableShortcuts = getSettingValue("disable-shortcuts") === "true";
return;
}
- let themePicker;
-
if (document.activeElement.tagName === "INPUT") {
switch (getVirtualKey(ev)) {
case "Escape":
displayHelp(true, ev);
break;
- case "t":
- case "T":
- displayHelp(false, ev);
- ev.preventDefault();
- themePicker = getThemePickerElement();
- themePicker.click();
- themePicker.focus();
- break;
-
default:
- if (getThemePickerElement().parentNode.contains(ev.target)) {
- handleThemeKeyDown(ev);
- }
- }
- }
- }
-
- function handleThemeKeyDown(ev) {
- const active = document.activeElement;
- const themes = getThemesElement();
- switch (getVirtualKey(ev)) {
- case "ArrowUp":
- ev.preventDefault();
- if (active.previousElementSibling && ev.target.id !== THEME_PICKER_ELEMENT_ID) {
- active.previousElementSibling.focus();
- } else {
- showThemeButtonState();
- themes.lastElementChild.focus();
- }
- break;
- case "ArrowDown":
- ev.preventDefault();
- if (active.nextElementSibling && ev.target.id !== THEME_PICKER_ELEMENT_ID) {
- active.nextElementSibling.focus();
- } else {
- showThemeButtonState();
- themes.firstElementChild.focus();
- }
- break;
- case "Enter":
- case "Return":
- case "Space":
- if (ev.target.id === THEME_PICKER_ELEMENT_ID && themes.style.display === "none") {
- ev.preventDefault();
- showThemeButtonState();
- themes.firstElementChild.focus();
+ break;
}
- break;
- case "Home":
- ev.preventDefault();
- themes.firstElementChild.focus();
- break;
- case "End":
- ev.preventDefault();
- themes.lastElementChild.focus();
- break;
- // The escape key is handled in handleEscape, not here,
- // so that pressing escape will close the menu even if it isn't focused
}
}
onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => {
if (e.parentNode.id !== "implementations-list" ||
(!hasClass(e, "implementors-toggle") &&
- !hasClass(e, "type-contents-toggle")))
- {
+ !hasClass(e, "type-contents-toggle"))
+ ) {
e.open = false;
}
});
const shortcuts = [
["?", "Show this help dialog"],
["S", "Focus the search field"],
- ["T", "Focus the theme picker menu"],
["↑", "Move up in search results"],
["↓", "Move down in search results"],
["← / →", "Switch result tab (when results focused)"],
// visible. This is necessary since updateScrapedExample calls scrollToLoc which
// depends on offsetHeight, a property that requires an element to be visible to
// compute correctly.
- setTimeout(() => { onEachLazy(moreExamples, updateScrapedExample); });
+ setTimeout(() => {
+ onEachLazy(moreExamples, updateScrapedExample);
+ });
}, {once: true});
});
})();
-/* global addClass, getNakedUrl, getSettingValue, hasOwnPropertyRustdoc, initSearch, onEach */
-/* global onEachLazy, removeClass, searchState, browserSupportsHistoryApi */
+/* global addClass, getNakedUrl, getSettingValue */
+/* global onEachLazy, removeClass, searchState, browserSupportsHistoryApi, exports */
"use strict";
// used for special search precedence
const TY_PRIMITIVE = itemTypes.indexOf("primitive");
const TY_KEYWORD = itemTypes.indexOf("keyword");
+const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../";
+
+function hasOwnPropertyRustdoc(obj, property) {
+ return Object.prototype.hasOwnProperty.call(obj, property);
+}
// In the search display, allows to switch between tabs.
function printTab(nb) {
return s1_len + s2_len;
}
-window.initSearch = rawSearchIndex => {
+function initSearch(rawSearchIndex) {
const MAX_LEV_DISTANCE = 3;
const MAX_RESULTS = 200;
const GENERICS_DATA = 2;
*/
let searchIndex;
let currentResults;
- const ALIASES = {};
- const params = searchState.getQueryStringParams();
-
- // Populate search bar with query string search term when provided,
- // but only if the input bar is empty. This avoid the obnoxious issue
- // where you start trying to do a search, and the index loads, and
- // suddenly your search is gone!
- if (searchState.input.value === "") {
- searchState.input.value = params.search || "";
- }
+ const ALIASES = Object.create(null);
function isWhitespace(c) {
return " \t\n\r".indexOf(c) !== -1;
if (foundExclamation) {
throw new Error("Cannot have more than one `!` in an ident");
} else if (parserState.pos + 1 < parserState.length &&
- isIdentCharacter(parserState.userQuery[parserState.pos + 1]))
- {
+ isIdentCharacter(parserState.userQuery[parserState.pos + 1])
+ ) {
throw new Error("`!` can only be at the end of an ident");
}
foundExclamation = true;
} else if (
isStopCharacter(c) ||
isSpecialStartCharacter(c) ||
- isSeparatorCharacter(c))
- {
+ isSeparatorCharacter(c)
+ ) {
break;
- }
- // If we allow paths ("str::string" for example).
- else if (c === ":") {
+ } else if (c === ":") { // If we allow paths ("str::string" for example).
if (!isPathStart(parserState)) {
break;
}
end = getIdentEndPosition(parserState);
}
if (parserState.pos < parserState.length &&
- parserState.userQuery[parserState.pos] === "<")
- {
+ parserState.userQuery[parserState.pos] === "<"
+ ) {
if (isInGenerics) {
throw new Error("Unexpected `<` after `<`");
} else if (start >= end) {
if (elem &&
elem.value !== "All crates" &&
- hasOwnPropertyRustdoc(rawSearchIndex, elem.value))
- {
+ hasOwnPropertyRustdoc(rawSearchIndex, elem.value)
+ ) {
return elem.value;
}
return null;
* @param {ParsedQuery} parsedQuery - The parsed user query
* @param {Object} searchWords - The list of search words to query against
* @param {Object} [filterCrates] - Crate to search in if defined
+ * @param {Object} [currentCrate] - Current crate, to rank results from this crate higher
*
* @return {ResultsTable}
*/
- function execQuery(parsedQuery, searchWords, filterCrates) {
+ function execQuery(parsedQuery, searchWords, filterCrates, currentCrate) {
const results_others = {}, results_in_args = {}, results_returned = {};
function transformResults(results) {
return out;
}
- function sortResults(results, isType) {
+ function sortResults(results, isType, preferredCrate) {
const userQuery = parsedQuery.userQuery;
const ar = [];
for (const entry in results) {
// sort by exact match with regard to the last word (mismatch goes later)
a = (aaa.word !== userQuery);
b = (bbb.word !== userQuery);
- if (a !== b) { return a - b; }
+ if (a !== b) {
+ return a - b;
+ }
// Sort by non levenshtein results and then levenshtein results by the distance
// (less changes required to match means higher rankings)
a = (aaa.lev);
b = (bbb.lev);
- if (a !== b) { return a - b; }
+ if (a !== b) {
+ return a - b;
+ }
- // sort by crate (non-current crate goes later)
- a = (aaa.item.crate !== window.currentCrate);
- b = (bbb.item.crate !== window.currentCrate);
- if (a !== b) { return a - b; }
+ // sort by crate (current crate comes first)
+ a = (aaa.item.crate !== preferredCrate);
+ b = (bbb.item.crate !== preferredCrate);
+ if (a !== b) {
+ return a - b;
+ }
// sort by item name length (longer goes later)
a = aaa.word.length;
b = bbb.word.length;
- if (a !== b) { return a - b; }
+ if (a !== b) {
+ return a - b;
+ }
// sort by item name (lexicographically larger goes later)
a = aaa.word;
b = bbb.word;
- if (a !== b) { return (a > b ? +1 : -1); }
+ if (a !== b) {
+ return (a > b ? +1 : -1);
+ }
// sort by index of keyword in item name (no literal occurrence goes later)
a = (aaa.index < 0);
b = (bbb.index < 0);
- if (a !== b) { return a - b; }
+ if (a !== b) {
+ return a - b;
+ }
// (later literal occurrence, if any, goes later)
a = aaa.index;
b = bbb.index;
- if (a !== b) { return a - b; }
+ if (a !== b) {
+ return a - b;
+ }
// special precedence for primitive and keyword pages
if ((aaa.item.ty === TY_PRIMITIVE && bbb.item.ty !== TY_KEYWORD) ||
// sort by description (no description goes later)
a = (aaa.item.desc === "");
b = (bbb.item.desc === "");
- if (a !== b) { return a - b; }
+ if (a !== b) {
+ return a - b;
+ }
// sort by type (later occurrence in `itemTypes` goes later)
a = aaa.item.ty;
b = bbb.item.ty;
- if (a !== b) { return a - b; }
+ if (a !== b) {
+ return a - b;
+ }
// sort by path (lexicographically larger goes later)
a = aaa.item.path;
b = bbb.item.path;
- if (a !== b) { return (a > b ? +1 : -1); }
+ if (a !== b) {
+ return (a > b ? +1 : -1);
+ }
// que sera, sera
return 0;
};
}
- function handleAliases(ret, query, filterCrates) {
+ function handleAliases(ret, query, filterCrates, currentCrate) {
const lowerQuery = query.toLowerCase();
// We separate aliases and crate aliases because we want to have current crate
// aliases to be before the others in the displayed results.
} else {
Object.keys(ALIASES).forEach(crate => {
if (ALIASES[crate][lowerQuery]) {
- const pushTo = crate === window.currentCrate ? crateAliases : aliases;
+ const pushTo = crate === currentCrate ? crateAliases : aliases;
const query_aliases = ALIASES[crate][lowerQuery];
for (const alias of query_aliases) {
pushTo.push(createAliasFromItem(searchIndex[alias]));
ret.others.pop();
}
};
- onEach(aliases, pushFunc);
- onEach(crateAliases, pushFunc);
+
+ aliases.forEach(pushFunc);
+ crateAliases.forEach(pushFunc);
}
/**
}
if (searchWord.indexOf(elem.pathLast) > -1 ||
- row.normalizedName.indexOf(elem.pathLast) > -1)
- {
+ row.normalizedName.indexOf(elem.pathLast) > -1
+ ) {
// filter type: ... queries
if (!results_others[fullId] !== undefined) {
index = row.normalizedName.indexOf(elem.pathLast);
}
}
lev = levenshtein(searchWord, elem.pathLast);
- if (lev > 0 && elem.pathLast.length > 2 && searchWord.indexOf(elem.pathLast) > -1)
- {
+ if (lev > 0 && elem.pathLast.length > 2 && searchWord.indexOf(elem.pathLast) > -1) {
if (elem.pathLast.length < 6) {
lev = 1;
} else {
}
const ret = createQueryResults(
- sortResults(results_in_args, true),
- sortResults(results_returned, true),
- sortResults(results_others, false),
+ sortResults(results_in_args, true, currentCrate),
+ sortResults(results_returned, true, currentCrate),
+ sortResults(results_others, false, currentCrate),
parsedQuery);
- handleAliases(ret, parsedQuery.original.replace(/"/g, ""), filterCrates);
+ handleAliases(ret, parsedQuery.original.replace(/"/g, ""), filterCrates, currentCrate);
if (parsedQuery.error !== null && ret.others.length !== 0) {
// It means some doc aliases were found so let's "remove" the error!
ret.query.error = null;
if (type === "mod") {
displayPath = path + "::";
- href = window.rootPath + path.replace(/::/g, "/") + "/" +
- name + "/index.html";
+ href = ROOT_PATH + path.replace(/::/g, "/") + "/" +
+ name + "/index.html";
} else if (type === "import") {
displayPath = item.path + "::";
- href = window.rootPath + item.path.replace(/::/g, "/") + "/index.html#reexport." + name;
+ href = ROOT_PATH + item.path.replace(/::/g, "/") + "/index.html#reexport." + name;
} else if (type === "primitive" || type === "keyword") {
displayPath = "";
- href = window.rootPath + path.replace(/::/g, "/") +
- "/" + type + "." + name + ".html";
+ href = ROOT_PATH + path.replace(/::/g, "/") +
+ "/" + type + "." + name + ".html";
} else if (type === "externcrate") {
displayPath = "";
- href = window.rootPath + name + "/index.html";
+ href = ROOT_PATH + name + "/index.html";
} else if (item.parent !== undefined) {
const myparent = item.parent;
let anchor = "#" + type + "." + name;
} else {
displayPath = path + "::" + myparent.name + "::";
}
- href = window.rootPath + path.replace(/::/g, "/") +
- "/" + pageType +
- "." + pageName +
- ".html" + anchor;
+ href = ROOT_PATH + path.replace(/::/g, "/") +
+ "/" + pageType +
+ "." + pageName +
+ ".html" + anchor;
} else {
displayPath = item.path + "::";
- href = window.rootPath + item.path.replace(/::/g, "/") +
- "/" + type + "." + name + ".html";
+ href = ROOT_PATH + item.path.replace(/::/g, "/") +
+ "/" + type + "." + name + ".html";
}
return [displayPath, href];
}
// By default, the search DOM element is "empty" (meaning it has no children not
// text content). Once a search has been run, it won't be empty, even if you press
// ESC or empty the search input (which also "cancels" the search).
- && (!search.firstChild || search.firstChild.innerText !== searchState.loadingText)))
- {
+ && (!search.firstChild || search.firstChild.innerText !== searchState.loadingText))
+ ) {
const elem = document.createElement("a");
elem.href = results.others[0].href;
removeClass(elem, "active");
let output = "<div id=\"search-settings\">" +
`<h1 class="search-results-title">Results for ${escape(results.query.userQuery)}` +
- `${typeFilter}</h1> in ${crates} </div>`;
+ `${typeFilter}</h1>${crates}</div>`;
if (results.query.error !== null) {
output += `<h3>Query parser error: "${results.query.error}".</h3>`;
output += "<div id=\"titles\">" +
let i = 0;
for (const elem of elems) {
const j = i;
- elem.onclick = () => { printTab(j); };
+ elem.onclick = () => printTab(j);
searchState.focusedByTab.push(null);
i += 1;
}
}
showResults(
- execQuery(query, searchWords, filterCrates),
+ execQuery(query, searchWords, filterCrates, window.currentCrate),
params.go_to_first,
filterCrates);
}
}
if (aliases) {
- ALIASES[crate] = {};
+ ALIASES[crate] = Object.create(null);
for (const alias_name in aliases) {
if (!hasOwnPropertyRustdoc(aliases, alias_name)) {
continue;
}
function registerSearchEvents() {
+ const params = searchState.getQueryStringParams();
+
+ // Populate search bar with query string search term when provided,
+ // but only if the input bar is empty. This avoid the obnoxious issue
+ // where you start trying to do a search, and the index loads, and
+ // suddenly your search is gone!
+ if (searchState.input.value === "") {
+ searchState.input.value = params.search || "";
+ }
+
const searchAfter500ms = () => {
searchState.clearInputTimeout();
if (searchState.input.value.length === 0) {
* @type {Array<string>}
*/
const searchWords = buildIndex(rawSearchIndex);
- registerSearchEvents();
-
- function runSearchIfNeeded() {
+ if (typeof window !== "undefined") {
+ registerSearchEvents();
// If there's a search term in the URL, execute the search now.
- if (searchState.getQueryStringParams().search) {
+ if (window.searchState.getQueryStringParams().search) {
search();
}
}
- runSearchIfNeeded();
-};
+ if (typeof exports !== "undefined") {
+ exports.initSearch = initSearch;
+ exports.execQuery = execQuery;
+ exports.parseQuery = parseQuery;
+ }
+ return searchWords;
+}
-if (window.searchIndex !== undefined) {
- initSearch(window.searchIndex);
+if (typeof window !== "undefined") {
+ window.initSearch = initSearch;
+ if (window.searchIndex !== undefined) {
+ initSearch(window.searchIndex);
+ }
+} else {
+ // Running in Node, not a browser. Run initSearch just to produce the
+ // exports.
+ initSearch({});
}
+
})();
// Local js definitions:
/* global getSettingValue, getVirtualKey, updateLocalStorage, updateSystemTheme */
-/* global addClass, removeClass, onEach, onEachLazy, NOT_DISPLAYED_ID */
-/* global MAIN_ID, getVar, getSettingsButton, switchDisplayedElement, getNotDisplayedElem */
+/* global addClass, removeClass, onEach, onEachLazy */
+/* global MAIN_ID, getVar, getSettingsButton */
"use strict";
];
// Then we build the DOM.
- const el = document.createElement("section");
+ const elementKind = isSettingsPage ? "section" : "div";
+ const innerHTML = `<div class="settings">${buildSettingsPageSections(settings)}</div>`;
+ const el = document.createElement(elementKind);
el.id = "settings";
- let innerHTML = `
- <div class="main-heading">
- <h1 class="fqn">
- <span class="in-band">Rustdoc settings</span>
- </h1>
- <span class="out-of-band">`;
-
- if (isSettingsPage) {
- innerHTML +=
- "<a id=\"back\" href=\"javascript:void(0)\" onclick=\"history.back();\">Back</a>";
- } else {
- innerHTML += "<a id=\"back\" href=\"javascript:void(0)\" " +
- "onclick=\"switchDisplayedElement(null);\">Back</a>";
- }
- innerHTML += `</span>
- </div>
- <div class="settings">${buildSettingsPageSections(settings)}</div>`;
-
el.innerHTML = innerHTML;
if (isSettingsPage) {
document.getElementById(MAIN_ID).appendChild(el);
} else {
- getNotDisplayedElem().appendChild(el);
+ el.setAttribute("tabindex", "-1");
+ getSettingsButton().appendChild(el);
}
return el;
}
const settingsMenu = buildSettingsPage();
+ function displaySettings() {
+ settingsMenu.style.display = "";
+ }
+
+ function elemIsInParent(elem, parent) {
+ while (elem && elem !== document.body) {
+ if (elem === parent) {
+ return true;
+ }
+ elem = elem.parentElement;
+ }
+ return false;
+ }
+
+ function blurHandler(event) {
+ const settingsButton = getSettingsButton();
+ if (!elemIsInParent(document.activeElement, settingsButton) &&
+ !elemIsInParent(event.relatedTarget, settingsButton)
+ ) {
+ window.hideSettings();
+ }
+ }
+
if (isSettingsPage) {
// We replace the existing "onclick" callback to do nothing if clicked.
getSettingsButton().onclick = function(event) {
} else {
// We replace the existing "onclick" callback.
const settingsButton = getSettingsButton();
+ const settingsMenu = document.getElementById("settings");
+ window.hideSettings = function() {
+ settingsMenu.style.display = "none";
+ };
settingsButton.onclick = function(event) {
+ if (elemIsInParent(event.target, settingsMenu)) {
+ return;
+ }
event.preventDefault();
- if (settingsMenu.parentElement.id === NOT_DISPLAYED_ID) {
- switchDisplayedElement(settingsMenu);
- } else {
+ if (settingsMenu.style.display !== "none") {
window.hideSettings();
+ } else {
+ displaySettings();
}
};
- window.hideSettings = function() {
- switchDisplayedElement(null);
- };
+ settingsButton.onblur = blurHandler;
+ settingsButton.querySelector("a").onblur = blurHandler;
+ onEachLazy(settingsMenu.querySelectorAll("input"), el => {
+ el.onblur = blurHandler;
+ });
+ settingsMenu.onblur = blurHandler;
}
// We now wait a bit for the web browser to end re-computing the DOM...
setEvents(settingsMenu);
// The setting menu is already displayed if we're on the settings page.
if (!isSettingsPage) {
- switchDisplayedElement(settingsMenu);
+ displaySettings();
}
removeClass(getSettingsButton(), "rotate");
}, 0);
reversed);
}
-// eslint-disable-next-line no-unused-vars
-function hasOwnPropertyRustdoc(obj, property) {
- return Object.prototype.hasOwnProperty.call(obj, property);
-}
-
function updateLocalStorage(name, value) {
try {
window.localStorage.setItem("rustdoc-" + name, value);
crate static SCRAPE_EXAMPLES_HELP_MD: &str = include_str!("static/scrape-examples-help.md");
-/// The file contents of `brush.svg`, the icon used for the theme-switch button.
-crate static BRUSH_SVG: &[u8] = include_bytes!("static/images/brush.svg");
-
/// The file contents of `wheel.svg`, the icon used for the settings button.
crate static WHEEL_SVG: &[u8] = include_bytes!("static/images/wheel.svg");
{%- endif -%}
</a> {#- -#}
<nav class="sub"> {#- -#}
- <div class="theme-picker hidden"> {#- -#}
- <button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu" title="themes"> {#- -#}
- <img width="22" height="22" alt="Pick another theme!" {# -#}
- src="{{static_root_path|safe}}brush{{page.resource_suffix}}.svg"> {#- -#}
- </button> {#- -#}
- <div id="theme-choices" role="menu"></div> {#- -#}
- </div> {#- -#}
<form class="search-form"> {#- -#}
<div class="search-container"> {#- -#}
<span></span> {#- This empty span is a hacky fix for Safari - See #93184 -#}
placeholder="Click or press ‘S’ to search, ‘?’ for more options…" {# -#}
type="search"> {#- -#}
<button type="button" id="help-button" title="help">?</button> {#- -#}
- <a id="settings-menu" href="{{page.root_path|safe}}settings.html" title="settings"> {#- -#}
- <img width="22" height="22" alt="Change settings" {# -#}
+ <div id="settings-menu" tabindex="-1">
+ <a href="{{page.root_path|safe}}settings.html" title="settings"> {#- -#}
+ <img width="22" height="22" alt="Change settings" {# -#}
src="{{static_root_path|safe}}wheel{{page.resource_suffix}}.svg"> {#- -#}
- </a> {#- -#}
+ </a> {#- -#}
+ </div>
</div> {#- -#}
</form> {#- -#}
</nav> {#- -#}
fn from_tcx(predicate: clean::WherePredicate, tcx: TyCtxt<'_>) -> Self {
use clean::WherePredicate::*;
match predicate {
- BoundPredicate { ty, bounds, .. } => WherePredicate::BoundPredicate {
+ BoundPredicate { ty, bounds, bound_params } => WherePredicate::BoundPredicate {
type_: ty.into_tcx(tcx),
bounds: bounds.into_iter().map(|x| x.into_tcx(tcx)).collect(),
- // FIXME: add `bound_params` to rustdoc-json-params?
+ generic_params: bound_params
+ .into_iter()
+ .map(|x| GenericParamDef {
+ name: x.0.to_string(),
+ kind: GenericParamDefKind::Lifetime { outlives: vec![] },
+ })
+ .collect(),
},
RegionPredicate { lifetime, bounds } => WherePredicate::RegionPredicate {
lifetime: lifetime.0.to_string(),
use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
use rustc_hir::Mutability;
use rustc_middle::ty::{DefIdTree, Ty, TyCtxt};
-use rustc_middle::{bug, span_bug, ty};
+use rustc_middle::{bug, ty};
use rustc_resolve::ParentScope;
use rustc_session::lint::Lint;
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{sym, Ident, Symbol};
-use rustc_span::{BytePos, DUMMY_SP};
+use rustc_span::BytePos;
use smallvec::{smallvec, SmallVec};
use std::borrow::Cow;
krate
}
-/// Top-level errors emitted by this pass.
-enum ErrorKind<'a> {
- Resolve(Box<ResolutionFailure<'a>>),
- AnchorFailure(AnchorFailure),
-}
-
-impl<'a> From<ResolutionFailure<'a>> for ErrorKind<'a> {
- fn from(err: ResolutionFailure<'a>) -> Self {
- ErrorKind::Resolve(box err)
- }
-}
-
#[derive(Copy, Clone, Debug, Hash)]
enum Res {
Def(DefKind, DefId),
}
}
- fn as_hir_res(self) -> Option<rustc_hir::def::Res> {
- match self {
- Res::Def(kind, id) => Some(rustc_hir::def::Res::Def(kind, id)),
- // FIXME: maybe this should handle the subset of PrimitiveType that fits into hir::PrimTy?
- Res::Primitive(_) => None,
- }
+ fn from_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Res {
+ Res::Def(tcx.def_kind(def_id), def_id)
}
/// Used for error reporting.
}
}
-/// A link failed to resolve.
-#[derive(Clone, Debug)]
+/// The link failed to resolve. [`resolution_failure`] should look to see if there's
+/// a more helpful error that can be given.
+#[derive(Debug)]
+struct UnresolvedPath<'a> {
+ /// Item on which the link is resolved, used for resolving `Self`.
+ item_id: ItemId,
+ /// The scope the link was resolved in.
+ module_id: DefId,
+ /// If part of the link resolved, this has the `Res`.
+ ///
+ /// In `[std::io::Error::x]`, `std::io::Error` would be a partial resolution.
+ partial_res: Option<Res>,
+ /// The remaining unresolved path segments.
+ ///
+ /// In `[std::io::Error::x]`, `x` would be unresolved.
+ unresolved: Cow<'a, str>,
+}
+
+#[derive(Debug)]
enum ResolutionFailure<'a> {
/// This resolved, but with the wrong namespace.
WrongNamespace {
/// even though `Result`'s actual namespace is [`Namespace::TypeNS`].
expected_ns: Namespace,
},
- /// The link failed to resolve. [`resolution_failure`] should look to see if there's
- /// a more helpful error that can be given.
- NotResolved {
- /// Item on which the link is resolved, used for resolving `Self`.
- item_id: ItemId,
- /// The scope the link was resolved in.
- module_id: DefId,
- /// If part of the link resolved, this has the `Res`.
- ///
- /// In `[std::io::Error::x]`, `std::io::Error` would be a partial resolution.
- partial_res: Option<Res>,
- /// The remaining unresolved path segments.
- ///
- /// In `[std::io::Error::x]`, `x` would be unresolved.
- unresolved: Cow<'a, str>,
- },
- /// This happens when rustdoc can't determine the parent scope for an item.
- /// It is always a bug in rustdoc.
- NoParentItem,
- /// This link has malformed generic parameters; e.g., the angle brackets are unbalanced.
- MalformedGenerics(MalformedGenerics),
- /// Used to communicate that this should be ignored, but shouldn't be reported to the user.
- ///
- /// This happens when there is no disambiguator and one of the namespaces
- /// failed to resolve.
- Dummy,
+ NotResolved(UnresolvedPath<'a>),
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Copy, Debug)]
enum MalformedGenerics {
/// This link has unbalanced angle brackets.
///
EmptyAngleBrackets,
}
-impl ResolutionFailure<'_> {
- /// This resolved fully (not just partially) but is erroneous for some other reason
- ///
- /// Returns the full resolution of the link, if present.
- fn full_res(&self) -> Option<Res> {
- match self {
- Self::WrongNamespace { res, expected_ns: _ } => Some(*res),
- _ => None,
- }
- }
-}
-
-#[derive(Clone, Copy)]
-enum AnchorFailure {
- /// User error: `[std#x#y]` is not valid
- MultipleAnchors,
- /// The anchor provided by the user conflicts with Rustdoc's generated anchor.
- ///
- /// This is an unfortunate state of affairs. Not every item that can be
- /// linked to has its own page; sometimes it is a subheading within a page,
- /// like for associated items. In those cases, rustdoc uses an anchor to
- /// link to the subheading. Since you can't have two anchors for the same
- /// link, Rustdoc disallows having a user-specified anchor.
- ///
- /// Most of the time this is fine, because you can just link to the page of
- /// the item if you want to provide your own anchor.
- RustdocAnchorConflict(Res),
-}
-
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
crate enum UrlFragment {
Item(ItemFragment),
VariantField,
}
-impl ItemFragment {
- /// Create a fragment for an associated item.
- #[instrument(level = "debug")]
- fn from_assoc_item(item: &ty::AssocItem) -> Self {
- let def_id = item.def_id;
- match item.kind {
- ty::AssocKind::Fn => {
- if item.defaultness.has_value() {
- ItemFragment(FragmentKind::Method, def_id)
+impl FragmentKind {
+ fn from_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> FragmentKind {
+ match tcx.def_kind(def_id) {
+ DefKind::AssocFn => {
+ if tcx.associated_item(def_id).defaultness.has_value() {
+ FragmentKind::Method
+ } else {
+ FragmentKind::TyMethod
+ }
+ }
+ DefKind::AssocConst => FragmentKind::AssociatedConstant,
+ DefKind::AssocTy => FragmentKind::AssociatedType,
+ DefKind::Variant => FragmentKind::Variant,
+ DefKind::Field => {
+ if tcx.def_kind(tcx.parent(def_id)) == DefKind::Variant {
+ FragmentKind::VariantField
} else {
- ItemFragment(FragmentKind::TyMethod, def_id)
+ FragmentKind::StructField
}
}
- ty::AssocKind::Const => ItemFragment(FragmentKind::AssociatedConstant, def_id),
- ty::AssocKind::Type => ItemFragment(FragmentKind::AssociatedType, def_id),
+ kind => bug!("unexpected associated item kind: {:?}", kind),
}
}
+}
+impl ItemFragment {
/// Render the fragment, including the leading `#`.
crate fn render(&self, s: &mut String, tcx: TyCtxt<'_>) -> std::fmt::Result {
write!(s, "#")?;
path_str: &'path str,
item_id: ItemId,
module_id: DefId,
- ) -> Result<(Res, Option<ItemFragment>), ErrorKind<'path>> {
+ ) -> Result<(Res, DefId), UnresolvedPath<'path>> {
let tcx = self.cx.tcx;
- let no_res = || ResolutionFailure::NotResolved {
+ let no_res = || UnresolvedPath {
item_id,
module_id,
partial_res: None,
let ty_res = self.resolve_path(&path, TypeNS, item_id, module_id).ok_or_else(no_res)?;
match ty_res {
- Res::Def(DefKind::Enum, did) => {
- if tcx
- .inherent_impls(did)
- .iter()
- .flat_map(|imp| tcx.associated_items(*imp).in_definition_order())
- .any(|item| item.name == variant_name)
- {
- // This is just to let `fold_item` know that this shouldn't be considered;
- // it's a bug for the error to make it to the user
- return Err(ResolutionFailure::Dummy.into());
- }
- match tcx.type_of(did).kind() {
- ty::Adt(def, _) if def.is_enum() => {
- if let Some(field) = def.all_fields().find(|f| f.name == variant_field_name)
- {
- Ok((ty_res, Some(ItemFragment(FragmentKind::VariantField, field.did))))
- } else {
- Err(ResolutionFailure::NotResolved {
- item_id,
- module_id,
- partial_res: Some(Res::Def(DefKind::Enum, def.did())),
- unresolved: variant_field_name.to_string().into(),
- }
- .into())
- }
+ Res::Def(DefKind::Enum, did) => match tcx.type_of(did).kind() {
+ ty::Adt(def, _) if def.is_enum() => {
+ if let Some(field) = def.all_fields().find(|f| f.name == variant_field_name) {
+ Ok((ty_res, field.did))
+ } else {
+ Err(UnresolvedPath {
+ item_id,
+ module_id,
+ partial_res: Some(Res::Def(DefKind::Enum, def.did())),
+ unresolved: variant_field_name.to_string().into(),
+ })
}
- _ => unreachable!(),
}
- }
- _ => Err(ResolutionFailure::NotResolved {
+ _ => unreachable!(),
+ },
+ _ => Err(UnresolvedPath {
item_id,
module_id,
partial_res: Some(ty_res),
unresolved: variant_name.to_string().into(),
- }
- .into()),
+ }),
}
}
prim_ty: PrimitiveType,
ns: Namespace,
item_name: Symbol,
- ) -> Option<(Res, ItemFragment)> {
+ ) -> Option<(Res, DefId)> {
let tcx = self.cx.tcx;
prim_ty.impls(tcx).find_map(|impl_| {
tcx.associated_items(impl_)
.find_by_name_and_namespace(tcx, Ident::with_dummy_span(item_name), ns, impl_)
- .map(|item| {
- let fragment = ItemFragment::from_assoc_item(item);
- (Res::Primitive(prim_ty), fragment)
- })
- })
- }
-
- /// Resolves a string as a macro.
- ///
- /// FIXME(jynelson): Can this be unified with `resolve()`?
- fn resolve_macro(
- &self,
- path_str: &'a str,
- item_id: ItemId,
- module_id: DefId,
- ) -> Result<Res, ResolutionFailure<'a>> {
- self.resolve_path(path_str, MacroNS, item_id, module_id).ok_or_else(|| {
- ResolutionFailure::NotResolved {
- item_id,
- module_id,
- partial_res: None,
- unresolved: path_str.into(),
- }
+ .map(|item| (Res::Primitive(prim_ty), item.def_id))
})
}
})
}
- /// HACK: Try to search the macro name in the list of all `macro_rules` items in the crate.
- /// Used when nothing else works, may often give an incorrect result.
- fn resolve_macro_rules(&self, path_str: &str, ns: Namespace) -> Option<Res> {
- if ns != MacroNS {
- return None;
- }
-
- self.cx
- .resolver_caches
- .all_macro_rules
- .get(&Symbol::intern(path_str))
- .copied()
- .and_then(|res| res.try_into().ok())
- }
-
/// Convenience wrapper around `resolve_rustdoc_path`.
///
/// This also handles resolving `true` and `false` as booleans.
})
})
.and_then(|res| res.try_into().ok())
- .or_else(|| resolve_primitive(path_str, ns))
- .or_else(|| self.resolve_macro_rules(path_str, ns));
+ .or_else(|| resolve_primitive(path_str, ns));
debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns);
result
}
ns: Namespace,
item_id: ItemId,
module_id: DefId,
- user_fragment: &Option<String>,
- ) -> Result<(Res, Option<UrlFragment>), ErrorKind<'path>> {
- let (res, rustdoc_fragment) = self.resolve_inner(path_str, ns, item_id, module_id)?;
- let chosen_fragment = match (user_fragment, rustdoc_fragment) {
- (Some(_), Some(r_frag)) => {
- let diag_res = match r_frag {
- ItemFragment(_, did) => Res::Def(self.cx.tcx.def_kind(did), did),
- };
- let failure = AnchorFailure::RustdocAnchorConflict(diag_res);
- return Err(ErrorKind::AnchorFailure(failure));
- }
- (Some(u_frag), None) => Some(UrlFragment::UserWritten(u_frag.clone())),
- (None, Some(r_frag)) => Some(UrlFragment::Item(r_frag)),
- (None, None) => None,
- };
- Ok((res, chosen_fragment))
- }
-
- fn resolve_inner<'path>(
- &mut self,
- path_str: &'path str,
- ns: Namespace,
- item_id: ItemId,
- module_id: DefId,
- ) -> Result<(Res, Option<ItemFragment>), ErrorKind<'path>> {
+ ) -> Result<(Res, Option<DefId>), UnresolvedPath<'path>> {
if let Some(res) = self.resolve_path(path_str, ns, item_id, module_id) {
- match res {
- // FIXME(#76467): make this fallthrough to lookup the associated
- // item a separate function.
- Res::Def(DefKind::AssocFn | DefKind::AssocConst, _) => assert_eq!(ns, ValueNS),
- Res::Def(DefKind::AssocTy, _) => assert_eq!(ns, TypeNS),
- Res::Def(DefKind::Variant, _) => {
- return handle_variant(self.cx, res);
- }
- // Not a trait item; just return what we found.
- _ => return Ok((res, None)),
- }
+ return Ok(match res {
+ Res::Def(
+ DefKind::AssocFn | DefKind::AssocConst | DefKind::AssocTy | DefKind::Variant,
+ def_id,
+ ) => (Res::from_def_id(self.cx.tcx, self.cx.tcx.parent(def_id)), Some(def_id)),
+ _ => ((res, None)),
+ });
+ } else if ns == MacroNS {
+ return Err(UnresolvedPath {
+ item_id,
+ module_id,
+ partial_res: None,
+ unresolved: path_str.into(),
+ });
}
// Try looking for methods and associated items.
// So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
.ok_or_else(|| {
debug!("found no `::`, assumming {} was correctly not in scope", item_name);
- ResolutionFailure::NotResolved {
+ UnresolvedPath {
item_id,
module_id,
partial_res: None,
resolve_primitive(&path_root, TypeNS)
.or_else(|| self.resolve_path(&path_root, TypeNS, item_id, module_id))
.and_then(|ty_res| {
- let (res, fragment) =
- self.resolve_associated_item(ty_res, item_name, ns, module_id)?;
-
- Some(Ok((res, Some(fragment))))
+ self.resolve_associated_item(ty_res, item_name, ns, module_id).map(Ok)
})
.unwrap_or_else(|| {
if ns == Namespace::ValueNS {
self.variant_field(path_str, item_id, module_id)
} else {
- Err(ResolutionFailure::NotResolved {
+ Err(UnresolvedPath {
item_id,
module_id,
partial_res: None,
unresolved: path_root.into(),
- }
- .into())
+ })
}
})
+ .map(|(res, def_id)| (res, Some(def_id)))
}
/// Convert a DefId to a Res, where possible.
ty::FnPtr(_) => Res::Primitive(Fn),
ty::Never => Res::Primitive(Never),
ty::Adt(ty::AdtDef(Interned(&ty::AdtDefData { did, .. }, _)), _) | ty::Foreign(did) => {
- Res::Def(self.cx.tcx.def_kind(did), did)
+ Res::from_def_id(self.cx.tcx, did)
}
ty::Projection(_)
| ty::Closure(..)
item_name: Symbol,
ns: Namespace,
module_id: DefId,
- ) -> Option<(Res, ItemFragment)> {
+ ) -> Option<(Res, DefId)> {
let tcx = self.cx.tcx;
match root_res {
Res::Primitive(prim) => {
self.resolve_primitive_associated_item(prim, ns, item_name).or_else(|| {
- let assoc_item = self
- .primitive_type_to_ty(prim)
+ self.primitive_type_to_ty(prim)
.map(|ty| {
resolve_associated_trait_item(ty, module_id, item_name, ns, self.cx)
})
- .flatten();
-
- assoc_item.map(|item| {
- let fragment = ItemFragment::from_assoc_item(&item);
- (root_res, fragment)
- })
+ .flatten()
+ .map(|item| (root_res, item.def_id))
})
}
Res::Def(DefKind::TyAlias, did) => {
ty::Adt(adt_def, _) => {
for variant in adt_def.variants() {
if variant.name == item_name {
- return Some((
- root_res,
- ItemFragment(FragmentKind::Variant, variant.def_id),
- ));
+ return Some((root_res, variant.def_id));
}
}
}
debug!("got associated item {:?}", assoc_item);
if let Some(item) = assoc_item {
- let fragment = ItemFragment::from_assoc_item(&item);
- return Some((root_res, fragment));
+ return Some((root_res, item.def_id));
}
if ns != Namespace::ValueNS {
};
let field =
def.non_enum_variant().fields.iter().find(|item| item.name == item_name)?;
- Some((root_res, ItemFragment(FragmentKind::StructField, field.did)))
+ Some((root_res, field.did))
}
Res::Def(DefKind::Trait, did) => tcx
.associated_items(did)
.find_by_name_and_namespace(tcx, Ident::with_dummy_span(item_name), ns, did)
.map(|item| {
- let fragment = ItemFragment::from_assoc_item(item);
let res = Res::Def(item.kind.as_def_kind(), item.def_id);
- (res, fragment)
+ (res, item.def_id)
}),
_ => None,
}
}
+}
- /// Used for reporting better errors.
- ///
- /// Returns whether the link resolved 'fully' in another namespace.
- /// 'fully' here means that all parts of the link resolved, not just some path segments.
- /// This returns the `Res` even if it was erroneous for some reason
- /// (such as having invalid URL fragments or being in the wrong namespace).
- fn check_full_res(
- &mut self,
- ns: Namespace,
- path_str: &str,
- item_id: ItemId,
- module_id: DefId,
- extra_fragment: &Option<String>,
- ) -> Option<Res> {
- // resolve() can't be used for macro namespace
- let result = match ns {
- Namespace::MacroNS => self
- .resolve_macro(path_str, item_id, module_id)
- .map(|res| (res, None))
- .map_err(ErrorKind::from),
- Namespace::TypeNS | Namespace::ValueNS => {
- self.resolve(path_str, ns, item_id, module_id, extra_fragment)
- }
- };
-
- let res = match result {
- Ok((res, frag)) => {
- if let Some(UrlFragment::Item(ItemFragment(_, id))) = frag {
- Some(Res::Def(self.cx.tcx.def_kind(id), id))
- } else {
- Some(res)
- }
- }
- Err(ErrorKind::Resolve(box kind)) => kind.full_res(),
- Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res))) => Some(res),
- Err(ErrorKind::AnchorFailure(AnchorFailure::MultipleAnchors)) => None,
- };
- res
- }
+fn full_res(tcx: TyCtxt<'_>, (base, assoc_item): (Res, Option<DefId>)) -> Res {
+ assoc_item.map_or(base, |def_id| Res::from_def_id(tcx, def_id))
}
/// Look to see if a resolved item has an associated item named `item_name`.
}
enum PreprocessingError {
- Anchor(AnchorFailure),
+ /// User error: `[std#x#y]` is not valid
+ MultipleAnchors,
Disambiguator(Range<usize>, String),
- Resolution(ResolutionFailure<'static>, String, Option<Disambiguator>),
+ MalformedGenerics(MalformedGenerics, String),
}
-impl From<AnchorFailure> for PreprocessingError {
- fn from(err: AnchorFailure) -> Self {
- Self::Anchor(err)
+impl PreprocessingError {
+ fn report(&self, cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>) {
+ match self {
+ PreprocessingError::MultipleAnchors => report_multiple_anchors(cx, diag_info),
+ PreprocessingError::Disambiguator(range, msg) => {
+ disambiguator_error(cx, diag_info, range.clone(), msg)
+ }
+ PreprocessingError::MalformedGenerics(err, path_str) => {
+ report_malformed_generics(cx, diag_info, *err, path_str)
+ }
+ }
}
}
let extra_fragment = parts.next();
if parts.next().is_some() {
// A valid link can't have multiple #'s
- return Some(Err(AnchorFailure::MultipleAnchors.into()));
+ return Some(Err(PreprocessingError::MultipleAnchors));
}
// Parse and strip the disambiguator from the link, if present.
let path_str = if path_str.contains(['<', '>'].as_slice()) {
match strip_generics_from_path(path_str) {
Ok(path) => path,
- Err(err_kind) => {
+ Err(err) => {
debug!("link has malformed generics: {}", path_str);
- return Some(Err(PreprocessingError::Resolution(
- err_kind,
- path_str.to_owned(),
- disambiguator,
- )));
+ return Some(Err(PreprocessingError::MalformedGenerics(err, path_str.to_owned())));
}
}
} else {
link_range: ori_link.range.clone(),
};
- let PreprocessingInfo { path_str, disambiguator, extra_fragment, link_text } = match pp_link
- {
- Ok(x) => x,
- Err(err) => {
- match err {
- PreprocessingError::Anchor(err) => anchor_failure(self.cx, diag_info, *err),
- PreprocessingError::Disambiguator(range, msg) => {
- disambiguator_error(self.cx, diag_info, range.clone(), msg)
- }
- PreprocessingError::Resolution(err, path_str, disambiguator) => {
- resolution_failure(
- self,
- diag_info,
- path_str,
- *disambiguator,
- smallvec![err.clone()],
- );
- }
- }
- return None;
- }
- };
+ let PreprocessingInfo { path_str, disambiguator, extra_fragment, link_text } =
+ pp_link.as_ref().map_err(|err| err.report(self.cx, diag_info.clone())).ok()?;
let disambiguator = *disambiguator;
- let inner_docs = item.inner_docs(self.cx.tcx);
-
// In order to correctly resolve intra-doc links we need to
// pick a base AST node to work from. If the documentation for
// this module came from an inner comment (//!) then we anchor
// we've already pushed this node onto the resolution stack but
// for outer comments we explicitly try and resolve against the
// parent_node first.
+ let inner_docs = item.inner_docs(self.cx.tcx);
let base_node =
if item.is_mod() && inner_docs { self.mod_ids.last().copied() } else { parent_node };
-
- let Some(module_id) = base_node else {
- // This is a bug.
- debug!("attempting to resolve item without parent module: {}", path_str);
- resolution_failure(
- self,
- diag_info,
- path_str,
- disambiguator,
- smallvec![ResolutionFailure::NoParentItem],
- );
- return None;
- };
+ let module_id = base_node.expect("doc link without parent module");
let (mut res, fragment) = self.resolve_with_disambiguator_cached(
ResolutionInfo {
}
}
- let res = self.resolve_with_disambiguator(&key, diag);
+ let res = self.resolve_with_disambiguator(&key, diag.clone()).and_then(|(res, def_id)| {
+ let fragment = match (&key.extra_fragment, def_id) {
+ (Some(_), Some(def_id)) => {
+ report_anchor_conflict(self.cx, diag, Res::from_def_id(self.cx.tcx, def_id));
+ return None;
+ }
+ (Some(u_frag), None) => Some(UrlFragment::UserWritten(u_frag.clone())),
+ (None, Some(def_id)) => Some(UrlFragment::Item(ItemFragment(
+ FragmentKind::from_def_id(self.cx.tcx, def_id),
+ def_id,
+ ))),
+ (None, None) => None,
+ };
+ Some((res, fragment))
+ });
// Cache only if resolved successfully - don't silence duplicate errors
if let Some(res) = res {
&mut self,
key: &ResolutionInfo,
diag: DiagnosticInfo<'_>,
- ) -> Option<(Res, Option<UrlFragment>)> {
+ ) -> Option<(Res, Option<DefId>)> {
let disambiguator = key.dis;
let path_str = &key.path_str;
let item_id = key.item_id;
let base_node = key.module_id;
- let extra_fragment = &key.extra_fragment;
match disambiguator.map(Disambiguator::ns) {
- Some(expected_ns @ (ValueNS | TypeNS)) => {
- match self.resolve(path_str, expected_ns, item_id, base_node, extra_fragment) {
+ Some(expected_ns) => {
+ match self.resolve(path_str, expected_ns, item_id, base_node) {
Ok(res) => Some(res),
- Err(ErrorKind::Resolve(box mut kind)) => {
+ Err(err) => {
// We only looked in one namespace. Try to give a better error if possible.
- if kind.full_res().is_none() {
- let other_ns = if expected_ns == ValueNS { TypeNS } else { ValueNS };
- // FIXME: really it should be `resolution_failure` that does this, not `resolve_with_disambiguator`
- // See https://github.com/rust-lang/rust/pull/76955#discussion_r493953382 for a good approach
- for new_ns in [other_ns, MacroNS] {
- if let Some(res) = self.check_full_res(
- new_ns,
- path_str,
- item_id,
- base_node,
- extra_fragment,
- ) {
- kind = ResolutionFailure::WrongNamespace { res, expected_ns };
+ // FIXME: really it should be `resolution_failure` that does this, not `resolve_with_disambiguator`.
+ // See https://github.com/rust-lang/rust/pull/76955#discussion_r493953382 for a good approach.
+ let mut err = ResolutionFailure::NotResolved(err);
+ for other_ns in [TypeNS, ValueNS, MacroNS] {
+ if other_ns != expected_ns {
+ if let Ok(res) =
+ self.resolve(path_str, other_ns, item_id, base_node)
+ {
+ err = ResolutionFailure::WrongNamespace {
+ res: full_res(self.cx.tcx, res),
+ expected_ns,
+ };
break;
}
}
}
- resolution_failure(self, diag, path_str, disambiguator, smallvec![kind]);
- // This could just be a normal link or a broken link
- // we could potentially check if something is
- // "intra-doc-link-like" and warn in that case.
- None
- }
- Err(ErrorKind::AnchorFailure(msg)) => {
- anchor_failure(self.cx, diag, msg);
- None
+ resolution_failure(self, diag, path_str, disambiguator, smallvec![err])
}
}
}
None => {
// Try everything!
- let mut candidates = PerNS {
- macro_ns: self
- .resolve_macro(path_str, item_id, base_node)
- .map(|res| (res, extra_fragment.clone().map(UrlFragment::UserWritten))),
- type_ns: match self.resolve(
- path_str,
- TypeNS,
- item_id,
- base_node,
- extra_fragment,
- ) {
- Ok(res) => {
- debug!("got res in TypeNS: {:?}", res);
- Ok(res)
- }
- Err(ErrorKind::AnchorFailure(msg)) => {
- anchor_failure(self.cx, diag, msg);
- return None;
- }
- Err(ErrorKind::Resolve(box kind)) => Err(kind),
- },
- value_ns: match self.resolve(
- path_str,
- ValueNS,
- item_id,
- base_node,
- extra_fragment,
- ) {
- Ok(res) => Ok(res),
- Err(ErrorKind::AnchorFailure(msg)) => {
- anchor_failure(self.cx, diag, msg);
- return None;
- }
- Err(ErrorKind::Resolve(box kind)) => Err(kind),
- }
- .and_then(|(res, fragment)| {
- // Constructors are picked up in the type namespace.
+ let mut candidate = |ns| {
+ self.resolve(path_str, ns, item_id, base_node)
+ .map_err(ResolutionFailure::NotResolved)
+ };
+
+ let candidates = PerNS {
+ macro_ns: candidate(MacroNS),
+ type_ns: candidate(TypeNS),
+ value_ns: candidate(ValueNS).and_then(|(res, def_id)| {
match res {
+ // Constructors are picked up in the type namespace.
Res::Def(DefKind::Ctor(..), _) => {
Err(ResolutionFailure::WrongNamespace { res, expected_ns: TypeNS })
}
- _ => {
- match (fragment, extra_fragment.clone()) {
- (Some(fragment), Some(_)) => {
- // Shouldn't happen but who knows?
- Ok((res, Some(fragment)))
- }
- (fragment, None) => Ok((res, fragment)),
- (None, fragment) => {
- Ok((res, fragment.map(UrlFragment::UserWritten)))
- }
- }
- }
+ _ => Ok((res, def_id)),
}
}),
};
let len = candidates.iter().filter(|res| res.is_ok()).count();
if len == 0 {
- resolution_failure(
+ return resolution_failure(
self,
diag,
path_str,
disambiguator,
candidates.into_iter().filter_map(|res| res.err()).collect(),
);
- // this could just be a normal link
- return None;
}
if len == 1 {
} else if len == 2 && is_derive_trait_collision(&candidates) {
Some(candidates.type_ns.unwrap())
} else {
- if is_derive_trait_collision(&candidates) {
- candidates.macro_ns = Err(ResolutionFailure::Dummy);
- }
+ let ignore_macro = is_derive_trait_collision(&candidates);
// If we're reporting an ambiguity, don't mention the namespaces that failed
- let candidates = candidates.map(|candidate| candidate.ok().map(|(res, _)| res));
+ let mut candidates =
+ candidates.map(|candidate| candidate.ok().map(|(res, _)| res));
+ if ignore_macro {
+ candidates.macro_ns = None;
+ }
ambiguity_error(self.cx, diag, path_str, candidates.present_items().collect());
None
}
}
- Some(MacroNS) => {
- match self.resolve_macro(path_str, item_id, base_node) {
- Ok(res) => Some((res, extra_fragment.clone().map(UrlFragment::UserWritten))),
- Err(mut kind) => {
- // `resolve_macro` only looks in the macro namespace. Try to give a better error if possible.
- for ns in [TypeNS, ValueNS] {
- if let Some(res) = self.check_full_res(
- ns,
- path_str,
- item_id,
- base_node,
- extra_fragment,
- ) {
- kind =
- ResolutionFailure::WrongNamespace { res, expected_ns: MacroNS };
- break;
- }
- }
- resolution_failure(self, diag, path_str, disambiguator, smallvec![kind]);
- None
- }
- }
- }
}
}
}
path_str: &str,
disambiguator: Option<Disambiguator>,
kinds: SmallVec<[ResolutionFailure<'_>; 3]>,
-) {
+) -> Option<(Res, Option<DefId>)> {
let tcx = collector.cx.tcx;
+ let mut recovered_res = None;
report_diagnostic(
tcx,
BROKEN_INTRA_DOC_LINKS,
}
variants_seen.push(variant);
- if let ResolutionFailure::NotResolved {
+ if let ResolutionFailure::NotResolved(UnresolvedPath {
item_id,
module_id,
partial_res,
unresolved,
- } = &mut failure
+ }) = &mut failure
{
use DefKind::*;
};
name = start;
for ns in [TypeNS, ValueNS, MacroNS] {
- if let Some(res) =
- collector.check_full_res(ns, start, item_id, module_id, &None)
- {
+ if let Ok(res) = collector.resolve(start, ns, item_id, module_id) {
debug!("found partial_res={:?}", res);
- *partial_res = Some(res);
+ *partial_res = Some(full_res(collector.cx.tcx, res));
*unresolved = end.into();
break 'outer;
}
diag.note(¬e);
}
- // If the link has `::` in it, assume it was meant to be an intra-doc link.
- // Otherwise, the `[]` might be unrelated.
- // FIXME: don't show this for autolinks (`<>`), `()` style links, or reference links
if !path_str.contains("::") {
- diag.help(r#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#);
+ if disambiguator.map_or(true, |d| d.ns() == MacroNS)
+ && let Some(&res) = collector.cx.resolver_caches.all_macro_rules
+ .get(&Symbol::intern(path_str))
+ {
+ diag.note(format!(
+ "`macro_rules` named `{path_str}` exists in this crate, \
+ but it is not in scope at this link's location"
+ ));
+ recovered_res = res.try_into().ok().map(|res| (res, None));
+ } else {
+ // If the link has `::` in it, assume it was meant to be an
+ // intra-doc link. Otherwise, the `[]` might be unrelated.
+ diag.help("to escape `[` and `]` characters, \
+ add '\\' before them like `\\[` or `\\]`");
+ }
}
continue;
}
let note = match failure {
ResolutionFailure::NotResolved { .. } => unreachable!("handled above"),
- ResolutionFailure::Dummy => continue,
ResolutionFailure::WrongNamespace { res, expected_ns } => {
suggest_disambiguator(res, diag, path_str, diag_info.ori_link, sp);
expected_ns.descr()
)
}
- ResolutionFailure::NoParentItem => {
- // FIXME(eddyb) this doesn't belong here, whatever made
- // the `ResolutionFailure::NoParentItem` should emit an
- // immediate or delayed `span_bug` about the issue.
- tcx.sess.delay_span_bug(
- sp.unwrap_or(DUMMY_SP),
- "intra-doc link missing parent item",
- );
-
- "BUG: all intra-doc links should have a parent item".to_owned()
- }
- ResolutionFailure::MalformedGenerics(variant) => match variant {
- MalformedGenerics::UnbalancedAngleBrackets => {
- String::from("unbalanced angle brackets")
- }
- MalformedGenerics::MissingType => {
- String::from("missing type for generic parameters")
- }
- MalformedGenerics::HasFullyQualifiedSyntax => {
- diag.note("see https://github.com/rust-lang/rust/issues/74563 for more information");
- String::from("fully-qualified syntax is unsupported")
- }
- MalformedGenerics::InvalidPathSeparator => {
- String::from("has invalid path separator")
- }
- MalformedGenerics::TooManyAngleBrackets => {
- String::from("too many angle brackets")
- }
- MalformedGenerics::EmptyAngleBrackets => {
- String::from("empty angle brackets")
- }
- },
};
if let Some(span) = sp {
diag.span_label(span, ¬e);
}
},
);
+
+ recovered_res
}
-/// Report an anchor failure.
-fn anchor_failure(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>, failure: AnchorFailure) {
- let (msg, anchor_idx) = match failure {
- AnchorFailure::MultipleAnchors => {
- (format!("`{}` contains multiple anchors", diag_info.ori_link), 1)
- }
- AnchorFailure::RustdocAnchorConflict(res) => (
- format!(
- "`{}` contains an anchor, but links to {kind}s are already anchored",
- diag_info.ori_link,
- kind = res.descr(),
- ),
- 0,
- ),
- };
+fn report_multiple_anchors(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>) {
+ let msg = format!("`{}` contains multiple anchors", diag_info.ori_link);
+ anchor_failure(cx, diag_info, &msg, 1)
+}
+fn report_anchor_conflict(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>, res: Res) {
+ let (link, kind) = (diag_info.ori_link, res.descr());
+ let msg = format!("`{link}` contains an anchor, but links to {kind}s are already anchored");
+ anchor_failure(cx, diag_info, &msg, 0)
+}
+
+/// Report an anchor failure.
+fn anchor_failure(
+ cx: &DocContext<'_>,
+ diag_info: DiagnosticInfo<'_>,
+ msg: &str,
+ anchor_idx: usize,
+) {
report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, &msg, &diag_info, |diag, sp| {
if let Some(mut sp) = sp {
if let Some((fragment_offset, _)) =
}
diag.span_label(sp, "invalid anchor");
}
- if let AnchorFailure::RustdocAnchorConflict(Res::Primitive(_)) = failure {
- if let Some(sp) = sp {
- span_bug!(sp, "anchors should be allowed now");
- } else {
- bug!("anchors should be allowed now");
- }
- }
});
}
});
}
+fn report_malformed_generics(
+ cx: &DocContext<'_>,
+ diag_info: DiagnosticInfo<'_>,
+ err: MalformedGenerics,
+ path_str: &str,
+) {
+ report_diagnostic(
+ cx.tcx,
+ BROKEN_INTRA_DOC_LINKS,
+ &format!("unresolved link to `{}`", path_str),
+ &diag_info,
+ |diag, sp| {
+ let note = match err {
+ MalformedGenerics::UnbalancedAngleBrackets => "unbalanced angle brackets",
+ MalformedGenerics::MissingType => "missing type for generic parameters",
+ MalformedGenerics::HasFullyQualifiedSyntax => {
+ diag.note(
+ "see https://github.com/rust-lang/rust/issues/74563 for more information",
+ );
+ "fully-qualified syntax is unsupported"
+ }
+ MalformedGenerics::InvalidPathSeparator => "has invalid path separator",
+ MalformedGenerics::TooManyAngleBrackets => "too many angle brackets",
+ MalformedGenerics::EmptyAngleBrackets => "empty angle brackets",
+ };
+ if let Some(span) = sp {
+ diag.span_label(span, note);
+ } else {
+ diag.note(note);
+ }
+ },
+ );
+}
+
/// Report an ambiguity error, where there were multiple possible resolutions.
fn ambiguity_error(
cx: &DocContext<'_>,
});
}
-/// Given an enum variant's res, return the res of its enum and the associated fragment.
-fn handle_variant(
- cx: &DocContext<'_>,
- res: Res,
-) -> Result<(Res, Option<ItemFragment>), ErrorKind<'static>> {
- let parent = cx.tcx.parent(res.def_id(cx.tcx));
- let parent_def = Res::Def(DefKind::Enum, parent);
- let variant = cx.tcx.expect_variant_res(res.as_hir_res().unwrap());
- Ok((parent_def, Some(ItemFragment(FragmentKind::Variant, variant.def_id))))
-}
-
/// Resolve a primitive type or value.
fn resolve_primitive(path_str: &str, ns: Namespace) -> Option<Res> {
if ns != TypeNS {
Some(Res::Primitive(prim))
}
-fn strip_generics_from_path(path_str: &str) -> Result<String, ResolutionFailure<'static>> {
+fn strip_generics_from_path(path_str: &str) -> Result<String, MalformedGenerics> {
let mut stripped_segments = vec![];
let mut path = path_str.chars().peekable();
let mut segment = Vec::new();
stripped_segments.push(stripped_segment);
}
} else {
- return Err(ResolutionFailure::MalformedGenerics(
- MalformedGenerics::InvalidPathSeparator,
- ));
+ return Err(MalformedGenerics::InvalidPathSeparator);
}
}
'<' => {
match path.next() {
Some('<') => {
- return Err(ResolutionFailure::MalformedGenerics(
- MalformedGenerics::TooManyAngleBrackets,
- ));
+ return Err(MalformedGenerics::TooManyAngleBrackets);
}
Some('>') => {
- return Err(ResolutionFailure::MalformedGenerics(
- MalformedGenerics::EmptyAngleBrackets,
- ));
+ return Err(MalformedGenerics::EmptyAngleBrackets);
}
Some(chr) => {
segment.push(chr);
let stripped_path = stripped_segments.join("::");
- if !stripped_path.is_empty() {
- Ok(stripped_path)
- } else {
- Err(ResolutionFailure::MalformedGenerics(MalformedGenerics::MissingType))
- }
+ if !stripped_path.is_empty() { Ok(stripped_path) } else { Err(MalformedGenerics::MissingType) }
}
-fn strip_generics_from_path_segment(
- segment: Vec<char>,
-) -> Result<String, ResolutionFailure<'static>> {
+fn strip_generics_from_path_segment(segment: Vec<char>) -> Result<String, MalformedGenerics> {
let mut stripped_segment = String::new();
let mut param_depth = 0;
if latest_generics_chunk.contains(" as ") {
// The segment tries to use fully-qualified syntax, which is currently unsupported.
// Give a helpful error message instead of completely ignoring the angle brackets.
- return Err(ResolutionFailure::MalformedGenerics(
- MalformedGenerics::HasFullyQualifiedSyntax,
- ));
+ return Err(MalformedGenerics::HasFullyQualifiedSyntax);
}
} else {
if param_depth == 0 {
Ok(stripped_segment)
} else {
// The segment has unbalanced angle brackets, e.g. `Vec<T` or `Vec<T>>`
- Err(ResolutionFailure::MalformedGenerics(MalformedGenerics::UnbalancedAngleBrackets))
+ Err(MalformedGenerics::UnbalancedAngleBrackets)
}
}
traits_in_scope: Default::default(),
all_traits: Default::default(),
all_trait_impls: Default::default(),
+ all_macro_rules: Default::default(),
document_private_items,
};
traits_in_scope: link_resolver.traits_in_scope,
all_traits: Some(link_resolver.all_traits),
all_trait_impls: Some(link_resolver.all_trait_impls),
- all_macro_rules: link_resolver.resolver.take_all_macro_rules(),
+ all_macro_rules: link_resolver.all_macro_rules,
}
}
traits_in_scope: DefIdMap<Vec<TraitCandidate>>,
all_traits: Vec<DefId>,
all_trait_impls: Vec<DefId>,
+ all_macro_rules: FxHashMap<Symbol, Res<ast::NodeId>>,
document_private_items: bool,
}
// using privacy, private traits and impls from other crates are never documented in
// the current crate, and links in their doc comments are not resolved.
for &def_id in &all_traits {
- if self.resolver.cstore().visibility_untracked(def_id) == Visibility::Public {
+ if self.resolver.cstore().visibility_untracked(def_id).is_public() {
self.resolve_doc_links_extern_impl(def_id, false);
}
}
for &(trait_def_id, impl_def_id, simplified_self_ty) in &all_trait_impls {
- if self.resolver.cstore().visibility_untracked(trait_def_id)
- == Visibility::Public
+ if self.resolver.cstore().visibility_untracked(trait_def_id).is_public()
&& simplified_self_ty.and_then(|ty| ty.def()).map_or(true, |ty_def_id| {
- self.resolver.cstore().visibility_untracked(ty_def_id)
- == Visibility::Public
+ self.resolver.cstore().visibility_untracked(ty_def_id).is_public()
})
{
self.resolve_doc_links_extern_impl(impl_def_id, false);
}
}
for (ty_def_id, impl_def_id) in all_inherent_impls {
- if self.resolver.cstore().visibility_untracked(ty_def_id) == Visibility::Public
- {
+ if self.resolver.cstore().visibility_untracked(ty_def_id).is_public() {
self.resolve_doc_links_extern_impl(impl_def_id, true);
}
}
}
fn resolve_doc_links_extern_impl(&mut self, def_id: DefId, is_inherent: bool) {
- self.resolve_doc_links_extern_outer(def_id, def_id);
+ self.resolve_doc_links_extern_outer_fixme(def_id, def_id);
let assoc_item_def_ids = Vec::from_iter(
self.resolver.cstore().associated_item_def_ids_untracked(def_id, self.sess),
);
for assoc_def_id in assoc_item_def_ids {
- if !is_inherent
- || self.resolver.cstore().visibility_untracked(assoc_def_id) == Visibility::Public
+ if !is_inherent || self.resolver.cstore().visibility_untracked(assoc_def_id).is_public()
{
- self.resolve_doc_links_extern_outer(assoc_def_id, def_id);
+ self.resolve_doc_links_extern_outer_fixme(assoc_def_id, def_id);
}
}
}
- fn resolve_doc_links_extern_outer(&mut self, def_id: DefId, scope_id: DefId) {
+ // FIXME: replace all uses with `resolve_doc_links_extern_outer` to actually resolve links, not
+ // just add traits in scope. This may be expensive and require benchmarking and optimization.
+ fn resolve_doc_links_extern_outer_fixme(&mut self, def_id: DefId, scope_id: DefId) {
if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
return;
}
- // FIXME: actually resolve links, not just add traits in scope.
if let Some(parent_id) = self.resolver.opt_parent(scope_id) {
self.add_traits_in_scope(parent_id);
}
}
+ fn resolve_doc_links_extern_outer(&mut self, def_id: DefId, scope_id: DefId) {
+ if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
+ return;
+ }
+ let attrs = Vec::from_iter(self.resolver.cstore().item_attrs_untracked(def_id, self.sess));
+ let parent_scope = ParentScope::module(
+ self.resolver.get_nearest_non_block_module(
+ self.resolver.opt_parent(scope_id).unwrap_or(scope_id),
+ ),
+ self.resolver,
+ );
+ self.resolve_doc_links(doc_attrs(attrs.iter()), parent_scope);
+ }
+
fn resolve_doc_links_extern_inner(&mut self, def_id: DefId) {
if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
return;
}
- // FIXME: actually resolve links, not just add traits in scope.
- self.add_traits_in_scope(def_id);
+ let attrs = Vec::from_iter(self.resolver.cstore().item_attrs_untracked(def_id, self.sess));
+ let parent_scope = ParentScope::module(self.resolver.expect_module(def_id), self.resolver);
+ self.resolve_doc_links(doc_attrs(attrs.iter()), parent_scope);
}
fn resolve_doc_links_local(&mut self, attrs: &[ast::Attribute]) {
}
}
- // FIXME: Resolve all prefixes for type-relative resolution or for diagnostics.
- if (need_assoc || !any_resolved) && pinfo.path_str.contains("::") {
- need_traits_in_scope = true;
+ // Resolve all prefixes for type-relative resolution or for diagnostics.
+ if need_assoc || !any_resolved {
+ let mut path = &pinfo.path_str[..];
+ while let Some(idx) = path.rfind("::") {
+ path = &path[..idx];
+ need_traits_in_scope = true;
+ for ns in [TypeNS, ValueNS, MacroNS] {
+ self.resolve_and_cache(path, ns, &parent_scope);
+ }
+ }
}
}
}
for child in self.resolver.module_children_or_reexports(module_id) {
// This condition should give a superset of `denied` from `fn clean_use_statement`.
- if child.vis == Visibility::Public
+ if child.vis.is_public()
|| self.document_private_items
&& child.vis != Visibility::Restricted(module_id)
&& module_id.is_local()
self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id());
}
ItemKind::MacroDef(macro_def) if macro_def.macro_rules => {
- self.parent_scope.macro_rules =
+ let (macro_rules_scope, res) =
self.resolver.macro_rules_scope(self.resolver.local_def_id(item.id));
+ self.parent_scope.macro_rules = macro_rules_scope;
+ self.all_macro_rules.insert(item.ident.name, res);
}
_ => {}
}
while let Some(did) = parent {
attr_buf.extend(
cx.tcx
- .get_attrs(did)
- .iter()
- .filter(|attr| attr.has_name(sym::doc))
+ .get_attrs(did, sym::doc)
.filter(|attr| {
if let Some([attr]) = attr.meta_item_list().as_deref() {
attr.has_name(sym::cfg)
// Run call-finder on all items
let mut calls = FxHashMap::default();
let mut finder = FindCalls { calls: &mut calls, tcx, map: tcx.hir(), cx, target_crates };
- tcx.hir().visit_all_item_likes(&mut finder.as_deep_visitor());
+ tcx.hir().deep_visit_all_item_likes(&mut finder);
// Sort call locations within a given file in document order
for fn_calls in calls.values_mut() {
-Subproject commit 593484fc1596707406b0b0b45b97feb08d2a1bfc
+Subproject commit 47848665966fc7393cb6f898077994f6dec2b591
use serde::{Deserialize, Serialize};
/// rustdoc format-version.
-pub const FORMAT_VERSION: u32 = 14;
+pub const FORMAT_VERSION: u32 = 15;
/// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information
/// about the language items in the local crate, as well as info about external items to allow
#[serde(rename = "type")]
type_: Type,
bounds: Vec<GenericBound>,
+ /// Used for Higher-Rank Trait Bounds (HRTBs)
+ /// ```plain
+ /// where for<'a> &'a T: Iterator,"
+ /// ^^^^^^^
+ /// |
+ /// this part
+ /// ```
+ generic_params: Vec<GenericParamDef>,
},
RegionPredicate {
lifetime: String,
TraitBound {
#[serde(rename = "trait")]
trait_: Type,
- /// Used for HRTBs
+ /// Used for Higher-Rank Trait Bounds (HRTBs)
+ /// ```plain
+ /// where F: for<'a, 'b> Fn(&'a u8, &'b u8)
+ /// ^^^^^^^^^^^
+ /// |
+ /// this part
+ /// ```
generic_params: Vec<GenericParamDef>,
modifier: TraitBoundModifier,
},
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct FunctionPointer {
pub decl: FnDecl,
+ /// Used for Higher-Rank Trait Bounds (HRTBs)
+ /// ```plain
+ /// for<'c> fn(val: &'c i32) -> i32
+ /// ^^^^^^^
+ /// |
+ /// this part
+ /// ```
pub generic_params: Vec<GenericParamDef>,
pub header: Header,
}
use std::arch::asm;
// CHECK-LABEL: @clobber_sysv64
-// CHECK: ={ax},={cx},={dx},={si},={di},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k0},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{dirflag},~{fpsr},~{flags},~{memory}
+// CHECK: ={ax},={cx},={dx},={si},={di},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k0},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{tmm0},~{tmm1},~{tmm2},~{tmm3},~{tmm4},~{tmm5},~{tmm6},~{tmm7},~{dirflag},~{fpsr},~{flags},~{memory}
#[no_mangle]
pub unsafe fn clobber_sysv64() {
asm!("", clobber_abi("sysv64"));
}
// CHECK-LABEL: @clobber_win64
-// CHECK: ={ax},={cx},={dx},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k0},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{dirflag},~{fpsr},~{flags},~{memory}
+// CHECK: ={ax},={cx},={dx},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k0},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{tmm0},~{tmm1},~{tmm2},~{tmm3},~{tmm4},~{tmm5},~{tmm6},~{tmm7},~{dirflag},~{fpsr},~{flags},~{memory}
#[no_mangle]
pub unsafe fn clobber_win64() {
asm!("", clobber_abi("win64"));
}
// CHECK-LABEL: @clobber_sysv64
-// CHECK: =&{dx},={ax},={cx},={si},={di},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k0},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{dirflag},~{fpsr},~{flags},~{memory}
+// CHECK: =&{dx},={ax},={cx},={si},={di},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k0},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{tmm0},~{tmm1},~{tmm2},~{tmm3},~{tmm4},~{tmm5},~{tmm6},~{tmm7},~{dirflag},~{fpsr},~{flags},~{memory}
#[no_mangle]
pub unsafe fn clobber_sysv64_edx() {
let foo: i32;
}
// CHECK-LABEL: @clobber_win64
-// CHECK: =&{dx},={ax},={cx},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k0},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{dirflag},~{fpsr},~{flags},~{memory}
+// CHECK: =&{dx},={ax},={cx},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k0},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{tmm0},~{tmm1},~{tmm2},~{tmm3},~{tmm4},~{tmm5},~{tmm6},~{tmm7},~{dirflag},~{fpsr},~{flags},~{memory}
#[no_mangle]
pub unsafe fn clobber_win64_edx() {
let foo: i32;
use std::arch::asm;
+// CHECK-LABEL: @amx_clobber
+// base: call void asm sideeffect inteldialect "", "~{tmm0}"()
+#[no_mangle]
+pub unsafe fn amx_clobber() {
+ asm!("", out("tmm0") _, options(nostack, nomem, preserves_flags));
+}
+
// CHECK-LABEL: @avx512_clobber
// base: call void asm sideeffect inteldialect "", "~{xmm31}"()
// avx512: call float asm sideeffect inteldialect "", "=&{xmm31}"()
--- /dev/null
+// compile-flags: -C opt-level=1
+// only-64bit (because we're using [ui]size)
+
+#![crate_type = "lib"]
+#![feature(core_intrinsics)]
+
+//! Basic optimizations are enabled because otherwise `x86_64-gnu-nopt` had an alloca.
+//! Uses a type with non-power-of-two size to avoid normalizations to shifts.
+
+use std::intrinsics::*;
+
+type RGB = [u8; 3];
+
+// CHECK-LABEL: @offset_from_odd_size
+#[no_mangle]
+pub unsafe fn offset_from_odd_size(a: *const RGB, b: *const RGB) -> isize {
+ // CHECK: start
+ // CHECK-NEXT: ptrtoint
+ // CHECK-NEXT: ptrtoint
+ // CHECK-NEXT: sub i64
+ // CHECK-NEXT: sdiv exact i64 %{{[0-9]+}}, 3
+ // CHECK-NEXT: ret i64
+ ptr_offset_from(a, b)
+}
+
+// CHECK-LABEL: @offset_from_unsigned_odd_size
+#[no_mangle]
+pub unsafe fn offset_from_unsigned_odd_size(a: *const RGB, b: *const RGB) -> usize {
+ // CHECK: start
+ // CHECK-NEXT: ptrtoint
+ // CHECK-NEXT: ptrtoint
+ // CHECK-NEXT: sub nuw i64
+ // CHECK-NEXT: udiv exact i64 %{{[0-9]+}}, 3
+ // CHECK-NEXT: ret i64
+ ptr_offset_from_unsigned(a, b)
+}
}
// Here we check that local debuginfo is mapped correctly.
-// CHECK: !DIFile(filename: "/the/src/remap_path_prefix/main.rs", directory: "/the/cwd"
+// CHECK: !DIFile(filename: "/the/src/remap_path_prefix/main.rs", directory: ""
// And here that debuginfo from other crates are expanded to absolute paths.
// CHECK: !DIFile(filename: "/the/aux-src/remap_path_prefix_aux.rs", directory: ""
--- /dev/null
+// compile-flags: -C opt-level=3 --edition=2021
+// only-x86_64
+// ignore-debug: the debug assertions get in the way
+
+#![crate_type = "lib"]
+#![feature(portable_simd)]
+
+use std::simd::Simd;
+const N: usize = 8;
+
+#[no_mangle]
+// CHECK-LABEL: @wider_reduce_simd
+pub fn wider_reduce_simd(x: Simd<u8, N>) -> u16 {
+ // CHECK: zext <8 x i8>
+ // CHECK-SAME: to <8 x i16>
+ // CHECK: call i16 @llvm.vector.reduce.add.v8i16(<8 x i16>
+ let x: Simd<u16, N> = x.cast();
+ x.reduce_sum()
+}
+
+#[no_mangle]
+// CHECK-LABEL: @wider_reduce_loop
+pub fn wider_reduce_loop(x: Simd<u8, N>) -> u16 {
+ // CHECK: zext <8 x i8>
+ // CHECK-SAME: to <8 x i16>
+ // CHECK: call i16 @llvm.vector.reduce.add.v8i16(<8 x i16>
+ let mut sum = 0_u16;
+ for i in 0..N {
+ sum += u16::from(x[i]);
+ }
+ sum
+}
+
+#[no_mangle]
+// CHECK-LABEL: @wider_reduce_iter
+pub fn wider_reduce_iter(x: Simd<u8, N>) -> u16 {
+ // CHECK: zext <8 x i8>
+ // CHECK-SAME: to <8 x i16>
+ // CHECK: call i16 @llvm.vector.reduce.add.v8i16(<8 x i16>
+ x.as_array().iter().copied().map(u16::from).sum()
+}
+
+// This iterator one is the most interesting, as it's the one
+// which used to not auto-vectorize due to a suboptimality in the
+// `<array::IntoIter as Iterator>::fold` implementation.
+
+#[no_mangle]
+// CHECK-LABEL: @wider_reduce_into_iter
+pub fn wider_reduce_into_iter(x: Simd<u8, N>) -> u16 {
+ // CHECK: zext <8 x i8>
+ // CHECK-SAME: to <8 x i16>
+ // CHECK: call i16 @llvm.vector.reduce.add.v8i16(<8 x i16>
+ x.to_array().into_iter().map(u16::from).sum()
+}
// compile-flags: -C panic=abort
-// Test that `nounwind` atributes are not applied to `C-unwind` extern functions
-// even when the code is compiled with `panic=abort`.
+// Test that `nounwind` atributes are also applied to extern `C-unwind` Rust functions
+// when the code is compiled with `panic=abort`.
#![crate_type = "lib"]
#![feature(c_unwind)]
-extern "C-unwind" {
- fn may_unwind();
-}
-
-// CHECK: @rust_item_that_can_unwind() unnamed_addr #0
+// CHECK: @rust_item_that_can_unwind() unnamed_addr [[ATTR0:#[0-9]+]]
#[no_mangle]
pub unsafe extern "C-unwind" fn rust_item_that_can_unwind() {
+ // CHECK: call void @_ZN4core9panicking15panic_no_unwind
may_unwind();
}
+extern "C-unwind" {
+ // CHECK: @may_unwind() unnamed_addr [[ATTR1:#[0-9]+]]
+ fn may_unwind();
+}
+
// Now, make sure that the LLVM attributes for this functions are correct. First, make
// sure that the first item is correctly marked with the `nounwind` attribute:
//
-// CHECK-NOT: attributes #0 = { {{.*}}nounwind{{.*}} }
+// CHECK: attributes [[ATTR0]] = { {{.*}}nounwind{{.*}} }
+//
+// Now, check that foreign item is correctly marked without the `nounwind` attribute.
+// CHECK-NOT: attributes [[ATTR1]] = { {{.*}}nounwind{{.*}} }
fn g() -> () {
let mut _0: (); // return place in scope 0 at $DIR/cycle.rs:11:8: 11:8
let _1: (); // in scope 0 at $DIR/cycle.rs:12:5: 12:12
++ let mut _2: fn() {main}; // in scope 0 at $DIR/cycle.rs:12:5: 12:12
++ let mut _5: (); // in scope 0 at $DIR/cycle.rs:6:5: 6:8
++ scope 1 (inlined f::<fn() {main}>) { // at $DIR/cycle.rs:12:5: 12:12
++ debug g => _2; // in scope 1 at $DIR/cycle.rs:5:6: 5:7
++ let _3: (); // in scope 1 at $DIR/cycle.rs:6:5: 6:8
++ let mut _4: &fn() {main}; // in scope 1 at $DIR/cycle.rs:6:5: 6:6
++ scope 2 (inlined <fn() {main} as Fn<()>>::call - shim(fn() {main})) { // at $DIR/cycle.rs:6:5: 6:8
++ }
++ }
bb0: {
StorageLive(_1); // scope 0 at $DIR/cycle.rs:12:5: 12:12
- _1 = f::<fn() {main}>(main) -> bb1; // scope 0 at $DIR/cycle.rs:12:5: 12:12
- // mir::Constant
- // + span: $DIR/cycle.rs:12:5: 12:6
- // + literal: Const { ty: fn(fn() {main}) {f::<fn() {main}>}, val: Value(Scalar(<ZST>)) }
+- _1 = f::<fn() {main}>(main) -> bb1; // scope 0 at $DIR/cycle.rs:12:5: 12:12
++ StorageLive(_2); // scope 0 at $DIR/cycle.rs:12:5: 12:12
++ _2 = main; // scope 0 at $DIR/cycle.rs:12:5: 12:12
// mir::Constant
+- // + span: $DIR/cycle.rs:12:5: 12:6
+- // + literal: Const { ty: fn(fn() {main}) {f::<fn() {main}>}, val: Value(Scalar(<ZST>)) }
+- // mir::Constant
// + span: $DIR/cycle.rs:12:7: 12:11
// + literal: Const { ty: fn() {main}, val: Value(Scalar(<ZST>)) }
++ StorageLive(_3); // scope 1 at $DIR/cycle.rs:6:5: 6:8
++ StorageLive(_4); // scope 1 at $DIR/cycle.rs:6:5: 6:6
++ _4 = &_2; // scope 1 at $DIR/cycle.rs:6:5: 6:6
++ StorageLive(_5); // scope 1 at $DIR/cycle.rs:6:5: 6:8
++ _5 = const (); // scope 1 at $DIR/cycle.rs:6:5: 6:8
++ _3 = move (*_4)() -> [return: bb4, unwind: bb2]; // scope 2 at $SRC_DIR/core/src/ops/function.rs:LL:COL
}
bb1: {
++ StorageDead(_2); // scope 0 at $DIR/cycle.rs:12:5: 12:12
StorageDead(_1); // scope 0 at $DIR/cycle.rs:12:12: 12:13
_0 = const (); // scope 0 at $DIR/cycle.rs:11:8: 13:2
return; // scope 0 at $DIR/cycle.rs:13:2: 13:2
++ }
++
++ bb2 (cleanup): {
++ drop(_2) -> bb3; // scope 1 at $DIR/cycle.rs:7:1: 7:2
++ }
++
++ bb3 (cleanup): {
++ resume; // scope 1 at $DIR/cycle.rs:5:1: 7:2
++ }
++
++ bb4: {
++ StorageDead(_5); // scope 1 at $DIR/cycle.rs:6:5: 6:8
++ StorageDead(_4); // scope 1 at $DIR/cycle.rs:6:7: 6:8
++ StorageDead(_3); // scope 1 at $DIR/cycle.rs:6:8: 6:9
++ drop(_2) -> bb1; // scope 1 at $DIR/cycle.rs:7:1: 7:2
}
}
fn main() -> () {
let mut _0: (); // return place in scope 0 at $DIR/cycle.rs:16:11: 16:11
let _1: (); // in scope 0 at $DIR/cycle.rs:17:5: 17:9
++ let mut _2: fn() {g}; // in scope 0 at $DIR/cycle.rs:17:5: 17:9
++ let mut _5: (); // in scope 0 at $DIR/cycle.rs:6:5: 6:8
++ scope 1 (inlined f::<fn() {g}>) { // at $DIR/cycle.rs:17:5: 17:9
++ debug g => _2; // in scope 1 at $DIR/cycle.rs:5:6: 5:7
++ let _3: (); // in scope 1 at $DIR/cycle.rs:6:5: 6:8
++ let mut _4: &fn() {g}; // in scope 1 at $DIR/cycle.rs:6:5: 6:6
++ scope 2 (inlined <fn() {g} as Fn<()>>::call - shim(fn() {g})) { // at $DIR/cycle.rs:6:5: 6:8
++ scope 3 (inlined g) { // at $SRC_DIR/core/src/ops/function.rs:LL:COL
++ let mut _6: fn() {main}; // in scope 3 at $DIR/cycle.rs:12:5: 12:12
++ scope 4 (inlined f::<fn() {main}>) { // at $DIR/cycle.rs:12:5: 12:12
++ debug g => _6; // in scope 4 at $DIR/cycle.rs:5:6: 5:7
++ let _7: (); // in scope 4 at $DIR/cycle.rs:6:5: 6:8
++ let mut _8: &fn() {main}; // in scope 4 at $DIR/cycle.rs:6:5: 6:6
++ scope 5 (inlined <fn() {main} as Fn<()>>::call - shim(fn() {main})) { // at $DIR/cycle.rs:6:5: 6:8
++ }
++ }
++ }
++ }
++ }
bb0: {
StorageLive(_1); // scope 0 at $DIR/cycle.rs:17:5: 17:9
- _1 = f::<fn() {g}>(g) -> bb1; // scope 0 at $DIR/cycle.rs:17:5: 17:9
- // mir::Constant
- // + span: $DIR/cycle.rs:17:5: 17:6
- // + literal: Const { ty: fn(fn() {g}) {f::<fn() {g}>}, val: Value(Scalar(<ZST>)) }
+- _1 = f::<fn() {g}>(g) -> bb1; // scope 0 at $DIR/cycle.rs:17:5: 17:9
++ StorageLive(_2); // scope 0 at $DIR/cycle.rs:17:5: 17:9
++ _2 = g; // scope 0 at $DIR/cycle.rs:17:5: 17:9
// mir::Constant
+- // + span: $DIR/cycle.rs:17:5: 17:6
+- // + literal: Const { ty: fn(fn() {g}) {f::<fn() {g}>}, val: Value(Scalar(<ZST>)) }
+- // mir::Constant
// + span: $DIR/cycle.rs:17:7: 17:8
// + literal: Const { ty: fn() {g}, val: Value(Scalar(<ZST>)) }
++ StorageLive(_3); // scope 1 at $DIR/cycle.rs:6:5: 6:8
++ StorageLive(_4); // scope 1 at $DIR/cycle.rs:6:5: 6:6
++ _4 = &_2; // scope 1 at $DIR/cycle.rs:6:5: 6:6
++ StorageLive(_5); // scope 1 at $DIR/cycle.rs:6:5: 6:8
++ _5 = const (); // scope 1 at $DIR/cycle.rs:6:5: 6:8
++ StorageLive(_6); // scope 3 at $DIR/cycle.rs:12:5: 12:12
++ StorageLive(_7); // scope 4 at $DIR/cycle.rs:6:5: 6:8
++ StorageLive(_8); // scope 4 at $DIR/cycle.rs:6:5: 6:6
++ _8 = &_6; // scope 4 at $DIR/cycle.rs:6:5: 6:6
++ _7 = move (*_8)() -> [return: bb4, unwind: bb2]; // scope 5 at $SRC_DIR/core/src/ops/function.rs:LL:COL
}
bb1: {
++ StorageDead(_2); // scope 0 at $DIR/cycle.rs:17:5: 17:9
StorageDead(_1); // scope 0 at $DIR/cycle.rs:17:9: 17:10
_0 = const (); // scope 0 at $DIR/cycle.rs:16:11: 18:2
return; // scope 0 at $DIR/cycle.rs:18:2: 18:2
++ }
++
++ bb2 (cleanup): {
++ drop(_2) -> bb3; // scope 1 at $DIR/cycle.rs:7:1: 7:2
++ }
++
++ bb3 (cleanup): {
++ resume; // scope 1 at $DIR/cycle.rs:5:1: 7:2
++ }
++
++ bb4: {
++ StorageDead(_8); // scope 4 at $DIR/cycle.rs:6:7: 6:8
++ StorageDead(_7); // scope 4 at $DIR/cycle.rs:6:8: 6:9
++ StorageDead(_6); // scope 3 at $DIR/cycle.rs:12:5: 12:12
++ StorageDead(_5); // scope 1 at $DIR/cycle.rs:6:5: 6:8
++ StorageDead(_4); // scope 1 at $DIR/cycle.rs:6:7: 6:8
++ StorageDead(_3); // scope 1 at $DIR/cycle.rs:6:8: 6:9
++ drop(_2) -> bb1; // scope 1 at $DIR/cycle.rs:7:1: 7:2
}
}
--- /dev/null
+#![crate_type = "lib"]
+
+use std::fmt::Debug;
+
+pub trait Cache {
+ type V: Debug;
+
+ fn store_nocache(&self);
+}
+
+pub trait Query {
+ type V;
+ type C: Cache<V = Self::V>;
+
+ fn cache<T>(s: &T) -> &Self::C;
+}
+
+// EMIT_MIR dyn_trait.mk_cycle.Inline.diff
+#[inline(always)]
+pub fn mk_cycle<V: Debug>(c: &dyn Cache<V = V>) {
+ c.store_nocache()
+}
+
+// EMIT_MIR dyn_trait.try_execute_query.Inline.diff
+#[inline(always)]
+pub fn try_execute_query<C: Cache>(c: &C) {
+ mk_cycle(c)
+}
+
+// EMIT_MIR dyn_trait.get_query.Inline.diff
+#[inline(always)]
+pub fn get_query<Q: Query, T>(t: &T) {
+ let c = Q::cache(t);
+ try_execute_query(c)
+}
--- /dev/null
+- // MIR for `get_query` before Inline
++ // MIR for `get_query` after Inline
+
+ fn get_query(_1: &T) -> () {
+ debug t => _1; // in scope 0 at $DIR/dyn-trait.rs:32:31: 32:32
+ let mut _0: (); // return place in scope 0 at $DIR/dyn-trait.rs:32:38: 32:38
+ let _2: &<Q as Query>::C; // in scope 0 at $DIR/dyn-trait.rs:33:9: 33:10
+ let mut _3: &T; // in scope 0 at $DIR/dyn-trait.rs:33:22: 33:23
+ let mut _4: &<Q as Query>::C; // in scope 0 at $DIR/dyn-trait.rs:34:23: 34:24
+ scope 1 {
+ debug c => _2; // in scope 1 at $DIR/dyn-trait.rs:33:9: 33:10
++ scope 2 (inlined try_execute_query::<<Q as Query>::C>) { // at $DIR/dyn-trait.rs:34:5: 34:25
++ debug c => _4; // in scope 2 at $DIR/dyn-trait.rs:26:36: 26:37
++ let mut _5: &dyn Cache<V = <Q as Query>::V>; // in scope 2 at $DIR/dyn-trait.rs:27:14: 27:15
++ let mut _6: &<Q as Query>::C; // in scope 2 at $DIR/dyn-trait.rs:27:14: 27:15
++ scope 3 (inlined mk_cycle::<<Q as Query>::V>) { // at $DIR/dyn-trait.rs:27:5: 27:16
++ debug c => _5; // in scope 3 at $DIR/dyn-trait.rs:20:27: 20:28
++ let mut _7: &dyn Cache<V = <Q as Query>::V>; // in scope 3 at $DIR/dyn-trait.rs:21:5: 21:22
++ }
++ }
+ }
+
+ bb0: {
+ StorageLive(_2); // scope 0 at $DIR/dyn-trait.rs:33:9: 33:10
+ StorageLive(_3); // scope 0 at $DIR/dyn-trait.rs:33:22: 33:23
+ _3 = &(*_1); // scope 0 at $DIR/dyn-trait.rs:33:22: 33:23
+ _2 = <Q as Query>::cache::<T>(move _3) -> bb1; // scope 0 at $DIR/dyn-trait.rs:33:13: 33:24
+ // mir::Constant
+ // + span: $DIR/dyn-trait.rs:33:13: 33:21
+ // + user_ty: UserType(0)
+ // + literal: Const { ty: for<'r> fn(&'r T) -> &'r <Q as Query>::C {<Q as Query>::cache::<T>}, val: Value(Scalar(<ZST>)) }
+ }
+
+ bb1: {
+ StorageDead(_3); // scope 0 at $DIR/dyn-trait.rs:33:23: 33:24
+ StorageLive(_4); // scope 1 at $DIR/dyn-trait.rs:34:23: 34:24
+ _4 = &(*_2); // scope 1 at $DIR/dyn-trait.rs:34:23: 34:24
+- _0 = try_execute_query::<<Q as Query>::C>(move _4) -> bb2; // scope 1 at $DIR/dyn-trait.rs:34:5: 34:25
++ StorageLive(_5); // scope 2 at $DIR/dyn-trait.rs:27:14: 27:15
++ StorageLive(_6); // scope 2 at $DIR/dyn-trait.rs:27:14: 27:15
++ _6 = _4; // scope 2 at $DIR/dyn-trait.rs:27:14: 27:15
++ _5 = move _6 as &dyn Cache<V = <Q as Query>::V> (Pointer(Unsize)); // scope 2 at $DIR/dyn-trait.rs:27:14: 27:15
++ StorageDead(_6); // scope 2 at $DIR/dyn-trait.rs:27:14: 27:15
++ StorageLive(_7); // scope 3 at $DIR/dyn-trait.rs:21:5: 21:22
++ _7 = _5; // scope 3 at $DIR/dyn-trait.rs:21:5: 21:22
++ _0 = <dyn Cache<V = <Q as Query>::V> as Cache>::store_nocache(move _7) -> bb2; // scope 3 at $DIR/dyn-trait.rs:21:5: 21:22
+ // mir::Constant
+- // + span: $DIR/dyn-trait.rs:34:5: 34:22
+- // + literal: Const { ty: for<'r> fn(&'r <Q as Query>::C) {try_execute_query::<<Q as Query>::C>}, val: Value(Scalar(<ZST>)) }
++ // + span: $DIR/dyn-trait.rs:21:7: 21:20
++ // + literal: Const { ty: for<'r> fn(&'r dyn Cache<V = <Q as Query>::V>) {<dyn Cache<V = <Q as Query>::V> as Cache>::store_nocache}, val: Value(Scalar(<ZST>)) }
+ }
+
+ bb2: {
++ StorageDead(_7); // scope 3 at $DIR/dyn-trait.rs:21:21: 21:22
++ StorageDead(_5); // scope 2 at $DIR/dyn-trait.rs:27:15: 27:16
+ StorageDead(_4); // scope 1 at $DIR/dyn-trait.rs:34:24: 34:25
+ StorageDead(_2); // scope 0 at $DIR/dyn-trait.rs:35:1: 35:2
+ return; // scope 0 at $DIR/dyn-trait.rs:35:2: 35:2
+ }
+ }
+
--- /dev/null
+- // MIR for `mk_cycle` before Inline
++ // MIR for `mk_cycle` after Inline
+
+ fn mk_cycle(_1: &dyn Cache<V = V>) -> () {
+ debug c => _1; // in scope 0 at $DIR/dyn-trait.rs:20:27: 20:28
+ let mut _0: (); // return place in scope 0 at $DIR/dyn-trait.rs:20:49: 20:49
+ let mut _2: &dyn Cache<V = V>; // in scope 0 at $DIR/dyn-trait.rs:21:5: 21:22
+
+ bb0: {
+ StorageLive(_2); // scope 0 at $DIR/dyn-trait.rs:21:5: 21:22
+ _2 = &(*_1); // scope 0 at $DIR/dyn-trait.rs:21:5: 21:22
+ _0 = <dyn Cache<V = V> as Cache>::store_nocache(move _2) -> bb1; // scope 0 at $DIR/dyn-trait.rs:21:5: 21:22
+ // mir::Constant
+ // + span: $DIR/dyn-trait.rs:21:7: 21:20
+ // + literal: Const { ty: for<'r> fn(&'r dyn Cache<V = V>) {<dyn Cache<V = V> as Cache>::store_nocache}, val: Value(Scalar(<ZST>)) }
+ }
+
+ bb1: {
+ StorageDead(_2); // scope 0 at $DIR/dyn-trait.rs:21:21: 21:22
+ return; // scope 0 at $DIR/dyn-trait.rs:22:2: 22:2
+ }
+ }
+
--- /dev/null
+- // MIR for `try_execute_query` before Inline
++ // MIR for `try_execute_query` after Inline
+
+ fn try_execute_query(_1: &C) -> () {
+ debug c => _1; // in scope 0 at $DIR/dyn-trait.rs:26:36: 26:37
+ let mut _0: (); // return place in scope 0 at $DIR/dyn-trait.rs:26:43: 26:43
+ let mut _2: &dyn Cache<V = <C as Cache>::V>; // in scope 0 at $DIR/dyn-trait.rs:27:14: 27:15
+ let mut _3: &C; // in scope 0 at $DIR/dyn-trait.rs:27:14: 27:15
++ scope 1 (inlined mk_cycle::<<C as Cache>::V>) { // at $DIR/dyn-trait.rs:27:5: 27:16
++ debug c => _2; // in scope 1 at $DIR/dyn-trait.rs:20:27: 20:28
++ let mut _4: &dyn Cache<V = <C as Cache>::V>; // in scope 1 at $DIR/dyn-trait.rs:21:5: 21:22
++ }
+
+ bb0: {
+ StorageLive(_2); // scope 0 at $DIR/dyn-trait.rs:27:14: 27:15
+ StorageLive(_3); // scope 0 at $DIR/dyn-trait.rs:27:14: 27:15
+ _3 = &(*_1); // scope 0 at $DIR/dyn-trait.rs:27:14: 27:15
+ _2 = move _3 as &dyn Cache<V = <C as Cache>::V> (Pointer(Unsize)); // scope 0 at $DIR/dyn-trait.rs:27:14: 27:15
+ StorageDead(_3); // scope 0 at $DIR/dyn-trait.rs:27:14: 27:15
+- _0 = mk_cycle::<<C as Cache>::V>(move _2) -> bb1; // scope 0 at $DIR/dyn-trait.rs:27:5: 27:16
++ StorageLive(_4); // scope 1 at $DIR/dyn-trait.rs:21:5: 21:22
++ _4 = _2; // scope 1 at $DIR/dyn-trait.rs:21:5: 21:22
++ _0 = <dyn Cache<V = <C as Cache>::V> as Cache>::store_nocache(move _4) -> bb1; // scope 1 at $DIR/dyn-trait.rs:21:5: 21:22
+ // mir::Constant
+- // + span: $DIR/dyn-trait.rs:27:5: 27:13
+- // + literal: Const { ty: for<'r> fn(&'r (dyn Cache<V = <C as Cache>::V> + 'r)) {mk_cycle::<<C as Cache>::V>}, val: Value(Scalar(<ZST>)) }
++ // + span: $DIR/dyn-trait.rs:21:7: 21:20
++ // + literal: Const { ty: for<'r> fn(&'r dyn Cache<V = <C as Cache>::V>) {<dyn Cache<V = <C as Cache>::V> as Cache>::store_nocache}, val: Value(Scalar(<ZST>)) }
+ }
+
+ bb1: {
++ StorageDead(_4); // scope 1 at $DIR/dyn-trait.rs:21:21: 21:22
+ StorageDead(_2); // scope 0 at $DIR/dyn-trait.rs:27:15: 27:16
+ return; // scope 0 at $DIR/dyn-trait.rs:28:2: 28:2
+ }
+ }
+
fn main() -> () {
let mut _0: (); // return place in scope 0 at $DIR/inline-cycle-generic.rs:8:11: 8:11
let _1: (); // in scope 0 at $DIR/inline-cycle-generic.rs:9:5: 9:24
-+ scope 1 (inlined <C as Call>::call) { // at $DIR/inline-cycle-generic.rs:9:5: 9:24
-+ scope 2 (inlined <B<A> as Call>::call) { // at $DIR/inline-cycle-generic.rs:38:9: 38:31
-+ }
-+ }
bb0: {
StorageLive(_1); // scope 0 at $DIR/inline-cycle-generic.rs:9:5: 9:24
-- _1 = <C as Call>::call() -> bb1; // scope 0 at $DIR/inline-cycle-generic.rs:9:5: 9:24
-+ _1 = <A as Call>::call() -> bb1; // scope 2 at $DIR/inline-cycle-generic.rs:31:9: 31:28
+ _1 = <C as Call>::call() -> bb1; // scope 0 at $DIR/inline-cycle-generic.rs:9:5: 9:24
// mir::Constant
-- // + span: $DIR/inline-cycle-generic.rs:9:5: 9:22
-- // + literal: Const { ty: fn() {<C as Call>::call}, val: Value(Scalar(<ZST>)) }
-+ // + span: $DIR/inline-cycle-generic.rs:31:9: 31:26
-+ // + literal: Const { ty: fn() {<A as Call>::call}, val: Value(Scalar(<ZST>)) }
+ // + span: $DIR/inline-cycle-generic.rs:9:5: 9:22
+ // + literal: Const { ty: fn() {<C as Call>::call}, val: Value(Scalar(<ZST>)) }
}
bb1: {
use rustc_borrowck::consumers::BodyWithBorrowckFacts;
use rustc_driver::Compilation;
use rustc_hir::def_id::LocalDefId;
-use rustc_hir::itemlikevisit::ItemLikeVisitor;
+use rustc_hir::def::DefKind;
use rustc_interface::interface::Compiler;
use rustc_interface::{Config, Queries};
use rustc_middle::ty::query::query_values::mir_borrowck;
queries.global_ctxt().unwrap().peek_mut().enter(|tcx| {
// Collect definition ids of MIR bodies.
let hir = tcx.hir();
- let mut visitor = HirVisitor { bodies: Vec::new() };
- hir.visit_all_item_likes(&mut visitor);
+ let mut bodies = Vec::new();
+
+ let crate_items = tcx.hir_crate_items(());
+ for id in crate_items.items() {
+ if matches!(tcx.def_kind(id.def_id), DefKind::Fn) {
+ bodies.push(id.def_id);
+ }
+ }
+
+ for id in crate_items.trait_items() {
+ if matches!(tcx.def_kind(id.def_id), DefKind::AssocFn) {
+ let trait_item = hir.trait_item(id);
+ if let rustc_hir::TraitItemKind::Fn(_, trait_fn) = &trait_item.kind {
+ if let rustc_hir::TraitFn::Provided(_) = trait_fn {
+ bodies.push(trait_item.def_id);
+ }
+ }
+ }
+ }
+
+ for id in crate_items.impl_items() {
+ if matches!(tcx.def_kind(id.def_id), DefKind::AssocFn) {
+ bodies.push(id.def_id);
+ }
+ }
// Trigger borrow checking of all bodies.
- for def_id in visitor.bodies {
+ for def_id in bodies {
let _ = tcx.optimized_mir(def_id);
}
original_mir_borrowck(tcx, def_id)
}
-/// Visitor that collects all body definition ids mentioned in the program.
-struct HirVisitor {
- bodies: Vec<LocalDefId>,
-}
-
-impl<'tcx> ItemLikeVisitor<'tcx> for HirVisitor {
- fn visit_item(&mut self, item: &rustc_hir::Item) {
- if let rustc_hir::ItemKind::Fn(..) = item.kind {
- self.bodies.push(item.def_id);
- }
- }
-
- fn visit_trait_item(&mut self, trait_item: &rustc_hir::TraitItem) {
- if let rustc_hir::TraitItemKind::Fn(_, trait_fn) = &trait_item.kind {
- if let rustc_hir::TraitFn::Provided(_) = trait_fn {
- self.bodies.push(trait_item.def_id);
- }
- }
- }
-
- fn visit_impl_item(&mut self, impl_item: &rustc_hir::ImplItem) {
- if let rustc_hir::ImplItemKind::Fn(..) = impl_item.kind {
- self.bodies.push(impl_item.def_id);
- }
- }
-
- fn visit_foreign_item(&mut self, _foreign_item: &rustc_hir::ForeignItem) {}
-}
-
/// Pull MIR bodies stored in the thread-local.
fn get_bodies<'tcx>(tcx: TyCtxt<'tcx>) -> Vec<(String, BodyWithBorrowckFacts<'tcx>)> {
MIR_BODIES.with(|state| {
--- /dev/null
+# This test makes sure that --remap-path-prefix has the expected effects on paths in debuginfo.
+# It tests several cases, each of them has a detailed description attached to it.
+
+# ignore-windows
+
+SRC_DIR := $(abspath .)
+SRC_DIR_PARENT := $(abspath ..)
+
+-include ../../run-make-fulldeps/tools.mk
+
+all: \
+ abs_input_outside_working_dir \
+ rel_input_remap_working_dir \
+ rel_input_remap_working_dir_parent \
+ rel_input_remap_working_dir_child \
+ abs_input_inside_working_dir \
+ abs_input_outside_working_dir
+
+# The compiler is called with an *ABSOLUTE PATH* as input, and that absolute path *is* within
+# the working directory of the compiler. We are remapping the path that contains `src`.
+abs_input_inside_working_dir:
+ # We explicitly switch to a directory that *is* a prefix of the directory our
+ # source code is contained in.
+ cd $(SRC_DIR) && $(RUSTC) $(SRC_DIR)/src/quux.rs -o "$(TMPDIR)/abs_input_inside_working_dir.rlib" -Cdebuginfo=2 --remap-path-prefix $(SRC_DIR)=REMAPPED
+ # We expect the path to the main source file to be remapped.
+ "$(LLVM_BIN_DIR)"/llvm-dwarfdump $(TMPDIR)/abs_input_inside_working_dir.rlib | $(CGREP) "REMAPPED/src/quux.rs"
+ # No weird duplication of remapped components (see #78479)
+ "$(LLVM_BIN_DIR)"/llvm-dwarfdump $(TMPDIR)/abs_input_inside_working_dir.rlib | $(CGREP) -v "REMAPPED/REMAPPED"
+
+# The compiler is called with an *ABSOLUTE PATH* as input, and that absolute path is *not* within
+# the working directory of the compiler. We are remapping both the path that contains `src` and
+# the working directory to the same thing. This setup corresponds to a workaround that is needed
+# when trying to remap everything to something that looks like a local path.
+# Relative paths are interpreted as relative to the compiler's working directory (e.g. in
+# debuginfo). If we also remap the working directory, the compiler strip it from other paths so
+# that the final outcome is the desired one again.
+abs_input_outside_working_dir:
+ # We explicitly switch to a directory that is *not* a prefix of the directory our
+ # source code is contained in.
+ cd $(TMPDIR) && $(RUSTC) $(SRC_DIR)/src/quux.rs -o "$(TMPDIR)/abs_input_outside_working_dir.rlib" -Cdebuginfo=2 --remap-path-prefix $(SRC_DIR)=REMAPPED --remap-path-prefix $(TMPDIR)=REMAPPED
+ "$(LLVM_BIN_DIR)"/llvm-dwarfdump $(TMPDIR)/abs_input_outside_working_dir.rlib | $(CGREP) "REMAPPED/src/quux.rs"
+ # No weird duplication of remapped components (see #78479)
+ "$(LLVM_BIN_DIR)"/llvm-dwarfdump $(TMPDIR)/abs_input_outside_working_dir.rlib | $(CGREP) -v "REMAPPED/REMAPPED"
+
+# The compiler is called with a *RELATIVE PATH* as input. We are remapping the working directory of
+# the compiler, which naturally is an implicit prefix of our relative input path. Debuginfo will
+# expand the relative path to an absolute path and we expect the working directory to be remapped
+# in that expansion.
+rel_input_remap_working_dir:
+ cd $(SRC_DIR) && $(RUSTC) src/quux.rs -o "$(TMPDIR)/rel_input_remap_working_dir.rlib" -Cdebuginfo=2 --remap-path-prefix "$(SRC_DIR)=REMAPPED"
+ "$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir.rlib" | $(CGREP) "REMAPPED/src/quux.rs"
+ # No weird duplication of remapped components (see #78479)
+ "$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir.rlib" | $(CGREP) -v "REMAPPED/REMAPPED"
+
+# The compiler is called with a *RELATIVE PATH* as input. We are remapping a *SUB-DIRECTORY* of the
+# compiler's working directory. This test makes sure that that directory is remapped even though it
+# won't actually show up in this form in the compiler's SourceMap and instead is only constructed
+# on demand during debuginfo generation.
+rel_input_remap_working_dir_child:
+ cd $(SRC_DIR) && $(RUSTC) src/quux.rs -o "$(TMPDIR)/rel_input_remap_working_dir_child.rlib" -Cdebuginfo=2 --remap-path-prefix "$(SRC_DIR)/src=REMAPPED"
+ # We expect `src/quux.rs` to have been remapped to `REMAPPED/quux.rs`.
+ "$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_child.rlib" | $(CGREP) "REMAPPED/quux.rs"
+ # We don't want to find the path that we just remapped anywhere in the DWARF
+ "$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_child.rlib" | $(CGREP) -v "$(SRC_DIR)/src"
+ # No weird duplication of remapped components (see #78479)
+ "$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_child.rlib" | $(CGREP) -v "REMAPPED/REMAPPED"
+
+# The compiler is called with a *RELATIVE PATH* as input. We are remapping a *PARENT DIRECTORY* of
+# the compiler's working directory.
+rel_input_remap_working_dir_parent:
+ cd $(SRC_DIR) && $(RUSTC) src/quux.rs -o "$(TMPDIR)/rel_input_remap_working_dir_parent.rlib" -Cdebuginfo=2 --remap-path-prefix "$(SRC_DIR_PARENT)=REMAPPED"
+ # We expect `src/quux.rs` to have been remapped to `REMAPPED/remap-path-prefix-dwarf/src/quux.rs`.
+ "$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_parent.rlib" | $(CGREP) "REMAPPED/remap-path-prefix-dwarf/src/quux.rs"
+ # We don't want to find the path that we just remapped anywhere in the DWARF
+ "$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_parent.rlib" | $(CGREP) -v "$(SRC_DIR_PARENT)"
+ # No weird duplication of remapped components (see #78479)
+ "$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_parent.rlib" | $(CGREP) -v "REMAPPED/REMAPPED"
--- /dev/null
+#![crate_type = "rlib"]
+
+pub fn foo() {
+ println!("foo");
+}
wait-for: "#crate-search"
assert-property: ("#crate-search", {"value": "lib2"})
assert-false: "#results .externcrate"
+
+// Checking that the text for the "title" is correct (the "All" comes from the "<select>").
+assert-text: ("#search-settings", "Results for test in All", STARTS_WITH)
+
+// Checking the display of the crate filter.
+// We start with the light theme.
+local-storage: {"rustdoc-theme": "light", "rustdoc-use-system-theme": "false"}
+reload:
+
+timeout: 2000
+wait-for: "#crate-search"
+assert-css: ("#crate-search", {
+ "border": "1px solid rgb(224, 224, 224)",
+ "color": "rgb(0, 0, 0)",
+ "background-color": "rgb(255, 255, 255)",
+})
+
+// We now check the dark theme.
+click: "#settings-menu"
+wait-for: "#settings"
+click: "#theme-dark"
+wait-for-css: ("#crate-search", {
+ "border": "1px solid rgb(240, 240, 240)",
+ "color": "rgb(17, 17, 17)",
+ "background-color": "rgb(240, 240, 240)",
+})
+
+// And finally we check the ayu theme.
+click: "#theme-ayu"
+wait-for-css: ("#crate-search", {
+ "border": "1px solid rgb(66, 76, 87)",
+ "color": "rgb(197, 197, 197)",
+ "background-color": "rgb(20, 25, 32)",
+})
// We now click on the settings button.
click: "#settings-menu"
wait-for: "#settings"
-assert: "#main-content.hidden"
assert-css: ("#settings", {"display": "block"})
// Let's close it by clicking on the same button.
click: "#settings-menu"
-assert-false: "#alternative-display #settings"
-assert: "#not-displayed #settings"
-assert: "#main-content:not(.hidden)"
-
-// Let's open and then close it again with the "close settings" button.
-click: "#settings-menu"
-wait-for: "#alternative-display #settings"
-assert: "#main-content.hidden"
-click: "#back"
-wait-for: "#not-displayed #settings"
-assert: "#main-content:not(.hidden)"
+wait-for-css: ("#settings", {"display": "none"})
// Let's check that pressing "ESCAPE" is closing it.
click: "#settings-menu"
-wait-for: "#alternative-display #settings"
+wait-for-css: ("#settings", {"display": "block"})
press-key: "Escape"
-wait-for: "#not-displayed #settings"
-assert: "#main-content:not(.hidden)"
+wait-for-css: ("#settings", {"display": "none"})
// Let's click on it when the search results are displayed.
focus: ".search-input"
write: "test"
wait-for: "#alternative-display #search"
click: "#settings-menu"
-wait-for: "#alternative-display #settings"
-assert: "#not-displayed #search"
+wait-for-css: ("#settings", {"display": "block"})
+// Ensure that the search is still displayed.
+wait-for: "#alternative-display #search"
assert: "#main-content.hidden"
// Now let's check the content of the settings menu.
// We check their text as well.
assert-text: ("#preferred-dark-theme .setting-name", "Preferred dark theme")
assert-text: ("#preferred-light-theme .setting-name", "Preferred light theme")
+
+// Now we go to the settings page to check that the CSS is loaded as expected.
+goto: file://|DOC_PATH|/settings.html
+wait-for: "#settings"
+assert-css: (".setting-line .toggle", {"width": "45px", "margin-right": "20px"})
assert-false: "#help.hidden"
press-key: "Escape"
assert-css: ("#help.hidden", {"display": "none"})
-// Check for the themes list.
-assert-css: ("#theme-choices", {"display": "none"})
-press-key: "t"
-assert-css: ("#theme-choices", {"display": "block"})
-press-key: "t"
-// We ensure that 't' hides back the menu.
-assert-css: ("#theme-choices", {"display": "none"})
-press-key: "t"
-assert-css: ("#theme-choices", {"display": "block"})
-press-key: "Escape"
-// We ensure that 'Escape' hides the menu too.
-assert-css: ("#theme-choices", {"display": "none"})
// Ensures that the theme change is working as expected.
goto: file://|DOC_PATH|/test_docs/index.html
-click: "#theme-picker"
-click: "#theme-choices > button:first-child"
-// should be the ayu theme so let's check the color
+local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": "dark"}
+reload:
+click: "#settings-menu"
+wait-for: "#theme-ayu"
+click: "#theme-ayu"
+// should be the ayu theme so let's check the color.
wait-for-css: ("body", { "background-color": "rgb(15, 20, 25)" })
-click: "#theme-choices > button:last-child"
-// should be the light theme so let's check the color
+assert-local-storage: { "rustdoc-theme": "ayu" }
+click: "#theme-light"
+// should be the light theme so let's check the color.
wait-for-css: ("body", { "background-color": "rgb(255, 255, 255)" })
+assert-local-storage: { "rustdoc-theme": "light" }
+click: "#theme-dark"
+// Should be the dark theme so let's check the color.
+wait-for-css: ("body", { "background-color": "rgb(53, 53, 53)" })
+assert-local-storage: { "rustdoc-theme": "dark" }
goto: file://|DOC_PATH|/settings.html
wait-for: "#settings"
--- /dev/null
+// exact-check
+
+const QUERY = ['constructor', '__proto__'];
+
+const EXPECTED = [
+ {
+ 'others': [],
+ 'returned': [],
+ 'in_args': [],
+ },
+ {
+ 'others': [],
+ 'returned': [],
+ 'in_args': [],
+ },
+];
--- /dev/null
+// The alias needed to be there to reproduce the bug
+// that used to be here.
+#[doc(alias="other_alias")]
+pub fn something_else() {}
--- /dev/null
+// ignore-tidy-linelength
+
+#![feature(no_core)]
+#![no_core]
+
+// @count generics.json "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type.inner.decl.inputs[*]" 1
+// @is - "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type.inner.decl.inputs[0][0]" '"val"'
+// @is - "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type.inner.decl.inputs[0][1].kind" '"borrowed_ref"'
+// @is - "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type.inner.decl.inputs[0][1].inner.lifetime" \"\'c\"
+// @is - "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type.inner.decl.output" '{ "kind": "primitive", "inner": "i32" }'
+// @count - "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type.inner.generic_params[*]" 1
+// @is - "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type.inner.generic_params[0].name" \"\'c\"
+// @is - "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type.inner.generic_params[0].kind" '{ "lifetime": { "outlives": [] } }'
+pub type WithHigherRankTraitBounds = for<'c> fn(val: &'c i32) -> i32;
// @set foo = generic_args.json "$.index[*][?(@.name=='Foo')].id"
pub trait Foo {}
+// @set generic_foo = generic_args.json "$.index[*][?(@.name=='GenericFoo')].id"
+pub trait GenericFoo<'a> {}
+
// @is - "$.index[*][?(@.name=='generics')].inner.generics.where_predicates" "[]"
// @count - "$.index[*][?(@.name=='generics')].inner.generics.params[*]" 1
// @is - "$.index[*][?(@.name=='generics')].inner.generics.params[0].name" '"F"'
// @is - "$.index[*][?(@.name=='impl_trait')].inner.decl.inputs[0][1].inner[0].trait_bound.trait.inner.id" $foo
pub fn impl_trait(f: impl Foo) {}
-// @count - "$.index[*][?(@.name=='where_clase')].inner.generics.params[*]" 1
+// @count - "$.index[*][?(@.name=='where_clase')].inner.generics.params[*]" 3
// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.params[0].name" '"F"'
// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.params[0].kind" '{"type": {"bounds": [], "default": null, "synthetic": false}}'
-// @count - "$.index[*][?(@.name=='where_clase')].inner.decl.inputs[*]" 1
+// @count - "$.index[*][?(@.name=='where_clase')].inner.decl.inputs[*]" 3
// @is - "$.index[*][?(@.name=='where_clase')].inner.decl.inputs[0][0]" '"f"'
// @is - "$.index[*][?(@.name=='where_clase')].inner.decl.inputs[0][1].kind" '"generic"'
// @is - "$.index[*][?(@.name=='where_clase')].inner.decl.inputs[0][1].inner" '"F"'
-// @count - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[*]" 1
+// @count - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[*]" 3
+
// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[0].bound_predicate.type" '{"inner": "F", "kind": "generic"}'
// @count - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[0].bound_predicate.bounds[*]" 1
// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[0].bound_predicate.bounds[0].trait_bound.trait.inner.id" $foo
-pub fn where_clase<F>(f: F)
+
+// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.type" '{"inner": "G", "kind": "generic"}'
+// @count - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.bounds[*]" 1
+// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.bounds[0].trait_bound.trait.inner.id" $generic_foo
+// @count - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.bounds[0].trait_bound.generic_params[*]" 1
+// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.bounds[0].trait_bound.generic_params[0].name" \"\'a\"
+// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.bounds[0].trait_bound.generic_params[0].kind" '{ "lifetime": { "outlives": [] } }'
+// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.generic_params" "[]"
+
+// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.type.kind" '"borrowed_ref"'
+// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.type.inner.lifetime" \"\'b\"
+// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.type.inner.type" '{"inner": "H", "kind": "generic"}'
+// @count - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.bounds[*]" 1
+// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.bounds[0].trait_bound.trait.inner.id" $foo
+// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.bounds[0].trait_bound.generic_params" "[]"
+// @count - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.generic_params[*]" 1
+// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.generic_params[0].name" \"\'b\"
+// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.generic_params[0].kind" '{ "lifetime": { "outlives": [] } }'
+pub fn where_clase<F, G, H>(f: F, g: G, h: H)
where
F: Foo,
+ G: for<'a> GenericFoo<'a>,
+ for<'b> &'b H: Foo,
{
}
}
}
-/// [before_but_limited_to_module] FIXME: This error should be reported
-// ERROR unresolved link to `before_but_limited_to_module`
-/// [after] FIXME: This error should be reported
-// ERROR unresolved link to `after`
-/// [str] FIXME: This error shouldn not be reported
-//~^ ERROR `str` is both a builtin type and a macro
+/// [before_but_limited_to_module]
+//~^ ERROR unresolved link to `before_but_limited_to_module`
+/// [after]
+//~^ ERROR unresolved link to `after`
+/// [str]
fn check() {}
macro_rules! after {
-error: `str` is both a builtin type and a macro
- --> $DIR/macro-rules-error.rs:17:6
+error: unresolved link to `before_but_limited_to_module`
+ --> $DIR/macro-rules-error.rs:13:6
|
-LL | /// [str] FIXME: This error shouldn not be reported
- | ^^^ ambiguous link
+LL | /// [before_but_limited_to_module]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `before_but_limited_to_module` in scope
|
note: the lint level is defined here
--> $DIR/macro-rules-error.rs:5:9
|
LL | #![deny(rustdoc::broken_intra_doc_links)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-help: to link to the builtin type, prefix with `prim@`
+ = note: `macro_rules` named `before_but_limited_to_module` exists in this crate, but it is not in scope at this link's location
+
+error: unresolved link to `after`
+ --> $DIR/macro-rules-error.rs:15:6
|
-LL | /// [prim@str] FIXME: This error shouldn not be reported
- | +++++
-help: to link to the macro, add an exclamation mark
+LL | /// [after]
+ | ^^^^^ no item named `after` in scope
|
-LL | /// [str!] FIXME: This error shouldn not be reported
- | +
+ = note: `macro_rules` named `after` exists in this crate, but it is not in scope at this link's location
-error: aborting due to previous error
+error: aborting due to 2 previous errors
}
#[derive(SessionDiagnostic)]
-//~^ ERROR no method named `into_diagnostic_arg` found for struct `Hello` in the current scope
+//~^ ERROR the trait bound `Hello: IntoDiagnosticArg` is not satisfied
#[error(code = "E0123", slug = "foo")]
struct ArgFieldWithoutSkip {
#[primary_span]
#[label]
spans: Vec<Span>,
}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct UnitField {
+ #[primary_span]
+ spans: Span,
+ #[help]
+ foo: (),
+ #[help = "a"]
+ bar: (),
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct OptUnitField {
+ #[primary_span]
+ spans: Span,
+ #[help]
+ foo: Option<()>,
+ #[help = "a"]
+ bar: Option<()>,
+}
LL | #[nonsense]
| ^^^^^^^^
-error[E0599]: no method named `into_diagnostic_arg` found for struct `Hello` in the current scope
+error[E0277]: the trait bound `Hello: IntoDiagnosticArg` is not satisfied
--> $DIR/diagnostic-derive.rs:329:10
|
-LL | struct Hello {}
- | ------------ method `into_diagnostic_arg` not found for this
-...
LL | #[derive(SessionDiagnostic)]
- | ^^^^^^^^^^^^^^^^^ method not found in `Hello`
- |
+ | ^^^^^^^^^^^^^^^^^ the trait `IntoDiagnosticArg` is not implemented for `Hello`
+ |
+ = help: the following other types implement trait `IntoDiagnosticArg`:
+ &'a str
+ Ident
+ String
+ Symbol
+ rustc_middle::ty::Ty<'tcx>
+ usize
+note: required by a bound in `DiagnosticBuilder::<'a, G>::set_arg`
+ --> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:531:19
+ |
+LL | arg: impl IntoDiagnosticArg,
+ | ^^^^^^^^^^^^^^^^^ required by this bound in `DiagnosticBuilder::<'a, G>::set_arg`
= note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 43 previous errors
-For more information about this error, try `rustc --explain E0599`.
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// needs-asm-support
+// build-pass
+
+#![feature(asm_const, asm_sym)]
+
+use std::arch::asm;
+
+fn foofoo<const N: usize>() {}
+
+unsafe fn foo<const N: usize>() {
+ asm!("/* {0} */", const N);
+ asm!("/* {0} */", const N + 1);
+ asm!("/* {0} */", sym foofoo::<N>);
+}
+
+fn barbar<T>() {}
+
+unsafe fn bar<T>() {
+ asm!("/* {0} */", const std::mem::size_of::<T>());
+ asm!("/* {0} */", const std::mem::size_of::<(T, T)>());
+ asm!("/* {0} */", sym barbar::<T>);
+ asm!("/* {0} */", sym barbar::<(T, T)>);
+}
+
+fn main() {
+ unsafe {
+ foo::<0>();
+ bar::<usize>();
+ }
+}
unsafe fn generic<T>() {
asm!("{}", sym generic::<T>);
- //~^ generic parameters may not be used in const operations
}
// Const operands must be integers and must be constants.
|
= help: `sym` operands must refer to either a function or a static
-error: generic parameters may not be used in const operations
- --> $DIR/type-check-1.rs:65:30
- |
-LL | asm!("{}", sym generic::<T>);
- | ^ cannot perform const operation using `T`
- |
- = note: type parameters may not be used in const expressions
- = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
-
error[E0308]: mismatched types
--> $DIR/type-check-1.rs:55:26
|
= note: all inline asm arguments must have a statically known size
error[E0308]: mismatched types
- --> $DIR/type-check-1.rs:74:25
+ --> $DIR/type-check-1.rs:73:25
|
LL | global_asm!("{}", const 0f32);
| ^^^^ expected integer, found `f32`
error[E0308]: mismatched types
- --> $DIR/type-check-1.rs:76:25
+ --> $DIR/type-check-1.rs:75:25
|
LL | global_asm!("{}", const 0 as *mut u8);
| ^^^^^^^^^^^^ expected integer, found *-ptr
= note: expected type `{integer}`
found raw pointer `*mut u8`
-error: aborting due to 15 previous errors
+error: aborting due to 14 previous errors
Some errors have detailed explanations: E0277, E0308, E0435.
For more information about an error, try `rustc --explain E0277`.
--- /dev/null
+// check-pass
+#![feature(generic_const_exprs)]
+#![allow(incomplete_features)]
+
+pub trait First {
+ const CONST: usize;
+}
+pub trait Second {}
+
+impl<'a> First for dyn Second
+where
+ &'a Self: First,
+{
+ const CONST: usize = <&Self>::CONST;
+}
+
+trait Third: First
+where
+ [u8; Self::CONST]:
+{
+ const VAL: [u8; Self::CONST] = [0; Self::CONST];
+}
+
+fn main() {}
--> $DIR/hr-associated-type-projection-1.rs:13:33
|
LL | impl<T: Copy + std::ops::Deref> UnsafeCopy<'_, T> for T {
- | - this type parameter ^^^^^^^^^^^^^^^^^ expected associated type, found type parameter `T`
+ | - this type parameter ^^^^^^^^^^^^^^^^^ expected type parameter `T`, found associated type
|
- = note: expected associated type `<T as Deref>::Target`
- found type parameter `T`
+ = note: expected type parameter `T`
+ found associated type `<T as Deref>::Target`
+note: required by a bound in `UnsafeCopy`
+ --> $DIR/hr-associated-type-projection-1.rs:3:64
+ |
+LL | trait UnsafeCopy<'a, T: Copy>
+ | ---------- required by a bound in this
+LL | where
+LL | for<'b> <Self as UnsafeCopy<'b, T>>::Item: std::ops::Deref<Target = T>,
+ | ^^^^^^^^^^ required by this bound in `UnsafeCopy`
help: consider further restricting this bound
|
LL | impl<T: Copy + std::ops::Deref + Deref<Target = T>> UnsafeCopy<'_, T> for T {
--- /dev/null
+// edition:2018
+// compile-flags: -Zdrop-tracking
+// Regression test for issue #73741
+// Ensures that we don't emit spurious errors when
+// a type error ocurrs in an `async fn`
+
+async fn weird() {
+ 1 = 2; //~ ERROR invalid left-hand side
+
+ let mut loop_count = 0;
+ async {}.await
+}
+
+fn main() {}
--- /dev/null
+error[E0070]: invalid left-hand side of assignment
+ --> $DIR/issue-73741-type-err-drop-tracking.rs:8:7
+ |
+LL | 1 = 2;
+ | - ^
+ | |
+ | cannot assign to this expression
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0070`.
error[E0277]: `for<'r> fn(&'r i32) -> &'r i32 {foo}` doesn't implement `Debug`
--> $DIR/issue-77910-1.rs:8:5
|
+LL | fn foo(s: &i32) -> &i32 {
+ | --- consider calling this function
+...
LL | assert_eq!(foo, y);
| ^^^^^^^^^^^^^^^^^^ `for<'r> fn(&'r i32) -> &'r i32 {foo}` cannot be formatted using `{:?}` because it doesn't implement `Debug`
|
= help: the trait `Debug` is not implemented for `for<'r> fn(&'r i32) -> &'r i32 {foo}`
- = help: the following other types implement trait `Debug`:
- extern "C" fn() -> Ret
- extern "C" fn(A) -> Ret
- extern "C" fn(A, ...) -> Ret
- extern "C" fn(A, B) -> Ret
- extern "C" fn(A, B, ...) -> Ret
- extern "C" fn(A, B, C) -> Ret
- extern "C" fn(A, B, C, ...) -> Ret
- extern "C" fn(A, B, C, D) -> Ret
- and 68 others
+ = help: use parentheses to call the function: `foo(s)`
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 2 previous errors
--> $DIR/builtin-superkinds-self-type.rs:10:16
|
LL | impl <T: Sync> Foo for T { }
- | ^^^ ...so that the type `T` will meet its required lifetime bounds
+ | ^^^ ...so that the type `T` will meet its required lifetime bounds...
|
+note: ...that is required by this bound
+ --> $DIR/builtin-superkinds-self-type.rs:6:24
+ |
+LL | trait Foo : Sized+Sync+'static {
+ | ^^^^^^^
help: consider adding an explicit lifetime bound...
|
LL | impl <T: Sync + 'static> Foo for T { }
--- /dev/null
+// Test that encountering closures during coherence does not cause issues.
+#![feature(type_alias_impl_trait)]
+type OpaqueClosure = impl Sized;
+fn defining_use() -> OpaqueClosure {
+ || ()
+}
+
+struct Wrapper<T>(T);
+trait Trait {}
+impl Trait for Wrapper<OpaqueClosure> {}
+//~^ ERROR cannot implement trait on type alias impl trait
+impl<T: Sync> Trait for Wrapper<T> {}
+//~^ ERROR conflicting implementations of trait `Trait` for type `Wrapper<OpaqueClosure>`
+
+fn main() {}
--- /dev/null
+error: cannot implement trait on type alias impl trait
+ --> $DIR/coherence-with-closure.rs:10:24
+ |
+LL | impl Trait for Wrapper<OpaqueClosure> {}
+ | ^^^^^^^^^^^^^
+ |
+note: type alias impl trait defined here
+ --> $DIR/coherence-with-closure.rs:3:22
+ |
+LL | type OpaqueClosure = impl Sized;
+ | ^^^^^^^^^^
+
+error[E0119]: conflicting implementations of trait `Trait` for type `Wrapper<OpaqueClosure>`
+ --> $DIR/coherence-with-closure.rs:12:1
+ |
+LL | impl Trait for Wrapper<OpaqueClosure> {}
+ | ------------------------------------- first implementation here
+LL |
+LL | impl<T: Sync> Trait for Wrapper<T> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Wrapper<OpaqueClosure>`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0119`.
--- /dev/null
+// Test that encountering closures during coherence does not cause issues.
+#![feature(type_alias_impl_trait, generators)]
+type OpaqueGenerator = impl Sized;
+fn defining_use() -> OpaqueGenerator {
+ || {
+ for i in 0..10 {
+ yield i;
+ }
+ }
+}
+
+struct Wrapper<T>(T);
+trait Trait {}
+impl Trait for Wrapper<OpaqueGenerator> {}
+//~^ ERROR cannot implement trait on type alias impl trait
+impl<T: Sync> Trait for Wrapper<T> {}
+//~^ ERROR conflicting implementations of trait `Trait` for type `Wrapper<OpaqueGenerator>`
+
+fn main() {}
--- /dev/null
+error: cannot implement trait on type alias impl trait
+ --> $DIR/coherence-with-generator.rs:14:24
+ |
+LL | impl Trait for Wrapper<OpaqueGenerator> {}
+ | ^^^^^^^^^^^^^^^
+ |
+note: type alias impl trait defined here
+ --> $DIR/coherence-with-generator.rs:3:24
+ |
+LL | type OpaqueGenerator = impl Sized;
+ | ^^^^^^^^^^
+
+error[E0119]: conflicting implementations of trait `Trait` for type `Wrapper<OpaqueGenerator>`
+ --> $DIR/coherence-with-generator.rs:16:1
+ |
+LL | impl Trait for Wrapper<OpaqueGenerator> {}
+ | --------------------------------------- first implementation here
+LL |
+LL | impl<T: Sync> Trait for Wrapper<T> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Wrapper<OpaqueGenerator>`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0119`.
#![feature(generic_const_exprs)]
#![allow(incomplete_features)]
fn test<const N: usize>() -> [u8; N + (|| 42)()] {}
-//~^ ERROR overly complex generic constant
+//~^ ERROR cycle detected when building an abstract representation
fn main() {}
-error: overly complex generic constant
+error[E0391]: cycle detected when building an abstract representation for test::{constant#0}
--> $DIR/closures.rs:3:35
|
LL | fn test<const N: usize>() -> [u8; N + (|| 42)()] {}
- | ^^^^-------^^
- | |
- | borrowing is not supported in generic constants
+ | ^^^^^^^^^^^^^
|
- = help: consider moving this anonymous constant into a `const` function
- = note: this operation may be supported in the future
+note: ...which requires building THIR for `test::{constant#0}`...
+ --> $DIR/closures.rs:3:35
+ |
+LL | fn test<const N: usize>() -> [u8; N + (|| 42)()] {}
+ | ^^^^^^^^^^^^^
+note: ...which requires type-checking `test::{constant#0}`...
+ --> $DIR/closures.rs:3:35
+ |
+LL | fn test<const N: usize>() -> [u8; N + (|| 42)()] {}
+ | ^^^^^^^^^^^^^
+ = note: ...which again requires building an abstract representation for test::{constant#0}, completing the cycle
+note: cycle used when checking that `test` is well-formed
+ --> $DIR/closures.rs:3:1
+ |
+LL | fn test<const N: usize>() -> [u8; N + (|| 42)()] {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0391`.
writes_to_specific_path(&cap);
//~^ ERROR: the trait bound `(): _Contains<&C>` is not satisfied [E0277]
//~| ERROR: unconstrained generic constant
+ //~| ERROR: mismatched types [E0308]
}
fn writes_to_specific_path<C: Delegates<()>>(cap: &C) {}
LL | impl<T, U> Delegates<U> for T where T: Contains<U, true> {}
| ^^^^^^^^^^^^ ^
note: required by a bound in `writes_to_specific_path`
- --> $DIR/issue-85848.rs:29:31
+ --> $DIR/issue-85848.rs:30:31
|
LL | fn writes_to_specific_path<C: Delegates<()>>(cap: &C) {}
| ^^^^^^^^^^^^^ required by this bound in `writes_to_specific_path`
LL | impl<T, U> Delegates<U> for T where T: Contains<U, true> {}
| ^^^^^^^^^^^^ ^
note: required by a bound in `writes_to_specific_path`
- --> $DIR/issue-85848.rs:29:31
+ --> $DIR/issue-85848.rs:30:31
|
LL | fn writes_to_specific_path<C: Delegates<()>>(cap: &C) {}
| ^^^^^^^^^^^^^ required by this bound in `writes_to_specific_path`
-error: aborting due to 2 previous errors
+error[E0308]: mismatched types
+ --> $DIR/issue-85848.rs:24:5
+ |
+LL | writes_to_specific_path(&cap);
+ | ^^^^^^^^^^^^^^^^^^^^^^^ expected `true`, found `{ contains::<T, U>() }`
+ |
+ = note: expected type `true`
+ found type `{ contains::<T, U>() }`
+
+error: aborting due to 3 previous errors
-For more information about this error, try `rustc --explain E0277`.
+Some errors have detailed explanations: E0277, E0308.
+For more information about an error, try `rustc --explain E0277`.
fn unit_literals() {
z(" ");
//~^ ERROR: the trait bound `&str: X` is not satisfied
+ //~| ERROR: unconstrained generic constant
}
fn main() {}
LL | T: X,
| ^ required by this bound in `z`
-error: aborting due to previous error
+error: unconstrained generic constant
+ --> $DIR/issue-86530.rs:16:5
+ |
+LL | z(" ");
+ | ^
+ |
+ = help: try adding a `where` bound using this expression: `where [(); T::Y]:`
+note: required by a bound in `z`
+ --> $DIR/issue-86530.rs:11:10
+ |
+LL | fn z<T>(t: T)
+ | - required by a bound in this
+...
+LL | [(); T::Y]: ,
+ | ^^^^ required by this bound in `z`
+
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.
--> $DIR/ub-uninhabit.rs:18:1
|
LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .<deref>: encountered a value of uninhabited type Bar
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a reference pointing to uninhabited type Bar
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 4, align: 4) {
--> $DIR/ub-uninhabit.rs:18:1
|
LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .<deref>: encountered a value of uninhabited type Bar
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a reference pointing to uninhabited type Bar
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 8) {
#![feature(const_float_bits_conv)]
#![feature(const_float_classify)]
+#![allow(unused_macro_rules)]
// Don't promote
const fn nop<T>(x: T) -> T { x }
--- /dev/null
+const fn make_fn_ptr() -> fn() {
+ || {}
+}
+
+static STAT: () = make_fn_ptr()();
+//~^ ERROR function pointer
+
+const CONST: () = make_fn_ptr()();
+//~^ ERROR function pointer
+
+const fn call_ptr() {
+ make_fn_ptr()();
+ //~^ ERROR function pointer
+}
+
+fn main() {}
--- /dev/null
+error: function pointer calls are not allowed in statics
+ --> $DIR/const-fn-ptr.rs:5:19
+ |
+LL | static STAT: () = make_fn_ptr()();
+ | ^^^^^^^^^^^^^^^
+
+error: function pointer calls are not allowed in constants
+ --> $DIR/const-fn-ptr.rs:8:19
+ |
+LL | const CONST: () = make_fn_ptr()();
+ | ^^^^^^^^^^^^^^^
+
+error: function pointer calls are not allowed in constant functions
+ --> $DIR/const-fn-ptr.rs:12:5
+ |
+LL | make_fn_ptr()();
+ | ^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
= note: closures need an RFC before allowed to be called in constant functions
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-error: function pointers are not allowed in const fn
+error: function pointer calls are not allowed in constant functions
--> $DIR/issue-56164.rs:7:5
|
LL | input()
static PTR_INT_CAST: () = {
let x = &0 as *const _ as usize;
//~^ ERROR could not evaluate static initializer
- //~| unable to turn pointer into raw bytes
+ //~| "exposing pointers" needs an rfc before being allowed inside constants
let _v = x == x;
};
--> $DIR/ptr_arith.rs:15:13
|
LL | let x = &0 as *const _ as usize;
- | ^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
+ | ^^^^^^^^^^^^^^^^^^^^^^^ "exposing pointers" needs an rfc before being allowed inside constants
error[E0080]: could not evaluate static initializer
--> $DIR/ptr_arith.rs:23:14
// run-pass
#![feature(const_ptr_offset_from)]
+#![feature(const_ptr_sub_ptr)]
+#![feature(ptr_sub_ptr)]
struct Struct {
field: (),
unsafe { ptr.offset_from(ptr) }
};
+pub const OFFSET_UNSIGNED: usize = {
+ let a = ['a', 'b', 'c'];
+ let ptr = a.as_ptr();
+ unsafe { ptr.add(2).sub_ptr(ptr) }
+};
+
fn main() {
assert_eq!(OFFSET, 0);
assert_eq!(OFFSET_2, 1);
assert_eq!(OVERFLOW, -1);
assert_eq!(OFFSET_EQUAL_INTS, 0);
+ assert_eq!(OFFSET_UNSIGNED, 2);
}
#![feature(const_ptr_offset_from)]
#![feature(core_intrinsics)]
-use std::intrinsics::ptr_offset_from;
+use std::intrinsics::{ptr_offset_from, ptr_offset_from_unsigned};
#[repr(C)]
struct Struct {
let uninit2 = std::mem::MaybeUninit::<Struct>::uninit();
let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct;
let offset = unsafe { ptr_offset_from(field_ptr, base_ptr) }; //~ERROR evaluation of constant value failed
- //~| cannot compute offset of pointers into different allocations.
+ //~| ptr_offset_from cannot compute offset of pointers into different allocations.
offset as usize
};
//~| pointer at offset 10 is out-of-bounds
};
+pub const DIFFERENT_ALLOC_UNSIGNED: usize = {
+ let uninit = std::mem::MaybeUninit::<Struct>::uninit();
+ let base_ptr: *const Struct = &uninit as *const _ as *const Struct;
+ let uninit2 = std::mem::MaybeUninit::<Struct>::uninit();
+ let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct;
+ let offset = unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) }; //~ERROR evaluation of constant value failed
+ //~| ptr_offset_from_unsigned cannot compute offset of pointers into different allocations.
+ offset as usize
+};
+
+const WRONG_ORDER_UNSIGNED: usize = {
+ let a = ['a', 'b', 'c'];
+ let p = a.as_ptr();
+ unsafe { ptr_offset_from_unsigned(p, p.add(2) ) } //~ERROR evaluation of constant value failed
+ //~| cannot compute a negative offset, but 0 < 8
+};
+
fn main() {}
LL | unsafe { ptr_offset_from(end_ptr, end_ptr) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc26 has size 4, so pointer at offset 10 is out-of-bounds
-error: aborting due to 8 previous errors
+error[E0080]: evaluation of constant value failed
+ --> $DIR/offset_from_ub.rs:78:27
+ |
+LL | let offset = unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ptr_offset_from_unsigned cannot compute offset of pointers into different allocations.
+
+error[E0080]: evaluation of constant value failed
+ --> $DIR/offset_from_ub.rs:86:14
+ |
+LL | unsafe { ptr_offset_from_unsigned(p, p.add(2) ) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ptr_offset_from_unsigned cannot compute a negative offset, but 0 < 8
+
+error: aborting due to 10 previous errors
For more information about this error, try `rustc --explain E0080`.
--> $DIR/validate_never_arrays.rs:4:1
|
LL | const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .<deref>[0]: encountered a value of the never type `!`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a reference pointing to uninhabited type [!; 1]
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 4, align: 4) {
--> $DIR/validate_never_arrays.rs:4:1
|
LL | const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .<deref>[0]: encountered a value of the never type `!`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a reference pointing to uninhabited type [!; 1]
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 8) {
-#[link(name = "")] //~ ERROR: given with empty name
+#[link(name = "")] //~ ERROR: link name must not be empty
extern "C" {}
fn main() {}
-error[E0454]: `#[link(name = "")]` given with empty name
- --> $DIR/empty-linkname.rs:1:1
+error[E0454]: link name must not be empty
+ --> $DIR/empty-linkname.rs:1:15
|
LL | #[link(name = "")]
- | ^^^^^^^^^^^^^^^^^^ empty name given
+ | ^^ empty link name
error: aborting due to previous error
--> $DIR/E0138.rs:7:1
|
LL | fn foo(argc: isize, argv: *const *const u8) -> isize { 0 }
- | ---------------------------------------------------------- previous `#[start]` function here
+ | ---------------------------------------------------- previous `#[start]` function here
...
LL | fn f(argc: isize, argv: *const *const u8) -> isize { 0 }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ multiple `start` functions
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ multiple `start` functions
error: aborting due to previous error
-error[E0454]: `#[link(name = "")]` given with empty name
- --> $DIR/E0454.rs:1:1
+error[E0454]: link name must not be empty
+ --> $DIR/E0454.rs:1:15
|
LL | #[link(name = "")] extern "C" {}
- | ^^^^^^^^^^^^^^^^^^ empty name given
+ | ^^ empty link name
error: aborting due to previous error
-error[E0458]: unknown kind: `wonderful_unicorn`
- --> $DIR/E0458.rs:1:8
+error[E0458]: unknown link kind `wonderful_unicorn`, expected one of: static, dylib, framework, raw-dylib
+ --> $DIR/E0458.rs:1:15
|
LL | #[link(kind = "wonderful_unicorn")] extern "C" {}
- | -------^^^^^^^^^^^^^^^^^^^^^^^^^^--
- | |
- | unknown kind
+ | ^^^^^^^^^^^^^^^^^^^ unknown link kind
-error[E0459]: `#[link(...)]` specified without `name = "foo"`
+error[E0459]: `#[link]` attribute requires a `name = "string"` argument
--> $DIR/E0458.rs:1:1
|
LL | #[link(kind = "wonderful_unicorn")] extern "C" {}
-error[E0459]: `#[link(...)]` specified without `name = "foo"`
+error[E0459]: `#[link]` attribute requires a `name = "string"` argument
--> $DIR/E0459.rs:1:1
|
LL | #[link(kind = "dylib")] extern "C" {}
use std::arch::asm;
+unsafe fn foo<const N: usize>() {
+ asm!("mov eax, {}", const N + 1);
+ //~^ ERROR const operands for inline assembly are unstable
+}
+
fn main() {
unsafe {
+ foo::<0>();
asm!("mov eax, {}", const 123);
//~^ ERROR const operands for inline assembly are unstable
}
error[E0658]: const operands for inline assembly are unstable
- --> $DIR/feature-gate-asm_const.rs:7:29
+ --> $DIR/feature-gate-asm_const.rs:6:25
+ |
+LL | asm!("mov eax, {}", const N + 1);
+ | ^^^^^^^^^^^
+ |
+ = note: see issue #93332 <https://github.com/rust-lang/rust/issues/93332> for more information
+ = help: add `#![feature(asm_const)]` to the crate attributes to enable
+
+error[E0658]: const operands for inline assembly are unstable
+ --> $DIR/feature-gate-asm_const.rs:13:29
|
LL | asm!("mov eax, {}", const 123);
| ^^^^^^^^^
= note: see issue #93332 <https://github.com/rust-lang/rust/issues/93332> for more information
= help: add `#![feature(asm_const)]` to the crate attributes to enable
-error: aborting due to previous error
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0658`.
use std::arch::asm;
+fn bar<const N: usize>() {}
+
+fn foo<const N: usize>() {
+ unsafe {
+ asm!("mov eax, {}", sym bar::<N>);
+ //~^ ERROR sym operands for inline assembly are unstable
+ }
+}
+
fn main() {
unsafe {
- asm!("mov eax, {}", sym main);
+ asm!("mov eax, {}", sym foo::<0>);
//~^ ERROR sym operands for inline assembly are unstable
}
}
error[E0658]: sym operands for inline assembly are unstable
- --> $DIR/feature-gate-asm_sym.rs:7:29
+ --> $DIR/feature-gate-asm_sym.rs:9:29
|
-LL | asm!("mov eax, {}", sym main);
- | ^^^^^^^^
+LL | asm!("mov eax, {}", sym bar::<N>);
+ | ^^^^^^^^^^^^
|
= note: see issue #93333 <https://github.com/rust-lang/rust/issues/93333> for more information
= help: add `#![feature(asm_sym)]` to the crate attributes to enable
-error: aborting due to previous error
+error[E0658]: sym operands for inline assembly are unstable
+ --> $DIR/feature-gate-asm_sym.rs:16:29
+ |
+LL | asm!("mov eax, {}", sym foo::<0>);
+ | ^^^^^^^^^^^^
+ |
+ = note: see issue #93333 <https://github.com/rust-lang/rust/issues/93333> for more information
+ = help: add `#![feature(asm_sym)]` to the crate attributes to enable
+
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0658`.
-error[E0658]: kind="link_cfg" is unstable
- --> $DIR/feature-gate-link_cfg.rs:1:1
+error[E0658]: link cfg is unstable
+ --> $DIR/feature-gate-link_cfg.rs:1:22
|
LL | #[link(name = "foo", cfg(foo))]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^
|
= note: see issue #37406 <https://github.com/rust-lang/rust/issues/37406> for more information
= help: add `#![feature(link_cfg)]` to the crate attributes to enable
-#[link(name = "foo", modifiers = "+as-needed")]
-//~^ ERROR: `#[link(modifiers="as-needed")]` is unstable
+#[link(name = "foo", kind = "dylib", modifiers = "+as-needed")]
+//~^ ERROR: linking modifier `as-needed` is unstable
extern "C" {}
fn main() {}
-error[E0658]: `#[link(modifiers="as-needed")]` is unstable
- --> $DIR/feature-gate-native_link_modifiers_as_needed.rs:1:34
+error[E0658]: linking modifier `as-needed` is unstable
+ --> $DIR/feature-gate-native_link_modifiers_as_needed.rs:1:50
|
-LL | #[link(name = "foo", modifiers = "+as-needed")]
- | ^^^^^^^^^^^^
+LL | #[link(name = "foo", kind = "dylib", modifiers = "+as-needed")]
+ | ^^^^^^^^^^^^
|
= note: see issue #81490 <https://github.com/rust-lang/rust/issues/81490> for more information
= help: add `#![feature(native_link_modifiers_as_needed)]` to the crate attributes to enable
-error: bundle linking modifier is currently unstable and only accepted on the nightly compiler
+error: linking modifier `bundle` is unstable and only accepted on the nightly compiler
-#[link(name = "foo", modifiers = "+bundle")]
-//~^ ERROR: `#[link(modifiers="bundle")]` is unstable
+#[link(name = "foo", kind = "static", modifiers = "+bundle")]
+//~^ ERROR: linking modifier `bundle` is unstable
extern "C" {}
fn main() {}
-error[E0658]: `#[link(modifiers="bundle")]` is unstable
- --> $DIR/feature-gate-native_link_modifiers_bundle.rs:1:34
+error[E0658]: linking modifier `bundle` is unstable
+ --> $DIR/feature-gate-native_link_modifiers_bundle.rs:1:51
|
-LL | #[link(name = "foo", modifiers = "+bundle")]
- | ^^^^^^^^^
+LL | #[link(name = "foo", kind = "static", modifiers = "+bundle")]
+ | ^^^^^^^^^
|
= note: see issue #81490 <https://github.com/rust-lang/rust/issues/81490> for more information
= help: add `#![feature(native_link_modifiers_bundle)]` to the crate attributes to enable
#[link(name = "foo", modifiers = "+verbatim")]
-//~^ ERROR: `#[link(modifiers="verbatim")]` is unstable
+//~^ ERROR: linking modifier `verbatim` is unstable
extern "C" {}
fn main() {}
-error[E0658]: `#[link(modifiers="verbatim")]` is unstable
+error[E0658]: linking modifier `verbatim` is unstable
--> $DIR/feature-gate-native_link_modifiers_verbatim.rs:1:34
|
LL | #[link(name = "foo", modifiers = "+verbatim")]
// only-windows
#[link(name = "foo", kind = "raw-dylib")]
-//~^ ERROR: kind="raw-dylib" is unstable
+//~^ ERROR: link kind `raw-dylib` is unstable
extern "C" {}
fn main() {}
-error[E0658]: kind="raw-dylib" is unstable
- --> $DIR/feature-gate-raw-dylib.rs:2:1
+error[E0658]: link kind `raw-dylib` is unstable
+ --> $DIR/feature-gate-raw-dylib.rs:2:29
|
LL | #[link(name = "foo", kind = "raw-dylib")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^
|
= note: see issue #58713 <https://github.com/rust-lang/rust/issues/58713> for more information
= help: add `#![feature(raw_dylib)]` to the crate attributes to enable
-warning: library kind `static-nobundle` has been superseded by specifying `-bundle` on library kind `static`. Try `static:-bundle`
+warning: library kind `static-nobundle` has been superseded by specifying modifier `-bundle` with library kind `static`. Try `static:-bundle`
#[link(name = "foo", kind = "static-nobundle")]
-//~^ WARNING: library kind `static-nobundle` has been superseded by specifying modifier `-bundle` with library kind `static`
-//~^^ ERROR: kind="static-nobundle" is unstable
+//~^ WARNING: link kind `static-nobundle` has been superseded by specifying modifier `-bundle` with link kind `static`
+//~^^ ERROR: link kind `static-nobundle` is unstable
extern "C" {}
fn main() {}
-warning: library kind `static-nobundle` has been superseded by specifying modifier `-bundle` with library kind `static`
- --> $DIR/feature-gate-static-nobundle.rs:1:22
+warning: link kind `static-nobundle` has been superseded by specifying modifier `-bundle` with link kind `static`
+ --> $DIR/feature-gate-static-nobundle.rs:1:29
|
LL | #[link(name = "foo", kind = "static-nobundle")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^
-error[E0658]: kind="static-nobundle" is unstable
- --> $DIR/feature-gate-static-nobundle.rs:1:22
+error[E0658]: link kind `static-nobundle` is unstable
+ --> $DIR/feature-gate-static-nobundle.rs:1:29
|
LL | #[link(name = "foo", kind = "static-nobundle")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^
|
= note: see issue #37403 <https://github.com/rust-lang/rust/issues/37403> for more information
= help: add `#![feature(static_nobundle)]` to the crate attributes to enable
-error[E0658]: trait upcasting coercion is experimental
+error[E0658]: cannot cast `dyn Bar` to `dyn Foo`, trait upcasting coercion is experimental
--> $DIR/feature-gate-trait_upcasting.rs:11:25
|
LL | let foo: &dyn Foo = bar;
|
= note: see issue #65991 <https://github.com/rust-lang/rust/issues/65991> for more information
= help: add `#![feature(trait_upcasting)]` to the crate attributes to enable
+ = note: required when coercing `&dyn Bar` into `&dyn Foo`
error: aborting due to previous error
//~^ WARN attribute should be applied to an `extern` block
//~| WARN this was previously accepted
//~| NOTE not an `extern` block
+
+ #[link()] extern "Rust" {}
+ //~^ WARN attribute should be applied to an `extern` block
+ //~| WARN this was previously accepted
}
struct StructForDeprecated;
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-warning: attribute should be applied to an `extern` block
+warning: attribute should be applied to an `extern` block with non-Rust ABI
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:554:1
|
LL | #[link()]
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
warning: `#[must_use]` has no effect when applied to a module
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:601:1
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:605:1
|
LL | #[must_use]
| ^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:614:1
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:618:1
|
LL | #[windows_subsystem = "windows"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:635:1
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:639:1
|
LL | #[crate_name = "0900"]
| ^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:654:1
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:658:1
|
LL | #[crate_type = "0800"]
| ^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:673:1
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:677:1
|
LL | #[feature(x0600)]
| ^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:693:1
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:697:1
|
LL | #[no_main]
| ^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:712:1
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:716:1
|
LL | #[no_builtins]
| ^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:731:1
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:735:1
|
LL | #[recursion_limit="0200"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:750:1
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:754:1
|
LL | #[type_length_limit="0100"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-warning: attribute should be applied to an `extern` block
+warning: attribute should be applied to an `extern` block with non-Rust ABI
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:64:1
|
LL | #![link()]
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-warning: attribute should be applied to an `extern` block
+warning: attribute should be applied to an `extern` block with non-Rust ABI
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:560:17
|
LL | mod inner { #![link()] }
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-warning: attribute should be applied to an `extern` block
+warning: attribute should be applied to an `extern` block with non-Rust ABI
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:565:5
|
LL | #[link()] fn f() { }
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-warning: attribute should be applied to an `extern` block
+warning: attribute should be applied to an `extern` block with non-Rust ABI
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:570:5
|
LL | #[link()] struct S;
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-warning: attribute should be applied to an `extern` block
+warning: attribute should be applied to an `extern` block with non-Rust ABI
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:575:5
|
LL | #[link()] type T = S;
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-warning: attribute should be applied to an `extern` block
+warning: attribute should be applied to an `extern` block with non-Rust ABI
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:580:5
|
LL | #[link()] impl S { }
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+warning: attribute should be applied to an `extern` block with non-Rust ABI
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:585:5
+ |
+LL | #[link()] extern "Rust" {}
+ | ^^^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+
warning: `#[must_use]` has no effect when applied to a module
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:603:17
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:607:17
|
LL | mod inner { #![must_use] }
| ^^^^^^^^^^^^
warning: `#[must_use]` has no effect when applied to a type alias
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:609:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:613:5
|
LL | #[must_use] type T = S;
| ^^^^^^^^^^^
warning: `#[must_use]` has no effect when applied to an item
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:611:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:615:5
|
LL | #[must_use] impl S { }
| ^^^^^^^^^^^
warning: crate-level attribute should be in the root module
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:617:17
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:621:17
|
LL | mod inner { #![windows_subsystem="windows"] }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:620:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:624:5
|
LL | #[windows_subsystem = "windows"] fn f() { }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:623:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:627:5
|
LL | #[windows_subsystem = "windows"] struct S;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:626:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:630:5
|
LL | #[windows_subsystem = "windows"] type T = S;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:629:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:633:5
|
LL | #[windows_subsystem = "windows"] impl S { }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be in the root module
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:638:17
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:642:17
|
LL | mod inner { #![crate_name="0900"] }
| ^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:641:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:645:5
|
LL | #[crate_name = "0900"] fn f() { }
| ^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:644:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:648:5
|
LL | #[crate_name = "0900"] struct S;
| ^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:647:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:651:5
|
LL | #[crate_name = "0900"] type T = S;
| ^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:650:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:654:5
|
LL | #[crate_name = "0900"] impl S { }
| ^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be in the root module
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:657:17
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:661:17
|
LL | mod inner { #![crate_type="0800"] }
| ^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:660:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:664:5
|
LL | #[crate_type = "0800"] fn f() { }
| ^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:663:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:667:5
|
LL | #[crate_type = "0800"] struct S;
| ^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:666:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:670:5
|
LL | #[crate_type = "0800"] type T = S;
| ^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:669:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:673:5
|
LL | #[crate_type = "0800"] impl S { }
| ^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be in the root module
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:676:17
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:680:17
|
LL | mod inner { #![feature(x0600)] }
| ^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:679:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:683:5
|
LL | #[feature(x0600)] fn f() { }
| ^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:682:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:686:5
|
LL | #[feature(x0600)] struct S;
| ^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:685:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:689:5
|
LL | #[feature(x0600)] type T = S;
| ^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:688:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:692:5
|
LL | #[feature(x0600)] impl S { }
| ^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be in the root module
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:696:17
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:700:17
|
LL | mod inner { #![no_main] }
| ^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:699:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:703:5
|
LL | #[no_main] fn f() { }
| ^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:702:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:706:5
|
LL | #[no_main] struct S;
| ^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:705:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:709:5
|
LL | #[no_main] type T = S;
| ^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:708:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:712:5
|
LL | #[no_main] impl S { }
| ^^^^^^^^^^
warning: crate-level attribute should be in the root module
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:715:17
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:719:17
|
LL | mod inner { #![no_builtins] }
| ^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:718:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:722:5
|
LL | #[no_builtins] fn f() { }
| ^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:721:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:725:5
|
LL | #[no_builtins] struct S;
| ^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:724:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:728:5
|
LL | #[no_builtins] type T = S;
| ^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:727:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:731:5
|
LL | #[no_builtins] impl S { }
| ^^^^^^^^^^^^^^
warning: crate-level attribute should be in the root module
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:734:17
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:738:17
|
LL | mod inner { #![recursion_limit="0200"] }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:737:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:741:5
|
LL | #[recursion_limit="0200"] fn f() { }
| ^^^^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:740:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:744:5
|
LL | #[recursion_limit="0200"] struct S;
| ^^^^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:743:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:747:5
|
LL | #[recursion_limit="0200"] type T = S;
| ^^^^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:746:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:750:5
|
LL | #[recursion_limit="0200"] impl S { }
| ^^^^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be in the root module
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:753:17
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:757:17
|
LL | mod inner { #![type_length_limit="0100"] }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:756:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:760:5
|
LL | #[type_length_limit="0100"] fn f() { }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:759:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:763:5
|
LL | #[type_length_limit="0100"] struct S;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:762:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:766:5
|
LL | #[type_length_limit="0100"] type T = S;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:765:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:769:5
|
LL | #[type_length_limit="0100"] impl S { }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(stable_features)]` on by default
-warning: 172 warnings emitted
+warning: 173 warnings emitted
--- /dev/null
+error: higher-ranked lifetime error
+ --> $DIR/issue-59311.rs:17:5
+ |
+LL | v.t(|| {});
+ | ^^^^^^^^^^
+ |
+ = note: could not prove [closure@$DIR/issue-59311.rs:17:9: 17:14] well-formed
+
+error: higher-ranked lifetime error
+ --> $DIR/issue-59311.rs:17:9
+ |
+LL | v.t(|| {});
+ | ^^^^^
+ |
+ = note: could not prove for<'a> &'a V: 'static
+
+error: aborting due to 2 previous errors
+
where
for<'a> &'a V: T + 'static,
{
- v.t(|| {}); //~ ERROR: higher-ranked lifetime error
+ v.t(|| {}); //~ ERROR: `&'a V` does not fulfill the required lifetime
}
fn main() {}
-error: higher-ranked lifetime error
- --> $DIR/issue-59311.rs:17:9
+error[E0477]: the type `&'a V` does not fulfill the required lifetime
+ --> $DIR/issue-59311.rs:17:5
|
LL | v.t(|| {});
- | ^^^^^
+ | ^^^^^^^^^^
|
- = note: could not prove for<'a> &'a V: 'static
+note: type must satisfy the static lifetime as required by this binding
+ --> $DIR/issue-59311.rs:15:24
+ |
+LL | for<'a> &'a V: T + 'static,
+ | ^^^^^^^
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0477`.
fn main() {
foo::<Vec<u32>>(vec![]);
}
+
+mod another {
+ use std::ops::Deref;
+
+ fn test<T, TDeref>()
+ where
+ T: Deref<Target = TDeref>,
+ TDeref: ?Sized,
+ for<'a> &'a TDeref: IntoIterator,
+ for<'a> <&'a TDeref as IntoIterator>::IntoIter: Clone,
+ {
+ }
+
+ fn main() {
+ test::<Vec<u8>, _>();
+ }
+}
--- /dev/null
+// check-pass
+
+trait Variable<'a> {
+ type Type;
+}
+
+impl Variable<'_> for () {
+ type Type = ();
+}
+
+fn check<F, T>(_: F)
+where
+ F: Fn(T), // <- if removed, all fn_* then require type annotations
+ F: for<'a> Fn(<T as Variable<'a>>::Type),
+ T: for<'a> Variable<'a>,
+{
+}
+
+fn test(arg: impl Fn(())) {
+ fn fn_1(_: ()) {}
+ let fn_2 = |_: ()| ();
+ let fn_3 = |a| fn_1(a);
+ let fn_4 = arg;
+
+ check(fn_1); // Error
+ check(fn_2); // Ok
+ check(fn_3); // Ok
+ check(fn_4); // Error
+}
+
+fn main() {}
-// check-pass
-
trait Trait<'a> {
type Out;
fn call(&'a self) -> Self::Out;
}
fn f() -> impl for<'a> Trait<'a, Out = impl Sized + 'a> {
+ //~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet
X(())
}
--- /dev/null
+error: higher kinded lifetime bounds on nested opaque types are not supported yet
+ --> $DIR/issue-54895.rs:15:53
+ |
+LL | fn f() -> impl for<'a> Trait<'a, Out = impl Sized + 'a> {
+ | ^^
+ |
+note: lifetime declared here
+ --> $DIR/issue-54895.rs:15:20
+ |
+LL | fn f() -> impl for<'a> Trait<'a, Out = impl Sized + 'a> {
+ | ^^
+
+error: aborting due to previous error
+
+++ /dev/null
-error: implementation of `FnOnce` is not general enough
- --> $DIR/issue-67830.rs:23:5
- |
-LL | Wrap(|a| Some(a).into_iter())
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
- |
- = note: closure with signature `fn(&'2 A) -> std::option::IntoIter<&A>` must implement `FnOnce<(&'1 A,)>`, for any lifetime `'1`...
- = note: ...but it actually implements `FnOnce<(&'2 A,)>`, for some specific lifetime `'2`
-
-error: implementation of `FnOnce` is not general enough
- --> $DIR/issue-67830.rs:23:5
- |
-LL | Wrap(|a| Some(a).into_iter())
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
- |
- = note: closure with signature `fn(&'2 A) -> std::option::IntoIter<&A>` must implement `FnOnce<(&'1 A,)>`, for any lifetime `'1`...
- = note: ...but it actually implements `FnOnce<(&'2 A,)>`, for some specific lifetime `'2`
-
-error: aborting due to 2 previous errors
-
struct A;
fn test() -> impl for<'a> MyFn<&'a A, Output=impl Iterator + 'a> {
- //~^ ERROR implementation of `FnOnce` is not general enough
+ //~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet
Wrap(|a| Some(a).into_iter())
}
-error: implementation of `FnOnce` is not general enough
- --> $DIR/issue-67830.rs:21:14
+error: higher kinded lifetime bounds on nested opaque types are not supported yet
+ --> $DIR/issue-67830.rs:21:62
|
LL | fn test() -> impl for<'a> MyFn<&'a A, Output=impl Iterator + 'a> {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
+ | ^^
|
- = note: closure with signature `fn(&'2 A) -> std::option::IntoIter<&A>` must implement `FnOnce<(&'1 A,)>`, for any lifetime `'1`...
- = note: ...but it actually implements `FnOnce<(&'2 A,)>`, for some specific lifetime `'2`
+note: lifetime declared here
+ --> $DIR/issue-67830.rs:21:23
+ |
+LL | fn test() -> impl for<'a> MyFn<&'a A, Output=impl Iterator + 'a> {
+ | ^^
error: aborting due to previous error
+++ /dev/null
-error: implementation of `Hrtb` is not general enough
- --> $DIR/issue-88236-2.rs:17:5
- |
-LL | &()
- | ^^^ implementation of `Hrtb` is not general enough
- |
- = note: `Hrtb<'0>` would have to be implemented for the type `&()`, for any lifetime `'0`...
- = note: ...but `Hrtb<'1>` is actually implemented for the type `&'1 ()`, for some specific lifetime `'1`
-
-error: implementation of `Hrtb` is not general enough
- --> $DIR/issue-88236-2.rs:17:5
- |
-LL | &()
- | ^^^ implementation of `Hrtb` is not general enough
- |
- = note: `Hrtb<'0>` would have to be implemented for the type `&()`, for any lifetime `'0`...
- = note: ...but `Hrtb<'1>` is actually implemented for the type `&'1 ()`, for some specific lifetime `'1`
-
-error: lifetime may not live long enough
- --> $DIR/issue-88236-2.rs:20:5
- |
-LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {
- | -- lifetime `'b` defined here
-LL | x
- | ^ returning this value requires that `'b` must outlive `'static`
- |
-help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'b` lifetime bound
- |
-LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> + 'b {
- | ++++
-help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'b` lifetime bound
- |
-LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a + 'b> {
- | ++++
-
-error: implementation of `Hrtb` is not general enough
- --> $DIR/issue-88236-2.rs:20:5
- |
-LL | x
- | ^ implementation of `Hrtb` is not general enough
- |
- = note: `Hrtb<'0>` would have to be implemented for the type `&()`, for any lifetime `'0`...
- = note: ...but `Hrtb<'1>` is actually implemented for the type `&'1 ()`, for some specific lifetime `'1`
-
-error: implementation of `Hrtb` is not general enough
- --> $DIR/issue-88236-2.rs:20:5
- |
-LL | x
- | ^ implementation of `Hrtb` is not general enough
- |
- = note: `Hrtb<'0>` would have to be implemented for the type `&()`, for any lifetime `'0`...
- = note: ...but `Hrtb<'1>` is actually implemented for the type `&'1 ()`, for some specific lifetime `'1`
-
-error: aborting due to 5 previous errors
-
}
fn make_impl() -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {}
+//~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet
+
fn make_weird_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {
- &() //~^ ERROR implementation of `Hrtb` is not general enough
+ //~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet
+ &()
}
+
fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {
- x //~^ ERROR implementation of `Hrtb` is not general enough
+ //~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet
+ x
}
fn main() {}
-error: implementation of `Hrtb` is not general enough
- --> $DIR/issue-88236-2.rs:16:38
+error: higher kinded lifetime bounds on nested opaque types are not supported yet
+ --> $DIR/issue-88236-2.rs:15:61
+ |
+LL | fn make_impl() -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {}
+ | ^^
+ |
+note: lifetime declared here
+ --> $DIR/issue-88236-2.rs:15:28
+ |
+LL | fn make_impl() -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {}
+ | ^^
+
+error: higher kinded lifetime bounds on nested opaque types are not supported yet
+ --> $DIR/issue-88236-2.rs:18:80
|
LL | fn make_weird_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Hrtb` is not general enough
+ | ^^
|
- = note: `Hrtb<'0>` would have to be implemented for the type `&()`, for any lifetime `'0`...
- = note: ...but `Hrtb<'1>` is actually implemented for the type `&'1 ()`, for some specific lifetime `'1`
+note: lifetime declared here
+ --> $DIR/issue-88236-2.rs:18:47
+ |
+LL | fn make_weird_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {
+ | ^^
-error: implementation of `Hrtb` is not general enough
- --> $DIR/issue-88236-2.rs:19:36
+error: higher kinded lifetime bounds on nested opaque types are not supported yet
+ --> $DIR/issue-88236-2.rs:23:78
|
LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Hrtb` is not general enough
+ | ^^
|
- = note: `Hrtb<'1>` would have to be implemented for the type `&()`, for any lifetime `'1`...
- = note: ...but `Hrtb<'_>` is actually implemented for the type `&()`
+note: lifetime declared here
+ --> $DIR/issue-88236-2.rs:23:45
+ |
+LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {
+ | ^^
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
-// check-pass
-
// this used to cause stack overflows
trait Hrtb<'a> {
}
fn make_impl() -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {}
+//~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet
fn main() {}
--- /dev/null
+error: higher kinded lifetime bounds on nested opaque types are not supported yet
+ --> $DIR/issue-88236.rs:15:61
+ |
+LL | fn make_impl() -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {}
+ | ^^
+ |
+note: lifetime declared here
+ --> $DIR/issue-88236.rs:15:28
+ |
+LL | fn make_impl() -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {}
+ | ^^
+
+error: aborting due to previous error
+
--- /dev/null
+// Test the interaction between rested RPIT and HRTB.
+
+trait Foo<'a> {
+ type Assoc;
+}
+
+impl Foo<'_> for () {
+ type Assoc = ();
+}
+
+// Alternative version of `Foo` whose impl uses `'a`.
+trait Bar<'a> {
+ type Assoc;
+}
+
+impl<'a> Bar<'a> for () {
+ type Assoc = &'a ();
+}
+
+trait Qux<'a> {}
+
+impl Qux<'_> for () {}
+
+// This is not supported.
+fn one_hrtb_outlives() -> impl for<'a> Foo<'a, Assoc = impl Sized + 'a> {}
+//~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet
+
+// This is not supported.
+fn one_hrtb_trait_param() -> impl for<'a> Foo<'a, Assoc = impl Qux<'a>> {}
+//~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet
+
+fn one_hrtb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'a> {}
+//~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet
+
+fn one_hrtb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl Qux<'a>> {}
+//~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet
+
+// This should pass.
+fn one_hrtb_mention_fn_trait_param<'b>() -> impl for<'a> Foo<'a, Assoc = impl Qux<'b>> {}
+
+// This should pass.
+fn one_hrtb_mention_fn_outlives<'b>() -> impl for<'a> Foo<'a, Assoc = impl Sized + 'b> {}
+
+// This should pass.
+fn one_hrtb_mention_fn_trait_param_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Qux<'b>> {}
+
+// This should pass.
+fn one_hrtb_mention_fn_outlives_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'b> {}
+
+// This should pass.
+fn two_htrb_trait_param() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Qux<'b>> {}
+
+// `'b` is not in scope for the outlives bound.
+fn two_htrb_outlives() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Sized + 'b> {}
+//~^ ERROR use of undeclared lifetime name `'b` [E0261]
+
+// This should pass.
+fn two_htrb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Qux<'b>> {}
+
+// `'b` is not in scope for the outlives bound.
+fn two_htrb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Sized + 'b> {}
+//~^ ERROR use of undeclared lifetime name `'b` [E0261]
+
+fn main() {}
--- /dev/null
+error[E0261]: use of undeclared lifetime name `'b`
+ --> $DIR/nested-rpit-hrtb.rs:54:77
+ |
+LL | fn two_htrb_outlives() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Sized + 'b> {}
+ | ^^ undeclared lifetime
+ |
+ = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
+help: consider making the bound lifetime-generic with a new `'b` lifetime
+ |
+LL | fn two_htrb_outlives() -> impl for<'b, 'a> Foo<'a, Assoc = impl for<'b> Sized + 'b> {}
+ | +++
+help: consider introducing lifetime `'b` here
+ |
+LL | fn two_htrb_outlives<'b>() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Sized + 'b> {}
+ | ++++
+
+error[E0261]: use of undeclared lifetime name `'b`
+ --> $DIR/nested-rpit-hrtb.rs:61:82
+ |
+LL | fn two_htrb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Sized + 'b> {}
+ | ^^ undeclared lifetime
+ |
+help: consider making the bound lifetime-generic with a new `'b` lifetime
+ |
+LL | fn two_htrb_outlives_uses() -> impl for<'b, 'a> Bar<'a, Assoc = impl for<'b> Sized + 'b> {}
+ | +++
+help: consider introducing lifetime `'b` here
+ |
+LL | fn two_htrb_outlives_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Sized + 'b> {}
+ | ++++
+
+error: higher kinded lifetime bounds on nested opaque types are not supported yet
+ --> $DIR/nested-rpit-hrtb.rs:25:69
+ |
+LL | fn one_hrtb_outlives() -> impl for<'a> Foo<'a, Assoc = impl Sized + 'a> {}
+ | ^^
+ |
+note: lifetime declared here
+ --> $DIR/nested-rpit-hrtb.rs:25:36
+ |
+LL | fn one_hrtb_outlives() -> impl for<'a> Foo<'a, Assoc = impl Sized + 'a> {}
+ | ^^
+
+error: higher kinded lifetime bounds on nested opaque types are not supported yet
+ --> $DIR/nested-rpit-hrtb.rs:29:68
+ |
+LL | fn one_hrtb_trait_param() -> impl for<'a> Foo<'a, Assoc = impl Qux<'a>> {}
+ | ^^
+ |
+note: lifetime declared here
+ --> $DIR/nested-rpit-hrtb.rs:29:39
+ |
+LL | fn one_hrtb_trait_param() -> impl for<'a> Foo<'a, Assoc = impl Qux<'a>> {}
+ | ^^
+
+error: higher kinded lifetime bounds on nested opaque types are not supported yet
+ --> $DIR/nested-rpit-hrtb.rs:32:74
+ |
+LL | fn one_hrtb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'a> {}
+ | ^^
+ |
+note: lifetime declared here
+ --> $DIR/nested-rpit-hrtb.rs:32:41
+ |
+LL | fn one_hrtb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'a> {}
+ | ^^
+
+error: higher kinded lifetime bounds on nested opaque types are not supported yet
+ --> $DIR/nested-rpit-hrtb.rs:35:73
+ |
+LL | fn one_hrtb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl Qux<'a>> {}
+ | ^^
+ |
+note: lifetime declared here
+ --> $DIR/nested-rpit-hrtb.rs:35:44
+ |
+LL | fn one_hrtb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl Qux<'a>> {}
+ | ^^
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0261`.
--- /dev/null
+// compile-flags: --test
+
+#![deny(unused_imports)]
+
+use std::io::BufRead; //~ ERROR unused import: `std::io::BufRead`
+
+fn a() {}
+fn b() {}
+
+mod test {
+ use super::a; //~ ERROR unused import: `super::a`
+
+ fn foo() {
+ use crate::b; //~ ERROR unused import: `crate::b`
+ }
+}
+
+mod tests {
+ use super::a; //~ ERROR unused import: `super::a`
+
+ fn foo() {
+ use crate::b; //~ ERROR unused import: `crate::b`
+ }
+}
+
+mod test_a {
+ use super::a; //~ ERROR unused import: `super::a`
+
+ fn foo() {
+ use crate::b; //~ ERROR unused import: `crate::b`
+ }
+}
+
+mod a_test {
+ use super::a; //~ ERROR unused import: `super::a`
+
+ fn foo() {
+ use crate::b; //~ ERROR unused import: `crate::b`
+ }
+}
+
+mod tests_a {
+ use super::a; //~ ERROR unused import: `super::a`
+
+ fn foo() {
+ use crate::b; //~ ERROR unused import: `crate::b`
+ }
+}
+
+mod a_tests {
+ use super::a; //~ ERROR unused import: `super::a`
+
+ fn foo() {
+ use crate::b; //~ ERROR unused import: `crate::b`
+ }
+}
+
+mod fastest_search {
+ use super::a; //~ ERROR unused import: `super::a`
+
+ fn foo() {
+ use crate::b; //~ ERROR unused import: `crate::b`
+ }
+}
+
+#[cfg(test)]
+mod test_has_attr {
+ use super::a; //~ ERROR unused import: `super::a`
+
+ fn foo() {
+ use crate::b; //~ ERROR unused import: `crate::b`
+ }
+}
+
+mod test_has_no_attr {
+ #[cfg(test)]
+ use super::a; //~ ERROR unused import: `super::a`
+
+ fn foo() {
+ use crate::b; //~ ERROR unused import: `crate::b`
+ }
+}
+
+fn main() {}
--- /dev/null
+error: unused import: `std::io::BufRead`
+ --> $DIR/unused-imports-in-test-mode.rs:5:5
+ |
+LL | use std::io::BufRead;
+ | ^^^^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/unused-imports-in-test-mode.rs:3:9
+ |
+LL | #![deny(unused_imports)]
+ | ^^^^^^^^^^^^^^
+
+error: unused import: `super::a`
+ --> $DIR/unused-imports-in-test-mode.rs:11:9
+ |
+LL | use super::a;
+ | ^^^^^^^^
+
+error: unused import: `crate::b`
+ --> $DIR/unused-imports-in-test-mode.rs:14:13
+ |
+LL | use crate::b;
+ | ^^^^^^^^
+
+error: unused import: `super::a`
+ --> $DIR/unused-imports-in-test-mode.rs:19:9
+ |
+LL | use super::a;
+ | ^^^^^^^^
+
+error: unused import: `crate::b`
+ --> $DIR/unused-imports-in-test-mode.rs:22:13
+ |
+LL | use crate::b;
+ | ^^^^^^^^
+
+error: unused import: `super::a`
+ --> $DIR/unused-imports-in-test-mode.rs:27:9
+ |
+LL | use super::a;
+ | ^^^^^^^^
+
+error: unused import: `crate::b`
+ --> $DIR/unused-imports-in-test-mode.rs:30:13
+ |
+LL | use crate::b;
+ | ^^^^^^^^
+
+error: unused import: `super::a`
+ --> $DIR/unused-imports-in-test-mode.rs:35:9
+ |
+LL | use super::a;
+ | ^^^^^^^^
+
+error: unused import: `crate::b`
+ --> $DIR/unused-imports-in-test-mode.rs:38:13
+ |
+LL | use crate::b;
+ | ^^^^^^^^
+
+error: unused import: `super::a`
+ --> $DIR/unused-imports-in-test-mode.rs:43:9
+ |
+LL | use super::a;
+ | ^^^^^^^^
+
+error: unused import: `crate::b`
+ --> $DIR/unused-imports-in-test-mode.rs:46:13
+ |
+LL | use crate::b;
+ | ^^^^^^^^
+
+error: unused import: `super::a`
+ --> $DIR/unused-imports-in-test-mode.rs:51:9
+ |
+LL | use super::a;
+ | ^^^^^^^^
+
+error: unused import: `crate::b`
+ --> $DIR/unused-imports-in-test-mode.rs:54:13
+ |
+LL | use crate::b;
+ | ^^^^^^^^
+
+error: unused import: `super::a`
+ --> $DIR/unused-imports-in-test-mode.rs:59:9
+ |
+LL | use super::a;
+ | ^^^^^^^^
+
+error: unused import: `crate::b`
+ --> $DIR/unused-imports-in-test-mode.rs:62:13
+ |
+LL | use crate::b;
+ | ^^^^^^^^
+
+error: unused import: `super::a`
+ --> $DIR/unused-imports-in-test-mode.rs:68:9
+ |
+LL | use super::a;
+ | ^^^^^^^^
+
+error: unused import: `crate::b`
+ --> $DIR/unused-imports-in-test-mode.rs:71:13
+ |
+LL | use crate::b;
+ | ^^^^^^^^
+
+error: unused import: `super::a`
+ --> $DIR/unused-imports-in-test-mode.rs:77:9
+ |
+LL | use super::a;
+ | ^^^^^^^^
+
+error: unused import: `crate::b`
+ --> $DIR/unused-imports-in-test-mode.rs:80:13
+ |
+LL | use crate::b;
+ | ^^^^^^^^
+
+error: aborting due to 19 previous errors
+
--- /dev/null
+// check-pass
+
+use std::cell::RefCell;
+
+pub fn f(v: Vec<RefCell<u8>>) {
+ let _t = &mut *v[0].borrow_mut();
+}
+
+fn main() {}
fn foo<const V: usize>() {
match 0 {
const { V } => {},
- //~^ ERROR const parameters cannot be referenced in patterns [E0158]
+ //~^ ERROR constant pattern depends on a generic parameter
+ //~| ERROR constant pattern depends on a generic parameter
_ => {},
}
}
-error[E0158]: const parameters cannot be referenced in patterns
+error: constant pattern depends on a generic parameter
--> $DIR/const-match-pat-generic.rs:8:9
|
LL | const { V } => {},
| ^^^^^^^^^^^
error: constant pattern depends on a generic parameter
- --> $DIR/const-match-pat-generic.rs:20:9
+ --> $DIR/const-match-pat-generic.rs:21:9
|
LL | const { f(V) } => {},
| ^^^^^^^^^^^^^^
error: constant pattern depends on a generic parameter
- --> $DIR/const-match-pat-generic.rs:20:9
+ --> $DIR/const-match-pat-generic.rs:8:9
+ |
+LL | const { V } => {},
+ | ^^^^^^^^^^^
+
+error: constant pattern depends on a generic parameter
+ --> $DIR/const-match-pat-generic.rs:21:9
|
LL | const { f(V) } => {},
| ^^^^^^^^^^^^^^
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
-For more information about this error, try `rustc --explain E0158`.
-error[E0658]: trait upcasting coercion is experimental
+error[E0658]: cannot cast `dyn Fn()` to `dyn FnMut()`, trait upcasting coercion is experimental
--> $DIR/issue-11515.rs:9:33
|
LL | let test = box Test { func: closure };
|
= note: see issue #65991 <https://github.com/rust-lang/rust/issues/65991> for more information
= help: add `#![feature(trait_upcasting)]` to the crate attributes to enable
+ = note: required when coercing `Box<(dyn Fn() + 'static)>` into `Box<(dyn FnMut() + 'static)>`
error: aborting due to previous error
// build-pass
+// compiler-opts: -Zmir-opt-level=2
+
#![allow(dead_code)]
trait Foo {
fn foo(&self);
-#[link(name = "foo", cfg("rlib"))] //~ ERROR invalid argument for `cfg(..)`
+#[link(name = "foo", cfg("rlib"))] //~ ERROR link cfg must have a single predicate argument
extern "C" {}
fn main() {}
-error: invalid argument for `cfg(..)`
- --> $DIR/issue-43925.rs:1:26
+error: link cfg must have a single predicate argument
+ --> $DIR/issue-43925.rs:1:22
|
LL | #[link(name = "foo", cfg("rlib"))]
- | ^^^^^^
+ | ^^^^^^^^^^^
error: aborting due to previous error
-#[link(name = "foo", cfg())] //~ ERROR `cfg()` must have an argument
+#[link(name = "foo", cfg())] //~ ERROR link cfg must have a single predicate argument
extern "C" {}
fn main() {}
-error: `cfg()` must have an argument
+error: link cfg must have a single predicate argument
--> $DIR/issue-43926.rs:1:22
|
LL | #[link(name = "foo", cfg())]
| -^^^^^
| |
| cannot use `+=` on type `&isize`
- |
-help: `+=` can be used on `isize`, you can dereference `x`
- |
-LL | let x = |ref x: isize| { *x += 1; };
- | +
error: aborting due to previous error
fn main() {
[1; <Multiply<Five, Five>>::VAL];
+ //~^ ERROR: constant expression depends on a generic parameter
}
trait TypeVal<T> {
const VAL: T;
error[E0412]: cannot find type `PhantomData` in this scope
- --> $DIR/issue-77919.rs:9:9
+ --> $DIR/issue-77919.rs:10:9
|
LL | _n: PhantomData,
| ^^^^^^^^^^^ not found in this scope
|
error[E0412]: cannot find type `VAL` in this scope
- --> $DIR/issue-77919.rs:11:63
+ --> $DIR/issue-77919.rs:12:63
|
LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
| - ^^^ not found in this scope
| help: you might be missing a type parameter: `, VAL`
error[E0046]: not all trait items implemented, missing: `VAL`
- --> $DIR/issue-77919.rs:11:1
+ --> $DIR/issue-77919.rs:12:1
|
LL | const VAL: T;
| ------------- `VAL` from trait
LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation
-error: aborting due to 3 previous errors
+error: constant expression depends on a generic parameter
+ --> $DIR/issue-77919.rs:2:9
+ |
+LL | [1; <Multiply<Five, Five>>::VAL];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this may fail depending on what value the parameter takes
+
+error: aborting due to 4 previous errors
Some errors have detailed explanations: E0046, E0412.
For more information about an error, try `rustc --explain E0046`.
+++ /dev/null
-#[link()] //~ ERROR: specified without `name =
-#[link(name = "")] //~ ERROR: with empty name
-#[link(name = "foo")]
-#[link(name = "foo", kind = "bar")] //~ ERROR: unknown kind
-extern "C" {}
-
-fn main() {}
+++ /dev/null
-error[E0459]: `#[link(...)]` specified without `name = "foo"`
- --> $DIR/bad-extern-link-attrs.rs:1:1
- |
-LL | #[link()]
- | ^^^^^^^^^ missing `name` argument
-
-error[E0454]: `#[link(name = "")]` given with empty name
- --> $DIR/bad-extern-link-attrs.rs:2:1
- |
-LL | #[link(name = "")]
- | ^^^^^^^^^^^^^^^^^^ empty name given
-
-error[E0458]: unknown kind: `bar`
- --> $DIR/bad-extern-link-attrs.rs:4:22
- |
-LL | #[link(name = "foo", kind = "bar")]
- | ---------------------^^^^^^^^^^^^--
- | |
- | unknown kind
-
-error: aborting due to 3 previous errors
-
-Some errors have detailed explanations: E0454, E0458, E0459.
-For more information about an error, try `rustc --explain E0454`.
--- /dev/null
+// Top-level ill-formed
+#[link] //~ ERROR attribute must be of the form
+ //~| WARN this was previously accepted
+#[link = "foo"] //~ ERROR attribute must be of the form
+ //~| WARN this was previously accepted
+extern "C" {}
+
+fn main() {}
--- /dev/null
+error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...")]`
+ --> $DIR/link-attr-validation-early.rs:2:1
+ |
+LL | #[link]
+ | ^^^^^^^
+ |
+ = note: `#[deny(ill_formed_attribute_input)]` on by default
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
+
+error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...")]`
+ --> $DIR/link-attr-validation-early.rs:4:1
+ |
+LL | #[link = "foo"]
+ | ^^^^^^^^^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+#![feature(native_link_modifiers_verbatim)]
+#![feature(link_cfg)]
+
+// Top-level ill-formed
+#[link(name = "...", "literal")] //~ ERROR unexpected `#[link]` argument
+#[link(name = "...", unknown)] //~ ERROR unexpected `#[link]` argument
+extern "C" {}
+
+// Duplicate arguments
+#[link(name = "foo", name = "bar")] //~ ERROR multiple `name` arguments
+#[link(name = "...", kind = "dylib", kind = "bar")] //~ ERROR multiple `kind` arguments
+#[link(name = "...", modifiers = "+verbatim", modifiers = "bar")] //~ ERROR multiple `modifiers` arguments
+#[link(name = "...", cfg(FALSE), cfg(FALSE))] //~ ERROR multiple `cfg` arguments
+#[link(wasm_import_module = "foo", wasm_import_module = "bar")] //~ ERROR multiple `wasm_import_module` arguments
+extern "C" {}
+
+// Ill-formed arguments
+#[link(name)] //~ ERROR link name must be of the form `name = "string"`
+ //~| ERROR `#[link]` attribute requires a `name = "string"` argument
+#[link(name())] //~ ERROR link name must be of the form `name = "string"`
+ //~| ERROR `#[link]` attribute requires a `name = "string"` argument
+#[link(name = "...", kind)] //~ ERROR link kind must be of the form `kind = "string"`
+#[link(name = "...", kind())] //~ ERROR link kind must be of the form `kind = "string"`
+#[link(name = "...", modifiers)] //~ ERROR link modifiers must be of the form `modifiers = "string"`
+#[link(name = "...", modifiers())] //~ ERROR link modifiers must be of the form `modifiers = "string"`
+#[link(name = "...", cfg)] //~ ERROR link cfg must be of the form `cfg(/* predicate */)`
+#[link(name = "...", cfg = "literal")] //~ ERROR link cfg must be of the form `cfg(/* predicate */)`
+#[link(name = "...", cfg("literal"))] //~ ERROR link cfg must have a single predicate argument
+#[link(name = "...", wasm_import_module)] //~ ERROR wasm import module must be of the form `wasm_import_module = "string"`
+#[link(name = "...", wasm_import_module())] //~ ERROR wasm import module must be of the form `wasm_import_module = "string"`
+extern "C" {}
+
+// Basic modifier validation
+#[link(name = "...", modifiers = "")] //~ ERROR invalid linking modifier syntax, expected '+' or '-' prefix
+#[link(name = "...", modifiers = "no-plus-minus")] //~ ERROR invalid linking modifier syntax, expected '+' or '-' prefix
+#[link(name = "...", modifiers = "+unknown")] //~ ERROR unknown linking modifier `unknown`
+#[link(name = "...", modifiers = "+verbatim,+verbatim")] //~ ERROR multiple `verbatim` modifiers
+extern "C" {}
+
+fn main() {}
--- /dev/null
+error: unexpected `#[link]` argument, expected one of: name, kind, modifiers, cfg, wasm_import_module
+ --> $DIR/link-attr-validation-late.rs:5:22
+ |
+LL | #[link(name = "...", "literal")]
+ | ^^^^^^^^^
+
+error: unexpected `#[link]` argument, expected one of: name, kind, modifiers, cfg, wasm_import_module
+ --> $DIR/link-attr-validation-late.rs:6:22
+ |
+LL | #[link(name = "...", unknown)]
+ | ^^^^^^^
+
+error: multiple `name` arguments in a single `#[link]` attribute
+ --> $DIR/link-attr-validation-late.rs:10:22
+ |
+LL | #[link(name = "foo", name = "bar")]
+ | ^^^^^^^^^^^^
+
+error: multiple `kind` arguments in a single `#[link]` attribute
+ --> $DIR/link-attr-validation-late.rs:11:38
+ |
+LL | #[link(name = "...", kind = "dylib", kind = "bar")]
+ | ^^^^^^^^^^^^
+
+error: multiple `modifiers` arguments in a single `#[link]` attribute
+ --> $DIR/link-attr-validation-late.rs:12:47
+ |
+LL | #[link(name = "...", modifiers = "+verbatim", modifiers = "bar")]
+ | ^^^^^^^^^^^^^^^^^
+
+error: multiple `cfg` arguments in a single `#[link]` attribute
+ --> $DIR/link-attr-validation-late.rs:13:34
+ |
+LL | #[link(name = "...", cfg(FALSE), cfg(FALSE))]
+ | ^^^^^^^^^^
+
+error: multiple `wasm_import_module` arguments in a single `#[link]` attribute
+ --> $DIR/link-attr-validation-late.rs:14:36
+ |
+LL | #[link(wasm_import_module = "foo", wasm_import_module = "bar")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: link name must be of the form `name = "string"`
+ --> $DIR/link-attr-validation-late.rs:18:8
+ |
+LL | #[link(name)]
+ | ^^^^
+
+error[E0459]: `#[link]` attribute requires a `name = "string"` argument
+ --> $DIR/link-attr-validation-late.rs:18:1
+ |
+LL | #[link(name)]
+ | ^^^^^^^^^^^^^ missing `name` argument
+
+error: link name must be of the form `name = "string"`
+ --> $DIR/link-attr-validation-late.rs:20:8
+ |
+LL | #[link(name())]
+ | ^^^^^^
+
+error[E0459]: `#[link]` attribute requires a `name = "string"` argument
+ --> $DIR/link-attr-validation-late.rs:20:1
+ |
+LL | #[link(name())]
+ | ^^^^^^^^^^^^^^^ missing `name` argument
+
+error: link kind must be of the form `kind = "string"`
+ --> $DIR/link-attr-validation-late.rs:22:22
+ |
+LL | #[link(name = "...", kind)]
+ | ^^^^
+
+error: link kind must be of the form `kind = "string"`
+ --> $DIR/link-attr-validation-late.rs:23:22
+ |
+LL | #[link(name = "...", kind())]
+ | ^^^^^^
+
+error: link modifiers must be of the form `modifiers = "string"`
+ --> $DIR/link-attr-validation-late.rs:24:22
+ |
+LL | #[link(name = "...", modifiers)]
+ | ^^^^^^^^^
+
+error: link modifiers must be of the form `modifiers = "string"`
+ --> $DIR/link-attr-validation-late.rs:25:22
+ |
+LL | #[link(name = "...", modifiers())]
+ | ^^^^^^^^^^^
+
+error: link cfg must be of the form `cfg(/* predicate */)`
+ --> $DIR/link-attr-validation-late.rs:26:22
+ |
+LL | #[link(name = "...", cfg)]
+ | ^^^
+
+error: link cfg must be of the form `cfg(/* predicate */)`
+ --> $DIR/link-attr-validation-late.rs:27:22
+ |
+LL | #[link(name = "...", cfg = "literal")]
+ | ^^^^^^^^^^^^^^^
+
+error: link cfg must have a single predicate argument
+ --> $DIR/link-attr-validation-late.rs:28:22
+ |
+LL | #[link(name = "...", cfg("literal"))]
+ | ^^^^^^^^^^^^^^
+
+error: wasm import module must be of the form `wasm_import_module = "string"`
+ --> $DIR/link-attr-validation-late.rs:29:22
+ |
+LL | #[link(name = "...", wasm_import_module)]
+ | ^^^^^^^^^^^^^^^^^^
+
+error: wasm import module must be of the form `wasm_import_module = "string"`
+ --> $DIR/link-attr-validation-late.rs:30:22
+ |
+LL | #[link(name = "...", wasm_import_module())]
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed
+ --> $DIR/link-attr-validation-late.rs:34:34
+ |
+LL | #[link(name = "...", modifiers = "")]
+ | ^^
+
+error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed
+ --> $DIR/link-attr-validation-late.rs:35:34
+ |
+LL | #[link(name = "...", modifiers = "no-plus-minus")]
+ | ^^^^^^^^^^^^^^^
+
+error: unknown linking modifier `unknown`, expected one of: bundle, verbatim, whole-archive, as-needed
+ --> $DIR/link-attr-validation-late.rs:36:34
+ |
+LL | #[link(name = "...", modifiers = "+unknown")]
+ | ^^^^^^^^^^
+
+error: multiple `verbatim` modifiers in a single `modifiers` argument
+ --> $DIR/link-attr-validation-late.rs:37:34
+ |
+LL | #[link(name = "...", modifiers = "+verbatim,+verbatim")]
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 24 previous errors
+
+For more information about this error, try `rustc --explain E0459`.
--- /dev/null
+#![feature(decl_macro)]
+#![deny(unused_macro_rules)]
+// To make sure we are not hitting this
+#![deny(unused_macros)]
+
+// Most simple case
+macro num {
+ (one) => { 1 },
+ (two) => { 2 }, //~ ERROR: 2nd rule of macro
+ (three) => { 3 },
+ (four) => { 4 }, //~ ERROR: 4th rule of macro
+}
+const _NUM: u8 = num!(one) + num!(three);
+
+// Check that allowing the lint works
+#[allow(unused_macro_rules)]
+macro num_allowed {
+ (one) => { 1 },
+ (two) => { 2 },
+ (three) => { 3 },
+ (four) => { 4 },
+}
+const _NUM_ALLOWED: u8 = num_allowed!(one) + num_allowed!(three);
+
+// Check that macro calls inside the macro trigger as usage
+macro num_rec {
+ (one) => { 1 },
+ (two) => {
+ num_rec!(one) + num_rec!(one)
+ },
+ (three) => { //~ ERROR: 3rd rule of macro
+ num_rec!(one) + num_rec!(two)
+ },
+ (four) => {
+ num_rec!(two) + num_rec!(two)
+ },
+}
+const _NUM_RECURSIVE: u8 = num_rec!(four);
+
+// No error if the macro is public
+pub macro num_public {
+ (one) => { 1 },
+ (two) => { 2 },
+ (three) => { 3 },
+ (four) => { 4 },
+}
+const _NUM_PUBLIC: u8 = num_public!(one) + num_public!(three);
+
+fn main() {}
--- /dev/null
+error: 4th rule of macro `num` is never used
+ --> $DIR/unused-macro-rules-decl.rs:11:5
+ |
+LL | (four) => { 4 },
+ | ^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/unused-macro-rules-decl.rs:2:9
+ |
+LL | #![deny(unused_macro_rules)]
+ | ^^^^^^^^^^^^^^^^^^
+
+error: 2nd rule of macro `num` is never used
+ --> $DIR/unused-macro-rules-decl.rs:9:5
+ |
+LL | (two) => { 2 },
+ | ^^^^^
+
+error: 3rd rule of macro `num_rec` is never used
+ --> $DIR/unused-macro-rules-decl.rs:31:5
+ |
+LL | (three) => {
+ | ^^^^^^^
+
+error: aborting due to 3 previous errors
+
+#![deny(unused_macro_rules)]
+// To make sure we are not hitting this
#![deny(unused_macros)]
// Most simple case
-macro_rules! unused { //~ ERROR: unused macro definition
- () => {};
+macro_rules! num {
+ (one) => { 1 };
+ (two) => { 2 }; //~ ERROR: 2nd rule of macro
+ (three) => { 3 };
+ (four) => { 4 }; //~ ERROR: 4th rule of macro
}
+const _NUM: u8 = num!(one) + num!(three);
-// Test macros created by macros
-macro_rules! create_macro {
- () => {
- macro_rules! m { //~ ERROR: unused macro definition
- () => {};
- }
- };
+// Check that allowing the lint works
+#[allow(unused_macro_rules)]
+macro_rules! num_allowed {
+ (one) => { 1 };
+ (two) => { 2 };
+ (three) => { 3 };
+ (four) => { 4 };
}
-create_macro!();
+const _NUM_ALLOWED: u8 = num_allowed!(one) + num_allowed!(three);
-#[allow(unused_macros)]
-mod bar {
- // Test that putting the #[deny] close to the macro's definition
- // works.
+// Check that macro calls inside the macro trigger as usage
+macro_rules! num_rec {
+ (one) => { 1 };
+ (two) => {
+ num_rec!(one) + num_rec!(one)
+ };
+ (three) => { //~ ERROR: 3rd rule of macro
+ num_rec!(one) + num_rec!(two)
+ };
+ (four) => { num_rec!(two) + num_rec!(two) };
+}
+const _NUM_RECURSIVE: u8 = num_rec!(four);
- #[deny(unused_macros)]
- macro_rules! unused { //~ ERROR: unused macro definition
- () => {};
- }
+// No error if the macro is being exported
+#[macro_export]
+macro_rules! num_exported {
+ (one) => { 1 };
+ (two) => { 2 };
+ (three) => { 3 };
+ (four) => { 4 };
}
+const _NUM_EXPORTED: u8 = num_exported!(one) + num_exported!(three);
fn main() {}
-error: unused macro definition: `unused`
- --> $DIR/unused-macro-rules.rs:4:14
+error: 4th rule of macro `num` is never used
+ --> $DIR/unused-macro-rules.rs:10:5
|
-LL | macro_rules! unused {
- | ^^^^^^
+LL | (four) => { 4 };
+ | ^^^^^^
|
note: the lint level is defined here
--> $DIR/unused-macro-rules.rs:1:9
|
-LL | #![deny(unused_macros)]
- | ^^^^^^^^^^^^^
+LL | #![deny(unused_macro_rules)]
+ | ^^^^^^^^^^^^^^^^^^
-error: unused macro definition: `m`
- --> $DIR/unused-macro-rules.rs:11:22
+error: 2nd rule of macro `num` is never used
+ --> $DIR/unused-macro-rules.rs:8:5
|
-LL | macro_rules! m {
- | ^
+LL | (two) => { 2 };
+ | ^^^^^
-error: unused macro definition: `unused`
- --> $DIR/unused-macro-rules.rs:24:18
+error: 3rd rule of macro `num_rec` is never used
+ --> $DIR/unused-macro-rules.rs:30:5
|
-LL | macro_rules! unused {
- | ^^^^^^
- |
-note: the lint level is defined here
- --> $DIR/unused-macro-rules.rs:23:12
- |
-LL | #[deny(unused_macros)]
- | ^^^^^^^^^^^^^
+LL | (three) => {
+ | ^^^^^^^
error: aborting due to 3 previous errors
+++ /dev/null
-#![feature(decl_macro)]
-#![deny(unused_macros)]
-
-// Most simple case
-macro unused { //~ ERROR: unused macro definition
- () => {}
-}
-
-#[allow(unused_macros)]
-mod bar {
- // Test that putting the #[deny] close to the macro's definition
- // works.
-
- #[deny(unused_macros)]
- macro unused { //~ ERROR: unused macro definition
- () => {}
- }
-}
-
-mod boo {
- pub(crate) macro unused { //~ ERROR: unused macro definition
- () => {}
- }
-}
-
-fn main() {}
+++ /dev/null
-error: unused macro definition: `unused`
- --> $DIR/unused-macro.rs:5:7
- |
-LL | macro unused {
- | ^^^^^^
- |
-note: the lint level is defined here
- --> $DIR/unused-macro.rs:2:9
- |
-LL | #![deny(unused_macros)]
- | ^^^^^^^^^^^^^
-
-error: unused macro definition: `unused`
- --> $DIR/unused-macro.rs:15:11
- |
-LL | macro unused {
- | ^^^^^^
- |
-note: the lint level is defined here
- --> $DIR/unused-macro.rs:14:12
- |
-LL | #[deny(unused_macros)]
- | ^^^^^^^^^^^^^
-
-error: unused macro definition: `unused`
- --> $DIR/unused-macro.rs:21:22
- |
-LL | pub(crate) macro unused {
- | ^^^^^^
-
-error: aborting due to 3 previous errors
-
--- /dev/null
+#![feature(decl_macro)]
+#![deny(unused_macros)]
+// To make sure we are not hitting this
+#![deny(unused_macro_rules)]
+
+// Most simple case
+macro unused { //~ ERROR: unused macro definition
+ () => {}
+}
+
+#[allow(unused_macros)]
+mod bar {
+ // Test that putting the #[deny] close to the macro's definition
+ // works.
+
+ #[deny(unused_macros)]
+ macro unused { //~ ERROR: unused macro definition
+ () => {}
+ }
+}
+
+mod boo {
+ pub(crate) macro unused { //~ ERROR: unused macro definition
+ () => {}
+ }
+}
+
+fn main() {}
--- /dev/null
+error: unused macro definition: `unused`
+ --> $DIR/unused-macros-decl.rs:7:7
+ |
+LL | macro unused {
+ | ^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/unused-macros-decl.rs:2:9
+ |
+LL | #![deny(unused_macros)]
+ | ^^^^^^^^^^^^^
+
+error: unused macro definition: `unused`
+ --> $DIR/unused-macros-decl.rs:17:11
+ |
+LL | macro unused {
+ | ^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/unused-macros-decl.rs:16:12
+ |
+LL | #[deny(unused_macros)]
+ | ^^^^^^^^^^^^^
+
+error: unused macro definition: `unused`
+ --> $DIR/unused-macros-decl.rs:23:22
+ |
+LL | pub(crate) macro unused {
+ | ^^^^^^
+
+error: aborting due to 3 previous errors
+
--- /dev/null
+#![deny(unused_macros)]
+// To make sure we are not hitting this
+#![deny(unused_macro_rules)]
+
+// Most simple case
+macro_rules! unused { //~ ERROR: unused macro definition
+ () => {};
+}
+
+// Test macros created by macros
+macro_rules! create_macro {
+ () => {
+ macro_rules! m { //~ ERROR: unused macro definition
+ () => {};
+ }
+ };
+}
+create_macro!();
+
+#[allow(unused_macros)]
+mod bar {
+ // Test that putting the #[deny] close to the macro's definition
+ // works.
+
+ #[deny(unused_macros)]
+ macro_rules! unused { //~ ERROR: unused macro definition
+ () => {};
+ }
+}
+
+fn main() {}
--- /dev/null
+error: unused macro definition: `unused`
+ --> $DIR/unused-macros.rs:6:14
+ |
+LL | macro_rules! unused {
+ | ^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/unused-macros.rs:1:9
+ |
+LL | #![deny(unused_macros)]
+ | ^^^^^^^^^^^^^
+
+error: unused macro definition: `m`
+ --> $DIR/unused-macros.rs:13:22
+ |
+LL | macro_rules! m {
+ | ^
+
+error: unused macro definition: `unused`
+ --> $DIR/unused-macros.rs:26:18
+ |
+LL | macro_rules! unused {
+ | ^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/unused-macros.rs:25:12
+ |
+LL | #[deny(unused_macros)]
+ | ^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
// run-pass
+#![allow(unused_macro_rules)]
+
/// A compile-time map from identifiers to arbitrary (heterogeneous) expressions
macro_rules! ident_map {
( $name:ident = { $($key:ident => $e:expr,)* } ) => {
// run-pass
+#[allow(unused_macro_rules)]
macro_rules! a {
($i:literal) => { "right" };
($i:tt) => { "wrong" };
let _ = line!();
}
+#[test]
+fn matches() {
+ let _ = matches!(1, x if x > 0);
+ let _ = matches!(1, x if x > 0,);
+}
+
#[test]
fn module_path() {
let _ = module_path!();
// run-pass
+#![allow(unused_macro_rules)]
//{{{ issue 40569 ==============================================================
};
}
+#[allow(unused_macro_rules)]
macro_rules! mtester_dbg {
($l:literal) => {
&format!("macro caught literal: {:?}", $l)
// run-pass
-#![allow(dead_code, unused_imports)]
+#![allow(dead_code, unused_imports, unused_macro_rules)]
#![feature(crate_visibility_modifier)]
/**
// run-pass
+#![allow(unused_macro_rules)]
+
macro_rules! m {
($e:expr) => {
"expr includes attr"
// run-pass
+#![allow(unused_macro_rules)]
+
use std::ops::*;
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
--> $DIR/main-wrong-location.rs:4:5
|
LL | fn main() { }
- | ^^^^^^^^^^^^^
+ | ^^^^^^^^^
= note: you have one or more functions named `main` not defined at the crate level
= help: consider moving the `main` function definitions
// compile-flags:-l static=
-// error-pattern: empty library name given via `-l`
+// error-pattern: library name must not be empty
fn main() {
}
-error: empty library name given via `-l`
-
-error: aborting due to previous error
+error: library name must not be empty
// compile-flags:-l bar=foo
-// error-pattern: unknown library kind `bar`, expected one of dylib, framework, or static
+// error-pattern: unknown library kind `bar`, expected one of: static, dylib, framework
fn main() {
}
-error: unknown library kind `bar`, expected one of dylib, framework, or static
+error: unknown library kind `bar`, expected one of: static, dylib, framework
// ignore-macos
// ignore-ios
// compile-flags:-l framework=foo
-// error-pattern: native frameworks are only available on macOS targets
+// error-pattern: library kind `framework` is only supported on Apple targets
fn main() {
}
-error: native frameworks are only available on macOS targets
+error: library kind `framework` is only supported on Apple targets
error: aborting due to previous error
// compile-flags:-l raw-dylib=foo
-// error-pattern: unknown library kind `raw-dylib`, expected one of dylib, framework, or static
+// error-pattern: unknown library kind `raw-dylib`, expected one of: static, dylib, framework
fn main() {
}
-error: unknown library kind `raw-dylib`, expected one of dylib, framework, or static
+error: unknown library kind `raw-dylib`, expected one of: static, dylib, framework
// Unspecified kind should fail with an error
// compile-flags: -l =mylib
-// error-pattern: unknown library kind ``, expected one of dylib, framework, or static
+// error-pattern: unknown library kind ``, expected one of: static, dylib, framework
fn main() {}
-error: unknown library kind ``, expected one of dylib, framework, or static
+error: unknown library kind ``, expected one of: static, dylib, framework
// Unspecified kind should fail with an error
// compile-flags: -l :+bundle=mylib
-// error-pattern: unknown library kind ``, expected one of dylib, framework, or static
+// error-pattern: unknown library kind ``, expected one of: static, dylib, framework
fn main() {}
-error: unknown library kind ``, expected one of dylib, framework, or static
+error: unknown library kind ``, expected one of: static, dylib, framework
-error: duplicating linking modifier is currently unstable and only accepted on the nightly compiler
+error: multiple `whole-archive` modifiers in a single `-l` option
#![feature(native_link_modifiers_bundle)]
#[link(name = "foo")]
-#[link( //~ ERROR multiple `modifiers` arguments in a single `#[link]` attribute
+#[link(
name = "bar",
kind = "static",
modifiers = "+whole-archive,-whole-archive",
- //~^ ERROR same modifier is used multiple times in a single `modifiers` argument
+ //~^ ERROR multiple `whole-archive` modifiers in a single `modifiers` argument
modifiers = "+bundle"
+ //~^ ERROR multiple `modifiers` arguments in a single `#[link]` attribute
)]
extern "C" {}
//~^ ERROR overriding linking modifiers from command line is not supported
-error: same modifier is used multiple times in a single `modifiers` argument
- --> $DIR/modifiers-override.rs:9:5
+error: multiple `modifiers` arguments in a single `#[link]` attribute
+ --> $DIR/modifiers-override.rs:11:5
|
-LL | modifiers = "+whole-archive,-whole-archive",
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | modifiers = "+bundle"
+ | ^^^^^^^^^^^^^^^^^^^^^
-error: multiple `modifiers` arguments in a single `#[link]` attribute
- --> $DIR/modifiers-override.rs:6:1
+error: multiple `whole-archive` modifiers in a single `modifiers` argument
+ --> $DIR/modifiers-override.rs:9:17
|
-LL | / #[link(
-LL | | name = "bar",
-LL | | kind = "static",
-LL | | modifiers = "+whole-archive,-whole-archive",
-LL | |
-LL | | modifiers = "+bundle"
-LL | | )]
- | |__^
+LL | modifiers = "+whole-archive,-whole-archive",
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: overriding linking modifiers from command line is not supported
- --> $DIR/modifiers-override.rs:13:1
+ --> $DIR/modifiers-override.rs:14:1
|
LL | extern "C" {}
| ^^^^^^^^^^^^^
error: overriding linking modifiers from command line is not supported
- --> $DIR/modifiers-override.rs:13:1
+ --> $DIR/modifiers-override.rs:14:1
|
LL | extern "C" {}
| ^^^^^^^^^^^^^
--- /dev/null
+// Regression test for #54779, checks if the diagnostics are confusing.
+
+#![feature(nll)]
+
+trait DebugWith<Cx: ?Sized> {
+ fn debug_with<'me>(&'me self, cx: &'me Cx) -> DebugCxPair<'me, Self, Cx> {
+ DebugCxPair { value: self, cx }
+ }
+
+ fn fmt_with(&self, cx: &Cx, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
+}
+
+struct DebugCxPair<'me, Value: ?Sized, Cx: ?Sized>
+where
+ Value: DebugWith<Cx>,
+{
+ value: &'me Value,
+ cx: &'me Cx,
+}
+
+trait DebugContext {}
+
+struct Foo {
+ bar: Bar,
+}
+
+impl DebugWith<dyn DebugContext> for Foo {
+ fn fmt_with(
+ &self,
+ cx: &dyn DebugContext,
+ fmt: &mut std::fmt::Formatter<'_>,
+ ) -> std::fmt::Result {
+ let Foo { bar } = self;
+ bar.debug_with(cx); //~ ERROR: lifetime may not live long enough
+ Ok(())
+ }
+}
+
+struct Bar {}
+
+impl DebugWith<dyn DebugContext> for Bar {
+ fn fmt_with(
+ &self,
+ cx: &dyn DebugContext,
+ fmt: &mut std::fmt::Formatter<'_>,
+ ) -> std::fmt::Result {
+ Ok(())
+ }
+}
+
+fn main() {}
--- /dev/null
+error: lifetime may not live long enough
+ --> $DIR/issue-54779-anon-static-lifetime.rs:34:24
+ |
+LL | cx: &dyn DebugContext,
+ | - let's call the lifetime of this reference `'1`
+...
+LL | bar.debug_with(cx);
+ | ^^ cast requires that `'1` must outlive `'static`
+
+error: aborting due to previous error
+
#[link(name = "foo", kind = "framework")]
extern "C" {}
-//~^^ ERROR: native frameworks are only available on macOS
+//~^^ ERROR: link kind `framework` is only supported on Apple targets
fn main() {}
-error[E0455]: native frameworks are only available on macOS targets
- --> $DIR/osx-frameworks.rs:3:1
+error[E0455]: link kind `framework` is only supported on Apple targets
+ --> $DIR/osx-frameworks.rs:3:29
|
LL | #[link(name = "foo", kind = "framework")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^
error: aborting due to previous error
--- /dev/null
+// error-pattern: this file contains an unclosed delimiter
+// error-pattern: this file contains an unclosed delimiter
+// error-pattern: expected pattern, found `=`
+// error-pattern: expected one of `)`, `,`, `->`, `where`, or `{`, found `]`
+// error-pattern: expected item, found `]`
+
+fn main() {}
+
+fn p([=(}
--- /dev/null
+error: this file contains an unclosed delimiter
+ --> $DIR/issue-81804.rs:9:11
+ |
+LL | fn p([=(}
+ | -- ^
+ | ||
+ | |unclosed delimiter
+ | unclosed delimiter
+
+error: this file contains an unclosed delimiter
+ --> $DIR/issue-81804.rs:9:11
+ |
+LL | fn p([=(}
+ | -- ^
+ | ||
+ | |unclosed delimiter
+ | unclosed delimiter
+
+error: expected pattern, found `=`
+ --> $DIR/issue-81804.rs:9:7
+ |
+LL | fn p([=(}
+ | ^ expected pattern
+
+error: expected one of `)`, `,`, `->`, `where`, or `{`, found `]`
+ --> $DIR/issue-81804.rs:9:8
+ |
+LL | fn p([=(}
+ | ^ -^
+ | | |
+ | | help: `)` may belong here
+ | unclosed delimiter
+
+error: expected item, found `]`
+ --> $DIR/issue-81804.rs:9:11
+ |
+LL | fn p([=(}
+ | ^ expected item
+
+error: aborting due to 5 previous errors
+
// Test that failing macro matchers will not cause pre-expansion errors
// even though they use a feature that is pre-expansion gated.
+#[allow(unused_macro_rules)]
macro_rules! m {
($e:expr) => { 0 }; // This fails on the input below due to `, foo`.
($e:expr,) => { 1 }; // This also fails to match due to `foo`.
--- /dev/null
+// edition:2021
+//! Missing docs lint warns about undocumented exported items.
+//! Use the lint to additionally verify that items are reachable
+//! but not exported.
+#![allow(non_camel_case_types)]
+#![deny(missing_docs)]
+
+mod hidden {
+ pub struct s;
+ pub enum e { x, y, z }
+ pub use e::*;
+ impl s {
+ pub fn f(&self) {}
+ }
+ impl e {
+ pub fn g(&self) {}
+ }
+}
+// Hide all type definitions while reexporting their constructors:
+mod e {}
+mod x {}
+mod y {}
+mod z {}
+mod s {}
+pub use hidden::*;
--- /dev/null
+// Verify that a type is considered reachable when its constructor is
+// reachable. The auxiliary library is constructed so that all types are
+// shadowed and cannot be named directly, while their constructors are
+// reexported. Regression test for issue #96934.
+//
+// aux-build:ctor_aux.rs
+// edition:2021
+// build-pass
+
+extern crate ctor_aux;
+
+fn main() {
+ ctor_aux::s.f();
+ ctor_aux::x.g();
+ ctor_aux::y.g();
+}
// build-fail
- //~^ overflow evaluating
+//~^ ERROR overflow evaluating the requirement
fn main() {
let mut iter = 0u8..1;
--- /dev/null
+// compile-flags: -l dylib=foo:bar
+// error-pattern: overriding linking modifiers from command line is not supported
+
+#![feature(native_link_modifiers_as_needed)]
+
+#![crate_type = "lib"]
+
+#[link(name = "foo", kind = "dylib", modifiers = "-as-needed")]
+extern "C" {}
--- /dev/null
+error: overriding linking modifiers from command line is not supported
+ --> $DIR/rename-modifiers.rs:9:1
+ |
+LL | extern "C" {}
+ | ^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
--> $DIR/typeck.rs:9:22
|
LL | Ok(x) if let Err(_) = x => {},
- | ^^^^^^ expected enum `Option`, found enum `Result`
+ | ^^^^^^ - this expression has type `Option<bool>`
+ | |
+ | expected enum `Option`, found enum `Result`
|
= note: expected enum `Option<bool>`
found enum `Result<_, _>`
--> $DIR/typeck.rs:11:22
|
LL | Ok(x) if let 0 = x => {},
- | ^ expected enum `Option`, found integer
+ | ^ - this expression has type `Option<bool>`
+ | |
+ | expected enum `Option`, found integer
|
= note: expected enum `Option<bool>`
found type `{integer}`
#![feature(raw_dylib)]
//~^ WARNING: the feature `raw_dylib` is incomplete
#[link(name = "foo", kind = "raw-dylib")]
-//~^ ERROR: `#[link(...)]` with `kind = "raw-dylib"` only supported on Windows
+//~^ ERROR: link kind `raw-dylib` is only supported on Windows targets
extern "C" {}
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #58713 <https://github.com/rust-lang/rust/issues/58713> for more information
-error: `#[link(...)]` with `kind = "raw-dylib"` only supported on Windows
- --> $DIR/raw-dylib-windows-only.rs:5:1
+error[E0455]: link kind `raw-dylib` is only supported on Windows targets
+ --> $DIR/raw-dylib-windows-only.rs:5:29
|
LL | #[link(name = "foo", kind = "raw-dylib")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^
error: aborting due to previous error; 1 warning emitted
+For more information about this error, try `rustc --explain E0455`.
-#![allow(dead_code, unused_variables, non_camel_case_types, non_upper_case_globals)]
+#![allow(dead_code, unused_variables, unused_macro_rules, bad_style)]
#![deny(keyword_idents)]
// edition:2015
-#![allow(dead_code, unused_variables, non_camel_case_types, non_upper_case_globals)]
+#![allow(dead_code, unused_variables, unused_macro_rules, bad_style)]
#![deny(keyword_idents)]
// edition:2015
| ^^^^^^^^ the trait `for<'b> Trait` is not implemented for `&'b S`
|
= help: the trait `Trait` is implemented for `&'a mut S`
+ = note: `for<'b> Trait` is implemented for `&'b mut S`, but not for `&'b S`
note: required by a bound in `foo`
--> $DIR/imm-ref-trait-object-literal-bound-regions.rs:11:20
|
|
= note: expected mutable reference `&mut Option<String>`
found enum `Option<_>`
-help: consider dereferencing here to assign to the mutable borrowed piece of memory
+help: consider dereferencing here to assign to the mutably borrowed value
|
LL | *opt = None;
| +
|
= note: expected mutable reference `&mut Option<String>`
found enum `Option<String>`
-help: consider dereferencing here to assign to the mutable borrowed piece of memory
+help: consider dereferencing here to assign to the mutably borrowed value
|
LL | *opt = Some(String::new())
| +
-error: symbol-name(_ZN5basic4main17h87acd86b3a6f1754E)
+error: symbol-name(_ZN5basic4main17hcbad207c0eeb0b3bE)
--> $DIR/basic.rs:8:1
|
LL | #[rustc_symbol_name]
| ^^^^^^^^^^^^^^^^^^^^
-error: demangling(basic::main::h87acd86b3a6f1754)
+error: demangling(basic::main::hcbad207c0eeb0b3b)
--> $DIR/basic.rs:8:1
|
LL | #[rustc_symbol_name]
-error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h8d22952c45e20d65E)
+error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h2f2efcf580c9b1eeE)
--> $DIR/issue-60925.rs:21:9
|
LL | #[rustc_symbol_name]
| ^^^^^^^^^^^^^^^^^^^^
-error: demangling(issue_60925::foo::Foo<issue_60925::llvm::Foo>::foo::h8d22952c45e20d65)
+error: demangling(issue_60925::foo::Foo<issue_60925::llvm::Foo>::foo::h2f2efcf580c9b1ee)
--> $DIR/issue-60925.rs:21:9
|
LL | #[rustc_symbol_name]
--> $DIR/assoc-type-in-superbad.rs:12:16
|
LL | type Key = u32;
- | ^^^ expected `i32`, found `u32`
+ | ^^^ expected `u32`, found `i32`
+ |
+note: required by a bound in `Foo`
+ --> $DIR/assoc-type-in-superbad.rs:7:25
+ |
+LL | pub trait Foo: Iterator<Item=<Self as Foo>::Key> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo`
error: aborting due to previous error
--- /dev/null
+struct A<T>(T);
+struct B;
+
+trait I<T> {}
+impl I<i32> for B {}
+impl I<u32> for B {}
+
+trait V<U> {
+ fn method(self) -> U;
+}
+
+impl<T, U> V<U> for A<T>
+where
+ T: I<U>,
+{
+ fn method(self) -> U { unimplemented!() }
+}
+
+fn main() {
+ let a = A(B);
+ a.method();
+ //~^ ERROR type annotations needed
+ //~| ERROR type annotations needed
+}
--- /dev/null
+error[E0282]: type annotations needed
+ --> $DIR/not-suggest-non-existing-fully-qualified-path.rs:21:7
+ |
+LL | a.method();
+ | --^^^^^^--
+ | | |
+ | | cannot infer type for type parameter `U` declared on the trait `V`
+ | this method call resolves to `U`
+
+error[E0283]: type annotations needed
+ --> $DIR/not-suggest-non-existing-fully-qualified-path.rs:21:7
+ |
+LL | a.method();
+ | --^^^^^^--
+ | | |
+ | | cannot infer type for type parameter `U`
+ | this method call resolves to `U`
+ |
+note: multiple `impl`s satisfying `B: I<_>` found
+ --> $DIR/not-suggest-non-existing-fully-qualified-path.rs:5:1
+ |
+LL | impl I<i32> for B {}
+ | ^^^^^^^^^^^^^^^^^
+LL | impl I<u32> for B {}
+ | ^^^^^^^^^^^^^^^^^
+note: required because of the requirements on the impl of `V<_>` for `A<B>`
+ --> $DIR/not-suggest-non-existing-fully-qualified-path.rs:12:12
+ |
+LL | impl<T, U> V<U> for A<T>
+ | ^^^^ ^^^^
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0282, E0283.
+For more information about an error, try `rustc --explain E0282`.
fn transmute<A, B>(x: A) -> B {
foo::<A, B, dyn Trait<A = A, B = B>>(x)
- //~^ ERROR type mismatch resolving `<dyn Trait<A = A, B = B> as SuperTrait>::A == B`
+ //~^ ERROR type mismatch resolving `<dyn Trait<B = B, A = A> as SuperTrait>::A == B`
}
fn foo<A, B, T: ?Sized>(x: T::A) -> B
-error[E0271]: type mismatch resolving `<dyn Trait<A = A, B = B> as SuperTrait>::A == B`
+error[E0271]: type mismatch resolving `<dyn Trait<B = B, A = A> as SuperTrait>::A == B`
--> $DIR/enforce-supertrait-projection.rs:9:5
|
LL | fn transmute<A, B>(x: A) -> B {
--- /dev/null
+warning: trait bound for<'any> &'any mut (): Clone does not depend on any type or lifetime parameters
+ --> $DIR/issue-73021-impossible-inline.rs:20:29
+ |
+LL | for<'any> &'any mut (): Clone,
+ | ^^^^^
+ |
+ = note: `#[warn(trivial_bounds)]` on by default
+
+warning: trait bound i32: Foo does not depend on any type or lifetime parameters
+ --> $DIR/issue-73021-impossible-inline.rs:28:21
+ |
+LL | struct S where i32: Foo;
+ | ^^^
+
+warning: trait bound i32: Foo does not depend on any type or lifetime parameters
+ --> $DIR/issue-73021-impossible-inline.rs:31:28
+ |
+LL | impl Foo for () where i32: Foo {
+ | ^^^
+
+warning: trait bound i32: Foo does not depend on any type or lifetime parameters
+ --> $DIR/issue-73021-impossible-inline.rs:40:19
+ |
+LL | fn f() where i32: Foo {
+ | ^^^
+
+warning: trait bound &'static str: Foo does not depend on any type or lifetime parameters
+ --> $DIR/issue-73021-impossible-inline.rs:48:28
+ |
+LL | fn g() where &'static str: Foo {
+ | ^^^
+
+warning: trait bound String: Neg does not depend on any type or lifetime parameters
+ --> $DIR/issue-73021-impossible-inline.rs:57:13
+ |
+LL | String: ::std::ops::Neg<Output = String>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: trait bound i32: Iterator does not depend on any type or lifetime parameters
+ --> $DIR/issue-73021-impossible-inline.rs:65:10
+ |
+LL | i32: Iterator,
+ | ^^^^^^^^
+
+warning: 7 warnings emitted
+
--- /dev/null
+warning: trait bound for<'any> &'any mut (): Clone does not depend on any type or lifetime parameters
+ --> $DIR/issue-73021-impossible-inline.rs:20:29
+ |
+LL | for<'any> &'any mut (): Clone,
+ | ^^^^^
+ |
+ = note: `#[warn(trivial_bounds)]` on by default
+
+warning: trait bound i32: Foo does not depend on any type or lifetime parameters
+ --> $DIR/issue-73021-impossible-inline.rs:28:21
+ |
+LL | struct S where i32: Foo;
+ | ^^^
+
+warning: trait bound i32: Foo does not depend on any type or lifetime parameters
+ --> $DIR/issue-73021-impossible-inline.rs:31:28
+ |
+LL | impl Foo for () where i32: Foo {
+ | ^^^
+
+warning: trait bound i32: Foo does not depend on any type or lifetime parameters
+ --> $DIR/issue-73021-impossible-inline.rs:40:19
+ |
+LL | fn f() where i32: Foo {
+ | ^^^
+
+warning: trait bound &'static str: Foo does not depend on any type or lifetime parameters
+ --> $DIR/issue-73021-impossible-inline.rs:48:28
+ |
+LL | fn g() where &'static str: Foo {
+ | ^^^
+
+warning: trait bound String: Neg does not depend on any type or lifetime parameters
+ --> $DIR/issue-73021-impossible-inline.rs:57:13
+ |
+LL | String: ::std::ops::Neg<Output = String>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: trait bound i32: Iterator does not depend on any type or lifetime parameters
+ --> $DIR/issue-73021-impossible-inline.rs:65:10
+ |
+LL | i32: Iterator,
+ | ^^^^^^^^
+
+warning: 7 warnings emitted
+
--- /dev/null
+// build-pass
+// revisions: no-opt inline
+// [inline]compile-flags: -Zmir-opt-level=3 --emit=mir
+#![feature(trivial_bounds)]
+#![allow(unused)]
+
+trait Foo {
+ fn test(&self);
+}
+
+fn foo<'a>(s: &'a mut ())
+where
+ &'a mut (): Foo,
+{
+ s.test();
+}
+
+fn clone(it: &mut ()) -> &mut ()
+where
+ for<'any> &'any mut (): Clone,
+ //~^ WARN trait bound for<'any> &'any mut (): Clone does not depend on any type or lifetime parameters
+{
+ it.clone()
+}
+
+fn generic_function<X: Foo>(x: X) {}
+
+struct S where i32: Foo;
+//~^ WARN trait bound i32: Foo does not depend on any type or lifetime parameters
+
+impl Foo for () where i32: Foo {
+//~^ WARN trait bound i32: Foo does not depend on any type or lifetime parameters
+ fn test(&self) {
+ 3i32.test();
+ Foo::test(&4i32);
+ generic_function(5i32);
+ }
+}
+
+fn f() where i32: Foo {
+//~^ WARN trait bound i32: Foo does not depend on any type or lifetime parameters
+ let s = S;
+ 3i32.test();
+ Foo::test(&4i32);
+ generic_function(5i32);
+}
+
+fn g() where &'static str: Foo {
+//~^ WARN trait bound &'static str: Foo does not depend on any type or lifetime parameters
+ "Foo".test();
+ Foo::test(&"Foo");
+ generic_function("Foo");
+}
+
+fn use_op(s: String) -> String
+where
+ String: ::std::ops::Neg<Output = String>,
+//~^ WARN trait bound String: Neg does not depend on any type or lifetime parameters
+{
+ -s
+}
+
+fn use_for()
+where
+ i32: Iterator,
+//~^ WARN trait bound i32: Iterator does not depend on any type or lifetime parameters
+{
+ for _ in 2i32 {}
+}
+
+fn main() {}
fn one_ty<T: Debug>(t: T) -> TwoTys<T, T> {
t
//~^ ERROR non-defining opaque type use in defining scope
- //~| ERROR `U` doesn't implement `Debug`
}
fn one_lifetime<'a>(t: &'a u32) -> TwoLifetimes<'a, 'a> {
-error[E0277]: `U` doesn't implement `Debug`
- --> $DIR/generic_duplicate_param_use.rs:16:5
- |
-LL | t
- | ^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug`
- |
-help: consider restricting type parameter `U`
- |
-LL | type TwoTys<T, U: std::fmt::Debug> = impl Debug;
- | +++++++++++++++++
-
error: non-defining opaque type use in defining scope
--> $DIR/generic_duplicate_param_use.rs:16:5
|
| ^ ^
error: non-defining opaque type use in defining scope
- --> $DIR/generic_duplicate_param_use.rs:22:5
+ --> $DIR/generic_duplicate_param_use.rs:21:5
|
LL | t
| ^
| ^^ ^^
error: non-defining opaque type use in defining scope
- --> $DIR/generic_duplicate_param_use.rs:27:5
+ --> $DIR/generic_duplicate_param_use.rs:26:5
|
LL | t
| ^
LL | type TwoConsts<const X: usize, const Y: usize> = impl Debug;
| ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
-For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+#![feature(type_alias_impl_trait)]
+#![allow(dead_code)]
+
+type Bug<T, U> = impl Fn(T) -> U + Copy;
+
+const CONST_BUG: Bug<u8, ()> = unsafe { std::mem::transmute(|_: u8| ()) };
+
+fn make_bug<T, U: From<T>>() -> Bug<T, U> {
+ |x| x.into() //~ ERROR the trait bound `U: From<T>` is not satisfied
+}
+
+fn main() {
+ CONST_BUG(0);
+}
--- /dev/null
+error[E0277]: the trait bound `U: From<T>` is not satisfied
+ --> $DIR/issue-53092.rs:9:5
+ |
+LL | |x| x.into()
+ | ^^^^^^^^^^^^ the trait `From<T>` is not implemented for `U`
+ |
+note: required by a bound in `make_bug`
+ --> $DIR/issue-53092.rs:8:19
+ |
+LL | fn make_bug<T, U: From<T>>() -> Bug<T, U> {
+ | ^^^^^^^ required by this bound in `make_bug`
+help: consider restricting type parameter `U`
+ |
+LL | type Bug<T, U: std::convert::From<T>> = impl Fn(T) -> U + Copy;
+ | +++++++++++++++++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+#![feature(type_alias_impl_trait)]
+
+// check-pass
+
+trait IterBits {
+ type BitsIter: Iterator<Item = u8>;
+ fn iter_bits(self, n: u8) -> Self::BitsIter;
+}
+
+impl<T: Copy, E> IterBits for T
+where
+ T: std::ops::Shr<Output = T>
+ + std::ops::BitAnd<T, Output = T>
+ + std::convert::From<u8>
+ + std::convert::TryInto<u8, Error = E>,
+ E: std::fmt::Debug,
+{
+ type BitsIter = impl std::iter::Iterator<Item = u8>;
+ fn iter_bits(self, n: u8) -> Self::BitsIter {
+ (0u8..n).rev().map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap())
+ }
+}
+
+fn main() {}
fn iter_bits(self, n: u8) -> Self::BitsIter {
(0u8..n).rev().map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap())
//~^ ERROR non-defining opaque type use in defining scope
- //~| ERROR type mismatch resolving
}
}
-error[E0271]: type mismatch resolving `<[closure@$DIR/issue-60564.rs:20:28: 20:100] as FnOnce<(u8,)>>::Output == I`
- --> $DIR/issue-60564.rs:20:9
- |
-LL | type IterBitsIter<T, E, I> = impl std::iter::Iterator<Item = I>;
- | - this type parameter
-...
-LL | (0u8..n).rev().map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap())
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found type parameter `I`
- |
- = note: expected type `u8`
- found type parameter `I`
- = note: required because of the requirements on the impl of `Iterator` for `Map<Rev<std::ops::Range<u8>>, [closure@$DIR/issue-60564.rs:20:28: 20:100]>`
-
error: non-defining opaque type use in defining scope
--> $DIR/issue-60564.rs:20:9
|
LL | type IterBitsIter<T, E, I> = impl std::iter::Iterator<Item = I>;
| ^
-error: aborting due to 2 previous errors
+error: aborting due to previous error
-For more information about this error, try `rustc --explain E0271`.
fn f<'a>() -> Alias<'a, ()> {}
//~^ ERROR non-defining opaque type use in defining scope
-//~| ERROR the trait bound `(): Trait<U>` is not satisfied
fn main() {}
-error[E0277]: the trait bound `(): Trait<U>` is not satisfied
- --> $DIR/issue-68368-non-defining-use.rs:9:29
- |
-LL | fn f<'a>() -> Alias<'a, ()> {}
- | ^^ the trait `Trait<U>` is not implemented for `()`
- |
-help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
- |
-LL | type Alias<'a, U> = impl Trait<U> where (): Trait<U>;
- | ++++++++++++++++++
-
error: non-defining opaque type use in defining scope
--> $DIR/issue-68368-non-defining-use.rs:9:29
|
LL | type Alias<'a, U> = impl Trait<U>;
| ^
-error: aborting due to 2 previous errors
+error: aborting due to previous error
-For more information about this error, try `rustc --explain E0277`.
fn my_fun() -> Return<()> {}
//~^ ERROR non-defining opaque type use in defining scope
-//~| ERROR non-defining opaque type use in defining scope
fn main() {}
LL | type Return<A> = impl WithAssoc<A, AssocType = impl SomeTrait + 'a>;
| ^
-error: non-defining opaque type use in defining scope
- --> $DIR/issue-69136-inner-lifetime-resolve-error.rs:20:27
- |
-LL | fn my_fun() -> Return<()> {}
- | ^^
- |
-note: used non-generic type `()` for generic parameter
- --> $DIR/issue-69136-inner-lifetime-resolve-error.rs:17:13
- |
-LL | type Return<A> = impl WithAssoc<A, AssocType = impl SomeTrait + 'a>;
- | ^
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0261`.
--- /dev/null
+#![feature(type_alias_impl_trait)]
+
+trait Bar {
+ fn bar(&self);
+}
+
+type FooFn<B> = impl FnOnce(B);
+
+fn foo<B: Bar>() -> FooFn<B> {
+ fn mop<B: Bar>(bar: B) { bar.bar() }
+ mop // NOTE: no function pointer, but function zst item
+ //~^ ERROR the trait bound `B: Bar` is not satisfied
+}
+
+fn main() {
+ let boom: FooFn<u32> = unsafe { core::mem::zeroed() };
+ boom(42);
+}
--- /dev/null
+error[E0277]: the trait bound `B: Bar` is not satisfied
+ --> $DIR/wf-check-fn-def.rs:11:5
+ |
+LL | mop // NOTE: no function pointer, but function zst item
+ | ^^^ the trait `Bar` is not implemented for `B`
+ |
+note: required by a bound in `mop`
+ --> $DIR/wf-check-fn-def.rs:10:15
+ |
+LL | fn mop<B: Bar>(bar: B) { bar.bar() }
+ | ^^^ required by this bound in `mop`
+help: consider restricting type parameter `B`
+ |
+LL | type FooFn<B: Bar> = impl FnOnce(B);
+ | +++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+#![feature(type_alias_impl_trait)]
+
+// build-pass
+
+trait Bar {
+ fn bar(&self);
+}
+
+type FooFn<B> = impl FnOnce(B);
+
+fn foo<B: Bar>() -> FooFn<B> {
+ fn mop<B: Bar>(bar: B) { bar.bar() }
+ mop as fn(B)
+ // function pointers don't have any obligations on them,
+ // thus the above compiles. It's obviously unsound to just
+ // procure a `FooFn` from the ether without making sure that
+ // the pointer is actually legal for all `B`
+}
+
+fn main() {
+ let boom: FooFn<u32> = unsafe { core::mem::zeroed() };
+ boom(42);
+}
--- /dev/null
+#![feature(type_alias_impl_trait)]
+
+trait Bar {
+ fn bar(&self);
+}
+
+type FooFn<B> = impl FnOnce();
+
+fn foo<B: Bar>(bar: B) -> FooFn<B> {
+ move || { bar.bar() }
+ //~^ ERROR the trait bound `B: Bar` is not satisfied
+}
+
+fn main() {
+ let boom: FooFn<u32> = unsafe { core::mem::zeroed() };
+ boom();
+}
--- /dev/null
+error[E0277]: the trait bound `B: Bar` is not satisfied
+ --> $DIR/wf_check_closures.rs:10:5
+ |
+LL | move || { bar.bar() }
+ | ^^^^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `B`
+ |
+note: required by a bound in `foo`
+ --> $DIR/wf_check_closures.rs:9:11
+ |
+LL | fn foo<B: Bar>(bar: B) -> FooFn<B> {
+ | ^^^ required by this bound in `foo`
+help: consider restricting type parameter `B`
+ |
+LL | type FooFn<B: Bar> = impl FnOnce();
+ | +++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// run-rustfix
+
+fn main() {
+ let x = std::sync::Mutex::new(1usize);
+ *x.lock().unwrap() = 2;
+ //~^ ERROR invalid left-hand side of assignment
+ *x.lock().unwrap() += 1;
+ //~^ ERROR binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>`
+
+ let mut y = x.lock().unwrap();
+ *y = 2;
+ //~^ ERROR mismatched types
+ *y += 1;
+ //~^ ERROR binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>`
+}
--- /dev/null
+// run-rustfix
+
+fn main() {
+ let x = std::sync::Mutex::new(1usize);
+ x.lock().unwrap() = 2;
+ //~^ ERROR invalid left-hand side of assignment
+ x.lock().unwrap() += 1;
+ //~^ ERROR binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>`
+
+ let mut y = x.lock().unwrap();
+ y = 2;
+ //~^ ERROR mismatched types
+ y += 1;
+ //~^ ERROR binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>`
+}
--- /dev/null
+error[E0070]: invalid left-hand side of assignment
+ --> $DIR/assign-non-lval-derefmut.rs:5:23
+ |
+LL | x.lock().unwrap() = 2;
+ | ----------------- ^
+ | |
+ | cannot assign to this expression
+ |
+help: consider dereferencing here to assign to the mutably borrowed value
+ |
+LL | *x.lock().unwrap() = 2;
+ | +
+
+error[E0368]: binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>`
+ --> $DIR/assign-non-lval-derefmut.rs:7:5
+ |
+LL | x.lock().unwrap() += 1;
+ | -----------------^^^^^
+ | |
+ | cannot use `+=` on type `MutexGuard<'_, usize>`
+ |
+help: `+=` can be used on `usize`, you can dereference `x.lock().unwrap()`
+ |
+LL | *x.lock().unwrap() += 1;
+ | +
+
+error[E0308]: mismatched types
+ --> $DIR/assign-non-lval-derefmut.rs:11:9
+ |
+LL | let mut y = x.lock().unwrap();
+ | ----------------- expected due to this value
+LL | y = 2;
+ | ^ expected struct `MutexGuard`, found integer
+ |
+ = note: expected struct `MutexGuard<'_, usize>`
+ found type `{integer}`
+help: consider dereferencing here to assign to the mutably borrowed value
+ |
+LL | *y = 2;
+ | +
+
+error[E0368]: binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>`
+ --> $DIR/assign-non-lval-derefmut.rs:13:5
+ |
+LL | y += 1;
+ | -^^^^^
+ | |
+ | cannot use `+=` on type `MutexGuard<'_, usize>`
+ |
+help: `+=` can be used on `usize`, you can dereference `y`
+ |
+LL | *y += 1;
+ | +
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0070, E0308, E0368.
+For more information about an error, try `rustc --explain E0070`.
--- /dev/null
+// run-rustfix
+
+fn main() {
+ let mut x = vec![1usize];
+ *x.last_mut().unwrap() = 2;
+ //~^ ERROR invalid left-hand side of assignment
+ *x.last_mut().unwrap() += 1;
+ //~^ ERROR binary assignment operation `+=` cannot be applied to type `&mut usize`
+
+ let y = x.last_mut().unwrap();
+ *y = 2;
+ //~^ ERROR mismatched types
+ *y += 1;
+ //~^ ERROR binary assignment operation `+=` cannot be applied to type `&mut usize`
+}
--- /dev/null
+// run-rustfix
+
+fn main() {
+ let mut x = vec![1usize];
+ x.last_mut().unwrap() = 2;
+ //~^ ERROR invalid left-hand side of assignment
+ x.last_mut().unwrap() += 1;
+ //~^ ERROR binary assignment operation `+=` cannot be applied to type `&mut usize`
+
+ let y = x.last_mut().unwrap();
+ y = 2;
+ //~^ ERROR mismatched types
+ y += 1;
+ //~^ ERROR binary assignment operation `+=` cannot be applied to type `&mut usize`
+}
--- /dev/null
+error[E0070]: invalid left-hand side of assignment
+ --> $DIR/assign-non-lval-mut-ref.rs:5:27
+ |
+LL | x.last_mut().unwrap() = 2;
+ | --------------------- ^
+ | |
+ | cannot assign to this expression
+ |
+help: consider dereferencing here to assign to the mutably borrowed value
+ |
+LL | *x.last_mut().unwrap() = 2;
+ | +
+
+error[E0368]: binary assignment operation `+=` cannot be applied to type `&mut usize`
+ --> $DIR/assign-non-lval-mut-ref.rs:7:5
+ |
+LL | x.last_mut().unwrap() += 1;
+ | ---------------------^^^^^
+ | |
+ | cannot use `+=` on type `&mut usize`
+ |
+help: `+=` can be used on `usize`, you can dereference `x.last_mut().unwrap()`
+ |
+LL | *x.last_mut().unwrap() += 1;
+ | +
+
+error[E0308]: mismatched types
+ --> $DIR/assign-non-lval-mut-ref.rs:11:9
+ |
+LL | let y = x.last_mut().unwrap();
+ | --------------------- expected due to this value
+LL | y = 2;
+ | ^ expected `&mut usize`, found integer
+ |
+help: consider dereferencing here to assign to the mutably borrowed value
+ |
+LL | *y = 2;
+ | +
+
+error[E0368]: binary assignment operation `+=` cannot be applied to type `&mut usize`
+ --> $DIR/assign-non-lval-mut-ref.rs:13:5
+ |
+LL | y += 1;
+ | -^^^^^
+ | |
+ | cannot use `+=` on type `&mut usize`
+ |
+help: `+=` can be used on `usize`, you can dereference `y`
+ |
+LL | *y += 1;
+ | +
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0070, E0308, E0368.
+For more information about an error, try `rustc --explain E0070`.
| -------------------------- ^
| |
| cannot assign to this expression
+ |
+help: consider dereferencing here to assign to the mutably borrowed value
+ |
+LL | *vec![].last_mut().unwrap() = 3_u8;
+ | +
error: aborting due to previous error
+#![feature(link_cfg)]
+
#[link(name = "...", wasm_import_module)] //~ ERROR: must be of the form
extern "C" {}
#[link(name = "...", wasm_import_module())] //~ ERROR: must be of the form
extern "C" {}
+#[link(wasm_import_module = "foo", name = "bar")] //~ ERROR: `wasm_import_module` is incompatible with other arguments
+extern "C" {}
+
+#[link(wasm_import_module = "foo", kind = "dylib")] //~ ERROR: `wasm_import_module` is incompatible with other arguments
+extern "C" {}
+
+#[link(wasm_import_module = "foo", cfg(FALSE))] //~ ERROR: `wasm_import_module` is incompatible with other arguments
+extern "C" {}
+
fn main() {}
-error: must be of the form `#[link(wasm_import_module = "...")]`
- --> $DIR/wasm-import-module.rs:1:22
+error: wasm import module must be of the form `wasm_import_module = "string"`
+ --> $DIR/wasm-import-module.rs:3:22
|
LL | #[link(name = "...", wasm_import_module)]
| ^^^^^^^^^^^^^^^^^^
-error: must be of the form `#[link(wasm_import_module = "...")]`
- --> $DIR/wasm-import-module.rs:4:22
+error: wasm import module must be of the form `wasm_import_module = "string"`
+ --> $DIR/wasm-import-module.rs:6:22
|
LL | #[link(name = "...", wasm_import_module(x))]
| ^^^^^^^^^^^^^^^^^^^^^
-error: must be of the form `#[link(wasm_import_module = "...")]`
- --> $DIR/wasm-import-module.rs:7:22
+error: wasm import module must be of the form `wasm_import_module = "string"`
+ --> $DIR/wasm-import-module.rs:9:22
|
LL | #[link(name = "...", wasm_import_module())]
| ^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 3 previous errors
+error: `wasm_import_module` is incompatible with other arguments in `#[link]` attributes
+ --> $DIR/wasm-import-module.rs:12:8
+ |
+LL | #[link(wasm_import_module = "foo", name = "bar")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `wasm_import_module` is incompatible with other arguments in `#[link]` attributes
+ --> $DIR/wasm-import-module.rs:15:8
+ |
+LL | #[link(wasm_import_module = "foo", kind = "dylib")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `wasm_import_module` is incompatible with other arguments in `#[link]` attributes
+ --> $DIR/wasm-import-module.rs:18:8
+ |
+LL | #[link(wasm_import_module = "foo", cfg(FALSE))]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
-Subproject commit a44758ac805600edbb6ba51e7e6fb81a6077c0cd
+Subproject commit a4c1cd0eb6b18082a7e693f5a665548fe1534be4
use if_chain::if_chain;
use rustc_errors::MultiSpan;
use rustc_hir::LangItem::OptionNone;
-use rustc_hir::{Arm, Expr, Guard, HirId, Pat, PatKind};
+use rustc_hir::{Arm, Expr, Guard, HirId, Let, Pat, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span;
(Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b),
};
// the binding must not be used in the if guard
- if outer_guard.map_or(true, |(Guard::If(e) | Guard::IfLet(_, e))| !is_local_used(cx, *e, binding_id));
+ if outer_guard.map_or(true, |(Guard::If(e) | Guard::IfLet(Let { init: e, .. }))| !is_local_used(cx, *e, binding_id));
// ...or anywhere in the inner expression
if match inner {
IfLetOrMatch::IfLet(_, _, body, els) => {
use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::{is_automatically_derived, is_default_equivalent, peel_blocks};
+use clippy_utils::{is_default_equivalent, peel_blocks};
use rustc_hir::{
def::{DefKind, Res},
Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
self_ty,
..
}) = item.kind;
- if let attrs = cx.tcx.hir().attrs(item.hir_id());
- if !is_automatically_derived(attrs);
+ if !cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived);
if !item.span.from_expansion();
if let Some(def_id) = trait_ref.trait_def_id();
if cx.tcx.is_diagnostic_item(sym::Default, def_id);
if let ImplItemKind::Fn(_, b) = &impl_item.kind;
if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
if let Some(adt_def) = cx.tcx.type_of(item.def_id).ty_adt_def();
+ if let attrs = cx.tcx.hir().attrs(item.hir_id());
if !attrs.iter().any(|attr| attr.doc_str().is_some());
if let child_attrs = cx.tcx.hir().attrs(impl_item_hir);
if !child_attrs.iter().any(|attr| attr.doc_str().is_some());
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_then};
use clippy_utils::paths;
use clippy_utils::ty::{implements_trait, is_copy};
-use clippy_utils::{is_automatically_derived, is_lint_allowed, match_def_path};
+use clippy_utils::{is_lint_allowed, match_def_path};
use if_chain::if_chain;
use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
use rustc_hir::{
}) = item.kind
{
let ty = cx.tcx.type_of(item.def_id);
- let attrs = cx.tcx.hir().attrs(item.hir_id());
- let is_automatically_derived = is_automatically_derived(attrs);
+ let is_automatically_derived =
+ cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived);
check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived);
then {
// Look for the PartialEq implementations for `ty`
cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
- let peq_is_automatically_derived = is_automatically_derived(cx.tcx.get_attrs(impl_id));
+ let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
if peq_is_automatically_derived == hash_is_automatically_derived {
return;
then {
// Look for the PartialOrd implementations for `ty`
cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
- let partial_ord_is_automatically_derived = is_automatically_derived(cx.tcx.get_attrs(impl_id));
+ let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
if partial_ord_is_automatically_derived == ord_is_automatically_derived {
return;
use rustc_hir::{
hir_id::HirIdSet,
intravisit::{walk_expr, Visitor},
- Block, Expr, ExprKind, Guard, HirId, Pat, Stmt, StmtKind, UnOp,
+ Block, Expr, ExprKind, Guard, HirId, Let, Pat, Stmt, StmtKind, UnOp,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
let mut is_map_used = self.is_map_used;
for arm in arms {
self.visit_pat(arm.pat);
- if let Some(Guard::If(guard) | Guard::IfLet(_, guard)) = arm.guard {
+ if let Some(Guard::If(guard) | Guard::IfLet(&Let { init: guard, .. })) = arm.guard {
self.visit_non_tail_expr(guard);
}
is_map_used |= self.visit_cond_arm(arm.body);
if check_inputs(cx, body.params, args);
let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap();
let substs = cx.typeck_results().node_substs(body.value.hir_id);
- let call_ty = cx.tcx.type_of(method_def_id).subst(cx.tcx, substs);
+ let call_ty = cx.tcx.bound_type_of(method_def_id).subst(cx.tcx, substs);
if check_sig(cx, closure_ty, call_ty);
then {
span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| {
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::is_must_use_ty;
-use clippy_utils::{match_def_path, must_use_attr, return_ty, trait_ref_of_method};
+use clippy_utils::{match_def_path, return_ty, trait_ref_of_method};
use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT};
pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
let attrs = cx.tcx.hir().attrs(item.hir_id());
- let attr = must_use_attr(attrs);
+ let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use);
if let hir::ItemKind::Fn(ref sig, _generics, ref body_id) = item.kind {
let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
let attrs = cx.tcx.hir().attrs(item.hir_id());
- let attr = must_use_attr(attrs);
+ let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use);
if let Some(attr) = attr {
check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
} else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.def_id).is_none() {
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
let attrs = cx.tcx.hir().attrs(item.hir_id());
- let attr = must_use_attr(attrs);
+ let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use);
if let Some(attr) = attr {
check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
} else if let hir::TraitFn::Provided(eid) = *eid {
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::subst::Subst;
-use rustc_middle::ty::{Opaque, PredicateKind::Trait};
+use rustc_middle::ty::{EarlyBinder, Opaque, PredicateKind::Trait};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{sym, Span};
use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt;
let preds = cx.tcx.explicit_item_bounds(id);
let mut is_future = false;
for &(p, _span) in preds {
- let p = p.subst(cx.tcx, subst);
+ let p = EarlyBinder(p).subst(cx.tcx, subst);
if let Some(trait_pred) = p.to_opt_poly_trait_pred() {
if Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() {
is_future = true;
-use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_opt;
use clippy_utils::{is_lint_allowed, meets_msrv, msrvs};
let id = cx.tcx.hir().local_def_id(v.id);
(matches!(v.data, hir::VariantData::Unit(_))
&& v.ident.as_str().starts_with('_')
- && is_doc_hidden(cx.tcx.get_attrs(id.to_def_id())))
+ && cx.tcx.is_doc_hidden(id.to_def_id()))
.then(|| (id, v.span))
});
if let Some((id, span)) = iter.next()
}
fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool {
- let attrs = cx.tcx.get_attrs(variant_def.def_id);
- clippy_utils::attrs::is_doc_hidden(attrs) || clippy_utils::attrs::is_unstable(attrs)
+ cx.tcx.is_doc_hidden(variant_def.def_id) || cx.tcx.has_attr(variant_def.def_id, sym::unstable)
}
ExprKind::MethodCall(path, arguments, _) => {
let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap();
let substs = cx.typeck_results().node_substs(e.hir_id);
- let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs);
+ let method_type = cx.tcx.bound_type_of(def_id).subst(cx.tcx, substs);
check_arguments(cx, arguments, method_type, path.ident.as_str(), "method");
},
_ => (),
// can't be implemented for unsafe new
return;
}
- if clippy_utils::is_doc_hidden(cx.tcx.hir().attrs(id)) {
+ if cx.tcx.is_doc_hidden(impl_item.def_id) {
// shouldn't be implemented when it is hidden in docs
return;
}
let mut vars = std::mem::take(&mut self.ret_vars);
let _ = arm.guard.as_ref().map(|guard| {
self.visit_expr(match guard {
- Guard::If(expr) | Guard::IfLet(_, expr) => expr,
+ Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => expr,
});
vars.append(&mut self.ret_vars);
});
use clippy_utils::diagnostics::span_lint_hir;
-use clippy_utils::is_automatically_derived;
use if_chain::if_chain;
use rustc_hir::{Impl, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if_chain! {
if let ItemKind::Impl(Impl { of_trait: Some(ref trait_ref), items: impl_items, .. }) = item.kind;
- let attrs = cx.tcx.hir().attrs(item.hir_id());
- if !is_automatically_derived(attrs);
+ if !cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived);
if let Some(eq_trait) = cx.tcx.lang_items().eq_trait();
if trait_ref.path.res.def_id() == eq_trait;
then {
}
// Give up on loops
- if terminator.successors().any(|s| *s == bb) {
+ if terminator.successors().any(|s| s == bb) {
continue;
}
// Short-circuit
if (usage.cloned_used && usage.clone_consumed_or_mutated) ||
// Give up on loops
- tdata.terminator().successors().any(|s| *s == bb)
+ tdata.terminator().successors().any(|s| s == bb)
{
return CloneUsage {
cloned_used: true,
fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
if let Some(adt) = ty.ty_adt_def() {
- if get_attr(cx.sess(), cx.tcx.get_attrs(adt.did()), "has_significant_drop").count() > 0 {
+ if get_attr(cx.sess(), cx.tcx.get_attrs_unchecked(adt.did()), "has_significant_drop").count() > 0 {
return true;
}
}
.non_enum_variant()
.fields
.iter()
- .map(|f| cx.tcx.type_of(f.did).subst(cx.tcx, substs));
+ .map(|f| cx.tcx.bound_type_of(f.did).subst(cx.tcx, substs));
let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else {
return ReducedTy::TypeErasure;
};
out!("if let Some(Guard::If({expr})) = {arm}.guard;");
self.expr(expr);
},
- Some(hir::Guard::IfLet(pat, expr)) => {
- bind!(self, pat, expr);
- out!("if let Some(Guard::IfLet({pat}, {expr}) = {arm}.guard;");
- self.pat(pat);
- self.expr(expr);
+ Some(hir::Guard::IfLet(let_expr)) => {
+ bind!(self, let_expr);
+ out!("if let Some(Guard::IfLet({let_expr}) = {arm}.guard;");
+ self.pat(field!(let_expr.pat));
+ self.expr(field!(let_expr.init));
},
}
self.expr(field!(arm.body));
-use rustc_ast::{ast, attr};
+use rustc_ast::ast;
use rustc_errors::Applicability;
use rustc_session::Session;
+use rustc_ast::attr;
use rustc_span::sym;
use std::str::FromStr;
.any(|l| attr::list_contains_name(&l, sym::hidden))
}
-/// Return true if the attributes contain `#[unstable]`
-pub fn is_unstable(attrs: &[ast::Attribute]) -> bool {
- attrs.iter().any(|attr| attr.has_name(sym::unstable))
-}
use rustc_lint::LateContext;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::ty::subst::{Subst, SubstsRef};
-use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty, TyCtxt};
+use rustc_middle::ty::{self, EarlyBinder, FloatTy, ScalarInt, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_span::symbol::Symbol;
use std::cmp::Ordering::{self, Equal};
let substs = if self.substs.is_empty() {
substs
} else {
- substs.subst(self.lcx.tcx, self.substs)
+ EarlyBinder(substs).subst(self.lcx.tcx, self.substs)
};
let result = self
fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool {
match (left, right) {
(Guard::If(l), Guard::If(r)) => self.eq_expr(l, r),
- (Guard::IfLet(lp, le), Guard::IfLet(rp, re)) => self.eq_pat(lp, rp) && self.eq_expr(le, re),
+ (Guard::IfLet(l), Guard::IfLet(r)) => {
+ self.eq_pat(l.pat, r.pat) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init)
+ },
_ => false,
}
}
pub fn hash_guard(&mut self, g: &Guard<'_>) {
match g {
- Guard::If(expr) | Guard::IfLet(_, expr) => {
+ Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => {
self.hash_expr(expr);
},
}
use std::sync::{Mutex, MutexGuard};
use if_chain::if_chain;
-use rustc_ast::ast::{self, Attribute, LitKind};
+use rustc_ast::ast::{self, LitKind};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::unhash::UnhashMap;
use rustc_hir as hir;
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_ID};
use rustc_hir::hir_id::{HirIdMap, HirIdSet};
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
-use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
use rustc_hir::{
def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl,
- ForeignItem, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource,
+ HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource,
Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind,
TraitRef, TyKind, UnOp,
};
}
}
-/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
-/// implementations have.
-pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
- has_attr(attrs, sym::automatically_derived)
-}
-
pub fn is_self(slf: &Param<'_>) -> bool {
if let PatKind::Binding(.., name, _) = slf.pat.kind {
name.name == kw::SelfLower
None
}
-// Finds the `#[must_use]` attribute, if any
-pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> {
- attrs.iter().find(|a| a.has_name(sym::must_use))
-}
-
// check if expr is calling method or function with #[must_use] attribute
pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let did = match expr.kind {
_ => None,
};
- did.map_or(false, |did| must_use_attr(cx.tcx.get_attrs(did)).is_some())
+ did.map_or(false, |did| cx.tcx.has_attr(did, sym::must_use))
}
/// Checks if an expression represents the identity function
false
}
-struct TestItemNamesVisitor<'tcx> {
- tcx: TyCtxt<'tcx>,
- names: Vec<Symbol>,
-}
-
-impl<'hir> ItemLikeVisitor<'hir> for TestItemNamesVisitor<'hir> {
- fn visit_item(&mut self, item: &Item<'_>) {
- if let ItemKind::Const(ty, _body) = item.kind {
- if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
- // We could also check for the type name `test::TestDescAndFn`
- if let Res::Def(DefKind::Struct, _) = path.res {
- let has_test_marker = self
- .tcx
- .hir()
- .attrs(item.hir_id())
- .iter()
- .any(|a| a.has_name(sym::rustc_test_marker));
- if has_test_marker {
- self.names.push(item.ident.name);
- }
- }
- }
- }
- }
- fn visit_trait_item(&mut self, _: &TraitItem<'_>) {}
- fn visit_impl_item(&mut self, _: &ImplItem<'_>) {}
- fn visit_foreign_item(&mut self, _: &ForeignItem<'_>) {}
-}
-
static TEST_ITEM_NAMES_CACHE: SyncOnceCell<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = SyncOnceCell::new();
fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
match map.entry(module) {
Entry::Occupied(entry) => f(entry.get()),
Entry::Vacant(entry) => {
- let mut visitor = TestItemNamesVisitor { tcx, names: Vec::new() };
- tcx.hir().visit_item_likes_in_module(module, &mut visitor);
- visitor.names.sort_unstable();
- f(&*entry.insert(visitor.names))
+ let mut names = Vec::new();
+ for id in tcx.hir().module_items(module) {
+ if matches!(tcx.def_kind(id.def_id), DefKind::Const)
+ && let item = tcx.hir().item(id)
+ && let ItemKind::Const(ty, _body) = item.kind {
+ if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
+ // We could also check for the type name `test::TestDescAndFn`
+ if let Res::Def(DefKind::Struct, _) = path.res {
+ let has_test_marker = tcx
+ .hir()
+ .attrs(item.hir_id())
+ .iter()
+ .any(|a| a.has_name(sym::rustc_test_marker));
+ if has_test_marker {
+ names.push(item.ident.name);
+ }
+ }
+ }
+ }
+ }
+ names.sort_unstable();
+ f(&*entry.insert(names))
},
}
}
use rustc_semver::RustcVersion;
use rustc_span::symbol::sym;
use rustc_span::Span;
-use rustc_target::spec::abi::Abi::RustIntrinsic;
use std::borrow::Cow;
type McfResult = Result<(), (Span, Cow<'static, str>)>;
// within const fns. `transmute` is allowed in all other const contexts.
// This won't really scale to more intrinsics or functions. Let's allow const
// transmutes in const fn before we add more hacks to this.
- if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute {
+ if tcx.is_intrinsic(fn_def_id) && tcx.item_name(fn_def_id) == sym::transmute {
return Err((
span,
"can only call `transmute` from const items, not `const fn`".into(),
use rustc_trait_selection::traits::query::normalize::AtExt;
use std::iter;
-use crate::{match_def_path, must_use_attr, path_res, paths};
+use crate::{match_def_path, path_res, paths};
// Checks if the given type implements copy.
pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
// Returns whether the type has #[must_use] attribute
pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
match ty.kind() {
- ty::Adt(adt, _) => must_use_attr(cx.tcx.get_attrs(adt.did())).is_some(),
- ty::Foreign(ref did) => must_use_attr(cx.tcx.get_attrs(*did)).is_some(),
+ ty::Adt(adt, _) => cx.tcx.has_attr(adt.did(), sym::must_use),
+ ty::Foreign(did) => cx.tcx.has_attr(*did, sym::must_use),
ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => {
// for the Array case we don't need to care for the len == 0 case
// because we don't want to lint functions returning empty arrays
is_must_use_ty(cx, *ty)
},
ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)),
- ty::Opaque(ref def_id, _) => {
+ ty::Opaque(def_id, _) => {
for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
if let ty::PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() {
- if must_use_attr(cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() {
+ if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) {
return true;
}
}
ty::Dynamic(binder, _) => {
for predicate in binder.iter() {
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
- if must_use_attr(cx.tcx.get_attrs(trait_ref.def_id)).is_some() {
+ if cx.tcx.has_attr(trait_ref.def_id, sym::must_use) {
return true;
}
}
let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs();
match *ty.kind() {
ty::Closure(_, subs) => Some(ExprFnSig::Closure(subs.as_closure().sig())),
- ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).subst(cx.tcx, subs))),
+ ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs))),
ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)),
ty::Dynamic(bounds, _) => {
let lang_items = cx.tcx.lang_items();
LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation
-error: aborting due to 3 previous errors
+error: constant expression depends on a generic parameter
+ --> $DIR/ice-6252.rs:13:9
+ |
+LL | [1; <Multiply<Five, Five>>::VAL];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this may fail depending on what value the parameter takes
+
+error: aborting due to 4 previous errors
Some errors have detailed explanations: E0046, E0412.
For more information about an error, try `rustc --explain E0046`.
/// Only run tests that match these filters
pub filters: Vec<String>,
+ /// Skip tests tests matching these substrings. Corresponds to
+ /// `test::TestOpts::skip`. `filter_exact` does not apply to these flags.
+ pub skip: Vec<String>,
+
/// Exactly match the filter, rather than a substring
pub filter_exact: bool,
)
.optopt("", "run", "whether to execute run-* tests", "auto | always | never")
.optflag("", "ignored", "run tests marked as ignored")
+ .optmulti("", "skip", "skip tests matching SUBSTRING. Can be passed multiple times", "SUBSTRING")
.optflag("", "exact", "filters match exactly")
.optopt(
"",
debugger: None,
run_ignored,
filters: matches.free.clone(),
+ skip: matches.opt_strs("skip"),
filter_exact: matches.opt_present("exact"),
force_pass_mode: matches.opt_str("pass").map(|mode| {
mode.parse::<PassMode>()
logv(c, format!("mode: {}", config.mode));
logv(c, format!("run_ignored: {}", config.run_ignored));
logv(c, format!("filters: {:?}", config.filters));
+ logv(c, format!("skip: {:?}", config.skip));
logv(c, format!("filter_exact: {}", config.filter_exact));
logv(
c,
shuffle: false,
shuffle_seed: None,
test_threads: None,
- skip: vec![],
+ skip: config.skip.clone(),
list: false,
options: test::Options::new(),
time_options: None,
debug!("found test file: {:?}", file_path.display());
let paths =
TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() };
+
tests.extend(make_test(config, &paths, inputs))
} else if file_path.is_dir() {
let relative_file_path = relative_dir_path.join(file.file_name());
normalize_path(parent_dir, "$DIR");
// Paths into the libstd/libcore
- let src_dir = self
- .config
- .src_base
- .parent()
- .unwrap()
- .parent()
- .unwrap()
- .parent()
- .unwrap()
- .join("library");
+ let base_dir = self.config.src_base.parent().unwrap().parent().unwrap().parent().unwrap();
+ let src_dir = base_dir.join("library");
normalize_path(&src_dir, "$SRC_DIR");
+ // `ui-fulldeps` tests can show paths to the compiler source when testing macros from
+ // `rustc_macros`
+ // eg. /home/user/rust/compiler
+ let compiler_src_dir = base_dir.join("compiler");
+ normalize_path(&compiler_src_dir, "$COMPILER_DIR");
+
if let Some(virtual_rust_source_base_dir) =
option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(PathBuf::from)
{
normalize_path(&virtual_rust_source_base_dir.join("library"), "$SRC_DIR");
+ normalize_path(&virtual_rust_source_base_dir.join("compiler"), "$COMPILER_DIR");
}
// Paths into the build directory
fn register_all() -> Vec<(&'static str, Option<&'static str>)> {
let mut long_codes: Vec<(&'static str, Option<&'static str>)> = Vec::new();
macro_rules! register_diagnostics {
- ($($ecode:ident: $message:expr,)*) => (
- register_diagnostics!{$($ecode:$message,)* ;}
- );
-
($($ecode:ident: $message:expr,)* ; $($code:ident,)*) => (
$(
{long_codes.extend([
-Subproject commit 3b8b6aa8b689971d3b8776cefe3d809501e1b8ff
+Subproject commit 19ef76477c0ea1722f91ab6b1c2dcb365f064992
try {
// This is more convenient that setting fields one by one.
let args = [
- "--variable", "DOC_PATH", opts["doc_folder"],
+ "--variable", "DOC_PATH", opts["doc_folder"], "--enable-fail-on-js-error",
];
if (opts["debug"]) {
debug = true;
const fs = require('fs');
const path = require('path');
-function getNextStep(content, pos, stop) {
- while (pos < content.length && content[pos] !== stop &&
- (content[pos] === ' ' || content[pos] === '\t' || content[pos] === '\n')) {
- pos += 1;
- }
- if (pos >= content.length) {
- return null;
- }
- if (content[pos] !== stop) {
- return pos * -1;
- }
- return pos;
-}
-
-// Stupid function extractor based on indent. Doesn't support block
-// comments. If someone puts a ' or an " in a block comment this
-// will blow up. Template strings are not tested and might also be
-// broken.
-function extractFunction(content, functionName) {
- var level = 0;
- var splitter = "function " + functionName + "(";
- var stop;
- var pos, start;
-
- while (true) {
- start = content.indexOf(splitter);
- if (start === -1) {
- break;
- }
- pos = start;
- while (pos < content.length && content[pos] !== ')') {
- pos += 1;
- }
- if (pos >= content.length) {
- break;
- }
- pos = getNextStep(content, pos + 1, '{');
- if (pos === null) {
- break;
- } else if (pos < 0) {
- content = content.slice(-pos);
- continue;
- }
- while (pos < content.length) {
- // Eat single-line comments
- if (content[pos] === '/' && pos > 0 && content[pos - 1] === '/') {
- do {
- pos += 1;
- } while (pos < content.length && content[pos] !== '\n');
-
- // Eat multiline comment.
- } else if (content[pos] === '*' && pos > 0 && content[pos - 1] === '/') {
- do {
- pos += 1;
- } while (pos < content.length && content[pos] !== '/' && content[pos - 1] !== '*');
-
- // Eat quoted strings
- } else if ((content[pos] === '"' || content[pos] === "'" || content[pos] === "`") &&
- (pos === 0 || content[pos - 1] !== '/')) {
- stop = content[pos];
- do {
- if (content[pos] === '\\') {
- pos += 1;
- }
- pos += 1;
- } while (pos < content.length && content[pos] !== stop);
-
- // Otherwise, check for block level.
- } else if (content[pos] === '{') {
- level += 1;
- } else if (content[pos] === '}') {
- level -= 1;
- if (level === 0) {
- return content.slice(start, pos + 1);
- }
- }
- pos += 1;
- }
- content = content.slice(start + 1);
- }
- return null;
-}
-
-// Stupid function extractor for array.
-function extractArrayVariable(content, arrayName, kind) {
- if (typeof kind === "undefined") {
- kind = "let ";
- }
- var splitter = kind + arrayName;
- while (true) {
- var start = content.indexOf(splitter);
- if (start === -1) {
- break;
- }
- var pos = getNextStep(content, start, '=');
- if (pos === null) {
- break;
- } else if (pos < 0) {
- content = content.slice(-pos);
- continue;
- }
- pos = getNextStep(content, pos, '[');
- if (pos === null) {
- break;
- } else if (pos < 0) {
- content = content.slice(-pos);
- continue;
- }
- while (pos < content.length) {
- if (content[pos] === '"' || content[pos] === "'") {
- var stop = content[pos];
- do {
- if (content[pos] === '\\') {
- pos += 2;
- } else {
- pos += 1;
- }
- } while (pos < content.length &&
- (content[pos] !== stop || content[pos - 1] === '\\'));
- } else if (content[pos] === ']' &&
- pos + 1 < content.length &&
- content[pos + 1] === ';') {
- return content.slice(start, pos + 2);
- }
- pos += 1;
- }
- content = content.slice(start + 1);
- }
- if (kind === "let ") {
- return extractArrayVariable(content, arrayName, "const ");
- }
- return null;
-}
-
-// Stupid function extractor for variable.
-function extractVariable(content, varName, kind) {
- if (typeof kind === "undefined") {
- kind = "let ";
- }
- var splitter = kind + varName;
- while (true) {
- var start = content.indexOf(splitter);
- if (start === -1) {
- break;
- }
- var pos = getNextStep(content, start, '=');
- if (pos === null) {
- break;
- } else if (pos < 0) {
- content = content.slice(-pos);
- continue;
- }
- while (pos < content.length) {
- if (content[pos] === '"' || content[pos] === "'") {
- var stop = content[pos];
- do {
- if (content[pos] === '\\') {
- pos += 2;
- } else {
- pos += 1;
- }
- } while (pos < content.length &&
- (content[pos] !== stop || content[pos - 1] === '\\'));
- } else if (content[pos] === ';' || content[pos] === ',') {
- return content.slice(start, pos + 1);
- }
- pos += 1;
- }
- content = content.slice(start + 1);
- }
- if (kind === "let ") {
- return extractVariable(content, varName, "const ");
- }
- return null;
-}
-
function loadContent(content) {
var Module = module.constructor;
var m = new Module();
return fs.readFileSync(filePath, 'utf8');
}
-function loadThings(thingsToLoad, kindOfLoad, funcToCall, fileContent) {
- var content = '';
- for (var i = 0; i < thingsToLoad.length; ++i) {
- var tmp = funcToCall(fileContent, thingsToLoad[i]);
- if (tmp === null) {
- console.log('unable to find ' + kindOfLoad + ' "' + thingsToLoad[i] + '"');
- process.exit(1);
- }
- content += tmp;
- content += 'exports.' + thingsToLoad[i] + ' = ' + thingsToLoad[i] + ';';
- }
- return content;
-}
-
function contentToDiffLine(key, value) {
return `"${key}": "${value}",`;
}
return null;
}
-function loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate) {
- if (searchIndex[searchIndex.length - 1].length === 0) {
- searchIndex.pop();
- }
- searchIndex.pop();
- var fullSearchIndex = searchIndex.join("\n") + '\nexports.rawSearchIndex = searchIndex;';
- searchIndex = loadContent(fullSearchIndex);
- var finalJS = "";
-
- var arraysToLoad = ["itemTypes"];
- var variablesToLoad = ["MAX_LEV_DISTANCE", "MAX_RESULTS", "NO_TYPE_FILTER",
- "GENERICS_DATA", "NAME", "INPUTS_DATA", "OUTPUT_DATA",
- "TY_PRIMITIVE", "TY_KEYWORD",
- "levenshtein_row2"];
- // execQuery first parameter is built in getQuery (which takes in the search input).
- // execQuery last parameter is built in buildIndex.
- // buildIndex requires the hashmap from search-index.
- var functionsToLoad = ["buildHrefAndPath", "pathSplitter", "levenshtein", "validateResult",
- "buildIndex", "execQuery", "parseQuery", "createQueryResults",
- "isWhitespace", "isSpecialStartCharacter", "isStopCharacter",
- "parseInput", "getItemsBefore", "getNextElem", "createQueryElement",
- "isReturnArrow", "isPathStart", "getStringElem", "newParsedQuery",
- "itemTypeFromName", "isEndCharacter", "isErrorCharacter",
- "isIdentCharacter", "isSeparatorCharacter", "getIdentEndPosition",
- "checkExtraTypeFilterCharacters", "isWhitespaceCharacter"];
-
- const functions = ["hasOwnPropertyRustdoc", "onEach"];
- ALIASES = {};
- finalJS += 'window = { "currentCrate": "' + crate + '", rootPath: "../" };\n';
- finalJS += loadThings(functions, 'function', extractFunction, storageJs);
- finalJS += loadThings(arraysToLoad, 'array', extractArrayVariable, searchJs);
- finalJS += loadThings(variablesToLoad, 'variable', extractVariable, searchJs);
- finalJS += loadThings(functionsToLoad, 'function', extractFunction, searchJs);
-
- var loaded = loadContent(finalJS);
- var index = loaded.buildIndex(searchIndex.rawSearchIndex);
-
- return [loaded, index];
-}
-
// This function checks if `expected` has all the required fields needed for the checks.
function checkNeededFields(fullPath, expected, error_text, queryName, position) {
let fieldsToCheck;
'compared to EXPECTED');
}
} else if (expected !== null && typeof expected !== "undefined" &&
- expected.constructor == Object)
- {
+ expected.constructor == Object) {
for (const key in expected) {
if (!expected.hasOwnProperty(key)) {
continue;
}
}
-function runParser(query, expected, loaded, loadedFile, queryName) {
+function runParser(query, expected, parseQuery, queryName) {
var error_text = [];
checkNeededFields("", expected, error_text, queryName, null);
if (error_text.length === 0) {
- valueCheck('', expected, loaded.parseQuery(query), error_text, queryName);
+ valueCheck('', expected, parseQuery(query), error_text, queryName);
}
return error_text;
}
-function runSearch(query, expected, index, loaded, loadedFile, queryName) {
- const filter_crate = loadedFile.FILTER_CRATE;
+function runSearch(query, expected, doSearch, loadedFile, queryName) {
const ignore_order = loadedFile.ignore_order;
const exact_check = loadedFile.exact_check;
- var results = loaded.execQuery(loaded.parseQuery(query), index, filter_crate);
+ var results = doSearch(query, loadedFile.FILTER_CRATE);
var error_text = [];
for (var key in expected) {
return 0;
}
-function runChecks(testFile, loaded, index) {
+function runChecks(testFile, doSearch, parseQuery) {
var checkExpected = false;
var checkParsed = false;
var testFileContent = readFile(testFile) + 'exports.QUERY = QUERY;';
if (checkExpected) {
res += runCheck(loadedFile, "EXPECTED", (query, expected, text) => {
- return runSearch(query, expected, index, loaded, loadedFile, text);
+ return runSearch(query, expected, doSearch, loadedFile, text);
});
}
if (checkParsed) {
res += runCheck(loadedFile, "PARSED", (query, expected, text) => {
- return runParser(query, expected, loaded, loadedFile, text);
+ return runParser(query, expected, parseQuery, text);
});
}
return res;
}
-function load_files(doc_folder, resource_suffix, crate) {
- var searchJs = readFile(path.join(doc_folder, "search" + resource_suffix + ".js"));
- var storageJs = readFile(path.join(doc_folder, "storage" + resource_suffix + ".js"));
- var searchIndex = readFile(
- path.join(doc_folder, "search-index" + resource_suffix + ".js")).split("\n");
-
- return loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate);
+/**
+ * Load searchNNN.js and search-indexNNN.js.
+ *
+ * @param {string} doc_folder - Path to a folder generated by running rustdoc
+ * @param {string} resource_suffix - Version number between filename and .js, e.g. "1.59.0"
+ * @returns {Object} - Object containing two keys: `doSearch`, which runs a search
+ * with the loaded index and returns a table of results; and `parseQuery`, which is the
+ * `parseQuery` function exported from the search module.
+ */
+function loadSearchJS(doc_folder, resource_suffix) {
+ const searchJs = path.join(doc_folder, "search" + resource_suffix + ".js");
+ const searchIndexJs = path.join(doc_folder, "search-index" + resource_suffix + ".js");
+ const searchIndex = require(searchIndexJs);
+ const searchModule = require(searchJs);
+ const searchWords = searchModule.initSearch(searchIndex.searchIndex);
+
+ return {
+ doSearch: function (queryStr, filterCrate, currentCrate) {
+ return searchModule.execQuery(searchModule.parseQuery(queryStr), searchWords,
+ filterCrate, currentCrate);
+ },
+ parseQuery: searchModule.parseQuery,
+ }
}
function showHelp() {
return null;
}
-function checkFile(test_file, opts, loaded, index) {
- const test_name = path.basename(test_file, ".js");
-
- process.stdout.write('Checking "' + test_name + '" ... ');
- return runChecks(test_file, loaded, index);
-}
-
function main(argv) {
var opts = parseOptions(argv.slice(2));
if (opts === null) {
return 1;
}
- var [loaded, index] = load_files(
+ let parseAndSearch = loadSearchJS(
opts["doc_folder"],
- opts["resource_suffix"],
- opts["crate_name"]);
+ opts["resource_suffix"]);
var errors = 0;
+ let doSearch = function (queryStr, filterCrate) {
+ return parseAndSearch.doSearch(queryStr, filterCrate, opts["crate_name"]);
+ };
+
if (opts["test_file"].length !== 0) {
- opts["test_file"].forEach(function(file) {
- errors += checkFile(file, opts, loaded, index);
+ opts["test_file"].forEach(function (file) {
+ process.stdout.write(`Testing ${file} ... `);
+ errors += runChecks(file, doSearch, parseAndSearch.parseQuery);
});
} else if (opts["test_folder"].length !== 0) {
- fs.readdirSync(opts["test_folder"]).forEach(function(file) {
+ fs.readdirSync(opts["test_folder"]).forEach(function (file) {
if (!file.endsWith(".js")) {
return;
}
- errors += checkFile(path.join(opts["test_folder"], file), opts, loaded, index);
+ process.stdout.write(`Testing ${file} ... `);
+ errors += runChecks(path.join(opts["test_folder"], file), doSearch,
+ parseAndSearch.parseQuery);
});
}
return errors > 0 ? 1 : 0;