Merge pull request #4474 from rust-lang/rustup-2025-07-18

Automatic Rustup
This commit is contained in:
Ralf Jung 2025-07-18 06:14:25 +00:00 committed by GitHub
commit 83395909d0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
378 changed files with 4070 additions and 2187 deletions

View file

@ -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"

View file

@ -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" }

View file

@ -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,

View file

@ -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")
}

View file

@ -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));

View file

@ -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);

View file

@ -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

View file

@ -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);
});
}

View file

@ -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 {

View file

@ -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");

View file

@ -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

View file

@ -71,6 +71,7 @@ impl AttributeKind {
TrackCaller(..) => Yes,
TypeConst(..) => Yes,
UnsafeSpecializationMarker(..) => No,
UnstableFeatureBound(..) => No,
Used { .. } => No,
// tidy-alphabetical-end
}

View file

@ -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 =

View file

@ -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];

View file

@ -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))) {

View file

@ -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 })

View file

@ -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;

View file

@ -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 {

View file

@ -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" }

View file

@ -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 {

View file

@ -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))))
}

View file

@ -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 {

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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}

View file

@ -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

View file

@ -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();

View file

@ -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);

View file

@ -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;

View file

@ -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 {

View file

@ -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>),

View file

@ -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);
}

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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));
}

View file

@ -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.

View file

@ -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));
}
}
}
}

View file

@ -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))

View file

@ -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,

View file

@ -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 {

View file

@ -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`.

View file

@ -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);

View file

@ -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

View file

@ -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(

View file

@ -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};

View file

@ -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(())

View file

@ -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.

View file

@ -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();

View file

@ -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,

View file

@ -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`

View file

@ -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.

View file

@ -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

View file

@ -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);

View file

@ -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,

View file

@ -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);

View file

@ -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,

View file

@ -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 =

View file

@ -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()
}

View file

@ -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,

View file

@ -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);

View file

@ -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

View file

@ -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,
};

View file

@ -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:?}"),

View file

@ -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.

View file

@ -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"
)]

View file

@ -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!(),
}

View file

@ -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!(),
};

View file

@ -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,
}
}

View file

@ -54,6 +54,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> {
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_)
| ty::ClauseKind::UnstableFeature(_)
| ty::ClauseKind::HostEffect(..) => {}
}
}

View file

@ -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");

View file

@ -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,
}
}

View file

@ -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);
}

View file

@ -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,
}
});

View file

@ -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() {

View file

@ -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}`

View file

@ -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(..)

View file

@ -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);
}

View file

@ -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;

View file

@ -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()
}

View file

@ -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,

View file

@ -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

View file

@ -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;
}
}

View file

@ -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,

View file

@ -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)

View file

@ -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,
..
})

View file

@ -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,

View file

@ -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 {

View file

@ -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(..)

View file

@ -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)),
}
}

View file

@ -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]`

View file

@ -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`

View file

@ -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,

View file

@ -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" }

View file

@ -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

View file

@ -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 {

View file

@ -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,
})))
}
}

View file

@ -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

View file

@ -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