Merge from rustc
This commit is contained in:
commit
120b97cac1
441 changed files with 6659 additions and 2554 deletions
12
Cargo.lock
12
Cargo.lock
|
|
@ -5892,6 +5892,18 @@ dependencies = [
|
|||
"compiler_builtins",
|
||||
"core",
|
||||
"libc",
|
||||
"unwinding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unwinding"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37a19a21a537f635c16c7576f22d0f2f7d63353c1337ad4ce0d8001c7952a25b"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"gimli",
|
||||
"rustc-std-workspace-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ If you wish to _contribute_ to the compiler, you should read
|
|||
[CONTRIBUTING.md](CONTRIBUTING.md) instead.
|
||||
|
||||
<details>
|
||||
<summary>Table of content</summary>
|
||||
<summary>Table of Contents</summary>
|
||||
|
||||
- [Quick Start](#quick-start)
|
||||
- [Installing from Source](#installing-from-source)
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ Rustdoc
|
|||
-------
|
||||
|
||||
- [Add warning block support in rustdoc](https://github.com/rust-lang/rust/pull/106561/)
|
||||
- [Accept additional user-defined syntax classes in fenced code blocks](https://github.com/rust-lang/rust/pull/110800/)
|
||||
- [rustdoc-search: add support for type parameters](https://github.com/rust-lang/rust/pull/112725/)
|
||||
- [rustdoc: show inner enum and struct in type definition for concrete type](https://github.com/rust-lang/rust/pull/114855/)
|
||||
|
||||
|
|
|
|||
|
|
@ -484,6 +484,20 @@ impl DroplessArena {
|
|||
}
|
||||
}
|
||||
|
||||
/// Allocates a string slice that is copied into the `DroplessArena`, returning a
|
||||
/// reference to it. Will panic if passed an empty string.
|
||||
///
|
||||
/// Panics:
|
||||
///
|
||||
/// - Zero-length string
|
||||
#[inline]
|
||||
pub fn alloc_str(&self, string: &str) -> &str {
|
||||
let slice = self.alloc_slice(string.as_bytes());
|
||||
|
||||
// SAFETY: the result has a copy of the same valid UTF-8 bytes.
|
||||
unsafe { std::str::from_utf8_unchecked(slice) }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that `mem` is valid for writes up to `size_of::<T>() * len`, and that
|
||||
|
|
@ -655,6 +669,14 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) {
|
|||
self.dropless.alloc_slice(value)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn alloc_str(&self, string: &str) -> &str {
|
||||
if string.is_empty() {
|
||||
return "";
|
||||
}
|
||||
self.dropless.alloc_str(string)
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub fn alloc_from_iter<T: ArenaAllocatable<'tcx, C>, C>(
|
||||
&self,
|
||||
|
|
|
|||
|
|
@ -1311,7 +1311,7 @@ pub struct Closure {
|
|||
pub binder: ClosureBinder,
|
||||
pub capture_clause: CaptureBy,
|
||||
pub constness: Const,
|
||||
pub asyncness: Async,
|
||||
pub coro_kind: Option<CoroutineKind>,
|
||||
pub movability: Movability,
|
||||
pub fn_decl: P<FnDecl>,
|
||||
pub body: P<Expr>,
|
||||
|
|
@ -2406,28 +2406,34 @@ pub enum Unsafe {
|
|||
No,
|
||||
}
|
||||
|
||||
/// Describes what kind of coroutine markers, if any, a function has.
|
||||
///
|
||||
/// Coroutine markers are things that cause the function to generate a coroutine, such as `async`,
|
||||
/// which makes the function return `impl Future`, or `gen`, which makes the function return `impl
|
||||
/// Iterator`.
|
||||
#[derive(Copy, Clone, Encodable, Decodable, Debug)]
|
||||
pub enum Async {
|
||||
Yes { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
|
||||
No,
|
||||
pub enum CoroutineKind {
|
||||
/// `async`, which evaluates to `impl Future`
|
||||
Async { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
|
||||
/// `gen`, which evaluates to `impl Iterator`
|
||||
Gen { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Encodable, Decodable, Debug)]
|
||||
pub enum Gen {
|
||||
Yes { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
|
||||
No,
|
||||
}
|
||||
|
||||
impl Async {
|
||||
impl CoroutineKind {
|
||||
pub fn is_async(self) -> bool {
|
||||
matches!(self, Async::Yes { .. })
|
||||
matches!(self, CoroutineKind::Async { .. })
|
||||
}
|
||||
|
||||
/// In this case this is an `async` return, the `NodeId` for the generated `impl Trait` item.
|
||||
pub fn opt_return_id(self) -> Option<(NodeId, Span)> {
|
||||
pub fn is_gen(self) -> bool {
|
||||
matches!(self, CoroutineKind::Gen { .. })
|
||||
}
|
||||
|
||||
/// In this case this is an `async` or `gen` return, the `NodeId` for the generated `impl Trait`
|
||||
/// item.
|
||||
pub fn return_id(self) -> (NodeId, Span) {
|
||||
match self {
|
||||
Async::Yes { return_impl_trait_id, span, .. } => Some((return_impl_trait_id, span)),
|
||||
Async::No => None,
|
||||
CoroutineKind::Async { return_impl_trait_id, span, .. }
|
||||
| CoroutineKind::Gen { return_impl_trait_id, span, .. } => (return_impl_trait_id, span),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2831,8 +2837,8 @@ impl Extern {
|
|||
pub struct FnHeader {
|
||||
/// The `unsafe` keyword, if any
|
||||
pub unsafety: Unsafe,
|
||||
/// The `async` keyword, if any
|
||||
pub asyncness: Async,
|
||||
/// Whether this is `async`, `gen`, or nothing.
|
||||
pub coro_kind: Option<CoroutineKind>,
|
||||
/// The `const` keyword, if any
|
||||
pub constness: Const,
|
||||
/// The `extern` keyword and corresponding ABI string, if any
|
||||
|
|
@ -2842,9 +2848,9 @@ pub struct FnHeader {
|
|||
impl FnHeader {
|
||||
/// Does this function header have any qualifiers or is it empty?
|
||||
pub fn has_qualifiers(&self) -> bool {
|
||||
let Self { unsafety, asyncness, constness, ext } = self;
|
||||
let Self { unsafety, coro_kind, constness, ext } = self;
|
||||
matches!(unsafety, Unsafe::Yes(_))
|
||||
|| asyncness.is_async()
|
||||
|| coro_kind.is_some()
|
||||
|| matches!(constness, Const::Yes(_))
|
||||
|| !matches!(ext, Extern::None)
|
||||
}
|
||||
|
|
@ -2852,12 +2858,7 @@ impl FnHeader {
|
|||
|
||||
impl Default for FnHeader {
|
||||
fn default() -> FnHeader {
|
||||
FnHeader {
|
||||
unsafety: Unsafe::No,
|
||||
asyncness: Async::No,
|
||||
constness: Const::No,
|
||||
ext: Extern::None,
|
||||
}
|
||||
FnHeader { unsafety: Unsafe::No, coro_kind: None, constness: Const::No, ext: Extern::None }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3177,7 +3178,7 @@ mod size_asserts {
|
|||
static_assert_size!(Block, 32);
|
||||
static_assert_size!(Expr, 72);
|
||||
static_assert_size!(ExprKind, 40);
|
||||
static_assert_size!(Fn, 152);
|
||||
static_assert_size!(Fn, 160);
|
||||
static_assert_size!(ForeignItem, 96);
|
||||
static_assert_size!(ForeignItemKind, 24);
|
||||
static_assert_size!(GenericArg, 24);
|
||||
|
|
|
|||
|
|
@ -121,8 +121,8 @@ pub trait MutVisitor: Sized {
|
|||
noop_visit_fn_decl(d, self);
|
||||
}
|
||||
|
||||
fn visit_asyncness(&mut self, a: &mut Async) {
|
||||
noop_visit_asyncness(a, self);
|
||||
fn visit_coro_kind(&mut self, a: &mut CoroutineKind) {
|
||||
noop_visit_coro_kind(a, self);
|
||||
}
|
||||
|
||||
fn visit_closure_binder(&mut self, b: &mut ClosureBinder) {
|
||||
|
|
@ -871,13 +871,14 @@ pub fn noop_visit_closure_binder<T: MutVisitor>(binder: &mut ClosureBinder, vis:
|
|||
}
|
||||
}
|
||||
|
||||
pub fn noop_visit_asyncness<T: MutVisitor>(asyncness: &mut Async, vis: &mut T) {
|
||||
match asyncness {
|
||||
Async::Yes { span: _, closure_id, return_impl_trait_id } => {
|
||||
pub fn noop_visit_coro_kind<T: MutVisitor>(coro_kind: &mut CoroutineKind, vis: &mut T) {
|
||||
match coro_kind {
|
||||
CoroutineKind::Async { span, closure_id, return_impl_trait_id }
|
||||
| CoroutineKind::Gen { span, closure_id, return_impl_trait_id } => {
|
||||
vis.visit_span(span);
|
||||
vis.visit_id(closure_id);
|
||||
vis.visit_id(return_impl_trait_id);
|
||||
}
|
||||
Async::No => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1170,9 +1171,9 @@ fn visit_const_item<T: MutVisitor>(
|
|||
}
|
||||
|
||||
pub fn noop_visit_fn_header<T: MutVisitor>(header: &mut FnHeader, vis: &mut T) {
|
||||
let FnHeader { unsafety, asyncness, constness, ext: _ } = header;
|
||||
let FnHeader { unsafety, coro_kind, constness, ext: _ } = header;
|
||||
visit_constness(constness, vis);
|
||||
vis.visit_asyncness(asyncness);
|
||||
coro_kind.as_mut().map(|coro_kind| vis.visit_coro_kind(coro_kind));
|
||||
visit_unsafety(unsafety, vis);
|
||||
}
|
||||
|
||||
|
|
@ -1406,7 +1407,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
|
|||
binder,
|
||||
capture_clause,
|
||||
constness,
|
||||
asyncness,
|
||||
coro_kind,
|
||||
movability: _,
|
||||
fn_decl,
|
||||
body,
|
||||
|
|
@ -1415,7 +1416,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
|
|||
}) => {
|
||||
vis.visit_closure_binder(binder);
|
||||
visit_constness(constness, vis);
|
||||
vis.visit_asyncness(asyncness);
|
||||
coro_kind.as_mut().map(|coro_kind| vis.visit_coro_kind(coro_kind));
|
||||
vis.visit_capture_by(capture_clause);
|
||||
vis.visit_fn_decl(fn_decl);
|
||||
vis.visit_expr(body);
|
||||
|
|
|
|||
|
|
@ -861,7 +861,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
|
|||
ExprKind::Closure(box Closure {
|
||||
binder,
|
||||
capture_clause,
|
||||
asyncness: _,
|
||||
coro_kind: _,
|
||||
constness: _,
|
||||
movability: _,
|
||||
fn_decl,
|
||||
|
|
|
|||
|
|
@ -195,39 +195,39 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
binder,
|
||||
capture_clause,
|
||||
constness,
|
||||
asyncness,
|
||||
coro_kind,
|
||||
movability,
|
||||
fn_decl,
|
||||
body,
|
||||
fn_decl_span,
|
||||
fn_arg_span,
|
||||
}) => {
|
||||
if let Async::Yes { closure_id, .. } = asyncness {
|
||||
self.lower_expr_async_closure(
|
||||
binder,
|
||||
*capture_clause,
|
||||
e.id,
|
||||
hir_id,
|
||||
*closure_id,
|
||||
fn_decl,
|
||||
body,
|
||||
*fn_decl_span,
|
||||
*fn_arg_span,
|
||||
)
|
||||
} else {
|
||||
self.lower_expr_closure(
|
||||
binder,
|
||||
*capture_clause,
|
||||
e.id,
|
||||
*constness,
|
||||
*movability,
|
||||
fn_decl,
|
||||
body,
|
||||
*fn_decl_span,
|
||||
*fn_arg_span,
|
||||
)
|
||||
}
|
||||
}
|
||||
}) => match coro_kind {
|
||||
Some(
|
||||
CoroutineKind::Async { closure_id, .. }
|
||||
| CoroutineKind::Gen { closure_id, .. },
|
||||
) => self.lower_expr_async_closure(
|
||||
binder,
|
||||
*capture_clause,
|
||||
e.id,
|
||||
hir_id,
|
||||
*closure_id,
|
||||
fn_decl,
|
||||
body,
|
||||
*fn_decl_span,
|
||||
*fn_arg_span,
|
||||
),
|
||||
None => self.lower_expr_closure(
|
||||
binder,
|
||||
*capture_clause,
|
||||
e.id,
|
||||
*constness,
|
||||
*movability,
|
||||
fn_decl,
|
||||
body,
|
||||
*fn_decl_span,
|
||||
*fn_arg_span,
|
||||
),
|
||||
},
|
||||
ExprKind::Block(blk, opt_label) => {
|
||||
let opt_label = self.lower_label(*opt_label);
|
||||
hir::ExprKind::Block(self.lower_block(blk, opt_label.is_some()), opt_label)
|
||||
|
|
|
|||
|
|
@ -206,15 +206,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
// `impl Future<Output = T>` here because lower_body
|
||||
// only cares about the input argument patterns in the function
|
||||
// declaration (decl), not the return types.
|
||||
let asyncness = header.asyncness;
|
||||
let body_id =
|
||||
this.lower_maybe_async_body(span, hir_id, decl, asyncness, body.as_deref());
|
||||
let coro_kind = header.coro_kind;
|
||||
let body_id = this.lower_maybe_coroutine_body(
|
||||
span,
|
||||
hir_id,
|
||||
decl,
|
||||
coro_kind,
|
||||
body.as_deref(),
|
||||
);
|
||||
|
||||
let itctx = ImplTraitContext::Universal;
|
||||
let (generics, decl) =
|
||||
this.lower_generics(generics, header.constness, id, &itctx, |this| {
|
||||
let ret_id = asyncness.opt_return_id();
|
||||
this.lower_fn_decl(decl, id, *fn_sig_span, FnDeclKind::Fn, ret_id)
|
||||
this.lower_fn_decl(decl, id, *fn_sig_span, FnDeclKind::Fn, coro_kind)
|
||||
});
|
||||
let sig = hir::FnSig {
|
||||
decl,
|
||||
|
|
@ -561,11 +565,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
.params
|
||||
.iter()
|
||||
.find(|param| {
|
||||
parent_hir
|
||||
.attrs
|
||||
.get(param.hir_id.local_id)
|
||||
.iter()
|
||||
.any(|attr| attr.has_name(sym::rustc_host))
|
||||
matches!(
|
||||
param.kind,
|
||||
hir::GenericParamKind::Const { is_host_effect: true, .. }
|
||||
)
|
||||
})
|
||||
.map(|param| param.def_id);
|
||||
}
|
||||
|
|
@ -725,27 +728,30 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
(generics, kind, expr.is_some())
|
||||
}
|
||||
AssocItemKind::Fn(box Fn { sig, generics, body: None, .. }) => {
|
||||
let asyncness = sig.header.asyncness;
|
||||
let names = self.lower_fn_params_to_names(&sig.decl);
|
||||
let (generics, sig) = self.lower_method_sig(
|
||||
generics,
|
||||
sig,
|
||||
i.id,
|
||||
FnDeclKind::Trait,
|
||||
asyncness.opt_return_id(),
|
||||
sig.header.coro_kind,
|
||||
);
|
||||
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
|
||||
}
|
||||
AssocItemKind::Fn(box Fn { sig, generics, body: Some(body), .. }) => {
|
||||
let asyncness = sig.header.asyncness;
|
||||
let body_id =
|
||||
self.lower_maybe_async_body(i.span, hir_id, &sig.decl, asyncness, Some(body));
|
||||
let body_id = self.lower_maybe_coroutine_body(
|
||||
i.span,
|
||||
hir_id,
|
||||
&sig.decl,
|
||||
sig.header.coro_kind,
|
||||
Some(body),
|
||||
);
|
||||
let (generics, sig) = self.lower_method_sig(
|
||||
generics,
|
||||
sig,
|
||||
i.id,
|
||||
FnDeclKind::Trait,
|
||||
asyncness.opt_return_id(),
|
||||
sig.header.coro_kind,
|
||||
);
|
||||
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true)
|
||||
}
|
||||
|
|
@ -834,12 +840,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
},
|
||||
),
|
||||
AssocItemKind::Fn(box Fn { sig, generics, body, .. }) => {
|
||||
let asyncness = sig.header.asyncness;
|
||||
let body_id = self.lower_maybe_async_body(
|
||||
let body_id = self.lower_maybe_coroutine_body(
|
||||
i.span,
|
||||
hir_id,
|
||||
&sig.decl,
|
||||
asyncness,
|
||||
sig.header.coro_kind,
|
||||
body.as_deref(),
|
||||
);
|
||||
let (generics, sig) = self.lower_method_sig(
|
||||
|
|
@ -847,7 +852,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
sig,
|
||||
i.id,
|
||||
if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
|
||||
asyncness.opt_return_id(),
|
||||
sig.header.coro_kind,
|
||||
);
|
||||
|
||||
(generics, hir::ImplItemKind::Fn(sig, body_id))
|
||||
|
|
@ -1011,17 +1016,23 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
})
|
||||
}
|
||||
|
||||
fn lower_maybe_async_body(
|
||||
/// Takes what may be the body of an `async fn` or a `gen fn` and wraps it in an `async {}` or
|
||||
/// `gen {}` block as appropriate.
|
||||
fn lower_maybe_coroutine_body(
|
||||
&mut self,
|
||||
span: Span,
|
||||
fn_id: hir::HirId,
|
||||
decl: &FnDecl,
|
||||
asyncness: Async,
|
||||
coro_kind: Option<CoroutineKind>,
|
||||
body: Option<&Block>,
|
||||
) -> hir::BodyId {
|
||||
let (closure_id, body) = match (asyncness, body) {
|
||||
(Async::Yes { closure_id, .. }, Some(body)) => (closure_id, body),
|
||||
_ => return self.lower_fn_body_block(span, decl, body),
|
||||
let (Some(coro_kind), Some(body)) = (coro_kind, body) else {
|
||||
return self.lower_fn_body_block(span, decl, body);
|
||||
};
|
||||
let closure_id = match coro_kind {
|
||||
CoroutineKind::Async { closure_id, .. } | CoroutineKind::Gen { closure_id, .. } => {
|
||||
closure_id
|
||||
}
|
||||
};
|
||||
|
||||
self.lower_body(|this| {
|
||||
|
|
@ -1163,44 +1174,54 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
parameters.push(new_parameter);
|
||||
}
|
||||
|
||||
let async_expr = this.make_async_expr(
|
||||
CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
|
||||
closure_id,
|
||||
None,
|
||||
body.span,
|
||||
hir::CoroutineSource::Fn,
|
||||
|this| {
|
||||
// Create a block from the user's function body:
|
||||
let user_body = this.lower_block_expr(body);
|
||||
let mkbody = |this: &mut LoweringContext<'_, 'hir>| {
|
||||
// Create a block from the user's function body:
|
||||
let user_body = this.lower_block_expr(body);
|
||||
|
||||
// Transform into `drop-temps { <user-body> }`, an expression:
|
||||
let desugared_span =
|
||||
this.mark_span_with_reason(DesugaringKind::Async, user_body.span, None);
|
||||
let user_body =
|
||||
this.expr_drop_temps(desugared_span, this.arena.alloc(user_body));
|
||||
// Transform into `drop-temps { <user-body> }`, an expression:
|
||||
let desugared_span =
|
||||
this.mark_span_with_reason(DesugaringKind::Async, user_body.span, None);
|
||||
let user_body = this.expr_drop_temps(desugared_span, this.arena.alloc(user_body));
|
||||
|
||||
// As noted above, create the final block like
|
||||
//
|
||||
// ```
|
||||
// {
|
||||
// let $param_pattern = $raw_param;
|
||||
// ...
|
||||
// drop-temps { <user-body> }
|
||||
// }
|
||||
// ```
|
||||
let body = this.block_all(
|
||||
desugared_span,
|
||||
this.arena.alloc_from_iter(statements),
|
||||
Some(user_body),
|
||||
);
|
||||
// As noted above, create the final block like
|
||||
//
|
||||
// ```
|
||||
// {
|
||||
// let $param_pattern = $raw_param;
|
||||
// ...
|
||||
// drop-temps { <user-body> }
|
||||
// }
|
||||
// ```
|
||||
let body = this.block_all(
|
||||
desugared_span,
|
||||
this.arena.alloc_from_iter(statements),
|
||||
Some(user_body),
|
||||
);
|
||||
|
||||
this.expr_block(body)
|
||||
},
|
||||
);
|
||||
this.expr_block(body)
|
||||
};
|
||||
let coroutine_expr = match coro_kind {
|
||||
CoroutineKind::Async { .. } => this.make_async_expr(
|
||||
CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
|
||||
closure_id,
|
||||
None,
|
||||
body.span,
|
||||
hir::CoroutineSource::Fn,
|
||||
mkbody,
|
||||
),
|
||||
CoroutineKind::Gen { .. } => this.make_gen_expr(
|
||||
CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
|
||||
closure_id,
|
||||
None,
|
||||
body.span,
|
||||
hir::CoroutineSource::Fn,
|
||||
mkbody,
|
||||
),
|
||||
};
|
||||
|
||||
let hir_id = this.lower_node_id(closure_id);
|
||||
this.maybe_forward_track_caller(body.span, fn_id, hir_id);
|
||||
let expr = hir::Expr { hir_id, kind: async_expr, span: this.lower_span(body.span) };
|
||||
let expr = hir::Expr { hir_id, kind: coroutine_expr, span: this.lower_span(body.span) };
|
||||
|
||||
(this.arena.alloc_from_iter(parameters), expr)
|
||||
})
|
||||
|
|
@ -1212,21 +1233,26 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
sig: &FnSig,
|
||||
id: NodeId,
|
||||
kind: FnDeclKind,
|
||||
is_async: Option<(NodeId, Span)>,
|
||||
coro_kind: Option<CoroutineKind>,
|
||||
) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
|
||||
let header = self.lower_fn_header(sig.header);
|
||||
let itctx = ImplTraitContext::Universal;
|
||||
let (generics, decl) =
|
||||
self.lower_generics(generics, sig.header.constness, id, &itctx, |this| {
|
||||
this.lower_fn_decl(&sig.decl, id, sig.span, kind, is_async)
|
||||
this.lower_fn_decl(&sig.decl, id, sig.span, kind, coro_kind)
|
||||
});
|
||||
(generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
|
||||
}
|
||||
|
||||
fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader {
|
||||
let asyncness = if let Some(CoroutineKind::Async { span, .. }) = h.coro_kind {
|
||||
hir::IsAsync::Async(span)
|
||||
} else {
|
||||
hir::IsAsync::NotAsync
|
||||
};
|
||||
hir::FnHeader {
|
||||
unsafety: self.lower_unsafety(h.unsafety),
|
||||
asyncness: self.lower_asyncness(h.asyncness),
|
||||
asyncness: asyncness,
|
||||
constness: self.lower_constness(h.constness),
|
||||
abi: self.lower_extern(h.ext),
|
||||
}
|
||||
|
|
@ -1268,13 +1294,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
});
|
||||
}
|
||||
|
||||
fn lower_asyncness(&mut self, a: Async) -> hir::IsAsync {
|
||||
match a {
|
||||
Async::Yes { span, .. } => hir::IsAsync::Async(span),
|
||||
Async::No => hir::IsAsync::NotAsync,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn lower_constness(&mut self, c: Const) -> hir::Constness {
|
||||
match c {
|
||||
Const::Yes(_) => hir::Constness::Const,
|
||||
|
|
@ -1352,27 +1371,17 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let host_param_parts = if let Const::Yes(span) = constness
|
||||
&& self.tcx.features().effects
|
||||
{
|
||||
if let Some(param) =
|
||||
generics.params.iter().find(|x| x.attrs.iter().any(|x| x.has_name(sym::rustc_host)))
|
||||
{
|
||||
// user has manually specified a `rustc_host` param, in this case, we set
|
||||
// the param id so that lowering logic can use that. But we don't create
|
||||
// another host param, so this gives `None`.
|
||||
self.host_param_id = Some(self.local_def_id(param.id));
|
||||
None
|
||||
} else {
|
||||
let param_node_id = self.next_node_id();
|
||||
let hir_id = self.next_id();
|
||||
let def_id = self.create_def(
|
||||
self.local_def_id(parent_node_id),
|
||||
param_node_id,
|
||||
sym::host,
|
||||
DefKind::ConstParam,
|
||||
span,
|
||||
);
|
||||
self.host_param_id = Some(def_id);
|
||||
Some((span, hir_id, def_id))
|
||||
}
|
||||
let param_node_id = self.next_node_id();
|
||||
let hir_id = self.next_id();
|
||||
let def_id = self.create_def(
|
||||
self.local_def_id(parent_node_id),
|
||||
param_node_id,
|
||||
sym::host,
|
||||
DefKind::ConstParam,
|
||||
span,
|
||||
);
|
||||
self.host_param_id = Some(def_id);
|
||||
Some((span, hir_id, def_id))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
|
@ -1436,19 +1445,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
self.children.push((def_id, hir::MaybeOwner::NonOwner(hir_id)));
|
||||
self.children.push((anon_const, hir::MaybeOwner::NonOwner(const_id)));
|
||||
|
||||
let attr_id = self.tcx.sess.parse_sess.attr_id_generator.mk_attr_id();
|
||||
|
||||
let attrs = self.arena.alloc_from_iter([Attribute {
|
||||
kind: AttrKind::Normal(P(NormalAttr::from_ident(Ident::new(
|
||||
sym::rustc_host,
|
||||
span,
|
||||
)))),
|
||||
span,
|
||||
id: attr_id,
|
||||
style: AttrStyle::Outer,
|
||||
}]);
|
||||
self.attrs.insert(hir_id.local_id, attrs);
|
||||
|
||||
let const_body = self.lower_body(|this| {
|
||||
(
|
||||
&[],
|
||||
|
|
@ -1490,6 +1486,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
hir_id: const_id,
|
||||
body: const_body,
|
||||
}),
|
||||
is_host_effect: true,
|
||||
},
|
||||
colon_span: None,
|
||||
pure_wrt_drop: false,
|
||||
|
|
|
|||
|
|
@ -1778,13 +1778,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
}))
|
||||
}
|
||||
|
||||
// Lowers a function declaration.
|
||||
//
|
||||
// `decl`: the unlowered (AST) function declaration.
|
||||
// `fn_node_id`: `impl Trait` arguments are lowered into generic parameters on the given `NodeId`.
|
||||
// `make_ret_async`: if `Some`, converts `-> T` into `-> impl Future<Output = T>` in the
|
||||
// return type. This is used for `async fn` declarations. The `NodeId` is the ID of the
|
||||
// return type `impl Trait` item, and the `Span` points to the `async` keyword.
|
||||
/// Lowers a function declaration.
|
||||
///
|
||||
/// `decl`: the unlowered (AST) function declaration.
|
||||
///
|
||||
/// `fn_node_id`: `impl Trait` arguments are lowered into generic parameters on the given
|
||||
/// `NodeId`.
|
||||
///
|
||||
/// `transform_return_type`: if `Some`, applies some conversion to the return type, such as is
|
||||
/// needed for `async fn` and `gen fn`. See [`CoroutineKind`] for more details.
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn lower_fn_decl(
|
||||
&mut self,
|
||||
|
|
@ -1792,7 +1794,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
fn_node_id: NodeId,
|
||||
fn_span: Span,
|
||||
kind: FnDeclKind,
|
||||
make_ret_async: Option<(NodeId, Span)>,
|
||||
coro: Option<CoroutineKind>,
|
||||
) -> &'hir hir::FnDecl<'hir> {
|
||||
let c_variadic = decl.c_variadic();
|
||||
|
||||
|
|
@ -1821,11 +1823,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
self.lower_ty_direct(¶m.ty, &itctx)
|
||||
}));
|
||||
|
||||
let output = if let Some((ret_id, _span)) = make_ret_async {
|
||||
let fn_def_id = self.local_def_id(fn_node_id);
|
||||
self.lower_async_fn_ret_ty(&decl.output, fn_def_id, ret_id, kind, fn_span)
|
||||
} else {
|
||||
match &decl.output {
|
||||
let output = match coro {
|
||||
Some(coro) => {
|
||||
let fn_def_id = self.local_def_id(fn_node_id);
|
||||
self.lower_coroutine_fn_ret_ty(&decl.output, fn_def_id, coro, kind, fn_span)
|
||||
}
|
||||
None => match &decl.output {
|
||||
FnRetTy::Ty(ty) => {
|
||||
let context = if kind.return_impl_trait_allowed() {
|
||||
let fn_def_id = self.local_def_id(fn_node_id);
|
||||
|
|
@ -1849,7 +1852,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
hir::FnRetTy::Return(self.lower_ty(ty, &context))
|
||||
}
|
||||
FnRetTy::Default(span) => hir::FnRetTy::DefaultReturn(self.lower_span(*span)),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
self.arena.alloc(hir::FnDecl {
|
||||
|
|
@ -1888,17 +1891,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// `fn_node_id`: `NodeId` of the parent function (used to create child impl trait definition)
|
||||
// `opaque_ty_node_id`: `NodeId` of the opaque `impl Trait` type that should be created
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn lower_async_fn_ret_ty(
|
||||
fn lower_coroutine_fn_ret_ty(
|
||||
&mut self,
|
||||
output: &FnRetTy,
|
||||
fn_def_id: LocalDefId,
|
||||
opaque_ty_node_id: NodeId,
|
||||
coro: CoroutineKind,
|
||||
fn_kind: FnDeclKind,
|
||||
fn_span: Span,
|
||||
) -> hir::FnRetTy<'hir> {
|
||||
let span = self.lower_span(fn_span);
|
||||
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, None);
|
||||
|
||||
let opaque_ty_node_id = match coro {
|
||||
CoroutineKind::Async { return_impl_trait_id, .. }
|
||||
| CoroutineKind::Gen { return_impl_trait_id, .. } => return_impl_trait_id,
|
||||
};
|
||||
|
||||
let captured_lifetimes: Vec<_> = self
|
||||
.resolver
|
||||
.take_extra_lifetime_params(opaque_ty_node_id)
|
||||
|
|
@ -1914,15 +1922,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
span,
|
||||
opaque_ty_span,
|
||||
|this| {
|
||||
let future_bound = this.lower_async_fn_output_type_to_future_bound(
|
||||
let bound = this.lower_coroutine_fn_output_type_to_bound(
|
||||
output,
|
||||
coro,
|
||||
span,
|
||||
ImplTraitContext::ReturnPositionOpaqueTy {
|
||||
origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
|
||||
fn_kind,
|
||||
},
|
||||
);
|
||||
arena_vec![this; future_bound]
|
||||
arena_vec![this; bound]
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -1931,9 +1940,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
}
|
||||
|
||||
/// Transforms `-> T` into `Future<Output = T>`.
|
||||
fn lower_async_fn_output_type_to_future_bound(
|
||||
fn lower_coroutine_fn_output_type_to_bound(
|
||||
&mut self,
|
||||
output: &FnRetTy,
|
||||
coro: CoroutineKind,
|
||||
span: Span,
|
||||
nested_impl_trait_context: ImplTraitContext,
|
||||
) -> hir::GenericBound<'hir> {
|
||||
|
|
@ -1948,17 +1958,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
FnRetTy::Default(ret_ty_span) => self.arena.alloc(self.ty_tup(*ret_ty_span, &[])),
|
||||
};
|
||||
|
||||
// "<Output = T>"
|
||||
// "<$assoc_ty_name = T>"
|
||||
let (assoc_ty_name, trait_lang_item) = match coro {
|
||||
CoroutineKind::Async { .. } => (hir::FN_OUTPUT_NAME, hir::LangItem::Future),
|
||||
CoroutineKind::Gen { .. } => (hir::ITERATOR_ITEM_NAME, hir::LangItem::Iterator),
|
||||
};
|
||||
|
||||
let future_args = self.arena.alloc(hir::GenericArgs {
|
||||
args: &[],
|
||||
bindings: arena_vec![self; self.output_ty_binding(span, output_ty)],
|
||||
bindings: arena_vec![self; self.assoc_ty_binding(assoc_ty_name, span, output_ty)],
|
||||
parenthesized: hir::GenericArgsParentheses::No,
|
||||
span_ext: DUMMY_SP,
|
||||
});
|
||||
|
||||
hir::GenericBound::LangItemTrait(
|
||||
// ::std::future::Future<future_params>
|
||||
hir::LangItem::Future,
|
||||
trait_lang_item,
|
||||
self.lower_span(span),
|
||||
self.next_id(),
|
||||
future_args,
|
||||
|
|
@ -2108,7 +2122,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
let default = default.as_ref().map(|def| self.lower_anon_const(def));
|
||||
(
|
||||
hir::ParamName::Plain(self.lower_ident(param.ident)),
|
||||
hir::GenericParamKind::Const { ty, default },
|
||||
hir::GenericParamKind::Const { ty, default, is_host_effect: false },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2536,15 +2550,6 @@ impl<'hir> GenericArgsCtor<'hir> {
|
|||
})
|
||||
});
|
||||
|
||||
let attr_id = lcx.tcx.sess.parse_sess.attr_id_generator.mk_attr_id();
|
||||
let attr = lcx.arena.alloc(Attribute {
|
||||
kind: AttrKind::Normal(P(NormalAttr::from_ident(Ident::new(sym::rustc_host, span)))),
|
||||
span,
|
||||
id: attr_id,
|
||||
style: AttrStyle::Outer,
|
||||
});
|
||||
lcx.attrs.insert(hir_id.local_id, std::slice::from_ref(attr));
|
||||
|
||||
let def_id = lcx.create_def(
|
||||
lcx.current_hir_id_owner.def_id,
|
||||
id,
|
||||
|
|
@ -2552,6 +2557,7 @@ impl<'hir> GenericArgsCtor<'hir> {
|
|||
DefKind::AnonConst,
|
||||
span,
|
||||
);
|
||||
|
||||
lcx.children.push((def_id, hir::MaybeOwner::NonOwner(hir_id)));
|
||||
self.args.push(hir::GenericArg::Const(hir::ConstArg {
|
||||
value: hir::AnonConst { def_id, hir_id, body },
|
||||
|
|
|
|||
|
|
@ -389,7 +389,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])),
|
||||
};
|
||||
let args = smallvec![GenericArg::Type(self.arena.alloc(self.ty_tup(*inputs_span, inputs)))];
|
||||
let binding = self.output_ty_binding(output_ty.span, output_ty);
|
||||
let binding = self.assoc_ty_binding(hir::FN_OUTPUT_NAME, output_ty.span, output_ty);
|
||||
(
|
||||
GenericArgsCtor {
|
||||
args,
|
||||
|
|
@ -401,13 +401,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
)
|
||||
}
|
||||
|
||||
/// An associated type binding `Output = $ty`.
|
||||
pub(crate) fn output_ty_binding(
|
||||
/// An associated type binding `$assoc_ty_name = $ty`.
|
||||
pub(crate) fn assoc_ty_binding(
|
||||
&mut self,
|
||||
assoc_ty_name: rustc_span::Symbol,
|
||||
span: Span,
|
||||
ty: &'hir hir::Ty<'hir>,
|
||||
) -> hir::TypeBinding<'hir> {
|
||||
let ident = Ident::with_dummy_span(hir::FN_OUTPUT_NAME);
|
||||
let ident = Ident::with_dummy_span(assoc_ty_name);
|
||||
let kind = hir::TypeBindingKind::Equality { term: ty.into() };
|
||||
let args = arena_vec![self;];
|
||||
let bindings = arena_vec![self;];
|
||||
|
|
|
|||
|
|
@ -1268,13 +1268,18 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
|
||||
self.check_c_variadic_type(fk);
|
||||
|
||||
// Functions cannot both be `const async`
|
||||
// Functions cannot both be `const async` or `const gen`
|
||||
if let Some(&FnHeader {
|
||||
constness: Const::Yes(cspan),
|
||||
asyncness: Async::Yes { span: aspan, .. },
|
||||
coro_kind:
|
||||
Some(
|
||||
CoroutineKind::Async { span: aspan, .. }
|
||||
| CoroutineKind::Gen { span: aspan, .. },
|
||||
),
|
||||
..
|
||||
}) = fk.header()
|
||||
{
|
||||
// FIXME(gen_blocks): Report a different error for `const gen`
|
||||
self.err_handler().emit_err(errors::ConstAndAsync {
|
||||
spans: vec![cspan, aspan],
|
||||
cspan,
|
||||
|
|
|
|||
|
|
@ -1490,9 +1490,14 @@ impl<'a> State<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn print_asyncness(&mut self, asyncness: ast::Async) {
|
||||
if asyncness.is_async() {
|
||||
self.word_nbsp("async");
|
||||
fn print_coro_kind(&mut self, coro_kind: ast::CoroutineKind) {
|
||||
match coro_kind {
|
||||
ast::CoroutineKind::Gen { .. } => {
|
||||
self.word_nbsp("gen");
|
||||
}
|
||||
ast::CoroutineKind::Async { .. } => {
|
||||
self.word_nbsp("async");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1685,7 +1690,7 @@ impl<'a> State<'a> {
|
|||
|
||||
fn print_fn_header_info(&mut self, header: ast::FnHeader) {
|
||||
self.print_constness(header.constness);
|
||||
self.print_asyncness(header.asyncness);
|
||||
header.coro_kind.map(|coro_kind| self.print_coro_kind(coro_kind));
|
||||
self.print_unsafety(header.unsafety);
|
||||
|
||||
match header.ext {
|
||||
|
|
|
|||
|
|
@ -413,7 +413,7 @@ impl<'a> State<'a> {
|
|||
binder,
|
||||
capture_clause,
|
||||
constness,
|
||||
asyncness,
|
||||
coro_kind,
|
||||
movability,
|
||||
fn_decl,
|
||||
body,
|
||||
|
|
@ -423,7 +423,7 @@ impl<'a> State<'a> {
|
|||
self.print_closure_binder(binder);
|
||||
self.print_constness(*constness);
|
||||
self.print_movability(*movability);
|
||||
self.print_asyncness(*asyncness);
|
||||
coro_kind.map(|coro_kind| self.print_coro_kind(coro_kind));
|
||||
self.print_capture_clause(*capture_clause);
|
||||
|
||||
self.print_fn_params_and_ret(fn_decl, true);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use rustc_hir::def::{CtorKind, Namespace};
|
|||
use rustc_hir::CoroutineKind;
|
||||
use rustc_index::IndexSlice;
|
||||
use rustc_infer::infer::BoundRegionConversionTime;
|
||||
use rustc_infer::traits::{FulfillmentErrorCode, SelectionError};
|
||||
use rustc_middle::mir::tcx::PlaceTy;
|
||||
use rustc_middle::mir::{
|
||||
AggregateKind, CallSource, ConstOperand, FakeReadCause, Local, LocalInfo, LocalKind, Location,
|
||||
|
|
@ -24,10 +25,9 @@ use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult};
|
|||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
|
||||
use rustc_target::abi::{FieldIdx, VariantIdx};
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::{
|
||||
type_known_to_meet_bound_modulo_regions, Obligation, ObligationCause,
|
||||
};
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _;
|
||||
use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions;
|
||||
|
||||
use super::borrow_set::BorrowData;
|
||||
use super::MirBorrowckCtxt;
|
||||
|
|
@ -1043,7 +1043,38 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
}
|
||||
CallKind::Normal { self_arg, desugaring, method_did, method_args } => {
|
||||
let self_arg = self_arg.unwrap();
|
||||
let mut has_sugg = false;
|
||||
let tcx = self.infcx.tcx;
|
||||
// Avoid pointing to the same function in multiple different
|
||||
// error messages.
|
||||
if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) {
|
||||
self.explain_iterator_advancement_in_for_loop_if_applicable(
|
||||
err,
|
||||
span,
|
||||
&move_spans,
|
||||
);
|
||||
|
||||
let func = tcx.def_path_str(method_did);
|
||||
err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
|
||||
func,
|
||||
place_name: place_name.clone(),
|
||||
span: self_arg.span,
|
||||
});
|
||||
}
|
||||
let parent_did = tcx.parent(method_did);
|
||||
let parent_self_ty =
|
||||
matches!(tcx.def_kind(parent_did), rustc_hir::def::DefKind::Impl { .. })
|
||||
.then_some(parent_did)
|
||||
.and_then(|did| match tcx.type_of(did).instantiate_identity().kind() {
|
||||
ty::Adt(def, ..) => Some(def.did()),
|
||||
_ => None,
|
||||
});
|
||||
let is_option_or_result = parent_self_ty.is_some_and(|def_id| {
|
||||
matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
|
||||
});
|
||||
if is_option_or_result && maybe_reinitialized_locations_is_empty {
|
||||
err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span });
|
||||
}
|
||||
if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
|
||||
let ty = moved_place.ty(self.body, tcx).ty;
|
||||
let suggest = match tcx.get_diagnostic_item(sym::IntoIterator) {
|
||||
|
|
@ -1108,7 +1139,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
// Erase and shadow everything that could be passed to the new infcx.
|
||||
let ty = moved_place.ty(self.body, tcx).ty;
|
||||
|
||||
if let ty::Adt(def, args) = ty.kind()
|
||||
if let ty::Adt(def, args) = ty.peel_refs().kind()
|
||||
&& Some(def.did()) == tcx.lang_items().pin_type()
|
||||
&& let ty::Ref(_, _, hir::Mutability::Mut) = args.type_at(0).kind()
|
||||
&& let self_ty = self.infcx.instantiate_binder_with_fresh_vars(
|
||||
|
|
@ -1124,56 +1155,76 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
span: move_span.shrink_to_hi(),
|
||||
},
|
||||
);
|
||||
has_sugg = true;
|
||||
}
|
||||
if let Some(clone_trait) = tcx.lang_items().clone_trait()
|
||||
&& let trait_ref = ty::TraitRef::new(tcx, clone_trait, [ty])
|
||||
&& let o = Obligation::new(
|
||||
tcx,
|
||||
ObligationCause::dummy(),
|
||||
self.param_env,
|
||||
ty::Binder::dummy(trait_ref),
|
||||
)
|
||||
&& self.infcx.predicate_must_hold_modulo_regions(&o)
|
||||
{
|
||||
err.span_suggestion_verbose(
|
||||
move_span.shrink_to_hi(),
|
||||
"you can `clone` the value and consume it, but this might not be \
|
||||
your desired behavior",
|
||||
".clone()".to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
if let Some(clone_trait) = tcx.lang_items().clone_trait() {
|
||||
let sugg = if moved_place
|
||||
.iter_projections()
|
||||
.any(|(_, elem)| matches!(elem, ProjectionElem::Deref))
|
||||
{
|
||||
vec![
|
||||
// We use the fully-qualified path because `.clone()` can
|
||||
// sometimes choose `<&T as Clone>` instead of `<T as Clone>`
|
||||
// when going through auto-deref, so this ensures that doesn't
|
||||
// happen, causing suggestions for `.clone().clone()`.
|
||||
(move_span.shrink_to_lo(), format!("<{ty} as Clone>::clone(&")),
|
||||
(move_span.shrink_to_hi(), ")".to_string()),
|
||||
]
|
||||
} else {
|
||||
vec![(move_span.shrink_to_hi(), ".clone()".to_string())]
|
||||
};
|
||||
if let Some(errors) =
|
||||
self.infcx.could_impl_trait(clone_trait, ty, self.param_env)
|
||||
&& !has_sugg
|
||||
{
|
||||
let msg = match &errors[..] {
|
||||
[] => "you can `clone` the value and consume it, but this \
|
||||
might not be your desired behavior"
|
||||
.to_string(),
|
||||
[error] => {
|
||||
format!(
|
||||
"you could `clone` the value and consume it, if \
|
||||
the `{}` trait bound could be satisfied",
|
||||
error.obligation.predicate,
|
||||
)
|
||||
}
|
||||
[errors @ .., last] => {
|
||||
format!(
|
||||
"you could `clone` the value and consume it, if \
|
||||
the following trait bounds could be satisfied: {} \
|
||||
and `{}`",
|
||||
errors
|
||||
.iter()
|
||||
.map(|e| format!("`{}`", e.obligation.predicate))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
last.obligation.predicate,
|
||||
)
|
||||
}
|
||||
};
|
||||
err.multipart_suggestion_verbose(
|
||||
msg,
|
||||
sugg.clone(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
for error in errors {
|
||||
if let FulfillmentErrorCode::CodeSelectionError(
|
||||
SelectionError::Unimplemented,
|
||||
) = error.code
|
||||
&& let ty::PredicateKind::Clause(ty::ClauseKind::Trait(
|
||||
pred,
|
||||
)) = error.obligation.predicate.kind().skip_binder()
|
||||
{
|
||||
self.infcx.err_ctxt().suggest_derive(
|
||||
&error.obligation,
|
||||
err,
|
||||
error.obligation.predicate.kind().rebind(pred),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Avoid pointing to the same function in multiple different
|
||||
// error messages.
|
||||
if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) {
|
||||
self.explain_iterator_advancement_in_for_loop_if_applicable(
|
||||
err,
|
||||
span,
|
||||
&move_spans,
|
||||
);
|
||||
|
||||
let func = tcx.def_path_str(method_did);
|
||||
err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
|
||||
func,
|
||||
place_name,
|
||||
span: self_arg.span,
|
||||
});
|
||||
}
|
||||
let parent_did = tcx.parent(method_did);
|
||||
let parent_self_ty =
|
||||
matches!(tcx.def_kind(parent_did), rustc_hir::def::DefKind::Impl { .. })
|
||||
.then_some(parent_did)
|
||||
.and_then(|did| match tcx.type_of(did).instantiate_identity().kind() {
|
||||
ty::Adt(def, ..) => Some(def.did()),
|
||||
_ => None,
|
||||
});
|
||||
let is_option_or_result = parent_self_ty.is_some_and(|def_id| {
|
||||
matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
|
||||
});
|
||||
if is_option_or_result && maybe_reinitialized_locations_is_empty {
|
||||
err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span });
|
||||
}
|
||||
}
|
||||
// Other desugarings takes &self, which cannot cause a move
|
||||
_ => {}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@ use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed
|
|||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::Node;
|
||||
use rustc_infer::traits;
|
||||
use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
|
||||
use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, InstanceDef, ToPredicate, Ty, TyCtxt};
|
||||
use rustc_middle::{
|
||||
hir::place::PlaceBase,
|
||||
mir::{self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location},
|
||||
|
|
@ -12,6 +13,8 @@ use rustc_middle::{
|
|||
use rustc_span::symbol::{kw, Symbol};
|
||||
use rustc_span::{sym, BytePos, DesugaringKind, Span};
|
||||
use rustc_target::abi::FieldIdx;
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
|
||||
|
||||
use crate::diagnostics::BorrowedContentSource;
|
||||
use crate::util::FindAssignments;
|
||||
|
|
@ -1212,6 +1215,103 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
if let Some(hir_id) = hir_id
|
||||
&& let Some(hir::Node::Local(local)) = hir_map.find(hir_id)
|
||||
{
|
||||
let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap());
|
||||
if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
|
||||
&& let Some(expr) = local.init
|
||||
&& let ty = tables.node_type_opt(expr.hir_id)
|
||||
&& let Some(ty) = ty
|
||||
&& let ty::Ref(..) = ty.kind()
|
||||
{
|
||||
match self
|
||||
.infcx
|
||||
.could_impl_trait(clone_trait, ty.peel_refs(), self.param_env)
|
||||
.as_deref()
|
||||
{
|
||||
Some([]) => {
|
||||
// The type implements Clone.
|
||||
err.span_help(
|
||||
expr.span,
|
||||
format!(
|
||||
"you can `clone` the `{}` value and consume it, but this \
|
||||
might not be your desired behavior",
|
||||
ty.peel_refs(),
|
||||
),
|
||||
);
|
||||
}
|
||||
None => {
|
||||
if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) =
|
||||
expr.kind
|
||||
&& segment.ident.name == sym::clone
|
||||
{
|
||||
err.span_help(
|
||||
span,
|
||||
format!(
|
||||
"`{}` doesn't implement `Clone`, so this call clones \
|
||||
the reference `{ty}`",
|
||||
ty.peel_refs(),
|
||||
),
|
||||
);
|
||||
}
|
||||
// The type doesn't implement Clone.
|
||||
let trait_ref = ty::Binder::dummy(ty::TraitRef::new(
|
||||
self.infcx.tcx,
|
||||
clone_trait,
|
||||
[ty.peel_refs()],
|
||||
));
|
||||
let obligation = traits::Obligation::new(
|
||||
self.infcx.tcx,
|
||||
traits::ObligationCause::dummy(),
|
||||
self.param_env,
|
||||
trait_ref,
|
||||
);
|
||||
self.infcx.err_ctxt().suggest_derive(
|
||||
&obligation,
|
||||
err,
|
||||
trait_ref.to_predicate(self.infcx.tcx),
|
||||
);
|
||||
}
|
||||
Some(errors) => {
|
||||
if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) =
|
||||
expr.kind
|
||||
&& segment.ident.name == sym::clone
|
||||
{
|
||||
err.span_help(
|
||||
span,
|
||||
format!(
|
||||
"`{}` doesn't implement `Clone` because its \
|
||||
implementations trait bounds could not be met, so \
|
||||
this call clones the reference `{ty}`",
|
||||
ty.peel_refs(),
|
||||
),
|
||||
);
|
||||
err.note(format!(
|
||||
"the following trait bounds weren't met: {}",
|
||||
errors
|
||||
.iter()
|
||||
.map(|e| e.obligation.predicate.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"),
|
||||
));
|
||||
}
|
||||
// The type doesn't implement Clone because of unmet obligations.
|
||||
for error in errors {
|
||||
if let traits::FulfillmentErrorCode::CodeSelectionError(
|
||||
traits::SelectionError::Unimplemented,
|
||||
) = error.code
|
||||
&& let ty::PredicateKind::Clause(ty::ClauseKind::Trait(
|
||||
pred,
|
||||
)) = error.obligation.predicate.kind().skip_binder()
|
||||
{
|
||||
self.infcx.err_ctxt().suggest_derive(
|
||||
&error.obligation,
|
||||
err,
|
||||
error.obligation.predicate.kind().rebind(pred),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let (changing, span, sugg) = match local.ty {
|
||||
Some(ty) => ("changing", ty.span, message),
|
||||
None => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use rustc_errors::{
|
||||
AddToDiagnostic, DiagnosticBuilder, EmissionGuarantee, Handler, IntoDiagnostic, MultiSpan,
|
||||
AddToDiagnostic, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic, MultiSpan,
|
||||
SingleLabelManySpans,
|
||||
};
|
||||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||
|
|
@ -446,9 +446,9 @@ pub(crate) struct EnvNotDefinedWithUserMessage {
|
|||
}
|
||||
|
||||
// Hand-written implementation to support custom user messages.
|
||||
impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for EnvNotDefinedWithUserMessage {
|
||||
impl<'a> IntoDiagnostic<'a> for EnvNotDefinedWithUserMessage {
|
||||
#[track_caller]
|
||||
fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, G> {
|
||||
fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
|
||||
#[expect(
|
||||
rustc::untranslatable_diagnostic,
|
||||
reason = "cannot translate user-provided messages"
|
||||
|
|
@ -801,8 +801,8 @@ pub(crate) struct AsmClobberNoReg {
|
|||
pub(crate) clobbers: Vec<Span>,
|
||||
}
|
||||
|
||||
impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for AsmClobberNoReg {
|
||||
fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, G> {
|
||||
impl<'a> IntoDiagnostic<'a> for AsmClobberNoReg {
|
||||
fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
|
||||
let mut diag =
|
||||
handler.struct_diagnostic(crate::fluent_generated::builtin_macros_asm_clobber_no_reg);
|
||||
diag.set_span(self.spans.clone());
|
||||
|
|
|
|||
|
|
@ -541,10 +541,14 @@ fn check_test_signature(
|
|||
return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "unsafe" }));
|
||||
}
|
||||
|
||||
if let ast::Async::Yes { span, .. } = f.sig.header.asyncness {
|
||||
if let Some(ast::CoroutineKind::Async { span, .. }) = f.sig.header.coro_kind {
|
||||
return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "async" }));
|
||||
}
|
||||
|
||||
if let Some(ast::CoroutineKind::Gen { span, .. }) = f.sig.header.coro_kind {
|
||||
return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "gen" }));
|
||||
}
|
||||
|
||||
// If the termination trait is active, the compiler will check that the output
|
||||
// type implements the `Termination` trait as `libtest` enforces that.
|
||||
let has_output = match &f.sig.decl.output {
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ impl ConcurrencyLimiter {
|
|||
// Make sure to drop the mutex guard first to prevent poisoning the mutex.
|
||||
drop(state);
|
||||
if let Some(err) = err {
|
||||
handler.fatal(err).raise();
|
||||
handler.fatal(err);
|
||||
} else {
|
||||
// The error was already emitted, but compilation continued. Raise a silent
|
||||
// fatal error.
|
||||
|
|
|
|||
|
|
@ -126,7 +126,8 @@ pub(crate) fn codegen_const_value<'tcx>(
|
|||
}
|
||||
}
|
||||
Scalar::Ptr(ptr, _size) => {
|
||||
let (alloc_id, offset) = ptr.into_parts(); // we know the `offset` is relative
|
||||
let (prov, offset) = ptr.into_parts(); // we know the `offset` is relative
|
||||
let alloc_id = prov.alloc_id();
|
||||
let base_addr = match fx.tcx.global_alloc(alloc_id) {
|
||||
GlobalAlloc::Memory(alloc) => {
|
||||
let data_id = data_id_for_alloc_id(
|
||||
|
|
@ -374,7 +375,8 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
|
|||
let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()).to_vec();
|
||||
data.define(bytes.into_boxed_slice());
|
||||
|
||||
for &(offset, alloc_id) in alloc.provenance().ptrs().iter() {
|
||||
for &(offset, prov) in alloc.provenance().ptrs().iter() {
|
||||
let alloc_id = prov.alloc_id();
|
||||
let addend = {
|
||||
let endianness = tcx.data_layout.endian;
|
||||
let offset = offset.bytes() as usize;
|
||||
|
|
|
|||
|
|
@ -120,9 +120,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.147"
|
||||
version = "0.2.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
||||
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(internal_features)]
|
||||
#![feature(core_intrinsics, coroutines, coroutine_trait, is_sorted)]
|
||||
|
||||
#[cfg(feature="master")]
|
||||
|
|
|
|||
|
|
@ -199,7 +199,8 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
|||
}
|
||||
}
|
||||
Scalar::Ptr(ptr, _size) => {
|
||||
let (alloc_id, offset) = ptr.into_parts();
|
||||
let (prov, offset) = ptr.into_parts(); // we know the `offset` is relative
|
||||
let alloc_id = prov.alloc_id();
|
||||
let base_addr =
|
||||
match self.tcx.global_alloc(alloc_id) {
|
||||
GlobalAlloc::Memory(alloc) => {
|
||||
|
|
|
|||
|
|
@ -285,7 +285,8 @@ pub fn const_alloc_to_gcc<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, alloc: ConstAl
|
|||
let pointer_size = dl.pointer_size.bytes() as usize;
|
||||
|
||||
let mut next_offset = 0;
|
||||
for &(offset, alloc_id) in alloc.provenance().ptrs().iter() {
|
||||
for &(offset, prov) in alloc.provenance().ptrs().iter() {
|
||||
let alloc_id = prov.alloc_id();
|
||||
let offset = offset.bytes();
|
||||
assert_eq!(offset as usize as u64, offset);
|
||||
let offset = offset as usize;
|
||||
|
|
@ -313,7 +314,7 @@ pub fn const_alloc_to_gcc<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, alloc: ConstAl
|
|||
|
||||
llvals.push(cx.scalar_to_backend(
|
||||
InterpScalar::from_pointer(
|
||||
interpret::Pointer::new(alloc_id, Size::from_bytes(ptr_offset)),
|
||||
interpret::Pointer::new(prov, Size::from_bytes(ptr_offset)),
|
||||
&cx.tcx,
|
||||
),
|
||||
abi::Scalar::Initialized { value: Primitive::Pointer(address_space), valid_range: WrappingRange::full(dl.pointer_size) },
|
||||
|
|
|
|||
|
|
@ -111,8 +111,8 @@ pub(crate) struct TargetFeatureDisableOrEnable<'a> {
|
|||
pub(crate) struct MissingFeatures;
|
||||
|
||||
impl IntoDiagnostic<'_, ErrorGuaranteed> for TargetFeatureDisableOrEnable<'_> {
|
||||
fn into_diagnostic(self, sess: &'_ Handler) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
|
||||
let mut diag = sess.struct_err(fluent::codegen_gcc_target_feature_disable_or_enable);
|
||||
fn into_diagnostic(self, handler: &'_ Handler) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
|
||||
let mut diag = handler.struct_err(fluent::codegen_gcc_target_feature_disable_or_enable);
|
||||
if let Some(span) = self.span {
|
||||
diag.set_span(span);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ fn prepare_lto(
|
|||
};
|
||||
|
||||
let symbol_filter = &|&(ref name, info): &(String, SymbolExportInfo)| {
|
||||
if info.level.is_below_threshold(export_threshold) || info.used {
|
||||
if info.level.is_below_threshold(export_threshold) || info.used || info.used_compiler {
|
||||
Some(CString::new(name.as_str()).unwrap())
|
||||
} else {
|
||||
None
|
||||
|
|
|
|||
|
|
@ -246,8 +246,8 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
|||
}
|
||||
}
|
||||
Scalar::Ptr(ptr, _size) => {
|
||||
let (alloc_id, offset) = ptr.into_parts();
|
||||
let (base_addr, base_addr_space) = match self.tcx.global_alloc(alloc_id) {
|
||||
let (prov, offset) = ptr.into_parts();
|
||||
let (base_addr, base_addr_space) = match self.tcx.global_alloc(prov.alloc_id()) {
|
||||
GlobalAlloc::Memory(alloc) => {
|
||||
let init = const_alloc_to_llvm(self, alloc);
|
||||
let alloc = alloc.inner();
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<
|
|||
}
|
||||
|
||||
let mut next_offset = 0;
|
||||
for &(offset, alloc_id) in alloc.provenance().ptrs().iter() {
|
||||
for &(offset, prov) in alloc.provenance().ptrs().iter() {
|
||||
let offset = offset.bytes();
|
||||
assert_eq!(offset as usize as u64, offset);
|
||||
let offset = offset as usize;
|
||||
|
|
@ -92,13 +92,10 @@ pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<
|
|||
.expect("const_alloc_to_llvm: could not read relocation pointer")
|
||||
as u64;
|
||||
|
||||
let address_space = cx.tcx.global_alloc(alloc_id).address_space(cx);
|
||||
let address_space = cx.tcx.global_alloc(prov.alloc_id()).address_space(cx);
|
||||
|
||||
llvals.push(cx.scalar_to_backend(
|
||||
InterpScalar::from_pointer(
|
||||
Pointer::new(alloc_id, Size::from_bytes(ptr_offset)),
|
||||
&cx.tcx,
|
||||
),
|
||||
InterpScalar::from_pointer(Pointer::new(prov, Size::from_bytes(ptr_offset)), &cx.tcx),
|
||||
Scalar::Initialized {
|
||||
value: Primitive::Pointer(address_space),
|
||||
valid_range: WrappingRange::full(dl.pointer_size),
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use std::path::Path;
|
|||
use crate::fluent_generated as fluent;
|
||||
use rustc_data_structures::small_c_str::SmallCStr;
|
||||
use rustc_errors::{
|
||||
DiagnosticBuilder, EmissionGuarantee, ErrorGuaranteed, Handler, IntoDiagnostic,
|
||||
DiagnosticBuilder, EmissionGuarantee, ErrorGuaranteed, FatalError, Handler, IntoDiagnostic,
|
||||
};
|
||||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||
use rustc_span::Span;
|
||||
|
|
@ -101,13 +101,13 @@ pub(crate) struct DynamicLinkingWithLTO;
|
|||
|
||||
pub(crate) struct ParseTargetMachineConfig<'a>(pub LlvmError<'a>);
|
||||
|
||||
impl<EM: EmissionGuarantee> IntoDiagnostic<'_, EM> for ParseTargetMachineConfig<'_> {
|
||||
fn into_diagnostic(self, sess: &'_ Handler) -> DiagnosticBuilder<'_, EM> {
|
||||
let diag: DiagnosticBuilder<'_, EM> = self.0.into_diagnostic(sess);
|
||||
impl IntoDiagnostic<'_, FatalError> for ParseTargetMachineConfig<'_> {
|
||||
fn into_diagnostic(self, handler: &'_ Handler) -> DiagnosticBuilder<'_, FatalError> {
|
||||
let diag: DiagnosticBuilder<'_, FatalError> = self.0.into_diagnostic(handler);
|
||||
let (message, _) = diag.styled_message().first().expect("`LlvmError` with no message");
|
||||
let message = sess.eagerly_translate_to_string(message.clone(), diag.args());
|
||||
let message = handler.eagerly_translate_to_string(message.clone(), diag.args());
|
||||
|
||||
let mut diag = sess.struct_diagnostic(fluent::codegen_llvm_parse_target_machine_config);
|
||||
let mut diag = handler.struct_diagnostic(fluent::codegen_llvm_parse_target_machine_config);
|
||||
diag.set_arg("error", message);
|
||||
diag
|
||||
}
|
||||
|
|
@ -124,8 +124,8 @@ pub(crate) struct TargetFeatureDisableOrEnable<'a> {
|
|||
pub(crate) struct MissingFeatures;
|
||||
|
||||
impl IntoDiagnostic<'_, ErrorGuaranteed> for TargetFeatureDisableOrEnable<'_> {
|
||||
fn into_diagnostic(self, sess: &'_ Handler) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
|
||||
let mut diag = sess.struct_err(fluent::codegen_llvm_target_feature_disable_or_enable);
|
||||
fn into_diagnostic(self, handler: &'_ Handler) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
|
||||
let mut diag = handler.struct_err(fluent::codegen_llvm_target_feature_disable_or_enable);
|
||||
if let Some(span) = self.span {
|
||||
diag.set_span(span);
|
||||
};
|
||||
|
|
@ -183,8 +183,8 @@ pub enum LlvmError<'a> {
|
|||
|
||||
pub(crate) struct WithLlvmError<'a>(pub LlvmError<'a>, pub String);
|
||||
|
||||
impl<EM: EmissionGuarantee> IntoDiagnostic<'_, EM> for WithLlvmError<'_> {
|
||||
fn into_diagnostic(self, sess: &'_ Handler) -> DiagnosticBuilder<'_, EM> {
|
||||
impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for WithLlvmError<'_> {
|
||||
fn into_diagnostic(self, handler: &'_ Handler) -> DiagnosticBuilder<'_, G> {
|
||||
use LlvmError::*;
|
||||
let msg_with_llvm_err = match &self.0 {
|
||||
WriteOutput { .. } => fluent::codegen_llvm_write_output_with_llvm_err,
|
||||
|
|
@ -201,7 +201,7 @@ impl<EM: EmissionGuarantee> IntoDiagnostic<'_, EM> for WithLlvmError<'_> {
|
|||
PrepareThinLtoModule => fluent::codegen_llvm_prepare_thin_lto_module_with_llvm_err,
|
||||
ParseBitcode => fluent::codegen_llvm_parse_bitcode_with_llvm_err,
|
||||
};
|
||||
let mut diag = self.0.into_diagnostic(sess);
|
||||
let mut diag = self.0.into_diagnostic(handler);
|
||||
diag.set_primary_message(msg_with_llvm_err);
|
||||
diag.set_arg("llvm_err", self.1);
|
||||
diag
|
||||
|
|
|
|||
|
|
@ -105,20 +105,21 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S
|
|||
}
|
||||
})
|
||||
.map(|def_id| {
|
||||
let codegen_attrs = tcx.codegen_fn_attrs(def_id.to_def_id());
|
||||
// We won't link right if this symbol is stripped during LTO.
|
||||
let name = tcx.symbol_name(Instance::mono(tcx, def_id.to_def_id())).name;
|
||||
// We have to preserve the symbols of the built-in functions during LTO.
|
||||
let is_builtin_fn = is_compiler_builtins
|
||||
&& symbol_export_level(tcx, def_id.to_def_id())
|
||||
.is_below_threshold(SymbolExportLevel::C);
|
||||
let used = is_builtin_fn || name == "rust_eh_personality";
|
||||
.is_below_threshold(SymbolExportLevel::C)
|
||||
&& codegen_attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE);
|
||||
let used = name == "rust_eh_personality";
|
||||
|
||||
let export_level = if special_runtime_crate {
|
||||
SymbolExportLevel::Rust
|
||||
} else {
|
||||
symbol_export_level(tcx, def_id.to_def_id())
|
||||
};
|
||||
let codegen_attrs = tcx.codegen_fn_attrs(def_id.to_def_id());
|
||||
debug!(
|
||||
"EXPORTED SYMBOL (local): {} ({:?})",
|
||||
tcx.symbol_name(Instance::mono(tcx, def_id.to_def_id())),
|
||||
|
|
@ -138,6 +139,7 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S
|
|||
used: codegen_attrs.flags.contains(CodegenFnAttrFlags::USED)
|
||||
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
|
||||
|| used,
|
||||
used_compiler: is_builtin_fn,
|
||||
};
|
||||
(def_id.to_def_id(), info)
|
||||
})
|
||||
|
|
@ -150,6 +152,7 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S
|
|||
level: SymbolExportLevel::C,
|
||||
kind: SymbolExportKind::Data,
|
||||
used: false,
|
||||
used_compiler: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -198,6 +201,7 @@ fn exported_symbols_provider_local(
|
|||
level: info.level,
|
||||
kind: SymbolExportKind::Text,
|
||||
used: info.used,
|
||||
used_compiler: false,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
|
@ -214,6 +218,7 @@ fn exported_symbols_provider_local(
|
|||
level: SymbolExportLevel::C,
|
||||
kind: SymbolExportKind::Text,
|
||||
used: false,
|
||||
used_compiler: false,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
|
@ -233,6 +238,7 @@ fn exported_symbols_provider_local(
|
|||
level: SymbolExportLevel::Rust,
|
||||
kind: SymbolExportKind::Text,
|
||||
used: false,
|
||||
used_compiler: false,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
|
@ -245,6 +251,7 @@ fn exported_symbols_provider_local(
|
|||
level: SymbolExportLevel::Rust,
|
||||
kind: SymbolExportKind::Data,
|
||||
used: false,
|
||||
used_compiler: false,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
|
@ -264,6 +271,7 @@ fn exported_symbols_provider_local(
|
|||
level: SymbolExportLevel::C,
|
||||
kind: SymbolExportKind::Data,
|
||||
used: false,
|
||||
used_compiler: false,
|
||||
},
|
||||
)
|
||||
}));
|
||||
|
|
@ -289,6 +297,7 @@ fn exported_symbols_provider_local(
|
|||
level: SymbolExportLevel::C,
|
||||
kind: SymbolExportKind::Data,
|
||||
used: false,
|
||||
used_compiler: false,
|
||||
},
|
||||
)
|
||||
}));
|
||||
|
|
@ -306,6 +315,7 @@ fn exported_symbols_provider_local(
|
|||
level: SymbolExportLevel::C,
|
||||
kind: SymbolExportKind::Data,
|
||||
used: true,
|
||||
used_compiler: false,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
|
@ -346,6 +356,7 @@ fn exported_symbols_provider_local(
|
|||
level: SymbolExportLevel::Rust,
|
||||
kind: SymbolExportKind::Text,
|
||||
used: false,
|
||||
used_compiler: false,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
|
@ -362,6 +373,7 @@ fn exported_symbols_provider_local(
|
|||
level: SymbolExportLevel::Rust,
|
||||
kind: SymbolExportKind::Text,
|
||||
used: false,
|
||||
used_compiler: false,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
|
|||
bug!("from_const: invalid ScalarPair layout: {:#?}", layout);
|
||||
};
|
||||
let a = Scalar::from_pointer(
|
||||
Pointer::new(bx.tcx().reserve_and_set_memory_alloc(data), Size::ZERO),
|
||||
Pointer::new(bx.tcx().reserve_and_set_memory_alloc(data).into(), Size::ZERO),
|
||||
&bx.tcx(),
|
||||
);
|
||||
let a_llval = bx.scalar_to_backend(
|
||||
|
|
|
|||
|
|
@ -461,6 +461,9 @@ const_eval_validation_uninhabited_val = {$front_matter}: encountered a value of
|
|||
const_eval_validation_uninit = {$front_matter}: encountered uninitialized memory, but {$expected}
|
||||
const_eval_validation_unsafe_cell = {$front_matter}: encountered `UnsafeCell` in a `const`
|
||||
|
||||
const_eval_write_through_immutable_pointer =
|
||||
writing through a pointer that was derived from a shared (immutable) reference
|
||||
|
||||
const_eval_write_to_read_only =
|
||||
writing to {$allocation} which is read-only
|
||||
const_eval_zst_pointer_out_of_bounds =
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
use std::mem;
|
||||
|
||||
use rustc_errors::{DiagnosticArgValue, DiagnosticMessage, IntoDiagnostic, IntoDiagnosticArg};
|
||||
use rustc_hir::CRATE_HIR_ID;
|
||||
use rustc_middle::mir::AssertKind;
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_middle::ty::{layout::LayoutError, ConstInt};
|
||||
use rustc_span::{ErrorGuaranteed, Span, Symbol, DUMMY_SP};
|
||||
|
||||
use super::InterpCx;
|
||||
use super::{CompileTimeInterpreter, InterpCx};
|
||||
use crate::errors::{self, FrameNote, ReportErrorExt};
|
||||
use crate::interpret::{ErrorHandled, InterpError, InterpErrorInfo, Machine, MachineStopType};
|
||||
use crate::interpret::{ErrorHandled, InterpError, InterpErrorInfo, MachineStopType};
|
||||
|
||||
/// The CTFE machine has some custom error kinds.
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -57,16 +59,20 @@ impl<'tcx> Into<InterpErrorInfo<'tcx>> for ConstEvalErrKind {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_span_and_frames<'tcx, 'mir, M: Machine<'mir, 'tcx>>(
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
pub fn get_span_and_frames<'tcx, 'mir>(
|
||||
tcx: TyCtxtAt<'tcx>,
|
||||
machine: &CompileTimeInterpreter<'mir, 'tcx>,
|
||||
) -> (Span, Vec<errors::FrameNote>)
|
||||
where
|
||||
'tcx: 'mir,
|
||||
{
|
||||
let mut stacktrace = ecx.generate_stacktrace();
|
||||
let mut stacktrace =
|
||||
InterpCx::<CompileTimeInterpreter<'mir, 'tcx>>::generate_stacktrace_from_stack(
|
||||
&machine.stack,
|
||||
);
|
||||
// Filter out `requires_caller_location` frames.
|
||||
stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*ecx.tcx));
|
||||
let span = stacktrace.first().map(|f| f.span).unwrap_or(ecx.tcx.span);
|
||||
stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*tcx));
|
||||
let span = stacktrace.first().map(|f| f.span).unwrap_or(tcx.span);
|
||||
|
||||
let mut frames = Vec::new();
|
||||
|
||||
|
|
@ -87,7 +93,7 @@ where
|
|||
|
||||
let mut last_frame: Option<errors::FrameNote> = None;
|
||||
for frame_info in &stacktrace {
|
||||
let frame = frame_info.as_note(*ecx.tcx);
|
||||
let frame = frame_info.as_note(*tcx);
|
||||
match last_frame.as_mut() {
|
||||
Some(last_frame)
|
||||
if last_frame.span == frame.span
|
||||
|
|
@ -156,3 +162,25 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit a lint from a const-eval situation.
|
||||
// Even if this is unused, please don't remove it -- chances are we will need to emit a lint during const-eval again in the future!
|
||||
pub(super) fn lint<'tcx, 'mir, L>(
|
||||
tcx: TyCtxtAt<'tcx>,
|
||||
machine: &CompileTimeInterpreter<'mir, 'tcx>,
|
||||
lint: &'static rustc_session::lint::Lint,
|
||||
decorator: impl FnOnce(Vec<errors::FrameNote>) -> L,
|
||||
) where
|
||||
L: for<'a> rustc_errors::DecorateLint<'a, ()>,
|
||||
{
|
||||
let (span, frames) = get_span_and_frames(tcx, machine);
|
||||
|
||||
tcx.emit_spanned_lint(
|
||||
lint,
|
||||
// We use the root frame for this so the crate that defines the const defines whether the
|
||||
// lint is emitted.
|
||||
machine.stack.first().and_then(|frame| frame.lint_root()).unwrap_or(CRATE_HIR_ID),
|
||||
span,
|
||||
decorator(frames),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,8 +155,8 @@ pub(super) fn op_to_const<'tcx>(
|
|||
match immediate {
|
||||
Left(ref mplace) => {
|
||||
// We know `offset` is relative to the allocation, so we can use `into_parts`.
|
||||
let (alloc_id, offset) = mplace.ptr().into_parts();
|
||||
let alloc_id = alloc_id.expect("cannot have `fake` place fot non-ZST type");
|
||||
let (prov, offset) = mplace.ptr().into_parts();
|
||||
let alloc_id = prov.expect("cannot have `fake` place for non-ZST type").alloc_id();
|
||||
ConstValue::Indirect { alloc_id, offset }
|
||||
}
|
||||
// see comment on `let force_as_immediate` above
|
||||
|
|
@ -178,8 +178,8 @@ pub(super) fn op_to_const<'tcx>(
|
|||
);
|
||||
let msg = "`op_to_const` on an immediate scalar pair must only be used on slice references to the beginning of an actual allocation";
|
||||
// We know `offset` is relative to the allocation, so we can use `into_parts`.
|
||||
let (alloc_id, offset) = a.to_pointer(ecx).expect(msg).into_parts();
|
||||
let alloc_id = alloc_id.expect(msg);
|
||||
let (prov, offset) = a.to_pointer(ecx).expect(msg).into_parts();
|
||||
let alloc_id = prov.expect(msg).alloc_id();
|
||||
let data = ecx.tcx.global_alloc(alloc_id).unwrap_memory();
|
||||
assert!(offset == abi::Size::ZERO, "{}", msg);
|
||||
let meta = b.to_target_usize(ecx).expect(msg);
|
||||
|
|
@ -338,7 +338,7 @@ pub fn eval_in_interpreter<'mir, 'tcx>(
|
|||
*ecx.tcx,
|
||||
error,
|
||||
None,
|
||||
|| super::get_span_and_frames(&ecx),
|
||||
|| super::get_span_and_frames(ecx.tcx, &ecx.machine),
|
||||
|span, frames| ConstEvalError {
|
||||
span,
|
||||
error_kind: kind,
|
||||
|
|
@ -353,7 +353,7 @@ pub fn eval_in_interpreter<'mir, 'tcx>(
|
|||
let validation =
|
||||
const_validate_mplace(&ecx, &mplace, is_static, cid.promoted.is_some());
|
||||
|
||||
let alloc_id = mplace.ptr().provenance.unwrap();
|
||||
let alloc_id = mplace.ptr().provenance.unwrap().alloc_id();
|
||||
|
||||
// Validation failed, report an error.
|
||||
if let Err(error) = validation {
|
||||
|
|
@ -419,7 +419,7 @@ pub fn const_report_error<'mir, 'tcx>(
|
|||
*ecx.tcx,
|
||||
error,
|
||||
None,
|
||||
|| crate::const_eval::get_span_and_frames(ecx),
|
||||
|| crate::const_eval::get_span_and_frames(ecx.tcx, &ecx.machine),
|
||||
move |span, frames| errors::UndefinedBehavior { span, ub_note, frames, raw_bytes },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,30 @@
|
|||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::PointerArithmetic;
|
||||
use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_span::Span;
|
||||
use std::borrow::Borrow;
|
||||
use std::fmt;
|
||||
use std::hash::Hash;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::fx::IndexEntry;
|
||||
use std::fmt;
|
||||
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::AssertMessage;
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout};
|
||||
use rustc_session::lint::builtin::WRITES_THROUGH_IMMUTABLE_POINTER;
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_span::Span;
|
||||
use rustc_target::abi::{Align, Size};
|
||||
use rustc_target::spec::abi::Abi as CallAbi;
|
||||
|
||||
use crate::errors::{LongRunning, LongRunningWarn};
|
||||
use crate::fluent_generated as fluent;
|
||||
use crate::interpret::{
|
||||
self, compile_time_machine, AllocId, ConstAllocation, FnArg, FnVal, Frame, ImmTy, InterpCx,
|
||||
InterpResult, OpTy, PlaceTy, Pointer, Scalar,
|
||||
self, compile_time_machine, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, FnVal,
|
||||
Frame, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, PointerArithmetic, Scalar,
|
||||
};
|
||||
|
||||
use super::error::*;
|
||||
|
|
@ -49,7 +49,7 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
|
|||
pub(super) num_evaluated_steps: usize,
|
||||
|
||||
/// The virtual call stack.
|
||||
pub(super) stack: Vec<Frame<'mir, 'tcx, AllocId, ()>>,
|
||||
pub(super) stack: Vec<Frame<'mir, 'tcx>>,
|
||||
|
||||
/// We need to make sure consts never point to anything mutable, even recursively. That is
|
||||
/// relied on for pattern matching on consts with references.
|
||||
|
|
@ -638,10 +638,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn expose_ptr(
|
||||
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||
_ptr: Pointer<AllocId>,
|
||||
) -> InterpResult<'tcx> {
|
||||
fn expose_ptr(_ecx: &mut InterpCx<'mir, 'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx> {
|
||||
// This is only reachable with -Zunleash-the-miri-inside-of-you.
|
||||
throw_unsup_format!("exposing pointers is not possible at compile-time")
|
||||
}
|
||||
|
|
@ -674,7 +671,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
|||
}
|
||||
|
||||
fn before_access_global(
|
||||
_tcx: TyCtxt<'tcx>,
|
||||
_tcx: TyCtxtAt<'tcx>,
|
||||
machine: &Self,
|
||||
alloc_id: AllocId,
|
||||
alloc: ConstAllocation<'tcx>,
|
||||
|
|
@ -711,6 +708,48 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn retag_ptr_value(
|
||||
ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||
_kind: mir::RetagKind,
|
||||
val: &ImmTy<'tcx, CtfeProvenance>,
|
||||
) -> InterpResult<'tcx, ImmTy<'tcx, CtfeProvenance>> {
|
||||
// If it's a frozen shared reference that's not already immutable, make it immutable.
|
||||
// (Do nothing on `None` provenance, that cannot store immutability anyway.)
|
||||
if let ty::Ref(_, ty, mutbl) = val.layout.ty.kind()
|
||||
&& *mutbl == Mutability::Not
|
||||
&& val.to_scalar_and_meta().0.to_pointer(ecx)?.provenance.is_some_and(|p| !p.immutable())
|
||||
// That next check is expensive, that's why we have all the guards above.
|
||||
&& ty.is_freeze(*ecx.tcx, ecx.param_env)
|
||||
{
|
||||
let place = ecx.ref_to_mplace(val)?;
|
||||
let new_place = place.map_provenance(|p| p.map(CtfeProvenance::as_immutable));
|
||||
Ok(ImmTy::from_immediate(new_place.to_ref(ecx), val.layout))
|
||||
} else {
|
||||
Ok(val.clone())
|
||||
}
|
||||
}
|
||||
|
||||
fn before_memory_write(
|
||||
tcx: TyCtxtAt<'tcx>,
|
||||
machine: &mut Self,
|
||||
_alloc_extra: &mut Self::AllocExtra,
|
||||
(_alloc_id, immutable): (AllocId, bool),
|
||||
range: AllocRange,
|
||||
) -> InterpResult<'tcx> {
|
||||
if range.size == Size::ZERO {
|
||||
// Nothing to check.
|
||||
return Ok(());
|
||||
}
|
||||
// Reject writes through immutable pointers.
|
||||
if immutable {
|
||||
super::lint(tcx, machine, WRITES_THROUGH_IMMUTABLE_POINTER, |frames| {
|
||||
crate::errors::WriteThroughImmutablePointer { frames }
|
||||
});
|
||||
}
|
||||
// Everything else is fine.
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Please do not add any code below the above `Machine` trait impl. I (oli-obk) plan more cleanups
|
||||
|
|
|
|||
|
|
@ -402,6 +402,13 @@ pub struct ConstEvalError {
|
|||
pub frame_notes: Vec<FrameNote>,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(const_eval_write_through_immutable_pointer)]
|
||||
pub struct WriteThroughImmutablePointer {
|
||||
#[subdiagnostic]
|
||||
pub frames: Vec<FrameNote>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_nullary_intrinsic_fail)]
|
||||
pub struct NullaryIntrinsicError {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ use hir::CRATE_HIR_ID;
|
|||
use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo};
|
||||
use rustc_middle::mir::interpret::{
|
||||
CtfeProvenance, ErrorHandled, InvalidMetaKind, ReportedErrorInfo,
|
||||
};
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::ty::layout::{
|
||||
self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers,
|
||||
|
|
@ -20,9 +22,9 @@ use rustc_span::Span;
|
|||
use rustc_target::abi::{call::FnAbi, Align, HasDataLayout, Size, TargetDataLayout};
|
||||
|
||||
use super::{
|
||||
AllocId, GlobalId, Immediate, InterpErrorInfo, InterpResult, MPlaceTy, Machine, MemPlace,
|
||||
MemPlaceMeta, Memory, MemoryKind, OpTy, Operand, Place, PlaceTy, Pointer, PointerArithmetic,
|
||||
Projectable, Provenance, Scalar, StackPopJump,
|
||||
GlobalId, Immediate, InterpErrorInfo, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta,
|
||||
Memory, MemoryKind, OpTy, Operand, Place, PlaceTy, Pointer, PointerArithmetic, Projectable,
|
||||
Provenance, Scalar, StackPopJump,
|
||||
};
|
||||
use crate::errors;
|
||||
use crate::util;
|
||||
|
|
@ -84,7 +86,7 @@ impl Drop for SpanGuard {
|
|||
}
|
||||
|
||||
/// A stack frame.
|
||||
pub struct Frame<'mir, 'tcx, Prov: Provenance = AllocId, Extra = ()> {
|
||||
pub struct Frame<'mir, 'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> {
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Function and callsite information
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -156,7 +158,7 @@ pub enum StackPopCleanup {
|
|||
|
||||
/// State of a local variable including a memoized layout
|
||||
#[derive(Clone)]
|
||||
pub struct LocalState<'tcx, Prov: Provenance = AllocId> {
|
||||
pub struct LocalState<'tcx, Prov: Provenance = CtfeProvenance> {
|
||||
value: LocalValue<Prov>,
|
||||
/// Don't modify if `Some`, this is only used to prevent computing the layout twice.
|
||||
/// Avoids computing the layout of locals that are never actually initialized.
|
||||
|
|
@ -177,7 +179,7 @@ impl<Prov: Provenance> std::fmt::Debug for LocalState<'_, Prov> {
|
|||
/// This does not store the type of the local; the type is given by `body.local_decls` and can never
|
||||
/// change, so by not storing here we avoid having to maintain that as an invariant.
|
||||
#[derive(Copy, Clone, Debug)] // Miri debug-prints these
|
||||
pub(super) enum LocalValue<Prov: Provenance = AllocId> {
|
||||
pub(super) enum LocalValue<Prov: Provenance = CtfeProvenance> {
|
||||
/// This local is not currently alive, and cannot be used at all.
|
||||
Dead,
|
||||
/// A normal, live local.
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use super::validity::RefTracking;
|
|||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::mir::interpret::InterpResult;
|
||||
use rustc_middle::mir::interpret::{CtfeProvenance, InterpResult};
|
||||
use rustc_middle::ty::{self, layout::TyAndLayout, Ty};
|
||||
|
||||
use rustc_ast::Mutability;
|
||||
|
|
@ -34,7 +34,7 @@ pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T> = Machine<
|
|||
'mir,
|
||||
'tcx,
|
||||
MemoryKind = T,
|
||||
Provenance = AllocId,
|
||||
Provenance = CtfeProvenance,
|
||||
ExtraFnVal = !,
|
||||
FrameExtra = (),
|
||||
AllocExtra = (),
|
||||
|
|
@ -135,7 +135,7 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_eval:
|
|||
alloc.mutability = Mutability::Not;
|
||||
};
|
||||
// link the alloc id to the actual allocation
|
||||
leftover_allocations.extend(alloc.provenance().ptrs().iter().map(|&(_, alloc_id)| alloc_id));
|
||||
leftover_allocations.extend(alloc.provenance().ptrs().iter().map(|&(_, prov)| prov.alloc_id()));
|
||||
let alloc = tcx.mk_const_alloc(alloc);
|
||||
tcx.set_alloc_id_memory(alloc_id, alloc);
|
||||
None
|
||||
|
|
@ -178,10 +178,10 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
|
|||
tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind()
|
||||
{
|
||||
let ptr = mplace.meta().unwrap_meta().to_pointer(&tcx)?;
|
||||
if let Some(alloc_id) = ptr.provenance {
|
||||
if let Some(prov) = ptr.provenance {
|
||||
// Explicitly choose const mode here, since vtables are immutable, even
|
||||
// if the reference of the fat pointer is mutable.
|
||||
self.intern_shallow(alloc_id, InternMode::Const, None);
|
||||
self.intern_shallow(prov.alloc_id(), InternMode::Const, None);
|
||||
} else {
|
||||
// Validation will error (with a better message) on an invalid vtable pointer.
|
||||
// Let validation show the error message, but make sure it *does* error.
|
||||
|
|
@ -191,7 +191,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
|
|||
}
|
||||
// Check if we have encountered this pointer+layout combination before.
|
||||
// Only recurse for allocation-backed pointers.
|
||||
if let Some(alloc_id) = mplace.ptr().provenance {
|
||||
if let Some(prov) = mplace.ptr().provenance {
|
||||
// Compute the mode with which we intern this. Our goal here is to make as many
|
||||
// statics as we can immutable so they can be placed in read-only memory by LLVM.
|
||||
let ref_mode = match self.mode {
|
||||
|
|
@ -234,7 +234,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
|
|||
InternMode::Const
|
||||
}
|
||||
};
|
||||
match self.intern_shallow(alloc_id, ref_mode, Some(referenced_ty)) {
|
||||
match self.intern_shallow(prov.alloc_id(), ref_mode, Some(referenced_ty)) {
|
||||
// No need to recurse, these are interned already and statics may have
|
||||
// cycles, so we don't want to recurse there
|
||||
Some(IsStaticOrFn) => {}
|
||||
|
|
@ -353,7 +353,7 @@ pub fn intern_const_alloc_recursive<
|
|||
leftover_allocations,
|
||||
// The outermost allocation must exist, because we allocated it with
|
||||
// `Memory::allocate`.
|
||||
ret.ptr().provenance.unwrap(),
|
||||
ret.ptr().provenance.unwrap().alloc_id(),
|
||||
base_intern_mode,
|
||||
Some(ret.layout.ty),
|
||||
);
|
||||
|
|
@ -431,7 +431,8 @@ pub fn intern_const_alloc_recursive<
|
|||
}
|
||||
let alloc = tcx.mk_const_alloc(alloc);
|
||||
tcx.set_alloc_id_memory(alloc_id, alloc);
|
||||
for &(_, alloc_id) in alloc.inner().provenance().ptrs().iter() {
|
||||
for &(_, prov) in alloc.inner().provenance().ptrs().iter() {
|
||||
let alloc_id = prov.alloc_id();
|
||||
if leftover_allocations.insert(alloc_id) {
|
||||
todo.push(alloc_id);
|
||||
}
|
||||
|
|
@ -503,10 +504,11 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>>
|
|||
// `allocate` picks a fresh AllocId that we will associate with its data below.
|
||||
let dest = self.allocate(layout, MemoryKind::Stack)?;
|
||||
f(self, &dest.clone().into())?;
|
||||
let mut alloc = self.memory.alloc_map.remove(&dest.ptr().provenance.unwrap()).unwrap().1;
|
||||
let mut alloc =
|
||||
self.memory.alloc_map.remove(&dest.ptr().provenance.unwrap().alloc_id()).unwrap().1;
|
||||
alloc.mutability = Mutability::Not;
|
||||
let alloc = self.tcx.mk_const_alloc(alloc);
|
||||
let alloc_id = dest.ptr().provenance.unwrap(); // this was just allocated, it must have provenance
|
||||
let alloc_id = dest.ptr().provenance.unwrap().alloc_id(); // this was just allocated, it must have provenance
|
||||
self.tcx.set_alloc_id_memory(alloc_id, alloc);
|
||||
Ok(alloc_id)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,15 +9,17 @@ use std::hash::Hash;
|
|||
use rustc_apfloat::{Float, FloatConvert};
|
||||
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_target::abi::{Align, Size};
|
||||
use rustc_target::spec::abi::Abi as CallAbi;
|
||||
|
||||
use super::{
|
||||
AllocBytes, AllocId, AllocKind, AllocRange, Allocation, ConstAllocation, FnArg, Frame, ImmTy,
|
||||
InterpCx, InterpResult, MPlaceTy, MemoryKind, Misalignment, OpTy, PlaceTy, Pointer, Provenance,
|
||||
AllocBytes, AllocId, AllocKind, AllocRange, Allocation, ConstAllocation, CtfeProvenance, FnArg,
|
||||
Frame, ImmTy, InterpCx, InterpResult, MPlaceTy, MemoryKind, Misalignment, OpTy, PlaceTy,
|
||||
Pointer, Provenance,
|
||||
};
|
||||
|
||||
/// Data returned by Machine::stack_pop,
|
||||
|
|
@ -292,7 +294,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
|
|||
/// `def_id` is `Some` if this is the "lazy" allocation of a static.
|
||||
#[inline]
|
||||
fn before_access_global(
|
||||
_tcx: TyCtxt<'tcx>,
|
||||
_tcx: TyCtxtAt<'tcx>,
|
||||
_machine: &Self,
|
||||
_alloc_id: AllocId,
|
||||
_allocation: ConstAllocation<'tcx>,
|
||||
|
|
@ -387,7 +389,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
|
|||
/// need to mutate.
|
||||
#[inline(always)]
|
||||
fn before_memory_read(
|
||||
_tcx: TyCtxt<'tcx>,
|
||||
_tcx: TyCtxtAt<'tcx>,
|
||||
_machine: &Self,
|
||||
_alloc_extra: &Self::AllocExtra,
|
||||
_prov: (AllocId, Self::ProvenanceExtra),
|
||||
|
|
@ -399,7 +401,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
|
|||
/// Hook for performing extra checks on a memory write access.
|
||||
#[inline(always)]
|
||||
fn before_memory_write(
|
||||
_tcx: TyCtxt<'tcx>,
|
||||
_tcx: TyCtxtAt<'tcx>,
|
||||
_machine: &mut Self,
|
||||
_alloc_extra: &mut Self::AllocExtra,
|
||||
_prov: (AllocId, Self::ProvenanceExtra),
|
||||
|
|
@ -411,7 +413,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
|
|||
/// Hook for performing extra operations on a memory deallocation.
|
||||
#[inline(always)]
|
||||
fn before_memory_deallocation(
|
||||
_tcx: TyCtxt<'tcx>,
|
||||
_tcx: TyCtxtAt<'tcx>,
|
||||
_machine: &mut Self,
|
||||
_alloc_extra: &mut Self::AllocExtra,
|
||||
_prov: (AllocId, Self::ProvenanceExtra),
|
||||
|
|
@ -513,8 +515,8 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
|
|||
/// A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines
|
||||
/// (CTFE and ConstProp) use the same instance. Here, we share that code.
|
||||
pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
|
||||
type Provenance = AllocId;
|
||||
type ProvenanceExtra = ();
|
||||
type Provenance = CtfeProvenance;
|
||||
type ProvenanceExtra = bool; // the "immutable" flag
|
||||
|
||||
type ExtraFnVal = !;
|
||||
|
||||
|
|
@ -567,14 +569,14 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
|
|||
def_id: DefId,
|
||||
) -> InterpResult<$tcx, Pointer> {
|
||||
// Use the `AllocId` associated with the `DefId`. Any actual *access* will fail.
|
||||
Ok(Pointer::new(ecx.tcx.reserve_and_set_static_alloc(def_id), Size::ZERO))
|
||||
Ok(Pointer::new(ecx.tcx.reserve_and_set_static_alloc(def_id).into(), Size::ZERO))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn adjust_alloc_base_pointer(
|
||||
_ecx: &InterpCx<$mir, $tcx, Self>,
|
||||
ptr: Pointer<AllocId>,
|
||||
) -> InterpResult<$tcx, Pointer<AllocId>> {
|
||||
ptr: Pointer<CtfeProvenance>,
|
||||
) -> InterpResult<$tcx, Pointer<CtfeProvenance>> {
|
||||
Ok(ptr)
|
||||
}
|
||||
|
||||
|
|
@ -582,7 +584,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
|
|||
fn ptr_from_addr_cast(
|
||||
_ecx: &InterpCx<$mir, $tcx, Self>,
|
||||
addr: u64,
|
||||
) -> InterpResult<$tcx, Pointer<Option<AllocId>>> {
|
||||
) -> InterpResult<$tcx, Pointer<Option<CtfeProvenance>>> {
|
||||
// Allow these casts, but make the pointer not dereferenceable.
|
||||
// (I.e., they behave like transmutation.)
|
||||
// This is correct because no pointers can ever be exposed in compile-time evaluation.
|
||||
|
|
@ -592,10 +594,10 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
|
|||
#[inline(always)]
|
||||
fn ptr_get_alloc(
|
||||
_ecx: &InterpCx<$mir, $tcx, Self>,
|
||||
ptr: Pointer<AllocId>,
|
||||
ptr: Pointer<CtfeProvenance>,
|
||||
) -> Option<(AllocId, Size, Self::ProvenanceExtra)> {
|
||||
// We know `offset` is relative to the allocation, so we can use `into_parts`.
|
||||
let (alloc_id, offset) = ptr.into_parts();
|
||||
Some((alloc_id, offset, ()))
|
||||
let (prov, offset) = ptr.into_parts();
|
||||
Some((prov.alloc_id(), offset, prov.immutable()))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ use crate::fluent_generated as fluent;
|
|||
|
||||
use super::{
|
||||
alloc_range, AllocBytes, AllocId, AllocMap, AllocRange, Allocation, CheckAlignMsg,
|
||||
CheckInAllocMsg, GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, Misalignment, Pointer,
|
||||
PointerArithmetic, Provenance, Scalar,
|
||||
CheckInAllocMsg, CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak,
|
||||
Misalignment, Pointer, PointerArithmetic, Provenance, Scalar,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
|
|
@ -159,9 +159,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
#[inline]
|
||||
pub fn global_base_pointer(
|
||||
&self,
|
||||
ptr: Pointer<AllocId>,
|
||||
ptr: Pointer<CtfeProvenance>,
|
||||
) -> InterpResult<'tcx, Pointer<M::Provenance>> {
|
||||
let alloc_id = ptr.provenance;
|
||||
let alloc_id = ptr.provenance.alloc_id();
|
||||
// We need to handle `extern static`.
|
||||
match self.tcx.try_get_global_alloc(alloc_id) {
|
||||
Some(GlobalAlloc::Static(def_id)) if self.tcx.is_thread_local_static(def_id) => {
|
||||
|
|
@ -339,7 +339,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
// Let the machine take some extra action
|
||||
let size = alloc.size();
|
||||
M::before_memory_deallocation(
|
||||
*self.tcx,
|
||||
self.tcx,
|
||||
&mut self.machine,
|
||||
&mut alloc.extra,
|
||||
(alloc_id, prov),
|
||||
|
|
@ -561,7 +561,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
(val, Some(def_id))
|
||||
}
|
||||
};
|
||||
M::before_access_global(*self.tcx, &self.machine, id, alloc, def_id, is_write)?;
|
||||
M::before_access_global(self.tcx, &self.machine, id, alloc, def_id, is_write)?;
|
||||
// We got tcx memory. Let the machine initialize its "extra" stuff.
|
||||
M::adjust_allocation(
|
||||
self,
|
||||
|
|
@ -626,7 +626,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
)?;
|
||||
if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc {
|
||||
let range = alloc_range(offset, size);
|
||||
M::before_memory_read(*self.tcx, &self.machine, &alloc.extra, (alloc_id, prov), range)?;
|
||||
M::before_memory_read(self.tcx, &self.machine, &alloc.extra, (alloc_id, prov), range)?;
|
||||
Ok(Some(AllocRef { alloc, range, tcx: *self.tcx, alloc_id }))
|
||||
} else {
|
||||
// Even in this branch we have to be sure that we actually access the allocation, in
|
||||
|
|
@ -687,13 +687,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
{
|
||||
let parts = self.get_ptr_access(ptr, size)?;
|
||||
if let Some((alloc_id, offset, prov)) = parts {
|
||||
let tcx = *self.tcx;
|
||||
let tcx = self.tcx;
|
||||
// FIXME: can we somehow avoid looking up the allocation twice here?
|
||||
// We cannot call `get_raw_mut` inside `check_and_deref_ptr` as that would duplicate `&mut self`.
|
||||
let (alloc, machine) = self.get_alloc_raw_mut(alloc_id)?;
|
||||
let range = alloc_range(offset, size);
|
||||
M::before_memory_write(tcx, machine, &mut alloc.extra, (alloc_id, prov), range)?;
|
||||
Ok(Some(AllocRefMut { alloc, range, tcx, alloc_id }))
|
||||
Ok(Some(AllocRefMut { alloc, range, tcx: *tcx, alloc_id }))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
|
@ -1133,7 +1133,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
let src_alloc = self.get_alloc_raw(src_alloc_id)?;
|
||||
let src_range = alloc_range(src_offset, size);
|
||||
M::before_memory_read(
|
||||
*tcx,
|
||||
tcx,
|
||||
&self.machine,
|
||||
&src_alloc.extra,
|
||||
(src_alloc_id, src_prov),
|
||||
|
|
@ -1163,7 +1163,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
let (dest_alloc, extra) = self.get_alloc_raw_mut(dest_alloc_id)?;
|
||||
let dest_range = alloc_range(dest_offset, size * num_copies);
|
||||
M::before_memory_write(
|
||||
*tcx,
|
||||
tcx,
|
||||
extra,
|
||||
&mut dest_alloc.extra,
|
||||
(dest_alloc_id, dest_prov),
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ use rustc_middle::{mir, ty};
|
|||
use rustc_target::abi::{self, Abi, HasDataLayout, Size};
|
||||
|
||||
use super::{
|
||||
alloc_range, from_known_layout, mir_assign_valid_types, AllocId, Frame, InterpCx, InterpResult,
|
||||
MPlaceTy, Machine, MemPlace, MemPlaceMeta, OffsetMode, PlaceTy, Pointer, Projectable,
|
||||
Provenance, Scalar,
|
||||
alloc_range, from_known_layout, mir_assign_valid_types, CtfeProvenance, Frame, InterpCx,
|
||||
InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, OffsetMode, PlaceTy, Pointer,
|
||||
Projectable, Provenance, Scalar,
|
||||
};
|
||||
|
||||
/// An `Immediate` represents a single immediate self-contained Rust value.
|
||||
|
|
@ -26,7 +26,7 @@ use super::{
|
|||
/// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely
|
||||
/// defined on `Immediate`, and do not have to work with a `Place`.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Immediate<Prov: Provenance = AllocId> {
|
||||
pub enum Immediate<Prov: Provenance = CtfeProvenance> {
|
||||
/// A single scalar value (must have *initialized* `Scalar` ABI).
|
||||
Scalar(Scalar<Prov>),
|
||||
/// A pair of two scalar value (must have `ScalarPair` ABI where both fields are
|
||||
|
|
@ -93,12 +93,23 @@ impl<Prov: Provenance> Immediate<Prov> {
|
|||
Immediate::Uninit => bug!("Got uninit where a scalar pair was expected"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the scalar from the first component and optionally the 2nd component as metadata.
|
||||
#[inline]
|
||||
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
|
||||
pub fn to_scalar_and_meta(self) -> (Scalar<Prov>, MemPlaceMeta<Prov>) {
|
||||
match self {
|
||||
Immediate::ScalarPair(val1, val2) => (val1, MemPlaceMeta::Meta(val2)),
|
||||
Immediate::Scalar(val) => (val, MemPlaceMeta::None),
|
||||
Immediate::Uninit => bug!("Got uninit where a scalar or scalar pair was expected"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ScalarPair needs a type to interpret, so we often have an immediate and a type together
|
||||
// as input for binary and cast operations.
|
||||
#[derive(Clone)]
|
||||
pub struct ImmTy<'tcx, Prov: Provenance = AllocId> {
|
||||
pub struct ImmTy<'tcx, Prov: Provenance = CtfeProvenance> {
|
||||
imm: Immediate<Prov>,
|
||||
pub layout: TyAndLayout<'tcx>,
|
||||
}
|
||||
|
|
@ -334,13 +345,13 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for ImmTy<'tcx, Prov> {
|
|||
/// or still in memory. The latter is an optimization, to delay reading that chunk of
|
||||
/// memory and to avoid having to store arbitrary-sized data here.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(super) enum Operand<Prov: Provenance = AllocId> {
|
||||
pub(super) enum Operand<Prov: Provenance = CtfeProvenance> {
|
||||
Immediate(Immediate<Prov>),
|
||||
Indirect(MemPlace<Prov>),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OpTy<'tcx, Prov: Provenance = AllocId> {
|
||||
pub struct OpTy<'tcx, Prov: Provenance = CtfeProvenance> {
|
||||
op: Operand<Prov>, // Keep this private; it helps enforce invariants.
|
||||
pub layout: TyAndLayout<'tcx>,
|
||||
}
|
||||
|
|
@ -750,17 +761,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
let layout = from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty))?;
|
||||
let imm = match val_val {
|
||||
mir::ConstValue::Indirect { alloc_id, offset } => {
|
||||
// We rely on mutability being set correctly in that allocation to prevent writes
|
||||
// where none should happen.
|
||||
let ptr = self.global_base_pointer(Pointer::new(alloc_id, offset))?;
|
||||
// This is const data, no mutation allowed.
|
||||
let ptr = self.global_base_pointer(Pointer::new(
|
||||
CtfeProvenance::from(alloc_id).as_immutable(),
|
||||
offset,
|
||||
))?;
|
||||
return Ok(self.ptr_to_mplace(ptr.into(), layout).into());
|
||||
}
|
||||
mir::ConstValue::Scalar(x) => adjust_scalar(x)?.into(),
|
||||
mir::ConstValue::ZeroSized => Immediate::Uninit,
|
||||
mir::ConstValue::Slice { data, meta } => {
|
||||
// We rely on mutability being set correctly in `data` to prevent writes
|
||||
// where none should happen.
|
||||
let ptr = Pointer::new(self.tcx.reserve_and_set_memory_alloc(data), Size::ZERO);
|
||||
// This is const data, no mutation allowed.
|
||||
let alloc_id = self.tcx.reserve_and_set_memory_alloc(data);
|
||||
let ptr = Pointer::new(CtfeProvenance::from(alloc_id).as_immutable(), Size::ZERO);
|
||||
Immediate::new_slice(self.global_base_pointer(ptr)?.into(), meta, self)
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,14 +14,14 @@ use rustc_middle::ty::Ty;
|
|||
use rustc_target::abi::{Abi, Align, HasDataLayout, Size};
|
||||
|
||||
use super::{
|
||||
alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckAlignMsg, ImmTy,
|
||||
Immediate, InterpCx, InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy,
|
||||
alloc_range, mir_assign_valid_types, AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance,
|
||||
ImmTy, Immediate, InterpCx, InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy,
|
||||
Operand, Pointer, PointerArithmetic, Projectable, Provenance, Readable, Scalar,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
|
||||
/// Information required for the sound usage of a `MemPlace`.
|
||||
pub enum MemPlaceMeta<Prov: Provenance = AllocId> {
|
||||
pub enum MemPlaceMeta<Prov: Provenance = CtfeProvenance> {
|
||||
/// The unsized payload (e.g. length for slices or vtable pointer for trait objects).
|
||||
Meta(Scalar<Prov>),
|
||||
/// `Sized` types or unsized `extern type`
|
||||
|
|
@ -49,7 +49,7 @@ impl<Prov: Provenance> MemPlaceMeta<Prov> {
|
|||
}
|
||||
|
||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
|
||||
pub(super) struct MemPlace<Prov: Provenance = AllocId> {
|
||||
pub(super) struct MemPlace<Prov: Provenance = CtfeProvenance> {
|
||||
/// The pointer can be a pure integer, with the `None` provenance.
|
||||
pub ptr: Pointer<Option<Prov>>,
|
||||
/// Metadata for unsized places. Interpretation is up to the type.
|
||||
|
|
@ -100,7 +100,7 @@ impl<Prov: Provenance> MemPlace<Prov> {
|
|||
|
||||
/// A MemPlace with its layout. Constructing it is only possible in this module.
|
||||
#[derive(Clone, Hash, Eq, PartialEq)]
|
||||
pub struct MPlaceTy<'tcx, Prov: Provenance = AllocId> {
|
||||
pub struct MPlaceTy<'tcx, Prov: Provenance = CtfeProvenance> {
|
||||
mplace: MemPlace<Prov>,
|
||||
pub layout: TyAndLayout<'tcx>,
|
||||
}
|
||||
|
|
@ -179,7 +179,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
|
|||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(super) enum Place<Prov: Provenance = AllocId> {
|
||||
pub(super) enum Place<Prov: Provenance = CtfeProvenance> {
|
||||
/// A place referring to a value allocated in the `Memory` system.
|
||||
Ptr(MemPlace<Prov>),
|
||||
|
||||
|
|
@ -195,7 +195,7 @@ pub(super) enum Place<Prov: Provenance = AllocId> {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PlaceTy<'tcx, Prov: Provenance = AllocId> {
|
||||
pub struct PlaceTy<'tcx, Prov: Provenance = CtfeProvenance> {
|
||||
place: Place<Prov>, // Keep this private; it helps enforce invariants.
|
||||
pub layout: TyAndLayout<'tcx>,
|
||||
}
|
||||
|
|
@ -406,11 +406,7 @@ where
|
|||
let pointee_type =
|
||||
val.layout.ty.builtin_deref(true).expect("`ref_to_mplace` called on non-ptr type").ty;
|
||||
let layout = self.layout_of(pointee_type)?;
|
||||
let (ptr, meta) = match **val {
|
||||
Immediate::Scalar(ptr) => (ptr, MemPlaceMeta::None),
|
||||
Immediate::ScalarPair(ptr, meta) => (ptr, MemPlaceMeta::Meta(meta)),
|
||||
Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)),
|
||||
};
|
||||
let (ptr, meta) = val.to_scalar_and_meta();
|
||||
|
||||
// `ref_to_mplace` is called on raw pointers even if they don't actually get dereferenced;
|
||||
// we hence can't call `size_and_align_of` since that asserts more validity than we want.
|
||||
|
|
|
|||
|
|
@ -18,14 +18,14 @@ use rustc_target::abi::{
|
|||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use super::{
|
||||
AllocId, FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, Projectable,
|
||||
Provenance, Scalar, StackPopCleanup,
|
||||
CtfeProvenance, FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, PlaceTy,
|
||||
Projectable, Provenance, Scalar, StackPopCleanup,
|
||||
};
|
||||
use crate::fluent_generated as fluent;
|
||||
|
||||
/// An argment passed to a function.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum FnArg<'tcx, Prov: Provenance = AllocId> {
|
||||
pub enum FnArg<'tcx, Prov: Provenance = CtfeProvenance> {
|
||||
/// Pass a copy of the given operand.
|
||||
Copy(OpTy<'tcx, Prov>),
|
||||
/// Allow for the argument to be passed in-place: destroy the value originally stored at that place and
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
#![feature(let_chains)]
|
||||
#![feature(panic_update_hook)]
|
||||
#![recursion_limit = "256"]
|
||||
#![allow(rustc::potential_query_instability)]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ impl Diagnostic {
|
|||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn new_with_code<M: Into<DiagnosticMessage>>(
|
||||
pub(crate) fn new_with_code<M: Into<DiagnosticMessage>>(
|
||||
level: Level,
|
||||
code: Option<DiagnosticId>,
|
||||
message: M,
|
||||
|
|
@ -281,7 +281,7 @@ impl Diagnostic {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update_unstable_expectation_id(
|
||||
pub(crate) fn update_unstable_expectation_id(
|
||||
&mut self,
|
||||
unstable_to_stable: &FxHashMap<LintExpectationId, LintExpectationId>,
|
||||
) {
|
||||
|
|
@ -307,14 +307,14 @@ impl Diagnostic {
|
|||
}
|
||||
|
||||
/// Indicates whether this diagnostic should show up in cargo's future breakage report.
|
||||
pub fn has_future_breakage(&self) -> bool {
|
||||
pub(crate) fn has_future_breakage(&self) -> bool {
|
||||
match self.code {
|
||||
Some(DiagnosticId::Lint { has_future_breakage, .. }) => has_future_breakage,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_force_warn(&self) -> bool {
|
||||
pub(crate) fn is_force_warn(&self) -> bool {
|
||||
match self.code {
|
||||
Some(DiagnosticId::Lint { is_force_warn, .. }) => is_force_warn,
|
||||
_ => false,
|
||||
|
|
@ -391,29 +391,6 @@ impl Diagnostic {
|
|||
self.note_expected_found_extra(expected_label, expected, found_label, found, &"", &"")
|
||||
}
|
||||
|
||||
pub fn note_unsuccessful_coercion(
|
||||
&mut self,
|
||||
expected: DiagnosticStyledString,
|
||||
found: DiagnosticStyledString,
|
||||
) -> &mut Self {
|
||||
let mut msg: Vec<_> =
|
||||
vec![(Cow::from("required when trying to coerce from type `"), Style::NoStyle)];
|
||||
msg.extend(expected.0.iter().map(|x| match *x {
|
||||
StringPart::Normal(ref s) => (Cow::from(s.clone()), Style::NoStyle),
|
||||
StringPart::Highlighted(ref s) => (Cow::from(s.clone()), Style::Highlight),
|
||||
}));
|
||||
msg.push((Cow::from("` to type '"), Style::NoStyle));
|
||||
msg.extend(found.0.iter().map(|x| match *x {
|
||||
StringPart::Normal(ref s) => (Cow::from(s.clone()), Style::NoStyle),
|
||||
StringPart::Highlighted(ref s) => (Cow::from(s.clone()), Style::Highlight),
|
||||
}));
|
||||
msg.push((Cow::from("`"), Style::NoStyle));
|
||||
|
||||
// For now, just attach these as notes
|
||||
self.highlighted_note(msg);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn note_expected_found_extra(
|
||||
&mut self,
|
||||
expected_label: &dyn fmt::Display,
|
||||
|
|
@ -475,7 +452,7 @@ impl Diagnostic {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn highlighted_note<M: Into<SubdiagnosticMessage>>(
|
||||
fn highlighted_note<M: Into<SubdiagnosticMessage>>(
|
||||
&mut self,
|
||||
msg: Vec<(M, Style)>,
|
||||
) -> &mut Self {
|
||||
|
|
@ -572,14 +549,6 @@ impl Diagnostic {
|
|||
self
|
||||
}
|
||||
|
||||
/// Clear any existing suggestions.
|
||||
pub fn clear_suggestions(&mut self) -> &mut Self {
|
||||
if let Ok(suggestions) = &mut self.suggestions {
|
||||
suggestions.clear();
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Helper for pushing to `self.suggestions`, if available (not disable).
|
||||
fn push_suggestion(&mut self, suggestion: CodeSuggestion) {
|
||||
if let Ok(suggestions) = &mut self.suggestions {
|
||||
|
|
@ -992,7 +961,7 @@ impl Diagnostic {
|
|||
/// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by
|
||||
/// combining it with the primary message of the diagnostic (if translatable, otherwise it just
|
||||
/// passes the user's string along).
|
||||
pub(crate) fn subdiagnostic_message_to_diagnostic_message(
|
||||
fn subdiagnostic_message_to_diagnostic_message(
|
||||
&self,
|
||||
attr: impl Into<SubdiagnosticMessage>,
|
||||
) -> DiagnosticMessage {
|
||||
|
|
|
|||
|
|
@ -18,18 +18,18 @@ use std::thread::panicking;
|
|||
/// Trait implemented by error types. This should not be implemented manually. Instead, use
|
||||
/// `#[derive(Diagnostic)]` -- see [rustc_macros::Diagnostic].
|
||||
#[rustc_diagnostic_item = "IntoDiagnostic"]
|
||||
pub trait IntoDiagnostic<'a, T: EmissionGuarantee = ErrorGuaranteed> {
|
||||
pub trait IntoDiagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> {
|
||||
/// Write out as a diagnostic out of `Handler`.
|
||||
#[must_use]
|
||||
fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, T>;
|
||||
fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, G>;
|
||||
}
|
||||
|
||||
impl<'a, T, E> IntoDiagnostic<'a, E> for Spanned<T>
|
||||
impl<'a, T, G> IntoDiagnostic<'a, G> for Spanned<T>
|
||||
where
|
||||
T: IntoDiagnostic<'a, E>,
|
||||
E: EmissionGuarantee,
|
||||
T: IntoDiagnostic<'a, G>,
|
||||
G: EmissionGuarantee,
|
||||
{
|
||||
fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, E> {
|
||||
fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, G> {
|
||||
let mut diag = self.node.into_diagnostic(handler);
|
||||
diag.set_span(self.span);
|
||||
diag
|
||||
|
|
@ -116,26 +116,6 @@ pub trait EmissionGuarantee: Sized {
|
|||
}
|
||||
|
||||
impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> {
|
||||
/// Convenience function for internal use, clients should use one of the
|
||||
/// `struct_*` methods on [`Handler`].
|
||||
#[track_caller]
|
||||
pub(crate) fn new_guaranteeing_error<M: Into<DiagnosticMessage>>(
|
||||
handler: &'a Handler,
|
||||
message: M,
|
||||
) -> Self {
|
||||
Self {
|
||||
inner: DiagnosticBuilderInner {
|
||||
state: DiagnosticBuilderState::Emittable(handler),
|
||||
diagnostic: Box::new(Diagnostic::new_with_code(
|
||||
Level::Error { lint: false },
|
||||
None,
|
||||
message,
|
||||
)),
|
||||
},
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Discard the guarantee `.emit()` would return, in favor of having the
|
||||
/// type `DiagnosticBuilder<'a, ()>`. This may be necessary whenever there
|
||||
/// is a common codepath handling both errors and warnings.
|
||||
|
|
@ -189,35 +169,7 @@ impl EmissionGuarantee for ErrorGuaranteed {
|
|||
handler: &Handler,
|
||||
msg: impl Into<DiagnosticMessage>,
|
||||
) -> DiagnosticBuilder<'_, Self> {
|
||||
DiagnosticBuilder::new_guaranteeing_error(handler, msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DiagnosticBuilder<'a, ()> {
|
||||
/// Convenience function for internal use, clients should use one of the
|
||||
/// `struct_*` methods on [`Handler`].
|
||||
#[track_caller]
|
||||
pub(crate) fn new<M: Into<DiagnosticMessage>>(
|
||||
handler: &'a Handler,
|
||||
level: Level,
|
||||
message: M,
|
||||
) -> Self {
|
||||
let diagnostic = Diagnostic::new_with_code(level, None, message);
|
||||
Self::new_diagnostic(handler, diagnostic)
|
||||
}
|
||||
|
||||
/// Creates a new `DiagnosticBuilder` with an already constructed
|
||||
/// diagnostic.
|
||||
#[track_caller]
|
||||
pub(crate) fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> Self {
|
||||
debug!("Created new diagnostic");
|
||||
Self {
|
||||
inner: DiagnosticBuilderInner {
|
||||
state: DiagnosticBuilderState::Emittable(handler),
|
||||
diagnostic: Box::new(diagnostic),
|
||||
},
|
||||
_marker: PhantomData,
|
||||
}
|
||||
DiagnosticBuilder::new(handler, Level::Error { lint: false }, msg)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -249,28 +201,6 @@ impl EmissionGuarantee for () {
|
|||
#[derive(Copy, Clone)]
|
||||
pub struct Noted;
|
||||
|
||||
impl<'a> DiagnosticBuilder<'a, Noted> {
|
||||
/// Convenience function for internal use, clients should use one of the
|
||||
/// `struct_*` methods on [`Handler`].
|
||||
pub(crate) fn new_note(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self {
|
||||
let diagnostic = Diagnostic::new_with_code(Level::Note, None, message);
|
||||
Self::new_diagnostic_note(handler, diagnostic)
|
||||
}
|
||||
|
||||
/// Creates a new `DiagnosticBuilder` with an already constructed
|
||||
/// diagnostic.
|
||||
pub(crate) fn new_diagnostic_note(handler: &'a Handler, diagnostic: Diagnostic) -> Self {
|
||||
debug!("Created new diagnostic");
|
||||
Self {
|
||||
inner: DiagnosticBuilderInner {
|
||||
state: DiagnosticBuilderState::Emittable(handler),
|
||||
diagnostic: Box::new(diagnostic),
|
||||
},
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EmissionGuarantee for Noted {
|
||||
fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
|
||||
match db.inner.state {
|
||||
|
|
@ -290,7 +220,7 @@ impl EmissionGuarantee for Noted {
|
|||
handler: &Handler,
|
||||
msg: impl Into<DiagnosticMessage>,
|
||||
) -> DiagnosticBuilder<'_, Self> {
|
||||
DiagnosticBuilder::new_note(handler, msg)
|
||||
DiagnosticBuilder::new(handler, Level::Note, msg)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -299,29 +229,6 @@ impl EmissionGuarantee for Noted {
|
|||
#[derive(Copy, Clone)]
|
||||
pub struct Bug;
|
||||
|
||||
impl<'a> DiagnosticBuilder<'a, Bug> {
|
||||
/// Convenience function for internal use, clients should use one of the
|
||||
/// `struct_*` methods on [`Handler`].
|
||||
#[track_caller]
|
||||
pub(crate) fn new_bug(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self {
|
||||
let diagnostic = Diagnostic::new_with_code(Level::Bug, None, message);
|
||||
Self::new_diagnostic_bug(handler, diagnostic)
|
||||
}
|
||||
|
||||
/// Creates a new `DiagnosticBuilder` with an already constructed
|
||||
/// diagnostic.
|
||||
pub(crate) fn new_diagnostic_bug(handler: &'a Handler, diagnostic: Diagnostic) -> Self {
|
||||
debug!("Created new diagnostic bug");
|
||||
Self {
|
||||
inner: DiagnosticBuilderInner {
|
||||
state: DiagnosticBuilderState::Emittable(handler),
|
||||
diagnostic: Box::new(diagnostic),
|
||||
},
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EmissionGuarantee for Bug {
|
||||
fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
|
||||
match db.inner.state {
|
||||
|
|
@ -342,22 +249,7 @@ impl EmissionGuarantee for Bug {
|
|||
handler: &Handler,
|
||||
msg: impl Into<DiagnosticMessage>,
|
||||
) -> DiagnosticBuilder<'_, Self> {
|
||||
DiagnosticBuilder::new_bug(handler, msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DiagnosticBuilder<'a, !> {
|
||||
/// Convenience function for internal use, clients should use one of the
|
||||
/// `struct_*` methods on [`Handler`].
|
||||
#[track_caller]
|
||||
pub(crate) fn new_fatal(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self {
|
||||
Self {
|
||||
inner: DiagnosticBuilderInner {
|
||||
state: DiagnosticBuilderState::Emittable(handler),
|
||||
diagnostic: Box::new(Diagnostic::new_with_code(Level::Fatal, None, message)),
|
||||
},
|
||||
_marker: PhantomData,
|
||||
}
|
||||
DiagnosticBuilder::new(handler, Level::Bug, msg)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -381,36 +273,7 @@ impl EmissionGuarantee for ! {
|
|||
handler: &Handler,
|
||||
msg: impl Into<DiagnosticMessage>,
|
||||
) -> DiagnosticBuilder<'_, Self> {
|
||||
DiagnosticBuilder::new_fatal(handler, msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DiagnosticBuilder<'a, rustc_span::fatal_error::FatalError> {
|
||||
/// Convenience function for internal use, clients should use one of the
|
||||
/// `struct_*` methods on [`Handler`].
|
||||
#[track_caller]
|
||||
pub(crate) fn new_almost_fatal(
|
||||
handler: &'a Handler,
|
||||
message: impl Into<DiagnosticMessage>,
|
||||
) -> Self {
|
||||
let diagnostic = Diagnostic::new_with_code(Level::Fatal, None, message);
|
||||
Self::new_diagnostic_almost_fatal(handler, diagnostic)
|
||||
}
|
||||
|
||||
/// Creates a new `DiagnosticBuilder` with an already constructed
|
||||
/// diagnostic.
|
||||
pub(crate) fn new_diagnostic_almost_fatal(
|
||||
handler: &'a Handler,
|
||||
diagnostic: Diagnostic,
|
||||
) -> Self {
|
||||
debug!("Created new diagnostic");
|
||||
Self {
|
||||
inner: DiagnosticBuilderInner {
|
||||
state: DiagnosticBuilderState::Emittable(handler),
|
||||
diagnostic: Box::new(diagnostic),
|
||||
},
|
||||
_marker: PhantomData,
|
||||
}
|
||||
DiagnosticBuilder::new(handler, Level::Fatal, msg)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -434,7 +297,7 @@ impl EmissionGuarantee for rustc_span::fatal_error::FatalError {
|
|||
handler: &Handler,
|
||||
msg: impl Into<DiagnosticMessage>,
|
||||
) -> DiagnosticBuilder<'_, Self> {
|
||||
DiagnosticBuilder::new_almost_fatal(handler, msg)
|
||||
DiagnosticBuilder::new(handler, Level::Fatal, msg)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -476,6 +339,32 @@ impl<G: EmissionGuarantee> DerefMut for DiagnosticBuilder<'_, G> {
|
|||
}
|
||||
|
||||
impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
|
||||
/// Convenience function for internal use, clients should use one of the
|
||||
/// `struct_*` methods on [`Handler`].
|
||||
#[track_caller]
|
||||
pub(crate) fn new<M: Into<DiagnosticMessage>>(
|
||||
handler: &'a Handler,
|
||||
level: Level,
|
||||
message: M,
|
||||
) -> Self {
|
||||
let diagnostic = Diagnostic::new(level, message);
|
||||
Self::new_diagnostic(handler, diagnostic)
|
||||
}
|
||||
|
||||
/// Creates a new `DiagnosticBuilder` with an already constructed
|
||||
/// diagnostic.
|
||||
#[track_caller]
|
||||
pub(crate) fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> Self {
|
||||
debug!("Created new diagnostic");
|
||||
Self {
|
||||
inner: DiagnosticBuilderInner {
|
||||
state: DiagnosticBuilderState::Emittable(handler),
|
||||
diagnostic: Box::new(diagnostic),
|
||||
},
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit the diagnostic.
|
||||
#[track_caller]
|
||||
pub fn emit(&mut self) -> G {
|
||||
|
|
@ -626,12 +515,6 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
|
|||
found_extra: &dyn fmt::Display,
|
||||
) -> &mut Self);
|
||||
|
||||
forward!(pub fn note_unsuccessful_coercion(
|
||||
&mut self,
|
||||
expected: DiagnosticStyledString,
|
||||
found: DiagnosticStyledString,
|
||||
) -> &mut Self);
|
||||
|
||||
forward!(pub fn note(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
|
||||
forward!(pub fn note_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
|
||||
forward!(pub fn span_note(
|
||||
|
|
@ -660,7 +543,6 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
|
|||
forward!(pub fn set_is_lint(&mut self,) -> &mut Self);
|
||||
|
||||
forward!(pub fn disable_suggestions(&mut self,) -> &mut Self);
|
||||
forward!(pub fn clear_suggestions(&mut self,) -> &mut Self);
|
||||
|
||||
forward!(pub fn multipart_suggestion(
|
||||
&mut self,
|
||||
|
|
|
|||
|
|
@ -2674,6 +2674,14 @@ fn from_stderr(color: ColorConfig) -> Destination {
|
|||
}
|
||||
}
|
||||
|
||||
/// On Windows, BRIGHT_BLUE is hard to read on black. Use cyan instead.
|
||||
///
|
||||
/// See #36178.
|
||||
#[cfg(windows)]
|
||||
const BRIGHT_BLUE: Color = Color::Cyan;
|
||||
#[cfg(not(windows))]
|
||||
const BRIGHT_BLUE: Color = Color::Blue;
|
||||
|
||||
impl Style {
|
||||
fn color_spec(&self, lvl: Level) -> ColorSpec {
|
||||
let mut spec = ColorSpec::new();
|
||||
|
|
@ -2688,11 +2696,7 @@ impl Style {
|
|||
Style::LineNumber => {
|
||||
spec.set_bold(true);
|
||||
spec.set_intense(true);
|
||||
if cfg!(windows) {
|
||||
spec.set_fg(Some(Color::Cyan));
|
||||
} else {
|
||||
spec.set_fg(Some(Color::Blue));
|
||||
}
|
||||
spec.set_fg(Some(BRIGHT_BLUE));
|
||||
}
|
||||
Style::Quotation => {}
|
||||
Style::MainHeaderMsg => {
|
||||
|
|
@ -2707,11 +2711,7 @@ impl Style {
|
|||
}
|
||||
Style::UnderlineSecondary | Style::LabelSecondary => {
|
||||
spec.set_bold(true).set_intense(true);
|
||||
if cfg!(windows) {
|
||||
spec.set_fg(Some(Color::Cyan));
|
||||
} else {
|
||||
spec.set_fg(Some(Color::Blue));
|
||||
}
|
||||
spec.set_fg(Some(BRIGHT_BLUE));
|
||||
}
|
||||
Style::HeaderMsg | Style::NoStyle => {}
|
||||
Style::Level(lvl) => {
|
||||
|
|
@ -2719,7 +2719,7 @@ impl Style {
|
|||
spec.set_bold(true);
|
||||
}
|
||||
Style::Highlight => {
|
||||
spec.set_bold(true);
|
||||
spec.set_bold(true).set_fg(Some(Color::Magenta));
|
||||
}
|
||||
}
|
||||
spec
|
||||
|
|
|
|||
|
|
@ -554,7 +554,8 @@ impl Drop for HandlerInner {
|
|||
// instead of "require some error happened". Sadly that isn't ideal, as
|
||||
// lints can be `#[allow]`'d, potentially leading to this triggering.
|
||||
// Also, "good path" should be replaced with a better naming.
|
||||
if !self.has_any_message() && !self.suppressed_expected_diag && !std::thread::panicking() {
|
||||
let has_any_message = self.err_count > 0 || self.lint_err_count > 0 || self.warn_count > 0;
|
||||
if !has_any_message && !self.suppressed_expected_diag && !std::thread::panicking() {
|
||||
let bugs = std::mem::replace(&mut self.good_path_delayed_bugs, Vec::new());
|
||||
self.flush_delayed(
|
||||
bugs,
|
||||
|
|
@ -675,15 +676,46 @@ impl Handler {
|
|||
/// Stash a given diagnostic with the given `Span` and [`StashKey`] as the key.
|
||||
/// Retrieve a stashed diagnostic with `steal_diagnostic`.
|
||||
pub fn stash_diagnostic(&self, span: Span, key: StashKey, diag: Diagnostic) {
|
||||
self.inner.borrow_mut().stash((span.with_parent(None), key), diag);
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
|
||||
let key = (span.with_parent(None), key);
|
||||
|
||||
if diag.is_error() {
|
||||
if matches!(diag.level, Level::Error { lint: true }) {
|
||||
inner.lint_err_count += 1;
|
||||
} else {
|
||||
inner.err_count += 1;
|
||||
}
|
||||
} else {
|
||||
// Warnings are only automatically flushed if they're forced.
|
||||
if diag.is_force_warn() {
|
||||
inner.warn_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic
|
||||
// if/when we have a more robust macro-friendly replacement for `(span, key)` as a key.
|
||||
// See the PR for a discussion.
|
||||
inner.stashed_diagnostics.insert(key, diag);
|
||||
}
|
||||
|
||||
/// Steal a previously stashed diagnostic with the given `Span` and [`StashKey`] as the key.
|
||||
pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option<DiagnosticBuilder<'_, ()>> {
|
||||
self.inner
|
||||
.borrow_mut()
|
||||
.steal((span.with_parent(None), key))
|
||||
.map(|diag| DiagnosticBuilder::new_diagnostic(self, diag))
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let key = (span.with_parent(None), key);
|
||||
let diag = inner.stashed_diagnostics.remove(&key)?;
|
||||
if diag.is_error() {
|
||||
if matches!(diag.level, Level::Error { lint: true }) {
|
||||
inner.lint_err_count -= 1;
|
||||
} else {
|
||||
inner.err_count -= 1;
|
||||
}
|
||||
} else {
|
||||
if diag.is_force_warn() {
|
||||
inner.warn_count -= 1;
|
||||
}
|
||||
}
|
||||
Some(DiagnosticBuilder::new_diagnostic(self, diag))
|
||||
}
|
||||
|
||||
pub fn has_stashed_diagnostic(&self, span: Span, key: StashKey) -> bool {
|
||||
|
|
@ -847,7 +879,7 @@ impl Handler {
|
|||
&self,
|
||||
msg: impl Into<DiagnosticMessage>,
|
||||
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
|
||||
DiagnosticBuilder::new_guaranteeing_error(self, msg)
|
||||
DiagnosticBuilder::new(self, Level::Error { lint: false }, msg)
|
||||
}
|
||||
|
||||
/// This should only be used by `rustc_middle::lint::struct_lint_level`. Do not use it for hard errors.
|
||||
|
|
@ -914,7 +946,7 @@ impl Handler {
|
|||
#[rustc_lint_diagnostics]
|
||||
#[track_caller]
|
||||
pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> {
|
||||
DiagnosticBuilder::new_fatal(self, msg)
|
||||
DiagnosticBuilder::new(self, Level::Fatal, msg)
|
||||
}
|
||||
|
||||
/// Construct a builder at the `Help` level with the `msg`.
|
||||
|
|
@ -996,19 +1028,42 @@ impl Handler {
|
|||
}
|
||||
|
||||
/// For documentation on this, see `Session::span_delayed_bug`.
|
||||
///
|
||||
/// Note: this function used to be called `delay_span_bug`. It was renamed
|
||||
/// to match similar functions like `span_bug`, `span_err`, etc.
|
||||
#[track_caller]
|
||||
pub fn span_delayed_bug(
|
||||
&self,
|
||||
span: impl Into<MultiSpan>,
|
||||
sp: impl Into<MultiSpan>,
|
||||
msg: impl Into<String>,
|
||||
) -> ErrorGuaranteed {
|
||||
self.inner.borrow_mut().span_delayed_bug(span, msg)
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
|
||||
// This is technically `self.treat_err_as_bug()` but `span_delayed_bug` is called before
|
||||
// incrementing `err_count` by one, so we need to +1 the comparing.
|
||||
// FIXME: Would be nice to increment err_count in a more coherent way.
|
||||
if inner.flags.treat_err_as_bug.is_some_and(|c| {
|
||||
inner.err_count + inner.lint_err_count + inner.delayed_bug_count() + 1 >= c.get()
|
||||
}) {
|
||||
// FIXME: don't abort here if report_delayed_bugs is off
|
||||
inner.span_bug(sp, msg.into());
|
||||
}
|
||||
let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg.into());
|
||||
diagnostic.set_span(sp.into());
|
||||
inner.emit_diagnostic(&mut diagnostic).unwrap()
|
||||
}
|
||||
|
||||
// FIXME(eddyb) note the comment inside `impl Drop for HandlerInner`, that's
|
||||
// where the explanation of what "good path" is (also, it should be renamed).
|
||||
pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) {
|
||||
self.inner.borrow_mut().good_path_delayed_bug(msg)
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
|
||||
let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg);
|
||||
if inner.flags.report_delayed_bugs {
|
||||
inner.emit_diagnostic(&mut diagnostic);
|
||||
}
|
||||
let backtrace = std::backtrace::Backtrace::capture();
|
||||
inner.good_path_delayed_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace));
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
|
|
@ -1034,34 +1089,34 @@ impl Handler {
|
|||
db
|
||||
}
|
||||
|
||||
// NOTE: intentionally doesn't raise an error so rustc_codegen_ssa only reports fatal errors in the main thread
|
||||
#[rustc_lint_diagnostics]
|
||||
pub fn fatal(&self, msg: impl Into<DiagnosticMessage>) -> FatalError {
|
||||
self.inner.borrow_mut().fatal(msg)
|
||||
pub fn fatal(&self, msg: impl Into<DiagnosticMessage>) -> ! {
|
||||
DiagnosticBuilder::<FatalError>::new(self, Fatal, msg).emit().raise()
|
||||
}
|
||||
|
||||
#[rustc_lint_diagnostics]
|
||||
pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
|
||||
self.inner.borrow_mut().err(msg)
|
||||
DiagnosticBuilder::<ErrorGuaranteed>::new(self, Error { lint: false }, msg).emit()
|
||||
}
|
||||
|
||||
#[rustc_lint_diagnostics]
|
||||
pub fn warn(&self, msg: impl Into<DiagnosticMessage>) {
|
||||
DiagnosticBuilder::new(self, Warning(None), msg).emit();
|
||||
DiagnosticBuilder::<()>::new(self, Warning(None), msg).emit();
|
||||
}
|
||||
|
||||
#[rustc_lint_diagnostics]
|
||||
pub fn note(&self, msg: impl Into<DiagnosticMessage>) {
|
||||
DiagnosticBuilder::new(self, Note, msg).emit();
|
||||
DiagnosticBuilder::<()>::new(self, Note, msg).emit();
|
||||
}
|
||||
|
||||
pub fn bug(&self, msg: impl Into<DiagnosticMessage>) -> ! {
|
||||
self.inner.borrow_mut().bug(msg)
|
||||
DiagnosticBuilder::<diagnostic_builder::Bug>::new(self, Bug, msg).emit();
|
||||
panic::panic_any(ExplicitBug);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn err_count(&self) -> usize {
|
||||
self.inner.borrow().err_count()
|
||||
self.inner.borrow().err_count
|
||||
}
|
||||
|
||||
pub fn has_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
|
|
@ -1072,26 +1127,103 @@ impl Handler {
|
|||
}
|
||||
|
||||
pub fn has_errors_or_lint_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
self.inner.borrow().has_errors_or_lint_errors().then(|| {
|
||||
let inner = self.inner.borrow();
|
||||
let has_errors_or_lint_errors = inner.has_errors() || inner.lint_err_count > 0;
|
||||
has_errors_or_lint_errors.then(|| {
|
||||
#[allow(deprecated)]
|
||||
ErrorGuaranteed::unchecked_claim_error_was_emitted()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn has_errors_or_span_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
|
||||
self.inner.borrow().has_errors_or_span_delayed_bugs().then(|| {
|
||||
let inner = self.inner.borrow();
|
||||
let has_errors_or_span_delayed_bugs =
|
||||
inner.has_errors() || !inner.span_delayed_bugs.is_empty();
|
||||
has_errors_or_span_delayed_bugs.then(|| {
|
||||
#[allow(deprecated)]
|
||||
ErrorGuaranteed::unchecked_claim_error_was_emitted()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_compilation_going_to_fail(&self) -> Option<ErrorGuaranteed> {
|
||||
self.inner.borrow().is_compilation_going_to_fail().then(|| {
|
||||
let inner = self.inner.borrow();
|
||||
let will_fail =
|
||||
inner.has_errors() || inner.lint_err_count > 0 || !inner.span_delayed_bugs.is_empty();
|
||||
will_fail.then(|| {
|
||||
#[allow(deprecated)]
|
||||
ErrorGuaranteed::unchecked_claim_error_was_emitted()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn print_error_count(&self, registry: &Registry) {
|
||||
self.inner.borrow_mut().print_error_count(registry)
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
|
||||
inner.emit_stashed_diagnostics();
|
||||
|
||||
let warnings = match inner.deduplicated_warn_count {
|
||||
0 => Cow::from(""),
|
||||
1 => Cow::from("1 warning emitted"),
|
||||
count => Cow::from(format!("{count} warnings emitted")),
|
||||
};
|
||||
let errors = match inner.deduplicated_err_count {
|
||||
0 => Cow::from(""),
|
||||
1 => Cow::from("aborting due to 1 previous error"),
|
||||
count => Cow::from(format!("aborting due to {count} previous errors")),
|
||||
};
|
||||
if inner.treat_err_as_bug() {
|
||||
return;
|
||||
}
|
||||
|
||||
match (errors.len(), warnings.len()) {
|
||||
(0, 0) => return,
|
||||
(0, _) => inner.emitter.emit_diagnostic(&Diagnostic::new(
|
||||
Level::Warning(None),
|
||||
DiagnosticMessage::Str(warnings),
|
||||
)),
|
||||
(_, 0) => {
|
||||
inner.emit_diagnostic(&mut Diagnostic::new(Fatal, errors));
|
||||
}
|
||||
(_, _) => {
|
||||
inner.emit_diagnostic(&mut Diagnostic::new(Fatal, format!("{errors}; {warnings}")));
|
||||
}
|
||||
}
|
||||
|
||||
let can_show_explain = inner.emitter.should_show_explain();
|
||||
let are_there_diagnostics = !inner.emitted_diagnostic_codes.is_empty();
|
||||
if can_show_explain && are_there_diagnostics {
|
||||
let mut error_codes = inner
|
||||
.emitted_diagnostic_codes
|
||||
.iter()
|
||||
.filter_map(|x| match &x {
|
||||
DiagnosticId::Error(s) if registry.try_find_description(s).is_ok() => {
|
||||
Some(s.clone())
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if !error_codes.is_empty() {
|
||||
error_codes.sort();
|
||||
if error_codes.len() > 1 {
|
||||
let limit = if error_codes.len() > 9 { 9 } else { error_codes.len() };
|
||||
inner.failure_note(format!(
|
||||
"Some errors have detailed explanations: {}{}",
|
||||
error_codes[..limit].join(", "),
|
||||
if error_codes.len() > 9 { "..." } else { "." }
|
||||
));
|
||||
inner.failure_note(format!(
|
||||
"For more information about an error, try \
|
||||
`rustc --explain {}`.",
|
||||
&error_codes[0]
|
||||
));
|
||||
} else {
|
||||
inner.failure_note(format!(
|
||||
"For more information about this error, try \
|
||||
`rustc --explain {}`.",
|
||||
&error_codes[0]
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn take_future_breakage_diagnostics(&self) -> Vec<Diagnostic> {
|
||||
|
|
@ -1099,7 +1231,11 @@ impl Handler {
|
|||
}
|
||||
|
||||
pub fn abort_if_errors(&self) {
|
||||
self.inner.borrow_mut().abort_if_errors()
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
inner.emit_stashed_diagnostics();
|
||||
if inner.has_errors() {
|
||||
FatalError.raise();
|
||||
}
|
||||
}
|
||||
|
||||
/// `true` if we haven't taught a diagnostic with this code already.
|
||||
|
|
@ -1108,11 +1244,11 @@ impl Handler {
|
|||
/// Used to suppress emitting the same error multiple times with extended explanation when
|
||||
/// calling `-Zteach`.
|
||||
pub fn must_teach(&self, code: &DiagnosticId) -> bool {
|
||||
self.inner.borrow_mut().must_teach(code)
|
||||
self.inner.borrow_mut().taught_diagnostics.insert(code.clone())
|
||||
}
|
||||
|
||||
pub fn force_print_diagnostic(&self, db: Diagnostic) {
|
||||
self.inner.borrow_mut().force_print_diagnostic(db)
|
||||
self.inner.borrow_mut().emitter.emit_diagnostic(&db);
|
||||
}
|
||||
|
||||
pub fn emit_diagnostic(&self, diagnostic: &mut Diagnostic) -> Option<ErrorGuaranteed> {
|
||||
|
|
@ -1196,11 +1332,11 @@ impl Handler {
|
|||
mut diag: Diagnostic,
|
||||
sp: impl Into<MultiSpan>,
|
||||
) -> Option<ErrorGuaranteed> {
|
||||
self.inner.borrow_mut().emit_diagnostic(diag.set_span(sp))
|
||||
self.emit_diagnostic(diag.set_span(sp))
|
||||
}
|
||||
|
||||
pub fn emit_artifact_notification(&self, path: &Path, artifact_type: &str) {
|
||||
self.inner.borrow_mut().emit_artifact_notification(path, artifact_type)
|
||||
self.inner.borrow_mut().emitter.emit_artifact_notification(path, artifact_type);
|
||||
}
|
||||
|
||||
pub fn emit_future_breakage_report(&self, diags: Vec<Diagnostic>) {
|
||||
|
|
@ -1219,7 +1355,7 @@ impl Handler {
|
|||
inner.bump_err_count();
|
||||
}
|
||||
|
||||
inner.emit_unused_externs(lint_level, unused_externs)
|
||||
inner.emitter.emit_unused_externs(lint_level, unused_externs)
|
||||
}
|
||||
|
||||
pub fn update_unstable_expectation_id(
|
||||
|
|
@ -1270,15 +1406,11 @@ impl Handler {
|
|||
}
|
||||
}
|
||||
|
||||
// Note: we prefer implementing operations on `Handler`, rather than
|
||||
// `HandlerInner`, whenever possible. This minimizes functions where
|
||||
// `Handler::foo()` just borrows `inner` and forwards a call to
|
||||
// `HanderInner::foo`.
|
||||
impl HandlerInner {
|
||||
fn must_teach(&mut self, code: &DiagnosticId) -> bool {
|
||||
self.taught_diagnostics.insert(code.clone())
|
||||
}
|
||||
|
||||
fn force_print_diagnostic(&mut self, db: Diagnostic) {
|
||||
self.emitter.emit_diagnostic(&db);
|
||||
}
|
||||
|
||||
/// Emit all stashed diagnostics.
|
||||
fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> {
|
||||
let has_errors = self.has_errors();
|
||||
|
|
@ -1312,6 +1444,11 @@ impl HandlerInner {
|
|||
|
||||
// FIXME(eddyb) this should ideally take `diagnostic` by value.
|
||||
fn emit_diagnostic(&mut self, diagnostic: &mut Diagnostic) -> Option<ErrorGuaranteed> {
|
||||
if matches!(diagnostic.level, Level::Error { .. } | Level::Fatal) && self.treat_err_as_bug()
|
||||
{
|
||||
diagnostic.level = Level::Bug;
|
||||
}
|
||||
|
||||
// The `LintExpectationId` can be stable or unstable depending on when it was created.
|
||||
// Diagnostics created before the definition of `HirId`s are unstable and can not yet
|
||||
// be stored. Instead, they are buffered until the `LintExpectationId` is replaced by
|
||||
|
|
@ -1427,17 +1564,9 @@ impl HandlerInner {
|
|||
guaranteed
|
||||
}
|
||||
|
||||
fn emit_artifact_notification(&mut self, path: &Path, artifact_type: &str) {
|
||||
self.emitter.emit_artifact_notification(path, artifact_type);
|
||||
}
|
||||
|
||||
fn emit_unused_externs(&mut self, lint_level: rustc_lint_defs::Level, unused_externs: &[&str]) {
|
||||
self.emitter.emit_unused_externs(lint_level, unused_externs);
|
||||
}
|
||||
|
||||
fn treat_err_as_bug(&self) -> bool {
|
||||
self.flags.treat_err_as_bug.is_some_and(|c| {
|
||||
self.err_count() + self.lint_err_count + self.delayed_bug_count() >= c.get()
|
||||
self.err_count + self.lint_err_count + self.delayed_bug_count() >= c.get()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -1445,141 +1574,8 @@ impl HandlerInner {
|
|||
self.span_delayed_bugs.len() + self.good_path_delayed_bugs.len()
|
||||
}
|
||||
|
||||
fn print_error_count(&mut self, registry: &Registry) {
|
||||
self.emit_stashed_diagnostics();
|
||||
|
||||
let warnings = match self.deduplicated_warn_count {
|
||||
0 => Cow::from(""),
|
||||
1 => Cow::from("1 warning emitted"),
|
||||
count => Cow::from(format!("{count} warnings emitted")),
|
||||
};
|
||||
let errors = match self.deduplicated_err_count {
|
||||
0 => Cow::from(""),
|
||||
1 => Cow::from("aborting due to 1 previous error"),
|
||||
count => Cow::from(format!("aborting due to {count} previous errors")),
|
||||
};
|
||||
if self.treat_err_as_bug() {
|
||||
return;
|
||||
}
|
||||
|
||||
match (errors.len(), warnings.len()) {
|
||||
(0, 0) => return,
|
||||
(0, _) => self.emitter.emit_diagnostic(&Diagnostic::new(
|
||||
Level::Warning(None),
|
||||
DiagnosticMessage::Str(warnings),
|
||||
)),
|
||||
(_, 0) => {
|
||||
let _ = self.fatal(errors);
|
||||
}
|
||||
(_, _) => {
|
||||
let _ = self.fatal(format!("{errors}; {warnings}"));
|
||||
}
|
||||
}
|
||||
|
||||
let can_show_explain = self.emitter.should_show_explain();
|
||||
let are_there_diagnostics = !self.emitted_diagnostic_codes.is_empty();
|
||||
if can_show_explain && are_there_diagnostics {
|
||||
let mut error_codes = self
|
||||
.emitted_diagnostic_codes
|
||||
.iter()
|
||||
.filter_map(|x| match &x {
|
||||
DiagnosticId::Error(s) if registry.try_find_description(s).is_ok() => {
|
||||
Some(s.clone())
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if !error_codes.is_empty() {
|
||||
error_codes.sort();
|
||||
if error_codes.len() > 1 {
|
||||
let limit = if error_codes.len() > 9 { 9 } else { error_codes.len() };
|
||||
self.failure_note(format!(
|
||||
"Some errors have detailed explanations: {}{}",
|
||||
error_codes[..limit].join(", "),
|
||||
if error_codes.len() > 9 { "..." } else { "." }
|
||||
));
|
||||
self.failure_note(format!(
|
||||
"For more information about an error, try \
|
||||
`rustc --explain {}`.",
|
||||
&error_codes[0]
|
||||
));
|
||||
} else {
|
||||
self.failure_note(format!(
|
||||
"For more information about this error, try \
|
||||
`rustc --explain {}`.",
|
||||
&error_codes[0]
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn stash(&mut self, key: (Span, StashKey), diagnostic: Diagnostic) {
|
||||
// Track the diagnostic for counts, but don't panic-if-treat-err-as-bug
|
||||
// yet; that happens when we actually emit the diagnostic.
|
||||
if diagnostic.is_error() {
|
||||
if matches!(diagnostic.level, Level::Error { lint: true }) {
|
||||
self.lint_err_count += 1;
|
||||
} else {
|
||||
self.err_count += 1;
|
||||
}
|
||||
} else {
|
||||
// Warnings are only automatically flushed if they're forced.
|
||||
if diagnostic.is_force_warn() {
|
||||
self.warn_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic
|
||||
// if/when we have a more robust macro-friendly replacement for `(span, key)` as a key.
|
||||
// See the PR for a discussion.
|
||||
self.stashed_diagnostics.insert(key, diagnostic);
|
||||
}
|
||||
|
||||
fn steal(&mut self, key: (Span, StashKey)) -> Option<Diagnostic> {
|
||||
let diagnostic = self.stashed_diagnostics.remove(&key)?;
|
||||
if diagnostic.is_error() {
|
||||
if matches!(diagnostic.level, Level::Error { lint: true }) {
|
||||
self.lint_err_count -= 1;
|
||||
} else {
|
||||
self.err_count -= 1;
|
||||
}
|
||||
} else {
|
||||
if diagnostic.is_force_warn() {
|
||||
self.warn_count -= 1;
|
||||
}
|
||||
}
|
||||
Some(diagnostic)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn err_count(&self) -> usize {
|
||||
self.err_count
|
||||
}
|
||||
|
||||
fn has_errors(&self) -> bool {
|
||||
self.err_count() > 0
|
||||
}
|
||||
fn has_errors_or_lint_errors(&self) -> bool {
|
||||
self.has_errors() || self.lint_err_count > 0
|
||||
}
|
||||
fn has_errors_or_span_delayed_bugs(&self) -> bool {
|
||||
self.has_errors() || !self.span_delayed_bugs.is_empty()
|
||||
}
|
||||
fn has_any_message(&self) -> bool {
|
||||
self.err_count() > 0 || self.lint_err_count > 0 || self.warn_count > 0
|
||||
}
|
||||
|
||||
fn is_compilation_going_to_fail(&self) -> bool {
|
||||
self.has_errors() || self.lint_err_count > 0 || !self.span_delayed_bugs.is_empty()
|
||||
}
|
||||
|
||||
fn abort_if_errors(&mut self) {
|
||||
self.emit_stashed_diagnostics();
|
||||
|
||||
if self.has_errors() {
|
||||
FatalError.raise();
|
||||
}
|
||||
self.err_count > 0
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
|
|
@ -1592,67 +1588,10 @@ impl HandlerInner {
|
|||
self.emit_diagnostic(diag.set_span(sp));
|
||||
}
|
||||
|
||||
/// For documentation on this, see `Session::span_delayed_bug`.
|
||||
///
|
||||
/// Note: this function used to be called `delay_span_bug`. It was renamed
|
||||
/// to match similar functions like `span_bug`, `span_err`, etc.
|
||||
#[track_caller]
|
||||
fn span_delayed_bug(
|
||||
&mut self,
|
||||
sp: impl Into<MultiSpan>,
|
||||
msg: impl Into<String>,
|
||||
) -> ErrorGuaranteed {
|
||||
// This is technically `self.treat_err_as_bug()` but `span_delayed_bug` is called before
|
||||
// incrementing `err_count` by one, so we need to +1 the comparing.
|
||||
// FIXME: Would be nice to increment err_count in a more coherent way.
|
||||
if self.flags.treat_err_as_bug.is_some_and(|c| {
|
||||
self.err_count() + self.lint_err_count + self.delayed_bug_count() + 1 >= c.get()
|
||||
}) {
|
||||
// FIXME: don't abort here if report_delayed_bugs is off
|
||||
self.span_bug(sp, msg.into());
|
||||
}
|
||||
let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg.into());
|
||||
diagnostic.set_span(sp.into());
|
||||
self.emit_diagnostic(&mut diagnostic).unwrap()
|
||||
}
|
||||
|
||||
// FIXME(eddyb) note the comment inside `impl Drop for HandlerInner`, that's
|
||||
// where the explanation of what "good path" is (also, it should be renamed).
|
||||
fn good_path_delayed_bug(&mut self, msg: impl Into<DiagnosticMessage>) {
|
||||
let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg);
|
||||
if self.flags.report_delayed_bugs {
|
||||
self.emit_diagnostic(&mut diagnostic);
|
||||
}
|
||||
let backtrace = std::backtrace::Backtrace::capture();
|
||||
self.good_path_delayed_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace));
|
||||
}
|
||||
|
||||
fn failure_note(&mut self, msg: impl Into<DiagnosticMessage>) {
|
||||
self.emit_diagnostic(&mut Diagnostic::new(FailureNote, msg));
|
||||
}
|
||||
|
||||
fn fatal(&mut self, msg: impl Into<DiagnosticMessage>) -> FatalError {
|
||||
self.emit(Fatal, msg);
|
||||
FatalError
|
||||
}
|
||||
|
||||
fn err(&mut self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
|
||||
self.emit(Error { lint: false }, msg)
|
||||
}
|
||||
|
||||
/// Emit an error; level should be `Error` or `Fatal`.
|
||||
fn emit(&mut self, level: Level, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
|
||||
if self.treat_err_as_bug() {
|
||||
self.bug(msg);
|
||||
}
|
||||
self.emit_diagnostic(&mut Diagnostic::new(level, msg)).unwrap()
|
||||
}
|
||||
|
||||
fn bug(&mut self, msg: impl Into<DiagnosticMessage>) -> ! {
|
||||
self.emit_diagnostic(&mut Diagnostic::new(Bug, msg));
|
||||
panic::panic_any(ExplicitBug);
|
||||
}
|
||||
|
||||
fn flush_delayed(
|
||||
&mut self,
|
||||
bugs: impl IntoIterator<Item = DelayedDiagnostic>,
|
||||
|
|
@ -1723,7 +1662,7 @@ impl HandlerInner {
|
|||
fn panic_if_treat_err_as_bug(&self) {
|
||||
if self.treat_err_as_bug() {
|
||||
match (
|
||||
self.err_count() + self.lint_err_count,
|
||||
self.err_count + self.lint_err_count,
|
||||
self.delayed_bug_count(),
|
||||
self.flags.treat_err_as_bug.map(|c| c.get()).unwrap(),
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -1145,11 +1145,6 @@ impl<'a> ExtCtxt<'a> {
|
|||
pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) {
|
||||
self.sess.diagnostic().span_err(sp, msg);
|
||||
}
|
||||
#[rustc_lint_diagnostics]
|
||||
#[track_caller]
|
||||
pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) {
|
||||
self.sess.diagnostic().span_warn(sp, msg);
|
||||
}
|
||||
pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<String>) -> ! {
|
||||
self.sess.diagnostic().span_bug(sp, msg);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -547,7 +547,7 @@ impl<'a> ExtCtxt<'a> {
|
|||
binder: ast::ClosureBinder::NotPresent,
|
||||
capture_clause: ast::CaptureBy::Ref,
|
||||
constness: ast::Const::No,
|
||||
asyncness: ast::Async::No,
|
||||
coro_kind: None,
|
||||
movability: ast::Movability::Movable,
|
||||
fn_decl,
|
||||
body,
|
||||
|
|
|
|||
|
|
@ -719,12 +719,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
and it is only intended to be used in `alloc`."
|
||||
),
|
||||
|
||||
rustc_attr!(
|
||||
rustc_host, AttributeType::Normal, template!(Word), ErrorFollowing,
|
||||
"#[rustc_host] annotates const generic parameters as the `host` effect param, \
|
||||
and it is only intended for internal use and as a desugaring."
|
||||
),
|
||||
|
||||
BuiltinAttribute {
|
||||
name: sym::rustc_diagnostic_item,
|
||||
// FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`.
|
||||
|
|
|
|||
|
|
@ -158,6 +158,9 @@ declare_features! (
|
|||
/// Allows using `#[plugin_registrar]` on functions.
|
||||
(removed, plugin_registrar, "1.54.0", Some(29597), None,
|
||||
Some("plugins are no longer supported")),
|
||||
/// Allows exhaustive integer pattern matching with `usize::MAX`/`isize::MIN`/`isize::MAX`.
|
||||
(removed, precise_pointer_size_matching, "1.32.0", Some(56354), None,
|
||||
Some("removed in favor of half-open ranges")),
|
||||
(removed, proc_macro_expr, "1.27.0", Some(54727), None,
|
||||
Some("subsumed by `#![feature(proc_macro_hygiene)]`")),
|
||||
(removed, proc_macro_gen, "1.27.0", Some(54727), None,
|
||||
|
|
|
|||
|
|
@ -56,8 +56,10 @@ macro_rules! declare_features {
|
|||
#[derive(Clone, Default, Debug)]
|
||||
pub struct Features {
|
||||
/// `#![feature]` attrs for language features, for error reporting.
|
||||
/// "declared" here means that the feature is actually enabled in the current crate.
|
||||
pub declared_lang_features: Vec<(Symbol, Span, Option<Symbol>)>,
|
||||
/// `#![feature]` attrs for non-language (library) features.
|
||||
/// "declared" here means that the feature is actually enabled in the current crate.
|
||||
pub declared_lib_features: Vec<(Symbol, Span)>,
|
||||
/// `declared_lang_features` + `declared_lib_features`.
|
||||
pub declared_features: FxHashSet<Symbol>,
|
||||
|
|
@ -133,9 +135,18 @@ macro_rules! declare_features {
|
|||
$(
|
||||
sym::$feature => status_to_enum!($status) == FeatureStatus::Internal,
|
||||
)*
|
||||
// Accepted/removed features aren't in this file but are never internal
|
||||
// (a removed feature might have been internal, but that's now irrelevant).
|
||||
_ if self.declared_features.contains(&feature) => false,
|
||||
_ if self.declared_features.contains(&feature) => {
|
||||
// This could be accepted/removed, or a libs feature.
|
||||
// Accepted/removed features aren't in this file but are never internal
|
||||
// (a removed feature might have been internal, but that's now irrelevant).
|
||||
// Libs features are internal if they end in `_internal` or `_internals`.
|
||||
// As a special exception we also consider `core_intrinsics` internal;
|
||||
// renaming that age-old feature is just not worth the hassle.
|
||||
// We just always test the name; it's not a big deal if we accidentally hit
|
||||
// an accepted/removed lang feature that way.
|
||||
let name = feature.as_str();
|
||||
name == "core_intrinsics" || name.ends_with("_internal") || name.ends_with("_internals")
|
||||
}
|
||||
_ => panic!("`{}` was not listed in `declare_features`", feature),
|
||||
}
|
||||
}
|
||||
|
|
@ -215,9 +226,6 @@ declare_features! (
|
|||
(internal, test_2018_feature, "1.31.0", None, Some(Edition::Edition2018)),
|
||||
/// Added for testing unstable lints; perma-unstable.
|
||||
(internal, test_unstable_lint, "1.60.0", None, None),
|
||||
/// Allows non-`unsafe` —and thus, unsound— access to `Pin` constructions.
|
||||
/// Marked `internal` since perma-unstable and unsound.
|
||||
(internal, unsafe_pin_internals, "1.60.0", None, None),
|
||||
/// Use for stable + negative coherence and strict coherence depending on trait's
|
||||
/// rustc_strict_coherence value.
|
||||
(unstable, with_negative_coherence, "1.60.0", None, None),
|
||||
|
|
@ -543,8 +551,6 @@ declare_features! (
|
|||
(unstable, offset_of_enum, "1.75.0", Some(106655), None),
|
||||
/// Allows using `#[optimize(X)]`.
|
||||
(unstable, optimize_attribute, "1.34.0", Some(54882), None),
|
||||
/// Allows exhaustive integer pattern matching on `usize` and `isize`.
|
||||
(unstable, precise_pointer_size_matching, "1.32.0", Some(56354), None),
|
||||
/// Allows macro attributes on expressions, statements and non-inline modules.
|
||||
(unstable, proc_macro_hygiene, "1.30.0", Some(54727), None),
|
||||
/// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions.
|
||||
|
|
|
|||
|
|
@ -487,6 +487,7 @@ pub enum GenericParamKind<'hir> {
|
|||
ty: &'hir Ty<'hir>,
|
||||
/// Optional default value for the const generic param
|
||||
default: Option<AnonConst>,
|
||||
is_host_effect: bool,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -2255,6 +2256,8 @@ pub enum ImplItemKind<'hir> {
|
|||
|
||||
/// The name of the associated type for `Fn` return types.
|
||||
pub const FN_OUTPUT_NAME: Symbol = sym::Output;
|
||||
/// The name of the associated type for `Iterator` item types.
|
||||
pub const ITERATOR_ITEM_NAME: Symbol = sym::Item;
|
||||
|
||||
/// Bind a type to an associated type (i.e., `A = Foo`).
|
||||
///
|
||||
|
|
|
|||
|
|
@ -874,7 +874,7 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Generi
|
|||
match param.kind {
|
||||
GenericParamKind::Lifetime { .. } => {}
|
||||
GenericParamKind::Type { ref default, .. } => walk_list!(visitor, visit_ty, default),
|
||||
GenericParamKind::Const { ref ty, ref default } => {
|
||||
GenericParamKind::Const { ref ty, ref default, is_host_effect: _ } => {
|
||||
visitor.visit_ty(ty);
|
||||
if let Some(ref default) = default {
|
||||
visitor.visit_const_param_default(param.hir_id, default);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorG
|
|||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_infer::traits::FulfillmentError;
|
||||
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, suggest_constraining_type_param, AssocItem, AssocKind, Ty, TyCtxt};
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::edit_distance::find_best_match_for_name;
|
||||
use rustc_span::symbol::{sym, Ident};
|
||||
|
|
@ -509,6 +509,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
if associated_types.values().all(|v| v.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
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.
|
||||
|
|
@ -581,6 +582,32 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
}
|
||||
}
|
||||
|
||||
// We get all the associated items that _are_ set,
|
||||
// so that we can check if any of their names match one of the ones we are missing.
|
||||
// This would mean that they are shadowing the associated type we are missing,
|
||||
// and we can then use their span to indicate this to the user.
|
||||
let bound_names = trait_bounds
|
||||
.iter()
|
||||
.filter_map(|poly_trait_ref| {
|
||||
let path = poly_trait_ref.trait_ref.path.segments.last()?;
|
||||
let args = path.args?;
|
||||
|
||||
Some(args.bindings.iter().filter_map(|binding| {
|
||||
let ident = binding.ident;
|
||||
let trait_def = path.res.def_id();
|
||||
let assoc_item = tcx.associated_items(trait_def).find_by_name_and_kind(
|
||||
tcx,
|
||||
ident,
|
||||
AssocKind::Type,
|
||||
trait_def,
|
||||
);
|
||||
|
||||
Some((ident.name, assoc_item?))
|
||||
}))
|
||||
})
|
||||
.flatten()
|
||||
.collect::<FxHashMap<Symbol, &AssocItem>>();
|
||||
|
||||
let mut names = names
|
||||
.into_iter()
|
||||
.map(|(trait_, mut assocs)| {
|
||||
|
|
@ -621,16 +648,42 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
*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;
|
||||
|
||||
if let Some(assoc_item) = bound_names.get(&item.name)
|
||||
&& assoc_item != &item
|
||||
{
|
||||
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));
|
||||
err.span_label(
|
||||
sp,
|
||||
format!("`{}{}` defined here{}", prefix, item.name, rename_message),
|
||||
);
|
||||
}
|
||||
}
|
||||
if potential_assoc_types.len() == assoc_items.len() {
|
||||
|
|
@ -638,8 +691,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
// 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) =
|
||||
(tcx.sess.source_map().span_to_snippet(*span), dupes)
|
||||
} else if let (Ok(snippet), false, false) =
|
||||
(tcx.sess.source_map().span_to_snippet(*span), dupes, shadows)
|
||||
{
|
||||
let types: Vec<_> =
|
||||
assoc_items.iter().map(|item| format!("{} = Type", item.name)).collect();
|
||||
|
|
@ -721,6 +774,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
err.span_help(where_constraints, where_msg);
|
||||
}
|
||||
}
|
||||
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -243,6 +243,31 @@ pub fn create_args_for_parent_generic_args<'tcx, 'a>(
|
|||
match (args_iter.peek(), params.peek()) {
|
||||
(Some(&arg), Some(¶m)) => {
|
||||
match (arg, ¶m.kind, arg_count.explicit_late_bound) {
|
||||
(
|
||||
GenericArg::Const(hir::ConstArg {
|
||||
is_desugared_from_effects: true,
|
||||
..
|
||||
}),
|
||||
GenericParamDefKind::Const { is_host_effect: false, .. }
|
||||
| GenericParamDefKind::Type { .. }
|
||||
| GenericParamDefKind::Lifetime,
|
||||
_,
|
||||
) => {
|
||||
// SPECIAL CASE FOR DESUGARED EFFECT PARAMS
|
||||
// This comes from the following example:
|
||||
//
|
||||
// ```
|
||||
// #[const_trait]
|
||||
// pub trait PartialEq<Rhs: ?Sized = Self> {}
|
||||
// impl const PartialEq for () {}
|
||||
// ```
|
||||
//
|
||||
// Since this is a const impl, we need to insert `<false>` at the end of
|
||||
// `PartialEq`'s generics, but this errors since `Rhs` isn't specified.
|
||||
// To work around this, we infer all arguments until we reach the host param.
|
||||
args.push(ctx.inferred_kind(Some(&args), param, infer_args));
|
||||
params.next();
|
||||
}
|
||||
(GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _)
|
||||
| (
|
||||
GenericArg::Type(_) | GenericArg::Infer(_),
|
||||
|
|
|
|||
|
|
@ -1182,10 +1182,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
if let Some(bound_span) = bound_span {
|
||||
err.span_label(
|
||||
bound_span,
|
||||
format!(
|
||||
"ambiguous `{assoc_name}` from `{}`",
|
||||
bound.print_only_trait_path(),
|
||||
),
|
||||
format!("ambiguous `{assoc_name}` from `{}`", bound.print_trait_sugared(),),
|
||||
);
|
||||
if let Some(constraint) = &is_equality {
|
||||
where_bounds.push(format!(
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
trait here instead: `trait NewTrait: {} {{}}`",
|
||||
regular_traits
|
||||
.iter()
|
||||
// FIXME: This should `print_sugared`, but also needs to integrate projection bounds...
|
||||
.map(|t| t.trait_ref().print_only_trait_path().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" + "),
|
||||
|
|
|
|||
|
|
@ -859,7 +859,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
|
|||
hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } => Ok(()),
|
||||
|
||||
// Const parameters are well formed if their type is structural match.
|
||||
hir::GenericParamKind::Const { ty: hir_ty, default: _ } => {
|
||||
hir::GenericParamKind::Const { ty: hir_ty, default: _, is_host_effect: _ } => {
|
||||
let ty = tcx.type_of(param.def_id).instantiate_identity();
|
||||
|
||||
if tcx.features().adt_const_params {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
|||
tcx.def_span(def_id),
|
||||
E0199,
|
||||
"implementing the trait `{}` is not unsafe",
|
||||
trait_ref.print_only_trait_path()
|
||||
trait_ref.print_trait_sugared()
|
||||
)
|
||||
.span_suggestion_verbose(
|
||||
item.span.with_hi(item.span.lo() + rustc_span::BytePos(7)),
|
||||
|
|
@ -40,13 +40,13 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
|||
tcx.def_span(def_id),
|
||||
E0200,
|
||||
"the trait `{}` requires an `unsafe impl` declaration",
|
||||
trait_ref.print_only_trait_path()
|
||||
trait_ref.print_trait_sugared()
|
||||
)
|
||||
.note(format!(
|
||||
"the trait `{}` enforces invariants that the compiler can't check. \
|
||||
Review the trait documentation and make sure this implementation \
|
||||
upholds those invariants before adding the `unsafe` keyword",
|
||||
trait_ref.print_only_trait_path()
|
||||
trait_ref.print_trait_sugared()
|
||||
))
|
||||
.span_suggestion_verbose(
|
||||
item.span.shrink_to_lo(),
|
||||
|
|
@ -69,7 +69,7 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
|||
"the trait `{}` enforces invariants that the compiler can't check. \
|
||||
Review the trait documentation and make sure this implementation \
|
||||
upholds those invariants before adding the `unsafe` keyword",
|
||||
trait_ref.print_only_trait_path()
|
||||
trait_ref.print_trait_sugared()
|
||||
))
|
||||
.span_suggestion_verbose(
|
||||
item.span.shrink_to_lo(),
|
||||
|
|
|
|||
|
|
@ -1383,7 +1383,7 @@ fn impl_trait_ref(
|
|||
let last_segment = path_segments.len() - 1;
|
||||
let mut args = *path_segments[last_segment].args();
|
||||
let last_arg = args.args.len() - 1;
|
||||
assert!(matches!(args.args[last_arg], hir::GenericArg::Const(anon_const) if tcx.has_attr(anon_const.value.def_id, sym::rustc_host)));
|
||||
assert!(matches!(args.args[last_arg], hir::GenericArg::Const(anon_const) if anon_const.is_desugared_from_effects));
|
||||
args.args = &args.args[..args.args.len() - 1];
|
||||
path_segments[last_segment].args = Some(&args);
|
||||
let path = hir::Path {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use rustc_hir::def_id::LocalDefId;
|
|||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_session::lint;
|
||||
use rustc_span::symbol::{kw, Symbol};
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_span::Span;
|
||||
|
||||
pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
||||
use rustc_hir::*;
|
||||
|
|
@ -298,13 +298,11 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
|||
kind,
|
||||
})
|
||||
}
|
||||
GenericParamKind::Const { default, .. } => {
|
||||
let is_host_param = tcx.has_attr(param.def_id, sym::rustc_host);
|
||||
|
||||
GenericParamKind::Const { ty: _, default, is_host_effect } => {
|
||||
if !matches!(allow_defaults, Defaults::Allowed)
|
||||
&& default.is_some()
|
||||
// `rustc_host` effect params are allowed to have defaults.
|
||||
&& !is_host_param
|
||||
// `host` effect params are allowed to have defaults.
|
||||
&& !is_host_effect
|
||||
{
|
||||
tcx.sess.span_err(
|
||||
param.span,
|
||||
|
|
@ -315,7 +313,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
|||
|
||||
let index = next_index();
|
||||
|
||||
if is_host_param {
|
||||
if is_host_effect {
|
||||
if let Some(idx) = host_effect_index {
|
||||
bug!("parent also has host effect param? index: {idx}, def: {def_id:?}");
|
||||
}
|
||||
|
|
@ -330,7 +328,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
|||
pure_wrt_drop: param.pure_wrt_drop,
|
||||
kind: ty::GenericParamDefKind::Const {
|
||||
has_default: default.is_some(),
|
||||
is_host_effect: is_host_param,
|
||||
is_host_effect,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
@ -489,7 +487,7 @@ struct AnonConstInParamTyDetector {
|
|||
|
||||
impl<'v> Visitor<'v> for AnonConstInParamTyDetector {
|
||||
fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) {
|
||||
if let GenericParamKind::Const { ty, default: _ } = p.kind {
|
||||
if let GenericParamKind::Const { ty, default: _, is_host_effect: _ } = p.kind {
|
||||
let prev = self.in_param_ty;
|
||||
self.in_param_ty = true;
|
||||
self.visit_ty(ty);
|
||||
|
|
|
|||
|
|
@ -992,7 +992,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
|
|||
self.visit_ty(ty);
|
||||
}
|
||||
}
|
||||
GenericParamKind::Const { ty, default } => {
|
||||
GenericParamKind::Const { ty, default, is_host_effect: _ } => {
|
||||
self.visit_ty(ty);
|
||||
if let Some(default) = default {
|
||||
self.visit_body(self.tcx.hir().body(default.body));
|
||||
|
|
|
|||
|
|
@ -2126,7 +2126,7 @@ impl<'a> State<'a> {
|
|||
self.print_type(default);
|
||||
}
|
||||
}
|
||||
GenericParamKind::Const { ty, ref default } => {
|
||||
GenericParamKind::Const { ty, ref default, is_host_effect: _ } => {
|
||||
self.word_space(":");
|
||||
self.print_type(ty);
|
||||
if let Some(default) = default {
|
||||
|
|
|
|||
|
|
@ -651,9 +651,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
},
|
||||
)
|
||||
}
|
||||
Some(hir::CoroutineKind::Gen(hir::CoroutineSource::Fn)) => {
|
||||
todo!("gen closures do not exist yet")
|
||||
}
|
||||
// For a `gen {}` block created as a `gen fn` body, we need the return type to be
|
||||
// ().
|
||||
Some(hir::CoroutineKind::Gen(hir::CoroutineSource::Fn)) => self.tcx.types.unit,
|
||||
|
||||
_ => astconv.ty_infer(None, decl.output.span()),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1619,6 +1619,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
None,
|
||||
);
|
||||
} else {
|
||||
if let Some(errors) =
|
||||
self.could_impl_trait(clone_trait_did, expected_ty, self.param_env)
|
||||
{
|
||||
match &errors[..] {
|
||||
[] => {}
|
||||
[error] => {
|
||||
diag.help(format!(
|
||||
"`Clone` is not implemented because the trait bound `{}` is \
|
||||
not satisfied",
|
||||
error.obligation.predicate,
|
||||
));
|
||||
}
|
||||
[errors @ .., last] => {
|
||||
diag.help(format!(
|
||||
"`Clone` is not implemented because the following trait bounds \
|
||||
could not be satisfied: {} and `{}`",
|
||||
errors
|
||||
.iter()
|
||||
.map(|e| format!("`{}`", e.obligation.predicate))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
last.obligation.predicate,
|
||||
));
|
||||
}
|
||||
}
|
||||
for error in errors {
|
||||
if let traits::FulfillmentErrorCode::CodeSelectionError(
|
||||
traits::SelectionError::Unimplemented,
|
||||
) = error.code
|
||||
&& let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
|
||||
error.obligation.predicate.kind().skip_binder()
|
||||
{
|
||||
self.infcx.err_ctxt().suggest_derive(
|
||||
&error.obligation,
|
||||
diag,
|
||||
error.obligation.predicate.kind().rebind(pred),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.suggest_derive(diag, &[(trait_ref.to_predicate(self.tcx), None, None)]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2288,7 +2288,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
ty::Adt(def, _) if def.did().is_local() => {
|
||||
spans.push_span_label(
|
||||
self.tcx.def_span(def.did()),
|
||||
format!("must implement `{}`", pred.trait_ref.print_only_trait_path()),
|
||||
format!("must implement `{}`", pred.trait_ref.print_trait_sugared()),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
|
|
@ -2299,7 +2299,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let msg = if preds.len() == 1 {
|
||||
format!(
|
||||
"an implementation of `{}` might be missing for `{}`",
|
||||
preds[0].trait_ref.print_only_trait_path(),
|
||||
preds[0].trait_ref.print_trait_sugared(),
|
||||
preds[0].self_ty()
|
||||
)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -2219,8 +2219,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
infer::ExistentialProjection(exp_found) => self.expected_found_str(exp_found),
|
||||
infer::PolyTraitRefs(exp_found) => {
|
||||
let pretty_exp_found = ty::error::ExpectedFound {
|
||||
expected: exp_found.expected.print_only_trait_path(),
|
||||
found: exp_found.found.print_only_trait_path(),
|
||||
expected: exp_found.expected.print_trait_sugared(),
|
||||
found: exp_found.found.print_trait_sugared(),
|
||||
};
|
||||
match self.expected_found_str(pretty_exp_found) {
|
||||
Some((expected, found, _, _)) if expected == found => {
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ use rustc_data_structures::sync::Lrc;
|
|||
use rustc_errors::registry::Registry;
|
||||
use rustc_errors::{ErrorGuaranteed, Handler};
|
||||
use rustc_lint::LintStore;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::util::Providers;
|
||||
use rustc_middle::{bug, ty};
|
||||
use rustc_parse::maybe_new_parser_from_source_str;
|
||||
use rustc_query_impl::QueryCtxt;
|
||||
use rustc_query_system::query::print_query_stack;
|
||||
|
|
@ -42,7 +42,7 @@ pub struct Compiler {
|
|||
}
|
||||
|
||||
/// Converts strings provided as `--cfg [cfgspec]` into a `Cfg`.
|
||||
pub(crate) fn parse_cfg(handler: &EarlyErrorHandler, cfgs: Vec<String>) -> Cfg {
|
||||
pub(crate) fn parse_cfg(handler: &Handler, cfgs: Vec<String>) -> Cfg {
|
||||
cfgs.into_iter()
|
||||
.map(|s| {
|
||||
let sess = ParseSess::with_silent_emitter(Some(format!(
|
||||
|
|
@ -52,10 +52,14 @@ pub(crate) fn parse_cfg(handler: &EarlyErrorHandler, cfgs: Vec<String>) -> Cfg {
|
|||
|
||||
macro_rules! error {
|
||||
($reason: expr) => {
|
||||
handler.early_error(format!(
|
||||
concat!("invalid `--cfg` argument: `{}` (", $reason, ")"),
|
||||
s
|
||||
));
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
#[allow(rustc::diagnostic_outside_of_impl)]
|
||||
handler
|
||||
.struct_fatal(format!(
|
||||
concat!("invalid `--cfg` argument: `{}` (", $reason, ")"),
|
||||
s
|
||||
))
|
||||
.emit();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -97,14 +101,13 @@ pub(crate) fn parse_cfg(handler: &EarlyErrorHandler, cfgs: Vec<String>) -> Cfg {
|
|||
}
|
||||
|
||||
/// Converts strings provided as `--check-cfg [specs]` into a `CheckCfg`.
|
||||
pub(crate) fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -> CheckCfg {
|
||||
pub(crate) fn parse_check_cfg(handler: &Handler, specs: Vec<String>) -> CheckCfg {
|
||||
// If any --check-cfg is passed then exhaustive_values and exhaustive_names
|
||||
// are enabled by default.
|
||||
let exhaustive_names = !specs.is_empty();
|
||||
let exhaustive_values = !specs.is_empty();
|
||||
let mut check_cfg = CheckCfg { exhaustive_names, exhaustive_values, ..CheckCfg::default() };
|
||||
|
||||
let mut old_syntax = None;
|
||||
for s in specs {
|
||||
let sess = ParseSess::with_silent_emitter(Some(format!(
|
||||
"this error occurred on the command line: `--check-cfg={s}`"
|
||||
|
|
@ -113,10 +116,14 @@ pub(crate) fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -
|
|||
|
||||
macro_rules! error {
|
||||
($reason:expr) => {
|
||||
handler.early_error(format!(
|
||||
concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"),
|
||||
s
|
||||
))
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
#[allow(rustc::diagnostic_outside_of_impl)]
|
||||
handler
|
||||
.struct_fatal(format!(
|
||||
concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"),
|
||||
s
|
||||
))
|
||||
.emit()
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -142,174 +149,101 @@ pub(crate) fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -
|
|||
expected_error();
|
||||
};
|
||||
|
||||
let mut set_old_syntax = || {
|
||||
// defaults are flipped for the old syntax
|
||||
if old_syntax == None {
|
||||
if !meta_item.has_name(sym::cfg) {
|
||||
expected_error();
|
||||
}
|
||||
|
||||
let mut names = Vec::new();
|
||||
let mut values: FxHashSet<_> = Default::default();
|
||||
|
||||
let mut any_specified = false;
|
||||
let mut values_specified = false;
|
||||
let mut values_any_specified = false;
|
||||
|
||||
for arg in args {
|
||||
if arg.is_word()
|
||||
&& let Some(ident) = arg.ident()
|
||||
{
|
||||
if values_specified {
|
||||
error!("`cfg()` names cannot be after values");
|
||||
}
|
||||
names.push(ident);
|
||||
} else if arg.has_name(sym::any)
|
||||
&& let Some(args) = arg.meta_item_list()
|
||||
{
|
||||
if any_specified {
|
||||
error!("`any()` cannot be specified multiple times");
|
||||
}
|
||||
any_specified = true;
|
||||
if !args.is_empty() {
|
||||
error!("`any()` must be empty");
|
||||
}
|
||||
} else if arg.has_name(sym::values)
|
||||
&& let Some(args) = arg.meta_item_list()
|
||||
{
|
||||
if names.is_empty() {
|
||||
error!("`values()` cannot be specified before the names");
|
||||
} else if values_specified {
|
||||
error!("`values()` cannot be specified multiple times");
|
||||
}
|
||||
values_specified = true;
|
||||
|
||||
for arg in args {
|
||||
if let Some(LitKind::Str(s, _)) = arg.lit().map(|lit| &lit.kind) {
|
||||
values.insert(Some(*s));
|
||||
} else if arg.has_name(sym::any)
|
||||
&& let Some(args) = arg.meta_item_list()
|
||||
{
|
||||
if values_any_specified {
|
||||
error!("`any()` in `values()` cannot be specified multiple times");
|
||||
}
|
||||
values_any_specified = true;
|
||||
if !args.is_empty() {
|
||||
error!("`any()` must be empty");
|
||||
}
|
||||
} else {
|
||||
error!("`values()` arguments must be string literals or `any()`");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!("`cfg()` arguments must be simple identifiers, `any()` or `values(...)`");
|
||||
}
|
||||
}
|
||||
|
||||
if values.is_empty() && !values_any_specified && !any_specified {
|
||||
values.insert(None);
|
||||
} else if !values.is_empty() && values_any_specified {
|
||||
error!(
|
||||
"`values()` arguments cannot specify string literals and `any()` at the same time"
|
||||
);
|
||||
}
|
||||
|
||||
if any_specified {
|
||||
if names.is_empty() && values.is_empty() && !values_specified && !values_any_specified {
|
||||
check_cfg.exhaustive_names = false;
|
||||
check_cfg.exhaustive_values = false;
|
||||
}
|
||||
old_syntax = Some(true);
|
||||
};
|
||||
|
||||
if meta_item.has_name(sym::names) {
|
||||
set_old_syntax();
|
||||
|
||||
check_cfg.exhaustive_names = true;
|
||||
for arg in args {
|
||||
if arg.is_word()
|
||||
&& let Some(ident) = arg.ident()
|
||||
{
|
||||
check_cfg.expecteds.entry(ident.name).or_insert(ExpectedValues::Any);
|
||||
} else {
|
||||
error!("`names()` arguments must be simple identifiers");
|
||||
}
|
||||
}
|
||||
} else if meta_item.has_name(sym::values) {
|
||||
set_old_syntax();
|
||||
|
||||
if let Some((name, values)) = args.split_first() {
|
||||
if name.is_word()
|
||||
&& let Some(ident) = name.ident()
|
||||
{
|
||||
let expected_values = check_cfg
|
||||
.expecteds
|
||||
.entry(ident.name)
|
||||
.and_modify(|expected_values| match expected_values {
|
||||
ExpectedValues::Some(_) => {}
|
||||
ExpectedValues::Any => {
|
||||
// handle the case where names(...) was done
|
||||
// before values by changing to a list
|
||||
*expected_values = ExpectedValues::Some(FxHashSet::default());
|
||||
}
|
||||
})
|
||||
.or_insert_with(|| ExpectedValues::Some(FxHashSet::default()));
|
||||
|
||||
let ExpectedValues::Some(expected_values) = expected_values else {
|
||||
bug!("`expected_values` should be a list a values")
|
||||
};
|
||||
|
||||
for val in values {
|
||||
if let Some(LitKind::Str(s, _)) = val.lit().map(|lit| &lit.kind) {
|
||||
expected_values.insert(Some(*s));
|
||||
} else {
|
||||
error!("`values()` arguments must be string literals");
|
||||
}
|
||||
}
|
||||
|
||||
if values.is_empty() {
|
||||
expected_values.insert(None);
|
||||
}
|
||||
} else {
|
||||
error!("`values()` first argument must be a simple identifier");
|
||||
}
|
||||
} else if args.is_empty() {
|
||||
check_cfg.exhaustive_values = true;
|
||||
} else {
|
||||
expected_error();
|
||||
}
|
||||
} else if meta_item.has_name(sym::cfg) {
|
||||
old_syntax = Some(false);
|
||||
|
||||
let mut names = Vec::new();
|
||||
let mut values: FxHashSet<_> = Default::default();
|
||||
|
||||
let mut any_specified = false;
|
||||
let mut values_specified = false;
|
||||
let mut values_any_specified = false;
|
||||
|
||||
for arg in args {
|
||||
if arg.is_word()
|
||||
&& let Some(ident) = arg.ident()
|
||||
{
|
||||
if values_specified {
|
||||
error!("`cfg()` names cannot be after values");
|
||||
}
|
||||
names.push(ident);
|
||||
} else if arg.has_name(sym::any)
|
||||
&& let Some(args) = arg.meta_item_list()
|
||||
{
|
||||
if any_specified {
|
||||
error!("`any()` cannot be specified multiple times");
|
||||
}
|
||||
any_specified = true;
|
||||
if !args.is_empty() {
|
||||
error!("`any()` must be empty");
|
||||
}
|
||||
} else if arg.has_name(sym::values)
|
||||
&& let Some(args) = arg.meta_item_list()
|
||||
{
|
||||
if names.is_empty() {
|
||||
error!("`values()` cannot be specified before the names");
|
||||
} else if values_specified {
|
||||
error!("`values()` cannot be specified multiple times");
|
||||
}
|
||||
values_specified = true;
|
||||
|
||||
for arg in args {
|
||||
if let Some(LitKind::Str(s, _)) = arg.lit().map(|lit| &lit.kind) {
|
||||
values.insert(Some(*s));
|
||||
} else if arg.has_name(sym::any)
|
||||
&& let Some(args) = arg.meta_item_list()
|
||||
{
|
||||
if values_any_specified {
|
||||
error!("`any()` in `values()` cannot be specified multiple times");
|
||||
}
|
||||
values_any_specified = true;
|
||||
if !args.is_empty() {
|
||||
error!("`any()` must be empty");
|
||||
}
|
||||
} else {
|
||||
error!("`values()` arguments must be string literals or `any()`");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!(
|
||||
"`cfg()` arguments must be simple identifiers, `any()` or `values(...)`"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if values.is_empty() && !values_any_specified && !any_specified {
|
||||
values.insert(None);
|
||||
} else if !values.is_empty() && values_any_specified {
|
||||
error!(
|
||||
"`values()` arguments cannot specify string literals and `any()` at the same time"
|
||||
);
|
||||
}
|
||||
|
||||
if any_specified {
|
||||
if names.is_empty()
|
||||
&& values.is_empty()
|
||||
&& !values_specified
|
||||
&& !values_any_specified
|
||||
{
|
||||
check_cfg.exhaustive_names = false;
|
||||
} else {
|
||||
error!("`cfg(any())` can only be provided in isolation");
|
||||
}
|
||||
} else {
|
||||
for name in names {
|
||||
check_cfg
|
||||
.expecteds
|
||||
.entry(name.name)
|
||||
.and_modify(|v| match v {
|
||||
ExpectedValues::Some(v) if !values_any_specified => {
|
||||
v.extend(values.clone())
|
||||
}
|
||||
ExpectedValues::Some(_) => *v = ExpectedValues::Any,
|
||||
ExpectedValues::Any => {}
|
||||
})
|
||||
.or_insert_with(|| {
|
||||
if values_any_specified {
|
||||
ExpectedValues::Any
|
||||
} else {
|
||||
ExpectedValues::Some(values.clone())
|
||||
}
|
||||
});
|
||||
}
|
||||
error!("`cfg(any())` can only be provided in isolation");
|
||||
}
|
||||
} else {
|
||||
expected_error();
|
||||
for name in names {
|
||||
check_cfg
|
||||
.expecteds
|
||||
.entry(name.name)
|
||||
.and_modify(|v| match v {
|
||||
ExpectedValues::Some(v) if !values_any_specified => {
|
||||
v.extend(values.clone())
|
||||
}
|
||||
ExpectedValues::Some(_) => *v = ExpectedValues::Any,
|
||||
ExpectedValues::Any => {}
|
||||
})
|
||||
.or_insert_with(|| {
|
||||
if values_any_specified {
|
||||
ExpectedValues::Any
|
||||
} else {
|
||||
ExpectedValues::Some(values.clone())
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -388,13 +322,13 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
|
|||
|| {
|
||||
crate::callbacks::setup_callbacks();
|
||||
|
||||
let handler = EarlyErrorHandler::new(config.opts.error_format);
|
||||
let early_handler = EarlyErrorHandler::new(config.opts.error_format);
|
||||
|
||||
let codegen_backend = if let Some(make_codegen_backend) = config.make_codegen_backend {
|
||||
make_codegen_backend(&config.opts)
|
||||
} else {
|
||||
util::get_codegen_backend(
|
||||
&handler,
|
||||
&early_handler,
|
||||
&config.opts.maybe_sysroot,
|
||||
config.opts.unstable_opts.codegen_backend.as_deref(),
|
||||
)
|
||||
|
|
@ -411,7 +345,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
|
|||
) {
|
||||
Ok(bundle) => bundle,
|
||||
Err(e) => {
|
||||
handler.early_error(format!("failed to load fluent bundle: {e}"));
|
||||
early_handler.early_error(format!("failed to load fluent bundle: {e}"));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -422,7 +356,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
|
|||
let target_override = codegen_backend.target_override(&config.opts);
|
||||
|
||||
let mut sess = rustc_session::build_session(
|
||||
&handler,
|
||||
early_handler,
|
||||
config.opts,
|
||||
CompilerIO {
|
||||
input: config.input,
|
||||
|
|
@ -444,12 +378,12 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
|
|||
|
||||
codegen_backend.init(&sess);
|
||||
|
||||
let cfg = parse_cfg(&handler, config.crate_cfg);
|
||||
let cfg = parse_cfg(&sess.diagnostic(), config.crate_cfg);
|
||||
let mut cfg = config::build_configuration(&sess, cfg);
|
||||
util::add_configuration(&mut cfg, &mut sess, &*codegen_backend);
|
||||
sess.parse_sess.config = cfg;
|
||||
|
||||
let mut check_cfg = parse_check_cfg(&handler, config.crate_check_cfg);
|
||||
let mut check_cfg = parse_check_cfg(&sess.diagnostic(), config.crate_check_cfg);
|
||||
check_cfg.fill_well_known(&sess.target);
|
||||
sess.parse_sess.check_config = check_cfg;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
#![feature(let_chains)]
|
||||
#![feature(try_blocks)]
|
||||
#![recursion_limit = "256"]
|
||||
#![allow(rustc::potential_query_instability)]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
|
||||
|
|
|
|||
|
|
@ -306,6 +306,8 @@ fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) {
|
|||
|
||||
// Gate identifiers containing invalid Unicode codepoints that were recovered during lexing.
|
||||
sess.parse_sess.bad_unicode_identifiers.with_lock(|identifiers| {
|
||||
// We will soon sort, so the initial order does not matter.
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
let mut identifiers: Vec<_> = identifiers.drain().collect();
|
||||
identifiers.sort_by_key(|&(key, _)| key);
|
||||
for (ident, mut spans) in identifiers.into_iter() {
|
||||
|
|
@ -431,6 +433,9 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
|
|||
escape_dep_filename(&file.prefer_local().to_string())
|
||||
};
|
||||
|
||||
// The entries will be used to declare dependencies beween files in a
|
||||
// Makefile-like output, so the iteration order does not matter.
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
let extra_tracked_files =
|
||||
file_depinfo.iter().map(|path_sym| normalize_path(PathBuf::from(path_sym.as_str())));
|
||||
files.extend(extra_tracked_files);
|
||||
|
|
@ -486,6 +491,8 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
|
|||
// Emit special comments with information about accessed environment variables.
|
||||
let env_depinfo = sess.parse_sess.env_depinfo.borrow();
|
||||
if !env_depinfo.is_empty() {
|
||||
// We will soon sort, so the initial order does not matter.
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
let mut envs: Vec<_> = env_depinfo
|
||||
.iter()
|
||||
.map(|(k, v)| (escape_dep_env(*k), v.map(escape_dep_env)))
|
||||
|
|
|
|||
|
|
@ -25,10 +25,10 @@ use std::num::NonZeroUsize;
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
fn mk_session(handler: &mut EarlyErrorHandler, matches: getopts::Matches) -> (Session, Cfg) {
|
||||
fn mk_session(matches: getopts::Matches) -> (Session, Cfg) {
|
||||
let mut early_handler = EarlyErrorHandler::new(ErrorOutputType::default());
|
||||
let registry = registry::Registry::new(&[]);
|
||||
let sessopts = build_session_options(handler, &matches);
|
||||
let cfg = parse_cfg(handler, matches.opt_strs("cfg"));
|
||||
let sessopts = build_session_options(&mut early_handler, &matches);
|
||||
let temps_dir = sessopts.unstable_opts.temps_dir.as_deref().map(PathBuf::from);
|
||||
let io = CompilerIO {
|
||||
input: Input::Str { name: FileName::Custom(String::new()), input: String::new() },
|
||||
|
|
@ -37,7 +37,7 @@ fn mk_session(handler: &mut EarlyErrorHandler, matches: getopts::Matches) -> (Se
|
|||
temps_dir,
|
||||
};
|
||||
let sess = build_session(
|
||||
handler,
|
||||
early_handler,
|
||||
sessopts,
|
||||
io,
|
||||
None,
|
||||
|
|
@ -51,6 +51,7 @@ fn mk_session(handler: &mut EarlyErrorHandler, matches: getopts::Matches) -> (Se
|
|||
Arc::default(),
|
||||
Default::default(),
|
||||
);
|
||||
let cfg = parse_cfg(&sess.diagnostic(), matches.opt_strs("cfg"));
|
||||
(sess, cfg)
|
||||
}
|
||||
|
||||
|
|
@ -117,8 +118,7 @@ fn assert_non_crate_hash_different(x: &Options, y: &Options) {
|
|||
fn test_switch_implies_cfg_test() {
|
||||
rustc_span::create_default_session_globals_then(|| {
|
||||
let matches = optgroups().parse(&["--test".to_string()]).unwrap();
|
||||
let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
|
||||
let (sess, cfg) = mk_session(&mut handler, matches);
|
||||
let (sess, cfg) = mk_session(matches);
|
||||
let cfg = build_configuration(&sess, cfg);
|
||||
assert!(cfg.contains(&(sym::test, None)));
|
||||
});
|
||||
|
|
@ -129,8 +129,7 @@ fn test_switch_implies_cfg_test() {
|
|||
fn test_switch_implies_cfg_test_unless_cfg_test() {
|
||||
rustc_span::create_default_session_globals_then(|| {
|
||||
let matches = optgroups().parse(&["--test".to_string(), "--cfg=test".to_string()]).unwrap();
|
||||
let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
|
||||
let (sess, cfg) = mk_session(&mut handler, matches);
|
||||
let (sess, cfg) = mk_session(matches);
|
||||
let cfg = build_configuration(&sess, cfg);
|
||||
let mut test_items = cfg.iter().filter(|&&(name, _)| name == sym::test);
|
||||
assert!(test_items.next().is_some());
|
||||
|
|
@ -142,23 +141,20 @@ fn test_switch_implies_cfg_test_unless_cfg_test() {
|
|||
fn test_can_print_warnings() {
|
||||
rustc_span::create_default_session_globals_then(|| {
|
||||
let matches = optgroups().parse(&["-Awarnings".to_string()]).unwrap();
|
||||
let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
|
||||
let (sess, _) = mk_session(&mut handler, matches);
|
||||
let (sess, _) = mk_session(matches);
|
||||
assert!(!sess.diagnostic().can_emit_warnings());
|
||||
});
|
||||
|
||||
rustc_span::create_default_session_globals_then(|| {
|
||||
let matches =
|
||||
optgroups().parse(&["-Awarnings".to_string(), "-Dwarnings".to_string()]).unwrap();
|
||||
let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
|
||||
let (sess, _) = mk_session(&mut handler, matches);
|
||||
let (sess, _) = mk_session(matches);
|
||||
assert!(sess.diagnostic().can_emit_warnings());
|
||||
});
|
||||
|
||||
rustc_span::create_default_session_globals_then(|| {
|
||||
let matches = optgroups().parse(&["-Adead_code".to_string()]).unwrap();
|
||||
let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
|
||||
let (sess, _) = mk_session(&mut handler, matches);
|
||||
let (sess, _) = mk_session(matches);
|
||||
assert!(sess.diagnostic().can_emit_warnings());
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,7 +162,11 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
|
|||
// Explicitly check for lints associated with 'closure_id', since
|
||||
// it does not have a corresponding AST node
|
||||
if let ast_visit::FnKind::Fn(_, _, sig, _, _, _) = fk {
|
||||
if let ast::Async::Yes { closure_id, .. } = sig.header.asyncness {
|
||||
if let Some(
|
||||
ast::CoroutineKind::Async { closure_id, .. }
|
||||
| ast::CoroutineKind::Gen { closure_id, .. },
|
||||
) = sig.header.coro_kind
|
||||
{
|
||||
self.check_id(closure_id);
|
||||
}
|
||||
}
|
||||
|
|
@ -223,7 +227,11 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
|
|||
// it does not have a corresponding AST node
|
||||
match e.kind {
|
||||
ast::ExprKind::Closure(box ast::Closure {
|
||||
asyncness: ast::Async::Yes { closure_id, .. },
|
||||
coro_kind:
|
||||
Some(
|
||||
ast::CoroutineKind::Async { closure_id, .. }
|
||||
| ast::CoroutineKind::Gen { closure_id, .. },
|
||||
),
|
||||
..
|
||||
}) => self.check_id(closure_id),
|
||||
_ => {}
|
||||
|
|
|
|||
|
|
@ -534,9 +534,9 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
|
|||
}
|
||||
|
||||
fn check_generic_param(&mut self, cx: &LateContext<'_>, param: &hir::GenericParam<'_>) {
|
||||
if let GenericParamKind::Const { .. } = param.kind {
|
||||
// `rustc_host` params are explicitly allowed to be lowercase.
|
||||
if cx.tcx.has_attr(param.def_id, sym::rustc_host) {
|
||||
if let GenericParamKind::Const { is_host_effect, .. } = param.kind {
|
||||
// `host` params are explicitly allowed to be lowercase.
|
||||
if is_host_effect {
|
||||
return;
|
||||
}
|
||||
NonUpperCaseGlobals::check_upper_case(cx, "const parameter", ¶m.name.ident());
|
||||
|
|
|
|||
|
|
@ -291,7 +291,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
|
|||
.map(|inner| MustUsePath::Pinned(Box::new(inner)))
|
||||
}
|
||||
ty::Adt(def, _) => is_def_must_use(cx, def.did(), span),
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => {
|
||||
ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => {
|
||||
elaborate(
|
||||
cx.tcx,
|
||||
cx.tcx.explicit_item_bounds(def).instantiate_identity_iter_copied(),
|
||||
|
|
|
|||
|
|
@ -8,6 +8,135 @@ use crate::{declare_lint, declare_lint_pass, FutureIncompatibilityReason};
|
|||
use rustc_span::edition::Edition;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
declare_lint_pass! {
|
||||
/// Does nothing as a lint pass, but registers some `Lint`s
|
||||
/// that are used by other parts of the compiler.
|
||||
HardwiredLints => [
|
||||
// tidy-alphabetical-start
|
||||
ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
|
||||
AMBIGUOUS_ASSOCIATED_ITEMS,
|
||||
AMBIGUOUS_GLOB_IMPORTS,
|
||||
AMBIGUOUS_GLOB_REEXPORTS,
|
||||
ARITHMETIC_OVERFLOW,
|
||||
ASM_SUB_REGISTER,
|
||||
BAD_ASM_STYLE,
|
||||
BARE_TRAIT_OBJECTS,
|
||||
BINDINGS_WITH_VARIANT_NAME,
|
||||
BREAK_WITH_LABEL_AND_LOOP,
|
||||
BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE,
|
||||
CENUM_IMPL_DROP_CAST,
|
||||
COHERENCE_LEAK_CHECK,
|
||||
COINDUCTIVE_OVERLAP_IN_COHERENCE,
|
||||
CONFLICTING_REPR_HINTS,
|
||||
CONST_EVALUATABLE_UNCHECKED,
|
||||
CONST_ITEM_MUTATION,
|
||||
CONST_PATTERNS_WITHOUT_PARTIAL_EQ,
|
||||
DEAD_CODE,
|
||||
DEPRECATED,
|
||||
DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
|
||||
DEPRECATED_IN_FUTURE,
|
||||
DEPRECATED_WHERE_CLAUSE_LOCATION,
|
||||
DUPLICATE_MACRO_ATTRIBUTES,
|
||||
ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
|
||||
ELIDED_LIFETIMES_IN_PATHS,
|
||||
EXPORTED_PRIVATE_DEPENDENCIES,
|
||||
FFI_UNWIND_CALLS,
|
||||
FORBIDDEN_LINT_GROUPS,
|
||||
FUNCTION_ITEM_REFERENCES,
|
||||
FUZZY_PROVENANCE_CASTS,
|
||||
HIDDEN_GLOB_REEXPORTS,
|
||||
ILL_FORMED_ATTRIBUTE_INPUT,
|
||||
ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
|
||||
IMPLIED_BOUNDS_ENTAILMENT,
|
||||
INCOMPLETE_INCLUDE,
|
||||
INDIRECT_STRUCTURAL_MATCH,
|
||||
INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
|
||||
INLINE_NO_SANITIZE,
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
INVALID_MACRO_EXPORT_ARGUMENTS,
|
||||
INVALID_TYPE_PARAM_DEFAULT,
|
||||
IRREFUTABLE_LET_PATTERNS,
|
||||
LARGE_ASSIGNMENTS,
|
||||
LATE_BOUND_LIFETIME_ARGUMENTS,
|
||||
LEGACY_DERIVE_HELPERS,
|
||||
LONG_RUNNING_CONST_EVAL,
|
||||
LOSSY_PROVENANCE_CASTS,
|
||||
MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
|
||||
MACRO_USE_EXTERN_CRATE,
|
||||
META_VARIABLE_MISUSE,
|
||||
MISSING_ABI,
|
||||
MISSING_FRAGMENT_SPECIFIER,
|
||||
MUST_NOT_SUSPEND,
|
||||
NAMED_ARGUMENTS_USED_POSITIONALLY,
|
||||
NON_EXHAUSTIVE_OMITTED_PATTERNS,
|
||||
NONTRIVIAL_STRUCTURAL_MATCH,
|
||||
ORDER_DEPENDENT_TRAIT_OBJECTS,
|
||||
OVERLAPPING_RANGE_ENDPOINTS,
|
||||
PATTERNS_IN_FNS_WITHOUT_BODY,
|
||||
POINTER_STRUCTURAL_MATCH,
|
||||
PRIVATE_BOUNDS,
|
||||
PRIVATE_INTERFACES,
|
||||
PROC_MACRO_BACK_COMPAT,
|
||||
PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
|
||||
PUB_USE_OF_PRIVATE_EXTERN_CRATE,
|
||||
REFINING_IMPL_TRAIT,
|
||||
RENAMED_AND_REMOVED_LINTS,
|
||||
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
|
||||
RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES,
|
||||
RUST_2021_INCOMPATIBLE_OR_PATTERNS,
|
||||
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
|
||||
RUST_2021_PRELUDE_COLLISIONS,
|
||||
SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
|
||||
SINGLE_USE_LIFETIMES,
|
||||
SOFT_UNSTABLE,
|
||||
STABLE_FEATURES,
|
||||
SUSPICIOUS_AUTO_TRAIT_IMPLS,
|
||||
TEST_UNSTABLE_LINT,
|
||||
TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
|
||||
TRIVIAL_CASTS,
|
||||
TRIVIAL_NUMERIC_CASTS,
|
||||
TYVAR_BEHIND_RAW_POINTER,
|
||||
UNCONDITIONAL_PANIC,
|
||||
UNCONDITIONAL_RECURSION,
|
||||
UNDEFINED_NAKED_FUNCTION_ABI,
|
||||
UNEXPECTED_CFGS,
|
||||
UNFULFILLED_LINT_EXPECTATIONS,
|
||||
UNINHABITED_STATIC,
|
||||
UNKNOWN_CRATE_TYPES,
|
||||
UNKNOWN_LINTS,
|
||||
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||
UNNAMEABLE_TEST_ITEMS,
|
||||
UNNAMEABLE_TYPES,
|
||||
UNREACHABLE_CODE,
|
||||
UNREACHABLE_PATTERNS,
|
||||
UNSAFE_OP_IN_UNSAFE_FN,
|
||||
UNSTABLE_NAME_COLLISIONS,
|
||||
UNSTABLE_SYNTAX_PRE_EXPANSION,
|
||||
UNSUPPORTED_CALLING_CONVENTIONS,
|
||||
UNUSED_ASSIGNMENTS,
|
||||
UNUSED_ASSOCIATED_TYPE_BOUNDS,
|
||||
UNUSED_ATTRIBUTES,
|
||||
UNUSED_CRATE_DEPENDENCIES,
|
||||
UNUSED_EXTERN_CRATES,
|
||||
UNUSED_FEATURES,
|
||||
UNUSED_IMPORTS,
|
||||
UNUSED_LABELS,
|
||||
UNUSED_LIFETIMES,
|
||||
UNUSED_MACRO_RULES,
|
||||
UNUSED_MACROS,
|
||||
UNUSED_MUT,
|
||||
UNUSED_QUALIFICATIONS,
|
||||
UNUSED_TUPLE_STRUCT_FIELDS,
|
||||
UNUSED_UNSAFE,
|
||||
UNUSED_VARIABLES,
|
||||
USELESS_DEPRECATED,
|
||||
WARNINGS,
|
||||
WHERE_CLAUSES_OBJECT_SAFETY,
|
||||
WRITES_THROUGH_IMMUTABLE_POINTER,
|
||||
// tidy-alphabetical-end
|
||||
]
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `forbidden_lint_groups` lint detects violations of
|
||||
/// `forbid` applied to a lint group. Due to a bug in the compiler,
|
||||
|
|
@ -3135,7 +3264,7 @@ declare_lint! {
|
|||
/// ### Example
|
||||
///
|
||||
/// ```text
|
||||
/// rustc --check-cfg 'names()'
|
||||
/// rustc --check-cfg 'cfg()'
|
||||
/// ```
|
||||
///
|
||||
/// ```rust,ignore (needs command line option)
|
||||
|
|
@ -3146,7 +3275,7 @@ declare_lint! {
|
|||
/// This will produce:
|
||||
///
|
||||
/// ```text
|
||||
/// warning: unknown condition name used
|
||||
/// warning: unexpected `cfg` condition name: `widnows`
|
||||
/// --> lint_example.rs:1:7
|
||||
/// |
|
||||
/// 1 | #[cfg(widnows)]
|
||||
|
|
@ -3157,9 +3286,10 @@ declare_lint! {
|
|||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// This lint is only active when a `--check-cfg='names(...)'` option has been passed
|
||||
/// to the compiler and triggers whenever an unknown condition name or value is used.
|
||||
/// The known condition include names or values passed in `--check-cfg`, `--cfg`, and some
|
||||
/// This lint is only active when `--check-cfg` arguments are being passed
|
||||
/// to the compiler and triggers whenever an unexpected condition name or value is used.
|
||||
///
|
||||
/// The known condition include names or values passed in `--check-cfg`, and some
|
||||
/// well-knows names and values built into the compiler.
|
||||
pub UNEXPECTED_CFGS,
|
||||
Warn,
|
||||
|
|
@ -3348,134 +3478,6 @@ declare_lint! {
|
|||
"name introduced by a private item shadows a name introduced by a public glob re-export",
|
||||
}
|
||||
|
||||
declare_lint_pass! {
|
||||
/// Does nothing as a lint pass, but registers some `Lint`s
|
||||
/// that are used by other parts of the compiler.
|
||||
HardwiredLints => [
|
||||
// tidy-alphabetical-start
|
||||
ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
|
||||
AMBIGUOUS_ASSOCIATED_ITEMS,
|
||||
AMBIGUOUS_GLOB_IMPORTS,
|
||||
AMBIGUOUS_GLOB_REEXPORTS,
|
||||
ARITHMETIC_OVERFLOW,
|
||||
ASM_SUB_REGISTER,
|
||||
BAD_ASM_STYLE,
|
||||
BARE_TRAIT_OBJECTS,
|
||||
BINDINGS_WITH_VARIANT_NAME,
|
||||
BREAK_WITH_LABEL_AND_LOOP,
|
||||
BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE,
|
||||
CENUM_IMPL_DROP_CAST,
|
||||
COHERENCE_LEAK_CHECK,
|
||||
COINDUCTIVE_OVERLAP_IN_COHERENCE,
|
||||
CONFLICTING_REPR_HINTS,
|
||||
CONST_EVALUATABLE_UNCHECKED,
|
||||
CONST_ITEM_MUTATION,
|
||||
CONST_PATTERNS_WITHOUT_PARTIAL_EQ,
|
||||
DEAD_CODE,
|
||||
DEPRECATED,
|
||||
DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
|
||||
DEPRECATED_IN_FUTURE,
|
||||
DEPRECATED_WHERE_CLAUSE_LOCATION,
|
||||
DUPLICATE_MACRO_ATTRIBUTES,
|
||||
ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
|
||||
ELIDED_LIFETIMES_IN_PATHS,
|
||||
EXPORTED_PRIVATE_DEPENDENCIES,
|
||||
FFI_UNWIND_CALLS,
|
||||
FORBIDDEN_LINT_GROUPS,
|
||||
FUNCTION_ITEM_REFERENCES,
|
||||
FUZZY_PROVENANCE_CASTS,
|
||||
HIDDEN_GLOB_REEXPORTS,
|
||||
ILL_FORMED_ATTRIBUTE_INPUT,
|
||||
ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
|
||||
IMPLIED_BOUNDS_ENTAILMENT,
|
||||
INCOMPLETE_INCLUDE,
|
||||
INDIRECT_STRUCTURAL_MATCH,
|
||||
INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
|
||||
INLINE_NO_SANITIZE,
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
INVALID_MACRO_EXPORT_ARGUMENTS,
|
||||
INVALID_TYPE_PARAM_DEFAULT,
|
||||
IRREFUTABLE_LET_PATTERNS,
|
||||
LARGE_ASSIGNMENTS,
|
||||
LATE_BOUND_LIFETIME_ARGUMENTS,
|
||||
LEGACY_DERIVE_HELPERS,
|
||||
LONG_RUNNING_CONST_EVAL,
|
||||
LOSSY_PROVENANCE_CASTS,
|
||||
MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
|
||||
MACRO_USE_EXTERN_CRATE,
|
||||
META_VARIABLE_MISUSE,
|
||||
MISSING_ABI,
|
||||
MISSING_FRAGMENT_SPECIFIER,
|
||||
MUST_NOT_SUSPEND,
|
||||
NAMED_ARGUMENTS_USED_POSITIONALLY,
|
||||
NON_EXHAUSTIVE_OMITTED_PATTERNS,
|
||||
NONTRIVIAL_STRUCTURAL_MATCH,
|
||||
ORDER_DEPENDENT_TRAIT_OBJECTS,
|
||||
OVERLAPPING_RANGE_ENDPOINTS,
|
||||
PATTERNS_IN_FNS_WITHOUT_BODY,
|
||||
POINTER_STRUCTURAL_MATCH,
|
||||
PRIVATE_BOUNDS,
|
||||
PRIVATE_INTERFACES,
|
||||
PROC_MACRO_BACK_COMPAT,
|
||||
PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
|
||||
PUB_USE_OF_PRIVATE_EXTERN_CRATE,
|
||||
REFINING_IMPL_TRAIT,
|
||||
RENAMED_AND_REMOVED_LINTS,
|
||||
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
|
||||
RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES,
|
||||
RUST_2021_INCOMPATIBLE_OR_PATTERNS,
|
||||
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
|
||||
RUST_2021_PRELUDE_COLLISIONS,
|
||||
SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
|
||||
SINGLE_USE_LIFETIMES,
|
||||
SOFT_UNSTABLE,
|
||||
STABLE_FEATURES,
|
||||
SUSPICIOUS_AUTO_TRAIT_IMPLS,
|
||||
TEST_UNSTABLE_LINT,
|
||||
TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
|
||||
TRIVIAL_CASTS,
|
||||
TRIVIAL_NUMERIC_CASTS,
|
||||
TYVAR_BEHIND_RAW_POINTER,
|
||||
UNCONDITIONAL_PANIC,
|
||||
UNCONDITIONAL_RECURSION,
|
||||
UNDEFINED_NAKED_FUNCTION_ABI,
|
||||
UNEXPECTED_CFGS,
|
||||
UNFULFILLED_LINT_EXPECTATIONS,
|
||||
UNINHABITED_STATIC,
|
||||
UNKNOWN_CRATE_TYPES,
|
||||
UNKNOWN_LINTS,
|
||||
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||
UNNAMEABLE_TEST_ITEMS,
|
||||
UNNAMEABLE_TYPES,
|
||||
UNREACHABLE_CODE,
|
||||
UNREACHABLE_PATTERNS,
|
||||
UNSAFE_OP_IN_UNSAFE_FN,
|
||||
UNSTABLE_NAME_COLLISIONS,
|
||||
UNSTABLE_SYNTAX_PRE_EXPANSION,
|
||||
UNSUPPORTED_CALLING_CONVENTIONS,
|
||||
UNUSED_ASSIGNMENTS,
|
||||
UNUSED_ASSOCIATED_TYPE_BOUNDS,
|
||||
UNUSED_ATTRIBUTES,
|
||||
UNUSED_CRATE_DEPENDENCIES,
|
||||
UNUSED_EXTERN_CRATES,
|
||||
UNUSED_FEATURES,
|
||||
UNUSED_IMPORTS,
|
||||
UNUSED_LABELS,
|
||||
UNUSED_LIFETIMES,
|
||||
UNUSED_MACRO_RULES,
|
||||
UNUSED_MACROS,
|
||||
UNUSED_MUT,
|
||||
UNUSED_QUALIFICATIONS,
|
||||
UNUSED_TUPLE_STRUCT_FIELDS,
|
||||
UNUSED_UNSAFE,
|
||||
UNUSED_VARIABLES,
|
||||
USELESS_DEPRECATED,
|
||||
WARNINGS,
|
||||
WHERE_CLAUSES_OBJECT_SAFETY,
|
||||
// tidy-alphabetical-end
|
||||
]
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `long_running_const_eval` lint is emitted when const
|
||||
/// eval is running for a long time to ensure rustc terminates
|
||||
|
|
@ -4620,3 +4622,37 @@ declare_lint! {
|
|||
reference: "issue #115010 <https://github.com/rust-lang/rust/issues/115010>",
|
||||
};
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `writes_through_immutable_pointer` lint detects writes through pointers derived from
|
||||
/// shared references.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// #![feature(const_mut_refs)]
|
||||
/// const WRITE_AFTER_CAST: () = unsafe {
|
||||
/// let mut x = 0;
|
||||
/// let ptr = &x as *const i32 as *mut i32;
|
||||
/// *ptr = 0;
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// Shared references are immutable (when there is no `UnsafeCell` involved),
|
||||
/// and writing through them or through pointers derived from them is Undefined Behavior.
|
||||
/// The compiler recently learned to detect such Undefined Behavior during compile-time
|
||||
/// evaluation, and in the future this will raise a hard error.
|
||||
///
|
||||
/// [future-incompatible]: ../index.md#future-incompatible-lints
|
||||
pub WRITES_THROUGH_IMMUTABLE_POINTER,
|
||||
Warn,
|
||||
"shared references are immutable, and pointers derived from them must not be written to",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
|
||||
reference: "issue #X <https://github.com/rust-lang/rust/issues/X>",
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ fn output(cmd: &mut Command) -> String {
|
|||
|
||||
fn main() {
|
||||
for component in REQUIRED_COMPONENTS.iter().chain(OPTIONAL_COMPONENTS.iter()) {
|
||||
println!("cargo:rustc-check-cfg=values(llvm_component,\"{component}\")");
|
||||
println!("cargo:rustc-check-cfg=cfg(llvm_component,values(\"{component}\"))");
|
||||
}
|
||||
|
||||
if tracked_env_var_os("RUST_CHECK").is_some() {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
#include "SuppressLLVMWarnings.h"
|
||||
|
||||
#include "llvm-c/BitReader.h"
|
||||
#include "llvm-c/Core.h"
|
||||
#include "llvm-c/Object.h"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include "SuppressLLVMWarnings.h"
|
||||
#include "llvm/Linker/Linker.h"
|
||||
|
||||
#include "LLVMWrapper.h"
|
||||
|
|
|
|||
13
compiler/rustc_llvm/llvm-wrapper/SuppressLLVMWarnings.h
Normal file
13
compiler/rustc_llvm/llvm-wrapper/SuppressLLVMWarnings.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef _rustc_llvm_SuppressLLVMWarnings_h
|
||||
#define _rustc_llvm_SuppressLLVMWarnings_h
|
||||
|
||||
// LLVM currently generates many warnings when compiled using MSVC. These warnings make it difficult
|
||||
// to diagnose real problems when working on C++ code, so we suppress them.
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4530) // C++ exception handler used, but unwind semantics are not enabled.
|
||||
#pragma warning(disable:4624) // 'xxx': destructor was implicitly defined as deleted
|
||||
#pragma warning(disable:4244) // conversion from 'xxx' to 'yyy', possible loss of data
|
||||
#endif
|
||||
|
||||
#endif // _rustc_llvm_SuppressLLVMWarnings_h
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
// * https://github.com/llvm/llvm-project/blob/8ef3e895ad8ab1724e2b87cabad1dacdc7a397a3/llvm/include/llvm/Object/ArchiveWriter.h
|
||||
// * https://github.com/llvm/llvm-project/blob/8ef3e895ad8ab1724e2b87cabad1dacdc7a397a3/llvm/lib/Object/ArchiveWriter.cpp
|
||||
|
||||
#include "SuppressLLVMWarnings.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/Object/ObjectFile.h"
|
||||
|
|
|
|||
|
|
@ -658,8 +658,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
let debugger_visualizers =
|
||||
stat!("debugger-visualizers", || self.encode_debugger_visualizers());
|
||||
|
||||
// Encode exported symbols info. This is prefetched in `encode_metadata` so we encode
|
||||
// this as late as possible to give the prefetching as much time as possible to complete.
|
||||
// Encode exported symbols info. This is prefetched in `encode_metadata`.
|
||||
let exported_symbols = stat!("exported-symbols", || {
|
||||
self.encode_exported_symbols(tcx.exported_symbols(LOCAL_CRATE))
|
||||
});
|
||||
|
|
@ -2193,21 +2192,13 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) {
|
|||
// there's no need to do dep-graph tracking for any of it.
|
||||
tcx.dep_graph.assert_ignored();
|
||||
|
||||
join(
|
||||
|| encode_metadata_impl(tcx, path),
|
||||
|| {
|
||||
if tcx.sess.threads() == 1 {
|
||||
return;
|
||||
}
|
||||
// Prefetch some queries used by metadata encoding.
|
||||
// This is not necessary for correctness, but is only done for performance reasons.
|
||||
// It can be removed if it turns out to cause trouble or be detrimental to performance.
|
||||
join(|| prefetch_mir(tcx), || tcx.exported_symbols(LOCAL_CRATE));
|
||||
},
|
||||
);
|
||||
}
|
||||
if tcx.sess.threads() != 1 {
|
||||
// Prefetch some queries used by metadata encoding.
|
||||
// This is not necessary for correctness, but is only done for performance reasons.
|
||||
// It can be removed if it turns out to cause trouble or be detrimental to performance.
|
||||
join(|| prefetch_mir(tcx), || tcx.exported_symbols(LOCAL_CRATE));
|
||||
}
|
||||
|
||||
fn encode_metadata_impl(tcx: TyCtxt<'_>, path: &Path) {
|
||||
let mut encoder = opaque::FileEncoder::new(path)
|
||||
.unwrap_or_else(|err| tcx.sess.emit_fatal(FailCreateFileEncoder { err }));
|
||||
encoder.emit_raw_bytes(METADATA_HEADER);
|
||||
|
|
|
|||
|
|
@ -35,7 +35,12 @@ pub enum SymbolExportKind {
|
|||
pub struct SymbolExportInfo {
|
||||
pub level: SymbolExportLevel,
|
||||
pub kind: SymbolExportKind,
|
||||
/// Used to mark these symbols not to be internalized by LTO. These symbols
|
||||
/// are also added to `symbols.o` to avoid circular dependencies when linking.
|
||||
pub used: bool,
|
||||
/// Also used to mark these symbols not to be internalized by LTO. But will
|
||||
/// not be added to `symbols.o`. Currently there are only builtin functions.
|
||||
pub used_compiler: bool,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ static_assert_size!(ConstValue<'_>, 24);
|
|||
|
||||
impl<'tcx> ConstValue<'tcx> {
|
||||
#[inline]
|
||||
pub fn try_to_scalar(&self) -> Option<Scalar<AllocId>> {
|
||||
pub fn try_to_scalar(&self) -> Option<Scalar> {
|
||||
match *self {
|
||||
ConstValue::Indirect { .. } | ConstValue::Slice { .. } | ConstValue::ZeroSized => None,
|
||||
ConstValue::Scalar(val) => Some(val),
|
||||
|
|
@ -161,8 +161,8 @@ impl<'tcx> ConstValue<'tcx> {
|
|||
return Some(&[]);
|
||||
}
|
||||
// Non-empty slice, must have memory. We know this is a relative pointer.
|
||||
let (inner_alloc_id, offset) = ptr.into_parts();
|
||||
let data = tcx.global_alloc(inner_alloc_id?).unwrap_memory();
|
||||
let (inner_prov, offset) = ptr.into_parts();
|
||||
let data = tcx.global_alloc(inner_prov?.alloc_id()).unwrap_memory();
|
||||
(data, offset.bytes(), offset.bytes() + len)
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ use rustc_span::DUMMY_SP;
|
|||
use rustc_target::abi::{Align, HasDataLayout, Size};
|
||||
|
||||
use super::{
|
||||
read_target_uint, write_target_uint, AllocId, BadBytesAccess, InterpError, InterpResult,
|
||||
Pointer, PointerArithmetic, Provenance, ResourceExhaustionInfo, Scalar, ScalarSizeMismatch,
|
||||
UndefinedBehaviorInfo, UnsupportedOpInfo,
|
||||
read_target_uint, write_target_uint, AllocId, BadBytesAccess, CtfeProvenance, InterpError,
|
||||
InterpResult, Pointer, PointerArithmetic, Provenance, ResourceExhaustionInfo, Scalar,
|
||||
ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo,
|
||||
};
|
||||
use crate::ty;
|
||||
use init_mask::*;
|
||||
|
|
@ -63,7 +63,7 @@ impl AllocBytes for Box<[u8]> {
|
|||
// hashed. (see the `Hash` impl below for more details), so the impl is not derived.
|
||||
#[derive(Clone, Eq, PartialEq, TyEncodable, TyDecodable)]
|
||||
#[derive(HashStable)]
|
||||
pub struct Allocation<Prov: Provenance = AllocId, Extra = (), Bytes = Box<[u8]>> {
|
||||
pub struct Allocation<Prov: Provenance = CtfeProvenance, Extra = (), Bytes = Box<[u8]>> {
|
||||
/// The actual bytes of the allocation.
|
||||
/// Note that the bytes of a pointer represent the offset of the pointer.
|
||||
bytes: Bytes,
|
||||
|
|
@ -336,14 +336,14 @@ impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Bytes: AllocBytes> Allocation<AllocId, (), Bytes> {
|
||||
/// Adjust allocation from the ones in tcx to a custom Machine instance
|
||||
/// with a different Provenance and Extra type.
|
||||
impl<Bytes: AllocBytes> Allocation<CtfeProvenance, (), Bytes> {
|
||||
/// Adjust allocation from the ones in `tcx` to a custom Machine instance
|
||||
/// with a different `Provenance` and `Extra` type.
|
||||
pub fn adjust_from_tcx<Prov: Provenance, Extra, Err>(
|
||||
self,
|
||||
cx: &impl HasDataLayout,
|
||||
extra: Extra,
|
||||
mut adjust_ptr: impl FnMut(Pointer<AllocId>) -> Result<Pointer<Prov>, Err>,
|
||||
mut adjust_ptr: impl FnMut(Pointer<CtfeProvenance>) -> Result<Pointer<Prov>, Err>,
|
||||
) -> Result<Allocation<Prov, Extra, Bytes>, Err> {
|
||||
let mut bytes = self.bytes;
|
||||
// Adjust provenance of pointers stored in this allocation.
|
||||
|
|
|
|||
|
|
@ -6,14 +6,14 @@ use std::cmp;
|
|||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_target::abi::{HasDataLayout, Size};
|
||||
|
||||
use super::{alloc_range, AllocError, AllocId, AllocRange, AllocResult, Provenance};
|
||||
use super::{alloc_range, AllocError, AllocRange, AllocResult, CtfeProvenance, Provenance};
|
||||
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
|
||||
|
||||
/// Stores the provenance information of pointers stored in memory.
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[derive(HashStable)]
|
||||
pub struct ProvenanceMap<Prov = AllocId> {
|
||||
/// Provenance in this map applies from the given offset for an entire pointer-size worth of
|
||||
pub struct ProvenanceMap<Prov = CtfeProvenance> {
|
||||
/// `Provenance` in this map applies from the given offset for an entire pointer-size worth of
|
||||
/// bytes. Two entries in this map are always at least a pointer size apart.
|
||||
ptrs: SortedMap<Size, Prov>,
|
||||
/// Provenance in this map only applies to the given single byte.
|
||||
|
|
@ -22,18 +22,19 @@ pub struct ProvenanceMap<Prov = AllocId> {
|
|||
bytes: Option<Box<SortedMap<Size, Prov>>>,
|
||||
}
|
||||
|
||||
// These impls are generic over `Prov` since `CtfeProvenance` is only decodable/encodable
|
||||
// for some particular `D`/`S`.
|
||||
impl<D: Decoder, Prov: Provenance + Decodable<D>> Decodable<D> for ProvenanceMap<Prov> {
|
||||
fn decode(d: &mut D) -> Self {
|
||||
assert!(!Prov::OFFSET_IS_ADDR); // only `AllocId` is ever serialized
|
||||
assert!(!Prov::OFFSET_IS_ADDR); // only `CtfeProvenance` is ever serialized
|
||||
Self { ptrs: Decodable::decode(d), bytes: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Encoder, Prov: Provenance + Encodable<S>> Encodable<S> for ProvenanceMap<Prov> {
|
||||
fn encode(&self, s: &mut S) {
|
||||
let Self { ptrs, bytes } = self;
|
||||
assert!(!Prov::OFFSET_IS_ADDR); // only `AllocId` is ever serialized
|
||||
debug_assert!(bytes.is_none());
|
||||
assert!(!Prov::OFFSET_IS_ADDR); // only `CtfeProvenance` is ever serialized
|
||||
debug_assert!(bytes.is_none()); // without `OFFSET_IS_ADDR`, this is always empty
|
||||
ptrs.encode(s)
|
||||
}
|
||||
}
|
||||
|
|
@ -54,10 +55,10 @@ impl ProvenanceMap {
|
|||
/// Give access to the ptr-sized provenances (which can also be thought of as relocations, and
|
||||
/// indeed that is how codegen treats them).
|
||||
///
|
||||
/// Only exposed with `AllocId` provenance, since it panics if there is bytewise provenance.
|
||||
/// Only exposed with `CtfeProvenance` provenance, since it panics if there is bytewise provenance.
|
||||
#[inline]
|
||||
pub fn ptrs(&self) -> &SortedMap<Size, AllocId> {
|
||||
debug_assert!(self.bytes.is_none()); // `AllocId::OFFSET_IS_ADDR` is false so this cannot fail
|
||||
pub fn ptrs(&self) -> &SortedMap<Size, CtfeProvenance> {
|
||||
debug_assert!(self.bytes.is_none()); // `CtfeProvenance::OFFSET_IS_ADDR` is false so this cannot fail
|
||||
&self.ptrs
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -290,7 +290,7 @@ macro_rules! impl_into_diagnostic_arg_through_debug {
|
|||
// These types have nice `Debug` output so we can just use them in diagnostics.
|
||||
impl_into_diagnostic_arg_through_debug! {
|
||||
AllocId,
|
||||
Pointer,
|
||||
Pointer<AllocId>,
|
||||
AllocRange,
|
||||
}
|
||||
|
||||
|
|
@ -323,7 +323,7 @@ pub enum UndefinedBehaviorInfo<'tcx> {
|
|||
/// Invalid metadata in a wide pointer
|
||||
InvalidMeta(InvalidMetaKind),
|
||||
/// Reading a C string that does not end within its allocation.
|
||||
UnterminatedCString(Pointer),
|
||||
UnterminatedCString(Pointer<AllocId>),
|
||||
/// Using a pointer after it got freed.
|
||||
PointerUseAfterFree(AllocId, CheckInAllocMsg),
|
||||
/// Used a pointer outside the bounds it is valid for.
|
||||
|
|
@ -350,11 +350,11 @@ pub enum UndefinedBehaviorInfo<'tcx> {
|
|||
/// Using a non-character `u32` as character.
|
||||
InvalidChar(u32),
|
||||
/// The tag of an enum does not encode an actual discriminant.
|
||||
InvalidTag(Scalar),
|
||||
InvalidTag(Scalar<AllocId>),
|
||||
/// Using a pointer-not-to-a-function as function pointer.
|
||||
InvalidFunctionPointer(Pointer),
|
||||
InvalidFunctionPointer(Pointer<AllocId>),
|
||||
/// Using a pointer-not-to-a-vtable as vtable pointer.
|
||||
InvalidVTablePointer(Pointer),
|
||||
InvalidVTablePointer(Pointer<AllocId>),
|
||||
/// Using a string that is not valid UTF-8,
|
||||
InvalidStr(std::str::Utf8Error),
|
||||
/// Using uninitialized data where it is not allowed.
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ pub use self::allocation::{
|
|||
InitChunk, InitChunkIter,
|
||||
};
|
||||
|
||||
pub use self::pointer::{Pointer, PointerArithmetic, Provenance};
|
||||
pub use self::pointer::{CtfeProvenance, Pointer, PointerArithmetic, Provenance};
|
||||
|
||||
/// Uniquely identifies one of the following:
|
||||
/// - A constant
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use super::{AllocId, InterpResult};
|
|||
use rustc_macros::HashStable;
|
||||
use rustc_target::abi::{HasDataLayout, Size};
|
||||
|
||||
use std::fmt;
|
||||
use std::{fmt, num::NonZeroU64};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Pointer arithmetic
|
||||
|
|
@ -114,22 +114,7 @@ pub trait Provenance: Copy + fmt::Debug + 'static {
|
|||
const OFFSET_IS_ADDR: bool;
|
||||
|
||||
/// Determines how a pointer should be printed.
|
||||
///
|
||||
/// Default impl is only good for when `OFFSET_IS_ADDR == true`.
|
||||
fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
assert!(Self::OFFSET_IS_ADDR);
|
||||
let (prov, addr) = ptr.into_parts(); // address is absolute
|
||||
write!(f, "{:#x}", addr.bytes())?;
|
||||
if f.alternate() {
|
||||
write!(f, "{prov:#?}")?;
|
||||
} else {
|
||||
write!(f, "{prov:?}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result;
|
||||
|
||||
/// If `OFFSET_IS_ADDR == false`, provenance must always be able to
|
||||
/// identify the allocation this ptr points to (i.e., this must return `Some`).
|
||||
|
|
@ -141,6 +126,80 @@ pub trait Provenance: Copy + fmt::Debug + 'static {
|
|||
fn join(left: Option<Self>, right: Option<Self>) -> Option<Self>;
|
||||
}
|
||||
|
||||
/// The type of provenance in the compile-time interpreter.
|
||||
/// This is a packed representation of an `AllocId` and an `immutable: bool`.
|
||||
#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct CtfeProvenance(NonZeroU64);
|
||||
|
||||
impl From<AllocId> for CtfeProvenance {
|
||||
fn from(value: AllocId) -> Self {
|
||||
let prov = CtfeProvenance(value.0);
|
||||
assert!(!prov.immutable(), "`AllocId` with the highest bit set cannot be used in CTFE");
|
||||
prov
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for CtfeProvenance {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.alloc_id(), f)?; // propagates `alternate` flag
|
||||
if self.immutable() {
|
||||
write!(f, "<imm>")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
const IMMUTABLE_MASK: u64 = 1 << 63; // the highest bit
|
||||
|
||||
impl CtfeProvenance {
|
||||
/// Returns the `AllocId` of this provenance.
|
||||
#[inline(always)]
|
||||
pub fn alloc_id(self) -> AllocId {
|
||||
AllocId(NonZeroU64::new(self.0.get() & !IMMUTABLE_MASK).unwrap())
|
||||
}
|
||||
|
||||
/// Returns whether this provenance is immutable.
|
||||
#[inline]
|
||||
pub fn immutable(self) -> bool {
|
||||
self.0.get() & IMMUTABLE_MASK != 0
|
||||
}
|
||||
|
||||
/// Returns an immutable version of this provenance.
|
||||
#[inline]
|
||||
pub fn as_immutable(self) -> Self {
|
||||
CtfeProvenance(self.0 | IMMUTABLE_MASK)
|
||||
}
|
||||
}
|
||||
|
||||
impl Provenance for CtfeProvenance {
|
||||
// With the `AllocId` as provenance, the `offset` is interpreted *relative to the allocation*,
|
||||
// so ptr-to-int casts are not possible (since we do not know the global physical offset).
|
||||
const OFFSET_IS_ADDR: bool = false;
|
||||
|
||||
fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Print AllocId.
|
||||
fmt::Debug::fmt(&ptr.provenance.alloc_id(), f)?; // propagates `alternate` flag
|
||||
// Print offset only if it is non-zero.
|
||||
if ptr.offset.bytes() > 0 {
|
||||
write!(f, "+{:#x}", ptr.offset.bytes())?;
|
||||
}
|
||||
// Print immutable status.
|
||||
if ptr.provenance.immutable() {
|
||||
write!(f, "<imm>")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_alloc_id(self) -> Option<AllocId> {
|
||||
Some(self.alloc_id())
|
||||
}
|
||||
|
||||
fn join(_left: Option<Self>, _right: Option<Self>) -> Option<Self> {
|
||||
panic!("merging provenance is not supported when `OFFSET_IS_ADDR` is false")
|
||||
}
|
||||
}
|
||||
|
||||
// We also need this impl so that one can debug-print `Pointer<AllocId>`
|
||||
impl Provenance for AllocId {
|
||||
// With the `AllocId` as provenance, the `offset` is interpreted *relative to the allocation*,
|
||||
// so ptr-to-int casts are not possible (since we do not know the global physical offset).
|
||||
|
|
@ -174,7 +233,7 @@ impl Provenance for AllocId {
|
|||
/// Pointers are "tagged" with provenance information; typically the `AllocId` they belong to.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
|
||||
#[derive(HashStable)]
|
||||
pub struct Pointer<Prov = AllocId> {
|
||||
pub struct Pointer<Prov = CtfeProvenance> {
|
||||
pub(super) offset: Size, // kept private to avoid accidental misinterpretation (meaning depends on `Prov` type)
|
||||
pub provenance: Prov,
|
||||
}
|
||||
|
|
@ -182,7 +241,7 @@ pub struct Pointer<Prov = AllocId> {
|
|||
static_assert_size!(Pointer, 16);
|
||||
// `Option<Prov>` pointers are also passed around quite a bit
|
||||
// (but not stored in permanent machine state).
|
||||
static_assert_size!(Pointer<Option<AllocId>>, 16);
|
||||
static_assert_size!(Pointer<Option<CtfeProvenance>>, 16);
|
||||
|
||||
// We want the `Debug` output to be readable as it is used by `derive(Debug)` for
|
||||
// all the Miri types.
|
||||
|
|
@ -215,7 +274,7 @@ impl<Prov: Provenance> fmt::Display for Pointer<Option<Prov>> {
|
|||
impl From<AllocId> for Pointer {
|
||||
#[inline(always)]
|
||||
fn from(alloc_id: AllocId) -> Self {
|
||||
Pointer::new(alloc_id, Size::ZERO)
|
||||
Pointer::new(alloc_id.into(), Size::ZERO)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,10 @@ use rustc_target::abi::{HasDataLayout, Size};
|
|||
|
||||
use crate::ty::ScalarInt;
|
||||
|
||||
use super::{AllocId, InterpResult, Pointer, PointerArithmetic, Provenance, ScalarSizeMismatch};
|
||||
use super::{
|
||||
AllocId, CtfeProvenance, InterpResult, Pointer, PointerArithmetic, Provenance,
|
||||
ScalarSizeMismatch,
|
||||
};
|
||||
|
||||
/// A `Scalar` represents an immediate, primitive value existing outside of a
|
||||
/// `memory::Allocation`. It is in many ways like a small chunk of an `Allocation`, up to 16 bytes in
|
||||
|
|
@ -22,7 +25,7 @@ use super::{AllocId, InterpResult, Pointer, PointerArithmetic, Provenance, Scala
|
|||
/// Do *not* match on a `Scalar`! Use the various `to_*` methods instead.
|
||||
#[derive(Clone, Copy, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
|
||||
#[derive(HashStable)]
|
||||
pub enum Scalar<Prov = AllocId> {
|
||||
pub enum Scalar<Prov = CtfeProvenance> {
|
||||
/// The raw bytes of a simple value.
|
||||
Int(ScalarInt),
|
||||
|
||||
|
|
@ -267,6 +270,9 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
|
|||
/// Will perform ptr-to-int casts if needed and possible.
|
||||
/// If that fails, we know the offset is relative, so we return an "erased" Scalar
|
||||
/// (which is useful for error messages but not much else).
|
||||
///
|
||||
/// The error type is `AllocId`, not `CtfeProvenance`, since `AllocId` is the "minimal"
|
||||
/// component all provenance types must have.
|
||||
#[inline]
|
||||
pub fn try_to_int(self) -> Result<ScalarInt, Scalar<AllocId>> {
|
||||
match self {
|
||||
|
|
|
|||
|
|
@ -1337,13 +1337,13 @@ pub fn write_allocations<'tcx>(
|
|||
fn alloc_ids_from_alloc(
|
||||
alloc: ConstAllocation<'_>,
|
||||
) -> impl DoubleEndedIterator<Item = AllocId> + '_ {
|
||||
alloc.inner().provenance().ptrs().values().copied()
|
||||
alloc.inner().provenance().ptrs().values().map(|p| p.alloc_id())
|
||||
}
|
||||
|
||||
fn alloc_ids_from_const_val(val: ConstValue<'_>) -> impl Iterator<Item = AllocId> + '_ {
|
||||
match val {
|
||||
ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _)) => {
|
||||
Either::Left(std::iter::once(ptr.provenance))
|
||||
Either::Left(std::iter::once(ptr.provenance.alloc_id()))
|
||||
}
|
||||
ConstValue::Scalar(interpret::Scalar::Int { .. }) => Either::Right(std::iter::empty()),
|
||||
ConstValue::ZeroSized => Either::Right(std::iter::empty()),
|
||||
|
|
|
|||
|
|
@ -381,7 +381,7 @@ impl<'tcx> Operand<'tcx> {
|
|||
impl<'tcx> ConstOperand<'tcx> {
|
||||
pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
|
||||
match self.const_.try_to_scalar() {
|
||||
Some(Scalar::Ptr(ptr, _size)) => match tcx.global_alloc(ptr.provenance) {
|
||||
Some(Scalar::Ptr(ptr, _size)) => match tcx.global_alloc(ptr.provenance.alloc_id()) {
|
||||
GlobalAlloc::Static(def_id) => {
|
||||
assert!(!tcx.is_thread_local_static(def_id));
|
||||
Some(def_id)
|
||||
|
|
|
|||
|
|
@ -264,6 +264,7 @@ trivial! {
|
|||
rustc_middle::middle::stability::DeprecationEntry,
|
||||
rustc_middle::mir::ConstQualifs,
|
||||
rustc_middle::mir::interpret::AllocId,
|
||||
rustc_middle::mir::interpret::CtfeProvenance,
|
||||
rustc_middle::mir::interpret::ErrorHandled,
|
||||
rustc_middle::mir::interpret::LitToConstError,
|
||||
rustc_middle::thir::ExprId,
|
||||
|
|
|
|||
|
|
@ -180,8 +180,8 @@ pub enum SelectionCandidate<'tcx> {
|
|||
///
|
||||
/// The evaluation results are ordered:
|
||||
/// - `EvaluatedToOk` implies `EvaluatedToOkModuloRegions`
|
||||
/// implies `EvaluatedToAmbig` implies `EvaluatedToUnknown`
|
||||
/// - `EvaluatedToErr` implies `EvaluatedToRecur`
|
||||
/// implies `EvaluatedToAmbig` implies `EvaluatedToAmbigStackDependent`
|
||||
/// - `EvaluatedToErr` implies `EvaluatedToErrStackDependent`
|
||||
/// - the "union" of evaluation results is equal to their maximum -
|
||||
/// all the "potential success" candidates can potentially succeed,
|
||||
/// so they are noops when unioned with a definite error, and within
|
||||
|
|
@ -199,7 +199,7 @@ pub enum EvaluationResult {
|
|||
/// Evaluation is known to be ambiguous -- it *might* hold for some
|
||||
/// assignment of inference variables, but it might not.
|
||||
///
|
||||
/// While this has the same meaning as `EvaluatedToUnknown` -- we can't
|
||||
/// While this has the same meaning as `EvaluatedToAmbigStackDependent` -- we can't
|
||||
/// know whether this obligation holds or not -- it is the result we
|
||||
/// would get with an empty stack, and therefore is cacheable.
|
||||
EvaluatedToAmbig,
|
||||
|
|
@ -207,8 +207,8 @@ pub enum EvaluationResult {
|
|||
/// variables. We are somewhat imprecise there, so we don't actually
|
||||
/// know the real result.
|
||||
///
|
||||
/// This can't be trivially cached for the same reason as `EvaluatedToRecur`.
|
||||
EvaluatedToUnknown,
|
||||
/// This can't be trivially cached for the same reason as `EvaluatedToErrStackDependent`.
|
||||
EvaluatedToAmbigStackDependent,
|
||||
/// Evaluation failed because we encountered an obligation we are already
|
||||
/// trying to prove on this branch.
|
||||
///
|
||||
|
|
@ -247,12 +247,12 @@ pub enum EvaluationResult {
|
|||
/// does not hold, because of the bound (which can indeed be satisfied
|
||||
/// by `SomeUnsizedType` from another crate).
|
||||
//
|
||||
// FIXME: when an `EvaluatedToRecur` goes past its parent root, we
|
||||
// FIXME: when an `EvaluatedToErrStackDependent` goes past its parent root, we
|
||||
// ought to convert it to an `EvaluatedToErr`, because we know
|
||||
// there definitely isn't a proof tree for that obligation. Not
|
||||
// doing so is still sound -- there isn't any proof tree, so the
|
||||
// branch still can't be a part of a minimal one -- but does not re-enable caching.
|
||||
EvaluatedToRecur,
|
||||
EvaluatedToErrStackDependent,
|
||||
/// Evaluation failed.
|
||||
EvaluatedToErr,
|
||||
}
|
||||
|
|
@ -276,15 +276,15 @@ impl EvaluationResult {
|
|||
| EvaluatedToOk
|
||||
| EvaluatedToOkModuloRegions
|
||||
| EvaluatedToAmbig
|
||||
| EvaluatedToUnknown => true,
|
||||
| EvaluatedToAmbigStackDependent => true,
|
||||
|
||||
EvaluatedToErr | EvaluatedToRecur => false,
|
||||
EvaluatedToErr | EvaluatedToErrStackDependent => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_stack_dependent(self) -> bool {
|
||||
match self {
|
||||
EvaluatedToUnknown | EvaluatedToRecur => true,
|
||||
EvaluatedToAmbigStackDependent | EvaluatedToErrStackDependent => true,
|
||||
|
||||
EvaluatedToOkModuloOpaqueTypes
|
||||
| EvaluatedToOk
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
use crate::arena::ArenaAllocatable;
|
||||
use crate::infer::canonical::{CanonicalVarInfo, CanonicalVarInfos};
|
||||
use crate::mir::interpret::CtfeProvenance;
|
||||
use crate::mir::{
|
||||
self,
|
||||
interpret::{AllocId, ConstAllocation},
|
||||
|
|
@ -164,6 +165,13 @@ impl<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>> Encodable<E> for AllocId {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>> Encodable<E> for CtfeProvenance {
|
||||
fn encode(&self, e: &mut E) {
|
||||
self.alloc_id().encode(e);
|
||||
self.immutable().encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>> Encodable<E> for ty::ParamEnv<'tcx> {
|
||||
fn encode(&self, e: &mut E) {
|
||||
self.caller_bounds().encode(e);
|
||||
|
|
@ -295,6 +303,15 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for AllocId {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for CtfeProvenance {
|
||||
fn decode(decoder: &mut D) -> Self {
|
||||
let alloc_id: AllocId = Decodable::decode(decoder);
|
||||
let prov = CtfeProvenance::from(alloc_id);
|
||||
let immutable: bool = Decodable::decode(decoder);
|
||||
if immutable { prov.as_immutable() } else { prov }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for ty::SymbolName<'tcx> {
|
||||
fn decode(decoder: &mut D) -> Self {
|
||||
ty::SymbolName::new(decoder.interner(), decoder.read_str())
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::middle::resolve_bound_vars as rbv;
|
||||
use crate::mir::interpret::{AllocId, ErrorHandled, LitToConstInput, Scalar};
|
||||
use crate::mir::interpret::{ErrorHandled, LitToConstInput, Scalar};
|
||||
use crate::ty::{self, GenericArgs, ParamEnv, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_data_structures::intern::Interned;
|
||||
use rustc_error_messages::MultiSpan;
|
||||
|
|
@ -413,7 +413,7 @@ impl<'tcx> Const<'tcx> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_to_scalar(self) -> Option<Scalar<AllocId>> {
|
||||
pub fn try_to_scalar(self) -> Option<Scalar> {
|
||||
self.try_to_valtree()?.try_to_scalar()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use super::ScalarInt;
|
||||
use crate::mir::interpret::{AllocId, Scalar};
|
||||
use crate::mir::interpret::Scalar;
|
||||
use crate::ty::{self, Ty, TyCtxt};
|
||||
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ impl<'tcx> ValTree<'tcx> {
|
|||
Self::Leaf(i)
|
||||
}
|
||||
|
||||
pub fn try_to_scalar(self) -> Option<Scalar<AllocId>> {
|
||||
pub fn try_to_scalar(self) -> Option<Scalar> {
|
||||
self.try_to_scalar_int().map(Scalar::Int)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2186,7 +2186,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
hir::Node::Item(hir::Item {
|
||||
kind: hir::ItemKind::Impl(hir::Impl { generics, .. }),
|
||||
..
|
||||
}) if generics.params.iter().any(|p| self.has_attr(p.def_id, sym::rustc_host))
|
||||
}) if generics.params.iter().any(|p| matches!(p.kind, hir::GenericParamKind::Const { is_host_effect: true, .. }))
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,6 +84,14 @@ impl<'a> HashStable<StableHashingContext<'a>> for mir::interpret::AllocId {
|
|||
}
|
||||
}
|
||||
|
||||
// CtfeProvenance is an AllocId and a bool.
|
||||
impl<'a> HashStable<StableHashingContext<'a>> for mir::interpret::CtfeProvenance {
|
||||
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
|
||||
self.alloc_id().hash_stable(hcx, hasher);
|
||||
self.immutable().hash_stable(hcx, hasher);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToStableHashKey<StableHashingContext<'a>> for region::Scope {
|
||||
type KeyType = region::Scope;
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue