Merge from rustc

This commit is contained in:
The Miri Cronjob Bot 2025-06-19 05:03:01 +00:00
commit e9433426a1
295 changed files with 15161 additions and 12950 deletions

View file

@ -3474,11 +3474,9 @@ name = "rustc_codegen_ssa"
version = "0.0.0"
dependencies = [
"ar_archive_writer",
"arrayvec",
"bitflags",
"bstr",
"cc",
"either",
"itertools",
"libc",
"object 0.37.0",

View file

@ -22,7 +22,7 @@ pub fn alloc_error_handler_name(alloc_error_handler_kind: AllocatorKind) -> &'st
}
}
pub const NO_ALLOC_SHIM_IS_UNSTABLE: &str = "__rust_no_alloc_shim_is_unstable";
pub const NO_ALLOC_SHIM_IS_UNSTABLE: &str = "__rust_no_alloc_shim_is_unstable_v2";
pub enum AllocatorTy {
Layout,

View file

@ -172,9 +172,6 @@ ast_lowering_template_modifier = template modifier
ast_lowering_this_not_async = this is not `async`
ast_lowering_underscore_array_length_unstable =
using `_` for array lengths is unstable
ast_lowering_underscore_expr_lhs_assign =
in expressions, `_` can only be used on the left-hand side of an assignment
.label = `_` not allowed here

View file

@ -48,7 +48,7 @@ use rustc_data_structures::sorted_map::SortedMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::sync::spawn;
use rustc_data_structures::tagged_ptr::TaggedRef;
use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey};
use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle};
use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId};
use rustc_hir::lints::DelayedLint;
@ -60,7 +60,7 @@ use rustc_index::{Idx, IndexSlice, IndexVec};
use rustc_macros::extension;
use rustc_middle::span_bug;
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
use rustc_session::parse::{add_feature_diagnostics, feature_err};
use rustc_session::parse::add_feature_diagnostics;
use rustc_span::symbol::{Ident, Symbol, kw, sym};
use rustc_span::{DUMMY_SP, DesugaringKind, Span};
use smallvec::SmallVec;
@ -2109,15 +2109,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// `ExprKind::Paren(ExprKind::Underscore)` and should also be lowered to `GenericArg::Infer`
match c.value.peel_parens().kind {
ExprKind::Underscore => {
if !self.tcx.features().generic_arg_infer() {
feature_err(
&self.tcx.sess,
sym::generic_arg_infer,
c.value.span,
fluent_generated::ast_lowering_underscore_array_length_unstable,
)
.stash(c.value.span, StashKey::UnderscoreForArrayLengths);
}
let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span), ());
self.arena.alloc(hir::ConstArg { hir_id: self.lower_node_id(c.id), kind: ct_kind })
}

View file

@ -8,7 +8,7 @@ use thin_vec::ThinVec;
use crate::{DefaultBodyStability, PartialConstStability, PrintAttribute, RustcVersion, Stability};
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, PrintAttribute)]
pub enum InlineAttr {
None,
Hint,
@ -221,6 +221,9 @@ pub enum AttributeKind {
/// Represents [`#[doc]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html).
DocComment { style: AttrStyle, kind: CommentKind, span: Span, comment: Symbol },
/// Represents `#[inline]` and `#[rustc_force_inline]`.
Inline(InlineAttr, Span),
/// Represents `#[rustc_macro_transparency]`.
MacroTransparency(Transparency),

View file

@ -11,4 +11,5 @@ pub struct AttributeLint<Id> {
#[derive(Clone, Debug, HashStable_Generic)]
pub enum AttributeLintKind {
UnusedDuplicate { this: Span, other: Span, warning: bool },
IllFormedAttributeInput { suggestions: Vec<String> },
}

View file

@ -23,8 +23,10 @@ attr_parsing_expects_feature_list =
attr_parsing_expects_features =
`{$name}` expects feature names
attr_parsing_incorrect_meta_item = expected a quoted string literal
attr_parsing_incorrect_meta_item_suggestion = consider surrounding this with quotes
attr_parsing_ill_formed_attribute_input = {$num_suggestions ->
[1] attribute must be of the form {$suggestions}
*[other] valid forms for the attribute are {$suggestions}
}
attr_parsing_incorrect_repr_format_align_one_arg =
incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
@ -81,9 +83,6 @@ attr_parsing_missing_note =
attr_parsing_missing_since =
missing 'since'
attr_parsing_multiple_item =
multiple '{$item}' items
attr_parsing_multiple_stability_levels =
multiple stability levels
@ -122,10 +121,6 @@ attr_parsing_unsupported_literal_cfg_boolean =
literal in `cfg` predicate value must be a boolean
attr_parsing_unsupported_literal_cfg_string =
literal in `cfg` predicate value must be a string
attr_parsing_unsupported_literal_deprecated_kv_pair =
item in `deprecated` must be a key/value pair
attr_parsing_unsupported_literal_deprecated_string =
literal in `deprecated` value must be a string
attr_parsing_unsupported_literal_generic =
unsupported literal
attr_parsing_unsupported_literal_suggestion =
@ -136,6 +131,7 @@ attr_parsing_unused_duplicate =
.suggestion = remove this attribute
.note = attribute also specified here
.warn = {-passes_previously_accepted}
attr_parsing_unused_multiple =
multiple `{$name}` attributes
.suggestion = remove this attribute

View file

@ -1,6 +1,7 @@
use std::iter;
use rustc_attr_data_structures::AttributeKind;
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Span, Symbol, sym};
use super::{CombineAttributeParser, ConvertFn};
@ -13,6 +14,7 @@ impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
const PATH: &[Symbol] = &[sym::allow_internal_unstable];
type Item = (Symbol, Span);
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowInternalUnstable;
const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
@ -29,6 +31,7 @@ impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser {
const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable];
type Item = Symbol;
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowConstFnUnstable;
const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,

View file

@ -1,4 +1,5 @@
use rustc_attr_data_structures::AttributeKind;
use rustc_feature::template;
use rustc_span::{Span, Symbol, sym};
use thin_vec::ThinVec;
@ -13,37 +14,33 @@ pub(crate) struct ConfusablesParser {
}
impl<S: Stage> AttributeParser<S> for ConfusablesParser {
const ATTRIBUTES: AcceptMapping<Self, S> = &[(&[sym::rustc_confusables], |this, cx, args| {
let Some(list) = args.list() else {
// FIXME(jdonszelmann): error when not a list? Bring validation code here.
// NOTE: currently subsequent attributes are silently ignored using
// tcx.get_attr().
return;
};
if list.is_empty() {
cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span });
}
for param in list.mixed() {
let span = param.span();
let Some(lit) = param.lit() else {
cx.emit_err(session_diagnostics::IncorrectMetaItem {
span,
suggestion: Some(session_diagnostics::IncorrectMetaItemSuggestion {
lo: span.shrink_to_lo(),
hi: span.shrink_to_hi(),
}),
});
continue;
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
&[sym::rustc_confusables],
template!(List: r#""name1", "name2", ..."#),
|this, cx, args| {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
return;
};
this.confusables.push(lit.symbol);
}
if list.is_empty() {
cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span });
}
this.first_span.get_or_insert(cx.attr_span);
})];
for param in list.mixed() {
let span = param.span();
let Some(lit) = param.lit().and_then(|i| i.value_str()) else {
cx.expected_string_literal(span, param.lit());
continue;
};
this.confusables.push(lit);
}
this.first_span.get_or_insert(cx.attr_span);
},
)];
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
if self.confusables.is_empty() {

View file

@ -1,4 +1,5 @@
use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation};
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Span, Symbol, sym};
use super::util::parse_version;
@ -6,7 +7,6 @@ use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
use crate::context::{AcceptContext, Stage};
use crate::parser::ArgParser;
use crate::session_diagnostics;
use crate::session_diagnostics::UnsupportedLiteralReason;
pub(crate) struct DeprecationParser;
@ -18,25 +18,18 @@ fn get<S: Stage>(
item: &Option<Symbol>,
) -> Option<Symbol> {
if item.is_some() {
cx.emit_err(session_diagnostics::MultipleItem { span: param_span, item: name.to_string() });
cx.duplicate_key(param_span, name);
return None;
}
if let Some(v) = arg.name_value() {
if let Some(value_str) = v.value_as_str() {
Some(value_str)
} else {
let lit = v.value_as_lit();
cx.emit_err(session_diagnostics::UnsupportedLiteral {
span: v.value_span,
reason: UnsupportedLiteralReason::DeprecatedString,
is_bytestr: lit.kind.is_bytestr(),
start_point_span: cx.sess().source_map().start_point(lit.span),
});
cx.expected_string_literal(v.value_span, Some(&v.value_as_lit()));
None
}
} else {
// FIXME(jdonszelmann): suggestion?
cx.emit_err(session_diagnostics::IncorrectMetaItem { span: param_span, suggestion: None });
cx.expected_name_value(param_span, Some(name));
None
}
}
@ -45,6 +38,11 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
const PATH: &[Symbol] = &[sym::deprecated];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const TEMPLATE: AttributeTemplate = template!(
Word,
List: r#"/*opt*/ since = "version", /*opt*/ note = "reason""#,
NameValueStr: "reason"
);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let features = cx.features();
@ -55,57 +53,60 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
let is_rustc = features.staged_api();
if let Some(value) = args.name_value()
&& let Some(value_str) = value.value_as_str()
{
note = Some(value_str)
} else if let Some(list) = args.list() {
for param in list.mixed() {
let param_span = param.span();
let Some(param) = param.meta_item() else {
cx.emit_err(session_diagnostics::UnsupportedLiteral {
span: param_span,
reason: UnsupportedLiteralReason::DeprecatedKvPair,
is_bytestr: false,
start_point_span: cx.sess().source_map().start_point(param_span),
});
return None;
};
let ident_name = param.path().word_sym();
match ident_name {
Some(name @ sym::since) => {
since = Some(get(cx, name, param_span, param.args(), &since)?);
}
Some(name @ sym::note) => {
note = Some(get(cx, name, param_span, param.args(), &note)?);
}
Some(name @ sym::suggestion) => {
if !features.deprecated_suggestion() {
cx.emit_err(session_diagnostics::DeprecatedItemSuggestion {
span: param_span,
is_nightly: cx.sess().is_nightly_build(),
details: (),
});
}
suggestion = Some(get(cx, name, param_span, param.args(), &suggestion)?);
}
_ => {
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param_span,
item: param.path().to_string(),
expected: if features.deprecated_suggestion() {
&["since", "note", "suggestion"]
} else {
&["since", "note"]
},
});
match args {
ArgParser::NoArgs => {
// ok
}
ArgParser::List(list) => {
for param in list.mixed() {
let Some(param) = param.meta_item() else {
cx.unexpected_literal(param.span());
return None;
};
let ident_name = param.path().word_sym();
match ident_name {
Some(name @ sym::since) => {
since = Some(get(cx, name, param.span(), param.args(), &since)?);
}
Some(name @ sym::note) => {
note = Some(get(cx, name, param.span(), param.args(), &note)?);
}
Some(name @ sym::suggestion) => {
if !features.deprecated_suggestion() {
cx.emit_err(session_diagnostics::DeprecatedItemSuggestion {
span: param.span(),
is_nightly: cx.sess().is_nightly_build(),
details: (),
});
}
suggestion =
Some(get(cx, name, param.span(), param.args(), &suggestion)?);
}
_ => {
cx.unknown_key(
param.span(),
param.path().to_string(),
if features.deprecated_suggestion() {
&["since", "note", "suggestion"]
} else {
&["since", "note"]
},
);
return None;
}
}
}
}
ArgParser::NameValue(v) => {
let Some(value) = v.value_as_str() else {
cx.expected_string_literal(v.value_span, Some(v.value_as_lit()));
return None;
};
note = Some(value);
}
}
let since = if let Some(since) = since {

View file

@ -29,7 +29,7 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
return None;
};
match l.meta_item().and_then(|i| i.word_without_args().map(|i| i.name)) {
match l.meta_item().and_then(|i| i.path().word_sym()) {
Some(sym::always) => {
Some(AttributeKind::Inline(InlineAttr::Always, cx.attr_span))
}
@ -63,7 +63,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
const TEMPLATE: AttributeTemplate = template!(Word, List: "reason", NameValueStr: "reason");
fn convert(cx: &AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let reason = match args {
ArgParser::NoArgs => None,
ArgParser::List(list) => {
@ -73,7 +73,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
};
let Some(reason) = l.lit().and_then(|i| i.kind.str()) else {
cx.expected_string_literal(l.span());
cx.expected_string_literal(l.span(), l.lit());
return None;
};
@ -81,7 +81,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
}
ArgParser::NameValue(v) => {
let Some(reason) = v.value_as_str() else {
cx.expected_string_literal(v.value_span);
cx.expected_string_literal(v.value_span, Some(v.value_as_lit()));
return None;
};

View file

@ -1,4 +1,5 @@
use rustc_attr_data_structures::AttributeKind;
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Symbol, sym};
use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
@ -9,10 +10,9 @@ pub(crate) struct AsPtrParser;
impl<S: Stage> SingleAttributeParser<S> for AsPtrParser {
const PATH: &[Symbol] = &[sym::rustc_as_ptr];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const TEMPLATE: AttributeTemplate = template!(Word);
fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
// FIXME: check that there's no args (this is currently checked elsewhere)

View file

@ -18,6 +18,7 @@ use std::marker::PhantomData;
use rustc_attr_data_structures::AttributeKind;
use rustc_attr_data_structures::lints::AttributeLintKind;
use rustc_feature::AttributeTemplate;
use rustc_span::{Span, Symbol};
use thin_vec::ThinVec;
@ -29,6 +30,7 @@ pub(crate) mod allow_unstable;
pub(crate) mod cfg;
pub(crate) mod confusables;
pub(crate) mod deprecation;
pub(crate) mod inline;
pub(crate) mod lint_helpers;
pub(crate) mod repr;
pub(crate) mod stability;
@ -36,7 +38,7 @@ pub(crate) mod transparency;
pub(crate) mod util;
type AcceptFn<T, S> = for<'sess> fn(&mut T, &mut AcceptContext<'_, 'sess, S>, &ArgParser<'_>);
type AcceptMapping<T, S> = &'static [(&'static [Symbol], AcceptFn<T, S>)];
type AcceptMapping<T, S> = &'static [(&'static [Symbol], AttributeTemplate, AcceptFn<T, S>)];
/// An [`AttributeParser`] is a type which searches for syntactic attributes.
///
@ -88,6 +90,9 @@ pub(crate) trait SingleAttributeParser<S: Stage>: 'static {
const ATTRIBUTE_ORDER: AttributeOrder;
const ON_DUPLICATE: OnDuplicate<S>;
/// The template this attribute parser should implement. Used for diagnostics.
const TEMPLATE: AttributeTemplate;
/// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`]
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind>;
}
@ -104,8 +109,10 @@ impl<T: SingleAttributeParser<S>, S: Stage> Default for Single<T, S> {
}
impl<T: SingleAttributeParser<S>, S: Stage> AttributeParser<S> for Single<T, S> {
const ATTRIBUTES: AcceptMapping<Self, S> =
&[(T::PATH, |group: &mut Single<T, S>, cx, args| {
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
T::PATH,
<T as SingleAttributeParser<S>>::TEMPLATE,
|group: &mut Single<T, S>, cx, args| {
if let Some(pa) = T::convert(cx, args) {
match T::ATTRIBUTE_ORDER {
// keep the first and report immediately. ignore this attribute
@ -126,7 +133,8 @@ impl<T: SingleAttributeParser<S>, S: Stage> AttributeParser<S> for Single<T, S>
group.1 = Some((pa, cx.attr_span));
}
})];
},
)];
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
Some(self.1?.0)
@ -223,6 +231,9 @@ pub(crate) trait CombineAttributeParser<S: Stage>: 'static {
type Item;
const CONVERT: ConvertFn<Self::Item>;
/// The template this attribute parser should implement. Used for diagnostics.
const TEMPLATE: AttributeTemplate;
/// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`]
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
@ -242,8 +253,11 @@ impl<T: CombineAttributeParser<S>, S: Stage> Default for Combine<T, S> {
}
impl<T: CombineAttributeParser<S>, S: Stage> AttributeParser<S> for Combine<T, S> {
const ATTRIBUTES: AcceptMapping<Self, S> =
&[(T::PATH, |group: &mut Combine<T, S>, cx, args| group.1.extend(T::extend(cx, args)))];
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
T::PATH,
<T as CombineAttributeParser<S>>::TEMPLATE,
|group: &mut Combine<T, S>, cx, args| group.1.extend(T::extend(cx, args)),
)];
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
if self.1.is_empty() { None } else { Some(T::CONVERT(self.1)) }

View file

@ -1,6 +1,7 @@
use rustc_abi::Align;
use rustc_ast::{IntTy, LitIntType, LitKind, UintTy};
use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr};
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
use super::{CombineAttributeParser, ConvertFn};
@ -23,6 +24,8 @@ impl<S: Stage> CombineAttributeParser<S> for ReprParser {
type Item = (ReprAttr, Span);
const PATH: &[Symbol] = &[sym::repr];
const CONVERT: ConvertFn<Self::Item> = AttributeKind::Repr;
// FIXME(jdonszelmann): never used
const TEMPLATE: AttributeTemplate = template!(List: "C");
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
@ -31,6 +34,7 @@ impl<S: Stage> CombineAttributeParser<S> for ReprParser {
let mut reprs = Vec::new();
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
return reprs;
};

View file

@ -5,7 +5,8 @@ use rustc_attr_data_structures::{
StableSince, UnstableReason, VERSION_PLACEHOLDER,
};
use rustc_errors::ErrorGuaranteed;
use rustc_span::{Span, Symbol, sym};
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Ident, Span, Symbol, sym};
use super::util::parse_version;
use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser};
@ -43,26 +44,39 @@ impl StabilityParser {
impl<S: Stage> AttributeParser<S> for StabilityParser {
const ATTRIBUTES: AcceptMapping<Self, S> = &[
(&[sym::stable], |this, cx, args| {
reject_outside_std!(cx);
if !this.check_duplicate(cx)
&& let Some((feature, level)) = parse_stability(cx, args)
{
this.stability = Some((Stability { level, feature }, cx.attr_span));
}
}),
(&[sym::unstable], |this, cx, args| {
reject_outside_std!(cx);
if !this.check_duplicate(cx)
&& let Some((feature, level)) = parse_unstability(cx, args)
{
this.stability = Some((Stability { level, feature }, cx.attr_span));
}
}),
(&[sym::rustc_allowed_through_unstable_modules], |this, cx, args| {
reject_outside_std!(cx);
this.allowed_through_unstable_modules = args.name_value().and_then(|i| i.value_as_str())
}),
(
&[sym::stable],
template!(List: r#"feature = "name", since = "version""#),
|this, cx, args| {
reject_outside_std!(cx);
if !this.check_duplicate(cx)
&& let Some((feature, level)) = parse_stability(cx, args)
{
this.stability = Some((Stability { level, feature }, cx.attr_span));
}
},
),
(
&[sym::unstable],
template!(List: r#"feature = "name", reason = "...", issue = "N""#),
|this, cx, args| {
reject_outside_std!(cx);
if !this.check_duplicate(cx)
&& let Some((feature, level)) = parse_unstability(cx, args)
{
this.stability = Some((Stability { level, feature }, cx.attr_span));
}
},
),
(
&[sym::rustc_allowed_through_unstable_modules],
template!(NameValueStr: "deprecation message"),
|this, cx, args| {
reject_outside_std!(cx);
this.allowed_through_unstable_modules =
args.name_value().and_then(|i| i.value_as_str())
},
),
];
fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
@ -96,8 +110,10 @@ pub(crate) struct BodyStabilityParser {
}
impl<S: Stage> AttributeParser<S> for BodyStabilityParser {
const ATTRIBUTES: AcceptMapping<Self, S> =
&[(&[sym::rustc_default_body_unstable], |this, cx, args| {
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
&[sym::rustc_default_body_unstable],
template!(List: r#"feature = "name", reason = "...", issue = "N""#),
|this, cx, args| {
reject_outside_std!(cx);
if this.stability.is_some() {
cx.dcx()
@ -105,7 +121,8 @@ impl<S: Stage> AttributeParser<S> for BodyStabilityParser {
} else if let Some((feature, level)) = parse_unstability(cx, args) {
this.stability = Some((DefaultBodyStability { level, feature }, cx.attr_span));
}
})];
},
)];
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
let (stability, span) = self.stability?;
@ -120,6 +137,7 @@ impl<S: Stage> SingleAttributeParser<S> for ConstStabilityIndirectParser {
const PATH: &[Symbol] = &[sym::rustc_const_stable_indirect];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Ignore;
const TEMPLATE: AttributeTemplate = template!(Word);
fn convert(_cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
Some(AttributeKind::ConstStabilityIndirect)
@ -146,7 +164,7 @@ impl ConstStabilityParser {
impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
const ATTRIBUTES: AcceptMapping<Self, S> = &[
(&[sym::rustc_const_stable], |this, cx, args| {
(&[sym::rustc_const_stable], template!(List: r#"feature = "name""#), |this, cx, args| {
reject_outside_std!(cx);
if !this.check_duplicate(cx)
@ -158,7 +176,7 @@ impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
));
}
}),
(&[sym::rustc_const_unstable], |this, cx, args| {
(&[sym::rustc_const_unstable], template!(List: r#"feature = "name""#), |this, cx, args| {
reject_outside_std!(cx);
if !this.check_duplicate(cx)
&& let Some((feature, level)) = parse_unstability(cx, args)
@ -169,7 +187,7 @@ impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
));
}
}),
(&[sym::rustc_promotable], |this, cx, _| {
(&[sym::rustc_promotable], template!(Word), |this, cx, _| {
reject_outside_std!(cx);
this.promotable = true;
}),
@ -199,12 +217,10 @@ fn insert_value_into_option_or_error<S: Stage>(
cx: &AcceptContext<'_, '_, S>,
param: &MetaItemParser<'_>,
item: &mut Option<Symbol>,
name: Ident,
) -> Option<()> {
if item.is_some() {
cx.emit_err(session_diagnostics::MultipleItem {
span: param.span(),
item: param.path().to_string(),
});
cx.duplicate_key(name.span, name.name);
None
} else if let Some(v) = param.args().name_value()
&& let Some(s) = v.value_as_str()
@ -212,10 +228,7 @@ fn insert_value_into_option_or_error<S: Stage>(
*item = Some(s);
Some(())
} else {
cx.emit_err(session_diagnostics::IncorrectMetaItem {
span: param.span(),
suggestion: None,
});
cx.expected_name_value(param.span(), Some(name.name));
None
}
}
@ -241,9 +254,14 @@ pub(crate) fn parse_stability<S: Stage>(
return None;
};
match param.path().word_sym() {
Some(sym::feature) => insert_value_into_option_or_error(cx, &param, &mut feature)?,
Some(sym::since) => insert_value_into_option_or_error(cx, &param, &mut since)?,
let word = param.path().word();
match word.map(|i| i.name) {
Some(sym::feature) => {
insert_value_into_option_or_error(cx, &param, &mut feature, word.unwrap())?
}
Some(sym::since) => {
insert_value_into_option_or_error(cx, &param, &mut since, word.unwrap())?
}
_ => {
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param_span,
@ -310,11 +328,16 @@ pub(crate) fn parse_unstability<S: Stage>(
return None;
};
match param.path().word_sym() {
Some(sym::feature) => insert_value_into_option_or_error(cx, &param, &mut feature)?,
Some(sym::reason) => insert_value_into_option_or_error(cx, &param, &mut reason)?,
let word = param.path().word();
match word.map(|i| i.name) {
Some(sym::feature) => {
insert_value_into_option_or_error(cx, &param, &mut feature, word.unwrap())?
}
Some(sym::reason) => {
insert_value_into_option_or_error(cx, &param, &mut reason, word.unwrap())?
}
Some(sym::issue) => {
insert_value_into_option_or_error(cx, &param, &mut issue)?;
insert_value_into_option_or_error(cx, &param, &mut issue, word.unwrap())?;
// These unwraps are safe because `insert_value_into_option_or_error` ensures the meta item
// is a name/value pair string literal.
@ -344,9 +367,11 @@ pub(crate) fn parse_unstability<S: Stage>(
is_soft = true;
}
Some(sym::implied_by) => {
insert_value_into_option_or_error(cx, &param, &mut implied_by)?
insert_value_into_option_or_error(cx, &param, &mut implied_by, word.unwrap())?
}
Some(sym::old_name) => {
insert_value_into_option_or_error(cx, &param, &mut old_name, word.unwrap())?
}
Some(sym::old_name) => insert_value_into_option_or_error(cx, &param, &mut old_name)?,
_ => {
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param.span(),

View file

@ -1,4 +1,5 @@
use rustc_attr_data_structures::AttributeKind;
use rustc_feature::{AttributeTemplate, template};
use rustc_span::hygiene::Transparency;
use rustc_span::{Symbol, sym};
@ -17,14 +18,23 @@ impl<S: Stage> SingleAttributeParser<S> for TransparencyParser {
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Custom(|cx, used, unused| {
cx.dcx().span_err(vec![used, unused], "multiple macro transparency attributes");
});
const TEMPLATE: AttributeTemplate =
template!(NameValueStr: "transparent|semitransparent|opaque");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
match args.name_value().and_then(|nv| nv.value_as_str()) {
let Some(nv) = args.name_value() else {
cx.expected_name_value(cx.attr_span, None);
return None;
};
match nv.value_as_str() {
Some(sym::transparent) => Some(Transparency::Transparent),
Some(sym::semiopaque | sym::semitransparent) => Some(Transparency::SemiOpaque),
Some(sym::opaque) => Some(Transparency::Opaque),
Some(other) => {
cx.dcx().span_err(cx.attr_span, format!("unknown macro transparency: `{other}`"));
Some(_) => {
cx.expected_specific_argument_strings(
nv.value_span,
vec!["transparent", "semitransparent", "opaque"],
);
None
}
None => None,

View file

@ -5,12 +5,11 @@ use std::ops::{Deref, DerefMut};
use std::sync::LazyLock;
use private::Sealed;
use rustc_ast as ast;
use rustc_ast::NodeId;
use rustc_ast::{self as ast, MetaItemLit, NodeId};
use rustc_attr_data_structures::AttributeKind;
use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind};
use rustc_errors::{DiagCtxtHandle, Diagnostic};
use rustc_feature::Features;
use rustc_feature::{AttributeTemplate, Features};
use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirId};
use rustc_session::Session;
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
@ -18,6 +17,7 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::deprecation::DeprecationParser;
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
use crate::attributes::lint_helpers::AsPtrParser;
use crate::attributes::repr::ReprParser;
use crate::attributes::stability::{
@ -26,11 +26,12 @@ use crate::attributes::stability::{
use crate::attributes::transparency::TransparencyParser;
use crate::attributes::{AttributeParser as _, Combine, Single};
use crate::parser::{ArgParser, MetaItemParser};
use crate::session_diagnostics::{AttributeParseError, AttributeParseErrorReason, UnknownMetaItem};
macro_rules! group_type {
($stage: ty) => {
LazyLock<(
BTreeMap<&'static [Symbol], Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $stage>, &ArgParser<'a>) + Send + Sync>>,
BTreeMap<&'static [Symbol], Vec<(AttributeTemplate, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $stage>, &ArgParser<'a>) + Send + Sync>)>>,
Vec<Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, $stage>) -> Option<AttributeKind>>>
)>
};
@ -59,7 +60,7 @@ macro_rules! attribute_parsers {
@[$ty: ty] pub(crate) static $name: ident = [$($names: ty),* $(,)?];
) => {
pub(crate) static $name: group_type!($ty) = LazyLock::new(|| {
let mut accepts = BTreeMap::<_, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $ty>, &ArgParser<'a>) + Send + Sync>>::new();
let mut accepts = BTreeMap::<_, Vec<(AttributeTemplate, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $ty>, &ArgParser<'a>) + Send + Sync>)>>::new();
let mut finalizes = Vec::<Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, $ty>) -> Option<AttributeKind>>>::new();
$(
{
@ -67,13 +68,12 @@ macro_rules! attribute_parsers {
static STATE_OBJECT: RefCell<$names> = RefCell::new(<$names>::default());
};
for (k, v) in <$names>::ATTRIBUTES {
let old = accepts.insert(*k, Box::new(|cx, args| {
for (path, template, accept_fn) in <$names>::ATTRIBUTES {
accepts.entry(*path).or_default().push((*template, Box::new(|cx, args| {
STATE_OBJECT.with_borrow_mut(|s| {
v(s, cx, args)
accept_fn(s, cx, args)
})
}));
assert!(old.is_none());
})));
}
finalizes.push(Box::new(|cx| {
@ -106,6 +106,8 @@ attribute_parsers!(
Single<AsPtrParser>,
Single<ConstStabilityIndirectParser>,
Single<DeprecationParser>,
Single<InlineParser>,
Single<RustcForceInlineParser>,
Single<TransparencyParser>,
// tidy-alphabetical-end
];
@ -165,6 +167,14 @@ pub(crate) struct AcceptContext<'f, 'sess, S: Stage> {
pub(crate) finalize_cx: FinalizeContext<'f, 'sess, S>,
/// The span of the attribute currently being parsed
pub(crate) attr_span: Span,
/// The expected structure of the attribute.
///
/// Used in reporting errors to give a hint to users what the attribute *should* look like.
pub(crate) template: &'f AttributeTemplate,
/// The name of the attribute we're currently accepting.
pub(crate) attr_path: AttrPath,
}
impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
@ -172,10 +182,133 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
S::emit_err(&self.sess, diag)
}
/// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing
/// must be delayed until after HIR is built. This method will take care of the details of
/// that.
pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) {
let id = self.target_id;
(self.emit_lint)(AttributeLint { id, span, kind: lint });
}
pub(crate) fn unknown_key(
&self,
span: Span,
found: String,
options: &'static [&'static str],
) -> ErrorGuaranteed {
self.emit_err(UnknownMetaItem { span, item: found, expected: options })
}
/// error that a string literal was expected.
/// You can optionally give the literal you did find (which you found not to be a string literal)
/// which can make better errors. For example, if the literal was a byte string it will suggest
/// removing the `b` prefix.
pub(crate) fn expected_string_literal(
&self,
span: Span,
actual_literal: Option<&MetaItemLit>,
) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedStringLiteral {
byte_string: actual_literal.and_then(|i| {
i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span))
}),
},
})
}
pub(crate) fn expected_list(&self, span: Span) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedList,
})
}
/// emit an error that a `name = value` pair was expected at this span. The symbol can be given for
/// a nicer error message talking about the specific name that was found lacking a value.
pub(crate) fn expected_name_value(&self, span: Span, name: Option<Symbol>) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedNameValue(name),
})
}
/// emit an error that a `name = value` pair was found where that name was already seen.
pub(crate) fn duplicate_key(&self, span: Span, key: Symbol) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::DuplicateKey(key),
})
}
/// an error that should be emitted when a [`MetaItemOrLitParser`](crate::parser::MetaItemOrLitParser)
/// was expected *not* to be a literal, but instead a meta item.
pub(crate) fn unexpected_literal(&self, span: Span) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::UnexpectedLiteral,
})
}
pub(crate) fn expected_single_argument(&self, span: Span) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedSingleArgument,
})
}
pub(crate) fn expected_specific_argument(
&self,
span: Span,
possibilities: Vec<&'static str>,
) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
possibilities,
strings: false,
},
})
}
pub(crate) fn expected_specific_argument_strings(
&self,
span: Span,
possibilities: Vec<&'static str>,
) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
possibilities,
strings: true,
},
})
}
}
impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> {
@ -374,18 +507,22 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
let args = parser.args();
let parts = path.segments().map(|i| i.name).collect::<Vec<_>>();
if let Some(accept) = S::parsers().0.get(parts.as_slice()) {
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
finalize_cx: FinalizeContext {
cx: self,
target_span,
target_id,
emit_lint: &mut emit_lint,
},
attr_span: lower_span(attr.span),
};
if let Some(accepts) = S::parsers().0.get(parts.as_slice()) {
for (template, accept) in accepts {
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
finalize_cx: FinalizeContext {
cx: self,
target_span,
target_id,
emit_lint: &mut emit_lint,
},
attr_span: lower_span(attr.span),
template,
attr_path: path.get_attribute_path(),
};
accept(&mut cx, args)
accept(&mut cx, args)
}
} else {
// If we're here, we must be compiling a tool attribute... Or someone
// forgot to parse their fancy new attribute. Let's warn them in any case.

View file

@ -1,5 +1,5 @@
use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind};
use rustc_errors::LintEmitter;
use rustc_errors::{DiagArgValue, LintEmitter};
use rustc_hir::HirId;
use crate::session_diagnostics;
@ -15,5 +15,18 @@ pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<HirId>, lint_emi
*span,
session_diagnostics::UnusedDuplicate { this, other, warning },
),
AttributeLintKind::IllFormedAttributeInput { suggestions } => {
lint_emitter.emit_node_span_lint(
rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT,
*id,
*span,
session_diagnostics::IllFormedAttributeInput {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
),
},
);
}
}
}

