Merge pull request #4474 from rust-lang/rustup-2025-07-18
Automatic Rustup
This commit is contained in:
commit
83395909d0
378 changed files with 4070 additions and 2187 deletions
|
|
@ -3136,9 +3136,9 @@ checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
|||
|
||||
[[package]]
|
||||
name = "rustc-literal-escaper"
|
||||
version = "0.0.4"
|
||||
version = "0.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab03008eb631b703dd16978282ae36c73282e7922fe101a4bd072a40ecea7b8b"
|
||||
checksum = "e4ee29da77c5a54f42697493cd4c9b9f31b74df666a6c04dfc4fde77abe0438b"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-main"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ edition = "2024"
|
|||
# tidy-alphabetical-start
|
||||
bitflags = "2.4.1"
|
||||
memchr = "2.7.4"
|
||||
rustc-literal-escaper = "0.0.4"
|
||||
rustc-literal-escaper = "0.0.5"
|
||||
rustc_ast_ir = { path = "../rustc_ast_ir" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_index = { path = "../rustc_index" }
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
//! - [`Attribute`]: Metadata associated with item.
|
||||
//! - [`UnOp`], [`BinOp`], and [`BinOpKind`]: Unary and binary operators.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::{cmp, fmt};
|
||||
|
||||
pub use GenericArgs::*;
|
||||
|
|
@ -155,6 +155,59 @@ impl Path {
|
|||
}
|
||||
}
|
||||
|
||||
/// Joins multiple symbols with "::" into a path, e.g. "a::b::c". If the first
|
||||
/// segment is `kw::PathRoot` it will be printed as empty, e.g. "::b::c".
|
||||
///
|
||||
/// The generics on the `path` argument mean it can accept many forms, such as:
|
||||
/// - `&[Symbol]`
|
||||
/// - `Vec<Symbol>`
|
||||
/// - `Vec<&Symbol>`
|
||||
/// - `impl Iterator<Item = Symbol>`
|
||||
/// - `impl Iterator<Item = &Symbol>`
|
||||
///
|
||||
/// Panics if `path` is empty or a segment after the first is `kw::PathRoot`.
|
||||
pub fn join_path_syms(path: impl IntoIterator<Item = impl Borrow<Symbol>>) -> String {
|
||||
// This is a guess at the needed capacity that works well in practice. It is slightly faster
|
||||
// than (a) starting with an empty string, or (b) computing the exact capacity required.
|
||||
// `8` works well because it's about the right size and jemalloc's size classes are all
|
||||
// multiples of 8.
|
||||
let mut iter = path.into_iter();
|
||||
let len_hint = iter.size_hint().1.unwrap_or(1);
|
||||
let mut s = String::with_capacity(len_hint * 8);
|
||||
|
||||
let first_sym = *iter.next().unwrap().borrow();
|
||||
if first_sym != kw::PathRoot {
|
||||
s.push_str(first_sym.as_str());
|
||||
}
|
||||
for sym in iter {
|
||||
let sym = *sym.borrow();
|
||||
debug_assert_ne!(sym, kw::PathRoot);
|
||||
s.push_str("::");
|
||||
s.push_str(sym.as_str());
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
/// Like `join_path_syms`, but for `Ident`s. This function is necessary because
|
||||
/// `Ident::to_string` does more than just print the symbol in the `name` field.
|
||||
pub fn join_path_idents(path: impl IntoIterator<Item = impl Borrow<Ident>>) -> String {
|
||||
let mut iter = path.into_iter();
|
||||
let len_hint = iter.size_hint().1.unwrap_or(1);
|
||||
let mut s = String::with_capacity(len_hint * 8);
|
||||
|
||||
let first_ident = *iter.next().unwrap().borrow();
|
||||
if first_ident.name != kw::PathRoot {
|
||||
s.push_str(&first_ident.to_string());
|
||||
}
|
||||
for ident in iter {
|
||||
let ident = *ident.borrow();
|
||||
debug_assert_ne!(ident.name, kw::PathRoot);
|
||||
s.push_str("::");
|
||||
s.push_str(&ident.to_string());
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
/// A segment of a path: an identifier, an optional lifetime, and a set of types.
|
||||
///
|
||||
/// E.g., `std`, `String` or `Box<T>`.
|
||||
|
|
@ -3637,6 +3690,7 @@ impl Default for FnHeader {
|
|||
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
pub struct Trait {
|
||||
pub constness: Const,
|
||||
pub safety: Safety,
|
||||
pub is_auto: IsAuto,
|
||||
pub ident: Ident,
|
||||
|
|
|
|||
|
|
@ -126,11 +126,11 @@ impl LitKind {
|
|||
token::CStr => {
|
||||
let s = symbol.as_str();
|
||||
let mut buf = Vec::with_capacity(s.len());
|
||||
unescape_c_str(s, |_span, c| match c {
|
||||
unescape_c_str(s, |_span, res| match res {
|
||||
Ok(MixedUnit::Char(c)) => {
|
||||
buf.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes())
|
||||
buf.extend_from_slice(c.get().encode_utf8(&mut [0; 4]).as_bytes())
|
||||
}
|
||||
Ok(MixedUnit::HighByte(b)) => buf.push(b),
|
||||
Ok(MixedUnit::HighByte(b)) => buf.push(b.get()),
|
||||
Err(err) => {
|
||||
assert!(!err.is_fatal(), "failed to unescape C string literal")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -738,7 +738,8 @@ macro_rules! common_visitor_and_walkers {
|
|||
try_visit!(vis.visit_ty(self_ty));
|
||||
visit_assoc_items(vis, items, AssocCtxt::Impl { of_trait: of_trait.is_some() })
|
||||
}
|
||||
ItemKind::Trait(box Trait { safety, is_auto: _, ident, generics, bounds, items }) => {
|
||||
ItemKind::Trait(box Trait { constness, safety, is_auto: _, ident, generics, bounds, items }) => {
|
||||
try_visit!(visit_constness(vis, constness));
|
||||
try_visit!(visit_safety(vis, safety));
|
||||
try_visit!(vis.visit_ident(ident));
|
||||
try_visit!(vis.visit_generics(generics));
|
||||
|
|
|
|||
|
|
@ -417,7 +417,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
items: new_impl_items,
|
||||
}))
|
||||
}
|
||||
ItemKind::Trait(box Trait { is_auto, safety, ident, generics, bounds, items }) => {
|
||||
ItemKind::Trait(box Trait {
|
||||
constness,
|
||||
is_auto,
|
||||
safety,
|
||||
ident,
|
||||
generics,
|
||||
bounds,
|
||||
items,
|
||||
}) => {
|
||||
let constness = self.lower_constness(*constness);
|
||||
let ident = self.lower_ident(*ident);
|
||||
let (generics, (safety, items, bounds)) = self.lower_generics(
|
||||
generics,
|
||||
|
|
@ -435,7 +444,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
(safety, items, bounds)
|
||||
},
|
||||
);
|
||||
hir::ItemKind::Trait(*is_auto, safety, ident, generics, bounds, items)
|
||||
hir::ItemKind::Trait(constness, *is_auto, safety, ident, generics, bounds, items)
|
||||
}
|
||||
ItemKind::TraitAlias(ident, generics, bounds) => {
|
||||
let ident = self.lower_ident(*ident);
|
||||
|
|
|
|||
|
|
@ -240,10 +240,10 @@ ast_passes_static_without_body =
|
|||
ast_passes_tilde_const_disallowed = `[const]` is not allowed here
|
||||
.closure = closures cannot have `[const]` trait bounds
|
||||
.function = this function is not `const`, so it cannot have `[const]` trait bounds
|
||||
.trait = this trait is not a `#[const_trait]`, so it cannot have `[const]` trait bounds
|
||||
.trait = this trait is not `const`, so it cannot have `[const]` trait bounds
|
||||
.trait_impl = this impl is not `const`, so it cannot have `[const]` trait bounds
|
||||
.impl = inherent impls cannot have `[const]` trait bounds
|
||||
.trait_assoc_ty = associated types in non-`#[const_trait]` traits cannot have `[const]` trait bounds
|
||||
.trait_assoc_ty = associated types in non-`const` traits cannot have `[const]` trait bounds
|
||||
.trait_impl_assoc_ty = associated types in non-const impls cannot have `[const]` trait bounds
|
||||
.inherent_assoc_ty = inherent associated types cannot have `[const]` trait bounds
|
||||
.object = trait objects cannot have `[const]` trait bounds
|
||||
|
|
|
|||
|
|
@ -49,14 +49,14 @@ enum SelfSemantic {
|
|||
}
|
||||
|
||||
enum TraitOrTraitImpl {
|
||||
Trait { span: Span, constness_span: Option<Span> },
|
||||
Trait { span: Span, constness: Const },
|
||||
TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref_span: Span },
|
||||
}
|
||||
|
||||
impl TraitOrTraitImpl {
|
||||
fn constness(&self) -> Option<Span> {
|
||||
match self {
|
||||
Self::Trait { constness_span: Some(span), .. }
|
||||
Self::Trait { constness: Const::Yes(span), .. }
|
||||
| Self::TraitImpl { constness: Const::Yes(span), .. } => Some(*span),
|
||||
_ => None,
|
||||
}
|
||||
|
|
@ -110,15 +110,10 @@ impl<'a> AstValidator<'a> {
|
|||
self.outer_trait_or_trait_impl = old;
|
||||
}
|
||||
|
||||
fn with_in_trait(
|
||||
&mut self,
|
||||
span: Span,
|
||||
constness_span: Option<Span>,
|
||||
f: impl FnOnce(&mut Self),
|
||||
) {
|
||||
fn with_in_trait(&mut self, span: Span, constness: Const, f: impl FnOnce(&mut Self)) {
|
||||
let old = mem::replace(
|
||||
&mut self.outer_trait_or_trait_impl,
|
||||
Some(TraitOrTraitImpl::Trait { span, constness_span }),
|
||||
Some(TraitOrTraitImpl::Trait { span, constness }),
|
||||
);
|
||||
f(self);
|
||||
self.outer_trait_or_trait_impl = old;
|
||||
|
|
@ -273,7 +268,7 @@ impl<'a> AstValidator<'a> {
|
|||
};
|
||||
|
||||
let make_trait_const_sugg = if const_trait_impl
|
||||
&& let TraitOrTraitImpl::Trait { span, constness_span: None } = parent
|
||||
&& let TraitOrTraitImpl::Trait { span, constness: ast::Const::No } = parent
|
||||
{
|
||||
Some(span.shrink_to_lo())
|
||||
} else {
|
||||
|
|
@ -1131,10 +1126,23 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
}
|
||||
visit::walk_item(self, item)
|
||||
}
|
||||
ItemKind::Trait(box Trait { is_auto, generics, ident, bounds, items, .. }) => {
|
||||
ItemKind::Trait(box Trait {
|
||||
constness,
|
||||
is_auto,
|
||||
generics,
|
||||
ident,
|
||||
bounds,
|
||||
items,
|
||||
..
|
||||
}) => {
|
||||
self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
|
||||
let is_const_trait =
|
||||
// FIXME(const_trait_impl) remove this
|
||||
let alt_const_trait_span =
|
||||
attr::find_by_name(&item.attrs, sym::const_trait).map(|attr| attr.span);
|
||||
let constness = match (*constness, alt_const_trait_span) {
|
||||
(Const::Yes(span), _) | (Const::No, Some(span)) => Const::Yes(span),
|
||||
(Const::No, None) => Const::No,
|
||||
};
|
||||
if *is_auto == IsAuto::Yes {
|
||||
// Auto traits cannot have generics, super traits nor contain items.
|
||||
self.deny_generic_params(generics, ident.span);
|
||||
|
|
@ -1145,13 +1153,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
|
||||
// Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound
|
||||
// context for the supertraits.
|
||||
let disallowed =
|
||||
is_const_trait.is_none().then(|| TildeConstReason::Trait { span: item.span });
|
||||
let disallowed = matches!(constness, ast::Const::No)
|
||||
.then(|| TildeConstReason::Trait { span: item.span });
|
||||
self.with_tilde_const(disallowed, |this| {
|
||||
this.visit_generics(generics);
|
||||
walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
|
||||
});
|
||||
self.with_in_trait(item.span, is_const_trait, |this| {
|
||||
self.with_in_trait(item.span, constness, |this| {
|
||||
walk_list!(this, visit_assoc_item, items, AssocCtxt::Trait);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -590,7 +590,7 @@ pub(crate) struct ConstBoundTraitObject {
|
|||
}
|
||||
|
||||
// FIXME(const_trait_impl): Consider making the note/reason the message of the diagnostic.
|
||||
// FIXME(const_trait_impl): Provide structured suggestions (e.g., add `const` / `#[const_trait]` here).
|
||||
// FIXME(const_trait_impl): Provide structured suggestions (e.g., add `const` here).
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_tilde_const_disallowed)]
|
||||
pub(crate) struct TildeConstDisallowed {
|
||||
|
|
|
|||
|
|
@ -357,6 +357,7 @@ impl<'a> State<'a> {
|
|||
self.bclose(item.span, empty, cb);
|
||||
}
|
||||
ast::ItemKind::Trait(box ast::Trait {
|
||||
constness,
|
||||
safety,
|
||||
is_auto,
|
||||
ident,
|
||||
|
|
@ -366,6 +367,7 @@ impl<'a> State<'a> {
|
|||
}) => {
|
||||
let (cb, ib) = self.head("");
|
||||
self.print_visibility(&item.vis);
|
||||
self.print_constness(*constness);
|
||||
self.print_safety(*safety);
|
||||
self.print_is_auto(*is_auto);
|
||||
self.word_nbsp("trait");
|
||||
|
|
|
|||
|
|
@ -420,6 +420,9 @@ pub enum AttributeKind {
|
|||
/// Represents `#[rustc_unsafe_specialization_marker]`.
|
||||
UnsafeSpecializationMarker(Span),
|
||||
|
||||
/// Represents `#[unstable_feature_bound]`.
|
||||
UnstableFeatureBound(ThinVec<(Symbol, Span)>),
|
||||
|
||||
/// Represents `#[used]`
|
||||
Used { used_by: UsedBy, span: Span },
|
||||
// tidy-alphabetical-end
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ impl AttributeKind {
|
|||
TrackCaller(..) => Yes,
|
||||
TypeConst(..) => Yes,
|
||||
UnsafeSpecializationMarker(..) => No,
|
||||
UnstableFeatureBound(..) => No,
|
||||
Used { .. } => No,
|
||||
// tidy-alphabetical-end
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,6 +136,9 @@ attr_parsing_unrecognized_repr_hint =
|
|||
attr_parsing_unstable_cfg_target_compact =
|
||||
compact `cfg(target(..))` is experimental and subject to change
|
||||
|
||||
attr_parsing_unstable_feature_bound_incompatible_stability = Item annotated with `#[unstable_feature_bound]` should not be stable
|
||||
.help = If this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]`
|
||||
|
||||
attr_parsing_unsupported_literal_cfg_boolean =
|
||||
literal in `cfg` predicate value must be a boolean
|
||||
attr_parsing_unsupported_literal_cfg_string =
|
||||
|
|
|
|||
|
|
@ -27,6 +27,26 @@ impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct UnstableFeatureBoundParser;
|
||||
impl<S: Stage> CombineAttributeParser<S> for UnstableFeatureBoundParser {
|
||||
const PATH: &'static [rustc_span::Symbol] = &[sym::unstable_feature_bound];
|
||||
type Item = (Symbol, Span);
|
||||
const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::UnstableFeatureBound(items);
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");
|
||||
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) -> impl IntoIterator<Item = Self::Item> {
|
||||
if !cx.features().staged_api() {
|
||||
cx.emit_err(session_diagnostics::StabilityOutsideStd { span: cx.attr_span });
|
||||
}
|
||||
parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0])
|
||||
.into_iter()
|
||||
.zip(iter::repeat(cx.attr_span))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct AllowConstFnUnstableParser;
|
||||
impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable];
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use rustc_session::parse::feature_err;
|
|||
use rustc_span::{Span, Symbol, sym};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::context::{AcceptContext, ShouldEmit, Stage};
|
||||
use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, NameValueParser};
|
||||
use crate::{
|
||||
CfgMatchesLintEmitter, fluent_generated, parse_version, session_diagnostics, try_gate_cfg,
|
||||
|
|
@ -90,7 +90,7 @@ fn parse_cfg_entry_version<S: Stage>(
|
|||
list: &MetaItemListParser<'_>,
|
||||
meta_span: Span,
|
||||
) -> Option<CfgEntry> {
|
||||
try_gate_cfg(sym::version, meta_span, cx.sess(), Some(cx.features()));
|
||||
try_gate_cfg(sym::version, meta_span, cx.sess(), cx.features_option());
|
||||
let Some(version) = list.single() else {
|
||||
cx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { span: list.span });
|
||||
return None;
|
||||
|
|
@ -119,7 +119,9 @@ fn parse_cfg_entry_target<S: Stage>(
|
|||
list: &MetaItemListParser<'_>,
|
||||
meta_span: Span,
|
||||
) -> Option<CfgEntry> {
|
||||
if !cx.features().cfg_target_compact() {
|
||||
if let Some(features) = cx.features_option()
|
||||
&& !features.cfg_target_compact()
|
||||
{
|
||||
feature_err(
|
||||
cx.sess(),
|
||||
sym::cfg_target_compact,
|
||||
|
|
@ -186,12 +188,13 @@ pub fn eval_config_entry(
|
|||
cfg_entry: &CfgEntry,
|
||||
id: NodeId,
|
||||
features: Option<&Features>,
|
||||
emit_lints: ShouldEmit,
|
||||
) -> EvalConfigResult {
|
||||
match cfg_entry {
|
||||
CfgEntry::All(subs, ..) => {
|
||||
let mut all = None;
|
||||
for sub in subs {
|
||||
let res = eval_config_entry(sess, sub, id, features);
|
||||
let res = eval_config_entry(sess, sub, id, features, emit_lints);
|
||||
// We cannot short-circuit because `eval_config_entry` emits some lints
|
||||
if !res.as_bool() {
|
||||
all.get_or_insert(res);
|
||||
|
|
@ -202,7 +205,7 @@ pub fn eval_config_entry(
|
|||
CfgEntry::Any(subs, span) => {
|
||||
let mut any = None;
|
||||
for sub in subs {
|
||||
let res = eval_config_entry(sess, sub, id, features);
|
||||
let res = eval_config_entry(sess, sub, id, features, emit_lints);
|
||||
// We cannot short-circuit because `eval_config_entry` emits some lints
|
||||
if res.as_bool() {
|
||||
any.get_or_insert(res);
|
||||
|
|
@ -214,7 +217,7 @@ pub fn eval_config_entry(
|
|||
})
|
||||
}
|
||||
CfgEntry::Not(sub, span) => {
|
||||
if eval_config_entry(sess, sub, id, features).as_bool() {
|
||||
if eval_config_entry(sess, sub, id, features, emit_lints).as_bool() {
|
||||
EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span }
|
||||
} else {
|
||||
EvalConfigResult::True
|
||||
|
|
@ -228,24 +231,28 @@ pub fn eval_config_entry(
|
|||
}
|
||||
}
|
||||
CfgEntry::NameValue { name, name_span, value, span } => {
|
||||
match sess.psess.check_config.expecteds.get(name) {
|
||||
Some(ExpectedValues::Some(values)) if !values.contains(&value.map(|(v, _)| v)) => {
|
||||
id.emit_span_lint(
|
||||
sess,
|
||||
UNEXPECTED_CFGS,
|
||||
*span,
|
||||
BuiltinLintDiag::UnexpectedCfgValue((*name, *name_span), *value),
|
||||
);
|
||||
if let ShouldEmit::ErrorsAndLints = emit_lints {
|
||||
match sess.psess.check_config.expecteds.get(name) {
|
||||
Some(ExpectedValues::Some(values))
|
||||
if !values.contains(&value.map(|(v, _)| v)) =>
|
||||
{
|
||||
id.emit_span_lint(
|
||||
sess,
|
||||
UNEXPECTED_CFGS,
|
||||
*span,
|
||||
BuiltinLintDiag::UnexpectedCfgValue((*name, *name_span), *value),
|
||||
);
|
||||
}
|
||||
None if sess.psess.check_config.exhaustive_names => {
|
||||
id.emit_span_lint(
|
||||
sess,
|
||||
UNEXPECTED_CFGS,
|
||||
*span,
|
||||
BuiltinLintDiag::UnexpectedCfgName((*name, *name_span), *value),
|
||||
);
|
||||
}
|
||||
_ => { /* not unexpected */ }
|
||||
}
|
||||
None if sess.psess.check_config.exhaustive_names => {
|
||||
id.emit_span_lint(
|
||||
sess,
|
||||
UNEXPECTED_CFGS,
|
||||
*span,
|
||||
BuiltinLintDiag::UnexpectedCfgName((*name, *name_span), *value),
|
||||
);
|
||||
}
|
||||
_ => { /* not unexpected */ }
|
||||
}
|
||||
|
||||
if sess.psess.config.contains(&(*name, value.map(|(v, _)| v))) {
|
||||
|
|
|
|||
|
|
@ -98,6 +98,16 @@ impl<S: Stage> AttributeParser<S> for StabilityParser {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some((Stability { level: StabilityLevel::Stable { .. }, .. }, _)) = self.stability {
|
||||
for other_attr in cx.all_attrs {
|
||||
if other_attr.word_is(sym::unstable_feature_bound) {
|
||||
cx.emit_err(session_diagnostics::UnstableFeatureBoundIncompatibleStability {
|
||||
span: cx.target_span,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (stability, span) = self.stability?;
|
||||
|
||||
Some(AttributeKind::Stability { stability, span })
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for DoNotImplementViaObjectParser {
|
|||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::DoNotImplementViaObject;
|
||||
}
|
||||
|
||||
// FIXME(const_trait_impl): remove this
|
||||
// Const traits
|
||||
|
||||
pub(crate) struct ConstTraitParser;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirI
|
|||
use rustc_session::Session;
|
||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
|
||||
|
||||
use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
|
||||
use crate::attributes::allow_unstable::{
|
||||
AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser,
|
||||
};
|
||||
use crate::attributes::codegen_attrs::{
|
||||
ColdParser, ExportNameParser, NakedParser, NoMangleParser, OmitGdbPrettyPrinterSectionParser,
|
||||
OptimizeParser, TargetFeatureParser, TrackCallerParser, UsedParser,
|
||||
|
|
@ -133,6 +135,7 @@ attribute_parsers!(
|
|||
Combine<AllowInternalUnstableParser>,
|
||||
Combine<ReprParser>,
|
||||
Combine<TargetFeatureParser>,
|
||||
Combine<UnstableFeatureBoundParser>,
|
||||
// tidy-alphabetical-end
|
||||
|
||||
// tidy-alphabetical-start
|
||||
|
|
@ -223,7 +226,7 @@ impl Stage for Early {
|
|||
sess: &'sess Session,
|
||||
diag: impl for<'x> Diagnostic<'x>,
|
||||
) -> ErrorGuaranteed {
|
||||
if self.emit_errors {
|
||||
if self.emit_errors.should_emit() {
|
||||
sess.dcx().emit_err(diag)
|
||||
} else {
|
||||
sess.dcx().create_err(diag).delay_as_bug()
|
||||
|
|
@ -254,7 +257,7 @@ pub struct Early {
|
|||
/// Whether to emit errors or delay them as a bug
|
||||
/// For most attributes, the attribute will be parsed again in the `Late` stage and in this case the errors should be delayed
|
||||
/// But for some, such as `cfg`, the attribute will be removed before the `Late` stage so errors must be emitted
|
||||
pub emit_errors: bool,
|
||||
pub emit_errors: ShouldEmit,
|
||||
}
|
||||
/// used when parsing attributes during ast lowering
|
||||
pub struct Late;
|
||||
|
|
@ -555,6 +558,25 @@ pub enum OmitDoc {
|
|||
Skip,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ShouldEmit {
|
||||
/// The operation will emit errors and lints.
|
||||
/// This is usually what you need.
|
||||
ErrorsAndLints,
|
||||
/// The operation will emit *not* errors and lints.
|
||||
/// Use this if you are *sure* that this operation will be called at a different time with `ShouldEmit::Emit`.
|
||||
Nothing,
|
||||
}
|
||||
|
||||
impl ShouldEmit {
|
||||
pub fn should_emit(&self) -> bool {
|
||||
match self {
|
||||
ShouldEmit::ErrorsAndLints => true,
|
||||
ShouldEmit::Nothing => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Context created once, for example as part of the ast lowering
|
||||
/// context, through which all attributes can be lowered.
|
||||
pub struct AttributeParser<'sess, S: Stage = Late> {
|
||||
|
|
@ -597,7 +619,7 @@ impl<'sess> AttributeParser<'sess, Early> {
|
|||
tools: Vec::new(),
|
||||
parse_only: Some(sym),
|
||||
sess,
|
||||
stage: Early { emit_errors: false },
|
||||
stage: Early { emit_errors: ShouldEmit::Nothing },
|
||||
};
|
||||
let mut parsed = p.parse_attribute_list(
|
||||
attrs,
|
||||
|
|
@ -620,7 +642,7 @@ impl<'sess> AttributeParser<'sess, Early> {
|
|||
target_span: Span,
|
||||
target_node_id: NodeId,
|
||||
features: Option<&'sess Features>,
|
||||
emit_errors: bool,
|
||||
emit_errors: ShouldEmit,
|
||||
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> T,
|
||||
template: &AttributeTemplate,
|
||||
) -> T {
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ pub use attributes::cfg_old::*;
|
|||
pub use attributes::util::{
|
||||
find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version,
|
||||
};
|
||||
pub use context::{AttributeParser, Early, Late, OmitDoc};
|
||||
pub use context::{AttributeParser, Early, Late, OmitDoc, ShouldEmit};
|
||||
pub use lints::emit_attribute_lint;
|
||||
|
||||
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
|
||||
|
|
|
|||
|
|
@ -503,6 +503,14 @@ pub(crate) struct UnrecognizedReprHint {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_unstable_feature_bound_incompatible_stability)]
|
||||
#[help]
|
||||
pub(crate) struct UnstableFeatureBoundIncompatibleStability {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_naked_functions_incompatible_attribute, code = E0736)]
|
||||
pub(crate) struct NakedFunctionIncompatibleAttribute {
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ use std::sync::Arc;
|
|||
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token;
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_ast::{join_path_idents, token};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_expand::base::{
|
||||
DummyResult, ExpandResult, ExtCtxt, MacEager, MacResult, MacroExpanderResult, resolve_path,
|
||||
|
|
@ -100,7 +100,7 @@ pub(crate) fn expand_mod(
|
|||
let sp = cx.with_def_site_ctxt(sp);
|
||||
check_zero_tts(cx, sp, tts, "module_path!");
|
||||
let mod_path = &cx.current_expansion.module.mod_path;
|
||||
let string = mod_path.iter().map(|x| x.to_string()).collect::<Vec<String>>().join("::");
|
||||
let string = join_path_idents(mod_path);
|
||||
|
||||
ExpandResult::Ready(MacEager::expr(cx.expr_str(sp, Symbol::intern(&string))))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use std::assert_matches::assert_matches;
|
|||
use std::iter;
|
||||
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::{self as ast, GenericParamKind, attr};
|
||||
use rustc_ast::{self as ast, GenericParamKind, attr, join_path_idents};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::{Applicability, Diag, Level};
|
||||
use rustc_expand::base::*;
|
||||
|
|
@ -446,12 +446,7 @@ fn get_location_info(cx: &ExtCtxt<'_>, fn_: &ast::Fn) -> (Symbol, usize, usize,
|
|||
}
|
||||
|
||||
fn item_path(mod_path: &[Ident], item_ident: &Ident) -> String {
|
||||
mod_path
|
||||
.iter()
|
||||
.chain(iter::once(item_ident))
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join("::")
|
||||
join_path_idents(mod_path.iter().chain(iter::once(item_ident)))
|
||||
}
|
||||
|
||||
enum ShouldPanic {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ use std::sync::Arc;
|
|||
|
||||
use gccjit::{Context, OutputKind};
|
||||
use object::read::archive::ArchiveFile;
|
||||
use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared};
|
||||
use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared};
|
||||
use rustc_codegen_ssa::back::symbol_export;
|
||||
use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput};
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
|
|
@ -176,7 +176,7 @@ pub(crate) fn run_fat(
|
|||
cgcx: &CodegenContext<GccCodegenBackend>,
|
||||
modules: Vec<FatLtoInput<GccCodegenBackend>>,
|
||||
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
|
||||
) -> Result<LtoModuleCodegen<GccCodegenBackend>, FatalError> {
|
||||
) -> Result<ModuleCodegen<GccContext>, FatalError> {
|
||||
let dcx = cgcx.create_dcx();
|
||||
let dcx = dcx.handle();
|
||||
let lto_data = prepare_lto(cgcx, dcx)?;
|
||||
|
|
@ -201,7 +201,7 @@ fn fat_lto(
|
|||
mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
|
||||
tmp_path: TempDir,
|
||||
//symbols_below_threshold: &[String],
|
||||
) -> Result<LtoModuleCodegen<GccCodegenBackend>, FatalError> {
|
||||
) -> Result<ModuleCodegen<GccContext>, FatalError> {
|
||||
let _timer = cgcx.prof.generic_activity("GCC_fat_lto_build_monolithic_module");
|
||||
info!("going for a fat lto");
|
||||
|
||||
|
|
@ -334,7 +334,7 @@ fn fat_lto(
|
|||
// of now.
|
||||
module.module_llvm.temp_dir = Some(tmp_path);
|
||||
|
||||
Ok(LtoModuleCodegen::Fat(module))
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
pub struct ModuleBuffer(PathBuf);
|
||||
|
|
@ -358,7 +358,7 @@ pub(crate) fn run_thin(
|
|||
cgcx: &CodegenContext<GccCodegenBackend>,
|
||||
modules: Vec<(String, ThinBuffer)>,
|
||||
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
|
||||
) -> Result<(Vec<LtoModuleCodegen<GccCodegenBackend>>, Vec<WorkProduct>), FatalError> {
|
||||
) -> Result<(Vec<ThinModule<GccCodegenBackend>>, Vec<WorkProduct>), FatalError> {
|
||||
let dcx = cgcx.create_dcx();
|
||||
let dcx = dcx.handle();
|
||||
let lto_data = prepare_lto(cgcx, dcx)?;
|
||||
|
|
@ -427,7 +427,7 @@ fn thin_lto(
|
|||
tmp_path: TempDir,
|
||||
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
|
||||
//_symbols_below_threshold: &[String],
|
||||
) -> Result<(Vec<LtoModuleCodegen<GccCodegenBackend>>, Vec<WorkProduct>), FatalError> {
|
||||
) -> Result<(Vec<ThinModule<GccCodegenBackend>>, Vec<WorkProduct>), FatalError> {
|
||||
let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_global_analysis");
|
||||
info!("going for that thin, thin LTO");
|
||||
|
||||
|
|
@ -573,8 +573,7 @@ fn thin_lto(
|
|||
}*/
|
||||
|
||||
info!(" - {}: re-compiled", module_name);
|
||||
opt_jobs
|
||||
.push(LtoModuleCodegen::Thin(ThinModule { shared: shared.clone(), idx: module_index }));
|
||||
opt_jobs.push(ThinModule { shared: shared.clone(), idx: module_index });
|
||||
}
|
||||
|
||||
// Save the current ThinLTO import information for the next compilation
|
||||
|
|
|
|||
|
|
@ -16,10 +16,12 @@ use crate::{GccCodegenBackend, GccContext};
|
|||
|
||||
pub(crate) fn codegen(
|
||||
cgcx: &CodegenContext<GccCodegenBackend>,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
module: ModuleCodegen<GccContext>,
|
||||
config: &ModuleConfig,
|
||||
) -> Result<CompiledModule, FatalError> {
|
||||
let dcx = cgcx.create_dcx();
|
||||
let dcx = dcx.handle();
|
||||
|
||||
let _timer = cgcx.prof.generic_activity_with_arg("GCC_module_codegen", &*module.name);
|
||||
{
|
||||
let context = &module.module_llvm.context;
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ use gccjit::{CType, Context, OptimizationLevel};
|
|||
use gccjit::{TargetInfo, Version};
|
||||
use rustc_ast::expand::allocator::AllocatorKind;
|
||||
use rustc_ast::expand::autodiff_attrs::AutoDiffItem;
|
||||
use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule};
|
||||
use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule};
|
||||
use rustc_codegen_ssa::back::write::{
|
||||
CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryFn,
|
||||
};
|
||||
|
|
@ -353,11 +353,16 @@ impl WriteBackendMethods for GccCodegenBackend {
|
|||
type ThinData = ThinData;
|
||||
type ThinBuffer = ThinBuffer;
|
||||
|
||||
fn run_fat_lto(
|
||||
fn run_and_optimize_fat_lto(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
modules: Vec<FatLtoInput<Self>>,
|
||||
cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
|
||||
) -> Result<LtoModuleCodegen<Self>, FatalError> {
|
||||
diff_fncs: Vec<AutoDiffItem>,
|
||||
) -> Result<ModuleCodegen<Self::Module>, FatalError> {
|
||||
if !diff_fncs.is_empty() {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
back::lto::run_fat(cgcx, modules, cached_modules)
|
||||
}
|
||||
|
||||
|
|
@ -365,7 +370,7 @@ impl WriteBackendMethods for GccCodegenBackend {
|
|||
cgcx: &CodegenContext<Self>,
|
||||
modules: Vec<(String, Self::ThinBuffer)>,
|
||||
cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
|
||||
) -> Result<(Vec<LtoModuleCodegen<Self>>, Vec<WorkProduct>), FatalError> {
|
||||
) -> Result<(Vec<ThinModule<Self>>, Vec<WorkProduct>), FatalError> {
|
||||
back::lto::run_thin(cgcx, modules, cached_modules)
|
||||
}
|
||||
|
||||
|
|
@ -387,14 +392,6 @@ impl WriteBackendMethods for GccCodegenBackend {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn optimize_fat(
|
||||
_cgcx: &CodegenContext<Self>,
|
||||
_module: &mut ModuleCodegen<Self::Module>,
|
||||
) -> Result<(), FatalError> {
|
||||
// TODO(antoyo)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn optimize_thin(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
thin: ThinModule<Self>,
|
||||
|
|
@ -404,11 +401,10 @@ impl WriteBackendMethods for GccCodegenBackend {
|
|||
|
||||
fn codegen(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
module: ModuleCodegen<Self::Module>,
|
||||
config: &ModuleConfig,
|
||||
) -> Result<CompiledModule, FatalError> {
|
||||
back::write::codegen(cgcx, dcx, module, config)
|
||||
back::write::codegen(cgcx, module, config)
|
||||
}
|
||||
|
||||
fn prepare_thin(
|
||||
|
|
@ -429,15 +425,6 @@ impl WriteBackendMethods for GccCodegenBackend {
|
|||
) -> Result<ModuleCodegen<Self::Module>, FatalError> {
|
||||
back::write::link(cgcx, dcx, modules)
|
||||
}
|
||||
|
||||
fn autodiff(
|
||||
_cgcx: &CodegenContext<Self>,
|
||||
_module: &ModuleCodegen<Self::Module>,
|
||||
_diff_functions: Vec<AutoDiffItem>,
|
||||
_config: &ModuleConfig,
|
||||
) -> Result<(), FatalError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
/// This is the entrypoint for a hot plugged rustc_codegen_gccjit
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
codegen_llvm_autodiff_without_enable = using the autodiff feature requires -Z autodiff=Enable
|
||||
codegen_llvm_autodiff_without_lto = using the autodiff feature requires using fat-lto
|
||||
|
||||
codegen_llvm_copy_bitcode = failed to copy bitcode to object file: {$err}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use std::sync::Arc;
|
|||
use std::{io, iter, slice};
|
||||
|
||||
use object::read::archive::ArchiveFile;
|
||||
use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared};
|
||||
use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared};
|
||||
use rustc_codegen_ssa::back::symbol_export;
|
||||
use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput};
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
|
|
@ -201,7 +201,7 @@ pub(crate) fn run_fat(
|
|||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
modules: Vec<FatLtoInput<LlvmCodegenBackend>>,
|
||||
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
|
||||
) -> Result<LtoModuleCodegen<LlvmCodegenBackend>, FatalError> {
|
||||
) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
|
||||
let dcx = cgcx.create_dcx();
|
||||
let dcx = dcx.handle();
|
||||
let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, dcx)?;
|
||||
|
|
@ -217,7 +217,7 @@ pub(crate) fn run_thin(
|
|||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
modules: Vec<(String, ThinBuffer)>,
|
||||
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
|
||||
) -> Result<(Vec<LtoModuleCodegen<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError> {
|
||||
) -> Result<(Vec<ThinModule<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError> {
|
||||
let dcx = cgcx.create_dcx();
|
||||
let dcx = dcx.handle();
|
||||
let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, dcx)?;
|
||||
|
|
@ -248,7 +248,7 @@ fn fat_lto(
|
|||
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
|
||||
mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
|
||||
symbols_below_threshold: &[*const libc::c_char],
|
||||
) -> Result<LtoModuleCodegen<LlvmCodegenBackend>, FatalError> {
|
||||
) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
|
||||
let _timer = cgcx.prof.generic_activity("LLVM_fat_lto_build_monolithic_module");
|
||||
info!("going for a fat lto");
|
||||
|
||||
|
|
@ -366,7 +366,7 @@ fn fat_lto(
|
|||
save_temp_bitcode(cgcx, &module, "lto.after-restriction");
|
||||
}
|
||||
|
||||
Ok(LtoModuleCodegen::Fat(module))
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
pub(crate) struct Linker<'a>(&'a mut llvm::Linker<'a>);
|
||||
|
|
@ -436,7 +436,7 @@ fn thin_lto(
|
|||
serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
|
||||
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
|
||||
symbols_below_threshold: &[*const libc::c_char],
|
||||
) -> Result<(Vec<LtoModuleCodegen<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError> {
|
||||
) -> Result<(Vec<ThinModule<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError> {
|
||||
let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_global_analysis");
|
||||
unsafe {
|
||||
info!("going for that thin, thin LTO");
|
||||
|
|
@ -568,10 +568,7 @@ fn thin_lto(
|
|||
}
|
||||
|
||||
info!(" - {}: re-compiled", module_name);
|
||||
opt_jobs.push(LtoModuleCodegen::Thin(ThinModule {
|
||||
shared: Arc::clone(&shared),
|
||||
idx: module_index,
|
||||
}));
|
||||
opt_jobs.push(ThinModule { shared: Arc::clone(&shared), idx: module_index });
|
||||
}
|
||||
|
||||
// Save the current ThinLTO import information for the next compilation
|
||||
|
|
|
|||
|
|
@ -817,10 +817,12 @@ pub(crate) fn link(
|
|||
|
||||
pub(crate) fn codegen(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
module: ModuleCodegen<ModuleLlvm>,
|
||||
config: &ModuleConfig,
|
||||
) -> Result<CompiledModule, FatalError> {
|
||||
let dcx = cgcx.create_dcx();
|
||||
let dcx = dcx.handle();
|
||||
|
||||
let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_codegen", &*module.name);
|
||||
{
|
||||
let llmod = module.module_llvm.llmod();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ use std::ptr;
|
|||
|
||||
use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, AutoDiffItem, DiffActivity, DiffMode};
|
||||
use rustc_codegen_ssa::ModuleCodegen;
|
||||
use rustc_codegen_ssa::back::write::ModuleConfig;
|
||||
use rustc_codegen_ssa::common::TypeKind;
|
||||
use rustc_codegen_ssa::traits::BaseTypeCodegenMethods;
|
||||
use rustc_errors::FatalError;
|
||||
|
|
@ -461,7 +460,6 @@ pub(crate) fn differentiate<'ll>(
|
|||
module: &'ll ModuleCodegen<ModuleLlvm>,
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
diff_items: Vec<AutoDiffItem>,
|
||||
_config: &ModuleConfig,
|
||||
) -> Result<(), FatalError> {
|
||||
for item in &diff_items {
|
||||
trace!("{}", item);
|
||||
|
|
|
|||
|
|
@ -37,10 +37,6 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for ParseTargetMachineConfig<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_llvm_autodiff_without_lto)]
|
||||
pub(crate) struct AutoDiffWithoutLTO;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_llvm_autodiff_without_enable)]
|
||||
pub(crate) struct AutoDiffWithoutEnable;
|
||||
|
|
|
|||
|
|
@ -26,11 +26,11 @@ use std::mem::ManuallyDrop;
|
|||
use back::owned_target_machine::OwnedTargetMachine;
|
||||
use back::write::{create_informational_target_machine, create_target_machine};
|
||||
use context::SimpleCx;
|
||||
use errors::{AutoDiffWithoutLTO, ParseTargetMachineConfig};
|
||||
use errors::ParseTargetMachineConfig;
|
||||
use llvm_util::target_config;
|
||||
use rustc_ast::expand::allocator::AllocatorKind;
|
||||
use rustc_ast::expand::autodiff_attrs::AutoDiffItem;
|
||||
use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule};
|
||||
use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule};
|
||||
use rustc_codegen_ssa::back::write::{
|
||||
CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryConfig, TargetMachineFactoryFn,
|
||||
};
|
||||
|
|
@ -43,7 +43,7 @@ use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
|||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_middle::util::Providers;
|
||||
use rustc_session::Session;
|
||||
use rustc_session::config::{Lto, OptLevel, OutputFilenames, PrintKind, PrintRequest};
|
||||
use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest};
|
||||
use rustc_span::Symbol;
|
||||
|
||||
mod back {
|
||||
|
|
@ -174,18 +174,29 @@ impl WriteBackendMethods for LlvmCodegenBackend {
|
|||
) -> Result<ModuleCodegen<Self::Module>, FatalError> {
|
||||
back::write::link(cgcx, dcx, modules)
|
||||
}
|
||||
fn run_fat_lto(
|
||||
fn run_and_optimize_fat_lto(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
modules: Vec<FatLtoInput<Self>>,
|
||||
cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
|
||||
) -> Result<LtoModuleCodegen<Self>, FatalError> {
|
||||
back::lto::run_fat(cgcx, modules, cached_modules)
|
||||
diff_fncs: Vec<AutoDiffItem>,
|
||||
) -> Result<ModuleCodegen<Self::Module>, FatalError> {
|
||||
let mut module = back::lto::run_fat(cgcx, modules, cached_modules)?;
|
||||
|
||||
if !diff_fncs.is_empty() {
|
||||
builder::autodiff::differentiate(&module, cgcx, diff_fncs)?;
|
||||
}
|
||||
|
||||
let dcx = cgcx.create_dcx();
|
||||
let dcx = dcx.handle();
|
||||
back::lto::run_pass_manager(cgcx, dcx, &mut module, false)?;
|
||||
|
||||
Ok(module)
|
||||
}
|
||||
fn run_thin_lto(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
modules: Vec<(String, Self::ThinBuffer)>,
|
||||
cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
|
||||
) -> Result<(Vec<LtoModuleCodegen<Self>>, Vec<WorkProduct>), FatalError> {
|
||||
) -> Result<(Vec<ThinModule<Self>>, Vec<WorkProduct>), FatalError> {
|
||||
back::lto::run_thin(cgcx, modules, cached_modules)
|
||||
}
|
||||
fn optimize(
|
||||
|
|
@ -196,14 +207,6 @@ impl WriteBackendMethods for LlvmCodegenBackend {
|
|||
) -> Result<(), FatalError> {
|
||||
back::write::optimize(cgcx, dcx, module, config)
|
||||
}
|
||||
fn optimize_fat(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
module: &mut ModuleCodegen<Self::Module>,
|
||||
) -> Result<(), FatalError> {
|
||||
let dcx = cgcx.create_dcx();
|
||||
let dcx = dcx.handle();
|
||||
back::lto::run_pass_manager(cgcx, dcx, module, false)
|
||||
}
|
||||
fn optimize_thin(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
thin: ThinModule<Self>,
|
||||
|
|
@ -212,11 +215,10 @@ impl WriteBackendMethods for LlvmCodegenBackend {
|
|||
}
|
||||
fn codegen(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
module: ModuleCodegen<Self::Module>,
|
||||
config: &ModuleConfig,
|
||||
) -> Result<CompiledModule, FatalError> {
|
||||
back::write::codegen(cgcx, dcx, module, config)
|
||||
back::write::codegen(cgcx, module, config)
|
||||
}
|
||||
fn prepare_thin(
|
||||
module: ModuleCodegen<Self::Module>,
|
||||
|
|
@ -227,19 +229,6 @@ impl WriteBackendMethods for LlvmCodegenBackend {
|
|||
fn serialize_module(module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer) {
|
||||
(module.name, back::lto::ModuleBuffer::new(module.module_llvm.llmod()))
|
||||
}
|
||||
/// Generate autodiff rules
|
||||
fn autodiff(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
module: &ModuleCodegen<Self::Module>,
|
||||
diff_fncs: Vec<AutoDiffItem>,
|
||||
config: &ModuleConfig,
|
||||
) -> Result<(), FatalError> {
|
||||
if cgcx.lto != Lto::Fat {
|
||||
let dcx = cgcx.create_dcx();
|
||||
return Err(dcx.handle().emit_almost_fatal(AutoDiffWithoutLTO));
|
||||
}
|
||||
builder::autodiff::differentiate(module, cgcx, diff_fncs, config)
|
||||
}
|
||||
}
|
||||
|
||||
impl LlvmCodegenBackend {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,8 @@
|
|||
use std::ffi::CString;
|
||||
use std::sync::Arc;
|
||||
|
||||
use rustc_ast::expand::autodiff_attrs::AutoDiffItem;
|
||||
use rustc_data_structures::memmap::Mmap;
|
||||
use rustc_errors::FatalError;
|
||||
|
||||
use super::write::CodegenContext;
|
||||
use crate::ModuleCodegen;
|
||||
use crate::back::write::ModuleConfig;
|
||||
use crate::traits::*;
|
||||
|
||||
pub struct ThinModule<B: WriteBackendMethods> {
|
||||
|
|
@ -42,61 +37,6 @@ pub struct ThinShared<B: WriteBackendMethods> {
|
|||
pub module_names: Vec<CString>,
|
||||
}
|
||||
|
||||
pub enum LtoModuleCodegen<B: WriteBackendMethods> {
|
||||
Fat(ModuleCodegen<B::Module>),
|
||||
Thin(ThinModule<B>),
|
||||
}
|
||||
|
||||
impl<B: WriteBackendMethods> LtoModuleCodegen<B> {
|
||||
pub fn name(&self) -> &str {
|
||||
match *self {
|
||||
LtoModuleCodegen::Fat(_) => "everything",
|
||||
LtoModuleCodegen::Thin(ref m) => m.name(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Optimize this module within the given codegen context.
|
||||
pub fn optimize(
|
||||
self,
|
||||
cgcx: &CodegenContext<B>,
|
||||
) -> Result<ModuleCodegen<B::Module>, FatalError> {
|
||||
match self {
|
||||
LtoModuleCodegen::Fat(mut module) => {
|
||||
B::optimize_fat(cgcx, &mut module)?;
|
||||
Ok(module)
|
||||
}
|
||||
LtoModuleCodegen::Thin(thin) => B::optimize_thin(cgcx, thin),
|
||||
}
|
||||
}
|
||||
|
||||
/// A "gauge" of how costly it is to optimize this module, used to sort
|
||||
/// biggest modules first.
|
||||
pub fn cost(&self) -> u64 {
|
||||
match *self {
|
||||
// Only one module with fat LTO, so the cost doesn't matter.
|
||||
LtoModuleCodegen::Fat(_) => 0,
|
||||
LtoModuleCodegen::Thin(ref m) => m.cost(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Run autodiff on Fat LTO module
|
||||
pub fn autodiff(
|
||||
self,
|
||||
cgcx: &CodegenContext<B>,
|
||||
diff_fncs: Vec<AutoDiffItem>,
|
||||
config: &ModuleConfig,
|
||||
) -> Result<LtoModuleCodegen<B>, FatalError> {
|
||||
match &self {
|
||||
LtoModuleCodegen::Fat(module) => {
|
||||
B::autodiff(cgcx, &module, diff_fncs, config)?;
|
||||
}
|
||||
_ => panic!("autodiff called with non-fat LTO module"),
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum SerializedModule<M: ModuleBufferMethods> {
|
||||
Local(M),
|
||||
FromRlib(Vec<u8>),
|
||||
|
|
|
|||
|
|
@ -397,50 +397,31 @@ impl<B: WriteBackendMethods> CodegenContext<B> {
|
|||
}
|
||||
}
|
||||
|
||||
fn generate_lto_work<B: ExtraBackendMethods>(
|
||||
fn generate_thin_lto_work<B: ExtraBackendMethods>(
|
||||
cgcx: &CodegenContext<B>,
|
||||
autodiff: Vec<AutoDiffItem>,
|
||||
needs_fat_lto: Vec<FatLtoInput<B>>,
|
||||
needs_thin_lto: Vec<(String, B::ThinBuffer)>,
|
||||
import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>,
|
||||
) -> Vec<(WorkItem<B>, u64)> {
|
||||
let _prof_timer = cgcx.prof.generic_activity("codegen_generate_lto_work");
|
||||
let _prof_timer = cgcx.prof.generic_activity("codegen_thin_generate_lto_work");
|
||||
|
||||
if !needs_fat_lto.is_empty() {
|
||||
assert!(needs_thin_lto.is_empty());
|
||||
let mut module =
|
||||
B::run_fat_lto(cgcx, needs_fat_lto, import_only_modules).unwrap_or_else(|e| e.raise());
|
||||
if cgcx.lto == Lto::Fat && !autodiff.is_empty() {
|
||||
let config = cgcx.config(ModuleKind::Regular);
|
||||
module = module.autodiff(cgcx, autodiff, config).unwrap_or_else(|e| e.raise());
|
||||
}
|
||||
// We are adding a single work item, so the cost doesn't matter.
|
||||
vec![(WorkItem::LTO(module), 0)]
|
||||
} else {
|
||||
if !autodiff.is_empty() {
|
||||
let dcx = cgcx.create_dcx();
|
||||
dcx.handle().emit_fatal(AutodiffWithoutLto {});
|
||||
}
|
||||
assert!(needs_fat_lto.is_empty());
|
||||
let (lto_modules, copy_jobs) = B::run_thin_lto(cgcx, needs_thin_lto, import_only_modules)
|
||||
.unwrap_or_else(|e| e.raise());
|
||||
lto_modules
|
||||
.into_iter()
|
||||
.map(|module| {
|
||||
let cost = module.cost();
|
||||
(WorkItem::LTO(module), cost)
|
||||
})
|
||||
.chain(copy_jobs.into_iter().map(|wp| {
|
||||
(
|
||||
WorkItem::CopyPostLtoArtifacts(CachedModuleCodegen {
|
||||
name: wp.cgu_name.clone(),
|
||||
source: wp,
|
||||
}),
|
||||
0, // copying is very cheap
|
||||
)
|
||||
}))
|
||||
.collect()
|
||||
}
|
||||
let (lto_modules, copy_jobs) =
|
||||
B::run_thin_lto(cgcx, needs_thin_lto, import_only_modules).unwrap_or_else(|e| e.raise());
|
||||
lto_modules
|
||||
.into_iter()
|
||||
.map(|module| {
|
||||
let cost = module.cost();
|
||||
(WorkItem::ThinLto(module), cost)
|
||||
})
|
||||
.chain(copy_jobs.into_iter().map(|wp| {
|
||||
(
|
||||
WorkItem::CopyPostLtoArtifacts(CachedModuleCodegen {
|
||||
name: wp.cgu_name.clone(),
|
||||
source: wp,
|
||||
}),
|
||||
0, // copying is very cheap
|
||||
)
|
||||
}))
|
||||
.collect()
|
||||
}
|
||||
|
||||
struct CompiledModules {
|
||||
|
|
@ -470,6 +451,7 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>(
|
|||
backend: B,
|
||||
tcx: TyCtxt<'_>,
|
||||
target_cpu: String,
|
||||
autodiff_items: &[AutoDiffItem],
|
||||
) -> OngoingCodegen<B> {
|
||||
let (coordinator_send, coordinator_receive) = channel();
|
||||
|
||||
|
|
@ -488,6 +470,7 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>(
|
|||
backend.clone(),
|
||||
tcx,
|
||||
&crate_info,
|
||||
autodiff_items,
|
||||
shared_emitter,
|
||||
codegen_worker_send,
|
||||
coordinator_receive,
|
||||
|
|
@ -736,15 +719,23 @@ pub(crate) enum WorkItem<B: WriteBackendMethods> {
|
|||
/// Copy the post-LTO artifacts from the incremental cache to the output
|
||||
/// directory.
|
||||
CopyPostLtoArtifacts(CachedModuleCodegen),
|
||||
/// Performs (Thin)LTO on the given module.
|
||||
LTO(lto::LtoModuleCodegen<B>),
|
||||
/// Performs fat LTO on the given module.
|
||||
FatLto {
|
||||
needs_fat_lto: Vec<FatLtoInput<B>>,
|
||||
import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>,
|
||||
autodiff: Vec<AutoDiffItem>,
|
||||
},
|
||||
/// Performs thin-LTO on the given module.
|
||||
ThinLto(lto::ThinModule<B>),
|
||||
}
|
||||
|
||||
impl<B: WriteBackendMethods> WorkItem<B> {
|
||||
fn module_kind(&self) -> ModuleKind {
|
||||
match *self {
|
||||
WorkItem::Optimize(ref m) => m.kind,
|
||||
WorkItem::CopyPostLtoArtifacts(_) | WorkItem::LTO(_) => ModuleKind::Regular,
|
||||
WorkItem::CopyPostLtoArtifacts(_) | WorkItem::FatLto { .. } | WorkItem::ThinLto(_) => {
|
||||
ModuleKind::Regular
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -792,7 +783,8 @@ impl<B: WriteBackendMethods> WorkItem<B> {
|
|||
match self {
|
||||
WorkItem::Optimize(m) => desc("opt", "optimize module", &m.name),
|
||||
WorkItem::CopyPostLtoArtifacts(m) => desc("cpy", "copy LTO artifacts for", &m.name),
|
||||
WorkItem::LTO(m) => desc("lto", "LTO module", m.name()),
|
||||
WorkItem::FatLto { .. } => desc("lto", "fat LTO module", "everything"),
|
||||
WorkItem::ThinLto(m) => desc("lto", "thin-LTO module", m.name()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -996,12 +988,24 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
|
|||
})
|
||||
}
|
||||
|
||||
fn execute_lto_work_item<B: ExtraBackendMethods>(
|
||||
fn execute_fat_lto_work_item<B: ExtraBackendMethods>(
|
||||
cgcx: &CodegenContext<B>,
|
||||
module: lto::LtoModuleCodegen<B>,
|
||||
needs_fat_lto: Vec<FatLtoInput<B>>,
|
||||
import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>,
|
||||
autodiff: Vec<AutoDiffItem>,
|
||||
module_config: &ModuleConfig,
|
||||
) -> Result<WorkItemResult<B>, FatalError> {
|
||||
let module = module.optimize(cgcx)?;
|
||||
let module = B::run_and_optimize_fat_lto(cgcx, needs_fat_lto, import_only_modules, autodiff)?;
|
||||
let module = B::codegen(cgcx, module, module_config)?;
|
||||
Ok(WorkItemResult::Finished(module))
|
||||
}
|
||||
|
||||
fn execute_thin_lto_work_item<B: ExtraBackendMethods>(
|
||||
cgcx: &CodegenContext<B>,
|
||||
module: lto::ThinModule<B>,
|
||||
module_config: &ModuleConfig,
|
||||
) -> Result<WorkItemResult<B>, FatalError> {
|
||||
let module = B::optimize_thin(cgcx, module)?;
|
||||
finish_intra_module_work(cgcx, module, module_config)
|
||||
}
|
||||
|
||||
|
|
@ -1010,11 +1014,8 @@ fn finish_intra_module_work<B: ExtraBackendMethods>(
|
|||
module: ModuleCodegen<B::Module>,
|
||||
module_config: &ModuleConfig,
|
||||
) -> Result<WorkItemResult<B>, FatalError> {
|
||||
let dcx = cgcx.create_dcx();
|
||||
let dcx = dcx.handle();
|
||||
|
||||
if !cgcx.opts.unstable_opts.combine_cgu || module.kind == ModuleKind::Allocator {
|
||||
let module = B::codegen(cgcx, dcx, module, module_config)?;
|
||||
let module = B::codegen(cgcx, module, module_config)?;
|
||||
Ok(WorkItemResult::Finished(module))
|
||||
} else {
|
||||
Ok(WorkItemResult::NeedsLink(module))
|
||||
|
|
@ -1031,9 +1032,6 @@ pub(crate) enum Message<B: WriteBackendMethods> {
|
|||
/// Sent from a backend worker thread.
|
||||
WorkItem { result: Result<WorkItemResult<B>, Option<WorkerFatalError>>, worker_id: usize },
|
||||
|
||||
/// A vector containing all the AutoDiff tasks that we have to pass to Enzyme.
|
||||
AddAutoDiffItems(Vec<AutoDiffItem>),
|
||||
|
||||
/// The frontend has finished generating something (backend IR or a
|
||||
/// post-LTO artifact) for a codegen unit, and it should be passed to the
|
||||
/// backend. Sent from the main thread.
|
||||
|
|
@ -1100,6 +1098,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
backend: B,
|
||||
tcx: TyCtxt<'_>,
|
||||
crate_info: &CrateInfo,
|
||||
autodiff_items: &[AutoDiffItem],
|
||||
shared_emitter: SharedEmitter,
|
||||
codegen_worker_send: Sender<CguMessage>,
|
||||
coordinator_receive: Receiver<Box<dyn Any + Send>>,
|
||||
|
|
@ -1109,6 +1108,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
) -> thread::JoinHandle<Result<CompiledModules, ()>> {
|
||||
let coordinator_send = tx_to_llvm_workers;
|
||||
let sess = tcx.sess;
|
||||
let autodiff_items = autodiff_items.to_vec();
|
||||
|
||||
let mut each_linked_rlib_for_lto = Vec::new();
|
||||
drop(link::each_linked_rlib(crate_info, None, &mut |cnum, path| {
|
||||
|
|
@ -1362,7 +1362,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
|
||||
// This is where we collect codegen units that have gone all the way
|
||||
// through codegen and LLVM.
|
||||
let mut autodiff_items = Vec::new();
|
||||
let mut compiled_modules = vec![];
|
||||
let mut compiled_allocator_module = None;
|
||||
let mut needs_link = Vec::new();
|
||||
|
|
@ -1474,20 +1473,37 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
let needs_thin_lto = mem::take(&mut needs_thin_lto);
|
||||
let import_only_modules = mem::take(&mut lto_import_only_modules);
|
||||
|
||||
for (work, cost) in generate_lto_work(
|
||||
&cgcx,
|
||||
autodiff_items.clone(),
|
||||
needs_fat_lto,
|
||||
needs_thin_lto,
|
||||
import_only_modules,
|
||||
) {
|
||||
let insertion_index = work_items
|
||||
.binary_search_by_key(&cost, |&(_, cost)| cost)
|
||||
.unwrap_or_else(|e| e);
|
||||
work_items.insert(insertion_index, (work, cost));
|
||||
if !needs_fat_lto.is_empty() {
|
||||
assert!(needs_thin_lto.is_empty());
|
||||
|
||||
work_items.push((
|
||||
WorkItem::FatLto {
|
||||
needs_fat_lto,
|
||||
import_only_modules,
|
||||
autodiff: autodiff_items.clone(),
|
||||
},
|
||||
0,
|
||||
));
|
||||
if cgcx.parallel {
|
||||
helper.request_token();
|
||||
}
|
||||
} else {
|
||||
if !autodiff_items.is_empty() {
|
||||
let dcx = cgcx.create_dcx();
|
||||
dcx.handle().emit_fatal(AutodiffWithoutLto {});
|
||||
}
|
||||
|
||||
for (work, cost) in
|
||||
generate_thin_lto_work(&cgcx, needs_thin_lto, import_only_modules)
|
||||
{
|
||||
let insertion_index = work_items
|
||||
.binary_search_by_key(&cost, |&(_, cost)| cost)
|
||||
.unwrap_or_else(|e| e);
|
||||
work_items.insert(insertion_index, (work, cost));
|
||||
if cgcx.parallel {
|
||||
helper.request_token();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1616,10 +1632,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
main_thread_state = MainThreadState::Idle;
|
||||
}
|
||||
|
||||
Message::AddAutoDiffItems(mut items) => {
|
||||
autodiff_items.append(&mut items);
|
||||
}
|
||||
|
||||
Message::CodegenComplete => {
|
||||
if codegen_state != Aborted {
|
||||
codegen_state = Completed;
|
||||
|
|
@ -1702,7 +1714,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
let dcx = dcx.handle();
|
||||
let module = B::run_link(&cgcx, dcx, needs_link).map_err(|_| ())?;
|
||||
let module =
|
||||
B::codegen(&cgcx, dcx, module, cgcx.config(ModuleKind::Regular)).map_err(|_| ())?;
|
||||
B::codegen(&cgcx, module, cgcx.config(ModuleKind::Regular)).map_err(|_| ())?;
|
||||
compiled_modules.push(module);
|
||||
}
|
||||
|
||||
|
|
@ -1842,10 +1854,22 @@ fn spawn_work<'a, B: ExtraBackendMethods>(
|
|||
);
|
||||
Ok(execute_copy_from_cache_work_item(&cgcx, m, module_config))
|
||||
}
|
||||
WorkItem::LTO(m) => {
|
||||
WorkItem::FatLto { needs_fat_lto, import_only_modules, autodiff } => {
|
||||
let _timer = cgcx
|
||||
.prof
|
||||
.generic_activity_with_arg("codegen_module_perform_lto", "everything");
|
||||
execute_fat_lto_work_item(
|
||||
&cgcx,
|
||||
needs_fat_lto,
|
||||
import_only_modules,
|
||||
autodiff,
|
||||
module_config,
|
||||
)
|
||||
}
|
||||
WorkItem::ThinLto(m) => {
|
||||
let _timer =
|
||||
cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", m.name());
|
||||
execute_lto_work_item(&cgcx, m, module_config)
|
||||
execute_thin_lto_work_item(&cgcx, m, module_config)
|
||||
}
|
||||
})
|
||||
};
|
||||
|
|
@ -2082,10 +2106,6 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
|
|||
drop(self.coordinator.sender.send(Box::new(Message::CodegenComplete::<B>)));
|
||||
}
|
||||
|
||||
pub(crate) fn submit_autodiff_items(&self, items: Vec<AutoDiffItem>) {
|
||||
drop(self.coordinator.sender.send(Box::new(Message::<B>::AddAutoDiffItems(items))));
|
||||
}
|
||||
|
||||
pub(crate) fn check_for_errors(&self, sess: &Session) {
|
||||
self.shared_emitter_main.check(sess, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -647,7 +647,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
|
|||
) -> OngoingCodegen<B> {
|
||||
// Skip crate items and just output metadata in -Z no-codegen mode.
|
||||
if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() {
|
||||
let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu);
|
||||
let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu, &[]);
|
||||
|
||||
ongoing_codegen.codegen_finished(tcx);
|
||||
|
||||
|
|
@ -667,7 +667,6 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
|
|||
// codegen units.
|
||||
let MonoItemPartitions { codegen_units, autodiff_items, .. } =
|
||||
tcx.collect_and_partition_mono_items(());
|
||||
let autodiff_fncs = autodiff_items.to_vec();
|
||||
|
||||
// Force all codegen_unit queries so they are already either red or green
|
||||
// when compile_codegen_unit accesses them. We are not able to re-execute
|
||||
|
|
@ -680,7 +679,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
|
|||
}
|
||||
}
|
||||
|
||||
let ongoing_codegen = start_async_codegen(backend.clone(), tcx, target_cpu);
|
||||
let ongoing_codegen = start_async_codegen(backend.clone(), tcx, target_cpu, autodiff_items);
|
||||
|
||||
// Codegen an allocator shim, if necessary.
|
||||
if let Some(kind) = allocator_kind_for_codegen(tcx) {
|
||||
|
|
@ -710,10 +709,6 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
|
|||
);
|
||||
}
|
||||
|
||||
if !autodiff_fncs.is_empty() {
|
||||
ongoing_codegen.submit_autodiff_items(autodiff_fncs);
|
||||
}
|
||||
|
||||
// For better throughput during parallel processing by LLVM, we used to sort
|
||||
// CGUs largest to smallest. This would lead to better thread utilization
|
||||
// by, for example, preventing a large CGU from being processed last and
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use rustc_ast::expand::autodiff_attrs::AutoDiffItem;
|
|||
use rustc_errors::{DiagCtxtHandle, FatalError};
|
||||
use rustc_middle::dep_graph::WorkProduct;
|
||||
|
||||
use crate::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule};
|
||||
use crate::back::lto::{SerializedModule, ThinModule};
|
||||
use crate::back::write::{CodegenContext, FatLtoInput, ModuleConfig};
|
||||
use crate::{CompiledModule, ModuleCodegen};
|
||||
|
||||
|
|
@ -20,13 +20,14 @@ pub trait WriteBackendMethods: Clone + 'static {
|
|||
dcx: DiagCtxtHandle<'_>,
|
||||
modules: Vec<ModuleCodegen<Self::Module>>,
|
||||
) -> Result<ModuleCodegen<Self::Module>, FatalError>;
|
||||
/// Performs fat LTO by merging all modules into a single one and returning it
|
||||
/// for further optimization.
|
||||
fn run_fat_lto(
|
||||
/// Performs fat LTO by merging all modules into a single one, running autodiff
|
||||
/// if necessary and running any further optimizations
|
||||
fn run_and_optimize_fat_lto(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
modules: Vec<FatLtoInput<Self>>,
|
||||
cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
|
||||
) -> Result<LtoModuleCodegen<Self>, FatalError>;
|
||||
diff_fncs: Vec<AutoDiffItem>,
|
||||
) -> Result<ModuleCodegen<Self::Module>, FatalError>;
|
||||
/// Performs thin LTO by performing necessary global analysis and returning two
|
||||
/// lists, one of the modules that need optimization and another for modules that
|
||||
/// can simply be copied over from the incr. comp. cache.
|
||||
|
|
@ -34,7 +35,7 @@ pub trait WriteBackendMethods: Clone + 'static {
|
|||
cgcx: &CodegenContext<Self>,
|
||||
modules: Vec<(String, Self::ThinBuffer)>,
|
||||
cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
|
||||
) -> Result<(Vec<LtoModuleCodegen<Self>>, Vec<WorkProduct>), FatalError>;
|
||||
) -> Result<(Vec<ThinModule<Self>>, Vec<WorkProduct>), FatalError>;
|
||||
fn print_pass_timings(&self);
|
||||
fn print_statistics(&self);
|
||||
fn optimize(
|
||||
|
|
@ -43,17 +44,12 @@ pub trait WriteBackendMethods: Clone + 'static {
|
|||
module: &mut ModuleCodegen<Self::Module>,
|
||||
config: &ModuleConfig,
|
||||
) -> Result<(), FatalError>;
|
||||
fn optimize_fat(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
llmod: &mut ModuleCodegen<Self::Module>,
|
||||
) -> Result<(), FatalError>;
|
||||
fn optimize_thin(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
thin: ThinModule<Self>,
|
||||
) -> Result<ModuleCodegen<Self::Module>, FatalError>;
|
||||
fn codegen(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
module: ModuleCodegen<Self::Module>,
|
||||
config: &ModuleConfig,
|
||||
) -> Result<CompiledModule, FatalError>;
|
||||
|
|
@ -62,12 +58,6 @@ pub trait WriteBackendMethods: Clone + 'static {
|
|||
want_summary: bool,
|
||||
) -> (String, Self::ThinBuffer);
|
||||
fn serialize_module(module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer);
|
||||
fn autodiff(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
module: &ModuleCodegen<Self::Module>,
|
||||
diff_fncs: Vec<AutoDiffItem>,
|
||||
config: &ModuleConfig,
|
||||
) -> Result<(), FatalError>;
|
||||
}
|
||||
|
||||
pub trait ThinBufferMethods: Send + Sync {
|
||||
|
|
|
|||
|
|
@ -56,6 +56,17 @@ const_eval_const_context = {$kind ->
|
|||
*[other] {""}
|
||||
}
|
||||
|
||||
const_eval_const_heap_ptr_in_final = encountered `const_allocate` pointer in final value that was not made global
|
||||
.note = use `const_make_global` to make allocated pointers immutable before returning
|
||||
|
||||
const_eval_const_make_global_ptr_already_made_global = attempting to call `const_make_global` twice on the same allocation {$alloc}
|
||||
|
||||
const_eval_const_make_global_ptr_is_non_heap = pointer passed to `const_make_global` does not point to a heap allocation: {$ptr}
|
||||
|
||||
const_eval_const_make_global_with_dangling_ptr = pointer passed to `const_make_global` is dangling: {$ptr}
|
||||
|
||||
const_eval_const_make_global_with_offset = making {$ptr} global which does not point to the beginning of an object
|
||||
|
||||
const_eval_copy_nonoverlapping_overlapping =
|
||||
`copy_nonoverlapping` called on overlapping ranges
|
||||
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ pub fn rustc_allow_const_fn_unstable(
|
|||
/// world into two functions: those that are safe to expose on stable (and hence may not use
|
||||
/// unstable features, not even recursively), and those that are not.
|
||||
pub fn is_fn_or_trait_safe_to_expose_on_stable(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
||||
// A default body in a `#[const_trait]` is const-stable when the trait is const-stable.
|
||||
// A default body in a `const trait` is const-stable when the trait is const-stable.
|
||||
if tcx.is_const_default_method(def_id) {
|
||||
return is_fn_or_trait_safe_to_expose_on_stable(tcx, tcx.parent(def_id));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ impl HasStaticRootDefId for DummyMachine {
|
|||
|
||||
impl<'tcx> interpret::Machine<'tcx> for DummyMachine {
|
||||
interpret::compile_time_machine!(<'tcx>);
|
||||
type MemoryKind = !;
|
||||
const PANIC_ON_ALLOC_FAIL: bool = true;
|
||||
|
||||
// We want to just eval random consts in the program, so `eval_mir_const` can fail.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::mem;
|
|||
|
||||
use rustc_errors::{Diag, DiagArgName, DiagArgValue, DiagMessage, IntoDiagArg};
|
||||
use rustc_middle::mir::AssertKind;
|
||||
use rustc_middle::mir::interpret::{Provenance, ReportedErrorInfo};
|
||||
use rustc_middle::mir::interpret::{AllocId, Provenance, ReportedErrorInfo};
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::ty::layout::LayoutError;
|
||||
use rustc_middle::ty::{ConstInt, TyCtxt};
|
||||
|
|
@ -11,8 +11,8 @@ use rustc_span::{Span, Symbol};
|
|||
use super::CompileTimeMachine;
|
||||
use crate::errors::{self, FrameNote, ReportErrorExt};
|
||||
use crate::interpret::{
|
||||
ErrorHandled, Frame, InterpErrorInfo, InterpErrorKind, MachineStopType, err_inval,
|
||||
err_machine_stop,
|
||||
CtfeProvenance, ErrorHandled, Frame, InterpErrorInfo, InterpErrorKind, MachineStopType,
|
||||
Pointer, err_inval, err_machine_stop,
|
||||
};
|
||||
|
||||
/// The CTFE machine has some custom error kinds.
|
||||
|
|
@ -22,8 +22,22 @@ pub enum ConstEvalErrKind {
|
|||
ModifiedGlobal,
|
||||
RecursiveStatic,
|
||||
AssertFailure(AssertKind<ConstInt>),
|
||||
Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
|
||||
Panic {
|
||||
msg: Symbol,
|
||||
line: u32,
|
||||
col: u32,
|
||||
file: Symbol,
|
||||
},
|
||||
WriteThroughImmutablePointer,
|
||||
/// Called `const_make_global` twice.
|
||||
ConstMakeGlobalPtrAlreadyMadeGlobal(AllocId),
|
||||
/// Called `const_make_global` on a non-heap pointer.
|
||||
ConstMakeGlobalPtrIsNonHeap(Pointer<Option<CtfeProvenance>>),
|
||||
/// Called `const_make_global` on a dangling pointer.
|
||||
ConstMakeGlobalWithDanglingPtr(Pointer<Option<CtfeProvenance>>),
|
||||
/// Called `const_make_global` on a pointer that does not start at the
|
||||
/// beginning of an object.
|
||||
ConstMakeGlobalWithOffset(Pointer<Option<CtfeProvenance>>),
|
||||
}
|
||||
|
||||
impl MachineStopType for ConstEvalErrKind {
|
||||
|
|
@ -38,6 +52,12 @@ impl MachineStopType for ConstEvalErrKind {
|
|||
RecursiveStatic => const_eval_recursive_static,
|
||||
AssertFailure(x) => x.diagnostic_message(),
|
||||
WriteThroughImmutablePointer => const_eval_write_through_immutable_pointer,
|
||||
ConstMakeGlobalPtrAlreadyMadeGlobal { .. } => {
|
||||
const_eval_const_make_global_ptr_already_made_global
|
||||
}
|
||||
ConstMakeGlobalPtrIsNonHeap(_) => const_eval_const_make_global_ptr_is_non_heap,
|
||||
ConstMakeGlobalWithDanglingPtr(_) => const_eval_const_make_global_with_dangling_ptr,
|
||||
ConstMakeGlobalWithOffset(_) => const_eval_const_make_global_with_offset,
|
||||
}
|
||||
}
|
||||
fn add_args(self: Box<Self>, adder: &mut dyn FnMut(DiagArgName, DiagArgValue)) {
|
||||
|
|
@ -51,6 +71,14 @@ impl MachineStopType for ConstEvalErrKind {
|
|||
Panic { msg, .. } => {
|
||||
adder("msg".into(), msg.into_diag_arg(&mut None));
|
||||
}
|
||||
ConstMakeGlobalPtrIsNonHeap(ptr)
|
||||
| ConstMakeGlobalWithOffset(ptr)
|
||||
| ConstMakeGlobalWithDanglingPtr(ptr) => {
|
||||
adder("ptr".into(), format!("{ptr:?}").into_diag_arg(&mut None));
|
||||
}
|
||||
ConstMakeGlobalPtrAlreadyMadeGlobal(alloc) => {
|
||||
adder("alloc".into(), alloc.into_diag_arg(&mut None));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use tracing::{debug, instrument, trace};
|
|||
use super::{CanAccessMutGlobal, CompileTimeInterpCx, CompileTimeMachine};
|
||||
use crate::const_eval::CheckAlignment;
|
||||
use crate::interpret::{
|
||||
CtfeValidationMode, GlobalId, Immediate, InternKind, InternResult, InterpCx, InterpErrorKind,
|
||||
CtfeValidationMode, GlobalId, Immediate, InternError, InternKind, InterpCx, InterpErrorKind,
|
||||
InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, ReturnContinuation, create_static_alloc,
|
||||
intern_const_alloc_recursive, interp_ok, throw_exhaust,
|
||||
};
|
||||
|
|
@ -93,25 +93,30 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
|
|||
// Since evaluation had no errors, validate the resulting constant.
|
||||
const_validate_mplace(ecx, &ret, cid)?;
|
||||
|
||||
// Only report this after validation, as validaiton produces much better diagnostics.
|
||||
// Only report this after validation, as validation produces much better diagnostics.
|
||||
// FIXME: ensure validation always reports this and stop making interning care about it.
|
||||
|
||||
match intern_result {
|
||||
Ok(()) => {}
|
||||
Err(InternResult::FoundDanglingPointer) => {
|
||||
Err(InternError::DanglingPointer) => {
|
||||
throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(
|
||||
ecx.tcx
|
||||
.dcx()
|
||||
.emit_err(errors::DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind }),
|
||||
)));
|
||||
}
|
||||
Err(InternResult::FoundBadMutablePointer) => {
|
||||
Err(InternError::BadMutablePointer) => {
|
||||
throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(
|
||||
ecx.tcx
|
||||
.dcx()
|
||||
.emit_err(errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind }),
|
||||
)));
|
||||
}
|
||||
Err(InternError::ConstAllocNotGlobal) => {
|
||||
throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(
|
||||
ecx.tcx.dcx().emit_err(errors::ConstHeapPtrInFinal { span: ecx.tcx.span }),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
interp_ok(R::make_result(ret, ecx))
|
||||
|
|
|
|||
|
|
@ -169,13 +169,19 @@ pub type CompileTimeInterpCx<'tcx> = InterpCx<'tcx, CompileTimeMachine<'tcx>>;
|
|||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub enum MemoryKind {
|
||||
Heap,
|
||||
Heap {
|
||||
/// Indicates whether `make_global` was called on this allocation.
|
||||
/// If this is `true`, the allocation must be immutable.
|
||||
was_made_global: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl fmt::Display for MemoryKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
MemoryKind::Heap => write!(f, "heap allocation"),
|
||||
MemoryKind::Heap { was_made_global } => {
|
||||
write!(f, "heap allocation{}", if *was_made_global { " (made global)" } else { "" })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -184,7 +190,7 @@ impl interpret::MayLeak for MemoryKind {
|
|||
#[inline(always)]
|
||||
fn may_leak(self) -> bool {
|
||||
match self {
|
||||
MemoryKind::Heap => false,
|
||||
MemoryKind::Heap { was_made_global } => was_made_global,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -314,8 +320,6 @@ impl<'tcx> CompileTimeMachine<'tcx> {
|
|||
impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
|
||||
compile_time_machine!(<'tcx>);
|
||||
|
||||
type MemoryKind = MemoryKind;
|
||||
|
||||
const PANIC_ON_ALLOC_FAIL: bool = false; // will be raised as a proper error
|
||||
|
||||
#[inline(always)]
|
||||
|
|
@ -359,8 +363,8 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
|
|||
if let ty::InstanceKind::Item(def) = instance.def {
|
||||
// Execution might have wandered off into other crates, so we cannot do a stability-
|
||||
// sensitive check here. But we can at least rule out functions that are not const at
|
||||
// all. That said, we have to allow calling functions inside a trait marked with
|
||||
// #[const_trait]. These *are* const-checked!
|
||||
// all. That said, we have to allow calling functions inside a `const trait`. These
|
||||
// *are* const-checked!
|
||||
if !ecx.tcx.is_const_fn(def) || ecx.tcx.has_attr(def, sym::rustc_do_not_const_check) {
|
||||
// We certainly do *not* want to actually call the fn
|
||||
// though, so be sure we return here.
|
||||
|
|
@ -420,7 +424,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
|
|||
let ptr = ecx.allocate_ptr(
|
||||
Size::from_bytes(size),
|
||||
align,
|
||||
interpret::MemoryKind::Machine(MemoryKind::Heap),
|
||||
interpret::MemoryKind::Machine(MemoryKind::Heap { was_made_global: false }),
|
||||
AllocInit::Uninit,
|
||||
)?;
|
||||
ecx.write_pointer(ptr, dest)?;
|
||||
|
|
@ -453,10 +457,17 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
|
|||
ecx.deallocate_ptr(
|
||||
ptr,
|
||||
Some((size, align)),
|
||||
interpret::MemoryKind::Machine(MemoryKind::Heap),
|
||||
interpret::MemoryKind::Machine(MemoryKind::Heap { was_made_global: false }),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
sym::const_make_global => {
|
||||
let ptr = ecx.read_pointer(&args[0])?;
|
||||
ecx.make_const_heap_ptr_global(ptr)?;
|
||||
ecx.write_pointer(ptr, dest)?;
|
||||
}
|
||||
|
||||
// The intrinsic represents whether the value is known to the optimizer (LLVM).
|
||||
// We're not doing any optimizations here, so there is no optimizer that could know the value.
|
||||
// (We know the value here in the machine of course, but this is the runtime of that code,
|
||||
|
|
|
|||
|
|
@ -43,6 +43,14 @@ pub(crate) struct MutablePtrInFinal {
|
|||
pub kind: InternKind,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_const_heap_ptr_in_final)]
|
||||
#[note]
|
||||
pub(crate) struct ConstHeapPtrInFinal {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_unstable_in_stable_exposed)]
|
||||
pub(crate) struct UnstableInStableExposed {
|
||||
|
|
|
|||
|
|
@ -26,21 +26,18 @@ use rustc_middle::ty::layout::TyAndLayout;
|
|||
use rustc_span::def_id::LocalDefId;
|
||||
use tracing::{instrument, trace};
|
||||
|
||||
use super::{
|
||||
AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, err_ub, interp_ok,
|
||||
};
|
||||
use crate::const_eval;
|
||||
use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, interp_ok};
|
||||
use crate::const_eval::DummyMachine;
|
||||
use crate::errors::NestedStaticInThreadLocal;
|
||||
use crate::{const_eval, errors};
|
||||
|
||||
pub trait CompileTimeMachine<'tcx, T> = Machine<
|
||||
pub trait CompileTimeMachine<'tcx> = Machine<
|
||||
'tcx,
|
||||
MemoryKind = T,
|
||||
MemoryKind = const_eval::MemoryKind,
|
||||
Provenance = CtfeProvenance,
|
||||
ExtraFnVal = !,
|
||||
FrameExtra = (),
|
||||
AllocExtra = (),
|
||||
MemoryMap = FxIndexMap<AllocId, (MemoryKind<T>, Allocation)>,
|
||||
MemoryMap = FxIndexMap<AllocId, (MemoryKind<const_eval::MemoryKind>, Allocation)>,
|
||||
> + HasStaticRootDefId;
|
||||
|
||||
pub trait HasStaticRootDefId {
|
||||
|
|
@ -62,18 +59,32 @@ impl HasStaticRootDefId for const_eval::CompileTimeMachine<'_> {
|
|||
/// already mutable (as a sanity check).
|
||||
///
|
||||
/// Returns an iterator over all relocations referred to by this allocation.
|
||||
fn intern_shallow<'tcx, T, M: CompileTimeMachine<'tcx, T>>(
|
||||
fn intern_shallow<'tcx, M: CompileTimeMachine<'tcx>>(
|
||||
ecx: &mut InterpCx<'tcx, M>,
|
||||
alloc_id: AllocId,
|
||||
mutability: Mutability,
|
||||
disambiguator: Option<&mut DisambiguatorState>,
|
||||
) -> Result<impl Iterator<Item = CtfeProvenance> + 'tcx, ()> {
|
||||
) -> Result<impl Iterator<Item = CtfeProvenance> + 'tcx, InternError> {
|
||||
trace!("intern_shallow {:?}", alloc_id);
|
||||
// remove allocation
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
let Some((_kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else {
|
||||
return Err(());
|
||||
let Some((kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else {
|
||||
return Err(InternError::DanglingPointer);
|
||||
};
|
||||
|
||||
match kind {
|
||||
MemoryKind::Machine(const_eval::MemoryKind::Heap { was_made_global }) => {
|
||||
if !was_made_global {
|
||||
// Attempting to intern a `const_allocate`d pointer that was not made global via
|
||||
// `const_make_global`. We want to error here, but we have to first put the
|
||||
// allocation back into the `alloc_map` to keep things in a consistent state.
|
||||
ecx.memory.alloc_map.insert(alloc_id, (kind, alloc));
|
||||
return Err(InternError::ConstAllocNotGlobal);
|
||||
}
|
||||
}
|
||||
MemoryKind::Stack | MemoryKind::CallerLocation => {}
|
||||
}
|
||||
|
||||
// Set allocation mutability as appropriate. This is used by LLVM to put things into
|
||||
// read-only memory, and also by Miri when evaluating other globals that
|
||||
// access this one.
|
||||
|
|
@ -99,7 +110,7 @@ fn intern_shallow<'tcx, T, M: CompileTimeMachine<'tcx, T>>(
|
|||
} else {
|
||||
ecx.tcx.set_alloc_id_memory(alloc_id, alloc);
|
||||
}
|
||||
Ok(alloc.0.0.provenance().ptrs().iter().map(|&(_, prov)| prov))
|
||||
Ok(alloc.inner().provenance().ptrs().iter().map(|&(_, prov)| prov))
|
||||
}
|
||||
|
||||
/// Creates a new `DefId` and feeds all the right queries to make this `DefId`
|
||||
|
|
@ -125,7 +136,7 @@ fn intern_as_new_static<'tcx>(
|
|||
tcx.set_nested_alloc_id_static(alloc_id, feed.def_id());
|
||||
|
||||
if tcx.is_thread_local_static(static_id.into()) {
|
||||
tcx.dcx().emit_err(NestedStaticInThreadLocal { span: tcx.def_span(static_id) });
|
||||
tcx.dcx().emit_err(errors::NestedStaticInThreadLocal { span: tcx.def_span(static_id) });
|
||||
}
|
||||
|
||||
// These do not inherit the codegen attrs of the parent static allocation, since
|
||||
|
|
@ -151,9 +162,10 @@ pub enum InternKind {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum InternResult {
|
||||
FoundBadMutablePointer,
|
||||
FoundDanglingPointer,
|
||||
pub enum InternError {
|
||||
BadMutablePointer,
|
||||
DanglingPointer,
|
||||
ConstAllocNotGlobal,
|
||||
}
|
||||
|
||||
/// Intern `ret` and everything it references.
|
||||
|
|
@ -163,11 +175,11 @@ pub enum InternResult {
|
|||
///
|
||||
/// For `InternKind::Static` the root allocation will not be interned, but must be handled by the caller.
|
||||
#[instrument(level = "debug", skip(ecx))]
|
||||
pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval::MemoryKind>>(
|
||||
pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx>>(
|
||||
ecx: &mut InterpCx<'tcx, M>,
|
||||
intern_kind: InternKind,
|
||||
ret: &MPlaceTy<'tcx>,
|
||||
) -> Result<(), InternResult> {
|
||||
) -> Result<(), InternError> {
|
||||
let mut disambiguator = DisambiguatorState::new();
|
||||
|
||||
// We are interning recursively, and for mutability we are distinguishing the "root" allocation
|
||||
|
|
@ -181,7 +193,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval
|
|||
}
|
||||
InternKind::Static(Mutability::Not) => {
|
||||
(
|
||||
// Outermost allocation is mutable if `!Freeze`.
|
||||
// Outermost allocation is mutable if `!Freeze` i.e. contains interior mutable types.
|
||||
if ret.layout.ty.is_freeze(*ecx.tcx, ecx.typing_env) {
|
||||
Mutability::Not
|
||||
} else {
|
||||
|
|
@ -224,6 +236,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval
|
|||
// We want to first report "dangling" and then "mutable", so we need to delay reporting these
|
||||
// errors.
|
||||
let mut result = Ok(());
|
||||
let mut found_bad_mutable_ptr = false;
|
||||
|
||||
// Keep interning as long as there are things to intern.
|
||||
// We show errors if there are dangling pointers, or mutable pointers in immutable contexts
|
||||
|
|
@ -278,18 +291,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval
|
|||
// when there is memory there that someone might expect to be mutable, but we make it immutable.
|
||||
let dangling = !is_already_global && !ecx.memory.alloc_map.contains_key(&alloc_id);
|
||||
if !dangling {
|
||||
// Found a mutable pointer inside a const where inner allocations should be
|
||||
// immutable.
|
||||
if !ecx.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you {
|
||||
span_bug!(
|
||||
ecx.tcx.span,
|
||||
"the static const safety checks accepted a mutable pointer they should not have accepted"
|
||||
);
|
||||
}
|
||||
// Prefer dangling pointer errors over mutable pointer errors
|
||||
if result.is_ok() {
|
||||
result = Err(InternResult::FoundBadMutablePointer);
|
||||
}
|
||||
found_bad_mutable_ptr = true;
|
||||
}
|
||||
}
|
||||
if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
|
||||
|
|
@ -310,18 +312,31 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval
|
|||
just_interned.insert(alloc_id);
|
||||
match intern_shallow(ecx, alloc_id, inner_mutability, Some(&mut disambiguator)) {
|
||||
Ok(nested) => todo.extend(nested),
|
||||
Err(()) => {
|
||||
ecx.tcx.dcx().delayed_bug("found dangling pointer during const interning");
|
||||
result = Err(InternResult::FoundDanglingPointer);
|
||||
Err(err) => {
|
||||
ecx.tcx.dcx().delayed_bug("error during const interning");
|
||||
result = Err(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
if found_bad_mutable_ptr && result.is_ok() {
|
||||
// We found a mutable pointer inside a const where inner allocations should be immutable,
|
||||
// and there was no other error. This should usually never happen! However, this can happen
|
||||
// in unleash-miri mode, so report it as a normal error then.
|
||||
if ecx.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you {
|
||||
result = Err(InternError::BadMutablePointer);
|
||||
} else {
|
||||
span_bug!(
|
||||
ecx.tcx.span,
|
||||
"the static const safety checks accepted a mutable pointer they should not have accepted"
|
||||
);
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Intern `ret`. This function assumes that `ret` references no other allocation.
|
||||
#[instrument(level = "debug", skip(ecx))]
|
||||
pub fn intern_const_alloc_for_constprop<'tcx, T, M: CompileTimeMachine<'tcx, T>>(
|
||||
pub fn intern_const_alloc_for_constprop<'tcx, M: CompileTimeMachine<'tcx>>(
|
||||
ecx: &mut InterpCx<'tcx, M>,
|
||||
alloc_id: AllocId,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
|
|
@ -330,10 +345,7 @@ pub fn intern_const_alloc_for_constprop<'tcx, T, M: CompileTimeMachine<'tcx, T>>
|
|||
return interp_ok(());
|
||||
}
|
||||
// Move allocation to `tcx`.
|
||||
if let Some(_) = intern_shallow(ecx, alloc_id, Mutability::Not, None)
|
||||
.map_err(|()| err_ub!(DeadLocal))?
|
||||
.next()
|
||||
{
|
||||
if let Some(_) = intern_shallow(ecx, alloc_id, Mutability::Not, None).unwrap().next() {
|
||||
// We are not doing recursive interning, so we don't currently support provenance.
|
||||
// (If this assertion ever triggers, we should just implement a
|
||||
// proper recursive interning loop -- or just call `intern_const_alloc_recursive`.
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@
|
|||
|
||||
use std::assert_matches::assert_matches;
|
||||
|
||||
use rustc_abi::{FieldIdx, Size};
|
||||
use rustc_abi::{FieldIdx, HasDataLayout, Size};
|
||||
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
|
||||
use rustc_middle::mir::interpret::{read_target_uint, write_target_uint};
|
||||
use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
|
||||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_middle::ty::{Ty, TyCtxt};
|
||||
|
|
@ -30,7 +31,7 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAll
|
|||
}
|
||||
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
/// Generates a value of `TypeId` for `ty` in-place.
|
||||
pub(crate) fn write_type_id(
|
||||
fn write_type_id(
|
||||
&mut self,
|
||||
ty: Ty<'tcx>,
|
||||
dest: &PlaceTy<'tcx, M::Provenance>,
|
||||
|
|
@ -48,8 +49,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// Here we rely on `TypeId` being a newtype around an array of pointers, so we
|
||||
// first project to its only field and then the array elements.
|
||||
let alloc_id = tcx.reserve_and_set_type_id_alloc(ty);
|
||||
let first = self.project_field(dest, FieldIdx::ZERO)?;
|
||||
let mut elem_iter = self.project_array_fields(&first)?;
|
||||
let arr = self.project_field(dest, FieldIdx::ZERO)?;
|
||||
let mut elem_iter = self.project_array_fields(&arr)?;
|
||||
while let Some((_, elem)) = elem_iter.next(self)? {
|
||||
// Decorate this part of the hash with provenance; leave the integer part unchanged.
|
||||
let hash_fragment = self.read_scalar(&elem)?.to_target_usize(&tcx)?;
|
||||
|
|
@ -61,6 +62,52 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
interp_ok(())
|
||||
}
|
||||
|
||||
/// Read a value of type `TypeId`, returning the type it represents.
|
||||
pub(crate) fn read_type_id(
|
||||
&self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, Ty<'tcx>> {
|
||||
// `TypeId` is a newtype around an array of pointers. All pointers must have the same
|
||||
// provenance, and that provenance represents the type.
|
||||
let ptr_size = self.pointer_size().bytes_usize();
|
||||
let arr = self.project_field(op, FieldIdx::ZERO)?;
|
||||
|
||||
let mut ty_and_hash = None;
|
||||
let mut elem_iter = self.project_array_fields(&arr)?;
|
||||
while let Some((idx, elem)) = elem_iter.next(self)? {
|
||||
let elem = self.read_pointer(&elem)?;
|
||||
let (elem_ty, elem_hash) = self.get_ptr_type_id(elem)?;
|
||||
// If this is the first element, remember the type and its hash.
|
||||
// If this is not the first element, ensure it is consistent with the previous ones.
|
||||
let full_hash = match ty_and_hash {
|
||||
None => {
|
||||
let hash = self.tcx.type_id_hash(elem_ty).as_u128();
|
||||
let mut hash_bytes = [0u8; 16];
|
||||
write_target_uint(self.data_layout().endian, &mut hash_bytes, hash).unwrap();
|
||||
ty_and_hash = Some((elem_ty, hash_bytes));
|
||||
hash_bytes
|
||||
}
|
||||
Some((ty, hash_bytes)) => {
|
||||
if ty != elem_ty {
|
||||
throw_ub_format!(
|
||||
"invalid `TypeId` value: not all bytes carry the same type id metadata"
|
||||
);
|
||||
}
|
||||
hash_bytes
|
||||
}
|
||||
};
|
||||
// Ensure the elem_hash matches the corresponding part of the full hash.
|
||||
let hash_frag = &full_hash[(idx as usize) * ptr_size..][..ptr_size];
|
||||
if read_target_uint(self.data_layout().endian, hash_frag).unwrap() != elem_hash.into() {
|
||||
throw_ub_format!(
|
||||
"invalid `TypeId` value: the hash does not match the type id metadata"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interp_ok(ty_and_hash.unwrap().0)
|
||||
}
|
||||
|
||||
/// Returns `true` if emulation happened.
|
||||
/// Here we implement the intrinsics that are common to all Miri instances; individual machines can add their own
|
||||
/// intrinsic handling.
|
||||
|
|
@ -97,47 +144,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
self.write_type_id(tp_ty, dest)?;
|
||||
}
|
||||
sym::type_id_eq => {
|
||||
// Both operands are `TypeId`, which is a newtype around an array of pointers.
|
||||
// Project until we have the array elements.
|
||||
let a_fields = self.project_field(&args[0], FieldIdx::ZERO)?;
|
||||
let b_fields = self.project_field(&args[1], FieldIdx::ZERO)?;
|
||||
|
||||
let mut a_fields = self.project_array_fields(&a_fields)?;
|
||||
let mut b_fields = self.project_array_fields(&b_fields)?;
|
||||
|
||||
let mut provenance_a = None;
|
||||
let mut provenance_b = None;
|
||||
let mut provenance_matches = true;
|
||||
|
||||
while let Some((i, a)) = a_fields.next(self)? {
|
||||
let (_, b) = b_fields.next(self)?.unwrap();
|
||||
|
||||
let a = self.deref_pointer(&a)?;
|
||||
let (a, offset_a) = self.get_ptr_type_id(a.ptr())?;
|
||||
|
||||
let b = self.deref_pointer(&b)?;
|
||||
let (b, offset_b) = self.get_ptr_type_id(b.ptr())?;
|
||||
|
||||
if *provenance_a.get_or_insert(a) != a {
|
||||
throw_ub_format!(
|
||||
"type_id_eq: the first TypeId argument is invalid, the provenance of chunk {i} does not match the first chunk's"
|
||||
)
|
||||
}
|
||||
if *provenance_b.get_or_insert(b) != b {
|
||||
throw_ub_format!(
|
||||
"type_id_eq: the second TypeId argument is invalid, the provenance of chunk {i} does not match the first chunk's"
|
||||
)
|
||||
}
|
||||
provenance_matches &= a == b;
|
||||
|
||||
if offset_a != offset_b && provenance_matches {
|
||||
throw_ub_format!(
|
||||
"type_id_eq: one of the TypeId arguments is invalid, chunk {i} of the hash does not match the type it represents"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
self.write_scalar(Scalar::from_bool(provenance_matches), dest)?;
|
||||
let a_ty = self.read_type_id(&args[0])?;
|
||||
let b_ty = self.read_type_id(&args[1])?;
|
||||
self.write_scalar(Scalar::from_bool(a_ty == b_ty), dest)?;
|
||||
}
|
||||
sym::variant_count => {
|
||||
let tp_ty = instance.args.type_at(0);
|
||||
|
|
|
|||
|
|
@ -649,6 +649,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
|
|||
|
||||
type ExtraFnVal = !;
|
||||
|
||||
type MemoryKind = $crate::const_eval::MemoryKind;
|
||||
type MemoryMap =
|
||||
rustc_data_structures::fx::FxIndexMap<AllocId, (MemoryKind<Self::MemoryKind>, Allocation)>;
|
||||
const GLOBAL_KIND: Option<Self::MemoryKind> = None; // no copying of globals from `tcx` to machine memory
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ use super::{
|
|||
Misalignment, Pointer, PointerArithmetic, Provenance, Scalar, alloc_range, err_ub,
|
||||
err_ub_custom, interp_ok, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format,
|
||||
};
|
||||
use crate::const_eval::ConstEvalErrKind;
|
||||
use crate::fluent_generated as fluent;
|
||||
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
|
|
@ -311,6 +312,51 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
interp_ok(new_ptr)
|
||||
}
|
||||
|
||||
/// Mark the `const_allocate`d allocation `ptr` points to as immutable so we can intern it.
|
||||
pub fn make_const_heap_ptr_global(
|
||||
&mut self,
|
||||
ptr: Pointer<Option<CtfeProvenance>>,
|
||||
) -> InterpResult<'tcx>
|
||||
where
|
||||
M: Machine<'tcx, MemoryKind = crate::const_eval::MemoryKind, Provenance = CtfeProvenance>,
|
||||
{
|
||||
let (alloc_id, offset, _) = self.ptr_get_alloc_id(ptr, 0)?;
|
||||
if offset.bytes() != 0 {
|
||||
return Err(ConstEvalErrKind::ConstMakeGlobalWithOffset(ptr)).into();
|
||||
}
|
||||
|
||||
if matches!(self.tcx.try_get_global_alloc(alloc_id), Some(_)) {
|
||||
// This points to something outside the current interpreter.
|
||||
return Err(ConstEvalErrKind::ConstMakeGlobalPtrIsNonHeap(ptr)).into();
|
||||
}
|
||||
|
||||
// If we can't find it in `alloc_map` it must be dangling (because we don't use
|
||||
// `extra_fn_ptr_map` in const-eval).
|
||||
let (kind, alloc) = self
|
||||
.memory
|
||||
.alloc_map
|
||||
.get_mut_or(alloc_id, || Err(ConstEvalErrKind::ConstMakeGlobalWithDanglingPtr(ptr)))?;
|
||||
|
||||
// Ensure this is actually a *heap* allocation, and record it as made-global.
|
||||
match kind {
|
||||
MemoryKind::Stack | MemoryKind::CallerLocation => {
|
||||
return Err(ConstEvalErrKind::ConstMakeGlobalPtrIsNonHeap(ptr)).into();
|
||||
}
|
||||
MemoryKind::Machine(crate::const_eval::MemoryKind::Heap { was_made_global }) => {
|
||||
if *was_made_global {
|
||||
return Err(ConstEvalErrKind::ConstMakeGlobalPtrAlreadyMadeGlobal(alloc_id))
|
||||
.into();
|
||||
}
|
||||
*was_made_global = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent further mutation, this is now an immutable global.
|
||||
alloc.mutability = Mutability::Not;
|
||||
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
pub fn deallocate_ptr(
|
||||
&mut self,
|
||||
|
|
@ -951,12 +997,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
pub fn get_ptr_type_id(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
) -> InterpResult<'tcx, (Ty<'tcx>, Size)> {
|
||||
) -> InterpResult<'tcx, (Ty<'tcx>, u64)> {
|
||||
let (alloc_id, offset, _meta) = self.ptr_get_alloc_id(ptr, 0)?;
|
||||
let GlobalAlloc::TypeId { ty } = self.tcx.global_alloc(alloc_id) else {
|
||||
throw_ub_format!("type_id_eq: `TypeId` provenance is not a type id")
|
||||
throw_ub_format!("invalid `TypeId` value: not all bytes carry type id metadata")
|
||||
};
|
||||
interp_ok((ty, offset))
|
||||
interp_ok((ty, offset.bytes()))
|
||||
}
|
||||
|
||||
pub fn get_ptr_fn(
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ pub use self::call::FnArg;
|
|||
pub use self::eval_context::{InterpCx, format_interp_error};
|
||||
use self::eval_context::{from_known_layout, mir_assign_valid_types};
|
||||
pub use self::intern::{
|
||||
HasStaticRootDefId, InternKind, InternResult, intern_const_alloc_for_constprop,
|
||||
HasStaticRootDefId, InternError, InternKind, intern_const_alloc_for_constprop,
|
||||
intern_const_alloc_recursive,
|
||||
};
|
||||
pub use self::machine::{AllocMap, Machine, MayLeak, ReturnAction, compile_time_machine};
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt};
|
|||
use rustc_middle::{bug, mir};
|
||||
use rustc_mir_dataflow::impls::always_storage_live_locals;
|
||||
use rustc_span::Span;
|
||||
use tracing::field::Empty;
|
||||
use tracing::{info_span, instrument, trace};
|
||||
|
||||
use super::{
|
||||
|
|
@ -396,7 +397,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// Finish things up.
|
||||
M::after_stack_push(self)?;
|
||||
self.frame_mut().loc = Left(mir::Location::START);
|
||||
let span = info_span!("frame", "{}", instance);
|
||||
// `tracing_separate_thread` is used to instruct the chrome_tracing [tracing::Layer] in Miri
|
||||
// to put the "frame" span on a separate trace thread/line than other spans, to make the
|
||||
// visualization in https://ui.perfetto.dev easier to interpret. It is set to a value of
|
||||
// [tracing::field::Empty] so that other tracing layers (e.g. the logger) will ignore it.
|
||||
let span = info_span!("frame", tracing_separate_thread = Empty, "{}", instance);
|
||||
self.frame_mut().tracing_span.enter(span);
|
||||
|
||||
interp_ok(())
|
||||
|
|
|
|||
|
|
@ -558,7 +558,15 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
|||
{
|
||||
// Everything should be already interned.
|
||||
let Some(global_alloc) = self.ecx.tcx.try_get_global_alloc(alloc_id) else {
|
||||
assert!(self.ecx.memory.alloc_map.get(alloc_id).is_none());
|
||||
if self.ecx.memory.alloc_map.contains_key(&alloc_id) {
|
||||
// This can happen when interning didn't complete due to, e.g.
|
||||
// missing `make_global`. This must mean other errors are already
|
||||
// being reported.
|
||||
self.ecx.tcx.dcx().delayed_bug(
|
||||
"interning did not complete, there should be an error",
|
||||
);
|
||||
return interp_ok(());
|
||||
}
|
||||
// We can't have *any* references to non-existing allocations in const-eval
|
||||
// as the rest of rustc isn't happy with them... so we throw an error, even
|
||||
// though for zero-sized references this isn't really UB.
|
||||
|
|
|
|||
|
|
@ -52,9 +52,9 @@ fn check_validity_requirement_strict<'tcx>(
|
|||
|
||||
let mut cx = InterpCx::new(cx.tcx(), DUMMY_SP, cx.typing_env, machine);
|
||||
|
||||
let allocated = cx
|
||||
.allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap))
|
||||
.expect("OOM: failed to allocate for uninit check");
|
||||
// It doesn't really matter which `MemoryKind` we use here, `Stack` is the least wrong.
|
||||
let allocated =
|
||||
cx.allocate(ty, MemoryKind::Stack).expect("OOM: failed to allocate for uninit check");
|
||||
|
||||
if kind == ValidityRequirement::Zero {
|
||||
cx.write_bytes_ptr(
|
||||
|
|
@ -184,9 +184,10 @@ pub(crate) fn validate_scalar_in_layout<'tcx>(
|
|||
let Ok(layout) = cx.layout_of(ty) else {
|
||||
bug!("could not compute layout of {scalar:?}:{ty:?}")
|
||||
};
|
||||
let allocated = cx
|
||||
.allocate(layout, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap))
|
||||
.expect("OOM: failed to allocate for uninit check");
|
||||
|
||||
// It doesn't really matter which `MemoryKind` we use here, `Stack` is the least wrong.
|
||||
let allocated =
|
||||
cx.allocate(layout, MemoryKind::Stack).expect("OOM: failed to allocate for uninit check");
|
||||
|
||||
cx.write_scalar(scalar, &allocated).unwrap();
|
||||
|
||||
|
|
|
|||
|
|
@ -1062,7 +1062,7 @@ pub trait ResolverExpand {
|
|||
fn next_node_id(&mut self) -> NodeId;
|
||||
fn invocation_parent(&self, id: LocalExpnId) -> LocalDefId;
|
||||
|
||||
fn resolve_dollar_crates(&mut self);
|
||||
fn resolve_dollar_crates(&self);
|
||||
fn visit_ast_fragment_with_placeholders(
|
||||
&mut self,
|
||||
expn_id: LocalExpnId,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use rustc_ast::{
|
|||
};
|
||||
use rustc_attr_parsing as attr;
|
||||
use rustc_attr_parsing::{
|
||||
AttributeParser, CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr,
|
||||
AttributeParser, CFG_TEMPLATE, EvalConfigResult, ShouldEmit, eval_config_entry, parse_cfg_attr,
|
||||
};
|
||||
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
|
||||
use rustc_feature::{
|
||||
|
|
@ -167,7 +167,9 @@ pub fn pre_configure_attrs(sess: &Session, attrs: &[Attribute]) -> ast::AttrVec
|
|||
.flat_map(|attr| strip_unconfigured.process_cfg_attr(attr))
|
||||
.take_while(|attr| {
|
||||
!is_cfg(attr)
|
||||
|| strip_unconfigured.cfg_true(attr, strip_unconfigured.lint_node_id).as_bool()
|
||||
|| strip_unconfigured
|
||||
.cfg_true(attr, strip_unconfigured.lint_node_id, ShouldEmit::Nothing)
|
||||
.as_bool()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
|
@ -401,10 +403,18 @@ impl<'a> StripUnconfigured<'a> {
|
|||
|
||||
/// Determines if a node with the given attributes should be included in this configuration.
|
||||
fn in_cfg(&self, attrs: &[Attribute]) -> bool {
|
||||
attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr, self.lint_node_id).as_bool())
|
||||
attrs.iter().all(|attr| {
|
||||
!is_cfg(attr)
|
||||
|| self.cfg_true(attr, self.lint_node_id, ShouldEmit::ErrorsAndLints).as_bool()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn cfg_true(&self, attr: &Attribute, node: NodeId) -> EvalConfigResult {
|
||||
pub(crate) fn cfg_true(
|
||||
&self,
|
||||
attr: &Attribute,
|
||||
node: NodeId,
|
||||
emit_errors: ShouldEmit,
|
||||
) -> EvalConfigResult {
|
||||
// We need to run this to do basic validation of the attribute, such as that lits are valid, etc
|
||||
// FIXME(jdonszelmann) this should not be necessary in the future
|
||||
match validate_attr::parse_meta(&self.sess.psess, attr) {
|
||||
|
|
@ -428,7 +438,7 @@ impl<'a> StripUnconfigured<'a> {
|
|||
attr.span,
|
||||
node,
|
||||
self.features,
|
||||
true,
|
||||
emit_errors,
|
||||
parse_cfg_attr,
|
||||
&CFG_TEMPLATE,
|
||||
) else {
|
||||
|
|
@ -436,7 +446,7 @@ impl<'a> StripUnconfigured<'a> {
|
|||
return EvalConfigResult::True;
|
||||
};
|
||||
|
||||
eval_config_entry(self.sess, &cfg, self.lint_node_id, self.features)
|
||||
eval_config_entry(self.sess, &cfg, self.lint_node_id, self.features, emit_errors)
|
||||
}
|
||||
|
||||
/// If attributes are not allowed on expressions, emit an error for `attr`
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use rustc_ast::{
|
|||
MetaItemKind, ModKind, NodeId, PatKind, StmtKind, TyKind, token,
|
||||
};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_attr_parsing::EvalConfigResult;
|
||||
use rustc_attr_parsing::{EvalConfigResult, ShouldEmit};
|
||||
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
|
||||
use rustc_errors::PResult;
|
||||
use rustc_feature::Features;
|
||||
|
|
@ -2171,7 +2171,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
|||
attr: ast::Attribute,
|
||||
pos: usize,
|
||||
) -> EvalConfigResult {
|
||||
let res = self.cfg().cfg_true(&attr, node.node_id());
|
||||
let res = self.cfg().cfg_true(&attr, node.node_id(), ShouldEmit::ErrorsAndLints);
|
||||
if res.as_bool() {
|
||||
// A trace attribute left in AST in place of the original `cfg` attribute.
|
||||
// It can later be used by lints or other diagnostics.
|
||||
|
|
|
|||
|
|
@ -614,6 +614,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
),
|
||||
|
||||
// RFC 2632
|
||||
// FIXME(const_trait_impl) remove this
|
||||
gated!(
|
||||
const_trait, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, const_trait_impl,
|
||||
"`const_trait` is a temporary placeholder for marking a trait that is suitable for `const` \
|
||||
|
|
@ -683,6 +684,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk,
|
||||
EncodeCrossCrate::Yes
|
||||
),
|
||||
ungated!(
|
||||
unstable_feature_bound, Normal, template!(Word, List: "feat1, feat2, ..."),
|
||||
DuplicatesOk, EncodeCrossCrate::No,
|
||||
),
|
||||
ungated!(
|
||||
rustc_const_unstable, Normal, template!(List: r#"feature = "name""#),
|
||||
DuplicatesOk, EncodeCrossCrate::Yes
|
||||
|
|
|
|||
|
|
@ -140,7 +140,9 @@ impl DefKey {
|
|||
pub(crate) fn compute_stable_hash(&self, parent: DefPathHash) -> DefPathHash {
|
||||
let mut hasher = StableHasher::new();
|
||||
|
||||
parent.hash(&mut hasher);
|
||||
// The new path is in the same crate as `parent`, and will contain the stable_crate_id.
|
||||
// Therefore, we only need to include information of the parent's local hash.
|
||||
parent.local_hash().hash(&mut hasher);
|
||||
|
||||
let DisambiguatedDefPathData { ref data, disambiguator } = self.disambiguated_data;
|
||||
|
||||
|
|
@ -181,32 +183,26 @@ pub struct DisambiguatedDefPathData {
|
|||
}
|
||||
|
||||
impl DisambiguatedDefPathData {
|
||||
pub fn fmt_maybe_verbose(&self, writer: &mut impl Write, verbose: bool) -> fmt::Result {
|
||||
pub fn as_sym(&self, verbose: bool) -> Symbol {
|
||||
match self.data.name() {
|
||||
DefPathDataName::Named(name) => {
|
||||
if verbose && self.disambiguator != 0 {
|
||||
write!(writer, "{}#{}", name, self.disambiguator)
|
||||
Symbol::intern(&format!("{}#{}", name, self.disambiguator))
|
||||
} else {
|
||||
writer.write_str(name.as_str())
|
||||
name
|
||||
}
|
||||
}
|
||||
DefPathDataName::Anon { namespace } => {
|
||||
if let DefPathData::AnonAssocTy(method) = self.data {
|
||||
write!(writer, "{}::{{{}#{}}}", method, namespace, self.disambiguator)
|
||||
Symbol::intern(&format!("{}::{{{}#{}}}", method, namespace, self.disambiguator))
|
||||
} else {
|
||||
write!(writer, "{{{}#{}}}", namespace, self.disambiguator)
|
||||
Symbol::intern(&format!("{{{}#{}}}", namespace, self.disambiguator))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DisambiguatedDefPathData {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.fmt_maybe_verbose(f, true)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Encodable, Decodable)]
|
||||
pub struct DefPath {
|
||||
/// The path leading from the crate root to the item.
|
||||
|
|
@ -250,7 +246,7 @@ impl DefPath {
|
|||
let mut s = String::with_capacity(self.data.len() * 16);
|
||||
|
||||
for component in &self.data {
|
||||
write!(s, "::{component}").unwrap();
|
||||
write!(s, "::{}", component.as_sym(true)).unwrap();
|
||||
}
|
||||
|
||||
s
|
||||
|
|
@ -266,7 +262,7 @@ impl DefPath {
|
|||
for component in &self.data {
|
||||
s.extend(opt_delimiter);
|
||||
opt_delimiter = Some('-');
|
||||
write!(s, "{component}").unwrap();
|
||||
write!(s, "{}", component.as_sym(true)).unwrap();
|
||||
}
|
||||
|
||||
s
|
||||
|
|
@ -361,8 +357,16 @@ impl Definitions {
|
|||
},
|
||||
};
|
||||
|
||||
let parent_hash = DefPathHash::new(stable_crate_id, Hash64::ZERO);
|
||||
let def_path_hash = key.compute_stable_hash(parent_hash);
|
||||
// We want *both* halves of a DefPathHash to depend on the crate-id of the defining crate.
|
||||
// The crate-id can be more easily changed than the DefPath of an item, so, in the case of
|
||||
// a crate-local DefPathHash collision, the user can simply "roll the dice again" for all
|
||||
// DefPathHashes in the crate by changing the crate disambiguator (e.g. via bumping the
|
||||
// crate's version number).
|
||||
//
|
||||
// Children paths will only hash the local portion, and still inherit the change to the
|
||||
// root hash.
|
||||
let def_path_hash =
|
||||
DefPathHash::new(stable_crate_id, Hash64::new(stable_crate_id.as_u64()));
|
||||
|
||||
// Create the root definition.
|
||||
let mut table = DefPathTable::new(stable_crate_id);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use rustc_ast::token::CommentKind;
|
|||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_ast::{
|
||||
self as ast, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitIntType,
|
||||
LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind,
|
||||
LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind, join_path_idents,
|
||||
};
|
||||
pub use rustc_ast::{
|
||||
AssignOp, AssignOpKind, AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind,
|
||||
|
|
@ -1168,7 +1168,7 @@ impl AttrPath {
|
|||
|
||||
impl fmt::Display for AttrPath {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.segments.iter().map(|i| i.to_string()).collect::<Vec<_>>().join("::"))
|
||||
write!(f, "{}", join_path_idents(&self.segments))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4163,6 +4163,7 @@ impl<'hir> Item<'hir> {
|
|||
|
||||
expect_trait,
|
||||
(
|
||||
Constness,
|
||||
IsAuto,
|
||||
Safety,
|
||||
Ident,
|
||||
|
|
@ -4170,8 +4171,8 @@ impl<'hir> Item<'hir> {
|
|||
GenericBounds<'hir>,
|
||||
&'hir [TraitItemId]
|
||||
),
|
||||
ItemKind::Trait(is_auto, safety, ident, generics, bounds, items),
|
||||
(*is_auto, *safety, *ident, generics, bounds, items);
|
||||
ItemKind::Trait(constness, is_auto, safety, ident, generics, bounds, items),
|
||||
(*constness, *is_auto, *safety, *ident, generics, bounds, items);
|
||||
|
||||
expect_trait_alias, (Ident, &'hir Generics<'hir>, GenericBounds<'hir>),
|
||||
ItemKind::TraitAlias(ident, generics, bounds), (*ident, generics, bounds);
|
||||
|
|
@ -4341,7 +4342,15 @@ pub enum ItemKind<'hir> {
|
|||
/// A union definition, e.g., `union Foo<A, B> {x: A, y: B}`.
|
||||
Union(Ident, &'hir Generics<'hir>, VariantData<'hir>),
|
||||
/// A trait definition.
|
||||
Trait(IsAuto, Safety, Ident, &'hir Generics<'hir>, GenericBounds<'hir>, &'hir [TraitItemId]),
|
||||
Trait(
|
||||
Constness,
|
||||
IsAuto,
|
||||
Safety,
|
||||
Ident,
|
||||
&'hir Generics<'hir>,
|
||||
GenericBounds<'hir>,
|
||||
&'hir [TraitItemId],
|
||||
),
|
||||
/// A trait alias.
|
||||
TraitAlias(Ident, &'hir Generics<'hir>, GenericBounds<'hir>),
|
||||
|
||||
|
|
@ -4385,7 +4394,7 @@ impl ItemKind<'_> {
|
|||
| ItemKind::Enum(ident, ..)
|
||||
| ItemKind::Struct(ident, ..)
|
||||
| ItemKind::Union(ident, ..)
|
||||
| ItemKind::Trait(_, _, ident, ..)
|
||||
| ItemKind::Trait(_, _, _, ident, ..)
|
||||
| ItemKind::TraitAlias(ident, ..) => Some(ident),
|
||||
|
||||
ItemKind::Use(_, UseKind::Glob | UseKind::ListStem)
|
||||
|
|
@ -4403,7 +4412,7 @@ impl ItemKind<'_> {
|
|||
| ItemKind::Enum(_, generics, _)
|
||||
| ItemKind::Struct(_, generics, _)
|
||||
| ItemKind::Union(_, generics, _)
|
||||
| ItemKind::Trait(_, _, _, generics, _, _)
|
||||
| ItemKind::Trait(_, _, _, _, generics, _, _)
|
||||
| ItemKind::TraitAlias(_, generics, _)
|
||||
| ItemKind::Impl(Impl { generics, .. }) => generics,
|
||||
_ => return None,
|
||||
|
|
|
|||
|
|
@ -618,7 +618,15 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V::
|
|||
try_visit!(visitor.visit_generics(generics));
|
||||
try_visit!(visitor.visit_variant_data(struct_definition));
|
||||
}
|
||||
ItemKind::Trait(_is_auto, _safety, ident, ref generics, bounds, trait_item_refs) => {
|
||||
ItemKind::Trait(
|
||||
_constness,
|
||||
_is_auto,
|
||||
_safety,
|
||||
ident,
|
||||
ref generics,
|
||||
bounds,
|
||||
trait_item_refs,
|
||||
) => {
|
||||
try_visit!(visitor.visit_ident(ident));
|
||||
try_visit!(visitor.visit_generics(generics));
|
||||
walk_list!(visitor, visit_param_bound, bounds);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ fn def_path_hash_depends_on_crate_id() {
|
|||
assert_ne!(h0.local_hash(), h1.local_hash());
|
||||
|
||||
fn mk_test_hash(stable_crate_id: StableCrateId) -> DefPathHash {
|
||||
let parent_hash = DefPathHash::new(stable_crate_id, Hash64::ZERO);
|
||||
let parent_hash =
|
||||
DefPathHash::new(stable_crate_id, Hash64::new(stable_crate_id.as_u64()));
|
||||
|
||||
let key = DefKey {
|
||||
parent: None,
|
||||
|
|
|
|||
|
|
@ -117,15 +117,15 @@ hir_analysis_coercion_between_struct_same_note = expected coercion between the s
|
|||
|
||||
hir_analysis_coercion_between_struct_single_note = expected a single field to be coerced, none found
|
||||
|
||||
hir_analysis_const_bound_for_non_const_trait = `{$modifier}` can only be applied to `#[const_trait]` traits
|
||||
hir_analysis_const_bound_for_non_const_trait = `{$modifier}` can only be applied to `const` traits
|
||||
.label = can't be applied to `{$trait_name}`
|
||||
.note = `{$trait_name}` can't be used with `{$modifier}` because it isn't annotated with `#[const_trait]`
|
||||
.suggestion = {$suggestion_pre}mark `{$trait_name}` as `#[const_trait]` to allow it to have `const` implementations
|
||||
.note = `{$trait_name}` can't be used with `{$modifier}` because it isn't `const`
|
||||
.suggestion = {$suggestion_pre}mark `{$trait_name}` as `const` to allow it to have `const` implementations
|
||||
|
||||
hir_analysis_const_impl_for_non_const_trait = const `impl` for trait `{$trait_name}` which is not marked with `#[const_trait]`
|
||||
hir_analysis_const_impl_for_non_const_trait = const `impl` for trait `{$trait_name}` which is not `const`
|
||||
.label = this trait is not `const`
|
||||
.suggestion = {$suggestion_pre}mark `{$trait_name}` as `#[const_trait]` to allow it to have `const` implementations
|
||||
.note = marking a trait with `#[const_trait]` ensures all default method bodies are `const`
|
||||
.suggestion = {$suggestion_pre}mark `{$trait_name}` as `const` to allow it to have `const` implementations
|
||||
.note = marking a trait with `const` ensures all default method bodies are `const`
|
||||
.adding = adding a non-const method body in the future would be a breaking change
|
||||
|
||||
hir_analysis_const_param_ty_impl_on_non_adt =
|
||||
|
|
|
|||
|
|
@ -889,8 +889,6 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
|
|||
tcx.ensure_ok().predicates_of(def_id);
|
||||
tcx.ensure_ok().explicit_item_bounds(def_id);
|
||||
tcx.ensure_ok().explicit_item_self_bounds(def_id);
|
||||
tcx.ensure_ok().item_bounds(def_id);
|
||||
tcx.ensure_ok().item_self_bounds(def_id);
|
||||
if tcx.is_conditionally_const(def_id) {
|
||||
tcx.ensure_ok().explicit_implied_const_bounds(def_id);
|
||||
tcx.ensure_ok().const_conditions(def_id);
|
||||
|
|
@ -1044,8 +1042,12 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
|
|||
let has_type = match assoc_item.container {
|
||||
ty::AssocItemContainer::Impl => true,
|
||||
ty::AssocItemContainer::Trait => {
|
||||
tcx.ensure_ok().item_bounds(def_id);
|
||||
tcx.ensure_ok().item_self_bounds(def_id);
|
||||
tcx.ensure_ok().explicit_item_bounds(def_id);
|
||||
tcx.ensure_ok().explicit_item_self_bounds(def_id);
|
||||
if tcx.is_conditionally_const(def_id) {
|
||||
tcx.ensure_ok().explicit_implied_const_bounds(def_id);
|
||||
tcx.ensure_ok().const_conditions(def_id);
|
||||
}
|
||||
res = res.and(check_trait_item(tcx, def_id));
|
||||
assoc_item.defaultness(tcx).has_value()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -422,6 +422,9 @@ pub(crate) fn check_intrinsic_type(
|
|||
vec![Ty::new_mut_ptr(tcx, tcx.types.u8), tcx.types.usize, tcx.types.usize],
|
||||
tcx.types.unit,
|
||||
),
|
||||
sym::const_make_global => {
|
||||
(0, 0, vec![Ty::new_mut_ptr(tcx, tcx.types.u8)], Ty::new_imm_ptr(tcx, tcx.types.u8))
|
||||
}
|
||||
|
||||
sym::ptr_offset_from => (
|
||||
1,
|
||||
|
|
|
|||
|
|
@ -2212,12 +2212,16 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
|
|||
let implied_obligations = traits::elaborate(tcx, predicates_with_span);
|
||||
|
||||
for (pred, obligation_span) in implied_obligations {
|
||||
// We lower empty bounds like `Vec<dyn Copy>:` as
|
||||
// `WellFormed(Vec<dyn Copy>)`, which will later get checked by
|
||||
// regular WF checking
|
||||
if let ty::ClauseKind::WellFormed(..) = pred.kind().skip_binder() {
|
||||
continue;
|
||||
match pred.kind().skip_binder() {
|
||||
// We lower empty bounds like `Vec<dyn Copy>:` as
|
||||
// `WellFormed(Vec<dyn Copy>)`, which will later get checked by
|
||||
// regular WF checking
|
||||
ty::ClauseKind::WellFormed(..)
|
||||
// Unstable feature goals cannot be proven in an empty environment so skip them
|
||||
| ty::ClauseKind::UnstableFeature(..) => continue,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Match the existing behavior.
|
||||
if pred.is_global() && !pred.has_type_flags(TypeFlags::HAS_BINDER_VARS) {
|
||||
let pred = self.normalize(span, None, pred);
|
||||
|
|
|
|||
|
|
@ -844,15 +844,20 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
|
|||
fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
|
||||
let item = tcx.hir_expect_item(def_id);
|
||||
|
||||
let (is_alias, is_auto, safety) = match item.kind {
|
||||
hir::ItemKind::Trait(is_auto, safety, ..) => (false, is_auto == hir::IsAuto::Yes, safety),
|
||||
hir::ItemKind::TraitAlias(..) => (true, false, hir::Safety::Safe),
|
||||
let (constness, is_alias, is_auto, safety) = match item.kind {
|
||||
hir::ItemKind::Trait(constness, is_auto, safety, ..) => {
|
||||
(constness, false, is_auto == hir::IsAuto::Yes, safety)
|
||||
}
|
||||
hir::ItemKind::TraitAlias(..) => (hir::Constness::NotConst, true, false, hir::Safety::Safe),
|
||||
_ => span_bug!(item.span, "trait_def_of_item invoked on non-trait"),
|
||||
};
|
||||
|
||||
let attrs = tcx.get_all_attrs(def_id);
|
||||
// Only regular traits can be const.
|
||||
let constness = if !is_alias && find_attr!(attrs, AttributeKind::ConstTrait(_)) {
|
||||
// FIXME(const_trait_impl): remove this
|
||||
let constness = if constness == hir::Constness::Const
|
||||
|| !is_alias && find_attr!(attrs, AttributeKind::ConstTrait(_))
|
||||
{
|
||||
hir::Constness::Const
|
||||
} else {
|
||||
hir::Constness::NotConst
|
||||
|
|
|
|||
|
|
@ -38,12 +38,13 @@ fn associated_type_bounds<'tcx>(
|
|||
let icx = ItemCtxt::new(tcx, assoc_item_def_id);
|
||||
let mut bounds = Vec::new();
|
||||
icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter);
|
||||
// Implicit bounds are added to associated types unless a `?Trait` bound is found
|
||||
|
||||
match filter {
|
||||
PredicateFilter::All
|
||||
| PredicateFilter::SelfOnly
|
||||
| PredicateFilter::SelfTraitThatDefines(_)
|
||||
| PredicateFilter::SelfAndAssociatedTypeBounds => {
|
||||
// Implicit bounds are added to associated types unless a `?Trait` bound is found.
|
||||
icx.lowerer().add_sizedness_bounds(
|
||||
&mut bounds,
|
||||
item_ty,
|
||||
|
|
@ -53,37 +54,48 @@ fn associated_type_bounds<'tcx>(
|
|||
span,
|
||||
);
|
||||
icx.lowerer().add_default_traits(&mut bounds, item_ty, hir_bounds, None, span);
|
||||
|
||||
// Also collect `where Self::Assoc: Trait` from the parent trait's where clauses.
|
||||
let trait_def_id = tcx.local_parent(assoc_item_def_id);
|
||||
let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id);
|
||||
|
||||
let item_trait_ref =
|
||||
ty::TraitRef::identity(tcx, tcx.parent(assoc_item_def_id.to_def_id()));
|
||||
bounds.extend(trait_predicates.predicates.iter().copied().filter_map(
|
||||
|(clause, span)| {
|
||||
remap_gat_vars_and_recurse_into_nested_projections(
|
||||
tcx,
|
||||
filter,
|
||||
item_trait_ref,
|
||||
assoc_item_def_id,
|
||||
span,
|
||||
clause,
|
||||
)
|
||||
},
|
||||
));
|
||||
}
|
||||
// `ConstIfConst` is only interested in `[const]` bounds.
|
||||
PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {}
|
||||
PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {
|
||||
// FIXME(const_trait_impl): We *could* uplift the
|
||||
// `where Self::Assoc: [const] Trait` bounds from the parent trait
|
||||
// here too, but we'd need to split `const_conditions` into two
|
||||
// queries (like we do for `trait_explicit_predicates_and_bounds`)
|
||||
// since we need to also filter the predicates *out* of the const
|
||||
// conditions or they lead to cycles in the trait solver when
|
||||
// utilizing these bounds. For now, let's do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
let trait_def_id = tcx.local_parent(assoc_item_def_id);
|
||||
let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id);
|
||||
|
||||
let item_trait_ref = ty::TraitRef::identity(tcx, tcx.parent(assoc_item_def_id.to_def_id()));
|
||||
let bounds_from_parent =
|
||||
trait_predicates.predicates.iter().copied().filter_map(|(clause, span)| {
|
||||
remap_gat_vars_and_recurse_into_nested_projections(
|
||||
tcx,
|
||||
filter,
|
||||
item_trait_ref,
|
||||
assoc_item_def_id,
|
||||
span,
|
||||
clause,
|
||||
)
|
||||
});
|
||||
|
||||
let all_bounds = tcx.arena.alloc_from_iter(bounds.into_iter().chain(bounds_from_parent));
|
||||
let bounds = tcx.arena.alloc_from_iter(bounds);
|
||||
debug!(
|
||||
"associated_type_bounds({}) = {:?}",
|
||||
tcx.def_path_str(assoc_item_def_id.to_def_id()),
|
||||
all_bounds
|
||||
bounds
|
||||
);
|
||||
|
||||
assert_only_contains_predicates_from(filter, all_bounds, item_ty);
|
||||
assert_only_contains_predicates_from(filter, bounds, item_ty);
|
||||
|
||||
all_bounds
|
||||
bounds
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +124,7 @@ fn remap_gat_vars_and_recurse_into_nested_projections<'tcx>(
|
|||
ty::ClauseKind::Trait(tr) => tr.self_ty(),
|
||||
ty::ClauseKind::Projection(proj) => proj.projection_term.self_ty(),
|
||||
ty::ClauseKind::TypeOutlives(outlives) => outlives.0,
|
||||
ty::ClauseKind::HostEffect(host) => host.self_ty(),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::assert_matches::assert_matches;
|
||||
|
||||
use hir::Node;
|
||||
use rustc_attr_data_structures::{AttributeKind, find_attr};
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
|
|
@ -162,7 +163,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
|
|||
.map(|t| ty::Binder::dummy(t.instantiate_identity()));
|
||||
}
|
||||
}
|
||||
ItemKind::Trait(_, _, _, _, self_bounds, ..)
|
||||
ItemKind::Trait(_, _, _, _, _, self_bounds, ..)
|
||||
| ItemKind::TraitAlias(_, _, self_bounds) => {
|
||||
is_trait = Some((self_bounds, item.span));
|
||||
}
|
||||
|
|
@ -333,6 +334,19 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
|
|||
predicates.extend(const_evaluatable_predicates_of(tcx, def_id, &predicates));
|
||||
}
|
||||
|
||||
let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id));
|
||||
// FIXME(staged_api): We might want to look at the normal stability attributes too but
|
||||
// first we would need a way to let std/core use APIs with unstable feature bounds from
|
||||
// within stable APIs.
|
||||
let allow_unstable_feature_attr =
|
||||
find_attr!(attrs, AttributeKind::UnstableFeatureBound(i) => i)
|
||||
.map(|i| i.as_slice())
|
||||
.unwrap_or_default();
|
||||
|
||||
for (feat_name, span) in allow_unstable_feature_attr {
|
||||
predicates.insert((ty::ClauseKind::UnstableFeature(*feat_name).upcast(tcx), *span));
|
||||
}
|
||||
|
||||
let mut predicates: Vec<_> = predicates.into_iter().collect();
|
||||
|
||||
// Subtle: before we store the predicates into the tcx, we
|
||||
|
|
@ -764,6 +778,7 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>(
|
|||
ty::ClauseKind::RegionOutlives(_)
|
||||
| ty::ClauseKind::ConstArgHasType(_, _)
|
||||
| ty::ClauseKind::WellFormed(_)
|
||||
| ty::ClauseKind::UnstableFeature(_)
|
||||
| ty::ClauseKind::ConstEvaluatable(_) => {
|
||||
bug!(
|
||||
"unexpected non-`Self` predicate when computing \
|
||||
|
|
@ -791,6 +806,7 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>(
|
|||
| ty::ClauseKind::ConstArgHasType(_, _)
|
||||
| ty::ClauseKind::WellFormed(_)
|
||||
| ty::ClauseKind::ConstEvaluatable(_)
|
||||
| ty::ClauseKind::UnstableFeature(_)
|
||||
| ty::ClauseKind::HostEffect(..) => {
|
||||
bug!(
|
||||
"unexpected non-`Self` predicate when computing \
|
||||
|
|
@ -1006,7 +1022,7 @@ pub(super) fn const_conditions<'tcx>(
|
|||
Node::Item(item) => match item.kind {
|
||||
hir::ItemKind::Impl(impl_) => (impl_.generics, None, false),
|
||||
hir::ItemKind::Fn { generics, .. } => (generics, None, false),
|
||||
hir::ItemKind::Trait(_, _, _, generics, supertraits, _) => {
|
||||
hir::ItemKind::Trait(_, _, _, _, generics, supertraits, _) => {
|
||||
(generics, Some((item.owner_id.def_id, supertraits)), false)
|
||||
}
|
||||
_ => bug!("const_conditions called on wrong item: {def_id:?}"),
|
||||
|
|
|
|||
|
|
@ -634,7 +634,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
|
|||
| hir::ItemKind::Enum(_, generics, _)
|
||||
| hir::ItemKind::Struct(_, generics, _)
|
||||
| hir::ItemKind::Union(_, generics, _)
|
||||
| hir::ItemKind::Trait(_, _, _, generics, ..)
|
||||
| hir::ItemKind::Trait(_, _, _, _, generics, ..)
|
||||
| hir::ItemKind::TraitAlias(_, generics, ..)
|
||||
| hir::ItemKind::Impl(&hir::Impl { generics, .. }) => {
|
||||
// These kinds of items have only early-bound lifetime parameters.
|
||||
|
|
|
|||
|
|
@ -525,6 +525,7 @@ pub(crate) struct ConstImplForNonConstTrait {
|
|||
pub trait_name: String,
|
||||
#[suggestion(
|
||||
applicability = "machine-applicable",
|
||||
// FIXME(const_trait_impl) fix this suggestion
|
||||
code = "#[const_trait] ",
|
||||
style = "verbose"
|
||||
)]
|
||||
|
|
@ -548,6 +549,7 @@ pub(crate) struct ConstBoundForNonConstTrait {
|
|||
pub suggestion_pre: &'static str,
|
||||
#[suggestion(
|
||||
applicability = "machine-applicable",
|
||||
// FIXME(const_trait_impl) fix this suggestion
|
||||
code = "#[const_trait] ",
|
||||
style = "verbose"
|
||||
)]
|
||||
|
|
|
|||
|
|
@ -635,7 +635,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
|
|||
self.suggest_adding_type_and_const_args(err);
|
||||
}
|
||||
ExcessTypesOrConsts { .. } => {
|
||||
// this can happen with `[const] T` where T isn't a const_trait.
|
||||
// this can happen with `[const] T` where T isn't a `const trait`.
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -334,7 +334,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
};
|
||||
|
||||
let (trait_generics, trait_bounds) = match parent_trait.kind {
|
||||
hir::ItemKind::Trait(_, _, _, generics, supertraits, _) => (generics, supertraits),
|
||||
hir::ItemKind::Trait(_, _, _, _, generics, supertraits, _) => (generics, supertraits),
|
||||
hir::ItemKind::TraitAlias(_, generics, supertraits) => (generics, supertraits),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -499,6 +499,7 @@ fn trait_specialization_kind<'tcx>(
|
|||
| ty::ClauseKind::ConstArgHasType(..)
|
||||
| ty::ClauseKind::WellFormed(_)
|
||||
| ty::ClauseKind::ConstEvaluatable(..)
|
||||
| ty::ClauseKind::UnstableFeature(_)
|
||||
| ty::ClauseKind::HostEffect(..) => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> {
|
|||
| ty::ClauseKind::ConstArgHasType(_, _)
|
||||
| ty::ClauseKind::WellFormed(_)
|
||||
| ty::ClauseKind::ConstEvaluatable(_)
|
||||
| ty::ClauseKind::UnstableFeature(_)
|
||||
| ty::ClauseKind::HostEffect(..) => {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -735,8 +735,17 @@ impl<'a> State<'a> {
|
|||
}
|
||||
self.bclose(item.span, cb);
|
||||
}
|
||||
hir::ItemKind::Trait(is_auto, safety, ident, generics, bounds, trait_items) => {
|
||||
hir::ItemKind::Trait(
|
||||
constness,
|
||||
is_auto,
|
||||
safety,
|
||||
ident,
|
||||
generics,
|
||||
bounds,
|
||||
trait_items,
|
||||
) => {
|
||||
let (cb, ib) = self.head("");
|
||||
self.print_constness(constness);
|
||||
self.print_is_auto(is_auto);
|
||||
self.print_safety(safety);
|
||||
self.word_nbsp("trait");
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
| ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
|
||||
| ty::PredicateKind::ConstEquate(..)
|
||||
| ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..))
|
||||
| ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_))
|
||||
| ty::PredicateKind::Ambiguous => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use std::fmt::Write;
|
|||
|
||||
use hir::def_id::DefId;
|
||||
use hir::{HirId, ItemKind};
|
||||
use rustc_ast::join_path_idents;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER};
|
||||
|
|
@ -383,13 +384,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// All that is left is `_`! We need to use the full path. It doesn't matter which one we
|
||||
// pick, so just take the first one.
|
||||
match import_items[0].kind {
|
||||
ItemKind::Use(path, _) => Some(
|
||||
path.segments
|
||||
.iter()
|
||||
.map(|segment| segment.ident.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("::"),
|
||||
),
|
||||
ItemKind::Use(path, _) => {
|
||||
Some(join_path_idents(path.segments.iter().map(|seg| seg.ident)))
|
||||
}
|
||||
_ => {
|
||||
span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -921,6 +921,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
|||
| ty::ClauseKind::ConstArgHasType(_, _)
|
||||
| ty::ClauseKind::WellFormed(_)
|
||||
| ty::ClauseKind::ConstEvaluatable(_)
|
||||
| ty::ClauseKind::UnstableFeature(_)
|
||||
| ty::ClauseKind::HostEffect(..) => None,
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1189,7 +1189,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
entry.1.insert((self_ty.span, ""));
|
||||
}
|
||||
Some(Node::Item(hir::Item {
|
||||
kind: hir::ItemKind::Trait(rustc_ast::ast::IsAuto::Yes, ..),
|
||||
kind: hir::ItemKind::Trait(_, rustc_ast::ast::IsAuto::Yes, ..),
|
||||
span: item_span,
|
||||
..
|
||||
})) => {
|
||||
|
|
@ -1201,7 +1201,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
Some(
|
||||
Node::Item(hir::Item {
|
||||
kind:
|
||||
hir::ItemKind::Trait(_, _, ident, ..)
|
||||
hir::ItemKind::Trait(_, _, _, ident, ..)
|
||||
| hir::ItemKind::TraitAlias(ident, ..),
|
||||
..
|
||||
})
|
||||
|
|
@ -4084,7 +4084,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
return;
|
||||
}
|
||||
Node::Item(hir::Item {
|
||||
kind: hir::ItemKind::Trait(_, _, ident, _, bounds, _),
|
||||
kind: hir::ItemKind::Trait(_, _, _, ident, _, bounds, _),
|
||||
..
|
||||
}) => {
|
||||
let (sp, sep, article) = if bounds.is_empty() {
|
||||
|
|
|
|||
|
|
@ -440,6 +440,7 @@ lint_invalid_asm_label_named = avoid using named labels in inline assembly
|
|||
.help = only local labels of the form `<number>:` should be used in inline asm
|
||||
.note = see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
|
||||
lint_invalid_asm_label_no_span = the label may be declared in the expansion of a macro
|
||||
|
||||
lint_invalid_crate_type_value = invalid `crate_type` value
|
||||
.suggestion = did you mean
|
||||
|
||||
|
|
@ -508,27 +509,50 @@ lint_metavariable_still_repeating = variable `{$name}` is still repeating at thi
|
|||
|
||||
lint_metavariable_wrong_operator = meta-variable repeats with different Kleene operator
|
||||
|
||||
lint_mismatched_lifetime_syntaxes =
|
||||
lifetime flowing from input to output with different syntax can be confusing
|
||||
.label_mismatched_lifetime_syntaxes_inputs =
|
||||
{$n_inputs ->
|
||||
[one] this lifetime flows
|
||||
*[other] these lifetimes flow
|
||||
} to the output
|
||||
.label_mismatched_lifetime_syntaxes_outputs =
|
||||
the {$n_outputs ->
|
||||
[one] lifetime gets
|
||||
*[other] lifetimes get
|
||||
} resolved as `{$lifetime_name}`
|
||||
lint_mismatched_lifetime_syntaxes_eliding_while_named =
|
||||
eliding a lifetime that's named elsewhere is confusing
|
||||
|
||||
lint_mismatched_lifetime_syntaxes_help =
|
||||
the same lifetime is referred to in inconsistent ways, making the signature confusing
|
||||
|
||||
lint_mismatched_lifetime_syntaxes_hiding_and_eliding_while_named =
|
||||
hiding or eliding a lifetime that's named elsewhere is confusing
|
||||
|
||||
lint_mismatched_lifetime_syntaxes_hiding_while_elided =
|
||||
hiding a lifetime that's elided elsewhere is confusing
|
||||
|
||||
lint_mismatched_lifetime_syntaxes_hiding_while_named =
|
||||
hiding a lifetime that's named elsewhere is confusing
|
||||
|
||||
lint_mismatched_lifetime_syntaxes_input_elided =
|
||||
the lifetime is elided here
|
||||
|
||||
lint_mismatched_lifetime_syntaxes_input_hidden =
|
||||
the lifetime is hidden here
|
||||
|
||||
lint_mismatched_lifetime_syntaxes_input_named =
|
||||
the lifetime is named here
|
||||
|
||||
lint_mismatched_lifetime_syntaxes_output_elided =
|
||||
the same lifetime is elided here
|
||||
|
||||
lint_mismatched_lifetime_syntaxes_output_hidden =
|
||||
the same lifetime is hidden here
|
||||
|
||||
lint_mismatched_lifetime_syntaxes_output_named =
|
||||
the same lifetime is named here
|
||||
|
||||
lint_mismatched_lifetime_syntaxes_suggestion_explicit =
|
||||
one option is to consistently use `{$lifetime_name}`
|
||||
consistently use `{$lifetime_name}`
|
||||
|
||||
lint_mismatched_lifetime_syntaxes_suggestion_implicit =
|
||||
one option is to consistently remove the lifetime
|
||||
remove the lifetime name from references
|
||||
|
||||
lint_mismatched_lifetime_syntaxes_suggestion_mixed =
|
||||
one option is to remove the lifetime for references and use the anonymous lifetime for paths
|
||||
remove the lifetime name from references and use `'_` for type paths
|
||||
|
||||
lint_mismatched_lifetime_syntaxes_suggestion_mixed_only_paths =
|
||||
use `'_` for type paths
|
||||
|
||||
lint_missing_unsafe_on_extern = extern blocks should be unsafe
|
||||
.suggestion = needs `unsafe` before the extern keyword
|
||||
|
|
@ -744,6 +768,9 @@ lint_redundant_semicolons_suggestion = remove {$multiple_semicolons ->
|
|||
*[false] this semicolon
|
||||
}
|
||||
|
||||
lint_reexport_private_dependency =
|
||||
{$kind} `{$name}` from private dependency '{$krate}' is re-exported
|
||||
|
||||
lint_remove_mut_from_pattern = remove `mut` from the parameter
|
||||
|
||||
lint_removed_lint = lint `{$name}` has been removed: {$reason}
|
||||
|
|
@ -790,6 +817,9 @@ lint_supertrait_as_deref_target = this `Deref` implementation is covered by an i
|
|||
.label2 = target type is a supertrait of `{$self_ty}`
|
||||
.help = consider removing this implementation or replacing it with a method instead
|
||||
|
||||
lint_surrogate_char_cast = surrogate values are not valid for `char`
|
||||
.note = `0xD800..=0xDFFF` are reserved for Unicode surrogates and are not valid `char` values
|
||||
|
||||
lint_suspicious_double_ref_clone =
|
||||
using `.clone()` on a double reference, which returns `{$ty}` instead of cloning the inner type
|
||||
|
||||
|
|
@ -799,6 +829,9 @@ lint_suspicious_double_ref_deref =
|
|||
lint_symbol_intern_string_literal = using `Symbol::intern` on a string literal
|
||||
.help = consider adding the symbol to `compiler/rustc_span/src/symbol.rs`
|
||||
|
||||
lint_too_large_char_cast = value exceeds maximum `char` value
|
||||
.note = maximum valid `char` value is `0x10FFFF`
|
||||
|
||||
lint_trailing_semi_macro = trailing semicolon in macro used in expression position
|
||||
.note1 = macro invocations at the end of a block are treated as expressions
|
||||
.note2 = to ignore the value produced by the macro, add a semicolon after the invocation of `{$name}`
|
||||
|
|
|
|||
|
|
@ -1519,8 +1519,9 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
|
|||
ClauseKind::TypeOutlives(..) |
|
||||
ClauseKind::RegionOutlives(..) => "lifetime",
|
||||
|
||||
ClauseKind::UnstableFeature(_)
|
||||
// `ConstArgHasType` is never global as `ct` is always a param
|
||||
ClauseKind::ConstArgHasType(..)
|
||||
| ClauseKind::ConstArgHasType(..)
|
||||
// Ignore projections, as they can only be global
|
||||
// if the trait bound is global
|
||||
| ClauseKind::Projection(..)
|
||||
|
|
|
|||
|
|
@ -351,6 +351,9 @@ pub fn decorate_builtin_lint(
|
|||
}
|
||||
.decorate_lint(diag);
|
||||
}
|
||||
BuiltinLintDiag::ReexportPrivateDependency { name, kind, krate } => {
|
||||
lints::ReexportPrivateDependency { name, kind, krate }.decorate_lint(diag);
|
||||
}
|
||||
BuiltinLintDiag::UnusedQualifications { removal_span } => {
|
||||
lints::UnusedQualifications { removal_span }.decorate_lint(diag);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ mod invalid_from_utf8;
|
|||
mod late;
|
||||
mod let_underscore;
|
||||
mod levels;
|
||||
mod lifetime_syntax;
|
||||
pub mod lifetime_syntax;
|
||||
mod lints;
|
||||
mod macro_expr_fragment_specifier_2024_migration;
|
||||
mod map_unit_fn;
|
||||
|
|
|
|||
|
|
@ -140,43 +140,115 @@ fn report_mismatches<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
fn lifetimes_use_matched_syntax(input_info: &[Info<'_>], output_info: &[Info<'_>]) -> bool {
|
||||
// Categorize lifetimes into source/syntax buckets.
|
||||
let mut n_hidden = 0;
|
||||
let mut n_elided = 0;
|
||||
let mut n_named = 0;
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
enum LifetimeSyntaxCategory {
|
||||
Hidden,
|
||||
Elided,
|
||||
Named,
|
||||
}
|
||||
|
||||
for info in input_info.iter().chain(output_info) {
|
||||
impl LifetimeSyntaxCategory {
|
||||
fn new(syntax_source: (hir::LifetimeSyntax, LifetimeSource)) -> Option<Self> {
|
||||
use LifetimeSource::*;
|
||||
use hir::LifetimeSyntax::*;
|
||||
|
||||
let syntax_source = (info.lifetime.syntax, info.lifetime.source);
|
||||
|
||||
match syntax_source {
|
||||
// Ignore any other kind of lifetime.
|
||||
(_, Other) => continue,
|
||||
|
||||
// E.g. `&T`.
|
||||
(Implicit, Reference | OutlivesBound | PreciseCapturing) |
|
||||
(Implicit, Reference) |
|
||||
// E.g. `&'_ T`.
|
||||
(ExplicitAnonymous, Reference | OutlivesBound | PreciseCapturing) |
|
||||
(ExplicitAnonymous, Reference) |
|
||||
// E.g. `ContainsLifetime<'_>`.
|
||||
(ExplicitAnonymous, Path { .. }) => n_elided += 1,
|
||||
(ExplicitAnonymous, Path { .. }) |
|
||||
// E.g. `+ '_`, `+ use<'_>`.
|
||||
(ExplicitAnonymous, OutlivesBound | PreciseCapturing) => {
|
||||
Some(Self::Elided)
|
||||
}
|
||||
|
||||
// E.g. `ContainsLifetime`.
|
||||
(Implicit, Path { .. }) => n_hidden += 1,
|
||||
(Implicit, Path { .. }) => {
|
||||
Some(Self::Hidden)
|
||||
}
|
||||
|
||||
// E.g. `&'a T`.
|
||||
(ExplicitBound, Reference | OutlivesBound | PreciseCapturing) |
|
||||
(ExplicitBound, Reference) |
|
||||
// E.g. `ContainsLifetime<'a>`.
|
||||
(ExplicitBound, Path { .. }) => n_named += 1,
|
||||
};
|
||||
(ExplicitBound, Path { .. }) |
|
||||
// E.g. `+ 'a`, `+ use<'a>`.
|
||||
(ExplicitBound, OutlivesBound | PreciseCapturing) => {
|
||||
Some(Self::Named)
|
||||
}
|
||||
|
||||
(Implicit, OutlivesBound | PreciseCapturing) |
|
||||
(_, Other) => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct LifetimeSyntaxCategories<T> {
|
||||
pub hidden: T,
|
||||
pub elided: T,
|
||||
pub named: T,
|
||||
}
|
||||
|
||||
impl<T> LifetimeSyntaxCategories<T> {
|
||||
fn select(&mut self, category: LifetimeSyntaxCategory) -> &mut T {
|
||||
use LifetimeSyntaxCategory::*;
|
||||
|
||||
match category {
|
||||
Elided => &mut self.elided,
|
||||
Hidden => &mut self.hidden,
|
||||
Named => &mut self.named,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> LifetimeSyntaxCategories<Vec<T>> {
|
||||
pub fn len(&self) -> LifetimeSyntaxCategories<usize> {
|
||||
LifetimeSyntaxCategories {
|
||||
hidden: self.hidden.len(),
|
||||
elided: self.elided.len(),
|
||||
named: self.named.len(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flatten(&self) -> impl Iterator<Item = &T> {
|
||||
let Self { hidden, elided, named } = self;
|
||||
[hidden.iter(), elided.iter(), named.iter()].into_iter().flatten()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Add for LifetimeSyntaxCategories<usize> {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self {
|
||||
hidden: self.hidden + rhs.hidden,
|
||||
elided: self.elided + rhs.elided,
|
||||
named: self.named + rhs.named,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lifetimes_use_matched_syntax(input_info: &[Info<'_>], output_info: &[Info<'_>]) -> bool {
|
||||
let mut syntax_counts = LifetimeSyntaxCategories::<usize>::default();
|
||||
|
||||
for info in input_info.iter().chain(output_info) {
|
||||
if let Some(category) = info.lifetime_syntax_category() {
|
||||
*syntax_counts.select(category) += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let syntax_counts = (n_hidden, n_elided, n_named);
|
||||
tracing::debug!(?syntax_counts);
|
||||
|
||||
matches!(syntax_counts, (_, 0, 0) | (0, _, 0) | (0, 0, _))
|
||||
matches!(
|
||||
syntax_counts,
|
||||
LifetimeSyntaxCategories { hidden: _, elided: 0, named: 0 }
|
||||
| LifetimeSyntaxCategories { hidden: 0, elided: _, named: 0 }
|
||||
| LifetimeSyntaxCategories { hidden: 0, elided: 0, named: _ }
|
||||
)
|
||||
}
|
||||
|
||||
fn emit_mismatch_diagnostic<'tcx>(
|
||||
|
|
@ -238,7 +310,7 @@ fn emit_mismatch_diagnostic<'tcx>(
|
|||
use LifetimeSource::*;
|
||||
use hir::LifetimeSyntax::*;
|
||||
|
||||
let syntax_source = (info.lifetime.syntax, info.lifetime.source);
|
||||
let syntax_source = info.syntax_source();
|
||||
|
||||
if let (_, Other) = syntax_source {
|
||||
// Ignore any other kind of lifetime.
|
||||
|
|
@ -259,7 +331,6 @@ fn emit_mismatch_diagnostic<'tcx>(
|
|||
// E.g. `&'_ T`.
|
||||
(ExplicitAnonymous, Reference) => {
|
||||
suggest_change_to_implicit.push(info);
|
||||
suggest_change_to_mixed_implicit.push(info);
|
||||
suggest_change_to_explicit_bound.push(info);
|
||||
}
|
||||
|
||||
|
|
@ -319,12 +390,22 @@ fn emit_mismatch_diagnostic<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
let categorize = |infos: &[Info<'_>]| {
|
||||
let mut categories = LifetimeSyntaxCategories::<Vec<_>>::default();
|
||||
for info in infos {
|
||||
if let Some(category) = info.lifetime_syntax_category() {
|
||||
categories.select(category).push(info.reporting_span());
|
||||
}
|
||||
}
|
||||
categories
|
||||
};
|
||||
|
||||
let inputs = categorize(input_info);
|
||||
let outputs = categorize(output_info);
|
||||
|
||||
let make_implicit_suggestions =
|
||||
|infos: &[&Info<'_>]| infos.iter().map(|i| i.removing_span()).collect::<Vec<_>>();
|
||||
|
||||
let inputs = input_info.iter().map(|info| info.reporting_span()).collect();
|
||||
let outputs = output_info.iter().map(|info| info.reporting_span()).collect();
|
||||
|
||||
let explicit_bound_suggestion = bound_lifetime.map(|info| {
|
||||
build_mismatch_suggestion(info.lifetime_name(), &suggest_change_to_explicit_bound)
|
||||
});
|
||||
|
|
@ -399,8 +480,6 @@ fn emit_mismatch_diagnostic<'tcx>(
|
|||
?explicit_anonymous_suggestion,
|
||||
);
|
||||
|
||||
let lifetime_name = bound_lifetime.map(|info| info.lifetime_name()).unwrap_or("'_").to_owned();
|
||||
|
||||
// We can produce a number of suggestions which may overwhelm
|
||||
// the user. Instead, we order the suggestions based on Rust
|
||||
// idioms. The "best" choice is shown to the user and the
|
||||
|
|
@ -413,8 +492,8 @@ fn emit_mismatch_diagnostic<'tcx>(
|
|||
|
||||
cx.emit_span_lint(
|
||||
MISMATCHED_LIFETIME_SYNTAXES,
|
||||
Vec::clone(&inputs),
|
||||
lints::MismatchedLifetimeSyntaxes { lifetime_name, inputs, outputs, suggestions },
|
||||
inputs.flatten().copied().collect::<Vec<_>>(),
|
||||
lints::MismatchedLifetimeSyntaxes { inputs, outputs, suggestions },
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -422,12 +501,12 @@ fn build_mismatch_suggestion(
|
|||
lifetime_name: &str,
|
||||
infos: &[&Info<'_>],
|
||||
) -> lints::MismatchedLifetimeSyntaxesSuggestion {
|
||||
let lifetime_name_sugg = lifetime_name.to_owned();
|
||||
let lifetime_name = lifetime_name.to_owned();
|
||||
|
||||
let suggestions = infos.iter().map(|info| info.suggestion(&lifetime_name)).collect();
|
||||
|
||||
lints::MismatchedLifetimeSyntaxesSuggestion::Explicit {
|
||||
lifetime_name_sugg,
|
||||
lifetime_name,
|
||||
suggestions,
|
||||
tool_only: false,
|
||||
}
|
||||
|
|
@ -441,6 +520,14 @@ struct Info<'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx> Info<'tcx> {
|
||||
fn syntax_source(&self) -> (hir::LifetimeSyntax, LifetimeSource) {
|
||||
(self.lifetime.syntax, self.lifetime.source)
|
||||
}
|
||||
|
||||
fn lifetime_syntax_category(&self) -> Option<LifetimeSyntaxCategory> {
|
||||
LifetimeSyntaxCategory::new(self.syntax_source())
|
||||
}
|
||||
|
||||
fn lifetime_name(&self) -> &str {
|
||||
self.lifetime.ident.as_str()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym};
|
|||
|
||||
use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds};
|
||||
use crate::errors::{OverruledAttributeSub, RequestedLevel};
|
||||
use crate::lifetime_syntax::LifetimeSyntaxCategories;
|
||||
use crate::{LateContext, fluent_generated as fluent};
|
||||
|
||||
// array_into_iter.rs
|
||||
|
|
@ -1746,6 +1747,20 @@ pub(crate) struct OverflowingLiteral<'a> {
|
|||
pub lit: String,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_surrogate_char_cast)]
|
||||
#[note]
|
||||
pub(crate) struct SurrogateCharCast {
|
||||
pub literal: u128,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_too_large_char_cast)]
|
||||
#[note]
|
||||
pub(crate) struct TooLargeCharCast {
|
||||
pub literal: u128,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_uses_power_alignment)]
|
||||
pub(crate) struct UsesPowerAlignment;
|
||||
|
|
@ -3080,6 +3095,14 @@ pub(crate) struct HiddenGlobReexports {
|
|||
pub namespace: String,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_reexport_private_dependency)]
|
||||
pub(crate) struct ReexportPrivateDependency {
|
||||
pub name: String,
|
||||
pub kind: String,
|
||||
pub krate: Symbol,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_unnecessary_qualification)]
|
||||
pub(crate) struct UnusedQualifications {
|
||||
|
|
@ -3194,30 +3217,59 @@ pub(crate) struct ReservedMultihash {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct MismatchedLifetimeSyntaxes {
|
||||
pub lifetime_name: String,
|
||||
pub inputs: Vec<Span>,
|
||||
pub outputs: Vec<Span>,
|
||||
pub inputs: LifetimeSyntaxCategories<Vec<Span>>,
|
||||
pub outputs: LifetimeSyntaxCategories<Vec<Span>>,
|
||||
|
||||
pub suggestions: Vec<MismatchedLifetimeSyntaxesSuggestion>,
|
||||
}
|
||||
|
||||
impl<'a, G: EmissionGuarantee> LintDiagnostic<'a, G> for MismatchedLifetimeSyntaxes {
|
||||
fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>) {
|
||||
diag.primary_message(fluent::lint_mismatched_lifetime_syntaxes);
|
||||
let counts = self.inputs.len() + self.outputs.len();
|
||||
let message = match counts {
|
||||
LifetimeSyntaxCategories { hidden: 0, elided: 0, named: 0 } => {
|
||||
panic!("No lifetime mismatch detected")
|
||||
}
|
||||
|
||||
diag.arg("lifetime_name", self.lifetime_name);
|
||||
LifetimeSyntaxCategories { hidden: _, elided: _, named: 0 } => {
|
||||
fluent::lint_mismatched_lifetime_syntaxes_hiding_while_elided
|
||||
}
|
||||
|
||||
diag.arg("n_inputs", self.inputs.len());
|
||||
for input in self.inputs {
|
||||
let a = diag.eagerly_translate(fluent::lint_label_mismatched_lifetime_syntaxes_inputs);
|
||||
diag.span_label(input, a);
|
||||
LifetimeSyntaxCategories { hidden: _, elided: 0, named: _ } => {
|
||||
fluent::lint_mismatched_lifetime_syntaxes_hiding_while_named
|
||||
}
|
||||
|
||||
LifetimeSyntaxCategories { hidden: 0, elided: _, named: _ } => {
|
||||
fluent::lint_mismatched_lifetime_syntaxes_eliding_while_named
|
||||
}
|
||||
|
||||
LifetimeSyntaxCategories { hidden: _, elided: _, named: _ } => {
|
||||
fluent::lint_mismatched_lifetime_syntaxes_hiding_and_eliding_while_named
|
||||
}
|
||||
};
|
||||
diag.primary_message(message);
|
||||
|
||||
for s in self.inputs.hidden {
|
||||
diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_input_hidden);
|
||||
}
|
||||
for s in self.inputs.elided {
|
||||
diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_input_elided);
|
||||
}
|
||||
for s in self.inputs.named {
|
||||
diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_input_named);
|
||||
}
|
||||
|
||||
diag.arg("n_outputs", self.outputs.len());
|
||||
for output in self.outputs {
|
||||
let a = diag.eagerly_translate(fluent::lint_label_mismatched_lifetime_syntaxes_outputs);
|
||||
diag.span_label(output, a);
|
||||
for s in self.outputs.hidden {
|
||||
diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_output_hidden);
|
||||
}
|
||||
for s in self.outputs.elided {
|
||||
diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_output_elided);
|
||||
}
|
||||
for s in self.outputs.named {
|
||||
diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_output_named);
|
||||
}
|
||||
|
||||
diag.help(fluent::lint_mismatched_lifetime_syntaxes_help);
|
||||
|
||||
let mut suggestions = self.suggestions.into_iter();
|
||||
if let Some(s) = suggestions.next() {
|
||||
|
|
@ -3245,7 +3297,7 @@ pub(crate) enum MismatchedLifetimeSyntaxesSuggestion {
|
|||
},
|
||||
|
||||
Explicit {
|
||||
lifetime_name_sugg: String,
|
||||
lifetime_name: String,
|
||||
suggestions: Vec<(Span, String)>,
|
||||
tool_only: bool,
|
||||
},
|
||||
|
|
@ -3285,6 +3337,12 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
|
|||
}
|
||||
|
||||
Mixed { implicit_suggestions, explicit_anonymous_suggestions, tool_only } => {
|
||||
let message = if implicit_suggestions.is_empty() {
|
||||
fluent::lint_mismatched_lifetime_syntaxes_suggestion_mixed_only_paths
|
||||
} else {
|
||||
fluent::lint_mismatched_lifetime_syntaxes_suggestion_mixed
|
||||
};
|
||||
|
||||
let implicit_suggestions =
|
||||
implicit_suggestions.into_iter().map(|s| (s, String::new()));
|
||||
|
||||
|
|
@ -3292,19 +3350,19 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
|
|||
implicit_suggestions.chain(explicit_anonymous_suggestions).collect();
|
||||
|
||||
diag.multipart_suggestion_with_style(
|
||||
fluent::lint_mismatched_lifetime_syntaxes_suggestion_mixed,
|
||||
message,
|
||||
suggestions,
|
||||
Applicability::MaybeIncorrect,
|
||||
style(tool_only),
|
||||
);
|
||||
}
|
||||
|
||||
Explicit { lifetime_name_sugg, suggestions, tool_only } => {
|
||||
diag.arg("lifetime_name_sugg", lifetime_name_sugg);
|
||||
Explicit { lifetime_name, suggestions, tool_only } => {
|
||||
diag.arg("lifetime_name", lifetime_name);
|
||||
let msg = diag.eagerly_translate(
|
||||
fluent::lint_mismatched_lifetime_syntaxes_suggestion_explicit,
|
||||
);
|
||||
diag.remove_arg("lifetime_name_sugg");
|
||||
diag.remove_arg("lifetime_name");
|
||||
diag.multipart_suggestion_with_style(
|
||||
msg,
|
||||
suggestions,
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable {
|
|||
let def_id = item.owner_id.to_def_id();
|
||||
// NOTE(nbdd0121): use `dyn_compatibility_violations` instead of `is_dyn_compatible` because
|
||||
// the latter will report `where_clause_object_safety` lint.
|
||||
if let hir::ItemKind::Trait(_, _, ident, ..) = item.kind
|
||||
if let hir::ItemKind::Trait(_, _, _, ident, ..) = item.kind
|
||||
&& cx.tcx.is_dyn_compatible(def_id)
|
||||
{
|
||||
let direct_super_traits_iter = cx
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use crate::context::LintContext;
|
|||
use crate::lints::{
|
||||
OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSignBitSub,
|
||||
OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt,
|
||||
RangeEndpointOutOfRange, UseInclusiveRange,
|
||||
RangeEndpointOutOfRange, SurrogateCharCast, TooLargeCharCast, UseInclusiveRange,
|
||||
};
|
||||
use crate::types::{OVERFLOWING_LITERALS, TypeLimits};
|
||||
|
||||
|
|
@ -38,12 +38,18 @@ fn lint_overflowing_range_endpoint<'tcx>(
|
|||
|
||||
// We only want to handle exclusive (`..`) ranges,
|
||||
// which are represented as `ExprKind::Struct`.
|
||||
let Node::ExprField(field) = cx.tcx.parent_hir_node(hir_id) else { return false };
|
||||
let Node::Expr(struct_expr) = cx.tcx.parent_hir_node(field.hir_id) else { return false };
|
||||
let Node::ExprField(field) = cx.tcx.parent_hir_node(hir_id) else {
|
||||
return false;
|
||||
};
|
||||
let Node::Expr(struct_expr) = cx.tcx.parent_hir_node(field.hir_id) else {
|
||||
return false;
|
||||
};
|
||||
if !is_range_literal(struct_expr) {
|
||||
return false;
|
||||
};
|
||||
let ExprKind::Struct(_, [start, end], _) = &struct_expr.kind else { return false };
|
||||
let ExprKind::Struct(_, [start, end], _) = &struct_expr.kind else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// We can suggest using an inclusive range
|
||||
// (`..=`) instead only if it is the `end` that is
|
||||
|
|
@ -61,7 +67,9 @@ fn lint_overflowing_range_endpoint<'tcx>(
|
|||
};
|
||||
|
||||
let sub_sugg = if span.lo() == lit_span.lo() {
|
||||
let Ok(start) = cx.sess().source_map().span_to_snippet(start.span) else { return false };
|
||||
let Ok(start) = cx.sess().source_map().span_to_snippet(start.span) else {
|
||||
return false;
|
||||
};
|
||||
UseInclusiveRange::WithoutParen {
|
||||
sugg: struct_expr.span.shrink_to_lo().to(lit_span.shrink_to_hi()),
|
||||
start,
|
||||
|
|
@ -316,11 +324,25 @@ fn lint_uint_literal<'tcx>(
|
|||
match par_e.kind {
|
||||
hir::ExprKind::Cast(..) => {
|
||||
if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() {
|
||||
cx.emit_span_lint(
|
||||
OVERFLOWING_LITERALS,
|
||||
par_e.span,
|
||||
OnlyCastu8ToChar { span: par_e.span, literal: lit_val },
|
||||
);
|
||||
if lit_val > 0x10FFFF {
|
||||
cx.emit_span_lint(
|
||||
OVERFLOWING_LITERALS,
|
||||
par_e.span,
|
||||
TooLargeCharCast { literal: lit_val },
|
||||
);
|
||||
} else if (0xD800..=0xDFFF).contains(&lit_val) {
|
||||
cx.emit_span_lint(
|
||||
OVERFLOWING_LITERALS,
|
||||
par_e.span,
|
||||
SurrogateCharCast { literal: lit_val },
|
||||
);
|
||||
} else {
|
||||
cx.emit_span_lint(
|
||||
OVERFLOWING_LITERALS,
|
||||
par_e.span,
|
||||
OnlyCastu8ToChar { span: par_e.span, literal: lit_val },
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -739,6 +739,11 @@ pub enum BuiltinLintDiag {
|
|||
/// The local binding that shadows the glob reexport.
|
||||
private_item_span: Span,
|
||||
},
|
||||
ReexportPrivateDependency {
|
||||
name: String,
|
||||
kind: String,
|
||||
krate: Symbol,
|
||||
},
|
||||
UnusedQualifications {
|
||||
/// The span of the unnecessarily-qualified path to remove.
|
||||
removal_span: Span,
|
||||
|
|
|
|||
|
|
@ -1131,7 +1131,7 @@ fn should_encode_mir(
|
|||
&& reachable_set.contains(&def_id)
|
||||
&& (generics.requires_monomorphization(tcx)
|
||||
|| tcx.cross_crate_inlinable(def_id)));
|
||||
// The function has a `const` modifier or is in a `#[const_trait]`.
|
||||
// The function has a `const` modifier or is in a `const trait`.
|
||||
let is_const_fn = tcx.is_const_fn(def_id.to_def_id())
|
||||
|| tcx.is_const_default_method(def_id.to_def_id());
|
||||
(is_const_fn, opt)
|
||||
|
|
|
|||
|
|
@ -940,7 +940,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
}) => until_within(*outer_span, ty.span),
|
||||
// With generics and bounds.
|
||||
Node::Item(Item {
|
||||
kind: ItemKind::Trait(_, _, _, generics, bounds, _),
|
||||
kind: ItemKind::Trait(_, _, _, _, generics, bounds, _),
|
||||
span: outer_span,
|
||||
..
|
||||
})
|
||||
|
|
|
|||
|
|
@ -257,7 +257,7 @@ pub enum InvalidProgramInfo<'tcx> {
|
|||
/// Details of why a pointer had to be in-bounds.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum CheckInAllocMsg {
|
||||
/// We are access memory.
|
||||
/// We are accessing memory.
|
||||
MemoryAccess,
|
||||
/// We are doing pointer arithmetic.
|
||||
InboundsPointerArithmetic,
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
|||
type FnInputTys = &'tcx [Ty<'tcx>];
|
||||
type ParamTy = ParamTy;
|
||||
type BoundTy = ty::BoundTy;
|
||||
type Symbol = Symbol;
|
||||
|
||||
type PlaceholderTy = ty::PlaceholderType;
|
||||
type ErrorGuaranteed = ErrorGuaranteed;
|
||||
|
|
@ -833,6 +834,13 @@ impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>> for &'tcx rustc_featu
|
|||
fn associated_const_equality(self) -> bool {
|
||||
self.associated_const_equality()
|
||||
}
|
||||
|
||||
fn feature_bound_holds_in_crate(self, symbol: Symbol) -> bool {
|
||||
// We don't consider feature bounds to hold in the crate when `staged_api` feature is
|
||||
// enabled, even if it is enabled through `#[feature]`.
|
||||
// This is to prevent accidentally leaking unstable APIs to stable.
|
||||
!self.staged_api() && self.enabled(symbol)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> rustc_type_ir::inherent::Span<TyCtxt<'tcx>> for Span {
|
||||
|
|
|
|||
|
|
@ -131,6 +131,7 @@ impl<'tcx> Predicate<'tcx> {
|
|||
| PredicateKind::Clause(ClauseKind::TypeOutlives(_))
|
||||
| PredicateKind::Clause(ClauseKind::Projection(_))
|
||||
| PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
|
||||
| PredicateKind::Clause(ClauseKind::UnstableFeature(_))
|
||||
| PredicateKind::DynCompatible(_)
|
||||
| PredicateKind::Subtype(_)
|
||||
| PredicateKind::Coerce(_)
|
||||
|
|
@ -649,6 +650,7 @@ impl<'tcx> Predicate<'tcx> {
|
|||
PredicateKind::Clause(ClauseKind::Projection(..))
|
||||
| PredicateKind::Clause(ClauseKind::HostEffect(..))
|
||||
| PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
|
||||
| PredicateKind::Clause(ClauseKind::UnstableFeature(_))
|
||||
| PredicateKind::NormalizesTo(..)
|
||||
| PredicateKind::AliasRelate(..)
|
||||
| PredicateKind::Subtype(..)
|
||||
|
|
@ -670,6 +672,7 @@ impl<'tcx> Predicate<'tcx> {
|
|||
PredicateKind::Clause(ClauseKind::Trait(..))
|
||||
| PredicateKind::Clause(ClauseKind::HostEffect(..))
|
||||
| PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
|
||||
| PredicateKind::Clause(ClauseKind::UnstableFeature(_))
|
||||
| PredicateKind::NormalizesTo(..)
|
||||
| PredicateKind::AliasRelate(..)
|
||||
| PredicateKind::Subtype(..)
|
||||
|
|
|
|||
|
|
@ -2430,7 +2430,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
|
|||
}
|
||||
|
||||
let verbose = self.should_print_verbose();
|
||||
disambiguated_data.fmt_maybe_verbose(self, verbose)?;
|
||||
write!(self, "{}", disambiguated_data.as_sym(verbose))?;
|
||||
|
||||
self.empty_path = false;
|
||||
|
||||
|
|
@ -3237,6 +3237,7 @@ define_print! {
|
|||
ty::ClauseKind::ConstEvaluatable(ct) => {
|
||||
p!("the constant `", print(ct), "` can be evaluated")
|
||||
}
|
||||
ty::ClauseKind::UnstableFeature(symbol) => p!("unstable feature: ", write("`{}`", symbol)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ pub struct TraitDef {
|
|||
|
||||
pub safety: hir::Safety,
|
||||
|
||||
/// Whether this trait has been annotated with `#[const_trait]`.
|
||||
/// Whether this trait is `const`.
|
||||
pub constness: hir::Constness,
|
||||
|
||||
/// If `true`, then this trait had the `#[rustc_paren_sugar]`
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ use crate::delegate::SolverDelegate;
|
|||
use crate::placeholder::BoundVarReplacer;
|
||||
use crate::solve::inspect::{self, ProofTreeBuilder};
|
||||
use crate::solve::search_graph::SearchGraph;
|
||||
use crate::solve::ty::may_use_unstable_feature;
|
||||
use crate::solve::{
|
||||
CanonicalInput, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluation, GoalEvaluationKind,
|
||||
GoalSource, GoalStalledOn, HasChanged, NestedNormalizationGoals, NoSolution, QueryInput,
|
||||
|
|
@ -550,6 +551,9 @@ where
|
|||
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
|
||||
self.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) })
|
||||
}
|
||||
ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(symbol)) => {
|
||||
self.compute_unstable_feature_goal(param_env, symbol)
|
||||
}
|
||||
ty::PredicateKind::Subtype(predicate) => {
|
||||
self.compute_subtype_goal(Goal { param_env, predicate })
|
||||
}
|
||||
|
|
@ -1177,6 +1181,14 @@ where
|
|||
) -> T {
|
||||
BoundVarReplacer::replace_bound_vars(&**self.delegate, universes, t).0
|
||||
}
|
||||
|
||||
pub(super) fn may_use_unstable_feature(
|
||||
&self,
|
||||
param_env: I::ParamEnv,
|
||||
symbol: I::Symbol,
|
||||
) -> bool {
|
||||
may_use_unstable_feature(&**self.delegate, param_env, symbol)
|
||||
}
|
||||
}
|
||||
|
||||
/// Eagerly replace aliases with inference variables, emitting `AliasRelate`
|
||||
|
|
|
|||
|
|
@ -148,6 +148,20 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn compute_unstable_feature_goal(
|
||||
&mut self,
|
||||
param_env: <I as Interner>::ParamEnv,
|
||||
symbol: <I as Interner>::Symbol,
|
||||
) -> QueryResult<I> {
|
||||
if self.may_use_unstable_feature(param_env, symbol) {
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
} else {
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe(
|
||||
MaybeCause::Ambiguity,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self))]
|
||||
fn compute_const_evaluatable_goal(
|
||||
&mut self,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ edition = "2024"
|
|||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
bitflags = "2.4.1"
|
||||
rustc-literal-escaper = "0.0.4"
|
||||
rustc-literal-escaper = "0.0.5"
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
|
|
|
|||
|
|
@ -855,6 +855,7 @@ parse_trailing_vert_not_allowed = a trailing `|` is not allowed in an or-pattern
|
|||
.suggestion = remove the `{$token}`
|
||||
|
||||
parse_trait_alias_cannot_be_auto = trait aliases cannot be `auto`
|
||||
parse_trait_alias_cannot_be_const = trait aliases cannot be `const`
|
||||
parse_trait_alias_cannot_be_unsafe = trait aliases cannot be `unsafe`
|
||||
|
||||
parse_transpose_dyn_or_impl = `for<...>` expected after `{$kw}`, not before
|
||||
|
|
|
|||
|
|
@ -1961,6 +1961,14 @@ pub(crate) struct TraitAliasCannotBeAuto {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(parse_trait_alias_cannot_be_const)]
|
||||
pub(crate) struct TraitAliasCannotBeConst {
|
||||
#[primary_span]
|
||||
#[label(parse_trait_alias_cannot_be_const)]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(parse_trait_alias_cannot_be_unsafe)]
|
||||
pub(crate) struct TraitAliasCannotBeUnsafe {
|
||||
|
|
|
|||
|
|
@ -244,6 +244,9 @@ impl<'a> Parser<'a> {
|
|||
self.bump(); // `static`
|
||||
let mutability = self.parse_mutability();
|
||||
self.parse_static_item(safety, mutability)?
|
||||
} else if self.check_keyword(exp!(Trait)) || self.check_trait_front_matter() {
|
||||
// TRAIT ITEM
|
||||
self.parse_item_trait(attrs, lo)?
|
||||
} else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) {
|
||||
// CONST ITEM
|
||||
if self.token.is_keyword(kw::Impl) {
|
||||
|
|
@ -262,9 +265,6 @@ impl<'a> Parser<'a> {
|
|||
define_opaque: None,
|
||||
}))
|
||||
}
|
||||
} else if self.check_keyword(exp!(Trait)) || self.check_auto_or_unsafe_trait_item() {
|
||||
// TRAIT ITEM
|
||||
self.parse_item_trait(attrs, lo)?
|
||||
} else if self.check_keyword(exp!(Impl))
|
||||
|| self.check_keyword(exp!(Unsafe)) && self.is_keyword_ahead(1, &[kw::Impl])
|
||||
{
|
||||
|
|
@ -373,7 +373,7 @@ impl<'a> Parser<'a> {
|
|||
pub(super) fn is_path_start_item(&mut self) -> bool {
|
||||
self.is_kw_followed_by_ident(kw::Union) // no: `union::b`, yes: `union U { .. }`
|
||||
|| self.is_reuse_path_item()
|
||||
|| self.check_auto_or_unsafe_trait_item() // no: `auto::b`, yes: `auto trait X { .. }`
|
||||
|| self.check_trait_front_matter() // no: `auto::b`, yes: `auto trait X { .. }`
|
||||
|| self.is_async_fn() // no(2015): `async::b`, yes: `async fn`
|
||||
|| matches!(self.is_macro_rules_item(), IsMacroRulesItem::Yes{..}) // no: `macro_rules::b`, yes: `macro_rules! mac`
|
||||
}
|
||||
|
|
@ -872,16 +872,19 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Is this an `(unsafe auto? | auto) trait` item?
|
||||
fn check_auto_or_unsafe_trait_item(&mut self) -> bool {
|
||||
/// Is this an `(const unsafe? auto?| unsafe auto? | auto) trait` item?
|
||||
fn check_trait_front_matter(&mut self) -> bool {
|
||||
// auto trait
|
||||
self.check_keyword(exp!(Auto)) && self.is_keyword_ahead(1, &[kw::Trait])
|
||||
// unsafe auto trait
|
||||
|| self.check_keyword(exp!(Unsafe)) && self.is_keyword_ahead(1, &[kw::Trait, kw::Auto])
|
||||
|| self.check_keyword(exp!(Const)) && ((self.is_keyword_ahead(1, &[kw::Trait]) || self.is_keyword_ahead(1, &[kw::Auto]) && self.is_keyword_ahead(2, &[kw::Trait]))
|
||||
|| self.is_keyword_ahead(1, &[kw::Unsafe]) && self.is_keyword_ahead(2, &[kw::Trait, kw::Auto]))
|
||||
}
|
||||
|
||||
/// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`.
|
||||
fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemKind> {
|
||||
let constness = self.parse_constness(Case::Sensitive);
|
||||
let safety = self.parse_safety(Case::Sensitive);
|
||||
// Parse optional `auto` prefix.
|
||||
let is_auto = if self.eat_keyword(exp!(Auto)) {
|
||||
|
|
@ -913,6 +916,9 @@ impl<'a> Parser<'a> {
|
|||
self.expect_semi()?;
|
||||
|
||||
let whole_span = lo.to(self.prev_token.span);
|
||||
if let Const::Yes(_) = constness {
|
||||
self.dcx().emit_err(errors::TraitAliasCannotBeConst { span: whole_span });
|
||||
}
|
||||
if is_auto == IsAuto::Yes {
|
||||
self.dcx().emit_err(errors::TraitAliasCannotBeAuto { span: whole_span });
|
||||
}
|
||||
|
|
@ -927,7 +933,15 @@ impl<'a> Parser<'a> {
|
|||
// It's a normal trait.
|
||||
generics.where_clause = self.parse_where_clause()?;
|
||||
let items = self.parse_item_list(attrs, |p| p.parse_trait_item(ForceCollect::No))?;
|
||||
Ok(ItemKind::Trait(Box::new(Trait { is_auto, safety, ident, generics, bounds, items })))
|
||||
Ok(ItemKind::Trait(Box::new(Trait {
|
||||
constness,
|
||||
is_auto,
|
||||
safety,
|
||||
ident,
|
||||
generics,
|
||||
bounds,
|
||||
items,
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
rustc-literal-escaper = "0.0.4"
|
||||
rustc-literal-escaper = "0.0.5"
|
||||
rustc_lexer = { path = "../rustc_lexer" }
|
||||
# tidy-alphabetical-end
|
||||
|
||||
|
|
|
|||
|
|
@ -667,6 +667,10 @@ passes_rustc_std_internal_symbol =
|
|||
attribute should be applied to functions or statics
|
||||
.label = not a function or static
|
||||
|
||||
passes_rustc_unstable_feature_bound =
|
||||
attribute should be applied to `impl` or free function outside of any `impl` or trait
|
||||
.label = not an `impl` or free function
|
||||
|
||||
passes_should_be_applied_to_fn =
|
||||
attribute should be applied to a function definition
|
||||
.label = {$on_crate ->
|
||||
|
|
@ -780,7 +784,7 @@ passes_unused_capture_maybe_capture_ref = value captured by `{$name}` is never r
|
|||
.help = did you mean to capture by reference instead?
|
||||
|
||||
passes_unused_default_method_body_const_note =
|
||||
`default_method_body_is_const` has been replaced with `#[const_trait]` on traits
|
||||
`default_method_body_is_const` has been replaced with `const` on traits
|
||||
|
||||
passes_unused_duplicate =
|
||||
unused attribute
|
||||
|
|
|
|||
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