Merge pull request #4532 from rust-lang/rustup-2025-08-20

Automatic Rustup
This commit is contained in:
Ralf Jung 2025-08-20 08:14:57 +00:00 committed by GitHub
commit 46765526e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
609 changed files with 15878 additions and 9046 deletions

View file

@ -4812,6 +4812,7 @@ dependencies = [
"serde_json",
"sha2",
"smallvec",
"stringdex",
"tempfile",
"threadpool",
"tracing",
@ -5225,6 +5226,15 @@ dependencies = [
"quote",
]
[[package]]
name = "stringdex"
version = "0.0.1-alpha4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2841fd43df5b1ff1b042e167068a1fe9b163dc93041eae56ab2296859013a9a0"
dependencies = [
"stacker",
]
[[package]]
name = "strsim"
version = "0.11.1"

View file

@ -12,9 +12,11 @@ attr_parsing_empty_attribute =
attr_parsing_invalid_target = `#[{$name}]` attribute cannot be used on {$target}
.help = `#[{$name}]` can {$only}be applied to {$applied}
.suggestion = remove the attribute
attr_parsing_invalid_target_lint = `#[{$name}]` attribute cannot be used on {$target}
.warn = {-attr_parsing_previously_accepted}
.help = `#[{$name}]` can {$only}be applied to {$applied}
.suggestion = remove the attribute
attr_parsing_empty_confusables =
expected at least one confusable name

View file

@ -54,6 +54,8 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
Allow(Target::TyAlias),
Allow(Target::Use),
Allow(Target::ForeignFn),
Allow(Target::ForeignStatic),
Allow(Target::ForeignTy),
Allow(Target::Field),
Allow(Target::Trait),
Allow(Target::AssocTy),

View file

@ -62,8 +62,8 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
}
}
ArgParser::NameValue(_) => {
let suggestions =
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "inline");
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(cx.attr_style, "inline");
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;

View file

@ -107,7 +107,7 @@ impl<S: Stage> AttributeParser<S> for MacroUseParser {
}
}
ArgParser::NameValue(_) => {
let suggestions = MACRO_USE_TEMPLATE.suggestions(false, sym::macro_use);
let suggestions = MACRO_USE_TEMPLATE.suggestions(cx.attr_style, sym::macro_use);
cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(

View file

@ -43,6 +43,7 @@ pub(crate) mod no_implicit_prelude;
pub(crate) mod non_exhaustive;
pub(crate) mod path;
pub(crate) mod proc_macro_attrs;
pub(crate) mod prototype;
pub(crate) mod repr;
pub(crate) mod rustc_internal;
pub(crate) mod semantics;

View file

@ -1,10 +1,12 @@
use rustc_errors::DiagArgValue;
use rustc_feature::{AttributeTemplate, template};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::{MethodKind, Target};
use rustc_span::{Symbol, sym};
use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
use crate::context::{ALL_TARGETS, AcceptContext, AllowedTargets, Stage};
use crate::context::MaybeWarn::{Allow, Error};
use crate::context::{AcceptContext, AllowedTargets, Stage};
use crate::parser::ArgParser;
use crate::session_diagnostics;
pub(crate) struct MustUseParser;
@ -13,7 +15,21 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
const PATH: &[Symbol] = &[sym::must_use];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs`
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
Allow(Target::Fn),
Allow(Target::Enum),
Allow(Target::Struct),
Allow(Target::Union),
Allow(Target::Method(MethodKind::Trait { body: false })),
Allow(Target::Method(MethodKind::Trait { body: true })),
Allow(Target::Method(MethodKind::Inherent)),
Allow(Target::ForeignFn),
// `impl Trait` in return position can trip
// `unused_must_use` if `Trait` is marked as
// `#[must_use]`
Allow(Target::Trait),
Error(Target::WherePredicate),
]);
const TEMPLATE: AttributeTemplate = template!(
Word, NameValueStr: "reason",
"https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"
@ -35,8 +51,8 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
Some(value_str)
}
ArgParser::List(_) => {
let suggestions =
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "must_use");
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(cx.attr_style, "must_use");
cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(

View file

@ -0,0 +1,140 @@
//! Attributes that are only used on function prototypes.
use rustc_feature::{AttributeTemplate, template};
use rustc_hir::Target;
use rustc_hir::attrs::{AttributeKind, MirDialect, MirPhase};
use rustc_span::{Span, Symbol, sym};
use super::{AttributeOrder, OnDuplicate};
use crate::attributes::SingleAttributeParser;
use crate::context::{AcceptContext, AllowedTargets, MaybeWarn, Stage};
use crate::parser::ArgParser;
pub(crate) struct CustomMirParser;
impl<S: Stage> SingleAttributeParser<S> for CustomMirParser {
const PATH: &[rustc_span::Symbol] = &[sym::custom_mir];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[MaybeWarn::Allow(Target::Fn)]);
const TEMPLATE: AttributeTemplate = template!(List: &[r#"dialect = "...", phase = "...""#]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
return None;
};
let mut dialect = None;
let mut phase = None;
let mut failed = false;
for item in list.mixed() {
let Some(meta_item) = item.meta_item() else {
cx.expected_name_value(item.span(), None);
failed = true;
break;
};
if let Some(arg) = meta_item.word_is(sym::dialect) {
extract_value(cx, sym::dialect, arg, meta_item.span(), &mut dialect, &mut failed);
} else if let Some(arg) = meta_item.word_is(sym::phase) {
extract_value(cx, sym::phase, arg, meta_item.span(), &mut phase, &mut failed);
} else if let Some(word) = meta_item.path().word() {
let word = word.to_string();
cx.unknown_key(meta_item.span(), word, &["dialect", "phase"]);
failed = true;
} else {
cx.expected_name_value(meta_item.span(), None);
failed = true;
};
}
let dialect = parse_dialect(cx, dialect, &mut failed);
let phase = parse_phase(cx, phase, &mut failed);
if failed {
return None;
}
Some(AttributeKind::CustomMir(dialect, phase, cx.attr_span))
}
}
fn extract_value<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
key: Symbol,
arg: &ArgParser<'_>,
span: Span,
out_val: &mut Option<(Symbol, Span)>,
failed: &mut bool,
) {
if out_val.is_some() {
cx.duplicate_key(span, key);
*failed = true;
return;
}
let Some(val) = arg.name_value() else {
cx.expected_single_argument(arg.span().unwrap_or(span));
*failed = true;
return;
};
let Some(value_sym) = val.value_as_str() else {
cx.expected_string_literal(val.value_span, Some(val.value_as_lit()));
*failed = true;
return;
};
*out_val = Some((value_sym, val.value_span));
}
fn parse_dialect<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
dialect: Option<(Symbol, Span)>,
failed: &mut bool,
) -> Option<(MirDialect, Span)> {
let (dialect, span) = dialect?;
let dialect = match dialect {
sym::analysis => MirDialect::Analysis,
sym::built => MirDialect::Built,
sym::runtime => MirDialect::Runtime,
_ => {
cx.expected_specific_argument(span, vec!["analysis", "built", "runtime"]);
*failed = true;
return None;
}
};
Some((dialect, span))
}
fn parse_phase<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
phase: Option<(Symbol, Span)>,
failed: &mut bool,
) -> Option<(MirPhase, Span)> {
let (phase, span) = phase?;
let phase = match phase {
sym::initial => MirPhase::Initial,
sym::post_cleanup => MirPhase::PostCleanup,
sym::optimized => MirPhase::Optimized,
_ => {
cx.expected_specific_argument(span, vec!["initial", "post-cleanup", "optimized"]);
*failed = true;
return None;
}
};
Some((phase, span))
}

View file

@ -54,6 +54,7 @@ const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Static),
Allow(Target::ForeignFn),
Allow(Target::ForeignStatic),
Allow(Target::ExternCrate),
]);
#[derive(Default)]

View file

@ -29,7 +29,7 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
ArgParser::NameValue(name_value) => {
let Some(str_value) = name_value.value_as_str() else {
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(false, "ignore");
.suggestions(cx.attr_style, "ignore");
let span = cx.attr_span;
cx.emit_lint(
AttributeLintKind::IllFormedAttributeInput { suggestions },
@ -40,8 +40,8 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
Some(str_value)
}
ArgParser::List(_) => {
let suggestions =
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "ignore");
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(cx.attr_style, "ignore");
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;

View file

@ -5,7 +5,7 @@ use std::sync::LazyLock;
use itertools::Itertools;
use private::Sealed;
use rustc_ast::{self as ast, LitKind, MetaItemLit, NodeId};
use rustc_ast::{self as ast, AttrStyle, LitKind, MetaItemLit, NodeId};
use rustc_errors::{DiagCtxtHandle, Diagnostic};
use rustc_feature::{AttributeTemplate, Features};
use rustc_hir::attrs::AttributeKind;
@ -46,6 +46,7 @@ use crate::attributes::path::PathParser as PathAttributeParser;
use crate::attributes::proc_macro_attrs::{
ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser,
};
use crate::attributes::prototype::CustomMirParser;
use crate::attributes::repr::{AlignParser, ReprParser};
use crate::attributes::rustc_internal::{
RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart,
@ -167,6 +168,7 @@ attribute_parsers!(
// tidy-alphabetical-start
Single<CoverageParser>,
Single<CustomMirParser>,
Single<DeprecationParser>,
Single<DummyParser>,
Single<ExportNameParser>,
@ -313,6 +315,7 @@ pub struct AcceptContext<'f, 'sess, S: Stage> {
/// The span of the attribute currently being parsed
pub(crate) attr_span: Span,
pub(crate) attr_style: AttrStyle,
/// The expected structure of the attribute.
///
/// Used in reporting errors to give a hint to users what the attribute *should* look like.
@ -394,6 +397,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span))
}),
},
attr_style: self.attr_style,
})
}
@ -404,6 +408,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedIntegerLiteral,
attr_style: self.attr_style,
})
}
@ -414,6 +419,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedList,
attr_style: self.attr_style,
})
}
@ -424,6 +430,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedNoArgs,
attr_style: self.attr_style,
})
}
@ -435,6 +442,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedIdentifier,
attr_style: self.attr_style,
})
}
@ -447,6 +455,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedNameValue(name),
attr_style: self.attr_style,
})
}
@ -458,6 +467,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::DuplicateKey(key),
attr_style: self.attr_style,
})
}
@ -470,6 +480,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::UnexpectedLiteral,
attr_style: self.attr_style,
})
}
@ -480,6 +491,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedSingleArgument,
attr_style: self.attr_style,
})
}
@ -490,6 +502,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedAtLeastOneArgument,
attr_style: self.attr_style,
})
}
@ -508,6 +521,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
strings: false,
list: false,
},
attr_style: self.attr_style,
})
}
@ -526,6 +540,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
strings: false,
list: true,
},
attr_style: self.attr_style,
})
}
@ -544,6 +559,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
strings: true,
list: false,
},
attr_style: self.attr_style,
})
}
@ -802,6 +818,7 @@ impl<'sess> AttributeParser<'sess, Early> {
},
},
attr_span: attr.span,
attr_style: attr.style,
template,
attr_path: path.get_attribute_path(),
};
@ -912,6 +929,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
emit_lint: &mut emit_lint,
},
attr_span: lower_span(attr.span),
attr_style: attr.style,
template: &accept.template,
attr_path: path.get_attribute_path(),
};
@ -1060,6 +1078,9 @@ pub(crate) fn allowed_targets_applied(
if !features.stmt_expr_attributes() {
allowed_targets.retain(|t| !matches!(t, Target::Expression | Target::Statement));
}
if !features.extern_types() {
allowed_targets.retain(|t| !matches!(t, Target::ForeignTy));
}
}
// We define groups of "similar" targets.

View file

@ -53,6 +53,7 @@ pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<HirId>, lint_emi
target: target.plural_name(),
applied: applied.clone(),
only,
attr_span: *span,
},
),
}

View file