View file

@ -2,7 +2,11 @@ use std::num::IntErrorKind;
use rustc_ast as ast;
use rustc_errors::codes::*;
use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level};
use rustc_errors::{
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
};
use rustc_feature::AttributeTemplate;
use rustc_hir::AttrPath;
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_span::{Span, Symbol};
@ -12,8 +16,6 @@ pub(crate) enum UnsupportedLiteralReason {
Generic,
CfgString,
CfgBoolean,
DeprecatedString,
DeprecatedKvPair,
}
#[derive(Diagnostic)]
@ -32,37 +34,6 @@ pub(crate) struct InvalidPredicate {
pub predicate: String,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_multiple_item, code = E0538)]
pub(crate) struct MultipleItem {
#[primary_span]
pub span: Span,
pub item: String,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_incorrect_meta_item, code = E0539)]
pub(crate) struct IncorrectMetaItem {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub suggestion: Option<IncorrectMetaItemSuggestion>,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(
attr_parsing_incorrect_meta_item_suggestion,
applicability = "maybe-incorrect"
)]
pub(crate) struct IncorrectMetaItemSuggestion {
#[suggestion_part(code = "\"")]
pub lo: Span,
#[suggestion_part(code = "\"")]
pub hi: Span,
}
/// Error code: E0541
pub(crate) struct UnknownMetaItem<'a> {
pub span: Span,
@ -217,6 +188,7 @@ pub(crate) struct InvalidReprHintNoValue {
}
/// Error code: E0565
// FIXME(jdonszelmann): slowly phased out
pub(crate) struct UnsupportedLiteral {
pub span: Span,
pub reason: UnsupportedLiteralReason,
@ -239,12 +211,6 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral {
UnsupportedLiteralReason::CfgBoolean => {
fluent::attr_parsing_unsupported_literal_cfg_boolean
}
UnsupportedLiteralReason::DeprecatedString => {
fluent::attr_parsing_unsupported_literal_deprecated_string
}
UnsupportedLiteralReason::DeprecatedKvPair => {
fluent::attr_parsing_unsupported_literal_deprecated_kv_pair
}
},
);
diag.span(self.span);
@ -462,6 +428,14 @@ pub(crate) struct UnusedDuplicate {
pub warning: bool,
}
// FIXME(jdonszelmann): duplicated in rustc_lints, should be moved here completely.
#[derive(LintDiagnostic)]
#[diag(attr_parsing_ill_formed_attribute_input)]
pub(crate) struct IllFormedAttributeInput {
pub num_suggestions: usize,
pub suggestions: DiagArgValue,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_stability_outside_std, code = E0734)]
pub(crate) struct StabilityOutsideStd {
@ -490,3 +464,115 @@ pub(crate) struct UnrecognizedReprHint {
#[primary_span]
pub span: Span,
}
pub(crate) enum AttributeParseErrorReason {
ExpectedStringLiteral { byte_string: Option<Span> },
ExpectedSingleArgument,
ExpectedList,
UnexpectedLiteral,
ExpectedNameValue(Option<Symbol>),
DuplicateKey(Symbol),
ExpectedSpecificArgument { possibilities: Vec<&'static str>, strings: bool },
}
pub(crate) struct AttributeParseError {
pub(crate) span: Span,
pub(crate) attr_span: Span,
pub(crate) template: AttributeTemplate,
pub(crate) attribute: AttrPath,
pub(crate) reason: AttributeParseErrorReason,
}
impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
let name = self.attribute.to_string();
let mut diag = Diag::new(dcx, level, format!("malformed `{name}` attribute input"));
diag.span(self.attr_span);
diag.code(E0539);
match self.reason {
AttributeParseErrorReason::ExpectedStringLiteral { byte_string } => {
if let Some(start_point_span) = byte_string {
diag.span_suggestion(
start_point_span,
fluent::attr_parsing_unsupported_literal_suggestion,
"",
Applicability::MaybeIncorrect,
);
diag.note("expected a normal string literal, not a byte string literal");
return diag;
} else {
diag.span_label(self.span, "expected a string literal here");
}
}
AttributeParseErrorReason::ExpectedSingleArgument => {
diag.span_label(self.span, "expected a single argument here");
diag.code(E0805);
}
AttributeParseErrorReason::ExpectedList => {
diag.span_label(self.span, "expected this to be a list");
}
AttributeParseErrorReason::DuplicateKey(key) => {
diag.span_label(self.span, format!("found `{key}` used as a key more than once"));
diag.code(E0538);
}
AttributeParseErrorReason::UnexpectedLiteral => {
diag.span_label(self.span, format!("didn't expect a literal here"));
diag.code(E0565);
}
AttributeParseErrorReason::ExpectedNameValue(None) => {
diag.span_label(
self.span,
format!("expected this to be of the form `{name} = \"...\"`"),
);
}
AttributeParseErrorReason::ExpectedNameValue(Some(name)) => {
diag.span_label(
self.span,
format!("expected this to be of the form `{name} = \"...\"`"),
);
}
AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings } => {
let quote = if strings { '"' } else { '`' };
match possibilities.as_slice() {
&[] => {}
&[x] => {
diag.span_label(
self.span,
format!("the only valid argument here is {quote}{x}{quote}"),
);
}
[first, second] => {
diag.span_label(self.span, format!("valid arguments are {quote}{first}{quote} or {quote}{second}{quote}"));
}
[first @ .., second_to_last, last] => {
let mut res = String::new();
for i in first {
res.push_str(&format!("{quote}{i}{quote}, "));
}
res.push_str(&format!(
"{quote}{second_to_last}{quote} or {quote}{last}{quote}"
));
diag.span_label(self.span, format!("valid arguments are {res}"));
}
}
}
}
let suggestions = self.template.suggestions(false, &name);
diag.span_suggestions(
self.attr_span,
if suggestions.len() == 1 {
"must be of the form"
} else {
"try changing it to one of the following valid forms of the attribute"
},
suggestions,
Applicability::HasPlaceholders,
);
diag
}
}

View file

@ -1,6 +1,7 @@
//! Allocator shim
// Adapted from rustc
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
use rustc_ast::expand::allocator::{
ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE,
alloc_error_handler_name, default_fn_name, global_fn_name,
@ -97,16 +98,31 @@ fn codegen_inner(
data.define(Box::new([val]));
module.define_data(data_id, &data).unwrap();
let data_id = module
.declare_data(
&mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE),
Linkage::Export,
false,
false,
)
.unwrap();
let mut data = DataDescription::new();
data.set_align(1);
data.define(Box::new([0]));
module.define_data(data_id, &data).unwrap();
{
let sig = Signature {
call_conv: module.target_config().default_call_conv,
params: vec![],
returns: vec![],
};
let func_id = module
.declare_function(
&mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE),
Linkage::Export,
&sig,
)
.unwrap();
let mut ctx = Context::new();
ctx.func.signature = sig;
let mut func_ctx = FunctionBuilderContext::new();
let mut bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
let block = bcx.create_block();
bcx.switch_to_block(block);
bcx.ins().return_(&[]);
bcx.seal_all_blocks();
bcx.finalize();
module.define_function(func_id, &mut ctx).unwrap();
}
}

View file

@ -0,0 +1,27 @@
{
"allowCompoundWords": true,
"dictionaries": ["cpp", "rust-extra", "rustc_codegen_gcc"],
"dictionaryDefinitions": [
{
"name": "rust-extra",
"path": "tools/cspell_dicts/rust.txt",
"addWords": true
},
{
"name": "rustc_codegen_gcc",
"path": "tools/cspell_dicts/rustc_codegen_gcc.txt",
"addWords": true
}
],
"files": [
"src/**/*.rs"
],
"ignorePaths": [
"src/intrinsic/archs.rs",
"src/intrinsic/llvm.rs"
],
"ignoreRegExpList": [
"/(FIXME|NOTE|TODO)\\([^)]+\\)/",
"__builtin_\\w*"
]
}

View file

@ -12,6 +12,8 @@ permissions:
env:
# Enable backtraces for easier debugging
RUST_BACKTRACE: 1
# For the run-make tests.
LLVM_BIN_DIR: /usr/bin
jobs:
build:
@ -48,7 +50,7 @@ jobs:
- name: Install packages
# `llvm-14-tools` is needed to install the `FileCheck` binary which is used for asm tests.
run: sudo apt-get install ninja-build ripgrep llvm-14-tools
run: sudo apt-get install ninja-build ripgrep llvm-14-tools llvm
- name: Install rustfmt & clippy
run: rustup component add rustfmt clippy
@ -61,11 +63,15 @@ jobs:
sudo dpkg --force-overwrite -i ${{ matrix.libgccjit_version.gcc }}
echo 'gcc-path = "/usr/lib/"' > config.toml
# Some run-make tests fail if we use our forked GCC because it doesn't
# bundle libstdc++, so we switch to gcc-14 to have a GCC that has
# libstdc++.
- name: Set default GCC to gcc-14
run: sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-14 30
- name: Set env
run: |
echo "workspace="$GITHUB_WORKSPACE >> $GITHUB_ENV
echo "LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
echo "LD_LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
#- name: Cache rust repository
## We only clone the rust repository for rustc tests
@ -76,12 +82,22 @@ jobs:
#path: rust
#key: ${{ runner.os }}-packages-${{ hashFiles('rust/.git/HEAD') }}
- name: Prepare
run: ./y.sh prepare --only-libcore
- name: Check formatting
run: ./y.sh fmt --check
- name: clippy
run: |
cargo clippy --all-targets -- -D warnings
cargo clippy --all-targets --no-default-features -- -D warnings
cargo clippy --manifest-path build_system/Cargo.toml --all-targets -- -D warnings
- name: Build
run: |
./y.sh prepare --only-libcore
./y.sh build --sysroot
./y.sh test --mini-tests
cargo test
./y.sh test --cargo-tests
- name: Run y.sh cargo build
run: |
@ -101,20 +117,19 @@ jobs:
run: |
./y.sh test --release --clean --build-sysroot ${{ matrix.commands }}
- name: Check formatting
run: ./y.sh fmt --check
- name: clippy
run: |
cargo clippy --all-targets -- -D warnings
cargo clippy --all-targets --features master -- -D warnings
duplicates:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- run: python tools/check_intrinsics_duplicates.py
spell_check:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: crate-ci/typos@v1.32.0
- uses: streetsidesoftware/cspell-action@v7
build_system:
runs-on: ubuntu-24.04
steps:

View file

@ -66,8 +66,8 @@ jobs:
run: |
sudo dpkg --force-overwrite -i gcc-15.deb
echo 'gcc-path = "/usr/lib"' > config.toml
echo "LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
echo "LD_LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
- name: Set env
run: |

View file

@ -65,8 +65,8 @@ jobs:
- name: Set env
run: |
echo "workspace="$GITHUB_WORKSPACE >> $GITHUB_ENV
echo "LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
echo "LD_LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
#- name: Cache rust repository
## We only clone the rust repository for rustc tests
@ -95,7 +95,7 @@ jobs:
./y.sh prepare --only-libcore --cross
./y.sh build --sysroot --features compiler_builtins/no-f16-f128 --target-triple m68k-unknown-linux-gnu
./y.sh test --mini-tests
CG_GCC_TEST_TARGET=m68k-unknown-linux-gnu cargo test
CG_GCC_TEST_TARGET=m68k-unknown-linux-gnu ./y.sh test --cargo-tests
./y.sh clean all
- name: Prepare dependencies

View file

@ -12,6 +12,8 @@ permissions:
env:
# Enable backtraces for easier debugging
RUST_BACKTRACE: 1
# For the run-make tests.
LLVM_BIN_DIR: /usr/bin
jobs:
build:
@ -36,7 +38,8 @@ jobs:
uses: Swatinem/rust-cache@v2
- name: Install packages
run: sudo apt-get install ninja-build ripgrep
# `llvm-14-tools` is needed to install the `FileCheck` binary which is used for run-make tests.
run: sudo apt-get install ninja-build ripgrep llvm-14-tools llvm
- name: Download artifact
run: curl -LO https://github.com/rust-lang/gcc/releases/latest/download/gcc-15.deb
@ -46,18 +49,21 @@ jobs:
sudo dpkg --force-overwrite -i gcc-15.deb
echo 'gcc-path = "/usr/lib/"' > config.toml
# Some run-make tests fail if we use our forked GCC because it doesn't
# bundle libstdc++, so we switch to gcc-14 to have a GCC that has
# libstdc++.
- name: Set default GCC to gcc-14
run: sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-14 30
- name: Set env
run: |
echo "workspace="$GITHUB_WORKSPACE >> $GITHUB_ENV
echo "LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
echo "LD_LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
- name: Build
run: |
./y.sh prepare --only-libcore
EMBED_LTO_BITCODE=1 ./y.sh build --sysroot --release --release-sysroot
./y.sh test --mini-tests
cargo test
./y.sh test --cargo-tests
./y.sh clean all
- name: Prepare dependencies

View file

@ -90,7 +90,7 @@ jobs:
if: ${{ !matrix.cargo_runner }}
run: |
./y.sh test --release --clean --release-sysroot --build-sysroot --mini-tests --std-tests --test-libcore
cargo test
./y.sh test --cargo-tests
- name: Run stdarch tests
if: ${{ !matrix.cargo_runner }}

View file

@ -19,4 +19,5 @@ tools/llvmint-2
llvm
build_system/target
config.toml
build
build
rustlantis

View file

@ -33,7 +33,7 @@ To run specific tests, use appropriate flags such as:
- `./y.sh test --test-libcore`
- `./y.sh test --std-tests`
- `cargo test -- <name of test>`
- `./y.sh test --cargo-tests -- <name of test>`
Additionally, you can run the tests of `libgccjit`:

View file

