diff --git a/Cargo.lock b/Cargo.lock index a3f7b3a4367db..f035068a600d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6070,6 +6070,7 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" name = "unicode-table-generator" version = "0.1.0" dependencies = [ + "rustc-hash 2.1.1", "ucd-parse", ] diff --git a/RELEASES.md b/RELEASES.md index 3ef074340de46..9dfb5c86e3cae 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -77,6 +77,7 @@ Compatibility Notes - [For `export_name`, `link_name`, and `link_section` attributes, if multiple of the same attribute is present, the first one now takes precedence.](https://github.com/rust-lang/rust/pull/153041) - [Update the minimum external LLVM to 21](https://github.com/rust-lang/rust/pull/153684) - On `avr` targets, C's `double` type is 32-bit by default, so [change `c_double` to `f32` on `avr` targets to match](https://github.com/rust-lang/rust/pull/154647). This is a breaking change, but necessary to make `c_double` match C's double. +- [`BTreeMap::append()` was optimized, which may now cause panics for types with incorrect `Ord` impls](https://github.com/rust-lang/rust/pull/153107) diff --git a/compiler/rustc_abi/src/canon_abi.rs b/compiler/rustc_abi/src/canon_abi.rs index 316cb05ec1806..6b4963ae92461 100644 --- a/compiler/rustc_abi/src/canon_abi.rs +++ b/compiler/rustc_abi/src/canon_abi.rs @@ -28,6 +28,7 @@ pub enum CanonAbi { Rust, RustCold, RustPreserveNone, + RustTail, /// An ABI that rustc does not know how to call or define. Custom, @@ -59,7 +60,10 @@ pub enum CanonAbi { impl CanonAbi { pub fn is_rustic_abi(self) -> bool { match self { - CanonAbi::Rust | CanonAbi::RustCold | CanonAbi::RustPreserveNone => true, + CanonAbi::Rust + | CanonAbi::RustCold + | CanonAbi::RustPreserveNone + | CanonAbi::RustTail => true, CanonAbi::C | CanonAbi::Custom | CanonAbi::Swift @@ -81,6 +85,7 @@ impl fmt::Display for CanonAbi { CanonAbi::Rust => ExternAbi::Rust, CanonAbi::RustCold => ExternAbi::RustCold, CanonAbi::RustPreserveNone => ExternAbi::RustPreserveNone, + CanonAbi::RustTail => ExternAbi::RustTail, CanonAbi::Custom => ExternAbi::Custom, CanonAbi::Swift => ExternAbi::Swift, CanonAbi::Arm(arm_call) => match arm_call { diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs index 3def8a8ccf0ba..f30b923eeed17 100644 --- a/compiler/rustc_abi/src/extern_abi.rs +++ b/compiler/rustc_abi/src/extern_abi.rs @@ -49,6 +49,11 @@ pub enum ExternAbi { /// forcing callers to save all registers. RustPreserveNone, + /// Ensures that calls in tail position can always be optimized into a jump. + /// + /// This ABI is not stable, and relies on LLVM implementation details. + RustTail, + /// Unstable impl detail that directly uses Rust types to describe the ABI to LLVM. /// Even normally-compatible Rust types can become ABI-incompatible with this ABI! Unadjusted, @@ -205,6 +210,7 @@ abi_impls! { System { unwind: true } =><= "system-unwind", SysV64 { unwind: false } =><= "sysv64", SysV64 { unwind: true } =><= "sysv64-unwind", + RustTail =><= "tail", Thiscall { unwind: false } =><= "thiscall", Thiscall { unwind: true } =><= "thiscall-unwind", Unadjusted =><= "unadjusted", @@ -280,7 +286,7 @@ impl ExternAbi { /// - are subject to change between compiler versions pub fn is_rustic_abi(self) -> bool { use ExternAbi::*; - matches!(self, Rust | RustCall | RustCold | RustPreserveNone) + matches!(self, Rust | RustCall | RustCold | RustPreserveNone | RustTail) } /// Returns whether the ABI supports C variadics. This only controls whether we allow *imports* @@ -354,6 +360,7 @@ impl ExternAbi { | Self::SysV64 { .. } | Self::Win64 { .. } | Self::RustPreserveNone + | Self::RustTail | Self::Swift => true, } } diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index e8df8ce6e6dc3..5628dffd51358 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -9,8 +9,7 @@ use rustc_session::errors::feature_err; use rustc_span::{Span, sym}; use rustc_target::asm; -use super::LoweringContext; -use super::errors::{ +use crate::diagnostics::{ AbiSpecifiedMultipleTimes, AttSyntaxOnlyX86, ClobberAbiNotSupported, InlineAsmUnsupportedTarget, InvalidAbiClobberAbi, InvalidAsmTemplateModifierConst, InvalidAsmTemplateModifierLabel, InvalidAsmTemplateModifierRegClass, @@ -18,7 +17,9 @@ use super::errors::{ InvalidRegisterClass, RegisterClassOnlyClobber, RegisterClassOnlyClobberStable, RegisterConflict, }; -use crate::{AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, ParamMode}; +use crate::{ + AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode, +}; impl<'hir> LoweringContext<'_, 'hir> { pub(crate) fn lower_inline_asm( diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 5af13c70ef693..68ae9e68b029a 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -56,7 +56,7 @@ use rustc_span::symbol::kw; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; use crate::delegation::generics::{GenericsGenerationResult, GenericsGenerationResults}; -use crate::errors::{ +use crate::diagnostics::{ CycleInDelegationSignatureResolution, DelegationAttemptedBlockWithDefsDeletion, DelegationBlockSpecifiedWhenNoParams, UnresolvedDelegationCallee, }; diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/diagnostics.rs similarity index 100% rename from compiler/rustc_ast_lowering/src/errors.rs rename to compiler/rustc_ast_lowering/src/diagnostics.rs diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index ce20aaf4e0276..23c7159cb91fe 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -19,17 +19,17 @@ use visit::{Visitor, walk_expr}; mod closure; -use super::errors::{ +use crate::diagnostics::{ AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, - FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody, - MoveExprOnlyInPlainClosures, NeverPatternWithBody, NeverPatternWithGuard, - UnderscoreExprLhsAssign, + FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, + InvalidLegacyConstGenericArg, MatchArmWithNoBody, MoveExprOnlyInPlainClosures, + NeverPatternWithBody, NeverPatternWithGuard, UnderscoreExprLhsAssign, UseConstGenericArg, + YieldInClosure, }; -use super::{ - GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt, +use crate::{ + AllowReturnTypeNotation, GenericArgsMode, ImplTraitContext, ImplTraitPosition, LoweringContext, + ParamMode, ResolverAstLoweringExt, TryBlockScope, }; -use crate::errors::{InvalidLegacyConstGenericArg, UseConstGenericArg, YieldInClosure}; -use crate::{AllowReturnTypeNotation, ImplTraitPosition, TryBlockScope}; pub(super) struct WillCreateDefIdsVisitor; diff --git a/compiler/rustc_ast_lowering/src/expr/closure.rs b/compiler/rustc_ast_lowering/src/expr/closure.rs index 1c2a22e475232..cc2c6ae2879c1 100644 --- a/compiler/rustc_ast_lowering/src/expr/closure.rs +++ b/compiler/rustc_ast_lowering/src/expr/closure.rs @@ -7,7 +7,7 @@ use rustc_span::Span; use super::{LoweringContext, MoveExprInitializerFinder, MoveExprState}; use crate::FnDeclKind; -use crate::errors::{ClosureCannotBeStatic, CoroutineTooManyParameters}; +use crate::diagnostics::{ClosureCannotBeStatic, CoroutineTooManyParameters}; impl<'hir> LoweringContext<'_, 'hir> { // Entry point for `ExprKind::Closure`. Plain closures go through diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 99c31551d234d..0e518c10e3bb7 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -19,7 +19,9 @@ use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; use tracing::instrument; -use super::errors::{InvalidAbi, InvalidAbiSuggestion, TupleStructWithDefault, UnionWithDefault}; +use super::diagnostics::{ + InvalidAbi, InvalidAbiSuggestion, TupleStructWithDefault, UnionWithDefault, +}; use super::stability::{enabled_names, gate_unstable_abi}; use super::{ AstOwner, FnDeclKind, GenericArgsMode, ImplTraitContext, ImplTraitPosition, LoweringContext, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 6dc7de2911c0c..8b4a2795ec90c 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -70,7 +70,7 @@ use smallvec::SmallVec; use thin_vec::ThinVec; use tracing::{debug, instrument, trace}; -use crate::errors::{AssocTyParentheses, AssocTyParenthesesSub, MisplacedImplTrait}; +use crate::diagnostics::{AssocTyParentheses, AssocTyParenthesesSub, MisplacedImplTrait}; use crate::item::Owners; macro_rules! arena_vec { @@ -83,7 +83,7 @@ mod asm; mod block; mod contract; mod delegation; -mod errors; +mod diagnostics; mod expr; mod format; mod index; @@ -1145,17 +1145,21 @@ impl<'hir> LoweringContext<'_, 'hir> { { let err = match (&data.inputs[..], &data.output) { ([_, ..], FnRetTy::Default(_)) => { - errors::BadReturnTypeNotation::Inputs { span: data.inputs_span } + diagnostics::BadReturnTypeNotation::Inputs { + span: data.inputs_span, + } } ([], FnRetTy::Default(_)) => { - errors::BadReturnTypeNotation::NeedsDots { span: data.inputs_span } + diagnostics::BadReturnTypeNotation::NeedsDots { + span: data.inputs_span, + } } // The case `T: Trait Ret>` is handled in the parser. (_, FnRetTy::Ty(ty)) => { let span = data.inputs_span.shrink_to_hi().to(ty.span); - errors::BadReturnTypeNotation::Output { + diagnostics::BadReturnTypeNotation::Output { span, - suggestion: errors::RTNSuggestion { + suggestion: diagnostics::RTNSuggestion { output: span, input: data.inputs_span, }, @@ -1226,7 +1230,7 @@ impl<'hir> LoweringContext<'_, 'hir> { _ => None, }; - let guar = self.dcx().emit_err(errors::MisplacedAssocTyBinding { + let guar = self.dcx().emit_err(diagnostics::MisplacedAssocTyBinding { span: constraint.span, suggestion, }); @@ -1529,7 +1533,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ast::GenericBound::Use(_, span) => Some(span), _ => None, }) { - self.tcx.dcx().emit_err(errors::NoPreciseCapturesOnApit { span }); + self.tcx.dcx().emit_err(diagnostics::NoPreciseCapturesOnApit { span }); } let def_id = self.local_def_id(*def_node_id); @@ -2123,7 +2127,7 @@ impl<'hir> LoweringContext<'_, 'hir> { .filter(|_| match source { hir::GenericParamSource::Generics => true, hir::GenericParamSource::Binder => { - self.dcx().emit_err(errors::GenericParamDefaultInBinder { + self.dcx().emit_err(diagnostics::GenericParamDefaultInBinder { span: param.span(), }); @@ -2154,7 +2158,8 @@ impl<'hir> LoweringContext<'_, 'hir> { .filter(|anon_const| match source { hir::GenericParamSource::Generics => true, hir::GenericParamSource::Binder => { - let err = errors::GenericParamDefaultInBinder { span: param.span() }; + let err = + diagnostics::GenericParamDefaultInBinder { span: param.span() }; if expr::WillCreateDefIdsVisitor .visit_expr(&anon_const.value) .is_break() diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index cf0615b8244e5..8780b70fadfa4 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -7,10 +7,10 @@ use rustc_hir::{self as hir, LangItem, Target}; use rustc_middle::span_bug; use rustc_span::{DesugaringKind, Ident, Span, Spanned, respan}; -use super::errors::{ +use crate::diagnostics::{ ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding, }; -use super::{ +use crate::{ AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode, }; diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index f5a306aa9140d..42c4af5add12d 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -11,11 +11,11 @@ use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Ident, Span, Symbol, sym}; use smallvec::smallvec; use tracing::{debug, instrument}; -use super::errors::{ +use crate::diagnostics::{ AsyncBoundNotOnTrait, AsyncBoundOnlyForFnTraits, BadReturnTypeNotation, GenericTypeWithParentheses, RTNSuggestion, UseAngleBrackets, }; -use super::{ +use crate::{ AllowReturnTypeNotation, GenericArgsCtor, GenericArgsMode, ImplTraitContext, ImplTraitPosition, LifetimeRes, LoweringContext, ParamMode, }; diff --git a/compiler/rustc_ast_lowering/src/stability.rs b/compiler/rustc_ast_lowering/src/stability.rs index 00b6a353d93f6..b58087e4aa3a6 100644 --- a/compiler/rustc_ast_lowering/src/stability.rs +++ b/compiler/rustc_ast_lowering/src/stability.rs @@ -100,6 +100,9 @@ pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> { feature: sym::rust_preserve_none_cc, explain: GateReason::Experimental, }), + ExternAbi::RustTail => { + Err(UnstableAbi { abi, feature: sym::rust_tail_cc, explain: GateReason::Experimental }) + } ExternAbi::RustInvalid => { Err(UnstableAbi { abi, feature: sym::rustc_attrs, explain: GateReason::ImplDetail }) } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index afaee6e542082..db0c5256184c5 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -39,7 +39,7 @@ use rustc_span::{Ident, Span, kw, sym}; use rustc_target::spec::{AbiMap, AbiMapping}; use thin_vec::thin_vec; -use crate::errors::{self, TildeConstReason}; +use crate::diagnostics::{self, TildeConstReason}; /// Is `self` allowed semantically as the first parameter in an `FnDecl`? enum SelfSemantic { @@ -161,7 +161,7 @@ impl<'a> AstValidator<'a> { fn check_type_alias_where_clause_location( &mut self, ty_alias: &TyAlias, - ) -> Result<(), errors::WhereClauseBeforeTypeAlias> { + ) -> Result<(), diagnostics::WhereClauseBeforeTypeAlias> { if ty_alias.ty.is_none() || !ty_alias.generics.where_clause.has_where_token { return Ok(()); } @@ -189,16 +189,16 @@ impl<'a> AstValidator<'a> { state.print_where_predicate(p); } - errors::WhereClauseBeforeTypeAliasSugg::Move { + diagnostics::WhereClauseBeforeTypeAliasSugg::Move { left: span, snippet: state.s.eof(), right: ty_alias.after_where_clause.span.shrink_to_hi(), } } else { - errors::WhereClauseBeforeTypeAliasSugg::Remove { span } + diagnostics::WhereClauseBeforeTypeAliasSugg::Remove { span } }; - Err(errors::WhereClauseBeforeTypeAlias { span, sugg }) + Err(diagnostics::WhereClauseBeforeTypeAlias { span, sugg }) } fn with_impl_trait(&mut self, outer_span: Option, f: impl FnOnce(&mut Self)) { @@ -226,7 +226,7 @@ impl<'a> AstValidator<'a> { if let Some(bound1) = use_bounds.next() && let Some(bound2) = use_bounds.next() { - self.dcx().emit_err(errors::DuplicatePreciseCapturing { bound1, bound2 }); + self.dcx().emit_err(diagnostics::DuplicatePreciseCapturing { bound1, bound2 }); } } TyKind::TraitObject(..) => self @@ -241,12 +241,16 @@ impl<'a> AstValidator<'a> { self.sess.dcx() } - fn visibility_not_permitted(&self, vis: &Visibility, note: errors::VisibilityNotPermittedNote) { + fn visibility_not_permitted( + &self, + vis: &Visibility, + note: diagnostics::VisibilityNotPermittedNote, + ) { if let VisibilityKind::Inherited = vis.kind { return; } - self.dcx().emit_err(errors::VisibilityNotPermitted { + self.dcx().emit_err(diagnostics::VisibilityNotPermitted { span: vis.span, note, remove_qualifier_sugg: vis.span, @@ -276,7 +280,7 @@ impl<'a> AstValidator<'a> { return; }; - self.dcx().emit_err(errors::ImplFnConst { span, parent_constness }); + self.dcx().emit_err(diagnostics::ImplFnConst { span, parent_constness }); } fn check_trait_fn_not_const(&self, constness: Const, parent: &TraitOrImpl) { @@ -309,7 +313,7 @@ impl<'a> AstValidator<'a> { }; let parent_constness = parent.constness(); - self.dcx().emit_err(errors::TraitFnConst { + self.dcx().emit_err(diagnostics::TraitFnConst { span, in_impl: matches!(parent, TraitOrImpl::TraitImpl { .. }), const_context_label: parent_constness, @@ -341,7 +345,7 @@ impl<'a> AstValidator<'a> { TraitOrImpl::Impl { .. } => "impl", }; - self.dcx().emit_err(errors::AsyncFnInConstTraitOrTraitImpl { + self.dcx().emit_err(diagnostics::AsyncFnInConstTraitOrTraitImpl { async_keyword, context, const_keyword, @@ -361,7 +365,7 @@ impl<'a> AstValidator<'a> { let max_num_args: usize = u16::MAX.into(); if fn_decl.inputs.len() > max_num_args { let Param { span, .. } = fn_decl.inputs[0]; - self.dcx().emit_fatal(errors::FnParamTooMany { span, max_num_args }); + self.dcx().emit_fatal(diagnostics::FnParamTooMany { span, max_num_args }); } } @@ -373,7 +377,7 @@ impl<'a> AstValidator<'a> { [ps @ .., _] => { for Param { ty, span, .. } in ps { if let TyKind::CVarArgs = ty.kind { - self.dcx().emit_err(errors::FnParamCVarArgsNotLast { span: *span }); + self.dcx().emit_err(diagnostics::FnParamCVarArgsNotLast { span: *span }); } } } @@ -400,9 +404,9 @@ impl<'a> AstValidator<'a> { }) .for_each(|attr| { if attr.is_doc_comment() { - self.dcx().emit_err(errors::FnParamDocComment { span: attr.span }); + self.dcx().emit_err(diagnostics::FnParamDocComment { span: attr.span }); } else { - self.dcx().emit_err(errors::FnParamForbiddenAttr { span: attr.span }); + self.dcx().emit_err(diagnostics::FnParamForbiddenAttr { span: attr.span }); } }); } @@ -410,7 +414,7 @@ impl<'a> AstValidator<'a> { fn check_decl_self_param(&self, fn_decl: &FnDecl, self_semantic: SelfSemantic) { if let (SelfSemantic::No, [param, ..]) = (self_semantic, &*fn_decl.inputs) { if param.is_self() { - self.dcx().emit_err(errors::FnParamForbiddenSelf { span: param.span }); + self.dcx().emit_err(diagnostics::FnParamForbiddenSelf { span: param.span }); } } } @@ -424,6 +428,7 @@ impl<'a> AstValidator<'a> { | CanonAbi::Rust | CanonAbi::RustCold | CanonAbi::RustPreserveNone + | CanonAbi::RustTail | CanonAbi::Swift | CanonAbi::Arm(_) | CanonAbi::X86(_) => { /* nothing to check */ } @@ -462,7 +467,8 @@ impl<'a> AstValidator<'a> { if spans.is_empty() { spans = vec![sig.span]; } - self.dcx().emit_err(errors::AbiX86Interrupt { spans, param_count }); + self.dcx() + .emit_err(diagnostics::AbiX86Interrupt { spans, param_count }); } self.reject_return(abi, sig); @@ -485,12 +491,15 @@ impl<'a> AstValidator<'a> { Safety::Safe(safe_span) => { let source_map = self.sess.psess.source_map(); let safe_span = source_map.span_until_non_whitespace(safe_span.to(sig.span)); - dcx.emit_err(errors::AbiCustomSafeForeignFunction { span: sig.span, safe_span }); + dcx.emit_err(diagnostics::AbiCustomSafeForeignFunction { + span: sig.span, + safe_span, + }); } Safety::Default => match ctxt { FnCtxt::Foreign => { /* all good */ } FnCtxt::Free | FnCtxt::Assoc(_) => { - dcx.emit_err(errors::AbiCustomSafeFunction { + dcx.emit_err(diagnostics::AbiCustomSafeFunction { span: sig.span, abi, unsafe_span: sig.span.shrink_to_lo(), @@ -508,7 +517,7 @@ impl<'a> AstValidator<'a> { .source_map() .span_until_non_whitespace(coroutine_kind.span().to(sig.span)); - self.dcx().emit_err(errors::AbiCannotBeCoroutine { + self.dcx().emit_err(diagnostics::AbiCannotBeCoroutine { span: sig.span, abi, coroutine_kind_span, @@ -525,7 +534,7 @@ impl<'a> AstValidator<'a> { _ => true, } { - self.dcx().emit_err(errors::AbiMustNotHaveReturnType { span: ret_ty.span, abi }); + self.dcx().emit_err(diagnostics::AbiMustNotHaveReturnType { span: ret_ty.span, abi }); } } @@ -546,7 +555,7 @@ impl<'a> AstValidator<'a> { let suggestion_span = header_span.shrink_to_hi().to(sig.decl.output.span()); let padding = if header_span.is_empty() { "" } else { " " }; - self.dcx().emit_err(errors::AbiMustNotHaveParametersOrReturnType { + self.dcx().emit_err(diagnostics::AbiMustNotHaveParametersOrReturnType { spans, symbol: ident.name, suggestion_span, @@ -567,7 +576,7 @@ impl<'a> AstValidator<'a> { if matches!(safety, Safety::Unsafe(_) | Safety::Safe(_)) && extern_safety == Safety::Default { - self.dcx().emit_err(errors::InvalidSafetyOnExtern { + self.dcx().emit_err(diagnostics::InvalidSafetyOnExtern { item_span: span, block: Some(self.current_extern_span().shrink_to_lo()), }); @@ -575,7 +584,7 @@ impl<'a> AstValidator<'a> { } None => { if matches!(safety, Safety::Safe(_)) { - self.dcx().emit_err(errors::InvalidSafetyOnItem { span }); + self.dcx().emit_err(diagnostics::InvalidSafetyOnItem { span }); } } } @@ -583,7 +592,7 @@ impl<'a> AstValidator<'a> { fn check_fn_ptr_safety(&self, span: Span, safety: Safety) { if matches!(safety, Safety::Safe(_)) { - self.dcx().emit_err(errors::InvalidSafetyOnFnPtr { span }); + self.dcx().emit_err(diagnostics::InvalidSafetyOnFnPtr { span }); } } @@ -597,11 +606,11 @@ impl<'a> AstValidator<'a> { match defaultness { Defaultness::Default(def_span) if matches!(allow_default, AllowDefault::No) => { let span = self.sess.source_map().guess_head_span(span); - self.dcx().emit_err(errors::ForbiddenDefault { span, def_span }); + self.dcx().emit_err(diagnostics::ForbiddenDefault { span, def_span }); } Defaultness::Final(def_span) if matches!(allow_final, AllowFinal::No) => { let span = self.sess.source_map().guess_head_span(span); - self.dcx().emit_err(errors::ForbiddenFinal { span, def_span }); + self.dcx().emit_err(diagnostics::ForbiddenFinal { span, def_span }); } _ => (), } @@ -612,7 +621,7 @@ impl<'a> AstValidator<'a> { && let Defaultness::Final(def_span) = defaultness { let span = self.sess.source_map().guess_head_span(item.span); - self.dcx().emit_err(errors::ForbiddenFinalWithoutBody { span, def_span }); + self.dcx().emit_err(diagnostics::ForbiddenFinalWithoutBody { span, def_span }); } } @@ -635,12 +644,12 @@ impl<'a> AstValidator<'a> { [b0] => b0.span(), [b0, .., bl] => b0.span().to(bl.span()), }; - self.dcx().emit_err(errors::BoundInContext { span, ctx }); + self.dcx().emit_err(diagnostics::BoundInContext { span, ctx }); } fn check_foreign_ty_genericless(&self, generics: &Generics, after_where_clause: &WhereClause) { let cannot_have = |span, descr, remove_descr| { - self.dcx().emit_err(errors::ExternTypesCannotHave { + self.dcx().emit_err(diagnostics::ExternTypesCannotHave { span, descr, remove_descr, @@ -666,7 +675,7 @@ impl<'a> AstValidator<'a> { let Some(body_span) = body_span else { return; }; - self.dcx().emit_err(errors::BodyInExtern { + self.dcx().emit_err(diagnostics::BodyInExtern { span: ident.span, body: body_span, block: self.current_extern_span(), @@ -679,7 +688,7 @@ impl<'a> AstValidator<'a> { let Some(body) = body else { return; }; - self.dcx().emit_err(errors::FnBodyInExtern { + self.dcx().emit_err(diagnostics::FnBodyInExtern { span: ident.span, body: body.span, block: self.current_extern_span(), @@ -697,7 +706,7 @@ impl<'a> AstValidator<'a> { FnHeader { safety: _, coroutine_kind, constness, ext }: FnHeader, ) { let report_err = |span, kw| { - self.dcx().emit_err(errors::FnQualifierInExtern { + self.dcx().emit_err(diagnostics::FnQualifierInExtern { span, kw, block: self.current_extern_span(), @@ -720,7 +729,7 @@ impl<'a> AstValidator<'a> { /// An item in `extern { ... }` cannot use non-ascii identifier. fn check_foreign_item_ascii_only(&self, ident: Ident) { if !ident.as_str().is_ascii() { - self.dcx().emit_err(errors::ExternItemAscii { + self.dcx().emit_err(diagnostics::ExternItemAscii { span: ident.span, block: self.current_extern_span(), }); @@ -752,7 +761,7 @@ impl<'a> AstValidator<'a> { } if let Some(coroutine_kind) = sig.header.coroutine_kind { - self.dcx().emit_err(errors::CoroutineAndCVariadic { + self.dcx().emit_err(diagnostics::CoroutineAndCVariadic { spans: vec![coroutine_kind.span(), variadic_param.span], coroutine_kind: coroutine_kind.as_str(), coroutine_span: coroutine_kind.span(), @@ -765,7 +774,7 @@ impl<'a> AstValidator<'a> { FnCtxt::Free | FnCtxt::Assoc(_) => { match self.sess.target.supports_c_variadic_definitions() { CVariadicStatus::NotSupported => { - self.dcx().emit_err(errors::CVariadicNotSupported { + self.dcx().emit_err(diagnostics::CVariadicNotSupported { variadic_span: variadic_param.span, target: &*self.sess.target.llvm_target, }); @@ -785,7 +794,7 @@ impl<'a> AstValidator<'a> { match sig.header.ext { Extern::Implicit(_) => { if !matches!(sig.header.safety, Safety::Unsafe(_)) { - self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + self.dcx().emit_err(diagnostics::CVariadicMustBeUnsafe { span: variadic_param.span, unsafe_span: sig.safety_span(), }); @@ -800,14 +809,14 @@ impl<'a> AstValidator<'a> { self.check_c_variadic_abi(abi, attrs, variadic_param.span, sig); if !matches!(sig.header.safety, Safety::Unsafe(_)) { - self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + self.dcx().emit_err(diagnostics::CVariadicMustBeUnsafe { span: variadic_param.span, unsafe_span: sig.safety_span(), }); } } Extern::None => { - let err = errors::CVariadicNoExtern { span: variadic_param.span }; + let err = diagnostics::CVariadicNoExtern { span: variadic_param.span }; self.dcx().emit_err(err); } } @@ -854,7 +863,7 @@ impl<'a> AstValidator<'a> { } CVariadicStatus::NotSupported => { // Some ABIs, e.g. `extern "Rust"`, never support c-variadic functions. - self.dcx().emit_err(errors::CVariadicBadNakedExtern { + self.dcx().emit_err(diagnostics::CVariadicBadNakedExtern { span: dotdotdot_span, abi: abi.as_str(), extern_span: sig.extern_span(), @@ -862,7 +871,7 @@ impl<'a> AstValidator<'a> { } } } else if !matches!(abi, ExternAbi::C { .. }) { - self.dcx().emit_err(errors::CVariadicBadExtern { + self.dcx().emit_err(diagnostics::CVariadicBadExtern { span: dotdotdot_span, abi: abi.as_str(), extern_span: sig.extern_span(), @@ -874,7 +883,7 @@ impl<'a> AstValidator<'a> { if ident.name != kw::Underscore { return; } - self.dcx().emit_err(errors::ItemUnderscore { span: ident.span, kind }); + self.dcx().emit_err(diagnostics::ItemUnderscore { span: ident.span, kind }); } fn check_nomangle_item_asciionly(&self, ident: Ident, item_span: Span) { @@ -882,26 +891,26 @@ impl<'a> AstValidator<'a> { return; } let span = self.sess.source_map().guess_head_span(item_span); - self.dcx().emit_err(errors::NoMangleAscii { span }); + self.dcx().emit_err(diagnostics::NoMangleAscii { span }); } fn check_mod_file_item_asciionly(&self, ident: Ident) { if ident.name.as_str().is_ascii() { return; } - self.dcx().emit_err(errors::ModuleNonAscii { span: ident.span, name: ident.name }); + self.dcx().emit_err(diagnostics::ModuleNonAscii { span: ident.span, name: ident.name }); } fn deny_const_auto_traits(&self, constness: Const) { if let Const::Yes(span) = constness { - self.dcx().emit_err(errors::ConstAutoTrait { span }); + self.dcx().emit_err(diagnostics::ConstAutoTrait { span }); } } fn deny_generic_params(&self, generics: &Generics, ident_span: Span) { if !generics.params.is_empty() { self.dcx() - .emit_err(errors::AutoTraitGeneric { span: generics.span, ident: ident_span }); + .emit_err(diagnostics::AutoTraitGeneric { span: generics.span, ident: ident_span }); } } @@ -909,7 +918,7 @@ impl<'a> AstValidator<'a> { if let [.., last] = &bounds[..] { let span = bounds.iter().map(|b| b.span()).collect(); let removal = ident.shrink_to_hi().to(last.span()); - self.dcx().emit_err(errors::AutoTraitBounds { span, removal, ident }); + self.dcx().emit_err(diagnostics::AutoTraitBounds { span, removal, ident }); } } @@ -917,7 +926,7 @@ impl<'a> AstValidator<'a> { if !where_clause.predicates.is_empty() { // FIXME: The current diagnostic is misleading since it only talks about // super trait and lifetime bounds while we should just say “bounds”. - self.dcx().emit_err(errors::AutoTraitBounds { + self.dcx().emit_err(diagnostics::AutoTraitBounds { span: vec![where_clause.span], removal: where_clause.span, ident, @@ -929,7 +938,7 @@ impl<'a> AstValidator<'a> { if !trait_items.is_empty() { let spans: Vec<_> = trait_items.iter().map(|i| i.kind.ident().unwrap().span).collect(); let total = trait_items.first().unwrap().span.to(trait_items.last().unwrap().span); - self.dcx().emit_err(errors::AutoTraitItems { spans, total, ident: ident_span }); + self.dcx().emit_err(diagnostics::AutoTraitItems { spans, total, ident: ident_span }); } } @@ -975,13 +984,13 @@ impl<'a> AstValidator<'a> { let args_len = arg_spans.len(); let constraint_len = constraint_spans.len(); // ...and then error: - self.dcx().emit_err(errors::ArgsBeforeConstraint { + self.dcx().emit_err(diagnostics::ArgsBeforeConstraint { arg_spans: arg_spans.clone(), constraints: constraint_spans[0], args: *arg_spans.iter().last().unwrap(), data: data.span, - constraint_spans: errors::EmptyLabelManySpans(constraint_spans), - arg_spans2: errors::EmptyLabelManySpans(arg_spans), + constraint_spans: diagnostics::EmptyLabelManySpans(constraint_spans), + arg_spans2: diagnostics::EmptyLabelManySpans(arg_spans), suggestion: self.correct_generic_order_suggestion(data), constraint_len, args_len, @@ -994,7 +1003,7 @@ impl<'a> AstValidator<'a> { self.check_fn_ptr_safety(bfty.decl_span, bfty.safety); self.check_fn_decl(&bfty.decl, SelfSemantic::No); Self::check_decl_no_pat(&bfty.decl, |span, _, _| { - self.dcx().emit_err(errors::PatternFnPointer { span }); + self.dcx().emit_err(diagnostics::PatternFnPointer { span }); }); if let Extern::Implicit(extern_span) = bfty.ext { self.handle_missing_abi(extern_span, ty.id); @@ -1005,8 +1014,9 @@ impl<'a> AstValidator<'a> { for bound in bounds { if let GenericBound::Outlives(lifetime) = bound { if any_lifetime_bounds { - self.dcx() - .emit_err(errors::TraitObjectBound { span: lifetime.ident.span }); + self.dcx().emit_err(diagnostics::TraitObjectBound { + span: lifetime.ident.span, + }); break; } any_lifetime_bounds = true; @@ -1015,7 +1025,7 @@ impl<'a> AstValidator<'a> { } TyKind::ImplTrait(_, bounds) => { if let Some(outer_impl_trait_sp) = self.outer_impl_trait_span { - self.dcx().emit_err(errors::NestedImplTrait { + self.dcx().emit_err(diagnostics::NestedImplTrait { span: ty.span, outer: outer_impl_trait_sp, inner: ty.span, @@ -1023,7 +1033,7 @@ impl<'a> AstValidator<'a> { } if !bounds.iter().any(|b| matches!(b, GenericBound::Trait(..))) { - self.dcx().emit_err(errors::AtLeastOneTrait { span: ty.span }); + self.dcx().emit_err(diagnostics::AtLeastOneTrait { span: ty.span }); } } _ => {} @@ -1034,7 +1044,7 @@ impl<'a> AstValidator<'a> { // FIXME(davidtwco): This is a hack to detect macros which produce spans of the // call site which do not have a macro backtrace. See #61963. if span.edition().at_least_edition_future() && self.features.explicit_extern_abis() { - self.dcx().emit_err(errors::MissingAbi { span }); + self.dcx().emit_err(diagnostics::MissingAbi { span }); } else if self .sess .source_map() @@ -1045,7 +1055,7 @@ impl<'a> AstValidator<'a> { MISSING_ABI, id, span, - errors::MissingAbiSugg { span, default_abi: ExternAbi::FALLBACK }, + diagnostics::MissingAbiSugg { span, default_abi: ExternAbi::FALLBACK }, ) } } @@ -1126,7 +1136,7 @@ fn validate_generic_param_order(dcx: DiagCtxtHandle<'_>, generics: &[GenericPara ordered_params += ">"; for (param_ord, (max_param, spans)) in &out_of_order { - dcx.emit_err(errors::OutOfOrderParams { + dcx.emit_err(diagnostics::OutOfOrderParams { spans: spans.clone(), sugg_span: span, param_ord: param_ord.to_string(), @@ -1171,15 +1181,15 @@ impl Visitor<'_> for AstValidator<'_> { self.visit_attrs_vis(&item.attrs, &item.vis); self.visibility_not_permitted( &item.vis, - errors::VisibilityNotPermittedNote::TraitImpl, + diagnostics::VisibilityNotPermittedNote::TraitImpl, ); if let TyKind::Dummy = self_ty.kind { // Abort immediately otherwise the `TyKind::Dummy` will reach HIR lowering, // which isn't allowed. Not a problem for this obscure, obsolete syntax. - self.dcx().emit_fatal(errors::ObsoleteAuto { span: item.span }); + self.dcx().emit_fatal(diagnostics::ObsoleteAuto { span: item.span }); } if let (&Safety::Unsafe(span), &ImplPolarity::Negative(sp)) = (safety, polarity) { - self.dcx().emit_err(errors::UnsafeNegativeImpl { + self.dcx().emit_err(diagnostics::UnsafeNegativeImpl { span: sp.to(t.path.span), negative: sp, r#unsafe: span, @@ -1212,7 +1222,7 @@ impl Visitor<'_> for AstValidator<'_> { self.visit_attrs_vis(&item.attrs, &item.vis); self.visibility_not_permitted( &item.vis, - errors::VisibilityNotPermittedNote::IndividualImplItems, + diagnostics::VisibilityNotPermittedNote::IndividualImplItems, ); let disallowed = matches!(constness, ast::Const::No) @@ -1254,19 +1264,19 @@ impl Visitor<'_> for AstValidator<'_> { let is_intrinsic = item.attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic)); if body.is_none() && !is_intrinsic && !self.is_sdylib_interface { - self.dcx().emit_err(errors::FnWithoutBody { + self.dcx().emit_err(diagnostics::FnWithoutBody { span: item.span, replace_span: self.ending_semi_or_hi(item.span), extern_block_suggestion: match sig.header.ext { Extern::None => None, Extern::Implicit(start_span) => { - Some(errors::ExternBlockSuggestion::Implicit { + Some(diagnostics::ExternBlockSuggestion::Implicit { start_span, end_span: item.span.shrink_to_hi(), }) } Extern::Explicit(abi, start_span) => { - Some(errors::ExternBlockSuggestion::Explicit { + Some(diagnostics::ExternBlockSuggestion::Explicit { start_span, end_span: item.span.shrink_to_hi(), abi: abi.symbol_unescaped, @@ -1283,18 +1293,18 @@ impl Visitor<'_> for AstValidator<'_> { let old_item = mem::replace(&mut self.extern_mod_span, Some(item.span)); self.visibility_not_permitted( &item.vis, - errors::VisibilityNotPermittedNote::IndividualForeignItems, + diagnostics::VisibilityNotPermittedNote::IndividualForeignItems, ); if &Safety::Default == safety { if item.span.at_least_rust_2024() { - self.dcx().emit_err(errors::MissingUnsafeOnExtern { span: item.span }); + self.dcx().emit_err(diagnostics::MissingUnsafeOnExtern { span: item.span }); } else { self.lint_buffer.buffer_lint( MISSING_UNSAFE_ON_EXTERN, item.id, item.span, - errors::MissingUnsafeOnExternLint { + diagnostics::MissingUnsafeOnExternLint { suggestion: item.span.shrink_to_lo(), }, ); @@ -1315,12 +1325,12 @@ impl Visitor<'_> for AstValidator<'_> { for variant in &def.variants { self.visibility_not_permitted( &variant.vis, - errors::VisibilityNotPermittedNote::EnumVariant, + diagnostics::VisibilityNotPermittedNote::EnumVariant, ); for field in variant.data.fields() { self.visibility_not_permitted( &field.vis, - errors::VisibilityNotPermittedNote::EnumVariant, + diagnostics::VisibilityNotPermittedNote::EnumVariant, ); } } @@ -1364,7 +1374,7 @@ impl Visitor<'_> for AstValidator<'_> { } ItemKind::Mod(safety, ident, mod_kind) => { if let &Safety::Unsafe(span) = safety { - self.dcx().emit_err(errors::UnsafeItem { span, kind: "module" }); + self.dcx().emit_err(diagnostics::UnsafeItem { span, kind: "module" }); } // Ensure that `path` attributes on modules are recorded as used (cf. issue #35584). if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _)) @@ -1381,13 +1391,15 @@ impl Visitor<'_> for AstValidator<'_> { item.attrs.iter().find(|attr| attr.has_name(sym::rustc_scalable_vector)); if let Some(attr) = scalable_vector_attr { if !matches!(vdata, VariantData::Tuple(..)) { - this.dcx() - .emit_err(errors::ScalableVectorNotTupleStruct { span: item.span }); + this.dcx().emit_err(diagnostics::ScalableVectorNotTupleStruct { + span: item.span, + }); } if !self.sess.target.arch.supports_scalable_vectors() && !self.sess.opts.actually_rustdoc { - this.dcx().emit_err(errors::ScalableVectorBadArch { span: attr.span }); + this.dcx() + .emit_err(diagnostics::ScalableVectorBadArch { span: attr.span }); } } @@ -1403,7 +1415,7 @@ impl Visitor<'_> for AstValidator<'_> { } ItemKind::Union(ident, generics, vdata) => { if vdata.fields().is_empty() { - self.dcx().emit_err(errors::FieldlessUnion { span: item.span }); + self.dcx().emit_err(diagnostics::FieldlessUnion { span: item.span }); } self.with_tilde_const(Some(TildeConstReason::Union { span: item.span }), |this| { match vdata { @@ -1419,7 +1431,7 @@ impl Visitor<'_> for AstValidator<'_> { ItemKind::Const(ConstItem { defaultness, ident, rhs_kind, .. }) => { self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No); if !rhs_kind.has_expr() { - self.dcx().emit_err(errors::ConstWithoutBody { + self.dcx().emit_err(diagnostics::ConstWithoutBody { span: item.span, replace_span: self.ending_semi_or_hi(item.span), }); @@ -1432,7 +1444,7 @@ impl Visitor<'_> for AstValidator<'_> { UNUSED_VISIBILITIES, item.id, item.vis.span, - errors::UnusedVisibility { span: item.vis.span }, + diagnostics::UnusedVisibility { span: item.vis.span }, ) } @@ -1441,11 +1453,11 @@ impl Visitor<'_> for AstValidator<'_> { ItemKind::Static(StaticItem { expr, safety, .. }) => { self.check_item_safety(item.span, *safety); if matches!(safety, Safety::Unsafe(_)) { - self.dcx().emit_err(errors::UnsafeStatic { span: item.span }); + self.dcx().emit_err(diagnostics::UnsafeStatic { span: item.span }); } if expr.is_none() { - self.dcx().emit_err(errors::StaticWithoutBody { + self.dcx().emit_err(diagnostics::StaticWithoutBody { span: item.span, replace_span: self.ending_semi_or_hi(item.span), }); @@ -1457,7 +1469,7 @@ impl Visitor<'_> for AstValidator<'_> { ) => { self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No); if ty.is_none() { - self.dcx().emit_err(errors::TyAliasWithoutBody { + self.dcx().emit_err(diagnostics::TyAliasWithoutBody { span: item.span, replace_span: self.ending_semi_or_hi(item.span), }); @@ -1469,7 +1481,7 @@ impl Visitor<'_> for AstValidator<'_> { self.dcx().emit_err(err); } } else if after_where_clause.has_where_token { - self.dcx().emit_err(errors::WhereClauseAfterTypeAlias { + self.dcx().emit_err(diagnostics::WhereClauseAfterTypeAlias { span: after_where_clause.span, help: self.sess.is_nightly_build(), }); @@ -1499,7 +1511,7 @@ impl Visitor<'_> for AstValidator<'_> { if let Some(attr) = attr::find_by_name(fi.attrs(), sym::track_caller) && self.extern_mod_abi != Some(ExternAbi::Rust) { - self.dcx().emit_err(errors::RequiresRustAbi { + self.dcx().emit_err(diagnostics::RequiresRustAbi { track_caller_span: attr.span, extern_abi_span: self.current_extern_span(), }); @@ -1573,7 +1585,7 @@ impl Visitor<'_> for AstValidator<'_> { } GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { if let Some(span) = prev_param_default { - self.dcx().emit_err(errors::GenericDefaultTrailing { span }); + self.dcx().emit_err(diagnostics::GenericDefaultTrailing { span }); break; } } @@ -1602,8 +1614,9 @@ impl Visitor<'_> for AstValidator<'_> { match bound { GenericBound::Trait(t) => { if !t.bound_generic_params.is_empty() { - self.dcx() - .emit_err(errors::NestedLifetimes { span: t.span }); + self.dcx().emit_err(diagnostics::NestedLifetimes { + span: t.span, + }); } } GenericBound::Outlives(_) => {} @@ -1627,12 +1640,13 @@ impl Visitor<'_> for AstValidator<'_> { BoundConstness::Always(_), BoundPolarity::Positive, ) => { - self.dcx().emit_err(errors::ConstBoundTraitObject { span: trait_ref.span }); + self.dcx() + .emit_err(diagnostics::ConstBoundTraitObject { span: trait_ref.span }); } (_, BoundConstness::Maybe(span), BoundPolarity::Positive) if let Some(reason) = self.disallow_tilde_const => { - self.dcx().emit_err(errors::TildeConstDisallowed { span, reason }); + self.dcx().emit_err(diagnostics::TildeConstDisallowed { span, reason }); } _ => {} } @@ -1645,7 +1659,7 @@ impl Visitor<'_> for AstValidator<'_> { Some(ast::GenericArgs::AngleBracketed(args)) => { for arg in &args.args { if let ast::AngleBracketedArg::Constraint(constraint) = arg { - self.dcx().emit_err(errors::ConstraintOnNegativeBound { + self.dcx().emit_err(diagnostics::ConstraintOnNegativeBound { span: constraint.span, }); } @@ -1653,9 +1667,11 @@ impl Visitor<'_> for AstValidator<'_> { } // The lowered form of parenthesized generic args contains an associated type binding. Some(ast::GenericArgs::Parenthesized(args)) => { - self.dcx().emit_err(errors::NegativeBoundWithParentheticalNotation { - span: args.span, - }); + self.dcx().emit_err( + diagnostics::NegativeBoundWithParentheticalNotation { + span: args.span, + }, + ); } Some(ast::GenericArgs::ParenthesizedElided(_)) | None => {} } @@ -1665,7 +1681,7 @@ impl Visitor<'_> for AstValidator<'_> { GenericBound::Use(_, span) => match ctxt { BoundKind::Impl => {} BoundKind::Bound | BoundKind::TraitObject | BoundKind::SuperTraits => { - self.dcx().emit_err(errors::PreciseCapturingNotAllowedHere { + self.dcx().emit_err(diagnostics::PreciseCapturingNotAllowedHere { loc: ctxt.descr(), span: *span, }); @@ -1705,7 +1721,7 @@ impl Visitor<'_> for AstValidator<'_> { if let Some(attr) = attr::find_by_name(attrs, sym::track_caller) && extern_abi != ExternAbi::Rust { - self.dcx().emit_err(errors::RequiresRustAbi { + self.dcx().emit_err(diagnostics::RequiresRustAbi { track_caller_span: attr.span, extern_abi_span, }); @@ -1722,7 +1738,7 @@ impl Visitor<'_> for AstValidator<'_> { .. }) = fk.header() { - self.dcx().emit_err(errors::ConstAndCoroutine { + self.dcx().emit_err(diagnostics::ConstAndCoroutine { spans: vec![coroutine_kind.span(), const_span], const_span, coroutine_span: coroutine_kind.span(), @@ -1754,11 +1770,11 @@ impl Visitor<'_> for AstValidator<'_> { id, span, move |dcx, level| { - let sub = errors::PatternsInFnsWithoutBodySub { ident, span }; + let sub = diagnostics::PatternsInFnsWithoutBodySub { ident, span }; if is_foreign { - errors::PatternsInFnsWithoutBody::Foreign { sub } + diagnostics::PatternsInFnsWithoutBody::Foreign { sub } } else { - errors::PatternsInFnsWithoutBody::Bodiless { sub } + diagnostics::PatternsInFnsWithoutBody::Bodiless { sub } } .into_diag(dcx, level) }, @@ -1766,8 +1782,10 @@ impl Visitor<'_> for AstValidator<'_> { } } else { match ctxt { - FnCtxt::Foreign => self.dcx().emit_err(errors::PatternInForeign { span }), - _ => self.dcx().emit_err(errors::PatternInBodiless { span }), + FnCtxt::Foreign => { + self.dcx().emit_err(diagnostics::PatternInForeign { span }) + } + _ => self.dcx().emit_err(diagnostics::PatternInBodiless { span }), }; } }); @@ -1814,7 +1832,7 @@ impl Visitor<'_> for AstValidator<'_> { match &item.kind { AssocItemKind::Const(ConstItem { rhs_kind, .. }) => { if !rhs_kind.has_expr() { - self.dcx().emit_err(errors::AssocConstWithoutBody { + self.dcx().emit_err(diagnostics::AssocConstWithoutBody { span: item.span, replace_span: self.ending_semi_or_hi(item.span), }); @@ -1822,7 +1840,7 @@ impl Visitor<'_> for AstValidator<'_> { } AssocItemKind::Fn(Fn { body, .. }) => { if body.is_none() && !self.is_sdylib_interface { - self.dcx().emit_err(errors::AssocFnWithoutBody { + self.dcx().emit_err(diagnostics::AssocFnWithoutBody { span: item.span, replace_span: self.ending_semi_or_hi(item.span), }); @@ -1830,7 +1848,7 @@ impl Visitor<'_> for AstValidator<'_> { } AssocItemKind::Type(TyAlias { bounds, ty, .. }) => { if ty.is_none() { - self.dcx().emit_err(errors::AssocTypeWithoutBody { + self.dcx().emit_err(diagnostics::AssocTypeWithoutBody { span: item.span, replace_span: self.ending_semi_or_hi(item.span), }); @@ -1845,8 +1863,8 @@ impl Visitor<'_> for AstValidator<'_> { && let Err(err) = self.check_type_alias_where_clause_location(ty_alias) { let sugg = match err.sugg { - errors::WhereClauseBeforeTypeAliasSugg::Remove { .. } => None, - errors::WhereClauseBeforeTypeAliasSugg::Move { snippet, right, .. } => { + diagnostics::WhereClauseBeforeTypeAliasSugg::Remove { .. } => None, + diagnostics::WhereClauseBeforeTypeAliasSugg::Move { snippet, right, .. } => { Some((right, snippet)) } }; @@ -1862,17 +1880,17 @@ impl Visitor<'_> for AstValidator<'_> { move |dcx, level| { let suggestion = match sugg { Some((right_sp, sugg)) => { - errors::DeprecatedWhereClauseLocationSugg::MoveToEnd { + diagnostics::DeprecatedWhereClauseLocationSugg::MoveToEnd { left: left_sp, right: right_sp, sugg, } } - None => errors::DeprecatedWhereClauseLocationSugg::RemoveWhere { + None => diagnostics::DeprecatedWhereClauseLocationSugg::RemoveWhere { span: err.span, }, }; - errors::DeprecatedWhereClauseLocation { suggestion }.into_diag(dcx, level) + diagnostics::DeprecatedWhereClauseLocation { suggestion }.into_diag(dcx, level) }, ); } @@ -1881,7 +1899,7 @@ impl Visitor<'_> for AstValidator<'_> { Some(parent @ (TraitOrImpl::Trait { .. } | TraitOrImpl::TraitImpl { .. })) => { self.visibility_not_permitted( &item.vis, - errors::VisibilityNotPermittedNote::TraitImpl, + diagnostics::VisibilityNotPermittedNote::TraitImpl, ); if let AssocItemKind::Fn(Fn { sig, .. }) = &item.kind { self.check_trait_fn_not_const(sig.header.constness, parent); @@ -1952,7 +1970,7 @@ fn deny_equality_constraints( predicate_span: Span, generics: &Generics, ) { - let mut err = errors::EqualityInWhere { span: predicate_span, assoc: None, assoc2: None }; + let mut err = diagnostics::EqualityInWhere { span: predicate_span, assoc: None, assoc2: None }; // Given `::Bar = RhsTy`, suggest `A: Foo`. if let TyKind::Path(Some(qself), full_path) = &predicate.lhs_ty.kind @@ -1995,7 +2013,7 @@ fn deny_equality_constraints( ); } } - err.assoc = Some(errors::AssociatedSuggestion { + err.assoc = Some(diagnostics::AssociatedSuggestion { span: predicate_span, ident: *ident, param: param.ident, @@ -2046,7 +2064,7 @@ fn deny_equality_constraints( } span }; - err.assoc2 = Some(errors::AssociatedSuggestion2 { + err.assoc2 = Some(diagnostics::AssociatedSuggestion2 { span, args, predicate: removal_span, diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/diagnostics.rs similarity index 100% rename from compiler/rustc_ast_passes/src/errors.rs rename to compiler/rustc_ast_passes/src/diagnostics.rs diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index fd0070bd9c529..09672d1e69f11 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -10,7 +10,7 @@ use rustc_session::errors::{feature_err, feature_warn}; use rustc_span::{Span, Spanned, Symbol, sym}; use thin_vec::ThinVec; -use crate::errors; +use crate::diagnostics; /// The common case. macro_rules! gate { @@ -133,7 +133,7 @@ impl<'a> PostExpansionVisitor<'a> { .collect(); if !const_param_spans.is_empty() { - self.sess.dcx().emit_err(errors::ForbiddenConstParam { const_param_spans }); + self.sess.dcx().emit_err(diagnostics::ForbiddenConstParam { const_param_spans }); } } @@ -144,9 +144,9 @@ impl<'a> PostExpansionVisitor<'a> { // Issue #149695 // Abort immediately otherwise items defined in complex bounds will be lowered into HIR, // which will cause ICEs when errors of the items visit unlowered parents. - self.sess.dcx().emit_fatal(errors::ForbiddenBound { spans }); + self.sess.dcx().emit_fatal(diagnostics::ForbiddenBound { spans }); } else { - self.sess.dcx().emit_err(errors::ForbiddenBound { spans }); + self.sess.dcx().emit_err(diagnostics::ForbiddenBound { spans }); } } } @@ -553,7 +553,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { // Under no circumstances do we want to advertise the feature name to users! if !visitor.features.negative_bounds() { for &span in spans.get(&sym::negative_bounds).into_iter().flatten() { - sess.dcx().emit_err(errors::NegativeBoundUnsupported { span }); + sess.dcx().emit_err(diagnostics::NegativeBoundUnsupported { span }); } } @@ -570,7 +570,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { .emit(); } else { let suggestion = span.shrink_to_hi(); - sess.dcx().emit_err(errors::MatchArmWithNoBody { span, suggestion }); + sess.dcx().emit_err(diagnostics::MatchArmWithNoBody { span, suggestion }); } } } @@ -645,7 +645,7 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) AttributeParser::parse_limited(sess, &krate.attrs, &[sym::feature]) { // `feature(...)` used on non-nightly. This is definitely an error. - let mut err = errors::FeatureOnNonNightly { + let mut err = diagnostics::FeatureOnNonNightly { span: first_span, channel: option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)"), stable_features: vec![], @@ -662,7 +662,7 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) .map(|feat| feat.stable_since) .flatten(); if let Some(since) = stable_since { - err.stable_features.push(errors::StableFeature { name, since }); + err.stable_features.push(diagnostics::StableFeature { name, since }); } else { all_stable = false; } @@ -688,7 +688,11 @@ fn check_incompatible_features(sess: &Session, features: &Features) { && let Some((f2_name, f2_span)) = enabled_features.clone().find(|(name, _)| name == f2) { let spans = vec![f1_span, f2_span]; - sess.dcx().emit_err(errors::IncompatibleFeatures { spans, f1: f1_name, f2: f2_name }); + sess.dcx().emit_err(diagnostics::IncompatibleFeatures { + spans, + f1: f1_name, + f2: f2_name, + }); } } } @@ -709,7 +713,11 @@ fn check_dependent_features(sess: &Session, features: &Features) { .map(|s| format!("`{}`", s.as_str())) .intersperse(String::from(", ")) .collect(); - sess.dcx().emit_err(errors::MissingDependentFeatures { parent_span, parent, missing }); + sess.dcx().emit_err(diagnostics::MissingDependentFeatures { + parent_span, + parent, + missing, + }); } } } @@ -727,7 +735,7 @@ fn check_new_solver_banned_features(sess: &Session, features: &Features) { .map(|feat| feat.attr_sp) { #[allow(rustc::symbol_intern_string_literal)] - sess.dcx().emit_err(errors::IncompatibleFeatures { + sess.dcx().emit_err(diagnostics::IncompatibleFeatures { spans: vec![gce_span], f1: Symbol::intern("-Znext-solver=globally"), f2: sym::generic_const_exprs, @@ -749,7 +757,7 @@ fn check_features_requiring_new_solver(sess: &Session, features: &Features) { .map(|feat| feat.attr_sp) { #[allow(rustc::symbol_intern_string_literal)] - sess.dcx().emit_err(errors::MissingDependentFeatures { + sess.dcx().emit_err(diagnostics::MissingDependentFeatures { parent_span: gca_span, parent: sym::generic_const_args, missing: String::from("-Znext-solver=globally"), diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index 8bf51dd10a844..86ad758807584 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -9,5 +9,5 @@ // tidy-alphabetical-end pub mod ast_validation; -mod errors; +mod diagnostics; pub mod feature_gate; diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index cf5a722f0529e..27960e96bc111 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -8,7 +8,7 @@ use rustc_ast::token::DocFragmentKind; use rustc_ast::{AttrItemKind, AttrStyle, CRATE_NODE_ID, NodeId, Safety}; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan}; -use rustc_feature::{AttributeTemplate, Features}; +use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, Features}; use rustc_hir::attrs::AttributeKind; use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Target}; use rustc_lint_defs::RegisteredTools; @@ -355,6 +355,9 @@ impl<'sess> AttributeParser<'sess> { &mut emit_lint, ); self.check_attribute_stability(&attr_path, attr_span, accept.stability); + if let [part] = parts.as_slice() { + debug_assert!(BUILTIN_ATTRIBUTE_MAP.contains(&part)); + } let Some(args) = ArgParser::from_attr_args( args, diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index bca2de041b657..4d93fa08fe0bd 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -1,7 +1,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::SubregionOrigin; -use rustc_infer::infer::canonical::QueryRegionConstraints; +use rustc_infer::infer::canonical::{QueryRegionConstraint, QueryRegionConstraints}; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound}; @@ -74,9 +74,9 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { let assumptions = elaborate::elaborate_outlives_assumptions(self.infcx.tcx, assumptions.iter().copied()); - for &(constraint, constraint_category, _) in constraints { + for &QueryRegionConstraint { constraint, category, .. } in constraints { constraint.iter_outlives().for_each(|predicate| { - self.convert(predicate, constraint_category, &assumptions); + self.convert(predicate, category, &assumptions); }); } } @@ -296,7 +296,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { // FIXME(higher_ranked_auto): What should we do with the assumptions here? if let Some(QueryRegionConstraints { constraints, assumptions: _ }) = constraints { next_outlives_predicates.extend(constraints.iter().flat_map( - |(constraint, category, _)| { + |QueryRegionConstraint { constraint, category, .. }| { constraint.iter_outlives().map(|outlives| (outlives, *category)) }, )); diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs index 5f78758513e17..544ad86120356 100644 --- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs +++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs @@ -6,7 +6,7 @@ use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::{Ident, Span, kw, sym}; use thin_vec::{ThinVec, thin_vec}; -use crate::errors; +use crate::diagnostics; use crate::util::check_builtin_macro_attribute; pub(crate) fn expand( @@ -31,7 +31,7 @@ pub(crate) fn expand( { (item, fn_kind.ident, true, ecx.with_def_site_ctxt(fn_kind.sig.span)) } else { - ecx.dcx().emit_err(errors::AllocErrorMustBeFn { span: item.span() }); + ecx.dcx().emit_err(diagnostics::AllocErrorMustBeFn { span: item.span() }); return vec![orig_item]; }; diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index a1e14b5245137..b67b1fdb42048 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -12,7 +12,7 @@ use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, sym}; use rustc_target::asm::InlineAsmArch; use smallvec::smallvec; -use crate::errors; +use crate::diagnostics; use crate::util::{ExprToSpannedString, expr_to_spanned_string}; /// Validated assembly arguments, ready for macro expansion. @@ -65,7 +65,7 @@ fn validate_asm_args<'a>( for arg in args { for attr in arg.attributes.0.iter() { if !matches!(attr.name(), Some(sym::cfg | sym::cfg_attr)) { - ecx.dcx().emit_err(errors::AsmAttributeNotSupported { span: attr.span() }); + ecx.dcx().emit_err(diagnostics::AsmAttributeNotSupported { span: attr.span() }); } } @@ -86,7 +86,7 @@ fn validate_asm_args<'a>( ) => {} ast::ExprKind::MacCall(..) => {} _ => { - let err = dcx.create_err(errors::AsmExpectedOther { + let err = dcx.create_err(diagnostics::AsmExpectedOther { span: template.span, is_inline_asm: matches!(asm_macro, AsmMacro::Asm), }); @@ -111,12 +111,12 @@ fn validate_asm_args<'a>( if explicit_reg { if name.is_some() { - dcx.emit_err(errors::AsmExplicitRegisterName { span }); + dcx.emit_err(diagnostics::AsmExplicitRegisterName { span }); } validated.reg_args.insert(slot); } else if let Some(name) = name { if let Some(&prev) = validated.named_args.get(&name) { - dcx.emit_err(errors::AsmDuplicateArg { + dcx.emit_err(diagnostics::AsmDuplicateArg { span, name, prev: validated.operands[prev].1, @@ -130,7 +130,7 @@ fn validate_asm_args<'a>( let explicit = validated.reg_args.iter().map(|p| validated.operands[p].1).collect(); - dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit }); + dcx.emit_err(diagnostics::AsmPositionalAfter { span, named, explicit }); } } AsmArgKind::Options(new_options) => { @@ -141,7 +141,7 @@ fn validate_asm_args<'a>( if !asm_macro.is_supported_option(options) { // Tool-only output. - dcx.emit_err(errors::AsmUnsupportedOption { + dcx.emit_err(diagnostics::AsmUnsupportedOption { span, symbol, span_with_comma, @@ -149,7 +149,7 @@ fn validate_asm_args<'a>( }); } else if validated.options.contains(options) { // Tool-only output. - dcx.emit_err(errors::AsmOptAlreadyprovided { + dcx.emit_err(diagnostics::AsmOptAlreadyprovided { span, symbol, span_with_comma, @@ -178,13 +178,13 @@ fn validate_asm_args<'a>( && validated.options.contains(ast::InlineAsmOptions::READONLY) { let spans = validated.options_spans.clone(); - dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "nomem", opt2: "readonly" }); + dcx.emit_err(diagnostics::AsmMutuallyExclusive { spans, opt1: "nomem", opt2: "readonly" }); } if validated.options.contains(ast::InlineAsmOptions::PURE) && validated.options.contains(ast::InlineAsmOptions::NORETURN) { let spans = validated.options_spans.clone(); - dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "pure", opt2: "noreturn" }); + dcx.emit_err(diagnostics::AsmMutuallyExclusive { spans, opt1: "pure", opt2: "noreturn" }); } if validated.options.contains(ast::InlineAsmOptions::PURE) && !validated @@ -192,7 +192,7 @@ fn validate_asm_args<'a>( .intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY) { let spans = validated.options_spans.clone(); - dcx.emit_err(errors::AsmPureCombine { spans }); + dcx.emit_err(diagnostics::AsmPureCombine { spans }); } let mut have_real_output = false; @@ -223,24 +223,24 @@ fn validate_asm_args<'a>( } } if validated.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output { - dcx.emit_err(errors::AsmPureNoOutput { spans: validated.options_spans.clone() }); + dcx.emit_err(diagnostics::AsmPureNoOutput { spans: validated.options_spans.clone() }); } if validated.options.contains(ast::InlineAsmOptions::NORETURN) && !outputs_sp.is_empty() && labels_sp.is_empty() { - let err = dcx.create_err(errors::AsmNoReturn { outputs_sp }); + let err = dcx.create_err(diagnostics::AsmNoReturn { outputs_sp }); // Bail out now since this is likely to confuse MIR return Err(err); } if validated.options.contains(ast::InlineAsmOptions::MAY_UNWIND) && !labels_sp.is_empty() { - dcx.emit_err(errors::AsmMayUnwind { labels_sp }); + dcx.emit_err(diagnostics::AsmMayUnwind { labels_sp }); } if !validated.clobber_abis.is_empty() { match asm_macro { AsmMacro::GlobalAsm | AsmMacro::NakedAsm => { - let err = dcx.create_err(errors::AsmUnsupportedClobberAbi { + let err = dcx.create_err(diagnostics::AsmUnsupportedClobberAbi { spans: validated.clobber_abis.iter().map(|(_, span)| *span).collect(), macro_name: asm_macro.macro_name(), }); @@ -250,7 +250,7 @@ fn validate_asm_args<'a>( } AsmMacro::Asm => { if !regclass_outputs.is_empty() { - dcx.emit_err(errors::AsmClobberNoReg { + dcx.emit_err(diagnostics::AsmClobberNoReg { spans: regclass_outputs, clobbers: validated.clobber_abis.iter().map(|(_, span)| *span).collect(), }); @@ -354,7 +354,7 @@ fn expand_preparsed_asm( lint::builtin::BAD_ASM_STYLE, find_span(".intel_syntax"), ecx.current_expansion.lint_node_id, - errors::AvoidIntelSyntax, + diagnostics::AvoidIntelSyntax, ); } if template_str.contains(".att_syntax") { @@ -362,7 +362,7 @@ fn expand_preparsed_asm( lint::builtin::BAD_ASM_STYLE, find_span(".att_syntax"), ecx.current_expansion.lint_node_id, - errors::AvoidAttSyntax, + diagnostics::AvoidAttSyntax, ); } } @@ -482,7 +482,7 @@ fn expand_preparsed_asm( None => { let span = arg.position_span; ecx.dcx() - .create_err(errors::AsmNoMatchedArgumentName { + .create_err(diagnostics::AsmNoMatchedArgumentName { name: name.to_owned(), span: span_in_template(span), }) @@ -497,7 +497,7 @@ fn expand_preparsed_asm( let mut modifier = chars.next(); if chars.next().is_some() { let span = arg.format.ty_span.map(span_in_template).unwrap_or(template_sp); - ecx.dcx().emit_err(errors::AsmModifierInvalid { span }); + ecx.dcx().emit_err(diagnostics::AsmModifierInvalid { span }); modifier = None; } diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs index 855da5caa312c..444510ee50572 100644 --- a/compiler/rustc_builtin_macros/src/assert.rs +++ b/compiler/rustc_builtin_macros/src/assert.rs @@ -11,8 +11,8 @@ use rustc_parse::parser::Parser; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; use thin_vec::thin_vec; +use crate::diagnostics; use crate::edition_panic::use_panic_2021; -use crate::errors; pub(crate) fn expand_assert<'cx>( cx: &'cx mut ExtCtxt<'_>, @@ -114,7 +114,7 @@ fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult< let mut parser = cx.new_parser_from_tts(stream); if parser.token == token::Eof { - return Err(cx.dcx().create_err(errors::AssertRequiresBoolean { span: sp })); + return Err(cx.dcx().create_err(diagnostics::AssertRequiresBoolean { span: sp })); } let cond_expr = parser.parse_expr()?; @@ -127,7 +127,8 @@ fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult< // // Emit an error about semicolon and suggest removing it. if parser.token == token::Semi { - cx.dcx().emit_err(errors::AssertRequiresExpression { span: sp, token: parser.token.span }); + cx.dcx() + .emit_err(diagnostics::AssertRequiresExpression { span: sp, token: parser.token.span }); parser.bump(); } @@ -140,7 +141,7 @@ fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult< let custom_message = if let token::Literal(token::Lit { kind: token::Str, .. }) = parser.token.kind { let comma = parser.prev_token.span.shrink_to_hi(); - cx.dcx().emit_err(errors::AssertMissingComma { span: parser.token.span, comma }); + cx.dcx().emit_err(diagnostics::AssertMissingComma { span: parser.token.span, comma }); parse_custom_message(&mut parser) } else if parser.eat(exp!(Comma)) { diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 76c43e0df1a24..595a8b1fecb87 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -24,7 +24,7 @@ mod llvm_enzyme { use thin_vec::{ThinVec, thin_vec}; use tracing::{debug, trace}; - use crate::errors; + use crate::diagnostics; pub(crate) fn outer_normal_attr( kind: &Box, @@ -101,7 +101,7 @@ mod llvm_enzyme { match x.try_into() { Ok(x) => x, Err(_) => { - dcx.emit_err(errors::AutoDiffInvalidWidth { + dcx.emit_err(diagnostics::AutoDiffInvalidWidth { span: meta_item[1].span(), width: x, }); @@ -120,7 +120,7 @@ mod llvm_enzyme { match res { Ok(x) => activities.push(x), Err(_) => { - dcx.emit_err(errors::AutoDiffUnknownActivity { + dcx.emit_err(diagnostics::AutoDiffUnknownActivity { span: x.span(), act: activity_str, }); @@ -238,14 +238,14 @@ mod llvm_enzyme { } _ => None, }) else { - dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() }); + dcx.emit_err(diagnostics::AutoDiffInvalidApplication { span: item.span() }); return vec![item]; }; let meta_item_vec: ThinVec = match meta_item.kind { ast::MetaItemKind::List(ref vec) => vec.clone(), _ => { - dcx.emit_err(errors::AutoDiffMissingConfig { span: item.span() }); + dcx.emit_err(diagnostics::AutoDiffMissingConfig { span: item.span() }); return vec![item]; } }; @@ -257,7 +257,7 @@ mod llvm_enzyme { let mut ts: Vec = vec![]; if meta_item_vec.len() < 1 { // At the bare minimum, we need a fnc name. - dcx.emit_err(errors::AutoDiffMissingConfig { span: item.span() }); + dcx.emit_err(diagnostics::AutoDiffMissingConfig { span: item.span() }); return vec![item]; } @@ -658,7 +658,7 @@ mod llvm_enzyme { let sig_args = sig.decl.inputs.len() + if has_ret { 1 } else { 0 }; let num_activities = x.input_activity.len() + if x.has_ret_activity() { 1 } else { 0 }; if sig_args != num_activities { - dcx.emit_err(errors::AutoDiffInvalidNumberActivities { + dcx.emit_err(diagnostics::AutoDiffInvalidNumberActivities { span, expected: sig_args, found: num_activities, @@ -679,7 +679,7 @@ mod llvm_enzyme { let mut errors = false; for (arg, activity) in sig.decl.inputs.iter().zip(x.input_activity.iter()) { if !valid_input_activity(x.mode, *activity) { - dcx.emit_err(errors::AutoDiffInvalidApplicationModeAct { + dcx.emit_err(diagnostics::AutoDiffInvalidApplicationModeAct { span, mode: x.mode.to_string(), act: activity.to_string(), @@ -687,7 +687,7 @@ mod llvm_enzyme { errors = true; } if !valid_ty_for_activity(&arg.ty, *activity) { - dcx.emit_err(errors::AutoDiffInvalidTypeForActivity { + dcx.emit_err(diagnostics::AutoDiffInvalidTypeForActivity { span: arg.ty.span, act: activity.to_string(), }); @@ -696,7 +696,7 @@ mod llvm_enzyme { } if has_ret && !valid_ret_activity(x.mode, x.ret_activity) { - dcx.emit_err(errors::AutoDiffInvalidRetAct { + dcx.emit_err(diagnostics::AutoDiffInvalidRetAct { span, mode: x.mode.to_string(), act: x.ret_activity.to_string(), diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index 2872cff0fdc7a..f2771f125ff54 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -16,7 +16,7 @@ use rustc_parse::exp; use rustc_parse::parser::Recovery; use rustc_span::{ErrorGuaranteed, Span, sym}; -use crate::errors; +use crate::diagnostics; pub(crate) fn expand_cfg( cx: &mut ExtCtxt<'_>, @@ -38,7 +38,7 @@ pub(crate) fn expand_cfg( fn parse_cfg(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream) -> Result { let mut parser = cx.new_parser_from_tts(tts); if parser.token == token::Eof { - return Err(cx.dcx().emit_err(errors::RequiresCfgPattern { span })); + return Err(cx.dcx().emit_err(diagnostics::RequiresCfgPattern { span })); } let meta = MetaItemOrLitParser::parse_single( @@ -70,7 +70,7 @@ fn parse_cfg(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream) -> Result(ecx: &ExtCtxt<'_>, mi: &'a ast::MetaItem) -> Option<&'a ast::Path> { - use errors::CfgAccessibleInvalid::*; + use diagnostics::CfgAccessibleInvalid::*; match mi.meta_item_list() { None => {} Some([]) => { @@ -62,7 +62,7 @@ impl MultiItemModifier for Expander { Ok(true) => ExpandResult::Ready(vec![item]), Ok(false) => ExpandResult::Ready(Vec::new()), Err(Indeterminate) if ecx.force_mode => { - ecx.dcx().emit_err(errors::CfgAccessibleIndeterminate { span }); + ecx.dcx().emit_err(diagnostics::CfgAccessibleIndeterminate { span }); ExpandResult::Ready(vec![item]) } Err(Indeterminate) => ExpandResult::Retry(item), diff --git a/compiler/rustc_builtin_macros/src/cfg_select.rs b/compiler/rustc_builtin_macros/src/cfg_select.rs index 35098722a910e..526ddd6a3811d 100644 --- a/compiler/rustc_builtin_macros/src/cfg_select.rs +++ b/compiler/rustc_builtin_macros/src/cfg_select.rs @@ -6,7 +6,7 @@ use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExp use rustc_span::{Ident, Span, sym}; use smallvec::SmallVec; -use crate::errors::CfgSelectNoMatches; +use crate::diagnostics::CfgSelectNoMatches; /// This intermediate structure is used to emit parse errors for the branches that are not chosen. /// The `MacResult` instance below parses all branches, emitting any errors it encounters, but only diff --git a/compiler/rustc_builtin_macros/src/concat.rs b/compiler/rustc_builtin_macros/src/concat.rs index c200539e12872..f359b8336dbd5 100644 --- a/compiler/rustc_builtin_macros/src/concat.rs +++ b/compiler/rustc_builtin_macros/src/concat.rs @@ -4,7 +4,7 @@ use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpa use rustc_session::errors::report_lit_error; use rustc_span::Symbol; -use crate::errors; +use crate::diagnostics; use crate::util::get_exprs_from_tts; pub(crate) fn expand_concat( @@ -38,10 +38,10 @@ pub(crate) fn expand_concat( accumulator.push_str(&b.to_string()); } Ok(LitKind::CStr(..)) => { - guar = Some(cx.dcx().emit_err(errors::ConcatCStrLit { span: e.span })); + guar = Some(cx.dcx().emit_err(diagnostics::ConcatCStrLit { span: e.span })); } Ok(LitKind::Byte(..) | LitKind::ByteStr(..)) => { - guar = Some(cx.dcx().emit_err(errors::ConcatBytestr { span: e.span })); + guar = Some(cx.dcx().emit_err(diagnostics::ConcatBytestr { span: e.span })); } Ok(LitKind::Err(guarantee)) => { guar = Some(guarantee); @@ -62,7 +62,7 @@ pub(crate) fn expand_concat( } } ExprKind::IncludedBytes(..) => { - cx.dcx().emit_err(errors::ConcatBytestr { span: e.span }); + cx.dcx().emit_err(diagnostics::ConcatBytestr { span: e.span }); } ExprKind::Err(guarantee) => { guar = Some(guarantee); @@ -75,7 +75,7 @@ pub(crate) fn expand_concat( } ExpandResult::Ready(if !missing_literal.is_empty() { - let guar = cx.dcx().emit_err(errors::ConcatMissingLiteral { spans: missing_literal }); + let guar = cx.dcx().emit_err(diagnostics::ConcatMissingLiteral { spans: missing_literal }); DummyResult::any(sp, guar) } else if let Some(guar) = guar { DummyResult::any(sp, guar) diff --git a/compiler/rustc_builtin_macros/src/concat_bytes.rs b/compiler/rustc_builtin_macros/src/concat_bytes.rs index 8885017b930d9..f86b1daa13d2d 100644 --- a/compiler/rustc_builtin_macros/src/concat_bytes.rs +++ b/compiler/rustc_builtin_macros/src/concat_bytes.rs @@ -4,7 +4,7 @@ use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpa use rustc_session::errors::report_lit_error; use rustc_span::{ErrorGuaranteed, Span}; -use crate::errors; +use crate::diagnostics; use crate::util::get_exprs_from_tts; /// Emits errors for literal expressions that are invalid inside and outside of an array. @@ -14,7 +14,7 @@ fn invalid_type_err( span: Span, is_nested: bool, ) -> ErrorGuaranteed { - use errors::{ + use diagnostics::{ ConcatBytesInvalid, ConcatBytesInvalidSuggestion, ConcatBytesNonU8, ConcatBytesOob, }; let snippet = cx.sess.source_map().span_to_snippet(span).ok(); @@ -105,7 +105,10 @@ fn handle_array_element( Ok(LitKind::Byte(val)) => return Some(val), Ok(LitKind::ByteStr(..)) => { guar.get_or_insert_with(|| { - dcx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: true }) + dcx.emit_err(diagnostics::ConcatBytesArray { + span: expr.span, + bytestr: true, + }) }); } _ => { @@ -115,12 +118,12 @@ fn handle_array_element( } ExprKind::Array(_) | ExprKind::Repeat(_, _) => { guar.get_or_insert_with(|| { - dcx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: false }) + dcx.emit_err(diagnostics::ConcatBytesArray { span: expr.span, bytestr: false }) }); } ExprKind::IncludedBytes(..) => { guar.get_or_insert_with(|| { - dcx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: false }) + dcx.emit_err(diagnostics::ConcatBytesArray { span: expr.span, bytestr: false }) }); } _ => missing_literals.push(expr.span), @@ -167,9 +170,10 @@ pub(crate) fn expand_concat_bytes( } } } else { - guar = Some( - cx.dcx().emit_err(errors::ConcatBytesBadRepeat { span: count.value.span }), - ); + guar = + Some(cx.dcx().emit_err(diagnostics::ConcatBytesBadRepeat { + span: count.value.span, + })); } } &ExprKind::Lit(token_lit) => match LitKind::from_token_lit(token_lit) { @@ -196,7 +200,8 @@ pub(crate) fn expand_concat_bytes( } } ExpandResult::Ready(if !missing_literals.is_empty() { - let guar = cx.dcx().emit_err(errors::ConcatBytesMissingLiteral { spans: missing_literals }); + let guar = + cx.dcx().emit_err(diagnostics::ConcatBytesMissingLiteral { spans: missing_literals }); MacEager::expr(DummyResult::raw_expr(sp, Some(guar))) } else if let Some(guar) = guar { MacEager::expr(DummyResult::raw_expr(sp, Some(guar))) diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index 09d827b0635e6..79c0563b9d183 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -9,7 +9,7 @@ use rustc_session::Session; use rustc_span::{ErrorGuaranteed, Ident, Span, sym}; use crate::cfg_eval::cfg_eval; -use crate::errors; +use crate::diagnostics; pub(crate) struct Expander { pub is_const: bool, @@ -131,7 +131,7 @@ fn report_bad_target( let bad_target = !matches!(item_kind, Some(ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..))); if bad_target { - return Err(sess.dcx().emit_err(errors::BadDeriveTarget { span, item: item.span() })); + return Err(sess.dcx().emit_err(diagnostics::BadDeriveTarget { span, item: item.span() })); } Ok(()) } @@ -141,11 +141,11 @@ fn report_unexpected_meta_item_lit(sess: &Session, lit: &ast::MetaItemLit) { ast::LitKind::Str(_, ast::StrStyle::Cooked) if rustc_lexer::is_ident(lit.symbol.as_str()) => { - errors::BadDeriveLitHelp::StrLit { sym: lit.symbol } + diagnostics::BadDeriveLitHelp::StrLit { sym: lit.symbol } } - _ => errors::BadDeriveLitHelp::Other, + _ => diagnostics::BadDeriveLitHelp::Other, }; - sess.dcx().emit_err(errors::BadDeriveLit { span: lit.span, help }); + sess.dcx().emit_err(diagnostics::BadDeriveLit { span: lit.span, help }); } fn report_path_args(sess: &Session, meta: &ast::MetaItem) { @@ -154,10 +154,10 @@ fn report_path_args(sess: &Session, meta: &ast::MetaItem) { match meta.kind { MetaItemKind::Word => {} MetaItemKind::List(..) => { - sess.dcx().emit_err(errors::DerivePathArgsList { span }); + sess.dcx().emit_err(diagnostics::DerivePathArgsList { span }); } MetaItemKind::NameValue(..) => { - sess.dcx().emit_err(errors::DerivePathArgsValue { span }); + sess.dcx().emit_err(diagnostics::DerivePathArgsValue { span }); } } } diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 1d9551f93a14c..43399b614b50a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -12,7 +12,7 @@ use rustc_macros::Diagnostic; use rustc_span::{Ident, Span, Symbol, sym}; use thin_vec::{ThinVec, thin_vec}; -use crate::errors; +use crate::diagnostics; macro_rules! path { ($span:expr, $($part:ident)::*) => { vec![$(Ident::new(sym::$part, $span),)*] } @@ -409,7 +409,7 @@ struct DetectNonGenericPointeeAttr<'a, 'b> { impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonGenericPointeeAttr<'a, 'b> { fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) -> Self::Result { if attr.has_name(sym::pointee) { - self.cx.dcx().emit_err(errors::NonGenericPointee { span: attr.span }); + self.cx.dcx().emit_err(diagnostics::NonGenericPointee { span: attr.span }); } } @@ -457,7 +457,7 @@ struct AlwaysErrorOnGenericParam<'a, 'b> { impl<'a, 'b> rustc_ast::visit::Visitor<'a> for AlwaysErrorOnGenericParam<'a, 'b> { fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) -> Self::Result { if attr.has_name(sym::pointee) { - self.cx.dcx().emit_err(errors::NonGenericPointee { span: attr.span }); + self.cx.dcx().emit_err(diagnostics::NonGenericPointee { span: attr.span }); } } } diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index 263ba2968eab4..cf48ff96aed1e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -9,7 +9,7 @@ use thin_vec::{ThinVec, thin_vec}; use crate::deriving::generic::ty::*; use crate::deriving::generic::*; -use crate::errors; +use crate::diagnostics; pub(crate) fn expand_deriving_default( cx: &ExtCtxt<'_>, @@ -177,10 +177,13 @@ fn extract_default_variant<'a>( .filter(|variant| !attr::contains_name(&variant.attrs, sym::non_exhaustive)); let suggs = possible_defaults - .map(|v| errors::NoDefaultVariantSugg { span: v.span.shrink_to_lo() }) + .map(|v| diagnostics::NoDefaultVariantSugg { span: v.span.shrink_to_lo() }) .collect(); - let guar = - cx.dcx().emit_err(errors::NoDefaultVariant { span: trait_span, item_span, suggs }); + let guar = cx.dcx().emit_err(diagnostics::NoDefaultVariant { + span: trait_span, + item_span, + suggs, + }); return Err(guar); } @@ -196,11 +199,13 @@ fn extract_default_variant<'a>( .filter_map(|attr| (attr.span != keep).then_some(attr.span)) }) .collect(); - (!spans.is_empty()) - .then_some(errors::MultipleDefaultsSugg { spans, ident: variant.ident }) + (!spans.is_empty()).then_some(diagnostics::MultipleDefaultsSugg { + spans, + ident: variant.ident, + }) }) .collect(); - let guar = cx.dcx().emit_err(errors::MultipleDefaults { + let guar = cx.dcx().emit_err(diagnostics::MultipleDefaults { span: trait_span, first: first.span, additional: rest.iter().map(|v| v.span).collect(), @@ -223,12 +228,13 @@ fn extract_default_variant<'a>( } else { "" }; - let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span, post }); + let guar = + cx.dcx().emit_err(diagnostics::NonUnitDefault { span: variant.ident.span, post }); return Err(guar); } if let Some(non_exhaustive_attr) = attr::find_by_name(&variant.attrs, sym::non_exhaustive) { - let guar = cx.dcx().emit_err(errors::NonExhaustiveDefault { + let guar = cx.dcx().emit_err(diagnostics::NonExhaustiveDefault { span: variant.ident.span, non_exhaustive: non_exhaustive_attr.span, }); @@ -252,10 +258,10 @@ fn validate_default_attribute( "this method must only be called with a variant that has a `#[default]` attribute", ), [first, rest @ ..] => { - let sugg = errors::MultipleDefaultAttrsSugg { + let sugg = diagnostics::MultipleDefaultAttrsSugg { spans: rest.iter().map(|attr| attr.span).collect(), }; - let guar = cx.dcx().emit_err(errors::MultipleDefaultAttrs { + let guar = cx.dcx().emit_err(diagnostics::MultipleDefaultAttrs { span: default_variant.ident.span, first: first.span, first_rest: rest[0].span, @@ -268,7 +274,7 @@ fn validate_default_attribute( } }; if !attr.is_word() { - let guar = cx.dcx().emit_err(errors::DefaultHasArg { span: attr.span }); + let guar = cx.dcx().emit_err(diagnostics::DefaultHasArg { span: attr.span }); return Err(guar); } @@ -287,7 +293,7 @@ impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, ' } else { "" }; - self.cx.dcx().emit_err(errors::NonUnitDefault { span: attr.span, post }); + self.cx.dcx().emit_err(diagnostics::NonUnitDefault { span: attr.span, post }); } rustc_ast::visit::walk_attribute(self, attr); diff --git a/compiler/rustc_builtin_macros/src/deriving/from.rs b/compiler/rustc_builtin_macros/src/deriving/from.rs index 2e4369f3bb1c8..c5fd0d87251f6 100644 --- a/compiler/rustc_builtin_macros/src/deriving/from.rs +++ b/compiler/rustc_builtin_macros/src/deriving/from.rs @@ -11,7 +11,7 @@ use crate::deriving::generic::{ combine_substructure, }; use crate::deriving::pathvec_std; -use crate::errors; +use crate::diagnostics; /// Generate an implementation of the `From` trait, provided that `item` /// is a struct or a tuple struct with exactly one field. @@ -38,7 +38,7 @@ pub(crate) fn expand_deriving_from( if let [field] = data.fields() { Ok(field.clone()) } else { - let guar = cx.dcx().emit_err(errors::DeriveFromWrongFieldCount { + let guar = cx.dcx().emit_err(diagnostics::DeriveFromWrongFieldCount { span: err_span(), multiple_fields: data.fields().len() > 1, }); @@ -46,7 +46,7 @@ pub(crate) fn expand_deriving_from( } } ItemKind::Enum(_, _, _) | ItemKind::Union(_, _, _) => { - let guar = cx.dcx().emit_err(errors::DeriveFromWrongTarget { + let guar = cx.dcx().emit_err(diagnostics::DeriveFromWrongTarget { span: err_span(), kind: &format!("{} {}", item.kind.article(), item.kind.descr()), }); diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 8b8af1685287c..ff6b15b173ed1 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -194,7 +194,7 @@ use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use ty::{Bounds, Path, Ref, Self_, Ty}; -use crate::{deriving, errors}; +use crate::{deriving, diagnostics}; pub(crate) mod ty; @@ -456,7 +456,7 @@ fn find_type_parameters( } fn visit_mac_call(&mut self, mac: &ast::MacCall) { - self.cx.dcx().emit_err(errors::DeriveMacroCall { span: mac.span() }); + self.cx.dcx().emit_err(diagnostics::DeriveMacroCall { span: mac.span() }); } } @@ -525,7 +525,7 @@ impl<'a> TraitDef<'a> { is_packed, ) } else { - cx.dcx().emit_err(errors::DeriveUnion { span: mitem.span }); + cx.dcx().emit_err(diagnostics::DeriveUnion { span: mitem.span }); return; } } diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/diagnostics.rs similarity index 100% rename from compiler/rustc_builtin_macros/src/errors.rs rename to compiler/rustc_builtin_macros/src/diagnostics.rs diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index d8d749cd35c4b..9bb34cdd642a0 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -9,7 +9,7 @@ use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym}; use thin_vec::{ThinVec, thin_vec}; -use crate::errors::{ +use crate::diagnostics::{ EiiAttributeNotSupported, EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe, EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroInStatementPosition, EiiSharedMacroTarget, EiiStaticArgumentRequired, diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs index d9af43fcd1c3d..aaa9117bb092b 100644 --- a/compiler/rustc_builtin_macros/src/env.rs +++ b/compiler/rustc_builtin_macros/src/env.rs @@ -15,7 +15,7 @@ use rustc_span::edit_distance::edit_distance; use rustc_span::{Ident, Span, Symbol, kw, sym}; use thin_vec::thin_vec; -use crate::errors; +use crate::diagnostics; use crate::util::{expr_to_string, get_exprs_from_tts, get_single_expr_from_tts}; fn lookup_env<'cx>(cx: &'cx ExtCtxt<'_>, var: Symbol) -> Result { @@ -76,7 +76,7 @@ pub(crate) fn expand_option_env<'cx>( unreachable!("`expr_to_string` ensures this is a string lit") }; - let guar = cx.dcx().emit_err(errors::EnvNotUnicode { span: sp, var: *symbol }); + let guar = cx.dcx().emit_err(diagnostics::EnvNotUnicode { span: sp, var: *symbol }); return ExpandResult::Ready(DummyResult::any(sp, guar)); } Ok(value) => cx.expr_call_global( @@ -98,7 +98,7 @@ pub(crate) fn expand_env<'cx>( }; let mut exprs = match mac { Ok(exprs) if exprs.is_empty() || exprs.len() > 2 => { - let guar = cx.dcx().emit_err(errors::EnvTakesArgs { span: sp }); + let guar = cx.dcx().emit_err(diagnostics::EnvTakesArgs { span: sp }); return ExpandResult::Ready(DummyResult::any(sp, guar)); } Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)), @@ -145,24 +145,26 @@ pub(crate) fn expand_env<'cx>( let guar = match err { VarError::NotPresent => { if let Some(msg_from_user) = custom_msg { - cx.dcx() - .emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user }) + cx.dcx().emit_err(diagnostics::EnvNotDefinedWithUserMessage { + span, + msg_from_user, + }) } else if let Some(suggested_var) = find_similar_cargo_var(var) && suggested_var != var { - cx.dcx().emit_err(errors::EnvNotDefined::CargoEnvVarTypo { + cx.dcx().emit_err(diagnostics::EnvNotDefined::CargoEnvVarTypo { span, var: *symbol, suggested_var: Symbol::intern(suggested_var), }) } else if is_cargo_env_var(var) { - cx.dcx().emit_err(errors::EnvNotDefined::CargoEnvVar { + cx.dcx().emit_err(diagnostics::EnvNotDefined::CargoEnvVar { span, var: *symbol, var_expr: pprust::expr_to_string(&var_expr), }) } else { - cx.dcx().emit_err(errors::EnvNotDefined::CustomEnvVar { + cx.dcx().emit_err(diagnostics::EnvNotDefined::CustomEnvVar { span, var: *symbol, var_expr: pprust::expr_to_string(&var_expr), @@ -170,7 +172,7 @@ pub(crate) fn expand_env<'cx>( } } VarError::NotUnicode(_) => { - cx.dcx().emit_err(errors::EnvNotUnicode { span, var: *symbol }) + cx.dcx().emit_err(diagnostics::EnvNotUnicode { span, var: *symbol }) } }; diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 6bb3fa884027e..007251ff3df05 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -20,7 +20,7 @@ use rustc_parse::exp; use rustc_parse_format as parse; use rustc_span::{BytePos, ErrorGuaranteed, Ident, InnerSpan, Span, Symbol}; -use crate::errors; +use crate::diagnostics; use crate::util::{ExprToSpannedString, expr_to_spanned_string}; // The format_args!() macro is expanded in three steps: @@ -73,7 +73,9 @@ fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, // parse the format string let fmtstr = match p.token.kind { - token::Eof => return Err(ecx.dcx().create_err(errors::FormatRequiresString { span: sp })), + token::Eof => { + return Err(ecx.dcx().create_err(diagnostics::FormatRequiresString { span: sp })); + } // This allows us to properly handle cases when the first comma // after the format string is mistakenly replaced with any operator, // which cause the expression parser to eat too much tokens. @@ -122,7 +124,7 @@ fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, p.expect(exp!(Eq))?; let expr = p.parse_expr()?; if let Some((_, prev)) = args.by_name(ident.name) { - ecx.dcx().emit_err(errors::FormatDuplicateArg { + ecx.dcx().emit_err(diagnostics::FormatDuplicateArg { span: ident.span, prev: prev.kind.ident().unwrap().span, duplicate: ident.span, @@ -135,7 +137,7 @@ fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, _ => { let expr = p.parse_expr()?; if !args.named_args().is_empty() { - return Err(ecx.dcx().create_err(errors::PositionalAfterNamed { + return Err(ecx.dcx().create_err(diagnostics::PositionalAfterNamed { span: expr.span, args: args .named_args() @@ -304,7 +306,7 @@ fn make_format_args( // argument span here. fmt_span }; - let mut e = errors::InvalidFormatString { + let mut e = diagnostics::InvalidFormatString { span: sp, note_: None, label_: None, @@ -313,12 +315,12 @@ fn make_format_args( label1: err.label, }; if let Some(note) = err.note { - e.note_ = Some(errors::InvalidFormatStringNote { note }); + e.note_ = Some(diagnostics::InvalidFormatStringNote { note }); } if let Some((label, span)) = err.secondary_label && is_source_literal { - e.label_ = Some(errors::InvalidFormatStringLabel { + e.label_ = Some(diagnostics::InvalidFormatStringLabel { span: fmt_span.from_inner(InnerSpan::new(span.start, span.end)), label, }); @@ -333,7 +335,7 @@ fn make_format_args( Some(arg) => arg.expr.span, None => fmt_span, }; - e.sugg_ = Some(errors::InvalidFormatStringSuggestion::UsePositional { + e.sugg_ = Some(diagnostics::InvalidFormatStringSuggestion::UsePositional { captured: captured_arg_span, len: args.unnamed_args().len().to_string(), span: span.shrink_to_hi(), @@ -344,19 +346,22 @@ fn make_format_args( parse::Suggestion::RemoveRawIdent(span) => { if is_source_literal { let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); - e.sugg_ = Some(errors::InvalidFormatStringSuggestion::RemoveRawIdent { span }) + e.sugg_ = + Some(diagnostics::InvalidFormatStringSuggestion::RemoveRawIdent { span }) } } parse::Suggestion::ReorderFormatParameter(span, replacement) => { let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); - e.sugg_ = Some(errors::InvalidFormatStringSuggestion::ReorderFormatParameter { - span, - replacement, - }); + e.sugg_ = + Some(diagnostics::InvalidFormatStringSuggestion::ReorderFormatParameter { + span, + replacement, + }); } parse::Suggestion::AddMissingColon(span) => { let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); - e.sugg_ = Some(errors::InvalidFormatStringSuggestion::AddMissingColon { span }); + e.sugg_ = + Some(diagnostics::InvalidFormatStringSuggestion::AddMissingColon { span }); } parse::Suggestion::UseRustDebugPrintingMacro => { // This targets `println!("{=}", x);` and `println!("{0=}", x);` @@ -367,7 +372,7 @@ fn make_format_args( let call_span = macro_span.source_callsite(); e.sugg_ = Some( - errors::InvalidFormatStringSuggestion::UseRustDebugPrintingMacro { + diagnostics::InvalidFormatStringSuggestion::UseRustDebugPrintingMacro { macro_span: call_span, replacement, }, @@ -436,7 +441,7 @@ fn make_format_args( } else { // For the moment capturing variables from format strings expanded from macros is // disabled (see RFC #2795) - let guar = ecx.dcx().emit_err(errors::FormatNoArgNamed { span, name }); + let guar = ecx.dcx().emit_err(diagnostics::FormatNoArgNamed { span, name }); unnamed_arg_after_named_arg = true; DummyResult::raw_expr(span, Some(guar)) }; @@ -659,7 +664,7 @@ fn make_format_args( (None, String::new()) }; - errors::NamedArgumentUsedPositionally { + diagnostics::NamedArgumentUsedPositionally { named_arg_sp: arg_name.span, position_label_sp: position_sp_for_msg, suggestion, @@ -701,12 +706,12 @@ fn invalid_placeholder_type_error( ("X", "UpperHex"), ] .into_iter() - .map(|(fmt, trait_name)| errors::FormatUnknownTraitSugg { span: sp, fmt, trait_name }) + .map(|(fmt, trait_name)| diagnostics::FormatUnknownTraitSugg { span: sp, fmt, trait_name }) .collect() } else { vec![] }; - ecx.dcx().emit_err(errors::FormatUnknownTrait { span: sp.unwrap_or(fmt_span), ty, suggs }); + ecx.dcx().emit_err(diagnostics::FormatUnknownTrait { span: sp.unwrap_or(fmt_span), ty, suggs }); } fn report_missing_placeholders( @@ -723,12 +728,14 @@ fn report_missing_placeholders( fmt_span: Span, ) { let mut diag = if let &[(span, named)] = &unused[..] { - ecx.dcx().create_err(errors::FormatUnusedArg { span, named }) + ecx.dcx().create_err(diagnostics::FormatUnusedArg { span, named }) } else { - let unused_labels = - unused.iter().map(|&(span, named)| errors::FormatUnusedArg { span, named }).collect(); + let unused_labels = unused + .iter() + .map(|&(span, named)| diagnostics::FormatUnusedArg { span, named }) + .collect(); let unused_spans = unused.iter().map(|&(span, _)| span).collect(); - ecx.dcx().create_err(errors::FormatUnusedArgs { + ecx.dcx().create_err(diagnostics::FormatUnusedArgs { fmt: fmt_span, unused: unused_spans, unused_labels, @@ -920,12 +927,12 @@ fn report_redundant_format_arguments<'a>( } let sugg = if args.named_args().len() == 0 { - Some(errors::FormatRedundantArgsSugg { spans: suggestion_spans }) + Some(diagnostics::FormatRedundantArgsSugg { spans: suggestion_spans }) } else { None }; - return Some(ecx.dcx().create_err(errors::FormatRedundantArgs { + return Some(ecx.dcx().create_err(diagnostics::FormatRedundantArgs { n: args_spans.len(), span: MultiSpan::from(args_spans), note: multispan, @@ -1034,7 +1041,7 @@ fn report_invalid_references( } else { MultiSpan::from_spans(spans) }; - e = ecx.dcx().create_err(errors::FormatPositionalMismatch { + e = ecx.dcx().create_err(diagnostics::FormatPositionalMismatch { span, n: num_placeholders, desc: num_args_desc, diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 684411d64d943..db034f6fb7011 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -9,7 +9,7 @@ use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::{Ident, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; -use crate::errors; +use crate::diagnostics; use crate::util::check_builtin_macro_attribute; pub(crate) fn expand( @@ -34,13 +34,14 @@ pub(crate) fn expand( { (item, *ident, true, ecx.with_def_site_ctxt(ty.span)) } else { - ecx.dcx().emit_err(errors::AllocMustStatics { span: item.span() }); + ecx.dcx().emit_err(diagnostics::AllocMustStatics { span: item.span() }); return vec![orig_item]; }; // Forbid `#[thread_local]` attributes on the item if let Some(attr) = item.attrs.iter().find(|x| x.has_name(sym::thread_local)) { - ecx.dcx().emit_err(errors::AllocCannotThreadLocal { span: item.span, attr: attr.span }); + ecx.dcx() + .emit_err(diagnostics::AllocCannotThreadLocal { span: item.span, attr: attr.span }); return vec![orig_item]; } diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 9e29262042f21..4a5ff44c402ab 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -33,10 +33,10 @@ mod concat_bytes; mod define_opaque; mod derive; mod deriving; +mod diagnostics; mod edition_panic; mod eii; mod env; -mod errors; mod format; mod format_foreign; mod global_allocator; diff --git a/compiler/rustc_builtin_macros/src/offload.rs b/compiler/rustc_builtin_macros/src/offload.rs index 9dbd2d45a93af..d20c07f8619dc 100644 --- a/compiler/rustc_builtin_macros/src/offload.rs +++ b/compiler/rustc_builtin_macros/src/offload.rs @@ -6,7 +6,7 @@ use rustc_session::config::Offload; use rustc_span::{Ident, Span, sym}; use thin_vec::thin_vec; -use crate::errors; +use crate::diagnostics; fn compile_for_device(ecx: &mut ExtCtxt<'_>) -> bool { ecx.sess.opts.unstable_opts.offload.contains(&Offload::Device) @@ -74,7 +74,7 @@ pub(crate) fn expand_kernel( let dcx = ecx.sess.dcx(); let Some((vis, sig, ident, generics, body)) = extract_fn(&item) else { - dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() }); + dcx.emit_err(diagnostics::AutoDiffInvalidApplication { span: item.span() }); return vec![item]; }; diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index aa936b46eec20..df9cebb8f7c03 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -16,7 +16,7 @@ use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use smallvec::smallvec; use thin_vec::{ThinVec, thin_vec}; -use crate::errors; +use crate::diagnostics; struct ProcMacroDerive { id: NodeId, @@ -91,7 +91,7 @@ pub fn inject( impl<'a> CollectProcMacros<'a> { fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) { if self.is_proc_macro_crate && self.in_root && vis.kind.is_pub() { - self.dcx.emit_err(errors::ProcMacro { span: sp }); + self.dcx.emit_err(diagnostics::ProcMacro { span: sp }); } } @@ -174,7 +174,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { fn visit_item(&mut self, item: &'a ast::Item) { if let ast::ItemKind::MacroDef(..) = item.kind { if self.is_proc_macro_crate && attr::contains_name(&item.attrs, sym::macro_export) { - self.dcx.emit_err(errors::ExportMacroRules { + self.dcx.emit_err(diagnostics::ExportMacroRules { span: self.source_map.guess_head_span(item.span), }); } @@ -238,7 +238,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { if !self.is_proc_macro_crate { self.dcx - .create_err(errors::AttributeOnlyUsableWithCrateType { + .create_err(diagnostics::AttributeOnlyUsableWithCrateType { span: attr.span, path: &pprust::path_to_string(&attr.get_normal_item().path), }) diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index ab7a9c3bccb38..fe2b5e1a45920 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -21,7 +21,7 @@ use rustc_span::source_map::SourceMap; use rustc_span::{ByteSymbol, Pos, Span, Symbol}; use smallvec::SmallVec; -use crate::errors; +use crate::diagnostics; use crate::util::{ check_zero_tts, get_single_str_from_tts, get_single_str_spanned_from_tts, parse_expr, }; @@ -153,7 +153,7 @@ pub(crate) fn expand_include<'cx>( INCOMPLETE_INCLUDE, p.token.span, self.node_id, - errors::IncompleteInclude, + diagnostics::IncompleteInclude, ); } Some(expr) @@ -176,7 +176,7 @@ pub(crate) fn expand_include<'cx>( Ok(Some(item)) => ret.push(item), Ok(None) => { if p.token != token::Eof { - p.dcx().emit_err(errors::ExpectedItem { + p.dcx().emit_err(diagnostics::ExpectedItem { span: p.token.span, token: &pprust::token_to_string(&p.token), }); diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index ac865958011a6..fda0660c48403 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -14,7 +14,7 @@ use rustc_span::{ErrorGuaranteed, Ident, RemapPathScopeComponents, Span, Symbol, use thin_vec::{ThinVec, thin_vec}; use tracing::debug; -use crate::errors; +use crate::diagnostics; use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute}; /// #[test_case] is used by custom test authors to mark tests @@ -44,7 +44,7 @@ pub(crate) fn expand_test_case( } } _ => { - ecx.dcx().emit_err(errors::TestCaseNonItem { span: anno_item.span() }); + ecx.dcx().emit_err(diagnostics::TestCaseNonItem { span: anno_item.span() }); return vec![]; } }; @@ -136,7 +136,7 @@ pub(crate) fn expand_test_or_bench( } if let Some(attr) = attr::find_by_name(&item.attrs, sym::naked) { - cx.dcx().emit_err(errors::NakedFunctionTestingAttribute { + cx.dcx().emit_err(diagnostics::NakedFunctionTestingAttribute { testing_span: attr_sp, naked_span: attr.span, }); @@ -525,27 +525,31 @@ fn check_test_signature( let dcx = cx.dcx(); if let ast::Safety::Unsafe(span) = f.sig.header.safety { - return Err(dcx.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "unsafe" })); + return Err(dcx.emit_err(diagnostics::TestBadFn { + span: i.span, + cause: span, + kind: "unsafe", + })); } if let Some(coroutine_kind) = f.sig.header.coroutine_kind { match coroutine_kind { ast::CoroutineKind::Async { span, .. } => { - return Err(dcx.emit_err(errors::TestBadFn { + return Err(dcx.emit_err(diagnostics::TestBadFn { span: i.span, cause: span, kind: "async", })); } ast::CoroutineKind::Gen { span, .. } => { - return Err(dcx.emit_err(errors::TestBadFn { + return Err(dcx.emit_err(diagnostics::TestBadFn { span: i.span, cause: span, kind: "gen", })); } ast::CoroutineKind::AsyncGen { span, .. } => { - return Err(dcx.emit_err(errors::TestBadFn { + return Err(dcx.emit_err(diagnostics::TestBadFn { span: i.span, cause: span, kind: "async gen", @@ -588,7 +592,7 @@ fn check_bench_signature( // N.B., inadequate check, but we're running // well before resolve, can't get too deep. if f.sig.decl.inputs.len() != 1 { - return Err(cx.dcx().emit_err(errors::BenchSig { span: i.span })); + return Err(cx.dcx().emit_err(diagnostics::BenchSig { span: i.span })); } Ok(()) } diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index db5aecde3472c..0b68b44769987 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -22,7 +22,7 @@ use smallvec::smallvec; use thin_vec::{ThinVec, thin_vec}; use tracing::debug; -use crate::errors; +use crate::diagnostics; #[derive(Clone)] struct Test { @@ -71,7 +71,7 @@ pub fn inject( // Silently allow compiling with panic=abort on these platforms, // but with old behavior (abort if a test fails). } else { - dcx.emit_err(errors::TestsNotSupport {}); + dcx.emit_err(diagnostics::TestsNotSupport {}); } PanicStrategy::Unwind } @@ -166,7 +166,7 @@ impl<'a> Visitor<'a> for InnerItemLinter<'_> { UNNAMEABLE_TEST_ITEMS, attr.span, i.id, - errors::UnnameableTestItems, + diagnostics::UnnameableTestItems, ); } } diff --git a/compiler/rustc_builtin_macros/src/trace_macros.rs b/compiler/rustc_builtin_macros/src/trace_macros.rs index 8264c17b4d171..88837e01a9407 100644 --- a/compiler/rustc_builtin_macros/src/trace_macros.rs +++ b/compiler/rustc_builtin_macros/src/trace_macros.rs @@ -2,7 +2,7 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; use rustc_span::{Span, kw}; -use crate::errors; +use crate::diagnostics; pub(crate) fn expand_trace_macros( cx: &mut ExtCtxt<'_>, @@ -21,7 +21,7 @@ pub(crate) fn expand_trace_macros( }; err |= iter.next().is_some(); if err { - cx.dcx().emit_err(errors::TraceMacros { span: sp }); + cx.dcx().emit_err(diagnostics::TraceMacros { span: sp }); } else { cx.set_trace_macros(value); } diff --git a/compiler/rustc_builtin_macros/src/util.rs b/compiler/rustc_builtin_macros/src/util.rs index 9ac3d0e7eac12..769016ecac2a7 100644 --- a/compiler/rustc_builtin_macros/src/util.rs +++ b/compiler/rustc_builtin_macros/src/util.rs @@ -10,7 +10,7 @@ use rustc_parse::{exp, parser}; use rustc_session::errors::report_lit_error; use rustc_span::{BytePos, Span, Symbol}; -use crate::errors; +use crate::diagnostics; pub(crate) fn check_builtin_macro_attribute(ecx: &ExtCtxt<'_>, meta_item: &MetaItem, name: Symbol) { // All the built-in macro attributes are "words" at the moment. @@ -48,7 +48,7 @@ pub(crate) fn warn_on_duplicate_attribute(ecx: &ExtCtxt<'_>, item: &Annotatable, DUPLICATE_MACRO_ATTRIBUTES, attr.span, ecx.current_expansion.lint_node_id, - errors::DuplicateMacroAttribute, + diagnostics::DuplicateMacroAttribute, ); } } @@ -150,7 +150,7 @@ pub(crate) fn expr_to_string( /// (this should be done as rarely as possible). pub(crate) fn check_zero_tts(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream, name: &str) { if !tts.is_empty() { - cx.dcx().emit_err(errors::TakesNoArguments { span, name }); + cx.dcx().emit_err(diagnostics::TakesNoArguments { span, name }); } } @@ -209,7 +209,7 @@ pub(crate) fn get_single_expr_from_tts( ) -> ExpandResult, ErrorGuaranteed>, ()> { let mut p = cx.new_parser_from_tts(tts); if p.token == token::Eof { - let guar = cx.dcx().emit_err(errors::OnlyOneArgument { span, name }); + let guar = cx.dcx().emit_err(diagnostics::OnlyOneArgument { span, name }); return ExpandResult::Ready(Err(guar)); } let ret = match parse_expr(&mut p) { @@ -219,7 +219,7 @@ pub(crate) fn get_single_expr_from_tts( let _ = p.eat(exp!(Comma)); if p.token != token::Eof { - cx.dcx().emit_err(errors::OnlyOneArgument { span, name }); + cx.dcx().emit_err(diagnostics::OnlyOneArgument { span, name }); } ExpandResult::Ready(Ok(ret)) } @@ -253,7 +253,7 @@ pub(crate) fn get_exprs_from_tts( continue; } if p.token != token::Eof { - let guar = cx.dcx().emit_err(errors::ExpectedCommaInList { span: p.token.span }); + let guar = cx.dcx().emit_err(diagnostics::ExpectedCommaInList { span: p.token.span }); return ExpandResult::Ready(Err(guar)); } } diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/cranelift-release-branch.yml b/compiler/rustc_codegen_cranelift/.github/workflows/cranelift-release-branch.yml new file mode 100644 index 0000000000000..5cfdc3e8f2936 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/.github/workflows/cranelift-release-branch.yml @@ -0,0 +1,64 @@ +name: Test upcoming Cranelift release branch + +on: + schedule: + - cron: "0 3 6 * *" + workflow_dispatch: {} + +permissions: {} + +env: + CARGO_BUILD_INCREMENTAL: false + RUSTFLAGS: "-Dwarnings" + +jobs: + test_upcoming_cranelift_release: + runs-on: ubuntu-latest + timeout-minutes: 90 + + steps: + - uses: actions/checkout@v6 + + - name: Determine latest Wasmtime release branch + id: wasmtime_release_branch + run: | + branches="$( + git ls-remote --heads https://github.com/bytecodealliance/wasmtime.git "refs/heads/release-*" \ + | awk '{print $2}' \ + | sed 's#refs/heads/##' \ + | sort -V + )" + if [[ -z "${branches}" ]]; then + echo "No wasmtime release branches found" + exit 1 + fi + latest="$(echo "${branches}" | tail -n 1)" + echo "Latest release branch: ${latest}" + echo "branch=${latest}" >> "$GITHUB_OUTPUT" + + - name: Patch Cargo.toml to use release branch Cranelift + run: | + cat >>Cargo.toml <", ); diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs index 216c87f095533..5c8b719ad5404 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs @@ -231,6 +231,8 @@ fn build_clif_sysroot_for_triple( // inlining. rustflags.push("-Zinline-mir".to_owned()); + rustflags.push("-Zdisable-incr-comp-backend-caching".to_owned()); + if let Some(prefix) = env::var_os("CG_CLIF_STDLIB_REMAP_PATH_PREFIX") { rustflags.push("--remap-path-prefix".to_owned()); rustflags.push(format!("library/={}/library", prefix.to_str().unwrap())); diff --git a/compiler/rustc_codegen_cranelift/build_system/main.rs b/compiler/rustc_codegen_cranelift/build_system/main.rs index 0720d72c6d7cb..852fda950d88b 100644 --- a/compiler/rustc_codegen_cranelift/build_system/main.rs +++ b/compiler/rustc_codegen_cranelift/build_system/main.rs @@ -59,7 +59,6 @@ fn main() { if env::var_os("RUST_BACKTRACE").is_none() { env::set_var("RUST_BACKTRACE", "1"); } - env::set_var("CG_CLIF_DISABLE_INCR_CACHE", "1"); let mut args = env::args().skip(1); let command = match args.next().as_deref() { diff --git a/compiler/rustc_codegen_cranelift/build_system/prepare.rs b/compiler/rustc_codegen_cranelift/build_system/prepare.rs index ba5cc9a29f599..1bc56e311ec7d 100644 --- a/compiler/rustc_codegen_cranelift/build_system/prepare.rs +++ b/compiler/rustc_codegen_cranelift/build_system/prepare.rs @@ -11,11 +11,13 @@ pub(crate) fn prepare(dirs: &Dirs) { std::fs::create_dir_all(&dirs.download_dir).unwrap(); crate::tests::RAND_REPO.fetch(dirs); crate::tests::REGEX_REPO.fetch(dirs); + crate::tests::GRAVIOLA_REPO.fetch(dirs); } pub(crate) struct GitRepo { url: GitRepoUrl, rev: &'static str, + submodules: &'static [&'static str], content_hash: &'static str, patch_name: &'static str, } @@ -71,10 +73,17 @@ impl GitRepo { user: &'static str, repo: &'static str, rev: &'static str, + submodules: &'static [&'static str], content_hash: &'static str, patch_name: &'static str, ) -> GitRepo { - GitRepo { url: GitRepoUrl::Github { user, repo }, rev, content_hash, patch_name } + GitRepo { + url: GitRepoUrl::Github { user, repo }, + rev, + submodules, + content_hash, + patch_name, + } } fn download_dir(&self, dirs: &Dirs) -> PathBuf { @@ -132,6 +141,7 @@ impl GitRepo { &download_dir, &format!("https://github.com/{}/{}.git", user, repo), self.rev, + self.submodules, ); } } @@ -160,7 +170,7 @@ impl GitRepo { } } -fn clone_repo(download_dir: &Path, repo: &str, rev: &str) { +fn clone_repo(download_dir: &Path, repo: &str, rev: &str, submodules: &[&str]) { eprintln!("[CLONE] {}", repo); match fs::remove_dir_all(download_dir) { @@ -180,6 +190,13 @@ fn clone_repo(download_dir: &Path, repo: &str, rev: &str) { checkout_cmd.arg("-q").arg(rev); spawn_and_wait(checkout_cmd); + if !submodules.is_empty() { + let mut submodule_cmd = git_command(download_dir, "submodule"); + submodule_cmd.arg("update").arg("--init"); + submodule_cmd.args(submodules); + spawn_and_wait(submodule_cmd); + } + std::fs::remove_dir_all(download_dir.join(".git")).unwrap(); } diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index 3b6a2e7a055cb..685bf8ce9a891 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -125,6 +125,7 @@ pub(crate) static RAND_REPO: GitRepo = GitRepo::github( "rust-random", "rand", "1f4507a8e1cf8050e4ceef95eeda8f64645b6719", + &[], "981f8bf489338978", "rand", ); @@ -135,12 +136,24 @@ pub(crate) static REGEX_REPO: GitRepo = GitRepo::github( "rust-lang", "regex", "061ee815ef2c44101dba7b0b124600fcb03c1912", + &[], "dc26aefbeeac03ca", "regex", ); static REGEX: CargoProject = CargoProject::new(REGEX_REPO.source_dir(), "regex_target"); +pub(crate) static GRAVIOLA_REPO: GitRepo = GitRepo::github( + "ctz", + "graviola", + "c779b83cfd7114c4802293700c92cfb5e05cb4b7", + &["thirdparty/cavp", "thirdparty/wycheproof"], + "e0925ceb21a56101", + "graviola", +); + +static GRAVIOLA: CargoProject = CargoProject::new(GRAVIOLA_REPO.source_dir(), "graviola_target"); + static PORTABLE_SIMD_SRC: RelPath = RelPath::build("portable-simd"); static PORTABLE_SIMD: CargoProject = CargoProject::new(PORTABLE_SIMD_SRC, "portable-simd_target"); @@ -199,6 +212,43 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ spawn_and_wait(build_cmd); } }), + TestCase::custom("test.graviola", &|runner| { + let (arch, _) = runner.target_compiler.triple.split_once('-').unwrap(); + + if !["aarch64", "x86_64"].contains(&arch) { + eprintln!("Skipping `graviola` tests: unsupported target"); + return; + } + + GRAVIOLA_REPO.patch(&runner.dirs); + GRAVIOLA.clean(&runner.dirs); + + if runner.is_native { + let mut test_cmd = GRAVIOLA.test(&runner.target_compiler, &runner.dirs); + + // FIXME: Disable AVX-512 until intrinsics are supported. + test_cmd.env("GRAVIOLA_CPU_DISABLE_avx512f", "1"); + test_cmd.env("GRAVIOLA_CPU_DISABLE_avx512bw", "1"); + test_cmd.env("GRAVIOLA_CPU_DISABLE_avx512vl", "1"); + + test_cmd.args([ + "-p", + "graviola", + "--lib", + "--", + "-q", + // FIXME: Disable AVX-512 until intrinsics are supported. + "--skip", + "check_counter512", + ]); + spawn_and_wait(test_cmd); + } else { + eprintln!("Cross-Compiling: Not running tests"); + let mut build_cmd = GRAVIOLA.build(&runner.target_compiler, &runner.dirs); + build_cmd.args(["-p", "graviola", "--lib"]); + spawn_and_wait(build_cmd); + } + }), TestCase::custom("test.portable-simd", &|runner| { apply_patches( &runner.dirs, diff --git a/compiler/rustc_codegen_cranelift/config.txt b/compiler/rustc_codegen_cranelift/config.txt index 72631355733c4..7c516e2164b40 100644 --- a/compiler/rustc_codegen_cranelift/config.txt +++ b/compiler/rustc_codegen_cranelift/config.txt @@ -20,7 +20,7 @@ aot.mini_core_hello_world testsuite.base_sysroot aot.arbitrary_self_types_pointers_and_wrappers -#jit.std_example # FIXME(#1619) broken for some reason +jit.std_example aot.std_example aot.dst_field_align aot.subslice-patterns-const-eval @@ -36,4 +36,5 @@ test.sysroot testsuite.extended_sysroot test.rust-random/rand test.regex +test.graviola test.portable-simd diff --git a/compiler/rustc_codegen_cranelift/example/neon.rs b/compiler/rustc_codegen_cranelift/example/neon.rs index 98a2a7af38f6b..1aec5badcbc26 100644 --- a/compiler/rustc_codegen_cranelift/example/neon.rs +++ b/compiler/rustc_codegen_cranelift/example/neon.rs @@ -9,6 +9,25 @@ use std::mem::transmute; #[cfg(target_arch = "aarch64")] use std::simd::*; +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "crc")] +unsafe fn test_crc32() { + assert!(std::arch::is_aarch64_feature_detected!("crc")); + + let a: u32 = 42; + let b: u64 = 0xdeadbeef; + + assert_eq!(__crc32b(a, b as u8), 0xEB0E363F); + assert_eq!(__crc32h(a, b as u16), 0x9A54BD80); + assert_eq!(__crc32w(a, b as u32), 0xF491F059); + assert_eq!(__crc32d(a, b as u64), 0xD14BBEA6); + + assert_eq!(__crc32cb(a, b as u8), 0xF67C32D8); + assert_eq!(__crc32ch(a, b as u16), 0x479108B8); + assert_eq!(__crc32cw(a, b as u32), 0x979F49F8); + assert_eq!(__crc32cd(a, b as u64), 0x0E6BE593); +} + #[cfg(target_arch = "aarch64")] unsafe fn test_vpmin_s8() { let a = i8x8::from([1, -2, 3, -4, 5, 6, 7, 8]); @@ -240,6 +259,272 @@ unsafe fn test_vrndnq_f32() { assert_eq!(r, e); } +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "aes")] +unsafe fn test_vaeseq_u8() { + // AArch64 llvm intrinsic: llvm.aarch64.crypto.aese + let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + let b = u8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); + let e = u8x16::from([ + 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, + 0xca, + ]); + let r: u8x16 = unsafe { transmute(vaeseq_u8(transmute(a), transmute(b))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "aes")] +unsafe fn test_vaesdq_u8() { + // AArch64 llvm intrinsic: llvm.aarch64.crypto.aesd + let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + let b = u8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); + let e = u8x16::from([ + 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, + 0x7c, + ]); + let r: u8x16 = unsafe { transmute(vaesdq_u8(transmute(a), transmute(b))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "aes")] +unsafe fn test_vaesmcq_u8() { + // AArch64 llvm intrinsic: llvm.aarch64.crypto.aesmc + let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + let e = u8x16::from([2, 7, 0, 5, 6, 3, 4, 1, 10, 15, 8, 13, 14, 11, 12, 9]); + let r: u8x16 = unsafe { transmute(vaesmcq_u8(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "aes")] +unsafe fn test_vaesimcq_u8() { + // AArch64 llvm intrinsic: llvm.aarch64.crypto.aesimc + let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + let e = u8x16::from([10, 15, 8, 13, 14, 11, 12, 9, 2, 7, 0, 5, 6, 3, 4, 1]); + let r: u8x16 = unsafe { transmute(vaesimcq_u8(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "sha2")] +unsafe fn test_vsha256hq_u32() { + // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha256h + let a = u32x4::from([0, 1, 2, 3]); + let b = u32x4::from([4, 5, 6, 7]); + let c = u32x4::from([8, 9, 10, 11]); + let e = u32x4::from([0x27bb4ae0, 0xd8f61f7c, 0xb7c1ecdc, 0x10800215]); + let r: u32x4 = unsafe { transmute(vsha256hq_u32(transmute(a), transmute(b), transmute(c))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "sha2")] +unsafe fn test_vsha256h2q_u32() { + // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha256h2 + let a = u32x4::from([0, 1, 2, 3]); + let b = u32x4::from([4, 5, 6, 7]); + let c = u32x4::from([8, 9, 10, 11]); + let e = u32x4::from([0x6989ee0d, 0x4b055920, 0x52800a12, 0x00000014]); + let r: u32x4 = unsafe { transmute(vsha256h2q_u32(transmute(a), transmute(b), transmute(c))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "sha2")] +unsafe fn test_vsha256su0q_u32() { + // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha256su0 + let a = u32x4::from([0, 1, 2, 3]); + let b = u32x4::from([4, 5, 6, 7]); + let e = u32x4::from([0x02004000, 0x04008001, 0x0600c002, 0x08010003]); + let r: u32x4 = unsafe { transmute(vsha256su0q_u32(transmute(a), transmute(b))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "sha2")] +unsafe fn test_vsha256su1q_u32() { + // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha256su1 + let a = u32x4::from([0, 1, 2, 3]); + let b = u32x4::from([4, 5, 6, 7]); + let c = u32x4::from([8, 9, 10, 11]); + let e = u32x4::from([0x00044005, 0x0004e007, 0xa802211b, 0xec036145]); + let r: u32x4 = unsafe { transmute(vsha256su1q_u32(transmute(a), transmute(b), transmute(c))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "aes")] +fn test_vmull_p64() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.pmull64 + let a: u64 = 3; + let b: u64 = 6; + let e: u128 = 10; + let r: u128 = vmull_p64(a, b); + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vmull_p8() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.pmull.v8i16 + let a = u8x8::from([0, 1, 2, 3, 4, 5, 6, 7]); + let b = u8x8::from([8, 9, 10, 11, 12, 13, 14, 15]); + let e = u16x8::from([0x0000, 0x0009, 0x0014, 0x001d, 0x0030, 0x0039, 0x0024, 0x002d]); + let r: u16x8 = unsafe { transmute(vmull_p8(transmute(a), transmute(b))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vqdmulh_s16() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.sqdmulh.v4i16 + let a = i16x4::from([1, 2, 4, 8]); + let b = i16x4::from([16384, 16384, 16384, 16384]); + let e = i16x4::from([0, 1, 2, 4]); + let r: i16x4 = unsafe { transmute(vqdmulh_s16(transmute(a), transmute(b))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vqdmulh_s32() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.sqdmulh.v2i32 + let a = i32x2::from([1, 2]); + let b = i32x2::from([1073741824, 1073741824]); + let e = i32x2::from([0, 1]); + let r: i32x2 = unsafe { transmute(vqdmulh_s32(transmute(a), transmute(b))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vqdmulhq_s16() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.sqdmulh.v8i16 + let a = i16x8::from([1, 2, 4, 8, 16, 32, 64, 128]); + let b = i16x8::from([16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384]); + let e = i16x8::from([0, 1, 2, 4, 8, 16, 32, 64]); + let r: i16x8 = unsafe { transmute(vqdmulhq_s16(transmute(a), transmute(b))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vqdmulhq_s32() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.sqdmulh.v4i32 + let a = i32x4::from([1, 2, 4, 8]); + let b = i32x4::from([1073741824, 1073741824, 1073741824, 1073741824]); + let e = i32x4::from([0, 1, 2, 4]); + let r: i32x4 = unsafe { transmute(vqdmulhq_s32(transmute(a), transmute(b))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddl_s8() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v4i16.v8i8 + let a = i8x8::from([1, 2, 3, 4, -5, -6, -7, -8]); + let e = i16x4::from([3, 7, -11, -15]); + let r: i16x4 = unsafe { transmute(vpaddl_s8(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddl_s16() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v2i32.v4i16 + let a = i16x4::from([1, 2, -3, -4]); + let e = i32x2::from([3, -7]); + let r: i32x2 = unsafe { transmute(vpaddl_s16(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddl_s32() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v1i64.v2i32 + let a = i32x2::from([1, -2]); + let e = i64x1::from([-1]); + let r: i64x1 = unsafe { transmute(vpaddl_s32(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddlq_s8() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v8i16.v16i8 + let a = i8x16::from([1, 2, 3, 4, 5, 6, 7, 8, -9, -10, -11, -12, -13, -14, -15, -16]); + let e = i16x8::from([3, 7, 11, 15, -19, -23, -27, -31]); + let r: i16x8 = unsafe { transmute(vpaddlq_s8(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddlq_s16() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v4i32.v8i16 + let a = i16x8::from([1, 2, 3, 4, -5, -6, -7, -8]); + let e = i32x4::from([3, 7, -11, -15]); + let r: i32x4 = unsafe { transmute(vpaddlq_s16(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddlq_s32() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v2i64.v4i32 + let a = i32x4::from([1, 2, -3, -4]); + let e = i64x2::from([3, -7]); + let r: i64x2 = unsafe { transmute(vpaddlq_s32(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddl_u8() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v4i16.v8i8 + let a = u8x8::from([255, 254, 253, 252, 251, 250, 249, 248]); + let e = u16x4::from([509, 505, 501, 497]); + let r: u16x4 = unsafe { transmute(vpaddl_u8(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddl_u16() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v2i32.v4i16 + let a = u16x4::from([65535, 65534, 65533, 65532]); + let e = u32x2::from([131069, 131065]); + let r: u32x2 = unsafe { transmute(vpaddl_u16(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddl_u32() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v1i64.v2i32 + let a = u32x2::from([4294967295, 4294967294]); + let e = u64x1::from([8589934589]); + let r: u64x1 = unsafe { transmute(vpaddl_u32(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddlq_u8() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v8i16.v16i8 + let a = u8x16::from([ + 255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240, + ]); + let e = u16x8::from([509, 505, 501, 497, 493, 489, 485, 481]); + let r: u16x8 = unsafe { transmute(vpaddlq_u8(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddlq_u16() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v4i32.v8i16 + let a = u16x8::from([65535, 65534, 65533, 65532, 65531, 65530, 65529, 65528]); + let e = u32x4::from([131069, 131065, 131061, 131057]); + let r: u32x4 = unsafe { transmute(vpaddlq_u16(transmute(a))) }; + assert_eq!(r, e); +} + +#[cfg(target_arch = "aarch64")] +unsafe fn test_vpaddlq_u32() { + // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v2i64.v4i32 + let a = u32x4::from([4294967295, 4294967294, 4294967293, 4294967292]); + let e = u64x2::from([8589934589, 8589934585]); + let r: u64x2 = unsafe { transmute(vpaddlq_u32(transmute(a))) }; + assert_eq!(r, e); +} + #[cfg(target_arch = "aarch64")] fn main() { unsafe { @@ -272,6 +557,40 @@ fn main() { test_vminq_f32(); test_vaddvq_f32(); test_vrndnq_f32(); + + test_crc32(); + + test_vaeseq_u8(); + test_vaesdq_u8(); + test_vaesmcq_u8(); + test_vaesimcq_u8(); + + test_vsha256hq_u32(); + test_vsha256h2q_u32(); + test_vsha256su0q_u32(); + test_vsha256su1q_u32(); + + test_vmull_p64(); + test_vmull_p8(); + + test_vqdmulh_s16(); + test_vqdmulh_s32(); + test_vqdmulhq_s16(); + test_vqdmulhq_s32(); + + test_vpaddl_s8(); + test_vpaddl_s16(); + test_vpaddl_s32(); + test_vpaddlq_s8(); + test_vpaddlq_s16(); + test_vpaddlq_s32(); + + test_vpaddl_u8(); + test_vpaddl_u16(); + test_vpaddl_u32(); + test_vpaddlq_u8(); + test_vpaddlq_u16(); + test_vpaddlq_u32(); } } diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index f0e38ae0610c9..252344b378e8a 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -1,8 +1,6 @@ #![feature(core_intrinsics, coroutines, coroutine_trait, repr_simd, tuple_trait, unboxed_closures)] #![allow(internal_features)] -#[cfg(target_arch = "x86_64")] -use std::arch::asm; #[cfg(target_arch = "x86_64")] use std::arch::x86_64::*; use std::hint::black_box; @@ -591,7 +589,7 @@ unsafe fn test_xmm_roundtrip() { let input = [1u8; 16]; let mut output = [0u8; 16]; - asm!( + std::arch::asm!( "movups {xmm}, [{input}]", "movups [{output}], {xmm}", input = in(reg) input.as_ptr(), @@ -611,7 +609,7 @@ unsafe fn test_ymm_roundtrip() { let input = [1u8; 32]; let mut output = [0u8; 32]; - asm!( + std::arch::asm!( "vmovups {ymm}, [{input}]", "vmovups [{output}], {ymm}", input = in(reg) input.as_ptr(), @@ -631,7 +629,7 @@ unsafe fn test_zmm_roundtrip() { let input = [1u8; 64]; let mut output = [0u8; 64]; - asm!( + std::arch::asm!( "vmovups {zmm}, [{input}]", "vmovups [{output}], {zmm}", input = in(reg) input.as_ptr(), diff --git a/compiler/rustc_codegen_cranelift/patches/0001-portable-simd-Disable-f16-usage-in-portable-simd.patch b/compiler/rustc_codegen_cranelift/patches/0001-portable-simd-Disable-f16-usage-in-portable-simd.patch index 029e493227cd1..a2fcd97349e2b 100644 --- a/compiler/rustc_codegen_cranelift/patches/0001-portable-simd-Disable-f16-usage-in-portable-simd.patch +++ b/compiler/rustc_codegen_cranelift/patches/0001-portable-simd-Disable-f16-usage-in-portable-simd.patch @@ -154,6 +154,21 @@ index 510f4c9..175cbce 100644 -impl_trait! { f16 { bits: u16, mask: i16 }, f32 { bits: u32, mask: i32 }, f64 { bits: u64, mask: i64 } } +impl_trait! { f32 { bits: u32, mask: i32 }, f64 { bits: u64, mask: i64 } } +diff --git a/crates/core_simd/src/simd/prelude.rs b/crates/core_simd/src/simd/prelude.rs +index 51b8def..6e93f16 100644 +--- a/crates/core_simd/src/simd/prelude.rs ++++ b/crates/core_simd/src/simd/prelude.rs +@@ -14,10 +14,6 @@ pub use super::{ + simd_swizzle, + }; + +-#[rustfmt::skip] +-#[doc(no_inline)] +-pub use super::{f16x1, f16x2, f16x4, f16x8, f16x16, f16x32, f16x64}; +- + #[rustfmt::skip] + #[doc(no_inline)] + pub use super::{f32x1, f32x2, f32x4, f32x8, f32x16, f32x32, f32x64}; diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index fbef69f..c8e0b8c 100644 --- a/crates/core_simd/src/vector.rs diff --git a/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch index b7276e43153bc..717495cbcdf33 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch @@ -34,17 +34,17 @@ index a60f0799c0e..af056fbf41f 100644 #[cfg(target_has_atomic_load_store = "8")] #[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs -index bf2b6d59f88..d5ccce03bbf 100644 +index 8a9a0b5..92ed9a6 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs -@@ -3585,42 +3585,6 @@ pub const fn as_ptr(&self) -> *mut $int_type { +@@ -3762,42 +3757,6 @@ atomic_int! { 8, u64 AtomicU64 } -#[cfg(target_has_atomic_load_store = "128")] -atomic_int! { - cfg(target_has_atomic = "128"), -- cfg(target_has_atomic_equal_alignment = "128"), +- cfg(target_has_atomic_primitive_alignment = "128"), - unstable(feature = "integer_atomics", issue = "99069"), - unstable(feature = "integer_atomics", issue = "99069"), - unstable(feature = "integer_atomics", issue = "99069"), @@ -62,7 +62,7 @@ index bf2b6d59f88..d5ccce03bbf 100644 -#[cfg(target_has_atomic_load_store = "128")] -atomic_int! { - cfg(target_has_atomic = "128"), -- cfg(target_has_atomic_equal_alignment = "128"), +- cfg(target_has_atomic_primitive_alignment = "128"), - unstable(feature = "integer_atomics", issue = "99069"), - unstable(feature = "integer_atomics", issue = "99069"), - unstable(feature = "integer_atomics", issue = "99069"), diff --git a/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-disable-f16-math.patch b/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-disable-f16-math.patch index 2aeb4a8a1874c..8d21359aa0043 100644 --- a/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-disable-f16-math.patch +++ b/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-disable-f16-math.patch @@ -7,11 +7,389 @@ Subject: [PATCH] Disable f16 math tests for cranelift coretests/tests/num/floats.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) -diff --git a/coretests/tests/floats/mod.rs b/coretests/tests/floats/mod.rs -index c61961f8584..d7b4fa20322 100644 +diff --git a/coretests/tests/num/floats.rs b/coretests/tests/num/floats.rs +index 1d7956b..01e4caa 100644 --- a/coretests/tests/num/floats.rs +++ b/coretests/tests/num/floats.rs -@@ -1534,7 +1534,7 @@ fn s_nan() -> Float { +@@ -444,7 +444,7 @@ pub(crate) use float_test; + float_test! { + name: num, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -463,7 +463,7 @@ float_test! { + name: num_rem, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -476,7 +476,7 @@ float_test! { + float_test! { + name: nan, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -496,7 +496,7 @@ float_test! { + float_test! { + name: infinity, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -514,7 +514,7 @@ float_test! { + float_test! { + name: neg_infinity, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -532,7 +532,7 @@ float_test! { + float_test! { + name: zero, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -550,7 +550,7 @@ float_test! { + float_test! { + name: neg_zero, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -570,7 +570,7 @@ float_test! { + float_test! { + name: one, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -588,7 +588,7 @@ float_test! { + float_test! { + name: is_nan, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -609,7 +609,7 @@ float_test! { + float_test! { + name: is_infinite, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -630,7 +630,7 @@ float_test! { + float_test! { + name: is_finite, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -651,7 +651,7 @@ float_test! { + float_test! { + name: is_normal, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -673,7 +673,7 @@ float_test! { + float_test! { + name: classify, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + }, + test { + let nan: Float = Float::NAN; +@@ -695,7 +695,7 @@ float_test! { + name: min, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -737,7 +737,7 @@ float_test! { + name: max, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -780,7 +780,7 @@ float_test! { + name: minimum, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -812,7 +812,7 @@ float_test! { + name: maximum, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -845,7 +845,7 @@ float_test! { + name: midpoint, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -898,7 +898,7 @@ float_test! { + attrs: { + const: #[cfg(false)], + // Needs powi +- f16: #[cfg(target_has_reliable_f16_math)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128_math)], + }, + test { +@@ -929,7 +929,7 @@ float_test! { + name: abs, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -948,7 +948,7 @@ float_test! { + name: copysign, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -964,7 +964,7 @@ float_test! { + attrs: { + const: #[cfg(false)], + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -982,7 +982,7 @@ float_test! { + attrs: { + const: #[cfg(false)], + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -998,7 +998,7 @@ float_test! { + name: floor, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1028,7 +1028,7 @@ float_test! { + name: ceil, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1058,7 +1058,7 @@ float_test! { + name: round, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1089,7 +1089,7 @@ float_test! { + name: round_ties_even, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1120,7 +1120,7 @@ float_test! { + name: trunc, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1150,7 +1150,7 @@ float_test! { + name: fract, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1182,7 +1182,7 @@ float_test! { + name: signum, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1200,7 +1200,7 @@ float_test! { + float_test! { + name: is_sign_positive, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -1219,7 +1219,7 @@ float_test! { + float_test! { + name: is_sign_negative, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -1238,7 +1238,7 @@ float_test! { + float_test! { + name: next_up, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -1269,7 +1269,7 @@ float_test! { + float_test! { + name: next_down, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -1303,7 +1303,7 @@ float_test! { + attrs: { + const: #[cfg(false)], + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1321,7 +1321,7 @@ float_test! { + name: clamp_min_greater_than_max, + attrs: { + const: #[cfg(false)], +- f16: #[should_panic, cfg(target_has_reliable_f16)], ++ f16: #[should_panic, cfg(false)], + f32: #[should_panic], + f64: #[should_panic], + f128: #[should_panic, cfg(target_has_reliable_f128)], +@@ -1335,7 +1335,7 @@ float_test! { + name: clamp_min_is_nan, + attrs: { + const: #[cfg(false)], +- f16: #[should_panic, cfg(target_has_reliable_f16)], ++ f16: #[should_panic, cfg(false)], + f32: #[should_panic], + f64: #[should_panic], + f128: #[should_panic, cfg(target_has_reliable_f128)], +@@ -1349,7 +1349,7 @@ float_test! { + name: clamp_max_is_nan, + attrs: { + const: #[cfg(false)], +- f16: #[should_panic, cfg(target_has_reliable_f16)], ++ f16: #[should_panic, cfg(false)], + f32: #[should_panic], + f64: #[should_panic], + f128: #[should_panic, cfg(target_has_reliable_f128)], +@@ -1363,7 +1363,7 @@ float_test! { + name: total_cmp, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1469,7 +1469,7 @@ float_test! { + attrs: { + const: #[cfg(false)], + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1526,7 +1526,7 @@ float_test! { + name: recip, + attrs: { + // Miri only uses softfloats here, so that always works +- f16: #[cfg(any(miri, target_has_reliable_f16_math))], ++ f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { +@@ -1549,7 +1549,7 @@ float_test! { + name: powi, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(target_has_reliable_f16_math)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128_math)], + }, + test { +@@ -1570,7 +1570,7 @@ float_test! { name: powf, attrs: { const: #[cfg(false)], @@ -20,7 +398,7 @@ index c61961f8584..d7b4fa20322 100644 f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1557,7 +1557,7 @@ fn s_nan() -> Float { +@@ -1593,7 +1593,7 @@ float_test! { name: exp, attrs: { const: #[cfg(false)], @@ -29,7 +407,7 @@ index c61961f8584..d7b4fa20322 100644 f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1578,7 +1578,7 @@ fn s_nan() -> Float { +@@ -1614,7 +1614,7 @@ float_test! { name: exp2, attrs: { const: #[cfg(false)], @@ -38,7 +416,7 @@ index c61961f8584..d7b4fa20322 100644 f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1598,7 +1598,7 @@ fn s_nan() -> Float { +@@ -1634,7 +1634,7 @@ float_test! { name: ln, attrs: { const: #[cfg(false)], @@ -47,7 +425,7 @@ index c61961f8584..d7b4fa20322 100644 f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1620,7 +1620,7 @@ fn s_nan() -> Float { +@@ -1656,7 +1656,7 @@ float_test! { name: log, attrs: { const: #[cfg(false)], @@ -56,7 +434,7 @@ index c61961f8584..d7b4fa20322 100644 f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1645,7 +1645,7 @@ fn s_nan() -> Float { +@@ -1681,7 +1681,7 @@ float_test! { name: log2, attrs: { const: #[cfg(false)], @@ -65,7 +443,7 @@ index c61961f8584..d7b4fa20322 100644 f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1668,7 +1668,7 @@ fn s_nan() -> Float { +@@ -1704,7 +1704,7 @@ float_test! { name: log10, attrs: { const: #[cfg(false)], @@ -74,7 +452,7 @@ index c61961f8584..d7b4fa20322 100644 f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1692,7 +1692,7 @@ fn s_nan() -> Float { +@@ -1728,7 +1728,7 @@ float_test! { name: asinh, attrs: { const: #[cfg(false)], @@ -83,7 +461,7 @@ index c61961f8584..d7b4fa20322 100644 f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1725,7 +1725,7 @@ fn s_nan() -> Float { +@@ -1764,7 +1764,7 @@ float_test! { name: acosh, attrs: { const: #[cfg(false)], @@ -92,7 +470,7 @@ index c61961f8584..d7b4fa20322 100644 f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1753,7 +1753,7 @@ fn s_nan() -> Float { +@@ -1795,7 +1795,7 @@ float_test! { name: atanh, attrs: { const: #[cfg(false)], @@ -101,7 +479,7 @@ index c61961f8584..d7b4fa20322 100644 f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1779,7 +1779,7 @@ fn s_nan() -> Float { +@@ -1821,7 +1821,7 @@ float_test! { name: gamma, attrs: { const: #[cfg(false)], @@ -110,7 +488,7 @@ index c61961f8584..d7b4fa20322 100644 f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -1814,7 +1814,7 @@ fn s_nan() -> Float { +@@ -1856,7 +1856,7 @@ float_test! { name: ln_gamma, attrs: { const: #[cfg(false)], @@ -119,7 +497,79 @@ index c61961f8584..d7b4fa20322 100644 f128: #[cfg(target_has_reliable_f128_math)], }, test { -@@ -2027,7 +2027,7 @@ fn s_nan() -> Float { +@@ -1874,7 +1874,7 @@ float_test! { + float_test! { + name: to_degrees, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -1895,7 +1895,7 @@ float_test! { + float_test! { + name: to_radians, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -1916,7 +1916,7 @@ float_test! { + float_test! { + name: to_algebraic, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -1940,7 +1940,7 @@ float_test! { + float_test! { + name: to_bits_conv, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -1967,7 +1967,7 @@ float_test! { + float_test! { + name: mul_add, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + // FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ + f32: #[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)], + f64: #[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)], +@@ -1992,7 +1992,7 @@ float_test! { + float_test! { + name: from, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -2049,7 +2049,7 @@ float_test! { + float_test! { + name: max_exact_integer_constant, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -2091,7 +2091,7 @@ float_test! { + float_test! { + name: min_exact_integer_constant, + attrs: { +- f16: #[cfg(target_has_reliable_f16)], ++ f16: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { +@@ -2156,7 +2156,7 @@ float_test! { attrs: { // FIXME(f16_f128): add math tests when available const: #[cfg(false)], diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain.toml b/compiler/rustc_codegen_cranelift/rust-toolchain.toml index 486078185db84..faadf08929557 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain.toml +++ b/compiler/rustc_codegen_cranelift/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2026-04-28" +channel = "nightly-2026-06-06" components = ["rust-src", "rustc-dev", "llvm-tools", "rustfmt"] profile = "minimal" diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh index 7ce54a45e2ce6..ab31c43fb1b12 100644 --- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh +++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh @@ -54,7 +54,7 @@ index 2e16f2cf27..3ac3df99a8 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1147,6 +1147,8 @@ class RustBuild(object): - args += ["-Zwarnings"] + if deny_warnings: env["CARGO_BUILD_WARNINGS"] = "deny" + env["RUSTFLAGS"] += " -Zbinary-dep-depinfo" diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index 4a663c48c0aff..683adeb49edd1 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -88,6 +88,8 @@ rm -r tests/run-make/reproducible-build-2 rm -r tests/run-make/no-builtins-lto rm -r tests/run-make/reachable-extern-fn-available-lto rm -r tests/run-make/no-builtins-linker-plugin-lto +rm -r tests/run-make/fat-then-thin-lto +rm -r tests/run-make/cross-lang-lto-upstream-rlibs # coverage instrumentation rm tests/ui/consts/precise-drop-with-coverage.rs @@ -146,6 +148,7 @@ rm tests/ui/consts/const-mut-refs-crate.rs # same rm tests/ui/abi/large-byval-align.rs # exceeds implementation limit of Cranelift rm -r tests/run-make/short-ice # ICE backtrace begin/end marker mismatch rm -r tests/run-make/naked-dead-code-elimination # function not eliminated +rm tests/ui/codegen/huge-stacks.rs # Cranelift doesn't allow stack frames to exceed 4GB # doesn't work due to the way the rustc test suite is invoked. # should work when using ./x.py test the way it is intended diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index a8b179f169bf6..0eb493036ce51 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -31,6 +31,11 @@ use crate::base::codegen_unwind_terminate; use crate::debuginfo::EXCEPTION_HANDLER_CLEANUP; use crate::prelude::*; +struct ArgValue<'tcx> { + value: CValue<'tcx>, + is_underaligned_pointee: bool, +} + fn clif_sig_from_fn_abi<'tcx>( tcx: TyCtxt<'tcx>, default_call_conv: CallConv, @@ -55,8 +60,9 @@ pub(crate) fn conv_to_call_conv( match c { CanonAbi::Rust | CanonAbi::RustCold | CanonAbi::C => default_call_conv, - // Cranelift doesn't currently have anything for this. - CanonAbi::RustPreserveNone => default_call_conv, + CanonAbi::RustPreserveNone | CanonAbi::RustTail => { + sess.dcx().fatal(format!("call conv {c:?} is LLVM-specific")) + } // Functions with this calling convention can only be called from assembly, but it is // possible to declare an `extern "custom"` block, so the backend still needs a calling @@ -71,7 +77,7 @@ pub(crate) fn conv_to_call_conv( }, CanonAbi::Interrupt(_) | CanonAbi::Arm(_) | CanonAbi::Swift => { - sess.dcx().fatal("call conv {c:?} is not yet implemented") + sess.dcx().fatal(format!("call conv {c:?} is not yet implemented")) } CanonAbi::GpuKernel => { unreachable!("tried to use {c:?} call conv which only exists on an unsupported target") @@ -245,8 +251,8 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ // None means pass_mode == NoPass enum ArgKind<'tcx> { - Normal(Option>), - Spread(Vec>>), + Normal(Option>), + Spread(Vec>>), } // FIXME implement variadics in cranelift @@ -299,8 +305,12 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ if fx.instance.def.requires_caller_location(fx.tcx) { // Store caller location for `#[track_caller]`. let arg_abi = arg_abis_iter.next().unwrap(); - fx.caller_location = - Some(cvalue_for_param(fx, None, None, arg_abi, &mut block_params_iter).unwrap()); + let param = cvalue_for_param(fx, None, None, arg_abi, &mut block_params_iter).unwrap(); + assert!( + !param.is_underaligned_pointee, + "caller location argument should not be underaligned", + ); + fx.caller_location = Some(param.value); } assert_eq!(arg_abis_iter.next(), None, "ArgAbi left behind for {:?}", fx.fn_abi); @@ -311,23 +321,24 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ for (local, arg_kind, ty) in func_params { // While this is normally an optimization to prevent an unnecessary copy when an argument is // not mutated by the current function, this is necessary to support unsized arguments. - if let ArgKind::Normal(Some(val)) = arg_kind { - if let Some((addr, meta)) = val.try_to_ptr() { - // Ownership of the value at the backing storage for an argument is passed to the - // callee per the ABI, so it is fine to borrow the backing storage of this argument - // to prevent a copy. - - let place = if let Some(meta) = meta { - CPlace::for_ptr_with_extra(addr, meta, val.layout()) - } else { - CPlace::for_ptr(addr, val.layout()) - }; + if let ArgKind::Normal(Some(ArgValue { value: val, is_underaligned_pointee: false })) = + arg_kind + && let Some((addr, meta)) = val.try_to_ptr() + { + // Ownership of the value at the backing storage for an argument is passed to the + // callee per the ABI, so it is fine to borrow the backing storage of this argument + // to prevent a copy. + + let place = if let Some(meta) = meta { + CPlace::for_ptr_with_extra(addr, meta, val.layout()) + } else { + CPlace::for_ptr(addr, val.layout()) + }; - self::comments::add_local_place_comments(fx, place, local); + self::comments::add_local_place_comments(fx, place, local); - assert_eq!(fx.local_map.push(place), local); - continue; - } + assert_eq!(fx.local_map.push(place), local); + continue; } let layout = fx.layout_of(ty); @@ -338,13 +349,22 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ match arg_kind { ArgKind::Normal(param) => { if let Some(param) = param { - place.write_cvalue(fx, param); + if param.is_underaligned_pointee { + place.write_cvalue_transmute(fx, param.value); + } else { + place.write_cvalue(fx, param.value); + } } } ArgKind::Spread(params) => { for (i, param) in params.into_iter().enumerate() { if let Some(param) = param { - place.place_field(fx, FieldIdx::new(i)).write_cvalue(fx, param); + let field_place = place.place_field(fx, FieldIdx::new(i)); + if param.is_underaligned_pointee { + field_place.write_cvalue_transmute(fx, param.value); + } else { + field_place.write_cvalue(fx, param.value); + } } } } diff --git a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs index edbee60471830..612f89e6a4217 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs @@ -7,6 +7,7 @@ use rustc_target::callconv::{ }; use smallvec::{SmallVec, smallvec}; +use super::ArgValue; use crate::prelude::*; use crate::value_and_place::assert_assignable; @@ -285,7 +286,7 @@ pub(super) fn cvalue_for_param<'tcx>( local_field: Option, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, block_params_iter: &mut impl Iterator, -) -> Option> { +) -> Option> { let block_params = arg_abi .get_abi_param(fx.tcx) .into_iter() @@ -306,30 +307,42 @@ pub(super) fn cvalue_for_param<'tcx>( arg_abi.layout, ); - match arg_abi.mode { - PassMode::Ignore => None, + let value = match arg_abi.mode { + PassMode::Ignore => return None, PassMode::Direct(_) => { assert_eq!(block_params.len(), 1, "{:?}", block_params); - Some(CValue::by_val(block_params[0], arg_abi.layout)) + CValue::by_val(block_params[0], arg_abi.layout) } PassMode::Pair(_, _) => { assert_eq!(block_params.len(), 2, "{:?}", block_params); - Some(CValue::by_val_pair(block_params[0], block_params[1], arg_abi.layout)) + CValue::by_val_pair(block_params[0], block_params[1], arg_abi.layout) } PassMode::Cast { ref cast, .. } => { - Some(from_casted_value(fx, &block_params, arg_abi.layout, cast)) + from_casted_value(fx, &block_params, arg_abi.layout, cast) } - PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => { + PassMode::Indirect { attrs, meta_attrs: None, on_stack: _ } => { assert_eq!(block_params.len(), 1, "{:?}", block_params); - Some(CValue::by_ref(Pointer::new(block_params[0]), arg_abi.layout)) + if let Some(pointee_align) = attrs.pointee_align + && pointee_align < arg_abi.layout.align.abi + && arg_abi.layout.is_sized() + && arg_abi.layout.size != Size::ZERO + { + // Underaligned pointer: treat as `[u8; size]` and transmute-copy into the real type. + let bytes_ty = Ty::new_array(fx.tcx, fx.tcx.types.u8, arg_abi.layout.size.bytes()); + let bytes_layout = fx.layout_of(bytes_ty); + return Some(ArgValue { + value: CValue::by_ref(Pointer::new(block_params[0]), bytes_layout), + is_underaligned_pointee: true, + }); + } else { + CValue::by_ref(Pointer::new(block_params[0]), arg_abi.layout) + } } PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { assert_eq!(block_params.len(), 2, "{:?}", block_params); - Some(CValue::by_ref_unsized( - Pointer::new(block_params[0]), - block_params[1], - arg_abi.layout, - )) + CValue::by_ref_unsized(Pointer::new(block_params[0]), block_params[1], arg_abi.layout) } - } + }; + + Some(ArgValue { value, is_underaligned_pointee: false }) } diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs index 4a9b0c0952ff3..3c18748ee24b4 100644 --- a/compiler/rustc_codegen_cranelift/src/allocator.rs +++ b/compiler/rustc_codegen_cranelift/src/allocator.rs @@ -5,20 +5,11 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use rustc_ast::expand::allocator::{ AllocatorMethod, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, default_fn_name, global_fn_name, }; -use rustc_codegen_ssa::base::{allocator_kind_for_codegen, allocator_shim_contents}; use rustc_symbol_mangling::mangle_internal_symbol; use crate::prelude::*; -/// Returns whether an allocator shim was created -pub(crate) fn codegen(tcx: TyCtxt<'_>, module: &mut dyn Module) -> bool { - let Some(kind) = allocator_kind_for_codegen(tcx) else { return false }; - let methods = allocator_shim_contents(tcx, kind); - codegen_inner(tcx, module, &methods); - true -} - -fn codegen_inner(tcx: TyCtxt<'_>, module: &mut dyn Module, methods: &[AllocatorMethod]) { +pub(crate) fn codegen(tcx: TyCtxt<'_>, module: &mut dyn Module, methods: &[AllocatorMethod]) { let usize_ty = module.target_config().pointer_type(); for method in methods { diff --git a/compiler/rustc_codegen_cranelift/src/analyze.rs b/compiler/rustc_codegen_cranelift/src/analyze.rs index 72380f50385a1..c4a31cabcf385 100644 --- a/compiler/rustc_codegen_cranelift/src/analyze.rs +++ b/compiler/rustc_codegen_cranelift/src/analyze.rs @@ -23,14 +23,10 @@ pub(crate) fn analyze(fx: &FunctionCx<'_, '_, '_>) -> IndexVec { for bb in fx.mir.basic_blocks.iter() { for stmt in bb.statements.iter() { - match &stmt.kind { - Assign(place_and_rval) => match &place_and_rval.1 { - Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { - flag_map[place.local] = SsaKind::NotSsa; - } - _ => {} - }, - _ => {} + if let Assign(place_and_rval) = &stmt.kind + && let Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) = &place_and_rval.1 + { + flag_map[place.local] = SsaKind::NotSsa; } } } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 2edbdb560f52b..467eceea221c8 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -147,7 +147,7 @@ pub(crate) fn codegen_fn<'tcx>( } pub(crate) fn compile_fn( - profiler: &SelfProfilerRef, + prof: &SelfProfilerRef, output_filenames: &OutputFilenames, should_write_ir: bool, cached_context: &mut Context, @@ -156,8 +156,7 @@ pub(crate) fn compile_fn( global_asm: &mut String, codegened_func: CodegenedFunction, ) { - let _timer = - profiler.generic_activity_with_arg("compile function", &*codegened_func.symbol_name); + let _timer = prof.generic_activity_with_arg("compile function", &*codegened_func.symbol_name); let clif_comments = codegened_func.clif_comments; global_asm.push_str(&codegened_func.inline_asm); @@ -196,7 +195,7 @@ pub(crate) fn compile_fn( }; // Define function - profiler.generic_activity("define function").run(|| { + prof.generic_activity("define function").run(|| { context.want_disasm = should_write_ir; match module.define_function(codegened_func.func_id, context) { Ok(()) => {} @@ -248,7 +247,7 @@ pub(crate) fn compile_fn( } // Define debuginfo for function - profiler.generic_activity("generate debug info").run(|| { + prof.generic_activity("generate debug info").run(|| { if let Some(debug_context) = debug_context { codegened_func.func_debug_cx.unwrap().finalize( debug_context, @@ -1052,7 +1051,7 @@ pub(crate) fn codegen_operand<'tcx>( Operand::RuntimeChecks(checks) => { let val = checks.value(fx.tcx.sess); let layout = fx.layout_of(fx.tcx.types.bool); - return CValue::const_val(fx, layout, val.into()); + CValue::const_val(fx, layout, val.into()) } } } diff --git a/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs b/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs deleted file mode 100644 index b5a81fc11d57b..0000000000000 --- a/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs +++ /dev/null @@ -1,205 +0,0 @@ -use std::sync::{Arc, Condvar, Mutex}; - -use rustc_data_structures::jobserver::{self, HelperThread}; -use rustc_errors::DiagCtxtHandle; - -// FIXME don't panic when a worker thread panics - -pub(super) struct ConcurrencyLimiter { - helper_thread: Option>, - state: Arc>, - available_token_condvar: Arc, - finished: bool, -} - -impl ConcurrencyLimiter { - pub(super) fn new(pending_jobs: usize) -> Self { - let state = Arc::new(Mutex::new(state::ConcurrencyLimiterState::new(pending_jobs))); - let available_token_condvar = Arc::new(Condvar::new()); - - let state_helper = state.clone(); - let available_token_condvar_helper = available_token_condvar.clone(); - let helper_thread = jobserver::client() - .clone() - .into_helper_thread(move |token| { - let mut state = state_helper.lock().unwrap(); - match token { - Ok(token) => { - state.add_new_token(token); - available_token_condvar_helper.notify_one(); - } - Err(err) => { - state.poison(format!("failed to acquire jobserver token: {}", err)); - // Notify all threads waiting for a token to give them a chance to - // gracefully exit. - available_token_condvar_helper.notify_all(); - } - } - }) - .unwrap(); - ConcurrencyLimiter { - helper_thread: Some(Mutex::new(helper_thread)), - state, - available_token_condvar, - finished: false, - } - } - - pub(super) fn acquire(&self, dcx: DiagCtxtHandle<'_>) -> ConcurrencyLimiterToken { - let mut state = self.state.lock().unwrap(); - loop { - state.assert_invariants(); - - match state.try_start_job() { - Ok(true) => { - return ConcurrencyLimiterToken { - state: self.state.clone(), - available_token_condvar: self.available_token_condvar.clone(), - }; - } - Ok(false) => {} - Err(err) => { - // An error happened when acquiring the token. Raise it as fatal error. - // Make sure to drop the mutex guard first to prevent poisoning the mutex. - drop(state); - if let Some(err) = err { - dcx.fatal(err); - } else { - // The error was already emitted, but compilation continued. Raise a silent - // fatal error. - rustc_errors::FatalError.raise(); - } - } - } - - self.helper_thread.as_ref().unwrap().lock().unwrap().request_token(); - state = self.available_token_condvar.wait(state).unwrap(); - } - } - - pub(crate) fn finished(mut self) { - self.helper_thread.take(); - - // Assert that all jobs have finished - let state = Mutex::get_mut(Arc::get_mut(&mut self.state).unwrap()).unwrap(); - state.assert_done(); - - self.finished = true; - } -} - -impl Drop for ConcurrencyLimiter { - fn drop(&mut self) { - if !self.finished && !std::thread::panicking() { - panic!("Forgot to call finished() on ConcurrencyLimiter"); - } - } -} - -#[derive(Debug)] -pub(super) struct ConcurrencyLimiterToken { - state: Arc>, - available_token_condvar: Arc, -} - -impl Drop for ConcurrencyLimiterToken { - fn drop(&mut self) { - let mut state = self.state.lock().unwrap(); - state.job_finished(); - self.available_token_condvar.notify_one(); - } -} - -mod state { - use rustc_data_structures::jobserver::Acquired; - - #[derive(Debug)] - pub(super) struct ConcurrencyLimiterState { - pending_jobs: usize, - active_jobs: usize, - - poisoned: bool, - stored_error: Option, - - // None is used to represent the implicit token, Some to represent explicit tokens - tokens: Vec>, - } - - impl ConcurrencyLimiterState { - pub(super) fn new(pending_jobs: usize) -> Self { - ConcurrencyLimiterState { - pending_jobs, - active_jobs: 0, - poisoned: false, - stored_error: None, - tokens: vec![None], - } - } - - pub(super) fn assert_invariants(&self) { - // There must be no excess active jobs - assert!(self.active_jobs <= self.pending_jobs); - - // There may not be more active jobs than there are tokens - assert!(self.active_jobs <= self.tokens.len()); - } - - pub(super) fn assert_done(&self) { - assert_eq!(self.pending_jobs, 0); - assert_eq!(self.active_jobs, 0); - } - - pub(super) fn add_new_token(&mut self, token: Acquired) { - self.tokens.push(Some(token)); - self.drop_excess_capacity(); - } - - pub(super) fn try_start_job(&mut self) -> Result> { - if self.poisoned { - return Err(self.stored_error.take()); - } - - if self.active_jobs < self.tokens.len() { - // Using existing token - self.job_started(); - return Ok(true); - } - - Ok(false) - } - - pub(super) fn job_started(&mut self) { - self.assert_invariants(); - self.active_jobs += 1; - self.drop_excess_capacity(); - self.assert_invariants(); - } - - pub(super) fn job_finished(&mut self) { - self.assert_invariants(); - self.pending_jobs -= 1; - self.active_jobs -= 1; - self.assert_invariants(); - self.drop_excess_capacity(); - self.assert_invariants(); - } - - pub(super) fn poison(&mut self, error: String) { - self.poisoned = true; - self.stored_error = Some(error); - } - - fn drop_excess_capacity(&mut self) { - self.assert_invariants(); - - // Drop all tokens that can never be used anymore - self.tokens.truncate(std::cmp::max(self.pending_jobs, 1)); - - // Keep some excess tokens to satisfy requests faster - const MAX_EXTRA_CAPACITY: usize = 2; - self.tokens.truncate(std::cmp::max(self.active_jobs + MAX_EXTRA_CAPACITY, 1)); - - self.assert_invariants(); - } - } -} diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index f85d21db11fb2..c986666f9c46e 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -455,7 +455,8 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant } else { ("", section_name.as_str()) }; - data.set_segment_section(segment_name, section_name); + // FIXME pass correct section flags on Mach-O + data.set_segment_section(segment_name, section_name, 0); } let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()).to_vec(); diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs index 1ce424332db20..4b0260a8abc74 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs @@ -204,7 +204,7 @@ impl UnwindContext { let mut data = DataDescription::new(); data.define(gcc_except_table.writer.into_vec().into_boxed_slice()); - data.set_segment_section("", ".gcc_except_table"); + data.set_segment_section("", ".gcc_except_table", 0); for reloc in &gcc_except_table.relocs { match reloc.name { diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 323fec06bcc58..fcaf80d968a49 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -1,184 +1,103 @@ //! The AOT driver uses [`cranelift_object`] to write object files suitable for linking into a //! standalone executable. -use std::env; +use std::convert::Infallible; use std::fs::File; use std::io::BufWriter; use std::path::PathBuf; use std::sync::Arc; -use std::thread::JoinHandle; +use std::time::Instant; use cranelift_object::{ObjectBuilder, ObjectModule}; -use rustc_codegen_ssa::assert_module_sources::CguReuse; -use rustc_codegen_ssa::back::write::produce_final_output_artifacts; -use rustc_codegen_ssa::base::determine_cgu_reuse; -use rustc_codegen_ssa::{CompiledModule, CompiledModules, ModuleKind}; +use rustc_ast::expand::allocator::AllocatorMethod; +use rustc_codegen_ssa::back::lto::ThinModule; +use rustc_codegen_ssa::back::write::{ + CodegenContext, FatLtoInput, ModuleConfig, SharedEmitter, TargetMachineFactoryFn, ThinLtoInput, +}; +use rustc_codegen_ssa::traits::{ExtraBackendMethods, WriteBackendMethods}; +use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind}; use rustc_data_structures::profiling::SelfProfilerRef; -use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher}; -use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; +use rustc_errors::DiagCtxt; use rustc_hir::attrs::Linkage as RLinkage; -use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; +use rustc_middle::dep_graph::WorkProduct; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc_middle::mono::{CodegenUnit, MonoItem, MonoItemData, Visibility}; +use rustc_middle::mono::{MonoItem, MonoItemData, Visibility}; use rustc_session::Session; -use rustc_session::config::{OutputFilenames, OutputType}; +use rustc_session::config::{OptLevel, OutputFilenames, OutputType}; +use rustc_span::Symbol; use crate::base::CodegenedFunction; -use crate::concurrency_limiter::{ConcurrencyLimiter, ConcurrencyLimiterToken}; use crate::debuginfo::TypeDebugContext; use crate::global_asm::{GlobalAsmConfig, GlobalAsmContext}; use crate::prelude::*; use crate::unwind_module::UnwindModule; -fn disable_incr_cache() -> bool { - env::var("CG_CLIF_DISABLE_INCR_CACHE").as_deref() == Ok("1") -} - -struct ModuleCodegenResult { - module: CompiledModule, - existing_work_product: Option<(WorkProductId, WorkProduct)>, -} - -enum OngoingModuleCodegen { - Sync(Result), - Async(JoinHandle>), -} - -impl StableHash for OngoingModuleCodegen { - fn stable_hash(&self, _: &mut Hcx, _: &mut StableHasher) { - // do nothing - } -} - -pub(crate) struct OngoingCodegen { - modules: Vec, - allocator_module: Option, - concurrency_limiter: ConcurrencyLimiter, -} - -impl OngoingCodegen { - pub(crate) fn join( - self, - sess: &Session, - outputs: &OutputFilenames, - ) -> (CompiledModules, FxIndexMap) { - let mut work_products = FxIndexMap::default(); - let mut modules = vec![]; - let disable_incr_cache = disable_incr_cache(); - - for module_codegen in self.modules { - let module_codegen_result = match module_codegen { - OngoingModuleCodegen::Sync(module_codegen_result) => module_codegen_result, - OngoingModuleCodegen::Async(join_handle) => match join_handle.join() { - Ok(module_codegen_result) => module_codegen_result, - Err(panic) => std::panic::resume_unwind(panic), - }, - }; - - let module_codegen_result = match module_codegen_result { - Ok(module_codegen_result) => module_codegen_result, - Err(err) => sess.dcx().fatal(err), - }; - let ModuleCodegenResult { module, existing_work_product } = module_codegen_result; - - if let Some((work_product_id, work_product)) = existing_work_product { - work_products.insert(work_product_id, work_product); - } else { - let work_product = if disable_incr_cache { - None - } else if let Some(global_asm_object) = &module.global_asm_object { - rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( - sess, - &module.name, - &[("o", module.object.as_ref().unwrap()), ("asm.o", global_asm_object)], - &[], - ) - } else { - rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( - sess, - &module.name, - &[("o", module.object.as_ref().unwrap())], - &[], - ) - }; - if let Some((work_product_id, work_product)) = work_product { - work_products.insert(work_product_id, work_product); - } - } - - modules.push(module); - } - - self.concurrency_limiter.finished(); - - sess.dcx().abort_if_errors(); - - let compiled_modules = CompiledModules { modules, allocator_module: self.allocator_module }; - - produce_final_output_artifacts(sess, &compiled_modules, outputs); - - (compiled_modules, work_products) - } +pub(crate) struct AotModule { + producer: String, + global_asm_config: GlobalAsmConfig, + module: UnwindModule, + debug_context: Option, + codegened_functions: Vec, + global_asm: String, } -fn make_module(sess: &Session, name: String) -> UnwindModule { - let isa = crate::build_isa(sess, false); +fn make_module(tcx: TyCtxt<'_>, cgu_name: &str) -> AotModule { + let isa = crate::build_isa(tcx.sess, false); - let mut builder = - ObjectBuilder::new(isa, name + ".o", cranelift_module::default_libcall_names()).unwrap(); + let mut builder = ObjectBuilder::new( + isa, + cgu_name.to_owned() + ".o", + cranelift_module::default_libcall_names(), + ) + .unwrap(); // Disable function sections by default on MSVC as it causes significant slowdowns with link.exe. // Maybe link.exe has exponential behavior when there are many sections with the same name? Also // explicitly disable it on MinGW as rustc already disables it by default on MinGW and as such // isn't tested. If rustc enables it in the future on MinGW, we can re-enable it too once it has // been on MinGW. - let default_function_sections = sess.target.function_sections && !sess.target.is_like_windows; + let default_function_sections = + tcx.sess.target.function_sections && !tcx.sess.target.is_like_windows; builder.per_function_section( - sess.opts.unstable_opts.function_sections.unwrap_or(default_function_sections), + tcx.sess.opts.unstable_opts.function_sections.unwrap_or(default_function_sections), ); - UnwindModule::new(ObjectModule::new(builder), true) -} + let module = UnwindModule::new(ObjectModule::new(builder), true); -fn emit_cgu( - output_filenames: &OutputFilenames, - prof: &SelfProfilerRef, - name: String, - module: UnwindModule, - debug: Option, - global_asm_object_file: Option, - producer: &str, -) -> Result { - let mut product = module.finish(); - - if let Some(mut debug) = debug { - debug.emit(&mut product); - } + let producer = crate::debuginfo::producer(tcx.sess); + let global_asm_config = GlobalAsmConfig::new(tcx.sess); + let debug_context = DebugContext::new(tcx, module.isa(), false, cgu_name); + let codegened_functions = vec![]; + let global_asm = String::new(); - let module = emit_module( - output_filenames, - prof, - product.object, - ModuleKind::Regular, - name.clone(), - global_asm_object_file, + AotModule { producer, - )?; - - Ok(ModuleCodegenResult { module, existing_work_product: None }) + global_asm_config, + module, + debug_context, + codegened_functions, + global_asm, + } } fn emit_module( output_filenames: &OutputFilenames, prof: &SelfProfilerRef, - mut object: cranelift_object::object::write::Object<'_>, + module: UnwindModule, + debug: Option, kind: ModuleKind, name: String, global_asm_object: Option, producer_str: &str, ) -> Result { - if object.format() == cranelift_object::object::BinaryFormat::Elf { - let comment_section = object.add_section( + let mut product = module.finish(); + + if let Some(mut debug) = debug { + debug.emit(&mut product); + } + + if product.object.format() == cranelift_object::object::BinaryFormat::Elf { + let comment_section = product.object.add_section( Vec::new(), b".comment".to_vec(), cranelift_object::object::SectionKind::OtherString, @@ -186,7 +105,7 @@ fn emit_module( let mut producer = vec![0]; producer.extend(producer_str.as_bytes()); producer.push(0); - object.set_section_data(comment_section, producer, 1); + product.object.set_section_data(comment_section, producer, 1); } let tmp_file = output_filenames.temp_path_for_cgu(OutputType::Object, &name); @@ -196,7 +115,7 @@ fn emit_module( }; let mut file = BufWriter::new(file); - if let Err(err) = object.write_stream(&mut file) { + if let Err(err) = product.object.write_stream(&mut file) { return Err(format!("error writing object file: {}", err)); } let file = match file.into_inner() { @@ -225,87 +144,22 @@ fn emit_module( }) } -fn reuse_workproduct_for_cgu( - tcx: TyCtxt<'_>, - cgu: &CodegenUnit<'_>, -) -> Result { - let work_product = cgu.previous_work_product(tcx); - let obj_out_regular = - tcx.output_filenames(()).temp_path_for_cgu(OutputType::Object, cgu.name().as_str()); - let source_file_regular = rustc_incremental::in_incr_comp_dir_sess( - tcx.sess, - work_product.saved_files.get("o").expect("no saved object file in work product"), - ); - - if let Err(err) = rustc_fs_util::link_or_copy(&source_file_regular, &obj_out_regular) { - return Err(format!( - "unable to copy {} to {}: {}", - source_file_regular.display(), - obj_out_regular.display(), - err - )); - } - - let obj_out_global_asm = - tcx.output_filenames(()).temp_path_ext_for_cgu("asm.o", cgu.name().as_str()); - let source_file_global_asm = if let Some(asm_o) = work_product.saved_files.get("asm.o") { - let source_file_global_asm = rustc_incremental::in_incr_comp_dir_sess(tcx.sess, asm_o); - if let Err(err) = rustc_fs_util::link_or_copy(&source_file_global_asm, &obj_out_global_asm) - { - return Err(format!( - "unable to copy {} to {}: {}", - source_file_global_asm.display(), - obj_out_global_asm.display(), - err - )); - } - Some(source_file_global_asm) - } else { - None - }; - - Ok(ModuleCodegenResult { - module: CompiledModule { - name: cgu.name().to_string(), - kind: ModuleKind::Regular, - object: Some(obj_out_regular), - global_asm_object: source_file_global_asm.as_ref().map(|_| obj_out_global_asm), - dwarf_object: None, - bytecode: None, - assembly: None, - llvm_ir: None, - links_from_incr_cache: if let Some(source_file_global_asm) = source_file_global_asm { - vec![source_file_regular, source_file_global_asm] - } else { - vec![source_file_regular] - }, - }, - existing_work_product: Some((cgu.work_product_id(), work_product)), - }) -} - -fn codegen_cgu_content( - tcx: TyCtxt<'_>, - module: &mut dyn Module, - cgu_name: rustc_span::Symbol, -) -> (Option, Vec, String) { +fn codegen_cgu(tcx: TyCtxt<'_>, cgu_name: Symbol) -> AotModule { let _timer = tcx.prof.generic_activity_with_arg("codegen cgu", cgu_name.as_str()); let cgu = tcx.codegen_unit(cgu_name); let mono_items = cgu.items_in_deterministic_order(tcx); - let mut debug_context = DebugContext::new(tcx, module.isa(), false, cgu_name.as_str()); - let mut global_asm = String::new(); + let mut module = make_module(tcx, cgu_name.as_str()); let mut type_dbg = TypeDebugContext::default(); - super::predefine_mono_items(tcx, module, &mono_items); - let mut codegened_functions = vec![]; + super::predefine_mono_items(tcx, &mut module.module, &mono_items); for (mono_item, item_data) in mono_items { match mono_item { MonoItem::Fn(instance) => { 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 global_asm }, + &mut GlobalAsmContext { tcx, global_asm: &mut module.global_asm }, instance, MonoItemData { linkage: RLinkage::External, @@ -321,186 +175,219 @@ fn codegen_cgu_content( } let codegened_function = crate::base::codegen_fn( tcx, - cgu_name, - debug_context.as_mut(), + cgu.name(), + module.debug_context.as_mut(), &mut type_dbg, Function::new(), - module, + &mut module.module, instance, ); - codegened_functions.push(codegened_function); + module.codegened_functions.push(codegened_function); } MonoItem::Static(def_id) => { - let data_id = crate::constant::codegen_static(tcx, module, def_id); - if let Some(debug_context) = debug_context.as_mut() { + let data_id = crate::constant::codegen_static(tcx, &mut module.module, def_id); + if let Some(debug_context) = module.debug_context.as_mut() { debug_context.define_static(tcx, &mut type_dbg, def_id, data_id); } } MonoItem::GlobalAsm(item_id) => { rustc_codegen_ssa::base::codegen_global_asm( - &mut GlobalAsmContext { tcx, global_asm: &mut global_asm }, + &mut GlobalAsmContext { tcx, global_asm: &mut module.global_asm }, item_id, ); } } } - crate::main_shim::maybe_create_entry_wrapper(tcx, module, false, cgu.is_primary()); + crate::main_shim::maybe_create_entry_wrapper(tcx, &mut module.module, false, cgu.is_primary()); - (debug_context, codegened_functions, global_asm) + module } -fn module_codegen( - tcx: TyCtxt<'_>, - global_asm_config: Arc, - cgu_name: rustc_span::Symbol, - token: ConcurrencyLimiterToken, -) -> OngoingModuleCodegen { - let mut module = make_module(tcx.sess, cgu_name.as_str().to_string()); +fn compile_cgu( + prof: &SelfProfilerRef, + output_filenames: &OutputFilenames, + should_write_ir: bool, + mut aot_module: AotModule, + cgu_name: String, + kind: ModuleKind, +) -> Result { + prof.generic_activity_with_arg("compile functions", &*cgu_name).run(|| { + cranelift_codegen::timing::set_thread_profiler(Box::new(super::MeasuremeProfiler( + prof.clone(), + ))); + + let mut cached_context = Context::new(); + for codegened_func in aot_module.codegened_functions { + crate::base::compile_fn( + &prof, + &output_filenames, + should_write_ir, + &mut cached_context, + &mut aot_module.module, + aot_module.debug_context.as_mut(), + &mut aot_module.global_asm, + codegened_func, + ); + } + }); - let (mut debug_context, codegened_functions, mut global_asm) = - codegen_cgu_content(tcx, &mut module, cgu_name); + let global_asm_object_file = + prof.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| { + if aot_module.global_asm.is_empty() { + return Ok::<_, String>(None); + } - let cgu_name = cgu_name.as_str().to_owned(); + let global_asm_object_file = output_filenames.temp_path_ext_for_cgu("asm.o", &cgu_name); + crate::global_asm::compile_global_asm( + &aot_module.global_asm_config, + aot_module.global_asm, + &global_asm_object_file, + )?; + + Ok(Some(global_asm_object_file)) + })?; + + prof.generic_activity_with_arg("write object file", &*cgu_name).run(|| { + emit_module( + output_filenames, + prof, + aot_module.module, + aot_module.debug_context, + kind, + cgu_name.clone(), + global_asm_object_file, + &aot_module.producer, + ) + }) +} - let producer = crate::debuginfo::producer(tcx.sess); +#[derive(Copy, Clone)] +pub(crate) struct AotDriver; + +impl ExtraBackendMethods for AotDriver { + type Module = AotModule; + + fn codegen_allocator<'tcx>( + &self, + tcx: TyCtxt<'tcx>, + module_name: &str, + methods: &[AllocatorMethod], + ) -> Self::Module { + let mut allocator_module = make_module(tcx, module_name); + crate::allocator::codegen(tcx, &mut allocator_module.module, methods); + allocator_module + } - let profiler = tcx.prof.clone(); - let output_filenames = tcx.output_filenames(()).clone(); - let should_write_ir = crate::pretty_clif::should_write_ir(tcx.sess); - - OngoingModuleCodegen::Async(std::thread::spawn(move || { - profiler.clone().generic_activity_with_arg("compile functions", &*cgu_name).run(|| { - cranelift_codegen::timing::set_thread_profiler(Box::new(super::MeasuremeProfiler( - profiler.clone(), - ))); - - let mut cached_context = Context::new(); - for codegened_func in codegened_functions { - crate::base::compile_fn( - &profiler, - &output_filenames, - should_write_ir, - &mut cached_context, - &mut module, - debug_context.as_mut(), - &mut global_asm, - codegened_func, - ); - } - }); - - let global_asm_object_file = - profiler.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| { - crate::global_asm::compile_global_asm(&global_asm_config, &cgu_name, global_asm) - })?; - - let codegen_result = - profiler.generic_activity_with_arg("write object file", &*cgu_name).run(|| { - emit_cgu( - &global_asm_config.output_filenames, - &profiler, - cgu_name, - module, - debug_context, - global_asm_object_file, - &producer, - ) - }); - std::mem::drop(token); - codegen_result - })) -} + fn compile_codegen_unit( + &self, + tcx: TyCtxt<'_>, + cgu_name: Symbol, + ) -> (ModuleCodegen, u64) { + let start_time = Instant::now(); + + let dep_node = tcx.codegen_unit(cgu_name).codegen_dep_node(tcx); + let (module, _) = tcx.dep_graph.with_task( + dep_node, + tcx, + || { + let aot_module = codegen_cgu(tcx, cgu_name); + ModuleCodegen::new_regular(cgu_name.as_str().to_owned(), aot_module) + }, + Some(rustc_middle::dep_graph::hash_result), + ); -fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option { - let mut allocator_module = make_module(tcx.sess, "allocator_shim".to_string()); - let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module); - - if created_alloc_shim { - let product = allocator_module.finish(); - - match emit_module( - tcx.output_filenames(()), - &tcx.sess.prof, - product.object, - ModuleKind::Allocator, - "allocator_shim".to_owned(), - None, - &crate::debuginfo::producer(tcx.sess), - ) { - Ok(allocator_module) => Some(allocator_module), - Err(err) => tcx.dcx().fatal(err), - } - } else { - None + let time_to_codegen = start_time.elapsed(); + + // We assume that the cost to run LLVM on a CGU is proportional to + // the time we needed for codegenning it. + let cost = time_to_codegen.as_nanos() as u64; + + (module, cost) } } -pub(crate) fn run_aot(tcx: TyCtxt<'_>) -> Box { - let cgus = tcx.collect_and_partition_mono_items(()).codegen_units; +impl WriteBackendMethods for AotDriver { + type Module = AotModule; - if tcx.dep_graph.is_fully_enabled() { - for cgu in cgus { - tcx.ensure_ok().codegen_unit(cgu.name()); - } + type TargetMachine = (); + + type ModuleBuffer = Infallible; + + type ThinData = Infallible; + + fn target_machine_factory( + &self, + _sess: &Session, + _opt_level: OptLevel, + _target_features: &[String], + ) -> TargetMachineFactoryFn { + Arc::new(|_, _| ()) } - // Calculate the CGU reuse - let cgu_reuse = tcx.sess.time("find_cgu_reuse", || { - cgus.iter().map(|cgu| determine_cgu_reuse(tcx, cgu)).collect::>() - }); + fn optimize_and_codegen_fat_lto( + _sess: &Session, + _cgcx: &CodegenContext, + _shared_emitter: &SharedEmitter, + _tm_factory: TargetMachineFactoryFn, + _exported_symbols_for_lto: &[String], + _each_linked_rlib_for_lto: &[PathBuf], + _modules: Vec>, + ) -> CompiledModule { + unreachable!() + } - rustc_codegen_ssa::assert_module_sources::assert_module_sources(tcx, &|cgu_reuse_tracker| { - for (i, cgu) in cgus.iter().enumerate() { - let cgu_reuse = cgu_reuse[i]; - cgu_reuse_tracker.set_actual_reuse(cgu.name().as_str(), cgu_reuse); - } - }); + fn run_thin_lto( + _cgcx: &CodegenContext, + _prof: &SelfProfilerRef, + _dcx: rustc_errors::DiagCtxtHandle<'_>, + _exported_symbols_for_lto: &[String], + _each_linked_rlib_for_lto: &[PathBuf], + _modules: Vec>, + ) -> (Vec>, Vec) { + unreachable!() + } - let global_asm_config = Arc::new(crate::global_asm::GlobalAsmConfig::new(tcx)); + fn optimize( + _cgcx: &CodegenContext, + _prof: &SelfProfilerRef, + _shared_emitter: &SharedEmitter, + _module: &mut ModuleCodegen, + _config: &ModuleConfig, + ) { + } - let disable_incr_cache = disable_incr_cache(); - let (todo_cgus, done_cgus) = - cgus.iter().enumerate().partition::, _>(|&(i, _)| match cgu_reuse[i] { - _ if disable_incr_cache => true, - CguReuse::No => true, - CguReuse::PreLto | CguReuse::PostLto => false, - }); + fn optimize_and_codegen_thin( + _cgcx: &CodegenContext, + _prof: &SelfProfilerRef, + _shared_emitter: &SharedEmitter, + _tm_factory: TargetMachineFactoryFn, + _thin: ThinModule, + ) -> CompiledModule { + unreachable!() + } - let concurrency_limiter = IntoDynSyncSend(ConcurrencyLimiter::new(todo_cgus.len())); + fn codegen( + cgcx: &CodegenContext, + prof: &SelfProfilerRef, + shared_emitter: &SharedEmitter, + module: ModuleCodegen, + config: &ModuleConfig, + ) -> CompiledModule { + compile_cgu( + prof, + &cgcx.output_filenames, + config.emit_ir, + module.module_llvm, + module.name, + module.kind, + ) + .unwrap_or_else(|err| { + let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); + dcx.handle().fatal(err) + }) + } - let modules: Vec<_> = - tcx.sess.time("codegen mono items", || { - let modules: Vec<_> = par_map(todo_cgus, |(_, cgu)| { - let dep_node = cgu.codegen_dep_node(tcx); - let (module, _) = tcx.dep_graph.with_task( - dep_node, - tcx, - || { - module_codegen( - tcx, - global_asm_config.clone(), - cgu.name(), - concurrency_limiter.acquire(tcx.dcx()), - ) - }, - Some(rustc_middle::dep_graph::hash_result), - ); - IntoDynSyncSend(module) - }); - modules - .into_iter() - .map(|module| module.0) - .chain(done_cgus.into_iter().map(|(_, cgu)| { - OngoingModuleCodegen::Sync(reuse_workproduct_for_cgu(tcx, cgu)) - })) - .collect() - }); - - let allocator_module = emit_allocator_module(tcx); - - Box::new(OngoingCodegen { - modules, - allocator_module, - concurrency_limiter: concurrency_limiter.0, - }) + fn serialize_module(_module: Self::Module, _is_thin: bool) -> Self::ModuleBuffer { + unreachable!() + } } diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 33b88d70d6f8d..7d6ece02e4a62 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -6,6 +6,7 @@ use std::os::raw::{c_char, c_int}; use cranelift_jit::{JITBuilder, JITModule}; use rustc_codegen_ssa::CrateInfo; +use rustc_codegen_ssa::base::{allocator_kind_for_codegen, allocator_shim_contents}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mono::MonoItem; use rustc_session::Session; @@ -28,7 +29,9 @@ fn create_jit_module( let cx = DebugContext::new(tcx, jit_module.isa(), false, "dummy_cgu_name"); - crate::allocator::codegen(tcx, &mut jit_module); + if let Some(kind) = allocator_kind_for_codegen(tcx) { + crate::allocator::codegen(tcx, &mut jit_module, &allocator_shim_contents(tcx, kind)); + } (jit_module, cx) } diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs index ee7e6732e6a77..769c008c13f75 100644 --- a/compiler/rustc_codegen_cranelift/src/global_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs @@ -2,9 +2,8 @@ //! standalone executable. use std::io::Write; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; -use std::sync::Arc; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_codegen_ssa::traits::{AsmCodegenMethods, GlobalAsmOperandRef}; @@ -12,7 +11,7 @@ use rustc_middle::ty::TyCtxt; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, HasTypingEnv, LayoutError, LayoutOfHelpers, }; -use rustc_session::config::OutputFilenames; +use rustc_session::Session; use rustc_target::asm::InlineAsmArch; use crate::prelude::*; @@ -163,32 +162,28 @@ fn codegen_global_asm_inner<'tcx>( pub(crate) struct GlobalAsmConfig { assembler: PathBuf, target: String, - pub(crate) output_filenames: Arc, } impl GlobalAsmConfig { - pub(crate) fn new(tcx: TyCtxt<'_>) -> Self { + pub(crate) fn new(sess: &Session) -> Self { GlobalAsmConfig { - assembler: crate::toolchain::get_toolchain_binary(tcx.sess, "as"), - target: match &tcx.sess.opts.target_triple { + assembler: crate::toolchain::get_toolchain_binary(sess, "as"), + target: match &sess.opts.target_triple { rustc_target::spec::TargetTuple::TargetTuple(triple) => triple.clone(), rustc_target::spec::TargetTuple::TargetJson { path_for_rustdoc, .. } => { path_for_rustdoc.to_str().unwrap().to_owned() } }, - output_filenames: tcx.output_filenames(()).clone(), } } } pub(crate) fn compile_global_asm( config: &GlobalAsmConfig, - cgu_name: &str, global_asm: String, -) -> Result, String> { - if global_asm.is_empty() { - return Ok(None); - } + global_asm_object_file: &Path, +) -> Result<(), String> { + assert!(!global_asm.is_empty()); // Remove all LLVM style comments let mut global_asm = global_asm @@ -198,8 +193,6 @@ pub(crate) fn compile_global_asm( .join("\n"); global_asm.push('\n'); - let global_asm_object_file = config.output_filenames.temp_path_ext_for_cgu("asm.o", cgu_name); - // Assemble `global_asm` if option_env!("CG_CLIF_FORCE_GNU_AS").is_some() { let mut child = Command::new(&config.assembler) @@ -266,5 +259,5 @@ pub(crate) fn compile_global_asm( } } - Ok(Some(global_asm_object_file)) + Ok(()) } diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index 8100d565b3974..f9fc7002be87b 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -424,13 +424,10 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { // Allocate stack slots for inout for (i, operand) in self.operands.iter().enumerate() { - match *operand { - CInlineAsmOperand::InOut { reg, out_place: Some(_), .. } => { - let slot = new_slot(reg.reg_class()); - slots_input[i] = Some(slot); - slots_output[i] = Some(slot); - } - _ => (), + if let CInlineAsmOperand::InOut { reg, out_place: Some(_), .. } = *operand { + let slot = new_slot(reg.reg_class()); + slots_input[i] = Some(slot); + slots_output[i] = Some(slot); } } @@ -456,11 +453,8 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { // Allocate stack slots for output for (i, operand) in self.operands.iter().enumerate() { - match *operand { - CInlineAsmOperand::Out { reg, place: Some(_), .. } => { - slots_output[i] = Some(new_slot(reg.reg_class())); - } - _ => (), + if let CInlineAsmOperand::Out { reg, place: Some(_), .. } = *operand { + slots_output[i] = Some(new_slot(reg.reg_class())); } } diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs index 3cd7ebb88f447..9da87a5774e8f 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs @@ -494,6 +494,385 @@ pub(super) fn codegen_aarch64_llvm_intrinsic_call<'tcx>( }); } */ + "llvm.aarch64.crc32b" + | "llvm.aarch64.crc32h" + | "llvm.aarch64.crc32w" + | "llvm.aarch64.crc32x" + | "llvm.aarch64.crc32cb" + | "llvm.aarch64.crc32ch" + | "llvm.aarch64.crc32cw" + | "llvm.aarch64.crc32cx" => { + // ARM ARM v8-A: CRC32{,C}{B,H,W,X}. + // Backs core::arch::aarch64::__crc32{,c}{b,h,w,d}. + intrinsic_args!(fx, args => (crc, v); intrinsic); + + let crc = crc.load_scalar(fx); + let v = v.load_scalar(fx); + + let asm = match intrinsic { + "llvm.aarch64.crc32b" => "crc32b w0, w0, w1", + "llvm.aarch64.crc32h" => "crc32h w0, w0, w1", + "llvm.aarch64.crc32w" => "crc32w w0, w0, w1", + "llvm.aarch64.crc32x" => "crc32x w0, w0, x1", + "llvm.aarch64.crc32cb" => "crc32cb w0, w0, w1", + "llvm.aarch64.crc32ch" => "crc32ch w0, w0, w1", + "llvm.aarch64.crc32cw" => "crc32cw w0, w0, w1", + "llvm.aarch64.crc32cx" => "crc32cx w0, w0, x1", + _ => unreachable!(), + }; + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String(asm.into())], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::x0, + )), + _late: true, + in_value: crc, + out_place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::x1, + )), + value: v, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + + "llvm.aarch64.crypto.aese" | "llvm.aarch64.crypto.aesd" => { + intrinsic_args!(fx, args => (a, b); intrinsic); + + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + let asm = match intrinsic { + "llvm.aarch64.crypto.aese" => "aese v0.16b, v1.16b", + "llvm.aarch64.crypto.aesd" => "aesd v0.16b, v1.16b", + _ => unreachable!(), + }; + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String(asm.into())], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v0, + )), + _late: true, + in_value: a, + out_place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v1, + )), + value: b, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + + "llvm.aarch64.crypto.aesmc" | "llvm.aarch64.crypto.aesimc" => { + intrinsic_args!(fx, args => (a); intrinsic); + + let a = a.load_scalar(fx); + + let asm = match intrinsic { + "llvm.aarch64.crypto.aesmc" => "aesmc v0.16b, v0.16b", + "llvm.aarch64.crypto.aesimc" => "aesimc v0.16b, v0.16b", + _ => unreachable!(), + }; + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String(asm.into())], + &[CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v0, + )), + _late: true, + in_value: a, + out_place: Some(ret), + }], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + + "llvm.aarch64.crypto.sha256h" | "llvm.aarch64.crypto.sha256h2" => { + intrinsic_args!(fx, args => (a, b, c); intrinsic); + + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + let c = c.load_scalar(fx); + + let asm = match intrinsic { + "llvm.aarch64.crypto.sha256h" => "sha256h q0, q1, v2.4s", + "llvm.aarch64.crypto.sha256h2" => "sha256h2 q0, q1, v2.4s", + _ => unreachable!(), + }; + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String(asm.into())], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v0, + )), + _late: true, + in_value: a, + out_place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v1, + )), + value: b, + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v2, + )), + value: c, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + + "llvm.aarch64.crypto.sha256su0" => { + intrinsic_args!(fx, args => (a, b); intrinsic); + + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String("sha256su0 v0.4s, v1.4s".into())], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v0, + )), + _late: true, + in_value: a, + out_place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v1, + )), + value: b, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + + "llvm.aarch64.crypto.sha256su1" => { + intrinsic_args!(fx, args => (a, b, c); intrinsic); + + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + let c = c.load_scalar(fx); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String("sha256su1 v0.4s, v1.4s, v2.4s".into())], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v0, + )), + _late: true, + in_value: a, + out_place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v1, + )), + value: b, + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v2, + )), + value: c, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + + "llvm.aarch64.neon.pmull64" => { + intrinsic_args!(fx, args => (a, b); intrinsic); + + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String( + "fmov d0, x0 + fmov d1, x1 + pmull v0.1q, v0.1d, v1.1d" + .into(), + )], + &[ + CInlineAsmOperand::Out { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v0, + )), + late: true, + place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::x0, + )), + value: a, + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::x1, + )), + value: b, + }, + CInlineAsmOperand::Out { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v1, + )), + late: true, + place: None, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + + "llvm.aarch64.neon.pmull.v8i16" => { + intrinsic_args!(fx, args => (a, b); intrinsic); + + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String("pmull v0.8h, v0.8b, v1.8b".into())], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v0, + )), + _late: true, + in_value: a, + out_place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::v1, + )), + value: b, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + + "llvm.aarch64.neon.sqdmulh.v2i32" + | "llvm.aarch64.neon.sqdmulh.v4i16" + | "llvm.aarch64.neon.sqdmulh.v4i32" + | "llvm.aarch64.neon.sqdmulh.v8i16" => { + // https://developer.arm.com/documentation/ddi0602/2026-03/SIMD-FP-Instructions/SQDMULH--vector---Signed-saturating-doubling-multiply-returning-high-half- + intrinsic_args!(fx, args => (a, b); intrinsic); + + // Simplify the "double and shift by esize" into "shift by esize - 1". + // https://github.com/qemu/qemu/blob/81cc5f39aa3042e9c0b2ea772b42a2c8b1488e76/target/arm/tcg/mve_helper.c#L1267-L1283 + let (result_ty, product_ty, shift, max) = match intrinsic { + "llvm.aarch64.neon.sqdmulh.v4i16" | "llvm.aarch64.neon.sqdmulh.v8i16" => { + (types::I16, types::I32, 15, i64::from(i16::MAX)) + } + "llvm.aarch64.neon.sqdmulh.v2i32" | "llvm.aarch64.neon.sqdmulh.v4i32" => { + (types::I32, types::I64, 31, i64::from(i32::MAX)) + } + _ => unreachable!(), + }; + + simd_pair_for_each_lane( + fx, + a, + b, + ret, + &|fx, _lane_ty, _res_lane_ty, a_lane, b_lane| { + let a_lane = fx.bcx.ins().sextend(product_ty, a_lane); + let b_lane = fx.bcx.ins().sextend(product_ty, b_lane); + let product = fx.bcx.ins().imul(a_lane, b_lane); + let product = fx.bcx.ins().sshr_imm(product, shift); + let max = fx.bcx.ins().iconst(product_ty, max); + let result = fx.bcx.ins().smin(product, max); + fx.bcx.ins().ireduce(result_ty, result) + }, + ); + } + + "llvm.aarch64.neon.saddlp.v1i64.v2i32" + | "llvm.aarch64.neon.saddlp.v2i32.v4i16" + | "llvm.aarch64.neon.saddlp.v2i64.v4i32" + | "llvm.aarch64.neon.saddlp.v4i16.v8i8" + | "llvm.aarch64.neon.saddlp.v4i32.v8i16" + | "llvm.aarch64.neon.saddlp.v8i16.v16i8" => { + // https://developer.arm.com/documentation/ddi0602/2026-03/SIMD-FP-Instructions/SADDLP--Signed-add-long-pairwise- + intrinsic_args!(fx, args => (a); intrinsic); + + let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx); + let ret_lane_layout = fx.layout_of(ret_lane_ty); + let wide_ty = fx.clif_type(ret_lane_ty).unwrap(); + + for lane_idx in 0..ret_lane_count { + let base = lane_idx * 2; + let a_lane0 = a.value_lane(fx, base).load_scalar(fx); + let a_lane1 = a.value_lane(fx, base + 1).load_scalar(fx); + let a_lane0 = fx.bcx.ins().sextend(wide_ty, a_lane0); + let a_lane1 = fx.bcx.ins().sextend(wide_ty, a_lane1); + let sum = fx.bcx.ins().iadd(a_lane0, a_lane1); + let res_lane = CValue::by_val(sum, ret_lane_layout); + ret.place_lane(fx, lane_idx).write_cvalue(fx, res_lane); + } + } + + "llvm.aarch64.neon.uaddlp.v1i64.v2i32" + | "llvm.aarch64.neon.uaddlp.v2i32.v4i16" + | "llvm.aarch64.neon.uaddlp.v2i64.v4i32" + | "llvm.aarch64.neon.uaddlp.v4i16.v8i8" + | "llvm.aarch64.neon.uaddlp.v4i32.v8i16" + | "llvm.aarch64.neon.uaddlp.v8i16.v16i8" => { + // https://developer.arm.com/documentation/ddi0602/2026-03/SIMD-FP-Instructions/UADDLP--Unsigned-add-long-pairwise- + intrinsic_args!(fx, args => (a); intrinsic); + + let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx); + let ret_lane_layout = fx.layout_of(ret_lane_ty); + let wide_ty = fx.clif_type(ret_lane_ty).unwrap(); + + for lane_idx in 0..ret_lane_count { + let base = lane_idx * 2; + let a_lane0 = a.value_lane(fx, base).load_scalar(fx); + let a_lane1 = a.value_lane(fx, base + 1).load_scalar(fx); + let a_lane0 = fx.bcx.ins().uextend(wide_ty, a_lane0); + let a_lane1 = fx.bcx.ins().uextend(wide_ty, a_lane1); + let sum = fx.bcx.ins().iadd(a_lane0, a_lane1); + let res_lane = CValue::by_val(sum, ret_lane_layout); + ret.place_lane(fx, lane_idx).write_cvalue(fx, res_lane); + } + } + _ => { fx.tcx.dcx().warn(format!( "unsupported AArch64 llvm intrinsic {}; replacing with trap", diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index b8e1886b2d3c1..5d7e457b8e1ba 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -371,7 +371,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( } for i in 0..lane_count { - let ret_lane = ret.place_lane(fx, i.into()); + let ret_lane = ret.place_lane(fx, i); ret_lane.write_cvalue(fx, value); } } diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 48858e20381dd..38cb986034859 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -18,9 +18,7 @@ extern crate rustc_codegen_ssa; extern crate rustc_const_eval; extern crate rustc_data_structures; extern crate rustc_errors; -extern crate rustc_fs_util; extern crate rustc_hir; -extern crate rustc_incremental; extern crate rustc_index; extern crate rustc_log; extern crate rustc_session; @@ -40,7 +38,7 @@ use std::sync::Arc; use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::settings::{self, Configurable}; use rustc_codegen_ssa::traits::CodegenBackend; -use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig}; +use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig, back}; use rustc_log::tracing::info; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_session::Session; @@ -60,7 +58,6 @@ mod codegen_f16_f128; mod codegen_i128; mod common; mod compiler_builtins; -mod concurrency_limiter; mod config; mod constant; mod debuginfo; @@ -133,7 +130,7 @@ impl CodegenBackend for CraneliftCodegenBackend { match sess.lto() { Lto::No | Lto::ThinLocal => {} Lto::Thin | Lto::Fat => { - sess.dcx().warn("LTO is not supported. You may get a linker error.") + sess.dcx().fatal("LTO is not supported by rustc_codegen_cranelift"); } } @@ -152,6 +149,10 @@ impl CodegenBackend for CraneliftCodegenBackend { } } + fn thin_lto_supported(&self) -> bool { + false + } + fn target_config(&self, sess: &Session) -> TargetConfig { // FIXME return the actually used target features. this is necessary for #[cfg(target_feature)] let target_features = match sess.target.arch { @@ -224,7 +225,7 @@ impl CodegenBackend for CraneliftCodegenBackend { #[cfg(not(feature = "jit"))] tcx.dcx().fatal("jit support was disabled when compiling rustc_codegen_cranelift"); } else { - driver::aot::run_aot(tcx) + Box::new(rustc_codegen_ssa::base::codegen_crate(driver::aot::AotDriver, tcx)) } } @@ -232,10 +233,13 @@ impl CodegenBackend for CraneliftCodegenBackend { &self, ongoing_codegen: Box, sess: &Session, - outputs: &OutputFilenames, - _crate_info: &CrateInfo, + _outputs: &OutputFilenames, + crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap) { - ongoing_codegen.downcast::().unwrap().join(sess, outputs) + ongoing_codegen + .downcast::>() + .unwrap() + .join(sess, crate_info) } fn fallback_intrinsics(&self) -> Vec { @@ -254,7 +258,9 @@ fn enable_verifier(sess: &Session) -> bool { } fn target_triple(sess: &Session) -> target_lexicon::Triple { - match sess.target.llvm_target.parse() { + // Use versioned target triple to make `OperatingSystem::MacOSX(...)` + // contain a value, which we use when emitting `LC_BUILD_VERSION`. + match back::versioned_llvm_target(sess).parse() { Ok(triple) => triple, Err(err) => sess.dcx().fatal(format!("target not recognized: {}", err)), } diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 1d9dcdab4ced9..a2a2cac3faaa8 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -686,17 +686,14 @@ impl<'tcx> CPlace<'tcx> { ) -> CPlace<'tcx> { let layout = self.layout(); - match self.inner { - CPlaceInner::VarPair(local, var1, var2) => { - let layout = layout.field(&*fx, field.index()); + if let CPlaceInner::VarPair(local, var1, var2) = self.inner { + let layout = layout.field(&*fx, field.index()); - match field.as_u32() { - 0 => return CPlace { inner: CPlaceInner::Var(local, var1), layout }, - 1 => return CPlace { inner: CPlaceInner::Var(local, var2), layout }, - _ => unreachable!("field should be 0 or 1"), - } + match field.as_u32() { + 0 => return CPlace { inner: CPlaceInner::Var(local, var1), layout }, + 1 => return CPlace { inner: CPlaceInner::Var(local, var2), layout }, + _ => unreachable!("field should be 0 or 1"), } - _ => {} } let (base, extra) = match self.inner { diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index fb243ff842c83..1b7bb8c907735 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -10,7 +10,7 @@ use rustc_middle::bug; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf; #[cfg(feature = "master")] -use rustc_session::config; +use rustc_session::{Session, config}; use rustc_target::callconv::{ArgAttributes, CastTarget, FnAbi, PassMode}; #[cfg(feature = "master")] use rustc_target::spec::Arch; @@ -230,32 +230,43 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { #[cfg(feature = "master")] fn gcc_cconv(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Option> { - conv_to_fn_attribute(self.conv, &cx.tcx.sess.target.arch) + conv_to_fn_attribute(cx.sess(), self.conv) } } #[cfg(feature = "master")] -pub fn conv_to_fn_attribute<'gcc>(conv: CanonAbi, arch: &Arch) -> Option> { +pub fn conv_to_fn_attribute<'gcc>(sess: &Session, conv: CanonAbi) -> Option> { let attribute = match conv { CanonAbi::C | CanonAbi::Rust => return None, - // gcc/gccjit does not have anything for this. - CanonAbi::RustPreserveNone => return None, + CanonAbi::RustPreserveNone => { + // This calling convention is LLVM-specific and unspecified. + sess.dcx() + .fatal("gcc/gccjit backend does not support RustPreserveNone calling convention") + } + CanonAbi::RustTail => { + // This calling convention is LLVM-specific and unspecified. + sess.dcx().fatal("gcc/gccjit backend does not support RustTail calling convention") + } CanonAbi::RustCold => FnAttribute::Cold, // Functions with this calling convention can only be called from assembly, but it is // possible to declare an `extern "custom"` block, so the backend still needs a calling // convention for declaring foreign functions. CanonAbi::Custom => return None, - // gcc/gccjit does not have anything for Swift's calling convention. - CanonAbi::Swift => panic!("gcc/gccjit backend does not support Swift calling convention"), + CanonAbi::Swift => { + // gcc/gccjit does not have anything for Swift's calling convention. + sess.dcx().fatal("gcc/gccjit backend does not support Swift calling convention") + } CanonAbi::Arm(arm_call) => match arm_call { ArmCall::CCmseNonSecureCall => FnAttribute::ArmCmseNonsecureCall, ArmCall::CCmseNonSecureEntry => FnAttribute::ArmCmseNonsecureEntry, ArmCall::Aapcs => FnAttribute::ArmPcs("aapcs"), }, - CanonAbi::GpuKernel => match arch { + CanonAbi::GpuKernel => match &sess.target.arch { &Arch::AmdGpu => FnAttribute::GcnAmdGpuHsaKernel, &Arch::Nvptx64 => FnAttribute::NvptxKernel, - arch => panic!("Arch {arch} does not support GpuKernel calling convention"), + arch => sess + .dcx() + .fatal(format!("Arch {arch} does not support GpuKernel calling convention")), }, // FIXME(antoyo): check if those AVR attributes are mapped correctly. CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind { diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index ed313859aeafa..ea71546ea1c0e 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -486,10 +486,10 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn declare_c_main(&self, fn_type: Self::Type) -> Option { let entry_name = self.sess().target.entry_name.as_ref(); if !self.functions.borrow().contains_key(entry_name) { - #[cfg(feature = "master")] - let conv = conv_to_fn_attribute(self.sess().target.entry_abi, &self.sess().target.arch); - #[cfg(not(feature = "master"))] - let conv = None; + let conv = cfg_select! { + feature = "master" => conv_to_fn_attribute(self.sess(), self.sess().target.entry_abi), + _ => None, + }; Some(self.declare_entry_fn(entry_name, fn_type, conv)) } else { // If the symbol already exists, it is an error: for example, the user wrote diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 3f6010e55928d..4d18818bbe7bd 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -723,6 +723,10 @@ pub(crate) fn to_llvm_calling_convention(sess: &Session, abi: CanonAbi) -> llvm: Arch::X86_64 | Arch::AArch64 => llvm::PreserveNone, _ => llvm::CCallConv, }, + CanonAbi::RustTail => match &sess.target.arch { + Arch::X86 | Arch::X86_64 | Arch::AArch64 => llvm::Tail, + _ => sess.dcx().fatal("extern \"tail\" is only supported on x86_64 and aarch64"), + }, // Functions with this calling convention can only be called from assembly, but it is // possible to declare an `extern "custom"` block, so the backend still needs a calling // convention for declaring foreign functions. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index be98544d89ecd..693326e198721 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -709,6 +709,8 @@ declare_features! ( (unstable, rust_cold_cc, "1.63.0", Some(97544)), /// Allows `extern "rust-preserve-none"`. (unstable, rust_preserve_none_cc, "1.95.0", Some(151401)), + /// Allows `extern "tail"`. + (unstable, rust_tail_cc, "CURRENT_RUSTC_VERSION", Some(157427)), /// Target features on s390x. (unstable, s390x_target_feature, "1.82.0", Some(150259)), /// Allows the use of the `sanitize` attribute. diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 7d687099f9715..e70936972e7c7 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -205,6 +205,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | CanonAbi::Rust | CanonAbi::RustCold | CanonAbi::RustPreserveNone + | CanonAbi::RustTail | CanonAbi::Swift | CanonAbi::Arm(_) | CanonAbi::X86(_) => {} diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index c9ec32159f476..48e1f553900b0 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -4796,7 +4796,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir::Node::Expr(rcvr) = self.tcx.hir_node(hir_id) else { return false; }; - let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, rcvr_ty.into_iter()); + // The trait may have generic parameters beyond `Self` (e.g. `Borrow`), and + // `rcvr_ty` may even be unknown. We only ever know the receiver type (the `Self` arg), + // so fill `Self` from `rcvr_ty` when available and the remaining parameters with fresh + // inference variables; building a `TraitRef` with a partial arg list would otherwise trip + // `debug_assert_args_compatible` and ICE. See #157189. + let trait_ref = ty::TraitRef::new_from_args( + self.tcx, + trait_def_id, + ty::GenericArgs::for_item(self.tcx, trait_def_id, |param, _| { + if param.index == 0 + && let Some(rcvr_ty) = rcvr_ty + { + rcvr_ty.into() + } else { + self.var_for_def(rcvr.span, param) + } + }), + ); let trait_pred = ty::Binder::dummy(ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Positive, diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 98763ff742f25..5641523c304c9 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -13,7 +13,7 @@ use std::iter; use rustc_index::{Idx, IndexVec}; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::bug; -use rustc_middle::infer::canonical::CanonicalVarKind; +use rustc_middle::infer::canonical::{CanonicalVarKind, QueryRegionConstraint}; use rustc_middle::ty::{self, BoundVar, GenericArg, GenericArgKind, Ty, TyCtxt, TypeFoldable}; use tracing::{debug, instrument}; @@ -188,7 +188,9 @@ impl<'tcx> InferCtxt<'tcx> { let InferOk { value: result_args, obligations } = self.query_response_instantiation(cause, param_env, original_values, query_response)?; - for (constraint, _category, vis) in &query_response.value.region_constraints.constraints { + for QueryRegionConstraint { constraint, visible_for_leak_check: vis, .. } in + &query_response.value.region_constraints.constraints + { let constraint = instantiate_value(self.tcx, &result_args, *constraint); match constraint { ty::RegionConstraint::Outlives(predicate) => { @@ -285,11 +287,12 @@ impl<'tcx> InferCtxt<'tcx> { (GenericArgKind::Lifetime(v_o), GenericArgKind::Lifetime(v_r)) => { if v_o != v_r { - output_query_region_constraints.constraints.push(( - ty::RegionEqPredicate(v_o, v_r).into(), - constraint_category, - ty::VisibleForLeakCheck::Yes, - )); + let constraint = QueryRegionConstraint { + constraint: ty::RegionEqPredicate(v_o, v_r).into(), + category: constraint_category, + visible_for_leak_check: ty::VisibleForLeakCheck::Yes, + }; + output_query_region_constraints.constraints.push(constraint); } } @@ -321,7 +324,7 @@ impl<'tcx> InferCtxt<'tcx> { let r_c = instantiate_value(self.tcx, &result_args, r_c); // Screen out `'a: 'a` or `'a == 'a` cases. - if r_c.0.is_trivial() { None } else { Some(r_c) } + if r_c.constraint.is_trivial() { None } else { Some(r_c) } }), ); @@ -616,7 +619,7 @@ pub fn make_query_region_constraints<'tcx>( debug!(?constraints); - let constraints: Vec<_> = constraints + let constraints: Vec> = constraints .iter() .map(|(c, origin)| match c.kind { ConstraintKind::VarSubVar @@ -625,22 +628,30 @@ pub fn make_query_region_constraints<'tcx>( | ConstraintKind::RegSubReg => { // Swap regions because we are going from sub (<=) to outlives (>=). let constraint = ty::OutlivesPredicate(c.sup.into(), c.sub).into(); - (constraint, origin.to_constraint_category(), c.visible_for_leak_check) + QueryRegionConstraint { + constraint, + category: origin.to_constraint_category(), + visible_for_leak_check: c.visible_for_leak_check, + } } ConstraintKind::VarEqVar | ConstraintKind::VarEqReg | ConstraintKind::RegEqReg => { let constraint = ty::RegionEqPredicate(c.sup, c.sub).into(); - (constraint, origin.to_constraint_category(), c.visible_for_leak_check) + QueryRegionConstraint { + constraint, + category: origin.to_constraint_category(), + visible_for_leak_check: c.visible_for_leak_check, + } } }) .chain(outlives_obligations.into_iter().map( |TypeOutlivesConstraint { sub_region, sup_type, origin }| { - ( - ty::OutlivesPredicate(sup_type.into(), sub_region).into(), - origin.to_constraint_category(), + QueryRegionConstraint { + constraint: ty::OutlivesPredicate(sup_type.into(), sub_region).into(), + category: origin.to_constraint_category(), // We don't do leak checks for type outlives - ty::VisibleForLeakCheck::Unreachable, - ) + visible_for_leak_check: ty::VisibleForLeakCheck::Unreachable, + } }, )) .collect(); diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 8a6caa9b10854..b03a07538ccd8 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -49,9 +49,6 @@ #include "llvm/Transforms/Instrumentation/RealtimeSanitizer.h" #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" #include "llvm/Transforms/Scalar/AnnotationRemarks.h" -#if LLVM_VERSION_GE(23, 0) -#include "llvm/Transforms/Utils/AssignGUID.h" -#endif #include "llvm/Transforms/Utils/CanonicalizeAliases.h" #include "llvm/Transforms/Utils/FunctionImportUtils.h" #include "llvm/Transforms/Utils/NameAnonGlobals.h" @@ -918,9 +915,6 @@ extern "C" LLVMRustResult LLVMRustOptimize( if (NeedThinLTOBufferPasses) { MPM.addPass(CanonicalizeAliasesPass()); MPM.addPass(NameAnonGlobalPass()); -#if LLVM_VERSION_GE(23, 0) - MPM.addPass(AssignGUIDPass()); -#endif } // For `-Copt-level=0`, and the pre-link fat/thin LTO stages. if (ThinLTOBufferRef && *ThinLTOBufferRef == nullptr) { @@ -1460,9 +1454,6 @@ extern "C" LLVMRustBuffer *LLVMRustModuleSerialize(LLVMModuleRef M, PB.registerLoopAnalyses(LAM); PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); ModulePassManager MPM; -#if LLVM_VERSION_GE(23, 0) - MPM.addPass(AssignGUIDPass()); -#endif MPM.addPass(ThinLTOBitcodeWriterPass(OS, nullptr)); MPM.run(*unwrap(M), MAM); } else { diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 1dff5740ab3bc..9458fa6381698 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -1,5 +1,6 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(result_option_map_or_default))] #![feature(error_iter)] #![feature(file_buffered)] #![feature(gen_blocks)] @@ -7,7 +8,6 @@ #![feature(min_specialization)] #![feature(never_type)] #![feature(proc_macro_internals)] -#![feature(result_option_map_or_default)] #![feature(strip_circumfix)] #![feature(trusted_len)] // tidy-alphabetical-end diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index ee8ab8a2ff931..8f182d096e759 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -135,9 +135,12 @@ impl<'tcx, R> QueryResponse<'tcx, R> { } } -// FIXME: Convert this into a struct -pub type QueryRegionConstraint<'tcx> = - (ty::RegionConstraint<'tcx>, ConstraintCategory<'tcx>, ty::VisibleForLeakCheck); +#[derive(Debug, StableHash, Hash, Eq, PartialEq, TypeVisitable, Clone, TypeFoldable, Copy)] +pub struct QueryRegionConstraint<'tcx> { + pub constraint: ty::RegionConstraint<'tcx>, + pub category: ConstraintCategory<'tcx>, + pub visible_for_leak_check: ty::VisibleForLeakCheck, +} #[derive(Default)] pub struct CanonicalParamEnvCache<'tcx> { diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 0cc57e5021f86..838dbdcbff58f 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -729,23 +729,6 @@ impl<'tcx> Pat<'tcx> { true }) } - - /// Whether this a never pattern. - pub fn is_never_pattern(&self) -> bool { - let mut is_never_pattern = false; - self.walk(|pat| match &pat.kind { - PatKind::Never => { - is_never_pattern = true; - false - } - PatKind::Or { pats } => { - is_never_pattern = pats.iter().all(|p| p.is_never_pattern()); - false - } - _ => true, - }); - is_never_pattern - } } #[derive(Clone, Debug, StableHash, TypeVisitable)] diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 682bec55b4a38..a0dd7b5c5cb5e 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1305,7 +1305,9 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option, abi: ExternAbi) | RustInvalid | Swift | Unadjusted => false, - Rust | RustCall | RustCold | RustPreserveNone => tcx.sess.panic_strategy().unwinds(), + Rust | RustCall | RustCold | RustPreserveNone | RustTail => { + tcx.sess.panic_strategy().unwinds() + } } } diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index e2d00238e2d59..b4ce8149f5e4d 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -1,10 +1,11 @@ use std::sync::Arc; use rustc_abi::FieldIdx; -use rustc_middle::mir::*; +use rustc_middle::mir::{Pinnedness, Place, PlaceElem, ProjectionElem}; use rustc_middle::span_bug; -use rustc_middle::thir::*; +use rustc_middle::thir::{Ascription, DerefPatBorrowMode, FieldPat, Pat, PatKind}; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; +use rustc_span::Span; use crate::builder::Builder; use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder}; @@ -66,16 +67,166 @@ fn prefix_slice_suffix<'a, 'tcx>( output_pairs } -impl<'tcx> MatchPairTree<'tcx> { - /// Recursively builds a match pair tree for the given pattern and its - /// subpatterns. - pub(super) fn for_pattern( - mut place_builder: PlaceBuilder<'tcx>, +impl<'tcx> FlatPat<'tcx> { + /// Creates a `FlatPat` containing a simplified [`MatchPairTree`] list/forest + /// for the given pattern. + pub(crate) fn new( + place: PlaceBuilder<'tcx>, pattern: &Pat<'tcx>, cx: &mut Builder<'_, 'tcx>, - match_pairs: &mut Vec, // Newly-created nodes are added to this vector - extra_data: &mut PatternExtraData<'tcx>, // Bindings/ascriptions are added here - ) { + ) -> Self { + // Recursively lower the THIR pattern into an intermediate form, + // then flatten into a `FlatPat`. + let inter_pat = InterPat::lower_thir_pat(cx, place, pattern); + FlatPat::from_inter_pat(inter_pat) + } + + /// Squashes an [`InterPat`] into a [`FlatPat`]. + /// + /// This is a separate function because it also needs to be called recursively + /// when squashing any or-patterns. + fn from_inter_pat(inter_pat: InterPat<'tcx>) -> Self { + let mut match_pairs = vec![]; + let mut extra_data = PatternExtraData { + span: inter_pat.pattern_span, + bindings: vec![], + ascriptions: vec![], + is_never: inter_pat.is_never, + }; + squash_inter_pat(inter_pat, &mut match_pairs, &mut extra_data); + + FlatPat { match_pairs, extra_data } + } +} + +/// Recursively squashes an [`InterPat`] into a forest of refutable [`MatchPairTree`] +/// nodes, while accumulating ascriptions and bindings. +fn squash_inter_pat<'tcx>( + inter_pat: InterPat<'tcx>, + match_pairs: &mut Vec>, // Newly-created nodes are added to this vector + extra_data: &mut PatternExtraData<'tcx>, // Bindings/ascriptions are added here +) { + // Destructure exhaustively to make sure we don't miss any fields. + let InterPat { + place, + testable_case, + subpats, + or_subpats, + ascriptions, + binding, + pattern_span, + is_never: _, // Not needed by `MatchPairTree` forests. + } = inter_pat; + + // Type ascriptions can appear regardless of whether the node is an or-pattern. + extra_data.ascriptions.extend(ascriptions); + + // Or and non-or patterns have very different handling. + if let Some(or_subpats) = or_subpats { + // We're dealing with an or-pattern node. + assert!(testable_case.is_none()); + assert!(subpats.is_empty()); + assert!(binding.is_none()); + + let or_subpats = or_subpats + .into_iter() + .map(|subpat| FlatPat::from_inter_pat(subpat)) + .collect::>(); + + if !or_subpats[0].extra_data.bindings.is_empty() { + // Hold a place for any bindings established in (possibly-nested) or-patterns. + // By only holding a place when bindings are present, we skip over any + // or-patterns that will be simplified by `merge_trivial_subcandidates`. In + // other words, we can assume this expands into subcandidates. + // FIXME(@dianne): this needs updating/removing if we always merge or-patterns + extra_data.bindings.push(super::SubpatternBindings::FromOrPattern); + } + + match_pairs.push(MatchPairTree { + // Or-patterns never need a place during MIR building. + place: None, + testable_case: TestableCase::Or { pats: or_subpats }, + subpairs: vec![], + pattern_span, + }); + } else { + // We're dealing with a node that isn't an or-pattern. + + // Recursively squash any subpatterns into refutable `MatchPairTree` forests. + // This must happen _before_ pushing the binding, as described by the binding step. + let mut subpairs = vec![]; + for subpat in subpats { + squash_inter_pat(subpat, &mut subpairs, extra_data); + } + + if let Some(testable_case) = testable_case { + // This pattern is refutable, so push a new match-pair node. + // + // If this match is inside a closure, it's essential that the place + // we're testing was actually captured! Be sure to keep `ExprUseVisitor` + // in sync with the refutability checks in this module. + assert!(place.is_some()); + assert!(!matches!(testable_case, TestableCase::Or { .. })); + match_pairs.push(MatchPairTree { place, testable_case, subpairs, pattern_span }); + } else { + // This pattern is irrefutable, so it doesn't need its own match-pair node. + // Just push its refutable subpatterns instead, if any. + match_pairs.extend(subpairs); + } + + // If present, the binding must be pushed _after_ traversing subpatterns. + // This is so that when lowering something like `x @ NonCopy { copy_field }`, + // the binding to `copy_field` will occur before the binding for `x`. + // See for more background. + if let Some(binding) = binding { + extra_data.bindings.push(super::SubpatternBindings::One(binding)); + } + } +} + +/// "Intermediate pattern", a partly-lowered THIR [`Pat`] that has not yet been +/// squashed into a forest of refutable [`MatchPairTree`] nodes. +/// +/// FIXME(Zalathar): This could potentially be split into different enum variants +/// for or-patterns and non-or patterns, but for now the flat structure makes +/// construction a bit easier, at the cost of more complicated invariants. +struct InterPat<'tcx> { + /// Place that this pattern node will test. + /// + /// If `None`, we're in a closure that didn't capture the relevant place, + /// because it won't actually be tested. + place: Option>, + /// Testable condition to compare the place to (e.g. "is 3" or "is Some"). + /// + /// If `None`, this pattern node is irrefutable or an or-pattern, + /// though it might have refutable descendants. + testable_case: Option>, + + /// Immediate subpatterns of a node that is *not* an or-pattern. + subpats: Vec>, + /// Immediate subpatterns of an or-pattern node. + /// + /// Invariant: If this is Some, then fields `subpats`, `testable_case`, + /// and `binding` must all be empty. + or_subpats: Option]>>, + + ascriptions: Vec>, + /// Binding to establish for a [`PatKind::Binding`] node. + binding: Option>, + + /// Span field of the THIR pattern this node was created from. + pattern_span: Span, + /// True if this pattern can never match, because all of its alternatives + /// contain a `!` pattern. + is_never: bool, +} + +impl<'tcx> InterPat<'tcx> { + fn lower_thir_pat( + cx: &mut Builder<'_, 'tcx>, + mut place_builder: PlaceBuilder<'tcx>, + pattern: &Pat<'tcx>, + ) -> Self { // Force the place type to the pattern's type. // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks? if let Some(resolved) = place_builder.resolve_upvar(cx) { @@ -99,14 +250,19 @@ impl<'tcx> MatchPairTree<'tcx> { } } + // Variables that will become `InterPat` fields: let place = place_builder.try_to_place(cx); + let mut subpats = vec![]; + let mut or_subpats = None; + let mut ascriptions = vec![]; + let mut binding = None; // Apply any type ascriptions to the value at `match_pair.place`. if let Some(place) = place && let Some(extra) = &pattern.extra { for &Ascription { ref annotation, variance } in &extra.ascriptions { - extra_data.ascriptions.push(super::Ascription { + ascriptions.push(super::Ascription { source: place, annotation: annotation.clone(), variance, @@ -114,22 +270,16 @@ impl<'tcx> MatchPairTree<'tcx> { } } - let mut subpairs = Vec::new(); let testable_case = match pattern.kind { PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None, PatKind::Or { ref pats } => { - let pats: Box<[FlatPat<'tcx>]> = - pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(); - if !pats[0].extra_data.bindings.is_empty() { - // Hold a place for any bindings established in (possibly-nested) or-patterns. - // By only holding a place when bindings are present, we skip over any - // or-patterns that will be simplified by `merge_trivial_subcandidates`. In - // other words, we can assume this expands into subcandidates. - // FIXME(@dianne): this needs updating/removing if we always merge or-patterns - extra_data.bindings.push(super::SubpatternBindings::FromOrPattern); - } - Some(TestableCase::Or { pats }) + or_subpats = Some( + pats.iter() + .map(|subpat| InterPat::lower_thir_pat(cx, place_builder.clone(), subpat)) + .collect::>(), + ); + None } PatKind::Range(ref range) => { @@ -165,48 +315,22 @@ impl<'tcx> MatchPairTree<'tcx> { } PatKind::Binding { mode, var, is_shorthand, ref subpattern, .. } => { - // In order to please the borrow checker, when lowering a pattern - // like `x @ subpat` we must establish any bindings in `subpat` - // before establishing the binding for `x`. - // - // For example (from #69971): - // - // ```ignore (illustrative) - // struct NonCopyStruct { - // copy_field: u32, - // } - // - // fn foo1(x: NonCopyStruct) { - // let y @ NonCopyStruct { copy_field: z } = x; - // // the above should turn into - // let z = x.copy_field; - // let y = x; - // } - // ``` - // First, recurse into the subpattern, if any. if let Some(subpattern) = subpattern.as_ref() { // this is the `x @ P` case; have to keep matching against `P` now - MatchPairTree::for_pattern( - place_builder, - subpattern, - cx, - &mut subpairs, - extra_data, - ); + subpats.push(InterPat::lower_thir_pat(cx, place_builder, subpattern)); } // Then push this binding, after any bindings in the subpattern. - if let Some(source) = place { - extra_data.bindings.push(super::SubpatternBindings::One(super::Binding { + if let Some(place) = place { + binding = Some(super::Binding { span: pattern.span, - source, + source: place, var_id: var, binding_mode: mode, is_shorthand, - })); + }); } - None } @@ -223,7 +347,7 @@ impl<'tcx> MatchPairTree<'tcx> { for (subplace, subpat) in prefix_slice_suffix(&place_builder, Some(array_len), prefix, slice, suffix) { - MatchPairTree::for_pattern(subplace, subpat, cx, &mut subpairs, extra_data); + subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat)); } } else { // If the array length couldn't be determined, ignore the @@ -243,12 +367,12 @@ impl<'tcx> MatchPairTree<'tcx> { for (subplace, subpat) in prefix_slice_suffix(&place_builder, None, prefix, slice, suffix) { - MatchPairTree::for_pattern(subplace, subpat, cx, &mut subpairs, extra_data); + subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat)); } if prefix.is_empty() && slice.is_some() && suffix.is_empty() { - // This pattern is shaped like `[..]`. It can match a slice - // of any length, so no length test is needed. + // A slice pattern shaped like `[..]` is irrefutable. + // It can match a slice of any length, so no length test is needed. None } else { // Any other shape of slice pattern requires a length test. @@ -269,7 +393,7 @@ impl<'tcx> MatchPairTree<'tcx> { let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)` for &FieldPat { field, pattern: ref subpat } in subpatterns { let subplace = downcast_place.clone_project(PlaceElem::Field(field, subpat.ty)); - MatchPairTree::for_pattern(subplace, subpat, cx, &mut subpairs, extra_data); + subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat)); } // We treat non-exhaustive enums the same independent of the crate they are @@ -286,7 +410,7 @@ impl<'tcx> MatchPairTree<'tcx> { PatKind::Leaf { ref subpatterns } => { for &FieldPat { field, pattern: ref subpat } in subpatterns { let subplace = place_builder.clone_project(PlaceElem::Field(field, subpat.ty)); - MatchPairTree::for_pattern(subplace, subpat, cx, &mut subpairs, extra_data); + subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat)); } None } @@ -296,27 +420,19 @@ impl<'tcx> MatchPairTree<'tcx> { Some(p_ty) if p_ty.is_ref() => p_ty, _ => span_bug!(pattern.span, "bad type for pinned deref: {:?}", pattern.ty), }; - MatchPairTree::for_pattern( + subpats.push(InterPat::lower_thir_pat( + cx, // Project into the `Pin(_)` struct, then deref the inner `&` or `&mut`. place_builder.field(FieldIdx::ZERO, pinned_ref_ty).deref(), subpattern, - cx, - &mut subpairs, - extra_data, - ); + )); None } PatKind::Deref { pin: Pinnedness::Not, ref subpattern } | PatKind::DerefPattern { ref subpattern, borrow: DerefPatBorrowMode::Box } => { - MatchPairTree::for_pattern( - place_builder.deref(), - subpattern, - cx, - &mut subpairs, - extra_data, - ); + subpats.push(InterPat::lower_thir_pat(cx, place_builder.deref(), subpattern)); None } @@ -330,13 +446,11 @@ impl<'tcx> MatchPairTree<'tcx> { Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability), pattern.span, ); - MatchPairTree::for_pattern( + subpats.push(InterPat::lower_thir_pat( + cx, PlaceBuilder::from(temp).deref(), subpattern, - cx, - &mut subpairs, - extra_data, - ); + )); Some(TestableCase::Deref { temp, mutability }) } @@ -348,24 +462,25 @@ impl<'tcx> MatchPairTree<'tcx> { PatKind::Never => Some(TestableCase::Never), }; - if let Some(testable_case) = testable_case { - // This pattern is refutable, so push a new match-pair node. - // - // Note: unless test_case is TestCase::Or, place must not be None. - // This means that the closure capture analysis in - // rustc_hir_typeck::upvar, and in particular the pattern handling - // code of ExprUseVisitor, must capture all of the places we'll use. - // Make sure to keep these two parts in sync! - match_pairs.push(MatchPairTree { - place, - testable_case, - subpairs, - pattern_span: pattern.span, - }) - } else { - // This pattern is irrefutable, so it doesn't need its own match-pair node. - // Just push its refutable subpatterns instead, if any. - match_pairs.extend(subpairs); + // A pattern node is guaranteed to never match if one of these is true: + // - The node itself is a never pattern (`!`). + // - It is not an or-pattern, and one of its subpatterns will never match. + // - It is an or-pattern, and _all_ of its or-subpatterns will never match. + let is_never = matches!(pattern.kind, PatKind::Never) + || subpats.iter().any(|subpat| subpat.is_never) + || or_subpats + .as_ref() + .is_some_and(|or_subpats| or_subpats.iter().all(|subpat| subpat.is_never)); + + InterPat { + place, + testable_case, + subpats, + or_subpats, + ascriptions, + binding, + pattern_span: pattern.span, + is_never, } } } diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 2fbf772daf3d4..8352ec92e88b0 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -999,24 +999,6 @@ struct FlatPat<'tcx> { extra_data: PatternExtraData<'tcx>, } -impl<'tcx> FlatPat<'tcx> { - /// Creates a `FlatPat` containing a simplified [`MatchPairTree`] list/forest - /// for the given pattern. - fn new(place: PlaceBuilder<'tcx>, pattern: &Pat<'tcx>, cx: &mut Builder<'_, 'tcx>) -> Self { - // Recursively build a tree of match pairs for the given pattern. - let mut match_pairs = vec![]; - let mut extra_data = PatternExtraData { - span: pattern.span, - bindings: Vec::new(), - ascriptions: Vec::new(), - is_never: pattern.is_never_pattern(), - }; - MatchPairTree::for_pattern(place, pattern, cx, &mut match_pairs, &mut extra_data); - - Self { match_pairs, extra_data } - } -} - /// Candidates are a generalization of (a) top-level match arms, and /// (b) sub-branches of or-patterns, allowing the match-lowering process to handle /// them both in a mostly-uniform way. For example, the list of candidates passed @@ -1226,7 +1208,7 @@ struct Ascription<'tcx> { /// and helps [`TestKind::Switch`] and [`TestKind::SwitchInt`] know what target /// values to use. /// -/// Created by [`MatchPairTree::for_pattern`], and then inspected primarily by: +/// Created by [`MatchPairTree`], and then inspected primarily by: /// - [`Builder::pick_test_for_match_pair`] (to choose a test) /// - [`Builder::choose_bucket_for_candidate`] (to see how the test interacts with a match pair) /// diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index 1227fe23713cb..cde885def8a41 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -455,6 +455,7 @@ pub enum CallConvention { PreserveMost, PreserveAll, PreserveNone, + Tail, Custom, diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index f71ca7213e9ed..9b9a480ab5f82 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -1167,6 +1167,7 @@ pub enum Abi { RiscvInterruptM, RiscvInterruptS, RustPreserveNone, + RustTail, RustInvalid, Custom, Swift, diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index 37f00da9f7103..f18fe84053b32 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -618,6 +618,7 @@ impl RustcInternal for Abi { Abi::RiscvInterruptM => rustc_abi::ExternAbi::RiscvInterruptM, Abi::RiscvInterruptS => rustc_abi::ExternAbi::RiscvInterruptS, Abi::RustPreserveNone => rustc_abi::ExternAbi::RustPreserveNone, + Abi::RustTail => rustc_abi::ExternAbi::RustTail, Abi::Custom => rustc_abi::ExternAbi::Custom, Abi::Swift => rustc_abi::ExternAbi::Swift, } diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs index f42399241cd28..7e0b04f8a7f61 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -125,6 +125,7 @@ impl<'tcx> Stable<'tcx> for CanonAbi { CanonAbi::Rust => CallConvention::Rust, CanonAbi::RustCold => CallConvention::Cold, CanonAbi::RustPreserveNone => CallConvention::PreserveNone, + CanonAbi::RustTail => CallConvention::Tail, CanonAbi::Custom => CallConvention::Custom, CanonAbi::Swift => CallConvention::Swift, CanonAbi::Arm(arm_call) => match arm_call { diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 925d4a5b40b20..8469c500b24c8 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -1046,6 +1046,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::ExternAbi { ExternAbi::Unadjusted => Abi::Unadjusted, ExternAbi::RustCold => Abi::RustCold, ExternAbi::RustPreserveNone => Abi::RustPreserveNone, + ExternAbi::RustTail => Abi::RustTail, ExternAbi::RustInvalid => Abi::RustInvalid, ExternAbi::RiscvInterruptM => Abi::RiscvInterruptM, ExternAbi::RiscvInterruptS => Abi::RiscvInterruptS, diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index f26405cb223c0..92bc577f59201 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -412,17 +412,16 @@ pub fn may_be_doc_link(link_type: LinkType) -> bool { pub(crate) fn attrs_to_preprocessed_links(attrs: &[ast::Attribute]) -> Vec> { let (doc_fragments, other_attrs) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), false); - let mut doc = - prepare_to_doc_link_resolution(&doc_fragments).into_values().next().unwrap_or_default(); + let doc = prepare_to_doc_link_resolution(&doc_fragments).into_values().next(); + let mut links = doc.as_deref().map(parse_links).unwrap_or_default(); for attr in other_attrs { if let Some(note) = attr.deprecation_note() { - doc += note.as_str(); - doc += "\n"; + links.extend(parse_links(note.as_str())); } } - parse_links(&doc) + links } /// Similar version of `markdown_links` from rustdoc. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index fb433aef68cf8..9313e869278db 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1703,6 +1703,7 @@ symbols! { rust_logo, rust_out, rust_preserve_none_cc, + rust_tail_cc, rustc, rustc_abi, // FIXME(#82232, #143834): temporary name to mitigate `#[align]` nameres ambiguity diff --git a/compiler/rustc_target/src/spec/abi_map.rs b/compiler/rustc_target/src/spec/abi_map.rs index 8c56dcb899718..f7a7ae1eb7555 100644 --- a/compiler/rustc_target/src/spec/abi_map.rs +++ b/compiler/rustc_target/src/spec/abi_map.rs @@ -100,6 +100,7 @@ impl AbiMap { (ExternAbi::RustCold, _) if self.os == OsKind::Windows => CanonAbi::Rust, (ExternAbi::RustCold, _) => CanonAbi::RustCold, (ExternAbi::RustPreserveNone, _) => CanonAbi::RustPreserveNone, + (ExternAbi::RustTail, _) => CanonAbi::RustTail, (ExternAbi::Custom, _) => CanonAbi::Custom, diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index bd22ba6b6bf6d..7c2f5c7f170aa 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -7,6 +7,7 @@ use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::canonical::{ Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarKind, CanonicalVarValues, + QueryRegionConstraint, }; use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt}; use rustc_infer::traits::solve::{FetchEligibleAssocItemResponse, Goal}; @@ -262,7 +263,9 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< let mut seen = FxHashMap::default(); let mut constraints = vec![]; - for (outlives, _, vis) in region_constraints.constraints { + for QueryRegionConstraint { constraint: outlives, visible_for_leak_check: vis, .. } in + region_constraints.constraints + { match seen.entry(outlives) { Entry::Occupied(occupied) => { let idx = occupied.get(); diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 8be26fed0ca42..a171a0de9dd79 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -1,4 +1,5 @@ use rustc_infer::infer::InferOk; +use rustc_infer::infer::canonical::QueryRegionConstraint; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_macros::extension; @@ -83,7 +84,7 @@ fn implied_outlives_bounds<'a, 'tcx>( // outlives bound required proving some higher-ranked coroutine obl. let QueryRegionConstraints { constraints, assumptions: _ } = constraints; let cause = ObligationCause::misc(span, body_id); - for &(constraint, _, vis) in &constraints { + for &QueryRegionConstraint { constraint, visible_for_leak_check: vis, .. } in &constraints { match constraint { ty::RegionConstraint::Outlives(predicate) => { infcx.register_outlives_constraint(predicate, vis, &cause) diff --git a/compiler/rustc_traits/src/coroutine_witnesses.rs b/compiler/rustc_traits/src/coroutine_witnesses.rs index 83a77f17b28ce..7fe8303b7459b 100644 --- a/compiler/rustc_traits/src/coroutine_witnesses.rs +++ b/compiler/rustc_traits/src/coroutine_witnesses.rs @@ -1,4 +1,5 @@ use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::canonical::QueryRegionConstraint; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::traits::{Obligation, ObligationCause}; @@ -80,7 +81,7 @@ fn compute_assumptions<'tcx>( tcx.mk_outlives_from_iter( constraints .into_iter() - .flat_map(|(constraint, _, _)| constraint.iter_outlives()) + .flat_map(|QueryRegionConstraint { constraint, .. }| constraint.iter_outlives()) // 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. diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 523c9b8b15858..785c1c8026a1c 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -1541,14 +1541,12 @@ impl Rc { /// /// # Safety /// - /// The pointer must have been obtained through `Rc::into_raw` and must satisfy the - /// same layout requirements specified in [`Rc::from_raw_in`][from_raw_in]. + /// The pointer must have been obtained through [`Rc::into_raw`] and must satisfy the + /// same layout requirements specified in [`Rc::from_raw_in`]. /// The associated `Rc` instance must be valid (i.e. the strong count must be at /// least 1) for the duration of this method, and `ptr` must point to a block of memory /// allocated by the global allocator. /// - /// [from_raw_in]: Rc::from_raw_in - /// /// # Examples /// /// ``` diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index cf04402e3d984..230039713de8a 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -659,6 +659,108 @@ impl str { s } + /// Returns the case-folded equivalent of this string slice, as a new [`String`]. + /// + /// Case folding is a transformation, mostly matching lowercase, that is meant to be used + /// for case-insensitive string comparisons. Case-folded strings should not usually + /// be exposed directly to users. + /// + /// For the precise specification of case folding, see + /// [Chapter 3 (Conformance)](https://www.unicode.org/versions/latest/core-spec/chapter-3/#G63737) + /// of the Unicode standard. + /// + /// Since some characters can expand into multiple characters when case folding, + /// this function returns a [`String`] instead of modifying the parameter in-place. + /// + /// No [normalization] (e.g. NFC) is performed, so visually and semantically identical strings + /// might still casefold differently. For example, `"Å"` (U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE) + /// is considered distinct from `"Å"` (A followed by U+030A COMBINING RING ABOVE), + /// even though Unicode considers them canonically equivalent. + /// + /// Like [`char::to_casefold_unnormalized()`] this method does not handle language-specific + /// casing, like Turkish and Azeri I/ı/İ/i. See that method's documentation + /// for more information. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(casefold)] + /// let s0 = "HELLO"; + /// let s1 = "Hello"; + /// + /// assert_eq!(s0.to_casefold_unnormalized(), s1.to_casefold_unnormalized()); + /// assert_eq!(s0.to_casefold_unnormalized(), "hello") + /// ``` + /// + /// Scripts without case are not changed: + /// + /// ``` + /// #![feature(casefold)] + /// let new_year = "农历新年"; + /// + /// assert_eq!(new_year, new_year.to_casefold_unnormalized()); + /// ``` + /// + /// One character can become multiple: + /// + /// ``` + /// #![feature(casefold)] + /// let s0 = "TSCHÜẞ"; + /// let s1 = "TSCHÜSS"; + /// let s2 = "tschüß"; + /// + /// assert_eq!(s0.to_casefold_unnormalized(), s1.to_casefold_unnormalized()); + /// assert_eq!(s0.to_casefold_unnormalized(), s2.to_casefold_unnormalized()); + /// assert_eq!(s0.to_casefold_unnormalized(), "tschüss"); + /// ``` + /// + /// No NFC [normalization] is performed: + /// + /// ```rust + /// #![feature(casefold)] + /// // These two strings are visually and semantically identical... + /// let comp = "Å"; + /// let decomp = "Å"; + /// + /// // ... but not codepoint-for-codepoint equal. + /// assert_eq!(comp, "\u{C5}"); + /// assert_eq!(decomp, "A\u{030A}"); + /// + /// // Their case-foldings are likewise unequal: + /// assert_eq!(comp.to_casefold_unnormalized(), "\u{E5}"); + /// assert_eq!(decomp.to_casefold_unnormalized(), "a\u{030A}"); + /// ``` + /// + /// [normalization]: https://www.unicode.org/faq/normalization + #[cfg(not(no_global_oom_handling))] + #[rustc_allow_incoherent_impl] + #[must_use = "this returns the case-folded string as a new String, \ + without modifying the original"] + #[unstable(feature = "casefold", issue = "154742")] + pub fn to_casefold_unnormalized(&self) -> String { + // SAFETY: `to_ascii_lowercase` preserves ASCII bytes, so the converted + // prefix remains valid UTF-8. + let (mut s, rest) = unsafe { convert_while_ascii(self, u8::to_ascii_lowercase) }; + + for c in rest.chars() { + match conversions::to_casefold(c) { + [a, '\0', _] => s.push(a), + [a, b, '\0'] => { + s.push(a); + s.push(b); + } + [a, b, c] => { + s.push(a); + s.push(b); + s.push(c); + } + } + } + s + } + /// Converts a [`Box`] into a [`String`] without copying or allocating. /// /// # Examples diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index b7c3e39c992b0..f7573c7d7fc3e 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -3,6 +3,7 @@ #![feature(const_heap)] #![feature(deque_extend_front)] #![feature(iter_array_chunks)] +#![feature(casefold)] #![feature(cow_is_borrowed)] #![feature(core_intrinsics)] #![feature(downcast_unchecked)] diff --git a/library/alloctests/tests/str.rs b/library/alloctests/tests/str.rs index dca2c49249aff..1875701b102a8 100644 --- a/library/alloctests/tests/str.rs +++ b/library/alloctests/tests/str.rs @@ -1886,7 +1886,13 @@ fn to_lowercase() { #[test] fn to_uppercase() { assert_eq!("".to_uppercase(), ""); - assert_eq!("aéDžßfiᾀ".to_uppercase(), "AÉDŽSSFIἈΙ"); + assert_eq!("aéDžßẞfiᾀ".to_uppercase(), "AÉDŽSSẞFIἈΙ"); +} + +#[test] +fn to_casefold_unnormalized() { + assert_eq!("".to_casefold_unnormalized(), ""); + assert_eq!("ꮿfiῲὼ\u{0345}ßẞΣς".to_casefold_unnormalized(), "Ꮿfiὼιὼιssssσσ"); } #[test] diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs index a957005972a20..c8c05ee559f74 100644 --- a/library/core/src/ascii/ascii_char.rs +++ b/library/core/src/ascii/ascii_char.rs @@ -476,6 +476,11 @@ impl AsciiChar { #[unstable(feature = "ascii_char", issue = "110998")] #[inline] pub const unsafe fn from_u8_unchecked(b: u8) -> Self { + assert_unsafe_precondition!( + check_library_ub, + "`ascii::Char::from_u8_unchecked` input cannot exceed 127.", + (b: u8 = b) => b <= 127, + ); // SAFETY: Our safety precondition is that `b` is in-range. unsafe { transmute(b) } } diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index d8b7fe44e453a..a397dd8df5359 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -1076,16 +1076,17 @@ impl char { } /// Returns `true` if this `char` has the `Case_Ignorable` property. This narrow-use property - /// is used to implement context-dependent casing for the Greek letter sigma (uppercase Σ), + /// is used to implement context-dependent casing for the Greek letter sigma (uppercase 'Σ'), /// which has two lowercase forms. /// /// `Case_Ignorable` is [described][D136] in Chapter 3 (Conformance) of the Unicode Core Specification, - /// and specified in the [Unicode Character Database][ucd] [`DerivedCoreProperties.txt`]; - /// see those resources for more information. + /// and specified in the [Unicode Character Database][ucd] [`DerivedCoreProperties.txt`]. + /// See those resources, as well as [`to_lowercase()`]'s documentation, for more information. /// /// [D136]: https://www.unicode.org/versions/latest/core-spec/chapter-3/#G63116 /// [ucd]: https://www.unicode.org/reports/tr44/ /// [`DerivedCoreProperties.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt + /// [`to_lowercase()`]: Self::to_lowercase() #[must_use] #[inline] #[unstable(feature = "case_ignorable", issue = "154848")] @@ -1155,8 +1156,6 @@ impl char { /// If this `char` expands to multiple `char`s, the iterator yields the `char`s given by /// [`SpecialCasing.txt`]. The maximum number of `char`s in a case mapping is 3. /// - /// [`SpecialCasing.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/SpecialCasing.txt - /// /// This operation performs an unconditional mapping without tailoring. That is, the conversion /// is independent of context and language. See [below](#notes-on-context-and-locale) /// for more information. @@ -1211,14 +1210,25 @@ impl char { /// /// ## Greek sigma /// - /// In Greek, the letter simga (uppercase Σ) has two lowercase forms: - /// ς which is used only at the end of a word, and σ which is used everywhere else. - /// `to_lowercase()` always uses the second form: + /// In Greek, the letter simga (uppercase 'Σ') has two lowercase forms: + /// 'σ' which is used in most situations, and 'ς' which appears only + /// at the end of a word. [`char::to_lowercase()`] always uses the first form: /// /// ``` /// assert_eq!('Σ'.to_lowercase().to_string(), "σ"); /// ``` /// + /// `str::to_lowercase()` (only available with the `alloc` crate) + /// *does* properly handle this contextual mapping, + /// so prefer using that method if you can. Alternatively, you can use + /// [`is_cased()`] and [`is_case_ignorable()`] to implement it yourself. + /// See `Final_Sigma` in [Table 3.17] of the Unicode Standard, + /// along with [`SpecialCasing.txt`], for more details. + /// + /// [`is_cased()`]: Self::is_cased() + /// [`is_case_ignorable()`]: Self::is_case_ignorable() + /// [Table 3.17]: https://www.unicode.org/versions/latest/core-spec/chapter-3/#G54277 + /// /// ## Turkish and Azeri I/ı/İ/i /// /// In Turkish and Azeri, the equivalent of 'i' in Latin has five forms instead of two: @@ -1226,13 +1236,13 @@ impl char { /// * 'Dotless': I / ı, sometimes written ï /// * 'Dotted': İ / i /// - /// Note that the uppercase undotted 'I' is the same as the Latin. Therefore: + /// Note that the uppercase undotted 'I' is the same codepoint as the Latin. Therefore: /// /// ``` /// let lower_i = 'I'.to_lowercase().to_string(); /// ``` /// - /// The value of `lower_i` here relies on the language of the text: if we're + /// `'I'`'s correct lowercase relies on the language of the text: if we're /// in `en-US`, it should be `"i"`, but if we're in `tr-TR` or `az-AZ`, it should /// be `"ı"`. `to_lowercase()` does not take this into account, and so: /// @@ -1243,6 +1253,8 @@ impl char { /// ``` /// /// holds across languages. + /// + /// [`SpecialCasing.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/SpecialCasing.txt #[must_use = "this returns the lowercased character as a new iterator, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] @@ -1393,22 +1405,22 @@ impl char { /// As stated above, this method is locale-insensitive. /// If you need locale support, consider using an external crate, /// like [`icu_casemap`](https://crates.io/crates/icu_casemap) - /// which is developed by Unicode. A description of a common - /// locale-dependent casing issue follows: + /// which is developed by Unicode. A description of one common + /// locale-dependent casing issue follows (there are others): /// /// In Turkish and Azeri, the equivalent of 'i' in Latin has five forms instead of two: /// /// * 'Dotless': I / ı, sometimes written ï /// * 'Dotted': İ / i /// - /// Note that the lowercase dotted 'i' is the same as the Latin. Therefore: + /// Note that the lowercase dotted 'i' is the same codepoint as the Latin. Therefore: /// /// ``` /// #![feature(titlecase)] /// let upper_i = 'i'.to_titlecase().to_string(); /// ``` /// - /// The value of `upper_i` here relies on the language of the text: if we're + /// `'i'`'s correct titlecase relies on the language of the text: if we're /// in `en-US`, it should be `"I"`, but if we're in `tr-TR` or `az-AZ`, it should /// be `"İ"`. `to_titlecase()` does not take this into account, and so: /// @@ -1505,21 +1517,21 @@ impl char { /// As stated above, this method is locale-insensitive. /// If you need locale support, consider using an external crate, /// like [`icu_casemap`](https://crates.io/crates/icu_casemap) - /// which is developed by Unicode. A description of a common - /// locale-dependent casing issue follows: + /// which is developed by Unicode. A description of one common + /// locale-dependent casing issue follows (there are others): /// /// In Turkish and Azeri, the equivalent of 'i' in Latin has five forms instead of two: /// /// * 'Dotless': I / ı, sometimes written ï /// * 'Dotted': İ / i /// - /// Note that the lowercase dotted 'i' is the same as the Latin. Therefore: + /// Note that the lowercase dotted 'i' is the same codepoint as the Latin. Therefore: /// /// ``` /// let upper_i = 'i'.to_uppercase().to_string(); /// ``` /// - /// The value of `upper_i` here relies on the language of the text: if we're + /// `'i'`'s correct uppercase relies on the language of the text: if we're /// in `en-US`, it should be `"I"`, but if we're in `tr-TR` or `az-AZ`, it should /// be `"İ"`. `to_uppercase()` does not take this into account, and so: /// @@ -1540,6 +1552,110 @@ impl char { ToUppercase(CaseMappingIter::new(conversions::to_upper(self))) } + /// Returns an iterator that yields the case folding of this `char` as one or more + /// `char`s. + /// + /// Case folding is meant to be used when performing case-insensitive string comparisons. + /// Case-folded strings should not usually be exposed directly to users. For most, + /// but not all, characters, the casefold mapping is identical to the lowercase one. + /// + /// This iterator yields the `char`(s) in the common or full case folding for this `char`, + /// as given by the [Unicode Character Database][ucd] [`CaseFolding.txt`]. + /// The maximum number of `char`s in a case folding is 3. + /// + /// [ucd]: https://www.unicode.org/reports/tr44/ + /// [`CaseFolding.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/CaseFolding.txt + /// + /// + /// No [normalization] (e.g. NFC) is performed, so visually and semantically identical characters + /// might still casefold differently. For example, `'ά'` (U+03AC GREEK SMALL LETTER ALPHA WITH TONOS) + /// is considered distinct from `'ά'` (U+1F71 GREEK SMALL LETTER ALPHA WITH OXIA), + /// even though Unicode considers them canonically equivalent. + /// + /// In addition, this method is independent of language/locale, + /// so the special behavior of I/ı/İ/i in Turkish and Azeri is not handled. + /// + /// In the [Unicode Standard], Chapter 4 (Character Properties) discusses case folding in + /// general and Chapter 3 (Conformance) discusses the default algorithm for case folding. + /// + /// [Unicode Standard]: https://www.unicode.org/versions/latest/ + /// + /// # Examples + /// + /// The German sharp S `'ß'` (U+DF) is a single Unicode code point + /// that casefolds to `"ss"`. Its uppercase variant '`ẞ`' (U+1E9E) + /// has the same case-folding. + /// + /// As an iterator: + /// + /// ``` + /// #![feature(casefold)] + /// assert!('ß'.to_casefold_unnormalized().eq(['s', 's'])); + /// assert!('ẞ'.to_casefold_unnormalized().eq(['s', 's'])); + /// ``` + /// + /// Using [`to_string`](../std/string/trait.ToString.html#tymethod.to_string): + /// + /// ``` + /// #![feature(casefold)] + /// assert_eq!('ß'.to_casefold_unnormalized().to_string(), "ss"); + /// assert_eq!('ẞ'.to_casefold_unnormalized().to_string(), "ss"); + /// ``` + /// + /// No [normalization] is performed: + /// + /// ```rust + /// #![feature(casefold)] + /// // These two characters are visually and semantically identical; + /// // Unicode considers them to be canonically equivalent. + /// let alpha_tonos = 'ά'; + /// let alpha_oxia = 'ά'; + /// + /// // However, they are different codepoints: + /// assert_eq!(alpha_tonos, '\u{03AC}'); + /// assert_eq!(alpha_oxia, '\u{1F71}'); + /// + /// // Their case-foldings are likewise unequal: + /// assert!(alpha_tonos.to_casefold_unnormalized().eq(['\u{03AC}'])); + /// assert!(alpha_oxia.to_casefold_unnormalized().eq(['\u{1F71}'])); + /// ``` + /// + /// # Note on locale + /// + /// In Turkish and Azeri, the equivalent of 'i' in Latin has five forms instead of two: + /// + /// * 'Dotless': I / ı, sometimes written ï + /// * 'Dotted': İ / i + /// + /// Note that the uppercase undotted 'I' is the same codepoint as the Latin. Therefore: + /// + /// ``` + /// #![feature(casefold)] + /// let casefold_i = 'I'.to_casefold_unnormalized().to_string(); + /// ``` + /// + /// `'I'`'s correct case folding relies on the language of the text: if we're + /// in `en-US`, it should be `"i"`, but if we're in `tr-TR` or `az-AZ`, it should + /// be `"ı"`. `to_casefold_unnormalized()` does not take this into account, and so: + /// + /// ``` + /// #![feature(casefold)] + /// let casefold_i = 'I'.to_casefold_unnormalized().to_string(); + /// + /// assert_eq!(casefold_i, "i"); + /// ``` + /// + /// holds across languages. + /// + /// [normalization]: https://www.unicode.org/faq/normalization + #[must_use = "this returns the case-folded character as a new iterator, \ + without modifying the original"] + #[unstable(feature = "casefold", issue = "154742")] + #[inline] + pub fn to_casefold_unnormalized(self) -> ToCasefold { + ToCasefold(CaseMappingIter::new(conversions::to_casefold(self))) + } + /// Checks if the value is within the ASCII range. /// /// # Examples diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index 2416406576204..eab7076c8427d 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -519,6 +519,21 @@ casemappingiter_impls! { ToLowercase } +casemappingiter_impls! { + #[unstable(feature = "casefold", issue = "154742")] + #[unstable(feature = "casefold", issue = "154742")] + #[unstable(feature = "casefold", issue = "154742")] + #[unstable(feature = "casefold", issue = "154742")] + #[unstable(feature = "casefold", issue = "154742")] + /// Returns an iterator that yields the case-folded equivalent of a `char`. + /// + /// This `struct` is created by the [`to_casefold_unnormalized`] method on [`char`]. See + /// its documentation for more. + /// + /// [`to_casefold_unnormalized`]: char::to_casefold_unnormalized + ToCasefold +} + #[derive(Debug, Clone)] struct CaseMappingIter(core::array::IntoIter); diff --git a/library/core/src/option.rs b/library/core/src/option.rs index a490a26aa0ff3..c11b2354d1638 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1283,8 +1283,6 @@ impl Option { /// # Examples /// /// ``` - /// #![feature(result_option_map_or_default)] - /// /// let x: Option<&str> = Some("hi"); /// let y: Option<&str> = None; /// @@ -1294,7 +1292,7 @@ impl Option { /// /// [default value]: Default::default #[inline] - #[unstable(feature = "result_option_map_or_default", issue = "138099")] + #[stable(feature = "result_option_map_or_default", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] pub const fn map_or_default(self, f: F) -> U where diff --git a/library/core/src/pin/unsafe_pinned.rs b/library/core/src/pin/unsafe_pinned.rs index 483cf529798b1..83d9e9acedbf7 100644 --- a/library/core/src/pin/unsafe_pinned.rs +++ b/library/core/src/pin/unsafe_pinned.rs @@ -178,5 +178,3 @@ impl, U> CoerceUnsized> for UnsafePinned #[unstable(feature = "dispatch_from_dyn", issue = "none")] // #[unstable(feature = "unsafe_pinned", issue = "125735")] impl, U> DispatchFromDyn> for UnsafePinned {} - -// FIXME(unsafe_pinned): impl PinCoerceUnsized for UnsafePinned? diff --git a/library/core/src/result.rs b/library/core/src/result.rs index f0e1a1def49d1..a1915fb2a792c 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -912,8 +912,6 @@ impl Result { /// # Examples /// /// ``` - /// #![feature(result_option_map_or_default)] - /// /// let x: Result<_, &str> = Ok("foo"); /// let y: Result<&str, _> = Err("bar"); /// @@ -923,7 +921,7 @@ impl Result { /// /// [default value]: Default::default #[inline] - #[unstable(feature = "result_option_map_or_default", issue = "138099")] + #[stable(feature = "result_option_map_or_default", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_result_trait_fn", issue = "144211")] pub const fn map_or_default(self, f: F) -> U where diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index fe8ed6c8e637e..c96aaecf94229 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2826,6 +2826,9 @@ impl str { /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, /// but without allocating and copying temporaries. /// + /// For Unicode-aware case-insensitive matching, consider + /// [`str::eq_ignore_case_unnormalized`]. + /// /// # Examples /// /// ``` @@ -2841,6 +2844,59 @@ impl str { self.as_bytes().eq_ignore_ascii_case(other.as_bytes()) } + /// Checks that two strings are a caseless match, according to + /// [Definition 144] in Chapter 3 of the Unicode Standard. + /// + /// [Definition 144]: https://www.unicode.org/versions/latest/core-spec/chapter-3/#G53513 + /// + /// Same as `a.to_casefold_unnormalized() == b.to_casefold_unnormalized()`, + /// but without allocating. See that method's documentation, + /// as well as [`char::to_casefold_unnormalized()`], + /// for more information about case folding. + /// + /// No [normalization] (e.g. NFC) is performed, so visually and semantically identical strings + /// might still compare unequal. For example, `"Å"` (U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE) + /// is considered distinct from `"Å"` (A followed by U+030A COMBINING RING ABOVE), + /// even though Unicode considers them canonically equivalent. + /// + /// In addition, this method is independent of language/locale, + /// so the special behavior of I/ı/İ/i in Turkish and Azeri is not handled. + /// + /// # Examples + /// + /// ``` + /// #![feature(casefold)] + /// assert!("Ferris".eq_ignore_case_unnormalized("FERRIS")); + /// assert!("Ferrös".eq_ignore_case_unnormalized("FERRÖS")); + /// assert!("ẞ".eq_ignore_case_unnormalized("ss")); + /// ``` + /// + /// No NFC [normalization] is performed: + /// + /// ```rust + /// #![feature(casefold)] + /// // These two strings are visually and semantically identical... + /// let comp = "Å"; + /// let decomp = "Å"; + /// + /// // ... but not codepoint-for-codepoint equal. + /// assert_eq!(comp, "\u{C5}"); + /// assert_eq!(decomp, "A\u{030A}"); + /// + /// // Their case-foldings are likewise unequal: + /// assert!(!comp.eq_ignore_case_unnormalized(decomp)); + /// ``` + /// + /// [normalization]: https://www.unicode.org/faq/normalization + #[unstable(feature = "casefold", issue = "154742")] + #[must_use] + #[inline] + pub fn eq_ignore_case_unnormalized(&self, other: &str) -> bool { + self.chars() + .flat_map(char::to_casefold_unnormalized) + .eq(other.chars().flat_map(char::to_casefold_unnormalized)) + } + /// Converts this string to its ASCII upper case equivalent in-place. /// /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index ec96099560915..4f22e019c52be 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -4223,8 +4223,9 @@ unsafe fn atomic_umin(dst: *mut T, val: T, order: Ordering) -> T { /// An atomic fence. /// /// Fences create synchronization between themselves and atomic operations or fences in other -/// threads. To achieve this, a fence prevents the compiler and CPU from reordering certain types of -/// memory operations around it. +/// threads. It can be helpful to think of a fence as preventing the compiler and CPU from +/// reordering certain types of memory operations around it, but that is a simplified model which +/// fails to capture some of the nuances. /// /// There are 3 different ways to use an atomic fence: /// @@ -4374,6 +4375,7 @@ unsafe fn atomic_umin(dst: *mut T, val: T, order: Ordering) -> T { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "fence"] +#[doc(alias = "atomic_thread_fence")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fence(order: Ordering) { // SAFETY: using an atomic fence is safe. @@ -4388,15 +4390,15 @@ pub fn fence(order: Ordering) { } } -/// A "compiler-only" atomic fence. +/// An atomic fence for synchronization within a single thread. /// /// Like [`fence`], this function establishes synchronization with other atomic operations and /// fences. However, unlike [`fence`], `compiler_fence` only establishes synchronization with /// operations *in the same thread*. This may at first sound rather useless, since code within a /// thread is typically already totally ordered and does not need any further synchronization. -/// However, there are cases where code can run on the same thread without being ordered: +/// However, there are cases where code can run on the same thread without being synchronized: /// - The most common case is that of a *signal handler*: a signal handler runs in the same thread -/// as the code it interrupted, but it is not ordered with respect to that code. `compiler_fence` +/// as the code it interrupted, but it is not synchronized with that code. `compiler_fence` /// can be used to establish synchronization between a thread and its signal handler, the same way /// that `fence` can be used to establish synchronization across threads. /// - Similar situations can arise in embedded programming with interrupt handlers, or in custom @@ -4407,9 +4409,14 @@ pub fn fence(order: Ordering) { /// [`fence`], synchronization still requires atomic operations to be used in both threads -- it is /// not possible to perform synchronization entirely with fences and non-atomic operations. /// -/// `compiler_fence` does not emit any machine code, but restricts the kinds of memory re-ordering -/// the compiler is allowed to do. `compiler_fence` corresponds to [`atomic_signal_fence`] in C and -/// C++. +/// `compiler_fence` does not emit any machine code. However, note that `compiler_fence` is also +/// *not* a "compiler barrier". It can be helpful to think of a `compiler_fence` as preventing the +/// compiler from reordering certain types of memory operations around it, but that is a simplified +/// model which fails to capture some of the nuances. The only actual guarantee made by +/// `compiler_fence` is establishing synchronization with signal handlers and similar kinds of code, +/// under the rules described in the [`fence`] documentation. +/// +/// `compiler_fence` corresponds to [`atomic_signal_fence`] in C and C++. /// /// [`atomic_signal_fence`]: https://en.cppreference.com/w/cpp/atomic/atomic_signal_fence /// @@ -4452,6 +4459,7 @@ pub fn fence(order: Ordering) { #[inline] #[stable(feature = "compiler_fences", since = "1.21.0")] #[rustc_diagnostic_item = "compiler_fence"] +#[doc(alias = "atomic_signal_fence")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn compiler_fence(order: Ordering) { // SAFETY: using an atomic fence is safe. diff --git a/library/core/src/sync/sync_view.rs b/library/core/src/sync/sync_view.rs index 3b02c4c727ca8..63e157bf90f23 100644 --- a/library/core/src/sync/sync_view.rs +++ b/library/core/src/sync/sync_view.rs @@ -188,7 +188,7 @@ impl SyncView { #[rustc_const_unstable(feature = "exclusive_wrapper", issue = "98407")] #[must_use] #[inline] - pub const fn as_pin(self: Pin<&Self>) -> Pin<&T> { + pub const fn as_pin_ref(self: Pin<&Self>) -> Pin<&T> { // SAFETY: `SyncView` can only produce `&T` if itself is unpinned // `Pin::map_unchecked` is not const, so we do this conversion manually unsafe { Pin::new_unchecked(&self.get_ref().inner) } diff --git a/library/core/src/unicode/unicode_data.rs b/library/core/src/unicode/unicode_data.rs index 83d3808051840..9df020ced674b 100644 --- a/library/core/src/unicode/unicode_data.rs +++ b/library/core/src/unicode/unicode_data.rs @@ -10,7 +10,8 @@ // to_lower : 1112 bytes, 1462 codepoints in 185 ranges (U+0000C0 - U+01E921) using 2-level LUT // to_upper : 1998 bytes, 1554 codepoints in 299 ranges (U+0000B5 - U+01E943) using 2-level LUT // to_title : 340 bytes, 135 codepoints in 49 ranges (U+0000DF - U+00FB17) using 2-level LUT -// Total : 9629 bytes +// to_casefold : 32 bytes, 174 codepoints in 5 ranges (U+000131 - U+00ABBF) using 2-level LUT +// Total : 9661 bytes #[inline(always)] const fn bitset_search< @@ -846,7 +847,7 @@ pub mod conversions { } pub fn to_lower(c: char) -> [char; 3] { - // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%253AChanges_When_Lowercased%253A%5D-%5B%253AASCII%253A%5D&abb=on + // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=[:Changes_When_Lowercased:]-[:ASCII:]&abb=on if c < '\u{C0}' { return [c.to_ascii_lowercase(), '\0', '\0']; } @@ -855,7 +856,7 @@ pub mod conversions { } pub fn to_upper(c: char) -> [char; 3] { - // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%253AChanges_When_Uppercased%253A%5D-%5B%253AASCII%253A%5D&abb=on + // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=[:Changes_When_Uppercased:]-[:ASCII:]&abb=on if c < '\u{B5}' { return [c.to_ascii_uppercase(), '\0', '\0']; } @@ -864,7 +865,7 @@ pub mod conversions { } pub fn to_title(c: char) -> [char; 3] { - // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%253AChanges_When_Titlecased%253A%5D-%5B%253AASCII%253A%5D&abb=on + // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=[:Changes_When_Titlecased:]-[:ASCII:]&abb=on if c < '\u{B5}' { return [c.to_ascii_uppercase(), '\0', '\0']; } @@ -872,6 +873,88 @@ pub mod conversions { lookup(c, &TITLECASE_LUT).or_else(|| lookup(c, &UPPERCASE_LUT)).unwrap_or([c, '\0', '\0']) } + pub fn to_casefold(c: char) -> [char; 3] { + // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=[:Changes_When_Casefolded:]-[:ASCII:]&abb=on + if c < '\u{B5}' { + return [c.to_ascii_lowercase(), '\0', '\0']; + } + + + lookup(c, &CASEFOLD_LUT).unwrap_or_else(|| { + // Fall back to lowercase of uppercase + + let uppercase = lookup(c, &UPPERCASE_LUT).unwrap_or([c, '\0', '\0']); + + // We need to take the lowercase of each character in `uppercase`, + // and then concatenate them together. + + // Lowercase the first uppercased char + let mut final_result = to_lower(uppercase[0]); + + if uppercase[1] != '\0' { + // There's a 2nd uppercase char, lowercase it as well + let lowercase_1 = to_lower(uppercase[1]); + + // The lowercase of the second uppercase character + // can't be 3 chars long; + // that would bring the total case-folding length + // above 3 characters, which would violate + // a Unicode stability guarantee. + debug_assert_eq!(lowercase_1[2], '\0'); + + // Currently, in every case where there + // are multiple uppercased characters, + // the lowercase of the first uppercase + // has length 1. However, Unicode doesn't + // guarantee this. + // If, after updating the Unicode data + // to a new Unicode version, the below + // assertion starts to fail in + // `coretests/tests/unicode.rs` `to_casefold()`, + // delete it, and uncomment the + // `if` condition and corresponding + // `else` block below it. + debug_assert_eq!(final_result[1], '\0'); + //if final_result[1] == '\0' { + + final_result[1] = lowercase_1[0]; + + if uppercase[2] != '\0' { + // There's a 3rd uppercased char, lowercase it as well. + // Because of the Unicode stability guarantee that case-folding + // does not expand a string more than 3x in length, + // we know this lowercase must be 1 char long. + + debug_assert_eq!(lowercase_1[1], '\0'); + let lowercase_2 = to_lower(uppercase[2]); + debug_assert_eq!(lowercase_2[1], '\0'); + debug_assert_eq!(lowercase_2[2], '\0'); + final_result[2] = lowercase_2[0]; + } else { + // Currently, the lowercase of + // the second uppercase character + // can't be 2 chars long either, + // but Unicode doesn't guarantee this. + // If, after updating the Unicode data + // to a new Unicode version, the below + // assertion starts to fail in + // `coretests/tests/unicode.rs` `to_casefold()`, + // delete it and uncomment the line + // below it. + debug_assert_eq!(lowercase_1[1], '\0'); + //final_result[2] = lowercase_1[1]; + } + + /*} else { + final_result[2] = lowercase_1[0]; + debug_assert_eq!(lowercase_1[1], '\0'); + debug_assert_eq!(uppercase[2], '\0') + }*/ + } + final_result + }) + } + static LOWERCASE_LUT: L1Lut = L1Lut { l2_luts: [ L2Lut { @@ -1188,4 +1271,24 @@ pub mod conversions { }, ], }; + + static CASEFOLD_LUT: L1Lut = L1Lut { + l2_luts: [ + L2Lut { + singles: &[ // 4 entries, 24 bytes + (Range::singleton(0x0131), 0), (Range::step_by_1(0x13a0..=0x13f5), 0), + (Range::step_by_1(0x13f8..=0x13fd), -8), (Range::step_by_1(0xab70..=0xabbf), 26672), + ], + multis: &[ // 1 entries, 8 bytes + (0x1e9e, [0x0073, 0x0073, 0x0000]), + ], + }, + L2Lut { + singles: &[ // 0 entries, 0 bytes + ], + multis: &[ // 0 entries, 0 bytes + ], + }, + ], + }; } diff --git a/library/coretests/tests/char.rs b/library/coretests/tests/char.rs index 43372005ad5f3..e0921e2f39af5 100644 --- a/library/coretests/tests/char.rs +++ b/library/coretests/tests/char.rs @@ -212,6 +212,41 @@ fn test_to_uppercase() { assert_eq!(upper('ᾀ'), "ἈΙ"); } +#[test] +fn test_to_casefold_unnormalized() { + fn fold(c: char) -> String { + let to_casefold = c.to_casefold_unnormalized(); + assert_eq!(to_casefold.len(), to_casefold.count()); + let iter: String = c.to_casefold_unnormalized().collect(); + let disp: String = c.to_casefold_unnormalized().to_string(); + assert_eq!(iter, disp); + let iter_rev: String = c.to_casefold_unnormalized().rev().collect(); + let disp_rev: String = disp.chars().rev().collect(); + assert_eq!(iter_rev, disp_rev); + iter + } + assert_eq!(fold('A'), "a"); + assert_eq!(fold('Ö'), "ö"); + assert_eq!(fold('ß'), "ss"); + assert_eq!(fold('ẞ'), "ss"); + assert_eq!(fold('Ü'), "ü"); + assert_eq!(fold('💩'), "💩"); + assert_eq!(fold('Σ'), "σ"); + assert_eq!(fold('ς'), "σ"); + assert_eq!(fold('Τ'), "τ"); + assert_eq!(fold('Ι'), "ι"); + assert_eq!(fold('Γ'), "γ"); + assert_eq!(fold('Μ'), "μ"); + assert_eq!(fold('Α'), "α"); + assert_eq!(fold('Dž'), "dž"); + assert_eq!(fold('fi'), "fi"); + assert_eq!(fold('İ'), "i\u{307}"); + assert_eq!(fold('ꮿ'), "Ꮿ"); + assert_eq!(fold('Ꮿ'), "Ꮿ"); + assert_eq!(fold('ῲ'), "ὼι"); + assert_eq!(fold('\u{0345}'), "ι"); +} + #[test] fn test_is_control() { assert!('\u{0}'.is_control()); diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index aa6aa1478bd70..c30ed92b0e946 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -10,6 +10,7 @@ #![feature(async_iterator)] #![feature(borrowed_buf_init)] #![feature(bstr)] +#![feature(casefold)] #![feature(cfg_target_has_reliable_f16_f128)] #![feature(char_internals)] #![feature(clone_to_uninit)] @@ -97,7 +98,6 @@ #![feature(pointer_is_aligned_to)] #![feature(portable_simd)] #![feature(ptr_metadata)] -#![feature(result_option_map_or_default)] #![feature(rustc_attrs)] #![feature(signed_bigint_helpers)] #![feature(slice_from_ptr_range)] diff --git a/library/coretests/tests/unicode.rs b/library/coretests/tests/unicode.rs index 12eed25a1feae..05cea23fd4781 100644 --- a/library/coretests/tests/unicode.rs +++ b/library/coretests/tests/unicode.rs @@ -124,3 +124,22 @@ fn to_titlecase() { unicode_data::conversions::to_upper, ); } + +/// This test verifies some assumptions we currently make about Unicode casings +/// which might be falsified by future versions of the standard. +/// It's important that it gets run with debug assertions enabled, +/// so that the debug assertions in `core/src/unicode/unicode_data.rs` +/// `conversions::to_casefold` get run with every possible Unicode character as input. +#[test] +#[cfg_attr(miri, ignore)] // Miri is too slow +fn to_casefold() { + test_case_mapping(test_data::TO_CASEFOLD, unicode_data::conversions::to_casefold, |c| { + let upper = unicode_data::conversions::to_upper(c); + let lower = upper.map(unicode_data::conversions::to_lower); + let mut result = ['\0'; 3]; + for (i, c) in lower.into_iter().flatten().filter(|&c| c != '\0').enumerate() { + result[i] = c; + } + result + }); +} diff --git a/library/coretests/tests/unicode/test_data.rs b/library/coretests/tests/unicode/test_data.rs index 962770a0ff830..77b976c489c9b 100644 --- a/library/coretests/tests/unicode/test_data.rs +++ b/library/coretests/tests/unicode/test_data.rs @@ -2931,3 +2931,94 @@ pub(super) static TO_TITLE: &[(char, [char; 3]); 135] = &[ ('\u{fb16}', ['\u{54e}', '\u{576}', '\u{0}']), ('\u{fb17}', ['\u{544}', '\u{56d}', '\u{0}']), ]; + +#[rustfmt::skip] +pub(super) static TO_CASEFOLD: &[(char, [char; 3]); 174] = &[ + ('\u{131}', ['\u{131}', '\u{0}', '\u{0}']), ('\u{13a0}', ['\u{13a0}', '\u{0}', '\u{0}']), + ('\u{13a1}', ['\u{13a1}', '\u{0}', '\u{0}']), ('\u{13a2}', ['\u{13a2}', '\u{0}', '\u{0}']), + ('\u{13a3}', ['\u{13a3}', '\u{0}', '\u{0}']), ('\u{13a4}', ['\u{13a4}', '\u{0}', '\u{0}']), + ('\u{13a5}', ['\u{13a5}', '\u{0}', '\u{0}']), ('\u{13a6}', ['\u{13a6}', '\u{0}', '\u{0}']), + ('\u{13a7}', ['\u{13a7}', '\u{0}', '\u{0}']), ('\u{13a8}', ['\u{13a8}', '\u{0}', '\u{0}']), + ('\u{13a9}', ['\u{13a9}', '\u{0}', '\u{0}']), ('\u{13aa}', ['\u{13aa}', '\u{0}', '\u{0}']), + ('\u{13ab}', ['\u{13ab}', '\u{0}', '\u{0}']), ('\u{13ac}', ['\u{13ac}', '\u{0}', '\u{0}']), + ('\u{13ad}', ['\u{13ad}', '\u{0}', '\u{0}']), ('\u{13ae}', ['\u{13ae}', '\u{0}', '\u{0}']), + ('\u{13af}', ['\u{13af}', '\u{0}', '\u{0}']), ('\u{13b0}', ['\u{13b0}', '\u{0}', '\u{0}']), + ('\u{13b1}', ['\u{13b1}', '\u{0}', '\u{0}']), ('\u{13b2}', ['\u{13b2}', '\u{0}', '\u{0}']), + ('\u{13b3}', ['\u{13b3}', '\u{0}', '\u{0}']), ('\u{13b4}', ['\u{13b4}', '\u{0}', '\u{0}']), + ('\u{13b5}', ['\u{13b5}', '\u{0}', '\u{0}']), ('\u{13b6}', ['\u{13b6}', '\u{0}', '\u{0}']), + ('\u{13b7}', ['\u{13b7}', '\u{0}', '\u{0}']), ('\u{13b8}', ['\u{13b8}', '\u{0}', '\u{0}']), + ('\u{13b9}', ['\u{13b9}', '\u{0}', '\u{0}']), ('\u{13ba}', ['\u{13ba}', '\u{0}', '\u{0}']), + ('\u{13bb}', ['\u{13bb}', '\u{0}', '\u{0}']), ('\u{13bc}', ['\u{13bc}', '\u{0}', '\u{0}']), + ('\u{13bd}', ['\u{13bd}', '\u{0}', '\u{0}']), ('\u{13be}', ['\u{13be}', '\u{0}', '\u{0}']), + ('\u{13bf}', ['\u{13bf}', '\u{0}', '\u{0}']), ('\u{13c0}', ['\u{13c0}', '\u{0}', '\u{0}']), + ('\u{13c1}', ['\u{13c1}', '\u{0}', '\u{0}']), ('\u{13c2}', ['\u{13c2}', '\u{0}', '\u{0}']), + ('\u{13c3}', ['\u{13c3}', '\u{0}', '\u{0}']), ('\u{13c4}', ['\u{13c4}', '\u{0}', '\u{0}']), + ('\u{13c5}', ['\u{13c5}', '\u{0}', '\u{0}']), ('\u{13c6}', ['\u{13c6}', '\u{0}', '\u{0}']), + ('\u{13c7}', ['\u{13c7}', '\u{0}', '\u{0}']), ('\u{13c8}', ['\u{13c8}', '\u{0}', '\u{0}']), + ('\u{13c9}', ['\u{13c9}', '\u{0}', '\u{0}']), ('\u{13ca}', ['\u{13ca}', '\u{0}', '\u{0}']), + ('\u{13cb}', ['\u{13cb}', '\u{0}', '\u{0}']), ('\u{13cc}', ['\u{13cc}', '\u{0}', '\u{0}']), + ('\u{13cd}', ['\u{13cd}', '\u{0}', '\u{0}']), ('\u{13ce}', ['\u{13ce}', '\u{0}', '\u{0}']), + ('\u{13cf}', ['\u{13cf}', '\u{0}', '\u{0}']), ('\u{13d0}', ['\u{13d0}', '\u{0}', '\u{0}']), + ('\u{13d1}', ['\u{13d1}', '\u{0}', '\u{0}']), ('\u{13d2}', ['\u{13d2}', '\u{0}', '\u{0}']), + ('\u{13d3}', ['\u{13d3}', '\u{0}', '\u{0}']), ('\u{13d4}', ['\u{13d4}', '\u{0}', '\u{0}']), + ('\u{13d5}', ['\u{13d5}', '\u{0}', '\u{0}']), ('\u{13d6}', ['\u{13d6}', '\u{0}', '\u{0}']), + ('\u{13d7}', ['\u{13d7}', '\u{0}', '\u{0}']), ('\u{13d8}', ['\u{13d8}', '\u{0}', '\u{0}']), + ('\u{13d9}', ['\u{13d9}', '\u{0}', '\u{0}']), ('\u{13da}', ['\u{13da}', '\u{0}', '\u{0}']), + ('\u{13db}', ['\u{13db}', '\u{0}', '\u{0}']), ('\u{13dc}', ['\u{13dc}', '\u{0}', '\u{0}']), + ('\u{13dd}', ['\u{13dd}', '\u{0}', '\u{0}']), ('\u{13de}', ['\u{13de}', '\u{0}', '\u{0}']), + ('\u{13df}', ['\u{13df}', '\u{0}', '\u{0}']), ('\u{13e0}', ['\u{13e0}', '\u{0}', '\u{0}']), + ('\u{13e1}', ['\u{13e1}', '\u{0}', '\u{0}']), ('\u{13e2}', ['\u{13e2}', '\u{0}', '\u{0}']), + ('\u{13e3}', ['\u{13e3}', '\u{0}', '\u{0}']), ('\u{13e4}', ['\u{13e4}', '\u{0}', '\u{0}']), + ('\u{13e5}', ['\u{13e5}', '\u{0}', '\u{0}']), ('\u{13e6}', ['\u{13e6}', '\u{0}', '\u{0}']), + ('\u{13e7}', ['\u{13e7}', '\u{0}', '\u{0}']), ('\u{13e8}', ['\u{13e8}', '\u{0}', '\u{0}']), + ('\u{13e9}', ['\u{13e9}', '\u{0}', '\u{0}']), ('\u{13ea}', ['\u{13ea}', '\u{0}', '\u{0}']), + ('\u{13eb}', ['\u{13eb}', '\u{0}', '\u{0}']), ('\u{13ec}', ['\u{13ec}', '\u{0}', '\u{0}']), + ('\u{13ed}', ['\u{13ed}', '\u{0}', '\u{0}']), ('\u{13ee}', ['\u{13ee}', '\u{0}', '\u{0}']), + ('\u{13ef}', ['\u{13ef}', '\u{0}', '\u{0}']), ('\u{13f0}', ['\u{13f0}', '\u{0}', '\u{0}']), + ('\u{13f1}', ['\u{13f1}', '\u{0}', '\u{0}']), ('\u{13f2}', ['\u{13f2}', '\u{0}', '\u{0}']), + ('\u{13f3}', ['\u{13f3}', '\u{0}', '\u{0}']), ('\u{13f4}', ['\u{13f4}', '\u{0}', '\u{0}']), + ('\u{13f5}', ['\u{13f5}', '\u{0}', '\u{0}']), ('\u{13f8}', ['\u{13f0}', '\u{0}', '\u{0}']), + ('\u{13f9}', ['\u{13f1}', '\u{0}', '\u{0}']), ('\u{13fa}', ['\u{13f2}', '\u{0}', '\u{0}']), + ('\u{13fb}', ['\u{13f3}', '\u{0}', '\u{0}']), ('\u{13fc}', ['\u{13f4}', '\u{0}', '\u{0}']), + ('\u{13fd}', ['\u{13f5}', '\u{0}', '\u{0}']), ('\u{1e9e}', ['s', 's', '\u{0}']), + ('\u{ab70}', ['\u{13a0}', '\u{0}', '\u{0}']), ('\u{ab71}', ['\u{13a1}', '\u{0}', '\u{0}']), + ('\u{ab72}', ['\u{13a2}', '\u{0}', '\u{0}']), ('\u{ab73}', ['\u{13a3}', '\u{0}', '\u{0}']), + ('\u{ab74}', ['\u{13a4}', '\u{0}', '\u{0}']), ('\u{ab75}', ['\u{13a5}', '\u{0}', '\u{0}']), + ('\u{ab76}', ['\u{13a6}', '\u{0}', '\u{0}']), ('\u{ab77}', ['\u{13a7}', '\u{0}', '\u{0}']), + ('\u{ab78}', ['\u{13a8}', '\u{0}', '\u{0}']), ('\u{ab79}', ['\u{13a9}', '\u{0}', '\u{0}']), + ('\u{ab7a}', ['\u{13aa}', '\u{0}', '\u{0}']), ('\u{ab7b}', ['\u{13ab}', '\u{0}', '\u{0}']), + ('\u{ab7c}', ['\u{13ac}', '\u{0}', '\u{0}']), ('\u{ab7d}', ['\u{13ad}', '\u{0}', '\u{0}']), + ('\u{ab7e}', ['\u{13ae}', '\u{0}', '\u{0}']), ('\u{ab7f}', ['\u{13af}', '\u{0}', '\u{0}']), + ('\u{ab80}', ['\u{13b0}', '\u{0}', '\u{0}']), ('\u{ab81}', ['\u{13b1}', '\u{0}', '\u{0}']), + ('\u{ab82}', ['\u{13b2}', '\u{0}', '\u{0}']), ('\u{ab83}', ['\u{13b3}', '\u{0}', '\u{0}']), + ('\u{ab84}', ['\u{13b4}', '\u{0}', '\u{0}']), ('\u{ab85}', ['\u{13b5}', '\u{0}', '\u{0}']), + ('\u{ab86}', ['\u{13b6}', '\u{0}', '\u{0}']), ('\u{ab87}', ['\u{13b7}', '\u{0}', '\u{0}']), + ('\u{ab88}', ['\u{13b8}', '\u{0}', '\u{0}']), ('\u{ab89}', ['\u{13b9}', '\u{0}', '\u{0}']), + ('\u{ab8a}', ['\u{13ba}', '\u{0}', '\u{0}']), ('\u{ab8b}', ['\u{13bb}', '\u{0}', '\u{0}']), + ('\u{ab8c}', ['\u{13bc}', '\u{0}', '\u{0}']), ('\u{ab8d}', ['\u{13bd}', '\u{0}', '\u{0}']), + ('\u{ab8e}', ['\u{13be}', '\u{0}', '\u{0}']), ('\u{ab8f}', ['\u{13bf}', '\u{0}', '\u{0}']), + ('\u{ab90}', ['\u{13c0}', '\u{0}', '\u{0}']), ('\u{ab91}', ['\u{13c1}', '\u{0}', '\u{0}']), + ('\u{ab92}', ['\u{13c2}', '\u{0}', '\u{0}']), ('\u{ab93}', ['\u{13c3}', '\u{0}', '\u{0}']), + ('\u{ab94}', ['\u{13c4}', '\u{0}', '\u{0}']), ('\u{ab95}', ['\u{13c5}', '\u{0}', '\u{0}']), + ('\u{ab96}', ['\u{13c6}', '\u{0}', '\u{0}']), ('\u{ab97}', ['\u{13c7}', '\u{0}', '\u{0}']), + ('\u{ab98}', ['\u{13c8}', '\u{0}', '\u{0}']), ('\u{ab99}', ['\u{13c9}', '\u{0}', '\u{0}']), + ('\u{ab9a}', ['\u{13ca}', '\u{0}', '\u{0}']), ('\u{ab9b}', ['\u{13cb}', '\u{0}', '\u{0}']), + ('\u{ab9c}', ['\u{13cc}', '\u{0}', '\u{0}']), ('\u{ab9d}', ['\u{13cd}', '\u{0}', '\u{0}']), + ('\u{ab9e}', ['\u{13ce}', '\u{0}', '\u{0}']), ('\u{ab9f}', ['\u{13cf}', '\u{0}', '\u{0}']), + ('\u{aba0}', ['\u{13d0}', '\u{0}', '\u{0}']), ('\u{aba1}', ['\u{13d1}', '\u{0}', '\u{0}']), + ('\u{aba2}', ['\u{13d2}', '\u{0}', '\u{0}']), ('\u{aba3}', ['\u{13d3}', '\u{0}', '\u{0}']), + ('\u{aba4}', ['\u{13d4}', '\u{0}', '\u{0}']), ('\u{aba5}', ['\u{13d5}', '\u{0}', '\u{0}']), + ('\u{aba6}', ['\u{13d6}', '\u{0}', '\u{0}']), ('\u{aba7}', ['\u{13d7}', '\u{0}', '\u{0}']), + ('\u{aba8}', ['\u{13d8}', '\u{0}', '\u{0}']), ('\u{aba9}', ['\u{13d9}', '\u{0}', '\u{0}']), + ('\u{abaa}', ['\u{13da}', '\u{0}', '\u{0}']), ('\u{abab}', ['\u{13db}', '\u{0}', '\u{0}']), + ('\u{abac}', ['\u{13dc}', '\u{0}', '\u{0}']), ('\u{abad}', ['\u{13dd}', '\u{0}', '\u{0}']), + ('\u{abae}', ['\u{13de}', '\u{0}', '\u{0}']), ('\u{abaf}', ['\u{13df}', '\u{0}', '\u{0}']), + ('\u{abb0}', ['\u{13e0}', '\u{0}', '\u{0}']), ('\u{abb1}', ['\u{13e1}', '\u{0}', '\u{0}']), + ('\u{abb2}', ['\u{13e2}', '\u{0}', '\u{0}']), ('\u{abb3}', ['\u{13e3}', '\u{0}', '\u{0}']), + ('\u{abb4}', ['\u{13e4}', '\u{0}', '\u{0}']), ('\u{abb5}', ['\u{13e5}', '\u{0}', '\u{0}']), + ('\u{abb6}', ['\u{13e6}', '\u{0}', '\u{0}']), ('\u{abb7}', ['\u{13e7}', '\u{0}', '\u{0}']), + ('\u{abb8}', ['\u{13e8}', '\u{0}', '\u{0}']), ('\u{abb9}', ['\u{13e9}', '\u{0}', '\u{0}']), + ('\u{abba}', ['\u{13ea}', '\u{0}', '\u{0}']), ('\u{abbb}', ['\u{13eb}', '\u{0}', '\u{0}']), + ('\u{abbc}', ['\u{13ec}', '\u{0}', '\u{0}']), ('\u{abbd}', ['\u{13ed}', '\u{0}', '\u{0}']), + ('\u{abbe}', ['\u{13ee}', '\u{0}', '\u{0}']), ('\u{abbf}', ['\u{13ef}', '\u{0}', '\u{0}']), +]; diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 2d1f6a2d7e9b0..cfcaabf80c9c0 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1803,14 +1803,13 @@ impl PathBuf { /// # Examples /// /// ``` - /// #![feature(pathbuf_into_string)] /// use std::path::PathBuf; /// /// let path_buf = PathBuf::from("foo"); /// let string = path_buf.into_string(); /// assert_eq!(string, Ok(String::from("foo"))); /// ``` - #[unstable(feature = "pathbuf_into_string", issue = "156203")] + #[stable(feature = "pathbuf_into_string", since = "CURRENT_RUSTC_VERSION")] pub fn into_string(self) -> Result { self.into_os_string().into_string().map_err(PathBuf::from) } diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index eefec06f68f54..c9221269440d8 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1441,267 +1441,146 @@ impl File { } } - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - ))] pub fn lock(&self) -> io::Result<()> { - cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX) })?; - return Ok(()); - } - - #[cfg(target_os = "solaris")] - pub fn lock(&self) -> io::Result<()> { - let mut flock: libc::flock = unsafe { mem::zeroed() }; - flock.l_type = libc::F_WRLCK as libc::c_short; - flock.l_whence = libc::SEEK_SET as libc::c_short; - cvt(unsafe { libc::fcntl(self.as_raw_fd(), libc::F_SETLKW, &flock) })?; - Ok(()) - } - - #[cfg(not(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "solaris", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - )))] - pub fn lock(&self) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "lock() not supported")) - } - - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - ))] - pub fn lock_shared(&self) -> io::Result<()> { - cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH) })?; - return Ok(()); - } - - #[cfg(target_os = "solaris")] - pub fn lock_shared(&self) -> io::Result<()> { - let mut flock: libc::flock = unsafe { mem::zeroed() }; - flock.l_type = libc::F_RDLCK as libc::c_short; - flock.l_whence = libc::SEEK_SET as libc::c_short; - cvt(unsafe { libc::fcntl(self.as_raw_fd(), libc::F_SETLKW, &flock) })?; - Ok(()) - } - - #[cfg(not(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "solaris", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - )))] - pub fn lock_shared(&self) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "lock_shared() not supported")) - } - - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - ))] - pub fn try_lock(&self) -> Result<(), TryLockError> { - let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) }); - if let Err(err) = result { - if err.kind() == io::ErrorKind::WouldBlock { - Err(TryLockError::WouldBlock) - } else { - Err(TryLockError::Error(err)) + cfg_select! { + any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "illumos", + target_os = "aix", + target_os = "android", + target_vendor = "apple", + ) => { + cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX) })?; + return Ok(()); + } + _ => { + Err(io::const_error!(io::ErrorKind::Unsupported, "lock() not supported")) } - } else { - Ok(()) } } - #[cfg(target_os = "solaris")] - pub fn try_lock(&self) -> Result<(), TryLockError> { - let mut flock: libc::flock = unsafe { mem::zeroed() }; - flock.l_type = libc::F_WRLCK as libc::c_short; - flock.l_whence = libc::SEEK_SET as libc::c_short; - let result = cvt(unsafe { libc::fcntl(self.as_raw_fd(), libc::F_SETLK, &flock) }); - if let Err(err) = result { - if err.kind() == io::ErrorKind::WouldBlock { - Err(TryLockError::WouldBlock) - } else { - Err(TryLockError::Error(err)) + pub fn lock_shared(&self) -> io::Result<()> { + cfg_select! { + any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "illumos", + target_os = "aix", + target_os = "android", + target_vendor = "apple", + ) => { + cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH) })?; + return Ok(()); + } + _ => { + Err(io::const_error!(io::ErrorKind::Unsupported, "lock_shared() not supported")) } - } else { - Ok(()) } } - #[cfg(not(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "solaris", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - )))] pub fn try_lock(&self) -> Result<(), TryLockError> { - Err(TryLockError::Error(io::const_error!( - io::ErrorKind::Unsupported, - "try_lock() not supported" - ))) - } - - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - ))] - pub fn try_lock_shared(&self) -> Result<(), TryLockError> { - let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH | libc::LOCK_NB) }); - if let Err(err) = result { - if err.kind() == io::ErrorKind::WouldBlock { - Err(TryLockError::WouldBlock) - } else { - Err(TryLockError::Error(err)) + cfg_select! { + any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "illumos", + target_os = "aix", + target_os = "android", + target_vendor = "apple", + ) => { + let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) }); + if let Err(err) = result { + if err.kind() == io::ErrorKind::WouldBlock { + Err(TryLockError::WouldBlock) + } else { + Err(TryLockError::Error(err)) + } + } else { + Ok(()) + } + } + _ => { + Err(TryLockError::Error(io::const_error!( + io::ErrorKind::Unsupported, + "try_lock() not supported" + ))) } - } else { - Ok(()) } } - #[cfg(target_os = "solaris")] pub fn try_lock_shared(&self) -> Result<(), TryLockError> { - let mut flock: libc::flock = unsafe { mem::zeroed() }; - flock.l_type = libc::F_RDLCK as libc::c_short; - flock.l_whence = libc::SEEK_SET as libc::c_short; - let result = cvt(unsafe { libc::fcntl(self.as_raw_fd(), libc::F_SETLK, &flock) }); - if let Err(err) = result { - if err.kind() == io::ErrorKind::WouldBlock { - Err(TryLockError::WouldBlock) - } else { - Err(TryLockError::Error(err)) + cfg_select! { + any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "illumos", + target_os = "aix", + target_os = "android", + target_vendor = "apple", + ) => { + let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH | libc::LOCK_NB) }); + if let Err(err) = result { + if err.kind() == io::ErrorKind::WouldBlock { + Err(TryLockError::WouldBlock) + } else { + Err(TryLockError::Error(err)) + } + } else { + Ok(()) + } + } + _ => { + Err(TryLockError::Error(io::const_error!( + io::ErrorKind::Unsupported, + "try_lock_shared() not supported" + ))) } - } else { - Ok(()) } } - #[cfg(not(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "solaris", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - )))] - pub fn try_lock_shared(&self) -> Result<(), TryLockError> { - Err(TryLockError::Error(io::const_error!( - io::ErrorKind::Unsupported, - "try_lock_shared() not supported" - ))) - } - - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - ))] pub fn unlock(&self) -> io::Result<()> { - cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_UN) })?; - return Ok(()); - } - - #[cfg(target_os = "solaris")] - pub fn unlock(&self) -> io::Result<()> { - let mut flock: libc::flock = unsafe { mem::zeroed() }; - flock.l_type = libc::F_UNLCK as libc::c_short; - flock.l_whence = libc::SEEK_SET as libc::c_short; - cvt(unsafe { libc::fcntl(self.as_raw_fd(), libc::F_SETLKW, &flock) })?; - Ok(()) - } - - #[cfg(not(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "solaris", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - )))] - pub fn unlock(&self) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "unlock() not supported")) + cfg_select! { + any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "illumos", + target_os = "aix", + target_os = "android", + target_vendor = "apple", + ) => { + cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_UN) })?; + return Ok(()); + } + _ => { + Err(io::const_error!(io::ErrorKind::Unsupported, "unlock() not supported")) + } + } } pub fn truncate(&self, size: u64) -> io::Result<()> { diff --git a/library/std/src/sys/process/unix/unsupported/wait_status/tests.rs b/library/std/src/sys/process/unix/unsupported/wait_status/tests.rs index 0d9232fac5e4e..51af14f0aaace 100644 --- a/library/std/src/sys/process/unix/unsupported/wait_status/tests.rs +++ b/library/std/src/sys/process/unix/unsupported/wait_status/tests.rs @@ -8,6 +8,7 @@ // I.e. we're using Linux as a proxy for "trad unix". #[cfg(target_os = "linux")] #[test] +#[cfg_attr(miri, ignore)] // Miri is too slow fn compare_with_linux() { use super::ExitStatus as Emulated; use crate::os::unix::process::ExitStatusExt as _; diff --git a/library/std/src/sys/thread_local/guard/windows.rs b/library/std/src/sys/thread_local/guard/windows.rs index 01ac9526910c2..7a9c69ae43784 100644 --- a/library/std/src/sys/thread_local/guard/windows.rs +++ b/library/std/src/sys/thread_local/guard/windows.rs @@ -106,6 +106,7 @@ impl Drop for EnableGuard { } } +/// Set up the current thread to invoke `cleanup` when it finishes. pub fn enable() { let registered = if cfg!(target_thread_local) { #[thread_local] diff --git a/library/std/src/sys/thread_local/key/windows.rs b/library/std/src/sys/thread_local/key/windows.rs index 2ff0fd1196e12..420b628068a3a 100644 --- a/library/std/src/sys/thread_local/key/windows.rs +++ b/library/std/src/sys/thread_local/key/windows.rs @@ -60,6 +60,12 @@ impl LazyKey { #[inline] pub fn force(&'static self) -> Key { + if self.dtor.is_some() { + // Needs to be called on all threads where the key might have a non-null value! + // Otherwise, `run_dtors` might not be called on this thread. + guard::enable(); + } + match self.key.load(Acquire) { 0 => unsafe { self.init() }, key => key - 1, @@ -88,6 +94,8 @@ impl LazyKey { } unsafe { + // Add ourselves to the `DTORS` list, so that when `run_dtors` gets called, + // our dtor is invoked. register_dtor(self); } @@ -144,8 +152,6 @@ static DTORS: Atomic<*mut LazyKey> = AtomicPtr::new(ptr::null_mut()); /// Should only be called once per key, otherwise loops or breaks may occur in /// the linked list. unsafe fn register_dtor(key: &'static LazyKey) { - guard::enable(); - let this = <*const LazyKey>::cast_mut(key); // Use acquire ordering to pass along the changes done by the previously // registered keys when we store the new head with release ordering. diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index e88011aa22dad..d48bb1c8b721e 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -81,7 +81,7 @@ pub(crate) mod destructors { /// This module provides a way to schedule the execution of the destructor list /// and the [runtime cleanup](crate::rt::thread_cleanup) function. Calling `enable` -/// should ensure that these functions are called at the right times. +/// sets up the current thread to ensure that these functions are called at the right times. pub(crate) mod guard { cfg_select! { all(target_thread_local, target_vendor = "apple") => { diff --git a/library/std/src/thread/lifecycle.rs b/library/std/src/thread/lifecycle.rs index af239bee55189..d3a97bbf08fa2 100644 --- a/library/std/src/thread/lifecycle.rs +++ b/library/std/src/thread/lifecycle.rs @@ -66,7 +66,7 @@ where let rust_start = move || { let f = f.into_inner(); let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { - crate::sys::backtrace::__rust_begin_short_backtrace(|| hooks.run()); + crate::sys::backtrace::__rust_begin_short_backtrace(|| hooks.inherit_and_run()); crate::sys::backtrace::__rust_begin_short_backtrace(f) })); // SAFETY: `their_packet` as been built just above and moved by the diff --git a/library/std/src/thread/spawnhook.rs b/library/std/src/thread/spawnhook.rs index 254793ac33d08..bb36fb687b4af 100644 --- a/library/std/src/thread/spawnhook.rs +++ b/library/std/src/thread/spawnhook.rs @@ -144,7 +144,7 @@ pub(super) struct ChildSpawnHooks { impl ChildSpawnHooks { // This is run on the newly spawned thread, directly at the start. - pub(super) fn run(self) { + pub(super) fn inherit_and_run(self) { SPAWN_HOOKS.set(self.hooks); for run in self.to_run { run(); diff --git a/library/std/tests/thread_local/tests.rs b/library/std/tests/thread_local/tests.rs index 7324b880af67d..633bab38eb4db 100644 --- a/library/std/tests/thread_local/tests.rs +++ b/library/std/tests/thread_local/tests.rs @@ -408,6 +408,7 @@ fn thread_current_in_dtor() { // https://github.com/rust-lang/rust/pull/148799#issuecomment-3731806901 #[cfg(target_os = "windows")] #[test] +#[cfg_attr(miri, ignore)] // Miri does not support fibers fn fiber_does_not_trigger_dtor() { use core::ffi::c_void; use std::ptr; diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs index ead6e0827501d..6967c7f7f2665 100644 --- a/library/unwind/src/libunwind.rs +++ b/library/unwind/src/libunwind.rs @@ -72,7 +72,7 @@ pub const unwinder_private_data_size: usize = 2; #[cfg(any(target_arch = "riscv64", target_arch = "riscv32"))] pub const unwinder_private_data_size: usize = 2; -#[cfg(all(target_arch = "wasm32", target_os = "emscripten"))] +#[cfg(all(target_family = "wasm", target_os = "emscripten"))] pub const unwinder_private_data_size: usize = 20; #[cfg(all(target_arch = "wasm32", any(target_os = "linux", target_os = "wasi")))] diff --git a/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile index 7ca2dbb7d9f36..2c9788a0607c3 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 +FROM ubuntu:26.04 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 72817ad64521a..79e04b12fbed3 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1645,6 +1645,11 @@ impl<'test> TestCx<'test> { if self.config.mode == TestMode::CodegenUnits { compiler.args(&["-Z", "human_readable_cgu_names"]); } + + if self.config.mode == TestMode::DebugInfo && cfg!(target_os = "windows") { + // Prevent debugger processes from creating new console windows. + compiler.args(&["-Z", r#"crate-attr=windows_subsystem="windows""#]); + } } if self.config.optimize_tests && compiler_kind == CompilerKind::Rustc { diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index cb1088fb08515..f7298a9a2f235 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -410,8 +410,7 @@ fn test_pread_pwrite() { assert_eq!(&buf1, b" m"); } -// Miri does not support the way this is implemented on Solaris -// (https://github.com/rust-lang/miri/issues/5038). +// Solaris does not support per-handle file locking. #[cfg(not(target_os = "solaris"))] fn test_flock() { let bytes = b"Hello, World!\n"; diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index b9427e1927f6c..ca9e9719add48 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -56,7 +56,7 @@ jobs: - name: Install Rust toolchain run: | RUSTC_VERSION=$(cat rust-version) - rustup-toolchain-install-master ${RUSTC_VERSION} -c cargo -c rust-src -c rustfmt + rustup-toolchain-install-master ${RUSTC_VERSION} -c cargo -c rust-src -c rustfmt -c rustc-dev -c llvm-tools rustup default ${RUSTC_VERSION} # Emulate a nightly toolchain, because the toolchain installed above does not have "nightly" @@ -69,10 +69,10 @@ jobs: run: echo "::add-matcher::.github/rust.json" - name: Test - run: cargo test --features sysroot-abi -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api -- --quiet + run: cargo test --features in-rust-tree -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api -- --quiet - name: Check salsa dependency - run: "! (cargo tree -p proc-macro-srv-cli | grep -q salsa)" + run: "! (cargo tree -p proc-macro-srv-cli -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api -i salsa)" rust: if: github.repository == 'rust-lang/rust-analyzer' diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index cbbeef00309e3..440ceb362dabd 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1887,7 +1887,6 @@ dependencies = [ "object", "paths", "proc-macro-test", - "ra-ap-rustc_lexer", "span", "temp-dir", ] @@ -1939,7 +1938,6 @@ dependencies = [ name = "profile" version = "0.0.0" dependencies = [ - "cfg-if", "libc", "perf-event", "tikv-jemalloc-ctl", diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index a209a0e631e0b..ff439119c9177 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -7,6 +7,7 @@ extern crate rustc_driver as _; pub use salsa; pub use salsa_macros; +use span::TextSize; // FIXME: Rename this crate, base db is non descriptive mod change; @@ -49,6 +50,7 @@ macro_rules! impl_intern_key { #[salsa_macros::interned(no_lifetime, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct $id { + #[returns(ref)] pub loc: $loc, } @@ -280,6 +282,8 @@ pub trait SourceDatabase: salsa::Database { fn crates_map(&self) -> Arc; fn nonce_and_revision(&self) -> (Nonce, salsa::Revision); + + fn line_column(&self, file: FileId, offset: TextSize) -> Result<(u32, u32), ()>; } static NEXT_NONCE: AtomicUsize = AtomicUsize::new(0); diff --git a/src/tools/rust-analyzer/crates/cfg/src/lib.rs b/src/tools/rust-analyzer/crates/cfg/src/lib.rs index 3e3d67cb4aaf3..5923849d62c22 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/lib.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/lib.rs @@ -222,7 +222,7 @@ impl fmt::Display for CfgDiff { for (i, atom) in self.disable.iter().enumerate() { let sep = match i { 0 => "", - _ if i == self.enable.len() - 1 => " and ", + _ if i == self.disable.len() - 1 => " and ", _ => ", ", }; f.write_str(sep)?; diff --git a/src/tools/rust-analyzer/crates/cfg/src/tests.rs b/src/tools/rust-analyzer/crates/cfg/src/tests.rs index bfc9220a05d9b..45cba042b33be 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/tests.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/tests.rs @@ -194,6 +194,10 @@ fn hints() { check_enable_hints("#![cfg(test)]", &opts, &[]); check_enable_hints("#![cfg(not(test))]", &opts, &["disable test"]); + + opts.insert_atom(Symbol::intern("a")); + opts.insert_atom(Symbol::intern("b")); + check_enable_hints("#![cfg(all(not(a), not(b)))]", &opts, &["disable a and b"]); } /// Tests that we don't suggest hints for cfgs that express an inconsistent formula. diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index 11e5c54246440..1776b7c84b4be 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -7,8 +7,7 @@ use hir_expand::{ use triomphe::Arc; use crate::{ - AssocItemId, AttrDefId, Macro2Loc, MacroExpander, MacroId, MacroRulesLoc, MacroRulesLocFlags, - TraitId, + AssocItemId, AttrDefId, MacroExpander, MacroId, MacroRulesLocFlags, TraitId, attrs::AttrFlags, item_tree::{ItemTree, file_item_tree}, nameres::crate_def_map, @@ -76,12 +75,13 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { MacroExpander::BuiltInAttr(it) => MacroDefKind::BuiltInAttr(in_file, it), MacroExpander::BuiltInDerive(it) => MacroDefKind::BuiltInDerive(in_file, it), MacroExpander::BuiltInEager(it) => MacroDefKind::BuiltInEager(in_file, it), + MacroExpander::UnimplementedBuiltIn => MacroDefKind::UnimplementedBuiltIn(in_file), } }; match id { MacroId::Macro2Id(it) => { - let loc: Macro2Loc = it.lookup(db); + let loc = it.lookup(db); MacroDefId { krate: loc.container.krate(db), @@ -92,7 +92,7 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { } } MacroId::MacroRulesId(it) => { - let loc: MacroRulesLoc = it.lookup(db); + let loc = it.lookup(db); MacroDefId { krate: loc.container.krate(db), 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 4dc72672314cc..8768413ce554f 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 @@ -332,6 +332,7 @@ pub enum ExpressionStoreDiagnostics { AwaitOutsideOfAsync { node: InFile>, location: String }, UndeclaredLabel { node: InFile>, name: Name }, PatternArgInExternFn { node: InFile> }, + FruInDestructuringAssignment { node: InFile> }, } impl ExpressionStoreBuilder { @@ -616,7 +617,7 @@ impl ExpressionStore { visitor.on_expr_opt(*end); } Pat::Lit(expr) | Pat::ConstBlock(expr) | Pat::Expr(expr) => visitor.on_expr(*expr), - Pat::Path(_) | Pat::Wild | Pat::Missing | Pat::Rest => {} + Pat::Path(_) | Pat::Wild | Pat::Missing | Pat::Rest | Pat::NotNull => {} &Pat::Bind { subpat, id: _ } => visitor.on_pat_opt(subpat), Pat::Or(args) | Pat::Tuple { args, ellipsis: _ } => visitor.on_pats(args), Pat::TupleStruct { args, ellipsis: _, path } => { @@ -719,7 +720,7 @@ impl ExpressionStore { } visitor.on_expr_opt(*tail); } - Expr::Loop { body, label: _ } => visitor.on_expr(*body), + Expr::Loop { body, label: _, source: _ } => visitor.on_expr(*body), Expr::Call { callee, args } => { visitor.on_expr(*callee); visitor.on_exprs(args); @@ -855,6 +856,10 @@ impl ExpressionStore { pub fn visit_type_ref_children(&self, type_ref: TypeRefId, mut visitor: impl StoreVisitor) { match &self[type_ref] { TypeRef::Never | TypeRef::Placeholder | TypeRef::TypeParam(_) | TypeRef::Error => {} + &TypeRef::PatternType(ty, pat) => { + visitor.on_type(ty); + visitor.on_pat(pat) + } TypeRef::Tuple(types) => visitor.on_types(types), TypeRef::Path(path) => visitor.on_path(path), TypeRef::RawPtr(inner, _) | TypeRef::Slice(inner) => visitor.on_type(*inner), 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 2fb47e59c546f..01423d5109d03 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 @@ -9,7 +9,7 @@ use syntax::ast; use triomphe::Arc; use crate::{ - DefWithBodyId, HasModule, + DefWithBodyId, ExpressionStoreOwnerId, HasModule, db::DefDatabase, expr_store::{ ExpressionStore, ExpressionStoreSourceMap, SelfParamPtr, lower::lower_body, pretty, @@ -160,12 +160,12 @@ impl Body { pub fn pretty_print_pat( &self, db: &dyn DefDatabase, - owner: DefWithBodyId, + owner: ExpressionStoreOwnerId, pat: PatId, oneline: bool, edition: Edition, ) -> String { - pretty::print_pat_hir(db, self, owner.into(), pat, oneline, edition) + pretty::print_pat_hir(db, self, owner, pat, oneline, edition) } } 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 8818096500242..242a0b0b4ff92 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 @@ -35,8 +35,8 @@ use thin_vec::ThinVec; use tt::TextRange; use crate::{ - AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, ItemContainerId, - MacroId, ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro, + AdtId, BlockId, BlockLoc, ConstId, DefWithBodyId, FunctionId, GenericDefId, ImplId, + ItemContainerId, MacroId, ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro, attrs::AttrFlags, db::DefDatabase, expr_store::{ @@ -49,9 +49,9 @@ use crate::{ }, hir::{ Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, - CoroutineKind, CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, - Movability, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, Statement, - generics::GenericParams, + CoroutineKind, CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, LoopSource, + MatchArm, Movability, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, + Statement, generics::GenericParams, }, item_scope::BuiltinShadowMode, item_tree::FieldsShape, @@ -719,6 +719,10 @@ impl<'db> ExprCollector<'db> { ast::Type::DynTraitType(inner) => TypeRef::DynTrait( self.type_bounds_from_ast(inner.type_bound_list(), impl_trait_lower_fn), ), + ast::Type::PatternType(inner) => TypeRef::PatternType( + self.lower_type_ref_opt(inner.ty(), impl_trait_lower_fn), + self.collect_ty_pat_opt(inner.pat()), + ), ast::Type::MacroType(mt) => match mt.macro_call() { Some(mcall) => { let macro_ptr = AstPtr::new(&mcall); @@ -1347,7 +1351,7 @@ impl<'db> ExprCollector<'db> { (self.hygiene_id_for(label.syntax().text_range()), self.collect_label(label)) }); let body = self.collect_labelled_block_opt(label, e.loop_body()); - self.alloc_expr(Expr::Loop { body, label: label.map(|it| it.1) }, syntax_ptr) + self.alloc_expr(Expr::Loop { body, label: label.map(|it| it.1), source: LoopSource::Loop }, syntax_ptr) } ast::Expr::WhileExpr(e) => self.collect_while_loop(syntax_ptr, e), ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e), @@ -1890,9 +1894,13 @@ impl<'db> ExprCollector<'db> { }; let record_field_list = e.record_expr_field_list()?; let ellipsis = record_field_list.dotdot_token().is_some(); - // We wanted to emit an error here if `record_field_list.spread().is_some()`, - // but that's already a syntax error in rustc, so we decided not to. - // See https://github.com/rust-lang/rust-analyzer/pull/22206#discussion_r3156097370 + if let Some(spread) = record_field_list.spread() { + self.store.diagnostics.push( + ExpressionStoreDiagnostics::FruInDestructuringAssignment { + node: self.expander.in_file(AstPtr::new(&spread)), + }, + ); + } let args = record_field_list .fields() .filter_map(|f| { @@ -2125,7 +2133,10 @@ impl<'db> ExprCollector<'db> { Expr::If { condition, then_branch: body, else_branch: Some(break_expr) }, syntax_ptr, ); - self.alloc_expr(Expr::Loop { body: if_expr, label: label.map(|it| it.1) }, syntax_ptr) + self.alloc_expr( + Expr::Loop { body: if_expr, label: label.map(|it| it.1), source: LoopSource::While }, + syntax_ptr, + ) } /// Desugar `ast::ForExpr` from: `[opt_ident]: for in ` into: @@ -2151,16 +2162,21 @@ impl<'db> ExprCollector<'db> { ) else { return self.missing_expr(); }; - let head = self.collect_expr_opt(e.iterable()); - let into_iter_fn_expr = self.alloc_expr(Expr::Path(into_iter_fn), syntax_ptr); - let iterator = self.alloc_expr( + let iterable = e.iterable(); + let syntax_ptr_iterable = iterable.as_ref().map_or(syntax_ptr, AstPtr::new); + let loop_body = e.loop_body().map(|it| it.into()); + let head = self.collect_expr_opt(iterable); + let into_iter_fn_expr = + self.alloc_expr_desugared_with_ptr(Expr::Path(into_iter_fn), syntax_ptr_iterable); + let iterator = self.alloc_expr_desugared_with_ptr( Expr::Call { callee: into_iter_fn_expr, args: Box::new([head]) }, - syntax_ptr, + syntax_ptr_iterable, ); let none_arm = MatchArm { pat: self.alloc_pat_desugared(Pat::Path(option_none)), guard: None, - expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr), + expr: self + .alloc_expr_desugared_with_ptr(Expr::Break { expr: None, label: None }, syntax_ptr), }; let some_pat = Pat::TupleStruct { path: option_some, @@ -2173,26 +2189,26 @@ impl<'db> ExprCollector<'db> { let some_arm = MatchArm { pat: self.alloc_pat_desugared(some_pat), guard: None, - expr: self.with_opt_labeled_rib(label, |this| { - this.collect_expr_opt(e.loop_body().map(|it| it.into())) - }), + expr: self.with_opt_labeled_rib(label, |this| this.collect_expr_opt(loop_body)), }; let iter_name = self.generate_new_name(); - let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr); - let iter_expr_mut = self.alloc_expr( + let iter_expr = self + .alloc_expr_desugared_with_ptr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr); + let iter_expr_mut = self.alloc_expr_desugared_with_ptr( Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut }, syntax_ptr, ); - let iter_next_fn_expr = self.alloc_expr(Expr::Path(iter_next_fn), syntax_ptr); - let iter_next_expr = self.alloc_expr( + let iter_next_fn_expr = + self.alloc_expr_desugared_with_ptr(Expr::Path(iter_next_fn), syntax_ptr); + let iter_next_expr = self.alloc_expr_desugared_with_ptr( Expr::Call { callee: iter_next_fn_expr, args: Box::new([iter_expr_mut]) }, syntax_ptr, ); - let loop_inner = self.alloc_expr( + let loop_inner = self.alloc_expr_desugared_with_ptr( Expr::Match { expr: iter_next_expr, arms: Box::new([none_arm, some_arm]) }, syntax_ptr, ); - let loop_inner = self.alloc_expr( + let loop_inner = self.alloc_expr_desugared_with_ptr( Expr::Block { id: None, statements: Box::default(), @@ -2201,8 +2217,14 @@ impl<'db> ExprCollector<'db> { }, syntax_ptr, ); - let loop_outer = self - .alloc_expr(Expr::Loop { body: loop_inner, label: label.map(|it| it.1) }, syntax_ptr); + let loop_outer = self.alloc_expr_desugared_with_ptr( + Expr::Loop { + body: loop_inner, + label: label.map(|it| it.1), + source: LoopSource::ForLoop, + }, + syntax_ptr, + ); let iter_binding = self.alloc_binding(iter_name, BindingAnnotation::Mutable, HygieneId::ROOT); let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None }); @@ -2562,9 +2584,9 @@ impl<'db> ExprCollector<'db> { } fn collect_extern_fn_param(&mut self, pat: Option) -> PatId { - // `extern` functions cannot have pattern-matched parameters, and furthermore, the identifiers - // in their parameters are always interpreted as bindings, even if in a normal function they - // won't be, because they would refer to a path pattern. + // parameters of functions in `extern` blocks can only be simple identifiers and wildcards. + // Furthermore, the identifiers in their parameters are always interpreted as bindings, even + // if in a normal function they won't be, because they would refer to a path pattern. let Some(pat) = pat else { return self.missing_pat() }; match &pat { @@ -2580,6 +2602,7 @@ impl<'db> ExprCollector<'db> { self.add_definition_to_binding(binding, pat); pat } + ast::Pat::WildcardPat(_) => self.alloc_pat(Pat::Wild, AstPtr::new(&pat)), _ => { self.store.diagnostics.push(ExpressionStoreDiagnostics::PatternArgInExternFn { node: self.expander.in_file(AstPtr::new(&pat)), @@ -2782,6 +2805,7 @@ impl<'db> ExprCollector<'db> { let inner = self.collect_pat_opt(inner.pat(), binding_list); Pat::Deref { inner } } + ast::Pat::NotNull(_) => Pat::NotNull, ast::Pat::ConstBlockPat(const_block_pat) => { if let Some(block) = const_block_pat.block_expr() { let expr_id = self.with_label_rib(RibKind::Constant, |this| { @@ -2907,6 +2931,114 @@ impl<'db> ExprCollector<'db> { } } + fn collect_ty_pat_opt(&mut self, pat: Option) -> PatId { + match pat { + Some(pat) => self.collect_ty_pat(pat), + None => self.missing_pat(), + } + } + + fn collect_ty_pat(&mut self, pat: ast::Pat) -> PatId { + let ptr = AstPtr::new(&pat); + match pat { + ast::Pat::NotNull(_) => self.alloc_pat(Pat::NotNull, ptr), + ast::Pat::OrPat(pat) => { + let pat = pat.pats().map(|pat| self.collect_ty_pat(pat)).collect(); + self.alloc_pat(Pat::Or(pat), ptr) + } + ast::Pat::RangePat(range_pat) => { + let start = range_pat + .start() + .map(|pat| { + self.with_fresh_binding_expr_root(|this| this.lower_ty_pat_range_side(pat)) + }) + .unwrap_or_else(|| self.lower_ty_pat_range_end(self.lang_items().RangeMin)); + let end = range_pat + .end() + .map(|pat| match range_pat.op_kind() { + Some(ast::RangeOp::Inclusive) | None => self + .with_fresh_binding_expr_root(|this| this.lower_ty_pat_range_side(pat)), + Some(ast::RangeOp::Exclusive) => self.lower_excluded_range_end(pat), + }) + .unwrap_or_else(|| self.lower_ty_pat_range_end(self.lang_items().RangeMax)); + self.alloc_pat( + Pat::Range { + start: Some(start), + end: Some(end), + range_type: ast::RangeOp::Inclusive, + }, + ptr, + ) + } + ast::Pat::MacroPat(pat) => { + let Some(call) = pat.macro_call() else { return self.missing_pat() }; + let ptr = AstPtr::new(&call); + self.collect_macro_call(call, ptr, true, |this, pat| this.collect_ty_pat_opt(pat)) + } + _ => { + // FIXME: Emit an error. + self.alloc_pat(Pat::Missing, ptr) + } + } + } + + fn lower_ty_pat_range_side(&mut self, pat: ast::Pat) -> ExprId { + let ptr = AstPtr::new(&pat); + match &pat { + ast::Pat::LiteralPat(it) => { + let Some((literal, _)) = pat_literal_to_hir(it) else { return self.missing_expr() }; + self.alloc_expr_from_pat(Expr::Literal(literal), ptr) + } + ast::Pat::ConstBlockPat(it) => { + if let Some(block) = it.block_expr() { + let expr_id = self.with_label_rib(RibKind::Constant, |this| { + this.with_binding_owner(|this| this.collect_block(block)) + }); + self.alloc_expr_from_pat(Expr::Const(expr_id), ptr) + } else { + self.missing_expr() + } + } + ast::Pat::PathPat(it) => { + let path = it + .path() + .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)); + self.alloc_expr_from_pat(path.map(Expr::Path).unwrap_or(Expr::Missing), ptr) + } + ast::Pat::IdentPat(it) if it.is_simple_ident() => { + let name = it.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); + self.alloc_expr_from_pat(Expr::Path(name.into()), ptr) + } + _ => self.missing_expr(), // FIXME: Emit an error. + } + } + + /// When a range has no end specified (`1..` or `1..=`) or no start specified (`..5` or `..=5`), + /// we instead use a constant of the MAX/MIN of the type. + /// This way the type system does not have to handle the lack of a start/end. + fn lower_ty_pat_range_end(&mut self, lang_item: Option) -> ExprId { + self.with_fresh_binding_expr_root(|this| { + this.alloc_expr_desugared( + this.lang_path(lang_item).map(Expr::Path).unwrap_or(Expr::Missing), + ) + }) + } + + /// Lowers the range end of an exclusive range (`2..5`) to an inclusive range 2..=(5 - 1). + /// This way the type system doesn't have to handle the distinction between inclusive/exclusive ranges. + fn lower_excluded_range_end(&mut self, pat: ast::Pat) -> ExprId { + self.with_fresh_binding_expr_root(|this| { + let excluded_end = this.lower_ty_pat_range_side(pat); + let range_sub_path = + this.lang_path(this.lang_items().RangeSub).map(Expr::Path).unwrap_or(Expr::Missing); + let range_sub_path = this.alloc_expr_desugared(range_sub_path); + this.alloc_expr_desugared(Expr::Call { + callee: range_sub_path, + args: Box::new([excluded_end]), + }) + }) + } + // endregion: patterns /// Returns `false` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `true` when 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 34cedbd728f9a..293adfc9bd45e 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 @@ -567,7 +567,7 @@ impl Printer<'_> { w!(self, " = "); self.print_expr_in(prec, *expr); } - Expr::Loop { body, label } => { + Expr::Loop { body, label, source: _ } => { if let Some(lbl) = label { w!(self, "{}: ", self.store[*lbl].name.display(self.db, self.edition)); } @@ -906,6 +906,7 @@ impl Printer<'_> { Pat::Missing => w!(self, "�"), Pat::Rest => w!(self, ".."), Pat::Wild => w!(self, "_"), + Pat::NotNull => w!(self, "!null"), Pat::Tuple { args, ellipsis } => { w!(self, "("); for (i, pat) in args.iter().enumerate() { @@ -1134,6 +1135,7 @@ impl Printer<'_> { LangItemTarget::TypeAliasId(it) => write_name!(it), LangItemTarget::TraitId(it) => write_name!(it), LangItemTarget::EnumVariantId(it) => write_name!(it), + LangItemTarget::ConstId(it) => write_name!(it), } if let Some(s) = s { @@ -1346,6 +1348,11 @@ impl Printer<'_> { w!(self, "dyn "); self.print_type_bounds(bounds); } + TypeRef::PatternType(ty, pat) => { + self.print_type_ref(*ty); + w!(self, " is "); + self.print_pat(*pat); + } } } 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 ddb828513775c..7a2c8dc3ffc7f 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 @@ -350,7 +350,7 @@ fn compute_expr_scopes( Expr::Unsafe { id, statements, tail } => { handle_block(*id, statements, *tail, None, scopes, scope, const_scope); } - Expr::Loop { body: body_expr, label } => { + Expr::Loop { body: body_expr, label, source: _ } => { let mut scope = scopes.new_labeled_scope(*scope, make_label(*label)); compute_expr_scopes(scopes, *body_expr, &mut scope, const_scope); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs index 71fcced2d85b9..906c4835c7959 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs @@ -199,6 +199,11 @@ fn f() { 4401, ), ), + containing_module_inside_def_map: None, + name_or_empty: Name { + symbol: "", + ctx: (), + }, }"#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index 2a1b7f7cb0cf8..6aaf8a674eee9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -142,9 +142,7 @@ fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Opt // - if the item is an enum variant, refer to it via the enum let loc = variant.lookup(ctx.db); if let Some(mut path) = find_path_inner(ctx, ItemInNs::Types(loc.parent.into()), max_len) { - path.push_segment( - loc.parent.enum_variants(ctx.db).variants[loc.index as usize].1.clone(), - ); + path.push_segment(loc.name.clone()); return Some(path); } // If this doesn't work, it seems we have no way of referring to the 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 6eba85926403b..2f7724c72e229 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -224,6 +224,7 @@ pub enum Expr { Loop { body: ExprId, label: Option, + source: LoopSource, }, Call { callee: ExprId, @@ -389,6 +390,17 @@ impl Expr { } } +/// The loop type that yielded an `Expr::Loop`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LoopSource { + /// A `loop { .. }` loop. + Loop, + /// A `while _ { .. }` loop. + While, + /// A `for _ in _ { .. }` loop. + ForLoop, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct OffsetOf { pub container: TypeRefId, @@ -717,6 +729,7 @@ pub enum Pat { Deref { inner: PatId, }, + NotNull, ConstBlock(ExprId), /// An expression inside a pattern. That can only occur inside assignments. /// @@ -734,7 +747,8 @@ impl Pat { | Pat::Wild | Pat::Missing | Pat::Rest - | Pat::Expr(_) => {} + | Pat::Expr(_) + | Pat::NotNull => {} Pat::Bind { subpat, .. } => { subpat.iter().copied().for_each(f); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs index 43e29c1f5ffcf..6cd8377b5fc6f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs @@ -9,7 +9,7 @@ use thin_vec::ThinVec; use crate::{ LifetimeParamId, TypeParamId, expr_store::{ExpressionStore, path::Path}, - hir::ExprId, + hir::{ExprId, PatId}, }; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -139,6 +139,7 @@ pub enum TypeRef { ImplTrait(ThinVec), DynTrait(ThinVec), TypeParam(TypeParamId), + PatternType(TypeRefId, PatId), Error, } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index ba077b1b2ef5d..f8be211bf92a4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -12,7 +12,7 @@ use span::Edition; use stdx::format_to; use crate::{ - AssocItemId, AttrDefId, Complete, FxIndexMap, ModuleDefId, ModuleId, TraitId, + AdtId, AssocItemId, AttrDefId, Complete, EnumId, FxIndexMap, ModuleDefId, ModuleId, TraitId, attrs::AttrFlags, db::DefDatabase, item_scope::{ImportOrExternCrate, ItemInNs}, @@ -208,7 +208,8 @@ impl ImportMap { complete: do_not_complete, }; - if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() { + let module_def = item.as_module_def_id(); + if let Some(ModuleDefId::TraitId(tr)) = module_def { Self::collect_trait_assoc_items( db, &mut map, @@ -216,6 +217,8 @@ impl ImportMap { matches!(item, ItemInNs::Types(_)), &import_info, ); + } else if let Some(ModuleDefId::AdtId(AdtId::EnumId(enum_))) = module_def { + Self::collect_enum_variants(db, &mut map, enum_, &import_info); } let (infos, _) = @@ -224,7 +227,7 @@ impl ImportMap { infos.push(import_info); // If we've just added a module, descend into it. - if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { + if let Some(ModuleDefId::ModuleId(mod_id)) = module_def { worklist.push(mod_id); } } @@ -234,6 +237,33 @@ impl ImportMap { map } + fn collect_enum_variants( + db: &dyn DefDatabase, + map: &mut ImportMapIndex, + enum_: EnumId, + enum_import_info: &ImportInfo, + ) { + let _p = tracing::info_span!("collect_enum_variants").entered(); + for (variant_name, &(variant, _)) in &enum_.enum_variants(db).variants { + let attr_id = variant.into(); + let attrs = AttrFlags::query(db, attr_id); + let do_not_complete = Complete::extract(false, attrs); + let variant_info = ImportInfo { + container: enum_import_info.container, + name: variant_name.clone(), + is_doc_hidden: attrs.contains(AttrFlags::IS_DOC_HIDDEN), + is_unstable: attrs.contains(AttrFlags::IS_UNSTABLE), + complete: do_not_complete, + }; + + let (infos, _) = map + .entry(ItemInNs::Types(variant.into())) + .or_insert_with(|| (SmallVec::new(), IsTraitAssocItem::No)); + infos.reserve_exact(1); + infos.push(variant_info); + } + } + fn collect_trait_assoc_items( db: &dyn DefDatabase, map: &mut ImportMapIndex, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index c2cbb9eda7268..f9d5a843bd8c9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -7,7 +7,7 @@ use intern::{Symbol, sym}; use stdx::impl_from; use crate::{ - AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, + AdtId, AssocItemId, AttrDefId, ConstId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, ItemContainerId, MacroId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, attrs::AttrFlags, db::DefDatabase, @@ -25,10 +25,11 @@ pub enum LangItemTarget { TypeAliasId(TypeAliasId), TraitId(TraitId), EnumVariantId(EnumVariantId), + ConstId(ConstId), } impl_from!( - EnumId, FunctionId, ImplId, StaticId, StructId, UnionId, TypeAliasId, TraitId, EnumVariantId for LangItemTarget + EnumId, FunctionId, ImplId, StaticId, StructId, UnionId, TypeAliasId, TraitId, EnumVariantId, ConstId for LangItemTarget ); /// Salsa query. This will look for lang items in a specific crate. @@ -51,7 +52,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option lang_items.collect_lang_item(db, f), AssocItemId::TypeAliasId(t) => lang_items.collect_lang_item(db, t), - AssocItemId::ConstId(_) => (), + AssocItemId::ConstId(c) => lang_items.collect_lang_item(db, c), } } } @@ -68,13 +69,13 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option { lang_items.collect_lang_item(db, alias) } - AssocItemId::ConstId(_) => {} + AssocItemId::ConstId(c) => lang_items.collect_lang_item(db, c), } }); } ModuleDefId::AdtId(AdtId::EnumId(e)) => { lang_items.collect_lang_item(db, e); - e.enum_variants(db).variants.iter().for_each(|&(id, _, _)| { + e.enum_variants(db).variants.values().for_each(|&(id, _)| { lang_items.collect_lang_item(db, id); }); } @@ -93,6 +94,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option { lang_items.collect_lang_item(db, t); } + ModuleDefId::ConstId(c) => lang_items.collect_lang_item(db, c), _ => {} } } @@ -646,6 +648,10 @@ language_item_table! { LangItems => FieldBase, sym::field_base, TypeAliasId; FieldType, sym::field_type, TypeAliasId; + RangeMin, sym::RangeMin, ConstId; + RangeMax, sym::RangeMax, ConstId; + RangeSub, sym::RangeSub, FunctionId; + @non_lang_core_traits: core::default, Default; core::fmt, Debug; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 3aaf89102fab9..a1bb82e7f25ce 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -48,7 +48,7 @@ pub mod find_path; pub mod import_map; pub mod visibility; -use intern::Interned; +use intern::{Interned, sym}; use rustc_abi::ExternAbi; use thin_vec::ThinVec; @@ -298,7 +298,7 @@ impl EnumId { pub fn enum_variants_with_diagnostics( self, db: &dyn DefDatabase, - ) -> &(EnumVariants, Option>) { + ) -> &(EnumVariants, ThinVec) { EnumVariants::of(db, self) } } @@ -367,20 +367,35 @@ impl ExternBlockId { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct EnumVariantLoc { pub id: AstId, pub parent: EnumId, - pub index: u32, + pub name: Name, } impl_intern!(EnumVariantId, EnumVariantLoc); impl_loc!(EnumVariantLoc, id: Variant, parent: EnumId); +impl EnumVariantLoc { + pub fn index(&self, db: &dyn DefDatabase) -> usize { + self.parent + .enum_variants(db) + .variants + .get_full(&self.name) + .expect("parent enum should include this variant") + .0 + } +} + impl EnumVariantId { pub fn fields(self, db: &dyn DefDatabase) -> &VariantFields { VariantFields::of(db, self.into()) } + pub fn index(self, db: &dyn DefDatabase) -> usize { + self.loc(db).index(db) + } + pub fn fields_with_source_map( self, db: &dyn DefDatabase, @@ -427,6 +442,7 @@ pub enum MacroExpander { BuiltInAttr(BuiltinAttrExpander), BuiltInDerive(BuiltinDeriveExpander), BuiltInEager(EagerExpander), + UnimplementedBuiltIn, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -457,6 +473,11 @@ pub struct ModuleIdLt<'db> { /// `BlockId` of that block expression. If `None`, this module is part of the crate-level /// `DefMap` of `krate`. pub block: Option, + /// The parent module of this module, or `None` if this is the root module inside the def + /// map (including for block def maps). + pub containing_module_inside_def_map: Option>, + /// The name of this module, or [`sym::__empty`] for the root module. + name_or_empty: Name, } pub type ModuleId = ModuleIdLt<'static>; @@ -502,21 +523,23 @@ impl ModuleId { } pub fn name(self, db: &dyn DefDatabase) -> Option { - let def_map = self.def_map(db); - let parent = def_map[self].parent?; - def_map[parent].children.iter().find_map(|(name, module_id)| { - if *module_id == self { Some(name.clone()) } else { None } - }) + let name = self.name_or_empty(db); + if *name.symbol() == sym::__empty { None } else { Some(name) } } /// Returns the module containing `self`, either the parent `mod`, or the module (or block) containing /// the block, if `self` corresponds to a block expression. pub fn containing_module(self, db: &dyn DefDatabase) -> Option { - self.def_map(db).containing_module(self) + self.containing_module_inside_def_map(db) + .or_else(|| self.block(db).map(|block| block.loc(db).module)) + .map(|module| { + // SAFETY: Not sure. + unsafe { module.to_static() } + }) } pub fn is_block_module(self, db: &dyn DefDatabase) -> bool { - self.block(db).is_some() && self.def_map(db).root_module_id() == self + self.block(db).is_some() && self.containing_module_inside_def_map(db).is_none() } } @@ -837,6 +860,12 @@ impl From for ExpressionStoreOwnerId { } } +impl From for ExpressionStoreOwnerId { + fn from(id: ImplId) -> Self { + ExpressionStoreOwnerId::Signature(id.into()) + } +} + impl GenericDefId { pub fn file_id_and_params_of( self, 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 88e3408a33b14..0c7e7a3ca070f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -66,7 +66,7 @@ use hir_expand::{ EditionedFileId, ErasedAstId, HirFileId, InFile, MacroCallId, mod_path::ModPath, name::Name, proc_macro::ProcMacroKind, }; -use intern::Symbol; +use intern::{Symbol, sym}; use itertools::Itertools; use rustc_hash::FxHashMap; use span::{Edition, FileAstId, FileId, ROOT_ERASED_FILE_AST_ID}; @@ -427,7 +427,7 @@ pub(crate) fn crate_local_def_map(db: &dyn DefDatabase, crate_id: Crate) -> DefM #[salsa_macros::tracked(returns(ref))] pub fn block_def_map(db: &dyn DefDatabase, block_id: BlockId) -> DefMap { - let BlockLoc { ast_id, module } = block_id.lookup(db); + let BlockLoc { ast_id, module } = *block_id.lookup(db); let visibility = Visibility::Module(module, VisibilityExplicitness::Implicit); let module_data = @@ -465,7 +465,16 @@ impl DefMap { block: Option, ) -> DefMap { let mut modules = ModulesMap::new(); - let root = unsafe { ModuleIdLt::new(db, krate, block.map(|it| it.block)).to_static() }; + let root = unsafe { + ModuleIdLt::new( + db, + krate, + block.map(|it| it.block), + None, + Name::new_symbol_root(sym::__empty), + ) + .to_static() + }; modules.insert(root, module_data); DefMap { @@ -841,6 +850,7 @@ pub(crate) fn macro_styles_from_id(db: &dyn DefDatabase, macro_id: MacroId) -> M MacroExpander::BuiltIn(_) | MacroExpander::BuiltInEager(_) => MacroCallStyles::FN_LIKE, MacroExpander::BuiltInAttr(_) => MacroCallStyles::ATTR, MacroExpander::BuiltInDerive(_) => MacroCallStyles::DERIVE, + MacroExpander::UnimplementedBuiltIn => MacroCallStyles::all(), // Unknown. } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs index b1d554738f71f..7b5b39cb08cbe 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs @@ -50,7 +50,7 @@ impl TraitItems { db: &dyn DefDatabase, tr: TraitId, ) -> (TraitItems, DefDiagnostics) { - let ItemLoc { container: module_id, id: ast_id } = tr.lookup(db); + let ItemLoc { container: module_id, id: ast_id } = *tr.lookup(db); let ast_id_map = db.ast_id_map(ast_id.file_id); let source = ast_id.with_value(ast_id_map.get(ast_id.value)).to_node(db); if source.eq_token().is_some() { @@ -115,7 +115,7 @@ impl ImplItems { #[salsa::tracked(returns(ref))] pub fn of(db: &dyn DefDatabase, id: ImplId) -> (ImplItems, DefDiagnostics) { let _p = tracing::info_span!("impl_items_with_diagnostics_query").entered(); - let ItemLoc { container: module_id, id: ast_id } = id.lookup(db); + let ItemLoc { container: module_id, id: ast_id } = *id.lookup(db); let collector = AssocItemCollector::new(db, module_id, ItemContainerId::ImplId(id), ast_id.file_id); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 5ef51dbb2ca53..a916cda7309b0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -1027,7 +1027,7 @@ impl<'db> DefCollector<'db> { .enum_variants(self.db) .variants .iter() - .map(|&(variant, ref name, _)| { + .map(|(name, &(variant, _))| { let res = PerNs::both(variant.into(), variant.into(), vis, None); (Some(name.clone()), res) }) @@ -2428,6 +2428,8 @@ impl ModCollector<'_, '_> { self.def_collector.db, self.def_collector.def_map.krate, self.def_collector.def_map.block_id(), + Some(self.module_id), + name.clone(), ) .to_static() }; @@ -2560,7 +2562,7 @@ impl ModCollector<'_, '_> { .def_map .diagnostics .push(DefDiagnostic::unimplemented_builtin_macro(self.module_id, f_ast_id)); - return; + MacroExpander::UnimplementedBuiltIn } } } else { @@ -2639,7 +2641,7 @@ impl ModCollector<'_, '_> { .def_map .diagnostics .push(DefDiagnostic::unimplemented_builtin_macro(self.module_id, f_ast_id)); - return; + MacroExpander::UnimplementedBuiltIn } } else { // Case 2: normal `macro` diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs index cf33cecf5fba9..54fd30fe6ba3e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs @@ -519,12 +519,8 @@ impl DefMap { // enum variant cov_mark::hit!(can_import_enum_variant); - let res = e - .enum_variants(db) - .variants - .iter() - .find(|(_, name, _)| name == segment) - .map(|&(variant, _, shape)| match shape { + let res = e.enum_variants(db).variants.get(segment).map(|&(variant, shape)| { + match shape { FieldsShape::Record => { PerNs::types(variant.into(), Visibility::Public, None) } @@ -534,7 +530,8 @@ impl DefMap { Visibility::Public, None, ), - }); + } + }); // FIXME: Need to filter visibility here and below? Not sure. return match res { Some(res) => { 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 8320221ffc8ca..0062e6c170785 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -194,7 +194,8 @@ impl<'db> Resolver<'db> { LangItemTarget::TraitId(it) => TypeNs::TraitId(it), LangItemTarget::FunctionId(_) | LangItemTarget::ImplId(_) - | LangItemTarget::StaticId(_) => return None, + | LangItemTarget::StaticId(_) + | LangItemTarget::ConstId(_) => return None, }; return Some(( type_ns, @@ -337,6 +338,7 @@ impl<'db> Resolver<'db> { LangItemTarget::StaticId(it) => ValueNs::StaticId(it), LangItemTarget::StructId(it) => ValueNs::StructId(it), LangItemTarget::EnumVariantId(it) => ValueNs::EnumVariantId(it), + LangItemTarget::ConstId(it) => ValueNs::ConstId(it), LangItemTarget::UnionId(_) | LangItemTarget::ImplId(_) | LangItemTarget::TypeAliasId(_) @@ -356,7 +358,8 @@ impl<'db> Resolver<'db> { LangItemTarget::TraitId(it) => TypeNs::TraitId(it), LangItemTarget::FunctionId(_) | LangItemTarget::ImplId(_) - | LangItemTarget::StaticId(_) => return None, + | LangItemTarget::StaticId(_) + | LangItemTarget::ConstId(_) => return None, }; // Remaining segments start from 0 because lang paths have no segments other than the remaining. return Some(( 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 e58befae20c04..e9307a125502a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -1,6 +1,6 @@ //! Item signature IR definitions -use std::{cell::LazyCell, ops::Not as _}; +use std::cell::LazyCell; use bitflags::bitflags; use cfg::{CfgExpr, CfgOptions}; @@ -19,8 +19,9 @@ use thin_vec::ThinVec; use triomphe::Arc; use crate::{ - ConstId, EnumId, EnumVariantId, EnumVariantLoc, ExternBlockId, FunctionId, HasModule, ImplId, - ItemContainerId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, UnionId, VariantId, + ConstId, EnumId, EnumVariantId, EnumVariantLoc, ExternBlockId, FunctionId, FxIndexMap, + HasModule, ImplId, ItemContainerId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, + UnionId, VariantId, attrs::AttrFlags, db::DefDatabase, expr_store::{ @@ -1055,7 +1056,7 @@ pub struct InactiveEnumVariantCode { #[derive(Debug, Clone, PartialEq, Eq)] pub struct EnumVariants { - pub variants: Box<[(EnumVariantId, Name, FieldsShape)]>, + pub variants: FxIndexMap, } #[salsa::tracked] @@ -1064,30 +1065,31 @@ impl EnumVariants { pub(crate) fn of( db: &dyn DefDatabase, e: EnumId, - ) -> (EnumVariants, Option>) { + ) -> (EnumVariants, ThinVec) { let loc = e.lookup(db); let source = loc.source(db); let ast_id_map = db.ast_id_map(source.file_id); let mut diagnostics = ThinVec::new(); let cfg_options = loc.container.krate(db).cfg_options(db); - let mut index = 0; let Some(variants) = source.value.variant_list() else { - return (EnumVariants { variants: Box::default() }, None); + return (EnumVariants { variants: FxIndexMap::default() }, ThinVec::new()); }; - let variants = variants + let mut variants = variants .variants() .filter_map(|variant| { let ast_id = ast_id_map.ast_id(&variant); match AttrFlags::is_cfg_enabled_for(&variant, cfg_options) { Ok(()) => { - let enum_variant = - EnumVariantLoc { id: source.with_value(ast_id), parent: e, index } - .intern(db); - index += 1; let name = as_name_opt(variant.name()); + let enum_variant = EnumVariantLoc { + id: source.with_value(ast_id), + parent: e, + name: name.clone(), + } + .intern(db); let shape = adt_shape(variant.kind()); - Some((enum_variant, name, shape)) + Some((name, (enum_variant, shape))) } Err(cfg) => { diagnostics.push(InactiveEnumVariantCode { @@ -1099,34 +1101,38 @@ impl EnumVariants { } } }) - .collect(); + .collect::>(); + variants.shrink_to_fit(); + diagnostics.shrink_to_fit(); - (EnumVariants { variants }, diagnostics.is_empty().not().then_some(diagnostics)) + (EnumVariants { variants }, diagnostics) } } impl EnumVariants { pub fn variant(&self, name: &Name) -> Option { - self.variants.iter().find_map(|(v, n, _)| if n == name { Some(*v) } else { None }) + self.variants.get(name).map(|&(id, _)| id) } pub fn variant_name_by_id(&self, variant_id: EnumVariantId) -> Option { self.variants .iter() - .find_map(|(id, name, _)| if *id == variant_id { Some(name.clone()) } else { None }) + .find_map(|(name, (id, _))| if *id == variant_id { Some(name.clone()) } else { None }) } // [Adopted from rustc](https://github.com/rust-lang/rust/blob/bd53aa3bf7a24a70d763182303bd75e5fc51a9af/compiler/rustc_middle/src/ty/adt.rs#L446-L448) pub fn is_payload_free(&self, db: &dyn DefDatabase) -> bool { - self.variants.iter().all(|&(v, _, _)| { + self.variants.values().all(|&(v, shape)| { // The condition check order is slightly modified from rustc // to improve performance by early returning with relatively fast checks - let variant = v.fields(db); - if !variant.fields().is_empty() { - return false; - } + // The outer if condition is whether this variant has const ctor or not - if !matches!(variant.shape, FieldsShape::Unit) { + if !matches!(shape, FieldsShape::Unit) { + let variant = v.fields(db); + if !variant.fields().is_empty() { + return false; + } + let body = Body::of(db, v.into()); // A variant with explicit discriminant if !matches!(body[body.root_expr()], crate::hir::Expr::Missing) { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs index b854d2aa218de..913b99223d9df 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs @@ -142,6 +142,10 @@ impl SourceDatabase for TestDB { fn nonce_and_revision(&self) -> (Nonce, salsa::Revision) { (self.nonce, salsa::plumbing::ZalsaDatabase::zalsa(self).current_revision()) } + + fn line_column(&self, _file: FileId, _offset: syntax::TextSize) -> Result<(u32, u32), ()> { + Err(()) + } } impl TestDB { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs index 8b031e364775c..c4da558fdab24 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs @@ -22,7 +22,7 @@ use crate::{ use syntax::{ ast::{ self, AstNode, FieldList, HasAttrs, HasGenericArgs, HasGenericParams, HasModuleItem, - HasName, HasTypeBounds, make, + HasName, HasTypeBounds, }, syntax_editor::{GetOrCreateWhereClause, SyntaxEditor}, }; @@ -1435,6 +1435,7 @@ fn coerce_pointee_expand( param_name: &str, replacement: &str, ) -> bool { + let make = editor.make(); return match ty { ast::Type::ArrayType(ty) => ty .ty() @@ -1462,8 +1463,8 @@ fn coerce_pointee_expand( if path.as_single_name_ref().is_some_and(|name| name.text() == param_name) { editor.replace( path.syntax(), - make::path_from_segments( - [make::path_segment(make::name_ref(replacement))], + make.path_from_segments( + [make.path_segment(make.name_ref(replacement))], false, ) .syntax(), @@ -1494,6 +1495,9 @@ fn coerce_pointee_expand( ast::Type::TupleType(ty) => any_long(ty.fields(), |ty| { substitute_type_in_bound(editor, ty, param_name, replacement) }), + ast::Type::PatternType(ty) => ty + .ty() + .is_some_and(|ty| substitute_type_in_bound(editor, ty, param_name, replacement)), ast::Type::InferType(_) | ast::Type::MacroType(_) | ast::Type::NeverType(_) => 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 eb7175c686a08..9181ad88b6cc5 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 @@ -137,6 +137,7 @@ register_builtin! { (const_format_args, ConstFormatArgs) => format_args_expand, (format_args_nl, FormatArgsNl) => format_args_nl_expand, (quote, Quote) => quote_expand, + (pattern_type, PatternType) => pattern_type_expand, EagerExpander: (compile_error, CompileError) => compile_error_expand, @@ -994,3 +995,15 @@ fn unescape_str(s: &str) -> Cow<'_, str> { Cow::Borrowed(s) } } + +fn pattern_type_expand( + _db: &dyn ExpandDatabase, + _arg_id: MacroCallId, + tt: &tt::TopSubtree, + call_site: Span, +) -> ExpandResult { + let mut tt = tt.clone(); + tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); + let pound = mk_pound(call_site); + ExpandResult::ok(quote! {call_site => builtin #pound pattern_type ( #tt ) }) +} diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index beae6e843e493..513115684f872 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -43,6 +43,7 @@ pub enum TokenExpander<'db> { BuiltInAttr(BuiltinAttrExpander), /// `derive(Copy)` and such. BuiltInDerive(BuiltinDeriveExpander), + UnimplementedBuiltIn, /// The thing we love the most here in rust-analyzer -- procedural macros. ProcMacro(CustomProcMacroExpander), } @@ -149,8 +150,8 @@ fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId, edition: Edition) -> match file { HirFileId::FileId(_) => SyntaxContext::root(edition), HirFileId::MacroFile(m) => { - let kind = m.loc(db).kind; - db.macro_arg_considering_derives(m, &kind).2.ctx + let kind = &m.loc(db).kind; + db.macro_arg_considering_derives(m, kind).2.ctx } } } @@ -311,6 +312,7 @@ pub fn expand_speculative( it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) } MacroDefKind::BuiltInAttr(_, it) => it.expand(db, actual_macro_call, &tt, span), + MacroDefKind::UnimplementedBuiltIn(_) => expand_unimplemented_builtin_macro(span), }; let expand_to = loc.expand_to(); @@ -335,6 +337,13 @@ pub fn expand_speculative( Some((node.syntax_node(), token)) } +fn expand_unimplemented_builtin_macro(span: Span) -> ExpandResult { + ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other(span, "this built-in macro is not implemented"), + ) +} + #[salsa::tracked(lru = 1024, returns(ref))] fn ast_id_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> AstIdMap { AstIdMap::from_source(&db.parse_or_expand(file_id)) @@ -396,7 +405,7 @@ pub(crate) fn parse_with_map( /// This resolves the [MacroCallId] to check if it is a derive macro if so get the [macro_arg] for the derive. /// Other wise return the [macro_arg] for the macro_call_id. /// -/// This is not connected to the database so it does not cached the result. However, the inner [macro_arg] query is +/// This is not connected to the database so it does not cache the result. However, the inner [macro_arg] query is #[allow(deprecated)] // we are macro_arg_considering_derives fn macro_arg_considering_derives<'db>( db: &'db dyn ExpandDatabase, @@ -538,15 +547,16 @@ impl<'db> TokenExpander<'db> { MacroDefKind::BuiltInDerive(_, expander) => TokenExpander::BuiltInDerive(expander), MacroDefKind::BuiltInEager(_, expander) => TokenExpander::BuiltInEager(expander), MacroDefKind::ProcMacro(_, expander, _) => TokenExpander::ProcMacro(expander), + MacroDefKind::UnimplementedBuiltIn(_) => TokenExpander::UnimplementedBuiltIn, } } } -fn macro_expand( - db: &dyn ExpandDatabase, +fn macro_expand<'db>( + db: &'db dyn ExpandDatabase, macro_call_id: MacroCallId, - loc: MacroCallLoc, -) -> ExpandResult<(Cow<'_, tt::TopSubtree>, MatchedArmIndex)> { + loc: &MacroCallLoc, +) -> ExpandResult<(Cow<'db, tt::TopSubtree>, MatchedArmIndex)> { let _p = tracing::info_span!("macro_expand").entered(); let (ExpandResult { value: (tt, matched_arm), err }, span) = match loc.def.kind { @@ -572,6 +582,9 @@ fn macro_expand( MacroDefKind::BuiltInDerive(_, it) => { it.expand(db, macro_call_id, arg, span).map_err(Into::into).zip_val(None) } + MacroDefKind::UnimplementedBuiltIn(_) => { + expand_unimplemented_builtin_macro(span).zip_val(None) + } MacroDefKind::BuiltInEager(_, it) => { // This might look a bit odd, but we do not expand the inputs to eager macros here. // Eager macros inputs are expanded, well, eagerly when we collect the macro calls. diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs index a19f58709b8f9..f8a560834adb3 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs @@ -246,7 +246,8 @@ fn eager_macro_recur( | MacroDefKind::BuiltIn(..) | MacroDefKind::BuiltInAttr(..) | MacroDefKind::BuiltInDerive(..) - | MacroDefKind::ProcMacro(..) => { + | MacroDefKind::ProcMacro(..) + | MacroDefKind::UnimplementedBuiltIn(..) => { let ExpandResult { value: (parse, tm), err } = lazy_expand( db, &def, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 0850d6156d112..9c5714be2de7d 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -81,7 +81,7 @@ macro_rules! impl_intern_lookup { impl $crate::Lookup for $id { type Database = dyn $db; type Data = $loc; - fn lookup(&self, db: &Self::Database) -> Self::Data { + fn lookup<'db>(&self, db: &'db Self::Database) -> &'db Self::Data { self.loc(db) } } @@ -98,7 +98,7 @@ pub trait Intern { pub trait Lookup { type Database: ?Sized; type Data; - fn lookup(&self, db: &Self::Database) -> Self::Data; + fn lookup<'db>(&self, db: &'db Self::Database) -> &'db Self::Data; } impl_intern_lookup!(ExpandDatabase, MacroCallId, MacroCallLoc); @@ -249,6 +249,7 @@ pub enum MacroDefKind { BuiltInAttr(AstId, BuiltinAttrExpander), BuiltInDerive(AstId, BuiltinDeriveExpander), BuiltInEager(AstId, EagerExpander), + UnimplementedBuiltIn(AstId), ProcMacro(AstId, CustomProcMacroExpander, ProcMacroKind), } @@ -265,7 +266,8 @@ impl MacroDefKind { | MacroDefKind::BuiltInAttr(id, _) | MacroDefKind::BuiltInDerive(id, _) | MacroDefKind::BuiltInEager(id, _) - | MacroDefKind::Declarative(id, ..) => id.erase(), + | MacroDefKind::Declarative(id, ..) + | MacroDefKind::UnimplementedBuiltIn(id) => id.erase(), } } } @@ -500,6 +502,7 @@ impl MacroCallId { MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr) => MacroKind::Attr, MacroDefKind::ProcMacro(_, _, ProcMacroKind::Bang) => MacroKind::ProcMacro, MacroDefKind::BuiltInAttr(..) => MacroKind::AttrBuiltIn, + MacroDefKind::UnimplementedBuiltIn(..) => MacroKind::Declarative, } } @@ -551,7 +554,8 @@ impl MacroDefId { | MacroDefKind::BuiltIn(id, _) | MacroDefKind::BuiltInAttr(id, _) | MacroDefKind::BuiltInDerive(id, _) - | MacroDefKind::BuiltInEager(id, _) => { + | MacroDefKind::BuiltInEager(id, _) + | MacroDefKind::UnimplementedBuiltIn(id) => { id.with_value(db.ast_id_map(id.file_id).get(id.value).text_range()) } MacroDefKind::ProcMacro(id, _, _) => { @@ -567,7 +571,8 @@ impl MacroDefId { | MacroDefKind::BuiltIn(id, _) | MacroDefKind::BuiltInAttr(id, _) | MacroDefKind::BuiltInDerive(id, _) - | MacroDefKind::BuiltInEager(id, _) => Either::Left(id), + | MacroDefKind::BuiltInEager(id, _) + | MacroDefKind::UnimplementedBuiltIn(id) => Either::Left(id), } } @@ -577,9 +582,9 @@ impl MacroDefId { pub fn is_attribute(&self) -> bool { match self.kind { - MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr) => { - true - } + MacroDefKind::BuiltInAttr(..) + | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr) + | MacroDefKind::UnimplementedBuiltIn(_) => true, MacroDefKind::Declarative(_, styles) => styles.contains(MacroCallStyles::ATTR), _ => false, } @@ -588,7 +593,8 @@ impl MacroDefId { pub fn is_derive(&self) -> bool { match self.kind { MacroDefKind::BuiltInDerive(..) - | MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive) => true, + | MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive) + | MacroDefKind::UnimplementedBuiltIn(_) => true, MacroDefKind::Declarative(_, styles) => styles.contains(MacroCallStyles::DERIVE), _ => false, } @@ -601,6 +607,7 @@ impl MacroDefId { | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Bang) | MacroDefKind::BuiltInEager(..) | MacroDefKind::Declarative(..) + | MacroDefKind::UnimplementedBuiltIn(_) ) } @@ -714,24 +721,27 @@ impl MacroCallKind { /// - fn_like! {}, it spans the path and token tree /// - #\[derive], it spans the `#[derive(...)]` attribute and the annotated item /// - #\[attr], it spans the `#[attr(...)]` attribute and the annotated item - pub fn original_call_range_with_input(self, db: &dyn ExpandDatabase) -> FileRange { - let mut kind = self; + pub fn original_call_range_with_input(&self, db: &dyn ExpandDatabase) -> FileRange { + let get_range = |kind: &_| match kind { + MacroCallKind::FnLike { ast_id, .. } => ast_id.erase(), + MacroCallKind::Derive { ast_id, .. } => ast_id.erase(), + MacroCallKind::Attr { ast_id, .. } => ast_id.erase(), + }; + + let mut ast_id = get_range(self); + let mut file_id = self.file_id(); let file_id = loop { - match kind.file_id() { + match file_id { HirFileId::MacroFile(file) => { - kind = file.loc(db).kind; + let kind = &file.loc(db).kind; + ast_id = get_range(kind); + file_id = kind.file_id(); } HirFileId::FileId(file_id) => break file_id, } }; - let range = match kind { - MacroCallKind::FnLike { ast_id, .. } => ast_id.to_ptr(db).text_range(), - MacroCallKind::Derive { ast_id, .. } => ast_id.to_ptr(db).text_range(), - MacroCallKind::Attr { ast_id, .. } => ast_id.to_ptr(db).text_range(), - }; - - FileRange { range, file_id } + FileRange { range: ast_id.to_ptr(db).text_range(), file_id } } /// Returns the original file range that best describes the location of this macro call. @@ -739,18 +749,8 @@ impl MacroCallKind { /// Here we try to roughly match what rustc does to improve diagnostics: fn-like macros /// get the macro path (rustc shows the whole `ast::MacroCall`), attribute macros get the /// attribute's range, and derives get only the specific derive that is being referred to. - pub fn original_call_range(self, db: &dyn ExpandDatabase, krate: Crate) -> FileRange { - let mut kind = self; - let file_id = loop { - match kind.file_id() { - HirFileId::MacroFile(file) => { - kind = file.loc(db).kind; - } - HirFileId::FileId(file_id) => break file_id, - } - }; - - let range = match kind { + pub fn original_call_range(&self, db: &dyn ExpandDatabase, krate: Crate) -> FileRange { + let get_range = |kind: &_| match kind { MacroCallKind::FnLike { ast_id, .. } => { let node = ast_id.to_node(db); node.path() @@ -761,11 +761,24 @@ impl MacroCallKind { } MacroCallKind::Derive { ast_id, derive_attr_index, .. } => { // FIXME: should be the range of the macro name, not the whole derive - derive_attr_index.find_attr_range(db, krate, ast_id).1.syntax().text_range() + derive_attr_index.find_attr_range(db, krate, *ast_id).1.syntax().text_range() } // FIXME: handle `cfg_attr` MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => { - attr_ids.invoc_attr().find_attr_range(db, krate, ast_id).1.syntax().text_range() + attr_ids.invoc_attr().find_attr_range(db, krate, *ast_id).1.syntax().text_range() + } + }; + + let mut range = get_range(self); + let mut file_id = self.file_id(); + let file_id = loop { + match file_id { + HirFileId::MacroFile(file) => { + let kind = &file.loc(db).kind; + range = get_range(kind); + file_id = kind.file_id(); + } + HirFileId::FileId(file_id) => break file_id, } }; @@ -797,7 +810,7 @@ pub struct ExpansionInfo<'db> { arg: InFile>, exp_map: &'db ExpansionSpanMap, arg_map: SpanMap<'db>, - loc: MacroCallLoc, + loc: &'db MacroCallLoc, } impl<'db> ExpansionInfo<'db> { @@ -1056,6 +1069,7 @@ intern::impl_internable!(ModPath); #[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[doc(alias = "MacroFileId")] pub struct MacroCallId { + #[returns(ref)] pub loc: MacroCallLoc, } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs index 79e6f0f5b7aa4..687bd1a71400f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs @@ -2,8 +2,7 @@ use base_db::Crate; use rustc_hash::FxHashMap; -use syntax::NodeOrToken; -use syntax::{SyntaxNode, ast::make}; +use syntax::SyntaxNode; use crate::{db::ExpandDatabase, span_map::ExpansionSpanMap}; @@ -22,7 +21,7 @@ pub fn prettify_macro_expansion( let mut syntax_ctx_id_to_dollar_crate_replacement = FxHashMap::default(); syntax_bridge::prettify_macro_expansion::prettify_macro_expansion( syn, - &mut |dollar_crate| { + &mut |dollar_crate, make| { let ctx = span_map.span_at(dollar_crate.text_range().start() + span_offset).ctx; let replacement = syntax_ctx_id_to_dollar_crate_replacement.entry(ctx).or_insert_with(|| { @@ -38,13 +37,13 @@ pub fn prettify_macro_expansion( // is inserted, and also understandable to the user. // Lastly, if nothing else found, resort to leaving `$crate`. if target_crate_id == macro_def_crate { - make::tokens::crate_kw() + make.token(syntax::SyntaxKind::CRATE_KW) } else if let Some(dep) = target_crate.dependencies.iter().find(|dep| dep.crate_id == macro_def_crate) { - make::tokens::ident(dep.name.as_str()) + make.ident(dep.name.as_str()) } else if let Some(crate_name) = ¯o_def_crate.extra_data(db).display_name { - make::tokens::ident(crate_name.crate_name().as_str()) + make.ident(crate_name.crate_name().as_str()) } else { dollar_crate.clone() } @@ -53,12 +52,7 @@ pub fn prettify_macro_expansion( // The parent may have many children, and looking for the token may yield incorrect results. return None; } - // We need to `clone_subtree()` but rowan doesn't provide such operation for tokens. - let parent = replacement.parent().unwrap().clone_subtree().clone_for_update(); - parent - .children_with_tokens() - .filter_map(NodeOrToken::into_token) - .find(|it| it.kind() == replacement.kind()) + Some(replacement.clone()) }, |_| (), ) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs index fe60fbc5109f5..928e3da6e8c9a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs @@ -193,7 +193,7 @@ pub fn predicates(db: &dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPr else { // Malformed derive. return GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind( - Clauses::default().store(), + Clauses::empty(interner).store(), )); }; let duplicated_bounds = @@ -305,7 +305,7 @@ fn simple_trait_predicates<'db>( loc.trait_, ), AdtId::EnumId(id) => { - for &(variant_id, _, _) in &id.enum_variants(interner.db).variants { + for &(variant_id, _) in id.enum_variants(interner.db).variants.values() { extend_assoc_type_bounds( interner, &mut assoc_type_bounds, 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 e1d6cec59421e..2c43feeb3b1a0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -5,7 +5,7 @@ mod tests; use base_db::Crate; use hir_def::{ - ConstId, EnumVariantId, ExpressionStoreOwnerId, GenericDefId, HasModule, StaticId, + ConstId, EnumVariantId, ExpressionStoreOwnerId, HasModule, StaticId, attrs::AttrFlags, expr_store::{Body, ExpressionStore, HygieneId, path::Path}, hir::{Expr, ExprId, Literal}, @@ -303,6 +303,7 @@ pub(crate) enum CreateConstError<'db> { UsedForbiddenParam, ResolveToNonConst, DoesNotResolve, + ConstHasGenerics, UnderscoreExpr, TypeMismatch { #[expect(unused, reason = "will need this for diagnostics")] @@ -321,9 +322,11 @@ pub(crate) fn path_to_const<'a, 'db>( let resolution = resolver .resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) .ok_or(CreateConstError::DoesNotResolve)?; + let no_generics = |def| crate::generics::generics(db, def).has_no_params(); let konst = match resolution { - ValueNs::ConstId(id) => GeneralConstId::ConstId(id), + ValueNs::ConstId(id) if no_generics(id.into()) => GeneralConstId::ConstId(id), ValueNs::StaticId(id) => GeneralConstId::StaticId(id), + ValueNs::ConstId(_) => return Err(CreateConstError::ConstHasGenerics), ValueNs::GenericParam(param) => { let index = generics().type_or_const_param_idx(param.into()); if forbid_params_after.is_some_and(|forbid_after| index >= forbid_after) { @@ -363,7 +366,10 @@ pub(crate) fn create_anon_const<'a, 'db>( Expr::Path(path) if let konst = path_to_const(interner.db, resolver, generics, forbid_params_after, path) - && !matches!(konst, Err(CreateConstError::DoesNotResolve)) => + && !matches!( + konst, + Err(CreateConstError::DoesNotResolve | CreateConstError::ConstHasGenerics) + ) => { konst } @@ -400,12 +406,10 @@ pub(crate) fn const_eval_discriminant_variant( let body = Body::of(db, def); let loc = variant_id.lookup(db); if matches!(body[body.root_expr()], Expr::Missing) { - let prev_idx = loc.index.checked_sub(1); + let prev_idx = loc.index(db).checked_sub(1); let value = match prev_idx { Some(prev_idx) => { - 1 + db.const_eval_discriminant( - loc.parent.enum_variants(db).variants[prev_idx as usize].0, - )? + 1 + db.const_eval_discriminant(loc.parent.enum_variants(db).variants[prev_idx].0)? } _ => 0, }; @@ -418,8 +422,11 @@ pub(crate) fn const_eval_discriminant_variant( let mir_body = db.monomorphized_mir_body( def.into(), GenericArgs::empty(interner).store(), - ParamEnvAndCrate { param_env: db.trait_environment(def.into()), krate: def.krate(db) } - .store(), + ParamEnvAndCrate { + param_env: db.trait_environment(def.generic_def(db)), + krate: def.krate(db), + } + .store(), )?; let c = interpret_mir(db, mir_body, false, None)?.0?; let c = if is_signed { allocation_as_isize(c) } else { allocation_as_usize(c) as i128 }; @@ -455,12 +462,8 @@ pub(crate) fn const_eval<'db>( let body = db.monomorphized_mir_body( def.into(), subst, - ParamEnvAndCrate { - param_env: db - .trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from(def))), - krate: def.krate(db), - } - .store(), + ParamEnvAndCrate { param_env: db.trait_environment(def.into()), krate: def.krate(db) } + .store(), )?; let c = interpret_mir(db, body, false, trait_env.as_ref().map(|env| env.as_ref()))?.0?; Ok(c.store()) @@ -499,7 +502,7 @@ pub(crate) fn anon_const_eval<'db>( def.into(), subst, ParamEnvAndCrate { - param_env: db.trait_environment(def.loc(db).owner), + param_env: db.trait_environment(def.loc(db).owner.generic_def(db)), krate: def.krate(db), } .store(), @@ -537,12 +540,8 @@ pub(crate) fn const_eval_static<'db>( let body = db.monomorphized_mir_body( def.into(), GenericArgs::empty(interner).store(), - ParamEnvAndCrate { - param_env: db - .trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from(def))), - krate: def.krate(db), - } - .store(), + ParamEnvAndCrate { param_env: db.trait_environment(def.into()), krate: def.krate(db) } + .store(), )?; let c = interpret_mir(db, body, false, None)?.0?; Ok(c.store()) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index f15866106928b..5421b97db2386 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -287,6 +287,9 @@ fn floating_point_casts() { check_number(r#"const GOAL: i8 = (0./0.) as i8"#, 0); check_number(r#"const GOAL: i8 = (1./0.) as i8"#, 127); check_number(r#"const GOAL: i8 = (-1./0.) as i8"#, -128); + check_number(r#"const GOAL: u8 = (1./0.) as u8"#, 255); + check_number(r#"const GOAL: u8 = 256.0f32 as u8"#, 255); + check_number(r#"const GOAL: u16 = 1e10f32 as u16"#, 65535); check_number(r#"const GOAL: i64 = 1e18f64 as f32 as i64"#, 999999984306749440); } 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 c24a5b943db08..511ab856107f8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -247,7 +247,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::lower::trait_environment)] #[salsa::transparent] - fn trait_environment<'db>(&'db self, def: ExpressionStoreOwnerId) -> ParamEnv<'db>; + fn trait_environment<'db>(&'db self, def: GenericDefId) -> ParamEnv<'db>; #[salsa::invoke(crate::lower::generic_defaults_with_diagnostics)] #[salsa::transparent] 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 76fbb66b063f0..ace361633916c 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 @@ -504,15 +504,15 @@ impl<'a> DeclValidator<'a> { fn validate_enum_variants(&mut self, enum_id: EnumId) { let data = enum_id.enum_variants(self.db); - for (variant_id, _, _) in data.variants.iter() { + for (variant_id, _) in data.variants.values() { self.validate_enum_variant_fields(*variant_id); } let edition = self.edition(enum_id); let mut enum_variants_replacements = data .variants - .iter() - .filter_map(|(_, name, _)| { + .keys() + .filter_map(|name| { to_camel_case(&name.display_no_db(edition).to_smolstr()).map(|new_name| { Replacement { current_name: name.clone(), 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 760ebd27e0573..be4de11ceb36b 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 @@ -83,7 +83,7 @@ impl<'db> BodyValidationDiagnostic<'db> { let _p = tracing::info_span!("BodyValidationDiagnostic::collect").entered(); let infer = InferenceResult::of(db, owner); let body = Body::of(db, owner); - let env = db.trait_environment(owner.into()); + let env = db.trait_environment(owner.generic_def(db)); let interner = DbInterner::new_with(db, owner.krate(db)); let infcx = interner.infer_ctxt().build(TypingMode::typeck_for_body(interner, owner.into())); 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 8356329d96ae3..14bff65220702 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 @@ -330,13 +330,7 @@ impl<'db> HirDisplay<'db> for Pat<'db> { match variant { VariantId::EnumVariantId(v) => { let loc = v.lookup(f.db); - write!( - f, - "{}", - loc.parent.enum_variants(f.db).variants[loc.index as usize] - .1 - .display(f.db, f.edition()) - )?; + write!(f, "{}", loc.name.display(f.db, f.edition()))?; } VariantId::StructId(s) => write!( f, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index 46959aaa5ac03..984ac1abfbb36 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -49,8 +49,7 @@ pub(crate) struct EnumVariantContiguousIndex(usize); impl EnumVariantContiguousIndex { fn from_enum_variant_id(db: &dyn HirDatabase, target_evid: EnumVariantId) -> Self { // Find the index of this variant in the list of variants. - use hir_def::Lookup; - let i = target_evid.lookup(db).index as usize; + let i = target_evid.index(db); EnumVariantContiguousIndex(i) } @@ -438,7 +437,7 @@ impl<'a, 'db> PatCx for MatchCheckCtx<'a, 'db> { ConstructorSet::NoConstructors } else { let mut variants = IndexVec::with_capacity(enum_data.variants.len()); - for &(variant, _, _) in enum_data.variants.iter() { + for &(variant, _) in enum_data.variants.values() { let is_uninhabited = is_enum_variant_uninhabited_from( cx.infcx, variant, subst, cx.module, self.env, ); 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 c37a194d47572..3c69c6a44b127 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 @@ -255,9 +255,8 @@ impl<'db> UnsafeVisitor<'db> { | Pat::Box { .. } | Pat::Deref { .. } | Pat::Expr(..) - | Pat::ConstBlock(..) => { - self.on_unsafe_op(current.into(), UnsafetyReason::UnionField) - } + | Pat::ConstBlock(..) + | Pat::NotNull => self.on_unsafe_op(current.into(), UnsafetyReason::UnionField), // `Or` only wraps other patterns, and `Missing`/`Wild` do not constitute a read. Pat::Missing | Pat::Rest | Pat::Wild | Pat::Or(_) => {} } 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 bc726b652fd4e..8edb178cd74f7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -15,7 +15,7 @@ use hir_def::{ expr_store::{ExpressionStore, path::Path}, find_path::{self, PrefixKind}, hir::{ - ClosureKind as HirClosureKind, CoroutineKind, + ClosureKind as HirClosureKind, CoroutineKind, PatId, generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate}, }, item_scope::ItemInNs, @@ -780,7 +780,7 @@ fn render_const_scalar<'db>( memory_map: &MemoryMap<'db>, ty: Ty<'db>, ) -> Result { - let param_env = ParamEnv::empty(); + let param_env = ParamEnv::empty(f.interner); let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis); let ty = infcx.at(&ObligationCause::dummy(), param_env).deeply_normalize(ty).unwrap_or(ty); render_const_scalar_inner(f, b, memory_map, ty, param_env) @@ -968,9 +968,7 @@ fn render_const_scalar_inner<'db>( s.fields(f.db), f, field_types, - f.db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from( - def, - ))), + f.db.trait_environment(def.into()), &layout, args, b, @@ -990,21 +988,13 @@ fn render_const_scalar_inner<'db>( return f.write_str(""); }; let loc = var_id.lookup(f.db); - write!( - f, - "{}", - loc.parent.enum_variants(f.db).variants[loc.index as usize] - .1 - .display(f.db, f.edition()) - )?; + write!(f, "{}", loc.name.display(f.db, f.edition()))?; let field_types = f.db.field_types(var_id.into()); render_variant_after_name( var_id.fields(f.db), f, field_types, - f.db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from( - def, - ))), + f.db.trait_environment(def.into()), var_layout, args, b, @@ -1065,7 +1055,7 @@ fn render_const_scalar_from_valtree<'db>( ty: Ty<'db>, valtree: ValTree<'db>, ) -> Result { - let param_env = ParamEnv::empty(); + let param_env = ParamEnv::empty(f.interner); let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis); let ty = infcx.at(&ObligationCause::dummy(), param_env).deeply_normalize(ty).unwrap_or(ty); render_const_scalar_from_valtree_inner(f, ty, valtree, param_env) @@ -1362,13 +1352,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } CallableDefId::EnumVariantId(e) => { let loc = e.lookup(db); - write!( - f, - "{}", - loc.parent.enum_variants(db).variants[loc.index as usize] - .1 - .display(db, f.edition()) - )? + write!(f, "{}", loc.name.display(db, f.edition()))? } }; f.end_location_link(); @@ -2362,39 +2346,58 @@ pub fn write_visibility<'db>( } pub trait HirDisplayWithExpressionStore<'db> { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result; + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result; } impl<'db, T: ?Sized + HirDisplayWithExpressionStore<'db>> HirDisplayWithExpressionStore<'db> for &'_ T { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { - T::hir_fmt(&**self, f, store) + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { + T::hir_fmt(&**self, f, owner, store) } } pub fn hir_display_with_store<'a, 'db, T: HirDisplayWithExpressionStore<'db> + 'a>( value: T, + owner: ExpressionStoreOwnerId, store: &'a ExpressionStore, ) -> impl HirDisplay<'db> + 'a { - ExpressionStoreAdapter(value, store) + ExpressionStoreAdapter(value, owner, store) } -struct ExpressionStoreAdapter<'a, T>(T, &'a ExpressionStore); +struct ExpressionStoreAdapter<'a, T>(T, ExpressionStoreOwnerId, &'a ExpressionStore); impl<'a, T> ExpressionStoreAdapter<'a, T> { - fn wrap(store: &'a ExpressionStore) -> impl Fn(T) -> ExpressionStoreAdapter<'a, T> { - move |value| ExpressionStoreAdapter(value, store) + fn wrap( + owner: ExpressionStoreOwnerId, + store: &'a ExpressionStore, + ) -> impl Fn(T) -> ExpressionStoreAdapter<'a, T> { + move |value| ExpressionStoreAdapter(value, owner, store) } } impl<'db, T: HirDisplayWithExpressionStore<'db>> HirDisplay<'db> for ExpressionStoreAdapter<'_, T> { fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { - T::hir_fmt(&self.0, f, self.1) + T::hir_fmt(&self.0, f, self.1, self.2) } } impl<'db> HirDisplayWithExpressionStore<'db> for LifetimeRefId { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + _owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { match &store[*self] { LifetimeRef::Named(name) => write!(f, "{}", name.display(f.db, f.edition())), LifetimeRef::Static => write!(f, "'static"), @@ -2413,7 +2416,12 @@ impl<'db> HirDisplayWithExpressionStore<'db> for LifetimeRefId { } impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { match &store[*self] { TypeRef::Never => write!(f, "!")?, TypeRef::TypeParam(param) => { @@ -2438,7 +2446,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { } _ => None, }) - .map(ExpressionStoreAdapter::wrap(store)), + .map(ExpressionStoreAdapter::wrap(owner, store)), " + ", )?; } @@ -2447,20 +2455,20 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { TypeRef::Placeholder => write!(f, "_")?, TypeRef::Tuple(elems) => { write!(f, "(")?; - f.write_joined(elems.iter().map(ExpressionStoreAdapter::wrap(store)), ", ")?; + f.write_joined(elems.iter().map(ExpressionStoreAdapter::wrap(owner, store)), ", ")?; if elems.len() == 1 { write!(f, ",")?; } write!(f, ")")?; } - TypeRef::Path(path) => path.hir_fmt(f, store)?, + TypeRef::Path(path) => path.hir_fmt(f, owner, store)?, TypeRef::RawPtr(inner, mutability) => { let mutability = match mutability { hir_def::type_ref::Mutability::Shared => "*const ", hir_def::type_ref::Mutability::Mut => "*mut ", }; write!(f, "{mutability}")?; - inner.hir_fmt(f, store)?; + inner.hir_fmt(f, owner, store)?; } TypeRef::Reference(ref_) => { let mutability = match ref_.mutability { @@ -2469,22 +2477,22 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { }; write!(f, "&")?; if let Some(lifetime) = &ref_.lifetime { - lifetime.hir_fmt(f, store)?; + lifetime.hir_fmt(f, owner, store)?; write!(f, " ")?; } write!(f, "{mutability}")?; - ref_.ty.hir_fmt(f, store)?; + ref_.ty.hir_fmt(f, owner, store)?; } TypeRef::Array(array) => { write!(f, "[")?; - array.ty.hir_fmt(f, store)?; + array.ty.hir_fmt(f, owner, store)?; write!(f, "; ")?; - array.len.hir_fmt(f, store)?; + array.len.hir_fmt(f, owner, store)?; write!(f, "]")?; } TypeRef::Slice(inner) => { write!(f, "[")?; - inner.hir_fmt(f, store)?; + inner.hir_fmt(f, owner, store)?; write!(f, "]")?; } TypeRef::Fn(fn_) => { @@ -2504,7 +2512,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { write!(f, "{}: ", name.display(f.db, f.edition()))?; } - param_type.hir_fmt(f, store)?; + param_type.hir_fmt(f, owner, store)?; if index != function_parameters.len() - 1 { write!(f, ", ")?; @@ -2518,18 +2526,29 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { TypeRef::Tuple(tup) if tup.is_empty() => {} _ => { write!(f, " -> ")?; - return_type.hir_fmt(f, store)?; + return_type.hir_fmt(f, owner, store)?; } } } } TypeRef::ImplTrait(bounds) => { write!(f, "impl ")?; - f.write_joined(bounds.iter().map(ExpressionStoreAdapter::wrap(store)), " + ")?; + f.write_joined( + bounds.iter().map(ExpressionStoreAdapter::wrap(owner, store)), + " + ", + )?; } TypeRef::DynTrait(bounds) => { write!(f, "dyn ")?; - f.write_joined(bounds.iter().map(ExpressionStoreAdapter::wrap(store)), " + ")?; + f.write_joined( + bounds.iter().map(ExpressionStoreAdapter::wrap(owner, store)), + " + ", + )?; + } + TypeRef::PatternType(ty, pat) => { + ty.hir_fmt(f, owner, store)?; + write!(f, " is ")?; + pat.hir_fmt(f, owner, store)?; } TypeRef::Error => write!(f, "{{error}}")?, } @@ -2538,7 +2557,12 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { } impl<'db> HirDisplayWithExpressionStore<'db> for ConstRef { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, _store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + _owner: ExpressionStoreOwnerId, + _store: &ExpressionStore, + ) -> Result { // FIXME write!(f, "{{const}}")?; @@ -2546,17 +2570,45 @@ impl<'db> HirDisplayWithExpressionStore<'db> for ConstRef { } } +impl<'db> HirDisplayWithExpressionStore<'db> for PatId { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { + write!( + f, + "{}", + hir_def::expr_store::pretty::print_pat_hir( + f.db, + store, + owner, + *self, + false, + f.edition() + ) + )?; + Ok(()) + } +} + impl<'db> HirDisplayWithExpressionStore<'db> for TypeBound { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { match self { &TypeBound::Path(path, modifier) => { match modifier { TraitBoundModifier::None => (), TraitBoundModifier::Maybe => write!(f, "?")?, } - store[path].hir_fmt(f, store) + store[path].hir_fmt(f, owner, store) } - TypeBound::Lifetime(lifetime) => lifetime.hir_fmt(f, store), + TypeBound::Lifetime(lifetime) => lifetime.hir_fmt(f, owner, store), TypeBound::ForLifetime(lifetimes, path) => { let edition = f.edition(); write!( @@ -2564,7 +2616,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeBound { "for<{}> ", lifetimes.iter().map(|it| it.display(f.db, edition)).format(", ") )?; - store[*path].hir_fmt(f, store) + store[*path].hir_fmt(f, owner, store) } TypeBound::Use(args) => { write!(f, "use<")?; @@ -2572,7 +2624,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeBound { let last = args.len().saturating_sub(1); for (idx, arg) in args.iter().enumerate() { match arg { - UseArgRef::Lifetime(lt) => lt.hir_fmt(f, store)?, + UseArgRef::Lifetime(lt) => lt.hir_fmt(f, owner, store)?, UseArgRef::Name(n) => write!(f, "{}", n.display(f.db, edition))?, } if idx != last { @@ -2587,11 +2639,16 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeBound { } impl<'db> HirDisplayWithExpressionStore<'db> for Path { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { match (self.type_anchor(), self.kind()) { (Some(anchor), _) => { write!(f, "<")?; - anchor.hir_fmt(f, store)?; + anchor.hir_fmt(f, owner, store)?; write!(f, ">")?; } (_, PathKind::Plain) => {} @@ -2634,7 +2691,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { }); if let Some(ty) = trait_self_ty { write!(f, "<")?; - ty.hir_fmt(f, store)?; + ty.hir_fmt(f, owner, store)?; write!(f, " as ")?; // Now format the path of the trait... } @@ -2664,17 +2721,17 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { if let Some(v) = tuple { if v.len() == 1 { write!(f, "(")?; - v[0].hir_fmt(f, store)?; + v[0].hir_fmt(f, owner, store)?; write!(f, ")")?; } else { - generic_args.args[0].hir_fmt(f, store)?; + generic_args.args[0].hir_fmt(f, owner, store)?; } } if let Some(ret) = generic_args.bindings[0].type_ref && !matches!(&store[ret], TypeRef::Tuple(v) if v.is_empty()) { write!(f, " -> ")?; - ret.hir_fmt(f, store)?; + ret.hir_fmt(f, owner, store)?; } } hir_def::expr_store::path::GenericArgsParentheses::No => { @@ -2687,7 +2744,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { } else { write!(f, ", ")?; } - arg.hir_fmt(f, store)?; + arg.hir_fmt(f, owner, store)?; } for binding in generic_args.bindings.iter() { if first { @@ -2700,7 +2757,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { match &binding.type_ref { Some(ty) => { write!(f, " = ")?; - ty.hir_fmt(f, store)? + ty.hir_fmt(f, owner, store)? } None => { write!(f, ": ")?; @@ -2708,7 +2765,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { binding .bounds .iter() - .map(ExpressionStoreAdapter::wrap(store)), + .map(ExpressionStoreAdapter::wrap(owner, store)), " + ", )?; } @@ -2735,14 +2792,21 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { } impl<'db> HirDisplayWithExpressionStore<'db> for hir_def::expr_store::path::GenericArg { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { match self { - hir_def::expr_store::path::GenericArg::Type(ty) => ty.hir_fmt(f, store), + hir_def::expr_store::path::GenericArg::Type(ty) => ty.hir_fmt(f, owner, store), hir_def::expr_store::path::GenericArg::Const(_c) => { // write!(f, "{}", c.display(f.db, f.edition())) write!(f, "") } - hir_def::expr_store::path::GenericArg::Lifetime(lifetime) => lifetime.hir_fmt(f, store), + hir_def::expr_store::path::GenericArg::Lifetime(lifetime) => { + lifetime.hir_fmt(f, owner, store) + } } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs index 61e6720a29186..e1fbc85960899 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs @@ -6,7 +6,6 @@ use hir_def::{ }; use rustc_hash::FxHashSet; use rustc_type_ir::inherent::{AdtDef, GenericArgs as _, IntoKind}; -use stdx::never; use crate::{ consteval, @@ -100,8 +99,8 @@ fn has_drop_glue_impl<'db>( AdtId::EnumId(id) => id .enum_variants(db) .variants - .iter() - .map(|&(variant, _, _)| { + .values() + .map(|&(variant, _)| { db.field_types(variant.into()) .iter() .map(|(_, field_ty)| { @@ -177,9 +176,7 @@ fn has_drop_glue_impl<'db>( } } TyKind::Infer(..) => unreachable!("inference vars shouldn't exist out of inference"), - TyKind::Pat(..) | TyKind::UnsafeBinder(..) => { - never!("we do not handle pattern and unsafe binder types"); - DropGlue::None - } + TyKind::Pat(ty, _) => has_drop_glue_impl(infcx, ty, env, visited), + TyKind::UnsafeBinder(ty) => has_drop_glue_impl(infcx, ty.skip_binder(), env, visited), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 39ffb91a8c5db..2df2789a2eee9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -297,11 +297,19 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] has_rest: bool, }, + ArrayPatternWithoutFixedLength { + #[type_visitable(ignore)] + pat: PatId, + }, ExpectedArrayOrSlicePat { #[type_visitable(ignore)] pat: PatId, found: StoredTy, }, + InvalidRangePatType { + #[type_visitable(ignore)] + pat: PatId, + }, DuplicateField { #[type_visitable(ignore)] field: ExprOrPatId, @@ -361,6 +369,12 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] expr: ExprId, }, + NonExhaustiveRecordPat { + #[type_visitable(ignore)] + pat: PatId, + #[type_visitable(ignore)] + variant: VariantId, + }, FunctionalRecordUpdateOnNonStruct { #[type_visitable(ignore)] base_expr: ExprId, @@ -386,6 +400,21 @@ pub enum InferenceDiagnostic { call_expr: ExprId, found: StoredTy, }, + CannotBeDereferenced { + #[type_visitable(ignore)] + expr: ExprId, + found: StoredTy, + }, + CannotImplicitlyDerefTraitObject { + #[type_visitable(ignore)] + pat: PatId, + found: StoredTy, + }, + CannotIndexInto { + #[type_visitable(ignore)] + expr: ExprId, + found: StoredTy, + }, TypedHole { #[type_visitable(ignore)] expr: ExprId, @@ -428,6 +457,10 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] def: GenericDefId, }, + MethodCallIllegalSizedBound { + #[type_visitable(ignore)] + call_expr: ExprId, + }, MethodCallIncorrectGenericsOrder { #[type_visitable(ignore)] expr: ExprId, @@ -459,6 +492,20 @@ pub enum InferenceDiagnostic { found: StoredTy, }, SolverDiagnostic(SolverDiagnostic), + ExplicitDropMethodUse { + #[type_visitable(ignore)] + kind: ExplicitDropMethodUseKind, + }, + MutableRefBinding { + #[type_visitable(ignore)] + pat: PatId, + }, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum ExplicitDropMethodUseKind { + MethodCall(ExprId), + Path(ExprOrPatId), } /// Represents coercing a value to a different type of value. @@ -1289,7 +1336,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { resolver: Resolver<'db>, allow_using_generic_params: bool, ) -> Self { - let trait_env = db.trait_environment(store_owner); + let trait_env = db.trait_environment(generic_def); let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), store_owner); let types = crate::next_solver::default_types(db); InferenceContext { @@ -2438,8 +2485,8 @@ impl<'body, 'db> InferenceContext<'body, 'db> { }; let args = path_ctx.substs_from_path_segment(it.into(), true, None, false, node.into()); + let interner = path_ctx.interner(); drop(ctx); - let interner = DbInterner::conjure(); let ty = self.db.ty(it.into()).instantiate(interner, args).skip_norm_wip(); let ty = self.insert_type_vars(ty); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs index deafff6b43e06..2642844e38405 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs @@ -1023,6 +1023,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { | Pat::Tuple { .. } | Pat::Wild | Pat::Missing + | Pat::NotNull | Pat::Rest => { // If the PatKind is Or, Box, Ref, Guard, or Tuple, the relevant accesses // are made later as these patterns contains subpatterns. @@ -1473,7 +1474,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { fn variant_index_for_adt(&self, pat_id: PatId) -> Result<(u32, VariantId)> { let variant = self.cx.result.variant_resolution_for_pat(pat_id).ok_or(ErrorGuaranteed)?; let variant_idx = match variant { - VariantId::EnumVariantId(variant) => variant.loc(self.cx.db).index, + VariantId::EnumVariantId(variant) => variant.index(self.cx.db) as u32, VariantId::StructId(_) | VariantId::UnionId(_) => 0, }; Ok((variant_idx, variant)) @@ -1696,6 +1697,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { | Pat::Range { .. } | Pat::Missing | Pat::Rest + | Pat::NotNull | Pat::Wild => { // always ok } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 0675b5e8578fc..75e64403341c8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -8,7 +8,8 @@ use hir_def::{ expr_store::path::{GenericArgs as HirGenericArgs, Path}, hir::{ Array, AsmOperand, AsmOptions, BinaryOp, BindingAnnotation, Expr, ExprId, ExprOrPatId, - InlineAsmKind, LabelId, Pat, PatId, RecordLitField, RecordSpread, Statement, UnaryOp, + InlineAsmKind, LabelId, LoopSource, Pat, PatId, RecordLitField, RecordSpread, Statement, + UnaryOp, }, resolver::ValueNs, signatures::VariantFields, @@ -201,6 +202,7 @@ impl<'db> InferenceContext<'_, 'db> { | Pat::Slice { .. } | Pat::ConstBlock(_) | Pat::Record { .. } + | Pat::NotNull | Pat::Missing => true, Pat::Expr(_) => unreachable!( "we don't call pat_guaranteed_to_constitute_read_for_never() with assignments" @@ -305,7 +307,7 @@ impl<'db> InferenceContext<'_, 'db> { } } else { if let Some(expected_ty) = expected.only_has_type(&mut self.table) { - _ = self.demand_eqtype(expr.into(), ty, expected_ty); + _ = self.demand_eqtype(expr.into(), expected_ty, ty); } ty } @@ -400,24 +402,29 @@ impl<'db> InferenceContext<'_, 'db> { }) .1 } - &Expr::Loop { body, label } => { - let ty = expected.coercion_target_type(&mut self.table, tgt_expr.into()); + &Expr::Loop { body, label, source } => { + let coerce = match source { + // you can only use break with a value from a normal `loop { }` + LoopSource::Loop => { + Some(expected.coercion_target_type(&mut self.table, body.into())) + } + LoopSource::While | LoopSource::ForLoop => None, + }; let (breaks, ()) = - self.with_breakable_ctx(BreakableKind::Loop, Some(ty), label, |this| { - this.infer_expr( + self.with_breakable_ctx(BreakableKind::Loop, coerce, label, |this| { + this.infer_expr_suptype_coerce_never( body, &Expectation::HasType(this.types.types.unit), ExprIsRead::Yes, ); }); - match breaks { - Some(breaks) => { - self.diverges = Diverges::Maybe; - breaks - } - None => self.types.types.never, + if breaks.may_break { + self.diverges = Diverges::Maybe; + } else { + self.diverges = Diverges::Always; } + breaks.coerce.map(|c| c.complete(self)).unwrap_or(self.types.types.unit) } Expr::Closure { body, args, ret_type, arg_types, closure_kind, capture_by: _ } => self .infer_closure( @@ -728,8 +735,13 @@ impl<'db> InferenceContext<'_, 'db> { self.table.select_obligations_where_possible(); trait_element_ty } - // FIXME: Report an error. - None => self.types.types.error, + None => { + self.push_diagnostic(InferenceDiagnostic::CannotIndexInto { + expr: tgt_expr, + found: base_t.store(), + }); + self.types.types.error + } } } Expr::Tuple { exprs, .. } => { @@ -1294,7 +1306,10 @@ impl<'db> InferenceContext<'_, 'db> { if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) { oprnd_t = ty; } else { - // FIXME: Report an error. + self.push_diagnostic(InferenceDiagnostic::CannotBeDereferenced { + expr, + found: oprnd_t.store(), + }); oprnd_t = self.types.types.error; } } @@ -1490,10 +1505,11 @@ impl<'db> InferenceContext<'_, 'db> { label: Option, expected: &Expectation<'db>, ) -> Ty<'db> { + let prev_diverges = self.diverges; let coerce_ty = expected.coercion_target_type(&mut self.table, expr.into()); let g = self.resolver.update_to_inner_scope(self.db, self.store_owner, expr); - let (break_ty, ty) = + let (ctxt, tail_expr_ty) = self.with_breakable_ctx(BreakableKind::Block, Some(coerce_ty), label, |this| { for stmt in statements { match stmt { @@ -1561,42 +1577,54 @@ impl<'db> InferenceContext<'_, 'db> { } } - // FIXME: This should make use of the breakable CoerceMany - if let Some(expr) = tail { - this.infer_expr_coerce(expr, expected, ExprIsRead::Yes) - } else { - // Citing rustc: if there is no explicit tail expression, - // that is typically equivalent to a tail expression - // of `()` -- except if the block diverges. In that - // case, there is no value supplied from the tail - // expression (assuming there are no other breaks, - // this implies that the type of the block will be - // `!`). - if this.diverges.is_always() { - // we don't even make an attempt at coercion - this.table.new_maybe_never_var(expr.into()) - } else if let Some(t) = expected.only_has_type(&mut this.table) { - if this - .coerce( - expr, - this.types.types.unit, - t, - AllowTwoPhase::No, - ExprIsRead::Yes, - ) - .is_err() - { - this.emit_type_mismatch(expr.into(), t, this.types.types.unit); - } - t - } else { - this.types.types.unit - } - } + // check the tail expression **without** holding the + // `enclosing_breakables` lock below. + tail.map(|expr| (expr, this.infer_expr_inner(expr, expected, ExprIsRead::Yes))) }); + + let mut coerce = ctxt.coerce.unwrap(); + if let Some((tail_expr, tail_expr_ty)) = tail_expr_ty { + let cause = ObligationCause::new(tail_expr); + coerce.coerce_inner( + self, + &cause, + tail_expr, + tail_expr_ty, + false, + false, + ExprIsRead::Yes, + ); + } else { + // Subtle: if there is no explicit tail expression, + // that is typically equivalent to a tail expression + // of `()` -- except if the block diverges. In that + // case, there is no value supplied from the tail + // expression (assuming there are no other breaks, + // this implies that the type of the block will be + // `!`). + // + // #41425 -- label the implicit `()` as being the + // "found type" here, rather than the "expected type". + if !self.diverges.is_always() { + coerce.coerce_forced_unit( + self, + expr, + &ObligationCause::new(expr), + false, + ExprIsRead::Yes, + ); + } + } + + if ctxt.may_break { + // If we can break from the block, then the block's exit is always reachable + // (... as long as the entry is reachable) - regardless of the tail of the block. + self.diverges = prev_diverges; + } + self.resolver.reset_to_guard(g); - break_ty.unwrap_or(ty) + coerce.complete(self) } fn lookup_field( @@ -2169,13 +2197,13 @@ impl<'db> InferenceContext<'_, 'db> { ty: Option>, label: Option, cb: impl FnOnce(&mut Self) -> T, - ) -> (Option>, T) { + ) -> (BreakableContext<'db>, T) { self.breakables.push({ BreakableContext { kind, may_break: false, coerce: ty.map(CoerceMany::new), label } }); let res = cb(self); let ctx = self.breakables.pop().expect("breakable stack broken"); - (if ctx.may_break { ctx.coerce.map(|ctx| ctx.complete(self)) } else { None }, res) + (ctx, res) } } 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 c3b532638f9d3..483f54a2270a2 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 @@ -160,7 +160,7 @@ impl<'db> InferenceContext<'_, 'db> { | Expr::Range { rhs: Some(expr), lhs: None, range_type: _ } | Expr::Await { expr } | Expr::Box { expr } - | Expr::Loop { body: expr, label: _ } + | Expr::Loop { body: expr, label: _, source: _ } | Expr::Cast { expr, type_ref: _ } => { self.infer_mut_expr(*expr, Mutability::Not); } 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 f21438647c14f..c36c29d6c7294 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 @@ -448,7 +448,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { ) } Pat::Missing => self.types.types.error, - Pat::Wild | Pat::Rest => expected, + Pat::Wild | Pat::Rest | Pat::NotNull => expected, // We allow any type here; we ensure that the type is uninhabited during match checking. // Pat::Never => expected, Pat::Path(_) => { @@ -662,6 +662,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> { Pat::Ref { .. } // No need to do anything on a missing pattern. | Pat::Missing + // No need to do anything on a `NotNull` pattern, they are only allowed in type contexts. + | Pat::NotNull // A `_`/`..` pattern works with any expected type, so there's no need to do anything. | Pat::Wild | Pat::Rest // Bindings also work with whatever the expected type is, @@ -843,7 +845,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) { // There exists a side that didn't meet our criteria that the end-point // be of a numeric or char type, as checked in `calc_side` above. - // FIXME: Emit an error. + self.push_diagnostic(InferenceDiagnostic::InvalidRangePatType { pat }); return self.types.types.error; } @@ -891,14 +893,14 @@ impl<'a, 'db> InferenceContext<'a, 'db> { let user_bind_annot = BindingMode::from_annotation(binding_data.mode); let bm = match user_bind_annot { BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(_) = def_br => { - // Only mention the experimental `mut_ref` feature if if we're in edition 2024 and + // Only mention the experimental `mut_ref` feature if we're in edition 2024 and // using other experimental matching features compatible with it. if self.edition.at_least_2024() && (self.features.ref_pat_eat_one_layer_2024 || self.features.ref_pat_eat_one_layer_2024_structural) { if !self.features.mut_ref { - // FIXME: Emit an error: binding cannot be both mutable and by-reference. + self.push_diagnostic(InferenceDiagnostic::MutableRefBinding { pat }); } BindingMode(def_br, Mutability::Mut) @@ -957,22 +959,23 @@ impl<'a, 'db> InferenceContext<'a, 'db> { local_ty } - fn check_dereferenceable(&self, expected: Ty<'db>, inner: PatId) -> Result<(), ()> { + fn check_dereferenceable( + &mut self, + expected: Ty<'db>, + pat: PatId, + inner: PatId, + ) -> Result<(), ()> { if let Pat::Bind { .. } = self.store[inner] && let Some(pointee_ty) = self.shallow_resolve(expected).builtin_deref(true) && let TyKind::Dynamic(..) = pointee_ty.kind() { // This is "x = dyn SomeTrait" being reduced from // "let &x = &dyn SomeTrait" or "let box x = Box", an error. - // FIXME: Emit an error. rustc emits this message: - const _CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ: &str = "\ -This error indicates that a pointer to a trait type cannot be implicitly dereferenced by a \ -pattern. Every trait defines a type, but because the size of trait implementors isn't fixed, \ -this type has no compile-time size. Therefore, all accesses to trait types must be through \ -pointers. If you encounter this error you should try to avoid dereferencing the pointer. - -You can read more about trait objects in the Trait Objects section of the Reference: \ -https://doc.rust-lang.org/reference/types.html#trait-objects"; + self.push_diagnostic(InferenceDiagnostic::CannotImplicitlyDerefTraitObject { + pat, + found: expected.store(), + }); + return Err(()); } Ok(()) } @@ -1155,7 +1158,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; fn check_record_pat_fields( &mut self, adt_ty: Ty<'db>, - _pat: PatId, + pat: PatId, variant: VariantId, fields: &[RecordFieldPat], has_rest_pat: bool, @@ -1233,7 +1236,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; // Require `..` if struct has non_exhaustive attribute. let non_exhaustive = self.has_applicable_non_exhaustive(variant.into()); if non_exhaustive && !has_rest_pat { - // FIXME: Emit an error. + self.push_diagnostic(InferenceDiagnostic::NonExhaustiveRecordPat { pat, variant }); } // Report an error if an incorrect number of fields was specified. @@ -1258,7 +1261,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; ) -> Ty<'db> { let interner = self.interner(); let (box_ty, inner_ty) = self - .check_dereferenceable(expected, inner) + .check_dereferenceable(expected, pat, inner) .map(|()| { // Here, `demand::subtype` is good enough, but I don't // think any errors can be introduced by using `demand::eqtype`. @@ -1471,7 +1474,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; } } - let (ref_ty, inner_ty) = match self.check_dereferenceable(expected, inner) { + let (ref_ty, inner_ty) = match self.check_dereferenceable(expected, pat, inner) { Ok(()) => { // `demand::subtype` would be good enough, but using `eqtype` turns // out to be equally general. See (note_1) for details. @@ -1699,7 +1702,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; // We have a variable-length pattern and don't know the array length. // This happens if we have e.g., // `let [a, b, ..] = arr` where `arr: [T; N]` where `const N: usize`. - // FIXME: Emit an error: cannot pattern-match on an array without a fixed length. + self.push_diagnostic(InferenceDiagnostic::ArrayPatternWithoutFixedLength { pat }); }; // If we get here, we must have emitted an error. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 704f15cc86cc6..0ec72edc3d59c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -11,7 +11,7 @@ use rustc_type_ir::inherent::{SliceLike, Ty as _}; use stdx::never; use crate::{ - InferenceDiagnostic, Span, ValueTyDefId, + ExplicitDropMethodUseKind, InferenceDiagnostic, Span, ValueTyDefId, infer::{ InferenceTyLoweringVarsCtx, diagnostics::InferenceTyLoweringContext as TyLoweringContext, }, @@ -33,6 +33,14 @@ impl<'db> InferenceContext<'_, 'db> { ) -> Option<(ValueNs, Ty<'db>)> { let (value, self_subst) = self.resolve_value_path_inner(path, id, false)?; + if let ValueNs::FunctionId(f) = value + && self.lang_items.Drop_drop.is_some_and(|drop_fn| drop_fn == f) + { + self.push_diagnostic(InferenceDiagnostic::ExplicitDropMethodUse { + kind: ExplicitDropMethodUseKind::Path(id), + }); + } + let (value_def, generic_def, substs) = match self.resolve_value_path(path, id, value, self_subst)? { ValuePathResolution::GenericDef(value_def, generic_def, substs) => { @@ -183,7 +191,30 @@ impl<'db> InferenceContext<'_, 'db> { match value_or_partial { ResolveValueResult::ValueNs(it) => { drop_ctx(ctx, no_diagnostics); - (it, None) + + let args = if let Path::LangItem(..) = path { + let def_and_container = match it { + ValueNs::ConstId(it) => Some((it.into(), it.loc(self.db).container)), + ValueNs::FunctionId(it) => Some((it.into(), it.loc(self.db).container)), + _ => None, + }; + let def_and_container = + def_and_container.and_then(|(def, container)| match container { + ItemContainerId::ImplId(it) => Some((def, it.into())), + ItemContainerId::TraitId(it) => Some((def, it.into())), + ItemContainerId::ExternBlockId(_) + | ItemContainerId::ModuleId(_) => None, + }); + def_and_container.map(|(def, container)| { + let args = self.infcx().fresh_args_for_item(id.into(), container); + self.write_assoc_resolution(id, def, args); + args + }) + } else { + None + }; + + (it, args) } ResolveValueResult::Partial(def, remaining_index) => { // there may be more intermediate segments between the resolved one and diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index f9ad76b0c12c0..4c808714d9506 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -577,7 +577,10 @@ pub(super) mod resolve_completely { diagnostics.retain_mut(|diagnostic| { self.resolve_completely(diagnostic); - if let InferenceDiagnostic::ExpectedFunction { found: ty, .. } + if let InferenceDiagnostic::CannotBeDereferenced { found: ty, .. } + | InferenceDiagnostic::CannotImplicitlyDerefTraitObject { found: ty, .. } + | InferenceDiagnostic::CannotIndexInto { found: ty, .. } + | InferenceDiagnostic::ExpectedFunction { found: ty, .. } | InferenceDiagnostic::ExpectedArrayOrSlicePat { found: ty, .. } | InferenceDiagnostic::UnresolvedField { receiver: ty, .. } | InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs index 0070d14f373dc..26253fbabf1d4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs @@ -125,7 +125,7 @@ impl<'a, 'db> UninhabitedFrom<'a, 'db> { AdtId::EnumId(e) => { let enum_data = e.enum_variants(self.db()); - for &(variant, _, _) in enum_data.variants.iter() { + for &(variant, _) in enum_data.variants.values() { let variant_inhabitedness = self.visit_variant(variant.into(), subst); match variant_inhabitedness { Break(VisiblyUninhabited) => (), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 3e569076ad9ec..2b0a93e61d598 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -10,12 +10,12 @@ use hir_def::{ use la_arena::{Idx, RawIdx}; use rustc_abi::{ - AddressSpace, Float, Integer, LayoutCalculator, Primitive, ReprOptions, Scalar, StructKind, - TargetDataLayout, WrappingRange, + AddressSpace, BackendRepr, FieldsShape, Float, Integer, LayoutCalculator, Niche, Primitive, + ReprOptions, Scalar, Size, StructKind, TargetDataLayout, WrappingRange, }; use rustc_index::IndexVec; use rustc_type_ir::{ - FloatTy, IntTy, UintTy, + FloatTy, IntTy, TypeVisitableExt as _, UintTy, inherent::{GenericArgs as _, IntoKind}, }; use triomphe::Arc; @@ -25,7 +25,8 @@ use crate::{ consteval::try_const_usize, db::HirDatabase, next_solver::{ - DbInterner, GenericArgs, StoredTy, Ty, TyKind, TypingMode, + Const, ConstKind, DbInterner, GenericArgs, PatternKind, StoredTy, Ty, TyKind, TypingMode, + ValueConst, infer::{DbInternerInferExt, traits::ObligationCause}, }, traits::StoredParamEnvAndCrate, @@ -37,6 +38,9 @@ pub use self::{adt::layout_of_adt_query, target::target_data_layout_query}; pub(crate) mod adt; pub(crate) mod target; +#[cfg(test)] +mod tests; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct RustcEnumVariantIdx(pub usize); @@ -341,12 +345,151 @@ pub fn layout_of_ty_query( return db .layout_of_ty(args.as_coroutine_closure().tupled_upvars_ty().store(), trait_env); } - TyKind::CoroutineWitness(_, _) => { return Err(LayoutError::NotImplemented); } - TyKind::Pat(_, _) | TyKind::UnsafeBinder(_) => { + TyKind::Pat(ty, pat) => { + let mut layout = (*db.layout_of_ty(ty.store(), trait_env.clone())?).clone(); + match pat.kind() { + PatternKind::Range { start, end } => { + if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr { + scalar.valid_range_mut().start = extract_const_value(start)? + .try_to_bits(db, trait_env.as_ref()) + .ok_or(LayoutError::Unknown)?; + + scalar.valid_range_mut().end = extract_const_value(end)? + .try_to_bits(db, trait_env.as_ref()) + .ok_or(LayoutError::Unknown)?; + + // FIXME(pattern_types): create implied bounds from pattern types in signatures + // that require that the range end is >= the range start so that we can't hit + // this error anymore without first having hit a trait solver error. + // Very fuzzy on the details here, but pattern types are an internal impl detail, + // so we can just go with this for now + if scalar.is_signed() { + let range = scalar.valid_range_mut(); + let start = layout.size.sign_extend(range.start); + let end = layout.size.sign_extend(range.end); + if end < start { + return Err(LayoutError::HasErrorType); + } + } else { + let range = scalar.valid_range_mut(); + if range.end < range.start { + return Err(LayoutError::HasErrorType); + } + }; + + let niche = Niche { + offset: Size::ZERO, + value: scalar.primitive(), + valid_range: scalar.valid_range(target), + }; + + layout.largest_niche = Some(niche); + } else { + panic!("pattern type with range but not scalar layout: {ty:?}, {layout:?}") + } + } + PatternKind::NotNull => { + if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) = + &mut layout.backend_repr + { + scalar.valid_range_mut().start = 1; + let niche = Niche { + offset: Size::ZERO, + value: scalar.primitive(), + valid_range: scalar.valid_range(target), + }; + + layout.largest_niche = Some(niche); + } else { + panic!( + "pattern type with `!null` pattern but not scalar/pair layout: {ty:?}, {layout:?}" + ) + } + } + + PatternKind::Or(variants) => match variants[0].kind() { + PatternKind::Range { .. } => { + if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr { + let variants: Result, _> = variants + .iter() + .map(|pat| match pat.kind() { + PatternKind::Range { start, end } => Ok::<_, LayoutError>(( + extract_const_value(start) + .unwrap() + .try_to_bits(db, trait_env.as_ref()) + .ok_or(LayoutError::Unknown)?, + extract_const_value(end) + .unwrap() + .try_to_bits(db, trait_env.as_ref()) + .ok_or(LayoutError::Unknown)?, + )), + PatternKind::NotNull | PatternKind::Or(_) => { + unreachable!("mixed or patterns are not allowed") + } + }) + .collect(); + let mut variants = variants?; + if !scalar.is_signed() { + return Err(LayoutError::HasErrorType); + } + variants.sort(); + if variants.len() != 2 { + return Err(LayoutError::HasErrorType); + } + + // first is the one starting at the signed in range min + let mut first = variants[0]; + let mut second = variants[1]; + if second.0 + == layout.size.truncate(layout.size.signed_int_min() as u128) + { + (second, first) = (first, second); + } + + if layout.size.sign_extend(first.1) >= layout.size.sign_extend(second.0) + { + return Err(LayoutError::HasErrorType); + } + if layout.size.signed_int_max() as u128 != second.1 { + return Err(LayoutError::HasErrorType); + } + + // Now generate a wrapping range (which aren't allowed in surface syntax). + scalar.valid_range_mut().start = second.0; + scalar.valid_range_mut().end = first.1; + + let niche = Niche { + offset: Size::ZERO, + value: scalar.primitive(), + valid_range: scalar.valid_range(target), + }; + + layout.largest_niche = Some(niche); + } else { + panic!( + "pattern type with range but not scalar layout: {ty:?}, {layout:?}" + ) + } + } + PatternKind::NotNull => panic!("or patterns can't contain `!null` patterns"), + PatternKind::Or(..) => panic!("patterns cannot have nested or patterns"), + }, + } + // Pattern types contain their base as their sole field. + // This allows the rest of the compiler to process pattern types just like + // single field transparent Adts, and only the parts of the compiler that + // specifically care about pattern types will have to handle it. + layout.fields = FieldsShape::Arbitrary { + offsets: [Size::ZERO].into_iter().collect(), + in_memory_order: [RustcFieldIdx::new(0)].into_iter().collect(), + }; + layout + } + TyKind::UnsafeBinder(_) => { return Err(LayoutError::NotImplemented); } @@ -371,6 +514,25 @@ pub(crate) fn layout_of_ty_cycle_result( Err(LayoutError::RecursiveTypeWithoutIndirection) } +fn extract_const_value<'db>(ct: Const<'db>) -> Result, LayoutError> { + match ct.kind() { + ConstKind::Value(cv) => Ok(cv), + ConstKind::Param(_) + | ConstKind::Expr(_) + | ConstKind::Unevaluated(_) + | ConstKind::Infer(_) + | ConstKind::Bound(..) + | ConstKind::Placeholder(_) => { + if ct.has_param() { + Err(LayoutError::HasPlaceholder) + } else { + Err(LayoutError::Unknown) + } + } + ConstKind::Error(_) => Err(LayoutError::HasErrorConst), + } +} + fn struct_tail_erasing_lifetimes<'a>(db: &'a dyn HirDatabase, pointee: Ty<'a>) -> Ty<'a> { match pointee.kind() { TyKind::Adt(def, args) => { @@ -411,6 +573,3 @@ fn field_ty<'a>( fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar { Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) } } - -#[cfg(test)] -mod tests; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index b7e1697059633..22dd53ca2dd01 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -60,8 +60,8 @@ pub fn layout_of_adt_query( let variants = e.enum_variants(db); let r = variants .variants - .iter() - .map(|&(v, _, _)| handle_variant(v.into(), v.fields(db))) + .values() + .map(|&(v, _)| handle_variant(v.into(), v.fields(db))) .collect::, _>>()?; (r, AttrFlags::repr(db, e.into()).unwrap_or_default(), false) } 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 bc18f05790f02..afbafb3fea7dc 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 @@ -1,7 +1,7 @@ use base_db::target::TargetData; use either::Either; use hir_def::{ - DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, HasModule, + DefWithBodyId, HasModule, expr_store::Body, signatures::{ EnumSignature, FunctionSignature, StructSignature, TypeAliasSignature, UnionSignature, @@ -92,13 +92,10 @@ fn eval_goal( ), Either::Right(ty_id) => db.ty(ty_id.into()).instantiate_identity().skip_norm_wip(), }; - let param_env = db.trait_environment( - match adt_or_type_alias_id { - Either::Left(adt) => hir_def::GenericDefId::AdtId(adt), - Either::Right(ty) => hir_def::GenericDefId::TypeAliasId(ty), - } - .into(), - ); + let param_env = db.trait_environment(match adt_or_type_alias_id { + Either::Left(adt) => hir_def::GenericDefId::AdtId(adt), + Either::Right(ty) => hir_def::GenericDefId::TypeAliasId(ty), + }); let krate = match adt_or_type_alias_id { Either::Left(it) => it.krate(&db), Either::Right(it) => it.krate(&db), @@ -145,8 +142,7 @@ fn eval_expr( .0; let infer = InferenceResult::of(&db, DefWithBodyId::from(function_id)); let goal_ty = infer.type_of_binding[b].clone(); - let param_env = - db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from(function_id))); + let param_env = db.trait_environment(function_id.into()); let krate = function_id.krate(&db); db.layout_of_ty(goal_ty, ParamEnvAndCrate { param_env, krate }.store()) }) @@ -182,6 +178,7 @@ fn check_fail(#[rust_analyzer::rust_fixture] ra_fixture: &str, e: LayoutError) { assert_eq!(r, Err(e)); } +#[rust_analyzer::macro_style(braces)] macro_rules! size_and_align { (minicore: $($x:tt),*;$($t:tt)*) => { { @@ -444,6 +441,7 @@ fn return_position_impl_trait() { // but rustc actually runs this code. let pinned = pin!(inp); struct EmptyWaker; + #[expect(clippy::manual_noop_waker, reason = "we don't have access to std here")] impl Wake for EmptyWaker { fn wake(self: Arc) { } @@ -529,13 +527,21 @@ fn tuple_ptr_with_dst_tail() { } #[test] -#[ignore = "FIXME: We need to have proper pattern types"] fn non_zero_and_non_null() { size_and_align! { minicore: non_zero, non_null, option; use core::{num::NonZeroU8, ptr::NonNull}; struct Goal(Option, Option>); } + check_size_and_align( + r#" +const END: usize = 10; +struct Goal(core::pattern_type!(usize is 0..=END)); + "#, + "//- minicore: pat\n", + 8, + 8, + ); } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 91e3b85aa1311..f612bdc266970 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -104,9 +104,10 @@ use crate::{ pub use autoderef::autoderef; pub use infer::{ - Adjust, Adjustment, AutoBorrow, BindingMode, ByRef, InferenceDiagnostic, InferenceResult, - InferenceTyDiagnosticSource, OverloadedDeref, PointerCast, cast::CastError, could_coerce, - could_unify, could_unify_deeply, infer_query_with_inspect, + Adjust, Adjustment, AutoBorrow, BindingMode, ByRef, ExplicitDropMethodUseKind, + InferenceDiagnostic, InferenceResult, InferenceTyDiagnosticSource, OverloadedDeref, + PointerCast, cast::CastError, could_coerce, could_unify, could_unify_deeply, + infer_query_with_inspect, }; pub use lower::{ GenericDefaults, GenericDefaultsRef, GenericPredicates, ImplTraits, LifetimeElisionKind, @@ -384,7 +385,7 @@ pub fn associated_type_shorthand_candidates( let mut dedup_map = FxHashSet::default(); let param_ty = Ty::new_param(interner, param, type_or_const_param_idx(db, param.into())); // We use the ParamEnv and not the predicates because the ParamEnv elaborates bounds. - let param_env = db.trait_environment(ExpressionStoreOwnerId::from(def)); + let param_env = db.trait_environment(def); for clause in param_env.clauses { let ClauseKind::Trait(trait_clause) = clause.kind().skip_binder() else { continue }; if trait_clause.self_ty() != param_ty { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 5b0bcd2be8380..df83b2abb870f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -18,9 +18,12 @@ use hir_def::{ TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId, builtin_type::BuiltinType, expr_store::{ExpressionStore, path::Path}, - hir::generics::{ - GenericParamDataRef, GenericParams, LocalTypeOrConstParamId, TypeOrConstParamData, - TypeParamProvenance, WherePredicate, + hir::{ + ExprId, PatId, + generics::{ + GenericParamDataRef, GenericParams, LocalTypeOrConstParamId, TypeOrConstParamData, + TypeParamProvenance, WherePredicate, + }, }, item_tree::FieldsShape, lang_item::LangItems, @@ -60,10 +63,10 @@ use crate::{ next_solver::{ AliasTy, Binder, BoundExistentialPredicates, Clause, ClauseKind, Clauses, Const, ConstKind, DbInterner, DefaultAny, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, FnSigKind, - FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, PolyFnSig, Predicate, Region, - StoredClauses, StoredEarlyBinder, StoredGenericArg, StoredGenericArgs, StoredPolyFnSig, - StoredTraitRef, StoredTy, TraitPredicate, TraitRef, Ty, Tys, Unnormalized, abi::Safety, - util::BottomUpFolder, + FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, PatList, Pattern, PolyFnSig, + Predicate, Region, StoredClauses, StoredEarlyBinder, StoredGenericArg, StoredGenericArgs, + StoredPolyFnSig, StoredTraitRef, StoredTy, TraitPredicate, TraitRef, Ty, Tys, Unnormalized, + abi::Safety, util::BottomUpFolder, }, }; @@ -198,7 +201,7 @@ pub trait TyLoweringInferVarsCtx<'db> { pub struct TyLoweringContext<'db, 'a> { pub db: &'db dyn HirDatabase, - interner: DbInterner<'db>, + pub(crate) interner: DbInterner<'db>, types: &'db crate::next_solver::DefaultAny<'db>, lang_items: &'db LangItems, resolver: &'a Resolver<'db>, @@ -357,6 +360,14 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } pub(crate) fn lower_const(&mut self, const_ref: ConstRef, const_type: Ty<'db>) -> Const<'db> { + self.lower_expr_as_const(const_ref.expr, const_type) + } + + pub(crate) fn lower_expr_as_const( + &mut self, + expr_id: ExprId, + const_type: Ty<'db>, + ) -> Const<'db> { #[expect(clippy::manual_map, reason = "a `map()` here generates a borrowck error")] let create_var = match &mut self.infer_vars { Some(infer_vars) => Some( @@ -368,7 +379,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self.interner, self.def, self.store, - const_ref.expr, + expr_id, self.resolver, const_type, &|| self.generics.get_or_init(|| generics(self.db, self.generic_def)), @@ -528,11 +539,43 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } } } + &TypeRef::PatternType(ty, pat) => { + let ty = self.lower_ty(ty); + let Some(pat) = self.lower_pattern_type(pat, ty) else { + return (self.types.types.error, res); + }; + Ty::new_pat(self.interner, ty, pat) + } TypeRef::Error => self.types.types.error, }; (ty, res) } + fn lower_pattern_type(&mut self, pat: PatId, ty: Ty<'db>) -> Option> { + let pat_kind = match self.store[pat] { + hir_def::hir::Pat::Range { start: Some(start), end: Some(end), range_type: _ } => { + rustc_type_ir::PatternKind::Range { + start: self.lower_expr_as_const(start, ty), + end: self.lower_expr_as_const(end, ty), + } + } + hir_def::hir::Pat::NotNull => rustc_type_ir::PatternKind::NotNull, + hir_def::hir::Pat::Or(ref pats) => rustc_type_ir::PatternKind::Or( + PatList::new_from_iter( + self.interner, + pats.iter().map(|&pat| self.lower_pattern_type(pat, ty).ok_or(())), + ) + .ok()?, + ), + hir_def::hir::Pat::Missing => return None, + _ => { + never!("pattern type can only be Range, NotNull or Or"); + return None; + } + }; + Some(Pattern::new(self.interner, pat_kind)) + } + fn lower_fn_ptr(&mut self, fn_: &FnType) -> Ty<'db> { let interner = self.interner; let (params, ret_ty) = fn_.split_params_and_ret(); @@ -2072,12 +2115,12 @@ impl<'db> GenericPredicates { /// A cycle can occur from malformed code. fn generic_predicates_cycle_result( - _db: &dyn HirDatabase, + db: &dyn HirDatabase, _: salsa::Id, _def: GenericDefId, ) -> TyLoweringResult { TyLoweringResult::empty(GenericPredicates::from_explicit_own_predicates( - StoredEarlyBinder::bind(Clauses::default().store()), + StoredEarlyBinder::bind(Clauses::empty(DbInterner::new_no_crate(db)).store()), )) } @@ -2086,7 +2129,7 @@ impl GenericPredicates { pub fn empty() -> &'static GenericPredicates { static EMPTY: OnceLock = OnceLock::new(); EMPTY.get_or_init(|| GenericPredicates { - predicates: StoredEarlyBinder::bind(Clauses::default().store()), + predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&[]).store()), has_trait_implied_predicate: false, parent_explicit_self_predicates_start: 0, own_predicates_start: 0, @@ -2197,12 +2240,7 @@ pub(crate) fn param_env_from_predicates<'db>( ParamEnv { clauses } } -pub(crate) fn trait_environment<'db>( - db: &'db dyn HirDatabase, - def: ExpressionStoreOwnerId, -) -> ParamEnv<'db> { - let def = def.generic_def(db); - +pub(crate) fn trait_environment<'db>(db: &'db dyn HirDatabase, def: GenericDefId) -> ParamEnv<'db> { return ParamEnv { clauses: trait_environment_query(db, def).as_ref() }; #[salsa::tracked(returns(ref))] 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 ff9718af111be..6633215679873 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 @@ -994,6 +994,10 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { }) }) } + + pub(crate) fn interner(&self) -> DbInterner<'db> { + self.ctx.interner + } } /// A const that were parsed like a type. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 5e90e371fcd9c..9e0188fd2604e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -35,7 +35,7 @@ use stdx::impl_from; use triomphe::Arc; use crate::{ - Span, all_super_traits, + InferenceDiagnostic, Span, all_super_traits, db::HirDatabase, infer::{InferenceContext, unify::InferenceTable}, lower::GenericPredicates, @@ -148,7 +148,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { debug!("result = {:?}", result); if result.illegal_sized_bound { - // FIXME: Report an error. + self.push_diagnostic(InferenceDiagnostic::MethodCallIllegalSizedBound { call_expr }); } self.write_expr_adj(receiver, result.adjustments); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs index c425e69dc59dd..d960a6547d462 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs @@ -18,7 +18,7 @@ use crate::{ Adjust, Adjustment, AutoBorrow, IncorrectGenericsLenKind, InferenceDiagnostic, LifetimeElisionKind, PointerCast, Span, db::HirDatabase, - infer::{AllowTwoPhase, AutoBorrowMutability, InferenceContext}, + infer::{AllowTwoPhase, AutoBorrowMutability, ExplicitDropMethodUseKind, InferenceContext}, lower::{ GenericPredicates, path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings}, @@ -582,7 +582,9 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { fn check_for_illegal_method_calls(&self) { // Disallow calls to the method `drop` defined in the `Drop` trait. if self.ctx.lang_items.Drop_drop.is_some_and(|drop_fn| drop_fn == self.candidate) { - // FIXME: Report an error. + self.ctx.push_diagnostic(InferenceDiagnostic::ExplicitDropMethodUse { + kind: ExplicitDropMethodUseKind::MethodCall(self.call_expr), + }); } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs index 4b2f0cfd7066b..84edb510237d6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs @@ -3,6 +3,7 @@ use std::{cell::RefCell, convert::Infallible, ops::ControlFlow}; +use base_db::FxIndexMap; use hir_def::{ AssocItemId, FunctionId, GenericParamId, ImplId, ItemContainerId, TraitId, hir::generics::GenericParams, @@ -10,7 +11,7 @@ use hir_def::{ }; use hir_expand::name::Name; use rustc_ast_ir::Mutability; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashSet; use rustc_type_ir::{ InferTy, TypeVisitableExt, Upcast, Variance, elaborate::{self, supertrait_def_ids}, @@ -719,7 +720,7 @@ impl<'db> ProbeChoice<'db> for ProbeForNameChoice<'db> { #[derive(Debug)] struct ProbeAllChoice<'db> { - candidates: RefCell>>, + candidates: RefCell>>, considering_visible_candidates: bool, } @@ -1294,6 +1295,15 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { return ControlFlow::Break(by_value_pick); } + if self.mode == Mode::Path { + // Don't autoref in path mode. + // rustc doesn't do that and it's not a big deal as non-autorefd methods take priority + // and if an autorefd one is selected, we'll register the `NonAutorefdT: Trait` obligation + // (which will fail) anyway. But it does have an impact when probing for all methods, + // which is something we need to stay accurate. + return ControlFlow::Continue(()); + } + let autoref_pick = self.pick_autorefd_method( step, self_ty, 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 5f61b1defb8c0..e82038907c449 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -1,6 +1,6 @@ //! MIR definitions and implementation -use std::{collections::hash_map::Entry, fmt::Display, iter}; +use std::{fmt::Display, iter}; use base_db::Crate; use either::Either; @@ -8,10 +8,14 @@ use hir_def::{ FieldId, StaticId, TupleFieldId, UnionId, VariantId, hir::{BindingId, Expr, ExprId, Ordering, PatId}, }; +use intern::{InternedSlice, InternedSliceRef, impl_slice_internable}; use la_arena::{Arena, ArenaMap, Idx, RawIdx}; use rustc_ast_ir::Mutability; use rustc_hash::FxHashMap; -use rustc_type_ir::inherent::{GenericArgs as _, IntoKind, Ty as _}; +use rustc_type_ir::{ + CollectAndApply, GenericTypeVisitable, + inherent::{GenericArgs as _, IntoKind, Ty as _}, +}; use smallvec::{SmallVec, smallvec}; use stdx::{impl_from, never}; @@ -24,6 +28,7 @@ use crate::{ next_solver::{ Allocation, AllocationData, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, StoredAllocation, StoredConst, StoredGenericArgs, StoredTy, Ty, TyKind, + impl_stored_interned_slice, infer::{InferCtxt, traits::ObligationCause}, obligation_ctxt::ObligationCtxt, }, @@ -145,7 +150,7 @@ impl<'db> Operand { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ProjectionElem { Deref, Field(Either), @@ -155,7 +160,7 @@ pub enum ProjectionElem { ConstantIndex { offset: u64, from_end: bool }, Subslice { from: u64, to: u64 }, //Downcast(Option, VariantIdx), - OpaqueCast(StoredTy), + OpaqueCast(std::convert::Infallible), } impl ProjectionElem { @@ -260,97 +265,114 @@ impl ProjectionElem { type PlaceElem = ProjectionElem; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct ProjectionId(u32); +impl GenericTypeVisitable for PlaceElem { + fn generic_visit_with(&self, _: &mut W) {} +} + +impl_slice_internable!(gc; ProjectionStorage, (), PlaceElem); +impl_stored_interned_slice!(ProjectionStorage, Projection, StoredProjection); -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ProjectionStore { - id_to_proj: FxHashMap>, - proj_to_id: FxHashMap, ProjectionId>, +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct Projection<'db> { + interned: InternedSliceRef<'db, ProjectionStorage>, } -impl Default for ProjectionStore { - fn default() -> Self { - let mut this = Self { id_to_proj: Default::default(), proj_to_id: Default::default() }; - // Ensure that [] will get the id 0 which is used in `ProjectionId::Empty` - this.intern(Box::new([])); - this +impl<'db> std::fmt::Debug for Projection<'db> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + (*self).as_slice().fmt(fmt) } } -impl ProjectionStore { - pub fn shrink_to_fit(&mut self) { - self.id_to_proj.shrink_to_fit(); - self.proj_to_id.shrink_to_fit(); +impl<'db> Projection<'db> { + pub fn new_from_iter(args: I) -> T::Output + where + I: IntoIterator, + T: CollectAndApply, + { + CollectAndApply::collect_and_apply(args.into_iter(), Self::new_from_slice) } - pub fn intern_if_exist(&self, projection: &[PlaceElem]) -> Option { - self.proj_to_id.get(projection).copied() + #[inline] + pub fn new_from_slice(slice: &[PlaceElem]) -> Self { + Self { interned: InternedSlice::from_header_and_slice((), slice) } } - pub fn intern(&mut self, projection: Box<[PlaceElem]>) -> ProjectionId { - let new_id = ProjectionId(self.proj_to_id.len() as u32); - match self.proj_to_id.entry(projection) { - Entry::Occupied(id) => *id.get(), - Entry::Vacant(e) => { - let key_clone = e.key().clone(); - e.insert(new_id); - self.id_to_proj.insert(new_id, key_clone); - new_id - } - } + #[inline] + pub fn as_slice(self) -> &'db [PlaceElem] { + &self.interned.get().slice + } + + pub fn project(self, projection: PlaceElem) -> Projection<'db> { + Projection::new_from_iter(self.as_slice().iter().copied().chain([projection])) } } -impl ProjectionId { - pub const EMPTY: ProjectionId = ProjectionId(0); +impl<'db> std::ops::Deref for Projection<'db> { + type Target = [PlaceElem]; - pub fn is_empty(self) -> bool { - self == ProjectionId::EMPTY + fn deref(&self) -> &Self::Target { + self.as_slice() } +} - pub fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] { - store.id_to_proj.get(&self).unwrap() +impl StoredProjection { + // FIXME: rename to as_slice + pub fn lookup(&self) -> &[PlaceElem] { + self.as_ref().as_slice() } - pub fn project(self, projection: PlaceElem, store: &mut ProjectionStore) -> ProjectionId { - let mut current = self.lookup(store).to_vec(); - current.push(projection); - store.intern(current.into()) + pub fn is_empty(&self) -> bool { + self.lookup().is_empty() } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +// FIXME: would be nicer to rename PlaceRef -> Place, Place -> StoredPlace, but I didn't want to blow up the diff +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct PlaceRef<'db> { + pub local: LocalId, + pub projection: Projection<'db>, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Place { pub local: LocalId, - pub projection: ProjectionId, + pub projection: StoredProjection, } impl Place { - fn is_parent(&self, child: &Place, store: &ProjectionStore) -> bool { + pub fn as_ref<'db>(&self) -> PlaceRef<'db> { + PlaceRef { local: self.local, projection: self.projection.as_ref() } + } +} + +impl<'db> PlaceRef<'db> { + fn is_parent(&self, child: PlaceRef<'db>) -> bool { self.local == child.local - && child.projection.lookup(store).starts_with(self.projection.lookup(store)) + && child.projection.as_slice().starts_with(self.projection.as_slice()) } /// The place itself is not included - fn iterate_over_parents<'a>( - &'a self, - store: &'a ProjectionStore, - ) -> impl Iterator + 'a { - let projection = self.projection.lookup(store); - (0..projection.len()).map(|x| &projection[0..x]).filter_map(move |x| { - Some(Place { local: self.local, projection: store.intern_if_exist(x)? }) + fn iterate_over_parents<'a>(&'a self) -> impl Iterator> + 'a { + let projection = self.projection.as_slice(); + (0..projection.len()).map(move |x| PlaceRef { + local: self.local, + projection: Projection::new_from_slice(&projection[0..x]), }) } - fn project(&self, projection: PlaceElem, store: &mut ProjectionStore) -> Place { - Place { local: self.local, projection: self.projection.project(projection, store) } + fn project(&self, projection: PlaceElem) -> PlaceRef<'db> { + PlaceRef { local: self.local, projection: self.projection.project(projection) } + } + + pub fn store(&self) -> Place { + Place { local: self.local, projection: self.projection.store() } } } -impl From for Place { +impl<'db> From for PlaceRef<'db> { fn from(local: LocalId) -> Self { - Self { local, projection: ProjectionId::EMPTY } + let empty: &[PlaceElem] = &[]; + PlaceRef { local, projection: Projection::new_from_slice(empty) } } } @@ -1081,7 +1103,6 @@ pub struct BasicBlock { #[derive(Debug, Clone, PartialEq, Eq)] pub struct MirBody { - pub projection_store: ProjectionStore, pub basic_blocks: Arena, pub locals: Arena, pub start_block: BasicBlockId, @@ -1099,15 +1120,11 @@ impl MirBody { self.binding_locals.iter().map(|(it, y)| (*y, it)).collect() } - fn walk_places(&mut self, mut f: impl FnMut(&mut Place, &mut ProjectionStore)) { - fn for_operand( - op: &mut Operand, - f: &mut impl FnMut(&mut Place, &mut ProjectionStore), - store: &mut ProjectionStore, - ) { + fn walk_places(&mut self, mut f: impl FnMut(&mut Place)) { + fn for_operand(op: &mut Operand, f: &mut impl FnMut(&mut Place)) { match &mut op.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { - f(p, store); + f(p); } OperandKind::Constant { .. } | OperandKind::Static(_) @@ -1118,25 +1135,25 @@ impl MirBody { for statement in &mut block.statements { match &mut statement.kind { StatementKind::Assign(p, r) => { - f(p, &mut self.projection_store); + f(p); match r { Rvalue::ShallowInitBoxWithAlloc(_) => (), Rvalue::ShallowInitBox(o, _) | Rvalue::UnaryOp(_, o) | Rvalue::Cast(_, o, _) | Rvalue::Repeat(o, _) - | Rvalue::Use(o) => for_operand(o, &mut f, &mut self.projection_store), + | Rvalue::Use(o) => for_operand(o, &mut f), Rvalue::CopyForDeref(p) | Rvalue::Discriminant(p) | Rvalue::Len(p) - | Rvalue::Ref(_, p) => f(p, &mut self.projection_store), + | Rvalue::Ref(_, p) => f(p), Rvalue::CheckedBinaryOp(_, o1, o2) => { - for_operand(o1, &mut f, &mut self.projection_store); - for_operand(o2, &mut f, &mut self.projection_store); + for_operand(o1, &mut f); + for_operand(o2, &mut f); } Rvalue::Aggregate(_, ops) => { for op in ops.iter_mut() { - for_operand(op, &mut f, &mut self.projection_store); + for_operand(op, &mut f); } } Rvalue::ThreadLocalRef(n) @@ -1145,9 +1162,7 @@ impl MirBody { | Rvalue::NullaryOp(n) => match *n {}, } } - StatementKind::FakeRead(p) | StatementKind::Deinit(p) => { - f(p, &mut self.projection_store) - } + StatementKind::FakeRead(p) | StatementKind::Deinit(p) => f(p), StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Nop => (), @@ -1155,9 +1170,7 @@ impl MirBody { } match &mut block.terminator { Some(x) => match &mut x.kind { - TerminatorKind::SwitchInt { discr, .. } => { - for_operand(discr, &mut f, &mut self.projection_store) - } + TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, &mut f), TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Goto { .. } @@ -1167,24 +1180,23 @@ impl MirBody { | TerminatorKind::Return | TerminatorKind::Unreachable => (), TerminatorKind::Drop { place, .. } => { - f(place, &mut self.projection_store); + f(place); } TerminatorKind::DropAndReplace { place, value, .. } => { - f(place, &mut self.projection_store); - for_operand(value, &mut f, &mut self.projection_store); + f(place); + for_operand(value, &mut f); } TerminatorKind::Call { func, args, destination, .. } => { - for_operand(func, &mut f, &mut self.projection_store); - args.iter_mut() - .for_each(|x| for_operand(x, &mut f, &mut self.projection_store)); - f(destination, &mut self.projection_store); + for_operand(func, &mut f); + args.iter_mut().for_each(|x| for_operand(x, &mut f)); + f(destination); } TerminatorKind::Assert { cond, .. } => { - for_operand(cond, &mut f, &mut self.projection_store); + for_operand(cond, &mut f); } TerminatorKind::Yield { value, resume_arg, .. } => { - for_operand(value, &mut f, &mut self.projection_store); - f(resume_arg, &mut self.projection_store); + for_operand(value, &mut f); + f(resume_arg); } }, None => (), @@ -1202,9 +1214,7 @@ impl MirBody { upvar_locals, param_locals, closures, - projection_store, } = self; - projection_store.shrink_to_fit(); basic_blocks.shrink_to_fit(); locals.shrink_to_fit(); binding_locals.shrink_to_fit(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs index 940bc572595e5..ff963fc121205 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs @@ -141,7 +141,7 @@ pub fn borrowck_query( let _p = tracing::info_span!("borrowck_query").entered(); let module = def.module(db); let interner = DbInterner::new_with(db, module.krate(db)); - let env = db.trait_environment(def.expression_store_owner(db)); + let env = db.trait_environment(def.generic_def(db)); // This calculates opaques defining scope which is a bit costly therefore is put outside `all_mir_bodies()`. let typing_mode = TypingMode::borrowck(interner, def.into()); let res = all_mir_bodies( @@ -195,11 +195,11 @@ fn moved_out_of_ref<'db>( ) -> Vec { let db = infcx.interner.db; let mut result = vec![]; - let mut for_operand = |op: &Operand, span: MirSpan| match op.kind { + let mut for_operand = |op: &Operand, span: MirSpan| match &op.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { let mut ty: Ty<'db> = body.locals[p.local].ty.as_ref(); let mut is_dereference_of_ref = false; - for proj in p.projection.lookup(&body.projection_store) { + for proj in p.projection.lookup() { if *proj == ProjectionElem::Deref && ty.as_reference().is_some() { is_dereference_of_ref = true; } @@ -290,10 +290,10 @@ fn partially_moved<'db>( ) -> Vec { let db = infcx.interner.db; let mut result = vec![]; - let mut for_operand = |op: &Operand, span: MirSpan| match op.kind { + let mut for_operand = |op: &Operand, span: MirSpan| match &op.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { let mut ty: Ty<'db> = body.locals[p.local].ty.as_ref(); - for proj in p.projection.lookup(&body.projection_store) { + for proj in p.projection.lookup() { ty = proj.projected_ty(infcx, env, ty, body.owner.module(db).krate(db)); } if !infcx.type_is_copy_modulo_regions(env, ty) && !ty.references_non_lt_error() { @@ -430,7 +430,7 @@ fn place_case<'db>( let db = infcx.interner.db; let mut is_part_of = false; let mut ty = body.locals[lvalue.local].ty.as_ref(); - for proj in lvalue.projection.lookup(&body.projection_store).iter() { + for proj in lvalue.projection.lookup().iter() { match proj { ProjectionElem::Deref if ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw ProjectionElem::Deref // It's direct in case of `Box` @@ -470,7 +470,7 @@ fn ever_initialized_map( for statement in &block.statements { match &statement.kind { StatementKind::Assign(p, _) => { - if p.projection.lookup(&body.projection_store).is_empty() && p.local == l { + if p.projection.is_empty() && p.local == l { is_ever_initialized = true; } } @@ -508,9 +508,7 @@ fn ever_initialized_map( | TerminatorKind::Return | TerminatorKind::Unreachable => (), TerminatorKind::Call { target, cleanup, destination, .. } => { - if destination.projection.lookup(&body.projection_store).is_empty() - && destination.local == l - { + if destination.projection.is_empty() && destination.local == l { is_ever_initialized = true; } target.iter().chain(cleanup).for_each(|&it| process(it, is_ever_initialized)); @@ -566,7 +564,7 @@ fn record_usage(local: LocalId, result: &mut ArenaMap } fn record_usage_for_operand(arg: &Operand, result: &mut ArenaMap) { - if let OperandKind::Copy(p) | OperandKind::Move(p) = arg.kind { + if let OperandKind::Copy(p) | OperandKind::Move(p) = &arg.kind { record_usage(p.local, result); } } @@ -674,7 +672,7 @@ fn mutability_of_locals<'db>( for arg in args.iter() { record_usage_for_operand(arg, &mut result); } - if destination.projection.lookup(&body.projection_store).is_empty() { + if destination.projection.is_empty() { if ever_init_map.get(destination.local).copied().unwrap_or_default() { push_mut_span(destination.local, terminator.span, &mut result); } else { 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 3372f6ec2ed9b..78b70edeeef78 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 @@ -56,7 +56,7 @@ use crate::{ use super::{ AggregateKind, BasicBlockId, BinOp, CastKind, LocalId, MirBody, MirLowerError, MirSpan, - Operand, OperandKind, Place, PlaceElem, ProjectionElem, ProjectionStore, Rvalue, StatementKind, + Operand, OperandKind, Place, PlaceElem, PlaceRef, ProjectionElem, Rvalue, StatementKind, TerminatorKind, UnOp, return_slot, }; @@ -567,26 +567,26 @@ impl std::fmt::Debug for MirEvalError { type Result<'db, T> = std::result::Result; #[derive(Debug, Default)] -struct DropFlags { - need_drop: FxHashSet, +struct DropFlags<'db> { + need_drop: FxHashSet>, } -impl DropFlags { - fn add_place(&mut self, p: Place, store: &ProjectionStore) { - if p.iterate_over_parents(store).any(|it| self.need_drop.contains(&it)) { +impl<'db> DropFlags<'db> { + fn add_place(&mut self, p: PlaceRef<'db>) { + if p.iterate_over_parents().any(|it| self.need_drop.contains(&it)) { return; } - self.need_drop.retain(|it| !p.is_parent(it, store)); + self.need_drop.retain(|it| !p.is_parent(*it)); self.need_drop.insert(p); } - fn remove_place(&mut self, p: &Place, store: &ProjectionStore) -> bool { + fn remove_place(&mut self, p: PlaceRef<'db>) -> bool { // FIXME: replace parents with parts - if let Some(parent) = p.iterate_over_parents(store).find(|it| self.need_drop.contains(it)) { + if let Some(parent) = p.iterate_over_parents().find(|it| self.need_drop.contains(it)) { self.need_drop.remove(&parent); return true; } - self.need_drop.remove(p) + self.need_drop.remove(&p) } fn clear(&mut self) { @@ -598,7 +598,7 @@ impl DropFlags { struct Locals<'a> { ptr: ArenaMap, body: &'a MirBody, - drop_flags: DropFlags, + drop_flags: DropFlags<'a>, } pub struct MirOutput { @@ -685,7 +685,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { db, random_state: oorandom::Rand64::new(0), param_env: trait_env.unwrap_or_else(|| ParamEnvAndCrate { - param_env: db.trait_environment(owner.expression_store_owner(db)), + param_env: db.trait_environment(owner.generic_def(db)), krate: crate_id, }), crate_id, @@ -757,9 +757,9 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { let mut addr = locals.ptr[p.local].addr; let mut ty: Ty<'db> = locals.body.locals[p.local].ty.as_ref(); let mut metadata: Option = None; // locals are always sized - for proj in p.projection.lookup(&locals.body.projection_store) { + for proj in p.projection.lookup() { let prev_ty = ty; - ty = self.projected_ty(ty, proj.clone()); + ty = self.projected_ty(ty, *proj); match proj { ProjectionElem::Deref => { metadata = if self.size_align_of(ty, locals)?.is_none() { @@ -844,7 +844,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { Variants::Multiple { variants, .. } => { &variants[match f.parent { hir_def::VariantId::EnumVariantId(it) => { - RustcEnumVariantIdx(it.lookup(self.db).index as usize) + RustcEnumVariantIdx(it.index(self.db)) } _ => { return Err(MirEvalError::InternalError( @@ -955,7 +955,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { let addr = self.place_addr(l, locals)?; let result = self.eval_rvalue(r, locals)?; self.copy_from_interval_or_owned(addr, result)?; - locals.drop_flags.add_place(*l, &locals.body.projection_store); + locals.drop_flags.add_place(l.as_ref()); } StatementKind::Deinit(_) => not_supported!("de-init statement"), StatementKind::StorageLive(_) @@ -1008,9 +1008,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { )?, it => not_supported!("unknown function type {it:?}"), }; - locals - .drop_flags - .add_place(*destination, &locals.body.projection_store); + locals.drop_flags.add_place(destination.as_ref()); if let Some(stack_frame) = stack_frame { self.code_stack.push(my_stack_frame); current_block_idx = stack_frame.locals.body.start_block; @@ -1091,7 +1089,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { ) -> Result<'db, ()> { let mut remain_args = body.param_locals.len(); for ((l, interval), value) in locals.ptr.iter().skip(1).zip(args) { - locals.drop_flags.add_place(l.into(), &locals.body.projection_store); + locals.drop_flags.add_place(l.into()); match value { IntervalOrOwned::Owned(value) => interval.write_from_bytes(self, &value)?, IntervalOrOwned::Borrowed(value) => interval.write_from_interval(self, value)?, @@ -1594,7 +1592,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { let max = 1i128 << (dest_bits - 1); (max - 1, -max) } else { - (1i128 << dest_bits, 0) + ((1i128 << dest_bits) - 1, 0) }; let value = (value as i128).min(max).max(min); let result = value.to_le_bytes(); @@ -1709,15 +1707,19 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { if let Some(it) = goal(kind) { return Ok(it); } - if let TyKind::Adt(adt_ef, subst) = kind - && let AdtId::StructId(struct_id) = adt_ef.def_id() - { - let field_types = self.db.field_types(struct_id.into()); - if let Some(ty) = - field_types.iter().last().map(|it| it.1.get().instantiate(self.interner(), subst)) - { - return self.coerce_unsized_look_through_fields(ty.skip_norm_wip(), goal); + match kind { + TyKind::Adt(adt_ef, subst) if let AdtId::StructId(struct_id) = adt_ef.def_id() => { + let field_types = self.db.field_types(struct_id.into()); + if let Some(ty) = field_types + .iter() + .last() + .map(|it| it.1.get().instantiate(self.interner(), subst)) + { + return self.coerce_unsized_look_through_fields(ty.skip_norm_wip(), goal); + } } + TyKind::Pat(ty, _) => return self.coerce_unsized_look_through_fields(ty, goal), + _ => (), } Err(MirEvalError::CoerceUnsizedError(ty.store())) } @@ -1837,8 +1839,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { _ => not_supported!("multi variant layout for non-enums"), }; let mut discriminant = self.const_eval_discriminant(enum_variant_id)?; - let lookup = enum_variant_id.lookup(self.db); - let rustc_enum_variant_idx = RustcEnumVariantIdx(lookup.index as usize); + let rustc_enum_variant_idx = RustcEnumVariantIdx(enum_variant_id.index(self.db)); let variant_layout = variants[rustc_enum_variant_idx].clone(); let have_tag = match tag_encoding { TagEncoding::Direct => true, @@ -1912,7 +1913,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { fn eval_operand(&mut self, it: &Operand, locals: &mut Locals<'a>) -> Result<'db, Interval> { Ok(match &it.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { - locals.drop_flags.remove_place(p, &locals.body.projection_store); + locals.drop_flags.remove_place(p.as_ref()); self.eval_place(p, locals)? } OperandKind::Static(st) => { @@ -3033,7 +3034,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { span: MirSpan, ) -> Result<'db, ()> { let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?; - if !locals.drop_flags.remove_place(place, &locals.body.projection_store) { + if !locals.drop_flags.remove_place(place.as_ref()) { return Ok(()); } let metadata = match metadata { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index 6bf966c3ef1d3..0e94a5b92dd1c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -1,4 +1,4 @@ -use hir_def::{GenericDefId, HasModule, signatures::FunctionSignature}; +use hir_def::{HasModule, signatures::FunctionSignature}; use hir_expand::EditionedFileId; use span::Edition; use syntax::{TextRange, TextSize}; @@ -41,7 +41,7 @@ fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String), func_id.into(), GenericArgs::empty(interner).store(), crate::ParamEnvAndCrate { - param_env: db.trait_environment(GenericDefId::from(func_id).into()), + param_env: db.trait_environment(func_id.into()), krate: func_id.krate(db), } .store(), 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 3852db909e392..4e52c1f7c305a 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 @@ -45,9 +45,9 @@ use crate::{ mir::{ AggregateKind, Arena, BasicBlock, BasicBlockId, BinOp, BorrowKind, CastKind, Either, Expr, FieldId, GenericArgs, Idx, InferenceResult, Local, LocalId, MemoryMap, MirBody, MirSpan, - Mutability, Operand, Place, PlaceElem, PointerCast, ProjectionElem, ProjectionStore, - RawIdx, Rvalue, Statement, StatementKind, SwitchTargets, Terminator, TerminatorKind, - TupleFieldId, Ty, UnOp, VariantId, return_slot, + Mutability, Operand, Place, PlaceElem, PointerCast, Projection, ProjectionElem, RawIdx, + Rvalue, Statement, StatementKind, SwitchTargets, Terminator, TerminatorKind, TupleFieldId, + Ty, UnOp, VariantId, return_slot, }, next_solver::{ Const, DbInterner, ParamConst, ParamEnv, Region, StoredGenericArgs, StoredTy, TyKind, @@ -57,7 +57,7 @@ use crate::{ }, }; -use super::OperandKind; +use super::{OperandKind, PlaceRef}; mod as_place; mod pattern_matching; @@ -276,10 +276,11 @@ impl MirLowerError { db: &dyn HirDatabase, p: &Path, display_target: DisplayTarget, + owner: ExpressionStoreOwnerId, store: &ExpressionStore, ) -> Self { Self::UnresolvedName( - hir_display_with_store(p, store).display(db, display_target).to_string(), + hir_display_with_store(p, owner, store).display(db, display_target).to_string(), ) } } @@ -302,7 +303,6 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let locals = Arena::new(); let binding_locals: ArenaMap = ArenaMap::new(); let mir = MirBody { - projection_store: ProjectionStore::default(), basic_blocks, locals, start_block, @@ -313,8 +313,8 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { closures: vec![], }; let store_owner = owner.expression_store_owner(db); - let resolver = store_owner.resolver(db); - let env = db.trait_environment(store_owner); + let resolver = owner.resolver(db); + let env = db.trait_environment(owner.generic_def(db)); let interner = DbInterner::new_with(db, resolver.krate()); // FIXME(next-solver): Is `non_body_analysis()` correct here? Don't we want to reveal opaque types defined by this body? let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); @@ -370,13 +370,16 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let Some((p, current)) = self.lower_expr_as_place(current, expr_id, true)? else { return Ok(None); }; - Ok(Some((Operand { kind: OperandKind::Copy(p), span: Some(expr_id.into()) }, current))) + Ok(Some(( + Operand { kind: OperandKind::Copy(p.store()), span: Some(expr_id.into()) }, + current, + ))) } fn lower_expr_to_place_with_adjust( &mut self, expr_id: ExprId, - place: Place, + place: PlaceRef<'db>, current: BasicBlockId, adjustments: &[Adjustment], ) -> Result<'db, Option> { @@ -395,7 +398,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.push_assignment( current, place, - Operand { kind: OperandKind::Copy(p), span: None }.into(), + Operand { kind: OperandKind::Copy(p.store()), span: None }.into(), expr_id.into(), ); Ok(Some(current)) @@ -421,7 +424,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { place, Rvalue::Cast( CastKind::PointerCoercion(*cast), - Operand { kind: OperandKind::Copy(p), span: None }, + Operand { kind: OperandKind::Copy(p.store()), span: None }, last.target.clone(), ), expr_id.into(), @@ -436,7 +439,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_expr_to_place_with_borrow_adjust( &mut self, expr_id: ExprId, - place: Place, + place: PlaceRef<'db>, current: BasicBlockId, rest: &[Adjustment], m: Mutability, @@ -447,14 +450,14 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { return Ok(None); }; let bk = BorrowKind::from_rustc_mutability(m); - self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into()); + self.push_assignment(current, place, Rvalue::Ref(bk, p.store()), expr_id.into()); Ok(Some(current)) } fn lower_expr_to_place( &mut self, expr_id: ExprId, - place: Place, + place: PlaceRef<'db>, prev_block: BasicBlockId, ) -> Result<'db, Option> { if let Some(adjustments) = self.infer.expr_adjustments.get(&expr_id) { @@ -466,7 +469,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_expr_to_place_without_adjust( &mut self, expr_id: ExprId, - place: Place, + place: PlaceRef<'db>, mut current: BasicBlockId, ) -> Result<'db, Option> { match &self.store[expr_id] { @@ -517,6 +520,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.db, p, DisplayTarget::from_crate(self.db, self.krate()), + self.owner.expression_store_owner(self.db), self.store, ) })?; @@ -533,7 +537,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.push_assignment( current, place, - Operand { kind: OperandKind::Copy(temp), span: None }.into(), + Operand { kind: OperandKind::Copy(temp.store()), span: None }.into(), expr_id.into(), ); Ok(Some(current)) @@ -667,7 +671,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.lower_block_to_place(statements, current, *tail, place, expr_id.into()) } } - Expr::Loop { body, label } => { + Expr::Loop { body, label, source: _ } => { self.lower_loop(current, place, *label, expr_id.into(), |this, begin| { let scope = this.push_drop_scope(); if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? { @@ -829,7 +833,9 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { .as_ref() .ok_or(MirLowerError::BreakWithoutLoop)?, }; - let Some(c) = self.lower_expr_to_place(expr, loop_data.place, current)? else { + let Some(c) = + self.lower_expr_to_place(expr, loop_data.place.as_ref(), current)? + else { return Ok(None); }; current = c; @@ -884,10 +890,12 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { }; let variant_id = self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| { - MirLowerError::UnresolvedName( - hir_display_with_store(path, self.store) - .display(self.db, self.display_target()) - .to_string(), + MirLowerError::unresolved_path( + self.db, + path, + self.display_target(), + self.owner.expression_store_owner(self.db), + self.store, ) })?; let subst = match self.expr_ty_without_adjust(expr_id).kind() { @@ -917,16 +925,18 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { .map(|(i, it)| match it { Some(it) => it, None => { - let p = sp.project( - ProjectionElem::Field(Either::Left(FieldId { + let p = sp.project(ProjectionElem::Field( + Either::Left(FieldId { parent: variant_id, local_id: LocalFieldId::from_raw(RawIdx::from( i as u32, )), - })), - &mut self.result.projection_store, - ); - Operand { kind: OperandKind::Copy(p), span: None } + }), + )); + Operand { + kind: OperandKind::Copy(p.store()), + span: None, + } } }) .collect(), @@ -944,13 +954,10 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { }; let local_id = variant_fields.field(name).ok_or(MirLowerError::UnresolvedField)?; - let place = place.project( - PlaceElem::Field(Either::Left(FieldId { - parent: union_id.into(), - local_id, - })), - &mut self.result.projection_store, - ); + let place = place.project(PlaceElem::Field(Either::Left(FieldId { + parent: union_id.into(), + local_id, + }))); self.lower_expr_to_place(*expr, place, current) } } @@ -997,7 +1004,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { return Ok(None); }; let bk = BorrowKind::from_hir_mutability(*mutability); - self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into()); + self.push_assignment(current, place, Rvalue::Ref(bk, p.store()), expr_id.into()); Ok(Some(current)) } Expr::Box { expr } => { @@ -1012,7 +1019,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { else { return Ok(None); }; - let p = place.project(ProjectionElem::Deref, &mut self.result.projection_store); + let p = place.project(ProjectionElem::Deref); self.push_assignment(current, p, operand.into(), expr_id.into()); Ok(Some(current)) } @@ -1027,7 +1034,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.push_assignment( current, place, - Operand { kind: OperandKind::Copy(p), span: None }.into(), + Operand { kind: OperandKind::Copy(p.store()), span: None }.into(), expr_id.into(), ); Ok(Some(current)) @@ -1120,7 +1127,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { }; let r_value = Rvalue::CheckedBinaryOp( op.into(), - Operand { kind: OperandKind::Copy(lhs_place), span: None }, + Operand { kind: OperandKind::Copy(lhs_place.store()), span: None }, rhs_op, ); self.push_assignment(current, lhs_place, r_value, expr_id.into()); @@ -1269,16 +1276,16 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { }; Ok(Place { local: this.binding_local(local)?, - projection: this - .result - .projection_store - .intern(convert_closure_capture_projections(self.db, place).collect()), + projection: Projection::new_from_iter(convert_closure_capture_projections( + self.db, place, + )) + .store(), }) }; for (place, _, sources) in &closure_data.fake_reads { let p = convert_place(self, place)?; - self.push_fake_read(current, p, span(sources)); + self.push_fake_read(current, p.as_ref(), span(sources)); } let captures = closure_data.min_captures.values().flatten(); @@ -1290,14 +1297,15 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let tmp_ty = capture.captured_ty(self.db); // FIXME: Handle more than one span. let capture_span = span(&capture.info.sources); - let tmp: Place = self.temp(tmp_ty, current, capture_span)?.into(); + let tmp = self.temp(tmp_ty, current, capture_span)?.into(); self.push_assignment( current, tmp, Rvalue::Ref(BorrowKind::from_hir(bk), p), capture_span, ); - operands.push(Operand { kind: OperandKind::Move(tmp), span: None }); + operands + .push(Operand { kind: OperandKind::Move(tmp.store()), span: None }); } UpvarCapture::ByValue => { operands.push(Operand { kind: OperandKind::Move(p), span: None }) @@ -1392,24 +1400,24 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { } } - fn push_field_projection(&mut self, place: &mut Place, expr_id: ExprId) -> Result<'db, ()> { + fn push_field_projection( + &mut self, + place: &mut PlaceRef<'db>, + expr_id: ExprId, + ) -> Result<'db, ()> { if let Expr::Field { expr, name } = &self.store[expr_id] { if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind() { let index = name.as_tuple_index().ok_or(MirLowerError::TypeError("named field on tuple"))? as u32; - *place = place.project( - ProjectionElem::Field(Either::Right(TupleFieldId { - tuple: TupleId(!0), // dummy as its unused - index, - })), - &mut self.result.projection_store, - ) + *place = place.project(ProjectionElem::Field(Either::Right(TupleFieldId { + tuple: TupleId(!0), // dummy as its unused + index, + }))) } else { let field = self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?; - *place = - place.project(ProjectionElem::Field(field), &mut self.result.projection_store); + *place = place.project(ProjectionElem::Field(field)); } } else { not_supported!("") @@ -1432,6 +1440,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.db, c, DisplayTarget::from_crate(db, owner.krate(db)), + self.owner.expression_store_owner(self.db), self.store, ) }; @@ -1523,7 +1532,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { &mut self, const_id: GeneralConstId, prev_block: BasicBlockId, - place: Place, + place: PlaceRef<'db>, subst: GenericArgs<'db>, span: MirSpan, ) -> Result<'db, ()> { @@ -1556,7 +1565,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn write_bytes_to_place( &mut self, prev_block: BasicBlockId, - place: Place, + place: PlaceRef<'db>, cv: Box<[u8]>, ty: Ty<'db>, span: MirSpan, @@ -1569,7 +1578,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { &mut self, variant_id: EnumVariantId, prev_block: BasicBlockId, - place: Place, + place: PlaceRef<'db>, ty: Ty<'db>, fields: Box<[Operand]>, span: MirSpan, @@ -1591,7 +1600,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { &mut self, func: Operand, args: impl Iterator, - place: Place, + place: PlaceRef<'db>, mut current: BasicBlockId, is_uninhabited: bool, span: MirSpan, @@ -1616,7 +1625,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { &mut self, func: Operand, args: Box<[Operand]>, - place: Place, + place: PlaceRef<'db>, current: BasicBlockId, is_uninhabited: bool, span: MirSpan, @@ -1627,7 +1636,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { TerminatorKind::Call { func, args, - destination: place, + destination: place.store(), target: b, cleanup: None, from_hir_call: true, @@ -1667,31 +1676,31 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.result.basic_blocks[block].statements.push(statement); } - fn push_fake_read(&mut self, block: BasicBlockId, p: Place, span: MirSpan) { - self.push_statement(block, StatementKind::FakeRead(p).with_span(span)); + fn push_fake_read(&mut self, block: BasicBlockId, p: PlaceRef<'db>, span: MirSpan) { + self.push_statement(block, StatementKind::FakeRead(p.store()).with_span(span)); } fn push_assignment( &mut self, block: BasicBlockId, - place: Place, + place: PlaceRef<'db>, rvalue: Rvalue, span: MirSpan, ) { - self.push_statement(block, StatementKind::Assign(place, rvalue).with_span(span)); + self.push_statement(block, StatementKind::Assign(place.store(), rvalue).with_span(span)); } - fn discr_temp_place(&mut self, current: BasicBlockId) -> Place { + fn discr_temp_place(&mut self, current: BasicBlockId) -> PlaceRef<'db> { match &self.discr_temp { - Some(it) => *it, + Some(it) => it.as_ref(), None => { // FIXME: rustc's ty is dependent on the adt type, maybe we need to do that as well let discr_ty = Ty::new_int(self.interner(), rustc_type_ir::IntTy::I128); - let tmp: Place = self + let tmp: PlaceRef<'_> = self .temp(discr_ty, current, MirSpan::Unknown) .expect("discr_ty is never unsized") .into(); - self.discr_temp = Some(tmp); + self.discr_temp = Some(tmp.store()); tmp } } @@ -1700,7 +1709,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_loop( &mut self, prev_block: BasicBlockId, - place: Place, + place: PlaceRef<'db>, label: Option, span: MirSpan, f: impl FnOnce(&mut MirLowerCtx<'_, 'db>, BasicBlockId) -> Result<'db, ()>, @@ -1709,7 +1718,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let prev = self.current_loop_blocks.replace(LoopBlocks { begin, end: None, - place, + place: place.store(), drop_scope_index: self.drop_scopes.len(), }); let prev_label = if let Some(label) = label { @@ -1811,7 +1820,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { statements: &[hir_def::hir::Statement], mut current: BasicBlockId, tail: Option, - place: Place, + place: PlaceRef<'db>, span: MirSpan, ) -> Result<'db, Option>> { let scope = self.push_drop_scope(); @@ -2063,7 +2072,11 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let prev = std::mem::replace(current, self.new_basic_block()); self.set_terminator( prev, - TerminatorKind::Drop { place: l.into(), target: *current, unwind: None }, + TerminatorKind::Drop { + place: PlaceRef::from(l).store(), + target: *current, + unwind: None, + }, span, ); } @@ -2197,15 +2210,18 @@ pub fn mir_body_for_closure_query<'db>( projections.push(ProjectionElem::ClosureField(capture_idx)); let capture_param_place = Place { local: closure_local, - projection: ctx.result.projection_store.intern(projections.into_boxed_slice()), - }; - let capture_local_place = Place { - local: capture_local, - projection: ctx.result.projection_store.intern(Box::new([])), + projection: Projection::new_from_slice(&projections).store(), }; + let capture_local_place = + Place { local: capture_local, projection: Projection::new_from_slice(&[]).store() }; let capture_local_rvalue = Rvalue::Use(Operand { kind: OperandKind::Move(capture_param_place), span: None }); - ctx.push_assignment(current, capture_local_place, capture_local_rvalue, MirSpan::Unknown); + ctx.push_assignment( + current, + capture_local_place.as_ref(), + capture_local_rvalue, + MirSpan::Unknown, + ); let local = capture.captured_local(); let local = ctx.binding_local(local)?; @@ -2225,8 +2241,8 @@ pub fn mir_body_for_closure_query<'db>( } let mut err = None; - ctx.result.walk_places(|mir_place, store| { - let mir_projections = mir_place.projection.lookup(store); + ctx.result.walk_places(|mir_place| { + let mir_projections = mir_place.projection.lookup(); if let Some(hir_places) = upvar_map.get(&mir_place.local) { let projections = hir_places.iter().find_map(|hir_place| { let iter = mir_projections @@ -2262,18 +2278,17 @@ pub fn mir_body_for_closure_query<'db>( match projections { Some((skip_projections_up_to, (hir_place, upvar_local))) => { mir_place.local = *upvar_local; - let mut result_projections = Vec::with_capacity( - usize::from(hir_place.is_by_ref()) - + (mir_projections.len() - skip_projections_up_to), - ); - if hir_place.is_by_ref() { - result_projections.push(ProjectionElem::Deref); - } - result_projections - .extend(mir_projections[skip_projections_up_to..].iter().cloned()); - mir_place.projection = store.intern(result_projections.into()); + let maybe_deref: &[PlaceElem] = + if hir_place.is_by_ref() { &[ProjectionElem::Deref] } else { &[] }; + mir_place.projection = Projection::new_from_iter( + maybe_deref + .iter() + .copied() + .chain(mir_projections[skip_projections_up_to..].iter().copied()), + ) + .store(); } - None => err = Some(*mir_place), + None => err = Some(mir_place.clone()), } } }); @@ -2309,10 +2324,7 @@ pub fn mir_body_query<'db>(db: &'db dyn HirDatabase, def: InferBodyId) -> Result .to_string(), InferBodyId::DefWithBodyId(DefWithBodyId::VariantId(it)) => { let loc = it.lookup(db); - loc.parent.enum_variants(db).variants[loc.index as usize] - .1 - .display(db, edition) - .to_string() + loc.name.display(db, edition).to_string() } InferBodyId::AnonConstId(_) => "{const}".to_owned(), }; 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 2ed7aedecffef..bb00a79480d84 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 @@ -14,15 +14,14 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, expr_id: ExprId, prev_block: BasicBlockId, - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let ty = self.expr_ty_without_adjust(expr_id); - let place = self.temp(ty, prev_block, expr_id.into())?; - let Some(current) = - self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? + let place = self.temp(ty, prev_block, expr_id.into())?.into(); + let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place, prev_block)? else { return Ok(None); }; - Ok(Some((place.into(), current))) + Ok(Some((place, current))) } fn lower_expr_to_some_place_with_adjust( @@ -30,18 +29,18 @@ impl<'db> MirLowerCtx<'_, 'db> { expr_id: ExprId, prev_block: BasicBlockId, adjustments: &[Adjustment], - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let ty = adjustments .last() .map(|it| it.target.as_ref()) .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)); - let place = self.temp(ty, prev_block, expr_id.into())?; + let place = self.temp(ty, prev_block, expr_id.into())?.into(); let Some(current) = - self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? + self.lower_expr_to_place_with_adjust(expr_id, place, prev_block, adjustments)? else { return Ok(None); }; - Ok(Some((place.into(), current))) + Ok(Some((place, current))) } pub(super) fn lower_expr_as_place_with_adjust( @@ -50,7 +49,7 @@ impl<'db> MirLowerCtx<'_, 'db> { expr_id: ExprId, upgrade_rvalue: bool, adjustments: &[Adjustment], - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let try_rvalue = |this: &mut MirLowerCtx<'_, 'db>| { if !upgrade_rvalue { return Err(MirLowerError::MutatingRvalue); @@ -69,7 +68,7 @@ impl<'db> MirLowerCtx<'_, 'db> { else { return Ok(None); }; - it.0 = it.0.project(ProjectionElem::Deref, &mut self.result.projection_store); + it.0 = it.0.project(ProjectionElem::Deref); Ok(Some(it)) } Adjust::Deref(Some(od)) => { @@ -108,7 +107,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current: BasicBlockId, expr_id: ExprId, upgrade_rvalue: bool, - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { match self.infer.expr_adjustments.get(&expr_id) { Some(a) => self.lower_expr_as_place_with_adjust(current, expr_id, upgrade_rvalue, a), None => self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue), @@ -120,7 +119,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current: BasicBlockId, expr_id: ExprId, upgrade_rvalue: bool, - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let try_rvalue = |this: &mut MirLowerCtx<'_, 'db>| { if !upgrade_rvalue { return Err(MirLowerError::MutatingRvalue); @@ -149,17 +148,14 @@ impl<'db> MirLowerCtx<'_, 'db> { ty, Mutability::Not, ); - let temp: Place = self.temp(ref_ty, current, expr_id.into())?.into(); + let temp = self.temp(ref_ty, current, expr_id.into())?.into(); self.push_assignment( current, temp, Operand { kind: OperandKind::Static(s), span: None }.into(), expr_id.into(), ); - Ok(Some(( - temp.project(ProjectionElem::Deref, &mut self.result.projection_store), - current, - ))) + Ok(Some((temp.project(ProjectionElem::Deref), current))) } _ => try_rvalue(self), } @@ -193,7 +189,7 @@ impl<'db> MirLowerCtx<'_, 'db> { let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else { return Ok(None); }; - r = r.project(ProjectionElem::Deref, &mut self.result.projection_store); + r = r.project(ProjectionElem::Deref); Ok(Some((r, current))) } Expr::UnaryOp { .. } => try_rvalue(self), @@ -256,8 +252,7 @@ impl<'db> MirLowerCtx<'_, 'db> { else { return Ok(None); }; - p_base = p_base - .project(ProjectionElem::Index(l_index), &mut self.result.projection_store); + p_base = p_base.project(ProjectionElem::Index(l_index)); Ok(Some((p_base, current))) } _ => try_rvalue(self), @@ -267,20 +262,20 @@ impl<'db> MirLowerCtx<'_, 'db> { fn lower_overloaded_index( &mut self, current: BasicBlockId, - place: Place, + place: PlaceRef<'db>, base_ty: Ty<'db>, result_ty: Ty<'db>, index_operand: Operand, span: MirSpan, index_fn: (FunctionId, GenericArgs<'db>), - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let mutability = match base_ty.as_reference() { Some((_, _, mutability)) => mutability, None => Mutability::Not, }; let result_ref = Ty::new_ref(self.interner(), Region::error(self.interner()), result_ty, mutability); - let mut result: Place = self.temp(result_ref, current, span)?.into(); + let mut result = self.temp(result_ref, current, span)?.into(); let index_fn_op = Operand::const_zst(Ty::new_fn_def( self.interner(), CallableDefId::FunctionId(index_fn.0).into(), @@ -288,7 +283,10 @@ impl<'db> MirLowerCtx<'_, 'db> { )); let Some(current) = self.lower_call( index_fn_op, - Box::new([Operand { kind: OperandKind::Copy(place), span: None }, index_operand]), + Box::new([ + Operand { kind: OperandKind::Copy(place.store()), span: None }, + index_operand, + ]), result, current, false, @@ -297,19 +295,19 @@ impl<'db> MirLowerCtx<'_, 'db> { else { return Ok(None); }; - result = result.project(ProjectionElem::Deref, &mut self.result.projection_store); + result = result.project(ProjectionElem::Deref); Ok(Some((result, current))) } fn lower_overloaded_deref( &mut self, current: BasicBlockId, - place: Place, + place: PlaceRef<'db>, source_ty: Ty<'db>, target_ty: Ty<'db>, span: MirSpan, mutability: bool, - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let lang_items = self.lang_items(); let (mutability, deref_fn, borrow_kind) = if !mutability { (Mutability::Not, lang_items.Deref_deref, BorrowKind::Shared) @@ -323,18 +321,18 @@ impl<'db> MirLowerCtx<'_, 'db> { let error_region = Region::error(self.interner()); let ty_ref = Ty::new_ref(self.interner(), error_region, source_ty, mutability); let target_ty_ref = Ty::new_ref(self.interner(), error_region, target_ty, mutability); - let ref_place: Place = self.temp(ty_ref, current, span)?.into(); - self.push_assignment(current, ref_place, Rvalue::Ref(borrow_kind, place), span); + let ref_place = self.temp(ty_ref, current, span)?.into(); + self.push_assignment(current, ref_place, Rvalue::Ref(borrow_kind, place.store()), span); let deref_fn = deref_fn.ok_or(MirLowerError::LangItemNotFound)?; let deref_fn_op = Operand::const_zst(Ty::new_fn_def( self.interner(), CallableDefId::FunctionId(deref_fn).into(), GenericArgs::new_from_slice(&[source_ty.into()]), )); - let mut result: Place = self.temp(target_ty_ref, current, span)?.into(); + let mut result = self.temp(target_ty_ref, current, span)?.into(); let Some(current) = self.lower_call( deref_fn_op, - Box::new([Operand { kind: OperandKind::Copy(ref_place), span: None }]), + Box::new([Operand { kind: OperandKind::Copy(ref_place.store()), span: None }]), result, current, false, @@ -343,7 +341,7 @@ impl<'db> MirLowerCtx<'_, 'db> { else { return Ok(None); }; - result = result.project(ProjectionElem::Deref, &mut self.result.projection_store); + result = result.project(ProjectionElem::Deref); Ok(Some((result, current))) } } 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 c924c5bdf0fdf..c306b6ca15f88 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 @@ -6,10 +6,10 @@ use rustc_type_ir::inherent::{IntoKind, Ty as _}; use crate::{ BindingMode, ByRef, mir::{ - LocalId, MutBorrowKind, Operand, OperandKind, + LocalId, MutBorrowKind, Operand, OperandKind, PlaceRef, Projection, lower::{ BasicBlockId, BinOp, BindingId, BorrowKind, Either, Expr, FieldId, Idx, MemoryMap, - MirLowerCtx, MirLowerError, MirSpan, Pat, PatId, Place, PlaceElem, ProjectionElem, + MirLowerCtx, MirLowerError, MirSpan, Pat, PatId, PlaceElem, ProjectionElem, RecordFieldPat, ResolveValueResult, Result, Rvalue, SwitchTargets, TerminatorKind, TupleFieldId, TupleId, Ty, TyKind, ValueNs, VariantId, }, @@ -65,7 +65,7 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, current: BasicBlockId, current_else: Option, - cond_place: Place, + cond_place: PlaceRef<'db>, pattern: PatId, ) -> Result<'db, (BasicBlockId, Option)> { let (current, current_else) = self.pattern_match_inner( @@ -88,7 +88,7 @@ impl<'db> MirLowerCtx<'_, 'db> { pub(super) fn pattern_match_assignment( &mut self, current: BasicBlockId, - value: Place, + value: PlaceRef<'db>, pattern: PatId, ) -> Result<'db, BasicBlockId> { let (current, _) = @@ -116,23 +116,23 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, mut current: BasicBlockId, mut current_else: Option, - mut cond_place: Place, + mut cond_place: PlaceRef<'db>, pattern: PatId, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option)> { let cnt = self.infer.pat_adjustments.get(&pattern).map(|x| x.len()).unwrap_or_default(); - cond_place.projection = self.result.projection_store.intern( + cond_place.projection = Projection::new_from_iter( cond_place .projection - .lookup(&self.result.projection_store) + .as_slice() .iter() .cloned() - .chain((0..cnt).map(|_| ProjectionElem::Deref)) - .collect::>() - .into(), + .chain((0..cnt).map(|_| ProjectionElem::Deref)), ); Ok(match &self.store[pattern] { - Pat::Missing | Pat::Rest => return Err(MirLowerError::IncompletePattern), + Pat::Missing | Pat::Rest | Pat::NotNull => { + return Err(MirLowerError::IncompletePattern); + } Pat::Wild => (current, current_else), Pat::Tuple { args, ellipsis } => { let subst = match self.infer.pat_ty(pattern).kind() { @@ -213,7 +213,7 @@ impl<'db> MirLowerCtx<'_, 'db> { self.lower_literal_or_const_to_operand(self.infer.pat_ty(pattern), l)?; let else_target = *current_else.get_or_insert_with(|| self.new_basic_block()); let next = self.new_basic_block(); - let discr: Place = + let discr = self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into(); self.push_assignment( current, @@ -221,11 +221,11 @@ impl<'db> MirLowerCtx<'_, 'db> { Rvalue::CheckedBinaryOp( binop, lv, - Operand { kind: OperandKind::Copy(cond_place), span: None }, + Operand { kind: OperandKind::Copy(cond_place.store()), span: None }, ), pattern.into(), ); - let discr = Operand { kind: OperandKind::Copy(discr), span: None }; + let discr = Operand { kind: OperandKind::Copy(discr.store()), span: None }; self.set_terminator( current, TerminatorKind::SwitchInt { @@ -261,13 +261,13 @@ impl<'db> MirLowerCtx<'_, 'db> { // emit runtime length check for slice if let TyKind::Slice(_) = pat_ty.kind() { let pattern_len = prefix.len() + suffix.len(); - let place_len: Place = self + let place_len = self .temp(Ty::new_usize(self.interner()), current, pattern.into())? .into(); self.push_assignment( current, place_len, - Rvalue::Len(cond_place), + Rvalue::Len(cond_place.store()), pattern.into(), ); let else_target = @@ -278,7 +278,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current, TerminatorKind::SwitchInt { discr: Operand { - kind: OperandKind::Copy(place_len), + kind: OperandKind::Copy(place_len.store()), span: None, }, targets: SwitchTargets::static_if( @@ -295,7 +295,7 @@ impl<'db> MirLowerCtx<'_, 'db> { MemoryMap::default(), Ty::new_usize(self.interner()), ); - let discr: Place = self + let discr = self .temp(Ty::new_bool(self.interner()), current, pattern.into())? .into(); self.push_assignment( @@ -304,11 +304,15 @@ impl<'db> MirLowerCtx<'_, 'db> { Rvalue::CheckedBinaryOp( BinOp::Le, c, - Operand { kind: OperandKind::Copy(place_len), span: None }, + Operand { + kind: OperandKind::Copy(place_len.store()), + span: None, + }, ), pattern.into(), ); - let discr = Operand { kind: OperandKind::Copy(discr), span: None }; + let discr = + Operand { kind: OperandKind::Copy(discr.store()), span: None }; self.set_terminator( current, TerminatorKind::SwitchInt { @@ -322,10 +326,10 @@ impl<'db> MirLowerCtx<'_, 'db> { } } for (i, &pat) in prefix.iter().enumerate() { - let next_place = cond_place.project( - ProjectionElem::ConstantIndex { offset: i as u64, from_end: false }, - &mut self.result.projection_store, - ); + let next_place = cond_place.project(ProjectionElem::ConstantIndex { + offset: i as u64, + from_end: false, + }); (current, current_else) = self.pattern_match_inner(current, current_else, next_place, pat, mode)?; } @@ -333,13 +337,10 @@ impl<'db> MirLowerCtx<'_, 'db> { && mode != MatchingMode::Check && let Pat::Bind { id, subpat: _ } = self.store[slice] { - let next_place = cond_place.project( - ProjectionElem::Subslice { - from: prefix.len() as u64, - to: suffix.len() as u64, - }, - &mut self.result.projection_store, - ); + let next_place = cond_place.project(ProjectionElem::Subslice { + from: prefix.len() as u64, + to: suffix.len() as u64, + }); let mode = self.infer.binding_modes[slice]; (current, current_else) = self.pattern_match_binding( id, @@ -351,10 +352,10 @@ impl<'db> MirLowerCtx<'_, 'db> { )?; } for (i, &pat) in suffix.iter().enumerate() { - let next_place = cond_place.project( - ProjectionElem::ConstantIndex { offset: i as u64, from_end: true }, - &mut self.result.projection_store, - ); + let next_place = cond_place.project(ProjectionElem::ConstantIndex { + offset: i as u64, + from_end: true, + }); (current, current_else) = self.pattern_match_inner(current, current_else, next_place, pat, mode)?; } @@ -376,6 +377,7 @@ impl<'db> MirLowerCtx<'_, 'db> { self.db, p, self.display_target(), + self.owner.expression_store_owner(self.db), self.store, ) }; @@ -417,19 +419,19 @@ impl<'db> MirLowerCtx<'_, 'db> { } not_supported!("path in pattern position that is not const or variant") }; - let tmp: Place = + let tmp = self.temp(self.infer.pat_ty(pattern), current, pattern.into())?.into(); let span = pattern.into(); self.lower_const(c.into(), current, tmp, subst, span)?; - let tmp2: Place = + let tmp2 = self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into(); self.push_assignment( current, tmp2, Rvalue::CheckedBinaryOp( BinOp::Eq, - Operand { kind: OperandKind::Copy(tmp), span: None }, - Operand { kind: OperandKind::Copy(cond_place), span: None }, + Operand { kind: OperandKind::Copy(tmp.store()), span: None }, + Operand { kind: OperandKind::Copy(cond_place.store()), span: None }, ), span, ); @@ -438,7 +440,7 @@ impl<'db> MirLowerCtx<'_, 'db> { self.set_terminator( current, TerminatorKind::SwitchInt { - discr: Operand { kind: OperandKind::Copy(tmp2), span: None }, + discr: Operand { kind: OperandKind::Copy(tmp2.store()), span: None }, targets: SwitchTargets::static_if(1, next, else_target), }, span, @@ -491,8 +493,7 @@ impl<'db> MirLowerCtx<'_, 'db> { )? } Pat::Ref { pat, mutability: _ } => { - let cond_place = - cond_place.project(ProjectionElem::Deref, &mut self.result.projection_store); + let cond_place = cond_place.project(ProjectionElem::Deref); self.pattern_match_inner(current, current_else, cond_place, *pat, mode)? } &Pat::Expr(expr) => { @@ -507,7 +508,7 @@ impl<'db> MirLowerCtx<'_, 'db> { self.push_assignment( current, lhs_place, - Operand { kind: OperandKind::Copy(cond_place), span: None }.into(), + Operand { kind: OperandKind::Copy(cond_place.store()), span: None }.into(), expr.into(), ); (current, current_else) @@ -522,7 +523,7 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, id: BindingId, mode: BindingMode, - cond_place: Place, + cond_place: PlaceRef<'db>, span: MirSpan, current: BasicBlockId, current_else: Option, @@ -538,7 +539,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current: BasicBlockId, target_place: LocalId, mode: BindingMode, - cond_place: Place, + cond_place: PlaceRef<'db>, span: MirSpan, ) { self.push_assignment( @@ -546,14 +547,15 @@ impl<'db> MirLowerCtx<'_, 'db> { target_place.into(), match mode { BindingMode(ByRef::No, _) => { - Operand { kind: OperandKind::Copy(cond_place), span: None }.into() + Operand { kind: OperandKind::Copy(cond_place.store()), span: None }.into() } BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Not), _) => { - Rvalue::Ref(BorrowKind::Shared, cond_place) - } - BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Mut), _) => { - Rvalue::Ref(BorrowKind::Mut { kind: MutBorrowKind::Default }, cond_place) + Rvalue::Ref(BorrowKind::Shared, cond_place.store()) } + BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Mut), _) => Rvalue::Ref( + BorrowKind::Mut { kind: MutBorrowKind::Default }, + cond_place.store(), + ), }, span, ); @@ -564,24 +566,23 @@ impl<'db> MirLowerCtx<'_, 'db> { current_else: Option, current: BasicBlockId, c: Operand, - cond_place: Place, + cond_place: PlaceRef<'db>, pattern: Idx, ) -> Result<'db, (BasicBlockId, Option)> { let then_target = self.new_basic_block(); let else_target = current_else.unwrap_or_else(|| self.new_basic_block()); - let discr: Place = - self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into(); + let discr = self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into(); self.push_assignment( current, discr, Rvalue::CheckedBinaryOp( BinOp::Eq, c, - Operand { kind: OperandKind::Copy(cond_place), span: None }, + Operand { kind: OperandKind::Copy(cond_place.store()), span: None }, ), pattern.into(), ); - let discr = Operand { kind: OperandKind::Copy(discr), span: None }; + let discr = Operand { kind: OperandKind::Copy(discr.store()), span: None }; self.set_terminator( current, TerminatorKind::SwitchInt { @@ -595,7 +596,7 @@ impl<'db> MirLowerCtx<'_, 'db> { fn pattern_matching_variant( &mut self, - cond_place: Place, + cond_place: PlaceRef<'db>, variant: VariantId, mut current: BasicBlockId, span: MirSpan, @@ -608,13 +609,18 @@ impl<'db> MirLowerCtx<'_, 'db> { if mode == MatchingMode::Check { let e = self.const_eval_discriminant(v)? as u128; let tmp = self.discr_temp_place(current); - self.push_assignment(current, tmp, Rvalue::Discriminant(cond_place), span); + self.push_assignment( + current, + tmp, + Rvalue::Discriminant(cond_place.store()), + span, + ); let next = self.new_basic_block(); let else_target = current_else.get_or_insert_with(|| self.new_basic_block()); self.set_terminator( current, TerminatorKind::SwitchInt { - discr: Operand { kind: OperandKind::Copy(tmp), span: None }, + discr: Operand { kind: OperandKind::Copy(tmp.store()), span: None }, targets: SwitchTargets::static_if(e, next, *else_target), }, span, @@ -653,7 +659,7 @@ impl<'db> MirLowerCtx<'_, 'db> { v: VariantId, current: BasicBlockId, current_else: Option, - cond_place: &Place, + cond_place: &PlaceRef<'db>, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option)> { Ok(match shape { @@ -697,11 +703,11 @@ impl<'db> MirLowerCtx<'_, 'db> { mut current: BasicBlockId, mut current_else: Option, args: impl Iterator, - cond_place: &Place, + cond_place: &PlaceRef<'db>, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option)> { for (proj, arg) in args { - let cond_place = cond_place.project(proj, &mut self.result.projection_store); + let cond_place = cond_place.project(proj); (current, current_else) = self.pattern_match_inner(current, current_else, cond_place, arg, mode)?; } @@ -715,7 +721,7 @@ impl<'db> MirLowerCtx<'_, 'db> { args: &[PatId], ellipsis: Option, fields: impl DoubleEndedIterator + Clone, - cond_place: &Place, + cond_place: &PlaceRef<'db>, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option)> { let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize)); 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 777cf170bc262..9054987066029 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 @@ -341,9 +341,7 @@ impl<'a, 'db> MirPrettyCtx<'a, 'db> { w!( this, " as {}).{}", - loc.parent.enum_variants(this.db).variants[loc.index as usize] - .1 - .display(this.db, this.display_target.edition), + loc.name.display(this.db, this.display_target.edition), name.display(this.db, this.display_target.edition) ); } @@ -375,7 +373,7 @@ impl<'a, 'db> MirPrettyCtx<'a, 'db> { } } } - f(self, p.local, p.projection.lookup(&self.body.projection_store)); + f(self, p.local, p.projection.lookup()); } fn operand(&mut self, r: &Operand) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs index 47b4b1dc4a3a3..f0d33ad2ddafc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs @@ -120,6 +120,7 @@ pub struct DefaultEmpty<'db> { pub clauses: Clauses<'db>, pub region_assumptions: RegionAssumptions<'db>, pub consts: Consts<'db>, + pub projection: crate::mir::Projection<'db>, } pub struct DefaultAny<'db> { @@ -237,6 +238,12 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> { let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; + let create_projection = |slice| { + let it = crate::mir::Projection::new_from_slice(slice); + // We need to increase the refcount (forever), so that the types won't be freed. + let it = ManuallyDrop::new(it.store()); + it.as_ref() + }; let str = create_ty(TyKind::Str); let statik = create_region(RegionKind::ReStatic); @@ -303,6 +310,7 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> { clauses: create_clauses(&[]), region_assumptions: create_region_assumptions(&[]), consts: create_consts(&[]), + projection: create_projection(&[]), }, one_invariant: create_variances_of(&[rustc_type_ir::Variance::Invariant]), one_covariant: create_variances_of(&[rustc_type_ir::Variance::Covariant]), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts/valtree.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts/valtree.rs index b856ee5a85a76..bef2386706814 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts/valtree.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts/valtree.rs @@ -8,6 +8,7 @@ use stdx::never; use crate::{ MemoryMap, ParamEnvAndCrate, consteval, + db::HirDatabase, mir::pad16, next_solver::{Const, Consts, TyKind, WorldExposer}, }; @@ -32,6 +33,34 @@ impl<'db> ValueConst<'db> { let value = ValTree::new(kind); ValueConst { ty, value } } + + /// Attempts to convert to a `ValTreeKind::Leaf` value. + pub fn try_to_leaf(self) -> Option { + match self.value.inner() { + ValTreeKind::Leaf(s) => Some(*s), + ValTreeKind::Branch(_) => None, + } + } + + /// Attempts to extract the raw bits from the constant. + /// + /// Fails if the value can't be represented as bits (e.g. because it is a reference + /// or an aggregate). + #[inline] + pub fn try_to_bits( + self, + db: &'db dyn HirDatabase, + param_env: ParamEnvAndCrate<'db>, + ) -> Option { + let (TyKind::Bool | TyKind::Char | TyKind::Uint(_) | TyKind::Int(_) | TyKind::Float(_)) = + self.ty.kind() + else { + return None; + }; + let scalar = self.try_to_leaf()?; + let size = db.layout_of_ty(self.ty.store(), param_env.store()).ok()?.size; + Some(scalar.to_bits(size)) + } } pub(super) fn allocation_to_const<'db>( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 839bdf17e7589..6b6dd549b34e0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -1,7 +1,6 @@ //! Infer context the next-trait-solver. use std::cell::{Cell, RefCell}; -use std::fmt; use std::ops::Range; use std::sync::Arc; @@ -308,32 +307,6 @@ pub enum BoundRegionConversionTime { AssocTypeProjection(SolverDefId), } -#[derive(Copy, Clone, Debug)] -pub struct FixupError { - unresolved: TyOrConstInferVar, -} - -impl fmt::Display for FixupError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use TyOrConstInferVar::*; - - match self.unresolved { - TyInt(_) => write!( - f, - "cannot determine the type of this integer; \ - add a suffix to specify the type explicitly" - ), - TyFloat(_) => write!( - f, - "cannot determine the type of this number; \ - add a suffix to specify the type explicitly" - ), - Ty(_) => write!(f, "unconstrained type"), - Const(_) => write!(f, "unconstrained const value"), - } - } -} - /// See the `region_obligations` field for more information. #[derive(Clone, Debug)] pub struct TypeOutlivesConstraint<'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs index 4584b35796456..362689ab5ce9e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs @@ -161,11 +161,11 @@ impl<'db> PredicateObligation<'db> { /// Flips the polarity of the inner predicate. /// /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. - pub fn flip_polarity(&self, _interner: DbInterner<'db>) -> Option> { + pub fn flip_polarity(&self, interner: DbInterner<'db>) -> Option> { Some(PredicateObligation { cause: self.cause, param_env: self.param_env, - predicate: self.predicate.flip_polarity()?, + predicate: self.predicate.flip_polarity(interner)?, recursion_depth: self.recursion_depth, }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index b3d31dd40bf9e..172b1a245f5e0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -330,6 +330,7 @@ unsafe impl Sync for DbInterner<'_> {} impl<'db> DbInterner<'db> { // FIXME(next-solver): remove this method + #[doc(hidden)] pub fn conjure() -> DbInterner<'db> { // Here we can not reinit the cache since we do that when we attach the db. crate::with_attached_db(|db| DbInterner { @@ -607,8 +608,8 @@ impl<'db> inherent::AdtDef> for AdtDef { hir_def::AdtId::EnumId(id) => id .enum_variants(db) .variants - .iter() - .flat_map(|&(variant_id, _, _)| field_tys(variant_id.into())) + .values() + .flat_map(|&(variant_id, _)| field_tys(variant_id.into())) .collect(), }; @@ -2581,6 +2582,7 @@ pub unsafe fn collect_ty_garbage() { gc.add_slice_storage::(); gc.add_slice_storage::(); gc.add_slice_storage::(); + gc.add_slice_storage::(); // SAFETY: // - By our precondition, there are no unrecorded types. @@ -2645,4 +2647,5 @@ impl_gc_visit_slice!( super::region::RegionAssumptionsStorage, super::ty::TysStorage, super::consts::ConstsStorage, + crate::mir::ProjectionStorage, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs index 30738dea5c958..cf492e65c3ff3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs @@ -229,7 +229,7 @@ impl<'db> Predicate<'db> { /// Flips the polarity of a Predicate. /// /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. - pub fn flip_polarity(self) -> Option> { + pub fn flip_polarity(self, interner: DbInterner<'db>) -> Option> { let kind = self .kind() .map_bound(|kind| match kind { @@ -245,7 +245,7 @@ impl<'db> Predicate<'db> { }) .transpose()?; - Some(Predicate::new(DbInterner::conjure(), kind)) + Some(Predicate::new(interner, kind)) } } @@ -355,13 +355,6 @@ impl<'db> rustc_type_ir::inherent::SliceLike for Clauses<'db> { } } -impl<'db> Default for Clauses<'db> { - #[inline] - fn default() -> Self { - Clauses::empty(DbInterner::conjure()) - } -} - impl<'db> rustc_type_ir::inherent::Clauses> for Clauses<'db> {} impl<'db> rustc_type_ir::TypeSuperFoldable> for Clauses<'db> { @@ -444,8 +437,8 @@ pub struct ParamEnv<'db> { } impl<'db> ParamEnv<'db> { - pub fn empty() -> Self { - ParamEnv { clauses: Clauses::empty(DbInterner::conjure()) } + pub fn empty(interner: DbInterner<'db>) -> Self { + ParamEnv { clauses: Clauses::empty(interner) } } pub fn clauses(self) -> Clauses<'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs b/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs index 4244b1bac443b..dfd1fd96c6564 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs @@ -1,8 +1,8 @@ //! Handling of opaque types, detection of defining scope and hidden type. use hir_def::{ - AssocItemId, AssocItemLoc, DefWithBodyId, ExpressionStoreOwnerId, FunctionId, GenericDefId, - HasModule, ItemContainerId, TypeAliasId, signatures::ImplSignature, + AssocItemId, AssocItemLoc, DefWithBodyId, FunctionId, HasModule, ItemContainerId, TypeAliasId, + signatures::ImplSignature, }; use hir_expand::name::Name; use la_arena::ArenaMap; @@ -129,10 +129,9 @@ pub(crate) fn tait_hidden_types( let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); let mut ocx = ObligationCtxt::new(&infcx); let cause = ObligationCause::dummy(); - let param_env = - db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from(type_alias))); + let param_env = db.trait_environment(type_alias.into()); - let defining_bodies = tait_defining_bodies(db, &loc); + let defining_bodies = tait_defining_bodies(db, loc); let mut result = ArenaMap::with_capacity(taits_count); for defining_body in defining_bodies { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs index 0b7dc4d30954f..0ea8003e5507d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs @@ -29,7 +29,7 @@ pub(crate) fn representability(db: &dyn HirDatabase, id: AdtId) -> Representabil AdtId::StructId(id) => variant_representability(db, id.into()), AdtId::UnionId(id) => variant_representability(db, id.into()), AdtId::EnumId(id) => { - for &(variant, ..) in &id.enum_variants(db).variants { + for &(variant, ..) in id.enum_variants(db).variants.values() { rtry!(variant_representability(db, variant.into())); } Representability::Representable @@ -105,7 +105,7 @@ fn params_in_repr(db: &dyn HirDatabase, def_id: AdtId) -> Box<[bool]> { AdtId::StructId(def_id) => handle_variant(def_id.into()), AdtId::UnionId(def_id) => handle_variant(def_id.into()), AdtId::EnumId(def_id) => { - for &(variant, ..) in &def_id.enum_variants(db).variants { + for &(variant, ..) in def_id.enum_variants(db).variants.values() { handle_variant(variant.into()); } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs index 467b598447d6e..2d206fe380023 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs @@ -1,9 +1,6 @@ //! Impl specialization related things -use hir_def::{ - ExpressionStoreOwnerId, GenericDefId, HasModule, ImplId, signatures::ImplSignature, - unstable_features::UnstableFeatures, -}; +use hir_def::{HasModule, ImplId, signatures::ImplSignature, unstable_features::UnstableFeatures}; use tracing::debug; use crate::{ @@ -48,9 +45,7 @@ fn specializes_query( specializing_impl_def_id: ImplId, parent_impl_def_id: ImplId, ) -> bool { - let trait_env = db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from( - specializing_impl_def_id, - ))); + let trait_env = db.trait_environment(specializing_impl_def_id.into()); let interner = DbInterner::new_with(db, specializing_impl_def_id.krate(db)); let specializing_impl_signature = ImplSignature::of(db, specializing_impl_def_id); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs index e19e26ebc4064..653fbe34f1581 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs @@ -132,6 +132,10 @@ impl SourceDatabase for TestDB { fn nonce_and_revision(&self) -> (Nonce, salsa::Revision) { (self.nonce, salsa::plumbing::ZalsaDatabase::zalsa(self).current_revision()) } + + fn line_column(&self, _file: FileId, _offset: syntax::TextSize) -> Result<(u32, u32), ()> { + Err(()) + } } #[salsa_macros::db] 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 d259ce7963f65..ce4eff701c90c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -460,7 +460,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { AdtId::EnumId(id) => variants.extend( id.enum_variants(&db) .variants - .iter() + .values() .map(|&(variant, ..)| (variant.into(), krate)), ), } @@ -600,7 +600,7 @@ pub(crate) fn visit_module( visit_body(db, body, cb); } ModuleDefId::AdtId(AdtId::EnumId(it)) => { - it.enum_variants(db).variants.iter().for_each(|&(it, _, _)| { + it.enum_variants(db).variants.values().for_each(|&(it, _)| { let body = Body::of(db, it.into()); cb(it.into()); visit_body(db, body, cb); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index 28a688d4a39ba..c0da6cfd307df 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -194,10 +194,8 @@ fn expr_macro_def_expanded_in_various_places() { !0..6 '1isize': isize !0..6 '1isize': isize !0..6 '1isize': isize - 39..442 '{ ...!(); }': {unknown} + 39..442 '{ ...!(); }': ! 73..94 'spam!(...am!())': {unknown} - 100..119 'for _ ...!() {}': fn into_iter(isize) -> ::IntoIter - 100..119 'for _ ...!() {}': ::IntoIter 100..119 'for _ ...!() {}': ! 100..119 'for _ ...!() {}': {unknown} 100..119 'for _ ...!() {}': &'? mut {unknown} @@ -208,6 +206,8 @@ fn expr_macro_def_expanded_in_various_places() { 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () 104..105 '_': {unknown} + 109..116 'spam!()': fn into_iter(isize) -> ::IntoIter + 109..116 'spam!()': ::IntoIter 117..119 '{}': () 124..134 '|| spam!()': impl Fn() -> isize 140..156 'while ...!() {}': ! @@ -288,10 +288,8 @@ fn expr_macro_rules_expanded_in_various_places() { !0..6 '1isize': isize !0..6 '1isize': isize !0..6 '1isize': isize - 53..456 '{ ...!(); }': {unknown} + 53..456 '{ ...!(); }': ! 87..108 'spam!(...am!())': {unknown} - 114..133 'for _ ...!() {}': fn into_iter(isize) -> ::IntoIter - 114..133 'for _ ...!() {}': ::IntoIter 114..133 'for _ ...!() {}': ! 114..133 'for _ ...!() {}': {unknown} 114..133 'for _ ...!() {}': &'? mut {unknown} @@ -302,6 +300,8 @@ fn expr_macro_rules_expanded_in_various_places() { 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () 118..119 '_': {unknown} + 123..130 'spam!()': fn into_iter(isize) -> ::IntoIter + 123..130 'spam!()': ::IntoIter 131..133 '{}': () 138..148 '|| spam!()': impl Fn() -> isize 154..170 'while ...!() {}': ! @@ -1493,7 +1493,7 @@ fn main() { !0..136 'builti...tack))': () !0..449 'builti...urn),)': ! 10..1236 '{ ... } }': () - 16..1234 'unsafe... }': () + 16..1234 'unsafe... }': ! 37..40 'foo': i32 43..44 '1': i32 58..63 'mut o': i32 diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index 4291c9ba18dc7..2084c01853a18 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1715,8 +1715,8 @@ fn f() { 95..103 'u32::foo': fn foo() -> u8 109..115 'S::foo': fn foo() -> u8 121..127 'T::foo': fn foo() -> u8 - 133..139 'U::foo': fn foo() -> u8 - 145..157 '<[u32]>::foo': fn foo<[u32]>() -> u8 + 133..139 'U::foo': {unknown} + 145..157 '<[u32]>::foo': {unknown} "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 91273cd177e8f..fd21286d50835 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -262,42 +262,42 @@ fn diverging_expression_1() { let x: u32 = { let y: u32 = { loop {}; }; }; } ", - expect![[r" - 11..39 '{ ...urn; }': () + expect![[r#" + 11..39 '{ ...urn; }': ! 21..22 'x': u32 30..36 'return': ! - 51..84 '{ ...; }; }': () + 51..84 '{ ...; }; }': ! 61..62 'x': u32 - 70..81 '{ return; }': u32 + 70..81 '{ return; }': ! 72..78 'return': ! - 96..125 '{ ... {}; }': () + 96..125 '{ ... {}; }': ! 106..107 'x': u32 115..122 'loop {}': ! 120..122 '{}': () - 137..170 '{ ...} }; }': () + 137..170 '{ ...} }; }': ! 147..148 'x': u32 156..167 '{ loop {} }': u32 158..165 'loop {}': ! 163..165 '{}': () - 182..246 '{ ...} }; }': () + 182..246 '{ ...} }; }': ! 192..193 'x': u32 201..243 '{ if t...}; } }': u32 203..241 'if tru... {}; }': u32 206..210 'true': bool - 211..223 '{ loop {}; }': u32 + 211..223 '{ loop {}; }': ! 213..220 'loop {}': ! 218..220 '{}': () - 229..241 '{ loop {}; }': u32 + 229..241 '{ loop {}; }': ! 231..238 'loop {}': ! 236..238 '{}': () - 258..310 '{ ...; }; }': () + 258..310 '{ ...; }; }': ! 268..269 'x': u32 - 277..307 '{ let ...; }; }': u32 + 277..307 '{ let ...; }; }': ! 283..284 'y': u32 - 292..304 '{ loop {}; }': u32 + 292..304 '{ loop {}; }': ! 294..301 'loop {}': ! 299..301 '{}': () - "]], + "#]], ); } @@ -312,7 +312,7 @@ fn diverging_expression_2() { } "#, expect![[r#" - 11..84 '{ ..." }; }': () + 11..84 '{ ..." }; }': ! 54..55 'x': u32 63..81 '{ loop...foo" }': u32 65..72 'loop {}': ! @@ -355,14 +355,12 @@ fn diverging_expression_3_break() { 54..55 'x': u32 63..82 '{ loop...k; } }': u32 65..80 'loop { break; }': u32 - 70..80 '{ break; }': () + 70..80 '{ break; }': ! 72..77 'break': ! 72..77: expected u32, got () 97..343 '{ ...; }; }': () 140..141 'x': u32 149..175 '{ for ...; }; }': u32 - 151..172 'for a ...eak; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 151..172 'for a ...eak; }': <{unknown} as IntoIterator>::IntoIter 151..172 'for a ...eak; }': ! 151..172 'for a ...eak; }': {unknown} 151..172 'for a ...eak; }': &'? mut {unknown} @@ -374,12 +372,12 @@ fn diverging_expression_3_break() { 151..172 'for a ...eak; }': () 155..156 'a': {unknown} 160..161 'b': {unknown} - 162..172 '{ break; }': () + 160..161 'b': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 160..161 'b': <{unknown} as IntoIterator>::IntoIter + 162..172 '{ break; }': ! 164..169 'break': ! 226..227 'x': u32 235..253 '{ for ... {}; }': u32 - 237..250 'for a in b {}': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 237..250 'for a in b {}': <{unknown} as IntoIterator>::IntoIter 237..250 'for a in b {}': ! 237..250 'for a in b {}': {unknown} 237..250 'for a in b {}': &'? mut {unknown} @@ -391,11 +389,11 @@ fn diverging_expression_3_break() { 237..250 'for a in b {}': () 241..242 'a': {unknown} 246..247 'b': {unknown} + 246..247 'b': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 246..247 'b': <{unknown} as IntoIterator>::IntoIter 248..250 '{}': () 304..305 'x': u32 313..340 '{ for ...; }; }': u32 - 315..337 'for a ...urn; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 315..337 'for a ...urn; }': <{unknown} as IntoIterator>::IntoIter 315..337 'for a ...urn; }': ! 315..337 'for a ...urn; }': {unknown} 315..337 'for a ...urn; }': &'? mut {unknown} @@ -407,7 +405,9 @@ fn diverging_expression_3_break() { 315..337 'for a ...urn; }': () 319..320 'a': {unknown} 324..325 'b': {unknown} - 326..337 '{ return; }': () + 324..325 'b': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 324..325 'b': <{unknown} as IntoIterator>::IntoIter + 326..337 '{ return; }': ! 328..334 'return': ! 149..175: expected u32, got () 235..253: expected u32, got () @@ -419,7 +419,7 @@ fn diverging_expression_3_break() { 409..430 'while ...eak; }': () 409..430 'while ...eak; }': () 415..419 'true': bool - 420..430 '{ break; }': () + 420..430 '{ break; }': ! 422..427 'break': ! 537..538 'x': u32 546..564 '{ whil... {}; }': u32 @@ -434,7 +434,7 @@ fn diverging_expression_3_break() { 626..648 'while ...urn; }': () 626..648 'while ...urn; }': () 632..636 'true': bool - 637..648 '{ return; }': () + 637..648 '{ return; }': ! 639..645 'return': ! 407..433: expected u32, got () 546..564: expected u32, got () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index e719f43e74b7b..a6e864916f40f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -47,8 +47,6 @@ fn infer_pattern() { 82..94 '(1, "hello")': (i32, &'? str) 83..84 '1': i32 86..93 '"hello"': &'static str - 101..150 'for (e... }': fn into_iter<[(i32, i32); 1]>([(i32, i32); 1]) -> <[(i32, i32); 1] as IntoIterator>::IntoIter - 101..150 'for (e... }': IntoIter<(i32, i32), 1> 101..150 'for (e... }': ! 101..150 'for (e... }': IntoIter<(i32, i32), 1> 101..150 'for (e... }': &'? mut IntoIter<(i32, i32), 1> @@ -62,6 +60,8 @@ fn infer_pattern() { 106..107 'e': i32 109..110 'f': i32 115..123 '[(0, 1)]': [(i32, i32); 1] + 115..123 '[(0, 1)]': fn into_iter<[(i32, i32); 1]>([(i32, i32); 1]) -> <[(i32, i32); 1] as IntoIterator>::IntoIter + 115..123 '[(0, 1)]': IntoIter<(i32, i32), 1> 116..122 '(0, 1)': (i32, i32) 117..118 '0': i32 120..121 '1': i32 @@ -606,7 +606,7 @@ fn enum_variant_through_self_in_pattern() { } "#, expect![[r#" - 75..217 '{ ... }': () + 75..217 '{ ... }': ! 85..210 'match ... }': () 92..99 'loop {}': ! 97..99 '{}': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 5a90e700acac9..22404087bffdc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -270,8 +270,6 @@ fn infer_std_crash_5() { "#, expect![[r#" 26..322 '{ ... } }': () - 32..320 'for co... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 32..320 'for co... }': <{unknown} as IntoIterator>::IntoIter 32..320 'for co... }': ! 32..320 'for co... }': {unknown} 32..320 'for co... }': &'? mut {unknown} @@ -281,27 +279,29 @@ fn infer_std_crash_5() { 32..320 'for co... }': () 32..320 'for co... }': () 32..320 'for co... }': () - 36..43 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 36..43 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 47..60 'doesnt_matter': {unknown} + 47..60 'doesnt_matter': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 47..60 'doesnt_matter': <{unknown} as IntoIterator>::IntoIter 61..320 '{ ... }': () - 75..79 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 82..166 'if doe... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 75..79 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 82..166 'if doe... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 85..98 'doesnt_matter': bool 99..128 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 113..118 'first': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 134..166 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 148..156 '&content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 149..156 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 181..188 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 191..313 'if ICE... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 134..166 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 148..156 '&content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 149..156 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 181..188 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 191..313 'if ICE... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 194..231 'ICE_RE..._VALUE': {unknown} 194..247 'ICE_RE...&name)': bool - 241..246 '&name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 242..246 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 248..276 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 262..266 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 282..313 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 296..303 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 241..246 '&name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 242..246 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 248..276 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 262..266 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 282..313 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 296..303 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} "#]], ); } @@ -416,7 +416,7 @@ fn issue_2669() { 120..215 '{ ... }': () 130..133 'end': fn end<{unknown}>() 130..135 'end()': () - 164..209 '{ ... }': () + 164..209 '{ ... }': ! 182..184 '_x': ! 191..198 'loop {}': ! 196..198 '{}': () @@ -631,7 +631,7 @@ fn issue_4053_diesel_where_clauses() { 65..69 'self': Self 267..271 'self': Self 466..470 'self': SelectStatement - 488..522 '{ ... }': {unknown} + 488..522 '{ ... }': () 498..502 'self': SelectStatement 498..508 'self.order': O 498..515 'self.o...into()': dyn QueryFragment + 'static @@ -1059,7 +1059,7 @@ fn cfg_tail() { 216..227 '{ "third" }': () 218..225 '"third"': &'static str 293..357 '{ ...] 15 }': () - 299..311 '{ "fourth" }': &'static str + 299..311 '{ "fourth" }': &'? str 301..309 '"fourth"': &'static str "#]], ) @@ -1261,8 +1261,6 @@ fn test() { "#, expect![[r#" 10..68 '{ ... } }': () - 16..66 'for _ ... }': fn into_iter<()>(()) -> <() as IntoIterator>::IntoIter - 16..66 'for _ ... }': <() as IntoIterator>::IntoIter 16..66 'for _ ... }': ! 16..66 'for _ ... }': {unknown} 16..66 'for _ ... }': &'? mut {unknown} @@ -1274,6 +1272,8 @@ fn test() { 16..66 'for _ ... }': () 20..21 '_': {unknown} 25..39 '{ let x = 0; }': () + 25..39 '{ let x = 0; }': fn into_iter<()>(()) -> <() as IntoIterator>::IntoIter + 25..39 '{ let x = 0; }': <() as IntoIterator>::IntoIter 31..32 'x': i32 35..36 '0': i32 40..66 '{ ... }': () @@ -2238,7 +2238,7 @@ type Bar = impl Foo; async fn f() -> Bar {} "#, expect![[r#" - 64..66 '{}': impl Foo + ?Sized + 64..66 '{}': () "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 33a12fcd1ee1e..c77b20f4b54c1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -357,7 +357,7 @@ where "#, expect![[r#" 182..183 't': T - 230..280 '{ ... {}; }': () + 230..280 '{ ... {}; }': ! 240..241 't': >::Output 270..277 'loop {}': ! 275..277 '{}': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index c0b8d93b47e32..b54ed080315e0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -135,7 +135,7 @@ fn test(a: u32, b: isize, c: !, d: &str) { 16..17 'b': isize 26..27 'c': ! 32..33 'd': &'? str - 41..120 '{ ...f32; }': () + 41..120 '{ ...f32; }': ! 47..48 'a': u32 54..55 'b': isize 61..62 'c': ! @@ -1018,14 +1018,14 @@ fn foo() { 28..32 'true': bool 33..50 '{ ... }': i32 43..44 '1': i32 - 56..79 '{ ... }': i32 + 56..79 '{ ... }': ! 66..72 'return': ! 89..92 '_x2': i32 95..148 'if tru... }': i32 98..102 'true': bool 103..120 '{ ... }': i32 113..114 '2': i32 - 126..148 '{ ... }': ! + 126..148 '{ ... }': i32 136..142 'return': ! 158..161 '_x3': i32 164..246 'match ... }': i32 @@ -1034,7 +1034,7 @@ fn foo() { 185..189 'true': bool 193..194 '3': i32 204..205 '_': bool - 209..240 '{ ... }': i32 + 209..240 '{ ... }': ! 223..229 'return': ! 256..259 '_x4': i32 262..319 'match ... }': i32 @@ -1939,7 +1939,7 @@ fn closure_return() { 16..58 '{ ...; }; }': u32 26..27 'x': impl Fn() -> usize 30..55 '|| -> ...n 1; }': impl Fn() -> usize - 42..55 '{ return 1; }': usize + 42..55 '{ return 1; }': ! 44..52 'return 1': ! 51..52 '1': usize "#]], @@ -1958,7 +1958,7 @@ fn closure_return_unit() { 16..47 '{ ...; }; }': u32 26..27 'x': impl Fn() 30..44 '|| { return; }': impl Fn() - 33..44 '{ return; }': () + 33..44 '{ return; }': ! 35..41 'return': ! "#]], ); @@ -2434,10 +2434,10 @@ fn infer_loop_break_with_val() { 59..168 '{ ... }; }': () 69..70 'x': Option 73..165 'loop {... }': Option - 78..165 '{ ... }': () + 78..165 '{ ... }': ! 88..132 'if fal... }': () 91..96 'false': bool - 97..132 '{ ... }': () + 97..132 '{ ... }': ! 111..121 'break None': ! 117..121 'None': Option 142..158 'break ...(true)': ! @@ -2470,7 +2470,7 @@ fn infer_loop_break_without_val() { 78..133 '{ ... }': () 88..127 'if fal... }': () 91..96 'false': bool - 97..127 '{ ... }': () + 97..127 '{ ... }': ! 111..116 'break': ! "#]], ); @@ -2500,24 +2500,24 @@ fn infer_labelled_break_with_val() { 19..21 '_x': impl Fn() -> bool 24..332 '|| 'ou... }': impl Fn() -> bool 27..332 ''outer... }': bool - 40..332 '{ ... }': () + 40..332 '{ ... }': ! 54..59 'inner': i8 62..300 ''inner... }': i8 - 75..300 '{ ... }': () + 75..300 '{ ... }': ! 93..94 'i': bool 97..113 'Defaul...efault': {unknown} 97..115 'Defaul...ault()': bool 129..269 'if (br... }': () 133..147 'break 'outer i': ! 146..147 'i': bool - 149..208 '{ ... }': () + 149..208 '{ ... }': ! 167..193 'loop {...5i8; }': ! - 172..193 '{ brea...5i8; }': () + 172..193 '{ brea...5i8; }': ! 174..190 'break ...er 5i8': ! 187..190 '5i8': i8 214..269 'if tru... }': () 217..221 'true': bool - 222..269 '{ ... }': () + 222..269 '{ ... }': ! 240..254 'break 'inner 6': ! 253..254 '6': i8 282..289 'break 7': ! @@ -2566,12 +2566,12 @@ fn foo() { 140..270 'if (br... }': () 144..158 'break 'outer i': ! 157..158 'i': bool - 160..209 '{ ... }': () + 160..209 '{ ... }': ! 178..194 'break ...er 5i8': ! 191..194 '5i8': i8 215..270 'if tru... }': () 218..222 'true': bool - 223..270 '{ ... }': () + 223..270 '{ ... }': ! 241..255 'break 'inner 6': ! 254..255 '6': i8 283..313 'break ... { 0 }': ! @@ -2666,7 +2666,7 @@ fn generic_default_in_struct_literal() { } "#, expect![[r#" - 99..319 '{ ...32); }': () + 99..319 '{ ...32); }': ! 109..110 'x': Thing 113..133 'Thing ...p {} }': Thing 124..131 'loop {}': ! @@ -3254,9 +3254,9 @@ fn main() { expect![[r#" 104..108 'self': &'? Box 188..192 'self': &'a Box> - 218..220 '{}': &'a T + 218..220 '{}': &'? T 242..246 'self': &'a Box> - 275..277 '{}': &'a Foo + 275..277 '{}': &'? Foo 297..301 'self': Box> 322..324 '{}': Foo 338..559 '{ ...r(); }': () @@ -4305,3 +4305,21 @@ enum Enum { "#]], ); } + +#[test] +fn labelled_block_break() { + check_types( + r#" +//- minicore: option +fn foo() { + 'a: { + if false { + break 'a Some(1); + } + None + // ^^^^ Option + }; +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index ea978cde58c19..85c93abcf9349 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -1490,7 +1490,7 @@ fn test(x: Box>, y: &dyn Trait) { expect![[r#" 29..33 'self': &'? Self 54..58 'self': &'? Self - 206..208 '{}': Box + 'static> + 206..208 '{}': Box + '?> 218..219 'x': Box + 'static> 242..243 'y': &'? (dyn Trait + 'static) 262..379 '{ ...2(); }': () @@ -1571,7 +1571,7 @@ fn test(x: Trait, y: &Trait) -> u64 { }"#, expect![[r#" 26..30 'self': &'? Self - 60..62 '{}': dyn Trait + 'static + 60..62 '{}': dyn Trait + '? 72..73 'x': dyn Trait + 'static 82..83 'y': &'? (dyn Trait + 'static) 100..175 '{ ...o(); }': u64 @@ -1712,7 +1712,7 @@ fn test>(x: T, y: impl Trait) { }"#, expect![[r#" 81..82 't': T - 109..111 '{}': ::Type + 109..111 '{}': () 143..144 't': T 154..156 '{}': U 186..187 't': T @@ -3027,13 +3027,13 @@ fn test() { 140..146 'IsCopy': IsCopy 140..153 'IsCopy.test()': bool 159..166 'NotCopy': NotCopy - 159..173 'NotCopy.test()': bool + 159..173 'NotCopy.test()': {unknown} 179..195 '(IsCop...sCopy)': (IsCopy, IsCopy) 179..202 '(IsCop...test()': bool 180..186 'IsCopy': IsCopy 188..194 'IsCopy': IsCopy 208..225 '(IsCop...tCopy)': (IsCopy, NotCopy) - 208..232 '(IsCop...test()': bool + 208..232 '(IsCop...test()': {unknown} 209..215 'IsCopy': IsCopy 217..224 'NotCopy': NotCopy "#]], @@ -3126,7 +3126,7 @@ fn test() { 79..194 '{ ...ized }': () 85..88 '1u8': u8 85..95 '1u8.test()': bool - 101..116 '(*"foo").test()': bool + 101..116 '(*"foo").test()': {unknown} 102..108 '*"foo"': str 103..108 '"foo"': &'static str 135..145 '(1u8, 1u8)': (u8, u8) @@ -3134,7 +3134,7 @@ fn test() { 136..139 '1u8': u8 141..144 '1u8': u8 158..171 '(1u8, *"foo")': (u8, str) - 158..178 '(1u8, ...test()': bool + 158..178 '(1u8, ...test()': {unknown} 159..162 '1u8': u8 164..170 '*"foo"': str 165..170 '"foo"': &'static str @@ -4069,7 +4069,7 @@ fn f() { 212..295 '{ ...ZED; }': () 218..239 'F::Exp..._SIZED': Yes 245..266 'F::Imp..._SIZED': Yes - 272..292 'F::Rel..._SIZED': Yes + 272..292 'F::Rel..._SIZED': {unknown} "#]], ); } @@ -5018,7 +5018,7 @@ where "#, expect![[r#" 84..86 'de': D - 135..138 '{ }': >::Error + 135..138 '{ }': () "#]], ); } @@ -5083,7 +5083,7 @@ fn main() { let _ = iter.into_iter(); }"#, expect![[r#" - 10..313 '{ ...r(); }': () + 10..313 '{ ...r(); }': ! 223..227 'iter': Box + 'static> 273..280 'loop {}': ! 278..280 '{}': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 9582f2ceba831..f6b5adfb6fffc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -121,7 +121,7 @@ pub fn implements_trait_unique<'db>( env: ParamEnvAndCrate<'db>, trait_: TraitId, ) -> bool { - implements_trait_unique_impl(db, env, trait_, &mut |infcx| { + implements_trait_unique_with_infcx(db, env, trait_, &mut |infcx| { infcx.fill_rest_fresh_args(Span::Dummy, trait_.into(), [ty.into()]) }) } @@ -133,10 +133,10 @@ pub fn implements_trait_unique_with_args<'db>( trait_: TraitId, args: GenericArgs<'db>, ) -> bool { - implements_trait_unique_impl(db, env, trait_, &mut |_| args) + implements_trait_unique_with_infcx(db, env, trait_, &mut |_| args) } -fn implements_trait_unique_impl<'db>( +pub fn implements_trait_unique_with_infcx<'db>( db: &'db dyn HirDatabase, env: ParamEnvAndCrate<'db>, trait_: TraitId, 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 7eee78b8c4419..49dacc16eb2c4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -25,8 +25,8 @@ use crate::{ db::HirDatabase, generics::{Generics, generics}, next_solver::{ - Const, ConstKind, DbInterner, ExistentialPredicate, GenericArgKind, GenericArgs, Region, - RegionKind, StoredVariancesOf, TermKind, Ty, TyKind, VariancesOf, + Const, ConstKind, DbInterner, ExistentialPredicate, GenericArgKind, GenericArgs, Pattern, + PatternKind, Region, RegionKind, StoredVariancesOf, TermKind, Ty, TyKind, VariancesOf, }, }; @@ -135,7 +135,7 @@ impl<'db> Context<'db> { AdtId::StructId(s) => add_constraints_from_variant(VariantId::StructId(s)), AdtId::UnionId(u) => add_constraints_from_variant(VariantId::UnionId(u)), AdtId::EnumId(e) => { - e.enum_variants(db).variants.iter().for_each(|&(variant, _, _)| { + e.enum_variants(db).variants.values().for_each(|&(variant, _)| { add_constraints_from_variant(VariantId::EnumVariantId(variant)) }); } @@ -249,17 +249,35 @@ impl<'db> Context<'db> { // we encounter this when walking the trait references for object // types, where we use Error as the Self type } + TyKind::Pat(typ, pat) => { + self.add_constraints_from_pat(pat); + self.add_constraints_from_ty(typ, variance); + } TyKind::Bound(..) => {} TyKind::CoroutineWitness(..) | TyKind::Placeholder(..) | TyKind::Infer(..) - | TyKind::UnsafeBinder(..) - | TyKind::Pat(..) => { + | TyKind::UnsafeBinder(..) => { never!("unexpected type encountered in variance inference: {:?}", ty) } } } + fn add_constraints_from_pat(&mut self, pat: Pattern<'db>) { + match pat.kind() { + PatternKind::Range { start, end } => { + self.add_constraints_from_const(start); + self.add_constraints_from_const(end); + } + PatternKind::NotNull => {} + PatternKind::Or(patterns) => { + for pat in patterns { + self.add_constraints_from_pat(pat) + } + } + } + } + fn add_constraints_from_invariant_args(&mut self, args: GenericArgs<'db>) { for k in args.iter() { match k.kind() { diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index f9cf05e73a103..9a61885ccb701 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -26,8 +26,8 @@ use stdx::never; use crate::{ Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, EnumVariant, - ExternCrateDecl, Field, Function, GenericParam, HasCrate, Impl, LangItem, LifetimeParam, Macro, - Module, ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, + ExternCrateDecl, Field, Function, GenericParam, Impl, LangItem, LifetimeParam, Macro, Module, + ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, }; #[derive(Debug, Clone, Copy)] @@ -487,27 +487,28 @@ fn resolve_impl_trait_item<'db>( ns: Option, ) -> Option { let krate = ty.krate(db); - let environment = crate::param_env_from_resolver(db, &resolver); + let param_env = ty.param_env(db); let traits_in_scope = resolver.traits_in_scope(db); // `ty.iterate_path_candidates()` require a scope, which is not available when resolving // attributes here. Use path resolution directly instead. // // FIXME: resolve type aliases (which are not yielded by iterate_path_candidates) - let interner = DbInterner::new_with(db, environment.krate); + let interner = DbInterner::new_with(db, param_env.krate); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); let features = resolver.top_level_def_map().features(); let ctx = MethodResolutionContext { infcx: &infcx, resolver: &resolver, - param_env: environment.param_env, + param_env: param_env.param_env, traits_in_scope: &traits_in_scope, - edition: krate.edition(db), + edition: krate.data(db).edition, features, call_span: hir_ty::Span::Dummy, receiver_span: hir_ty::Span::Dummy, }; - let resolution = ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), ty.ty); + let resolution = + ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), ty.ty.skip_binder()); let resolution = match resolution { Ok(resolution) => resolution.item, Err(MethodError::PrivateMatch(resolution)) => resolution.item, diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index a044f24587bae..f3188c9aada55 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -15,12 +15,12 @@ use hir_def::{ }; use hir_expand::{HirFileId, InFile, mod_path::ModPath, name::Name}; use hir_ty::{ - CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, ParamEnvAndCrate, + CastError, ExplicitDropMethodUseKind, InferenceDiagnostic, InferenceTyDiagnosticSource, PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind, db::HirDatabase, diagnostics::{BodyValidationDiagnostic, UnsafetyReason}, display::{DisplayTarget, HirDisplay}, - next_solver::DbInterner, + next_solver::{DbInterner, EarlyBinder}, solver_errors::SolverDiagnosticKind, }; use stdx::{impl_from, never}; @@ -31,7 +31,7 @@ use syntax::{ }; use triomphe::Arc; -use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type, Variant}; +use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type, TypeOwnerId, Variant}; pub use hir_def::VariantId; pub use hir_ty::{ @@ -100,11 +100,17 @@ macro_rules! diagnostics { } diagnostics![AnyDiagnostic<'db> -> + ArrayPatternWithoutFixedLength, AwaitOutsideOfAsync, BreakOutsideOfLoop, + CannotBeDereferenced<'db>, + CannotImplicitlyDerefTraitObject<'db>, + CannotIndexInto<'db>, CastToUnsized<'db>, ExpectedArrayOrSlicePat<'db>, ExpectedFunction<'db>, + ExplicitDropMethodUse, + FruInDestructuringAssignment, FunctionalRecordUpdateOnNonStruct, GenericDefaultRefersToSelf, InactiveCode, @@ -115,19 +121,23 @@ diagnostics![AnyDiagnostic<'db> -> InvalidCast<'db>, InvalidDeriveTarget, InvalidLhsOfAssignment, + InvalidRangePatType, MacroDefError, MacroError, MacroExpansionParseError, MalformedDerive, + MethodCallIllegalSizedBound, MismatchedArgCount, MismatchedTupleStructPatArgCount, MissingFields, MissingMatchArms, MissingUnsafe, MovedOutOfRef<'db>, + MutableRefBinding, NeedMut, NonExhaustiveLet, NonExhaustiveRecordExpr, + NonExhaustiveRecordPat, NoSuchField, MismatchedArrayPatLen, DuplicateField, @@ -297,18 +307,56 @@ pub struct MismatchedArrayPatLen { pub has_rest: bool, } +#[derive(Debug)] +pub struct ArrayPatternWithoutFixedLength { + pub pat: InFile, +} + #[derive(Debug)] pub struct ExpectedArrayOrSlicePat<'db> { pub pat: InFile, pub found: Type<'db>, } +#[derive(Debug)] +pub struct InvalidRangePatType { + pub pat: InFile, +} + #[derive(Debug)] pub struct ExpectedFunction<'db> { pub call: InFile, pub found: Type<'db>, } +#[derive(Debug)] +pub struct CannotBeDereferenced<'db> { + pub expr: InFile, + pub found: Type<'db>, +} + +#[derive(Debug)] +pub struct CannotImplicitlyDerefTraitObject<'db> { + pub pat: InFile, + pub found: Type<'db>, +} + +#[derive(Debug)] +pub struct CannotIndexInto<'db> { + pub expr: InFile, + pub found: Type<'db>, +} + +#[derive(Debug)] +pub struct ExplicitDropMethodUse { + pub expr_or_path: Either>, InFile>>, +} + +#[derive(Debug)] +pub struct FruInDestructuringAssignment { + pub node: InFile>, +} + #[derive(Debug)] pub struct FunctionalRecordUpdateOnNonStruct { pub base_expr: InFile, @@ -400,6 +448,12 @@ pub struct NonExhaustiveRecordExpr { pub expr: InFile, } +#[derive(Debug)] +pub struct NonExhaustiveRecordPat { + pub pat: InFile, + pub variant: Variant, +} + #[derive(Debug)] pub struct TypeMismatch<'db> { pub expr_or_pat: InFile, @@ -582,6 +636,11 @@ pub struct InvalidLhsOfAssignment { pub lhs: InFile>>, } +#[derive(Debug)] +pub struct MethodCallIllegalSizedBound { + pub call_expr: InFile, +} + #[derive(Debug)] pub struct PatternArgInExternFn { pub node: InFile>, @@ -594,6 +653,11 @@ pub struct UnimplementedTrait<'db> { pub root_trait_predicate: Option>, } +#[derive(Debug)] +pub struct MutableRefBinding { + pub pat: InFile, +} + impl<'db> AnyDiagnostic<'db> { pub(crate) fn body_validation_diagnostic( db: &'db dyn HirDatabase, @@ -733,7 +797,7 @@ impl<'db> AnyDiagnostic<'db> { d: &'db InferenceDiagnostic, source_map: &hir_def::expr_store::BodySourceMap, sig_map: &hir_def::expr_store::ExpressionStoreSourceMap, - env: ParamEnvAndCrate<'db>, + type_owner: TypeOwnerId, ) -> Option> { let expr_syntax = |expr| { source_map @@ -757,6 +821,7 @@ impl<'db> AnyDiagnostic<'db> { ExprOrPatId::ExprId(expr) => expr_syntax(expr), ExprOrPatId::PatId(pat) => pat_syntax(pat), }; + let new_ty = |ty| Type { owner: type_owner, ty: EarlyBinder::bind(ty) }; let span_syntax = |span| match span { hir_ty::Span::ExprId(idx) => expr_syntax(idx).map(|it| it.upcast()), hir_ty::Span::PatId(idx) => pat_syntax(idx).map(|it| it.upcast()), @@ -784,9 +849,21 @@ impl<'db> AnyDiagnostic<'db> { let pat = pat_syntax(pat)?.map(Into::into); MismatchedArrayPatLen { pat, expected, found, has_rest }.into() } + &InferenceDiagnostic::ArrayPatternWithoutFixedLength { pat } => { + let pat = pat_syntax(pat)?.map(Into::into); + ArrayPatternWithoutFixedLength { pat }.into() + } InferenceDiagnostic::ExpectedArrayOrSlicePat { pat, found } => { let pat = pat_syntax(*pat)?.map(Into::into); - ExpectedArrayOrSlicePat { pat, found: Type::new(db, def, found.as_ref()) }.into() + ExpectedArrayOrSlicePat { + pat, + found: Type { owner: type_owner, ty: EarlyBinder::bind(found.as_ref()) }, + } + .into() + } + &InferenceDiagnostic::InvalidRangePatType { pat } => { + let pat = pat_syntax(pat)?.map(Into::into); + InvalidRangePatType { pat }.into() } &InferenceDiagnostic::DuplicateField { field: expr, variant } => { let expr_or_pat = match expr { @@ -812,8 +889,7 @@ impl<'db> AnyDiagnostic<'db> { } InferenceDiagnostic::ExpectedFunction { call_expr, found } => { let call_expr = expr_syntax(*call_expr)?; - ExpectedFunction { call: call_expr, found: Type::new(db, def, found.as_ref()) } - .into() + ExpectedFunction { call: call_expr, found: new_ty(found.as_ref()) }.into() } InferenceDiagnostic::UnresolvedField { expr, @@ -825,7 +901,7 @@ impl<'db> AnyDiagnostic<'db> { UnresolvedField { expr, name: name.clone(), - receiver: Type::new(db, def, receiver.as_ref()), + receiver: new_ty(receiver.as_ref()), method_with_same_name_exists: *method_with_same_name_exists, } .into() @@ -841,10 +917,10 @@ impl<'db> AnyDiagnostic<'db> { UnresolvedMethodCall { expr, name: name.clone(), - receiver: Type::new(db, def, receiver.as_ref()), + receiver: new_ty(receiver.as_ref()), field_with_same_name: field_with_same_name .as_ref() - .map(|ty| Type::new(db, def, ty.as_ref())), + .map(|ty| new_ty(ty.as_ref())), assoc_func_with_same_name: assoc_func_with_same_name.map(Into::into), } .into() @@ -872,12 +948,16 @@ impl<'db> AnyDiagnostic<'db> { &InferenceDiagnostic::NonExhaustiveRecordExpr { expr } => { NonExhaustiveRecordExpr { expr: expr_syntax(expr)? }.into() } + &InferenceDiagnostic::NonExhaustiveRecordPat { pat, variant } => { + let pat = pat_syntax(pat)?.map(Into::into); + NonExhaustiveRecordPat { pat, variant: variant.into() }.into() + } &InferenceDiagnostic::FunctionalRecordUpdateOnNonStruct { base_expr } => { FunctionalRecordUpdateOnNonStruct { base_expr: expr_syntax(base_expr)? }.into() } InferenceDiagnostic::TypedHole { expr, expected } => { let expr = expr_syntax(*expr)?; - TypedHole { expr, expected: Type::new(db, def, expected.as_ref()) }.into() + TypedHole { expr, expected: new_ty(expected.as_ref()) }.into() } &InferenceDiagnostic::MismatchedTupleStructPatArgCount { pat, expected, found } => { let InFile { file_id, value } = pat_syntax(pat)?; @@ -888,14 +968,26 @@ impl<'db> AnyDiagnostic<'db> { } InferenceDiagnostic::CastToUnsized { expr, cast_ty } => { let expr = expr_syntax(*expr)?; - CastToUnsized { expr, cast_ty: Type::new(db, def, cast_ty.as_ref()) }.into() + CastToUnsized { expr, cast_ty: new_ty(cast_ty.as_ref()) }.into() } InferenceDiagnostic::InvalidCast { expr, error, expr_ty, cast_ty } => { let expr = expr_syntax(*expr)?; - let expr_ty = Type::new(db, def, expr_ty.as_ref()); - let cast_ty = Type::new(db, def, cast_ty.as_ref()); + let expr_ty = new_ty(expr_ty.as_ref()); + let cast_ty = new_ty(cast_ty.as_ref()); InvalidCast { expr, error: *error, expr_ty, cast_ty }.into() } + InferenceDiagnostic::CannotBeDereferenced { expr, found } => { + let expr = expr_syntax(*expr)?; + CannotBeDereferenced { expr, found: new_ty(found.as_ref()) }.into() + } + InferenceDiagnostic::CannotImplicitlyDerefTraitObject { pat, found } => { + let pat = pat_syntax(*pat)?.map(Into::into); + CannotImplicitlyDerefTraitObject { pat, found: new_ty(found.as_ref()) }.into() + } + InferenceDiagnostic::CannotIndexInto { expr, found } => { + let expr = expr_syntax(*expr)?; + CannotIndexInto { expr, found: new_ty(found.as_ref()) }.into() + } InferenceDiagnostic::TyDiagnostic { source, diag } => { let source_map = match source { InferenceTyDiagnosticSource::Body => source_map, @@ -963,13 +1055,13 @@ impl<'db> AnyDiagnostic<'db> { let lhs = expr_syntax(lhs)?; InvalidLhsOfAssignment { lhs }.into() } + &InferenceDiagnostic::MethodCallIllegalSizedBound { call_expr } => { + MethodCallIllegalSizedBound { call_expr: expr_syntax(call_expr)? }.into() + } &InferenceDiagnostic::TypeMustBeKnown { at_point, ref top_term } => { let at_point = span_syntax(at_point)?; let top_term = top_term.as_ref().map(|top_term| match top_term.as_ref().kind() { - rustc_type_ir::GenericArgKind::Type(ty) => Either::Left(Type { - ty, - env: crate::body_param_env_from_has_crate(db, def), - }), + rustc_type_ir::GenericArgKind::Type(ty) => Either::Left(new_ty(ty)), // FIXME: Printing the const to string is definitely not the correct thing to do here. rustc_type_ir::GenericArgKind::Const(konst) => Either::Right( konst.display(db, DisplayTarget::from_crate(db, def.krate(db))).to_string(), @@ -988,14 +1080,37 @@ impl<'db> AnyDiagnostic<'db> { let expr_or_pat = expr_or_pat_syntax(*node)?; TypeMismatch { expr_or_pat, - expected: Type { env, ty: expected.as_ref() }, - actual: Type { env, ty: found.as_ref() }, + expected: Type { owner: type_owner, ty: EarlyBinder::bind(expected.as_ref()) }, + actual: Type { owner: type_owner, ty: EarlyBinder::bind(found.as_ref()) }, } .into() } InferenceDiagnostic::SolverDiagnostic(d) => { let span = span_syntax(d.span)?; - Self::solver_diagnostic(db, &d.kind, span, env)? + Self::solver_diagnostic(db, &d.kind, span, type_owner)? + } + InferenceDiagnostic::ExplicitDropMethodUse { kind } => { + let expr_or_path = match kind { + ExplicitDropMethodUseKind::MethodCall(expr) => { + let expr = expr_syntax(*expr)?; + let expr = expr.with_value(expr.value.cast::()?); + Either::Left(expr) + } + ExplicitDropMethodUseKind::Path(path_expr_id) => { + let syntax = expr_or_pat_syntax(*path_expr_id)?; + let file_id = syntax.file_id; + let syntax = + syntax.with_value(syntax.value.cast::()?).to_node(db); + let path = syntax.path()?; + let path = InFile::new(file_id, AstPtr::new(&path)); + Either::Right(path) + } + }; + ExplicitDropMethodUse { expr_or_path }.into() + } + InferenceDiagnostic::MutableRefBinding { pat } => { + let pat = pat_syntax(*pat)?.map(Into::into); + MutableRefBinding { pat }.into() } }) } @@ -1004,16 +1119,21 @@ impl<'db> AnyDiagnostic<'db> { db: &'db dyn HirDatabase, d: &'db SolverDiagnosticKind, span: SpanSyntax, - env: ParamEnvAndCrate<'db>, + type_owner: TypeOwnerId, ) -> Option> { let interner = DbInterner::new_no_crate(db); Some(match d { SolverDiagnosticKind::TraitUnimplemented { trait_predicate, root_trait_predicate } => { - let trait_predicate = - crate::TraitPredicate { inner: trait_predicate.get(interner), env }; + let trait_predicate = crate::TraitPredicate { + inner: trait_predicate.get(interner), + owner: type_owner, + }; let root_trait_predicate = root_trait_predicate.as_ref().map(|root_trait_predicate| { - crate::TraitPredicate { inner: root_trait_predicate.get(interner), env } + crate::TraitPredicate { + inner: root_trait_predicate.get(interner), + owner: type_owner, + } }); UnimplementedTrait { span, trait_predicate, root_trait_predicate }.into() } diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index a71851ea8ceff..ed18482bf3807 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -2,7 +2,8 @@ use either::Either; use hir_def::{ - AdtId, BuiltinDeriveImplId, FunctionId, GenericDefId, ImplId, ItemContainerId, + AdtId, BuiltinDeriveImplId, DefWithBodyId, ExpressionStoreOwnerId, FunctionId, GenericDefId, + ImplId, ItemContainerId, builtin_derive::BuiltinDeriveImplMethod, expr_store::{Body, ExpressionStore}, hir::generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate}, @@ -32,7 +33,7 @@ use crate::{ Adt, AnyFunctionId, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum, EnumVariant, ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam, Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitPredicate, - TraitRef, TupleField, Type, TypeAlias, TypeNs, TypeOrConstParam, TypeParam, Union, + TraitRef, TupleField, Type, TypeAlias, TypeOrConstParam, TypeParam, Union, }; fn write_builtin_derive_impl_method<'db>( @@ -102,8 +103,11 @@ impl<'db> HirDisplay<'db> for Function { if f.show_container_bounds() && !params.is_empty() { write_trait_header(trait_.into(), f)?; f.write_char('\n')?; - has_disaplayable_predicates(f.db, params, params_store) - .then_some((params, params_store)) + has_disaplayable_predicates(f.db, params, params_store).then_some(( + params, + trait_.into(), + params_store, + )) } else { None } @@ -113,8 +117,11 @@ impl<'db> HirDisplay<'db> for Function { if f.show_container_bounds() && !params.is_empty() { write_impl_header(impl_, f)?; f.write_char('\n')?; - has_disaplayable_predicates(f.db, params, params_store) - .then_some((params, params_store)) + has_disaplayable_predicates(f.db, params, params_store).then_some(( + params, + impl_.into(), + params_store, + )) } else { None } @@ -125,7 +132,7 @@ impl<'db> HirDisplay<'db> for Function { // Write signature of the function let has_written_where = write_function(f, id)?; - if let Some((container_params, container_params_store)) = container_params { + if let Some((container_params, owner, container_params_store)) = container_params { if !has_written_where { f.write_str("\nwhere")?; } @@ -135,7 +142,12 @@ impl<'db> HirDisplay<'db> for Function { _ => unreachable!(), }; write!(f, "\n // Bounds from {container_name}:",)?; - write_where_predicates(container_params, container_params_store, f)?; + write_where_predicates( + container_params, + ExpressionStoreOwnerId::Signature(owner), + container_params_store, + f, + )?; } Ok(()) } @@ -197,6 +209,7 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re let comma = if too_long_param { ",\n " } else { ", " }; // FIXME: Use resolved `param.ty` once we no longer discard lifetimes let body = Body::of(db, func_id.into()); + let owner = DefWithBodyId::FunctionId(func_id).into(); for (type_ref, param) in data.params.iter().zip(func.assoc_fn_params(db)).skip(skip_self) { if !first { f.write_str(comma)?; @@ -205,11 +218,11 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re } let pat_id = body.params[param.idx - body.self_param().is_some() as usize]; - let pat_str = body.pretty_print_pat(db, func_id.into(), pat_id, true, f.edition()); + let pat_str = body.pretty_print_pat(db, owner, pat_id, true, f.edition()); f.write_str(&pat_str)?; f.write_str(": ")?; - type_ref.hir_fmt(f, &data.store)?; + type_ref.hir_fmt(f, owner, &data.store)?; } if data.is_varargs() { @@ -258,7 +271,7 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re TypeRef::Tuple(tup) if tup.is_empty() => {} _ => { f.write_str(" -> ")?; - ret_type.hir_fmt(f, &data.store)?; + ret_type.hir_fmt(f, owner, &data.store)?; } } } @@ -278,7 +291,8 @@ fn write_impl_header<'db>(impl_: ImplId, f: &mut HirFormatter<'_, 'db>) -> Resul let impl_data = ImplSignature::of(db, impl_); if let Some(target_trait) = &impl_data.target_trait { f.write_char(' ')?; - hir_display_with_store(&impl_data.store[target_trait.path], &impl_data.store).hir_fmt(f)?; + hir_display_with_store(&impl_data.store[target_trait.path], impl_.into(), &impl_data.store) + .hir_fmt(f)?; f.write_str(" for")?; } @@ -306,13 +320,14 @@ impl<'db> HirDisplay<'db> for SelfParam { }; let data = FunctionSignature::of(f.db, func); let param = *data.params.first().unwrap(); + let owner = ExpressionStoreOwnerId::Body(func.into()); match &data.store[param] { TypeRef::Path(p) if p.is_self_type() => f.write_str("self"), TypeRef::Reference(ref_) if matches!(&data.store[ref_.ty], TypeRef::Path(p) if p.is_self_type()) => { f.write_char('&')?; if let Some(lifetime) = &ref_.lifetime { - lifetime.hir_fmt(f, &data.store)?; + lifetime.hir_fmt(f, owner, &data.store)?; f.write_char(' ')?; } if let hir_def::type_ref::Mutability::Mut = ref_.mutability { @@ -322,7 +337,7 @@ impl<'db> HirDisplay<'db> for SelfParam { } _ => { f.write_str("self: ")?; - param.hir_fmt(f, &data.store) + param.hir_fmt(f, owner, &data.store) } } } @@ -517,7 +532,11 @@ impl<'db> HirDisplay<'db> for EnumVariant { f.write_str(", ")?; } // Enum variant fields must be pub. - field.type_ref.hir_fmt(f, &data.store)?; + field.type_ref.hir_fmt( + f, + ExpressionStoreOwnerId::VariantFields(self.id.into()), + &data.store, + )?; } f.write_char(')')?; } @@ -533,13 +552,7 @@ impl<'db> HirDisplay<'db> for EnumVariant { impl<'db> HirDisplay<'db> for Type<'db> { fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { - self.ty.hir_fmt(f) - } -} - -impl<'db> HirDisplay<'db> for TypeNs<'db> { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { - self.ty.hir_fmt(f) + self.ty.skip_binder().hir_fmt(f) } } @@ -579,7 +592,7 @@ impl<'db> HirDisplay<'db> for TypeParam { let params = GenericParams::of(f.db, self.id.parent()); let param_data = ¶ms[self.id.local_id()]; let krate = self.id.parent().krate(f.db).id; - let ty = self.ty(f.db).ty; + let ty = self.ty(f.db).ty.skip_binder(); let predicates = GenericPredicates::query_all(f.db, self.id.parent()); let predicates = predicates .iter_identity() @@ -666,6 +679,7 @@ fn write_generic_params_or_args<'db>( include_defaults: bool, ) -> Result { let (params, store) = GenericParams::with_store(f.db, def); + let owner = def.into(); if params.iter_lt().next().is_none() && params.iter_type_or_consts().all(|it| it.1.const_param().is_none()) && params @@ -701,17 +715,17 @@ fn write_generic_params_or_args<'db>( write!(f, "{}", name.display(f.db, f.edition()))?; if include_defaults && let Some(default) = &ty.default { f.write_str(" = ")?; - default.hir_fmt(f, store)?; + default.hir_fmt(f, owner, store)?; } } TypeOrConstParamData::ConstParamData(c) => { delim(f)?; write!(f, "const {}: ", name.display(f.db, f.edition()))?; - c.ty.hir_fmt(f, store)?; + c.ty.hir_fmt(f, owner, store)?; if include_defaults && let Some(default) = &c.default { f.write_str(" = ")?; - default.hir_fmt(f, store)?; + default.hir_fmt(f, owner, store)?; } } } @@ -729,7 +743,7 @@ fn write_where_clause<'db>(def: GenericDefId, f: &mut HirFormatter<'_, 'db>) -> } f.write_str("\nwhere")?; - write_where_predicates(params, store, f)?; + write_where_predicates(params, def.into(), store, f)?; Ok(true) } @@ -752,6 +766,7 @@ fn has_disaplayable_predicates( fn write_where_predicates<'db>( params: &GenericParams, + owner: ExpressionStoreOwnerId, store: &ExpressionStore, f: &mut HirFormatter<'_, 'db>, ) -> Result { @@ -783,29 +798,31 @@ fn write_where_predicates<'db>( f.write_str("\n ")?; match pred { TypeBound { target, bound } => { - target.hir_fmt(f, store)?; + target.hir_fmt(f, owner, store)?; f.write_str(": ")?; - bound.hir_fmt(f, store)?; + bound.hir_fmt(f, owner, store)?; } Lifetime { target, bound } => { - target.hir_fmt(f, store)?; + target.hir_fmt(f, owner, store)?; write!(f, ": ")?; - bound.hir_fmt(f, store)?; + bound.hir_fmt(f, owner, store)?; } ForLifetime { lifetimes, target, bound } => { let lifetimes = lifetimes.iter().map(|it| it.display(f.db, f.edition())).join(", "); write!(f, "for<{lifetimes}> ")?; - target.hir_fmt(f, store)?; + target.hir_fmt(f, owner, store)?; f.write_str(": ")?; - bound.hir_fmt(f, store)?; + bound.hir_fmt(f, owner, store)?; } } while let Some(nxt) = iter.next_if(|nxt| check_same_target(pred, nxt)) { f.write_str(" + ")?; match nxt { - TypeBound { bound, .. } | ForLifetime { bound, .. } => bound.hir_fmt(f, store)?, - Lifetime { bound, .. } => bound.hir_fmt(f, store)?, + TypeBound { bound, .. } | ForLifetime { bound, .. } => { + bound.hir_fmt(f, owner, store)? + } + Lifetime { bound, .. } => bound.hir_fmt(f, owner, store)?, } } f.write_str(",")?; @@ -830,7 +847,7 @@ impl<'db> HirDisplay<'db> for Const { Some(name) => write!(f, "{}: ", name.display(f.db, f.edition()))?, None => f.write_str("_: ")?, } - data.type_ref.hir_fmt(f, &data.store)?; + data.type_ref.hir_fmt(f, ExpressionStoreOwnerId::Signature(self.id.into()), &data.store)?; Ok(()) } } @@ -844,7 +861,7 @@ impl<'db> HirDisplay<'db> for Static { f.write_str("mut ")?; } write!(f, "{}: ", data.name.display(f.db, f.edition()))?; - data.type_ref.hir_fmt(f, &data.store)?; + data.type_ref.hir_fmt(f, ExpressionStoreOwnerId::Signature(self.id.into()), &data.store)?; Ok(()) } } @@ -925,13 +942,19 @@ impl<'db> HirDisplay<'db> for TypeAlias { if !data.bounds.is_empty() { f.write_str(": ")?; f.write_joined( - data.bounds.iter().map(|bound| hir_display_with_store(bound, &data.store)), + data.bounds.iter().map(|bound| { + hir_display_with_store( + bound, + ExpressionStoreOwnerId::Signature(self.id.into()), + &data.store, + ) + }), " + ", )?; } if let Some(ty) = data.ty { f.write_str(" = ")?; - ty.hir_fmt(f, &data.store)?; + ty.hir_fmt(f, ExpressionStoreOwnerId::Signature(self.id.into()), &data.store)?; } write_where_clause(def_id, f)?; Ok(()) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 63b834a8d1d59..d187763151a22 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -39,7 +39,8 @@ mod display; pub use hir_def::ModuleId; use std::{ - fmt, + borrow::Borrow, + fmt, iter, mem::discriminant, ops::{ControlFlow, Not}, }; @@ -93,19 +94,17 @@ use hir_ty::{ method_resolution::{self, InherentImpls, MethodResolutionContext}, mir::interpret_mir, next_solver::{ - AliasTy, AnyImplId, ClauseKind, ConstKind, DbInterner, EarlyBinder, EarlyParamRegion, - ErrorGuaranteed, GenericArg, GenericArgs, ParamConst, ParamEnv, PolyFnSig, Region, - RegionKind, SolverDefId, Ty, TyKind, TypingMode, + AliasTy, AnyImplId, ClauseKind, DbInterner, EarlyBinder, ErrorGuaranteed, GenericArg, + GenericArgs, ParamEnv, PolyFnSig, Region, SolverDefId, Ty, TyKind, TypingMode, infer::{DbInternerInferExt, InferCtxt}, }, traits::{self, is_inherent_impl_coherent, structurally_normalize_ty}, }; use itertools::Itertools; -use rustc_hash::FxHashSet; +use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ - AliasTyKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, - TypeVisitor, fast_reject, - inherent::{GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _}, + AliasTyKind, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, fast_reject, + inherent::{AdtDef as _, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _}, }; use span::{AstIdNode, Edition, FileId}; use stdx::{format_to, impl_from, never}; @@ -183,6 +182,7 @@ pub use { mir::{MirEvalError, MirLowerError}, next_solver::abi::Safety, next_solver::{clear_tls_solver_cache, collect_ty_garbage}, + setup_tracing, }, // FIXME: These are needed for import assets, properly encapsulate them. hir_ty::{method_resolution::TraitImpls, next_solver::SimplifiedType}, @@ -198,6 +198,7 @@ use { name::AsName, span_map::{ExpansionSpanMap, RealSpanMap, SpanMap}, }, + hir_ty::next_solver, }; /// hir::Crate describes a single crate. It's the main interface with which @@ -781,22 +782,20 @@ impl Module { let (variants, diagnostics) = e.id.enum_variants_with_diagnostics(db); let file = e.id.lookup(db).id.file_id; let ast_id_map = db.ast_id_map(file); - if let Some(diagnostics) = &diagnostics { - for diag in diagnostics.iter() { - acc.push( - InactiveCode { - node: InFile::new( - file, - ast_id_map.get(diag.ast_id).syntax_node_ptr(), - ), - cfg: diag.cfg.clone(), - opts: diag.opts.clone(), - } - .into(), - ); - } + for diag in diagnostics { + acc.push( + InactiveCode { + node: InFile::new( + file, + ast_id_map.get(diag.ast_id).syntax_node_ptr(), + ), + cfg: diag.cfg.clone(), + opts: diag.opts.clone(), + } + .into(), + ); } - for &(v, _, _) in &variants.variants { + for &(v, _) in variants.variants.values() { let source_map = &v.fields_with_source_map(db).1; push_ty_diagnostics( db, @@ -946,14 +945,17 @@ impl Module { .collect(); if !missing.is_empty() { + let env = ParamEnvAndCrate { + param_env: db.trait_environment(GenericDefId::from(impl_id)), + krate: self.id.krate(db), + }; let self_ty = db.impl_self_ty(impl_id).instantiate_identity().skip_norm_wip(); - let self_ty = structurally_normalize_ty( - &infcx, - self_ty, - db.trait_environment(GenericDefId::from(impl_id).into()), - ); + let self_ty = structurally_normalize_ty(&infcx, self_ty, env.param_env); + let tail_ty = struct_tail_raw(db, interner, self_ty, |ty| { + structurally_normalize_ty(&infcx, ty, env.param_env) + }); let self_ty_is_guaranteed_unsized = matches!( - self_ty.kind(), + tail_ty.kind(), TyKind::Dynamic(..) | TyKind::Slice(..) | TyKind::Str ); if self_ty_is_guaranteed_unsized { @@ -1332,24 +1334,6 @@ pub struct Field { pub(crate) id: LocalFieldId, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InstantiatedField<'db> { - pub(crate) inner: Field, - pub(crate) args: GenericArgs<'db>, -} - -impl<'db> InstantiatedField<'db> { - /// Returns the type as in the signature of the struct. - pub fn ty(&self, db: &'db dyn HirDatabase) -> TypeNs<'db> { - let interner = DbInterner::new_no_crate(db); - - let var_id = self.inner.parent.into(); - let field = db.field_types(var_id)[self.inner.id].get(); - let ty = field.instantiate(interner, self.args).skip_norm_wip(); - TypeNs::new(db, var_id, ty) - } -} - #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] pub struct TupleField { pub owner: InferBodyId, @@ -1370,7 +1354,7 @@ impl TupleField { .get(self.index as usize) .copied() .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)); - Type { env: body_param_env_from_has_crate(db, self.owner.expression_store_owner(db)), ty } + Type::new_body(db, self.owner.expression_store_owner(db), ty) } } @@ -1420,46 +1404,14 @@ impl Field { /// Returns the type as in the signature of the struct. Only use this in the /// context of the field definition. - pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> TypeNs<'db> { - let var_id = self.parent.into(); - let ty = db.field_types(var_id)[self.id].get().skip_binder(); - TypeNs::new(db, var_id, ty) - } - - // FIXME: Find better API to also handle const generics - pub fn ty_with_args<'db>( - &self, - db: &'db dyn HirDatabase, - generics: impl Iterator>, - ) -> Type<'db> { + pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> { let var_id = self.parent.into(); - let def_id: AdtId = match self.parent { - Variant::Struct(it) => it.id.into(), - Variant::Union(it) => it.id.into(), - Variant::EnumVariant(it) => it.parent_enum(db).id.into(), - }; - let interner = DbInterner::new_no_crate(db); - let args = generic_args_from_tys(interner, def_id.into(), generics.map(|ty| ty.ty)); - let ty = db.field_types(var_id)[self.id].get().instantiate(interner, args).skip_norm_wip(); - Type::new(db, var_id, ty) + let ty = db.field_types(var_id)[self.id].get().instantiate_identity().skip_norm_wip(); + Type::new(var_id.adt_id(db).into(), ty) } pub fn layout<'db>(&self, db: &'db dyn HirDatabase) -> Result, LayoutError> { - db.layout_of_ty( - self.ty(db).ty.store(), - param_env_from_has_crate( - db, - match hir_def::VariantId::from(self.parent) { - hir_def::VariantId::EnumVariantId(id) => { - GenericDefId::AdtId(id.lookup(db).parent.into()) - } - hir_def::VariantId::StructId(id) => GenericDefId::AdtId(id.into()), - hir_def::VariantId::UnionId(id) => GenericDefId::AdtId(id.into()), - }, - ) - .store(), - ) - .map(|layout| Layout(layout, db.target_data_layout(self.krate(db).into()).unwrap())) + self.ty(db).layout(db) } pub fn parent_def(&self, _db: &dyn HirDatabase) -> Variant { @@ -1504,10 +1456,6 @@ impl Struct { Type::from_def(db, self.id) } - pub fn ty_params(self, db: &dyn HirDatabase) -> Type<'_> { - Type::from_def_params(db, self.id) - } - pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type<'_> { Type::from_value_def(db, self.id) } @@ -1531,11 +1479,6 @@ impl Struct { pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE) } - - pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedStruct<'db> { - let args = infer_ctxt.fresh_args_for_item(hir_ty::Span::Dummy, self.id.into()); - InstantiatedStruct { inner: self, args } - } } impl HasVisibility for Struct { @@ -1546,34 +1489,6 @@ impl HasVisibility for Struct { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InstantiatedStruct<'db> { - pub(crate) inner: Struct, - pub(crate) args: GenericArgs<'db>, -} - -impl<'db> InstantiatedStruct<'db> { - pub fn fields(self, db: &dyn HirDatabase) -> Vec> { - self.inner - .id - .fields(db) - .fields() - .iter() - .map(|(id, _)| InstantiatedField { - inner: Field { parent: self.inner.into(), id }, - args: self.args, - }) - .collect() - } - - pub fn ty(self, db: &'db dyn HirDatabase) -> TypeNs<'db> { - let interner = DbInterner::new_no_crate(db); - - let ty = db.ty(self.inner.id.into()); - TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args).skip_norm_wip()) - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Union { pub(crate) id: UnionId, @@ -1592,10 +1507,6 @@ impl Union { Type::from_def(db, self.id) } - pub fn ty_params(self, db: &dyn HirDatabase) -> Type<'_> { - Type::from_def_params(db, self.id) - } - pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type<'_> { Type::from_value_def(db, self.id) } @@ -1644,7 +1555,7 @@ impl Enum { } pub fn variants(self, db: &dyn HirDatabase) -> Vec { - self.id.enum_variants(db).variants.iter().map(|&(id, _, _)| EnumVariant { id }).collect() + self.id.enum_variants(db).variants.values().map(|&(id, _)| EnumVariant { id }).collect() } pub fn num_variants(self, db: &dyn HirDatabase) -> usize { @@ -1659,15 +1570,11 @@ impl Enum { Type::from_def(db, self.id) } - pub fn ty_params<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { - Type::from_def_params(db, self.id) - } - /// The type of the enum variant bodies. pub fn variant_body_ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { let interner = DbInterner::new_no_crate(db); - Type::new_for_crate( - self.id.lookup(db).container.krate(db), + Type::no_params( + Type::builtin_type_crate(db), match EnumSignature::variant_body_type(db, self.id) { layout::IntegerType::Pointer(sign) => match sign { true => Ty::new_int(interner, rustc_type_ir::IntTy::Isize), @@ -1721,27 +1628,6 @@ impl HasVisibility for Enum { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InstantiatedEnum<'db> { - pub(crate) inner: Enum, - pub(crate) args: GenericArgs<'db>, -} - -impl<'db> InstantiatedEnum<'db> { - pub fn ty(self, db: &'db dyn HirDatabase) -> TypeNs<'db> { - let interner = DbInterner::new_no_crate(db); - - let ty = db.ty(self.inner.id.into()); - TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args).skip_norm_wip()) - } -} - -impl From<&EnumVariant> for DefWithBodyId { - fn from(&v: &EnumVariant) -> Self { - DefWithBodyId::VariantId(v.into()) - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct EnumVariant { pub(crate) id: EnumVariantId, @@ -1761,9 +1647,7 @@ impl EnumVariant { } pub fn name(self, db: &dyn HirDatabase) -> Name { - let lookup = self.id.lookup(db); - let enum_ = lookup.parent; - enum_.enum_variants(db).variants[lookup.index as usize].1.clone() + self.id.lookup(db).name.clone() } pub fn fields(self, db: &dyn HirDatabase) -> Vec { @@ -1798,7 +1682,7 @@ impl EnumVariant { layout::Variants::Multiple { variants, .. } => Layout( { let lookup = self.id.lookup(db); - let rustc_enum_variant_idx = RustcEnumVariantIdx(lookup.index as usize); + let rustc_enum_variant_idx = RustcEnumVariantIdx(lookup.index(db)); Arc::new(variants[rustc_enum_variant_idx].clone()) }, db.target_data_layout(parent_enum.krate(db).into()).unwrap(), @@ -1810,40 +1694,6 @@ impl EnumVariant { pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE) } - - pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedVariant<'db> { - let args = infer_ctxt.fresh_args_for_item( - hir_ty::Span::Dummy, - self.parent_enum(infer_ctxt.interner.db()).id.into(), - ); - InstantiatedVariant { inner: self, args } - } -} - -// FIXME: Rename to `EnumVariant` -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InstantiatedVariant<'db> { - pub(crate) inner: EnumVariant, - pub(crate) args: GenericArgs<'db>, -} - -impl<'db> InstantiatedVariant<'db> { - pub fn parent_enum(self, db: &dyn HirDatabase) -> InstantiatedEnum<'db> { - InstantiatedEnum { inner: self.inner.id.lookup(db).parent.into(), args: self.args } - } - - pub fn fields(self, db: &dyn HirDatabase) -> Vec> { - self.inner - .id - .fields(db) - .fields() - .iter() - .map(|(id, _)| InstantiatedField { - inner: Field { parent: self.inner.into(), id }, - args: self.args, - }) - .collect() - } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -1892,23 +1742,6 @@ impl Adt { Type::from_def(db, id) } - /// Turns this ADT into a type with the given type parameters. This isn't - /// the greatest API, FIXME find a better one. - pub fn ty_with_args<'db>( - self, - db: &'db dyn HirDatabase, - args: impl IntoIterator>, - ) -> Type<'db> { - let id = AdtId::from(self); - let interner = DbInterner::new_no_crate(db); - let ty = Ty::new_adt( - interner, - id, - generic_args_from_tys(interner, id.into(), args.into_iter().map(|ty| ty.ty)), - ); - Type::new(db, id, ty) - } - pub fn module(self, db: &dyn HirDatabase) -> Module { match self { Adt::Struct(s) => s.module(db), @@ -2016,8 +1849,7 @@ impl AnonConst { pub fn ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { let loc = self.id.loc(db); - let env = body_param_env_from_has_crate(db, loc.owner); - Type { env, ty: loc.ty.get().instantiate_identity().skip_norm_wip() } + Type { owner: self.id.into(), ty: loc.ty.get() } } pub fn eval(self, db: &dyn HirDatabase) -> Result, ConstEvalError> { @@ -2127,7 +1959,7 @@ impl DefWithBody { }, DefWithBody::Static(it) => it.id.into(), DefWithBody::Const(it) => it.id.into(), - DefWithBody::EnumVariant(it) => it.into(), + DefWithBody::EnumVariant(it) => it.id.into(), }) } @@ -2167,8 +1999,6 @@ impl DefWithBody { let Ok(id) = self.try_into() else { return; }; - let krate = self.module(db).id.krate(db); - let env = body_param_env_from_has_crate(db, id); let (body, source_map) = Body::with_source_map(db, id); let sig_source_map = match self { @@ -2191,6 +2021,7 @@ impl DefWithBody { expr_store_diagnostics(db, acc, source_map); let infer = InferenceResult::of(db, id); + let type_owner = id.generic_def(db).into(); for d in infer.diagnostics() { acc.extend(AnyDiagnostic::inference_diagnostic( db, @@ -2198,7 +2029,7 @@ impl DefWithBody { d, source_map, sig_source_map, - env, + type_owner, )); } @@ -2267,8 +2098,11 @@ impl DefWithBody { mir::MirSpan::Unknown => continue, }; acc.push( - MovedOutOfRef { ty: Type::new_for_crate(krate, moof.ty.as_ref()), span } - .into(), + MovedOutOfRef { + ty: Type { owner: type_owner, ty: EarlyBinder::bind(moof.ty.as_ref()) }, + span, + } + .into(), ) } let mol = &borrowck_result.mutability_of_locals; @@ -2365,9 +2199,9 @@ impl DefWithBody { ) -> impl Iterator> { self.id().into_iter().flat_map(move |def_id| { let infer = InferenceResult::of(db, def_id); - let resolver = def_id.resolver(db); + let def_id = def_id.generic_def(db); - infer.expression_types().map(move |(_, ty)| Type::new_with_resolver(db, &resolver, ty)) + infer.expression_types().map(move |(_, ty)| Type::new(def_id, ty)) }) } @@ -2375,9 +2209,9 @@ impl DefWithBody { pub fn pattern_types<'db>(self, db: &'db dyn HirDatabase) -> impl Iterator> { self.id().into_iter().flat_map(move |def_id| { let infer = InferenceResult::of(db, def_id); - let resolver = def_id.resolver(db); + let def_id = def_id.generic_def(db); - infer.pattern_types().map(move |(_, ty)| Type::new_with_resolver(db, &resolver, ty)) + infer.pattern_types().map(move |(_, ty)| Type::new(def_id, ty)) }) } @@ -2385,9 +2219,9 @@ impl DefWithBody { pub fn binding_types<'db>(self, db: &'db dyn HirDatabase) -> impl Iterator> { self.id().into_iter().flat_map(move |def_id| { let infer = InferenceResult::of(db, def_id); - let resolver = def_id.resolver(db); + let def_id = def_id.generic_def(db); - infer.binding_types().map(move |(_, ty)| Type::new_with_resolver(db, &resolver, ty)) + infer.binding_types().map(move |(_, ty)| Type::new(def_id, ty)) }) } } @@ -2420,6 +2254,9 @@ fn expr_store_diagnostics<'db>( ExpressionStoreDiagnostics::PatternArgInExternFn { node } => { PatternArgInExternFn { node: *node }.into() } + ExpressionStoreDiagnostics::FruInDestructuringAssignment { node } => { + FruInDestructuringAssignment { node: *node }.into() + } }); } @@ -2468,12 +2305,9 @@ impl Function { AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => { // Get the type for the trait function, as we can't get the type for the impl function // because it has not `CallableDefId`. - let krate = impl_.module(db).krate(db); - let interner = DbInterner::new_with(db, krate); - let param_env = hir_ty::builtin_derive::param_env(interner, impl_); - let env = ParamEnvAndCrate { param_env, krate }; + // FIXME: This does not account for replacing `Self`. Do we really need that? let Some(trait_method) = method.trait_method(db, impl_) else { - return Type { env, ty: Ty::new_error(interner, ErrorGuaranteed) }; + return Type::unknown(); }; Function::from(trait_method).ty(db) } @@ -2483,160 +2317,36 @@ impl Function { pub fn fn_ptr_type(self, db: &dyn HirDatabase) -> Type<'_> { match self.id { AnyFunctionId::FunctionId(id) => { - let resolver = id.resolver(db); let interner = DbInterner::new_no_crate(db); - // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. let callable_sig = db.callable_item_signature(id.into()).instantiate_identity().skip_norm_wip(); let ty = Ty::new_fn_ptr(interner, callable_sig); - Type::new_with_resolver_inner(db, &resolver, ty) + Type::new(id.into(), ty) } AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => { - struct ParamsShifter<'db> { - interner: DbInterner<'db>, - shift_by: i32, - } - - impl<'db> TypeFolder> for ParamsShifter<'db> { - fn cx(&self) -> DbInterner<'db> { - self.interner - } - - fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { - if let TyKind::Param(param) = ty.kind() { - Ty::new_param( - self.interner, - param.id, - param.index.checked_add_signed(self.shift_by).unwrap(), - ) - } else { - ty.super_fold_with(self) - } - } - - fn fold_const( - &mut self, - ct: hir_ty::next_solver::Const<'db>, - ) -> hir_ty::next_solver::Const<'db> { - if let ConstKind::Param(param) = ct.kind() { - hir_ty::next_solver::Const::new_param( - self.interner, - ParamConst { - id: param.id, - index: param.index.checked_add_signed(self.shift_by).unwrap(), - }, - ) - } else { - ct.super_fold_with(self) - } - } - - fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { - if let RegionKind::ReEarlyParam(param) = r.kind() { - Region::new_early_param( - self.interner, - EarlyParamRegion { - id: param.id, - index: param.index.checked_add_signed(self.shift_by).unwrap(), - }, - ) - } else { - r - } - } - } - // Get the type for the trait function, as we can't get the type for the impl function // because it has not `CallableDefId`. - let krate = impl_.module(db).krate(db); - let interner = DbInterner::new_with(db, krate); - let param_env = hir_ty::builtin_derive::param_env(interner, impl_); - let env = ParamEnvAndCrate { param_env, krate }; + // FIXME: This does not account for replacing `Self`. Do we really need that? let Some(trait_method) = method.trait_method(db, impl_) else { - return Type { env, ty: Ty::new_error(interner, ErrorGuaranteed) }; + return Type::unknown(); }; - // The procedure works as follows: the method may have additional generic parameters (e.g. `Hash::hash()`), - // and we want them to be params of the impl method as well. So we start with the trait method identity - // args and extract from them the trait method own args. In parallel, we retrieve the impl trait ref. - // Now we can put our args as [...impl_trait_ref.args, ...trait_method_own_args], but we have one problem: - // the args in `trait_method_own_args` use indices appropriate for the trait method, which are not necessarily - // good for the impl method. So we shift them by `impl_generics_len - trait_generics_len`, which is essentially - // `impl_generics_len - impl_trait_ref.args.len()`. - let trait_method_fn_ptr = Ty::new_fn_ptr( - interner, - db.callable_item_signature(trait_method.into()) - .instantiate_identity() - .skip_norm_wip(), - ); - let impl_trait_ref = hir_ty::builtin_derive::impl_trait(interner, impl_) - .instantiate_identity() - .skip_norm_wip(); - let trait_method_args = - GenericArgs::identity_for_item(interner, trait_method.into()); - let trait_method_own_args = GenericArgs::new_from_iter( - interner, - trait_method_args.iter().skip(impl_trait_ref.args.len()), - ); - let impl_params_count = hir_ty::builtin_derive::generic_params_count(db, impl_); - let shift_args_by = impl_params_count as i32 - impl_trait_ref.args.len() as i32; - let shifted_trait_method_own_args = trait_method_own_args - .fold_with(&mut ParamsShifter { interner, shift_by: shift_args_by }); - let impl_method_args = GenericArgs::new_from_iter( - interner, - impl_trait_ref.args.iter().chain(shifted_trait_method_own_args), - ); - let impl_method_fn_ptr = EarlyBinder::bind(trait_method_fn_ptr) - .instantiate(interner, impl_method_args) - .skip_norm_wip(); - Type { env, ty: impl_method_fn_ptr } + Function::from(trait_method).fn_ptr_type(db) } } } - fn fn_sig<'db>(self, db: &'db dyn HirDatabase) -> (ParamEnvAndCrate<'db>, PolyFnSig<'db>) { + fn fn_sig<'db>(self, db: &'db dyn HirDatabase) -> (TypeOwnerId, PolyFnSig<'db>) { let fn_ptr = self.fn_ptr_type(db); - let TyKind::FnPtr(sig_tys, hdr) = fn_ptr.ty.kind() else { + let TyKind::FnPtr(sig_tys, hdr) = fn_ptr.ty.skip_binder().kind() else { unreachable!(); }; - (fn_ptr.env, sig_tys.with(hdr)) + (fn_ptr.owner, sig_tys.with(hdr)) } - // FIXME: Find a better API to express all combinations here, perhaps we should have `PreInstantiationType`? - /// Get this function's return type pub fn ret_type(self, db: &dyn HirDatabase) -> Type<'_> { - let (env, sig) = self.fn_sig(db); - Type { env, ty: sig.skip_binder().output() } - } - - // FIXME: Find better API to also handle const generics - pub fn ret_type_with_args<'db>( - self, - db: &'db dyn HirDatabase, - generics: impl Iterator>, - ) -> Type<'db> { - let ret_type = self.ret_type(db); - let interner = DbInterner::new_no_crate(db); - let args = self.adapt_generic_args(interner, generics); - ret_type.derived(EarlyBinder::bind(ret_type.ty).instantiate(interner, args).skip_norm_wip()) - } - - fn adapt_generic_args<'db>( - self, - interner: DbInterner<'db>, - generics: impl Iterator>, - ) -> GenericArgs<'db> { - let generics = generics.map(|ty| ty.ty); - match self.id { - AnyFunctionId::FunctionId(id) => generic_args_from_tys(interner, id.into(), generics), - AnyFunctionId::BuiltinDeriveImplMethod { impl_, .. } => { - let impl_args = GenericArgs::identity_for_item(interner, impl_.into()); - GenericArgs::new_from_iter( - interner, - impl_args.iter().chain(generics.map(Into::into)), - ) - } - } + let (owner, sig) = self.fn_sig(db); + Type { owner, ty: EarlyBinder::bind(sig.skip_binder().output()) } } pub fn async_ret_type<'db>(self, db: &'db dyn HirDatabase) -> Option> { @@ -2646,15 +2356,13 @@ impl Function { if !self.is_async(db) { return None; } - let resolver = id.resolver(db); - // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. let ret_ty = db.callable_item_signature(id.into()).instantiate_identity().skip_binder().output(); for pred in ret_ty.impl_trait_bounds(db).into_iter().flatten() { if let ClauseKind::Projection(projection) = pred.kind().skip_binder() && let Some(output_ty) = projection.term.as_type() { - return Type::new_with_resolver_inner(db, &resolver, output_ty).into(); + return Some(Type::new(id.into(), output_ty)); } } None @@ -2680,7 +2388,7 @@ impl Function { } pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec> { - let (env, sig) = self.fn_sig(db); + let (owner, sig) = self.fn_sig(db); let func = match self.id { AnyFunctionId::FunctionId(id) => Callee::Def(CallableDefId::FunctionId(id)), AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => { @@ -2691,7 +2399,11 @@ impl Function { .inputs() .iter() .enumerate() - .map(|(idx, &ty)| Param { func: func.clone(), ty: Type { env, ty }, idx }) + .map(|(idx, &ty)| Param { + func: func.clone(), + ty: Type { owner, ty: EarlyBinder::bind(ty) }, + idx, + }) .collect() } @@ -2717,28 +2429,6 @@ impl Function { params } - // FIXME: Find better API to also handle const generics - pub fn params_without_self_with_args<'db>( - self, - db: &'db dyn HirDatabase, - generics: impl Iterator>, - ) -> Vec> { - let interner = DbInterner::new_no_crate(db); - let args = self.adapt_generic_args(interner, generics); - let params = self.params_without_self(db); - params - .into_iter() - .map(|param| Param { - func: param.func, - idx: param.idx, - ty: Type { - env: param.ty.env, - ty: EarlyBinder::bind(param.ty.ty).instantiate(interner, args).skip_norm_wip(), - }, - }) - .collect() - } - pub fn is_const(self, db: &dyn HirDatabase) -> bool { match self.id { AnyFunctionId::FunctionId(id) => FunctionSignature::of(db, id).is_const(), @@ -2753,6 +2443,13 @@ impl Function { } } + pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool { + match self.id { + AnyFunctionId::FunctionId(id) => FunctionSignature::of(db, id).is_unsafe(), + AnyFunctionId::BuiltinDeriveImplMethod { .. } => false, + } + } + pub fn is_varargs(self, db: &dyn HirDatabase) -> bool { match self.id { AnyFunctionId::FunctionId(id) => FunctionSignature::of(db, id).is_varargs(), @@ -2913,7 +2610,7 @@ impl Function { id.into(), GenericArgs::empty(interner).store(), ParamEnvAndCrate { - param_env: db.trait_environment(GenericDefId::from(id).into()), + param_env: db.trait_environment(id.into()), krate: id.module(db).krate(db), } .store(), @@ -3085,20 +2782,8 @@ impl SelfParam { } pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> { - let (env, sig) = self.func.fn_sig(db); - Type { env, ty: sig.skip_binder().inputs()[0] } - } - - // FIXME: Find better API to also handle const generics - pub fn ty_with_args<'db>( - &self, - db: &'db dyn HirDatabase, - generics: impl Iterator>, - ) -> Type<'db> { - let interner = DbInterner::new_no_crate(db); - let args = self.func.adapt_generic_args(interner, generics); - let Type { env, ty } = self.ty(db); - Type { env, ty: EarlyBinder::bind(ty).instantiate(interner, args).skip_norm_wip() } + let (owner, sig) = self.func.fn_sig(db); + Type { owner, ty: EarlyBinder::bind(sig.skip_binder().inputs()[0]) } } } @@ -3430,10 +3115,6 @@ impl TypeAlias { Type::from_def(db, self.id) } - pub fn ty_params(self, db: &dyn HirDatabase) -> Type<'_> { - Type::from_def_params(db, self.id) - } - pub fn name(self, db: &dyn HirDatabase) -> Name { TypeAliasSignature::of(db, self.id).name.clone() } @@ -3482,10 +3163,13 @@ impl BuiltinType { } } + pub fn bool() -> BuiltinType { + BuiltinType { inner: hir_def::builtin_type::BuiltinType::Bool } + } + pub fn ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { - let core = Crate::core(db).map(|core| core.id).unwrap_or_else(|| all_crates(db)[0]); let interner = DbInterner::new_no_crate(db); - Type::new_for_crate(core, Ty::from_builtin_type(interner, self.inner)) + Type::no_params(Type::builtin_type_crate(db), Ty::from_builtin_type(interner, self.inner)) } pub fn name(self) -> Name { @@ -3598,6 +3282,7 @@ impl Macro { } MacroExpander::BuiltInAttr(_) => MacroKind::AttrBuiltIn, MacroExpander::BuiltInDerive(_) => MacroKind::DeriveBuiltIn, + MacroExpander::UnimplementedBuiltIn => MacroKind::Declarative, }, MacroId::MacroRulesId(it) => match it.lookup(db).expander { MacroExpander::Declarative { .. } => MacroKind::Declarative, @@ -3606,6 +3291,7 @@ impl Macro { } MacroExpander::BuiltInAttr(_) => MacroKind::AttrBuiltIn, MacroExpander::BuiltInDerive(_) => MacroKind::DeriveBuiltIn, + MacroExpander::UnimplementedBuiltIn => MacroKind::Declarative, }, MacroId::ProcMacroId(it) => match it.lookup(db).kind { ProcMacroKind::CustomDerive => MacroKind::Derive, @@ -4278,23 +3964,19 @@ impl GenericDef { // We cannot call this `Substitution` unfortunately... #[derive(Debug)] pub struct GenericSubstitution<'db> { + owner: TypeOwnerId, def: GenericDefId, subst: GenericArgs<'db>, - env: ParamEnvAndCrate<'db>, } impl<'db> GenericSubstitution<'db> { - fn new(def: GenericDefId, subst: GenericArgs<'db>, env: ParamEnvAndCrate<'db>) -> Self { - Self { def, subst, env } + fn new(def: GenericDefId, subst: GenericArgs<'db>, owner: TypeOwnerId) -> Self { + Self { owner, def, subst } } - fn new_from_fn( - def: Function, - subst: GenericArgs<'db>, - env: ParamEnvAndCrate<'db>, - ) -> Option { + fn new_from_fn(def: Function, subst: GenericArgs<'db>, owner: TypeOwnerId) -> Option { match def.id { - AnyFunctionId::FunctionId(def) => Some(Self::new(def.into(), subst, env)), + AnyFunctionId::FunctionId(def) => Some(Self::new(def.into(), subst, owner)), AnyFunctionId::BuiltinDeriveImplMethod { .. } => None, } } @@ -4341,7 +4023,12 @@ impl<'db> GenericSubstitution<'db> { .zip(type_params); container_params .chain(self_params) - .filter_map(|(ty, name)| Some((name?.symbol().clone(), Type { ty, env: self.env }))) + .filter_map(|(ty, name)| { + Some(( + name?.symbol().clone(), + Type { ty: EarlyBinder::bind(ty), owner: self.owner }, + )) + }) .collect() } } @@ -4454,7 +4141,7 @@ impl Local { let def = self.parent; let infer = InferenceResult::of(db, self.parent_infer); let ty = infer.binding_ty(self.binding_id); - Type::new(db, def, ty) + Type::new_body(db, def, ty) } /// All definitions for this local. Example: `let (a$0, _) | (_, a$0) = it;` @@ -4754,18 +4441,17 @@ impl TypeParam { } pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> { - let resolver = self.id.parent().resolver(db); let interner = DbInterner::new_no_crate(db); let index = hir_ty::type_or_const_param_idx(db, self.id.into()); let ty = Ty::new_param(interner, self.id, index); - Type::new_with_resolver_inner(db, &resolver, ty) + Type::new(self.id.parent(), ty) } /// FIXME: this only lists trait bounds from the item defining the type /// parameter, not additional bounds that might be added e.g. by a method if /// the parameter comes from an impl! pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec { - let self_ty = self.ty(db).ty; + let self_ty = self.ty(db).ty.instantiate_identity().skip_norm_wip(); GenericPredicates::query_explicit(db, self.id.parent()) .iter_identity() .filter_map(|pred| match &pred.kind().skip_binder() { @@ -4779,10 +4465,9 @@ impl TypeParam { pub fn default(self, db: &dyn HirDatabase) -> Option> { let ty = generic_arg_from_param(db, self.id.into())?; - let resolver = self.id.parent().resolver(db); match ty.kind() { rustc_type_ir::GenericArgKind::Type(it) if !it.is_ty_error() => { - Some(Type::new_with_resolver_inner(db, &resolver, it)) + Some(Type::new(self.id.parent(), it)) } _ => None, } @@ -4843,7 +4528,7 @@ impl ConstParam { } pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> { - Type::new(db, self.id.parent(), db.const_param_ty(self.id)) + Type::new(self.id.parent(), db.const_param_ty(self.id)) } pub fn default(self, db: &dyn HirDatabase, display_target: DisplayTarget) -> Option { @@ -4965,21 +4650,26 @@ impl Impl { /// blanket impls, and only does a shallow type constructor check. In fact, this should've probably been on `Adt` /// etc., and not on `Type`. If you would want to create a precise list of all impls applying to a type, /// you would need to include blanket impls, and try to prove to predicates for each candidate. - pub fn all_for_type<'db>(db: &'db dyn HirDatabase, Type { ty, env }: Type<'db>) -> Vec { + pub fn all_for_type<'db>(db: &'db dyn HirDatabase, ty: Type<'db>) -> Vec { let mut result = Vec::new(); let interner = DbInterner::new_no_crate(db); - let Some(simplified_ty) = - fast_reject::simplify_type(interner, ty, fast_reject::TreatParams::AsRigid) - else { + let Some(simplified_ty) = fast_reject::simplify_type( + interner, + ty.ty.skip_binder(), + fast_reject::TreatParams::AsRigid, + ) else { return Vec::new(); }; let mut extend_with_impls = |impls: Either<&[ImplId], &[BuiltinDeriveImplId]>| match impls { Either::Left(impls) => result.extend(impls.iter().copied().map(Impl::from)), Either::Right(impls) => result.extend(impls.iter().copied().map(Impl::from)), }; - method_resolution::with_incoherent_inherent_impls(db, env.krate, &simplified_ty, |impls| { - extend_with_impls(Either::Left(impls)) - }); + method_resolution::with_incoherent_inherent_impls( + db, + ty.krate(db), + &simplified_ty, + |impls| extend_with_impls(Either::Left(impls)), + ); if let Some(module) = method_resolution::simplified_type_module(db, &simplified_ty) { InherentImpls::for_each_crate_and_block( db, @@ -4987,7 +4677,7 @@ impl Impl { module.block(db), &mut |impls| extend_with_impls(Either::Left(impls.for_self_ty(&simplified_ty))), ); - std::iter::successors(module.block(db), |block| block.loc(db).module.block(db)) + iter::successors(module.block(db), |block| block.loc(db).module.block(db)) .filter_map(|block| TraitImpls::for_block(db, block).as_deref()) .for_each(|impls| impls.for_self_ty(&simplified_ty, &mut extend_with_impls)); for &krate in &*all_crates(db) { @@ -5042,21 +4732,16 @@ impl Impl { match self.id { AnyImplId::ImplId(id) => { let trait_ref = db.impl_trait(id)?.instantiate_identity().skip_norm_wip(); - let resolver = id.resolver(db); - Some(TraitRef::new_with_resolver(db, &resolver, trait_ref)) + Some(TraitRef::new(id.into(), trait_ref)) } AnyImplId::BuiltinDeriveImplId(id) => { let loc = id.loc(db); let krate = loc.module(db).krate(db); let interner = DbInterner::new_with(db, krate); - let env = ParamEnvAndCrate { - param_env: hir_ty::builtin_derive::param_env(interner, id), - krate, - }; let trait_ref = hir_ty::builtin_derive::impl_trait(interner, id) .instantiate_identity() .skip_norm_wip(); - Some(TraitRef { env, trait_ref }) + Some(TraitRef { owner: TypeOwnerId::BuiltinDeriveImplId(id), trait_ref }) } } } @@ -5064,24 +4749,16 @@ impl Impl { pub fn self_ty(self, db: &dyn HirDatabase) -> Type<'_> { match self.id { AnyImplId::ImplId(id) => { - let resolver = id.resolver(db); - // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. let ty = db.impl_self_ty(id).instantiate_identity().skip_norm_wip(); - Type::new_with_resolver_inner(db, &resolver, ty) + Type::new(id.into(), ty) } AnyImplId::BuiltinDeriveImplId(id) => { let loc = id.loc(db); let krate = loc.module(db).krate(db); let interner = DbInterner::new_with(db, krate); - let env = ParamEnvAndCrate { - param_env: hir_ty::builtin_derive::param_env(interner, id), - krate, - }; - let ty = hir_ty::builtin_derive::impl_trait(interner, id) - .instantiate_identity() - .skip_norm_wip() - .self_ty(); - Type { env, ty } + let ty = + hir_ty::builtin_derive::impl_trait(interner, id).map_bound(|it| it.self_ty()); + Type { owner: TypeOwnerId::BuiltinDeriveImplId(id), ty } } } } @@ -5143,38 +4820,33 @@ impl Impl { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct TraitRef<'db> { - env: ParamEnvAndCrate<'db>, + owner: TypeOwnerId, trait_ref: hir_ty::next_solver::TraitRef<'db>, } impl<'db> TraitRef<'db> { - pub(crate) fn new_with_resolver( - db: &'db dyn HirDatabase, - resolver: &Resolver<'_>, - trait_ref: hir_ty::next_solver::TraitRef<'db>, - ) -> Self { - let env = param_env_from_resolver(db, resolver); - TraitRef { env, trait_ref } + fn new(owner: GenericDefId, trait_ref: hir_ty::next_solver::TraitRef<'db>) -> Self { + Self { owner: TypeOwnerId::GenericDefId(owner), trait_ref } } pub fn trait_(&self) -> Trait { Trait { id: self.trait_ref.def_id.0 } } - pub fn self_ty(&self) -> TypeNs<'_> { + pub fn self_ty(&self) -> Type<'_> { let ty = self.trait_ref.self_ty(); - TypeNs { env: self.env, ty } + Type { owner: self.owner, ty: EarlyBinder::bind(ty) } } /// Returns `idx`-th argument of this trait reference if it is a type argument. Note that the /// first argument is the `Self` type. - pub fn get_type_argument(&self, idx: usize) -> Option> { + pub fn get_type_argument(&self, idx: usize) -> Option> { self.trait_ref .args .as_slice() .get(idx) .and_then(|arg| arg.ty()) - .map(|ty| TypeNs { env: self.env, ty }) + .map(|ty| Type { owner: self.owner, ty: EarlyBinder::bind(ty) }) } } @@ -5186,6 +4858,7 @@ enum AnyClosureId { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Closure<'db> { + owner: TypeOwnerId, id: AnyClosureId, subst: GenericArgs<'db>, } @@ -5223,12 +4896,11 @@ impl<'db> Closure<'db> { let InternedClosure { owner: infer_owner, expr: closure, .. } = closure; let infer = InferenceResult::of(db, infer_owner); let owner = infer_owner.expression_store_owner(db); - let param_env = body_param_env_from_has_crate(db, owner); infer.closures_data[&closure] .min_captures .values() .flatten() - .map(|capture| ClosureCapture { owner, infer_owner, closure, capture, param_env }) + .map(|capture| ClosureCapture { owner, infer_owner, closure, capture }) .collect() } @@ -5319,7 +4991,6 @@ pub struct ClosureCapture<'db> { infer_owner: InferBodyId, closure: ExprId, capture: &'db hir_ty::closure_analysis::CapturedPlace, - param_env: ParamEnvAndCrate<'db>, } impl<'db> ClosureCapture<'db> { @@ -5427,14 +5098,14 @@ impl<'db> ClosureCapture<'db> { result } - pub fn ty(&self, _db: &'db dyn HirDatabase) -> Type<'db> { - Type { env: self.param_env, ty: self.capture.place.ty() } + pub fn ty(&self, db: &'db dyn HirDatabase) -> Type<'db> { + Type::new_body(db, self.owner, self.capture.place.ty()) } /// The type that is stored in the closure, which is different from [`Self::ty()`], representing /// the place's type, when the capture is by ref. pub fn captured_ty(&self, db: &'db dyn HirDatabase) -> Type<'db> { - Type { env: self.param_env, ty: self.capture.captured_ty(db) } + Type::new_body(db, self.owner, self.capture.captured_ty(db)) } } @@ -5510,69 +5181,143 @@ impl CaptureUsageSource { } } -#[derive(Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +enum TypeOwnerId { + GenericDefId(GenericDefId), + BuiltinDeriveImplId(BuiltinDeriveImplId), + AnonConstId(AnonConstId), + // FIXME: What do when we unify two different crates? Currently we just randomly keep one. + NoParams(base_db::Crate), +} +impl_from!(GenericDefId, BuiltinDeriveImplId, AnonConstId for TypeOwnerId); + +impl TypeOwnerId { + fn unify(self, other: Self) -> Option { + match (self, other) { + (TypeOwnerId::NoParams(_), owner) => Some(owner), + (owner, TypeOwnerId::NoParams(_)) => Some(owner), + (_, _) => { + if self == other { + Some(self) + } else { + None + } + } + } + } + + #[track_caller] + fn must_unify(self, other: Self) -> Self { + self.unify(other).expect("failed to unify type owners") + } + + fn can_rebase_into( + self, + db: &dyn HirDatabase, + rebase_into: Self, + self_ty: EarlyBinder<'_, Ty<'_>>, + ) -> bool { + if self == rebase_into || !self_ty.skip_binder().has_param() { + return true; + } + let self_def = match self { + TypeOwnerId::GenericDefId(def) => def, + TypeOwnerId::BuiltinDeriveImplId(_) | TypeOwnerId::AnonConstId(_) => return false, + TypeOwnerId::NoParams(_) => return true, + }; + let self_def = match self_def { + GenericDefId::ImplId(def) => ItemContainerId::ImplId(def), + GenericDefId::TraitId(def) => ItemContainerId::TraitId(def), + GenericDefId::AdtId(_) + | GenericDefId::ConstId(_) + | GenericDefId::FunctionId(_) + | GenericDefId::StaticId(_) + | GenericDefId::TypeAliasId(_) => return false, + }; + let rebase_into_def = match rebase_into { + TypeOwnerId::GenericDefId(def) => def, + TypeOwnerId::BuiltinDeriveImplId(_) + | TypeOwnerId::AnonConstId(_) + | TypeOwnerId::NoParams(_) => return false, + }; + let rebase_into_parent = match rebase_into_def { + GenericDefId::ConstId(def) => def.loc(db).container, + GenericDefId::FunctionId(def) => def.loc(db).container, + GenericDefId::TypeAliasId(def) => def.loc(db).container, + GenericDefId::AdtId(_) + | GenericDefId::ImplId(_) + | GenericDefId::StaticId(_) + | GenericDefId::TraitId(_) => return false, + }; + self_def == rebase_into_parent + } +} + +/// Note: A [`Type`] remembers its origin. Trying to do anything (except comparing) +/// with types of different origins will cause errors or panics. Instead, use the `instantiate` methods. +#[derive(Clone, Debug)] pub struct Type<'db> { - env: ParamEnvAndCrate<'db>, - ty: Ty<'db>, + owner: TypeOwnerId, + ty: EarlyBinder<'db, Ty<'db>>, } -impl<'db> Type<'db> { - pub(crate) fn new_with_resolver( - db: &'db dyn HirDatabase, - resolver: &Resolver<'_>, - ty: Ty<'db>, - ) -> Self { - Type::new_with_resolver_inner(db, resolver, ty) +impl<'db> std::hash::Hash for Type<'db> { + fn hash(&self, state: &mut H) { + // Do not hash the owner as different owners can compare the same. + // self.owner.hash(state); + self.ty.hash(state); } +} - pub(crate) fn new_with_resolver_inner( - db: &'db dyn HirDatabase, - resolver: &Resolver<'_>, - ty: Ty<'db>, - ) -> Self { - let environment = param_env_from_resolver(db, resolver); - Type { env: environment, ty } +impl<'db> PartialEq for Type<'db> { + fn eq(&self, other: &Self) -> bool { + if self.ty != other.ty { + return false; + } + hir_ty::with_attached_db(|db| { + self.owner.can_rebase_into(db, other.owner, self.ty) + || other.owner.can_rebase_into(db, self.owner, other.ty) + }) } +} - pub(crate) fn new_for_crate(krate: base_db::Crate, ty: Ty<'db>) -> Self { - Type { env: empty_param_env(krate), ty } +impl<'db> Eq for Type<'db> {} + +impl<'db> Type<'db> { + fn new(owner: GenericDefId, ty: Ty<'db>) -> Self { + Type { owner: TypeOwnerId::GenericDefId(owner), ty: EarlyBinder::bind(ty) } } - fn new(db: &'db dyn HirDatabase, lexical_env: impl HasResolver, ty: Ty<'db>) -> Self { - let resolver = lexical_env.resolver(db); - let environment = param_env_from_resolver(db, &resolver); - Type { env: environment, ty } + fn new_body(db: &dyn HirDatabase, owner: ExpressionStoreOwnerId, ty: Ty<'db>) -> Self { + Self::new(owner.generic_def(db), ty) } - fn from_def(db: &'db dyn HirDatabase, def: impl Into + HasResolver) -> Self { - let interner = DbInterner::new_no_crate(db); - let ty = db.ty(def.into()); - let def = match def.into() { - TyDefId::AdtId(it) => GenericDefId::AdtId(it), - TyDefId::TypeAliasId(it) => GenericDefId::TypeAliasId(it), - TyDefId::BuiltinType(_) => { - return Type::new(db, def, ty.skip_binder()); - } - }; - let args = GenericArgs::error_for_item(interner, def.into()); - Type::new(db, def, ty.instantiate(interner, args).skip_norm_wip()) + fn no_params(krate: base_db::Crate, ty: Ty<'db>) -> Self { + Type { owner: TypeOwnerId::NoParams(krate), ty: EarlyBinder::bind(ty) } } - // FIXME: We shouldn't leak `TyKind::Param`s. - fn from_def_params(db: &'db dyn HirDatabase, def: impl Into + HasResolver) -> Self { - let ty = db.ty(def.into()); - Type::new(db, def, ty.instantiate_identity().skip_norm_wip()) + fn builtin_type_crate(db: &'db dyn HirDatabase) -> base_db::Crate { + // It doesn't really matter. + all_crates(db)[0] } - fn from_value_def( - db: &'db dyn HirDatabase, - def: impl Into + HasResolver, - ) -> Self { - let interner = DbInterner::new_no_crate(db); - let Some(ty) = db.value_ty(def.into()) else { - return Type::new(db, def, Ty::new_error(interner, ErrorGuaranteed)); + fn from_def(db: &'db dyn HirDatabase, def: impl Into) -> Self { + let def = def.into(); + let ty = db.ty(def); + let owner = match def { + TyDefId::AdtId(it) => TypeOwnerId::GenericDefId(GenericDefId::AdtId(it)), + TyDefId::TypeAliasId(it) => TypeOwnerId::GenericDefId(GenericDefId::TypeAliasId(it)), + TyDefId::BuiltinType(_) => TypeOwnerId::NoParams(Self::builtin_type_crate(db)), + }; + Type { owner, ty } + } + + fn from_value_def(db: &'db dyn HirDatabase, def: impl Into) -> Self { + let def = def.into(); + let Some(ty) = db.value_ty(def) else { + return Type::unknown(); }; - let def = match def.into() { + let def = match def { ValueTyDefId::ConstId(it) => GenericDefId::ConstId(it), ValueTyDefId::FunctionId(it) => GenericDefId::FunctionId(it), ValueTyDefId::StructId(it) => GenericDefId::AdtId(AdtId::StructId(it)), @@ -5580,52 +5325,192 @@ impl<'db> Type<'db> { ValueTyDefId::EnumVariantId(it) => { GenericDefId::AdtId(AdtId::EnumId(it.lookup(db).parent)) } - ValueTyDefId::StaticId(_) => { - return Type::new(db, def, ty.skip_binder()); + ValueTyDefId::StaticId(it) => { + return Type::no_params(hir_def::HasModule::krate(&it, db), ty.skip_binder()); + } + }; + Type::new(def, ty.instantiate_identity().skip_norm_wip()) + } + + /// Replace any generic parameters with error types. + pub fn instantiate_with_errors(&self) -> Self { + let interner = DbInterner::conjure(); + let krate = self.krate(interner.db()); + let args = match self.owner { + TypeOwnerId::GenericDefId(def) => GenericArgs::error_for_item(interner, def.into()), + TypeOwnerId::BuiltinDeriveImplId(def) => { + GenericArgs::error_for_item(interner, def.into()) } + TypeOwnerId::AnonConstId(def) => GenericArgs::error_for_item(interner, def.into()), + TypeOwnerId::NoParams(_) => GenericArgs::empty(interner), }; - let args = GenericArgs::error_for_item(interner, def.into()); - Type::new(db, def, ty.instantiate(interner, args).skip_norm_wip()) + Type::no_params(krate, self.ty.instantiate(interner, args).skip_norm_wip()) + } + + // FIXME: Find some way with const params, maybe even lifetimes? + pub fn instantiate(&self, args: impl IntoIterator>>) -> Type<'db> { + let interner = DbInterner::conjure(); + let (args, owner) = match self.owner { + TypeOwnerId::GenericDefId(def) => generic_args_from_tys(interner, def.into(), args), + TypeOwnerId::BuiltinDeriveImplId(def) => { + generic_args_from_tys(interner, def.into(), args) + } + TypeOwnerId::AnonConstId(def) => generic_args_from_tys(interner, def.into(), args), + TypeOwnerId::NoParams(krate) => { + (GenericArgs::empty(interner), TypeOwnerId::NoParams(krate)) + } + }; + Type { owner, ty: EarlyBinder::bind(self.ty.instantiate(interner, args).skip_norm_wip()) } + } + + /// Instantiates multiple types with infer vars, keeping the same infer vars for the same owners. + fn instantiate_many_with_infer( + tys: impl IntoIterator>>, + infcx: &InferCtxt<'db>, + ) -> impl Iterator> { + let mut var_for_param = FxHashMap::default(); + tys.into_iter().map(move |ty| { + let ty = ty.borrow(); + let owner = match ty.owner { + TypeOwnerId::GenericDefId(def) => def.into(), + TypeOwnerId::BuiltinDeriveImplId(def) => def.into(), + TypeOwnerId::AnonConstId(def) => def.into(), + TypeOwnerId::NoParams(_) => return ty.ty.skip_binder(), + }; + let args = GenericArgs::for_item(infcx.interner, owner, |_, param, _| { + *var_for_param + .entry(param) + .or_insert_with(|| infcx.var_for_def(param, hir_ty::Span::Dummy)) + }); + + ty.ty.instantiate(infcx.interner, args).skip_norm_wip() + }) + } + + /// Tries to put this type as-is in the context of `rebase_into`. This will return `Some(_)` if: + /// + /// - The type does not reference generic parameters, or + /// - `rebase_into` is in the context of a child of our context (for example, a function in an impl). + pub fn try_rebase_into( + &self, + db: &'db dyn HirDatabase, + rebase_into: &Type<'db>, + ) -> Option { + if self.owner.can_rebase_into(db, rebase_into.owner, self.ty) { + Some(Type { owner: rebase_into.owner, ty: self.ty }) + } else { + None + } + } + + /// If `self` can be rebased into `rebase_into`, returns that. Otherwise, instantiates `self` with errors + /// and returns that. + pub fn rebase_into_or_error( + &self, + db: &'db dyn HirDatabase, + rebase_into: &Type<'db>, + ) -> Type<'db> { + self.try_rebase_into(db, rebase_into).unwrap_or_else(|| self.instantiate_with_errors()) } - pub fn new_slice(ty: Self) -> Self { + pub fn try_rebase_into_owner( + &self, + db: &'db dyn HirDatabase, + new_owner: GenericDef, + ) -> Option { + let new_owner = new_owner.id()?.into(); + if self.owner.can_rebase_into(db, new_owner, self.ty) { + Some(Type { owner: new_owner, ty: self.ty }) + } else { + None + } + } + + pub fn rebase_into_owner_or_error( + &self, + db: &'db dyn HirDatabase, + new_owner: GenericDef, + ) -> Self { + self.try_rebase_into_owner(db, new_owner).unwrap_or_else(|| self.instantiate_with_errors()) + } + + pub fn unknown() -> Self { let interner = DbInterner::conjure(); - Type { env: ty.env, ty: Ty::new_slice(interner, ty.ty) } + Type::no_params( + Self::builtin_type_crate(interner.db()), + Ty::new_error(interner, ErrorGuaranteed), + ) + } + + pub fn new_slice(db: &'db dyn HirDatabase, ty: Self) -> Self { + let interner = DbInterner::new_no_crate(db); + Type { owner: ty.owner, ty: ty.ty.map_bound(|ty| Ty::new_slice(interner, ty)) } + } + + pub fn new_tuple( + db: &'db dyn HirDatabase, + tys: impl IntoIterator>>, + ) -> Self { + let interner = DbInterner::new_no_crate(db); + let mut owner = None::; + let ty = EarlyBinder::bind(Ty::new_tup_from_iter( + interner, + tys.into_iter().map(|ty| { + let ty = ty.borrow(); + + match &mut owner { + Some(owner) => *owner = owner.must_unify(ty.owner), + None => owner = Some(ty.owner), + } + + ty.ty.skip_binder() + }), + )); + let owner = + owner.unwrap_or_else(|| TypeOwnerId::NoParams(Self::builtin_type_crate(interner.db()))); + Type { owner, ty } } - pub fn new_tuple(krate: base_db::Crate, tys: &[Self]) -> Self { - let tys = tys.iter().map(|it| it.ty); + pub fn new_unit() -> Self { let interner = DbInterner::conjure(); - Type { env: empty_param_env(krate), ty: Ty::new_tup_from_iter(interner, tys) } + Type::no_params(Self::builtin_type_crate(interner.db()), Ty::new_unit(interner)) } pub fn is_unit(&self) -> bool { - self.ty.is_unit() + self.ty.skip_binder().is_unit() } pub fn is_bool(&self) -> bool { - matches!(self.ty.kind(), TyKind::Bool) + matches!(self.ty.skip_binder().kind(), TyKind::Bool) } pub fn is_str(&self) -> bool { - matches!(self.ty.kind(), TyKind::Str) + matches!(self.ty.skip_binder().kind(), TyKind::Str) } pub fn is_never(&self) -> bool { - matches!(self.ty.kind(), TyKind::Never) + matches!(self.ty.skip_binder().kind(), TyKind::Never) } pub fn is_mutable_reference(&self) -> bool { - matches!(self.ty.kind(), TyKind::Ref(.., hir_ty::next_solver::Mutability::Mut)) + matches!( + self.ty.skip_binder().kind(), + TyKind::Ref(.., hir_ty::next_solver::Mutability::Mut) + ) } pub fn is_reference(&self) -> bool { - matches!(self.ty.kind(), TyKind::Ref(..)) + matches!(self.ty.skip_binder().kind(), TyKind::Ref(..)) } pub fn contains_reference(&self, db: &'db dyn HirDatabase) -> bool { let interner = DbInterner::new_no_crate(db); - return self.ty.visit_with(&mut Visitor { interner }).is_break(); + return self + .ty + .instantiate_identity() + .skip_norm_wip() + .visit_with(&mut Visitor { interner }) + .is_break(); fn is_phantom_data(db: &dyn HirDatabase, adt_id: AdtId) -> bool { match adt_id { @@ -5681,8 +5566,8 @@ impl<'db> Type<'db> { AdtId::EnumId(id) => id .enum_variants(self.interner.db()) .variants - .iter() - .map(|&(variant_id, _, _)| variant_id_to_fields(variant_id.into())) + .values() + .map(|&(variant_id, _)| variant_id_to_fields(variant_id.into())) .collect(), AdtId::UnionId(id) => { vec![variant_id_to_fields(id.into())] @@ -5703,89 +5588,129 @@ impl<'db> Type<'db> { } pub fn as_reference(&self) -> Option<(Type<'db>, Mutability)> { - let TyKind::Ref(_lt, ty, m) = self.ty.kind() else { return None }; + let TyKind::Ref(_lt, ty, m) = self.ty.skip_binder().kind() else { return None }; let m = Mutability::from_mutable(matches!(m, hir_ty::next_solver::Mutability::Mut)); Some((self.derived(ty), m)) } - pub fn add_reference(&self, mutability: Mutability) -> Self { - let interner = DbInterner::conjure(); + pub fn as_reference_inner(&self) -> Option> { + self.as_reference().map(|(inner, _)| inner) + } + + pub fn add_reference(&self, db: &'db dyn HirDatabase, mutability: Mutability) -> Self { + let interner = DbInterner::new_no_crate(db); let ty_mutability = match mutability { Mutability::Shared => hir_ty::next_solver::Mutability::Not, Mutability::Mut => hir_ty::next_solver::Mutability::Mut, }; - self.derived(Ty::new_ref(interner, Region::error(interner), self.ty, ty_mutability)) + self.derived(Ty::new_ref( + interner, + Region::error(interner), + self.ty.skip_binder(), + ty_mutability, + )) } pub fn is_slice(&self) -> bool { - matches!(self.ty.kind(), TyKind::Slice(..)) + matches!(self.ty.skip_binder().kind(), TyKind::Slice(..)) } pub fn is_usize(&self) -> bool { - matches!(self.ty.kind(), TyKind::Uint(rustc_type_ir::UintTy::Usize)) + matches!(self.ty.skip_binder().kind(), TyKind::Uint(rustc_type_ir::UintTy::Usize)) } pub fn is_float(&self) -> bool { - matches!(self.ty.kind(), TyKind::Float(_)) + matches!(self.ty.skip_binder().kind(), TyKind::Float(_)) } pub fn is_char(&self) -> bool { - matches!(self.ty.kind(), TyKind::Char) + matches!(self.ty.skip_binder().kind(), TyKind::Char) } pub fn is_int_or_uint(&self) -> bool { - matches!(self.ty.kind(), TyKind::Int(_) | TyKind::Uint(_)) + matches!(self.ty.skip_binder().kind(), TyKind::Int(_) | TyKind::Uint(_)) } pub fn is_scalar(&self) -> bool { matches!( - self.ty.kind(), + self.ty.skip_binder().kind(), TyKind::Bool | TyKind::Char | TyKind::Int(_) | TyKind::Uint(_) | TyKind::Float(_) ) } pub fn is_tuple(&self) -> bool { - matches!(self.ty.kind(), TyKind::Tuple(..)) - } - - pub fn remove_ref(&self) -> Option> { - match self.ty.kind() { - TyKind::Ref(_, ty, _) => Some(self.derived(ty)), - _ => None, - } + matches!(self.ty.skip_binder().kind(), TyKind::Tuple(..)) } pub fn as_slice(&self) -> Option> { - match self.ty.kind() { + match self.ty.skip_binder().kind() { TyKind::Slice(ty) => Some(self.derived(ty)), _ => None, } } pub fn strip_references(&self) -> Self { - self.derived(self.ty.strip_references()) + self.derived(self.ty.skip_binder().strip_references()) } // FIXME: This is the same as `remove_ref()`, remove one of these methods. pub fn strip_reference(&self) -> Self { - self.derived(self.ty.strip_reference()) + self.derived(self.ty.skip_binder().strip_reference()) } pub fn is_unknown(&self) -> bool { - self.ty.is_ty_error() + self.ty.skip_binder().is_ty_error() + } + + fn krate(&self, db: &'db dyn HirDatabase) -> base_db::Crate { + match self.owner { + TypeOwnerId::GenericDefId(def) => hir_def::HasModule::krate(&def, db), + TypeOwnerId::BuiltinDeriveImplId(def) => { + hir_def::HasModule::krate(&def.loc(db).adt, db) + } + TypeOwnerId::AnonConstId(def) => hir_def::HasModule::krate(&def, db), + TypeOwnerId::NoParams(krate) => krate, + } + } + + fn param_env(&self, db: &'db dyn HirDatabase) -> ParamEnvAndCrate<'db> { + let interner = DbInterner::new_no_crate(db); + let krate = self.krate(db); + match self.owner { + TypeOwnerId::GenericDefId(def) => { + ParamEnvAndCrate { param_env: db.trait_environment(def), krate } + } + TypeOwnerId::BuiltinDeriveImplId(def) => ParamEnvAndCrate { + param_env: hir_ty::builtin_derive::param_env(interner, def), + krate, + }, + TypeOwnerId::AnonConstId(def) => ParamEnvAndCrate { + param_env: db.trait_environment(def.loc(db).owner.generic_def(db)), + krate, + }, + TypeOwnerId::NoParams(_) => { + ParamEnvAndCrate { param_env: ParamEnv::empty(interner), krate } + } + } } /// Checks that particular type `ty` implements `std::future::IntoFuture` or /// `std::future::Future` and returns the `Output` associated type. /// This function is used in `.await` syntax completion. pub fn into_future_output(&self, db: &'db dyn HirDatabase) -> Option> { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let env = self.param_env(db); + let lang_items = hir_def::lang_item::lang_items(db, env.krate); let (trait_, output_assoc_type) = lang_items .IntoFuture .zip(lang_items.IntoFutureOutput) .or(lang_items.Future.zip(lang_items.FutureOutput))?; - if !traits::implements_trait_unique(self.ty, db, self.env, trait_) { + if !traits::implements_trait_unique( + self.ty.instantiate_identity().skip_norm_wip(), + db, + env, + trait_, + ) { return None; } @@ -5794,32 +5719,46 @@ impl<'db> Type<'db> { /// This does **not** resolve `IntoFuture`, only `Future`. pub fn future_output(self, db: &'db dyn HirDatabase) -> Option> { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let krate = self.krate(db); + let lang_items = hir_def::lang_item::lang_items(db, krate); let future_output = lang_items.FutureOutput?; self.normalize_trait_assoc_type(db, &[], future_output.into()) } /// This does **not** resolve `IntoIterator`, only `Iterator`. pub fn iterator_item(self, db: &'db dyn HirDatabase) -> Option> { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let krate = self.krate(db); + let lang_items = hir_def::lang_item::lang_items(db, krate); let iterator_item = lang_items.IteratorItem?; self.normalize_trait_assoc_type(db, &[], iterator_item.into()) } pub fn impls_iterator(self, db: &'db dyn HirDatabase) -> bool { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let env = self.param_env(db); + let lang_items = hir_def::lang_item::lang_items(db, env.krate); let Some(iterator_trait) = lang_items.Iterator else { return false; }; - traits::implements_trait_unique(self.ty, db, self.env, iterator_trait) + traits::implements_trait_unique( + self.ty.instantiate_identity().skip_norm_wip(), + db, + env, + iterator_trait, + ) } /// Resolves the projection `::IntoIter` and returns the resulting type pub fn into_iterator_iter(self, db: &'db dyn HirDatabase) -> Option> { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let env = self.param_env(db); + let lang_items = hir_def::lang_item::lang_items(db, env.krate); let trait_ = lang_items.IntoIterator?; - if !traits::implements_trait_unique(self.ty, db, self.env, trait_) { + if !traits::implements_trait_unique( + self.ty.instantiate_identity().skip_norm_wip(), + db, + env, + trait_, + ) { return None; } @@ -5832,24 +5771,63 @@ impl<'db> Type<'db> { /// This function can be used to check if a particular type is callable, since FnOnce is a /// supertrait of Fn and FnMut, so all callable types implements at least FnOnce. pub fn impls_fnonce(&self, db: &'db dyn HirDatabase) -> bool { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let env = self.param_env(db); + let lang_items = hir_def::lang_item::lang_items(db, env.krate); let fnonce_trait = match lang_items.FnOnce { Some(it) => it, None => return false, }; - traits::implements_trait_unique(self.ty, db, self.env, fnonce_trait) + traits::implements_trait_unique( + self.ty.instantiate_identity().skip_norm_wip(), + db, + env, + fnonce_trait, + ) } // FIXME: Find better API that also handles const generics pub fn impls_trait(&self, db: &'db dyn HirDatabase, trait_: Trait, args: &[Type<'db>]) -> bool { + let env = self.param_env(db); let interner = DbInterner::new_no_crate(db); - let args = generic_args_from_tys( - interner, - trait_.id.into(), - std::iter::once(self.ty).chain(args.iter().map(|ty| ty.ty)), - ); - traits::implements_trait_unique_with_args(db, self.env, trait_.id, args) + let (args, _owner) = + generic_args_from_tys(interner, trait_.id.into(), iter::once(self).chain(args)); + traits::implements_trait_unique_with_args(db, env, trait_.id, args) + } + + /// Unlike [`Type::impls_trait()`], which checks whether the type always implements the trait, + /// this check whether there are any generic args substitution for `args`` that will cause the + /// trait to be implemented. + /// + /// For example, suppose we're there's `struct Foo` and we're checking `Foo: Trait`. + /// `impls_trait()` will return true only if there is `impl Trait for Foo`, while this + /// method will also return true if there is only `impl Trait for Foo`. + /// + /// Note that you can of course instantiate `Foo` with `` and then the checks will + /// be the same, but this check for *any* substitution. + /// + /// Unlike almost anything that takes more than one type, you *can* pass types from different origins + /// to this function. + pub fn has_any_impl( + &self, + db: &'db dyn HirDatabase, + trait_: Trait, + args: &[Type<'db>], + ) -> bool { + let interner = DbInterner::new_no_crate(db); + let env = ParamEnvAndCrate { param_env: ParamEnv::empty(interner), krate: self.krate(db) }; + traits::implements_trait_unique_with_infcx(db, env, trait_.id, &mut |infcx| { + let mut args = Self::instantiate_many_with_infer(iter::once(self).chain(args), infcx); + GenericArgs::for_item(infcx.interner, trait_.id.into(), |_, param, _| { + if let GenericParamId::TypeParamId(_) = param + && let Some(arg) = args.next() + { + arg.into() + } else { + infcx.var_for_def(param, hir_ty::Span::Dummy) + } + }) + }) } pub fn normalize_trait_assoc_type( @@ -5858,12 +5836,10 @@ impl<'db> Type<'db> { args: &[Type<'db>], alias: TypeAlias, ) -> Option> { - let interner = DbInterner::new_with(db, self.env.krate); - let args = generic_args_from_tys( - interner, - alias.id.into(), - std::iter::once(self.ty).chain(args.iter().map(|ty| ty.ty)), - ); + let env = self.param_env(db); + let interner = DbInterner::new_with(db, env.krate); + let (args, owner) = + generic_args_from_tys(interner, alias.id.into(), iter::once(self).chain(args)); // FIXME: We don't handle GATs yet. let projection = Ty::new_alias( interner, @@ -5875,12 +5851,13 @@ impl<'db> Type<'db> { ); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - let ty = structurally_normalize_ty(&infcx, projection, self.env.param_env); - if ty.is_ty_error() { None } else { Some(self.derived(ty)) } + let ty = structurally_normalize_ty(&infcx, projection, env.param_env); + if ty.is_ty_error() { None } else { Some(Type { owner, ty: EarlyBinder::bind(ty) }) } } pub fn is_copy(&self, db: &'db dyn HirDatabase) -> bool { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let env = self.param_env(db); + let lang_items = hir_def::lang_item::lang_items(db, env.krate); let Some(copy_trait) = lang_items.Copy else { return false; }; @@ -5889,7 +5866,7 @@ impl<'db> Type<'db> { pub fn as_callable(&self, db: &'db dyn HirDatabase) -> Option> { let interner = DbInterner::new_no_crate(db); - let callee = match self.ty.kind() { + let callee = match self.ty.skip_binder().kind() { TyKind::Closure(id, subst) => Callee::Closure(id.0, subst), TyKind::CoroutineClosure(id, subst) => Callee::CoroutineClosure(id.0, subst), TyKind::FnPtr(..) => Callee::FnPtr, @@ -5897,7 +5874,9 @@ impl<'db> Type<'db> { // This will happen when it implements fn or fn mut, since we add an autoborrow adjustment TyKind::Ref(_, inner_ty, _) => return self.derived(inner_ty).as_callable(db), _ => { - let (fn_trait, sig) = hir_ty::callable_sig_from_fn_trait(self.ty, self.env, db)?; + let env = self.param_env(db); + let (fn_trait, sig) = + hir_ty::callable_sig_from_fn_trait(self.ty.skip_binder(), env, db)?; return Some(Callable { ty: self.clone(), sig, @@ -5907,74 +5886,77 @@ impl<'db> Type<'db> { } }; - let sig = self.ty.callable_sig(interner)?; + let sig = self.ty.skip_binder().callable_sig(interner)?; Some(Callable { ty: self.clone(), sig, callee, is_bound_method: false }) } pub fn is_closure(&self) -> bool { - matches!(self.ty.kind(), TyKind::Closure { .. }) + matches!(self.ty.skip_binder().kind(), TyKind::Closure { .. }) } pub fn as_closure(&self) -> Option> { - match self.ty.kind() { + match self.ty.skip_binder().kind() { TyKind::Closure(id, subst) => { - Some(Closure { id: AnyClosureId::ClosureId(id.0), subst }) - } - TyKind::CoroutineClosure(id, subst) => { - Some(Closure { id: AnyClosureId::CoroutineClosureId(id.0), subst }) + Some(Closure { id: AnyClosureId::ClosureId(id.0), subst, owner: self.owner }) } + TyKind::CoroutineClosure(id, subst) => Some(Closure { + id: AnyClosureId::CoroutineClosureId(id.0), + subst, + owner: self.owner, + }), _ => None, } } pub fn is_fn(&self) -> bool { - matches!(self.ty.kind(), TyKind::FnDef(..) | TyKind::FnPtr { .. }) + matches!(self.ty.skip_binder().kind(), TyKind::FnDef(..) | TyKind::FnPtr { .. }) } pub fn is_array(&self) -> bool { - matches!(self.ty.kind(), TyKind::Array(..)) + matches!(self.ty.skip_binder().kind(), TyKind::Array(..)) } - pub fn is_packed(&self, db: &'db dyn HirDatabase) -> bool { - let adt_id = match self.ty.kind() { - TyKind::Adt(adt_def, ..) => adt_def.def_id(), - _ => return false, - }; - - let adt = adt_id.into(); - match adt { - Adt::Struct(s) => s.repr(db).unwrap_or_default().pack.is_some(), + pub fn is_packed(&self, _db: &'db dyn HirDatabase) -> bool { + match self.ty.skip_binder().kind() { + TyKind::Adt(adt_def, ..) => adt_def.is_packed(), _ => false, } } pub fn is_raw_ptr(&self) -> bool { - matches!(self.ty.kind(), TyKind::RawPtr(..)) + matches!(self.ty.skip_binder().kind(), TyKind::RawPtr(..)) } pub fn is_mutable_raw_ptr(&self) -> bool { // Used outside of rust-analyzer (e.g. by `ra_ap_hir` consumers). - matches!(self.ty.kind(), TyKind::RawPtr(.., hir_ty::next_solver::Mutability::Mut)) + matches!( + self.ty.skip_binder().kind(), + TyKind::RawPtr(.., hir_ty::next_solver::Mutability::Mut) + ) } pub fn as_raw_ptr(&self) -> Option<(Type<'db>, Mutability)> { // Used outside of rust-analyzer (e.g. by `ra_ap_hir` consumers). - let TyKind::RawPtr(ty, m) = self.ty.kind() else { return None }; + let TyKind::RawPtr(ty, m) = self.ty.skip_binder().kind() else { return None }; let m = Mutability::from_mutable(matches!(m, hir_ty::next_solver::Mutability::Mut)); Some((self.derived(ty), m)) } pub fn remove_raw_ptr(&self) -> Option> { - if let TyKind::RawPtr(ty, _) = self.ty.kind() { Some(self.derived(ty)) } else { None } + if let TyKind::RawPtr(ty, _) = self.ty.skip_binder().kind() { + Some(self.derived(ty)) + } else { + None + } } pub fn contains_unknown(&self) -> bool { - self.ty.references_non_lt_error() + self.ty.skip_binder().references_non_lt_error() } pub fn fields(&self, db: &'db dyn HirDatabase) -> Vec<(Field, Self)> { let interner = DbInterner::new_no_crate(db); - let (variant_id, substs) = match self.ty.kind() { + let (variant_id, substs) = match self.ty.skip_binder().kind() { TyKind::Adt(adt_def, substs) => { let id = match adt_def.def_id() { AdtId::StructId(id) => id.into(), @@ -5997,7 +5979,7 @@ impl<'db> Type<'db> { } pub fn tuple_fields(&self, _db: &'db dyn HirDatabase) -> Vec { - if let TyKind::Tuple(substs) = self.ty.kind() { + if let TyKind::Tuple(substs) = self.ty.skip_binder().kind() { substs.iter().map(|ty| self.derived(ty)).collect() } else { Vec::new() @@ -6005,17 +5987,18 @@ impl<'db> Type<'db> { } pub fn as_array(&self, db: &'db dyn HirDatabase) -> Option<(Self, usize)> { - if let TyKind::Array(ty, len) = self.ty.kind() { + if let TyKind::Array(ty, len) = self.ty.skip_binder().kind() { try_const_usize(db, len).map(|it| (self.derived(ty), it as usize)) } else { None } } - pub fn fingerprint_for_trait_impl(&self) -> Option { + // FIXME: We should probably remove this. + pub fn fingerprint_for_trait_impl(&self, db: &'db dyn HirDatabase) -> Option { fast_reject::simplify_type( - DbInterner::conjure(), - self.ty, + DbInterner::new_no_crate(db), + self.ty.skip_binder(), fast_reject::TreatParams::AsRigid, ) } @@ -6031,9 +6014,10 @@ impl<'db> Type<'db> { fn autoderef_(&self, db: &'db dyn HirDatabase) -> impl Iterator> { let interner = DbInterner::new_no_crate(db); + let env = self.param_env(db); // There should be no inference vars in types passed here - let canonical = hir_ty::replace_errors_with_variables(interner, &self.ty); - autoderef(db, self.env, canonical) + let canonical = hir_ty::replace_errors_with_variables(interner, &self.ty.skip_binder()); + autoderef(db, env, canonical) } // This would be nicer if it just returned an iterator, but that runs into @@ -6065,17 +6049,20 @@ impl<'db> Type<'db> { } } }; + let krate = self.krate(db); let interner = DbInterner::new_no_crate(db); - let Some(simplified_type) = - fast_reject::simplify_type(interner, self.ty, fast_reject::TreatParams::AsRigid) - else { + let Some(simplified_type) = fast_reject::simplify_type( + interner, + self.ty.skip_binder(), + fast_reject::TreatParams::AsRigid, + ) else { return; }; method_resolution::with_incoherent_inherent_impls( db, - self.env.krate, + krate, &simplified_type, &mut handle_impls, ); @@ -6111,12 +6098,12 @@ impl<'db> Type<'db> { /// - "U" /// ``` pub fn type_arguments(&self) -> impl Iterator> + '_ { - match self.ty.strip_references().kind() { + match self.ty.skip_binder().strip_references().kind() { TyKind::Adt(_, substs) => Either::Left(substs.types().map(move |ty| self.derived(ty))), TyKind::Tuple(substs) => { Either::Right(Either::Left(substs.iter().map(move |ty| self.derived(ty)))) } - _ => Either::Right(Either::Right(std::iter::empty())), + _ => Either::Right(Either::Right(iter::empty())), } } @@ -6144,6 +6131,7 @@ impl<'db> Type<'db> { display_target: DisplayTarget, ) -> impl Iterator + 'a { self.ty + .skip_binder() .strip_references() .as_adt() .into_iter() @@ -6234,7 +6222,7 @@ impl<'db> Type<'db> { }; let infcx = interner.infer_ctxt().build(typing_mode); let features = resolver.top_level_def_map().features(); - let environment = param_env_from_resolver(db, resolver); + let environment = self.param_env(db); let ctx = MethodResolutionContext { infcx: &infcx, resolver, @@ -6269,7 +6257,8 @@ impl<'db> Type<'db> { self.with_method_resolution(db, scope.resolver(), traits_in_scope, |ctx| { // There should be no inference vars in types passed here - let canonical = hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty); + let canonical = + hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty.skip_binder()); let (self_ty, _) = ctx.infcx.instantiate_canonical(hir_ty::Span::Dummy, &canonical); match name { @@ -6377,7 +6366,8 @@ impl<'db> Type<'db> { self.with_method_resolution(db, scope.resolver(), traits_in_scope, |ctx| { // There should be no inference vars in types passed here - let canonical = hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty); + let canonical = + hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty.skip_binder()); let (self_ty, _) = ctx.infcx.instantiate_canonical(hir_ty::Span::Dummy, &canonical); match name { @@ -6426,23 +6416,23 @@ impl<'db> Type<'db> { } pub fn as_adt(&self) -> Option { - let (adt, _subst) = self.ty.as_adt()?; + let (adt, _subst) = self.ty.skip_binder().as_adt()?; Some(adt.into()) } /// Holes in the args can come from lifetime/const params. pub fn as_adt_with_args(&self) -> Option<(Adt, Vec>>)> { - let (adt, args) = self.ty.as_adt()?; + let (adt, args) = self.ty.skip_binder().as_adt()?; let args = args.iter().map(|arg| Some(self.derived(arg.ty()?))).collect(); Some((adt.into(), args)) } pub fn as_builtin(&self) -> Option { - self.ty.as_builtin().map(|inner| BuiltinType { inner }) + self.ty.skip_binder().as_builtin().map(|inner| BuiltinType { inner }) } pub fn as_dyn_trait(&self) -> Option { - self.ty.dyn_trait().map(Into::into) + self.ty.skip_binder().dyn_trait().map(Into::into) } /// If a type can be represented as `dyn Trait`, returns all traits accessible via this type, @@ -6461,11 +6451,11 @@ impl<'db> Type<'db> { pub fn env_traits(&self, db: &'db dyn HirDatabase) -> impl Iterator { let _p = tracing::info_span!("env_traits").entered(); + let env = self.param_env(db); self.autoderef_(db) .filter(|ty| matches!(ty.kind(), TyKind::Param(_))) - .flat_map(|ty| { - self.env - .param_env + .flat_map(move |ty| { + env.param_env .clauses() .iter() .filter_map(move |pred| match pred.kind().skip_binder() { @@ -6479,7 +6469,7 @@ impl<'db> Type<'db> { } pub fn as_impl_traits(&self, db: &'db dyn HirDatabase) -> Option> { - self.ty.impl_trait_bounds(db).map(|it| { + self.ty.skip_binder().impl_trait_bounds(db).map(|it| { it.into_iter().filter_map(|pred| match pred.kind().skip_binder() { ClauseKind::Trait(trait_ref) => Some(Trait::from(trait_ref.def_id().0)), _ => None, @@ -6489,7 +6479,7 @@ impl<'db> Type<'db> { pub fn as_associated_type_parent_trait(&self, db: &'db dyn HirDatabase) -> Option { let TyKind::Alias(AliasTy { kind: AliasTyKind::Projection { def_id }, .. }) = - self.ty.kind() + self.ty.skip_binder().kind() else { return None; }; @@ -6500,7 +6490,7 @@ impl<'db> Type<'db> { } fn derived(&self, ty: Ty<'db>) -> Self { - Type { env: self.env, ty } + Type { owner: self.owner, ty: EarlyBinder::bind(ty) } } /// Visits every type, including generic arguments, in this type. `callback` is called with type @@ -6508,7 +6498,7 @@ impl<'db> Type<'db> { pub fn walk(&self, db: &'db dyn HirDatabase, callback: impl FnMut(Type<'db>)) { struct Visitor<'db, F> { db: &'db dyn HirDatabase, - env: ParamEnvAndCrate<'db>, + owner: TypeOwnerId, callback: F, visited: FxHashSet>, } @@ -6523,7 +6513,7 @@ impl<'db> Type<'db> { return; } - (self.callback)(Type { env: self.env, ty }); + (self.callback)(Type { owner: self.owner, ty: EarlyBinder::bind(ty) }); if let Some(bounds) = ty.impl_trait_bounds(self.db) { bounds.visit_with(self); @@ -6533,17 +6523,23 @@ impl<'db> Type<'db> { } } - let mut visitor = Visitor { db, env: self.env, callback, visited: FxHashSet::default() }; - self.ty.visit_with(&mut visitor); + let mut visitor = + Visitor { db, owner: self.owner, callback, visited: FxHashSet::default() }; + self.ty.skip_binder().visit_with(&mut visitor); } /// Check if type unifies with another type. /// /// Note that we consider placeholder types to unify with everything. /// For example `Option` and `Option` unify although there is unresolved goal `T = U`. pub fn could_unify_with(&self, db: &'db dyn HirDatabase, other: &Type<'db>) -> bool { + self.owner.must_unify(other.owner); + let env = self.param_env(db); let interner = DbInterner::new_no_crate(db); - let tys = hir_ty::replace_errors_with_variables(interner, &(self.ty, other.ty)); - hir_ty::could_unify(db, self.env, &tys) + let tys = hir_ty::replace_errors_with_variables( + interner, + &(self.ty.skip_binder(), other.ty.skip_binder()), + ); + hir_ty::could_unify(db, env, &tys) } /// Check if type unifies with another type eagerly making sure there are no unresolved goals. @@ -6551,19 +6547,29 @@ impl<'db> Type<'db> { /// This means that placeholder types are not considered to unify if there are any bounds set on /// them. For example `Option` and `Option` do not unify as we cannot show that `T = U` pub fn could_unify_with_deeply(&self, db: &'db dyn HirDatabase, other: &Type<'db>) -> bool { + self.owner.must_unify(other.owner); + let env = self.param_env(db); let interner = DbInterner::new_no_crate(db); - let tys = hir_ty::replace_errors_with_variables(interner, &(self.ty, other.ty)); - hir_ty::could_unify_deeply(db, self.env, &tys) + let tys = hir_ty::replace_errors_with_variables( + interner, + &(self.ty.skip_binder(), other.ty.skip_binder()), + ); + hir_ty::could_unify_deeply(db, env, &tys) } pub fn could_coerce_to(&self, db: &'db dyn HirDatabase, to: &Type<'db>) -> bool { + self.owner.must_unify(to.owner); + let env = self.param_env(db); let interner = DbInterner::new_no_crate(db); - let tys = hir_ty::replace_errors_with_variables(interner, &(self.ty, to.ty)); - hir_ty::could_coerce(db, self.env, &tys) + let tys = hir_ty::replace_errors_with_variables( + interner, + &(self.ty.skip_binder(), to.ty.skip_binder()), + ); + hir_ty::could_coerce(db, env, &tys) } pub fn as_type_param(&self, _db: &'db dyn HirDatabase) -> Option { - match self.ty.kind() { + match self.ty.skip_binder().kind() { TyKind::Param(param) => Some(TypeParam { id: param.id }), _ => None, } @@ -6571,60 +6577,23 @@ impl<'db> Type<'db> { /// Returns unique `GenericParam`s contained in this type. pub fn generic_params(&self, db: &'db dyn HirDatabase) -> FxHashSet { - hir_ty::collect_params(&self.ty) + hir_ty::collect_params(&self.ty.skip_binder()) .into_iter() .map(|id| TypeOrConstParam { id }.split(db).either_into()) .collect() } pub fn layout(&self, db: &'db dyn HirDatabase) -> Result, LayoutError> { - db.layout_of_ty(self.ty.store(), self.env.store()) - .map(|layout| Layout(layout, db.target_data_layout(self.env.krate).unwrap())) + let env = self.param_env(db); + db.layout_of_ty(self.ty.skip_binder().store(), env.store()) + .map(|layout| Layout(layout, db.target_data_layout(env.krate).unwrap())) } pub fn drop_glue(&self, db: &'db dyn HirDatabase) -> DropGlue { - let interner = DbInterner::new_with(db, self.env.krate); + let env = self.param_env(db); + let interner = DbInterner::new_with(db, env.krate); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - hir_ty::drop::has_drop_glue(&infcx, self.ty, self.env.param_env) - } -} - -#[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub struct TypeNs<'db> { - env: ParamEnvAndCrate<'db>, - ty: Ty<'db>, -} - -impl<'db> TypeNs<'db> { - fn new(db: &'db dyn HirDatabase, lexical_env: impl HasResolver, ty: Ty<'db>) -> Self { - let resolver = lexical_env.resolver(db); - let environment = param_env_from_resolver(db, &resolver); - TypeNs { env: environment, ty } - } - - pub fn to_type(&self, _db: &'db dyn HirDatabase) -> Type<'db> { - Type { env: self.env, ty: self.ty } - } - - // FIXME: Find better API that also handles const generics - pub fn impls_trait(&self, infcx: InferCtxt<'db>, trait_: Trait, args: &[TypeNs<'db>]) -> bool { - let args = GenericArgs::new_from_iter( - infcx.interner, - [self.ty].into_iter().chain(args.iter().map(|t| t.ty)).map(GenericArg::from), - ); - let trait_ref = - hir_ty::next_solver::TraitRef::new_from_args(infcx.interner, trait_.id.into(), args); - let obligation = hir_ty::next_solver::infer::traits::Obligation::new( - infcx.interner, - hir_ty::next_solver::infer::traits::ObligationCause::dummy(), - self.env.param_env, - trait_ref, - ); - infcx.predicate_must_hold_modulo_regions(&obligation) - } - - pub fn is_bool(&self) -> bool { - matches!(self.ty.kind(), rustc_type_ir::TyKind::Bool) + hir_ty::drop::has_drop_glue(&infcx, self.ty.skip_binder(), env.param_env) } } @@ -6689,12 +6658,16 @@ impl<'db> Callable<'db> { Callee::Def(CallableDefId::EnumVariantId(it)) => { CallableKind::TupleEnumVariant(it.into()) } - Callee::Closure(id, subst) => { - CallableKind::Closure(Closure { id: AnyClosureId::ClosureId(id), subst }) - } - Callee::CoroutineClosure(id, subst) => { - CallableKind::Closure(Closure { id: AnyClosureId::CoroutineClosureId(id), subst }) - } + Callee::Closure(id, subst) => CallableKind::Closure(Closure { + id: AnyClosureId::ClosureId(id), + subst, + owner: self.ty.owner, + }), + Callee::CoroutineClosure(id, subst) => CallableKind::Closure(Closure { + id: AnyClosureId::CoroutineClosureId(id), + subst, + owner: self.ty.owner, + }), Callee::FnPtr => CallableKind::FnPtr, Callee::FnImpl(fn_) => CallableKind::FnImpl(fn_.into()), } @@ -6835,7 +6808,7 @@ impl<'db> Layout<'db> { reverse_index .into_iter() .flatten() - .chain(std::iter::once((0, self.0.size.bytes()))) + .chain(iter::once((0, self.0.size.bytes()))) .tuple_windows() .filter_map(|((i, start), (_, end))| { let size = field_size(i)?; @@ -6993,7 +6966,7 @@ pub enum PredicatePolarity { #[derive(Debug, Clone, PartialEq, Eq)] pub struct TraitPredicate<'db> { inner: hir_ty::next_solver::TraitPredicate<'db>, - env: ParamEnvAndCrate<'db>, + owner: TypeOwnerId, } impl<'db> TraitPredicate<'db> { @@ -7005,7 +6978,7 @@ impl<'db> TraitPredicate<'db> { } pub fn trait_ref(&self) -> TraitRef<'db> { - TraitRef { env: self.env, trait_ref: self.inner.trait_ref } + TraitRef { owner: self.owner, trait_ref: self.inner.trait_ref } } } @@ -7075,8 +7048,8 @@ impl HasCrate for TypeAlias { } impl HasCrate for Type<'_> { - fn krate(&self, _db: &dyn HirDatabase) -> Crate { - self.env.krate.into() + fn krate(&self, db: &dyn HirDatabase) -> Crate { + self.krate(db).into() } } @@ -7395,21 +7368,33 @@ fn as_name_opt(name: Option) -> Name { name.map_or_else(Name::missing, |name| name.as_name()) } +#[track_caller] fn generic_args_from_tys<'db>( interner: DbInterner<'db>, def_id: SolverDefId, - args: impl IntoIterator>, -) -> GenericArgs<'db> { + args: impl IntoIterator>>, +) -> (GenericArgs<'db>, TypeOwnerId) { + let mut owner = None::; let mut args = args.into_iter(); - GenericArgs::for_item(interner, def_id, |_, id, _| { + let args = GenericArgs::for_item(interner, def_id, |_, id, _| { if matches!(id, GenericParamId::TypeParamId(_)) && let Some(arg) = args.next() { - arg.into() + let arg = arg.borrow(); + + match &mut owner { + Some(owner) => *owner = owner.must_unify(arg.owner), + None => owner = Some(arg.owner), + } + + arg.ty.skip_binder().into() } else { next_solver::GenericArg::error_from_id(interner, id) } - }) + }); + let owner = + owner.unwrap_or_else(|| TypeOwnerId::NoParams(Type::builtin_type_crate(interner.db()))); + (args, owner) } fn has_non_default_type_params(db: &dyn HirDatabase, generic_def: GenericDefId) -> bool { @@ -7425,46 +7410,58 @@ fn has_non_default_type_params(db: &dyn HirDatabase, generic_def: GenericDefId) }) } -fn param_env_from_resolver<'db>( - db: &'db dyn HirDatabase, - resolver: &Resolver<'_>, -) -> ParamEnvAndCrate<'db> { - ParamEnvAndCrate { - param_env: resolver - .generic_def() - .map_or_else(ParamEnv::empty, |generic_def| db.trait_environment(generic_def.into())), - krate: resolver.krate(), - } -} - fn param_env_from_has_crate<'db>( db: &'db dyn HirDatabase, id: impl hir_def::HasModule + Into + Copy, -) -> ParamEnvAndCrate<'db> { - ParamEnvAndCrate { param_env: db.trait_environment(id.into().into()), krate: id.krate(db) } -} - -fn body_param_env_from_has_crate<'db>( - db: &'db dyn HirDatabase, - id: impl hir_def::HasModule + Into + Copy, ) -> ParamEnvAndCrate<'db> { ParamEnvAndCrate { param_env: db.trait_environment(id.into()), krate: id.krate(db) } } -fn empty_param_env<'db>(krate: base_db::Crate) -> ParamEnvAndCrate<'db> { - ParamEnvAndCrate { param_env: ParamEnv::empty(), krate } -} - // FIXME: We probably don't want to expose this. pub trait MacroCallIdExt { - fn loc(self, db: &dyn HirDatabase) -> hir_expand::MacroCallLoc; + fn loc(self, db: &dyn HirDatabase) -> &hir_expand::MacroCallLoc; } impl MacroCallIdExt for span::MacroCallId { #[inline] - fn loc(self, db: &dyn HirDatabase) -> hir_expand::MacroCallLoc { + fn loc(self, db: &dyn HirDatabase) -> &hir_expand::MacroCallLoc { hir_expand::MacroCallId::from(self).loc(db) } } -pub use hir_ty::next_solver; -pub use hir_ty::setup_tracing; +// Like https://github.com/rust-lang/rust/blob/7c3c88f42ad444f4688b865591d84660be4ece2f/compiler/rustc_middle/src/ty/util.rs#L254-L310 +pub fn struct_tail_raw<'db>( + db: &'db dyn HirDatabase, + interner: DbInterner<'db>, + mut ty: Ty<'db>, + mut normalize: impl FnMut(Ty<'db>) -> Ty<'db>, +) -> Ty<'db> { + let recursion_limit = 16; + for iteration in 0.. { + if iteration >= recursion_limit { + return Ty::new_error(interner, ErrorGuaranteed); + } + match ty.kind() { + TyKind::Adt(def, args) => { + let AdtId::StructId(def_id) = def.def_id() else { break }; + let last_field = db.field_types(def_id.into()).iter().next_back(); + match last_field { + Some((_, field)) => { + ty = normalize(field.get().instantiate(interner, args).skip_norm_wip()) + } + None => break, + } + } + TyKind::Tuple(tys) if let Some((&last_ty, _)) = tys.split_last() => { + ty = last_ty; + } + TyKind::Tuple(_) => break, + TyKind::Pat(inner, _) => { + ty = inner; + } + _ => { + break; + } + } + } + ty +} diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index a1bbe47188b46..f633bb063fdd2 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1088,7 +1088,12 @@ impl<'db> SemanticsImpl<'db> { /// That is, we strictly check if it lies inside the input of a macro call. pub fn is_inside_macro_call(&self, token @ InFile { value, .. }: InFile<&SyntaxToken>) -> bool { value.parent_ancestors().any(|ancestor| { - if ast::MacroCall::can_cast(ancestor.kind()) { + if let Some(macro_call) = ast::MacroCall::cast(ancestor.clone()) + // If this is the *path* of a macro, it's not inside the call. + && macro_call.path().is_none_or(|path| { + !path.syntax().text_range().contains_range(value.text_range()) + }) + { return true; } @@ -1323,7 +1328,7 @@ impl<'db> SemanticsImpl<'db> { .map(|(call_id, item)| { let item_range = item.syntax().text_range(); let loc = call_id.loc(db); - let text_range = match loc.kind { + let text_range = match &loc.kind { hir_expand::MacroCallKind::Attr { censored_attr_ids: attr_ids, .. @@ -1740,11 +1745,7 @@ impl<'db> SemanticsImpl<'db> { analyzer.expr_adjustments(expr).map(|it| { it.iter() .map(|adjust| { - let target = Type::new_with_resolver( - self.db, - &analyzer.resolver, - adjust.target.as_ref(), - ); + let target = analyzer.ty(adjust.target.as_ref()); let kind = match adjust.kind { hir_ty::Adjust::NeverToAny => Adjust::NeverToAny, hir_ty::Adjust::Deref(Some(hir_ty::OverloadedDeref(m))) => { @@ -1771,6 +1772,10 @@ impl<'db> SemanticsImpl<'db> { }) } + pub fn expr_is_diverging(&self, expr: &ast::Expr) -> bool { + (|| self.analyze(expr.syntax())?.expr_is_diverging(self.db, expr))().unwrap_or(false) + } + pub fn type_of_expr(&self, expr: &ast::Expr) -> Option> { self.analyze(expr.syntax())? .type_of_expr(self.db, expr) @@ -1835,10 +1840,10 @@ impl<'db> SemanticsImpl<'db> { let substs = hir_ty::next_solver::GenericArgs::for_item(interner, trait_.id.into(), |_, id, _| { assert!(matches!(id, hir_def::GenericParamId::TypeParamId(_)), "expected a type"); - subst.next().expect("too few subst").ty.into() + subst.next().expect("too few subst").ty.skip_binder().into() }); assert!(subst.next().is_none(), "too many subst"); - Some(match self.db.lookup_impl_method(env.env, func, substs).0 { + Some(match self.db.lookup_impl_method(env.param_env(self.db), func, substs).0 { Either::Left(it) => it.into(), Either::Right((impl_, method)) => { Function { id: AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } } @@ -1946,6 +1951,15 @@ impl<'db> SemanticsImpl<'db> { self.analyze(field.syntax())?.resolve_record_pat_field(self.db, field) } + // FIXME: Remove this from https://github.com/rust-lang/rust-analyzer/pull/22449#discussion_r3299763452 + pub fn resolve_tuple_struct_pat_fields( + &self, + tuple_struct_pat: &ast::TupleStructPat, + ) -> Option)>> { + self.analyze(tuple_struct_pat.syntax())? + .resolve_tuple_struct_pat_fields(self.db, tuple_struct_pat) + } + // FIXME: Replace this with `resolve_macro_call2` pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option { let macro_call = self.find_file(macro_call.syntax()).with_value(macro_call); @@ -2436,7 +2450,7 @@ impl<'db> SemanticsImpl<'db> { AnyImplId::ImplId(id) => id, AnyImplId::BuiltinDeriveImplId(id) => return Some(id.loc(self.db).adt.into()), }; - let source = hir_def::src::HasSource::ast_ptr(&id.loc(self.db), self.db); + let source = hir_def::src::HasSource::ast_ptr(id.loc(self.db), self.db); let mut file_id = source.file_id; let adt_ast_id = loop { let macro_call = file_id.macro_file()?; diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs index babeb3591345c..97c5a451ab6b8 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs @@ -202,7 +202,7 @@ impl ChildBySource for EnumId { let ast_id_map = db.ast_id_map(loc.id.file_id); - self.enum_variants(db).variants.iter().for_each(|&(variant, _, _)| { + self.enum_variants(db).variants.values().for_each(|&(variant, _)| { res[keys::ENUM_VARIANT].insert(ast_id_map.get(variant.lookup(db).id.value), variant); }); let (_, source_map) = EnumSignature::with_source_map(db, *self); 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 06182620c86e2..1f9520d780f03 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -41,8 +41,8 @@ use hir_ty::{ lang_items::lang_items_for_bin_op, method_resolution::{self, CandidateId}, next_solver::{ - AliasTy, DbInterner, DefaultAny, ErrorGuaranteed, GenericArgs, ParamEnv, Region, Ty, - TyKind, TypingMode, infer::DbInternerInferExt, + AliasTy, DbInterner, DefaultAny, EarlyBinder, ErrorGuaranteed, GenericArgs, ParamEnv, + Region, Ty, TyKind, TypingMode, infer::DbInternerInferExt, }, traits::structurally_normalize_ty, }; @@ -63,7 +63,7 @@ use syntax::{ use crate::{ Adt, AnyFunctionId, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const, DeriveHelper, EnumVariant, Field, Function, GenericSubstitution, Local, Macro, ModuleDef, - SemanticsImpl, Static, Struct, ToolModule, Trait, TupleField, Type, TypeAlias, + SemanticsImpl, Static, Struct, ToolModule, Trait, TupleField, Type, TypeAlias, TypeOwnerId, db::HirDatabase, semantics::{PathResolution, PathResolutionPerNs}, }; @@ -75,6 +75,7 @@ pub(crate) struct SourceAnalyzer<'db> { pub(crate) file_id: HirFileId, pub(crate) resolver: Resolver<'db>, pub(crate) body_or_sig: Option>, + pub(crate) type_owner: TypeOwnerId, pub(crate) infer_body: Option, } @@ -148,6 +149,7 @@ impl<'db> SourceAnalyzer<'db> { resolver, body_or_sig: Some(BodyOrSig::Body { def, body, source_map, infer }), file_id, + type_owner: def.generic_def(db).into(), infer_body: Some(def.into()), } } @@ -212,6 +214,7 @@ impl<'db> SourceAnalyzer<'db> { resolver, body_or_sig: Some(BodyOrSig::Sig { def, store, source_map, generics, infer }), file_id, + type_owner: def.into(), infer_body, } } @@ -261,6 +264,7 @@ impl<'db> SourceAnalyzer<'db> { infer, }), file_id, + type_owner: GenericDefId::from(def.adt_id(db)).into(), infer_body, } } @@ -269,7 +273,16 @@ impl<'db> SourceAnalyzer<'db> { resolver: Resolver<'db>, node: InFile<&SyntaxNode>, ) -> SourceAnalyzer<'db> { - SourceAnalyzer { resolver, body_or_sig: None, file_id: node.file_id, infer_body: None } + SourceAnalyzer { + type_owner: resolver + .generic_def() + .map(Into::into) + .unwrap_or_else(|| TypeOwnerId::NoParams(resolver.krate())), + resolver, + body_or_sig: None, + file_id: node.file_id, + infer_body: None, + } } fn owner(&self) -> Option { @@ -288,6 +301,10 @@ impl<'db> SourceAnalyzer<'db> { }) } + pub(crate) fn ty(&self, ty: Ty<'db>) -> Type<'db> { + Type { owner: self.type_owner, ty: EarlyBinder::bind(ty) } + } + pub(crate) fn def( &self, ) -> Option<( @@ -330,13 +347,21 @@ impl<'db> SourceAnalyzer<'db> { } fn trait_environment(&self, db: &'db dyn HirDatabase) -> ParamEnvAndCrate<'db> { - self.param_and(self.body_or_sig.as_ref().map_or_else(ParamEnv::empty, |body_or_sig| { - match *body_or_sig { - BodyOrSig::Body { def, .. } => db.trait_environment(def.into()), - BodyOrSig::VariantFields { def, .. } => db.trait_environment(def.into()), - BodyOrSig::Sig { def, .. } => db.trait_environment(def.into()), - } - })) + self.param_and(self.body_or_sig.as_ref().map_or_else( + || ParamEnv::empty(DbInterner::new_no_crate(db)), + |body_or_sig| { + let def = match *body_or_sig { + BodyOrSig::Body { def, .. } => def.generic_def(db), + BodyOrSig::VariantFields { def, .. } => match def { + VariantId::EnumVariantId(def) => def.loc(db).parent.into(), + VariantId::StructId(def) => def.into(), + VariantId::UnionId(def) => def.into(), + }, + BodyOrSig::Sig { def, .. } => def, + }; + db.trait_environment(def) + }, + )) } pub(crate) fn expr_id(&self, expr: ast::Expr) -> Option { @@ -418,12 +443,41 @@ impl<'db> SourceAnalyzer<'db> { } } - Some(Type::new_with_resolver(db, &self.resolver, ty)) + Some(self.ty(ty)) + } + + pub(crate) fn expr_is_diverging( + &self, + _db: &'db dyn HirDatabase, + expr: &ast::Expr, + ) -> Option { + let expr_id = self.expr_id(expr.clone())?; + let store = self.store()?; + let infer = self.infer()?; + Some(self.expr_id_is_diverging(store, infer, expr_id)) + } + + fn expr_id_is_diverging( + &self, + store: &ExpressionStore, + infer: &InferenceResult, + expr_id: ExprOrPatId, + ) -> bool { + // FIXME: This is an approximation, perhaps we need to store a set of diverging exprs in inference? + if infer.type_of_expr_or_pat(expr_id).is_some_and(|ty| ty.is_never()) { + true + } else if let ExprOrPatId::ExprId(expr_id) = expr_id + && let Expr::Block { tail: Some(tail), .. } = store[expr_id] + { + self.expr_id_is_diverging(store, infer, tail.into()) + } else { + false + } } pub(crate) fn type_of_expr( &self, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, expr: &ast::Expr, ) -> Option<(Type<'db>, Option>)> { let expr_id = self.expr_id(expr.clone())?; @@ -433,13 +487,13 @@ impl<'db> SourceAnalyzer<'db> { .and_then(|expr_id| infer.expr_adjustment(expr_id)) .and_then(|adjusts| adjusts.last().map(|adjust| adjust.target.as_ref())); let ty = infer.expr_or_pat_ty(expr_id); - let mk_ty = |ty: Ty<'db>| Type::new_with_resolver(db, &self.resolver, ty); + let mk_ty = |ty: Ty<'db>| self.ty(ty); Some((mk_ty(ty), coerced.map(mk_ty))) } pub(crate) fn type_of_pat( &self, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, pat: &ast::Pat, ) -> Option<(Type<'db>, Option>)> { let expr_or_pat_id = self.pat_id(pat)?; @@ -456,25 +510,25 @@ impl<'db> SourceAnalyzer<'db> { }; let ty = infer.expr_or_pat_ty(expr_or_pat_id); - let mk_ty = |ty: Ty<'db>| Type::new_with_resolver(db, &self.resolver, ty); + let mk_ty = |ty: Ty<'db>| self.ty(ty); Some((mk_ty(ty), coerced.map(mk_ty))) } pub(crate) fn type_of_binding_in_pat( &self, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, pat: &ast::IdentPat, ) -> Option> { let binding_id = self.binding_id_of_pat(pat)?; let infer = self.infer()?; let ty = infer.binding_ty(binding_id); - let mk_ty = |ty: Ty<'db>| Type::new_with_resolver(db, &self.resolver, ty); + let mk_ty = |ty: Ty<'db>| self.ty(ty); Some(mk_ty(ty)) } pub(crate) fn type_of_self( &self, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, _param: &ast::SelfParam, ) -> Option> { let binding = match self.body_or_sig.as_ref()? { @@ -482,7 +536,7 @@ impl<'db> SourceAnalyzer<'db> { BodyOrSig::Body { body, .. } => body.self_param()?, }; let ty = self.infer()?.binding_ty(binding); - Some(Type::new_with_resolver(db, &self.resolver, ty)) + Some(self.ty(ty)) } pub(crate) fn binding_mode_of_pat( @@ -504,7 +558,7 @@ impl<'db> SourceAnalyzer<'db> { } pub(crate) fn pattern_adjustments( &self, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, pat: &ast::Pat, ) -> Option; 1]>> { let pat_id = self.pat_id(pat)?; @@ -513,7 +567,7 @@ impl<'db> SourceAnalyzer<'db> { infer .pat_adjustment(pat_id.as_pat()?)? .iter() - .map(|adjust| Type::new_with_resolver(db, &self.resolver, adjust.source.as_ref())) + .map(|adjust| self.ty(adjust.source.as_ref())) .collect(), ) } @@ -527,7 +581,7 @@ impl<'db> SourceAnalyzer<'db> { let (func, args) = self.infer()?.method_resolution(expr_id)?; let interner = DbInterner::new_no_crate(db); let ty = db.value_ty(func.into())?.instantiate(interner, args).skip_norm_wip(); - let ty = Type::new_with_resolver(db, &self.resolver, ty); + let ty = self.ty(ty); let mut res = ty.as_callable(db)?; res.is_bound_method = true; Some(res) @@ -557,7 +611,7 @@ impl<'db> SourceAnalyzer<'db> { self.resolve_impl_method_or_trait_def_with_subst(db, f_in_trait, substs); Some(( Either::Left(fn_), - GenericSubstitution::new_from_fn(fn_, subst, self.trait_environment(db)), + GenericSubstitution::new_from_fn(fn_, subst, self.type_owner), )) } None => { @@ -592,12 +646,12 @@ impl<'db> SourceAnalyzer<'db> { &self, field_expr: ExprId, infer: &InferenceResult, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, ) -> Option> { let body = self.store()?; if let Expr::Field { expr: object_expr, name: _ } = body[field_expr] { let (adt, subst) = infer.type_of_expr_with_adjust(object_expr)?.as_adt()?; - return Some(GenericSubstitution::new(adt.into(), subst, self.trait_environment(db))); + return Some(GenericSubstitution::new(adt.into(), subst, self.type_owner)); } None } @@ -628,10 +682,7 @@ impl<'db> SourceAnalyzer<'db> { }, None => inference_result.method_resolution(expr_id).map(|(f, substs)| { let (f, subst) = self.resolve_impl_method_or_trait_def_with_subst(db, f, substs); - ( - Either::Right(f), - GenericSubstitution::new_from_fn(f, subst, self.trait_environment(db)), - ) + (Either::Right(f), GenericSubstitution::new_from_fn(f, subst, self.type_owner)) }), } } @@ -721,7 +772,7 @@ impl<'db> SourceAnalyzer<'db> { .map(Trait::from); if let Some(into_future_trait) = into_future_trait { - let type_ = Type::new_with_resolver(db, &self.resolver, ty); + let type_ = self.ty(ty); if type_.impls_trait(db, into_future_trait, &[]) { let items = into_future_trait.items(db); let into_future_type = items.into_iter().find_map(|item| match item { @@ -733,7 +784,7 @@ impl<'db> SourceAnalyzer<'db> { _ => None, })?; let future_trait = type_.normalize_trait_assoc_type(db, &[], into_future_type)?; - ty = future_trait.ty; + ty = future_trait.ty.skip_binder(); } } @@ -882,8 +933,8 @@ impl<'db> SourceAnalyzer<'db> { Some(( field.into(), local, - Type::new_with_resolver(db, &self.resolver, field_ty), - GenericSubstitution::new(adt.into(), subst, self.trait_environment(db)), + self.ty(field_ty), + GenericSubstitution::new(adt.into(), subst, self.type_owner), )) } @@ -906,11 +957,33 @@ impl<'db> SourceAnalyzer<'db> { .skip_norm_wip(); Some(( field.into(), - Type::new_with_resolver(db, &self.resolver, field_ty), - GenericSubstitution::new(adt.into(), subst, self.trait_environment(db)), + self.ty(field_ty), + GenericSubstitution::new(adt.into(), subst, self.type_owner), )) } + pub(crate) fn resolve_tuple_struct_pat_fields( + &self, + db: &'db dyn HirDatabase, + tuple_struct_pat: &ast::TupleStructPat, + ) -> Option)>> { + let interner = DbInterner::new_no_crate(db); + let pat_id = self.pat_id(&tuple_struct_pat.clone().into())?; + let variant_id = self.infer()?.variant_resolution_for_pat(pat_id.as_pat()?)?; + let (_adt, substs) = self.infer()?.pat_ty(pat_id.as_pat()?).as_adt()?; + + Some( + db.field_types(variant_id) + .iter() + .map(|(local_id, ty)| { + let def = Field { parent: variant_id.into(), id: local_id }; + let ty = ty.get().instantiate(interner, substs).skip_norm_wip(); + (def, self.ty(ty)) + }) + .collect(), + ) + } + pub(crate) fn resolve_bind_pat_to_const( &self, db: &'db dyn HirDatabase, @@ -962,15 +1035,15 @@ impl<'db> SourceAnalyzer<'db> { let container = offset_of_expr.ty()?; let container = self.type_of_type(db, &container)?; - let trait_env = container.env; + let env = self.trait_environment(db); - let interner = DbInterner::new_with(db, trait_env.krate); + let interner = DbInterner::new_with(db, env.krate); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - let mut container = Either::Right(container.ty); + let mut container = Either::Right(container.ty.skip_binder()); for field_name in offset_of_expr.fields() { if let Either::Right(container) = &mut container { - *container = structurally_normalize_ty(&infcx, *container, trait_env.param_env); + *container = structurally_normalize_ty(&infcx, *container, env.param_env); } let handle_variants = |variant: VariantId, subst: GenericArgs<'db>, container: &mut _| { @@ -1017,7 +1090,10 @@ impl<'db> SourceAnalyzer<'db> { }; if field_name.syntax().text_range() == name_ref.syntax().text_range() { - return Some((field_def, GenericSubstitution::new(generic_def, subst, trait_env))); + return Some(( + field_def, + GenericSubstitution::new(generic_def, subst, self.type_owner), + )); } } never!("the `NameRef` is a child of the `OffsetOfExpr`, we should've visited it"); @@ -1045,7 +1121,7 @@ impl<'db> SourceAnalyzer<'db> { let subst = GenericSubstitution::new( f_in_trait.into(), subs, - self.trait_environment(db), + self.type_owner, ); (AssocItem::Function(f_in_trait.into()), Some(subst)) } @@ -1058,14 +1134,14 @@ impl<'db> SourceAnalyzer<'db> { let subst = GenericSubstitution::new_from_fn( fn_, subst, - self.trait_environment(db), + self.type_owner, ); (AssocItem::Function(fn_), subst) } else { let subst = GenericSubstitution::new( f_in_trait.into(), subs, - self.trait_environment(db), + self.type_owner, ); (AssocItem::Function(f_in_trait.into()), Some(subst)) } @@ -1075,11 +1151,8 @@ impl<'db> SourceAnalyzer<'db> { CandidateId::ConstId(const_id) => { let (konst, subst) = self.resolve_impl_const_or_trait_def_with_subst(db, const_id, subs); - let subst = GenericSubstitution::new( - konst.into(), - subst, - self.trait_environment(db), - ); + let subst = + GenericSubstitution::new(konst.into(), subst, self.type_owner); (AssocItem::Const(konst.into()), Some(subst)) } }; @@ -1103,20 +1176,13 @@ impl<'db> SourceAnalyzer<'db> { CandidateId::ConstId(const_id) => { let (konst, subst) = self.resolve_impl_const_or_trait_def_with_subst(db, const_id, subs); - let subst = GenericSubstitution::new( - konst.into(), - subst, - self.trait_environment(db), - ); + let subst = + GenericSubstitution::new(konst.into(), subst, self.type_owner); (AssocItemId::from(konst), subst) } CandidateId::FunctionId(function_id) => ( function_id.into(), - GenericSubstitution::new( - function_id.into(), - subs, - self.trait_environment(db), - ), + GenericSubstitution::new(function_id.into(), subs, self.type_owner), ), }; return Some((PathResolution::Def(AssocItem::from(assoc).into()), Some(subst))); @@ -1320,12 +1386,11 @@ impl<'db> SourceAnalyzer<'db> { } else { return None; }; - let env = self.trait_environment(db); let (subst, expected_resolution) = match ty.kind() { TyKind::Adt(adt_def, subst) => { let adt_id = adt_def.def_id(); ( - GenericSubstitution::new(adt_id.into(), subst, env), + GenericSubstitution::new(adt_id.into(), subst, self.type_owner), PathResolution::Def(ModuleDef::Adt(adt_id.into())), ) } @@ -1336,7 +1401,7 @@ impl<'db> SourceAnalyzer<'db> { }) => { let assoc_id = def_id.0; ( - GenericSubstitution::new(assoc_id.into(), args, env), + GenericSubstitution::new(assoc_id.into(), args, self.type_owner), PathResolution::Def(ModuleDef::TypeAlias(assoc_id.into())), ) } @@ -1347,7 +1412,7 @@ impl<'db> SourceAnalyzer<'db> { CallableDefId::EnumVariantId(_) => return None, }; ( - GenericSubstitution::new(generic_def_id, subst, env), + GenericSubstitution::new(generic_def_id, subst, self.type_owner), PathResolution::Def(ModuleDefId::from(fn_id.0).into()), ) } @@ -1466,7 +1531,7 @@ impl<'db> SourceAnalyzer<'db> { .map(|local_id| { let field = FieldId { parent: variant, local_id }; let ty = field_types[local_id].get().instantiate(interner, substs).skip_norm_wip(); - (field.into(), Type::new_with_resolver_inner(db, &self.resolver, ty)) + (field.into(), self.ty(ty)) }) .collect() } @@ -1589,7 +1654,7 @@ impl<'db> SourceAnalyzer<'db> { func: FunctionId, substs: GenericArgs<'db>, ) -> (Function, GenericArgs<'db>) { - let owner = match self.resolver.expression_store_owner() { + let owner = match self.resolver.generic_def() { Some(it) => it, None => return (func.into(), substs), }; @@ -1609,7 +1674,7 @@ impl<'db> SourceAnalyzer<'db> { const_id: ConstId, subs: GenericArgs<'db>, ) -> (ConstId, GenericArgs<'db>) { - let owner = match self.resolver.expression_store_owner() { + let owner = match self.resolver.generic_def() { Some(it) => it, None => return (const_id, subs), }; diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index ff56544d82e01..c4040c1c0099b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -190,8 +190,8 @@ impl<'a> SymbolCollector<'a> { let enum_name = Symbol::intern(EnumSignature::of(this.db, id).name.as_str()); 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); + for (variant_name, (variant_id, _)) in &variants.variants { + this.push_decl(*variant_id, variant_name, false, None); } }); } @@ -401,7 +401,7 @@ impl<'a> SymbolCollector<'a> { fn collect_from_impl(&mut self, impl_id: ImplId) { let impl_data = ImplSignature::of(self.db, impl_id); let impl_name = Some( - hir_display_with_store(impl_data.self_ty, &impl_data.store) + hir_display_with_store(impl_data.self_ty, impl_id.into(), &impl_data.store) .display( self.db, crate::Impl::from(impl_id).krate(self.db).to_display_target(self.db), diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search.rs b/src/tools/rust-analyzer/crates/hir/src/term_search.rs index af2371d49349d..1cc6766bfb3b4 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search.rs @@ -145,7 +145,7 @@ impl<'db> LookupTable<'db> { self.data .iter() .find(|(t, _)| { - t.add_reference(Mutability::Shared).could_unify_with_deeply(db, ty) + t.add_reference(db, Mutability::Shared).could_unify_with_deeply(db, ty) }) .map(|(t, it)| { it.exprs(t) diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs index e3d0121e49123..a824c8bdca5d9 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs @@ -310,21 +310,18 @@ impl<'db> Expr<'db> { Expr::Local(it) => it.ty(db), Expr::ConstParam(it) => it.ty(db), Expr::FamousType { ty, .. } => ty.clone(), - Expr::Function { func, generics, .. } => { - func.ret_type_with_args(db, generics.iter().cloned()) - } - Expr::Method { func, generics, target, .. } => func.ret_type_with_args( - db, - target.ty(db).type_arguments().chain(generics.iter().cloned()), - ), + Expr::Function { func, generics, .. } => func.ret_type(db).instantiate(generics), + Expr::Method { func, generics, target, .. } => func + .ret_type(db) + .instantiate(target.ty(db).type_arguments().chain(generics.iter().cloned())), Expr::Variant { variant, generics, .. } => { - Adt::from(variant.parent_enum(db)).ty_with_args(db, generics.iter().cloned()) + Adt::from(variant.parent_enum(db)).ty(db).instantiate(generics) } Expr::Struct { strukt, generics, .. } => { - Adt::from(*strukt).ty_with_args(db, generics.iter().cloned()) + Adt::from(*strukt).ty(db).instantiate(generics) } Expr::Tuple { ty, .. } => ty.clone(), - Expr::Field { expr, field } => field.ty_with_args(db, expr.ty(db).type_arguments()), + Expr::Field { expr, field } => field.ty(db).instantiate(expr.ty(db).type_arguments()), Expr::Reference(it) => it.ty(db), Expr::Many(ty) => ty.clone(), } diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs index 8700326e17b37..2b7f7da3bf0d3 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs @@ -10,18 +10,13 @@ use std::iter; -use hir_ty::{ - db::HirDatabase, - mir::BorrowKind, - next_solver::{DbInterner, Ty}, -}; +use hir_ty::{db::HirDatabase, mir::BorrowKind}; use itertools::Itertools; use rustc_hash::FxHashSet; -use rustc_type_ir::inherent::Ty as _; use crate::{ - Adt, AssocItem, GenericDef, GenericParam, HasAttrs, HasVisibility, Impl, ModuleDef, ScopeDef, - Type, TypeParam, term_search::Expr, + Adt, AssocItem, BuiltinType, GenericDef, GenericParam, HasAttrs, HasVisibility, Impl, + ModuleDef, ScopeDef, Type, TypeParam, term_search::Expr, }; use super::{LookupTable, NewTypesKey, TermSearchCtx}; @@ -87,7 +82,7 @@ pub(super) fn trivial<'a, 'lt, 'db, DB: HirDatabase>( return None; } - ty.could_unify_with_deeply(db, &ctx.goal).then_some(expr) + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(expr) }) } @@ -137,7 +132,7 @@ pub(super) fn assoc_const<'a, 'lt, 'db, DB: HirDatabase>( lookup.insert(ty.clone(), std::iter::once(expr.clone())); - ty.could_unify_with_deeply(db, &ctx.goal).then_some(expr) + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(expr) }) } @@ -202,7 +197,9 @@ pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>( // Early exit if some param cannot be filled from lookup let param_exprs: Vec>> = fields .into_iter() - .map(|field| lookup.find(db, &field.ty_with_args(db, generics.iter().cloned()))) + .map(|field| { + lookup.find(db, &field.ty(db).instantiate(generics.iter().cloned())) + }) .collect::>()?; // Note that we need special case for 0 param constructors because of multi cartesian @@ -252,7 +249,7 @@ pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>( .fields(db) .into_iter() .map(|field| { - lookup.find(db, &field.ty_with_args(db, generics.iter().cloned())) + lookup.find(db, &field.ty(db).instantiate(generics.iter().cloned())) }) .collect::>()?; @@ -285,7 +282,9 @@ pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>( } Adt::Union(_) => None, }) - .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) + .filter_map(|(ty, exprs)| { + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs) + }) .flatten() } @@ -364,7 +363,7 @@ pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>( }) .collect::>()?; - let ret_ty = it.ret_type_with_args(db, generics.iter().cloned()); + let ret_ty = it.ret_type(db).instantiate(generics.iter().cloned()); // Filter out private and unsafe functions if !it.is_visible_from(db, module) || it.is_unsafe_to_call( @@ -381,10 +380,10 @@ pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>( // Early exit if some param cannot be filled from lookup let param_exprs: Vec>> = it - .params_without_self_with_args(db, generics.iter().cloned()) + .params_without_self(db) .into_iter() .map(|field| { - let ty = field.ty(); + let ty = &field.ty().instantiate(&generics); match ty.is_mutable_reference() { true => None, false => lookup.find_autoref(db, ty), @@ -418,7 +417,9 @@ pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>( _ => None, }) .flatten() - .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) + .filter_map(|(ty, exprs)| { + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs) + }) .flatten() } @@ -493,7 +494,7 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( return None; } - let ret_ty = it.ret_type_with_args(db, ty.type_arguments()); + let ret_ty = it.ret_type(db).instantiate(ty.type_arguments()); // Filter out functions that return references if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() { @@ -501,12 +502,12 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( } // Ignore functions that do not change the type - if ty.could_unify_with_deeply(db, &ret_ty) { + if ty.instantiate_with_errors().could_unify_with_deeply(db, &ret_ty) { return None; } let self_ty = - it.self_param(db).expect("No self param").ty_with_args(db, ty.type_arguments()); + it.self_param(db).expect("No self param").ty(db).instantiate(ty.type_arguments()); // Ignore functions that have different self type if !self_ty.autoderef(db).any(|s_ty| ty == s_ty) { @@ -517,9 +518,9 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( // Early exit if some param cannot be filled from lookup let param_exprs: Vec>> = it - .params_without_self_with_args(db, ty.type_arguments()) + .params_without_self(db) .into_iter() - .map(|field| lookup.find_autoref(db, field.ty())) + .map(|field| lookup.find_autoref(db, &field.ty().instantiate(ty.type_arguments()))) .collect::>()?; let generics: Vec<_> = ty.type_arguments().collect(); @@ -540,7 +541,9 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( Some((ret_ty, fn_exprs)) }) - .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) + .filter_map(|(ty, exprs)| { + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs) + }) .flatten() } @@ -581,7 +584,9 @@ pub(super) fn struct_projection<'a, 'lt, 'db, DB: HirDatabase>( Some((filed_ty, exprs)) }) }) - .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) + .filter_map(|(ty, exprs)| { + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs) + }) .flatten() } @@ -604,20 +609,18 @@ pub(super) fn famous_types<'a, 'lt, 'db, DB: HirDatabase>( lookup: &'lt mut LookupTable<'db>, ) -> impl Iterator> + use<'a, 'db, 'lt, DB> { let db = ctx.sema.db; - let module = ctx.scope.module(); - let interner = DbInterner::new_no_crate(db); - let bool_ty = Ty::new_bool(interner); - let unit_ty = Ty::new_unit(interner); + let bool_ty = BuiltinType::bool().ty(db); + let unit_ty = Type::new_unit(); [ - Expr::FamousType { ty: Type::new(db, module.id, bool_ty), value: "true" }, - Expr::FamousType { ty: Type::new(db, module.id, bool_ty), value: "false" }, - Expr::FamousType { ty: Type::new(db, module.id, unit_ty), value: "()" }, + Expr::FamousType { ty: bool_ty.clone(), value: "true" }, + Expr::FamousType { ty: bool_ty, value: "false" }, + Expr::FamousType { ty: unit_ty, value: "()" }, ] .into_iter() .inspect(|exprs| { lookup.insert(exprs.ty(db), std::iter::once(exprs.clone())); }) - .filter(|expr| expr.ty(db).could_unify_with_deeply(db, &ctx.goal)) + .filter(|expr| expr.ty(db).instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal)) } /// # Impl static method (without self type) tactic @@ -691,7 +694,7 @@ pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>( return None; } - let ret_ty = it.ret_type_with_args(db, ty.type_arguments()); + let ret_ty = it.ret_type(db).instantiate(ty.type_arguments()); // Filter out functions that return references if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() { @@ -700,9 +703,9 @@ pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>( // Early exit if some param cannot be filled from lookup let param_exprs: Vec>> = it - .params_without_self_with_args(db, ty.type_arguments()) + .params_without_self(db) .into_iter() - .map(|field| lookup.find_autoref(db, field.ty())) + .map(|field| lookup.find_autoref(db, &field.ty().instantiate(ty.type_arguments()))) .collect::>()?; // Note that we need special case for 0 param constructors because of multi cartesian @@ -722,7 +725,9 @@ pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>( Some((ret_ty, fn_exprs)) }) - .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) + .filter_map(|(ty, exprs)| { + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs) + }) .flatten() } @@ -745,7 +750,6 @@ pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>( should_continue: &'a dyn std::ops::Fn() -> bool, ) -> impl Iterator> + use<'a, 'db, 'lt, DB> { let db = ctx.sema.db; - let module = ctx.scope.module(); lookup .types_wishlist() @@ -774,7 +778,7 @@ pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>( .filter(|_| should_continue()) .map(|params| { let tys: Vec> = params.iter().map(|it| it.ty(db)).collect(); - let tuple_ty = Type::new_tuple(module.krate(db).into(), &tys); + let tuple_ty = Type::new_tuple(db, &tys); let expr = Expr::Tuple { ty: tuple_ty.clone(), params }; lookup.insert(tuple_ty, iter::once(expr.clone())); @@ -785,5 +789,10 @@ pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>( Some(exprs) }) .flatten() - .filter_map(|expr| expr.ty(db).could_unify_with_deeply(db, &ctx.goal).then_some(expr)) + .filter_map(|expr| { + expr.ty(db) + .instantiate_with_errors() + .could_unify_with_deeply(db, &ctx.goal) + .then_some(expr) + }) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs index b2194ab3dcd71..e7a2b1345241c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs @@ -1,10 +1,8 @@ -use ide_db::{ - source_change::SourceChangeBuilder, syntax_helpers::node_ext::for_each_break_and_continue_expr, -}; +use ide_db::syntax_helpers::node_ext::for_each_break_and_continue_expr; use syntax::{ SyntaxToken, T, ast::{self, AstNode, HasLoopBody}, - syntax_editor::{Position, SyntaxEditor}, + syntax_editor::{Position, SyntaxAnnotation, SyntaxEditor}, }; use crate::{AssistContext, AssistId, Assists}; @@ -24,8 +22,8 @@ use crate::{AssistContext, AssistId, Assists}; // -> // ``` // fn main() { -// ${1:'l}: loop { -// break ${2:'l}; +// ${0:'l}: loop { +// break ${0:'l}; // continue ${0:'l}; // } // } @@ -53,8 +51,11 @@ pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_, '_>) ]; editor.insert_all(Position::before(&loop_kw), elements); - if let Some(cap) = ctx.config.snippet_cap { - editor.add_annotation(label.syntax(), builder.make_placeholder_snippet(cap)); + let annotation = + ctx.config.snippet_cap.map(|cap| builder.make_placeholder_snippet(cap)); + + if let Some(annotation) = annotation { + editor.add_annotation(label.syntax(), annotation); } let loop_body = loop_expr.loop_body().and_then(|it| it.stmt_list()); @@ -64,13 +65,14 @@ pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_, '_>) ast::Expr::ContinueExpr(continue_expr) => continue_expr.continue_token(), _ => return, }; - if let Some(token) = token { - insert_label_after_token(&editor, &token, ctx, builder); + if let Some(token) = token + && let Some(annotation) = annotation + { + insert_label_after_token(&editor, &token, annotation); } }); builder.add_file_edits(ctx.vfs_file_id(), editor); - builder.rename(); }, ) } @@ -86,17 +88,14 @@ fn loop_token(loop_expr: &ast::AnyHasLoopBody) -> Option { fn insert_label_after_token( editor: &SyntaxEditor, token: &SyntaxToken, - ctx: &AssistContext<'_, '_>, - builder: &mut SourceChangeBuilder, + annotation: SyntaxAnnotation, ) { let make = editor.make(); let label = make.lifetime("'l"); let elements = vec![make.whitespace(" ").into(), label.syntax().clone().into()]; editor.insert_all(Position::after(token), elements); - if let Some(cap) = ctx.config.snippet_cap { - editor.add_annotation(label.syntax(), builder.make_placeholder_snippet(cap)); - } + editor.add_annotation(label.syntax(), annotation); } #[cfg(test)] @@ -118,8 +117,8 @@ fn main() { }"#, r#" fn main() { - ${1:'l}: loop { - break ${2:'l}; + ${0:'l}: loop { + break ${0:'l}; continue ${0:'l}; } }"#, @@ -139,8 +138,8 @@ fn main() { }"#, r#" fn main() { - ${1:'l}: while true { - break ${2:'l}; + ${0:'l}: while true { + break ${0:'l}; continue ${0:'l}; } }"#, @@ -160,8 +159,8 @@ fn main() { }"#, r#" fn main() { - ${1:'l}: for _ in 0..5 { - break ${2:'l}; + ${0:'l}: for _ in 0..5 { + break ${0:'l}; continue ${0:'l}; } }"#, @@ -185,8 +184,8 @@ fn main() { }"#, r#" fn main() { - ${1:'l}: loop { - break ${2:'l}; + ${0:'l}: loop { + break ${0:'l}; continue ${0:'l}; loop { break; @@ -217,8 +216,8 @@ fn main() { loop { break; continue; - ${1:'l}: loop { - break ${2:'l}; + ${0:'l}: loop { + break ${0:'l}; continue ${0:'l}; } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 667a1d7813c5c..632fe0d72cfa5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -80,16 +80,25 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_, let module = scope.module(); let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(scope.krate())); let self_ty = if ctx.config.prefer_self_ty { - scope.expression_store_owner().and_then(|def| { - match def { - hir::ExpressionStoreOwner::Body(def_with_body) => { - def_with_body.as_assoc_item(ctx.db()) + scope + .expression_store_owner() + .and_then(|def| { + match def { + hir::ExpressionStoreOwner::Body(def_with_body) => { + def_with_body.as_assoc_item(ctx.db()) + } + hir::ExpressionStoreOwner::Signature(def) => def.as_assoc_item(ctx.db()), + hir::ExpressionStoreOwner::VariantFields(_) => None, + }? + .implementing_ty(ctx.db()) + }) + .map(|self_ty| { + if let Some(owner) = scope.generic_def() { + self_ty.try_rebase_into_owner(ctx.db(), owner).unwrap() + } else { + self_ty } - hir::ExpressionStoreOwner::Signature(def) => def.as_assoc_item(ctx.db()), - hir::ExpressionStoreOwner::VariantFields(_) => None, - }? - .implementing_ty(ctx.db()) - }) + }) } else { None }; @@ -613,7 +622,7 @@ fn build_pat( hir::StructKind::Tuple => { let mut name_generator = suggest_name::NameGenerator::default(); let pats = fields.into_iter().map(|f| { - let name = name_generator.for_type(&f.ty(db).to_type(db), db, edition); + let name = name_generator.for_type(&f.ty(db), db, edition); match name { Some(name) => make.ident_pat(false, false, make.name(&name)).into(), None => make.wildcard_pat().into(), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs index ac0bae7cd9b1a..dd082476d2d62 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs @@ -293,7 +293,7 @@ pub(crate) fn relevance_score( if let Some(ty) = ty { if ty == *expected { score = 100000; - } else if ty.could_unify_with(ctx.db(), expected) { + } else if ty.could_unify_with(ctx.db(), &expected.instantiate_with_errors()) { score = 10000; } } @@ -2037,4 +2037,60 @@ fn baz() { "Import `foo::Ext` without `as _`", ); } + + #[test] + fn local_enum_variant() { + check_assist( + auto_import, + r#" +mod foo { + pub enum ControlFlow { + Continue, + } +} + +fn main() { + Continue$0; +} + "#, + r#" +use foo::ControlFlow::Continue; + +mod foo { + pub enum ControlFlow { + Continue, + } +} + +fn main() { + Continue; +} + "#, + ); + } + + #[test] + fn foreign_enum_variant() { + check_assist( + auto_import, + r#" +//- /foo.rs crate:foo +pub enum ControlFlow { + Continue, +} + +//- /main.rs crate:main deps:foo +fn main() { + Continue$0; +} + "#, + r#" +use foo::ControlFlow::Continue; + +fn main() { + Continue; +} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs index 07e12f0320be7..65c1c7cb70e78 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs @@ -229,6 +229,7 @@ fn remove_mut_and_collect_idents( | ast::Pat::LiteralPat(_) | ast::Pat::PathPat(_) | ast::Pat::WildcardPat(_) + | ast::Pat::NotNull(_) | ast::Pat::ConstBlockPat(_) => pat.clone(), // don't support macro pat yet ast::Pat::MacroPat(_) => return 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 a93ab138e5f0e..9dffdf3f367c4 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 @@ -93,7 +93,7 @@ fn find_arms( let mut extracting = None; let mut diverging = None; for arm in arms { - if ctx.sema.type_of_expr(&arm.expr()?)?.original().is_never() { + if ctx.sema.expr_is_diverging(&arm.expr()?) { diverging = Some(arm); } else { extracting = Some(arm); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs index 4aa11b4e03c85..8ae322c020038 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs @@ -137,13 +137,7 @@ fn expand_tuple_struct_rest_pattern( pat.fields() .take(prefix_count) .chain(fields[prefix_count..fields.len() - suffix_count].iter().map(|f| { - gen_unnamed_pat( - ctx, - make, - &mut name_gen, - &f.ty(ctx.db()).to_type(ctx.sema.db), - f.index(), - ) + gen_unnamed_pat(ctx, make, &mut name_gen, &f.ty(ctx.db()), f.index()) })) .chain(pat.fields().skip(prefix_count + 1)), ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index 44123dc20dee0..67bb7fc03ee55 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -21,7 +21,7 @@ use itertools::Itertools; use syntax::{ Edition, SyntaxElement, SyntaxKind::{self, COMMENT}, - SyntaxNode, SyntaxToken, T, TextRange, TextSize, TokenAtOffset, WalkEvent, + SyntaxNode, T, TextRange, WalkEvent, ast::{ self, AstNode, AstToken, HasAttrs, HasGenericParams, HasName, edit::{AstNodeEdit, IndentLevel}, @@ -103,7 +103,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - let ret_ty = body.return_ty(ctx)?; let control_flow = body.external_control_flow(ctx, &container_info)?; - let ret_values = body.ret_values(ctx, node.parent().as_ref().unwrap_or(&node)); + let ret_values = body.ret_values(ctx); let target_range = body.text_range(); @@ -968,13 +968,11 @@ impl FunctionBody { fn ret_values<'a>( &self, ctx: &'a AssistContext<'_, '_>, - parent: &SyntaxNode, ) -> impl Iterator + 'a { - let parent = parent.clone(); let range = self.text_range(); locals_defined_in_body(&ctx.sema, self) .into_iter() - .filter_map(move |local| local_outlives_body(ctx, range, local, &parent)) + .filter_map(move |local| local_outlives_body(ctx, range, local)) } /// Analyses the function body for external control flow. @@ -1174,15 +1172,11 @@ fn has_exclusive_usages( usages .iter() .filter(|reference| body.contains_range(reference.range)) - .any(|reference| reference_is_exclusive(reference, body, ctx)) + .any(|reference| reference_is_exclusive(reference, ctx)) } /// checks if this reference requires `&mut` access inside node -fn reference_is_exclusive( - reference: &FileReference, - node: &dyn HasTokenAtOffset, - ctx: &AssistContext<'_, '_>, -) -> bool { +fn reference_is_exclusive(reference: &FileReference, ctx: &AssistContext<'_, '_>) -> bool { // FIXME: this quite an incorrect way to go about doing this :-) // `FileReference` is an IDE-type --- it encapsulates data communicated to the human, // but doesn't necessary fully reflect all the intricacies of the underlying language semantics @@ -1195,7 +1189,7 @@ fn reference_is_exclusive( } // we take `&mut` reference to variable: `&mut v` - let path = match path_element_of_reference(node, reference.range) { + let path = match path_element_of(reference) { Some(path) => path, None => return false, }; @@ -1205,11 +1199,6 @@ fn reference_is_exclusive( /// checks if this expr requires `&mut` access, recurses on field access fn expr_require_exclusive_access(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) -> Option { - if let ast::Expr::MacroExpr(_) = expr { - // FIXME: expand macro and check output for mutable usages of the variable? - return None; - } - let parent = expr.syntax().parent()?; if let Some(bin_expr) = ast::BinExpr::cast(parent.clone()) { @@ -1238,66 +1227,19 @@ fn expr_require_exclusive_access(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) Some(false) } -trait HasTokenAtOffset { - fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset; -} - -impl HasTokenAtOffset for SyntaxNode { - fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset { - SyntaxNode::token_at_offset(self, offset) - } -} - -impl HasTokenAtOffset for FunctionBody { - fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset { - match self { - FunctionBody::Expr(expr) => expr.syntax().token_at_offset(offset), - FunctionBody::Span { parent, text_range, .. } => { - match parent.syntax().token_at_offset(offset) { - TokenAtOffset::None => TokenAtOffset::None, - TokenAtOffset::Single(t) => { - if text_range.contains_range(t.text_range()) { - TokenAtOffset::Single(t) - } else { - TokenAtOffset::None - } - } - TokenAtOffset::Between(a, b) => { - match ( - text_range.contains_range(a.text_range()), - text_range.contains_range(b.text_range()), - ) { - (true, true) => TokenAtOffset::Between(a, b), - (true, false) => TokenAtOffset::Single(a), - (false, true) => TokenAtOffset::Single(b), - (false, false) => TokenAtOffset::None, - } - } - } - } - } - } -} - /// find relevant `ast::Expr` for reference /// /// # Preconditions /// /// `node` must cover `reference`, that is `node.text_range().contains_range(reference.range)` -fn path_element_of_reference( - node: &dyn HasTokenAtOffset, - reference_range: TextRange, -) -> Option { - let token = node.token_at_offset(reference_range.start()).right_biased().or_else(|| { - stdx::never!(false, "cannot find token at variable usage: {:?}", reference_range); - None - })?; - let path = token.parent_ancestors().find_map(ast::Expr::cast).or_else(|| { - stdx::never!(false, "cannot find path parent of variable usage: {:?}", token); +fn path_element_of(reference: &FileReference) -> Option { + let path = reference.name.syntax().ancestors().find_map(ast::Expr::cast).or_else(|| { + stdx::never!(false, "cannot find path parent of variable usage: {:?}", reference.name); None })?; stdx::always!( - matches!(path, ast::Expr::PathExpr(_) | ast::Expr::MacroExpr(_)), + matches!(path, ast::Expr::PathExpr(_)) + || path.syntax().parent().and_then(ast::FormatArgsExpr::cast).is_some(), "unexpected expression type for variable usage: {:?}", path ); @@ -1327,14 +1269,13 @@ fn local_outlives_body( ctx: &AssistContext<'_, '_>, body_range: TextRange, local: Local, - parent: &SyntaxNode, ) -> Option { let usages = LocalUsages::find_local_usages(ctx, local); let mut has_mut_usages = false; let mut any_outlives = false; for usage in usages.iter() { if body_range.end() <= usage.range.start() { - has_mut_usages |= reference_is_exclusive(usage, parent, ctx); + has_mut_usages |= reference_is_exclusive(usage, ctx); any_outlives |= true; if has_mut_usages { break; // no need to check more elements we have all the info we wanted @@ -2093,12 +2034,11 @@ fn fix_param_usages( let mut usages_for_param: Vec<(&Param<'_>, Vec)> = Vec::new(); let mut usages_for_self_param: Vec = Vec::new(); let source_range = source_syntax.text_range(); - let source_start = source_range.start(); + let syntax_offset = source_range.start() - syntax.text_range().start(); let reference_filter = |reference: &FileReference| { source_range.contains_range(reference.range).then_some(())?; - let local_range = reference.range - source_start; - path_element_of_reference(syntax, local_range) + path_element_of(reference) }; if let Some(self_param) = to_this_param { @@ -2119,10 +2059,20 @@ fn fix_param_usages( } let make = editor.make(); + let to_original = |old: &SyntaxNode| { + ctx.sema + .original_range_opt(old) + .map(|orig| crate::utils::cover_edit_range(syntax, orig.range - syntax_offset)) + }; + let replace = |range, new: &SyntaxNode| { + editor.replace_all(range, vec![new.clone().into()]); + }; for self_usage in usages_for_self_param { - let this_expr = make.expr_path(make.ident_path("this")); - editor.replace(self_usage.syntax(), this_expr.syntax()); + if let Some(original) = to_original(self_usage.syntax()) { + let this_expr = make.expr_path(make.ident_path("this")); + replace(original, this_expr.syntax()) + } } for (param, usages) in usages_for_param { for usage in usages { @@ -2135,24 +2085,33 @@ fn fix_param_usages( // do nothing } Some(ast::Expr::RefExpr(node)) - if param.kind() == ParamKind::MutRef && node.mut_token().is_some() => + if param.kind() == ParamKind::MutRef + && node.mut_token().is_some() + && let Some(original) = to_original(node.syntax()) => { - editor.replace( - node.syntax(), + replace( + original, node.expr().expect("RefExpr::expr() cannot be None").syntax(), ); } Some(ast::Expr::RefExpr(node)) - if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() => + if param.kind() == ParamKind::SharedRef + && node.mut_token().is_none() + && let Some(original) = to_original(node.syntax()) => { - editor.replace( - node.syntax(), + replace( + original, node.expr().expect("RefExpr::expr() cannot be None").syntax(), ); } Some(_) | None => { - let p = make.expr_prefix(T![*], usage.clone()); - editor.replace(usage.syntax(), p.syntax()) + if let Some(original) = to_original(usage.syntax()) + // ignore variable in format string + && !matches!(usage, ast::Expr::Literal(_)) + { + let p = make.expr_prefix(T![*], usage.clone()); + replace(original, p.syntax()) + } } } } @@ -3519,6 +3478,61 @@ fn $0fun_name(n: &mut i32) { ); } + #[test] + fn mut_param_because_of_mut_ref_in_macro() { + check_assist( + extract_function, + r#" +macro_rules! refmut { ($e:expr) => { &mut $e }; } +fn foo() { + let mut n = 1; + $0let v = refmut!(n); + *v += 1;$0 + let k = n; +} +"#, + r#" +macro_rules! refmut { ($e:expr) => { &mut $e }; } +fn foo() { + let mut n = 1; + fun_name(&mut n); + let k = n; +} + +fn $0fun_name(n: &mut i32) { + let v = refmut!(*n); + *v += 1; +} +"#, + ); + + check_assist( + extract_function, + r#" +macro_rules! id { ($e:expr) => { $e }; } +fn foo() { + let mut n = 1; + $0let v = id!(&mut n); + *v += 1;$0 + let k = n; +} +"#, + r#" +macro_rules! id { ($e:expr) => { $e }; } +fn foo() { + let mut n = 1; + fun_name(&mut n); + let k = n; +} + +fn $0fun_name(n: &mut i32) { + let v = id!(n); + *v += 1; +} +"#, + ); + } + #[test] fn mut_param_by_value_because_of_mut_ref() { check_assist( @@ -6396,7 +6410,42 @@ fn main() { } fn $0fun_name(s: &Foo) { - *print!("{}{}", s, s); + print!("{}{}", *s, *s); +}"#, + ); + + check_assist( + extract_function, + r#" +//- minicore: fmt +#[derive(Debug)] +struct Foo(&'static str); + +impl Foo { + fn text(&self) -> &str { self.0 } +} + +fn main() { + let s = Foo(""); + $0print!("{s}{s}");$0 + let _ = s.text() == ""; +}"#, + r#" +#[derive(Debug)] +struct Foo(&'static str); + +impl Foo { + fn text(&self) -> &str { self.0 } +} + +fn main() { + let s = Foo(""); + fun_name(&s); + let _ = s.text() == ""; +} + +fn $0fun_name(s: &Foo) { + print!("{s}{s}"); }"#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs index 9e06a17337d8e..40eaed0080a0a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs @@ -733,6 +733,7 @@ fn check_def_in_mod_and_out_sel( Definition::Static(x) => check_item!(x), Definition::Trait(x) => check_item!(x), Definition::TypeAlias(x) => check_item!(x), + Definition::Macro(x) => check_item!(x), _ => {} } @@ -1813,4 +1814,33 @@ mod foo { "#, ) } + + #[test] + fn test_extract_module_macro_call_import() { + check_assist( + extract_module, + r" +macro_rules! my_macro { + () => {}; +} + +$0fn bar() { + my_macro!(); +}$0 + ", + r" +macro_rules! my_macro { + () => {}; +} + +mod modname { + use super::my_macro; + + pub(crate) fn bar() { + my_macro!(); + } +} + ", + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 50ce8d9b4d5ac..5e6e74bc94429 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -249,10 +249,6 @@ fn tag_generics_in_variant(ty: &ast::Type, generics: &mut [(ast::GenericParam, b } } param if matches!(token.kind(), T![ident]) => { - #[expect( - clippy::collapsible_match, - reason = "it won't compile since in the guard, `param` is immutable" - )] if match param { ast::GenericParam::ConstParam(konst) => konst .name() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index d4a0490f16a41..e1b91c9e89d5a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -13,7 +13,10 @@ use syntax::{ syntax_editor::{Element, Position}, }; -use crate::{AssistContext, AssistId, Assists, utils::is_body_const}; +use crate::{ + AssistContext, AssistId, Assists, + utils::{cover_edit_range, is_body_const}, +}; // Assist: extract_variable // @@ -107,9 +110,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - .skip_while(|it| !it.text_range().contains_range(range)) .find_map(valid_target_expr(ctx))?; let original_range = ctx.sema.original_range(expr.syntax()); - let (first, last) = extract_token_range_of(&node, original_range.range)?; - let to_extract = first.syntax_element()..=last.syntax_element(); - (to_extract, expr) + (cover_edit_range(&node, original_range.range), expr) } else { let expr = node .descendants() @@ -3000,6 +3001,28 @@ fn main() { let $0var_name = 2+3; let x = foo!(= Foo { x: var_name + 4 }); } +"#, + "Extract into variable", + ); + } + + #[test] + fn regression_22441() { + check_assist_by_label( + extract_variable, + r#" +//- minicore: option, write +fn main() { + let maybe_str = Some("world"); + write!((), "Hello, {}!", $0maybe_str.unwrap()$0); +} +"#, + r#" +fn main() { + let maybe_str = Some("world"); + let $0var_name = maybe_str.unwrap(); + write!((), "Hello, {}!", var_name); +} "#, "Extract into variable", ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs index 52df6182ac56f..1617016172d02 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs @@ -1,4 +1,3 @@ -use hir::next_solver::{DbInterner, TypingMode}; use ide_db::{RootDatabase, famous_defs::FamousDefs}; use syntax::ast::{self, AstNode, HasName, edit::AstNodeEdit, syntax_factory::SyntaxFactory}; use syntax::syntax_editor::Position; @@ -159,16 +158,12 @@ fn existing_from_impl( let variant = sema.to_def(variant)?; let krate = variant.module(db).krate(db); let from_trait = FamousDefs(sema, krate).core_convert_From()?; - let interner = DbInterner::new_with(db, krate.base()); - use hir::next_solver::infer::DbInternerInferExt; - let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); - let variant = variant.instantiate_infer(&infcx); let enum_ = variant.parent_enum(sema.db); let field_ty = variant.fields(sema.db).first()?.ty(sema.db); let enum_ty = enum_.ty(sema.db); tracing::debug!(?enum_, ?field_ty, ?enum_ty); - enum_ty.impls_trait(infcx, from_trait, &[field_ty]).then_some(()) + enum_ty.has_any_impl(db, from_trait, &[field_ty]).then_some(()) } #[cfg(test)] 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 index 4348dfa212c70..d5629e2e7e073 100644 --- 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 @@ -1,4 +1,3 @@ -use hir::next_solver::{DbInterner, TypingMode}; use hir::{HasCrate, ModuleDef, Semantics}; use ide_db::use_trivial_constructor::use_trivial_constructor_with_factory; use ide_db::{ @@ -233,15 +232,11 @@ fn from_impl_exists( let strukt = sema.to_def(strukt)?; let krate = strukt.krate(db); let from_trait = FamousDefs(sema, krate).core_convert_From()?; - let interner = DbInterner::new_with(db, krate.base()); - use hir::next_solver::infer::DbInternerInferExt; - let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); - let strukt = strukt.instantiate_infer(&infcx); let field_ty = strukt.fields(db).get(main_field_i)?.ty(db); let struct_ty = strukt.ty(db); tracing::debug!(?strukt, ?field_ty, ?struct_ty); - struct_ty.impls_trait(infcx, from_trait, &[field_ty]).then_some(()) + struct_ty.has_any_impl(db, from_trait, &[field_ty]).then_some(()) } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs index af048c6ae0415..8897e59355e45 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs @@ -7,7 +7,7 @@ use hir::{ sym, }; use ide_db::{ - EditionedFileId, FileId, FxHashMap, RootDatabase, + EditionedFileId, FxHashMap, RootDatabase, base_db::Crate, defs::Definition, imports::insert_use::remove_use_tree_if_simple, @@ -21,7 +21,7 @@ use syntax::{ ast::{ self, HasArgList, HasGenericArgs, Pat, PathExpr, edit::{AstNodeEdit, IndentLevel}, - make, + syntax_factory::SyntaxFactory, }, syntax_editor::SyntaxEditor, }; @@ -79,7 +79,11 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_, '_> let function = ctx.sema.to_def(&ast_func)?; - let params = get_fn_params(ctx.sema.db, function, ¶m_list)?; + let def_file_editor = SyntaxEditor::new(ast_func.syntax().ancestors().last().unwrap()).0; + let params = get_fn_params(ctx.sema.db, function, ¶m_list, def_file_editor.make())?; + + let mut file_editors = FxHashMap::default(); + file_editors.insert(vfs_def_file, def_file_editor); let usages = Definition::Function(function).usages(&ctx.sema); if !usages.at_least_one() { @@ -106,7 +110,6 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_, '_> let mut usages = usages.all(); let current_file_usage = usages.references.remove(&def_file); - let mut file_editors: FxHashMap = FxHashMap::default(); let mut remove_def = true; let mut inline_refs_for_file = |file_id: EditionedFileId, refs: Vec| { let file_id = file_id.file_id(ctx.db()); @@ -170,10 +173,10 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_, '_> None => builder.edit_file(vfs_def_file), } if remove_def { - let editor = file_editors + file_editors .entry(vfs_def_file) - .or_insert_with(|| builder.make_editor(ast_func.syntax())); - editor.delete(ast_func.syntax()); + .or_insert_with(|| builder.make_editor(ast_func.syntax())) + .delete(ast_func.syntax()); } for (file_id, editor) in file_editors { builder.add_file_edits(file_id, editor); @@ -251,7 +254,9 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Opt cov_mark::hit!(inline_call_recursive); return None; } - let params = get_fn_params(ctx.sema.db, function, ¶m_list)?; + let syntax = call_info.node.syntax().clone(); + let editor = SyntaxEditor::new(syntax.ancestors().last().unwrap()).0; + let params = get_fn_params(ctx.sema.db, function, ¶m_list, editor.make())?; if call_info.arguments.len() != params.len() { // Can't inline the function because they've passed the wrong number of @@ -260,9 +265,7 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Opt return None; } - let syntax = call_info.node.syntax().clone(); acc.add(AssistId::refactor_inline("inline_call"), label, syntax.text_range(), |builder| { - let editor = builder.make_editor(call_info.node.syntax()); let replacement = inline(&ctx.sema, file_id, function, &fn_body, ¶ms, &call_info, &editor); editor.replace(call_info.node.syntax(), replacement.syntax()); @@ -311,6 +314,7 @@ fn get_fn_params<'db>( db: &'db dyn HirDatabase, function: hir::Function, param_list: &ast::ParamList, + make: &SyntaxFactory, ) -> Option, hir::Param<'db>)>> { let mut assoc_fn_params = function.assoc_fn_params(db).into_iter(); @@ -318,10 +322,10 @@ fn get_fn_params<'db>( if let Some(self_param) = param_list.self_param() { // Keep `ref` and `mut` and transform them into `&` and `mut` later params.push(( - make::ident_pat( + make.ident_pat( self_param.amp_token().is_some(), self_param.mut_token().is_some(), - make::name("this"), + make.name("this"), ) .into(), None, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs index cb48554083ab4..5e07d85db9cf2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs @@ -52,8 +52,8 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Op ImportCandidate::Path(candidate) if !candidate.qualifier.is_empty() => { cov_mark::hit!(qualify_path_qualifier_start); let path = ast::Path::cast(syntax_under_caret.clone())?; - let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); - QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) + let first_seg_generics = path.segments().next()?.generic_arg_list(); + QualifyCandidate::QualifierStart(path, first_seg_generics) } ImportCandidate::Path(_) => { cov_mark::hit!(qualify_path_unqualified_name); @@ -113,7 +113,7 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Op Some(()) } pub(crate) enum QualifyCandidate<'db> { - QualifierStart(ast::PathSegment, Option), + QualifierStart(ast::Path, Option), UnqualifiedName(Option), TraitAssocItem(ast::Path, ast::PathSegment), TraitMethod(&'db RootDatabase, ast::MethodCallExpr), @@ -131,9 +131,11 @@ impl QualifyCandidate<'_> { ) { let import = mod_path_to_ast_with_factory(editor.make(), import, edition); match self { - QualifyCandidate::QualifierStart(segment, generics) => { + QualifyCandidate::QualifierStart(path, generics) => { let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); - replacer(format!("{import}{generics}::{segment}")); + let suffix = + path.segments().skip(1).map(|s| s.to_string()).collect::>().join("::"); + replacer(format!("{import}{generics}::{suffix}")); } QualifyCandidate::UnqualifiedName(generics) => { let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); @@ -284,6 +286,76 @@ fmt::Formatter ); } + #[test] + fn applicable_when_multiple_segments() { + check_assist( + qualify_path, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +b::c::foo$0 +"#, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +a::b::c::foo +"#, + ); + check_assist( + qualify_path, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +b::c$0::foo +"#, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +a::b::c::foo +"#, + ); + check_assist( + qualify_path, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +b$0::c::foo +"#, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +a::b::c::foo +"#, + ); + } + + #[test] + fn applicable_when_multiple_segments_with_generics() { + check_assist( + qualify_path, + r#" +mod a { + pub mod b { + pub struct TestStruct(T); + impl TestStruct { + pub const TEST_CONST: u8 = 42; + } + } +} +fn main() { + b::TestStruct::<()>::TEST_CONST$0; +} +"#, + r#" +mod a { + pub mod b { + pub struct TestStruct(T); + impl TestStruct { + pub const TEST_CONST: u8 = 42; + } + } +} +fn main() { + a::b::TestStruct::<()>::TEST_CONST; +} +"#, + ); + } + #[test] fn applicable_when_found_an_import() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index aa3f917f124df..ff2d0544b2a18 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -171,7 +171,7 @@ fn make_else_arm( let (pattern, expr) = if let Some(else_expr) = else_expr { let pattern = match conditionals { [(None, Some(_), _)] => make.literal_pat("false").into(), - [(Some(pat), _, _)] => match ctx + [(Some(pat), None, _)] => match ctx .sema .type_of_pat(pat) .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty.adjusted())) @@ -955,6 +955,31 @@ fn foo(x: Option) { ); } + #[test] + fn special_case_option_with_guard() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: option +fn foo(x: Option) { + $0if let Some(x) = x && x != 4 { + println!("{}", x) + } else { + println!("none") + } +} +"#, + r#" +fn foo(x: Option) { + match x { + Some(x) if x != 4 => println!("{}", x), + _ => println!("none"), + } +} +"#, + ); + } + #[test] fn special_case_option_ref() { check_assist( @@ -1005,6 +1030,31 @@ fn foo(x: Option) { ); } + #[test] + fn special_case_inverted_option_with_guard() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: option +fn foo(x: Option) { + $0if let None = x && other_cond { + println!("none") + } else { + println!("some") + } +} +"#, + r#" +fn foo(x: Option) { + match x { + None if other_cond => println!("none"), + _ => println!("some"), + } +} +"#, + ); + } + #[test] fn special_case_result() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs index 7aa9a82109c1e..17ee8597c1020 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs @@ -1,8 +1,8 @@ use hir::Semantics; -use ide_db::{RootDatabase, assists::AssistId, defs::Definition}; +use ide_db::{RootDatabase, assists::AssistId, defs::Definition, famous_defs::FamousDefs}; use syntax::{ AstNode, - ast::{self, Expr, HasArgList, make, syntax_factory::SyntaxFactory}, + ast::{self, Expr, HasArgList, syntax_factory::SyntaxFactory}, }; use crate::{AssistContext, Assists, utils::wrap_paren_in_call}; @@ -64,9 +64,29 @@ pub(crate) fn replace_with_lazy_method( format!("Replace {method_name} with {method_name_lazy}"), call.syntax().text_range(), |builder| { - let closured = into_closure(&last_arg, &method_name_lazy); - builder.replace(method_name.syntax().text_range(), method_name_lazy); - builder.replace_ast(last_arg, closured); + let editor = builder.make_editor(call.syntax()); + let add_param = match &*method_name_lazy { + "and_then" => true, + "or_else" | "unwrap_or_else" => FamousDefs(&ctx.sema, scope.krate()) + .core_result_Result() + .is_some_and(|result| { + result + .ty(ctx.db()) + .instantiate_with_errors() + .could_unify_with(ctx.db(), &receiver_ty) + }), + _ => false, + }; + let closured = into_closure(&last_arg, add_param, editor.make()); + editor.replace(method_name.syntax(), editor.make().name(&method_name_lazy).syntax()); + editor.replace(last_arg.syntax(), closured.syntax()); + if let Some(cap) = ctx.config.snippet_cap + && let ast::Expr::ClosureExpr(closured) = closured + && let Some(param) = closured.param_list().and_then(|it| it.params().next()) + { + editor.add_annotation(param.syntax(), builder.make_placeholder_snippet(cap)); + } + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } @@ -83,7 +103,7 @@ fn lazy_method_name(name: &str) -> String { } } -fn into_closure(param: &Expr, name_lazy: &str) -> Expr { +fn into_closure(param: &Expr, add_param: bool, make: &SyntaxFactory) -> Expr { (|| { if let ast::Expr::CallExpr(call) = param { if call.arg_list()?.args().count() == 0 { Some(call.expr()?) } else { None } @@ -92,9 +112,8 @@ fn into_closure(param: &Expr, name_lazy: &str) -> Expr { } })() .unwrap_or_else(|| { - let pats = (name_lazy == "and_then") - .then(|| make::untyped_param(make::ext::simple_ident_pat(make::name("it")).into())); - make::expr_closure(pats, param.clone()).into() + let pats = add_param.then(|| make.untyped_param(make.wildcard_pat().into())); + make.expr_closure(pats, param.clone()).into() }) } @@ -156,14 +175,16 @@ pub(crate) fn replace_with_eager_method( format!("Replace {method_name} with {method_name_eager}"), call.syntax().text_range(), |builder| { - builder.replace(method_name.syntax().text_range(), method_name_eager); - let called = into_call(&last_arg, &ctx.sema); - builder.replace_ast(last_arg, called); + let editor = builder.make_editor(call.syntax()); + let called = into_call(&last_arg, &ctx.sema, editor.make()); + editor.replace(method_name.syntax(), editor.make().name(method_name_eager).syntax()); + editor.replace(last_arg.syntax(), called.syntax()); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } -fn into_call(param: &Expr, sema: &Semantics<'_, RootDatabase>) -> Expr { +fn into_call(param: &Expr, sema: &Semantics<'_, RootDatabase>, make: &SyntaxFactory) -> Expr { (|| { if let ast::Expr::ClosureExpr(closure) = param { let mut params = closure.param_list()?.params(); @@ -183,8 +204,8 @@ fn into_call(param: &Expr, sema: &Semantics<'_, RootDatabase>) -> Expr { } })() .unwrap_or_else(|| { - let callable = wrap_paren_in_call(param.clone(), &SyntaxFactory::without_mappings()); - make::expr_call(callable, make::arg_list(Vec::new())).into() + let callable = wrap_paren_in_call(param.clone(), make); + make.expr_call(callable, make.arg_list(Vec::new())).into() }) } @@ -213,7 +234,7 @@ mod tests { check_assist( replace_with_lazy_method, r#" -//- minicore: option, fn +//- minicore: option, result, fn fn foo() { let foo = Some(1); return foo.unwrap_$0or(2); @@ -228,6 +249,26 @@ fn foo() { ) } + #[test] + fn replace_or_with_or_else_with_parameter() { + check_assist( + replace_with_lazy_method, + r#" +//- minicore: option, result, fn +fn foo() { + let foo = Ok(1); + return foo.unwrap_$0or(2); +} +"#, + r#" +fn foo() { + let foo = Ok(1); + return foo.unwrap_or_else(|${0:_}| 2); +} +"#, + ) + } + #[test] fn replace_or_with_or_else_call() { check_assist( @@ -358,7 +399,7 @@ fn foo() { r#" fn foo() { let foo = Some("foo"); - return foo.and_then(|it| Some("bar")); + return foo.and_then(|${0:_}| Some("bar")); } "#, ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs index f9c103aab8f1f..032cc28858c29 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs @@ -92,7 +92,7 @@ pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - }; let semantic_ty = ty_constructor .map(|ty_constructor| { - hir::Adt::from(ty_constructor).ty_with_args(ctx.db(), [ty.clone()]) + hir::Adt::from(ty_constructor).ty(ctx.db()).instantiate([ty.clone()]) }) .unwrap_or_else(|| ty.clone()); (ast_ty, semantic_ty) @@ -256,7 +256,7 @@ fn wrapper_alias<'db>( ); let new_ty = - hir::Adt::from(enum_ty).ty_with_args(ctx.db(), [semantic_ret_type.clone()]); + hir::Adt::from(enum_ty).ty(ctx.db()).instantiate([semantic_ret_type.clone()]); Some((make.ty_path(path), new_ty)) }) 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 d3ee35aa86940..15572e55291d6 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 @@ -204,8 +204,8 @@ fn main() { "#####, r#####" fn main() { - ${1:'l}: loop { - break ${2:'l}; + ${0:'l}: loop { + break ${0:'l}; continue ${0:'l}; } } 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 096f6678a58b8..1b6c9a579aba0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -417,6 +417,7 @@ fn check_pat_variant_nested_or_literal_with_depth( | ast::Pat::PathPat(_) | ast::Pat::BoxPat(_) | ast::Pat::DerefPat(_) + | ast::Pat::NotNull(_) | ast::Pat::ConstBlockPat(_) => true, ast::Pat::IdentPat(ident_pat) => ident_pat.pat().is_some_and(|pat| { @@ -841,8 +842,8 @@ pub(crate) fn convert_reference_type<'db>( } fn could_deref_to_target(ty: &hir::Type<'_>, target: &hir::Type<'_>, db: &dyn HirDatabase) -> bool { - let ty_ref = ty.add_reference(hir::Mutability::Shared); - let target_ref = target.add_reference(hir::Mutability::Shared); + let ty_ref = ty.add_reference(db, hir::Mutability::Shared); + let target_ref = target.add_reference(db, hir::Mutability::Shared); ty_ref.could_coerce_to(db, &target_ref) } @@ -870,7 +871,7 @@ fn handle_as_ref_slice( famous_defs: &FamousDefs<'_, '_>, ) -> Option<(ReferenceConversionType, bool)> { let type_argument = ty.type_arguments().next()?; - let slice_type = hir::Type::new_slice(type_argument); + let slice_type = hir::Type::new_slice(db, type_argument); ty.impls_trait(db, famous_defs.core_convert_AsRef()?, slice::from_ref(&slice_type)).then_some(( ReferenceConversionType::AsRefSlice, @@ -1181,13 +1182,5 @@ pub(crate) fn is_never_block( sema: &Semantics<'_, RootDatabase>, block_expr: &ast::BlockExpr, ) -> bool { - if let Some(tail_expr) = block_expr.tail_expr() { - sema.type_of_expr(&tail_expr).is_some_and(|ty| ty.original.is_never()) - } else if let Some(ast::Stmt::ExprStmt(expr_stmt)) = block_expr.statements().last() - && let Some(expr) = expr_stmt.expr() - { - sema.type_of_expr(&expr).is_some_and(|ty| ty.original.is_never()) - } else { - false - } + sema.expr_is_diverging(&block_expr.clone().into()) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index f3190bbbc82e6..20048ea97b2c8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -199,10 +199,10 @@ impl Completions { item.add_to(self, ctx.db); } - pub(crate) fn add_expr( + pub(crate) fn add_expr<'db>( &mut self, - ctx: &CompletionContext<'_, '_>, - expr: &hir::term_search::Expr<'_>, + ctx: &CompletionContext<'_, 'db>, + expr: &hir::term_search::Expr<'db>, ) { if let Some(item) = render_expr(ctx, expr) { item.add_to(self, ctx.db) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 2cc2200df901b..59c6c55c22b95 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -1,9 +1,9 @@ //! Completes references after dot (fields and method calls). -use std::ops::ControlFlow; +use std::{collections::hash_map, ops::ControlFlow}; -use hir::{Complete, Function, HasContainer, ItemContainer, MethodCandidateCallback}; -use ide_db::FxHashSet; +use hir::{Complete, Function, HasContainer, ItemContainer, MethodCandidateCallback, Name}; +use ide_db::{FxHashMap, FxHashSet}; use itertools::Either; use syntax::SmolStr; @@ -93,7 +93,7 @@ pub(crate) fn complete_dot( // Does <&receiver_ty as IntoIterator>::IntoIter` exist? Assume `iter` is valid let iter = receiver_ty .autoderef(ctx.db) - .map(|ty| ty.strip_references().add_reference(hir::Mutability::Shared)) + .map(|ty| ty.strip_references().add_reference(ctx.db, hir::Mutability::Shared)) .find_map(|ty| ty.into_iterator_iter(ctx.db)) .map(|ty| (ty, SmolStr::new_static("iter()"))); // Does ::IntoIter` exist? @@ -239,6 +239,9 @@ fn complete_methods( // duplicated, trait methods can. And it is still useful to show all of them (even when there // is also an inherent method, especially considering that it may be private, and filtered later). seen_methods: FxHashSet, + // However, duplicate inherent methods is usually meaningless + // https://github.com/rust-lang/rust-analyzer/issues/20773#issuecomment-4302781553 + seen_inherent_methods: FxHashMap, } impl MethodCandidateCallback for Callback<'_, '_, F> @@ -249,7 +252,21 @@ fn complete_methods( // `where` clauses or `dyn Trait`. fn on_inherent_method(&mut self, func: hir::Function) -> ControlFlow<()> { if func.self_param(self.ctx.db).is_some() && self.seen_methods.insert(func) { - (self.f)(func); + let same_name = self.seen_inherent_methods.entry(func.name(self.ctx.db)); + let do_complete = match &same_name { + hash_map::Entry::Vacant(_) => true, + hash_map::Entry::Occupied(same_func) => { + match self.ctx.is_visible(same_func.get()) { + crate::context::Visible::Yes => false, + crate::context::Visible::Editable => true, + crate::context::Visible::No => true, + } + } + }; + if do_complete { + same_name.insert_entry(func); + (self.f)(func); + } } ControlFlow::Continue(()) } @@ -277,7 +294,12 @@ fn complete_methods( &ctx.scope, traits_in_scope, None, - Callback { ctx, f, seen_methods: FxHashSet::default() }, + Callback { + ctx, + f, + seen_methods: FxHashSet::default(), + seen_inherent_methods: FxHashMap::default(), + }, ); } @@ -869,6 +891,86 @@ fn test(a: A) { ); } + #[test] + fn test_inherent_method_no_same_name() { + check_no_kw( + r#" +//- minicore: deref +struct A {} +struct B {} +impl core::ops::Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { loop {} } +} +trait Foo { fn foo(&self) -> u32 {} } +impl Foo for A {} +impl Foo for B {} +impl A { fn foo(&self) -> u8 {} } +impl B { fn foo(&self) -> u16 {} } +fn test(a: A) { + a.$0 +} +"#, + expect![[r#" + me deref() (use core::ops::Deref) fn(&self) -> &::Target + me foo() fn(&self) -> u8 + me foo() (as Foo) fn(&self) -> u32 + "#]], + ); + + check_no_kw( + r#" +//- minicore: deref +//- /dep.rs crate:dep +pub struct A {} +pub struct B {} +pub struct C {} +pub struct D {} +pub struct E {} +pub struct F {} +impl core::ops::Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::Deref for B { + type Target = C; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::Deref for C { + type Target = D; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::Deref for D { + type Target = E; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::Deref for E { + type Target = F; + fn deref(&self) -> &Self::Target { loop {} } +} +pub trait Foo { fn foo(&self) -> u32 {} } +impl Foo for A {} +impl Foo for B {} +impl A { fn foo(&self) -> u8 {} } +impl B { pub fn foo(&self) -> u16 {} } +impl C { fn foo(&self) -> i8 {} } +impl D { fn foo(&self) -> i16 {} } +impl E { pub fn foo(&self) -> i32 {} } +impl F { pub fn foo(&self) -> f32 {} } +//- /main.rs crate:main deps:dep +use dep::*; +fn test(a: A) { + a.$0 +} +"#, + expect![[r#" + me deref() (use core::ops::Deref) fn(&self) -> &::Target + me foo() fn(&self) -> u16 + me foo() (as Foo) fn(&self) -> u32 + "#]], + ); + } + #[test] fn test_completion_works_in_consts() { check_no_kw( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index a2a4cbac2161b..8d906064c0244 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -182,6 +182,9 @@ pub(crate) fn complete_expr_path( } _ => return, }; + // Note: this is not *required* here, we do it to also find methods that require + // the type to be instantiated with specific types. + let ty = ty.instantiate_with_errors(); if let Some(hir::Adt::Enum(e)) = ty.as_adt() { cov_mark::hit!(completes_variant_through_alias); @@ -317,12 +320,7 @@ pub(crate) fn complete_expr_path( } // synthetic names currently leak out as we lack synthetic hygiene, so filter them // out here - ScopeDef::Local(_) => - { - #[expect( - clippy::collapsible_match, - reason = "this changes meaning, causing the next arm to be selected" - )] + ScopeDef::Local(_) => { if !name.as_str().starts_with('<') { acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index 0cb39dd10885b..540089cf91052 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -259,7 +259,7 @@ pub(crate) fn complete_postfix( postfix_snippet( "lete", "let Ok else {}", - format!("let Ok({placeholder}) = {receiver_text} else {{\n $2\n}};\n$0"), + format!("let Ok({placeholder}) = {receiver_text} else {{\n $2\n}};$0"), ) .add_to(acc, ctx.db); @@ -281,9 +281,7 @@ pub(crate) fn complete_postfix( postfix_snippet( "lete", "let Some else {}", - format!( - "let Some({placeholder}) = {receiver_text} else {{\n $2\n}};\n$0" - ), + format!("let Some({placeholder}) = {receiver_text} else {{\n $2\n}};$0"), ) .add_to(acc, ctx.db); @@ -1101,8 +1099,28 @@ fn main() { let bar = Some(true); let Some(${1:bar}) = bar else { $2 -}; -$0 +};$0 +} +"#, + ); + + check_edit( + "lete", + r#" +//- minicore: option +fn main() { + let bar = Some(true); + bar.$0 + other(); +} +"#, + r#" +fn main() { + let bar = Some(true); + let Some(${1:bar}) = bar else { + $2 +};$0 + other(); } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs index 1238a91dad871..16e89bd0de5a7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs @@ -1,7 +1,8 @@ //! Complete fields in record literals and patterns. use ide_db::SymbolKind; use syntax::{ - SmolStr, + SmolStr, T, + algo::next_non_trivia_token, ast::{self, Expr}, }; @@ -28,11 +29,7 @@ pub(crate) fn complete_record_pattern_fields( record_pat.record_pat_field_list().and_then(|fl| fl.fields().next()).is_some(); match were_fields_specified { - false => un - .fields(ctx.db) - .into_iter() - .map(|f| (f, f.ty(ctx.db).to_type(ctx.db))) - .collect(), + false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(), true => return, } } @@ -60,11 +57,7 @@ pub(crate) fn complete_record_expr_fields( record_expr.record_expr_field_list().and_then(|fl| fl.fields().next()).is_some(); match were_fields_specified { - false => un - .fields(ctx.db) - .into_iter() - .map(|f| (f, f.ty(ctx.db).to_type(ctx.db))) - .collect(), + false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(), true => return, } } @@ -105,7 +98,9 @@ pub(crate) fn add_default_update( let impls_default_trait = default_trait .zip(ty) .is_some_and(|(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[])); - if impls_default_trait { + let ends_of_record_list = + next_non_trivia_token(ctx.token.clone()).is_none_or(|it| it.kind() == T!['}']); + if impls_default_trait && ends_of_record_list { // FIXME: This should make use of scope_def like completions so we get all the other goodies // that is we should handle this like actually completing the default function let completion_text = "..Default::default()"; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index 21f624be2cb7d..ac52c816e52a9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -44,6 +44,7 @@ pub struct CompletionConfig<'a> { pub enum AutoImportExclusionType { Always, Methods, + SubItems, } #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index f7fced3f062e9..f7bb14391b7de 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -611,7 +611,7 @@ impl<'db> CompletionContext<'_, 'db> { let Some(unstable_feature) = attrs.unstable_feature(self.db) else { return true; }; - !INTERNAL_FEATURES.contains(&unstable_feature) + !is_internal_feature(&unstable_feature) || self.krate.is_unstable_feature_enabled(self.db, &unstable_feature) } @@ -729,6 +729,13 @@ impl<'db> CompletionContext<'_, 'db> { vec![] } } + + pub(crate) fn rebase_ty(&self, ty: &hir::Type<'db>) -> hir::Type<'db> { + self.scope + .generic_def() + .and_then(|def| ty.try_rebase_into_owner(self.db, def)) + .unwrap_or_else(|| ty.instantiate_with_errors()) + } } // CompletionContext construction @@ -848,8 +855,23 @@ impl<'a, 'db> CompletionContext<'a, 'db> { .map(|it| (it.into_module_def(), *kind)) }) .collect(); + let exclude_subitems = exclude_flyimport + .iter() + .flat_map(|it| match it { + (ModuleDef::Module(module), AutoImportExclusionType::SubItems) => { + module.scope(db, None) + } + _ => vec![], + }) + .filter_map(|(_, def)| match def { + ScopeDef::ModuleDef(module_def) => Some(module_def), + _ => None, + }) + .collect::>(); exclude_flyimport .extend(exclude_traits.iter().map(|&t| (t.into(), AutoImportExclusionType::Always))); + exclude_flyimport + .extend(exclude_subitems.into_iter().map(|it| (it, AutoImportExclusionType::Always))); // FIXME: This should be part of `CompletionAnalysis` / `expand_and_analyze` let complete_semicolon = if !config.add_semicolon_to_unit { @@ -858,7 +880,13 @@ impl<'a, 'db> CompletionContext<'a, 'db> { sema.token_ancestors_with_macros(token.clone()).find(|node| { matches!( node.kind(), - BLOCK_EXPR | MATCH_ARM | CLOSURE_EXPR | ARG_LIST | PAREN_EXPR | ARRAY_EXPR + BLOCK_EXPR + | MATCH_ARM + | CLOSURE_EXPR + | ARG_LIST + | PAREN_EXPR + | ARRAY_EXPR + | MATCH_EXPR ) }) { @@ -949,6 +977,7 @@ const INTERNAL_FEATURES_LIST: &[Symbol] = &[ sym::eii_internals, sym::field_representing_type_raw, sym::intrinsics, + sym::core_intrinsics, sym::lang_items, sym::link_cfg, sym::more_maybe_bounds, @@ -972,3 +1001,12 @@ const INTERNAL_FEATURES_LIST: &[Symbol] = &[ static INTERNAL_FEATURES: LazyLock> = LazyLock::new(|| INTERNAL_FEATURES_LIST.iter().cloned().collect()); + +fn is_internal_feature(feature: &Symbol) -> bool { + if INTERNAL_FEATURES.contains(feature) { + return true; + } + // Libs features are internal if they end in `_internal` or `_internals`. + let feature = feature.as_str(); + feature.ends_with("_internal") || feature.ends_with("_internals") +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index faeb97f93f7d1..b2ee94d49cbfe 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -641,13 +641,23 @@ fn expected_type_and_name<'db>( } for _ in refs_level..0 { cov_mark::hit!(expected_type_fn_param_deref); - ty = ty.add_reference(hir::Mutability::Shared); + ty = ty.add_reference(sema.db, hir::Mutability::Shared); } ty } _ => ty, }; + let mut generic_def = None; + let mut rebase_ty = { + let node = node.clone(); + move |ty: hir::Type<'db>| { + let def = *generic_def + .get_or_insert_with(|| sema.scope(&node).and_then(|scope| scope.generic_def())); + def.and_then(|def| ty.try_rebase_into_owner(sema.db, def)) + .unwrap_or_else(|| ty.instantiate_with_errors()) + } + }; let (ty, name) = loop { break match_ast! { match node { @@ -791,26 +801,22 @@ fn expected_type_and_name<'db>( (ty, None) }, ast::TupleStructPat(it) => { - let fields = it.path().and_then(|path| match sema.resolve_path(&path)? { - hir::PathResolution::Def(hir::ModuleDef::Adt(adt)) => Some(adt.as_struct()?.fields(sema.db)), - hir::PathResolution::Def(hir::ModuleDef::EnumVariant(variant)) => Some(variant.fields(sema.db)), - _ => None, - }); + let fields = sema.resolve_tuple_struct_pat_fields(&it); let nr = it.fields().take_while(|it| it.syntax().text_range().end() <= token.text_range().start()).count(); - let ty = fields.and_then(|fields| Some(fields.get(nr)?.ty(sema.db).to_type(sema.db))); + let ty = fields.and_then(|fields| Some(rebase_ty(fields.get(nr)?.1.clone()))); (ty, None) }, ast::Fn(it) => { cov_mark::hit!(expected_type_fn_ret_with_leading_char); cov_mark::hit!(expected_type_fn_ret_without_leading_char); let def = sema.to_def(&it); - (def.map(|def| def.ret_type(sema.db)), None) + (def.map(|def| rebase_ty(def.ret_type(sema.db))), None) }, ast::ReturnExpr(it) => { let fn_ = sema.ancestors_with_macros(it.syntax().clone()) .find_map(Either::::cast); let ty = fn_.and_then(|f| match f { - Either::Left(f) => Some(sema.to_def(&f)?.ret_type(sema.db)), + Either::Left(f) => Some(rebase_ty(sema.to_def(&f)?.ret_type(sema.db))), Either::Right(f) => { let ty = sema.type_of_expr(&f.into())?.original.as_callable(sema.db)?; Some(ty.return_type()) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs index 94d904932ac56..1d1a55c6fe144 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs @@ -329,6 +329,27 @@ fn foo(x: Foo) -> Foo { "#, expect![[r#"ty: u32, name: ?"#]], ); + + check_expected_type_and_name( + r#" +//- minicore: option +struct Foo(T); +fn foo(x: Foo>) -> Foo { + match x { Foo($0) => () } +} +"#, + expect![[r#"ty: Option, name: ?"#]], + ); + check_expected_type_and_name( + r#" +//- minicore: option +enum Foo { Var(T) }; +fn foo(x: Foo>) -> Foo { + match x { Foo::Var($0) => () } +} +"#, + expect![[r#"ty: Option, name: ?"#]], + ); } #[test] diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index cfadec6287947..61281f8cfb8f1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -181,6 +181,9 @@ pub struct CompletionRelevance { /// } /// ``` pub is_local: bool, + /// This is missing variant in the patterns. + /// Maybe this can also be used for struct fields. + pub is_missing: bool, /// Populated when the completion item comes from a trait (impl). pub trait_: Option, /// This is set when an import is suggested in a use item whose name is already imported. @@ -286,6 +289,7 @@ impl CompletionRelevance { exact_name_match, type_match, is_local, + is_missing, is_name_already_imported, requires_import, is_private_editable, @@ -300,16 +304,19 @@ impl CompletionRelevance { // only applicable for completions within use items // lower rank for conflicting import names if is_name_already_imported { - score -= 1; + score -= 15; } // slightly prefer locals if is_local { - score += 1; + score += 2; + } + if is_missing { + score += 2; } // lower rank private things if !is_private_editable { - score += 1; + score += 10; } if let Some(trait_) = trait_ { @@ -330,10 +337,10 @@ impl CompletionRelevance { // lower rank for items that need an import if requires_import { - score -= 1; + score -= 12; } if exact_name_match { - score += 20; + score += 40; } match postfix_match { Some(CompletionRelevancePostfixMatch::Exact) => score += 100, @@ -341,16 +348,26 @@ impl CompletionRelevance { None => (), }; score += match type_match { - Some(CompletionRelevanceTypeMatch::Exact) => 18, - Some(CompletionRelevanceTypeMatch::CouldUnify) => 5, + Some(CompletionRelevanceTypeMatch::Exact) => 35, + Some(CompletionRelevanceTypeMatch::CouldUnify) => 15, None => 0, }; if let Some(function) = function { - let mut fn_score = match function.return_type { - CompletionRelevanceReturnType::DirectConstructor => 15, - CompletionRelevanceReturnType::Builder => 10, - CompletionRelevanceReturnType::Constructor => 5, - CompletionRelevanceReturnType::Other => 0u32, + let mut fn_score = if requires_import { + // Rank constructors that require imports lower than those who don't. + match function.return_type { + CompletionRelevanceReturnType::DirectConstructor => 8, + CompletionRelevanceReturnType::Builder => 5, + CompletionRelevanceReturnType::Constructor => 3, + CompletionRelevanceReturnType::Other => 0u32, + } + } else { + match function.return_type { + CompletionRelevanceReturnType::DirectConstructor => 15, + CompletionRelevanceReturnType::Builder => 10, + CompletionRelevanceReturnType::Constructor => 5, + CompletionRelevanceReturnType::Other => 0u32, + } }; // When a fn is bumped due to return type: @@ -368,12 +385,12 @@ impl CompletionRelevance { }; if has_local_inherent_impl { - score -= 5; + score -= 8; } // lower rank for deprecated items if is_deprecated { - score -= 5; + score -= 15; } score @@ -824,15 +841,17 @@ mod tests { is_private_editable: true, ..default }], - vec![Cr { - trait_: Some(crate::item::CompletionRelevanceTraitInfo { - notable_trait: false, - is_op_method: true, - }), - ..default - }], + vec![ + Cr { + trait_: Some(crate::item::CompletionRelevanceTraitInfo { + notable_trait: false, + is_op_method: true, + }), + ..default + }, + Cr { is_private_editable: true, ..default }, + ], vec![Cr { postfix_match: Some(CompletionRelevancePostfixMatch::NonExact), ..default }], - vec![Cr { is_private_editable: true, ..default }], vec![default], vec![Cr { is_local: true, ..default }], vec![Cr { type_match: Some(CompletionRelevanceTypeMatch::CouldUnify), ..default }], diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index fbbdffefe324b..e48847c983b4e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -71,7 +71,7 @@ impl<'a, 'db> RenderContext<'a, 'db> { self.completion.config.snippet_cap } - fn db(&self) -> &'a RootDatabase { + fn db(&self) -> &'db RootDatabase { self.completion.db } @@ -181,9 +181,9 @@ pub(crate) fn render_field( if !expected_fn_type && let Some(receiver) = &dot_access.receiver - && let Some(receiver) = ctx.completion.sema.original_ast_node(receiver.clone()) + && let Some(receiver) = ctx.completion.sema.original_range_opt(receiver.syntax()) { - builder.insert(receiver.syntax().text_range().start(), "(".to_owned()); + builder.insert(receiver.range.start(), "(".to_owned()); builder.insert(ctx.source_range().end(), ")".to_owned()); let is_parens_needed = !matches!(dot_access.kind, DotAccessKind::Method); @@ -198,10 +198,10 @@ pub(crate) fn render_field( item.insert_text(field_with_receiver(receiver.as_deref(), &escaped_name)); } if let Some(receiver) = &dot_access.receiver - && let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) + && let Some(original) = ctx.completion.sema.original_range_opt(receiver.syntax()) && let Some(ref_mode) = compute_ref_match(ctx.completion, ty) { - item.ref_match(ref_mode, original.syntax().text_range().start()); + item.ref_match(ref_mode, original.range.start()); } item.doc_aliases(ctx.doc_aliases); item.build(db) @@ -294,9 +294,9 @@ pub(crate) fn render_resolution_with_import_pat( Some(render_resolution_pat(ctx, pattern_ctx, local_name, Some(import_edit), resolution)) } -pub(crate) fn render_expr( - ctx: &CompletionContext<'_, '_>, - expr: &hir::term_search::Expr<'_>, +pub(crate) fn render_expr<'db>( + ctx: &CompletionContext<'_, 'db>, + expr: &hir::term_search::Expr<'db>, ) -> Option { let mut i = 1; let mut snippet_formatter = |ty: &hir::Type<'_>| { @@ -341,7 +341,7 @@ pub(crate) fn render_expr( "Autogenerated expression by term search", ))); item.set_relevance(crate::CompletionRelevance { - type_match: compute_type_match(ctx, &expr.ty(ctx.db)), + type_match: compute_type_match(ctx, &ctx.rebase_ty(&expr.ty(ctx.db))), ..Default::default() }); for trait_ in expr.traits_used(ctx.db) { @@ -405,8 +405,8 @@ fn render_resolution_pat( } } -fn render_resolution_path( - ctx: RenderContext<'_, '_>, +fn render_resolution_path<'db>( + ctx: RenderContext<'_, 'db>, path_ctx: &PathCompletionCtx<'_>, local_name: hir::Name, import_to_add: Option, @@ -471,18 +471,21 @@ fn render_resolution_path( .insert_snippet(cap, ""); // set is snippet } } - let allow_module_path = matches!(path_ctx.kind, PathKind::Use) || !config.add_colons_to_module; + let allow_module_path = matches!(path_ctx.kind, PathKind::Use) + || completion.token.next_token().is_some_and(|it| it.kind() == syntax::T![::]) + || !config.add_colons_to_module; if !allow_module_path && matches!(resolution, ScopeDef::ModuleDef(Module(_))) { insert_text = format_smolstr!("{insert_text}::"); item.lookup_by(name.clone()).label(insert_text.clone()); } adds_ret_type_arrow(completion, path_ctx, &mut item, insert_text.into()); - let mut set_item_relevance = |ty: Type<'_>| { + let mut set_item_relevance = |ty: Type<'db>| { if !ty.is_unknown() { item.detail(ty.display(db, krate).to_string()); } + let ty = completion.rebase_ty(&ty); item.set_relevance(CompletionRelevance { type_match: compute_type_match(completion, &ty), exact_name_match: compute_exact_name_match(completion, &name), @@ -492,7 +495,13 @@ fn render_resolution_path( ..CompletionRelevance::default() }); - path_ref_match(completion, path_ctx, &ty, &mut item); + match resolution { + ScopeDef::Local(_) + | ScopeDef::ModuleDef(ModuleDef::Const(_) | ModuleDef::Static(_)) => { + path_ref_match(completion, path_ctx, &ty, &mut item) + } + _ => (), + } }; match resolution { @@ -680,8 +689,8 @@ fn compute_type_match( // &mut ty -> &ty if completion_ty.is_mutable_reference() - && let Some(expected_type) = expected_type.remove_ref() - && let Some(completion_ty) = completion_ty.remove_ref() + && let Some((expected_type, _)) = expected_type.as_reference() + && let Some((completion_ty, _)) = completion_ty.as_reference() { return match_types(ctx, &expected_type, &completion_ty); } @@ -709,18 +718,19 @@ fn compute_ref_match( ctx: &CompletionContext<'_, '_>, completion_ty: &hir::Type<'_>, ) -> Option { - let expected_type = ctx.expected_type.as_ref()?; - let expected_without_ref = expected_type.remove_ref(); - let completion_without_ref = completion_ty.remove_ref(); - if expected_type.could_unify_with(ctx.db, completion_ty) { + if compute_type_match(ctx, completion_ty).is_some() || completion_ty.is_unit() { return None; } - if let Some(expected_without_ref) = &expected_without_ref + let expected_type = ctx.expected_type.as_ref()?; + let expected_without_ref = expected_type.as_reference(); + let completion_without_ref = completion_ty.as_reference(); + + if let Some((expected_without_ref, _)) = &expected_without_ref && (completion_without_ref.is_none() || completion_ty.could_unify_with(ctx.db, expected_without_ref)) && completion_ty .autoderef(ctx.db) - .any(|ty| ty.could_unify_with(ctx.db, expected_without_ref)) + .any(|ty| !ty.is_unknown() && ty.could_unify_with(ctx.db, expected_without_ref)) { cov_mark::hit!(suggest_ref); let mutability = if expected_type.is_mutable_reference() { @@ -731,7 +741,7 @@ fn compute_ref_match( return Some(CompletionItemRefMode::Reference(mutability)); } - if let Some(completion_without_ref) = completion_without_ref + if let Some((completion_without_ref, _)) = completion_without_ref && completion_without_ref == *expected_type && completion_without_ref.is_copy(ctx.db) { @@ -750,10 +760,10 @@ fn path_ref_match( ) { if let Some(original_path) = &path_ctx.original_path { // At least one char was typed by the user already, in that case look for the original path - if let Some(original_path) = completion.sema.original_ast_node(original_path.clone()) + if let Some(original_path) = completion.sema.original_range_opt(original_path.syntax()) && let Some(ref_mode) = compute_ref_match(completion, ty) { - item.ref_match(ref_mode, original_path.syntax().text_range().start()); + item.ref_match(ref_mode, original_path.range.start()); } } else { // completion requested on an empty identifier, there is no path here yet. @@ -865,6 +875,7 @@ mod tests { exact_name_match, type_match, is_local, + is_missing, trait_, is_name_already_imported: _, requires_import, @@ -880,6 +891,7 @@ mod tests { (type_match == Some(CompletionRelevanceTypeMatch::CouldUnify), "type_could_unify"), (exact_name_match, "name"), (is_local, "local"), + (is_missing, "missing"), (postfix_match == Some(CompletionRelevancePostfixMatch::Exact), "snippet"), (trait_.is_some_and(|it| it.is_op_method), "op_method"), (requires_import, "requires_import"), @@ -944,9 +956,9 @@ fn main() { } "#, expect![[r#" - st dep::test_mod_b::Struct {…} dep::test_mod_b::Struct { } [type_could_unify] - ex dep::test_mod_b::Struct { } [type_could_unify] - st Struct Struct [type_could_unify+requires_import] + st dep::test_mod_b::Struct {…} dep::test_mod_b::Struct { } [type] + ex dep::test_mod_b::Struct { } [type] + st Struct Struct [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(Struct) [] @@ -984,7 +996,7 @@ fn main() { } "#, expect![[r#" - un Union Union [type_could_unify+requires_import] + un Union Union [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(Union) [] @@ -1020,9 +1032,9 @@ fn main() { } "#, expect![[r#" - ev dep::test_mod_b::Enum::variant dep::test_mod_b::Enum::variant [type_could_unify] - ex dep::test_mod_b::Enum::variant [type_could_unify] - en Enum Enum [type_could_unify+requires_import] + ev dep::test_mod_b::Enum::variant dep::test_mod_b::Enum::variant [type] + ex dep::test_mod_b::Enum::variant [type] + en Enum Enum [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(Enum) [] @@ -1058,11 +1070,13 @@ fn main() { } "#, expect![[r#" - ev dep::test_mod_b::Enum::Variant dep::test_mod_b::Enum::Variant [type_could_unify] - ex dep::test_mod_b::Enum::Variant [type_could_unify] + ev dep::test_mod_b::Enum::Variant dep::test_mod_b::Enum::Variant [type] + ex dep::test_mod_b::Enum::Variant [type] + ev Variant Variant [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(Enum) [] + ev Variant Variant [requires_import] "#]], ); } @@ -1122,7 +1136,7 @@ fn main() { } "#, expect![[r#" - ct CONST i32 [type_could_unify+requires_import] + ct CONST i32 [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(i32) [] @@ -1154,7 +1168,7 @@ fn main() { } "#, expect![[r#" - sc STATIC i32 [type_could_unify+requires_import] + sc STATIC i32 [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(i32) [] @@ -1281,6 +1295,7 @@ fn main() { Foo::Fo$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1333,6 +1348,7 @@ fn main() { Foo::Fo$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1478,6 +1494,7 @@ fn main() { Foo::Fo$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1564,6 +1581,7 @@ fn main() { let _: m::Spam = S$0 } Exact, ), is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1602,6 +1620,7 @@ fn main() { let _: m::Spam = S$0 } Exact, ), is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1653,6 +1672,7 @@ fn main() { som$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1712,6 +1732,7 @@ fn main() { som$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1755,6 +1776,7 @@ fn main() { A$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1798,6 +1820,7 @@ fn main() { A$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1843,6 +1866,7 @@ fn main() { A::$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1879,6 +1903,7 @@ fn main() { A::$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1929,6 +1954,7 @@ fn main() { A$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1972,6 +1998,7 @@ fn main() { A$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2012,6 +2039,7 @@ impl A$0 exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2052,6 +2080,7 @@ fn main() { A$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2096,6 +2125,7 @@ fn main() { a$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2140,6 +2170,7 @@ fn main() { A { the$0 } } CouldUnify, ), is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2195,6 +2226,7 @@ impl S { exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2288,6 +2320,7 @@ use self::E::*; exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2360,6 +2393,7 @@ fn foo(s: S) { s.$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2580,6 +2614,7 @@ fn f() -> i32 { Exact, ), is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2639,7 +2674,6 @@ fn go(world: &WorldSnapshot) { go(w$0) } st WorldSnapshot {…} WorldSnapshot { _f: () } [] st &WorldSnapshot {…} [type] st WorldSnapshot WorldSnapshot [] - st &WorldSnapshot [type] fn go(…) fn(&WorldSnapshot) [] "#]], ); @@ -2687,6 +2721,7 @@ fn main() { exact_name_match: false, type_match: None, is_local: true, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2704,6 +2739,204 @@ fn main() { ); } + #[test] + fn complete_ref_match_in_macro() { + check_kinds( + r#" +macro_rules! id { ($($t:tt)*) => ($($t)*); } +fn foo(data: &i32) {} +fn main() { + let indent = 2i32; + id!(foo(i$0)) +} +"#, + &[CompletionItemKind::SymbolKind(SymbolKind::Local)], + expect![[r#" + [ + CompletionItem { + label: "indent", + detail_left: None, + detail_right: Some( + "i32", + ), + source_range: 114..115, + delete: 114..115, + insert: "indent", + kind: SymbolKind( + Local, + ), + detail: "i32", + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: true, + is_missing: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: false, + }, + ref_match: "&@114", + }, + ] + "#]], + ); + + check_kinds( + r#" +macro_rules! id { ($($t:tt)*) => ($($t)*); } +fn foo(data: &i32) {} +fn indent() -> i32 { i32 } +fn main() { + id!(foo(i$0)) +} +"#, + &[CompletionItemKind::SymbolKind(SymbolKind::Function)], + expect![[r#" + [ + CompletionItem { + label: "foo(…)", + detail_left: None, + detail_right: Some( + "fn(&i32)", + ), + source_range: 118..119, + delete: 118..119, + insert: "foo(${1:data})$0", + kind: SymbolKind( + Function, + ), + lookup: "foo", + detail: "fn(&i32)", + trigger_call_info: true, + }, + CompletionItem { + label: "indent()", + detail_left: None, + detail_right: Some( + "fn() -> i32", + ), + source_range: 118..119, + delete: 118..119, + insert: "indent()$0", + kind: SymbolKind( + Function, + ), + lookup: "indent", + detail: "fn() -> i32", + ref_match: "&@118", + }, + CompletionItem { + label: "main()", + detail_left: None, + detail_right: Some( + "fn()", + ), + source_range: 118..119, + delete: 118..119, + insert: "main()$0", + kind: SymbolKind( + Function, + ), + lookup: "main", + detail: "fn()", + }, + ] + "#]], + ); + + // FIXME: It is best to test `S.in` if speculative execution is implemented + check_kinds( + r#" +macro_rules! id { ($($t:tt)*) => ($($t)*); } +fn foo(data: &i32) {} +struct S; +impl S {fn indent(&self) -> i32 { i32 }} +fn main() { + id!(foo(S.i$0)) +} +"#, + &[CompletionItemKind::SymbolKind(SymbolKind::Method)], + expect![[r#" + [ + CompletionItem { + label: "indent()", + detail_left: None, + detail_right: Some( + "fn(&self) -> i32", + ), + source_range: 144..145, + delete: 144..145, + insert: "indent()$0", + kind: SymbolKind( + Method, + ), + lookup: "indent", + detail: "fn(&self) -> i32", + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + is_missing: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: Some( + CompletionRelevanceFn { + has_params: true, + has_self_param: true, + return_type: Other, + }, + ), + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: false, + }, + ref_match: "&@142", + }, + ] + "#]], + ); + + check_kinds( + r#" +macro_rules! id { ($($t:tt)*) => ($($t)*); } +fn foo(data: &i32) {} +struct S { indent: i32 } +fn main(s: S) { + id!(foo(s.i$0)) +} +"#, + &[CompletionItemKind::SymbolKind(SymbolKind::Field)], + expect![[r#" + [ + CompletionItem { + label: "indent", + detail_left: None, + detail_right: Some( + "i32", + ), + source_range: 122..123, + delete: 122..123, + insert: "indent", + kind: SymbolKind( + Field, + ), + detail: "i32", + ref_match: "&@120", + }, + ] + "#]], + ); + } + #[test] fn too_many_arguments() { cov_mark::check!(too_many_arguments); @@ -2824,6 +3057,37 @@ mod b { ); } + #[test] + fn score_patterns() { + check_relevance( + r#" +struct Foo(Bar); +struct Bar { field: i32 } +fn go(Foo($0): Foo) {} +"#, + expect![[r#" + bn Bar {…} Bar { field$1 }$0 [type] + st Bar [] + st Foo [] + bn Foo(…) Foo($1)$0 [] + "#]], + ); + + check_relevance( + r#" +struct Foo(Bar); +enum Bar { Variant { field: i32 } } +fn go(foo: Foo) { match foo { Foo($0) } } +"#, + expect![[r#" + bn Bar::Variant {…} Bar::Variant { field$1 }$0 [type] + en Bar [] + st Foo [] + bn Foo(…) Foo($1)$0 [] + "#]], + ); + } + #[test] fn test_avoid_redundant_suggestion() { check_relevance( @@ -2865,7 +3129,6 @@ fn main() { st S S [] st &mut S [type] st S S [] - st &mut S [type] fn foo(…) fn(&mut S) [] fn main() fn() [] "#]], @@ -2879,6 +3142,7 @@ fn main() { foo(&mut $0); } "#, + // FIXME: There are many `S` here expect![[r#" lc s S [type+name+local] st S S [type] @@ -2940,12 +3204,51 @@ fn main() { st &mut S(…) [type] lc ssss S [local] lc &mut ssss [type+local] - st S S<{unknown}> [] - st &mut S [type] + st S S [] fn foo(…) fn(&mut S) [] fn main() fn() [] "#]], ); + // Regression test https://github.com/rust-lang/rust-analyzer/issues/22324 + check_relevance( + r#" +//- minicore: deref +struct S(T); +impl core::ops::Deref for S { + type Target = T; +} +fn foo(s: &u32) {} +fn main() { + let ssss = S(); + foo($0); +} + "#, + // FIXME: term_search exclude ssss.0 (field.ty().is_unknown()) + expect![[r#" + ex ssss.0 [type_could_unify] + lc ssss S<{unknown}> [local] + st S S [] + md core:: [] + fn foo(…) fn(&u32) [] + fn main() fn() [] + "#]], + ); + check_relevance( + r#" +//- minicore: deref +fn foo(s: &T) {} +fn main() { + let ssss = &mut 2i32; + foo($0); +} + "#, + expect![[r#" + lc ssss &mut i32 [type_could_unify+local] + md core:: [] + fn foo(…) fn(&T) [] + fn main() fn() [] + "#]], + ); } #[test] @@ -3016,9 +3319,7 @@ fn main() { lc t T [local] lc &t [type+local] st S S [] - st &S [type] st T T [] - st &T [type] md core:: [] fn foo(…) fn(&S) [] fn main() fn() [] @@ -3065,9 +3366,7 @@ fn main() { lc t T [local] lc &mut t [type+local] st S S [] - st &mut S [type] st T T [] - st &mut T [type] md core:: [] fn foo(…) fn(&mut S) [] fn main() fn() [] @@ -3131,7 +3430,6 @@ fn bar(t: &Foo) {} ev Foo::B Foo::B [] ev &Foo::B [type] en Foo Foo [] - en &Foo [type] fn bar(…) fn(&Foo) [] fn foo() fn() [] "#]], @@ -3166,9 +3464,7 @@ fn main() { st &S [type] ex core::ops::Deref::deref(&bar()) [type_could_unify] st S S [] - st &S [type] st T T [] - st &T [type] fn bar() fn() -> T [] fn &bar() [type] md core:: [] @@ -3499,6 +3795,7 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -3594,6 +3891,7 @@ fn foo() { Exact, ), is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -3648,6 +3946,7 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -3690,14 +3989,14 @@ fn foo() { } "#, expect![[r#" - ev Foo::B Foo::B [type_could_unify] - ev Foo::A(…) Foo::A(T) [type_could_unify] lc foo Foo [type+local] ex Foo::B [type] ex foo [type] - en Foo Foo<{unknown}> [type_could_unify] + ev Foo::B Foo::B [type_could_unify] + ev Foo::A(…) Foo::A(T) [type_could_unify] + en Foo Foo [type_could_unify] + fn baz() fn() -> Foo [type_could_unify] fn bar() fn() -> Foo [] - fn baz() fn() -> Foo [] fn foo() fn() [] "#]], ); @@ -3727,6 +4026,7 @@ fn main() { &[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)], expect![[r#" sn not !expr [snippet] + me not() fn(self) -> ::Output [type_could_unify+requires_import] sn box Box::new(expr) [] sn call function(expr) [] sn const const {} [] @@ -3740,7 +4040,6 @@ fn main() { sn return return expr [] sn unsafe unsafe {} [] sn while while expr {} [] - me not() fn(self) -> ::Output [requires_import] "#]], ); } @@ -3791,7 +4090,7 @@ enum Foo { en Foo Foo [] st Other Other [] sp Self Foo [] - st Vec<…> Vec<{unknown}> [] + st Vec<…> Vec [] "#]], ); } @@ -4140,6 +4439,7 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: Some( CompletionRelevanceTraitInfo { notable_trait: true, @@ -4176,6 +4476,7 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: Some( CompletionRelevanceTraitInfo { notable_trait: true, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 97d5a25f493d5..4f70a90affbdf 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -76,7 +76,7 @@ fn render( completion.edition, ); - let ret_type = func.ret_type(db); + let ret_type = ctx.completion.rebase_ty(&func.ret_type(db)); let assoc_item = func.as_assoc_item(db); let trait_info = @@ -107,6 +107,7 @@ fn render( let function = assoc_item .and_then(|assoc_item| assoc_item.implementing_ty(db)) + .map(|self_type| ctx.completion.rebase_ty(&self_type)) .map(|self_type| compute_return_type_match(db, &ctx, self_type, &ret_type)) .map(|return_type| CompletionRelevanceFn { has_params: has_self_param || func.num_params(db) > 0, @@ -118,7 +119,7 @@ fn render( type_match: if has_call_parens || complete_call_parens.is_some() { compute_type_match(completion, &ret_type) } else { - compute_type_match(completion, &func.ty(db)) + compute_type_match(completion, &ctx.completion.rebase_ty(&func.ty(db))) }, exact_name_match: compute_exact_name_match(completion, &call), function, @@ -132,10 +133,10 @@ fn render( super::path_ref_match(completion, path_ctx, &ret_type, &mut item); } FuncKind::Method(DotAccess { receiver: Some(receiver), .. }, _) => { - if let Some(original_expr) = completion.sema.original_ast_node(receiver.clone()) + if let Some(original_expr) = completion.sema.original_range_opt(receiver.syntax()) && let Some(ref_mode) = compute_ref_match(completion, &ret_type) { - item.ref_match(ref_mode, original_expr.syntax().text_range().start()); + item.ref_match(ref_mode, original_expr.range.start()); } } _ => (), @@ -287,7 +288,7 @@ pub(super) fn add_call_parens<'b>( } fn ref_of_param(ctx: &CompletionContext<'_, '_>, arg: &str, ty: &hir::Type<'_>) -> &'static str { - if let Some(derefed_ty) = ty.remove_ref() { + if let Some(derefed_ty) = ty.as_reference_inner() { for (name, local) in ctx.locals.iter().sorted_by_key(|&(k, _)| k.clone()) { if name.as_str() == arg { return if local.ty(ctx.db) == derefed_ty { @@ -949,6 +950,25 @@ fn foo() {} fn bar() { let _ = [foo()$0]; } +"#, + ); + } + + #[test] + fn no_semicolon_in_match() { + check_edit( + r#"foo"#, + r#" +fn foo() {} +fn bar() { + match fo$0 {} +} +"#, + r#" +fn foo() {} +fn bar() { + match foo()$0 {} +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs index 9e0cec62e6418..943ff58219693 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs @@ -127,7 +127,7 @@ fn render( item.set_documentation(thing.docs(db)).set_deprecated(thing.is_deprecated(&ctx)); - let ty = thing.ty(db); + let ty = ctx.completion.rebase_ty(&thing.ty(db)); item.set_relevance(CompletionRelevance { type_match: compute_type_match(ctx.completion, &ty), // function is a misnomer here, this is more about constructor information diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs index 392ecbc302ae5..13a83eb216324 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs @@ -103,21 +103,21 @@ pub(crate) fn render_variant_pat( )) } -fn build_completion( - ctx: RenderContext<'_, '_>, +fn build_completion<'db>( + ctx: RenderContext<'_, 'db>, label: SmolStr, lookup: SmolStr, pat: String, def: impl HasDocs, - adt_ty: hir::Type<'_>, + adt_ty: hir::Type<'db>, // Missing in context of match statement completions is_variant_missing: bool, ) -> CompletionItem { let mut relevance = ctx.completion_relevance(); + let adt_ty = ctx.completion.rebase_ty(&adt_ty); - if is_variant_missing { - relevance.type_match = super::compute_type_match(ctx.completion, &adt_ty); - } + relevance.type_match = super::compute_type_match(ctx.completion, &adt_ty); + relevance.is_missing = is_variant_missing; let mut item = CompletionItem::new( CompletionItemKind::Binding, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs index f86af6cdcb7d8..8d1b88596982b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs @@ -32,7 +32,7 @@ pub(crate) fn render_record_lit( if let Some(local) = ctx.locals.get(&field_name) && local .ty(ctx.db) - .could_unify_with_deeply(ctx.db, &field.ty(ctx.db).to_type(ctx.db)) + .could_unify_with_deeply(ctx.db, &ctx.rebase_ty(&field.ty(ctx.db))) { f(&format_args!("{}{tab}", field_name.display(ctx.db, ctx.edition))) } else { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index c1205f9e189eb..e49afa66ad738 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -1055,6 +1055,7 @@ fn brr() { fn brr() fn() st YoloVariant YoloVariant st YoloVariant {…} YoloVariant { f: usize } + ev Yolo(…) (use HH::Yolo) Yolo(YoloVariant) bt u32 u32 kw const kw crate:: @@ -1157,6 +1158,12 @@ fn complete_module_colons() { r#"mod module {} fn foo() { module:: }"#, ); + check_edit( + "module", + r#"mod module {} fn foo() { $0foo::bar }"#, + r#"mod module {} fn foo() { module::foo::bar }"#, + ); + check_edit_with_config( CompletionConfig { add_colons_to_module: false, ..TEST_CONFIG }, "module", @@ -1165,6 +1172,27 @@ fn complete_module_colons() { ); } +#[test] +fn complete_module_exists_colons() { + check_edit( + "module", + r#"mod module {} fn foo() { $0::bar }"#, + r#"mod module {} fn foo() { module::bar }"#, + ); + + check_edit( + "module", + r#" +macro_rules! i { ($i:ident) => { $i::bar } } +mod module {} +fn foo() { i!($0) }"#, + r#" +macro_rules! i { ($i:ident) => { $i::bar } } +mod module {} +fn foo() { i!(module) }"#, + ); +} + #[test] fn else_completion_after_if() { check( @@ -2344,7 +2372,7 @@ fn main() { $0 } //- /std.rs crate:std -#[unstable(feature = "intrinsics")] +#[unstable(feature = "core_intrinsics")] pub mod intrinsics {} "#, expect![[r#" @@ -2935,6 +2963,84 @@ fn foo() { ); } +#[test] +fn flyimport_excluded_mod_items_from_flyimport() { + check_with_config( + CompletionConfig { + exclude_flyimport: vec![( + "ra_test_fixture::xpack::xmodule2".to_owned(), + AutoImportExclusionType::SubItems, + )], + ..TEST_CONFIG + }, + r#" +mod xpack { + mod xmodule1 { + pub struct XOther; + } + pub mod xmodule2 { + pub use super::xmodule1::*; + pub struct XStruct; + pub fn xfn() {} + } +} + +fn foo() { + x$0 +} + "#, + expect![[r#" + ct CONST Unit + en Enum Enum + fn foo() fn() + fn function() fn() + ma makro!(…) macro_rules! makro + md module:: + md xmodule2:: (use xpack::xmodule2) + md xpack:: + sc STATIC Unit + st Record Record + st Tuple Tuple + st Unit Unit + un Union Union + ev TupleV(…) TupleV(u32) + bt u32 u32 + kw async + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} + #[test] fn excluded_trait_method_is_excluded_from_path_completion() { check_with_config( @@ -3092,8 +3198,8 @@ fn bar() { } "#, expect![[r#" - en Option Option<{unknown}> - en Result Result<{unknown}, {unknown}> + en Option Option + en Result Result fn bar() fn() lc i i32 ma const_format_args!(…) macro_rules! const_format_args @@ -3901,3 +4007,105 @@ fn tryme(param: impl SubTrait) { "#]], ); } + +#[test] +fn can_complete_macro_path_inside_expansion() { + check( + r#" +macro_rules! bar { () => (); } +macro_rules! foo { ($i:ident) => { $i!() }; } +fn main() { + foo!(ba$0); +} + "#, + expect![[r#" + fn main() fn() + ma bar macro_rules! bar + ma foo macro_rules! foo + bt u32 u32 + kw const + kw crate:: + kw false + kw for + kw if + kw if let + kw loop + kw match + kw return + kw self:: + kw true + kw unsafe + kw while + kw while let + "#]], + ); +} + +#[test] +fn no_completion_for_autorefd_traits_in_path_mode() { + check( + r#" +//- minicore: clone +trait Test1 {} + +fn test(test: H) { + H::$0 +} + "#, + expect![""], + ); +} + +#[test] +fn imported_enum_variant_has_lower_priority() { + check( + r#" +pub struct String {} +mod foo { + pub enum Foo { String } +} +fn main() { + Strin$0 +} + "#, + expect![[r#" + fn main() fn() + md foo:: + st String String + ev String (use foo::Foo::String) String + bt u32 u32 + kw async + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 231623a42fc65..45db8ecfc6a5f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -781,9 +781,9 @@ fn main() { } "#, expect![[r#" - me random_method(…) (use dep::test_mod::TestTrait) fn(&self) DEPRECATED ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8 DEPRECATED fn weird_function() (use dep::test_mod::TestTrait) fn() DEPRECATED + me random_method(…) (use dep::test_mod::TestTrait) fn(&self) DEPRECATED "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs index 9826a8ed7b33a..3f3a6f4cf574d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs @@ -14,7 +14,7 @@ struct Foo<'lt, T, const C: usize> where $0 {} en Enum Enum ma makro!(…) macro_rules! makro md module:: - st Foo<…> Foo<'_, {unknown}, _> + st Foo<…> Foo<'lt, T, C> st Record Record st Tuple Tuple st Unit Unit @@ -91,7 +91,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {} en Enum Enum ma makro!(…) macro_rules! makro md module:: - st Foo<…> Foo<'_, {unknown}, _> + st Foo<…> Foo<'lt, T, C> st Record Record st Tuple Tuple st Unit Unit diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs index ddb9294469007..aecf8eb651e1a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs @@ -157,6 +157,8 @@ fn foo(f: Struct) { fn in_functional_update() { cov_mark::check!(functional_update); + // FIXME: This should filter out all completions that do not have the type `Foo` + // I think maybe ranking by type match is enough check( r#" //- minicore:default @@ -210,7 +212,6 @@ fn main() { #[test] fn functional_update_no_dot() { cov_mark::check!(functional_update_field); - // FIXME: This should filter out all completions that do not have the type `Foo` check( r#" //- minicore:default @@ -280,6 +281,48 @@ fn main() { ); } +#[test] +fn functional_update_non_last() { + check( + r#" +//- minicore:default +struct Foo { foo1: u32, foo2: u32 } +impl Default for Foo { + fn default() -> Self { loop {} } +} + +fn main() { + let thing = 1; + let foo = Foo { foo1: 0, foo2: 0 }; + let foo2 = Foo { $0 thing } +} +"#, + expect![[r#" + fd foo1 u32 + fd foo2 u32 + "#]], + ); + check( + r#" +//- minicore:default +struct Foo { foo1: u32, foo2: u32 } +impl Default for Foo { + fn default() -> Self { loop {} } +} + +fn main() { + let thing = 1; + let foo = Foo { foo1: 0, foo2: 0 }; + let foo2 = Foo { $0thing } +} +"#, + expect![[r#" + fd foo1 u32 + fd foo2 u32 + "#]], + ); +} + #[test] fn functional_update_fields_completion() { // Complete fields before functional update `..` diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs index 24080334ae9b9..ad058901c0473 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs @@ -15,8 +15,8 @@ struct Foo<'lt, T, const C: usize> { en Enum Enum ma makro!(…) macro_rules! makro md module:: - sp Self Foo<'_, {unknown}, _> - st Foo<…> Foo<'_, {unknown}, _> + sp Self Foo<'lt, T, C> + st Foo<…> Foo<'lt, T, C> st Record Record st Tuple Tuple st Unit Unit @@ -44,8 +44,8 @@ struct Foo<'lt, T, const C: usize>(f$0); en Enum Enum ma makro!(…) macro_rules! makro md module:: - sp Self Foo<'_, {unknown}, _> - st Foo<…> Foo<'_, {unknown}, _> + sp Self Foo<'lt, T, C> + st Foo<…> Foo<'lt, T, C> st Record Record st Tuple Tuple st Unit Unit @@ -409,7 +409,7 @@ const FOO: $0 = Foo(2); en Enum Enum ma makro!(…) macro_rules! makro md module:: - st Foo<…> Foo<{unknown}> + st Foo<…> Foo st Record Record st Tuple Tuple st Unit Unit @@ -438,7 +438,7 @@ static FOO: $0 = Foo(2); en Enum Enum ma makro!(…) macro_rules! makro md module:: - st Foo<…> Foo<{unknown}> + st Foo<…> Foo st Record Record st Tuple Tuple st Unit Unit @@ -629,7 +629,7 @@ fn foo<'lt, T, const C: usize>() { en Enum Enum ma makro!(…) macro_rules! makro md module:: - st Foo<…> Foo<{unknown}> + st Foo<…> Foo st Record Record st Tuple Tuple st Unit Unit diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index 264bb4fa814dc..683adff6154af 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -572,13 +572,6 @@ pub const DEFAULT_LINTS: &[Lint] = &[ warn_since: None, deny_since: None, }, - Lint { - label: "inline_always_mismatching_target_features", - description: r##"detects when a function annotated with `#[inline(always)]` and `#[target_feature(enable = "..")]` is inlined into a caller without the required target feature"##, - default_severity: Severity::Warning, - warn_since: None, - deny_since: None, - }, Lint { label: "inline_no_sanitize", description: r##"detects incompatible use of `#[inline(always)]` and `#[sanitize(... = "off")]`"##, @@ -2451,6 +2444,22 @@ The tracking issue for this feature is: [#111889] [#111889]: https://github.com/rust-lang/rust/issues/111889 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "abi_swift", + description: r##"# `abi_swift` + +Allows `extern "Swift" fn()`. + +The tracking issue for this feature is: [#156481] + +[#156481]: https://github.com/rust-lang/rust/issues/156481 + ------------------------ "##, default_severity: Severity::Allow, @@ -3925,22 +3934,6 @@ The tracking issue for this feature is: [#151828] [#151828]: https://github.com/rust-lang/rust/issues/151828 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "bool_to_result", - description: r##"# `bool_to_result` - - - -The tracking issue for this feature is: [#142748] - -[#142748]: https://github.com/rust-lang/rust/issues/142748 - ------------------------ "##, default_severity: Severity::Allow, @@ -4638,22 +4631,6 @@ The tracking issue for this feature is: [#94039] [#94039]: https://github.com/rust-lang/rust/issues/94039 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "cfg_target_has_atomic_equal_alignment", - description: r##"# `cfg_target_has_atomic_equal_alignment` - -Allows `cfg(target_has_atomic_equal_alignment = "...")`. - -The tracking issue for this feature is: [#93822] - -[#93822]: https://github.com/rust-lang/rust/issues/93822 - ------------------------ "##, default_severity: Severity::Allow, @@ -4845,6 +4822,22 @@ The tracking issue for this feature is: [#148519] [#148519]: https://github.com/rust-lang/rust/issues/148519 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "clflushopt_target_feature", + description: r##"# `clflushopt_target_feature` + +The `clflushopt` target feature on x86. + +The tracking issue for this feature is: [#157096] + +[#157096]: https://github.com/rust-lang/rust/issues/157096 + ------------------------ "##, default_severity: Severity::Allow, @@ -11160,6 +11153,22 @@ The tracking issue for this feature is: [#147456] [#147456]: https://github.com/rust-lang/rust/issues/147456 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "move_expr", + description: r##"# `move_expr` + +Allows `move(expr)` in closures. + +The tracking issue for this feature is: [#155050] + +[#155050]: https://github.com/rust-lang/rust/issues/155050 + ------------------------ "##, default_severity: Severity::Allow, @@ -11256,6 +11265,22 @@ The tracking issue for this feature is: [#123076] [#123076]: https://github.com/rust-lang/rust/issues/123076 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "mut_restriction", + description: r##"# `mut_restriction` + +Allows `mut(crate) field: Type` restrictions. + +The tracking issue for this feature is: [#105077] + +[#105077]: https://github.com/rust-lang/rust/issues/105077 + ------------------------ "##, default_severity: Severity::Allow, @@ -12062,6 +12087,20 @@ The tracking issue for this feature is: [#118485] [#118485]: https://github.com/rust-lang/rust/issues/118485 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "os_str_split_at", + description: r##"# `os_str_split_at` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + ------------------------ "##, default_severity: Severity::Allow, @@ -12250,22 +12289,6 @@ The tracking issue for this feature is: [#153328] [#153328]: https://github.com/rust-lang/rust/issues/153328 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "path_is_empty", - description: r##"# `path_is_empty` - - - -The tracking issue for this feature is: [#148494] - -[#148494]: https://github.com/rust-lang/rust/issues/148494 - ------------------------ "##, default_severity: Severity::Allow, @@ -13460,6 +13483,22 @@ The tracking issue for this feature is: [#138099] [#138099]: https://github.com/rust-lang/rust/issues/138099 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "return_address", + description: r##"# `return_address` + + + +The tracking issue for this feature is: [#154966] + +[#154966]: https://github.com/rust-lang/rust/issues/154966 + ------------------------ "##, default_severity: Severity::Allow, @@ -13934,6 +13973,22 @@ The tracking issue for this feature is: [#56975] [#56975]: https://github.com/rust-lang/rust/issues/56975 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "share_trait", + description: r##"# `share_trait` + + + +The tracking issue for this feature is: [#156756] + +[#156756]: https://github.com/rust-lang/rust/issues/156756 + ------------------------ "##, default_severity: Severity::Allow, @@ -14920,22 +14975,6 @@ The tracking issue for this feature is: [#95439] [#95439]: https://github.com/rust-lang/rust/issues/95439 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "target_feature_inline_always", - description: r##"# `target_feature_inline_always` - -Allows the use of target_feature when a function is marked inline(always). - -The tracking issue for this feature is: [#145574] - -[#145574]: https://github.com/rust-lang/rust/issues/145574 - ------------------------ "##, default_severity: Severity::Allow, @@ -15676,22 +15715,6 @@ This feature has no tracking issue, and is therefore likely internal to the comp -The tracking issue for this feature is: [#37572] - -[#37572]: https://github.com/rust-lang/rust/issues/37572 - ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "trusted_len_next_unchecked", - description: r##"# `trusted_len_next_unchecked` - - - The tracking issue for this feature is: [#37572] [#37572]: https://github.com/rust-lang/rust/issues/37572 @@ -16299,6 +16322,22 @@ The tracking issue for this feature is: [#89517] [#89517]: https://github.com/rust-lang/rust/issues/89517 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "unix_kill_process_group", + description: r##"# `unix_kill_process_group` + + + +The tracking issue for this feature is: [#156537] + +[#156537]: https://github.com/rust-lang/rust/issues/156537 + ------------------------ "##, default_severity: Severity::Allow, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index e0501b5e44ea3..6edc550b331a1 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -814,13 +814,13 @@ fn trait_applicable_items<'db>( let mut deref_chain = trait_candidate.receiver_ty.autoderef(db).collect::>(); // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!) if let Some((ty, _len)) = deref_chain.last().and_then(|ty| ty.as_array(db)) { - let slice = Type::new_slice(ty); + let slice = Type::new_slice(db, ty); deref_chain.push(slice); } deref_chain .into_iter() .flat_map(|ty| { - let fingerprint = ty.fingerprint_for_trait_impl()?; + let fingerprint = ty.fingerprint_for_trait_impl(db)?; let mut crates = vec![]; if let Some(adt) = ty.as_adt() { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 6180e3186cab2..2b1525e2b289c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -180,6 +180,10 @@ impl SourceDatabase for RootDatabase { fn nonce_and_revision(&self) -> (Nonce, salsa::Revision) { (self.nonce, salsa::plumbing::ZalsaDatabase::zalsa(self).current_revision()) } + + fn line_column(&self, file: FileId, offset: syntax::TextSize) -> Result<(u32, u32), ()> { + line_index(self, file).try_line_col(offset).map(|lc| (lc.line, lc.col)).ok_or(()) + } } impl Default for RootDatabase { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs index 11ba815dab2ec..e30b21c139fad 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -216,12 +216,7 @@ pub fn walk_ty(ty: &ast::Type, cb: &mut dyn FnMut(ast::Type) -> bool) { preorder.skip_subtree(); cb(ty); } - Some(ty) => - { - #[expect( - clippy::collapsible_match, - reason = "it won't compile due to exhaustiveness" - )] + Some(ty) => { if cb(ty) { preorder.skip_subtree(); } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs index 76fea5c2623ca..15920595a8271 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -412,7 +412,7 @@ fn from_type( edition: Edition, ) -> Option { let ty = sema.type_of_expr(expr)?.adjusted(); - let ty = ty.remove_ref().unwrap_or(ty); + let ty = ty.strip_reference(); name_of_type(&ty, sema.db, edition) } @@ -445,7 +445,7 @@ fn name_of_type<'db>( return None; } name - } else if let Some(inner_ty) = ty.remove_ref() { + } else if let Some((inner_ty, _)) = ty.as_reference() { return name_of_type(&inner_ty, db, edition); } else if let Some(inner_ty) = ty.as_slice() { return Some(sequence_name(Some(&inner_ty), db, edition)); 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 1b20a574bd1b1..a84f75cb4e053 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 @@ -38,7 +38,7 @@ "Enum", ), is_alias: false, - is_assoc: true, + is_assoc: false, is_import: false, do_not_complete: Yes, _marker: PhantomData<&()>, @@ -110,7 +110,7 @@ "Enum", ), is_alias: false, - is_assoc: true, + is_assoc: false, is_import: false, do_not_complete: Yes, _marker: PhantomData<&()>, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/array_pattern_without_fixed_length.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/array_pattern_without_fixed_length.rs new file mode 100644 index 0000000000000..e7d0868350f31 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/array_pattern_without_fixed_length.rs @@ -0,0 +1,46 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: array-pattern-without-fixed-length +// +// This diagnostic is triggered when a rest array pattern is matched against an +// array with a non-constant length. +pub(crate) fn array_pattern_without_fixed_length( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::ArrayPatternWithoutFixedLength, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0730"), + "cannot pattern-match on an array without a fixed length", + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn array_pattern_without_fixed_length() { + check_diagnostics( + r#" +fn f(arr: [u8; N]) { + let [_head, _tail @ ..] = arr; + //^^^^^^^^^^^^^^^^^^^ error: cannot pattern-match on an array without a fixed length +} +"#, + ); + } + + #[test] + fn fixed_length_array_pattern_is_ok() { + check_diagnostics( + r#" +fn f(arr: [u8; 3]) { + let [_head, _tail @ ..] = arr; +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs index b7265c47b6fb2..43eb4ab8e03a2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs @@ -126,14 +126,13 @@ fn foo() { #[test] fn value_break_in_for_loop() { - // FIXME: the error is correct, but the message is terrible check_diagnostics( r#" //- minicore: iterator fn test() { for _ in [()] { break 3; - // ^ error: expected (), found i32 + // ^^^^^^^ error: can't break with a value in this position } } "#, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_be_dereferenced.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_be_dereferenced.rs new file mode 100644 index 0000000000000..6c9726f8af2ee --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_be_dereferenced.rs @@ -0,0 +1,74 @@ +use hir::HirDisplay; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: cannot-be-dereferenced +// +// This diagnostic is triggered if the operand of a dereference expression +// cannot be dereferenced. +pub(crate) fn cannot_be_dereferenced( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::CannotBeDereferenced<'_>, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0614"), + format!( + "type `{}` cannot be dereferenced", + d.found.display(ctx.sema.db, ctx.display_target) + ), + d.expr.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn cannot_be_dereferenced() { + check_diagnostics( + r#" +fn f() { + let x = 1i32; + let _ = *x; + //^^ error: type `i32` cannot be dereferenced +} +"#, + ); + } + + #[test] + fn allows_reference_deref() { + check_diagnostics( + r#" +fn f(x: &i32) { + let _ = *x; +} +"#, + ); + } + + #[test] + fn allows_overloaded_deref() { + check_diagnostics( + r#" +//- minicore: deref +struct Wrapper(i32); + +impl core::ops::Deref for Wrapper { + type Target = i32; + + fn deref(&self) -> &i32 { + &self.0 + } +} + +fn f(x: Wrapper) { + let _ = *x; +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_implicitly_deref_trait_object.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_implicitly_deref_trait_object.rs new file mode 100644 index 0000000000000..eca14144dd1c5 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_implicitly_deref_trait_object.rs @@ -0,0 +1,53 @@ +use hir::HirDisplay; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: cannot-implicitly-deref-trait-object +// +// This diagnostic is triggered when a pointer to a trait object is implicitly +// dereferenced by a pattern. +pub(crate) fn cannot_implicitly_deref_trait_object( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::CannotImplicitlyDerefTraitObject<'_>, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0033"), + format!( + "type `{}` cannot be dereferenced", + d.found.display(ctx.sema.db, ctx.display_target) + ), + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn trait_object_pattern_deref() { + check_diagnostics( + r#" +trait Trait {} + +fn f(x: &dyn Trait) { + let &ref _y = x; + //^^^^^^^ error: type `&(dyn Trait + 'static)` cannot be dereferenced +} +"#, + ); + } + + #[test] + fn allows_sized_ref_pattern_deref() { + check_diagnostics( + r#" +fn f(x: &i32) { + let &ref _y = x; +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_index_into.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_index_into.rs new file mode 100644 index 0000000000000..1e42313b4a887 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_index_into.rs @@ -0,0 +1,77 @@ +use hir::HirDisplay; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: cannot-index-into +// +// This diagnostic is triggered if indexing is used on a type that cannot be +// indexed. +pub(crate) fn cannot_index_into( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::CannotIndexInto<'_>, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0608"), + format!( + "cannot index into a value of type `{}`", + d.found.display(ctx.sema.db, ctx.display_target) + ), + d.expr.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn cannot_index_into() { + check_diagnostics( + r#" +//- minicore: index +fn f() { + let x = 1i32; + let _ = x[0]; + //^^^^ error: cannot index into a value of type `i32` +} +"#, + ); + } + + #[test] + fn allows_array_indexing() { + check_diagnostics( + r#" +//- minicore: index, slice +fn f() { + let x = [1i32, 2]; + let _ = x[0]; +} +"#, + ); + } + + #[test] + fn allows_overloaded_indexing() { + check_diagnostics( + r#" +//- minicore: index +struct Bag(i32); + +impl core::ops::Index for Bag { + type Output = i32; + + fn index(&self, _: usize) -> &Self::Output { + &self.0 + } +} + +fn f(x: Bag) { + let _ = x[0]; +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/explicit_drop_method_use.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/explicit_drop_method_use.rs new file mode 100644 index 0000000000000..b02bccf5a533c --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/explicit_drop_method_use.rs @@ -0,0 +1,426 @@ +use either::Either; +use hir::InFile; +use ide_db::assists::Assist; +use ide_db::source_change::{SourceChange, SourceChangeBuilder}; +use ide_db::text_edit::TextEdit; +use itertools::Itertools; +use syntax::{ + AstNode, AstPtr, + ast::{self, HasArgList}, +}; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_range, fix}; + +// Diagnostic: explicit-drop-method-use +// +// This diagnostic is triggered when the `Drop::drop` method is called (or named) explicitly. +pub(crate) fn explicit_drop_method_use( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::ExplicitDropMethodUse, +) -> Diagnostic { + match d.expr_or_path { + Either::Left(expr) => { + let display_range = adjusted_display_range(ctx, expr, &|node| { + Some(node.name_ref()?.syntax().text_range()) + }); + Diagnostic::new( + DiagnosticCode::RustcHardError("E0040"), + "explicit use of destructor method", + display_range, + ) + .stable() + .with_main_node(expr.map(Into::into)) + .with_fixes(fix_method_call(ctx, expr)) + } + Either::Right(path) => Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0040"), + "explicit use of destructor method", + path.map(Into::into), + ) + .stable() + .with_fixes(fix_path(ctx, path)), + } +} + +fn fix_method_call( + ctx: &DiagnosticsContext<'_, '_>, + mcall_ptr: InFile>, +) -> Option> { + if mcall_ptr.file_id.is_macro() { + // TODO: handle macro calls. Rough plan: + // 1. upmap the range of the receiver and the range of the whole call + // 2. delete everything outside the receiver and replace it with `drop(...)`, using range edits only. + return None; + } + + let db = ctx.db(); + + let file_id = mcall_ptr.file_id; + let mcall = mcall_ptr.to_node(db); + let range = mcall.syntax().text_range(); + + // `mcall` is `foo.drop()` -- extract the receiver, and wrap it in `drop()` + // NOTE: it could theoretically be `(&mut foo).drop()` instead, in which case the fix + // below would be incorrect, as it'd result in `drop((&mut foo))` instead of `drop(foo)` + // -- but we don't bother to deal with that case. + let recv = mcall.receiver()?; + + let mut builder = SourceChangeBuilder::new(file_id.original_file(db).file_id(db)); + let editor = builder.make_editor(mcall.syntax()); + let make = editor.make(); + let new_call = + make.expr_call(make.expr_path(make.path_from_text("drop")), make.arg_list([recv])); + builder.replace_ast(ast::Expr::MethodCallExpr(mcall), ast::Expr::CallExpr(new_call)); + let source_change = builder.finish(); + Some(vec![fix("use-drop-function", "Use `drop` function", source_change, range)]) +} + +fn fix_path( + ctx: &DiagnosticsContext<'_, '_>, + path_ptr: InFile>, +) -> Option> { + let db = ctx.db(); + + let file_id = path_ptr.file_id; + let path = path_ptr.to_node(db); + + if let Some(call) = + path.syntax().parent().and_then(|it| it.parent()).and_then(ast::CallExpr::cast) + { + if file_id.is_macro() { + // TODO: make this work in macros? Might not be worth it, as this is a niche way to trigger this + // already niche error + return None; + } + + // `call` is `Drop::drop(&mut foo)` -- extract the arg, and wrap it in `drop()` + let arg_list = call.arg_list()?; + let ref_recv = arg_list.args().exactly_one().ok()?; + let ast::Expr::RefExpr(ref_recv) = ref_recv else { + return None; + }; + let recv = ref_recv.expr()?; + + let range = call.syntax().text_range(); + + let mut builder = SourceChangeBuilder::new(file_id.original_file(db).file_id(db)); + let editor = builder.make_editor(call.syntax()); + let make = editor.make(); + let new_call = + make.expr_call(make.expr_path(make.path_from_text("drop")), make.arg_list([recv])); + builder.replace_ast(call, new_call); + let source_change = builder.finish(); + Some(vec![fix("use-drop-function", "Use `drop` function", source_change, range)]) + } else { + // `path` could be the `Foo::drop` in `let d = Foo::drop;` + // -- replace the path with `drop` + + let range = InFile::new(file_id, path.syntax().text_range()) + .original_node_file_range_rooted_opt(db)?; + + let edit = TextEdit::replace(range.range, "drop".to_owned()); + let source_change = SourceChange::from_text_edit(range.file_id.file_id(db), edit); + Some(vec![fix("use-drop-function", "Use `drop` function", source_change, range.range)]) + } +} + +#[cfg(test)] +mod tests { + use crate::tests::{ + check_diagnostics, check_diagnostics_with_disabled, check_fix, check_fix_with_disabled, + }; + + #[test] + fn method_call_diagnostic() { + check_diagnostics( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + a.drop(); + // ^^^^ 💡 error: explicit use of destructor method +} +"#, + ); + } + + #[test] + fn method_call_fix() { + check_fix( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + a.drop$0(); +} +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + drop(a); +} +"#, + ); + } + + #[test] + fn qualified_call_1_diagnostic() { + check_diagnostics( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + A::drop(&mut a); + // ^^^^^^^ 💡 error: explicit use of destructor method +} +"#, + ); + } + + #[test] + fn qualified_call_1_fix() { + check_fix( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + A::drop(&mut a$0); +} +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + drop(a); +} +"#, + ) + } + + #[test] + fn qualified_call_2_diagnostic() { + check_diagnostics( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + Drop::drop(&mut a); + // ^^^^^^^^^^ 💡 error: explicit use of destructor method +} +"#, + ); + } + + #[test] + fn qualified_call_2_fix() { + check_fix( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + Drop::drop(&mut a$0); +} +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + drop(a); +} +"#, + ) + } + + #[test] + fn fully_qualified_call_diagnostic() { + check_diagnostics( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + ::drop(&mut a); + // ^^^^^^^^^^^^^^^^^ 💡 error: explicit use of destructor method +} +"#, + ); + } + + #[test] + fn fully_qualified_call_fix() { + check_fix( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + ::drop(&mut a$0); +} +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + drop(a); +} +"#, + ) + } + + #[test] + fn path_diagnostic() { + check_diagnostics_with_disabled( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + let d = A::drop; + // ^^^^^^^ 💡 error: explicit use of destructor method + d(&mut a); +} +"#, + // Because of the error, the code isn't analyzed further (?), and so `d` is warned on as unused. + // Arguably a bug in r-a (rustc doesn't emit a warning in this case) + // FIXME: remove this once r-a no longer warns + &["unused_variables"], + ); + } + + #[test] + // NOTE: Here, the fix is not completely correct, as it doesn't replace `d(&mut a)` with `d(a)`. + // Oh well, rustc doesn't either + fn path_fix() { + check_fix_with_disabled( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + let d = A::drop$0; + d(&mut a); +} +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + let d = drop; + d(&mut a); +} +"#, + // Because of the error, the code isn't analyzed further (?), and so `d` is warned on as unused. + // Arguably a bug in r-a (rustc doesn't emit a warning in this case) + // FIXME: remove this once r-a no longer warns + &["unused_variables"], + ); + } + + #[test] + // NOTE: Here, the fix is not completely correct, as it doesn't replace `d(&mut a)` with `d(a)`. + // Oh well, rustc doesn't either + fn path_fix_in_macro() { + check_fix( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +macro_rules! main { + ($e:expr) => { + fn main() { $e } + } +} + +main!{{ + let mut a = A; + let d = A::drop$0; + d(&mut a); +}}; +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +macro_rules! main { + ($e:expr) => { + fn main() { $e } + } +} + +main!{{ + let mut a = A; + let d = drop; + d(&mut a); +}}; +"#, + ); + } + + #[test] + fn std_mem_drop() { + check_diagnostics( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(a: A) { + drop(a); +} +"#, + ); + } + + #[test] + fn inherent_drop_method() { + check_diagnostics( + r#" +struct A; +impl A { fn drop(&mut self) {} } + +fn main(mut a: A) { + a.drop(); +} +"#, + ); + } + + #[test] + fn custom_trait_drop_method() { + check_diagnostics( + r#" +struct A; +trait MyDrop { fn drop(&mut self); } +impl MyDrop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + a.drop(); +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs new file mode 100644 index 0000000000000..f8d3d80d62f6b --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs @@ -0,0 +1,67 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: fru-in-destructuring-assignment +// +// This diagnostic is triggered when a destructuring assignment contains functional record update +pub(crate) fn fru_in_destructuring_assignment( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::FruInDestructuringAssignment, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::SyntaxError, + "functional record updates are not allowed in destructuring assignments", + d.node.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_diagnostics, check_diagnostics_with_disabled}; + + #[test] + fn spread_variable() { + check_diagnostics_with_disabled( + r#" +struct Foo { bar: u32, baz: u32 } +fn test(f: Foo, g: Foo, mut bar: u32, mut baz: u32) { + Foo { ..g } = f; + // ^ error: functional record updates are not allowed in destructuring assignments + Foo { bar, ..g } = f; + // ^ error: functional record updates are not allowed in destructuring assignments + Foo { bar, baz, ..g } = f; + // ^ error: functional record updates are not allowed in destructuring assignments +} + "#, + // We don't end up using neither `bar` nor `baz` + &["unused_variables"], + ); + } + + #[test] + fn spread_default() { + check_diagnostics( + r#" +struct Foo { bar: u32, baz: u32 } +fn test(f: Foo) { + Foo { ..Default::default() } = f; + // ^^^^^^^^^^^^^^^^^^ error: functional record updates are not allowed in destructuring assignments +} + "#, + ); + } + + #[test] + fn spread_struct() { + check_diagnostics( + r#" +struct Foo { bar: u32, baz: u32 } +fn test(f: Foo) { + Foo { ..Foo { bar: 0, baz: 0 } } = f; + // ^^^^^^^^^^^^^^^^^^^^^^ error: functional record updates are not allowed in destructuring assignments +} + "#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_range_pat_type.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_range_pat_type.rs new file mode 100644 index 0000000000000..465f59559d294 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_range_pat_type.rs @@ -0,0 +1,55 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: invalid-range-pat-type +// +// This diagnostic is triggered when a range pattern is used with a type that +// is neither `char` nor numeric. +pub(crate) fn invalid_range_pat_type( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::InvalidRangePatType, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0029"), + "only `char` and numeric types are allowed in range patterns", + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn bool_range_pattern() { + check_diagnostics( + r#" +fn f(x: bool) { + match x { + false..=true => {} + //^^^^^^^^^^^^ error: only `char` and numeric types are allowed in range patterns + } +} +"#, + ); + } + + #[test] + fn numeric_and_char_range_patterns() { + check_diagnostics( + r#" +fn f(x: u8, c: char) { + match x { + 0..=9 => {} + _ => {} + } + match c { + 'a'..='z' => {} + _ => {} + } +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs index b6571e02efbe6..7acbcad22c5a3 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs @@ -327,4 +327,21 @@ fn it_works() { "#, ); } + + #[test] + fn unimplemented_builtin_macro() { + check_diagnostics( + r#" +#[rustc_builtin_macro] +macro_rules! unimplemented_builtin_macro { + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: unimplemented built-in macro + () => {}; +} + + #[unimplemented_builtin_macro] +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this built-in macro is not implemented +struct Foo; + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/method_call_illegal_sized_bound.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/method_call_illegal_sized_bound.rs new file mode 100644 index 0000000000000..0a0ac8627c3d1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/method_call_illegal_sized_bound.rs @@ -0,0 +1,78 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: method-call-illegal-sized-bound +// +// This diagnostic is triggered when a method is called on a trait-object +// receiver but the method's predicates require `Self: Sized`, which the +// trait object cannot satisfy. +pub(crate) fn method_call_illegal_sized_bound( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::MethodCallIllegalSizedBound, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0277"), + "the method cannot be invoked on a trait object because its `Self: Sized` bound is not satisfied", + d.call_expr.map(|it| it.into()), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn sized_bound_method_on_trait_object_errors() { + check_diagnostics( + r#" +//- minicore: sized +trait Foo { + fn cant_call(&self) where Self: Sized; +} + +fn f(x: &dyn Foo) { + x.cant_call(); + //^^^^^^^^^^^^^ error: the method cannot be invoked on a trait object because its `Self: Sized` bound is not satisfied +} +"#, + ); + } + + #[test] + fn method_without_sized_bound_on_trait_object_does_not_error() { + check_diagnostics( + r#" +//- minicore: sized +trait Foo { + fn dyn_safe(&self); +} + +fn f(x: &dyn Foo) { + x.dyn_safe(); +} +"#, + ); + } + + #[test] + fn sized_bound_method_on_concrete_type_does_not_error() { + check_diagnostics( + r#" +//- minicore: sized +trait Foo { + fn cant_dispatch(&self) where Self: Sized; +} + +struct S; +impl Foo for S { + fn cant_dispatch(&self) {} +} + +fn f(s: &S) { + s.cant_dispatch(); +} +"#, + ); + } +} 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 b4ddb239c8cc4..d279e9b4e8da0 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 @@ -425,17 +425,20 @@ fn main() { fn raw_deref_on_union_field() { check_diagnostics( r#" +//- minicore: index, slice +#![allow(unused_variables)] + fn main() { union U { a: u8 } - let x = U { a: 3 }; + let mut x = U { a: 3 }; let a = &raw mut x.a; union U1 { - a: u8 + a: usize } let x = U1 { a: 3 }; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 31becd1d74960..49aa6d7bd9469 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -1313,7 +1313,7 @@ fn main() { fn regression_20662() { check_diagnostics( r#" -//- minicore: index +//- minicore: index, slice pub trait A: core::ops::IndexMut { type T: A; } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs new file mode 100644 index 0000000000000..b56619612d25b --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs @@ -0,0 +1,64 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: mutable-ref +// +// This diagnostic is triggered when binding is taken that is both mutable and by-reference. +pub(crate) fn mutable_ref_binding( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::MutableRefBinding, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0658"), + "bindings cannot be both mutable and by-reference by default in 2024 edition. add experimental #![feature(mut_ref)] for this functionality", + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn mutable_ref_binding_missing_feature() { + check_diagnostics( + r#" +//- minicore: option +#![feature(ref_pat_eat_one_layer_2024)] +struct TestStruct { + val: i32 +} +fn main() { + let opt_ref = &Some(TestStruct {val: 1}); + + if let Some(mut x) = opt_ref { + //^^^^^ error: bindings cannot be both mutable and by-reference by default in 2024 edition. add experimental #![feature(mut_ref)] for this functionality + x = &TestStruct{val: 5}; + } +} +"#, + ); + } + + #[test] + fn mutable_ref_binding_with_feature() { + check_diagnostics( + r#" +//- minicore: option +#![feature(ref_pat_eat_one_layer_2024)] +#![feature(mut_ref)] +struct TestStruct { + val: i32 +} +fn main() { + let opt_ref = &Some(TestStruct{val: 1}); + + if let Some(mut x) = opt_ref { + x = &TestStruct{val: 5}; + } +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs index 7959fddc757f4..3dd6744b05bdb 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -116,14 +116,13 @@ fn missing_record_expr_field_fixes( let mut new_field = new_field.to_string(); // FIXME: check submodule instead of FileId - if usage_file_id != def_file_id && !matches!(def_id, hir::Variant::EnumVariant(_)) { - new_field = format!("pub(crate) {new_field}"); - } - new_field = format!("\n{indent}{new_field}{postfix}"); - - if needs_comma { - new_field = format!(",{new_field}"); - } + let vis = if usage_file_id != def_file_id && !matches!(def_id, hir::Variant::EnumVariant(_)) { + "pub(crate) " + } else { + "" + }; + let comma = if needs_comma { "," } else { "" }; + new_field = format!("{comma}\n{indent}{vis}{new_field}{postfix}"); let source_change = SourceChange::from_text_edit( def_file_id.file_id(sema.db), diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_pat.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_pat.rs new file mode 100644 index 0000000000000..ea587e6037db1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_pat.rs @@ -0,0 +1,79 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: non-exhaustive-record-pat +// +// This diagnostic is triggered if a record pattern destructures a `#[non_exhaustive]` +// struct or enum variant from another crate without `..`. +pub(crate) fn non_exhaustive_record_pat( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::NonExhaustiveRecordPat, +) -> Diagnostic { + let item = match d.variant { + hir::Variant::Struct(_) => "struct", + hir::Variant::Union(_) => "union", + hir::Variant::EnumVariant(_) => "variant", + }; + + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0638"), + format!("`..` required with {item} marked as non-exhaustive"), + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn reports_external_non_exhaustive_struct_pattern_without_rest() { + check_diagnostics( + r#" +//- /lib.rs crate:lib +#[non_exhaustive] +pub struct S { + pub field: u32, +} + +fn local_ok(s: S) { + let S { field } = s; + let _ = field; +} + +//- /main.rs crate:main deps:lib +fn main(s: lib::S) { + let lib::S { field } = s; + //^^^^^^^^^^^^^^^^ error: `..` required with struct marked as non-exhaustive + let _ = field; +} +"#, + ); + } + + #[test] + fn reports_external_non_exhaustive_variant_pattern_without_rest() { + check_diagnostics( + r#" +//- /lib.rs crate:lib +pub enum E { + #[non_exhaustive] + V { field: u32 }, +} + +fn local_ok(e: E) { + let E::V { field } = e; + let _ = field; +} + +//- /main.rs crate:main deps:lib +fn main(e: lib::E) { + let lib::E::V { field } = e; + //^^^^^^^^^^^^^^^^^^^ error: `..` required with variant marked as non-exhaustive + let _ = field; +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs index 459ec175b158f..8a54834361a60 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs @@ -20,6 +20,24 @@ pub(crate) fn pattern_arg_in_extern_fn( mod tests { use crate::tests::check_diagnostics; + #[test] + fn ident_pattern_allowed() { + check_diagnostics( + r#" +unsafe extern { fn foo(a: i32); } + "#, + ); + } + + #[test] + fn wildcard_pattern_allowed() { + check_diagnostics( + r#" +unsafe extern { fn foo(_: i32); } + "#, + ); + } + #[test] fn tuple_pattern() { check_diagnostics( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs index 5f5e155bd79ea..1ffef25b50605 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs @@ -149,10 +149,18 @@ impl Trait for () { type Item = (); fn item() {} } +impl Trait for Adt {} + //^^^^^ error: not all trait items implemented, missing: `type Item`, `fn item` // Items with Self: Sized bound not required to be implemented for unsized types. impl Trait for str {} impl Trait for dyn OtherTrait {} +impl Trait for Adt<[i32]> {} +impl Trait for Slice {} +impl Trait for (str,) {} + +struct Adt(i32, T); +struct Slice([T]); "#, ) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 250c692d16f80..5a1856c89df3a 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -8,7 +8,7 @@ use ide_db::{ use syntax::{ AstNode, AstPtr, TextSize, ast::{ - self, BlockExpr, Expr, ExprStmt, HasArgList, + self, BlockExpr, Expr, ExprStmt, HasArgList, RefExpr, edit::{AstNodeEdit, IndentLevel}, }, }; @@ -51,10 +51,10 @@ pub(crate) fn type_mismatch( format!( "expected {}, found {}", d.expected - .display(ctx.sema.db, ctx.display_target) + .display(ctx.db(), ctx.display_target) .with_closure_style(ClosureStyle::ClosureWithId), d.actual - .display(ctx.sema.db, ctx.display_target) + .display(ctx.db(), ctx.display_target) .with_closure_style(ClosureStyle::ClosureWithId), ), display_range, @@ -69,7 +69,7 @@ fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>) -> Option< if let Some(expr_ptr) = d.expr_or_pat.value.cast::() { let expr_ptr = &InFile { file_id: d.expr_or_pat.file_id, value: expr_ptr }; - add_reference(ctx, d, expr_ptr, &mut fixes); + add_or_fix_reference(ctx, d, expr_ptr, &mut fixes); add_missing_ok_or_some(ctx, d, expr_ptr, &mut fixes); remove_unnecessary_wrapper(ctx, d, expr_ptr, &mut fixes); remove_semicolon(ctx, d, expr_ptr, &mut fixes); @@ -79,7 +79,7 @@ fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>) -> Option< if fixes.is_empty() { None } else { Some(fixes) } } -fn add_reference( +fn add_or_fix_reference( ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, expr_ptr: &InFile>, @@ -87,13 +87,56 @@ fn add_reference( ) -> Option<()> { let range = ctx.sema.diagnostics_display_range((*expr_ptr).map(|it| it.into())); - let (_, mutability) = d.expected.as_reference()?; - let actual_with_ref = d.actual.add_reference(mutability); - if !actual_with_ref.could_coerce_to(ctx.sema.db, &d.expected) { + let (expected_with_ref_removed, expected_mutability) = d.expected.as_reference()?; + + if let Some((actual_with_ref_removed, hir::Mutability::Shared)) = d.actual.as_reference() + && expected_mutability == hir::Mutability::Mut + && actual_with_ref_removed.could_coerce_to(ctx.db(), &expected_with_ref_removed) + { + // The actual type is `&T`, and the expected type is `&mut T`, (or `U` that `T` can be coerced to). + // It's likely that, instead of adding a reference, we should just change the mutability of + // the existing one. + + let expr = expr_ptr.to_node(ctx.db()); + // If the node comes from a macro expansion, then we shouldn't assist, + // as the suggestion would overwrite the macro _definition_ position + let expr = ctx.sema.original_ast_node(expr)?; + let expr_without_ref = RefExpr::cast(expr.syntax().clone())?.expr()?; + + let pos = expr_without_ref.syntax().text_range().start(); + let edit = TextEdit::insert(pos, expected_mutability.as_keyword_for_ref().to_owned()); + let source_change = SourceChange::from_text_edit(range.file_id, edit); + acc.push(fix( + "make_reference_mutable", + "Make reference mutable", + source_change, + range.range, + )); + return Some(()); + } + + let actual_with_ref = d.actual.add_reference(ctx.db(), expected_mutability); + if !actual_with_ref.could_coerce_to(ctx.db(), &d.expected) { return None; } - let ampersands = format!("&{}", mutability.as_keyword_for_ref()); + let expr = expr_ptr.to_node(ctx.db()); + let assign = expr + .syntax() + .parent() + .and_then(ast::BinExpr::cast) + .filter(|it| it.op_kind() == Some(ast::BinaryOp::Assignment { op: None })); + if let Some(assign) = assign + && expected_mutability.is_mut() + && let Some(range) = ctx.sema.original_range_opt(assign.syntax()) + { + let edit = TextEdit::insert(range.range.start(), "*".to_owned()); + let source_change = SourceChange::from_text_edit(range.file_id.file_id(ctx.db()), edit); + acc.push(fix("add_deref_here", "Add deref here", source_change, range.range)); + return Some(()); + } + + let ampersands = format!("&{}", expected_mutability.as_keyword_for_ref()); let edit = TextEdit::insert(range.range.start(), ampersands); let source_change = SourceChange::from_text_edit(range.file_id, edit); @@ -107,7 +150,7 @@ fn add_missing_ok_or_some( expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { - let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); + let root = ctx.db().parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); let hir::FileRange { file_id, range: expr_range } = ctx.sema.original_range_opt(expr.syntax())?; @@ -126,14 +169,13 @@ fn add_missing_ok_or_some( let variant_name = if Some(expected_enum) == core_result { "Ok" } else { "Some" }; - let wrapped_actual_ty = - expected_adt.ty_with_args(ctx.sema.db, std::iter::once(d.actual.clone())); + let wrapped_actual_ty = expected_adt.ty(ctx.db()).instantiate([d.actual.clone()]); - if !d.expected.could_unify_with(ctx.sema.db, &wrapped_actual_ty) { + if !d.expected.could_unify_with(ctx.db(), &wrapped_actual_ty) { return None; } - let file_id = file_id.file_id(ctx.sema.db); + let file_id = file_id.file_id(ctx.db()); if d.actual.is_unit() { if let Expr::BlockExpr(block) = &expr { @@ -202,7 +244,7 @@ fn remove_unnecessary_wrapper( expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { - let db = ctx.sema.db; + let db = ctx.db(); let root = db.parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); // FIXME: support inside MacroCall? @@ -225,7 +267,7 @@ fn remove_unnecessary_wrapper( return None; } - let inner_type = variant.fields(db).first()?.ty_with_args(db, d.actual.type_arguments()); + let inner_type = variant.fields(db).first()?.ty(db).instantiate(d.actual.type_arguments()); if !d.expected.could_unify_with(db, &inner_type) { return None; } @@ -233,7 +275,7 @@ fn remove_unnecessary_wrapper( let inner_arg = call_expr.arg_list()?.args().next()?; let file_id = expr_ptr.file_id.original_file(db); - let mut builder = SourceChangeBuilder::new(file_id.file_id(ctx.sema.db)); + let mut builder = SourceChangeBuilder::new(file_id.file_id(ctx.db())); let editor; match inner_arg { // We're returning `()` @@ -267,7 +309,7 @@ fn remove_unnecessary_wrapper( } } - builder.add_file_edits(file_id.file_id(ctx.sema.db), editor); + builder.add_file_edits(file_id.file_id(ctx.db()), editor); let name = format!("Remove unnecessary {}() wrapper", variant.name(db).as_str()); acc.push(fix( "remove_unnecessary_wrapper", @@ -284,7 +326,7 @@ fn remove_semicolon( expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { - let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); + let root = ctx.db().parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); if !d.actual.is_unit() { return None; @@ -294,14 +336,14 @@ fn remove_semicolon( let expr_before_semi = block.statements().last().and_then(|s| ExprStmt::cast(s.syntax().clone()))?; let type_before_semi = ctx.sema.type_of_expr(&expr_before_semi.expr()?)?.original(); - if !type_before_semi.could_coerce_to(ctx.sema.db, &d.expected) { + if !type_before_semi.could_coerce_to(ctx.db(), &d.expected) { return None; } let semicolon_range = expr_before_semi.semicolon_token()?.text_range(); let edit = TextEdit::delete(semicolon_range); let source_change = SourceChange::from_text_edit( - expr_ptr.file_id.original_file(ctx.sema.db).file_id(ctx.sema.db), + expr_ptr.file_id.original_file(ctx.db()).file_id(ctx.db()), edit, ); @@ -315,21 +357,21 @@ fn str_ref_to_owned( expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { - let expected = d.expected.display(ctx.sema.db, ctx.display_target); + let expected = d.expected.display(ctx.db(), ctx.display_target); // FIXME do this properly let is_applicable = d.actual.strip_reference().is_str() && expected.to_string() == "String"; if !is_applicable { return None; } - let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); + let root = ctx.db().parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); let hir::FileRange { file_id, range } = ctx.sema.original_range_opt(expr.syntax())?; let to_owned = ".to_owned()".to_owned(); let edit = TextEdit::insert(range.end(), to_owned); - let source_change = SourceChange::from_text_edit(file_id.file_id(ctx.sema.db), edit); + let source_change = SourceChange::from_text_edit(file_id.file_id(ctx.db()), edit); acc.push(fix("str_ref_to_owned", "Add .to_owned() here", source_change, range)); Some(()) @@ -391,6 +433,112 @@ fn test(_arg: &mut i32) {} ); } + #[test] + fn fix_reference_to_int() { + check_fix( + r#" +fn main() { + test($0&123); +} +fn test(_arg: &mut i32) {} + "#, + r#" +fn main() { + test(&mut 123); +} +fn test(_arg: &mut i32) {} + "#, + ); + } + + #[test] + fn add_reference_to_parenthesized_int() { + check_fix( + r#" +fn main() { + test(($0123)); +} +fn test(_arg: &i32) {} + "#, + r#" +fn main() { + test((&123)); +} +fn test(_arg: &i32) {} + "#, + ); + } + + #[test] + fn add_mutable_reference_to_parenthesized_int() { + check_fix( + r#" +fn main() { + test(($0123)); +} +fn test(_arg: &mut i32) {} + "#, + r#" +fn main() { + test((&mut 123)); +} +fn test(_arg: &mut i32) {} + "#, + ); + } + + #[test] + fn fix_reference_to_parenthesized_int_paren_inside_ref() { + check_fix( + r#" +fn main() { + test(&$0(123)); +} +fn test(_arg: &mut i32) {} + "#, + r#" +fn main() { + test(&mut (123)); +} +fn test(_arg: &mut i32) {} + "#, + ); + } + + #[test] + fn fix_reference_to_parenthesized_int_ref_inside_paren() { + check_fix( + r#" +fn main() { + test(($0&123)); +} +fn test(_arg: &mut i32) {} + "#, + r#" +fn main() { + test((&mut 123)); +} +fn test(_arg: &mut i32) {} + "#, + ); + } + + #[test] + fn add_deref_in_assign() { + check_fix( + r#" +fn test(arg: &mut i32) { + arg = $02; +} + "#, + r#" +fn test(arg: &mut i32) { + *arg = 2; +} + "#, + ); + } + #[test] fn add_reference_to_array() { check_fix( @@ -410,6 +558,19 @@ fn test(_arg: &[i32]) {} ); } + #[test] + fn fix_reference_to_array() { + check_no_fix( + r#" +//- minicore: coerce_unsized +fn main() { + test($0&[1, 2, 3]); +} +fn test(_arg: &mut [i32]) {} + "#, + ); + } + #[test] fn add_reference_with_autoderef() { check_fix( @@ -443,6 +604,49 @@ fn test(_arg: &Bar) {} ); } + #[test] + // FIXME: this should suggest making the reference mutable instead: `&Foo -> &mut Foo`. + // Currently it doesn't, as the logic for that assist strips away references, and thus checks + // whether `Foo` can be coerced to `Bar` (which it can't), instead of checking `&mut Foo` to + // `&mut Bar` (which it can) + fn fix_reference_with_autoderef() { + check_fix( + r#" +//- minicore: coerce_unsized, deref_mut +struct Foo; +struct Bar; +impl core::ops::Deref for Foo { + type Target = Bar; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::DerefMut for Foo { + fn deref_mut(&mut self) -> &mut Self::Target { loop {} } +} + +fn main() { + test($0&Foo); +} +fn test(_arg: &mut Bar) {} + "#, + r#" +struct Foo; +struct Bar; +impl core::ops::Deref for Foo { + type Target = Bar; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::DerefMut for Foo { + fn deref_mut(&mut self) -> &mut Self::Target { loop {} } +} + +fn main() { + test(&mut &Foo); +} +fn test(_arg: &mut Bar) {} + "#, + ); + } + #[test] fn add_reference_to_method_call() { check_fix( @@ -483,6 +687,38 @@ fn main() { ); } + #[test] + fn add_mutable_reference_to_let_stmt() { + check_fix( + r#" +fn main() { + let _test: &mut i32 = $0123; +} + "#, + r#" +fn main() { + let _test: &mut i32 = &mut 123; +} + "#, + ); + } + + #[test] + fn fix_reference_to_let_stmt() { + check_fix( + r#" +fn main() { + let _test: &mut i32 = $0&123; +} + "#, + r#" +fn main() { + let _test: &mut i32 = &mut 123; +} + "#, + ); + } + #[test] fn add_reference_to_macro_call() { check_fix( @@ -512,16 +748,50 @@ fn main() { } #[test] - fn add_mutable_reference_to_let_stmt() { + fn fix_reference_to_macro_call() { check_fix( r#" +macro_rules! thousand { + () => { + 1000_u64 + }; +} + +fn test(_foo: &mut u64) {} fn main() { - let _test: &mut i32 = $0123; + test($0&thousand!()); } "#, r#" +macro_rules! thousand { + () => { + 1000_u64 + }; +} + +fn test(_foo: &mut u64) {} fn main() { - let _test: &mut i32 = &mut 123; + test(&mut thousand!()); +} + "#, + ); + } + + #[test] + // If the immutable reference comes from a macro expansion, + // we can't do anything to change it to a mutable one. + fn dont_fix_reference_inside_macro_call() { + check_no_fix( + r#" +macro_rules! thousand { + () => { + &1000_u64 + }; +} + +fn test(_foo: &mut u64) {} +fn main() { + test($0thousand!()); } "#, ); @@ -1206,6 +1476,20 @@ fn f() { ); } + #[test] + fn type_mismatch_in_condition() { + check_diagnostics( + r#" +fn f() { + if 1 {} + //^ error: expected bool, found i32 + match () { _ if 1 => (), _ => () } + //^ error: expected bool, found i32 +} +"#, + ); + } + #[test] fn regression_14768() { check_diagnostics( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs index d94ceef642f77..4a253bc8310c0 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs @@ -69,4 +69,20 @@ fn foo() { "#, ); } + + #[test] + fn for_iterable() { + check_diagnostics( + r#" +//- minicore: iterator +fn foo() { + for _ in () {} + // ^^ error: the trait bound `(): Iterator` is not satisfied + // ^^ error: the trait bound `(): Iterator` is not satisfied + // | required by the bound `(): IntoIterator` +} + + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs index 93caf281f035f..01929a5144719 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs @@ -129,25 +129,17 @@ fn assoc_func_fix( let receiver_type = &ctx.sema.type_of_expr(&receiver)?.original; let assoc_fn_params = f.assoc_fn_params(db); - let need_to_take_receiver_as_first_arg = if assoc_fn_params.is_empty() { - false - } else { - assoc_fn_params - .first() - .map(|first_arg| { - // For generic type, say `Box`, take `Box::into_raw(b: Self)` as example, - // type of `b` is `Self`, which is `Box`, containing unspecified generics. - // However, type of `receiver` is specified, it could be `Box` or something like that, - // so `first_arg.ty() == receiver_type` evaluate to `false` here. - // Here add `first_arg.ty().as_adt() == receiver_type.as_adt()` as guard, - // apply `.as_adt()` over `Box` or `Box` gets `Box`, so we get `true` here. - - // FIXME: it fails when type of `b` is `Box` with other generic param different from `receiver` - first_arg.ty() == receiver_type - || first_arg.ty().as_adt() == receiver_type.as_adt() - }) - .unwrap_or(false) - }; + let need_to_take_receiver_as_first_arg = assoc_fn_params.first().is_some_and(|first_arg| { + // For generic type, say `Box`, take `Box::into_raw(b: Self)` as example, + // type of `b` is `Self`, which is `Box`, containing unspecified generics. + // However, type of `receiver` is specified, it could be `Box` or something like that, + // so `first_arg.ty() == receiver_type` evaluate to `false` here. + // Here add `first_arg.ty().as_adt() == receiver_type.as_adt()` as guard, + // apply `.as_adt()` over `Box` or `Box` gets `Box`, so we get `true` here. + + // FIXME: it fails when type of `b` is `Box` with other generic param different from `receiver` + first_arg.ty() == receiver_type || first_arg.ty().as_adt() == receiver_type.as_adt() + }); let mut receiver_type_adt_name = receiver_type.as_adt()?.name(db).display_no_db(ctx.edition).to_smolstr(); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 49b3234a11d8e..aec68b55c78dc 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -29,13 +29,19 @@ extern crate rustc_driver as _; mod handlers { + pub(crate) mod array_pattern_without_fixed_length; pub(crate) mod await_outside_of_async; pub(crate) mod bad_rtn; pub(crate) mod break_outside_of_loop; + pub(crate) mod cannot_be_dereferenced; + pub(crate) mod cannot_implicitly_deref_trait_object; + pub(crate) mod cannot_index_into; pub(crate) mod duplicate_field; pub(crate) mod elided_lifetimes_in_path; pub(crate) mod expected_array_or_slice_pat; pub(crate) mod expected_function; + pub(crate) mod explicit_drop_method_use; + pub(crate) mod fru_in_destructuring_assignment; pub(crate) mod functional_record_update_on_non_struct; pub(crate) mod generic_args_prohibited; pub(crate) mod generic_default_refers_to_self; @@ -47,8 +53,10 @@ mod handlers { pub(crate) mod invalid_cast; pub(crate) mod invalid_derive_target; pub(crate) mod invalid_lhs_of_assignment; + pub(crate) mod invalid_range_pat_type; pub(crate) mod macro_error; pub(crate) mod malformed_derive; + pub(crate) mod method_call_illegal_sized_bound; pub(crate) mod mismatched_arg_count; pub(crate) mod mismatched_array_pat_len; pub(crate) mod missing_fields; @@ -57,9 +65,11 @@ mod handlers { pub(crate) mod missing_unsafe; pub(crate) mod moved_out_of_ref; pub(crate) mod mutability_errors; + pub(crate) mod mutable_ref; pub(crate) mod no_such_field; pub(crate) mod non_exhaustive_let; pub(crate) mod non_exhaustive_record_expr; + pub(crate) mod non_exhaustive_record_pat; pub(crate) mod parenthesized_generic_args_without_fn_trait; pub(crate) mod pattern_arg_in_extern_fn; pub(crate) mod private_assoc_item; @@ -426,7 +436,15 @@ pub fn semantic_diagnostics( for diag in diags { let d = match diag { AnyDiagnostic::AwaitOutsideOfAsync(d) => handlers::await_outside_of_async::await_outside_of_async(&ctx, &d), + AnyDiagnostic::CannotBeDereferenced(d) => handlers::cannot_be_dereferenced::cannot_be_dereferenced(&ctx, &d), + AnyDiagnostic::CannotImplicitlyDerefTraitObject(d) => handlers::cannot_implicitly_deref_trait_object::cannot_implicitly_deref_trait_object(&ctx, &d), + AnyDiagnostic::CannotIndexInto(d) => handlers::cannot_index_into::cannot_index_into(&ctx, &d), AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d), + AnyDiagnostic::ArrayPatternWithoutFixedLength(d) => { + handlers::array_pattern_without_fixed_length::array_pattern_without_fixed_length( + &ctx, &d, + ) + } AnyDiagnostic::ExpectedArrayOrSlicePat(d) => handlers::expected_array_or_slice_pat::expected_array_or_slice_pat(&ctx, &d), AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d), AnyDiagnostic::FunctionalRecordUpdateOnNonStruct(d) => handlers::functional_record_update_on_non_struct::functional_record_update_on_non_struct(&ctx, &d), @@ -452,12 +470,14 @@ pub fn semantic_diagnostics( continue; }, AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d), + AnyDiagnostic::MethodCallIllegalSizedBound(d) => handlers::method_call_illegal_sized_bound::method_call_illegal_sized_bound(&ctx, &d), AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d), AnyDiagnostic::MismatchedArrayPatLen(d) => handlers::mismatched_array_pat_len::mismatched_array_pat_len(&ctx, &d), AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d), AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d), AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d), AnyDiagnostic::MovedOutOfRef(d) => handlers::moved_out_of_ref::moved_out_of_ref(&ctx, &d), + AnyDiagnostic::MutableRefBinding(d) => handlers::mutable_ref::mutable_ref_binding(&ctx, &d), AnyDiagnostic::NeedMut(d) => match handlers::mutability_errors::need_mut(&ctx, &d) { Some(it) => it, None => continue, @@ -466,6 +486,9 @@ pub fn semantic_diagnostics( AnyDiagnostic::NonExhaustiveRecordExpr(d) => { handlers::non_exhaustive_record_expr::non_exhaustive_record_expr(&ctx, &d) } + AnyDiagnostic::NonExhaustiveRecordPat(d) => { + handlers::non_exhaustive_record_pat::non_exhaustive_record_pat(&ctx, &d) + } AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d), AnyDiagnostic::DuplicateField(d) => handlers::duplicate_field::duplicate_field(&ctx, &d), AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d), @@ -519,10 +542,13 @@ pub fn semantic_diagnostics( AnyDiagnostic::ElidedLifetimesInPath(d) => handlers::elided_lifetimes_in_path::elided_lifetimes_in_path(&ctx, &d), AnyDiagnostic::GenericDefaultRefersToSelf(d) => handlers::generic_default_refers_to_self::generic_default_refers_to_self(&ctx, &d), AnyDiagnostic::InvalidLhsOfAssignment(d) => handlers::invalid_lhs_of_assignment::invalid_lhs_of_assignment(&ctx, &d), + AnyDiagnostic::InvalidRangePatType(d) => handlers::invalid_range_pat_type::invalid_range_pat_type(&ctx, &d), AnyDiagnostic::TypeMustBeKnown(d) => handlers::type_must_be_known::type_must_be_known(&ctx, &d), AnyDiagnostic::PatternArgInExternFn(d) => handlers::pattern_arg_in_extern_fn::pattern_arg_in_extern_fn(&ctx, &d), AnyDiagnostic::UnionExprMustHaveExactlyOneField(d) => handlers::union_expr_must_have_exactly_one_field::union_expr_must_have_exactly_one_field(&ctx, &d), AnyDiagnostic::UnimplementedTrait(d) => handlers::unimplemented_trait::unimplemented_trait(&ctx, &d), + AnyDiagnostic::FruInDestructuringAssignment(d) => handlers::fru_in_destructuring_assignment::fru_in_destructuring_assignment(&ctx, &d), + AnyDiagnostic::ExplicitDropMethodUse(d) => handlers::explicit_drop_method_use::explicit_drop_method_use(&ctx, &d), }; res.push(d) } diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs index ae208fe1b5615..ffd144a827e34 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs @@ -88,7 +88,7 @@ pub(crate) fn goto_type_definition( ast::Pat(it) => sema.type_of_pat(&it)?.original, ast::SelfParam(it) => sema.type_of_self(&it)?, ast::Type(it) => sema.resolve_type(&it)?, - ast::RecordField(it) => sema.to_def(&it)?.ty(db).to_type(db), + ast::RecordField(it) => sema.to_def(&it)?.ty(db), // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise ast::NameRef(it) => { if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) { diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index df1fcecc991fe..c3a8e0362fee8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -469,7 +469,7 @@ pub(crate) fn hover_for_definition( Definition::Local(it) => Some(it.ty(db)), Definition::GenericParam(hir::GenericParam::ConstParam(it)) => Some(it.ty(db)), Definition::GenericParam(hir::GenericParam::TypeParam(it)) => Some(it.ty(db)), - Definition::Field(field) => Some(field.ty(db).to_type(db)), + Definition::Field(field) => Some(field.ty(db)), Definition::TupleField(it) => Some(it.ty(db)), Definition::Function(it) => Some(it.ty(db)), Definition::Adt(it) => Some(it.ty(db)), @@ -630,7 +630,7 @@ fn goto_type_action_for_def( let ty = match def { Definition::Local(it) => Some(it.ty(db)), - Definition::Field(field) => Some(field.ty(db).to_type(db)), + Definition::Field(field) => Some(field.ty(db)), Definition::TupleField(field) => Some(field.ty(db)), Definition::Const(it) => Some(it.ty(db)), Definition::Static(it) => Some(it.ty(db)), diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index e08bbc5c21b65..da4f185d75641 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -664,14 +664,14 @@ pub(super) fn definition( } let drop_info = match def { Definition::Field(field) => { - DropInfo { drop_glue: field.ty(db).to_type(db).drop_glue(db), has_dtor: None } + DropInfo { drop_glue: field.ty(db).drop_glue(db), has_dtor: None } } Definition::Adt(Adt::Struct(strukt)) => { - let struct_drop_glue = strukt.ty_params(db).drop_glue(db); + let struct_drop_glue = strukt.ty(db).drop_glue(db); let mut fields_drop_glue = strukt .fields(db) .iter() - .map(|field| field.ty(db).to_type(db).drop_glue(db)) + .map(|field| field.ty(db).drop_glue(db)) .max() .unwrap_or(DropGlue::None); let has_dtor = match (fields_drop_glue, struct_drop_glue) { @@ -688,10 +688,10 @@ pub(super) fn definition( // Unions cannot have fields with drop glue. Definition::Adt(Adt::Union(union)) => DropInfo { drop_glue: DropGlue::None, - has_dtor: Some(union.ty_params(db).drop_glue(db) != DropGlue::None), + has_dtor: Some(union.ty(db).drop_glue(db) != DropGlue::None), }, Definition::Adt(Adt::Enum(enum_)) => { - let enum_drop_glue = enum_.ty_params(db).drop_glue(db); + let enum_drop_glue = enum_.ty(db).drop_glue(db); let fields_drop_glue = enum_ .variants(db) .iter() @@ -699,7 +699,7 @@ pub(super) fn definition( variant .fields(db) .iter() - .map(|field| field.ty(db).to_type(db).drop_glue(db)) + .map(|field| field.ty(db).drop_glue(db)) .max() .unwrap_or(DropGlue::None) }) @@ -714,13 +714,13 @@ pub(super) fn definition( let fields_drop_glue = variant .fields(db) .iter() - .map(|field| field.ty(db).to_type(db).drop_glue(db)) + .map(|field| field.ty(db).drop_glue(db)) .max() .unwrap_or(DropGlue::None); DropInfo { drop_glue: fields_drop_glue, has_dtor: None } } Definition::TypeAlias(type_alias) => { - DropInfo { drop_glue: type_alias.ty_params(db).drop_glue(db), has_dtor: None } + DropInfo { drop_glue: type_alias.ty(db).drop_glue(db), has_dtor: None } } Definition::Local(local) => { DropInfo { drop_glue: local.ty(db).drop_glue(db), has_dtor: None } 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 bf5e0be37420d..30644fe2db898 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -3152,6 +3152,35 @@ fn test_hover_layout_of_enum() { ); } +#[test] +fn test_hover_layout_nonzero_type_alias() { + check( + r#"//- minicore: non_zero +use core::num; +trait Trait { type Inner; } +impl Trait for u8 { type Inner = num::NonZeroU8; } +#[repr(transparent)] +struct NonZero(T::Inner); +type NonZeroU8$0 = NonZero; +"#, + expect![[r#" + *NonZeroU8* + + ```rust + ra_test_fixture + ``` + + ```rust + type NonZeroU8 = NonZero + ``` + + --- + + size = 1, align = 1, niches = 1, no Drop + "#]], + ); +} + #[test] fn test_hover_layout_padding_info() { check( 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 57aba51b4e859..f6b13cbe0b94d 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 @@ -43,7 +43,7 @@ pub(super) fn hints( for (_, bb) in mir.basic_blocks.iter() { let terminator = bb.terminator.as_ref()?; - if let TerminatorKind::Drop { place, .. } = terminator.kind { + if let TerminatorKind::Drop { place, .. } = &terminator.kind { if !place.projection.is_empty() { continue; // Ignore complex cases for now } diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index f70bb3353fd5d..b8c14dc09f9a2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -964,7 +964,7 @@ pub(crate) fn orig_range_with_focus_r( // *should* contain the name _ => { let call = call(); - let kind = call.kind; + let kind = &call.kind; let range = kind.clone().original_call_range_with_input(db); //If the focus range is in the attribute/derive body, we // need to point the call site to the entire body, if not, fall back diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index b664187932efa..26c6776107846 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -16,7 +16,7 @@ use std::fmt::Write; use stdx::{always, format_to, never}; use syntax::{ AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize, - ast::{self, HasArgList, make, prec::ExprPrecedence}, + ast::{self, HasArgList, prec::ExprPrecedence}, }; use ide_db::text_edit::TextEdit; @@ -522,11 +522,11 @@ fn rename_to_self( }; let first_param_ty = first_param.ty(); let impl_ty = impl_.self_ty(sema.db); - let (ty, self_param) = if impl_ty.remove_ref().is_some() { + let (ty, self_param) = if impl_ty.is_reference() { // if the impl is a ref to the type we can just match the `&T` with self directly (first_param_ty.clone(), "self") } else { - first_param_ty.remove_ref().map_or((first_param_ty.clone(), "self"), |ty| { + first_param_ty.as_reference_inner().map_or((first_param_ty.clone(), "self"), |ty| { (ty, if first_param_ty.is_mutable_reference() { "&mut self" } else { "&self" }) }) }; @@ -818,11 +818,12 @@ fn rename_elided_lifetime( let mut builder = SourceChangeBuilder::new(position.file_id); let editor = builder.make_editor(&root); + let make = editor.make(); - editor.replace(lifetime_token, make::lifetime(new_name).syntax().clone()); + editor.replace(lifetime_token, make.lifetime(new_name).syntax().clone()); if let Some(has_generic_params) = parent.ancestors().find_map(ast::AnyHasGenericParams::cast) { - let lifetime_param = make::lifetime_param(make::lifetime(new_name)); + let lifetime_param = make.lifetime_param(make.lifetime(new_name)); editor.add_generic_param(&has_generic_params, lifetime_param.into()); } diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index edcf0dc22b231..0022c1148a14c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -174,9 +174,15 @@ fn signature_help_for_call( match callable.kind() { hir::CallableKind::Function(func) => { res.doc = func.docs(db).map(Documentation::into_owned); + if func.is_const(db) { + format_to!(res.signature, "const "); + } if func.is_async(db) { format_to!(res.signature, "async "); } + if func.is_unsafe(db) { + format_to!(res.signature, "unsafe "); + } format_to!(res.signature, "fn {}", func.name(db).display(db, edition)); let generic_params = GenericDef::Function(func) @@ -529,7 +535,7 @@ fn signature_help_for_tuple_struct_pat( pat.syntax(), token, pat.fields(), - fields.into_iter().map(|it| it.ty(db).to_type(db)), + fields.into_iter().map(|it| it.ty(db)), display_target, )) } @@ -2664,4 +2670,76 @@ fn main() { "#]], ); } + + #[test] + fn test_const_function() { + check( + r#" +//- minicore: sized, fn +pub const fn foo(x: u32, y: u32) -> u32 { x + y } + +fn main() { + foo($0) +} + "#, + expect![[r#" + const fn foo(x: u32, y: u32) -> u32 + ^^^^^^ ------ + "#]], + ); + } + + #[test] + fn test_unsafe_function() { + check( + r#" +//- minicore: sized, fn +pub unsafe fn foo(x: u32, y: u32) -> u32 { x + y } + +fn main() { + unsafe { foo($0) } +} + "#, + expect![[r#" + unsafe fn foo(x: u32, y: u32) -> u32 + ^^^^^^ ------ + "#]], + ); + } + + #[test] + fn test_const_unsafe_function() { + check( + r#" +//- minicore: sized, fn +pub const unsafe fn foo(x: u32, y: u32) -> u32 { x + y } + +fn main() { + unsafe { foo($0) } +} + "#, + expect![[r#" + const unsafe fn foo(x: u32, y: u32) -> u32 + ^^^^^^ ------ + "#]], + ); + } + + #[test] + fn test_async_unsafe_function() { + check( + r#" +//- minicore: sized, fn, future +pub async unsafe fn foo(x: u32, y: u32) -> u32 { x + y } + +fn main() { + unsafe { foo($0) } +} + "#, + expect![[r#" + async unsafe fn foo(x: u32, y: u32) -> u32 + ^^^^^^ ------ + "#]], + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index 76bb06328b7cf..9af6d67b59803 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -177,7 +177,7 @@ pub(super) fn doc_comment( match sema.first_crate(vfs_file_id) { Some(krate) => krate.base().data(sema.db).proc_macro_cwd.clone(), None => { - // Arbitrarily pick /, since from_single_file treats this file as as /main.rs anyway. + // Arbitrarily pick /, since from_single_file treats this file as /main.rs anyway. Arc::new(ide_db::base_db::AbsPathBuf::try_from("/").unwrap()) } } diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs index 1b9df9722b07e..03bde6f3e545b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs @@ -98,7 +98,7 @@ pub(crate) fn view_memory_layout( Definition::BuiltinType(it) => it.ty(db), Definition::SelfType(it) => it.self_ty(db), Definition::Local(it) => it.ty(db), - Definition::Field(it) => it.ty(db).to_type(db), + Definition::Field(it) => it.ty(db), Definition::Const(it) => it.ty(db), Definition::Static(it) => it.ty(db), _ => return None, diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index ac6daaf00681f..e5f66a202ee6f 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -475,6 +475,7 @@ define_symbols! { PartialOrd, CoercePointee, path, + pattern_type, Pending, phantom_data, pieces, @@ -645,6 +646,7 @@ define_symbols! { eii_internals, field_representing_type_raw, intrinsics, + core_intrinsics, link_cfg, more_maybe_bounds, negative_bounds, @@ -670,4 +672,7 @@ define_symbols! { deref_patterns, mut_ref, type_changing_struct_update, + RangeMin, + RangeMax, + RangeSub, } diff --git a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml index 91b012e05071f..7b5e51fba5f18 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml +++ b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml @@ -30,7 +30,7 @@ vfs.workspace = true intern.workspace = true [features] -in-rust-tree = ["hir-expand/in-rust-tree"] +in-rust-tree = ["hir-expand/in-rust-tree", "proc-macro-api/in-rust-tree"] [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 801eaeaea9f1f..fd90bc404aca4 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -609,11 +609,9 @@ impl ProcMacroExpander for Expander { SubRequest::LineColumn { file_id, ast_id, offset } => { let range = resolve_sub_span(db, file_id, ast_id, TextRange::empty(TextSize::from(offset))); - let source = db.file_text(range.file_id.file_id(db)).text(db); - let line_index = ide_db::line_index::LineIndex::new(source); - let (line, column) = line_index - .try_line_col(range.range.start()) - .map(|lc| (lc.line + 1, lc.col + 1)) + let (line, column) = db + .line_column(range.file_id.file_id(db), range.range.start()) + .map(|(line, col)| (line + 1, col + 1)) .unwrap_or((1, 1)); // proc_macro::Span line/column are 1-based Ok(SubResponse::LineColumnResult { line, column }) @@ -726,6 +724,8 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::SpanParentResult { parent_span: None }) } + // FIXME: implement this + SubRequest::SpanJoin { .. } => Ok(SubResponse::SpanJoinResult { span: None }), }; match self.0.expand( subtree.view(), diff --git a/src/tools/rust-analyzer/crates/mbe/src/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/tests.rs index 271dfc877b475..f3ebb663d92a9 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/tests.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/tests.rs @@ -74,7 +74,7 @@ fn check_( "{}", syntax_bridge::prettify_macro_expansion::prettify_macro_expansion( node.syntax_node(), - &mut |_| None, + &mut |_, _| None, |_| () ) ); diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs index 5726f085a03e9..29fa11720aca7 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs @@ -12,6 +12,7 @@ pub(super) const PATTERN_FIRST: TokenSet = T![_], T![-], T![.], + T![!], ])); const PAT_TOP_FIRST: TokenSet = PATTERN_FIRST.union(TokenSet::new(&[T![|]])); @@ -256,6 +257,7 @@ fn atom_pat(p: &mut Parser<'_>, recovery_set: TokenSet) -> Option ref_pat(p), T!['('] => tuple_pat(p), T!['['] => slice_pat(p), + T![!] => not_null_pat(p), _ => { p.err_recover("expected pattern", recovery_set); @@ -435,6 +437,18 @@ fn ref_pat(p: &mut Parser<'_>) -> CompletedMarker { m.complete(p, REF_PAT) } +// test not_null_pat +// fn main() { +// let (!a | !&0) = (); +// } +fn not_null_pat(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(T![!])); + let m = p.start(); + p.bump(T![!]); + pattern_single(p); + m.complete(p, NOT_NULL) +} + // test tuple_pat // fn main() { // let (a, b, ..) = (); diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs index 667bb68c649c5..f92afde60de15 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs @@ -1,3 +1,5 @@ +use crate::grammar::entry::prefix::pat; + use super::*; pub(super) const TYPE_FIRST: TokenSet = paths::PATH_FIRST.union(TokenSet::new(&[ @@ -341,6 +343,10 @@ fn bare_dyn_trait_type(p: &mut Parser<'_>) { // type B = crate::foo!(); fn path_or_macro_type(p: &mut Parser<'_>, allow_bounds: bool) { assert!(paths::is_path_start(p)); + if p.at_contextual_kw(T![builtin]) && p.nth_at(1, T![#]) { + pattern_type(p); + return; + } let r = p.start(); let m = p.start(); @@ -411,3 +417,23 @@ pub(super) fn opt_type_bounds_as_dyn_trait_type( // Finally precede everything with DYN_TRAIT_TYPE m.precede(p).complete(p, DYN_TRAIT_TYPE) } + +// test pattern_type +// type T = builtin#pattern_type (u8 is 0..10); +fn pattern_type(p: &mut Parser<'_>) { + let m = p.start(); + p.bump_remap(T![builtin]); + p.bump(T![#]); + if p.eat_contextual_kw(T![pattern_type]) { + p.expect(T!['(']); + type_(p); + if !p.eat_contextual_kw(T![is]) { + p.error("expected `is`") + } + pat(p); + p.expect(T![')']); + m.complete(p, PATTERN_TYPE); + } else { + m.abandon(p); + } +} 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 b1867275cebfa..dd675e0834051 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 @@ -127,6 +127,7 @@ pub enum SyntaxKind { GLOBAL_ASM_KW, INLATEOUT_KW, INOUT_KW, + IS_KW, LABEL_KW, LATEOUT_KW, MACRO_RULES_KW, @@ -135,9 +136,11 @@ pub enum SyntaxKind { NOMEM_KW, NORETURN_KW, NOSTACK_KW, + NULL_KW, OFFSET_OF_KW, OPTIONS_KW, OUT_KW, + PATTERN_TYPE_KW, PRESERVES_FLAGS_KW, PURE_KW, RAW_KW, @@ -254,6 +257,7 @@ pub enum SyntaxKind { NAME, NAME_REF, NEVER_TYPE, + NOT_NULL, OFFSET_OF_EXPR, OR_PAT, PARAM, @@ -268,6 +272,7 @@ pub enum SyntaxKind { PATH_PAT, PATH_SEGMENT, PATH_TYPE, + PATTERN_TYPE, PREFIX_EXPR, PTR_TYPE, RANGE_EXPR, @@ -439,6 +444,7 @@ impl SyntaxKind { | NAME | NAME_REF | NEVER_TYPE + | NOT_NULL | OFFSET_OF_EXPR | OR_PAT | PARAM @@ -453,6 +459,7 @@ impl SyntaxKind { | PATH_PAT | PATH_SEGMENT | PATH_TYPE + | PATTERN_TYPE | PREFIX_EXPR | PTR_TYPE | RANGE_EXPR @@ -636,6 +643,7 @@ impl SyntaxKind { GLOBAL_ASM_KW => "global_asm", INLATEOUT_KW => "inlateout", INOUT_KW => "inout", + IS_KW => "is", LABEL_KW => "label", LATEOUT_KW => "lateout", MACRO_RULES_KW => "macro_rules", @@ -644,9 +652,11 @@ impl SyntaxKind { NOMEM_KW => "nomem", NORETURN_KW => "noreturn", NOSTACK_KW => "nostack", + NULL_KW => "null", OFFSET_OF_KW => "offset_of", OPTIONS_KW => "options", OUT_KW => "out", + PATTERN_TYPE_KW => "pattern_type", PRESERVES_FLAGS_KW => "preserves_flags", PURE_KW => "pure", RAW_KW => "raw", @@ -742,6 +752,7 @@ impl SyntaxKind { GLOBAL_ASM_KW => true, INLATEOUT_KW => true, INOUT_KW => true, + IS_KW => true, LABEL_KW => true, LATEOUT_KW => true, MACRO_RULES_KW => true, @@ -750,9 +761,11 @@ impl SyntaxKind { NOMEM_KW => true, NORETURN_KW => true, NOSTACK_KW => true, + NULL_KW => true, OFFSET_OF_KW => true, OPTIONS_KW => true, OUT_KW => true, + PATTERN_TYPE_KW => true, PRESERVES_FLAGS_KW => true, PURE_KW => true, RAW_KW => true, @@ -836,6 +849,7 @@ impl SyntaxKind { GLOBAL_ASM_KW => true, INLATEOUT_KW => true, INOUT_KW => true, + IS_KW => true, LABEL_KW => true, LATEOUT_KW => true, MACRO_RULES_KW => true, @@ -844,9 +858,11 @@ impl SyntaxKind { NOMEM_KW => true, NORETURN_KW => true, NOSTACK_KW => true, + NULL_KW => true, OFFSET_OF_KW => true, OPTIONS_KW => true, OUT_KW => true, + PATTERN_TYPE_KW => true, PRESERVES_FLAGS_KW => true, PURE_KW => true, RAW_KW => true, @@ -993,6 +1009,7 @@ impl SyntaxKind { "global_asm" => GLOBAL_ASM_KW, "inlateout" => INLATEOUT_KW, "inout" => INOUT_KW, + "is" => IS_KW, "label" => LABEL_KW, "lateout" => LATEOUT_KW, "macro_rules" => MACRO_RULES_KW, @@ -1001,9 +1018,11 @@ impl SyntaxKind { "nomem" => NOMEM_KW, "noreturn" => NORETURN_KW, "nostack" => NOSTACK_KW, + "null" => NULL_KW, "offset_of" => OFFSET_OF_KW, "options" => OPTIONS_KW, "out" => OUT_KW, + "pattern_type" => PATTERN_TYPE_KW, "preserves_flags" => PRESERVES_FLAGS_KW, "pure" => PURE_KW, "raw" => RAW_KW, @@ -1168,6 +1187,7 @@ macro_rules ! T_ { [global_asm] => { $ crate :: SyntaxKind :: GLOBAL_ASM_KW }; [inlateout] => { $ crate :: SyntaxKind :: INLATEOUT_KW }; [inout] => { $ crate :: SyntaxKind :: INOUT_KW }; + [is] => { $ crate :: SyntaxKind :: IS_KW }; [label] => { $ crate :: SyntaxKind :: LABEL_KW }; [lateout] => { $ crate :: SyntaxKind :: LATEOUT_KW }; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW }; @@ -1176,9 +1196,11 @@ macro_rules ! T_ { [nomem] => { $ crate :: SyntaxKind :: NOMEM_KW }; [noreturn] => { $ crate :: SyntaxKind :: NORETURN_KW }; [nostack] => { $ crate :: SyntaxKind :: NOSTACK_KW }; + [null] => { $ crate :: SyntaxKind :: NULL_KW }; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW }; [options] => { $ crate :: SyntaxKind :: OPTIONS_KW }; [out] => { $ crate :: SyntaxKind :: OUT_KW }; + [pattern_type] => { $ crate :: SyntaxKind :: PATTERN_TYPE_KW }; [preserves_flags] => { $ crate :: SyntaxKind :: PRESERVES_FLAGS_KW }; [pure] => { $ crate :: SyntaxKind :: PURE_KW }; [raw] => { $ crate :: SyntaxKind :: RAW_KW }; 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 7aaf270a77bf7..ccf8b89be74a2 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 @@ -474,6 +474,8 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/nocontentexpr_after_item.rs"); } #[test] + fn not_null_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/not_null_pat.rs"); } + #[test] fn offset_of_parens() { run_and_expect_no_errors("test_data/parser/inline/ok/offset_of_parens.rs"); } @@ -506,6 +508,8 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/path_type_with_bounds.rs"); } #[test] + fn pattern_type() { run_and_expect_no_errors("test_data/parser/inline/ok/pattern_type.rs"); } + #[test] fn placeholder_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/placeholder_pat.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast index 2334b730e4cc8..234070bcb1355 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast @@ -37,27 +37,26 @@ SOURCE_FILE MATCH_ARM ATTR POUND "#" - ERROR + NOT_NULL BANG "!" - ARRAY_EXPR - L_BRACK "[" - CALL_EXPR - PATH_EXPR + SLICE_PAT + L_BRACK "[" + TUPLE_STRUCT_PAT PATH PATH_SEGMENT NAME_REF IDENT "doc" - ARG_LIST L_PAREN "(" - LITERAL - STRING "\"Not allowed here\"" + LITERAL_PAT + LITERAL + STRING "\"Not allowed here\"" R_PAREN ")" - R_BRACK "]" - WHITESPACE "\n " - MATCH_ARM - WILDCARD_PAT + R_BRACK "]" + WHITESPACE "\n " + UNDERSCORE_EXPR UNDERSCORE "_" - WHITESPACE " " + WHITESPACE " " + MATCH_ARM FAT_ARROW "=>" WHITESPACE " " TUPLE_EXPR @@ -103,22 +102,21 @@ SOURCE_FILE MATCH_ARM ATTR POUND "#" - ERROR + NOT_NULL BANG "!" - ARRAY_EXPR - L_BRACK "[" - CALL_EXPR - PATH_EXPR + SLICE_PAT + L_BRACK "[" + TUPLE_STRUCT_PAT PATH PATH_SEGMENT NAME_REF IDENT "doc" - ARG_LIST L_PAREN "(" - LITERAL - STRING "\"Nor here\"" + LITERAL_PAT + LITERAL + STRING "\"Nor here\"" R_PAREN ")" - R_BRACK "]" + R_BRACK "]" WHITESPACE "\n " R_CURLY "}" WHITESPACE "\n\n " @@ -146,27 +144,26 @@ SOURCE_FILE WHITESPACE "\n " ATTR POUND "#" - ERROR + NOT_NULL BANG "!" - ARRAY_EXPR - L_BRACK "[" - CALL_EXPR - PATH_EXPR + SLICE_PAT + L_BRACK "[" + TUPLE_STRUCT_PAT PATH PATH_SEGMENT NAME_REF IDENT "doc" - ARG_LIST L_PAREN "(" - LITERAL - STRING "\"Nor here\"" + LITERAL_PAT + LITERAL + STRING "\"Nor here\"" R_PAREN ")" - R_BRACK "]" - WHITESPACE "\n " - MATCH_ARM - WILDCARD_PAT + R_BRACK "]" + WHITESPACE "\n " + UNDERSCORE_EXPR UNDERSCORE "_" - WHITESPACE " " + WHITESPACE " " + MATCH_ARM FAT_ARROW "=>" WHITESPACE " " TUPLE_EXPR @@ -190,13 +187,13 @@ SOURCE_FILE R_CURLY "}" WHITESPACE "\n" error 52: expected L_BRACK -error 52: expected pattern -error 53: expected FAT_ARROW -error 78: expected `,` +error 78: expected FAT_ARROW +error 88: expected `,` +error 89: expected pattern error 161: expected L_BRACK -error 161: expected pattern -error 162: expected FAT_ARROW +error 179: expected FAT_ARROW +error 179: expected expression error 232: expected L_BRACK -error 232: expected pattern -error 233: expected FAT_ARROW -error 250: expected `,` +error 250: expected FAT_ARROW +error 260: expected `,` +error 261: expected pattern diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rast new file mode 100644 index 0000000000000..7a2e655201b67 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rast @@ -0,0 +1,46 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "main" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + PAREN_PAT + L_PAREN "(" + OR_PAT + NOT_NULL + BANG "!" + IDENT_PAT + NAME + IDENT "a" + WHITESPACE " " + PIPE "|" + WHITESPACE " " + NOT_NULL + BANG "!" + REF_PAT + AMP "&" + LITERAL_PAT + LITERAL + INT_NUMBER "0" + R_PAREN ")" + WHITESPACE " " + EQ "=" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rs new file mode 100644 index 0000000000000..f44fae5a77347 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rs @@ -0,0 +1,3 @@ +fn main() { + let (!a | !&0) = (); +} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rast new file mode 100644 index 0000000000000..c9caeb1f9b210 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rast @@ -0,0 +1,34 @@ +SOURCE_FILE + TYPE_ALIAS + TYPE_KW "type" + WHITESPACE " " + NAME + IDENT "T" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PATTERN_TYPE + BUILTIN_KW "builtin" + POUND "#" + PATTERN_TYPE_KW "pattern_type" + WHITESPACE " " + L_PAREN "(" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "u8" + WHITESPACE " " + IS_KW "is" + WHITESPACE " " + RANGE_PAT + LITERAL_PAT + LITERAL + INT_NUMBER "0" + DOT2 ".." + LITERAL_PAT + LITERAL + INT_NUMBER "10" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rs new file mode 100644 index 0000000000000..c909c7b708053 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rs @@ -0,0 +1 @@ +type T = builtin#pattern_type (u8 is 0..10); diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index a135a469e87e4..7342e0ecdcf61 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -34,9 +34,8 @@ semver.workspace = true rayon.workspace = true [features] -sysroot-abi = ["proc-macro-srv", "proc-macro-srv/sysroot-abi"] +in-rust-tree = ["proc-macro-srv", "proc-macro-srv/in-rust-tree"] default = [] -in-rust-tree = [] [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs index ba59cb219b9a4..75c3bf8d35bb3 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs @@ -12,8 +12,8 @@ use span::Span; use crate::{ ProcMacro, ProcMacroKind, ServerError, bidirectional_protocol::msg::{ - BidirectionalMessage, ExpandMacro, ExpandMacroData, ExpnGlobals, Request, Response, - SubRequest, SubResponse, + ApiVersionCheck, BidirectionalMessage, ExpandMacro, ExpandMacroData, ExpnGlobals, + ListMacros, Request, Response, SubRequest, SubResponse, }, legacy_protocol::{ SpanMode, @@ -98,7 +98,7 @@ pub(crate) fn version_check( srv: &ProcMacroServerProcess, callback: SubCallback<'_>, ) -> Result { - let request = BidirectionalMessage::Request(Request::ApiVersionCheck {}); + let request = BidirectionalMessage::Request(Request::ApiVersionCheck(ApiVersionCheck {})); let response_payload = run_request(srv, request, callback)?; @@ -135,9 +135,9 @@ pub(crate) fn find_proc_macros( dylib_path: &AbsPath, callback: SubCallback<'_>, ) -> Result, String>, ServerError> { - let request = BidirectionalMessage::Request(Request::ListMacros { + let request = BidirectionalMessage::Request(Request::ListMacros(ListMacros { dylib_path: dylib_path.to_path_buf().into(), - }); + })); let response_payload = run_request(srv, request, callback)?; @@ -186,25 +186,12 @@ pub(crate) fn expand( match response_payload { BidirectionalMessage::Response(Response::ExpandMacro(it)) => Ok(it - .map(|tree| { - let mut expanded = FlatTree::to_subtree_resolved(tree, version, &span_data_table); - if proc_macro.needs_fixup_change() { - proc_macro.change_fixup_to_match_old_server(&mut expanded); - } - expanded - }) - .map_err(|msg| msg.0)), - BidirectionalMessage::Response(Response::ExpandMacroExtended(it)) => Ok(it .map(|resp| { - let mut expanded = FlatTree::to_subtree_resolved( + FlatTree::to_subtree_resolved( resp.tree, version, &deserialize_span_data_index_map(&resp.span_data_table), - ); - if proc_macro.needs_fixup_change() { - proc_macro.change_fixup_to_match_old_server(&mut expanded); - } - expanded + ) }) .map_err(|msg| msg.0)), _ => Err(ServerError { message: "unexpected response".to_owned(), io: None }), diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs index ab4bed81e6118..e516297f06196 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs @@ -1,6 +1,8 @@ //! Bidirectional protocol messages +#![expect(clippy::disallowed_types)] use std::{ + collections::{HashMap, HashSet}, io::{self, BufRead, Write}, ops::Range, }; @@ -16,13 +18,54 @@ use crate::{ #[derive(Debug, Serialize, Deserialize)] pub enum SubRequest { - FilePath { file_id: u32 }, - SourceText { file_id: u32, ast_id: u32, start: u32, end: u32 }, - LocalFilePath { file_id: u32 }, - LineColumn { file_id: u32, ast_id: u32, offset: u32 }, - ByteRange { file_id: u32, ast_id: u32, start: u32, end: u32 }, - SpanSource { file_id: u32, ast_id: u32, start: u32, end: u32, ctx: u32 }, - SpanParent { file_id: u32, ast_id: u32, start: u32, end: u32, ctx: u32 }, + FilePath { + file_id: u32, + }, + SourceText { + file_id: u32, + ast_id: u32, + start: u32, + end: u32, + }, + LocalFilePath { + file_id: u32, + }, + LineColumn { + file_id: u32, + ast_id: u32, + offset: u32, + }, + ByteRange { + file_id: u32, + ast_id: u32, + start: u32, + end: u32, + }, + SpanSource { + file_id: u32, + ast_id: u32, + start: u32, + end: u32, + ctx: u32, + }, + SpanParent { + file_id: u32, + ast_id: u32, + start: u32, + end: u32, + ctx: u32, + }, + SpanJoin { + file_id: u32, + ast_id_first: u32, + start_first: u32, + end_first: u32, + ctx_first: u32, + ast_id_second: u32, + start_second: u32, + end_second: u32, + ctx_second: u32, + }, } #[derive(Debug, Serialize, Deserialize)] @@ -54,6 +97,9 @@ pub enum SubResponse { SpanParentResult { parent_span: Option, }, + SpanJoinResult { + span: Option, + }, Cancel { reason: String, }, @@ -68,6 +114,15 @@ pub struct ParentSpan { pub ctx: u32, } +#[derive(Debug, Serialize, Deserialize)] +pub struct SpanJoin { + pub ast_id: u32, + pub start: u32, + pub end: u32, + pub ctx: u32, +} + +#[expect(clippy::large_enum_variant)] #[derive(Debug, Serialize, Deserialize)] pub enum BidirectionalMessage { Request(Request), @@ -78,21 +133,29 @@ pub enum BidirectionalMessage { #[derive(Debug, Serialize, Deserialize)] pub enum Request { - ListMacros { dylib_path: Utf8PathBuf }, + ListMacros(ListMacros), ExpandMacro(Box), - ApiVersionCheck {}, + ApiVersionCheck(ApiVersionCheck), SetConfig(ServerConfig), } +#[expect(clippy::large_enum_variant)] #[derive(Debug, Serialize, Deserialize)] pub enum Response { ListMacros(Result, String>), - ExpandMacro(Result), ApiVersionCheck(u32), SetConfig(ServerConfig), - ExpandMacroExtended(Result), + ExpandMacro(Result), } +#[derive(Debug, Serialize, Deserialize)] +pub struct ListMacros { + pub dylib_path: Utf8PathBuf, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ApiVersionCheck {} + #[derive(Debug, Serialize, Deserialize)] pub struct ExpandMacro { pub lib: Utf8PathBuf, @@ -102,9 +165,11 @@ pub struct ExpandMacro { } #[derive(Debug, Serialize, Deserialize)] -pub struct ExpandMacroExtended { +pub struct ExpandMacroResponse { pub tree: FlatTree, pub span_data_table: Vec, + pub tracked_env_vars: HashMap, Option>>, + pub tracked_paths: HashSet>, } #[derive(Debug, Serialize, Deserialize)] diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs index bb0dde4728609..9b71a8b70c8fc 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs @@ -193,9 +193,7 @@ impl Message for Response { #[cfg(test)] mod tests { use intern::Symbol; - use span::{ - Edition, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, TextRange, TextSize, - }; + use span::{ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, TextRange, TextSize}; use tt::{ Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, TopSubtree, TopSubtreeBuilder, @@ -205,6 +203,11 @@ mod tests { use super::*; + fn make_ctx() -> SyntaxContext { + // SAFETY: Tests do not use a Database, so this won't ever be used within salsa. + unsafe { SyntaxContext::from_u32(0) } + } + fn fixture_token_tree_top_many_none() -> TopSubtree { let anchor = SpanAnchor { file_id: span::EditionedFileId::new( @@ -215,16 +218,8 @@ mod tests { }; let mut builder = TopSubtreeBuilder::new(Delimiter { - open: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, - close: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, + open: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, + close: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, kind: DelimiterKind::Invisible, }); @@ -234,7 +229,7 @@ mod tests { span: Span { range: TextRange::at(TextSize::new(0), TextSize::of("struct")), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, is_raw: tt::IdentIsRaw::No, } @@ -246,7 +241,7 @@ mod tests { span: Span { range: TextRange::at(TextSize::new(5), TextSize::of("r#Foo")), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, is_raw: tt::IdentIsRaw::Yes, } @@ -257,7 +252,7 @@ mod tests { Span { range: TextRange::at(TextSize::new(10), TextSize::of("\"Foo\"")), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, tt::LitKind::Str, ))); @@ -266,7 +261,7 @@ mod tests { span: Span { range: TextRange::at(TextSize::new(13), TextSize::of('@')), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, spacing: Spacing::Joint, })); @@ -275,7 +270,7 @@ mod tests { Span { range: TextRange::at(TextSize::new(14), TextSize::of('{')), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, ); builder.open( @@ -283,7 +278,7 @@ mod tests { Span { range: TextRange::at(TextSize::new(15), TextSize::of('[')), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, ); builder.push(Leaf::Literal(Literal::new( @@ -291,7 +286,7 @@ mod tests { Span { range: TextRange::at(TextSize::new(16), TextSize::of("0u32")), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, tt::LitKind::Integer, "u32", @@ -299,13 +294,13 @@ mod tests { builder.close(Span { range: TextRange::at(TextSize::new(20), TextSize::of(']')), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }); builder.close(Span { range: TextRange::at(TextSize::new(21), TextSize::of('}')), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }); builder.build() @@ -321,16 +316,8 @@ mod tests { }; let builder = TopSubtreeBuilder::new(Delimiter { - open: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, - close: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, + open: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, + close: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, kind: DelimiterKind::Invisible, }); @@ -347,16 +334,8 @@ mod tests { }; let builder = TopSubtreeBuilder::new(Delimiter { - open: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, - close: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, + open: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, + close: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, kind: DelimiterKind::Brace, }); @@ -405,7 +384,7 @@ mod tests { } #[test] - #[cfg(feature = "sysroot-abi")] + #[cfg(feature = "in-rust-tree")] fn test_proc_macro_rpc_works_ts() { for tt in [ fixture_token_tree_top_many_none, diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs index 248de70f0e71d..3015bd0c0eccc 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs @@ -34,7 +34,7 @@ //! as we don't have bincode in Cargo.toml yet, lets stick with serde_json for //! the time being. -#[cfg(feature = "sysroot-abi")] +#[cfg(feature = "in-rust-tree")] use proc_macro_srv::TokenStream; use std::collections::VecDeque; @@ -195,7 +195,7 @@ impl FlatTree { } } -#[cfg(feature = "sysroot-abi")] +#[cfg(feature = "in-rust-tree")] impl FlatTree { pub fn from_tokenstream( tokenstream: proc_macro_srv::TokenStream, @@ -591,7 +591,7 @@ impl<'a, T: SpanTransformer, U> Writer<'a, '_, T, U> { T::token_id_of(self.span_data_table, span) } - #[cfg(feature = "sysroot-abi")] + #[cfg(feature = "in-rust-tree")] pub(crate) fn intern(&mut self, text: &'a str) -> u32 { let table = &mut self.text; *self.string_table.entry(text.into()).or_insert_with(|| { @@ -611,7 +611,7 @@ impl<'a, T: SpanTransformer, U> Writer<'a, '_, T, U> { } } -#[cfg(feature = "sysroot-abi")] +#[cfg(feature = "in-rust-tree")] impl<'a, T: SpanTransformer> Writer<'a, '_, T, Option>> { @@ -852,7 +852,7 @@ impl> Reader<'_, T> { } } -#[cfg(feature = "sysroot-abi")] +#[cfg(feature = "in-rust-tree")] impl Reader<'_, T> { pub(crate) fn read_tokenstream( self, diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs index e83ddb859498b..149f6e44f9bea 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs @@ -5,13 +5,12 @@ //! is used to provide basic infrastructure for communication between two //! processes: Client (RA itself), Server (the external program) -#![cfg_attr(not(feature = "sysroot-abi"), allow(unused_crate_dependencies))] +#![cfg_attr(not(feature = "in-rust-tree"), allow(unused_crate_dependencies))] #![cfg_attr( - feature = "sysroot-abi", - feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span) + feature = "in-rust-tree", + feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span, rustc_private) )] #![allow(internal_features, unused_features)] -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs index 80e4ed05c36d8..035c12669c8f6 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs @@ -240,8 +240,9 @@ impl ProcMacroServerProcess { /// Enable support for rust-analyzer span mode if the server supports it. pub(crate) fn rust_analyzer_spans(&self) -> bool { match self.protocol { - Protocol::LegacyJson { mode } => mode == SpanMode::RustAnalyzer, - Protocol::BidirectionalPostcardPrototype { mode } => mode == SpanMode::RustAnalyzer, + Protocol::LegacyJson { mode } | Protocol::BidirectionalPostcardPrototype { mode } => { + mode == SpanMode::RustAnalyzer + } } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml index f586fe7644d77..44e19f2d3c3bf 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml @@ -31,9 +31,8 @@ proc-macro-test.path = "../proc-macro-srv/proc-macro-test" [features] default = [] -# default = ["sysroot-abi"] -sysroot-abi = ["proc-macro-srv/sysroot-abi", "proc-macro-api/sysroot-abi"] -in-rust-tree = ["proc-macro-srv/in-rust-tree", "sysroot-abi"] +# default = ["in-rust-tree"] +in-rust-tree = ["proc-macro-srv/in-rust-tree", "proc-macro-api/in-rust-tree"] [[bin]] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/README.md b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/README.md index 02a67ac3ecc16..93a3c7ffc2dda 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/README.md +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/README.md @@ -9,12 +9,12 @@ rust-analyzer uses a RPC (via stdio) client-server architecture for procedural m 1. Proc macros are dynamic libraries that can segfault, bringing down the entire process, so running them out of process allows rust-analyzer to recover from fatal errors. 2. Proc macro dylibs are compiled against a specific rustc version and require matching internal APIs to load and execute, as such having this binary shipped as a rustup component allows us to always match the rustc version irrespective of the rust-analyzer version used. -## The `sysroot-abi` Feature +## The `in-rust-tree` Feature -**The `sysroot-abi` feature is required for the binary to actually function.** Without it, the binary will return an error: +**The `in-rust-tree` feature is required for the binary to actually function.** Without it, the binary will return an error: ``` -proc-macro-srv-cli needs to be compiled with the `sysroot-abi` feature to function +proc-macro-srv-cli needs to be compiled with the `in-rust-tree` feature to function ``` This feature is necessary because the proc-macro server needs access to unstable rustc internals (`proc_macro_internals`, `proc_macro_diagnostic`, `proc_macro_span`) which are only available on nightly or with `RUSTC_BOOTSTRAP=1`. @@ -24,10 +24,10 @@ rust-analyzer is a stable toolchain project though, so the feature flag is used ```bash # Using nightly toolchain -cargo build -p proc-macro-srv-cli --features sysroot-abi +cargo build -p proc-macro-srv-cli --features in-rust-tree # Or with RUSTC_BOOTSTRAP on stable -RUSTC_BOOTSTRAP=1 cargo build -p proc-macro-srv-cli --features sysroot-abi +RUSTC_BOOTSTRAP=1 cargo build -p proc-macro-srv-cli --features in-rust-tree ``` ### Installing the proc-macro server @@ -42,7 +42,7 @@ cargo xtask install --proc-macro-server ## Testing ```bash -cargo test --features sysroot-abi -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api +cargo test --features in-rust-tree -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api ``` The tests use a test proc macro dylib built by the `proc-macro-test` crate, which compiles a small proc macro implementation during build time. diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs index 8475c05ae8a1e..c330928fbc9c3 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs @@ -1,11 +1,10 @@ -//! Library interface for `proc-macro-srv-cli`. -//! -//! This module exposes the server main loop and protocol format for integration testing. - -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] - -#[cfg(feature = "in-rust-tree")] -extern crate rustc_driver as _; - -#[cfg(feature = "sysroot-abi")] -pub mod main_loop; +//! Library interface for `proc-macro-srv-cli`. +//! +//! This module exposes the server main loop and protocol format for integration testing. + +#![cfg(feature = "in-rust-tree")] +#![feature(rustc_private)] + +extern crate rustc_driver as _; + +pub mod main_loop; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs index 928753659f1cc..926633df628df 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs @@ -1,7 +1,7 @@ //! A standalone binary for `proc-macro-srv`. //! Driver for proc macro server #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] -#![cfg_attr(not(feature = "sysroot-abi"), allow(unused_crate_dependencies))] +#![cfg_attr(not(feature = "in-rust-tree"), allow(unused_crate_dependencies))] #![allow(clippy::print_stdout, clippy::print_stderr)] #[cfg(feature = "in-rust-tree")] @@ -12,7 +12,7 @@ mod version; use clap::{Command, ValueEnum}; use proc_macro_api::ProtocolFormat; -#[cfg(feature = "sysroot-abi")] +#[cfg(feature = "in-rust-tree")] use proc_macro_srv_cli::main_loop::run; fn main() -> std::io::Result<()> { @@ -91,7 +91,7 @@ impl ValueEnum for ProtocolFormatArg { } } -#[cfg(not(feature = "sysroot-abi"))] +#[cfg(not(feature = "in-rust-tree"))] fn run( _: &mut std::io::BufReader, _: &mut std::io::Stdout, @@ -99,7 +99,7 @@ fn run( ) -> std::io::Result<()> { Err(std::io::Error::new( std::io::ErrorKind::Unsupported, - "proc-macro-srv-cli needs to be compiled with the `sysroot-abi` feature to function" + "proc-macro-srv-cli needs to be compiled with the `in-rust-tree` feature to function" .to_owned(), )) } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index c525ed848b2fc..6697b6380dd20 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -1,4 +1,5 @@ //! The main loop of the proc-macro server. +use proc_macro_api::bidirectional_protocol::msg::{ApiVersionCheck, ListMacros}; use proc_macro_api::{ ProtocolFormat, bidirectional_protocol::msg as bidirectional, legacy_protocol::msg as legacy, version::CURRENT_API_VERSION, @@ -72,7 +73,7 @@ fn run_new( match req { bidirectional::BidirectionalMessage::Request(request) => match request { - bidirectional::Request::ListMacros { dylib_path } => { + bidirectional::Request::ListMacros(ListMacros { dylib_path }) => { let res = srv.list_macros(&dylib_path).map(|macros| { macros .into_iter() @@ -83,7 +84,7 @@ fn run_new( send_response(stdout, bidirectional::Response::ListMacros(res))?; } - bidirectional::Request::ApiVersionCheck {} => { + bidirectional::Request::ApiVersionCheck(ApiVersionCheck {}) => { send_response( stdout, bidirectional::Response::ApiVersionCheck(CURRENT_API_VERSION), @@ -142,6 +143,7 @@ fn handle_expand_id( let attributes = attributes .map(|it| it.to_tokenstream_unresolved::(CURRENT_API_VERSION, |_, b| b)); + let mut tracked_env = Default::default(); let res = srv .expand( lib, @@ -153,11 +155,18 @@ fn handle_expand_id( def_site, call_site, mixed_site, + &mut tracked_env, None, ) .map(|it| { legacy::FlatTree::from_tokenstream_raw::(it, call_site, CURRENT_API_VERSION) }) + .map(|tree| bidirectional::ExpandMacroResponse { + tree, + span_data_table: vec![], + tracked_env_vars: tracked_env.env_vars, + tracked_paths: tracked_env.paths, + }) .map_err(|e| legacy::PanicMessage(e.into_string().unwrap_or_default())); send_response(stdout, bidirectional::Response::ExpandMacro(res)) @@ -343,6 +352,46 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { other => handle_failure(other), } } + + fn span_join( + &mut self, + first: proc_macro_srv::span::Span, + second: proc_macro_srv::span::Span, + ) -> Option { + assert_eq!(first.anchor.file_id, second.anchor.file_id); + let response = self.roundtrip(bidirectional::SubRequest::SpanJoin { + file_id: first.anchor.file_id.as_u32(), + ast_id_first: first.anchor.ast_id.into_raw(), + start_first: first.range.start().into(), + end_first: first.range.end().into(), + ctx_first: first.ctx.into_u32(), + ast_id_second: second.anchor.ast_id.into_raw(), + start_second: second.range.start().into(), + end_second: second.range.end().into(), + ctx_second: second.ctx.into_u32(), + }); + + match response { + Ok(bidirectional::SubResponse::SpanJoinResult { span }) => { + span.map(|bidirectional::SpanJoin { ast_id, start, end, ctx }| { + proc_macro_srv::span::Span { + range: proc_macro_srv::span::TextRange::new( + proc_macro_srv::span::TextSize::new(start), + proc_macro_srv::span::TextSize::new(end), + ), + anchor: proc_macro_srv::span::SpanAnchor { + file_id: first.anchor.file_id, + ast_id: proc_macro_srv::span::ErasedFileAstId::from_raw(ast_id), + }, + // SAFETY: spans originate from the server. If the protocol is violated, + // undefined behavior is the caller’s responsibility. + ctx: unsafe { proc_macro_srv::span::SyntaxContext::from_u32(ctx) }, + } + }) + } + other => handle_failure(other), + } + } } fn handle_expand_ra( @@ -383,6 +432,8 @@ fn handle_expand_ra( }) }); + let mut tracked_env = Default::default(); + let res = srv .expand( lib, @@ -394,6 +445,7 @@ fn handle_expand_ra( def_site, call_site, mixed_site, + &mut tracked_env, Some(&mut ProcMacroClientHandle { stdin, stdout, buf }), ) .map(|it| { @@ -407,10 +459,15 @@ fn handle_expand_ra( legacy::serialize_span_data_index_map(&span_data_table), ) }) - .map(|(tree, span_data_table)| bidirectional::ExpandMacroExtended { tree, span_data_table }) + .map(|(tree, span_data_table)| bidirectional::ExpandMacroResponse { + tree, + span_data_table, + tracked_env_vars: tracked_env.env_vars, + tracked_paths: tracked_env.paths, + }) .map_err(|e| legacy::PanicMessage(e.into_string().unwrap_or_default())); - send_response(stdout, bidirectional::Response::ExpandMacroExtended(res)) + send_response(stdout, bidirectional::Response::ExpandMacro(res)) } fn run_old( @@ -480,6 +537,7 @@ fn run_old( def_site, call_site, mixed_site, + &mut Default::default(), None, ) .map(|it| { @@ -522,6 +580,7 @@ fn run_old( def_site, call_site, mixed_site, + &mut Default::default(), None, ) .map(|it| { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs index ba9657a9bb45e..9c55eed08e526 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs @@ -1,7 +1,6 @@ -#![cfg(feature = "sysroot-abi")] -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#![cfg(feature = "in-rust-tree")] +#![feature(rustc_private)] -#[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; mod common { @@ -15,7 +14,10 @@ use expect_test::expect; use proc_macro_api::{ ProtocolFormat::BidirectionalPostcardPrototype, bidirectional_protocol::{ - msg::{ExpandMacro, ExpandMacroData, ExpnGlobals, Request, Response}, + msg::{ + ApiVersionCheck, ExpandMacro, ExpandMacroData, ExpnGlobals, ListMacros, Request, + Response, + }, reject_subrequests, }, legacy_protocol::msg::{PanicMessage, ServerConfig, SpanDataIndexMap, SpanMode}, @@ -25,8 +27,12 @@ use proc_macro_api::{ #[test] fn test_bidi_version_check_bidirectional() { with_server(BidirectionalPostcardPrototype, |writer, reader| { - let response = - request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); + let response = request_bidirectional( + writer, + reader, + Request::ApiVersionCheck(ApiVersionCheck {}), + reject_subrequests, + ); match response { Response::ApiVersionCheck(version) => { @@ -44,7 +50,7 @@ fn test_bidi_list_macros() { let response = request_bidirectional( writer, reader, - Request::ListMacros { dylib_path }, + Request::ListMacros(ListMacros { dylib_path }), &reject_subrequests, ); @@ -84,7 +90,7 @@ fn test_bidi_list_macros_invalid_path() { let response = request_bidirectional( writer, reader, - Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() }, + Request::ListMacros(ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() }), reject_subrequests, ); @@ -168,8 +174,12 @@ fn test_bidi_basic_call_flow() { with_server(BidirectionalPostcardPrototype, |writer, reader| { let dylib_path = proc_macro_test_dylib_path(); - let response1 = - request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); + let response1 = request_bidirectional( + writer, + reader, + Request::ApiVersionCheck(ApiVersionCheck {}), + reject_subrequests, + ); assert!(matches!(response1, Response::ApiVersionCheck(_))); let response2 = request_bidirectional( @@ -183,7 +193,7 @@ fn test_bidi_basic_call_flow() { let response3 = request_bidirectional( writer, reader, - Request::ListMacros { dylib_path: dylib_path.clone() }, + Request::ListMacros(ListMacros { dylib_path: dylib_path.clone() }), reject_subrequests, ); assert!(matches!(response3, Response::ListMacros(Ok(_)))); @@ -195,8 +205,12 @@ fn test_bidi_expand_nonexistent_macro() { with_server(BidirectionalPostcardPrototype, |writer, reader| { let dylib_path = proc_macro_test_dylib_path(); - let version_response = - request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); + let version_response = request_bidirectional( + writer, + reader, + Request::ApiVersionCheck(ApiVersionCheck {}), + reject_subrequests, + ); let Response::ApiVersionCheck(version) = version_response else { panic!("expected version check response"); }; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs index 3049e98004051..b78e10745274d 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs @@ -135,6 +135,11 @@ pub(crate) fn proc_macro_test_dylib_path() -> Utf8PathBuf { path.into() } +fn make_ctx() -> SyntaxContext { + // SAFETY: Tests do not use a Database, so this won't ever be used within salsa. + unsafe { SyntaxContext::from_u32(0) } +} + /// Creates a simple empty token tree suitable for testing. pub(crate) fn create_empty_token_tree( version: u32, @@ -144,11 +149,7 @@ pub(crate) fn create_empty_token_tree( file_id: EditionedFileId::new(FileId::from_raw(0), Edition::CURRENT), ast_id: span::ROOT_ERASED_FILE_AST_ID, }; - let span = Span { - range: TextRange::empty(0.into()), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }; + let span = Span { range: TextRange::empty(0.into()), anchor, ctx: make_ctx() }; let builder = TopSubtreeBuilder::new(Delimiter { open: span, diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs index 562cf0c2516f4..f5cbaa7421eb7 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs @@ -3,10 +3,9 @@ //! These tests exercise the full client-server RPC procedure using in-memory //! channels without needing to spawn the actual server and client processes. -#![cfg(feature = "sysroot-abi")] -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#![cfg(feature = "in-rust-tree")] +#![feature(rustc_private)] -#[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; mod common { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index 8e5617f8a20eb..a161d796936a6 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -23,8 +23,6 @@ paths.workspace = true span = { path = "../span", version = "0.0.0", default-features = false} intern.workspace = true -ra-ap-rustc_lexer.workspace = true - [target.'cfg(unix)'.dependencies] libc.workspace = true @@ -38,8 +36,11 @@ proc-macro-test.path = "./proc-macro-test" [features] default = [] -sysroot-abi = [] -in-rust-tree = ["sysroot-abi"] +# default = ["in-rust-tree"] +in-rust-tree = [] [lints] workspace = true + +[package.metadata.rust-analyzer] +rustc_private=true diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs index 9a65538675fe9..96daa2c4625cd 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs @@ -12,7 +12,7 @@ use object::Object; use paths::{Utf8Path, Utf8PathBuf}; use crate::{ - PanicMessage, ProcMacroClientHandle, ProcMacroKind, ProcMacroSrvSpan, + PanicMessage, ProcMacroClientHandle, ProcMacroKind, ProcMacroSrvSpan, TrackedEnv, dylib::proc_macros::ProcMacros, token_stream::TokenStream, }; @@ -45,14 +45,22 @@ impl Expander { def_site: S, call_site: S, mixed_site: S, - callback: Option>, + tracked_env: &'a mut TrackedEnv, + callback: Option>, ) -> Result, PanicMessage> where as bridge::server::Server>::TokenStream: Default, { - self.inner - .proc_macros - .expand(macro_name, macro_body, attribute, def_site, call_site, mixed_site, callback) + self.inner.proc_macros.expand( + macro_name, + macro_body, + attribute, + def_site, + call_site, + mixed_site, + tracked_env, + callback, + ) } pub(crate) fn list_macros(&self) -> impl Iterator { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/proc_macros.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/proc_macros.rs index cf00be0327cf2..4ed32f8e6ca1b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/proc_macros.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/proc_macros.rs @@ -1,5 +1,7 @@ //! Proc macro ABI -use crate::{ProcMacroClientHandle, ProcMacroKind, ProcMacroSrvSpan, token_stream::TokenStream}; +use crate::{ + ProcMacroClientHandle, ProcMacroKind, ProcMacroSrvSpan, TrackedEnv, token_stream::TokenStream, +}; use rustc_proc_macro::bridge; #[repr(transparent)] @@ -12,7 +14,7 @@ impl From for crate::PanicMessage { } impl ProcMacros { - pub(crate) fn expand( + pub(crate) fn expand<'a, S: ProcMacroSrvSpan>( &self, macro_name: &str, macro_body: TokenStream, @@ -20,7 +22,8 @@ impl ProcMacros { def_site: S, call_site: S, mixed_site: S, - callback: Option>, + tracked_env: &'a mut TrackedEnv, + callback: Option>, ) -> Result, crate::PanicMessage> { let parsed_attributes = attribute.unwrap_or_default(); @@ -31,7 +34,7 @@ impl ProcMacros { { let res = client.run( &bridge::server::SAME_THREAD, - S::make_server(call_site, def_site, mixed_site, callback), + S::make_server(call_site, def_site, mixed_site, tracked_env, callback), macro_body, cfg!(debug_assertions), ); @@ -40,7 +43,7 @@ impl ProcMacros { bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => { let res = client.run( &bridge::server::SAME_THREAD, - S::make_server(call_site, def_site, mixed_site, callback), + S::make_server(call_site, def_site, mixed_site, tracked_env, callback), macro_body, cfg!(debug_assertions), ); @@ -49,7 +52,7 @@ impl ProcMacros { bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => { let res = client.run( &bridge::server::SAME_THREAD, - S::make_server(call_site, def_site, mixed_site, callback), + S::make_server(call_site, def_site, mixed_site, tracked_env, callback), parsed_attributes, macro_body, cfg!(debug_assertions), diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index 0bdc379cb6264..7562a2e6648b2 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -7,33 +7,16 @@ //! //! * We use `tt` for proc-macro `TokenStream` server, it is easier to manipulate and interact with //! RA than `proc-macro2` token stream. -//! * By **copying** the whole rustc `lib_proc_macro` code, we are able to build this with `stable` -//! rustc rather than `unstable`. (Although in general ABI compatibility is still an issue)… - -#![cfg(feature = "sysroot-abi")] -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] -#![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)] -#![allow( - unreachable_pub, - internal_features, - clippy::disallowed_types, - clippy::print_stderr, - unused_crate_dependencies, - unused_features -)] + +#![cfg(feature = "in-rust-tree")] +#![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span, rustc_private)] +#![expect(unreachable_pub, internal_features, clippy::disallowed_types, clippy::print_stderr)] +#![allow(unused_features, unused_crate_dependencies)] #![deny(deprecated_safe, clippy::undocumented_unsafe_blocks)] -#[cfg(not(feature = "in-rust-tree"))] -extern crate proc_macro as rustc_proc_macro; -#[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; -#[cfg(feature = "in-rust-tree")] -extern crate rustc_proc_macro; - -#[cfg(not(feature = "in-rust-tree"))] -extern crate ra_ap_rustc_lexer as rustc_lexer; -#[cfg(feature = "in-rust-tree")] extern crate rustc_lexer; +extern crate rustc_proc_macro; mod bridge; mod dylib; @@ -41,7 +24,7 @@ mod server_impl; mod token_stream; use std::{ - collections::{HashMap, hash_map::Entry}, + collections::{HashMap, HashSet, hash_map::Entry}, env, ffi::OsString, fs, @@ -52,7 +35,7 @@ use std::{ }; use paths::{Utf8Path, Utf8PathBuf}; -use span::Span; +use span::{FIXUP_ERASED_FILE_AST_ID_MARKER, Span}; use temp_dir::TempDir; pub use crate::server_impl::token_id::SpanId; @@ -123,6 +106,7 @@ pub trait ProcMacroClientInterface { fn byte_range(&mut self, span: Span) -> Range; fn span_source(&mut self, span: Span) -> Span; fn span_parent(&mut self, span: Span) -> Option; + fn span_join(&mut self, first: Span, second: Span) -> Option; } const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; @@ -144,7 +128,7 @@ impl ExpandError { } impl ProcMacroSrv<'_> { - pub fn expand( + pub fn expand<'a, S: ProcMacroSrvSpan + 'a>( &self, lib: impl AsRef, env: &[(String, String)], @@ -155,7 +139,8 @@ impl ProcMacroSrv<'_> { def_site: S, call_site: S, mixed_site: S, - callback: Option>, + tracked_env: &'a mut TrackedEnv, + callback: Option>, ) -> Result, ExpandError> { let snapped_env = self.env; let expander = self.expander(lib.as_ref()).map_err(|err| ExpandError::Internal { @@ -172,13 +157,18 @@ impl ProcMacroSrv<'_> { .name(macro_name.to_owned()) .spawn_scoped(s, move || { expander.expand( - macro_name, macro_body, attribute, def_site, call_site, mixed_site, + macro_name, + macro_body, + attribute, + def_site, + call_site, + mixed_site, + tracked_env, callback, ) }); match thread.unwrap().join() { Ok(res) => res.map_err(ExpandError::Panic), - Err(payload) => { if let Some(marker) = payload.downcast_ref::() { return match marker { @@ -235,6 +225,12 @@ impl ProcMacroSrv<'_> { } } +#[derive(Default)] +pub struct TrackedEnv { + pub env_vars: HashMap, Option>>, + pub paths: HashSet>, +} + pub trait ProcMacroSrvSpan: Copy + Send + Sync { type Server<'a>: rustc_proc_macro::bridge::server::Server< TokenStream = crate::token_stream::TokenStream, @@ -243,6 +239,7 @@ pub trait ProcMacroSrvSpan: Copy + Send + Sync { call_site: Self, def_site: Self, mixed_site: Self, + tracked_env: &'a mut TrackedEnv, callback: Option>, ) -> Self::Server<'a>; } @@ -254,16 +251,10 @@ impl ProcMacroSrvSpan for SpanId { call_site: Self, def_site: Self, mixed_site: Self, + _: &'a mut TrackedEnv, callback: Option>, ) -> Self::Server<'a> { - Self::Server { - call_site, - def_site, - mixed_site, - callback, - tracked_env_vars: Default::default(), - tracked_paths: Default::default(), - } + Self::Server { call_site, def_site, mixed_site, callback } } } @@ -273,6 +264,7 @@ impl ProcMacroSrvSpan for Span { call_site: Self, def_site: Self, mixed_site: Self, + tracked_env: &'a mut TrackedEnv, callback: Option>, ) -> Self::Server<'a> { Self::Server { @@ -280,8 +272,8 @@ impl ProcMacroSrvSpan for Span { def_site, mixed_site, callback, - tracked_env_vars: Default::default(), - tracked_paths: Default::default(), + tracked_env, + fixup_id: FIXUP_ERASED_FILE_AST_ID_MARKER, } } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index 6b6bfcc934888..bdac9c920c433 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -4,30 +4,25 @@ //! It is an unfortunate result of how the proc-macro API works that we need to look into the //! concrete representation of the spans, and as such, RustRover cannot make use of this unless they //! change their representation to be compatible with rust-analyzer's. -use std::{ - collections::{HashMap, HashSet}, - ops::{Bound, Range}, -}; +use std::ops::{Bound, Range}; use intern::Symbol; use rustc_proc_macro::bridge::server; -use span::{FIXUP_ERASED_FILE_AST_ID_MARKER, Span, TextRange, TextSize}; +use span::{ErasedFileAstId, Span, TextRange, TextSize}; use crate::{ - ProcMacroClientHandle, + ProcMacroClientHandle, TrackedEnv, bridge::{Diagnostic, ExpnGlobals, Literal, TokenTree}, server_impl::literal_from_str, }; pub struct RaSpanServer<'a> { - // FIXME: Report this back to the caller to track as dependencies - pub tracked_env_vars: HashMap, Option>>, - // FIXME: Report this back to the caller to track as dependencies - pub tracked_paths: HashSet>, + pub tracked_env: &'a mut TrackedEnv, pub call_site: Span, pub def_site: Span, pub mixed_site: Span, pub callback: Option>, + pub fixup_id: ErasedFileAstId, } impl server::Server for RaSpanServer<'_> { @@ -56,10 +51,10 @@ impl server::Server for RaSpanServer<'_> { } fn track_env_var(&mut self, var: &str, value: Option<&str>) { - self.tracked_env_vars.insert(var.into(), value.map(Into::into)); + self.tracked_env.env_vars.insert(var.into(), value.map(Into::into)); } fn track_path(&mut self, path: &str) { - self.tracked_paths.insert(path.into()); + self.tracked_env.paths.insert(path.into()); } fn literal_from_str(&mut self, s: &str) -> Result, String> { @@ -185,24 +180,18 @@ impl server::Server for RaSpanServer<'_> { fn span_join(&mut self, first: Self::Span, second: Self::Span) -> Option { // We can't modify the span range for fixup spans, those are meaningful to fixup, so just // prefer the non-fixup span. - if first.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + if first.anchor.ast_id == self.fixup_id { return Some(second); } - if second.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + if second.anchor.ast_id == self.fixup_id { return Some(first); } - // FIXME: Once we can talk back to the client, implement a "long join" request for anchors - // that differ in [AstId]s as joining those spans requires resolving the AstIds. if first.anchor != second.anchor { - return None; + return self.callback.as_mut()?.span_join(first, second); } - // Differing context, we can't merge these so prefer the one that's root + // Differing context, we can't merge these if first.ctx != second.ctx { - if first.ctx.is_root() { - return Some(second); - } else if second.ctx.is_root() { - return Some(first); - } + return Some(first); } Some(Span { range: first.range.cover(second.range), @@ -217,7 +206,7 @@ impl server::Server for RaSpanServer<'_> { end: Bound, ) -> Option { // We can't modify the span range for fixup spans, those are meaningful to fixup. - if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + if span.anchor.ast_id == self.fixup_id { return Some(span); } let length = span.range.len().into(); @@ -260,7 +249,7 @@ impl server::Server for RaSpanServer<'_> { fn span_end(&mut self, span: Self::Span) -> Self::Span { // We can't modify the span range for fixup spans, those are meaningful to fixup. - if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + if span.anchor.ast_id == self.fixup_id { return span; } Span { range: TextRange::empty(span.range.end()), ..span } @@ -268,7 +257,7 @@ impl server::Server for RaSpanServer<'_> { fn span_start(&mut self, span: Self::Span) -> Self::Span { // We can't modify the span range for fixup spans, those are meaningful to fixup. - if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + if span.anchor.ast_id == self.fixup_id { return span; } Span { range: TextRange::empty(span.range.start()), ..span } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs index e1c96095c8fc1..6c393b8befb15 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs @@ -1,9 +1,6 @@ //! proc-macro server backend based on [`proc_macro_api::msg::SpanId`] as the backing span. //! This backend is rather inflexible, used by RustRover and older rust-analyzer versions. -use std::{ - collections::{HashMap, HashSet}, - ops::{Bound, Range}, -}; +use std::ops::{Bound, Range}; use intern::Symbol; use rustc_proc_macro::bridge::server; @@ -26,10 +23,6 @@ impl std::fmt::Debug for SpanId { type Span = SpanId; pub struct SpanIdServer<'a> { - // FIXME: Report this back to the caller to track as dependencies - pub tracked_env_vars: HashMap, Option>>, - // FIXME: Report this back to the caller to track as dependencies - pub tracked_paths: HashSet>, pub call_site: Span, pub def_site: Span, pub mixed_site: Span, @@ -60,12 +53,9 @@ impl server::Server for SpanIdServer<'_> { fn injected_env_var(&mut self, _: &str) -> Option { None } - fn track_env_var(&mut self, var: &str, value: Option<&str>) { - self.tracked_env_vars.insert(var.into(), value.map(Into::into)); - } - fn track_path(&mut self, path: &str) { - self.tracked_paths.insert(path.into()); - } + fn track_env_var(&mut self, _: &str, _: Option<&str>) {} + + fn track_path(&mut self, _: &str) {} fn literal_from_str(&mut self, s: &str) -> Result, String> { literal_from_str(s, self.call_site) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs index ebef9a9a519a7..a8ddb216f0e41 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs @@ -31,23 +31,23 @@ fn test_derive_empty() { IDENT 1 r#u32 "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..6#ROOT2024 struct - IDENT 42:Root[0000, 0]@7..8#ROOT2024 S - GROUP {} 42:Root[0000, 0]@9..10#ROOT2024 42:Root[0000, 0]@46..47#ROOT2024 42:Root[0000, 0]@9..47#ROOT2024 - IDENT 42:Root[0000, 0]@11..16#ROOT2024 field - PUNCT 42:Root[0000, 0]@16..17#ROOT2024 : [alone] - PUNCT 42:Root[0000, 0]@18..19#ROOT2024 & [joint] - PUNCT 42:Root[0000, 0]@22..23#ROOT2024 ' [joint] - IDENT 42:Root[0000, 0]@22..24#ROOT2024 r#lt - IDENT 42:Root[0000, 0]@25..27#ROOT2024 fn - GROUP () 42:Root[0000, 0]@27..28#ROOT2024 42:Root[0000, 0]@31..32#ROOT2024 42:Root[0000, 0]@27..32#ROOT2024 - IDENT 42:Root[0000, 0]@28..31#ROOT2024 u32 - PUNCT 42:Root[0000, 0]@33..34#ROOT2024 - [joint] - PUNCT 42:Root[0000, 0]@34..35#ROOT2024 > [alone] - PUNCT 42:Root[0000, 0]@36..37#ROOT2024 & [joint] - PUNCT 42:Root[0000, 0]@38..39#ROOT2024 ' [joint] - IDENT 42:Root[0000, 0]@38..39#ROOT2024 a - IDENT 42:Root[0000, 0]@42..45#ROOT2024 r#u32 + IDENT 42:Root[0000, 0]@0..6#0 struct + IDENT 42:Root[0000, 0]@7..8#0 S + GROUP {} 42:Root[0000, 0]@9..10#0 42:Root[0000, 0]@46..47#0 42:Root[0000, 0]@9..47#0 + IDENT 42:Root[0000, 0]@11..16#0 field + PUNCT 42:Root[0000, 0]@16..17#0 : [alone] + PUNCT 42:Root[0000, 0]@18..19#0 & [joint] + PUNCT 42:Root[0000, 0]@22..23#0 ' [joint] + IDENT 42:Root[0000, 0]@22..24#0 r#lt + IDENT 42:Root[0000, 0]@25..27#0 fn + GROUP () 42:Root[0000, 0]@27..28#0 42:Root[0000, 0]@31..32#0 42:Root[0000, 0]@27..32#0 + IDENT 42:Root[0000, 0]@28..31#0 u32 + PUNCT 42:Root[0000, 0]@33..34#0 - [joint] + PUNCT 42:Root[0000, 0]@34..35#0 > [alone] + PUNCT 42:Root[0000, 0]@36..37#0 & [joint] + PUNCT 42:Root[0000, 0]@38..39#0 ' [joint] + IDENT 42:Root[0000, 0]@38..39#0 a + IDENT 42:Root[0000, 0]@42..45#0 r#u32 "#]], ); } @@ -137,76 +137,76 @@ pub struct Foo { PUNCT 1 , [alone] "#]], expect![[r#" - PUNCT 42:Root[0000, 0]@1..2#ROOT2024 # [joint] - GROUP [] 42:Root[0000, 0]@2..3#ROOT2024 42:Root[0000, 0]@52..53#ROOT2024 42:Root[0000, 0]@2..53#ROOT2024 - IDENT 42:Root[0000, 0]@3..9#ROOT2024 helper - GROUP () 42:Root[0000, 0]@9..10#ROOT2024 42:Root[0000, 0]@51..52#ROOT2024 42:Root[0000, 0]@9..52#ROOT2024 - IDENT 42:Root[0000, 0]@10..18#ROOT2024 build_fn - GROUP () 42:Root[0000, 0]@18..19#ROOT2024 42:Root[0000, 0]@50..51#ROOT2024 42:Root[0000, 0]@18..51#ROOT2024 - IDENT 42:Root[0000, 0]@19..26#ROOT2024 private - PUNCT 42:Root[0000, 0]@26..27#ROOT2024 , [alone] - IDENT 42:Root[0000, 0]@28..32#ROOT2024 name - PUNCT 42:Root[0000, 0]@33..34#ROOT2024 = [alone] - LITER 42:Root[0000, 0]@35..50#ROOT2024 Str partial_build - IDENT 42:Root[0000, 0]@54..57#ROOT2024 pub - IDENT 42:Root[0000, 0]@58..64#ROOT2024 struct - IDENT 42:Root[0000, 0]@65..68#ROOT2024 Foo - GROUP {} 42:Root[0000, 0]@69..70#ROOT2024 42:Root[0000, 0]@190..191#ROOT2024 42:Root[0000, 0]@69..191#ROOT2024 - PUNCT 42:Root[0000, 0]@0..0#ROOT2024 # [alone] - GROUP [] 42:Root[0000, 0]@0..0#ROOT2024 42:Root[0000, 0]@0..0#ROOT2024 42:Root[0000, 0]@0..0#ROOT2024 - IDENT 42:Root[0000, 0]@0..0#ROOT2024 doc - PUNCT 42:Root[0000, 0]@0..0#ROOT2024 = [alone] - LITER 42:Root[0000, 0]@75..130#ROOT2024 Str / The domain where this federated instance is running - PUNCT 42:Root[0000, 0]@135..136#ROOT2024 # [joint] - GROUP [] 42:Root[0000, 0]@136..137#ROOT2024 42:Root[0000, 0]@157..158#ROOT2024 42:Root[0000, 0]@136..158#ROOT2024 - IDENT 42:Root[0000, 0]@137..143#ROOT2024 helper - GROUP () 42:Root[0000, 0]@143..144#ROOT2024 42:Root[0000, 0]@156..157#ROOT2024 42:Root[0000, 0]@143..157#ROOT2024 - IDENT 42:Root[0000, 0]@144..150#ROOT2024 setter - GROUP () 42:Root[0000, 0]@150..151#ROOT2024 42:Root[0000, 0]@155..156#ROOT2024 42:Root[0000, 0]@150..156#ROOT2024 - IDENT 42:Root[0000, 0]@151..155#ROOT2024 into - IDENT 42:Root[0000, 0]@163..166#ROOT2024 pub - GROUP () 42:Root[0000, 0]@166..167#ROOT2024 42:Root[0000, 0]@172..173#ROOT2024 42:Root[0000, 0]@166..173#ROOT2024 - IDENT 42:Root[0000, 0]@167..172#ROOT2024 crate - IDENT 42:Root[0000, 0]@174..180#ROOT2024 domain - PUNCT 42:Root[0000, 0]@180..181#ROOT2024 : [alone] - IDENT 42:Root[0000, 0]@182..188#ROOT2024 String - PUNCT 42:Root[0000, 0]@188..189#ROOT2024 , [alone] - - - PUNCT 42:Root[0000, 0]@1..2#ROOT2024 # [joint] - GROUP [] 42:Root[0000, 0]@2..3#ROOT2024 42:Root[0000, 0]@52..53#ROOT2024 42:Root[0000, 0]@2..53#ROOT2024 - IDENT 42:Root[0000, 0]@3..9#ROOT2024 helper - GROUP () 42:Root[0000, 0]@9..10#ROOT2024 42:Root[0000, 0]@51..52#ROOT2024 42:Root[0000, 0]@9..52#ROOT2024 - IDENT 42:Root[0000, 0]@10..18#ROOT2024 build_fn - GROUP () 42:Root[0000, 0]@18..19#ROOT2024 42:Root[0000, 0]@50..51#ROOT2024 42:Root[0000, 0]@18..51#ROOT2024 - IDENT 42:Root[0000, 0]@19..26#ROOT2024 private - PUNCT 42:Root[0000, 0]@26..27#ROOT2024 , [alone] - IDENT 42:Root[0000, 0]@28..32#ROOT2024 name - PUNCT 42:Root[0000, 0]@33..34#ROOT2024 = [alone] - LITER 42:Root[0000, 0]@35..50#ROOT2024 Str partial_build - IDENT 42:Root[0000, 0]@54..57#ROOT2024 pub - IDENT 42:Root[0000, 0]@58..64#ROOT2024 struct - IDENT 42:Root[0000, 0]@65..68#ROOT2024 Foo - GROUP {} 42:Root[0000, 0]@69..70#ROOT2024 42:Root[0000, 0]@190..191#ROOT2024 42:Root[0000, 0]@69..191#ROOT2024 - PUNCT 42:Root[0000, 0]@0..0#ROOT2024 # [alone] - GROUP [] 42:Root[0000, 0]@0..0#ROOT2024 42:Root[0000, 0]@0..0#ROOT2024 42:Root[0000, 0]@0..0#ROOT2024 - IDENT 42:Root[0000, 0]@0..0#ROOT2024 doc - PUNCT 42:Root[0000, 0]@0..0#ROOT2024 = [alone] - LITER 42:Root[0000, 0]@75..130#ROOT2024 Str / The domain where this federated instance is running - PUNCT 42:Root[0000, 0]@135..136#ROOT2024 # [joint] - GROUP [] 42:Root[0000, 0]@136..137#ROOT2024 42:Root[0000, 0]@157..158#ROOT2024 42:Root[0000, 0]@136..158#ROOT2024 - IDENT 42:Root[0000, 0]@137..143#ROOT2024 helper - GROUP () 42:Root[0000, 0]@143..144#ROOT2024 42:Root[0000, 0]@156..157#ROOT2024 42:Root[0000, 0]@143..157#ROOT2024 - IDENT 42:Root[0000, 0]@144..150#ROOT2024 setter - GROUP () 42:Root[0000, 0]@150..151#ROOT2024 42:Root[0000, 0]@155..156#ROOT2024 42:Root[0000, 0]@150..156#ROOT2024 - IDENT 42:Root[0000, 0]@151..155#ROOT2024 into - IDENT 42:Root[0000, 0]@163..166#ROOT2024 pub - GROUP () 42:Root[0000, 0]@166..167#ROOT2024 42:Root[0000, 0]@172..173#ROOT2024 42:Root[0000, 0]@166..173#ROOT2024 - IDENT 42:Root[0000, 0]@167..172#ROOT2024 crate - IDENT 42:Root[0000, 0]@174..180#ROOT2024 domain - PUNCT 42:Root[0000, 0]@180..181#ROOT2024 : [alone] - IDENT 42:Root[0000, 0]@182..188#ROOT2024 String - PUNCT 42:Root[0000, 0]@188..189#ROOT2024 , [alone] + PUNCT 42:Root[0000, 0]@1..2#0 # [joint] + GROUP [] 42:Root[0000, 0]@2..3#0 42:Root[0000, 0]@52..53#0 42:Root[0000, 0]@2..53#0 + IDENT 42:Root[0000, 0]@3..9#0 helper + GROUP () 42:Root[0000, 0]@9..10#0 42:Root[0000, 0]@51..52#0 42:Root[0000, 0]@9..52#0 + IDENT 42:Root[0000, 0]@10..18#0 build_fn + GROUP () 42:Root[0000, 0]@18..19#0 42:Root[0000, 0]@50..51#0 42:Root[0000, 0]@18..51#0 + IDENT 42:Root[0000, 0]@19..26#0 private + PUNCT 42:Root[0000, 0]@26..27#0 , [alone] + IDENT 42:Root[0000, 0]@28..32#0 name + PUNCT 42:Root[0000, 0]@33..34#0 = [alone] + LITER 42:Root[0000, 0]@35..50#0 Str partial_build + IDENT 42:Root[0000, 0]@54..57#0 pub + IDENT 42:Root[0000, 0]@58..64#0 struct + IDENT 42:Root[0000, 0]@65..68#0 Foo + GROUP {} 42:Root[0000, 0]@69..70#0 42:Root[0000, 0]@190..191#0 42:Root[0000, 0]@69..191#0 + PUNCT 42:Root[0000, 0]@0..0#0 # [alone] + GROUP [] 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 + IDENT 42:Root[0000, 0]@0..0#0 doc + PUNCT 42:Root[0000, 0]@0..0#0 = [alone] + LITER 42:Root[0000, 0]@75..130#0 Str / The domain where this federated instance is running + PUNCT 42:Root[0000, 0]@135..136#0 # [joint] + GROUP [] 42:Root[0000, 0]@136..137#0 42:Root[0000, 0]@157..158#0 42:Root[0000, 0]@136..158#0 + IDENT 42:Root[0000, 0]@137..143#0 helper + GROUP () 42:Root[0000, 0]@143..144#0 42:Root[0000, 0]@156..157#0 42:Root[0000, 0]@143..157#0 + IDENT 42:Root[0000, 0]@144..150#0 setter + GROUP () 42:Root[0000, 0]@150..151#0 42:Root[0000, 0]@155..156#0 42:Root[0000, 0]@150..156#0 + IDENT 42:Root[0000, 0]@151..155#0 into + IDENT 42:Root[0000, 0]@163..166#0 pub + GROUP () 42:Root[0000, 0]@166..167#0 42:Root[0000, 0]@172..173#0 42:Root[0000, 0]@166..173#0 + IDENT 42:Root[0000, 0]@167..172#0 crate + IDENT 42:Root[0000, 0]@174..180#0 domain + PUNCT 42:Root[0000, 0]@180..181#0 : [alone] + IDENT 42:Root[0000, 0]@182..188#0 String + PUNCT 42:Root[0000, 0]@188..189#0 , [alone] + + + PUNCT 42:Root[0000, 0]@1..2#0 # [joint] + GROUP [] 42:Root[0000, 0]@2..3#0 42:Root[0000, 0]@52..53#0 42:Root[0000, 0]@2..53#0 + IDENT 42:Root[0000, 0]@3..9#0 helper + GROUP () 42:Root[0000, 0]@9..10#0 42:Root[0000, 0]@51..52#0 42:Root[0000, 0]@9..52#0 + IDENT 42:Root[0000, 0]@10..18#0 build_fn + GROUP () 42:Root[0000, 0]@18..19#0 42:Root[0000, 0]@50..51#0 42:Root[0000, 0]@18..51#0 + IDENT 42:Root[0000, 0]@19..26#0 private + PUNCT 42:Root[0000, 0]@26..27#0 , [alone] + IDENT 42:Root[0000, 0]@28..32#0 name + PUNCT 42:Root[0000, 0]@33..34#0 = [alone] + LITER 42:Root[0000, 0]@35..50#0 Str partial_build + IDENT 42:Root[0000, 0]@54..57#0 pub + IDENT 42:Root[0000, 0]@58..64#0 struct + IDENT 42:Root[0000, 0]@65..68#0 Foo + GROUP {} 42:Root[0000, 0]@69..70#0 42:Root[0000, 0]@190..191#0 42:Root[0000, 0]@69..191#0 + PUNCT 42:Root[0000, 0]@0..0#0 # [alone] + GROUP [] 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 + IDENT 42:Root[0000, 0]@0..0#0 doc + PUNCT 42:Root[0000, 0]@0..0#0 = [alone] + LITER 42:Root[0000, 0]@75..130#0 Str / The domain where this federated instance is running + PUNCT 42:Root[0000, 0]@135..136#0 # [joint] + GROUP [] 42:Root[0000, 0]@136..137#0 42:Root[0000, 0]@157..158#0 42:Root[0000, 0]@136..158#0 + IDENT 42:Root[0000, 0]@137..143#0 helper + GROUP () 42:Root[0000, 0]@143..144#0 42:Root[0000, 0]@156..157#0 42:Root[0000, 0]@143..157#0 + IDENT 42:Root[0000, 0]@144..150#0 setter + GROUP () 42:Root[0000, 0]@150..151#0 42:Root[0000, 0]@155..156#0 42:Root[0000, 0]@150..156#0 + IDENT 42:Root[0000, 0]@151..155#0 into + IDENT 42:Root[0000, 0]@163..166#0 pub + GROUP () 42:Root[0000, 0]@166..167#0 42:Root[0000, 0]@172..173#0 42:Root[0000, 0]@166..173#0 + IDENT 42:Root[0000, 0]@167..172#0 crate + IDENT 42:Root[0000, 0]@174..180#0 domain + PUNCT 42:Root[0000, 0]@180..181#0 : [alone] + IDENT 42:Root[0000, 0]@182..188#0 String + PUNCT 42:Root[0000, 0]@188..189#0 , [alone] "#]], ); } @@ -232,19 +232,19 @@ fn test_derive_error() { PUNCT 1 ; [alone] "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..6#ROOT2024 struct - IDENT 42:Root[0000, 0]@7..8#ROOT2024 S - GROUP {} 42:Root[0000, 0]@9..10#ROOT2024 42:Root[0000, 0]@22..23#ROOT2024 42:Root[0000, 0]@9..23#ROOT2024 - IDENT 42:Root[0000, 0]@11..16#ROOT2024 field - PUNCT 42:Root[0000, 0]@16..17#ROOT2024 : [alone] - IDENT 42:Root[0000, 0]@18..21#ROOT2024 u32 - - - IDENT 42:Root[0000, 0]@0..13#ROOT2024 compile_error - PUNCT 42:Root[0000, 0]@13..14#ROOT2024 ! [joint] - GROUP () 42:Root[0000, 0]@14..15#ROOT2024 42:Root[0000, 0]@62..63#ROOT2024 42:Root[0000, 0]@14..63#ROOT2024 - LITER 42:Root[0000, 0]@15..62#ROOT2024 Str #[derive(DeriveError)] struct S {field : u32} - PUNCT 42:Root[0000, 0]@63..64#ROOT2024 ; [alone] + IDENT 42:Root[0000, 0]@0..6#0 struct + IDENT 42:Root[0000, 0]@7..8#0 S + GROUP {} 42:Root[0000, 0]@9..10#0 42:Root[0000, 0]@22..23#0 42:Root[0000, 0]@9..23#0 + IDENT 42:Root[0000, 0]@11..16#0 field + PUNCT 42:Root[0000, 0]@16..17#0 : [alone] + IDENT 42:Root[0000, 0]@18..21#0 u32 + + + IDENT 42:Root[0000, 0]@0..13#0 compile_error + PUNCT 42:Root[0000, 0]@13..14#0 ! [joint] + GROUP () 42:Root[0000, 0]@14..15#0 42:Root[0000, 0]@62..63#0 42:Root[0000, 0]@14..63#0 + LITER 42:Root[0000, 0]@15..62#0 Str #[derive(DeriveError)] struct S {field : u32} + PUNCT 42:Root[0000, 0]@63..64#0 ; [alone] "#]], ); } @@ -273,22 +273,22 @@ fn test_fn_like_macro_noop() { GROUP [] 1 1 1 "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..5#ROOT2024 ident - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@7..8#ROOT2024 Integer 0 - PUNCT 42:Root[0000, 0]@8..9#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@10..11#ROOT2024 Integer 1 - PUNCT 42:Root[0000, 0]@11..12#ROOT2024 , [alone] - GROUP [] 42:Root[0000, 0]@13..14#ROOT2024 42:Root[0000, 0]@14..15#ROOT2024 42:Root[0000, 0]@13..15#ROOT2024 - - - IDENT 42:Root[0000, 0]@0..5#ROOT2024 ident - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@7..8#ROOT2024 Integer 0 - PUNCT 42:Root[0000, 0]@8..9#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@10..11#ROOT2024 Integer 1 - PUNCT 42:Root[0000, 0]@11..12#ROOT2024 , [alone] - GROUP [] 42:Root[0000, 0]@13..14#ROOT2024 42:Root[0000, 0]@14..15#ROOT2024 42:Root[0000, 0]@13..15#ROOT2024 + IDENT 42:Root[0000, 0]@0..5#0 ident + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + LITER 42:Root[0000, 0]@7..8#0 Integer 0 + PUNCT 42:Root[0000, 0]@8..9#0 , [alone] + LITER 42:Root[0000, 0]@10..11#0 Integer 1 + PUNCT 42:Root[0000, 0]@11..12#0 , [alone] + GROUP [] 42:Root[0000, 0]@13..14#0 42:Root[0000, 0]@14..15#0 42:Root[0000, 0]@13..15#0 + + + IDENT 42:Root[0000, 0]@0..5#0 ident + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + LITER 42:Root[0000, 0]@7..8#0 Integer 0 + PUNCT 42:Root[0000, 0]@8..9#0 , [alone] + LITER 42:Root[0000, 0]@10..11#0 Integer 1 + PUNCT 42:Root[0000, 0]@11..12#0 , [alone] + GROUP [] 42:Root[0000, 0]@13..14#0 42:Root[0000, 0]@14..15#0 42:Root[0000, 0]@13..15#0 "#]], ); } @@ -315,20 +315,20 @@ fn test_fn_like_macro_clone_ident_subtree() { IDENT 1 ident3 "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..5#ROOT2024 ident - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - GROUP [] 42:Root[0000, 0]@7..8#ROOT2024 42:Root[0000, 0]@22..23#ROOT2024 42:Root[0000, 0]@7..23#ROOT2024 - IDENT 42:Root[0000, 0]@8..14#ROOT2024 ident2 - PUNCT 42:Root[0000, 0]@14..15#ROOT2024 , [alone] - IDENT 42:Root[0000, 0]@16..22#ROOT2024 ident3 - - - IDENT 42:Root[0000, 0]@0..5#ROOT2024 ident - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - GROUP [] 42:Root[0000, 0]@7..23#ROOT2024 42:Root[0000, 0]@7..23#ROOT2024 42:Root[0000, 0]@7..23#ROOT2024 - IDENT 42:Root[0000, 0]@8..14#ROOT2024 ident2 - PUNCT 42:Root[0000, 0]@14..15#ROOT2024 , [alone] - IDENT 42:Root[0000, 0]@16..22#ROOT2024 ident3 + IDENT 42:Root[0000, 0]@0..5#0 ident + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + GROUP [] 42:Root[0000, 0]@7..8#0 42:Root[0000, 0]@22..23#0 42:Root[0000, 0]@7..23#0 + IDENT 42:Root[0000, 0]@8..14#0 ident2 + PUNCT 42:Root[0000, 0]@14..15#0 , [alone] + IDENT 42:Root[0000, 0]@16..22#0 ident3 + + + IDENT 42:Root[0000, 0]@0..5#0 ident + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + GROUP [] 42:Root[0000, 0]@7..23#0 42:Root[0000, 0]@7..23#0 42:Root[0000, 0]@7..23#0 + IDENT 42:Root[0000, 0]@8..14#0 ident2 + PUNCT 42:Root[0000, 0]@14..15#0 , [alone] + IDENT 42:Root[0000, 0]@16..22#0 ident3 "#]], ); } @@ -345,10 +345,10 @@ fn test_fn_like_macro_clone_raw_ident() { IDENT 1 r#async "#]], expect![[r#" - IDENT 42:Root[0000, 0]@2..7#ROOT2024 r#async + IDENT 42:Root[0000, 0]@2..7#0 r#async - IDENT 42:Root[0000, 0]@2..7#ROOT2024 r#async + IDENT 42:Root[0000, 0]@2..7#0 r#async "#]], ); } @@ -366,11 +366,11 @@ fn test_fn_like_fn_like_span_join() { IDENT 1 r#joined "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..3#ROOT2024 foo - IDENT 42:Root[0000, 0]@8..11#ROOT2024 bar + IDENT 42:Root[0000, 0]@0..3#0 foo + IDENT 42:Root[0000, 0]@8..11#0 bar - IDENT 42:Root[0000, 0]@0..11#ROOT2024 r#joined + IDENT 42:Root[0000, 0]@0..11#0 r#joined "#]], ); } @@ -391,14 +391,14 @@ fn test_fn_like_fn_like_span_ops() { IDENT 1 start_span "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..12#ROOT2024 set_def_site - IDENT 42:Root[0000, 0]@13..33#ROOT2024 resolved_at_def_site - IDENT 42:Root[0000, 0]@34..44#ROOT2024 start_span + IDENT 42:Root[0000, 0]@0..12#0 set_def_site + IDENT 42:Root[0000, 0]@13..33#0 resolved_at_def_site + IDENT 42:Root[0000, 0]@34..44#0 start_span - IDENT 41:Root[0000, 0]@0..150#ROOT2024 set_def_site - IDENT 42:Root[0000, 0]@13..33#ROOT2024 resolved_at_def_site - IDENT 42:Root[0000, 0]@34..34#ROOT2024 start_span + IDENT 41:Root[0000, 0]@0..150#0 set_def_site + IDENT 42:Root[0000, 0]@13..33#0 resolved_at_def_site + IDENT 42:Root[0000, 0]@34..34#0 start_span "#]], ); } @@ -428,19 +428,19 @@ fn test_fn_like_mk_literals() { expect![[r#" - LITER 42:Root[0000, 0]@0..100#ROOT2024 ByteStr byte_string - LITER 42:Root[0000, 0]@0..100#ROOT2024 Char c - LITER 42:Root[0000, 0]@0..100#ROOT2024 Str string - LITER 42:Root[0000, 0]@0..100#ROOT2024 Str -string - LITER 42:Root[0000, 0]@0..100#ROOT2024 CStr cstring - LITER 42:Root[0000, 0]@0..100#ROOT2024 Float 3.14f64 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Float -3.14f64 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Float 3.14 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Float -3.14 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 123i64 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer -123i64 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 123 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer -123 + LITER 42:Root[0000, 0]@0..100#0 ByteStr byte_string + LITER 42:Root[0000, 0]@0..100#0 Char c + LITER 42:Root[0000, 0]@0..100#0 Str string + LITER 42:Root[0000, 0]@0..100#0 Str -string + LITER 42:Root[0000, 0]@0..100#0 CStr cstring + LITER 42:Root[0000, 0]@0..100#0 Float 3.14f64 + LITER 42:Root[0000, 0]@0..100#0 Float -3.14f64 + LITER 42:Root[0000, 0]@0..100#0 Float 3.14 + LITER 42:Root[0000, 0]@0..100#0 Float -3.14 + LITER 42:Root[0000, 0]@0..100#0 Integer 123i64 + LITER 42:Root[0000, 0]@0..100#0 Integer -123i64 + LITER 42:Root[0000, 0]@0..100#0 Integer 123 + LITER 42:Root[0000, 0]@0..100#0 Integer -123 "#]], ); } @@ -459,8 +459,8 @@ fn test_fn_like_mk_idents() { expect![[r#" - IDENT 42:Root[0000, 0]@0..100#ROOT2024 standard - IDENT 42:Root[0000, 0]@0..100#ROOT2024 r#raw + IDENT 42:Root[0000, 0]@0..100#0 standard + IDENT 42:Root[0000, 0]@0..100#0 r#raw "#]], ); } @@ -515,48 +515,48 @@ fn test_fn_like_macro_clone_literals() { LITER 1 CStr null "#]], expect![[r#" - LITER 42:Root[0000, 0]@0..4#ROOT2024 Integer 1u16 - PUNCT 42:Root[0000, 0]@4..5#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@6..11#ROOT2024 Integer 2_u32 - PUNCT 42:Root[0000, 0]@11..12#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@13..14#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@14..18#ROOT2024 Integer 4i64 - PUNCT 42:Root[0000, 0]@18..19#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@20..27#ROOT2024 Float 3.14f32 - PUNCT 42:Root[0000, 0]@27..28#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@29..43#ROOT2024 Str hello bridge - PUNCT 42:Root[0000, 0]@43..44#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@45..61#ROOT2024 Str suffixedsuffix - PUNCT 42:Root[0000, 0]@61..62#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@63..73#ROOT2024 StrRaw(2) raw - PUNCT 42:Root[0000, 0]@73..74#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@75..78#ROOT2024 Char a - PUNCT 42:Root[0000, 0]@78..79#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@80..84#ROOT2024 Byte b - PUNCT 42:Root[0000, 0]@84..85#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@86..93#ROOT2024 CStr null - - - LITER 42:Root[0000, 0]@0..4#ROOT2024 Integer 1u16 - PUNCT 42:Root[0000, 0]@4..5#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@6..11#ROOT2024 Integer 2_u32 - PUNCT 42:Root[0000, 0]@11..12#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@13..14#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@14..18#ROOT2024 Integer 4i64 - PUNCT 42:Root[0000, 0]@18..19#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@20..27#ROOT2024 Float 3.14f32 - PUNCT 42:Root[0000, 0]@27..28#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@29..43#ROOT2024 Str hello bridge - PUNCT 42:Root[0000, 0]@43..44#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@45..61#ROOT2024 Str suffixedsuffix - PUNCT 42:Root[0000, 0]@61..62#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@63..73#ROOT2024 StrRaw(2) raw - PUNCT 42:Root[0000, 0]@73..74#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@75..78#ROOT2024 Char a - PUNCT 42:Root[0000, 0]@78..79#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@80..84#ROOT2024 Byte b - PUNCT 42:Root[0000, 0]@84..85#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@86..93#ROOT2024 CStr null + LITER 42:Root[0000, 0]@0..4#0 Integer 1u16 + PUNCT 42:Root[0000, 0]@4..5#0 , [alone] + LITER 42:Root[0000, 0]@6..11#0 Integer 2_u32 + PUNCT 42:Root[0000, 0]@11..12#0 , [alone] + PUNCT 42:Root[0000, 0]@13..14#0 - [alone] + LITER 42:Root[0000, 0]@14..18#0 Integer 4i64 + PUNCT 42:Root[0000, 0]@18..19#0 , [alone] + LITER 42:Root[0000, 0]@20..27#0 Float 3.14f32 + PUNCT 42:Root[0000, 0]@27..28#0 , [alone] + LITER 42:Root[0000, 0]@29..43#0 Str hello bridge + PUNCT 42:Root[0000, 0]@43..44#0 , [alone] + LITER 42:Root[0000, 0]@45..61#0 Str suffixedsuffix + PUNCT 42:Root[0000, 0]@61..62#0 , [alone] + LITER 42:Root[0000, 0]@63..73#0 StrRaw(2) raw + PUNCT 42:Root[0000, 0]@73..74#0 , [alone] + LITER 42:Root[0000, 0]@75..78#0 Char a + PUNCT 42:Root[0000, 0]@78..79#0 , [alone] + LITER 42:Root[0000, 0]@80..84#0 Byte b + PUNCT 42:Root[0000, 0]@84..85#0 , [alone] + LITER 42:Root[0000, 0]@86..93#0 CStr null + + + LITER 42:Root[0000, 0]@0..4#0 Integer 1u16 + PUNCT 42:Root[0000, 0]@4..5#0 , [alone] + LITER 42:Root[0000, 0]@6..11#0 Integer 2_u32 + PUNCT 42:Root[0000, 0]@11..12#0 , [alone] + PUNCT 42:Root[0000, 0]@13..14#0 - [alone] + LITER 42:Root[0000, 0]@14..18#0 Integer 4i64 + PUNCT 42:Root[0000, 0]@18..19#0 , [alone] + LITER 42:Root[0000, 0]@20..27#0 Float 3.14f32 + PUNCT 42:Root[0000, 0]@27..28#0 , [alone] + LITER 42:Root[0000, 0]@29..43#0 Str hello bridge + PUNCT 42:Root[0000, 0]@43..44#0 , [alone] + LITER 42:Root[0000, 0]@45..61#0 Str suffixedsuffix + PUNCT 42:Root[0000, 0]@61..62#0 , [alone] + LITER 42:Root[0000, 0]@63..73#0 StrRaw(2) raw + PUNCT 42:Root[0000, 0]@73..74#0 , [alone] + LITER 42:Root[0000, 0]@75..78#0 Char a + PUNCT 42:Root[0000, 0]@78..79#0 , [alone] + LITER 42:Root[0000, 0]@80..84#0 Byte b + PUNCT 42:Root[0000, 0]@84..85#0 , [alone] + LITER 42:Root[0000, 0]@86..93#0 CStr null "#]], ); } @@ -593,30 +593,30 @@ fn test_fn_like_macro_negative_literals() { LITER 1 Float 2.7 "#]], expect![[r#" - PUNCT 42:Root[0000, 0]@0..1#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@1..5#ROOT2024 Integer 1u16 - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@7..8#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@9..14#ROOT2024 Integer 2_u32 - PUNCT 42:Root[0000, 0]@14..15#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@16..17#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@17..24#ROOT2024 Float 3.14f32 - PUNCT 42:Root[0000, 0]@24..25#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@26..27#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@28..31#ROOT2024 Float 2.7 - - - PUNCT 42:Root[0000, 0]@0..1#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@1..5#ROOT2024 Integer 1u16 - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@7..8#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@9..14#ROOT2024 Integer 2_u32 - PUNCT 42:Root[0000, 0]@14..15#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@16..17#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@17..24#ROOT2024 Float 3.14f32 - PUNCT 42:Root[0000, 0]@24..25#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@26..27#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@28..31#ROOT2024 Float 2.7 + PUNCT 42:Root[0000, 0]@0..1#0 - [alone] + LITER 42:Root[0000, 0]@1..5#0 Integer 1u16 + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + PUNCT 42:Root[0000, 0]@7..8#0 - [alone] + LITER 42:Root[0000, 0]@9..14#0 Integer 2_u32 + PUNCT 42:Root[0000, 0]@14..15#0 , [alone] + PUNCT 42:Root[0000, 0]@16..17#0 - [alone] + LITER 42:Root[0000, 0]@17..24#0 Float 3.14f32 + PUNCT 42:Root[0000, 0]@24..25#0 , [alone] + PUNCT 42:Root[0000, 0]@26..27#0 - [alone] + LITER 42:Root[0000, 0]@28..31#0 Float 2.7 + + + PUNCT 42:Root[0000, 0]@0..1#0 - [alone] + LITER 42:Root[0000, 0]@1..5#0 Integer 1u16 + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + PUNCT 42:Root[0000, 0]@7..8#0 - [alone] + LITER 42:Root[0000, 0]@9..14#0 Integer 2_u32 + PUNCT 42:Root[0000, 0]@14..15#0 , [alone] + PUNCT 42:Root[0000, 0]@16..17#0 - [alone] + LITER 42:Root[0000, 0]@17..24#0 Float 3.14f32 + PUNCT 42:Root[0000, 0]@24..25#0 , [alone] + PUNCT 42:Root[0000, 0]@26..27#0 - [alone] + LITER 42:Root[0000, 0]@28..31#0 Float 2.7 "#]], ); } @@ -647,20 +647,20 @@ fn test_attr_macro() { PUNCT 1 ; [alone] "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..3#ROOT2024 mod - IDENT 42:Root[0000, 0]@4..5#ROOT2024 m - GROUP {} 42:Root[0000, 0]@6..7#ROOT2024 42:Root[0000, 0]@7..8#ROOT2024 42:Root[0000, 0]@6..8#ROOT2024 + IDENT 42:Root[0000, 0]@0..3#0 mod + IDENT 42:Root[0000, 0]@4..5#0 m + GROUP {} 42:Root[0000, 0]@6..7#0 42:Root[0000, 0]@7..8#0 42:Root[0000, 0]@6..8#0 - IDENT 42:Root[0000, 0]@0..4#ROOT2024 some - IDENT 42:Root[0000, 0]@5..14#ROOT2024 arguments + IDENT 42:Root[0000, 0]@0..4#0 some + IDENT 42:Root[0000, 0]@5..14#0 arguments - IDENT 42:Root[0000, 0]@0..13#ROOT2024 compile_error - PUNCT 42:Root[0000, 0]@13..14#ROOT2024 ! [joint] - GROUP () 42:Root[0000, 0]@14..15#ROOT2024 42:Root[0000, 0]@55..56#ROOT2024 42:Root[0000, 0]@14..56#ROOT2024 - LITER 42:Root[0000, 0]@15..55#ROOT2024 Str #[attr_error(some arguments)] mod m {} - PUNCT 42:Root[0000, 0]@56..57#ROOT2024 ; [alone] + IDENT 42:Root[0000, 0]@0..13#0 compile_error + PUNCT 42:Root[0000, 0]@13..14#0 ! [joint] + GROUP () 42:Root[0000, 0]@14..15#0 42:Root[0000, 0]@55..56#0 42:Root[0000, 0]@14..56#0 + LITER 42:Root[0000, 0]@15..55#0 Str #[attr_error(some arguments)] mod m {} + PUNCT 42:Root[0000, 0]@56..57#0 ; [alone] "#]], ); } @@ -722,8 +722,8 @@ fn test_fn_like_span_line_column() { " hello", expect![[r#" - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 2 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 1 + LITER 42:Root[0000, 0]@0..100#0 Integer 2 + LITER 42:Root[0000, 0]@0..100#0 Integer 1 "#]], ); } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs index 31beca20d61ee..9780bcf3481b4 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs @@ -11,6 +11,11 @@ use crate::{ token_stream::TokenStream, }; +fn make_ctx() -> SyntaxContext { + // SAFETY: Tests do not use a Database, so this won't ever be used within salsa. + unsafe { SyntaxContext::from_u32(0) } +} + fn parse_string(call_site: SpanId, src: &str) -> TokenStream { TokenStream::from_str(src, call_site).unwrap() } @@ -62,7 +67,16 @@ fn assert_expand_impl( let attr_ts_string = attr_ts.as_ref().map(|it| format!("{it:?}")); let res = expander - .expand(macro_name, input_ts, attr_ts, def_site, call_site, mixed_site, None) + .expand( + macro_name, + input_ts, + attr_ts, + def_site, + call_site, + mixed_site, + &mut Default::default(), + None, + ) .unwrap(); expect.assert_eq(&format!( "{input_ts_string}{}{}{}", @@ -77,7 +91,7 @@ fn assert_expand_impl( file_id: EditionedFileId::current_edition(FileId::from_raw(41)), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContext::root(span::Edition::CURRENT), + ctx: make_ctx(), }; let call_site = Span { range: TextRange::new(0.into(), 100.into()), @@ -85,7 +99,7 @@ fn assert_expand_impl( file_id: EditionedFileId::current_edition(FileId::from_raw(42)), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContext::root(span::Edition::CURRENT), + ctx: make_ctx(), }; let mixed_site = call_site; @@ -94,8 +108,18 @@ fn assert_expand_impl( let fixture_string = format!("{fixture:?}"); let attr_string = attr.as_ref().map(|it| format!("{it:?}")); - let res = - expander.expand(macro_name, fixture, attr, def_site, call_site, mixed_site, None).unwrap(); + let res = expander + .expand( + macro_name, + fixture, + attr, + def_site, + call_site, + mixed_site, + &mut Default::default(), + None, + ) + .unwrap(); expect_spanned.assert_eq(&format!( "{fixture_string}{}{}{}", if attr_string.is_some() { "\n\n" } else { "" }, @@ -150,6 +174,10 @@ impl ProcMacroClientInterface for MockCallback<'_> { fn span_parent(&mut self, _span: Span) -> Option { None } + + fn span_join(&mut self, _: Span, _: Span) -> Option { + None + } } pub fn assert_expand_with_callback( @@ -166,7 +194,7 @@ pub fn assert_expand_with_callback( file_id: EditionedFileId::current_edition(FileId::from_raw(41)), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContext::root(span::Edition::CURRENT), + ctx: make_ctx(), }; let call_site = Span { range: TextRange::new(0.into(), 100.into()), @@ -174,7 +202,7 @@ pub fn assert_expand_with_callback( file_id: EditionedFileId::current_edition(FileId::from_raw(42)), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContext::root(span::Edition::CURRENT), + ctx: make_ctx(), }; let mixed_site = call_site; @@ -182,7 +210,16 @@ pub fn assert_expand_with_callback( let mut callback = MockCallback { text: ra_fixture }; let res = expander - .expand(macro_name, fixture, None, def_site, call_site, mixed_site, Some(&mut callback)) + .expand( + macro_name, + fixture, + None, + def_site, + call_site, + mixed_site, + &mut Default::default(), + Some(&mut callback), + ) .unwrap(); expect_spanned.assert_eq(&format!("{res:?}")); } diff --git a/src/tools/rust-analyzer/crates/profile/Cargo.toml b/src/tools/rust-analyzer/crates/profile/Cargo.toml index 048d3776ca33f..2cffb269fd677 100644 --- a/src/tools/rust-analyzer/crates/profile/Cargo.toml +++ b/src/tools/rust-analyzer/crates/profile/Cargo.toml @@ -13,7 +13,6 @@ rust-version.workspace = true doctest = false [dependencies] -cfg-if = "1.0.1" jemalloc-ctl = { version = "0.5.4", package = "tikv-jemalloc-ctl", optional = true } [target.'cfg(all(target_os = "linux", not(target_env = "ohos"), any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))'.dependencies] diff --git a/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs b/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs index 1462259d627b3..a8a409bd4686d 100644 --- a/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs +++ b/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs @@ -3,8 +3,6 @@ //! Measures the total size of all currently allocated objects. use std::fmt; -use cfg_if::cfg_if; - #[derive(Copy, Clone)] pub struct MemoryUsage { pub allocated: Bytes, @@ -25,15 +23,17 @@ impl std::ops::Sub for MemoryUsage { impl MemoryUsage { pub fn now() -> MemoryUsage { - cfg_if! { - if #[cfg(all(feature = "jemalloc", not(target_env = "msvc")))] { + cfg_select! { + all(feature = "jemalloc", not(target_env = "msvc")) => { jemalloc_ctl::epoch::advance().unwrap(); MemoryUsage { allocated: Bytes(jemalloc_ctl::stats::allocated::read().unwrap() as isize), } - } else if #[cfg(all(target_os = "linux", target_env = "gnu"))] { + } + all(target_os = "linux", target_env = "gnu") => { memusage_linux() - } else if #[cfg(windows)] { + } + windows => { // There doesn't seem to be an API for determining heap usage, so we try to // approximate that by using the Commit Charge value. @@ -48,7 +48,8 @@ impl MemoryUsage { let usage = unsafe { mem_counters.assume_init().PagefileUsage }; MemoryUsage { allocated: Bytes(usage as isize) } - } else { + } + _ => { MemoryUsage { allocated: Bytes(0) } } } 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 61e0b1e611cfa..1a036c3b99195 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 @@ -14,14 +14,16 @@ use hir::{ Adt, AssocItem, Crate, DefWithBody, FindPathConfig, GenericDef, HasCrate, HasSource, HirDisplay, ModuleDef, Name, Variant, crate_lang_items, db::{DefDatabase, ExpandDatabase, HirDatabase}, - next_solver::{DbInterner, GenericArgs}, }; use hir_def::{ DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, SyntheticSyntax, expr_store::{Body, BodySourceMap, ExpressionStore}, hir::{ExprId, PatId, generics::GenericParams}, }; -use hir_ty::InferenceResult; +use hir_ty::{ + InferenceResult, + next_solver::{DbInterner, GenericArgs}, +}; use ide::{ Analysis, AnalysisHost, AnnotationConfig, DiagnosticsConfig, Edition, InlayFieldsToResolve, InlayHintsConfig, LineCol, RaFixtureConfig, RootDatabase, @@ -415,7 +417,7 @@ impl flags::AnalysisStats { hir_def::AdtId::from(a), GenericArgs::empty(interner).store(), hir_ty::ParamEnvAndCrate { - param_env: db.trait_environment(GenericDefId::from(a).into()), + param_env: db.trait_environment(a.into()), krate: a.krate(db).into(), } .store(), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index f4c3f24ce6306..6f532e4224885 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -662,6 +662,9 @@ config_data! { /// For traits the type "methods" can be used to only exclude the methods but not the trait /// itself. /// + /// For modules the type "subItems" can be used to only exclude the all items in it but not the module + /// itself. This does not include items defined in nested modules. + /// /// This setting also inherits `#rust-analyzer.completion.excludeTraits#`. completion_autoimport_exclude: Vec = vec![ AutoImportExclusion::Verbose { path: "core::borrow::Borrow".to_owned(), r#type: AutoImportExclusionType::Methods }, @@ -1938,6 +1941,9 @@ impl Config { AutoImportExclusionType::Methods => { ide_completion::AutoImportExclusionType::Methods } + AutoImportExclusionType::SubItems => { + ide_completion::AutoImportExclusionType::SubItems + } }, ), }) @@ -2997,6 +3003,7 @@ pub enum AutoImportExclusion { pub enum AutoImportExclusionType { Always, Methods, + SubItems, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -4128,10 +4135,11 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json }, "type": { "type": "string", - "enum": ["always", "methods"], + "enum": ["always", "methods", "subItems"], "enumDescriptions": [ "Do not show this item or its methods (if it is a trait) in auto-import completions.", - "Do not show this traits methods in auto-import completions." + "Do not show this trait's methods in auto-import completions.", + "Do not show this module's all items in it in auto-import completions." ], }, } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index aad8bece95546..3c9a8c147d925 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -63,7 +63,12 @@ pub(crate) enum Target { } impl CargoOptions { - pub(crate) fn apply_on_command(&self, cmd: &mut Command, ws_target_dir: Option<&Utf8Path>) { + pub(crate) fn apply_on_command( + &self, + cmd: &mut Command, + ws_target_dir: Option<&Utf8Path>, + package_repr: Option<&str>, + ) { for target in &self.target_tuples { cmd.args(["--target", target.as_str()]); } @@ -83,8 +88,24 @@ impl CargoOptions { cmd.arg("--no-default-features"); } if !self.features.is_empty() { + // If we are scoped to a particular package, filter any features of the form + // `crate/feature` which target other packages. + let features = if let Some(name) = package_repr { + let filtered = self + .features + .iter() + .filter(|f| match f.split_once('/') { + Some((c, _)) => c == name, + None => true, + }) + .map(|s| s.as_str()) + .collect::>(); + filtered.join(" ") + } else { + self.features.join(" ") + }; cmd.arg("--features"); - cmd.arg(self.features.join(" ")); + cmd.arg(features); } } if let Some(target_dir) = self.target_dir_config.target_dir(ws_target_dir) { @@ -890,12 +911,18 @@ impl FlycheckActor { cmd.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info"); cmd.arg(&cargo_options.subcommand); - match scope { - FlycheckScope::Workspace => cmd.arg("--workspace"), + let package_repr = match scope { + FlycheckScope::Workspace => { + cmd.arg("--workspace"); + None + } FlycheckScope::Package { package: PackageSpecifier::Cargo { package_id }, .. - } => cmd.arg("-p").arg(&package_id.repr), + } => { + cmd.arg("-p").arg(&package_id.repr); + Some(package_id.repr.as_str()) + } FlycheckScope::Package { package: PackageSpecifier::BuildInfo { .. }, .. } => { @@ -935,6 +962,7 @@ impl FlycheckActor { cargo_options.apply_on_command( &mut cmd, self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path), + package_repr, ); cmd.args(&cargo_options.extra_args); Some((cmd, FlycheckCommandOrigin::Cargo)) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index ca6a2e70b09f5..5bc0f5f0a72aa 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -1662,7 +1662,10 @@ pub(crate) fn handle_code_lens( .map(|spec| { matches!( spec.target_kind(), - TargetKind::Bin | TargetKind::Example | TargetKind::Test + TargetKind::Bin + | TargetKind::Example + | TargetKind::Test + | TargetKind::Bench ) }) .unwrap_or(false), @@ -2345,7 +2348,7 @@ fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&TargetSpec>) -> b match &cargo_spec { Some(spec) => !matches!( spec.target_kind(), - TargetKind::Bin | TargetKind::Example | TargetKind::Test + TargetKind::Bin | TargetKind::Example | TargetKind::Test | TargetKind::Bench ), None => true, } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index a9ce6f728b6a5..92d5323e96804 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -478,6 +478,7 @@ impl GlobalState { (false, false) }; + let mut gc_elapsed = None; if self.is_quiescent() { let became_quiescent = !was_quiescent; if became_quiescent { @@ -541,8 +542,10 @@ impl GlobalState { && self.fmt_pool.handle.is_empty() && current_revision != self.last_gc_revision { + let gc_start = Instant::now(); self.analysis_host.trigger_garbage_collection(); - self.last_gc_revision = current_revision; + self.last_gc_revision = self.analysis_host.raw_database().nonce_and_revision().1; + gc_elapsed = Some(gc_start.elapsed()); } } @@ -588,10 +591,14 @@ impl GlobalState { let loop_duration = loop_start.elapsed(); if loop_duration > Duration::from_millis(100) && was_quiescent { tracing::warn!( - "overly long loop turn took {loop_duration:?} (event handling took {event_handling_duration:?}): {event_dbg_msg}" + "overly long loop turn took {loop_duration:?}:\n\ + (event handling took {event_handling_duration:?}): {event_dbg_msg}\n\ + (garbage collection took {gc_elapsed:?})" ); self.poke_rust_analyzer_developer(format!( - "overly long loop turn took {loop_duration:?} (event handling took {event_handling_duration:?}): {event_dbg_msg}" + "overly long loop turn took {loop_duration:?}:\n\ + (event handling took {event_handling_duration:?}): {event_dbg_msg}\n\ + (garbage collection took {gc_elapsed:?})" )); } } @@ -1260,6 +1267,10 @@ impl GlobalState { let mut dispatcher = RequestDispatcher { req: Some(req), global_state: self }; dispatcher.on_sync_mut::(|s, ()| { s.shutdown_requested = true; + s.proc_macro_clients = + std::iter::repeat_with(|| None).take(s.proc_macro_clients.len()).collect(); + s.flycheck.iter().for_each(|handle| handle.cancel()); + s.discover_handles.clear(); Ok(()) }); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs index 6a93a0ebb4cec..05fc7a8dd6cb9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs @@ -3,6 +3,7 @@ use std::mem; use rustc_hash::FxHashMap; +use triomphe::Arc; use vfs::VfsPath; /// Holds the set of in-memory documents. @@ -11,7 +12,7 @@ use vfs::VfsPath; /// might be different from what's on disk. #[derive(Default, Clone)] pub(crate) struct MemDocs { - mem_docs: FxHashMap, + mem_docs: Arc>>, added_or_removed: bool, } @@ -22,7 +23,7 @@ impl MemDocs { pub(crate) fn insert(&mut self, path: VfsPath, data: DocumentData) -> Result<(), ()> { self.added_or_removed = true; - match self.mem_docs.insert(path, data) { + match Arc::make_mut(&mut self.mem_docs).insert(path, Arc::new(data)) { Some(_) => Err(()), None => Ok(()), } @@ -30,20 +31,20 @@ impl MemDocs { pub(crate) fn remove(&mut self, path: &VfsPath) -> Result<(), ()> { self.added_or_removed = true; - match self.mem_docs.remove(path) { + match Arc::make_mut(&mut self.mem_docs).remove(path) { Some(_) => Ok(()), None => Err(()), } } pub(crate) fn get(&self, path: &VfsPath) -> Option<&DocumentData> { - self.mem_docs.get(path) + self.mem_docs.get(path).map(Arc::as_ref) } pub(crate) fn get_mut(&mut self, path: &VfsPath) -> Option<&mut DocumentData> { // NB: don't set `self.added_or_removed` here, as that purposefully only // tracks changes to the key set. - self.mem_docs.get_mut(path) + Arc::make_mut(&mut self.mem_docs).get_mut(path).map(Arc::make_mut) } pub(crate) fn iter(&self) -> impl Iterator { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs index 0d9c8310d8587..31f35df5c796d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs @@ -124,7 +124,7 @@ impl CargoTestHandle { cmd.arg("--no-fail-fast"); cmd.arg("--manifest-path"); cmd.arg(root.join("Cargo.toml")); - options.apply_on_command(&mut cmd, ws_target_dir); + options.apply_on_command(&mut cmd, ws_target_dir, Some(&test_target.package)); cmd.arg("--"); if let Some(path) = path { cmd.arg(path); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs index 386edb82f4e93..c6f1f81139d28 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs @@ -76,7 +76,6 @@ fn main() { } #[test] -#[ignore = "this test tends to stuck, FIXME: investigate that"] fn test_flycheck_diagnostic_with_override_command() { if skip_slow_tests() { return; @@ -113,7 +112,6 @@ fn main() {} } #[test] -#[ignore = "this test tends to stuck, FIXME: investigate that"] fn test_flycheck_diagnostics_with_override_command_cleared_after_fix() { if skip_slow_tests() { return; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs index 73904036730b9..2c4b978a4d732 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs @@ -378,8 +378,9 @@ impl Server { { continue; } + } else if req.method != "workspace/diagnostic/refresh" { + panic!("unexpected request: {req:?}") } - panic!("unexpected request: {req:?}") } Message::Notification(_) => (), Message::Response(res) => { diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index f475de93e0582..70b0447569bf4 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -19,7 +19,9 @@ //! # The Call-site Hierarchy //! //! `ExpnData::call_site` in rustc, `MacroCallLoc::call_site` in rust-analyzer. +#[cfg(feature = "salsa")] use crate::Edition; + use std::fmt; /// A syntax context describes a hierarchy tracking order of macro definitions. @@ -282,7 +284,7 @@ const _: () = { let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); fields.edition } - None => Edition::from_u32(SyntaxContext::MAX_ID - self.into_u32()), + None => Edition::from_u32(SyntaxContext::MAX_ROOT_ID - self.into_u32()), } } @@ -332,32 +334,9 @@ const _: () = { } }; -impl SyntaxContext { - #[inline] - pub fn is_root(self) -> bool { - (SyntaxContext::MAX_ID - Edition::LATEST as u32) <= self.into_u32() - && self.into_u32() <= (SyntaxContext::MAX_ID - Edition::Edition2015 as u32) - } - - #[inline] - pub fn remove_root_edition(&mut self) { - if self.is_root() { - *self = Self::root(Edition::Edition2015); - } - } - - /// The root context, which is the parent of all other contexts. All `FileId`s have this context. - #[inline] - pub const fn root(edition: Edition) -> Self { - let edition = edition as u32; - // SAFETY: Roots are valid `SyntaxContext`s - unsafe { SyntaxContext::from_u32(SyntaxContext::MAX_ID - edition) } - } -} - #[cfg(feature = "salsa")] impl<'db> SyntaxContext { - const MAX_ID: u32 = salsa::Id::MAX_U32 - 1; + const MAX_ROOT_ID: u32 = salsa::Id::MAX_U32 + Edition::LATEST as u32; #[inline] pub const fn into_u32(self) -> u32 { @@ -378,7 +357,8 @@ impl<'db> SyntaxContext { if self.is_root() { None } else { - // SAFETY: By our invariant, this is either a root (which we verified it's not) or a valid `salsa::Id`. + // SAFETY: By our invariant, this is either a root (which we verified it's not) or a + // valid `salsa::Id` index. unsafe { Some(salsa::Id::from_index(self.0)) } } } @@ -389,6 +369,27 @@ impl<'db> SyntaxContext { unsafe { Self::from_u32(id.index()) } } + #[inline] + pub fn is_root(self) -> bool { + (SyntaxContext::MAX_ROOT_ID - Edition::LATEST as u32) <= self.into_u32() + && self.into_u32() <= (SyntaxContext::MAX_ROOT_ID - Edition::Edition2015 as u32) + } + + #[inline] + pub fn remove_root_edition(&mut self) { + if self.is_root() { + *self = Self::root(Edition::Edition2015); + } + } + + /// The root context, which is the parent of all other contexts. All `FileId`s have this context. + #[inline] + pub const fn root(edition: Edition) -> Self { + let edition = edition as u32; + // SAFETY: Roots are valid `SyntaxContext`s + unsafe { SyntaxContext::from_u32(SyntaxContext::MAX_ROOT_ID - edition) } + } + #[inline] pub fn outer_mark( self, @@ -447,15 +448,8 @@ impl<'db> SyntaxContext { #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] pub struct SyntaxContext(u32); -#[allow(dead_code)] -const SALSA_MAX_ID_MIRROR: u32 = u32::MAX - 0xFF; -#[cfg(feature = "salsa")] -const _: () = assert!(salsa::Id::MAX_U32 == SALSA_MAX_ID_MIRROR); - #[cfg(not(feature = "salsa"))] impl SyntaxContext { - const MAX_ID: u32 = SALSA_MAX_ID_MIRROR - 1; - pub const fn into_u32(self) -> u32 { self.0 } @@ -496,16 +490,28 @@ impl Transparency { } } +#[cfg(feature = "salsa")] impl fmt::Display for SyntaxContext { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.is_root() { - write!(f, "ROOT{}", Edition::from_u32(SyntaxContext::MAX_ID - self.into_u32()).number()) + write!( + f, + "ROOT{}", + Edition::from_u32(SyntaxContext::MAX_ROOT_ID - self.into_u32()).number() + ) } else { write!(f, "{}", self.into_u32()) } } } +#[cfg(not(feature = "salsa"))] +impl fmt::Display for SyntaxContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.into_u32()) + } +} + impl std::fmt::Debug for SyntaxContext { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if f.alternate() { @@ -515,3 +521,69 @@ impl std::fmt::Debug for SyntaxContext { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_root_edition_is_root() { + for edition in Edition::iter() { + let ctx = SyntaxContext::root(edition); + assert!(ctx.is_root(), "{edition} root should be identified as root"); + } + } + + #[test] + fn test_root_edition_editions() { + let db = salsa::DatabaseImpl::new(); + for edition in Edition::iter() { + let ctx = SyntaxContext::root(edition); + assert_eq!(edition, ctx.edition(&db), "{edition} root should have edition {edition}"); + } + } + + #[test] + fn test_roots_do_not_overlap_with_salsa_ids() { + for edition in Edition::iter() { + let root = SyntaxContext::root(edition); + let root_u32 = root.into_u32(); + assert!( + root_u32 >= salsa::Id::MAX_U32, + "Root context for {:?} (value {}) must be >= salsa::Id::MAX_U32 ({}) to avoid collision", + edition, + root_u32, + salsa::Id::MAX_U32 + ); + } + } + + #[test] + fn test_non_root_value_is_not_root() { + for edition in Edition::iter() { + // SAFETY: This is just for testing purposes + let ctx = unsafe { SyntaxContext::from_u32(edition as u32 + 1) }; + assert!(!ctx.is_root(), "{edition} root should be identified as root"); + } + } + + #[test] + fn test_interned_context_round_trips_through_u32() { + let db = salsa::DatabaseImpl::new(); + let root = SyntaxContext::root(Edition::Edition2015); + let ctx = SyntaxContext::new( + &db, + None, + Transparency::Opaque, + Edition::Edition2021, + root, + |_| root, + |_| root, + ); + + // SAFETY: The value was produced by `SyntaxContext::into_u32` above. + let round_tripped = unsafe { SyntaxContext::from_u32(ctx.into_u32()) }; + assert_eq!(round_tripped.edition(&db), Edition::Edition2021); + assert_eq!(round_tripped.parent(&db), root); + } +} diff --git a/src/tools/rust-analyzer/crates/span/src/lib.rs b/src/tools/rust-analyzer/crates/span/src/lib.rs index bfe7b2620d56c..8274a94edb4ce 100644 --- a/src/tools/rust-analyzer/crates/span/src/lib.rs +++ b/src/tools/rust-analyzer/crates/span/src/lib.rs @@ -51,13 +51,20 @@ impl Span { } // Differing context, we can't merge these so prefer the one that's root if self.ctx != other.ctx { + #[cfg(feature = "salsa")] if self.ctx.is_root() { return Some(other); } else if other.ctx.is_root() { return Some(self); } + None + } else { + Some(Span { + range: self.range.cover(other.range), + anchor: other.anchor, + ctx: other.ctx, + }) } - Some(Span { range: self.range.cover(other.range), anchor: other.anchor, ctx: other.ctx }) } pub fn eq_ignoring_ctx(self, other: Self) -> bool { diff --git a/src/tools/rust-analyzer/crates/span/src/map.rs b/src/tools/rust-analyzer/crates/span/src/map.rs index dc7d471aa03ac..d8309b04d4fc0 100644 --- a/src/tools/rust-analyzer/crates/span/src/map.rs +++ b/src/tools/rust-analyzer/crates/span/src/map.rs @@ -6,8 +6,8 @@ use std::{fmt, hash::Hash}; use stdx::{always, itertools::Itertools}; use crate::{ - EditionedFileId, ErasedFileAstId, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, - TextRange, TextSize, + EditionedFileId, ErasedFileAstId, ROOT_ERASED_FILE_AST_ID, Span, SyntaxContext, TextRange, + TextSize, }; /// Maps absolute text ranges for the corresponding file to the relevant span data. @@ -220,6 +220,7 @@ impl RealSpanMap { Self { file_id, pairs, end } } + #[cfg(feature = "salsa")] pub fn span_for_range(&self, range: TextRange) -> Span { assert!( range.end() <= self.end, @@ -234,7 +235,7 @@ impl RealSpanMap { let (offset, ast_id) = self.pairs[idx - 1]; Span { range: range - offset, - anchor: SpanAnchor { file_id: self.file_id, ast_id }, + anchor: crate::SpanAnchor { file_id: self.file_id, ast_id }, ctx: SyntaxContext::root(self.file_id.edition()), } } diff --git a/src/tools/rust-analyzer/crates/stdx/src/assert.rs b/src/tools/rust-analyzer/crates/stdx/src/assert.rs index 91c279798c266..6032395a4dd85 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/assert.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/assert.rs @@ -43,7 +43,7 @@ /// Asserts that the condition is always true and returns its actual value. /// -/// If the condition is true does nothing and and evaluates to true. +/// If the condition is true does nothing and evaluates to true. /// /// If the condition is false: /// * panics if `force` feature or `debug_assertions` are enabled, @@ -71,7 +71,7 @@ macro_rules! always { /// Asserts that the condition is never true and returns its actual value. /// -/// If the condition is false does nothing and and evaluates to false. +/// If the condition is false does nothing and evaluates to false. /// /// If the condition is true: /// * panics if `force` feature or `debug_assertions` are enabled, diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml b/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml index c928f23e02e66..a539cea67f027 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml @@ -20,8 +20,7 @@ syntax.workspace = true parser.workspace = true tt.workspace = true stdx.workspace = true -# span = {workspace = true, default-features = false} does not work -span = { path = "../span", version = "0.0.0", default-features = false} +span.workspace = true intern.workspace = true [dev-dependencies] diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs index 648119ed7013c..001c920c9b824 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs @@ -3,6 +3,7 @@ use syntax::{ NodeOrToken, SyntaxKind::{self, *}, SyntaxNode, SyntaxToken, T, WalkEvent, + ast::syntax_factory::SyntaxFactory, syntax_editor::{Position, SyntaxEditor}, }; @@ -21,7 +22,7 @@ pub enum PrettifyWsKind { #[deprecated = "use `hir_expand::prettify_macro_expansion()` instead"] pub fn prettify_macro_expansion( syn: SyntaxNode, - dollar_crate_replacement: &mut dyn FnMut(&SyntaxToken) -> Option, + dollar_crate_replacement: &mut dyn FnMut(&SyntaxToken, &SyntaxFactory) -> Option, inspect_mods: impl FnOnce(&[(Position, PrettifyWsKind)]), ) -> SyntaxNode { let mut indent = 0; @@ -66,7 +67,7 @@ pub fn prettify_macro_expansion( }; if token.kind() == SyntaxKind::IDENT && token.text() == "$crate" - && let Some(replacement) = dollar_crate_replacement(&token) + && let Some(replacement) = dollar_crate_replacement(&token, editor.make()) { dollar_crate_replacements.push((token.clone(), replacement)); } @@ -191,7 +192,7 @@ mod tests { let source_file = syntax::ast::SourceFile::parse(&ra_fixture, span::Edition::CURRENT); let syn = remove_whitespaces(&source_file.syntax_node()); - let pretty = prettify_macro_expansion(syn, &mut |_| None, |_| ()); + let pretty = prettify_macro_expansion(syn, &mut |_, _| None, |_| ()); let mut pretty = pretty.to_string(); if pretty.contains('\n') { pretty.push('\n'); diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 6bcf8ba743bc8..408f2f4b32c75 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -665,6 +665,7 @@ Type = | NeverType | ParenType | PathType +| PatternType | PtrType | RefType | SliceType @@ -706,6 +707,9 @@ FnPtrType = ForType = ForBinder Type +PatternType = + 'builtin' '#' 'pattern_type' '(' Type 'is' Pat ')' + ImplTraitType = 'impl' TypeBoundList @@ -749,6 +753,10 @@ Pat = | TupleStructPat | ConstBlockPat | DerefPat +| NotNull + +NotNull = + '!' 'null' DerefPat = 'builtin' '#' 'deref' '(' Pat ')' 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 e3e5c499d4ea0..c18311bfa3ad1 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 @@ -1186,6 +1186,15 @@ impl NeverType { #[inline] pub fn excl_token(&self) -> Option { support::token(&self.syntax, T![!]) } } +pub struct NotNull { + pub(crate) syntax: SyntaxNode, +} +impl NotNull { + #[inline] + pub fn excl_token(&self) -> Option { support::token(&self.syntax, T![!]) } + #[inline] + pub fn null_token(&self) -> Option { support::token(&self.syntax, T![null]) } +} pub struct OffsetOfExpr { pub(crate) syntax: SyntaxNode, } @@ -1357,6 +1366,29 @@ impl PathType { #[inline] pub fn path(&self) -> Option { support::child(&self.syntax) } } +pub struct PatternType { + pub(crate) syntax: SyntaxNode, +} +impl PatternType { + #[inline] + pub fn pat(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn ty(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn pound_token(&self) -> Option { support::token(&self.syntax, T![#]) } + #[inline] + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } + #[inline] + pub fn builtin_token(&self) -> Option { support::token(&self.syntax, T![builtin]) } + #[inline] + pub fn is_token(&self) -> Option { support::token(&self.syntax, T![is]) } + #[inline] + pub fn pattern_type_token(&self) -> Option { + support::token(&self.syntax, T![pattern_type]) + } +} pub struct PrefixExpr { pub(crate) syntax: SyntaxNode, } @@ -2275,6 +2307,7 @@ pub enum Pat { IdentPat(IdentPat), LiteralPat(LiteralPat), MacroPat(MacroPat), + NotNull(NotNull), OrPat(OrPat), ParenPat(ParenPat), PathPat(PathPat), @@ -2307,6 +2340,7 @@ pub enum Type { NeverType(NeverType), ParenType(ParenType), PathType(PathType), + PatternType(PatternType), PtrType(PtrType), RefType(RefType), SliceType(SliceType), @@ -5363,6 +5397,38 @@ impl fmt::Debug for NeverType { f.debug_struct("NeverType").field("syntax", &self.syntax).finish() } } +impl AstNode for NotNull { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + NOT_NULL + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == NOT_NULL } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for NotNull { + fn hash(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for NotNull {} +impl PartialEq for NotNull { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for NotNull { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for NotNull { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("NotNull").field("syntax", &self.syntax).finish() + } +} impl AstNode for OffsetOfExpr { #[inline] fn kind() -> SyntaxKind @@ -5811,6 +5877,38 @@ impl fmt::Debug for PathType { f.debug_struct("PathType").field("syntax", &self.syntax).finish() } } +impl AstNode for PatternType { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PATTERN_TYPE + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == PATTERN_TYPE } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for PatternType { + fn hash(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for PatternType {} +impl PartialEq for PatternType { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for PatternType { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for PatternType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PatternType").field("syntax", &self.syntax).finish() + } +} impl AstNode for PrefixExpr { #[inline] fn kind() -> SyntaxKind @@ -8581,6 +8679,10 @@ impl From for Pat { #[inline] fn from(node: MacroPat) -> Pat { Pat::MacroPat(node) } } +impl From for Pat { + #[inline] + fn from(node: NotNull) -> Pat { Pat::NotNull(node) } +} impl From for Pat { #[inline] fn from(node: OrPat) -> Pat { Pat::OrPat(node) } @@ -8636,6 +8738,7 @@ impl AstNode for Pat { | IDENT_PAT | LITERAL_PAT | MACRO_PAT + | NOT_NULL | OR_PAT | PAREN_PAT | PATH_PAT @@ -8658,6 +8761,7 @@ impl AstNode for Pat { IDENT_PAT => Pat::IdentPat(IdentPat { syntax }), LITERAL_PAT => Pat::LiteralPat(LiteralPat { syntax }), MACRO_PAT => Pat::MacroPat(MacroPat { syntax }), + NOT_NULL => Pat::NotNull(NotNull { syntax }), OR_PAT => Pat::OrPat(OrPat { syntax }), PAREN_PAT => Pat::ParenPat(ParenPat { syntax }), PATH_PAT => Pat::PathPat(PathPat { syntax }), @@ -8682,6 +8786,7 @@ impl AstNode for Pat { Pat::IdentPat(it) => &it.syntax, Pat::LiteralPat(it) => &it.syntax, Pat::MacroPat(it) => &it.syntax, + Pat::NotNull(it) => &it.syntax, Pat::OrPat(it) => &it.syntax, Pat::ParenPat(it) => &it.syntax, Pat::PathPat(it) => &it.syntax, @@ -8748,6 +8853,10 @@ impl From for Type { #[inline] fn from(node: PathType) -> Type { Type::PathType(node) } } +impl From for Type { + #[inline] + fn from(node: PatternType) -> Type { Type::PatternType(node) } +} impl From for Type { #[inline] fn from(node: PtrType) -> Type { Type::PtrType(node) } @@ -8779,6 +8888,7 @@ impl AstNode for Type { | NEVER_TYPE | PAREN_TYPE | PATH_TYPE + | PATTERN_TYPE | PTR_TYPE | REF_TYPE | SLICE_TYPE @@ -8798,6 +8908,7 @@ impl AstNode for Type { NEVER_TYPE => Type::NeverType(NeverType { syntax }), PAREN_TYPE => Type::ParenType(ParenType { syntax }), PATH_TYPE => Type::PathType(PathType { syntax }), + PATTERN_TYPE => Type::PatternType(PatternType { syntax }), PTR_TYPE => Type::PtrType(PtrType { syntax }), REF_TYPE => Type::RefType(RefType { syntax }), SLICE_TYPE => Type::SliceType(SliceType { syntax }), @@ -8819,6 +8930,7 @@ impl AstNode for Type { Type::NeverType(it) => &it.syntax, Type::ParenType(it) => &it.syntax, Type::PathType(it) => &it.syntax, + Type::PatternType(it) => &it.syntax, Type::PtrType(it) => &it.syntax, Type::RefType(it) => &it.syntax, Type::SliceType(it) => &it.syntax, @@ -10453,6 +10565,11 @@ impl std::fmt::Display for NeverType { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for NotNull { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for OffsetOfExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -10523,6 +10640,11 @@ impl std::fmt::Display for PathType { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for PatternType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for PrefixExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) 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 1070af65e7598..2f7eab2423ce5 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 @@ -479,6 +479,18 @@ impl SyntaxFactory { ast } + pub fn lifetime_param(&self, lifetime: ast::Lifetime) -> ast::LifetimeParam { + let ast = make::lifetime_param(lifetime.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(lifetime.syntax().clone(), ast.lifetime().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + pub fn generic_param_list( &self, params: impl IntoIterator, diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs index 0338d976b0d58..f2b979eb9d1ae 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs @@ -3,7 +3,7 @@ use crate::{ AstToken, Direction, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, T, algo::neighbor, - ast::{self, AstNode, HasGenericParams, HasName, edit::IndentLevel, make}, + ast::{self, AstNode, HasGenericParams, HasName, edit::IndentLevel}, syntax_editor::{Position, SyntaxEditor}, }; @@ -207,6 +207,7 @@ impl ast::AssocItemList { /// Attention! This function does align the first line of `item` with respect to `self`, /// but it does _not_ change indentation of other lines (if any). pub fn add_items(&self, editor: &SyntaxEditor, items: Vec) { + let make = editor.make(); let (indent, position, whitespace) = match self.assoc_items().last() { Some(last_item) => ( IndentLevel::from_node(last_item.syntax()), @@ -228,7 +229,7 @@ impl ast::AssocItemList { .flat_map(|(i, item)| { let whitespace = if i != 0 { "\n\n" } else { whitespace }; vec![ - make::tokens::whitespace(&format!("{whitespace}{indent}")).into(), + make.whitespace(&format!("{whitespace}{indent}")).into(), item.syntax().clone().into(), ] }) @@ -390,10 +391,11 @@ impl ast::VariantList { impl ast::Fn { pub fn replace_or_insert_body(&self, editor: &SyntaxEditor, body: ast::BlockExpr) { + let make = editor.make(); if let Some(old_body) = self.body() { editor.replace(old_body.syntax(), body.syntax()); } else { - let single_space = make::tokens::single_space(); + let single_space = make.whitespace(" "); let elements = vec![single_space.into(), body.syntax().clone().into()]; if let Some(semicolon) = self.semicolon_token() { diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs index 1f6262c897c7f..730e9aeb52946 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs @@ -507,7 +507,7 @@ impl MiniCore { active_regions.drain(active_regions.len() - active_line_region..); } if inactive_line_region > 0 { - inactive_regions.drain(inactive_regions.len() - active_line_region..); + inactive_regions.drain(inactive_regions.len() - inactive_line_region..); } } diff --git a/src/tools/rust-analyzer/crates/test-utils/src/lib.rs b/src/tools/rust-analyzer/crates/test-utils/src/lib.rs index 62867fd5b55b6..4eab7f4b18424 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/lib.rs @@ -260,7 +260,7 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> { let &(_, idx) = prev_line_annotations .iter() .find(|&&(off, _idx)| off == offset) - .unwrap(); + .expect("annotation continuation not found"); res[idx].1.push('\n'); res[idx].1.push_str(&content); res[idx].1.push('\n'); 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 8975fa56d7272..bd15dca609b12 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -54,11 +54,12 @@ //! iterators: iterator, fn //! manually_drop: drop //! matches: -//! non_null: -//! non_zero: +//! non_null: pat +//! non_zero: pat, transmute, option //! option: panic //! ord: eq, option //! panic: fmt +//! pat: panic //! phantom_data: //! pin: //! pointee: copy, send, sync, ord, hash, unpin, phantom_data @@ -538,10 +539,10 @@ pub mod ptr { // endregion:pointee // region:non_null - #[rustc_layout_scalar_valid_range_start(1)] #[rustc_nonnull_optimization_guaranteed] + #[repr(transparent)] pub struct NonNull { - pointer: *const T, + pointer: crate::pattern_type!(*const T is !null), } // region:coerce_unsized impl @@ -1713,6 +1714,43 @@ pub mod result { #[lang = "Err"] Err(E), } + impl Result { + pub const fn or(self, res: Result) -> Result { + match self { + Ok(v) => Ok(v), + Err(_) => res, + } + } + + pub const fn unwrap_or(self, default: T) -> T { + match self { + Ok(t) => t, + Err(_) => default, + } + } + + // region:fn + pub const fn or_else(self, op: O) -> Result + where + O: FnOnce(E) -> Result, + { + match self { + Ok(t) => Ok(t), + Err(e) => op(e), + } + } + + pub const fn unwrap_or_else(self, op: F) -> T + where + F: FnOnce(E) -> T, + { + match self { + Ok(t) => t, + Err(e) => op(e), + } + } + // endregion:fn + } } // endregion:result @@ -2278,12 +2316,61 @@ mod macros { // endregion:deref_pat } +// region:pat +pub mod pat { + #[macro_export] + #[rustc_builtin_macro(pattern_type)] + macro_rules! pattern_type { + ($($arg:tt)*) => { + /* compiler built-in */ + }; + } + + pub const trait RangePattern { + #[lang = "RangeMin"] + const MIN: Self; + + #[lang = "RangeMax"] + const MAX: Self; + + #[lang = "RangeSub"] + fn sub_one(self) -> Self; + } + + impl const RangePattern for u8 { + const MIN: u8 = 0; + const MAX: u8 = 0xFF; + fn sub_one(self) -> Self { + if self == Self::MIN { + panic!("exclusive range end at minimum value of type") + } else { + self - 1 + } + } + } + + // region:coerce_unsized + impl + crate::ops::CoerceUnsized for pattern_type!(*const T is !null) + where + T: crate::marker::Unsize, + { + } + // endregion:coerce_unsized + + // region:dispatch_from_dyn + impl, U> + crate::ops::DispatchFromDyn for pattern_type!(T is !null) + { + } + // endregion:dispatch_from_dyn +} +// endregion:pat + // region:non_zero pub mod num { #[repr(transparent)] - #[rustc_layout_scalar_valid_range_start(1)] - #[rustc_nonnull_optimization_guaranteed] - pub struct NonZeroU8(u8); + pub struct NonZeroU8(crate::pattern_type!(u8 is 1..)); } // endregion:non_zero @@ -2392,6 +2479,7 @@ pub mod prelude { hash::derive::Hash, // :hash, derive iter::{FromIterator, IntoIterator, Iterator}, // :iterator macros::builtin::{derive, derive_const}, // :derive + macros::deref, // :deref_pat marker::Copy, // :copy marker::Send, // :send marker::Sized, // :sized @@ -2405,7 +2493,6 @@ pub mod prelude { panic, // :panic result::Result::{self, Err, Ok}, // :result str::FromStr, // :str - macros::deref, // :deref_pat }; } diff --git a/src/tools/rust-analyzer/crates/tt/Cargo.toml b/src/tools/rust-analyzer/crates/tt/Cargo.toml index 6cfb76400e3cc..9a798b592d03c 100644 --- a/src/tools/rust-analyzer/crates/tt/Cargo.toml +++ b/src/tools/rust-analyzer/crates/tt/Cargo.toml @@ -24,7 +24,6 @@ intern.workspace = true ra-ap-rustc_lexer.workspace = true [features] -default = [] in-rust-tree = [] [lints] diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs index 72b0d762ef62d..7b46c33596441 100644 --- a/src/tools/rust-analyzer/crates/tt/src/lib.rs +++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs @@ -765,27 +765,24 @@ impl Subtree { pub fn pretty(tkns: TokenTreesView<'_>) -> String { return dispatch_ref! { - match tkns.repr => tt => pretty_impl(tt) + match tkns.repr => tt => pretty_impl(tkns, tt) }; use crate::storage::TokenTree; - fn tokentree_to_text(tkn: &TokenTree, tkns: &mut &[TokenTree]) -> String { + fn tokentree_to_text( + tkns_view: TokenTreesView<'_>, + tkn: &TokenTree, + tkns: &mut &[TokenTree], + ) -> String { match tkn { TokenTree::Ident { sym, is_raw, .. } => format!("{}{}", is_raw.as_str(), sym), - &TokenTree::Literal { ref text_and_suffix, kind, suffix_len, span: _ } => { + &TokenTree::Literal { ref text_and_suffix, kind, suffix_len, span } => { format!( "{}", Literal { text_and_suffix: text_and_suffix.clone(), - span: Span { - range: TextRange::empty(TextSize::new(0)), - anchor: span::SpanAnchor { - file_id: span::EditionedFileId::from_raw(0), - ast_id: span::FIXUP_ERASED_FILE_AST_ID_MARKER - }, - ctx: span::SyntaxContext::root(span::Edition::Edition2015) - }, + span: span.span(tkns_view.span_parts), kind, suffix_len } @@ -794,7 +791,7 @@ pub fn pretty(tkns: TokenTreesView<'_>) -> String { TokenTree::Punct { char, .. } => format!("{}", char), TokenTree::Subtree { len, delim_kind, .. } => { let (subtree_content, rest) = tkns.split_at(*len as usize); - let content = pretty_impl(subtree_content); + let content = pretty_impl(tkns_view, subtree_content); *tkns = rest; let (open, close) = match *delim_kind { DelimiterKind::Brace => ("{", "}"), @@ -807,13 +804,16 @@ pub fn pretty(tkns: TokenTreesView<'_>) -> String { } } - fn pretty_impl(mut tkns: &[TokenTree]) -> String { + fn pretty_impl( + tkns_view: TokenTreesView<'_>, + mut tkns: &[TokenTree], + ) -> String { let mut last = String::new(); let mut last_to_joint = true; while let Some((tkn, rest)) = tkns.split_first() { tkns = rest; - last = [last, tokentree_to_text(tkn, &mut tkns)].join(if last_to_joint { + last = [last, tokentree_to_text(tkns_view, tkn, &mut tkns)].join(if last_to_joint { "" } else { " " diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 069c8211dbb6a..7bbb9e0258169 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -446,6 +446,9 @@ verbose form `{ "path": "path::to::item", type: "always" }`. For traits the type "methods" can be used to only exclude the methods but not the trait itself. +For modules the type "subItems" can be used to only exclude the all items in it but not the module +itself. This does not include items defined in nested modules. + This setting also inherits `#rust-analyzer.completion.excludeTraits#`. diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 14369e6f33e43..8df606d4c4ca8 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -1328,7 +1328,7 @@ "title": "Completion", "properties": { "rust-analyzer.completion.autoimport.exclude": { - "markdownDescription": "A list of full paths to items to exclude from auto-importing completions.\n\nTraits in this list won't have their methods suggested in completions unless the trait\nis in scope.\n\nYou can either specify a string path which defaults to type \"always\" or use the more\nverbose form `{ \"path\": \"path::to::item\", type: \"always\" }`.\n\nFor traits the type \"methods\" can be used to only exclude the methods but not the trait\nitself.\n\nThis setting also inherits `#rust-analyzer.completion.excludeTraits#`.", + "markdownDescription": "A list of full paths to items to exclude from auto-importing completions.\n\nTraits in this list won't have their methods suggested in completions unless the trait\nis in scope.\n\nYou can either specify a string path which defaults to type \"always\" or use the more\nverbose form `{ \"path\": \"path::to::item\", type: \"always\" }`.\n\nFor traits the type \"methods\" can be used to only exclude the methods but not the trait\nitself.\n\nFor modules the type \"subItems\" can be used to only exclude the all items in it but not the module\nitself. This does not include items defined in nested modules.\n\nThis setting also inherits `#rust-analyzer.completion.excludeTraits#`.", "default": [ { "path": "core::borrow::Borrow", @@ -1355,11 +1355,13 @@ "type": "string", "enum": [ "always", - "methods" + "methods", + "subItems" ], "enumDescriptions": [ "Do not show this item or its methods (if it is a trait) in auto-import completions.", - "Do not show this traits methods in auto-import completions." + "Do not show this trait's methods in auto-import completions.", + "Do not show this module's all items in it in auto-import completions." ] } } diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index 1e8579c04511c..4b1ba970b7c4b 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -8afb6a8b1b32fce2f8aa7520517833338dc36c5e +ba0949ab745985a442e274ba52e8fb348cb0c662 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 43462d1c6e044..49a625bdbd400 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 @@ -123,6 +123,7 @@ const CONTEXTUAL_KEYWORDS: &[&str] = &[ "bikeshed", "cfg_attr", "cfg", + "null", ]; // keywords we use for special macro expansions const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[ @@ -151,6 +152,8 @@ const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[ "readonly", "sym", "deref", + "pattern_type", + "is", ]; // keywords that are keywords depending on the edition diff --git a/src/tools/rust-analyzer/xtask/src/dist.rs b/src/tools/rust-analyzer/xtask/src/dist.rs index 57a6a0eae1be8..e8bedbe79e56e 100644 --- a/src/tools/rust-analyzer/xtask/src/dist.rs +++ b/src/tools/rust-analyzer/xtask/src/dist.rs @@ -107,7 +107,9 @@ fn dist_server( ) -> anyhow::Result<()> { let _e = sh.push_env("CFG_RELEASE", release); let _e = sh.push_env("CARGO_PROFILE_RELEASE_LTO", "thin"); + let _e = sh.push_env("CARGO_PROFILE_RELEASE_CODEGEN_UNITS", "1"); let _e = sh.push_env("CARGO_PROFILE_DEV_REL_LTO", "thin"); + let _e = sh.push_env("CARGO_PROFILE_DEV_REL_CODEGEN_UNITS", "1"); // Uncomment to enable debug info for releases. Note that: // * debug info is split on windows and macs, so it does nothing for those platforms, diff --git a/src/tools/rust-analyzer/xtask/src/install.rs b/src/tools/rust-analyzer/xtask/src/install.rs index a803b4e943a6f..bbb6d9aeac25e 100644 --- a/src/tools/rust-analyzer/xtask/src/install.rs +++ b/src/tools/rust-analyzer/xtask/src/install.rs @@ -179,7 +179,7 @@ fn install_proc_macro_server(sh: &Shell, opts: ProcMacroServerOpt) -> anyhow::Re let mut cmd = cmd!( sh, - "cargo install --path crates/proc-macro-srv-cli --profile={profile} --locked --force --features sysroot-abi" + "cargo install --path crates/proc-macro-srv-cli --profile={profile} --locked --force --features in-rust-tree" ); if std::env::var_os("RUSTUP_TOOLCHAIN").is_none() { cmd = cmd.env("RUSTUP_TOOLCHAIN", "nightly"); diff --git a/src/tools/rust-analyzer/xtask/src/pgo.rs b/src/tools/rust-analyzer/xtask/src/pgo.rs index 0c6f499811a24..ca6dace940b54 100644 --- a/src/tools/rust-analyzer/xtask/src/pgo.rs +++ b/src/tools/rust-analyzer/xtask/src/pgo.rs @@ -91,7 +91,7 @@ fn download_crate_for_training(sh: &Shell, pgo_dir: &Path, repo: &str) -> anyhow let target_path = pgo_dir.join(normalized_path); cmd!(sh, "git clone --depth 1 https://github.com/{repo} {revision...} {target_path}") .run() - .with_context(|| "cannot download PGO training crate from {repo}")?; + .with_context(|| format!("cannot download PGO training crate from {repo}"))?; Ok(target_path) } diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index ef08286122916..89aae46ec8f23 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -597,6 +597,7 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ "log", "mach2", "memchr", + "memmap2", "object", "proc-macro2", "quote", diff --git a/src/tools/unicode-table-generator/Cargo.toml b/src/tools/unicode-table-generator/Cargo.toml index 3ca6e9e316f1d..3be916dc69bf5 100644 --- a/src/tools/unicode-table-generator/Cargo.toml +++ b/src/tools/unicode-table-generator/Cargo.toml @@ -7,3 +7,4 @@ edition = "2024" [dependencies] ucd-parse = "0.1.3" +rustc-hash = "2.0.0" diff --git a/src/tools/unicode-table-generator/src/cascading_map.rs b/src/tools/unicode-table-generator/src/cascading_map.rs index 56e6401908dcf..da06049beb575 100644 --- a/src/tools/unicode-table-generator/src/cascading_map.rs +++ b/src/tools/unicode-table-generator/src/cascading_map.rs @@ -1,7 +1,8 @@ -use std::collections::HashMap; use std::fmt::Write as _; use std::ops::Range; +use rustc_hash::FxHashMap; + use crate::fmt_list; use crate::raw_emitter::RawEmitter; @@ -27,7 +28,7 @@ impl RawEmitter { println!("there are {} points", points.len()); // how many distinct ranges need to be counted? - let mut codepoints_by_high_bytes = HashMap::>::new(); + let mut codepoints_by_high_bytes = FxHashMap::>::default(); for point in points { // assert that there is no whitespace over the 0x3000 range. assert!(point <= 0x3000, "the highest unicode whitespace value has changed"); diff --git a/src/tools/unicode-table-generator/src/case_mapping.rs b/src/tools/unicode-table-generator/src/case_mapping.rs index b7b385542ef53..9336eaf670e6a 100644 --- a/src/tools/unicode-table-generator/src/case_mapping.rs +++ b/src/tools/unicode-table-generator/src/case_mapping.rs @@ -48,21 +48,33 @@ use std::ops::RangeInclusive; use crate::fmt_helpers::Hex; use crate::{UnicodeData, fmt_list}; -pub(crate) fn generate_case_mapping(data: &UnicodeData) -> (String, [(String, usize); 3]) { +pub(crate) fn generate_case_mapping(data: &UnicodeData) -> (String, [(String, usize); 4]) { let mut file = String::new(); file.push_str("\n\n"); file.push_str(HEADER.trim_start()); file.push('\n'); - let (lower_tables, lower_desc, lower_size) = generate_tables("LOWER", &data.to_lower); + let (lower_tables, lower_desc, lower_size) = generate_tables("LOWERCASE", &data.to_lower); file.push_str(&lower_tables); file.push_str("\n\n"); - let (upper_tables, upper_desc, upper_size) = generate_tables("UPPER", &data.to_upper); + let (upper_tables, upper_desc, upper_size) = generate_tables("UPPERCASE", &data.to_upper); file.push_str(&upper_tables); file.push_str("\n\n"); - let (title_tables, title_desc, title_size) = generate_tables("TITLE", &data.to_title); + let (title_tables, title_desc, title_size) = generate_tables("TITLECASE", &data.to_title); file.push_str(&title_tables); - (file, [(lower_desc, lower_size), (upper_desc, upper_size), (title_desc, title_size)]) + file.push_str("\n\n"); + let (casefold_tables, casefold_desc, casefold_size) = + generate_tables("CASEFOLD", &data.to_casefold); + file.push_str(&casefold_tables); + ( + file, + [ + (lower_desc, lower_size), + (upper_desc, upper_size), + (title_desc, title_size), + (casefold_desc, casefold_size), + ], + ) } // So far, only planes 0 and 1 (Basic Multilingual Plane and Supplementary @@ -205,7 +217,7 @@ fn generate_tables(case: &str, data: &BTreeMap) -> (String, Strin output_high, input_high, "Case-mapping a character should not change its plane" ); - let delta = output_low as i16 - input_low as i16; + let delta = output_low.wrapping_sub(input_low).cast_signed(); let range = Range::singleton(input_low); l2_lut.singles.push((range, delta)); } @@ -264,7 +276,7 @@ fn generate_tables(case: &str, data: &BTreeMap) -> (String, Strin let size = l1_lut.size(); let num_ranges = l1_lut.l2_luts.iter().map(|l2| l2.singles.len() + l2.multis.len()).sum::(); - let table = format!("static {case}CASE_LUT: L1Lut = {l1_lut:#?};"); + let table = format!("static {case}_LUT: L1Lut = {l1_lut:#?};"); let desc = format!( "{:6} codepoints in {:3} ranges (U+{:06X} - U+{:06X}) using 2-level LUT", data.len(), @@ -381,7 +393,7 @@ fn lookup(input: char, l1_lut: &L1Lut) -> Option<[char; 3]> { } pub fn to_lower(c: char) -> [char; 3] { - // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%253AChanges_When_Lowercased%253A%5D-%5B%253AASCII%253A%5D&abb=on + // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=[:Changes_When_Lowercased:]-[:ASCII:]&abb=on if c < '\u{C0}' { return [c.to_ascii_lowercase(), '\0', '\0']; } @@ -390,7 +402,7 @@ pub fn to_lower(c: char) -> [char; 3] { } pub fn to_upper(c: char) -> [char; 3] { - // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%253AChanges_When_Uppercased%253A%5D-%5B%253AASCII%253A%5D&abb=on + // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=[:Changes_When_Uppercased:]-[:ASCII:]&abb=on if c < '\u{B5}' { return [c.to_ascii_uppercase(), '\0', '\0']; } @@ -399,11 +411,93 @@ pub fn to_upper(c: char) -> [char; 3] { } pub fn to_title(c: char) -> [char; 3] { - // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%253AChanges_When_Titlecased%253A%5D-%5B%253AASCII%253A%5D&abb=on + // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=[:Changes_When_Titlecased:]-[:ASCII:]&abb=on if c < '\u{B5}' { return [c.to_ascii_uppercase(), '\0', '\0']; } lookup(c, &TITLECASE_LUT).or_else(|| lookup(c, &UPPERCASE_LUT)).unwrap_or([c, '\0', '\0']) } + +pub fn to_casefold(c: char) -> [char; 3] { + // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=[:Changes_When_Casefolded:]-[:ASCII:]&abb=on + if c < '\u{B5}' { + return [c.to_ascii_lowercase(), '\0', '\0']; + } + + + lookup(c, &CASEFOLD_LUT).unwrap_or_else(|| { + // Fall back to lowercase of uppercase + + let uppercase = lookup(c, &UPPERCASE_LUT).unwrap_or([c, '\0', '\0']); + + // We need to take the lowercase of each character in `uppercase`, + // and then concatenate them together. + + // Lowercase the first uppercased char + let mut final_result = to_lower(uppercase[0]); + + if uppercase[1] != '\0' { + // There's a 2nd uppercase char, lowercase it as well + let lowercase_1 = to_lower(uppercase[1]); + + // The lowercase of the second uppercase character + // can't be 3 chars long; + // that would bring the total case-folding length + // above 3 characters, which would violate + // a Unicode stability guarantee. + debug_assert_eq!(lowercase_1[2], '\0'); + + // Currently, in every case where there + // are multiple uppercased characters, + // the lowercase of the first uppercase + // has length 1. However, Unicode doesn't + // guarantee this. + // If, after updating the Unicode data + // to a new Unicode version, the below + // assertion starts to fail in + // `coretests/tests/unicode.rs` `to_casefold()`, + // delete it, and uncomment the + // `if` condition and corresponding + // `else` block below it. + debug_assert_eq!(final_result[1], '\0'); + //if final_result[1] == '\0' { + + final_result[1] = lowercase_1[0]; + + if uppercase[2] != '\0' { + // There's a 3rd uppercased char, lowercase it as well. + // Because of the Unicode stability guarantee that case-folding + // does not expand a string more than 3x in length, + // we know this lowercase must be 1 char long. + + debug_assert_eq!(lowercase_1[1], '\0'); + let lowercase_2 = to_lower(uppercase[2]); + debug_assert_eq!(lowercase_2[1], '\0'); + debug_assert_eq!(lowercase_2[2], '\0'); + final_result[2] = lowercase_2[0]; + } else { + // Currently, the lowercase of + // the second uppercase character + // can't be 2 chars long either, + // but Unicode doesn't guarantee this. + // If, after updating the Unicode data + // to a new Unicode version, the below + // assertion starts to fail in + // `coretests/tests/unicode.rs` `to_casefold()`, + // delete it and uncomment the line + // below it. + debug_assert_eq!(lowercase_1[1], '\0'); + //final_result[2] = lowercase_1[1]; + } + + /*} else { + final_result[2] = lowercase_1[0]; + debug_assert_eq!(lowercase_1[1], '\0'); + debug_assert_eq!(uppercase[2], '\0') + }*/ + } + final_result + }) +} "; diff --git a/src/tools/unicode-table-generator/src/main.rs b/src/tools/unicode-table-generator/src/main.rs index 398b4c7b7ec5a..a55cd2f657a6d 100644 --- a/src/tools/unicode-table-generator/src/main.rs +++ b/src/tools/unicode-table-generator/src/main.rs @@ -71,11 +71,12 @@ //! index of that offset is utilized as the answer to whether we're in the set //! or not. -use std::collections::{BTreeMap, HashMap}; +use std::collections::BTreeMap; use std::fmt::Write; use std::ops::Range; -use ucd_parse::Codepoints; +use rustc_hash::{FxHashMap, FxHashSet}; +use ucd_parse::{Codepoint, Codepoints}; mod cascading_map; mod case_mapping; @@ -106,6 +107,9 @@ struct UnicodeData { to_title: BTreeMap, /// Only stores mappings that are not to self to_lower: BTreeMap, + /// Only stores mappings that differ from + /// `to_upper` followed by `to_lower` + to_casefold: BTreeMap, } fn to_mapping( @@ -126,7 +130,7 @@ static UNICODE_DIRECTORY: &str = "unicode-downloads"; fn load_data() -> UnicodeData { unicode_download::fetch_latest(); - let mut properties = HashMap::new(); + let mut properties = FxHashMap::default(); for row in ucd_parse::parse::<_, ucd_parse::CoreProperty>(&UNICODE_DIRECTORY).unwrap() { if let Some(name) = PROPERTIES.iter().find(|prop| **prop == row.property.as_str()) { properties.entry(*name).or_insert_with(Vec::new).push(row.codepoints); @@ -138,7 +142,8 @@ fn load_data() -> UnicodeData { } } - let [mut to_lower, mut to_upper, mut to_title] = [const { BTreeMap::new() }; 3]; + let [mut to_lower, mut to_upper, mut to_title, mut to_casefold] = + [const { BTreeMap::new() }; 4]; for row in ucd_parse::UnicodeDataExpander::new( ucd_parse::parse::<_, ucd_parse::UnicodeData>(&UNICODE_DIRECTORY).unwrap(), ) { @@ -189,6 +194,78 @@ fn load_data() -> UnicodeData { } } + fn get_mapping_from_btreemap<'a>( + cp: Codepoint, + map: &'a BTreeMap, + ) -> Vec { + let mapping = + map.get(&cp.value()).copied().map(|cs| cs.map(|c| Codepoint::from_u32(c).unwrap())); + + mapping + .as_ref() + .map(|cs| { + let nul = Codepoint::from_u32(0).unwrap(); + if cs[1] == nul { + &cs[..1] + } else if cs[2] == nul { + &cs[..2] + } else { + &cs[..] + } + }) + .map_or_else(|| vec![cp], ToOwned::to_owned) + } + + let mut nontrivial_casefold = FxHashSet::default(); + + for row in ucd_parse::parse::<_, ucd_parse::CaseFold>(&UNICODE_DIRECTORY).unwrap() { + use ucd_parse::{CaseStatus, Codepoint}; + if matches!(row.status, CaseStatus::Common | CaseStatus::Full) { + let key = row.codepoint.value(); + nontrivial_casefold.insert(key); + + // We store case-fold data only for characters whose case-folding + // differs from the lowercase of their uppercase. + + let lower_upper_mapping: Vec = + get_mapping_from_btreemap(row.codepoint, &to_upper) + .into_iter() + .flat_map(|cp| get_mapping_from_btreemap(cp, &to_lower)) + .collect(); + + if let Some(casefold) = to_mapping(&lower_upper_mapping, &row.mapping) { + to_casefold.insert(key, casefold); + } + } + } + + // Now, account for characters that remain unchanged by case-folding + // (and are therefore omitted from `CaseFolding.txt`), + // but yet differ from the lowercase of their uppercase. + + for c in '\0'..=char::MAX { + let cnum: u32 = c.into(); + if !nontrivial_casefold.contains(&cnum) { + let cp = Codepoint::from_u32(cnum).unwrap(); + + use std::collections::btree_map::Entry; + match to_casefold.entry(cnum) { + Entry::Vacant(vacant_entry) => { + let lower_upper_mapping: Vec = + get_mapping_from_btreemap(cp, &to_upper) + .into_iter() + .flat_map(|cp| get_mapping_from_btreemap(cp, &to_lower)) + .collect(); + + if let Some(casefold) = to_mapping(&lower_upper_mapping, &[cp]) { + vacant_entry.insert(casefold); + } + } + Entry::Occupied(_) => {} + } + } + } + // Filter out ASCII codepoints. to_lower.retain(|&c, _| c > 0x7f); to_upper.retain(|&c, _| c > 0x7f); @@ -207,7 +284,7 @@ fn load_data() -> UnicodeData { .collect(); properties.sort_by_key(|p| p.0); - UnicodeData { ranges: properties, to_lower, to_title, to_upper } + UnicodeData { ranges: properties, to_lower, to_title, to_upper, to_casefold } } fn main() { @@ -259,7 +336,9 @@ fn main() { total_bytes += emitter.bytes_used; } let (conversions, sizes) = case_mapping::generate_case_mapping(&unicode_data); - for (name, (desc, size)) in ["to_lower", "to_upper", "to_title"].iter().zip(sizes) { + for (name, (desc, size)) in + ["to_lower", "to_upper", "to_title", "to_casefold"].iter().zip(sizes) + { table_file.push_str(&format!("// {:16}: {:5} bytes, {desc}\n", name, size,)); total_bytes += size; } @@ -369,10 +448,11 @@ pub(super) static {prop_upper}: &[RangeInclusive; {is_true_len}] = &[{is_t .unwrap(); } - for (name, lut) in ["TO_LOWER", "TO_UPPER", "TO_TITLE"].iter().zip([ + for (name, lut) in ["TO_LOWER", "TO_UPPER", "TO_TITLE", "TO_CASEFOLD"].iter().zip([ &data.to_lower, &data.to_upper, &data.to_title, + &data.to_casefold, ]) { let lut = lut .iter() diff --git a/src/tools/unicode-table-generator/src/raw_emitter.rs b/src/tools/unicode-table-generator/src/raw_emitter.rs index 297965615c1a5..de3395df3806e 100644 --- a/src/tools/unicode-table-generator/src/raw_emitter.rs +++ b/src/tools/unicode-table-generator/src/raw_emitter.rs @@ -1,7 +1,9 @@ -use std::collections::{BTreeMap, BTreeSet, HashMap}; +use std::collections::{BTreeMap, BTreeSet}; use std::fmt::{self, Write}; use std::ops::Range; +use rustc_hash::FxHashMap; + use crate::fmt_list; #[derive(Clone)] @@ -126,8 +128,11 @@ impl RawEmitter { for chunk in compressed_words.chunks(chunk_length) { chunks.insert(chunk); } - let chunk_map = - chunks.iter().enumerate().map(|(idx, &chunk)| (chunk, idx)).collect::>(); + let chunk_map = chunks + .iter() + .enumerate() + .map(|(idx, &chunk)| (chunk, idx)) + .collect::>(); let mut chunk_indices = Vec::new(); for chunk in compressed_words.chunks(chunk_length) { chunk_indices.push(chunk_map[chunk]); @@ -186,7 +191,7 @@ struct Canonicalized { /// Maps an input unique word to the associated index (u8) which is into /// canonical_words or canonicalized_words (in order). - unique_mapping: HashMap, + unique_mapping: FxHashMap, } impl Canonicalized { @@ -253,7 +258,7 @@ impl Canonicalized { // These are mapped words, which will be represented by an index into // the canonical_words and a Mapping; u16 when encoded. let mut canonicalized_words = Vec::new(); - let mut unique_mapping = HashMap::new(); + let mut unique_mapping = FxHashMap::default(); #[derive(Debug, PartialEq, Eq)] enum UniqueMapping { @@ -361,7 +366,7 @@ impl Canonicalized { }, ) }) - .collect::>(); + .collect::>(); let mut distinct_indices = BTreeSet::new(); for &w in unique_words { diff --git a/src/tools/unicode-table-generator/src/unicode_download.rs b/src/tools/unicode-table-generator/src/unicode_download.rs index c9826170905c2..b2fcf6444033d 100644 --- a/src/tools/unicode-table-generator/src/unicode_download.rs +++ b/src/tools/unicode-table-generator/src/unicode_download.rs @@ -7,8 +7,13 @@ static URL_PREFIX: &str = "https://www.unicode.org/Public/UCD/latest/ucd/"; static README: &str = "ReadMe.txt"; -static RESOURCES: &[&str] = - &["DerivedCoreProperties.txt", "PropList.txt", "UnicodeData.txt", "SpecialCasing.txt"]; +static RESOURCES: &[&str] = &[ + "CaseFolding.txt", + "DerivedCoreProperties.txt", + "PropList.txt", + "SpecialCasing.txt", + "UnicodeData.txt", +]; #[track_caller] fn fetch(url: &str) -> Output { diff --git a/tests/codegen-llvm/bpf-allows-unaligned.rs b/tests/codegen-llvm/bpf-allows-unaligned.rs index 7e95a56d984c9..c7a70d5b2e502 100644 --- a/tests/codegen-llvm/bpf-allows-unaligned.rs +++ b/tests/codegen-llvm/bpf-allows-unaligned.rs @@ -5,7 +5,7 @@ #[no_mangle] #[target_feature(enable = "allows-misaligned-mem-access")] -// CHECK: define noundef zeroext i8 @foo(i8 noundef returned %arg) unnamed_addr #0 +// CHECK: define noundef zeroext i8 @foo(i8 noundef returned %arg) unnamed_addr #0 { pub unsafe fn foo(arg: u8) -> u8 { arg } diff --git a/tests/codegen-llvm/branch-protection.rs b/tests/codegen-llvm/branch-protection.rs index 11847c256d6ba..ed1cb2cd137ea 100644 --- a/tests/codegen-llvm/branch-protection.rs +++ b/tests/codegen-llvm/branch-protection.rs @@ -22,7 +22,7 @@ extern crate minicore; use minicore::*; // A basic test function. -// CHECK: @test(){{.*}} [[ATTR:#[0-9]+]] +// CHECK: @test(){{.*}} [[ATTR:#[0-9]+]] { #[no_mangle] pub fn test() {} diff --git a/tests/codegen-llvm/frame-pointer-cli-control.rs b/tests/codegen-llvm/frame-pointer-cli-control.rs index 79cdfc70f1ad7..911a5f03cbcda 100644 --- a/tests/codegen-llvm/frame-pointer-cli-control.rs +++ b/tests/codegen-llvm/frame-pointer-cli-control.rs @@ -45,7 +45,7 @@ Specific cases where platforms or tools rely on frame pointers for sound or corr extern crate minicore; -// CHECK: i32 @peach{{.*}}[[PEACH_ATTRS:\#[0-9]+]] +// CHECK: i32 @peach{{.*}}[[PEACH_ATTRS:\#[0-9]+]] { #[no_mangle] pub fn peach(x: u32) -> u32 { x diff --git a/tests/codegen-llvm/frame-pointer.rs b/tests/codegen-llvm/frame-pointer.rs index a52d95a23862d..1d0dd762826b2 100644 --- a/tests/codegen-llvm/frame-pointer.rs +++ b/tests/codegen-llvm/frame-pointer.rs @@ -18,7 +18,7 @@ extern crate minicore; use minicore::*; -// CHECK: define i32 @peach{{.*}}[[PEACH_ATTRS:\#[0-9]+]] +// CHECK: define i32 @peach{{.*}}[[PEACH_ATTRS:\#[0-9]+]] { #[no_mangle] pub fn peach(x: u32) -> u32 { x diff --git a/tests/codegen-llvm/gpu-convergent.rs b/tests/codegen-llvm/gpu-convergent.rs index 376d65a3d4a25..bb9271ab69996 100644 --- a/tests/codegen-llvm/gpu-convergent.rs +++ b/tests/codegen-llvm/gpu-convergent.rs @@ -17,7 +17,7 @@ extern "C" { fn ext(); } -// CHECK: define {{.*}}_kernel void @fun(i32{{.*}}) unnamed_addr #[[ATTR:[0-9]+]] +// CHECK: define {{.*}}_kernel void @fun(i32{{.*}}) unnamed_addr #[[ATTR:[0-9]+]] { // CHECK: declare void @ext() unnamed_addr #[[ATTR]] // CHECK: attributes #[[ATTR]] = {{.*}} convergent #[no_mangle] diff --git a/tests/codegen-llvm/instrument-coverage/testprog.rs b/tests/codegen-llvm/instrument-coverage/testprog.rs index 67c49c438f9f7..ef61ede6de8ee 100644 --- a/tests/codegen-llvm/instrument-coverage/testprog.rs +++ b/tests/codegen-llvm/instrument-coverage/testprog.rs @@ -101,7 +101,7 @@ fn main() { // CHECK-SAME: @__llvm_prf_nm // CHECK-SAME: section "llvm.metadata" -// CHECK: define internal { {{.*}} } @_R{{[a-zA-Z0-9_]+}}testprog14will_be_called() unnamed_addr #{{[0-9]+}} +// CHECK: define internal { {{.*}} } @_R{{[a-zA-Z0-9_]+}}testprog14will_be_called() unnamed_addr #{{[0-9]+}} { // CHECK-NEXT: start: // CHECK-NOT: define internal // CHECK: atomicrmw add ptr @@ -109,7 +109,7 @@ fn main() { // CHECK: declare void @llvm.instrprof.increment(ptr, i64, i32, i32) #[[LLVM_INSTRPROF_INCREMENT_ATTR:[0-9]+]] -// WIN: define linkonce_odr hidden i32 @__llvm_profile_runtime_user() #[[LLVM_PROFILE_RUNTIME_USER_ATTR:[0-9]+]] comdat {{.*}} +// WIN: define linkonce_odr hidden i32 @__llvm_profile_runtime_user() #[[LLVM_PROFILE_RUNTIME_USER_ATTR:[0-9]+]] comdat {{.*}}{ // WIN-NEXT: %1 = load i32, ptr @__llvm_profile_runtime // WIN-NEXT: ret i32 %1 // WIN-NEXT: } diff --git a/tests/codegen-llvm/link_section.rs b/tests/codegen-llvm/link_section.rs index 61bde683c0a41..f196ea86c447d 100644 --- a/tests/codegen-llvm/link_section.rs +++ b/tests/codegen-llvm/link_section.rs @@ -29,7 +29,7 @@ pub static VAR2: E = E::A(666); #[link_section = "__TEST,three"] pub static VAR3: E = E::B(1.); -// CHECK: define {{(dso_local )?}}void @fn1() {{.*}} section "__TEST,four" +// CHECK: define {{(dso_local )?}}void @fn1() {{.*}} section "__TEST,four" { #[no_mangle] #[link_section = "__TEST,four"] pub fn fn1() {} diff --git a/tests/codegen-llvm/preserve-none.rs b/tests/codegen-llvm/preserve-none.rs index b45e49a466bf3..b8c8db25f272c 100644 --- a/tests/codegen-llvm/preserve-none.rs +++ b/tests/codegen-llvm/preserve-none.rs @@ -19,7 +19,7 @@ extern crate minicore; // UNSUPPORTED: define{{( dso_local)?}} void @peach(i16 #[no_mangle] #[inline(never)] -pub extern "rust-preserve-none" fn peach(x: u16) { +pub extern "rust-preserve-none" fn peach(_: u16) { loop {} } diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs index 5b1aa97ab3338..7639ce7b10448 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs @@ -7,21 +7,21 @@ pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { // CHECK-LABEL: define{{.*}}foo - // CHECK-SAME: {{.*}}!type ![[TYPE1:[0-9]+]] + // CHECK-SAME: {{.*}}![[TYPE1:[0-9]+]] // CHECK: call i1 @llvm.type.test(ptr {{%f|%0}}, metadata !"_ZTSFu3i32S_E.normalized.generalized") f(arg) } pub fn bar(f: fn(i32, i32) -> i32, arg1: i32, arg2: i32) -> i32 { // CHECK-LABEL: define{{.*}}bar - // CHECK-SAME: {{.*}}!type ![[TYPE2:[0-9]+]] + // CHECK-SAME: {{.*}}![[TYPE2:[0-9]+]] // CHECK: call i1 @llvm.type.test(ptr {{%f|%0}}, metadata !"_ZTSFu3i32S_S_E.normalized.generalized") f(arg1, arg2) } pub fn baz(f: fn(i32, i32, i32) -> i32, arg1: i32, arg2: i32, arg3: i32) -> i32 { // CHECK-LABEL: define{{.*}}baz - // CHECK-SAME: {{.*}}!type ![[TYPE3:[0-9]+]] + // CHECK-SAME: {{.*}}![[TYPE3:[0-9]+]] // CHECK: call i1 @llvm.type.test(ptr {{%f|%0}}, metadata !"_ZTSFu3i32S_S_S_E.normalized.generalized") f(arg1, arg2, arg3) } diff --git a/tests/codegen-llvm/some-non-zero-from-atomic-optimization.rs b/tests/codegen-llvm/some-non-zero-from-atomic-optimization.rs index 3df2d569f9a40..35317b0dd39cc 100644 --- a/tests/codegen-llvm/some-non-zero-from-atomic-optimization.rs +++ b/tests/codegen-llvm/some-non-zero-from-atomic-optimization.rs @@ -72,7 +72,7 @@ pub unsafe fn some_non_zero_from_atomic_get() -> Option { /// /// The way we check that the LLVM IR is correct is by making sure that neither /// `panic` nor `unreachable` is part of the LLVM IR: -// CHECK-LABEL: define {{.*}} i64 @some_non_zero_from_atomic_get2() {{.*}} +// CHECK-LABEL: define {{.*}} i64 @some_non_zero_from_atomic_get2() {{.*}} { // CHECK-NOT: panic // CHECK-NOT: unreachable #[no_mangle] diff --git a/tests/codegen-llvm/tailcc.rs b/tests/codegen-llvm/tailcc.rs new file mode 100644 index 0000000000000..15ae69cf0498c --- /dev/null +++ b/tests/codegen-llvm/tailcc.rs @@ -0,0 +1,28 @@ +//@ add-minicore +//@ revisions: I586 X86_64 AARCH64 +//@ [I586] compile-flags: -C no-prepopulate-passes --target=i586-unknown-linux-gnu +//@ [I586] needs-llvm-components: x86 +//@ [X86_64] compile-flags: -C no-prepopulate-passes --target=x86_64-unknown-linux-gnu +//@ [X86_64] needs-llvm-components: x86 +//@ [AARCH64] compile-flags: -C no-prepopulate-passes --target=aarch64-unknown-linux-gnu +//@ [AARCH64] needs-llvm-components: aarch64 + +#![crate_type = "lib"] +#![feature(no_core, rust_tail_cc, explicit_tail_calls)] +#![no_core] + +extern crate minicore; + +// CHECK: define{{( dso_local)?}} tailcc void @peach(i16 +#[no_mangle] +#[inline(never)] +pub extern "tail" fn peach(_: u16) { + loop {} +} + +// CHECK: call tailcc void @peach(i16 +pub fn quince(x: u16) { + if let 12345u16 = x { + peach(54321); + } +} diff --git a/tests/codegen-llvm/target-feature-negative-implication.rs b/tests/codegen-llvm/target-feature-negative-implication.rs index 376599738e526..a9cdca4283991 100644 --- a/tests/codegen-llvm/target-feature-negative-implication.rs +++ b/tests/codegen-llvm/target-feature-negative-implication.rs @@ -13,7 +13,7 @@ use minicore::*; #[no_mangle] pub unsafe fn banana() { // CHECK-LABEL: @banana() - // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] + // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] { } // CHECK: attributes [[BANANAATTRS]] diff --git a/tests/codegen-llvm/target-feature-overrides.rs b/tests/codegen-llvm/target-feature-overrides.rs index 3bf05c7977ebf..2adc8ee6f53bc 100644 --- a/tests/codegen-llvm/target-feature-overrides.rs +++ b/tests/codegen-llvm/target-feature-overrides.rs @@ -23,7 +23,7 @@ extern "C" { #[no_mangle] pub unsafe fn apple() -> u32 { // CHECK-LABEL: @apple() - // CHECK-SAME: [[APPLEATTRS:#[0-9]+]] + // CHECK-SAME: [[APPLEATTRS:#[0-9]+]] { // CHECK: {{.*}}call{{.*}}@peach peach() } @@ -32,7 +32,7 @@ pub unsafe fn apple() -> u32 { #[no_mangle] pub unsafe fn banana() -> u32 { // CHECK-LABEL: @banana() - // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] + // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] { // COMPAT: {{.*}}call{{.*}}@peach // INCOMPAT: {{.*}}call{{.*}}@apple apple() // Compatible for inline in COMPAT revision and can't be inlined in INCOMPAT diff --git a/tests/codegen-llvm/unwind-abis/aapcs-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/aapcs-unwind-abi.rs index 279780e3a7aeb..ecace722e0dbe 100644 --- a/tests/codegen-llvm/unwind-abis/aapcs-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/aapcs-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `aapcs-unwind` extern functions. `aapcs-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "aapcs" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "aapcs-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/c-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/c-unwind-abi.rs index 1b3312839e3e8..46c08b5fc4ff4 100644 --- a/tests/codegen-llvm/unwind-abis/c-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/c-unwind-abi.rs @@ -7,11 +7,11 @@ #![crate_type = "lib"] -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "C" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "C-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/cdecl-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/cdecl-unwind-abi.rs index 6f4eafb353ccb..8e643d6ce4947 100644 --- a/tests/codegen-llvm/unwind-abis/cdecl-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/cdecl-unwind-abi.rs @@ -7,11 +7,11 @@ #![crate_type = "lib"] -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "cdecl" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "cdecl-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/fastcall-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/fastcall-unwind-abi.rs index 51c6fd15b9c5c..7df46813ed1dd 100644 --- a/tests/codegen-llvm/unwind-abis/fastcall-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/fastcall-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `fastcall-unwind` extern functions. `fastcall-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "fastcall" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "fastcall-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/stdcall-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/stdcall-unwind-abi.rs index b5fcea52b4d61..cc06ee125495a 100644 --- a/tests/codegen-llvm/unwind-abis/stdcall-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/stdcall-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // extern functions. `stdcall-unwind` functions MUST NOT have this attribute. We disable // optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "stdcall" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "stdcall-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/system-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/system-unwind-abi.rs index 15fce95fe285b..5f9102483464b 100644 --- a/tests/codegen-llvm/unwind-abis/system-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/system-unwind-abi.rs @@ -7,11 +7,11 @@ #![crate_type = "lib"] -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "system" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "system-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/sysv64-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/sysv64-unwind-abi.rs index 1293e7c0a5f82..69bfaf80b4be6 100644 --- a/tests/codegen-llvm/unwind-abis/sysv64-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/sysv64-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `sysv64-unwind` extern functions. `sysv64-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "sysv64" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "sysv64-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/thiscall-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/thiscall-unwind-abi.rs index a9b6c34ee58e2..05f6b8b70e171 100644 --- a/tests/codegen-llvm/unwind-abis/thiscall-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/thiscall-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `thiscall-unwind` extern functions. `thiscall-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "thiscall" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "thiscall-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/vectorcall-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/vectorcall-unwind-abi.rs index 8cedb55ae1d28..d001a16b32a1c 100644 --- a/tests/codegen-llvm/unwind-abis/vectorcall-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/vectorcall-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `vectorcall-unwind` extern functions. `vectorcall-unwind` functions MUST NOT have this attribute. // We disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "vectorcall" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "vectorcall-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/win64-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/win64-unwind-abi.rs index 2a3ad330406e6..257f00b54e4d8 100644 --- a/tests/codegen-llvm/unwind-abis/win64-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/win64-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `win64-unwind` extern functions. `win64-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "win64" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "win64-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/run-make/dynamic-loading-cdylib/foo.rs b/tests/run-make/dynamic-loading-cdylib/foo.rs index 15ce4825d1b33..5d29ca209edd8 100644 --- a/tests/run-make/dynamic-loading-cdylib/foo.rs +++ b/tests/run-make/dynamic-loading-cdylib/foo.rs @@ -6,18 +6,36 @@ pub extern "C" fn extern_fn_1(a: u32, b: u32) -> u32 { a + b } -struct NotifyOnDrop; +#[derive(Default)] +struct NotifyOnDrop { + last_result: std::cell::Cell, +} + +impl NotifyOnDrop { + fn save_and_print(&self, result: u32) { + self.last_result.set(result); + } +} impl Drop for NotifyOnDrop { fn drop(&mut self) { - println!("drop"); + println!("dropping, last result: {}", self.last_result.get()); } } +thread_local! { + static FOO: NotifyOnDrop = NotifyOnDrop::default(); +} + #[no_mangle] pub extern "C" fn extern_fn_2(a: u32, b: u32) -> u32 { - thread_local!(static FOO: NotifyOnDrop = NotifyOnDrop); - FOO.with(|_foo| {}); - println!("extern_fn_2"); - a * b + let result = a * b; + + FOO.with(|foo| { + foo.save_and_print(result); + }); + + println!("extern_fn_2({a}, {b})"); + + result } diff --git a/tests/run-make/dynamic-loading-cdylib/load_and_unload.rs b/tests/run-make/dynamic-loading-cdylib/load_and_unload.rs index 24409c16e8fea..ec3c558156c7b 100644 --- a/tests/run-make/dynamic-loading-cdylib/load_and_unload.rs +++ b/tests/run-make/dynamic-loading-cdylib/load_and_unload.rs @@ -101,6 +101,15 @@ fn main() { let result = unsafe { extern_fn_2(2, 3) }; println!("result of extern_fn_2(2, 3): {}", result); + println!("spawning thread"); + std::thread::spawn(move || { + let result = unsafe { extern_fn_2(4, 5) }; + println!("result of extern_fn_2(4, 5) in other thread: {}", result); + }) + .join() + .expect("Thread panicked"); + println!("thread joined"); + libloading::unload(foo); println!("unloaded library"); } diff --git a/tests/run-make/dynamic-loading-cdylib/output_unix.txt b/tests/run-make/dynamic-loading-cdylib/output_unix.txt index f79d5a2a63fa6..6e1736ca38d86 100644 --- a/tests/run-make/dynamic-loading-cdylib/output_unix.txt +++ b/tests/run-make/dynamic-loading-cdylib/output_unix.txt @@ -1,7 +1,12 @@ loaded library extern_fn_1 result of extern_fn_1(2, 3): 5 -extern_fn_2 +extern_fn_2(2, 3) result of extern_fn_2(2, 3): 6 +spawning thread +extern_fn_2(4, 5) +result of extern_fn_2(4, 5) in other thread: 20 +dropping, last result: 20 +thread joined unloaded library -drop +dropping, last result: 6 diff --git a/tests/run-make/dynamic-loading-cdylib/output_windows.txt b/tests/run-make/dynamic-loading-cdylib/output_windows.txt index 5ce77ad164359..e87baf4b52110 100644 --- a/tests/run-make/dynamic-loading-cdylib/output_windows.txt +++ b/tests/run-make/dynamic-loading-cdylib/output_windows.txt @@ -1,7 +1,12 @@ loaded library extern_fn_1 result of extern_fn_1(2, 3): 5 -extern_fn_2 +extern_fn_2(2, 3) result of extern_fn_2(2, 3): 6 -drop +spawning thread +extern_fn_2(4, 5) +result of extern_fn_2(4, 5) in other thread: 20 +dropping, last result: 20 +thread joined +dropping, last result: 6 unloaded library diff --git a/tests/rustdoc-ui/intra-doc/deprecated-note-link-after-unclosed-code-fence.rs b/tests/rustdoc-ui/intra-doc/deprecated-note-link-after-unclosed-code-fence.rs new file mode 100644 index 0000000000000..ac85c1474c1aa --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/deprecated-note-link-after-unclosed-code-fence.rs @@ -0,0 +1,12 @@ +//@ check-pass + +// Regression test for https://github.com/rust-lang/rust/issues/157326. +#![allow(rustdoc::invalid_rust_codeblocks)] +#![deprecated(note = "use [Env::try_invoke] instead")] +//! ``` + +pub struct Env; + +impl Env { + pub fn try_invoke(&self) {} +} diff --git a/tests/ui/abi/rust-preserve-none-cc.rs b/tests/ui/abi/rust-preserve-none-cc.rs index deacb926971c8..20895e1583f4e 100644 --- a/tests/ui/abi/rust-preserve-none-cc.rs +++ b/tests/ui/abi/rust-preserve-none-cc.rs @@ -1,5 +1,6 @@ //@ run-pass //@ needs-unwind +//@ ignore-backends: gcc #![feature(rust_preserve_none_cc)] @@ -17,9 +18,7 @@ extern "rust-preserve-none" fn oven_explosion() { #[inline(never)] fn bite_into(yummy: u64) -> u64 { - let did_it_actually = std::panic::catch_unwind(move || { - oven_explosion() - }); + let did_it_actually = std::panic::catch_unwind(move || oven_explosion()); assert!(did_it_actually.is_err()); yummy - 25 } @@ -52,16 +51,26 @@ extern "rust-preserve-none" fn lotsa_apples( and_a.rome.iter().sum(), fuji + ambrosia, cosmic_crisp - honeycrisp, - bite_into(and_a.golden_delicious) + bite_into(and_a.golden_delicious), ) } fn main() { - let pie = lotsa_apples(220, 140, 210.54201234, &[180, 210], (), CrateOf { - mcintosh: 150.0, - golden_delicious: 185, - jonagold: None, - rome: [180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202] - }, 270, 193.1, &[]); + let pie = lotsa_apples( + 220, + 140, + 210.54201234, + &[180, 210], + (), + CrateOf { + mcintosh: 150.0, + golden_delicious: 185, + jonagold: None, + rome: [180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202], + }, + 270, + 193.1, + &[], + ); assert_eq!(pie, (2292, 403.64201234, 50, 160)); } diff --git a/tests/ui/abi/rust-tail-cc.rs b/tests/ui/abi/rust-tail-cc.rs new file mode 100644 index 0000000000000..aba5d0d695647 --- /dev/null +++ b/tests/ui/abi/rust-tail-cc.rs @@ -0,0 +1,80 @@ +//@ revisions: aarch64 x32 x64 +//@ run-pass +//@[aarch64] only-aarch64 +//@[x32] only-x86 +//@[x64] only-x86_64 +//@ needs-unwind +//@ ignore-backends: gcc + +#![feature(rust_tail_cc)] + +struct CrateOf<'a> { + mcintosh: f64, + golden_delicious: u64, + jonagold: Option<&'a u64>, + rome: [u64; 12], +} + +#[inline(never)] +extern "tail" fn oven_explosion() { + panic!("bad time"); +} + +#[inline(never)] +fn bite_into(yummy: u64) -> u64 { + let did_it_actually = std::panic::catch_unwind(move || oven_explosion()); + assert!(did_it_actually.is_err()); + yummy - 25 +} + +#[inline(never)] +extern "tail" fn lotsa_apples( + honeycrisp: u64, + gala: u32, + fuji: f64, + granny_smith: &[u64], + pink_lady: (), + and_a: CrateOf<'static>, + cosmic_crisp: u64, + ambrosia: f64, + winesap: &[u64], +) -> (u64, f64, u64, u64) { + assert_eq!(honeycrisp, 220); + assert_eq!(gala, 140); + assert_eq!(fuji, 210.54201234); + assert_eq!(granny_smith, &[180, 210]); + assert_eq!(pink_lady, ()); + assert_eq!(and_a.mcintosh, 150.0); + assert_eq!(and_a.golden_delicious, 185); + assert_eq!(and_a.jonagold, None); // my scales can't weight these gargantuans. + assert_eq!(and_a.rome, [180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202]); + assert_eq!(cosmic_crisp, 270); + assert_eq!(ambrosia, 193.1); + assert_eq!(winesap, &[]); + ( + and_a.rome.iter().sum(), + fuji + ambrosia, + cosmic_crisp - honeycrisp, + bite_into(and_a.golden_delicious), + ) +} + +fn main() { + let pie = lotsa_apples( + 220, + 140, + 210.54201234, + &[180, 210], + (), + CrateOf { + mcintosh: 150.0, + golden_delicious: 185, + jonagold: None, + rome: [180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202], + }, + 270, + 193.1, + &[], + ); + assert_eq!(pie, (2292, 403.64201234, 50, 160)); +} diff --git a/tests/ui/issues/issue-20055-box-trait.rs b/tests/ui/box/box-array-match-temporary-drop.rs similarity index 90% rename from tests/ui/issues/issue-20055-box-trait.rs rename to tests/ui/box/box-array-match-temporary-drop.rs index 43f1f43ba2785..d87c4307760f8 100644 --- a/tests/ui/issues/issue-20055-box-trait.rs +++ b/tests/ui/box/box-array-match-temporary-drop.rs @@ -1,5 +1,5 @@ //@ run-pass -// See Issues #20055 and #21695. +// See Issues https://github.com/rust-lang/rust/issues/20055 and https://github.com/rust-lang/rust/issues/21695. // We are checking here that the temporaries `Box<[i8, k]>`, for `k` // in 1, 2, 3, 4, that are induced by the match expression are diff --git a/tests/ui/issues/issue-20055-box-trait.stderr b/tests/ui/box/box-array-match-temporary-drop.stderr similarity index 83% rename from tests/ui/issues/issue-20055-box-trait.stderr rename to tests/ui/box/box-array-match-temporary-drop.stderr index b1cbb2a571733..09caa6741afa7 100644 --- a/tests/ui/issues/issue-20055-box-trait.stderr +++ b/tests/ui/box/box-array-match-temporary-drop.stderr @@ -1,5 +1,5 @@ warning: method `dummy` is never used - --> $DIR/issue-20055-box-trait.rs:11:8 + --> $DIR/box-array-match-temporary-drop.rs:11:8 | LL | trait Boo { | --- method in this trait diff --git a/tests/ui/box/box-deref-in-match-arm-no-invalid-ir.rs b/tests/ui/box/box-deref-in-match-arm-no-invalid-ir.rs new file mode 100644 index 0000000000000..8c005a392dca9 --- /dev/null +++ b/tests/ui/box/box-deref-in-match-arm-no-invalid-ir.rs @@ -0,0 +1,18 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/18845 + +//@ run-pass +// This used to generate invalid IR in that even if we took the +// `false` branch we'd still try to free the Box from the other +// arm. This was due to treating `*Box::new(9)` as an rvalue datum +// instead of as a place. + +fn test(foo: bool) -> u8 { + match foo { + true => *Box::new(9), + false => 0 + } +} + +fn main() { + assert_eq!(9, test(true)); +} diff --git a/tests/ui/issues/issue-23491.rs b/tests/ui/box/box-dst-node-with-empty-slice.rs similarity index 68% rename from tests/ui/issues/issue-23491.rs rename to tests/ui/box/box-dst-node-with-empty-slice.rs index 0a2dfa4257a03..15ee8d4434b5d 100644 --- a/tests/ui/issues/issue-23491.rs +++ b/tests/ui/box/box-dst-node-with-empty-slice.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/23491 + //@ run-pass #![allow(unused_variables)] diff --git a/tests/ui/issues/issue-20055-box-unsized-array.rs b/tests/ui/box/box-fixed-array-coerce-unsized-match.rs similarity index 88% rename from tests/ui/issues/issue-20055-box-unsized-array.rs rename to tests/ui/box/box-fixed-array-coerce-unsized-match.rs index 2663bcfb4f727..4251495347183 100644 --- a/tests/ui/issues/issue-20055-box-unsized-array.rs +++ b/tests/ui/box/box-fixed-array-coerce-unsized-match.rs @@ -1,5 +1,5 @@ //@ run-pass -// Issue #2005: Check that boxed fixed-size arrays are properly +// Issue https://github.com/rust-lang/rust/issues/20055: Check that boxed fixed-size arrays are properly // accounted for (namely, only deallocated if they were actually // created) when they appear as temporaries in unused arms of a match // expression. diff --git a/tests/ui/feature-gates/feature-gate-rust-tail-cc.rs b/tests/ui/feature-gates/feature-gate-rust-tail-cc.rs new file mode 100644 index 0000000000000..b2bbf606cc7a8 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-rust-tail-cc.rs @@ -0,0 +1,21 @@ +#![crate_type = "lib"] + +extern "tail" fn apple() {} //~ ERROR "tail" ABI is experimental + +trait T { + extern "tail" fn banana(); //~ ERROR "tail" ABI is experimental + extern "tail" fn citrus() {} //~ ERROR "tail" ABI is experimental +} + +struct S; +impl T for S { + extern "tail" fn banana() {} //~ ERROR "tail" ABI is experimental +} + +impl S { + extern "tail" fn durian() {} //~ ERROR "tail" ABI is experimental +} + +type Fig = extern "tail" fn(); //~ ERROR "tail" ABI is experimental + +extern "tail" {} //~ ERROR "tail" ABI is experimental diff --git a/tests/ui/feature-gates/feature-gate-rust-tail-cc.stderr b/tests/ui/feature-gates/feature-gate-rust-tail-cc.stderr new file mode 100644 index 0000000000000..e588569e6bd79 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-rust-tail-cc.stderr @@ -0,0 +1,73 @@ +error[E0658]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:3:8 + | +LL | extern "tail" fn apple() {} + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` 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[E0658]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:6:12 + | +LL | extern "tail" fn banana(); + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` 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[E0658]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:7:12 + | +LL | extern "tail" fn citrus() {} + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` 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[E0658]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:12:12 + | +LL | extern "tail" fn banana() {} + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` 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[E0658]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:16:12 + | +LL | extern "tail" fn durian() {} + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` 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[E0658]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:19:19 + | +LL | type Fig = extern "tail" fn(); + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` 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[E0658]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:21:8 + | +LL | extern "tail" {} + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` 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 7 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs new file mode 100644 index 0000000000000..0a98cdef5c923 --- /dev/null +++ b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs @@ -0,0 +1,24 @@ +//! Regression test for #157189. +//! +//! When a method call fails to resolve, the "trait which provides `` is +//! implemented but not in scope" diagnostic probes all traits for a method of the +//! same name. Here `.borrow()` matches `std::borrow::Borrow::borrow`, and `Borrow` +//! has a generic parameter (`Borrowed`) besides `Self`. Building the trait +//! reference for the diagnostic used to pass only the receiver type as the single +//! argument, which mismatched the trait's generics and ICEd in +//! `debug_assert_args_compatible`. It should just report the error. + +trait Foo { + extern "C" fn borrow(&self); +} + +struct Bar; + +fn main() { + let foo: Box usize> = Box::new(Bar); + //~^ ERROR expected a `Fn(bool)` closure, found `Bar` + foo.borrow(); + //~^ ERROR no method named `borrow` found + foo.take() + //~^ ERROR `Box usize>` is not an iterator +} diff --git a/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr new file mode 100644 index 0000000000000..aa46a41fa3378 --- /dev/null +++ b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr @@ -0,0 +1,65 @@ +error[E0599]: no method named `borrow` found for struct `Box usize>` in the current scope + --> $DIR/method-suggestion-trait-with-extra-generics-no-ice.rs:20:9 + | +LL | foo.borrow(); + | ^^^^^^ + | + --> $SRC_DIR/core/src/borrow.rs:LL:COL + | + = note: the method is available for `Box usize>` here + | + = help: items from traits can only be used if the trait is in scope +help: use parentheses to call this trait object + | +LL | foo(/* bool */).borrow(); + | ++++++++++++ +help: trait `Borrow` which provides `borrow` is implemented but not in scope; perhaps you want to import it + | +LL + use std::borrow::Borrow; + | +help: there is a method `borrow_mut` with a similar name + | +LL | foo.borrow_mut(); + | ++++ + +error[E0277]: expected a `Fn(bool)` closure, found `Bar` + --> $DIR/method-suggestion-trait-with-extra-generics-no-ice.rs:18:43 + | +LL | let foo: Box usize> = Box::new(Bar); + | ^^^^^^^^^^^^^ expected an `Fn(bool)` closure, found `Bar` + | +help: the trait `Fn(bool)` is not implemented for `Bar` + --> $DIR/method-suggestion-trait-with-extra-generics-no-ice.rs:15:1 + | +LL | struct Bar; + | ^^^^^^^^^^ + = note: required for the cast from `Box` to `Box usize>` + +error[E0599]: `Box usize>` is not an iterator + --> $DIR/method-suggestion-trait-with-extra-generics-no-ice.rs:22:9 + | +LL | foo.take() + | ^^^^ + | | + | this is an associated function, not a method + | `Box usize>` is not an iterator + | + = note: found the following associated functions; to be used as methods, functions must have a `self` parameter + = note: the candidate is defined in an impl for the type `Box` + = note: the following trait bounds were not satisfied: + `dyn Fn(bool) -> usize: Iterator` + which is required by `Box usize>: Iterator` + `Box usize>: Iterator` + which is required by `&mut Box usize>: Iterator` + `dyn Fn(bool) -> usize: Iterator` + which is required by `&mut dyn Fn(bool) -> usize: Iterator` +help: use associated function syntax instead + | +LL - foo.take() +LL + Box:: usize>::take(foo) + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0599. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/print-request/print-calling-conventions.stdout b/tests/ui/print-request/print-calling-conventions.stdout index 506bbea23e171..af4f491fc069e 100644 --- a/tests/ui/print-request/print-calling-conventions.stdout +++ b/tests/ui/print-request/print-calling-conventions.stdout @@ -29,6 +29,7 @@ system system-unwind sysv64 sysv64-unwind +tail thiscall thiscall-unwind unadjusted