@ -1,6 +1,6 @@
use std::num::IntErrorKind;
use rustc_ast as ast;
use rustc_ast::{self as ast, AttrStyle};
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
@ -489,6 +489,8 @@ pub(crate) struct InvalidTargetLint {
pub target: &'static str,
pub applied: String,
pub only: &'static str,
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
pub attr_span: Span,
}
#[derive(Diagnostic)]
@ -496,6 +498,7 @@ pub(crate) struct InvalidTargetLint {
#[diag(attr_parsing_invalid_target)]
pub(crate) struct InvalidTarget {
#[primary_span]
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
pub span: Span,
pub name: Symbol,
pub target: &'static str,
@ -579,6 +582,7 @@ pub(crate) enum AttributeParseErrorReason {
pub(crate) struct AttributeParseError {
pub(crate) span: Span,
pub(crate) attr_span: Span,
pub(crate) attr_style: AttrStyle,
pub(crate) template: AttributeTemplate,
pub(crate) attribute: AttrPath,
pub(crate) reason: AttributeParseErrorReason,
@ -717,7 +721,8 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
if let Some(link) = self.template.docs {
diag.note(format!("for more information, visit <{link}>"));
}
let suggestions = self.template.suggestions(false, &name);
let suggestions = self.template.suggestions(self.attr_style, &name);
diag.span_suggestions(
self.attr_span,
if suggestions.len() == 1 {

View file

@ -90,7 +90,7 @@ borrowck_lifetime_constraints_error =
lifetime may not live long enough
borrowck_limitations_implies_static =
due to current limitations in the borrow checker, this implies a `'static` lifetime
due to a current limitation of the type system, this implies a `'static` lifetime
borrowck_move_closure_suggestion =
consider adding 'move' keyword before the nested closure

View file

@ -6,7 +6,9 @@ use rustc_abi::{FieldIdx, VariantIdx};
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, listify};
use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::{self as hir, CoroutineKind, LangItem};
use rustc_hir::{
self as hir, CoroutineKind, GenericBound, LangItem, WhereBoundPredicate, WherePredicateKind,
};
use rustc_index::{IndexSlice, IndexVec};
use rustc_infer::infer::{BoundRegionConversionTime, NllRegionVariableOrigin};
use rustc_infer::traits::SelectionError;
@ -658,25 +660,66 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
/// Add a note to region errors and borrow explanations when higher-ranked regions in predicates
/// implicitly introduce an "outlives `'static`" constraint.
///
/// This is very similar to `fn suggest_static_lifetime_for_gat_from_hrtb` which handles this
/// note for failed type tests instead of outlives errors.
fn add_placeholder_from_predicate_note<G: EmissionGuarantee>(
&self,
err: &mut Diag<'_, G>,
diag: &mut Diag<'_, G>,
path: &[OutlivesConstraint<'tcx>],
) {
let predicate_span = path.iter().find_map(|constraint| {
let tcx = self.infcx.tcx;
let Some((gat_hir_id, generics)) = path.iter().find_map(|constraint| {
let outlived = constraint.sub;
if let Some(origin) = self.regioncx.definitions.get(outlived)
&& let NllRegionVariableOrigin::Placeholder(_) = origin.origin
&& let ConstraintCategory::Predicate(span) = constraint.category
&& let NllRegionVariableOrigin::Placeholder(placeholder) = origin.origin
&& let Some(id) = placeholder.bound.kind.get_id()
&& let Some(placeholder_id) = id.as_local()
&& let gat_hir_id = tcx.local_def_id_to_hir_id(placeholder_id)
&& let Some(generics_impl) =
tcx.parent_hir_node(tcx.parent_hir_id(gat_hir_id)).generics()
{
Some(span)
Some((gat_hir_id, generics_impl))
} else {
None
}
});
}) else {
return;
};
if let Some(span) = predicate_span {
err.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
// Look for the where-bound which introduces the placeholder.
// As we're using the HIR, we need to handle both `for<'a> T: Trait<'a>`
// and `T: for<'a> Trait`<'a>.
for pred in generics.predicates {
let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
bound_generic_params,
bounds,
..
}) = pred.kind
else {
continue;
};
if bound_generic_params
.iter()
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
.is_some()
{
diag.span_note(pred.span, fluent::borrowck_limitations_implies_static);
return;
}
for bound in bounds.iter() {
if let GenericBound::Trait(bound) = bound {
if bound
.bound_generic_params
.iter()
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
.is_some()
{
diag.span_note(bound.span, fluent::borrowck_limitations_implies_static);
return;
}
}
}
}
}

View file

@ -3,6 +3,7 @@
use core::ops::ControlFlow;
use either::Either;
use hir::{ExprKind, Param};
use rustc_abi::FieldIdx;
use rustc_errors::{Applicability, Diag};
@ -12,15 +13,16 @@ use rustc_middle::bug;
use rustc_middle::hir::place::PlaceBase;
use rustc_middle::mir::visit::PlaceContext;
use rustc_middle::mir::{
self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location, Mutability, Place,
PlaceRef, ProjectionElem,
self, BindingForm, Body, BorrowKind, Local, LocalDecl, LocalInfo, LocalKind, Location,
Mutability, Operand, Place, PlaceRef, ProjectionElem, RawPtrKind, Rvalue, Statement,
StatementKind, TerminatorKind,
};
use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, Upcast};
use rustc_span::{BytePos, DesugaringKind, Span, Symbol, kw, sym};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits;
use tracing::debug;
use tracing::{debug, trace};
use crate::diagnostics::BorrowedContentSource;
use crate::{MirBorrowckCtxt, session_diagnostics};
@ -31,6 +33,33 @@ pub(crate) enum AccessKind {
Mutate,
}
/// Finds all statements that assign directly to local (i.e., X = ...) and returns their
/// locations.
fn find_assignments(body: &Body<'_>, local: Local) -> Vec<Location> {
use rustc_middle::mir::visit::Visitor;
struct FindLocalAssignmentVisitor {
needle: Local,
locations: Vec<Location>,
}
impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor {
fn visit_local(&mut self, local: Local, place_context: PlaceContext, location: Location) {
if self.needle != local {
return;
}
if place_context.is_place_assignment() {
self.locations.push(location);
}
}
}
let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] };
visitor.visit_body(body);
visitor.locations
}
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
pub(crate) fn report_mutability_error(
&mut self,
@ -384,7 +413,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
}
// Also suggest adding mut for upvars
// Also suggest adding mut for upvars.
PlaceRef {
local,
projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
@ -438,9 +467,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
}
// complete hack to approximate old AST-borrowck
// diagnostic: if the span starts with a mutable borrow of
// a local variable, then just suggest the user remove it.
// Complete hack to approximate old AST-borrowck diagnostic: if the span starts
// with a mutable borrow of a local variable, then just suggest the user remove it.
PlaceRef { local: _, projection: [] }
if self
.infcx
@ -769,7 +797,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
);
}
// point to span of upvar making closure call require mutable borrow
// Point to span of upvar making closure call that requires a mutable borrow
fn show_mutating_upvar(
&self,
tcx: TyCtxt<'_>,
@ -825,7 +853,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
} else {
bug!("not an upvar")
};
// sometimes we deliberately don't store the name of a place when coming from a macro in
// Sometimes we deliberately don't store the name of a place when coming from a macro in
// another crate. We generally want to limit those diagnostics a little, to hide
// implementation details (such as those from pin!() or format!()). In that case show a
// slightly different error message, or none at all if something else happened. In other
@ -936,8 +964,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
let def_id = tcx.hir_enclosing_body_owner(fn_call_id);
let mut look_at_return = true;
// If the HIR node is a function or method call gets the def ID
// of the called function or method and the span and args of the call expr
// If the HIR node is a function or method call, get the DefId
// of the callee function or method, the span, and args of the call expr
let get_call_details = || {
let hir::Node::Expr(hir::Expr { hir_id, kind, .. }) = node else {
return None;
@ -1051,7 +1079,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
let mut cur_expr = expr;
while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind {
if path_segment.ident.name == sym::iter {
// check `_ty` has `iter_mut` method
// Check that the type has an `iter_mut` method.
let res = self
.infcx
.tcx
@ -1081,38 +1109,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
}
/// Finds all statements that assign directly to local (i.e., X = ...) and returns their
/// locations.
fn find_assignments(&self, local: Local) -> Vec<Location> {
use rustc_middle::mir::visit::Visitor;
struct FindLocalAssignmentVisitor {
needle: Local,
locations: Vec<Location>,
}
impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor {
fn visit_local(
&mut self,
local: Local,
place_context: PlaceContext,
location: Location,
) {
if self.needle != local {
return;
}
if place_context.is_place_assignment() {
self.locations.push(location);
}
}
}
let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] };
visitor.visit_body(self.body);
visitor.locations
}
fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) {
let local_decl = &self.body.local_decls[local];
@ -1122,7 +1118,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
let (is_trait_sig, is_local, local_trait) = self.is_error_in_trait(local);
if is_trait_sig && !is_local {
// Do not suggest to change the signature when the trait comes from another crate.
// Do not suggest changing the signature when the trait comes from another crate.
err.span_label(
local_decl.source_info.span,
format!("this is an immutable {pointer_desc}"),
@ -1131,11 +1127,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
let decl_span = local_decl.source_info.span;
let amp_mut_sugg = match *local_decl.local_info() {
let (amp_mut_sugg, local_var_ty_info) = match *local_decl.local_info() {
LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span);
let additional = local_trait.map(|span| suggest_ampmut_self(self.infcx.tcx, span));
Some(AmpMutSugg { has_sugg: true, span, suggestion, additional })
(AmpMutSugg::Type { span, suggestion, additional }, None)
}
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
@ -1143,79 +1139,54 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
opt_ty_info,
..
})) => {
// check if the RHS is from desugaring
// Check if the RHS is from desugaring.
let first_assignment = find_assignments(&self.body, local).first().copied();
let first_assignment_stmt = first_assignment
.and_then(|loc| self.body[loc.block].statements.get(loc.statement_index));
trace!(?first_assignment_stmt);
let opt_assignment_rhs_span =
self.find_assignments(local).first().map(|&location| {
if let Some(mir::Statement {
source_info: _,
kind:
mir::StatementKind::Assign(box (
_,
mir::Rvalue::Use(mir::Operand::Copy(place)),
)),
..
}) = self.body[location.block].statements.get(location.statement_index)
{
self.body.local_decls[place.local].source_info.span
} else {
self.body.source_info(location).span
}
});
match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
// on for loops, RHS points to the iterator part
Some(DesugaringKind::ForLoop) => {
let span = opt_assignment_rhs_span.unwrap();
self.suggest_similar_mut_method_for_for_loop(err, span);
first_assignment.map(|loc| self.body.source_info(loc).span);
let mut source_span = opt_assignment_rhs_span;
if let Some(mir::Statement {
source_info: _,
kind:
mir::StatementKind::Assign(box (_, mir::Rvalue::Use(mir::Operand::Copy(place)))),
..
}) = first_assignment_stmt
{
let local_span = self.body.local_decls[place.local].source_info.span;
// `&self` in async functions have a `desugaring_kind`, but the local we assign
// it with does not, so use the local_span for our checks later.
source_span = Some(local_span);
if let Some(DesugaringKind::ForLoop) = local_span.desugaring_kind() {
// On for loops, RHS points to the iterator part.
self.suggest_similar_mut_method_for_for_loop(err, local_span);
err.span_label(
span,
local_span,
format!("this iterator yields `{pointer_sigil}` {pointer_desc}s",),
);
None
}
// don't create labels for compiler-generated spans
Some(_) => None,
// don't create labels for the span not from user's code
None if opt_assignment_rhs_span
.is_some_and(|span| self.infcx.tcx.sess.source_map().is_imported(span)) =>
{
None
}
None => {
if name != kw::SelfLower {
suggest_ampmut(
self.infcx.tcx,
local_decl.ty,
decl_span,
opt_assignment_rhs_span,
opt_ty_info,
)
} else {
match local_decl.local_info() {
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
opt_ty_info: None,
..
})) => {
let (span, sugg) =
suggest_ampmut_self(self.infcx.tcx, decl_span);
Some(AmpMutSugg {
has_sugg: true,
span,
suggestion: sugg,
additional: None,
})
}
// explicit self (eg `self: &'a Self`)
_ => suggest_ampmut(
self.infcx.tcx,
local_decl.ty,
decl_span,
opt_assignment_rhs_span,
opt_ty_info,
),
}
}
return;
}
}
// Don't create labels for compiler-generated spans or spans not from users' code.
if source_span.is_some_and(|s| {
s.desugaring_kind().is_some() || self.infcx.tcx.sess.source_map().is_imported(s)
}) {
return;
}
// This could be because we're in an `async fn`.
if name == kw::SelfLower && opt_ty_info.is_none() {
let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span);
(AmpMutSugg::Type { span, suggestion, additional: None }, None)
} else if let Some(sugg) =
suggest_ampmut(self.infcx, self.body(), first_assignment_stmt)
{
(sugg, opt_ty_info)
} else {
return;
}
}
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
@ -1223,181 +1194,238 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
..
})) => {
let pattern_span: Span = local_decl.source_info.span;
suggest_ref_mut(self.infcx.tcx, pattern_span).map(|span| AmpMutSugg {
has_sugg: true,
span,
suggestion: "mut ".to_owned(),
additional: None,
})
let Some(span) = suggest_ref_mut(self.infcx.tcx, pattern_span) else {
return;
};
(AmpMutSugg::Type { span, suggestion: "mut ".to_owned(), additional: None }, None)
}
_ => unreachable!(),
};
match amp_mut_sugg {
Some(AmpMutSugg {
has_sugg: true,
span: err_help_span,
suggestion: suggested_code,
additional,
}) => {
let mut sugg = vec![(err_help_span, suggested_code)];
if let Some(s) = additional {
sugg.push(s);
}
let mut suggest = |suggs: Vec<_>, applicability, extra| {
if suggs.iter().any(|(span, _)| self.infcx.tcx.sess.source_map().is_imported(*span)) {
return;
}
if sugg.iter().all(|(span, _)| !self.infcx.tcx.sess.source_map().is_imported(*span))
{
err.multipart_suggestion_verbose(
format!(
"consider changing this to be a mutable {pointer_desc}{}",
if is_trait_sig {
" in the `impl` method and the `trait` definition"
} else {
""
}
),
sugg,
Applicability::MachineApplicable,
err.multipart_suggestion_verbose(
format!(
"consider changing this to be a mutable {pointer_desc}{}{extra}",
if is_trait_sig {
" in the `impl` method and the `trait` definition"
} else {
""
}
),
suggs,
applicability,
);
};
let (mut sugg, add_type_annotation_if_not_exists) = match amp_mut_sugg {
AmpMutSugg::Type { span, suggestion, additional } => {
let mut sugg = vec![(span, suggestion)];
sugg.extend(additional);
suggest(sugg, Applicability::MachineApplicable, "");
return;
}
AmpMutSugg::MapGetMut { span, suggestion } => {
if self.infcx.tcx.sess.source_map().is_imported(span) {
return;
}
err.multipart_suggestion_verbose(
"consider using `get_mut`",
vec![(span, suggestion)],
Applicability::MaybeIncorrect,
);
return;
}
AmpMutSugg::Expr { span, suggestion } => {
// `Expr` suggestions should change type annotations if they already exist (probably immut),
// but do not add new type annotations.
(vec![(span, suggestion)], false)
}
AmpMutSugg::ChangeBinding => (vec![], true),
};
// Find a binding's type to make mutable.
let (binding_exists, span) = match local_var_ty_info {
// If this is a variable binding with an explicit type,
// then we will suggest changing it to be mutable.
// This is `Applicability::MachineApplicable`.
Some(ty_span) => (true, ty_span),
// Otherwise, we'll suggest *adding* an annotated type, we'll suggest
// the RHS's type for that.
// This is `Applicability::HasPlaceholders`.
None => (false, decl_span),
};
if !binding_exists && !add_type_annotation_if_not_exists {
suggest(sugg, Applicability::MachineApplicable, "");
return;
}
// If the binding already exists and is a reference with an explicit
// lifetime, then we can suggest adding ` mut`. This is special-cased from
// the path without an explicit lifetime.
let (sugg_span, sugg_str, suggest_now) = if let Ok(src) = self.infcx.tcx.sess.source_map().span_to_snippet(span)
&& src.starts_with("&'")
// Note that `&' a T` is invalid so this is correct.
&& let Some(ws_pos) = src.find(char::is_whitespace)
{
let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
(span, " mut".to_owned(), true)
// If there is already a binding, we modify it to be `mut`.
} else if binding_exists {
// Shrink the span to just after the `&` in `&variable`.
let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
(span, "mut ".to_owned(), true)
} else {
// Otherwise, suggest that the user annotates the binding; We provide the
// type of the local.
let ty = local_decl.ty.builtin_deref(true).unwrap();
(span, format!("{}mut {}", if local_decl.ty.is_ref() { "&" } else { "*" }, ty), false)
};
if suggest_now {
// Suggest changing `&x` to `&mut x` and changing `&T` to `&mut T` at the same time.
let has_change = !sugg.is_empty();
sugg.push((sugg_span, sugg_str));
suggest(
sugg,
Applicability::MachineApplicable,
// FIXME(fee1-dead) this somehow doesn't fire
if has_change { " and changing the binding's type" } else { "" },
);
return;
} else if !sugg.is_empty() {
suggest(sugg, Applicability::MachineApplicable, "");
return;
}
let def_id = self.body.source.def_id();
let hir_id = if let Some(local_def_id) = def_id.as_local()
&& let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
{
BindingFinder { span: sugg_span }.visit_body(&body).break_value()
} else {
None
};
let node = hir_id.map(|hir_id| self.infcx.tcx.hir_node(hir_id));
let Some(hir::Node::LetStmt(local)) = node else {
err.span_label(
sugg_span,
format!("consider changing this binding's type to be: `{sugg_str}`"),
);
return;
};
let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap());
if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
&& let Some(expr) = local.init
&& let ty = tables.node_type_opt(expr.hir_id)
&& let Some(ty) = ty
&& let ty::Ref(..) = ty.kind()
{
match self
.infcx
.type_implements_trait_shallow(clone_trait, ty.peel_refs(), self.infcx.param_env)
.as_deref()
{
Some([]) => {
// FIXME: This error message isn't useful, since we're just
// vaguely suggesting to clone a value that already
// implements `Clone`.
//
// A correct suggestion here would take into account the fact
// that inference may be affected by missing types on bindings,
// etc., to improve "tests/ui/borrowck/issue-91206.stderr", for
// example.
}
None => {
if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind
&& segment.ident.name == sym::clone
{
err.span_help(
span,
format!(
"`{}` doesn't implement `Clone`, so this call clones \
the reference `{ty}`",
ty.peel_refs(),
),
);
}
// The type doesn't implement Clone.
let trait_ref = ty::Binder::dummy(ty::TraitRef::new(
self.infcx.tcx,
clone_trait,
[ty.peel_refs()],
));
let obligation = traits::Obligation::new(
self.infcx.tcx,
traits::ObligationCause::dummy(),
self.infcx.param_env,
trait_ref,
);
self.infcx.err_ctxt().suggest_derive(
&obligation,
err,
trait_ref.upcast(self.infcx.tcx),
);
}
}
Some(AmpMutSugg {
has_sugg: false, span: err_label_span, suggestion: message, ..
}) => {
let def_id = self.body.source.def_id();
let hir_id = if let Some(local_def_id) = def_id.as_local()
&& let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
{
BindingFinder { span: err_label_span }.visit_body(&body).break_value()
} else {
None
};
if let Some(hir_id) = hir_id
&& let hir::Node::LetStmt(local) = self.infcx.tcx.hir_node(hir_id)
{
let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap());
if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
&& let Some(expr) = local.init
&& let ty = tables.node_type_opt(expr.hir_id)
&& let Some(ty) = ty
&& let ty::Ref(..) = ty.kind()
Some(errors) => {
if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind
&& segment.ident.name == sym::clone
{
match self
.infcx
.type_implements_trait_shallow(
clone_trait,
ty.peel_refs(),
self.infcx.param_env,
)
.as_deref()
{
Some([]) => {
// FIXME: This error message isn't useful, since we're just
// vaguely suggesting to clone a value that already
// implements `Clone`.
//
// A correct suggestion here would take into account the fact
// that inference may be affected by missing types on bindings,
// etc., to improve "tests/ui/borrowck/issue-91206.stderr", for
// example.
}
None => {
if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) =
expr.kind
&& segment.ident.name == sym::clone
{
err.span_help(
span,
format!(
"`{}` doesn't implement `Clone`, so this call clones \
the reference `{ty}`",
ty.peel_refs(),
),
);
}
// The type doesn't implement Clone.
let trait_ref = ty::Binder::dummy(ty::TraitRef::new(
self.infcx.tcx,
clone_trait,
[ty.peel_refs()],
));
let obligation = traits::Obligation::new(
self.infcx.tcx,
traits::ObligationCause::dummy(),
self.infcx.param_env,
trait_ref,
);
self.infcx.err_ctxt().suggest_derive(
&obligation,
err,
trait_ref.upcast(self.infcx.tcx),
);
}
Some(errors) => {
if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) =
expr.kind
&& segment.ident.name == sym::clone
{
err.span_help(
span,
format!(
"`{}` doesn't implement `Clone` because its \
err.span_help(
span,
format!(
"`{}` doesn't implement `Clone` because its \
implementations trait bounds could not be met, so \
this call clones the reference `{ty}`",
ty.peel_refs(),
),
);
err.note(format!(
"the following trait bounds weren't met: {}",
errors
.iter()
.map(|e| e.obligation.predicate.to_string())
.collect::<Vec<_>>()
.join("\n"),
));
}
// The type doesn't implement Clone because of unmet obligations.
for error in errors {
if let traits::FulfillmentErrorCode::Select(
traits::SelectionError::Unimplemented,
) = error.code
&& let ty::PredicateKind::Clause(ty::ClauseKind::Trait(
pred,
)) = error.obligation.predicate.kind().skip_binder()
{
self.infcx.err_ctxt().suggest_derive(
&error.obligation,
err,
error.obligation.predicate.kind().rebind(pred),
);
}
}
}
ty.peel_refs(),
),
);
err.note(format!(
"the following trait bounds weren't met: {}",
errors
.iter()
.map(|e| e.obligation.predicate.to_string())
.collect::<Vec<_>>()
.join("\n"),
));
}
// The type doesn't implement Clone because of unmet obligations.
for error in errors {
if let traits::FulfillmentErrorCode::Select(
traits::SelectionError::Unimplemented,
) = error.code
&& let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
error.obligation.predicate.kind().skip_binder()
{
self.infcx.err_ctxt().suggest_derive(
&error.obligation,
err,
error.obligation.predicate.kind().rebind(pred),
);
}
}
let (changing, span, sugg) = match local.ty {
Some(ty) => ("changing", ty.span, message),
None => {
("specifying", local.pat.span.shrink_to_hi(), format!(": {message}"))
}
};
err.span_suggestion_verbose(
span,
format!("consider {changing} this binding's type"),
sugg,
Applicability::HasPlaceholders,
);
} else {
err.span_label(
err_label_span,
format!("consider changing this binding's type to be: `{message}`"),
);
}
}
None => {}
}
let (changing, span, sugg) = match local.ty {
Some(ty) => ("changing", ty.span, sugg_str),
None => ("specifying", local.pat.span.shrink_to_hi(), format!(": {sugg_str}")),
};
err.span_suggestion_verbose(
span,
format!("consider {changing} this binding's type"),
sugg,
Applicability::HasPlaceholders,
);
}
}
@ -1464,11 +1492,25 @@ fn suggest_ampmut_self(tcx: TyCtxt<'_>, span: Span) -> (Span, String) {
}
}
struct AmpMutSugg {
has_sugg: bool,
span: Span,
suggestion: String,
additional: Option<(Span, String)>,
enum AmpMutSugg {
/// Type suggestion. Changes `&self` to `&mut self`, `x: &T` to `x: &mut T`,
/// `ref x` to `ref mut x`, etc.
Type {
span: Span,
suggestion: String,
additional: Option<(Span, String)>,
},
/// Suggestion for expressions, `&x` to `&mut x`, `&x[i]` to `&mut x[i]`, etc.
Expr {
span: Span,
suggestion: String,
},
/// Suggests `.get_mut` in the case of `&map[&key]` for Hash/BTreeMap.
MapGetMut {
span: Span,
suggestion: String,
},
ChangeBinding,
}
// When we want to suggest a user change a local variable to be a `&mut`, there
@ -1487,110 +1529,111 @@ struct AmpMutSugg {
// This implementation attempts to emulate AST-borrowck prioritization
// by trying (3.), then (2.) and finally falling back on (1.).
fn suggest_ampmut<'tcx>(
tcx: TyCtxt<'tcx>,
decl_ty: Ty<'tcx>,
decl_span: Span,
opt_assignment_rhs_span: Option<Span>,
opt_ty_info: Option<Span>,
infcx: &crate::BorrowckInferCtxt<'tcx>,
body: &Body<'tcx>,
opt_assignment_rhs_stmt: Option<&Statement<'tcx>>,
) -> Option<AmpMutSugg> {
// if there is a RHS and it starts with a `&` from it, then check if it is
let tcx = infcx.tcx;
// If there is a RHS and it starts with a `&` from it, then check if it is
// mutable, and if not, put suggest putting `mut ` to make it mutable.
// we don't have to worry about lifetime annotations here because they are
// We don't have to worry about lifetime annotations here because they are
// not valid when taking a reference. For example, the following is not valid Rust:
//
// let x: &i32 = &'a 5;
// ^^ lifetime annotation not allowed
//
if let Some(rhs_span) = opt_assignment_rhs_span
&& let Ok(rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span)
&& let Some(rhs_str_no_amp) = rhs_str.strip_prefix('&')
if let Some(rhs_stmt) = opt_assignment_rhs_stmt
&& let StatementKind::Assign(box (lhs, rvalue)) = &rhs_stmt.kind
&& let mut rhs_span = rhs_stmt.source_info.span
&& let Ok(mut rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span)
{
// Suggest changing `&raw const` to `&raw mut` if applicable.
if rhs_str_no_amp.trim_start().strip_prefix("raw const").is_some() {
let const_idx = rhs_str.find("const").unwrap() as u32;
let const_span = rhs_span
.with_lo(rhs_span.lo() + BytePos(const_idx))
.with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32));
let mut rvalue = rvalue;
return Some(AmpMutSugg {
has_sugg: true,
span: const_span,
suggestion: "mut".to_owned(),
additional: None,
});
}
// Figure out if rhs already is `&mut`.
let is_mut = if let Some(rest) = rhs_str_no_amp.trim_start().strip_prefix("mut") {
match rest.chars().next() {
// e.g. `&mut x`
Some(c) if c.is_whitespace() => true,
// e.g. `&mut(x)`
Some('(') => true,
// e.g. `&mut{x}`
Some('{') => true,
// e.g. `&mutablevar`
_ => false,
// Take some special care when handling `let _x = &*_y`:
// We want to know if this is part of an overloaded index, so `let x = &a[0]`,
// or whether this is a usertype ascription (`let _x: &T = y`).
if let Rvalue::Ref(_, BorrowKind::Shared, place) = rvalue
&& place.projection.len() == 1
&& place.projection[0] == ProjectionElem::Deref
&& let Some(assign) = find_assignments(&body, place.local).first()
{
// If this is a usertype ascription (`let _x: &T = _y`) then pierce through it as either we want
// to suggest `&mut` on the expression (handled here) or we return `None` and let the caller
// suggest `&mut` on the type if the expression seems fine (e.g. `let _x: &T = &mut _y`).
if let Some(user_ty_projs) = body.local_decls[lhs.local].user_ty.as_ref()
&& let [user_ty_proj] = user_ty_projs.contents.as_slice()
&& user_ty_proj.projs.is_empty()
&& let Either::Left(rhs_stmt_new) = body.stmt_at(*assign)
&& let StatementKind::Assign(box (_, rvalue_new)) = &rhs_stmt_new.kind
&& let rhs_span_new = rhs_stmt_new.source_info.span
&& let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span)
{
(rvalue, rhs_span, rhs_str) = (rvalue_new, rhs_span_new, rhs_str_new);
}
} else {
false
};
// if the reference is already mutable then there is nothing we can do
// here.
if !is_mut {
// shrink the span to just after the `&` in `&variable`
let span = rhs_span.with_lo(rhs_span.lo() + BytePos(1)).shrink_to_lo();
// FIXME(Ezrashaw): returning is bad because we still might want to
// update the annotated type, see #106857.
return Some(AmpMutSugg {
has_sugg: true,
span,
suggestion: "mut ".to_owned(),
additional: None,
});
if let Either::Right(call) = body.stmt_at(*assign)
&& let TerminatorKind::Call {
func: Operand::Constant(box const_operand), args, ..
} = &call.kind
&& let ty::FnDef(method_def_id, method_args) = *const_operand.ty().kind()
&& let Some(trait_) = tcx.trait_of_assoc(method_def_id)
&& tcx.is_lang_item(trait_, hir::LangItem::Index)
{
let trait_ref = ty::TraitRef::from_assoc(
tcx,
tcx.require_lang_item(hir::LangItem::IndexMut, rhs_span),
method_args,
);
// The type only implements `Index` but not `IndexMut`, we must not suggest `&mut`.
if !infcx
.type_implements_trait(trait_ref.def_id, trait_ref.args, infcx.param_env)
.must_apply_considering_regions()
{
// Suggest `get_mut` if type is a `BTreeMap` or `HashMap`.
if let ty::Adt(def, _) = trait_ref.self_ty().kind()
&& [sym::BTreeMap, sym::HashMap]
.into_iter()
.any(|s| tcx.is_diagnostic_item(s, def.did()))
&& let [map, key] = &**args
&& let Ok(map) = tcx.sess.source_map().span_to_snippet(map.span)
&& let Ok(key) = tcx.sess.source_map().span_to_snippet(key.span)
{
let span = rhs_span;
let suggestion = format!("{map}.get_mut({key}).unwrap()");
return Some(AmpMutSugg::MapGetMut { span, suggestion });
}
return None;
}
}
}
let sugg = match rvalue {
Rvalue::Ref(_, BorrowKind::Shared, _) if let Some(ref_idx) = rhs_str.find('&') => {
// Shrink the span to just after the `&` in `&variable`.
Some((
rhs_span.with_lo(rhs_span.lo() + BytePos(ref_idx as u32 + 1)).shrink_to_lo(),
"mut ".to_owned(),
))
}
Rvalue::RawPtr(RawPtrKind::Const, _) if let Some(const_idx) = rhs_str.find("const") => {
// Suggest changing `&raw const` to `&raw mut` if applicable.
let const_idx = const_idx as u32;
Some((
rhs_span
.with_lo(rhs_span.lo() + BytePos(const_idx))
.with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32)),
"mut".to_owned(),
))
}
_ => None,
};
if let Some((span, suggestion)) = sugg {
return Some(AmpMutSugg::Expr { span, suggestion });
}
}
let (binding_exists, span) = match opt_ty_info {
// if this is a variable binding with an explicit type,
// then we will suggest changing it to be mutable.
// this is `Applicability::MachineApplicable`.
Some(ty_span) => (true, ty_span),
// otherwise, we'll suggest *adding* an annotated type, we'll suggest
// the RHS's type for that.
// this is `Applicability::HasPlaceholders`.
None => (false, decl_span),
};
// if the binding already exists and is a reference with an explicit
// lifetime, then we can suggest adding ` mut`. this is special-cased from
// the path without an explicit lifetime.
if let Ok(src) = tcx.sess.source_map().span_to_snippet(span)
&& src.starts_with("&'")
// note that `& 'a T` is invalid so this is correct.
&& let Some(ws_pos) = src.find(char::is_whitespace)
{
let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
Some(AmpMutSugg { has_sugg: true, span, suggestion: " mut".to_owned(), additional: None })
// if there is already a binding, we modify it to be `mut`
} else if binding_exists {
// shrink the span to just after the `&` in `&variable`
let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
Some(AmpMutSugg { has_sugg: true, span, suggestion: "mut ".to_owned(), additional: None })
} else {
// otherwise, suggest that the user annotates the binding; we provide the
// type of the local.
let ty = decl_ty.builtin_deref(true).unwrap();
Some(AmpMutSugg {
has_sugg: false,
span,
suggestion: format!("{}mut {}", if decl_ty.is_ref() { "&" } else { "*" }, ty),
additional: None,
})
}
Some(AmpMutSugg::ChangeBinding)
}
/// If the type is a `Coroutine`, `Closure`, or `CoroutineClosure`

View file