@ -81,6 +81,18 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "getrandom"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi",
]
[[package]]
name = "hermit-abi"
version = "0.3.1"
@ -111,9 +123,9 @@ checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
[[package]]
name = "memchr"
@ -137,6 +149,12 @@ version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "r-efi"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[package]]
name = "regex"
version = "1.8.4"
@ -166,9 +184,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.42"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
dependencies = [
"bitflags",
"errno",
@ -188,12 +206,12 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.14.0"
version = "3.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
dependencies = [
"cfg-if",
"fastrand",
"getrandom",
"once_cell",
"rustix",
"windows-sys",
@ -242,6 +260,15 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "winapi"
version = "0.3.9"
@ -345,3 +372,12 @@ name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags",
]

View file

@ -31,7 +31,7 @@ gccjit = "2.7"
[dev-dependencies]
boml = "0.3.1"
lang_tester = "0.8.0"
tempfile = "3.7.1"
tempfile = "3.20"
[profile.dev]
# By compiling dependencies with optimizations, performing tests gets much faster.

View file

@ -0,0 +1,9 @@
[default.extend-words]
ba = "ba"
hsa = "hsa"
olt = "olt"
seh = "seh"
typ = "typ"
[files]
extend-exclude = ["src/intrinsic/archs.rs"]

View file

@ -1,24 +1,24 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "addr2line"
version = "0.22.0"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"compiler_builtins",
"gimli 0.29.0",
"gimli",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
[[package]]
name = "adler"
version = "1.0.2"
name = "adler2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
@ -33,10 +33,21 @@ dependencies = [
]
[[package]]
name = "allocator-api2"
version = "0.2.18"
name = "alloctests"
version = "0.0.0"
dependencies = [
"rand",
"rand_xorshift",
]
[[package]]
name = "cc"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8"
dependencies = [
"shlex",
]
[[package]]
name = "cfg-if"
@ -50,10 +61,11 @@ dependencies = [
[[package]]
name = "compiler_builtins"
version = "0.1.118"
version = "0.1.160"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92afe7344b64cccf3662ca26d5d1c0828ab826f04206b97d856e3625e390e4b5"
checksum = "6376049cfa92c0aa8b9ac95fae22184b981c658208d4ed8a1dc553cd83612895"
dependencies = [
"cc",
"rustc-std-workspace-core",
]
@ -61,11 +73,19 @@ dependencies = [
name = "core"
version = "0.0.0"
[[package]]
name = "coretests"
version = "0.0.0"
dependencies = [
"rand",
"rand_xorshift",
]
[[package]]
name = "dlmalloc"
version = "0.2.6"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3264b043b8e977326c1ee9e723da2c1f8d09a99df52cacf00b4dbce5ac54414d"
checksum = "8cff88b751e7a276c4ab0e222c3f355190adc6dde9ce39c851db39da34990df7"
dependencies = [
"cfg-if",
"compiler_builtins",
@ -97,20 +117,9 @@ dependencies = [
[[package]]
name = "gimli"
version = "0.29.0"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
[[package]]
name = "gimli"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e1d97fbe9722ba9bbd0c97051c2956e726562b61f86a25a4360398a40edfc9"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-alloc",
@ -119,11 +128,10 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.14.5"
version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
dependencies = [
"allocator-api2",
"compiler_builtins",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
@ -131,9 +139,9 @@ dependencies = [
[[package]]
name = "hermit-abi"
version = "0.4.0"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-alloc",
@ -142,9 +150,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.155"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
dependencies = [
"rustc-std-workspace-core",
]
@ -161,11 +169,11 @@ dependencies = [
[[package]]
name = "miniz_oxide"
version = "0.7.4"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
dependencies = [
"adler",
"adler2",
"compiler_builtins",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
@ -173,9 +181,9 @@ dependencies = [
[[package]]
name = "object"
version = "0.36.3"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"compiler_builtins",
"memchr",
@ -188,7 +196,6 @@ name = "panic_abort"
version = "0.0.0"
dependencies = [
"alloc",
"cfg-if",
"compiler_builtins",
"core",
"libc",
@ -211,14 +218,22 @@ name = "proc_macro"
version = "0.0.0"
dependencies = [
"core",
"rustc-literal-escaper",
"std",
]
[[package]]
name = "profiler_builtins"
version = "0.0.0"
dependencies = [
"cc",
]
[[package]]
name = "r-efi"
version = "4.5.0"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9e935efc5854715dfc0a4c9ef18dc69dee0ec3bf9cc3ab740db831c0fdd86a3"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
@ -226,15 +241,39 @@ dependencies = [
[[package]]
name = "r-efi-alloc"
version = "1.0.0"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31d6f09fe2b6ad044bc3d2c34ce4979796581afd2f1ebc185837e02421e02fd7"
checksum = "e43c53ff1a01d423d1cb762fd991de07d32965ff0ca2e4f80444ac7804198203"
dependencies = [
"compiler_builtins",
"r-efi",
"rustc-std-workspace-core",
]
[[package]]
name = "rand"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
dependencies = [
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
[[package]]
name = "rand_xorshift"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
dependencies = [
"rand_core",
]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
@ -245,6 +284,15 @@ dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "rustc-literal-escaper"
version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0041b6238913c41fe704213a4a9329e2f685a156d1781998128b4149c230ad04"
dependencies = [
"rustc-std-workspace-std",
]
[[package]]
name = "rustc-std-workspace-alloc"
version = "1.99.0"
@ -266,6 +314,12 @@ dependencies = [
"std",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "std"
version = "0.0.0"
@ -286,10 +340,13 @@ dependencies = [
"panic_unwind",
"r-efi",
"r-efi-alloc",
"rand",
"rand_xorshift",
"rustc-demangle",
"std_detect",
"unwind",
"wasi",
"windows-targets 0.0.0",
]
[[package]]
@ -298,6 +355,7 @@ version = "0.1.5"
dependencies = [
"cfg-if",
"compiler_builtins",
"libc",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
@ -306,10 +364,8 @@ dependencies = [
name = "sysroot"
version = "0.0.0"
dependencies = [
"alloc",
"compiler_builtins",
"core",
"proc_macro",
"profiler_builtins",
"std",
"test",
]
@ -326,9 +382,9 @@ dependencies = [
[[package]]
name = "unicode-width"
version = "0.1.13"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
@ -348,12 +404,12 @@ dependencies = [
[[package]]
name = "unwinding"
version = "0.2.2"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc55842d0db6329a669d55a623c674b02d677b16bfb2d24857d4089d41eba882"
checksum = "8393f2782b6060a807337ff353780c1ca15206f9ba2424df18cb6e733bd7b345"
dependencies = [
"compiler_builtins",
"gimli 0.30.0",
"gimli",
"rustc-std-workspace-core",
]
@ -370,13 +426,17 @@ dependencies = [
[[package]]
name = "windows-sys"
version = "0.52.0"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.0.0"
[[package]]
name = "windows-targets"
version = "0.52.6"

View file

@ -33,7 +33,7 @@ impl BuildArg {
}
arg => {
if !build_arg.config_info.parse_argument(arg, &mut args)? {
return Err(format!("Unknown argument `{}`", arg));
return Err(format!("Unknown argument `{arg}`"));
}
}
}
@ -105,14 +105,14 @@ pub fn create_build_sysroot_content(start_dir: &Path) -> Result<(), String> {
if !start_dir.is_dir() {
create_dir(start_dir)?;
}
copy_file("build_system/build_sysroot/Cargo.toml", &start_dir.join("Cargo.toml"))?;
copy_file("build_system/build_sysroot/Cargo.lock", &start_dir.join("Cargo.lock"))?;
copy_file("build_system/build_sysroot/Cargo.toml", start_dir.join("Cargo.toml"))?;
copy_file("build_system/build_sysroot/Cargo.lock", start_dir.join("Cargo.lock"))?;
let src_dir = start_dir.join("src");
if !src_dir.is_dir() {
create_dir(&src_dir)?;
}
copy_file("build_system/build_sysroot/lib.rs", &start_dir.join("src/lib.rs"))
copy_file("build_system/build_sysroot/lib.rs", start_dir.join("src/lib.rs"))
}
pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Result<(), String> {
@ -169,7 +169,7 @@ pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Resu
run_command(&[&"cp", &"-r", &dir_to_copy, &sysroot_path], None).map(|_| ())
};
walk_dir(
start_dir.join(&format!("target/{}/{}/deps", config.target_triple, channel)),
start_dir.join(format!("target/{}/{}/deps", config.target_triple, channel)),
&mut copier.clone(),
&mut copier,
false,

View file

@ -17,12 +17,12 @@ enum CleanArg {
impl CleanArg {
fn new() -> Result<Self, String> {
// We skip the binary and the "clean" option.
for arg in std::env::args().skip(2) {
if let Some(arg) = std::env::args().nth(2) {
return match arg.as_str() {
"all" => Ok(Self::All),
"ui-tests" => Ok(Self::UiTests),
"--help" => Ok(Self::Help),
a => Err(format!("Unknown argument `{}`", a)),
a => Err(format!("Unknown argument `{a}`")),
};
}
Ok(Self::default())

View file

@ -43,7 +43,7 @@ impl Args {
}
arg => {
if !command_args.config_info.parse_argument(arg, &mut args)? {
return Err(format!("Unknown option {}", arg));
return Err(format!("Unknown option {arg}"));
}
}
}
@ -52,7 +52,7 @@ impl Args {
Some(p) => p.into(),
None => PathBuf::from("./gcc"),
};
return Ok(Some(command_args));
Ok(Some(command_args))
}
}
@ -64,7 +64,7 @@ pub fn run() -> Result<(), String> {
let result = git_clone("https://github.com/rust-lang/gcc", Some(&args.out_path), false)?;
if result.ran_clone {
let gcc_commit = args.config_info.get_gcc_commit()?;
println!("Checking out GCC commit `{}`...", gcc_commit);
println!("Checking out GCC commit `{gcc_commit}`...");
run_command_with_output(
&[&"git", &"checkout", &gcc_commit],
Some(Path::new(&result.repo_dir)),

View file

@ -66,7 +66,7 @@ impl ConfigFile {
"Expected a boolean for `download-gccjit`",
);
}
_ => return failed_config_parsing(config_file, &format!("Unknown key `{}`", key)),
_ => return failed_config_parsing(config_file, &format!("Unknown key `{key}`")),
}
}
match (config.gcc_path.as_mut(), config.download_gccjit) {
@ -86,9 +86,7 @@ impl ConfigFile {
let path = Path::new(gcc_path);
*gcc_path = path
.canonicalize()
.map_err(|err| {
format!("Failed to get absolute path of `{}`: {:?}", gcc_path, err)
})?
.map_err(|err| format!("Failed to get absolute path of `{gcc_path}`: {err:?}"))?
.display()
.to_string();
}
@ -175,7 +173,7 @@ impl ConfigInfo {
"--sysroot-panic-abort" => self.sysroot_panic_abort = true,
"--gcc-path" => match args.next() {
Some(arg) if !arg.is_empty() => {
self.gcc_path = Some(arg.into());
self.gcc_path = Some(arg);
}
_ => {
return Err("Expected a value after `--gcc-path`, found nothing".to_string());
@ -244,7 +242,7 @@ impl ConfigInfo {
let libgccjit_so = output_dir.join(libgccjit_so_name);
if !libgccjit_so.is_file() && !self.no_download {
// Download time!
let tempfile_name = format!("{}.download", libgccjit_so_name);
let tempfile_name = format!("{libgccjit_so_name}.download");
let tempfile = output_dir.join(&tempfile_name);
let is_in_ci = std::env::var("GITHUB_ACTIONS").is_ok();
@ -262,14 +260,14 @@ impl ConfigInfo {
)
})?;
println!("Downloaded libgccjit.so version {} successfully!", commit);
println!("Downloaded libgccjit.so version {commit} successfully!");
// We need to create a link named `libgccjit.so.0` because that's what the linker is
// looking for.
create_symlink(&libgccjit_so, output_dir.join(&format!("{}.0", libgccjit_so_name)))?;
create_symlink(&libgccjit_so, output_dir.join(format!("{libgccjit_so_name}.0")))?;
}
let gcc_path = output_dir.display().to_string();
println!("Using `{}` as path for libgccjit", gcc_path);
println!("Using `{gcc_path}` as path for libgccjit");
self.gcc_path = Some(gcc_path);
Ok(())
}
@ -286,8 +284,7 @@ impl ConfigInfo {
// since we already have everything we need.
if let Some(gcc_path) = &self.gcc_path {
println!(
"`--gcc-path` was provided, ignoring config file. Using `{}` as path for libgccjit",
gcc_path
"`--gcc-path` was provided, ignoring config file. Using `{gcc_path}` as path for libgccjit"
);
return Ok(());
}
@ -343,7 +340,7 @@ impl ConfigInfo {
self.dylib_ext = match os_name.as_str() {
"Linux" => "so",
"Darwin" => "dylib",
os => return Err(format!("unsupported OS `{}`", os)),
os => return Err(format!("unsupported OS `{os}`")),
}
.to_string();
let rustc = match env.get("RUSTC") {
@ -355,10 +352,10 @@ impl ConfigInfo {
None => return Err("no host found".to_string()),
};
if self.target_triple.is_empty() {
if let Some(overwrite) = env.get("OVERWRITE_TARGET_TRIPLE") {
self.target_triple = overwrite.clone();
}
if self.target_triple.is_empty()
&& let Some(overwrite) = env.get("OVERWRITE_TARGET_TRIPLE")
{
self.target_triple = overwrite.clone();
}
if self.target_triple.is_empty() {
self.target_triple = self.host_triple.clone();
@ -378,7 +375,7 @@ impl ConfigInfo {
}
let current_dir =
std_env::current_dir().map_err(|error| format!("`current_dir` failed: {:?}", error))?;
std_env::current_dir().map_err(|error| format!("`current_dir` failed: {error:?}"))?;
let channel = if self.channel == Channel::Release {
"release"
} else if let Some(channel) = env.get("CHANNEL") {
@ -391,15 +388,15 @@ impl ConfigInfo {
self.cg_backend_path = current_dir
.join("target")
.join(channel)
.join(&format!("librustc_codegen_gcc.{}", self.dylib_ext))
.join(format!("librustc_codegen_gcc.{}", self.dylib_ext))
.display()
.to_string();
self.sysroot_path =
current_dir.join(&get_sysroot_dir()).join("sysroot").display().to_string();
current_dir.join(get_sysroot_dir()).join("sysroot").display().to_string();
if let Some(backend) = &self.backend {
// This option is only used in the rust compiler testsuite. The sysroot is handled
// by its build system directly so no need to set it ourselves.
rustflags.push(format!("-Zcodegen-backend={}", backend));
rustflags.push(format!("-Zcodegen-backend={backend}"));
} else {
rustflags.extend_from_slice(&[
"--sysroot".to_string(),
@ -412,10 +409,10 @@ impl ConfigInfo {
// We have a different environment variable than RUSTFLAGS to make sure those flags are
// only sent to rustc_codegen_gcc and not the LLVM backend.
if let Some(cg_rustflags) = env.get("CG_RUSTFLAGS") {
rustflags.extend_from_slice(&split_args(&cg_rustflags)?);
rustflags.extend_from_slice(&split_args(cg_rustflags)?);
}
if let Some(test_flags) = env.get("TEST_FLAGS") {
rustflags.extend_from_slice(&split_args(&test_flags)?);
rustflags.extend_from_slice(&split_args(test_flags)?);
}
if let Some(linker) = linker {
@ -438,8 +435,8 @@ impl ConfigInfo {
env.insert("RUSTC_LOG".to_string(), "warn".to_string());
let sysroot = current_dir
.join(&get_sysroot_dir())
.join(&format!("sysroot/lib/rustlib/{}/lib", self.target_triple));
.join(get_sysroot_dir())
.join(format!("sysroot/lib/rustlib/{}/lib", self.target_triple));
let ld_library_path = format!(
"{target}:{sysroot}:{gcc_path}",
target = self.cargo_target_dir,
@ -505,7 +502,7 @@ fn download_gccjit(
with_progress_bar: bool,
) -> Result<(), String> {
let url = if std::env::consts::OS == "linux" && std::env::consts::ARCH == "x86_64" {
format!("https://github.com/rust-lang/gcc/releases/download/master-{}/libgccjit.so", commit)
format!("https://github.com/rust-lang/gcc/releases/download/master-{commit}/libgccjit.so")
} else {
eprintln!(
"\
@ -518,7 +515,7 @@ to `download-gccjit = false` and set `gcc-path` to the appropriate directory."
));
};
println!("Downloading `{}`...", url);
println!("Downloading `{url}`...");
// Try curl. If that fails and we are on windows, fallback to PowerShell.
let mut ret = run_command_with_output(
@ -538,7 +535,7 @@ to `download-gccjit = false` and set `gcc-path` to the appropriate directory."
if with_progress_bar { &"--progress-bar" } else { &"-s" },
&url.as_str(),
],
Some(&output_dir),
Some(output_dir),
);
if ret.is_err() && cfg!(windows) {
eprintln!("Fallback to PowerShell");
@ -549,12 +546,11 @@ to `download-gccjit = false` and set `gcc-path` to the appropriate directory."
&"-Command",
&"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
&format!(
"(New-Object System.Net.WebClient).DownloadFile('{}', '{}')",
url, tempfile_name,
"(New-Object System.Net.WebClient).DownloadFile('{url}', '{tempfile_name}')",
)
.as_str(),
],
Some(&output_dir),
Some(output_dir),
);
}
ret

View file

@ -16,21 +16,21 @@ fn show_usage() {
pub fn run() -> Result<(), String> {
let mut check = false;
// We skip binary name and the `info` command.
let mut args = std::env::args().skip(2);
while let Some(arg) = args.next() {
let args = std::env::args().skip(2);
for arg in args {
match arg.as_str() {
"--help" => {
show_usage();
return Ok(());
}
"--check" => check = true,
_ => return Err(format!("Unknown option {}", arg)),
_ => return Err(format!("Unknown option {arg}")),
}
}
let cmd: &[&dyn AsRef<OsStr>] =
if check { &[&"cargo", &"fmt", &"--check"] } else { &[&"cargo", &"fmt"] };
run_command_with_output(cmd, Some(&Path::new(".")))?;
run_command_with_output(cmd, Some(&Path::new("build_system")))
run_command_with_output(cmd, Some(Path::new(".")))?;
run_command_with_output(cmd, Some(Path::new("build_system")))
}

View file

@ -0,0 +1,289 @@
use std::ffi::OsStr;
use std::path::Path;
mod reduce;
use crate::utils::run_command_with_output;
fn show_usage() {
println!(
r#"
`fuzz` command help:
--reduce : Reduces a file generated by rustlantis
--help : Show this help
--start : Start of the fuzzed range
--count : The number of cases to fuzz
-j --jobs : The number of threads to use during fuzzing"#
);
}
pub fn run() -> Result<(), String> {
// We skip binary name and the `fuzz` command.
let mut args = std::env::args().skip(2);
let mut start = 0;
let mut count = 100;
let mut threads =
std::thread::available_parallelism().map(|threads| threads.get()).unwrap_or(1);
while let Some(arg) = args.next() {
match arg.as_str() {
"--reduce" => {
let Some(path) = args.next() else {
return Err("--reduce must be provided with a path".into());
};
if !std::fs::exists(&path).unwrap_or(false) {
return Err("--reduce must be provided with a valid path".into());
}
reduce::reduce(&path);
return Ok(());
}
"--help" => {
show_usage();
return Ok(());
}
"--start" => {
start =
str::parse(&args.next().ok_or_else(|| "Fuzz start not provided!".to_string())?)
.map_err(|err| (format!("Fuzz start not a number {err:?}!")))?;
}
"--count" => {
count =
str::parse(&args.next().ok_or_else(|| "Fuzz count not provided!".to_string())?)
.map_err(|err| (format!("Fuzz count not a number {err:?}!")))?;
}
"-j" | "--jobs" => {
threads = str::parse(
&args.next().ok_or_else(|| "Fuzz thread count not provided!".to_string())?,
)
.map_err(|err| (format!("Fuzz thread count not a number {err:?}!")))?;
}
_ => return Err(format!("Unknown option {arg}")),
}
}
// Ensure that we have a cloned version of rustlantis on hand.
crate::utils::git_clone(
"https://github.com/cbeuw/rustlantis.git",
Some("clones/rustlantis".as_ref()),
true,
)
.map_err(|err| (format!("Git clone failed with message: {err:?}!")))?;
// Ensure that we are on the newest rustlantis commit.
let cmd: &[&dyn AsRef<OsStr>] = &[&"git", &"pull", &"origin"];
run_command_with_output(cmd, Some(Path::new("clones/rustlantis")))?;
// Build the release version of rustlantis
let cmd: &[&dyn AsRef<OsStr>] = &[&"cargo", &"build", &"--release"];
run_command_with_output(cmd, Some(Path::new("clones/rustlantis")))?;
// Fuzz a given range
fuzz_range(start, start + count, threads);
Ok(())
}
/// Fuzzes a range `start..end` with `threads`.
fn fuzz_range(start: u64, end: u64, threads: usize) {
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, Instant};
// Total amount of files to fuzz
let total = end - start;
// Currently fuzzed element
let start = Arc::new(AtomicU64::new(start));
// Count time during fuzzing
let start_time = Instant::now();
let mut workers = Vec::with_capacity(threads);
// Spawn `threads`..
for _ in 0..threads {
let start = start.clone();
// .. which each will ..
workers.push(std::thread::spawn(move || {
// ... grab the next fuzz seed ...
while start.load(Ordering::Relaxed) < end {
let next = start.fetch_add(1, Ordering::Relaxed);
// .. test that seed .
match test(next, false) {
Err(err) => {
// If the test failed at compile-time...
println!("test({next}) failed because {err:?}");
// ... copy that file to the directory `target/fuzz/compiletime_error`...
let mut out_path: std::path::PathBuf =
"target/fuzz/compiletime_error".into();
std::fs::create_dir_all(&out_path).unwrap();
// .. into a file named `fuzz{seed}.rs`.
out_path.push(format!("fuzz{next}.rs"));
std::fs::copy(err, out_path).unwrap();
}
Ok(Err(err)) => {
// If the test failed at run-time...
println!("The LLVM and GCC results don't match for {err:?}");
// ... generate a new file, which prints temporaries(instead of hashing them)...
let mut out_path: std::path::PathBuf = "target/fuzz/runtime_error".into();
std::fs::create_dir_all(&out_path).unwrap();
let Ok(Err(tmp_print_err)) = test(next, true) else {
// ... if that file does not reproduce the issue...
// ... save the original sample in a file named `fuzz{seed}.rs`...
out_path.push(format!("fuzz{next}.rs"));
std::fs::copy(err, &out_path).unwrap();
continue;
};
// ... if that new file still produces the issue, copy it to `fuzz{seed}.rs`..
out_path.push(format!("fuzz{next}.rs"));
std::fs::copy(tmp_print_err, &out_path).unwrap();
// ... and start reducing it, using some properties of `rustlantis` to speed up the process.
reduce::reduce(&out_path);
}
// If the test passed, do nothing
Ok(Ok(())) => (),
}
}
}));
}
// The "manager" thread loop.
while start.load(Ordering::Relaxed) < end || !workers.iter().all(|t| t.is_finished()) {
// Every 500 ms...
let five_hundred_millis = Duration::from_millis(500);
std::thread::sleep(five_hundred_millis);
// ... calculate the remaining fuzz iters ...
let remaining = end - start.load(Ordering::Relaxed);
// ... fix the count(the start counter counts the cases that
// begun fuzzing, and not only the ones that are done)...
let fuzzed = (total - remaining).saturating_sub(threads as u64);
// ... and the fuzz speed ...
let iter_per_sec = fuzzed as f64 / start_time.elapsed().as_secs_f64();
// .. and use them to display fuzzing stats.
println!(
"fuzzed {fuzzed} cases({}%), at rate {iter_per_sec} iter/s, remaining ~{}s",
(100 * fuzzed) as f64 / total as f64,
(remaining as f64) / iter_per_sec
)
}
drop(workers);
}
/// Builds & runs a file with LLVM.
fn debug_llvm(path: &std::path::Path) -> Result<Vec<u8>, String> {
// Build a file named `llvm_elf`...
let exe_path = path.with_extension("llvm_elf");
// ... using the LLVM backend ...
let output = std::process::Command::new("rustc")
.arg(path)
.arg("-o")
.arg(&exe_path)
.output()
.map_err(|err| format!("{err:?}"))?;
// ... check that the compilation succeeded ...
if !output.status.success() {
return Err(format!("LLVM compilation failed:{output:?}"));
}
// ... run the resulting executable ...
let output =
std::process::Command::new(&exe_path).output().map_err(|err| format!("{err:?}"))?;
// ... check it run normally ...
if !output.status.success() {
return Err(format!(
"The program at {path:?}, compiled with LLVM, exited unsuccessfully:{output:?}"
));
}
// ... cleanup that executable ...
std::fs::remove_file(exe_path).map_err(|err| format!("{err:?}"))?;
// ... and return the output(stdout + stderr - this allows UB checks to fire).
let mut res = output.stdout;
res.extend(output.stderr);
Ok(res)
}
/// Builds & runs a file with GCC.
fn release_gcc(path: &std::path::Path) -> Result<Vec<u8>, String> {
// Build a file named `gcc_elf`...
let exe_path = path.with_extension("gcc_elf");
// ... using the GCC backend ...
let output = std::process::Command::new("./y.sh")
.arg("rustc")
.arg(path)
.arg("-O")
.arg("-o")
.arg(&exe_path)
.output()
.map_err(|err| format!("{err:?}"))?;
// ... check that the compilation succeeded ...
if !output.status.success() {
return Err(format!("GCC compilation failed:{output:?}"));
}
// ... run the resulting executable ..
let output =
std::process::Command::new(&exe_path).output().map_err(|err| format!("{err:?}"))?;
// ... check it run normally ...
if !output.status.success() {
return Err(format!(
"The program at {path:?}, compiled with GCC, exited unsuccessfully:{output:?}"
));
}
// ... cleanup that executable ...
std::fs::remove_file(exe_path).map_err(|err| format!("{err:?}"))?;
// ... and return the output(stdout + stderr - this allows UB checks to fire).
let mut res = output.stdout;
res.extend(output.stderr);
Ok(res)
}
type ResultCache = Option<(Vec<u8>, Vec<u8>)>;
/// Generates a new rustlantis file, & compares the result of running it with GCC and LLVM.
fn test(seed: u64, print_tmp_vars: bool) -> Result<Result<(), std::path::PathBuf>, String> {
// Generate a Rust source...
let source_file = generate(seed, print_tmp_vars)?;
test_file(&source_file, true)
}
/// Tests a file with a cached LLVM result. Used for reduction, when it is known
/// that a given transformation should not change the execution result.
fn test_cached(
source_file: &Path,
remove_tmps: bool,
cache: &mut ResultCache,
) -> Result<Result<(), std::path::PathBuf>, String> {
// Test `source_file` with release GCC ...
let gcc_res = release_gcc(source_file)?;
if cache.is_none() {
// ...test `source_file` with debug LLVM ...
*cache = Some((debug_llvm(source_file)?, gcc_res.clone()));
}
let (llvm_res, old_gcc) = cache.as_ref().unwrap();
// ... compare the results ...
if *llvm_res != gcc_res && gcc_res == *old_gcc {
// .. if they don't match, report an error.
Ok(Err(source_file.to_path_buf()))
} else {
if remove_tmps {
std::fs::remove_file(source_file).map_err(|err| format!("{err:?}"))?;
}
Ok(Ok(()))
}
}
fn test_file(
source_file: &Path,
remove_tmps: bool,
) -> Result<Result<(), std::path::PathBuf>, String> {
let mut uncached = None;
test_cached(source_file, remove_tmps, &mut uncached)
}
/// Generates a new rustlantis file for us to run tests on.
fn generate(seed: u64, print_tmp_vars: bool) -> Result<std::path::PathBuf, String> {
use std::io::Write;
let mut out_path = std::env::temp_dir();
out_path.push(format!("fuzz{seed}.rs"));
// We need to get the command output here.
let mut generate = std::process::Command::new("cargo");
generate
.args(["run", "--release", "--bin", "generate"])
.arg(format!("{seed}"))
.current_dir("clones/rustlantis");
if print_tmp_vars {
generate.arg("--debug");
}
let out = generate.output().map_err(|err| format!("{err:?}"))?;
// Stuff the rustlantis output in a source file.
std::fs::File::create(&out_path)
.map_err(|err| format!("{err:?}"))?
.write_all(&out.stdout)
.map_err(|err| format!("{err:?}"))?;
Ok(out_path)
}

View file

@ -0,0 +1,432 @@
use std::io::Write;
use std::path::{Path, PathBuf};
use super::ResultCache;
/// Saves a reduced file for a given `stage`
fn save_reduction(lines: &[String], path: &Path, stage: &str) {
let mut path = path.to_path_buf();
path.set_extension(format!("rs.{stage}"));
let mut file = std::fs::File::create(&path).expect("Could not create the reduced example file");
for line in lines {
file.write_all(line.as_bytes()).expect("Could not save the reduced example");
}
}
/// Checks if a given reduction is valid.
fn test_reduction(lines: &[String], path: &Path, cache: &mut ResultCache) -> bool {
let mut path = path.to_path_buf();
path.set_extension("rs_reduced");
let mut file = std::fs::File::create(&path).expect("Could not create the reduced example file");
for line in lines {
file.write_all(line.as_bytes()).expect("Could not save the reduced example");
}
let res = super::test_cached(&path, false, cache);
let Ok(Err(_)) = res else {
return false;
};
true
}
/// Removes duplicate assignments in bulk.
/// If a line A = B is followed directly by A = C,
/// then removing the first line ought to be fully sound,
/// and not change the behaviour of the program at all. Detect & remove such lines.
fn remove_dup_assign(
file: &mut Vec<String>,
path: &PathBuf,
starts: usize,
ends: usize,
cache: &mut ResultCache,
) {
let mut file_copy = file.clone();
let mut reduction_count = 0;
// Not worth it.
if ends - starts < 8 {
return;
}
for index in starts..ends {
let Some((prefix, _)) = file_copy[index].split_once('=') else {
continue;
};
let Some((prefix2, postifx2)) = file_copy[index + 1].split_once('=') else {
continue;
};
let prefix = prefix.trim();
let prefix2 = prefix2.trim();
// FIXME: Right now, remove_dup_assign cares about assignments to the exact same place.
// However, given an assigemnt like this:
// ```
// A.0 = 1_u32;
// A = (2_u32, 3.0);
// ```
// The first assignment could be safely omitted.
// Additionally, we try to check if the second assignment could depend on the first one.
// In such cases, the result is likely to change, so we bail.
if prefix == prefix2 && !postifx2.contains(prefix) {
file_copy[index] = "".into();
reduction_count += 1;
}
}
// We have removed no lines - no point in testing.
if reduction_count == 0 {
return;
}
// Check if the removed lines affected the execution result in any way, shape or form.
if test_reduction(&file_copy, path, cache) {
println!("Reduced {path:?} by {reduction_count} lines `remove_dup_assign`");
*file = file_copy;
} else {
// The execution result changed.
// This can occur if the second assignment depended on the first one.
// Eg.
// ```
// a = b + c;
// a = a + d;
// ```
remove_dup_assign(file, path, starts, (starts + ends) / 2, cache);
remove_dup_assign(file, path, (starts + ends) / 2, ends, cache);
}
save_reduction(file, path, "remove_dup_assign");
}
/// Removes all the unneeded calls to `dump_var`. This is not something tools like `cvise` can do,
/// but it greately speeds up MIR interpretation + native execution.
fn remove_dump_var(file: &mut Vec<String>, path: &PathBuf) {
let mut curr = 0;
// ... try disabling `dump_vars` one by one, until only the necessary ones are left.
while curr < file.len() {
let Some(line) = file[curr..].iter().position(|line| line.contains("dump_var")) else {
// No more `dump_var`s to remove - exit early.
break;
};
// Make the line absolute again.
let line = line + curr;
let mut file_copy = file.clone();
// Try removing 3 consecutive lines(the call, block end and block beginning). This effectively removes a `dump_var`.
file_copy.remove(line);
file_copy.remove(line);
file_copy.remove(line);
// Not cached - the execution result can change.
let mut uncached = None;
// Check if this reduction is valid.
if test_reduction(&file_copy, path, &mut uncached) {
println!("Reduced {path:?} by 3 lines `remove_dump_var`");
*file = file_copy;
curr = line;
} else {
curr = line + 1;
}
}
save_reduction(file, path, "remove_dump_var");
}
/// Replaces matches with gotos where possible.
/// This exploits some properties of rustlantis(match arm order),
/// and is only soundly applicable to MIR generated by it.
/// Still, it is not something `cvise` can do, but it simplifies the code a ton.
fn match_to_goto(file: &mut Vec<String>, path: &PathBuf, cache: &mut ResultCache) {
let mut curr = 0;
while curr < file.len() {
let Some(match_starts) = file[curr..].iter().position(|line| line.contains("match")) else {
// No more `match`es to remove - exit early.
break;
};
let match_starts = match_starts + curr;
// Find the end of the match
let Some(match_ends) = file[match_starts..].iter().position(|line| line.contains('}'))
else {
// Can't find match end - exit early.
break;
};
let match_ends = match_ends + match_starts;
let match_body = &file[match_starts..match_ends];
// Find where this match should normally jump to.
// This *should* be the second-last arm of the match, as per the paper(the remaining blocks are decoys).
// If this ever changes, this reduction may not always be sound.
// This is not a problem, however: we NEED to use MIRI for reduction anwyway,
// and it will catch this issue.
let jumps_to = &match_body[match_body.len() - 2].trim();
let Some((_, bb_ident)) = jumps_to.split_once("bb") else {
break;
};
// We now have the number of the block we jump to at runtime.
let bb_ident = bb_ident.trim_matches(',');
// Try replacing this match with an unconditional jump.
let mut file_copy = file.clone();
for _ in match_starts..(match_ends + 1) {
file_copy.remove(match_starts);
}
file_copy.insert(match_starts, format!("Goto(bb{bb_ident})\n"));
if test_reduction(&file_copy, path, cache) {
println!("Reduced {path:?} by {} lines `match_to_goto`", match_ends - match_starts);
*file = file_copy;
curr = match_starts;
} else {
curr = match_ends;
}
}
save_reduction(file, path, "match_to_goto");
}
/// At this point, we can try "killing" blocks, by replacing their bodies with calls to `abort`.
/// This is always sound(the program aborts, so no UB can occur after the block),
/// and allows us to safely remove *a lot* of unneeded blocks.
fn block_abort(file: &mut Vec<String>, path: &PathBuf, cache: &mut ResultCache) {
let mut curr = 0;
while curr < file.len() {
let Some(block_starts) = file[curr..]
.iter()
.position(|line| line.starts_with("bb") && line.trim_end().ends_with(" = {"))
else {
// No more `block`s to kill - exit early.
break;
};
let block_starts = block_starts + curr;
// Find the beginning of the next block to find the end of this block.
let Some(block_ends) = file[(block_starts + 1)..]
.iter()
.position(|line| line.starts_with("bb") && line.trim_end().ends_with(" = {"))
else {
// No more `block`s to kill - exit early.
break;
};
let block_ends = block_starts + block_ends;
let block_starts = block_starts + 1;
let mut file_copy = file.clone();
// Remove the block body...
for _ in block_starts..(block_ends) {
file_copy.remove(block_starts);
}
// ..and insert an unconditional call to abort.
file_copy.insert(
block_starts,
"Call(tmp = core::intrinsics::abort(), ReturnTo(bb1), UnwindUnreachable())\n"
.to_string(),
);
file_copy.insert(block_starts, "let tmp = ();\n".to_string());
if test_reduction(&file_copy, path, cache) {
println!("Reduced {path:?} by {} lines `block_abort`", block_ends - block_starts - 2);
*file = file_copy;
curr = block_starts;
} else {
curr = block_ends;
}
}
save_reduction(file, path, "block_abort");
}
/// Removes unreachable basic blocks
fn remove_block(file: &mut Vec<String>, path: &PathBuf, cache: &mut ResultCache) {
let mut curr = 0;
// Next, we try to outright remove blocks.
while curr < file.len() {
let Some(block_starts) = file[curr..]
.iter()
.position(|line| line.starts_with("bb") && line.trim_end().ends_with(" = {"))
else {
// No more `block`s to remove - exit early.
break;
};
let block_starts = block_starts + curr;
// Find the beginning of the next block to find the end of this block.
let Some(block_ends) = file[(block_starts + 1)..]
.iter()
.position(|line| line.starts_with("bb") && line.trim_end().ends_with(" = {"))
else {
// No more `block`s to remove - exit early.
break;
};
let block_ends = block_starts + block_ends + 1;
// Large blocks are likely to be necessary.
if block_ends - block_starts > 6 {
curr = block_starts + 1;
continue;
}
let mut file_copy = file.clone();
file_copy.drain(block_starts..block_ends);
if test_reduction(&file_copy, path, cache) {
println!("Reduced {path:?} by {} lines `remove_blocks`", block_ends - block_starts);
*file = file_copy;
curr = block_starts;
} else {
curr = block_starts + 1;
}
}
save_reduction(file, path, "remove_block");
}
/// Merges blocks ending with unconditional jumps.
fn linearize_cf(file: &mut Vec<String>, path: &PathBuf, cache: &mut ResultCache) {
let mut curr = 0;
// Next, we try to linearize the control flow. What the does that mean?
// Given a sequence like this:
// Goto(bb22)
// }
// bb22 = {
// We remove those 3 lines, merging the blocks together. This is not something `cvise` can do,
// and it makes other transformations easier.
while curr < file.len() {
let Some(block_starts) = file[curr..]
.iter()
.position(|line| line.starts_with("bb") && line.trim_end().ends_with(" = {"))
else {
// No more `block`s to remove - exit early.
break;
};
let block_starts = block_starts + curr;
// Extract the block id.
let Some((block, _)) = file[block_starts].split_once('=') else {
curr = block_starts + 1;
continue;
};
let block = block.trim();
if file[block_starts - 2].trim() != format!("Goto({block})") {
curr = block_starts + 1;
continue;
}
let mut file_copy = file.clone();
// Try removing 3 consecutive lines(the goto, block end and block beginning). This effectively removes a `Goto(next)`.
file_copy.remove(block_starts - 2);
file_copy.remove(block_starts - 2);
file_copy.remove(block_starts - 2);
// Check if this reduction is valid.
if test_reduction(&file_copy, path, cache) {
println!("Reduced {path:?} by 3 lines `linearize_cf`");
*file = file_copy;
curr = block_starts;
} else {
curr = block_starts + 1;
}
}
save_reduction(file, path, "linearize_cf");
}
/// Replaces a call to a given function with a 0 assignment to the destination place, and a Goto.
/// This is always sound, because:
/// 1. All the functions arguments are always initialized
/// 2. and point to initialized memory(the operand of &raw must be an initialized place in rustlantis).
fn remove_fn_calls(file: &mut Vec<String>, path: &PathBuf, cache: &mut ResultCache) {
let mut curr = 0;
while curr < file.len() {
let Some(fn_call) =
file[curr..].iter().position(|line| line.contains("Call(") && line.contains(" = fn"))
else {
// No more calls to remove - exit early.
break;
};
let fn_call = fn_call + curr;
let line = file[fn_call].trim();
// Skip the Call(
let line = &line["Call(".len()..];
// Extract the destination place
let Some((place, line)) = line.split_once('=') else {
curr = fn_call + 1;
continue;
};
// Skip till the return block id.
let Some((_, line)) = line.split_once("ReturnTo(") else {
curr = fn_call + 1;
continue;
};
// Extract the full return block
let Some((block, _)) = line.split_once(')') else {
curr = fn_call + 1;
continue;
};
let mut file_copy = file.clone();
// Remove the call.
file_copy.remove(fn_call);
file_copy.insert(fn_call, format!("Goto({block})\n"));
file_copy.insert(fn_call, format!("{place} = 0;\n"));
// Check if this reduction is valid.
if test_reduction(&file_copy, path, cache) {
println!("Reduced {path:?} using `remove_fn_calls` {cache:?}");
*file = file_copy;
curr = fn_call;
} else {
curr = fn_call + 1;
}
}
save_reduction(file, path, "remove_fn_calls");
}
/// Fully removes unreachable functions.
fn remove_fns(file: &mut Vec<String>, path: &PathBuf, cache: &mut ResultCache) {
let mut curr = 0;
while curr < file.len() {
// Find a function start
let Some(fn_start) = file[curr..].iter().position(|line| {
line.contains("#[custom_mir(dialect = \"runtime\", phase = \"initial\")]")
}) else {
// No more functions to remove - exit early.
break;
};
// Find the next function(and use that to find the end of this one).
// FIXME: this check is flawed: it will never remove the very last function(the one before main).
// The other checks will turn that function into a single call to abort, but it is still annoying that it is kept.
let fn_start = fn_start + curr;
let Some(fn_end) = file[(fn_start + 3)..].iter().position(|line| line.contains("fn fn"))
else {
// No more functions to remove - exit early.
break;
};
let fn_end = fn_start + 2 + fn_end;
let mut file_copy = file.clone();
// Remove the function.\\
file_copy.drain(fn_start..fn_end);
// Check if this reduction is valid.
if test_reduction(&file_copy, path, cache) {
println!("Reduced {path:?} by {} lines `remove_fns`", fn_end - fn_start);
*file = file_copy;
} else {
curr = fn_start + 1;
}
}
save_reduction(file, path, "remove_fns");
}
pub(super) fn reduce(path: impl AsRef<Path>) {
let path = path.as_ref().to_owned();
// ... read the file to a buffer ..
let file = std::fs::read_to_string(&path).expect("Could not open the file to reduce");
let mut file: Vec<_> = file.split_inclusive('\n').map(|s| s.to_string()).collect();
// ... and run reduction passes.
println!("running `remove_dump_var` on {path:?}.");
remove_dump_var(&mut file, &path);
// After `dump_var`, the execution results ought not to change. Cache them.
let mut cache = None;
// Fill the cache
assert!(
test_reduction(&file, &path, &mut cache),
"Reduction error: check that the input file is a valid reproducer."
);
println!("cache:{cache:?}");
println!("running `remove_fn_calls` on {path:?}.");
remove_fn_calls(&mut file, &path, &mut cache);
println!("running `remove_fns` on {path:?}.");
remove_fns(&mut file, &path, &mut cache);
let len = file.len();
println!("running `remove_dup_assign` on {path:?}.");
remove_dup_assign(&mut file, &path, 0, len, &mut cache);
file.retain(|line| !line.is_empty());
println!("running `match_to_goto` on {path:?}.");
match_to_goto(&mut file, &path, &mut cache);
println!("running `block_abort` on {path:?}.");
block_abort(&mut file, &path, &mut cache);
println!("running `remove_block` on {path:?}.");
remove_block(&mut file, &path, &mut cache);
println!("running `linearize_cf` on {path:?}.");
linearize_cf(&mut file, &path, &mut cache);
let mut out = std::fs::File::create(&path).expect("Could not save the reduction result.");
let file = file.into_iter().collect::<String>();
out.write_all(file.as_bytes()).expect("failed to write into file");
}

View file

@ -15,7 +15,7 @@ pub fn run() -> Result<(), String> {
config.no_download = true;
config.setup_gcc_path()?;
if let Some(gcc_path) = config.gcc_path {
println!("{}", gcc_path);
println!("{gcc_path}");
}
Ok(())
}

View file

@ -5,6 +5,7 @@ mod clean;
mod clone_gcc;
mod config;
mod fmt;
mod fuzz;
mod info;
mod prepare;
mod rust_tools;
@ -42,7 +43,8 @@ Commands:
test : Runs tests for the project.
info : Displays information about the build environment and project configuration.
clone-gcc : Clones the GCC compiler from a specified source.
fmt : Runs rustfmt"
fmt : Runs rustfmt
fuzz : Fuzzes `cg_gcc` using rustlantis"
);
}
@ -56,6 +58,7 @@ pub enum Command {
Test,
Info,
Fmt,
Fuzz,
}
fn main() {
@ -75,6 +78,7 @@ fn main() {
Some("info") => Command::Info,
Some("clone-gcc") => Command::CloneGcc,
Some("fmt") => Command::Fmt,
Some("fuzz") => Command::Fuzz,
Some("--help") => {
usage();
process::exit(0);
@ -97,6 +101,7 @@ fn main() {
Command::Info => info::run(),
Command::CloneGcc => clone_gcc::run(),
Command::Fmt => fmt::run(),
Command::Fuzz => fuzz::run(),
} {
eprintln!("Command failed to run: {e}");
process::exit(1);

View file

@ -18,9 +18,9 @@ fn prepare_libcore(
if let Some(path) = sysroot_source {
rustlib_dir = Path::new(&path)
.canonicalize()
.map_err(|error| format!("Failed to canonicalize path: {:?}", error))?;
.map_err(|error| format!("Failed to canonicalize path: {error:?}"))?;
if !rustlib_dir.is_dir() {
return Err(format!("Custom sysroot path {:?} not found", rustlib_dir));
return Err(format!("Custom sysroot path {rustlib_dir:?} not found"));
}
} else {
let rustc_path = match get_rustc_path() {
@ -36,17 +36,17 @@ fn prepare_libcore(
rustlib_dir = parent
.join("../lib/rustlib/src/rust")
.canonicalize()
.map_err(|error| format!("Failed to canonicalize path: {:?}", error))?;
.map_err(|error| format!("Failed to canonicalize path: {error:?}"))?;
if !rustlib_dir.is_dir() {
return Err("Please install `rust-src` component".to_string());
}
}
let sysroot_dir = sysroot_path.join("sysroot_src");
if sysroot_dir.is_dir() {
if let Err(error) = fs::remove_dir_all(&sysroot_dir) {
return Err(format!("Failed to remove `{}`: {:?}", sysroot_dir.display(), error,));
}
if sysroot_dir.is_dir()
&& let Err(error) = fs::remove_dir_all(&sysroot_dir)
{
return Err(format!("Failed to remove `{}`: {:?}", sysroot_dir.display(), error,));
}
let sysroot_library_dir = sysroot_dir.join("library");
@ -122,7 +122,7 @@ fn prepare_rand() -> Result<(), String> {
// Apply patch for the rand crate.
let file_path = "patches/crates/0001-Remove-deny-warnings.patch";
let rand_dir = Path::new("build/rand");
println!("[GIT] apply `{}`", file_path);
println!("[GIT] apply `{file_path}`");
let path = Path::new("../..").join(file_path);
run_command_with_output(&[&"git", &"apply", &path], Some(rand_dir))?;
run_command_with_output(&[&"git", &"add", &"-A"], Some(rand_dir))?;
@ -149,7 +149,7 @@ fn clone_and_setup<F>(repo_url: &str, checkout_commit: &str, extra: Option<F>) -
where
F: Fn(&Path) -> Result<(), String>,
{
let clone_result = git_clone_root_dir(repo_url, &Path::new(crate::BUILD_DIR), false)?;
let clone_result = git_clone_root_dir(repo_url, Path::new(crate::BUILD_DIR), false)?;
if !clone_result.ran_clone {
println!("`{}` has already been cloned", clone_result.repo_name);
}

View file

@ -1,24 +1,22 @@
use std::collections::HashMap;
use std::ffi::OsStr;
#[cfg(unix)]
use std::os::unix::process::CommandExt;
use std::path::PathBuf;
use crate::config::ConfigInfo;
use crate::utils::{
get_toolchain, run_command_with_output_and_env_no_err, rustc_toolchain_version_info,
rustc_version_info,
};
use crate::utils::{get_toolchain, rustc_toolchain_version_info, rustc_version_info};
fn args(command: &str) -> Result<Option<Vec<String>>, String> {
// We skip the binary and the "cargo"/"rustc" option.
if let Some("--help") = std::env::args().skip(2).next().as_deref() {
if let Some("--help") = std::env::args().nth(2).as_deref() {
usage(command);
return Ok(None);
}
let args = std::env::args().skip(2).collect::<Vec<_>>();
if args.is_empty() {
return Err(format!(
"Expected at least one argument for `{}` subcommand, found none",
command
"Expected at least one argument for `{command}` subcommand, found none"
));
}
Ok(Some(args))
@ -27,12 +25,11 @@ fn args(command: &str) -> Result<Option<Vec<String>>, String> {
fn usage(command: &str) {
println!(
r#"
`{}` command help:
`{command}` command help:
[args] : Arguments to be passed to the cargo command
--help : Show this help
"#,
command,
)
}
@ -51,10 +48,10 @@ impl RustcTools {
// expected.
let current_dir = std::env::current_dir()
.and_then(|path| path.canonicalize())
.map_err(|error| format!("Failed to get current directory path: {:?}", error))?;
.map_err(|error| format!("Failed to get current directory path: {error:?}"))?;
let current_exe = std::env::current_exe()
.and_then(|path| path.canonicalize())
.map_err(|error| format!("Failed to get current exe path: {:?}", error))?;
.map_err(|error| format!("Failed to get current exe path: {error:?}"))?;
let mut parent_dir =
current_exe.components().map(|comp| comp.as_os_str()).collect::<Vec<_>>();
// We run this script from "build_system/target/release/y", so we need to remove these elements.
@ -68,7 +65,7 @@ impl RustcTools {
));
}
}
let parent_dir = PathBuf::from(parent_dir.join(&OsStr::new("/")));
let parent_dir = PathBuf::from(parent_dir.join(OsStr::new("/")));
std::env::set_current_dir(&parent_dir).map_err(|error| {
format!("Failed to go to `{}` folder: {:?}", parent_dir.display(), error)
})?;
@ -92,11 +89,31 @@ impl RustcTools {
std::env::set_current_dir(&current_dir).map_err(|error| {
format!("Failed to go back to `{}` folder: {:?}", current_dir.display(), error)
})?;
let toolchain = format!("+{}", toolchain);
let toolchain = format!("+{toolchain}");
Ok(Some(Self { toolchain, args, env, config }))
}
}
fn exec(input: &[&dyn AsRef<OsStr>], env: &HashMap<String, String>) -> Result<(), String> {
#[cfg(unix)]
{
// We use `exec` to call the `execvp` syscall instead of creating a new process where the
// command will be executed because very few signals can actually kill a current process,
// so if segmentation fault (SIGSEGV signal) happens and we raise to the current process,
// it will simply do nothing and we won't have the nice error message for the shell.
let error = crate::utils::get_command_inner(input, None, Some(env)).exec();
eprintln!("execvp syscall failed: {error:?}");
std::process::exit(1);
}
#[cfg(not(unix))]
{
if crate::utils::run_command_with_output_and_env_no_err(input, None, Some(env)).is_err() {
std::process::exit(1);
}
Ok(())
}
}
pub fn run_cargo() -> Result<(), String> {
let Some(mut tools) = RustcTools::new("cargo")? else { return Ok(()) };
let rustflags = tools.env.get("RUSTFLAGS").cloned().unwrap_or_default();
@ -105,11 +122,7 @@ pub fn run_cargo() -> Result<(), String> {
for arg in &tools.args {
command.push(arg);
}
if run_command_with_output_and_env_no_err(&command, None, Some(&tools.env)).is_err() {
std::process::exit(1);
}
Ok(())
exec(&command, &tools.env)
}
pub fn run_rustc() -> Result<(), String> {
@ -118,8 +131,5 @@ pub fn run_rustc() -> Result<(), String> {
for arg in &tools.args {
command.push(arg);
}
if run_command_with_output_and_env_no_err(&command, None, Some(&tools.env)).is_err() {
std::process::exit(1);
}
Ok(())
exec(&command, &tools.env)
}

View file

@ -9,8 +9,8 @@ use crate::build;
use crate::config::{Channel, ConfigInfo};
use crate::utils::{
create_dir, get_sysroot_dir, get_toolchain, git_clone, git_clone_root_dir, remove_file,
run_command, run_command_with_env, run_command_with_output_and_env, rustc_version_info,
split_args, walk_dir,
run_command, run_command_with_env, run_command_with_output, run_command_with_output_and_env,
rustc_version_info, split_args, walk_dir,
};
type Env = HashMap<String, String>;
@ -42,7 +42,7 @@ fn get_runners() -> Runners {
);
runners.insert("--extended-regex-tests", ("Run extended regex tests", extended_regex_tests));
runners.insert("--mini-tests", ("Run mini tests", mini_tests));
runners.insert("--cargo-tests", ("Run cargo tests", cargo_tests));
runners
}
@ -53,9 +53,9 @@ fn get_number_after_arg(
match args.next() {
Some(nb) if !nb.is_empty() => match usize::from_str(&nb) {
Ok(nb) => Ok(nb),
Err(_) => Err(format!("Expected a number after `{}`, found `{}`", option, nb)),
Err(_) => Err(format!("Expected a number after `{option}`, found `{nb}`")),
},
_ => Err(format!("Expected a number after `{}`, found nothing", option)),
_ => Err(format!("Expected a number after `{option}`, found nothing")),
}
}
@ -76,8 +76,8 @@ fn show_usage() {
for (option, (doc, _)) in get_runners() {
// FIXME: Instead of using the hard-coded `23` value, better to compute it instead.
let needed_spaces = 23_usize.saturating_sub(option.len());
let spaces: String = std::iter::repeat(' ').take(needed_spaces).collect();
println!(" {}{}: {}", option, spaces, doc);
let spaces: String = std::iter::repeat_n(' ', needed_spaces).collect();
println!(" {option}{spaces}: {doc}");
}
println!(" --help : Show this help");
}
@ -88,6 +88,8 @@ struct TestArg {
use_system_gcc: bool,
runners: Vec<String>,
flags: Vec<String>,
/// Additional arguments, to be passed to commands like `cargo test`.
test_args: Vec<String>,
nb_parts: Option<usize>,
current_part: Option<usize>,
sysroot_panic_abort: bool,
@ -137,13 +139,14 @@ impl TestArg {
test_arg.sysroot_features.push(feature);
}
_ => {
return Err(format!("Expected an argument after `{}`, found nothing", arg));
return Err(format!("Expected an argument after `{arg}`, found nothing"));
}
},
"--help" => {
show_usage();
return Ok(None);
}
"--" => test_arg.test_args.extend(&mut args),
x if runners.contains_key(x)
&& !test_arg.runners.iter().any(|runner| runner == x) =>
{
@ -151,7 +154,7 @@ impl TestArg {
}
arg => {
if !test_arg.config_info.parse_argument(arg, &mut args)? {
return Err(format!("Unknown option {}", arg));
return Err(format!("Unknown option {arg}"));
}
}
}
@ -189,7 +192,7 @@ fn build_if_no_backend(env: &Env, args: &TestArg) -> Result<(), String> {
command.push(&"--release");
&tmp_env
} else {
&env
env
};
for flag in args.flags.iter() {
command.push(flag);
@ -203,6 +206,33 @@ fn clean(_env: &Env, args: &TestArg) -> Result<(), String> {
create_dir(&path)
}
fn cargo_tests(test_env: &Env, test_args: &TestArg) -> Result<(), String> {
// First, we call `mini_tests` to build minicore for us. This ensures we are testing with a working `minicore`,
// and that any changes we have made affect `minicore`(since it would get rebuilt).
mini_tests(test_env, test_args)?;
// Then, we copy some of the env vars from `test_env`
// We don't want to pass things like `RUSTFLAGS`, since they contain the -Zcodegen-backend flag.
// That would force `cg_gcc` to *rebuild itself* and only then run tests, which is undesirable.
let mut env = HashMap::new();
env.insert(
"LD_LIBRARY_PATH".into(),
test_env.get("LD_LIBRARY_PATH").expect("LD_LIBRARY_PATH missing!").to_string(),
);
env.insert(
"LIBRARY_PATH".into(),
test_env.get("LIBRARY_PATH").expect("LIBRARY_PATH missing!").to_string(),
);
env.insert(
"CG_RUSTFLAGS".into(),
test_env.get("CG_RUSTFLAGS").map(|s| s.as_str()).unwrap_or("").to_string(),
);
// Pass all the default args + the user-specified ones.
let mut args: Vec<&dyn AsRef<OsStr>> = vec![&"cargo", &"test"];
args.extend(test_args.test_args.iter().map(|s| s as &dyn AsRef<OsStr>));
run_command_with_output_and_env(&args, None, Some(&env))?;
Ok(())
}
fn mini_tests(env: &Env, args: &TestArg) -> Result<(), String> {
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!("[BUILD] mini_core");
@ -222,7 +252,7 @@ fn mini_tests(env: &Env, args: &TestArg) -> Result<(), String> {
&"--target",
&args.config_info.target_triple,
]);
run_command_with_output_and_env(&command, None, Some(&env))?;
run_command_with_output_and_env(&command, None, Some(env))?;
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!("[BUILD] example");
@ -234,7 +264,7 @@ fn mini_tests(env: &Env, args: &TestArg) -> Result<(), String> {
&"--target",
&args.config_info.target_triple,
]);
run_command_with_output_and_env(&command, None, Some(&env))?;
run_command_with_output_and_env(&command, None, Some(env))?;
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!("[AOT] mini_core_hello_world");
@ -249,14 +279,14 @@ fn mini_tests(env: &Env, args: &TestArg) -> Result<(), String> {
&"--target",
&args.config_info.target_triple,
]);
run_command_with_output_and_env(&command, None, Some(&env))?;
run_command_with_output_and_env(&command, None, Some(env))?;
let command: &[&dyn AsRef<OsStr>] = &[
&Path::new(&args.config_info.cargo_target_dir).join("mini_core_hello_world"),
&"abc",
&"bcd",
];
maybe_run_command_in_vm(&command, env, args)?;
maybe_run_command_in_vm(command, env, args)?;
Ok(())
}
@ -454,22 +484,47 @@ fn setup_rustc(env: &mut Env, args: &TestArg) -> Result<PathBuf, String> {
} else {
run_command_with_output_and_env(&[&"git", &"checkout"], rust_dir, Some(env))?;
}
let mut patches = Vec::new();
walk_dir(
"patches/tests",
&mut |_| Ok(()),
&mut |file_path: &Path| {
patches.push(file_path.to_path_buf());
Ok(())
},
false,
)?;
patches.sort();
// TODO: remove duplication with prepare.rs by creating a apply_patch function in the utils
// module.
for file_path in patches {
println!("[GIT] apply `{}`", file_path.display());
let path = Path::new("../..").join(file_path);
run_command_with_output(&[&"git", &"apply", &path], rust_dir)?;
run_command_with_output(&[&"git", &"add", &"-A"], rust_dir)?;
run_command_with_output(
&[&"git", &"commit", &"--no-gpg-sign", &"-m", &format!("Patch {}", path.display())],
rust_dir,
)?;
}
let cargo = String::from_utf8(
run_command_with_env(&[&"rustup", &"which", &"cargo"], rust_dir, Some(env))?.stdout,
)
.map_err(|error| format!("Failed to retrieve cargo path: {:?}", error))
.map_err(|error| format!("Failed to retrieve cargo path: {error:?}"))
.and_then(|cargo| {
let cargo = cargo.trim().to_owned();
if cargo.is_empty() { Err(format!("`cargo` path is empty")) } else { Ok(cargo) }
if cargo.is_empty() { Err("`cargo` path is empty".to_string()) } else { Ok(cargo) }
})?;
let rustc = String::from_utf8(
run_command_with_env(&[&"rustup", &toolchain, &"which", &"rustc"], rust_dir, Some(env))?
.stdout,
)
.map_err(|error| format!("Failed to retrieve rustc path: {:?}", error))
.map_err(|error| format!("Failed to retrieve rustc path: {error:?}"))
.and_then(|rustc| {
let rustc = rustc.trim().to_owned();
if rustc.is_empty() { Err(format!("`rustc` path is empty")) } else { Ok(rustc) }
if rustc.is_empty() { Err("`rustc` path is empty".to_string()) } else { Ok(rustc) }
})?;
let llvm_filecheck = match run_command_with_env(
&[
@ -479,7 +534,8 @@ fn setup_rustc(env: &mut Env, args: &TestArg) -> Result<PathBuf, String> {
which FileCheck-11 || \
which FileCheck-12 || \
which FileCheck-13 || \
which FileCheck-14",
which FileCheck-14 || \
which FileCheck",
],
rust_dir,
Some(env),
@ -487,13 +543,15 @@ fn setup_rustc(env: &mut Env, args: &TestArg) -> Result<PathBuf, String> {
Ok(cmd) => String::from_utf8_lossy(&cmd.stdout).to_string(),
Err(_) => {
eprintln!("Failed to retrieve LLVM FileCheck, ignoring...");
// FIXME: the test tests/run-make/no-builtins-attribute will fail if we cannot find
// FileCheck.
String::new()
}
};
let file_path = rust_dir_path.join("config.toml");
std::fs::write(
&file_path,
&format!(
format!(
r#"change-id = 115898
[rust]
@ -532,7 +590,7 @@ fn asm_tests(env: &Env, args: &TestArg) -> Result<(), String> {
let codegen_backend_path = format!(
"{pwd}/target/{channel}/librustc_codegen_gcc.{dylib_ext}",
pwd = std::env::current_dir()
.map_err(|error| format!("`current_dir` failed: {:?}", error))?
.map_err(|error| format!("`current_dir` failed: {error:?}"))?
.display(),
channel = args.config_info.channel.as_str(),
dylib_ext = args.config_info.dylib_ext,
@ -587,11 +645,11 @@ where
F: Fn(&[&dyn AsRef<OsStr>], Option<&Path>, &Env) -> Result<(), String>,
{
let toolchain = get_toolchain()?;
let toolchain_arg = format!("+{}", toolchain);
let toolchain_arg = format!("+{toolchain}");
let rustc_version = String::from_utf8(
run_command_with_env(&[&args.config_info.rustc_command[0], &"-V"], cwd, Some(env))?.stdout,
)
.map_err(|error| format!("Failed to retrieve rustc version: {:?}", error))?;
.map_err(|error| format!("Failed to retrieve rustc version: {error:?}"))?;
let rustc_toolchain_version = String::from_utf8(
run_command_with_env(
&[&args.config_info.rustc_command[0], &toolchain_arg, &"-V"],
@ -600,20 +658,19 @@ where
)?
.stdout,
)
.map_err(|error| format!("Failed to retrieve rustc +toolchain version: {:?}", error))?;
.map_err(|error| format!("Failed to retrieve rustc +toolchain version: {error:?}"))?;
if rustc_version != rustc_toolchain_version {
eprintln!(
"rustc_codegen_gcc is built for `{}` but the default rustc version is `{}`.",
rustc_toolchain_version, rustc_version,
"rustc_codegen_gcc is built for `{rustc_toolchain_version}` but the default rustc version is `{rustc_version}`.",
);
eprintln!("Using `{}`.", rustc_toolchain_version);
eprintln!("Using `{rustc_toolchain_version}`.");
}
let mut env = env.clone();
let rustflags = env.get("RUSTFLAGS").cloned().unwrap_or_default();
env.insert("RUSTDOCFLAGS".to_string(), rustflags);
let mut cargo_command: Vec<&dyn AsRef<OsStr>> = vec![&"cargo", &toolchain_arg];
cargo_command.extend_from_slice(&command);
cargo_command.extend_from_slice(command);
callback(&cargo_command, cwd, &env)
}
@ -680,7 +737,15 @@ fn test_libcore(env: &Env, args: &TestArg) -> Result<(), String> {
println!("[TEST] libcore");
let path = get_sysroot_dir().join("sysroot_src/library/coretests");
let _ = remove_dir_all(path.join("target"));
run_cargo_command(&[&"test"], Some(&path), env, args)?;
// TODO(antoyo): run in release mode when we fix the failures.
// TODO(antoyo): remove the --skip f16::test_total_cmp when this issue is fixed:
// https://github.com/rust-lang/rust/issues/141503
run_cargo_command(
&[&"test", &"--", &"--skip", &"f16::test_total_cmp"],
Some(&path),
env,
args,
)?;
Ok(())
}
@ -818,7 +883,7 @@ fn contains_ui_error_patterns(file_path: &Path, keep_lto_tests: bool) -> Result<
// Tests generating errors.
let file = File::open(file_path)
.map_err(|error| format!("Failed to read `{}`: {:?}", file_path.display(), error))?;
for line in BufReader::new(file).lines().filter_map(|line| line.ok()) {
for line in BufReader::new(file).lines().map_while(Result::ok) {
let line = line.trim();
if line.is_empty() {
continue;
@ -887,7 +952,7 @@ where
if !prepare_files_callback(&rust_path)? {
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!("Keeping all {} tests", test_type);
println!("Keeping all {test_type} tests");
}
if test_type == "ui" {
@ -919,8 +984,7 @@ where
"borrowck",
"test-attrs",
]
.iter()
.any(|name| *name == dir_name)
.contains(&dir_name)
{
remove_dir_all(dir).map_err(|error| {
format!("Failed to remove folder `{}`: {:?}", dir.display(), error)
@ -975,10 +1039,7 @@ where
if nb_parts > 0 {
let current_part = args.current_part.unwrap();
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!(
"Splitting ui_test into {} parts (and running part {})",
nb_parts, current_part
);
println!("Splitting ui_test into {nb_parts} parts (and running part {current_part})");
let out = String::from_utf8(
run_command(
&[
@ -996,7 +1057,7 @@ where
)?
.stdout,
)
.map_err(|error| format!("Failed to retrieve output of find command: {:?}", error))?;
.map_err(|error| format!("Failed to retrieve output of find command: {error:?}"))?;
let mut files = out
.split('\n')
.map(|line| line.trim())
@ -1016,7 +1077,7 @@ where
}
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!("[TEST] rustc {} test suite", test_type);
println!("[TEST] rustc {test_type} test suite");
env.insert("COMPILETEST_FORCE_STAGE0".to_string(), "1".to_string());
let extra =
@ -1040,7 +1101,7 @@ where
&"always",
&"--stage",
&"0",
&format!("tests/{}", test_type),
&format!("tests/{test_type}"),
&"--compiletest-rustc-args",
&rustc_args,
],
@ -1051,19 +1112,18 @@ where
}
fn test_rustc(env: &Env, args: &TestArg) -> Result<(), String> {
//test_rustc_inner(env, args, |_| Ok(false), false, "run-make")?;
test_rustc_inner(env, args, |_| Ok(false), false, "run-make")?;
test_rustc_inner(env, args, |_| Ok(false), false, "ui")
}
fn test_failing_rustc(env: &Env, args: &TestArg) -> Result<(), String> {
let result1 = Ok(());
/*test_rustc_inner(
let result1 = test_rustc_inner(
env,
args,
retain_files_callback("tests/failing-run-make-tests.txt", "run-make"),
false,
"run-make",
)*/
);
let result2 = test_rustc_inner(
env,
@ -1084,14 +1144,13 @@ fn test_successful_rustc(env: &Env, args: &TestArg) -> Result<(), String> {
false,
"ui",
)?;
Ok(())
/*test_rustc_inner(
test_rustc_inner(
env,
args,
remove_files_callback("tests/failing-run-make-tests.txt", "run-make"),
false,
"run-make",
)*/
)
}
fn test_failing_ui_pattern_tests(env: &Env, args: &TestArg) -> Result<(), String> {
@ -1118,7 +1177,7 @@ fn retain_files_callback<'a>(
run_command(
&[
&"find",
&format!("tests/{}", test_type),
&format!("tests/{test_type}"),
&"-mindepth",
&"1",
&"-type",
@ -1137,7 +1196,7 @@ fn retain_files_callback<'a>(
run_command(
&[
&"find",
&format!("tests/{}", test_type),
&format!("tests/{test_type}"),
&"-type",
&"f",
&"-name",
@ -1152,15 +1211,12 @@ fn retain_files_callback<'a>(
}
// Putting back only the failing ones.
if let Ok(files) = std::fs::read_to_string(&file_path) {
if let Ok(files) = std::fs::read_to_string(file_path) {
for file in files.split('\n').map(|line| line.trim()).filter(|line| !line.is_empty()) {
run_command(&[&"git", &"checkout", &"--", &file], Some(&rust_path))?;
run_command(&[&"git", &"checkout", &"--", &file], Some(rust_path))?;
}
} else {
println!(
"Failed to read `{}`, not putting back failing {} tests",
file_path, test_type
);
println!("Failed to read `{file_path}`, not putting back failing {test_type} tests");
}
Ok(true)
@ -1188,8 +1244,7 @@ fn remove_files_callback<'a>(
}
} else {
println!(
"Failed to read `{}`, not putting back failing {} tests",
file_path, test_type
"Failed to read `{file_path}`, not putting back failing {test_type} tests"
);
}
} else {
@ -1202,7 +1257,7 @@ fn remove_files_callback<'a>(
remove_file(&path)?;
}
} else {
println!("Failed to read `{}`, not putting back failing ui tests", file_path);
println!("Failed to read `{file_path}`, not putting back failing ui tests");
}
}
Ok(true)
@ -1217,7 +1272,9 @@ fn run_all(env: &Env, args: &TestArg) -> Result<(), String> {
// asm_tests(env, args)?;
test_libcore(env, args)?;
extended_sysroot_tests(env, args)?;
cargo_tests(env, args)?;
test_rustc(env, args)?;
Ok(())
}

View file

@ -1,7 +1,5 @@
use std::collections::HashMap;
use std::ffi::OsStr;
#[cfg(unix)]
use std::ffi::c_int;
use std::fmt::Debug;
use std::fs;
#[cfg(unix)]
@ -9,11 +7,6 @@ use std::os::unix::process::ExitStatusExt;
use std::path::{Path, PathBuf};
use std::process::{Command, ExitStatus, Output};
#[cfg(unix)]
unsafe extern "C" {
fn raise(signal: c_int) -> c_int;
}
fn exec_command(
input: &[&dyn AsRef<OsStr>],
cwd: Option<&Path>,
@ -27,17 +20,14 @@ fn exec_command(
#[cfg(unix)]
{
if let Some(signal) = status.signal() {
unsafe {
raise(signal as _);
}
// In case the signal didn't kill the current process.
return Err(command_error(input, &cwd, format!("Process received signal {}", signal)));
return Err(command_error(input, &cwd, format!("Process received signal {signal}")));
}
}
Ok(status)
}
fn get_command_inner(
pub(crate) fn get_command_inner(
input: &[&dyn AsRef<OsStr>],
cwd: Option<&Path>,
env: Option<&HashMap<String, String>>,
@ -75,18 +65,18 @@ fn check_exit_status(
);
let input = input.iter().map(|i| i.as_ref()).collect::<Vec<&OsStr>>();
if show_err {
eprintln!("Command `{:?}` failed", input);
eprintln!("Command `{input:?}` failed");
}
if let Some(output) = output {
let stdout = String::from_utf8_lossy(&output.stdout);
if !stdout.is_empty() {
error.push_str("\n==== STDOUT ====\n");
error.push_str(&*stdout);
error.push_str(&stdout);
}
let stderr = String::from_utf8_lossy(&output.stderr);
if !stderr.is_empty() {
error.push_str("\n==== STDERR ====\n");
error.push_str(&*stderr);
error.push_str(&stderr);
}
}
Err(error)
@ -136,6 +126,7 @@ pub fn run_command_with_output_and_env(
Ok(())
}
#[cfg(not(unix))]
pub fn run_command_with_output_and_env_no_err(
input: &[&dyn AsRef<OsStr>],
cwd: Option<&Path>,
@ -242,7 +233,7 @@ pub fn get_toolchain() -> Result<String, String> {
if !line.starts_with("channel") {
return None;
}
line.split('"').skip(1).next()
line.split('"').nth(1)
})
.next()
{
@ -281,7 +272,7 @@ fn git_clone_inner(
}
fn get_repo_name(url: &str) -> String {
let repo_name = url.split('/').last().unwrap();
let repo_name = url.split('/').next_back().unwrap();
match repo_name.strip_suffix(".git") {
Some(n) => n.to_string(),
None => repo_name.to_string(),

View file

@ -77,18 +77,18 @@ fn main() {
assert_eq!(tmp as i128, -0x1234_5678_9ABC_DEF0i128);
// Check that all u/i128 <-> float casts work correctly.
let houndred_u128 = 100u128;
let houndred_i128 = 100i128;
let houndred_f32 = 100.0f32;
let houndred_f64 = 100.0f64;
assert_eq!(houndred_u128 as f32, 100.0);
assert_eq!(houndred_u128 as f64, 100.0);
assert_eq!(houndred_f32 as u128, 100);
assert_eq!(houndred_f64 as u128, 100);
assert_eq!(houndred_i128 as f32, 100.0);
assert_eq!(houndred_i128 as f64, 100.0);
assert_eq!(houndred_f32 as i128, 100);
assert_eq!(houndred_f64 as i128, 100);
let hundred_u128 = 100u128;
let hundred_i128 = 100i128;
let hundred_f32 = 100.0f32;
let hundred_f64 = 100.0f64;
assert_eq!(hundred_u128 as f32, 100.0);
assert_eq!(hundred_u128 as f64, 100.0);
assert_eq!(hundred_f32 as u128, 100);
assert_eq!(hundred_f64 as u128, 100);
assert_eq!(hundred_i128 as f32, 100.0);
assert_eq!(hundred_i128 as f64, 100.0);
assert_eq!(hundred_f32 as i128, 100);
assert_eq!(hundred_f64 as i128, 100);
let _a = 1u32 << 2u8;

View file

@ -0,0 +1,39 @@
From cdb3d407740e4f15c3746051f8ba89b8e74e99d3 Mon Sep 17 00:00:00 2001
From: None <none@example.com>
Date: Fri, 30 May 2025 13:46:22 -0400
Subject: [PATCH] Pin compiler_builtins to 0.1.160
---
library/alloc/Cargo.toml | 2 +-
library/std/Cargo.toml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml
index 9d0d957..365c9dc 100644
--- a/library/alloc/Cargo.toml
+++ b/library/alloc/Cargo.toml
@@ -16,7 +16,7 @@ bench = false
[dependencies]
core = { path = "../core", public = true }
-compiler_builtins = { version = "=0.1.159", features = ['rustc-dep-of-std'] }
+compiler_builtins = { version = "=0.1.160", features = ['rustc-dep-of-std'] }
[features]
compiler-builtins-mem = ['compiler_builtins/mem']
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index 4ff4895..31371f0 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -18,7 +18,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
panic_unwind = { path = "../panic_unwind", optional = true }
panic_abort = { path = "../panic_abort" }
core = { path = "../core", public = true }
-compiler_builtins = { version = "=0.1.159" }
+compiler_builtins = { version = "=0.1.160" }
unwind = { path = "../unwind" }
hashbrown = { version = "0.15", default-features = false, features = [
'rustc-dep-of-std',
--
2.49.0

View file

@ -0,0 +1,25 @@
From a131c69e54b5c02fe3b517e8f3ad23d4f784ffc8 Mon Sep 17 00:00:00 2001
From: Antoni Boucher <bouanto@zoho.com>
Date: Fri, 13 Jun 2025 20:25:33 -0400
Subject: [PATCH] Workaround to make a run-make test pass
---
tests/run-make/linker-warning/rmake.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/run-make/linker-warning/rmake.rs b/tests/run-make/linker-warning/rmake.rs
index bc21739fefc..0946a7e2a48 100644
--- a/tests/run-make/linker-warning/rmake.rs
+++ b/tests/run-make/linker-warning/rmake.rs
@@ -55,7 +55,7 @@ fn main() {
diff()
.expected_file("short-error.txt")
.actual_text("(linker error)", out.stderr())
- .normalize(r#"/rustc[^/]*/"#, "/rustc/")
+ .normalize(r#"/tmp/rustc[^/]*/"#, "/tmp/rustc/")
.normalize(
regex::escape(run_make_support::build_root().to_str().unwrap()),
"/build-root",
--
2.49.0

View file

@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2025-05-12"
channel = "nightly-2025-05-21"
components = ["rust-src", "rustc-dev", "llvm-tools-preview"]

View file

@ -57,7 +57,7 @@ pub(crate) unsafe fn codegen(
let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name));
let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name));
create_wrapper_function(tcx, context, &from_name, &to_name, &types, output);
create_wrapper_function(tcx, context, &from_name, Some(&to_name), &types, output);
}
}
@ -66,7 +66,7 @@ pub(crate) unsafe fn codegen(
tcx,
context,
&mangle_internal_symbol(tcx, "__rust_alloc_error_handler"),
&mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind)),
Some(&mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind))),
&[usize, usize],
None,
);
@ -81,21 +81,21 @@ pub(crate) unsafe fn codegen(
let value = context.new_rvalue_from_int(i8, value as i32);
global.global_set_initializer_rvalue(value);
let name = mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE);
let global = context.new_global(None, GlobalKind::Exported, i8, name);
#[cfg(feature = "master")]
global.add_attribute(VarAttribute::Visibility(symbol_visibility_to_gcc(
tcx.sess.default_visibility(),
)));
let value = context.new_rvalue_from_int(i8, 0);
global.global_set_initializer_rvalue(value);
create_wrapper_function(
tcx,
context,
&mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE),
None,
&[],
None,
);
}
fn create_wrapper_function(
tcx: TyCtxt<'_>,
context: &Context<'_>,
from_name: &str,
to_name: &str,
to_name: Option<&str>,
types: &[Type<'_>],
output: Option<Type<'_>>,
) {
@ -124,34 +124,40 @@ fn create_wrapper_function(
// TODO(antoyo): emit unwind tables.
}
let args: Vec<_> = types
.iter()
.enumerate()
.map(|(index, typ)| context.new_parameter(None, *typ, format!("param{}", index)))
.collect();
let callee = context.new_function(
None,
FunctionType::Extern,
output.unwrap_or(void),
&args,
to_name,
false,
);
#[cfg(feature = "master")]
callee.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden));
let block = func.new_block("entry");
let args = args
.iter()
.enumerate()
.map(|(i, _)| func.get_param(i as i32).to_rvalue())
.collect::<Vec<_>>();
let ret = context.new_call(None, callee, &args);
//llvm::LLVMSetTailCall(ret, True);
if output.is_some() {
block.end_with_return(None, ret);
if let Some(to_name) = to_name {
let args: Vec<_> = types
.iter()
.enumerate()
.map(|(index, typ)| context.new_parameter(None, *typ, format!("param{}", index)))
.collect();
let callee = context.new_function(
None,
FunctionType::Extern,
output.unwrap_or(void),
&args,
to_name,
false,
);
#[cfg(feature = "master")]
callee.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden));
let args = args
.iter()
.enumerate()
.map(|(i, _)| func.get_param(i as i32).to_rvalue())
.collect::<Vec<_>>();
let ret = context.new_call(None, callee, &args);
//llvm::LLVMSetTailCall(ret, True);
if output.is_some() {
block.end_with_return(None, ret);
} else {
block.add_eval(None, ret);
block.end_with_void_return(None);
}
} else {
assert!(output.is_none());
block.end_with_void_return(None);
}

View file

@ -1,3 +1,5 @@
// cSpell:ignoreRegExp [afkspqvwy]reg
use std::borrow::Cow;
use gccjit::{LValue, RValue, ToRValue, Type};
@ -138,7 +140,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
// `outputs.len() + inputs.len()`.
let mut labels = vec![];
// Clobbers collected from `out("explicit register") _` and `inout("expl_reg") var => _`
// Clobbers collected from `out("explicit register") _` and `inout("explicit_reg") var => _`
let mut clobbers = vec![];
// We're trying to preallocate space for the template
@ -203,7 +205,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
// is also used as an in register, do not add it to the clobbers list.
// it will be treated as a lateout register with `out_place: None`
if !late {
bug!("input registers can only be used as lateout regisers");
bug!("input registers can only be used as lateout registers");
}
("r", dummy_output_type(self.cx, reg.reg_class()))
} else {
@ -641,7 +643,8 @@ fn explicit_reg_to_gcc(reg: InlineAsmReg) -> &'static str {
},
}
}
InlineAsmReg::Arm(reg) => reg.name(),
InlineAsmReg::AArch64(reg) => reg.name(),
_ => unimplemented!(),
}
}

View file

@ -16,7 +16,7 @@ use crate::gcc_util::to_gcc_features;
/// Checks if the function `instance` is recursively inline.
/// Returns `false` if a functions is guaranteed to be non-recursive, and `true` if it *might* be recursive.
#[cfg(feature = "master")]
fn resursively_inline<'gcc, 'tcx>(
fn recursively_inline<'gcc, 'tcx>(
cx: &CodegenCx<'gcc, 'tcx>,
instance: ty::Instance<'tcx>,
) -> bool {
@ -61,7 +61,7 @@ fn inline_attr<'gcc, 'tcx>(
//
// That prevents issues steming from recursive `#[inline(always)]` at a *relatively* small cost.
// We *only* need to check all the terminators of a function marked with this attribute.
if resursively_inline(cx, instance) {
if recursively_inline(cx, instance) {
Some(FnAttribute::Inline)
} else {
Some(FnAttribute::AlwaysInline)

View file

@ -11,11 +11,12 @@
// does not remove it?
//
// TODO(antoyo): for performance, check which optimizations the C++ frontend enables.
//
// cSpell:disable
// Fix these warnings:
// /usr/bin/ld: warning: type of symbol `_RNvNvNvNtCs5JWOrf9uCus_5rayon11thread_pool19WORKER_THREAD_STATE7___getit5___KEY' changed from 1 to 6 in /tmp/ccKeUSiR.ltrans0.ltrans.o
// /usr/bin/ld: warning: type of symbol `_RNvNvNvNvNtNtNtCsAj5i4SGTR7_3std4sync4mpmc5waker17current_thread_id5DUMMY7___getit5___KEY' changed from 1 to 6 in /tmp/ccKeUSiR.ltrans0.ltrans.o
// /usr/bin/ld: warning: incremental linking of LTO and non-LTO objects; using -flinker-output=nolto-rel which will bypass whole program optimization
// cSpell:enable
use std::ffi::{CStr, CString};
use std::fs::{self, File};
use std::path::{Path, PathBuf};

View file

@ -186,6 +186,7 @@ pub(crate) fn codegen(
if fat_lto {
let lto_path = format!("{}.lto", path);
// cSpell:disable
// FIXME(antoyo): The LTO frontend generates the following warning:
// ../build_sysroot/sysroot_src/library/core/src/num/dec2flt/lemire.rs:150:15: warning: type of _ZN4core3num7dec2flt5table17POWER_OF_FIVE_12817ha449a68fb31379e4E does not match original declaration [-Wlto-type-mismatch]
// 150 | let (lo5, hi5) = POWER_OF_FIVE_128[index];
@ -193,6 +194,7 @@ pub(crate) fn codegen(
// lto1: note: _ZN4core3num7dec2flt5table17POWER_OF_FIVE_12817ha449a68fb31379e4E was previously declared here
//
// This option is to mute it to make the UI tests pass with LTO enabled.
// cSpell:enable
context.add_driver_option("-Wno-lto-type-mismatch");
// NOTE: this doesn't actually generate an executable. With the above
// flags, it combines the .o files together in another .o.

View file

@ -765,7 +765,15 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
#[cfg(feature = "master")]
match self.cx.type_kind(a_type) {
TypeKind::Half | TypeKind::Float => {
TypeKind::Half => {
let fmodf = self.context.get_builtin_function("fmodf");
let f32_type = self.type_f32();
let a = self.context.new_cast(self.location, a, f32_type);
let b = self.context.new_cast(self.location, b, f32_type);
let result = self.context.new_call(self.location, fmodf, &[a, b]);
return self.context.new_cast(self.location, result, a_type);
}
TypeKind::Float => {
let fmodf = self.context.get_builtin_function("fmodf");
return self.context.new_call(self.location, fmodf, &[a, b]);
}
@ -774,8 +782,19 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
return self.context.new_call(self.location, fmod, &[a, b]);
}
TypeKind::FP128 => {
let fmodl = self.context.get_builtin_function("fmodl");
return self.context.new_call(self.location, fmodl, &[a, b]);
let f128_type = self.type_f128();
let fmodf128 = self.context.new_function(
None,
gccjit::FunctionType::Extern,
f128_type,
&[
self.context.new_parameter(None, f128_type, "a"),
self.context.new_parameter(None, f128_type, "b"),
],
"fmodf128",
false,
);
return self.context.new_call(self.location, fmodf128, &[a, b]);
}
_ => (),
}
@ -924,7 +943,12 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
// dereference after a drop, for instance.
// FIXME(antoyo): this check that we don't call get_aligned() a second time on a type.
// Ideally, we shouldn't need to do this check.
let aligned_type = if pointee_ty == self.cx.u128_type || pointee_ty == self.cx.i128_type {
// FractalFir: the `align == self.int128_align` check ensures we *do* call `get_aligned` if
// the alignment of a `u128`/`i128` is not the one mandated by the ABI. This ensures we handle
// under-aligned loads correctly.
let aligned_type = if (pointee_ty == self.cx.u128_type || pointee_ty == self.cx.i128_type)
&& align == self.int128_align
{
pointee_ty
} else {
pointee_ty.get_aligned(align.bytes())
@ -1010,13 +1034,13 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
let b_offset = a.size(self).align_to(b.align(self).abi);
let mut load = |i, scalar: &abi::Scalar, align| {
let llptr = if i == 0 {
let ptr = if i == 0 {
place.val.llval
} else {
self.inbounds_ptradd(place.val.llval, self.const_usize(b_offset.bytes()))
};
let llty = place.layout.scalar_pair_element_gcc_type(self, i);
let load = self.load(llty, llptr, align);
let load = self.load(llty, ptr, align);
scalar_load_metadata(self, load, scalar);
if scalar.is_bool() { self.trunc(load, self.type_i1()) } else { load }
};

View file

@ -34,7 +34,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>)
unreachable!();
/*
// Create a fn pointer with the new signature.
let ptrty = fn_abi.ptr_to_gcc_type(cx);
let ptrtype = fn_abi.ptr_to_gcc_type(cx);
// This is subtle and surprising, but sometimes we have to bitcast
// the resulting fn pointer. The reason has to do with external
@ -59,7 +59,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>)
// This can occur on either a crate-local or crate-external
// reference. It also occurs when testing libcore and in some
// other weird situations. Annoying.
if cx.val_ty(func) != ptrty {
if cx.val_ty(func) != ptrtype {
// TODO(antoyo): cast the pointer.
func
}

View file

@ -9,7 +9,6 @@ use rustc_middle::mir::Mutability;
use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar};
use rustc_middle::ty::layout::LayoutOf;
use crate::consts::const_alloc_to_gcc;
use crate::context::CodegenCx;
use crate::type_of::LayoutGccExt;
@ -46,12 +45,65 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
}
pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> RValue<'gcc> {
let context = &cx.context;
let byte_type = context.new_type::<u8>();
let typ = context.new_array_type(None, byte_type, bytes.len() as u64);
let elements: Vec<_> =
bytes.iter().map(|&byte| context.new_rvalue_from_int(byte_type, byte as i32)).collect();
context.new_array_constructor(None, typ, &elements)
// Instead of always using an array of bytes, use an array of larger integers of target endianness
// if possible. This reduces the amount of `rvalues` we use, which reduces memory usage significantly.
//
// FIXME(FractalFir): Consider using `global_set_initializer` instead. Before this is done, we need to confirm that
// `global_set_initializer` is more memory efficient than the current solution.
// `global_set_initializer` calls `global_set_initializer_rvalue` under the hood - does it generate an array of rvalues,
// or is it using a more efficient representation?
match bytes.len() % 8 {
0 => {
let context = &cx.context;
let byte_type = context.new_type::<u64>();
let typ = context.new_array_type(None, byte_type, bytes.len() as u64 / 8);
let elements: Vec<_> = bytes
.chunks_exact(8)
.map(|arr| {
let arr: [u8; 8] = arr.try_into().unwrap();
context.new_rvalue_from_long(
byte_type,
// Since we are representing arbitrary byte runs as integers, we need to follow the target
// endianness.
match cx.sess().target.options.endian {
rustc_abi::Endian::Little => u64::from_le_bytes(arr) as i64,
rustc_abi::Endian::Big => u64::from_be_bytes(arr) as i64,
},
)
})
.collect();
context.new_array_constructor(None, typ, &elements)
}
4 => {
let context = &cx.context;
let byte_type = context.new_type::<u32>();
let typ = context.new_array_type(None, byte_type, bytes.len() as u64 / 4);
let elements: Vec<_> = bytes
.chunks_exact(4)
.map(|arr| {
let arr: [u8; 4] = arr.try_into().unwrap();
context.new_rvalue_from_int(
byte_type,
match cx.sess().target.options.endian {
rustc_abi::Endian::Little => u32::from_le_bytes(arr) as i32,
rustc_abi::Endian::Big => u32::from_be_bytes(arr) as i32,
},
)
})
.collect();
context.new_array_constructor(None, typ, &elements)
}
_ => {
let context = cx.context;
let byte_type = context.new_type::<u8>();
let typ = context.new_array_type(None, byte_type, bytes.len() as u64);
let elements: Vec<_> = bytes
.iter()
.map(|&byte| context.new_rvalue_from_int(byte_type, byte as i32))
.collect();
context.new_array_constructor(None, typ, &elements)
}
}
}
pub fn type_is_pointer(typ: Type<'_>) -> bool {
@ -185,14 +237,15 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> {
// FIXME(antoyo): there's some issues with using the u128 code that follows, so hard-code
// the paths for floating-point values.
if ty == self.float_type {
// TODO: Remove this code?
/*if ty == self.float_type {
return self
.context
.new_rvalue_from_double(ty, f32::from_bits(data as u32) as f64);
}
if ty == self.double_type {
return self.context.new_rvalue_from_double(ty, f64::from_bits(data as u64));
}
}*/
let value = self.const_uint_big(self.type_ix(bitsize), data);
let bytesize = layout.size(self).bytes();
@ -212,7 +265,20 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> {
let alloc_id = prov.alloc_id();
let base_addr = match self.tcx.global_alloc(alloc_id) {
GlobalAlloc::Memory(alloc) => {
let init = const_alloc_to_gcc(self, alloc);
// For ZSTs directly codegen an aligned pointer.
// This avoids generating a zero-sized constant value and actually needing a
// real address at runtime.
if alloc.inner().len() == 0 {
assert_eq!(offset.bytes(), 0);
let val = self.const_usize(alloc.inner().align.bytes());
return if matches!(layout.primitive(), Pointer(_)) {
self.context.new_cast(None, val, ty)
} else {
self.const_bitcast(val, ty)
};
}
let init = self.const_data_from_alloc(alloc);
let alloc = alloc.inner();
let value = match alloc.mutability {
Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None),
@ -234,7 +300,7 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> {
}),
)))
.unwrap_memory();
let init = const_alloc_to_gcc(self, alloc);
let init = self.const_data_from_alloc(alloc);
self.static_addr_of(init, alloc.inner().align, None)
}
GlobalAlloc::Static(def_id) => {
@ -257,7 +323,19 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> {
}
fn const_data_from_alloc(&self, alloc: ConstAllocation<'_>) -> Self::Value {
const_alloc_to_gcc(self, alloc)
// We ignore the alignment for the purpose of deduping RValues
// The alignment is not handled / used in any way by `const_alloc_to_gcc`,
// so it is OK to overwrite it here.
let mut mock_alloc = alloc.inner().clone();
mock_alloc.align = rustc_abi::Align::MAX;
// Check if the rvalue is already in the cache - if so, just return it directly.
if let Some(res) = self.const_cache.borrow().get(&mock_alloc) {
return *res;
}
// Rvalue not in the cache - convert and add it.
let res = crate::consts::const_alloc_to_gcc_uncached(self, alloc);
self.const_cache.borrow_mut().insert(mock_alloc, res);
res
}
fn const_ptr_byte_offset(&self, base_addr: Self::Value, offset: abi::Size) -> Self::Value {

View file

@ -36,18 +36,14 @@ fn set_global_alignment<'gcc, 'tcx>(
impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> {
fn static_addr_of(&self, cv: RValue<'gcc>, align: Align, kind: Option<&str>) -> RValue<'gcc> {
// TODO(antoyo): implement a proper rvalue comparison in libgccjit instead of doing the
// following:
for (value, variable) in &*self.const_globals.borrow() {
if format!("{:?}", value) == format!("{:?}", cv) {
if let Some(global_variable) = self.global_lvalues.borrow().get(variable) {
let alignment = align.bits() as i32;
if alignment > global_variable.get_alignment() {
global_variable.set_alignment(alignment);
}
if let Some(variable) = self.const_globals.borrow().get(&cv) {
if let Some(global_variable) = self.global_lvalues.borrow().get(variable) {
let alignment = align.bits() as i32;
if alignment > global_variable.get_alignment() {
global_variable.set_alignment(alignment);
}
return *variable;
}
return *variable;
}
let global_value = self.static_addr_of_mut(cv, align, kind);
#[cfg(feature = "master")]
@ -288,8 +284,10 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
global
}
}
pub fn const_alloc_to_gcc<'gcc>(
/// Converts a given const alloc to a gcc Rvalue, without any caching or deduplication.
/// YOU SHOULD NOT call this function directly - that may break the semantics of Rust.
/// Use `const_data_from_alloc` instead.
pub(crate) fn const_alloc_to_gcc_uncached<'gcc>(
cx: &CodegenCx<'gcc, '_>,
alloc: ConstAllocation<'_>,
) -> RValue<'gcc> {
@ -321,7 +319,7 @@ pub fn const_alloc_to_gcc<'gcc>(
// and we properly interpret the provenance as a relocation pointer offset.
alloc.inspect_with_uninit_and_ptr_outside_interpreter(offset..(offset + pointer_size)),
)
.expect("const_alloc_to_llvm: could not read relocation pointer")
.expect("const_alloc_to_gcc_uncached: could not read relocation pointer")
as u64;
let address_space = cx.tcx.global_alloc(alloc_id).address_space(cx);
@ -360,7 +358,7 @@ fn codegen_static_initializer<'gcc, 'tcx>(
def_id: DefId,
) -> Result<(RValue<'gcc>, ConstAllocation<'tcx>), ErrorHandled> {
let alloc = cx.tcx.eval_static_initializer(def_id)?;
Ok((const_alloc_to_gcc(cx, alloc), alloc))
Ok((cx.const_data_from_alloc(alloc), alloc))
}
fn check_and_apply_linkage<'gcc, 'tcx>(

View file

@ -1,14 +1,16 @@
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use gccjit::{
Block, CType, Context, Function, FunctionPtrType, FunctionType, LValue, Location, RValue, Type,
};
use rustc_abi::{HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx};
use rustc_abi::{Align, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx};
use rustc_codegen_ssa::base::wants_msvc_seh;
use rustc_codegen_ssa::errors as ssa_errors;
use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, MiscCodegenMethods};
use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_middle::mir::interpret::Allocation;
use rustc_middle::mir::mono::CodegenUnit;
use rustc_middle::span_bug;
use rustc_middle::ty::layout::{
@ -28,6 +30,8 @@ use crate::common::SignType;
#[cfg_attr(not(feature = "master"), allow(dead_code))]
pub struct CodegenCx<'gcc, 'tcx> {
/// A cache of converted ConstAllocs
pub const_cache: RefCell<HashMap<Allocation, RValue<'gcc>>>,
pub codegen_unit: &'tcx CodegenUnit<'tcx>,
pub context: &'gcc Context<'gcc>,
@ -129,6 +133,9 @@ pub struct CodegenCx<'gcc, 'tcx> {
#[cfg(feature = "master")]
pub cleanup_blocks: RefCell<FxHashSet<Block<'gcc>>>,
/// The alignment of a u128/i128 type.
// We cache this, since it is needed for alignment checks during loads.
pub int128_align: Align,
}
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
@ -220,6 +227,12 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
}
let mut cx = Self {
int128_align: tcx
.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(tcx.types.i128))
.expect("Can't get the layout of `i128`")
.align
.abi,
const_cache: Default::default(),
codegen_unit,
context,
current_func: RefCell::new(None),
@ -428,8 +441,8 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
// `rust_eh_personality` function, but rather we wired it up to the
// CRT's custom personality function, which forces LLVM to consider
// landing pads as "landing pads for SEH".
if let Some(llpersonality) = self.eh_personality.get() {
return llpersonality;
if let Some(personality_func) = self.eh_personality.get() {
return personality_func;
}
let tcx = self.tcx;
let func = match tcx.lang_items().eh_personality() {

View file

@ -143,6 +143,7 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
// To find a list of GCC's names, check https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> {
let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch };
// cSpell:disable
match (arch, s) {
// FIXME: seems like x87 does not exist?
("x86", "x87") => smallvec![],
@ -181,6 +182,7 @@ pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]>
("aarch64", "sve2-bitperm") => smallvec!["sve2-bitperm", "neon"],
(_, s) => smallvec![s],
}
// cSpell:enable
}
fn arch_to_gcc(name: &str) -> &str {

View file

@ -2,6 +2,8 @@
//! This module exists because some integer types are not supported on some gcc platforms, e.g.
//! 128-bit integers on 32-bit platforms and thus require to be handled manually.
// cSpell:words cmpti divti modti mulodi muloti udivti umodti
use gccjit::{BinaryOp, ComparisonOp, FunctionType, Location, RValue, ToRValue, Type, UnaryOp};
use rustc_abi::{CanonAbi, Endian, ExternAbi};
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
@ -913,9 +915,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
debug_assert!(value_type.dyncast_array().is_some());
let name_suffix = match self.type_kind(dest_typ) {
// cSpell:disable
TypeKind::Float => "tisf",
TypeKind::Double => "tidf",
TypeKind::FP128 => "tixf",
TypeKind::FP128 => "titf",
// cSpell:enable
kind => panic!("cannot cast a non-native integer to type {:?}", kind),
};
let sign = if signed { "" } else { "un" };
@ -957,8 +961,10 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
debug_assert!(dest_typ.dyncast_array().is_some());
let name_suffix = match self.type_kind(value_type) {
// cSpell:disable
TypeKind::Float => "sfti",
TypeKind::Double => "dfti",
// cSpell:enable
kind => panic!("cannot cast a {:?} to non-native integer", kind),
};
let sign = if signed { "" } else { "uns" };

File diff suppressed because it is too large Load diff

View file

@ -1012,7 +1012,7 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function
};
let func = cx.context.get_builtin_function(gcc_name);
cx.functions.borrow_mut().insert(gcc_name.to_string(), func);
return func;
func
}
#[cfg(feature = "master")]
@ -1548,10 +1548,13 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function
"llvm.x86.tcmmrlfp16ps" => "__builtin_trap",
// NOTE: this file is generated by https://github.com/GuillaumeGomez/llvmint/blob/master/generate_list.py
_ => include!("archs.rs"),
_ => map_arch_intrinsic(name),
};
let func = cx.context.get_target_builtin_function(gcc_name);
cx.functions.borrow_mut().insert(gcc_name.to_string(), func);
func
}
#[cfg(feature = "master")]
include!("archs.rs");

View file

@ -196,6 +196,95 @@ fn get_simple_function<'gcc, 'tcx>(
))
}
fn get_simple_function_f128<'gcc, 'tcx>(
cx: &CodegenCx<'gcc, 'tcx>,
name: Symbol,
) -> Option<Function<'gcc>> {
if !cx.supports_f128_type {
return None;
}
let f128_type = cx.type_f128();
let func_name = match name {
sym::ceilf128 => "ceilf128",
sym::floorf128 => "floorf128",
sym::truncf128 => "truncf128",
sym::roundf128 => "roundf128",
sym::round_ties_even_f128 => "roundevenf128",
sym::sqrtf128 => "sqrtf128",
_ => return None,
};
Some(cx.context.new_function(
None,
FunctionType::Extern,
f128_type,
&[cx.context.new_parameter(None, f128_type, "a")],
func_name,
false,
))
}
fn get_simple_function_f128_2args<'gcc, 'tcx>(
cx: &CodegenCx<'gcc, 'tcx>,
name: Symbol,
) -> Option<Function<'gcc>> {
if !cx.supports_f128_type {
return None;
}
let f128_type = cx.type_f128();
let func_name = match name {
sym::maxnumf128 => "fmaxf128",
sym::minnumf128 => "fminf128",
_ => return None,
};
Some(cx.context.new_function(
None,
FunctionType::Extern,
f128_type,
&[
cx.context.new_parameter(None, f128_type, "a"),
cx.context.new_parameter(None, f128_type, "b"),
],
func_name,
false,
))
}
fn f16_builtin<'gcc, 'tcx>(
cx: &CodegenCx<'gcc, 'tcx>,
name: Symbol,
args: &[OperandRef<'tcx, RValue<'gcc>>],
) -> RValue<'gcc> {
let f32_type = cx.type_f32();
let builtin_name = match name {
sym::ceilf16 => "__builtin_ceilf",
sym::floorf16 => "__builtin_floorf",
sym::fmaf16 => "fmaf",
sym::maxnumf16 => "__builtin_fmaxf",
sym::minnumf16 => "__builtin_fminf",
sym::powf16 => "__builtin_powf",
sym::powif16 => {
let func = cx.context.get_builtin_function("__builtin_powif");
let arg0 = cx.context.new_cast(None, args[0].immediate(), f32_type);
let args = [arg0, args[1].immediate()];
let result = cx.context.new_call(None, func, &args);
return cx.context.new_cast(None, result, cx.type_f16());
}
sym::roundf16 => "__builtin_roundf",
sym::round_ties_even_f16 => "__builtin_rintf",
sym::sqrtf16 => "__builtin_sqrtf",
sym::truncf16 => "__builtin_truncf",
_ => unreachable!(),
};
let func = cx.context.get_builtin_function(builtin_name);
let args: Vec<_> =
args.iter().map(|arg| cx.context.new_cast(None, arg.immediate(), f32_type)).collect();
let result = cx.context.new_call(None, func, &args);
cx.context.new_cast(None, result, cx.type_f16())
}
impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
fn codegen_intrinsic_call(
&mut self,
@ -211,7 +300,9 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
let fn_args = instance.args;
let simple = get_simple_intrinsic(self, name);
let simple_func = get_simple_function(self, name);
let simple_func = get_simple_function(self, name)
.or_else(|| get_simple_function_f128(self, name))
.or_else(|| get_simple_function_f128_2args(self, name));
// FIXME(tempdragon): Re-enable `clippy::suspicious_else_formatting` if the following issue is solved:
// https://github.com/rust-lang/rust-clippy/issues/12497
@ -234,17 +325,55 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
&args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
)
}
sym::fmaf16 => {
// TODO(antoyo): use the correct builtin for f16.
let func = self.cx.context.get_builtin_function("fmaf");
let args: Vec<_> = args
.iter()
.map(|arg| {
self.cx.context.new_cast(self.location, arg.immediate(), self.cx.type_f32())
})
.collect();
let result = self.cx.context.new_call(self.location, func, &args);
self.cx.context.new_cast(self.location, result, self.cx.type_f16())
sym::ceilf16
| sym::floorf16
| sym::fmaf16
| sym::maxnumf16
| sym::minnumf16
| sym::powf16
| sym::powif16
| sym::roundf16
| sym::round_ties_even_f16
| sym::sqrtf16
| sym::truncf16 => f16_builtin(self, name, args),
sym::fmaf128 => {
let f128_type = self.cx.type_f128();
let func = self.cx.context.new_function(
None,
FunctionType::Extern,
f128_type,
&[
self.cx.context.new_parameter(None, f128_type, "a"),
self.cx.context.new_parameter(None, f128_type, "b"),
self.cx.context.new_parameter(None, f128_type, "c"),
],
"fmaf128",
false,
);
self.cx.context.new_call(
self.location,
func,
&args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
)
}
sym::powif128 => {
let f128_type = self.cx.type_f128();
let func = self.cx.context.new_function(
None,
FunctionType::Extern,
f128_type,
&[
self.cx.context.new_parameter(None, f128_type, "a"),
self.cx.context.new_parameter(None, self.int_type, "b"),
],
"__powitf2",
false,
);
self.cx.context.new_call(
self.location,
func,
&args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
)
}
sym::is_val_statically_known => {
let a = args[0].immediate();
@ -526,7 +655,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
fn type_checked_load(
&mut self,
_llvtable: Self::Value,
_vtable: Self::Value,
_vtable_byte_offset: u64,
_typeid: Self::Value,
) -> Self::Value {
@ -622,23 +751,23 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
// We instead thus allocate some scratch space...
let scratch_size = cast.size(bx);
let scratch_align = cast.align(bx);
let llscratch = bx.alloca(scratch_size, scratch_align);
bx.lifetime_start(llscratch, scratch_size);
let scratch = bx.alloca(scratch_size, scratch_align);
bx.lifetime_start(scratch, scratch_size);
// ... where we first store the value...
rustc_codegen_ssa::mir::store_cast(bx, cast, val, llscratch, scratch_align);
rustc_codegen_ssa::mir::store_cast(bx, cast, val, scratch, scratch_align);
// ... and then memcpy it to the intended destination.
bx.memcpy(
dst.val.llval,
self.layout.align.abi,
llscratch,
scratch,
scratch_align,
bx.const_usize(self.layout.size.bytes()),
MemFlags::empty(),
);
bx.lifetime_end(llscratch, scratch_size);
bx.lifetime_end(scratch, scratch_size);
}
} else {
OperandValue::Immediate(val).store(bx, dst);

View file

@ -1081,7 +1081,9 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
let (_, element_ty1) = args[1].layout.ty.simd_size_and_type(bx.tcx());
let (_, element_ty2) = args[2].layout.ty.simd_size_and_type(bx.tcx());
let (pointer_count, underlying_ty) = match *element_ty1.kind() {
ty::RawPtr(p_ty, mutbl) if p_ty == in_elem && mutbl == hir::Mutability::Mut => {
ty::RawPtr(p_ty, mutability)
if p_ty == in_elem && mutability == hir::Mutability::Mut =>
{
(ptr_count(element_ty1), non_ptr(element_ty1))
}
_ => {

View file

@ -3,10 +3,12 @@
* TODO(antoyo): support #[inline] attributes.
* TODO(antoyo): support LTO (gcc's equivalent to Full LTO is -flto -flto-partition=one https://documentation.suse.com/sbp/all/html/SBP-GCC-10/index.html).
* For Thin LTO, this might be helpful:
// cspell:disable-next-line
* In gcc 4.6 -fwhopr was removed and became default with -flto. The non-whopr path can still be executed via -flto-partition=none.
* Or the new incremental LTO (https://www.phoronix.com/news/GCC-Incremental-LTO-Patches)?
*
* Maybe some missing optizations enabled by rustc's LTO is in there: https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
* Maybe some missing optimizations enabled by rustc's LTO is in there: https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
// cspell:disable-next-line
* Like -fipa-icf (should be already enabled) and maybe -fdevirtualize-at-ltrans.
* TODO: disable debug info always being emitted. Perhaps this slows down things?
*
@ -206,7 +208,7 @@ impl CodegenBackend for GccCodegenBackend {
#[cfg(not(feature = "master"))]
{
let temp_dir = TempDir::new().expect("cannot create temporary directory");
let temp_file = temp_dir.into_path().join("result.asm");
let temp_file = temp_dir.keep().join("result.asm");
let check_context = Context::default();
check_context.set_print_errors_to_stderr(false);
let _int128_ty = check_context.new_c_type(CType::UInt128t);
@ -430,10 +432,11 @@ impl WriteBackendMethods for GccCodegenBackend {
) -> Result<ModuleCodegen<Self::Module>, FatalError> {
back::write::link(cgcx, dcx, modules)
}
fn autodiff(
_cgcx: &CodegenContext<Self>,
_module: &ModuleCodegen<Self::Module>,
_diff_fncs: Vec<AutoDiffItem>,
_diff_functions: Vec<AutoDiffItem>,
_config: &ModuleConfig,
) -> Result<(), FatalError> {
unimplemented!()
@ -494,12 +497,14 @@ fn target_config(sess: &Session, target_info: &LockedTargetInfo) -> TargetConfig
return false;
}
target_info.cpu_supports(feature)
// cSpell:disable
/*
adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512fp16, avx512ifma,
avx512pf, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpopcntdq,
bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, gfni, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm,
sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, vaes, vpclmulqdq, xsave, xsavec, xsaveopt, xsaves
*/
// cSpell:enable
})
.map(Symbol::intern)
.collect()
@ -508,13 +513,16 @@ fn target_config(sess: &Session, target_info: &LockedTargetInfo) -> TargetConfig
let target_features = f(false);
let unstable_target_features = f(true);
let has_reliable_f16 = target_info.supports_target_dependent_type(CType::Float16);
let has_reliable_f128 = target_info.supports_target_dependent_type(CType::Float128);
TargetConfig {
target_features,
unstable_target_features,
// There are no known bugs with GCC support for f16 or f128
has_reliable_f16: true,
has_reliable_f16_math: true,
has_reliable_f128: true,
has_reliable_f128_math: true,
has_reliable_f16,
has_reliable_f16_math: has_reliable_f16,
has_reliable_f128,
has_reliable_f128_math: has_reliable_f128,
}
}

View file

@ -302,13 +302,13 @@ impl<'gcc, 'tcx> BaseTypeCodegenMethods for CodegenCx<'gcc, 'tcx> {
#[cfg_attr(feature = "master", allow(unused_mut))]
fn type_array(&self, ty: Type<'gcc>, mut len: u64) -> Type<'gcc> {
#[cfg(not(feature = "master"))]
if let Some(struct_type) = ty.is_struct() {
if struct_type.get_field_count() == 0 {
// NOTE: since gccjit only supports i32 for the array size and libcore's tests uses a
// size of usize::MAX in test_binary_search, we workaround this by setting the size to
// zero for ZSTs.
len = 0;
}
if let Some(struct_type) = ty.is_struct()
&& struct_type.get_field_count() == 0
{
// NOTE: since gccjit only supports i32 for the array size and libcore's tests uses a
// size of usize::MAX in test_binary_search, we workaround this by setting the size to
// zero for ZSTs.
len = 0;
}
self.context.new_array_type(None, ty, len)

View file

@ -217,7 +217,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
let ty = match *self.ty.kind() {
// NOTE: we cannot remove this match like in the LLVM codegen because the call
// to fn_ptr_backend_type handle the on-stack attribute.
// TODO(antoyo): find a less hackish way to hande the on-stack attribute.
// TODO(antoyo): find a less hackish way to handle the on-stack attribute.
ty::FnPtr(sig_tys, hdr) => cx
.fn_ptr_backend_type(cx.fn_abi_of_fn_ptr(sig_tys.with(hdr), ty::List::empty())),
_ => self.scalar_gcc_type_at(cx, scalar, Size::ZERO),

View file

@ -1,26 +1,12 @@
tests/ui/allocator/no_std-alloc-error-handler-custom.rs
tests/ui/allocator/no_std-alloc-error-handler-default.rs
tests/ui/asm/may_unwind.rs
tests/ui/functions-closures/parallel-codegen-closures.rs
tests/ui/linkage-attr/linkage1.rs
tests/ui/lto/dylib-works.rs
tests/ui/sepcomp/sepcomp-cci.rs
tests/ui/sepcomp/sepcomp-extern.rs
tests/ui/sepcomp/sepcomp-fns-backwards.rs
tests/ui/sepcomp/sepcomp-fns.rs
tests/ui/sepcomp/sepcomp-statics.rs
tests/ui/asm/x86_64/may_unwind.rs
tests/ui/panics/catch-unwind-bang.rs
tests/ui/drop/dynamic-drop-async.rs
tests/ui/cfg/cfg-panic-abort.rs
tests/ui/drop/repeat-drop.rs
tests/ui/coroutine/panic-drops-resume.rs
tests/ui/fmt/format-args-capture.rs
tests/ui/coroutine/panic-drops.rs
tests/ui/intrinsics/panic-uninitialized-zeroed.rs
tests/ui/iterators/iter-sum-overflow-debug.rs
tests/ui/iterators/iter-sum-overflow-overflow-checks.rs
tests/ui/mir/mir_calls_to_shims.rs
tests/ui/mir/mir_drop_order.rs
tests/ui/mir/mir_let_chains_drop_order.rs
tests/ui/oom_unwind.rs
@ -31,27 +17,15 @@ tests/ui/unwind-no-uwtable.rs
tests/ui/parser/unclosed-delimiter-in-dep.rs
tests/ui/consts/missing_span_in_backtrace.rs
tests/ui/drop/dynamic-drop.rs
tests/ui/issues/issue-43853.rs
tests/ui/issues/issue-47364.rs
tests/ui/macros/rfc-2011-nicer-assert-messages/assert-without-captures-does-not-create-unnecessary-code.rs
tests/ui/rfcs/rfc-1857-stabilize-drop-order/drop-order.rs
tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.rs
tests/ui/simd/issue-17170.rs
tests/ui/simd/issue-39720.rs
tests/ui/alloc-error/default-alloc-error-hook.rs
tests/ui/coroutine/panic-safe.rs
tests/ui/issues/issue-14875.rs
tests/ui/issues/issue-29948.rs
tests/ui/panics/nested_panic_caught.rs
tests/ui/process/println-with-broken-pipe.rs
tests/ui/lto/thin-lto-inlines2.rs
tests/ui/lto/weak-works.rs
tests/ui/panic-runtime/lto-abort.rs
tests/ui/lto/thin-lto-inlines.rs
tests/ui/lto/thin-lto-global-allocator.rs
tests/ui/lto/msvc-imp-present.rs
tests/ui/panic-runtime/lto-abort.rs
tests/ui/lto/lto-thin-rustc-loads-linker-plugin.rs
tests/ui/lto/all-crates.rs
tests/ui/async-await/deep-futures-are-freeze.rs
tests/ui/coroutine/resume-after-return.rs
tests/ui/simd/masked-load-store.rs
@ -59,15 +33,11 @@ tests/ui/simd/repr_packed.rs
tests/ui/async-await/in-trait/dont-project-to-specializable-projection.rs
tests/ui/consts/try-operator.rs
tests/ui/coroutine/unwind-abort-mix.rs
tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.rs
tests/ui/impl-trait/equality-in-canonical-query.rs
tests/ui/consts/issue-miri-1910.rs
tests/ui/mir/mir_heavy_promoted.rs
tests/ui/consts/const_cmp_type_id.rs
tests/ui/consts/issue-73976-monomorphic.rs
tests/ui/consts/issue-94675.rs
tests/ui/traits/const-traits/const-drop-fail.rs
tests/ui/traits/const-traits/const-drop.rs
tests/ui/runtime/on-broken-pipe/child-processes.rs
tests/ui/sanitizer/cfi/assoc-ty-lifetime-issue-123053.rs
tests/ui/sanitizer/cfi/async-closures.rs
@ -85,14 +55,9 @@ tests/ui/sanitizer/cfi/can-reveal-opaques.rs
tests/ui/sanitizer/kcfi-mangling.rs
tests/ui/statics/const_generics.rs
tests/ui/backtrace/dylib-dep.rs
tests/ui/errors/pic-linker.rs
tests/ui/delegation/fn-header.rs
tests/ui/consts/zst_no_llvm_alloc.rs
tests/ui/consts/const-eval/parse_ints.rs
tests/ui/simd/intrinsic/generic-arithmetic-pass.rs
tests/ui/simd/intrinsic/generic-as.rs
tests/ui/backtrace/backtrace.rs
tests/ui/lifetimes/tail-expr-lock-poisoning.rs
tests/ui/runtime/rt-explody-panic-payloads.rs
tests/ui/codegen/equal-pointers-unequal/as-cast/inline1.rs
tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs
@ -108,4 +73,9 @@ tests/ui/codegen/equal-pointers-unequal/strict-provenance/segfault.rs
tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs
tests/ui/simd/simd-bitmask-notpow2.rs
tests/ui/codegen/StackColoring-not-blowup-stack-issue-40883.rs
tests/ui/numbers-arithmetic/u128-as-f32.rs
tests/ui/lto/all-crates.rs
tests/ui/uninhabited/uninhabited-transparent-return-abi.rs
tests/ui/coroutine/panic-drops-resume.rs
tests/ui/coroutine/panic-drops.rs
tests/ui/coroutine/panic-safe.rs

View file

@ -57,10 +57,10 @@ pub fn main_inner(profile: Profile) {
#[cfg(not(feature = "master"))]
fn filter(filename: &Path) -> bool {
if let Some(filename) = filename.to_str() {
if filename.ends_with("gep.rs") {
return false;
}
if let Some(filename) = filename.to_str()
&& filename.ends_with("gep.rs")
{
return false;
}
rust_filter(filename)
}

View file

@ -0,0 +1,31 @@
// Compiler:
//
// Run-time:
// status: 0
#![feature(no_core)]
#![no_std]
#![no_core]
#![no_main]
extern crate mini_core;
use intrinsics::black_box;
use mini_core::*;
#[repr(packed(1))]
pub struct ScalarInt {
data: u128,
size: u8,
}
#[inline(never)]
#[no_mangle]
fn read_data(a: &ScalarInt) {
black_box(a.data);
}
#[no_mangle]
extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 {
let data =
[black_box(ScalarInt { data: 0, size: 1 }), black_box(ScalarInt { data: 0, size: 1 })];
read_data(&data[1]);
0
}

View file

@ -0,0 +1,2 @@
lateout
repr

View file

@ -0,0 +1,75 @@
aapcs
addo
archs
ashl
ashr
cgcx
clzll
cmse
codegened
csky
ctlz
ctpop
cttz
ctzll
flto
fmaximumf
fmuladd
fmuladdf
fminimumf
fmul
fptosi
fptosui
fptoui
fwrapv
gimple
hrtb
immediates
liblto
llbb
llcx
llextra
llfn
lgcc
llmod
llresult
llret
ltrans
llty
llval
llvals
loong
lshr
masm
maximumf
maxnumf
mavx
mcmodel
minimumf
minnumf
monomorphization
monomorphizations
monomorphized
monomorphizing
movnt
mulo
nvptx
pointee
powitf
reassoc
riscv
rlib
roundevenf
rustc
sitofp
sizet
spir
subo
sysv
tbaa
uitofp
unord
uninlined
utrunc
xabort
zext

View file

@ -168,25 +168,39 @@ def update_intrinsics(llvm_path, llvmint, llvmint2):
os.path.dirname(os.path.abspath(__file__)),
"../src/intrinsic/archs.rs",
)
# A hashmap of all architectures. This allows us to first match on the architecture, and then on the intrinsics.
# This speeds up the comparison, and makes our code considerably smaller.
# Since all intrinsic names start with "llvm.", we skip that prefix.
print("Updating content of `{}`...".format(output_file))
with open(output_file, "w", encoding="utf8") as out:
out.write("// File generated by `rustc_codegen_gcc/tools/generate_intrinsics.py`\n")
out.write("// DO NOT EDIT IT!\n")
out.write("match name {\n")
out.write("/// Translate a given LLVM intrinsic name to an equivalent GCC one.\n")
out.write("fn map_arch_intrinsic(name:&str)->&str{\n")
out.write('let Some(name) = name.strip_prefix("llvm.") else { unimplemented!("***** unsupported LLVM intrinsic {}", name) };\n')
out.write('let Some((arch, name)) = name.split_once(\'.\') else { unimplemented!("***** unsupported LLVM intrinsic {}", name) };\n')
out.write("match arch {\n")
for arch in archs:
if len(intrinsics[arch]) == 0:
continue
out.write("\"{}\" => {{ #[allow(non_snake_case)] fn {}(name: &str) -> &str {{ match name {{".format(arch,arch))
intrinsics[arch].sort(key=lambda x: (x[0], x[2]))
out.write(' // {}\n'.format(arch))
for entry in intrinsics[arch]:
llvm_name = entry[0].removeprefix("llvm.");
llvm_name = llvm_name.removeprefix(arch);
llvm_name = llvm_name.removeprefix(".");
if entry[2] is True: # if it is a duplicate
out.write(' // [DUPLICATE]: "{}" => "{}",\n'.format(entry[0], entry[1]))
out.write(' // [DUPLICATE]: "{}" => "{}",\n'.format(llvm_name, entry[1]))
elif "_round_mask" in entry[1]:
out.write(' // [INVALID CONVERSION]: "{}" => "{}",\n'.format(entry[0], entry[1]))
out.write(' // [INVALID CONVERSION]: "{}" => "{}",\n'.format(llvm_name, entry[1]))
else:
out.write(' "{}" => "{}",\n'.format(entry[0], entry[1]))
out.write(' _ => unimplemented!("***** unsupported LLVM intrinsic {}", name),\n')
out.write("}\n")
out.write(' "{}" => "{}",\n'.format(llvm_name, entry[1]))
out.write(' _ => unimplemented!("***** unsupported LLVM intrinsic {}", name),\n')
out.write("}} }} {}(name) }}\n,".format(arch))
out.write(' _ => unimplemented!("***** unsupported LLVM architecture {}", name),\n')
out.write("}\n}")
subprocess.call(["rustfmt", output_file])
print("Done!")

View file

@ -57,7 +57,7 @@ pub(crate) unsafe fn codegen(
let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name));
let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name));
create_wrapper_function(tcx, &cx, &from_name, &to_name, &args, output, false);
create_wrapper_function(tcx, &cx, &from_name, Some(&to_name), &args, output, false);
}
}
@ -66,7 +66,7 @@ pub(crate) unsafe fn codegen(
tcx,
&cx,
&mangle_internal_symbol(tcx, "__rust_alloc_error_handler"),
&mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind)),
Some(&mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind))),
&[usize, usize], // size, align
None,
true,
@ -81,11 +81,16 @@ pub(crate) unsafe fn codegen(
let llval = llvm::LLVMConstInt(i8, val as u64, False);
llvm::set_initializer(ll_g, llval);
let name = mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE);
let ll_g = cx.declare_global(&name, i8);
llvm::set_visibility(ll_g, llvm::Visibility::from_generic(tcx.sess.default_visibility()));
let llval = llvm::LLVMConstInt(i8, 0, False);
llvm::set_initializer(ll_g, llval);
// __rust_no_alloc_shim_is_unstable_v2
create_wrapper_function(
tcx,
&cx,
&mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE),
None,
&[],
None,
false,
);
}
if tcx.sess.opts.debuginfo != DebugInfo::None {
@ -99,7 +104,7 @@ fn create_wrapper_function(
tcx: TyCtxt<'_>,
cx: &SimpleCx<'_>,
from_name: &str,
to_name: &str,
to_name: Option<&str>,
args: &[&Type],
output: Option<&Type>,
no_return: bool,
@ -128,33 +133,38 @@ fn create_wrapper_function(
attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]);
}
let callee = declare_simple_fn(
&cx,
to_name,
llvm::CallConv::CCallConv,
llvm::UnnamedAddr::Global,
llvm::Visibility::Hidden,
ty,
);
if let Some(no_return) = no_return {
// -> ! DIFlagNoReturn
attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]);
}
llvm::set_visibility(callee, llvm::Visibility::Hidden);
let llbb = unsafe { llvm::LLVMAppendBasicBlockInContext(cx.llcx, llfn, c"entry".as_ptr()) };
let mut bx = SBuilder::build(&cx, llbb);
let args = args
.iter()
.enumerate()
.map(|(i, _)| llvm::get_param(llfn, i as c_uint))
.collect::<Vec<_>>();
let ret = bx.call(ty, callee, &args, None);
llvm::LLVMSetTailCall(ret, True);
if output.is_some() {
bx.ret(ret);
if let Some(to_name) = to_name {
let callee = declare_simple_fn(
&cx,
to_name,
llvm::CallConv::CCallConv,
llvm::UnnamedAddr::Global,
llvm::Visibility::Hidden,
ty,
);
if let Some(no_return) = no_return {
// -> ! DIFlagNoReturn
attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]);
}
llvm::set_visibility(callee, llvm::Visibility::Hidden);
let args = args
.iter()
.enumerate()
.map(|(i, _)| llvm::get_param(llfn, i as c_uint))
.collect::<Vec<_>>();
let ret = bx.call(ty, callee, &args, None);
llvm::LLVMSetTailCall(ret, True);
if output.is_some() {
bx.ret(ret);
} else {
bx.ret_void()
}
} else {
assert!(output.is_none());
bx.ret_void()
}
}

View file

@ -6,13 +6,11 @@ edition = "2024"
[dependencies]
# tidy-alphabetical-start
ar_archive_writer = "0.4.2"
arrayvec = { version = "0.7", default-features = false }
bitflags = "2.4.1"
bstr = "1.11.3"
# Pinned so `cargo update` bumps don't cause breakage. Please also update the
# `cc` in `rustc_llvm` if you update the `cc` here.
cc = "=1.2.16"
either = "1.5.0"
itertools = "0.12"
pathdiff = "0.2.0"
regex = "1.4"

View file

@ -219,6 +219,7 @@ fn exported_symbols_provider_local<'tcx>(
.chain([
mangle_internal_symbol(tcx, "__rust_alloc_error_handler"),
mangle_internal_symbol(tcx, OomStrategy::SYMBOL),
mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE),
])
{
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name));
@ -232,19 +233,6 @@ fn exported_symbols_provider_local<'tcx>(
},
));
}
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(
tcx,
&mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE),
));
symbols.push((
exported_symbol,
SymbolExportInfo {
level: SymbolExportLevel::Rust,
kind: SymbolExportKind::Data,
used: false,
},
))
}
if tcx.sess.instrument_coverage() || tcx.sess.opts.cg.profile_generate.enabled() {

View file

@ -4,7 +4,9 @@ use rustc_abi::ExternAbi;
use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr};
use rustc_attr_data_structures::ReprAttr::ReprAlign;
use rustc_attr_data_structures::{AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr};
use rustc_attr_data_structures::{
AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, find_attr,
};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
@ -21,7 +23,6 @@ use rustc_session::parse::feature_err;
use rustc_session::{Session, lint};
use rustc_span::{Ident, Span, sym};
use rustc_target::spec::SanitizerSet;
use tracing::debug;
use crate::errors;
use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature_attr};
@ -83,7 +84,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
let rust_target_features = tcx.rust_target_features(LOCAL_CRATE);
let mut inline_span = None;
let mut link_ordinal_span = None;
let mut no_sanitize_span = None;
let mut mixed_export_name_no_mangle_lint_state = MixedExportNameAndNoMangleState::default();
@ -449,48 +449,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
mixed_export_name_no_mangle_lint_state.lint_if_mixed(tcx);
codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
if !attr.has_name(sym::inline) {
return ia;
}
if attr.is_word() {
return InlineAttr::Hint;
}
let Some(ref items) = attr.meta_item_list() else {
return ia;
};
inline_span = Some(attr.span());
let [item] = &items[..] else {
tcx.dcx().emit_err(errors::ExpectedOneArgument { span: attr.span() });
return InlineAttr::None;
};
if item.has_name(sym::always) {
InlineAttr::Always
} else if item.has_name(sym::never) {
InlineAttr::Never
} else {
tcx.dcx().emit_err(errors::InvalidArgument { span: items[0].span() });
InlineAttr::None
}
});
codegen_fn_attrs.inline = attrs.iter().fold(codegen_fn_attrs.inline, |ia, attr| {
if !attr.has_name(sym::rustc_force_inline) || !tcx.features().rustc_attrs() {
return ia;
}
if attr.is_word() {
InlineAttr::Force { attr_span: attr.span(), reason: None }
} else if let Some(val) = attr.value_str() {
InlineAttr::Force { attr_span: attr.span(), reason: Some(val) }
} else {
debug!("`rustc_force_inline` not checked by attribute validation");
ia
}
});
let inline_span;
(codegen_fn_attrs.inline, inline_span) = if let Some((inline_attr, span)) =
find_attr!(attrs, AttributeKind::Inline(i, span) => (*i, *span))
{
(inline_attr, Some(span))
} else {
(InlineAttr::None, None)
};
// naked function MUST NOT be inlined! This attribute is required for the rust compiler itself,
// but not for the code generation backend because at that point the naked function will just be
@ -511,7 +477,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
return OptimizeAttr::Default;
};
inline_span = Some(attr.span());
let [item] = &items[..] else {
tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() });
return OptimizeAttr::Default;

View file

@ -208,13 +208,6 @@ pub(crate) struct OutOfRangeInteger {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_expected_one_argument, code = E0534)]
pub(crate) struct ExpectedOneArgument {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_expected_one_argument, code = E0722)]
pub(crate) struct ExpectedOneArgumentOptimize {
@ -222,14 +215,6 @@ pub(crate) struct ExpectedOneArgumentOptimize {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_invalid_argument, code = E0535)]
#[help]
pub(crate) struct InvalidArgument {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_invalid_argument, code = E0722)]
pub(crate) struct InvalidArgumentOptimize {

View file

@ -317,7 +317,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let name = if bx.sess().fewer_names() {
None
} else {
Some(match whole_local_var.or(fallback_var.clone()) {
Some(match whole_local_var.or_else(|| fallback_var.clone()) {
Some(var) if var.name != sym::empty => var.name.to_string(),
_ => format!("{local:?}"),
})

View file

@ -205,7 +205,7 @@ fn prefix_and_suffix<'tcx>(
let mut end = String::new();
match asm_binary_format {
BinaryFormat::Elf => {
let section = link_section.unwrap_or(format!(".text.{asm_name}"));
let section = link_section.unwrap_or_else(|| format!(".text.{asm_name}"));
let progbits = match is_arm {
true => "%progbits",
@ -239,7 +239,7 @@ fn prefix_and_suffix<'tcx>(
}
}
BinaryFormat::MachO => {
let section = link_section.unwrap_or("__TEXT,__text".to_string());
let section = link_section.unwrap_or_else(|| "__TEXT,__text".to_string());
writeln!(begin, ".pushsection {},regular,pure_instructions", section).unwrap();
writeln!(begin, ".balign {align_bytes}").unwrap();
write_linkage(&mut begin).unwrap();
@ -256,7 +256,7 @@ fn prefix_and_suffix<'tcx>(
}
}
BinaryFormat::Coff => {
let section = link_section.unwrap_or(format!(".text.{asm_name}"));
let section = link_section.unwrap_or_else(|| format!(".text.{asm_name}"));
writeln!(begin, ".pushsection {},\"xr\"", section).unwrap();
writeln!(begin, ".balign {align_bytes}").unwrap();
write_linkage(&mut begin).unwrap();
@ -273,7 +273,7 @@ fn prefix_and_suffix<'tcx>(
}
}
BinaryFormat::Wasm => {
let section = link_section.unwrap_or(format!(".text.{asm_name}"));
let section = link_section.unwrap_or_else(|| format!(".text.{asm_name}"));
writeln!(begin, ".section {section},\"\",@").unwrap();
// wasm functions cannot be aligned, so skip

View file

@ -1,9 +1,9 @@
use std::fmt;
use arrayvec::ArrayVec;
use either::Either;
use rustc_abi as abi;
use rustc_abi::{Align, BackendRepr, FIRST_VARIANT, Primitive, Size, TagEncoding, Variants};
use rustc_abi::{
Align, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, TagEncoding, VariantIdx, Variants,
};
use rustc_middle::mir::interpret::{Pointer, Scalar, alloc_range};
use rustc_middle::mir::{self, ConstValue};
use rustc_middle::ty::Ty;
@ -13,6 +13,7 @@ use rustc_session::config::OptLevel;
use tracing::{debug, instrument};
use super::place::{PlaceRef, PlaceValue};
use super::rvalue::transmute_immediate;
use super::{FunctionCx, LocalRef};
use crate::common::IntPredicate;
use crate::traits::*;
@ -69,31 +70,6 @@ pub enum OperandValue<V> {
}
impl<V: CodegenObject> OperandValue<V> {
/// If this is ZeroSized/Immediate/Pair, return an array of the 0/1/2 values.
/// If this is Ref, return the place.
#[inline]
pub(crate) fn immediates_or_place(self) -> Either<ArrayVec<V, 2>, PlaceValue<V>> {
match self {
OperandValue::ZeroSized => Either::Left(ArrayVec::new()),
OperandValue::Immediate(a) => Either::Left(ArrayVec::from_iter([a])),
OperandValue::Pair(a, b) => Either::Left([a, b].into()),
OperandValue::Ref(p) => Either::Right(p),
}
}
/// Given an array of 0/1/2 immediate values, return ZeroSized/Immediate/Pair.
#[inline]
pub(crate) fn from_immediates(immediates: ArrayVec<V, 2>) -> Self {
let mut it = immediates.into_iter();
let Some(a) = it.next() else {
return OperandValue::ZeroSized;
};
let Some(b) = it.next() else {
return OperandValue::Immediate(a);
};
OperandValue::Pair(a, b)
}
/// Treat this value as a pointer and return the data pointer and
/// optional metadata as backend values.
///
@ -595,6 +571,105 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
}
}
}
/// Creates an incomplete operand containing the [`abi::Scalar`]s expected based
/// on the `layout` passed. This is for use with [`OperandRef::insert_field`]
/// later to set the necessary immediate(s).
///
/// Returns `None` for `layout`s which cannot be built this way.
pub(crate) fn builder(
layout: TyAndLayout<'tcx>,
) -> Option<OperandRef<'tcx, Result<V, abi::Scalar>>> {
let val = match layout.backend_repr {
BackendRepr::Memory { .. } if layout.is_zst() => OperandValue::ZeroSized,
BackendRepr::Scalar(s) => OperandValue::Immediate(Err(s)),
BackendRepr::ScalarPair(a, b) => OperandValue::Pair(Err(a), Err(b)),
BackendRepr::Memory { .. } | BackendRepr::SimdVector { .. } => return None,
};
Some(OperandRef { val, layout })
}
}
impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, Result<V, abi::Scalar>> {
pub(crate) fn insert_field<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
&mut self,
bx: &mut Bx,
v: VariantIdx,
f: FieldIdx,
operand: OperandRef<'tcx, V>,
) {
let (expect_zst, is_zero_offset) = if let abi::FieldsShape::Primitive = self.layout.fields {
// The other branch looking at field layouts ICEs for primitives,
// so we need to handle them separately.
// Multiple fields is possible for cases such as aggregating
// a thin pointer, where the second field is the unit.
assert!(!self.layout.is_zst());
assert_eq!(v, FIRST_VARIANT);
let first_field = f == FieldIdx::ZERO;
(!first_field, first_field)
} else {
let variant_layout = self.layout.for_variant(bx.cx(), v);
let field_layout = variant_layout.field(bx.cx(), f.as_usize());
let field_offset = variant_layout.fields.offset(f.as_usize());
(field_layout.is_zst(), field_offset == Size::ZERO)
};
let mut update = |tgt: &mut Result<V, abi::Scalar>, src, from_scalar| {
let from_bty = bx.cx().type_from_scalar(from_scalar);
let to_scalar = tgt.unwrap_err();
let to_bty = bx.cx().type_from_scalar(to_scalar);
let imm = transmute_immediate(bx, src, from_scalar, from_bty, to_scalar, to_bty);
*tgt = Ok(imm);
};
match (operand.val, operand.layout.backend_repr) {
(OperandValue::ZeroSized, _) if expect_zst => {}
(OperandValue::Immediate(v), BackendRepr::Scalar(from_scalar)) => match &mut self.val {
OperandValue::Immediate(val @ Err(_)) if is_zero_offset => {
update(val, v, from_scalar);
}
OperandValue::Pair(fst @ Err(_), _) if is_zero_offset => {
update(fst, v, from_scalar);
}
OperandValue::Pair(_, snd @ Err(_)) if !is_zero_offset => {
update(snd, v, from_scalar);
}
_ => bug!("Tried to insert {operand:?} into {v:?}.{f:?} of {self:?}"),
},
(OperandValue::Pair(a, b), BackendRepr::ScalarPair(from_sa, from_sb)) => {
match &mut self.val {
OperandValue::Pair(fst @ Err(_), snd @ Err(_)) => {
update(fst, a, from_sa);
update(snd, b, from_sb);
}
_ => bug!("Tried to insert {operand:?} into {v:?}.{f:?} of {self:?}"),
}
}
_ => bug!("Unsupported operand {operand:?} inserting into {v:?}.{f:?} of {self:?}"),
}
}
/// After having set all necessary fields, this converts the
/// `OperandValue<Result<V, _>>` (as obtained from [`OperandRef::builder`])
/// to the normal `OperandValue<V>`.
///
/// ICEs if any required fields were not set.
pub fn build(&self) -> OperandRef<'tcx, V> {
let OperandRef { val, layout } = *self;
let unwrap = |r: Result<V, abi::Scalar>| match r {
Ok(v) => v,
Err(_) => bug!("OperandRef::build called while fields are missing {self:?}"),
};
let val = match val {
OperandValue::ZeroSized => OperandValue::ZeroSized,
OperandValue::Immediate(v) => OperandValue::Immediate(unwrap(v)),
OperandValue::Pair(a, b) => OperandValue::Pair(unwrap(a), unwrap(b)),
OperandValue::Ref(_) => bug!(),
};
OperandRef { val, layout }
}
}
impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {

View file

@ -1,7 +1,6 @@
use std::assert_matches::assert_matches;
use arrayvec::ArrayVec;
use rustc_abi::{self as abi, FIRST_VARIANT, FieldIdx};
use rustc_abi::{self as abi, FIRST_VARIANT};
use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
@ -708,38 +707,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// `rvalue_creates_operand` has arranged that we only get here if
// we can build the aggregate immediate from the field immediates.
let mut inputs = ArrayVec::<Bx::Value, 2>::new();
let mut input_scalars = ArrayVec::<abi::Scalar, 2>::new();
for field_idx in layout.fields.index_by_increasing_offset() {
let field_idx = FieldIdx::from_usize(field_idx);
let op = self.codegen_operand(bx, &fields[field_idx]);
let values = op.val.immediates_or_place().left_or_else(|p| {
bug!("Field {field_idx:?} is {p:?} making {layout:?}");
});
let scalars = self.value_kind(op.layout).scalars().unwrap();
assert_eq!(values.len(), scalars.len());
inputs.extend(values);
input_scalars.extend(scalars);
let Some(mut builder) = OperandRef::builder(layout) else {
bug!("Cannot use type in operand builder: {layout:?}")
};
for (field_idx, field) in fields.iter_enumerated() {
let op = self.codegen_operand(bx, field);
builder.insert_field(bx, FIRST_VARIANT, field_idx, op);
}
let output_scalars = self.value_kind(layout).scalars().unwrap();
itertools::izip!(&mut inputs, input_scalars, output_scalars).for_each(
|(v, in_s, out_s)| {
if in_s != out_s {
// We have to be really careful about bool here, because
// `(bool,)` stays i1 but `Cell<bool>` becomes i8.
*v = bx.from_immediate(*v);
*v = bx.to_immediate_scalar(*v, out_s);
}
},
);
let val = OperandValue::from_immediates(inputs);
assert!(
val.is_expected_variant_for_type(self.cx, layout),
"Made wrong variant {val:?} for type {layout:?}",
);
OperandRef { val, layout }
builder.build()
}
mir::Rvalue::ShallowInitBox(ref operand, content_ty) => {
let operand = self.codegen_operand(bx, operand);
@ -1082,10 +1058,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
mir::AggregateKind::Coroutine(..) | mir::AggregateKind::CoroutineClosure(..) => false,
};
allowed_kind && {
let ty = rvalue.ty(self.mir, self.cx.tcx());
let ty = self.monomorphize(ty);
let ty = rvalue.ty(self.mir, self.cx.tcx());
let ty = self.monomorphize(ty);
let layout = self.cx.spanned_layout_of(ty, span);
!self.cx.is_backend_ref(layout)
OperandRef::<Bx::Value>::builder(layout).is_some()
}
}
}
@ -1129,23 +1105,12 @@ enum OperandValueKind {
ZeroSized,
}
impl OperandValueKind {
fn scalars(self) -> Option<ArrayVec<abi::Scalar, 2>> {
Some(match self {
OperandValueKind::ZeroSized => ArrayVec::new(),
OperandValueKind::Immediate(a) => ArrayVec::from_iter([a]),
OperandValueKind::Pair(a, b) => [a, b].into(),
OperandValueKind::Ref => return None,
})
}
}
/// Transmutes one of the immediates from an [`OperandValue::Immediate`]
/// or an [`OperandValue::Pair`] to an immediate of the target type.
///
/// `to_backend_ty` must be the *non*-immediate backend type (so it will be
/// `i8`, not `i1`, for `bool`-like types.)
fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
pub(super) fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
bx: &mut Bx,
mut imm: Bx::Value,
from_scalar: abi::Scalar,

View file

@ -1,4 +1,4 @@
use rustc_abi::{AddressSpace, Float, Integer, Reg};
use rustc_abi::{AddressSpace, Float, Integer, Primitive, Reg, Scalar};
use rustc_middle::bug;
use rustc_middle::ty::Ty;
use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, TyAndLayout};
@ -84,6 +84,24 @@ pub trait DerivedTypeCodegenMethods<'tcx>:
fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool {
ty.is_freeze(self.tcx(), self.typing_env())
}
fn type_from_primitive(&self, p: Primitive) -> Self::Type {
use Primitive::*;
match p {
Int(i, _) => self.type_from_integer(i),
Float(f) => self.type_from_float(f),
Pointer(address_space) => self.type_ptr_ext(address_space),
}
}
fn type_from_scalar(&self, s: Scalar) -> Self::Type {
// `MaybeUninit` being `repr(transparent)` somewhat implies that the type
// of a scalar has to be the type of its primitive (which is true in LLVM,
// where noundef is a parameter attribute or metadata) but if we ever get
// a backend where that's no longer true, every use of this will need to
// to carefully scrutinized and re-evaluated.
self.type_from_primitive(s.primitive())
}
}
impl<'tcx, T> DerivedTypeCodegenMethods<'tcx> for T where

View file

@ -1,8 +1,14 @@
#### Note: this error code is no longer emitted by the compiler
This is because it was too specific to the `inline` attribute.
Similar diagnostics occur for other attributes too.
The example here will now emit `E0805`
The `inline` attribute was malformed.
Erroneous code example:
```compile_fail,E0534
```compile_fail,E0805
#[inline()] // error: expected one argument
pub fn something() {}

View file

@ -1,8 +1,13 @@
An unknown argument was given to the `inline` attribute.
#### Note: this error code is no longer emitted by the compiler
This is because it was too specific to the `inline` attribute.
Similar diagnostics occur for other attributes too.
The example here will now emit `E0539`
Erroneous code example:
```compile_fail,E0535
```compile_fail,E0539
#[inline(unknown)] // error: invalid argument
pub fn something() {}

View file

@ -24,8 +24,7 @@ struct Stable;
const fn stable_fn() {}
```
Meta items are the key-value pairs inside of an attribute.
To fix these issues you need to give required key-value pairs.
To fix the above example, you can write the following:
```
#![feature(staged_api)]
@ -49,3 +48,29 @@ struct Stable;
#[rustc_const_stable(feature = "stable_fn", since = "1.39.0")] // ok!
const fn stable_fn() {}
```
Several causes of this are,
an attribute may have expected you to give a list but you gave a
`name = value` pair:
```compile_fail,E0539
// wrong, should be `#[repr(C)]`
#[repr = "C"]
struct Foo {}
```
Or a `name = value` pair, but you gave a list:
```compile_fail,E0539
// wrong, should be `note = "reason"`
#[deprecated(since = "1.0.0", note("reason"))]
struct Foo {}
```
Or it expected some specific word but you gave an unexpected one:
```compile_fail,E0539
// should be `always` or `never`
#[inline(maybe_if_you_feel_like_it)]
fn foo() {}
```

View file

@ -9,10 +9,9 @@ struct Repr {}
fn main() {}
```
Literals in attributes are new and largely unsupported in built-in attributes.
Work to support literals where appropriate is ongoing. Try using an unquoted
name instead:
Not all attributes support literals in their input,
and in some cases they expect an identifier instead.
That would be the solution in the case of `repr`:
```
#[repr(C)] // ok!
struct Repr {}

View file

@ -0,0 +1,26 @@
An attribute was given an invalid number of arguments
Erroneous code example:
```compile_fail,E0805
#[inline()] // error! should either have a single argument, or no parentheses
fn foo() {}
#[inline(always, never)] // error! should have only one argument, not two
fn bar() {}
```
To fix this, either give the right number of arguments the attribute needs.
In the case of inline, this could be none at all:
```
#[inline]
fn foo() {}
```
or only one:
```
#[inline(always)]
fn foo() {}
```

View file

@ -547,6 +547,7 @@ E0801: 0801,
E0802: 0802,
E0803: 0803,
E0804: 0804,
E0805: 0805,
);
)
}

View file

@ -34,6 +34,7 @@ use crate::snippet::{
Annotation, AnnotationColumn, AnnotationType, Line, MultilineAnnotation, Style, StyledString,
};
use crate::styled_buffer::StyledBuffer;
use crate::timings::TimingRecord;
use crate::translation::{Translate, to_fluent_args};
use crate::{
CodeSuggestion, DiagInner, DiagMessage, ErrCode, FluentBundle, LazyFallbackBundle, Level,
@ -164,11 +165,16 @@ impl Margin {
}
}
pub enum TimingEvent {
Start,
End,
}
const ANONYMIZED_LINE_NUM: &str = "LL";
pub type DynEmitter = dyn Emitter + DynSend;
/// Emitter trait for emitting errors.
/// Emitter trait for emitting errors and other structured information.
pub trait Emitter: Translate {
/// Emit a structured diagnostic.
fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry);
@ -177,6 +183,10 @@ pub trait Emitter: Translate {
/// Currently only supported for the JSON format.
fn emit_artifact_notification(&mut self, _path: &Path, _artifact_type: &str) {}
/// Emit a timestamp with start/end of a timing section.
/// Currently only supported for the JSON format.
fn emit_timing_section(&mut self, _record: TimingRecord, _event: TimingEvent) {}
/// Emit a report about future breakage.
/// Currently only supported for the JSON format.
fn emit_future_breakage_report(&mut self, _diags: Vec<DiagInner>, _registry: &Registry) {}

View file

@ -28,9 +28,10 @@ use termcolor::{ColorSpec, WriteColor};
use crate::diagnostic::IsLint;
use crate::emitter::{
ColorConfig, Destination, Emitter, HumanEmitter, HumanReadableErrorType, OutputTheme,
should_show_source_code,
TimingEvent, should_show_source_code,
};
use crate::registry::Registry;
use crate::timings::{TimingRecord, TimingSection};
use crate::translation::{Translate, to_fluent_args};
use crate::{
CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, Subdiag, Suggestions,
@ -104,6 +105,7 @@ impl JsonEmitter {
enum EmitTyped<'a> {
Diagnostic(Diagnostic),
Artifact(ArtifactNotification<'a>),
SectionTiming(SectionTimestamp<'a>),
FutureIncompat(FutureIncompatReport<'a>),
UnusedExtern(UnusedExterns<'a>),
}
@ -135,6 +137,21 @@ impl Emitter for JsonEmitter {
}
}
fn emit_timing_section(&mut self, record: TimingRecord, event: TimingEvent) {
let event = match event {
TimingEvent::Start => "start",
TimingEvent::End => "end",
};
let name = match record.section {
TimingSection::Linking => "link",
};
let data = SectionTimestamp { name, event, timestamp: record.timestamp };
let result = self.emit(EmitTyped::SectionTiming(data));
if let Err(e) = result {
panic!("failed to print timing section: {e:?}");
}
}
fn emit_future_breakage_report(&mut self, diags: Vec<crate::DiagInner>, registry: &Registry) {
let data: Vec<FutureBreakageItem<'_>> = diags
.into_iter()
@ -263,6 +280,16 @@ struct ArtifactNotification<'a> {
emit: &'a str,
}
#[derive(Serialize)]
struct SectionTimestamp<'a> {
/// Name of the section
name: &'a str,
/// Start/end of the section
event: &'a str,
/// Opaque timestamp.
timestamp: u128,
}
#[derive(Serialize)]
struct FutureBreakageItem<'a> {
// Always EmitTyped::Diagnostic, but we want to make sure it gets serialized

View file

@ -7,6 +7,7 @@
#![allow(internal_features)]
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
#![cfg_attr(not(bootstrap), allow(rustc::direct_use_of_rustc_type_ir))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(array_windows)]
@ -74,7 +75,9 @@ pub use snippet::Style;
pub use termcolor::{Color, ColorSpec, WriteColor};
use tracing::debug;
use crate::emitter::TimingEvent;
use crate::registry::Registry;
use crate::timings::TimingRecord;
pub mod annotate_snippet_emitter_writer;
pub mod codes;
@ -90,6 +93,7 @@ mod snippet;
mod styled_buffer;
#[cfg(test)]
mod tests;
pub mod timings;
pub mod translation;
pub type PResult<'a, T> = Result<T, Diag<'a>>;
@ -1156,6 +1160,14 @@ impl<'a> DiagCtxtHandle<'a> {
self.inner.borrow_mut().emitter.emit_artifact_notification(path, artifact_type);
}
pub fn emit_timing_section_start(&self, record: TimingRecord) {
self.inner.borrow_mut().emitter.emit_timing_section(record, TimingEvent::Start);
}
pub fn emit_timing_section_end(&self, record: TimingRecord) {
self.inner.borrow_mut().emitter.emit_timing_section(record, TimingEvent::End);
}
pub fn emit_future_breakage_report(&self) {
let inner = &mut *self.inner.borrow_mut();
let diags = std::mem::take(&mut inner.future_breakage_diagnostics);

View file

@ -0,0 +1,80 @@
use std::time::Instant;
use crate::DiagCtxtHandle;
/// A high-level section of the compilation process.
#[derive(Copy, Clone, Debug)]
pub enum TimingSection {
/// Time spent linking.
Linking,
}
/// Section with attached timestamp
#[derive(Copy, Clone, Debug)]
pub struct TimingRecord {
pub section: TimingSection,
/// Microseconds elapsed since some predetermined point in time (~start of the rustc process).
pub timestamp: u128,
}
impl TimingRecord {
fn from_origin(origin: Instant, section: TimingSection) -> Self {
Self { section, timestamp: Instant::now().duration_since(origin).as_micros() }
}
pub fn section(&self) -> TimingSection {
self.section
}
pub fn timestamp(&self) -> u128 {
self.timestamp
}
}
/// Manages emission of start/end section timings, enabled through `--json=timings`.
pub struct TimingSectionHandler {
/// Time when the compilation session started.
/// If `None`, timing is disabled.
origin: Option<Instant>,
}
impl TimingSectionHandler {
pub fn new(enabled: bool) -> Self {
let origin = if enabled { Some(Instant::now()) } else { None };
Self { origin }
}
/// Returns a RAII guard that will immediately emit a start the provided section, and then emit
/// its end when it is dropped.
pub fn start_section<'a>(
&self,
diag_ctxt: DiagCtxtHandle<'a>,
section: TimingSection,
) -> TimingSectionGuard<'a> {
TimingSectionGuard::create(diag_ctxt, section, self.origin)
}
}
/// RAII wrapper for starting and ending section timings.
pub struct TimingSectionGuard<'a> {
dcx: DiagCtxtHandle<'a>,
section: TimingSection,
origin: Option<Instant>,
}
impl<'a> TimingSectionGuard<'a> {
fn create(dcx: DiagCtxtHandle<'a>, section: TimingSection, origin: Option<Instant>) -> Self {
if let Some(origin) = origin {
dcx.emit_timing_section_start(TimingRecord::from_origin(origin, section));
}
Self { dcx, section, origin }
}
}
impl<'a> Drop for TimingSectionGuard<'a> {
fn drop(&mut self) {
if let Some(origin) = self.origin {
self.dcx.emit_timing_section_end(TimingRecord::from_origin(origin, self.section));
}
}
}

View file

@ -220,6 +220,8 @@ declare_features! (
(accepted, fn_must_use, "1.27.0", Some(43302)),
/// Allows capturing variables in scope using format_args!
(accepted, format_args_capture, "1.58.0", Some(67984)),
/// Infer generic args for both consts and types.
(accepted, generic_arg_infer, "CURRENT_RUSTC_VERSION", Some(85077)),
/// Allows associated types to be generic, e.g., `type Foo<T>;` (RFC 1598).
(accepted, generic_associated_types, "1.65.0", Some(44265)),
/// Allows attributes on lifetime/type formal parameters in generics (RFC 1327).

View file

@ -111,6 +111,7 @@ pub enum AttributeGate {
Ungated,
}
// FIXME(jdonszelmann): move to rustc_attr_data_structures
/// A template that the attribute input must match.
/// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now.
#[derive(Clone, Copy, Default)]
@ -127,6 +128,26 @@ pub struct AttributeTemplate {
pub name_value_str: Option<&'static str>,
}
impl AttributeTemplate {
pub fn suggestions(&self, inner: bool, name: impl std::fmt::Display) -> Vec<String> {
let mut suggestions = vec![];
let inner = if inner { "!" } else { "" };
if self.word {
suggestions.push(format!("#{inner}[{name}]"));
}
if let Some(descr) = self.list {
suggestions.push(format!("#{inner}[{name}({descr})]"));
}
suggestions.extend(self.one_of.iter().map(|&word| format!("#{inner}[{name}({word})]")));
if let Some(descr) = self.name_value_str {
suggestions.push(format!("#{inner}[{name} = \"{descr}\"]"));
}
suggestions.sort();
suggestions
}
}
/// How to handle multiple duplicate attributes on the same item.
#[derive(Clone, Copy, Default)]
pub enum AttributeDuplicates {
@ -181,20 +202,21 @@ pub enum AttributeDuplicates {
/// A convenience macro for constructing attribute templates.
/// E.g., `template!(Word, List: "description")` means that the attribute
/// supports forms `#[attr]` and `#[attr(description)]`.
#[macro_export]
macro_rules! template {
(Word) => { template!(@ true, None, &[], None) };
(List: $descr: expr) => { template!(@ false, Some($descr), &[], None) };
(OneOf: $one_of: expr) => { template!(@ false, None, $one_of, None) };
(NameValueStr: $descr: expr) => { template!(@ false, None, &[], Some($descr)) };
(Word, List: $descr: expr) => { template!(@ true, Some($descr), &[], None) };
(Word, NameValueStr: $descr: expr) => { template!(@ true, None, &[], Some($descr)) };
(Word) => { $crate::template!(@ true, None, &[], None) };
(List: $descr: expr) => { $crate::template!(@ false, Some($descr), &[], None) };
(OneOf: $one_of: expr) => { $crate::template!(@ false, None, $one_of, None) };
(NameValueStr: $descr: expr) => { $crate::template!(@ false, None, &[], Some($descr)) };
(Word, List: $descr: expr) => { $crate::template!(@ true, Some($descr), &[], None) };
(Word, NameValueStr: $descr: expr) => { $crate::template!(@ true, None, &[], Some($descr)) };
(List: $descr1: expr, NameValueStr: $descr2: expr) => {
template!(@ false, Some($descr1), &[], Some($descr2))
$crate::template!(@ false, Some($descr1), &[], Some($descr2))
};
(Word, List: $descr1: expr, NameValueStr: $descr2: expr) => {
template!(@ true, Some($descr1), &[], Some($descr2))
$crate::template!(@ true, Some($descr1), &[], Some($descr2))
};
(@ $word: expr, $list: expr, $one_of: expr, $name_value_str: expr) => { AttributeTemplate {
(@ $word: expr, $list: expr, $one_of: expr, $name_value_str: expr) => { $crate::AttributeTemplate {
word: $word, list: $list, one_of: $one_of, name_value_str: $name_value_str
} };
}

View file

@ -520,8 +520,6 @@ declare_features! (
(unstable, frontmatter, "1.88.0", Some(136889)),
/// Allows defining gen blocks and `gen fn`.
(unstable, gen_blocks, "1.75.0", Some(117078)),
/// Infer generic args for both consts and types.
(unstable, generic_arg_infer, "1.55.0", Some(85077)),
/// Allows non-trivial generic constants which have to have wfness manually propagated to callers
(incomplete, generic_const_exprs, "1.56.0", Some(76560)),
/// Allows generic parameters and where-clauses on free & associated const items.

View file

@ -452,13 +452,6 @@ fn infer_placeholder_type<'tcx>(
if let Some(ty) = node.ty() {
visitor.visit_ty_unambig(ty);
}
// If we have just one span, let's try to steal a const `_` feature error.
let try_steal_span = if !tcx.features().generic_arg_infer() && visitor.spans.len() == 1
{
visitor.spans.first().copied()
} else {
None
};
// If we didn't find any infer tys, then just fallback to `span`.
if visitor.spans.is_empty() {
visitor.spans.push(span);
@ -489,15 +482,7 @@ fn infer_placeholder_type<'tcx>(
}
}
if let Some(try_steal_span) = try_steal_span {
cx.dcx().try_steal_replace_and_emit_err(
try_steal_span,
StashKey::UnderscoreForArrayLengths,
diag,
)
} else {
diag.emit()
}
diag.emit()
});
Ty::new_error(tcx, guar)
}

View file

@ -8,7 +8,7 @@ use rustc_middle::ty::{
self, GenericArgsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty,
};
use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS;
use rustc_span::{kw, sym};
use rustc_span::kw;
use smallvec::SmallVec;
use tracing::{debug, instrument};
@ -258,19 +258,6 @@ pub fn lower_generic_args<'tcx: 'a, 'a>(
GenericParamDefKind::Const { .. },
_,
) => {
if let GenericParamDefKind::Const { .. } = param.kind
&& let GenericArg::Infer(inf) = arg
&& !tcx.features().generic_arg_infer()
{
rustc_session::parse::feature_err(
tcx.sess,
sym::generic_arg_infer,
inf.span,
"const arguments cannot yet be inferred with `_`",
)
.emit();
}
// We lower to an infer even when the feature gate is not enabled
// as it is useful for diagnostics to be able to see a `ConstKind::Infer`
args.push(ctx.provided_kind(&args, param, arg));

View file

@ -706,7 +706,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.sess
.source_map()
.span_to_snippet(lhs_expr.span)
.unwrap_or("_".to_string()),
.unwrap_or_else(|_| "_".to_string()),
};
if op.span().can_be_used_for_suggestions() {

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