Merge from rustc

This commit is contained in:
The Miri Cronjob Bot 2024-12-15 05:08:18 +00:00
commit 581989d1c6
668 changed files with 7073 additions and 4620 deletions

View file

@ -3507,7 +3507,6 @@ dependencies = [
"cc",
"either",
"itertools",
"jobserver",
"libc",
"object 0.36.5",
"pathdiff",

View file

@ -1382,6 +1382,7 @@ impl Expr {
| ExprKind::Tup(_)
| ExprKind::Type(..)
| ExprKind::Underscore
| ExprKind::UnsafeBinderCast(..)
| ExprKind::While(..)
| ExprKind::Err(_)
| ExprKind::Dummy => ExprPrecedence::Unambiguous,
@ -1509,7 +1510,13 @@ pub enum ExprKind {
/// `'label: for await? pat in iter { block }`
///
/// This is desugared to a combination of `loop` and `match` expressions.
ForLoop { pat: P<Pat>, iter: P<Expr>, body: P<Block>, label: Option<Label>, kind: ForLoopKind },
ForLoop {
pat: P<Pat>,
iter: P<Expr>,
body: P<Block>,
label: Option<Label>,
kind: ForLoopKind,
},
/// Conditionless loop (can be exited with `break`, `continue`, or `return`).
///
/// `'label: loop { block }`
@ -1614,6 +1621,8 @@ pub enum ExprKind {
/// A `format_args!()` expression.
FormatArgs(P<FormatArgs>),
UnsafeBinderCast(UnsafeBinderCastKind, P<Expr>, Option<P<Ty>>),
/// Placeholder for an expression that wasn't syntactically well formed in some way.
Err(ErrorGuaranteed),
@ -1652,6 +1661,16 @@ impl GenBlockKind {
}
}
/// Whether we're unwrapping or wrapping an unsafe binder
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Encodable, Decodable, HashStable_Generic)]
pub enum UnsafeBinderCastKind {
// e.g. `&i32` -> `unsafe<'a> &'a i32`
Wrap,
// e.g. `unsafe<'a> &'a i32` -> `&i32`
Unwrap,
}
/// The explicit `Self` type in a "qualified path". The actual
/// path, including the trait and the associated item, is stored
/// separately. `position` represents the index of the associated
@ -2223,6 +2242,12 @@ pub struct BareFnTy {
pub decl_span: Span,
}
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct UnsafeBinderTy {
pub generic_params: ThinVec<GenericParam>,
pub inner_ty: P<Ty>,
}
/// The various kinds of type recognized by the compiler.
//
// Adding a new variant? Please update `test_ty` in `tests/ui/macros/stringify.rs`.
@ -2242,6 +2267,8 @@ pub enum TyKind {
PinnedRef(Option<Lifetime>, MutTy),
/// A bare function (e.g., `fn(usize) -> bool`).
BareFn(P<BareFnTy>),
/// An unsafe existential lifetime binder (e.g., `unsafe<'a> &'a ()`).
UnsafeBinder(P<UnsafeBinderTy>),
/// The never type (`!`).
Never,
/// A tuple (`(A, B, C, D,...)`).
@ -2877,7 +2904,7 @@ pub enum ModKind {
/// or with definition outlined to a separate file `mod foo;` and already loaded from it.
/// The inner span is from the first token past `{` to the last token until `}`,
/// or from the first to the last token in the loaded file.
Loaded(ThinVec<P<Item>>, Inline, ModSpans),
Loaded(ThinVec<P<Item>>, Inline, ModSpans, Result<(), ErrorGuaranteed>),
/// Module with definition outlined to a separate file `mod foo;` but not yet loaded from it.
Unloaded,
}

View file

@ -558,6 +558,11 @@ pub fn walk_ty<T: MutVisitor>(vis: &mut T, ty: &mut P<Ty>) {
vis.visit_fn_decl(decl);
vis.visit_span(decl_span);
}
TyKind::UnsafeBinder(binder) => {
let UnsafeBinderTy { generic_params, inner_ty } = binder.deref_mut();
generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param));
vis.visit_ty(inner_ty);
}
TyKind::Tup(tys) => visit_thin_vec(tys, |ty| vis.visit_ty(ty)),
TyKind::Paren(ty) => vis.visit_ty(ty),
TyKind::Pat(ty, pat) => {
@ -1212,7 +1217,12 @@ impl WalkItemKind for ItemKind {
ItemKind::Mod(safety, mod_kind) => {
visit_safety(vis, safety);
match mod_kind {
ModKind::Loaded(items, _inline, ModSpans { inner_span, inject_use_span }) => {
ModKind::Loaded(
items,
_inline,
ModSpans { inner_span, inject_use_span },
_,
) => {
items.flat_map_in_place(|item| vis.flat_map_item(item));
vis.visit_span(inner_span);
vis.visit_span(inject_use_span);
@ -1775,6 +1785,12 @@ pub fn walk_expr<T: MutVisitor>(vis: &mut T, Expr { kind, id, span, attrs, token
ExprKind::TryBlock(body) => vis.visit_block(body),
ExprKind::Lit(_token) => {}
ExprKind::IncludedBytes(_bytes) => {}
ExprKind::UnsafeBinderCast(_kind, expr, ty) => {
vis.visit_expr(expr);
if let Some(ty) = ty {
vis.visit_ty(ty);
}
}
ExprKind::Err(_guar) => {}
ExprKind::Dummy => {}
}

View file

@ -152,6 +152,7 @@ pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool {
| Underscore
| Yeet(..)
| Yield(..)
| UnsafeBinderCast(..)
| Err(..)
| Dummy => return false,
}
@ -232,6 +233,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
| Paren(_)
| Try(_)
| Yeet(None)
| UnsafeBinderCast(..)
| Err(_)
| Dummy => break None,
}
@ -253,6 +255,10 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
ty = &mut_ty.ty;
}
ast::TyKind::UnsafeBinder(binder) => {
ty = &binder.inner_ty;
}
ast::TyKind::BareFn(fn_ty) => match &fn_ty.decl.output {
ast::FnRetTy::Default(_) => break None,
ast::FnRetTy::Ty(ret) => ty = ret,

View file

@ -380,7 +380,7 @@ impl WalkItemKind for ItemKind {
try_visit!(visitor.visit_fn(kind, span, id));
}
ItemKind::Mod(_unsafety, mod_kind) => match mod_kind {
ModKind::Loaded(items, _inline, _inner_span) => {
ModKind::Loaded(items, _inline, _inner_span, _) => {
walk_list!(visitor, visit_item, items);
}
ModKind::Unloaded => {}
@ -522,6 +522,10 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result {
walk_list!(visitor, visit_generic_param, generic_params);
try_visit!(visitor.visit_fn_decl(decl));
}
TyKind::UnsafeBinder(binder) => {
walk_list!(visitor, visit_generic_param, &binder.generic_params);
try_visit!(visitor.visit_ty(&binder.inner_ty));
}
TyKind::Path(maybe_qself, path) => {
try_visit!(visitor.visit_qself(maybe_qself));
try_visit!(visitor.visit_path(path, *id));
@ -1226,6 +1230,10 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
ExprKind::TryBlock(body) => try_visit!(visitor.visit_block(body)),
ExprKind::Lit(_token) => {}
ExprKind::IncludedBytes(_bytes) => {}
ExprKind::UnsafeBinderCast(_kind, expr, ty) => {
try_visit!(visitor.visit_expr(expr));
visit_opt!(visitor, visit_ty, ty);
}
ExprKind::Err(_guar) => {}
ExprKind::Dummy => {}
}

View file

@ -1,5 +1,6 @@
use rustc_ast::{Block, BlockCheckMode, Local, LocalKind, Stmt, StmtKind};
use rustc_hir as hir;
use rustc_span::sym;
use smallvec::SmallVec;
use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext};
@ -82,11 +83,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
(self.arena.alloc_from_iter(stmts), expr)
}
/// Return an `ImplTraitContext` that allows impl trait in bindings if
/// the feature gate is enabled, or issues a feature error if it is not.
fn impl_trait_in_bindings_ctxt(&self, position: ImplTraitPosition) -> ImplTraitContext {
if self.tcx.features().impl_trait_in_bindings() {
ImplTraitContext::InBinding
} else {
ImplTraitContext::FeatureGated(position, sym::impl_trait_in_bindings)
}
}
fn lower_local(&mut self, l: &Local) -> &'hir hir::LetStmt<'hir> {
let ty = l
.ty
.as_ref()
.map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Variable)));
// Let statements are allowed to have impl trait in bindings.
let ty = l.ty.as_ref().map(|t| {
self.lower_ty(t, self.impl_trait_in_bindings_ctxt(ImplTraitPosition::Variable))
});
let init = l.kind.init().map(|init| self.lower_expr(init));
let hir_id = self.lower_node_id(l.id);
let pat = self.lower_pat(&l.pat);

View file

@ -379,6 +379,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
ExprKind::Yield(opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()),
ExprKind::Err(guar) => hir::ExprKind::Err(*guar),
ExprKind::UnsafeBinderCast(kind, expr, ty) => hir::ExprKind::UnsafeBinderCast(
*kind,
self.lower_expr(expr),
ty.as_ref().map(|ty| {
self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Cast))
}),
),
ExprKind::Dummy => {
span_bug!(e.span, "lowered ExprKind::Dummy")
}

View file

@ -238,7 +238,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
})
}
ItemKind::Mod(_, mod_kind) => match mod_kind {
ModKind::Loaded(items, _, spans) => {
ModKind::Loaded(items, _, spans, _) => {
hir::ItemKind::Mod(self.lower_mod(items, spans))
}
ModKind::Unloaded => panic!("`mod` items should have been loaded by now"),

View file

@ -260,6 +260,13 @@ enum ImplTraitContext {
/// equivalent to a new opaque type like `type T = impl Debug; fn foo() -> T`.
///
OpaqueTy { origin: hir::OpaqueTyOrigin<LocalDefId> },
/// Treat `impl Trait` as a "trait ascription", which is like a type
/// variable but that also enforces that a set of trait goals hold.
///
/// This is useful to guide inference for unnameable types.
InBinding,
/// `impl Trait` is unstably accepted in this position.
FeatureGated(ImplTraitPosition, Symbol),
/// `impl Trait` is not accepted in this position.
@ -1228,6 +1235,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
param_names: self.lower_fn_params_to_names(&f.decl),
}))
}
TyKind::UnsafeBinder(f) => {
let generic_params = self.lower_lifetime_binder(t.id, &f.generic_params);
hir::TyKind::UnsafeBinder(self.arena.alloc(hir::UnsafeBinderTy {
generic_params,
inner_ty: self.lower_ty(&f.inner_ty, itctx),
}))
}
TyKind::Never => hir::TyKind::Never,
TyKind::Tup(tys) => hir::TyKind::Tup(
self.arena.alloc_from_iter(tys.iter().map(|ty| self.lower_ty_direct(ty, itctx))),
@ -1320,6 +1334,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
path
}
ImplTraitContext::InBinding => {
hir::TyKind::TraitAscription(self.lower_param_bounds(bounds, itctx))
}
ImplTraitContext::FeatureGated(position, feature) => {
let guar = self
.tcx

View file

@ -1029,7 +1029,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.dcx().emit_err(errors::UnsafeItem { span, kind: "module" });
}
// Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _))
if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _, _))
&& !attr::contains_name(&item.attrs, sym::path)
{
self.check_mod_file_item_asciionly(item.ident);

View file

@ -560,6 +560,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(return_type_notation, "return type notation is experimental");
gate_all!(pin_ergonomics, "pinned reference syntax is experimental");
gate_all!(unsafe_fields, "`unsafe` fields are experimental");
gate_all!(unsafe_binders, "unsafe binder types are experimental");
if !visitor.features.never_patterns() {
if let Some(spans) = spans.get(&sym::never_patterns) {

View file

@ -1198,6 +1198,14 @@ impl<'a> State<'a> {
ast::TyKind::BareFn(f) => {
self.print_ty_fn(f.ext, f.safety, &f.decl, None, &f.generic_params);
}
ast::TyKind::UnsafeBinder(f) => {
self.ibox(INDENT_UNIT);
self.word("unsafe");
self.print_generic_params(&f.generic_params);
self.nbsp();
self.print_type(&f.inner_ty);
self.end();
}
ast::TyKind::Path(None, path) => {
self.print_path(path, false, 0);
}

View file

@ -772,6 +772,25 @@ impl<'a> State<'a> {
self.word_nbsp("try");
self.print_block_with_attrs(blk, attrs)
}
ast::ExprKind::UnsafeBinderCast(kind, expr, ty) => {
self.word("builtin # ");
match kind {
ast::UnsafeBinderCastKind::Wrap => self.word("wrap_binder"),
ast::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder"),
}
self.popen();
self.ibox(0);
self.print_expr(expr, FixupContext::default());
if let Some(ty) = ty {
self.word(",");
self.space();
self.print_type(ty);
}
self.end();
self.pclose();
}
ast::ExprKind::Err(_) => {
self.popen();
self.word("/*ERROR*/");

View file

@ -34,6 +34,25 @@ pub struct BorrowSet<'tcx> {
pub(crate) locals_state_at_exit: LocalsStateAtExit,
}
// These methods are public to support borrowck consumers.
impl<'tcx> BorrowSet<'tcx> {
pub fn location_map(&self) -> &FxIndexMap<Location, BorrowData<'tcx>> {
&self.location_map
}
pub fn activation_map(&self) -> &FxIndexMap<Location, Vec<BorrowIndex>> {
&self.activation_map
}
pub fn local_map(&self) -> &FxIndexMap<mir::Local, FxIndexSet<BorrowIndex>> {
&self.local_map
}
pub fn locals_state_at_exit(&self) -> &LocalsStateAtExit {
&self.locals_state_at_exit
}
}
impl<'tcx> Index<BorrowIndex> for BorrowSet<'tcx> {
type Output = BorrowData<'tcx>;
@ -45,7 +64,7 @@ impl<'tcx> Index<BorrowIndex> for BorrowSet<'tcx> {
/// Location where a two-phase borrow is activated, if a borrow
/// is in fact a two-phase borrow.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub(crate) enum TwoPhaseActivation {
pub enum TwoPhaseActivation {
NotTwoPhase,
NotActivated,
ActivatedAt(Location),
@ -68,6 +87,33 @@ pub struct BorrowData<'tcx> {
pub(crate) assigned_place: mir::Place<'tcx>,
}
// These methods are public to support borrowck consumers.
impl<'tcx> BorrowData<'tcx> {
pub fn reserve_location(&self) -> Location {
self.reserve_location
}
pub fn activation_location(&self) -> TwoPhaseActivation {
self.activation_location
}
pub fn kind(&self) -> mir::BorrowKind {
self.kind
}
pub fn region(&self) -> RegionVid {
self.region
}
pub fn borrowed_place(&self) -> mir::Place<'tcx> {
self.borrowed_place
}
pub fn assigned_place(&self) -> mir::Place<'tcx> {
self.assigned_place
}
}
impl<'tcx> fmt::Display for BorrowData<'tcx> {
fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
let kind = match self.kind {
@ -120,7 +166,7 @@ impl LocalsStateAtExit {
}
impl<'tcx> BorrowSet<'tcx> {
pub(crate) fn build(
pub fn build(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
locals_are_invalidated_at_exit: bool,

View file

@ -5,15 +5,15 @@ use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::mir::{Body, Promoted};
use rustc_middle::ty::TyCtxt;
pub use super::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation};
pub use super::constraints::OutlivesConstraint;
pub use super::dataflow::{BorrowIndex, Borrows, calculate_borrows_out_of_scope_at_location};
pub use super::facts::{AllFacts as PoloniusInput, RustcFacts};
pub use super::facts::{AllFacts as PoloniusInput, PoloniusRegionVid, RustcFacts};
pub use super::location::{LocationTable, RichLocation};
pub use super::nll::PoloniusOutput;
pub use super::place_ext::PlaceExt;
pub use super::places_conflict::{PlaceConflictBias, places_conflict};
pub use super::region_infer::RegionInferenceContext;
use crate::borrow_set::BorrowSet;
/// Options determining the output behavior of [`get_body_with_borrowck_facts`].
///

View file

@ -8,7 +8,10 @@ use rustc_middle::mir::{
};
use rustc_middle::ty::{RegionVid, TyCtxt};
use rustc_mir_dataflow::fmt::DebugWithContext;
use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces};
use rustc_mir_dataflow::impls::{
EverInitializedPlaces, EverInitializedPlacesDomain, MaybeUninitializedPlaces,
MaybeUninitializedPlacesDomain,
};
use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice, SwitchIntEdgeEffects};
use tracing::debug;
@ -24,7 +27,7 @@ pub(crate) struct Borrowck<'a, 'tcx> {
}
impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
type Domain = BorrowckDomain<'a, 'tcx>;
type Domain = BorrowckDomain;
const NAME: &'static str = "borrowck";
@ -41,48 +44,48 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
unreachable!();
}
fn apply_before_statement_effect(
fn apply_early_statement_effect(
&mut self,
state: &mut Self::Domain,
stmt: &mir::Statement<'tcx>,
loc: Location,
) {
self.borrows.apply_before_statement_effect(&mut state.borrows, stmt, loc);
self.uninits.apply_before_statement_effect(&mut state.uninits, stmt, loc);
self.ever_inits.apply_before_statement_effect(&mut state.ever_inits, stmt, loc);
self.borrows.apply_early_statement_effect(&mut state.borrows, stmt, loc);
self.uninits.apply_early_statement_effect(&mut state.uninits, stmt, loc);
self.ever_inits.apply_early_statement_effect(&mut state.ever_inits, stmt, loc);
}
fn apply_statement_effect(
fn apply_primary_statement_effect(
&mut self,
state: &mut Self::Domain,
stmt: &mir::Statement<'tcx>,
loc: Location,
) {
self.borrows.apply_statement_effect(&mut state.borrows, stmt, loc);
self.uninits.apply_statement_effect(&mut state.uninits, stmt, loc);
self.ever_inits.apply_statement_effect(&mut state.ever_inits, stmt, loc);
self.borrows.apply_primary_statement_effect(&mut state.borrows, stmt, loc);
self.uninits.apply_primary_statement_effect(&mut state.uninits, stmt, loc);
self.ever_inits.apply_primary_statement_effect(&mut state.ever_inits, stmt, loc);
}
fn apply_before_terminator_effect(
fn apply_early_terminator_effect(
&mut self,
state: &mut Self::Domain,
term: &mir::Terminator<'tcx>,
loc: Location,
) {
self.borrows.apply_before_terminator_effect(&mut state.borrows, term, loc);
self.uninits.apply_before_terminator_effect(&mut state.uninits, term, loc);
self.ever_inits.apply_before_terminator_effect(&mut state.ever_inits, term, loc);
self.borrows.apply_early_terminator_effect(&mut state.borrows, term, loc);
self.uninits.apply_early_terminator_effect(&mut state.uninits, term, loc);
self.ever_inits.apply_early_terminator_effect(&mut state.ever_inits, term, loc);
}
fn apply_terminator_effect<'mir>(
fn apply_primary_terminator_effect<'mir>(
&mut self,
state: &mut Self::Domain,
term: &'mir mir::Terminator<'tcx>,
loc: Location,
) -> TerminatorEdges<'mir, 'tcx> {
self.borrows.apply_terminator_effect(&mut state.borrows, term, loc);
self.uninits.apply_terminator_effect(&mut state.uninits, term, loc);
self.ever_inits.apply_terminator_effect(&mut state.ever_inits, term, loc);
self.borrows.apply_primary_terminator_effect(&mut state.borrows, term, loc);
self.uninits.apply_primary_terminator_effect(&mut state.uninits, term, loc);
self.ever_inits.apply_primary_terminator_effect(&mut state.ever_inits, term, loc);
// This return value doesn't matter. It's only used by `iterate_to_fixpoint`, which this
// analysis doesn't use.
@ -110,14 +113,14 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
}
}
impl JoinSemiLattice for BorrowckDomain<'_, '_> {
impl JoinSemiLattice for BorrowckDomain {
fn join(&mut self, _other: &Self) -> bool {
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
unreachable!();
}
}
impl<'tcx, C> DebugWithContext<C> for BorrowckDomain<'_, 'tcx>
impl<'tcx, C> DebugWithContext<C> for BorrowckDomain
where
C: rustc_mir_dataflow::move_paths::HasMoveData<'tcx>,
{
@ -160,10 +163,10 @@ where
/// The transient state of the dataflow analyses used by the borrow checker.
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct BorrowckDomain<'a, 'tcx> {
pub(crate) borrows: <Borrows<'a, 'tcx> as Analysis<'tcx>>::Domain,
pub(crate) uninits: <MaybeUninitializedPlaces<'a, 'tcx> as Analysis<'tcx>>::Domain,
pub(crate) ever_inits: <EverInitializedPlaces<'a, 'tcx> as Analysis<'tcx>>::Domain,
pub(crate) struct BorrowckDomain {
pub(crate) borrows: BorrowsDomain,
pub(crate) uninits: MaybeUninitializedPlacesDomain,
pub(crate) ever_inits: EverInitializedPlacesDomain,
}
rustc_index::newtype_index! {
@ -503,7 +506,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
/// That means they went out of a nonlexical scope
fn kill_loans_out_of_scope_at_location(
&self,
trans: &mut <Self as Analysis<'tcx>>::Domain,
state: &mut <Self as Analysis<'tcx>>::Domain,
location: Location,
) {
// NOTE: The state associated with a given `location`
@ -518,14 +521,14 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
// region, then setting that gen-bit will override any
// potential kill introduced here.
if let Some(indices) = self.borrows_out_of_scope_at_location.get(&location) {
trans.kill_all(indices.iter().copied());
state.kill_all(indices.iter().copied());
}
}
/// Kill any borrows that conflict with `place`.
fn kill_borrows_on_place(
&self,
trans: &mut <Self as Analysis<'tcx>>::Domain,
state: &mut <Self as Analysis<'tcx>>::Domain,
place: Place<'tcx>,
) {
debug!("kill_borrows_on_place: place={:?}", place);
@ -543,7 +546,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
// `places_conflict` for every borrow.
if place.projection.is_empty() {
if !self.body.local_decls[place.local].is_ref_to_static() {
trans.kill_all(other_borrows_of_local);
state.kill_all(other_borrows_of_local);
}
return;
}
@ -562,10 +565,12 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
)
});
trans.kill_all(definitely_conflicting_borrows);
state.kill_all(definitely_conflicting_borrows);
}
}
type BorrowsDomain = BitSet<BorrowIndex>;
/// Forward dataflow computation of the set of borrows that are in scope at a particular location.
/// - we gen the introduced loans
/// - we kill loans on locals going out of (regular) scope
@ -574,7 +579,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
/// - we also kill loans of conflicting places when overwriting a shared path: e.g. borrows of
/// `a.b.c` when `a` is overwritten.
impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
type Domain = BitSet<BorrowIndex>;
type Domain = BorrowsDomain;
const NAME: &'static str = "borrows";
@ -588,18 +593,18 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
// function execution, so this method has no effect.
}
fn apply_before_statement_effect(
fn apply_early_statement_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
location: Location,
) {
self.kill_loans_out_of_scope_at_location(trans, location);
self.kill_loans_out_of_scope_at_location(state, location);
}
fn apply_statement_effect(
fn apply_primary_statement_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
stmt: &mir::Statement<'tcx>,
location: Location,
) {
@ -617,18 +622,18 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
panic!("could not find BorrowIndex for location {location:?}");
});
trans.gen_(index);
state.gen_(index);
}
// Make sure there are no remaining borrows for variables
// that are assigned over.
self.kill_borrows_on_place(trans, *lhs);
self.kill_borrows_on_place(state, *lhs);
}
mir::StatementKind::StorageDead(local) => {
// Make sure there are no remaining borrows for locals that
// are gone out of scope.
self.kill_borrows_on_place(trans, Place::from(*local));
self.kill_borrows_on_place(state, Place::from(*local));
}
mir::StatementKind::FakeRead(..)
@ -646,18 +651,18 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
}
}
fn apply_before_terminator_effect(
fn apply_early_terminator_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
_terminator: &mir::Terminator<'tcx>,
location: Location,
) {
self.kill_loans_out_of_scope_at_location(trans, location);
self.kill_loans_out_of_scope_at_location(state, location);
}
fn apply_terminator_effect<'mir>(
fn apply_primary_terminator_effect<'mir>(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
_location: Location,
) -> TerminatorEdges<'mir, 'tcx> {
@ -666,7 +671,7 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
if let mir::InlineAsmOperand::Out { place: Some(place), .. }
| mir::InlineAsmOperand::InOut { out_place: Some(place), .. } = *op
{
self.kill_borrows_on_place(trans, place);
self.kill_borrows_on_place(state, place);
}
}
}

