Rollup merge of #142673 - oli-obk:uninit-read-mem, r=RalfJung
Show the offset, length and memory of uninit read errors
r? ``@RalfJung``
I want to improve memory dumps in general. Not sure yet how to do so best within rust diagnostics, but in a perfect world I could generate a dummy in-memory file (that contains the rendered memory dump) that we then can then provide regular rustc `Span`s to. So we'd basically report normal diagnostics for them with squiggly lines and everything.
diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs
index 6542cbf..3157b18 100644
--- a/compiler/rustc_attr_data_structures/src/attributes.rs
+++ b/compiler/rustc_attr_data_structures/src/attributes.rs
@@ -110,6 +110,22 @@ pub enum DeprecatedSince {
Err,
}
+#[derive(
+ Copy,
+ Debug,
+ Eq,
+ PartialEq,
+ Encodable,
+ Decodable,
+ Clone,
+ HashStable_Generic,
+ PrintAttribute
+)]
+pub enum CoverageStatus {
+ On,
+ Off,
+}
+
impl Deprecation {
/// Whether an item marked with #[deprecated(since = "X")] is currently
/// deprecated (i.e., whether X is not greater than the current rustc
@@ -274,6 +290,9 @@ pub enum AttributeKind {
/// Represents `#[const_trait]`.
ConstTrait(Span),
+ /// Represents `#[coverage]`.
+ Coverage(Span, CoverageStatus),
+
///Represents `#[rustc_deny_explicit_impl]`.
DenyExplicitImpl(Span),
diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
index 6b54827..8a3eb2a 100644
--- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
+++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
@@ -28,6 +28,7 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate {
ConstStability { .. } => Yes,
ConstStabilityIndirect => No,
ConstTrait(..) => No,
+ Coverage(..) => No,
DenyExplicitImpl(..) => No,
Deprecation { .. } => Yes,
DoNotImplementViaObject(..) => No,
diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
index 34d9b04..3e54277 100644
--- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
@@ -1,4 +1,4 @@
-use rustc_attr_data_structures::{AttributeKind, OptimizeAttr, UsedBy};
+use rustc_attr_data_structures::{AttributeKind, CoverageStatus, OptimizeAttr, UsedBy};
use rustc_feature::{AttributeTemplate, template};
use rustc_session::parse::feature_err;
use rustc_span::{Span, Symbol, sym};
@@ -52,6 +52,45 @@ impl<S: Stage> NoArgsAttributeParser<S> for ColdParser {
const CREATE: fn(Span) -> AttributeKind = AttributeKind::Cold;
}
+pub(crate) struct CoverageParser;
+
+impl<S: Stage> SingleAttributeParser<S> for CoverageParser {
+ const PATH: &[Symbol] = &[sym::coverage];
+ const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
+ const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
+ const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::off, sym::on]);
+
+ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+ let Some(args) = args.list() else {
+ cx.expected_specific_argument_and_list(cx.attr_span, vec!["on", "off"]);
+ return None;
+ };
+
+ let Some(arg) = args.single() else {
+ cx.expected_single_argument(args.span);
+ return None;
+ };
+
+ let fail_incorrect_argument = |span| cx.expected_specific_argument(span, vec!["on", "off"]);
+
+ let Some(arg) = arg.meta_item() else {
+ fail_incorrect_argument(args.span);
+ return None;
+ };
+
+ let status = match arg.path().word_sym() {
+ Some(sym::off) => CoverageStatus::Off,
+ Some(sym::on) => CoverageStatus::On,
+ None | Some(_) => {
+ fail_incorrect_argument(arg.span());
+ return None;
+ }
+ };
+
+ Some(AttributeKind::Coverage(cx.attr_span, status))
+ }
+}
+
pub(crate) struct ExportNameParser;
impl<S: Stage> SingleAttributeParser<S> for ExportNameParser {
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index 1449680..4d692d9 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -17,8 +17,9 @@
AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser,
};
use crate::attributes::codegen_attrs::{
- ColdParser, ExportNameParser, NakedParser, NoMangleParser, OmitGdbPrettyPrinterSectionParser,
- OptimizeParser, TargetFeatureParser, TrackCallerParser, UsedParser,
+ ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser,
+ OmitGdbPrettyPrinterSectionParser, OptimizeParser, TargetFeatureParser, TrackCallerParser,
+ UsedParser,
};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::deprecation::DeprecationParser;
@@ -139,6 +140,7 @@ mod late {
// tidy-alphabetical-end
// tidy-alphabetical-start
+ Single<CoverageParser>,
Single<DeprecationParser>,
Single<DummyParser>,
Single<ExportNameParser>,
@@ -452,6 +454,25 @@ pub(crate) fn expected_specific_argument(
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
possibilities,
strings: false,
+ list: false,
+ },
+ })
+ }
+
+ pub(crate) fn expected_specific_argument_and_list(
+ &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,
+ list: true,
},
})
}
@@ -469,6 +490,7 @@ pub(crate) fn expected_specific_argument_strings(
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
possibilities,
strings: true,
+ list: false,
},
})
}
diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
index 5b0bf0e..9a400e0 100644
--- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs
+++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
@@ -533,7 +533,9 @@ pub(crate) struct LinkOrdinalOutOfRange {
pub(crate) enum AttributeParseErrorReason {
ExpectedNoArgs,
- ExpectedStringLiteral { byte_string: Option<Span> },
+ ExpectedStringLiteral {
+ byte_string: Option<Span>,
+ },
ExpectedIntegerLiteral,
ExpectedAtLeastOneArgument,
ExpectedSingleArgument,
@@ -541,7 +543,12 @@ pub(crate) enum AttributeParseErrorReason {
UnexpectedLiteral,
ExpectedNameValue(Option<Symbol>),
DuplicateKey(Symbol),
- ExpectedSpecificArgument { possibilities: Vec<&'static str>, strings: bool },
+ ExpectedSpecificArgument {
+ possibilities: Vec<&'static str>,
+ strings: bool,
+ /// Should we tell the user to write a list when they didn't?
+ list: bool,
+ },
}
pub(crate) struct AttributeParseError {
@@ -615,7 +622,11 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
format!("expected this to be of the form `{name} = \"...\"`"),
);
}
- AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings } => {
+ AttributeParseErrorReason::ExpectedSpecificArgument {
+ possibilities,
+ strings,
+ list: false,
+ } => {
let quote = if strings { '"' } else { '`' };
match possibilities.as_slice() {
&[] => {}
@@ -641,6 +652,38 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
}
}
}
+ AttributeParseErrorReason::ExpectedSpecificArgument {
+ possibilities,
+ strings,
+ list: true,
+ } => {
+ let quote = if strings { '"' } else { '`' };
+ match possibilities.as_slice() {
+ &[] => {}
+ &[x] => {
+ diag.span_label(
+ self.span,
+ format!(
+ "this attribute is only valid with {quote}{x}{quote} as an argument"
+ ),
+ );
+ }
+ [first, second] => {
+ diag.span_label(self.span, format!("this attribute is only valid with either {quote}{first}{quote} or {quote}{second}{quote} as an argument"));
+ }
+ [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!("this attribute is only valid with one of the following arguments: {res}"));
+ }
+ }
+ }
}
let suggestions = self.template.suggestions(false, &name);
diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
index ca636a8..9bb96b9 100644
--- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
+++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
@@ -1,3 +1,4 @@
+use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def_id::LocalDefId;
use rustc_infer::infer::canonical::QueryRegionConstraints;
use rustc_infer::infer::outlives::env::RegionBoundPairs;
@@ -7,7 +8,7 @@
use rustc_infer::traits::query::type_op::DeeplyNormalize;
use rustc_middle::bug;
use rustc_middle::ty::{
- self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions,
+ self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, elaborate, fold_regions,
};
use rustc_span::Span;
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
@@ -70,10 +71,12 @@ pub(crate) fn new(
#[instrument(skip(self), level = "debug")]
pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) {
- let QueryRegionConstraints { outlives } = query_constraints;
+ let QueryRegionConstraints { outlives, assumptions } = query_constraints;
+ let assumptions =
+ elaborate::elaborate_outlives_assumptions(self.infcx.tcx, assumptions.iter().copied());
for &(predicate, constraint_category) in outlives {
- self.convert(predicate, constraint_category);
+ self.convert(predicate, constraint_category, &assumptions);
}
}
@@ -112,15 +115,20 @@ pub(crate) fn apply_closure_requirements(
self.category = outlives_requirement.category;
self.span = outlives_requirement.blame_span;
- self.convert(ty::OutlivesPredicate(subject, outlived_region), self.category);
+ self.convert(
+ ty::OutlivesPredicate(subject, outlived_region),
+ self.category,
+ &Default::default(),
+ );
}
(self.category, self.span, self.from_closure) = backup;
}
fn convert(
&mut self,
- predicate: ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
+ predicate: ty::ArgOutlivesPredicate<'tcx>,
constraint_category: ConstraintCategory<'tcx>,
+ higher_ranked_assumptions: &FxHashSet<ty::ArgOutlivesPredicate<'tcx>>,
) {
let tcx = self.infcx.tcx;
debug!("generate: constraints at: {:#?}", self.locations);
@@ -150,7 +158,15 @@ fn convert(
}
let mut next_outlives_predicates = vec![];
- for (ty::OutlivesPredicate(k1, r2), constraint_category) in outlives_predicates {
+ for (pred, constraint_category) in outlives_predicates {
+ // Constraint is implied by a coroutine's well-formedness.
+ if self.infcx.tcx.sess.opts.unstable_opts.higher_ranked_assumptions
+ && higher_ranked_assumptions.contains(&pred)
+ {
+ continue;
+ }
+
+ let ty::OutlivesPredicate(k1, r2) = pred;
match k1.kind() {
GenericArgKind::Lifetime(r1) => {
let r1_vid = self.to_region_vid(r1);
@@ -266,14 +282,15 @@ fn normalize_and_add_type_outlives_constraints(
&self,
ty: Ty<'tcx>,
next_outlives_predicates: &mut Vec<(
- ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
+ ty::ArgOutlivesPredicate<'tcx>,
ConstraintCategory<'tcx>,
)>,
) -> Ty<'tcx> {
match self.param_env.and(DeeplyNormalize { value: ty }).fully_perform(self.infcx, self.span)
{
Ok(TypeOpOutput { output: ty, constraints, .. }) => {
- if let Some(QueryRegionConstraints { outlives }) = constraints {
+ // FIXME(higher_ranked_auto): What should we do with the assumptions here?
+ if let Some(QueryRegionConstraints { outlives, assumptions: _ }) = constraints {
next_outlives_predicates.extend(outlives.iter().copied());
}
ty
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index f877e5e..d500088 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -131,6 +131,11 @@ pub(crate) fn type_check<'tcx>(
pre_obligations.is_empty(),
"there should be no incoming region obligations = {pre_obligations:#?}",
);
+ let pre_assumptions = infcx.take_registered_region_assumptions();
+ assert!(
+ pre_assumptions.is_empty(),
+ "there should be no incoming region assumptions = {pre_assumptions:#?}",
+ );
debug!(?normalized_inputs_and_output);
diff --git a/compiler/rustc_builtin_macros/src/deriving/bounds.rs b/compiler/rustc_builtin_macros/src/deriving/bounds.rs
index a98e9c6..dd8f0e4 100644
--- a/compiler/rustc_builtin_macros/src/deriving/bounds.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/bounds.rs
@@ -23,6 +23,7 @@ pub(crate) fn expand_deriving_copy(
methods: Vec::new(),
associated_types: Vec::new(),
is_const,
+ is_staged_api_crate: cx.ecfg.features.staged_api(),
};
trait_def.expand(cx, mitem, item, push);
@@ -46,6 +47,7 @@ pub(crate) fn expand_deriving_const_param_ty(
methods: Vec::new(),
associated_types: Vec::new(),
is_const,
+ is_staged_api_crate: cx.ecfg.features.staged_api(),
};
trait_def.expand(cx, mitem, item, push);
@@ -60,6 +62,7 @@ pub(crate) fn expand_deriving_const_param_ty(
methods: Vec::new(),
associated_types: Vec::new(),
is_const,
+ is_staged_api_crate: cx.ecfg.features.staged_api(),
};
trait_def.expand(cx, mitem, item, push);
@@ -83,6 +86,7 @@ pub(crate) fn expand_deriving_unsized_const_param_ty(
methods: Vec::new(),
associated_types: Vec::new(),
is_const,
+ is_staged_api_crate: cx.ecfg.features.staged_api(),
};
trait_def.expand(cx, mitem, item, push);
diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs
index 69f8c27..3c78f53 100644
--- a/compiler/rustc_builtin_macros/src/deriving/clone.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs
@@ -87,6 +87,7 @@ pub(crate) fn expand_deriving_clone(
}],
associated_types: Vec::new(),
is_const,
+ is_staged_api_crate: cx.ecfg.features.staged_api(),
};
trait_def.expand_ext(cx, mitem, item, push, is_simple)
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
index eca79e4..29d5312 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
@@ -43,6 +43,7 @@ pub(crate) fn expand_deriving_eq(
}],
associated_types: Vec::new(),
is_const,
+ is_staged_api_crate: cx.ecfg.features.staged_api(),
};
trait_def.expand_ext(cx, mitem, item, push, true)
}
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
index 1ed44c2..0e1ecf3 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
@@ -34,6 +34,7 @@ pub(crate) fn expand_deriving_ord(
}],
associated_types: Vec::new(),
is_const,
+ is_staged_api_crate: cx.ecfg.features.staged_api(),
};
trait_def.expand(cx, mitem, item, push)
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
index b1d950b..990835f 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
@@ -30,6 +30,7 @@ pub(crate) fn expand_deriving_partial_eq(
methods: Vec::new(),
associated_types: Vec::new(),
is_const: false,
+ is_staged_api_crate: cx.ecfg.features.staged_api(),
};
structural_trait_def.expand(cx, mitem, item, push);
@@ -58,6 +59,7 @@ pub(crate) fn expand_deriving_partial_eq(
methods,
associated_types: Vec::new(),
is_const,
+ is_staged_api_crate: cx.ecfg.features.staged_api(),
};
trait_def.expand(cx, mitem, item, push)
}
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
index 0a076dd..f5d262e 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
@@ -64,6 +64,7 @@ pub(crate) fn expand_deriving_partial_ord(
methods: vec![partial_cmp_def],
associated_types: Vec::new(),
is_const,
+ is_staged_api_crate: cx.ecfg.features.staged_api(),
};
trait_def.expand(cx, mitem, item, push)
}
diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs
index 8ab2198..1d63ce7 100644
--- a/compiler/rustc_builtin_macros/src/deriving/debug.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs
@@ -41,6 +41,7 @@ pub(crate) fn expand_deriving_debug(
}],
associated_types: Vec::new(),
is_const,
+ is_staged_api_crate: cx.ecfg.features.staged_api(),
};
trait_def.expand(cx, mitem, item, push)
}
diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs
index 1fe567e..b4e2d27 100644
--- a/compiler/rustc_builtin_macros/src/deriving/default.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/default.rs
@@ -51,6 +51,7 @@ pub(crate) fn expand_deriving_default(
}],
associated_types: Vec::new(),
is_const,
+ is_staged_api_crate: cx.ecfg.features.staged_api(),
};
trait_def.expand(cx, mitem, item, push)
}
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index c55a9e7..b24e556 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -181,9 +181,11 @@
pub(crate) use StaticFields::*;
pub(crate) use SubstructureFields::*;
use rustc_ast::ptr::P;
+use rustc_ast::token::{IdentIsRaw, LitKind, Token, TokenKind};
+use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenTree};
use rustc_ast::{
- self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind,
- Generics, Mutability, PatKind, VariantData,
+ self as ast, AnonConst, AttrArgs, BindingMode, ByRef, DelimArgs, EnumDef, Expr, GenericArg,
+ GenericParamKind, Generics, Mutability, PatKind, Safety, VariantData,
};
use rustc_attr_data_structures::{AttributeKind, ReprPacked};
use rustc_attr_parsing::AttributeParser;
@@ -222,6 +224,8 @@ pub(crate) struct TraitDef<'a> {
pub associated_types: Vec<(Ident, Ty)>,
pub is_const: bool,
+
+ pub is_staged_api_crate: bool,
}
pub(crate) struct MethodDef<'a> {
@@ -784,8 +788,45 @@ fn create_derived_impl(
// Create the type of `self`.
let path = cx.path_all(self.span, false, vec![type_ident], self_params);
let self_type = cx.ty_path(path);
+ let rustc_const_unstable =
+ cx.path_ident(self.span, Ident::new(sym::rustc_const_unstable, self.span));
- let attrs = thin_vec![cx.attr_word(sym::automatically_derived, self.span),];
+ let mut attrs = thin_vec![cx.attr_word(sym::automatically_derived, self.span),];
+
+ // Only add `rustc_const_unstable` attributes if `derive_const` is used within libcore/libstd,
+ // Other crates don't need stability attributes, so adding them is not useful, but libcore needs them
+ // on all const trait impls.
+ if self.is_const && self.is_staged_api_crate {
+ attrs.push(
+ cx.attr_nested(
+ rustc_ast::AttrItem {
+ unsafety: Safety::Default,
+ path: rustc_const_unstable,
+ args: AttrArgs::Delimited(DelimArgs {
+ dspan: DelimSpan::from_single(self.span),
+ delim: rustc_ast::token::Delimiter::Parenthesis,
+ tokens: [
+ TokenKind::Ident(sym::feature, IdentIsRaw::No),
+ TokenKind::Eq,
+ TokenKind::lit(LitKind::Str, sym::derive_const, None),
+ TokenKind::Comma,
+ TokenKind::Ident(sym::issue, IdentIsRaw::No),
+ TokenKind::Eq,
+ TokenKind::lit(LitKind::Str, sym::derive_const_issue, None),
+ ]
+ .into_iter()
+ .map(|kind| {
+ TokenTree::Token(Token { kind, span: self.span }, Spacing::Alone)
+ })
+ .collect(),
+ }),
+ tokens: None,
+ },
+ self.span,
+ ),
+ )
+ }
+
let opt_trait_ref = Some(trait_ref);
cx.item(
diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs
index 6e6dbe1..7853444 100644
--- a/compiler/rustc_builtin_macros/src/deriving/hash.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs
@@ -41,6 +41,7 @@ pub(crate) fn expand_deriving_hash(
}],
associated_types: Vec::new(),
is_const,
+ is_staged_api_crate: cx.ecfg.features.staged_api(),
};
hash_trait_def.expand(cx, mitem, item, push);
diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
index 442151f..8ec3599 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
@@ -530,8 +530,8 @@ fn codegen_cgu_content(
for (mono_item, item_data) in mono_items {
match mono_item {
MonoItem::Fn(instance) => {
- if tcx.codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED)
- {
+ let flags = tcx.codegen_instance_attrs(instance.def).flags;
+ if flags.contains(CodegenFnAttrFlags::NAKED) {
rustc_codegen_ssa::mir::naked_asm::codegen_naked_asm(
&mut GlobalAsmContext { tcx, global_asm: &mut cx.global_asm },
instance,
diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs
index b1f185b..b349750 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs
@@ -127,7 +127,7 @@ fn codegen_and_compile_fn<'tcx>(
module: &mut dyn Module,
instance: Instance<'tcx>,
) {
- if tcx.codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) {
+ if tcx.codegen_instance_attrs(instance.def).flags.contains(CodegenFnAttrFlags::NAKED) {
tcx.dcx()
.span_fatal(tcx.def_span(instance.def_id()), "Naked asm is not supported in JIT mode");
}
diff --git a/compiler/rustc_codegen_cranelift/src/driver/mod.rs b/compiler/rustc_codegen_cranelift/src/driver/mod.rs
index ffd47ca..8f83c30b 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/mod.rs
@@ -35,7 +35,7 @@ fn predefine_mono_items<'tcx>(
is_compiler_builtins,
);
let is_naked = tcx
- .codegen_fn_attrs(instance.def_id())
+ .codegen_instance_attrs(instance.def)
.flags
.contains(CodegenFnAttrFlags::NAKED);
module
diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs
index bf0927d..7a1ae6c 100644
--- a/compiler/rustc_codegen_gcc/src/attributes.rs
+++ b/compiler/rustc_codegen_gcc/src/attributes.rs
@@ -87,7 +87,7 @@ pub fn from_fn_attrs<'gcc, 'tcx>(
#[cfg_attr(not(feature = "master"), allow(unused_variables))] func: Function<'gcc>,
instance: ty::Instance<'tcx>,
) {
- let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id());
+ let codegen_fn_attrs = cx.tcx.codegen_instance_attrs(instance.def);
#[cfg(feature = "master")]
{
diff --git a/compiler/rustc_codegen_gcc/src/callee.rs b/compiler/rustc_codegen_gcc/src/callee.rs
index 189ac7c..e7ca95a 100644
--- a/compiler/rustc_codegen_gcc/src/callee.rs
+++ b/compiler/rustc_codegen_gcc/src/callee.rs
@@ -105,7 +105,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>)
let is_hidden = if is_generic {
// This is a monomorphization of a generic function.
if !(cx.tcx.sess.opts.share_generics()
- || tcx.codegen_fn_attrs(instance_def_id).inline
+ || tcx.codegen_instance_attrs(instance.def).inline
== rustc_attr_data_structures::InlineAttr::Never)
{
// When not sharing generics, all instances are in the same
diff --git a/compiler/rustc_codegen_gcc/src/mono_item.rs b/compiler/rustc_codegen_gcc/src/mono_item.rs
index 539e3ac..51f35cb 100644
--- a/compiler/rustc_codegen_gcc/src/mono_item.rs
+++ b/compiler/rustc_codegen_gcc/src/mono_item.rs
@@ -53,7 +53,7 @@ fn predefine_fn(
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
self.linkage.set(base::linkage_to_gcc(linkage));
let decl = self.declare_fn(symbol_name, fn_abi);
- //let attrs = self.tcx.codegen_fn_attrs(instance.def_id());
+ //let attrs = self.tcx.codegen_instance_attrs(instance.def);
attributes::from_fn_attrs(self, decl, instance);
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs
index 1ea5a06..c32f11b 100644
--- a/compiler/rustc_codegen_llvm/src/attributes.rs
+++ b/compiler/rustc_codegen_llvm/src/attributes.rs
@@ -344,7 +344,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
llfn: &'ll Value,
instance: ty::Instance<'tcx>,
) {
- let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id());
+ let codegen_fn_attrs = cx.tcx.codegen_instance_attrs(instance.def);
let mut to_add = SmallVec::<[_; 16]>::new();
diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs
index 6d68eca..5a3dd90 100644
--- a/compiler/rustc_codegen_llvm/src/callee.rs
+++ b/compiler/rustc_codegen_llvm/src/callee.rs
@@ -102,7 +102,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t
let is_hidden = if is_generic {
// This is a monomorphization of a generic function.
if !(cx.tcx.sess.opts.share_generics()
- || tcx.codegen_fn_attrs(instance_def_id).inline
+ || tcx.codegen_instance_attrs(instance.def).inline
== rustc_attr_data_structures::InlineAttr::Never)
{
// When not sharing generics, all instances are in the same
diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs
index 8f70270..f9edade 100644
--- a/compiler/rustc_codegen_llvm/src/mono_item.rs
+++ b/compiler/rustc_codegen_llvm/src/mono_item.rs
@@ -55,8 +55,8 @@ fn predefine_fn(
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
let lldecl = self.declare_fn(symbol_name, fn_abi, Some(instance));
llvm::set_linkage(lldecl, base::linkage_to_llvm(linkage));
- let attrs = self.tcx.codegen_fn_attrs(instance.def_id());
- base::set_link_section(lldecl, attrs);
+ let attrs = self.tcx.codegen_instance_attrs(instance.def);
+ base::set_link_section(lldecl, &attrs);
if (linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR)
&& self.tcx.sess.target.supports_comdat()
{
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index b467733..5ce301c 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -2542,12 +2542,7 @@ fn add_order_independent_options(
// sections to ensure we have all the data for PGO.
let keep_metadata =
crate_type == CrateType::Dylib || sess.opts.cg.profile_generate.enabled();
- if crate_type != CrateType::Executable || !sess.opts.unstable_opts.export_executable_symbols
- {
- cmd.gc_sections(keep_metadata);
- } else {
- cmd.no_gc_sections();
- }
+ cmd.gc_sections(keep_metadata);
}
cmd.set_output_kind(link_output_kind, crate_type, out_filename);
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index e0a3ad5..0507973 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -326,7 +326,6 @@ fn add_object(&mut self, path: &Path) {
link_or_cc_args(self, &[path]);
}
fn gc_sections(&mut self, keep_metadata: bool);
- fn no_gc_sections(&mut self);
fn full_relro(&mut self);
fn partial_relro(&mut self);
fn no_relro(&mut self);
@@ -688,12 +687,6 @@ fn gc_sections(&mut self, keep_metadata: bool) {
}
}
- fn no_gc_sections(&mut self) {
- if self.is_gnu || self.sess.target.is_like_wasm {
- self.link_arg("--no-gc-sections");
- }
- }
-
fn optimize(&mut self) {
if !self.is_gnu && !self.sess.target.is_like_wasm {
return;
@@ -1010,10 +1003,6 @@ fn gc_sections(&mut self, _keep_metadata: bool) {
}
}
- fn no_gc_sections(&mut self) {
- self.link_arg("/OPT:NOREF,NOICF");
- }
-
fn full_relro(&mut self) {
// noop
}
@@ -1243,10 +1232,6 @@ fn gc_sections(&mut self, _keep_metadata: bool) {
// noop
}
- fn no_gc_sections(&mut self) {
- // noop
- }
-
fn optimize(&mut self) {
// Emscripten performs own optimizations
self.cc_arg(match self.sess.opts.optimize {
@@ -1418,10 +1403,6 @@ fn gc_sections(&mut self, _keep_metadata: bool) {
self.link_arg("--gc-sections");
}
- fn no_gc_sections(&mut self) {
- self.link_arg("--no-gc-sections");
- }
-
fn optimize(&mut self) {
// The -O flag is, as of late 2023, only used for merging of strings and debuginfo, and
// only differentiates -O0 and -O1. It does not apply to LTO.
@@ -1567,10 +1548,6 @@ fn gc_sections(&mut self, keep_metadata: bool) {
}
}
- fn no_gc_sections(&mut self) {
- self.link_arg("--no-gc-sections");
- }
-
fn optimize(&mut self) {
// GNU-style linkers support optimization with -O. GNU ld doesn't
// need a numeric argument, but other linkers do.
@@ -1734,10 +1711,6 @@ fn gc_sections(&mut self, _keep_metadata: bool) {
self.link_arg("-bgc");
}
- fn no_gc_sections(&mut self) {
- self.link_arg("-bnogc");
- }
-
fn optimize(&mut self) {}
fn pgo_gen(&mut self) {
@@ -1982,8 +1955,6 @@ fn no_relro(&mut self) {}
fn gc_sections(&mut self, _keep_metadata: bool) {}
- fn no_gc_sections(&mut self) {}
-
fn pgo_gen(&mut self) {}
fn no_crt_objects(&mut self) {}
@@ -2057,8 +2028,6 @@ fn no_relro(&mut self) {}
fn gc_sections(&mut self, _keep_metadata: bool) {}
- fn no_gc_sections(&mut self) {}
-
fn pgo_gen(&mut self) {}
fn no_crt_objects(&mut self) {}
@@ -2139,8 +2108,6 @@ fn no_relro(&mut self) {}
fn gc_sections(&mut self, _keep_metadata: bool) {}
- fn no_gc_sections(&mut self) {}
-
fn pgo_gen(&mut self) {}
fn no_crt_objects(&mut self) {}
diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
index 025f5fb..b8f635a 100644
--- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
@@ -356,7 +356,7 @@ pub(crate) fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) {
LocalRef::Operand(operand) => {
// Don't spill operands onto the stack in naked functions.
// See: https://github.com/rust-lang/rust/issues/42779
- let attrs = bx.tcx().codegen_fn_attrs(self.instance.def_id());
+ let attrs = bx.tcx().codegen_instance_attrs(self.instance.def);
if attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
return;
}
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index fa69820..50d0f91 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -390,9 +390,8 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
let mut num_untupled = None;
- let codegen_fn_attrs = bx.tcx().codegen_fn_attrs(fx.instance.def_id());
- let naked = codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED);
- if naked {
+ let codegen_fn_attrs = bx.tcx().codegen_instance_attrs(fx.instance.def);
+ if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
return vec![];
}
diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
index beaf895..42e435c 100644
--- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
@@ -128,7 +128,7 @@ fn prefix_and_suffix<'tcx>(
let is_arm = tcx.sess.target.arch == "arm";
let is_thumb = tcx.sess.unstable_target_features.contains(&sym::thumb_mode);
- let attrs = tcx.codegen_fn_attrs(instance.def_id());
+ let attrs = tcx.codegen_instance_attrs(instance.def);
let link_section = attrs.link_section.map(|symbol| symbol.as_str().to_string());
// If no alignment is specified, an alignment of 4 bytes is used.
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index e90463a..2587e89 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -611,11 +611,19 @@ pub(crate) fn codegen_rvalue_operand(
let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty());
let fn_ty = bx.fn_decl_backend_type(fn_abi);
let fn_attrs = if bx.tcx().def_kind(instance.def_id()).has_codegen_attrs() {
- Some(bx.tcx().codegen_fn_attrs(instance.def_id()))
+ Some(bx.tcx().codegen_instance_attrs(instance.def))
} else {
None
};
- bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, &[], None, Some(instance))
+ bx.call(
+ fn_ty,
+ fn_attrs.as_deref(),
+ Some(fn_abi),
+ fn_ptr,
+ &[],
+ None,
+ Some(instance),
+ )
} else {
bx.get_static(def_id)
};
diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs
index 7b4268a..b9040c3 100644
--- a/compiler/rustc_codegen_ssa/src/mono_item.rs
+++ b/compiler/rustc_codegen_ssa/src/mono_item.rs
@@ -41,12 +41,8 @@ fn define<Bx: BuilderMethods<'a, 'tcx>>(
base::codegen_global_asm(cx, item_id);
}
MonoItem::Fn(instance) => {
- if cx
- .tcx()
- .codegen_fn_attrs(instance.def_id())
- .flags
- .contains(CodegenFnAttrFlags::NAKED)
- {
+ let flags = cx.tcx().codegen_instance_attrs(instance.def).flags;
+ if flags.contains(CodegenFnAttrFlags::NAKED) {
naked_asm::codegen_naked_asm::<Bx::CodegenCx>(cx, instance, item_data);
} else {
base::codegen_instance::<Bx>(cx, instance);
@@ -75,7 +71,7 @@ fn predefine<Bx: BuilderMethods<'a, 'tcx>>(
cx.predefine_static(def_id, linkage, visibility, symbol_name);
}
MonoItem::Fn(instance) => {
- let attrs = cx.tcx().codegen_fn_attrs(instance.def_id());
+ let attrs = cx.tcx().codegen_instance_attrs(instance.def);
if attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
// do not define this function; it will become a global assembly block
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index b2cbbc2..47228de 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -936,7 +936,7 @@ pub fn get_alloc_info(&self, id: AllocId) -> AllocInfo {
if let Some(fn_val) = self.get_fn_alloc(id) {
let align = match fn_val {
FnVal::Instance(instance) => {
- self.tcx.codegen_fn_attrs(instance.def_id()).alignment.unwrap_or(Align::ONE)
+ self.tcx.codegen_instance_attrs(instance.def).alignment.unwrap_or(Align::ONE)
}
// Machine-specific extra functions currently do not support alignment restrictions.
FnVal::Other(_) => Align::ONE,
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 1849038..f3ed604 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -1237,9 +1237,55 @@ pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option<geto
return None;
}
+ warn_on_confusing_output_filename_flag(early_dcx, &matches, args);
+
Some(matches)
}
+/// Warn if `-o` is used without a space between the flag name and the value
+/// and the value is a high-value confusables,
+/// e.g. `-optimize` instead of `-o optimize`, see issue #142812.
+fn warn_on_confusing_output_filename_flag(
+ early_dcx: &EarlyDiagCtxt,
+ matches: &getopts::Matches,
+ args: &[String],
+) {
+ fn eq_ignore_separators(s1: &str, s2: &str) -> bool {
+ let s1 = s1.replace('-', "_");
+ let s2 = s2.replace('-', "_");
+ s1 == s2
+ }
+
+ if let Some(name) = matches.opt_str("o")
+ && let Some(suspect) = args.iter().find(|arg| arg.starts_with("-o") && *arg != "-o")
+ {
+ let filename = suspect.strip_prefix("-").unwrap_or(suspect);
+ let optgroups = config::rustc_optgroups();
+ let fake_args = ["optimize", "o0", "o1", "o2", "o3", "ofast", "og", "os", "oz"];
+
+ // Check if provided filename might be confusing in conjunction with `-o` flag,
+ // i.e. consider `-o{filename}` such as `-optimize` with `filename` being `ptimize`.
+ // There are high-value confusables, for example:
+ // - Long name of flags, e.g. `--out-dir` vs `-out-dir`
+ // - C compiler flag, e.g. `optimize`, `o0`, `o1`, `o2`, `o3`, `ofast`.
+ // - Codegen flags, e.g. `pt-level` of `-opt-level`.
+ if optgroups.iter().any(|option| eq_ignore_separators(option.long_name(), filename))
+ || config::CG_OPTIONS.iter().any(|option| eq_ignore_separators(option.name(), filename))
+ || fake_args.iter().any(|arg| eq_ignore_separators(arg, filename))
+ {
+ early_dcx.early_warn(
+ "option `-o` has no space between flag name and value, which can be confusing",
+ );
+ early_dcx.early_note(format!(
+ "output filename `-o {name}` is applied instead of a flag named `o{name}`"
+ ));
+ early_dcx.early_help(format!(
+ "insert a space between `-o` and `{name}` if this is intentional: `-o {name}`"
+ ));
+ }
+ }
+}
+
fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> {
let mut parser = unwrap_or_emit_fatal(match &sess.io.input {
Input::File(file) => new_parser_from_file(&sess.psess, file, None),
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index 85683c1..51d6e43 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -3,8 +3,8 @@
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::util::literal;
use rustc_ast::{
- self as ast, AnonConst, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, UnOp,
- attr, token, tokenstream,
+ self as ast, AnonConst, AttrItem, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind,
+ UnOp, attr, token, tokenstream,
};
use rustc_span::source_map::Spanned;
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
@@ -766,4 +766,10 @@ pub fn attr_nested_word(&self, outer: Symbol, inner: Symbol, span: Span) -> ast:
span,
)
}
+
+ // Builds an attribute fully manually.
+ pub fn attr_nested(&self, inner: AttrItem, span: Span) -> ast::Attribute {
+ let g = &self.sess.psess.attr_id_generator;
+ attr::mk_attr_from_item(g, inner, None, ast::AttrStyle::Outer, span)
+ }
}
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index a20becb..e38ca9e 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -158,7 +158,7 @@
hir_analysis_drop_impl_negative = negative `Drop` impls are not supported
hir_analysis_drop_impl_on_wrong_item =
- the `Drop` trait may only be implemented for local structs, enums, and unions
+ the `{$trait_}` trait may only be implemented for local structs, enums, and unions
.label = must be a struct, enum, or union in the current crate
hir_analysis_drop_impl_reservation = reservation `Drop` impls are not supported
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index 8356a0a..27948f5 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -37,6 +37,7 @@ pub(super) fn check_trait<'tcx>(
let lang_items = tcx.lang_items();
let checker = Checker { tcx, trait_def_id, impl_def_id, impl_header };
checker.check(lang_items.drop_trait(), visit_implementation_of_drop)?;
+ checker.check(lang_items.async_drop_trait(), visit_implementation_of_drop)?;
checker.check(lang_items.copy_trait(), visit_implementation_of_copy)?;
checker.check(lang_items.const_param_ty_trait(), |checker| {
visit_implementation_of_const_param_ty(checker, LangItem::ConstParamTy)
@@ -83,7 +84,10 @@ fn visit_implementation_of_drop(checker: &Checker<'_>) -> Result<(), ErrorGuaran
let impl_ = tcx.hir_expect_item(impl_did).expect_impl();
- Err(tcx.dcx().emit_err(errors::DropImplOnWrongItem { span: impl_.self_ty.span }))
+ Err(tcx.dcx().emit_err(errors::DropImplOnWrongItem {
+ span: impl_.self_ty.span,
+ trait_: tcx.item_name(checker.impl_header.trait_ref.skip_binder().def_id),
+ }))
}
fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index eb65050..fbd21f8 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -205,6 +205,7 @@ pub(crate) struct DropImplOnWrongItem {
#[primary_span]
#[label]
pub span: Span,
+ pub trait_: Symbol,
}
#[derive(Diagnostic)]
diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs
index 301f5de..99a633e 100644
--- a/compiler/rustc_hir_analysis/src/outlives/utils.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs
@@ -7,8 +7,7 @@
/// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred
/// must be added to the struct header.
-pub(crate) type RequiredPredicates<'tcx> =
- FxIndexMap<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>, Span>;
+pub(crate) type RequiredPredicates<'tcx> = FxIndexMap<ty::ArgOutlivesPredicate<'tcx>, Span>;
/// Given a requirement `T: 'a` or `'b: 'a`, deduce the
/// outlives_component and add it to `required_predicates`
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index 459c049..a413f80 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -161,16 +161,7 @@ pub(crate) fn check_expr_closure(
// Resume type defaults to `()` if the coroutine has no argument.
let resume_ty = liberated_sig.inputs().get(0).copied().unwrap_or(tcx.types.unit);
- // In the new solver, we can just instantiate this eagerly
- // with the witness. This will ensure that goals that don't need
- // to stall on interior types will get processed eagerly.
- let interior = if self.next_trait_solver() {
- Ty::new_coroutine_witness(tcx, expr_def_id.to_def_id(), parent_args)
- } else {
- self.next_ty_var(expr_span)
- };
-
- self.deferred_coroutine_interiors.borrow_mut().push((expr_def_id, interior));
+ let interior = Ty::new_coroutine_witness(tcx, expr_def_id.to_def_id(), parent_args);
// Coroutines that come from coroutine closures have not yet determined
// their kind ty, so make a fresh infer var which will be constrained
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index af1fc04..0b3d50f 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -625,50 +625,23 @@ pub(crate) fn resolve_coroutine_interiors(&self) {
// trigger query cycle ICEs, as doing so requires MIR.
self.select_obligations_where_possible(|_| {});
- let coroutines = std::mem::take(&mut *self.deferred_coroutine_interiors.borrow_mut());
- debug!(?coroutines);
+ let ty::TypingMode::Analysis { defining_opaque_types_and_generators } = self.typing_mode()
+ else {
+ bug!();
+ };
- let mut obligations = vec![];
-
- if !self.next_trait_solver() {
- for &(coroutine_def_id, interior) in coroutines.iter() {
- debug!(?coroutine_def_id);
-
- // Create the `CoroutineWitness` type that we will unify with `interior`.
- let args = ty::GenericArgs::identity_for_item(
- self.tcx,
- self.tcx.typeck_root_def_id(coroutine_def_id.to_def_id()),
- );
- let witness =
- Ty::new_coroutine_witness(self.tcx, coroutine_def_id.to_def_id(), args);
-
- // Unify `interior` with `witness` and collect all the resulting obligations.
- let span = self.tcx.hir_body_owned_by(coroutine_def_id).value.span;
- let ty::Infer(ty::InferTy::TyVar(_)) = interior.kind() else {
- span_bug!(span, "coroutine interior witness not infer: {:?}", interior.kind())
- };
- let ok = self
- .at(&self.misc(span), self.param_env)
- // Will never define opaque types, as all we do is instantiate a type variable.
- .eq(DefineOpaqueTypes::Yes, interior, witness)
- .expect("Failed to unify coroutine interior type");
-
- obligations.extend(ok.obligations);
- }
- }
-
- if !coroutines.is_empty() {
- obligations.extend(
+ if defining_opaque_types_and_generators
+ .iter()
+ .any(|def_id| self.tcx.is_coroutine(def_id.to_def_id()))
+ {
+ self.typeck_results.borrow_mut().coroutine_stalled_predicates.extend(
self.fulfillment_cx
.borrow_mut()
- .drain_stalled_obligations_for_coroutines(&self.infcx),
+ .drain_stalled_obligations_for_coroutines(&self.infcx)
+ .into_iter()
+ .map(|o| (o.predicate, o.cause)),
);
}
-
- self.typeck_results
- .borrow_mut()
- .coroutine_stalled_predicates
- .extend(obligations.into_iter().map(|o| (o.predicate, o.cause)));
}
#[instrument(skip(self), level = "debug")]
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 3261327..1f3969b 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -1650,7 +1650,8 @@ fn consider_candidates(
}
}
- let sources = candidates.iter().map(|p| self.candidate_source(p, self_ty)).collect();
+ let sources =
+ applicable_candidates.iter().map(|p| self.candidate_source(p.0, self_ty)).collect();
return Some(Err(MethodError::Ambiguity(sources)));
}
diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs
index 26be5fc..9f4ab8c 100644
--- a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs
+++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs
@@ -63,8 +63,6 @@ pub(crate) struct TypeckRootCtxt<'tcx> {
pub(super) deferred_asm_checks: RefCell<Vec<(&'tcx hir::InlineAsm<'tcx>, HirId)>>,
- pub(super) deferred_coroutine_interiors: RefCell<Vec<(LocalDefId, Ty<'tcx>)>>,
-
pub(super) deferred_repeat_expr_checks:
RefCell<Vec<(&'tcx hir::Expr<'tcx>, Ty<'tcx>, ty::Const<'tcx>)>>,
@@ -103,7 +101,6 @@ pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
deferred_cast_checks: RefCell::new(Vec::new()),
deferred_transmute_checks: RefCell::new(Vec::new()),
deferred_asm_checks: RefCell::new(Vec::new()),
- deferred_coroutine_interiors: RefCell::new(Vec::new()),
deferred_repeat_expr_checks: RefCell::new(Vec::new()),
diverging_type_vars: RefCell::new(Default::default()),
infer_var_info: RefCell::new(Default::default()),
diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs
index d5d49e3..6be53c9 100644
--- a/compiler/rustc_infer/src/infer/canonical/query_response.rs
+++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs
@@ -116,9 +116,15 @@ fn make_query_response<T>(
}
let region_obligations = self.take_registered_region_obligations();
+ let region_assumptions = self.take_registered_region_assumptions();
debug!(?region_obligations);
let region_constraints = self.with_region_constraints(|region_constraints| {
- make_query_region_constraints(tcx, region_obligations, region_constraints)
+ make_query_region_constraints(
+ tcx,
+ region_obligations,
+ region_constraints,
+ region_assumptions,
+ )
});
debug!(?region_constraints);
@@ -169,6 +175,11 @@ pub fn instantiate_query_response_and_region_obligations<R>(
self.register_outlives_constraint(predicate, cause);
}
+ for assumption in &query_response.value.region_constraints.assumptions {
+ let assumption = instantiate_value(self.tcx, &result_args, *assumption);
+ self.register_region_assumption(assumption);
+ }
+
let user_result: R =
query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone());
@@ -292,6 +303,18 @@ pub fn instantiate_nll_query_response_and_region_obligations<R>(
}),
);
+ // FIXME(higher_ranked_auto): Optimize this to instantiate all assumptions
+ // at once, rather than calling `instantiate_value` repeatedly which may
+ // create more universes.
+ output_query_region_constraints.assumptions.extend(
+ query_response
+ .value
+ .region_constraints
+ .assumptions
+ .iter()
+ .map(|&r_c| instantiate_value(self.tcx, &result_args, r_c)),
+ );
+
let user_result: R =
query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone());
@@ -567,6 +590,7 @@ pub fn make_query_region_constraints<'tcx>(
tcx: TyCtxt<'tcx>,
outlives_obligations: Vec<TypeOutlivesConstraint<'tcx>>,
region_constraints: &RegionConstraintData<'tcx>,
+ assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>,
) -> QueryRegionConstraints<'tcx> {
let RegionConstraintData { constraints, verifys } = region_constraints;
@@ -602,5 +626,5 @@ pub fn make_query_region_constraints<'tcx>(
}))
.collect();
- QueryRegionConstraints { outlives }
+ QueryRegionConstraints { outlives, assumptions }
}
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index cc3ad92..2d269e3 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -149,6 +149,13 @@ pub struct InferCtxtInner<'tcx> {
/// that all type inference variables have been bound and so forth.
region_obligations: Vec<TypeOutlivesConstraint<'tcx>>,
+ /// The outlives bounds that we assume must hold about placeholders that
+ /// come from instantiating the binder of coroutine-witnesses. These bounds
+ /// are deduced from the well-formedness of the witness's types, and are
+ /// necessary because of the way we anonymize the regions in a coroutine,
+ /// which may cause types to no longer be considered well-formed.
+ region_assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>,
+
/// Caches for opaque type inference.
opaque_type_storage: OpaqueTypeStorage<'tcx>,
}
@@ -164,7 +171,8 @@ fn new() -> InferCtxtInner<'tcx> {
int_unification_storage: Default::default(),
float_unification_storage: Default::default(),
region_constraint_storage: Some(Default::default()),
- region_obligations: vec![],
+ region_obligations: Default::default(),
+ region_assumptions: Default::default(),
opaque_type_storage: Default::default(),
}
}
@@ -175,6 +183,11 @@ pub fn region_obligations(&self) -> &[TypeOutlivesConstraint<'tcx>] {
}
#[inline]
+ pub fn region_assumptions(&self) -> &[ty::ArgOutlivesPredicate<'tcx>] {
+ &self.region_assumptions
+ }
+
+ #[inline]
pub fn projection_cache(&mut self) -> traits::ProjectionCache<'_, 'tcx> {
self.projection_cache.with_log(&mut self.undo_log)
}
diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs
index cb5a33c..47b738a 100644
--- a/compiler/rustc_infer/src/infer/outlives/env.rs
+++ b/compiler/rustc_infer/src/infer/outlives/env.rs
@@ -1,4 +1,4 @@
-use rustc_data_structures::fx::FxIndexSet;
+use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_data_structures::transitive_relation::TransitiveRelationBuilder;
use rustc_middle::{bug, ty};
use tracing::debug;
@@ -39,6 +39,9 @@ pub struct OutlivesEnvironment<'tcx> {
/// optimized in the future, though.
region_bound_pairs: RegionBoundPairs<'tcx>,
known_type_outlives: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
+ /// Assumptions that come from the well-formedness of coroutines that we prove
+ /// auto trait bounds for during the type checking of this body.
+ higher_ranked_assumptions: FxHashSet<ty::ArgOutlivesPredicate<'tcx>>,
}
/// "Region-bound pairs" tracks outlives relations that are known to
@@ -52,6 +55,7 @@ pub fn from_normalized_bounds(
param_env: ty::ParamEnv<'tcx>,
known_type_outlives: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
extra_bounds: impl IntoIterator<Item = OutlivesBound<'tcx>>,
+ higher_ranked_assumptions: FxHashSet<ty::ArgOutlivesPredicate<'tcx>>,
) -> Self {
let mut region_relation = TransitiveRelationBuilder::default();
let mut region_bound_pairs = RegionBoundPairs::default();
@@ -88,6 +92,7 @@ pub fn from_normalized_bounds(
known_type_outlives,
free_region_map: FreeRegionMap { relation: region_relation.freeze() },
region_bound_pairs,
+ higher_ranked_assumptions,
}
}
@@ -102,4 +107,8 @@ pub fn region_bound_pairs(&self) -> &RegionBoundPairs<'tcx> {
pub fn known_type_outlives(&self) -> &[ty::PolyTypeOutlivesPredicate<'tcx>] {
&self.known_type_outlives
}
+
+ pub fn higher_ranked_assumptions(&self) -> &FxHashSet<ty::ArgOutlivesPredicate<'tcx>> {
+ &self.higher_ranked_assumptions
+ }
}
diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs
index 2a4544c..19911bf 100644
--- a/compiler/rustc_infer/src/infer/outlives/mod.rs
+++ b/compiler/rustc_infer/src/infer/outlives/mod.rs
@@ -10,6 +10,7 @@
use super::{InferCtxt, RegionResolutionError, SubregionOrigin};
use crate::infer::free_regions::RegionRelations;
use crate::infer::lexical_region_resolve;
+use crate::infer::region_constraints::Constraint;
pub mod env;
pub mod for_liveness;
@@ -54,18 +55,29 @@ pub fn resolve_regions_with_normalize(
}
};
- let storage = {
+ let mut storage = {
let mut inner = self.inner.borrow_mut();
let inner = &mut *inner;
assert!(
self.tainted_by_errors().is_some() || inner.region_obligations.is_empty(),
"region_obligations not empty: {:#?}",
- inner.region_obligations
+ inner.region_obligations,
);
assert!(!UndoLogs::<UndoLog<'_>>::in_snapshot(&inner.undo_log));
inner.region_constraint_storage.take().expect("regions already resolved")
};
+ // Filter out any region-region outlives assumptions that are implied by
+ // coroutine well-formedness.
+ if self.tcx.sess.opts.unstable_opts.higher_ranked_assumptions {
+ storage.data.constraints.retain(|(constraint, _)| match *constraint {
+ Constraint::RegSubReg(r1, r2) => !outlives_env
+ .higher_ranked_assumptions()
+ .contains(&ty::OutlivesPredicate(r2.into(), r1)),
+ _ => true,
+ });
+ }
+
let region_rels = &RegionRelations::new(self.tcx, outlives_env.free_region_map());
let (lexical_region_resolutions, errors) =
@@ -93,6 +105,11 @@ pub fn take_and_reset_region_constraints(&self) -> RegionConstraintData<'tcx> {
"region_obligations not empty: {:#?}",
self.inner.borrow().region_obligations
);
+ assert!(
+ self.inner.borrow().region_assumptions.is_empty(),
+ "region_assumptions not empty: {:#?}",
+ self.inner.borrow().region_assumptions
+ );
self.inner.borrow_mut().unwrap_region_constraints().take_and_reset_data()
}
diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs
index 43504bd..a8520c0 100644
--- a/compiler/rustc_infer/src/infer/outlives/obligations.rs
+++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs
@@ -84,7 +84,7 @@
impl<'tcx> InferCtxt<'tcx> {
pub fn register_outlives_constraint(
&self,
- ty::OutlivesPredicate(arg, r2): ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
+ ty::OutlivesPredicate(arg, r2): ty::ArgOutlivesPredicate<'tcx>,
cause: &ObligationCause<'tcx>,
) {
match arg.kind() {
@@ -170,6 +170,16 @@ pub fn take_registered_region_obligations(&self) -> Vec<TypeOutlivesConstraint<'
std::mem::take(&mut self.inner.borrow_mut().region_obligations)
}
+ pub fn register_region_assumption(&self, assumption: ty::ArgOutlivesPredicate<'tcx>) {
+ let mut inner = self.inner.borrow_mut();
+ inner.undo_log.push(UndoLog::PushRegionAssumption);
+ inner.region_assumptions.push(assumption);
+ }
+
+ pub fn take_registered_region_assumptions(&self) -> Vec<ty::ArgOutlivesPredicate<'tcx>> {
+ std::mem::take(&mut self.inner.borrow_mut().region_assumptions)
+ }
+
/// Process the region obligations that must be proven (during
/// `regionck`) for the given `body_id`, given information about
/// the region bounds in scope and so forth.
@@ -220,6 +230,14 @@ pub fn process_registered_region_obligations(
let (sup_type, sub_region) =
(sup_type, sub_region).fold_with(&mut OpportunisticRegionResolver::new(self));
+ if self.tcx.sess.opts.unstable_opts.higher_ranked_assumptions
+ && outlives_env
+ .higher_ranked_assumptions()
+ .contains(&ty::OutlivesPredicate(sup_type.into(), sub_region))
+ {
+ continue;
+ }
+
debug!(?sup_type, ?sub_region, ?origin);
let outlives = &mut TypeOutlives::new(
diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs
index 34e649a..40e4c32 100644
--- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs
+++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs
@@ -28,6 +28,7 @@ pub(crate) enum UndoLog<'tcx> {
RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
ProjectionCache(traits::UndoLog<'tcx>),
PushTypeOutlivesConstraint,
+ PushRegionAssumption,
}
macro_rules! impl_from {
@@ -77,6 +78,9 @@ fn reverse(&mut self, undo: UndoLog<'tcx>) {
let popped = self.region_obligations.pop();
assert_matches!(popped, Some(_), "pushed region constraint but could not pop it");
}
+ UndoLog::PushRegionAssumption => {
+ self.region_assumptions.pop();
+ }
}
}
}
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 8244f0b..73e6883 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -1585,6 +1585,8 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
if let ExprKind::Unary(UnOp::Neg, ref inner) = expr.kind
&& let ExprKind::Unary(UnOp::Neg, ref inner2) = inner.kind
&& !matches!(inner2.kind, ExprKind::Unary(UnOp::Neg, _))
+ // Don't lint if this jumps macro expansion boundary (Issue #143980)
+ && expr.span.eq_ctxt(inner.span)
{
cx.emit_span_lint(
DOUBLE_NEGATIONS,
diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs
index 2bbc48b..4fe4c2d 100644
--- a/compiler/rustc_middle/src/infer/canonical.rs
+++ b/compiler/rustc_middle/src/infer/canonical.rs
@@ -81,13 +81,18 @@ pub struct QueryResponse<'tcx, R> {
#[derive(HashStable, TypeFoldable, TypeVisitable)]
pub struct QueryRegionConstraints<'tcx> {
pub outlives: Vec<QueryOutlivesConstraint<'tcx>>,
+ pub assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>,
}
impl QueryRegionConstraints<'_> {
- /// Represents an empty (trivially true) set of region
- /// constraints.
+ /// Represents an empty (trivially true) set of region constraints.
+ ///
+ /// FIXME(higher_ranked_auto): This could still just be true if there are only assumptions?
+ /// Because I don't expect for us to get cases where an assumption from one query would
+ /// discharge a requirement from another query, which is a potential problem if we did throw
+ /// away these assumptions because there were no constraints.
pub fn is_empty(&self) -> bool {
- self.outlives.is_empty()
+ self.outlives.is_empty() && self.assumptions.is_empty()
}
}
@@ -130,8 +135,7 @@ pub fn is_proven(&self) -> bool {
}
}
-pub type QueryOutlivesConstraint<'tcx> =
- (ty::OutlivesPredicate<'tcx, GenericArg<'tcx>>, ConstraintCategory<'tcx>);
+pub type QueryOutlivesConstraint<'tcx> = (ty::ArgOutlivesPredicate<'tcx>, ConstraintCategory<'tcx>);
#[derive(Default)]
pub struct CanonicalParamEnvCache<'tcx> {
diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
index 2f16d38..6eae3b5 100644
--- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
+++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
@@ -1,3 +1,5 @@
+use std::borrow::Cow;
+
use rustc_abi::Align;
use rustc_ast::expand::autodiff_attrs::AutoDiffAttrs;
use rustc_attr_data_structures::{InlineAttr, InstructionSetAttr, OptimizeAttr};
@@ -6,6 +8,26 @@
use rustc_target::spec::SanitizerSet;
use crate::mir::mono::Linkage;
+use crate::ty::{InstanceKind, TyCtxt};
+
+impl<'tcx> TyCtxt<'tcx> {
+ pub fn codegen_instance_attrs(
+ self,
+ instance_kind: InstanceKind<'_>,
+ ) -> Cow<'tcx, CodegenFnAttrs> {
+ let mut attrs = Cow::Borrowed(self.codegen_fn_attrs(instance_kind.def_id()));
+
+ // Drop the `#[naked]` attribute on non-item `InstanceKind`s, like the shims that
+ // are generated for indirect function calls.
+ if !matches!(instance_kind, InstanceKind::Item(_)) {
+ if attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
+ attrs.to_mut().flags.remove(CodegenFnAttrFlags::NAKED);
+ }
+ }
+
+ attrs
+ }
+}
#[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)]
pub struct CodegenFnAttrs {
diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs
index 47ba850..2d7ddd1 100644
--- a/compiler/rustc_middle/src/mir/mono.rs
+++ b/compiler/rustc_middle/src/mir/mono.rs
@@ -152,7 +152,7 @@ pub fn instantiation_mode(&self, tcx: TyCtxt<'tcx>) -> InstantiationMode {
// If the function is #[naked] or contains any other attribute that requires exactly-once
// instantiation:
// We emit an unused_attributes lint for this case, which should be kept in sync if possible.
- let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id());
+ let codegen_fn_attrs = tcx.codegen_instance_attrs(instance.def);
if codegen_fn_attrs.contains_extern_indicator()
|| codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED)
{
@@ -219,7 +219,7 @@ pub fn instantiation_mode(&self, tcx: TyCtxt<'tcx>) -> InstantiationMode {
// functions the same as those that unconditionally get LocalCopy codegen. It's only when
// we get here that we can at least not codegen a #[inline(never)] generic function in all
// of our CGUs.
- if let InlineAttr::Never = tcx.codegen_fn_attrs(instance.def_id()).inline
+ if let InlineAttr::Never = codegen_fn_attrs.inline
&& self.is_generic_fn()
{
return InstantiationMode::GloballyShared { may_conflict: true };
@@ -234,14 +234,13 @@ pub fn instantiation_mode(&self, tcx: TyCtxt<'tcx>) -> InstantiationMode {
}
pub fn explicit_linkage(&self, tcx: TyCtxt<'tcx>) -> Option<Linkage> {
- let def_id = match *self {
- MonoItem::Fn(ref instance) => instance.def_id(),
- MonoItem::Static(def_id) => def_id,
+ let instance_kind = match *self {
+ MonoItem::Fn(ref instance) => instance.def,
+ MonoItem::Static(def_id) => InstanceKind::Item(def_id),
MonoItem::GlobalAsm(..) => return None,
};
- let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id);
- codegen_fn_attrs.linkage
+ tcx.codegen_instance_attrs(instance_kind).linkage
}
/// Returns `true` if this instance is instantiable - whether it has no unsatisfied
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 935cc88..d5c2b6d 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -988,7 +988,7 @@
}
query coroutine_hidden_types(
- def_id: DefId
+ def_id: DefId,
) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, ty::CoroutineWitnessTypes<TyCtxt<'tcx>>>> {
desc { "looking up the hidden types stored across await points in a coroutine" }
}
@@ -1505,6 +1505,15 @@
separate_provide_extern
}
+ /// Returns the `CodegenFnAttrs` for the item at `def_id`.
+ ///
+ /// If possible, use `tcx.codegen_instance_attrs` instead. That function takes the
+ /// instance kind into account.
+ ///
+ /// For example, the `#[naked]` attribute should be applied for `InstanceKind::Item`,
+ /// but should not be applied if the instance kind is `InstanceKind::ReifyShim`.
+ /// Using this query would include the attribute regardless of the actual instance
+ /// kind at the call site.
query codegen_fn_attrs(def_id: DefId) -> &'tcx CodegenFnAttrs {
desc { |tcx| "computing codegen attributes of `{}`", tcx.def_path_str(def_id) }
arena_cache
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 915b062..81c13e5 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -163,6 +163,8 @@ fn with_cached_task<T>(self, task: impl FnOnce() -> T) -> (T, DepNodeIndex) {
type BoundRegion = ty::BoundRegion;
type PlaceholderRegion = ty::PlaceholderRegion;
+ type RegionAssumptions = &'tcx ty::List<ty::ArgOutlivesPredicate<'tcx>>;
+
type ParamEnv = ty::ParamEnv<'tcx>;
type Predicate = Predicate<'tcx>;
@@ -714,17 +716,13 @@ fn opaque_types_and_coroutines_defined_by(
self,
defining_anchor: Self::LocalDefId,
) -> Self::LocalDefIds {
- if self.next_trait_solver_globally() {
- let coroutines_defined_by = self
- .nested_bodies_within(defining_anchor)
- .iter()
- .filter(|def_id| self.is_coroutine(def_id.to_def_id()));
- self.mk_local_def_ids_from_iter(
- self.opaque_types_defined_by(defining_anchor).iter().chain(coroutines_defined_by),
- )
- } else {
- self.opaque_types_defined_by(defining_anchor)
- }
+ let coroutines_defined_by = self
+ .nested_bodies_within(defining_anchor)
+ .iter()
+ .filter(|def_id| self.is_coroutine(def_id.to_def_id()));
+ self.mk_local_def_ids_from_iter(
+ self.opaque_types_defined_by(defining_anchor).iter().chain(coroutines_defined_by),
+ )
}
}
@@ -882,6 +880,7 @@ pub struct CtxtInterners<'tcx> {
offset_of: InternedSet<'tcx, List<(VariantIdx, FieldIdx)>>,
valtree: InternedSet<'tcx, ty::ValTreeKind<'tcx>>,
patterns: InternedSet<'tcx, List<ty::Pattern<'tcx>>>,
+ outlives: InternedSet<'tcx, List<ty::ArgOutlivesPredicate<'tcx>>>,
}
impl<'tcx> CtxtInterners<'tcx> {
@@ -919,6 +918,7 @@ fn new(arena: &'tcx WorkerLocal<Arena<'tcx>>) -> CtxtInterners<'tcx> {
offset_of: InternedSet::with_capacity(N),
valtree: InternedSet::with_capacity(N),
patterns: InternedSet::with_capacity(N),
+ outlives: InternedSet::with_capacity(N),
}
}
@@ -2700,6 +2700,7 @@ impl<'tcx> TyCtxt<'tcx> {
captures: intern_captures(&'tcx ty::CapturedPlace<'tcx>),
offset_of: pub mk_offset_of((VariantIdx, FieldIdx)),
patterns: pub mk_patterns(Pattern<'tcx>),
+ outlives: pub mk_outlives(ty::ArgOutlivesPredicate<'tcx>),
);
impl<'tcx> TyCtxt<'tcx> {
@@ -3115,6 +3116,17 @@ pub fn mk_bound_variable_kinds_from_iter<I, T>(self, iter: I) -> T::Output
T::collect_and_apply(iter, |xs| self.mk_bound_variable_kinds(xs))
}
+ pub fn mk_outlives_from_iter<I, T>(self, iter: I) -> T::Output
+ where
+ I: Iterator<Item = T>,
+ T: CollectAndApply<
+ ty::ArgOutlivesPredicate<'tcx>,
+ &'tcx ty::List<ty::ArgOutlivesPredicate<'tcx>>,
+ >,
+ {
+ T::collect_and_apply(iter, |xs| self.mk_outlives(xs))
+ }
+
/// Emit a lint at `span` from a lint struct (some type that implements `LintDiagnostic`,
/// typically generated by `#[derive(LintDiagnostic)]`).
#[track_caller]
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 6e8f1e8..a7cde2a 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -88,7 +88,7 @@
pub use self::parameterized::ParameterizedOverTcx;
pub use self::pattern::{Pattern, PatternKind};
pub use self::predicate::{
- AliasTerm, Clause, ClauseKind, CoercePredicate, ExistentialPredicate,
+ AliasTerm, ArgOutlivesPredicate, Clause, ClauseKind, CoercePredicate, ExistentialPredicate,
ExistentialPredicateStableCmpExt, ExistentialProjection, ExistentialTraitRef,
HostEffectPredicate, NormalizesTo, OutlivesPredicate, PolyCoercePredicate,
PolyExistentialPredicate, PolyExistentialProjection, PolyExistentialTraitRef,
diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs
index ec22248..46f254e 100644
--- a/compiler/rustc_middle/src/ty/predicate.rs
+++ b/compiler/rustc_middle/src/ty/predicate.rs
@@ -26,6 +26,7 @@
pub type OutlivesPredicate<'tcx, T> = ir::OutlivesPredicate<TyCtxt<'tcx>, T>;
pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, ty::Region<'tcx>>;
pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, Ty<'tcx>>;
+pub type ArgOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>;
pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>;
pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder<'tcx, RegionOutlivesPredicate<'tcx>>;
pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder<'tcx, TypeOutlivesPredicate<'tcx>>;
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index af9c98b..ab31d94 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -802,4 +802,5 @@ fn fold_with<F: TypeFolder<TyCtxt<'tcx>>>(
&'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> : mk_poly_existential_predicates,
&'tcx ty::List<PlaceElem<'tcx>> : mk_place_elems,
&'tcx ty::List<ty::Pattern<'tcx>> : mk_patterns,
+ &'tcx ty::List<ty::ArgOutlivesPredicate<'tcx>> : mk_outlives,
}
diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs
index ccf76dc..986c001 100644
--- a/compiler/rustc_mir_transform/src/coverage/query.rs
+++ b/compiler/rustc_mir_transform/src/coverage/query.rs
@@ -1,3 +1,4 @@
+use rustc_attr_data_structures::{AttributeKind, CoverageStatus, find_attr};
use rustc_index::bit_set::DenseBitSet;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::coverage::{BasicCoverageBlock, CoverageIdsInfo, CoverageKind, MappingKind};
@@ -5,7 +6,6 @@
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::util::Providers;
use rustc_span::def_id::LocalDefId;
-use rustc_span::sym;
use tracing::trace;
use crate::coverage::counters::node_flow::make_node_counters;
@@ -58,26 +58,20 @@ fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
/// Query implementation for `coverage_attr_on`.
fn coverage_attr_on(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
// Check for annotations directly on this def.
- if let Some(attr) = tcx.get_attr(def_id, sym::coverage) {
- match attr.meta_item_list().as_deref() {
- Some([item]) if item.has_name(sym::off) => return false,
- Some([item]) if item.has_name(sym::on) => return true,
- Some(_) | None => {
- // Other possibilities should have been rejected by `rustc_parse::validate_attr`.
- // Use `span_delayed_bug` to avoid an ICE in failing builds (#127880).
- tcx.dcx().span_delayed_bug(attr.span(), "unexpected value of coverage attribute");
- }
+ if let Some(coverage_status) =
+ find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Coverage(_, status) => status)
+ {
+ *coverage_status == CoverageStatus::On
+ } else {
+ match tcx.opt_local_parent(def_id) {
+ // Check the parent def (and so on recursively) until we find an
+ // enclosing attribute or reach the crate root.
+ Some(parent) => tcx.coverage_attr_on(parent),
+ // We reached the crate root without seeing a coverage attribute, so
+ // allow coverage instrumentation by default.
+ None => true,
}
}
-
- match tcx.opt_local_parent(def_id) {
- // Check the parent def (and so on recursively) until we find an
- // enclosing attribute or reach the crate root.
- Some(parent) => tcx.coverage_attr_on(parent),
- // We reached the crate root without seeing a coverage attribute, so
- // allow coverage instrumentation by default.
- None => true,
- }
}
/// Query implementation for `coverage_ids_info`.
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index 6b11706..07717b7 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -105,7 +105,6 @@
use rustc_middle::ty::layout::HasTypingEnv;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::DUMMY_SP;
-use rustc_span::def_id::DefId;
use smallvec::SmallVec;
use tracing::{debug, instrument, trace};
@@ -130,7 +129,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let mut state = VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls);
for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) {
- let opaque = state.new_opaque();
+ let opaque = state.new_opaque(body.local_decls[local].ty);
state.assign(local, opaque);
}
@@ -155,22 +154,6 @@ fn is_required(&self) -> bool {
struct VnIndex {}
}
-/// Computing the aggregate's type can be quite slow, so we only keep the minimal amount of
-/// information to reconstruct it when needed.
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
-enum AggregateTy<'tcx> {
- /// Invariant: this must not be used for an empty array.
- Array,
- Tuple,
- Def(DefId, ty::GenericArgsRef<'tcx>),
- RawPtr {
- /// Needed for cast propagation.
- data_pointer_ty: Ty<'tcx>,
- /// The data pointer can be anything thin, so doesn't determine the output.
- output_pointer_ty: Ty<'tcx>,
- },
-}
-
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
enum AddressKind {
Ref(BorrowKind),
@@ -193,7 +176,14 @@ enum Value<'tcx> {
},
/// An aggregate value, either tuple/closure/struct/enum.
/// This does not contain unions, as we cannot reason with the value.
- Aggregate(AggregateTy<'tcx>, VariantIdx, Vec<VnIndex>),
+ Aggregate(VariantIdx, Vec<VnIndex>),
+ /// A raw pointer aggregate built from a thin pointer and metadata.
+ RawPtr {
+ /// Thin pointer component. This is field 0 in MIR.
+ pointer: VnIndex,
+ /// Metadata component. This is field 1 in MIR.
+ metadata: VnIndex,
+ },
/// This corresponds to a `[value; count]` expression.
Repeat(VnIndex, ty::Const<'tcx>),
/// The address of a place.
@@ -206,7 +196,7 @@ enum Value<'tcx> {
// Extractions.
/// This is the *value* obtained by projecting another value.
- Projection(VnIndex, ProjectionElem<VnIndex, Ty<'tcx>>),
+ Projection(VnIndex, ProjectionElem<VnIndex, ()>),
/// Discriminant of the given value.
Discriminant(VnIndex),
/// Length of an array or slice.
@@ -219,8 +209,6 @@ enum Value<'tcx> {
Cast {
kind: CastKind,
value: VnIndex,
- from: Ty<'tcx>,
- to: Ty<'tcx>,
},
}
@@ -228,12 +216,13 @@ struct VnState<'body, 'tcx> {
tcx: TyCtxt<'tcx>,
ecx: InterpCx<'tcx, DummyMachine>,
local_decls: &'body LocalDecls<'tcx>,
+ is_coroutine: bool,
/// Value stored in each local.
locals: IndexVec<Local, Option<VnIndex>>,
/// Locals that are assigned that value.
// This vector does not hold all the values of `VnIndex` that we create.
rev_locals: IndexVec<VnIndex, SmallVec<[Local; 1]>>,
- values: FxIndexSet<Value<'tcx>>,
+ values: FxIndexSet<(Value<'tcx>, Ty<'tcx>)>,
/// Values evaluated as constants if possible.
evaluated: IndexVec<VnIndex, Option<OpTy<'tcx>>>,
/// Counter to generate different values.
@@ -265,6 +254,7 @@ fn new(
tcx,
ecx: InterpCx::new(tcx, DUMMY_SP, typing_env, DummyMachine),
local_decls,
+ is_coroutine: body.coroutine.is_some(),
locals: IndexVec::from_elem(None, local_decls),
rev_locals: IndexVec::with_capacity(num_values),
values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()),
@@ -282,8 +272,8 @@ fn typing_env(&self) -> ty::TypingEnv<'tcx> {
}
#[instrument(level = "trace", skip(self), ret)]
- fn insert(&mut self, value: Value<'tcx>) -> VnIndex {
- let (index, new) = self.values.insert_full(value);
+ fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> VnIndex {
+ let (index, new) = self.values.insert_full((value, ty));
let index = VnIndex::from_usize(index);
if new {
// Grow `evaluated` and `rev_locals` here to amortize the allocations.
@@ -305,20 +295,33 @@ fn next_opaque(&mut self) -> usize {
/// Create a new `Value` for which we have no information at all, except that it is distinct
/// from all the others.
#[instrument(level = "trace", skip(self), ret)]
- fn new_opaque(&mut self) -> VnIndex {
+ fn new_opaque(&mut self, ty: Ty<'tcx>) -> VnIndex {
let value = Value::Opaque(self.next_opaque());
- self.insert(value)
+ self.insert(ty, value)
}
/// Create a new `Value::Address` distinct from all the others.
#[instrument(level = "trace", skip(self), ret)]
fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> VnIndex {
+ let pty = place.ty(self.local_decls, self.tcx).ty;
+ let ty = match kind {
+ AddressKind::Ref(bk) => {
+ Ty::new_ref(self.tcx, self.tcx.lifetimes.re_erased, pty, bk.to_mutbl_lossy())
+ }
+ AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, pty, mutbl.to_mutbl_lossy()),
+ };
let value = Value::Address { place, kind, provenance: self.next_opaque() };
- self.insert(value)
+ self.insert(ty, value)
}
+ #[inline]
fn get(&self, index: VnIndex) -> &Value<'tcx> {
- self.values.get_index(index.as_usize()).unwrap()
+ &self.values.get_index(index.as_usize()).unwrap().0
+ }
+
+ #[inline]
+ fn ty(&self, index: VnIndex) -> Ty<'tcx> {
+ self.values.get_index(index.as_usize()).unwrap().1
}
/// Record that `local` is assigned `value`. `local` must be SSA.
@@ -341,29 +344,29 @@ fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex {
debug_assert_ne!(disambiguator, 0);
disambiguator
};
- self.insert(Value::Constant { value, disambiguator })
+ self.insert(value.ty(), Value::Constant { value, disambiguator })
}
fn insert_bool(&mut self, flag: bool) -> VnIndex {
// Booleans are deterministic.
let value = Const::from_bool(self.tcx, flag);
debug_assert!(value.is_deterministic());
- self.insert(Value::Constant { value, disambiguator: 0 })
+ self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: 0 })
}
- fn insert_scalar(&mut self, scalar: Scalar, ty: Ty<'tcx>) -> VnIndex {
+ fn insert_scalar(&mut self, ty: Ty<'tcx>, scalar: Scalar) -> VnIndex {
// Scalars are deterministic.
let value = Const::from_scalar(self.tcx, scalar, ty);
debug_assert!(value.is_deterministic());
- self.insert(Value::Constant { value, disambiguator: 0 })
+ self.insert(ty, Value::Constant { value, disambiguator: 0 })
}
- fn insert_tuple(&mut self, values: Vec<VnIndex>) -> VnIndex {
- self.insert(Value::Aggregate(AggregateTy::Tuple, VariantIdx::ZERO, values))
+ fn insert_tuple(&mut self, ty: Ty<'tcx>, values: Vec<VnIndex>) -> VnIndex {
+ self.insert(ty, Value::Aggregate(VariantIdx::ZERO, values))
}
- fn insert_deref(&mut self, value: VnIndex) -> VnIndex {
- let value = self.insert(Value::Projection(value, ProjectionElem::Deref));
+ fn insert_deref(&mut self, ty: Ty<'tcx>, value: VnIndex) -> VnIndex {
+ let value = self.insert(ty, Value::Projection(value, ProjectionElem::Deref));
self.derefs.push(value);
value
}
@@ -371,14 +374,23 @@ fn insert_deref(&mut self, value: VnIndex) -> VnIndex {
fn invalidate_derefs(&mut self) {
for deref in std::mem::take(&mut self.derefs) {
let opaque = self.next_opaque();
- *self.values.get_index_mut2(deref.index()).unwrap() = Value::Opaque(opaque);
+ self.values.get_index_mut2(deref.index()).unwrap().0 = Value::Opaque(opaque);
}
}
#[instrument(level = "trace", skip(self), ret)]
fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
use Value::*;
+ let ty = self.ty(value);
+ // Avoid computing layouts inside a coroutine, as that can cause cycles.
+ let ty = if !self.is_coroutine || ty.is_scalar() {
+ self.ecx.layout_of(ty).ok()?
+ } else {
+ return None;
+ };
let op = match *self.get(value) {
+ _ if ty.is_zst() => ImmTy::uninit(ty).into(),
+
Opaque(_) => return None,
// Do not bother evaluating repeat expressions. This would uselessly consume memory.
Repeat(..) => return None,
@@ -386,42 +398,14 @@ fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
Constant { ref value, disambiguator: _ } => {
self.ecx.eval_mir_constant(value, DUMMY_SP, None).discard_err()?
}
- Aggregate(kind, variant, ref fields) => {
+ Aggregate(variant, ref fields) => {
let fields = fields
.iter()
.map(|&f| self.evaluated[f].as_ref())
.collect::<Option<Vec<_>>>()?;
- let ty = match kind {
- AggregateTy::Array => {
- assert!(fields.len() > 0);
- Ty::new_array(self.tcx, fields[0].layout.ty, fields.len() as u64)
- }
- AggregateTy::Tuple => {
- Ty::new_tup_from_iter(self.tcx, fields.iter().map(|f| f.layout.ty))
- }
- AggregateTy::Def(def_id, args) => {
- self.tcx.type_of(def_id).instantiate(self.tcx, args)
- }
- AggregateTy::RawPtr { output_pointer_ty, .. } => output_pointer_ty,
- };
- let variant = if ty.is_enum() { Some(variant) } else { None };
- let ty = self.ecx.layout_of(ty).ok()?;
- if ty.is_zst() {
- ImmTy::uninit(ty).into()
- } else if matches!(kind, AggregateTy::RawPtr { .. }) {
- // Pointers don't have fields, so don't `project_field` them.
- let data = self.ecx.read_pointer(fields[0]).discard_err()?;
- let meta = if fields[1].layout.is_zst() {
- MemPlaceMeta::None
- } else {
- MemPlaceMeta::Meta(self.ecx.read_scalar(fields[1]).discard_err()?)
- };
- let ptr_imm = Immediate::new_pointer_with_meta(data, meta, &self.ecx);
- ImmTy::from_immediate(ptr_imm, ty).into()
- } else if matches!(
- ty.backend_repr,
- BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)
- ) {
+ let variant = if ty.ty.is_enum() { Some(variant) } else { None };
+ if matches!(ty.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..))
+ {
let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?;
let variant_dest = if let Some(variant) = variant {
self.ecx.project_downcast(&dest, variant).discard_err()?
@@ -446,32 +430,46 @@ fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
return None;
}
}
+ RawPtr { pointer, metadata } => {
+ let pointer = self.evaluated[pointer].as_ref()?;
+ let metadata = self.evaluated[metadata].as_ref()?;
+
+ // Pointers don't have fields, so don't `project_field` them.
+ let data = self.ecx.read_pointer(pointer).discard_err()?;
+ let meta = if metadata.layout.is_zst() {
+ MemPlaceMeta::None
+ } else {
+ MemPlaceMeta::Meta(self.ecx.read_scalar(metadata).discard_err()?)
+ };
+ let ptr_imm = Immediate::new_pointer_with_meta(data, meta, &self.ecx);
+ ImmTy::from_immediate(ptr_imm, ty).into()
+ }
Projection(base, elem) => {
- let value = self.evaluated[base].as_ref()?;
+ let base = self.evaluated[base].as_ref()?;
let elem = match elem {
ProjectionElem::Deref => ProjectionElem::Deref,
ProjectionElem::Downcast(name, read_variant) => {
ProjectionElem::Downcast(name, read_variant)
}
- ProjectionElem::Field(f, ty) => ProjectionElem::Field(f, ty),
+ ProjectionElem::Field(f, ()) => ProjectionElem::Field(f, ty.ty),
ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
ProjectionElem::ConstantIndex { offset, min_length, from_end }
}
ProjectionElem::Subslice { from, to, from_end } => {
ProjectionElem::Subslice { from, to, from_end }
}
- ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty),
- ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty),
- ProjectionElem::UnwrapUnsafeBinder(ty) => {
- ProjectionElem::UnwrapUnsafeBinder(ty)
+ ProjectionElem::OpaqueCast(()) => ProjectionElem::OpaqueCast(ty.ty),
+ ProjectionElem::Subtype(()) => ProjectionElem::Subtype(ty.ty),
+ ProjectionElem::UnwrapUnsafeBinder(()) => {
+ ProjectionElem::UnwrapUnsafeBinder(ty.ty)
}
// This should have been replaced by a `ConstantIndex` earlier.
ProjectionElem::Index(_) => return None,
};
- self.ecx.project(value, elem).discard_err()?
+ self.ecx.project(base, elem).discard_err()?
}
- Address { place, kind, provenance: _ } => {
+ Address { place, kind: _, provenance: _ } => {
if !place.is_indirect_first_projection() {
return None;
}
@@ -487,19 +485,7 @@ fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
mplace = self.ecx.project(&mplace, proj).discard_err()?;
}
let pointer = mplace.to_ref(&self.ecx);
- let ty = match kind {
- AddressKind::Ref(bk) => Ty::new_ref(
- self.tcx,
- self.tcx.lifetimes.re_erased,
- mplace.layout.ty,
- bk.to_mutbl_lossy(),
- ),
- AddressKind::Address(mutbl) => {
- Ty::new_ptr(self.tcx, mplace.layout.ty, mutbl.to_mutbl_lossy())
- }
- };
- let layout = self.ecx.layout_of(ty).ok()?;
- ImmTy::from_immediate(pointer, layout).into()
+ ImmTy::from_immediate(pointer, ty).into()
}
Discriminant(base) => {
@@ -511,32 +497,28 @@ fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
}
Len(slice) => {
let slice = self.evaluated[slice].as_ref()?;
- let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
let len = slice.len(&self.ecx).discard_err()?;
- let imm = ImmTy::from_uint(len, usize_layout);
- imm.into()
+ ImmTy::from_uint(len, ty).into()
}
- NullaryOp(null_op, ty) => {
- let layout = self.ecx.layout_of(ty).ok()?;
+ NullaryOp(null_op, arg_ty) => {
+ let arg_layout = self.ecx.layout_of(arg_ty).ok()?;
if let NullOp::SizeOf | NullOp::AlignOf = null_op
- && layout.is_unsized()
+ && arg_layout.is_unsized()
{
return None;
}
let val = match null_op {
- NullOp::SizeOf => layout.size.bytes(),
- NullOp::AlignOf => layout.align.abi.bytes(),
+ NullOp::SizeOf => arg_layout.size.bytes(),
+ NullOp::AlignOf => arg_layout.align.abi.bytes(),
NullOp::OffsetOf(fields) => self
.ecx
.tcx
- .offset_of_subfield(self.typing_env(), layout, fields.iter())
+ .offset_of_subfield(self.typing_env(), arg_layout, fields.iter())
.bytes(),
NullOp::UbChecks => return None,
NullOp::ContractChecks => return None,
};
- let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
- let imm = ImmTy::from_uint(val, usize_layout);
- imm.into()
+ ImmTy::from_uint(val, ty).into()
}
UnaryOp(un_op, operand) => {
let operand = self.evaluated[operand].as_ref()?;
@@ -552,30 +534,27 @@ fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
let val = self.ecx.binary_op(bin_op, &lhs, &rhs).discard_err()?;
val.into()
}
- Cast { kind, value, from: _, to } => match kind {
+ Cast { kind, value } => match kind {
CastKind::IntToInt | CastKind::IntToFloat => {
let value = self.evaluated[value].as_ref()?;
let value = self.ecx.read_immediate(value).discard_err()?;
- let to = self.ecx.layout_of(to).ok()?;
- let res = self.ecx.int_to_int_or_float(&value, to).discard_err()?;
+ let res = self.ecx.int_to_int_or_float(&value, ty).discard_err()?;
res.into()
}
CastKind::FloatToFloat | CastKind::FloatToInt => {
let value = self.evaluated[value].as_ref()?;
let value = self.ecx.read_immediate(value).discard_err()?;
- let to = self.ecx.layout_of(to).ok()?;
- let res = self.ecx.float_to_float_or_int(&value, to).discard_err()?;
+ let res = self.ecx.float_to_float_or_int(&value, ty).discard_err()?;
res.into()
}
CastKind::Transmute => {
let value = self.evaluated[value].as_ref()?;
- let to = self.ecx.layout_of(to).ok()?;
// `offset` for immediates generally only supports projections that match the
// type of the immediate. However, as a HACK, we exploit that it can also do
// limited transmutes: it only works between types with the same layout, and
// cannot transmute pointers to integers.
if value.as_mplace_or_imm().is_right() {
- let can_transmute = match (value.layout.backend_repr, to.backend_repr) {
+ let can_transmute = match (value.layout.backend_repr, ty.backend_repr) {
(BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => {
s1.size(&self.ecx) == s2.size(&self.ecx)
&& !matches!(s1.primitive(), Primitive::Pointer(..))
@@ -595,13 +574,12 @@ fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
return None;
}
}
- value.offset(Size::ZERO, to, &self.ecx).discard_err()?
+ value.offset(Size::ZERO, ty, &self.ecx).discard_err()?
}
CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) => {
let src = self.evaluated[value].as_ref()?;
- let to = self.ecx.layout_of(to).ok()?;
- let dest = self.ecx.allocate(to, MemoryKind::Stack).discard_err()?;
- self.ecx.unsize_into(src, to, &dest.clone().into()).discard_err()?;
+ let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?;
+ self.ecx.unsize_into(src, ty, &dest.clone().into()).discard_err()?;
self.ecx
.alloc_mark_immutable(dest.ptr().provenance.unwrap().alloc_id())
.discard_err()?;
@@ -610,15 +588,13 @@ fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
CastKind::FnPtrToPtr | CastKind::PtrToPtr => {
let src = self.evaluated[value].as_ref()?;
let src = self.ecx.read_immediate(src).discard_err()?;
- let to = self.ecx.layout_of(to).ok()?;
- let ret = self.ecx.ptr_to_ptr(&src, to).discard_err()?;
+ let ret = self.ecx.ptr_to_ptr(&src, ty).discard_err()?;
ret.into()
}
CastKind::PointerCoercion(ty::adjustment::PointerCoercion::UnsafeFnPointer, _) => {
let src = self.evaluated[value].as_ref()?;
let src = self.ecx.read_immediate(src).discard_err()?;
- let to = self.ecx.layout_of(to).ok()?;
- ImmTy::from_immediate(*src, to).into()
+ ImmTy::from_immediate(*src, ty).into()
}
_ => return None,
},
@@ -628,31 +604,30 @@ fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
fn project(
&mut self,
- place: PlaceRef<'tcx>,
+ place_ty: PlaceTy<'tcx>,
value: VnIndex,
proj: PlaceElem<'tcx>,
from_non_ssa_index: &mut bool,
- ) -> Option<VnIndex> {
+ ) -> Option<(PlaceTy<'tcx>, VnIndex)> {
+ let projection_ty = place_ty.projection_ty(self.tcx, proj);
let proj = match proj {
ProjectionElem::Deref => {
- let ty = place.ty(self.local_decls, self.tcx).ty;
- if let Some(Mutability::Not) = ty.ref_mutability()
- && let Some(pointee_ty) = ty.builtin_deref(true)
- && pointee_ty.is_freeze(self.tcx, self.typing_env())
+ if let Some(Mutability::Not) = place_ty.ty.ref_mutability()
+ && projection_ty.ty.is_freeze(self.tcx, self.typing_env())
{
// An immutable borrow `_x` always points to the same value for the
// lifetime of the borrow, so we can merge all instances of `*_x`.
- return Some(self.insert_deref(value));
+ return Some((projection_ty, self.insert_deref(projection_ty.ty, value)));
} else {
return None;
}
}
ProjectionElem::Downcast(name, index) => ProjectionElem::Downcast(name, index),
- ProjectionElem::Field(f, ty) => {
- if let Value::Aggregate(_, _, fields) = self.get(value) {
- return Some(fields[f.as_usize()]);
+ ProjectionElem::Field(f, _) => {
+ if let Value::Aggregate(_, fields) = self.get(value) {
+ return Some((projection_ty, fields[f.as_usize()]));
} else if let Value::Projection(outer_value, ProjectionElem::Downcast(_, read_variant)) = self.get(value)
- && let Value::Aggregate(_, written_variant, fields) = self.get(*outer_value)
+ && let Value::Aggregate(written_variant, fields) = self.get(*outer_value)
// This pass is not aware of control-flow, so we do not know whether the
// replacement we are doing is actually reachable. We could be in any arm of
// ```
@@ -670,14 +645,14 @@ fn project(
// a downcast to an inactive variant.
&& written_variant == read_variant
{
- return Some(fields[f.as_usize()]);
+ return Some((projection_ty, fields[f.as_usize()]));
}
- ProjectionElem::Field(f, ty)
+ ProjectionElem::Field(f, ())
}
ProjectionElem::Index(idx) => {
if let Value::Repeat(inner, _) = self.get(value) {
*from_non_ssa_index |= self.locals[idx].is_none();
- return Some(*inner);
+ return Some((projection_ty, *inner));
}
let idx = self.locals[idx]?;
ProjectionElem::Index(idx)
@@ -685,15 +660,16 @@ fn project(
ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
match self.get(value) {
Value::Repeat(inner, _) => {
- return Some(*inner);
+ return Some((projection_ty, *inner));
}
- Value::Aggregate(AggregateTy::Array, _, operands) => {
+ Value::Aggregate(_, operands) => {
let offset = if from_end {
operands.len() - offset as usize
} else {
offset as usize
};
- return operands.get(offset).copied();
+ let value = operands.get(offset).copied()?;
+ return Some((projection_ty, value));
}
_ => {}
};
@@ -702,12 +678,13 @@ fn project(
ProjectionElem::Subslice { from, to, from_end } => {
ProjectionElem::Subslice { from, to, from_end }
}
- ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty),
- ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty),
- ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(ty),
+ ProjectionElem::OpaqueCast(_) => ProjectionElem::OpaqueCast(()),
+ ProjectionElem::Subtype(_) => ProjectionElem::Subtype(()),
+ ProjectionElem::UnwrapUnsafeBinder(_) => ProjectionElem::UnwrapUnsafeBinder(()),
};
- Some(self.insert(Value::Projection(value, proj)))
+ let value = self.insert(projection_ty.ty, Value::Projection(value, proj));
+ Some((projection_ty, value))
}
/// Simplify the projection chain if we know better.
@@ -769,6 +746,8 @@ fn simplify_place_value(
// Invariant: `value` holds the value up-to the `index`th projection excluded.
let mut value = self.locals[place.local]?;
+ // Invariant: `value` has type `place_ty`, with optional downcast variant if needed.
+ let mut place_ty = PlaceTy::from_ty(self.local_decls[place.local].ty);
let mut from_non_ssa_index = false;
for (index, proj) in place.projection.iter().enumerate() {
if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value)
@@ -786,8 +765,7 @@ fn simplify_place_value(
place_ref = PlaceRef { local, projection: &place.projection[index..] };
}
- let base = PlaceRef { local: place.local, projection: &place.projection[..index] };
- value = self.project(base, value, proj, &mut from_non_ssa_index)?;
+ (place_ty, value) = self.project(place_ty, value, proj, &mut from_non_ssa_index)?;
}
if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value)
@@ -864,14 +842,9 @@ fn simplify_rvalue(
self.simplify_place_projection(place, location);
return Some(self.new_pointer(*place, AddressKind::Address(mutbl)));
}
- Rvalue::WrapUnsafeBinder(ref mut op, ty) => {
+ Rvalue::WrapUnsafeBinder(ref mut op, _) => {
let value = self.simplify_operand(op, location)?;
- Value::Cast {
- kind: CastKind::Transmute,
- value,
- from: op.ty(self.local_decls, self.tcx),
- to: ty,
- }
+ Value::Cast { kind: CastKind::Transmute, value }
}
// Operations.
@@ -896,18 +869,17 @@ fn simplify_rvalue(
// Unsupported values.
Rvalue::ThreadLocalRef(..) | Rvalue::ShallowInitBox(..) => return None,
};
- debug!(?value);
- Some(self.insert(value))
+ let ty = rvalue.ty(self.local_decls, self.tcx);
+ Some(self.insert(ty, value))
}
fn simplify_discriminant(&mut self, place: VnIndex) -> Option<VnIndex> {
- if let Value::Aggregate(enum_ty, variant, _) = *self.get(place)
- && let AggregateTy::Def(enum_did, enum_args) = enum_ty
- && let DefKind::Enum = self.tcx.def_kind(enum_did)
+ let enum_ty = self.ty(place);
+ if enum_ty.is_enum()
+ && let Value::Aggregate(variant, _) = *self.get(place)
{
- let enum_ty = self.tcx.type_of(enum_did).instantiate(self.tcx, enum_args);
let discr = self.ecx.discriminant_for_variant(enum_ty, variant).discard_err()?;
- return Some(self.insert_scalar(discr.to_scalar(), discr.layout.ty));
+ return Some(self.insert_scalar(discr.layout.ty, discr.to_scalar()));
}
None
@@ -915,12 +887,13 @@ fn simplify_discriminant(&mut self, place: VnIndex) -> Option<VnIndex> {
fn try_as_place_elem(
&mut self,
- proj: ProjectionElem<VnIndex, Ty<'tcx>>,
+ ty: Ty<'tcx>,
+ proj: ProjectionElem<VnIndex, ()>,
loc: Location,
) -> Option<PlaceElem<'tcx>> {
Some(match proj {
ProjectionElem::Deref => ProjectionElem::Deref,
- ProjectionElem::Field(idx, ty) => ProjectionElem::Field(idx, ty),
+ ProjectionElem::Field(idx, ()) => ProjectionElem::Field(idx, ty),
ProjectionElem::Index(idx) => {
let Some(local) = self.try_as_local(idx, loc) else {
return None;
@@ -935,9 +908,9 @@ fn try_as_place_elem(
ProjectionElem::Subslice { from, to, from_end }
}
ProjectionElem::Downcast(symbol, idx) => ProjectionElem::Downcast(symbol, idx),
- ProjectionElem::OpaqueCast(idx) => ProjectionElem::OpaqueCast(idx),
- ProjectionElem::Subtype(idx) => ProjectionElem::Subtype(idx),
- ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(ty),
+ ProjectionElem::OpaqueCast(()) => ProjectionElem::OpaqueCast(ty),
+ ProjectionElem::Subtype(()) => ProjectionElem::Subtype(ty),
+ ProjectionElem::UnwrapUnsafeBinder(()) => ProjectionElem::UnwrapUnsafeBinder(ty),
})
}
@@ -983,8 +956,8 @@ fn simplify_aggregate_to_copy(
// Allow introducing places with non-constant offsets, as those are still better than
// reconstructing an aggregate.
- if let Some(place) = self.try_as_place(copy_from_local_value, location, true)
- && rvalue.ty(self.local_decls, self.tcx) == place.ty(self.local_decls, self.tcx).ty
+ if self.ty(copy_from_local_value) == rvalue.ty(self.local_decls, self.tcx)
+ && let Some(place) = self.try_as_place(copy_from_local_value, location, true)
{
// Avoid creating `*a = copy (*b)`, as they might be aliases resulting in overlapping assignments.
// FIXME: This also avoids any kind of projection, not just derefs. We can add allowed projections.
@@ -1004,9 +977,11 @@ fn simplify_aggregate(
rvalue: &mut Rvalue<'tcx>,
location: Location,
) -> Option<VnIndex> {
+ let tcx = self.tcx;
+ let ty = rvalue.ty(self.local_decls, tcx);
+
let Rvalue::Aggregate(box ref kind, ref mut field_ops) = *rvalue else { bug!() };
- let tcx = self.tcx;
if field_ops.is_empty() {
let is_zst = match *kind {
AggregateKind::Array(..)
@@ -1021,87 +996,72 @@ fn simplify_aggregate(
};
if is_zst {
- let ty = rvalue.ty(self.local_decls, tcx);
return Some(self.insert_constant(Const::zero_sized(ty)));
}
}
- let (mut ty, variant_index) = match *kind {
- AggregateKind::Array(..) => {
+ let fields: Vec<_> = field_ops
+ .iter_mut()
+ .map(|op| {
+ self.simplify_operand(op, location)
+ .unwrap_or_else(|| self.new_opaque(op.ty(self.local_decls, self.tcx)))
+ })
+ .collect();
+
+ let variant_index = match *kind {
+ AggregateKind::Array(..) | AggregateKind::Tuple => {
assert!(!field_ops.is_empty());
- (AggregateTy::Array, FIRST_VARIANT)
+ FIRST_VARIANT
}
- AggregateKind::Tuple => {
- assert!(!field_ops.is_empty());
- (AggregateTy::Tuple, FIRST_VARIANT)
- }
- AggregateKind::Closure(did, args)
- | AggregateKind::CoroutineClosure(did, args)
- | AggregateKind::Coroutine(did, args) => (AggregateTy::Def(did, args), FIRST_VARIANT),
- AggregateKind::Adt(did, variant_index, args, _, None) => {
- (AggregateTy::Def(did, args), variant_index)
- }
+ AggregateKind::Closure(..)
+ | AggregateKind::CoroutineClosure(..)
+ | AggregateKind::Coroutine(..) => FIRST_VARIANT,
+ AggregateKind::Adt(_, variant_index, _, _, None) => variant_index,
// Do not track unions.
AggregateKind::Adt(_, _, _, _, Some(_)) => return None,
- AggregateKind::RawPtr(pointee_ty, mtbl) => {
+ AggregateKind::RawPtr(..) => {
assert_eq!(field_ops.len(), 2);
- let data_pointer_ty = field_ops[FieldIdx::ZERO].ty(self.local_decls, self.tcx);
- let output_pointer_ty = Ty::new_ptr(self.tcx, pointee_ty, mtbl);
- (AggregateTy::RawPtr { data_pointer_ty, output_pointer_ty }, FIRST_VARIANT)
+ let [mut pointer, metadata] = fields.try_into().unwrap();
+
+ // Any thin pointer of matching mutability is fine as the data pointer.
+ let mut was_updated = false;
+ while let Value::Cast { kind: CastKind::PtrToPtr, value: cast_value } =
+ self.get(pointer)
+ && let ty::RawPtr(from_pointee_ty, from_mtbl) = self.ty(*cast_value).kind()
+ && let ty::RawPtr(_, output_mtbl) = ty.kind()
+ && from_mtbl == output_mtbl
+ && from_pointee_ty.is_sized(self.tcx, self.typing_env())
+ {
+ pointer = *cast_value;
+ was_updated = true;
+ }
+
+ if was_updated && let Some(op) = self.try_as_operand(pointer, location) {
+ field_ops[FieldIdx::ZERO] = op;
+ }
+
+ return Some(self.insert(ty, Value::RawPtr { pointer, metadata }));
}
};
- let mut fields: Vec<_> = field_ops
- .iter_mut()
- .map(|op| self.simplify_operand(op, location).unwrap_or_else(|| self.new_opaque()))
- .collect();
-
- if let AggregateTy::RawPtr { data_pointer_ty, output_pointer_ty } = &mut ty {
- let mut was_updated = false;
-
- // Any thin pointer of matching mutability is fine as the data pointer.
- while let Value::Cast {
- kind: CastKind::PtrToPtr,
- value: cast_value,
- from: cast_from,
- to: _,
- } = self.get(fields[0])
- && let ty::RawPtr(from_pointee_ty, from_mtbl) = cast_from.kind()
- && let ty::RawPtr(_, output_mtbl) = output_pointer_ty.kind()
- && from_mtbl == output_mtbl
- && from_pointee_ty.is_sized(self.tcx, self.typing_env())
- {
- fields[0] = *cast_value;
- *data_pointer_ty = *cast_from;
- was_updated = true;
- }
-
- if was_updated && let Some(op) = self.try_as_operand(fields[0], location) {
- field_ops[FieldIdx::ZERO] = op;
- }
- }
-
- if let AggregateTy::Array = ty
- && fields.len() > 4
- {
+ if ty.is_array() && fields.len() > 4 {
let first = fields[0];
if fields.iter().all(|&v| v == first) {
let len = ty::Const::from_target_usize(self.tcx, fields.len().try_into().unwrap());
if let Some(op) = self.try_as_operand(first, location) {
*rvalue = Rvalue::Repeat(op, len);
}
- return Some(self.insert(Value::Repeat(first, len)));
+ return Some(self.insert(ty, Value::Repeat(first, len)));
}
}
- if let AggregateTy::Def(_, _) = ty
- && let Some(value) =
- self.simplify_aggregate_to_copy(lhs, rvalue, location, &fields, variant_index)
+ if let Some(value) =
+ self.simplify_aggregate_to_copy(lhs, rvalue, location, &fields, variant_index)
{
return Some(value);
}
- Some(self.insert(Value::Aggregate(ty, variant_index, fields)))
+ Some(self.insert(ty, Value::Aggregate(variant_index, fields)))
}
#[instrument(level = "trace", skip(self), ret)]
@@ -1112,6 +1072,8 @@ fn simplify_unary(
location: Location,
) -> Option<VnIndex> {
let mut arg_index = self.simplify_operand(arg_op, location)?;
+ let arg_ty = self.ty(arg_index);
+ let ret_ty = op.ty(self.tcx, arg_ty);
// PtrMetadata doesn't care about *const vs *mut vs & vs &mut,
// so start by removing those distinctions so we can update the `Operand`
@@ -1127,8 +1089,8 @@ fn simplify_unary(
// we can't always know exactly what the metadata are.
// To allow things like `*mut (?A, ?T)` <-> `*mut (?B, ?T)`,
// it's fine to get a projection as the type.
- Value::Cast { kind: CastKind::PtrToPtr, value: inner, from, to }
- if self.pointers_have_same_metadata(*from, *to) =>
+ Value::Cast { kind: CastKind::PtrToPtr, value: inner }
+ if self.pointers_have_same_metadata(self.ty(*inner), arg_ty) =>
{
arg_index = *inner;
was_updated = true;
@@ -1165,26 +1127,22 @@ fn simplify_unary(
(UnOp::Not, Value::BinaryOp(BinOp::Ne, lhs, rhs)) => {
Value::BinaryOp(BinOp::Eq, *lhs, *rhs)
}
- (UnOp::PtrMetadata, Value::Aggregate(AggregateTy::RawPtr { .. }, _, fields)) => {
- return Some(fields[1]);
- }
+ (UnOp::PtrMetadata, Value::RawPtr { metadata, .. }) => return Some(*metadata),
// We have an unsizing cast, which assigns the length to wide pointer metadata.
(
UnOp::PtrMetadata,
Value::Cast {
kind: CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _),
- from,
- to,
- ..
+ value: inner,
},
- ) if let ty::Slice(..) = to.builtin_deref(true).unwrap().kind()
- && let ty::Array(_, len) = from.builtin_deref(true).unwrap().kind() =>
+ ) if let ty::Slice(..) = arg_ty.builtin_deref(true).unwrap().kind()
+ && let ty::Array(_, len) = self.ty(*inner).builtin_deref(true).unwrap().kind() =>
{
return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len)));
}
_ => Value::UnaryOp(op, arg_index),
};
- Some(self.insert(value))
+ Some(self.insert(ret_ty, value))
}
#[instrument(level = "trace", skip(self), ret)]
@@ -1197,25 +1155,23 @@ fn simplify_binary(
) -> Option<VnIndex> {
let lhs = self.simplify_operand(lhs_operand, location);
let rhs = self.simplify_operand(rhs_operand, location);
+
// Only short-circuit options after we called `simplify_operand`
// on both operands for side effect.
let mut lhs = lhs?;
let mut rhs = rhs?;
- let lhs_ty = lhs_operand.ty(self.local_decls, self.tcx);
+ let lhs_ty = self.ty(lhs);
// If we're comparing pointers, remove `PtrToPtr` casts if the from
// types of both casts and the metadata all match.
if let BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge = op
&& lhs_ty.is_any_ptr()
- && let Value::Cast {
- kind: CastKind::PtrToPtr, value: lhs_value, from: lhs_from, ..
- } = self.get(lhs)
- && let Value::Cast {
- kind: CastKind::PtrToPtr, value: rhs_value, from: rhs_from, ..
- } = self.get(rhs)
- && lhs_from == rhs_from
- && self.pointers_have_same_metadata(*lhs_from, lhs_ty)
+ && let Value::Cast { kind: CastKind::PtrToPtr, value: lhs_value } = self.get(lhs)
+ && let Value::Cast { kind: CastKind::PtrToPtr, value: rhs_value } = self.get(rhs)
+ && let lhs_from = self.ty(*lhs_value)
+ && lhs_from == self.ty(*rhs_value)
+ && self.pointers_have_same_metadata(lhs_from, lhs_ty)
{
lhs = *lhs_value;
rhs = *rhs_value;
@@ -1230,8 +1186,9 @@ fn simplify_binary(
if let Some(value) = self.simplify_binary_inner(op, lhs_ty, lhs, rhs) {
return Some(value);
}
+ let ty = op.ty(self.tcx, lhs_ty, self.ty(rhs));
let value = Value::BinaryOp(op, lhs, rhs);
- Some(self.insert(value))
+ Some(self.insert(ty, value))
}
fn simplify_binary_inner(
@@ -1323,19 +1280,19 @@ fn simplify_binary_inner(
| BinOp::Shr,
Left(0),
_,
- ) => self.insert_scalar(Scalar::from_uint(0u128, layout.size), lhs_ty),
+ ) => self.insert_scalar(lhs_ty, Scalar::from_uint(0u128, layout.size)),
// Attempt to simplify `x | ALL_ONES` to `ALL_ONES`.
(BinOp::BitOr, _, Left(ones)) | (BinOp::BitOr, Left(ones), _)
if ones == layout.size.truncate(u128::MAX)
|| (layout.ty.is_bool() && ones == 1) =>
{
- self.insert_scalar(Scalar::from_uint(ones, layout.size), lhs_ty)
+ self.insert_scalar(lhs_ty, Scalar::from_uint(ones, layout.size))
}
// Sub/Xor with itself.
(BinOp::Sub | BinOp::SubWithOverflow | BinOp::SubUnchecked | BinOp::BitXor, a, b)
if a == b =>
{
- self.insert_scalar(Scalar::from_uint(0u128, layout.size), lhs_ty)
+ self.insert_scalar(lhs_ty, Scalar::from_uint(0u128, layout.size))
}
// Comparison:
// - if both operands can be computed as bits, just compare the bits;
@@ -1349,8 +1306,9 @@ fn simplify_binary_inner(
};
if op.is_overflowing() {
+ let ty = Ty::new_tup(self.tcx, &[self.ty(result), self.tcx.types.bool]);
let false_val = self.insert_bool(false);
- Some(self.insert_tuple(vec![result, false_val]))
+ Some(self.insert_tuple(ty, vec![result, false_val]))
} else {
Some(result)
}
@@ -1366,9 +1324,9 @@ fn simplify_cast(
use CastKind::*;
use rustc_middle::ty::adjustment::PointerCoercion::*;
- let mut from = initial_operand.ty(self.local_decls, self.tcx);
let mut kind = *initial_kind;
let mut value = self.simplify_operand(initial_operand, location)?;
+ let mut from = self.ty(value);
if from == to {
return Some(value);
}
@@ -1376,7 +1334,7 @@ fn simplify_cast(
if let CastKind::PointerCoercion(ReifyFnPointer | ClosureFnPointer(_), _) = kind {
// Each reification of a generic fn may get a different pointer.
// Do not try to merge them.
- return Some(self.new_opaque());
+ return Some(self.new_opaque(to));
}
let mut was_ever_updated = false;
@@ -1399,23 +1357,22 @@ fn simplify_cast(
// If a cast just casts away the metadata again, then we can get it by
// casting the original thin pointer passed to `from_raw_parts`
if let PtrToPtr = kind
- && let Value::Aggregate(AggregateTy::RawPtr { data_pointer_ty, .. }, _, fields) =
- self.get(value)
+ && let Value::RawPtr { pointer, .. } = self.get(value)
&& let ty::RawPtr(to_pointee, _) = to.kind()
&& to_pointee.is_sized(self.tcx, self.typing_env())
{
- from = *data_pointer_ty;
- value = fields[0];
+ from = self.ty(*pointer);
+ value = *pointer;
was_updated_this_iteration = true;
- if *data_pointer_ty == to {
- return Some(fields[0]);
+ if from == to {
+ return Some(*pointer);
}
}
// Aggregate-then-Transmute can just transmute the original field value,
// so long as the bytes of a value from only from a single field.
if let Transmute = kind
- && let Value::Aggregate(_aggregate_ty, variant_idx, field_values) = self.get(value)
+ && let Value::Aggregate(variant_idx, field_values) = self.get(value)
&& let Some((field_idx, field_ty)) =
self.value_is_all_in_one_field(from, *variant_idx)
{
@@ -1428,13 +1385,8 @@ fn simplify_cast(
}
// Various cast-then-cast cases can be simplified.
- if let Value::Cast {
- kind: inner_kind,
- value: inner_value,
- from: inner_from,
- to: inner_to,
- } = *self.get(value)
- {
+ if let Value::Cast { kind: inner_kind, value: inner_value } = *self.get(value) {
+ let inner_from = self.ty(inner_value);
let new_kind = match (inner_kind, kind) {
// Even if there's a narrowing cast in here that's fine, because
// things like `*mut [i32] -> *mut i32 -> *const i32` and
@@ -1443,9 +1395,7 @@ fn simplify_cast(
// PtrToPtr-then-Transmute is fine so long as the pointer cast is identity:
// `*const T -> *mut T -> NonNull<T>` is fine, but we need to check for narrowing
// to skip things like `*const [i32] -> *const i32 -> NonNull<T>`.
- (PtrToPtr, Transmute)
- if self.pointers_have_same_metadata(inner_from, inner_to) =>
- {
+ (PtrToPtr, Transmute) if self.pointers_have_same_metadata(inner_from, from) => {
Some(Transmute)
}
// Similarly, for Transmute-then-PtrToPtr. Note that we need to check different
@@ -1456,7 +1406,7 @@ fn simplify_cast(
// If would be legal to always do this, but we don't want to hide information
// from the backend that it'd otherwise be able to use for optimizations.
(Transmute, Transmute)
- if !self.type_may_have_niche_of_interest_to_backend(inner_to) =>
+ if !self.type_may_have_niche_of_interest_to_backend(from) =>
{
Some(Transmute)
}
@@ -1485,7 +1435,7 @@ fn simplify_cast(
*initial_kind = kind;
}
- Some(self.insert(Value::Cast { kind, value, from, to }))
+ Some(self.insert(to, Value::Cast { kind, value }))
}
fn simplify_len(&mut self, place: &mut Place<'tcx>, location: Location) -> Option<VnIndex> {
@@ -1507,18 +1457,18 @@ fn simplify_len(&mut self, place: &mut Place<'tcx>, location: Location) -> Optio
}
// We have an unsizing cast, which assigns the length to wide pointer metadata.
- if let Value::Cast { kind, from, to, .. } = self.get(inner)
+ if let Value::Cast { kind, value: from } = self.get(inner)
&& let CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) = kind
- && let Some(from) = from.builtin_deref(true)
+ && let Some(from) = self.ty(*from).builtin_deref(true)
&& let ty::Array(_, len) = from.kind()
- && let Some(to) = to.builtin_deref(true)
+ && let Some(to) = self.ty(inner).builtin_deref(true)
&& let ty::Slice(..) = to.kind()
{
return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len)));
}
// Fallback: a symbolic `Len`.
- Some(self.insert(Value::Len(inner)))
+ Some(self.insert(self.tcx.types.usize, Value::Len(inner)))
}
fn pointers_have_same_metadata(&self, left_ptr_ty: Ty<'tcx>, right_ptr_ty: Ty<'tcx>) -> bool {
@@ -1727,7 +1677,7 @@ fn try_as_place(
return Some(place);
} else if let Value::Projection(pointer, proj) = *self.get(index)
&& (allow_complex_projection || proj.is_stable_offset())
- && let Some(proj) = self.try_as_place_elem(proj, loc)
+ && let Some(proj) = self.try_as_place_elem(self.ty(index), proj, loc)
{
projection.push(proj);
index = pointer;
@@ -1755,7 +1705,7 @@ fn tcx(&self) -> TyCtxt<'tcx> {
fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
self.simplify_place_projection(place, location);
- if context.is_mutating_use() && !place.projection.is_empty() {
+ if context.is_mutating_use() && place.is_indirect() {
// Non-local mutation maybe invalidate deref.
self.invalidate_derefs();
}
@@ -1767,36 +1717,42 @@ fn visit_operand(&mut self, operand: &mut Operand<'tcx>, location: Location) {
self.super_operand(operand, location);
}
- fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, location: Location) {
- if let StatementKind::Assign(box (ref mut lhs, ref mut rvalue)) = stmt.kind {
- self.simplify_place_projection(lhs, location);
+ fn visit_assign(
+ &mut self,
+ lhs: &mut Place<'tcx>,
+ rvalue: &mut Rvalue<'tcx>,
+ location: Location,
+ ) {
+ self.simplify_place_projection(lhs, location);
- let value = self.simplify_rvalue(lhs, rvalue, location);
- let value = if let Some(local) = lhs.as_local()
- && self.ssa.is_ssa(local)
- // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark
- // `local` as reusable if we have an exact type match.
- && self.local_decls[local].ty == rvalue.ty(self.local_decls, self.tcx)
+ let value = self.simplify_rvalue(lhs, rvalue, location);
+ if let Some(value) = value {
+ if let Some(const_) = self.try_as_constant(value) {
+ *rvalue = Rvalue::Use(Operand::Constant(Box::new(const_)));
+ } else if let Some(place) = self.try_as_place(value, location, false)
+ && *rvalue != Rvalue::Use(Operand::Move(place))
+ && *rvalue != Rvalue::Use(Operand::Copy(place))
{
- let value = value.unwrap_or_else(|| self.new_opaque());
- self.assign(local, value);
- Some(value)
- } else {
- value
- };
- if let Some(value) = value {
- if let Some(const_) = self.try_as_constant(value) {
- *rvalue = Rvalue::Use(Operand::Constant(Box::new(const_)));
- } else if let Some(place) = self.try_as_place(value, location, false)
- && *rvalue != Rvalue::Use(Operand::Move(place))
- && *rvalue != Rvalue::Use(Operand::Copy(place))
- {
- *rvalue = Rvalue::Use(Operand::Copy(place));
- self.reused_locals.insert(place.local);
- }
+ *rvalue = Rvalue::Use(Operand::Copy(place));
+ self.reused_locals.insert(place.local);
}
}
- self.super_statement(stmt, location);
+
+ if lhs.is_indirect() {
+ // Non-local mutation maybe invalidate deref.
+ self.invalidate_derefs();
+ }
+
+ if let Some(local) = lhs.as_local()
+ && self.ssa.is_ssa(local)
+ && let rvalue_ty = rvalue.ty(self.local_decls, self.tcx)
+ // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark
+ // `local` as reusable if we have an exact type match.
+ && self.local_decls[local].ty == rvalue_ty
+ {
+ let value = value.unwrap_or_else(|| self.new_opaque(rvalue_ty));
+ self.assign(local, value);
+ }
}
fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
@@ -1804,7 +1760,8 @@ fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Loca
if let Some(local) = destination.as_local()
&& self.ssa.is_ssa(local)
{
- let opaque = self.new_opaque();
+ let ty = self.local_decls[local].ty;
+ let opaque = self.new_opaque(ty);
self.assign(local, opaque);
}
}
diff --git a/compiler/rustc_parse/src/lexer/diagnostics.rs b/compiler/rustc_parse/src/lexer/diagnostics.rs
index 0b97d4e..6de001f 100644
--- a/compiler/rustc_parse/src/lexer/diagnostics.rs
+++ b/compiler/rustc_parse/src/lexer/diagnostics.rs
@@ -34,9 +34,12 @@ pub(super) fn same_indentation_level(sm: &SourceMap, open_sp: Span, close_sp: Sp
// When we get a `)` or `]` for `{`, we should emit help message here
// it's more friendly compared to report `unmatched error` in later phase
-fn report_missing_open_delim(err: &mut Diag<'_>, unmatched_delims: &[UnmatchedDelim]) -> bool {
+pub(super) fn report_missing_open_delim(
+ err: &mut Diag<'_>,
+ unmatched_delims: &mut Vec<UnmatchedDelim>,
+) -> bool {
let mut reported_missing_open = false;
- for unmatch_brace in unmatched_delims.iter() {
+ unmatched_delims.retain(|unmatch_brace| {
if let Some(delim) = unmatch_brace.found_delim
&& matches!(delim, Delimiter::Parenthesis | Delimiter::Bracket)
{
@@ -45,13 +48,20 @@ fn report_missing_open_delim(err: &mut Diag<'_>, unmatched_delims: &[UnmatchedDe
Delimiter::Bracket => "[",
_ => unreachable!(),
};
+
+ if let Some(unclosed_span) = unmatch_brace.unclosed_span {
+ err.span_label(unclosed_span, "the nearest open delimiter");
+ }
err.span_label(
unmatch_brace.found_span.shrink_to_lo(),
format!("missing open `{missed_open}` for this delimiter"),
);
reported_missing_open = true;
+ false
+ } else {
+ true
}
- }
+ });
reported_missing_open
}
@@ -61,10 +71,6 @@ pub(super) fn report_suspicious_mismatch_block(
sm: &SourceMap,
delim: Delimiter,
) {
- if report_missing_open_delim(err, &diag_info.unmatched_delims) {
- return;
- }
-
let mut matched_spans: Vec<(Span, bool)> = diag_info
.matching_block_spans
.iter()
diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs
index fbea958..6474819 100644
--- a/compiler/rustc_parse/src/lexer/tokentrees.rs
+++ b/compiler/rustc_parse/src/lexer/tokentrees.rs
@@ -3,7 +3,9 @@
use rustc_ast_pretty::pprust::token_to_string;
use rustc_errors::Diag;
-use super::diagnostics::{report_suspicious_mismatch_block, same_indentation_level};
+use super::diagnostics::{
+ report_missing_open_delim, report_suspicious_mismatch_block, same_indentation_level,
+};
use super::{Lexer, UnmatchedDelim};
impl<'psess, 'src> Lexer<'psess, 'src> {
@@ -244,7 +246,16 @@ fn close_delim_err(&mut self, delim: Delimiter) -> Diag<'psess> {
let msg = format!("unexpected closing delimiter: `{token_str}`");
let mut err = self.dcx().struct_span_err(self.token.span, msg);
- report_suspicious_mismatch_block(&mut err, &self.diag_info, self.psess.source_map(), delim);
+ // if there is no missing open delim, report suspicious mismatch block
+ if !report_missing_open_delim(&mut err, &mut self.diag_info.unmatched_delims) {
+ report_suspicious_mismatch_block(
+ &mut err,
+ &self.diag_info,
+ self.psess.source_map(),
+ delim,
+ );
+ }
+
err.span_label(self.token.span, "unexpected closing delimiter");
err
}
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index 783d79d..a476f0d 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -318,6 +318,7 @@ pub fn check_builtin_meta_item(
| sym::rustc_layout_scalar_valid_range_end
| sym::no_implicit_prelude
| sym::automatically_derived
+ | sym::coverage
) {
return;
}
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index d8ffced..2766b14 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -288,6 +288,9 @@ fn check_attributes(
&Attribute::Parsed(AttributeKind::StdInternalSymbol(attr_span)) => {
self.check_rustc_std_internal_symbol(attr_span, span, target)
}
+ &Attribute::Parsed(AttributeKind::Coverage(attr_span, _)) => {
+ self.check_coverage(attr_span, span, target)
+ }
Attribute::Unparsed(attr_item) => {
style = Some(attr_item.style);
match attr.path().as_slice() {
@@ -297,7 +300,6 @@ fn check_attributes(
[sym::diagnostic, sym::on_unimplemented, ..] => {
self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target)
}
- [sym::coverage, ..] => self.check_coverage(attr, span, target),
[sym::no_sanitize, ..] => {
self.check_no_sanitize(attr, span, target)
}
@@ -588,7 +590,7 @@ fn check_inline(
/// Checks that `#[coverage(..)]` is applied to a function/closure/method,
/// or to an impl block or module.
- fn check_coverage(&self, attr: &Attribute, target_span: Span, target: Target) {
+ fn check_coverage(&self, attr_span: Span, target_span: Span, target: Target) {
let mut not_fn_impl_mod = None;
let mut no_body = None;
@@ -611,7 +613,7 @@ fn check_coverage(&self, attr: &Attribute, target_span: Span, target: Target) {
}
self.dcx().emit_err(errors::CoverageAttributeNotAllowed {
- attr_span: attr.span(),
+ attr_span,
not_fn_impl_mod,
no_body,
help: (),
diff --git a/compiler/rustc_public/src/rustc_internal/mod.rs b/compiler/rustc_public/src/rustc_internal/mod.rs
index 5d7c825..01354fc 100644
--- a/compiler/rustc_public/src/rustc_internal/mod.rs
+++ b/compiler/rustc_public/src/rustc_internal/mod.rs
@@ -144,10 +144,10 @@ pub fn run<F, T>(tcx: TyCtxt<'_>, f: F) -> Result<T, Error>
#[macro_export]
macro_rules! run {
($args:expr, $callback_fn:ident) => {
- run_driver!($args, || $callback_fn())
+ $crate::run_driver!($args, || $callback_fn())
};
($args:expr, $callback:expr) => {
- run_driver!($args, $callback)
+ $crate::run_driver!($args, $callback)
};
}
@@ -158,10 +158,10 @@ macro_rules! run {
#[macro_export]
macro_rules! run_with_tcx {
($args:expr, $callback_fn:ident) => {
- run_driver!($args, |tcx| $callback_fn(tcx), with_tcx)
+ $crate::run_driver!($args, |tcx| $callback_fn(tcx), with_tcx)
};
($args:expr, $callback:expr) => {
- run_driver!($args, $callback, with_tcx)
+ $crate::run_driver!($args, $callback, with_tcx)
};
}
@@ -191,11 +191,11 @@ macro_rules! run_driver {
use rustc_public::CompilerError;
use std::ops::ControlFlow;
- pub struct StableMir<B = (), C = (), F = fn($(optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C>>
+ pub struct StableMir<B = (), C = (), F = fn($($crate::optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C>>
where
B: Send,
C: Send,
- F: FnOnce($(optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C> + Send,
+ F: FnOnce($($crate::optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C> + Send,
{
callback: Option<F>,
result: Option<ControlFlow<B, C>>,
@@ -205,7 +205,7 @@ impl<B, C, F> StableMir<B, C, F>
where
B: Send,
C: Send,
- F: FnOnce($(optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C> + Send,
+ F: FnOnce($($crate::optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C> + Send,
{
/// Creates a new `StableMir` instance, with given test_function and arguments.
pub fn new(callback: F) -> Self {
@@ -240,7 +240,7 @@ impl<B, C, F> Callbacks for StableMir<B, C, F>
where
B: Send,
C: Send,
- F: FnOnce($(optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C> + Send,
+ F: FnOnce($($crate::optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C> + Send,
{
/// Called after analysis. Return value instructs the compiler whether to
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
@@ -251,7 +251,7 @@ fn after_analysis<'tcx>(
) -> Compilation {
if let Some(callback) = self.callback.take() {
rustc_internal::run(tcx, || {
- self.result = Some(callback($(optional!($with_tcx tcx))?));
+ self.result = Some(callback($($crate::optional!($with_tcx tcx))?));
})
.unwrap();
if self.result.as_ref().is_some_and(|val| val.is_continue()) {
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index e56aabf..675ea9d 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -49,8 +49,7 @@ pub(crate) fn define_binding(
ns: Namespace,
binding: NameBinding<'ra>,
) {
- let key = self.new_disambiguated_key(ident, ns);
- if let Err(old_binding) = self.try_define(parent, key, binding, false) {
+ if let Err(old_binding) = self.try_define(parent, ident, ns, binding, false) {
self.report_conflict(parent, ident, ns, old_binding, binding);
}
}
@@ -442,16 +441,18 @@ fn add_import(
self.r.indeterminate_imports.push(import);
match import.kind {
- // Don't add unresolved underscore imports to modules
- ImportKind::Single { target: Ident { name: kw::Underscore, .. }, .. } => {}
ImportKind::Single { target, type_ns_only, .. } => {
- self.r.per_ns(|this, ns| {
- if !type_ns_only || ns == TypeNS {
- let key = BindingKey::new(target, ns);
- let mut resolution = this.resolution(current_module, key).borrow_mut();
- resolution.single_imports.insert(import);
- }
- });
+ // Don't add underscore imports to `single_imports`
+ // because they cannot define any usable names.
+ if target.name != kw::Underscore {
+ self.r.per_ns(|this, ns| {
+ if !type_ns_only || ns == TypeNS {
+ let key = BindingKey::new(target, ns);
+ let mut resolution = this.resolution(current_module, key).borrow_mut();
+ resolution.single_imports.insert(import);
+ }
+ });
+ }
}
// We don't add prelude imports to the globs since they only affect lexical scopes,
// which are not relevant to import resolution.
@@ -1405,9 +1406,12 @@ fn visit_assoc_item(&mut self, item: &'a AssocItem, ctxt: AssocCtxt) {
let parent = self.parent_scope.module;
let expansion = self.parent_scope.expansion;
self.r.define(parent, ident, ns, self.res(def_id), vis, item.span, expansion);
- } else if !matches!(&item.kind, AssocItemKind::Delegation(deleg) if deleg.from_glob) {
+ } else if !matches!(&item.kind, AssocItemKind::Delegation(deleg) if deleg.from_glob)
+ && ident.name != kw::Underscore
+ {
+ // Don't add underscore names, they cannot be looked up anyway.
let impl_def_id = self.r.tcx.local_parent(local_def_id);
- let key = BindingKey::new(ident.normalize_to_macros_2_0(), ns);
+ let key = BindingKey::new(ident, ns);
self.r.impl_binding_keys.entry(impl_def_id).or_default().insert(key);
}
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 0a4c25b..b4c15ed 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -25,7 +25,7 @@
use smallvec::SmallVec;
use tracing::debug;
-use crate::Namespace::*;
+use crate::Namespace::{self, *};
use crate::diagnostics::{DiagMode, Suggestion, import_candidates};
use crate::errors::{
CannotBeReexportedCratePublic, CannotBeReexportedCratePublicNS, CannotBeReexportedPrivate,
@@ -338,13 +338,20 @@ pub(crate) fn import(
pub(crate) fn try_define(
&mut self,
module: Module<'ra>,
- key: BindingKey,
+ ident: Ident,
+ ns: Namespace,
binding: NameBinding<'ra>,
warn_ambiguity: bool,
) -> Result<(), NameBinding<'ra>> {
let res = binding.res();
- self.check_reserved_macro_name(key.ident, res);
+ self.check_reserved_macro_name(ident, res);
self.set_binding_parent_module(binding, module);
+ // Even if underscore names cannot be looked up, we still need to add them to modules,
+ // because they can be fetched by glob imports from those modules, and bring traits
+ // into scope both directly and through glob imports.
+ let key = BindingKey::new_disambiguated(ident, ns, || {
+ (module.0.0.lazy_resolutions.borrow().len() + 1).try_into().unwrap()
+ });
self.update_resolution(module, key, warn_ambiguity, |this, resolution| {
if let Some(old_binding) = resolution.best_binding() {
if res == Res::Err && old_binding.res() != Res::Err {
@@ -383,7 +390,7 @@ pub(crate) fn try_define(
(old_glob @ true, false) | (old_glob @ false, true) => {
let (glob_binding, non_glob_binding) =
if old_glob { (old_binding, binding) } else { (binding, old_binding) };
- if key.ns == MacroNS
+ if ns == MacroNS
&& non_glob_binding.expansion != LocalExpnId::ROOT
&& glob_binding.res() != non_glob_binding.res()
{
@@ -489,10 +496,10 @@ fn update_resolution<T, F>(
};
if self.is_accessible_from(binding.vis, scope) {
let imported_binding = self.import(binding, *import);
- let key = BindingKey { ident, ..key };
let _ = self.try_define(
import.parent_scope.module,
- key,
+ ident,
+ key.ns,
imported_binding,
warn_ambiguity,
);
@@ -514,11 +521,15 @@ fn import_dummy_binding(&mut self, import: Import<'ra>, is_indeterminate: bool)
let dummy_binding = self.dummy_binding;
let dummy_binding = self.import(dummy_binding, import);
self.per_ns(|this, ns| {
- let key = BindingKey::new(target, ns);
- let _ = this.try_define(import.parent_scope.module, key, dummy_binding, false);
- this.update_resolution(import.parent_scope.module, key, false, |_, resolution| {
- resolution.single_imports.swap_remove(&import);
- })
+ let module = import.parent_scope.module;
+ let _ = this.try_define(module, target, ns, dummy_binding, false);
+ // Don't remove underscores from `single_imports`, they were never added.
+ if target.name != kw::Underscore {
+ let key = BindingKey::new(target, ns);
+ this.update_resolution(module, key, false, |_, resolution| {
+ resolution.single_imports.swap_remove(&import);
+ })
+ }
});
self.record_use(target, dummy_binding, Used::Other);
} else if import.imported_module.get().is_none() {
@@ -895,7 +906,7 @@ fn resolve_import(&mut self, import: Import<'ra>) -> usize {
PendingBinding::Ready(Some(imported_binding))
}
Err(Determinacy::Determined) => {
- // Don't update the resolution for underscores, because it was never added.
+ // Don't remove underscores from `single_imports`, they were never added.
if target.name != kw::Underscore {
let key = BindingKey::new(target, ns);
this.update_resolution(parent, key, false, |_, resolution| {
@@ -1510,7 +1521,8 @@ fn resolve_glob_import(&mut self, import: Import<'ra>) {
.is_some_and(|binding| binding.warn_ambiguity_recursive());
let _ = self.try_define(
import.parent_scope.module,
- key,
+ key.ident,
+ key.ns,
imported_binding,
warn_ambiguity,
);
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 753b936..93cec8d 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -2899,9 +2899,21 @@ fn with_generic_param_rib<'c, F>(
}
if param.ident.name == kw::UnderscoreLifetime {
+ // To avoid emitting two similar errors,
+ // we need to check if the span is a raw underscore lifetime, see issue #143152
+ let is_raw_underscore_lifetime = self
+ .r
+ .tcx
+ .sess
+ .psess
+ .raw_identifier_spans
+ .iter()
+ .any(|span| span == param.span());
+
self.r
.dcx()
- .emit_err(errors::UnderscoreLifetimeIsReserved { span: param.ident.span });
+ .create_err(errors::UnderscoreLifetimeIsReserved { span: param.ident.span })
+ .emit_unless(is_raw_underscore_lifetime);
// Record lifetime res, so lowering knows there is something fishy.
self.record_lifetime_param(param.id, LifetimeRes::Error);
continue;
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index dae30b7..f38fee8 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -532,15 +532,26 @@ struct BindingKey {
/// identifier.
ident: Ident,
ns: Namespace,
- /// 0 if ident is not `_`, otherwise a value that's unique to the specific
- /// `_` in the expanded AST that introduced this binding.
+ /// When we add an underscore binding (with ident `_`) to some module, this field has
+ /// a non-zero value that uniquely identifies this binding in that module.
+ /// For non-underscore bindings this field is zero.
+ /// When a key is constructed for name lookup (as opposed to name definition), this field is
+ /// also zero, even for underscore names, so for underscores the lookup will never succeed.
disambiguator: u32,
}
impl BindingKey {
fn new(ident: Ident, ns: Namespace) -> Self {
- let ident = ident.normalize_to_macros_2_0();
- BindingKey { ident, ns, disambiguator: 0 }
+ BindingKey { ident: ident.normalize_to_macros_2_0(), ns, disambiguator: 0 }
+ }
+
+ fn new_disambiguated(
+ ident: Ident,
+ ns: Namespace,
+ disambiguator: impl FnOnce() -> u32,
+ ) -> BindingKey {
+ let disambiguator = if ident.name == kw::Underscore { disambiguator() } else { 0 };
+ BindingKey { ident: ident.normalize_to_macros_2_0(), ns, disambiguator }
}
}
@@ -1087,8 +1098,6 @@ pub struct Resolver<'ra, 'tcx> {
extern_module_map: RefCell<FxIndexMap<DefId, Module<'ra>>>,
binding_parent_modules: FxHashMap<NameBinding<'ra>, Module<'ra>>,
- underscore_disambiguator: u32,
-
/// Maps glob imports to the names of items actually imported.
glob_map: FxIndexMap<LocalDefId, FxIndexSet<Symbol>>,
glob_error: Option<ErrorGuaranteed>,
@@ -1501,7 +1510,6 @@ pub fn new(
extern_crate_map: Default::default(),
module_children: Default::default(),
trait_map: NodeMap::default(),
- underscore_disambiguator: 0,
empty_module,
local_module_map,
extern_module_map: Default::default(),
@@ -1887,17 +1895,6 @@ fn find_transitive_imports(
import_ids
}
- fn new_disambiguated_key(&mut self, ident: Ident, ns: Namespace) -> BindingKey {
- let ident = ident.normalize_to_macros_2_0();
- let disambiguator = if ident.name == kw::Underscore {
- self.underscore_disambiguator += 1;
- self.underscore_disambiguator
- } else {
- 0
- };
- BindingKey { ident, ns, disambiguator }
- }
-
fn resolutions(&mut self, module: Module<'ra>) -> &'ra Resolutions<'ra> {
if module.populate_on_access.get() {
module.populate_on_access.set(false);
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 77ef7f5..0e8904a 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -530,7 +530,7 @@ fn glob_delegation_suffixes(
target_trait.for_each_child(self, |this, ident, ns, _binding| {
// FIXME: Adjust hygiene for idents from globs, like for glob imports.
if let Some(overriding_keys) = this.impl_binding_keys.get(&impl_def_id)
- && overriding_keys.contains(&BindingKey::new(ident.normalize_to_macros_2_0(), ns))
+ && overriding_keys.contains(&BindingKey::new(ident, ns))
{
// The name is overridden, do not produce it from the glob delegation.
} else {
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index a91e214..d6215e1 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -1708,6 +1708,11 @@ pub fn apply(&self, options: &mut getopts::Options) {
OptionKind::FlagMulti => options.optflagmulti(short_name, long_name, desc),
};
}
+
+ /// This is for diagnostics-only.
+ pub fn long_name(&self) -> &str {
+ self.long_name
+ }
}
pub fn make_opt(
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 626262c..2bdde2f 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -2256,6 +2256,8 @@ pub(crate) fn parse_align(slot: &mut Option<Align>, v: Option<&str>) -> bool {
environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)"),
has_thread_local: Option<bool> = (None, parse_opt_bool, [TRACKED],
"explicitly enable the `cfg(target_thread_local)` directive"),
+ higher_ranked_assumptions: bool = (false, parse_bool, [TRACKED],
+ "allow deducing higher-ranked outlives assumptions from coroutines when proving auto traits"),
hint_mostly_unused: bool = (false, parse_bool, [TRACKED],
"hint that most of this crate will go unused, to minimize work for uncalled functions"),
human_readable_cgu_names: bool = (false, parse_bool, [TRACKED],
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 7147f37..d28a73b 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -840,6 +840,7 @@
derive,
derive_coerce_pointee,
derive_const,
+ derive_const_issue: "118304",
derive_default_enum,
derive_smart_pointer,
destruct,
diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs
index a9bf5ea..6bcb7f6 100644
--- a/compiler/rustc_symbol_mangling/src/lib.rs
+++ b/compiler/rustc_symbol_mangling/src/lib.rs
@@ -180,7 +180,7 @@ fn compute_symbol_name<'tcx>(
// FIXME(eddyb) Precompute a custom symbol name based on attributes.
let attrs = if tcx.def_kind(def_id).has_codegen_attrs() {
- tcx.codegen_fn_attrs(def_id)
+ &tcx.codegen_instance_attrs(instance.def)
} else {
CodegenFnAttrs::EMPTY
};
diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs
index 0118321..7c6b7b1 100644
--- a/compiler/rustc_trait_selection/src/infer.rs
+++ b/compiler/rustc_trait_selection/src/infer.rs
@@ -33,8 +33,8 @@ fn type_is_copy_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx
let ty = self.resolve_vars_if_possible(ty);
// FIXME(#132279): This should be removed as it causes us to incorrectly
- // handle opaques in their defining scope.
- if !self.next_trait_solver() && !(param_env, ty).has_infer() {
+ // handle opaques in their defining scope, and stalled coroutines.
+ if !self.next_trait_solver() && !(param_env, ty).has_infer() && !ty.has_coroutines() {
return self.tcx.type_is_copy_modulo_regions(self.typing_env(param_env), ty);
}
diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs
index 068e90b..2b33b8a 100644
--- a/compiler/rustc_trait_selection/src/regions.rs
+++ b/compiler/rustc_trait_selection/src/regions.rs
@@ -4,7 +4,7 @@
use rustc_macros::extension;
use rustc_middle::traits::ObligationCause;
use rustc_middle::traits::query::NoSolution;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, Ty, elaborate};
use crate::traits::ScrubbedTraitError;
use crate::traits::outlives_bounds::InferCtxtExt;
@@ -46,6 +46,11 @@ fn new_with_implied_bounds_compat(
}
}
+ // FIXME(-Znext-trait-solver): Normalize these.
+ let higher_ranked_assumptions = infcx.take_registered_region_assumptions();
+ let higher_ranked_assumptions =
+ elaborate::elaborate_outlives_assumptions(infcx.tcx, higher_ranked_assumptions);
+
// FIXME: This needs to be modified so that we normalize the known type
// outlives obligations then elaborate them into their region/type components.
// Otherwise, `<W<'a> as Mirror>::Assoc: 'b` will not imply `'a: 'b` even
@@ -59,6 +64,7 @@ fn new_with_implied_bounds_compat(
assumed_wf_tys,
disable_implied_bounds_hack,
),
+ higher_ranked_assumptions,
)
}
}
diff --git a/compiler/rustc_trait_selection/src/solve.rs b/compiler/rustc_trait_selection/src/solve.rs
index 5a5d161..f589616 100644
--- a/compiler/rustc_trait_selection/src/solve.rs
+++ b/compiler/rustc_trait_selection/src/solve.rs
@@ -7,7 +7,7 @@
mod select;
pub(crate) use delegate::SolverDelegate;
-pub use fulfill::{FulfillmentCtxt, NextSolverError};
+pub use fulfill::{FulfillmentCtxt, NextSolverError, StalledOnCoroutines};
pub(crate) use normalize::deeply_normalize_for_diagnostics;
pub use normalize::{
deeply_normalize, deeply_normalize_with_skipped_universes,
diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs
index 1a24254..17429e1 100644
--- a/compiler/rustc_trait_selection/src/solve/delegate.rs
+++ b/compiler/rustc_trait_selection/src/solve/delegate.rs
@@ -206,14 +206,18 @@ fn well_formed_goals(
.map(|obligations| obligations.into_iter().map(|obligation| obligation.as_goal()).collect())
}
- fn make_deduplicated_outlives_constraints(
- &self,
- ) -> Vec<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>> {
+ fn make_deduplicated_outlives_constraints(&self) -> Vec<ty::ArgOutlivesPredicate<'tcx>> {
// Cannot use `take_registered_region_obligations` as we may compute the response
// inside of a `probe` whenever we have multiple choices inside of the solver.
let region_obligations = self.0.inner.borrow().region_obligations().to_owned();
+ let region_assumptions = self.0.inner.borrow().region_assumptions().to_owned();
let region_constraints = self.0.with_region_constraints(|region_constraints| {
- make_query_region_constraints(self.tcx, region_obligations, region_constraints)
+ make_query_region_constraints(
+ self.tcx,
+ region_obligations,
+ region_constraints,
+ region_assumptions,
+ )
});
let mut seen = FxHashSet::default();
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index d56042a..3ce0f02 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -10,7 +10,8 @@
FromSolverError, PredicateObligation, PredicateObligations, TraitEngine,
};
use rustc_middle::ty::{
- self, DelayedSet, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, TypingMode,
+ self, DelayedSet, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
+ TypingMode,
};
use rustc_next_trait_solver::delegate::SolverDelegate as _;
use rustc_next_trait_solver::solve::{
@@ -254,7 +255,7 @@ fn drain_stalled_obligations_for_coroutines(
&mut self,
infcx: &InferCtxt<'tcx>,
) -> PredicateObligations<'tcx> {
- let stalled_generators = match infcx.typing_mode() {
+ let stalled_coroutines = match infcx.typing_mode() {
TypingMode::Analysis { defining_opaque_types_and_generators } => {
defining_opaque_types_and_generators
}
@@ -264,7 +265,7 @@ fn drain_stalled_obligations_for_coroutines(
| TypingMode::PostAnalysis => return Default::default(),
};
- if stalled_generators.is_empty() {
+ if stalled_coroutines.is_empty() {
return Default::default();
}
@@ -275,7 +276,7 @@ fn drain_stalled_obligations_for_coroutines(
.visit_proof_tree(
obl.as_goal(),
&mut StalledOnCoroutines {
- stalled_generators,
+ stalled_coroutines,
span: obl.cause.span,
cache: Default::default(),
},
@@ -297,10 +298,10 @@ fn drain_stalled_obligations_for_coroutines(
///
/// This function can be also return false positives, which will lead to poor diagnostics
/// so we want to keep this visitor *precise* too.
-struct StalledOnCoroutines<'tcx> {
- stalled_generators: &'tcx ty::List<LocalDefId>,
- span: Span,
- cache: DelayedSet<Ty<'tcx>>,
+pub struct StalledOnCoroutines<'tcx> {
+ pub stalled_coroutines: &'tcx ty::List<LocalDefId>,
+ pub span: Span,
+ pub cache: DelayedSet<Ty<'tcx>>,
}
impl<'tcx> inspect::ProofTreeVisitor<'tcx> for StalledOnCoroutines<'tcx> {
@@ -330,12 +331,14 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
}
if let ty::CoroutineWitness(def_id, _) = *ty.kind()
- && def_id.as_local().is_some_and(|def_id| self.stalled_generators.contains(&def_id))
+ && def_id.as_local().is_some_and(|def_id| self.stalled_coroutines.contains(&def_id))
{
- return ControlFlow::Break(());
+ ControlFlow::Break(())
+ } else if ty.has_coroutines() {
+ ty.super_visit_with(self)
+ } else {
+ ControlFlow::Continue(())
}
-
- ty.super_visit_with(self)
}
}
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index ce5a4ed..f50f01a 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -461,6 +461,7 @@ fn impl_intersection_has_negative_obligation(
// requirements, when proving the negated where clauses below.
drop(equate_obligations);
drop(infcx.take_registered_region_obligations());
+ drop(infcx.take_registered_region_assumptions());
drop(infcx.take_and_reset_region_constraints());
plug_infer_with_placeholders(
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 2b5a41e..e35f893 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -3,6 +3,7 @@
use rustc_data_structures::obligation_forest::{
Error, ForestObligation, ObligationForest, ObligationProcessor, Outcome, ProcessResult,
};
+use rustc_hir::def_id::LocalDefId;
use rustc_infer::infer::DefineOpaqueTypes;
use rustc_infer::traits::{
FromSolverError, PolyTraitObligation, PredicateObligations, ProjectionCacheKey, SelectionError,
@@ -12,8 +13,10 @@
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::{
- self, Binder, Const, GenericArgsRef, TypeVisitableExt, TypingMode, may_use_unstable_feature,
+ self, Binder, Const, GenericArgsRef, TypeVisitable, TypeVisitableExt, TypingMode,
+ may_use_unstable_feature,
};
+use rustc_span::DUMMY_SP;
use thin_vec::{ThinVec, thin_vec};
use tracing::{debug, debug_span, instrument};
@@ -26,6 +29,7 @@
};
use crate::error_reporting::InferCtxtErrorExt;
use crate::infer::{InferCtxt, TyOrConstInferVar};
+use crate::solve::StalledOnCoroutines;
use crate::traits::normalize::normalize_with_depth_to;
use crate::traits::project::{PolyProjectionObligation, ProjectionCacheKeyExt as _};
use crate::traits::query::evaluate_obligation::InferCtxtExt;
@@ -168,8 +172,25 @@ fn drain_stalled_obligations_for_coroutines(
&mut self,
infcx: &InferCtxt<'tcx>,
) -> PredicateObligations<'tcx> {
- let mut processor =
- DrainProcessor { removed_predicates: PredicateObligations::new(), infcx };
+ let stalled_coroutines = match infcx.typing_mode() {
+ TypingMode::Analysis { defining_opaque_types_and_generators } => {
+ defining_opaque_types_and_generators
+ }
+ TypingMode::Coherence
+ | TypingMode::Borrowck { defining_opaque_types: _ }
+ | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ }
+ | TypingMode::PostAnalysis => return Default::default(),
+ };
+
+ if stalled_coroutines.is_empty() {
+ return Default::default();
+ }
+
+ let mut processor = DrainProcessor {
+ infcx,
+ removed_predicates: PredicateObligations::new(),
+ stalled_coroutines,
+ };
let outcome: Outcome<_, _> = self.predicates.process_obligations(&mut processor);
assert!(outcome.errors.is_empty());
return processor.removed_predicates;
@@ -177,6 +198,7 @@ fn drain_stalled_obligations_for_coroutines(
struct DrainProcessor<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>,
removed_predicates: PredicateObligations<'tcx>,
+ stalled_coroutines: &'tcx ty::List<LocalDefId>,
}
impl<'tcx> ObligationProcessor for DrainProcessor<'_, 'tcx> {
@@ -185,10 +207,14 @@ impl<'tcx> ObligationProcessor for DrainProcessor<'_, 'tcx> {
type OUT = Outcome<Self::Obligation, Self::Error>;
fn needs_process_obligation(&self, pending_obligation: &Self::Obligation) -> bool {
- pending_obligation
- .stalled_on
- .iter()
- .any(|&var| self.infcx.ty_or_const_infer_var_changed(var))
+ self.infcx
+ .resolve_vars_if_possible(pending_obligation.obligation.predicate)
+ .visit_with(&mut StalledOnCoroutines {
+ stalled_coroutines: self.stalled_coroutines,
+ span: DUMMY_SP,
+ cache: Default::default(),
+ })
+ .is_break()
}
fn process_obligation(
diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
index 59d3ac2..5351803 100644
--- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
@@ -78,7 +78,10 @@ fn implied_outlives_bounds<'a, 'tcx>(
bounds.retain(|bound| !bound.has_placeholders());
if !constraints.is_empty() {
- let QueryRegionConstraints { outlives } = constraints;
+ // FIXME(higher_ranked_auto): Should we register assumptions here?
+ // We otherwise would get spurious errors if normalizing an implied
+ // outlives bound required proving some higher-ranked coroutine obl.
+ let QueryRegionConstraints { outlives, assumptions: _ } = constraints;
let cause = ObligationCause::misc(span, body_id);
for &(predicate, _) in &outlives {
infcx.register_outlives_constraint(predicate, &cause);
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
index 3b54924..f027ba1 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
@@ -80,6 +80,11 @@ pub fn scrape_region_constraints<'tcx, Op, R>(
pre_obligations.is_empty(),
"scrape_region_constraints: incoming region obligations = {pre_obligations:#?}",
);
+ let pre_assumptions = infcx.take_registered_region_assumptions();
+ assert!(
+ pre_assumptions.is_empty(),
+ "scrape_region_constraints: incoming region assumptions = {pre_assumptions:#?}",
+ );
let value = infcx.commit_if_ok(|_| {
let ocx = ObligationCtxt::new(infcx);
@@ -100,11 +105,13 @@ pub fn scrape_region_constraints<'tcx, Op, R>(
let value = infcx.resolve_vars_if_possible(value);
let region_obligations = infcx.take_registered_region_obligations();
+ let region_assumptions = infcx.take_registered_region_assumptions();
let region_constraint_data = infcx.take_and_reset_region_constraints();
let region_constraints = query_response::make_query_region_constraints(
infcx.tcx,
region_obligations,
®ion_constraint_data,
+ region_assumptions,
);
if region_constraints.is_empty() {
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
index 4bdf043..018e974 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
@@ -180,8 +180,9 @@ fn fully_perform(
span,
)?;
output.error_info = error_info;
- if let Some(QueryRegionConstraints { outlives }) = output.constraints {
+ if let Some(QueryRegionConstraints { outlives, assumptions }) = output.constraints {
region_constraints.outlives.extend(outlives.iter().cloned());
+ region_constraints.assumptions.extend(assumptions.iter().cloned());
}
output.constraints = if region_constraints.is_empty() {
None
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index cc188a2..2c70895 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -842,6 +842,14 @@ fn assemble_candidates_from_auto_impls(
}
}
+ ty::CoroutineWitness(def_id, _) => {
+ if self.should_stall_coroutine_witness(def_id) {
+ candidates.ambiguous = true;
+ } else {
+ candidates.vec.push(AutoImplCandidate);
+ }
+ }
+
ty::Bool
| ty::Char
| ty::Int(_)
@@ -861,7 +869,6 @@ fn assemble_candidates_from_auto_impls(
| ty::Coroutine(..)
| ty::Never
| ty::Tuple(_)
- | ty::CoroutineWitness(..)
| ty::UnsafeBinder(_) => {
// Only consider auto impls of unsafe traits when there are
// no unsafe fields.
@@ -1119,12 +1126,7 @@ fn assemble_builtin_copy_clone_candidate(
match *self_ty.kind() {
// These impls are built-in because we cannot express sufficiently
// generic impls in libcore.
- ty::FnDef(..)
- | ty::FnPtr(..)
- | ty::Error(_)
- | ty::Tuple(..)
- | ty::CoroutineWitness(..)
- | ty::Pat(..) => {
+ ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) | ty::Tuple(..) | ty::Pat(..) => {
candidates.vec.push(BuiltinCandidate);
}
@@ -1192,6 +1194,14 @@ fn assemble_builtin_copy_clone_candidate(
}
}
+ ty::CoroutineWitness(coroutine_def_id, _) => {
+ if self.should_stall_coroutine_witness(coroutine_def_id) {
+ candidates.ambiguous = true;
+ } else {
+ candidates.vec.push(SizedCandidate);
+ }
+ }
+
// Fallback to whatever user-defined impls or param-env clauses exist in this case.
ty::Adt(..) | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => {}
@@ -1229,7 +1239,6 @@ fn assemble_builtin_sized_candidate(
| ty::Char
| ty::Ref(..)
| ty::Coroutine(..)
- | ty::CoroutineWitness(..)
| ty::Array(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
@@ -1238,6 +1247,14 @@ fn assemble_builtin_sized_candidate(
candidates.vec.push(SizedCandidate);
}
+ ty::CoroutineWitness(coroutine_def_id, _) => {
+ if self.should_stall_coroutine_witness(coroutine_def_id) {
+ candidates.ambiguous = true;
+ } else {
+ candidates.vec.push(SizedCandidate);
+ }
+ }
+
// Conditionally `Sized`.
ty::Tuple(..) | ty::Pat(..) | ty::Adt(..) | ty::UnsafeBinder(_) => {
candidates.vec.push(SizedCandidate);
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index ee8cef2..488094b 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -411,18 +411,33 @@ fn confirm_auto_impl_candidate(
obligation.predicate.self_ty().map_bound(|ty| self.infcx.shallow_resolve(ty));
let self_ty = self.infcx.enter_forall_and_leak_universe(self_ty);
- let types = self.constituent_types_for_ty(self_ty)?;
- let types = self.infcx.enter_forall_and_leak_universe(types);
+ let constituents = self.constituent_types_for_auto_trait(self_ty)?;
+ let constituents = self.infcx.enter_forall_and_leak_universe(constituents);
let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived);
- let obligations = self.collect_predicates_for_types(
+ let mut obligations = self.collect_predicates_for_types(
obligation.param_env,
- cause,
+ cause.clone(),
obligation.recursion_depth + 1,
obligation.predicate.def_id(),
- types,
+ constituents.types,
);
+ // FIXME(coroutine_clone): We could uplift this into `collect_predicates_for_types`
+ // and do this for `Copy`/`Clone` too, but that's feature-gated so it doesn't really
+ // matter yet.
+ for assumption in constituents.assumptions {
+ let assumption = normalize_with_depth_to(
+ self,
+ obligation.param_env,
+ cause.clone(),
+ obligation.recursion_depth + 1,
+ assumption,
+ &mut obligations,
+ );
+ self.infcx.register_region_assumption(assumption);
+ }
+
Ok(obligations)
})
}
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 10bcf86..2b563c5 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -20,6 +20,7 @@
use rustc_infer::infer::at::ToTrace;
use rustc_infer::infer::relate::TypeRelation;
use rustc_infer::traits::{PredicateObligations, TraitObligation};
+use rustc_macros::{TypeFoldable, TypeVisitable};
use rustc_middle::bug;
use rustc_middle::dep_graph::{DepNodeIndex, dep_kinds};
pub use rustc_middle::traits::select::*;
@@ -1512,7 +1513,8 @@ fn can_use_global_caches(
defining_opaque_types_and_generators: defining_opaque_types,
}
| TypingMode::Borrowck { defining_opaque_types } => {
- defining_opaque_types.is_empty() || !pred.has_opaque_types()
+ defining_opaque_types.is_empty()
+ || (!pred.has_opaque_types() && !pred.has_coroutines())
}
// The hidden types of `defined_opaque_types` is not local to the current
// inference context, so we can freely move this to the global cache.
@@ -2255,10 +2257,10 @@ fn coroutine_is_gen(&mut self, self_ty: Ty<'tcx>) -> bool {
/// Zed<i32> where enum Zed { A(T), B(u32) } -> [i32, u32]
/// ```
#[instrument(level = "debug", skip(self), ret)]
- fn constituent_types_for_ty(
+ fn constituent_types_for_auto_trait(
&self,
t: Ty<'tcx>,
- ) -> Result<ty::Binder<'tcx, Vec<Ty<'tcx>>>, SelectionError<'tcx>> {
+ ) -> Result<ty::Binder<'tcx, AutoImplConstituents<'tcx>>, SelectionError<'tcx>> {
Ok(match *t.kind() {
ty::Uint(_)
| ty::Int(_)
@@ -2269,17 +2271,26 @@ fn constituent_types_for_ty(
| ty::Error(_)
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
| ty::Never
- | ty::Char => ty::Binder::dummy(Vec::new()),
+ | ty::Char => {
+ ty::Binder::dummy(AutoImplConstituents { types: vec![], assumptions: vec![] })
+ }
// This branch is only for `experimental_default_bounds`.
// Other foreign types were rejected earlier in
// `assemble_candidates_from_auto_impls`.
- ty::Foreign(..) => ty::Binder::dummy(Vec::new()),
+ ty::Foreign(..) => {
+ ty::Binder::dummy(AutoImplConstituents { types: vec![], assumptions: vec![] })
+ }
- ty::UnsafeBinder(ty) => ty.map_bound(|ty| vec![ty]),
+ ty::UnsafeBinder(ty) => {
+ ty.map_bound(|ty| AutoImplConstituents { types: vec![ty], assumptions: vec![] })
+ }
// Treat this like `struct str([u8]);`
- ty::Str => ty::Binder::dummy(vec![Ty::new_slice(self.tcx(), self.tcx().types.u8)]),
+ ty::Str => ty::Binder::dummy(AutoImplConstituents {
+ types: vec![Ty::new_slice(self.tcx(), self.tcx().types.u8)],
+ assumptions: vec![],
+ }),
ty::Placeholder(..)
| ty::Dynamic(..)
@@ -2291,30 +2302,41 @@ fn constituent_types_for_ty(
}
ty::RawPtr(element_ty, _) | ty::Ref(_, element_ty, _) => {
- ty::Binder::dummy(vec![element_ty])
+ ty::Binder::dummy(AutoImplConstituents {
+ types: vec![element_ty],
+ assumptions: vec![],
+ })
}
- ty::Pat(ty, _) | ty::Array(ty, _) | ty::Slice(ty) => ty::Binder::dummy(vec![ty]),
+ ty::Pat(ty, _) | ty::Array(ty, _) | ty::Slice(ty) => {
+ ty::Binder::dummy(AutoImplConstituents { types: vec![ty], assumptions: vec![] })
+ }
ty::Tuple(tys) => {
// (T1, ..., Tn) -- meets any bound that all of T1...Tn meet
- ty::Binder::dummy(tys.iter().collect())
+ ty::Binder::dummy(AutoImplConstituents {
+ types: tys.iter().collect(),
+ assumptions: vec![],
+ })
}
ty::Closure(_, args) => {
let ty = self.infcx.shallow_resolve(args.as_closure().tupled_upvars_ty());
- ty::Binder::dummy(vec![ty])
+ ty::Binder::dummy(AutoImplConstituents { types: vec![ty], assumptions: vec![] })
}
ty::CoroutineClosure(_, args) => {
let ty = self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty());
- ty::Binder::dummy(vec![ty])
+ ty::Binder::dummy(AutoImplConstituents { types: vec![ty], assumptions: vec![] })
}
ty::Coroutine(_, args) => {
let ty = self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty());
let witness = args.as_coroutine().witness();
- ty::Binder::dummy([ty].into_iter().chain(iter::once(witness)).collect())
+ ty::Binder::dummy(AutoImplConstituents {
+ types: [ty].into_iter().chain(iter::once(witness)).collect(),
+ assumptions: vec![],
+ })
}
ty::CoroutineWitness(def_id, args) => self
@@ -2322,16 +2344,23 @@ fn constituent_types_for_ty(
.tcx
.coroutine_hidden_types(def_id)
.instantiate(self.infcx.tcx, args)
- .map_bound(|witness| witness.types.to_vec()),
+ .map_bound(|witness| AutoImplConstituents {
+ types: witness.types.to_vec(),
+ assumptions: witness.assumptions.to_vec(),
+ }),
// For `PhantomData<T>`, we pass `T`.
ty::Adt(def, args) if def.is_phantom_data() => {
- ty::Binder::dummy(args.types().collect())
+ ty::Binder::dummy(AutoImplConstituents {
+ types: args.types().collect(),
+ assumptions: vec![],
+ })
}
- ty::Adt(def, args) => {
- ty::Binder::dummy(def.all_fields().map(|f| f.ty(self.tcx(), args)).collect())
- }
+ ty::Adt(def, args) => ty::Binder::dummy(AutoImplConstituents {
+ types: def.all_fields().map(|f| f.ty(self.tcx(), args)).collect(),
+ assumptions: vec![],
+ }),
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
if self.infcx.can_define_opaque_ty(def_id) {
@@ -2341,7 +2370,10 @@ fn constituent_types_for_ty(
// which enforces a DAG between the functions requiring
// the auto trait bounds in question.
match self.tcx().type_of_opaque(def_id) {
- Ok(ty) => ty::Binder::dummy(vec![ty.instantiate(self.tcx(), args)]),
+ Ok(ty) => ty::Binder::dummy(AutoImplConstituents {
+ types: vec![ty.instantiate(self.tcx(), args)],
+ assumptions: vec![],
+ }),
Err(_) => {
return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id));
}
@@ -2811,6 +2843,18 @@ fn impl_or_trait_obligations(
obligations
}
+
+ fn should_stall_coroutine_witness(&self, def_id: DefId) -> bool {
+ match self.infcx.typing_mode() {
+ TypingMode::Analysis { defining_opaque_types_and_generators: stalled_generators } => {
+ def_id.as_local().is_some_and(|def_id| stalled_generators.contains(&def_id))
+ }
+ TypingMode::Coherence
+ | TypingMode::PostAnalysis
+ | TypingMode::Borrowck { defining_opaque_types: _ }
+ | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => false,
+ }
+ }
}
impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> {
@@ -3121,3 +3165,9 @@ pub(crate) enum ProjectionMatchesProjection {
Ambiguous,
No,
}
+
+#[derive(Clone, Debug, TypeFoldable, TypeVisitable)]
+pub(crate) struct AutoImplConstituents<'tcx> {
+ pub types: Vec<Ty<'tcx>>,
+ pub assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>,
+}
diff --git a/compiler/rustc_traits/src/coroutine_witnesses.rs b/compiler/rustc_traits/src/coroutine_witnesses.rs
index 447e131..87d17f3 100644
--- a/compiler/rustc_traits/src/coroutine_witnesses.rs
+++ b/compiler/rustc_traits/src/coroutine_witnesses.rs
@@ -1,5 +1,10 @@
use rustc_hir::def_id::DefId;
-use rustc_middle::ty::{self, TyCtxt, fold_regions};
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
+use rustc_infer::infer::resolve::OpportunisticRegionResolver;
+use rustc_infer::traits::{Obligation, ObligationCause};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions};
+use rustc_trait_selection::traits::{ObligationCtxt, with_replaced_escaping_bound_vars};
/// Return the set of types that should be taken into account when checking
/// trait bounds on a coroutine's internal state. This properly replaces
@@ -30,8 +35,57 @@ pub(crate) fn coroutine_hidden_types<'tcx>(
}),
);
+ let assumptions = compute_assumptions(tcx, def_id, bound_tys);
+
ty::EarlyBinder::bind(ty::Binder::bind_with_vars(
- ty::CoroutineWitnessTypes { types: bound_tys },
+ ty::CoroutineWitnessTypes { types: bound_tys, assumptions },
tcx.mk_bound_variable_kinds(&vars),
))
}
+
+fn compute_assumptions<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def_id: DefId,
+ bound_tys: &'tcx ty::List<Ty<'tcx>>,
+) -> &'tcx ty::List<ty::ArgOutlivesPredicate<'tcx>> {
+ let infcx = tcx.infer_ctxt().build(ty::TypingMode::Analysis {
+ defining_opaque_types_and_generators: ty::List::empty(),
+ });
+ with_replaced_escaping_bound_vars(&infcx, &mut vec![None], bound_tys, |bound_tys| {
+ let param_env = tcx.param_env(def_id);
+ let ocx = ObligationCtxt::new(&infcx);
+
+ ocx.register_obligations(bound_tys.iter().map(|ty| {
+ Obligation::new(
+ tcx,
+ ObligationCause::dummy(),
+ param_env,
+ ty::ClauseKind::WellFormed(ty.into()),
+ )
+ }));
+ let _errors = ocx.select_all_or_error();
+
+ let region_obligations = infcx.take_registered_region_obligations();
+ let region_assumptions = infcx.take_registered_region_assumptions();
+ let region_constraints = infcx.take_and_reset_region_constraints();
+
+ let outlives = make_query_region_constraints(
+ tcx,
+ region_obligations,
+ ®ion_constraints,
+ region_assumptions,
+ )
+ .outlives
+ .fold_with(&mut OpportunisticRegionResolver::new(&infcx));
+
+ tcx.mk_outlives_from_iter(
+ outlives
+ .into_iter()
+ .map(|(o, _)| o)
+ // FIXME(higher_ranked_auto): We probably should deeply resolve these before
+ // filtering out infers which only correspond to unconstrained infer regions
+ // which we can sometimes get.
+ .filter(|o| !o.has_infer()),
+ )
+ })
+}
diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs
index 3800824..dc15cc3 100644
--- a/compiler/rustc_type_ir/src/elaborate.rs
+++ b/compiler/rustc_type_ir/src/elaborate.rs
@@ -371,3 +371,54 @@ fn size_hint(&self) -> (usize, Option<usize>) {
(0, upper)
}
}
+
+pub fn elaborate_outlives_assumptions<I: Interner>(
+ cx: I,
+ assumptions: impl IntoIterator<Item = ty::OutlivesPredicate<I, I::GenericArg>>,
+) -> HashSet<ty::OutlivesPredicate<I, I::GenericArg>> {
+ let mut collected = HashSet::default();
+
+ for ty::OutlivesPredicate(arg1, r2) in assumptions {
+ collected.insert(ty::OutlivesPredicate(arg1, r2));
+ match arg1.kind() {
+ // Elaborate the components of an type, since we may have substituted a
+ // generic coroutine with a more specific type.
+ ty::GenericArgKind::Type(ty1) => {
+ let mut components = smallvec![];
+ push_outlives_components(cx, ty1, &mut components);
+ for c in components {
+ match c {
+ Component::Region(r1) => {
+ if !r1.is_bound() {
+ collected.insert(ty::OutlivesPredicate(r1.into(), r2));
+ }
+ }
+
+ Component::Param(p) => {
+ let ty = Ty::new_param(cx, p);
+ collected.insert(ty::OutlivesPredicate(ty.into(), r2));
+ }
+
+ Component::Placeholder(p) => {
+ let ty = Ty::new_placeholder(cx, p);
+ collected.insert(ty::OutlivesPredicate(ty.into(), r2));
+ }
+
+ Component::Alias(alias_ty) => {
+ collected.insert(ty::OutlivesPredicate(alias_ty.to_ty(cx).into(), r2));
+ }
+
+ Component::UnresolvedInferenceVariable(_) | Component::EscapingAlias(_) => {
+ }
+ }
+ }
+ }
+ // Nothing to elaborate for a region.
+ ty::GenericArgKind::Lifetime(_) => {}
+ // Consts don't really participate in outlives.
+ ty::GenericArgKind::Const(_) => {}
+ }
+ }
+
+ collected
+}
diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs
index a231908..d7b9e0c 100644
--- a/compiler/rustc_type_ir/src/flags.rs
+++ b/compiler/rustc_type_ir/src/flags.rs
@@ -130,6 +130,12 @@ pub struct TypeFlags: u32 {
/// Does this have any binders with bound vars (e.g. that need to be anonymized)?
const HAS_BINDER_VARS = 1 << 23;
+
+ /// Does this type have any coroutine witnesses in it?
+ // FIXME: This should probably be changed to track whether the type has any
+ // *coroutines* in it, though this will happen if we remove coroutine witnesses
+ // altogether.
+ const HAS_TY_CORO = 1 << 24;
}
}
@@ -240,10 +246,12 @@ fn add_kind(&mut self, kind: &ty::TyKind<I>) {
self.add_flags(TypeFlags::HAS_TY_PARAM);
}
- ty::Closure(_, args)
- | ty::Coroutine(_, args)
- | ty::CoroutineClosure(_, args)
- | ty::CoroutineWitness(_, args) => {
+ ty::Closure(_, args) | ty::Coroutine(_, args) | ty::CoroutineClosure(_, args) => {
+ self.add_args(args.as_slice());
+ }
+
+ ty::CoroutineWitness(_, args) => {
+ self.add_flags(TypeFlags::HAS_TY_CORO);
self.add_args(args.as_slice());
}
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index 0ec326d..e323124 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -146,6 +146,13 @@ fn mk_tracked<T: Debug + Clone>(
type BoundRegion: BoundVarLike<Self>;
type PlaceholderRegion: PlaceholderLike<Self, Bound = Self::BoundRegion>;
+ type RegionAssumptions: Copy
+ + Debug
+ + Hash
+ + Eq
+ + SliceLike<Item = ty::OutlivesPredicate<Self, Self::GenericArg>>
+ + TypeFoldable<Self>;
+
// Predicates
type ParamEnv: ParamEnv<Self>;
type Predicate: Predicate<Self>;
diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs
index db6fbef..7c66542 100644
--- a/compiler/rustc_type_ir/src/ty_kind.rs
+++ b/compiler/rustc_type_ir/src/ty_kind.rs
@@ -1150,4 +1150,5 @@ pub struct FnHeader<I: Interner> {
#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
pub struct CoroutineWitnessTypes<I: Interner> {
pub types: I::Tys,
+ pub assumptions: I::RegionAssumptions,
}
diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs
index a96ac97..5104484 100644
--- a/compiler/rustc_type_ir/src/visit.rs
+++ b/compiler/rustc_type_ir/src/visit.rs
@@ -269,6 +269,10 @@ fn has_opaque_types(&self) -> bool {
self.has_type_flags(TypeFlags::HAS_TY_OPAQUE)
}
+ fn has_coroutines(&self) -> bool {
+ self.has_type_flags(TypeFlags::HAS_TY_CORO)
+ }
+
fn references_error(&self) -> bool {
self.has_type_flags(TypeFlags::HAS_ERROR)
}
diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs
index 30f4205..d0ba9c3 100644
--- a/library/alloc/src/fmt.rs
+++ b/library/alloc/src/fmt.rs
@@ -348,13 +348,13 @@
//! format := '{' [ argument ] [ ':' format_spec ] [ ws ] * '}'
//! argument := integer | identifier
//!
-//! format_spec := [[fill]align][sign]['#']['0'][width]['.' precision]type
+//! format_spec := [[fill]align][sign]['#']['0'][width]['.' precision][type]
//! fill := character
//! align := '<' | '^' | '>'
//! sign := '+' | '-'
//! width := count
//! precision := count | '*'
-//! type := '' | '?' | 'x?' | 'X?' | identifier
+//! type := '?' | 'x?' | 'X?' | identifier
//! count := parameter | integer
//! parameter := argument '$'
//! ```
diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs
index 03b120f..6419ae9 100644
--- a/library/core/src/cmp.rs
+++ b/library/core/src/cmp.rs
@@ -381,7 +381,8 @@ pub struct AssertParamIsEq<T: Eq + PointeeSized> {
///
/// assert_eq!(2.cmp(&1), Ordering::Greater);
/// ```
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+#[derive(Clone, Copy, Eq, PartialOrd, Ord, Debug, Hash)]
+#[derive_const(PartialEq)]
#[stable(feature = "rust1", since = "1.0.0")]
// This is a lang item only so that `BinOp::Cmp` in MIR can return it.
// It has no special behavior, but does require that the three variants
diff --git a/library/core/src/cmp/bytewise.rs b/library/core/src/cmp/bytewise.rs
index a06a522..7d61c93 100644
--- a/library/core/src/cmp/bytewise.rs
+++ b/library/core/src/cmp/bytewise.rs
@@ -17,11 +17,15 @@
/// - Neither `Self` nor `Rhs` have provenance, so integer comparisons are correct.
/// - `<Self as PartialEq<Rhs>>::{eq,ne}` are equivalent to comparing the bytes.
#[rustc_specialization_trait]
-pub(crate) unsafe trait BytewiseEq<Rhs = Self>: PartialEq<Rhs> + Sized {}
+#[const_trait]
+pub(crate) unsafe trait BytewiseEq<Rhs = Self>:
+ ~const PartialEq<Rhs> + Sized
+{
+}
macro_rules! is_bytewise_comparable {
($($t:ty),+ $(,)?) => {$(
- unsafe impl BytewiseEq for $t {}
+ unsafe impl const BytewiseEq for $t {}
)+};
}
diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs
index fa27aca..f90e685 100644
--- a/library/core/src/intrinsics/mod.rs
+++ b/library/core/src/intrinsics/mod.rs
@@ -2208,6 +2208,7 @@ pub const fn ptr_guaranteed_cmp<T>(ptr: *const T, other: *const T) -> u8 {
/// [valid]: crate::ptr#safety
#[rustc_nounwind]
#[rustc_intrinsic]
+#[rustc_const_unstable(feature = "const_cmp", issue = "143800")]
pub const unsafe fn compare_bytes(left: *const u8, right: *const u8, bytes: usize) -> i32;
/// See documentation of [`std::hint::black_box`] for details.
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index e08edde..729de5f 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -103,6 +103,7 @@
#![feature(cfg_select)]
#![feature(cfg_target_has_reliable_f16_f128)]
#![feature(const_carrying_mul_add)]
+#![feature(const_cmp)]
#![feature(const_destruct)]
#![feature(const_eval_select)]
#![feature(core_intrinsics)]
@@ -146,6 +147,7 @@
#![feature(const_trait_impl)]
#![feature(decl_macro)]
#![feature(deprecated_suggestion)]
+#![feature(derive_const)]
#![feature(doc_cfg)]
#![feature(doc_cfg_hide)]
#![feature(doc_notable_trait)]
diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs
index 1b6dbc2..8ac6ce2 100644
--- a/library/core/src/macros/mod.rs
+++ b/library/core/src/macros/mod.rs
@@ -1615,7 +1615,7 @@ macro_rules! trace_macros {
/// See [the reference] for more info.
///
/// [the reference]: ../../../reference/attributes/derive.html
- #[unstable(feature = "derive_const", issue = "none")]
+ #[unstable(feature = "derive_const", issue = "118304")]
#[rustc_builtin_macro]
pub macro derive_const($item:item) {
/* compiler built-in */
diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs
index 11d50e0..b8900c4 100644
--- a/library/core/src/num/nonzero.rs
+++ b/library/core/src/num/nonzero.rs
@@ -200,9 +200,10 @@ impl<T> UseCloned for NonZero<T> where T: ZeroablePrimitive {}
impl<T> Copy for NonZero<T> where T: ZeroablePrimitive {}
#[stable(feature = "nonzero", since = "1.28.0")]
-impl<T> PartialEq for NonZero<T>
+#[rustc_const_unstable(feature = "const_cmp", issue = "143800")]
+impl<T> const PartialEq for NonZero<T>
where
- T: ZeroablePrimitive + PartialEq,
+ T: ZeroablePrimitive + ~const PartialEq,
{
#[inline]
fn eq(&self, other: &Self) -> bool {
diff --git a/library/core/src/option.rs b/library/core/src/option.rs
index 9f43275..8036c59e 100644
--- a/library/core/src/option.rs
+++ b/library/core/src/option.rs
@@ -577,6 +577,7 @@
#![stable(feature = "rust1", since = "1.0.0")]
use crate::iter::{self, FusedIterator, TrustedLen};
+use crate::marker::Destruct;
use crate::ops::{self, ControlFlow, Deref, DerefMut};
use crate::panicking::{panic, panic_display};
use crate::pin::Pin;
@@ -649,7 +650,8 @@ pub const fn is_some(&self) -> bool {
#[must_use]
#[inline]
#[stable(feature = "is_some_and", since = "1.70.0")]
- pub fn is_some_and(self, f: impl FnOnce(T) -> bool) -> bool {
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn is_some_and(self, f: impl ~const FnOnce(T) -> bool + ~const Destruct) -> bool {
match self {
None => false,
Some(x) => f(x),
@@ -697,7 +699,8 @@ pub const fn is_none(&self) -> bool {
#[must_use]
#[inline]
#[stable(feature = "is_none_or", since = "1.82.0")]
- pub fn is_none_or(self, f: impl FnOnce(T) -> bool) -> bool {
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn is_none_or(self, f: impl ~const FnOnce(T) -> bool + ~const Destruct) -> bool {
match self {
None => true,
Some(x) => f(x),
@@ -1023,7 +1026,12 @@ pub const fn unwrap(self) -> T {
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn unwrap_or(self, default: T) -> T {
+ #[rustc_allow_const_fn_unstable(const_precise_live_drops)]
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn unwrap_or(self, default: T) -> T
+ where
+ T: ~const Destruct,
+ {
match self {
Some(x) => x,
None => default,
@@ -1042,9 +1050,10 @@ pub fn unwrap_or(self, default: T) -> T {
#[inline]
#[track_caller]
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn unwrap_or_else<F>(self, f: F) -> T
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn unwrap_or_else<F>(self, f: F) -> T
where
- F: FnOnce() -> T,
+ F: ~const FnOnce() -> T + ~const Destruct,
{
match self {
Some(x) => x,
@@ -1073,9 +1082,10 @@ pub fn unwrap_or_else<F>(self, f: F) -> T
/// [`FromStr`]: crate::str::FromStr
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn unwrap_or_default(self) -> T
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn unwrap_or_default(self) -> T
where
- T: Default,
+ T: ~const Default,
{
match self {
Some(x) => x,
@@ -1139,9 +1149,10 @@ pub fn unwrap_or_default(self) -> T
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn map<U, F>(self, f: F) -> Option<U>
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn map<U, F>(self, f: F) -> Option<U>
where
- F: FnOnce(T) -> U,
+ F: ~const FnOnce(T) -> U + ~const Destruct,
{
match self {
Some(x) => Some(f(x)),
@@ -1169,7 +1180,11 @@ pub fn map<U, F>(self, f: F) -> Option<U>
/// ```
#[inline]
#[stable(feature = "result_option_inspect", since = "1.76.0")]
- pub fn inspect<F: FnOnce(&T)>(self, f: F) -> Self {
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn inspect<F>(self, f: F) -> Self
+ where
+ F: ~const FnOnce(&T) + ~const Destruct,
+ {
if let Some(ref x) = self {
f(x);
}
@@ -1198,9 +1213,11 @@ pub fn inspect<F: FnOnce(&T)>(self, f: F) -> Self {
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[must_use = "if you don't need the returned value, use `if let` instead"]
- pub fn map_or<U, F>(self, default: U, f: F) -> U
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn map_or<U, F>(self, default: U, f: F) -> U
where
- F: FnOnce(T) -> U,
+ F: ~const FnOnce(T) -> U + ~const Destruct,
+ U: ~const Destruct,
{
match self {
Some(t) => f(t),
@@ -1243,10 +1260,11 @@ pub fn map_or<U, F>(self, default: U, f: F) -> U
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn map_or_else<U, D, F>(self, default: D, f: F) -> U
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn map_or_else<U, D, F>(self, default: D, f: F) -> U
where
- D: FnOnce() -> U,
- F: FnOnce(T) -> U,
+ D: ~const FnOnce() -> U + ~const Destruct,
+ F: ~const FnOnce(T) -> U + ~const Destruct,
{
match self {
Some(t) => f(t),
@@ -1273,10 +1291,11 @@ pub fn map_or_else<U, D, F>(self, default: D, f: F) -> U
/// [default value]: Default::default
#[inline]
#[unstable(feature = "result_option_map_or_default", issue = "138099")]
- pub fn map_or_default<U, F>(self, f: F) -> U
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn map_or_default<U, F>(self, f: F) -> U
where
- U: Default,
- F: FnOnce(T) -> U,
+ U: ~const Default,
+ F: ~const FnOnce(T) -> U + ~const Destruct,
{
match self {
Some(t) => f(t),
@@ -1307,7 +1326,8 @@ pub fn map_or_default<U, F>(self, f: F) -> U
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn ok_or<E>(self, err: E) -> Result<T, E> {
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn ok_or<E: ~const Destruct>(self, err: E) -> Result<T, E> {
match self {
Some(v) => Ok(v),
None => Err(err),
@@ -1332,9 +1352,10 @@ pub fn ok_or<E>(self, err: E) -> Result<T, E> {
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn ok_or_else<E, F>(self, err: F) -> Result<T, E>
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn ok_or_else<E, F>(self, err: F) -> Result<T, E>
where
- F: FnOnce() -> E,
+ F: ~const FnOnce() -> E + ~const Destruct,
{
match self {
Some(v) => Ok(v),
@@ -1463,7 +1484,12 @@ pub fn iter_mut(&mut self) -> IterMut<'_, T> {
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn and<U>(self, optb: Option<U>) -> Option<U> {
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn and<U>(self, optb: Option<U>) -> Option<U>
+ where
+ T: ~const Destruct,
+ U: ~const Destruct,
+ {
match self {
Some(_) => optb,
None => None,
@@ -1502,9 +1528,10 @@ pub fn and<U>(self, optb: Option<U>) -> Option<U> {
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_confusables("flat_map", "flatmap")]
- pub fn and_then<U, F>(self, f: F) -> Option<U>
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn and_then<U, F>(self, f: F) -> Option<U>
where
- F: FnOnce(T) -> Option<U>,
+ F: ~const FnOnce(T) -> Option<U> + ~const Destruct,
{
match self {
Some(x) => f(x),
@@ -1538,9 +1565,11 @@ pub fn and_then<U, F>(self, f: F) -> Option<U>
/// [`Some(t)`]: Some
#[inline]
#[stable(feature = "option_filter", since = "1.27.0")]
- pub fn filter<P>(self, predicate: P) -> Self
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn filter<P>(self, predicate: P) -> Self
where
- P: FnOnce(&T) -> bool,
+ P: ~const FnOnce(&T) -> bool + ~const Destruct,
+ T: ~const Destruct,
{
if let Some(x) = self {
if predicate(&x) {
@@ -1579,7 +1608,11 @@ pub fn filter<P>(self, predicate: P) -> Self
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn or(self, optb: Option<T>) -> Option<T> {
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn or(self, optb: Option<T>) -> Option<T>
+ where
+ T: ~const Destruct,
+ {
match self {
x @ Some(_) => x,
None => optb,
@@ -1601,9 +1634,13 @@ pub fn or(self, optb: Option<T>) -> Option<T> {
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn or_else<F>(self, f: F) -> Option<T>
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn or_else<F>(self, f: F) -> Option<T>
where
- F: FnOnce() -> Option<T>,
+ F: ~const FnOnce() -> Option<T> + ~const Destruct,
+ //FIXME(const_hack): this `T: ~const Destruct` is unnecessary, but even precise live drops can't tell
+ // no value of type `T` gets dropped here
+ T: ~const Destruct,
{
match self {
x @ Some(_) => x,
@@ -1634,7 +1671,11 @@ pub fn or_else<F>(self, f: F) -> Option<T>
/// ```
#[inline]
#[stable(feature = "option_xor", since = "1.37.0")]
- pub fn xor(self, optb: Option<T>) -> Option<T> {
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn xor(self, optb: Option<T>) -> Option<T>
+ where
+ T: ~const Destruct,
+ {
match (self, optb) {
(a @ Some(_), None) => a,
(None, b @ Some(_)) => b,
@@ -1668,7 +1709,11 @@ pub fn xor(self, optb: Option<T>) -> Option<T> {
#[must_use = "if you intended to set a value, consider assignment instead"]
#[inline]
#[stable(feature = "option_insert", since = "1.53.0")]
- pub fn insert(&mut self, value: T) -> &mut T {
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn insert(&mut self, value: T) -> &mut T
+ where
+ T: ~const Destruct,
+ {
*self = Some(value);
// SAFETY: the code above just filled the option
@@ -1720,9 +1765,10 @@ pub fn get_or_insert(&mut self, value: T) -> &mut T {
/// ```
#[inline]
#[stable(feature = "option_get_or_insert_default", since = "1.83.0")]
- pub fn get_or_insert_default(&mut self) -> &mut T
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn get_or_insert_default(&mut self) -> &mut T
where
- T: Default,
+ T: ~const Default + ~const Destruct,
{
self.get_or_insert_with(T::default)
}
@@ -1746,9 +1792,11 @@ pub fn get_or_insert_default(&mut self) -> &mut T
/// ```
#[inline]
#[stable(feature = "option_entry", since = "1.20.0")]
- pub fn get_or_insert_with<F>(&mut self, f: F) -> &mut T
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn get_or_insert_with<F>(&mut self, f: F) -> &mut T
where
- F: FnOnce() -> T,
+ F: ~const FnOnce() -> T + ~const Destruct,
+ T: ~const Destruct,
{
if let None = self {
*self = Some(f());
@@ -1812,9 +1860,10 @@ pub const fn take(&mut self) -> Option<T> {
/// ```
#[inline]
#[stable(feature = "option_take_if", since = "1.80.0")]
- pub fn take_if<P>(&mut self, predicate: P) -> Option<T>
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn take_if<P>(&mut self, predicate: P) -> Option<T>
where
- P: FnOnce(&mut T) -> bool,
+ P: ~const FnOnce(&mut T) -> bool + ~const Destruct,
{
if self.as_mut().map_or(false, predicate) { self.take() } else { None }
}
@@ -1859,7 +1908,12 @@ pub const fn replace(&mut self, value: T) -> Option<T> {
/// assert_eq!(x.zip(z), None);
/// ```
#[stable(feature = "option_zip_option", since = "1.46.0")]
- pub fn zip<U>(self, other: Option<U>) -> Option<(T, U)> {
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn zip<U>(self, other: Option<U>) -> Option<(T, U)>
+ where
+ T: ~const Destruct,
+ U: ~const Destruct,
+ {
match (self, other) {
(Some(a), Some(b)) => Some((a, b)),
_ => None,
@@ -1895,9 +1949,12 @@ pub fn zip<U>(self, other: Option<U>) -> Option<(T, U)> {
/// assert_eq!(x.zip_with(None, Point::new), None);
/// ```
#[unstable(feature = "option_zip", issue = "70086")]
- pub fn zip_with<U, F, R>(self, other: Option<U>, f: F) -> Option<R>
+ #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
+ pub const fn zip_with<U, F, R>(self, other: Option<U>, f: F) -> Option<R>
where
- F: FnOnce(T, U) -> R,
+ F: ~const FnOnce(T, U) -> R + ~const Destruct,
+ T: ~const Destruct,
+ U: ~const Destruct,
{
match (self, other) {
(Some(a), Some(b)) => Some(f(a, b)),
@@ -2243,7 +2300,8 @@ fn from(o: &'a mut Option<T>) -> Option<&'a mut T> {
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> crate::marker::StructuralPartialEq for Option<T> {}
#[stable(feature = "rust1", since = "1.0.0")]
-impl<T: PartialEq> PartialEq for Option<T> {
+#[rustc_const_unstable(feature = "const_cmp", issue = "143800")]
+impl<T: ~const PartialEq> const PartialEq for Option<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
// Spelling out the cases explicitly optimizes better than
diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs
index 7b9e049..a4be66b 100644
--- a/library/core/src/prelude/v1.rs
+++ b/library/core/src/prelude/v1.rs
@@ -80,7 +80,7 @@
alloc_error_handler, bench, derive, global_allocator, test, test_case,
};
-#[unstable(feature = "derive_const", issue = "none")]
+#[unstable(feature = "derive_const", issue = "118304")]
pub use crate::macros::builtin::derive_const;
#[unstable(
diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs
index 5ce72b4..1eda8bc 100644
--- a/library/core/src/slice/cmp.rs
+++ b/library/core/src/slice/cmp.rs
@@ -8,9 +8,10 @@
use crate::ops::ControlFlow;
#[stable(feature = "rust1", since = "1.0.0")]
-impl<T, U> PartialEq<[U]> for [T]
+#[rustc_const_unstable(feature = "const_cmp", issue = "143800")]
+impl<T, U> const PartialEq<[U]> for [T]
where
- T: PartialEq<U>,
+ T: ~const PartialEq<U>,
{
fn eq(&self, other: &[U]) -> bool {
SlicePartialEq::equal(self, other)
@@ -94,6 +95,8 @@ fn __chaining_ge(&self, other: &Self) -> ControlFlow<bool> {
#[doc(hidden)]
// intermediate trait for specialization of slice's PartialEq
+#[const_trait]
+#[rustc_const_unstable(feature = "const_cmp", issue = "143800")]
trait SlicePartialEq<B> {
fn equal(&self, other: &[B]) -> bool;
@@ -103,9 +106,10 @@ fn not_equal(&self, other: &[B]) -> bool {
}
// Generic slice equality
-impl<A, B> SlicePartialEq<B> for [A]
+#[rustc_const_unstable(feature = "const_cmp", issue = "143800")]
+impl<A, B> const SlicePartialEq<B> for [A]
where
- A: PartialEq<B>,
+ A: ~const PartialEq<B>,
{
default fn equal(&self, other: &[B]) -> bool {
if self.len() != other.len() {
@@ -115,11 +119,14 @@ impl<A, B> SlicePartialEq<B> for [A]
// Implemented as explicit indexing rather
// than zipped iterators for performance reasons.
// See PR https://github.com/rust-lang/rust/pull/116846
- for idx in 0..self.len() {
+ // FIXME(const_hack): make this a `for idx in 0..self.len()` loop.
+ let mut idx = 0;
+ while idx < self.len() {
// bound checks are optimized away
if self[idx] != other[idx] {
return false;
}
+ idx += 1;
}
true
@@ -128,9 +135,10 @@ impl<A, B> SlicePartialEq<B> for [A]
// When each element can be compared byte-wise, we can compare all the bytes
// from the whole size in one call to the intrinsics.
-impl<A, B> SlicePartialEq<B> for [A]
+#[rustc_const_unstable(feature = "const_cmp", issue = "143800")]
+impl<A, B> const SlicePartialEq<B> for [A]
where
- A: BytewiseEq<B>,
+ A: ~const BytewiseEq<B>,
{
fn equal(&self, other: &[B]) -> bool {
if self.len() != other.len() {
diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs
index 42ffc59..d0f2b92 100644
--- a/library/core/src/str/traits.rs
+++ b/library/core/src/str/traits.rs
@@ -23,7 +23,8 @@ fn cmp(&self, other: &str) -> Ordering {
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl PartialEq for str {
+#[rustc_const_unstable(feature = "const_cmp", issue = "143800")]
+impl const PartialEq for str {
#[inline]
fn eq(&self, other: &str) -> bool {
self.as_bytes() == other.as_bytes()
diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs
index 69f0335..70c1113 100644
--- a/library/std/src/prelude/v1.rs
+++ b/library/std/src/prelude/v1.rs
@@ -67,7 +67,7 @@
alloc_error_handler, bench, derive, global_allocator, test, test_case,
};
-#[unstable(feature = "derive_const", issue = "none")]
+#[unstable(feature = "derive_const", issue = "118304")]
pub use core::prelude::v1::derive_const;
// Do not `doc(no_inline)` either.
diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs
index 899e3fd..d4cbbe6 100644
--- a/src/bootstrap/src/core/build_steps/gcc.rs
+++ b/src/bootstrap/src/core/build_steps/gcc.rs
@@ -220,21 +220,18 @@ fn build_gcc(metadata: &Meta, builder: &Builder<'_>, target: TargetSelection) {
t!(fs::create_dir_all(install_dir));
// GCC creates files (e.g. symlinks to the downloaded dependencies)
- // in the source directory, which does not work with our CI setup, where we mount
+ // in the source directory, which does not work with our CI/Docker setup, where we mount
// source directories as read-only on Linux.
- // Therefore, as a part of the build in CI, we first copy the whole source directory
- // to the build directory, and perform the build from there.
- let src_dir = if builder.config.is_running_on_ci {
- let src_dir = builder.gcc_out(target).join("src");
- if src_dir.exists() {
- builder.remove_dir(&src_dir);
- }
- builder.create_dir(&src_dir);
- builder.cp_link_r(root, &src_dir);
- src_dir
- } else {
- root.clone()
- };
+ // And in general, we shouldn't be modifying the source directories if possible, even for local
+ // builds.
+ // Therefore, we first copy the whole source directory to the build directory, and perform the
+ // build from there.
+ let src_dir = builder.gcc_out(target).join("src");
+ if src_dir.exists() {
+ builder.remove_dir(&src_dir);
+ }
+ builder.create_dir(&src_dir);
+ builder.cp_link_r(root, &src_dir);
command(src_dir.join("contrib/download_prerequisites")).current_dir(&src_dir).run(builder);
let mut configure_cmd = command(src_dir.join("configure"));
diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs
index 307aa52..71fab0e 100644
--- a/src/bootstrap/src/core/config/toml/rust.rs
+++ b/src/bootstrap/src/core/config/toml/rust.rs
@@ -531,6 +531,14 @@ pub fn apply_rust_config(&mut self, toml_rust: Option<Rust>, warnings: Warnings)
lld_enabled = lld_enabled_toml;
std_features = std_features_toml;
+ if optimize_toml.as_ref().is_some_and(|v| matches!(v, RustOptimize::Bool(false))) {
+ eprintln!(
+ "WARNING: setting `optimize` to `false` is known to cause errors and \
+ should be considered unsupported. Refer to `bootstrap.example.toml` \
+ for more details."
+ );
+ }
+
optimize = optimize_toml;
self.rust_new_symbol_mangling = new_symbol_mangling;
set(&mut self.rust_optimize_tests, optimize_tests);
diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml
index 6c5e37b..f0c52fe 100644
--- a/src/ci/github-actions/jobs.yml
+++ b/src/ci/github-actions/jobs.yml
@@ -34,6 +34,8 @@
os: windows-2022
<<: *base-job
+ # NOTE: windows-2025 has less disk space available than windows-2022,
+ # because the D drive is missing.
- &job-windows-25
os: windows-2025
<<: *base-job
@@ -542,13 +544,13 @@
env:
RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-sanitizers --enable-profiler
SCRIPT: make ci-msvc-py
- <<: *job-windows-25
+ <<: *job-windows
- name: x86_64-msvc-2
env:
RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-sanitizers --enable-profiler
SCRIPT: make ci-msvc-ps1
- <<: *job-windows-25
+ <<: *job-windows
# i686-msvc is split into two jobs to run tests in parallel.
- name: i686-msvc-1
diff --git a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml
index 1e430d8..ad570ee 100644
--- a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml
+++ b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml
@@ -9,106 +9,12 @@
jobs:
pull:
if: github.repository == 'rust-lang/rustc-dev-guide'
- runs-on: ubuntu-latest
- outputs:
- pr_url: ${{ steps.update-pr.outputs.pr_url }}
- permissions:
- contents: write
- pull-requests: write
- steps:
- - uses: actions/checkout@v4
- with:
- # We need the full history for josh to work
- fetch-depth: '0'
- - name: Install stable Rust toolchain
- run: rustup update stable
- - uses: Swatinem/rust-cache@v2
- with:
- workspaces: "josh-sync"
- # Cache the josh directory with checked out rustc
- cache-directories: "/home/runner/.cache/rustc-dev-guide-josh"
- - name: Install josh
- run: RUSTFLAGS="--cap-lints warn" cargo install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04
- - name: Setup bot git name and email
- run: |
- git config --global user.name 'The rustc-dev-guide Cronjob Bot'
- git config --global user.email 'github-actions@github.com'
- - name: Perform rustc-pull
- id: rustc-pull
- # Turn off -e to disable early exit
- shell: bash {0}
- run: |
- cargo run --manifest-path josh-sync/Cargo.toml -- rustc-pull
- exitcode=$?
-
- # If no pull was performed, we want to mark this job as successful,
- # but we do not want to perform the follow-up steps.
- if [ $exitcode -eq 0 ]; then
- echo "pull_result=pull-finished" >> $GITHUB_OUTPUT
- elif [ $exitcode -eq 2 ]; then
- echo "pull_result=skipped" >> $GITHUB_OUTPUT
- exitcode=0
- fi
-
- exit ${exitcode}
- - name: Push changes to a branch
- if: ${{ steps.rustc-pull.outputs.pull_result == 'pull-finished' }}
- run: |
- # Update a sticky branch that is used only for rustc pulls
- BRANCH="rustc-pull"
- git switch -c $BRANCH
- git push -u origin $BRANCH --force
- - name: Create pull request
- id: update-pr
- if: ${{ steps.rustc-pull.outputs.pull_result == 'pull-finished' }}
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- # Check if an open pull request for an rustc pull update already exists
- # If it does, the previous push has just updated it
- # If not, we create it now
- RESULT=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | length' --json title`
- if [[ "$RESULT" -eq 0 ]]; then
- echo "Creating new pull request"
- PR_URL=`gh pr create -B master --title 'Rustc pull update' --body 'Latest update from rustc.'`
- echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT
- else
- PR_URL=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | .[0].url' --json url,title`
- echo "Updating pull request ${PR_URL}"
- echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT
- fi
- send-zulip-message:
- needs: [pull]
- if: ${{ !cancelled() }}
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - name: Compute message
- id: create-message
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- if [ "${{ needs.pull.result }}" == "failure" ]; then
- WORKFLOW_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
- echo "message=Rustc pull sync failed. Check out the [workflow URL]($WORKFLOW_URL)." >> $GITHUB_OUTPUT
- else
- CREATED_AT=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | .[0].createdAt' --json createdAt,title`
- PR_URL=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | .[0].url' --json url,title`
- week_ago=$(date +%F -d '7 days ago')
-
- # If there is an open PR that is at least a week old, post a message about it
- if [[ -n $DATE_GH && $DATE_GH < $week_ago ]]; then
- echo "message=A PR with a Rustc pull has been opened for more a week. Check out the [PR](${PR_URL})." >> $GITHUB_OUTPUT
- fi
- fi
- - name: Send a Zulip message about updated PR
- if: ${{ steps.create-message.outputs.message != '' }}
- uses: zulip/github-actions-zulip/send-message@e4c8f27c732ba9bd98ac6be0583096dea82feea5
- with:
- api-key: ${{ secrets.ZULIP_API_TOKEN }}
- email: "rustc-dev-guide-gha-notif-bot@rust-lang.zulipchat.com"
- organization-url: "https://rust-lang.zulipchat.com"
- to: 196385
- type: "stream"
- topic: "Subtree sync automation"
- content: ${{ steps.create-message.outputs.message }}
+ uses: rust-lang/josh-sync/.github/workflows/rustc-pull.yml@main
+ with:
+ zulip-stream-id: 196385
+ zulip-bot-email: "rustc-dev-guide-gha-notif-bot@rust-lang.zulipchat.com"
+ pr-base-branch: master
+ branch-name: rustc-pull
+ secrets:
+ zulip-api-token: ${{ secrets.ZULIP_API_TOKEN }}
+ token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/src/doc/rustc-dev-guide/README.md b/src/doc/rustc-dev-guide/README.md
index 0425c15..5932da4 100644
--- a/src/doc/rustc-dev-guide/README.md
+++ b/src/doc/rustc-dev-guide/README.md
@@ -72,49 +72,6 @@
## Synchronizing josh subtree with rustc
-This repository is linked to `rust-lang/rust` as a [josh](https://josh-project.github.io/josh/intro.html) subtree. You can use the following commands to synchronize the subtree in both directions.
+This repository is linked to `rust-lang/rust` as a [josh](https://josh-project.github.io/josh/intro.html) subtree. You can use the [rustc-josh-sync](https://github.com/rust-lang/josh-sync) tool to perform synchronization.
-You'll need to install `josh-proxy` locally via
-
-```
-cargo install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04
-```
-Older versions of `josh-proxy` may not round trip commits losslessly so it is important to install this exact version.
-
-### Pull changes from `rust-lang/rust` into this repository
-
-1) Checkout a new branch that will be used to create a PR into `rust-lang/rustc-dev-guide`
-2) Run the pull command
- ```
- cargo run --manifest-path josh-sync/Cargo.toml rustc-pull
- ```
-3) Push the branch to your fork and create a PR into `rustc-dev-guide`
-
-### Push changes from this repository into `rust-lang/rust`
-
-NOTE: If you use Git protocol to push to your fork of `rust-lang/rust`,
-ensure that you have this entry in your Git config,
-else the 2 steps that follow would prompt for a username and password:
-
-```
-[url "git@github.com:"]
-insteadOf = "https://github.com/"
-```
-
-1) Run the push command to create a branch named `<branch-name>` in a `rustc` fork under the `<gh-username>` account
- ```
- cargo run --manifest-path josh-sync/Cargo.toml rustc-push <branch-name> <gh-username>
- ```
-2) Create a PR from `<branch-name>` into `rust-lang/rust`
-
-#### Minimal git config
-
-For simplicity (ease of implementation purposes), the josh-sync script simply calls out to system git. This means that the git invocation may be influenced by global (or local) git configuration.
-
-You may observe "Nothing to pull" even if you *know* rustc-pull has something to pull if your global git config sets `fetch.prunetags = true` (and possibly other configurations may cause unexpected outcomes).
-
-To minimize the likelihood of this happening, you may wish to keep a separate *minimal* git config that *only* has `[user]` entries from global git config, then repoint system git to use the minimal git config instead. E.g.
-
-```
-GIT_CONFIG_GLOBAL=/path/to/minimal/gitconfig GIT_CONFIG_SYSTEM='' cargo run --manifest-path josh-sync/Cargo.toml -- rustc-pull
-```
+You can find a guide on how to perform the synchronization [here](./src/external-repos.md#synchronizing-a-josh-subtree).
diff --git a/src/doc/rustc-dev-guide/josh-sync/Cargo.lock b/src/doc/rustc-dev-guide/josh-sync/Cargo.lock
deleted file mode 100644
index a8183a7..0000000
--- a/src/doc/rustc-dev-guide/josh-sync/Cargo.lock
+++ /dev/null
@@ -1,430 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 4
-
-[[package]]
-name = "anstream"
-version = "0.6.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
-dependencies = [
- "anstyle",
- "anstyle-parse",
- "anstyle-query",
- "anstyle-wincon",
- "colorchoice",
- "is_terminal_polyfill",
- "utf8parse",
-]
-
-[[package]]
-name = "anstyle"
-version = "1.0.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
-
-[[package]]
-name = "anstyle-parse"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
-dependencies = [
- "utf8parse",
-]
-
-[[package]]
-name = "anstyle-query"
-version = "1.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
-dependencies = [
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "anstyle-wincon"
-version = "3.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
-dependencies = [
- "anstyle",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "anyhow"
-version = "1.0.95"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
-
-[[package]]
-name = "bitflags"
-version = "2.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "clap"
-version = "4.5.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
-dependencies = [
- "clap_builder",
- "clap_derive",
-]
-
-[[package]]
-name = "clap_builder"
-version = "4.5.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
-dependencies = [
- "anstream",
- "anstyle",
- "clap_lex",
- "strsim",
-]
-
-[[package]]
-name = "clap_derive"
-version = "4.5.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
-dependencies = [
- "heck",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "clap_lex"
-version = "0.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
-
-[[package]]
-name = "colorchoice"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
-
-[[package]]
-name = "directories"
-version = "5.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35"
-dependencies = [
- "dirs-sys",
-]
-
-[[package]]
-name = "dirs-sys"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
-dependencies = [
- "libc",
- "option-ext",
- "redox_users",
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.2.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi",
-]
-
-[[package]]
-name = "heck"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
-
-[[package]]
-name = "is_terminal_polyfill"
-version = "1.70.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
-
-[[package]]
-name = "josh-sync"
-version = "0.0.0"
-dependencies = [
- "anyhow",
- "clap",
- "directories",
- "xshell",
-]
-
-[[package]]
-name = "libc"
-version = "0.2.169"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
-
-[[package]]
-name = "libredox"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
-dependencies = [
- "bitflags",
- "libc",
-]
-
-[[package]]
-name = "option-ext"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.92"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.38"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "redox_users"
-version = "0.4.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
-dependencies = [
- "getrandom",
- "libredox",
- "thiserror",
-]
-
-[[package]]
-name = "strsim"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
-
-[[package]]
-name = "syn"
-version = "2.0.93"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "thiserror"
-version = "1.0.69"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
-dependencies = [
- "thiserror-impl",
-]
-
-[[package]]
-name = "thiserror-impl"
-version = "1.0.69"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
-
-[[package]]
-name = "utf8parse"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
-
-[[package]]
-name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
-
-[[package]]
-name = "windows-sys"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
-dependencies = [
- "windows-targets 0.48.5",
-]
-
-[[package]]
-name = "windows-sys"
-version = "0.59.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
-dependencies = [
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
-dependencies = [
- "windows_aarch64_gnullvm 0.48.5",
- "windows_aarch64_msvc 0.48.5",
- "windows_i686_gnu 0.48.5",
- "windows_i686_msvc 0.48.5",
- "windows_x86_64_gnu 0.48.5",
- "windows_x86_64_gnullvm 0.48.5",
- "windows_x86_64_msvc 0.48.5",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
-dependencies = [
- "windows_aarch64_gnullvm 0.52.6",
- "windows_aarch64_msvc 0.52.6",
- "windows_i686_gnu 0.52.6",
- "windows_i686_gnullvm",
- "windows_i686_msvc 0.52.6",
- "windows_x86_64_gnu 0.52.6",
- "windows_x86_64_gnullvm 0.52.6",
- "windows_x86_64_msvc 0.52.6",
-]
-
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
-
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
-
-[[package]]
-name = "windows_i686_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
-
-[[package]]
-name = "xshell"
-version = "0.2.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e7290c623014758632efe00737145b6867b66292c42167f2ec381eb566a373d"
-dependencies = [
- "xshell-macros",
-]
-
-[[package]]
-name = "xshell-macros"
-version = "0.2.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32ac00cd3f8ec9c1d33fb3e7958a82df6989c42d747bd326c822b1d625283547"
diff --git a/src/doc/rustc-dev-guide/josh-sync/Cargo.toml b/src/doc/rustc-dev-guide/josh-sync/Cargo.toml
deleted file mode 100644
index 1f8bf2a..0000000
--- a/src/doc/rustc-dev-guide/josh-sync/Cargo.toml
+++ /dev/null
@@ -1,9 +0,0 @@
-[package]
-name = "josh-sync"
-edition = "2024"
-
-[dependencies]
-anyhow = "1.0.95"
-clap = { version = "4.5.21", features = ["derive"] }
-directories = "5"
-xshell = "0.2.6"
diff --git a/src/doc/rustc-dev-guide/josh-sync/README.md b/src/doc/rustc-dev-guide/josh-sync/README.md
deleted file mode 100644
index a3dd876..0000000
--- a/src/doc/rustc-dev-guide/josh-sync/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-# Git josh sync
-This utility serves for syncing the josh git subtree to and from the rust-lang/rust repository.
-
-See CLI help for usage.
diff --git a/src/doc/rustc-dev-guide/josh-sync/src/main.rs b/src/doc/rustc-dev-guide/josh-sync/src/main.rs
deleted file mode 100644
index aeedee5..0000000
--- a/src/doc/rustc-dev-guide/josh-sync/src/main.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-use clap::Parser;
-
-use crate::sync::{GitSync, RustcPullError};
-
-mod sync;
-
-#[derive(clap::Parser)]
-enum Args {
- /// Pull changes from the main `rustc` repository.
- /// This creates new commits that should be then merged into `rustc-dev-guide`.
- RustcPull,
- /// Push changes from `rustc-dev-guide` to the given `branch` of a `rustc` fork under the given
- /// GitHub `username`.
- /// The pushed branch should then be merged into the `rustc` repository.
- RustcPush { branch: String, github_username: String },
-}
-
-fn main() -> anyhow::Result<()> {
- let args = Args::parse();
- let sync = GitSync::from_current_dir()?;
- match args {
- Args::RustcPull => {
- if let Err(error) = sync.rustc_pull(None) {
- match error {
- RustcPullError::NothingToPull => {
- eprintln!("Nothing to pull");
- std::process::exit(2);
- }
- RustcPullError::PullFailed(error) => {
- eprintln!("Pull failure: {error:?}");
- std::process::exit(1);
- }
- }
- }
- }
- Args::RustcPush { github_username, branch } => {
- sync.rustc_push(github_username, branch)?;
- }
- }
- Ok(())
-}
diff --git a/src/doc/rustc-dev-guide/josh-sync/src/sync.rs b/src/doc/rustc-dev-guide/josh-sync/src/sync.rs
deleted file mode 100644
index ed38d14..0000000
--- a/src/doc/rustc-dev-guide/josh-sync/src/sync.rs
+++ /dev/null
@@ -1,275 +0,0 @@
-use std::io::Write;
-use std::ops::Not;
-use std::path::PathBuf;
-use std::time::Duration;
-use std::{env, net, process};
-
-use anyhow::{Context, anyhow, bail};
-use xshell::{Shell, cmd};
-
-/// Used for rustc syncs.
-const JOSH_FILTER: &str = ":/src/doc/rustc-dev-guide";
-const JOSH_PORT: u16 = 42042;
-const UPSTREAM_REPO: &str = "rust-lang/rust";
-
-pub enum RustcPullError {
- /// No changes are available to be pulled.
- NothingToPull,
- /// A rustc-pull has failed, probably a git operation error has occurred.
- PullFailed(anyhow::Error),
-}
-
-impl<E> From<E> for RustcPullError
-where
- E: Into<anyhow::Error>,
-{
- fn from(error: E) -> Self {
- Self::PullFailed(error.into())
- }
-}
-
-pub struct GitSync {
- dir: PathBuf,
-}
-
-/// This code was adapted from the miri repository
-/// (https://github.com/rust-lang/miri/blob/6a68a79f38064c3bc30617cca4bdbfb2c336b140/miri-script/src/commands.rs#L236).
-impl GitSync {
- pub fn from_current_dir() -> anyhow::Result<Self> {
- Ok(Self { dir: std::env::current_dir()? })
- }
-
- pub fn rustc_pull(&self, commit: Option<String>) -> Result<(), RustcPullError> {
- let sh = Shell::new()?;
- sh.change_dir(&self.dir);
- let commit = commit.map(Ok).unwrap_or_else(|| {
- let rust_repo_head =
- cmd!(sh, "git ls-remote https://github.com/{UPSTREAM_REPO}/ HEAD").read()?;
- rust_repo_head
- .split_whitespace()
- .next()
- .map(|front| front.trim().to_owned())
- .ok_or_else(|| anyhow!("Could not obtain Rust repo HEAD from remote."))
- })?;
- // Make sure the repo is clean.
- if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() {
- return Err(anyhow::anyhow!(
- "working directory must be clean before performing rustc pull"
- )
- .into());
- }
- // Make sure josh is running.
- let josh = Self::start_josh()?;
- let josh_url =
- format!("http://localhost:{JOSH_PORT}/{UPSTREAM_REPO}.git@{commit}{JOSH_FILTER}.git");
-
- let previous_base_commit = sh.read_file("rust-version")?.trim().to_string();
- if previous_base_commit == commit {
- return Err(RustcPullError::NothingToPull);
- }
-
- // Update rust-version file. As a separate commit, since making it part of
- // the merge has confused the heck out of josh in the past.
- // We pass `--no-verify` to avoid running git hooks.
- // We do this before the merge so that if there are merge conflicts, we have
- // the right rust-version file while resolving them.
- sh.write_file("rust-version", format!("{commit}\n"))?;
- const PREPARING_COMMIT_MESSAGE: &str = "Preparing for merge from rustc";
- cmd!(sh, "git commit rust-version --no-verify -m {PREPARING_COMMIT_MESSAGE}")
- .run()
- .context("FAILED to commit rust-version file, something went wrong")?;
-
- // Fetch given rustc commit.
- cmd!(sh, "git fetch {josh_url}")
- .run()
- .inspect_err(|_| {
- // Try to un-do the previous `git commit`, to leave the repo in the state we found it.
- cmd!(sh, "git reset --hard HEAD^")
- .run()
- .expect("FAILED to clean up again after failed `git fetch`, sorry for that");
- })
- .context("FAILED to fetch new commits, something went wrong (committing the rust-version file has been undone)")?;
-
- // This should not add any new root commits. So count those before and after merging.
- let num_roots = || -> anyhow::Result<u32> {
- Ok(cmd!(sh, "git rev-list HEAD --max-parents=0 --count")
- .read()
- .context("failed to determine the number of root commits")?
- .parse::<u32>()?)
- };
- let num_roots_before = num_roots()?;
-
- let sha =
- cmd!(sh, "git rev-parse HEAD").output().context("FAILED to get current commit")?.stdout;
-
- // Merge the fetched commit.
- const MERGE_COMMIT_MESSAGE: &str = "Merge from rustc";
- cmd!(sh, "git merge FETCH_HEAD --no-verify --no-ff -m {MERGE_COMMIT_MESSAGE}")
- .run()
- .context("FAILED to merge new commits, something went wrong")?;
-
- let current_sha =
- cmd!(sh, "git rev-parse HEAD").output().context("FAILED to get current commit")?.stdout;
- if current_sha == sha {
- cmd!(sh, "git reset --hard HEAD^")
- .run()
- .expect("FAILED to clean up after creating the preparation commit");
- eprintln!(
- "No merge was performed, no changes to pull were found. Rolled back the preparation commit."
- );
- return Err(RustcPullError::NothingToPull);
- }
-
- // Check that the number of roots did not increase.
- if num_roots()? != num_roots_before {
- return Err(anyhow::anyhow!(
- "Josh created a new root commit. This is probably not the history you want."
- )
- .into());
- }
-
- drop(josh);
- Ok(())
- }
-
- pub fn rustc_push(&self, github_user: String, branch: String) -> anyhow::Result<()> {
- let sh = Shell::new()?;
- sh.change_dir(&self.dir);
- let base = sh.read_file("rust-version")?.trim().to_owned();
- // Make sure the repo is clean.
- if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() {
- bail!("working directory must be clean before running `rustc-push`");
- }
- // Make sure josh is running.
- let josh = Self::start_josh()?;
- let josh_url =
- format!("http://localhost:{JOSH_PORT}/{github_user}/rust.git{JOSH_FILTER}.git");
-
- // Find a repo we can do our preparation in.
- if let Ok(rustc_git) = env::var("RUSTC_GIT") {
- // If rustc_git is `Some`, we'll use an existing fork for the branch updates.
- sh.change_dir(rustc_git);
- } else {
- // Otherwise, do this in the local repo.
- println!(
- "This will pull a copy of the rust-lang/rust history into this checkout, growing it by about 1GB."
- );
- print!(
- "To avoid that, abort now and set the `RUSTC_GIT` environment variable to an existing rustc checkout. Proceed? [y/N] "
- );
- std::io::stdout().flush()?;
- let mut answer = String::new();
- std::io::stdin().read_line(&mut answer)?;
- if answer.trim().to_lowercase() != "y" {
- std::process::exit(1);
- }
- };
- // Prepare the branch. Pushing works much better if we use as base exactly
- // the commit that we pulled from last time, so we use the `rust-version`
- // file to find out which commit that would be.
- println!("Preparing {github_user}/rust (base: {base})...");
- if cmd!(sh, "git fetch https://github.com/{github_user}/rust {branch}")
- .ignore_stderr()
- .read()
- .is_ok()
- {
- println!(
- "The branch '{branch}' seems to already exist in 'https://github.com/{github_user}/rust'. Please delete it and try again."
- );
- std::process::exit(1);
- }
- cmd!(sh, "git fetch https://github.com/{UPSTREAM_REPO} {base}").run()?;
- cmd!(sh, "git push https://github.com/{github_user}/rust {base}:refs/heads/{branch}")
- .ignore_stdout()
- .ignore_stderr() // silence the "create GitHub PR" message
- .run()?;
- println!();
-
- // Do the actual push.
- sh.change_dir(&self.dir);
- println!("Pushing changes...");
- cmd!(sh, "git push {josh_url} HEAD:{branch}").run()?;
- println!();
-
- // Do a round-trip check to make sure the push worked as expected.
- cmd!(sh, "git fetch {josh_url} {branch}").ignore_stderr().read()?;
- let head = cmd!(sh, "git rev-parse HEAD").read()?;
- let fetch_head = cmd!(sh, "git rev-parse FETCH_HEAD").read()?;
- if head != fetch_head {
- bail!(
- "Josh created a non-roundtrip push! Do NOT merge this into rustc!\n\
- Expected {head}, got {fetch_head}."
- );
- }
- println!(
- "Confirmed that the push round-trips back to rustc-dev-guide properly. Please create a rustc PR:"
- );
- println!(
- // Open PR with `subtree update` title to silence the `no-merges` triagebot check
- " https://github.com/{UPSTREAM_REPO}/compare/{github_user}:{branch}?quick_pull=1&title=rustc-dev-guide+subtree+update&body=r?+@ghost"
- );
-
- drop(josh);
- Ok(())
- }
-
- fn start_josh() -> anyhow::Result<impl Drop> {
- // Determine cache directory.
- let local_dir = {
- let user_dirs =
- directories::ProjectDirs::from("org", "rust-lang", "rustc-dev-guide-josh").unwrap();
- user_dirs.cache_dir().to_owned()
- };
-
- // Start josh, silencing its output.
- let mut cmd = process::Command::new("josh-proxy");
- cmd.arg("--local").arg(local_dir);
- cmd.arg("--remote").arg("https://github.com");
- cmd.arg("--port").arg(JOSH_PORT.to_string());
- cmd.arg("--no-background");
- cmd.stdout(process::Stdio::null());
- cmd.stderr(process::Stdio::null());
- let josh = cmd.spawn().context("failed to start josh-proxy, make sure it is installed")?;
-
- // Create a wrapper that stops it on drop.
- struct Josh(process::Child);
- impl Drop for Josh {
- fn drop(&mut self) {
- #[cfg(unix)]
- {
- // Try to gracefully shut it down.
- process::Command::new("kill")
- .args(["-s", "INT", &self.0.id().to_string()])
- .output()
- .expect("failed to SIGINT josh-proxy");
- // Sadly there is no "wait with timeout"... so we just give it some time to finish.
- std::thread::sleep(Duration::from_millis(100));
- // Now hopefully it is gone.
- if self.0.try_wait().expect("failed to wait for josh-proxy").is_some() {
- return;
- }
- }
- // If that didn't work (or we're not on Unix), kill it hard.
- eprintln!(
- "I have to kill josh-proxy the hard way, let's hope this does not break anything."
- );
- self.0.kill().expect("failed to SIGKILL josh-proxy");
- }
- }
-
- // Wait until the port is open. We try every 10ms until 1s passed.
- for _ in 0..100 {
- // This will generally fail immediately when the port is still closed.
- let josh_ready = net::TcpStream::connect_timeout(
- &net::SocketAddr::from(([127, 0, 0, 1], JOSH_PORT)),
- Duration::from_millis(1),
- );
- if josh_ready.is_ok() {
- return Ok(Josh(josh));
- }
- // Not ready yet.
- std::thread::sleep(Duration::from_millis(10));
- }
- bail!("Even after waiting for 1s, josh-proxy is still not available.")
- }
-}
diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version
index e444613..3f10132 100644
--- a/src/doc/rustc-dev-guide/rust-version
+++ b/src/doc/rustc-dev-guide/rust-version
@@ -1 +1 @@
-c96a69059ecc618b519da385a6ccd03155aa0237
+fd2eb391d032181459773f3498c17b198513e0d0
diff --git a/src/doc/rustc-dev-guide/src/autodiff/installation.md b/src/doc/rustc-dev-guide/src/autodiff/installation.md
index c9b28dc..a550f6d 100644
--- a/src/doc/rustc-dev-guide/src/autodiff/installation.md
+++ b/src/doc/rustc-dev-guide/src/autodiff/installation.md
@@ -6,14 +6,14 @@
First you need to clone and configure the Rust repository:
```bash
-git clone --depth=1 git@github.com:rust-lang/rust.git
+git clone git@github.com:rust-lang/rust
cd rust
./configure --enable-llvm-link-shared --enable-llvm-plugins --enable-llvm-enzyme --release-channel=nightly --enable-llvm-assertions --enable-clang --enable-lld --enable-option-checking --enable-ninja --disable-docs
```
Afterwards you can build rustc using:
```bash
-./x.py build --stage 1 library
+./x build --stage 1 library
```
Afterwards rustc toolchain link will allow you to use it through cargo:
@@ -25,10 +25,10 @@
You can then run our test cases:
```bash
-./x.py test --stage 1 tests/codegen/autodiff
-./x.py test --stage 1 tests/pretty/autodiff
-./x.py test --stage 1 tests/ui/autodiff
-./x.py test --stage 1 tests/ui/feature-gates/feature-gate-autodiff.rs
+./x test --stage 1 tests/codegen/autodiff
+./x test --stage 1 tests/pretty/autodiff
+./x test --stage 1 tests/ui/autodiff
+./x test --stage 1 tests/ui/feature-gates/feature-gate-autodiff.rs
```
Autodiff is still experimental, so if you want to use it in your own projects, you will need to add `lto="fat"` to your Cargo.toml
@@ -45,7 +45,7 @@
```
Then build rustc in a slightly altered way:
```bash
-git clone --depth=1 https://github.com/rust-lang/rust.git
+git clone https://github.com/rust-lang/rust
cd rust
./configure --enable-llvm-link-shared --enable-llvm-plugins --enable-llvm-enzyme --release-channel=nightly --enable-llvm-assertions --enable-clang --enable-lld --enable-option-checking --enable-ninja --disable-docs
./x dist
@@ -66,7 +66,7 @@
However, if you prefer to just build Enzyme without Rust, then these instructions might help.
```bash
-git clone --depth=1 git@github.com:llvm/llvm-project.git
+git clone git@github.com:llvm/llvm-project
cd llvm-project
mkdir build
cd build
@@ -77,7 +77,7 @@
This gives you a working LLVM build, now we can continue with building Enzyme.
Leave the `llvm-project` folder, and execute the following commands:
```bash
-git clone git@github.com:EnzymeAD/Enzyme.git
+git clone git@github.com:EnzymeAD/Enzyme
cd Enzyme/enzyme
mkdir build
cd build
diff --git a/src/doc/rustc-dev-guide/src/external-repos.md b/src/doc/rustc-dev-guide/src/external-repos.md
index f3170c9..ecc65b2 100644
--- a/src/doc/rustc-dev-guide/src/external-repos.md
+++ b/src/doc/rustc-dev-guide/src/external-repos.md
@@ -9,7 +9,7 @@
As a general rule:
- Use crates.io for libraries that could be useful for others in the ecosystem
- Use subtrees for tools that depend on compiler internals and need to be updated if there are breaking
-changes
+ changes
- Use submodules for tools that are independent of the compiler
## External Dependencies (subtrees)
@@ -23,6 +23,8 @@
* [rust-analyzer](https://github.com/rust-lang/rust-analyzer)
* [rustc_codegen_cranelift](https://github.com/rust-lang/rustc_codegen_cranelift)
* [rustc-dev-guide](https://github.com/rust-lang/rustc-dev-guide)
+* [compiler-builtins](https://github.com/rust-lang/compiler-builtins)
+* [stdarch](https://github.com/rust-lang/stdarch)
In contrast to `submodule` dependencies
(see below for those), the `subtree` dependencies are just regular files and directories which can
@@ -34,20 +36,61 @@
`subtree` dependencies are currently managed by two distinct approaches:
* Using `git subtree`
- * `clippy` ([sync guide](https://doc.rust-lang.org/nightly/clippy/development/infrastructure/sync.html#performing-the-sync-from-rust-langrust-to-clippy))
- * `portable-simd` ([sync script](https://github.com/rust-lang/portable-simd/blob/master/subtree-sync.sh))
- * `rustfmt`
- * `rustc_codegen_cranelift` ([sync script](https://github.com/rust-lang/rustc_codegen_cranelift/blob/113af154d459e41b3dc2c5d7d878e3d3a8f33c69/scripts/rustup.sh#L7))
+ * `clippy` ([sync guide](https://doc.rust-lang.org/nightly/clippy/development/infrastructure/sync.html#performing-the-sync-from-rust-langrust-to-clippy))
+ * `portable-simd` ([sync script](https://github.com/rust-lang/portable-simd/blob/master/subtree-sync.sh))
+ * `rustfmt`
+ * `rustc_codegen_cranelift` ([sync script](https://github.com/rust-lang/rustc_codegen_cranelift/blob/113af154d459e41b3dc2c5d7d878e3d3a8f33c69/scripts/rustup.sh#L7))
* Using the [josh] tool
- * `miri` ([sync guide](https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#advanced-topic-syncing-with-the-rustc-repo))
- * `rust-analyzer` ([sync script](https://github.com/rust-lang/rust-analyzer/blob/2e13684be123eca7181aa48e043e185d8044a84a/xtask/src/release.rs#L147))
- * `rustc-dev-guide` ([sync guide](https://github.com/rust-lang/rustc-dev-guide#synchronizing-josh-subtree-with-rustc))
+ * `miri` ([sync guide](https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#advanced-topic-syncing-with-the-rustc-repo))
+ * `rust-analyzer` ([sync script](https://github.com/rust-lang/rust-analyzer/blob/2e13684be123eca7181aa48e043e185d8044a84a/xtask/src/release.rs#L147))
+ * `rustc-dev-guide` ([josh sync](#synchronizing-a-josh-subtree))
+ * `compiler-builtins` ([josh sync](#synchronizing-a-josh-subtree))
+ * `stdarch` ([josh sync](#synchronizing-a-josh-subtree))
-The [josh] tool is an alternative to git subtrees, which manages git history in a different way and scales better to larger repositories. Specific tooling is required to work with josh, you can check out the `miri` or `rust-analyzer` scripts linked above for inspiration. If you want to migrate a repository dependency from `git subtree` or `git submodule` to josh, you can check out [this guide](https://hackmd.io/7pOuxnkdQDaL1Y1FQr65xg).
+### Josh subtrees
-Below you can find a guide on how to perform push and pull synchronization with the main rustc repo using `git subtree`, although these instructions might differ repo from repo.
+The [josh] tool is an alternative to git subtrees, which manages git history in a different way and scales better to larger repositories. Specific tooling is required to work with josh; you can check out the `miri` or `rust-analyzer` scripts linked above for inspiration. We provide a helper [`rustc-josh-sync`][josh-sync] tool to help with the synchronization, described [below](#synchronizing-a-josh-subtree).
-### Synchronizing a subtree
+### Synchronizing a Josh subtree
+
+We use a dedicated tool called [`rustc-josh-sync`][josh-sync] for performing Josh subtree updates.
+Currently, we are migrating Josh repositories to it. So far, it is used in:
+
+- compiler-builtins
+- rustc-dev-guide
+- stdarch
+
+To install the tool:
+```
+cargo install --locked --git https://github.com/rust-lang/josh-sync
+```
+
+Both pulls (synchronize changes from rust-lang/rust into the subtree) and pushes (synchronize
+changes from the subtree to rust-lang/rust) are performed from the subtree repository (so first
+switch to its repository checkout directory in your terminal).
+
+#### Performing pull
+1) Checkout a new branch that will be used to create a PR into the subtree
+2) Run the pull command
+ ```
+ rustc-josh-sync pull
+ ```
+3) Push the branch to your fork and create a PR into the subtree repository
+ - If you have `gh` CLI installed, `rustc-josh-sync` can create the PR for you.
+
+#### Performing push
+
+1) Run the push command to create a branch named `<branch-name>` in a `rustc` fork under the `<gh-username>` account
+ ```
+ rustc-josh-sync push <branch-name> <gh-username>
+ ```
+2) Create a PR from `<branch-name>` into `rust-lang/rust`
+
+### Creating a new Josh subtree dependency
+
+If you want to migrate a repository dependency from `git subtree` or `git submodule` to josh, you can check out [this guide](https://hackmd.io/7pOuxnkdQDaL1Y1FQr65xg).
+
+### Synchronizing a git subtree
Periodically the changes made to subtree based dependencies need to be synchronized between this
repository and the upstream tool repositories.
@@ -129,3 +172,4 @@
[toolstate website]: https://rust-lang-nursery.github.io/rust-toolstate/
[Toolstate chapter]: https://forge.rust-lang.org/infra/toolstate.html
[josh]: https://josh-project.github.io/josh/intro.html
+[josh-sync]: https://github.com/rust-lang/josh-sync
diff --git a/src/doc/rustc-dev-guide/src/offload/installation.md b/src/doc/rustc-dev-guide/src/offload/installation.md
index 2536af0..1962314 100644
--- a/src/doc/rustc-dev-guide/src/offload/installation.md
+++ b/src/doc/rustc-dev-guide/src/offload/installation.md
@@ -6,14 +6,14 @@
First you need to clone and configure the Rust repository:
```bash
-git clone --depth=1 git@github.com:rust-lang/rust.git
+git clone git@github.com:rust-lang/rust
cd rust
./configure --enable-llvm-link-shared --release-channel=nightly --enable-llvm-assertions --enable-offload --enable-enzyme --enable-clang --enable-lld --enable-option-checking --enable-ninja --disable-docs
```
Afterwards you can build rustc using:
```bash
-./x.py build --stage 1 library
+./x build --stage 1 library
```
Afterwards rustc toolchain link will allow you to use it through cargo:
@@ -26,7 +26,7 @@
## Build instruction for LLVM itself
```bash
-git clone --depth=1 git@github.com:llvm/llvm-project.git
+git clone git@github.com:llvm/llvm-project
cd llvm-project
mkdir build
cd build
@@ -40,7 +40,7 @@
## Testing
run
```
-./x.py test --stage 1 tests/codegen/gpu_offload
+./x test --stage 1 tests/codegen/gpu_offload
```
## Usage
diff --git a/src/doc/rustc-dev-guide/src/solve/invariants.md b/src/doc/rustc-dev-guide/src/solve/invariants.md
index fd12b19..8ec15f3 100644
--- a/src/doc/rustc-dev-guide/src/solve/invariants.md
+++ b/src/doc/rustc-dev-guide/src/solve/invariants.md
@@ -4,17 +4,15 @@
There are a lot of invariants - things the type system guarantees to be true at all times -
which are desirable or expected from other languages and type systems. Unfortunately, quite
-a few of them do not hold in Rust right now. This is either a fundamental to its design or
-caused by bugs and something that may change in the future.
+a few of them do not hold in Rust right now. This is either fundamental to its design or
+caused by bugs, and something that may change in the future.
-It is important to know about the things you can assume while working on - and with - the
+It is important to know about the things you can assume while working on, and with, the
type system, so here's an incomplete and unofficial list of invariants of
the core type system:
-- ✅: this invariant mostly holds, with some weird exceptions, you can rely on it outside
-of these cases
-- ❌: this invariant does not hold, either due to bugs or by design, you must not rely on
-it for soundness or have to be incredibly careful when doing so
+- ✅: this invariant mostly holds, with some weird exceptions or current bugs
+- ❌: this invariant does not hold, and is unlikely to do so in the future; do not rely on it for soundness or have to be incredibly careful when doing so
### `wf(X)` implies `wf(normalize(X))` ✅
@@ -23,20 +21,23 @@
otherwise we would have to re-check for well-formedness for these
types.
+This currently does not hold due to a type system unsoundness: [#84533](https://github.com/rust-lang/rust/issues/84533).
+
### Structural equality modulo regions implies semantic equality ✅
If you have a some type and equate it to itself after replacing any regions with unique
inference variables in both the lhs and rhs, the now potentially structurally different
types should still be equal to each other.
-Needed to prevent goals from succeeding in HIR typeck and then failing in MIR borrowck.
-If this invariant is broken MIR typeck ends up failing with an ICE.
+This is needed to prevent goals from succeeding in HIR typeck and then failing in MIR borrowck.
+If this invariant is broken, MIR typeck ends up failing with an ICE.
### Applying inference results from a goal does not change its result ❌
TODO: this invariant is formulated in a weird way and needs to be elaborated.
Pretty much: I would like this check to only fail if there's a solver bug:
-https://github.com/rust-lang/rust/blob/2ffeb4636b4ae376f716dc4378a7efb37632dc2d/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs#L391-L407
+<https://github.com/rust-lang/rust/blob/2ffeb4636b4ae376f716dc4378a7efb37632dc2d/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs#L391-L407>.
+We should readd this check and see where it breaks :3
If we prove some goal/equate types/whatever, apply the resulting inference constraints,
and then redo the original action, the result should be the same.
@@ -73,34 +74,10 @@
It is however very difficult to imagine a sound type system without this invariant, so
the issue is that the invariant is broken, not that we incorrectly rely on it.
-### Generic goals and their instantiations have the same result ✅
-
-Pretty much: If we successfully typecheck a generic function concrete instantiations
-of that function should also typeck. We should not get errors post-monomorphization.
-We can however get overflow errors at that point.
-
-TODO: example for overflow error post-monomorphization
-
-This invariant is relied on to allow the normalization of generic aliases. Breaking
-it can easily result in unsoundness, e.g. [#57893](https://github.com/rust-lang/rust/issues/57893)
-
-### Trait goals in empty environments are proven by a unique impl ✅
-
-If a trait goal holds with an empty environment, there should be a unique `impl`,
-either user-defined or builtin, which is used to prove that goal. This is
-necessary to select a unique method.
-
-We do however break this invariant in few cases, some of which are due to bugs,
-some by design:
-- *marker traits* are allowed to overlap as they do not have associated items
-- *specialization* allows specializing impls to overlap with their parent
-- the builtin trait object trait implementation can overlap with a user-defined impl:
-[#57893]
-
### The type system is complete ❌
-The type system is not complete, it often adds unnecessary inference constraints, and errors
-even though the goal could hold.
+The type system is not complete.
+It often adds unnecessary inference constraints, and errors even though the goal could hold.
- method selection
- opaque type inference
@@ -108,47 +85,88 @@
- preferring `ParamEnv` candidates over `Impl` candidates during candidate selection
in the trait solver
+### Goals keep their result from HIR typeck afterwards ✅
+
+Having a goal which succeeds during HIR typeck but fails when being reevaluated during MIR borrowck causes ICE, e.g. [#140211](https://github.com/rust-lang/rust/issues/140211).
+
+Having a goal which succeeds during HIR typeck but fails after being instantiated is unsound, e.g. [#140212](https://github.com/rust-lang/rust/issues/140212).
+
+It is interesting that we allow some incompleteness in the trait solver while still maintaining this limitation. It would be nice if there was a clear way to separate the "allowed incompleteness" from behavior which would break this invariant.
+
+#### Normalization must not change results
+
+This invariant is relied on to allow the normalization of generic aliases. Breaking
+it can easily result in unsoundness, e.g. [#57893](https://github.com/rust-lang/rust/issues/57893).
+
+#### Goals may still overflow after instantiation
+
+This happens they start to hit the recursion limit.
+We also have diverging aliases which are scuffed.
+It's unclear how these should be handled :3
+
+### Trait goals in empty environments are proven by a unique impl ✅
+
+If a trait goal holds with an empty environment, there should be a unique `impl`,
+either user-defined or builtin, which is used to prove that goal. This is
+necessary to select unique methods and associated items.
+
+We do however break this invariant in a few cases, some of which are due to bugs, some by design:
+- *marker traits* are allowed to overlap as they do not have associated items
+- *specialization* allows specializing impls to overlap with their parent
+- the builtin trait object trait implementation can overlap with a user-defined impl:
+[#57893](https://github.com/rust-lang/rust/issues/57893)
+
+
#### The type system is complete during the implicit negative overlap check in coherence ✅
-For more on overlap checking: [coherence]
+For more on overlap checking, see [Coherence chapter].
-During the implicit negative overlap check in coherence we must never return *error* for
-goals which can be proven. This would allow for overlapping impls with potentially different
-associated items, breaking a bunch of other invariants.
+During the implicit negative overlap check in coherence,
+we must never return *error* for goals which can be proven.
+This would allow for overlapping impls with potentially different associated items,
+breaking a bunch of other invariants.
This invariant is currently broken in many different ways while actually something we rely on.
We have to be careful as it is quite easy to break:
- generalization of aliases
- generalization during subtyping binders (luckily not exploitable in coherence)
-### Trait solving must be (free) lifetime agnostic ✅
+### Trait solving must not depend on lifetimes being different ✅
-Trait solving during codegen should have the same result as during typeck. As we erase
-all free regions during codegen we must not rely on them during typeck. A noteworthy example
-is special behavior for `'static`.
+If a goal holds with lifetimes being different, it must also hold with these lifetimes being the same. We otherwise get post-monomorphization errors during codegen or unsoundness due to invalid vtables.
+
+We could also just get inconsistent behavior when first proving a goal with different lifetimes which are later constrained to be equal.
+
+### Trait solving in bodies must not depend on lifetimes being equal ✅
We also have to be careful with relying on equality of regions in the trait solver.
This is fine for codegen, as we treat all erased regions as equal. We can however
lose equality information from HIR to MIR typeck.
-The new solver "uniquifies regions" during canonicalization, canonicalizing `u32: Trait<'x, 'x>`
-as `exists<'0, '1> u32: Trait<'0, '1>`, to make it harder to rely on this property.
+This currently does not hold with the new solver: [trait-system-refactor-initiative#27](https://github.com/rust-lang/trait-system-refactor-initiative/issues/27).
### Removing ambiguity makes strictly more things compile ❌
Ideally we *should* not rely on ambiguity for things to compile.
Not doing that will cause future improvements to be breaking changes.
-Due to *incompleteness* this is not the case and improving inference can result in inference
-changes, breaking existing projects.
+Due to *incompleteness* this is not the case,
+and improving inference can result in inference changes, breaking existing projects.
### Semantic equality implies structural equality ✅
Two types being equal in the type system must mean that they have the
same `TypeId` after instantiating their generic parameters with concrete
-arguments. This currently does not hold: [#97156].
+arguments. We can otherwise use their different `TypeId`s to impact trait selection.
-[#57893]: https://github.com/rust-lang/rust/issues/57893
-[#97156]: https://github.com/rust-lang/rust/issues/97156
-[#114936]: https://github.com/rust-lang/rust/issues/114936
-[coherence]: ../coherence.md
+We lookup types using structural equality during codegen, but this shouldn't necessarily be unsound
+- may result in redundant method codegen or backend type check errors?
+- we also rely on it in CTFE assertions
+
+### Semantically different types have different `TypeId`s ✅
+
+Semantically different `'static` types need different `TypeId`s to avoid transmutes,
+for example `for<'a> fn(&'a str)` vs `fn(&'static str)` must have a different `TypeId`.
+
+
+[coherence chapter]: ../coherence.md
diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md
index 839076b..63aa08c 100644
--- a/src/doc/rustc-dev-guide/src/tests/directives.md
+++ b/src/doc/rustc-dev-guide/src/tests/directives.md
@@ -59,7 +59,7 @@
| `aux-crate` | Like `aux-build` but makes available as extern prelude | All except `run-make` | `<extern_prelude_name>=<path/to/aux/file.rs>` |
| `aux-codegen-backend` | Similar to `aux-build` but pass the compiled dylib to `-Zcodegen-backend` when building the main file | `ui-fulldeps` | Path to codegen backend file |
| `proc-macro` | Similar to `aux-build`, but for aux forces host and don't use `-Cprefer-dynamic`[^pm]. | All except `run-make` | Path to auxiliary proc-macro `.rs` file |
-| `build-aux-docs` | Build docs for auxiliaries as well | All except `run-make` | N/A |
+| `build-aux-docs` | Build docs for auxiliaries as well. Note that this only works with `aux-build`, not `aux-crate`. | All except `run-make` | N/A |
[^pm]: please see the Auxiliary proc-macro section in the
[compiletest](./compiletest.md) chapter for specifics.
diff --git a/src/doc/rustc-dev-guide/src/tests/intro.md b/src/doc/rustc-dev-guide/src/tests/intro.md
index c55d60f..79b96c4 100644
--- a/src/doc/rustc-dev-guide/src/tests/intro.md
+++ b/src/doc/rustc-dev-guide/src/tests/intro.md
@@ -111,12 +111,14 @@
This requires building all of the documentation, which might take a while.
-### Dist check
+### `distcheck`
`distcheck` verifies that the source distribution tarball created by the build
system will unpack, build, and run all tests.
-> Example: `./x test distcheck`
+```console
+./x test distcheck
+```
### Tool tests
diff --git a/src/doc/rustc-dev-guide/src/tests/misc.md b/src/doc/rustc-dev-guide/src/tests/misc.md
index c0288b3..39f8817 100644
--- a/src/doc/rustc-dev-guide/src/tests/misc.md
+++ b/src/doc/rustc-dev-guide/src/tests/misc.md
@@ -9,7 +9,7 @@
- `RUSTC_BOOTSTRAP=1` will "cheat" and bypass usual stability checking, allowing
you to use unstable features and cli flags on a stable `rustc`.
-- `RUSTC_BOOTSTRAP=-1` will force a given `rustc` to pretend that is a stable
+- `RUSTC_BOOTSTRAP=-1` will force a given `rustc` to pretend it is a stable
compiler, even if it's actually a nightly `rustc`. This is useful because some
behaviors of the compiler (e.g. diagnostics) can differ depending on whether
the compiler is nightly or not.
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 9c077e5..f309e34 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -1086,7 +1086,7 @@ fn check_fn_target_features(
ecx: &MiriInterpCx<'tcx>,
instance: ty::Instance<'tcx>,
) -> InterpResult<'tcx> {
- let attrs = ecx.tcx.codegen_fn_attrs(instance.def_id());
+ let attrs = ecx.tcx.codegen_instance_attrs(instance.def);
if attrs
.target_features
.iter()
@@ -1790,7 +1790,7 @@ fn get_global_alloc_salt(
ecx.tcx.sess.opts.unstable_opts.cross_crate_inline_threshold,
InliningThreshold::Always
) || !matches!(
- ecx.tcx.codegen_fn_attrs(instance.def_id()).inline,
+ ecx.tcx.codegen_instance_attrs(instance.def).inline,
InlineAttr::Never
);
!is_generic && !can_be_inlined
diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index 7432a82..e55cd80 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -154,6 +154,22 @@
]
[[package]]
+name = "cargo-util-schemas"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dc1a6f7b5651af85774ae5a34b4e8be397d9cf4bc063b7e6dbd99a841837830"
+dependencies = [
+ "semver",
+ "serde",
+ "serde-untagged",
+ "serde-value",
+ "thiserror 2.0.12",
+ "toml",
+ "unicode-xid",
+ "url",
+]
+
+[[package]]
name = "cargo_metadata"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -161,7 +177,22 @@
dependencies = [
"camino",
"cargo-platform",
- "cargo-util-schemas",
+ "cargo-util-schemas 0.2.0",
+ "semver",
+ "serde",
+ "serde_json",
+ "thiserror 2.0.12",
+]
+
+[[package]]
+name = "cargo_metadata"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cfca2aaa699835ba88faf58a06342a314a950d2b9686165e038286c30316868"
+dependencies = [
+ "camino",
+ "cargo-platform",
+ "cargo-util-schemas 0.8.2",
"semver",
"serde",
"serde_json",
@@ -1190,13 +1221,16 @@
name = "lsp-server"
version = "0.7.8"
dependencies = [
+ "anyhow",
"crossbeam-channel",
"ctrlc",
"log",
"lsp-types",
+ "rustc-hash 2.1.1",
"serde",
"serde_derive",
"serde_json",
+ "toolchain",
]
[[package]]
@@ -1471,7 +1505,7 @@
"edition",
"expect-test",
"ra-ap-rustc_lexer",
- "rustc-literal-escaper 0.0.4",
+ "rustc-literal-escaper",
"stdx",
"tracing",
]
@@ -1599,7 +1633,7 @@
name = "proc-macro-test"
version = "0.0.0"
dependencies = [
- "cargo_metadata",
+ "cargo_metadata 0.20.0",
]
[[package]]
@@ -1640,7 +1674,7 @@
dependencies = [
"anyhow",
"base-db",
- "cargo_metadata",
+ "cargo_metadata 0.21.0",
"cfg",
"expect-test",
"intern",
@@ -1722,9 +1756,9 @@
[[package]]
name = "ra-ap-rustc_abi"
-version = "0.116.0"
+version = "0.121.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a967e3a9cd3e38b543f503978e0eccee461e3aea3f7b10e944959bff41dbe612"
+checksum = "3ee51482d1c9d3e538acda8cce723db8eea1a81540544bf362bf4c3d841b2329"
dependencies = [
"bitflags 2.9.1",
"ra-ap-rustc_hashes",
@@ -1734,18 +1768,18 @@
[[package]]
name = "ra-ap-rustc_hashes"
-version = "0.116.0"
+version = "0.121.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ea4c755ecbbffa5743c251344f484ebe571ec7bc5b36d80b2a8ae775d1a7a40"
+checksum = "19c8f1e0c28e24e1b4c55dc08058c6c9829df2204497d4034259f491d348c204"
dependencies = [
"rustc-stable-hash",
]
[[package]]
name = "ra-ap-rustc_index"
-version = "0.116.0"
+version = "0.121.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aca7ad7cf911538c619caa2162339fe98637e9e46f11bb0484ef96735df4d64a"
+checksum = "5f33f429cec6b92fa2c7243883279fb29dd233fdc3e94099aff32aa91aa87f50"
dependencies = [
"ra-ap-rustc_index_macros",
"smallvec",
@@ -1753,9 +1787,9 @@
[[package]]
name = "ra-ap-rustc_index_macros"
-version = "0.116.0"
+version = "0.121.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8767ba551c9355bc3031be072cc4bb0381106e5e7cd275e72b7a8c76051c4070"
+checksum = "b9b55910dbe1fe7ef34bdc1d1bcb41e99b377eb680ea58a1218d95d6b4152257"
dependencies = [
"proc-macro2",
"quote",
@@ -1764,9 +1798,9 @@
[[package]]
name = "ra-ap-rustc_lexer"
-version = "0.116.0"
+version = "0.121.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6101374afb267e6c27e4e2eb0b1352e9f3504c1a8f716f619cd39244e2ed92ab"
+checksum = "22944e31fb91e9b3e75bcbc91e37d958b8c0825a6160927f2856831d2ce83b36"
dependencies = [
"memchr",
"unicode-properties",
@@ -1775,19 +1809,19 @@
[[package]]
name = "ra-ap-rustc_parse_format"
-version = "0.116.0"
+version = "0.121.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ecd88a19f00da4f43e6727d5013444cbc399804b5046dfa2bbcd28ebed3970ce"
+checksum = "81057891bc2063ad9e353f29462fbc47a0f5072560af34428ae9313aaa5e9d97"
dependencies = [
"ra-ap-rustc_lexer",
- "rustc-literal-escaper 0.0.2",
+ "rustc-literal-escaper",
]
[[package]]
name = "ra-ap-rustc_pattern_analysis"
-version = "0.116.0"
+version = "0.121.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb332dd32d7850a799862533b1c021e6062558861a4ad57817bf522499fbb892"
+checksum = "fe21a3542980d56d2435e96c2720773cac1c63fd4db666417e414729da192eb3"
dependencies = [
"ra-ap-rustc_index",
"rustc-hash 2.1.1",
@@ -1855,7 +1889,7 @@
dependencies = [
"anyhow",
"base64",
- "cargo_metadata",
+ "cargo_metadata 0.21.0",
"cfg",
"crossbeam-channel",
"dirs",
@@ -1934,12 +1968,6 @@
[[package]]
name = "rustc-literal-escaper"
-version = "0.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0041b6238913c41fe704213a4a9329e2f685a156d1781998128b4149c230ad04"
-
-[[package]]
-name = "rustc-literal-escaper"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab03008eb631b703dd16978282ae36c73282e7922fe101a4bd072a40ecea7b8b"
@@ -2231,7 +2259,7 @@
"rayon",
"rowan",
"rustc-hash 2.1.1",
- "rustc-literal-escaper 0.0.4",
+ "rustc-literal-escaper",
"rustc_apfloat",
"smol_str",
"stdx",
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index d268ce5..41fa06a 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -4,7 +4,7 @@
resolver = "2"
[workspace.package]
-rust-version = "1.86"
+rust-version = "1.88"
edition = "2024"
license = "MIT OR Apache-2.0"
authors = ["rust-analyzer team"]
@@ -89,11 +89,11 @@
vfs = { path = "./crates/vfs", version = "0.0.0" }
edition = { path = "./crates/edition", version = "0.0.0" }
-ra-ap-rustc_lexer = { version = "0.116", default-features = false }
-ra-ap-rustc_parse_format = { version = "0.116", default-features = false }
-ra-ap-rustc_index = { version = "0.116", default-features = false }
-ra-ap-rustc_abi = { version = "0.116", default-features = false }
-ra-ap-rustc_pattern_analysis = { version = "0.116", default-features = false }
+ra-ap-rustc_lexer = { version = "0.121", default-features = false }
+ra-ap-rustc_parse_format = { version = "0.121", default-features = false }
+ra-ap-rustc_index = { version = "0.121", default-features = false }
+ra-ap-rustc_abi = { version = "0.121", default-features = false }
+ra-ap-rustc_pattern_analysis = { version = "0.121", default-features = false }
# local crates that aren't published to crates.io. These should not have versions.
@@ -106,7 +106,7 @@
anyhow = "1.0.98"
arrayvec = "0.7.6"
bitflags = "2.9.1"
-cargo_metadata = "0.20.0"
+cargo_metadata = "0.21.0"
camino = "1.1.10"
chalk-solve = { version = "0.103.0", default-features = false }
chalk-ir = "0.103.0"
@@ -138,7 +138,11 @@
rowan = "=0.15.15"
# Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work
# on impls without it
-salsa = { version = "0.23.0", default-features = true, features = ["rayon","salsa_unstable", "macros"] }
+salsa = { version = "0.23.0", default-features = true, features = [
+ "rayon",
+ "salsa_unstable",
+ "macros",
+] }
salsa-macros = "0.23.0"
semver = "1.0.26"
serde = { version = "1.0.219" }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs
index 51612f3..d3dfc05 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs
@@ -22,6 +22,7 @@
use smallvec::SmallVec;
use span::{Edition, SyntaxContext};
use syntax::{AstPtr, SyntaxNodePtr, ast};
+use thin_vec::ThinVec;
use triomphe::Arc;
use tt::TextRange;
@@ -93,17 +94,17 @@ pub(crate) fn is_root(self) -> bool {
pub type LifetimePtr = AstPtr<ast::Lifetime>;
pub type LifetimeSource = InFile<LifetimePtr>;
+// We split the store into types-only and expressions, because most stores (e.g. generics)
+// don't store any expressions and this saves memory. Same thing for the source map.
#[derive(Debug, PartialEq, Eq)]
-pub struct ExpressionStore {
- pub exprs: Arena<Expr>,
- pub pats: Arena<Pat>,
- pub bindings: Arena<Binding>,
- pub labels: Arena<Label>,
- pub types: Arena<TypeRef>,
- pub lifetimes: Arena<LifetimeRef>,
+struct ExpressionOnlyStore {
+ exprs: Arena<Expr>,
+ pats: Arena<Pat>,
+ bindings: Arena<Binding>,
+ labels: Arena<Label>,
/// Id of the closure/coroutine that owns the corresponding binding. If a binding is owned by the
/// top level expression, it will not be listed in here.
- pub binding_owners: FxHashMap<BindingId, ExprId>,
+ binding_owners: FxHashMap<BindingId, ExprId>,
/// Block expressions in this store that may contain inner items.
block_scopes: Box<[BlockId]>,
@@ -114,8 +115,118 @@ pub struct ExpressionStore {
ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>,
}
+#[derive(Debug, PartialEq, Eq)]
+pub struct ExpressionStore {
+ expr_only: Option<Box<ExpressionOnlyStore>>,
+ pub types: Arena<TypeRef>,
+ pub lifetimes: Arena<LifetimeRef>,
+}
+
+#[derive(Debug, Eq, Default)]
+struct ExpressionOnlySourceMap {
+ // AST expressions can create patterns in destructuring assignments. Therefore, `ExprSource` can also map
+ // to `PatId`, and `PatId` can also map to `ExprSource` (the other way around is unaffected).
+ expr_map: FxHashMap<ExprSource, ExprOrPatId>,
+ expr_map_back: ArenaMap<ExprId, ExprOrPatSource>,
+
+ pat_map: FxHashMap<PatSource, ExprOrPatId>,
+ pat_map_back: ArenaMap<PatId, ExprOrPatSource>,
+
+ label_map: FxHashMap<LabelSource, LabelId>,
+ label_map_back: ArenaMap<LabelId, LabelSource>,
+
+ binding_definitions:
+ ArenaMap<BindingId, SmallVec<[PatId; 2 * size_of::<usize>() / size_of::<PatId>()]>>,
+
+ /// We don't create explicit nodes for record fields (`S { record_field: 92 }`).
+ /// Instead, we use id of expression (`92`) to identify the field.
+ field_map_back: FxHashMap<ExprId, FieldSource>,
+ pat_field_map_back: FxHashMap<PatId, PatFieldSource>,
+
+ template_map: Option<Box<FormatTemplate>>,
+
+ expansions: FxHashMap<InFile<MacroCallPtr>, MacroCallId>,
+
+ /// Diagnostics accumulated during lowering. These contain `AstPtr`s and so are stored in
+ /// the source map (since they're just as volatile).
+ //
+ // We store diagnostics on the `ExpressionOnlySourceMap` because diagnostics are rare (except
+ // maybe for cfgs, and they are also not common in type places).
+ diagnostics: ThinVec<ExpressionStoreDiagnostics>,
+}
+
+impl PartialEq for ExpressionOnlySourceMap {
+ fn eq(&self, other: &Self) -> bool {
+ // we only need to compare one of the two mappings
+ // as the other is a reverse mapping and thus will compare
+ // the same as normal mapping
+ let Self {
+ expr_map: _,
+ expr_map_back,
+ pat_map: _,
+ pat_map_back,
+ label_map: _,
+ label_map_back,
+ // If this changed, our pattern data must have changed
+ binding_definitions: _,
+ // If this changed, our expression data must have changed
+ field_map_back: _,
+ // If this changed, our pattern data must have changed
+ pat_field_map_back: _,
+ template_map,
+ expansions,
+ diagnostics,
+ } = self;
+ *expr_map_back == other.expr_map_back
+ && *pat_map_back == other.pat_map_back
+ && *label_map_back == other.label_map_back
+ && *template_map == other.template_map
+ && *expansions == other.expansions
+ && *diagnostics == other.diagnostics
+ }
+}
+
#[derive(Debug, Eq, Default)]
pub struct ExpressionStoreSourceMap {
+ expr_only: Option<Box<ExpressionOnlySourceMap>>,
+
+ types_map_back: ArenaMap<TypeRefId, TypeSource>,
+ types_map: FxHashMap<TypeSource, TypeRefId>,
+
+ lifetime_map_back: ArenaMap<LifetimeRefId, LifetimeSource>,
+ #[expect(
+ unused,
+ reason = "this is here for completeness, and maybe we'll need it in the future"
+ )]
+ lifetime_map: FxHashMap<LifetimeSource, LifetimeRefId>,
+}
+
+impl PartialEq for ExpressionStoreSourceMap {
+ fn eq(&self, other: &Self) -> bool {
+ // we only need to compare one of the two mappings
+ // as the other is a reverse mapping and thus will compare
+ // the same as normal mapping
+ let Self { expr_only, types_map_back, types_map: _, lifetime_map_back, lifetime_map: _ } =
+ self;
+ *expr_only == other.expr_only
+ && *types_map_back == other.types_map_back
+ && *lifetime_map_back == other.lifetime_map_back
+ }
+}
+
+/// The body of an item (function, const etc.).
+#[derive(Debug, Eq, PartialEq, Default)]
+pub struct ExpressionStoreBuilder {
+ pub exprs: Arena<Expr>,
+ pub pats: Arena<Pat>,
+ pub bindings: Arena<Binding>,
+ pub labels: Arena<Label>,
+ pub lifetimes: Arena<LifetimeRef>,
+ pub binding_owners: FxHashMap<BindingId, ExprId>,
+ pub types: Arena<TypeRef>,
+ block_scopes: Vec<BlockId>,
+ ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>,
+
// AST expressions can create patterns in destructuring assignments. Therefore, `ExprSource` can also map
// to `PatId`, and `PatId` can also map to `ExprSource` (the other way around is unaffected).
expr_map: FxHashMap<ExprSource, ExprOrPatId>,
@@ -143,62 +254,14 @@ pub struct ExpressionStoreSourceMap {
template_map: Option<Box<FormatTemplate>>,
- pub expansions: FxHashMap<InFile<MacroCallPtr>, MacroCallId>,
+ expansions: FxHashMap<InFile<MacroCallPtr>, MacroCallId>,
/// Diagnostics accumulated during lowering. These contain `AstPtr`s and so are stored in
/// the source map (since they're just as volatile).
- pub diagnostics: Vec<ExpressionStoreDiagnostics>,
-}
-
-impl PartialEq for ExpressionStoreSourceMap {
- fn eq(&self, other: &Self) -> bool {
- // we only need to compare one of the two mappings
- // as the other is a reverse mapping and thus will compare
- // the same as normal mapping
- let Self {
- expr_map: _,
- expr_map_back,
- pat_map: _,
- pat_map_back,
- label_map: _,
- label_map_back,
- types_map_back,
- types_map: _,
- lifetime_map_back,
- lifetime_map: _,
- // If this changed, our pattern data must have changed
- binding_definitions: _,
- // If this changed, our expression data must have changed
- field_map_back: _,
- // If this changed, our pattern data must have changed
- pat_field_map_back: _,
- template_map,
- expansions,
- diagnostics,
- } = self;
- *expr_map_back == other.expr_map_back
- && *pat_map_back == other.pat_map_back
- && *label_map_back == other.label_map_back
- && *types_map_back == other.types_map_back
- && *lifetime_map_back == other.lifetime_map_back
- && *template_map == other.template_map
- && *expansions == other.expansions
- && *diagnostics == other.diagnostics
- }
-}
-
-/// The body of an item (function, const etc.).
-#[derive(Debug, Eq, PartialEq, Default)]
-pub struct ExpressionStoreBuilder {
- pub exprs: Arena<Expr>,
- pub pats: Arena<Pat>,
- pub bindings: Arena<Binding>,
- pub labels: Arena<Label>,
- pub lifetimes: Arena<LifetimeRef>,
- pub binding_owners: FxHashMap<BindingId, ExprId>,
- pub types: Arena<TypeRef>,
- block_scopes: Vec<BlockId>,
- ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>,
+ //
+ // We store diagnostics on the `ExpressionOnlySourceMap` because diagnostics are rare (except
+ // maybe for cfgs, and they are also not common in type places).
+ pub(crate) diagnostics: Vec<ExpressionStoreDiagnostics>,
}
#[derive(Default, Debug, Eq, PartialEq)]
@@ -226,7 +289,7 @@ pub enum ExpressionStoreDiagnostics {
}
impl ExpressionStoreBuilder {
- pub fn finish(self) -> ExpressionStore {
+ pub fn finish(self) -> (ExpressionStore, ExpressionStoreSourceMap) {
let Self {
block_scopes,
mut exprs,
@@ -237,6 +300,23 @@ pub fn finish(self) -> ExpressionStore {
mut ident_hygiene,
mut types,
mut lifetimes,
+
+ mut expr_map,
+ mut expr_map_back,
+ mut pat_map,
+ mut pat_map_back,
+ mut label_map,
+ mut label_map_back,
+ mut types_map_back,
+ mut types_map,
+ mut lifetime_map_back,
+ mut lifetime_map,
+ mut binding_definitions,
+ mut field_map_back,
+ mut pat_field_map_back,
+ mut template_map,
+ mut expansions,
+ diagnostics,
} = self;
exprs.shrink_to_fit();
labels.shrink_to_fit();
@@ -247,24 +327,90 @@ pub fn finish(self) -> ExpressionStore {
types.shrink_to_fit();
lifetimes.shrink_to_fit();
- ExpressionStore {
- exprs,
- pats,
- bindings,
- labels,
- binding_owners,
- types,
- lifetimes,
- block_scopes: block_scopes.into_boxed_slice(),
- ident_hygiene,
+ expr_map.shrink_to_fit();
+ expr_map_back.shrink_to_fit();
+ pat_map.shrink_to_fit();
+ pat_map_back.shrink_to_fit();
+ label_map.shrink_to_fit();
+ label_map_back.shrink_to_fit();
+ types_map_back.shrink_to_fit();
+ types_map.shrink_to_fit();
+ lifetime_map_back.shrink_to_fit();
+ lifetime_map.shrink_to_fit();
+ binding_definitions.shrink_to_fit();
+ field_map_back.shrink_to_fit();
+ pat_field_map_back.shrink_to_fit();
+ if let Some(template_map) = &mut template_map {
+ let FormatTemplate {
+ format_args_to_captures,
+ asm_to_captures,
+ implicit_capture_to_source,
+ } = &mut **template_map;
+ format_args_to_captures.shrink_to_fit();
+ asm_to_captures.shrink_to_fit();
+ implicit_capture_to_source.shrink_to_fit();
}
+ expansions.shrink_to_fit();
+
+ let has_exprs =
+ !exprs.is_empty() || !labels.is_empty() || !pats.is_empty() || !bindings.is_empty();
+
+ let store = {
+ let expr_only = if has_exprs {
+ Some(Box::new(ExpressionOnlyStore {
+ exprs,
+ pats,
+ bindings,
+ labels,
+ binding_owners,
+ block_scopes: block_scopes.into_boxed_slice(),
+ ident_hygiene,
+ }))
+ } else {
+ None
+ };
+ ExpressionStore { expr_only, types, lifetimes }
+ };
+
+ let source_map = {
+ let expr_only = if has_exprs || !expansions.is_empty() || !diagnostics.is_empty() {
+ Some(Box::new(ExpressionOnlySourceMap {
+ expr_map,
+ expr_map_back,
+ pat_map,
+ pat_map_back,
+ label_map,
+ label_map_back,
+ binding_definitions,
+ field_map_back,
+ pat_field_map_back,
+ template_map,
+ expansions,
+ diagnostics: ThinVec::from_iter(diagnostics),
+ }))
+ } else {
+ None
+ };
+ ExpressionStoreSourceMap {
+ expr_only,
+ types_map_back,
+ types_map,
+ lifetime_map_back,
+ lifetime_map,
+ }
+ };
+
+ (store, source_map)
}
}
impl ExpressionStore {
- pub fn empty_singleton() -> Arc<Self> {
- static EMPTY: LazyLock<Arc<ExpressionStore>> =
- LazyLock::new(|| Arc::new(ExpressionStoreBuilder::default().finish()));
+ pub fn empty_singleton() -> (Arc<ExpressionStore>, Arc<ExpressionStoreSourceMap>) {
+ static EMPTY: LazyLock<(Arc<ExpressionStore>, Arc<ExpressionStoreSourceMap>)> =
+ LazyLock::new(|| {
+ let (store, source_map) = ExpressionStoreBuilder::default().finish();
+ (Arc::new(store), Arc::new(source_map))
+ });
EMPTY.clone()
}
@@ -273,7 +419,12 @@ pub fn blocks<'a>(
&'a self,
db: &'a dyn DefDatabase,
) -> impl Iterator<Item = (BlockId, &'a DefMap)> + 'a {
- self.block_scopes.iter().map(move |&block| (block, block_def_map(db, block)))
+ self.expr_only
+ .as_ref()
+ .map(|it| &*it.block_scopes)
+ .unwrap_or_default()
+ .iter()
+ .map(move |&block| (block, block_def_map(db, block)))
}
pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
@@ -320,7 +471,8 @@ pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(PatId)) {
}
pub fn is_binding_upvar(&self, binding: BindingId, relative_to: ExprId) -> bool {
- match self.binding_owners.get(&binding) {
+ let Some(expr_only) = &self.expr_only else { return false };
+ match expr_only.binding_owners.get(&binding) {
Some(it) => {
// We assign expression ids in a way that outer closures will receive
// a lower id
@@ -330,6 +482,11 @@ pub fn is_binding_upvar(&self, binding: BindingId, relative_to: ExprId) -> bool
}
}
+ #[inline]
+ pub fn binding_owner(&self, id: BindingId) -> Option<ExprId> {
+ self.expr_only.as_ref()?.binding_owners.get(&id).copied()
+ }
+
/// Walks the immediate children expressions and calls `f` for each child expression.
///
/// Note that this does not walk const blocks.
@@ -601,16 +758,22 @@ pub fn walk_exprs_in_pat(&self, pat_id: PatId, f: &mut impl FnMut(ExprId)) {
});
}
+ #[inline]
+ #[track_caller]
+ fn assert_expr_only(&self) -> &ExpressionOnlyStore {
+ self.expr_only.as_ref().expect("should have `ExpressionStore::expr_only`")
+ }
+
fn binding_hygiene(&self, binding: BindingId) -> HygieneId {
- self.bindings[binding].hygiene
+ self.assert_expr_only().bindings[binding].hygiene
}
pub fn expr_path_hygiene(&self, expr: ExprId) -> HygieneId {
- self.ident_hygiene.get(&expr.into()).copied().unwrap_or(HygieneId::ROOT)
+ self.assert_expr_only().ident_hygiene.get(&expr.into()).copied().unwrap_or(HygieneId::ROOT)
}
pub fn pat_path_hygiene(&self, pat: PatId) -> HygieneId {
- self.ident_hygiene.get(&pat.into()).copied().unwrap_or(HygieneId::ROOT)
+ self.assert_expr_only().ident_hygiene.get(&pat.into()).copied().unwrap_or(HygieneId::ROOT)
}
pub fn expr_or_pat_path_hygiene(&self, id: ExprOrPatId) -> HygieneId {
@@ -619,43 +782,72 @@ pub fn expr_or_pat_path_hygiene(&self, id: ExprOrPatId) -> HygieneId {
ExprOrPatId::PatId(id) => self.pat_path_hygiene(id),
}
}
+
+ #[inline]
+ pub fn exprs(&self) -> impl Iterator<Item = (ExprId, &Expr)> {
+ match &self.expr_only {
+ Some(it) => it.exprs.iter(),
+ None => const { &Arena::new() }.iter(),
+ }
+ }
+
+ #[inline]
+ pub fn pats(&self) -> impl Iterator<Item = (PatId, &Pat)> {
+ match &self.expr_only {
+ Some(it) => it.pats.iter(),
+ None => const { &Arena::new() }.iter(),
+ }
+ }
+
+ #[inline]
+ pub fn bindings(&self) -> impl Iterator<Item = (BindingId, &Binding)> {
+ match &self.expr_only {
+ Some(it) => it.bindings.iter(),
+ None => const { &Arena::new() }.iter(),
+ }
+ }
}
impl Index<ExprId> for ExpressionStore {
type Output = Expr;
+ #[inline]
fn index(&self, expr: ExprId) -> &Expr {
- &self.exprs[expr]
+ &self.assert_expr_only().exprs[expr]
}
}
impl Index<PatId> for ExpressionStore {
type Output = Pat;
+ #[inline]
fn index(&self, pat: PatId) -> &Pat {
- &self.pats[pat]
+ &self.assert_expr_only().pats[pat]
}
}
impl Index<LabelId> for ExpressionStore {
type Output = Label;
+ #[inline]
fn index(&self, label: LabelId) -> &Label {
- &self.labels[label]
+ &self.assert_expr_only().labels[label]
}
}
impl Index<BindingId> for ExpressionStore {
type Output = Binding;
+ #[inline]
fn index(&self, b: BindingId) -> &Binding {
- &self.bindings[b]
+ &self.assert_expr_only().bindings[b]
}
}
impl Index<TypeRefId> for ExpressionStore {
type Output = TypeRef;
+ #[inline]
fn index(&self, b: TypeRefId) -> &TypeRef {
&self.types[b]
}
@@ -664,6 +856,7 @@ fn index(&self, b: TypeRefId) -> &TypeRef {
impl Index<LifetimeRefId> for ExpressionStore {
type Output = LifetimeRef;
+ #[inline]
fn index(&self, b: LifetimeRefId) -> &LifetimeRef {
&self.lifetimes[b]
}
@@ -684,12 +877,6 @@ fn index(&self, index: PathId) -> &Self::Output {
// FIXME: Change `node_` prefix to something more reasonable.
// Perhaps `expr_syntax` and `expr_id`?
impl ExpressionStoreSourceMap {
- pub fn empty_singleton() -> Arc<Self> {
- static EMPTY: LazyLock<Arc<ExpressionStoreSourceMap>> =
- LazyLock::new(|| Arc::new(ExpressionStoreSourceMap::default()));
- EMPTY.clone()
- }
-
pub fn expr_or_pat_syntax(&self, id: ExprOrPatId) -> Result<ExprOrPatSource, SyntheticSyntax> {
match id {
ExprOrPatId::ExprId(id) => self.expr_syntax(id),
@@ -697,30 +884,46 @@ pub fn expr_or_pat_syntax(&self, id: ExprOrPatId) -> Result<ExprOrPatSource, Syn
}
}
+ #[inline]
+ fn expr_or_synthetic(&self) -> Result<&ExpressionOnlySourceMap, SyntheticSyntax> {
+ self.expr_only.as_deref().ok_or(SyntheticSyntax)
+ }
+
+ #[inline]
+ fn expr_only(&self) -> Option<&ExpressionOnlySourceMap> {
+ self.expr_only.as_deref()
+ }
+
+ #[inline]
+ #[track_caller]
+ fn assert_expr_only(&self) -> &ExpressionOnlySourceMap {
+ self.expr_only.as_ref().expect("should have `ExpressionStoreSourceMap::expr_only`")
+ }
+
pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprOrPatSource, SyntheticSyntax> {
- self.expr_map_back.get(expr).cloned().ok_or(SyntheticSyntax)
+ self.expr_or_synthetic()?.expr_map_back.get(expr).cloned().ok_or(SyntheticSyntax)
}
pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprOrPatId> {
let src = node.map(AstPtr::new);
- self.expr_map.get(&src).cloned()
+ self.expr_only()?.expr_map.get(&src).cloned()
}
pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<MacroCallId> {
let src = node.map(AstPtr::new);
- self.expansions.get(&src).cloned()
+ self.expr_only()?.expansions.get(&src).cloned()
}
pub fn macro_calls(&self) -> impl Iterator<Item = (InFile<MacroCallPtr>, MacroCallId)> + '_ {
- self.expansions.iter().map(|(&a, &b)| (a, b))
+ self.expr_only().into_iter().flat_map(|it| it.expansions.iter().map(|(&a, &b)| (a, b)))
}
pub fn pat_syntax(&self, pat: PatId) -> Result<ExprOrPatSource, SyntheticSyntax> {
- self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax)
+ self.expr_or_synthetic()?.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax)
}
pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<ExprOrPatId> {
- self.pat_map.get(&node.map(AstPtr::new)).cloned()
+ self.expr_only()?.pat_map.get(&node.map(AstPtr::new)).cloned()
}
pub fn type_syntax(&self, id: TypeRefId) -> Result<TypeSource, SyntheticSyntax> {
@@ -732,49 +935,50 @@ pub fn node_type(&self, node: InFile<&ast::Type>) -> Option<TypeRefId> {
}
pub fn label_syntax(&self, label: LabelId) -> LabelSource {
- self.label_map_back[label]
+ self.assert_expr_only().label_map_back[label]
}
pub fn patterns_for_binding(&self, binding: BindingId) -> &[PatId] {
- self.binding_definitions.get(binding).map_or(&[], Deref::deref)
+ self.assert_expr_only().binding_definitions.get(binding).map_or(&[], Deref::deref)
}
pub fn node_label(&self, node: InFile<&ast::Label>) -> Option<LabelId> {
let src = node.map(AstPtr::new);
- self.label_map.get(&src).cloned()
+ self.expr_only()?.label_map.get(&src).cloned()
}
pub fn field_syntax(&self, expr: ExprId) -> FieldSource {
- self.field_map_back[&expr]
+ self.assert_expr_only().field_map_back[&expr]
}
pub fn pat_field_syntax(&self, pat: PatId) -> PatFieldSource {
- self.pat_field_map_back[&pat]
+ self.assert_expr_only().pat_field_map_back[&pat]
}
pub fn macro_expansion_expr(&self, node: InFile<&ast::MacroExpr>) -> Option<ExprOrPatId> {
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::MacroExpr>).map(AstPtr::upcast);
- self.expr_map.get(&src).copied()
+ self.expr_only()?.expr_map.get(&src).copied()
}
pub fn expansions(&self) -> impl Iterator<Item = (&InFile<MacroCallPtr>, &MacroCallId)> {
- self.expansions.iter()
+ self.expr_only().into_iter().flat_map(|it| it.expansions.iter())
}
pub fn expansion(&self, node: InFile<&ast::MacroCall>) -> Option<MacroCallId> {
- self.expansions.get(&node.map(AstPtr::new)).copied()
+ self.expr_only()?.expansions.get(&node.map(AstPtr::new)).copied()
}
pub fn implicit_format_args(
&self,
node: InFile<&ast::FormatArgsExpr>,
) -> Option<(HygieneId, &[(syntax::TextRange, Name)])> {
+ let expr_only = self.expr_only()?;
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
- let (hygiene, names) = self
+ let (hygiene, names) = expr_only
.template_map
.as_ref()?
.format_args_to_captures
- .get(&self.expr_map.get(&src)?.as_expr()?)?;
+ .get(&expr_only.expr_map.get(&src)?.as_expr()?)?;
Some((*hygiene, &**names))
}
@@ -782,67 +986,28 @@ pub fn format_args_implicit_capture(
&self,
capture_expr: ExprId,
) -> Option<InFile<(ExprPtr, TextRange)>> {
- self.template_map.as_ref()?.implicit_capture_to_source.get(&capture_expr).copied()
+ self.expr_only()?
+ .template_map
+ .as_ref()?
+ .implicit_capture_to_source
+ .get(&capture_expr)
+ .copied()
}
pub fn asm_template_args(
&self,
node: InFile<&ast::AsmExpr>,
) -> Option<(ExprId, &[Vec<(syntax::TextRange, usize)>])> {
+ let expr_only = self.expr_only()?;
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
- let expr = self.expr_map.get(&src)?.as_expr()?;
- Some(expr)
- .zip(self.template_map.as_ref()?.asm_to_captures.get(&expr).map(std::ops::Deref::deref))
+ let expr = expr_only.expr_map.get(&src)?.as_expr()?;
+ Some(expr).zip(
+ expr_only.template_map.as_ref()?.asm_to_captures.get(&expr).map(std::ops::Deref::deref),
+ )
}
/// Get a reference to the source map's diagnostics.
pub fn diagnostics(&self) -> &[ExpressionStoreDiagnostics] {
- &self.diagnostics
- }
-
- fn shrink_to_fit(&mut self) {
- let Self {
- expr_map,
- expr_map_back,
- pat_map,
- pat_map_back,
- label_map,
- label_map_back,
- field_map_back,
- pat_field_map_back,
- expansions,
- template_map,
- diagnostics,
- binding_definitions,
- types_map,
- types_map_back,
- lifetime_map_back,
- lifetime_map,
- } = self;
- if let Some(template_map) = template_map {
- let FormatTemplate {
- format_args_to_captures,
- asm_to_captures,
- implicit_capture_to_source,
- } = &mut **template_map;
- format_args_to_captures.shrink_to_fit();
- asm_to_captures.shrink_to_fit();
- implicit_capture_to_source.shrink_to_fit();
- }
- expr_map.shrink_to_fit();
- expr_map_back.shrink_to_fit();
- pat_map.shrink_to_fit();
- pat_map_back.shrink_to_fit();
- label_map.shrink_to_fit();
- label_map_back.shrink_to_fit();
- field_map_back.shrink_to_fit();
- pat_field_map_back.shrink_to_fit();
- expansions.shrink_to_fit();
- diagnostics.shrink_to_fit();
- binding_definitions.shrink_to_fit();
- types_map.shrink_to_fit();
- types_map_back.shrink_to_fit();
- lifetime_map.shrink_to_fit();
- lifetime_map_back.shrink_to_fit();
+ self.expr_only().map(|it| &*it.diagnostics).unwrap_or_default()
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs
index fb6d931..c955393 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs
@@ -36,6 +36,7 @@ pub struct Body {
impl ops::Deref for Body {
type Target = ExpressionStore;
+ #[inline]
fn deref(&self) -> &Self::Target {
&self.store
}
@@ -61,6 +62,7 @@ pub struct BodySourceMap {
impl ops::Deref for BodySourceMap {
type Target = ExpressionStoreSourceMap;
+ #[inline]
fn deref(&self) -> &Self::Target {
&self.store
}
@@ -102,9 +104,7 @@ pub(crate) fn body_with_source_map_query(
}
};
let module = def.module(db);
- let (body, mut source_map) =
- lower_body(db, def, file_id, module, params, body, is_async_fn);
- source_map.store.shrink_to_fit();
+ let (body, source_map) = lower_body(db, def, file_id, module, params, body, is_async_fn);
(Arc::new(body), Arc::new(source_map))
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
index c0e51b3..4e87774 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
@@ -121,14 +121,10 @@ pub(super) fn lower_body(
params = (0..count).map(|_| collector.missing_pat()).collect();
};
let body_expr = collector.missing_expr();
+ let (store, source_map) = collector.store.finish();
return (
- Body {
- store: collector.store.finish(),
- params: params.into_boxed_slice(),
- self_param,
- body_expr,
- },
- BodySourceMap { self_param: source_map_self_param, store: collector.source_map },
+ Body { store, params: params.into_boxed_slice(), self_param, body_expr },
+ BodySourceMap { self_param: source_map_self_param, store: source_map },
);
}
@@ -171,14 +167,10 @@ pub(super) fn lower_body(
},
);
+ let (store, source_map) = collector.store.finish();
(
- Body {
- store: collector.store.finish(),
- params: params.into_boxed_slice(),
- self_param,
- body_expr,
- },
- BodySourceMap { self_param: source_map_self_param, store: collector.source_map },
+ Body { store, params: params.into_boxed_slice(), self_param, body_expr },
+ BodySourceMap { self_param: source_map_self_param, store: source_map },
)
}
@@ -190,7 +182,8 @@ pub(crate) fn lower_type_ref(
let mut expr_collector = ExprCollector::new(db, module, type_ref.file_id);
let type_ref =
expr_collector.lower_type_ref_opt(type_ref.value, &mut ExprCollector::impl_trait_allocator);
- (expr_collector.store.finish(), expr_collector.source_map, type_ref)
+ let (store, source_map) = expr_collector.store.finish();
+ (store, source_map, type_ref)
}
pub(crate) fn lower_generic_params(
@@ -205,7 +198,8 @@ pub(crate) fn lower_generic_params(
let mut collector = generics::GenericParamsCollector::new(def);
collector.lower(&mut expr_collector, param_list, where_clause);
let params = collector.finish();
- (Arc::new(expr_collector.store.finish()), params, expr_collector.source_map)
+ let (store, source_map) = expr_collector.store.finish();
+ (Arc::new(store), params, source_map)
}
pub(crate) fn lower_impl(
@@ -232,7 +226,8 @@ pub(crate) fn lower_impl(
impl_syntax.value.where_clause(),
);
let params = collector.finish();
- (expr_collector.store.finish(), expr_collector.source_map, self_ty, trait_, params)
+ let (store, source_map) = expr_collector.store.finish();
+ (store, source_map, self_ty, trait_, params)
}
pub(crate) fn lower_trait(
@@ -253,7 +248,8 @@ pub(crate) fn lower_trait(
trait_syntax.value.where_clause(),
);
let params = collector.finish();
- (expr_collector.store.finish(), expr_collector.source_map, params)
+ let (store, source_map) = expr_collector.store.finish();
+ (store, source_map, params)
}
pub(crate) fn lower_trait_alias(
@@ -274,7 +270,8 @@ pub(crate) fn lower_trait_alias(
trait_syntax.value.where_clause(),
);
let params = collector.finish();
- (expr_collector.store.finish(), expr_collector.source_map, params)
+ let (store, source_map) = expr_collector.store.finish();
+ (store, source_map, params)
}
pub(crate) fn lower_type_alias(
@@ -313,7 +310,8 @@ pub(crate) fn lower_type_alias(
.value
.ty()
.map(|ty| expr_collector.lower_type_ref(ty, &mut ExprCollector::impl_trait_allocator));
- (expr_collector.store.finish(), expr_collector.source_map, params, bounds, type_ref)
+ let (store, source_map) = expr_collector.store.finish();
+ (store, source_map, params, bounds, type_ref)
}
pub(crate) fn lower_function(
@@ -421,9 +419,10 @@ pub(crate) fn lower_function(
} else {
return_type
};
+ let (store, source_map) = expr_collector.store.finish();
(
- expr_collector.store.finish(),
- expr_collector.source_map,
+ store,
+ source_map,
generics,
params.into_boxed_slice(),
return_type,
@@ -440,7 +439,6 @@ pub struct ExprCollector<'db> {
local_def_map: &'db LocalDefMap,
module: ModuleId,
pub store: ExpressionStoreBuilder,
- pub(crate) source_map: ExpressionStoreSourceMap,
// state stuff
// Prevent nested impl traits like `impl Foo<impl Bar>`.
@@ -551,7 +549,6 @@ pub fn new(
module,
def_map,
local_def_map,
- source_map: ExpressionStoreSourceMap::default(),
store: ExpressionStoreBuilder::default(),
expander,
current_try_block_label: None,
@@ -698,7 +695,7 @@ fn lower_abi(abi: ast::Abi) -> Symbol {
let id = self.collect_macro_call(mcall, macro_ptr, true, |this, expansion| {
this.lower_type_ref_opt(expansion, impl_trait_lower_fn)
});
- self.source_map.types_map.insert(src, id);
+ self.store.types_map.insert(src, id);
return id;
}
None => TypeRef::Error,
@@ -732,8 +729,8 @@ pub(crate) fn lower_type_ref_opt_disallow_impl_trait(
fn alloc_type_ref(&mut self, type_ref: TypeRef, node: TypePtr) -> TypeRefId {
let id = self.store.types.alloc(type_ref);
let ptr = self.expander.in_file(node);
- self.source_map.types_map_back.insert(id, ptr);
- self.source_map.types_map.insert(ptr, id);
+ self.store.types_map_back.insert(id, ptr);
+ self.store.types_map.insert(ptr, id);
id
}
@@ -744,8 +741,8 @@ fn alloc_lifetime_ref(
) -> LifetimeRefId {
let id = self.store.lifetimes.alloc(lifetime_ref);
let ptr = self.expander.in_file(node);
- self.source_map.lifetime_map_back.insert(id, ptr);
- self.source_map.lifetime_map.insert(ptr, id);
+ self.store.lifetime_map_back.insert(id, ptr);
+ self.store.lifetime_map.insert(ptr, id);
id
}
@@ -1190,14 +1187,14 @@ fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> {
}
ast::Expr::ContinueExpr(e) => {
let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
- self.source_map.diagnostics.push(e);
+ self.store.diagnostics.push(e);
None
});
self.alloc_expr(Expr::Continue { label }, syntax_ptr)
}
ast::Expr::BreakExpr(e) => {
let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
- self.source_map.diagnostics.push(e);
+ self.store.diagnostics.push(e);
None
});
let expr = e.expr().map(|e| self.collect_expr(e));
@@ -1207,7 +1204,7 @@ fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> {
let inner = self.collect_expr_opt(e.expr());
// make the paren expr point to the inner expression as well for IDE resolution
let src = self.expander.in_file(syntax_ptr);
- self.source_map.expr_map.insert(src, inner.into());
+ self.store.expr_map.insert(src, inner.into());
inner
}
ast::Expr::ReturnExpr(e) => {
@@ -1248,7 +1245,7 @@ fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> {
None => self.missing_expr(),
};
let src = self.expander.in_file(AstPtr::new(&field));
- self.source_map.field_map_back.insert(expr, src);
+ self.store.field_map_back.insert(expr, src);
Some(RecordLitField { name, expr })
})
.collect();
@@ -1271,12 +1268,10 @@ fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> {
ast::Expr::AwaitExpr(e) => {
let expr = self.collect_expr_opt(e.expr());
if let Awaitable::No(location) = self.is_lowering_awaitable_block() {
- self.source_map.diagnostics.push(
- ExpressionStoreDiagnostics::AwaitOutsideOfAsync {
- node: self.expander.in_file(AstPtr::new(&e)),
- location: location.to_string(),
- },
- );
+ self.store.diagnostics.push(ExpressionStoreDiagnostics::AwaitOutsideOfAsync {
+ node: self.expander.in_file(AstPtr::new(&e)),
+ location: location.to_string(),
+ });
}
self.alloc_expr(Expr::Await { expr }, syntax_ptr)
}
@@ -1442,7 +1437,7 @@ fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> {
// Make the macro-call point to its expanded expression so we can query
// semantics on syntax pointers to the macro
let src = self.expander.in_file(syntax_ptr);
- self.source_map.expr_map.insert(src, id.into());
+ self.store.expr_map.insert(src, id.into());
id
}
None => self.alloc_expr(Expr::Missing, syntax_ptr),
@@ -1486,7 +1481,7 @@ fn collect_expr_as_pat(&mut self, expr: ast::Expr) -> PatId {
let expr = self.collect_expr(expr);
// Do not use `alloc_pat_from_expr()` here, it will override the entry in `expr_map`.
let id = self.store.pats.alloc(Pat::Expr(expr));
- self.source_map.pat_map_back.insert(id, src);
+ self.store.pat_map_back.insert(id, src);
id
})
}
@@ -1555,7 +1550,7 @@ fn maybe_collect_expr_as_pat(&mut self, expr: &ast::Expr) -> Option<PatId> {
let id = self.collect_macro_call(e, macro_ptr, true, |this, expansion| {
this.collect_expr_as_pat_opt(expansion)
});
- self.source_map.expr_map.insert(src, id.into());
+ self.store.expr_map.insert(src, id.into());
id
}
ast::Expr::RecordExpr(e) => {
@@ -1576,7 +1571,7 @@ fn maybe_collect_expr_as_pat(&mut self, expr: &ast::Expr) -> Option<PatId> {
let pat = self.collect_expr_as_pat(field_expr);
let name = f.field_name()?.as_name();
let src = self.expander.in_file(AstPtr::new(&f).wrap_left());
- self.source_map.pat_field_map_back.insert(pat, src);
+ self.store.pat_field_map_back.insert(pat, src);
Some(RecordFieldPat { name, pat })
})
.collect();
@@ -1622,7 +1617,7 @@ fn collect_possibly_rest(
);
if let Either::Left(pat) = pat {
let src = this.expander.in_file(AstPtr::new(&expr).wrap_left());
- this.source_map.pat_map_back.insert(pat, src);
+ this.store.pat_map_back.insert(pat, src);
}
pat
}
@@ -1968,7 +1963,7 @@ fn collect_macro_call<T, U>(
self.module.krate(),
resolver,
&mut |ptr, call| {
- _ = self.source_map.expansions.insert(ptr.map(|(it, _)| it), call);
+ _ = self.store.expansions.insert(ptr.map(|(it, _)| it), call);
},
)
}
@@ -1978,19 +1973,17 @@ fn collect_macro_call<T, U>(
Ok(res) => res,
Err(UnresolvedMacro { path }) => {
if record_diagnostics {
- self.source_map.diagnostics.push(
- ExpressionStoreDiagnostics::UnresolvedMacroCall {
- node: self.expander.in_file(syntax_ptr),
- path,
- },
- );
+ self.store.diagnostics.push(ExpressionStoreDiagnostics::UnresolvedMacroCall {
+ node: self.expander.in_file(syntax_ptr),
+ path,
+ });
}
return collector(self, None);
}
};
if record_diagnostics {
if let Some(err) = res.err {
- self.source_map
+ self.store
.diagnostics
.push(ExpressionStoreDiagnostics::MacroError { node: macro_call_ptr, err });
}
@@ -2001,7 +1994,7 @@ fn collect_macro_call<T, U>(
// Keep collecting even with expansion errors so we can provide completions and
// other services in incomplete macro expressions.
if let Some(macro_file) = self.expander.current_file_id().macro_file() {
- self.source_map.expansions.insert(macro_call_ptr, macro_file);
+ self.store.expansions.insert(macro_call_ptr, macro_file);
}
if record_diagnostics {
@@ -2050,7 +2043,7 @@ fn collect_macro_as_stmt(
// Make the macro-call point to its expanded expression so we can query
// semantics on syntax pointers to the macro
let src = self.expander.in_file(syntax_ptr);
- self.source_map.expr_map.insert(src, tail.into());
+ self.store.expr_map.insert(src, tail.into());
})
}
@@ -2361,7 +2354,7 @@ fn collect_pat(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatI
let pat = self.collect_pat(ast_pat, binding_list);
let name = f.field_name()?.as_name();
let src = self.expander.in_file(AstPtr::new(&f).wrap_right());
- self.source_map.pat_field_map_back.insert(pat, src);
+ self.store.pat_field_map_back.insert(pat, src);
Some(RecordFieldPat { name, pat })
})
.collect();
@@ -2424,7 +2417,7 @@ fn collect_pat(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatI
self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
this.collect_pat_opt(expanded_pat, binding_list)
});
- self.source_map.pat_map.insert(src, pat.into());
+ self.store.pat_map.insert(src, pat.into());
return pat;
}
None => Pat::Missing,
@@ -2515,7 +2508,7 @@ fn collect_pat_possibly_rest(
}
});
if let Some(pat) = pat.left() {
- self.source_map.pat_map.insert(src, pat.into());
+ self.store.pat_map.insert(src, pat.into());
}
pat
}
@@ -2537,7 +2530,7 @@ fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> bool {
match enabled {
Ok(()) => true,
Err(cfg) => {
- self.source_map.diagnostics.push(ExpressionStoreDiagnostics::InactiveCode {
+ self.store.diagnostics.push(ExpressionStoreDiagnostics::InactiveCode {
node: self.expander.in_file(SyntaxNodePtr::new(owner.syntax())),
cfg,
opts: self.cfg_options.clone(),
@@ -2548,7 +2541,7 @@ fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> bool {
}
fn add_definition_to_binding(&mut self, binding_id: BindingId, pat_id: PatId) {
- self.source_map.binding_definitions.entry(binding_id).or_default().push(pat_id);
+ self.store.binding_definitions.entry(binding_id).or_default().push(pat_id);
}
// region: labels
@@ -2724,7 +2717,7 @@ fn collect_format_args(
|name, range| {
let expr_id = self.alloc_expr_desugared(Expr::Path(Path::from(name)));
if let Some(range) = range {
- self.source_map
+ self.store
.template_map
.get_or_insert_with(Default::default)
.implicit_capture_to_source
@@ -2836,7 +2829,7 @@ fn collect_format_args(
)
};
- self.source_map
+ self.store
.template_map
.get_or_insert_with(Default::default)
.format_args_to_captures
@@ -3386,8 +3379,8 @@ impl ExprCollector<'_> {
fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
let src = self.expander.in_file(ptr);
let id = self.store.exprs.alloc(expr);
- self.source_map.expr_map_back.insert(id, src.map(AstPtr::wrap_left));
- self.source_map.expr_map.insert(src, id.into());
+ self.store.expr_map_back.insert(id, src.map(AstPtr::wrap_left));
+ self.store.expr_map.insert(src, id.into());
id
}
// FIXME: desugared exprs don't have ptr, that's wrong and should be fixed.
@@ -3398,9 +3391,9 @@ fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
fn alloc_expr_desugared_with_ptr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
let src = self.expander.in_file(ptr);
let id = self.store.exprs.alloc(expr);
- self.source_map.expr_map_back.insert(id, src.map(AstPtr::wrap_left));
+ self.store.expr_map_back.insert(id, src.map(AstPtr::wrap_left));
// We intentionally don't fill this as it could overwrite a non-desugared entry
- // self.source_map.expr_map.insert(src, id);
+ // self.store.expr_map.insert(src, id);
id
}
fn missing_expr(&mut self) -> ExprId {
@@ -3423,24 +3416,24 @@ fn alloc_binding(
fn alloc_pat_from_expr(&mut self, pat: Pat, ptr: ExprPtr) -> PatId {
let src = self.expander.in_file(ptr);
let id = self.store.pats.alloc(pat);
- self.source_map.expr_map.insert(src, id.into());
- self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_left));
+ self.store.expr_map.insert(src, id.into());
+ self.store.pat_map_back.insert(id, src.map(AstPtr::wrap_left));
id
}
fn alloc_expr_from_pat(&mut self, expr: Expr, ptr: PatPtr) -> ExprId {
let src = self.expander.in_file(ptr);
let id = self.store.exprs.alloc(expr);
- self.source_map.pat_map.insert(src, id.into());
- self.source_map.expr_map_back.insert(id, src.map(AstPtr::wrap_right));
+ self.store.pat_map.insert(src, id.into());
+ self.store.expr_map_back.insert(id, src.map(AstPtr::wrap_right));
id
}
fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
let src = self.expander.in_file(ptr);
let id = self.store.pats.alloc(pat);
- self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_right));
- self.source_map.pat_map.insert(src, id.into());
+ self.store.pat_map_back.insert(id, src.map(AstPtr::wrap_right));
+ self.store.pat_map.insert(src, id.into());
id
}
// FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
@@ -3454,8 +3447,8 @@ fn missing_pat(&mut self) -> PatId {
fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
let src = self.expander.in_file(ptr);
let id = self.store.labels.alloc(label);
- self.source_map.label_map_back.insert(id, src);
- self.source_map.label_map.insert(src, id);
+ self.store.label_map_back.insert(id, src);
+ self.store.label_map.insert(src, id);
id
}
// FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow.
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs
index d36e520..3bc4afb 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs
@@ -10,7 +10,7 @@
use crate::{
expr_store::lower::{ExprCollector, FxIndexSet},
- hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmRegOrRegClass},
+ hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmKind, InlineAsmRegOrRegClass},
};
impl ExprCollector<'_> {
@@ -269,11 +269,20 @@ pub(super) fn lower_inline_asm(
}
})
};
+
+ let kind = if asm.global_asm_token().is_some() {
+ InlineAsmKind::GlobalAsm
+ } else if asm.naked_asm_token().is_some() {
+ InlineAsmKind::NakedAsm
+ } else {
+ InlineAsmKind::Asm
+ };
+
let idx = self.alloc_expr(
- Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options }),
+ Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options, kind }),
syntax_ptr,
);
- self.source_map
+ self.store
.template_map
.get_or_insert_with(Default::default)
.asm_to_captures
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs
index 8fd81c7..f507841 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs
@@ -23,7 +23,7 @@ fn lower_path(path: ast::Path) -> (TestDB, ExpressionStore, Option<Path>) {
let mut ctx =
ExprCollector::new(&db, crate_def_map(&db, krate).root_module_id(), file_id.into());
let lowered_path = ctx.lower_path(path, &mut ExprCollector::impl_trait_allocator);
- let store = ctx.store.finish();
+ let (store, _) = ctx.store.finish();
(db, store, lowered_path)
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs
index 87bcd33..f1b0113 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs
@@ -902,7 +902,7 @@ fn print_pat(&mut self, pat: PatId) {
let mut same_name = false;
if let Pat::Bind { id, subpat: None } = &self.store[arg.pat] {
if let Binding { name, mode: BindingAnnotation::Unannotated, .. } =
- &self.store.bindings[*id]
+ &self.store.assert_expr_only().bindings[*id]
{
if name.as_str() == field_name {
same_name = true;
@@ -1063,7 +1063,7 @@ fn print_literal(&mut self, literal: &Literal) {
}
fn print_binding(&mut self, id: BindingId) {
- let Binding { name, mode, .. } = &self.store.bindings[id];
+ let Binding { name, mode, .. } = &self.store.assert_expr_only().bindings[id];
let mode = match mode {
BindingAnnotation::Unannotated => "",
BindingAnnotation::Mutable => "mut ",
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs
index 2dd0b9b..1952dae 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs
@@ -106,7 +106,9 @@ fn new_body(body: &Body) -> ExprScopes {
let mut scopes = ExprScopes {
scopes: Arena::default(),
scope_entries: Arena::default(),
- scope_by_expr: ArenaMap::with_capacity(body.exprs.len()),
+ scope_by_expr: ArenaMap::with_capacity(
+ body.expr_only.as_ref().map_or(0, |it| it.exprs.len()),
+ ),
};
let mut root = scopes.root_scope();
if let Some(self_param) = body.self_param {
@@ -179,7 +181,7 @@ fn add_bindings(
binding: BindingId,
hygiene: HygieneId,
) {
- let Binding { name, .. } = &store.bindings[binding];
+ let Binding { name, .. } = &store[binding];
let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding, hygiene });
self.scopes[scope].entries =
IdxRange::new_inclusive(self.scopes[scope].entries.start()..=entry);
@@ -251,7 +253,7 @@ fn compute_expr_scopes(
scope: &mut ScopeId,
) {
let make_label =
- |label: &Option<LabelId>| label.map(|label| (label, store.labels[label].name.clone()));
+ |label: &Option<LabelId>| label.map(|label| (label, store[label].name.clone()));
let compute_expr_scopes = |scopes: &mut ExprScopes, expr: ExprId, scope: &mut ScopeId| {
compute_expr_scopes(expr, store, scopes, scope)
@@ -534,9 +536,8 @@ fn do_check_local_name(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected
};
let resolved = scopes.resolve_name_in_scope(expr_scope, &name_ref.as_name()).unwrap();
- let pat_src = source_map
- .pat_syntax(*source_map.binding_definitions[resolved.binding()].first().unwrap())
- .unwrap();
+ let pat_src =
+ source_map.pat_syntax(source_map.patterns_for_binding(resolved.binding())[0]).unwrap();
let local_name = pat_src.value.syntax_node_ptr().to_node(file.syntax());
assert_eq!(local_name.text_range(), expected_name.syntax().text_range());
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs
index 927e280..c31428b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs
@@ -508,9 +508,9 @@ fn f() {
}
"#,
);
- assert_eq!(body.bindings.len(), 1, "should have a binding for `B`");
+ assert_eq!(body.assert_expr_only().bindings.len(), 1, "should have a binding for `B`");
assert_eq!(
- body.bindings[BindingId::from_raw(RawIdx::from_u32(0))].name.as_str(),
+ body[BindingId::from_raw(RawIdx::from_u32(0))].name.as_str(),
"B",
"should have a binding for `B`",
);
@@ -566,6 +566,7 @@ const fn f(x: i32) -> i32 {
);
let mtch_arms = body
+ .assert_expr_only()
.exprs
.iter()
.find_map(|(_, expr)| {
@@ -578,10 +579,10 @@ const fn f(x: i32) -> i32 {
.unwrap();
let MatchArm { pat, .. } = mtch_arms[1];
- match body.pats[pat] {
+ match body[pat] {
Pat::Range { start, end } => {
- let hir_start = &body.exprs[start.unwrap()];
- let hir_end = &body.exprs[end.unwrap()];
+ let hir_start = &body[start.unwrap()];
+ let hir_end = &body[end.unwrap()];
assert!(matches!(hir_start, Expr::Path { .. }));
assert!(matches!(hir_end, Expr::Path { .. }));
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
index 0fc7857..e70cd2c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
@@ -332,6 +332,17 @@ pub struct OffsetOf {
pub struct InlineAsm {
pub operands: Box<[(Option<Name>, AsmOperand)]>,
pub options: AsmOptions,
+ pub kind: InlineAsmKind,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum InlineAsmKind {
+ /// `asm!()`.
+ Asm,
+ /// `global_asm!()`.
+ GlobalAsm,
+ /// `naked_asm!()`.
+ NakedAsm,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
index f327366..5ab61c8 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
@@ -143,6 +143,8 @@ fn lower_mod_item(&mut self, item: &ast::Item) -> Option<ModItemId> {
ast::Item::MacroRules(ast) => self.lower_macro_rules(ast)?.into(),
ast::Item::MacroDef(ast) => self.lower_macro_def(ast)?.into(),
ast::Item::ExternBlock(ast) => self.lower_extern_block(ast).into(),
+ // FIXME: Handle `global_asm!()`.
+ ast::Item::AsmExpr(_) => return None,
};
let attrs = RawAttrs::new(self.db, item, self.span_map());
self.add_attrs(mod_item.ast_id(), attrs);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
index 5923b3e..91b42be 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
@@ -35,10 +35,10 @@ fn imports() {
#![no_std]
#![doc = " another file comment"]
- // AstId: ExternCrate[5A82, 0]
+ // AstId: ExternCrate[070B, 0]
pub(self) extern crate self as renamed;
- // AstId: ExternCrate[7E1C, 0]
+ // AstId: ExternCrate[1EA5, 0]
pub(in super) extern crate bli;
// AstId: Use[0000, 0]
@@ -78,15 +78,15 @@ fn extern_blocks() {
// AstId: ExternBlock[0000, 0]
extern {
#[on_extern_type]
- // AstId: TypeAlias[9FDF, 0]
+ // AstId: TypeAlias[A09C, 0]
pub(self) type ExType;
#[on_extern_static]
- // AstId: Static[43C1, 0]
+ // AstId: Static[D85E, 0]
pub(self) static EX_STATIC = _;
#[on_extern_fn]
- // AstId: Fn[452D, 0]
+ // AstId: Fn[B240, 0]
pub(self) fn ex_fn;
}
"#]],
@@ -124,20 +124,20 @@ enum E {
}
"#,
expect![[r#"
- // AstId: Struct[DFF3, 0]
+ // AstId: Struct[ED35, 0]
pub(self) struct Unit;
#[derive(Debug)]
- // AstId: Struct[C7A1, 0]
+ // AstId: Struct[A47C, 0]
pub(self) struct Struct { ... }
- // AstId: Struct[DAC2, 0]
+ // AstId: Struct[C8C9, 0]
pub(self) struct Tuple(...);
- // AstId: Union[2DBB, 0]
+ // AstId: Union[2797, 0]
pub(self) union Ize { ... }
- // AstId: Enum[7FF8, 0]
+ // AstId: Enum[7D23, 0]
pub(self) enum E { ... }
"#]],
);
@@ -162,18 +162,18 @@ trait Tr: SuperTrait + 'lifetime {
}
"#,
expect![[r#"
- // AstId: Static[B393, 0]
+ // AstId: Static[F7C1, 0]
pub static ST = _;
- // AstId: Const[B309, 0]
+ // AstId: Const[84BB, 0]
pub(self) const _ = _;
#[attr]
#[inner_attr_in_fn]
- // AstId: Fn[75E3, 0]
+ // AstId: Fn[BE8F, 0]
pub(self) fn f;
- // AstId: Trait[2998, 0]
+ // AstId: Trait[9320, 0]
pub(self) trait Tr { ... }
"#]],
);
@@ -197,16 +197,16 @@ fn fn_in_module() {}
expect![[r##"
#[doc = " outer"]
#[doc = " inner"]
- // AstId: Module[CF93, 0]
+ // AstId: Module[03AE, 0]
pub(self) mod inline {
// AstId: Use[0000, 0]
pub(self) use super::*;
- // AstId: Fn[1B26, 0]
+ // AstId: Fn[2A78, 0]
pub(self) fn fn_in_module;
}
- // AstId: Module[8994, 0]
+ // AstId: Module[C08B, 0]
pub(self) mod outline;
"##]],
);
@@ -225,13 +225,13 @@ macro_rules! m {
m!();
"#,
expect![[r#"
- // AstId: MacroRules[88CE, 0]
+ // AstId: MacroRules[7E68, 0]
macro_rules! m { ... }
- // AstId: MacroDef[DC34, 0]
+ // AstId: MacroDef[1C1E, 0]
pub macro m2 { ... }
- // AstId: MacroCall[612F, 0], SyntaxContextId: ROOT2024, ExpandTo: Items
+ // AstId: MacroCall[7E68, 0], SyntaxContextId: ROOT2024, ExpandTo: Items
m!(...);
"#]],
);
@@ -244,7 +244,7 @@ fn pub_self() {
pub(self) struct S;
"#,
expect![[r#"
- // AstId: Struct[42E2, 0]
+ // AstId: Struct[5024, 0]
pub(self) struct S;
"#]],
)
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
index 293868d..1c3af47 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
@@ -28,6 +28,19 @@ fn test_asm_expand() {
r#"
#[rustc_builtin_macro]
macro_rules! asm {() => {}}
+#[rustc_builtin_macro]
+macro_rules! global_asm {() => {}}
+#[rustc_builtin_macro]
+macro_rules! naked_asm {() => {}}
+
+global_asm! {
+ ""
+}
+
+#[unsafe(naked)]
+extern "C" fn foo() {
+ naked_asm!("");
+}
fn main() {
let i: u64 = 3;
@@ -45,6 +58,17 @@ fn main() {
expect![[r##"
#[rustc_builtin_macro]
macro_rules! asm {() => {}}
+#[rustc_builtin_macro]
+macro_rules! global_asm {() => {}}
+#[rustc_builtin_macro]
+macro_rules! naked_asm {() => {}}
+
+builtin #global_asm ("")
+
+#[unsafe(naked)]
+extern "C" fn foo() {
+ builtin #naked_asm ("");
+}
fn main() {
let i: u64 = 3;
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
index c6d901e..c489c1f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -35,9 +35,9 @@ struct $ident {
};
}
-struct#0:MacroRules[8C8E, 0]@58..64#14336# MyTraitMap2#0:MacroCall[D499, 0]@31..42#ROOT2024# {#0:MacroRules[8C8E, 0]@72..73#14336#
- map#0:MacroRules[8C8E, 0]@86..89#14336#:#0:MacroRules[8C8E, 0]@89..90#14336# #0:MacroRules[8C8E, 0]@89..90#14336#::#0:MacroRules[8C8E, 0]@91..93#14336#std#0:MacroRules[8C8E, 0]@93..96#14336#::#0:MacroRules[8C8E, 0]@96..98#14336#collections#0:MacroRules[8C8E, 0]@98..109#14336#::#0:MacroRules[8C8E, 0]@109..111#14336#HashSet#0:MacroRules[8C8E, 0]@111..118#14336#<#0:MacroRules[8C8E, 0]@118..119#14336#(#0:MacroRules[8C8E, 0]@119..120#14336#)#0:MacroRules[8C8E, 0]@120..121#14336#>#0:MacroRules[8C8E, 0]@121..122#14336#,#0:MacroRules[8C8E, 0]@122..123#14336#
-}#0:MacroRules[8C8E, 0]@132..133#14336#
+struct#0:MacroRules[BE8F, 0]@58..64#14336# MyTraitMap2#0:MacroCall[BE8F, 0]@31..42#ROOT2024# {#0:MacroRules[BE8F, 0]@72..73#14336#
+ map#0:MacroRules[BE8F, 0]@86..89#14336#:#0:MacroRules[BE8F, 0]@89..90#14336# #0:MacroRules[BE8F, 0]@89..90#14336#::#0:MacroRules[BE8F, 0]@91..93#14336#std#0:MacroRules[BE8F, 0]@93..96#14336#::#0:MacroRules[BE8F, 0]@96..98#14336#collections#0:MacroRules[BE8F, 0]@98..109#14336#::#0:MacroRules[BE8F, 0]@109..111#14336#HashSet#0:MacroRules[BE8F, 0]@111..118#14336#<#0:MacroRules[BE8F, 0]@118..119#14336#(#0:MacroRules[BE8F, 0]@119..120#14336#)#0:MacroRules[BE8F, 0]@120..121#14336#>#0:MacroRules[BE8F, 0]@121..122#14336#,#0:MacroRules[BE8F, 0]@122..123#14336#
+}#0:MacroRules[BE8F, 0]@132..133#14336#
"#]],
);
}
@@ -75,12 +75,12 @@ macro_rules! f {
};
}
-fn#0:MacroCall[D499, 0]@30..32#ROOT2024# main#0:MacroCall[D499, 0]@33..37#ROOT2024#(#0:MacroCall[D499, 0]@37..38#ROOT2024#)#0:MacroCall[D499, 0]@38..39#ROOT2024# {#0:MacroCall[D499, 0]@40..41#ROOT2024#
- 1#0:MacroCall[D499, 0]@50..51#ROOT2024#;#0:MacroCall[D499, 0]@51..52#ROOT2024#
- 1.0#0:MacroCall[D499, 0]@61..64#ROOT2024#;#0:MacroCall[D499, 0]@64..65#ROOT2024#
- (#0:MacroCall[D499, 0]@74..75#ROOT2024#(#0:MacroCall[D499, 0]@75..76#ROOT2024#1#0:MacroCall[D499, 0]@76..77#ROOT2024#,#0:MacroCall[D499, 0]@77..78#ROOT2024# )#0:MacroCall[D499, 0]@78..79#ROOT2024#,#0:MacroCall[D499, 0]@79..80#ROOT2024# )#0:MacroCall[D499, 0]@80..81#ROOT2024#.#0:MacroCall[D499, 0]@81..82#ROOT2024#0#0:MacroCall[D499, 0]@82..85#ROOT2024#.#0:MacroCall[D499, 0]@82..85#ROOT2024#0#0:MacroCall[D499, 0]@82..85#ROOT2024#;#0:MacroCall[D499, 0]@85..86#ROOT2024#
- let#0:MacroCall[D499, 0]@95..98#ROOT2024# x#0:MacroCall[D499, 0]@99..100#ROOT2024# =#0:MacroCall[D499, 0]@101..102#ROOT2024# 1#0:MacroCall[D499, 0]@103..104#ROOT2024#;#0:MacroCall[D499, 0]@104..105#ROOT2024#
-}#0:MacroCall[D499, 0]@110..111#ROOT2024#
+fn#0:MacroCall[BE8F, 0]@30..32#ROOT2024# main#0:MacroCall[BE8F, 0]@33..37#ROOT2024#(#0:MacroCall[BE8F, 0]@37..38#ROOT2024#)#0:MacroCall[BE8F, 0]@38..39#ROOT2024# {#0:MacroCall[BE8F, 0]@40..41#ROOT2024#
+ 1#0:MacroCall[BE8F, 0]@50..51#ROOT2024#;#0:MacroCall[BE8F, 0]@51..52#ROOT2024#
+ 1.0#0:MacroCall[BE8F, 0]@61..64#ROOT2024#;#0:MacroCall[BE8F, 0]@64..65#ROOT2024#
+ (#0:MacroCall[BE8F, 0]@74..75#ROOT2024#(#0:MacroCall[BE8F, 0]@75..76#ROOT2024#1#0:MacroCall[BE8F, 0]@76..77#ROOT2024#,#0:MacroCall[BE8F, 0]@77..78#ROOT2024# )#0:MacroCall[BE8F, 0]@78..79#ROOT2024#,#0:MacroCall[BE8F, 0]@79..80#ROOT2024# )#0:MacroCall[BE8F, 0]@80..81#ROOT2024#.#0:MacroCall[BE8F, 0]@81..82#ROOT2024#0#0:MacroCall[BE8F, 0]@82..85#ROOT2024#.#0:MacroCall[BE8F, 0]@82..85#ROOT2024#0#0:MacroCall[BE8F, 0]@82..85#ROOT2024#;#0:MacroCall[BE8F, 0]@85..86#ROOT2024#
+ let#0:MacroCall[BE8F, 0]@95..98#ROOT2024# x#0:MacroCall[BE8F, 0]@99..100#ROOT2024# =#0:MacroCall[BE8F, 0]@101..102#ROOT2024# 1#0:MacroCall[BE8F, 0]@103..104#ROOT2024#;#0:MacroCall[BE8F, 0]@104..105#ROOT2024#
+}#0:MacroCall[BE8F, 0]@110..111#ROOT2024#
"#]],
@@ -171,7 +171,7 @@ macro_rules! identity {
}
fn main(foo: ()) {
- /* error: unresolved macro unresolved */"helloworld!"#0:Fn[B9C7, 0]@236..321#ROOT2024#;
+ /* error: unresolved macro unresolved */"helloworld!"#0:Fn[15AE, 0]@236..321#ROOT2024#;
}
}
@@ -197,7 +197,7 @@ macro_rules! mk_struct {
#[macro_use]
mod foo;
-struct#1:MacroRules[E572, 0]@59..65#14336# Foo#0:MacroCall[BDD3, 0]@32..35#ROOT2024#(#1:MacroRules[E572, 0]@70..71#14336#u32#0:MacroCall[BDD3, 0]@41..44#ROOT2024#)#1:MacroRules[E572, 0]@74..75#14336#;#1:MacroRules[E572, 0]@75..76#14336#
+struct#1:MacroRules[DB0C, 0]@59..65#14336# Foo#0:MacroCall[DB0C, 0]@32..35#ROOT2024#(#1:MacroRules[DB0C, 0]@70..71#14336#u32#0:MacroCall[DB0C, 0]@41..44#ROOT2024#)#1:MacroRules[DB0C, 0]@74..75#14336#;#1:MacroRules[DB0C, 0]@75..76#14336#
"#]],
);
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
index 1c69b37..5e95b06 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -20,13 +20,14 @@
use expect_test::Expect;
use hir_expand::{
AstId, InFile, MacroCallId, MacroCallKind, MacroKind,
+ builtin::quote::quote,
db::ExpandDatabase,
proc_macro::{ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind},
span_map::SpanMapRef,
};
-use intern::Symbol;
+use intern::{Symbol, sym};
use itertools::Itertools;
-use span::{Edition, Span};
+use span::{Edition, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext};
use stdx::{format_to, format_to_acc};
use syntax::{
AstNode, AstPtr,
@@ -34,7 +35,9 @@
SyntaxNode, T,
ast::{self, edit::IndentLevel},
};
+use syntax_bridge::token_tree_to_syntax_node;
use test_fixture::WithFixture;
+use tt::{TextRange, TextSize};
use crate::{
AdtId, Lookup, ModuleDefId,
@@ -386,3 +389,38 @@ fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
other.type_id() == TypeId::of::<Self>()
}
}
+
+#[test]
+fn regression_20171() {
+ // This really isn't the appropriate place to put this test, but it's convenient with access to `quote!`.
+ let span = Span {
+ range: TextRange::empty(TextSize::new(0)),
+ anchor: SpanAnchor {
+ file_id: span::EditionedFileId::current_edition(span::FileId::from_raw(0)),
+ ast_id: ROOT_ERASED_FILE_AST_ID,
+ },
+ ctx: SyntaxContext::root(Edition::CURRENT),
+ };
+ let close_brace = tt::Punct { char: '}', spacing: tt::Spacing::Alone, span };
+ let dotdot1 = tt::Punct { char: '.', spacing: tt::Spacing::Joint, span };
+ let dotdot2 = tt::Punct { char: '.', spacing: tt::Spacing::Alone, span };
+ let dollar_crate = sym::dollar_crate;
+ let tt = quote! {
+ span => {
+ if !((matches!(
+ drive_parser(&mut parser, data, false),
+ Err(TarParserError::CorruptField {
+ field: CorruptFieldContext::PaxKvLength,
+ error: GeneralParseError::ParseInt(ParseIntError { #dotdot1 #dotdot2 })
+ })
+ #close_brace ))) {
+ #dollar_crate::panic::panic_2021!();
+ }}
+ };
+ token_tree_to_syntax_node(
+ &tt,
+ syntax_bridge::TopEntryPoint::MacroStmts,
+ &mut |_| Edition::CURRENT,
+ Edition::CURRENT,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
index d5ae6f8..6952a9d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
@@ -181,9 +181,9 @@ fn foo(&self) {
self.0. 1;
}
-fn#0:Fn[4D85, 0]@45..47#ROOT2024# foo#0:Fn[4D85, 0]@48..51#ROOT2024#(#0:Fn[4D85, 0]@51..52#ROOT2024#�:Fn[4D85, 0]@52..53#ROOT2024#self#0:Fn[4D85, 0]@53..57#ROOT2024# )#0:Fn[4D85, 0]@57..58#ROOT2024# {#0:Fn[4D85, 0]@59..60#ROOT2024#
- self#0:Fn[4D85, 0]@65..69#ROOT2024# .#0:Fn[4D85, 0]@69..70#ROOT2024#0#0:Fn[4D85, 0]@70..71#ROOT2024#.#0:Fn[4D85, 0]@71..72#ROOT2024#1#0:Fn[4D85, 0]@73..74#ROOT2024#;#0:Fn[4D85, 0]@74..75#ROOT2024#
-}#0:Fn[4D85, 0]@76..77#ROOT2024#"#]],
+fn#0:Fn[8A31, 0]@45..47#ROOT2024# foo#0:Fn[8A31, 0]@48..51#ROOT2024#(#0:Fn[8A31, 0]@51..52#ROOT2024#�:Fn[8A31, 0]@52..53#ROOT2024#self#0:Fn[8A31, 0]@53..57#ROOT2024# )#0:Fn[8A31, 0]@57..58#ROOT2024# {#0:Fn[8A31, 0]@59..60#ROOT2024#
+ self#0:Fn[8A31, 0]@65..69#ROOT2024# .#0:Fn[8A31, 0]@69..70#ROOT2024#0#0:Fn[8A31, 0]@70..71#ROOT2024#.#0:Fn[8A31, 0]@71..72#ROOT2024#1#0:Fn[8A31, 0]@73..74#ROOT2024#;#0:Fn[8A31, 0]@74..75#ROOT2024#
+}#0:Fn[8A31, 0]@76..77#ROOT2024#"#]],
);
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
index 0837308..5030585 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
@@ -373,19 +373,14 @@ pub fn crate_def_map(db: &dyn DefDatabase, crate_id: Crate) -> &DefMap {
crate_local_def_map(db, crate_id).def_map(db)
}
-#[allow(unused_lifetimes)]
-mod __ {
- use super::*;
- #[salsa_macros::tracked]
- pub(crate) struct DefMapPair<'db> {
- #[tracked]
- #[returns(ref)]
- pub(crate) def_map: DefMap,
- #[returns(ref)]
- pub(crate) local: LocalDefMap,
- }
+#[salsa_macros::tracked]
+pub(crate) struct DefMapPair<'db> {
+ #[tracked]
+ #[returns(ref)]
+ pub(crate) def_map: DefMap,
+ #[returns(ref)]
+ pub(crate) local: LocalDefMap,
}
-pub(crate) use __::DefMapPair;
#[salsa_macros::tracked(returns(ref))]
pub(crate) fn crate_local_def_map(db: &dyn DefDatabase, crate_id: Crate) -> DefMapPair<'_> {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
index 6f32198..316ad5d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
@@ -1052,17 +1052,6 @@ fn process_names(&self, acc: &mut ScopeNames, db: &'db dyn DefDatabase) {
}
}
-pub fn resolver_for_expr(
- db: &dyn DefDatabase,
- owner: DefWithBodyId,
- expr_id: ExprId,
-) -> Resolver<'_> {
- let r = owner.resolver(db);
- let scopes = db.expr_scopes(owner);
- let scope_id = scopes.scope_for(expr_id);
- resolver_for_scope_(db, scopes, scope_id, r, owner)
-}
-
pub fn resolver_for_scope(
db: &dyn DefDatabase,
owner: DefWithBodyId,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs
index 1958eb6..92e610b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs
@@ -779,14 +779,10 @@ pub(crate) fn query(
Arc::new(VariantFields { fields, store: Arc::new(store), shape }),
Arc::new(source_map),
),
- None => (
- Arc::new(VariantFields {
- fields: Arena::default(),
- store: ExpressionStore::empty_singleton(),
- shape,
- }),
- ExpressionStoreSourceMap::empty_singleton(),
- ),
+ None => {
+ let (store, source_map) = ExpressionStore::empty_singleton();
+ (Arc::new(VariantFields { fields: Arena::default(), store, shape }), source_map)
+ }
}
}
@@ -878,7 +874,7 @@ fn lower_fields<Field: ast::HasAttrs + ast::HasVisibility>(
idx += 1;
}
Err(cfg) => {
- col.source_map.diagnostics.push(
+ col.store.diagnostics.push(
crate::expr_store::ExpressionStoreDiagnostics::InactiveCode {
node: InFile::new(fields.file_id, SyntaxNodePtr::new(field.syntax())),
cfg,
@@ -891,9 +887,9 @@ fn lower_fields<Field: ast::HasAttrs + ast::HasVisibility>(
if !has_fields {
return None;
}
- let store = col.store.finish();
+ let (store, source_map) = col.store.finish();
arena.shrink_to_fit();
- Some((arena, store, col.source_map))
+ Some((arena, store, source_map))
}
#[derive(Debug, PartialEq, Eq)]
@@ -980,7 +976,7 @@ pub fn is_payload_free(&self, db: &dyn DefDatabase) -> bool {
if !matches!(variant.shape, FieldsShape::Unit) {
let body = db.body(v.into());
// A variant with explicit discriminant
- if body.exprs[body.body_expr] != crate::hir::Expr::Missing {
+ if !matches!(body[body.body_expr], crate::hir::Expr::Missing) {
return false;
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs
index 800b40a..60fbc66 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs
@@ -125,8 +125,8 @@ pub fn find_builtin_macro(
(assert, Assert) => assert_expand,
(stringify, Stringify) => stringify_expand,
(asm, Asm) => asm_expand,
- (global_asm, GlobalAsm) => asm_expand,
- (naked_asm, NakedAsm) => asm_expand,
+ (global_asm, GlobalAsm) => global_asm_expand,
+ (naked_asm, NakedAsm) => naked_asm_expand,
(cfg, Cfg) => cfg_expand,
(core_panic, CorePanic) => panic_expand,
(std_panic, StdPanic) => panic_expand,
@@ -325,6 +325,36 @@ fn asm_expand(
ExpandResult::ok(expanded)
}
+fn global_asm_expand(
+ _db: &dyn ExpandDatabase,
+ _id: MacroCallId,
+ tt: &tt::TopSubtree,
+ span: Span,
+) -> ExpandResult<tt::TopSubtree> {
+ let mut tt = tt.clone();
+ tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Parenthesis;
+ let pound = mk_pound(span);
+ let expanded = quote! {span =>
+ builtin #pound global_asm #tt
+ };
+ ExpandResult::ok(expanded)
+}
+
+fn naked_asm_expand(
+ _db: &dyn ExpandDatabase,
+ _id: MacroCallId,
+ tt: &tt::TopSubtree,
+ span: Span,
+) -> ExpandResult<tt::TopSubtree> {
+ let mut tt = tt.clone();
+ tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Parenthesis;
+ let pound = mk_pound(span);
+ let expanded = quote! {span =>
+ builtin #pound naked_asm #tt
+ };
+ ExpandResult::ok(expanded)
+}
+
fn cfg_expand(
db: &dyn ExpandDatabase,
id: MacroCallId,
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs
index d5874f8..70c38d4 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs
@@ -129,7 +129,7 @@ macro_rules! quote {
}
}
}
-pub(super) use quote;
+pub use quote;
pub trait ToTokenTree {
fn to_tokens(self, span: Span, builder: &mut TopSubtreeBuilder);
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
index 679f611..217d991 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
@@ -179,10 +179,9 @@ pub fn as_str(&self) -> &str {
self.symbol.as_str()
}
- #[inline]
pub fn display<'a>(
&'a self,
- db: &dyn salsa::Database,
+ db: &dyn crate::db::ExpandDatabase,
edition: Edition,
) -> impl fmt::Display + 'a {
_ = db;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
index 24530a5..14b9cd20 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
@@ -281,7 +281,7 @@ pub(crate) fn const_eval_discriminant_variant(
let def = variant_id.into();
let body = db.body(def);
let loc = variant_id.lookup(db);
- if body.exprs[body.body_expr] == Expr::Missing {
+ if matches!(body[body.body_expr], Expr::Missing) {
let prev_idx = loc.index.checked_sub(1);
let value = match prev_idx {
Some(prev_idx) => {
@@ -334,7 +334,7 @@ fn has_closure(body: &Body, expr: ExprId) -> bool {
// Type checking clousres need an isolated body (See the above FIXME). Bail out early to prevent panic.
return unknown_const(infer[expr].clone());
}
- if let Expr::Path(p) = &ctx.body.exprs[expr] {
+ if let Expr::Path(p) = &ctx.body[expr] {
let resolver = &ctx.resolver;
if let Some(c) =
path_to_const(db, resolver, p, mode, || ctx.generics(), debruijn, infer[expr].clone())
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
index 5d3be07..b3d4684 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
@@ -273,8 +273,9 @@ fn impl_datum(&self, krate: Crate, impl_id: chalk_db::ImplId)
#[salsa::invoke(crate::variance::variances_of)]
#[salsa::cycle(
- cycle_fn = crate::variance::variances_of_cycle_fn,
- cycle_initial = crate::variance::variances_of_cycle_initial,
+ // cycle_fn = crate::variance::variances_of_cycle_fn,
+ // cycle_initial = crate::variance::variances_of_cycle_initial,
+ cycle_result = crate::variance::variances_of_cycle_initial,
)]
fn variances_of(&self, def: GenericDefId) -> Option<Arc<[crate::variance::Variance]>>;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
index 9c0f8f4..40fe307 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -226,11 +226,10 @@ fn validate_func_body(&mut self, func: FunctionId) {
let body = self.db.body(func.into());
let edition = self.edition(func);
let mut pats_replacements = body
- .pats
- .iter()
+ .pats()
.filter_map(|(pat_id, pat)| match pat {
Pat::Bind { id, .. } => {
- let bind_name = &body.bindings[*id].name;
+ let bind_name = &body[*id].name;
let mut suggested_text = to_lower_snake_case(bind_name.as_str())?;
if is_raw_identifier(&suggested_text, edition) {
suggested_text.insert_str(0, "r#");
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
index 5d56957..5ae6bf6 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
@@ -101,7 +101,7 @@ fn validate_body(&mut self, db: &dyn HirDatabase) {
self.check_for_trailing_return(body.body_expr, &body);
}
- for (id, expr) in body.exprs.iter() {
+ for (id, expr) in body.exprs() {
if let Some((variant, missed_fields, true)) =
record_literal_missing_fields(db, &self.infer, id, expr)
{
@@ -132,7 +132,7 @@ fn validate_body(&mut self, db: &dyn HirDatabase) {
}
}
- for (id, pat) in body.pats.iter() {
+ for (id, pat) in body.pats() {
if let Some((variant, missed_fields, true)) =
record_pattern_missing_fields(db, &self.infer, id, pat)
{
@@ -389,7 +389,7 @@ fn check_for_trailing_return(&mut self, body_expr: ExprId, body: &Body) {
if !self.validate_lints {
return;
}
- match &body.exprs[body_expr] {
+ match &body[body_expr] {
Expr::Block { statements, tail, .. } => {
let last_stmt = tail.or_else(|| match statements.last()? {
Statement::Expr { expr, .. } => Some(*expr),
@@ -428,7 +428,7 @@ fn check_for_unnecessary_else(&mut self, id: ExprId, expr: &Expr, db: &dyn HirDa
if else_branch.is_none() {
return;
}
- if let Expr::Block { statements, tail, .. } = &self.body.exprs[*then_branch] {
+ if let Expr::Block { statements, tail, .. } = &self.body[*then_branch] {
let last_then_expr = tail.or_else(|| match statements.last()? {
Statement::Expr { expr, .. } => Some(*expr),
_ => None,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
index c3ab5af..ca132fb 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
@@ -150,7 +150,7 @@ fn lower_pattern_unadjusted(&mut self, pat: PatId) -> Pat {
hir_def::hir::Pat::Bind { id, subpat, .. } => {
let bm = self.infer.binding_modes[pat];
ty = &self.infer[id];
- let name = &self.body.bindings[id].name;
+ let name = &self.body[id].name;
match (bm, ty.kind(Interner)) {
(BindingMode::Ref(_), TyKind::Ref(.., rty)) => ty = rty,
(BindingMode::Ref(_), _) => {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 20cf3c7..f6ad3c7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -7,7 +7,7 @@
use hir_def::{
AdtId, DefWithBodyId, FieldId, FunctionId, VariantId,
expr_store::{Body, path::Path},
- hir::{AsmOperand, Expr, ExprId, ExprOrPatId, Pat, PatId, Statement, UnaryOp},
+ hir::{AsmOperand, Expr, ExprId, ExprOrPatId, InlineAsmKind, Pat, PatId, Statement, UnaryOp},
resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs},
signatures::StaticFlags,
type_ref::Rawness,
@@ -217,7 +217,7 @@ fn walk_pats_top(&mut self, pats: impl Iterator<Item = PatId>, parent_expr: Expr
}
fn walk_pat(&mut self, current: PatId) {
- let pat = &self.body.pats[current];
+ let pat = &self.body[current];
if self.inside_union_destructure {
match pat {
@@ -264,7 +264,7 @@ fn walk_pat(&mut self, current: PatId) {
}
fn walk_expr(&mut self, current: ExprId) {
- let expr = &self.body.exprs[current];
+ let expr = &self.body[current];
let inside_assignment = mem::replace(&mut self.inside_assignment, false);
match expr {
&Expr::Call { callee, .. } => {
@@ -284,7 +284,7 @@ fn walk_expr(&mut self, current: ExprId) {
self.resolver.reset_to_guard(guard);
}
Expr::Ref { expr, rawness: Rawness::RawPtr, mutability: _ } => {
- match self.body.exprs[*expr] {
+ match self.body[*expr] {
// Do not report unsafe for `addr_of[_mut]!(EXTERN_OR_MUT_STATIC)`,
// see https://github.com/rust-lang/rust/pull/125834.
Expr::Path(_) => return,
@@ -315,7 +315,12 @@ fn walk_expr(&mut self, current: ExprId) {
self.inside_assignment = old_inside_assignment;
}
Expr::InlineAsm(asm) => {
- self.on_unsafe_op(current.into(), UnsafetyReason::InlineAsm);
+ if asm.kind == InlineAsmKind::Asm {
+ // `naked_asm!()` requires `unsafe` on the attribute (`#[unsafe(naked)]`),
+ // and `global_asm!()` doesn't require it at all.
+ self.on_unsafe_op(current.into(), UnsafetyReason::InlineAsm);
+ }
+
asm.operands.iter().for_each(|(_, op)| match op {
AsmOperand::In { expr, .. }
| AsmOperand::Out { expr: Some(expr), .. }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
index 810fe76..b3760e3 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -795,6 +795,14 @@ fn render_const_scalar(
let Some(bytes) = memory_map.get(addr, size_one * count) else {
return f.write_str("<ref-data-not-available>");
};
+ let expected_len = count * size_one;
+ if bytes.len() < expected_len {
+ never!(
+ "Memory map size is too small. Expected {expected_len}, got {}",
+ bytes.len(),
+ );
+ return f.write_str("<layout-error>");
+ }
f.write_str("&[")?;
let mut first = true;
for i in 0..count {
@@ -2328,6 +2336,7 @@ fn hir_fmt(
store[*path].hir_fmt(f, store)
}
TypeBound::Use(args) => {
+ write!(f, "use<")?;
let edition = f.edition();
let last = args.len().saturating_sub(1);
for (idx, arg) in args.iter().enumerate() {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
index d2eaf21..3f7eba9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
@@ -273,7 +273,7 @@ fn pat_iter_bound_mutability(&self, mut pat: impl Iterator<Item = PatId>) -> Mut
fn pat_bound_mutability(&self, pat: PatId) -> Mutability {
let mut r = Mutability::Not;
self.body.walk_bindings_in_pat(pat, |b| {
- if self.body.bindings[b].mode == BindingAnnotation::RefMut {
+ if self.body[b].mode == BindingAnnotation::RefMut {
r = Mutability::Mut;
}
});
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
index 99d3b5c..18288b7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
@@ -459,7 +459,7 @@ fn infer_bind_pat(
expected: &Ty,
decl: Option<DeclContext>,
) -> Ty {
- let Binding { mode, .. } = self.body.bindings[binding];
+ let Binding { mode, .. } = self.body[binding];
let mode = if mode == BindingAnnotation::Unannotated {
default_bm
} else {
@@ -639,7 +639,7 @@ fn pat_is_irrefutable(&self, decl_ctxt: Option<DeclContext>) -> bool {
pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool {
let mut res = false;
body.walk_pats(pat_id, &mut |pat| {
- res |= matches!(body[pat], Pat::Bind { id, .. } if body.bindings[id].mode == BindingAnnotation::Ref);
+ res |= matches!(body[pat], Pat::Bind { id, .. } if body[id].mode == BindingAnnotation::Ref);
});
res
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs
index 88c33ec..82d0ed4 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs
@@ -2,7 +2,7 @@
use base_db::Crate;
use hir_def::layout::TargetDataLayout;
-use rustc_abi::{AlignFromBytesError, TargetDataLayoutErrors, AddressSpace};
+use rustc_abi::{AddressSpace, AlignFromBytesError, TargetDataLayoutErrors};
use triomphe::Arc;
use crate::db::HirDatabase;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
index cc7d74f..b3bc226 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
@@ -119,8 +119,7 @@ fn eval_expr(
.unwrap();
let hir_body = db.body(function_id.into());
let b = hir_body
- .bindings
- .iter()
+ .bindings()
.find(|x| x.1.name.display_no_db(file_id.edition(&db)).to_smolstr() == "goal")
.unwrap()
.0;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs
index 06686b6..5c06234 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs
@@ -1018,8 +1018,12 @@ fn check_generic_args_len(
}
let lifetime_args_len = def_generics.len_lifetimes_self();
- if provided_lifetimes_count == 0 && lifetime_args_len > 0 && !lowering_assoc_type_generics {
- // In generic associated types, we never allow inferring the lifetimes.
+ if provided_lifetimes_count == 0
+ && lifetime_args_len > 0
+ && (!lowering_assoc_type_generics || infer_args)
+ {
+ // In generic associated types, we never allow inferring the lifetimes, but only in type context, that is
+ // when `infer_args == false`. In expression/pattern context we always allow inferring them, even for GATs.
match lifetime_elision {
&LifetimeElisionKind::AnonymousCreateParameter { report_in_path } => {
ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, report_in_path);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
index bf80ed7..482b420 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
@@ -1212,10 +1212,9 @@ pub fn is_ref_span(&self, body: &Body) -> bool {
match *self {
MirSpan::ExprId(expr) => matches!(body[expr], Expr::Ref { .. }),
// FIXME: Figure out if this is correct wrt. match ergonomics.
- MirSpan::BindingId(binding) => matches!(
- body.bindings[binding].mode,
- BindingAnnotation::Ref | BindingAnnotation::RefMut
- ),
+ MirSpan::BindingId(binding) => {
+ matches!(body[binding].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut)
+ }
MirSpan::PatId(_) | MirSpan::SelfParam | MirSpan::Unknown => false,
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
index 55fada1..9a97bd6 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
@@ -31,8 +31,8 @@
use triomphe::Arc;
use crate::{
- CallableDefId, ClosureId, ComplexMemoryMap, Const, ConstData, ConstScalar, FnDefId, Interner,
- MemoryMap, Substitution, ToChalk, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
+ AliasTy, CallableDefId, ClosureId, ComplexMemoryMap, Const, ConstData, ConstScalar, FnDefId,
+ Interner, MemoryMap, Substitution, ToChalk, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
consteval::{ConstEvalError, intern_const_scalar, try_const_usize},
db::{HirDatabase, InternedClosure},
display::{ClosureStyle, DisplayTarget, HirDisplay},
@@ -2195,7 +2195,7 @@ fn rec(
}
}
}
- chalk_ir::TyKind::Array(inner, len) => {
+ TyKind::Array(inner, len) => {
let len = match try_const_usize(this.db, len) {
Some(it) => it as usize,
None => not_supported!("non evaluatable array len in patching addresses"),
@@ -2213,7 +2213,7 @@ fn rec(
)?;
}
}
- chalk_ir::TyKind::Tuple(_, subst) => {
+ TyKind::Tuple(_, subst) => {
let layout = this.layout(ty)?;
for (id, ty) in subst.iter(Interner).enumerate() {
let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument
@@ -2229,7 +2229,7 @@ fn rec(
)?;
}
}
- chalk_ir::TyKind::Adt(adt, subst) => match adt.0 {
+ TyKind::Adt(adt, subst) => match adt.0 {
AdtId::StructId(s) => {
let data = s.fields(this.db);
let layout = this.layout(ty)?;
@@ -2280,6 +2280,10 @@ fn rec(
}
AdtId::UnionId(_) => (),
},
+ TyKind::Alias(AliasTy::Projection(proj)) => {
+ let ty = this.db.normalize_projection(proj.clone(), this.trait_env.clone());
+ rec(this, bytes, &ty, locals, mm, stack_depth_limit - 1)?;
+ }
_ => (),
}
Ok(())
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
index 845d6b8..07d8147 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
@@ -321,7 +321,7 @@ fn lower_expr_to_some_operand(
current: BasicBlockId,
) -> Result<Option<(Operand, BasicBlockId)>> {
if !self.has_adjustments(expr_id) {
- if let Expr::Literal(l) = &self.body.exprs[expr_id] {
+ if let Expr::Literal(l) = &self.body[expr_id] {
let ty = self.expr_ty_without_adjust(expr_id);
return Ok(Some((self.lower_literal_to_operand(ty, l)?, current)));
}
@@ -411,7 +411,7 @@ fn lower_expr_to_place_without_adjust(
place: Place,
mut current: BasicBlockId,
) -> Result<Option<BasicBlockId>> {
- match &self.body.exprs[expr_id] {
+ match &self.body[expr_id] {
Expr::OffsetOf(_) => {
not_supported!("builtin#offset_of")
}
@@ -1374,7 +1374,7 @@ fn push_field_projection(&mut self, place: &mut Place, expr_id: ExprId) -> Resul
}
fn lower_literal_or_const_to_operand(&mut self, ty: Ty, loc: &ExprId) -> Result<Operand> {
- match &self.body.exprs[*loc] {
+ match &self.body[*loc] {
Expr::Literal(l) => self.lower_literal_to_operand(ty, l),
Expr::Path(c) => {
let owner = self.owner;
@@ -1850,7 +1850,7 @@ fn lower_params_and_bindings(
self.drop_scopes.last_mut().unwrap().locals.push(local_id);
if let Pat::Bind { id, subpat: None } = self.body[it] {
if matches!(
- self.body.bindings[id].mode,
+ self.body[id].mode,
BindingAnnotation::Unannotated | BindingAnnotation::Mutable
) {
self.result.binding_locals.insert(id, local_id);
@@ -1859,7 +1859,7 @@ fn lower_params_and_bindings(
local_id
}));
// and then rest of bindings
- for (id, _) in self.body.bindings.iter() {
+ for (id, _) in self.body.bindings() {
if !pick_binding(id) {
continue;
}
@@ -2126,7 +2126,7 @@ pub fn mir_body_for_closure_query(
.result
.binding_locals
.into_iter()
- .filter(|it| ctx.body.binding_owners.get(&it.0).copied() == Some(expr))
+ .filter(|it| ctx.body.binding_owner(it.0) == Some(expr))
.collect();
if let Some(err) = err {
return Err(MirLowerError::UnresolvedUpvar(err));
@@ -2191,7 +2191,7 @@ pub fn lower_to_mir(
// 0 is return local
ctx.result.locals.alloc(Local { ty: ctx.expr_ty_after_adjustments(root_expr) });
let binding_picker = |b: BindingId| {
- let owner = ctx.body.binding_owners.get(&b).copied();
+ let owner = ctx.body.binding_owner(b);
if root_expr == body.body_expr { owner.is_none() } else { owner == Some(root_expr) }
};
// 1 to param_len is for params
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
index e7bffea..e074c2d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
@@ -133,7 +133,7 @@ pub(super) fn lower_expr_as_place_without_adjust(
}
this.lower_expr_to_some_place_without_adjust(expr_id, current)
};
- match &self.body.exprs[expr_id] {
+ match &self.body[expr_id] {
Expr::Path(p) => {
let resolver_guard =
self.resolver.update_to_inner_scope(self.db, self.owner, expr_id);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
index 61c0685..3325226 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -130,7 +130,7 @@ fn pattern_match_inner(
.collect::<Vec<_>>()
.into(),
);
- Ok(match &self.body.pats[pattern] {
+ Ok(match &self.body[pattern] {
Pat::Missing => return Err(MirLowerError::IncompletePattern),
Pat::Wild => (current, current_else),
Pat::Tuple { args, ellipsis } => {
@@ -436,7 +436,7 @@ fn pattern_match_inner(
(next, Some(else_target))
}
},
- Pat::Lit(l) => match &self.body.exprs[*l] {
+ Pat::Lit(l) => match &self.body[*l] {
Expr::Literal(l) => {
if mode == MatchingMode::Check {
let c = self.lower_literal_to_operand(self.infer[pattern].clone(), l)?;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
index 78a69cf..aad54f8 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
@@ -219,7 +219,7 @@ fn locals(&mut self) {
fn local_name(&self, local: LocalId) -> LocalName {
match self.local_to_binding.get(local) {
- Some(b) => LocalName::Binding(self.hir_body.bindings[*b].name.clone(), local),
+ Some(b) => LocalName::Binding(self.hir_body[*b].name.clone(), local),
None => LocalName::Unknown(local),
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
index 79754bc..9605a0b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
@@ -168,7 +168,7 @@ fn check_impl(
let inference_result = db.infer(def);
for (pat, mut ty) in inference_result.type_of_pat.iter() {
- if let Pat::Bind { id, .. } = body.pats[pat] {
+ if let Pat::Bind { id, .. } = body[pat] {
ty = &inference_result.type_of_binding[id];
}
let node = match pat_node(&body_source_map, pat, &db) {
@@ -316,7 +316,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
}
for (pat, mut ty) in inference_result.type_of_pat.iter() {
- if let Pat::Bind { id, .. } = body.pats[pat] {
+ if let Pat::Bind { id, .. } = body[pat] {
ty = &inference_result.type_of_binding[id];
}
let node = match body_source_map.pat_syntax(pat) {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs
index 87d9df6..08a215f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs
@@ -54,14 +54,14 @@ pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> Option<Ar
variances.is_empty().not().then(|| Arc::from_iter(variances))
}
-pub(crate) fn variances_of_cycle_fn(
- _db: &dyn HirDatabase,
- _result: &Option<Arc<[Variance]>>,
- _count: u32,
- _def: GenericDefId,
-) -> salsa::CycleRecoveryAction<Option<Arc<[Variance]>>> {
- salsa::CycleRecoveryAction::Iterate
-}
+// pub(crate) fn variances_of_cycle_fn(
+// _db: &dyn HirDatabase,
+// _result: &Option<Arc<[Variance]>>,
+// _count: u32,
+// _def: GenericDefId,
+// ) -> salsa::CycleRecoveryAction<Option<Arc<[Variance]>>> {
+// salsa::CycleRecoveryAction::Iterate
+// }
pub(crate) fn variances_of_cycle_initial(
db: &dyn HirDatabase,
@@ -965,7 +965,7 @@ fn prove_fixedpoint() {
struct FixedPoint<T, U, V>(&'static FixedPoint<(), T, U>, V);
"#,
expect![[r#"
- FixedPoint[T: covariant, U: covariant, V: covariant]
+ FixedPoint[T: bivariant, U: bivariant, V: bivariant]
"#]],
);
}
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 5c6f622..1b2b769 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -2036,7 +2036,7 @@ pub fn diagnostics<'db>(
)
}
let mol = &borrowck_result.mutability_of_locals;
- for (binding_id, binding_data) in body.bindings.iter() {
+ for (binding_id, binding_data) in body.bindings() {
if binding_data.problems.is_some() {
// We should report specific diagnostics for these problems, not `need-mut` and `unused-mut`.
continue;
@@ -3222,7 +3222,8 @@ pub fn is_env_or_option_env(&self, db: &dyn HirDatabase) -> bool {
}
}
- pub fn is_asm_or_global_asm(&self, db: &dyn HirDatabase) -> bool {
+ /// Is this `asm!()`, or a variant of it (e.g. `global_asm!()`)?
+ pub fn is_asm_like(&self, db: &dyn HirDatabase) -> bool {
match self.id {
MacroId::Macro2Id(it) => {
matches!(it.lookup(db).expander, MacroExpander::BuiltIn(m) if m.is_asm())
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 247bb69..adba592 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -677,8 +677,7 @@ pub fn speculative_expand_derive_as_pseudo_attr_macro(
pub fn rename_conflicts(&self, to_be_renamed: &Local, new_name: &Name) -> Vec<Local> {
let body = self.db.body(to_be_renamed.parent);
let resolver = to_be_renamed.parent.resolver(self.db);
- let starting_expr =
- body.binding_owners.get(&to_be_renamed.binding_id).copied().unwrap_or(body.body_expr);
+ let starting_expr = body.binding_owner(to_be_renamed.binding_id).unwrap_or(body.body_expr);
let mut visitor = RenameConflictsVisitor {
body: &body,
conflicts: FxHashSet::default(),
@@ -1776,7 +1775,7 @@ pub fn get_unsafe_ops(&self, def: DefWithBody) -> FxHashSet<ExprOrPatSource> {
pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
let Some(mac) = self.resolve_macro_call(macro_call) else { return false };
- if mac.is_asm_or_global_asm(self.db) {
+ if mac.is_asm_like(self.db) {
return true;
}
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index 0662bfd..ecc6e5f 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -242,11 +242,7 @@ fn type_id(&self, pat: &ast::Type) -> Option<TypeRefId> {
fn binding_id_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingId> {
let pat_id = self.pat_id(&pat.clone().into())?;
- if let Pat::Bind { id, .. } = self.store()?.pats[pat_id.as_pat()?] {
- Some(id)
- } else {
- None
- }
+ if let Pat::Bind { id, .. } = self.store()?[pat_id.as_pat()?] { Some(id) } else { None }
}
pub(crate) fn expr_adjustments(&self, expr: &ast::Expr) -> Option<&[Adjustment]> {
@@ -995,7 +991,7 @@ pub(crate) fn resolve_path(
let parent_hir_path = path
.parent_path()
.and_then(|p| collector.lower_path(p, &mut ExprCollector::impl_trait_error_allocator));
- let store = collector.store.finish();
+ let (store, _) = collector.store.finish();
// Case where path is a qualifier of a use tree, e.g. foo::bar::{Baz, Qux} where we are
// trying to resolve foo::bar.
@@ -1204,7 +1200,7 @@ pub(crate) fn resolve_hir_path_per_ns(
let mut collector = ExprCollector::new(db, self.resolver.module(), self.file_id);
let hir_path =
collector.lower_path(path.clone(), &mut ExprCollector::impl_trait_error_allocator)?;
- let store = collector.store.finish();
+ let (store, _) = collector.store.finish();
Some(resolve_hir_path_(
db,
&self.resolver,
@@ -1439,9 +1435,11 @@ fn scope_for(
) -> Option<ScopeId> {
node.ancestors_with_macros(db)
.take_while(|it| {
- !ast::Item::can_cast(it.kind())
- || ast::MacroCall::can_cast(it.kind())
- || ast::Use::can_cast(it.kind())
+ let kind = it.kind();
+ !ast::Item::can_cast(kind)
+ || ast::MacroCall::can_cast(kind)
+ || ast::Use::can_cast(kind)
+ || ast::AsmExpr::can_cast(kind)
})
.filter_map(|it| it.map(ast::Expr::cast).transpose())
.filter_map(|it| source_map.node_expr(it.as_ref())?.as_expr())
diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
index 7566508..dca1019 100644
--- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
@@ -125,6 +125,13 @@ fn collect_from_module(&mut self, module_id: ModuleId) {
}
ModuleDefId::AdtId(AdtId::EnumId(id)) => {
this.push_decl(id, name, false, None);
+ let enum_name = this.db.enum_signature(id).name.as_str().to_smolstr();
+ this.with_container_name(Some(enum_name), |this| {
+ let variants = id.enum_variants(this.db);
+ for (variant_id, variant_name, _) in &variants.variants {
+ this.push_decl(*variant_id, variant_name, true, None);
+ }
+ });
}
ModuleDefId::AdtId(AdtId::UnionId(id)) => {
this.push_decl(id, name, false, None);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
index efcbcef..9126e86 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
@@ -1,8 +1,8 @@
use ide_db::defs::{Definition, NameRefClass};
use syntax::{
AstNode, SyntaxNode,
- ast::{self, HasName, Name},
- ted,
+ ast::{self, HasName, Name, syntax_factory::SyntaxFactory},
+ syntax_editor::SyntaxEditor,
};
use crate::{
@@ -121,34 +121,36 @@ fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Opti
// Rename `extracted` with `binding` in `pat`.
fn rename_variable(pat: &ast::Pat, extracted: &[Name], binding: ast::Pat) -> SyntaxNode {
- let syntax = pat.syntax().clone_for_update();
+ let syntax = pat.syntax().clone_subtree();
+ let mut editor = SyntaxEditor::new(syntax.clone());
+ let make = SyntaxFactory::with_mappings();
let extracted = extracted
.iter()
- .map(|e| syntax.covering_element(e.syntax().text_range()))
+ .map(|e| e.syntax().text_range() - pat.syntax().text_range().start())
+ .map(|r| syntax.covering_element(r))
.collect::<Vec<_>>();
for extracted_syntax in extracted {
// If `extracted` variable is a record field, we should rename it to `binding`,
// otherwise we just need to replace `extracted` with `binding`.
-
if let Some(record_pat_field) =
extracted_syntax.ancestors().find_map(ast::RecordPatField::cast)
{
if let Some(name_ref) = record_pat_field.field_name() {
- ted::replace(
+ editor.replace(
record_pat_field.syntax(),
- ast::make::record_pat_field(
- ast::make::name_ref(&name_ref.text()),
- binding.clone(),
+ make.record_pat_field(
+ make.name_ref(&name_ref.text()),
+ binding.clone_for_update(),
)
- .syntax()
- .clone_for_update(),
+ .syntax(),
);
}
} else {
- ted::replace(extracted_syntax, binding.clone().syntax().clone_for_update());
+ editor.replace(extracted_syntax, binding.syntax().clone_for_update());
}
}
- syntax
+ editor.add_mappings(make.finish_with_mappings());
+ editor.finish().new_root().clone()
}
#[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
index 32c4ae2..8d27574 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
@@ -4,7 +4,8 @@
use syntax::{
SyntaxKind,
ast::{self, AstNode, HasAttrs, HasGenericParams, HasVisibility},
- match_ast, ted,
+ match_ast,
+ syntax_editor::{Position, SyntaxEditor},
};
use crate::{AssistContext, AssistId, Assists, assist_context::SourceChangeBuilder};
@@ -97,11 +98,14 @@ fn edit_struct_def(
// Note that we don't need to consider macro files in this function because this is
// currently not triggered for struct definitions inside macro calls.
let tuple_fields = record_fields.fields().filter_map(|f| {
- let field = ast::make::tuple_field(f.visibility(), f.ty()?).clone_for_update();
- ted::insert_all(
- ted::Position::first_child_of(field.syntax()),
+ let field = ast::make::tuple_field(f.visibility(), f.ty()?);
+ let mut editor = SyntaxEditor::new(field.syntax().clone());
+ editor.insert_all(
+ Position::first_child_of(field.syntax()),
f.attrs().map(|attr| attr.syntax().clone_subtree().clone_for_update().into()).collect(),
);
+ let field_syntax = editor.finish().new_root().clone();
+ let field = ast::TupleField::cast(field_syntax)?;
Some(field)
});
let tuple_fields = ast::make::tuple_field_list(tuple_fields);
@@ -1086,8 +1090,7 @@ pub struct $0Foo {
}
"#,
r#"
-pub struct Foo(#[my_custom_attr]
-u32);
+pub struct Foo(#[my_custom_attr]u32);
"#,
);
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs
index 79a78ab..47233fb 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs
@@ -2,7 +2,7 @@
use stdx::format_to;
use syntax::{
AstNode,
- ast::{self, HasGenericParams, HasName, Impl, make},
+ ast::{self, HasGenericParams, HasName, HasTypeBounds, Impl, make},
};
use crate::{
@@ -88,20 +88,19 @@ fn generate_trait_impl_text_from_impl(
let generic_params = impl_.generic_param_list().map(|generic_params| {
let lifetime_params =
generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
- let ty_or_const_params = generic_params.type_or_const_params().map(|param| {
+ let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| {
// remove defaults since they can't be specified in impls
- match param {
+ let param = match param {
ast::TypeOrConstParam::Type(param) => {
- let param = param.clone_for_update();
- param.remove_default();
+ let param = make::type_param(param.name()?, param.type_bound_list());
ast::GenericParam::TypeParam(param)
}
ast::TypeOrConstParam::Const(param) => {
- let param = param.clone_for_update();
- param.remove_default();
+ let param = make::const_param(param.name()?, param.ty()?);
ast::GenericParam::ConstParam(param)
}
- }
+ };
+ Some(param)
});
make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
index c7e5e41..20ee925 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
@@ -294,7 +294,7 @@ fn generate_setter_from_info(info: &AssistInfo, record_field_info: &RecordFieldI
let self_expr = make::ext::expr_self();
let lhs = make::expr_field(self_expr, field_name);
let rhs = make::expr_path(make::ext::ident_path(field_name));
- let assign_stmt = make::expr_stmt(make::expr_assignment(lhs, rhs));
+ let assign_stmt = make::expr_stmt(make::expr_assignment(lhs, rhs).into());
let body = make::block_expr([assign_stmt.into()], None);
// Make the setter fn
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs
index 2862e6d..14601ca 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs
@@ -1,14 +1,14 @@
use syntax::{
ast::{self, AstNode, HasName, edit_in_place::Indent, make},
- ted,
+ syntax_editor::{Position, SyntaxEditor},
};
use crate::{AssistContext, AssistId, Assists, utils};
-fn insert_impl(impl_: ast::Impl, nominal: &ast::Adt) {
+fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &ast::Adt) {
let indent = nominal.indent_level();
- ted::insert_all_raw(
- ted::Position::after(nominal.syntax()),
+ editor.insert_all(
+ Position::after(nominal.syntax()),
vec![
// Add a blank line after the ADT, and indentation for the impl to match the ADT
make::tokens::whitespace(&format!("\n\n{indent}")).into(),
@@ -51,14 +51,17 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
// Generate the impl
let impl_ = utils::generate_impl(&nominal);
+ let mut editor = edit.make_editor(nominal.syntax());
// Add a tabstop after the left curly brace
if let Some(cap) = ctx.config.snippet_cap {
if let Some(l_curly) = impl_.assoc_item_list().and_then(|it| it.l_curly_token()) {
- edit.add_tabstop_after_token(cap, l_curly);
+ let tabstop = edit.make_tabstop_after(cap);
+ editor.add_annotation(l_curly, tabstop);
}
}
- insert_impl(impl_, &edit.make_mut(nominal));
+ insert_impl(&mut editor, &impl_, &nominal);
+ edit.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
@@ -97,18 +100,22 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) ->
// Generate the impl
let impl_ = utils::generate_trait_impl_intransitive(&nominal, make::ty_placeholder());
+ let mut editor = edit.make_editor(nominal.syntax());
// Make the trait type a placeholder snippet
if let Some(cap) = ctx.config.snippet_cap {
if let Some(trait_) = impl_.trait_() {
- edit.add_placeholder_snippet(cap, trait_);
+ let placeholder = edit.make_placeholder_snippet(cap);
+ editor.add_annotation(trait_.syntax(), placeholder);
}
if let Some(l_curly) = impl_.assoc_item_list().and_then(|it| it.l_curly_token()) {
- edit.add_tabstop_after_token(cap, l_curly);
+ let tabstop = edit.make_tabstop_after(cap);
+ editor.add_annotation(l_curly, tabstop);
}
}
- insert_impl(impl_, &edit.make_mut(nominal));
+ insert_impl(&mut editor, &impl_, &nominal);
+ edit.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
index bab2ccf..4ddab2c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
@@ -1,6 +1,6 @@
-use ide_db::famous_defs::FamousDefs;
+use ide_db::{famous_defs::FamousDefs, traits::resolve_target_trait};
use syntax::{
- AstNode,
+ AstNode, T,
ast::{self, edit_in_place::Indent, make},
ted,
};
@@ -32,7 +32,7 @@
//
// $0impl<T> core::ops::IndexMut<Axis> for [T; 3] {
// fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
-// &self[index as usize]
+// &mut self[index as usize]
// }
// }
//
@@ -48,36 +48,34 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
let impl_def = ctx.find_node_at_offset::<ast::Impl>()?.clone_for_update();
let indent = impl_def.indent_level();
- let trait_ = impl_def.trait_()?;
- if let ast::Type::PathType(trait_path) = trait_ {
- let trait_type = ctx.sema.resolve_trait(&trait_path.path()?)?;
- let scope = ctx.sema.scope(trait_path.syntax())?;
- if trait_type != FamousDefs(&ctx.sema, scope.krate()).core_convert_Index()? {
- return None;
- }
- }
+ let ast::Type::PathType(path) = impl_def.trait_()? else {
+ return None;
+ };
+ let trait_name = path.path()?.segment()?.name_ref()?;
+
+ let scope = ctx.sema.scope(impl_def.trait_()?.syntax())?;
+ let famous = FamousDefs(&ctx.sema, scope.krate());
+
+ let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?;
+ let trait_new = get_trait_mut(&trait_, famous)?;
// Index -> IndexMut
- let index_trait = impl_def
- .syntax()
- .descendants()
- .filter_map(ast::NameRef::cast)
- .find(|it| it.text() == "Index")?;
- ted::replace(
- index_trait.syntax(),
- make::path_segment(make::name_ref("IndexMut")).clone_for_update().syntax(),
- );
+ ted::replace(trait_name.syntax(), make::name_ref(trait_new).clone_for_update().syntax());
// index -> index_mut
- let trait_method_name = impl_def
+ let (trait_method_name, new_trait_method_name) = impl_def
.syntax()
.descendants()
.filter_map(ast::Name::cast)
- .find(|it| it.text() == "index")?;
- ted::replace(trait_method_name.syntax(), make::name("index_mut").clone_for_update().syntax());
+ .find_map(process_method_name)?;
+ ted::replace(
+ trait_method_name.syntax(),
+ make::name(new_trait_method_name).clone_for_update().syntax(),
+ );
- let type_alias = impl_def.syntax().descendants().find_map(ast::TypeAlias::cast)?;
- ted::remove(type_alias.syntax());
+ if let Some(type_alias) = impl_def.syntax().descendants().find_map(ast::TypeAlias::cast) {
+ ted::remove(type_alias.syntax());
+ }
// &self -> &mut self
let mut_self_param = make::mut_self_param();
@@ -87,15 +85,14 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
// &Self::Output -> &mut Self::Output
let ret_type = impl_def.syntax().descendants().find_map(ast::RetType::cast)?;
- ted::replace(
- ret_type.syntax(),
- make::ret_type(make::ty("&mut Self::Output")).clone_for_update().syntax(),
- );
+ let new_ret_type = process_ret_type(&ret_type)?;
+ ted::replace(ret_type.syntax(), make::ret_type(new_ret_type).clone_for_update().syntax());
let fn_ = impl_def.assoc_item_list()?.assoc_items().find_map(|it| match it {
ast::AssocItem::Fn(f) => Some(f),
_ => None,
})?;
+ let _ = process_ref_mut(&fn_);
let assoc_list = make::assoc_item_list().clone_for_update();
ted::replace(impl_def.assoc_item_list()?.syntax(), assoc_list.syntax());
@@ -104,7 +101,7 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
let target = impl_def.syntax().text_range();
acc.add(
AssistId::generate("generate_mut_trait_impl"),
- "Generate `IndexMut` impl from this `Index` trait",
+ format!("Generate `{trait_new}` impl from this `{trait_name}` trait"),
target,
|edit| {
edit.insert(target.start(), format!("$0{impl_def}\n\n{indent}"));
@@ -112,6 +109,52 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
)
}
+fn process_ref_mut(fn_: &ast::Fn) -> Option<()> {
+ let expr = fn_.body()?.tail_expr()?;
+ match &expr {
+ ast::Expr::RefExpr(ref_expr) if ref_expr.mut_token().is_none() => {
+ ted::insert_all_raw(
+ ted::Position::after(ref_expr.amp_token()?),
+ vec![make::token(T![mut]).into(), make::tokens::whitespace(" ").into()],
+ );
+ }
+ _ => {}
+ }
+ None
+}
+
+fn get_trait_mut(apply_trait: &hir::Trait, famous: FamousDefs<'_, '_>) -> Option<&'static str> {
+ let trait_ = Some(apply_trait);
+ if trait_ == famous.core_convert_Index().as_ref() {
+ return Some("IndexMut");
+ }
+ if trait_ == famous.core_convert_AsRef().as_ref() {
+ return Some("AsMut");
+ }
+ if trait_ == famous.core_borrow_Borrow().as_ref() {
+ return Some("BorrowMut");
+ }
+ None
+}
+
+fn process_method_name(name: ast::Name) -> Option<(ast::Name, &'static str)> {
+ let new_name = match &*name.text() {
+ "index" => "index_mut",
+ "as_ref" => "as_mut",
+ "borrow" => "borrow_mut",
+ _ => return None,
+ };
+ Some((name, new_name))
+}
+
+fn process_ret_type(ref_ty: &ast::RetType) -> Option<ast::Type> {
+ let ty = ref_ty.ty()?;
+ let ast::Type::RefType(ref_type) = ty else {
+ return None;
+ };
+ Some(make::ty_ref(ref_type.ty()?, true))
+}
+
#[cfg(test)]
mod tests {
use crate::tests::{check_assist, check_assist_not_applicable};
@@ -139,7 +182,7 @@ pub enum Axis { X = 0, Y = 1, Z = 2 }
$0impl<T> core::ops::IndexMut<Axis> for [T; 3] {
fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
- &self[index as usize]
+ &mut self[index as usize]
}
}
@@ -188,6 +231,35 @@ fn index(&self, index: Axis) -> &Self::Output {
}
"#,
);
+
+ check_assist(
+ generate_mut_trait_impl,
+ r#"
+//- minicore: as_ref
+struct Foo(i32);
+
+impl core::convert::AsRef$0<i32> for Foo {
+ fn as_ref(&self) -> &i32 {
+ &self.0
+ }
+}
+"#,
+ r#"
+struct Foo(i32);
+
+$0impl core::convert::AsMut<i32> for Foo {
+ fn as_mut(&mut self) -> &mut i32 {
+ &mut self.0
+ }
+}
+
+impl core::convert::AsRef<i32> for Foo {
+ fn as_ref(&self) -> &i32 {
+ &self.0
+ }
+}
+"#,
+ );
}
#[test]
@@ -287,5 +359,13 @@ pub trait Index<Idx: ?Sized> {}
impl<T> Index$0<i32> for [T; 3] {}
"#,
);
+ check_assist_not_applicable(
+ generate_mut_trait_impl,
+ r#"
+pub trait AsRef<T: ?Sized> {}
+
+impl AsRef$0<i32> for [T; 3] {}
+"#,
+ );
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
index 4837f92..51c2f65 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
@@ -1,5 +1,6 @@
use ide_db::{
- imports::import_assets::item_for_path_search, use_trivial_constructor::use_trivial_constructor,
+ imports::import_assets::item_for_path_search, syntax_helpers::suggest_name::NameGenerator,
+ use_trivial_constructor::use_trivial_constructor,
};
use syntax::{
ast::{self, AstNode, HasName, HasVisibility, StructKind, edit_in_place::Indent, make},
@@ -35,10 +36,30 @@
pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
- // We want to only apply this to non-union structs with named fields
let field_list = match strukt.kind() {
- StructKind::Record(named) => named,
- _ => return None,
+ StructKind::Record(named) => {
+ named.fields().filter_map(|f| Some((f.name()?, f.ty()?))).collect::<Vec<_>>()
+ }
+ StructKind::Tuple(tuple) => {
+ let mut name_generator = NameGenerator::default();
+ tuple
+ .fields()
+ .enumerate()
+ .filter_map(|(i, f)| {
+ let ty = f.ty()?;
+ let name = match name_generator.for_type(
+ &ctx.sema.resolve_type(&ty)?,
+ ctx.db(),
+ ctx.edition(),
+ ) {
+ Some(name) => name,
+ None => name_generator.suggest_name(&format!("_{i}")),
+ };
+ Some((make::name(name.as_str()), f.ty()?))
+ })
+ .collect::<Vec<_>>()
+ }
+ StructKind::Unit => return None,
};
// Return early if we've found an existing new fn
@@ -50,11 +71,9 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
let target = strukt.syntax().text_range();
acc.add(AssistId::generate("generate_new"), "Generate `new`", target, |builder| {
let trivial_constructors = field_list
- .fields()
- .map(|f| {
- let name = f.name()?;
-
- let ty = ctx.sema.resolve_type(&f.ty()?)?;
+ .iter()
+ .map(|(name, ty)| {
+ let ty = ctx.sema.resolve_type(ty)?;
let item_in_ns = hir::ItemInNs::from(hir::ModuleDef::from(ty.as_adt()?));
@@ -73,34 +92,44 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
edition,
)?;
- Some(make::record_expr_field(make::name_ref(&name.text()), Some(expr)))
+ Some((make::name_ref(&name.text()), Some(expr)))
})
.collect::<Vec<_>>();
- let params = field_list.fields().enumerate().filter_map(|(i, f)| {
+ let params = field_list.iter().enumerate().filter_map(|(i, (name, ty))| {
if trivial_constructors[i].is_none() {
- let name = f.name()?;
- let ty = f.ty()?;
-
- Some(make::param(make::ident_pat(false, false, name).into(), ty))
+ Some(make::param(make::ident_pat(false, false, name.clone()).into(), ty.clone()))
} else {
None
}
});
let params = make::param_list(None, params);
- let fields = field_list.fields().enumerate().filter_map(|(i, f)| {
- let constructor = trivial_constructors[i].clone();
- if constructor.is_some() {
+ let fields = field_list.iter().enumerate().map(|(i, (name, _))| {
+ if let Some(constructor) = trivial_constructors[i].clone() {
constructor
} else {
- Some(make::record_expr_field(make::name_ref(&f.name()?.text()), None))
+ (make::name_ref(&name.text()), None)
}
});
- let fields = make::record_expr_field_list(fields);
- let record_expr = make::record_expr(make::ext::ident_path("Self"), fields);
- let body = make::block_expr(None, Some(record_expr.into()));
+ let tail_expr: ast::Expr = match strukt.kind() {
+ StructKind::Record(_) => {
+ let fields = fields.map(|(name, expr)| make::record_expr_field(name, expr));
+ let fields = make::record_expr_field_list(fields);
+ make::record_expr(make::ext::ident_path("Self"), fields).into()
+ }
+ StructKind::Tuple(_) => {
+ let args = fields.map(|(arg, expr)| {
+ let arg = || make::expr_path(make::path_unqualified(make::path_segment(arg)));
+ expr.unwrap_or_else(arg)
+ });
+ let arg_list = make::arg_list(args);
+ make::expr_call(make::expr_path(make::ext::ident_path("Self")), arg_list).into()
+ }
+ StructKind::Unit => unreachable!(),
+ };
+ let body = make::block_expr(None, tail_expr.into());
let ret_type = make::ret_type(make::ty_path(make::ext::ident_path("Self")));
@@ -120,8 +149,35 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
.clone_for_update();
fn_.indent(1.into());
- // Add a tabstop before the name
if let Some(cap) = ctx.config.snippet_cap {
+ match strukt.kind() {
+ StructKind::Tuple(_) => {
+ let struct_args = fn_
+ .body()
+ .unwrap()
+ .syntax()
+ .descendants()
+ .filter(|it| syntax::ast::ArgList::can_cast(it.kind()))
+ .flat_map(|args| args.children())
+ .filter(|it| syntax::ast::PathExpr::can_cast(it.kind()))
+ .enumerate()
+ .filter_map(|(i, node)| {
+ if trivial_constructors[i].is_none() { Some(node) } else { None }
+ });
+ if let Some(fn_params) = fn_.param_list() {
+ for (struct_arg, fn_param) in struct_args.zip(fn_params.params()) {
+ if let Some(fn_pat) = fn_param.pat() {
+ let fn_pat = fn_pat.syntax().clone();
+ builder
+ .add_placeholder_snippet_group(cap, vec![struct_arg, fn_pat]);
+ }
+ }
+ }
+ }
+ _ => {}
+ }
+
+ // Add a tabstop before the name
if let Some(name) = fn_.name() {
builder.add_tabstop_before(cap, name);
}
@@ -157,7 +213,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
}
#[cfg(test)]
-mod tests {
+mod record_tests {
use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
use super::*;
@@ -695,3 +751,308 @@ pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
);
}
}
+
+#[cfg(test)]
+mod tuple_tests {
+ use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
+
+ use super::*;
+
+ #[test]
+ fn test_generate_new_with_zst_fields() {
+ check_assist(
+ generate_new,
+ r#"
+struct Empty;
+
+struct Foo(Empty$0);
+"#,
+ r#"
+struct Empty;
+
+struct Foo(Empty);
+
+impl Foo {
+ fn $0new() -> Self {
+ Self(Empty)
+ }
+}
+"#,
+ );
+ check_assist(
+ generate_new,
+ r#"
+struct Empty;
+
+struct Foo(String, Empty$0);
+"#,
+ r#"
+struct Empty;
+
+struct Foo(String, Empty);
+
+impl Foo {
+ fn $0new(${1:_0}: String) -> Self {
+ Self(${1:_0}, Empty)
+ }
+}
+"#,
+ );
+ check_assist(
+ generate_new,
+ r#"
+enum Empty { Bar }
+
+struct Foo(Empty$0);
+"#,
+ r#"
+enum Empty { Bar }
+
+struct Foo(Empty);
+
+impl Foo {
+ fn $0new() -> Self {
+ Self(Empty::Bar)
+ }
+}
+"#,
+ );
+
+ // make sure the assist only works on unit variants
+ check_assist(
+ generate_new,
+ r#"
+struct Empty {}
+
+struct Foo(Empty$0);
+"#,
+ r#"
+struct Empty {}
+
+struct Foo(Empty);
+
+impl Foo {
+ fn $0new(${1:empty}: Empty) -> Self {
+ Self(${1:empty})
+ }
+}
+"#,
+ );
+ check_assist(
+ generate_new,
+ r#"
+enum Empty { Bar {} }
+
+struct Foo(Empty$0);
+"#,
+ r#"
+enum Empty { Bar {} }
+
+struct Foo(Empty);
+
+impl Foo {
+ fn $0new(${1:empty}: Empty) -> Self {
+ Self(${1:empty})
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn test_generate_new() {
+ check_assist(
+ generate_new,
+ r#"
+struct Foo($0);
+"#,
+ r#"
+struct Foo();
+
+impl Foo {
+ fn $0new() -> Self {
+ Self()
+ }
+}
+"#,
+ );
+ check_assist(
+ generate_new,
+ r#"
+struct Foo<T: Clone>($0);
+"#,
+ r#"
+struct Foo<T: Clone>();
+
+impl<T: Clone> Foo<T> {
+ fn $0new() -> Self {
+ Self()
+ }
+}
+"#,
+ );
+ check_assist(
+ generate_new,
+ r#"
+struct Foo<'a, T: Foo<'a>>($0);
+"#,
+ r#"
+struct Foo<'a, T: Foo<'a>>();
+
+impl<'a, T: Foo<'a>> Foo<'a, T> {
+ fn $0new() -> Self {
+ Self()
+ }
+}
+"#,
+ );
+ check_assist(
+ generate_new,
+ r#"
+struct Foo(String$0);
+"#,
+ r#"
+struct Foo(String);
+
+impl Foo {
+ fn $0new(${1:_0}: String) -> Self {
+ Self(${1:_0})
+ }
+}
+"#,
+ );
+ check_assist(
+ generate_new,
+ r#"
+struct Vec<T> { };
+struct Foo(String, Vec<i32>$0);
+"#,
+ r#"
+struct Vec<T> { };
+struct Foo(String, Vec<i32>);
+
+impl Foo {
+ fn $0new(${1:_0}: String, ${2:items}: Vec<i32>) -> Self {
+ Self(${1:_0}, ${2:items})
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn check_that_visibility_modifiers_dont_get_brought_in() {
+ check_assist(
+ generate_new,
+ r#"
+struct Vec<T> { };
+struct Foo(pub String, pub Vec<i32>$0);
+"#,
+ r#"
+struct Vec<T> { };
+struct Foo(pub String, pub Vec<i32>);
+
+impl Foo {
+ fn $0new(${1:_0}: String, ${2:items}: Vec<i32>) -> Self {
+ Self(${1:_0}, ${2:items})
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn generate_new_not_applicable_if_fn_exists() {
+ check_assist_not_applicable(
+ generate_new,
+ r#"
+struct Foo($0);
+
+impl Foo {
+ fn new() -> Self {
+ Self
+ }
+}
+"#,
+ );
+
+ check_assist_not_applicable(
+ generate_new,
+ r#"
+struct Foo($0);
+
+impl Foo {
+ fn New() -> Self {
+ Self
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn generate_new_target() {
+ check_assist_target(
+ generate_new,
+ r#"
+struct SomeThingIrrelevant;
+/// Has a lifetime parameter
+struct Foo<'a, T: Foo<'a>>($0);
+struct EvenMoreIrrelevant;
+"#,
+ "/// Has a lifetime parameter
+struct Foo<'a, T: Foo<'a>>();",
+ );
+ }
+
+ #[test]
+ fn test_unrelated_new() {
+ check_assist(
+ generate_new,
+ r#"
+pub struct AstId<N: AstNode> {
+ file_id: HirFileId,
+ file_ast_id: FileAstId<N>,
+}
+
+impl<N: AstNode> AstId<N> {
+ pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
+ AstId { file_id, file_ast_id }
+ }
+}
+
+pub struct Source<T>(pub HirFileId,$0 pub T);
+
+impl<T> Source<T> {
+ pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
+ Source(self.file_id, f(self.ast))
+ }
+}
+"#,
+ r#"
+pub struct AstId<N: AstNode> {
+ file_id: HirFileId,
+ file_ast_id: FileAstId<N>,
+}
+
+impl<N: AstNode> AstId<N> {
+ pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
+ AstId { file_id, file_ast_id }
+ }
+}
+
+pub struct Source<T>(pub HirFileId, pub T);
+
+impl<T> Source<T> {
+ pub fn $0new(${1:_0}: HirFileId, ${2:_1}: T) -> Self {
+ Self(${1:_0}, ${2:_1})
+ }
+
+ pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
+ Source(self.file_id, f(self.ast))
+ }
+}
+"#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
new file mode 100644
index 0000000..4e95ceb
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
@@ -0,0 +1,1000 @@
+use ast::make;
+use hir::{HasCrate, ModuleDef, Semantics};
+use ide_db::{
+ RootDatabase, famous_defs::FamousDefs, helpers::mod_path_to_ast,
+ imports::import_assets::item_for_path_search, use_trivial_constructor::use_trivial_constructor,
+};
+use syntax::{
+ TokenText,
+ ast::{self, AstNode, HasGenericParams, HasName, edit, edit_in_place::Indent},
+};
+
+use crate::{
+ AssistId,
+ assist_context::{AssistContext, Assists},
+ utils::add_cfg_attrs_to,
+};
+
+// Assist: generate_single_field_struct_from
+//
+// Implement From for a single field structure, ignore trivial types.
+//
+// ```
+// # //- minicore: from, phantom_data
+// use core::marker::PhantomData;
+// struct $0Foo<T> {
+// id: i32,
+// _phantom_data: PhantomData<T>,
+// }
+// ```
+// ->
+// ```
+// use core::marker::PhantomData;
+// struct Foo<T> {
+// id: i32,
+// _phantom_data: PhantomData<T>,
+// }
+//
+// impl<T> From<i32> for Foo<T> {
+// fn from(id: i32) -> Self {
+// Self { id, _phantom_data: PhantomData }
+// }
+// }
+// ```
+pub(crate) fn generate_single_field_struct_from(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_>,
+) -> Option<()> {
+ let strukt_name = ctx.find_node_at_offset::<ast::Name>()?;
+ let adt = ast::Adt::cast(strukt_name.syntax().parent()?)?;
+ let ast::Adt::Struct(strukt) = adt else {
+ return None;
+ };
+
+ let sema = &ctx.sema;
+ let (names, types) = get_fields(&strukt)?;
+
+ let module = sema.scope(strukt.syntax())?.module();
+ let constructors = make_constructors(ctx, module, &types);
+
+ if constructors.iter().filter(|expr| expr.is_none()).count() != 1 {
+ return None;
+ }
+ let main_field_i = constructors.iter().position(Option::is_none)?;
+ if from_impl_exists(&strukt, main_field_i, &ctx.sema).is_some() {
+ return None;
+ }
+
+ let main_field_name =
+ names.as_ref().map_or(TokenText::borrowed("value"), |names| names[main_field_i].text());
+ let main_field_ty = types[main_field_i].clone();
+
+ acc.add(
+ AssistId::generate("generate_single_field_struct_from"),
+ "Generate single field `From`",
+ strukt.syntax().text_range(),
+ |builder| {
+ let indent = strukt.indent_level();
+ let ty_where_clause = strukt.where_clause();
+ let type_gen_params = strukt.generic_param_list();
+ let type_gen_args = type_gen_params.as_ref().map(|params| params.to_generic_args());
+ let trait_gen_args = Some(make::generic_arg_list([ast::GenericArg::TypeArg(
+ make::type_arg(main_field_ty.clone()),
+ )]));
+
+ let ty = make::ty(&strukt_name.text());
+
+ let constructor =
+ make_adt_constructor(names.as_deref(), constructors, &main_field_name);
+ let body = make::block_expr([], Some(constructor));
+
+ let fn_ = make::fn_(
+ None,
+ make::name("from"),
+ None,
+ None,
+ make::param_list(
+ None,
+ [make::param(
+ make::path_pat(make::path_from_text(&main_field_name)),
+ main_field_ty,
+ )],
+ ),
+ body,
+ Some(make::ret_type(make::ty("Self"))),
+ false,
+ false,
+ false,
+ false,
+ )
+ .clone_for_update();
+
+ fn_.indent(1.into());
+
+ let impl_ = make::impl_trait(
+ false,
+ None,
+ trait_gen_args,
+ type_gen_params,
+ type_gen_args,
+ false,
+ make::ty("From"),
+ ty.clone(),
+ None,
+ ty_where_clause.map(|wc| edit::AstNodeEdit::reset_indent(&wc)),
+ None,
+ )
+ .clone_for_update();
+
+ impl_.get_or_create_assoc_item_list().add_item(fn_.into());
+
+ add_cfg_attrs_to(&strukt, &impl_);
+
+ impl_.reindent_to(indent);
+
+ builder.insert(strukt.syntax().text_range().end(), format!("\n\n{indent}{impl_}"));
+ },
+ )
+}
+
+fn make_adt_constructor(
+ names: Option<&[ast::Name]>,
+ constructors: Vec<Option<ast::Expr>>,
+ main_field_name: &TokenText<'_>,
+) -> ast::Expr {
+ if let Some(names) = names {
+ let fields = make::record_expr_field_list(names.iter().zip(constructors).map(
+ |(name, initializer)| {
+ make::record_expr_field(make::name_ref(&name.text()), initializer)
+ },
+ ));
+ make::record_expr(make::path_from_text("Self"), fields).into()
+ } else {
+ let arg_list = make::arg_list(constructors.into_iter().map(|expr| {
+ expr.unwrap_or_else(|| make::expr_path(make::path_from_text(main_field_name)))
+ }));
+ make::expr_call(make::expr_path(make::path_from_text("Self")), arg_list).into()
+ }
+}
+
+fn make_constructors(
+ ctx: &AssistContext<'_>,
+ module: hir::Module,
+ types: &[ast::Type],
+) -> Vec<Option<ast::Expr>> {
+ let (db, sema) = (ctx.db(), &ctx.sema);
+ types
+ .iter()
+ .map(|ty| {
+ let ty = sema.resolve_type(ty)?;
+ if ty.is_unit() {
+ return Some(make::expr_tuple([]).into());
+ }
+ let item_in_ns = ModuleDef::Adt(ty.as_adt()?).into();
+ let edition = module.krate().edition(db);
+
+ let ty_path = module.find_path(
+ db,
+ item_for_path_search(db, item_in_ns)?,
+ ctx.config.import_path_config(),
+ )?;
+
+ use_trivial_constructor(db, mod_path_to_ast(&ty_path, edition), &ty, edition)
+ })
+ .collect()
+}
+
+fn get_fields(strukt: &ast::Struct) -> Option<(Option<Vec<ast::Name>>, Vec<ast::Type>)> {
+ Some(match strukt.kind() {
+ ast::StructKind::Unit => return None,
+ ast::StructKind::Record(fields) => {
+ let names = fields.fields().map(|field| field.name()).collect::<Option<_>>()?;
+ let types = fields.fields().map(|field| field.ty()).collect::<Option<_>>()?;
+ (Some(names), types)
+ }
+ ast::StructKind::Tuple(fields) => {
+ (None, fields.fields().map(|field| field.ty()).collect::<Option<_>>()?)
+ }
+ })
+}
+
+fn from_impl_exists(
+ strukt: &ast::Struct,
+ main_field_i: usize,
+ sema: &Semantics<'_, RootDatabase>,
+) -> Option<()> {
+ let db = sema.db;
+ let strukt = sema.to_def(strukt)?;
+ let krate = strukt.krate(db);
+ let from_trait = FamousDefs(sema, krate).core_convert_From()?;
+ let ty = strukt.fields(db).get(main_field_i)?.ty(db);
+
+ strukt.ty(db).impls_trait(db, from_trait, &[ty]).then_some(())
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::{check_assist, check_assist_not_applicable};
+
+ use super::generate_single_field_struct_from;
+
+ #[test]
+ fn works() {
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo {
+ foo: i32,
+ }
+ "#,
+ r#"
+ struct Foo {
+ foo: i32,
+ }
+
+ impl From<i32> for Foo {
+ fn from(foo: i32) -> Self {
+ Self { foo }
+ }
+ }
+ "#,
+ );
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from, phantom_data
+ struct $0Foo {
+ b1: (),
+ b2: core::marker::PhantomData,
+ foo: i32,
+ a1: (),
+ a2: core::marker::PhantomData,
+ }
+ "#,
+ r#"
+ struct Foo {
+ b1: (),
+ b2: core::marker::PhantomData,
+ foo: i32,
+ a1: (),
+ a2: core::marker::PhantomData,
+ }
+
+ impl From<i32> for Foo {
+ fn from(foo: i32) -> Self {
+ Self { b1: (), b2: core::marker::PhantomData, foo, a1: (), a2: core::marker::PhantomData }
+ }
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn cfgs() {
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ #[cfg(feature = "foo")]
+ #[cfg(test)]
+ struct $0Foo {
+ foo: i32,
+ }
+ "#,
+ r#"
+ #[cfg(feature = "foo")]
+ #[cfg(test)]
+ struct Foo {
+ foo: i32,
+ }
+
+ #[cfg(feature = "foo")]
+ #[cfg(test)]
+ impl From<i32> for Foo {
+ fn from(foo: i32) -> Self {
+ Self { foo }
+ }
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn indent() {
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ mod foo {
+ struct $0Foo {
+ foo: i32,
+ }
+ }
+ "#,
+ r#"
+ mod foo {
+ struct Foo {
+ foo: i32,
+ }
+
+ impl From<i32> for Foo {
+ fn from(foo: i32) -> Self {
+ Self { foo }
+ }
+ }
+ }
+ "#,
+ );
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ mod foo {
+ mod bar {
+ struct $0Foo {
+ foo: i32,
+ }
+ }
+ }
+ "#,
+ r#"
+ mod foo {
+ mod bar {
+ struct Foo {
+ foo: i32,
+ }
+
+ impl From<i32> for Foo {
+ fn from(foo: i32) -> Self {
+ Self { foo }
+ }
+ }
+ }
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn where_clause_indent() {
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ mod foo {
+ mod bar {
+ trait Trait {}
+ struct $0Foo<T>
+ where
+ T: Trait,
+ {
+ foo: T,
+ }
+ }
+ }
+ "#,
+ r#"
+ mod foo {
+ mod bar {
+ trait Trait {}
+ struct Foo<T>
+ where
+ T: Trait,
+ {
+ foo: T,
+ }
+
+ impl<T> From<T> for Foo<T>
+ where
+ T: Trait,
+ {
+ fn from(foo: T) -> Self {
+ Self { foo }
+ }
+ }
+ }
+ }
+ "#,
+ );
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ mod foo {
+ mod bar {
+ trait Trait<const B: bool> {}
+ struct $0Foo<T>
+ where
+ T: Trait<{
+ true
+ }>
+ {
+ foo: T,
+ }
+ }
+ }
+ "#,
+ r#"
+ mod foo {
+ mod bar {
+ trait Trait<const B: bool> {}
+ struct Foo<T>
+ where
+ T: Trait<{
+ true
+ }>
+ {
+ foo: T,
+ }
+
+ impl<T> From<T> for Foo<T>
+ where
+ T: Trait<{
+ true
+ }>
+ {
+ fn from(foo: T) -> Self {
+ Self { foo }
+ }
+ }
+ }
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn generics() {
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo<T> {
+ foo: T,
+ }
+ "#,
+ r#"
+ struct Foo<T> {
+ foo: T,
+ }
+
+ impl<T> From<T> for Foo<T> {
+ fn from(foo: T) -> Self {
+ Self { foo }
+ }
+ }
+ "#,
+ );
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo<T: Send> {
+ foo: T,
+ }
+ "#,
+ r#"
+ struct Foo<T: Send> {
+ foo: T,
+ }
+
+ impl<T: Send> From<T> for Foo<T> {
+ fn from(foo: T) -> Self {
+ Self { foo }
+ }
+ }
+ "#,
+ );
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo<T: Send> where T: Sync,{
+ foo: T,
+ }
+ "#,
+ r#"
+ struct Foo<T: Send> where T: Sync,{
+ foo: T,
+ }
+
+ impl<T: Send> From<T> for Foo<T>
+ where T: Sync,
+ {
+ fn from(foo: T) -> Self {
+ Self { foo }
+ }
+ }
+ "#,
+ );
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo<T: Send> where T: Sync {
+ foo: T,
+ }
+ "#,
+ r#"
+ struct Foo<T: Send> where T: Sync {
+ foo: T,
+ }
+
+ impl<T: Send> From<T> for Foo<T>
+ where T: Sync
+ {
+ fn from(foo: T) -> Self {
+ Self { foo }
+ }
+ }
+ "#,
+ );
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo<T: Send> where T: Sync, Self: Send {
+ foo: T,
+ }
+ "#,
+ r#"
+ struct Foo<T: Send> where T: Sync, Self: Send {
+ foo: T,
+ }
+
+ impl<T: Send> From<T> for Foo<T>
+ where T: Sync, Self: Send
+ {
+ fn from(foo: T) -> Self {
+ Self { foo }
+ }
+ }
+ "#,
+ );
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo<T: Send>
+ where T: Sync, Self: Send
+ {
+ foo: T,
+ }
+ "#,
+ r#"
+ struct Foo<T: Send>
+ where T: Sync, Self: Send
+ {
+ foo: T,
+ }
+
+ impl<T: Send> From<T> for Foo<T>
+ where T: Sync, Self: Send
+ {
+ fn from(foo: T) -> Self {
+ Self { foo }
+ }
+ }
+ "#,
+ );
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo<T: Send>
+ where T: Sync, Self: Send,
+ {
+ foo: T,
+ }
+ "#,
+ r#"
+ struct Foo<T: Send>
+ where T: Sync, Self: Send,
+ {
+ foo: T,
+ }
+
+ impl<T: Send> From<T> for Foo<T>
+ where T: Sync, Self: Send,
+ {
+ fn from(foo: T) -> Self {
+ Self { foo }
+ }
+ }
+ "#,
+ );
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo<T: Send>
+ where T: Sync,
+ Self: Send,
+ {
+ foo: T,
+ }
+ "#,
+ r#"
+ struct Foo<T: Send>
+ where T: Sync,
+ Self: Send,
+ {
+ foo: T,
+ }
+
+ impl<T: Send> From<T> for Foo<T>
+ where T: Sync,
+ Self: Send,
+ {
+ fn from(foo: T) -> Self {
+ Self { foo }
+ }
+ }
+ "#,
+ );
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo<T: Send>
+ where
+ T: Sync,
+ Self: Send,
+ {
+ foo: T,
+ }
+ "#,
+ r#"
+ struct Foo<T: Send>
+ where
+ T: Sync,
+ Self: Send,
+ {
+ foo: T,
+ }
+
+ impl<T: Send> From<T> for Foo<T>
+ where
+ T: Sync,
+ Self: Send,
+ {
+ fn from(foo: T) -> Self {
+ Self { foo }
+ }
+ }
+ "#,
+ );
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo<T: Send + Sync>
+ where
+ T: Sync,
+ Self: Send,
+ {
+ foo: T,
+ }
+ "#,
+ r#"
+ struct Foo<T: Send + Sync>
+ where
+ T: Sync,
+ Self: Send,
+ {
+ foo: T,
+ }
+
+ impl<T: Send + Sync> From<T> for Foo<T>
+ where
+ T: Sync,
+ Self: Send,
+ {
+ fn from(foo: T) -> Self {
+ Self { foo }
+ }
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn tuple() {
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo(i32);
+ "#,
+ r#"
+ struct Foo(i32);
+
+ impl From<i32> for Foo {
+ fn from(value: i32) -> Self {
+ Self(value)
+ }
+ }
+ "#,
+ );
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo<T>(T);
+ "#,
+ r#"
+ struct Foo<T>(T);
+
+ impl<T> From<T> for Foo<T> {
+ fn from(value: T) -> Self {
+ Self(value)
+ }
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn trivial() {
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from, phantom_data
+ use core::marker::PhantomData;
+ struct $0Foo(i32, PhantomData<i32>);
+ "#,
+ r#"
+ use core::marker::PhantomData;
+ struct Foo(i32, PhantomData<i32>);
+
+ impl From<i32> for Foo {
+ fn from(value: i32) -> Self {
+ Self(value, PhantomData)
+ }
+ }
+ "#,
+ );
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from, phantom_data
+ use core::marker::PhantomData;
+ struct $0Foo(i32, PhantomData<()>);
+ "#,
+ r#"
+ use core::marker::PhantomData;
+ struct Foo(i32, PhantomData<()>);
+
+ impl From<i32> for Foo {
+ fn from(value: i32) -> Self {
+ Self(value, PhantomData)
+ }
+ }
+ "#,
+ );
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from, phantom_data
+ use core::marker::PhantomData;
+ struct $0Foo(PhantomData<()>, i32, PhantomData<()>);
+ "#,
+ r#"
+ use core::marker::PhantomData;
+ struct Foo(PhantomData<()>, i32, PhantomData<()>);
+
+ impl From<i32> for Foo {
+ fn from(value: i32) -> Self {
+ Self(PhantomData, value, PhantomData)
+ }
+ }
+ "#,
+ );
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from, phantom_data
+ use core::marker::PhantomData;
+ struct $0Foo<T>(PhantomData<T>, i32, PhantomData<()>);
+ "#,
+ r#"
+ use core::marker::PhantomData;
+ struct Foo<T>(PhantomData<T>, i32, PhantomData<()>);
+
+ impl<T> From<i32> for Foo<T> {
+ fn from(value: i32) -> Self {
+ Self(PhantomData, value, PhantomData)
+ }
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn unit() {
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo(i32, ());
+ "#,
+ r#"
+ struct Foo(i32, ());
+
+ impl From<i32> for Foo {
+ fn from(value: i32) -> Self {
+ Self(value, ())
+ }
+ }
+ "#,
+ );
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo((), i32, ());
+ "#,
+ r#"
+ struct Foo((), i32, ());
+
+ impl From<i32> for Foo {
+ fn from(value: i32) -> Self {
+ Self((), value, ())
+ }
+ }
+ "#,
+ );
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo((), (), i32, ());
+ "#,
+ r#"
+ struct Foo((), (), i32, ());
+
+ impl From<i32> for Foo {
+ fn from(value: i32) -> Self {
+ Self((), (), value, ())
+ }
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn invalid_multiple_main_field() {
+ check_assist_not_applicable(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo(i32, i32);
+ "#,
+ );
+ check_assist_not_applicable(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo<T>(i32, T);
+ "#,
+ );
+ check_assist_not_applicable(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo<T>(T, T);
+ "#,
+ );
+ check_assist_not_applicable(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo<T> { foo: T, bar: i32 }
+ "#,
+ );
+ check_assist_not_applicable(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo { foo: i32, bar: i64 }
+ "#,
+ );
+ }
+
+ #[test]
+ fn exists_other_from() {
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo(i32);
+
+ impl From<&i32> for Foo {
+ fn from(value: &i32) -> Self {
+ todo!()
+ }
+ }
+ "#,
+ r#"
+ struct Foo(i32);
+
+ impl From<i32> for Foo {
+ fn from(value: i32) -> Self {
+ Self(value)
+ }
+ }
+
+ impl From<&i32> for Foo {
+ fn from(value: &i32) -> Self {
+ todo!()
+ }
+ }
+ "#,
+ );
+ check_assist(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo(i32);
+
+ type X = i32;
+
+ impl From<&X> for Foo {
+ fn from(value: &X) -> Self {
+ todo!()
+ }
+ }
+ "#,
+ r#"
+ struct Foo(i32);
+
+ impl From<i32> for Foo {
+ fn from(value: i32) -> Self {
+ Self(value)
+ }
+ }
+
+ type X = i32;
+
+ impl From<&X> for Foo {
+ fn from(value: &X) -> Self {
+ todo!()
+ }
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn exists_from() {
+ check_assist_not_applicable(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo(i32);
+
+ impl From<i32> for Foo {
+ fn from(_: i32) -> Self {
+ todo!()
+ }
+ }
+ "#,
+ );
+ check_assist_not_applicable(
+ generate_single_field_struct_from,
+ r#"
+ //- minicore: from
+ struct $0Foo(i32);
+
+ type X = i32;
+
+ impl From<X> for Foo {
+ fn from(_: X) -> Self {
+ todo!()
+ }
+ }
+ "#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs
index 5f626d2..1b0c313 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs
@@ -1,7 +1,8 @@
use syntax::{
AstNode,
- ast::{self, make},
- ted,
+ algo::find_node_at_range,
+ ast::{self, syntax_factory::SyntaxFactory},
+ syntax_editor::SyntaxEditor,
};
use crate::{
@@ -66,33 +67,51 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) ->
return None;
}
}
+ let target = tgt.syntax().text_range();
+ let edit_tgt = tgt.syntax().clone_subtree();
+ let assignments: Vec<_> = collector
+ .assignments
+ .into_iter()
+ .filter_map(|(stmt, rhs)| {
+ Some((
+ find_node_at_range::<ast::BinExpr>(
+ &edit_tgt,
+ stmt.syntax().text_range() - target.start(),
+ )?,
+ find_node_at_range::<ast::Expr>(
+ &edit_tgt,
+ rhs.syntax().text_range() - target.start(),
+ )?,
+ ))
+ })
+ .collect();
+
+ let mut editor = SyntaxEditor::new(edit_tgt);
+ for (stmt, rhs) in assignments {
+ let mut stmt = stmt.syntax().clone();
+ if let Some(parent) = stmt.parent() {
+ if ast::ExprStmt::cast(parent.clone()).is_some() {
+ stmt = parent.clone();
+ }
+ }
+ editor.replace(stmt, rhs.syntax());
+ }
+ let new_tgt_root = editor.finish().new_root().clone();
+ let new_tgt = ast::Expr::cast(new_tgt_root)?;
acc.add(
AssistId::refactor_extract("pull_assignment_up"),
"Pull assignment up",
- tgt.syntax().text_range(),
+ target,
move |edit| {
- let assignments: Vec<_> = collector
- .assignments
- .into_iter()
- .map(|(stmt, rhs)| (edit.make_mut(stmt), rhs.clone_for_update()))
- .collect();
+ let make = SyntaxFactory::with_mappings();
+ let mut editor = edit.make_editor(tgt.syntax());
+ let assign_expr = make.expr_assignment(collector.common_lhs, new_tgt.clone());
+ let assign_stmt = make.expr_stmt(assign_expr.into());
- let tgt = edit.make_mut(tgt);
-
- for (stmt, rhs) in assignments {
- let mut stmt = stmt.syntax().clone();
- if let Some(parent) = stmt.parent() {
- if ast::ExprStmt::cast(parent.clone()).is_some() {
- stmt = parent.clone();
- }
- }
- ted::replace(stmt, rhs.syntax());
- }
- let assign_expr = make::expr_assignment(collector.common_lhs, tgt.clone());
- let assign_stmt = make::expr_stmt(assign_expr);
-
- ted::replace(tgt.syntax(), assign_stmt.syntax().clone_for_update());
+ editor.replace(tgt.syntax(), assign_stmt.syntax());
+ editor.add_mappings(make.finish_with_mappings());
+ edit.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
index 52ace03..9356d02 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
@@ -1,8 +1,9 @@
use itertools::Itertools;
use syntax::{
- Edition, NodeOrToken, SyntaxElement, T, TextRange, TextSize,
- ast::{self, AstNode, AstToken, make},
- match_ast, ted,
+ Edition, NodeOrToken, SyntaxNode, SyntaxToken, T,
+ ast::{self, AstNode, make},
+ match_ast,
+ syntax_editor::{Position, SyntaxEditor},
};
use crate::{AssistContext, AssistId, Assists};
@@ -40,21 +41,23 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
let replacements =
macro_calls.into_iter().filter_map(compute_dbg_replacement).collect::<Vec<_>>();
-
- acc.add(
- AssistId::quick_fix("remove_dbg"),
- "Remove dbg!()",
- replacements.iter().map(|&(range, _)| range).reduce(|acc, range| acc.cover(range))?,
- |builder| {
- for (range, expr) in replacements {
- if let Some(expr) = expr {
- builder.replace(range, expr.to_string());
- } else {
- builder.delete(range);
- }
+ let target = replacements
+ .iter()
+ .flat_map(|(node_or_token, _)| node_or_token.iter())
+ .map(|t| t.text_range())
+ .reduce(|acc, range| acc.cover(range))?;
+ acc.add(AssistId::quick_fix("remove_dbg"), "Remove dbg!()", target, |builder| {
+ let mut editor = builder.make_editor(ctx.source_file().syntax());
+ for (range, expr) in replacements {
+ if let Some(expr) = expr {
+ editor.insert(Position::before(range[0].clone()), expr.syntax().clone_for_update());
}
- },
- )
+ for node_or_token in range {
+ editor.delete(node_or_token);
+ }
+ }
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
+ })
}
/// Returns `None` when either
@@ -63,7 +66,9 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
/// - (`macro_expr` has no parent - is that possible?)
///
/// Returns `Some(_, None)` when the macro call should just be removed.
-fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Option<ast::Expr>)> {
+fn compute_dbg_replacement(
+ macro_expr: ast::MacroExpr,
+) -> Option<(Vec<NodeOrToken<SyntaxNode, SyntaxToken>>, Option<ast::Expr>)> {
let macro_call = macro_expr.macro_call()?;
let tt = macro_call.token_tree()?;
let r_delim = NodeOrToken::Token(tt.right_delimiter_token()?);
@@ -88,22 +93,22 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt
match_ast! {
match parent {
ast::StmtList(_) => {
- let range = macro_expr.syntax().text_range();
- let range = match whitespace_start(macro_expr.syntax().prev_sibling_or_token()) {
- Some(start) => range.cover_offset(start),
- None => range,
- };
- (range, None)
+ let mut replace = vec![macro_expr.syntax().clone().into()];
+ if let Some(prev_sibling) = macro_expr.syntax().prev_sibling_or_token()
+ && prev_sibling.kind() == syntax::SyntaxKind::WHITESPACE {
+ replace.push(prev_sibling);
+ }
+ (replace, None)
},
ast::ExprStmt(it) => {
- let range = it.syntax().text_range();
- let range = match whitespace_start(it.syntax().prev_sibling_or_token()) {
- Some(start) => range.cover_offset(start),
- None => range,
- };
- (range, None)
+ let mut replace = vec![it.syntax().clone().into()];
+ if let Some(prev_sibling) = it.syntax().prev_sibling_or_token()
+ && prev_sibling.kind() == syntax::SyntaxKind::WHITESPACE {
+ replace.push(prev_sibling);
+ }
+ (replace, None)
},
- _ => (macro_call.syntax().text_range(), Some(make::ext::expr_unit())),
+ _ => (vec![macro_call.syntax().clone().into()], Some(make::ext::expr_unit())),
}
}
}
@@ -147,13 +152,13 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt
};
let expr = replace_nested_dbgs(expr.clone());
let expr = if wrap { make::expr_paren(expr).into() } else { expr.clone_subtree() };
- (macro_call.syntax().text_range(), Some(expr))
+ (vec![macro_call.syntax().clone().into()], Some(expr))
}
// dbg!(expr0, expr1, ...)
exprs => {
let exprs = exprs.iter().cloned().map(replace_nested_dbgs);
let expr = make::expr_tuple(exprs);
- (macro_call.syntax().text_range(), Some(expr.into()))
+ (vec![macro_call.syntax().clone().into()], Some(expr.into()))
}
})
}
@@ -178,8 +183,8 @@ fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr {
return replaced;
}
- let expanded = expanded.clone_for_update();
-
+ let expanded = expanded.clone_subtree();
+ let mut editor = SyntaxEditor::new(expanded.syntax().clone());
// We need to collect to avoid mutation during traversal.
let macro_exprs: Vec<_> =
expanded.syntax().descendants().filter_map(ast::MacroExpr::cast).collect();
@@ -191,17 +196,13 @@ fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr {
};
if let Some(expr) = expr_opt {
- ted::replace(mac.syntax(), expr.syntax().clone_for_update());
+ editor.replace(mac.syntax(), expr.syntax().clone_for_update());
} else {
- ted::remove(mac.syntax());
+ editor.delete(mac.syntax());
}
}
-
- expanded
-}
-
-fn whitespace_start(it: Option<SyntaxElement>) -> Option<TextSize> {
- Some(it?.into_token().and_then(ast::Whitespace::cast)?.syntax().text_range().start())
+ let expanded_syntax = editor.finish().new_root().clone();
+ ast::Expr::cast(expanded_syntax).unwrap()
}
#[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs
index 62914ee..5ef8ba4 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs
@@ -64,13 +64,12 @@ pub(crate) fn replace_is_method_with_if_let_method(
let pat = make.tuple_struct_pat(make.ident_path(text), [var_pat.into()]);
let let_expr = make.expr_let(pat.into(), receiver);
- if let Some(cap) = ctx.config.snippet_cap {
- if let Some(ast::Pat::TupleStructPat(pat)) = let_expr.pat() {
- if let Some(first_var) = pat.fields().next() {
- let placeholder = edit.make_placeholder_snippet(cap);
- editor.add_annotation(first_var.syntax(), placeholder);
- }
- }
+ if let Some(cap) = ctx.config.snippet_cap
+ && let Some(ast::Pat::TupleStructPat(pat)) = let_expr.pat()
+ && let Some(first_var) = pat.fields().next()
+ {
+ let placeholder = edit.make_placeholder_snippet(cap);
+ editor.add_annotation(first_var.syntax(), placeholder);
}
editor.replace(call_expr.syntax(), let_expr.syntax());
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
index c260443..cde0d87 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
@@ -172,6 +172,7 @@ mod handlers {
mod generate_is_empty_from_len;
mod generate_mut_trait_impl;
mod generate_new;
+ mod generate_single_field_struct_from;
mod generate_trait_from_impl;
mod inline_call;
mod inline_const_as_literal;
@@ -305,6 +306,7 @@ pub(crate) fn all() -> &'static [Handler] {
generate_mut_trait_impl::generate_mut_trait_impl,
generate_new::generate_new,
generate_trait_from_impl::generate_trait_from_impl,
+ generate_single_field_struct_from::generate_single_field_struct_from,
inline_call::inline_call,
inline_call::inline_into_callers,
inline_const_as_literal::inline_const_as_literal,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
index 72f7195..fc1c692 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
@@ -1933,7 +1933,7 @@ pub enum Axis { X = 0, Y = 1, Z = 2 }
$0impl<T> core::ops::IndexMut<Axis> for [T; 3] {
fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
- &self[index as usize]
+ &mut self[index as usize]
}
}
@@ -1995,6 +1995,34 @@ fn $0set_name(&mut self, name: String) {
}
#[test]
+fn doctest_generate_single_field_struct_from() {
+ check_doc_test(
+ "generate_single_field_struct_from",
+ r#####"
+//- minicore: from, phantom_data
+use core::marker::PhantomData;
+struct $0Foo<T> {
+ id: i32,
+ _phantom_data: PhantomData<T>,
+}
+"#####,
+ r#####"
+use core::marker::PhantomData;
+struct Foo<T> {
+ id: i32,
+ _phantom_data: PhantomData<T>,
+}
+
+impl<T> From<i32> for Foo<T> {
+ fn from(id: i32) -> Self {
+ Self { id, _phantom_data: PhantomData }
+ }
+}
+"#####,
+ )
+}
+
+#[test]
fn doctest_generate_trait_from_impl() {
check_doc_test(
"generate_trait_from_impl",
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
index 87a4c2e..2c8cb6e 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -594,12 +594,10 @@ fn generate_impl_text_inner(
let generic_params = adt.generic_param_list().map(|generic_params| {
let lifetime_params =
generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
- let ty_or_const_params = generic_params.type_or_const_params().map(|param| {
- match param {
+ let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| {
+ let param = match param {
ast::TypeOrConstParam::Type(param) => {
- let param = param.clone_for_update();
// remove defaults since they can't be specified in impls
- param.remove_default();
let mut bounds =
param.type_bound_list().map_or_else(Vec::new, |it| it.bounds().collect());
if let Some(trait_) = trait_text {
@@ -610,17 +608,16 @@ fn generate_impl_text_inner(
}
};
// `{ty_param}: {bounds}`
- let param =
- make::type_param(param.name().unwrap(), make::type_bound_list(bounds));
+ let param = make::type_param(param.name()?, make::type_bound_list(bounds));
ast::GenericParam::TypeParam(param)
}
ast::TypeOrConstParam::Const(param) => {
- let param = param.clone_for_update();
// remove defaults since they can't be specified in impls
- param.remove_default();
+ let param = make::const_param(param.name()?, param.ty()?);
ast::GenericParam::ConstParam(param)
}
- }
+ };
+ Some(param)
});
make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
@@ -695,12 +692,10 @@ fn generate_impl_inner(
let generic_params = adt.generic_param_list().map(|generic_params| {
let lifetime_params =
generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
- let ty_or_const_params = generic_params.type_or_const_params().map(|param| {
- match param {
+ let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| {
+ let param = match param {
ast::TypeOrConstParam::Type(param) => {
- let param = param.clone_for_update();
// remove defaults since they can't be specified in impls
- param.remove_default();
let mut bounds =
param.type_bound_list().map_or_else(Vec::new, |it| it.bounds().collect());
if let Some(trait_) = &trait_ {
@@ -711,17 +706,16 @@ fn generate_impl_inner(
}
};
// `{ty_param}: {bounds}`
- let param =
- make::type_param(param.name().unwrap(), make::type_bound_list(bounds));
+ let param = make::type_param(param.name()?, make::type_bound_list(bounds));
ast::GenericParam::TypeParam(param)
}
ast::TypeOrConstParam::Const(param) => {
- let param = param.clone_for_update();
// remove defaults since they can't be specified in impls
- param.remove_default();
+ let param = make::const_param(param.name()?, param.ty()?);
ast::GenericParam::ConstParam(param)
}
- }
+ };
+ Some(param)
});
make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
@@ -749,16 +743,23 @@ fn generate_impl_inner(
.clone_for_update();
// Copy any cfg attrs from the original adt
- let cfg_attrs = adt
- .attrs()
- .filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false));
- for attr in cfg_attrs {
- impl_.add_attr(attr.clone_for_update());
- }
+ add_cfg_attrs_to(adt, &impl_);
impl_
}
+pub(crate) fn add_cfg_attrs_to<T, U>(from: &T, to: &U)
+where
+ T: HasAttrs,
+ U: AttrsOwnerEdit,
+{
+ let cfg_attrs =
+ from.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg"));
+ for attr in cfg_attrs {
+ to.add_attr(attr.clone_for_update());
+ }
+}
+
pub(crate) fn add_method_to_adt(
builder: &mut SourceChangeBuilder,
adt: &ast::Adt,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
index 092219a..975c2f0 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -37,6 +37,7 @@
SymbolKind, documentation::HasDocs, path_transform::PathTransform,
syntax_helpers::prettify_macro_expansion, traits::get_missing_assoc_items,
};
+use syntax::ast::HasGenericParams;
use syntax::{
AstNode, SmolStr, SyntaxElement, SyntaxKind, T, TextRange, ToSmolStr,
ast::{self, HasGenericArgs, HasTypeBounds, edit_in_place::AttrsOwnerEdit, make},
@@ -390,6 +391,12 @@ fn add_type_alias_impl(
} else if let Some(end) = transformed_ty.eq_token().map(|tok| tok.text_range().start())
{
end
+ } else if let Some(end) = transformed_ty
+ .where_clause()
+ .and_then(|wc| wc.where_token())
+ .map(|tok| tok.text_range().start())
+ {
+ end
} else if let Some(end) =
transformed_ty.semicolon_token().map(|tok| tok.text_range().start())
{
@@ -400,17 +407,29 @@ fn add_type_alias_impl(
let len = end - start;
let mut decl = transformed_ty.syntax().text().slice(..len).to_string();
- if !decl.ends_with(' ') {
- decl.push(' ');
- }
- decl.push_str("= ");
+ decl.truncate(decl.trim_end().len());
+ decl.push_str(" = ");
+
+ let wc = transformed_ty
+ .where_clause()
+ .map(|wc| {
+ let ws = wc
+ .where_token()
+ .and_then(|it| it.prev_token())
+ .filter(|token| token.kind() == SyntaxKind::WHITESPACE)
+ .map(|token| token.to_string())
+ .unwrap_or_else(|| " ".into());
+ format!("{ws}{wc}")
+ })
+ .unwrap_or_default();
match ctx.config.snippet_cap {
Some(cap) => {
- let snippet = format!("{decl}$0;");
+ let snippet = format!("{decl}$0{wc};");
item.snippet_edit(cap, TextEdit::replace(replacement_range, snippet));
}
None => {
+ decl.push_str(&wc);
item.text_edit(TextEdit::replace(replacement_range, decl));
}
};
@@ -1440,6 +1459,30 @@ impl<'b> Tr<'b> for () {
"#,
);
}
+ #[test]
+ fn includes_where_clause() {
+ check_edit(
+ "type Ty",
+ r#"
+trait Tr {
+ type Ty where Self: Copy;
+}
+
+impl Tr for () {
+ $0
+}
+"#,
+ r#"
+trait Tr {
+ type Ty where Self: Copy;
+}
+
+impl Tr for () {
+ type Ty = $0 where Self: Copy;
+}
+"#,
+ );
+ }
#[test]
fn strips_comments() {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
index 179d669..ac32649 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
@@ -458,6 +458,33 @@ impl B for A {
r"
struct A;
trait B {
+type O<'a>
+where
+Self: 'a;
+}
+impl B for A {
+$0
+}
+",
+ r#"
+struct A;
+trait B {
+type O<'a>
+where
+Self: 'a;
+}
+impl B for A {
+type O<'a> = $0
+where
+Self: 'a;
+}
+"#,
+ );
+ check_edit(
+ "type O",
+ r"
+struct A;
+trait B {
type O: ?Sized = u32;
}
impl B for A {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index de046e7..973256c 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -11,6 +11,40 @@
},
[
FileSymbol {
+ name: "A",
+ def: Variant(
+ Variant {
+ id: EnumVariantId(
+ 7800,
+ ),
+ },
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ EditionedFileId(
+ Id(2000),
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: VARIANT,
+ range: 201..202,
+ },
+ name_ptr: AstPtr(
+ SyntaxNodePtr {
+ kind: NAME,
+ range: 201..202,
+ },
+ ),
+ },
+ container_name: Some(
+ "Enum",
+ ),
+ is_alias: false,
+ is_assoc: true,
+ is_import: false,
+ do_not_complete: Yes,
+ },
+ FileSymbol {
name: "Alias",
def: TypeAlias(
TypeAlias {
@@ -43,6 +77,40 @@
do_not_complete: Yes,
},
FileSymbol {
+ name: "B",
+ def: Variant(
+ Variant {
+ id: EnumVariantId(
+ 7801,
+ ),
+ },
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ EditionedFileId(
+ Id(2000),
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: VARIANT,
+ range: 204..205,
+ },
+ name_ptr: AstPtr(
+ SyntaxNodePtr {
+ kind: NAME,
+ range: 204..205,
+ },
+ ),
+ },
+ container_name: Some(
+ "Enum",
+ ),
+ is_alias: false,
+ is_assoc: true,
+ is_import: false,
+ do_not_complete: Yes,
+ },
+ FileSymbol {
name: "CONST",
def: Const(
Const {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
index 06f3575..7402133 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
@@ -183,4 +183,28 @@ fn main() {
"#,
);
}
+
+ #[test]
+ fn generic_assoc_type_infer_lifetime_in_expr_position() {
+ check_diagnostics(
+ r#"
+//- minicore: sized
+struct Player;
+
+struct Foo<'c, C> {
+ _v: &'c C,
+}
+trait WithSignals: Sized {
+ type SignalCollection<'c, C>;
+ fn __signals_from_external(&self) -> Self::SignalCollection<'_, Self>;
+}
+impl WithSignals for Player {
+ type SignalCollection<'c, C> = Foo<'c, C>;
+ fn __signals_from_external(&self) -> Self::SignalCollection<'_, Self> {
+ Self::SignalCollection { _v: self }
+ }
+}
+ "#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index d8f6e81..17caf63 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -983,4 +983,19 @@ fn test() {
"#,
);
}
+
+ #[test]
+ fn naked_asm_is_safe() {
+ check_diagnostics(
+ r#"
+#[rustc_builtin_macro]
+macro_rules! naked_asm { () => {} }
+
+#[unsafe(naked)]
+extern "C" fn naked() {
+ naked_asm!("");
+}
+ "#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
index f58202a..a5d9a10 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
@@ -505,7 +505,7 @@ fn map_links<'e>(
Event::End(Tag::Link(link_type, target, _)) => {
in_link = false;
Event::End(Tag::Link(
- end_link_type.unwrap_or(link_type),
+ end_link_type.take().unwrap_or(link_type),
end_link_target.take().unwrap_or(target),
CowStr::Borrowed(""),
))
@@ -514,7 +514,7 @@ fn map_links<'e>(
let (link_type, link_target_s, link_name) =
callback(&end_link_target.take().unwrap(), &s, range, end_link_type.unwrap());
end_link_target = Some(CowStr::Boxed(link_target_s.into()));
- if !matches!(end_link_type, Some(LinkType::Autolink)) {
+ if !matches!(end_link_type, Some(LinkType::Autolink)) && link_type.is_some() {
end_link_type = link_type;
}
Event::Text(CowStr::Boxed(link_name.into()))
@@ -523,7 +523,7 @@ fn map_links<'e>(
let (link_type, link_target_s, link_name) =
callback(&end_link_target.take().unwrap(), &s, range, end_link_type.unwrap());
end_link_target = Some(CowStr::Boxed(link_target_s.into()));
- if !matches!(end_link_type, Some(LinkType::Autolink)) {
+ if !matches!(end_link_type, Some(LinkType::Autolink)) && link_type.is_some() {
end_link_type = link_type;
}
Event::Code(CowStr::Boxed(link_name.into()))
diff --git a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs
index 9bd8504..c081796 100755
--- a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs
@@ -23,6 +23,7 @@ pub enum FoldKind {
WhereClause,
ReturnType,
MatchArm,
+ Function,
// region: item runs
Modules,
Consts,
@@ -47,6 +48,7 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
let mut res = vec![];
let mut visited_comments = FxHashSet::default();
let mut visited_nodes = FxHashSet::default();
+ let mut merged_fn_bodies = FxHashSet::default();
// regions can be nested, here is a LIFO buffer
let mut region_starts: Vec<TextSize> = vec![];
@@ -59,6 +61,32 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
NodeOrToken::Token(token) => token.text().contains('\n'),
};
if is_multiline {
+ // for the func with multiline param list
+ if matches!(element.kind(), FN) {
+ if let NodeOrToken::Node(node) = &element {
+ if let Some(fn_node) = ast::Fn::cast(node.clone()) {
+ if !fn_node
+ .param_list()
+ .map(|param_list| param_list.syntax().text().contains_char('\n'))
+ .unwrap_or(false)
+ {
+ continue;
+ }
+
+ if let Some(body) = fn_node.body() {
+ res.push(Fold {
+ range: TextRange::new(
+ node.text_range().start(),
+ node.text_range().end(),
+ ),
+ kind: FoldKind::Function,
+ });
+ merged_fn_bodies.insert(body.syntax().text_range());
+ continue;
+ }
+ }
+ }
+ }
res.push(Fold { range: element.text_range(), kind });
continue;
}
@@ -152,6 +180,7 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
ARG_LIST | PARAM_LIST | GENERIC_ARG_LIST | GENERIC_PARAM_LIST => Some(FoldKind::ArgList),
ARRAY_EXPR => Some(FoldKind::Array),
RET_TYPE => Some(FoldKind::ReturnType),
+ FN => Some(FoldKind::Function),
WHERE_CLAUSE => Some(FoldKind::WhereClause),
ASSOC_ITEM_LIST
| RECORD_FIELD_LIST
@@ -291,6 +320,7 @@ mod tests {
use super::*;
+ #[track_caller]
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (ranges, text) = extract_tags(ra_fixture, "fold");
@@ -322,6 +352,7 @@ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
FoldKind::WhereClause => "whereclause",
FoldKind::ReturnType => "returntype",
FoldKind::MatchArm => "matcharm",
+ FoldKind::Function => "function",
FoldKind::TraitAliases => "traitaliases",
FoldKind::ExternCrates => "externcrates",
};
@@ -330,6 +361,23 @@ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
}
#[test]
+ fn test_fold_func_with_multiline_param_list() {
+ check(
+ r#"
+<fold function>fn func<fold arglist>(
+ a: i32,
+ b: i32,
+ c: i32,
+)</fold> <fold block>{
+
+
+
+}</fold></fold>
+"#,
+ );
+ }
+
+ #[test]
fn test_fold_comments() {
check(
r#"
@@ -541,10 +589,10 @@ fn fold_record_literals() {
fn fold_multiline_params() {
check(
r#"
-fn foo<fold arglist>(
+<fold function>fn foo<fold arglist>(
x: i32,
y: String,
-)</fold> {}
+)</fold> {}</fold>
"#,
)
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index f63499a..c548021 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -10958,3 +10958,68 @@ fn bar() -> Foo
"#]],
);
}
+
+#[test]
+fn regression_20190() {
+ check(
+ r#"
+struct Foo;
+
+/// [`foo` bar](Foo).
+fn has_docs$0() {}
+ "#,
+ expect.
+ "#]],
+ );
+}
+
+#[test]
+fn regression_20225() {
+ check(
+ r#"
+//- minicore: coerce_unsized
+trait Trait {
+ type Type<'a, T: ?Sized + 'a>;
+}
+
+enum Borrowed {}
+
+impl Trait for Borrowed {
+ type Type<'a, T: ?Sized + 'a> = &'a T;
+}
+
+enum Enum<'a, T: Trait + 'a> {
+ Variant1(T::Type<'a, [Enum<'a, T>]>),
+ Variant2,
+}
+
+impl Enum<'_, Borrowed> {
+ const CONSTANT$0: Self = Self::Variant1(&[Self::Variant2]);
+}
+ "#,
+ expect![[r#"
+ *CONSTANT*
+
+ ```rust
+ ra_test_fixture::Enum
+ ```
+
+ ```rust
+ const CONSTANT: Self = Variant1(&[Variant2])
+ ```
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs
index bf4688e..d0539ab 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs
@@ -92,7 +92,7 @@ pub(super) fn hints(
},
MirSpan::Unknown => continue,
};
- let binding = &hir.bindings[binding_idx];
+ let binding = &hir[binding_idx];
let name = binding.name.display_no_db(display_target.edition).to_smolstr();
if name.starts_with("<ra@") {
continue; // Ignore desugared variables
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs
index cd01c07..0da1785 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs
@@ -17,8 +17,12 @@ pub(super) fn hints(
let parent = path.syntax().parent()?;
let range = match path {
Either::Left(path) => {
- let paren =
- parent.ancestors().take_while(|it| ast::ParenType::can_cast(it.kind())).last();
+ let paren = parent
+ .ancestors()
+ .take_while(|it| {
+ ast::ParenType::can_cast(it.kind()) || ast::ForType::can_cast(it.kind())
+ })
+ .last();
let parent = paren.as_ref().and_then(|it| it.parent()).unwrap_or(parent);
if ast::TypeBound::can_cast(parent.kind())
|| ast::TypeAnchor::can_cast(parent.kind())
@@ -34,7 +38,7 @@ pub(super) fn hints(
return None;
}
sema.resolve_trait(&path.path()?)?;
- paren.map_or_else(|| path.syntax().text_range(), |it| it.text_range())
+ path.syntax().text_range()
}
Either::Right(dyn_) => {
if dyn_.dyn_token().is_some() {
@@ -89,7 +93,7 @@ fn foo(_: &T, _: for<'a> T) {}
impl T {}
// ^ dyn
impl T for (T) {}
- // ^^^ dyn
+ // ^ dyn
impl T
"#,
);
@@ -112,7 +116,7 @@ fn foo(
_: &mut (T + T)
// ^^^^^ dyn
_: *mut (T),
- // ^^^ dyn
+ // ^ dyn
) {}
"#,
);
@@ -136,4 +140,26 @@ fn foo(
"#]],
);
}
+
+ #[test]
+ fn hrtb_bound_does_not_add_dyn() {
+ check(
+ r#"
+//- minicore: fn
+fn test<F>(f: F) where F: for<'a> FnOnce(&'a i32) {}
+ // ^: Sized
+ "#,
+ );
+ }
+
+ #[test]
+ fn with_parentheses() {
+ check(
+ r#"
+trait T {}
+fn foo(v: &(T)) {}
+ // ^ dyn
+ "#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
index 0ac25da..2b4151e 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
@@ -4,7 +4,7 @@
use super::*;
-pub(super) use atom::{EXPR_RECOVERY_SET, LITERAL_FIRST, literal};
+pub(super) use atom::{EXPR_RECOVERY_SET, LITERAL_FIRST, literal, parse_asm_expr};
pub(crate) use atom::{block_expr, match_arm_list};
#[derive(PartialEq, Eq)]
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
index 8ed0fc6..7665656 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
@@ -253,8 +253,7 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
let m = p.start();
p.bump_remap(T![builtin]);
p.bump(T![#]);
- if p.at_contextual_kw(T![offset_of]) {
- p.bump_remap(T![offset_of]);
+ if p.eat_contextual_kw(T![offset_of]) {
p.expect(T!['(']);
type_(p);
p.expect(T![,]);
@@ -278,8 +277,7 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
p.expect(T![')']);
}
Some(m.complete(p, OFFSET_OF_EXPR))
- } else if p.at_contextual_kw(T![format_args]) {
- p.bump_remap(T![format_args]);
+ } else if p.eat_contextual_kw(T![format_args]) {
p.expect(T!['(']);
expr(p);
if p.eat(T![,]) {
@@ -302,7 +300,16 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
}
p.expect(T![')']);
Some(m.complete(p, FORMAT_ARGS_EXPR))
- } else if p.at_contextual_kw(T![asm]) {
+ } else if p.eat_contextual_kw(T![asm])
+ || p.eat_contextual_kw(T![global_asm])
+ || p.eat_contextual_kw(T![naked_asm])
+ {
+ // test asm_kinds
+ // fn foo() {
+ // builtin#asm("");
+ // builtin#global_asm("");
+ // builtin#naked_asm("");
+ // }
parse_asm_expr(p, m)
} else {
m.abandon(p);
@@ -321,8 +328,7 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
// tmp = out(reg) _,
// );
// }
-fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
- p.bump_remap(T![asm]);
+pub(crate) fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
p.expect(T!['(']);
if expr(p).is_none() {
p.err_and_bump("expected asm template");
@@ -411,11 +417,10 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
dir_spec.abandon(p);
op.abandon(p);
op_n.abandon(p);
- p.err_and_bump("expected asm operand");
- // improves error recovery and handles err_and_bump recovering from `{` which gets
- // the parser stuck here
+ // improves error recovery
if p.at(T!['{']) {
+ p.error("expected asm operand");
// test_err bad_asm_expr
// fn foo() {
// builtin#asm(
@@ -423,6 +428,8 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
// );
// }
expr(p);
+ } else {
+ p.err_and_bump("expected asm operand");
}
if p.at(T!['}']) {
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
index b9f4866..8e551b0 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
@@ -261,6 +261,19 @@ fn opt_item_without_modifiers(p: &mut Parser<'_>, m: Marker) -> Result<(), Marke
T![const] if (la == IDENT || la == T![_] || la == T![mut]) => consts::konst(p, m),
T![static] if (la == IDENT || la == T![_] || la == T![mut]) => consts::static_(p, m),
+ IDENT
+ if p.at_contextual_kw(T![builtin])
+ && p.nth_at(1, T![#])
+ && p.nth_at_contextual_kw(2, T![global_asm]) =>
+ {
+ p.bump_remap(T![builtin]);
+ p.bump(T![#]);
+ p.bump_remap(T![global_asm]);
+ // test global_asm
+ // builtin#global_asm("")
+ expressions::parse_asm_expr(p, m);
+ }
+
_ => return Err(m),
};
Ok(())
diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
index bff9acd..8fff1c3 100644
--- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
@@ -11,8 +11,8 @@
use std::ops;
use rustc_literal_escaper::{
- unescape_byte, unescape_byte_str, unescape_c_str, unescape_char, unescape_str, EscapeError,
- Mode,
+ EscapeError, Mode, unescape_byte, unescape_byte_str, unescape_c_str, unescape_char,
+ unescape_str,
};
use crate::{
diff --git a/src/tools/rust-analyzer/crates/parser/src/parser.rs b/src/tools/rust-analyzer/crates/parser/src/parser.rs
index 36a363a..ca02d9f 100644
--- a/src/tools/rust-analyzer/crates/parser/src/parser.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/parser.rs
@@ -29,7 +29,7 @@ pub(crate) struct Parser<'t> {
edition: Edition,
}
-const PARSER_STEP_LIMIT: usize = 15_000_000;
+const PARSER_STEP_LIMIT: usize = if cfg!(debug_assertions) { 150_000 } else { 15_000_000 };
impl<'t> Parser<'t> {
pub(super) fn new(inp: &'t Input, edition: Edition) -> Parser<'t> {
@@ -254,7 +254,10 @@ pub(crate) fn expect(&mut self, kind: SyntaxKind) -> bool {
/// Create an error node and consume the next token.
pub(crate) fn err_and_bump(&mut self, message: &str) {
- self.err_recover(message, TokenSet::EMPTY);
+ let m = self.start();
+ self.error(message);
+ self.bump_any();
+ m.complete(self, ERROR);
}
/// Create an error node and consume the next token unless it is in the recovery set.
diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
index f534546..12a13ca 100644
--- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
@@ -120,12 +120,14 @@ pub enum SyntaxKind {
DYN_KW,
FORMAT_ARGS_KW,
GEN_KW,
+ GLOBAL_ASM_KW,
INLATEOUT_KW,
INOUT_KW,
LABEL_KW,
LATEOUT_KW,
MACRO_RULES_KW,
MAY_UNWIND_KW,
+ NAKED_ASM_KW,
NOMEM_KW,
NORETURN_KW,
NOSTACK_KW,
@@ -599,12 +601,14 @@ pub const fn text(self) -> &'static str {
DEFAULT_KW => "default",
DYN_KW => "dyn",
FORMAT_ARGS_KW => "format_args",
+ GLOBAL_ASM_KW => "global_asm",
INLATEOUT_KW => "inlateout",
INOUT_KW => "inout",
LABEL_KW => "label",
LATEOUT_KW => "lateout",
MACRO_RULES_KW => "macro_rules",
MAY_UNWIND_KW => "may_unwind",
+ NAKED_ASM_KW => "naked_asm",
NOMEM_KW => "nomem",
NORETURN_KW => "noreturn",
NOSTACK_KW => "nostack",
@@ -699,12 +703,14 @@ pub fn is_contextual_keyword(self, edition: Edition) -> bool {
DEFAULT_KW => true,
DYN_KW if edition < Edition::Edition2018 => true,
FORMAT_ARGS_KW => true,
+ GLOBAL_ASM_KW => true,
INLATEOUT_KW => true,
INOUT_KW => true,
LABEL_KW => true,
LATEOUT_KW => true,
MACRO_RULES_KW => true,
MAY_UNWIND_KW => true,
+ NAKED_ASM_KW => true,
NOMEM_KW => true,
NORETURN_KW => true,
NOSTACK_KW => true,
@@ -787,12 +793,14 @@ pub fn is_keyword(self, edition: Edition) -> bool {
DEFAULT_KW => true,
DYN_KW if edition < Edition::Edition2018 => true,
FORMAT_ARGS_KW => true,
+ GLOBAL_ASM_KW => true,
INLATEOUT_KW => true,
INOUT_KW => true,
LABEL_KW => true,
LATEOUT_KW => true,
MACRO_RULES_KW => true,
MAY_UNWIND_KW => true,
+ NAKED_ASM_KW => true,
NOMEM_KW => true,
NORETURN_KW => true,
NOSTACK_KW => true,
@@ -938,12 +946,14 @@ pub fn from_contextual_keyword(ident: &str, edition: Edition) -> Option<SyntaxKi
"default" => DEFAULT_KW,
"dyn" if edition < Edition::Edition2018 => DYN_KW,
"format_args" => FORMAT_ARGS_KW,
+ "global_asm" => GLOBAL_ASM_KW,
"inlateout" => INLATEOUT_KW,
"inout" => INOUT_KW,
"label" => LABEL_KW,
"lateout" => LATEOUT_KW,
"macro_rules" => MACRO_RULES_KW,
"may_unwind" => MAY_UNWIND_KW,
+ "naked_asm" => NAKED_ASM_KW,
"nomem" => NOMEM_KW,
"noreturn" => NORETURN_KW,
"nostack" => NOSTACK_KW,
@@ -998,7 +1008,7 @@ pub fn from_char(c: char) -> Option<SyntaxKind> {
}
}
#[macro_export]
-macro_rules ! T_ { [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [att_syntax] => { $ crate :: SyntaxKind :: ATT_SYNTAX_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [inlateout] => { $ crate :: SyntaxKind :: INLATEOUT_KW } ; [inout] => { $ crate :: SyntaxKind :: INOUT_KW } ; [label] => { $ crate :: SyntaxKind :: LABEL_KW } ; [lateout] => { $ crate :: SyntaxKind :: LATEOUT_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [may_unwind] => { $ crate :: SyntaxKind :: MAY_UNWIND_KW } ; [nomem] => { $ crate :: SyntaxKind :: NOMEM_KW } ; [noreturn] => { $ crate :: SyntaxKind :: NORETURN_KW } ; [nostack] => { $ crate :: SyntaxKind :: NOSTACK_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [options] => { $ crate :: SyntaxKind :: OPTIONS_KW } ; [out] => { $ crate :: SyntaxKind :: OUT_KW } ; [preserves_flags] => { $ crate :: SyntaxKind :: PRESERVES_FLAGS_KW } ; [pure] => { $ crate :: SyntaxKind :: PURE_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [readonly] => { $ crate :: SyntaxKind :: READONLY_KW } ; [safe] => { $ crate :: SyntaxKind :: SAFE_KW } ; [sym] => { $ crate :: SyntaxKind :: SYM_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [int_number] => { $ crate :: SyntaxKind :: INT_NUMBER } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [string] => { $ crate :: SyntaxKind :: STRING } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; [frontmatter] => { $ crate :: SyntaxKind :: FRONTMATTER } ; }
+macro_rules ! T_ { [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [att_syntax] => { $ crate :: SyntaxKind :: ATT_SYNTAX_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [global_asm] => { $ crate :: SyntaxKind :: GLOBAL_ASM_KW } ; [inlateout] => { $ crate :: SyntaxKind :: INLATEOUT_KW } ; [inout] => { $ crate :: SyntaxKind :: INOUT_KW } ; [label] => { $ crate :: SyntaxKind :: LABEL_KW } ; [lateout] => { $ crate :: SyntaxKind :: LATEOUT_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [may_unwind] => { $ crate :: SyntaxKind :: MAY_UNWIND_KW } ; [naked_asm] => { $ crate :: SyntaxKind :: NAKED_ASM_KW } ; [nomem] => { $ crate :: SyntaxKind :: NOMEM_KW } ; [noreturn] => { $ crate :: SyntaxKind :: NORETURN_KW } ; [nostack] => { $ crate :: SyntaxKind :: NOSTACK_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [options] => { $ crate :: SyntaxKind :: OPTIONS_KW } ; [out] => { $ crate :: SyntaxKind :: OUT_KW } ; [preserves_flags] => { $ crate :: SyntaxKind :: PRESERVES_FLAGS_KW } ; [pure] => { $ crate :: SyntaxKind :: PURE_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [readonly] => { $ crate :: SyntaxKind :: READONLY_KW } ; [safe] => { $ crate :: SyntaxKind :: SAFE_KW } ; [sym] => { $ crate :: SyntaxKind :: SYM_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [int_number] => { $ crate :: SyntaxKind :: INT_NUMBER } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [string] => { $ crate :: SyntaxKind :: STRING } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; [frontmatter] => { $ crate :: SyntaxKind :: FRONTMATTER } ; }
impl ::core::marker::Copy for SyntaxKind {}
impl ::core::clone::Clone for SyntaxKind {
#[inline]
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
index 6ec4192..cef7b0e 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
@@ -21,6 +21,8 @@ fn arb_self_types() {
#[test]
fn asm_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_expr.rs"); }
#[test]
+ fn asm_kinds() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_kinds.rs"); }
+ #[test]
fn asm_label() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_label.rs"); }
#[test]
fn assoc_const_eq() {
@@ -298,6 +300,8 @@ fn generic_param_list() {
run_and_expect_no_errors("test_data/parser/inline/ok/generic_param_list.rs");
}
#[test]
+ fn global_asm() { run_and_expect_no_errors("test_data/parser/inline/ok/global_asm.rs"); }
+ #[test]
fn half_open_range_pat() {
run_and_expect_no_errors("test_data/parser/inline/ok/half_open_range_pat.rs");
}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_kinds.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_kinds.rast
new file mode 100644
index 0000000..c337d89
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_kinds.rast
@@ -0,0 +1,48 @@
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "foo"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ ASM_EXPR
+ BUILTIN_KW "builtin"
+ POUND "#"
+ ASM_KW "asm"
+ L_PAREN "("
+ LITERAL
+ STRING "\"\""
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ ASM_EXPR
+ BUILTIN_KW "builtin"
+ POUND "#"
+ GLOBAL_ASM_KW "global_asm"
+ L_PAREN "("
+ LITERAL
+ STRING "\"\""
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ ASM_EXPR
+ BUILTIN_KW "builtin"
+ POUND "#"
+ NAKED_ASM_KW "naked_asm"
+ L_PAREN "("
+ LITERAL
+ STRING "\"\""
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_kinds.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_kinds.rs
new file mode 100644
index 0000000..9c03e9d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_kinds.rs
@@ -0,0 +1,5 @@
+fn foo() {
+ builtin#asm("");
+ builtin#global_asm("");
+ builtin#naked_asm("");
+}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/global_asm.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/global_asm.rast
new file mode 100644
index 0000000..5337c56
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/global_asm.rast
@@ -0,0 +1,10 @@
+SOURCE_FILE
+ ASM_EXPR
+ BUILTIN_KW "builtin"
+ POUND "#"
+ GLOBAL_ASM_KW "global_asm"
+ L_PAREN "("
+ LITERAL
+ STRING "\"\""
+ R_PAREN ")"
+ WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/global_asm.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/global_asm.rs
new file mode 100644
index 0000000..967ce1f
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/global_asm.rs
@@ -0,0 +1 @@
+builtin#global_asm("")
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs
new file mode 100644
index 0000000..7966f74
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs
@@ -0,0 +1,34 @@
+//! Read `.cargo/config.toml` as a JSON object
+use rustc_hash::FxHashMap;
+use toolchain::Tool;
+
+use crate::{ManifestPath, Sysroot, utf8_stdout};
+
+pub(crate) type CargoConfigFile = serde_json::Map<String, serde_json::Value>;
+
+pub(crate) fn read(
+ manifest: &ManifestPath,
+ extra_env: &FxHashMap<String, Option<String>>,
+ sysroot: &Sysroot,
+) -> Option<CargoConfigFile> {
+ let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
+ cargo_config
+ .args(["-Z", "unstable-options", "config", "get", "--format", "json"])
+ .env("RUSTC_BOOTSTRAP", "1");
+ if manifest.is_rust_manifest() {
+ cargo_config.arg("-Zscript");
+ }
+
+ tracing::debug!("Discovering cargo config by {:?}", cargo_config);
+ let json: serde_json::Map<String, serde_json::Value> = utf8_stdout(&mut cargo_config)
+ .inspect(|json| {
+ tracing::debug!("Discovered cargo config: {:?}", json);
+ })
+ .inspect_err(|err| {
+ tracing::debug!("Failed to discover cargo config: {:?}", err);
+ })
+ .ok()
+ .and_then(|stdout| serde_json::from_str(&stdout).ok())?;
+
+ Some(json)
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
index 4bacc90..daadcd9 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
@@ -300,8 +300,6 @@ pub struct CargoMetadataConfig {
pub extra_args: Vec<String>,
/// Extra env vars to set when invoking the cargo command
pub extra_env: FxHashMap<String, Option<String>>,
- /// The target dir for this workspace load.
- pub target_dir: Utf8PathBuf,
/// What kind of metadata are we fetching: workspace, rustc, or sysroot.
pub kind: &'static str,
/// The toolchain version, if known.
@@ -317,188 +315,6 @@ struct PackageMetadata {
}
impl CargoWorkspace {
- /// Fetches the metadata for the given `cargo_toml` manifest.
- /// A successful result may contain another metadata error if the initial fetching failed but
- /// the `--no-deps` retry succeeded.
- ///
- /// The sysroot is used to set the `RUSTUP_TOOLCHAIN` env var when invoking cargo
- /// to ensure that the rustup proxy uses the correct toolchain.
- pub fn fetch_metadata(
- cargo_toml: &ManifestPath,
- current_dir: &AbsPath,
- config: &CargoMetadataConfig,
- sysroot: &Sysroot,
- no_deps: bool,
- locked: bool,
- progress: &dyn Fn(String),
- ) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
- let res = Self::fetch_metadata_(
- cargo_toml,
- current_dir,
- config,
- sysroot,
- no_deps,
- locked,
- progress,
- );
- if let Ok((_, Some(ref e))) = res {
- tracing::warn!(
- %cargo_toml,
- ?e,
- "`cargo metadata` failed, but retry with `--no-deps` succeeded"
- );
- }
- res
- }
-
- fn fetch_metadata_(
- cargo_toml: &ManifestPath,
- current_dir: &AbsPath,
- config: &CargoMetadataConfig,
- sysroot: &Sysroot,
- no_deps: bool,
- locked: bool,
- progress: &dyn Fn(String),
- ) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
- let cargo = sysroot.tool(Tool::Cargo, current_dir, &config.extra_env);
- let mut meta = MetadataCommand::new();
- meta.cargo_path(cargo.get_program());
- cargo.get_envs().for_each(|(var, val)| _ = meta.env(var, val.unwrap_or_default()));
- meta.manifest_path(cargo_toml.to_path_buf());
- match &config.features {
- CargoFeatures::All => {
- meta.features(CargoOpt::AllFeatures);
- }
- CargoFeatures::Selected { features, no_default_features } => {
- if *no_default_features {
- meta.features(CargoOpt::NoDefaultFeatures);
- }
- if !features.is_empty() {
- meta.features(CargoOpt::SomeFeatures(features.clone()));
- }
- }
- }
- meta.current_dir(current_dir);
-
- let mut other_options = vec![];
- // cargo metadata only supports a subset of flags of what cargo usually accepts, and usually
- // the only relevant flags for metadata here are unstable ones, so we pass those along
- // but nothing else
- let mut extra_args = config.extra_args.iter();
- while let Some(arg) = extra_args.next() {
- if arg == "-Z" {
- if let Some(arg) = extra_args.next() {
- other_options.push("-Z".to_owned());
- other_options.push(arg.to_owned());
- }
- }
- }
-
- if !config.targets.is_empty() {
- other_options.extend(
- config.targets.iter().flat_map(|it| ["--filter-platform".to_owned(), it.clone()]),
- );
- }
- if no_deps {
- other_options.push("--no-deps".to_owned());
- }
-
- let mut using_lockfile_copy = false;
- // The manifest is a rust file, so this means its a script manifest
- if cargo_toml.is_rust_manifest() {
- other_options.push("-Zscript".to_owned());
- } else if config
- .toolchain_version
- .as_ref()
- .is_some_and(|v| *v >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH)
- {
- let lockfile = <_ as AsRef<Utf8Path>>::as_ref(cargo_toml).with_extension("lock");
- let target_lockfile = config
- .target_dir
- .join("rust-analyzer")
- .join("metadata")
- .join(config.kind)
- .join("Cargo.lock");
- match std::fs::copy(&lockfile, &target_lockfile) {
- Ok(_) => {
- using_lockfile_copy = true;
- other_options.push("--lockfile-path".to_owned());
- other_options.push(target_lockfile.to_string());
- }
- Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
- // There exists no lockfile yet
- using_lockfile_copy = true;
- other_options.push("--lockfile-path".to_owned());
- other_options.push(target_lockfile.to_string());
- }
- Err(e) => {
- tracing::warn!(
- "Failed to copy lock file from `{lockfile}` to `{target_lockfile}`: {e}",
- );
- }
- }
- }
- if using_lockfile_copy {
- other_options.push("-Zunstable-options".to_owned());
- meta.env("RUSTC_BOOTSTRAP", "1");
- }
- // No need to lock it if we copied the lockfile, we won't modify the original after all/
- // This way cargo cannot error out on us if the lockfile requires updating.
- if !using_lockfile_copy && locked {
- other_options.push("--locked".to_owned());
- }
- meta.other_options(other_options);
-
- // FIXME: Fetching metadata is a slow process, as it might require
- // calling crates.io. We should be reporting progress here, but it's
- // unclear whether cargo itself supports it.
- progress("cargo metadata: started".to_owned());
-
- let res = (|| -> anyhow::Result<(_, _)> {
- let mut errored = false;
- let output =
- spawn_with_streaming_output(meta.cargo_command(), &mut |_| (), &mut |line| {
- errored = errored || line.starts_with("error") || line.starts_with("warning");
- if errored {
- progress("cargo metadata: ?".to_owned());
- return;
- }
- progress(format!("cargo metadata: {line}"));
- })?;
- if !output.status.success() {
- progress(format!("cargo metadata: failed {}", output.status));
- let error = cargo_metadata::Error::CargoMetadata {
- stderr: String::from_utf8(output.stderr)?,
- }
- .into();
- if !no_deps {
- // If we failed to fetch metadata with deps, try again without them.
- // This makes r-a still work partially when offline.
- if let Ok((metadata, _)) = Self::fetch_metadata_(
- cargo_toml,
- current_dir,
- config,
- sysroot,
- true,
- locked,
- progress,
- ) {
- return Ok((metadata, Some(error)));
- }
- }
- return Err(error);
- }
- let stdout = from_utf8(&output.stdout)?
- .lines()
- .find(|line| line.starts_with('{'))
- .ok_or(cargo_metadata::Error::NoJson)?;
- Ok((cargo_metadata::MetadataCommand::parse(stdout)?, None))
- })()
- .with_context(|| format!("Failed to run `{:?}`", meta.cargo_command()));
- progress("cargo metadata: finished".to_owned());
- res
- }
-
pub fn new(
mut meta: cargo_metadata::Metadata,
ws_manifest_path: ManifestPath,
@@ -733,3 +549,214 @@ pub fn requires_rustc_private(&self) -> bool {
self.requires_rustc_private
}
}
+
+pub(crate) struct FetchMetadata {
+ command: cargo_metadata::MetadataCommand,
+ lockfile_path: Option<Utf8PathBuf>,
+ kind: &'static str,
+ no_deps: bool,
+ no_deps_result: anyhow::Result<cargo_metadata::Metadata>,
+ other_options: Vec<String>,
+}
+
+impl FetchMetadata {
+ /// Builds a command to fetch metadata for the given `cargo_toml` manifest.
+ ///
+ /// Performs a lightweight pre-fetch using the `--no-deps` option,
+ /// available via [`FetchMetadata::no_deps_metadata`], to gather basic
+ /// information such as the `target-dir`.
+ ///
+ /// The provided sysroot is used to set the `RUSTUP_TOOLCHAIN`
+ /// environment variable when invoking Cargo, ensuring that the
+ /// rustup proxy selects the correct toolchain.
+ pub(crate) fn new(
+ cargo_toml: &ManifestPath,
+ current_dir: &AbsPath,
+ config: &CargoMetadataConfig,
+ sysroot: &Sysroot,
+ no_deps: bool,
+ ) -> Self {
+ let cargo = sysroot.tool(Tool::Cargo, current_dir, &config.extra_env);
+ let mut command = MetadataCommand::new();
+ command.cargo_path(cargo.get_program());
+ cargo.get_envs().for_each(|(var, val)| _ = command.env(var, val.unwrap_or_default()));
+ command.manifest_path(cargo_toml.to_path_buf());
+ match &config.features {
+ CargoFeatures::All => {
+ command.features(CargoOpt::AllFeatures);
+ }
+ CargoFeatures::Selected { features, no_default_features } => {
+ if *no_default_features {
+ command.features(CargoOpt::NoDefaultFeatures);
+ }
+ if !features.is_empty() {
+ command.features(CargoOpt::SomeFeatures(features.clone()));
+ }
+ }
+ }
+ command.current_dir(current_dir);
+
+ let mut needs_nightly = false;
+ let mut other_options = vec![];
+ // cargo metadata only supports a subset of flags of what cargo usually accepts, and usually
+ // the only relevant flags for metadata here are unstable ones, so we pass those along
+ // but nothing else
+ let mut extra_args = config.extra_args.iter();
+ while let Some(arg) = extra_args.next() {
+ if arg == "-Z" {
+ if let Some(arg) = extra_args.next() {
+ needs_nightly = true;
+ other_options.push("-Z".to_owned());
+ other_options.push(arg.to_owned());
+ }
+ }
+ }
+
+ let mut lockfile_path = None;
+ if cargo_toml.is_rust_manifest() {
+ needs_nightly = true;
+ other_options.push("-Zscript".to_owned());
+ } else if config
+ .toolchain_version
+ .as_ref()
+ .is_some_and(|v| *v >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH)
+ {
+ lockfile_path = Some(<_ as AsRef<Utf8Path>>::as_ref(cargo_toml).with_extension("lock"));
+ }
+
+ if !config.targets.is_empty() {
+ other_options.extend(
+ config.targets.iter().flat_map(|it| ["--filter-platform".to_owned(), it.clone()]),
+ );
+ }
+
+ command.other_options(other_options.clone());
+
+ if needs_nightly {
+ command.env("RUSTC_BOOTSTRAP", "1");
+ }
+
+ // Pre-fetch basic metadata using `--no-deps`, which:
+ // - avoids fetching registries like crates.io,
+ // - skips dependency resolution and does not modify lockfiles,
+ // - and thus doesn't require progress reporting or copying lockfiles.
+ //
+ // Useful as a fast fallback to extract info like `target-dir`.
+ let cargo_command;
+ let no_deps_result = if no_deps {
+ command.no_deps();
+ cargo_command = command.cargo_command();
+ command.exec()
+ } else {
+ let mut no_deps_command = command.clone();
+ no_deps_command.no_deps();
+ cargo_command = no_deps_command.cargo_command();
+ no_deps_command.exec()
+ }
+ .with_context(|| format!("Failed to run `{cargo_command:?}`"));
+
+ Self { command, lockfile_path, kind: config.kind, no_deps, no_deps_result, other_options }
+ }
+
+ pub(crate) fn no_deps_metadata(&self) -> Option<&cargo_metadata::Metadata> {
+ self.no_deps_result.as_ref().ok()
+ }
+
+ /// Executes the metadata-fetching command.
+ ///
+ /// A successful result may still contain a metadata error if the full fetch failed,
+ /// but the fallback `--no-deps` pre-fetch succeeded during command construction.
+ pub(crate) fn exec(
+ self,
+ target_dir: &Utf8Path,
+ locked: bool,
+ progress: &dyn Fn(String),
+ ) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
+ let Self { mut command, lockfile_path, kind, no_deps, no_deps_result, mut other_options } =
+ self;
+
+ if no_deps {
+ return no_deps_result.map(|m| (m, None));
+ }
+
+ let mut using_lockfile_copy = false;
+ // The manifest is a rust file, so this means its a script manifest
+ if let Some(lockfile) = lockfile_path {
+ let target_lockfile =
+ target_dir.join("rust-analyzer").join("metadata").join(kind).join("Cargo.lock");
+ match std::fs::copy(&lockfile, &target_lockfile) {
+ Ok(_) => {
+ using_lockfile_copy = true;
+ other_options.push("--lockfile-path".to_owned());
+ other_options.push(target_lockfile.to_string());
+ }
+ Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
+ // There exists no lockfile yet
+ using_lockfile_copy = true;
+ other_options.push("--lockfile-path".to_owned());
+ other_options.push(target_lockfile.to_string());
+ }
+ Err(e) => {
+ tracing::warn!(
+ "Failed to copy lock file from `{lockfile}` to `{target_lockfile}`: {e}",
+ );
+ }
+ }
+ }
+ if using_lockfile_copy {
+ other_options.push("-Zunstable-options".to_owned());
+ command.env("RUSTC_BOOTSTRAP", "1");
+ }
+ // No need to lock it if we copied the lockfile, we won't modify the original after all/
+ // This way cargo cannot error out on us if the lockfile requires updating.
+ if !using_lockfile_copy && locked {
+ other_options.push("--locked".to_owned());
+ }
+ command.other_options(other_options);
+
+ // FIXME: Fetching metadata is a slow process, as it might require
+ // calling crates.io. We should be reporting progress here, but it's
+ // unclear whether cargo itself supports it.
+ progress("cargo metadata: started".to_owned());
+
+ let res = (|| -> anyhow::Result<(_, _)> {
+ let mut errored = false;
+ let output =
+ spawn_with_streaming_output(command.cargo_command(), &mut |_| (), &mut |line| {
+ errored = errored || line.starts_with("error") || line.starts_with("warning");
+ if errored {
+ progress("cargo metadata: ?".to_owned());
+ return;
+ }
+ progress(format!("cargo metadata: {line}"));
+ })?;
+ if !output.status.success() {
+ progress(format!("cargo metadata: failed {}", output.status));
+ let error = cargo_metadata::Error::CargoMetadata {
+ stderr: String::from_utf8(output.stderr)?,
+ }
+ .into();
+ if !no_deps {
+ // If we failed to fetch metadata with deps, return pre-fetched result without them.
+ // This makes r-a still work partially when offline.
+ if let Ok(metadata) = no_deps_result {
+ tracing::warn!(
+ ?error,
+ "`cargo metadata` failed and returning succeeded result with `--no-deps`"
+ );
+ return Ok((metadata, Some(error)));
+ }
+ }
+ return Err(error);
+ }
+ let stdout = from_utf8(&output.stdout)?
+ .lines()
+ .find(|line| line.starts_with('{'))
+ .ok_or(cargo_metadata::Error::NoJson)?;
+ Ok((cargo_metadata::MetadataCommand::parse(stdout)?, None))
+ })()
+ .with_context(|| format!("Failed to run `{:?}`", command.cargo_command()));
+ progress("cargo metadata: finished".to_owned());
+ res
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/env.rs b/src/tools/rust-analyzer/crates/project-model/src/env.rs
index 9e0415c..d281492 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/env.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/env.rs
@@ -1,10 +1,9 @@
//! Cargo-like environment variables injection.
use base_db::Env;
-use paths::{Utf8Path, Utf8PathBuf};
-use rustc_hash::FxHashMap;
+use paths::Utf8Path;
use toolchain::Tool;
-use crate::{ManifestPath, PackageData, Sysroot, TargetKind, utf8_stdout};
+use crate::{ManifestPath, PackageData, TargetKind, cargo_config_file::CargoConfigFile};
/// Recreates the compile-time environment variables that Cargo sets.
///
@@ -61,104 +60,68 @@ pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: Targe
env.set("CARGO_CRATE_NAME", cargo_name.replace('-', "_"));
}
-pub(crate) fn cargo_config_env(
- manifest: &ManifestPath,
- extra_env: &FxHashMap<String, Option<String>>,
- sysroot: &Sysroot,
-) -> Env {
- let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
- cargo_config
- .args(["-Z", "unstable-options", "config", "get", "env"])
- .env("RUSTC_BOOTSTRAP", "1");
- if manifest.is_rust_manifest() {
- cargo_config.arg("-Zscript");
- }
- // if successful we receive `env.key.value = "value" per entry
- tracing::debug!("Discovering cargo config env by {:?}", cargo_config);
- utf8_stdout(&mut cargo_config)
- .map(|stdout| parse_output_cargo_config_env(manifest, &stdout))
- .inspect(|env| {
- tracing::debug!("Discovered cargo config env: {:?}", env);
- })
- .inspect_err(|err| {
- tracing::debug!("Failed to discover cargo config env: {:?}", err);
- })
- .unwrap_or_default()
-}
-
-fn parse_output_cargo_config_env(manifest: &ManifestPath, stdout: &str) -> Env {
+pub(crate) fn cargo_config_env(manifest: &ManifestPath, config: &Option<CargoConfigFile>) -> Env {
let mut env = Env::default();
- let mut relatives = vec![];
- for (key, val) in
- stdout.lines().filter_map(|l| l.strip_prefix("env.")).filter_map(|l| l.split_once(" = "))
- {
- let val = val.trim_matches('"').to_owned();
- if let Some((key, modifier)) = key.split_once('.') {
- match modifier {
- "relative" => relatives.push((key, val)),
- "value" => _ = env.insert(key, val),
- _ => {
- tracing::warn!(
- "Unknown modifier in cargo config env: {}, expected `relative` or `value`",
- modifier
- );
- continue;
- }
- }
- } else {
- env.insert(key, val);
- }
- }
+ let Some(serde_json::Value::Object(env_json)) = config.as_ref().and_then(|c| c.get("env"))
+ else {
+ return env;
+ };
+
// FIXME: The base here should be the parent of the `.cargo/config` file, not the manifest.
// But cargo does not provide this information.
let base = <_ as AsRef<Utf8Path>>::as_ref(manifest.parent());
- for (key, relative) in relatives {
- if relative != "true" {
- continue;
- }
- if let Some(suffix) = env.get(key) {
- env.insert(key, base.join(suffix).to_string());
- }
- }
- env
-}
-pub(crate) fn cargo_config_build_target_dir(
- manifest: &ManifestPath,
- extra_env: &FxHashMap<String, Option<String>>,
- sysroot: &Sysroot,
-) -> Option<Utf8PathBuf> {
- let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
- cargo_config
- .args(["-Z", "unstable-options", "config", "get", "build.target-dir"])
- .env("RUSTC_BOOTSTRAP", "1");
- if manifest.is_rust_manifest() {
- cargo_config.arg("-Zscript");
+ for (key, entry) in env_json {
+ let serde_json::Value::Object(entry) = entry else {
+ continue;
+ };
+ let Some(value) = entry.get("value").and_then(|v| v.as_str()) else {
+ continue;
+ };
+
+ let value = if entry
+ .get("relative")
+ .and_then(|v| v.as_bool())
+ .is_some_and(std::convert::identity)
+ {
+ base.join(value).to_string()
+ } else {
+ value.to_owned()
+ };
+ env.insert(key, value);
}
- utf8_stdout(&mut cargo_config)
- .map(|stdout| {
- Utf8Path::new(stdout.trim_start_matches("build.target-dir = ").trim_matches('"'))
- .to_owned()
- })
- .ok()
+
+ env
}
#[test]
fn parse_output_cargo_config_env_works() {
- let stdout = r#"
-env.CARGO_WORKSPACE_DIR.relative = true
-env.CARGO_WORKSPACE_DIR.value = ""
-env.RELATIVE.relative = true
-env.RELATIVE.value = "../relative"
-env.INVALID.relative = invalidbool
-env.INVALID.value = "../relative"
-env.TEST.value = "test"
-"#
- .trim();
+ let raw = r#"
+{
+ "env": {
+ "CARGO_WORKSPACE_DIR": {
+ "relative": true,
+ "value": ""
+ },
+ "INVALID": {
+ "relative": "invalidbool",
+ "value": "../relative"
+ },
+ "RELATIVE": {
+ "relative": true,
+ "value": "../relative"
+ },
+ "TEST": {
+ "value": "test"
+ }
+ }
+}
+"#;
+ let config: CargoConfigFile = serde_json::from_str(raw).unwrap();
let cwd = paths::Utf8PathBuf::try_from(std::env::current_dir().unwrap()).unwrap();
let manifest = paths::AbsPathBuf::assert(cwd.join("Cargo.toml"));
let manifest = ManifestPath::try_from(manifest).unwrap();
- let env = parse_output_cargo_config_env(&manifest, stdout);
+ let env = cargo_config_env(&manifest, &Some(config));
assert_eq!(env.get("CARGO_WORKSPACE_DIR").as_deref(), Some(cwd.join("").as_str()));
assert_eq!(env.get("RELATIVE").as_deref(), Some(cwd.join("../relative").as_str()));
assert_eq!(env.get("INVALID").as_deref(), Some("../relative"));
diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
index 436af64..3bf3d06 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
@@ -24,7 +24,7 @@ pub mod toolchain_info {
use std::path::Path;
- use crate::{ManifestPath, Sysroot};
+ use crate::{ManifestPath, Sysroot, cargo_config_file::CargoConfigFile};
#[derive(Copy, Clone)]
pub enum QueryConfig<'a> {
@@ -32,11 +32,12 @@ pub enum QueryConfig<'a> {
Rustc(&'a Sysroot, &'a Path),
/// Attempt to use cargo to query the desired information, honoring cargo configurations.
/// If this fails, falls back to invoking `rustc` directly.
- Cargo(&'a Sysroot, &'a ManifestPath),
+ Cargo(&'a Sysroot, &'a ManifestPath, &'a Option<CargoConfigFile>),
}
}
mod build_dependencies;
+mod cargo_config_file;
mod cargo_workspace;
mod env;
mod manifest_path;
diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
index 9f19260..9781c46 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
@@ -9,14 +9,15 @@
use anyhow::{Result, format_err};
use itertools::Itertools;
-use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
+use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf};
use rustc_hash::FxHashMap;
use stdx::format_to;
use toolchain::{Tool, probe_for_binary};
use crate::{
CargoWorkspace, ManifestPath, ProjectJson, RustSourceWorkspaceConfig,
- cargo_workspace::CargoMetadataConfig, utf8_stdout,
+ cargo_workspace::{CargoMetadataConfig, FetchMetadata},
+ utf8_stdout,
};
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -211,6 +212,7 @@ pub fn load_workspace(
sysroot_source_config: &RustSourceWorkspaceConfig,
no_deps: bool,
current_dir: &AbsPath,
+ target_dir: &Utf8Path,
progress: &dyn Fn(String),
) -> Option<RustLibSrcWorkspace> {
assert!(matches!(self.workspace, RustLibSrcWorkspace::Empty), "workspace already loaded");
@@ -224,6 +226,7 @@ pub fn load_workspace(
match self.load_library_via_cargo(
&library_manifest,
current_dir,
+ target_dir,
cargo_config,
no_deps,
progress,
@@ -319,6 +322,7 @@ fn load_library_via_cargo(
&self,
library_manifest: &ManifestPath,
current_dir: &AbsPath,
+ target_dir: &Utf8Path,
cargo_config: &CargoMetadataConfig,
no_deps: bool,
progress: &dyn Fn(String),
@@ -331,16 +335,11 @@ fn load_library_via_cargo(
Some("nightly".to_owned()),
);
- let (mut res, _) = CargoWorkspace::fetch_metadata(
- library_manifest,
- current_dir,
- &cargo_config,
- self,
- no_deps,
- // Make sure we never attempt to write to the sysroot
- true,
- progress,
- )?;
+ // Make sure we never attempt to write to the sysroot
+ let locked = true;
+ let (mut res, _) =
+ FetchMetadata::new(library_manifest, current_dir, &cargo_config, self, no_deps)
+ .exec(target_dir, locked, progress)?;
// Patch out `rustc-std-workspace-*` crates to point to the real crates.
// This is done prior to `CrateGraph` construction to prevent de-duplication logic from failing.
diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
index f229e9a..ed72520 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
@@ -239,8 +239,13 @@ fn smoke_test_real_sysroot_cargo() {
);
let cwd = AbsPathBuf::assert_utf8(temp_dir().join("smoke_test_real_sysroot_cargo"));
std::fs::create_dir_all(&cwd).unwrap();
- let loaded_sysroot =
- sysroot.load_workspace(&RustSourceWorkspaceConfig::default_cargo(), false, &cwd, &|_| ());
+ let loaded_sysroot = sysroot.load_workspace(
+ &RustSourceWorkspaceConfig::default_cargo(),
+ false,
+ &cwd,
+ &Utf8PathBuf::default(),
+ &|_| (),
+ );
if let Some(loaded_sysroot) = loaded_sysroot {
sysroot.set_workspace(loaded_sysroot);
}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs
index a77f767..6e06e88 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs
@@ -63,7 +63,7 @@ fn rustc_print_cfg(
) -> anyhow::Result<String> {
const RUSTC_ARGS: [&str; 2] = ["--print", "cfg"];
let (sysroot, current_dir) = match config {
- QueryConfig::Cargo(sysroot, cargo_toml) => {
+ QueryConfig::Cargo(sysroot, cargo_toml, _) => {
let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS);
if let Some(target) = target {
@@ -109,7 +109,7 @@ fn cargo() {
let sysroot = Sysroot::empty();
let manifest_path =
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
- let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
+ let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
assert_ne!(get(cfg, None, &FxHashMap::default()), vec![]);
}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs
index a4d0ec6..a28f468 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs
@@ -20,7 +20,7 @@ pub fn get(
})
};
let (sysroot, current_dir) = match config {
- QueryConfig::Cargo(sysroot, cargo_toml) => {
+ QueryConfig::Cargo(sysroot, cargo_toml, _) => {
let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
cmd.env("RUSTC_BOOTSTRAP", "1");
cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS).args([
@@ -66,7 +66,7 @@ fn cargo() {
let sysroot = Sysroot::empty();
let manifest_path =
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
- let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
+ let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
assert!(get(cfg, None, &FxHashMap::default()).is_ok());
}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_tuple.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_tuple.rs
index f6ab853..9f12ede 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_tuple.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_tuple.rs
@@ -5,7 +5,9 @@
use rustc_hash::FxHashMap;
use toolchain::Tool;
-use crate::{ManifestPath, Sysroot, toolchain_info::QueryConfig, utf8_stdout};
+use crate::{
+ Sysroot, cargo_config_file::CargoConfigFile, toolchain_info::QueryConfig, utf8_stdout,
+};
/// For cargo, runs `cargo -Zunstable-options config get build.target` to get the configured project target(s).
/// For rustc, runs `rustc --print -vV` to get the host target.
@@ -20,8 +22,8 @@ pub fn get(
}
let (sysroot, current_dir) = match config {
- QueryConfig::Cargo(sysroot, cargo_toml) => {
- match cargo_config_build_target(cargo_toml, extra_env, sysroot) {
+ QueryConfig::Cargo(sysroot, cargo_toml, config_file) => {
+ match config_file.as_ref().and_then(cargo_config_build_target) {
Some(it) => return Ok(it),
None => (sysroot, cargo_toml.parent().as_ref()),
}
@@ -50,30 +52,30 @@ fn rustc_discover_host_tuple(
}
}
-fn cargo_config_build_target(
- cargo_toml: &ManifestPath,
- extra_env: &FxHashMap<String, Option<String>>,
- sysroot: &Sysroot,
-) -> Option<Vec<String>> {
- let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
- cmd.current_dir(cargo_toml.parent()).env("RUSTC_BOOTSTRAP", "1");
- cmd.args(["-Z", "unstable-options", "config", "get", "build.target"]);
- // if successful we receive `build.target = "target-tuple"`
- // or `build.target = ["<target 1>", ..]`
- // this might be `error: config value `build.target` is not set` in which case we
- // don't wanna log the error
- utf8_stdout(&mut cmd).and_then(parse_output_cargo_config_build_target).ok()
+fn cargo_config_build_target(config: &CargoConfigFile) -> Option<Vec<String>> {
+ match parse_json_cargo_config_build_target(config) {
+ Ok(v) => v,
+ Err(e) => {
+ tracing::debug!("Failed to discover cargo config build target {e:?}");
+ None
+ }
+ }
}
// Parses `"build.target = [target-tuple, target-tuple, ...]"` or `"build.target = "target-tuple"`
-fn parse_output_cargo_config_build_target(stdout: String) -> anyhow::Result<Vec<String>> {
- let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"');
-
- if !trimmed.starts_with('[') {
- return Ok([trimmed.to_owned()].to_vec());
+fn parse_json_cargo_config_build_target(
+ config: &CargoConfigFile,
+) -> anyhow::Result<Option<Vec<String>>> {
+ let target = config.get("build").and_then(|v| v.as_object()).and_then(|m| m.get("target"));
+ match target {
+ Some(serde_json::Value::String(s)) => Ok(Some(vec![s.to_owned()])),
+ Some(v) => serde_json::from_value(v.clone())
+ .map(Option::Some)
+ .context("Failed to parse `build.target` as an array of target"),
+ // t`error: config value `build.target` is not set`, in which case we
+ // don't wanna log the error
+ None => Ok(None),
}
-
- serde_json::from_str(trimmed).context("Failed to parse `build.target` as an array of target")
}
#[cfg(test)]
@@ -90,7 +92,7 @@ fn cargo() {
let sysroot = Sysroot::empty();
let manifest_path =
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
- let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
+ let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
assert!(get(cfg, None, &FxHashMap::default()).is_ok());
}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/version.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/version.rs
index 91ba859..357053d 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/version.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/version.rs
@@ -12,7 +12,7 @@ pub(crate) fn get(
extra_env: &FxHashMap<String, Option<String>>,
) -> Result<Option<Version>, anyhow::Error> {
let (mut cmd, prefix) = match config {
- QueryConfig::Cargo(sysroot, cargo_toml) => {
+ QueryConfig::Cargo(sysroot, cargo_toml, _) => {
(sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env), "cargo ")
}
QueryConfig::Rustc(sysroot, current_dir) => {
@@ -44,7 +44,7 @@ fn cargo() {
let sysroot = Sysroot::empty();
let manifest_path =
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
- let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
+ let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
assert!(get(cfg, &FxHashMap::default()).is_ok());
}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index 43db84b..677f29e 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -25,11 +25,9 @@
ProjectJson, ProjectManifest, RustSourceWorkspaceConfig, Sysroot, TargetData, TargetKind,
WorkspaceBuildScripts,
build_dependencies::BuildScriptOutput,
- cargo_workspace::{CargoMetadataConfig, DepKind, PackageData, RustLibSource},
- env::{
- cargo_config_build_target_dir, cargo_config_env, inject_cargo_env,
- inject_cargo_package_env, inject_rustc_tool_env,
- },
+ cargo_config_file,
+ cargo_workspace::{CargoMetadataConfig, DepKind, FetchMetadata, PackageData, RustLibSource},
+ env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env},
project_json::{Crate, CrateArrayIdx},
sysroot::RustLibSrcWorkspace,
toolchain_info::{QueryConfig, rustc_cfg, target_data_layout, target_tuple, version},
@@ -270,7 +268,9 @@ struct Root {
tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.rust_lib_src_root(), root = ?sysroot.root(), "Using sysroot");
progress("querying project metadata".to_owned());
- let toolchain_config = QueryConfig::Cargo(&sysroot, cargo_toml);
+ let config_file = cargo_config_file::read(cargo_toml, extra_env, &sysroot);
+ let config_file_ = config_file.clone();
+ let toolchain_config = QueryConfig::Cargo(&sysroot, cargo_toml, &config_file_);
let targets =
target_tuple::get(toolchain_config, target.as_deref(), extra_env).unwrap_or_default();
let toolchain = version::get(toolchain_config, extra_env)
@@ -282,10 +282,24 @@ struct Root {
.ok()
.flatten();
+ let fetch_metadata = FetchMetadata::new(
+ cargo_toml,
+ workspace_dir,
+ &CargoMetadataConfig {
+ features: features.clone(),
+ targets: targets.clone(),
+ extra_args: extra_args.clone(),
+ extra_env: extra_env.clone(),
+ toolchain_version: toolchain.clone(),
+ kind: "workspace",
+ },
+ &sysroot,
+ *no_deps,
+ );
let target_dir = config
.target_dir
.clone()
- .or_else(|| cargo_config_build_target_dir(cargo_toml, extra_env, &sysroot))
+ .or_else(|| fetch_metadata.no_deps_metadata().map(|m| m.target_directory.clone()))
.unwrap_or_else(|| workspace_dir.join("target").into());
// We spawn a bunch of processes to query various information about the workspace's
@@ -319,7 +333,7 @@ struct Root {
};
rustc_dir.and_then(|rustc_dir| {
info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
- match CargoWorkspace::fetch_metadata(
+ match FetchMetadata::new(
&rustc_dir,
workspace_dir,
&CargoMetadataConfig {
@@ -327,15 +341,12 @@ struct Root {
targets: targets.clone(),
extra_args: extra_args.clone(),
extra_env: extra_env.clone(),
- target_dir: target_dir.clone(),
toolchain_version: toolchain.clone(),
kind: "rustc-dev"
},
&sysroot,
*no_deps,
- true,
- progress,
- ) {
+ ).exec(&target_dir, true, progress) {
Ok((meta, _error)) => {
let workspace = CargoWorkspace::new(
meta,
@@ -364,40 +375,22 @@ struct Root {
})
});
- let cargo_metadata = s.spawn(|| {
- CargoWorkspace::fetch_metadata(
- cargo_toml,
- workspace_dir,
- &CargoMetadataConfig {
- features: features.clone(),
- targets: targets.clone(),
- extra_args: extra_args.clone(),
- extra_env: extra_env.clone(),
- target_dir: target_dir.clone(),
- toolchain_version: toolchain.clone(),
- kind: "workspace",
- },
- &sysroot,
- *no_deps,
- false,
- progress,
- )
- });
+ let cargo_metadata = s.spawn(|| fetch_metadata.exec(&target_dir, false, progress));
let loaded_sysroot = s.spawn(|| {
sysroot.load_workspace(
&RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
config,
&targets,
toolchain.clone(),
- target_dir.clone(),
)),
config.no_deps,
workspace_dir,
+ &target_dir,
progress,
)
});
let cargo_config_extra_env =
- s.spawn(|| cargo_config_env(cargo_toml, extra_env, &sysroot));
+ s.spawn(move || cargo_config_env(cargo_toml, &config_file));
thread::Result::Ok((
rustc_cfg.join()?,
data_layout.join()?,
@@ -476,9 +469,7 @@ pub fn load_inline(
let target_dir = config
.target_dir
.clone()
- .or_else(|| {
- cargo_config_build_target_dir(project_json.manifest()?, &config.extra_env, &sysroot)
- })
+ .or_else(|| cargo_target_dir(project_json.manifest()?, &config.extra_env, &sysroot))
.unwrap_or_else(|| project_root.join("target").into());
// We spawn a bunch of processes to query various information about the workspace's
@@ -502,6 +493,7 @@ pub fn load_inline(
&RustSourceWorkspaceConfig::Json(*sysroot_project),
config.no_deps,
project_root,
+ &target_dir,
progress,
)
} else {
@@ -510,10 +502,10 @@ pub fn load_inline(
config,
&targets,
toolchain.clone(),
- target_dir,
)),
config.no_deps,
project_root,
+ &target_dir,
progress,
)
}
@@ -554,7 +546,8 @@ pub fn load_detached_file(
None => Sysroot::empty(),
};
- let query_config = QueryConfig::Cargo(&sysroot, detached_file);
+ let config_file = cargo_config_file::read(detached_file, &config.extra_env, &sysroot);
+ let query_config = QueryConfig::Cargo(&sysroot, detached_file, &config_file);
let toolchain = version::get(query_config, &config.extra_env).ok().flatten();
let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
.unwrap_or_default();
@@ -563,7 +556,7 @@ pub fn load_detached_file(
let target_dir = config
.target_dir
.clone()
- .or_else(|| cargo_config_build_target_dir(detached_file, &config.extra_env, &sysroot))
+ .or_else(|| cargo_target_dir(detached_file, &config.extra_env, &sysroot))
.unwrap_or_else(|| dir.join("target").into());
let loaded_sysroot = sysroot.load_workspace(
@@ -571,17 +564,17 @@ pub fn load_detached_file(
config,
&targets,
toolchain.clone(),
- target_dir.clone(),
)),
config.no_deps,
dir,
+ &target_dir,
&|_| (),
);
if let Some(loaded_sysroot) = loaded_sysroot {
sysroot.set_workspace(loaded_sysroot);
}
- let cargo_script = CargoWorkspace::fetch_metadata(
+ let fetch_metadata = FetchMetadata::new(
detached_file,
dir,
&CargoMetadataConfig {
@@ -589,25 +582,26 @@ pub fn load_detached_file(
targets,
extra_args: config.extra_args.clone(),
extra_env: config.extra_env.clone(),
- target_dir,
toolchain_version: toolchain.clone(),
kind: "detached-file",
},
&sysroot,
config.no_deps,
- false,
- &|_| (),
- )
- .ok()
- .map(|(ws, error)| {
- let cargo_config_extra_env =
- cargo_config_env(detached_file, &config.extra_env, &sysroot);
- (
- CargoWorkspace::new(ws, detached_file.clone(), cargo_config_extra_env, false),
- WorkspaceBuildScripts::default(),
- error.map(Arc::new),
- )
- });
+ );
+ let target_dir = config
+ .target_dir
+ .clone()
+ .or_else(|| fetch_metadata.no_deps_metadata().map(|m| m.target_directory.clone()))
+ .unwrap_or_else(|| dir.join("target").into());
+ let cargo_script =
+ fetch_metadata.exec(&target_dir, false, &|_| ()).ok().map(|(ws, error)| {
+ let cargo_config_extra_env = cargo_config_env(detached_file, &config_file);
+ (
+ CargoWorkspace::new(ws, detached_file.clone(), cargo_config_extra_env, false),
+ WorkspaceBuildScripts::default(),
+ error.map(Arc::new),
+ )
+ });
Ok(ProjectWorkspace {
kind: ProjectWorkspaceKind::DetachedFile {
@@ -1889,15 +1883,33 @@ fn sysroot_metadata_config(
config: &CargoConfig,
targets: &[String],
toolchain_version: Option<Version>,
- target_dir: Utf8PathBuf,
) -> CargoMetadataConfig {
CargoMetadataConfig {
features: Default::default(),
targets: targets.to_vec(),
extra_args: Default::default(),
extra_env: config.extra_env.clone(),
- target_dir,
toolchain_version,
kind: "sysroot",
}
}
+
+fn cargo_target_dir(
+ manifest: &ManifestPath,
+ extra_env: &FxHashMap<String, Option<String>>,
+ sysroot: &Sysroot,
+) -> Option<Utf8PathBuf> {
+ let cargo = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
+ let mut meta = cargo_metadata::MetadataCommand::new();
+ meta.cargo_path(cargo.get_program());
+ meta.manifest_path(manifest);
+ // `--no-deps` doesn't (over)write lockfiles as it doesn't do any package resolve.
+ // So we can use it to get `target_directory` before copying lockfiles
+ let mut other_options = vec!["--no-deps".to_owned()];
+ if manifest.is_rust_manifest() {
+ meta.env("RUSTC_BOOTSTRAP", "1");
+ other_options.push("-Zscript".to_owned());
+ }
+ meta.other_options(other_options);
+ meta.exec().map(|m| m.target_directory).ok()
+}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index 0ee0198..fc89f48 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -796,7 +796,7 @@ fn run_inference(
// region:expressions
let (previous_exprs, previous_unknown, previous_partially_unknown) =
(num_exprs, num_exprs_unknown, num_exprs_partially_unknown);
- for (expr_id, _) in body.exprs.iter() {
+ for (expr_id, _) in body.exprs() {
let ty = &inference_result[expr_id];
num_exprs += 1;
let unknown_or_partial = if ty.is_unknown() {
@@ -901,7 +901,7 @@ fn run_inference(
// region:patterns
let (previous_pats, previous_unknown, previous_partially_unknown) =
(num_pats, num_pats_unknown, num_pats_partially_unknown);
- for (pat_id, _) in body.pats.iter() {
+ for (pat_id, _) in body.pats() {
let ty = &inference_result[pat_id];
num_pats += 1;
let unknown_or_partial = if ty.is_unknown() {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
index f97bf83..30ac93f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -9,6 +9,7 @@
use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig};
use ide_db::base_db;
use itertools::Either;
+use paths::Utf8PathBuf;
use profile::StopWatch;
use project_model::toolchain_info::{QueryConfig, target_data_layout};
use project_model::{
@@ -79,6 +80,7 @@ fn new() -> Result<Self> {
&RustSourceWorkspaceConfig::default_cargo(),
false,
&path,
+ &Utf8PathBuf::default(),
&|_| (),
);
if let Some(loaded_sysroot) = loaded_sysroot {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
index 8a848fb..292be1d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -911,7 +911,8 @@ pub(crate) fn folding_range(
| FoldKind::Array
| FoldKind::TraitAliases
| FoldKind::ExternCrates
- | FoldKind::MatchArm => None,
+ | FoldKind::MatchArm
+ | FoldKind::Function => None,
};
let range = range(line_index, fold.range);
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
index 59073af..1b940c7 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -880,7 +880,8 @@ fn main() {{}}
#[test]
fn diagnostics_dont_block_typing() {
- if skip_slow_tests() {
+ if skip_slow_tests() || std::env::var("CI").is_ok() {
+ // FIXME: This test is failing too frequently (therefore we disable it on CI).
return;
}
diff --git a/src/tools/rust-analyzer/crates/span/src/ast_id.rs b/src/tools/rust-analyzer/crates/span/src/ast_id.rs
index 121d2e3..a9288ec 100644
--- a/src/tools/rust-analyzer/crates/span/src/ast_id.rs
+++ b/src/tools/rust-analyzer/crates/span/src/ast_id.rs
@@ -92,6 +92,7 @@ macro_rules! kind {
Use,
Impl,
BlockExpr,
+ AsmExpr,
Fixup,
);
if f.alternate() {
@@ -144,6 +145,10 @@ enum ErasedFileAstIdKind {
Impl,
/// Associated with [`BlockExprFileAstId`].
BlockExpr,
+ // `global_asm!()` is an item, so we need to give it an `AstId`. So we give to all inline asm
+ // because incrementality is not a problem, they will always be the only item in the macro file,
+ // and memory usage also not because they're rare.
+ AsmExpr,
/// Keep this last.
Root,
}
@@ -204,14 +209,17 @@ fn ast_id_for(
.or_else(|| extern_block_ast_id(node, index_map))
.or_else(|| use_ast_id(node, index_map))
.or_else(|| impl_ast_id(node, index_map))
+ .or_else(|| asm_expr_ast_id(node, index_map))
}
fn should_alloc(node: &SyntaxNode) -> bool {
- should_alloc_has_name(node)
- || should_alloc_assoc_item(node)
- || ast::ExternBlock::can_cast(node.kind())
- || ast::Use::can_cast(node.kind())
- || ast::Impl::can_cast(node.kind())
+ let kind = node.kind();
+ should_alloc_has_name(kind)
+ || should_alloc_assoc_item(kind)
+ || ast::ExternBlock::can_cast(kind)
+ || ast::Use::can_cast(kind)
+ || ast::Impl::can_cast(kind)
+ || ast::AsmExpr::can_cast(kind)
}
#[inline]
@@ -278,7 +286,6 @@ pub fn erase(self) -> ErasedFileAstId {
#[derive(Hash)]
struct ErasedHasNameFileAstId<'a> {
- kind: SyntaxKind,
name: &'a str,
}
@@ -332,6 +339,19 @@ fn use_ast_id(
}
}
+impl AstIdNode for ast::AsmExpr {}
+
+fn asm_expr_ast_id(
+ node: &SyntaxNode,
+ index_map: &mut ErasedAstIdNextIndexMap,
+) -> Option<ErasedFileAstId> {
+ if ast::AsmExpr::can_cast(node.kind()) {
+ Some(index_map.new_id(ErasedFileAstIdKind::AsmExpr, ()))
+ } else {
+ None
+ }
+}
+
impl AstIdNode for ast::Impl {}
fn impl_ast_id(
@@ -433,7 +453,6 @@ impl $AstIdNode for ast::$ident {}
)+
fn has_name_ast_id(node: &SyntaxNode, index_map: &mut ErasedAstIdNextIndexMap) -> Option<ErasedFileAstId> {
- let kind = node.kind();
match_ast! {
match node {
$(
@@ -441,7 +460,6 @@ fn has_name_ast_id(node: &SyntaxNode, index_map: &mut ErasedAstIdNextIndexMap) -
let name = node.$name_method();
let name = name.as_ref().map_or("", |it| it.text_non_mutable());
let result = ErasedHasNameFileAstId {
- kind,
name,
};
Some(index_map.new_id(ErasedFileAstIdKind::$ident, result))
@@ -452,8 +470,7 @@ fn has_name_ast_id(node: &SyntaxNode, index_map: &mut ErasedAstIdNextIndexMap) -
}
}
- fn should_alloc_has_name(node: &SyntaxNode) -> bool {
- let kind = node.kind();
+ fn should_alloc_has_name(kind: SyntaxKind) -> bool {
false $( || ast::$ident::can_cast(kind) )*
}
};
@@ -483,7 +500,6 @@ fn assoc_item_ast_id(
index_map: &mut ErasedAstIdNextIndexMap,
parent: Option<&ErasedFileAstId>,
) -> Option<ErasedFileAstId> {
- let kind = node.kind();
match_ast! {
match node {
$(
@@ -491,7 +507,6 @@ fn assoc_item_ast_id(
let name = $name_callback(node);
let name = name.as_ref().map_or("", |it| it.text_non_mutable());
let properties = ErasedHasNameFileAstId {
- kind,
name,
};
let result = ErasedAssocItemFileAstId {
@@ -506,8 +521,7 @@ fn assoc_item_ast_id(
}
}
- fn should_alloc_assoc_item(node: &SyntaxNode) -> bool {
- let kind = node.kind();
+ fn should_alloc_assoc_item(kind: SyntaxKind) -> bool {
false $( || ast::$ident::can_cast(kind) )*
}
};
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram
index 3f43947..4cbc88c 100644
--- a/src/tools/rust-analyzer/crates/syntax/rust.ungram
+++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram
@@ -158,6 +158,7 @@
| TypeAlias
| Union
| Use
+| AsmExpr
MacroRules =
Attr* Visibility?
@@ -409,7 +410,8 @@
// global_asm := "global_asm!(" format_string *("," format_string) *("," operand) [","] ")"
// format_string := STRING_LITERAL / RAW_STRING_LITERAL
AsmExpr =
- Attr* 'builtin' '#' 'asm' '(' template:(Expr (',' Expr)*) (AsmPiece (',' AsmPiece)*)? ','? ')'
+ Attr* 'builtin' '#' ( 'asm' | 'global_asm' | 'naked_asm' )
+ '(' template:(Expr (',' Expr)*) (AsmPiece (',' AsmPiece)*)? ','? ')'
// operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_"
AsmOperandExpr = in_expr:Expr ('=>' out_expr:Expr)?
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
index e60243f..e902516 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
@@ -406,42 +406,6 @@ pub fn remove_predicate(&self, predicate: ast::WherePred) {
}
}
-impl ast::TypeParam {
- pub fn remove_default(&self) {
- if let Some((eq, last)) = self
- .syntax()
- .children_with_tokens()
- .find(|it| it.kind() == T![=])
- .zip(self.syntax().last_child_or_token())
- {
- ted::remove_all(eq..=last);
-
- // remove any trailing ws
- if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) {
- last.detach();
- }
- }
- }
-}
-
-impl ast::ConstParam {
- pub fn remove_default(&self) {
- if let Some((eq, last)) = self
- .syntax()
- .children_with_tokens()
- .find(|it| it.kind() == T![=])
- .zip(self.syntax().last_child_or_token())
- {
- ted::remove_all(eq..=last);
-
- // remove any trailing ws
- if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) {
- last.detach();
- }
- }
- }
-}
-
pub trait Removable: AstNode {
fn remove(&self);
}
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
index 79a9f4d..2b86246 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
@@ -118,6 +118,14 @@ pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax,
pub fn asm_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![asm]) }
#[inline]
pub fn builtin_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![builtin]) }
+ #[inline]
+ pub fn global_asm_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![global_asm])
+ }
+ #[inline]
+ pub fn naked_asm_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![naked_asm])
+ }
}
pub struct AsmLabel {
pub(crate) syntax: SyntaxNode,
@@ -2087,6 +2095,7 @@ impl ast::HasAttrs for GenericParam {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Item {
+ AsmExpr(AsmExpr),
Const(Const),
Enum(Enum),
ExternBlock(ExternBlock),
@@ -2106,7 +2115,6 @@ pub enum Item {
Use(Use),
}
impl ast::HasAttrs for Item {}
-impl ast::HasDocComments for Item {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Pat {
@@ -8409,6 +8417,10 @@ fn syntax(&self) -> &SyntaxNode {
}
}
}
+impl From<AsmExpr> for Item {
+ #[inline]
+ fn from(node: AsmExpr) -> Item { Item::AsmExpr(node) }
+}
impl From<Const> for Item {
#[inline]
fn from(node: Const) -> Item { Item::Const(node) }
@@ -8482,7 +8494,8 @@ impl AstNode for Item {
fn can_cast(kind: SyntaxKind) -> bool {
matches!(
kind,
- CONST
+ ASM_EXPR
+ | CONST
| ENUM
| EXTERN_BLOCK
| EXTERN_CRATE
@@ -8504,6 +8517,7 @@ fn can_cast(kind: SyntaxKind) -> bool {
#[inline]
fn cast(syntax: SyntaxNode) -> Option<Self> {
let res = match syntax.kind() {
+ ASM_EXPR => Item::AsmExpr(AsmExpr { syntax }),
CONST => Item::Const(Const { syntax }),
ENUM => Item::Enum(Enum { syntax }),
EXTERN_BLOCK => Item::ExternBlock(ExternBlock { syntax }),
@@ -8528,6 +8542,7 @@ fn cast(syntax: SyntaxNode) -> Option<Self> {
#[inline]
fn syntax(&self) -> &SyntaxNode {
match self {
+ Item::AsmExpr(it) => &it.syntax,
Item::Const(it) => &it.syntax,
Item::Enum(it) => &it.syntax,
Item::ExternBlock(it) => &it.syntax,
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
index 3093328..d67f24f 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
@@ -680,7 +680,7 @@ pub fn expr_tuple(elements: impl IntoIterator<Item = ast::Expr>) -> ast::TupleEx
let expr = elements.into_iter().format(", ");
expr_from_text(&format!("({expr})"))
}
-pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr {
+pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::BinExpr {
expr_from_text(&format!("{lhs} = {rhs}"))
}
fn expr_from_text<E: Into<ast::Expr> + AstNode>(text: &str) -> E {
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs
index 17cc5f9..1ba6107 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs
@@ -440,6 +440,19 @@ pub fn expr_tuple(&self, fields: impl IntoIterator<Item = ast::Expr>) -> ast::Tu
ast
}
+ pub fn expr_assignment(&self, lhs: ast::Expr, rhs: ast::Expr) -> ast::BinExpr {
+ let ast = make::expr_assignment(lhs.clone(), rhs.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(lhs.syntax().clone(), ast.lhs().unwrap().syntax().clone());
+ builder.map_node(rhs.syntax().clone(), ast.rhs().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn expr_bin(&self, lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::BinExpr {
let ast::Expr::BinExpr(ast) =
make::expr_bin_op(lhs.clone(), op, rhs.clone()).clone_for_update()
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
index e5d8f78..7b719b5 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -1940,6 +1940,7 @@ pub mod v1 {
clone::Clone, // :clone
cmp::{Eq, PartialEq}, // :eq
cmp::{Ord, PartialOrd}, // :ord
+ convert::AsMut, // :as_mut
convert::AsRef, // :as_ref
convert::{From, Into, TryFrom, TryInto}, // :from
default::Default, // :default
diff --git a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml
index 35a5a4d..1fc1da5 100644
--- a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml
+++ b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml
@@ -16,6 +16,9 @@
[dev-dependencies]
lsp-types = "=0.95"
ctrlc = "3.4.7"
+anyhow.workspace = true
+rustc-hash.workspace = true
+toolchain.workspace = true
[lints]
workspace = true
diff --git a/src/tools/rust-analyzer/lib/lsp-server/examples/goto_def.rs b/src/tools/rust-analyzer/lib/lsp-server/examples/goto_def.rs
deleted file mode 100644
index 6b3acda..0000000
--- a/src/tools/rust-analyzer/lib/lsp-server/examples/goto_def.rs
+++ /dev/null
@@ -1,132 +0,0 @@
-//! A minimal example LSP server that can only respond to the `gotoDefinition` request. To use
-//! this example, execute it and then send an `initialize` request.
-//!
-//! ```no_run
-//! Content-Length: 85
-//!
-//! {"jsonrpc": "2.0", "method": "initialize", "id": 1, "params": {"capabilities": {}}}
-//! ```
-//!
-//! This will respond with a server response. Then send it a `initialized` notification which will
-//! have no response.
-//!
-//! ```no_run
-//! Content-Length: 59
-//!
-//! {"jsonrpc": "2.0", "method": "initialized", "params": {}}
-//! ```
-//!
-//! Once these two are sent, then we enter the main loop of the server. The only request this
-//! example can handle is `gotoDefinition`:
-//!
-//! ```no_run
-//! Content-Length: 159
-//!
-//! {"jsonrpc": "2.0", "method": "textDocument/definition", "id": 2, "params": {"textDocument": {"uri": "file://temp"}, "position": {"line": 1, "character": 1}}}
-//! ```
-//!
-//! To finish up without errors, send a shutdown request:
-//!
-//! ```no_run
-//! Content-Length: 67
-//!
-//! {"jsonrpc": "2.0", "method": "shutdown", "id": 3, "params": null}
-//! ```
-//!
-//! The server will exit the main loop and finally we send a `shutdown` notification to stop
-//! the server.
-//!
-//! ```
-//! Content-Length: 54
-//!
-//! {"jsonrpc": "2.0", "method": "exit", "params": null}
-//! ```
-
-#![allow(clippy::print_stderr)]
-
-use std::error::Error;
-
-use lsp_types::OneOf;
-use lsp_types::{
- GotoDefinitionResponse, InitializeParams, ServerCapabilities, request::GotoDefinition,
-};
-
-use lsp_server::{Connection, ExtractError, Message, Request, RequestId, Response};
-
-fn main() -> Result<(), Box<dyn Error + Sync + Send>> {
- // Note that we must have our logging only write out to stderr.
- eprintln!("starting generic LSP server");
-
- // Create the transport. Includes the stdio (stdin and stdout) versions but this could
- // also be implemented to use sockets or HTTP.
- let (connection, io_threads) = Connection::stdio();
-
- // Run the server and wait for the two threads to end (typically by trigger LSP Exit event).
- let server_capabilities = serde_json::to_value(&ServerCapabilities {
- definition_provider: Some(OneOf::Left(true)),
- ..Default::default()
- })
- .unwrap();
- let initialization_params = match connection.initialize(server_capabilities) {
- Ok(it) => it,
- Err(e) => {
- if e.channel_is_disconnected() {
- io_threads.join()?;
- }
- return Err(e.into());
- }
- };
- main_loop(connection, initialization_params)?;
- io_threads.join()?;
-
- // Shut down gracefully.
- eprintln!("shutting down server");
- Ok(())
-}
-
-fn main_loop(
- connection: Connection,
- params: serde_json::Value,
-) -> Result<(), Box<dyn Error + Sync + Send>> {
- let _params: InitializeParams = serde_json::from_value(params).unwrap();
- eprintln!("starting example main loop");
- for msg in &connection.receiver {
- eprintln!("got msg: {msg:?}");
- match msg {
- Message::Request(req) => {
- if connection.handle_shutdown(&req)? {
- return Ok(());
- }
- eprintln!("got request: {req:?}");
- match cast::<GotoDefinition>(req) {
- Ok((id, params)) => {
- eprintln!("got gotoDefinition request #{id}: {params:?}");
- let result = Some(GotoDefinitionResponse::Array(Vec::new()));
- let result = serde_json::to_value(&result).unwrap();
- let resp = Response { id, result: Some(result), error: None };
- connection.sender.send(Message::Response(resp))?;
- continue;
- }
- Err(err @ ExtractError::JsonError { .. }) => panic!("{err:?}"),
- Err(ExtractError::MethodMismatch(req)) => req,
- };
- // ...
- }
- Message::Response(resp) => {
- eprintln!("got response: {resp:?}");
- }
- Message::Notification(not) => {
- eprintln!("got notification: {not:?}");
- }
- }
- }
- Ok(())
-}
-
-fn cast<R>(req: Request) -> Result<(RequestId, R::Params), ExtractError<Request>>
-where
- R: lsp_types::request::Request,
- R::Params: serde::de::DeserializeOwned,
-{
- req.extract(R::METHOD)
-}
diff --git a/src/tools/rust-analyzer/lib/lsp-server/examples/manual_test.sh b/src/tools/rust-analyzer/lib/lsp-server/examples/manual_test.sh
new file mode 100755
index 0000000..d028ac4
--- /dev/null
+++ b/src/tools/rust-analyzer/lib/lsp-server/examples/manual_test.sh
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+# Simple nine-packet LSP test for examples/minimal_lsp.rs
+# Usage (two tabs):
+#
+# mkfifo /tmp/lsp_pipe # one-time setup
+# # tab 1 – run the server
+# cat /tmp/lsp_pipe | cargo run --example minimal_lsp
+#
+# # tab 2 – fire the packets (this script)
+# bash examples/manual_test.sh # blocks until server exits
+#
+# If you don’t use a second tab, run the script in the background:
+#
+# bash examples/manual_test.sh & # writer in background
+# cat /tmp/lsp_pipe | cargo run --example minimal_lsp
+#
+# The script opens /tmp/lsp_pipe for writing (exec 3>) and sends each JSON
+# packet with a correct Content-Length header.
+#
+# One-liner alternative (single terminal, no FIFO):
+#
+# cargo run --example minimal_lsp <<'EOF'
+# … nine packets …
+# EOF
+#
+# Both approaches feed identical bytes to minimal_lsp via stdin.
+
+set -eu
+PIPE=${1:-/tmp/lsp_pipe}
+
+mkfifo -m 600 "$PIPE" 2>/dev/null || true # create once, ignore if exists
+
+# open write end so the fifo stays open
+exec 3> "$PIPE"
+
+send() {
+ local body=$1
+ local len=$(printf '%s' "$body" | wc -c)
+ printf 'Content-Length: %d\r\n\r\n%s' "$len" "$body" >&3
+}
+
+send '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"capabilities":{}}}'
+send '{"jsonrpc":"2.0","method":"initialized","params":{}}'
+send '{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///tmp/foo.rs","languageId":"rust","version":1,"text":"fn main( ){println!(\"hi\") }"}}}'
+send '{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///tmp/foo.rs"},"position":{"line":0,"character":0}}}'
+send '{"jsonrpc":"2.0","id":3,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///tmp/foo.rs"},"position":{"line":0,"character":0}}}'
+send '{"jsonrpc":"2.0","id":4,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///tmp/foo.rs"},"position":{"line":0,"character":0}}}'
+send '{"jsonrpc":"2.0","id":5,"method":"textDocument/formatting","params":{"textDocument":{"uri":"file:///tmp/foo.rs"},"options":{"tabSize":4,"insertSpaces":true}}}'
+send '{"jsonrpc":"2.0","id":6,"method":"shutdown","params":null}'
+send '{"jsonrpc":"2.0","method":"exit","params":null}'
+
+exec 3>&-
+echo "Packets sent – watch the other terminal for responses."
diff --git a/src/tools/rust-analyzer/lib/lsp-server/examples/minimal_lsp.rs b/src/tools/rust-analyzer/lib/lsp-server/examples/minimal_lsp.rs
new file mode 100644
index 0000000..5eef999
--- /dev/null
+++ b/src/tools/rust-analyzer/lib/lsp-server/examples/minimal_lsp.rs
@@ -0,0 +1,335 @@
+//! Minimal Language‑Server‑Protocol example: **`minimal_lsp.rs`**
+//! =============================================================
+//!
+//! | ↔ / ← | LSP method | What the implementation does |
+//! |-------|------------|------------------------------|
+//! | ↔ | `initialize` / `initialized` | capability handshake |
+//! | ← | `textDocument/publishDiagnostics` | pushes a dummy info diagnostic whenever the buffer changes |
+//! | ← | `textDocument/definition` | echoes an empty location array so the jump works |
+//! | ← | `textDocument/completion` | offers one hard‑coded item `HelloFromLSP` |
+//! | ← | `textDocument/hover` | shows *Hello from minimal_lsp* markdown |
+//! | ← | `textDocument/formatting` | pipes the doc through **rustfmt** and returns a full‑file edit |
+//!
+//! ### Quick start
+//! ```bash
+//! cd rust-analyzer/lib/lsp-server
+//! cargo run --example minimal_lsp
+//! ```
+//!
+//! ### Minimal manual session (all nine packets)
+//! ```no_run
+//! # 1. initialize - server replies with capabilities
+//! Content-Length: 85
+
+//! {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"capabilities":{}}}
+//!
+//! # 2. initialized - no response expected
+//! Content-Length: 59
+
+//! {"jsonrpc":"2.0","method":"initialized","params":{}}
+//!
+//! # 3. didOpen - provide initial buffer text
+//! Content-Length: 173
+
+//! {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///tmp/foo.rs","languageId":"rust","version":1,"text":"fn main( ){println!(\"hi\") }"}}}
+//!
+//! # 4. completion - expect HelloFromLSP
+//! Content-Length: 139
+
+//! {"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///tmp/foo.rs"},"position":{"line":0,"character":0}}}
+//!
+//! # 5. hover - expect markdown greeting
+//! Content-Length: 135
+
+//! {"jsonrpc":"2.0","id":3,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///tmp/foo.rs"},"position":{"line":0,"character":0}}}
+//!
+//! # 6. goto-definition - dummy empty array
+//! Content-Length: 139
+
+//! {"jsonrpc":"2.0","id":4,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///tmp/foo.rs"},"position":{"line":0,"character":0}}}
+//!
+//! # 7. formatting - rustfmt full document
+//! Content-Length: 157
+
+//! {"jsonrpc":"2.0","id":5,"method":"textDocument/formatting","params":{"textDocument":{"uri":"file:///tmp/foo.rs"},"options":{"tabSize":4,"insertSpaces":true}}}
+//!
+//! # 8. shutdown request - server acks and prepares to exit
+//! Content-Length: 67
+
+//! {"jsonrpc":"2.0","id":6,"method":"shutdown","params":null}
+//!
+//! # 9. exit notification - terminates the server
+//! Content-Length: 54
+
+//! {"jsonrpc":"2.0","method":"exit","params":null}
+//! ```
+//!
+
+use std::{error::Error, io::Write};
+
+use rustc_hash::FxHashMap; // fast hash map
+use std::process::Stdio;
+use toolchain::command; // clippy-approved wrapper
+
+#[allow(clippy::print_stderr, clippy::disallowed_types, clippy::disallowed_methods)]
+use anyhow::{Context, Result, anyhow, bail};
+use lsp_server::{Connection, Message, Request as ServerRequest, RequestId, Response};
+use lsp_types::notification::Notification as _; // for METHOD consts
+use lsp_types::request::Request as _;
+use lsp_types::{
+ CompletionItem,
+ CompletionItemKind,
+ // capability helpers
+ CompletionOptions,
+ CompletionResponse,
+ Diagnostic,
+ DiagnosticSeverity,
+ DidChangeTextDocumentParams,
+ DidOpenTextDocumentParams,
+ DocumentFormattingParams,
+ Hover,
+ HoverContents,
+ HoverProviderCapability,
+ // core
+ InitializeParams,
+ MarkedString,
+ OneOf,
+ Position,
+ PublishDiagnosticsParams,
+ Range,
+ ServerCapabilities,
+ TextDocumentSyncCapability,
+ TextDocumentSyncKind,
+ TextEdit,
+ Url,
+ // notifications
+ notification::{DidChangeTextDocument, DidOpenTextDocument, PublishDiagnostics},
+ // requests
+ request::{Completion, Formatting, GotoDefinition, HoverRequest},
+}; // for METHOD consts
+
+// =====================================================================
+// main
+// =====================================================================
+
+#[allow(clippy::print_stderr)]
+fn main() -> std::result::Result<(), Box<dyn Error + Sync + Send>> {
+ log::error!("starting minimal_lsp");
+
+ // transport
+ let (connection, io_thread) = Connection::stdio();
+
+ // advertised capabilities
+ let caps = ServerCapabilities {
+ text_document_sync: Some(TextDocumentSyncCapability::Kind(TextDocumentSyncKind::FULL)),
+ completion_provider: Some(CompletionOptions::default()),
+ definition_provider: Some(OneOf::Left(true)),
+ hover_provider: Some(HoverProviderCapability::Simple(true)),
+ document_formatting_provider: Some(OneOf::Left(true)),
+ ..Default::default()
+ };
+ let init_value = serde_json::json!({
+ "capabilities": caps,
+ "offsetEncoding": ["utf-8"],
+ });
+
+ let init_params = connection.initialize(init_value)?;
+ main_loop(connection, init_params)?;
+ io_thread.join()?;
+ log::error!("shutting down server");
+ Ok(())
+}
+
+// =====================================================================
+// event loop
+// =====================================================================
+
+fn main_loop(
+ connection: Connection,
+ params: serde_json::Value,
+) -> std::result::Result<(), Box<dyn Error + Sync + Send>> {
+ let _init: InitializeParams = serde_json::from_value(params)?;
+ let mut docs: FxHashMap<Url, String> = FxHashMap::default();
+
+ for msg in &connection.receiver {
+ match msg {
+ Message::Request(req) => {
+ if connection.handle_shutdown(&req)? {
+ break;
+ }
+ if let Err(err) = handle_request(&connection, &req, &mut docs) {
+ log::error!("[lsp] request {} failed: {err}", &req.method);
+ }
+ }
+ Message::Notification(note) => {
+ if let Err(err) = handle_notification(&connection, ¬e, &mut docs) {
+ log::error!("[lsp] notification {} failed: {err}", note.method);
+ }
+ }
+ Message::Response(resp) => log::error!("[lsp] response: {resp:?}"),
+ }
+ }
+ Ok(())
+}
+
+// =====================================================================
+// notifications
+// =====================================================================
+
+fn handle_notification(
+ conn: &Connection,
+ note: &lsp_server::Notification,
+ docs: &mut FxHashMap<Url, String>,
+) -> Result<()> {
+ match note.method.as_str() {
+ DidOpenTextDocument::METHOD => {
+ let p: DidOpenTextDocumentParams = serde_json::from_value(note.params.clone())?;
+ let uri = p.text_document.uri;
+ docs.insert(uri.clone(), p.text_document.text);
+ publish_dummy_diag(conn, &uri)?;
+ }
+ DidChangeTextDocument::METHOD => {
+ let p: DidChangeTextDocumentParams = serde_json::from_value(note.params.clone())?;
+ if let Some(change) = p.content_changes.into_iter().next() {
+ let uri = p.text_document.uri;
+ docs.insert(uri.clone(), change.text);
+ publish_dummy_diag(conn, &uri)?;
+ }
+ }
+ _ => {}
+ }
+ Ok(())
+}
+
+// =====================================================================
+// requests
+// =====================================================================
+
+fn handle_request(
+ conn: &Connection,
+ req: &ServerRequest,
+ docs: &mut FxHashMap<Url, String>,
+) -> Result<()> {
+ match req.method.as_str() {
+ GotoDefinition::METHOD => {
+ send_ok(conn, req.id.clone(), &lsp_types::GotoDefinitionResponse::Array(Vec::new()))?;
+ }
+ Completion::METHOD => {
+ let item = CompletionItem {
+ label: "HelloFromLSP".into(),
+ kind: Some(CompletionItemKind::FUNCTION),
+ detail: Some("dummy completion".into()),
+ ..Default::default()
+ };
+ send_ok(conn, req.id.clone(), &CompletionResponse::Array(vec![item]))?;
+ }
+ HoverRequest::METHOD => {
+ let hover = Hover {
+ contents: HoverContents::Scalar(MarkedString::String(
+ "Hello from *minimal_lsp*".into(),
+ )),
+ range: None,
+ };
+ send_ok(conn, req.id.clone(), &hover)?;
+ }
+ Formatting::METHOD => {
+ let p: DocumentFormattingParams = serde_json::from_value(req.params.clone())?;
+ let uri = p.text_document.uri;
+ let text = docs
+ .get(&uri)
+ .ok_or_else(|| anyhow!("document not in cache – did you send DidOpen?"))?;
+ let formatted = run_rustfmt(text)?;
+ let edit = TextEdit { range: full_range(text), new_text: formatted };
+ send_ok(conn, req.id.clone(), &vec![edit])?;
+ }
+ _ => send_err(
+ conn,
+ req.id.clone(),
+ lsp_server::ErrorCode::MethodNotFound,
+ "unhandled method",
+ )?,
+ }
+ Ok(())
+}
+
+// =====================================================================
+// diagnostics
+// =====================================================================
+fn publish_dummy_diag(conn: &Connection, uri: &Url) -> Result<()> {
+ let diag = Diagnostic {
+ range: Range::new(Position::new(0, 0), Position::new(0, 1)),
+ severity: Some(DiagnosticSeverity::INFORMATION),
+ code: None,
+ code_description: None,
+ source: Some("minimal_lsp".into()),
+ message: "dummy diagnostic".into(),
+ related_information: None,
+ tags: None,
+ data: None,
+ };
+ let params =
+ PublishDiagnosticsParams { uri: uri.clone(), diagnostics: vec![diag], version: None };
+ conn.sender.send(Message::Notification(lsp_server::Notification::new(
+ PublishDiagnostics::METHOD.to_owned(),
+ params,
+ )))?;
+ Ok(())
+}
+
+// =====================================================================
+// helpers
+// =====================================================================
+
+fn run_rustfmt(input: &str) -> Result<String> {
+ let cwd = std::env::current_dir().expect("can't determine CWD");
+ let mut child = command("rustfmt", &cwd, &FxHashMap::default())
+ .arg("--emit")
+ .arg("stdout")
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .context("failed to spawn rustfmt – is it installed?")?;
+
+ let Some(stdin) = child.stdin.as_mut() else {
+ bail!("stdin unavailable");
+ };
+ stdin.write_all(input.as_bytes())?;
+ let output = child.wait_with_output()?;
+ if !output.status.success() {
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ bail!("rustfmt failed: {stderr}");
+ }
+ Ok(String::from_utf8(output.stdout)?)
+}
+
+fn full_range(text: &str) -> Range {
+ let last_line_idx = text.lines().count().saturating_sub(1) as u32;
+ let last_col = text.lines().last().map_or(0, |l| l.chars().count()) as u32;
+ Range::new(Position::new(0, 0), Position::new(last_line_idx, last_col))
+}
+
+fn send_ok<T: serde::Serialize>(conn: &Connection, id: RequestId, result: &T) -> Result<()> {
+ let resp = Response { id, result: Some(serde_json::to_value(result)?), error: None };
+ conn.sender.send(Message::Response(resp))?;
+ Ok(())
+}
+
+fn send_err(
+ conn: &Connection,
+ id: RequestId,
+ code: lsp_server::ErrorCode,
+ msg: &str,
+) -> Result<()> {
+ let resp = Response {
+ id,
+ result: None,
+ error: Some(lsp_server::ResponseError {
+ code: code as i32,
+ message: msg.into(),
+ data: None,
+ }),
+ };
+ conn.sender.send(Message::Response(resp))?;
+ Ok(())
+}
diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version
index 9027932..57ff326 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-ad3b7257615c28aaf8212a189ec032b8af75de51
+a9fb6103b05c6ad6eee6bed4c0bb5a2e8e1024c6
diff --git a/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs b/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs
index d8cbf89..b9f570f 100644
--- a/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs
+++ b/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs
@@ -116,6 +116,8 @@ fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
// keywords we use for special macro expansions
const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[
"asm",
+ "naked_asm",
+ "global_asm",
"att_syntax",
"builtin",
"clobber_abi",
diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt
index 8b57db2..77414be 100644
--- a/src/tools/tidy/src/issues.txt
+++ b/src/tools/tidy/src/issues.txt
@@ -1021,7 +1021,6 @@
ui/foreign/issue-99276-same-type-lifetimes.rs
ui/function-pointer/issue-102289.rs
ui/functions-closures/closure-expected-type/issue-38714.rs
-ui/generic-associated-types/bugs/issue-100013.rs
ui/generic-associated-types/bugs/issue-80626.rs
ui/generic-associated-types/bugs/issue-87735.rs
ui/generic-associated-types/bugs/issue-87755.rs
@@ -1099,7 +1098,6 @@
ui/generic-associated-types/issue-91139.rs
ui/generic-associated-types/issue-91883.rs
ui/generic-associated-types/issue-92033.rs
-ui/generic-associated-types/issue-92096.rs
ui/generic-associated-types/issue-92280.rs
ui/generic-associated-types/issue-92954.rs
ui/generic-associated-types/issue-93141.rs
diff --git a/tests/codegen/sanitizer/kcfi/naked-function.rs b/tests/codegen/sanitizer/kcfi/naked-function.rs
new file mode 100644
index 0000000..2c8cdc9
--- /dev/null
+++ b/tests/codegen/sanitizer/kcfi/naked-function.rs
@@ -0,0 +1,47 @@
+//@ add-core-stubs
+//@ revisions: aarch64 x86_64
+//@ [aarch64] compile-flags: --target aarch64-unknown-none
+//@ [aarch64] needs-llvm-components: aarch64
+//@ [x86_64] compile-flags: --target x86_64-unknown-none
+//@ [x86_64] needs-llvm-components: x86
+//@ compile-flags: -Ctarget-feature=-crt-static -Zsanitizer=kcfi -Cno-prepopulate-passes -Copt-level=0
+
+#![feature(no_core, lang_items)]
+#![crate_type = "lib"]
+#![no_core]
+
+extern crate minicore;
+use minicore::*;
+
+struct Thing;
+trait MyTrait {
+ #[unsafe(naked)]
+ extern "C" fn my_naked_function() {
+ // the real function is defined
+ // CHECK: .globl
+ // CHECK-SAME: my_naked_function
+ naked_asm!("ret")
+ }
+}
+impl MyTrait for Thing {}
+
+// the shim calls the real function
+// CHECK-LABEL: define
+// CHECK-SAME: my_naked_function
+// CHECK-SAME: reify.shim.fnptr
+
+// CHECK-LABEL: main
+#[unsafe(no_mangle)]
+pub fn main() {
+ // Trick the compiler into generating an indirect call.
+ const F: extern "C" fn() = Thing::my_naked_function;
+
+ // main calls the shim function
+ // CHECK: call void
+ // CHECK-SAME: my_naked_function
+ // CHECK-SAME: reify.shim.fnptr
+ (F)();
+}
+
+// CHECK: declare !kcfi_type
+// CHECK-SAME: my_naked_function
diff --git a/tests/crashes/128094.rs b/tests/crashes/128094.rs
deleted file mode 100644
index 56d09d7..0000000
--- a/tests/crashes/128094.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-//@ known-bug: rust-lang/rust#128094
-//@ compile-flags: -Zmir-enable-passes=+GVN
-//@ edition: 2018
-
-pub enum Request {
- TestSome(T),
-}
-
-pub async fn handle_event(event: Request) {
- async move {
- static instance: Request = Request { bar: 17 };
- &instance
- }
- .await;
-}
diff --git a/tests/crashes/135128.rs b/tests/crashes/135128.rs
deleted file mode 100644
index c718b75..0000000
--- a/tests/crashes/135128.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-//@ known-bug: #135128
-//@ compile-flags: -Copt-level=1
-//@ edition: 2021
-
-#![feature(trivial_bounds)]
-
-async fn return_str() -> str
-where
- str: Sized,
-{
- *"Sized".to_string().into_boxed_str()
-}
-fn main() {}
diff --git a/tests/mir-opt/const_prop/transmute.rs b/tests/mir-opt/const_prop/transmute.rs
index 892b91a..33cbefb 100644
--- a/tests/mir-opt/const_prop/transmute.rs
+++ b/tests/mir-opt/const_prop/transmute.rs
@@ -56,7 +56,7 @@ union Union32 {
pub unsafe fn unreachable_direct() -> ! {
// CHECK-LABEL: fn unreachable_direct(
// CHECK: = const ();
- // CHECK: = const () as Never (Transmute);
+ // CHECK: = const ZeroSized: Never;
let x: Never = unsafe { transmute(()) };
match x {}
}
diff --git a/tests/mir-opt/const_prop/transmute.unreachable_direct.GVN.32bit.diff b/tests/mir-opt/const_prop/transmute.unreachable_direct.GVN.32bit.diff
index 3364782..5aeb55a 100644
--- a/tests/mir-opt/const_prop/transmute.unreachable_direct.GVN.32bit.diff
+++ b/tests/mir-opt/const_prop/transmute.unreachable_direct.GVN.32bit.diff
@@ -15,7 +15,7 @@
- _2 = ();
- _1 = move _2 as Never (Transmute);
+ _2 = const ();
-+ _1 = const () as Never (Transmute);
++ _1 = const ZeroSized: Never;
unreachable;
}
}
diff --git a/tests/mir-opt/const_prop/transmute.unreachable_direct.GVN.64bit.diff b/tests/mir-opt/const_prop/transmute.unreachable_direct.GVN.64bit.diff
index 3364782..5aeb55a 100644
--- a/tests/mir-opt/const_prop/transmute.unreachable_direct.GVN.64bit.diff
+++ b/tests/mir-opt/const_prop/transmute.unreachable_direct.GVN.64bit.diff
@@ -15,7 +15,7 @@
- _2 = ();
- _1 = move _2 as Never (Transmute);
+ _2 = const ();
-+ _1 = const () as Never (Transmute);
++ _1 = const ZeroSized: Never;
unreachable;
}
}
diff --git a/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-abort.diff b/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-abort.diff
index 770c673..ffe4a02 100644
--- a/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-abort.diff
+++ b/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-abort.diff
@@ -18,7 +18,8 @@
bb0: {
_4 = copy _1 as *const T (PtrToPtr);
- _5 = PtrMetadata(copy _4);
+- _5 = PtrMetadata(copy _4);
++ _5 = const ();
_6 = copy _1 as *const (&A, [T]) (PtrToPtr);
- _7 = PtrMetadata(copy _6);
+ _7 = PtrMetadata(copy _1);
diff --git a/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-unwind.diff b/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-unwind.diff
index 770c673..ffe4a02 100644
--- a/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-unwind.diff
+++ b/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-unwind.diff
@@ -18,7 +18,8 @@
bb0: {
_4 = copy _1 as *const T (PtrToPtr);
- _5 = PtrMetadata(copy _4);
+- _5 = PtrMetadata(copy _4);
++ _5 = const ();
_6 = copy _1 as *const (&A, [T]) (PtrToPtr);
- _7 = PtrMetadata(copy _6);
+ _7 = PtrMetadata(copy _1);
diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs
index 6ef320c..5d348bc 100644
--- a/tests/mir-opt/gvn.rs
+++ b/tests/mir-opt/gvn.rs
@@ -869,7 +869,7 @@ fn generic_cast_metadata<T, A: ?Sized, B: ?Sized>(ps: *const [T], pa: *const A,
// Metadata usize -> (), do not optimize.
// CHECK: [[T:_.+]] = copy _1 as
- // CHECK-NEXT: PtrMetadata(copy [[T]])
+ // CHECK-NEXT: const ();
let t1 = CastPtrToPtr::<_, *const T>(ps);
let m1 = PtrMetadata(t1);
diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir
index 3b58f1d..8c5fbda 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir
@@ -109,7 +109,6 @@
}
bb4: {
- StorageLive(_15);
_14 = &mut _13;
_15 = <Enumerate<std::slice::Iter<'_, T>> as Iterator>::next(move _14) -> [return: bb5, unwind: bb11];
}
@@ -120,7 +119,6 @@
}
bb6: {
- StorageDead(_15);
StorageDead(_13);
drop(_2) -> [return: bb7, unwind continue];
}
@@ -135,14 +133,13 @@
StorageLive(_19);
_19 = &_2;
StorageLive(_20);
- _20 = (copy _17, copy _18);
+ _20 = copy ((_15 as Some).0: (usize, &T));
_21 = <impl Fn(usize, &T) as Fn<(usize, &T)>>::call(move _19, move _20) -> [return: bb9, unwind: bb11];
}
bb9: {
StorageDead(_20);
StorageDead(_19);
- StorageDead(_15);
goto -> bb4;
}
diff --git a/tests/run-make/export-executable-symbols/rmake.rs b/tests/run-make/export-executable-symbols/rmake.rs
index 884c736..aa130b0 100644
--- a/tests/run-make/export-executable-symbols/rmake.rs
+++ b/tests/run-make/export-executable-symbols/rmake.rs
@@ -4,20 +4,22 @@
// symbol.
// See https://github.com/rust-lang/rust/pull/85673
-//@ only-unix
-// Reason: the export-executable-symbols flag only works on Unix
-// due to hardcoded platform-specific implementation
-// (See #85673)
-//@ ignore-cross-compile
//@ ignore-wasm
+//@ ignore-cross-compile
-use run_make_support::{bin_name, llvm_readobj, rustc};
+use run_make_support::object::Object;
+use run_make_support::{bin_name, is_darwin, object, rustc};
fn main() {
- rustc().arg("-Zexport-executable-symbols").input("main.rs").crate_type("bin").run();
- llvm_readobj()
- .symbols()
- .input(bin_name("main"))
- .run()
- .assert_stdout_contains("exported_symbol");
+ rustc()
+ .arg("-Ctarget-feature=-crt-static")
+ .arg("-Zexport-executable-symbols")
+ .input("main.rs")
+ .crate_type("bin")
+ .run();
+ let name: &[u8] = if is_darwin() { b"_exported_symbol" } else { b"exported_symbol" };
+ let contents = std::fs::read(bin_name("main")).unwrap();
+ let object = object::File::parse(contents.as_slice()).unwrap();
+ let found = object.exports().unwrap().iter().any(|x| x.name() == name);
+ assert!(found);
}
diff --git a/tests/run-make/mte-ffi/bar.h b/tests/run-make/mte-ffi/bar.h
index 9b030c6..a2292ae 100644
--- a/tests/run-make/mte-ffi/bar.h
+++ b/tests/run-make/mte-ffi/bar.h
@@ -1,5 +1,3 @@
-// FIXME(#141600) the mte-ffi test doesn't fail in aarch64-gnu
-
#ifndef __BAR_H
#define __BAR_H
diff --git a/tests/run-make/mte-ffi/bar_float.c b/tests/run-make/mte-ffi/bar_float.c
index acc2f5d..a1590f6 100644
--- a/tests/run-make/mte-ffi/bar_float.c
+++ b/tests/run-make/mte-ffi/bar_float.c
@@ -3,9 +3,9 @@
#include <stdint.h>
#include "bar.h"
-extern void foo(float*);
+extern void foo(char*);
-void bar(float *ptr) {
+void bar(char *ptr) {
if (((uintptr_t)ptr >> 56) != 0x1f) {
fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n");
exit(1);
diff --git a/tests/run-make/mte-ffi/bar_int.c b/tests/run-make/mte-ffi/bar_int.c
index c92e765..d1c79e9 100644
--- a/tests/run-make/mte-ffi/bar_int.c
+++ b/tests/run-make/mte-ffi/bar_int.c
@@ -5,7 +5,7 @@
extern void foo(unsigned int *);
-void bar(unsigned int *ptr) {
+void bar(char *ptr) {
if (((uintptr_t)ptr >> 56) != 0x1f) {
fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n");
exit(1);
diff --git a/tests/run-make/mte-ffi/bar_string.c b/tests/run-make/mte-ffi/bar_string.c
index 8e1202f..5669ffd 100644
--- a/tests/run-make/mte-ffi/bar_string.c
+++ b/tests/run-make/mte-ffi/bar_string.c
@@ -1,7 +1,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
-#include <string.h>
#include "bar.h"
extern void foo(char*);
@@ -33,7 +32,7 @@
// Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be),
// and a different value in the ignored top 4 bits.
- ptr = (char *)((uintptr_t)ptr | 0x1fl << 56);
+ ptr = (unsigned int *)((uintptr_t)ptr | 0x1fl << 56);
if (mte_enabled()) {
set_tag(ptr);
diff --git a/tests/run-make/mte-ffi/rmake.rs b/tests/run-make/mte-ffi/rmake.rs
index a8da0dc..71d8318 100644
--- a/tests/run-make/mte-ffi/rmake.rs
+++ b/tests/run-make/mte-ffi/rmake.rs
@@ -2,6 +2,13 @@
//! FFI boundaries (C <-> Rust). This test does not require MTE: whilst the test will use MTE if
//! available, if it is not, arbitrary tag bits are set using TBI.
+//@ ignore-test (FIXME #141600)
+//
+// FIXME(#141600): this test is broken in two ways:
+// 1. This test triggers `-Wincompatible-pointer-types` on GCC 14.
+// 2. This test requires ARMv8.5+ w/ MTE extensions enabled, but GHA CI runner hardware do not have
+// this enabled.
+
//@ only-aarch64-unknown-linux-gnu
// Reason: this test is only valid for AArch64 with `gcc`. The linker must be explicitly specified
// when cross-compiling, so it is limited to `aarch64-unknown-linux-gnu`.
diff --git a/tests/run-make/option-output-no-space/main.rs b/tests/run-make/option-output-no-space/main.rs
new file mode 100644
index 0000000..f328e4d
--- /dev/null
+++ b/tests/run-make/option-output-no-space/main.rs
@@ -0,0 +1 @@
+fn main() {}
diff --git a/tests/run-make/option-output-no-space/rmake.rs b/tests/run-make/option-output-no-space/rmake.rs
new file mode 100644
index 0000000..2c42f15
--- /dev/null
+++ b/tests/run-make/option-output-no-space/rmake.rs
@@ -0,0 +1,95 @@
+// This test is to check if the warning is emitted when no space
+// between `-o` and arg is applied, see issue #142812
+
+//@ ignore-cross-compile
+use run_make_support::rustc;
+
+fn main() {
+ // test fake args
+ rustc()
+ .input("main.rs")
+ .arg("-optimize")
+ .run()
+ .assert_stderr_contains(
+ "warning: option `-o` has no space between flag name and value, which can be confusing",
+ )
+ .assert_stderr_contains(
+ "note: output filename `-o ptimize` is applied instead of a flag named `optimize`",
+ );
+ rustc()
+ .input("main.rs")
+ .arg("-o0")
+ .run()
+ .assert_stderr_contains(
+ "warning: option `-o` has no space between flag name and value, which can be confusing",
+ )
+ .assert_stderr_contains(
+ "note: output filename `-o 0` is applied instead of a flag named `o0`",
+ );
+ rustc().input("main.rs").arg("-o1").run();
+ // test real args by iter optgroups
+ rustc()
+ .input("main.rs")
+ .arg("-out-dir")
+ .run()
+ .assert_stderr_contains(
+ "warning: option `-o` has no space between flag name and value, which can be confusing",
+ )
+ .assert_stderr_contains(
+ "note: output filename `-o ut-dir` is applied instead of a flag named `out-dir`",
+ )
+ .assert_stderr_contains(
+ "help: insert a space between `-o` and `ut-dir` if this is intentional: `-o ut-dir`",
+ );
+ // test real args by iter CG_OPTIONS
+ rustc()
+ .input("main.rs")
+ .arg("-opt_level")
+ .run()
+ .assert_stderr_contains(
+ "warning: option `-o` has no space between flag name and value, which can be confusing",
+ )
+ .assert_stderr_contains(
+ "note: output filename `-o pt_level` is applied instead of a flag named `opt_level`",
+ )
+ .assert_stderr_contains(
+ "help: insert a space between `-o` and `pt_level` if this is intentional: `-o pt_level`"
+ );
+ // separater in-sensitive
+ rustc()
+ .input("main.rs")
+ .arg("-opt-level")
+ .run()
+ .assert_stderr_contains(
+ "warning: option `-o` has no space between flag name and value, which can be confusing",
+ )
+ .assert_stderr_contains(
+ "note: output filename `-o pt-level` is applied instead of a flag named `opt-level`",
+ )
+ .assert_stderr_contains(
+ "help: insert a space between `-o` and `pt-level` if this is intentional: `-o pt-level`"
+ );
+ rustc()
+ .input("main.rs")
+ .arg("-overflow-checks")
+ .run()
+ .assert_stderr_contains(
+ "warning: option `-o` has no space between flag name and value, which can be confusing",
+ )
+ .assert_stderr_contains(
+ "note: output filename `-o verflow-checks` \
+ is applied instead of a flag named `overflow-checks`",
+ )
+ .assert_stderr_contains(
+ "help: insert a space between `-o` and `verflow-checks` \
+ if this is intentional: `-o verflow-checks`",
+ );
+
+ // No warning for Z_OPTIONS
+ rustc().input("main.rs").arg("-oom").run().assert_stderr_equals("");
+
+ // test no warning when there is space between `-o` and arg
+ rustc().input("main.rs").arg("-o").arg("ptimize").run().assert_stderr_equals("");
+ rustc().input("main.rs").arg("--out-dir").arg("xxx").run().assert_stderr_equals("");
+ rustc().input("main.rs").arg("-o").arg("out-dir").run().assert_stderr_equals("");
+}
diff --git a/tests/ui/asm/naked-function-shim.rs b/tests/ui/asm/naked-function-shim.rs
new file mode 100644
index 0000000..4694d0c
--- /dev/null
+++ b/tests/ui/asm/naked-function-shim.rs
@@ -0,0 +1,31 @@
+// The indirect call will generate a shim that then calls the actual function. Test that
+// this is handled correctly. See also https://github.com/rust-lang/rust/issues/143266.
+
+//@ build-pass
+//@ add-core-stubs
+//@ revisions: aarch64 x86_64
+//@ [aarch64] compile-flags: --target aarch64-unknown-none
+//@ [aarch64] needs-llvm-components: aarch64
+//@ [x86_64] compile-flags: --target x86_64-unknown-none
+//@ [x86_64] needs-llvm-components: x86
+
+#![feature(no_core, lang_items)]
+#![crate_type = "lib"]
+#![no_core]
+
+extern crate minicore;
+use minicore::*;
+
+trait MyTrait {
+ #[unsafe(naked)]
+ extern "C" fn foo(&self) {
+ naked_asm!("ret")
+ }
+}
+
+impl MyTrait for i32 {}
+
+fn main() {
+ let x: extern "C" fn(&_) = <dyn MyTrait as MyTrait>::foo;
+ x(&1);
+}
diff --git a/tests/ui/async-await/async-closures/def-path.stderr b/tests/ui/async-await/async-closures/def-path.stderr
index b50e353..58a5b0b 100644
--- a/tests/ui/async-await/async-closures/def-path.stderr
+++ b/tests/ui/async-await/async-closures/def-path.stderr
@@ -5,11 +5,11 @@
| -- the expected `async` closure body
LL |
LL | let () = x();
- | ^^ --- this expression has type `{static main::{closure#0}::{closure#0}<?17t> upvar_tys=?16t resume_ty=ResumeTy yield_ty=() return_ty=() witness=?5t}`
+ | ^^ --- this expression has type `{static main::{closure#0}::{closure#0}<?16t> upvar_tys=?15t resume_ty=ResumeTy yield_ty=() return_ty=() witness={main::{closure#0}::{closure#0}}}`
| |
| expected `async` closure body, found `()`
|
- = note: expected `async` closure body `{static main::{closure#0}::{closure#0}<?17t> upvar_tys=?16t resume_ty=ResumeTy yield_ty=() return_ty=() witness=?5t}`
+ = note: expected `async` closure body `{static main::{closure#0}::{closure#0}<?16t> upvar_tys=?15t resume_ty=ResumeTy yield_ty=() return_ty=() witness={main::{closure#0}::{closure#0}}}`
found unit type `()`
error: aborting due to 1 previous error
diff --git a/tests/ui/async-await/async-drop/foreign-fundamental.rs b/tests/ui/async-await/async-drop/foreign-fundamental.rs
new file mode 100644
index 0000000..1c192fc
--- /dev/null
+++ b/tests/ui/async-await/async-drop/foreign-fundamental.rs
@@ -0,0 +1,21 @@
+//@ edition: 2018
+
+#![feature(async_drop)]
+//~^ WARN the feature `async_drop` is incomplete
+
+use std::future::AsyncDrop;
+use std::pin::Pin;
+
+struct Foo;
+
+impl AsyncDrop for &Foo {
+ //~^ ERROR the `AsyncDrop` trait may only be implemented for
+ async fn drop(self: Pin<&mut Self>) {}
+}
+
+impl AsyncDrop for Pin<Foo> {
+ //~^ ERROR the `AsyncDrop` trait may only be implemented for
+ async fn drop(self: Pin<&mut Self>) {}
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/async-drop/foreign-fundamental.stderr b/tests/ui/async-await/async-drop/foreign-fundamental.stderr
new file mode 100644
index 0000000..7b52329
--- /dev/null
+++ b/tests/ui/async-await/async-drop/foreign-fundamental.stderr
@@ -0,0 +1,24 @@
+warning: the feature `async_drop` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/foreign-fundamental.rs:3:12
+ |
+LL | #![feature(async_drop)]
+ | ^^^^^^^^^^
+ |
+ = note: see issue #126482 <https://github.com/rust-lang/rust/issues/126482> for more information
+ = note: `#[warn(incomplete_features)]` on by default
+
+error[E0120]: the `AsyncDrop` trait may only be implemented for local structs, enums, and unions
+ --> $DIR/foreign-fundamental.rs:11:20
+ |
+LL | impl AsyncDrop for &Foo {
+ | ^^^^ must be a struct, enum, or union in the current crate
+
+error[E0120]: the `AsyncDrop` trait may only be implemented for local structs, enums, and unions
+ --> $DIR/foreign-fundamental.rs:16:20
+ |
+LL | impl AsyncDrop for Pin<Foo> {
+ | ^^^^^^^^ must be a struct, enum, or union in the current crate
+
+error: aborting due to 2 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0120`.
diff --git a/tests/ui/async-await/drop-tracking-unresolved-typeck-results.stderr b/tests/ui/async-await/drop-tracking-unresolved-typeck-results.no_assumptions.stderr
similarity index 88%
rename from tests/ui/async-await/drop-tracking-unresolved-typeck-results.stderr
rename to tests/ui/async-await/drop-tracking-unresolved-typeck-results.no_assumptions.stderr
index 0d3ee8a..d5560bf 100644
--- a/tests/ui/async-await/drop-tracking-unresolved-typeck-results.stderr
+++ b/tests/ui/async-await/drop-tracking-unresolved-typeck-results.no_assumptions.stderr
@@ -1,9 +1,7 @@
error: implementation of `FnOnce` is not general enough
- --> $DIR/drop-tracking-unresolved-typeck-results.rs:98:5
+ --> $DIR/drop-tracking-unresolved-typeck-results.rs:102:5
|
LL | / send(async {
-LL | |
-LL | |
LL | | Next(&Buffered(Map(Empty(PhantomData), ready::<&()>), FuturesOrdered(PhantomData), 0)).await
LL | | });
| |______^ implementation of `FnOnce` is not general enough
@@ -12,11 +10,9 @@
= note: ...but it actually implements `FnOnce<(&(),)>`
error: implementation of `FnOnce` is not general enough
- --> $DIR/drop-tracking-unresolved-typeck-results.rs:98:5
+ --> $DIR/drop-tracking-unresolved-typeck-results.rs:102:5
|
LL | / send(async {
-LL | |
-LL | |
LL | | Next(&Buffered(Map(Empty(PhantomData), ready::<&()>), FuturesOrdered(PhantomData), 0)).await
LL | | });
| |______^ implementation of `FnOnce` is not general enough
diff --git a/tests/ui/async-await/drop-tracking-unresolved-typeck-results.rs b/tests/ui/async-await/drop-tracking-unresolved-typeck-results.rs
index e6c2954..16f9293 100644
--- a/tests/ui/async-await/drop-tracking-unresolved-typeck-results.rs
+++ b/tests/ui/async-await/drop-tracking-unresolved-typeck-results.rs
@@ -1,5 +1,9 @@
//@ incremental
//@ edition: 2021
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] check-pass
+//@[no_assumptions] known-bug: #110338
use std::future::*;
use std::marker::PhantomData;
@@ -96,8 +100,6 @@ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
fn main() {
send(async {
- //~^ ERROR implementation of `FnOnce` is not general enough
- //~| ERROR implementation of `FnOnce` is not general enough
Next(&Buffered(Map(Empty(PhantomData), ready::<&()>), FuturesOrdered(PhantomData), 0)).await
});
}
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-1.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-1.no_assumptions.stderr
new file mode 100644
index 0000000..b298a3b
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-1.no_assumptions.stderr
@@ -0,0 +1,38 @@
+error[E0308]: mismatched types
+ --> $DIR/higher-ranked-auto-trait-1.rs:37:5
+ |
+LL | / async {
+LL | | let _y = &();
+LL | | let _x = filter(FilterMap {
+LL | | f: || async move { *_y },
+... |
+LL | | drop(_x);
+LL | | }
+ | |_____^ one type is more general than the other
+ |
+ = note: expected `async` block `{async block@$DIR/higher-ranked-auto-trait-1.rs:40:19: 40:29}`
+ found `async` block `{async block@$DIR/higher-ranked-auto-trait-1.rs:40:19: 40:29}`
+ = note: no two async blocks, even if identical, have the same type
+ = help: consider pinning your async block and casting it to a trait object
+
+error[E0308]: mismatched types
+ --> $DIR/higher-ranked-auto-trait-1.rs:37:5
+ |
+LL | / async {
+LL | | let _y = &();
+LL | | let _x = filter(FilterMap {
+LL | | f: || async move { *_y },
+... |
+LL | | drop(_x);
+LL | | }
+ | |_____^ one type is more general than the other
+ |
+ = note: expected `async` block `{async block@$DIR/higher-ranked-auto-trait-1.rs:40:19: 40:29}`
+ found `async` block `{async block@$DIR/higher-ranked-auto-trait-1.rs:40:19: 40:29}`
+ = note: no two async blocks, even if identical, have the same type
+ = help: consider pinning your async block and casting it to a trait object
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-1.rs b/tests/ui/async-await/higher-ranked-auto-trait-1.rs
new file mode 100644
index 0000000..740f7e2
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-1.rs
@@ -0,0 +1,48 @@
+// Repro for <https://github.com/rust-lang/rust/issues/79648#issuecomment-749127947>.
+//@ edition: 2021
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] check-pass
+//@[no_assumptions] known-bug: #110338
+
+use std::future::Future;
+use std::marker::PhantomData;
+
+trait Stream {
+ type Item;
+}
+
+struct Filter<St: Stream> {
+ pending_item: St::Item,
+}
+
+fn filter<St: Stream>(_: St) -> Filter<St> {
+ unimplemented!();
+}
+
+struct FilterMap<Fut, F> {
+ f: F,
+ pending: PhantomData<Fut>,
+}
+
+impl<Fut, F> Stream for FilterMap<Fut, F>
+where
+ F: FnMut() -> Fut,
+ Fut: Future,
+{
+ type Item = ();
+}
+
+pub fn get_foo() -> impl Future + Send {
+ async {
+ let _y = &();
+ let _x = filter(FilterMap {
+ f: || async move { *_y },
+ pending: PhantomData,
+ });
+ async {}.await;
+ drop(_x);
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-10.assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-10.assumptions.stderr
new file mode 100644
index 0000000..6fcf1b1
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-10.assumptions.stderr
@@ -0,0 +1,21 @@
+error: implementation of `Foo` is not general enough
+ --> $DIR/higher-ranked-auto-trait-10.rs:32:5
+ |
+LL | Box::new(async move { get_foo(x).await })
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
+ |
+ = note: `Foo<'1>` would have to be implemented for the type `&'0 str`, for any two lifetimes `'0` and `'1`...
+ = note: ...but `Foo<'2>` is actually implemented for the type `&'2 str`, for some specific lifetime `'2`
+
+error: implementation of `Foo` is not general enough
+ --> $DIR/higher-ranked-auto-trait-10.rs:32:5
+ |
+LL | Box::new(async move { get_foo(x).await })
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
+ |
+ = note: `Foo<'1>` would have to be implemented for the type `&'0 str`, for any two lifetimes `'0` and `'1`...
+ = note: ...but `Foo<'2>` is actually implemented for the type `&'2 str`, for some specific lifetime `'2`
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-10.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-10.no_assumptions.stderr
new file mode 100644
index 0000000..6fcf1b1
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-10.no_assumptions.stderr
@@ -0,0 +1,21 @@
+error: implementation of `Foo` is not general enough
+ --> $DIR/higher-ranked-auto-trait-10.rs:32:5
+ |
+LL | Box::new(async move { get_foo(x).await })
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
+ |
+ = note: `Foo<'1>` would have to be implemented for the type `&'0 str`, for any two lifetimes `'0` and `'1`...
+ = note: ...but `Foo<'2>` is actually implemented for the type `&'2 str`, for some specific lifetime `'2`
+
+error: implementation of `Foo` is not general enough
+ --> $DIR/higher-ranked-auto-trait-10.rs:32:5
+ |
+LL | Box::new(async move { get_foo(x).await })
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
+ |
+ = note: `Foo<'1>` would have to be implemented for the type `&'0 str`, for any two lifetimes `'0` and `'1`...
+ = note: ...but `Foo<'2>` is actually implemented for the type `&'2 str`, for some specific lifetime `'2`
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-10.rs b/tests/ui/async-await/higher-ranked-auto-trait-10.rs
new file mode 100644
index 0000000..4bfa279
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-10.rs
@@ -0,0 +1,35 @@
+// Repro for <https://github.com/rust-lang/rust/issues/92415#issue-1090723521>.
+//@ edition: 2021
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] known-bug: unknown
+//@[no_assumptions] known-bug: #110338
+
+use std::any::Any;
+use std::future::Future;
+
+trait Foo<'a>: Sized {
+ type Error;
+ fn foo(x: &'a str) -> Result<Self, Self::Error>;
+}
+
+impl<'a> Foo<'a> for &'a str {
+ type Error = ();
+
+ fn foo(x: &'a str) -> Result<Self, Self::Error> {
+ Ok(x)
+ }
+}
+
+async fn get_foo<'a, T>(x: &'a str) -> Result<T, <T as Foo<'a>>::Error>
+where
+ T: Foo<'a>,
+{
+ Foo::foo(x)
+}
+
+fn bar<'a>(x: &'a str) -> Box<dyn Future<Output = Result<&'a str, ()>> + Send + 'a> {
+ Box::new(async move { get_foo(x).await })
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-11.assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-11.assumptions.stderr
new file mode 100644
index 0000000..d39843f
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-11.assumptions.stderr
@@ -0,0 +1,20 @@
+error: lifetime may not live long enough
+ --> $DIR/higher-ranked-auto-trait-11.rs:27:9
+ |
+LL | impl<'a, T> Foo<'a> for MyType<T>
+ | -- lifetime `'a` defined here
+...
+LL | Box::pin(async move { <T as Foo<'a>>::foo().await })
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ coercion requires that `'a` must outlive `'static`
+
+error: implementation of `Send` is not general enough
+ --> $DIR/higher-ranked-auto-trait-11.rs:27:9
+ |
+LL | Box::pin(async move { <T as Foo<'a>>::foo().await })
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Send` is not general enough
+ |
+ = note: `Send` would have to be implemented for the type `<T as Foo<'0>>::Future`, for any lifetime `'0`...
+ = note: ...but `Send` is actually implemented for the type `<T as Foo<'1>>::Future`, for some specific lifetime `'1`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-11.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-11.no_assumptions.stderr
new file mode 100644
index 0000000..d39843f
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-11.no_assumptions.stderr
@@ -0,0 +1,20 @@
+error: lifetime may not live long enough
+ --> $DIR/higher-ranked-auto-trait-11.rs:27:9
+ |
+LL | impl<'a, T> Foo<'a> for MyType<T>
+ | -- lifetime `'a` defined here
+...
+LL | Box::pin(async move { <T as Foo<'a>>::foo().await })
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ coercion requires that `'a` must outlive `'static`
+
+error: implementation of `Send` is not general enough
+ --> $DIR/higher-ranked-auto-trait-11.rs:27:9
+ |
+LL | Box::pin(async move { <T as Foo<'a>>::foo().await })
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Send` is not general enough
+ |
+ = note: `Send` would have to be implemented for the type `<T as Foo<'0>>::Future`, for any lifetime `'0`...
+ = note: ...but `Send` is actually implemented for the type `<T as Foo<'1>>::Future`, for some specific lifetime `'1`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-11.rs b/tests/ui/async-await/higher-ranked-auto-trait-11.rs
new file mode 100644
index 0000000..3aebdd8
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-11.rs
@@ -0,0 +1,31 @@
+// Repro for <https://github.com/rust-lang/rust/issues/60658#issuecomment-1509321859>.
+//@ edition: 2021
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] known-bug: unknown
+//@[no_assumptions] known-bug: #110338
+
+use core::pin::Pin;
+use std::future::Future;
+
+pub trait Foo<'a> {
+ type Future: Future<Output = ()>;
+
+ fn foo() -> Self::Future;
+}
+
+struct MyType<T>(T);
+
+impl<'a, T> Foo<'a> for MyType<T>
+where
+ T: Foo<'a>,
+ T::Future: Send,
+{
+ type Future = Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
+
+ fn foo() -> Self::Future {
+ Box::pin(async move { <T as Foo<'a>>::foo().await })
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-12.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-12.no_assumptions.stderr
new file mode 100644
index 0000000..63e71cb
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-12.no_assumptions.stderr
@@ -0,0 +1,18 @@
+error: implementation of `Robot` is not general enough
+ --> $DIR/higher-ranked-auto-trait-12.rs:31:20
+ |
+LL | let _my_task = this_is_send(async move {
+ | ____________________^
+LL | | let _my_iter = IRobot {
+LL | | id: 32,
+LL | | robot: source,
+LL | | };
+LL | | yield_now().await;
+LL | | });
+ | |______^ implementation of `Robot` is not general enough
+ |
+ = note: `Box<(dyn Robot<Id = u32> + Send + '0)>` must implement `Robot`, for any lifetime `'0`...
+ = note: ...but `Robot` is actually implemented for the type `Box<(dyn Robot<Id = u32> + Send + 'static)>`
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-12.rs b/tests/ui/async-await/higher-ranked-auto-trait-12.rs
new file mode 100644
index 0000000..b1cca5c
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-12.rs
@@ -0,0 +1,40 @@
+// Repro for <https://github.com/rust-lang/rust/issues/71671#issuecomment-848994782>.
+//@ edition: 2021
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] check-pass
+//@[no_assumptions] known-bug: #110338
+
+pub trait Robot {
+ type Id;
+}
+
+pub type DynRobot = Box<dyn Robot<Id = u32> + Send>;
+
+impl Robot for DynRobot {
+ type Id = u32;
+}
+
+struct IRobot<R: Robot> {
+ id: R::Id,
+ robot: R,
+}
+
+// stand-in for tokio::spawn
+fn this_is_send<T: Send>(value: T) -> T {
+ value
+}
+
+async fn yield_now() {}
+
+fn test(source: DynRobot) {
+ let _my_task = this_is_send(async move {
+ let _my_iter = IRobot {
+ id: 32,
+ robot: source,
+ };
+ yield_now().await;
+ });
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-13.assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-13.assumptions.stderr
new file mode 100644
index 0000000..f692187
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-13.assumptions.stderr
@@ -0,0 +1,41 @@
+error: implementation of `Getter` is not general enough
+ --> $DIR/higher-ranked-auto-trait-13.rs:65:5
+ |
+LL | assert_send(my_send_async_method(struct_with_lifetime, data));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough
+ |
+ = note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2`
+
+error: implementation of `Getter` is not general enough
+ --> $DIR/higher-ranked-auto-trait-13.rs:65:5
+ |
+LL | assert_send(my_send_async_method(struct_with_lifetime, data));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough
+ |
+ = note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2`
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: implementation of `Getter` is not general enough
+ --> $DIR/higher-ranked-auto-trait-13.rs:65:5
+ |
+LL | assert_send(my_send_async_method(struct_with_lifetime, data));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough
+ |
+ = note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2`
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: implementation of `Getter` is not general enough
+ --> $DIR/higher-ranked-auto-trait-13.rs:65:5
+ |
+LL | assert_send(my_send_async_method(struct_with_lifetime, data));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough
+ |
+ = note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2`
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-13.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-13.no_assumptions.stderr
new file mode 100644
index 0000000..cfbdaa8
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-13.no_assumptions.stderr
@@ -0,0 +1,60 @@
+error: implementation of `Getter` is not general enough
+ --> $DIR/higher-ranked-auto-trait-13.rs:65:5
+ |
+LL | assert_send(my_send_async_method(struct_with_lifetime, data));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough
+ |
+ = note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2`
+
+error: implementation of `Getter` is not general enough
+ --> $DIR/higher-ranked-auto-trait-13.rs:65:5
+ |
+LL | assert_send(my_send_async_method(struct_with_lifetime, data));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough
+ |
+ = note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2`
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: implementation of `Callable` is not general enough
+ --> $DIR/higher-ranked-auto-trait-13.rs:65:5
+ |
+LL | assert_send(my_send_async_method(struct_with_lifetime, data));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Callable` is not general enough
+ |
+ = note: `Callable<'_>` would have to be implemented for the type `ConstructableImpl<'0>`, for any lifetime `'0`...
+ = note: ...but `Callable<'1>` is actually implemented for the type `ConstructableImpl<'1>`, for some specific lifetime `'1`
+
+error: implementation of `Getter` is not general enough
+ --> $DIR/higher-ranked-auto-trait-13.rs:65:5
+ |
+LL | assert_send(my_send_async_method(struct_with_lifetime, data));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough
+ |
+ = note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2`
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: implementation of `Getter` is not general enough
+ --> $DIR/higher-ranked-auto-trait-13.rs:65:5
+ |
+LL | assert_send(my_send_async_method(struct_with_lifetime, data));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough
+ |
+ = note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2`
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: implementation of `Callable` is not general enough
+ --> $DIR/higher-ranked-auto-trait-13.rs:65:5
+ |
+LL | assert_send(my_send_async_method(struct_with_lifetime, data));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Callable` is not general enough
+ |
+ = note: `Callable<'_>` would have to be implemented for the type `ConstructableImpl<'0>`, for any lifetime `'0`...
+ = note: ...but `Callable<'1>` is actually implemented for the type `ConstructableImpl<'1>`, for some specific lifetime `'1`
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: aborting due to 6 previous errors
+
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-13.rs b/tests/ui/async-await/higher-ranked-auto-trait-13.rs
new file mode 100644
index 0000000..4bce0f5
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-13.rs
@@ -0,0 +1,68 @@
+// Repro for <https://github.com/rust-lang/rust/issues/114046#issue-1819720359>.
+//@ edition: 2021
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] known-bug: unknown
+//@[no_assumptions] known-bug: #110338
+
+use std::marker::PhantomData;
+
+trait Callable<'a>: Send + Sync {
+ fn callable(data: &'a [u8]);
+}
+
+trait Getter<'a>: Send + Sync {
+ type ItemSize: Send + Sync;
+
+ fn get(data: &'a [u8]);
+}
+
+struct List<'a, A: Getter<'a>> {
+ data: &'a [u8],
+ item_size: A::ItemSize, // Removing this member causes the code to compile
+ phantom: PhantomData<A>,
+}
+
+struct GetterImpl<'a, T: Callable<'a> + 'a> {
+ p: PhantomData<&'a T>,
+}
+
+impl<'a, T: Callable<'a> + 'a> Getter<'a> for GetterImpl<'a, T> {
+ type ItemSize = ();
+
+ fn get(data: &'a [u8]) {
+ <T>::callable(data);
+ }
+}
+
+struct ConstructableImpl<'a> {
+ _data: &'a [u8],
+}
+
+impl<'a> Callable<'a> for ConstructableImpl<'a> {
+ fn callable(_: &'a [u8]) {}
+}
+
+struct StructWithLifetime<'a> {
+ marker: &'a PhantomData<u8>,
+}
+
+async fn async_method() {}
+
+fn assert_send(_: impl Send + Sync) {}
+
+// This async method ought to be send, but is not
+async fn my_send_async_method(_struct_with_lifetime: &mut StructWithLifetime<'_>, data: &Vec<u8>) {
+ let _named =
+ List::<'_, GetterImpl<ConstructableImpl<'_>>> { data, item_size: (), phantom: PhantomData };
+ // Moving the await point above the constructed of _named, causes
+ // the method to become send, even though _named is Send + Sync
+ async_method().await;
+ assert_send(_named);
+}
+
+fn dummy(struct_with_lifetime: &mut StructWithLifetime<'_>, data: &Vec<u8>) {
+ assert_send(my_send_async_method(struct_with_lifetime, data));
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-14.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-14.no_assumptions.stderr
new file mode 100644
index 0000000..b47db2b
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-14.no_assumptions.stderr
@@ -0,0 +1,33 @@
+error: implementation of `FnOnce` is not general enough
+ --> $DIR/higher-ranked-auto-trait-14.rs:20:5
+ |
+LL | / async move {
+LL | | let xs = unique_x.union(&cached)
+LL | | // .copied() // works
+LL | | .map(|x| *x) // error
+LL | | ;
+LL | | let blah = val.blah(xs.into_iter()).await;
+LL | | }
+ | |_____^ implementation of `FnOnce` is not general enough
+ |
+ = note: closure with signature `fn(&'0 u32) -> u32` must implement `FnOnce<(&'1 u32,)>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but it actually implements `FnOnce<(&u32,)>`
+
+error: implementation of `FnOnce` is not general enough
+ --> $DIR/higher-ranked-auto-trait-14.rs:20:5
+ |
+LL | / async move {
+LL | | let xs = unique_x.union(&cached)
+LL | | // .copied() // works
+LL | | .map(|x| *x) // error
+LL | | ;
+LL | | let blah = val.blah(xs.into_iter()).await;
+LL | | }
+ | |_____^ implementation of `FnOnce` is not general enough
+ |
+ = note: closure with signature `fn(&'0 u32) -> u32` must implement `FnOnce<(&'1 u32,)>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but it actually implements `FnOnce<(&u32,)>`
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-14.rs b/tests/ui/async-await/higher-ranked-auto-trait-14.rs
new file mode 100644
index 0000000..5ed12cd
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-14.rs
@@ -0,0 +1,29 @@
+// Repro for <https://github.com/rust-lang/rust/issues/124757#issue-2279603232>.
+//@ edition: 2021
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] check-pass
+//@[no_assumptions] known-bug: #110338
+
+use std::collections::HashSet;
+use std::future::Future;
+
+trait MyTrait {
+ fn blah(&self, x: impl Iterator<Item = u32>) -> impl Future<Output = ()> + Send;
+}
+
+fn foo<T: MyTrait + Send + Sync>(
+ val: T,
+ unique_x: HashSet<u32>,
+) -> impl Future<Output = ()> + Send {
+ let cached = HashSet::new();
+ async move {
+ let xs = unique_x.union(&cached)
+ // .copied() // works
+ .map(|x| *x) // error
+ ;
+ let blah = val.blah(xs.into_iter()).await;
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-15.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-15.no_assumptions.stderr
new file mode 100644
index 0000000..b4f3570
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-15.no_assumptions.stderr
@@ -0,0 +1,21 @@
+error: implementation of `FnOnce` is not general enough
+ --> $DIR/higher-ranked-auto-trait-15.rs:20:5
+ |
+LL | require_send(future);
+ | ^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
+ |
+ = note: closure with signature `fn(&'0 Vec<i32>) -> std::slice::Iter<'_, i32>` must implement `FnOnce<(&'1 Vec<i32>,)>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but it actually implements `FnOnce<(&Vec<i32>,)>`
+
+error: implementation of `FnOnce` is not general enough
+ --> $DIR/higher-ranked-auto-trait-15.rs:20:5
+ |
+LL | require_send(future);
+ | ^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
+ |
+ = note: closure with signature `fn(&'0 Vec<i32>) -> std::slice::Iter<'_, i32>` must implement `FnOnce<(&'1 Vec<i32>,)>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but it actually implements `FnOnce<(&Vec<i32>,)>`
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-15.rs b/tests/ui/async-await/higher-ranked-auto-trait-15.rs
new file mode 100644
index 0000000..153fcac
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-15.rs
@@ -0,0 +1,21 @@
+// Repro for <https://github.com/rust-lang/rust/issues/126044#issuecomment-2154313449>.
+//@ edition: 2021
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] check-pass
+//@[no_assumptions] known-bug: #110338
+
+async fn listen() {
+ let things: Vec<Vec<i32>> = vec![];
+ for _ in things.iter().map(|n| n.iter()).flatten() {
+ // comment this line and everything compiles
+ async {}.await;
+ }
+}
+
+fn require_send<T: Send>(_x: T) {}
+
+fn main() {
+ let future = listen();
+ require_send(future);
+}
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-16.assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-16.assumptions.stderr
new file mode 100644
index 0000000..412c31b
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-16.assumptions.stderr
@@ -0,0 +1,25 @@
+error: implementation of `AsyncFnOnce` is not general enough
+ --> $DIR/higher-ranked-auto-trait-16.rs:18:5
+ |
+LL | / assert_send(async {
+LL | | commit_if_ok(&mut ctxt, async |_| todo!()).await;
+LL | | });
+ | |______^ implementation of `AsyncFnOnce` is not general enough
+ |
+ = note: `{async closure@$DIR/higher-ranked-auto-trait-16.rs:19:33: 19:42}` must implement `AsyncFnOnce<(&mut Ctxt<'1>,)>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but it actually implements `AsyncFnOnce<(&mut Ctxt<'_>,)>`
+
+error: implementation of `AsyncFnOnce` is not general enough
+ --> $DIR/higher-ranked-auto-trait-16.rs:18:5
+ |
+LL | / assert_send(async {
+LL | | commit_if_ok(&mut ctxt, async |_| todo!()).await;
+LL | | });
+ | |______^ implementation of `AsyncFnOnce` is not general enough
+ |
+ = note: `{async closure@$DIR/higher-ranked-auto-trait-16.rs:19:33: 19:42}` must implement `AsyncFnOnce<(&mut Ctxt<'1>,)>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but it actually implements `AsyncFnOnce<(&mut Ctxt<'_>,)>`
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-16.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-16.no_assumptions.stderr
new file mode 100644
index 0000000..412c31b
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-16.no_assumptions.stderr
@@ -0,0 +1,25 @@
+error: implementation of `AsyncFnOnce` is not general enough
+ --> $DIR/higher-ranked-auto-trait-16.rs:18:5
+ |
+LL | / assert_send(async {
+LL | | commit_if_ok(&mut ctxt, async |_| todo!()).await;
+LL | | });
+ | |______^ implementation of `AsyncFnOnce` is not general enough
+ |
+ = note: `{async closure@$DIR/higher-ranked-auto-trait-16.rs:19:33: 19:42}` must implement `AsyncFnOnce<(&mut Ctxt<'1>,)>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but it actually implements `AsyncFnOnce<(&mut Ctxt<'_>,)>`
+
+error: implementation of `AsyncFnOnce` is not general enough
+ --> $DIR/higher-ranked-auto-trait-16.rs:18:5
+ |
+LL | / assert_send(async {
+LL | | commit_if_ok(&mut ctxt, async |_| todo!()).await;
+LL | | });
+ | |______^ implementation of `AsyncFnOnce` is not general enough
+ |
+ = note: `{async closure@$DIR/higher-ranked-auto-trait-16.rs:19:33: 19:42}` must implement `AsyncFnOnce<(&mut Ctxt<'1>,)>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but it actually implements `AsyncFnOnce<(&mut Ctxt<'_>,)>`
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-16.rs b/tests/ui/async-await/higher-ranked-auto-trait-16.rs
new file mode 100644
index 0000000..2b206f0
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-16.rs
@@ -0,0 +1,23 @@
+// Repro for <https://github.com/rust-lang/rust/issues/126350#issue-2349492101>.
+//@ edition: 2021
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] known-bug: unknown
+//@[no_assumptions] known-bug: #110338
+
+fn assert_send<T: Send>(_: T) {}
+
+#[derive(Clone)]
+struct Ctxt<'a>(&'a ());
+
+async fn commit_if_ok<'a>(ctxt: &mut Ctxt<'a>, f: impl AsyncFnOnce(&mut Ctxt<'a>)) {
+ f(&mut ctxt.clone()).await;
+}
+
+fn operation(mut ctxt: Ctxt<'_>) {
+ assert_send(async {
+ commit_if_ok(&mut ctxt, async |_| todo!()).await;
+ });
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-17.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-17.no_assumptions.stderr
new file mode 100644
index 0000000..152900c
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-17.no_assumptions.stderr
@@ -0,0 +1,29 @@
+error: implementation of `FnOnce` is not general enough
+ --> $DIR/higher-ranked-auto-trait-17.rs:12:5
+ |
+LL | / async move {
+LL | | let iter = Adaptor::new(a.iter().map(|_: &()| {}));
+LL | | std::future::pending::<()>().await;
+LL | | drop(iter);
+LL | | }
+ | |_____^ implementation of `FnOnce` is not general enough
+ |
+ = note: closure with signature `fn(&'0 ())` must implement `FnOnce<(&'1 (),)>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but it actually implements `FnOnce<(&(),)>`
+
+error: implementation of `FnOnce` is not general enough
+ --> $DIR/higher-ranked-auto-trait-17.rs:12:5
+ |
+LL | / async move {
+LL | | let iter = Adaptor::new(a.iter().map(|_: &()| {}));
+LL | | std::future::pending::<()>().await;
+LL | | drop(iter);
+LL | | }
+ | |_____^ implementation of `FnOnce` is not general enough
+ |
+ = note: closure with signature `fn(&'0 ())` must implement `FnOnce<(&'1 (),)>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but it actually implements `FnOnce<(&(),)>`
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-17.rs b/tests/ui/async-await/higher-ranked-auto-trait-17.rs
new file mode 100644
index 0000000..1524324
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-17.rs
@@ -0,0 +1,30 @@
+// Repro for <https://github.com/rust-lang/rust/issues/114177#issue-1826550174>.
+//@ edition: 2021
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] check-pass
+//@[no_assumptions] known-bug: #110338
+
+// Using `impl Future` instead of `async to ensure that the Future is Send.
+//
+// In the original code `a` would be `&[T]`. For more minimization I've removed the reference.
+fn foo(a: [(); 0]) -> impl std::future::Future<Output = ()> + Send {
+ async move {
+ let iter = Adaptor::new(a.iter().map(|_: &()| {}));
+ std::future::pending::<()>().await;
+ drop(iter);
+ }
+}
+
+struct Adaptor<T: Iterator> {
+ iter: T,
+ v: T::Item,
+}
+
+impl<T: Iterator> Adaptor<T> {
+ pub fn new(_: T) -> Self {
+ Self { iter: todo!(), v: todo!() }
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-2.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-2.no_assumptions.stderr
new file mode 100644
index 0000000..2fc4441
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-2.no_assumptions.stderr
@@ -0,0 +1,49 @@
+error: lifetime bound not satisfied
+ --> $DIR/higher-ranked-auto-trait-2.rs:16:9
+ |
+LL | / async move {
+LL | | // asks for an unspecified lifetime to outlive itself? weird diagnostics
+LL | | self.run(t).await;
+LL | | }
+ | |_________^
+ |
+ = note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
+
+error: lifetime bound not satisfied
+ --> $DIR/higher-ranked-auto-trait-2.rs:16:9
+ |
+LL | / async move {
+LL | | // asks for an unspecified lifetime to outlive itself? weird diagnostics
+LL | | self.run(t).await;
+LL | | }
+ | |_________^
+ |
+ = note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: lifetime bound not satisfied
+ --> $DIR/higher-ranked-auto-trait-2.rs:16:9
+ |
+LL | / async move {
+LL | | // asks for an unspecified lifetime to outlive itself? weird diagnostics
+LL | | self.run(t).await;
+LL | | }
+ | |_________^
+ |
+ = note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: lifetime bound not satisfied
+ --> $DIR/higher-ranked-auto-trait-2.rs:16:9
+ |
+LL | / async move {
+LL | | // asks for an unspecified lifetime to outlive itself? weird diagnostics
+LL | | self.run(t).await;
+LL | | }
+ | |_________^
+ |
+ = note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-2.rs b/tests/ui/async-await/higher-ranked-auto-trait-2.rs
new file mode 100644
index 0000000..6c75597
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-2.rs
@@ -0,0 +1,23 @@
+// Repro for <https://github.com/rust-lang/rust/issues/111105#issue-1692860759>.
+//@ edition: 2021
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] check-pass
+//@[no_assumptions] known-bug: #110338
+
+use std::future::Future;
+
+pub trait Foo: Sync {
+ fn run<'a, 'b: 'a, T: Sync>(&'a self, _: &'b T) -> impl Future<Output = ()> + 'a + Send;
+}
+
+pub trait FooExt: Foo {
+ fn run_via<'a, 'b: 'a, T: Sync>(&'a self, t: &'b T) -> impl Future<Output = ()> + 'a + Send {
+ async move {
+ // asks for an unspecified lifetime to outlive itself? weird diagnostics
+ self.run(t).await;
+ }
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-3.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-3.no_assumptions.stderr
new file mode 100644
index 0000000..c5c98ac
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-3.no_assumptions.stderr
@@ -0,0 +1,12 @@
+error: lifetime bound not satisfied
+ --> $DIR/higher-ranked-auto-trait-3.rs:66:9
+ |
+LL | / async {
+LL | | self.fi_2.get_iter(cx).await;
+LL | | }
+ | |_________^
+ |
+ = note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-3.rs b/tests/ui/async-await/higher-ranked-auto-trait-3.rs
new file mode 100644
index 0000000..d42d423
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-3.rs
@@ -0,0 +1,72 @@
+// Repro for <https://github.com/rust-lang/rust/issues/100013#issue-1323807923>.
+//@ edition: 2021
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] check-pass
+//@[no_assumptions] known-bug: #110338
+
+#![feature(impl_trait_in_assoc_type)]
+
+use std::future::Future;
+
+pub trait FutureIterator: 'static {
+ type Iterator;
+
+ type Future<'s, 'cx>: Future<Output = Self::Iterator> + Send + 'cx
+ where
+ 's: 'cx;
+
+ fn get_iter<'s, 'cx>(&'s self, info: &'cx ()) -> Self::Future<'s, 'cx>;
+}
+
+trait IterCaller: 'static {
+ type Future1<'cx>: Future<Output = ()> + Send + 'cx;
+ type Future2<'cx>: Future<Output = ()> + Send + 'cx;
+
+ fn call_1<'s, 'cx>(&'s self, cx: &'cx ()) -> Self::Future1<'cx>
+ where
+ 's: 'cx;
+ fn call_2<'s, 'cx>(&'s self, cx: &'cx ()) -> Self::Future2<'cx>
+ where
+ 's: 'cx;
+}
+
+struct UseIter<FI1, FI2> {
+ fi_1: FI1,
+ fi_2: FI2,
+}
+
+impl<FI1, FI2> IterCaller for UseIter<FI1, FI2>
+where
+ FI1: FutureIterator + 'static + Send + Sync,
+ for<'s, 'cx> FI1::Future<'s, 'cx>: Send,
+ FI2: FutureIterator + 'static + Send + Sync,
+{
+ type Future1<'cx> = impl Future<Output = ()> + Send + 'cx
+ where
+ Self: 'cx;
+
+ type Future2<'cx> = impl Future<Output = ()> + Send + 'cx
+ where
+ Self: 'cx;
+
+ fn call_1<'s, 'cx>(&'s self, cx: &'cx ()) -> Self::Future1<'cx>
+ where
+ 's: 'cx,
+ {
+ async {
+ self.fi_1.get_iter(cx).await;
+ }
+ }
+
+ fn call_2<'s, 'cx>(&'s self, cx: &'cx ()) -> Self::Future2<'cx>
+ where
+ 's: 'cx,
+ {
+ async {
+ self.fi_2.get_iter(cx).await;
+ }
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-4.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-4.no_assumptions.stderr
new file mode 100644
index 0000000..5aa5b83
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-4.no_assumptions.stderr
@@ -0,0 +1,10 @@
+error: higher-ranked lifetime error
+ --> $DIR/higher-ranked-auto-trait-4.rs:29:5
+ |
+LL | / async {
+LL | | let _ = evil_function::<dyn BoringTrait, _>().await;
+LL | | }
+ | |_____^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-4.rs b/tests/ui/async-await/higher-ranked-auto-trait-4.rs
new file mode 100644
index 0000000..0586af4
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-4.rs
@@ -0,0 +1,34 @@
+// Repro for <https://github.com/rust-lang/rust/issues/102211#issuecomment-2891975128>.
+//@ edition: 2021
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] check-pass
+//@[no_assumptions] known-bug: #110338
+
+use std::future::Future;
+
+trait BoringTrait {}
+
+trait TraitWithAssocType<I> {
+ type Assoc;
+}
+
+impl<T> TraitWithAssocType<()> for T
+where
+ T: ?Sized + 'static,
+{
+ type Assoc = ();
+}
+
+fn evil_function<T: TraitWithAssocType<I> + ?Sized, I>()
+-> impl Future<Output = Result<(), T::Assoc>> {
+ async { Ok(()) }
+}
+
+fn fails_to_compile() -> impl std::future::Future<Output = ()> + Send {
+ async {
+ let _ = evil_function::<dyn BoringTrait, _>().await;
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-5.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-5.no_assumptions.stderr
new file mode 100644
index 0000000..8fa3c74
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-5.no_assumptions.stderr
@@ -0,0 +1,13 @@
+error: implementation of `Send` is not general enough
+ --> $DIR/higher-ranked-auto-trait-5.rs:13:5
+ |
+LL | / assert_send(async {
+LL | | call_me.call().await;
+LL | | });
+ | |______^ implementation of `Send` is not general enough
+ |
+ = note: `Send` would have to be implemented for the type `&'0 str`, for any lifetime `'0`...
+ = note: ...but `Send` is actually implemented for the type `&'1 str`, for some specific lifetime `'1`
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-5.rs b/tests/ui/async-await/higher-ranked-auto-trait-5.rs
new file mode 100644
index 0000000..9a8b3f4
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-5.rs
@@ -0,0 +1,54 @@
+// Repro for <https://github.com/rust-lang/rust/issues/130113#issue-2512517191>.
+//@ edition: 2021
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] check-pass
+//@[no_assumptions] known-bug: #110338
+
+use std::future::Future;
+
+fn main() {
+ let call_me = Wrap(CallMeImpl { value: "test" });
+
+ assert_send(async {
+ call_me.call().await;
+ });
+}
+
+pub fn assert_send<F>(_future: F)
+where
+ F: Future + Send,
+{
+}
+
+pub trait CallMe {
+ fn call(&self) -> impl Future<Output = ()> + Send;
+}
+
+struct Wrap<T>(T);
+
+impl<S> CallMe for Wrap<S>
+where
+ S: CallMe + Send,
+{
+ // adding `+ Send` to this RPIT fixes the issue
+ fn call(&self) -> impl Future<Output = ()> {
+ self.0.call()
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct CallMeImpl<T> {
+ value: T,
+}
+
+impl<T> CallMe for CallMeImpl<T>
+where
+ // Can replace `Send` by `ToString`, `Clone`, whatever. When removing the
+ // `Send` bound, the compiler produces a higher-ranked lifetime error.
+ T: Send + 'static,
+{
+ fn call(&self) -> impl Future<Output = ()> {
+ async {}
+ }
+}
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-6.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-6.no_assumptions.stderr
new file mode 100644
index 0000000..d1f2d9a
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-6.no_assumptions.stderr
@@ -0,0 +1,50 @@
+error[E0308]: mismatched types
+ --> $DIR/higher-ranked-auto-trait-6.rs:16:5
+ |
+LL | Box::new(async { new(|| async { f().await }).await })
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
+ |
+ = note: expected `async` block `{async block@$DIR/higher-ranked-auto-trait-6.rs:16:29: 16:34}`
+ found `async` block `{async block@$DIR/higher-ranked-auto-trait-6.rs:16:29: 16:34}`
+ = note: no two async blocks, even if identical, have the same type
+ = help: consider pinning your async block and casting it to a trait object
+
+error[E0308]: mismatched types
+ --> $DIR/higher-ranked-auto-trait-6.rs:16:5
+ |
+LL | Box::new(async { new(|| async { f().await }).await })
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
+ |
+ = note: expected `async` block `{async block@$DIR/higher-ranked-auto-trait-6.rs:16:29: 16:34}`
+ found `async` block `{async block@$DIR/higher-ranked-auto-trait-6.rs:16:29: 16:34}`
+ = note: no two async blocks, even if identical, have the same type
+ = help: consider pinning your async block and casting it to a trait object
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error[E0308]: mismatched types
+ --> $DIR/higher-ranked-auto-trait-6.rs:16:5
+ |
+LL | Box::new(async { new(|| async { f().await }).await })
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
+ |
+ = note: expected `async` block `{async block@$DIR/higher-ranked-auto-trait-6.rs:16:29: 16:34}`
+ found `async` block `{async block@$DIR/higher-ranked-auto-trait-6.rs:16:29: 16:34}`
+ = note: no two async blocks, even if identical, have the same type
+ = help: consider pinning your async block and casting it to a trait object
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error[E0308]: mismatched types
+ --> $DIR/higher-ranked-auto-trait-6.rs:16:5
+ |
+LL | Box::new(async { new(|| async { f().await }).await })
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
+ |
+ = note: expected `async` block `{async block@$DIR/higher-ranked-auto-trait-6.rs:16:29: 16:34}`
+ found `async` block `{async block@$DIR/higher-ranked-auto-trait-6.rs:16:29: 16:34}`
+ = note: no two async blocks, even if identical, have the same type
+ = help: consider pinning your async block and casting it to a trait object
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-6.rs b/tests/ui/async-await/higher-ranked-auto-trait-6.rs
new file mode 100644
index 0000000..2c6adf8
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-6.rs
@@ -0,0 +1,59 @@
+// Repro for <https://github.com/rust-lang/rust/issues/82921#issue-825114180>.
+//@ edition: 2021
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] check-pass
+//@[no_assumptions] known-bug: #110338
+
+use core::future::Future;
+use core::marker::PhantomData;
+use core::pin::Pin;
+use core::task::{Context, Poll};
+
+async fn f() {}
+
+pub fn fail<'a>() -> Box<dyn Future<Output = ()> + Send + 'a> {
+ Box::new(async { new(|| async { f().await }).await })
+}
+
+fn new<A, B>(_a: A) -> F<A, B>
+where
+ A: Fn() -> B,
+{
+ F { _i: PhantomData }
+}
+
+trait Stream {
+ type Item;
+}
+
+struct T<A, B> {
+ _a: PhantomData<A>,
+ _b: PhantomData<B>,
+}
+
+impl<A, B> Stream for T<A, B>
+where
+ A: Fn() -> B,
+{
+ type Item = B;
+}
+
+struct F<A, B>
+where
+ A: Fn() -> B,
+{
+ _i: PhantomData<<T<A, B> as Stream>::Item>,
+}
+
+impl<A, B> Future for F<A, B>
+where
+ A: Fn() -> B,
+{
+ type Output = ();
+ fn poll(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Self::Output> {
+ unimplemented!()
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-7.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-7.no_assumptions.stderr
new file mode 100644
index 0000000..dcb8075
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-7.no_assumptions.stderr
@@ -0,0 +1,8 @@
+error: `S` does not live long enough
+ --> $DIR/higher-ranked-auto-trait-7.rs:26:5
+ |
+LL | future::<'a, S, _>(async move {
+ | ^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-7.rs b/tests/ui/async-await/higher-ranked-auto-trait-7.rs
new file mode 100644
index 0000000..abded5a
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-7.rs
@@ -0,0 +1,33 @@
+// Repro for <https://github.com/rust-lang/rust/issues/90696#issuecomment-963375847>.
+//@ edition: 2021
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] check-pass
+//@[no_assumptions] known-bug: #110338
+
+#![allow(dropping_copy_types)]
+
+use std::{future::Future, marker::PhantomData};
+
+trait Trait {
+ type Associated<'a>: Send
+ where
+ Self: 'a;
+}
+
+fn future<'a, S: Trait + 'a, F>(f: F) -> F
+where
+ F: Future<Output = ()> + Send,
+{
+ f
+}
+
+fn foo<'a, S: Trait + 'a>() {
+ future::<'a, S, _>(async move {
+ let result: PhantomData<S::Associated<'a>> = PhantomData;
+ async {}.await;
+ drop(result);
+ });
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-8.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-8.no_assumptions.stderr
new file mode 100644
index 0000000..6208675
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-8.no_assumptions.stderr
@@ -0,0 +1,10 @@
+error: higher-ranked lifetime error
+ --> $DIR/higher-ranked-auto-trait-8.rs:26:5
+ |
+LL | needs_send(use_my_struct(second_struct)); // ERROR
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: could not prove `impl Future<Output = ()>: Send`
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-8.rs b/tests/ui/async-await/higher-ranked-auto-trait-8.rs
new file mode 100644
index 0000000..91cef20
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-8.rs
@@ -0,0 +1,27 @@
+// Repro for <https://github.com/rust-lang/rust/issues/64552#issuecomment-532801232>.
+//@ edition: 2021
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] check-pass
+//@[no_assumptions] known-bug: #110338
+
+fn needs_send<T: Send>(_val: T) {}
+async fn use_async<T>(_val: T) {}
+
+struct MyStruct<'a, T: 'a> {
+ val: &'a T,
+}
+
+unsafe impl<'a, T: 'a> Send for MyStruct<'a, T> {}
+
+async fn use_my_struct(val: MyStruct<'static, &'static u8>) {
+ use_async(val).await;
+}
+
+fn main() {
+ let first_struct: MyStruct<'static, &'static u8> = MyStruct { val: &&26 };
+ let second_struct: MyStruct<'static, &'static u8> = MyStruct { val: &&27 };
+
+ needs_send(first_struct);
+ needs_send(use_my_struct(second_struct)); // ERROR
+}
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-9.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-9.no_assumptions.stderr
new file mode 100644
index 0000000..809cbcf
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-9.no_assumptions.stderr
@@ -0,0 +1,11 @@
+error: implementation of `Debug` is not general enough
+ --> $DIR/higher-ranked-auto-trait-9.rs:43:50
+ |
+LL | let fut: &(dyn Future<Output = ()> + Send) = &fut as _;
+ | ^^^^^^^^^ implementation of `Debug` is not general enough
+ |
+ = note: `(dyn Any + '0)` must implement `Debug`, for any lifetime `'0`...
+ = note: ...but `Debug` is actually implemented for the type `(dyn Any + 'static)`
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/async-await/higher-ranked-auto-trait-9.rs b/tests/ui/async-await/higher-ranked-auto-trait-9.rs
new file mode 100644
index 0000000..66edbf2
--- /dev/null
+++ b/tests/ui/async-await/higher-ranked-auto-trait-9.rs
@@ -0,0 +1,44 @@
+// Repro for <https://github.com/rust-lang/rust/issues/87425#issue-952059416>.
+//@ edition: 2021
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] check-pass
+//@[no_assumptions] known-bug: #110338
+
+use std::any::Any;
+use std::fmt;
+use std::future::Future;
+
+pub trait Foo {
+ type Item;
+}
+
+impl<F, I> Foo for F
+where
+ Self: FnOnce() -> I,
+ I: fmt::Debug,
+{
+ type Item = I;
+}
+
+async fn foo_item<F: Foo>(_: F) -> F::Item {
+ unimplemented!()
+}
+
+fn main() {
+ let fut = async {
+ let callback = || -> Box<dyn Any> { unimplemented!() };
+
+ // Using plain fn instead of a closure fixes the error,
+ // though you obviously can't capture any state...
+ // fn callback() -> Box<dyn Any> {
+ // todo!()
+ // }
+
+ foo_item(callback).await;
+ };
+
+ // Removing `+ Send` bound also fixes the error,
+ // though at the cost of loosing `Send`ability...
+ let fut: &(dyn Future<Output = ()> + Send) = &fut as _;
+}
diff --git a/tests/ui/async-await/return-type-notation/issue-110963-early.stderr b/tests/ui/async-await/return-type-notation/issue-110963-early.no_assumptions.stderr
similarity index 94%
rename from tests/ui/async-await/return-type-notation/issue-110963-early.stderr
rename to tests/ui/async-await/return-type-notation/issue-110963-early.no_assumptions.stderr
index d6c3bd12..bb43636 100644
--- a/tests/ui/async-await/return-type-notation/issue-110963-early.stderr
+++ b/tests/ui/async-await/return-type-notation/issue-110963-early.no_assumptions.stderr
@@ -1,5 +1,5 @@
error: implementation of `Send` is not general enough
- --> $DIR/issue-110963-early.rs:14:5
+ --> $DIR/issue-110963-early.rs:17:5
|
LL | / spawn(async move {
LL | | let mut hc = hc;
@@ -13,7 +13,7 @@
= note: ...but `Send` is actually implemented for the type `impl Future<Output = bool> { <HC as HealthCheck>::check<'2>(..) }`, for some specific lifetime `'2`
error: implementation of `Send` is not general enough
- --> $DIR/issue-110963-early.rs:14:5
+ --> $DIR/issue-110963-early.rs:17:5
|
LL | / spawn(async move {
LL | | let mut hc = hc;
diff --git a/tests/ui/async-await/return-type-notation/issue-110963-early.rs b/tests/ui/async-await/return-type-notation/issue-110963-early.rs
index 46b8fbf..8c3180c 100644
--- a/tests/ui/async-await/return-type-notation/issue-110963-early.rs
+++ b/tests/ui/async-await/return-type-notation/issue-110963-early.rs
@@ -1,5 +1,8 @@
//@ edition: 2021
-//@ known-bug: #110963
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] check-pass
+//@[no_assumptions] known-bug: #110338
#![feature(return_type_notation)]
diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr
index 56f2be3..0d0c338 100644
--- a/tests/ui/attributes/malformed-attrs.stderr
+++ b/tests/ui/attributes/malformed-attrs.stderr
@@ -37,19 +37,6 @@
LL | #[crate_name]
| ^^^^^^^^^^^^^ help: must be of the form: `#[crate_name = "name"]`
-error: malformed `coverage` attribute input
- --> $DIR/malformed-attrs.rs:90:1
- |
-LL | #[coverage]
- | ^^^^^^^^^^^
- |
-help: the following are the possible correct uses
- |
-LL | #[coverage(off)]
- | +++++
-LL | #[coverage(on)]
- | ++++
-
error: malformed `no_sanitize` attribute input
--> $DIR/malformed-attrs.rs:92:1
|
@@ -460,6 +447,19 @@
LL | #[link_section]
| ^^^^^^^^^^^^^^^ help: must be of the form: `#[link_section = "name"]`
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/malformed-attrs.rs:90:1
+ |
+LL | #[coverage]
+ | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
+ |
+help: try changing it to one of the following valid forms of the attribute
+ |
+LL | #[coverage(off)]
+ | +++++
+LL | #[coverage(on)]
+ | ++++
+
error[E0565]: malformed `no_implicit_prelude` attribute input
--> $DIR/malformed-attrs.rs:97:1
|
diff --git a/tests/ui/consts/const-compare-bytes-ub.rs b/tests/ui/consts/const-compare-bytes-ub.rs
index 0bc8585..7e3df92 100644
--- a/tests/ui/consts/const-compare-bytes-ub.rs
+++ b/tests/ui/consts/const-compare-bytes-ub.rs
@@ -1,6 +1,6 @@
//@ check-fail
-#![feature(core_intrinsics)]
+#![feature(core_intrinsics, const_cmp)]
use std::intrinsics::compare_bytes;
use std::mem::MaybeUninit;
diff --git a/tests/ui/consts/const-compare-bytes.rs b/tests/ui/consts/const-compare-bytes.rs
index cd5cdfd..9563375 100644
--- a/tests/ui/consts/const-compare-bytes.rs
+++ b/tests/ui/consts/const-compare-bytes.rs
@@ -1,6 +1,6 @@
//@ run-pass
-#![feature(core_intrinsics)]
+#![feature(core_intrinsics, const_cmp)]
use std::intrinsics::compare_bytes;
fn main() {
diff --git a/tests/ui/consts/const-eval/auxiliary/stability.rs b/tests/ui/consts/const-eval/auxiliary/stability.rs
index e615955..48ced3b 100644
--- a/tests/ui/consts/const-eval/auxiliary/stability.rs
+++ b/tests/ui/consts/const-eval/auxiliary/stability.rs
@@ -1,10 +1,11 @@
// Crate that exports a const fn. Used for testing cross-crate.
-#![crate_type="rlib"]
+#![crate_type = "rlib"]
#![stable(feature = "rust1", since = "1.0.0")]
-
#![feature(staged_api)]
#[stable(feature = "rust1", since = "1.0.0")]
-#[rustc_const_unstable(feature="foo", issue = "none")]
-pub const fn foo() -> u32 { 42 }
+#[rustc_const_unstable(feature = "foo", issue = "none")]
+pub const fn foo() -> u32 {
+ 42
+}
diff --git a/tests/ui/coroutine/clone-impl.rs b/tests/ui/coroutine/clone-impl.rs
index b07fad1..e528f03 100644
--- a/tests/ui/coroutine/clone-impl.rs
+++ b/tests/ui/coroutine/clone-impl.rs
@@ -38,39 +38,40 @@ fn test2() {
check_clone(&gen_copy_1);
}
-fn test3() {
+fn test3_upvars() {
let clonable_0: Vec<u32> = Vec::new();
let gen_clone_0 = #[coroutine]
move || {
- let v = vec!['a'];
- yield;
- drop(v);
drop(clonable_0);
};
check_copy(&gen_clone_0);
//~^ ERROR the trait bound `Vec<u32>: Copy` is not satisfied
- //~| ERROR the trait bound `Vec<char>: Copy` is not satisfied
check_clone(&gen_clone_0);
}
+fn test3_witness() {
+ let gen_clone_1 = #[coroutine]
+ move || {
+ let v = vec!['a'];
+ yield;
+ drop(v);
+ };
+ check_copy(&gen_clone_1);
+ //~^ ERROR the trait bound `Vec<char>: Copy` is not satisfied
+ check_clone(&gen_clone_1);
+}
+
fn test4() {
let clonable_1: Vec<u32> = Vec::new();
let gen_clone_1 = #[coroutine]
move || {
- let v = vec!['a'];
- /*
- let n = NonClone;
- drop(n);
- */
yield;
let n = NonClone;
drop(n);
- drop(v);
drop(clonable_1);
};
check_copy(&gen_clone_1);
//~^ ERROR the trait bound `Vec<u32>: Copy` is not satisfied
- //~| ERROR the trait bound `Vec<char>: Copy` is not satisfied
check_clone(&gen_clone_1);
}
diff --git a/tests/ui/coroutine/clone-impl.stderr b/tests/ui/coroutine/clone-impl.stderr
index ed933fe..714e5aa 100644
--- a/tests/ui/coroutine/clone-impl.stderr
+++ b/tests/ui/coroutine/clone-impl.stderr
@@ -1,104 +1,59 @@
error[E0277]: the trait bound `Vec<u32>: Copy` is not satisfied in `{coroutine@$DIR/clone-impl.rs:44:5: 44:12}`
- --> $DIR/clone-impl.rs:50:5
+ --> $DIR/clone-impl.rs:47:16
|
LL | move || {
| ------- within this `{coroutine@$DIR/clone-impl.rs:44:5: 44:12}`
...
LL | check_copy(&gen_clone_0);
- | ^^^^^^^^^^^^^^^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:44:5: 44:12}`, the trait `Copy` is not implemented for `Vec<u32>`
+ | ^^^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:44:5: 44:12}`, the trait `Copy` is not implemented for `Vec<u32>`
|
note: captured value does not implement `Copy`
- --> $DIR/clone-impl.rs:48:14
+ --> $DIR/clone-impl.rs:45:14
|
LL | drop(clonable_0);
| ^^^^^^^^^^ has type `Vec<u32>` which does not implement `Copy`
note: required by a bound in `check_copy`
- --> $DIR/clone-impl.rs:90:18
+ --> $DIR/clone-impl.rs:91:18
|
LL | fn check_copy<T: Copy>(_x: &T) {}
| ^^^^ required by this bound in `check_copy`
-error[E0277]: the trait bound `Vec<char>: Copy` is not satisfied in `{coroutine@$DIR/clone-impl.rs:44:5: 44:12}`
- --> $DIR/clone-impl.rs:50:5
+error[E0277]: the trait bound `Vec<u32>: Copy` is not satisfied in `{coroutine@$DIR/clone-impl.rs:67:5: 67:12}`
+ --> $DIR/clone-impl.rs:73:16
|
LL | move || {
- | ------- within this `{coroutine@$DIR/clone-impl.rs:44:5: 44:12}`
-...
-LL | check_copy(&gen_clone_0);
- | ^^^^^^^^^^^^^^^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:44:5: 44:12}`, the trait `Copy` is not implemented for `Vec<char>`
- |
-note: coroutine does not implement `Copy` as this value is used across a yield
- --> $DIR/clone-impl.rs:46:9
- |
-LL | let v = vec!['a'];
- | - has type `Vec<char>` which does not implement `Copy`
-LL | yield;
- | ^^^^^ yield occurs here, with `v` maybe used later
-note: required by a bound in `check_copy`
- --> $DIR/clone-impl.rs:90:18
- |
-LL | fn check_copy<T: Copy>(_x: &T) {}
- | ^^^^ required by this bound in `check_copy`
-
-error[E0277]: the trait bound `Vec<u32>: Copy` is not satisfied in `{coroutine@$DIR/clone-impl.rs:59:5: 59:12}`
- --> $DIR/clone-impl.rs:71:5
- |
-LL | move || {
- | ------- within this `{coroutine@$DIR/clone-impl.rs:59:5: 59:12}`
+ | ------- within this `{coroutine@$DIR/clone-impl.rs:67:5: 67:12}`
...
LL | check_copy(&gen_clone_1);
- | ^^^^^^^^^^^^^^^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:59:5: 59:12}`, the trait `Copy` is not implemented for `Vec<u32>`
+ | ^^^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:67:5: 67:12}`, the trait `Copy` is not implemented for `Vec<u32>`
|
note: captured value does not implement `Copy`
- --> $DIR/clone-impl.rs:69:14
+ --> $DIR/clone-impl.rs:71:14
|
LL | drop(clonable_1);
| ^^^^^^^^^^ has type `Vec<u32>` which does not implement `Copy`
note: required by a bound in `check_copy`
- --> $DIR/clone-impl.rs:90:18
+ --> $DIR/clone-impl.rs:91:18
|
LL | fn check_copy<T: Copy>(_x: &T) {}
| ^^^^ required by this bound in `check_copy`
-error[E0277]: the trait bound `Vec<char>: Copy` is not satisfied in `{coroutine@$DIR/clone-impl.rs:59:5: 59:12}`
- --> $DIR/clone-impl.rs:71:5
+error[E0277]: the trait bound `NonClone: Copy` is not satisfied in `{coroutine@$DIR/clone-impl.rs:81:5: 81:12}`
+ --> $DIR/clone-impl.rs:85:16
|
LL | move || {
- | ------- within this `{coroutine@$DIR/clone-impl.rs:59:5: 59:12}`
-...
-LL | check_copy(&gen_clone_1);
- | ^^^^^^^^^^^^^^^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:59:5: 59:12}`, the trait `Copy` is not implemented for `Vec<char>`
- |
-note: coroutine does not implement `Copy` as this value is used across a yield
- --> $DIR/clone-impl.rs:65:9
- |
-LL | let v = vec!['a'];
- | - has type `Vec<char>` which does not implement `Copy`
-...
-LL | yield;
- | ^^^^^ yield occurs here, with `v` maybe used later
-note: required by a bound in `check_copy`
- --> $DIR/clone-impl.rs:90:18
- |
-LL | fn check_copy<T: Copy>(_x: &T) {}
- | ^^^^ required by this bound in `check_copy`
-
-error[E0277]: the trait bound `NonClone: Copy` is not satisfied in `{coroutine@$DIR/clone-impl.rs:80:5: 80:12}`
- --> $DIR/clone-impl.rs:84:5
- |
-LL | move || {
- | ------- within this `{coroutine@$DIR/clone-impl.rs:80:5: 80:12}`
+ | ------- within this `{coroutine@$DIR/clone-impl.rs:81:5: 81:12}`
...
LL | check_copy(&gen_non_clone);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:80:5: 80:12}`, the trait `Copy` is not implemented for `NonClone`
+ | ^^^^^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:81:5: 81:12}`, the trait `Copy` is not implemented for `NonClone`
|
note: captured value does not implement `Copy`
- --> $DIR/clone-impl.rs:82:14
+ --> $DIR/clone-impl.rs:83:14
|
LL | drop(non_clonable);
| ^^^^^^^^^^^^ has type `NonClone` which does not implement `Copy`
note: required by a bound in `check_copy`
- --> $DIR/clone-impl.rs:90:18
+ --> $DIR/clone-impl.rs:91:18
|
LL | fn check_copy<T: Copy>(_x: &T) {}
| ^^^^ required by this bound in `check_copy`
@@ -108,22 +63,22 @@
LL | struct NonClone;
|
-error[E0277]: the trait bound `NonClone: Clone` is not satisfied in `{coroutine@$DIR/clone-impl.rs:80:5: 80:12}`
- --> $DIR/clone-impl.rs:86:5
+error[E0277]: the trait bound `NonClone: Clone` is not satisfied in `{coroutine@$DIR/clone-impl.rs:81:5: 81:12}`
+ --> $DIR/clone-impl.rs:87:17
|
LL | move || {
- | ------- within this `{coroutine@$DIR/clone-impl.rs:80:5: 80:12}`
+ | ------- within this `{coroutine@$DIR/clone-impl.rs:81:5: 81:12}`
...
LL | check_clone(&gen_non_clone);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:80:5: 80:12}`, the trait `Clone` is not implemented for `NonClone`
+ | ^^^^^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:81:5: 81:12}`, the trait `Clone` is not implemented for `NonClone`
|
note: captured value does not implement `Clone`
- --> $DIR/clone-impl.rs:82:14
+ --> $DIR/clone-impl.rs:83:14
|
LL | drop(non_clonable);
| ^^^^^^^^^^^^ has type `NonClone` which does not implement `Clone`
note: required by a bound in `check_clone`
- --> $DIR/clone-impl.rs:91:19
+ --> $DIR/clone-impl.rs:92:19
|
LL | fn check_clone<T: Clone>(_x: &T) {}
| ^^^^^ required by this bound in `check_clone`
@@ -133,6 +88,28 @@
LL | struct NonClone;
|
-error: aborting due to 6 previous errors
+error[E0277]: the trait bound `Vec<char>: Copy` is not satisfied in `{coroutine@$DIR/clone-impl.rs:54:5: 54:12}`
+ --> $DIR/clone-impl.rs:59:5
+ |
+LL | move || {
+ | ------- within this `{coroutine@$DIR/clone-impl.rs:54:5: 54:12}`
+...
+LL | check_copy(&gen_clone_1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:54:5: 54:12}`, the trait `Copy` is not implemented for `Vec<char>`
+ |
+note: coroutine does not implement `Copy` as this value is used across a yield
+ --> $DIR/clone-impl.rs:56:9
+ |
+LL | let v = vec!['a'];
+ | - has type `Vec<char>` which does not implement `Copy`
+LL | yield;
+ | ^^^^^ yield occurs here, with `v` maybe used later
+note: required by a bound in `check_copy`
+ --> $DIR/clone-impl.rs:91:18
+ |
+LL | fn check_copy<T: Copy>(_x: &T) {}
+ | ^^^^ required by this bound in `check_copy`
+
+error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/coroutine/print/coroutine-print-verbose-3.stderr b/tests/ui/coroutine/print/coroutine-print-verbose-3.stderr
index 2f9f20c..4a1e5b0 100644
--- a/tests/ui/coroutine/print/coroutine-print-verbose-3.stderr
+++ b/tests/ui/coroutine/print/coroutine-print-verbose-3.stderr
@@ -11,7 +11,7 @@
| |_____^ expected `()`, found coroutine
|
= note: expected unit type `()`
- found coroutine `{main::{closure#0} upvar_tys=?4t resume_ty=() yield_ty=i32 return_ty=&'?1 str witness=?6t}`
+ found coroutine `{main::{closure#0} upvar_tys=?4t resume_ty=() yield_ty=i32 return_ty=&'?1 str witness={main::{closure#0}}}`
error: aborting due to 1 previous error
diff --git a/tests/ui/coverage-attr/bad-attr-ice.feat.stderr b/tests/ui/coverage-attr/bad-attr-ice.feat.stderr
index dc84394..5a003af 100644
--- a/tests/ui/coverage-attr/bad-attr-ice.feat.stderr
+++ b/tests/ui/coverage-attr/bad-attr-ice.feat.stderr
@@ -1,10 +1,10 @@
-error: malformed `coverage` attribute input
+error[E0539]: malformed `coverage` attribute input
--> $DIR/bad-attr-ice.rs:11:1
|
LL | #[coverage]
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL | #[coverage(off)]
| +++++
@@ -13,3 +13,4 @@
error: aborting due to 1 previous error
+For more information about this error, try `rustc --explain E0539`.
diff --git a/tests/ui/coverage-attr/bad-attr-ice.nofeat.stderr b/tests/ui/coverage-attr/bad-attr-ice.nofeat.stderr
index 49b8974..4501e5e 100644
--- a/tests/ui/coverage-attr/bad-attr-ice.nofeat.stderr
+++ b/tests/ui/coverage-attr/bad-attr-ice.nofeat.stderr
@@ -1,16 +1,3 @@
-error: malformed `coverage` attribute input
- --> $DIR/bad-attr-ice.rs:11:1
- |
-LL | #[coverage]
- | ^^^^^^^^^^^
- |
-help: the following are the possible correct uses
- |
-LL | #[coverage(off)]
- | +++++
-LL | #[coverage(on)]
- | ++++
-
error[E0658]: the `#[coverage]` attribute is an experimental feature
--> $DIR/bad-attr-ice.rs:11:1
|
@@ -21,6 +8,20 @@
= help: add `#![feature(coverage_attribute)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/bad-attr-ice.rs:11:1
+ |
+LL | #[coverage]
+ | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
+ |
+help: try changing it to one of the following valid forms of the attribute
+ |
+LL | #[coverage(off)]
+ | +++++
+LL | #[coverage(on)]
+ | ++++
+
error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0658`.
+Some errors have detailed explanations: E0539, E0658.
+For more information about an error, try `rustc --explain E0539`.
diff --git a/tests/ui/coverage-attr/bad-syntax.stderr b/tests/ui/coverage-attr/bad-syntax.stderr
index fa500b5..927f61d 100644
--- a/tests/ui/coverage-attr/bad-syntax.stderr
+++ b/tests/ui/coverage-attr/bad-syntax.stderr
@@ -1,119 +1,3 @@
-error: malformed `coverage` attribute input
- --> $DIR/bad-syntax.rs:17:1
- |
-LL | #[coverage]
- | ^^^^^^^^^^^
- |
-help: the following are the possible correct uses
- |
-LL | #[coverage(off)]
- | +++++
-LL | #[coverage(on)]
- | ++++
-
-error: malformed `coverage` attribute input
- --> $DIR/bad-syntax.rs:20:1
- |
-LL | #[coverage = true]
- | ^^^^^^^^^^^^^^^^^^
- |
-help: the following are the possible correct uses
- |
-LL - #[coverage = true]
-LL + #[coverage(off)]
- |
-LL - #[coverage = true]
-LL + #[coverage(on)]
- |
-
-error: malformed `coverage` attribute input
- --> $DIR/bad-syntax.rs:23:1
- |
-LL | #[coverage()]
- | ^^^^^^^^^^^^^
- |
-help: the following are the possible correct uses
- |
-LL | #[coverage(off)]
- | +++
-LL | #[coverage(on)]
- | ++
-
-error: malformed `coverage` attribute input
- --> $DIR/bad-syntax.rs:26:1
- |
-LL | #[coverage(off, off)]
- | ^^^^^^^^^^^^^^^^^^^^^
- |
-help: the following are the possible correct uses
- |
-LL - #[coverage(off, off)]
-LL + #[coverage(off)]
- |
-LL - #[coverage(off, off)]
-LL + #[coverage(on)]
- |
-
-error: malformed `coverage` attribute input
- --> $DIR/bad-syntax.rs:29:1
- |
-LL | #[coverage(off, on)]
- | ^^^^^^^^^^^^^^^^^^^^
- |
-help: the following are the possible correct uses
- |
-LL - #[coverage(off, on)]
-LL + #[coverage(off)]
- |
-LL - #[coverage(off, on)]
-LL + #[coverage(on)]
- |
-
-error: malformed `coverage` attribute input
- --> $DIR/bad-syntax.rs:32:1
- |
-LL | #[coverage(bogus)]
- | ^^^^^^^^^^^^^^^^^^
- |
-help: the following are the possible correct uses
- |
-LL - #[coverage(bogus)]
-LL + #[coverage(off)]
- |
-LL - #[coverage(bogus)]
-LL + #[coverage(on)]
- |
-
-error: malformed `coverage` attribute input
- --> $DIR/bad-syntax.rs:35:1
- |
-LL | #[coverage(bogus, off)]
- | ^^^^^^^^^^^^^^^^^^^^^^^
- |
-help: the following are the possible correct uses
- |
-LL - #[coverage(bogus, off)]
-LL + #[coverage(off)]
- |
-LL - #[coverage(bogus, off)]
-LL + #[coverage(on)]
- |
-
-error: malformed `coverage` attribute input
- --> $DIR/bad-syntax.rs:38:1
- |
-LL | #[coverage(off, bogus)]
- | ^^^^^^^^^^^^^^^^^^^^^^^
- |
-help: the following are the possible correct uses
- |
-LL - #[coverage(off, bogus)]
-LL + #[coverage(off)]
- |
-LL - #[coverage(off, bogus)]
-LL + #[coverage(on)]
- |
-
error: expected identifier, found `,`
--> $DIR/bad-syntax.rs:44:12
|
@@ -150,5 +34,135 @@
LL | #[coverage(on)]
| ^^^^^^^^^^^^^^^
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/bad-syntax.rs:17:1
+ |
+LL | #[coverage]
+ | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
+ |
+help: try changing it to one of the following valid forms of the attribute
+ |
+LL | #[coverage(off)]
+ | +++++
+LL | #[coverage(on)]
+ | ++++
+
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/bad-syntax.rs:20:1
+ |
+LL | #[coverage = true]
+ | ^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
+ |
+help: try changing it to one of the following valid forms of the attribute
+ |
+LL - #[coverage = true]
+LL + #[coverage(off)]
+ |
+LL - #[coverage = true]
+LL + #[coverage(on)]
+ |
+
+error[E0805]: malformed `coverage` attribute input
+ --> $DIR/bad-syntax.rs:23:1
+ |
+LL | #[coverage()]
+ | ^^^^^^^^^^--^
+ | |
+ | expected a single argument here
+ |
+help: try changing it to one of the following valid forms of the attribute
+ |
+LL | #[coverage(off)]
+ | +++
+LL | #[coverage(on)]
+ | ++
+
+error[E0805]: malformed `coverage` attribute input
+ --> $DIR/bad-syntax.rs:26:1
+ |
+LL | #[coverage(off, off)]
+ | ^^^^^^^^^^----------^
+ | |
+ | expected a single argument here
+ |
+help: try changing it to one of the following valid forms of the attribute
+ |
+LL - #[coverage(off, off)]
+LL + #[coverage(off)]
+ |
+LL - #[coverage(off, off)]
+LL + #[coverage(on)]
+ |
+
+error[E0805]: malformed `coverage` attribute input
+ --> $DIR/bad-syntax.rs:29:1
+ |
+LL | #[coverage(off, on)]
+ | ^^^^^^^^^^---------^
+ | |
+ | expected a single argument here
+ |
+help: try changing it to one of the following valid forms of the attribute
+ |
+LL - #[coverage(off, on)]
+LL + #[coverage(off)]
+ |
+LL - #[coverage(off, on)]
+LL + #[coverage(on)]
+ |
+
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/bad-syntax.rs:32:1
+ |
+LL | #[coverage(bogus)]
+ | ^^^^^^^^^^^-----^^
+ | |
+ | valid arguments are `on` or `off`
+ |
+help: try changing it to one of the following valid forms of the attribute
+ |
+LL - #[coverage(bogus)]
+LL + #[coverage(off)]
+ |
+LL - #[coverage(bogus)]
+LL + #[coverage(on)]
+ |
+
+error[E0805]: malformed `coverage` attribute input
+ --> $DIR/bad-syntax.rs:35:1
+ |
+LL | #[coverage(bogus, off)]
+ | ^^^^^^^^^^------------^
+ | |
+ | expected a single argument here
+ |
+help: try changing it to one of the following valid forms of the attribute
+ |
+LL - #[coverage(bogus, off)]
+LL + #[coverage(off)]
+ |
+LL - #[coverage(bogus, off)]
+LL + #[coverage(on)]
+ |
+
+error[E0805]: malformed `coverage` attribute input
+ --> $DIR/bad-syntax.rs:38:1
+ |
+LL | #[coverage(off, bogus)]
+ | ^^^^^^^^^^------------^
+ | |
+ | expected a single argument here
+ |
+help: try changing it to one of the following valid forms of the attribute
+ |
+LL - #[coverage(off, bogus)]
+LL + #[coverage(off)]
+ |
+LL - #[coverage(off, bogus)]
+LL + #[coverage(on)]
+ |
+
error: aborting due to 11 previous errors
+Some errors have detailed explanations: E0539, E0805.
+For more information about an error, try `rustc --explain E0539`.
diff --git a/tests/ui/coverage-attr/name-value.rs b/tests/ui/coverage-attr/name-value.rs
index ffd9afe..8171dbb 100644
--- a/tests/ui/coverage-attr/name-value.rs
+++ b/tests/ui/coverage-attr/name-value.rs
@@ -20,7 +20,6 @@ mod my_mod_inner {
#[coverage = "off"]
//~^ ERROR malformed `coverage` attribute input
-//~| ERROR [E0788]
struct MyStruct;
#[coverage = "off"]
@@ -28,22 +27,18 @@ mod my_mod_inner {
impl MyStruct {
#[coverage = "off"]
//~^ ERROR malformed `coverage` attribute input
- //~| ERROR [E0788]
const X: u32 = 7;
}
#[coverage = "off"]
//~^ ERROR malformed `coverage` attribute input
-//~| ERROR [E0788]
trait MyTrait {
#[coverage = "off"]
//~^ ERROR malformed `coverage` attribute input
- //~| ERROR [E0788]
const X: u32;
#[coverage = "off"]
//~^ ERROR malformed `coverage` attribute input
- //~| ERROR [E0788]
type T;
}
@@ -52,12 +47,10 @@ trait MyTrait {
impl MyTrait for MyStruct {
#[coverage = "off"]
//~^ ERROR malformed `coverage` attribute input
- //~| ERROR [E0788]
const X: u32 = 8;
#[coverage = "off"]
//~^ ERROR malformed `coverage` attribute input
- //~| ERROR [E0788]
type T = ();
}
diff --git a/tests/ui/coverage-attr/name-value.stderr b/tests/ui/coverage-attr/name-value.stderr
index f24db78..a838ec5 100644
--- a/tests/ui/coverage-attr/name-value.stderr
+++ b/tests/ui/coverage-attr/name-value.stderr
@@ -1,10 +1,10 @@
-error: malformed `coverage` attribute input
+error[E0539]: malformed `coverage` attribute input
--> $DIR/name-value.rs:12:1
|
LL | #[coverage = "off"]
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL - #[coverage = "off"]
LL + #[coverage(off)]
@@ -13,28 +13,28 @@
LL + #[coverage(on)]
|
-error: malformed `coverage` attribute input
+error[E0539]: malformed `coverage` attribute input
--> $DIR/name-value.rs:17:5
|
LL | #![coverage = "off"]
- | ^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL - #![coverage = "off"]
-LL + #![coverage(off)]
+LL + #[coverage(off)]
|
LL - #![coverage = "off"]
-LL + #![coverage(on)]
+LL + #[coverage(on)]
|
-error: malformed `coverage` attribute input
+error[E0539]: malformed `coverage` attribute input
--> $DIR/name-value.rs:21:1
|
LL | #[coverage = "off"]
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL - #[coverage = "off"]
LL + #[coverage(off)]
@@ -43,13 +43,13 @@
LL + #[coverage(on)]
|
-error: malformed `coverage` attribute input
- --> $DIR/name-value.rs:26:1
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/name-value.rs:25:1
|
LL | #[coverage = "off"]
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL - #[coverage = "off"]
LL + #[coverage(off)]
@@ -58,13 +58,13 @@
LL + #[coverage(on)]
|
-error: malformed `coverage` attribute input
- --> $DIR/name-value.rs:29:5
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/name-value.rs:28:5
|
LL | #[coverage = "off"]
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL - #[coverage = "off"]
LL + #[coverage(off)]
@@ -73,13 +73,13 @@
LL + #[coverage(on)]
|
-error: malformed `coverage` attribute input
- --> $DIR/name-value.rs:35:1
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/name-value.rs:33:1
|
LL | #[coverage = "off"]
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL - #[coverage = "off"]
LL + #[coverage(off)]
@@ -88,13 +88,13 @@
LL + #[coverage(on)]
|
-error: malformed `coverage` attribute input
- --> $DIR/name-value.rs:39:5
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/name-value.rs:36:5
|
LL | #[coverage = "off"]
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL - #[coverage = "off"]
LL + #[coverage(off)]
@@ -103,13 +103,13 @@
LL + #[coverage(on)]
|
-error: malformed `coverage` attribute input
- --> $DIR/name-value.rs:44:5
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/name-value.rs:40:5
|
LL | #[coverage = "off"]
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL - #[coverage = "off"]
LL + #[coverage(off)]
@@ -118,13 +118,13 @@
LL + #[coverage(on)]
|
-error: malformed `coverage` attribute input
- --> $DIR/name-value.rs:50:1
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/name-value.rs:45:1
|
LL | #[coverage = "off"]
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL - #[coverage = "off"]
LL + #[coverage(off)]
@@ -133,13 +133,13 @@
LL + #[coverage(on)]
|
-error: malformed `coverage` attribute input
- --> $DIR/name-value.rs:53:5
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/name-value.rs:48:5
|
LL | #[coverage = "off"]
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL - #[coverage = "off"]
LL + #[coverage(off)]
@@ -148,13 +148,13 @@
LL + #[coverage(on)]
|
-error: malformed `coverage` attribute input
- --> $DIR/name-value.rs:58:5
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/name-value.rs:52:5
|
LL | #[coverage = "off"]
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL - #[coverage = "off"]
LL + #[coverage(off)]
@@ -163,13 +163,13 @@
LL + #[coverage(on)]
|
-error: malformed `coverage` attribute input
- --> $DIR/name-value.rs:64:1
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/name-value.rs:57:1
|
LL | #[coverage = "off"]
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL - #[coverage = "off"]
LL + #[coverage(off)]
@@ -178,87 +178,6 @@
LL + #[coverage(on)]
|
-error[E0788]: coverage attribute not allowed here
- --> $DIR/name-value.rs:21:1
- |
-LL | #[coverage = "off"]
- | ^^^^^^^^^^^^^^^^^^^
-...
-LL | struct MyStruct;
- | ---------------- not a function, impl block, or module
- |
- = help: coverage attribute can be applied to a function (with body), impl block, or module
+error: aborting due to 12 previous errors
-error[E0788]: coverage attribute not allowed here
- --> $DIR/name-value.rs:35:1
- |
-LL | #[coverage = "off"]
- | ^^^^^^^^^^^^^^^^^^^
-...
-LL | / trait MyTrait {
-LL | | #[coverage = "off"]
-... |
-LL | | type T;
-LL | | }
- | |_- not a function, impl block, or module
- |
- = help: coverage attribute can be applied to a function (with body), impl block, or module
-
-error[E0788]: coverage attribute not allowed here
- --> $DIR/name-value.rs:39:5
- |
-LL | #[coverage = "off"]
- | ^^^^^^^^^^^^^^^^^^^
-...
-LL | const X: u32;
- | ------------- not a function, impl block, or module
- |
- = help: coverage attribute can be applied to a function (with body), impl block, or module
-
-error[E0788]: coverage attribute not allowed here
- --> $DIR/name-value.rs:44:5
- |
-LL | #[coverage = "off"]
- | ^^^^^^^^^^^^^^^^^^^
-...
-LL | type T;
- | ------- not a function, impl block, or module
- |
- = help: coverage attribute can be applied to a function (with body), impl block, or module
-
-error[E0788]: coverage attribute not allowed here
- --> $DIR/name-value.rs:29:5
- |
-LL | #[coverage = "off"]
- | ^^^^^^^^^^^^^^^^^^^
-...
-LL | const X: u32 = 7;
- | ----------------- not a function, impl block, or module
- |
- = help: coverage attribute can be applied to a function (with body), impl block, or module
-
-error[E0788]: coverage attribute not allowed here
- --> $DIR/name-value.rs:53:5
- |
-LL | #[coverage = "off"]
- | ^^^^^^^^^^^^^^^^^^^
-...
-LL | const X: u32 = 8;
- | ----------------- not a function, impl block, or module
- |
- = help: coverage attribute can be applied to a function (with body), impl block, or module
-
-error[E0788]: coverage attribute not allowed here
- --> $DIR/name-value.rs:58:5
- |
-LL | #[coverage = "off"]
- | ^^^^^^^^^^^^^^^^^^^
-...
-LL | type T = ();
- | ------------ not a function, impl block, or module
- |
- = help: coverage attribute can be applied to a function (with body), impl block, or module
-
-error: aborting due to 19 previous errors
-
-For more information about this error, try `rustc --explain E0788`.
+For more information about this error, try `rustc --explain E0539`.
diff --git a/tests/ui/coverage-attr/subword.stderr b/tests/ui/coverage-attr/subword.stderr
index a5d1a49..32a09a1 100644
--- a/tests/ui/coverage-attr/subword.stderr
+++ b/tests/ui/coverage-attr/subword.stderr
@@ -1,10 +1,12 @@
-error: malformed `coverage` attribute input
+error[E0539]: malformed `coverage` attribute input
--> $DIR/subword.rs:8:1
|
LL | #[coverage(yes(milord))]
- | ^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^-----------^^
+ | |
+ | valid arguments are `on` or `off`
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL - #[coverage(yes(milord))]
LL + #[coverage(off)]
@@ -13,13 +15,15 @@
LL + #[coverage(on)]
|
-error: malformed `coverage` attribute input
+error[E0539]: malformed `coverage` attribute input
--> $DIR/subword.rs:11:1
|
LL | #[coverage(no(milord))]
- | ^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^----------^^
+ | |
+ | valid arguments are `on` or `off`
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL - #[coverage(no(milord))]
LL + #[coverage(off)]
@@ -28,13 +32,15 @@
LL + #[coverage(on)]
|
-error: malformed `coverage` attribute input
+error[E0539]: malformed `coverage` attribute input
--> $DIR/subword.rs:14:1
|
LL | #[coverage(yes = "milord")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^--------------^^
+ | |
+ | valid arguments are `on` or `off`
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL - #[coverage(yes = "milord")]
LL + #[coverage(off)]
@@ -43,13 +49,15 @@
LL + #[coverage(on)]
|
-error: malformed `coverage` attribute input
+error[E0539]: malformed `coverage` attribute input
--> $DIR/subword.rs:17:1
|
LL | #[coverage(no = "milord")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^-------------^^
+ | |
+ | valid arguments are `on` or `off`
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL - #[coverage(no = "milord")]
LL + #[coverage(off)]
@@ -60,3 +68,4 @@
error: aborting due to 4 previous errors
+For more information about this error, try `rustc --explain E0539`.
diff --git a/tests/ui/coverage-attr/word-only.rs b/tests/ui/coverage-attr/word-only.rs
index d0f7439..81bd558 100644
--- a/tests/ui/coverage-attr/word-only.rs
+++ b/tests/ui/coverage-attr/word-only.rs
@@ -20,7 +20,6 @@ mod my_mod_inner {
#[coverage]
//~^ ERROR malformed `coverage` attribute input
-//~| ERROR [E0788]
struct MyStruct;
#[coverage]
@@ -28,22 +27,18 @@ mod my_mod_inner {
impl MyStruct {
#[coverage]
//~^ ERROR malformed `coverage` attribute input
- //~| ERROR [E0788]
const X: u32 = 7;
}
#[coverage]
//~^ ERROR malformed `coverage` attribute input
-//~| ERROR [E0788]
trait MyTrait {
#[coverage]
//~^ ERROR malformed `coverage` attribute input
- //~| ERROR [E0788]
const X: u32;
#[coverage]
//~^ ERROR malformed `coverage` attribute input
- //~| ERROR [E0788]
type T;
}
@@ -52,12 +47,10 @@ trait MyTrait {
impl MyTrait for MyStruct {
#[coverage]
//~^ ERROR malformed `coverage` attribute input
- //~| ERROR [E0788]
const X: u32 = 8;
#[coverage]
//~^ ERROR malformed `coverage` attribute input
- //~| ERROR [E0788]
type T = ();
}
diff --git a/tests/ui/coverage-attr/word-only.stderr b/tests/ui/coverage-attr/word-only.stderr
index 2773db9..dd16136 100644
--- a/tests/ui/coverage-attr/word-only.stderr
+++ b/tests/ui/coverage-attr/word-only.stderr
@@ -1,240 +1,161 @@
-error: malformed `coverage` attribute input
+error[E0539]: malformed `coverage` attribute input
--> $DIR/word-only.rs:12:1
|
LL | #[coverage]
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL | #[coverage(off)]
| +++++
LL | #[coverage(on)]
| ++++
-error: malformed `coverage` attribute input
+error[E0539]: malformed `coverage` attribute input
--> $DIR/word-only.rs:17:5
|
LL | #![coverage]
- | ^^^^^^^^^^^^
+ | ^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
-LL | #![coverage(off)]
- | +++++
-LL | #![coverage(on)]
- | ++++
+LL - #![coverage]
+LL + #[coverage(off)]
+ |
+LL - #![coverage]
+LL + #[coverage(on)]
+ |
-error: malformed `coverage` attribute input
+error[E0539]: malformed `coverage` attribute input
--> $DIR/word-only.rs:21:1
|
LL | #[coverage]
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL | #[coverage(off)]
| +++++
LL | #[coverage(on)]
| ++++
-error: malformed `coverage` attribute input
- --> $DIR/word-only.rs:26:1
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/word-only.rs:25:1
|
LL | #[coverage]
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL | #[coverage(off)]
| +++++
LL | #[coverage(on)]
| ++++
-error: malformed `coverage` attribute input
- --> $DIR/word-only.rs:29:5
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/word-only.rs:28:5
|
LL | #[coverage]
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL | #[coverage(off)]
| +++++
LL | #[coverage(on)]
| ++++
-error: malformed `coverage` attribute input
- --> $DIR/word-only.rs:35:1
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/word-only.rs:33:1
|
LL | #[coverage]
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL | #[coverage(off)]
| +++++
LL | #[coverage(on)]
| ++++
-error: malformed `coverage` attribute input
- --> $DIR/word-only.rs:39:5
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/word-only.rs:36:5
|
LL | #[coverage]
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL | #[coverage(off)]
| +++++
LL | #[coverage(on)]
| ++++
-error: malformed `coverage` attribute input
- --> $DIR/word-only.rs:44:5
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/word-only.rs:40:5
|
LL | #[coverage]
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL | #[coverage(off)]
| +++++
LL | #[coverage(on)]
| ++++
-error: malformed `coverage` attribute input
- --> $DIR/word-only.rs:50:1
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/word-only.rs:45:1
|
LL | #[coverage]
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL | #[coverage(off)]
| +++++
LL | #[coverage(on)]
| ++++
-error: malformed `coverage` attribute input
- --> $DIR/word-only.rs:53:5
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/word-only.rs:48:5
|
LL | #[coverage]
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL | #[coverage(off)]
| +++++
LL | #[coverage(on)]
| ++++
-error: malformed `coverage` attribute input
- --> $DIR/word-only.rs:58:5
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/word-only.rs:52:5
|
LL | #[coverage]
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL | #[coverage(off)]
| +++++
LL | #[coverage(on)]
| ++++
-error: malformed `coverage` attribute input
- --> $DIR/word-only.rs:64:1
+error[E0539]: malformed `coverage` attribute input
+ --> $DIR/word-only.rs:57:1
|
LL | #[coverage]
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
-help: the following are the possible correct uses
+help: try changing it to one of the following valid forms of the attribute
|
LL | #[coverage(off)]
| +++++
LL | #[coverage(on)]
| ++++
-error[E0788]: coverage attribute not allowed here
- --> $DIR/word-only.rs:21:1
- |
-LL | #[coverage]
- | ^^^^^^^^^^^
-...
-LL | struct MyStruct;
- | ---------------- not a function, impl block, or module
- |
- = help: coverage attribute can be applied to a function (with body), impl block, or module
+error: aborting due to 12 previous errors
-error[E0788]: coverage attribute not allowed here
- --> $DIR/word-only.rs:35:1
- |
-LL | #[coverage]
- | ^^^^^^^^^^^
-...
-LL | / trait MyTrait {
-LL | | #[coverage]
-... |
-LL | | type T;
-LL | | }
- | |_- not a function, impl block, or module
- |
- = help: coverage attribute can be applied to a function (with body), impl block, or module
-
-error[E0788]: coverage attribute not allowed here
- --> $DIR/word-only.rs:39:5
- |
-LL | #[coverage]
- | ^^^^^^^^^^^
-...
-LL | const X: u32;
- | ------------- not a function, impl block, or module
- |
- = help: coverage attribute can be applied to a function (with body), impl block, or module
-
-error[E0788]: coverage attribute not allowed here
- --> $DIR/word-only.rs:44:5
- |
-LL | #[coverage]
- | ^^^^^^^^^^^
-...
-LL | type T;
- | ------- not a function, impl block, or module
- |
- = help: coverage attribute can be applied to a function (with body), impl block, or module
-
-error[E0788]: coverage attribute not allowed here
- --> $DIR/word-only.rs:29:5
- |
-LL | #[coverage]
- | ^^^^^^^^^^^
-...
-LL | const X: u32 = 7;
- | ----------------- not a function, impl block, or module
- |
- = help: coverage attribute can be applied to a function (with body), impl block, or module
-
-error[E0788]: coverage attribute not allowed here
- --> $DIR/word-only.rs:53:5
- |
-LL | #[coverage]
- | ^^^^^^^^^^^
-...
-LL | const X: u32 = 8;
- | ----------------- not a function, impl block, or module
- |
- = help: coverage attribute can be applied to a function (with body), impl block, or module
-
-error[E0788]: coverage attribute not allowed here
- --> $DIR/word-only.rs:58:5
- |
-LL | #[coverage]
- | ^^^^^^^^^^^
-...
-LL | type T = ();
- | ------------ not a function, impl block, or module
- |
- = help: coverage attribute can be applied to a function (with body), impl block, or module
-
-error: aborting due to 19 previous errors
-
-For more information about this error, try `rustc --explain E0788`.
+For more information about this error, try `rustc --explain E0539`.
diff --git a/tests/ui/deduplicate-diagnostics.deduplicate.stderr b/tests/ui/diagnostic-flags/deduplicate-diagnostics.deduplicate.stderr
similarity index 79%
rename from tests/ui/deduplicate-diagnostics.deduplicate.stderr
rename to tests/ui/diagnostic-flags/deduplicate-diagnostics.deduplicate.stderr
index 5df2c68..c0d568e 100644
--- a/tests/ui/deduplicate-diagnostics.deduplicate.stderr
+++ b/tests/ui/diagnostic-flags/deduplicate-diagnostics.deduplicate.stderr
@@ -1,11 +1,11 @@
error[E0452]: malformed lint attribute input
- --> $DIR/deduplicate-diagnostics.rs:8:8
+ --> $DIR/deduplicate-diagnostics.rs:10:8
|
LL | #[deny("literal")]
| ^^^^^^^^^ bad attribute argument
error: cannot find derive macro `Unresolved` in this scope
- --> $DIR/deduplicate-diagnostics.rs:4:10
+ --> $DIR/deduplicate-diagnostics.rs:6:10
|
LL | #[derive(Unresolved)]
| ^^^^^^^^^^
diff --git a/tests/ui/deduplicate-diagnostics.duplicate.stderr b/tests/ui/diagnostic-flags/deduplicate-diagnostics.duplicate.stderr
similarity index 81%
rename from tests/ui/deduplicate-diagnostics.duplicate.stderr
rename to tests/ui/diagnostic-flags/deduplicate-diagnostics.duplicate.stderr
index 48e2ba7..74d7066 100644
--- a/tests/ui/deduplicate-diagnostics.duplicate.stderr
+++ b/tests/ui/diagnostic-flags/deduplicate-diagnostics.duplicate.stderr
@@ -1,17 +1,17 @@
error[E0452]: malformed lint attribute input
- --> $DIR/deduplicate-diagnostics.rs:8:8
+ --> $DIR/deduplicate-diagnostics.rs:10:8
|
LL | #[deny("literal")]
| ^^^^^^^^^ bad attribute argument
error: cannot find derive macro `Unresolved` in this scope
- --> $DIR/deduplicate-diagnostics.rs:4:10
+ --> $DIR/deduplicate-diagnostics.rs:6:10
|
LL | #[derive(Unresolved)]
| ^^^^^^^^^^
error: cannot find derive macro `Unresolved` in this scope
- --> $DIR/deduplicate-diagnostics.rs:4:10
+ --> $DIR/deduplicate-diagnostics.rs:6:10
|
LL | #[derive(Unresolved)]
| ^^^^^^^^^^
@@ -19,7 +19,7 @@
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error[E0452]: malformed lint attribute input
- --> $DIR/deduplicate-diagnostics.rs:8:8
+ --> $DIR/deduplicate-diagnostics.rs:10:8
|
LL | #[deny("literal")]
| ^^^^^^^^^ bad attribute argument
@@ -27,7 +27,7 @@
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error[E0452]: malformed lint attribute input
- --> $DIR/deduplicate-diagnostics.rs:8:8
+ --> $DIR/deduplicate-diagnostics.rs:10:8
|
LL | #[deny("literal")]
| ^^^^^^^^^ bad attribute argument
diff --git a/tests/ui/deduplicate-diagnostics.rs b/tests/ui/diagnostic-flags/deduplicate-diagnostics.rs
similarity index 84%
rename from tests/ui/deduplicate-diagnostics.rs
rename to tests/ui/diagnostic-flags/deduplicate-diagnostics.rs
index 299c1f5..4870526 100644
--- a/tests/ui/deduplicate-diagnostics.rs
+++ b/tests/ui/diagnostic-flags/deduplicate-diagnostics.rs
@@ -1,3 +1,5 @@
+//! Test that `-Z deduplicate-diagnostics` flag properly deduplicates diagnostic messages.
+
//@ revisions: duplicate deduplicate
//@[deduplicate] compile-flags: -Z deduplicate-diagnostics=yes
diff --git a/tests/ui/error-codes/E0120.rs b/tests/ui/error-codes/E0120.rs
index 35f544f..4e5cc7d 100644
--- a/tests/ui/error-codes/E0120.rs
+++ b/tests/ui/error-codes/E0120.rs
@@ -1,4 +1,4 @@
-trait MyTrait { fn foo() {} }
+trait MyTrait { fn foo(&self) {} }
impl Drop for dyn MyTrait {
//~^ ERROR E0120
diff --git a/tests/ui/expr/syntax-edge-cases-lint-clean.rs b/tests/ui/expr/weird-exprs.rs
similarity index 100%
rename from tests/ui/expr/syntax-edge-cases-lint-clean.rs
rename to tests/ui/expr/weird-exprs.rs
diff --git a/tests/ui/log-poly.rs b/tests/ui/fmt/println-debug-different-types.rs
similarity index 72%
rename from tests/ui/log-poly.rs
rename to tests/ui/fmt/println-debug-different-types.rs
index 64994a5..9e21be1 100644
--- a/tests/ui/log-poly.rs
+++ b/tests/ui/fmt/println-debug-different-types.rs
@@ -1,8 +1,10 @@
+//! Smoke test for println!() with debug format specifiers.
+
//@ run-pass
#[derive(Debug)]
enum Numbers {
- Three
+ Three,
}
pub fn main() {
diff --git a/tests/ui/generic-associated-types/bugs/issue-100013.rs b/tests/ui/generic-associated-types/bugs/issue-100013.rs
deleted file mode 100644
index ac72c29..0000000
--- a/tests/ui/generic-associated-types/bugs/issue-100013.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-//@ check-fail
-//@ known-bug: #100013
-//@ edition: 2021
-
-// We really should accept this, but we need implied bounds between the regions
-// in a coroutine interior.
-
-pub trait FutureIterator {
- type Future<'s, 'cx>: Send
- where
- 's: 'cx;
-}
-
-fn call<I: FutureIterator>() -> impl Send {
- async { // a coroutine checked for autotrait impl `Send`
- let x = None::<I::Future<'_, '_>>; // a type referencing GAT
- async {}.await; // a yield point
- }
-}
-
-fn call2<'a, 'b, I: FutureIterator>() -> impl Send {
- async { // a coroutine checked for autotrait impl `Send`
- let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
- async {}.await; // a yield point
- }
-}
-
-fn call3<'a: 'b, 'b, I: FutureIterator>() -> impl Send {
- async { // a coroutine checked for autotrait impl `Send`
- let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
- async {}.await; // a yield point
- }
-}
-
-fn main() {}
diff --git a/tests/ui/generic-associated-types/bugs/issue-100013.stderr b/tests/ui/generic-associated-types/bugs/issue-100013.stderr
deleted file mode 100644
index ff82aeb..0000000
--- a/tests/ui/generic-associated-types/bugs/issue-100013.stderr
+++ /dev/null
@@ -1,48 +0,0 @@
-error: lifetime bound not satisfied
- --> $DIR/issue-100013.rs:15:5
- |
-LL | / async { // a coroutine checked for autotrait impl `Send`
-LL | | let x = None::<I::Future<'_, '_>>; // a type referencing GAT
-LL | | async {}.await; // a yield point
-LL | | }
- | |_____^
- |
- = note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
-
-error: lifetime bound not satisfied
- --> $DIR/issue-100013.rs:22:5
- |
-LL | / async { // a coroutine checked for autotrait impl `Send`
-LL | | let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
-LL | | async {}.await; // a yield point
-LL | | }
- | |_____^
- |
- = note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
-
-error: lifetime may not live long enough
- --> $DIR/issue-100013.rs:23:17
- |
-LL | fn call2<'a, 'b, I: FutureIterator>() -> impl Send {
- | -- -- lifetime `'b` defined here
- | |
- | lifetime `'a` defined here
-LL | async { // a coroutine checked for autotrait impl `Send`
-LL | let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'b`
- |
- = help: consider adding the following bound: `'a: 'b`
-
-error: lifetime bound not satisfied
- --> $DIR/issue-100013.rs:29:5
- |
-LL | / async { // a coroutine checked for autotrait impl `Send`
-LL | | let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
-LL | | async {}.await; // a yield point
-LL | | }
- | |_____^
- |
- = note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
-
-error: aborting due to 4 previous errors
-
diff --git a/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives-2.no_assumptions.stderr b/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives-2.no_assumptions.stderr
new file mode 100644
index 0000000..8b62fb6
--- /dev/null
+++ b/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives-2.no_assumptions.stderr
@@ -0,0 +1,24 @@
+error: lifetime bound not satisfied
+ --> $DIR/higher-ranked-coroutine-param-outlives-2.rs:14:5
+ |
+LL | / async { // a coroutine checked for autotrait impl `Send`
+LL | | let x = None::<I::Future<'_, '_>>; // a type referencing GAT
+LL | | async {}.await; // a yield point
+LL | | }
+ | |_____^
+ |
+ = note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
+
+error: lifetime bound not satisfied
+ --> $DIR/higher-ranked-coroutine-param-outlives-2.rs:21:5
+ |
+LL | / async { // a coroutine checked for autotrait impl `Send`
+LL | | let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
+LL | | async {}.await; // a yield point
+LL | | }
+ | |_____^
+ |
+ = note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives-2.rs b/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives-2.rs
new file mode 100644
index 0000000..a5ed162
--- /dev/null
+++ b/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives-2.rs
@@ -0,0 +1,27 @@
+//@ edition: 2021
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] check-pass
+//@[no_assumptions] known-bug: #110338
+
+pub trait FutureIterator {
+ type Future<'s, 'cx>: Send
+ where
+ 's: 'cx;
+}
+
+fn call<I: FutureIterator>() -> impl Send {
+ async { // a coroutine checked for autotrait impl `Send`
+ let x = None::<I::Future<'_, '_>>; // a type referencing GAT
+ async {}.await; // a yield point
+ }
+}
+
+fn call2<'a: 'b, 'b, I: FutureIterator>() -> impl Send {
+ async { // a coroutine checked for autotrait impl `Send`
+ let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
+ async {}.await; // a yield point
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/generic-associated-types/issue-92096.stderr b/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives.no_assumptions.stderr
similarity index 74%
rename from tests/ui/generic-associated-types/issue-92096.stderr
rename to tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives.no_assumptions.stderr
index b9a16cf..f747ba9 100644
--- a/tests/ui/generic-associated-types/issue-92096.stderr
+++ b/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives.no_assumptions.stderr
@@ -1,5 +1,5 @@
error: `C` does not live long enough
- --> $DIR/issue-92096.rs:17:5
+ --> $DIR/higher-ranked-coroutine-param-outlives.rs:21:5
|
LL | async move { c.connect().await }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives.rs b/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives.rs
new file mode 100644
index 0000000..5f683bd
--- /dev/null
+++ b/tests/ui/generic-associated-types/higher-ranked-coroutine-param-outlives.rs
@@ -0,0 +1,24 @@
+//@ edition:2018
+//@ revisions: assumptions no_assumptions
+//@[assumptions] compile-flags: -Zhigher-ranked-assumptions
+//@[assumptions] check-pass
+//@[no_assumptions] known-bug: #110338
+
+use std::future::Future;
+
+trait Client {
+ type Connecting<'a>: Future + Send
+ where
+ Self: 'a;
+
+ fn connect(&'_ self) -> Self::Connecting<'_>;
+}
+
+fn call_connect<C>(c: &'_ C) -> impl '_ + Future + Send
+where
+ C: Client + Send + Sync,
+{
+ async move { c.connect().await }
+}
+
+fn main() {}
diff --git a/tests/ui/generic-associated-types/issue-92096.rs b/tests/ui/generic-associated-types/issue-92096.rs
deleted file mode 100644
index a34c417..0000000
--- a/tests/ui/generic-associated-types/issue-92096.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-//@ edition:2018
-
-use std::future::Future;
-
-trait Client {
- type Connecting<'a>: Future + Send
- where
- Self: 'a;
-
- fn connect(&'_ self) -> Self::Connecting<'_>;
-}
-
-fn call_connect<C>(c: &'_ C) -> impl '_ + Future + Send
-where
- C: Client + Send + Sync,
-{
- async move { c.connect().await }
- //~^ ERROR `C` does not live long enough
- //
- // FIXME(#71723). This is because we infer at some point a value of
- //
- // impl Future<Output = <C as Client>::Connection<'_>>
- //
- // and then we somehow fail the WF check because `where C: 'a` is not known,
- // but I'm not entirely sure how that comes about.
-}
-
-fn main() {}
diff --git a/tests/ui/impl-trait/call_method_without_import.no_import.stderr b/tests/ui/impl-trait/call_method_without_import.no_import.stderr
index e59409e..dbac74b 100644
--- a/tests/ui/impl-trait/call_method_without_import.no_import.stderr
+++ b/tests/ui/impl-trait/call_method_without_import.no_import.stderr
@@ -22,15 +22,10 @@
= help: items from traits can only be used if the trait is in scope
help: the following traits which provide `fmt` are implemented but not in scope; perhaps you want to import one of them
|
-LL + use std::fmt::Binary;
- |
LL + use std::fmt::Debug;
|
-LL + use std::fmt::Display;
+LL + use std::fmt::Pointer;
|
-LL + use std::fmt::LowerExp;
- |
- = and 5 other candidates
error: aborting due to 2 previous errors
diff --git a/tests/ui/impl-trait/issues/issue-55872-3.rs b/tests/ui/impl-trait/issues/issue-55872-3.rs
index 698e7f3..763b4b9 100644
--- a/tests/ui/impl-trait/issues/issue-55872-3.rs
+++ b/tests/ui/impl-trait/issues/issue-55872-3.rs
@@ -14,6 +14,7 @@ impl<S> Bar for S {
fn foo<T>() -> Self::E {
//~^ ERROR : Copy` is not satisfied [E0277]
//~| ERROR type parameter `T` is part of concrete type
+ //~| ERROR type parameter `T` is part of concrete type
async {}
}
}
diff --git a/tests/ui/impl-trait/issues/issue-55872-3.stderr b/tests/ui/impl-trait/issues/issue-55872-3.stderr
index 3281dcc..ce2dd7f 100644
--- a/tests/ui/impl-trait/issues/issue-55872-3.stderr
+++ b/tests/ui/impl-trait/issues/issue-55872-3.stderr
@@ -1,11 +1,11 @@
-error[E0277]: the trait bound `{async block@$DIR/issue-55872-3.rs:17:9: 17:14}: Copy` is not satisfied
+error[E0277]: the trait bound `{async block@$DIR/issue-55872-3.rs:18:9: 18:14}: Copy` is not satisfied
--> $DIR/issue-55872-3.rs:14:20
|
LL | fn foo<T>() -> Self::E {
- | ^^^^^^^ the trait `Copy` is not implemented for `{async block@$DIR/issue-55872-3.rs:17:9: 17:14}`
+ | ^^^^^^^ the trait `Copy` is not implemented for `{async block@$DIR/issue-55872-3.rs:18:9: 18:14}`
...
LL | async {}
- | -------- return type was inferred to be `{async block@$DIR/issue-55872-3.rs:17:9: 17:14}` here
+ | -------- return type was inferred to be `{async block@$DIR/issue-55872-3.rs:18:9: 18:14}` here
error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
--> $DIR/issue-55872-3.rs:14:20
@@ -13,6 +13,14 @@
LL | fn foo<T>() -> Self::E {
| ^^^^^^^
-error: aborting due to 2 previous errors
+error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
+ --> $DIR/issue-55872-3.rs:14:20
+ |
+LL | fn foo<T>() -> Self::E {
+ | ^^^^^^^
+ |
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/impl-trait/no-method-suggested-traits.stderr b/tests/ui/impl-trait/no-method-suggested-traits.stderr
index 061c9bd..b376f20 100644
--- a/tests/ui/impl-trait/no-method-suggested-traits.stderr
+++ b/tests/ui/impl-trait/no-method-suggested-traits.stderr
@@ -9,12 +9,8 @@
|
LL + use foo::Bar;
|
-LL + use no_method_suggested_traits::Reexported;
- |
LL + use no_method_suggested_traits::foo::PubPub;
|
-LL + use no_method_suggested_traits::qux::PrivPub;
- |
help: there is a method `method2` with a similar name
|
LL | 1u32.method2();
@@ -31,12 +27,8 @@
|
LL + use foo::Bar;
|
-LL + use no_method_suggested_traits::Reexported;
- |
LL + use no_method_suggested_traits::foo::PubPub;
|
-LL + use no_method_suggested_traits::qux::PrivPub;
- |
help: there is a method `method2` with a similar name
|
LL | std::rc::Rc::new(&mut Box::new(&1u32)).method2();
diff --git a/tests/ui/linking/export-executable-symbols.rs b/tests/ui/linking/export-executable-symbols.rs
index aea5527..2bff58c 100644
--- a/tests/ui/linking/export-executable-symbols.rs
+++ b/tests/ui/linking/export-executable-symbols.rs
@@ -1,22 +1,22 @@
//@ run-pass
-//@ only-linux
-//@ only-gnu
-//@ compile-flags: -Zexport-executable-symbols
+//@ compile-flags: -Ctarget-feature=-crt-static -Zexport-executable-symbols
+//@ ignore-wasm
+//@ ignore-cross-compile
//@ edition: 2024
// Regression test for <https://github.com/rust-lang/rust/issues/101610>.
#![feature(rustc_private)]
-extern crate libc;
-
#[unsafe(no_mangle)]
fn hack() -> u64 {
998244353
}
fn main() {
+ #[cfg(unix)]
unsafe {
+ extern crate libc;
let handle = libc::dlopen(std::ptr::null(), libc::RTLD_NOW);
let ptr = libc::dlsym(handle, c"hack".as_ptr());
let ptr: Option<unsafe fn() -> u64> = std::mem::transmute(ptr);
@@ -27,4 +27,24 @@ fn main() {
panic!("symbol `hack` is not found");
}
}
+ #[cfg(windows)]
+ unsafe {
+ type PCSTR = *const u8;
+ type HMODULE = *mut core::ffi::c_void;
+ type FARPROC = Option<unsafe extern "system" fn() -> isize>;
+ #[link(name = "kernel32", kind = "raw-dylib")]
+ unsafe extern "system" {
+ fn GetModuleHandleA(lpmodulename: PCSTR) -> HMODULE;
+ fn GetProcAddress(hmodule: HMODULE, lpprocname: PCSTR) -> FARPROC;
+ }
+ let handle = GetModuleHandleA(std::ptr::null_mut());
+ let ptr = GetProcAddress(handle, b"hack\0".as_ptr());
+ let ptr: Option<unsafe fn() -> u64> = std::mem::transmute(ptr);
+ if let Some(f) = ptr {
+ assert!(f() == 998244353);
+ println!("symbol `hack` is found successfully");
+ } else {
+ panic!("symbol `hack` is not found");
+ }
+ }
}
diff --git a/tests/ui/darwin-ld64.rs b/tests/ui/linking/ld64-cross-compilation.rs
similarity index 71%
rename from tests/ui/darwin-ld64.rs
rename to tests/ui/linking/ld64-cross-compilation.rs
index 75acc07..d6c6d1f 100644
--- a/tests/ui/darwin-ld64.rs
+++ b/tests/ui/linking/ld64-cross-compilation.rs
@@ -1,11 +1,11 @@
+//! This is a regression test for https://github.com/rust-lang/rust/issues/140686.
+//! Although this is a ld64(ld-classic) bug, we still need to support it
+//! due to cross-compilation and support for older Xcode.
+
//@ compile-flags: -Copt-level=3 -Ccodegen-units=256 -Clink-arg=-ld_classic
//@ run-pass
//@ only-x86_64-apple-darwin
-// This is a regression test for https://github.com/rust-lang/rust/issues/140686.
-// Although this is a ld64(ld-classic) bug, we still need to support it
-// due to cross-compilation and support for older Xcode.
-
fn main() {
let dst: Vec<u8> = Vec::new();
let len = broken_func(std::hint::black_box(2), dst);
diff --git a/tests/ui/lint/lint-double-negations-macro.rs b/tests/ui/lint/lint-double-negations-macro.rs
new file mode 100644
index 0000000..a658327
--- /dev/null
+++ b/tests/ui/lint/lint-double-negations-macro.rs
@@ -0,0 +1,16 @@
+//@ check-pass
+macro_rules! neg {
+ ($e: expr) => {
+ -$e
+ };
+}
+macro_rules! bad_macro {
+ ($e: expr) => {
+ --$e //~ WARN use of a double negation
+ };
+}
+
+fn main() {
+ neg!(-1);
+ bad_macro!(1);
+}
diff --git a/tests/ui/lint/lint-double-negations-macro.stderr b/tests/ui/lint/lint-double-negations-macro.stderr
new file mode 100644
index 0000000..d6ac9be
--- /dev/null
+++ b/tests/ui/lint/lint-double-negations-macro.stderr
@@ -0,0 +1,20 @@
+warning: use of a double negation
+ --> $DIR/lint-double-negations-macro.rs:9:9
+ |
+LL | --$e
+ | ^^^^
+...
+LL | bad_macro!(1);
+ | ------------- in this macro invocation
+ |
+ = note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages
+ = note: use `-= 1` if you meant to decrement the value
+ = note: `#[warn(double_negations)]` on by default
+ = note: this warning originates in the macro `bad_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: add parentheses for clarity
+ |
+LL | -(-$e)
+ | + +
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr b/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr
index 32cff62..aeecb82 100644
--- a/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr
+++ b/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr
@@ -20,17 +20,12 @@
LL | let z = x.foo();
| ^^^ multiple `foo` found
|
-note: candidate #1 is defined in the trait `FinalFoo`
- --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:60:5
- |
-LL | fn foo(&self) -> u8;
- | ^^^^^^^^^^^^^^^^^^^^
-note: candidate #2 is defined in an impl of the trait `NuisanceFoo` for the type `T`
+note: candidate #1 is defined in an impl of the trait `NuisanceFoo` for the type `T`
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:73:9
|
LL | fn foo(self) {}
| ^^^^^^^^^^^^
-note: candidate #3 is defined in an impl of the trait `X` for the type `T`
+note: candidate #2 is defined in an impl of the trait `X` for the type `T`
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:46:9
|
LL | fn foo(self: Smaht<Self, u64>) -> u64 {
@@ -38,14 +33,9 @@
help: disambiguate the method for candidate #1
|
LL - let z = x.foo();
-LL + let z = FinalFoo::foo(&x);
- |
-help: disambiguate the method for candidate #2
- |
-LL - let z = x.foo();
LL + let z = NuisanceFoo::foo(x);
|
-help: disambiguate the method for candidate #3
+help: disambiguate the method for candidate #2
|
LL - let z = x.foo();
LL + let z = X::foo(x);
diff --git a/tests/ui/methods/wrong-ambig-message.rs b/tests/ui/methods/wrong-ambig-message.rs
new file mode 100644
index 0000000..f88d77e
--- /dev/null
+++ b/tests/ui/methods/wrong-ambig-message.rs
@@ -0,0 +1,34 @@
+fn main() {
+ trait Hello {
+ fn name(&self) -> String;
+ }
+
+ #[derive(Debug)]
+ struct Container2 {
+ val: String,
+ }
+
+ trait AName2 {
+ fn name(&self) -> String;
+ }
+
+ trait BName2 {
+ fn name(&self, v: bool) -> String;
+ }
+
+ impl AName2 for Container2 {
+ fn name(&self) -> String {
+ "aname2".into()
+ }
+ }
+
+ impl BName2 for Container2 {
+ fn name(&self, _v: bool) -> String {
+ "bname2".into()
+ }
+ }
+
+ let c2 = Container2 { val: "abc".into() };
+ println!("c2 = {:?}", c2.name());
+ //~^ ERROR: multiple applicable items in scope
+}
diff --git a/tests/ui/methods/wrong-ambig-message.stderr b/tests/ui/methods/wrong-ambig-message.stderr
new file mode 100644
index 0000000..9a25459
--- /dev/null
+++ b/tests/ui/methods/wrong-ambig-message.stderr
@@ -0,0 +1,30 @@
+error[E0034]: multiple applicable items in scope
+ --> $DIR/wrong-ambig-message.rs:32:30
+ |
+LL | println!("c2 = {:?}", c2.name());
+ | ^^^^ multiple `name` found
+ |
+note: candidate #1 is defined in an impl of the trait `AName2` for the type `Container2`
+ --> $DIR/wrong-ambig-message.rs:20:9
+ |
+LL | fn name(&self) -> String {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+note: candidate #2 is defined in an impl of the trait `BName2` for the type `Container2`
+ --> $DIR/wrong-ambig-message.rs:26:9
+ |
+LL | fn name(&self, _v: bool) -> String {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: disambiguate the method for candidate #1
+ |
+LL - println!("c2 = {:?}", c2.name());
+LL + println!("c2 = {:?}", AName2::name(&c2));
+ |
+help: disambiguate the method for candidate #2
+ |
+LL - println!("c2 = {:?}", c2.name());
+LL + println!("c2 = {:?}", BName2::name(&c2));
+ |
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0034`.
diff --git a/tests/ui/mir/gvn-nonsensical-coroutine-layout.rs b/tests/ui/mir/gvn-nonsensical-coroutine-layout.rs
new file mode 100644
index 0000000..f0d174c
--- /dev/null
+++ b/tests/ui/mir/gvn-nonsensical-coroutine-layout.rs
@@ -0,0 +1,19 @@
+//! Verify that we do not ICE when a coroutine body is malformed.
+//@ compile-flags: -Zmir-enable-passes=+GVN
+//@ edition: 2018
+
+pub enum Request {
+ TestSome(T),
+ //~^ ERROR cannot find type `T` in this scope [E0412]
+}
+
+pub async fn handle_event(event: Request) {
+ async move {
+ static instance: Request = Request { bar: 17 };
+ //~^ ERROR expected struct, variant or union type, found enum `Request` [E0574]
+ &instance
+ }
+ .await;
+}
+
+fn main() {}
diff --git a/tests/ui/mir/gvn-nonsensical-coroutine-layout.stderr b/tests/ui/mir/gvn-nonsensical-coroutine-layout.stderr
new file mode 100644
index 0000000..abc7b16
--- /dev/null
+++ b/tests/ui/mir/gvn-nonsensical-coroutine-layout.stderr
@@ -0,0 +1,26 @@
+error[E0412]: cannot find type `T` in this scope
+ --> $DIR/gvn-nonsensical-coroutine-layout.rs:6:14
+ |
+LL | TestSome(T),
+ | ^ not found in this scope
+ |
+help: you might be missing a type parameter
+ |
+LL | pub enum Request<T> {
+ | +++
+
+error[E0574]: expected struct, variant or union type, found enum `Request`
+ --> $DIR/gvn-nonsensical-coroutine-layout.rs:12:36
+ |
+LL | static instance: Request = Request { bar: 17 };
+ | ^^^^^^^ not a struct, variant or union type
+ |
+help: consider importing this struct instead
+ |
+LL + use std::error::Request;
+ |
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0412, E0574.
+For more information about an error, try `rustc --explain E0412`.
diff --git a/tests/ui/mir/gvn-nonsensical-sized-str.rs b/tests/ui/mir/gvn-nonsensical-sized-str.rs
new file mode 100644
index 0000000..29a82f8
--- /dev/null
+++ b/tests/ui/mir/gvn-nonsensical-sized-str.rs
@@ -0,0 +1,16 @@
+//! Verify that we do not ICE when optimizing bodies with nonsensical bounds.
+//@ compile-flags: -Copt-level=1
+//@ edition: 2021
+//@ build-pass
+
+#![feature(trivial_bounds)]
+
+async fn return_str() -> str
+where
+ str: Sized,
+ //~^ WARN trait bound str: Sized does not depend on any type or lifetime parameters
+{
+ *"Sized".to_string().into_boxed_str()
+}
+
+fn main() {}
diff --git a/tests/ui/mir/gvn-nonsensical-sized-str.stderr b/tests/ui/mir/gvn-nonsensical-sized-str.stderr
new file mode 100644
index 0000000..08f0193
--- /dev/null
+++ b/tests/ui/mir/gvn-nonsensical-sized-str.stderr
@@ -0,0 +1,10 @@
+warning: trait bound str: Sized does not depend on any type or lifetime parameters
+ --> $DIR/gvn-nonsensical-sized-str.rs:10:10
+ |
+LL | str: Sized,
+ | ^^^^^
+ |
+ = note: `#[warn(trivial_bounds)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/parser/deli-ident-issue-2.rs b/tests/ui/parser/deli-ident-issue-2.rs
index 5394760..419933c 100644
--- a/tests/ui/parser/deli-ident-issue-2.rs
+++ b/tests/ui/parser/deli-ident-issue-2.rs
@@ -1,6 +1,6 @@
fn main() {
if 1 < 2 {
- let _a = vec!]; //~ ERROR mismatched closing delimiter
+ let _a = vec!];
}
} //~ ERROR unexpected closing delimiter
diff --git a/tests/ui/parser/deli-ident-issue-2.stderr b/tests/ui/parser/deli-ident-issue-2.stderr
index e0188cd..703cbf1 100644
--- a/tests/ui/parser/deli-ident-issue-2.stderr
+++ b/tests/ui/parser/deli-ident-issue-2.stderr
@@ -1,19 +1,13 @@
-error: mismatched closing delimiter: `]`
- --> $DIR/deli-ident-issue-2.rs:2:14
- |
-LL | if 1 < 2 {
- | ^ unclosed delimiter
-LL | let _a = vec!];
- | ^ mismatched closing delimiter
-
error: unexpected closing delimiter: `}`
--> $DIR/deli-ident-issue-2.rs:5:1
|
+LL | if 1 < 2 {
+ | - the nearest open delimiter
LL | let _a = vec!];
| - missing open `[` for this delimiter
LL | }
LL | }
| ^ unexpected closing delimiter
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
diff --git a/tests/ui/parser/issues/issue-104367.stderr b/tests/ui/parser/issues/issue-104367.stderr
index c067d12..f01fa4a 100644
--- a/tests/ui/parser/issues/issue-104367.stderr
+++ b/tests/ui/parser/issues/issue-104367.stderr
@@ -18,7 +18,6 @@
LL | #![cfg] {
| - unclosed delimiter
LL | #![w,)
- | - missing open `(` for this delimiter
LL |
| ^
diff --git a/tests/ui/parser/issues/issue-105209.rs b/tests/ui/parser/issues/issue-105209.rs
index f4e3315..12c902e 100644
--- a/tests/ui/parser/issues/issue-105209.rs
+++ b/tests/ui/parser/issues/issue-105209.rs
@@ -1,3 +1,3 @@
//@ compile-flags: -Zunpretty=ast-tree
#![c={#![c[)x //~ ERROR mismatched closing delimiter
- //~ ERROR this file contains an unclosed delimiter
+ //~ ERROR this file contains an unclosed delimiter
diff --git a/tests/ui/parser/issues/issue-105209.stderr b/tests/ui/parser/issues/issue-105209.stderr
index 72017e4..75643d1 100644
--- a/tests/ui/parser/issues/issue-105209.stderr
+++ b/tests/ui/parser/issues/issue-105209.stderr
@@ -7,16 +7,15 @@
| unclosed delimiter
error: this file contains an unclosed delimiter
- --> $DIR/issue-105209.rs:3:68
+ --> $DIR/issue-105209.rs:3:56
|
LL | #![c={#![c[)x
- | - - - - missing open `(` for this delimiter
- | | | |
- | | | unclosed delimiter
+ | - - - unclosed delimiter
+ | | |
| | unclosed delimiter
| unclosed delimiter
LL |
- | ^
+ | ^
error: aborting due to 2 previous errors
diff --git a/tests/ui/parser/issues/issue-62973.stderr b/tests/ui/parser/issues/issue-62973.stderr
index ea3e2be..c7fc5bc 100644
--- a/tests/ui/parser/issues/issue-62973.stderr
+++ b/tests/ui/parser/issues/issue-62973.stderr
@@ -18,10 +18,8 @@
--> $DIR/issue-62973.rs:10:2
|
LL | fn p() { match s { v, E { [) {) }
- | - - - - missing open `(` for this delimiter
- | | | |
- | | | missing open `(` for this delimiter
- | | unclosed delimiter
+ | - - unclosed delimiter
+ | |
| unclosed delimiter
LL |
LL |
diff --git a/tests/ui/parser/issues/issue-63116.stderr b/tests/ui/parser/issues/issue-63116.stderr
index e5bad84..736a0ac 100644
--- a/tests/ui/parser/issues/issue-63116.stderr
+++ b/tests/ui/parser/issues/issue-63116.stderr
@@ -10,9 +10,8 @@
--> $DIR/issue-63116.rs:4:18
|
LL | impl W <s(f;Y(;]
- | - -^
- | | |
- | | missing open `[` for this delimiter
+ | - ^
+ | |
| unclosed delimiter
error: aborting due to 2 previous errors
diff --git a/tests/ui/parser/issues/issue-67377-invalid-syntax-in-enum-discriminant.stderr b/tests/ui/parser/issues/issue-67377-invalid-syntax-in-enum-discriminant.stderr
index b82b0f3..5301d43 100644
--- a/tests/ui/parser/issues/issue-67377-invalid-syntax-in-enum-discriminant.stderr
+++ b/tests/ui/parser/issues/issue-67377-invalid-syntax-in-enum-discriminant.stderr
@@ -28,18 +28,14 @@
error: this file contains an unclosed delimiter
--> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:23:65
|
-LL | V = [PhantomData; { [ () ].len() ].len() as isize,
- | - missing open `[` for this delimiter
-...
-LL | V = [Vec::new; { [].len() ].len() as isize,
- | - missing open `[` for this delimiter
-...
LL | mod c {
| - unclosed delimiter
LL | enum Bug {
-LL | V = [Vec::new; { [0].len() ].len() as isize,
- | - missing open `[` for this delimiter
+ | - this delimiter might not be properly closed...
...
+LL | }
+ | - ...as it matches this but it has different indentation
+LL |
LL | fn main() {}
| ^
diff --git a/tests/ui/parser/issues/issue-68987-unmatch-issue-2.rs b/tests/ui/parser/issues/issue-68987-unmatch-issue-2.rs
index 89aaa68..9b4452e 100644
--- a/tests/ui/parser/issues/issue-68987-unmatch-issue-2.rs
+++ b/tests/ui/parser/issues/issue-68987-unmatch-issue-2.rs
@@ -1,7 +1,7 @@
// FIXME: this case need more work to fix
// currently the TokenTree matching ')' with '{', which is not user friendly for diagnostics
async fn obstest() -> Result<> {
- let obs_connect = || -> Result<(), MyError) { //~ ERROR mismatched closing delimiter
+ let obs_connect = || -> Result<(), MyError) {
async {
}
}
diff --git a/tests/ui/parser/issues/issue-68987-unmatch-issue-2.stderr b/tests/ui/parser/issues/issue-68987-unmatch-issue-2.stderr
index 0ecb748..c29a4ff 100644
--- a/tests/ui/parser/issues/issue-68987-unmatch-issue-2.stderr
+++ b/tests/ui/parser/issues/issue-68987-unmatch-issue-2.stderr
@@ -1,19 +1,13 @@
-error: mismatched closing delimiter: `)`
- --> $DIR/issue-68987-unmatch-issue-2.rs:3:32
- |
-LL | async fn obstest() -> Result<> {
- | ^ unclosed delimiter
-LL | let obs_connect = || -> Result<(), MyError) {
- | ^ mismatched closing delimiter
-
error: unexpected closing delimiter: `}`
--> $DIR/issue-68987-unmatch-issue-2.rs:14:1
|
+LL | async fn obstest() -> Result<> {
+ | - the nearest open delimiter
LL | let obs_connect = || -> Result<(), MyError) {
| - missing open `(` for this delimiter
...
LL | }
| ^ unexpected closing delimiter
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
diff --git a/tests/ui/parser/issues/issue-68987-unmatch-issue-3.rs b/tests/ui/parser/issues/issue-68987-unmatch-issue-3.rs
index e98df8d..e71e273 100644
--- a/tests/ui/parser/issues/issue-68987-unmatch-issue-3.rs
+++ b/tests/ui/parser/issues/issue-68987-unmatch-issue-3.rs
@@ -3,6 +3,6 @@ fn f(i: u32, j: u32) {
let res = String::new();
let mut cnt = i;
while cnt < j {
- write!&mut res, " "); //~ ERROR mismatched closing delimiter
+ write!&mut res, " ");
}
} //~ ERROR unexpected closing delimiter
diff --git a/tests/ui/parser/issues/issue-68987-unmatch-issue-3.stderr b/tests/ui/parser/issues/issue-68987-unmatch-issue-3.stderr
index dfc4407..6b012af 100644
--- a/tests/ui/parser/issues/issue-68987-unmatch-issue-3.stderr
+++ b/tests/ui/parser/issues/issue-68987-unmatch-issue-3.stderr
@@ -1,19 +1,13 @@
-error: mismatched closing delimiter: `)`
- --> $DIR/issue-68987-unmatch-issue-3.rs:5:19
- |
-LL | while cnt < j {
- | ^ unclosed delimiter
-LL | write!&mut res, " ");
- | ^ mismatched closing delimiter
-
error: unexpected closing delimiter: `}`
--> $DIR/issue-68987-unmatch-issue-3.rs:8:1
|
+LL | while cnt < j {
+ | - the nearest open delimiter
LL | write!&mut res, " ");
| - missing open `(` for this delimiter
LL | }
LL | }
| ^ unexpected closing delimiter
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
diff --git a/tests/ui/parser/issues/issue-81827.stderr b/tests/ui/parser/issues/issue-81827.stderr
index 986ed6b..9737c8c 100644
--- a/tests/ui/parser/issues/issue-81827.stderr
+++ b/tests/ui/parser/issues/issue-81827.stderr
@@ -11,9 +11,8 @@
--> $DIR/issue-81827.rs:7:27
|
LL | fn r()->i{0|{#[cfg(r(0{]0
- | - - - ^
- | | | |
- | | | missing open `[` for this delimiter
+ | - - ^
+ | | |
| | unclosed delimiter
| unclosed delimiter
diff --git a/tests/ui/parser/issues/unnessary-error-issue-138401.rs b/tests/ui/parser/issues/unnessary-error-issue-138401.rs
new file mode 100644
index 0000000..208c516
--- /dev/null
+++ b/tests/ui/parser/issues/unnessary-error-issue-138401.rs
@@ -0,0 +1,6 @@
+pub fn foo(x: i64) -> i64 {
+ x.abs)
+}
+//~^ ERROR unexpected closing delimiter: `}`
+
+fn main() {}
diff --git a/tests/ui/parser/issues/unnessary-error-issue-138401.stderr b/tests/ui/parser/issues/unnessary-error-issue-138401.stderr
new file mode 100644
index 0000000..54c73b5
--- /dev/null
+++ b/tests/ui/parser/issues/unnessary-error-issue-138401.stderr
@@ -0,0 +1,12 @@
+error: unexpected closing delimiter: `}`
+ --> $DIR/unnessary-error-issue-138401.rs:3:1
+ |
+LL | pub fn foo(x: i64) -> i64 {
+ | - the nearest open delimiter
+LL | x.abs)
+ | - missing open `(` for this delimiter
+LL | }
+ | ^ unexpected closing delimiter
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/lexical-scopes.rs b/tests/ui/shadowed/shadowing-generic-item.rs
similarity index 75%
rename from tests/ui/lexical-scopes.rs
rename to tests/ui/shadowed/shadowing-generic-item.rs
index 46cfdf1..c3a0ced 100644
--- a/tests/ui/lexical-scopes.rs
+++ b/tests/ui/shadowed/shadowing-generic-item.rs
@@ -1,3 +1,5 @@
+//! Test that generic parameters shadow structs and modules with the same name.
+
struct T { i: i32 }
fn f<T>() {
let t = T { i: 0 }; //~ ERROR expected struct, variant or union type, found type parameter `T`
diff --git a/tests/ui/lexical-scopes.stderr b/tests/ui/shadowed/shadowing-generic-item.stderr
similarity index 89%
rename from tests/ui/lexical-scopes.stderr
rename to tests/ui/shadowed/shadowing-generic-item.stderr
index f0eaa1a..55a0bb3 100644
--- a/tests/ui/lexical-scopes.stderr
+++ b/tests/ui/shadowed/shadowing-generic-item.stderr
@@ -1,5 +1,5 @@
error[E0574]: expected struct, variant or union type, found type parameter `T`
- --> $DIR/lexical-scopes.rs:3:13
+ --> $DIR/shadowing-generic-item.rs:5:13
|
LL | struct T { i: i32 }
| - you might have meant to refer to this struct
@@ -9,7 +9,7 @@
| ^ not a struct, variant or union type
error[E0599]: no function or associated item named `f` found for type parameter `Foo` in the current scope
- --> $DIR/lexical-scopes.rs:10:10
+ --> $DIR/shadowing-generic-item.rs:12:10
|
LL | fn g<Foo>() {
| --- function or associated item `f` not found for this type parameter
diff --git a/tests/ui/struct-ctor-mangling.rs b/tests/ui/struct-ctor-mangling.rs
deleted file mode 100644
index f32cbb7..0000000
--- a/tests/ui/struct-ctor-mangling.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-//@ run-pass
-
-fn size_of_val<T>(_: &T) -> usize {
- std::mem::size_of::<T>()
-}
-
-struct Foo(#[allow(dead_code)] i64);
-
-// Test that the (symbol) mangling of `Foo` (the `struct` type) and that of
-// `typeof Foo` (the function type of the `struct` constructor) don't collide.
-fn main() {
- size_of_val(&Foo(0));
- size_of_val(&Foo);
-}
diff --git a/tests/ui/structs/default-field-values/const-trait-default-field-value.rs b/tests/ui/structs/default-field-values/const-trait-default-field-value.rs
new file mode 100644
index 0000000..60c22ef
--- /dev/null
+++ b/tests/ui/structs/default-field-values/const-trait-default-field-value.rs
@@ -0,0 +1,37 @@
+//@ check-pass
+
+// Ensure that `default_field_values` and `const_default` interact properly.
+
+#![feature(const_default)]
+#![feature(const_trait_impl)]
+#![feature(default_field_values)]
+#![feature(derive_const)]
+
+#[derive(PartialEq, Eq, Debug)]
+#[derive_const(Default)]
+struct S {
+ r: Option<String> = <Option<_> as Default>::default(),
+ s: String = String::default(),
+ o: Option<String> = Option::<String>::default(),
+ p: std::marker::PhantomData<()> = std::marker::PhantomData::default(),
+ q: Option<String> = <Option<String> as Default>::default(),
+ t: Option<String> = Option::default(),
+ v: Option<String> = const { Option::default() },
+}
+
+const _: S = S { .. };
+const _: S = const { S { .. } };
+const _: S = S::default();
+const _: S = const { S::default() };
+
+fn main() {
+ let s = S { .. };
+ assert_eq!(s.r, None);
+ assert_eq!(&s.s, "");
+ assert_eq!(s.o, None);
+ assert_eq!(s.p, std::marker::PhantomData);
+ assert_eq!(s.q, None);
+ assert_eq!(s.t, None);
+ assert_eq!(s.v, None);
+ assert_eq!(s, S::default());
+}
diff --git a/tests/ui/suggestions/issue-94171.stderr b/tests/ui/suggestions/issue-94171.stderr
index bcbd46c..52306a2 100644
--- a/tests/ui/suggestions/issue-94171.stderr
+++ b/tests/ui/suggestions/issue-94171.stderr
@@ -22,9 +22,7 @@
--> $DIR/issue-94171.rs:5:52
|
LL | fn L(]{match
- | -- unclosed delimiter
- | |
- | missing open `[` for this delimiter
+ | - unclosed delimiter
LL | (; {`
| - - unclosed delimiter
| |
diff --git a/tests/ui/symbol-names/struct-constructor-mangling.rs b/tests/ui/symbol-names/struct-constructor-mangling.rs
new file mode 100644
index 0000000..ec8791e
--- /dev/null
+++ b/tests/ui/symbol-names/struct-constructor-mangling.rs
@@ -0,0 +1,14 @@
+//! Test that the symbol mangling of Foo-the-constructor-function versus Foo-the-type do not collide
+
+//@ run-pass
+
+fn size_of_val<T>(_: &T) -> usize {
+ std::mem::size_of::<T>()
+}
+
+struct Foo(#[allow(dead_code)] i64);
+
+fn main() {
+ size_of_val(&Foo(0));
+ size_of_val(&Foo);
+}
diff --git a/tests/ui/traits/const-traits/const-impl-trait.rs b/tests/ui/traits/const-traits/const-impl-trait.rs
index dc96042..da28d9a 100644
--- a/tests/ui/traits/const-traits/const-impl-trait.rs
+++ b/tests/ui/traits/const-traits/const-impl-trait.rs
@@ -1,7 +1,7 @@
//@ compile-flags: -Znext-solver
//@ known-bug: #110395
-// Broken until we have `const PartialEq` impl in stdlib
+// Broken until `(): const PartialEq`
#![allow(incomplete_features)]
#![feature(const_trait_impl, const_cmp, const_destruct)]
diff --git a/tests/ui/traits/const-traits/const_derives/derive-const-gate.stderr b/tests/ui/traits/const-traits/const_derives/derive-const-gate.stderr
index 5ed12b3..cbc62d6 100644
--- a/tests/ui/traits/const-traits/const_derives/derive-const-gate.stderr
+++ b/tests/ui/traits/const-traits/const_derives/derive-const-gate.stderr
@@ -4,6 +4,7 @@
LL | #[derive_const(Debug)]
| ^^^^^^^^^^^^
|
+ = note: see issue #118304 <https://github.com/rust-lang/rust/issues/118304> for more information
= help: add `#![feature(derive_const)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
diff --git a/tests/ui/traits/const-traits/match-non-const-eq.gated.stderr b/tests/ui/traits/const-traits/match-non-const-eq.gated.stderr
deleted file mode 100644
index 89e59e5..0000000
--- a/tests/ui/traits/const-traits/match-non-const-eq.gated.stderr
+++ /dev/null
@@ -1,12 +0,0 @@
-error[E0015]: cannot match on `str` in constant functions
- --> $DIR/match-non-const-eq.rs:7:9
- |
-LL | "a" => (), //FIXME [gated]~ ERROR can't compare `str` with `str` in const contexts
- | ^^^
- |
- = note: `str` cannot be compared in compile-time, and therefore cannot be used in `match`es
- = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0015`.
diff --git a/tests/ui/traits/const-traits/match-non-const-eq.rs b/tests/ui/traits/const-traits/match-non-const-eq.rs
index 73f8af8..03adb8d 100644
--- a/tests/ui/traits/const-traits/match-non-const-eq.rs
+++ b/tests/ui/traits/const-traits/match-non-const-eq.rs
@@ -1,11 +1,12 @@
-//@ known-bug: #110395
//@ revisions: stock gated
-#![cfg_attr(gated, feature(const_trait_impl))]
+#![cfg_attr(gated, feature(const_trait_impl, const_cmp))]
+//@[gated] check-pass
const fn foo(input: &'static str) {
match input {
- "a" => (), //FIXME [gated]~ ERROR can't compare `str` with `str` in const contexts
- //FIXME ~^ ERROR cannot match on `str` in constant functions
+ "a" => (),
+ //[stock]~^ ERROR cannot match on `str` in constant functions
+ //[stock]~| ERROR `PartialEq` is not yet stable as a const trait
_ => (),
}
}
diff --git a/tests/ui/traits/const-traits/match-non-const-eq.stock.stderr b/tests/ui/traits/const-traits/match-non-const-eq.stock.stderr
index 89e59e5..5b66244 100644
--- a/tests/ui/traits/const-traits/match-non-const-eq.stock.stderr
+++ b/tests/ui/traits/const-traits/match-non-const-eq.stock.stderr
@@ -1,12 +1,26 @@
-error[E0015]: cannot match on `str` in constant functions
+error[E0658]: cannot match on `str` in constant functions
--> $DIR/match-non-const-eq.rs:7:9
|
-LL | "a" => (), //FIXME [gated]~ ERROR can't compare `str` with `str` in const contexts
+LL | "a" => (),
| ^^^
|
= note: `str` cannot be compared in compile-time, and therefore cannot be used in `match`es
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+ = note: see issue #143874 <https://github.com/rust-lang/rust/issues/143874> for more information
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+ = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-error: aborting due to 1 previous error
+error: `PartialEq` is not yet stable as a const trait
+ --> $DIR/match-non-const-eq.rs:7:9
+ |
+LL | "a" => (),
+ | ^^^
+ |
+help: add `#![feature(const_cmp)]` to the crate attributes to enable
+ |
+LL + #![feature(const_cmp)]
+ |
-For more information about this error, try `rustc --explain E0015`.
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/typeck/issue-91334.stderr b/tests/ui/typeck/issue-91334.stderr
index 01e3491..a348e1e 100644
--- a/tests/ui/typeck/issue-91334.stderr
+++ b/tests/ui/typeck/issue-91334.stderr
@@ -11,9 +11,8 @@
--> $DIR/issue-91334.rs:7:23
|
LL | fn f(){||yield(((){),
- | - - - ^
- | | | |
- | | | missing open `(` for this delimiter
+ | - - ^
+ | | |
| | unclosed delimiter
| unclosed delimiter
diff --git a/tests/ui/underscore-lifetime/raw-underscore-lifetime.rs b/tests/ui/underscore-lifetime/raw-underscore-lifetime.rs
new file mode 100644
index 0000000..874b3d2
--- /dev/null
+++ b/tests/ui/underscore-lifetime/raw-underscore-lifetime.rs
@@ -0,0 +1,9 @@
+// This test is to ensure that the raw underscore lifetime won't emit two duplicate errors.
+// See issue #143152
+
+//@ edition: 2021
+
+fn f<'r#_>(){}
+//~^ ERROR `_` cannot be a raw lifetime
+
+fn main() {}
diff --git a/tests/ui/underscore-lifetime/raw-underscore-lifetime.stderr b/tests/ui/underscore-lifetime/raw-underscore-lifetime.stderr
new file mode 100644
index 0000000..bdb357a
--- /dev/null
+++ b/tests/ui/underscore-lifetime/raw-underscore-lifetime.stderr
@@ -0,0 +1,8 @@
+error: `_` cannot be a raw lifetime
+ --> $DIR/raw-underscore-lifetime.rs:6:6
+ |
+LL | fn f<'r#_>(){}
+ | ^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/triagebot.toml b/triagebot.toml
index 2f8114a..61d8a81 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -471,6 +471,7 @@
[autolabel."T-infra"]
trigger_files = [
+ ".github/workflows",
"src/ci",
"src/tools/bump-stage0",
"src/tools/cargotest",
@@ -598,6 +599,12 @@
"src/tools/clippy",
]
+[autolabel."A-CI"]
+trigger_files = [
+ ".github/workflows",
+ "src/ci",
+]
+
# ------------------------------------------------------------------------------
# Prioritization and team nominations
# ------------------------------------------------------------------------------
@@ -1316,6 +1323,7 @@
"@Kobzol",
"@marcoieni",
"@jdno",
+ "@jieyouxu",
]
rustdoc = [
"@GuillaumeGomez",