@ -215,7 +215,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
diag: &mut Diag<'_>,
lower_bound: RegionVid,
) {
let mut suggestions = vec![];
let tcx = self.infcx.tcx;
// find generic associated types in the given region 'lower_bound'
@ -237,9 +236,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
.collect::<Vec<_>>();
debug!(?gat_id_and_generics);
// find higher-ranked trait bounds bounded to the generic associated types
// Look for the where-bound which introduces the placeholder.
// As we're using the HIR, we need to handle both `for<'a> T: Trait<'a>`
// and `T: for<'a> Trait`<'a>.
let mut hrtb_bounds = vec![];
gat_id_and_generics.iter().flatten().for_each(|(gat_hir_id, generics)| {
gat_id_and_generics.iter().flatten().for_each(|&(gat_hir_id, generics)| {
for pred in generics.predicates {
let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) =
pred.kind
@ -248,17 +249,32 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
};
if bound_generic_params
.iter()
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == *gat_hir_id)
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
.is_some()
{
for bound in *bounds {
hrtb_bounds.push(bound);
}
} else {
for bound in *bounds {
if let Trait(trait_bound) = bound {
if trait_bound
.bound_generic_params
.iter()
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
.is_some()
{
hrtb_bounds.push(bound);
return;
}
}
}
}
}
});
debug!(?hrtb_bounds);
let mut suggestions = vec![];
hrtb_bounds.iter().for_each(|bound| {
let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }) = bound else {
return;

View file

@ -124,8 +124,13 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
// by using `ty_vid rel B` and then finally and end by equating `ty_vid` to
// the opaque.
let mut enable_subtyping = |ty, opaque_is_expected| {
let ty_vid = infcx.next_ty_var_id_in_universe(self.span(), ty::UniverseIndex::ROOT);
// We create the fresh inference variable in the highest universe.
// In theory we could limit it to the highest universe in the args of
// the opaque but that isn't really worth the effort.
//
// We'll make sure that the opaque type can actually name everything
// in its hidden type later on.
let ty_vid = infcx.next_ty_vid(self.span());
let variance = if opaque_is_expected {
self.ambient_variance
} else {

View file

@ -27,21 +27,39 @@ pub(crate) fn expand_deriving_from(
cx.dcx().bug("derive(From) used on something else than an item");
};
// #[derive(From)] is currently usable only on structs with exactly one field.
let field = if let ItemKind::Struct(_, _, data) = &item.kind
&& let [field] = data.fields()
{
Some(field.clone())
} else {
None
let err_span = || {
let item_span = item.kind.ident().map(|ident| ident.span).unwrap_or(item.span);
MultiSpan::from_spans(vec![span, item_span])
};
let from_type = match &field {
Some(field) => Ty::AstTy(field.ty.clone()),
// We don't have a type to put into From<...> if we don't have a single field, so just put
// unit there.
None => Ty::Unit,
// `#[derive(From)]` is currently usable only on structs with exactly one field.
let field = match &item.kind {
ItemKind::Struct(_, _, data) => {
if let [field] = data.fields() {
Ok(field.clone())
} else {
let guar = cx.dcx().emit_err(errors::DeriveFromWrongFieldCount {
span: err_span(),
multiple_fields: data.fields().len() > 1,
});
Err(guar)
}
}
ItemKind::Enum(_, _, _) | ItemKind::Union(_, _, _) => {
let guar = cx.dcx().emit_err(errors::DeriveFromWrongTarget {
span: err_span(),
kind: &format!("{} {}", item.kind.article(), item.kind.descr()),
});
Err(guar)
}
_ => cx.dcx().bug("Invalid derive(From) ADT input"),
};
let from_type = Ty::AstTy(match field {
Ok(ref field) => field.ty.clone(),
Err(guar) => cx.ty(span, ast::TyKind::Err(guar)),
});
let path =
Path::new_(pathvec_std!(convert::From), vec![Box::new(from_type.clone())], PathKind::Std);
@ -71,34 +89,17 @@ pub(crate) fn expand_deriving_from(
attributes: thin_vec![cx.attr_word(sym::inline, span)],
fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
combine_substructure: combine_substructure(Box::new(|cx, span, substructure| {
let Some(field) = &field else {
let item_span = item.kind.ident().map(|ident| ident.span).unwrap_or(item.span);
let err_span = MultiSpan::from_spans(vec![span, item_span]);
let error = match &item.kind {
ItemKind::Struct(_, _, data) => {
cx.dcx().emit_err(errors::DeriveFromWrongFieldCount {
span: err_span,
multiple_fields: data.fields().len() > 1,
})
}
ItemKind::Enum(_, _, _) | ItemKind::Union(_, _, _) => {
cx.dcx().emit_err(errors::DeriveFromWrongTarget {
span: err_span,
kind: &format!("{} {}", item.kind.article(), item.kind.descr()),
})
}
_ => cx.dcx().bug("Invalid derive(From) ADT input"),
};
return BlockOrExpr::new_expr(DummyResult::raw_expr(span, Some(error)));
let field = match field {
Ok(ref field) => field,
Err(guar) => {
return BlockOrExpr::new_expr(DummyResult::raw_expr(span, Some(guar)));
}
};
let self_kw = Ident::new(kw::SelfUpper, span);
let expr: Box<ast::Expr> = match substructure.fields {
SubstructureFields::StaticStruct(variant, _) => match variant {
// Self {
// field: value
// }
// Self { field: value }
VariantData::Struct { .. } => cx.expr_struct_ident(
span,
self_kw,

View file

@ -8,7 +8,6 @@
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(autodiff)]
#![feature(box_patterns)]
#![feature(decl_macro)]
#![feature(if_let_guard)]

View file

@ -497,7 +497,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-module", module));
let name =
codegen_fn_attrs.link_name.unwrap_or_else(|| cx.tcx.item_name(instance.def_id()));
codegen_fn_attrs.symbol_name.unwrap_or_else(|| cx.tcx.item_name(instance.def_id()));
let name = name.as_str();
to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-name", name));
}

View file

@ -1,104 +1,21 @@
//! A helper class for dealing with static archives
use std::ffi::{CStr, CString, c_char, c_void};
use std::path::{Path, PathBuf};
use std::{io, mem, ptr, str};
use std::ffi::{CStr, c_char, c_void};
use std::io;
use rustc_codegen_ssa::back::archive::{
ArArchiveBuilder, ArchiveBuildFailure, ArchiveBuilder, ArchiveBuilderBuilder,
DEFAULT_OBJECT_READER, ObjectReader, UnknownArchiveKind, try_extract_macho_fat_archive,
ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER, ObjectReader,
};
use rustc_session::Session;
use crate::llvm::archive_ro::{ArchiveRO, Child};
use crate::llvm::{self, ArchiveKind, last_error};
/// Helper for adding many files to an archive.
#[must_use = "must call build() to finish building the archive"]
pub(crate) struct LlvmArchiveBuilder<'a> {
sess: &'a Session,
additions: Vec<Addition>,
}
enum Addition {
File { path: PathBuf, name_in_archive: String },
Archive { path: PathBuf, archive: ArchiveRO, skip: Box<dyn FnMut(&str) -> bool> },
}
impl Addition {
fn path(&self) -> &Path {
match self {
Addition::File { path, .. } | Addition::Archive { path, .. } => path,
}
}
}
fn is_relevant_child(c: &Child<'_>) -> bool {
match c.name() {
Some(name) => !name.contains("SYMDEF"),
None => false,
}
}
impl<'a> ArchiveBuilder for LlvmArchiveBuilder<'a> {
fn add_archive(
&mut self,
archive: &Path,
skip: Box<dyn FnMut(&str) -> bool + 'static>,
) -> io::Result<()> {
let mut archive = archive.to_path_buf();
if self.sess.target.llvm_target.contains("-apple-macosx") {
if let Some(new_archive) = try_extract_macho_fat_archive(self.sess, &archive)? {
archive = new_archive
}
}
let archive_ro = match ArchiveRO::open(&archive) {
Ok(ar) => ar,
Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
};
if self.additions.iter().any(|ar| ar.path() == archive) {
return Ok(());
}
self.additions.push(Addition::Archive {
path: archive,
archive: archive_ro,
skip: Box::new(skip),
});
Ok(())
}
/// Adds an arbitrary file to this archive
fn add_file(&mut self, file: &Path) {
let name = file.file_name().unwrap().to_str().unwrap();
self.additions
.push(Addition::File { path: file.to_path_buf(), name_in_archive: name.to_owned() });
}
/// Combine the provided files, rlibs, and native libraries into a single
/// `Archive`.
fn build(mut self: Box<Self>, output: &Path) -> bool {
match self.build_with_llvm(output) {
Ok(any_members) => any_members,
Err(error) => {
self.sess.dcx().emit_fatal(ArchiveBuildFailure { path: output.to_owned(), error })
}
}
}
}
use crate::llvm;
pub(crate) struct LlvmArchiveBuilderBuilder;
impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a> {
// Keeping LlvmArchiveBuilder around in case of a regression caused by using
// ArArchiveBuilder.
// FIXME(#128955) remove a couple of months after #128936 gets merged in case
// no regression is found.
if false {
Box::new(LlvmArchiveBuilder { sess, additions: Vec::new() })
} else {
Box::new(ArArchiveBuilder::new(sess, &LLVM_OBJECT_READER))
}
// Use the `object` crate to build archives, with a little bit of help from LLVM.
Box::new(ArArchiveBuilder::new(sess, &LLVM_OBJECT_READER))
}
}
@ -178,91 +95,3 @@ fn llvm_is_64_bit_object_file(buf: &[u8]) -> bool {
fn llvm_is_ec_object_file(buf: &[u8]) -> bool {
unsafe { llvm::LLVMRustIsECObject(buf.as_ptr(), buf.len()) }
}
impl<'a> LlvmArchiveBuilder<'a> {
fn build_with_llvm(&mut self, output: &Path) -> io::Result<bool> {
let kind = &*self.sess.target.archive_format;
let kind = kind
.parse::<ArchiveKind>()
.map_err(|_| kind)
.unwrap_or_else(|kind| self.sess.dcx().emit_fatal(UnknownArchiveKind { kind }));
let mut additions = mem::take(&mut self.additions);
// Values in the `members` list below will contain pointers to the strings allocated here.
// So they need to get dropped after all elements of `members` get freed.
let mut strings = Vec::new();
let mut members = Vec::new();
let dst = CString::new(output.to_str().unwrap())?;
unsafe {
for addition in &mut additions {
match addition {
Addition::File { path, name_in_archive } => {
let path = CString::new(path.to_str().unwrap())?;
let name = CString::new(name_in_archive.as_bytes())?;
members.push(llvm::LLVMRustArchiveMemberNew(
path.as_ptr(),
name.as_ptr(),
None,
));
strings.push(path);
strings.push(name);
}
Addition::Archive { archive, skip, .. } => {
for child in archive.iter() {
let child = child.map_err(string_to_io_error)?;
if !is_relevant_child(&child) {
continue;
}
let child_name = child.name().unwrap();
if skip(child_name) {
continue;
}
// It appears that LLVM's archive writer is a little
// buggy if the name we pass down isn't just the
// filename component, so chop that off here and
// pass it in.
//
// See LLVM bug 25877 for more info.
let child_name =
Path::new(child_name).file_name().unwrap().to_str().unwrap();
let name = CString::new(child_name)?;
let m = llvm::LLVMRustArchiveMemberNew(
ptr::null(),
name.as_ptr(),
Some(child.raw),
);
members.push(m);
strings.push(name);
}
}
}
}
let r = llvm::LLVMRustWriteArchive(
dst.as_ptr(),
members.len() as libc::size_t,
members.as_ptr() as *const &_,
true,
kind,
self.sess.target.arch == "arm64ec",
);
let ret = if r.into_result().is_err() {
let msg = last_error().unwrap_or_else(|| "failed to write archive".into());
Err(io::Error::new(io::ErrorKind::Other, msg))
} else {
Ok(!members.is_empty())
};
for member in members {
llvm::LLVMRustArchiveMemberFree(member);
}
ret
}
}
}
fn string_to_io_error(s: String) -> io::Error {
io::Error::new(io::ErrorKind::Other, format!("bad archive: {s}"))
}

View file

@ -0,0 +1,5 @@
pub(crate) mod archive;
pub(crate) mod lto;
pub(crate) mod owned_target_machine;
mod profiling;
pub(crate) mod write;

View file

@ -1,4 +1,5 @@
use std::ffi::{CStr, c_char};
use std::assert_matches::assert_matches;
use std::ffi::CStr;
use std::marker::PhantomData;
use std::ptr::NonNull;
@ -41,11 +42,9 @@ impl OwnedTargetMachine {
args_cstr_buff: &[u8],
use_wasm_eh: bool,
) -> Result<Self, LlvmError<'static>> {
assert!(args_cstr_buff.len() > 0);
assert!(
*args_cstr_buff.last().unwrap() == 0,
"The last character must be a null terminator."
);
// The argument list is passed as the concatenation of one or more C strings.
// This implies that there must be a last byte, and it must be 0.
assert_matches!(args_cstr_buff, [.., b'\0'], "the last byte must be a NUL terminator");
// SAFETY: llvm::LLVMRustCreateTargetMachine copies pointed to data
let tm_ptr = unsafe {
@ -71,7 +70,7 @@ impl OwnedTargetMachine {
output_obj_file.as_ptr(),
debug_info_compression.as_ptr(),
use_emulated_tls,
args_cstr_buff.as_ptr() as *const c_char,
args_cstr_buff.as_ptr(),
args_cstr_buff.len(),
use_wasm_eh,
)
@ -99,7 +98,7 @@ impl Drop for OwnedTargetMachine {
// llvm::LLVMRustCreateTargetMachine OwnedTargetMachine is not copyable so there is no
// double free or use after free.
unsafe {
llvm::LLVMRustDisposeTargetMachine(self.tm_unique.as_mut());
llvm::LLVMRustDisposeTargetMachine(self.tm_unique.as_ptr());
}
}
}

View file

@ -1453,7 +1453,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
instance: Option<Instance<'tcx>>,
) {
let call = self.call(llty, fn_attrs, Some(fn_abi), llfn, args, funclet, instance);
llvm::LLVMRustSetTailCallKind(call, llvm::TailCallKind::MustTail);
llvm::LLVMSetTailCallKind(call, llvm::TailCallKind::MustTail);
match &fn_abi.ret.mode {
PassMode::Ignore | PassMode::Indirect { .. } => self.ret_void(),

View file

@ -377,6 +377,15 @@ pub(crate) unsafe fn create_module<'ll>(
}
}
if let Some(regparm_count) = sess.opts.unstable_opts.regparm {
llvm::add_module_flag_u32(
llmod,
llvm::ModuleFlagMergeBehavior::Error,
"NumRegisterParameters",
regparm_count,
);
}
if let Some(BranchProtection { bti, pac_ret }) = sess.opts.unstable_opts.branch_protection {
if sess.target.arch == "aarch64" {
llvm::add_module_flag_u32(
@ -462,6 +471,15 @@ pub(crate) unsafe fn create_module<'ll>(
}
}
if sess.opts.unstable_opts.indirect_branch_cs_prefix {
llvm::add_module_flag_u32(
llmod,
llvm::ModuleFlagMergeBehavior::Override,
"indirect_branch_cs_prefix",
1,
);
}
match (sess.opts.unstable_opts.small_data_threshold, sess.target.small_data_threshold_support())
{
// Set up the small-data optimization limit for architectures that use

View file

@ -46,18 +46,11 @@ use rustc_session::Session;
use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest};
use rustc_span::Symbol;
mod back {
pub(crate) mod archive;
pub(crate) mod lto;
pub(crate) mod owned_target_machine;
mod profiling;
pub(crate) mod write;
}
mod abi;
mod allocator;
mod asm;
mod attributes;
mod back;
mod base;
mod builder;
mod callee;

View file

@ -1,94 +0,0 @@
//! A wrapper around LLVM's archive (.a) code
use std::path::Path;
use std::{slice, str};
use rustc_fs_util::path_to_c_string;
pub(crate) struct ArchiveRO {
pub raw: &'static mut super::Archive,
}
unsafe impl Send for ArchiveRO {}
pub(crate) struct Iter<'a> {
raw: &'a mut super::ArchiveIterator<'a>,
}
pub(crate) struct Child<'a> {
pub raw: &'a mut super::ArchiveChild<'a>,
}
impl ArchiveRO {
/// Opens a static archive for read-only purposes. This is more optimized
/// than the `open` method because it uses LLVM's internal `Archive` class
/// rather than shelling out to `ar` for everything.
///
/// If this archive is used with a mutable method, then an error will be
/// raised.
pub(crate) fn open(dst: &Path) -> Result<ArchiveRO, String> {
unsafe {
let s = path_to_c_string(dst);
let ar = super::LLVMRustOpenArchive(s.as_ptr()).ok_or_else(|| {
super::last_error().unwrap_or_else(|| "failed to open archive".to_owned())
})?;
Ok(ArchiveRO { raw: ar })
}
}
pub(crate) fn iter(&self) -> Iter<'_> {
unsafe { Iter { raw: super::LLVMRustArchiveIteratorNew(self.raw) } }
}
}
impl Drop for ArchiveRO {
fn drop(&mut self) {
unsafe {
super::LLVMRustDestroyArchive(&mut *(self.raw as *mut _));
}
}
}
impl<'a> Iterator for Iter<'a> {
type Item = Result<Child<'a>, String>;
fn next(&mut self) -> Option<Result<Child<'a>, String>> {
unsafe {
match super::LLVMRustArchiveIteratorNext(self.raw) {
Some(raw) => Some(Ok(Child { raw })),
None => super::last_error().map(Err),
}
}
}
}
impl<'a> Drop for Iter<'a> {
fn drop(&mut self) {
unsafe {
super::LLVMRustArchiveIteratorFree(&mut *(self.raw as *mut _));
}
}
}
impl<'a> Child<'a> {
pub(crate) fn name(&self) -> Option<&'a str> {
unsafe {
let mut name_len = 0;
let name_ptr = super::LLVMRustArchiveChildName(self.raw, &mut name_len);
if name_ptr.is_null() {
None
} else {
let name = slice::from_raw_parts(name_ptr as *const u8, name_len as usize);
str::from_utf8(name).ok().map(|s| s.trim())
}
}
}
}
impl<'a> Drop for Child<'a> {
fn drop(&mut self) {
unsafe {
super::LLVMRustArchiveChildFree(&mut *(self.raw as *mut _));
}
}
}

View file

@ -97,6 +97,7 @@ pub(crate) enum ModuleFlagMergeBehavior {
// Consts for the LLVM CallConv type, pre-cast to usize.
/// Must match the layout of `LLVMTailCallKind`.
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(C)]
#[allow(dead_code)]
@ -332,10 +333,15 @@ impl RealPredicate {
}
}
/// LLVMTypeKind
#[derive(Copy, Clone, PartialEq, Debug)]
/// Must match the layout of `LLVMTypeKind`.
///
/// Use [`RawEnum<TypeKind>`] for values of `LLVMTypeKind` returned from LLVM,
/// to avoid risk of UB if LLVM adds new enum values.
///
/// All of LLVM's variants should be declared here, even if no Rust-side code refers
/// to them, because unknown variants will cause [`RawEnum::to_rust`] to panic.
#[derive(Copy, Clone, PartialEq, Debug, TryFromU32)]
#[repr(C)]
#[expect(dead_code, reason = "Some variants are unused, but are kept to match LLVM-C")]
pub(crate) enum TypeKind {
Void = 0,
Half = 1,
@ -610,17 +616,6 @@ pub(crate) enum DiagnosticLevel {
Remark,
}
/// LLVMRustArchiveKind
#[derive(Copy, Clone)]
#[repr(C)]
pub(crate) enum ArchiveKind {
K_GNU,
K_BSD,
K_DARWIN,
K_COFF,
K_AIXBIG,
}
unsafe extern "C" {
// LLVMRustThinLTOData
pub(crate) type ThinLTOData;
@ -769,19 +764,12 @@ pub(crate) struct Builder<'a>(InvariantOpaque<'a>);
pub(crate) struct PassManager<'a>(InvariantOpaque<'a>);
unsafe extern "C" {
pub type TargetMachine;
pub(crate) type Archive;
}
#[repr(C)]
pub(crate) struct ArchiveIterator<'a>(InvariantOpaque<'a>);
#[repr(C)]
pub(crate) struct ArchiveChild<'a>(InvariantOpaque<'a>);
unsafe extern "C" {
pub(crate) type Twine;
pub(crate) type DiagnosticInfo;
pub(crate) type SMDiagnostic;
}
#[repr(C)]
pub(crate) struct RustArchiveMember<'a>(InvariantOpaque<'a>);
/// Opaque pointee of `LLVMOperandBundleRef`.
#[repr(C)]
pub(crate) struct OperandBundle<'a>(InvariantOpaque<'a>);
@ -1046,6 +1034,8 @@ unsafe extern "C" {
CanThrow: llvm::Bool,
) -> &'ll Value;
pub(crate) safe fn LLVMGetTypeKind(Ty: &Type) -> RawEnum<TypeKind>;
// Operations on integer types
pub(crate) fn LLVMInt1TypeInContext(C: &Context) -> &Type;
pub(crate) fn LLVMInt8TypeInContext(C: &Context) -> &Type;
@ -1197,7 +1187,7 @@ unsafe extern "C" {
pub(crate) safe fn LLVMIsGlobalConstant(GlobalVar: &Value) -> Bool;
pub(crate) safe fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool);
pub(crate) safe fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool);
pub(crate) safe fn LLVMRustSetTailCallKind(CallInst: &Value, Kind: TailCallKind);
pub(crate) safe fn LLVMSetTailCallKind(CallInst: &Value, kind: TailCallKind);
// Operations on attributes
pub(crate) fn LLVMCreateStringAttribute(
@ -1841,9 +1831,6 @@ unsafe extern "C" {
// Create and destroy contexts.
pub(crate) fn LLVMRustContextCreate(shouldDiscardNames: bool) -> &'static mut Context;
/// See llvm::LLVMTypeKind::getTypeID.
pub(crate) fn LLVMRustGetTypeKind(Ty: &Type) -> TypeKind;
// Operations on all values
pub(crate) fn LLVMRustGlobalAddMetadata<'a>(
Val: &'a Value,
@ -2438,7 +2425,7 @@ unsafe extern "C" {
OutputObjFile: *const c_char,
DebugInfoCompression: *const c_char,
UseEmulatedTls: bool,
ArgsCstrBuff: *const c_char,
ArgsCstrBuff: *const c_uchar, // See "PTR_LEN_STR".
ArgsCstrBuffLen: usize,
UseWasmEH: bool,
) -> *mut TargetMachine;
@ -2505,19 +2492,6 @@ unsafe extern "C" {
pub(crate) fn LLVMRustSetNormalizedTarget(M: &Module, triple: *const c_char);
pub(crate) fn LLVMRustRunRestrictionPass(M: &Module, syms: *const *const c_char, len: size_t);
pub(crate) fn LLVMRustOpenArchive(path: *const c_char) -> Option<&'static mut Archive>;
pub(crate) fn LLVMRustArchiveIteratorNew(AR: &Archive) -> &mut ArchiveIterator<'_>;
pub(crate) fn LLVMRustArchiveIteratorNext<'a>(
AIR: &ArchiveIterator<'a>,
) -> Option<&'a mut ArchiveChild<'a>>;
pub(crate) fn LLVMRustArchiveChildName(
ACR: &ArchiveChild<'_>,
size: &mut size_t,
) -> *const c_char;
pub(crate) fn LLVMRustArchiveChildFree<'a>(ACR: &'a mut ArchiveChild<'a>);
pub(crate) fn LLVMRustArchiveIteratorFree<'a>(AIR: &'a mut ArchiveIterator<'a>);
pub(crate) fn LLVMRustDestroyArchive(AR: &'static mut Archive);
pub(crate) fn LLVMRustWriteTwineToString(T: &Twine, s: &RustString);
pub(crate) fn LLVMRustUnpackOptimizationDiagnostic<'a>(
@ -2555,21 +2529,6 @@ unsafe extern "C" {
num_ranges: &mut usize,
) -> bool;
pub(crate) fn LLVMRustWriteArchive(
Dst: *const c_char,
NumMembers: size_t,
Members: *const &RustArchiveMember<'_>,
WriteSymbtab: bool,
Kind: ArchiveKind,
isEC: bool,
) -> LLVMRustResult;
pub(crate) fn LLVMRustArchiveMemberNew<'a>(
Filename: *const c_char,
Name: *const c_char,
Child: Option<&ArchiveChild<'a>>,
) -> &'a mut RustArchiveMember<'a>;
pub(crate) fn LLVMRustArchiveMemberFree<'a>(Member: &'a mut RustArchiveMember<'a>);
pub(crate) fn LLVMRustSetDataLayoutFromTargetMachine<'a>(M: &'a Module, TM: &'a TargetMachine);
pub(crate) fn LLVMRustPositionBuilderPastAllocas<'a>(B: &Builder<'a>, Fn: &'a Value);

View file

@ -3,7 +3,6 @@
use std::ffi::{CStr, CString};
use std::num::NonZero;
use std::ptr;
use std::str::FromStr;
use std::string::FromUtf8Error;
use libc::c_uint;
@ -16,7 +15,6 @@ pub(crate) use self::MetadataType::*;
pub(crate) use self::ffi::*;
use crate::common::AsCCharPtr;
pub(crate) mod archive_ro;
pub(crate) mod diagnostic;
pub(crate) mod enzyme_ffi;
mod ffi;
@ -152,21 +150,6 @@ pub(crate) enum CodeGenOptSize {
CodeGenOptSizeAggressive = 2,
}
impl FromStr for ArchiveKind {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"gnu" => Ok(ArchiveKind::K_GNU),
"bsd" => Ok(ArchiveKind::K_BSD),
"darwin" => Ok(ArchiveKind::K_DARWIN),
"coff" => Ok(ArchiveKind::K_COFF),
"aix_big" => Ok(ArchiveKind::K_AIXBIG),
_ => Err(()),
}
}
}
pub(crate) fn SetInstructionCallConv(instr: &Value, cc: CallConv) {
unsafe {
LLVMSetInstructionCallConv(instr, cc as c_uint);

View file

@ -277,6 +277,7 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
{
None
}
("loongarch32" | "loongarch64", "32s") if get_version().0 < 21 => None,
// Filter out features that are not supported by the current LLVM version
("riscv32" | "riscv64", "zacas") if get_version().0 < 20 => None,
(

View file

@ -204,7 +204,7 @@ impl<'ll, CX: Borrow<SCx<'ll>>> BaseTypeCodegenMethods for GenericCx<'ll, CX> {
}
fn type_kind(&self, ty: &'ll Type) -> TypeKind {
unsafe { llvm::LLVMRustGetTypeKind(ty).to_generic() }
llvm::LLVMGetTypeKind(ty).to_rust().to_generic()
}
fn type_ptr(&self) -> &'ll Type {

View file

@ -171,8 +171,8 @@ codegen_ssa_invalid_monomorphization_unsupported_symbol = invalid monomorphizati
codegen_ssa_invalid_monomorphization_unsupported_symbol_of_size = invalid monomorphization of `{$name}` intrinsic: unsupported {$symbol} from `{$in_ty}` with element `{$in_elem}` of size `{$size}` to `{$ret_ty}`
codegen_ssa_invalid_no_sanitize = invalid argument for `no_sanitize`
.note = expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread`
codegen_ssa_invalid_sanitize = invalid argument for `sanitize`
.note = expected one of: `address`, `kernel_address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow_call_stack`, or `thread`
codegen_ssa_invalid_windows_subsystem = invalid windows subsystem `{$subsystem}`, only `windows` and `console` are allowed

View file

@ -2435,6 +2435,13 @@ fn linker_with_args(
// Passed after compiler-generated options to support manual overriding when necessary.
add_user_defined_link_args(cmd, sess);
// ------------ Builtin configurable linker scripts ------------
// The user's link args should be able to overwrite symbols in the compiler's
// linker script that were weakly defined (i.e. defined with `PROVIDE()`). For this
// to work correctly, the user needs to be able to specify linker arguments like
// `--defsym` and `--script` *before* any builtin linker scripts are evaluated.
add_link_script(cmd, sess, tmpdir, crate_type);
// ------------ Object code and libraries, order-dependent ------------
// Post-link CRT objects.
@ -2469,8 +2476,6 @@ fn add_order_independent_options(
let apple_sdk_root = add_apple_sdk(cmd, sess, flavor);
add_link_script(cmd, sess, tmpdir, crate_type);
if sess.target.os == "fuchsia"
&& crate_type == CrateType::Executable
&& !matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))

View file

@ -858,7 +858,7 @@ pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>(
instance: Instance<'tcx>,
) -> bool {
fn is_llvm_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
if let Some(name) = tcx.codegen_fn_attrs(def_id).link_name {
if let Some(name) = tcx.codegen_fn_attrs(def_id).symbol_name {
name.as_str().starts_with("llvm.")
} else {
false

View file

@ -76,32 +76,6 @@ fn parse_instruction_set_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> Option<Instr
}
}
// FIXME(jdonszelmann): remove when no_sanitize becomes a parsed attr
fn parse_no_sanitize_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> Option<SanitizerSet> {
let list = attr.meta_item_list()?;
let mut sanitizer_set = SanitizerSet::empty();
for item in list.iter() {
match item.name() {
Some(sym::address) => {
sanitizer_set |= SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS
}
Some(sym::cfi) => sanitizer_set |= SanitizerSet::CFI,
Some(sym::kcfi) => sanitizer_set |= SanitizerSet::KCFI,
Some(sym::memory) => sanitizer_set |= SanitizerSet::MEMORY,
Some(sym::memtag) => sanitizer_set |= SanitizerSet::MEMTAG,
Some(sym::shadow_call_stack) => sanitizer_set |= SanitizerSet::SHADOWCALLSTACK,
Some(sym::thread) => sanitizer_set |= SanitizerSet::THREAD,
Some(sym::hwaddress) => sanitizer_set |= SanitizerSet::HWADDRESS,
_ => {
tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() });
}
}
}
Some(sanitizer_set)
}
// FIXME(jdonszelmann): remove when patchable_function_entry becomes a parsed attr
fn parse_patchable_function_entry(
tcx: TyCtxt<'_>,
@ -160,7 +134,7 @@ fn parse_patchable_function_entry(
#[derive(Default)]
struct InterestingAttributeDiagnosticSpans {
link_ordinal: Option<Span>,
no_sanitize: Option<Span>,
sanitize: Option<Span>,
inline: Option<Span>,
no_mangle: Option<Span>,
}
@ -181,7 +155,7 @@ fn process_builtin_attrs(
match p {
AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
AttributeKind::ExportName { name, .. } => {
codegen_fn_attrs.export_name = Some(*name)
codegen_fn_attrs.symbol_name = Some(*name)
}
AttributeKind::Inline(inline, span) => {
codegen_fn_attrs.inline = *inline;
@ -189,7 +163,13 @@ fn process_builtin_attrs(
}
AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align),
AttributeKind::LinkName { name, .. } => codegen_fn_attrs.link_name = Some(*name),
AttributeKind::LinkName { name, .. } => {
// FIXME Remove check for foreign functions once #[link_name] on non-foreign
// functions is a hard error
if tcx.is_foreign_item(did) {
codegen_fn_attrs.symbol_name = Some(*name);
}
}
AttributeKind::LinkOrdinal { ordinal, span } => {
codegen_fn_attrs.link_ordinal = Some(*ordinal);
interesting_spans.link_ordinal = Some(*span);
@ -329,11 +309,7 @@ fn process_builtin_attrs(
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED
}
sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL,
sym::no_sanitize => {
interesting_spans.no_sanitize = Some(attr.span());
codegen_fn_attrs.no_sanitize |=
parse_no_sanitize_attr(tcx, attr).unwrap_or_default();
}
sym::sanitize => interesting_spans.sanitize = Some(attr.span()),
sym::instruction_set => {
codegen_fn_attrs.instruction_set = parse_instruction_set_attr(tcx, attr)
}
@ -357,6 +333,8 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code
codegen_fn_attrs.alignment =
Ord::max(codegen_fn_attrs.alignment, tcx.sess.opts.unstable_opts.min_function_alignment);
// Compute the disabled sanitizers.
codegen_fn_attrs.no_sanitize |= tcx.disabled_sanitizers_for(did);
// On trait methods, inherit the `#[align]` of the trait's method prototype.
codegen_fn_attrs.alignment = Ord::max(codegen_fn_attrs.alignment, tcx.inherited_align(did));
@ -409,7 +387,7 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code
// * `#[rustc_std_internal_symbol]` mangles the symbol name in a special way
// both for exports and imports through foreign items. This is handled further,
// during symbol mangling logic.
} else if codegen_fn_attrs.link_name.is_some() {
} else if codegen_fn_attrs.symbol_name.is_some() {
// * This can be overridden with the `#[link_name]` attribute
} else {
// NOTE: there's one more exception that we cannot apply here. On wasm,
@ -454,17 +432,17 @@ fn check_result(
if !codegen_fn_attrs.no_sanitize.is_empty()
&& codegen_fn_attrs.inline.always()
&& let (Some(no_sanitize_span), Some(inline_span)) =
(interesting_spans.no_sanitize, interesting_spans.inline)
(interesting_spans.sanitize, interesting_spans.inline)
{
let hir_id = tcx.local_def_id_to_hir_id(did);
tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, |lint| {
lint.primary_message("`no_sanitize` will have no effect after inlining");
lint.primary_message("setting `sanitize` off will have no effect after inlining");
lint.span_note(inline_span, "inlining requested here");
})
}
// error when specifying link_name together with link_ordinal
if let Some(_) = codegen_fn_attrs.link_name
if let Some(_) = codegen_fn_attrs.symbol_name
&& let Some(_) = codegen_fn_attrs.link_ordinal
{
let msg = "cannot use `#[link_name]` with `#[link_ordinal]`";
@ -515,8 +493,7 @@ fn handle_lang_items(
&& let Some(link_name) = lang_item.link_name()
{
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
codegen_fn_attrs.export_name = Some(link_name);
codegen_fn_attrs.link_name = Some(link_name);
codegen_fn_attrs.symbol_name = Some(link_name);
}
// error when using no_mangle on a lang item item
@ -582,6 +559,93 @@ fn opt_trait_item(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
}
}
/// For an attr that has the `sanitize` attribute, read the list of
/// disabled sanitizers. `current_attr` holds the information about
/// previously parsed attributes.
fn parse_sanitize_attr(
tcx: TyCtxt<'_>,
attr: &Attribute,
current_attr: SanitizerSet,
) -> SanitizerSet {
let mut result = current_attr;
if let Some(list) = attr.meta_item_list() {
for item in list.iter() {
let MetaItemInner::MetaItem(set) = item else {
tcx.dcx().emit_err(errors::InvalidSanitize { span: attr.span() });
break;
};
let segments = set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
match segments.as_slice() {
// Similar to clang, sanitize(address = ..) and
// sanitize(kernel_address = ..) control both ASan and KASan
// Source: https://reviews.llvm.org/D44981.
[sym::address] | [sym::kernel_address] if set.value_str() == Some(sym::off) => {
result |= SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS
}
[sym::address] | [sym::kernel_address] if set.value_str() == Some(sym::on) => {
result &= !SanitizerSet::ADDRESS;
result &= !SanitizerSet::KERNELADDRESS;
}
[sym::cfi] if set.value_str() == Some(sym::off) => result |= SanitizerSet::CFI,
[sym::cfi] if set.value_str() == Some(sym::on) => result &= !SanitizerSet::CFI,
[sym::kcfi] if set.value_str() == Some(sym::off) => result |= SanitizerSet::KCFI,
[sym::kcfi] if set.value_str() == Some(sym::on) => result &= !SanitizerSet::KCFI,
[sym::memory] if set.value_str() == Some(sym::off) => {
result |= SanitizerSet::MEMORY
}
[sym::memory] if set.value_str() == Some(sym::on) => {
result &= !SanitizerSet::MEMORY
}
[sym::memtag] if set.value_str() == Some(sym::off) => {
result |= SanitizerSet::MEMTAG
}
[sym::memtag] if set.value_str() == Some(sym::on) => {
result &= !SanitizerSet::MEMTAG
}
[sym::shadow_call_stack] if set.value_str() == Some(sym::off) => {
result |= SanitizerSet::SHADOWCALLSTACK
}
[sym::shadow_call_stack] if set.value_str() == Some(sym::on) => {
result &= !SanitizerSet::SHADOWCALLSTACK
}
[sym::thread] if set.value_str() == Some(sym::off) => {
result |= SanitizerSet::THREAD
}
[sym::thread] if set.value_str() == Some(sym::on) => {
result &= !SanitizerSet::THREAD
}
[sym::hwaddress] if set.value_str() == Some(sym::off) => {
result |= SanitizerSet::HWADDRESS
}
[sym::hwaddress] if set.value_str() == Some(sym::on) => {
result &= !SanitizerSet::HWADDRESS
}
_ => {
tcx.dcx().emit_err(errors::InvalidSanitize { span: attr.span() });
}
}
}
}
result
}
fn disabled_sanitizers_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerSet {
// Backtrack to the crate root.
let disabled = match tcx.opt_local_parent(did) {
// Check the parent (recursively).
Some(parent) => tcx.disabled_sanitizers_for(parent),
// We reached the crate root without seeing an attribute, so
// there is no sanitizers to exclude.
None => SanitizerSet::empty(),
};
// Check for a sanitize annotation directly on this def.
if let Some(attr) = tcx.get_attr(did, sym::sanitize) {
return parse_sanitize_attr(tcx, attr, disabled);
}
disabled
}
/// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller
/// applied to the method prototype.
fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
@ -706,6 +770,11 @@ pub fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
}
pub(crate) fn provide(providers: &mut Providers) {
*providers =
Providers { codegen_fn_attrs, should_inherit_track_caller, inherited_align, ..*providers };
*providers = Providers {
codegen_fn_attrs,
should_inherit_track_caller,
inherited_align,
disabled_sanitizers_for,
..*providers
};
}

View file

@ -1121,9 +1121,9 @@ impl IntoDiagArg for ExpectedPointerMutability {
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_invalid_no_sanitize)]
#[diag(codegen_ssa_invalid_sanitize)]
#[note]
pub(crate) struct InvalidNoSanitize {
pub(crate) struct InvalidSanitize {
#[primary_span]
pub span: Span,
}

View file

@ -180,6 +180,7 @@ fn parse_rust_feature_flag<'a>(
while let Some(new_feature) = new_features.pop() {
if features.insert(new_feature) {
if let Some(implied_features) = inverse_implied_features.get(&new_feature) {
#[allow(rustc::potential_query_instability)]
new_features.extend(implied_features)
}
}

View file

@ -27,8 +27,9 @@ use crate::{enter_trace_span, fluent_generated as fluent};
pub enum FnArg<'tcx, Prov: Provenance = CtfeProvenance> {
/// Pass a copy of the given operand.
Copy(OpTy<'tcx, Prov>),
/// Allow for the argument to be passed in-place: destroy the value originally stored at that place and
/// make the place inaccessible for the duration of the function call.
/// Allow for the argument to be passed in-place: destroy the value originally stored at that
/// place and make the place inaccessible for the duration of the function call. This *must* be
/// an in-memory place so that we can do the proper alias checks.
InPlace(MPlaceTy<'tcx, Prov>),
}
@ -379,6 +380,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
}
}
// *Before* pushing the new frame, determine whether the return destination is in memory.
// Need to use `place_to_op` to be *sure* we get the mplace if there is one.
let destination_mplace = self.place_to_op(destination)?.as_mplace_or_imm().left();
// Push the "raw" frame -- this leaves locals uninitialized.
self.push_stack_frame_raw(instance, body, destination, cont)?;
// If an error is raised here, pop the frame again to get an accurate backtrace.
@ -496,7 +502,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// Protect return place for in-place return value passing.
// We only need to protect anything if this is actually an in-memory place.
if let Left(mplace) = destination.as_mplace_or_local() {
if let Some(mplace) = destination_mplace {
M::protect_in_place_function_argument(self, &mplace)?;
}

View file

@ -325,8 +325,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let _trace = enter_trace_span!(
M,
"instantiate_from_frame_and_normalize_erasing_regions",
"{}",
frame.instance
%frame.instance
);
frame
.instance
@ -583,6 +582,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
span: Span,
layout: Option<TyAndLayout<'tcx>>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
let _trace = enter_trace_span!(M, const_eval::eval_mir_constant, ?val);
let const_val = val.eval(*self.tcx, self.typing_env, span).map_err(|err| {
if M::ALL_CONSTS_ARE_PRECHECKED {
match err {

View file

@ -175,6 +175,16 @@ impl<Prov: Provenance> Immediate<Prov> {
}
interp_ok(())
}
pub fn has_provenance(&self) -> bool {
match self {
Immediate::Scalar(scalar) => matches!(scalar, Scalar::Ptr { .. }),
Immediate::ScalarPair(s1, s2) => {
matches!(s1, Scalar::Ptr { .. }) || matches!(s2, Scalar::Ptr { .. })
}
Immediate::Uninit => false,
}
}
}
// ScalarPair needs a type to interpret, so we often have an immediate and a type together

View file

@ -234,6 +234,12 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> {
}
/// A place is either an mplace or some local.
///
/// Note that the return value can be different even for logically identical places!
/// Specifically, if a local is stored in-memory, this may return `Local` or `MPlaceTy`
/// depending on how the place was constructed. In other words, seeing `Local` here does *not*
/// imply that this place does not point to memory. Every caller must therefore always handle
/// both cases.
#[inline(always)]
pub fn as_mplace_or_local(
&self,
@ -759,6 +765,13 @@ where
&mut self,
dest: &impl Writeable<'tcx, M::Provenance>,
) -> InterpResult<'tcx> {
// If this is an efficiently represented local variable without provenance, skip the
// `as_mplace_or_mutable_local` that would otherwise force this local into memory.
if let Right(imm) = dest.to_op(self)?.as_mplace_or_imm() {
if !imm.has_provenance() {
return interp_ok(());
}
}
match self.as_mplace_or_mutable_local(&dest.to_place())? {
Right((local_val, _local_layout, local)) => {
local_val.clear_provenance()?;

View file

@ -20,7 +20,7 @@ use super::{
MemoryKind, Operand, PlaceTy, Pointer, Provenance, ReturnAction, Scalar, from_known_layout,
interp_ok, throw_ub, throw_unsup,
};
use crate::errors;
use crate::{enter_trace_span, errors};
// The Phantomdata exists to prevent this type from being `Send`. If it were sent across a thread
// boundary and dropped in the other thread, it would exit the span in the other thread.
@ -386,6 +386,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// Make sure all the constants required by this frame evaluate successfully (post-monomorphization check).
for &const_ in body.required_consts() {
// We can't use `eval_mir_constant` here as that assumes that all required consts have
// already been checked, so we need a separate tracing call.
let _trace = enter_trace_span!(M, const_eval::required_consts, ?const_.const_);
let c =
self.instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?;
c.eval(*self.tcx, self.typing_env, const_.span).map_err(|err| {

View file

@ -4,6 +4,7 @@
use either::Either;
use rustc_abi::{FIRST_VARIANT, FieldIdx};
use rustc_data_structures::fx::FxHashSet;
use rustc_index::IndexSlice;
use rustc_middle::ty::{self, Instance, Ty};
use rustc_middle::{bug, mir, span_bug};
@ -389,8 +390,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
/// Evaluate the arguments of a function call
fn eval_fn_call_argument(
&self,
&mut self,
op: &mir::Operand<'tcx>,
move_definitely_disjoint: bool,
) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> {
interp_ok(match op {
mir::Operand::Copy(_) | mir::Operand::Constant(_) => {
@ -399,24 +401,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
FnArg::Copy(op)
}
mir::Operand::Move(place) => {
// If this place lives in memory, preserve its location.
// We call `place_to_op` which will be an `MPlaceTy` whenever there exists
// an mplace for this place. (This is in contrast to `PlaceTy::as_mplace_or_local`
// which can return a local even if that has an mplace.)
let place = self.eval_place(*place)?;
let op = self.place_to_op(&place)?;
match op.as_mplace_or_imm() {
Either::Left(mplace) => FnArg::InPlace(mplace),
Either::Right(_imm) => {
// This argument doesn't live in memory, so there's no place
// to make inaccessible during the call.
// We rely on there not being any stray `PlaceTy` that would let the
// caller directly access this local!
// This is also crucial for tail calls, where we want the `FnArg` to
// stay valid when the old stack frame gets popped.
FnArg::Copy(op)
if move_definitely_disjoint {
// We still have to ensure that no *other* pointers are used to access this place,
// so *if* it is in memory then we have to treat it as `InPlace`.
// Use `place_to_op` to guarantee that we notice it being in memory.
let op = self.place_to_op(&place)?;
match op.as_mplace_or_imm() {
Either::Left(mplace) => FnArg::InPlace(mplace),
Either::Right(_imm) => FnArg::Copy(op),
}
} else {
// We have to force this into memory to detect aliasing among `Move` arguments.
FnArg::InPlace(self.force_allocation(&place)?)
}
}
})
@ -425,18 +422,46 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
/// Shared part of `Call` and `TailCall` implementation — finding and evaluating all the
/// necessary information about callee and arguments to make a call.
fn eval_callee_and_args(
&self,
&mut self,
terminator: &mir::Terminator<'tcx>,
func: &mir::Operand<'tcx>,
args: &[Spanned<mir::Operand<'tcx>>],
) -> InterpResult<'tcx, EvaluatedCalleeAndArgs<'tcx, M>> {
let func = self.eval_operand(func, None)?;
// Evaluating function call arguments. The tricky part here is dealing with `Move`
// arguments: we have to ensure no two such arguments alias. This would be most easily done
// by just forcing them all into memory and then doing the usual in-place argument
// protection, but then we'd force *a lot* of arguments into memory. So we do some syntactic
// pre-processing here where if all `move` arguments are syntactically distinct local
// variables (and none is indirect), we can skip the in-memory forcing.
let move_definitely_disjoint = 'move_definitely_disjoint: {
let mut previous_locals = FxHashSet::<mir::Local>::default();
for arg in args {
let mir::Operand::Move(place) = arg.node else {
continue; // we can skip non-`Move` arguments.
};
if place.is_indirect_first_projection() {
// An indirect `Move` argument could alias with anything else...
break 'move_definitely_disjoint false;
}
if !previous_locals.insert(place.local) {
// This local is the base for two arguments! They might overlap.
break 'move_definitely_disjoint false;
}
}
// We found no violation so they are all definitely disjoint.
true
};
let args = args
.iter()
.map(|arg| self.eval_fn_call_argument(&arg.node))
.map(|arg| self.eval_fn_call_argument(&arg.node, move_definitely_disjoint))
.collect::<InterpResult<'tcx, Vec<_>>>()?;
let fn_sig_binder = func.layout.ty.fn_sig(*self.tcx);
let fn_sig_binder = {
let _trace = enter_trace_span!(M, "fn_sig", ty = ?func.layout.ty.kind());
func.layout.ty.fn_sig(*self.tcx)
};
let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.typing_env, fn_sig_binder);
let extra_args = &args[fn_sig.inputs().len()..];
let extra_args =

View file

@ -1418,7 +1418,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let _trace = enter_trace_span!(
M,
"validate_operand",
"recursive={recursive}, reset_provenance_and_padding={reset_provenance_and_padding}, val={val:?}"
recursive,
reset_provenance_and_padding,
?val,
);
// Note that we *could* actually be in CTFE here with `-Zextra-const-ub-checks`, but it's

View file

@ -9,6 +9,7 @@ use rustc_abi::TargetDataLayoutErrors;
use rustc_ast::util::parser::ExprPrecedence;
use rustc_ast_pretty::pprust;
use rustc_hir::RustcVersion;
use rustc_hir::attrs::{MirDialect, MirPhase};
use rustc_macros::Subdiagnostic;
use rustc_span::edition::Edition;
use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol};
@ -312,6 +313,28 @@ impl IntoDiagArg for ExprPrecedence {
}
}
impl IntoDiagArg for MirDialect {
fn into_diag_arg(self, _path: &mut Option<PathBuf>) -> DiagArgValue {
let arg = match self {
MirDialect::Analysis => "analysis",
MirDialect::Built => "built",
MirDialect::Runtime => "runtime",
};
DiagArgValue::Str(Cow::Borrowed(arg))
}
}
impl IntoDiagArg for MirPhase {
fn into_diag_arg(self, _path: &mut Option<PathBuf>) -> DiagArgValue {
let arg = match self {
MirPhase::Initial => "initial",
MirPhase::PostCleanup => "post-cleanup",
MirPhase::Optimized => "optimized",
};
DiagArgValue::Str(Cow::Borrowed(arg))
}
}
#[derive(Clone)]
pub struct DiagSymbolList<S = Symbol>(Vec<S>);

View file

@ -17,7 +17,7 @@ use std::path::Path;
use std::sync::Arc;
use derive_setters::Setters;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::sync::{DynSend, IntoDynSyncSend};
use rustc_error_messages::{FluentArgs, SpanLabel};
use rustc_lexer;
@ -1853,7 +1853,7 @@ impl HumanEmitter {
&& line_idx + 1 == annotated_file.lines.len(),
);
let mut to_add = FxHashMap::default();
let mut to_add = FxIndexMap::default();
for (depth, style) in depths {
// FIXME(#120456) - is `swap_remove` correct?

View file

@ -70,7 +70,7 @@ expand_invalid_fragment_specifier =
invalid fragment specifier `{$fragment}`
.help = {$help}
expand_macro_args_bad_delim = macro attribute argument matchers require parentheses
expand_macro_args_bad_delim = `{$rule_kw}` rule argument matchers require parentheses
expand_macro_args_bad_delim_sugg = the delimiters should be `(` and `)`
expand_macro_body_stability =

View file

@ -490,6 +490,7 @@ pub(crate) struct MacroArgsBadDelim {
pub span: Span,
#[subdiagnostic]
pub sugg: MacroArgsBadDelimSugg,
pub rule_kw: Symbol,
}
#[derive(Subdiagnostic)]

View file

@ -16,6 +16,7 @@ use rustc_attr_parsing::{EvalConfigResult, ShouldEmit};
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
use rustc_errors::PResult;
use rustc_feature::Features;
use rustc_hir::def::MacroKinds;
use rustc_parse::parser::{
AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, RecoverColon, RecoverComma,
token_descr,
@ -565,6 +566,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
.map(|DeriveResolution { path, item, exts: _, is_const }| {
// FIXME: Consider using the derive resolutions (`_exts`)
// instead of enqueuing the derives to be resolved again later.
// Note that this can result in duplicate diagnostics.
let expn_id = LocalExpnId::fresh_empty();
derive_invocations.push((
Invocation {
@ -922,6 +924,35 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
}
fragment
}
SyntaxExtensionKind::MacroRules(expander)
if expander.kinds().contains(MacroKinds::DERIVE) =>
{
if is_const {
let guar = self
.cx
.dcx()
.span_err(span, "macro `derive` does not support const derives");
return ExpandResult::Ready(fragment_kind.dummy(span, guar));
}
let body = item.to_tokens();
match expander.expand_derive(self.cx, span, &body) {
Ok(tok_result) => {
let fragment =
self.parse_ast_fragment(tok_result, fragment_kind, &path, span);
if macro_stats {
update_derive_macro_stats(
self.cx,
fragment_kind,
span,
&path,
&fragment,
);
}
fragment
}
Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)),
}
}
_ => unreachable!(),
},
InvocationKind::GlobDelegation { item, of_trait } => {

View file

@ -14,14 +14,22 @@ use super::macro_rules::{MacroRule, NoopTracker, parser_from_cx};
use crate::expand::{AstFragmentKind, parse_ast_fragment};
use crate::mbe::macro_parser::ParseResult::*;
use crate::mbe::macro_parser::{MatcherLoc, NamedParseResult, TtParser};
use crate::mbe::macro_rules::{Tracker, try_match_macro, try_match_macro_attr};
use crate::mbe::macro_rules::{
Tracker, try_match_macro, try_match_macro_attr, try_match_macro_derive,
};
pub(super) enum FailedMacro<'a> {
Func,
Attr(&'a TokenStream),
Derive,
}
pub(super) fn failed_to_match_macro(
psess: &ParseSess,
sp: Span,
def_span: Span,
name: Ident,
attr_args: Option<&TokenStream>,
args: FailedMacro<'_>,
body: &TokenStream,
rules: &[MacroRule],
) -> (Span, ErrorGuaranteed) {
@ -36,10 +44,12 @@ pub(super) fn failed_to_match_macro(
// diagnostics.
let mut tracker = CollectTrackerAndEmitter::new(psess.dcx(), sp);
let try_success_result = if let Some(attr_args) = attr_args {
try_match_macro_attr(psess, name, attr_args, body, rules, &mut tracker)
} else {
try_match_macro(psess, name, body, rules, &mut tracker)
let try_success_result = match args {
FailedMacro::Func => try_match_macro(psess, name, body, rules, &mut tracker),
FailedMacro::Attr(attr_args) => {
try_match_macro_attr(psess, name, attr_args, body, rules, &mut tracker)
}
FailedMacro::Derive => try_match_macro_derive(psess, name, body, rules, &mut tracker),
};
if try_success_result.is_ok() {
@ -90,7 +100,7 @@ pub(super) fn failed_to_match_macro(
}
// Check whether there's a missing comma in this macro call, like `println!("{}" a);`
if attr_args.is_none()
if let FailedMacro::Func = args
&& let Some((body, comma_span)) = body.add_comma()
{
for rule in rules {

View file

@ -27,10 +27,10 @@ use rustc_session::Session;
use rustc_session::parse::{ParseSess, feature_err};
use rustc_span::edition::Edition;
use rustc_span::hygiene::Transparency;
use rustc_span::{Ident, Span, kw, sym};
use rustc_span::{Ident, Span, Symbol, kw, sym};
use tracing::{debug, instrument, trace, trace_span};
use super::diagnostics::failed_to_match_macro;
use super::diagnostics::{FailedMacro, failed_to_match_macro};
use super::macro_parser::{NamedMatches, NamedParseResult};
use super::{SequenceRepetition, diagnostics};
use crate::base::{
@ -138,6 +138,8 @@ pub(super) enum MacroRule {
body_span: Span,
rhs: mbe::TokenTree,
},
/// A derive rule, for use with `#[m]`
Derive { body: Vec<MatcherLoc>, body_span: Span, rhs: mbe::TokenTree },
}
pub struct MacroRulesMacroExpander {
@ -157,6 +159,7 @@ impl MacroRulesMacroExpander {
MacroRule::Attr { args_span, body_span, ref rhs, .. } => {
(MultiSpan::from_spans(vec![args_span, body_span]), rhs)
}
MacroRule::Derive { body_span, ref rhs, .. } => (MultiSpan::from_span(body_span), rhs),
};
if has_compile_error_macro(rhs) { None } else { Some((&self.name, span)) }
}
@ -164,6 +167,63 @@ impl MacroRulesMacroExpander {
pub fn kinds(&self) -> MacroKinds {
self.kinds
}
pub fn expand_derive(
&self,
cx: &mut ExtCtxt<'_>,
sp: Span,
body: &TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> {
// This is similar to `expand_macro`, but they have very different signatures, and will
// diverge further once derives support arguments.
let Self { name, ref rules, node_id, .. } = *self;
let psess = &cx.sess.psess;
if cx.trace_macros() {
let msg = format!("expanding `#[derive({name})] {}`", pprust::tts_to_string(body));
trace_macros_note(&mut cx.expansions, sp, msg);
}
match try_match_macro_derive(psess, name, body, rules, &mut NoopTracker) {
Ok((rule_index, rule, named_matches)) => {
let MacroRule::Derive { rhs, .. } = rule else {
panic!("try_match_macro_derive returned non-derive rule");
};
let mbe::TokenTree::Delimited(rhs_span, _, rhs) = rhs else {
cx.dcx().span_bug(sp, "malformed macro derive rhs");
};
let id = cx.current_expansion.id;
let tts = transcribe(psess, &named_matches, rhs, *rhs_span, self.transparency, id)
.map_err(|e| e.emit())?;
if cx.trace_macros() {
let msg = format!("to `{}`", pprust::tts_to_string(&tts));
trace_macros_note(&mut cx.expansions, sp, msg);
}
if is_defined_in_current_crate(node_id) {
cx.resolver.record_macro_rule_usage(node_id, rule_index);
}
Ok(tts)
}
Err(CanRetry::No(guar)) => Err(guar),
Err(CanRetry::Yes) => {
let (_, guar) = failed_to_match_macro(
cx.psess(),
sp,
self.span,
name,
FailedMacro::Derive,
body,
rules,
);
cx.macro_error_and_trace_macros_diag();
Err(guar)
}
}
}
}
impl TTMacroExpander for MacroRulesMacroExpander {
@ -325,8 +385,15 @@ fn expand_macro<'cx>(
}
Err(CanRetry::Yes) => {
// Retry and emit a better error.
let (span, guar) =
failed_to_match_macro(cx.psess(), sp, def_span, name, None, &arg, rules);
let (span, guar) = failed_to_match_macro(
cx.psess(),
sp,
def_span,
name,
FailedMacro::Func,
&arg,
rules,
);
cx.macro_error_and_trace_macros_diag();
DummyResult::any(span, guar)
}
@ -388,8 +455,15 @@ fn expand_macro_attr(
Err(CanRetry::No(guar)) => Err(guar),
Err(CanRetry::Yes) => {
// Retry and emit a better error.
let (_, guar) =
failed_to_match_macro(cx.psess(), sp, def_span, name, Some(&args), &body, rules);
let (_, guar) = failed_to_match_macro(
cx.psess(),
sp,
def_span,
name,
FailedMacro::Attr(&args),
&body,
rules,
);
cx.trace_macros_diag();
Err(guar)
}
@ -522,6 +596,7 @@ pub(super) fn try_match_macro_attr<'matcher, T: Tracker<'matcher>>(
match result {
Success(body_named_matches) => {
psess.gated_spans.merge(gated_spans_snapshot);
#[allow(rustc::potential_query_instability)]
named_matches.extend(body_named_matches);
return Ok((i, rule, named_matches));
}
@ -536,6 +611,44 @@ pub(super) fn try_match_macro_attr<'matcher, T: Tracker<'matcher>>(
Err(CanRetry::Yes)
}
/// Try expanding the macro derive. Returns the index of the successful arm and its
/// named_matches if it was successful, and nothing if it failed. On failure, it's the caller's job
/// to use `track` accordingly to record all errors correctly.
#[instrument(level = "debug", skip(psess, body, rules, track), fields(tracking = %T::description()))]
pub(super) fn try_match_macro_derive<'matcher, T: Tracker<'matcher>>(
psess: &ParseSess,
name: Ident,
body: &TokenStream,
rules: &'matcher [MacroRule],
track: &mut T,
) -> Result<(usize, &'matcher MacroRule, NamedMatches), CanRetry> {
// This uses the same strategy as `try_match_macro`
let body_parser = parser_from_cx(psess, body.clone(), T::recovery());
let mut tt_parser = TtParser::new(name);
for (i, rule) in rules.iter().enumerate() {
let MacroRule::Derive { body, .. } = rule else { continue };
let mut gated_spans_snapshot = mem::take(&mut *psess.gated_spans.spans.borrow_mut());
let result = tt_parser.parse_tt(&mut Cow::Borrowed(&body_parser), body, track);
track.after_arm(true, &result);
match result {
Success(named_matches) => {
psess.gated_spans.merge(gated_spans_snapshot);
return Ok((i, rule, named_matches));
}
Failure(_) => {
mem::swap(&mut gated_spans_snapshot, &mut psess.gated_spans.spans.borrow_mut())
}
Error(_, _) => return Err(CanRetry::Yes),
ErrorReported(guar) => return Err(CanRetry::No(guar)),
}
}
Err(CanRetry::Yes)
}
/// Converts a macro item into a syntax extension.
pub fn compile_declarative_macro(
sess: &Session,
@ -569,7 +682,7 @@ pub fn compile_declarative_macro(
let mut rules = Vec::new();
while p.token != token::Eof {
let args = if p.eat_keyword_noexpect(sym::attr) {
let (args, is_derive) = if p.eat_keyword_noexpect(sym::attr) {
kinds |= MacroKinds::ATTR;
if !features.macro_attr() {
feature_err(sess, sym::macro_attr, span, "`macro_rules!` attributes are unstable")
@ -579,16 +692,46 @@ pub fn compile_declarative_macro(
return dummy_syn_ext(guar);
}
let args = p.parse_token_tree();
check_args_parens(sess, &args);
check_args_parens(sess, sym::attr, &args);
let args = parse_one_tt(args, RulePart::Pattern, sess, node_id, features, edition);
check_emission(check_lhs(sess, node_id, &args));
if let Some(guar) = check_no_eof(sess, &p, "expected macro attr body") {
return dummy_syn_ext(guar);
}
Some(args)
(Some(args), false)
} else if p.eat_keyword_noexpect(sym::derive) {
kinds |= MacroKinds::DERIVE;
let derive_keyword_span = p.prev_token.span;
if !features.macro_derive() {
feature_err(sess, sym::macro_attr, span, "`macro_rules!` derives are unstable")
.emit();
}
if let Some(guar) = check_no_eof(sess, &p, "expected `()` after `derive`") {
return dummy_syn_ext(guar);
}
let args = p.parse_token_tree();
check_args_parens(sess, sym::derive, &args);
let args_empty_result = check_args_empty(sess, &args);
let args_not_empty = args_empty_result.is_err();
check_emission(args_empty_result);
if let Some(guar) = check_no_eof(sess, &p, "expected macro derive body") {
return dummy_syn_ext(guar);
}
// If the user has `=>` right after the `()`, they might have forgotten the empty
// parentheses.
if p.token == token::FatArrow {
let mut err = sess
.dcx()
.struct_span_err(p.token.span, "expected macro derive body, got `=>`");
if args_not_empty {
err.span_label(derive_keyword_span, "need `()` after this `derive`");
}
return dummy_syn_ext(err.emit());
}
(None, true)
} else {
kinds |= MacroKinds::BANG;
None
(None, false)
};
let lhs_tt = p.parse_token_tree();
let lhs_tt = parse_one_tt(lhs_tt, RulePart::Pattern, sess, node_id, features, edition);
@ -619,6 +762,8 @@ pub fn compile_declarative_macro(
let args = mbe::macro_parser::compute_locs(&delimited.tts);
let body_span = lhs_span;
rules.push(MacroRule::Attr { args, args_span, body: lhs, body_span, rhs: rhs_tt });
} else if is_derive {
rules.push(MacroRule::Derive { body: lhs, body_span: lhs_span, rhs: rhs_tt });
} else {
rules.push(MacroRule::Func { lhs, lhs_span, rhs: rhs_tt });
}
@ -665,7 +810,7 @@ fn check_no_eof(sess: &Session, p: &Parser<'_>, msg: &'static str) -> Option<Err
None
}
fn check_args_parens(sess: &Session, args: &tokenstream::TokenTree) {
fn check_args_parens(sess: &Session, rule_kw: Symbol, args: &tokenstream::TokenTree) {
// This does not handle the non-delimited case; that gets handled separately by `check_lhs`.
if let tokenstream::TokenTree::Delimited(dspan, _, delim, _) = args
&& *delim != Delimiter::Parenthesis
@ -673,10 +818,21 @@ fn check_args_parens(sess: &Session, args: &tokenstream::TokenTree) {
sess.dcx().emit_err(errors::MacroArgsBadDelim {
span: dspan.entire(),
sugg: errors::MacroArgsBadDelimSugg { open: dspan.open, close: dspan.close },
rule_kw,
});
}
}
fn check_args_empty(sess: &Session, args: &tokenstream::TokenTree) -> Result<(), ErrorGuaranteed> {
match args {
tokenstream::TokenTree::Delimited(.., delimited) if delimited.is_empty() => Ok(()),
_ => {
let msg = "`derive` rules do not accept arguments; `derive` must be followed by `()`";
Err(sess.dcx().span_err(args.span(), msg))
}
}
}
fn check_lhs(sess: &Session, node_id: NodeId, lhs: &mbe::TokenTree) -> Result<(), ErrorGuaranteed> {
let e1 = check_lhs_nt_follows(sess, node_id, lhs);
let e2 = check_lhs_no_empty_seq(sess, slice::from_ref(lhs));

View file

@ -6,6 +6,7 @@ use AttributeDuplicates::*;
use AttributeGate::*;
use AttributeType::*;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::AttrStyle;
use rustc_hir::attrs::EncodeCrossCrate;
use rustc_span::edition::Edition;
use rustc_span::{Symbol, sym};
@ -132,9 +133,12 @@ pub struct AttributeTemplate {
}
impl AttributeTemplate {
pub fn suggestions(&self, inner: bool, name: impl std::fmt::Display) -> Vec<String> {
pub fn suggestions(&self, style: AttrStyle, name: impl std::fmt::Display) -> Vec<String> {
let mut suggestions = vec![];
let inner = if inner { "!" } else { "" };
let inner = match style {
AttrStyle::Outer => "",
AttrStyle::Inner => "!",
};
if self.word {
suggestions.push(format!("#{inner}[{name}]"));
}
@ -741,9 +745,8 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
ErrorPreceding, EncodeCrossCrate::No
),
gated!(
no_sanitize, Normal,
template!(List: &["address, kcfi, memory, thread"]), DuplicatesOk,
EncodeCrossCrate::No, experimental!(no_sanitize)
sanitize, Normal, template!(List: &[r#"address = "on|off""#, r#"kernel_address = "on|off""#, r#"cfi = "on|off""#, r#"hwaddress = "on|off""#, r#"kcfi = "on|off""#, r#"memory = "on|off""#, r#"memtag = "on|off""#, r#"shadow_call_stack = "on|off""#, r#"thread = "on|off""#]), ErrorPreceding,
EncodeCrossCrate::No, sanitize, experimental!(sanitize),
),
gated!(
coverage, Normal, template!(OneOf: &[sym::off, sym::on]),

View file

@ -190,6 +190,9 @@ declare_features! (
(removed, no_coverage, "1.74.0", Some(84605), Some("renamed to `coverage_attribute`"), 114656),
/// Allows `#[no_debug]`.
(removed, no_debug, "1.43.0", Some(29721), Some("removed due to lack of demand"), 69667),
// Allows the use of `no_sanitize` attribute.
/// The feature was renamed to `sanitize` and the attribute to `#[sanitize(xyz = "on|off")]`
(removed, no_sanitize, "CURRENT_RUSTC_VERSION", Some(39699), Some(r#"renamed to sanitize(xyz = "on|off")"#), 142681),
/// Note: this feature was previously recorded in a separate
/// `STABLE_REMOVED` list because it, uniquely, was once stable but was
/// then removed. But there was no utility storing it separately, so now

View file

@ -556,6 +556,8 @@ declare_features! (
(incomplete, loop_match, "1.90.0", Some(132306)),
/// Allow `macro_rules!` attribute rules
(unstable, macro_attr, "CURRENT_RUSTC_VERSION", Some(83527)),
/// Allow `macro_rules!` derive rules
(unstable, macro_derive, "CURRENT_RUSTC_VERSION", Some(143549)),
/// Give access to additional metadata about declarative macro meta-variables.
(unstable, macro_metavar_expr, "1.61.0", Some(83527)),
/// Provides a way to concatenate identifiers using metavariable expressions.
@ -592,8 +594,6 @@ declare_features! (
(unstable, new_range, "1.86.0", Some(123741)),
/// Allows `#![no_core]`.
(unstable, no_core, "1.3.0", Some(29639)),
/// Allows the use of `no_sanitize` attribute.
(unstable, no_sanitize, "1.42.0", Some(39699)),
/// Allows using the `non_exhaustive_omitted_patterns` lint.
(unstable, non_exhaustive_omitted_patterns_lint, "1.57.0", Some(89554)),
/// Allows `for<T>` binders in where-clauses
@ -626,6 +626,8 @@ declare_features! (
(unstable, return_type_notation, "1.70.0", Some(109417)),
/// Allows `extern "rust-cold"`.
(unstable, rust_cold_cc, "1.63.0", Some(97544)),
/// Allows the use of the `sanitize` attribute.
(unstable, sanitize, "CURRENT_RUSTC_VERSION", Some(39699)),
/// Allows the use of SIMD types in functions declared in `extern` blocks.
(unstable, simd_ffi, "1.0.0", Some(27731)),
/// Allows specialization of implementations (RFC 1210).

View file

@ -205,6 +205,22 @@ pub enum Linkage {
WeakODR,
}
#[derive(Clone, Copy, Decodable, Debug, Encodable, PartialEq)]
#[derive(HashStable_Generic, PrintAttribute)]
pub enum MirDialect {
Analysis,
Built,
Runtime,
}
#[derive(Clone, Copy, Decodable, Debug, Encodable, PartialEq)]
#[derive(HashStable_Generic, PrintAttribute)]
pub enum MirPhase {
Initial,
PostCleanup,
Optimized,
}
/// Represents parsed *built-in* inert attributes.
///
/// ## Overview
@ -324,6 +340,9 @@ pub enum AttributeKind {
/// Represents `#[coverage(..)]`.
Coverage(Span, CoverageAttrKind),
/// Represents `#[custom_mir]`.
CustomMir(Option<(MirDialect, Span)>, Option<(MirPhase, Span)>, Span),
///Represents `#[rustc_deny_explicit_impl]`.
DenyExplicitImpl(Span),

View file

@ -31,6 +31,7 @@ impl AttributeKind {
ConstTrait(..) => No,
Coroutine(..) => No,
Coverage(..) => No,
CustomMir(_, _, _) => Yes,
DenyExplicitImpl(..) => No,
Deprecation { .. } => Yes,
DoNotImplementViaObject(..) => No,

View file

@ -290,6 +290,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| ExprKind::Let(..)
| ExprKind::Loop(..)
| ExprKind::Match(..) => {}
// Do not warn on `as` casts from never to any,
// they are sometimes required to appeal typeck.
ExprKind::Cast(_, _) => {}
// If `expr` is a result of desugaring the try block and is an ok-wrapped
// diverging expression (e.g. it arose from desugaring of `try { return }`),
// we skip issuing a warning because it is autogenerated code.

View file

@ -782,22 +782,30 @@ impl<'tcx> InferCtxt<'tcx> {
self.inner.borrow_mut().type_variables().num_vars()
}
pub fn next_ty_vid(&self, span: Span) -> TyVid {
self.next_ty_vid_with_origin(TypeVariableOrigin { span, param_def_id: None })
}
pub fn next_ty_vid_with_origin(&self, origin: TypeVariableOrigin) -> TyVid {
self.inner.borrow_mut().type_variables().new_var(self.universe(), origin)
}
pub fn next_ty_vid_in_universe(&self, span: Span, universe: ty::UniverseIndex) -> TyVid {
let origin = TypeVariableOrigin { span, param_def_id: None };
self.inner.borrow_mut().type_variables().new_var(universe, origin)
}
pub fn next_ty_var(&self, span: Span) -> Ty<'tcx> {
self.next_ty_var_with_origin(TypeVariableOrigin { span, param_def_id: None })
}
pub fn next_ty_var_with_origin(&self, origin: TypeVariableOrigin) -> Ty<'tcx> {
let vid = self.inner.borrow_mut().type_variables().new_var(self.universe(), origin);
let vid = self.next_ty_vid_with_origin(origin);
Ty::new_var(self.tcx, vid)
}
pub fn next_ty_var_id_in_universe(&self, span: Span, universe: ty::UniverseIndex) -> TyVid {
let origin = TypeVariableOrigin { span, param_def_id: None };
self.inner.borrow_mut().type_variables().new_var(universe, origin)
}
pub fn next_ty_var_in_universe(&self, span: Span, universe: ty::UniverseIndex) -> Ty<'tcx> {
let vid = self.next_ty_var_id_in_universe(span, universe);
let vid = self.next_ty_vid_in_universe(span, universe);
Ty::new_var(self.tcx, vid)
}

View file

@ -285,7 +285,9 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch
.expecteds
.entry(name.name)
.and_modify(|v| match v {
ExpectedValues::Some(v) if !values_any_specified => {
ExpectedValues::Some(v) if !values_any_specified =>
{
#[allow(rustc::potential_query_instability)]
v.extend(values.clone())
}
ExpectedValues::Some(_) => *v = ExpectedValues::Any,

View file

@ -807,6 +807,7 @@ fn test_unstable_options_tracking_hash() {
tracked!(hint_mostly_unused, true);
tracked!(human_readable_cgu_names, true);
tracked!(incremental_ignore_spans, true);
tracked!(indirect_branch_cs_prefix, true);
tracked!(inline_mir, Some(true));
tracked!(inline_mir_hint_threshold, Some(123));
tracked!(inline_mir_threshold, Some(123));

View file

@ -179,7 +179,7 @@ impl ClashingExternDeclarations {
/// symbol's name.
fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: hir::OwnerId) -> SymbolName {
if let Some((overridden_link_name, overridden_link_name_span)) =
tcx.codegen_fn_attrs(fi).link_name.map(|overridden_link_name| {
tcx.codegen_fn_attrs(fi).symbol_name.map(|overridden_link_name| {
// FIXME: Instead of searching through the attributes again to get span
// information, we could have codegen_fn_attrs also give span information back for
// where the attribute was defined. However, until this is found to be a

View file

@ -1,10 +1,10 @@
//! Some lints that are only useful in the compiler or crates that use compiler internals, such as
//! Clippy.
use rustc_hir::HirId;
use rustc_hir::def::Res;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy};
use rustc_hir::{Expr, ExprKind, HirId};
use rustc_middle::ty::{self, ClauseKind, GenericArgsRef, PredicatePolarity, TraitPredicate, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::{Span, sym};
@ -56,25 +56,6 @@ impl LateLintPass<'_> for DefaultHashTypes {
}
}
/// Helper function for lints that check for expressions with calls and use typeck results to
/// get the `DefId` and `GenericArgsRef` of the function.
fn typeck_results_of_method_fn<'tcx>(
cx: &LateContext<'tcx>,
expr: &hir::Expr<'_>,
) -> Option<(Span, DefId, ty::GenericArgsRef<'tcx>)> {
match expr.kind {
hir::ExprKind::MethodCall(segment, ..)
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) =>
{
Some((segment.ident.span, def_id, cx.typeck_results().node_args(expr.hir_id)))
}
_ => match cx.typeck_results().node_type(expr.hir_id).kind() {
&ty::FnDef(def_id, args) => Some((expr.span, def_id, args)),
_ => None,
},
}
}
declare_tool_lint! {
/// The `potential_query_instability` lint detects use of methods which can lead to
/// potential query instability, such as iterating over a `HashMap`.
@ -101,10 +82,12 @@ declare_tool_lint! {
declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY, UNTRACKED_QUERY_INFORMATION]);
impl LateLintPass<'_> for QueryStability {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
let Some((span, def_id, args)) = typeck_results_of_method_fn(cx, expr) else { return };
if let Ok(Some(instance)) = ty::Instance::try_resolve(cx.tcx, cx.typing_env(), def_id, args)
impl<'tcx> LateLintPass<'tcx> for QueryStability {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if let Some((callee_def_id, span, generic_args, _recv, _args)) =
get_callee_span_generic_args_and_args(cx, expr)
&& let Ok(Some(instance)) =
ty::Instance::try_resolve(cx.tcx, cx.typing_env(), callee_def_id, generic_args)
{
let def_id = instance.def_id();
if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) {
@ -113,7 +96,15 @@ impl LateLintPass<'_> for QueryStability {
span,
QueryInstability { query: cx.tcx.item_name(def_id) },
);
} else if has_unstable_into_iter_predicate(cx, callee_def_id, generic_args) {
let call_span = span.with_hi(expr.span.hi());
cx.emit_span_lint(
POTENTIAL_QUERY_INSTABILITY,
call_span,
QueryInstability { query: sym::into_iter },
);
}
if cx.tcx.has_attr(def_id, sym::rustc_lint_untracked_query_information) {
cx.emit_span_lint(
UNTRACKED_QUERY_INFORMATION,
@ -125,6 +116,64 @@ impl LateLintPass<'_> for QueryStability {
}
}
fn has_unstable_into_iter_predicate<'tcx>(
cx: &LateContext<'tcx>,
callee_def_id: DefId,
generic_args: GenericArgsRef<'tcx>,
) -> bool {
let Some(into_iterator_def_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator) else {
return false;
};
let Some(into_iter_fn_def_id) = cx.tcx.lang_items().into_iter_fn() else {
return false;
};
let predicates = cx.tcx.predicates_of(callee_def_id).instantiate(cx.tcx, generic_args);
for (predicate, _) in predicates {
let ClauseKind::Trait(TraitPredicate { trait_ref, polarity: PredicatePolarity::Positive }) =
predicate.kind().skip_binder()
else {
continue;
};
// Does the function or method require any of its arguments to implement `IntoIterator`?
if trait_ref.def_id != into_iterator_def_id {
continue;
}
let Ok(Some(instance)) =
ty::Instance::try_resolve(cx.tcx, cx.typing_env(), into_iter_fn_def_id, trait_ref.args)
else {
continue;
};
// Does the input type's `IntoIterator` implementation have the
// `rustc_lint_query_instability` attribute on its `into_iter` method?
if cx.tcx.has_attr(instance.def_id(), sym::rustc_lint_query_instability) {
return true;
}
}
false
}
/// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
/// `Span`, `GenericArgs`, and arguments. This is a slight augmentation of a similarly named Clippy
/// function, `get_callee_generic_args_and_args`.
fn get_callee_span_generic_args_and_args<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
) -> Option<(DefId, Span, GenericArgsRef<'tcx>, Option<&'tcx Expr<'tcx>>, &'tcx [Expr<'tcx>])> {
if let ExprKind::Call(callee, args) = expr.kind
&& let callee_ty = cx.typeck_results().expr_ty(callee)
&& let ty::FnDef(callee_def_id, generic_args) = callee_ty.kind()
{
return Some((*callee_def_id, callee.span, generic_args, None, args));
}
if let ExprKind::MethodCall(segment, recv, args, _) = expr.kind
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
{
let generic_args = cx.typeck_results().node_args(expr.hir_id);
return Some((method_def_id, segment.ident.span, generic_args, Some(recv), args));
}
None
}
declare_tool_lint! {
/// The `usage_of_ty_tykind` lint detects usages of `ty::TyKind::<kind>`,
/// where `ty::<kind>` would suffice.
@ -461,33 +510,22 @@ declare_tool_lint! {
declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL]);
impl LateLintPass<'_> for Diagnostics {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
let collect_args_tys_and_spans = |args: &[hir::Expr<'_>], reserve_one_extra: bool| {
let mut result = Vec::with_capacity(args.len() + usize::from(reserve_one_extra));
result.extend(args.iter().map(|arg| (cx.typeck_results().expr_ty(arg), arg.span)));
result
};
// Only check function calls and method calls.
let (span, def_id, fn_gen_args, arg_tys_and_spans) = match expr.kind {
hir::ExprKind::Call(callee, args) => {
match cx.typeck_results().node_type(callee.hir_id).kind() {
&ty::FnDef(def_id, fn_gen_args) => {
(callee.span, def_id, fn_gen_args, collect_args_tys_and_spans(args, false))
}
_ => return, // occurs for fns passed as args
}
}
hir::ExprKind::MethodCall(_segment, _recv, args, _span) => {
let Some((span, def_id, fn_gen_args)) = typeck_results_of_method_fn(cx, expr)
else {
return;
};
let mut args = collect_args_tys_and_spans(args, true);
args.insert(0, (cx.tcx.types.self_param, _recv.span)); // dummy inserted for `self`
(span, def_id, fn_gen_args, args)
}
_ => return,
let Some((def_id, span, fn_gen_args, recv, args)) =
get_callee_span_generic_args_and_args(cx, expr)
else {
return;
};
let mut arg_tys_and_spans = collect_args_tys_and_spans(args, recv.is_some());
if let Some(recv) = recv {
arg_tys_and_spans.insert(0, (cx.tcx.types.self_param, recv.span)); // dummy inserted for `self`
}
Self::diagnostic_outside_of_impl(cx, span, expr.hir_id, def_id, fn_gen_args);
Self::untranslatable_diagnostic(cx, def_id, &arg_tys_and_spans);
@ -496,7 +534,7 @@ impl LateLintPass<'_> for Diagnostics {
impl Diagnostics {
// Is the type `{D,Subd}iagMessage`?
fn is_diag_message<'cx>(cx: &LateContext<'cx>, ty: MiddleTy<'cx>) -> bool {
fn is_diag_message<'cx>(cx: &LateContext<'cx>, ty: Ty<'cx>) -> bool {
if let Some(adt_def) = ty.ty_adt_def()
&& let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did())
&& matches!(name, sym::DiagMessage | sym::SubdiagMessage)
@ -510,7 +548,7 @@ impl Diagnostics {
fn untranslatable_diagnostic<'cx>(
cx: &LateContext<'cx>,
def_id: DefId,
arg_tys_and_spans: &[(MiddleTy<'cx>, Span)],
arg_tys_and_spans: &[(Ty<'cx>, Span)],
) {
let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
let predicates = cx.tcx.predicates_of(def_id).instantiate_identity(cx.tcx).predicates;

View file

@ -2301,18 +2301,18 @@ declare_lint! {
declare_lint! {
/// The `inline_no_sanitize` lint detects incompatible use of
/// [`#[inline(always)]`][inline] and [`#[no_sanitize(...)]`][no_sanitize].
/// [`#[inline(always)]`][inline] and [`#[sanitize(xyz = "off")]`][sanitize].
///
/// [inline]: https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute
/// [no_sanitize]: https://doc.rust-lang.org/nightly/unstable-book/language-features/no-sanitize.html
/// [sanitize]: https://doc.rust-lang.org/nightly/unstable-book/language-features/no-sanitize.html
///
/// ### Example
///
/// ```rust
/// #![feature(no_sanitize)]
/// #![feature(sanitize)]
///
/// #[inline(always)]
/// #[no_sanitize(address)]
/// #[sanitize(address = "off")]
/// fn x() {}
///
/// fn main() {
@ -2325,11 +2325,11 @@ declare_lint! {
/// ### Explanation
///
/// The use of the [`#[inline(always)]`][inline] attribute prevents the
/// the [`#[no_sanitize(...)]`][no_sanitize] attribute from working.
/// the [`#[sanitize(xyz = "off")]`][sanitize] attribute from working.
/// Consider temporarily removing `inline` attribute.
pub INLINE_NO_SANITIZE,
Warn,
"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`",
r#"detects incompatible use of `#[inline(always)]` and `#[sanitize(... = "off")]`"#,
}
declare_lint! {

View file

@ -226,7 +226,6 @@ fn main() {
rerun_if_changed_anything_in_dir(Path::new("llvm-wrapper"));
cfg.file("llvm-wrapper/PassWrapper.cpp")
.file("llvm-wrapper/RustWrapper.cpp")
.file("llvm-wrapper/ArchiveWrapper.cpp")
.file("llvm-wrapper/CoverageMappingWrapper.cpp")
.file("llvm-wrapper/SymbolWrapper.cpp")
.file("llvm-wrapper/Linker.cpp")

View file

@ -1,208 +0,0 @@
#include "LLVMWrapper.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ArchiveWriter.h"
#include "llvm/Support/Path.h"
using namespace llvm;
using namespace llvm::object;
struct RustArchiveMember {
const char *Filename;
const char *Name;
Archive::Child Child;
RustArchiveMember()
: Filename(nullptr), Name(nullptr), Child(nullptr, nullptr, nullptr) {}
~RustArchiveMember() {}
};
struct RustArchiveIterator {
bool First;
Archive::child_iterator Cur;
Archive::child_iterator End;
std::unique_ptr<Error> Err;
RustArchiveIterator(Archive::child_iterator Cur, Archive::child_iterator End,
std::unique_ptr<Error> Err)
: First(true), Cur(Cur), End(End), Err(std::move(Err)) {}
};
enum class LLVMRustArchiveKind {
GNU,
BSD,
DARWIN,
COFF,
AIX_BIG,
};
static Archive::Kind fromRust(LLVMRustArchiveKind Kind) {
switch (Kind) {
case LLVMRustArchiveKind::GNU:
return Archive::K_GNU;
case LLVMRustArchiveKind::BSD:
return Archive::K_BSD;
case LLVMRustArchiveKind::DARWIN:
return Archive::K_DARWIN;
case LLVMRustArchiveKind::COFF:
return Archive::K_COFF;
case LLVMRustArchiveKind::AIX_BIG:
return Archive::K_AIXBIG;
default:
report_fatal_error("Bad ArchiveKind.");
}
}
typedef OwningBinary<Archive> *LLVMRustArchiveRef;
typedef RustArchiveMember *LLVMRustArchiveMemberRef;
typedef Archive::Child *LLVMRustArchiveChildRef;
typedef Archive::Child const *LLVMRustArchiveChildConstRef;
typedef RustArchiveIterator *LLVMRustArchiveIteratorRef;
extern "C" LLVMRustArchiveRef LLVMRustOpenArchive(char *Path) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BufOr = MemoryBuffer::getFile(
Path, /*IsText*/ false, /*RequiresNullTerminator=*/false);
if (!BufOr) {
LLVMRustSetLastError(BufOr.getError().message().c_str());
return nullptr;
}
Expected<std::unique_ptr<Archive>> ArchiveOr =
Archive::create(BufOr.get()->getMemBufferRef());
if (!ArchiveOr) {
LLVMRustSetLastError(toString(ArchiveOr.takeError()).c_str());
return nullptr;
}
OwningBinary<Archive> *Ret = new OwningBinary<Archive>(
std::move(ArchiveOr.get()), std::move(BufOr.get()));
return Ret;
}
extern "C" void LLVMRustDestroyArchive(LLVMRustArchiveRef RustArchive) {
delete RustArchive;
}
extern "C" LLVMRustArchiveIteratorRef
LLVMRustArchiveIteratorNew(LLVMRustArchiveRef RustArchive) {
Archive *Archive = RustArchive->getBinary();
std::unique_ptr<Error> Err = std::make_unique<Error>(Error::success());
auto Cur = Archive->child_begin(*Err);
if (*Err) {
LLVMRustSetLastError(toString(std::move(*Err)).c_str());
return nullptr;
}
auto End = Archive->child_end();
return new RustArchiveIterator(Cur, End, std::move(Err));
}
extern "C" LLVMRustArchiveChildConstRef
LLVMRustArchiveIteratorNext(LLVMRustArchiveIteratorRef RAI) {
if (RAI->Cur == RAI->End)
return nullptr;
// Advancing the iterator validates the next child, and this can
// uncover an error. LLVM requires that we check all Errors,
// so we only advance the iterator if we actually need to fetch
// the next child.
// This means we must not advance the iterator in the *first* call,
// but instead advance it *before* fetching the child in all later calls.
if (!RAI->First) {
++RAI->Cur;
if (*RAI->Err) {
LLVMRustSetLastError(toString(std::move(*RAI->Err)).c_str());
return nullptr;
}
} else {
RAI->First = false;
}
if (RAI->Cur == RAI->End)
return nullptr;
const Archive::Child &Child = *RAI->Cur.operator->();
Archive::Child *Ret = new Archive::Child(Child);
return Ret;
}
extern "C" void LLVMRustArchiveChildFree(LLVMRustArchiveChildRef Child) {
delete Child;
}
extern "C" void LLVMRustArchiveIteratorFree(LLVMRustArchiveIteratorRef RAI) {
delete RAI;
}
extern "C" const char *
LLVMRustArchiveChildName(LLVMRustArchiveChildConstRef Child, size_t *Size) {
Expected<StringRef> NameOrErr = Child->getName();
if (!NameOrErr) {
// rustc_codegen_llvm currently doesn't use this error string, but it might
// be useful in the future, and in the meantime this tells LLVM that the
// error was not ignored and that it shouldn't abort the process.
LLVMRustSetLastError(toString(NameOrErr.takeError()).c_str());
return nullptr;
}
StringRef Name = NameOrErr.get();
*Size = Name.size();
return Name.data();
}
extern "C" LLVMRustArchiveMemberRef
LLVMRustArchiveMemberNew(char *Filename, char *Name,
LLVMRustArchiveChildRef Child) {
RustArchiveMember *Member = new RustArchiveMember;
Member->Filename = Filename;
Member->Name = Name;
if (Child)
Member->Child = *Child;
return Member;
}
extern "C" void LLVMRustArchiveMemberFree(LLVMRustArchiveMemberRef Member) {
delete Member;
}
extern "C" LLVMRustResult LLVMRustWriteArchive(
char *Dst, size_t NumMembers, const LLVMRustArchiveMemberRef *NewMembers,
bool WriteSymbtab, LLVMRustArchiveKind RustKind, bool isEC) {
std::vector<NewArchiveMember> Members;
auto Kind = fromRust(RustKind);
for (size_t I = 0; I < NumMembers; I++) {
auto Member = NewMembers[I];
assert(Member->Name);
if (Member->Filename) {
Expected<NewArchiveMember> MOrErr =
NewArchiveMember::getFile(Member->Filename, true);
if (!MOrErr) {
LLVMRustSetLastError(toString(MOrErr.takeError()).c_str());
return LLVMRustResult::Failure;
}
MOrErr->MemberName = sys::path::filename(MOrErr->MemberName);
Members.push_back(std::move(*MOrErr));
} else {
Expected<NewArchiveMember> MOrErr =
NewArchiveMember::getOldMember(Member->Child, true);
if (!MOrErr) {
LLVMRustSetLastError(toString(MOrErr.takeError()).c_str());
return LLVMRustResult::Failure;
}
Members.push_back(std::move(*MOrErr));
}
}
auto SymtabMode = WriteSymbtab ? SymtabWritingMode::NormalSymtab
: SymtabWritingMode::NoSymtab;
auto Result =
writeArchive(Dst, Members, SymtabMode, Kind, true, false, nullptr, isEC);
if (!Result)
return LLVMRustResult::Success;
LLVMRustSetLastError(toString(std::move(Result)).c_str());
return LLVMRustResult::Failure;
}

View file

@ -1460,60 +1460,6 @@ LLVMRustGetDiagInfoKind(LLVMDiagnosticInfoRef DI) {
return toRust((DiagnosticKind)unwrap(DI)->getKind());
}
// This is kept distinct from LLVMGetTypeKind, because when
// a new type kind is added, the Rust-side enum must be
// updated or UB will result.
extern "C" LLVMTypeKind LLVMRustGetTypeKind(LLVMTypeRef Ty) {
switch (unwrap(Ty)->getTypeID()) {
case Type::VoidTyID:
return LLVMVoidTypeKind;
case Type::HalfTyID:
return LLVMHalfTypeKind;
case Type::FloatTyID:
return LLVMFloatTypeKind;
case Type::DoubleTyID:
return LLVMDoubleTypeKind;
case Type::X86_FP80TyID:
return LLVMX86_FP80TypeKind;
case Type::FP128TyID:
return LLVMFP128TypeKind;
case Type::PPC_FP128TyID:
return LLVMPPC_FP128TypeKind;
case Type::LabelTyID:
return LLVMLabelTypeKind;
case Type::MetadataTyID:
return LLVMMetadataTypeKind;
case Type::IntegerTyID:
return LLVMIntegerTypeKind;
case Type::FunctionTyID:
return LLVMFunctionTypeKind;
case Type::StructTyID:
return LLVMStructTypeKind;
case Type::ArrayTyID:
return LLVMArrayTypeKind;
case Type::PointerTyID:
return LLVMPointerTypeKind;
case Type::FixedVectorTyID:
return LLVMVectorTypeKind;
case Type::TokenTyID:
return LLVMTokenTypeKind;
case Type::ScalableVectorTyID:
return LLVMScalableVectorTypeKind;
case Type::BFloatTyID:
return LLVMBFloatTypeKind;
case Type::X86_AMXTyID:
return LLVMX86_AMXTypeKind;
default: {
std::string error;
auto stream = llvm::raw_string_ostream(error);
stream << "Rust does not support the TypeID: " << unwrap(Ty)->getTypeID()
<< " for the type: " << *unwrap(Ty);
stream.flush();
report_fatal_error(error.c_str());
}
}
}
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef)
extern "C" LLVMSMDiagnosticRef LLVMRustGetSMDiagnostic(LLVMDiagnosticInfoRef DI,
@ -1993,29 +1939,3 @@ extern "C" void LLVMRustSetNoSanitizeHWAddress(LLVMValueRef Global) {
MD.NoHWAddress = true;
GV.setSanitizerMetadata(MD);
}
enum class LLVMRustTailCallKind {
None = 0,
Tail = 1,
MustTail = 2,
NoTail = 3
};
extern "C" void LLVMRustSetTailCallKind(LLVMValueRef Call,
LLVMRustTailCallKind Kind) {
CallInst *CI = unwrap<CallInst>(Call);
switch (Kind) {
case LLVMRustTailCallKind::None:
CI->setTailCallKind(CallInst::TCK_None);
break;
case LLVMRustTailCallKind::Tail:
CI->setTailCallKind(CallInst::TCK_Tail);
break;
case LLVMRustTailCallKind::MustTail:
CI->setTailCallKind(CallInst::TCK_MustTail);
break;
case LLVMRustTailCallKind::NoTail:
CI->setTailCallKind(CallInst::TCK_NoTail);
break;
}
}

View file

@ -4,7 +4,7 @@ use syn::spanned::Spanned;
use syn::{Data, Fields, Ident};
use synstructure::Structure;
fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, TokenStream) {
fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream) {
let string_name = name.to_string();
let mut disps = vec![quote! {let mut __printed_anything = false;}];
@ -43,7 +43,6 @@ fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, Tok
#(#disps)*
__p.word("}");
},
quote! { true },
)
}
Fields::Unnamed(fields_unnamed) => {
@ -76,10 +75,9 @@ fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, Tok
#(#disps)*
__p.pclose();
},
quote! { true },
)
}
Fields::Unit => (quote! {}, quote! { __p.word(#string_name) }, quote! { true }),
Fields::Unit => (quote! {}, quote! { __p.word(#string_name) }),
}
}
@ -89,51 +87,33 @@ pub(crate) fn print_attribute(input: Structure<'_>) -> TokenStream {
};
// Must be applied to an enum type.
let (code, printed) = match &input.ast().data {
let code = match &input.ast().data {
Data::Enum(e) => {
let (arms, printed) = e
let arms = e
.variants
.iter()
.map(|x| {
let ident = &x.ident;
let (pat, code, printed) = print_fields(ident, &x.fields);
let (pat, code) = print_fields(ident, &x.fields);
(
quote! {
Self::#ident #pat => {#code}
},
quote! {
Self::#ident #pat => {#printed}
},
)
quote! {
Self::#ident #pat => {#code}
}
})
.unzip::<_, _, Vec<_>, Vec<_>>();
.collect::<Vec<_>>();
(
quote! {
match self {
#(#arms)*
}
},
quote! {
match self {
#(#printed)*
}
},
)
quote! {
match self {
#(#arms)*
}
}
}
Data::Struct(s) => {
let (pat, code, printed) = print_fields(&input.ast().ident, &s.fields);
(
quote! {
let Self #pat = self;
#code
},
quote! {
let Self #pat = self;
#printed
},
)
let (pat, code) = print_fields(&input.ast().ident, &s.fields);
quote! {
let Self #pat = self;
#code
}
}
Data::Union(u) => {
return span_error(u.union_token.span(), "can't derive PrintAttribute on unions");
@ -144,7 +124,7 @@ pub(crate) fn print_attribute(input: Structure<'_>) -> TokenStream {
input.gen_impl(quote! {
#[allow(unused)]
gen impl PrintAttribute for @Self {
fn should_render(&self) -> bool { #printed }
fn should_render(&self) -> bool { true }
fn print_attribute(&self, __p: &mut rustc_ast_pretty::pp::Printer) { #code }
}
})

View file

@ -32,6 +32,7 @@ use rustc_session::cstore::{CrateDepKind, CrateSource, ExternCrate, ExternCrateS
use rustc_session::lint::{self, BuiltinLintDiag};
use rustc_session::output::validate_crate_name;
use rustc_session::search_paths::PathKind;
use rustc_span::def_id::DefId;
use rustc_span::edition::Edition;
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};
use rustc_target::spec::{PanicStrategy, Target};
@ -275,6 +276,10 @@ impl CStore {
.filter_map(|(cnum, data)| data.as_deref().map(|data| (cnum, data)))
}
pub fn all_proc_macro_def_ids(&self) -> impl Iterator<Item = DefId> {
self.iter_crate_data().flat_map(|(krate, data)| data.proc_macros_for_crate(krate, self))
}
fn push_dependencies_in_postorder(&self, deps: &mut IndexSet<CrateNum>, cnum: CrateNum) {
if !deps.contains(&cnum) {
let data = self.get_crate_data(cnum);

View file

@ -701,7 +701,7 @@ impl<'tcx> Collector<'tcx> {
.link_ordinal
.map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord)));
let name = codegen_fn_attrs.link_name.unwrap_or_else(|| self.tcx.item_name(item));
let name = codegen_fn_attrs.symbol_name.unwrap_or_else(|| self.tcx.item_name(item));
if self.tcx.sess.target.binary_format == BinaryFormat::Elf {
let name = name.as_str();

View file

@ -2014,6 +2014,22 @@ impl CrateMetadata {
self.root.is_proc_macro_crate()
}
pub(crate) fn proc_macros_for_crate(
&self,
krate: CrateNum,
cstore: &CStore,
) -> impl Iterator<Item = DefId> {
gen move {
for def_id in self.root.proc_macro_data.as_ref().into_iter().flat_map(move |data| {
data.macros
.decode(CrateMetadataRef { cdata: self, cstore })
.map(move |index| DefId { index, krate })
}) {
yield def_id;
}
}
}
pub(crate) fn name(&self) -> Symbol {
self.root.header.name
}

View file

@ -35,14 +35,10 @@ pub struct CodegenFnAttrs {
pub inline: InlineAttr,
/// Parsed representation of the `#[optimize]` attribute
pub optimize: OptimizeAttr,
/// The `#[export_name = "..."]` attribute, indicating a custom symbol a
/// function should be exported under
pub export_name: Option<Symbol>,
/// The `#[link_name = "..."]` attribute, indicating a custom symbol an
/// imported function should be imported as. Note that `export_name`
/// probably isn't set when this is set, this is for foreign items while
/// `#[export_name]` is for Rust-defined functions.
pub link_name: Option<Symbol>,
/// The name this function will be imported/exported under. This can be set
/// using the `#[export_name = "..."]` or `#[link_name = "..."]` attribute
/// depending on if this is a function definition or foreign function.
pub symbol_name: Option<Symbol>,
/// The `#[link_ordinal = "..."]` attribute, indicating an ordinal an
/// imported function has in the dynamic library. Note that this must not
/// be set when `link_name` is set. This is for foreign items with the
@ -61,8 +57,8 @@ pub struct CodegenFnAttrs {
/// The `#[link_section = "..."]` attribute, or what executable section this
/// should be placed in.
pub link_section: Option<Symbol>,
/// The `#[no_sanitize(...)]` attribute. Indicates sanitizers for which
/// instrumentation should be disabled inside the annotated function.
/// The `#[sanitize(xyz = "off")]` attribute. Indicates sanitizers for which
/// instrumentation should be disabled inside the function.
pub no_sanitize: SanitizerSet,
/// The `#[instruction_set(set)]` attribute. Indicates if the generated code should
/// be generated against a specific instruction set. Only usable on architectures which allow
@ -167,8 +163,7 @@ impl CodegenFnAttrs {
flags: CodegenFnAttrFlags::empty(),
inline: InlineAttr::None,
optimize: OptimizeAttr::Default,
export_name: None,
link_name: None,
symbol_name: None,
link_ordinal: None,
target_features: vec![],
safe_target_features: false,
@ -196,7 +191,7 @@ impl CodegenFnAttrs {
self.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
|| self.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
|| self.export_name.is_some()
|| self.symbol_name.is_some()
|| match self.linkage {
// These are private, so make sure we don't try to consider
// them external.

View file

@ -115,48 +115,6 @@ impl MirPhase {
MirPhase::Runtime(runtime_phase) => (3, 1 + runtime_phase as usize),
}
}
/// Parses a `MirPhase` from a pair of strings. Panics if this isn't possible for any reason.
pub fn parse(dialect: String, phase: Option<String>) -> Self {
match &*dialect.to_ascii_lowercase() {
"built" => {
assert!(phase.is_none(), "Cannot specify a phase for `Built` MIR");
MirPhase::Built
}
"analysis" => Self::Analysis(AnalysisPhase::parse(phase)),
"runtime" => Self::Runtime(RuntimePhase::parse(phase)),
_ => bug!("Unknown MIR dialect: '{}'", dialect),
}
}
}
impl AnalysisPhase {
pub fn parse(phase: Option<String>) -> Self {
let Some(phase) = phase else {
return Self::Initial;
};
match &*phase.to_ascii_lowercase() {
"initial" => Self::Initial,
"post_cleanup" | "post-cleanup" | "postcleanup" => Self::PostCleanup,
_ => bug!("Unknown analysis phase: '{}'", phase),
}
}
}
impl RuntimePhase {
pub fn parse(phase: Option<String>) -> Self {
let Some(phase) = phase else {
return Self::Initial;
};
match &*phase.to_ascii_lowercase() {
"initial" => Self::Initial,
"post_cleanup" | "post-cleanup" | "postcleanup" => Self::PostCleanup,
"optimized" => Self::Optimized,
_ => bug!("Unknown runtime phase: '{}'", phase),
}
}
}
/// Where a specific `mir::Body` comes from.

View file

@ -929,7 +929,10 @@ impl<'tcx> TerminatorKind<'tcx> {
}
Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"),
Unreachable => write!(fmt, "unreachable"),
Drop { place, .. } => write!(fmt, "drop({place:?})"),
Drop { place, async_fut: None, .. } => write!(fmt, "drop({place:?})"),
Drop { place, async_fut: Some(async_fut), .. } => {
write!(fmt, "async drop({place:?}; poll={async_fut:?})")
}
Call { func, args, destination, .. } => {
write!(fmt, "{destination:?} = ")?;
write!(fmt, "{func:?}(")?;

View file

@ -531,13 +531,20 @@ macro_rules! make_mir_visitor {
unwind: _,
replace: _,
drop: _,
async_fut: _,
async_fut,
} => {
self.visit_place(
place,
PlaceContext::MutatingUse(MutatingUseContext::Drop),
location
);
if let Some(async_fut) = async_fut {
self.visit_local(
$(&$mutability)? *async_fut,
PlaceContext::MutatingUse(MutatingUseContext::Borrow),
location
);
}
}
TerminatorKind::Call {

View file

@ -343,6 +343,7 @@ trivial! {
rustc_span::Symbol,
rustc_span::Ident,
rustc_target::spec::PanicStrategy,
rustc_target::spec::SanitizerSet,
rustc_type_ir::Variance,
u32,
usize,

View file

@ -100,7 +100,7 @@ use rustc_session::lint::LintExpectationId;
use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::source_map::Spanned;
use rustc_span::{DUMMY_SP, Span, Symbol};
use rustc_target::spec::PanicStrategy;
use rustc_target::spec::{PanicStrategy, SanitizerSet};
use {rustc_abi as abi, rustc_ast as ast, rustc_hir as hir};
use crate::infer::canonical::{self, Canonical};
@ -2686,6 +2686,16 @@ rustc_queries! {
desc { |tcx| "looking up anon const kind of `{}`", tcx.def_path_str(def_id) }
separate_provide_extern
}
/// Checks for the nearest `#[sanitize(xyz = "off")]` or
/// `#[sanitize(xyz = "on")]` on this def and any enclosing defs, up to the
/// crate root.
///
/// Returns the set of sanitizers that is explicitly disabled for this def.
query disabled_sanitizers_for(key: LocalDefId) -> SanitizerSet {
desc { |tcx| "checking what set of sanitizers are enabled on `{}`", tcx.def_path_str(key) }
feedable
}
}
rustc_with_all_queries! { define_callbacks! }

View file

@ -645,34 +645,29 @@ impl<'a, 'tcx> SpanDecoder for CacheDecoder<'a, 'tcx> {
let parent = Option::<LocalDefId>::decode(self);
let tag: u8 = Decodable::decode(self);
if tag == TAG_PARTIAL_SPAN {
return Span::new(BytePos(0), BytePos(0), ctxt, parent);
} else if tag == TAG_RELATIVE_SPAN {
let dlo = u32::decode(self);
let dto = u32::decode(self);
let (lo, hi) = match tag {
TAG_PARTIAL_SPAN => (BytePos(0), BytePos(0)),
TAG_RELATIVE_SPAN => {
let dlo = u32::decode(self);
let dto = u32::decode(self);
let enclosing = self.tcx.source_span_untracked(parent.unwrap()).data_untracked();
let span = Span::new(
enclosing.lo + BytePos::from_u32(dlo),
enclosing.lo + BytePos::from_u32(dto),
ctxt,
parent,
);
let enclosing = self.tcx.source_span_untracked(parent.unwrap()).data_untracked();
(enclosing.lo + BytePos::from_u32(dlo), enclosing.lo + BytePos::from_u32(dto))
}
TAG_FULL_SPAN => {
let file_lo_index = SourceFileIndex::decode(self);
let line_lo = usize::decode(self);
let col_lo = RelativeBytePos::decode(self);
let len = BytePos::decode(self);
return span;
} else {
debug_assert_eq!(tag, TAG_FULL_SPAN);
}
let file_lo_index = SourceFileIndex::decode(self);
let line_lo = usize::decode(self);
let col_lo = RelativeBytePos::decode(self);
let len = BytePos::decode(self);
let file_lo = self.file_index_to_file(file_lo_index);
let lo = file_lo.lines()[line_lo - 1] + col_lo;
let lo = file_lo.absolute_position(lo);
let hi = lo + len;
let file_lo = self.file_index_to_file(file_lo_index);
let lo = file_lo.lines()[line_lo - 1] + col_lo;
let lo = file_lo.absolute_position(lo);
let hi = lo + len;
(lo, hi)
}
_ => unreachable!(),
};
Span::new(lo, hi, ctxt, parent)
}

View file

@ -19,10 +19,10 @@
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::DefId;
use rustc_hir::{Attribute, HirId};
use rustc_hir::{HirId, attrs};
use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::bug;
use rustc_middle::mir::*;
use rustc_middle::span_bug;
use rustc_middle::thir::*;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::Span;
@ -39,7 +39,8 @@ pub(super) fn build_custom_mir<'tcx>(
return_ty: Ty<'tcx>,
return_ty_span: Span,
span: Span,
attr: &Attribute,
dialect: Option<attrs::MirDialect>,
phase: Option<attrs::MirPhase>,
) -> Body<'tcx> {
let mut body = Body {
basic_blocks: BasicBlocks::new(IndexVec::new()),
@ -72,7 +73,7 @@ pub(super) fn build_custom_mir<'tcx>(
inlined_parent_scope: None,
local_data: ClearCrossCrate::Set(SourceScopeLocalData { lint_root: hir_id }),
});
body.injection_phase = Some(parse_attribute(attr));
body.injection_phase = Some(parse_attribute(dialect, phase));
let mut pctxt = ParseCtxt {
tcx,
@ -98,40 +99,38 @@ pub(super) fn build_custom_mir<'tcx>(
body
}
fn parse_attribute(attr: &Attribute) -> MirPhase {
let meta_items = attr.meta_item_list().unwrap();
let mut dialect: Option<String> = None;
let mut phase: Option<String> = None;
// Not handling errors properly for this internal attribute; will just abort on errors.
for nested in meta_items {
let name = nested.name().unwrap();
let value = nested.value_str().unwrap().as_str().to_string();
match name.as_str() {
"dialect" => {
assert!(dialect.is_none());
dialect = Some(value);
}
"phase" => {
assert!(phase.is_none());
phase = Some(value);
}
other => {
span_bug!(
nested.span(),
"Unexpected key while parsing custom_mir attribute: '{}'",
other
);
}
}
}
/// Turns the arguments passed to `#[custom_mir(..)]` into a proper
/// [`MirPhase`]. Panics if this isn't possible for any reason.
fn parse_attribute(dialect: Option<attrs::MirDialect>, phase: Option<attrs::MirPhase>) -> MirPhase {
let Some(dialect) = dialect else {
// Caught during attribute checking.
assert!(phase.is_none());
return MirPhase::Built;
};
MirPhase::parse(dialect, phase)
match dialect {
attrs::MirDialect::Built => {
// Caught during attribute checking.
assert!(phase.is_none(), "Cannot specify a phase for `Built` MIR");
MirPhase::Built
}
attrs::MirDialect::Analysis => match phase {
None | Some(attrs::MirPhase::Initial) => MirPhase::Analysis(AnalysisPhase::Initial),
Some(attrs::MirPhase::PostCleanup) => MirPhase::Analysis(AnalysisPhase::PostCleanup),
Some(attrs::MirPhase::Optimized) => {
// Caught during attribute checking.
bug!("`optimized` dialect is not compatible with the `analysis` dialect")
}
},
attrs::MirDialect::Runtime => match phase {
None | Some(attrs::MirPhase::Initial) => MirPhase::Runtime(RuntimePhase::Initial),
Some(attrs::MirPhase::PostCleanup) => MirPhase::Runtime(RuntimePhase::PostCleanup),
Some(attrs::MirPhase::Optimized) => MirPhase::Runtime(RuntimePhase::Optimized),
},
}
}
struct ParseCtxt<'a, 'tcx> {

View file

@ -11,9 +11,10 @@ use rustc_ast::attr;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sorted_map::SortedIndexMultiMap;
use rustc_errors::ErrorGuaranteed;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, ItemLocalId, Node};
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, ItemLocalId, Node, find_attr};
use rustc_index::bit_set::GrowableBitSet;
use rustc_index::{Idx, IndexSlice, IndexVec};
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
@ -479,8 +480,7 @@ fn construct_fn<'tcx>(
ty => span_bug!(span_with_body, "unexpected type of body: {ty:?}"),
};
if let Some(custom_mir_attr) =
tcx.hir_attrs(fn_id).iter().find(|attr| attr.has_name(sym::custom_mir))
if let Some((dialect, phase)) = find_attr!(tcx.hir_attrs(fn_id), AttributeKind::CustomMir(dialect, phase, _) => (dialect, phase))
{
return custom::build_custom_mir(
tcx,
@ -492,7 +492,8 @@ fn construct_fn<'tcx>(
return_ty,
return_ty_span,
span_with_body,
custom_mir_attr,
dialect.as_ref().map(|(d, _)| *d),
phase.as_ref().map(|(p, _)| *p),
);
}

View file

@ -5,8 +5,9 @@ use std::ops::Bound;
use rustc_ast::AsmMacro;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::DiagArgValue;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::DefKind;
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability};
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability, find_attr};
use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
use rustc_middle::mir::BorrowKind;
use rustc_middle::span_bug;
@ -1157,7 +1158,7 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
// Closures and inline consts are handled by their owner, if it has a body
assert!(!tcx.is_typeck_child(def.to_def_id()));
// Also, don't safety check custom MIR
if tcx.has_attr(def, sym::custom_mir) {
if find_attr!(tcx.get_all_attrs(def), AttributeKind::CustomMir(..) => ()).is_some() {
return;
}

View file

@ -4,11 +4,11 @@
use rustc_data_structures::steal::Steal;
use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::HirId;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::lang_items::LangItem;
use rustc_hir::{self as hir, HirId, find_attr};
use rustc_middle::bug;
use rustc_middle::middle::region;
use rustc_middle::thir::*;
@ -111,10 +111,8 @@ impl<'tcx> ThirBuildCx<'tcx> {
typeck_results,
rvalue_scopes: &typeck_results.rvalue_scopes,
body_owner: def.to_def_id(),
apply_adjustments: tcx
.hir_attrs(hir_id)
.iter()
.all(|attr| !attr.has_name(rustc_span::sym::custom_mir)),
apply_adjustments:
!find_attr!(tcx.hir_attrs(hir_id), AttributeKind::CustomMir(..) => ()).is_some(),
}
}

View file

@ -27,7 +27,7 @@ enum CanonicalizeInputKind {
ParamEnv,
/// When canonicalizing predicates, we don't keep `'static`. If we're
/// currently outside of the trait solver and canonicalize the root goal
/// during HIR typeck, we replace each occurance of a region with a
/// during HIR typeck, we replace each occurrence of a region with a
/// unique region variable. See the comment on `InferCtxt::in_hir_typeck`
/// for more details.
Predicate { is_hir_typeck_root_goal: bool },

View file

@ -6,8 +6,8 @@ use rustc_type_ir::inherent::*;
use rustc_type_ir::lang_items::TraitSolverLangItem;
use rustc_type_ir::solve::{CanonicalResponse, SizedTraitKind};
use rustc_type_ir::{
self as ty, Interner, Movability, TraitPredicate, TraitRef, TypeVisitableExt as _, TypingMode,
Upcast as _, elaborate,
self as ty, Interner, Movability, PredicatePolarity, TraitPredicate, TraitRef,
TypeVisitableExt as _, TypingMode, Upcast as _, elaborate,
};
use tracing::{debug, instrument, trace};
@ -133,19 +133,26 @@ where
cx: I,
clause_def_id: I::DefId,
goal_def_id: I::DefId,
polarity: PredicatePolarity,
) -> bool {
clause_def_id == goal_def_id
// PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so
// check for a `MetaSized` supertrait being matched against a `Sized` assumption.
//
// `PointeeSized` bounds are syntactic sugar for a lack of bounds so don't need this.
|| (cx.is_lang_item(clause_def_id, TraitSolverLangItem::Sized)
|| (polarity == PredicatePolarity::Positive
&& cx.is_lang_item(clause_def_id, TraitSolverLangItem::Sized)
&& cx.is_lang_item(goal_def_id, TraitSolverLangItem::MetaSized))
}
if let Some(trait_clause) = assumption.as_trait_clause()
&& trait_clause.polarity() == goal.predicate.polarity
&& trait_def_id_matches(ecx.cx(), trait_clause.def_id(), goal.predicate.def_id())
&& trait_def_id_matches(
ecx.cx(),
trait_clause.def_id(),
goal.predicate.def_id(),
goal.predicate.polarity,
)
&& DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
goal.predicate.trait_ref.args,
trait_clause.skip_binder().trait_ref.args,
@ -168,6 +175,8 @@ where
// PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so
// check for a `Sized` subtrait when looking for `MetaSized`. `PointeeSized` bounds
// are syntactic sugar for a lack of bounds so don't need this.
// We don't need to check polarity, `fast_reject_assumption` already rejected non-`Positive`
// polarity `Sized` assumptions as matching non-`Positive` `MetaSized` goals.
if ecx.cx().is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::MetaSized)
&& ecx.cx().is_lang_item(trait_clause.def_id(), TraitSolverLangItem::Sized)
{

View file

@ -748,9 +748,6 @@ parse_parentheses_with_struct_fields = invalid `struct` delimiters or `fn` call
.suggestion_braces_for_struct = if `{$type}` is a struct, use braces as delimiters
.suggestion_no_fields_for_fn = if `{$type}` is a function, use the arguments directly
parse_parenthesized_lifetime = parenthesized lifetime bounds are not supported
parse_parenthesized_lifetime_suggestion = remove the parentheses
parse_path_double_colon = path separator must be a double colon
.suggestion = use a double colon instead

View file

@ -3163,27 +3163,6 @@ pub(crate) struct ModifierLifetime {
pub modifier: &'static str,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(
parse_parenthesized_lifetime_suggestion,
applicability = "machine-applicable"
)]
pub(crate) struct RemoveParens {
#[suggestion_part(code = "")]
pub lo: Span,
#[suggestion_part(code = "")]
pub hi: Span,
}
#[derive(Diagnostic)]
#[diag(parse_parenthesized_lifetime)]
pub(crate) struct ParenthesizedLifetime {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub sugg: RemoveParens,
}
#[derive(Diagnostic)]
#[diag(parse_underscore_literal_suffix)]
pub(crate) struct UnderscoreLiteralSuffix {

View file

@ -49,6 +49,7 @@ pub(crate) fn lex_token_trees<'psess, 'src>(
mut src: &'src str,
mut start_pos: BytePos,
override_span: Option<Span>,
frontmatter_allowed: FrontmatterAllowed,
) -> Result<TokenStream, Vec<Diag<'psess>>> {
// Skip `#!`, if present.
if let Some(shebang_len) = rustc_lexer::strip_shebang(src) {
@ -56,7 +57,7 @@ pub(crate) fn lex_token_trees<'psess, 'src>(
start_pos = start_pos + BytePos::from_usize(shebang_len);
}
let cursor = Cursor::new(src, FrontmatterAllowed::Yes);
let cursor = Cursor::new(src, frontmatter_allowed);
let mut lexer = Lexer {
psess,
start_pos,

View file

@ -20,6 +20,7 @@ use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{AttrItem, Attribute, MetaItemInner, token};
use rustc_ast_pretty::pprust;
use rustc_errors::{Diag, EmissionGuarantee, FatalError, PResult, pluralize};
use rustc_lexer::FrontmatterAllowed;
use rustc_session::parse::ParseSess;
use rustc_span::source_map::SourceMap;
use rustc_span::{FileName, SourceFile, Span};
@ -146,7 +147,7 @@ fn new_parser_from_source_file(
source_file: Arc<SourceFile>,
) -> Result<Parser<'_>, Vec<Diag<'_>>> {
let end_pos = source_file.end_position();
let stream = source_file_to_stream(psess, source_file, None)?;
let stream = source_file_to_stream(psess, source_file, None, FrontmatterAllowed::Yes)?;
let mut parser = Parser::new(psess, stream, None);
if parser.token == token::Eof {
parser.token.span = Span::new(end_pos, end_pos, parser.token.span.ctxt(), None);
@ -161,7 +162,9 @@ pub fn source_str_to_stream(
override_span: Option<Span>,
) -> Result<TokenStream, Vec<Diag<'_>>> {
let source_file = psess.source_map().new_source_file(name, source);
source_file_to_stream(psess, source_file, override_span)
// used mainly for `proc_macro` and the likes, not for our parsing purposes, so don't parse
// frontmatters as frontmatters.
source_file_to_stream(psess, source_file, override_span, FrontmatterAllowed::No)
}
/// Given a source file, produces a sequence of token trees. Returns any buffered errors from
@ -170,6 +173,7 @@ fn source_file_to_stream<'psess>(
psess: &'psess ParseSess,
source_file: Arc<SourceFile>,
override_span: Option<Span>,
frontmatter_allowed: FrontmatterAllowed,
) -> Result<TokenStream, Vec<Diag<'psess>>> {
let src = source_file.src.as_ref().unwrap_or_else(|| {
psess.dcx().bug(format!(
@ -178,7 +182,13 @@ fn source_file_to_stream<'psess>(
));
});
lexer::lex_token_trees(psess, src.as_str(), source_file.start_pos, override_span)
lexer::lex_token_trees(
psess,
src.as_str(),
source_file.start_pos,
override_span,
frontmatter_allowed,
)
}
/// Runs the given subparser `f` on the tokens of the given `attr`'s item.

View file

@ -2397,12 +2397,12 @@ impl<'a> Parser<'a> {
let before = self.prev_token;
let binder = if self.check_keyword(exp!(For)) {
let lo = self.token.span;
let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?;
let (bound_vars, _) = self.parse_higher_ranked_binder()?;
let span = lo.to(self.prev_token.span);
self.psess.gated_spans.gate(sym::closure_lifetime_binder, span);
ClosureBinder::For { span, generic_params: lifetime_defs }
ClosureBinder::For { span, generic_params: bound_vars }
} else {
ClosureBinder::NotPresent
};

View file

@ -172,8 +172,11 @@ impl<'a> Parser<'a> {
})
}
/// Parses a (possibly empty) list of lifetime and type parameters, possibly including
/// a trailing comma and erroneous trailing attributes.
/// Parse a (possibly empty) list of generic (lifetime, type, const) parameters.
///
/// ```ebnf
/// GenericParams = (GenericParam ("," GenericParam)* ","?)?
/// ```
pub(super) fn parse_generic_params(&mut self) -> PResult<'a, ThinVec<ast::GenericParam>> {
let mut params = ThinVec::new();
let mut done = false;
@ -520,7 +523,7 @@ impl<'a> Parser<'a> {
// * `for<'a> Trait1<'a>: Trait2<'a /* ok */>`
// * `(for<'a> Trait1<'a>): Trait2<'a /* not ok */>`
// * `for<'a> for<'b> Trait1<'a, 'b>: Trait2<'a /* ok */, 'b /* not ok */>`
let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?;
let (bound_vars, _) = self.parse_higher_ranked_binder()?;
// Parse type with mandatory colon and (possibly empty) bounds,
// or with mandatory equality sign and the second type.
@ -528,7 +531,7 @@ impl<'a> Parser<'a> {
if self.eat(exp!(Colon)) {
let bounds = self.parse_generic_bounds()?;
Ok(ast::WherePredicateKind::BoundPredicate(ast::WhereBoundPredicate {
bound_generic_params: lifetime_defs,
bound_generic_params: bound_vars,
bounded_ty: ty,
bounds,
}))

View file

@ -308,11 +308,11 @@ impl<'a> Parser<'a> {
// Function pointer type or bound list (trait object type) starting with a poly-trait.
// `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T`
// `for<'lt> Trait1<'lt> + Trait2 + 'a`
let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?;
let (bound_vars, _) = self.parse_higher_ranked_binder()?;
if self.check_fn_front_matter(false, Case::Sensitive) {
self.parse_ty_fn_ptr(
lo,
lifetime_defs,
bound_vars,
Some(self.prev_token.span.shrink_to_lo()),
recover_return_sign,
)?
@ -326,7 +326,7 @@ impl<'a> Parser<'a> {
let path = self.parse_path(PathStyle::Type)?;
let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus();
let kind = self.parse_remaining_bounds_path(
lifetime_defs,
bound_vars,
path,
lo,
parse_plus,
@ -359,7 +359,7 @@ impl<'a> Parser<'a> {
let path = self.parse_path(PathStyle::Type)?;
let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus();
self.parse_remaining_bounds_path(
lifetime_defs,
bound_vars,
path,
lo,
parse_plus,
@ -443,7 +443,7 @@ impl<'a> Parser<'a> {
let ty = ts.into_iter().next().unwrap();
let maybe_bounds = allow_plus == AllowPlus::Yes && self.token.is_like_plus();
match ty.kind {
// `(TY_BOUND_NOPAREN) + BOUND + ...`.
// `"(" BareTraitBound ")" "+" Bound "+" ...`.
TyKind::Path(None, path) if maybe_bounds => self.parse_remaining_bounds_path(
ThinVec::new(),
path,
@ -853,10 +853,13 @@ impl<'a> Parser<'a> {
Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds))
}
fn parse_precise_capturing_args(
&mut self,
) -> PResult<'a, (ThinVec<PreciseCapturingArg>, Span)> {
let lo = self.token.span;
/// Parse a use-bound aka precise capturing list.
///
/// ```ebnf
/// UseBound = "use" "<" (PreciseCapture ("," PreciseCapture)* ","?)? ">"
/// PreciseCapture = "Self" | Ident | Lifetime
/// ```
fn parse_use_bound(&mut self, lo: Span, parens: ast::Parens) -> PResult<'a, GenericBound> {
self.expect_lt()?;
let (args, _, _) = self.parse_seq_to_before_tokens(
&[exp!(Gt)],
@ -882,7 +885,13 @@ impl<'a> Parser<'a> {
},
)?;
self.expect_gt()?;
Ok((args, lo.to(self.prev_token.span)))
if let ast::Parens::Yes = parens {
self.expect(exp!(CloseParen))?;
self.report_parenthesized_bound(lo, self.prev_token.span, "precise capturing lists");
}
Ok(GenericBound::Use(args, lo.to(self.prev_token.span)))
}
/// Is a `dyn B0 + ... + Bn` type allowed here?
@ -940,9 +949,10 @@ impl<'a> Parser<'a> {
self.parse_generic_bounds_common(AllowPlus::Yes)
}
/// Parses bounds of a type parameter `BOUND + BOUND + ...`, possibly with trailing `+`.
/// Parse generic bounds.
///
/// See `parse_generic_bound` for the `BOUND` grammar.
/// Only if `allow_plus` this parses a `+`-separated list of bounds (trailing `+` is admitted).
/// Otherwise, this only parses a single bound or none.
fn parse_generic_bounds_common(&mut self, allow_plus: AllowPlus) -> PResult<'a, GenericBounds> {
let mut bounds = Vec::new();
@ -988,48 +998,56 @@ impl<'a> Parser<'a> {
|| self.check_keyword(exp!(Use))
}
/// Parses a bound according to the grammar:
/// Parse a bound.
///
/// ```ebnf
/// BOUND = TY_BOUND | LT_BOUND
/// Bound = LifetimeBound | UseBound | TraitBound
/// ```
fn parse_generic_bound(&mut self) -> PResult<'a, GenericBound> {
let lo = self.token.span;
let leading_token = self.prev_token;
let lo = self.token.span;
// We only admit parenthesized *trait* bounds. However, we want to gracefully recover from
// other kinds of parenthesized bounds, so parse the opening parenthesis *here*.
//
// In the future we might want to lift this syntactic restriction and
// introduce "`GenericBound::Paren(Box<GenericBound>)`".
let parens = if self.eat(exp!(OpenParen)) { ast::Parens::Yes } else { ast::Parens::No };
let bound = if self.token.is_lifetime() {
self.parse_generic_lt_bound(lo, parens)?
if self.token.is_lifetime() {
self.parse_lifetime_bound(lo, parens)
} else if self.eat_keyword(exp!(Use)) {
// parse precise captures, if any. This is `use<'lt, 'lt, P, P>`; a list of
// lifetimes and ident params (including SelfUpper). These are validated later
// for order, duplication, and whether they actually reference params.
let use_span = self.prev_token.span;
let (args, args_span) = self.parse_precise_capturing_args()?;
GenericBound::Use(args, use_span.to(args_span))
self.parse_use_bound(lo, parens)
} else {
self.parse_generic_ty_bound(lo, parens, &leading_token)?
};
Ok(bound)
self.parse_trait_bound(lo, parens, &leading_token)
}
}
/// Parses a lifetime ("outlives") bound, e.g. `'a`, according to:
/// Parse a lifetime-bound aka outlives-bound.
///
/// ```ebnf
/// LT_BOUND = LIFETIME
/// LifetimeBound = Lifetime
/// ```
fn parse_generic_lt_bound(
&mut self,
lo: Span,
parens: ast::Parens,
) -> PResult<'a, GenericBound> {
fn parse_lifetime_bound(&mut self, lo: Span, parens: ast::Parens) -> PResult<'a, GenericBound> {
let lt = self.expect_lifetime();
let bound = GenericBound::Outlives(lt);
if let ast::Parens::Yes = parens {
// FIXME(Centril): Consider not erroring here and accepting `('lt)` instead,
// possibly introducing `GenericBound::Paren(Box<GenericBound>)`?
self.recover_paren_lifetime(lo)?;
self.expect(exp!(CloseParen))?;
self.report_parenthesized_bound(lo, self.prev_token.span, "lifetime bounds");
}
Ok(bound)
Ok(GenericBound::Outlives(lt))
}
fn report_parenthesized_bound(&self, lo: Span, hi: Span, kind: &str) -> ErrorGuaranteed {
let mut diag =
self.dcx().struct_span_err(lo.to(hi), format!("{kind} may not be parenthesized"));
diag.multipart_suggestion(
"remove the parentheses",
vec![(lo, String::new()), (hi, String::new())],
Applicability::MachineApplicable,
);
diag.emit()
}
/// Emits an error if any trait bound modifiers were present.
@ -1074,27 +1092,17 @@ impl<'a> Parser<'a> {
unreachable!("lifetime bound intercepted in `parse_generic_ty_bound` but no modifiers?")
}
/// Recover on `('lifetime)` with `(` already eaten.
fn recover_paren_lifetime(&mut self, lo: Span) -> PResult<'a, ()> {
self.expect(exp!(CloseParen))?;
let span = lo.to(self.prev_token.span);
let sugg = errors::RemoveParens { lo, hi: self.prev_token.span };
self.dcx().emit_err(errors::ParenthesizedLifetime { span, sugg });
Ok(())
}
/// Parses the modifiers that may precede a trait in a bound, e.g. `?Trait` or `[const] Trait`.
///
/// If no modifiers are present, this does not consume any tokens.
///
/// ```ebnf
/// CONSTNESS = [["["] "const" ["]"]]
/// ASYNCNESS = ["async"]
/// POLARITY = ["?" | "!"]
/// Constness = ("const" | "[" "const" "]")?
/// Asyncness = "async"?
/// Polarity = ("?" | "!")?
/// ```
///
/// See `parse_generic_ty_bound` for the complete grammar of trait bound modifiers.
/// See `parse_trait_bound` for more context.
fn parse_trait_bound_modifiers(&mut self) -> PResult<'a, TraitBoundModifiers> {
let modifier_lo = self.token.span;
let constness = self.parse_bound_constness()?;
@ -1187,20 +1195,21 @@ impl<'a> Parser<'a> {
})
}
/// Parses a type bound according to:
/// ```ebnf
/// TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN)
/// TY_BOUND_NOPAREN = [for<GENERIC_PARAMS> CONSTNESS ASYNCNESS | POLARITY] SIMPLE_PATH
/// ```
/// Parse a trait bound.
///
/// For example, this grammar accepts `for<'a: 'b> [const] ?m::Trait<'a>`.
fn parse_generic_ty_bound(
/// ```ebnf
/// TraitBound = BareTraitBound | "(" BareTraitBound ")"
/// BareTraitBound =
/// (HigherRankedBinder Constness Asyncness | Polarity)
/// TypePath
/// ```
fn parse_trait_bound(
&mut self,
lo: Span,
parens: ast::Parens,
leading_token: &Token,
) -> PResult<'a, GenericBound> {
let (mut lifetime_defs, binder_span) = self.parse_late_bound_lifetime_defs()?;
let (mut bound_vars, binder_span) = self.parse_higher_ranked_binder()?;
let modifiers_lo = self.token.span;
let modifiers = self.parse_trait_bound_modifiers()?;
@ -1223,11 +1232,11 @@ impl<'a> Parser<'a> {
// e.g. `T: for<'a> 'a` or `T: [const] 'a`.
if self.token.is_lifetime() {
let _: ErrorGuaranteed = self.error_lt_bound_with_modifiers(modifiers, binder_span);
return self.parse_generic_lt_bound(lo, parens);
return self.parse_lifetime_bound(lo, parens);
}
if let (more_lifetime_defs, Some(binder_span)) = self.parse_late_bound_lifetime_defs()? {
lifetime_defs.extend(more_lifetime_defs);
if let (more_bound_vars, Some(binder_span)) = self.parse_higher_ranked_binder()? {
bound_vars.extend(more_bound_vars);
self.dcx().emit_err(errors::BinderBeforeModifiers { binder_span, modifiers_span });
}
@ -1287,7 +1296,7 @@ impl<'a> Parser<'a> {
};
if self.may_recover() && self.token == TokenKind::OpenParen {
self.recover_fn_trait_with_lifetime_params(&mut path, &mut lifetime_defs)?;
self.recover_fn_trait_with_lifetime_params(&mut path, &mut bound_vars)?;
}
if let ast::Parens::Yes = parens {
@ -1310,7 +1319,7 @@ impl<'a> Parser<'a> {
}
let poly_trait =
PolyTraitRef::new(lifetime_defs, path, modifiers, lo.to(self.prev_token.span), parens);
PolyTraitRef::new(bound_vars, path, modifiers, lo.to(self.prev_token.span), parens);
Ok(GenericBound::Trait(poly_trait))
}
@ -1349,8 +1358,12 @@ impl<'a> Parser<'a> {
}
}
/// Optionally parses `for<$generic_params>`.
pub(super) fn parse_late_bound_lifetime_defs(
/// Parse an optional higher-ranked binder.
///
/// ```ebnf
/// HigherRankedBinder = ("for" "<" GenericParams ">")?
/// ```
pub(super) fn parse_higher_ranked_binder(
&mut self,
) -> PResult<'a, (ThinVec<GenericParam>, Option<Span>)> {
if self.eat_keyword(exp!(For)) {

View file

@ -74,6 +74,16 @@ passes_const_stable_not_stable =
attribute `#[rustc_const_stable]` can only be applied to functions that are declared `#[stable]`
.label = attribute specified here
passes_custom_mir_incompatible_dialect_and_phase =
The {$dialect} dialect is not compatible with the {$phase} phase
.dialect_span = this dialect...
.phase_span = ... is not compatible with this phase
passes_custom_mir_phase_requires_dialect =
`dialect` key required
.phase_span = `phase` argument requires a `dialect` argument
passes_dead_codes =
{ $multiple ->
*[true] multiple {$descr}s are
@ -422,10 +432,6 @@ passes_must_not_suspend =
`must_not_suspend` attribute should be applied to a struct, enum, union, or trait
.label = is not a struct, enum, union, or trait
passes_must_use_no_effect =
`#[must_use]` has no effect when applied to {$target}
.suggestion = remove the attribute
passes_no_link =
attribute should be applied to an `extern crate` item
.label = not an `extern crate` item
@ -444,10 +450,6 @@ passes_no_main_function =
.teach_note = If you don't know the basics of Rust, you can go look to the Rust Book to get started: https://doc.rust-lang.org/book/
.non_function_main = non-function item at `crate::main` is found
passes_no_sanitize =
`#[no_sanitize({$attr_str})]` should be applied to {$accepted_kind}
.label = not {$accepted_kind}
passes_non_exhaustive_with_default_field_values =
`#[non_exhaustive]` can't be used to annotate items with default field values
.label = this struct has default field values
@ -542,6 +544,12 @@ passes_rustc_pub_transparent =
attribute should be applied to `#[repr(transparent)]` types
.label = not a `#[repr(transparent)]` type
passes_sanitize_attribute_not_allowed =
sanitize attribute not allowed here
.not_fn_impl_mod = not a function, impl block, or module
.no_body = function has no body
.help = sanitize attribute can be applied to a function (with body), impl block, or module
passes_should_be_applied_to_fn =
attribute should be applied to a function definition
.label = {$on_crate ->

View file

@ -18,7 +18,7 @@ use rustc_feature::{
ACCEPTED_LANG_FEATURES, AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP,
BuiltinAttribute,
};
use rustc_hir::attrs::{AttributeKind, InlineAttr, ReprAttr};
use rustc_hir::attrs::{AttributeKind, InlineAttr, MirDialect, MirPhase, ReprAttr};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalModDefId;
use rustc_hir::intravisit::{self, Visitor};
@ -194,8 +194,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
Attribute::Parsed(AttributeKind::MayDangle(attr_span)) => {
self.check_may_dangle(hir_id, *attr_span)
}
Attribute::Parsed(AttributeKind::MustUse { span, .. }) => {
self.check_must_use(hir_id, *span, target)
&Attribute::Parsed(AttributeKind::CustomMir(dialect, phase, attr_span)) => {
self.check_custom_mir(dialect, phase, attr_span)
}
Attribute::Parsed(
AttributeKind::BodyStability { .. }
@ -246,9 +246,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::Coverage (..)
| AttributeKind::ShouldPanic { .. }
| AttributeKind::Coroutine(..)
| AttributeKind::Linkage(..),
| AttributeKind::Linkage(..)
| AttributeKind::MustUse { .. },
) => { /* do nothing */ }
Attribute::Unparsed(attr_item) => {
style = Some(attr_item.style);
match attr.path().as_slice() {
@ -258,8 +258,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
[sym::diagnostic, sym::on_unimplemented, ..] => {
self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target)
}
[sym::no_sanitize, ..] => {
self.check_no_sanitize(attr, span, target)
[sym::sanitize, ..] => {
self.check_sanitize(attr, span, target)
}
[sym::thread_local, ..] => self.check_thread_local(attr, span, target),
[sym::doc, ..] => self.check_doc_attrs(
@ -331,8 +331,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| sym::panic_handler
| sym::lang
| sym::needs_allocator
| sym::default_lib_allocator
| sym::custom_mir,
| sym::default_lib_allocator,
..
] => {}
[name, rest@..] => {
@ -482,39 +481,43 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}
fn check_no_sanitize(&self, attr: &Attribute, span: Span, target: Target) {
/// Checks that the `#[sanitize(..)]` attribute is applied to a
/// function/closure/method, or to an impl block or module.
fn check_sanitize(&self, attr: &Attribute, target_span: Span, target: Target) {
let mut not_fn_impl_mod = None;
let mut no_body = None;
if let Some(list) = attr.meta_item_list() {
for item in list.iter() {
let sym = item.name();
match sym {
Some(s @ sym::address | s @ sym::hwaddress) => {
let is_valid =
matches!(target, Target::Fn | Target::Method(..) | Target::Static);
if !is_valid {
self.dcx().emit_err(errors::NoSanitize {
attr_span: item.span(),
defn_span: span,
accepted_kind: "a function or static",
attr_str: s.as_str(),
});
}
let MetaItemInner::MetaItem(set) = item else {
return;
};
let segments = set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
match target {
Target::Fn
| Target::Closure
| Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent)
| Target::Impl { .. }
| Target::Mod => return,
Target::Static if matches!(segments.as_slice(), [sym::address]) => return,
// These are "functions", but they aren't allowed because they don't
// have a body, so the usual explanation would be confusing.
Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
no_body = Some(target_span);
}
_ => {
let is_valid = matches!(target, Target::Fn | Target::Method(..));
if !is_valid {
self.dcx().emit_err(errors::NoSanitize {
attr_span: item.span(),
defn_span: span,
accepted_kind: "a function",
attr_str: &match sym {
Some(name) => name.to_string(),
None => "...".to_string(),
},
});
}
not_fn_impl_mod = Some(target_span);
}
}
}
self.dcx().emit_err(errors::SanitizeAttributeNotAllowed {
attr_span: attr.span(),
not_fn_impl_mod,
no_body,
help: (),
});
}
}
@ -561,7 +564,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}
}
/// Checks if `#[collapse_debuginfo]` is applied to a macro.
fn check_collapse_debuginfo(&self, attr: &Attribute, span: Span, target: Target) {
match target {
@ -1259,41 +1261,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}
/// Warns against some misuses of `#[must_use]`
fn check_must_use(&self, hir_id: HirId, attr_span: Span, target: Target) {
if matches!(
target,
Target::Fn
| Target::Enum
| Target::Struct
| Target::Union
| Target::Method(MethodKind::Trait { body: false } | MethodKind::Inherent)
| Target::ForeignFn
// `impl Trait` in return position can trip
// `unused_must_use` if `Trait` is marked as
// `#[must_use]`
| Target::Trait
) {
return;
}
// `#[must_use]` can be applied to a trait method definition with a default body
if let Target::Method(MethodKind::Trait { body: true }) = target
&& let parent_def_id = self.tcx.hir_get_parent_item(hir_id).def_id
&& let containing_item = self.tcx.hir_expect_item(parent_def_id)
&& let hir::ItemKind::Trait(..) = containing_item.kind
{
return;
}
self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
attr_span,
errors::MustUseNoEffect { target: target.plural_name(), attr_span },
);
}
/// Checks if `#[must_not_suspend]` is applied to a struct, enum, union, or trait.
fn check_must_not_suspend(&self, attr: &Attribute, span: Span, target: Target) {
match target {
@ -2113,6 +2080,48 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
self.dcx().emit_err(errors::ConstContinueAttr { attr_span, node_span });
};
}
fn check_custom_mir(
&self,
dialect: Option<(MirDialect, Span)>,
phase: Option<(MirPhase, Span)>,
attr_span: Span,
) {
let Some((dialect, dialect_span)) = dialect else {
if let Some((_, phase_span)) = phase {
self.dcx()
.emit_err(errors::CustomMirPhaseRequiresDialect { attr_span, phase_span });
}
return;
};
match dialect {
MirDialect::Analysis => {
if let Some((MirPhase::Optimized, phase_span)) = phase {
self.dcx().emit_err(errors::CustomMirIncompatibleDialectAndPhase {
dialect,
phase: MirPhase::Optimized,
attr_span,
dialect_span,
phase_span,
});
}
}
MirDialect::Built => {
if let Some((phase, phase_span)) = phase {
self.dcx().emit_err(errors::CustomMirIncompatibleDialectAndPhase {
dialect,
phase,
attr_span,
dialect_span,
phase_span,
});
}
}
MirDialect::Runtime => {}
}
}
}
impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {

View file

@ -7,6 +7,7 @@ use rustc_errors::{
MultiSpan, Subdiagnostic,
};
use rustc_hir::Target;
use rustc_hir::attrs::{MirDialect, MirPhase};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::{MainDefinition, Ty};
use rustc_span::{DUMMY_SP, Span, Symbol};
@ -357,14 +358,6 @@ pub(crate) struct BothFfiConstAndPure {
pub attr_span: Span,
}
#[derive(LintDiagnostic)]
#[diag(passes_must_use_no_effect)]
pub(crate) struct MustUseNoEffect {
pub target: &'static str,
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
pub attr_span: Span,
}
#[derive(Diagnostic)]
#[diag(passes_must_not_suspend)]
pub(crate) struct MustNotSuspend {
@ -1488,15 +1481,21 @@ pub(crate) struct AttrCrateLevelOnlySugg {
pub attr: Span,
}
/// "sanitize attribute not allowed here"
#[derive(Diagnostic)]
#[diag(passes_no_sanitize)]
pub(crate) struct NoSanitize<'a> {
#[diag(passes_sanitize_attribute_not_allowed)]
pub(crate) struct SanitizeAttributeNotAllowed {
#[primary_span]
pub attr_span: Span,
#[label]
pub defn_span: Span,
pub accepted_kind: &'a str,
pub attr_str: &'a str,
/// "not a function, impl block, or module"
#[label(passes_not_fn_impl_mod)]
pub not_fn_impl_mod: Option<Span>,
/// "function has no body"
#[label(passes_no_body)]
pub no_body: Option<Span>,
/// "sanitize attribute can be applied to a function (with body), impl block, or module"
#[help]
pub help: (),
}
// FIXME(jdonszelmann): move back to rustc_attr
@ -1570,3 +1569,25 @@ pub(crate) struct ReprAlignShouldBeAlign {
pub span: Span,
pub item: &'static str,
}
#[derive(Diagnostic)]
#[diag(passes_custom_mir_phase_requires_dialect)]
pub(crate) struct CustomMirPhaseRequiresDialect {
#[primary_span]
pub attr_span: Span,
#[label]
pub phase_span: Span,
}
#[derive(Diagnostic)]
#[diag(passes_custom_mir_incompatible_dialect_and_phase)]
pub(crate) struct CustomMirIncompatibleDialectAndPhase {
pub dialect: MirDialect,
pub phase: MirPhase,
#[primary_span]
pub attr_span: Span,
#[label]
pub dialect_span: Span,
#[label]
pub phase_span: Span,
}

View file

@ -93,6 +93,9 @@ resolve_consider_adding_a_derive =
resolve_consider_adding_macro_export =
consider adding a `#[macro_export]` to the macro in the imported module
resolve_consider_marking_as_pub_crate =
in case you want to use the macro within this crate only, reduce the visibility to `pub(crate)`
resolve_consider_declaring_with_pub =
consider declaring type or module `{$ident}` with `pub`
@ -249,7 +252,7 @@ resolve_macro_cannot_use_as_attr =
`{$ident}` exists, but has no `attr` rules
resolve_macro_cannot_use_as_derive =
`{$ident}` exists, but a declarative macro cannot be used as a derive macro
`{$ident}` exists, but has no `derive` rules
resolve_macro_defined_later =
a macro with the same name exists, but it appears later

View file

@ -210,6 +210,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
}
/// Add every proc macro accessible from the current crate to the `macro_map` so diagnostics can
/// find them for suggestions.
pub(crate) fn register_macros_for_all_crates(&mut self) {
if !self.all_crate_macros_already_registered {
for def_id in self.cstore().all_proc_macro_def_ids() {
self.get_macro_by_def_id(def_id);
}
self.all_crate_macros_already_registered = true;
}
}
pub(crate) fn build_reduced_graph(
&mut self,
fragment: &AstFragment,
@ -474,6 +485,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
root_span,
root_id,
vis,
vis_span: item.vis.span,
});
self.r.indeterminate_imports.push(import);
@ -966,6 +978,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
span: item.span,
module_path: Vec::new(),
vis,
vis_span: item.vis.span,
});
if used {
self.r.import_use_map.insert(import, Used::Other);
@ -1100,6 +1113,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
span,
module_path: Vec::new(),
vis: Visibility::Restricted(CRATE_DEF_ID),
vis_span: item.vis.span,
})
};
@ -1270,6 +1284,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
span,
module_path: Vec::new(),
vis,
vis_span: item.vis.span,
});
self.r.import_use_map.insert(import, Used::Other);
let import_binding = self.r.import(binding, import);

View file

@ -1469,33 +1469,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
krate: &Crate,
sugg_span: Option<Span>,
) {
// Bring imported but unused `derive` macros into `macro_map` so we ensure they can be used
// for suggestions.
self.cm().visit_scopes(
ScopeSet::Macro(MacroKind::Derive),
&parent_scope,
ident.span.ctxt(),
|this, scope, _use_prelude, _ctxt| {
let Scope::Module(m, _) = scope else {
return None;
};
for (_, resolution) in this.resolutions(m).borrow().iter() {
let Some(binding) = resolution.borrow().best_binding() else {
continue;
};
let Res::Def(DefKind::Macro(kinds), def_id) = binding.res() else {
continue;
};
if !kinds.intersects(MacroKinds::ATTR | MacroKinds::DERIVE) {
continue;
}
// By doing this all *imported* macros get added to the `macro_map` even if they
// are *unused*, which makes the later suggestions find them and work.
let _ = this.get_macro_by_def_id(def_id);
}
None::<()>
},
);
// Bring all unused `derive` macros into `macro_map` so we ensure they can be used for
// suggestions.
self.register_macros_for_all_crates();
let is_expected =
&|res: Res| res.macro_kinds().is_some_and(|k| k.contains(macro_kind.into()));

View file

@ -775,6 +775,17 @@ pub(crate) struct ConsiderAddingMacroExport {
pub(crate) span: Span,
}
#[derive(Subdiagnostic)]
#[suggestion(
resolve_consider_marking_as_pub_crate,
code = "pub(crate)",
applicability = "maybe-incorrect"
)]
pub(crate) struct ConsiderMarkingAsPubCrate {
#[primary_span]
pub(crate) vis_span: Span,
}
#[derive(Subdiagnostic)]
#[note(resolve_consider_marking_as_pub)]
pub(crate) struct ConsiderMarkingAsPub {

View file

@ -30,7 +30,7 @@ use crate::diagnostics::{DiagMode, Suggestion, import_candidates};
use crate::errors::{
CannotBeReexportedCratePublic, CannotBeReexportedCratePublicNS, CannotBeReexportedPrivate,
CannotBeReexportedPrivateNS, CannotDetermineImportResolution, CannotGlobImportAllCrates,
ConsiderAddingMacroExport, ConsiderMarkingAsPub,
ConsiderAddingMacroExport, ConsiderMarkingAsPub, ConsiderMarkingAsPubCrate,
};
use crate::{
AmbiguityError, AmbiguityKind, BindingKey, CmResolver, Determinacy, Finalize, ImportSuggestion,
@ -184,6 +184,9 @@ pub(crate) struct ImportData<'ra> {
/// |`use foo` | `ModuleOrUniformRoot::CurrentScope` | - |
pub imported_module: Cell<Option<ModuleOrUniformRoot<'ra>>>,
pub vis: Visibility,
/// Span of the visibility.
pub vis_span: Span,
}
/// All imports are unique and allocated on a same arena,
@ -866,7 +869,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
ImportKind::Glob { .. } => {
// FIXME: Use mutable resolver directly as a hack, this should be an output of
// specualtive resolution.
// speculative resolution.
self.get_mut_unchecked().resolve_glob_import(import);
return 0;
}
@ -903,7 +906,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// We need the `target`, `source` can be extracted.
let imported_binding = this.import(binding, import);
// FIXME: Use mutable resolver directly as a hack, this should be an output of
// specualtive resolution.
// speculative resolution.
this.get_mut_unchecked().define_binding_local(
parent,
target,
@ -917,7 +920,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
if target.name != kw::Underscore {
let key = BindingKey::new(target, ns);
// FIXME: Use mutable resolver directly as a hack, this should be an output of
// specualtive resolution.
// speculative resolution.
this.get_mut_unchecked().update_local_resolution(
parent,
key,
@ -1368,6 +1371,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
err.subdiagnostic( ConsiderAddingMacroExport {
span: binding.span,
});
err.subdiagnostic( ConsiderMarkingAsPubCrate {
vis_span: import.vis_span,
});
}
_ => {
err.subdiagnostic( ConsiderMarkingAsPub {

View file

@ -4270,7 +4270,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
if path.len() == 2
&& let [segment] = prefix_path
{
// Delay to check whether methond name is an associated function or not
// Delay to check whether method name is an associated function or not
// ```
// let foo = Foo {};
// foo::bar(); // possibly suggest to foo.bar();

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