View file

@ -1100,12 +1100,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
let decl_span = local_decl.source_info.span;
let label = match *local_decl.local_info() {
let amp_mut_sugg = match *local_decl.local_info() {
LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
let suggestion = suggest_ampmut_self(self.infcx.tcx, decl_span);
let additional =
local_trait.map(|span| (span, suggest_ampmut_self(self.infcx.tcx, span)));
Some((true, decl_span, suggestion, additional))
Some(AmpMutSugg { has_sugg: true, span: decl_span, suggestion, additional })
}
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
@ -1150,7 +1150,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
None
}
None => {
let (has_sugg, decl_span, sugg) = if name != kw::SelfLower {
if name != kw::SelfLower {
suggest_ampmut(
self.infcx.tcx,
local_decl.ty,
@ -1165,7 +1165,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
..
})) => {
let sugg = suggest_ampmut_self(self.infcx.tcx, decl_span);
(true, decl_span, sugg)
Some(AmpMutSugg {
has_sugg: true,
span: decl_span,
suggestion: sugg,
additional: None,
})
}
// explicit self (eg `self: &'a Self`)
_ => suggest_ampmut(
@ -1176,8 +1181,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
opt_ty_info,
),
}
};
Some((has_sugg, decl_span, sugg, None))
}
}
}
}
@ -1187,15 +1191,24 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
..
})) => {
let pattern_span: Span = local_decl.source_info.span;
suggest_ref_mut(self.infcx.tcx, pattern_span)
.map(|span| (true, span, "mut ".to_owned(), None))
suggest_ref_mut(self.infcx.tcx, pattern_span).map(|span| AmpMutSugg {
has_sugg: true,
span,
suggestion: "mut ".to_owned(),
additional: None,
})
}
_ => unreachable!(),
};
match label {
Some((true, err_help_span, suggested_code, additional)) => {
match amp_mut_sugg {
Some(AmpMutSugg {
has_sugg: true,
span: err_help_span,
suggestion: suggested_code,
additional,
}) => {
let mut sugg = vec![(err_help_span, suggested_code)];
if let Some(s) = additional {
sugg.push(s);
@ -1217,7 +1230,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
);
}
}
Some((false, err_label_span, message, _)) => {
Some(AmpMutSugg {
has_sugg: false, span: err_label_span, suggestion: message, ..
}) => {
let def_id = self.body.source.def_id();
let hir_id = if let Some(local_def_id) = def_id.as_local()
&& let Some(body) = self.infcx.tcx.hir().maybe_body_owned_by(local_def_id)
@ -1422,6 +1437,13 @@ fn suggest_ampmut_self<'tcx>(tcx: TyCtxt<'tcx>, span: Span) -> String {
}
}
struct AmpMutSugg {
has_sugg: bool,
span: Span,
suggestion: String,
additional: Option<(Span, String)>,
}
// When we want to suggest a user change a local variable to be a `&mut`, there
// are three potential "obvious" things to highlight:
//
@ -1443,7 +1465,7 @@ fn suggest_ampmut<'tcx>(
decl_span: Span,
opt_assignment_rhs_span: Option<Span>,
opt_ty_info: Option<Span>,
) -> (bool, Span, String) {
) -> Option<AmpMutSugg> {
// if there is a RHS and it starts with a `&` from it, then check if it is
// mutable, and if not, put suggest putting `mut ` to make it mutable.
// we don't have to worry about lifetime annotations here because they are
@ -1456,6 +1478,11 @@ fn suggest_ampmut<'tcx>(
&& let Ok(src) = tcx.sess.source_map().span_to_snippet(assignment_rhs_span)
&& let Some(stripped) = src.strip_prefix('&')
{
let is_raw_ref = stripped.trim_start().starts_with("raw ");
// We don't support raw refs yet
if is_raw_ref {
return None;
}
let is_mut = if let Some(rest) = stripped.trim_start().strip_prefix("mut") {
match rest.chars().next() {
// e.g. `&mut x`
@ -1479,7 +1506,12 @@ fn suggest_ampmut<'tcx>(
// FIXME(Ezrashaw): returning is bad because we still might want to
// update the annotated type, see #106857.
return (true, span, "mut ".to_owned());
return Some(AmpMutSugg {
has_sugg: true,
span,
suggestion: "mut ".to_owned(),
additional: None,
});
}
}
@ -1504,18 +1536,23 @@ fn suggest_ampmut<'tcx>(
&& let Some(ws_pos) = src.find(char::is_whitespace)
{
let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
(true, span, " mut".to_owned())
Some(AmpMutSugg { has_sugg: true, span, suggestion: " mut".to_owned(), additional: None })
// if there is already a binding, we modify it to be `mut`
} else if binding_exists {
// shrink the span to just after the `&` in `&variable`
let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
(true, span, "mut ".to_owned())
Some(AmpMutSugg { has_sugg: true, span, suggestion: "mut ".to_owned(), additional: None })
} else {
// otherwise, suggest that the user annotates the binding; we provide the
// type of the local.
let ty = decl_ty.builtin_deref(true).unwrap();
(false, span, format!("{}mut {}", if decl_ty.is_ref() { "&" } else { "*" }, ty))
Some(AmpMutSugg {
has_sugg: false,
span,
suggestion: format!("{}mut {}", if decl_ty.is_ref() { "&" } else { "*" }, ty),
additional: None,
})
}
}

View file

@ -43,7 +43,7 @@ use rustc_mir_dataflow::impls::{
use rustc_mir_dataflow::move_paths::{
InitIndex, InitLocation, LookupResult, MoveData, MoveOutIndex, MovePathIndex,
};
use rustc_mir_dataflow::{Analysis, EntrySets, Results, ResultsVisitor, visit_results};
use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results};
use rustc_session::lint::builtin::UNUSED_MUT;
use rustc_span::{Span, Symbol};
use smallvec::SmallVec;
@ -426,14 +426,14 @@ fn get_flow_results<'a, 'tcx>(
ever_inits: ever_inits.analysis,
};
assert_eq!(borrows.entry_sets.len(), uninits.entry_sets.len());
assert_eq!(borrows.entry_sets.len(), ever_inits.entry_sets.len());
let entry_sets: EntrySets<'_, Borrowck<'_, '_>> =
itertools::izip!(borrows.entry_sets, uninits.entry_sets, ever_inits.entry_sets)
assert_eq!(borrows.entry_states.len(), uninits.entry_states.len());
assert_eq!(borrows.entry_states.len(), ever_inits.entry_states.len());
let entry_states: EntryStates<'_, Borrowck<'_, '_>> =
itertools::izip!(borrows.entry_states, uninits.entry_states, ever_inits.entry_states)
.map(|(borrows, uninits, ever_inits)| BorrowckDomain { borrows, uninits, ever_inits })
.collect();
Results { analysis, entry_sets }
Results { analysis, entry_states }
}
pub(crate) struct BorrowckInferCtxt<'tcx> {
@ -600,10 +600,10 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
// 3. assignments do not affect things loaned out as immutable
// 4. moves do not affect things loaned out in any way
impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, '_, 'tcx> {
fn visit_statement_before_primary_effect(
fn visit_after_early_statement_effect(
&mut self,
_results: &mut Results<'tcx, Borrowck<'a, 'tcx>>,
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
stmt: &'a Statement<'tcx>,
location: Location,
) {
@ -674,10 +674,10 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<
}
}
fn visit_terminator_before_primary_effect(
fn visit_after_early_terminator_effect(
&mut self,
_results: &mut Results<'tcx, Borrowck<'a, 'tcx>>,
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
term: &'a Terminator<'tcx>,
loc: Location,
) {
@ -787,10 +787,10 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<
}
}
fn visit_terminator_after_primary_effect(
fn visit_after_primary_terminator_effect(
&mut self,
_results: &mut Results<'tcx, Borrowck<'a, 'tcx>>,
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
term: &'a Terminator<'tcx>,
loc: Location,
) {
@ -983,7 +983,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
place_span: (Place<'tcx>, Span),
kind: (AccessDepth, ReadOrWrite),
is_local_mutation_allowed: LocalMutationIsAllowed,
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
) {
let (sd, rw) = kind;
@ -1032,7 +1032,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
place_span: (Place<'tcx>, Span),
sd: AccessDepth,
rw: ReadOrWrite,
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
) -> bool {
let mut error_reported = false;
@ -1172,7 +1172,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
location: Location,
place_span: (Place<'tcx>, Span),
kind: AccessDepth,
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
) {
// Write of P[i] or *P requires P init'd.
self.check_if_assigned_path_is_moved(location, place_span, state);
@ -1190,7 +1190,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
&mut self,
location: Location,
(rvalue, span): (&'a Rvalue<'tcx>, Span),
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
) {
match rvalue {
&Rvalue::Ref(_ /*rgn*/, bk, place) => {
@ -1448,7 +1448,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
&mut self,
location: Location,
(operand, span): (&'a Operand<'tcx>, Span),
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
) {
match *operand {
Operand::Copy(place) => {
@ -1568,12 +1568,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
}
}
fn check_activations(
&mut self,
location: Location,
span: Span,
state: &BorrowckDomain<'a, 'tcx>,
) {
fn check_activations(&mut self, location: Location, span: Span, state: &BorrowckDomain) {
// Two-phase borrow support: For each activation that is newly
// generated at this statement, check if it interferes with
// another borrow.
@ -1731,7 +1726,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
location: Location,
desired_action: InitializationRequiringAction,
place_span: (PlaceRef<'tcx>, Span),
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
) {
let maybe_uninits = &state.uninits;
@ -1836,7 +1831,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
location: Location,
desired_action: InitializationRequiringAction,
place_span: (PlaceRef<'tcx>, Span),
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
) {
let maybe_uninits = &state.uninits;
@ -1935,7 +1930,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
&mut self,
location: Location,
(place, span): (Place<'tcx>, Span),
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
) {
debug!("check_if_assigned_path_is_moved place: {:?}", place);
@ -2001,7 +1996,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
location: Location,
base: PlaceRef<'tcx>,
span: Span,
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
) {
// rust-lang/rust#21232: Until Rust allows reads from the
// initialized parts of partially initialized structs, we
@ -2092,7 +2087,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
(place, span): (Place<'tcx>, Span),
kind: ReadOrWrite,
is_local_mutation_allowed: LocalMutationIsAllowed,
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
location: Location,
) -> bool {
debug!(
@ -2206,18 +2201,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
}
}
fn is_local_ever_initialized(
&self,
local: Local,
state: &BorrowckDomain<'a, 'tcx>,
) -> Option<InitIndex> {
fn is_local_ever_initialized(&self, local: Local, state: &BorrowckDomain) -> Option<InitIndex> {
let mpi = self.move_data.rev_lookup.find_local(local)?;
let ii = &self.move_data.init_path_map[mpi];
ii.into_iter().find(|&&index| state.ever_inits.contains(index)).copied()
}
/// Adds the place into the used mutable variables set
fn add_used_mut(&mut self, root_place: RootPlace<'tcx>, state: &BorrowckDomain<'a, 'tcx>) {
fn add_used_mut(&mut self, root_place: RootPlace<'tcx>, state: &BorrowckDomain) {
match root_place {
RootPlace { place_local: local, place_projection: [], is_local_mutation_allowed } => {
// If the local may have been initialized, and it is now currently being

View file

@ -275,7 +275,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
user_ty: ty::UserType<'tcx>,
span: Span,
) {
let ty::UserType::Ty(user_ty) = user_ty else { bug!() };
let ty::UserTypeKind::Ty(user_ty) = user_ty.kind else { bug!() };
// A fast path for a common case with closure input/output types.
if let ty::Infer(_) = user_ty.kind() {

View file

@ -110,7 +110,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
) {
self.ascribe_user_type_skip_wf(
arg_decl.ty,
ty::UserType::Ty(user_ty),
ty::UserType::new(ty::UserTypeKind::Ty(user_ty)),
arg_decl.source_info.span,
);
}
@ -119,7 +119,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
let output_decl = &body.local_decls[RETURN_PLACE];
self.ascribe_user_type_skip_wf(
output_decl.ty,
ty::UserType::Ty(user_provided_sig.output()),
ty::UserType::new(ty::UserTypeKind::Ty(user_provided_sig.output())),
output_decl.source_info.span,
);
}

View file

@ -31,7 +31,7 @@ use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::{
self, Binder, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, CoroutineArgsExt,
Dynamic, GenericArgsRef, OpaqueHiddenType, OpaqueTypeKey, RegionVid, Ty, TyCtxt, UserArgs,
UserType, UserTypeAnnotationIndex,
UserTypeAnnotationIndex,
};
use rustc_middle::{bug, span_bug};
use rustc_mir_dataflow::ResultsCursor;
@ -370,7 +370,10 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
} else {
self.cx.ascribe_user_type(
constant.const_.ty(),
UserType::TypeOf(uv.def, UserArgs { args: uv.args, user_self_ty: None }),
ty::UserType::new(ty::UserTypeKind::TypeOf(uv.def, UserArgs {
args: uv.args,
user_self_ty: None,
})),
locations.span(self.cx.body),
);
}
@ -991,9 +994,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
for user_annotation in self.user_type_annotations {
let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation;
let annotation = self.instantiate_canonical(span, user_ty);
if let ty::UserType::TypeOf(def, args) = annotation
if let ty::UserTypeKind::TypeOf(def, args) = annotation.kind
&& let DefKind::InlineConst = tcx.def_kind(def)
{
assert!(annotation.bounds.is_empty());
self.check_inline_const(inferred_ty, def.expect_local(), args, span);
} else {
self.ascribe_user_type(inferred_ty, annotation, span);

View file

@ -323,7 +323,8 @@ impl<'cx, 'a> Context<'cx, 'a> {
| ExprKind::While(_, _, _)
| ExprKind::Yeet(_)
| ExprKind::Become(_)
| ExprKind::Yield(_) => {}
| ExprKind::Yield(_)
| ExprKind::UnsafeBinderCast(..) => {}
}
}

View file

@ -141,8 +141,10 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
// We don't want to recurse into anything other than mods, since
// mods or tests inside of functions will break things
if let ast::ItemKind::Mod(_, ModKind::Loaded(.., ast::ModSpans { inner_span: span, .. })) =
item.kind
if let ast::ItemKind::Mod(
_,
ModKind::Loaded(.., ast::ModSpans { inner_span: span, .. }, _),
) = item.kind
{
let prev_tests = mem::take(&mut self.tests);
walk_item_kind(

View file

@ -1,8 +1,7 @@
use std::sync::{Arc, Condvar, Mutex};
use jobserver::HelperThread;
use rustc_data_structures::jobserver::{self, HelperThread};
use rustc_errors::DiagCtxtHandle;
use rustc_session::Session;
// FIXME don't panic when a worker thread panics
@ -14,14 +13,13 @@ pub(super) struct ConcurrencyLimiter {
}
impl ConcurrencyLimiter {
pub(super) fn new(sess: &Session, pending_jobs: usize) -> Self {
pub(super) fn new(pending_jobs: usize) -> Self {
let state = Arc::new(Mutex::new(state::ConcurrencyLimiterState::new(pending_jobs)));
let available_token_condvar = Arc::new(Condvar::new());
let state_helper = state.clone();
let available_token_condvar_helper = available_token_condvar.clone();
let helper_thread = sess
.jobserver
let helper_thread = jobserver::client()
.clone()
.into_helper_thread(move |token| {
let mut state = state_helper.lock().unwrap();
@ -113,7 +111,7 @@ impl Drop for ConcurrencyLimiterToken {
}
mod state {
use jobserver::Acquired;
use rustc_data_structures::jobserver::Acquired;
#[derive(Debug)]
pub(super) struct ConcurrencyLimiterState {

View file

@ -679,7 +679,7 @@ pub(crate) fn run_aot(
metadata_module: None,
metadata,
crate_info: CrateInfo::new(tcx, target_cpu),
concurrency_limiter: ConcurrencyLimiter::new(tcx.sess, 0),
concurrency_limiter: ConcurrencyLimiter::new(0),
});
};
@ -711,7 +711,7 @@ pub(crate) fn run_aot(
CguReuse::PreLto | CguReuse::PostLto => false,
});
let concurrency_limiter = IntoDynSyncSend(ConcurrencyLimiter::new(tcx.sess, todo_cgus.len()));
let concurrency_limiter = IntoDynSyncSend(ConcurrencyLimiter::new(todo_cgus.len()));
let modules = tcx.sess.time("codegen mono items", || {
let mut modules: Vec<_> = par_map(todo_cgus, |(_, cgu)| {

View file

@ -287,12 +287,7 @@ fn dep_symbol_lookup_fn(
let mut dylib_paths = Vec::new();
let data = &crate_info
.dependency_formats
.iter()
.find(|(crate_type, _data)| *crate_type == rustc_session::config::CrateType::Executable)
.unwrap()
.1;
let data = &crate_info.dependency_formats[&rustc_session::config::CrateType::Executable].1;
// `used_crates` is in reverse postorder in terms of dependencies. Reverse the order here to
// get a postorder which ensures that all dependencies of a dylib are loaded before the dylib
// itself. This helps the dynamic linker to find dylibs not in the regular dynamic library

View file

@ -12,7 +12,6 @@
#![warn(unused_lifetimes)]
// tidy-alphabetical-end
extern crate jobserver;
#[macro_use]
extern crate rustc_middle;
extern crate rustc_abi;
@ -175,7 +174,11 @@ impl CodegenBackend for CraneliftCodegenBackend {
}
}
fn target_features(&self, sess: &Session, _allow_unstable: bool) -> Vec<rustc_span::Symbol> {
fn target_features_cfg(
&self,
sess: &Session,
_allow_unstable: bool,
) -> Vec<rustc_span::Symbol> {
// FIXME return the actually used target features. this is necessary for #[cfg(target_feature)]
if sess.target.arch == "x86_64" && sess.target.os != "none" {
// x86_64 mandates SSE2 support

View file

@ -9,7 +9,7 @@ codegen_gcc_lto_not_supported =
LTO is not supported. You may get a linker error.
codegen_gcc_forbidden_ctarget_feature =
target feature `{$feature}` cannot be toggled with `-Ctarget-feature`
target feature `{$feature}` cannot be toggled with `-Ctarget-feature`: {$reason}
codegen_gcc_unwinding_inline_asm =
GCC backend does not support unwinding from inline asm

View file

@ -28,6 +28,7 @@ pub(crate) struct UnstableCTargetFeature<'a> {
#[diag(codegen_gcc_forbidden_ctarget_feature)]
pub(crate) struct ForbiddenCTargetFeature<'a> {
pub feature: &'a str,
pub reason: &'a str,
}
#[derive(Subdiagnostic)]

View file

@ -5,7 +5,7 @@ use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable;
use rustc_data_structures::fx::FxHashMap;
use rustc_middle::bug;
use rustc_session::Session;
use rustc_target::target_features::{RUSTC_SPECIFIC_FEATURES, Stability};
use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES;
use smallvec::{SmallVec, smallvec};
use crate::errors::{
@ -94,13 +94,17 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
};
sess.dcx().emit_warn(unknown_feature);
}
Some((_, Stability::Stable, _)) => {}
Some((_, Stability::Unstable(_), _)) => {
// An unstable feature. Warn about using it.
sess.dcx().emit_warn(UnstableCTargetFeature { feature });
}
Some((_, Stability::Forbidden { .. }, _)) => {
sess.dcx().emit_err(ForbiddenCTargetFeature { feature });
Some((_, stability, _)) => {
if let Err(reason) =
stability.compute_toggleability(&sess.target).allow_toggle()
{
sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason });
} else if stability.requires_nightly().is_some() {
// An unstable feature. Warn about using it. (It makes little sense
// to hard-error here since we just warn about fully unknown
// features above).
sess.dcx().emit_warn(UnstableCTargetFeature { feature });
}
}
}

View file

@ -260,8 +260,8 @@ impl CodegenBackend for GccCodegenBackend {
.join(sess)
}
fn target_features(&self, sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
target_features(sess, allow_unstable, &self.target_info)
fn target_features_cfg(&self, sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
target_features_cfg(sess, allow_unstable, &self.target_info)
}
}
@ -472,7 +472,8 @@ fn to_gcc_opt_level(optlevel: Option<OptLevel>) -> OptimizationLevel {
}
}
pub fn target_features(
/// Returns the features that should be set in `cfg(target_feature)`.
fn target_features_cfg(
sess: &Session,
allow_unstable: bool,
target_info: &LockedTargetInfo,
@ -481,9 +482,9 @@ pub fn target_features(
sess.target
.rust_target_features()
.iter()
.filter(|(_, gate, _)| gate.is_supported())
.filter(|(_, gate, _)| gate.in_cfg())
.filter_map(|&(feature, gate, _)| {
if sess.is_nightly_build() || allow_unstable || gate.is_stable() {
if sess.is_nightly_build() || allow_unstable || gate.requires_nightly().is_none() {
Some(feature)
} else {
None

View file

@ -75,10 +75,10 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
// Encode all filenames referenced by coverage mappings in this CGU.
let filenames_buffer = global_file_table.make_filenames_buffer(tcx);
let filenames_size = filenames_buffer.len();
let filenames_val = cx.const_bytes(&filenames_buffer);
let filenames_ref = llvm_cov::hash_bytes(&filenames_buffer);
// The `llvm-cov` tool uses this hash to associate each covfun record with
// its corresponding filenames table, since the final binary will typically
// contain multiple covmap records from different compilation units.
let filenames_hash = llvm_cov::hash_bytes(&filenames_buffer);
let mut unused_function_names = Vec::new();
@ -101,7 +101,7 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
for covfun in &covfun_records {
unused_function_names.extend(covfun.mangled_function_name_if_unused());
covfun::generate_covfun_record(cx, filenames_ref, covfun)
covfun::generate_covfun_record(cx, filenames_hash, covfun)
}
// For unused functions, we need to take their mangled names and store them
@ -126,7 +126,7 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
// Generate the coverage map header, which contains the filenames used by
// this CGU's coverage mappings, and store it in a well-known global.
// (This is skipped if we returned early due to having no covfun records.)
generate_covmap_record(cx, covmap_version, filenames_size, filenames_val);
generate_covmap_record(cx, covmap_version, &filenames_buffer);
}
/// Maps "global" (per-CGU) file ID numbers to their underlying filenames.
@ -225,38 +225,35 @@ fn span_file_name(tcx: TyCtxt<'_>, span: Span) -> Symbol {
/// Generates the contents of the covmap record for this CGU, which mostly
/// consists of a header and a list of filenames. The record is then stored
/// as a global variable in the `__llvm_covmap` section.
fn generate_covmap_record<'ll>(
cx: &CodegenCx<'ll, '_>,
version: u32,
filenames_size: usize,
filenames_val: &'ll llvm::Value,
) {
debug!("cov map: filenames_size = {}, 0-based version = {}", filenames_size, version);
// Create the coverage data header (Note, fields 0 and 2 are now always zero,
// as of `llvm::coverage::CovMapVersion::Version4`.)
let zero_was_n_records_val = cx.const_u32(0);
let filenames_size_val = cx.const_u32(filenames_size as u32);
let zero_was_coverage_size_val = cx.const_u32(0);
let version_val = cx.const_u32(version);
let cov_data_header_val = cx.const_struct(
&[zero_was_n_records_val, filenames_size_val, zero_was_coverage_size_val, version_val],
/*packed=*/ false,
fn generate_covmap_record<'ll>(cx: &CodegenCx<'ll, '_>, version: u32, filenames_buffer: &[u8]) {
// A covmap record consists of four target-endian u32 values, followed by
// the encoded filenames table. Two of the header fields are unused in
// modern versions of the LLVM coverage mapping format, and are always 0.
// <https://llvm.org/docs/CoverageMappingFormat.html#llvm-ir-representation>
// See also `src/llvm-project/clang/lib/CodeGen/CoverageMappingGen.cpp`.
let covmap_header = cx.const_struct(
&[
cx.const_u32(0), // (unused)
cx.const_u32(filenames_buffer.len() as u32),
cx.const_u32(0), // (unused)
cx.const_u32(version),
],
/* packed */ false,
);
let covmap_record = cx
.const_struct(&[covmap_header, cx.const_bytes(filenames_buffer)], /* packed */ false);
// Create the complete LLVM coverage data value to add to the LLVM IR
let covmap_data =
cx.const_struct(&[cov_data_header_val, filenames_val], /*packed=*/ false);
let llglobal = llvm::add_global(cx.llmod, cx.val_ty(covmap_data), &llvm_cov::covmap_var_name());
llvm::set_initializer(llglobal, covmap_data);
llvm::set_global_constant(llglobal, true);
llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage);
llvm::set_section(llglobal, &llvm_cov::covmap_section_name(cx.llmod));
let covmap_global =
llvm::add_global(cx.llmod, cx.val_ty(covmap_record), &llvm_cov::covmap_var_name());
llvm::set_initializer(covmap_global, covmap_record);
llvm::set_global_constant(covmap_global, true);
llvm::set_linkage(covmap_global, llvm::Linkage::PrivateLinkage);
llvm::set_section(covmap_global, &llvm_cov::covmap_section_name(cx.llmod));
// LLVM's coverage mapping format specifies 8-byte alignment for items in this section.
// <https://llvm.org/docs/CoverageMappingFormat.html>
llvm::set_alignment(llglobal, Align::EIGHT);
cx.add_used_global(llglobal);
llvm::set_alignment(covmap_global, Align::EIGHT);
cx.add_used_global(covmap_global);
}
/// Each CGU will normally only emit coverage metadata for the functions that it actually generates.

View file

@ -136,7 +136,7 @@ fn fill_region_tables<'tcx>(
/// as a global variable in the `__llvm_covfun` section.
pub(crate) fn generate_covfun_record<'tcx>(
cx: &CodegenCx<'_, 'tcx>,
filenames_ref: u64,
filenames_hash: u64,
covfun: &CovfunRecord<'tcx>,
) {
let &CovfunRecord {
@ -155,46 +155,45 @@ pub(crate) fn generate_covfun_record<'tcx>(
regions,
);
// Concatenate the encoded coverage mappings
let coverage_mapping_size = coverage_mapping_buffer.len();
let coverage_mapping_val = cx.const_bytes(&coverage_mapping_buffer);
// A covfun record consists of four target-endian integers, followed by the
// encoded mapping data in bytes. Note that the length field is 32 bits.
// <https://llvm.org/docs/CoverageMappingFormat.html#llvm-ir-representation>
// See also `src/llvm-project/clang/lib/CodeGen/CoverageMappingGen.cpp` and
// `COVMAP_V3` in `src/llvm-project/llvm/include/llvm/ProfileData/InstrProfData.inc`.
let func_name_hash = llvm_cov::hash_bytes(mangled_function_name.as_bytes());
let func_name_hash_val = cx.const_u64(func_name_hash);
let coverage_mapping_size_val = cx.const_u32(coverage_mapping_size as u32);
let source_hash_val = cx.const_u64(source_hash);
let filenames_ref_val = cx.const_u64(filenames_ref);
let func_record_val = cx.const_struct(
let covfun_record = cx.const_struct(
&[
func_name_hash_val,
coverage_mapping_size_val,
source_hash_val,
filenames_ref_val,
coverage_mapping_val,
cx.const_u64(func_name_hash),
cx.const_u32(coverage_mapping_buffer.len() as u32),
cx.const_u64(source_hash),
cx.const_u64(filenames_hash),
cx.const_bytes(&coverage_mapping_buffer),
],
/*packed=*/ true,
// This struct needs to be packed, so that the 32-bit length field
// doesn't have unexpected padding.
true,
);
// Choose a variable name to hold this function's covfun data.
// Functions that are used have a suffix ("u") to distinguish them from
// unused copies of the same function (from different CGUs), so that if a
// linker sees both it won't discard the used copy's data.
let func_record_var_name =
CString::new(format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" }))
.unwrap();
debug!("function record var name: {:?}", func_record_var_name);
let u = if is_used { "u" } else { "" };
let covfun_var_name = CString::new(format!("__covrec_{func_name_hash:X}{u}")).unwrap();
debug!("function record var name: {covfun_var_name:?}");
let llglobal = llvm::add_global(cx.llmod, cx.val_ty(func_record_val), &func_record_var_name);
llvm::set_initializer(llglobal, func_record_val);
llvm::set_global_constant(llglobal, true);
llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage);
llvm::set_visibility(llglobal, llvm::Visibility::Hidden);
llvm::set_section(llglobal, cx.covfun_section_name());
let covfun_global = llvm::add_global(cx.llmod, cx.val_ty(covfun_record), &covfun_var_name);
llvm::set_initializer(covfun_global, covfun_record);
llvm::set_global_constant(covfun_global, true);
llvm::set_linkage(covfun_global, llvm::Linkage::LinkOnceODRLinkage);
llvm::set_visibility(covfun_global, llvm::Visibility::Hidden);
llvm::set_section(covfun_global, cx.covfun_section_name());
// LLVM's coverage mapping format specifies 8-byte alignment for items in this section.
// <https://llvm.org/docs/CoverageMappingFormat.html>
llvm::set_alignment(llglobal, Align::EIGHT);
llvm::set_alignment(covfun_global, Align::EIGHT);
if cx.target_spec().supports_comdat() {
llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name);
llvm::set_comdat(cx.llmod, covfun_global, &covfun_var_name);
}
cx.add_used_global(llglobal);
cx.add_used_global(covfun_global);
}

View file

@ -27,7 +27,7 @@ use std::mem::ManuallyDrop;
use back::owned_target_machine::OwnedTargetMachine;
use back::write::{create_informational_target_machine, create_target_machine};
use errors::ParseTargetMachineConfig;
pub use llvm_util::target_features;
pub use llvm_util::target_features_cfg;
use rustc_ast::expand::allocator::AllocatorKind;
use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule};
use rustc_codegen_ssa::back::write::{
@ -330,8 +330,8 @@ impl CodegenBackend for LlvmCodegenBackend {
llvm_util::print_version();
}
fn target_features(&self, sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
target_features(sess, allow_unstable)
fn target_features_cfg(&self, sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
target_features_cfg(sess, allow_unstable)
}
fn codegen_crate<'tcx>(

View file

@ -17,7 +17,7 @@ use rustc_session::Session;
use rustc_session::config::{PrintKind, PrintRequest};
use rustc_span::symbol::Symbol;
use rustc_target::spec::{MergeFunctions, PanicStrategy, SmallDataThresholdSupport};
use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES, Stability};
use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES};
use crate::back::write::create_informational_target_machine;
use crate::errors::{
@ -300,7 +300,7 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
/// Must express features in the way Rust understands them.
///
/// We do not have to worry about RUSTC_SPECIFIC_FEATURES here, those are handled outside codegen.
pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
let mut features: FxHashSet<Symbol> = Default::default();
// Add base features for the target.
@ -316,7 +316,7 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
sess.target
.rust_target_features()
.iter()
.filter(|(_, gate, _)| gate.is_supported())
.filter(|(_, gate, _)| gate.in_cfg())
.filter(|(feature, _, _)| {
// skip checking special features, as LLVM may not understand them
if RUSTC_SPECIAL_FEATURES.contains(feature) {
@ -372,9 +372,9 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
sess.target
.rust_target_features()
.iter()
.filter(|(_, gate, _)| gate.is_supported())
.filter(|(_, gate, _)| gate.in_cfg())
.filter_map(|&(feature, gate, _)| {
if sess.is_nightly_build() || allow_unstable || gate.is_stable() {
if sess.is_nightly_build() || allow_unstable || gate.requires_nightly().is_none() {
Some(feature)
} else {
None
@ -493,7 +493,7 @@ fn print_target_features(sess: &Session, tm: &llvm::TargetMachine, out: &mut Str
.rust_target_features()
.iter()
.filter_map(|(feature, gate, _implied)| {
if !gate.is_supported() {
if !gate.in_cfg() {
// Only list (experimentally) supported features.
return None;
}
@ -716,13 +716,17 @@ pub(crate) fn global_llvm_features(
};
sess.dcx().emit_warn(unknown_feature);
}
Some((_, Stability::Stable, _)) => {}
Some((_, Stability::Unstable(_), _)) => {
// An unstable feature. Warn about using it.
sess.dcx().emit_warn(UnstableCTargetFeature { feature });
}
Some((_, Stability::Forbidden { reason }, _)) => {
sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason });
Some((_, stability, _)) => {
if let Err(reason) =
stability.compute_toggleability(&sess.target).allow_toggle()
{
sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason });
} else if stability.requires_nightly().is_some() {
// An unstable feature. Warn about using it. It makes little sense
// to hard-error here since we just warn about fully unknown
// features above.
sess.dcx().emit_warn(UnstableCTargetFeature { feature });
}
}
}

View file

@ -11,7 +11,6 @@ bitflags = "2.4.1"
cc = "1.1.23"
either = "1.5.0"
itertools = "0.12"
jobserver = "0.1.28"
pathdiff = "0.2.0"
regex = "1.4"
rustc_abi = { path = "../rustc_abi" }

View file

@ -236,7 +236,13 @@ pub fn each_linked_rlib(
) -> Result<(), errors::LinkRlibError> {
let crates = info.used_crates.iter();
let fmts = if crate_type.is_none() {
let fmts = if let Some(crate_type) = crate_type {
let Some(fmts) = info.dependency_formats.get(&crate_type) else {
return Err(errors::LinkRlibError::MissingFormat);
};
fmts
} else {
for combination in info.dependency_formats.iter().combinations(2) {
let (ty1, list1) = &combination[0];
let (ty2, list2) = &combination[1];
@ -252,18 +258,7 @@ pub fn each_linked_rlib(
if info.dependency_formats.is_empty() {
return Err(errors::LinkRlibError::MissingFormat);
}
&info.dependency_formats[0].1
} else {
let fmts = info
.dependency_formats
.iter()
.find_map(|&(ty, ref list)| if Some(ty) == crate_type { Some(list) } else { None });
let Some(fmts) = fmts else {
return Err(errors::LinkRlibError::MissingFormat);
};
fmts
info.dependency_formats.first().unwrap().1
};
for &cnum in crates {
@ -624,8 +619,7 @@ fn link_staticlib(
let fmts = codegen_results
.crate_info
.dependency_formats
.iter()
.find_map(|&(ty, ref list)| if ty == CrateType::Staticlib { Some(list) } else { None })
.get(&CrateType::Staticlib)
.expect("no dependency formats for staticlib");
let mut all_rust_dylibs = vec![];
@ -2355,11 +2349,10 @@ fn linker_with_args(
// they are used within inlined functions or instantiated generic functions. We do this *after*
// handling the raw-dylib symbols in the current crate to make sure that those are chosen first
// by the linker.
let (_, dependency_linkage) = codegen_results
let dependency_linkage = codegen_results
.crate_info
.dependency_formats
.iter()
.find(|(ty, _)| *ty == crate_type)
.get(&crate_type)
.expect("failed to find crate type in dependency format list");
// We sort the libraries below
@ -2738,11 +2731,10 @@ fn add_upstream_rust_crates(
// Linking to a rlib involves just passing it to the linker (the linker
// will slurp up the object files inside), and linking to a dynamic library
// involves just passing the right -l flag.
let (_, data) = codegen_results
let data = codegen_results
.crate_info
.dependency_formats
.iter()
.find(|(ty, _)| *ty == crate_type)
.get(&crate_type)
.expect("failed to find crate type in dependency format list");
if sess.target.is_like_aix {

View file

@ -1749,7 +1749,7 @@ fn for_each_exported_symbols_include_dep<'tcx>(
}
let formats = tcx.dependency_formats(());
let deps = formats.iter().find_map(|(t, list)| (*t == crate_type).then_some(list)).unwrap();
let deps = &formats[&crate_type];
for (index, dep_format) in deps.iter().enumerate() {
let cnum = CrateNum::new(index + 1);

View file

@ -6,9 +6,9 @@ use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender, channel};
use std::{fs, io, mem, str, thread};
use jobserver::{Acquired, Client};
use rustc_ast::attr;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_data_structures::jobserver::{self, Acquired};
use rustc_data_structures::memmap::Mmap;
use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard};
use rustc_errors::emitter::Emitter;
@ -456,7 +456,6 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>(
metadata_module: Option<CompiledModule>,
) -> OngoingCodegen<B> {
let (coordinator_send, coordinator_receive) = channel();
let sess = tcx.sess;
let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID);
let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins);
@ -477,7 +476,6 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>(
shared_emitter,
codegen_worker_send,
coordinator_receive,
sess.jobserver.clone(),
Arc::new(regular_config),
Arc::new(metadata_config),
Arc::new(allocator_config),
@ -1093,7 +1091,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
shared_emitter: SharedEmitter,
codegen_worker_send: Sender<CguMessage>,
coordinator_receive: Receiver<Box<dyn Any + Send>>,
jobserver: Client,
regular_config: Arc<ModuleConfig>,
metadata_config: Arc<ModuleConfig>,
allocator_config: Arc<ModuleConfig>,
@ -1145,7 +1142,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
// get tokens on `coordinator_receive` which will
// get managed in the main loop below.
let coordinator_send2 = coordinator_send.clone();
let helper = jobserver
let helper = jobserver::client()
.into_helper_thread(move |token| {
drop(coordinator_send2.send(Box::new(Message::Token::<B>(token))));
})

View file

@ -11,7 +11,7 @@ use rustc_middle::ty::TyCtxt;
use rustc_session::parse::feature_err;
use rustc_span::Span;
use rustc_span::symbol::{Symbol, sym};
use rustc_target::target_features::{self, Stability};
use rustc_target::target_features;
use crate::errors;
@ -20,7 +20,7 @@ use crate::errors;
pub(crate) fn from_target_feature_attr(
tcx: TyCtxt<'_>,
attr: &ast::Attribute,
rust_target_features: &UnordMap<String, target_features::Stability>,
rust_target_features: &UnordMap<String, target_features::StabilityComputed>,
target_features: &mut Vec<TargetFeature>,
) {
let Some(list) = attr.meta_item_list() else { return };
@ -63,32 +63,24 @@ pub(crate) fn from_target_feature_attr(
return None;
};
// Only allow target features whose feature gates have been enabled.
let allowed = match stability {
Stability::Forbidden { .. } => false,
Stability::Stable => true,
Stability::Unstable(name) => rust_features.enabled(*name),
};
if !allowed {
match stability {
Stability::Stable => unreachable!(),
&Stability::Unstable(lang_feature_name) => {
feature_err(
&tcx.sess,
lang_feature_name,
item.span(),
format!("the target feature `{feature}` is currently unstable"),
)
.emit();
}
Stability::Forbidden { reason } => {
tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
span: item.span(),
feature,
reason,
});
}
}
// Only allow target features whose feature gates have been enabled
// and which are permitted to be toggled.
if let Err(reason) = stability.allow_toggle() {
tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
span: item.span(),
feature,
reason,
});
} else if let Some(nightly_feature) = stability.requires_nightly()
&& !rust_features.enabled(nightly_feature)
{
feature_err(
&tcx.sess,
nightly_feature,
item.span(),
format!("the target feature `{feature}` is currently unstable"),
)
.emit();
}
Some(Symbol::intern(feature))
}));
@ -156,18 +148,19 @@ pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers {
rust_target_features: |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
let target = &tcx.sess.target;
if tcx.sess.opts.actually_rustdoc {
// rustdoc needs to be able to document functions that use all the features, so
// whitelist them all
rustc_target::target_features::all_rust_features()
.map(|(a, b)| (a.to_string(), b))
.map(|(a, b)| (a.to_string(), b.compute_toggleability(target)))
.collect()
} else {
tcx.sess
.target
.rust_target_features()
.iter()
.map(|&(a, b, _)| (a.to_string(), b))
.map(|&(a, b, _)| (a.to_string(), b.compute_toggleability(target)))
.collect()
}
},

View file

@ -45,7 +45,9 @@ pub trait CodegenBackend {
fn print(&self, _req: &PrintRequest, _out: &mut String, _sess: &Session) {}
fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec<Symbol> {
/// Returns the features that should be set in `cfg(target_features)`.
/// RUSTC_SPECIFIC_FEATURES should be skipped here, those are handled outside codegen.
fn target_features_cfg(&self, _sess: &Session, _allow_unstable: bool) -> Vec<Symbol> {
vec![]
}

View file

@ -573,12 +573,27 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
) => {}
Rvalue::ShallowInitBox(_, _) => {}
Rvalue::UnaryOp(_, operand) => {
Rvalue::UnaryOp(op, operand) => {
let ty = operand.ty(self.body, self.tcx);
if is_int_bool_float_or_char(ty) {
// Int, bool, float, and char operations are fine.
} else {
span_bug!(self.span, "non-primitive type in `Rvalue::UnaryOp`: {:?}", ty);
match op {
UnOp::Not | UnOp::Neg => {
if is_int_bool_float_or_char(ty) {
// Int, bool, float, and char operations are fine.
} else {
span_bug!(
self.span,
"non-primitive type in `Rvalue::UnaryOp{op:?}`: {ty:?}",
);
}
}
UnOp::PtrMetadata => {
if !ty.is_ref() && !ty.is_unsafe_ptr() {
span_bug!(
self.span,
"non-pointer type in `Rvalue::UnaryOp({op:?})`: {ty:?}",
);
}
}
}
}

View file

@ -329,7 +329,7 @@ where
self.transfer_function(state).initialize_state();
}
fn apply_statement_effect(
fn apply_primary_statement_effect(
&mut self,
state: &mut Self::Domain,
statement: &mir::Statement<'tcx>,
@ -338,7 +338,7 @@ where
self.transfer_function(state).visit_statement(statement, location);
}
fn apply_terminator_effect<'mir>(
fn apply_primary_terminator_effect<'mir>(
&mut self,
state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,

View file

@ -4,27 +4,29 @@ use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::query::Providers;
use rustc_middle::ty::TyCtxt;
pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
fn parent_impl_constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness {
let parent_id = tcx.local_parent(def_id);
matches!(tcx.def_kind(parent_id), DefKind::Impl { .. })
&& tcx.constness(parent_id) == hir::Constness::Const
if matches!(tcx.def_kind(parent_id), DefKind::Impl { .. })
&& let Some(header) = tcx.impl_trait_header(parent_id)
{
header.constness
} else {
hir::Constness::NotConst
}
}
/// Checks whether an item is considered to be `const`. If it is a constructor, anonymous const,
/// const block, const item or associated const, it is const. If it is a trait impl/function,
/// Checks whether an item is considered to be `const`. If it is a constructor, it is const.
/// If it is an assoc method or function,
/// return if it has a `const` modifier. If it is an intrinsic, report whether said intrinsic
/// has a `rustc_const_{un,}stable` attribute. Otherwise, return `Constness::NotConst`.
/// has a `rustc_const_{un,}stable` attribute. Otherwise, panic.
fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness {
let node = tcx.hir_node_by_def_id(def_id);
match node {
hir::Node::Ctor(_)
| hir::Node::AnonConst(_)
| hir::Node::ConstBlock(_)
hir::Node::Ctor(hir::VariantData::Tuple(..))
| hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => {
hir::Constness::Const
}
hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.constness,
hir::Node::ForeignItem(_) => {
// Foreign items cannot be evaluated at compile-time.
hir::Constness::NotConst
@ -38,10 +40,12 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness {
// If the function itself is not annotated with `const`, it may still be a `const fn`
// if it resides in a const trait impl.
let is_const = is_parent_const_impl_raw(tcx, def_id);
if is_const { hir::Constness::Const } else { hir::Constness::NotConst }
parent_impl_constness(tcx, def_id)
} else {
hir::Constness::NotConst
tcx.dcx().span_bug(
tcx.def_span(def_id),
format!("should not be requesting the constness of items that can't be const: {node:#?}: {:?}", tcx.def_kind(def_id))
)
}
}
}

View file

@ -249,10 +249,9 @@ impl<'tcx> CompileTimeInterpCx<'tcx> {
} else if self.tcx.is_lang_item(def_id, LangItem::PanicFmt) {
// For panic_fmt, call const_panic_fmt instead.
let const_def_id = self.tcx.require_lang_item(LangItem::ConstPanicFmt, None);
// FIXME(@lcnr): why does this use an empty env if we've got a `param_env` right here.
let new_instance = ty::Instance::expect_resolve(
*self.tcx,
ty::TypingEnv::fully_monomorphized(),
self.typing_env(),
const_def_id,
instance.args,
self.cur_span(),

View file

@ -540,10 +540,14 @@ pub trait Machine<'tcx>: Sized {
interp_ok(ReturnAction::Normal)
}
/// Called immediately after an "immediate" local variable is read
/// Called immediately after an "immediate" local variable is read in a given frame
/// (i.e., this is called for reads that do not end up accessing addressable memory).
#[inline(always)]
fn after_local_read(_ecx: &InterpCx<'tcx, Self>, _local: mir::Local) -> InterpResult<'tcx> {
fn after_local_read(
_ecx: &InterpCx<'tcx, Self>,
_frame: &Frame<'tcx, Self::Provenance, Self::FrameExtra>,
_local: mir::Local,
) -> InterpResult<'tcx> {
interp_ok(())
}

View file

@ -15,9 +15,9 @@ use rustc_middle::{bug, mir, span_bug, ty};
use tracing::trace;
use super::{
CtfeProvenance, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, OffsetMode,
PlaceTy, Pointer, Projectable, Provenance, Scalar, alloc_range, err_ub, from_known_layout,
interp_ok, mir_assign_valid_types, throw_ub,
CtfeProvenance, Frame, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta,
OffsetMode, PlaceTy, Pointer, Projectable, Provenance, Scalar, alloc_range, err_ub,
from_known_layout, interp_ok, mir_assign_valid_types, throw_ub,
};
/// An `Immediate` represents a single immediate self-contained Rust value.
@ -297,6 +297,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
#[inline]
pub fn from_bool(b: bool, tcx: TyCtxt<'tcx>) -> Self {
// Can use any typing env, since `bool` is always monomorphic.
let layout = tcx
.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(tcx.types.bool))
.unwrap();
@ -305,17 +306,18 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
#[inline]
pub fn from_ordering(c: std::cmp::Ordering, tcx: TyCtxt<'tcx>) -> Self {
// Can use any typing env, since `Ordering` is always monomorphic.
let ty = tcx.ty_ordering_enum(None);
let layout =
tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)).unwrap();
Self::from_scalar(Scalar::from_i8(c as i8), layout)
}
pub fn from_pair(a: Self, b: Self, tcx: TyCtxt<'tcx>) -> Self {
let layout = tcx
pub fn from_pair(a: Self, b: Self, cx: &(impl HasTypingEnv<'tcx> + HasTyCtxt<'tcx>)) -> Self {
let layout = cx
.tcx()
.layout_of(
ty::TypingEnv::fully_monomorphized()
.as_query_input(Ty::new_tup(tcx, &[a.layout.ty, b.layout.ty])),
cx.typing_env().as_query_input(Ty::new_tup(cx.tcx(), &[a.layout.ty, b.layout.ty])),
)
.unwrap();
Self::from_scalar_pair(a.to_scalar(), b.to_scalar(), layout)
@ -706,23 +708,32 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
interp_ok(str)
}
/// Read from a local of the current frame.
/// Will not access memory, instead an indirect `Operand` is returned.
///
/// This is public because it is used by [priroda](https://github.com/oli-obk/priroda) to get an
/// OpTy from a local.
/// Read from a local of the current frame. Convenience method for [`InterpCx::local_at_frame_to_op`].
pub fn local_to_op(
&self,
local: mir::Local,
layout: Option<TyAndLayout<'tcx>>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
let frame = self.frame();
self.local_at_frame_to_op(self.frame(), local, layout)
}
/// Read from a local of a given frame.
/// Will not access memory, instead an indirect `Operand` is returned.
///
/// This is public because it is used by [Aquascope](https://github.com/cognitive-engineering-lab/aquascope/)
/// to get an OpTy from a local.
pub fn local_at_frame_to_op(
&self,
frame: &Frame<'tcx, M::Provenance, M::FrameExtra>,
local: mir::Local,
layout: Option<TyAndLayout<'tcx>>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
let layout = self.layout_of_local(frame, local, layout)?;
let op = *frame.locals[local].access()?;
if matches!(op, Operand::Immediate(_)) {
assert!(!layout.is_unsized());
}
M::after_local_read(self, local)?;
M::after_local_read(self, frame, local)?;
interp_ok(OpTy { op, layout })
}

View file

@ -222,7 +222,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let res = ImmTy::from_scalar_int(result, left.layout);
return interp_ok(if with_overflow {
let overflow = ImmTy::from_bool(overflow, *self.tcx);
ImmTy::from_pair(res, overflow, *self.tcx)
ImmTy::from_pair(res, overflow, self)
} else {
res
});
@ -279,7 +279,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let res = ImmTy::from_scalar_int(result, left.layout);
if with_overflow {
let overflow = ImmTy::from_bool(overflow, *self.tcx);
ImmTy::from_pair(res, overflow, *self.tcx)
ImmTy::from_pair(res, overflow, self)
} else {
res
}

View file

@ -584,8 +584,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
interp_ok(())
}
/// This is public because it is used by [Aquascope](https://github.com/cognitive-engineering-lab/aquascope/)
/// to analyze all the locals in a stack frame.
#[inline(always)]
pub(super) fn layout_of_local(
pub fn layout_of_local(
&self,
frame: &Frame<'tcx, M::Provenance, M::FrameExtra>,
local: mir::Local,

View file

@ -9,6 +9,7 @@ use rustc_middle::ty::layout::FnAbiOf;
use rustc_middle::ty::{self, Instance, Ty};
use rustc_middle::{bug, mir, span_bug};
use rustc_span::source_map::Spanned;
use rustc_span::{DesugaringKind, Span};
use rustc_target::callconv::FnAbi;
use tracing::{info, instrument, trace};
@ -80,7 +81,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
use rustc_middle::mir::StatementKind::*;
match &stmt.kind {
Assign(box (place, rvalue)) => self.eval_rvalue_into_place(rvalue, *place)?,
Assign(box (place, rvalue)) => {
self.eval_rvalue_into_place(rvalue, *place, stmt.source_info.span)?
}
SetDiscriminant { place, variant_index } => {
let dest = self.eval_place(**place)?;
@ -159,6 +162,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
&mut self,
rvalue: &mir::Rvalue<'tcx>,
place: mir::Place<'tcx>,
span: Span,
) -> InterpResult<'tcx> {
let dest = self.eval_place(place)?;
// FIXME: ensure some kind of non-aliasing between LHS and RHS?
@ -250,8 +254,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let src = self.eval_place(place)?;
let place = self.force_allocation(&src)?;
let mut val = ImmTy::from_immediate(place.to_ref(self), dest.layout);
if !place_base_raw {
if !place_base_raw
&& span.desugaring_kind() != Some(DesugaringKind::IndexBoundsCheckReborrow)
{
// If this was not already raw, it needs retagging.
// As a special hack, we exclude the desugared `PtrMetadata(&raw const *_n)`
// from indexing. (Really we should not do any retag on `&raw` but that does not
// currently work with Stacked Borrows.)
val = M::retag_ptr_value(self, mir::RetagKind::Raw, &val)?;
}
self.write_immediate(*val, &dest)?;

View file

@ -1,6 +1,6 @@
use std::sync::{LazyLock, OnceLock};
pub use jobserver_crate::Client;
pub use jobserver_crate::{Acquired, Client, HelperThread};
use jobserver_crate::{FromEnv, FromEnvErrorKind};
// We can only call `from_env_ext` once per process

View file

@ -45,7 +45,7 @@ use rustc_errors::registry::Registry;
use rustc_errors::{ColorConfig, DiagCtxt, ErrCode, FatalError, PResult, markdown};
use rustc_feature::find_gated_cfg;
use rustc_interface::util::{self, get_codegen_backend};
use rustc_interface::{Linker, Queries, interface, passes};
use rustc_interface::{Linker, interface, passes};
use rustc_lint::unerased_lint_store;
use rustc_metadata::creader::MetadataLoader;
use rustc_metadata::locator;
@ -158,13 +158,10 @@ pub trait Callbacks {
/// Called after parsing the crate root. Submodules are not yet parsed when
/// this callback is called. Return value instructs the compiler whether to
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
#[deprecated = "This callback will likely be removed or stop giving access \
to the TyCtxt in the future. Use either the after_expansion \
or the after_analysis callback instead."]
fn after_crate_root_parsing<'tcx>(
fn after_crate_root_parsing(
&mut self,
_compiler: &interface::Compiler,
_queries: &'tcx Queries<'tcx>,
_queries: &ast::Crate,
) -> Compilation {
Compilation::Continue
}
@ -173,7 +170,7 @@ pub trait Callbacks {
fn after_expansion<'tcx>(
&mut self,
_compiler: &interface::Compiler,
_queries: &'tcx Queries<'tcx>,
_tcx: TyCtxt<'tcx>,
) -> Compilation {
Compilation::Continue
}
@ -350,6 +347,8 @@ fn run_compiler(
callbacks.config(&mut config);
let registered_lints = config.register_lints.is_some();
interface::run_compiler(config, |compiler| {
let sess = &compiler.sess;
let codegen_backend = &*compiler.codegen_backend;
@ -365,7 +364,7 @@ fn run_compiler(
// `--help`/`-Zhelp`/`-Chelp`. This is the earliest it can run, because
// it must happen after lints are registered, during session creation.
if sess.opts.describe_lints {
describe_lints(sess);
describe_lints(sess, registered_lints);
return early_exit();
}
@ -416,8 +415,9 @@ fn run_compiler(
return early_exit();
}
#[allow(deprecated)]
if callbacks.after_crate_root_parsing(compiler, queries) == Compilation::Stop {
if callbacks.after_crate_root_parsing(compiler, &*queries.parse().borrow())
== Compilation::Stop
{
return early_exit();
}
@ -425,18 +425,18 @@ fn run_compiler(
return early_exit();
}
// Make sure name resolution and macro expansion is run.
queries.global_ctxt().enter(|tcx| tcx.resolver_for_lowering());
if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir {
queries.global_ctxt().enter(|tcxt| dump_feature_usage_metrics(tcxt, metrics_dir));
}
if callbacks.after_expansion(compiler, queries) == Compilation::Stop {
return early_exit();
}
queries.global_ctxt().enter(|tcx| {
// Make sure name resolution and macro expansion is run.
let _ = tcx.resolver_for_lowering();
if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir {
dump_feature_usage_metrics(tcx, metrics_dir);
}
if callbacks.after_expansion(compiler, tcx) == Compilation::Stop {
return early_exit();
}
passes::write_dep_info(tcx);
if sess.opts.output_types.contains_key(&OutputType::DepInfo)
@ -982,7 +982,7 @@ the command line flag directly.
}
/// Write to stdout lint command options, together with a list of all available lints
pub fn describe_lints(sess: &Session) {
pub fn describe_lints(sess: &Session, registered_lints: bool) {
safe_println!(
"
Available lint options:
@ -1086,7 +1086,7 @@ Available lint options:
print_lint_groups(builtin_groups, true);
match (sess.registered_lints, loaded.len(), loaded_groups.len()) {
match (registered_lints, loaded.len(), loaded_groups.len()) {
(false, 0, _) | (false, _, 0) => {
safe_println!("Lint tools like Clippy can load additional lints and lint groups.");
}

View file

@ -3048,11 +3048,19 @@ impl FileWithAnnotatedLines {
// working correctly.
let middle = min(ann.line_start + 4, ann.line_end);
// We'll show up to 4 lines past the beginning of the multispan start.
// We will *not* include the tail of lines that are only whitespace.
// We will *not* include the tail of lines that are only whitespace, a comment or
// a bare delimiter.
let filter = |s: &str| {
let s = s.trim();
// Consider comments as empty, but don't consider docstrings to be empty.
!(s.starts_with("//") && !(s.starts_with("///") || s.starts_with("//!")))
// Consider lines with nothing but whitespace, a single delimiter as empty.
&& !["", "{", "}", "(", ")", "[", "]"].contains(&s)
};
let until = (ann.line_start..middle)
.rev()
.filter_map(|line| file.get_line(line - 1).map(|s| (line + 1, s)))
.find(|(_, s)| !s.trim().is_empty())
.find(|(_, s)| filter(s))
.map(|(line, _)| line)
.unwrap_or(ann.line_start);
for line in ann.line_start + 1..until {
@ -3060,7 +3068,8 @@ impl FileWithAnnotatedLines {
add_annotation_to_file(&mut output, Lrc::clone(&file), line, ann.as_line());
}
let line_end = ann.line_end - 1;
if middle < line_end {
let end_is_empty = file.get_line(line_end - 1).map_or(false, |s| !filter(&s));
if middle < line_end && !end_is_empty {
add_annotation_to_file(&mut output, Lrc::clone(&file), line_end, ann.as_line());
}
} else {

View file

@ -723,7 +723,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
item_inner.kind,
ItemKind::Mod(
_,
ModKind::Unloaded | ModKind::Loaded(_, Inline::No, _),
ModKind::Unloaded | ModKind::Loaded(_, Inline::No, _, _),
)
) =>
{
@ -889,7 +889,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
fn visit_item(&mut self, item: &'ast ast::Item) {
match &item.kind {
ItemKind::Mod(_, mod_kind)
if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _)) =>
if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _, _)) =>
{
feature_err(
self.sess,
@ -1195,7 +1195,7 @@ impl InvocationCollectorNode for P<ast::Item> {
let ecx = &mut collector.cx;
let (file_path, dir_path, dir_ownership) = match mod_kind {
ModKind::Loaded(_, inline, _) => {
ModKind::Loaded(_, inline, _, _) => {
// Inline `mod foo { ... }`, but we still need to push directories.
let (dir_path, dir_ownership) = mod_dir_path(
ecx.sess,
@ -1217,15 +1217,21 @@ impl InvocationCollectorNode for P<ast::Item> {
ModKind::Unloaded => {
// We have an outline `mod foo;` so we need to parse the file.
let old_attrs_len = attrs.len();
let ParsedExternalMod { items, spans, file_path, dir_path, dir_ownership } =
parse_external_mod(
ecx.sess,
ident,
span,
&ecx.current_expansion.module,
ecx.current_expansion.dir_ownership,
&mut attrs,
);
let ParsedExternalMod {
items,
spans,
file_path,
dir_path,
dir_ownership,
had_parse_error,
} = parse_external_mod(
ecx.sess,
ident,
span,
&ecx.current_expansion.module,
ecx.current_expansion.dir_ownership,
&mut attrs,
);
if let Some(lint_store) = ecx.lint_store {
lint_store.pre_expansion_lint(
@ -1239,7 +1245,7 @@ impl InvocationCollectorNode for P<ast::Item> {
);
}
*mod_kind = ModKind::Loaded(items, Inline::No, spans);
*mod_kind = ModKind::Loaded(items, Inline::No, spans, had_parse_error);
node.attrs = attrs;
if node.attrs.len() > old_attrs_len {
// If we loaded an out-of-line module and added some inner attributes,

View file

@ -37,6 +37,7 @@ pub(crate) struct ParsedExternalMod {
pub file_path: PathBuf,
pub dir_path: PathBuf,
pub dir_ownership: DirOwnership,
pub had_parse_error: Result<(), ErrorGuaranteed>,
}
pub enum ModError<'a> {
@ -74,14 +75,17 @@ pub(crate) fn parse_external_mod(
attrs.extend(inner_attrs);
(items, inner_span, mp.file_path)
};
// (1) ...instead, we return a dummy module.
let (items, spans, file_path) =
result.map_err(|err| err.report(sess, span)).unwrap_or_default();
let ((items, spans, file_path), had_parse_error) = match result {
Err(err) => (Default::default(), Err(err.report(sess, span))),
Ok(result) => (result, Ok(())),
};
// Extract the directory path for submodules of the module.
let dir_path = file_path.parent().unwrap_or(&file_path).to_owned();
ParsedExternalMod { items, spans, file_path, dir_path, dir_ownership }
ParsedExternalMod { items, spans, file_path, dir_path, dir_ownership, had_parse_error }
}
pub(crate) fn mod_dir_path(

View file

@ -126,9 +126,6 @@ declare_features! (
better implied higher-ranked implied bounds support"
)
),
/// Allows `impl Trait` in bindings (`let`, `const`, `static`).
(removed, impl_trait_in_bindings, "1.55.0", Some(63065),
Some("the implementation was not maintainable, the feature may get reintroduced once the current refactorings are done")),
(removed, import_shadowing, "1.0.0", None, None),
/// Allows in-band quantification of lifetime bindings (e.g., `fn foo(x: &'a u8) -> &'a u8`).
(removed, in_band_lifetimes, "1.23.0", Some(44524),

View file

@ -342,6 +342,7 @@ declare_features! (
(unstable, sse4a_target_feature, "1.27.0", Some(44839)),
(unstable, tbm_target_feature, "1.27.0", Some(44839)),
(unstable, wasm_target_feature, "1.30.0", Some(44839)),
(unstable, x87_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)),
// !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!
// Features are listed in alphabetical order. Tidy will fail if you don't keep it this way.
// !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!
@ -516,6 +517,8 @@ declare_features! (
(unstable, if_let_guard, "1.47.0", Some(51114)),
/// Allows `impl Trait` to be used inside associated types (RFC 2515).
(unstable, impl_trait_in_assoc_type, "1.70.0", Some(63063)),
/// Allows `impl Trait` in bindings (`let`).
(unstable, impl_trait_in_bindings, "1.64.0", Some(63065)),
/// Allows `impl Trait` as output type in `Fn` traits in return position of functions.
(unstable, impl_trait_in_fn_trait_return, "1.64.0", Some(99697)),
/// Allows associated types in inherent impls.
@ -635,6 +638,8 @@ declare_features! (
/// Allows creation of instances of a struct by moving fields that have
/// not changed from prior instances of the same struct (RFC #2528)
(unstable, type_changing_struct_update, "1.58.0", Some(86555)),
/// Allows using `unsafe<'a> &'a T` unsafe binder types.
(incomplete, unsafe_binders, "CURRENT_RUSTC_VERSION", Some(130516)),
/// Allows declaring fields `unsafe`.
(incomplete, unsafe_fields, "CURRENT_RUSTC_VERSION", Some(132922)),
/// Allows const generic parameters to be defined with types that

View file

@ -8,7 +8,7 @@ use rustc_ast::{
};
pub use rustc_ast::{
BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, ByRef, CaptureBy,
ImplPolarity, IsAuto, Movability, Mutability, UnOp,
ImplPolarity, IsAuto, Movability, Mutability, UnOp, UnsafeBinderCastKind,
};
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::sorted_map::SortedMap;
@ -1740,6 +1740,7 @@ impl Expr<'_> {
| ExprKind::Struct(..)
| ExprKind::Tup(_)
| ExprKind::Type(..)
| ExprKind::UnsafeBinderCast(..)
| ExprKind::Err(_) => ExprPrecedence::Unambiguous,
ExprKind::DropTemps(ref expr, ..) => expr.precedence(),
@ -1769,6 +1770,9 @@ impl Expr<'_> {
// https://github.com/rust-lang/rfcs/blob/master/text/0803-type-ascription.md#type-ascription-and-temporaries
ExprKind::Type(ref e, _) => e.is_place_expr(allow_projections_from),
// Unsafe binder cast preserves place-ness of the sub-expression.
ExprKind::UnsafeBinderCast(_, e, _) => e.is_place_expr(allow_projections_from),
ExprKind::Unary(UnOp::Deref, _) => true,
ExprKind::Field(ref base, _) | ExprKind::Index(ref base, _, _) => {
@ -1850,7 +1854,8 @@ impl Expr<'_> {
| ExprKind::Field(base, _)
| ExprKind::Index(base, _, _)
| ExprKind::AddrOf(.., base)
| ExprKind::Cast(base, _) => {
| ExprKind::Cast(base, _)
| ExprKind::UnsafeBinderCast(_, base, _) => {
// This isn't exactly true for `Index` and all `Unary`, but we are using this
// method exclusively for diagnostics and there's a *cultural* pressure against
// them being used only for its side-effects.
@ -2020,6 +2025,22 @@ pub fn is_range_literal(expr: &Expr<'_>) -> bool {
}
}
/// Checks if the specified expression needs parentheses for prefix
/// or postfix suggestions to be valid.
/// For example, `a + b` requires parentheses to suggest `&(a + b)`,
/// but just `a` does not.
/// Similarly, `(a + b).c()` also requires parentheses.
/// This should not be used for other types of suggestions.
pub fn expr_needs_parens(expr: &Expr<'_>) -> bool {
match expr.kind {
// parenthesize if needed (Issue #46756)
ExprKind::Cast(_, _) | ExprKind::Binary(_, _, _) => true,
// parenthesize borrows of range literals (Issue #54505)
_ if is_range_literal(expr) => true,
_ => false,
}
}
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub enum ExprKind<'hir> {
/// Allow anonymous constants from an inline `const` block
@ -2144,6 +2165,10 @@ pub enum ExprKind<'hir> {
/// A suspension point for coroutines (i.e., `yield <expr>`).
Yield(&'hir Expr<'hir>, YieldSource),
/// Operators which can be used to interconvert `unsafe` binder types.
/// e.g. `unsafe<'a> &'a i32` <=> `&i32`.
UnsafeBinderCast(UnsafeBinderCastKind, &'hir Expr<'hir>, Option<&'hir Ty<'hir>>),
/// A placeholder for an expression that wasn't syntactically well formed in some way.
Err(rustc_span::ErrorGuaranteed),
}
@ -2780,6 +2805,12 @@ pub struct BareFnTy<'hir> {
pub param_names: &'hir [Ident],
}
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct UnsafeBinderTy<'hir> {
pub generic_params: &'hir [GenericParam<'hir>],
pub inner_ty: &'hir Ty<'hir>,
}
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct OpaqueTy<'hir> {
pub hir_id: HirId,
@ -2878,6 +2909,8 @@ pub enum TyKind<'hir> {
Ref(&'hir Lifetime, MutTy<'hir>),
/// A bare function (e.g., `fn(usize) -> bool`).
BareFn(&'hir BareFnTy<'hir>),
/// An unsafe binder type (e.g. `unsafe<'a> Foo<'a>`).
UnsafeBinder(&'hir UnsafeBinderTy<'hir>),
/// The never type (`!`).
Never,
/// A tuple (`(A, B, C, D, ...)`).
@ -2889,6 +2922,8 @@ pub enum TyKind<'hir> {
Path(QPath<'hir>),
/// An opaque type definition itself. This is only used for `impl Trait`.
OpaqueDef(&'hir OpaqueTy<'hir>),
/// A trait ascription type, which is `impl Trait` within a local binding.
TraitAscription(GenericBounds<'hir>),
/// A trait object type `Bound1 + Bound2 + Bound3`
/// where `Bound` is a trait or a lifetime.
TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax),
@ -4042,9 +4077,7 @@ impl<'hir> Node<'hir> {
_ => None,
},
Node::TraitItem(ti) => match ti.kind {
TraitItemKind::Fn(ref sig, TraitFn::Provided(_)) => {
Some(FnKind::Method(ti.ident, sig))
}
TraitItemKind::Fn(ref sig, _) => Some(FnKind::Method(ti.ident, sig)),
_ => None,
},
Node::ImplItem(ii) => match ii.kind {

View file

@ -857,6 +857,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
ExprKind::Yield(ref subexpression, _) => {
try_visit!(visitor.visit_expr(subexpression));
}
ExprKind::UnsafeBinderCast(_kind, expr, ty) => {
try_visit!(visitor.visit_expr(expr));
visit_opt!(visitor, visit_ty, ty);
}
ExprKind::Lit(_) | ExprKind::Err(_) => {}
}
V::Result::output()
@ -886,12 +890,19 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul
walk_list!(visitor, visit_generic_param, function_declaration.generic_params);
try_visit!(visitor.visit_fn_decl(function_declaration.decl));
}
TyKind::UnsafeBinder(ref unsafe_binder) => {
walk_list!(visitor, visit_generic_param, unsafe_binder.generic_params);
try_visit!(visitor.visit_ty(unsafe_binder.inner_ty));
}
TyKind::Path(ref qpath) => {
try_visit!(visitor.visit_qpath(qpath, typ.hir_id, typ.span));
}
TyKind::OpaqueDef(opaque) => {
try_visit!(visitor.visit_opaque_ty(opaque));
}
TyKind::TraitAscription(bounds) => {
walk_list!(visitor, visit_param_bound, bounds);
}
TyKind::Array(ref ty, ref length) => {
try_visit!(visitor.visit_ty(ty));
try_visit!(visitor.visit_const_arg(length));

View file

@ -246,6 +246,18 @@ hir_analysis_invalid_receiver_ty = invalid `self` parameter type: `{$receiver_ty
hir_analysis_invalid_receiver_ty_help =
consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box<Self>`, `self: Rc<Self>`, or `self: Arc<Self>`
hir_analysis_invalid_receiver_ty_help_no_arbitrary_self_types =
consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`)
hir_analysis_invalid_receiver_ty_help_nonnull_note =
`NonNull` does not implement `Receiver` because it has methods that may shadow the referent; consider wrapping your `NonNull` in a newtype wrapper for which you implement `Receiver`
hir_analysis_invalid_receiver_ty_help_weak_note =
`Weak` does not implement `Receiver` because it has methods that may shadow the referent; consider wrapping your `Weak` in a newtype wrapper for which you implement `Receiver`
hir_analysis_invalid_receiver_ty_no_arbitrary_self_types = invalid `self` parameter type: `{$receiver_ty}`
.note = type of `self` must be `Self` or a type that dereferences to it
hir_analysis_invalid_union_field =
field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
.note = union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`

View file

@ -44,6 +44,7 @@ use {rustc_ast as ast, rustc_hir as hir};
use crate::autoderef::Autoderef;
use crate::collect::CollectItemTypesVisitor;
use crate::constrained_generic_params::{Parameter, identify_constrained_generic_params};
use crate::errors::InvalidReceiverTyHint;
use crate::{errors, fluent_generated as fluent};
pub(super) struct WfCheckingCtxt<'a, 'tcx> {
@ -1748,8 +1749,25 @@ fn check_method_receiver<'tcx>(
// Report error; would not have worked with `arbitrary_self_types[_pointers]`.
{
match receiver_validity_err {
ReceiverValidityError::DoesNotDeref if arbitrary_self_types_level.is_some() => {
let hint = match receiver_ty
.builtin_deref(false)
.unwrap_or(receiver_ty)
.ty_adt_def()
.and_then(|adt_def| tcx.get_diagnostic_name(adt_def.did()))
{
Some(sym::RcWeak | sym::ArcWeak) => Some(InvalidReceiverTyHint::Weak),
Some(sym::NonNull) => Some(InvalidReceiverTyHint::NonNull),
_ => None,
};
tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty, hint })
}
ReceiverValidityError::DoesNotDeref => {
tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty })
tcx.dcx().emit_err(errors::InvalidReceiverTyNoArbitrarySelfTypes {
span,
receiver_ty,
})
}
ReceiverValidityError::MethodGenericParamUsed => {
tcx.dcx().emit_err(errors::InvalidGenericReceiverTy { span, receiver_ty })

View file

@ -29,7 +29,7 @@ use rustc_errors::{
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, Visitor, walk_generics};
use rustc_hir::{self as hir, GenericParamKind, Node};
use rustc_hir::{self as hir, GenericParamKind, HirId, Node};
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::ObligationCause;
use rustc_middle::hir::nested_filter;
@ -201,12 +201,13 @@ pub(crate) fn placeholder_type_error_diag<'cx, 'tcx>(
placeholder_types.iter().map(|sp| (*sp, (*type_name).to_string())).collect();
if let Some(generics) = generics {
if let Some(arg) = params.iter().find(|arg| {
matches!(arg.name, hir::ParamName::Plain(Ident { name: kw::Underscore, .. }))
if let Some(span) = params.iter().find_map(|arg| match arg.name {
hir::ParamName::Plain(Ident { name: kw::Underscore, span }) => Some(span),
_ => None,
}) {
// Account for `_` already present in cases like `struct S<_>(_);` and suggest
// `struct S<T>(T);` instead of `struct S<_, T>(T);`.
sugg.push((arg.span, (*type_name).to_string()));
sugg.push((span, (*type_name).to_string()));
} else if let Some(span) = generics.span_for_param_suggestion() {
// Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`.
sugg.push((span, format!(", {type_name}")));
@ -436,6 +437,15 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
ty::Const::new_error_with_message(self.tcx(), span, "bad placeholder constant")
}
fn register_trait_ascription_bounds(
&self,
_: Vec<(ty::Clause<'tcx>, Span)>,
_: HirId,
span: Span,
) {
self.dcx().span_delayed_bug(span, "trait ascription type not allowed here");
}
fn probe_ty_param_bounds(
&self,
span: Span,
@ -1330,7 +1340,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
..
})
| Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), .. }) => {
infer_return_ty_for_fn_sig(sig, generics, def_id, &icx)
lower_fn_sig_recovering_infer_ret_ty(&icx, sig, generics, def_id)
}
ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => {
@ -1347,7 +1357,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
None,
)
} else {
infer_return_ty_for_fn_sig(sig, generics, def_id, &icx)
lower_fn_sig_recovering_infer_ret_ty(&icx, sig, generics, def_id)
}
}
@ -1397,99 +1407,108 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
ty::EarlyBinder::bind(output)
}
fn infer_return_ty_for_fn_sig<'tcx>(
sig: &hir::FnSig<'tcx>,
generics: &hir::Generics<'_>,
def_id: LocalDefId,
fn lower_fn_sig_recovering_infer_ret_ty<'tcx>(
icx: &ItemCtxt<'tcx>,
sig: &'tcx hir::FnSig<'tcx>,
generics: &'tcx hir::Generics<'tcx>,
def_id: LocalDefId,
) -> ty::PolyFnSig<'tcx> {
if let Some(infer_ret_ty) = sig.decl.output.get_infer_ret_ty() {
return recover_infer_ret_ty(icx, infer_ret_ty, generics, def_id);
}
icx.lowerer().lower_fn_ty(
icx.tcx().local_def_id_to_hir_id(def_id),
sig.header.safety,
sig.header.abi,
sig.decl,
Some(generics),
None,
)
}
fn recover_infer_ret_ty<'tcx>(
icx: &ItemCtxt<'tcx>,
infer_ret_ty: &'tcx hir::Ty<'tcx>,
generics: &'tcx hir::Generics<'tcx>,
def_id: LocalDefId,
) -> ty::PolyFnSig<'tcx> {
let tcx = icx.tcx;
let hir_id = tcx.local_def_id_to_hir_id(def_id);
match sig.decl.output.get_infer_ret_ty() {
Some(ty) => {
let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id];
// Typeck doesn't expect erased regions to be returned from `type_of`.
// This is a heuristic approach. If the scope has region parameters,
// we should change fn_sig's lifetime from `ReErased` to `ReError`,
// otherwise to `ReStatic`.
let has_region_params = generics.params.iter().any(|param| match param.kind {
GenericParamKind::Lifetime { .. } => true,
_ => false,
});
let fn_sig = fold_regions(tcx, fn_sig, |r, _| match *r {
ty::ReErased => {
if has_region_params {
ty::Region::new_error_with_message(
tcx,
DUMMY_SP,
"erased region is not allowed here in return type",
)
} else {
tcx.lifetimes.re_static
}
}
_ => r,
});
let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id];
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_ty(ty);
let mut diag = bad_placeholder(icx.lowerer(), visitor.0, "return type");
let ret_ty = fn_sig.output();
// Don't leak types into signatures unless they're nameable!
// For example, if a function returns itself, we don't want that
// recursive function definition to leak out into the fn sig.
let mut recovered_ret_ty = None;
if let Some(suggestable_ret_ty) = ret_ty.make_suggestable(tcx, false, None) {
diag.span_suggestion(
ty.span,
"replace with the correct return type",
suggestable_ret_ty,
Applicability::MachineApplicable,
);
recovered_ret_ty = Some(suggestable_ret_ty);
} else if let Some(sugg) = suggest_impl_trait(
&tcx.infer_ctxt().build(TypingMode::non_body_analysis()),
tcx.param_env(def_id),
ret_ty,
) {
diag.span_suggestion(
ty.span,
"replace with an appropriate return type",
sugg,
Applicability::MachineApplicable,
);
} else if ret_ty.is_closure() {
diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound");
// Typeck doesn't expect erased regions to be returned from `type_of`.
// This is a heuristic approach. If the scope has region parameters,
// we should change fn_sig's lifetime from `ReErased` to `ReError`,
// otherwise to `ReStatic`.
let has_region_params = generics.params.iter().any(|param| match param.kind {
GenericParamKind::Lifetime { .. } => true,
_ => false,
});
let fn_sig = fold_regions(tcx, fn_sig, |r, _| match *r {
ty::ReErased => {
if has_region_params {
ty::Region::new_error_with_message(
tcx,
DUMMY_SP,
"erased region is not allowed here in return type",
)
} else {
tcx.lifetimes.re_static
}
// Also note how `Fn` traits work just in case!
if ret_ty.is_closure() {
diag.note(
"for more information on `Fn` traits and closure types, see \
https://doc.rust-lang.org/book/ch13-01-closures.html",
);
}
let guar = diag.emit();
ty::Binder::dummy(tcx.mk_fn_sig(
fn_sig.inputs().iter().copied(),
recovered_ret_ty.unwrap_or_else(|| Ty::new_error(tcx, guar)),
fn_sig.c_variadic,
fn_sig.safety,
fn_sig.abi,
))
}
None => icx.lowerer().lower_fn_ty(
hir_id,
sig.header.safety,
sig.header.abi,
sig.decl,
Some(generics),
None,
),
_ => r,
});
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_ty(infer_ret_ty);
let mut diag = bad_placeholder(icx.lowerer(), visitor.0, "return type");
let ret_ty = fn_sig.output();
// Don't leak types into signatures unless they're nameable!
// For example, if a function returns itself, we don't want that
// recursive function definition to leak out into the fn sig.
let mut recovered_ret_ty = None;
if let Some(suggestable_ret_ty) = ret_ty.make_suggestable(tcx, false, None) {
diag.span_suggestion(
infer_ret_ty.span,
"replace with the correct return type",
suggestable_ret_ty,
Applicability::MachineApplicable,
);
recovered_ret_ty = Some(suggestable_ret_ty);
} else if let Some(sugg) = suggest_impl_trait(
&tcx.infer_ctxt().build(TypingMode::non_body_analysis()),
tcx.param_env(def_id),
ret_ty,
) {
diag.span_suggestion(
infer_ret_ty.span,
"replace with an appropriate return type",
sugg,
Applicability::MachineApplicable,
);
} else if ret_ty.is_closure() {
diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound");
}
// Also note how `Fn` traits work just in case!
if ret_ty.is_closure() {
diag.note(
"for more information on `Fn` traits and closure types, see \
https://doc.rust-lang.org/book/ch13-01-closures.html",
);
}
let guar = diag.emit();
ty::Binder::dummy(tcx.mk_fn_sig(
fn_sig.inputs().iter().copied(),
recovered_ret_ty.unwrap_or_else(|| Ty::new_error(tcx, guar)),
fn_sig.c_variadic,
fn_sig.safety,
fn_sig.abi,
))
}
pub fn suggest_impl_trait<'tcx>(
@ -1611,7 +1630,7 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::ImplTrai
impl_.of_trait.as_ref().map(|ast_trait_ref| {
let selfty = tcx.type_of(def_id).instantiate_identity();
check_impl_constness(tcx, tcx.is_const_trait_impl(def_id.to_def_id()), ast_trait_ref);
check_impl_constness(tcx, impl_.constness, ast_trait_ref);
let trait_ref = icx.lowerer().lower_impl_trait_ref(ast_trait_ref, selfty);
@ -1619,22 +1638,23 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::ImplTrai
trait_ref: ty::EarlyBinder::bind(trait_ref),
safety: impl_.safety,
polarity: polarity_of_impl(tcx, def_id, impl_, item.span),
constness: impl_.constness,
}
})
}
fn check_impl_constness(
tcx: TyCtxt<'_>,
is_const: bool,
constness: hir::Constness,
hir_trait_ref: &hir::TraitRef<'_>,
) -> Option<ErrorGuaranteed> {
if !is_const {
return None;
) {
if let hir::Constness::NotConst = constness {
return;
}
let trait_def_id = hir_trait_ref.trait_def_id()?;
let Some(trait_def_id) = hir_trait_ref.trait_def_id() else { return };
if tcx.is_const_trait(trait_def_id) {
return None;
return;
}
let trait_name = tcx.item_name(trait_def_id).to_string();
@ -1650,14 +1670,14 @@ fn check_impl_constness(
),
(false, _) | (_, false) => (None, ""),
};
Some(tcx.dcx().emit_err(errors::ConstImplForNonConstTrait {
tcx.dcx().emit_err(errors::ConstImplForNonConstTrait {
trait_ref_span: hir_trait_ref.path.span,
trait_name,
local_trait_span,
suggestion_pre,
marking: (),
adding: (),
}))
});
}
fn polarity_of_impl(

View file

@ -470,6 +470,12 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S
self.outer_index.shift_out(1);
res
}
hir::TyKind::UnsafeBinder(_) => {
self.outer_index.shift_in(1);
let res = intravisit::walk_ty(self, ty);
self.outer_index.shift_out(1);
res
}
_ => intravisit::walk_ty(self, ty),
}
}

View file

@ -781,6 +781,36 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
intravisit::walk_ty(this, ty);
});
}
hir::TyKind::UnsafeBinder(binder) => {
let (mut bound_vars, binders): (FxIndexMap<LocalDefId, ResolvedArg>, Vec<_>) =
binder
.generic_params
.iter()
.enumerate()
.map(|(late_bound_idx, param)| {
(
(param.def_id, ResolvedArg::late(late_bound_idx as u32, param)),
late_arg_as_bound_arg(self.tcx, param),
)
})
.unzip();
deny_non_region_late_bound(self.tcx, &mut bound_vars, "function pointer types");
self.record_late_bound_vars(ty.hir_id, binders);
let scope = Scope::Binder {
hir_id: ty.hir_id,
bound_vars,
s: self.scope,
scope_type: BinderScopeType::Normal,
where_bound_origin: None,
};
self.with(scope, |this| {
// a bare fn has no bounds, so everything
// contained within is scoped within its binder.
intravisit::walk_ty(this, ty);
});
}
hir::TyKind::TraitObject(bounds, lifetime, _) => {
debug!(?bounds, ?lifetime, "TraitObject");
let scope = Scope::TraitRefBoundary { s: self.scope };
@ -822,6 +852,21 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
};
self.with(scope, |this| this.visit_ty(mt.ty));
}
hir::TyKind::TraitAscription(bounds) => {
let scope = Scope::TraitRefBoundary { s: self.scope };
self.with(scope, |this| {
let scope = Scope::LateBoundary {
s: this.scope,
what: "`impl Trait` in binding",
deny_late_regions: true,
};
this.with(scope, |this| {
for bound in bounds {
this.visit_param_bound(bound);
}
})
});
}
_ => intravisit::walk_ty(self, ty),
}
}

View file

@ -1655,6 +1655,24 @@ pub(crate) struct NonConstRange {
pub span: Span,
}
#[derive(Subdiagnostic)]
pub(crate) enum InvalidReceiverTyHint {
#[note(hir_analysis_invalid_receiver_ty_help_weak_note)]
Weak,
#[note(hir_analysis_invalid_receiver_ty_help_nonnull_note)]
NonNull,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_invalid_receiver_ty_no_arbitrary_self_types, code = E0307)]
#[note]
#[help(hir_analysis_invalid_receiver_ty_help_no_arbitrary_self_types)]
pub(crate) struct InvalidReceiverTyNoArbitrarySelfTypes<'tcx> {
#[primary_span]
pub span: Span,
pub receiver_ty: Ty<'tcx>,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_invalid_receiver_ty, code = E0307)]
#[note]
@ -1663,6 +1681,8 @@ pub(crate) struct InvalidReceiverTy<'tcx> {
#[primary_span]
pub span: Span,
pub receiver_ty: Ty<'tcx>,
#[subdiagnostic]
pub hint: Option<InvalidReceiverTyHint>,
}
#[derive(Diagnostic)]

View file

@ -1,9 +1,8 @@
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_errors::codes::*;
use rustc_errors::struct_span_code_err;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS;
use rustc_middle::span_bug;
use rustc_middle::ty::fold::BottomUpFolder;
@ -11,7 +10,7 @@ use rustc_middle::ty::{
self, DynKind, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable,
TypeVisitableExt, Upcast,
};
use rustc_span::{ErrorGuaranteed, Span};
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility;
use rustc_trait_selection::traits::{self, hir_ty_lowering_dyn_compatibility_violations};
use rustc_type_ir::elaborate::ClauseWithSupertraitSpan;
@ -128,8 +127,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}
let mut associated_types: FxIndexMap<Span, FxIndexSet<DefId>> = FxIndexMap::default();
let mut needed_associated_types = FxIndexSet::default();
let principal_span = regular_traits.first().map_or(DUMMY_SP, |info| info.bottom().1);
let regular_traits_refs_spans = trait_bounds
.into_iter()
.filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()));
@ -145,13 +145,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let bound_predicate = pred.kind();
match bound_predicate.skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
let pred = bound_predicate.rebind(pred);
associated_types.entry(original_span).or_default().extend(
tcx.associated_items(pred.def_id())
// FIXME(negative_bounds): Handle this correctly...
let trait_ref =
tcx.anonymize_bound_vars(bound_predicate.rebind(pred.trait_ref));
needed_associated_types.extend(
tcx.associated_items(trait_ref.def_id())
.in_definition_order()
.filter(|item| item.kind == ty::AssocKind::Type)
.filter(|item| !item.is_impl_trait_in_trait())
.map(|item| item.def_id),
// If the associated type has a `where Self: Sized` bound,
// we do not need to constrain the associated type.
.filter(|item| !tcx.generics_require_sized_self(item.def_id))
.map(|item| (item.def_id, trait_ref)),
);
}
ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => {
@ -201,26 +206,25 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// So every `Projection` clause is an `Assoc = Foo` bound. `associated_types` contains all associated
// types's `DefId`, so the following loop removes all the `DefIds` of the associated types that have a
// corresponding `Projection` clause
for def_ids in associated_types.values_mut() {
for (projection_bound, span) in &projection_bounds {
let def_id = projection_bound.item_def_id();
def_ids.swap_remove(&def_id);
if tcx.generics_require_sized_self(def_id) {
tcx.emit_node_span_lint(
UNUSED_ASSOCIATED_TYPE_BOUNDS,
hir_id,
*span,
crate::errors::UnusedAssociatedTypeBounds { span: *span },
);
}
for (projection_bound, span) in &projection_bounds {
let def_id = projection_bound.item_def_id();
let trait_ref = tcx.anonymize_bound_vars(
projection_bound.map_bound(|p| p.projection_term.trait_ref(tcx)),
);
needed_associated_types.swap_remove(&(def_id, trait_ref));
if tcx.generics_require_sized_self(def_id) {
tcx.emit_node_span_lint(
UNUSED_ASSOCIATED_TYPE_BOUNDS,
hir_id,
*span,
crate::errors::UnusedAssociatedTypeBounds { span: *span },
);
}
// If the associated type has a `where Self: Sized` bound, we do not need to constrain the associated
// type in the `dyn Trait`.
def_ids.retain(|def_id| !tcx.generics_require_sized_self(def_id));
}
if let Err(guar) = self.check_for_required_assoc_tys(
associated_types,
principal_span,
needed_associated_types,
potential_assoc_types,
hir_trait_bounds,
) {

View file

@ -9,7 +9,6 @@ use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_middle::bug;
use rustc_middle::query::Key;
use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _};
use rustc_middle::ty::{
self, AdtDef, Binder, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeVisitableExt,
@ -722,51 +721,42 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
/// emit a generic note suggesting using a `where` clause to constraint instead.
pub(crate) fn check_for_required_assoc_tys(
&self,
associated_types: FxIndexMap<Span, FxIndexSet<DefId>>,
principal_span: Span,
missing_assoc_types: FxIndexSet<(DefId, ty::PolyTraitRef<'tcx>)>,
potential_assoc_types: Vec<usize>,
trait_bounds: &[hir::PolyTraitRef<'_>],
) -> Result<(), ErrorGuaranteed> {
if associated_types.values().all(|v| v.is_empty()) {
if missing_assoc_types.is_empty() {
return Ok(());
}
let tcx = self.tcx();
// FIXME: Marked `mut` so that we can replace the spans further below with a more
// appropriate one, but this should be handled earlier in the span assignment.
let associated_types: FxIndexMap<Span, Vec<_>> = associated_types
// FIXME: This logic needs some more care w.r.t handling of conflicts
let missing_assoc_types: Vec<_> = missing_assoc_types
.into_iter()
.map(|(span, def_ids)| {
(span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect())
})
.map(|(def_id, trait_ref)| (tcx.associated_item(def_id), trait_ref))
.collect();
let mut names: FxIndexMap<String, Vec<Symbol>> = Default::default();
let mut names: FxIndexMap<_, Vec<Symbol>> = Default::default();
let mut names_len = 0;
// Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and
// `issue-22560.rs`.
let mut trait_bound_spans: Vec<Span> = vec![];
let mut dyn_compatibility_violations = Ok(());
for (span, items) in &associated_types {
if !items.is_empty() {
trait_bound_spans.push(*span);
}
for assoc_item in items {
let trait_def_id = assoc_item.container_id(tcx);
names.entry(tcx.def_path_str(trait_def_id)).or_default().push(assoc_item.name);
names_len += 1;
for (assoc_item, trait_ref) in &missing_assoc_types {
names.entry(trait_ref).or_default().push(assoc_item.name);
names_len += 1;
let violations =
dyn_compatibility_violations_for_assoc_item(tcx, trait_def_id, *assoc_item);
if !violations.is_empty() {
dyn_compatibility_violations = Err(report_dyn_incompatibility(
tcx,
*span,
None,
trait_def_id,
&violations,
)
.emit());
}
let violations =
dyn_compatibility_violations_for_assoc_item(tcx, trait_ref.def_id(), *assoc_item);
if !violations.is_empty() {
dyn_compatibility_violations = Err(report_dyn_incompatibility(
tcx,
principal_span,
None,
trait_ref.def_id(),
&violations,
)
.emit());
}
}
@ -814,6 +804,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.into_iter()
.map(|(trait_, mut assocs)| {
assocs.sort();
let trait_ = trait_.print_trait_sugared();
format!("{} in `{trait_}`", match &assocs[..] {
[] => String::new(),
[only] => format!("`{only}`"),
@ -827,10 +818,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
names.sort();
let names = names.join(", ");
trait_bound_spans.sort();
let mut err = struct_span_code_err!(
self.dcx(),
trait_bound_spans,
principal_span,
E0191,
"the value of the associated type{} {} must be specified",
pluralize!(names_len),
@ -840,81 +830,83 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let mut types_count = 0;
let mut where_constraints = vec![];
let mut already_has_generics_args_suggestion = false;
for (span, assoc_items) in &associated_types {
let mut names: UnordMap<_, usize> = Default::default();
for item in assoc_items {
types_count += 1;
*names.entry(item.name).or_insert(0) += 1;
}
let mut dupes = false;
let mut shadows = false;
for item in assoc_items {
let prefix = if names[&item.name] > 1 {
let trait_def_id = item.container_id(tcx);
dupes = true;
format!("{}::", tcx.def_path_str(trait_def_id))
} else if bound_names.get(&item.name).is_some_and(|x| x != &item) {
let trait_def_id = item.container_id(tcx);
shadows = true;
format!("{}::", tcx.def_path_str(trait_def_id))
} else {
String::new()
};
let mut is_shadowed = false;
let mut names: UnordMap<_, usize> = Default::default();
for (item, _) in &missing_assoc_types {
types_count += 1;
*names.entry(item.name).or_insert(0) += 1;
}
let mut dupes = false;
let mut shadows = false;
for (item, trait_ref) in &missing_assoc_types {
let prefix = if names[&item.name] > 1 {
let trait_def_id = trait_ref.def_id();
dupes = true;
format!("{}::", tcx.def_path_str(trait_def_id))
} else if bound_names.get(&item.name).is_some_and(|x| *x != item) {
let trait_def_id = trait_ref.def_id();
shadows = true;
format!("{}::", tcx.def_path_str(trait_def_id))
} else {
String::new()
};
if let Some(assoc_item) = bound_names.get(&item.name)
&& assoc_item != &item
{
is_shadowed = true;
let mut is_shadowed = false;
let rename_message =
if assoc_item.def_id.is_local() { ", consider renaming it" } else { "" };
err.span_label(
tcx.def_span(assoc_item.def_id),
format!("`{}{}` shadowed here{}", prefix, item.name, rename_message),
);
}
let rename_message = if is_shadowed { ", consider renaming it" } else { "" };
if let Some(sp) = tcx.hir().span_if_local(item.def_id) {
err.span_label(
sp,
format!("`{}{}` defined here{}", prefix, item.name, rename_message),
);
}
}
if potential_assoc_types.len() == assoc_items.len() {
// When the amount of missing associated types equals the number of
// extra type arguments present. A suggesting to replace the generic args with
// associated types is already emitted.
already_has_generics_args_suggestion = true;
} else if let (Ok(snippet), false, false) =
(tcx.sess.source_map().span_to_snippet(*span), dupes, shadows)
if let Some(assoc_item) = bound_names.get(&item.name)
&& *assoc_item != item
{
let types: Vec<_> =
assoc_items.iter().map(|item| format!("{} = Type", item.name)).collect();
let code = if snippet.ends_with('>') {
// The user wrote `Trait<'a>` or similar and we don't have a type we can
// suggest, but at least we can clue them to the correct syntax
// `Trait<'a, Item = Type>` while accounting for the `<'a>` in the
// suggestion.
format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", "))
} else if in_expr_or_pat {
// The user wrote `Iterator`, so we don't have a type we can suggest, but at
// least we can clue them to the correct syntax `Iterator::<Item = Type>`.
format!("{}::<{}>", snippet, types.join(", "))
} else {
// The user wrote `Iterator`, so we don't have a type we can suggest, but at
// least we can clue them to the correct syntax `Iterator<Item = Type>`.
format!("{}<{}>", snippet, types.join(", "))
};
suggestions.push((*span, code));
} else if dupes {
where_constraints.push(*span);
is_shadowed = true;
let rename_message =
if assoc_item.def_id.is_local() { ", consider renaming it" } else { "" };
err.span_label(
tcx.def_span(assoc_item.def_id),
format!("`{}{}` shadowed here{}", prefix, item.name, rename_message),
);
}
let rename_message = if is_shadowed { ", consider renaming it" } else { "" };
if let Some(sp) = tcx.hir().span_if_local(item.def_id) {
err.span_label(
sp,
format!("`{}{}` defined here{}", prefix, item.name, rename_message),
);
}
}
if potential_assoc_types.len() == missing_assoc_types.len() {
// When the amount of missing associated types equals the number of
// extra type arguments present. A suggesting to replace the generic args with
// associated types is already emitted.
already_has_generics_args_suggestion = true;
} else if let (Ok(snippet), false, false) =
(tcx.sess.source_map().span_to_snippet(principal_span), dupes, shadows)
{
let types: Vec<_> = missing_assoc_types
.iter()
.map(|(item, _)| format!("{} = Type", item.name))
.collect();
let code = if snippet.ends_with('>') {
// The user wrote `Trait<'a>` or similar and we don't have a type we can
// suggest, but at least we can clue them to the correct syntax
// `Trait<'a, Item = Type>` while accounting for the `<'a>` in the
// suggestion.
format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", "))
} else if in_expr_or_pat {
// The user wrote `Iterator`, so we don't have a type we can suggest, but at
// least we can clue them to the correct syntax `Iterator::<Item = Type>`.
format!("{}::<{}>", snippet, types.join(", "))
} else {
// The user wrote `Iterator`, so we don't have a type we can suggest, but at
// least we can clue them to the correct syntax `Iterator<Item = Type>`.
format!("{}<{}>", snippet, types.join(", "))
};
suggestions.push((principal_span, code));
} else if dupes {
where_constraints.push(principal_span);
}
let where_msg = "consider introducing a new type parameter, adding `where` constraints \
using the fully-qualified path to the associated types";
if !where_constraints.is_empty() && suggestions.is_empty() {
@ -925,32 +917,29 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
if suggestions.len() != 1 || already_has_generics_args_suggestion {
// We don't need this label if there's an inline suggestion, show otherwise.
for (span, assoc_items) in &associated_types {
let mut names: FxIndexMap<_, usize> = FxIndexMap::default();
for item in assoc_items {
types_count += 1;
*names.entry(item.name).or_insert(0) += 1;
}
let mut label = vec![];
for item in assoc_items {
let postfix = if names[&item.name] > 1 {
let trait_def_id = item.container_id(tcx);
format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id))
} else {
String::new()
};
label.push(format!("`{}`{}", item.name, postfix));
}
if !label.is_empty() {
err.span_label(
*span,
format!(
"associated type{} {} must be specified",
pluralize!(label.len()),
label.join(", "),
),
);
}
let mut names: FxIndexMap<_, usize> = FxIndexMap::default();
for (item, _) in &missing_assoc_types {
types_count += 1;
*names.entry(item.name).or_insert(0) += 1;
}
let mut label = vec![];
for (item, trait_ref) in &missing_assoc_types {
let postfix = if names[&item.name] > 1 {
format!(" (from trait `{}`)", trait_ref.print_trait_sugared())
} else {
String::new()
};
label.push(format!("`{}`{}", item.name, postfix));
}
if !label.is_empty() {
err.span_label(
principal_span,
format!(
"associated type{} {} must be specified",
pluralize!(label.len()),
label.join(", "),
),
);
}
}
suggestions.sort_by_key(|&(span, _)| span);
@ -1007,8 +996,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
)),
..
}) = node
&& let Some(ty_def_id) = qself_ty.ty_def_id()
&& let [inherent_impl] = tcx.inherent_impls(ty_def_id)
&& let Some(adt_def) = qself_ty.ty_adt_def()
&& let [inherent_impl] = tcx.inherent_impls(adt_def.did())
&& let name = format!("{ident2}_{ident3}")
&& let Some(ty::AssocItem { kind: ty::AssocKind::Fn, .. }) = tcx
.associated_items(inherent_impl)

View file

@ -123,6 +123,13 @@ pub trait HirTyLowerer<'tcx> {
/// Returns the const to use when a const is omitted.
fn ct_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Const<'tcx>;
fn register_trait_ascription_bounds(
&self,
bounds: Vec<(ty::Clause<'tcx>, Span)>,
hir_id: HirId,
span: Span,
);
/// Probe bounds in scope where the bounded type coincides with the given type parameter.
///
/// Rephrased, this returns bounds of the form `T: Trait`, where `T` is a type parameter
@ -2312,6 +2319,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
self.lower_fn_ty(hir_ty.hir_id, bf.safety, bf.abi, bf.decl, None, Some(hir_ty)),
)
}
hir::TyKind::UnsafeBinder(_binder) => {
let guar = self
.dcx()
.struct_span_err(hir_ty.span, "unsafe binders are not yet implemented")
.emit();
Ty::new_error(tcx, guar)
}
hir::TyKind::TraitObject(bounds, lifetime, repr) => {
if let Some(guar) = self.prohibit_or_lint_bare_trait_object_ty(hir_ty) {
// Don't continue with type analysis if the `dyn` keyword is missing
@ -2368,6 +2382,25 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
self.lower_opaque_ty(opaque_ty.def_id, in_trait)
}
hir::TyKind::TraitAscription(hir_bounds) => {
// Impl trait in bindings lower as an infer var with additional
// set of type bounds.
let self_ty = self.ty_infer(None, hir_ty.span);
let mut bounds = Bounds::default();
self.lower_bounds(
self_ty,
hir_bounds.iter(),
&mut bounds,
ty::List::empty(),
PredicateFilter::All,
);
self.register_trait_ascription_bounds(
bounds.clauses().collect(),
hir_ty.hir_id,
hir_ty.span,
);
self_ty
}
// If we encounter a type relative path with RTN generics, then it must have
// *not* gone through `lower_ty_maybe_return_type_notation`, and therefore
// it's certainly in an illegal position.

View file

@ -288,7 +288,13 @@ impl<'a> State<'a> {
hir::TyKind::BareFn(f) => {
self.print_ty_fn(f.abi, f.safety, f.decl, None, f.generic_params, f.param_names);
}
hir::TyKind::UnsafeBinder(unsafe_binder) => {
self.print_unsafe_binder(unsafe_binder);
}
hir::TyKind::OpaqueDef(..) => self.word("/*impl Trait*/"),
hir::TyKind::TraitAscription(bounds) => {
self.print_bounds("impl", bounds);
}
hir::TyKind::Path(ref qpath) => self.print_qpath(qpath, false),
hir::TyKind::TraitObject(bounds, lifetime, syntax) => {
if syntax == ast::TraitObjectSyntax::Dyn {
@ -339,6 +345,15 @@ impl<'a> State<'a> {
self.end()
}
fn print_unsafe_binder(&mut self, unsafe_binder: &hir::UnsafeBinderTy<'_>) {
self.ibox(INDENT_UNIT);
self.word("unsafe");
self.print_generic_params(unsafe_binder.generic_params);
self.nbsp();
self.print_type(unsafe_binder.inner_ty);
self.end();
}
fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) {
self.hardbreak_if_not_bol();
self.maybe_print_comment(item.span.lo());
@ -1530,6 +1545,19 @@ impl<'a> State<'a> {
self.word(")");
}
hir::ExprKind::UnsafeBinderCast(kind, expr, ty) => {
match kind {
hir::UnsafeBinderCastKind::Wrap => self.word("wrap_binder!("),
hir::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder!("),
}
self.print_expr(expr);
if let Some(ty) = ty {
self.word(",");
self.space();
self.print_type(ty);
}
self.word(")");
}
hir::ExprKind::Yield(expr, _) => {
self.word_space("yield");
self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump);

View file

@ -72,12 +72,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if self.try_structurally_resolve_type(expr.span, ty).is_never()
&& self.expr_guaranteed_to_constitute_read_for_never(expr)
{
if let Some(_) = self.typeck_results.borrow().adjustments().get(expr.hir_id) {
if let Some(adjustments) = self.typeck_results.borrow().adjustments().get(expr.hir_id) {
let reported = self.dcx().span_delayed_bug(
expr.span,
"expression with never type wound up being adjusted",
);
return Ty::new_error(self.tcx(), reported);
return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &adjustments[..] {
target.to_owned()
} else {
Ty::new_error(self.tcx(), reported)
};
}
let adj_ty = self.next_ty_var(expr.span);
@ -329,6 +334,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Assignment does call `drop_in_place`, though, but its safety
// requirements are not the same.
ExprKind::AddrOf(..) | hir::ExprKind::Field(..) => false,
// Place-preserving expressions only constitute reads if their
// parent expression constitutes a read.
ExprKind::Type(..) | ExprKind::UnsafeBinderCast(..) => {
self.expr_guaranteed_to_constitute_read_for_never(expr)
}
ExprKind::Assign(lhs, _, _) => {
// Only the LHS does not constitute a read
expr.hir_id != lhs.hir_id
@ -353,7 +365,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| ExprKind::Binary(_, _, _)
| ExprKind::Unary(_, _)
| ExprKind::Cast(_, _)
| ExprKind::Type(_, _)
| ExprKind::DropTemps(_)
| ExprKind::If(_, _, _)
| ExprKind::Closure(_)
@ -564,7 +575,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.check_expr_index(base, idx, expr, brackets_span)
}
ExprKind::Yield(value, _) => self.check_expr_yield(value, expr),
hir::ExprKind::Err(guar) => Ty::new_error(tcx, guar),
ExprKind::UnsafeBinderCast(kind, expr, ty) => {
self.check_expr_unsafe_binder_cast(kind, expr, ty, expected)
}
ExprKind::Err(guar) => Ty::new_error(tcx, guar),
}
}
@ -1634,6 +1648,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
fn check_expr_unsafe_binder_cast(
&self,
_kind: hir::UnsafeBinderCastKind,
expr: &'tcx hir::Expr<'tcx>,
_hir_ty: Option<&'tcx hir::Ty<'tcx>>,
_expected: Expectation<'tcx>,
) -> Ty<'tcx> {
let guar =
self.dcx().struct_span_err(expr.span, "unsafe binders are not yet implemented").emit();
Ty::new_error(self.tcx, guar)
}
fn check_expr_array(
&self,
args: &'tcx [hir::Expr<'tcx>],

View file

@ -341,6 +341,10 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
self.walk_expr(subexpr)?;
}
hir::ExprKind::UnsafeBinderCast(_, subexpr, _) => {
self.walk_expr(subexpr)?;
}
hir::ExprKind::Unary(hir::UnOp::Deref, base) => {
// *base
self.walk_expr(base)?;
@ -1360,7 +1364,10 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
self.cat_res(expr.hir_id, expr.span, expr_ty, res)
}
// both type ascription and unsafe binder casts don't affect
// the place-ness of the subexpression.
hir::ExprKind::Type(e, _) => self.cat_expr(e),
hir::ExprKind::UnsafeBinderCast(_, e, _) => self.cat_expr(e),
hir::ExprKind::AddrOf(..)
| hir::ExprKind::Call(..)

View file

@ -4,11 +4,11 @@ use std::slice;
use rustc_abi::FieldIdx;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::Visitor;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{ExprKind, GenericArg, HirId, Node, QPath};
use rustc_hir::{self as hir, ExprKind, GenericArg, HirId, Node, QPath, intravisit};
use rustc_hir_analysis::hir_ty_lowering::errors::GenericsArgsErrExtend;
use rustc_hir_analysis::hir_ty_lowering::generics::{
check_generic_arg_count_for_call, lower_generic_args,
@ -25,7 +25,7 @@ use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
use rustc_middle::ty::{
self, AdtKind, CanonicalUserType, GenericArgKind, GenericArgsRef, GenericParamDefKind,
IsIdentity, Ty, TyCtxt, UserArgs, UserSelfTy, UserType,
IsIdentity, Ty, TyCtxt, UserArgs, UserSelfTy,
};
use rustc_middle::{bug, span_bug};
use rustc_session::lint;
@ -216,11 +216,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
debug!("fcx {}", self.tag());
if Self::can_contain_user_lifetime_bounds((args, user_self_ty)) {
let canonicalized =
self.canonicalize_user_type_annotation(UserType::TypeOf(def_id, UserArgs {
args,
user_self_ty,
}));
let canonicalized = self.canonicalize_user_type_annotation(ty::UserType::new(
ty::UserTypeKind::TypeOf(def_id, UserArgs { args, user_self_ty }),
));
debug!(?canonicalized);
self.write_user_type_annotation(hir_id, canonicalized);
}
@ -462,13 +460,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
LoweredTy::from_raw(self, hir_ty.span, ty)
}
/// Walk a `hir_ty` and collect any clauses that may have come from a type
/// within the `hir_ty`. These clauses will be canonicalized with a user type
/// annotation so that we can enforce these bounds in borrowck, too.
pub(crate) fn collect_impl_trait_clauses_from_hir_ty(
&self,
hir_ty: &'tcx hir::Ty<'tcx>,
) -> ty::Clauses<'tcx> {
struct CollectClauses<'a, 'tcx> {
clauses: Vec<ty::Clause<'tcx>>,
fcx: &'a FnCtxt<'a, 'tcx>,
}
impl<'tcx> intravisit::Visitor<'tcx> for CollectClauses<'_, 'tcx> {
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
if let Some(clauses) = self.fcx.trait_ascriptions.borrow().get(&ty.hir_id.local_id)
{
self.clauses.extend(clauses.iter().cloned());
}
intravisit::walk_ty(self, ty)
}
}
let mut clauses = CollectClauses { clauses: vec![], fcx: self };
clauses.visit_ty(hir_ty);
self.tcx.mk_clauses(&clauses.clauses)
}
#[instrument(level = "debug", skip_all)]
pub(crate) fn lower_ty_saving_user_provided_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
pub(crate) fn lower_ty_saving_user_provided_ty(&self, hir_ty: &'tcx hir::Ty<'tcx>) -> Ty<'tcx> {
let ty = self.lower_ty(hir_ty);
debug!(?ty);
if Self::can_contain_user_lifetime_bounds(ty.raw) {
let c_ty = self.canonicalize_response(UserType::Ty(ty.raw));
let c_ty = self.canonicalize_response(ty::UserType::new(ty::UserTypeKind::Ty(ty.raw)));
debug!(?c_ty);
self.typeck_results.borrow_mut().user_provided_types_mut().insert(hir_ty.hir_id, c_ty);
}

View file

@ -10,10 +10,11 @@ use std::ops::Deref;
use hir::def_id::CRATE_DEF_ID;
use rustc_errors::DiagCtxtHandle;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{self as hir, HirId, ItemLocalMap};
use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, RegionInferReason};
use rustc_infer::infer;
use rustc_infer::traits::Obligation;
use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt};
use rustc_session::Session;
use rustc_span::symbol::Ident;
@ -114,6 +115,12 @@ pub(crate) struct FnCtxt<'a, 'tcx> {
pub(super) diverging_fallback_behavior: DivergingFallbackBehavior,
pub(super) diverging_block_behavior: DivergingBlockBehavior,
/// Clauses that we lowered as part of the `impl_trait_in_bindings` feature.
///
/// These are stored here so we may collect them when canonicalizing user
/// type ascriptions later.
pub(super) trait_ascriptions: RefCell<ItemLocalMap<Vec<ty::Clause<'tcx>>>>,
}
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@ -141,6 +148,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fallback_has_occurred: Cell::new(false),
diverging_fallback_behavior,
diverging_block_behavior,
trait_ascriptions: Default::default(),
}
}
@ -252,6 +260,30 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
}
}
fn register_trait_ascription_bounds(
&self,
bounds: Vec<(ty::Clause<'tcx>, Span)>,
hir_id: HirId,
_span: Span,
) {
for (clause, span) in bounds {
if clause.has_escaping_bound_vars() {
self.dcx().span_delayed_bug(span, "clause should have no escaping bound vars");
continue;
}
self.trait_ascriptions.borrow_mut().entry(hir_id.local_id).or_default().push(clause);
let clause = self.normalize(span, clause);
self.register_predicate(Obligation::new(
self.tcx,
self.misc(span),
self.param_env,
clause,
));
}
}
fn probe_ty_param_bounds(
&self,
_: Span,

View file

@ -10,7 +10,7 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::lang_items::LangItem;
use rustc_hir::{
Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId,
Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicateKind,
Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicateKind, expr_needs_parens,
};
use rustc_hir_analysis::collect::suggest_impl_trait;
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
@ -35,7 +35,6 @@ use tracing::{debug, instrument};
use super::FnCtxt;
use crate::fn_ctxt::rustc_span::BytePos;
use crate::hir::is_range_literal;
use crate::method::probe;
use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
use crate::{errors, fluent_generated as fluent};
@ -2648,7 +2647,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
let make_sugg = |expr: &Expr<'_>, span: Span, sugg: &str| {
if self.needs_parentheses(expr) {
if expr_needs_parens(expr) {
(
vec![
(span.shrink_to_lo(), format!("{prefix}{sugg}(")),
@ -2861,7 +2860,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return None;
}
if self.needs_parentheses(expr) {
if expr_needs_parens(expr) {
return Some((
vec![
(span, format!("{suggestion}(")),
@ -2902,16 +2901,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
false
}
fn needs_parentheses(&self, expr: &hir::Expr<'_>) -> bool {
match expr.kind {
// parenthesize if needed (Issue #46756)
hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true,
// parenthesize borrows of range literals (Issue #54505)
_ if is_range_literal(expr) => true,
_ => false,
}
}
pub(crate) fn suggest_cast(
&self,
err: &mut Diag<'_>,

View file

@ -2,7 +2,7 @@ use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{HirId, PatKind};
use rustc_infer::traits::ObligationCauseCode;
use rustc_middle::ty::{Ty, UserType};
use rustc_middle::ty::{self, Ty};
use rustc_span::Span;
use rustc_span::def_id::LocalDefId;
use tracing::debug;
@ -92,7 +92,12 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
Some(ref ty) => {
let o_ty = self.fcx.lower_ty(ty);
let c_ty = self.fcx.infcx.canonicalize_user_type_annotation(UserType::Ty(o_ty.raw));
let c_ty = self.fcx.infcx.canonicalize_user_type_annotation(
ty::UserType::new_with_bounds(
ty::UserTypeKind::Ty(o_ty.raw),
self.fcx.collect_impl_trait_clauses_from_hir_ty(ty),
),
);
debug!("visit_local: ty.hir_id={:?} o_ty={:?} c_ty={:?}", ty.hir_id, o_ty, c_ty);
self.fcx
.typeck_results

View file

@ -17,7 +17,6 @@ use rustc_middle::ty::adjustment::{
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::{
self, GenericArgs, GenericArgsRef, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt, UserArgs,
UserType,
};
use rustc_middle::{bug, span_bug};
use rustc_span::{DUMMY_SP, Span};
@ -491,9 +490,8 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
user_self_ty: None, // not relevant here
};
self.fcx.canonicalize_user_type_annotation(UserType::TypeOf(
pick.item.def_id,
user_args,
self.fcx.canonicalize_user_type_annotation(ty::UserType::new(
ty::UserTypeKind::TypeOf(pick.item.def_id, user_args),
))
});

View file

@ -10,8 +10,12 @@ use rustc_errors::{
};
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::pat_util::EnumerateAndAdjustIterator;
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, LangItem, Mutability, Pat, PatKind};
use rustc_hir::{
self as hir, BindingMode, ByRef, ExprKind, HirId, LangItem, Mutability, Pat, PatKind,
expr_needs_parens,
};
use rustc_infer::infer;
use rustc_middle::traits::PatternOriginExpr;
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
use rustc_middle::{bug, span_bug};
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
@ -94,10 +98,32 @@ struct PatInfo<'a, 'tcx> {
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn pattern_cause(&self, ti: &TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> {
// If origin_expr exists, then expected represents the type of origin_expr.
// If span also exists, then span == origin_expr.span (although it doesn't need to exist).
// In that case, we can peel away references from both and treat them
// as the same.
let origin_expr_info = ti.origin_expr.map(|mut cur_expr| {
let mut count = 0;
// cur_ty may have more layers of references than cur_expr.
// We can only make suggestions about cur_expr, however, so we'll
// use that as our condition for stopping.
while let ExprKind::AddrOf(.., inner) = &cur_expr.kind {
cur_expr = inner;
count += 1;
}
PatternOriginExpr {
peeled_span: cur_expr.span,
peeled_count: count,
peeled_prefix_suggestion_parentheses: expr_needs_parens(cur_expr),
}
});
let code = ObligationCauseCode::Pattern {
span: ti.span,
root_ty: ti.expected,
origin_expr: ti.origin_expr.is_some(),
origin_expr: origin_expr_info,
};
self.cause(cause_span, code)
}

View file

@ -476,7 +476,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
for (local_id, c_ty) in sorted_user_provided_types {
let hir_id = HirId { owner: common_hir_owner, local_id };
if let ty::UserType::TypeOf(_, user_args) = c_ty.value {
if let ty::UserTypeKind::TypeOf(_, user_args) = c_ty.value.kind {
// This is a unit-testing mechanism.
let span = self.tcx().hir().span(hir_id);
// We need to buffer the errors in order to guarantee a consistent

View file

@ -371,7 +371,6 @@ pub(crate) fn initialize_checked_jobserver(early_dcx: &EarlyDiagCtxt) {
// JUSTIFICATION: before session exists, only config
#[allow(rustc::bad_opt_access)]
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R {
trace!("run_compiler");
@ -425,7 +424,11 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
config.opts.unstable_opts.translate_directionality_markers,
) {
Ok(bundle) => bundle,
Err(e) => early_dcx.early_fatal(format!("failed to load fluent bundle: {e}")),
Err(e) => {
// We can't translate anything if we failed to load translations
#[allow(rustc::untranslatable_diagnostic)]
early_dcx.early_fatal(format!("failed to load fluent bundle: {e}"))
}
};
let mut locale_resources = config.locale_resources;
@ -479,7 +482,6 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
let mut lint_store = rustc_lint::new_lint_store(sess.enable_internal_lints());
if let Some(register_lints) = config.register_lints.as_deref() {
register_lints(&sess, &mut lint_store);
sess.registered_lints = true;
}
sess.lint_store = Some(Lrc::new(lint_store));

View file

@ -1125,6 +1125,18 @@ pub(crate) fn start_codegen<'tcx>(
}
}
// This must run after monomorphization so that all generic types
// have been instantiated.
if tcx.sess.opts.unstable_opts.print_type_sizes {
tcx.sess.code_stats.print_type_sizes();
}
if tcx.sess.opts.unstable_opts.print_vtable_sizes {
let crate_name = tcx.crate_name(LOCAL_CRATE);
tcx.sess.code_stats.print_vtable_sizes(crate_name);
}
codegen
}

View file

@ -127,18 +127,6 @@ impl Linker {
) -> Linker {
let ongoing_codegen = passes::start_codegen(codegen_backend, tcx);
// This must run after monomorphization so that all generic types
// have been instantiated.
if tcx.sess.opts.unstable_opts.print_type_sizes {
tcx.sess.code_stats.print_type_sizes();
}
if tcx.sess.opts.unstable_opts.print_vtable_sizes {
let crate_name = tcx.crate_name(LOCAL_CRATE);
tcx.sess.code_stats.print_vtable_sizes(crate_name);
}
Linker {
dep_graph: tcx.dep_graph.clone(),
output_filenames: Arc::clone(tcx.output_filenames(())),

View file

@ -35,10 +35,10 @@ pub type MakeBackendFn = fn() -> Box<dyn CodegenBackend>;
pub fn add_configuration(cfg: &mut Cfg, sess: &mut Session, codegen_backend: &dyn CodegenBackend) {
let tf = sym::target_feature;
let unstable_target_features = codegen_backend.target_features(sess, true);
let unstable_target_features = codegen_backend.target_features_cfg(sess, true);
sess.unstable_target_features.extend(unstable_target_features.iter().cloned());
let target_features = codegen_backend.target_features(sess, false);
let target_features = codegen_backend.target_features_cfg(sess, false);
sess.target_features.extend(target_features.iter().cloned());
cfg.extend(target_features.into_iter().map(|feat| (tf, Some(feat))));

View file

@ -806,10 +806,14 @@ lint_unexpected_cfg_add_build_rs_println = or consider adding `{$build_rs_printl
lint_unexpected_cfg_add_cargo_feature = consider using a Cargo feature instead
lint_unexpected_cfg_add_cargo_toml_lint_cfg = or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint:{$cargo_toml_lint_cfg}
lint_unexpected_cfg_add_cmdline_arg = to expect this configuration use `{$cmdline_arg}`
lint_unexpected_cfg_cargo_update = the {$macro_kind} `{$macro_name}` may come from an old version of it's defining crate, try updating your dependencies with `cargo update`
lint_unexpected_cfg_define_features = consider defining some features in `Cargo.toml`
lint_unexpected_cfg_doc_cargo = see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
lint_unexpected_cfg_doc_rustc = see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration
lint_unexpected_cfg_from_external_macro_origin = using a cfg inside a {$macro_kind} will use the cfgs from the destination crate and not the ones from the defining crate
lint_unexpected_cfg_from_external_macro_refer = try refering to `{$macro_name}` crate for guidance on how handle this unexpected cfg
lint_unexpected_cfg_name = unexpected `cfg` condition name: `{$name}`
lint_unexpected_cfg_name_expected_names = expected names are: {$possibilities}{$and_more ->
[0] {""}

View file

@ -3038,7 +3038,7 @@ impl EarlyLintPass for SpecialModuleName {
for item in &krate.items {
if let ast::ItemKind::Mod(
_,
ast::ModKind::Unloaded | ast::ModKind::Loaded(_, ast::Inline::No, _),
ast::ModKind::Unloaded | ast::ModKind::Loaded(_, ast::Inline::No, _, _),
) = item.kind
{
if item.attrs.iter().any(|a| a.has_name(sym::path)) {

View file

@ -1,9 +1,10 @@
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::bug;
use rustc_session::Session;
use rustc_session::config::ExpectedValues;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::symbol::Ident;
use rustc_span::{Span, Symbol, sym};
use rustc_span::{ExpnKind, Span, Symbol, sym};
use crate::lints;
@ -60,6 +61,35 @@ fn cargo_help_sub(
}
}
fn rustc_macro_help(span: Span) -> Option<lints::UnexpectedCfgRustcMacroHelp> {
let oexpn = span.ctxt().outer_expn_data();
if let Some(def_id) = oexpn.macro_def_id
&& let ExpnKind::Macro(macro_kind, macro_name) = oexpn.kind
&& def_id.krate != LOCAL_CRATE
{
Some(lints::UnexpectedCfgRustcMacroHelp { macro_kind: macro_kind.descr(), macro_name })
} else {
None
}
}
fn cargo_macro_help(span: Span) -> Option<lints::UnexpectedCfgCargoMacroHelp> {
let oexpn = span.ctxt().outer_expn_data();
if let Some(def_id) = oexpn.macro_def_id
&& let ExpnKind::Macro(macro_kind, macro_name) = oexpn.kind
&& def_id.krate != LOCAL_CRATE
{
Some(lints::UnexpectedCfgCargoMacroHelp {
macro_kind: macro_kind.descr(),
macro_name,
// FIXME: Get access to a `TyCtxt` from an `EarlyContext`
// crate_name: cx.tcx.crate_name(def_id.krate),
})
} else {
None
}
}
pub(super) fn unexpected_cfg_name(
sess: &Session,
(name, name_span): (Symbol, Span),
@ -85,6 +115,7 @@ pub(super) fn unexpected_cfg_name(
};
let is_from_cargo = rustc_session::utils::was_invoked_from_cargo();
let is_from_external_macro = rustc_middle::lint::in_external_macro(sess, name_span);
let mut is_feature_cfg = name == sym::feature;
let code_sugg = if is_feature_cfg && is_from_cargo {
@ -185,12 +216,21 @@ pub(super) fn unexpected_cfg_name(
};
let invocation_help = if is_from_cargo {
let sub = if !is_feature_cfg { Some(cargo_help_sub(sess, &inst)) } else { None };
lints::unexpected_cfg_name::InvocationHelp::Cargo { sub }
let help = if !is_feature_cfg && !is_from_external_macro {
Some(cargo_help_sub(sess, &inst))
} else {
None
};
lints::unexpected_cfg_name::InvocationHelp::Cargo {
help,
macro_help: cargo_macro_help(name_span),
}
} else {
lints::unexpected_cfg_name::InvocationHelp::Rustc(lints::UnexpectedCfgRustcHelp::new(
&inst(EscapeQuotes::No),
))
let help = lints::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No));
lints::unexpected_cfg_name::InvocationHelp::Rustc {
help,
macro_help: rustc_macro_help(name_span),
}
};
lints::UnexpectedCfgName { code_sugg, invocation_help, name }
@ -216,7 +256,9 @@ pub(super) fn unexpected_cfg_value(
.copied()
.flatten()
.collect();
let is_from_cargo = rustc_session::utils::was_invoked_from_cargo();
let is_from_external_macro = rustc_middle::lint::in_external_macro(sess, name_span);
// Show the full list if all possible values for a given name, but don't do it
// for names as the possibilities could be very long
@ -284,25 +326,31 @@ pub(super) fn unexpected_cfg_value(
};
let invocation_help = if is_from_cargo {
let help = if name == sym::feature {
let help = if name == sym::feature && !is_from_external_macro {
if let Some((value, _value_span)) = value {
Some(lints::unexpected_cfg_value::CargoHelp::AddFeature { value })
} else {
Some(lints::unexpected_cfg_value::CargoHelp::DefineFeatures)
}
} else if can_suggest_adding_value {
} else if can_suggest_adding_value && !is_from_external_macro {
Some(lints::unexpected_cfg_value::CargoHelp::Other(cargo_help_sub(sess, &inst)))
} else {
None
};
lints::unexpected_cfg_value::InvocationHelp::Cargo(help)
lints::unexpected_cfg_value::InvocationHelp::Cargo {
help,
macro_help: cargo_macro_help(name_span),
}
} else {
let help = if can_suggest_adding_value {
Some(lints::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No)))
} else {
None
};
lints::unexpected_cfg_value::InvocationHelp::Rustc(help)
lints::unexpected_cfg_value::InvocationHelp::Rustc {
help,
macro_help: rustc_macro_help(name_span),
}
};
lints::UnexpectedCfgValue {

View file

@ -192,6 +192,8 @@ fn is_temporary_rvalue(expr: &Expr<'_>) -> bool {
| ExprKind::DropTemps(..)
| ExprKind::Let(..) => false,
ExprKind::UnsafeBinderCast(..) => false,
// Not applicable
ExprKind::Type(..) | ExprKind::Err(..) => false,
}

View file

@ -422,6 +422,7 @@ impl<'tcx, 'a> Visitor<'tcx> for FindSignificantDropper<'tcx, 'a> {
hir::ExprKind::Unary(_, expr)
| hir::ExprKind::Cast(expr, _)
| hir::ExprKind::Type(expr, _)
| hir::ExprKind::UnsafeBinderCast(_, expr, _)
| hir::ExprKind::Yield(expr, _)
| hir::ExprKind::AddrOf(_, _, expr)
| hir::ExprKind::Match(expr, _, _)

View file

@ -2172,6 +2172,25 @@ impl UnexpectedCfgRustcHelp {
}
}
#[derive(Subdiagnostic)]
#[note(lint_unexpected_cfg_from_external_macro_origin)]
#[help(lint_unexpected_cfg_from_external_macro_refer)]
pub(crate) struct UnexpectedCfgRustcMacroHelp {
pub macro_kind: &'static str,
pub macro_name: Symbol,
}
#[derive(Subdiagnostic)]
#[note(lint_unexpected_cfg_from_external_macro_origin)]
#[help(lint_unexpected_cfg_from_external_macro_refer)]
#[help(lint_unexpected_cfg_cargo_update)]
pub(crate) struct UnexpectedCfgCargoMacroHelp {
pub macro_kind: &'static str,
pub macro_name: Symbol,
// FIXME: Figure out a way to get the crate name
// crate_name: String,
}
#[derive(LintDiagnostic)]
#[diag(lint_unexpected_cfg_name)]
pub(crate) struct UnexpectedCfgName {
@ -2276,10 +2295,17 @@ pub(crate) mod unexpected_cfg_name {
#[note(lint_unexpected_cfg_doc_cargo)]
Cargo {
#[subdiagnostic]
sub: Option<super::UnexpectedCfgCargoHelp>,
macro_help: Option<super::UnexpectedCfgCargoMacroHelp>,
#[subdiagnostic]
help: Option<super::UnexpectedCfgCargoHelp>,
},
#[note(lint_unexpected_cfg_doc_rustc)]
Rustc(#[subdiagnostic] super::UnexpectedCfgRustcHelp),
Rustc {
#[subdiagnostic]
macro_help: Option<super::UnexpectedCfgRustcMacroHelp>,
#[subdiagnostic]
help: super::UnexpectedCfgRustcHelp,
},
}
}
@ -2382,9 +2408,19 @@ pub(crate) mod unexpected_cfg_value {
#[derive(Subdiagnostic)]
pub(crate) enum InvocationHelp {
#[note(lint_unexpected_cfg_doc_cargo)]
Cargo(#[subdiagnostic] Option<CargoHelp>),
Cargo {
#[subdiagnostic]
help: Option<CargoHelp>,
#[subdiagnostic]
macro_help: Option<super::UnexpectedCfgCargoMacroHelp>,
},
#[note(lint_unexpected_cfg_doc_rustc)]
Rustc(#[subdiagnostic] Option<super::UnexpectedCfgRustcHelp>),
Rustc {
#[subdiagnostic]
help: Option<super::UnexpectedCfgRustcHelp>,
#[subdiagnostic]
macro_help: Option<super::UnexpectedCfgRustcMacroHelp>,
},
}
#[derive(Subdiagnostic)]

View file

@ -77,7 +77,7 @@ pub(crate) fn calculate(tcx: TyCtxt<'_>) -> Dependencies {
verify_ok(tcx, &linkage);
(ty, linkage)
})
.collect::<Vec<_>>()
.collect()
}
fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList {

View file

@ -1264,12 +1264,7 @@ fn should_encode_fn_sig(def_kind: DefKind) -> bool {
fn should_encode_constness(def_kind: DefKind) -> bool {
match def_kind {
DefKind::Fn
| DefKind::AssocFn
| DefKind::Closure
| DefKind::Impl { of_trait: true }
| DefKind::Variant
| DefKind::Ctor(..) => true,
DefKind::Fn | DefKind::AssocFn | DefKind::Closure | DefKind::Ctor(_, CtorKind::Fn) => true,
DefKind::Struct
| DefKind::Union
@ -1281,7 +1276,7 @@ fn should_encode_constness(def_kind: DefKind) -> bool {
| DefKind::Static { .. }
| DefKind::TyAlias
| DefKind::OpaqueTy
| DefKind::Impl { of_trait: false }
| DefKind::Impl { .. }
| DefKind::ForeignTy
| DefKind::ConstParam
| DefKind::InlineConst
@ -1296,6 +1291,8 @@ fn should_encode_constness(def_kind: DefKind) -> bool {
| DefKind::LifetimeParam
| DefKind::GlobalAsm
| DefKind::ExternCrate
| DefKind::Ctor(_, CtorKind::Const)
| DefKind::Variant
| DefKind::SyntheticCoroutineBody => false,
}
}
@ -2164,10 +2161,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
fn encode_dylib_dependency_formats(&mut self) -> LazyArray<Option<LinkagePreference>> {
empty_proc_macro!(self);
let formats = self.tcx.dependency_formats(());
for (ty, arr) in formats.iter() {
if *ty != CrateType::Dylib {
continue;
}
if let Some(arr) = formats.get(&CrateType::Dylib) {
return self.lazy_array(arr.iter().map(|slot| match *slot {
Linkage::NotLinked | Linkage::IncludedFromDylib => None,

View file

@ -7,6 +7,7 @@
// FIXME: move this file to rustc_metadata::dependency_format, but
// this will introduce circular dependency between rustc_metadata and rustc_middle
use rustc_data_structures::fx::FxIndexMap;
use rustc_macros::{Decodable, Encodable, HashStable};
use rustc_session::config::CrateType;
@ -18,7 +19,7 @@ pub type DependencyList = Vec<Linkage>;
/// A mapping of all required dependencies for a particular flavor of output.
///
/// This is local to the tcx, and is generally relevant to one session.
pub type Dependencies = Vec<(CrateType, DependencyList)>;
pub type Dependencies = FxIndexMap<CrateType, DependencyList>;
#[derive(Copy, Clone, PartialEq, Debug, HashStable, Encodable, Decodable)]
pub enum Linkage {

View file

@ -467,6 +467,9 @@ impl<'tcx> Const<'tcx> {
let const_val = tcx.valtree_to_const_val((ty, valtree));
Self::Val(const_val, ty)
}
ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => {
Self::Unevaluated(UnevaluatedConst { def, args, promoted: None }, ty)
}
_ => Self::Ty(ty, c),
}
}

View file

@ -294,10 +294,22 @@ pub enum Linkage {
Common,
}
/// Specifies the symbol visibility with regards to dynamic linking.
///
/// Visibility doesn't have any effect when linkage is internal.
///
/// DSO means dynamic shared object, that is a dynamically linked executable or dylib.
#[derive(Copy, Clone, PartialEq, Debug, HashStable)]
pub enum Visibility {
/// Export the symbol from the DSO and apply overrides of the symbol by outside DSOs to within
/// the DSO if the object file format supports this.
Default,
/// Hide the symbol outside of the defining DSO even when external linkage is used to export it
/// from the object file.
Hidden,
/// Export the symbol from the DSO, but don't apply overrides of the symbol by outside DSOs to
/// within the DSO. Equivalent to default visibility with object file formats that don't support
/// overriding exported symbols by another DSO.
Protected,
}

View file

@ -41,7 +41,8 @@ pub trait Key: Sized {
None
}
fn ty_def_id(&self) -> Option<DefId> {
/// Used to detect when ADT def ids are used as keys in a cycle for better error reporting.
fn def_id_for_ty_in_cycle(&self) -> Option<DefId> {
None
}
}
@ -423,7 +424,7 @@ impl<'tcx> Key for Ty<'tcx> {
DUMMY_SP
}
fn ty_def_id(&self) -> Option<DefId> {
fn def_id_for_ty_in_cycle(&self) -> Option<DefId> {
match *self.kind() {
ty::Adt(adt, _) => Some(adt.did()),
ty::Coroutine(def_id, ..) => Some(def_id),
@ -471,8 +472,8 @@ impl<'tcx, T: Key> Key for ty::PseudoCanonicalInput<'tcx, T> {
self.value.default_span(tcx)
}
fn ty_def_id(&self) -> Option<DefId> {
self.value.ty_def_id()
fn def_id_for_ty_in_cycle(&self) -> Option<DefId> {
self.value.def_id_for_ty_in_cycle()
}
}
@ -593,7 +594,7 @@ impl<'tcx> Key for (ValidityRequirement, ty::PseudoCanonicalInput<'tcx, Ty<'tcx>
DUMMY_SP
}
fn ty_def_id(&self) -> Option<DefId> {
fn def_id_for_ty_in_cycle(&self) -> Option<DefId> {
match self.1.value.kind() {
ty::Adt(adt, _) => Some(adt.did()),
_ => None,

View file

@ -746,7 +746,10 @@ rustc_queries! {
desc { |tcx| "computing drop-check constraints for `{}`", tcx.def_path_str(key) }
}
/// Returns `true` if this is a const fn / const impl.
/// Returns the constness of function-like things (tuple struct/variant constructors, functions,
/// methods)
///
/// Will ICE if used on things that are always const or never const.
///
/// **Do not call this function manually.** It is only meant to cache the base data for the
/// higher-level functions. Consider using `is_const_fn` or `is_const_trait_impl` instead.
@ -2230,7 +2233,7 @@ rustc_queries! {
}
/// Returns the Rust target features for the current target. These are not always the same as LLVM target features!
query rust_target_features(_: CrateNum) -> &'tcx UnordMap<String, rustc_target::target_features::Stability> {
query rust_target_features(_: CrateNum) -> &'tcx UnordMap<String, rustc_target::target_features::StabilityComputed> {
arena_cache
eval_always
desc { "looking up Rust target features" }

View file

@ -44,16 +44,16 @@ pub struct DynamicQuery<'tcx, C: QueryCache> {
pub format_value: fn(&C::Value) -> String,
}
pub struct QuerySystemFns<'tcx> {
pub struct QuerySystemFns {
pub engine: QueryEngine,
pub local_providers: Providers,
pub extern_providers: ExternProviders,
pub encode_query_results: fn(
pub encode_query_results: for<'tcx> fn(
tcx: TyCtxt<'tcx>,
encoder: &mut CacheEncoder<'_, 'tcx>,
query_result_index: &mut EncodedDepNodeIndex,
),
pub try_mark_green: fn(tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool,
pub try_mark_green: for<'tcx> fn(tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool,
}
pub struct QuerySystem<'tcx> {
@ -68,7 +68,7 @@ pub struct QuerySystem<'tcx> {
/// This is `None` if we are not incremental compilation mode
pub on_disk_cache: Option<OnDiskCache>,
pub fns: QuerySystemFns<'tcx>,
pub fns: QuerySystemFns,
pub jobs: AtomicU64,
}
@ -323,7 +323,7 @@ macro_rules! define_callbacks {
// Increase this limit if necessary, but do try to keep the size low if possible
#[cfg(target_pointer_width = "64")]
const _: () = {
if mem::size_of::<Key<'static>>() > 80 {
if mem::size_of::<Key<'static>>() > 88 {
panic!("{}", concat!(
"the query `",
stringify!($name),

View file

@ -316,8 +316,8 @@ pub enum ObligationCauseCode<'tcx> {
span: Option<Span>,
/// The root expected type induced by a scrutinee or type expression.
root_ty: Ty<'tcx>,
/// Whether the `Span` came from an expression or a type expression.
origin_expr: bool,
/// Information about the `Span`, if it came from an expression, otherwise `None`.
origin_expr: Option<PatternOriginExpr>,
},
/// Computing common supertype in an if expression
@ -530,6 +530,25 @@ pub struct MatchExpressionArmCause<'tcx> {
pub tail_defines_return_position_impl_trait: Option<LocalDefId>,
}
/// Information about the origin expression of a pattern, relevant to diagnostics.
/// Fields here refer to the scrutinee of a pattern.
/// If the scrutinee isn't given in the diagnostic, then this won't exist.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(TypeFoldable, TypeVisitable, HashStable, TyEncodable, TyDecodable)]
pub struct PatternOriginExpr {
/// A span representing the scrutinee expression, with all leading references
/// peeled from the expression.
/// Only references in the expression are peeled - if the expression refers to a variable
/// whose type is a reference, then that reference is kept because it wasn't created
/// in the expression.
pub peeled_span: Span,
/// The number of references that were peeled to produce `peeled_span`.
pub peeled_count: usize,
/// Does the peeled expression need to be wrapped in parentheses for
/// a prefix suggestion (i.e., dereference) to be valid.
pub peeled_prefix_suggestion_parentheses: bool,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(TypeFoldable, TypeVisitable, HashStable, TyEncodable, TyDecodable)]
pub struct IfExpressionCause<'tcx> {

View file

@ -3141,7 +3141,7 @@ impl<'tcx> TyCtxt<'tcx> {
/// Whether the trait impl is marked const. This does not consider stability or feature gates.
pub fn is_const_trait_impl(self, def_id: DefId) -> bool {
self.def_kind(def_id) == DefKind::Impl { of_trait: true }
&& self.constness(def_id) == hir::Constness::Const
&& self.impl_trait_header(def_id).unwrap().constness == hir::Constness::Const
}
pub fn intrinsic(self, def_id: impl IntoQueryParam<DefId> + Copy) -> Option<ty::IntrinsicDef> {

View file

@ -95,7 +95,7 @@ pub use self::sty::{
pub use self::trait_def::TraitDef;
pub use self::typeck_results::{
CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, IsIdentity,
TypeckResults, UserType, UserTypeAnnotationIndex,
TypeckResults, UserType, UserTypeAnnotationIndex, UserTypeKind,
};
pub use self::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason};
@ -254,6 +254,7 @@ pub struct ImplTraitHeader<'tcx> {
pub trait_ref: ty::EarlyBinder<'tcx, ty::TraitRef<'tcx>>,
pub polarity: ImplPolarity,
pub safety: hir::Safety,
pub constness: hir::Constness,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)]
@ -2005,17 +2006,25 @@ impl<'tcx> TyCtxt<'tcx> {
let def_id: DefId = def_id.into();
match self.def_kind(def_id) {
DefKind::Impl { of_trait: true } => {
self.constness(def_id) == hir::Constness::Const
&& self.is_const_trait(
self.trait_id_of_impl(def_id)
.expect("expected trait for trait implementation"),
)
let header = self.impl_trait_header(def_id).unwrap();
header.constness == hir::Constness::Const
&& self.is_const_trait(header.trait_ref.skip_binder().def_id)
}
DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) => {
self.constness(def_id) == hir::Constness::Const
}
DefKind::Trait => self.is_const_trait(def_id),
DefKind::AssocTy | DefKind::AssocFn => {
DefKind::AssocTy => {
let parent_def_id = self.parent(def_id);
match self.def_kind(parent_def_id) {
DefKind::Impl { of_trait: false } => false,
DefKind::Impl { of_trait: true } | DefKind::Trait => {
self.is_conditionally_const(parent_def_id)
}
_ => bug!("unexpected parent item of associated type: {parent_def_id:?}"),
}
}
DefKind::AssocFn => {
let parent_def_id = self.parent(def_id);
match self.def_kind(parent_def_id) {
DefKind::Impl { of_trait: false } => {
@ -2024,7 +2033,7 @@ impl<'tcx> TyCtxt<'tcx> {
DefKind::Impl { of_trait: true } | DefKind::Trait => {
self.is_conditionally_const(parent_def_id)
}
_ => bug!("unexpected parent item of associated item: {parent_def_id:?}"),
_ => bug!("unexpected parent item of associated fn: {parent_def_id:?}"),
}
}
DefKind::OpaqueTy => match self.opaque_ty_origin(def_id) {

View file

@ -700,12 +700,31 @@ pub struct CanonicalUserTypeAnnotation<'tcx> {
/// Canonical user type annotation.
pub type CanonicalUserType<'tcx> = Canonical<'tcx, UserType<'tcx>>;
#[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable)]
#[derive(Eq, Hash, HashStable, TypeFoldable, TypeVisitable)]
pub struct UserType<'tcx> {
pub kind: UserTypeKind<'tcx>,
pub bounds: ty::Clauses<'tcx>,
}
impl<'tcx> UserType<'tcx> {
pub fn new(kind: UserTypeKind<'tcx>) -> UserType<'tcx> {
UserType { kind, bounds: ty::ListWithCachedTypeInfo::empty() }
}
/// A user type annotation with additional bounds that need to be enforced.
/// These bounds are lowered from `impl Trait` in bindings.
pub fn new_with_bounds(kind: UserTypeKind<'tcx>, bounds: ty::Clauses<'tcx>) -> UserType<'tcx> {
UserType { kind, bounds }
}
}
/// A user-given type annotation attached to a constant. These arise
/// from constants that are named via paths, like `Foo::<A>::new` and
/// so forth.
#[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable)]
#[derive(Eq, Hash, HashStable, TypeFoldable, TypeVisitable)]
pub enum UserType<'tcx> {
pub enum UserTypeKind<'tcx> {
Ty(Ty<'tcx>),
/// The canonical type is the result of `type_of(def_id)` with the
@ -721,9 +740,13 @@ impl<'tcx> IsIdentity for CanonicalUserType<'tcx> {
/// Returns `true` if this represents the generic parameters of the form `[?0, ?1, ?2]`,
/// i.e., each thing is mapped to a canonical variable with the same index.
fn is_identity(&self) -> bool {
match self.value {
UserType::Ty(_) => false,
UserType::TypeOf(_, user_args) => {
if !self.value.bounds.is_empty() {
return false;
}
match self.value.kind {
UserTypeKind::Ty(_) => false,
UserTypeKind::TypeOf(_, user_args) => {
if user_args.user_self_ty.is_some() {
return false;
}
@ -764,6 +787,18 @@ impl<'tcx> IsIdentity for CanonicalUserType<'tcx> {
}
impl<'tcx> std::fmt::Display for UserType<'tcx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.bounds.is_empty() {
self.kind.fmt(f)
} else {
self.kind.fmt(f)?;
write!(f, " + ")?;
std::fmt::Debug::fmt(&self.bounds, f)
}
}
}
impl<'tcx> std::fmt::Display for UserTypeKind<'tcx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Ty(arg0) => {

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