Merge from rustc

This commit is contained in:
The Miri Cronjob Bot 2024-10-04 05:07:56 +00:00
commit 7014ae87df
295 changed files with 4256 additions and 1741 deletions

9
.gitignore vendored
View file

@ -86,4 +86,13 @@ package.json
## Rustdoc GUI tests
tests/rustdoc-gui/src/**.lock
## direnv
.envrc
.direnv/
## nix
flake.nix
flake.lock
default.nix
# Before adding new lines, see the comment at the top.

View file

@ -199,6 +199,12 @@ dependencies = [
"object 0.36.4",
]
[[package]]
name = "arrayref"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
[[package]]
name = "arrayvec"
version = "0.7.6"
@ -262,6 +268,19 @@ version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "blake3"
version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d08263faac5cde2a4d52b513dadb80846023aade56fcd8fc99ba73ba8050e92"
dependencies = [
"arrayref",
"arrayvec",
"cc",
"cfg-if",
"constant_time_eq",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
@ -722,6 +741,12 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "constant_time_eq"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
@ -4378,6 +4403,7 @@ dependencies = [
name = "rustc_span"
version = "0.0.0"
dependencies = [
"blake3",
"derive-where",
"indexmap",
"itoa",

View file

@ -286,7 +286,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
parent: this.local_def_id(id),
in_assoc_ty: false,
},
fn_kind: None,
}),
},
);
@ -983,7 +982,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
parent: this.local_def_id(i.id),
in_assoc_ty: true,
},
fn_kind: None,
});
hir::ImplItemKind::Type(ty)
}

View file

@ -288,12 +288,7 @@ enum ImplTraitContext {
/// Example: `fn foo() -> impl Debug`, where `impl Debug` is conceptually
/// equivalent to a new opaque type like `type T = impl Debug; fn foo() -> T`.
///
OpaqueTy {
origin: hir::OpaqueTyOrigin,
/// Only used to change the lifetime capture rules, since
/// RPITIT captures all in scope, RPIT does not.
fn_kind: Option<FnDeclKind>,
},
OpaqueTy { origin: hir::OpaqueTyOrigin },
/// `impl Trait` is unstably accepted in this position.
FeatureGated(ImplTraitPosition, Symbol),
/// `impl Trait` is not accepted in this position.
@ -1404,14 +1399,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
TyKind::ImplTrait(def_node_id, bounds) => {
let span = t.span;
match itctx {
ImplTraitContext::OpaqueTy { origin, fn_kind } => self.lower_opaque_impl_trait(
span,
origin,
*def_node_id,
bounds,
fn_kind,
itctx,
),
ImplTraitContext::OpaqueTy { origin } => {
self.lower_opaque_impl_trait(span, origin, *def_node_id, bounds, itctx)
}
ImplTraitContext::Universal => {
if let Some(span) = bounds.iter().find_map(|bound| match *bound {
ast::GenericBound::Use(_, span) => Some(span),
@ -1513,7 +1503,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
origin: hir::OpaqueTyOrigin,
opaque_ty_node_id: NodeId,
bounds: &GenericBounds,
fn_kind: Option<FnDeclKind>,
itctx: ImplTraitContext,
) -> hir::TyKind<'hir> {
// Make sure we know that some funky desugaring has been going on here.
@ -1555,11 +1544,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
.map(|(ident, id, _)| Lifetime { id, ident })
.collect()
}
hir::OpaqueTyOrigin::FnReturn(..) => {
if matches!(
fn_kind.expect("expected RPITs to be lowered with a FnKind"),
FnDeclKind::Impl | FnDeclKind::Trait
) || self.tcx.features().lifetime_capture_rules_2024
hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl, .. } => {
if in_trait_or_impl.is_some()
|| self.tcx.features().lifetime_capture_rules_2024
|| span.at_least_rust_2024()
{
// return-position impl trait in trait was decided to capture all
@ -1576,16 +1563,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
lifetime_collector::lifetimes_in_bounds(self.resolver, bounds)
}
}
hir::OpaqueTyOrigin::AsyncFn(..) => {
hir::OpaqueTyOrigin::AsyncFn { .. } => {
unreachable!("should be using `lower_async_fn_ret_ty`")
}
}
};
debug!(?captured_lifetimes_to_duplicate);
match fn_kind {
// Deny `use<>` on RPITIT in trait/trait-impl for now.
Some(FnDeclKind::Trait | FnDeclKind::Impl) => {
// Feature gate for RPITIT + use<..>
match origin {
rustc_hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl: Some(_), .. } => {
if let Some(span) = bounds.iter().find_map(|bound| match *bound {
ast::GenericBound::Use(_, span) => Some(span),
_ => None,
@ -1593,20 +1580,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.tcx.dcx().emit_err(errors::NoPreciseCapturesOnRpitit { span });
}
}
None
| Some(
FnDeclKind::Fn
| FnDeclKind::Inherent
| FnDeclKind::ExternFn
| FnDeclKind::Closure
| FnDeclKind::Pointer,
) => {}
_ => {}
}
self.lower_opaque_inner(
opaque_ty_node_id,
origin,
matches!(fn_kind, Some(FnDeclKind::Trait)),
captured_lifetimes_to_duplicate,
span,
opaque_ty_span,
@ -1618,7 +1597,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
&mut self,
opaque_ty_node_id: NodeId,
origin: hir::OpaqueTyOrigin,
in_trait: bool,
captured_lifetimes_to_duplicate: FxIndexSet<Lifetime>,
span: Span,
opaque_ty_span: Span,
@ -1747,7 +1725,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
bounds,
origin,
lifetime_mapping,
in_trait,
};
// Generate an `type Foo = impl Trait;` declaration.
@ -1776,7 +1753,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
hir::TyKind::OpaqueDef(
hir::ItemId { owner_id: hir::OwnerId { def_id: opaque_ty_def_id } },
generic_args,
in_trait,
)
}
@ -1864,12 +1840,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
None => match &decl.output {
FnRetTy::Ty(ty) => {
let itctx = match kind {
FnDeclKind::Fn
| FnDeclKind::Inherent
| FnDeclKind::Trait
| FnDeclKind::Impl => ImplTraitContext::OpaqueTy {
origin: hir::OpaqueTyOrigin::FnReturn(self.local_def_id(fn_node_id)),
fn_kind: Some(kind),
FnDeclKind::Fn | FnDeclKind::Inherent => ImplTraitContext::OpaqueTy {
origin: hir::OpaqueTyOrigin::FnReturn {
parent: self.local_def_id(fn_node_id),
in_trait_or_impl: None,
},
},
FnDeclKind::Trait => ImplTraitContext::OpaqueTy {
origin: hir::OpaqueTyOrigin::FnReturn {
parent: self.local_def_id(fn_node_id),
in_trait_or_impl: Some(hir::RpitContext::Trait),
},
},
FnDeclKind::Impl => ImplTraitContext::OpaqueTy {
origin: hir::OpaqueTyOrigin::FnReturn {
parent: self.local_def_id(fn_node_id),
in_trait_or_impl: Some(hir::RpitContext::TraitImpl),
},
},
FnDeclKind::ExternFn => {
ImplTraitContext::Disallowed(ImplTraitPosition::ExternFnReturn)
@ -1951,10 +1938,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
.map(|(ident, id, _)| Lifetime { id, ident })
.collect();
let in_trait_or_impl = match fn_kind {
FnDeclKind::Trait => Some(hir::RpitContext::Trait),
FnDeclKind::Impl => Some(hir::RpitContext::TraitImpl),
FnDeclKind::Fn | FnDeclKind::Inherent => None,
FnDeclKind::ExternFn | FnDeclKind::Closure | FnDeclKind::Pointer => unreachable!(),
};
let opaque_ty_ref = self.lower_opaque_inner(
opaque_ty_node_id,
hir::OpaqueTyOrigin::AsyncFn(fn_def_id),
matches!(fn_kind, FnDeclKind::Trait),
hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, in_trait_or_impl },
captured_lifetimes,
span,
opaque_ty_span,
@ -1964,8 +1957,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
coro,
opaque_ty_span,
ImplTraitContext::OpaqueTy {
origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
fn_kind: Some(fn_kind),
origin: hir::OpaqueTyOrigin::FnReturn {
parent: fn_def_id,
in_trait_or_impl,
},
},
);
arena_vec![this; bound]

View file

@ -63,7 +63,7 @@ impl TraitOrTraitImpl {
}
struct AstValidator<'a> {
session: &'a Session,
sess: &'a Session,
features: &'a Features,
/// The span of the `extern` in an `extern { ... }` block, if any.
@ -267,7 +267,7 @@ impl<'a> AstValidator<'a> {
}
fn dcx(&self) -> DiagCtxtHandle<'a> {
self.session.dcx()
self.sess.dcx()
}
fn visibility_not_permitted(&self, vis: &Visibility, note: errors::VisibilityNotPermittedNote) {
@ -359,7 +359,7 @@ impl<'a> AstValidator<'a> {
in_impl: matches!(parent, TraitOrTraitImpl::TraitImpl { .. }),
const_context_label: parent_constness,
remove_const_sugg: (
self.session.source_map().span_extend_while_whitespace(span),
self.sess.source_map().span_extend_while_whitespace(span),
match parent_constness {
Some(_) => rustc_errors::Applicability::MachineApplicable,
None => rustc_errors::Applicability::MaybeIncorrect,
@ -472,7 +472,7 @@ impl<'a> AstValidator<'a> {
fn check_defaultness(&self, span: Span, defaultness: Defaultness) {
if let Defaultness::Default(def_span) = defaultness {
let span = self.session.source_map().guess_head_span(span);
let span = self.sess.source_map().guess_head_span(span);
self.dcx().emit_err(errors::ForbiddenDefault { span, def_span });
}
}
@ -480,7 +480,7 @@ impl<'a> AstValidator<'a> {
/// If `sp` ends with a semicolon, returns it as a `Span`
/// Otherwise, returns `sp.shrink_to_hi()`
fn ending_semi_or_hi(&self, sp: Span) -> Span {
let source_map = self.session.source_map();
let source_map = self.sess.source_map();
let end = source_map.end_point(sp);
if source_map.span_to_snippet(end).is_ok_and(|s| s == ";") {
@ -552,7 +552,7 @@ impl<'a> AstValidator<'a> {
}
fn current_extern_span(&self) -> Span {
self.session.source_map().guess_head_span(self.extern_mod.unwrap())
self.sess.source_map().guess_head_span(self.extern_mod.unwrap())
}
/// An `fn` in `extern { ... }` cannot have qualifiers, e.g. `async fn`.
@ -648,7 +648,7 @@ impl<'a> AstValidator<'a> {
if ident.name.as_str().is_ascii() {
return;
}
let span = self.session.source_map().guess_head_span(item_span);
let span = self.sess.source_map().guess_head_span(item_span);
self.dcx().emit_err(errors::NoMangleAscii { span });
}
@ -753,7 +753,7 @@ impl<'a> AstValidator<'a> {
self.dcx().emit_err(errors::PatternFnPointer { span });
});
if let Extern::Implicit(_) = bfty.ext {
let sig_span = self.session.source_map().next_point(ty.span.shrink_to_lo());
let sig_span = self.sess.source_map().next_point(ty.span.shrink_to_lo());
self.maybe_lint_missing_abi(sig_span, ty.id);
}
}
@ -795,7 +795,7 @@ impl<'a> AstValidator<'a> {
// FIXME(davidtwco): This is a hack to detect macros which produce spans of the
// call site which do not have a macro backtrace. See #61963.
if self
.session
.sess
.source_map()
.span_to_snippet(span)
.is_ok_and(|snippet| !snippet.starts_with("#["))
@ -885,7 +885,7 @@ fn validate_generic_param_order(dcx: DiagCtxtHandle<'_>, generics: &[GenericPara
impl<'a> Visitor<'a> for AstValidator<'a> {
fn visit_attribute(&mut self, attr: &Attribute) {
validate_attr::check_attr(&self.session.psess, attr);
validate_attr::check_attr(&self.sess.psess, attr);
}
fn visit_ty(&mut self, ty: &'a Ty) {
@ -1192,7 +1192,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
} else if where_clauses.after.has_where_token {
self.dcx().emit_err(errors::WhereClauseAfterTypeAlias {
span: where_clauses.after.span,
help: self.session.is_nightly_build(),
help: self.sess.is_nightly_build(),
});
}
}
@ -1328,7 +1328,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
(BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_))
if !self.features.more_maybe_bounds =>
{
self.session
self.sess
.create_feature_err(
errors::OptionalTraitSupertrait {
span: trait_ref.span,
@ -1341,7 +1341,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
(BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_))
if !self.features.more_maybe_bounds =>
{
self.session
self.sess
.create_feature_err(
errors::OptionalTraitObject { span: trait_ref.span },
sym::more_maybe_bounds,
@ -1752,13 +1752,13 @@ fn deny_equality_constraints(
}
pub fn check_crate(
session: &Session,
sess: &Session,
features: &Features,
krate: &Crate,
lints: &mut LintBuffer,
) -> bool {
let mut validator = AstValidator {
session,
sess,
features,
extern_mod: None,
outer_trait_or_trait_impl: None,

View file

@ -832,7 +832,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
fn get_future_inner_return_ty(&self, hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
let hir = self.infcx.tcx.hir();
let hir::TyKind::OpaqueDef(id, _, _) = hir_ty.kind else {
let hir::TyKind::OpaqueDef(id, _) = hir_ty.kind else {
span_bug!(
hir_ty.span,
"lowered return type of async fn is not OpaqueDef: {:?}",

View file

@ -503,8 +503,8 @@ impl<'tcx> LazyOpaqueTyEnv<'tcx> {
let &Self { tcx, def_id, .. } = self;
let origin = tcx.opaque_type_origin(def_id);
let parent = match origin {
hir::OpaqueTyOrigin::FnReturn(parent)
| hir::OpaqueTyOrigin::AsyncFn(parent)
hir::OpaqueTyOrigin::FnReturn { parent, .. }
| hir::OpaqueTyOrigin::AsyncFn { parent, .. }
| hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent,
};
let param_env = tcx.param_env(parent);

View file

@ -630,6 +630,7 @@ pub(crate) fn file_metadata<'ll>(cx: &CodegenCx<'ll, '_>, source_file: &SourceFi
rustc_span::SourceFileHashAlgorithm::Md5 => llvm::ChecksumKind::MD5,
rustc_span::SourceFileHashAlgorithm::Sha1 => llvm::ChecksumKind::SHA1,
rustc_span::SourceFileHashAlgorithm::Sha256 => llvm::ChecksumKind::SHA256,
rustc_span::SourceFileHashAlgorithm::Blake3 => llvm::ChecksumKind::None,
};
let hash_value = hex_encode(source_file.src_hash.hash_bytes());

View file

@ -2632,7 +2632,7 @@ impl<'hir> Ty<'hir> {
}
TyKind::Tup(tys) => tys.iter().any(Self::is_suggestable_infer_ty),
TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => mut_ty.ty.is_suggestable_infer_ty(),
TyKind::OpaqueDef(_, generic_args, _) => are_suggestable_generic_args(generic_args),
TyKind::OpaqueDef(_, generic_args) => are_suggestable_generic_args(generic_args),
TyKind::Path(QPath::TypeRelative(ty, segment)) => {
ty.is_suggestable_infer_ty() || are_suggestable_generic_args(segment.args().args)
}
@ -2762,10 +2762,6 @@ pub struct OpaqueTy<'hir> {
/// This mapping associated a captured lifetime (first parameter) with the new
/// early-bound lifetime that was generated for the opaque.
pub lifetime_mapping: &'hir [(&'hir Lifetime, LocalDefId)],
/// Whether the opaque is a return-position impl trait (or async future)
/// originating from a trait method. This makes it so that the opaque is
/// lowered as an associated type.
pub in_trait: bool,
}
#[derive(Debug, Clone, Copy, HashStable_Generic)]
@ -2802,13 +2798,29 @@ pub struct PreciseCapturingNonLifetimeArg {
pub res: Res,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, HashStable_Generic)]
pub enum RpitContext {
Trait,
TraitImpl,
}
/// From whence the opaque type came.
#[derive(Copy, Clone, PartialEq, Eq, Debug, HashStable_Generic)]
pub enum OpaqueTyOrigin {
/// `-> impl Trait`
FnReturn(LocalDefId),
FnReturn {
/// The defining function.
parent: LocalDefId,
// Whether this is an RPITIT (return position impl trait in trait)
in_trait_or_impl: Option<RpitContext>,
},
/// `async fn`
AsyncFn(LocalDefId),
AsyncFn {
/// The defining function.
parent: LocalDefId,
// Whether this is an AFIT (async fn in trait)
in_trait_or_impl: Option<RpitContext>,
},
/// type aliases: `type Foo = impl Trait;`
TyAlias {
/// The type alias or associated type parent of the TAIT/ATPIT
@ -2856,7 +2868,7 @@ pub enum TyKind<'hir> {
/// possibly parameters) that are actually bound on the `impl Trait`.
///
/// The last parameter specifies whether this opaque appears in a trait definition.
OpaqueDef(ItemId, &'hir [GenericArg<'hir>], bool),
OpaqueDef(ItemId, &'hir [GenericArg<'hir>]),
/// A trait object type `Bound1 + Bound2 + Bound3`
/// where `Bound` is a trait or a lifetime.
TraitObject(

View file

@ -894,7 +894,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul
TyKind::Path(ref qpath) => {
try_visit!(visitor.visit_qpath(qpath, typ.hir_id, typ.span));
}
TyKind::OpaqueDef(item_id, lifetimes, _in_trait) => {
TyKind::OpaqueDef(item_id, lifetimes) => {
try_visit!(visitor.visit_nested_item(item_id));
walk_list!(visitor, visit_generic_arg, lifetimes);
}

View file

@ -336,9 +336,9 @@ fn check_opaque_meets_bounds<'tcx>(
origin: &hir::OpaqueTyOrigin,
) -> Result<(), ErrorGuaranteed> {
let defining_use_anchor = match *origin {
hir::OpaqueTyOrigin::FnReturn(did)
| hir::OpaqueTyOrigin::AsyncFn(did)
| hir::OpaqueTyOrigin::TyAlias { parent: did, .. } => did,
hir::OpaqueTyOrigin::FnReturn { parent, .. }
| hir::OpaqueTyOrigin::AsyncFn { parent, .. }
| hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent,
};
let param_env = tcx.param_env(defining_use_anchor);
@ -346,8 +346,8 @@ fn check_opaque_meets_bounds<'tcx>(
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let args = match *origin {
hir::OpaqueTyOrigin::FnReturn(parent)
| hir::OpaqueTyOrigin::AsyncFn(parent)
hir::OpaqueTyOrigin::FnReturn { parent, .. }
| hir::OpaqueTyOrigin::AsyncFn { parent, .. }
| hir::OpaqueTyOrigin::TyAlias { parent, .. } => GenericArgs::identity_for_item(
tcx, parent,
)
@ -409,7 +409,7 @@ fn check_opaque_meets_bounds<'tcx>(
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin {
if let hir::OpaqueTyOrigin::FnReturn { .. } | hir::OpaqueTyOrigin::AsyncFn { .. } = origin {
// HACK: this should also fall through to the hidden type check below, but the original
// implementation had a bug where equivalent lifetimes are not identical. This caused us
// to reject existing stable code that is otherwise completely fine. The real fix is to
@ -736,8 +736,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) {
check_opaque_precise_captures(tcx, def_id);
let origin = tcx.opaque_type_origin(def_id);
if let hir::OpaqueTyOrigin::FnReturn(fn_def_id)
| hir::OpaqueTyOrigin::AsyncFn(fn_def_id) = origin
if let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. }
| hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, .. } = origin
&& let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(fn_def_id)
&& let (_, hir::TraitFn::Required(..)) = trait_item.expect_fn()
{

View file

@ -94,8 +94,8 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
if !tcx.hir().get_if_local(impl_opaque.def_id).is_some_and(|node| {
matches!(
node.expect_item().expect_opaque_ty().origin,
hir::OpaqueTyOrigin::AsyncFn(def_id) | hir::OpaqueTyOrigin::FnReturn(def_id)
if def_id == impl_m.def_id.expect_local()
hir::OpaqueTyOrigin::AsyncFn { parent, .. } | hir::OpaqueTyOrigin::FnReturn { parent, .. }
if parent == impl_m.def_id.expect_local()
)
}) {
report_mismatched_rpitit_signature(

View file

@ -210,11 +210,11 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
Node::Item(item) => match item.kind {
ItemKind::OpaqueTy(&hir::OpaqueTy {
origin:
hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id),
in_trait,
hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, in_trait_or_impl }
| hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, in_trait_or_impl },
..
}) => {
if in_trait {
if in_trait_or_impl.is_some() {
assert_matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn);
} else {
assert_matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn | DefKind::Fn);

View file

@ -370,39 +370,47 @@ pub(super) fn explicit_item_bounds_with_filter(
..
}) => associated_type_bounds(tcx, def_id, bounds, *span, filter),
hir::Node::Item(hir::Item {
kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, in_trait: false, .. }),
kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, origin, .. }),
span,
..
}) => {
let args = GenericArgs::identity_for_item(tcx, def_id);
let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args);
let bounds = opaque_type_bounds(tcx, def_id, bounds, item_ty, *span, filter);
assert_only_contains_predicates_from(filter, bounds, item_ty);
bounds
}
// Since RPITITs are lowered as projections in `<dyn HirTyLowerer>::lower_ty`, when we're
// asking for the item bounds of the *opaques* in a trait's default method signature, we
// need to map these projections back to opaques.
hir::Node::Item(hir::Item {
kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, in_trait: true, origin, .. }),
span,
..
}) => {
let (hir::OpaqueTyOrigin::FnReturn(fn_def_id)
| hir::OpaqueTyOrigin::AsyncFn(fn_def_id)) = *origin
else {
span_bug!(*span, "RPITIT cannot be a TAIT, but got origin {origin:?}");
};
let args = GenericArgs::identity_for_item(tcx, def_id);
let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args);
let bounds = &*tcx.arena.alloc_slice(
&opaque_type_bounds(tcx, def_id, bounds, item_ty, *span, filter)
.to_vec()
.fold_with(&mut AssocTyToOpaque { tcx, fn_def_id: fn_def_id.to_def_id() }),
);
assert_only_contains_predicates_from(filter, bounds, item_ty);
bounds
}
}) => match origin {
// Since RPITITs are lowered as projections in `<dyn HirTyLowerer>::lower_ty`,
// when we're asking for the item bounds of the *opaques* in a trait's default
// method signature, we need to map these projections back to opaques.
rustc_hir::OpaqueTyOrigin::FnReturn {
parent,
in_trait_or_impl: Some(hir::RpitContext::Trait),
}
| rustc_hir::OpaqueTyOrigin::AsyncFn {
parent,
in_trait_or_impl: Some(hir::RpitContext::Trait),
} => {
let args = GenericArgs::identity_for_item(tcx, def_id);
let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args);
let bounds = &*tcx.arena.alloc_slice(
&opaque_type_bounds(tcx, def_id, bounds, item_ty, *span, filter)
.to_vec()
.fold_with(&mut AssocTyToOpaque { tcx, fn_def_id: parent.to_def_id() }),
);
assert_only_contains_predicates_from(filter, bounds, item_ty);
bounds
}
rustc_hir::OpaqueTyOrigin::FnReturn {
parent: _,
in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl),
}
| rustc_hir::OpaqueTyOrigin::AsyncFn {
parent: _,
in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl),
}
| rustc_hir::OpaqueTyOrigin::TyAlias { parent: _, .. } => {
let args = GenericArgs::identity_for_item(tcx, def_id);
let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args);
let bounds = opaque_type_bounds(tcx, def_id, bounds, item_ty, *span, filter);
assert_only_contains_predicates_from(filter, bounds, item_ty);
bounds
}
},
hir::Node::Item(hir::Item { kind: hir::ItemKind::TyAlias(..), .. }) => &[],
_ => bug!("item_bounds called on {:?}", def_id),
};

View file

@ -332,7 +332,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
// and the duplicated parameter, to ensure that they do not get out of sync.
if let Node::Item(&Item { kind: ItemKind::OpaqueTy(..), .. }) = node {
let opaque_ty_node = tcx.parent_hir_node(hir_id);
let Node::Ty(&hir::Ty { kind: TyKind::OpaqueDef(_, lifetimes, _), .. }) = opaque_ty_node
let Node::Ty(&hir::Ty { kind: TyKind::OpaqueDef(_, lifetimes), .. }) = opaque_ty_node
else {
bug!("unexpected {opaque_ty_node:?}")
};

View file

@ -515,8 +515,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
}
hir::ItemKind::OpaqueTy(&hir::OpaqueTy {
origin:
hir::OpaqueTyOrigin::FnReturn(parent)
| hir::OpaqueTyOrigin::AsyncFn(parent)
hir::OpaqueTyOrigin::FnReturn { parent, .. }
| hir::OpaqueTyOrigin::AsyncFn { parent, .. }
| hir::OpaqueTyOrigin::TyAlias { parent, .. },
generics,
..
@ -689,7 +689,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
};
self.with(scope, |this| this.visit_ty(mt.ty));
}
hir::TyKind::OpaqueDef(item_id, lifetimes, _in_trait) => {
hir::TyKind::OpaqueDef(item_id, lifetimes) => {
// Resolve the lifetimes in the bounds to the lifetime defs in the generics.
// `fn foo<'a>() -> impl MyTrait<'a> { ... }` desugars to
// `type MyAnonTy<'b> = impl MyTrait<'b>;`

View file

@ -618,11 +618,13 @@ pub(super) fn type_of_opaque(
// Opaque types desugared from `impl Trait`.
ItemKind::OpaqueTy(&OpaqueTy {
origin:
hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner),
in_trait,
hir::OpaqueTyOrigin::FnReturn { parent: owner, in_trait_or_impl }
| hir::OpaqueTyOrigin::AsyncFn { parent: owner, in_trait_or_impl },
..
}) => {
if in_trait && !tcx.defaultness(owner).has_value() {
if in_trait_or_impl == Some(hir::RpitContext::Trait)
&& !tcx.defaultness(owner).has_value()
{
span_bug!(
tcx.def_span(def_id),
"tried to get type of this RPITIT with no definition"

View file

@ -2087,21 +2087,41 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself));
self.lower_path(opt_self_ty, path, hir_ty.hir_id, false)
}
&hir::TyKind::OpaqueDef(item_id, lifetimes, in_trait) => {
&hir::TyKind::OpaqueDef(item_id, lifetimes) => {
let opaque_ty = tcx.hir().item(item_id);
match opaque_ty.kind {
hir::ItemKind::OpaqueTy(&hir::OpaqueTy { .. }) => {
hir::ItemKind::OpaqueTy(&hir::OpaqueTy { origin, .. }) => {
let local_def_id = item_id.owner_id.def_id;
// If this is an RPITIT and we are using the new RPITIT lowering scheme, we
// generate the def_id of an associated type for the trait and return as
// type a projection.
let def_id = if in_trait {
tcx.associated_type_for_impl_trait_in_trait(local_def_id).to_def_id()
} else {
local_def_id.to_def_id()
};
self.lower_opaque_ty(def_id, lifetimes, in_trait)
match origin {
hir::OpaqueTyOrigin::FnReturn {
in_trait_or_impl: Some(hir::RpitContext::Trait),
..
}
| hir::OpaqueTyOrigin::AsyncFn {
in_trait_or_impl: Some(hir::RpitContext::Trait),
..
} => self.lower_opaque_ty(
tcx.associated_type_for_impl_trait_in_trait(local_def_id)
.to_def_id(),
lifetimes,
true,
),
hir::OpaqueTyOrigin::FnReturn {
in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl),
..
}
| hir::OpaqueTyOrigin::AsyncFn {
in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl),
..
}
| hir::OpaqueTyOrigin::TyAlias { .. } => {
self.lower_opaque_ty(local_def_id.to_def_id(), lifetimes, false)
}
}
}
ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i),
}

View file

@ -602,7 +602,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.map(|(k, _)| (k.def_id, k.args))?,
_ => return None,
};
let hir::OpaqueTyOrigin::FnReturn(parent_def_id) = self.tcx.opaque_type_origin(def_id)
let hir::OpaqueTyOrigin::FnReturn { parent: parent_def_id, .. } =
self.tcx.opaque_type_origin(def_id)
else {
return None;
};

View file

@ -18,8 +18,8 @@ use rustc_middle::ty::{
self, GenericArgs, GenericArgsRef, GenericParamDefKind, Ty, TypeVisitableExt,
};
use rustc_middle::{bug, span_bug};
use rustc_span::Span;
use rustc_span::symbol::Ident;
use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use rustc_trait_selection::traits::{self, NormalizeExt};
use tracing::{debug, instrument};
@ -46,17 +46,17 @@ pub(crate) struct MethodCallee<'tcx> {
#[derive(Debug)]
pub(crate) enum MethodError<'tcx> {
// Did not find an applicable method, but we did find various near-misses that may work.
/// Did not find an applicable method, but we did find various near-misses that may work.
NoMatch(NoMatchData<'tcx>),
// Multiple methods might apply.
/// Multiple methods might apply.
Ambiguity(Vec<CandidateSource>),
// Found an applicable method, but it is not visible. The third argument contains a list of
// not-in-scope traits which may work.
/// Found an applicable method, but it is not visible. The third argument contains a list of
/// not-in-scope traits which may work.
PrivateMatch(DefKind, DefId, Vec<DefId>),
// Found a `Self: Sized` bound where `Self` is a trait object.
/// Found a `Self: Sized` bound where `Self` is a trait object.
IllegalSizedBound {
candidates: Vec<DefId>,
needs_mut: bool,
@ -64,8 +64,11 @@ pub(crate) enum MethodError<'tcx> {
self_expr: &'tcx hir::Expr<'tcx>,
},
// Found a match, but the return type is wrong
/// Found a match, but the return type is wrong
BadReturnType,
/// Error has already been emitted, no need to emit another one.
ErrorReported(ErrorGuaranteed),
}
// Contains a list of static methods that may apply, a list of unsatisfied trait predicates which
@ -120,6 +123,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Err(PrivateMatch(..)) => false,
Err(IllegalSizedBound { .. }) => true,
Err(BadReturnType) => false,
Err(ErrorReported(_)) => false,
}
}

View file

@ -446,13 +446,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => bug!("unexpected bad final type in method autoderef"),
};
self.demand_eqtype(span, ty, Ty::new_error(self.tcx, guar));
return Err(MethodError::NoMatch(NoMatchData {
static_candidates: Vec::new(),
unsatisfied_predicates: Vec::new(),
out_of_scope_traits: Vec::new(),
similar_candidate: None,
mode,
}));
return Err(MethodError::ErrorReported(guar));
}
}

View file

@ -229,20 +229,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
match error {
MethodError::NoMatch(mut no_match_data) => {
return self.report_no_match_method_error(
span,
rcvr_ty,
item_name,
call_id,
source,
args,
sugg_span,
&mut no_match_data,
expected,
trait_missing_method,
);
}
MethodError::NoMatch(mut no_match_data) => self.report_no_match_method_error(
span,
rcvr_ty,
item_name,
call_id,
source,
args,
sugg_span,
&mut no_match_data,
expected,
trait_missing_method,
),
MethodError::Ambiguity(mut sources) => {
let mut err = struct_span_code_err!(
@ -263,7 +261,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&mut sources,
Some(sugg_span),
);
return err.emit();
err.emit()
}
MethodError::PrivateMatch(kind, def_id, out_of_scope_traits) => {
@ -284,7 +282,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.unwrap_or_else(|| self.tcx.def_span(def_id));
err.span_label(sp, format!("private {kind} defined here"));
self.suggest_valid_traits(&mut err, item_name, out_of_scope_traits, true);
return err.emit();
err.emit()
}
MethodError::IllegalSizedBound { candidates, needs_mut, bound_span, self_expr } => {
@ -383,9 +381,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
}
return err.emit();
err.emit()
}
MethodError::ErrorReported(guar) => guar,
MethodError::BadReturnType => bug!("no return type expectations but got BadReturnType"),
}
}

View file

@ -1,4 +1,4 @@
//! There are four type combiners: [TypeRelating], [Lub], and [Glb],
//! There are four type combiners: [TypeRelating], `Lub`, and `Glb`,
//! and `NllTypeRelating` in rustc_borrowck, which is only used for NLL.
//!
//! Each implements the trait [TypeRelation] and contains methods for
@ -26,8 +26,7 @@ use rustc_middle::ty::{self, InferConst, IntType, Ty, TyCtxt, TypeVisitableExt,
pub use rustc_next_trait_solver::relate::combine::*;
use tracing::debug;
use super::glb::Glb;
use super::lub::Lub;
use super::lattice::{LatticeOp, LatticeOpKind};
use super::type_relating::TypeRelating;
use super::{RelateResult, StructurallyRelateAliases};
use crate::infer::{DefineOpaqueTypes, InferCtxt, TypeTrace, relate};
@ -303,12 +302,12 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
TypeRelating::new(self, StructurallyRelateAliases::No, ty::Contravariant)
}
pub fn lub<'a>(&'a mut self) -> Lub<'a, 'infcx, 'tcx> {
Lub::new(self)
pub(crate) fn lub<'a>(&'a mut self) -> LatticeOp<'a, 'infcx, 'tcx> {
LatticeOp::new(self, LatticeOpKind::Lub)
}
pub fn glb<'a>(&'a mut self) -> Glb<'a, 'infcx, 'tcx> {
Glb::new(self)
pub(crate) fn glb<'a>(&'a mut self) -> LatticeOp<'a, 'infcx, 'tcx> {
LatticeOp::new(self, LatticeOpKind::Glb)
}
pub fn register_obligations(

View file

@ -1,159 +0,0 @@
//! Greatest lower bound. See [`lattice`].
use rustc_middle::traits::solve::Goal;
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::Span;
use tracing::{debug, instrument};
use super::StructurallyRelateAliases;
use super::combine::{CombineFields, PredicateEmittingRelation};
use super::lattice::{self, LatticeDir};
use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin};
use crate::traits::ObligationCause;
/// "Greatest lower bound" (common subtype)
pub struct Glb<'combine, 'infcx, 'tcx> {
fields: &'combine mut CombineFields<'infcx, 'tcx>,
}
impl<'combine, 'infcx, 'tcx> Glb<'combine, 'infcx, 'tcx> {
pub fn new(fields: &'combine mut CombineFields<'infcx, 'tcx>) -> Glb<'combine, 'infcx, 'tcx> {
Glb { fields }
}
}
impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Glb<'_, '_, 'tcx> {
fn cx(&self) -> TyCtxt<'tcx> {
self.fields.tcx()
}
fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
&mut self,
variance: ty::Variance,
_info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
match variance {
ty::Invariant => self.fields.equate(StructurallyRelateAliases::No).relate(a, b),
ty::Covariant => self.relate(a, b),
// FIXME(#41044) -- not correct, need test
ty::Bivariant => Ok(a),
ty::Contravariant => self.fields.lub().relate(a, b),
}
}
#[instrument(skip(self), level = "trace")]
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
lattice::super_lattice_tys(self, a, b)
}
#[instrument(skip(self), level = "trace")]
fn regions(
&mut self,
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone()));
// GLB(&'static u8, &'a u8) == &RegionLUB('static, 'a) u8 == &'static u8
Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().lub_regions(
self.cx(),
origin,
a,
b,
))
}
#[instrument(skip(self), level = "trace")]
fn consts(
&mut self,
a: ty::Const<'tcx>,
b: ty::Const<'tcx>,
) -> RelateResult<'tcx, ty::Const<'tcx>> {
self.fields.infcx.super_combine_consts(self, a, b)
}
fn binders<T>(
&mut self,
a: ty::Binder<'tcx, T>,
b: ty::Binder<'tcx, T>,
) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
where
T: Relate<TyCtxt<'tcx>>,
{
// GLB of a binder and itself is just itself
if a == b {
return Ok(a);
}
debug!("binders(a={:?}, b={:?})", a, b);
if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() {
// When higher-ranked types are involved, computing the GLB is
// very challenging, switch to invariance. This is obviously
// overly conservative but works ok in practice.
self.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b)?;
Ok(a)
} else {
Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?))
}
}
}
impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx, 'tcx> {
fn infcx(&self) -> &'infcx InferCtxt<'tcx> {
self.fields.infcx
}
fn cause(&self) -> &ObligationCause<'tcx> {
&self.fields.trace.cause
}
fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> {
let mut sub = self.fields.sub();
sub.relate(v, a)?;
sub.relate(v, b)?;
Ok(())
}
fn define_opaque_types(&self) -> DefineOpaqueTypes {
self.fields.define_opaque_types
}
}
impl<'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> for Glb<'_, '_, 'tcx> {
fn span(&self) -> Span {
self.fields.trace.span()
}
fn structurally_relate_aliases(&self) -> StructurallyRelateAliases {
StructurallyRelateAliases::No
}
fn param_env(&self) -> ty::ParamEnv<'tcx> {
self.fields.param_env
}
fn register_predicates(
&mut self,
obligations: impl IntoIterator<Item: ty::Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>>>,
) {
self.fields.register_predicates(obligations);
}
fn register_goals(
&mut self,
obligations: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
) {
self.fields.register_obligations(obligations);
}
fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasRelate(
a.into(),
b.into(),
// FIXME(deferred_projection_equality): This isn't right, I think?
ty::AliasRelationDirection::Equate,
))]);
}
}

View file

@ -17,99 +17,247 @@
//!
//! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order)
use rustc_middle::ty::relate::RelateResult;
use rustc_middle::ty::{self, Ty, TyVar};
use tracing::instrument;
use rustc_middle::traits::solve::Goal;
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
use rustc_middle::ty::{self, Ty, TyCtxt, TyVar, TypeVisitableExt};
use rustc_span::Span;
use tracing::{debug, instrument};
use super::combine::PredicateEmittingRelation;
use crate::infer::{DefineOpaqueTypes, InferCtxt};
use crate::traits::ObligationCause;
use super::StructurallyRelateAliases;
use super::combine::{CombineFields, PredicateEmittingRelation};
use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin};
/// Trait for returning data about a lattice, and for abstracting
/// over the "direction" of the lattice operation (LUB/GLB).
///
/// GLB moves "down" the lattice (to smaller values); LUB moves
/// "up" the lattice (to bigger values).
pub(crate) trait LatticeDir<'f, 'tcx>: PredicateEmittingRelation<InferCtxt<'tcx>> {
fn infcx(&self) -> &'f InferCtxt<'tcx>;
#[derive(Clone, Copy)]
pub(crate) enum LatticeOpKind {
Glb,
Lub,
}
fn cause(&self) -> &ObligationCause<'tcx>;
impl LatticeOpKind {
fn invert(self) -> Self {
match self {
LatticeOpKind::Glb => LatticeOpKind::Lub,
LatticeOpKind::Lub => LatticeOpKind::Glb,
}
}
}
fn define_opaque_types(&self) -> DefineOpaqueTypes;
/// A greatest lower bound" (common subtype) or least upper bound (common supertype).
pub(crate) struct LatticeOp<'combine, 'infcx, 'tcx> {
fields: &'combine mut CombineFields<'infcx, 'tcx>,
kind: LatticeOpKind,
}
impl<'combine, 'infcx, 'tcx> LatticeOp<'combine, 'infcx, 'tcx> {
pub(crate) fn new(
fields: &'combine mut CombineFields<'infcx, 'tcx>,
kind: LatticeOpKind,
) -> LatticeOp<'combine, 'infcx, 'tcx> {
LatticeOp { fields, kind }
}
}
impl<'tcx> TypeRelation<TyCtxt<'tcx>> for LatticeOp<'_, '_, 'tcx> {
fn cx(&self) -> TyCtxt<'tcx> {
self.fields.tcx()
}
fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
&mut self,
variance: ty::Variance,
_info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
match variance {
ty::Invariant => self.fields.equate(StructurallyRelateAliases::No).relate(a, b),
ty::Covariant => self.relate(a, b),
// FIXME(#41044) -- not correct, need test
ty::Bivariant => Ok(a),
ty::Contravariant => {
self.kind = self.kind.invert();
let res = self.relate(a, b);
self.kind = self.kind.invert();
res
}
}
}
/// Relates two types using a given lattice.
#[instrument(skip(self), level = "trace")]
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
if a == b {
return Ok(a);
}
let infcx = self.fields.infcx;
let a = infcx.shallow_resolve(a);
let b = infcx.shallow_resolve(b);
match (a.kind(), b.kind()) {
// If one side is known to be a variable and one is not,
// create a variable (`v`) to represent the LUB. Make sure to
// relate `v` to the non-type-variable first (by passing it
// first to `relate_bound`). Otherwise, we would produce a
// subtype obligation that must then be processed.
//
// Example: if the LHS is a type variable, and RHS is
// `Box<i32>`, then we current compare `v` to the RHS first,
// which will instantiate `v` with `Box<i32>`. Then when `v`
// is compared to the LHS, we instantiate LHS with `Box<i32>`.
// But if we did in reverse order, we would create a `v <:
// LHS` (or vice versa) constraint and then instantiate
// `v`. This would require further processing to achieve same
// end-result; in particular, this screws up some of the logic
// in coercion, which expects LUB to figure out that the LHS
// is (e.g.) `Box<i32>`. A more obvious solution might be to
// iterate on the subtype obligations that are returned, but I
// think this suffices. -nmatsakis
(&ty::Infer(TyVar(..)), _) => {
let v = infcx.next_ty_var(self.fields.trace.cause.span);
self.relate_bound(v, b, a)?;
Ok(v)
}
(_, &ty::Infer(TyVar(..))) => {
let v = infcx.next_ty_var(self.fields.trace.cause.span);
self.relate_bound(v, a, b)?;
Ok(v)
}
(
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
) if a_def_id == b_def_id => infcx.super_combine_tys(self, a, b),
(&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
| (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
if self.fields.define_opaque_types == DefineOpaqueTypes::Yes
&& def_id.is_local()
&& !infcx.next_trait_solver() =>
{
self.register_goals(infcx.handle_opaque_type(
a,
b,
self.span(),
self.param_env(),
)?);
Ok(a)
}
_ => infcx.super_combine_tys(self, a, b),
}
}
#[instrument(skip(self), level = "trace")]
fn regions(
&mut self,
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone()));
let mut inner = self.fields.infcx.inner.borrow_mut();
let mut constraints = inner.unwrap_region_constraints();
Ok(match self.kind {
// GLB(&'static u8, &'a u8) == &RegionLUB('static, 'a) u8 == &'static u8
LatticeOpKind::Glb => constraints.lub_regions(self.cx(), origin, a, b),
// LUB(&'static u8, &'a u8) == &RegionGLB('static, 'a) u8 == &'a u8
LatticeOpKind::Lub => constraints.glb_regions(self.cx(), origin, a, b),
})
}
#[instrument(skip(self), level = "trace")]
fn consts(
&mut self,
a: ty::Const<'tcx>,
b: ty::Const<'tcx>,
) -> RelateResult<'tcx, ty::Const<'tcx>> {
self.fields.infcx.super_combine_consts(self, a, b)
}
fn binders<T>(
&mut self,
a: ty::Binder<'tcx, T>,
b: ty::Binder<'tcx, T>,
) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
where
T: Relate<TyCtxt<'tcx>>,
{
// GLB/LUB of a binder and itself is just itself
if a == b {
return Ok(a);
}
debug!("binders(a={:?}, b={:?})", a, b);
if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() {
// When higher-ranked types are involved, computing the GLB/LUB is
// very challenging, switch to invariance. This is obviously
// overly conservative but works ok in practice.
self.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b)?;
Ok(a)
} else {
Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?))
}
}
}
impl<'combine, 'infcx, 'tcx> LatticeOp<'combine, 'infcx, 'tcx> {
// Relates the type `v` to `a` and `b` such that `v` represents
// the LUB/GLB of `a` and `b` as appropriate.
//
// Subtle hack: ordering *may* be significant here. This method
// relates `v` to `a` first, which may help us to avoid unnecessary
// type variable obligations. See caller for details.
fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()>;
}
/// Relates two types using a given lattice.
#[instrument(skip(this), level = "debug")]
pub fn super_lattice_tys<'a, 'tcx: 'a, L>(
this: &mut L,
a: Ty<'tcx>,
b: Ty<'tcx>,
) -> RelateResult<'tcx, Ty<'tcx>>
where
L: LatticeDir<'a, 'tcx>,
{
if a == b {
return Ok(a);
}
let infcx = this.infcx();
let a = infcx.shallow_resolve(a);
let b = infcx.shallow_resolve(b);
match (a.kind(), b.kind()) {
// If one side is known to be a variable and one is not,
// create a variable (`v`) to represent the LUB. Make sure to
// relate `v` to the non-type-variable first (by passing it
// first to `relate_bound`). Otherwise, we would produce a
// subtype obligation that must then be processed.
//
// Example: if the LHS is a type variable, and RHS is
// `Box<i32>`, then we current compare `v` to the RHS first,
// which will instantiate `v` with `Box<i32>`. Then when `v`
// is compared to the LHS, we instantiate LHS with `Box<i32>`.
// But if we did in reverse order, we would create a `v <:
// LHS` (or vice versa) constraint and then instantiate
// `v`. This would require further processing to achieve same
// end-result; in particular, this screws up some of the logic
// in coercion, which expects LUB to figure out that the LHS
// is (e.g.) `Box<i32>`. A more obvious solution might be to
// iterate on the subtype obligations that are returned, but I
// think this suffices. -nmatsakis
(&ty::Infer(TyVar(..)), _) => {
let v = infcx.next_ty_var(this.cause().span);
this.relate_bound(v, b, a)?;
Ok(v)
fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> {
let mut sub = self.fields.sub();
match self.kind {
LatticeOpKind::Glb => {
sub.relate(v, a)?;
sub.relate(v, b)?;
}
LatticeOpKind::Lub => {
sub.relate(a, v)?;
sub.relate(b, v)?;
}
}
(_, &ty::Infer(TyVar(..))) => {
let v = infcx.next_ty_var(this.cause().span);
this.relate_bound(v, a, b)?;
Ok(v)
}
(
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
) if a_def_id == b_def_id => infcx.super_combine_tys(this, a, b),
(&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
| (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
if this.define_opaque_types() == DefineOpaqueTypes::Yes
&& def_id.is_local()
&& !this.infcx().next_trait_solver() =>
{
this.register_goals(infcx.handle_opaque_type(a, b, this.span(), this.param_env())?);
Ok(a)
}
_ => infcx.super_combine_tys(this, a, b),
Ok(())
}
}
impl<'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> for LatticeOp<'_, '_, 'tcx> {
fn span(&self) -> Span {
self.fields.trace.span()
}
fn structurally_relate_aliases(&self) -> StructurallyRelateAliases {
StructurallyRelateAliases::No
}
fn param_env(&self) -> ty::ParamEnv<'tcx> {
self.fields.param_env
}
fn register_predicates(
&mut self,
obligations: impl IntoIterator<Item: ty::Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>>>,
) {
self.fields.register_predicates(obligations);
}
fn register_goals(
&mut self,
obligations: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
) {
self.fields.register_obligations(obligations);
}
fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasRelate(
a.into(),
b.into(),
// FIXME(deferred_projection_equality): This isn't right, I think?
ty::AliasRelationDirection::Equate,
))]);
}
}

View file

@ -1,158 +0,0 @@
//! Least upper bound. See [`lattice`].
use rustc_middle::traits::solve::Goal;
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::Span;
use tracing::{debug, instrument};
use super::StructurallyRelateAliases;
use super::combine::{CombineFields, PredicateEmittingRelation};
use super::lattice::{self, LatticeDir};
use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin};
use crate::traits::ObligationCause;
/// "Least upper bound" (common supertype)
pub struct Lub<'combine, 'infcx, 'tcx> {
fields: &'combine mut CombineFields<'infcx, 'tcx>,
}
impl<'combine, 'infcx, 'tcx> Lub<'combine, 'infcx, 'tcx> {
pub fn new(fields: &'combine mut CombineFields<'infcx, 'tcx>) -> Lub<'combine, 'infcx, 'tcx> {
Lub { fields }
}
}
impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Lub<'_, '_, 'tcx> {
fn cx(&self) -> TyCtxt<'tcx> {
self.fields.tcx()
}
fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
&mut self,
variance: ty::Variance,
_info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
match variance {
ty::Invariant => self.fields.equate(StructurallyRelateAliases::No).relate(a, b),
ty::Covariant => self.relate(a, b),
// FIXME(#41044) -- not correct, need test
ty::Bivariant => Ok(a),
ty::Contravariant => self.fields.glb().relate(a, b),
}
}
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
lattice::super_lattice_tys(self, a, b)
}
#[instrument(skip(self), level = "trace")]
fn regions(
&mut self,
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone()));
// LUB(&'static u8, &'a u8) == &RegionGLB('static, 'a) u8 == &'a u8
Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().glb_regions(
self.cx(),
origin,
a,
b,
))
}
#[instrument(skip(self), level = "trace")]
fn consts(
&mut self,
a: ty::Const<'tcx>,
b: ty::Const<'tcx>,
) -> RelateResult<'tcx, ty::Const<'tcx>> {
self.fields.infcx.super_combine_consts(self, a, b)
}
fn binders<T>(
&mut self,
a: ty::Binder<'tcx, T>,
b: ty::Binder<'tcx, T>,
) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
where
T: Relate<TyCtxt<'tcx>>,
{
// LUB of a binder and itself is just itself
if a == b {
return Ok(a);
}
debug!("binders(a={:?}, b={:?})", a, b);
if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() {
// When higher-ranked types are involved, computing the LUB is
// very challenging, switch to invariance. This is obviously
// overly conservative but works ok in practice.
self.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b)?;
Ok(a)
} else {
Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?))
}
}
}
impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, 'tcx> {
fn infcx(&self) -> &'infcx InferCtxt<'tcx> {
self.fields.infcx
}
fn cause(&self) -> &ObligationCause<'tcx> {
&self.fields.trace.cause
}
fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> {
let mut sub = self.fields.sub();
sub.relate(a, v)?;
sub.relate(b, v)?;
Ok(())
}
fn define_opaque_types(&self) -> DefineOpaqueTypes {
self.fields.define_opaque_types
}
}
impl<'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> for Lub<'_, '_, 'tcx> {
fn span(&self) -> Span {
self.fields.trace.span()
}
fn structurally_relate_aliases(&self) -> StructurallyRelateAliases {
StructurallyRelateAliases::No
}
fn param_env(&self) -> ty::ParamEnv<'tcx> {
self.fields.param_env
}
fn register_predicates(
&mut self,
obligations: impl IntoIterator<Item: ty::Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>>>,
) {
self.fields.register_predicates(obligations);
}
fn register_goals(
&mut self,
obligations: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
) {
self.fields.register_obligations(obligations)
}
fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasRelate(
a.into(),
b.into(),
// FIXME(deferred_projection_equality): This isn't right, I think?
ty::AliasRelationDirection::Equate,
))]);
}
}

View file

@ -10,8 +10,6 @@ pub use self::combine::{CombineFields, PredicateEmittingRelation};
#[allow(hidden_glob_reexports)]
pub(super) mod combine;
mod generalize;
mod glb;
mod higher_ranked;
mod lattice;
mod lub;
mod type_relating;

View file

@ -389,12 +389,13 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
let file_loader = config.file_loader.unwrap_or_else(|| Box::new(RealFileLoader));
let path_mapping = config.opts.file_path_mapping();
let hash_kind = config.opts.unstable_opts.src_hash_algorithm(&target);
let checksum_hash_kind = config.opts.unstable_opts.checksum_hash_algorithm();
util::run_in_thread_pool_with_globals(
&early_dcx,
config.opts.edition,
config.opts.unstable_opts.threads,
SourceMapInputs { file_loader, path_mapping, hash_kind },
SourceMapInputs { file_loader, path_mapping, hash_kind, checksum_hash_kind },
|current_gcx| {
// The previous `early_dcx` can't be reused here because it doesn't
// impl `Send`. Creating a new one is fine.

View file

@ -1,6 +1,7 @@
// tidy-alphabetical-start
#![feature(decl_macro)]
#![feature(file_buffered)]
#![feature(iter_intersperse)]
#![feature(let_chains)]
#![feature(try_blocks)]
#![warn(unreachable_pub)]

View file

@ -32,8 +32,8 @@ use rustc_session::cstore::Untracked;
use rustc_session::output::{collect_crate_types, filename_for_input, find_crate_name};
use rustc_session::search_paths::PathKind;
use rustc_session::{Limit, Session};
use rustc_span::FileName;
use rustc_span::symbol::{Symbol, sym};
use rustc_span::{FileName, SourceFileHash, SourceFileHashAlgorithm};
use rustc_target::spec::PanicStrategy;
use rustc_trait_selection::traits;
use tracing::{info, instrument};
@ -417,15 +417,23 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
let result: io::Result<()> = try {
// Build a list of files used to compile the output and
// write Makefile-compatible dependency rules
let mut files: Vec<String> = sess
let mut files: Vec<(String, u64, Option<SourceFileHash>)> = sess
.source_map()
.files()
.iter()
.filter(|fmap| fmap.is_real_file())
.filter(|fmap| !fmap.is_imported())
.map(|fmap| escape_dep_filename(&fmap.name.prefer_local().to_string()))
.map(|fmap| {
(
escape_dep_filename(&fmap.name.prefer_local().to_string()),
fmap.source_len.0 as u64,
fmap.checksum_hash,
)
})
.collect();
let checksum_hash_algo = sess.opts.unstable_opts.checksum_hash_algorithm;
// Account for explicitly marked-to-track files
// (e.g. accessed in proc macros).
let file_depinfo = sess.psess.file_depinfo.borrow();
@ -437,22 +445,58 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
// The entries will be used to declare dependencies between files in a
// Makefile-like output, so the iteration order does not matter.
fn hash_iter_files<P: AsRef<Path>>(
it: impl Iterator<Item = P>,
checksum_hash_algo: Option<SourceFileHashAlgorithm>,
) -> impl Iterator<Item = (P, u64, Option<SourceFileHash>)> {
it.map(move |path| {
match checksum_hash_algo.and_then(|algo| {
fs::File::open(path.as_ref())
.and_then(|mut file| {
SourceFileHash::new(algo, &mut file).map(|h| (file, h))
})
.and_then(|(file, h)| file.metadata().map(|m| (m.len(), h)))
.map_err(|e| {
tracing::error!(
"failed to compute checksum, omitting it from dep-info {} {e}",
path.as_ref().display()
)
})
.ok()
}) {
Some((file_len, checksum)) => (path, file_len, Some(checksum)),
None => (path, 0, None),
}
})
}
#[allow(rustc::potential_query_instability)]
let extra_tracked_files =
file_depinfo.iter().map(|path_sym| normalize_path(PathBuf::from(path_sym.as_str())));
let extra_tracked_files = hash_iter_files(
file_depinfo.iter().map(|path_sym| normalize_path(PathBuf::from(path_sym.as_str()))),
checksum_hash_algo,
);
files.extend(extra_tracked_files);
// We also need to track used PGO profile files
if let Some(ref profile_instr) = sess.opts.cg.profile_use {
files.push(normalize_path(profile_instr.as_path().to_path_buf()));
files.extend(hash_iter_files(
iter::once(normalize_path(profile_instr.as_path().to_path_buf())),
checksum_hash_algo,
));
}
if let Some(ref profile_sample) = sess.opts.unstable_opts.profile_sample_use {
files.push(normalize_path(profile_sample.as_path().to_path_buf()));
files.extend(hash_iter_files(
iter::once(normalize_path(profile_sample.as_path().to_path_buf())),
checksum_hash_algo,
));
}
// Debugger visualizer files
for debugger_visualizer in tcx.debugger_visualizers(LOCAL_CRATE) {
files.push(normalize_path(debugger_visualizer.path.clone().unwrap()));
files.extend(hash_iter_files(
iter::once(normalize_path(debugger_visualizer.path.clone().unwrap())),
checksum_hash_algo,
));
}
if sess.binary_dep_depinfo() {
@ -460,33 +504,54 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
if backend.contains('.') {
// If the backend name contain a `.`, it is the path to an external dynamic
// library. If not, it is not a path.
files.push(backend.to_string());
files.extend(hash_iter_files(
iter::once(backend.to_string()),
checksum_hash_algo,
));
}
}
for &cnum in tcx.crates(()) {
let source = tcx.used_crate_source(cnum);
if let Some((path, _)) = &source.dylib {
files.push(escape_dep_filename(&path.display().to_string()));
files.extend(hash_iter_files(
iter::once(escape_dep_filename(&path.display().to_string())),
checksum_hash_algo,
));
}
if let Some((path, _)) = &source.rlib {
files.push(escape_dep_filename(&path.display().to_string()));
files.extend(hash_iter_files(
iter::once(escape_dep_filename(&path.display().to_string())),
checksum_hash_algo,
));
}
if let Some((path, _)) = &source.rmeta {
files.push(escape_dep_filename(&path.display().to_string()));
files.extend(hash_iter_files(
iter::once(escape_dep_filename(&path.display().to_string())),
checksum_hash_algo,
));
}
}
}
let write_deps_to_file = |file: &mut dyn Write| -> io::Result<()> {
for path in out_filenames {
writeln!(file, "{}: {}\n", path.display(), files.join(" "))?;
writeln!(
file,
"{}: {}\n",
path.display(),
files
.iter()
.map(|(path, _file_len, _checksum_hash_algo)| path.as_str())
.intersperse(" ")
.collect::<String>()
)?;
}
// Emit a fake target for each input file to the compilation. This
// prevents `make` from spitting out an error if a file is later
// deleted. For more info see #28735
for path in files {
for (path, _file_len, _checksum_hash_algo) in &files {
writeln!(file, "{path}:")?;
}
@ -510,6 +575,19 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
}
}
// If caller requested this information, add special comments about source file checksums.
// These are not necessarily the same checksums as was used in the debug files.
if sess.opts.unstable_opts.checksum_hash_algorithm().is_some() {
files
.iter()
.filter_map(|(path, file_len, hash_algo)| {
hash_algo.map(|hash_algo| (path, file_len, hash_algo))
})
.try_for_each(|(path, file_len, checksum_hash)| {
writeln!(file, "# checksum:{checksum_hash} file_len:{file_len} {path}")
})?;
}
Ok(())
};

View file

@ -44,10 +44,12 @@ where
let sysroot = filesearch::materialize_sysroot(sessopts.maybe_sysroot.clone());
let target = rustc_session::config::build_target_config(&early_dcx, &sessopts, &sysroot);
let hash_kind = sessopts.unstable_opts.src_hash_algorithm(&target);
let checksum_hash_kind = sessopts.unstable_opts.checksum_hash_algorithm();
let sm_inputs = Some(SourceMapInputs {
file_loader: Box::new(RealFileLoader) as _,
path_mapping: sessopts.file_path_mapping(),
hash_kind,
checksum_hash_kind,
});
rustc_span::create_session_globals_then(DEFAULT_EDITION, sm_inputs, || {

View file

@ -259,8 +259,8 @@ where
// If it's owned by this function
&& let opaque =
self.tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty()
&& let hir::OpaqueTyOrigin::FnReturn(parent_def_id) = opaque.origin
&& parent_def_id == self.parent_def_id
&& let hir::OpaqueTyOrigin::FnReturn { parent, .. } = opaque.origin
&& parent == self.parent_def_id
{
let opaque_span = self.tcx.def_span(opaque_def_id);
let new_capture_rules =

View file

@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
// That's because although we may have an opaque type on the function,
// it won't have a hidden type, so proving predicates about it is
// not really meaningful.
if let hir::OpaqueTyOrigin::FnReturn(method_def_id) = opaque.origin
if let hir::OpaqueTyOrigin::FnReturn { parent: method_def_id, .. } = opaque.origin
&& let hir::Node::TraitItem(trait_item) = cx.tcx.hir_node_by_def_id(method_def_id)
&& !trait_item.defaultness.has_value()
{
@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
&& cx.tcx.parent(opaque_ty.def_id) == def_id
&& matches!(
opaque.origin,
hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_)
hir::OpaqueTyOrigin::FnReturn { .. } | hir::OpaqueTyOrigin::AsyncFn { .. }
)
{
return;
@ -114,8 +114,10 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
// return type is well-formed in traits even when `Self` isn't sized.
if let ty::Param(param_ty) = *proj_term.kind()
&& param_ty.name == kw::SelfUpper
&& matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn(_))
&& opaque.in_trait
&& matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn {
in_trait_or_impl: Some(hir::RpitContext::Trait),
..
})
{
return;
}

View file

@ -1,6 +1,5 @@
// tidy-alphabetical-start
#![allow(internal_features)]
#![allow(rustc::potential_query_instability)]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(control_flow_enum)]

View file

@ -218,7 +218,7 @@ use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::{cmp, fmt};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_data_structures::memmap::Mmap;
use rustc_data_structures::owned_slice::slice_owned;
use rustc_data_structures::svh::Svh;
@ -385,7 +385,7 @@ impl<'a> CrateLocator<'a> {
let dylib_suffix = &self.target.dll_suffix;
let staticlib_suffix = &self.target.staticlib_suffix;
let mut candidates: FxHashMap<_, (FxHashMap<_, _>, FxHashMap<_, _>, FxHashMap<_, _>)> =
let mut candidates: FxIndexMap<_, (FxIndexMap<_, _>, FxIndexMap<_, _>, FxIndexMap<_, _>)> =
Default::default();
// First, find all possible candidate rlibs and dylibs purely based on
@ -460,7 +460,7 @@ impl<'a> CrateLocator<'a> {
// A Library candidate is created if the metadata for the set of
// libraries corresponds to the crate id and hash criteria that this
// search is being performed for.
let mut libraries = FxHashMap::default();
let mut libraries = FxIndexMap::default();
for (_hash, (rlibs, rmetas, dylibs)) in candidates {
if let Some((svh, lib)) = self.extract_lib(rlibs, rmetas, dylibs)? {
libraries.insert(svh, lib);
@ -494,9 +494,9 @@ impl<'a> CrateLocator<'a> {
fn extract_lib(
&mut self,
rlibs: FxHashMap<PathBuf, PathKind>,
rmetas: FxHashMap<PathBuf, PathKind>,
dylibs: FxHashMap<PathBuf, PathKind>,
rlibs: FxIndexMap<PathBuf, PathKind>,
rmetas: FxIndexMap<PathBuf, PathKind>,
dylibs: FxIndexMap<PathBuf, PathKind>,
) -> Result<Option<(Svh, Library)>, CrateError> {
let mut slot = None;
// Order here matters, rmeta should come first. See comment in
@ -534,7 +534,7 @@ impl<'a> CrateLocator<'a> {
// The `PathBuf` in `slot` will only be used for diagnostic purposes.
fn extract_one(
&mut self,
m: FxHashMap<PathBuf, PathKind>,
m: FxIndexMap<PathBuf, PathKind>,
flavor: CrateFlavor,
slot: &mut Option<(Svh, MetadataBlob, PathBuf)>,
) -> Result<Option<(PathBuf, PathKind)>, CrateError> {
@ -702,9 +702,9 @@ impl<'a> CrateLocator<'a> {
// First, filter out all libraries that look suspicious. We only accept
// files which actually exist that have the correct naming scheme for
// rlibs/dylibs.
let mut rlibs = FxHashMap::default();
let mut rmetas = FxHashMap::default();
let mut dylibs = FxHashMap::default();
let mut rlibs = FxIndexMap::default();
let mut rmetas = FxIndexMap::default();
let mut dylibs = FxIndexMap::default();
for loc in &self.exact_paths {
if !loc.canonicalized().exists() {
return Err(CrateError::ExternLocationNotExist(

View file

@ -1702,6 +1702,7 @@ impl<'a> CrateMetadataRef<'a> {
let rustc_span::SourceFile {
mut name,
src_hash,
checksum_hash,
start_pos: original_start_pos,
source_len,
lines,
@ -1752,6 +1753,7 @@ impl<'a> CrateMetadataRef<'a> {
let local_version = sess.source_map().new_imported_source_file(
name,
src_hash,
checksum_hash,
stable_id,
source_len.to_u32(),
self.cnum,

View file

@ -1186,9 +1186,9 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) ->
DefKind::OpaqueTy => {
let origin = tcx.opaque_type_origin(def_id);
if let hir::OpaqueTyOrigin::FnReturn(fn_def_id)
| hir::OpaqueTyOrigin::AsyncFn(fn_def_id) = origin
&& let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(fn_def_id)
if let hir::OpaqueTyOrigin::FnReturn { parent, .. }
| hir::OpaqueTyOrigin::AsyncFn { parent, .. } = origin
&& let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(parent)
&& let (_, hir::TraitFn::Required(..)) = trait_item.expect_fn()
{
false

View file

@ -1139,13 +1139,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String {
ItemKind::ForeignMod { .. } => "foreign mod",
ItemKind::GlobalAsm(..) => "global asm",
ItemKind::TyAlias(..) => "ty",
ItemKind::OpaqueTy(opaque) => {
if opaque.in_trait {
"opaque type in trait"
} else {
"opaque type"
}
}
ItemKind::OpaqueTy(..) => "opaque type",
ItemKind::Enum(..) => "enum",
ItemKind::Struct(..) => "struct",
ItemKind::Union(..) => "union",

View file

@ -510,7 +510,7 @@ impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
) => {
self.0.push(ty);
}
hir::TyKind::OpaqueDef(item_id, _, _) => {
hir::TyKind::OpaqueDef(item_id, _) => {
self.0.push(ty);
let item = self.1.item(item_id);
hir::intravisit::walk_item(self, item);

View file

@ -187,8 +187,8 @@ pub struct ResolverGlobalCtxt {
/// Mapping from ident span to path span for paths that don't exist as written, but that
/// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`.
pub confused_type_with_std_module: FxIndexMap<Span, Span>,
pub doc_link_resolutions: FxHashMap<LocalDefId, DocLinkResMap>,
pub doc_link_traits_in_scope: FxHashMap<LocalDefId, Vec<DefId>>,
pub doc_link_resolutions: FxIndexMap<LocalDefId, DocLinkResMap>,
pub doc_link_traits_in_scope: FxIndexMap<LocalDefId, Vec<DefId>>,
pub all_macro_rules: FxHashMap<Symbol, Res<ast::NodeId>>,
pub stripped_cfg_items: Steal<Vec<StrippedCfgItem>>,
}

View file

@ -26,6 +26,13 @@ parse_async_move_block_in_2015 = `async move` blocks are only allowed in Rust 20
parse_async_move_order_incorrect = the order of `move` and `async` is incorrect
.suggestion = try switching the order
parse_at_dot_dot_in_struct_pattern = `@ ..` is not supported in struct patterns
.suggestion = bind to each field separately or, if you don't need them, just remove `{$ident} @`
parse_at_in_struct_pattern = Unexpected `@` in struct pattern
.note = struct patterns use `field: pattern` syntax to bind to fields
.help = consider replacing `new_name @ field_name` with `field_name: new_name` if that is what you intended
parse_attr_after_generic = trailing attribute after generic parameter
.label = attributes must go before parameters

View file

@ -2571,6 +2571,25 @@ pub(crate) struct EnumPatternInsteadOfIdentifier {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_at_dot_dot_in_struct_pattern)]
pub(crate) struct AtDotDotInStructPattern {
#[primary_span]
pub span: Span,
#[suggestion(code = "", style = "verbose", applicability = "machine-applicable")]
pub remove: Span,
pub ident: Ident,
}
#[derive(Diagnostic)]
#[diag(parse_at_in_struct_pattern)]
#[note]
#[help]
pub(crate) struct AtInStructPattern {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_dot_dot_dot_for_remaining_fields)]
pub(crate) struct DotDotDotForRemainingFields {

View file

@ -17,15 +17,16 @@ use thin_vec::{ThinVec, thin_vec};
use super::{ForceCollect, Parser, PathStyle, Restrictions, Trailing, UsePreAttrPos};
use crate::errors::{
self, AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed,
DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt,
ExpectedCommaAfterPatternField, GenericArgsInPatRequireTurbofishSyntax,
InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern,
ParenRangeSuggestion, PatternOnWrongSideOfAt, RemoveLet, RepeatedMutInPattern,
SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg,
TrailingVertNotAllowed, UnexpectedExpressionInPattern, UnexpectedExpressionInPatternSugg,
UnexpectedLifetimeInPattern, UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg,
UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, WrapInParens,
self, AmbiguousRangePattern, AtDotDotInStructPattern, AtInStructPattern,
DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed, DotDotDotRestPattern,
EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt, ExpectedCommaAfterPatternField,
GenericArgsInPatRequireTurbofishSyntax, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow,
InclusiveRangeNoEnd, InvalidMutInPattern, ParenRangeSuggestion, PatternOnWrongSideOfAt,
RemoveLet, RepeatedMutInPattern, SwitchRefBoxOrder, TopLevelOrPatternNotAllowed,
TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed, UnexpectedExpressionInPattern,
UnexpectedExpressionInPatternSugg, UnexpectedLifetimeInPattern, UnexpectedParenInRangePat,
UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam,
UnexpectedVertVertInPattern, WrapInParens,
};
use crate::parser::expr::{DestructuredFloat, could_be_unclosed_char_literal};
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
@ -1433,7 +1434,7 @@ impl<'a> Parser<'a> {
/// Parses the fields of a struct-like pattern.
fn parse_pat_fields(&mut self) -> PResult<'a, (ThinVec<PatField>, PatFieldsRest)> {
let mut fields = ThinVec::new();
let mut fields: ThinVec<PatField> = ThinVec::new();
let mut etc = PatFieldsRest::None;
let mut ate_comma = true;
let mut delayed_err: Option<Diag<'a>> = None;
@ -1454,12 +1455,22 @@ impl<'a> Parser<'a> {
// check that a comma comes after every field
if !ate_comma {
let mut err =
self.dcx().create_err(ExpectedCommaAfterPatternField { span: self.token.span });
let err = if self.token == token::At {
let prev_field = fields
.last()
.expect("Unreachable on first iteration, not empty otherwise")
.ident;
self.report_misplaced_at_in_struct_pat(prev_field)
} else {
let mut err = self
.dcx()
.create_err(ExpectedCommaAfterPatternField { span: self.token.span });
self.recover_misplaced_pattern_modifiers(&fields, &mut err);
err
};
if let Some(delayed) = delayed_err {
delayed.emit();
}
self.recover_misplaced_pattern_modifiers(&fields, &mut err);
return Err(err);
}
ate_comma = false;
@ -1594,6 +1605,23 @@ impl<'a> Parser<'a> {
Ok((fields, etc))
}
#[deny(rustc::untranslatable_diagnostic)]
fn report_misplaced_at_in_struct_pat(&self, prev_field: Ident) -> Diag<'a> {
debug_assert_eq!(self.token, token::At);
let span = prev_field.span.to(self.token.span);
if let Some(dot_dot_span) =
self.look_ahead(1, |t| if t == &token::DotDot { Some(t.span) } else { None })
{
self.dcx().create_err(AtDotDotInStructPattern {
span: span.to(dot_dot_span),
remove: span.until(dot_dot_span),
ident: prev_field,
})
} else {
self.dcx().create_err(AtInStructPattern { span: span })
}
}
/// If the user writes `S { ref field: name }` instead of `S { field: ref name }`, we suggest
/// the correct code.
fn recover_misplaced_pattern_modifiers(&self, fields: &ThinVec<PatField>, err: &mut Diag<'a>) {

View file

@ -656,7 +656,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
}
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
if let TyKind::OpaqueDef(item_id, _, _) = ty.kind {
if let TyKind::OpaqueDef(item_id, _) = ty.kind {
let item = self.tcx.hir().item(item_id);
intravisit::walk_item(self, item);
}

View file

@ -637,17 +637,44 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
if self.impl_trait_pass
&& let hir::ItemKind::OpaqueTy(opaque) = item.kind
&& !opaque.in_trait
{
// FIXME: This is some serious pessimization intended to workaround deficiencies
// in the reachability pass (`middle/reachable.rs`). Types are marked as link-time
// reachable if they are returned via `impl Trait`, even from private functions.
let pub_ev = EffectiveVisibility::from_vis(ty::Visibility::Public);
self.reach_through_impl_trait(item.owner_id.def_id, pub_ev)
.generics()
.predicates()
.ty();
return;
let should_visit = match opaque.origin {
hir::OpaqueTyOrigin::FnReturn {
parent,
in_trait_or_impl: Some(hir::RpitContext::Trait),
}
| hir::OpaqueTyOrigin::AsyncFn {
parent,
in_trait_or_impl: Some(hir::RpitContext::Trait),
} => match self.tcx.hir_node_by_def_id(parent).expect_trait_item().expect_fn().1 {
hir::TraitFn::Required(_) => false,
hir::TraitFn::Provided(..) => true,
},
// Always visit RPITs in functions that have definitions,
// and all TAITs.
hir::OpaqueTyOrigin::FnReturn {
in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl),
..
}
| hir::OpaqueTyOrigin::AsyncFn {
in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl),
..
}
| hir::OpaqueTyOrigin::TyAlias { .. } => true,
};
if should_visit {
// FIXME: This is some serious pessimization intended to workaround deficiencies
// in the reachability pass (`middle/reachable.rs`). Types are marked as link-time
// reachable if they are returned via `impl Trait`, even from private functions.
let pub_ev = EffectiveVisibility::from_vis(ty::Visibility::Public);
self.reach_through_impl_trait(item.owner_id.def_id, pub_ev)
.generics()
.predicates()
.ty();
return;
}
}
// Update levels of nested things and mark all items

View file

@ -2,7 +2,7 @@
// tidy-alphabetical-start
#![allow(internal_features)]
#![allow(rustc::potential_query_instability, unused_parens)]
#![allow(unused_parens)]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(min_specialization)]

View file

@ -68,6 +68,8 @@ impl<'a> HashStable<StableHashingContext<'a>> for SourceFile {
// Do not hash the source as it is not encoded
src: _,
ref src_hash,
// Already includes src_hash, this is redundant
checksum_hash: _,
external_src: _,
start_pos: _,
source_len: _,

View file

@ -1196,8 +1196,8 @@ pub struct Resolver<'ra, 'tcx> {
stripped_cfg_items: Vec<StrippedCfgItem<NodeId>>,
effective_visibilities: EffectiveVisibilities,
doc_link_resolutions: FxHashMap<LocalDefId, DocLinkResMap>,
doc_link_traits_in_scope: FxHashMap<LocalDefId, Vec<DefId>>,
doc_link_resolutions: FxIndexMap<LocalDefId, DocLinkResMap>,
doc_link_traits_in_scope: FxIndexMap<LocalDefId, Vec<DefId>>,
all_macro_rules: FxHashMap<Symbol, Res>,
/// Invocation ids of all glob delegations.

View file

@ -1242,6 +1242,10 @@ impl UnstableOptions {
}
})
}
pub fn checksum_hash_algorithm(&self) -> Option<SourceFileHashAlgorithm> {
self.checksum_hash_algorithm
}
}
// The type of entry function, so users can have their own entry functions

View file

@ -418,7 +418,9 @@ mod desc {
"one of: `legacy`, `v0` (RFC 2603), or `hashed`";
pub(crate) const parse_opt_symbol_visibility: &str =
"one of: `hidden`, `protected`, or `interposable`";
pub(crate) const parse_src_file_hash: &str = "either `md5` or `sha1`";
pub(crate) const parse_cargo_src_file_hash: &str =
"one of `blake3`, `md5`, `sha1`, or `sha256`";
pub(crate) const parse_src_file_hash: &str = "one of `md5`, `sha1`, or `sha256`";
pub(crate) const parse_relocation_model: &str =
"one of supported relocation models (`rustc --print relocation-models`)";
pub(crate) const parse_code_model: &str =
@ -1288,6 +1290,19 @@ mod parse {
true
}
pub(crate) fn parse_cargo_src_file_hash(
slot: &mut Option<SourceFileHashAlgorithm>,
v: Option<&str>,
) -> bool {
match v.and_then(|s| SourceFileHashAlgorithm::from_str(s).ok()) {
Some(hash_kind) => {
*slot = Some(hash_kind);
}
_ => return false,
}
true
}
pub(crate) fn parse_target_feature(slot: &mut String, v: Option<&str>) -> bool {
match v {
Some(s) => {
@ -1688,6 +1703,8 @@ options! {
"instrument control-flow architecture protection"),
check_cfg_all_expected: bool = (false, parse_bool, [UNTRACKED],
"show all expected values in check-cfg diagnostics (default: no)"),
checksum_hash_algorithm: Option<SourceFileHashAlgorithm> = (None, parse_cargo_src_file_hash, [TRACKED],
"hash algorithm of source files used to check freshness in cargo (`blake3` or `sha256`)"),
codegen_backend: Option<String> = (None, parse_opt_string, [TRACKED],
"the backend to use"),
combine_cgu: bool = (false, parse_bool, [TRACKED],

View file

@ -5,6 +5,7 @@ edition = "2021"
[dependencies]
# tidy-alphabetical-start
blake3 = "1.5.2"
derive-where = "1.2.7"
indexmap = { version = "2.0.0" }
itoa = "1.0"

View file

@ -75,7 +75,9 @@ pub mod profiling;
use std::borrow::Cow;
use std::cmp::{self, Ordering};
use std::fmt::Display;
use std::hash::Hash;
use std::io::{self, Read};
use std::ops::{Add, Range, Sub};
use std::path::{Path, PathBuf};
use std::str::FromStr;
@ -1395,6 +1397,18 @@ pub enum SourceFileHashAlgorithm {
Md5,
Sha1,
Sha256,
Blake3,
}
impl Display for SourceFileHashAlgorithm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Md5 => "md5",
Self::Sha1 => "sha1",
Self::Sha256 => "sha256",
Self::Blake3 => "blake3",
})
}
}
impl FromStr for SourceFileHashAlgorithm {
@ -1405,12 +1419,13 @@ impl FromStr for SourceFileHashAlgorithm {
"md5" => Ok(SourceFileHashAlgorithm::Md5),
"sha1" => Ok(SourceFileHashAlgorithm::Sha1),
"sha256" => Ok(SourceFileHashAlgorithm::Sha256),
"blake3" => Ok(SourceFileHashAlgorithm::Blake3),
_ => Err(()),
}
}
}
/// The hash of the on-disk source file used for debug info.
/// The hash of the on-disk source file used for debug info and cargo freshness checks.
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
#[derive(HashStable_Generic, Encodable, Decodable)]
pub struct SourceFileHash {
@ -1418,12 +1433,22 @@ pub struct SourceFileHash {
value: [u8; 32],
}
impl Display for SourceFileHash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}=", self.kind)?;
for byte in self.value[0..self.hash_len()].into_iter() {
write!(f, "{byte:02x}")?;
}
Ok(())
}
}
impl SourceFileHash {
pub fn new(kind: SourceFileHashAlgorithm, src: &str) -> SourceFileHash {
pub fn new_in_memory(kind: SourceFileHashAlgorithm, src: impl AsRef<[u8]>) -> SourceFileHash {
let mut hash = SourceFileHash { kind, value: Default::default() };
let len = hash.hash_len();
let value = &mut hash.value[..len];
let data = src.as_bytes();
let data = src.as_ref();
match kind {
SourceFileHashAlgorithm::Md5 => {
value.copy_from_slice(&Md5::digest(data));
@ -1434,13 +1459,94 @@ impl SourceFileHash {
SourceFileHashAlgorithm::Sha256 => {
value.copy_from_slice(&Sha256::digest(data));
}
}
SourceFileHashAlgorithm::Blake3 => value.copy_from_slice(blake3::hash(data).as_bytes()),
};
hash
}
pub fn new(kind: SourceFileHashAlgorithm, src: impl Read) -> Result<SourceFileHash, io::Error> {
let mut hash = SourceFileHash { kind, value: Default::default() };
let len = hash.hash_len();
let value = &mut hash.value[..len];
// Buffer size is the recommended amount to fully leverage SIMD instructions on AVX-512 as per
// blake3 documentation.
let mut buf = vec![0; 16 * 1024];
fn digest<T>(
mut hasher: T,
mut update: impl FnMut(&mut T, &[u8]),
finish: impl FnOnce(T, &mut [u8]),
mut src: impl Read,
buf: &mut [u8],
value: &mut [u8],
) -> Result<(), io::Error> {
loop {
let bytes_read = src.read(buf)?;
if bytes_read == 0 {
break;
}
update(&mut hasher, &buf[0..bytes_read]);
}
finish(hasher, value);
Ok(())
}
match kind {
SourceFileHashAlgorithm::Sha256 => {
digest(
Sha256::new(),
|h, b| {
h.update(b);
},
|h, out| out.copy_from_slice(&h.finalize()),
src,
&mut buf,
value,
)?;
}
SourceFileHashAlgorithm::Sha1 => {
digest(
Sha1::new(),
|h, b| {
h.update(b);
},
|h, out| out.copy_from_slice(&h.finalize()),
src,
&mut buf,
value,
)?;
}
SourceFileHashAlgorithm::Md5 => {
digest(
Md5::new(),
|h, b| {
h.update(b);
},
|h, out| out.copy_from_slice(&h.finalize()),
src,
&mut buf,
value,
)?;
}
SourceFileHashAlgorithm::Blake3 => {
digest(
blake3::Hasher::new(),
|h, b| {
h.update(b);
},
|h, out| out.copy_from_slice(h.finalize().as_bytes()),
src,
&mut buf,
value,
)?;
}
}
Ok(hash)
}
/// Check if the stored hash matches the hash of the string.
pub fn matches(&self, src: &str) -> bool {
Self::new(self.kind, src) == *self
Self::new_in_memory(self.kind, src.as_bytes()) == *self
}
/// The bytes of the hash.
@ -1453,7 +1559,7 @@ impl SourceFileHash {
match self.kind {
SourceFileHashAlgorithm::Md5 => 16,
SourceFileHashAlgorithm::Sha1 => 20,
SourceFileHashAlgorithm::Sha256 => 32,
SourceFileHashAlgorithm::Sha256 | SourceFileHashAlgorithm::Blake3 => 32,
}
}
}
@ -1509,6 +1615,10 @@ pub struct SourceFile {
pub src: Option<Lrc<String>>,
/// The source code's hash.
pub src_hash: SourceFileHash,
/// Used to enable cargo to use checksums to check if a crate is fresh rather
/// than mtimes. This might be the same as `src_hash`, and if the requested algorithm
/// is identical we won't compute it twice.
pub checksum_hash: Option<SourceFileHash>,
/// The external source code (used for external crates, which will have a `None`
/// value as `self.src`.
pub external_src: FreezeLock<ExternalSource>,
@ -1536,6 +1646,7 @@ impl Clone for SourceFile {
name: self.name.clone(),
src: self.src.clone(),
src_hash: self.src_hash,
checksum_hash: self.checksum_hash,
external_src: self.external_src.clone(),
start_pos: self.start_pos,
source_len: self.source_len,
@ -1552,6 +1663,7 @@ impl<S: SpanEncoder> Encodable<S> for SourceFile {
fn encode(&self, s: &mut S) {
self.name.encode(s);
self.src_hash.encode(s);
self.checksum_hash.encode(s);
// Do not encode `start_pos` as it's global state for this session.
self.source_len.encode(s);
@ -1625,6 +1737,7 @@ impl<D: SpanDecoder> Decodable<D> for SourceFile {
fn decode(d: &mut D) -> SourceFile {
let name: FileName = Decodable::decode(d);
let src_hash: SourceFileHash = Decodable::decode(d);
let checksum_hash: Option<SourceFileHash> = Decodable::decode(d);
let source_len: RelativeBytePos = Decodable::decode(d);
let lines = {
let num_lines: u32 = Decodable::decode(d);
@ -1650,6 +1763,7 @@ impl<D: SpanDecoder> Decodable<D> for SourceFile {
source_len,
src: None,
src_hash,
checksum_hash,
// Unused - the metadata decoder will construct
// a new SourceFile, filling in `external_src` properly
external_src: FreezeLock::frozen(ExternalSource::Unneeded),
@ -1733,9 +1847,17 @@ impl SourceFile {
name: FileName,
mut src: String,
hash_kind: SourceFileHashAlgorithm,
checksum_hash_kind: Option<SourceFileHashAlgorithm>,
) -> Result<Self, OffsetOverflowError> {
// Compute the file hash before any normalization.
let src_hash = SourceFileHash::new(hash_kind, &src);
let src_hash = SourceFileHash::new_in_memory(hash_kind, src.as_bytes());
let checksum_hash = checksum_hash_kind.map(|checksum_hash_kind| {
if checksum_hash_kind == hash_kind {
src_hash
} else {
SourceFileHash::new_in_memory(checksum_hash_kind, src.as_bytes())
}
});
let normalized_pos = normalize_src(&mut src);
let stable_id = StableSourceFileId::from_filename_in_current_crate(&name);
@ -1748,6 +1870,7 @@ impl SourceFile {
name,
src: Some(Lrc::new(src)),
src_hash,
checksum_hash,
external_src: FreezeLock::frozen(ExternalSource::Unneeded),
start_pos: BytePos::from_u32(0),
source_len: RelativeBytePos::from_u32(source_len),

View file

@ -175,6 +175,7 @@ pub struct SourceMapInputs {
pub file_loader: Box<dyn FileLoader + Send + Sync>,
pub path_mapping: FilePathMapping,
pub hash_kind: SourceFileHashAlgorithm,
pub checksum_hash_kind: Option<SourceFileHashAlgorithm>,
}
pub struct SourceMap {
@ -187,6 +188,12 @@ pub struct SourceMap {
/// The algorithm used for hashing the contents of each source file.
hash_kind: SourceFileHashAlgorithm,
/// Similar to `hash_kind`, however this algorithm is used for checksums to determine if a crate is fresh.
/// `cargo` is the primary user of these.
///
/// If this is equal to `hash_kind` then the checksum won't be computed twice.
checksum_hash_kind: Option<SourceFileHashAlgorithm>,
}
impl SourceMap {
@ -195,17 +202,19 @@ impl SourceMap {
file_loader: Box::new(RealFileLoader),
path_mapping,
hash_kind: SourceFileHashAlgorithm::Md5,
checksum_hash_kind: None,
})
}
pub fn with_inputs(
SourceMapInputs { file_loader, path_mapping, hash_kind }: SourceMapInputs,
SourceMapInputs { file_loader, path_mapping, hash_kind, checksum_hash_kind }: SourceMapInputs,
) -> SourceMap {
SourceMap {
files: Default::default(),
file_loader: IntoDynSyncSend(file_loader),
path_mapping,
hash_kind,
checksum_hash_kind,
}
}
@ -307,7 +316,8 @@ impl SourceMap {
match self.source_file_by_stable_id(stable_id) {
Some(lrc_sf) => Ok(lrc_sf),
None => {
let source_file = SourceFile::new(filename, src, self.hash_kind)?;
let source_file =
SourceFile::new(filename, src, self.hash_kind, self.checksum_hash_kind)?;
// Let's make sure the file_id we generated above actually matches
// the ID we generate for the SourceFile we just created.
@ -326,6 +336,7 @@ impl SourceMap {
&self,
filename: FileName,
src_hash: SourceFileHash,
checksum_hash: Option<SourceFileHash>,
stable_id: StableSourceFileId,
source_len: u32,
cnum: CrateNum,
@ -340,6 +351,7 @@ impl SourceMap {
name: filename,
src: None,
src_hash,
checksum_hash,
external_src: FreezeLock::new(ExternalSource::Foreign {
kind: ExternalSourceKind::AbsentOk,
metadata_index,

View file

@ -229,6 +229,7 @@ fn t10() {
let SourceFile {
name,
src_hash,
checksum_hash,
source_len,
lines,
multibyte_chars,
@ -240,6 +241,7 @@ fn t10() {
let imported_src_file = sm.new_imported_source_file(
name,
src_hash,
checksum_hash,
stable_id,
source_len.to_u32(),
CrateNum::ZERO,

View file

@ -3,9 +3,13 @@ use super::*;
#[test]
fn test_lookup_line() {
let source = "abcdefghijklm\nabcdefghij\n...".to_owned();
let mut sf =
SourceFile::new(FileName::Anon(Hash64::ZERO), source, SourceFileHashAlgorithm::Sha256)
.unwrap();
let mut sf = SourceFile::new(
FileName::Anon(Hash64::ZERO),
source,
SourceFileHashAlgorithm::Sha256,
Some(SourceFileHashAlgorithm::Sha256),
)
.unwrap();
sf.start_pos = BytePos(3);
assert_eq!(sf.lines(), &[RelativeBytePos(0), RelativeBytePos(14), RelativeBytePos(25)]);

View file

@ -23,6 +23,7 @@ pub(crate) fn target() -> Target {
linker: Some("rust-lld".into()),
link_script: Some(LINKER_SCRIPT.into()),
os: "horizon".into(),
vendor: "nintendo".into(),
max_atomic_width: Some(128),
stack_probes: StackProbeType::Inline,
panic_strategy: PanicStrategy::Abort,

View file

@ -14,6 +14,7 @@ pub(crate) fn target() -> Target {
data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
arch: "arm".into(),
options: TargetOptions {
abi: "eabi".into(),
linker: Some("arm-kmc-eabi-gcc".into()),
features: "+v7,+soft-float,+thumb2,-neon".into(),
relocation_model: RelocModel::Static,

View file

@ -14,6 +14,7 @@ pub(crate) fn target() -> Target {
data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
arch: "arm".into(),
options: TargetOptions {
abi: "eabihf".into(),
linker: Some("arm-kmc-eabi-gcc".into()),
features: "+v7,+vfp3,-d32,+thumb2,-neon".into(),
relocation_model: RelocModel::Static,

View file

@ -284,7 +284,7 @@ pub fn suggest_new_region_bound(
}
match fn_return.kind {
// FIXME(precise_captures): Suggest adding to `use<...>` list instead.
TyKind::OpaqueDef(item_id, _, _) => {
TyKind::OpaqueDef(item_id, _) => {
let item = tcx.hir().item(item_id);
let ItemKind::OpaqueTy(opaque) = &item.kind else {
return;

View file

@ -857,7 +857,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
fn visit_ty(&mut self, ty: &'hir hir::Ty<'hir>) {
let hir::TyKind::OpaqueDef(item_id, _, _) = ty.kind else {
let hir::TyKind::OpaqueDef(item_id, _) = ty.kind else {
return hir::intravisit::walk_ty(self, ty);
};
let opaque_ty = self.tcx.hir().item(item_id).expect_opaque_ty();
@ -1271,7 +1271,7 @@ fn suggest_precise_capturing<'tcx>(
let hir::OpaqueTy { bounds, origin, .. } =
tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();
let hir::OpaqueTyOrigin::FnReturn(fn_def_id) = *origin else {
let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. } = *origin else {
return;
};

View file

@ -245,6 +245,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
span, "silent safe transmute error"
);
}
GetSafeTransmuteErrorAndReason::Default => {
(err_msg, None)
}
GetSafeTransmuteErrorAndReason::Error {
err_msg,
safe_transmute_explanation,
@ -2226,6 +2229,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
) -> GetSafeTransmuteErrorAndReason {
use rustc_transmute::Answer;
// We don't assemble a transmutability candidate for types that are generic
// and we should have ambiguity for types that still have non-region infer.
if obligation.predicate.has_non_region_param() || obligation.has_non_region_infer() {
return GetSafeTransmuteErrorAndReason::Default;
}
// Erase regions because layout code doesn't particularly care about regions.
let trait_ref =
self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref));
@ -2248,6 +2257,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
let dst = trait_ref.args.type_at(0);
let src = trait_ref.args.type_at(1);
let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
@ -2630,7 +2640,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
def_id: DefId,
) -> ErrorGuaranteed {
let name = match self.tcx.opaque_type_origin(def_id.expect_local()) {
hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => {
hir::OpaqueTyOrigin::FnReturn { .. } | hir::OpaqueTyOrigin::AsyncFn { .. } => {
"opaque type".to_string()
}
hir::OpaqueTyOrigin::TyAlias { .. } => {

View file

@ -43,6 +43,7 @@ pub struct ImplCandidate<'tcx> {
enum GetSafeTransmuteErrorAndReason {
Silent,
Default,
Error { err_msg: String, safe_transmute_explanation: Option<String> },
}

View file

@ -195,10 +195,11 @@ pub(crate) mod rustc {
impl<'tcx> From<&LayoutError<'tcx>> for Err {
fn from(err: &LayoutError<'tcx>) -> Self {
match err {
LayoutError::Unknown(..) | LayoutError::ReferencesError(..) => Self::UnknownLayout,
LayoutError::Unknown(..)
| LayoutError::ReferencesError(..)
| LayoutError::NormalizationFailure(..) => Self::UnknownLayout,
LayoutError::SizeOverflow(..) => Self::SizeOverflow,
LayoutError::Cycle(err) => Self::TypeError(*err),
err => unimplemented!("{:?}", err),
}
}
}

View file

@ -323,7 +323,7 @@ fn associated_types_for_impl_traits_in_associated_fn(
impl<'tcx> Visitor<'tcx> for RPITVisitor<'tcx> {
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
if let hir::TyKind::OpaqueDef(item_id, _, _) = ty.kind
if let hir::TyKind::OpaqueDef(item_id, _) = ty.kind
&& self.rpits.insert(item_id.owner_id.def_id)
{
let opaque_item =
@ -379,7 +379,8 @@ fn associated_type_for_impl_trait_in_trait(
tcx: TyCtxt<'_>,
opaque_ty_def_id: LocalDefId,
) -> LocalDefId {
let (hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id)) =
let (hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. }
| hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, .. }) =
tcx.opaque_type_origin(opaque_ty_def_id)
else {
bug!("expected opaque for {opaque_ty_def_id:?}");

View file

@ -141,7 +141,8 @@ impl<'tcx> OpaqueTypeCollector<'tcx> {
let origin = self.tcx.opaque_type_origin(alias_ty.def_id.expect_local());
trace!(?origin);
match origin {
rustc_hir::OpaqueTyOrigin::FnReturn(_) | rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {}
rustc_hir::OpaqueTyOrigin::FnReturn { .. }
| rustc_hir::OpaqueTyOrigin::AsyncFn { .. } => {}
rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty, .. } => {
if !in_assoc_ty && !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) {
return;

View file

@ -374,7 +374,10 @@ impl<'a, T: Ord, A: Allocator> PeekMut<'a, T, A> {
// the caller could've mutated the element. It is removed from the
// heap on the next line and pop() is not sensitive to its value.
}
this.heap.pop().unwrap()
// SAFETY: Have a `PeekMut` element proves that the associated binary heap being non-empty,
// so the `pop` operation will not fail.
unsafe { this.heap.pop().unwrap_unchecked() }
}
}

View file

@ -1438,6 +1438,14 @@ impl<K, V> Clone for Iter<'_, K, V> {
}
}
#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")]
impl<K, V> Default for Iter<'_, K, V> {
#[inline]
fn default() -> Self {
Iter { base: Default::default() }
}
}
#[stable(feature = "std_debug", since = "1.16.0")]
impl<K: Debug, V: Debug> fmt::Debug for Iter<'_, K, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -1476,6 +1484,14 @@ impl<'a, K, V> IterMut<'a, K, V> {
}
}
#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")]
impl<K, V> Default for IterMut<'_, K, V> {
#[inline]
fn default() -> Self {
IterMut { base: Default::default() }
}
}
/// An owning iterator over the entries of a `HashMap`.
///
/// This `struct` is created by the [`into_iter`] method on [`HashMap`]
@ -1506,6 +1522,14 @@ impl<K, V> IntoIter<K, V> {
}
}
#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")]
impl<K, V> Default for IntoIter<K, V> {
#[inline]
fn default() -> Self {
IntoIter { base: Default::default() }
}
}
/// An iterator over the keys of a `HashMap`.
///
/// This `struct` is created by the [`keys`] method on [`HashMap`]. See its
@ -1538,6 +1562,14 @@ impl<K, V> Clone for Keys<'_, K, V> {
}
}
#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")]
impl<K, V> Default for Keys<'_, K, V> {
#[inline]
fn default() -> Self {
Keys { inner: Default::default() }
}
}
#[stable(feature = "std_debug", since = "1.16.0")]
impl<K: Debug, V> fmt::Debug for Keys<'_, K, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -1577,6 +1609,14 @@ impl<K, V> Clone for Values<'_, K, V> {
}
}
#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")]
impl<K, V> Default for Values<'_, K, V> {
#[inline]
fn default() -> Self {
Values { inner: Default::default() }
}
}
#[stable(feature = "std_debug", since = "1.16.0")]
impl<K, V: Debug> fmt::Debug for Values<'_, K, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -1665,6 +1705,14 @@ pub struct ValuesMut<'a, K: 'a, V: 'a> {
inner: IterMut<'a, K, V>,
}
#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")]
impl<K, V> Default for ValuesMut<'_, K, V> {
#[inline]
fn default() -> Self {
ValuesMut { inner: Default::default() }
}
}
/// An owning iterator over the keys of a `HashMap`.
///
/// This `struct` is created by the [`into_keys`] method on [`HashMap`].
@ -1687,6 +1735,14 @@ pub struct IntoKeys<K, V> {
inner: IntoIter<K, V>,
}
#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")]
impl<K, V> Default for IntoKeys<K, V> {
#[inline]
fn default() -> Self {
IntoKeys { inner: Default::default() }
}
}
/// An owning iterator over the values of a `HashMap`.
///
/// This `struct` is created by the [`into_values`] method on [`HashMap`].
@ -1709,6 +1765,14 @@ pub struct IntoValues<K, V> {
inner: IntoIter<K, V>,
}
#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")]
impl<K, V> Default for IntoValues<K, V> {
#[inline]
fn default() -> Self {
IntoValues { inner: Default::default() }
}
}
/// A builder for computing where in a HashMap a key-value pair would be stored.
///
/// See the [`HashMap::raw_entry_mut`] docs for usage examples.

View file

@ -1244,6 +1244,14 @@ pub struct Iter<'a, K: 'a> {
base: base::Iter<'a, K>,
}
#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")]
impl<K> Default for Iter<'_, K> {
#[inline]
fn default() -> Self {
Iter { base: Default::default() }
}
}
/// An owning iterator over the items of a `HashSet`.
///
/// This `struct` is created by the [`into_iter`] method on [`HashSet`]
@ -1265,6 +1273,14 @@ pub struct IntoIter<K> {
base: base::IntoIter<K>,
}
#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")]
impl<K> Default for IntoIter<K> {
#[inline]
fn default() -> Self {
IntoIter { base: Default::default() }
}
}
/// A draining iterator over the items of a `HashSet`.
///
/// This `struct` is created by the [`drain`] method on [`HashSet`].

View file

@ -394,6 +394,7 @@ impl Stdin {
/// in which case it will wait for the Enter key to be pressed before
/// continuing
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_confusables("get_line")]
pub fn read_line(&self, buf: &mut String) -> io::Result<usize> {
self.lock().read_line(buf)
}

View file

@ -21,9 +21,10 @@ pub use crate::panicking::{begin_panic, panic_count};
pub use core::panicking::{panic_display, panic_fmt};
#[rustfmt::skip]
use crate::any::Any;
use crate::sync::Once;
use crate::sys;
use crate::thread::{self, Thread};
use crate::{mem, panic, sys};
// Prints to the "panic output", depending on the platform this may be:
// - the standard error output
@ -66,6 +67,11 @@ macro_rules! rtunwrap {
};
}
fn handle_rt_panic(e: Box<dyn Any + Send>) {
mem::forget(e);
rtabort!("initialization or cleanup bug");
}
// One-time runtime initialization.
// Runs before `main`.
// SAFETY: must be called only once during runtime initialization.
@ -101,6 +107,20 @@ unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
thread::set_current(thread);
}
/// Clean up the thread-local runtime state. This *should* be run after all other
/// code managed by the Rust runtime, but will not cause UB if that condition is
/// not fulfilled. Also note that this function is not guaranteed to be run, but
/// skipping it will cause leaks and therefore is to be avoided.
pub(crate) fn thread_cleanup() {
// This function is run in situations where unwinding leads to an abort
// (think `extern "C"` functions). Abort here instead so that we can
// print a nice message.
panic::catch_unwind(|| {
crate::thread::drop_current();
})
.unwrap_or_else(handle_rt_panic);
}
// One-time runtime cleanup.
// Runs after `main` or at program exit.
// NOTE: this is not guaranteed to run, for example when the program aborts.
@ -123,11 +143,6 @@ fn lang_start_internal(
argv: *const *const u8,
sigpipe: u8,
) -> Result<isize, !> {
use crate::{mem, panic};
let rt_abort = move |e| {
mem::forget(e);
rtabort!("initialization or cleanup bug");
};
// Guard against the code called by this function from unwinding outside of the Rust-controlled
// code, which is UB. This is a requirement imposed by a combination of how the
// `#[lang="start"]` attribute is implemented as well as by the implementation of the panicking
@ -139,16 +154,17 @@ fn lang_start_internal(
// prevent std from accidentally introducing a panic to these functions. Another is from
// user code from `main` or, more nefariously, as described in e.g. issue #86030.
// SAFETY: Only called once during runtime initialization.
panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }).map_err(rt_abort)?;
panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) })
.unwrap_or_else(handle_rt_panic);
let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize)
.map_err(move |e| {
mem::forget(e);
rtabort!("drop of the panic payload panicked");
});
panic::catch_unwind(cleanup).map_err(rt_abort)?;
panic::catch_unwind(cleanup).unwrap_or_else(handle_rt_panic);
// Guard against multiple threads calling `libc::exit` concurrently.
// See the documentation for `unique_thread_exit` for more information.
panic::catch_unwind(|| crate::sys::exit_guard::unique_thread_exit()).map_err(rt_abort)?;
panic::catch_unwind(crate::sys::exit_guard::unique_thread_exit).unwrap_or_else(handle_rt_panic);
ret_code
}

View file

@ -92,7 +92,11 @@ pub unsafe extern "C" fn runtime_entry(
unsafe {
crate::sys::thread_local::destructors::run();
}
unsafe { hermit_abi::exit(result) }
crate::rt::thread_cleanup();
unsafe {
hermit_abi::exit(result);
}
}
#[inline]

View file

@ -53,6 +53,7 @@ impl Thread {
// run all destructors
crate::sys::thread_local::destructors::run();
crate::rt::thread_cleanup();
}
}
}

View file

@ -113,7 +113,7 @@ use crate::mem;
use crate::ptr::{self, NonNull, null_mut, without_provenance_mut};
use crate::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release};
use crate::sync::atomic::{AtomicBool, AtomicPtr};
use crate::thread::{self, Thread};
use crate::thread::{self, Thread, ThreadId};
// Locking uses exponential backoff. `SPIN_COUNT` indicates how many times the
// locking operation will be retried.
@ -200,7 +200,9 @@ impl Node {
fn prepare(&mut self) {
// Fall back to creating an unnamed `Thread` handle to allow locking in
// TLS destructors.
self.thread.get_or_init(|| thread::try_current().unwrap_or_else(Thread::new_unnamed));
self.thread.get_or_init(|| {
thread::try_current().unwrap_or_else(|| Thread::new_unnamed(ThreadId::new()))
});
self.completed = AtomicBool::new(false);
}

View file

@ -26,6 +26,7 @@ pub fn enable() {
unsafe extern "C" fn run_dtors(_: *mut u8) {
unsafe {
destructors::run();
crate::rt::thread_cleanup();
}
}
}

View file

@ -3,10 +3,12 @@
//! that will run all native TLS destructors in the destructor list.
use crate::ptr;
use crate::sys::thread_local::destructors;
use crate::sys::thread_local::key::{LazyKey, set};
#[cfg(target_thread_local)]
pub fn enable() {
use crate::sys::thread_local::destructors;
static DTORS: LazyKey = LazyKey::new(Some(run));
// Setting the key value to something other than NULL will result in the
@ -18,6 +20,41 @@ pub fn enable() {
unsafe extern "C" fn run(_: *mut u8) {
unsafe {
destructors::run();
// On platforms with `__cxa_thread_atexit_impl`, `destructors::run`
// does nothing on newer systems as the TLS destructors are
// registered with the system. But because all of those platforms
// call the destructors of TLS keys after the registered ones, this
// function will still be run last (at the time of writing).
crate::rt::thread_cleanup();
}
}
}
/// On platforms with key-based TLS, the system runs the destructors for us.
/// We still have to make sure that [`crate::rt::thread_cleanup`] is called,
/// however. This is done by defering the execution of a TLS destructor to
/// the next round of destruction inside the TLS destructors.
#[cfg(not(target_thread_local))]
pub fn enable() {
const DEFER: *mut u8 = ptr::without_provenance_mut(1);
const RUN: *mut u8 = ptr::without_provenance_mut(2);
static CLEANUP: LazyKey = LazyKey::new(Some(run));
unsafe { set(CLEANUP.force(), DEFER) }
unsafe extern "C" fn run(state: *mut u8) {
if state == DEFER {
// Make sure that this function is run again in the next round of
// TLS destruction. If there is no futher round, there will be leaks,
// but that's okay, `thread_cleanup` is not guaranteed to be called.
unsafe { set(CLEANUP.force(), RUN) }
} else {
debug_assert_eq!(state, RUN);
// If the state is still RUN in the next round of TLS destruction,
// it means that no other TLS destructors defined by this runtime
// have been run, as they would have set the state to DEFER.
crate::rt::thread_cleanup();
}
}
}

View file

@ -19,6 +19,9 @@ pub fn enable() {
}
unsafe extern "C" fn tls_dtor(_unused: *mut u8) {
unsafe { destructors::run() };
unsafe {
destructors::run();
crate::rt::thread_cleanup();
}
}
}

View file

@ -80,13 +80,13 @@ pub static CALLBACK: unsafe extern "system" fn(*mut c_void, u32, *mut c_void) =
unsafe extern "system" fn tls_callback(_h: *mut c_void, dw_reason: u32, _pv: *mut c_void) {
if dw_reason == c::DLL_THREAD_DETACH || dw_reason == c::DLL_PROCESS_DETACH {
#[cfg(target_thread_local)]
unsafe {
#[cfg(target_thread_local)]
super::super::destructors::run();
}
#[cfg(not(target_thread_local))]
unsafe {
#[cfg(not(target_thread_local))]
super::super::key::run_dtors();
crate::rt::thread_cleanup();
}
}
}

View file

@ -212,4 +212,6 @@ unsafe fn run_dtors() {
unsafe { cur = (*cur).next };
}
}
crate::rt::thread_cleanup();
}

View file

@ -31,12 +31,15 @@ cfg_if::cfg_if! {
))] {
mod statik;
pub use statik::{EagerStorage, LazyStorage, thread_local_inner};
pub(crate) use statik::{LocalPointer, local_pointer};
} else if #[cfg(target_thread_local)] {
mod native;
pub use native::{EagerStorage, LazyStorage, thread_local_inner};
pub(crate) use native::{LocalPointer, local_pointer};
} else {
mod os;
pub use os::{Storage, thread_local_inner};
pub(crate) use os::{LocalPointer, local_pointer};
}
}
@ -72,36 +75,47 @@ pub(crate) mod destructors {
}
/// This module provides a way to schedule the execution of the destructor list
/// on systems without a per-variable destructor system.
mod guard {
/// and the [runtime cleanup](crate::rt::thread_cleanup) function. Calling `enable`
/// should ensure that these functions are called at the right times.
pub(crate) mod guard {
cfg_if::cfg_if! {
if #[cfg(all(target_thread_local, target_vendor = "apple"))] {
mod apple;
pub(super) use apple::enable;
pub(crate) use apple::enable;
} else if #[cfg(target_os = "windows")] {
mod windows;
pub(super) use windows::enable;
pub(crate) use windows::enable;
} else if #[cfg(any(
all(target_family = "wasm", target_feature = "atomics"),
target_family = "wasm",
target_os = "uefi",
target_os = "zkvm",
))] {
pub(super) fn enable() {
// FIXME: Right now there is no concept of "thread exit", but
// this is likely going to show up at some point in the form of
// an exported symbol that the wasm runtime is going to be
// expected to call. For now we just leak everything, but if
// such a function starts to exist it will probably need to
// iterate the destructor list with this function:
pub(crate) fn enable() {
// FIXME: Right now there is no concept of "thread exit" on
// wasm, but this is likely going to show up at some point in
// the form of an exported symbol that the wasm runtime is going
// to be expected to call. For now we just leak everything, but
// if such a function starts to exist it will probably need to
// iterate the destructor list with these functions:
#[cfg(all(target_family = "wasm", target_feature = "atomics"))]
#[allow(unused)]
use super::destructors::run;
#[allow(unused)]
use crate::rt::thread_cleanup;
}
} else if #[cfg(target_os = "hermit")] {
pub(super) fn enable() {}
} else if #[cfg(any(
target_os = "hermit",
target_os = "xous",
))] {
// `std` is the only runtime, so it just calls the destructor functions
// itself when the time comes.
pub(crate) fn enable() {}
} else if #[cfg(target_os = "solid_asp3")] {
mod solid;
pub(super) use solid::enable;
} else if #[cfg(all(target_thread_local, not(target_family = "wasm")))] {
pub(crate) use solid::enable;
} else {
mod key;
pub(super) use key::enable;
pub(crate) use key::enable;
}
}
}

View file

@ -29,6 +29,9 @@
//! eliminates the `Destroyed` state for these values, which can allow more niche
//! optimizations to occur for the `State` enum. For `Drop` types, `()` is used.
use crate::cell::Cell;
use crate::ptr;
mod eager;
mod lazy;
@ -107,3 +110,31 @@ pub macro thread_local_inner {
$crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*);
},
}
#[rustc_macro_transparency = "semitransparent"]
pub(crate) macro local_pointer {
() => {},
($vis:vis static $name:ident; $($rest:tt)*) => {
#[thread_local]
$vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new();
$crate::sys::thread_local::local_pointer! { $($rest)* }
},
}
pub(crate) struct LocalPointer {
p: Cell<*mut ()>,
}
impl LocalPointer {
pub const fn __new() -> LocalPointer {
LocalPointer { p: Cell::new(ptr::null_mut()) }
}
pub fn get(&self) -> *mut () {
self.p.get()
}
pub fn set(&self, p: *mut ()) {
self.p.set(p)
}
}

View file

@ -1,8 +1,8 @@
use super::abort_on_dtor_unwind;
use super::key::{Key, LazyKey, get, set};
use super::{abort_on_dtor_unwind, guard};
use crate::cell::Cell;
use crate::marker::PhantomData;
use crate::ptr;
use crate::sys::thread_local::key::{Key, LazyKey, get, set};
#[doc(hidden)]
#[allow_internal_unstable(thread_local_internals)]
@ -138,5 +138,35 @@ unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) {
drop(ptr);
// SAFETY: `key` is the TLS key `ptr` was stored under.
unsafe { set(key, ptr::null_mut()) };
// Make sure that the runtime cleanup will be performed
// after the next round of TLS destruction.
guard::enable();
});
}
#[rustc_macro_transparency = "semitransparent"]
pub(crate) macro local_pointer {
() => {},
($vis:vis static $name:ident; $($rest:tt)*) => {
$vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new();
$crate::sys::thread_local::local_pointer! { $($rest)* }
},
}
pub(crate) struct LocalPointer {
key: LazyKey,
}
impl LocalPointer {
pub const fn __new() -> LocalPointer {
LocalPointer { key: LazyKey::new(None) }
}
pub fn get(&'static self) -> *mut () {
unsafe { get(self.key.force()) as *mut () }
}
pub fn set(&'static self, p: *mut ()) {
unsafe { set(self.key.force(), p as *mut u8) }
}
}

View file

@ -1,7 +1,8 @@
//! On some targets like wasm there's no threads, so no need to generate
//! thread locals and we can instead just use plain statics!
use crate::cell::UnsafeCell;
use crate::cell::{Cell, UnsafeCell};
use crate::ptr;
#[doc(hidden)]
#[allow_internal_unstable(thread_local_internals)]
@ -93,3 +94,33 @@ impl<T> LazyStorage<T> {
// SAFETY: the target doesn't have threads.
unsafe impl<T> Sync for LazyStorage<T> {}
#[rustc_macro_transparency = "semitransparent"]
pub(crate) macro local_pointer {
() => {},
($vis:vis static $name:ident; $($rest:tt)*) => {
$vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new();
$crate::sys::thread_local::local_pointer! { $($rest)* }
},
}
pub(crate) struct LocalPointer {
p: Cell<*mut ()>,
}
impl LocalPointer {
pub const fn __new() -> LocalPointer {
LocalPointer { p: Cell::new(ptr::null_mut()) }
}
pub fn get(&self) -> *mut () {
self.p.get()
}
pub fn set(&self, p: *mut ()) {
self.p.set(p)
}
}
// SAFETY: the target doesn't have threads.
unsafe impl Sync for LocalPointer {}

View file

@ -0,0 +1,252 @@
use super::{Thread, ThreadId};
use crate::mem::ManuallyDrop;
use crate::ptr;
use crate::sys::thread_local::local_pointer;
const NONE: *mut () = ptr::null_mut();
const BUSY: *mut () = ptr::without_provenance_mut(1);
const DESTROYED: *mut () = ptr::without_provenance_mut(2);
local_pointer! {
static CURRENT;
}
/// Persistent storage for the thread ID.
///
/// We store the thread ID so that it never gets destroyed during the lifetime
/// of a thread, either using `#[thread_local]` or multiple `local_pointer!`s.
mod id {
use super::*;
cfg_if::cfg_if! {
if #[cfg(target_thread_local)] {
use crate::cell::Cell;
#[thread_local]
static ID: Cell<Option<ThreadId>> = Cell::new(None);
pub(super) const CHEAP: bool = true;
pub(super) fn get() -> Option<ThreadId> {
ID.get()
}
pub(super) fn set(id: ThreadId) {
ID.set(Some(id))
}
} else if #[cfg(target_pointer_width = "16")] {
local_pointer! {
static ID0;
static ID16;
static ID32;
static ID48;
}
pub(super) const CHEAP: bool = false;
pub(super) fn get() -> Option<ThreadId> {
let id0 = ID0.get().addr() as u64;
let id16 = ID16.get().addr() as u64;
let id32 = ID32.get().addr() as u64;
let id48 = ID48.get().addr() as u64;
ThreadId::from_u64((id48 << 48) + (id32 << 32) + (id16 << 16) + id0)
}
pub(super) fn set(id: ThreadId) {
let val = id.as_u64().get();
ID0.set(ptr::without_provenance_mut(val as usize));
ID16.set(ptr::without_provenance_mut((val >> 16) as usize));
ID32.set(ptr::without_provenance_mut((val >> 32) as usize));
ID48.set(ptr::without_provenance_mut((val >> 48) as usize));
}
} else if #[cfg(target_pointer_width = "32")] {
local_pointer! {
static ID0;
static ID32;
}
pub(super) const CHEAP: bool = false;
pub(super) fn get() -> Option<ThreadId> {
let id0 = ID0.get().addr() as u64;
let id32 = ID32.get().addr() as u64;
ThreadId::from_u64((id32 << 32) + id0)
}
pub(super) fn set(id: ThreadId) {
let val = id.as_u64().get();
ID0.set(ptr::without_provenance_mut(val as usize));
ID32.set(ptr::without_provenance_mut((val >> 32) as usize));
}
} else {
local_pointer! {
static ID;
}
pub(super) const CHEAP: bool = true;
pub(super) fn get() -> Option<ThreadId> {
let id = ID.get().addr() as u64;
ThreadId::from_u64(id)
}
pub(super) fn set(id: ThreadId) {
let val = id.as_u64().get();
ID.set(ptr::without_provenance_mut(val as usize));
}
}
}
#[inline]
pub(super) fn get_or_init() -> ThreadId {
get().unwrap_or_else(
#[cold]
|| {
let id = ThreadId::new();
id::set(id);
id
},
)
}
}
/// Sets the thread handle for the current thread.
///
/// Aborts if the handle or the ID has been set already.
pub(crate) fn set_current(thread: Thread) {
if CURRENT.get() != NONE || id::get().is_some() {
// Using `panic` here can add ~3kB to the binary size. We have complete
// control over where this is called, so just abort if there is a bug.
rtabort!("thread::set_current should only be called once per thread");
}
id::set(thread.id());
// Make sure that `crate::rt::thread_cleanup` will be run, which will
// call `drop_current`.
crate::sys::thread_local::guard::enable();
CURRENT.set(thread.into_raw().cast_mut());
}
/// Gets the id of the thread that invokes it.
///
/// This function will always succeed, will always return the same value for
/// one thread and is guaranteed not to call the global allocator.
#[inline]
pub(crate) fn current_id() -> ThreadId {
// If accessing the persistant thread ID takes multiple TLS accesses, try
// to retrieve it from the current thread handle, which will only take one
// TLS access.
if !id::CHEAP {
let current = CURRENT.get();
if current > DESTROYED {
unsafe {
let current = ManuallyDrop::new(Thread::from_raw(current));
return current.id();
}
}
}
id::get_or_init()
}
/// Gets a handle to the thread that invokes it, if the handle has been initialized.
pub(crate) fn try_current() -> Option<Thread> {
let current = CURRENT.get();
if current > DESTROYED {
unsafe {
let current = ManuallyDrop::new(Thread::from_raw(current));
Some((*current).clone())
}
} else {
None
}
}
/// Gets a handle to the thread that invokes it.
///
/// # Examples
///
/// Getting a handle to the current thread with `thread::current()`:
///
/// ```
/// use std::thread;
///
/// let handler = thread::Builder::new()
/// .name("named thread".into())
/// .spawn(|| {
/// let handle = thread::current();
/// assert_eq!(handle.name(), Some("named thread"));
/// })
/// .unwrap();
///
/// handler.join().unwrap();
/// ```
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn current() -> Thread {
let current = CURRENT.get();
if current > DESTROYED {
unsafe {
let current = ManuallyDrop::new(Thread::from_raw(current));
(*current).clone()
}
} else {
init_current(current)
}
}
#[cold]
fn init_current(current: *mut ()) -> Thread {
if current == NONE {
CURRENT.set(BUSY);
// If the thread ID was initialized already, use it.
let id = id::get_or_init();
let thread = Thread::new_unnamed(id);
// Make sure that `crate::rt::thread_cleanup` will be run, which will
// call `drop_current`.
crate::sys::thread_local::guard::enable();
CURRENT.set(thread.clone().into_raw().cast_mut());
thread
} else if current == BUSY {
// BUSY exists solely for this check, but as it is in the slow path, the
// extra TLS write above shouldn't matter. The alternative is nearly always
// a stack overflow.
// If you came across this message, contact the author of your allocator.
// If you are said author: A surprising amount of functions inside the
// standard library (e.g. `Mutex`, `thread_local!`, `File` when using long
// paths, even `panic!` when using unwinding), need memory allocation, so
// you'll get circular dependencies all over the place when using them.
// I (joboet) highly recommend using only APIs from core in your allocator
// and implementing your own system abstractions. Still, if you feel that
// a particular API should be entirely allocation-free, feel free to open
// an issue on the Rust repository, we'll see what we can do.
rtabort!(
"\n
Attempted to access thread-local data while allocating said data.\n
Do not access functions that allocate in the global allocator!\n
This is a bug in the global allocator.\n
"
)
} else {
debug_assert_eq!(current, DESTROYED);
panic!(
"use of std::thread::current() is not possible after the thread's
local data has been destroyed"
)
}
}
/// This should be run in [`crate::rt::thread_cleanup`] to reset the thread
/// handle.
pub(crate) fn drop_current() {
let current = CURRENT.get();
if current > DESTROYED {
unsafe {
CURRENT.set(DESTROYED);
drop(Thread::from_raw(current));
}
}
}

View file

@ -1,7 +1,7 @@
use crate::cell::{Cell, UnsafeCell};
use crate::sync::atomic::{AtomicU8, Ordering};
use crate::sync::{Arc, Condvar, Mutex};
use crate::thread::{self, LocalKey};
use crate::thread::{self, Builder, LocalKey};
use crate::thread_local;
#[derive(Clone, Default)]
@ -343,3 +343,34 @@ fn join_orders_after_tls_destructors() {
jh2.join().unwrap();
}
}
// Test that thread::current is still available in TLS destructors.
#[test]
fn thread_current_in_dtor() {
// Go through one round of TLS destruction first.
struct Defer;
impl Drop for Defer {
fn drop(&mut self) {
RETRIEVE.with(|_| {});
}
}
struct RetrieveName;
impl Drop for RetrieveName {
fn drop(&mut self) {
*NAME.lock().unwrap() = Some(thread::current().name().unwrap().to_owned());
}
}
static NAME: Mutex<Option<String>> = Mutex::new(None);
thread_local! {
static DEFER: Defer = const { Defer };
static RETRIEVE: RetrieveName = const { RetrieveName };
}
Builder::new().name("test".to_owned()).spawn(|| DEFER.with(|_| {})).unwrap().join().unwrap();
let name = NAME.lock().unwrap();
let name = name.as_ref().unwrap();
assert_eq!(name, "test");
}

View file

@ -141,7 +141,7 @@
//! [`Result`]: crate::result::Result
//! [`Ok`]: crate::result::Result::Ok
//! [`Err`]: crate::result::Result::Err
//! [`thread::current`]: current
//! [`thread::current`]: current::current
//! [`thread::Result`]: Result
//! [`unpark`]: Thread::unpark
//! [`thread::park_timeout`]: park_timeout
@ -159,7 +159,7 @@
mod tests;
use crate::any::Any;
use crate::cell::{Cell, OnceCell, UnsafeCell};
use crate::cell::UnsafeCell;
use crate::ffi::CStr;
use crate::marker::PhantomData;
use crate::mem::{self, ManuallyDrop, forget};
@ -179,6 +179,12 @@ mod scoped;
#[stable(feature = "scoped_threads", since = "1.63.0")]
pub use scoped::{Scope, ScopedJoinHandle, scope};
mod current;
#[stable(feature = "rust1", since = "1.0.0")]
pub use current::current;
pub(crate) use current::{current_id, drop_current, set_current, try_current};
////////////////////////////////////////////////////////////////////////////////
// Thread-local storage
////////////////////////////////////////////////////////////////////////////////
@ -471,7 +477,11 @@ impl Builder {
amt
});
let my_thread = name.map_or_else(Thread::new_unnamed, Thread::new);
let id = ThreadId::new();
let my_thread = match name {
Some(name) => Thread::new(id, name.into()),
None => Thread::new_unnamed(id),
};
let their_thread = my_thread.clone();
let my_packet: Arc<Packet<'scope, T>> = Arc::new(Packet {
@ -509,6 +519,9 @@ impl Builder {
let f = MaybeDangling::new(f);
let main = move || {
// Immediately store the thread handle to avoid setting it or its ID
// twice, which would cause an abort.
set_current(their_thread.clone());
if let Some(name) = their_thread.cname() {
imp::Thread::set_name(name);
}
@ -516,7 +529,6 @@ impl Builder {
crate::io::set_output_capture(output_capture);
let f = f.into_inner();
set_current(their_thread);
let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
crate::sys::backtrace::__rust_begin_short_backtrace(f)
}));
@ -690,84 +702,6 @@ where
Builder::new().spawn(f).expect("failed to spawn thread")
}
thread_local! {
// Invariant: `CURRENT` and `CURRENT_ID` will always be initialized together.
// If `CURRENT` is initialized, then `CURRENT_ID` will hold the same value
// as `CURRENT.id()`.
static CURRENT: OnceCell<Thread> = const { OnceCell::new() };
static CURRENT_ID: Cell<Option<ThreadId>> = const { Cell::new(None) };
}
/// Sets the thread handle for the current thread.
///
/// Aborts if the handle has been set already to reduce code size.
pub(crate) fn set_current(thread: Thread) {
let tid = thread.id();
// Using `unwrap` here can add ~3kB to the binary size. We have complete
// control over where this is called, so just abort if there is a bug.
CURRENT.with(|current| match current.set(thread) {
Ok(()) => CURRENT_ID.set(Some(tid)),
Err(_) => rtabort!("thread::set_current should only be called once per thread"),
});
}
/// Gets a handle to the thread that invokes it.
///
/// In contrast to the public `current` function, this will not panic if called
/// from inside a TLS destructor.
pub(crate) fn try_current() -> Option<Thread> {
CURRENT
.try_with(|current| {
current
.get_or_init(|| {
let thread = Thread::new_unnamed();
CURRENT_ID.set(Some(thread.id()));
thread
})
.clone()
})
.ok()
}
/// Gets the id of the thread that invokes it.
#[inline]
pub(crate) fn current_id() -> ThreadId {
CURRENT_ID.get().unwrap_or_else(|| {
// If `CURRENT_ID` isn't initialized yet, then `CURRENT` must also not be initialized.
// `current()` will initialize both `CURRENT` and `CURRENT_ID` so subsequent calls to
// `current_id()` will succeed immediately.
current().id()
})
}
/// Gets a handle to the thread that invokes it.
///
/// # Examples
///
/// Getting a handle to the current thread with `thread::current()`:
///
/// ```
/// use std::thread;
///
/// let handler = thread::Builder::new()
/// .name("named thread".into())
/// .spawn(|| {
/// let handle = thread::current();
/// assert_eq!(handle.name(), Some("named thread"));
/// })
/// .unwrap();
///
/// handler.join().unwrap();
/// ```
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn current() -> Thread {
try_current().expect(
"use of std::thread::current() is not possible \
after the thread's local data has been destroyed",
)
}
/// Cooperatively gives up a timeslice to the OS scheduler.
///
/// This calls the underlying OS scheduler's yield primitive, signaling
@ -1225,8 +1159,11 @@ pub fn park_timeout(dur: Duration) {
pub struct ThreadId(NonZero<u64>);
impl ThreadId {
// DO NOT rely on this value.
const MAIN_THREAD: ThreadId = ThreadId(unsafe { NonZero::new_unchecked(1) });
// Generate a new unique thread ID.
fn new() -> ThreadId {
pub(crate) fn new() -> ThreadId {
#[cold]
fn exhausted() -> ! {
panic!("failed to generate unique thread ID: bitspace exhausted")
@ -1236,7 +1173,7 @@ impl ThreadId {
if #[cfg(target_has_atomic = "64")] {
use crate::sync::atomic::AtomicU64;
static COUNTER: AtomicU64 = AtomicU64::new(0);
static COUNTER: AtomicU64 = AtomicU64::new(1);
let mut last = COUNTER.load(Ordering::Relaxed);
loop {
@ -1252,7 +1189,7 @@ impl ThreadId {
} else {
use crate::sync::{Mutex, PoisonError};
static COUNTER: Mutex<u64> = Mutex::new(0);
static COUNTER: Mutex<u64> = Mutex::new(1);
let mut counter = COUNTER.lock().unwrap_or_else(PoisonError::into_inner);
let Some(id) = counter.checked_add(1) else {
@ -1269,6 +1206,11 @@ impl ThreadId {
}
}
#[cfg(not(target_thread_local))]
fn from_u64(v: u64) -> Option<ThreadId> {
NonZero::new(v).map(ThreadId)
}
/// This returns a numeric identifier for the thread identified by this
/// `ThreadId`.
///
@ -1369,27 +1311,27 @@ impl Inner {
/// should instead use a function like `spawn` to create new threads, see the
/// docs of [`Builder`] and [`spawn`] for more details.
///
/// [`thread::current`]: current
/// [`thread::current`]: current::current
pub struct Thread {
inner: Pin<Arc<Inner>>,
}
impl Thread {
/// Used only internally to construct a thread object without spawning.
pub(crate) fn new(name: String) -> Thread {
Self::new_inner(ThreadName::Other(name.into()))
pub(crate) fn new(id: ThreadId, name: String) -> Thread {
Self::new_inner(id, ThreadName::Other(name.into()))
}
pub(crate) fn new_unnamed() -> Thread {
Self::new_inner(ThreadName::Unnamed)
pub(crate) fn new_unnamed(id: ThreadId) -> Thread {
Self::new_inner(id, ThreadName::Unnamed)
}
// Used in runtime to construct main thread
pub(crate) fn new_main() -> Thread {
Self::new_inner(ThreadName::Main)
Self::new_inner(ThreadId::MAIN_THREAD, ThreadName::Main)
}
fn new_inner(name: ThreadName) -> Thread {
fn new_inner(id: ThreadId, name: ThreadName) -> Thread {
// We have to use `unsafe` here to construct the `Parker` in-place,
// which is required for the UNIX implementation.
//
@ -1399,7 +1341,7 @@ impl Thread {
let mut arc = Arc::<Inner>::new_uninit();
let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr();
(&raw mut (*ptr).name).write(name);
(&raw mut (*ptr).id).write(ThreadId::new());
(&raw mut (*ptr).id).write(id);
Parker::new_in_place(&raw mut (*ptr).parker);
Pin::new_unchecked(arc.assume_init())
};

View file

@ -1828,7 +1828,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
Array(Box::new(clean_ty(ty, cx)), length.into())
}
TyKind::Tup(tys) => Tuple(tys.iter().map(|ty| clean_ty(ty, cx)).collect()),
TyKind::OpaqueDef(item_id, _, _) => {
TyKind::OpaqueDef(item_id, _) => {
let item = cx.tcx.hir().item(item_id);
if let hir::ItemKind::OpaqueTy(ty) = item.kind {
ImplTrait(ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect())

View file

@ -186,8 +186,6 @@ pub(crate) fn run(
let mut collector = CreateRunnableDocTests::new(options, opts);
let hir_collector = HirCollector::new(
&compiler.sess,
tcx.hir(),
ErrorCodes::from(compiler.sess.opts.unstable_features.is_nightly_build()),
enable_per_target_ignores,
tcx,

View file

@ -6,11 +6,9 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sync::Lrc;
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
use rustc_hir::{self as hir, CRATE_HIR_ID, intravisit};
use rustc_middle::hir::map::Map;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::TyCtxt;
use rustc_resolve::rustdoc::span_of_fragments;
use rustc_session::Session;
use rustc_span::source_map::SourceMap;
use rustc_span::{BytePos, DUMMY_SP, FileName, Pos, Span};
@ -63,30 +61,22 @@ impl DocTestVisitor for RustCollector {
fn visit_header(&mut self, _name: &str, _level: u32) {}
}
pub(super) struct HirCollector<'a, 'tcx> {
sess: &'a Session,
map: Map<'tcx>,
pub(super) struct HirCollector<'tcx> {
codes: ErrorCodes,
tcx: TyCtxt<'tcx>,
enable_per_target_ignores: bool,
collector: RustCollector,
}
impl<'a, 'tcx> HirCollector<'a, 'tcx> {
pub fn new(
sess: &'a Session,
map: Map<'tcx>,
codes: ErrorCodes,
enable_per_target_ignores: bool,
tcx: TyCtxt<'tcx>,
) -> Self {
impl<'tcx> HirCollector<'tcx> {
pub fn new(codes: ErrorCodes, enable_per_target_ignores: bool, tcx: TyCtxt<'tcx>) -> Self {
let collector = RustCollector {
source_map: sess.psess.clone_source_map(),
source_map: tcx.sess.psess.clone_source_map(),
cur_path: vec![],
position: DUMMY_SP,
tests: vec![],
};
Self { sess, map, codes, enable_per_target_ignores, tcx, collector }
Self { codes, enable_per_target_ignores, tcx, collector }
}
pub fn collect_crate(mut self) -> Vec<ScrapedDocTest> {
@ -98,7 +88,7 @@ impl<'a, 'tcx> HirCollector<'a, 'tcx> {
}
}
impl<'a, 'tcx> HirCollector<'a, 'tcx> {
impl<'tcx> HirCollector<'tcx> {
fn visit_testable<F: FnOnce(&mut Self)>(
&mut self,
name: String,
@ -108,7 +98,7 @@ impl<'a, 'tcx> HirCollector<'a, 'tcx> {
) {
let ast_attrs = self.tcx.hir().attrs(self.tcx.local_def_id_to_hir_id(def_id));
if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) {
if !cfg.matches(&self.sess.psess, Some(self.tcx.features())) {
if !cfg.matches(&self.tcx.sess.psess, Some(self.tcx.features())) {
return;
}
}
@ -141,17 +131,17 @@ impl<'a, 'tcx> HirCollector<'a, 'tcx> {
}
}
impl<'a, 'tcx> intravisit::Visitor<'tcx> for HirCollector<'a, 'tcx> {
impl<'tcx> intravisit::Visitor<'tcx> for HirCollector<'tcx> {
type NestedFilter = nested_filter::All;
fn nested_visit_map(&mut self) -> Self::Map {
self.map
self.tcx.hir()
}
fn visit_item(&mut self, item: &'tcx hir::Item<'_>) {
let name = match &item.kind {
hir::ItemKind::Impl(impl_) => {
rustc_hir_pretty::id_to_string(&self.map, impl_.self_ty.hir_id)
rustc_hir_pretty::id_to_string(&self.tcx.hir(), impl_.self_ty.hir_id)
}
_ => item.ident.to_string(),
};

View file

@ -515,7 +515,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
self.add_to_current_mod(item, renamed, import_id);
}
hir::ItemKind::OpaqueTy(hir::OpaqueTy {
origin: hir::OpaqueTyOrigin::AsyncFn(_) | hir::OpaqueTyOrigin::FnReturn(_),
origin: hir::OpaqueTyOrigin::AsyncFn { .. } | hir::OpaqueTyOrigin::FnReturn { .. },
..
}) => {
// return-position impl traits are never nameable, and should never be documented.

View file

@ -19,7 +19,7 @@ jobs:
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: '18.x'

View file

@ -5871,6 +5871,7 @@ Released 2018-09-13
[`ref_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_as_ptr
[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference
[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
[`ref_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option
[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
[`ref_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_patterns
[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro

View file

@ -353,6 +353,7 @@ Suppress lints whenever the suggested change would cause breakage for other crat
* [`rc_buffer`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer)
* [`rc_mutex`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex)
* [`redundant_allocation`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation)
* [`ref_option`](https://rust-lang.github.io/rust-clippy/master/index.html#ref_option)
* [`single_call_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#single_call_fn)
* [`trivially_copy_pass_by_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref)
* [`unnecessary_box_returns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns)

View file

@ -378,6 +378,7 @@ define_Conf! {
rc_buffer,
rc_mutex,
redundant_allocation,
ref_option,
single_call_fn,
trivially_copy_pass_by_ref,
unnecessary_box_returns,

View file

@ -19,6 +19,7 @@ macro_rules! msrv_aliases {
msrv_aliases! {
1,83,0 { CONST_EXTERN_FN }
1,83,0 { CONST_FLOAT_BITS_CONV }
1,82,0 { IS_NONE_OR }
1,81,0 { LINT_REASONS_STABILIZATION }
1,80,0 { BOX_INTO_ITER}
1,77,0 { C_STR_LITERALS }

View file

@ -1,3 +1,5 @@
use clippy_config::Conf;
use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::eq_expr_value;
use clippy_utils::source::SpanRangeExt;
@ -7,7 +9,7 @@ use rustc_errors::Applicability;
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp};
use rustc_lint::{LateContext, LateLintPass, Level};
use rustc_session::declare_lint_pass;
use rustc_session::{RustcVersion, impl_lint_pass};
use rustc_span::def_id::LocalDefId;
use rustc_span::{Span, sym};
@ -69,9 +71,25 @@ declare_clippy_lint! {
}
// For each pairs, both orders are considered.
const METHODS_WITH_NEGATION: [(&str, &str); 2] = [("is_some", "is_none"), ("is_err", "is_ok")];
const METHODS_WITH_NEGATION: [(Option<RustcVersion>, &str, &str); 3] = [
(None, "is_some", "is_none"),
(None, "is_err", "is_ok"),
(Some(msrvs::IS_NONE_OR), "is_some_and", "is_none_or"),
];
declare_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]);
pub struct NonminimalBool {
msrv: Msrv,
}
impl NonminimalBool {
pub fn new(conf: &'static Conf) -> Self {
Self {
msrv: conf.msrv.clone(),
}
}
}
impl_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]);
impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
fn check_fn(
@ -83,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
_: Span,
_: LocalDefId,
) {
NonminimalBoolVisitor { cx }.visit_body(body);
NonminimalBoolVisitor { cx, msrv: &self.msrv }.visit_body(body);
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
@ -100,6 +118,8 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
_ => {},
}
}
extract_msrv_attr!(LateContext);
}
fn inverted_bin_op_eq_str(op: BinOpKind) -> Option<&'static str> {
@ -176,11 +196,11 @@ fn check_inverted_bool_in_condition(
);
}
fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) {
fn check_simplify_not(cx: &LateContext<'_>, msrv: &Msrv, expr: &Expr<'_>) {
if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind
&& !expr.span.from_expansion()
&& !inner.span.from_expansion()
&& let Some(suggestion) = simplify_not(cx, inner)
&& let Some(suggestion) = simplify_not(cx, msrv, inner)
&& cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow
{
span_lint_and_sugg(
@ -197,6 +217,7 @@ fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) {
struct NonminimalBoolVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
msrv: &'a Msrv,
}
use quine_mc_cluskey::Bool;
@ -205,7 +226,7 @@ struct Hir2Qmm<'a, 'tcx, 'v> {
cx: &'a LateContext<'tcx>,
}
impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
impl<'v> Hir2Qmm<'_, '_, 'v> {
fn extract(&mut self, op: BinOpKind, a: &[&'v Expr<'_>], mut v: Vec<Bool>) -> Result<Vec<Bool>, String> {
for a in a {
if let ExprKind::Binary(binop, lhs, rhs) = &a.kind {
@ -289,10 +310,11 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
struct SuggestContext<'a, 'tcx, 'v> {
terminals: &'v [&'v Expr<'v>],
cx: &'a LateContext<'tcx>,
msrv: &'a Msrv,
output: String,
}
impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
impl SuggestContext<'_, '_, '_> {
fn recurse(&mut self, suggestion: &Bool) -> Option<()> {
use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True};
match suggestion {
@ -311,7 +333,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
},
Term(n) => {
let terminal = self.terminals[n as usize];
if let Some(str) = simplify_not(self.cx, terminal) {
if let Some(str) = simplify_not(self.cx, self.msrv, terminal) {
self.output.push_str(&str);
} else {
self.output.push('!');
@ -358,7 +380,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
}
}
fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
fn simplify_not(cx: &LateContext<'_>, curr_msrv: &Msrv, expr: &Expr<'_>) -> Option<String> {
match &expr.kind {
ExprKind::Binary(binop, lhs, rhs) => {
if !implements_ord(cx, lhs) {
@ -389,7 +411,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
Some(format!("{lhs_snippet}{op}{rhs_snippet}"))
})
},
ExprKind::MethodCall(path, receiver, [], _) => {
ExprKind::MethodCall(path, receiver, args, _) => {
let type_of_receiver = cx.typeck_results().expr_ty(receiver);
if !is_type_diagnostic_item(cx, type_of_receiver, sym::Option)
&& !is_type_diagnostic_item(cx, type_of_receiver, sym::Result)
@ -399,21 +421,41 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
METHODS_WITH_NEGATION
.iter()
.copied()
.flat_map(|(a, b)| vec![(a, b), (b, a)])
.find(|&(a, _)| {
let path: &str = path.ident.name.as_str();
a == path
.flat_map(|(msrv, a, b)| vec![(msrv, a, b), (msrv, b, a)])
.find(|&(msrv, a, _)| msrv.is_none_or(|msrv| curr_msrv.meets(msrv)) && a == path.ident.name.as_str())
.and_then(|(_, _, neg_method)| {
let negated_args = args
.iter()
.map(|arg| simplify_not(cx, curr_msrv, arg))
.collect::<Option<Vec<_>>>()?
.join(", ");
Some(format!(
"{}.{neg_method}({negated_args})",
receiver.span.get_source_text(cx)?
))
})
.and_then(|(_, neg_method)| Some(format!("{}.{neg_method}()", receiver.span.get_source_text(cx)?)))
},
ExprKind::Closure(closure) => {
let body = cx.tcx.hir().body(closure.body);
let params = body
.params
.iter()
.map(|param| param.span.get_source_text(cx).map(|t| t.to_string()))
.collect::<Option<Vec<_>>>()?
.join(", ");
let negated = simplify_not(cx, curr_msrv, body.value)?;
Some(format!("|{params}| {negated}"))
},
ExprKind::Unary(UnOp::Not, expr) => expr.span.get_source_text(cx).map(|t| t.to_string()),
_ => None,
}
}
fn suggest(cx: &LateContext<'_>, suggestion: &Bool, terminals: &[&Expr<'_>]) -> String {
fn suggest(cx: &LateContext<'_>, msrv: &Msrv, suggestion: &Bool, terminals: &[&Expr<'_>]) -> String {
let mut suggest_context = SuggestContext {
terminals,
cx,
msrv,
output: String::new(),
};
suggest_context.recurse(suggestion);
@ -475,7 +517,7 @@ fn terminal_stats(b: &Bool) -> Stats {
stats
}
impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
impl<'tcx> NonminimalBoolVisitor<'_, 'tcx> {
fn bool_expr(&self, e: &'tcx Expr<'_>) {
let mut h2q = Hir2Qmm {
terminals: Vec::new(),
@ -526,7 +568,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
diag.span_suggestion(
e.span,
"it would look like the following",
suggest(self.cx, suggestion, &h2q.terminals),
suggest(self.cx, self.msrv, suggestion, &h2q.terminals),
// nonminimal_bool can produce minimal but
// not human readable expressions (#3141)
Applicability::Unspecified,
@ -569,12 +611,12 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
}
};
if improvements.is_empty() {
check_simplify_not(self.cx, e);
check_simplify_not(self.cx, self.msrv, e);
} else {
nonminimal_bool_lint(
improvements
.into_iter()
.map(|suggestion| suggest(self.cx, suggestion, &h2q.terminals))
.map(|suggestion| suggest(self.cx, self.msrv, suggestion, &h2q.terminals))
.collect(),
);
}
@ -582,7 +624,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
}
}
impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'_, 'tcx> {
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
if !e.span.from_expansion() {
match &e.kind {

View file

@ -91,7 +91,7 @@ fn is_local_vec_expn(cx: &LateContext<'_>, expr: &Expr<'_>, ref_expr: &Expr<'_>)
#[derive(Default)]
struct InferVisitor(bool);
impl<'tcx> Visitor<'tcx> for InferVisitor {
impl Visitor<'_> for InferVisitor {
fn visit_ty(&mut self, t: &Ty<'_>) {
self.0 |= matches!(t.kind, TyKind::Infer | TyKind::OpaqueDef(..) | TyKind::TraitObject(..));
if !self.0 {

View file

@ -10,27 +10,27 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata, ignore_publish: b
// only run the lint if publish is `None` (`publish = true` or skipped entirely)
// or if the vector isn't empty (`publish = ["something"]`)
if package.publish.as_ref().filter(|publish| publish.is_empty()).is_none() || ignore_publish {
if is_empty_str(&package.description) {
if is_empty_str(package.description.as_ref()) {
missing_warning(cx, package, "package.description");
}
if is_empty_str(&package.license) && is_empty_str(&package.license_file) {
if is_empty_str(package.license.as_ref()) && is_empty_str(package.license_file.as_ref()) {
missing_warning(cx, package, "either package.license or package.license_file");
}
if is_empty_str(&package.repository) {
if is_empty_str(package.repository.as_ref()) {
missing_warning(cx, package, "package.repository");
}
if is_empty_str(&package.readme) {
if is_empty_str(package.readme.as_ref()) {
missing_warning(cx, package, "package.readme");
}
if is_empty_vec(&package.keywords) {
if is_empty_vec(package.keywords.as_ref()) {
missing_warning(cx, package, "package.keywords");
}
if is_empty_vec(&package.categories) {
if is_empty_vec(package.categories.as_ref()) {
missing_warning(cx, package, "package.categories");
}
}
@ -42,8 +42,8 @@ fn missing_warning(cx: &LateContext<'_>, package: &cargo_metadata::Package, fiel
span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, message);
}
fn is_empty_str<T: AsRef<std::ffi::OsStr>>(value: &Option<T>) -> bool {
value.as_ref().map_or(true, |s| s.as_ref().is_empty())
fn is_empty_str<T: AsRef<std::ffi::OsStr>>(value: Option<&T>) -> bool {
value.map_or(true, |s| s.as_ref().is_empty())
}
fn is_empty_vec(value: &[String]) -> bool {

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_no_std_crate;
use clippy_utils::source::snippet_with_context;
use clippy_utils::std_or_core;
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind};
use rustc_lint::LateContext;
@ -16,8 +16,8 @@ pub(super) fn check<'tcx>(
) {
if matches!(cast_to.kind, TyKind::Ptr(_))
&& let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind
&& let Some(std_or_core) = std_or_core(cx)
{
let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
let macro_name = match mutability {
Mutability::Not => "addr_of",
Mutability::Mut => "addr_of_mut",
@ -40,7 +40,7 @@ pub(super) fn check<'tcx>(
expr.span,
"borrow as raw pointer",
"try",
format!("{core_or_std}::ptr::{macro_name}!({snip})"),
format!("{std_or_core}::ptr::{macro_name}!({snip})"),
Applicability::MachineApplicable,
);
}

View file

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use clippy_utils::{ExprUseNode, expr_use_ctxt, is_no_std_crate};
use clippy_utils::{ExprUseNode, expr_use_ctxt, std_or_core};
use rustc_errors::Applicability;
use rustc_hir::{Expr, Mutability, Ty, TyKind};
use rustc_lint::LateContext;
@ -25,8 +25,8 @@ pub(super) fn check<'tcx>(
&& let use_cx = expr_use_ctxt(cx, expr)
// TODO: only block the lint if `cast_expr` is a temporary
&& !matches!(use_cx.use_node(cx), ExprUseNode::LetStmt(_) | ExprUseNode::ConstStatic(_))
&& let Some(std_or_core) = std_or_core(cx)
{
let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
let fn_name = match to_mutbl {
Mutability::Not => "from_ref",
Mutability::Mut => "from_mut",
@ -56,7 +56,7 @@ pub(super) fn check<'tcx>(
expr.span,
"reference as raw pointer",
"try",
format!("{core_or_std}::ptr::{fn_name}{turbofish}({cast_expr_sugg})"),
format!("{std_or_core}::ptr::{fn_name}{turbofish}({cast_expr_sugg})"),
app,
);
}

View file

@ -48,7 +48,7 @@ impl CheckedConversions {
impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
impl LateLintPass<'_> for CheckedConversions {
fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
if let ExprKind::Binary(op, lhs, rhs) = item.kind
&& let (lt1, gt1, op2) = match op.node {

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