diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 428f37b8af450..e860ef61a5332 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -249,14 +249,14 @@ impl LazyAttrTokenStreamInner { break_last_token, node_replacements, } => { - // The token produced by the final call to `{,inlined_}next` was not + // The token produced by the final call to `{,inlined_}next_and_bump` was not // actually consumed by the callback. The combination of chaining the // initial token and using `take` produces the desired result - we // produce an empty `TokenStream` if no calls were made, and omit the // final token otherwise. let mut cursor_snapshot = cursor_snapshot.clone(); let tokens = iter::once(FlatToken::Token(*start_token)) - .chain(iter::repeat_with(|| FlatToken::Token(cursor_snapshot.next()))) + .chain(iter::repeat_with(|| FlatToken::Token(cursor_snapshot.next_and_bump()))) .take(*num_calls as usize); if node_replacements.is_empty() { @@ -883,36 +883,48 @@ impl<'t> Iterator for TokenStreamIter<'t> { #[derive(Clone, Debug)] struct TokenTreeCursor { stream: TokenStream, - /// Points to the current token tree in the stream. In `TokenCursor::curr`, - /// this can be any token tree. In `TokenCursor::stack`, this is always a - /// `TokenTree::Delimited`. - index: usize, + /// Points to the next token tree (or one past the end of the stream). + next_idx: usize, } impl TokenTreeCursor { #[inline] fn new(stream: TokenStream) -> Self { - TokenTreeCursor { stream, index: 0 } + TokenTreeCursor { stream, next_idx: 0 } } + /// Gets the current token tree within this cursor. In a debug build it panics on a cursor that + /// hasn't been bumped; in a release build it will return `None`. #[inline] fn curr(&self) -> Option<&TokenTree> { - self.stream.get(self.index) + debug_assert!(self.next_idx > 0); + self.stream.get(self.next_idx - 1) } + /// Gets the next token tree without advancing. + #[inline] + fn next(&self) -> Option<&TokenTree> { + self.stream.get(self.next_idx) + } + + /// Gets the token tree `n` ahead. `look_ahead(1)` is equivalent to `next()`. `look_ahead(0)` + /// isn't allowed and will panic. + #[inline] fn look_ahead(&self, n: usize) -> Option<&TokenTree> { - self.stream.get(self.index + n) + assert_ne!(n, 0); + self.stream.get(self.next_idx + (n - 1)) } + /// Move the cursor to the next token tree. #[inline] fn bump(&mut self) { - self.index += 1; + self.next_idx += 1; } - // For skipping ahead in rare circumstances. + /// For skipping ahead in rare circumstances. #[inline] fn bump_to_end(&mut self) { - self.index = self.stream.len(); + self.next_idx = self.stream.len(); } } @@ -922,15 +934,16 @@ impl TokenTreeCursor { /// what the parser expects, for the most part. #[derive(Clone, Debug)] pub struct TokenCursor { - // Cursor for the current (innermost) token stream. The index within the + // Cursor for the current (innermost) token stream. The `next_idx` within the // cursor can point to any token tree in the stream (or one past the end). - // The delimiters for this token stream are found in `self.stack.last()`; - // if that is `None` we are in the outermost token stream which never has - // delimiters. + // The delimiters for this token stream are found in the current token tree + // in `self.stack.last()`; if that is `None` we are in the outermost token + // stream which never has delimiters. curr: TokenTreeCursor, - // Token streams surrounding the current one. The index within each cursor - // always points to a `TokenTree::Delimited`. + // Token streams surrounding the current one. The `next_idx` within each cursor + // is always greater than zero and always points one past the current + // `TokenTree::Delimited`. stack: Vec, } @@ -940,12 +953,13 @@ impl TokenCursor { TokenCursor { curr: TokenTreeCursor::new(stream), stack: vec![] } } - pub fn next(&mut self) -> (Token, Spacing) { - self.inlined_next() + /// Gets the next token and advances the cursor by one. + pub fn next_and_bump(&mut self) -> (Token, Spacing) { + self.inlined_next_and_bump() } - /// An `n` of zero is the next token tree in the current token stream; won't look outside the - /// current token stream. + /// An `n` of 1 is the next token tree in the current token stream; won't look outside the + /// current token stream. `look_ahead(0)` isn't allowed and will panic. #[inline] pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> { self.curr.look_ahead(n) @@ -955,7 +969,7 @@ impl TokenCursor { /// delimited sequence. Panics if we are not within a delimited sequence. #[inline] pub fn look_ahead_past_close_delim(&self) -> Option<&TokenTree> { - self.stack.last().unwrap().look_ahead(1) + self.stack.last().unwrap().next() } /// Clones the `TokenTree::Delimited` that we are currently within. Panics if we are not within @@ -991,12 +1005,12 @@ impl TokenCursor { /// This always-inlined version should only be used on hot code paths. #[inline(always)] - pub fn inlined_next(&mut self) -> (Token, Spacing) { + pub fn inlined_next_and_bump(&mut self) -> (Token, Spacing) { loop { // FIXME: we currently don't return `Delimiter::Invisible` open/close delims. To fix // #67062 we will need to, whereupon the `delim != Delimiter::Invisible` conditions // below can be removed. - if let Some(tree) = self.curr.curr() { + if let Some(tree) = self.curr.next() { match tree { &TokenTree::Token(token, spacing) => { debug_assert!(!token.kind.is_delim()); @@ -1006,6 +1020,7 @@ impl TokenCursor { } &TokenTree::Delimited(sp, spacing, delim, ref tts) => { let trees = TokenTreeCursor::new(tts.clone()); + self.curr.bump(); // move past the `Delimited` self.stack.push(mem::replace(&mut self.curr, trees)); if !delim.skip() { return (Token::new(delim.as_open_token_kind(), sp.open), spacing.open); @@ -1019,7 +1034,6 @@ impl TokenCursor { panic!("parent should be Delimited") }; self.curr = parent; - self.curr.bump(); // move past the `Delimited` if !delim.skip() { return (Token::new(delim.as_close_token_kind(), span.close), spacing.close); } diff --git a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs index c4d4ddcf6b753..1c552ca1a9c32 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs @@ -122,8 +122,8 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { } _ => unreachable!("{:?}", self.layout.backend_repr), }, - PassMode::Cast { ref cast, pad_i32 } => { - assert!(!pad_i32, "padding support not yet implemented"); + PassMode::Cast { ref cast, pad_i32_count } => { + assert_eq!(pad_i32_count, 0, "padding support not yet implemented"); cast_target_to_abi_params(cast).into_iter().map(|(_, param)| param).collect() } PassMode::Indirect { attrs, meta_attrs: None, on_stack } => { diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index 1b7bb8c907735..2901eb8b1a6d2 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -168,11 +168,13 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { )); continue; } - PassMode::Cast { ref cast, pad_i32 } => { - // add padding - if pad_i32 { - argument_tys.push(Reg::i32().gcc_type(cx)); - } + PassMode::Cast { ref cast, pad_i32_count } => { + // Add padding. + argument_tys.extend(std::iter::repeat_n( + Reg::i32().gcc_type(cx), + usize::from(pad_i32_count), + )); + let ty = cast.gcc_type(cx); apply_attrs(ty, &cast.attrs, argument_tys.len()) } diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index c6c32236ab49f..53192c0a087e4 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -346,8 +346,8 @@ impl<'gcc, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn_abi.ptr_to_gcc_type(self) } - fn reg_backend_type(&self, _ty: &Reg) -> Type<'gcc> { - unimplemented!(); + fn reg_backend_type(&self, ty: &Reg) -> Type<'gcc> { + ty.gcc_type(self) } fn fn_decl_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Type<'gcc> { diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 65bb32ee666f2..816ebe3fcf3d9 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -249,7 +249,7 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { bug!("unsized `ArgAbi` cannot be stored"); } - PassMode::Cast { cast, pad_i32: _ } => { + PassMode::Cast { cast, pad_i32_count: _ } => { // The ABI mandates that the value is passed as a different struct representation. // Spill and reload it from the stack to convert from the ABI representation to // the Rust representation. @@ -366,7 +366,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { let llreturn_ty = match &self.ret.mode { PassMode::Ignore => cx.type_void(), PassMode::Direct(_) | PassMode::Pair(..) => self.ret.layout.immediate_llvm_type(cx), - PassMode::Cast { cast, pad_i32: _ } => cast.llvm_type(cx), + PassMode::Cast { cast, pad_i32_count: _ } => cast.llvm_type(cx), PassMode::Indirect { .. } => { llargument_tys.push(cx.type_ptr()); cx.type_void() @@ -405,11 +405,13 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { continue; } PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => cx.type_ptr(), - PassMode::Cast { cast, pad_i32 } => { - // add padding - if *pad_i32 { - llargument_tys.push(Reg::i32().llvm_type(cx)); - } + PassMode::Cast { cast, pad_i32_count } => { + // Add padding. + llargument_tys.extend(std::iter::repeat_n( + Reg::i32().llvm_type(cx), + usize::from(*pad_i32_count), + )); + // Compute the LLVM type we use for this function from the cast type. // We assume here that ABI-compatible Rust types have the same cast type. cast.llvm_type(cx) @@ -511,7 +513,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { ); } } - PassMode::Cast { cast, pad_i32: _ } => { + PassMode::Cast { cast, pad_i32_count: _ } => { cast.attrs.apply_attrs_to_llfn(llvm::AttributePlace::ReturnValue, cx, llfn); } _ => {} @@ -580,8 +582,8 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { apply_range_attr(llvm::AttributePlace::Argument(ii), scalar_b); } } - PassMode::Cast { cast, pad_i32 } => { - if *pad_i32 { + PassMode::Cast { cast, pad_i32_count } => { + for _ in 0..*pad_i32_count { apply(&ArgAttributes::new()); } apply(&cast.attrs); @@ -630,7 +632,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { ); attributes::apply_to_callsite(callsite, llvm::AttributePlace::Argument(i), &[sret]); } - PassMode::Cast { cast, pad_i32: _ } => { + PassMode::Cast { cast, pad_i32_count: _ } => { cast.attrs.apply_attrs_to_callsite( llvm::AttributePlace::ReturnValue, bx.cx, @@ -666,8 +668,8 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { apply(bx.cx, a); apply(bx.cx, b); } - PassMode::Cast { cast, pad_i32 } => { - if *pad_i32 { + PassMode::Cast { cast, pad_i32_count } => { + for _ in 0..*pad_i32_count { apply(bx.cx, &ArgAttributes::new()); } apply(bx.cx, &cast.attrs); diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 7f907bc630b2f..afd9a88784c2f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -588,7 +588,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - PassMode::Cast { cast: cast_ty, pad_i32: _ } => { + PassMode::Cast { cast: cast_ty, pad_i32_count: _ } => { let op = match self.locals[mir::RETURN_PLACE] { LocalRef::Operand(op) => op, LocalRef::PendingOperand => bug!("use of return before def"), @@ -1936,9 +1936,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ) { match arg.mode { PassMode::Ignore => return, - PassMode::Cast { pad_i32: true, .. } => { + PassMode::Cast { pad_i32_count, .. } => { // Fill padding with undef value, where applicable. - llargs.push(bx.const_undef(bx.reg_backend_type(&Reg::i32()))); + let undef = bx.const_undef(bx.reg_backend_type(&Reg::i32())); + llargs.extend(std::iter::repeat_n(undef, usize::from(pad_i32_count))); } PassMode::Pair(..) => match op.val { Pair(a, b) => { @@ -2025,7 +2026,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if by_ref && !arg.is_indirect() { // Have to load the argument, maybe while casting it. - if let PassMode::Cast { cast, pad_i32: _ } = &arg.mode { + if let PassMode::Cast { cast, pad_i32_count: _ } = &arg.mode { // The ABI mandates that the value is passed as a different struct representation. // Spill and reload it from the stack to convert from the Rust representation to // the ABI representation. diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index f8f4f09f75825..6e87a295e9d2b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -501,8 +501,8 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( for i in 0..tupled_arg_tys.len() { let arg = &fx.fn_abi.args[idx]; idx += 1; - if let PassMode::Cast { pad_i32: true, .. } = arg.mode { - llarg_idx += 1; + if let PassMode::Cast { pad_i32_count, .. } = arg.mode { + llarg_idx += usize::from(pad_i32_count); } let pr_field = place.project_field(bx, i); bx.store_fn_arg(arg, &mut llarg_idx, pr_field); @@ -529,8 +529,8 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let arg = &fx.fn_abi.args[idx]; idx += 1; - if let PassMode::Cast { pad_i32: true, .. } = arg.mode { - llarg_idx += 1; + if let PassMode::Cast { pad_i32_count, .. } = arg.mode { + llarg_idx += usize::from(pad_i32_count); } if !memory_locals.contains(local) { diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 33cc321ea6d32..05b87bb6d7159 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -472,9 +472,9 @@ fn wasm_type<'tcx>(signature: &mut String, arg_abi: &ArgAbi<'_, Ty<'tcx>>, ptr_t } other => unreachable!("{other:?}"), }, - PassMode::Cast { pad_i32, ref cast } => { + PassMode::Cast { pad_i32_count, ref cast } => { // For wasm, Cast is used for single-field primitive wrappers like `struct Wrapper(i64);` - assert!(!pad_i32, "not currently used by wasm calling convention"); + assert_eq!(pad_i32_count, 0, "not currently used by wasm calling convention"); assert!(cast.prefix.is_empty(), "no prefix"); assert_eq!(cast.rest.total, arg_abi.layout.size, "single item"); diff --git a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs index 5f44e2e288821..e6c278bd8ce7b 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs @@ -24,7 +24,7 @@ enum UsesVectorRegisters { fn passes_vectors_by_value(mode: &PassMode, repr: &BackendRepr) -> UsesVectorRegisters { match mode { PassMode::Ignore | PassMode::Indirect { .. } => UsesVectorRegisters::No, - PassMode::Cast { pad_i32: _, cast } + PassMode::Cast { pad_i32_count: _, cast } if cast.prefix.iter().any(|x| matches!(x.kind, RegKind::Vector { .. })) || matches!(cast.rest.unit.kind, RegKind::Vector { .. }) => { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 7bbace5bdc6d2..80c1eeb4ef041 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -496,9 +496,7 @@ impl<'a> Parser<'a> { // Check the first token after the delimiter that closes the current // delimited sequence. (Panics if used in the outermost token stream, which - // has no delimiters.) It uses a clone of the relevant tree cursor to skip - // past the entire `TokenTree::Delimited` in a single step, avoiding the - // need for unbounded token lookahead. + // has no delimiters.) // // Primarily used when `self.token` matches `OpenInvisible(_))`, to look // ahead through the current metavar expansion. @@ -1125,7 +1123,7 @@ impl<'a> Parser<'a> { pub fn bump(&mut self) { // Note: destructuring here would give nicer code, but it was found in #96210 to be slower // than `.0`/`.1` access. - let mut next = self.token_cursor.inlined_next(); + let mut next = self.token_cursor.inlined_next_and_bump(); self.num_bump_calls += 1; // We got a token from the underlying cursor and no longer need to // worry about an unglued token. See `break_and_eat` for more details. @@ -1153,8 +1151,8 @@ impl<'a> Parser<'a> { // Typically around 98% of the `dist > 0` cases have `dist == 1`, so we // have a fast special case for that. if dist == 1 { - // `look_ahead(0)` returns the *next* token. - match self.token_cursor.look_ahead(0) { + // `look_ahead(1)` returns the next token. + match self.token_cursor.look_ahead(1) { Some(tree) => { // Indexing stayed within the current token tree. match tree { @@ -1180,13 +1178,13 @@ impl<'a> Parser<'a> { } } - // Just clone the token cursor and use `next`, skipping delimiters as + // Just clone the token cursor and use `next_and_bump`, skipping delimiters as // necessary. Slow but simple. let mut cursor = self.token_cursor.clone(); let mut i = 0; let mut token = Token::dummy(); while i < dist { - token = cursor.next().0; + token = cursor.next_and_bump().0; if let token::OpenInvisible(origin) | token::CloseInvisible(origin) = token.kind && origin.skip() { @@ -1198,14 +1196,13 @@ impl<'a> Parser<'a> { } /// Like `look_ahead`, but skips over token trees rather than tokens. Useful - /// when looking past possible metavariable pasting sites. + /// when looking past possible metavariable pasting sites. Panics if `dist` is zero. pub fn tree_look_ahead( &self, dist: usize, looker: impl FnOnce(&TokenTree) -> R, ) -> Option { - assert_ne!(dist, 0); - self.token_cursor.look_ahead(dist - 1).map(looker) + self.token_cursor.look_ahead(dist).map(looker) } /// Returns whether any of the given keywords are `dist` tokens ahead of the current one. @@ -1411,7 +1408,7 @@ impl<'a> Parser<'a> { debug_assert_eq!(self.token_cursor.depth(), target_depth); } else { loop { - // Advance one token at a time, so `TokenCursor::next()` + // Advance one token at a time, so `TokenCursor::next_and_bump()` // can capture these tokens if necessary. self.bump(); if self.token_cursor.depth() == target_depth { diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index 02674e4107c77..910f4a5745a7d 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -55,7 +55,7 @@ pub enum PassMode { /// The argument has a layout abi of `ScalarPair`. Pair(Opaque, Opaque), /// Pass the argument after casting it. - Cast { pad_i32: bool, cast: Opaque }, + Cast { pad_i32_count: u8, cast: Opaque }, /// Pass the argument indirectly via a hidden pointer. Indirect { attrs: Opaque, meta_attrs: Opaque, on_stack: bool }, } diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs index 31104ce897ffb..4bb00b4c04394 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -165,8 +165,8 @@ impl<'tcx> Stable<'tcx> for callconv::PassMode { callconv::PassMode::Pair(first, second) => { PassMode::Pair(opaque(first), opaque(second)) } - callconv::PassMode::Cast { pad_i32, cast } => { - PassMode::Cast { pad_i32: *pad_i32, cast: opaque(cast) } + callconv::PassMode::Cast { pad_i32_count, cast } => { + PassMode::Cast { pad_i32_count: *pad_i32_count, cast: opaque(cast) } } callconv::PassMode::Indirect { attrs, meta_attrs, on_stack } => PassMode::Indirect { attrs: opaque(attrs), diff --git a/compiler/rustc_target/src/callconv/mips.rs b/compiler/rustc_target/src/callconv/mips.rs index d2572cc035c1c..d3a39d05b4964 100644 --- a/compiler/rustc_target/src/callconv/mips.rs +++ b/compiler/rustc_target/src/callconv/mips.rs @@ -35,7 +35,7 @@ where let size = arg.layout.size; if arg.layout.is_aggregate() { - let pad_i32 = !offset.is_aligned(align); + let pad_i32 = u8::from(!offset.is_aligned(align)); arg.cast_to_and_pad_i32(Uniform::new(Reg::i32(), size), pad_i32); } else { arg.extend_integer_width_to(32); diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index a9d5ec958889f..8002f98507ba8 100644 --- a/compiler/rustc_target/src/callconv/mips64.rs +++ b/compiler/rustc_target/src/callconv/mips64.rs @@ -96,7 +96,7 @@ where // Detect need for padding let align = Ord::clamp(arg.layout.align.abi, dl.i64_align, dl.i128_align); - let pad_i32 = !offset.is_aligned(align); + let pad_i32 = u8::from(!offset.is_aligned(align)); if !arg.layout.is_aggregate() { extend_integer_width_mips(arg, 64); diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index a06a6a0a69e12..26fedbd8a5481 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -55,8 +55,9 @@ pub enum PassMode { Pair(ArgAttributes, ArgAttributes), /// Pass the argument after casting it. See the `CastTarget` docs for details. /// - /// `pad_i32` indicates if a `Reg::i32()` dummy argument is emitted before the real argument. - Cast { pad_i32: bool, cast: Box }, + /// `pad_i32` indicates how many `Reg::i32()` dummy arguments are emitted before the real + /// argument. + Cast { pad_i32_count: u8, cast: Box }, /// Pass the argument indirectly via a hidden pointer. /// /// The `meta_attrs` value, if any, is for the metadata (vtable or length) of an unsized @@ -84,8 +85,8 @@ impl PassMode { (PassMode::Direct(a1), PassMode::Direct(a2)) => a1.eq_abi(a2), (PassMode::Pair(a1, b1), PassMode::Pair(a2, b2)) => a1.eq_abi(a2) && b1.eq_abi(b2), ( - PassMode::Cast { cast: c1, pad_i32: pad1 }, - PassMode::Cast { cast: c2, pad_i32: pad2 }, + PassMode::Cast { cast: c1, pad_i32_count: pad1 }, + PassMode::Cast { cast: c2, pad_i32_count: pad2 }, ) => c1.eq_abi(c2) && pad1 == pad2, ( PassMode::Indirect { attrs: a1, meta_attrs: None, on_stack: s1 }, @@ -507,12 +508,12 @@ impl<'a, Ty> ArgAbi<'a, Ty> { } pub fn cast_to>(&mut self, target: T) { - self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32: false }; + self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32_count: 0 }; } pub fn cast_to_with_attrs>(&mut self, target: T, attrs: ArgAttributes) { self.mode = - PassMode::Cast { cast: Box::new(target.into().with_attrs(attrs)), pad_i32: false }; + PassMode::Cast { cast: Box::new(target.into().with_attrs(attrs)), pad_i32_count: 0 }; } /// Cast to `target`, forwarding `NoUndef` only when the layout provably has no uninit @@ -535,8 +536,8 @@ impl<'a, Ty> ArgAbi<'a, Ty> { self.cast_to_with_attrs(target, attr.into()); } - pub fn cast_to_and_pad_i32>(&mut self, target: T, pad_i32: bool) { - self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32 }; + pub fn cast_to_and_pad_i32>(&mut self, target: T, pad_i32_count: u8) { + self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32_count }; } pub fn is_indirect(&self) -> bool { diff --git a/compiler/rustc_target/src/callconv/sparc.rs b/compiler/rustc_target/src/callconv/sparc.rs index d424214aa497e..71af508915e59 100644 --- a/compiler/rustc_target/src/callconv/sparc.rs +++ b/compiler/rustc_target/src/callconv/sparc.rs @@ -34,7 +34,7 @@ where let align = arg.layout.align.abi.max(dl.i32_align).min(dl.i64_align); if arg.layout.is_aggregate() { - let pad_i32 = !offset.is_aligned(align); + let pad_i32 = u8::from(!offset.is_aligned(align)); arg.cast_to_and_pad_i32(Uniform::new(Reg::i32(), size), pad_i32); } else { arg.extend_integer_width_to(32); diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index 6b19f8ebd76ce..7e441d7100c39 100644 --- a/compiler/rustc_target/src/callconv/sparc64.rs +++ b/compiler/rustc_target/src/callconv/sparc64.rs @@ -190,7 +190,7 @@ fn classify_arg<'a, Ty, C>( _ => CastTarget::prefixed(regs, Uniform::new(Reg::i8(), Size::ZERO)), }; - arg.cast_to_and_pad_i32(cast_target.with_attrs(attrs.into()), pad); + arg.cast_to_and_pad_i32(cast_target.with_attrs(attrs.into()), u8::from(pad)); } pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index 93a70987560fa..540b4aa0efdda 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -795,21 +795,6 @@ const impl From for T { } } -/// **Stability note:** This impl does not yet exist, but we are -/// "reserving space" to add it in the future. See -/// [rust-lang/rust#64715][#64715] for details. -/// -/// [#64715]: https://github.com/rust-lang/rust/issues/64715 -#[stable(feature = "convert_infallible", since = "1.34.0")] -#[rustc_reservation_impl = "permitting this impl would forbid us from adding \ - `impl From for T` later; see rust-lang/rust#64715 for details"] -#[rustc_const_unstable(feature = "const_convert", issue = "143773")] -const impl From for T { - fn from(t: !) -> T { - t - } -} - // TryFrom implies TryInto #[stable(feature = "try_from", since = "1.34.0")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] diff --git a/src/bootstrap/src/cli_main.rs b/src/bootstrap/src/cli_main.rs index 8a74b2e598283..7fd4d3b692d99 100644 --- a/src/bootstrap/src/cli_main.rs +++ b/src/bootstrap/src/cli_main.rs @@ -16,7 +16,7 @@ use std::{env, process}; use crate::core::builder::StepStack; use crate::core::config::flags::{Flags, Subcommand}; use crate::core::config::{ChangeId, Config}; -use crate::core::session::Build; +use crate::core::session::Session; use crate::debug; use crate::utils::change_tracker::{ CONFIG_CHANGE_HISTORY, find_recent_config_change_ids, human_readable_changes, @@ -157,9 +157,9 @@ pub fn main() { t!(symlink_dir_inner(&tracing_dir, &latest_trace_dir)); } - debug!("creating new build based on config"); - let mut build = Build::new(config); - build.build(); + debug!("creating new session based on config"); + let mut sess = Session::new(config); + sess.build(); if suggest_setup { println!("WARNING: you have not made a `bootstrap.toml`"); @@ -213,8 +213,8 @@ pub fn main() { #[cfg(feature = "tracing")] { - build.report_summary(&tracing_dir.join("command-stats.txt"), _start_time); - build.report_step_graph(&tracing_dir); + sess.report_summary(&tracing_dir.join("command-stats.txt"), _start_time); + sess.report_step_graph(&tracing_dir); guard.copy_to_dir(&tracing_dir); eprintln!("Tracing/profiling output has been written to {}", latest_trace_dir.display()); } diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 4a75cdbb1562f..45d84d533fe14 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -647,7 +647,7 @@ impl CommandLineStep for GccCodegenBackend { fn run(self, builder: &Builder<'_>) { // FIXME: remove once https://github.com/rust-lang/rust/issues/112393 is resolved - if builder.build.config.vendor { + if builder.sess.config.vendor { println!("Skipping checking of `rustc_codegen_gcc` with vendoring enabled."); return; } diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs index 23f12bbb63e72..1c96f5ccd00d8 100644 --- a/src/bootstrap/src/core/build_steps/clean.rs +++ b/src/bootstrap/src/core/build_steps/clean.rs @@ -14,7 +14,7 @@ use crate::core::builder::{ }; use crate::core::compiler::Compiler; use crate::core::config::flags::Subcommand; -use crate::core::session::{Build, Mode}; +use crate::core::session::{Mode, Session}; use crate::utils::build_stamp::BuildStamp; use crate::utils::helpers::t; @@ -47,7 +47,7 @@ impl CommandLineStep for CleanAll { panic!("--all and --stage can't be used at the same time for `x clean`"); } - clean(builder.build, all, stage) + clean(builder.sess, all, stage) } } @@ -105,8 +105,8 @@ clean_crate_tree! { Std, Mode::Std, "sysroot"; } -fn clean(build: &Build, all: bool, stage: Option) { - if build.config.dry_run() { +fn clean(sess: &Session, all: bool, stage: Option) { + if sess.config.dry_run() { return; } @@ -114,23 +114,23 @@ fn clean(build: &Build, all: bool, stage: Option) { // Clean the entire build directory if all { - rm_rf(&build.out); + rm_rf(&sess.out); return; } // Clean the target stage artifacts if let Some(stage) = stage { - clean_specific_stage(build, stage); + clean_specific_stage(sess, stage); return; } // Follow the default behaviour - clean_default(build); + clean_default(sess); } -fn clean_specific_stage(build: &Build, stage: u32) { - for host in &build.hosts { - let entries = match build.out.join(host).read_dir() { +fn clean_specific_stage(sess: &Session, stage: u32) { + for host in &sess.hosts { + let entries = match sess.out.join(host).read_dir() { Ok(iter) => iter, Err(_) => continue, }; @@ -150,18 +150,18 @@ fn clean_specific_stage(build: &Build, stage: u32) { } } -fn clean_default(build: &Build) { - rm_rf(&build.out.join("tmp")); - rm_rf(&build.out.join("dist")); - rm_rf(&build.out.join("bootstrap").join(".last-warned-change-id")); - rm_rf(&build.out.join("bootstrap-shims-dump")); - rm_rf(BuildStamp::new(&build.out).with_prefix("rustfmt").path()); +fn clean_default(sess: &Session) { + rm_rf(&sess.out.join("tmp")); + rm_rf(&sess.out.join("dist")); + rm_rf(&sess.out.join("bootstrap").join(".last-warned-change-id")); + rm_rf(&sess.out.join("bootstrap-shims-dump")); + rm_rf(BuildStamp::new(&sess.out).with_prefix("rustfmt").path()); - let mut hosts: Vec<_> = build.hosts.iter().map(|t| build.out.join(t)).collect(); + let mut hosts: Vec<_> = sess.hosts.iter().map(|t| sess.out.join(t)).collect(); // After cross-compilation, artifacts of the host architecture (which may differ from build.host) // might not get removed. // Adding its path (linked one for easier accessibility) will solve this problem. - hosts.push(build.out.join("host")); + hosts.push(sess.out.join("host")); for host in hosts { let entries = match host.read_dir() { diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 9970c5b21056e..bb598d19c9fc9 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -777,7 +777,7 @@ impl Step for StdLink { }; let is_downloaded_beta_stage0 = builder - .build + .sess .config .initial_rustc .starts_with(builder.out.join(compiler.host).join("stage0/bin")); @@ -1147,7 +1147,7 @@ impl CommandLineStep for Rustc { cargo.arg("-p").arg(krate); } - if builder.build.config.enable_bolt_settings && build_compiler.stage == 1 { + if builder.sess.config.enable_bolt_settings && build_compiler.stage == 1 { // Relocations are required for BOLT to work. cargo.env("RUSTC_BOLT_LINK_FLAGS", "1"); } @@ -1256,7 +1256,7 @@ pub fn rustc_cargo( // us a faster startup time. However GNU ld < 2.40 will error if we try to link a shared object // with direct references to protected symbols, so for now we only use protected symbols if // linking with LLD is enabled. - if builder.build.config.bootstrap_override_lld.is_used() { + if builder.sess.config.bootstrap_override_lld.is_used() { cargo.rustflag("-Zdefault-visibility=protected"); } diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 53e7746d220f5..2112ec090f0b6 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -664,7 +664,7 @@ impl CommandLineStep for Rustc { let page_src = file_entry.path(); let page_dst = man_dst.join(file_entry.file_name()); let src_text = t!(std::fs::read_to_string(&page_src)); - let version = builder.rust_info().version(builder.build, &builder.version); + let version = builder.rust_info().version(builder.sess, &builder.version); let new_text = src_text.replace("", &version); t!(std::fs::write(&page_dst, &new_text)); } @@ -774,7 +774,7 @@ impl Step for DebuggerScripts { cp_debugger_script("gdb_load_rust_pretty_printers.py"); cp_debugger_script("gdb_lookup.py"); cp_debugger_script("gdb_providers.py"); - if builder.build.unstable_features() { + if builder.sess.unstable_features() { cp_debugger_script("gdb_trim_paths.py"); } @@ -787,7 +787,7 @@ impl Step for DebuggerScripts { cp_debugger_script("lldb_lookup.py"); cp_debugger_script("lldb_providers.py"); - if builder.build.unstable_features() { + if builder.sess.unstable_features() { cp_debugger_script("lldb_trim_paths.py"); } } @@ -1624,7 +1624,7 @@ impl CommandLineStep for Miri { // This prevents miri from being built for "dist" or "install" // on the stable/beta channels. It is a nightly-only tool and should // not be included. - if !builder.build.unstable_features() { + if !builder.sess.unstable_features() { return None; } @@ -1681,7 +1681,7 @@ impl CommandLineStep for CraneliftCodegenBackend { // This prevents rustc_codegen_cranelift from being built for "dist" // or "install" on the stable/beta channels. It is not yet stable and // should not be included. - if !builder.build.unstable_features() { + if !builder.sess.unstable_features() { return None; } @@ -1755,7 +1755,7 @@ impl CommandLineStep for GccCodegenBackend { // This prevents rustc_codegen_gcc from being built for "dist" // or "install" on the stable/beta channels. It is not yet stable and // should not be included. - if !builder.build.unstable_features() { + if !builder.sess.unstable_features() { return None; } @@ -2837,7 +2837,7 @@ impl CommandLineStep for Enzyme { // This prevents Enzyme from being built for "dist" // or "install" on the stable/beta channels. It is not yet stable and // should not be included. - if !builder.build.unstable_features() { + if !builder.sess.unstable_features() { return None; } @@ -3232,7 +3232,7 @@ impl CommandLineStep for Gcc { // This prevents gcc from being built for "dist" // or "install" on the stable/beta channels. It is not yet stable and // should not be included. - if !builder.build.unstable_features() { + if !builder.sess.unstable_features() { return None; } diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index d4dc9c2de53e6..4d16812127641 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -56,14 +56,14 @@ fn rustfmt( } } -fn get_rustfmt_version(build: &Builder<'_>) -> Option<(String, BuildStamp)> { - let stamp_file = BuildStamp::new(&build.out).with_prefix("rustfmt"); +fn get_rustfmt_version(builder: &Builder<'_>) -> Option<(String, BuildStamp)> { + let stamp_file = BuildStamp::new(&builder.out).with_prefix("rustfmt"); - let rustfmt = build.ensure(InternalRustfmt); + let rustfmt = builder.ensure(InternalRustfmt); let mut cmd = command(rustfmt.as_ref()?); cmd.arg("--version"); - let output = cmd.allow_failure().run_capture(build); + let output = cmd.allow_failure().run_capture(builder); if output.is_failure() { return None; } @@ -71,16 +71,16 @@ fn get_rustfmt_version(build: &Builder<'_>) -> Option<(String, BuildStamp)> { } /// Return whether the format cache can be reused. -fn verify_rustfmt_version(build: &Builder<'_>) -> bool { - let Some((version, stamp_file)) = get_rustfmt_version(build) else { +fn verify_rustfmt_version(builder: &Builder<'_>) -> bool { + let Some((version, stamp_file)) = get_rustfmt_version(builder) else { return false; }; stamp_file.add_stamp(version).is_up_to_date() } /// Updates the last rustfmt version used. -fn update_rustfmt_version(build: &Builder<'_>) { - let Some((version, stamp_file)) = get_rustfmt_version(build) else { +fn update_rustfmt_version(builder: &Builder<'_>) { + let Some((version, stamp_file)) = get_rustfmt_version(builder) else { return; }; @@ -91,16 +91,17 @@ fn update_rustfmt_version(build: &Builder<'_>) { /// Does not include removed files. /// /// Returns `None` if all files should be formatted. -fn get_modified_rs_files(build: &Builder<'_>) -> Result>, String> { +fn get_modified_rs_files(builder: &Builder<'_>) -> Result>, String> { // In CI `get_git_modified_files` returns something different to normal environment. // This shouldn't be called in CI anyway. - assert!(!build.config.is_running_on_ci()); + assert!(!builder.config.is_running_on_ci()); - if !verify_rustfmt_version(build) { + if !verify_rustfmt_version(builder) { return Ok(None); } - get_git_modified_files(&build.config.git_config(), Some(&build.config.src), &["rs"]).map(Some) + get_git_modified_files(&builder.config.git_config(), Some(&builder.config.src), &["rs"]) + .map(Some) } /// Rustfmt set via the config, or downloaded from CI, used to format local Rust code. @@ -143,13 +144,13 @@ fn print_paths(verb: &str, adjective: Option<&str>, paths: &[String]) { } pub fn format( - build: &Builder<'_>, + builder: &Builder<'_>, rustfmt_path: PathBuf, check: bool, all: bool, paths: &[PathBuf], ) { - if build.kind == Kind::Format && build.top_stage != 0 { + if builder.kind == Kind::Format && builder.top_stage != 0 { eprintln!("ERROR: `x fmt` only supports stage 0."); eprintln!("HELP: Use `x run rustfmt` to run in-tree rustfmt."); helpers::exit_process(1); @@ -161,7 +162,7 @@ pub fn format( ); helpers::exit_process(1); }; - if build.config.dry_run() { + if builder.config.dry_run() { return; } @@ -169,13 +170,15 @@ pub fn format( // `--all` is specified or we are in CI. We check all files in CI to avoid bugs in // `get_modified_rs_files` letting regressions slip through; we also care about CI time less // since this is still very fast compared to building the compiler. - let all = all || build.config.is_running_on_ci(); + let all = all || builder.config.is_running_on_ci(); - let mut builder = ignore::types::TypesBuilder::new(); - builder.add_defaults(); - builder.select("rust"); - let matcher = builder.build().unwrap(); - let rustfmt_config = build.src.join("rustfmt.toml"); + let matcher = { + let mut types = ignore::types::TypesBuilder::new(); + types.add_defaults(); + types.select("rust"); + types.build().unwrap() + }; + let rustfmt_config = builder.src.join("rustfmt.toml"); if !rustfmt_config.exists() { eprintln!("fmt error: Not running formatting checks; rustfmt.toml does not exist."); eprintln!("fmt error: This may happen in distributed tarballs."); @@ -183,7 +186,7 @@ pub fn format( } let rustfmt_config = t!(std::fs::read_to_string(&rustfmt_config)); let rustfmt_config: RustfmtConfig = t!(toml::from_str(&rustfmt_config)); - let mut override_builder = ignore::overrides::OverrideBuilder::new(&build.src); + let mut override_builder = ignore::overrides::OverrideBuilder::new(&builder.src); for ignore in rustfmt_config.ignore { if ignore.starts_with('!') { // A `!`-prefixed entry could be added as a whitelisted entry in `override_builder`, @@ -199,23 +202,23 @@ pub fn format( } } let git_available = - helpers::git(None).allow_failure().arg("--version").run_capture(build).is_success(); + helpers::git(None).allow_failure().arg("--version").run_capture(builder).is_success(); let mut adjective = None; if git_available { - let in_working_tree = helpers::git(Some(&build.src)) + let in_working_tree = helpers::git(Some(&builder.src)) .allow_failure() .arg("rev-parse") .arg("--is-inside-work-tree") - .run_capture(build) + .run_capture(builder) .is_success(); if in_working_tree { - let untracked_paths_output = helpers::git(Some(&build.src)) + let untracked_paths_output = helpers::git(Some(&builder.src)) .arg("status") .arg("--porcelain") .arg("-z") .arg("--untracked-files=normal") - .run_capture_stdout(build) + .run_capture_stdout(builder) .stdout(); let untracked_paths: Vec<_> = untracked_paths_output .split_terminator('\0') @@ -236,7 +239,7 @@ pub fn format( } if !all { adjective = Some("modified"); - match get_modified_rs_files(build) { + match get_modified_rs_files(builder) { Ok(Some(files)) => { if files.is_empty() { println!("fmt info: No modified files detected for formatting."); @@ -271,13 +274,13 @@ pub fn format( let override_ = override_builder.build().unwrap(); // `override` is a reserved keyword assert!(rustfmt_path.exists(), "{}", rustfmt_path.display()); - let src = build.src.clone(); + let src = builder.src.clone(); let (tx, rx): (SyncSender, _) = std::sync::mpsc::sync_channel(128); let walker = WalkBuilder::new(src.clone()).types(matcher).overrides(override_).build_parallel(); // There is a lot of blocking involved in spawning a child process and reading files to format. // Spawn more processes than available concurrency to keep the CPU busy. - let max_processes = build.jobs() as usize * 2; + let max_processes = builder.jobs() as usize * 2; // Spawn child processes on a separate thread so we can batch entries we have received from // ignore. @@ -370,5 +373,5 @@ pub fn format( // // NOTE: Because of the exit above, this is only reachable if formatting / format checking // succeeded. So we are not committing the version if formatting was not good. - update_rustfmt_version(build); + update_rustfmt_version(builder); } diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs index d3540bd21b0e4..650921d7231fc 100644 --- a/src/bootstrap/src/core/build_steps/gcc.rs +++ b/src/bootstrap/src/core/build_steps/gcc.rs @@ -290,7 +290,7 @@ fn build_gcc(metadata: &Meta, builder: &Builder<'_>, target_pair: GccTargetPair) // Target on which libgccjit.so will be executed. Here we will generate a dylib with // instructions for that target. let host = target_pair.host; - if builder.build.cc_tool(host).is_like_clang() || builder.build.cxx_tool(host).is_like_clang() { + if builder.sess.cc_tool(host).is_like_clang() || builder.sess.cxx_tool(host).is_like_clang() { panic!( "Attempting to build GCC using Clang, which is known to misbehave. Please use GCC as the host C/C++ compiler. " ); @@ -327,19 +327,19 @@ fn build_gcc(metadata: &Meta, builder: &Builder<'_>, target_pair: GccTargetPair) .arg("--with-bugurl=https://github.com/rust-lang/gcc/") .arg(format!("--prefix={}", install_dir.display())); - let cc = builder.build.cc(host).display().to_string(); + let cc = builder.sess.cc(host).display().to_string(); let cc = builder - .build + .sess .config .ccache .as_ref() .map_or_else(|| cc.clone(), |ccache| format!("{ccache} {cc}")); configure_cmd.env("CC", cc); - if let Ok(ref cxx) = builder.build.cxx(host) { + if let Ok(ref cxx) = builder.sess.cxx(host) { let cxx = cxx.display().to_string(); let cxx = builder - .build + .sess .config .ccache .as_ref() diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 1fba19c21a162..7e78835849c10 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -1314,7 +1314,7 @@ impl CommandLineStep for OmpOffload { let offload_clang_dir = if !builder.config.llvm_clang { // We must have an external clang to use. - builder.build.config.offload_clang_dir.clone() + builder.sess.config.offload_clang_dir.clone() } else { // No need to specify it, since we use the in-tree clang None diff --git a/src/bootstrap/src/core/build_steps/perf.rs b/src/bootstrap/src/core/build_steps/perf.rs index 2ea091532ae12..cc81d9243fe26 100644 --- a/src/bootstrap/src/core/build_steps/perf.rs +++ b/src/bootstrap/src/core/build_steps/perf.rs @@ -140,7 +140,7 @@ pub fn perf(builder: &Builder<'_>, args: &PerfArgs) { target: builder.config.host_target, }); - let rustc_perf_dir = builder.build.tempdir().join("rustc-perf"); + let rustc_perf_dir = builder.sess.tempdir().join("rustc-perf"); let results_dir = rustc_perf_dir.join("results"); builder.create_dir(&results_dir); @@ -158,7 +158,7 @@ pub fn perf(builder: &Builder<'_>, args: &PerfArgs) { | PerfCommand::Cachegrind { .. } => true, PerfCommand::Benchmark { .. } | PerfCommand::Compare { .. } => false, }; - if is_profiling && builder.build.config.rust_debuginfo_level_rustc == DebuginfoLevel::None { + if is_profiling && builder.sess.config.rust_debuginfo_level_rustc == DebuginfoLevel::None { builder.info(r#"WARNING: You are compiling rustc without debuginfo, this will make profiling less useful. Consider setting `rust.debuginfo-level = 1` in `bootstrap.toml`."#); } diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index 243b09acaa308..6051f5a27d71f 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -148,7 +148,7 @@ impl CommandLineStep for Miri { } fn run(self, builder: &Builder<'_>) { - let host = builder.build.host_target; + let host = builder.sess.host_target; let compilers = self.compilers; let target = self.target; @@ -257,7 +257,7 @@ impl CommandLineStep for GenerateCopyright { let paths_to_vendor = default_paths_to_vendor(builder); for (_, submodules) in &paths_to_vendor { for submodule in submodules { - builder.build.require_submodule(submodule, None); + builder.sess.require_submodule(submodule, None); } } let cargo_manifests = paths_to_vendor @@ -491,7 +491,7 @@ impl CommandLineStep for Rustfmt { } fn run(self, builder: &Builder<'_>) { - let host = builder.build.host_target; + let host = builder.sess.host_target; // `x run` uses stage 0 by default but rustfmt does not work well with stage 0. // Change the stage to 1 if it's not set explicitly. diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index a04dc3b8afb0d..d12f78dff2c72 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -173,7 +173,7 @@ impl CommandLineStep for Profile { } fn run(self, builder: &Builder<'_>) { - setup(&builder.build.config, self); + setup(&builder.sess.config, self); } } diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 5c16416266139..cb3dbe34c24bf 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -719,7 +719,7 @@ impl CommandLineStep for Miri { /// Runs `cargo test` for miri. fn run(self, builder: &Builder<'_>) { - let host = builder.build.host_target; + let host = builder.sess.host_target; let target = self.target; let stage = builder.top_stage; if stage == 0 { @@ -813,7 +813,7 @@ impl CommandLineStep for CargoMiri { /// Tests `cargo miri test`. fn run(self, builder: &Builder<'_>) { - let host = builder.build.host_target; + let host = builder.sess.host_target; let target = self.target; let stage = builder.top_stage; if stage == 0 { @@ -897,7 +897,7 @@ impl CommandLineStep for Priroda { /// Runs `cargo test` for priroda, reusing the Miri sysroot and binary. fn run(self, builder: &Builder<'_>) { - let host = builder.build.host_target; + let host = builder.sess.host_target; let target = self.target; let stage = builder.top_stage; @@ -1509,7 +1509,7 @@ fn get_browser_ui_test_version_inner( let mut command = command(yarn); command .arg("--cwd") - .arg(&builder.build.out) + .arg(&builder.sess.out) .arg("list") .arg("--parseable") .arg("--long") @@ -2285,7 +2285,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the // At stage 0 (stage - 1) we are using the stage0 compiler. Using `self.target` can lead // finding an incorrect compiler path on cross-targets, as the stage 0 is always equal to // `build.build` in the configuration. - let build = builder.build.host_target; + let build = builder.sess.host_target; test_compiler = builder.compiler(test_compiler.stage - 1, build); let test_stage = test_compiler.stage + 1; (test_stage, format!("stage{test_stage}-{build}")) @@ -2522,11 +2522,11 @@ Please disable assertions with `rust.debug-assertions = false`. cmd.arg("--bypass-ignore-backends"); } - if builder.build.config.llvm_enzyme { + if builder.sess.config.llvm_enzyme { cmd.arg("--has-enzyme"); } - if builder.build.config.llvm_offload { + if builder.sess.config.llvm_offload { cmd.arg("--has-offload"); } @@ -4016,7 +4016,7 @@ impl CommandLineStep for BootstrapPy { // Forward command-line args after `--` to unittest, for filtering etc. .args(builder.config.test_args()) .env("BUILD_DIR", &builder.out) - .env("BUILD_PLATFORM", builder.build.host_target.triple) + .env("BUILD_PLATFORM", builder.sess.host_target.triple) .env("BOOTSTRAP_TEST_RUSTC_BIN", &builder.initial_rustc) .env("BOOTSTRAP_TEST_CARGO_BIN", &builder.initial_cargo) .current_dir(builder.src.join("src/bootstrap/")); @@ -4049,7 +4049,7 @@ impl CommandLineStep for Bootstrap { let record_failed_tests = builder.ensure(SetupFailedTestsFile); // Some tests require cargo submodule to be present. - builder.build.require_submodule("src/tools/cargo", None); + builder.sess.require_submodule("src/tools/cargo", None); let mut cargo = tool::prepare_tool_cargo( builder, diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index e70352158feee..922784fb7e13a 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -212,7 +212,7 @@ pub fn prepare_tool_cargo( cargo.arg("--manifest-path").arg(dir.join("Cargo.toml")); let mut features = extra_features.to_vec(); - if builder.build.config.cargo_native_static { + if builder.sess.config.cargo_native_static { if path.ends_with("cargo") || path.ends_with("clippy") || path.ends_with("miri") @@ -865,7 +865,7 @@ impl CommandLineStep for Cargo { } fn run(self, builder: &Builder<'_>) -> ToolBuildResult { - builder.build.require_submodule("src/tools/cargo", None); + builder.sess.require_submodule("src/tools/cargo", None); builder.std(self.build_compiler, builder.host_target); builder.std(self.build_compiler, self.target); @@ -1523,7 +1523,7 @@ fn extended_rustc_tool_is_default_step( && builder.config.tools.as_ref().map_or( // By default, on nightly/dev enable all tools, else only // build stable tools. - stable || builder.build.unstable_features(), + stable || builder.sess.unstable_features(), // If `tools` is set, search list for this tool. |tools| { tools.iter().any(|tool| match tool.as_ref() { diff --git a/src/bootstrap/src/core/build_steps/vendor.rs b/src/bootstrap/src/core/build_steps/vendor.rs index 1bf9331500f8b..c6dd815d532ac 100644 --- a/src/bootstrap/src/core/build_steps/vendor.rs +++ b/src/bootstrap/src/core/build_steps/vendor.rs @@ -101,7 +101,7 @@ impl CommandLineStep for Vendor { // These submodules must be present for `x vendor` to work. for (_, submodules) in &to_vendor { for submodule in submodules { - builder.build.require_submodule(submodule, None); + builder.sess.require_submodule(submodule, None); } } diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 81e95c5a6f4ac..83598a52ed62b 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -646,7 +646,7 @@ impl Builder<'_> { // from out of tree it shouldn't matter, since x.py is only used for // building in-tree. let color_logs = ["RUSTDOC_LOG_COLOR", "RUSTC_LOG_COLOR", "RUST_LOG_COLOR"]; - match self.build.config.color { + match self.sess.config.color { Color::Always => { cargo.arg("--color=always"); for log in &color_logs { @@ -1172,14 +1172,14 @@ impl Builder<'_> { match mode { Mode::Rustc | Mode::Codegen => { if let Some(ref map_to) = - self.build.debuginfo_map_to(GitRepo::Rustc, RemapScheme::NonCompiler) + self.sess.debuginfo_map_to(GitRepo::Rustc, RemapScheme::NonCompiler) { // Tell the compiler which prefix was used for remapping the standard library cargo.env("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR", map_to); } if let Some(ref map_to) = - self.build.debuginfo_map_to(GitRepo::Rustc, RemapScheme::Compiler) + self.sess.debuginfo_map_to(GitRepo::Rustc, RemapScheme::Compiler) { // Tell the compiler which prefix was used for remapping the compiler it-self cargo.env("CFG_VIRTUAL_RUSTC_DEV_SOURCE_BASE_DIR", map_to); @@ -1190,14 +1190,14 @@ impl Builder<'_> { format!("compiler/={map_to}/compiler"), // rustc creates absolute paths (in part bc of the `rust-src` unremap // and for working directory) so let's remap the build directory as well. - format!("{}={map_to}", self.build.src.display()), + format!("{}={map_to}", self.sess.src.display()), // remap OUT_DIR so they don't leak into artifacts. - format!("{}={map_to}/out", self.build.out.display()), + format!("{}={map_to}/out", self.sess.out.display()), // on windows, rustc may use forward slashes internally #[cfg(windows)] format!( "{}={map_to}\\out", - self.build.out.display().to_string().replace('/', "\\") + self.sess.out.display().to_string().replace('/', "\\") ), ] .join("\t"); @@ -1210,7 +1210,7 @@ impl Builder<'_> { | Mode::ToolStd | Mode::ToolTarget => { if let Some(ref map_to) = - self.build.debuginfo_map_to(GitRepo::Rustc, RemapScheme::NonCompiler) + self.sess.debuginfo_map_to(GitRepo::Rustc, RemapScheme::NonCompiler) { // When building the standard library sources, we want to apply the std remap scheme. let map = [ @@ -1218,14 +1218,14 @@ impl Builder<'_> { format!("library/={map_to}/library"), // rustc creates absolute paths (in part bc of the `rust-src` unremap // and for working directory) so let's remap the build directory as well. - format!("{}={map_to}", self.build.src.display()), + format!("{}={map_to}", self.sess.src.display()), // remap OUT_DIR so they don't leak into artifacts. - format!("{}={map_to}/out", self.build.out.display()), + format!("{}={map_to}/out", self.sess.out.display()), // on windows, rustc may use forward slashes internally #[cfg(windows)] format!( "{}={map_to}\\out", - self.build.out.display().to_string().replace('/', "\\") + self.sess.out.display().to_string().replace('/', "\\") ), ] .join("\t"); @@ -1236,7 +1236,7 @@ impl Builder<'_> { if self.config.rust_remap_debuginfo { let mut env_var = OsString::new(); - if let Some(vendor) = self.build.vendored_crates_path() { + if let Some(vendor) = self.sess.vendored_crates_path() { env_var.push(vendor); env_var.push("=/rust/deps"); } else { @@ -1261,8 +1261,8 @@ impl Builder<'_> { prepare_shims_dump_dir(self); cargo - .env("DUMP_BOOTSTRAP_SHIMS", self.build.out.join("bootstrap-shims-dump")) - .env("BUILD_OUT", &self.build.out) + .env("DUMP_BOOTSTRAP_SHIMS", self.sess.out.join("bootstrap-shims-dump")) + .env("BUILD_OUT", &self.sess.out) .env("CARGO_HOME", t!(home::cargo_home())); }; diff --git a/src/bootstrap/src/core/builder/cli_paths/tests.rs b/src/bootstrap/src/core/builder/cli_paths/tests.rs index 3a92bf37bdf0a..a6754e063caf4 100644 --- a/src/bootstrap/src/core/builder/cli_paths/tests.rs +++ b/src/bootstrap/src/core/builder/cli_paths/tests.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use std::sync::{Arc, Mutex}; use crate::core::builder::{Builder, CommandLineStepDescription}; -use crate::core::session::Build; +use crate::core::session::Session; use crate::utils::tests::TestCtx; fn render_steps_for_cli_args(args_str: &str) -> String { @@ -25,11 +25,11 @@ fn render_steps_for_cli_args(args_str: &str) -> String { .hosts(hosts) .targets(targets) .create_config(); - let mut build = Build::new(config); + let mut sess = Session::new(config); // Some rustdoc test steps are only run by default if nodejs is // configured/discovered, causing inconsistency. - build.config.nodejs = Some(PathBuf::from("node")); - let mut builder = Builder::new(&build); + sess.config.nodejs = Some(PathBuf::from("node")); + let mut builder = Builder::new(&sess); // Tell the builder to log steps that it would run, instead of running them. let buf = Arc::new(Mutex::new(String::new())); diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 98fceeae9df5c..7f3a94b157efb 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -25,7 +25,7 @@ use crate::core::compiler::Compiler; use crate::core::config::flags::Subcommand; use crate::core::config::{DryRun, TargetSelection}; use crate::core::metadata::Crate; -use crate::core::session::Build; +use crate::core::session::Session; use crate::trace; use crate::utils::build_stamp::BuildStamp; use crate::utils::cache::Cache; @@ -43,7 +43,7 @@ mod tests; /// into account build configuration from e.g. bootstrap.toml. pub(crate) struct Builder<'a> { /// Build configuration from e.g. bootstrap.toml. - pub build: &'a Build, + pub sess: &'a Session, /// The stage to use. Either implicitly determined based on subcommand, or /// explicitly specified with `--stage N`. Normally this is the stage we @@ -69,7 +69,7 @@ pub(crate) struct Builder<'a> { /// "bar"]`. pub paths: Vec, - /// Cached list of submodules from self.build.src. + /// Cached list of submodules from self.sess.src. submodule_paths_cache: OnceLock>, /// When enabled by tests, this causes the top-level steps that _would_ be @@ -81,10 +81,10 @@ pub(crate) struct Builder<'a> { } impl Deref for Builder<'_> { - type Target = Build; + type Target = Session; fn deref(&self) -> &Self::Target { - self.build + self.sess } } @@ -278,7 +278,7 @@ pub struct RunConfig<'a> { impl RunConfig<'_> { pub fn build_triple(&self) -> TargetSelection { - self.builder.build.host_target + self.builder.sess.host_target } /// Return a list of crate names selected by `run.paths`. @@ -1024,19 +1024,19 @@ impl<'a> Builder<'a> { } Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std), Kind::Vendor => describe!(vendor::Vendor), - // special-cased in Build::build() + // special-cased in Session::build() Kind::Format | Kind::Perf => vec![], Kind::MiriTest | Kind::MiriSetup => unreachable!(), } } - pub fn get_help(build: &Build, kind: Kind) -> Option { + pub fn get_help(sess: &Session, kind: Kind) -> Option { let step_descriptions = Builder::get_step_descriptions(kind); if step_descriptions.is_empty() { return None; } - let builder = Self::new_internal(build, kind, vec![]); + let builder = Self::new_internal(sess, kind, vec![]); let builder = &builder; let mut should_run = ShouldRun::new(builder); @@ -1062,10 +1062,10 @@ impl<'a> Builder<'a> { Some(help) } - fn new_internal(build: &Build, kind: Kind, paths: Vec) -> Builder<'_> { + fn new_internal(sess: &Session, kind: Kind, paths: Vec) -> Builder<'_> { Builder { - build, - top_stage: build.config.stage, + sess, + top_stage: sess.config.stage, kind, cache: Cache::new(), stack: RefCell::new(Vec::new()), @@ -1076,9 +1076,9 @@ impl<'a> Builder<'a> { } } - pub fn new(build: &Build) -> Builder<'_> { - let paths = &build.config.paths; - let (kind, paths) = match build.config.cmd { + pub fn new(sess: &Session) -> Builder<'_> { + let paths = &sess.config.paths; + let (kind, paths) = match sess.config.cmd { Subcommand::Build { .. } => (Kind::Build, &paths[..]), Subcommand::Check { .. } => (Kind::Check, &paths[..]), Subcommand::Clippy { .. } => (Kind::Clippy, &paths[..]), @@ -1101,7 +1101,7 @@ impl<'a> Builder<'a> { }; StepStack::with_current(|stack| stack.clear()); - Self::new_internal(build, kind, paths.to_owned()) + Self::new_internal(sess, kind, paths.to_owned()) } pub fn execute_cli(&self) { @@ -1231,10 +1231,10 @@ impl<'a> Builder<'a> { host: TargetSelection, target: TargetSelection, ) -> Compiler { - let mut resolved_compiler = if self.build.force_use_stage2(stage) { + let mut resolved_compiler = if self.sess.force_use_stage2(stage) { trace!(target: "COMPILER_FOR", ?stage, "force_use_stage2"); self.compiler(2, self.config.host_target) - } else if self.build.force_use_stage1(stage, target) { + } else if self.sess.force_use_stage1(stage, target) { trace!(target: "COMPILER_FOR", ?stage, "force_use_stage1"); self.compiler(1, self.config.host_target) } else { @@ -1368,7 +1368,7 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler pub fn sysroot_libdir_relative(&self, compiler: Compiler) -> &Path { match self.config.libdir_relative() { Some(relative_libdir) if compiler.stage >= 1 => relative_libdir, - _ if compiler.stage == 0 => &self.build.initial_relative_libdir, + _ if compiler.stage == 0 => &self.sess.initial_relative_libdir, _ => Path::new("lib"), } } @@ -1436,8 +1436,7 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler pub fn cargo_miri_cmd(&self, run_compiler: Compiler) -> BootstrapCommand { assert!(run_compiler.stage > 0, "miri can not be invoked at stage 0"); - let compilers = - RustcPrivateCompilers::new(self, run_compiler.stage, self.build.host_target); + let compilers = RustcPrivateCompilers::new(self, run_compiler.stage, self.sess.host_target); assert_eq!(run_compiler, compilers.target_compiler()); // Prepare the tools @@ -1467,7 +1466,7 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler .config .initial_cargo_clippy .clone() - .unwrap_or_else(|| self.build.config.download_clippy()); + .unwrap_or_else(|| self.sess.config.download_clippy()); let mut cmd = command(cargo_clippy); cmd.env("CARGO", &self.initial_cargo); @@ -1583,7 +1582,7 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler #[cfg(feature = "tracing")] { if let Some(parent) = stack.last() { - let mut graph = self.build.step_graph.borrow_mut(); + let mut graph = self.sess.step_graph.borrow_mut(); graph.register_cached_step(&step, parent, self.config.dry_run()); } } @@ -1593,7 +1592,7 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler #[cfg(feature = "tracing")] { let parent = stack.last(); - let mut graph = self.build.step_graph.borrow_mut(); + let mut graph = self.sess.step_graph.borrow_mut(); graph.register_step_execution(&step, parent, self.config.dry_run()); } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 5ef46a25a1e4f..fed59c3b14f2c 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -23,8 +23,8 @@ fn configure_with_args(cmd: &[&str], host: &[&str], target: &[&str]) -> Config { } fn run_build(paths: &[PathBuf], config: Config) -> Cache { - let build = Build::new(config); - let builder = Builder::new(&build); + let sess = Session::new(config); + let builder = Builder::new(&sess); builder.run_step_descriptions(&Builder::get_step_descriptions(builder.kind), paths); builder.cache } @@ -114,13 +114,13 @@ fn parse_config_download_rustc_at(path: &Path, download_rustc: &str, ci: bool) - mod sysroot_target_dirs { use super::{ - Build, Builder, Compiler, TEST_TRIPLE_1, TEST_TRIPLE_2, TargetSelection, configure, + Builder, Compiler, Session, TEST_TRIPLE_1, TEST_TRIPLE_2, TargetSelection, configure, }; #[test] fn test_sysroot_target_libdir() { - let build = Build::new(configure("build", &[TEST_TRIPLE_1], &[TEST_TRIPLE_1])); - let builder = Builder::new(&build); + let sess = Session::new(configure("build", &[TEST_TRIPLE_1], &[TEST_TRIPLE_1])); + let builder = Builder::new(&sess); let target_triple_1 = TargetSelection::from_user(TEST_TRIPLE_1); let compiler = Compiler::new(1, target_triple_1); let target_triple_2 = TargetSelection::from_user(TEST_TRIPLE_2); @@ -139,8 +139,8 @@ mod sysroot_target_dirs { #[test] fn test_sysroot_target_bindir() { - let build = Build::new(configure("build", &[TEST_TRIPLE_1], &[TEST_TRIPLE_1])); - let builder = Builder::new(&build); + let sess = Session::new(configure("build", &[TEST_TRIPLE_1], &[TEST_TRIPLE_1])); + let builder = Builder::new(&sess); let target_triple_1 = TargetSelection::from_user(TEST_TRIPLE_1); let compiler = Compiler::new(1, target_triple_1); let target_triple_2 = TargetSelection::from_user(TEST_TRIPLE_2); @@ -242,8 +242,8 @@ fn test_prebuilt_llvm_config_path_resolution() { "#, ); - let build = Build::new(config); - let builder = Builder::new(&build); + let sess = Session::new(config); + let builder = Builder::new(&sess); let expected = PathBuf::from("/some/path/to/llvm-config"); @@ -270,8 +270,8 @@ fn test_prebuilt_llvm_config_path_resolution() { "#, ); - let build = Build::new(config.clone()); - let builder = Builder::new(&build); + let sess = Session::new(config.clone()); + let builder = Builder::new(&sess); let actual = get_llvm_build_status(&builder, builder.config.host_target) .llvm_output() @@ -293,8 +293,8 @@ fn test_prebuilt_llvm_config_path_resolution() { // CI-LLVM isn't always available; check if it's enabled before testing. if config.llvm_ci_mode.download_from_ci() { - let build = Build::new(config.clone()); - let builder = Builder::new(&build); + let sess = Session::new(config.clone()); + let builder = Builder::new(&sess); let actual = get_llvm_build_status(&builder, builder.config.host_target) .llvm_output() @@ -317,8 +317,8 @@ fn test_is_builder_target() { for (target1, target2) in [(target1, target2), (target2, target1)] { let mut config = configure("build", &[], &[]); config.host_target = target1; - let build = Build::new(config); - let builder = Builder::new(&build); + let sess = Session::new(config); + let builder = Builder::new(&sess); assert!(builder.config.is_host_target(target1)); assert!(!builder.config.is_host_target(target2)); @@ -3126,8 +3126,8 @@ impl ConfigBuilder { fn run(self) -> Cache { let config = self.create_config(); - let build = Build::new(config); - let builder = Builder::new(&build); + let sess = Session::new(config); + let builder = Builder::new(&sess); builder .run_step_descriptions(&Builder::get_step_descriptions(builder.kind), &builder.paths); builder.cache diff --git a/src/bootstrap/src/core/compiler.rs b/src/bootstrap/src/core/compiler.rs index 5602e8ffd1efd..f0db8b3bd8ce4 100644 --- a/src/bootstrap/src/core/compiler.rs +++ b/src/bootstrap/src/core/compiler.rs @@ -1,7 +1,7 @@ use std::hash::{Hash, Hasher}; use crate::core::config::TargetSelection; -use crate::core::session::Build; +use crate::core::session::Session; /// A structure representing a Rust compiler. /// @@ -39,9 +39,9 @@ impl Compiler { self.forced_compiler = forced_compiler; } - /// Returns `true` if this is a snapshot compiler for `build`'s configuration - pub(crate) fn is_snapshot(&self, build: &Build) -> bool { - self.stage == 0 && self.host == build.host_target + /// Returns `true` if this is a snapshot compiler for the session's configuration + pub(crate) fn is_snapshot(&self, sess: &Session) -> bool { + self.stage == 0 && self.host == sess.host_target } /// Indicates whether the compiler was forced to use a specific stage. diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index d96300e0789aa..6df39a0927738 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -94,7 +94,7 @@ pub(crate) struct Config { pub bypass_bootstrap_lock: bool, pub ccache: Option, pub sde: Option, - /// Call Build::ninja() instead of this. + /// Call `Session::ninja` instead of this. pub ninja_in_file: bool, pub submodules: Option, pub compiler_docs: bool, @@ -1853,7 +1853,7 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to /// /// This *does not* update the submodule if `bootstrap.toml` explicitly says /// not to, or if we're not in a git repository (like a plain source - /// tarball). Typically [`crate::core::session::Build::require_submodule`] should be + /// tarball). Typically [`crate::core::session::Session::require_submodule`] should be /// used instead to provide a nice error to the user if the submodule is /// missing. #[cfg_attr( diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index da479251c68ab..109eb916d3fbd 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -17,7 +17,7 @@ use crate::core::build_steps::test::TestTarget; use crate::core::builder::{Builder, Kind}; use crate::core::config::Config; use crate::core::config::target_selection::{TargetSelectionList, target_selection_list}; -use crate::core::session::Build; +use crate::core::session::Session; use crate::utils::helpers; #[derive(Copy, Clone, Default, Debug, ValueEnum)] @@ -223,8 +223,8 @@ impl Flags { println!("NOTE: updating submodules before printing available paths"); let flags = Self::parse(&[String::from("build")]); let config = Config::parse(flags); - let build = Build::new(config); - let paths = Builder::get_help(&build, subcommand); + let sess = Session::new(config); + let paths = Builder::get_help(&sess, subcommand); if let Some(s) = paths { println!("{s}"); } else { diff --git a/src/bootstrap/src/core/metadata.rs b/src/bootstrap/src/core/metadata.rs index 5e88277008971..8e66598fda993 100644 --- a/src/bootstrap/src/core/metadata.rs +++ b/src/bootstrap/src/core/metadata.rs @@ -11,7 +11,7 @@ use std::path::PathBuf; use serde_derive::Deserialize; -use crate::core::session::Build; +use crate::core::session::Session; use crate::utils::exec::command; use crate::utils::helpers::t; @@ -24,8 +24,8 @@ pub(crate) struct Crate { } impl Crate { - pub(crate) fn local_path(&self, build: &Build) -> PathBuf { - self.path.strip_prefix(&build.config.src).unwrap().into() + pub(crate) fn local_path(&self, sess: &Session) -> PathBuf { + self.path.strip_prefix(&sess.config.src).unwrap().into() } } @@ -55,10 +55,10 @@ struct Dependency { source: Option, } -/// Collects and stores package metadata of each workspace members into `build`, +/// Collects and stores package metadata of each workspace members into `sess`, /// by executing `cargo metadata` commands. -pub fn build(build: &mut Build) { - for package in workspace_members(build) { +pub(crate) fn build(sess: &mut Session) { + for package in workspace_members(sess) { if package.source.is_none() { let name = package.name; let mut path = PathBuf::from(package.manifest_path); @@ -75,9 +75,9 @@ pub fn build(build: &mut Build) { path, features: package.features.keys().cloned().collect(), }; - let relative_path = krate.local_path(build); - build.crates.insert(name.clone(), krate); - let existing_path = build.crate_paths.insert(relative_path, name); + let relative_path = krate.local_path(sess); + sess.crates.insert(name.clone(), krate); + let existing_path = sess.crate_paths.insert(relative_path, name); assert!( existing_path.is_none(), "multiple crates with the same path: {}", @@ -91,9 +91,9 @@ pub fn build(build: &mut Build) { /// /// This is used to resolve specific crate paths in `fn should_run` to compile /// particular crate (e.g., `x build sysroot` to build library/sysroot). -fn workspace_members(build: &Build) -> Vec { +fn workspace_members(sess: &Session) -> Vec { let collect_metadata = |manifest_path| { - let mut cargo = command(&build.initial_cargo); + let mut cargo = command(&sess.initial_cargo); cargo // Will read the libstd Cargo.toml // which uses the unstable `public-dependency` feature. @@ -103,8 +103,8 @@ fn workspace_members(build: &Build) -> Vec { .arg("1") .arg("--no-deps") .arg("--manifest-path") - .arg(build.src.join(manifest_path)); - let metadata_output = cargo.run_in_dry_run().run_capture_stdout(build).stdout(); + .arg(sess.src.join(manifest_path)); + let metadata_output = cargo.run_in_dry_run().run_capture_stdout(sess).stdout(); let Output { packages, .. } = t!(serde_json::from_str(&metadata_output)); packages }; diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 148f2ac1212c0..27638810fb7bb 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -18,7 +18,7 @@ use crate::core::build_steps::tool; use crate::core::builder::Builder; use crate::core::config::flags::Subcommand; use crate::core::config::{CompilerBuiltins, DebuggerPath, Target}; -use crate::core::session::Build; +use crate::core::session::Session; use crate::utils::exec::command; use crate::utils::helpers::{self, t}; @@ -78,15 +78,15 @@ impl Finder { } } -pub fn check(build: &mut Build) { +pub(crate) fn check(sess: &mut Session) { let mut skip_target_sanity = env::var_os("BOOTSTRAP_SKIP_TARGET_SANITY").is_some_and(|s| s == "1" || s == "true"); - skip_target_sanity |= matches!(build.config.cmd, Subcommand::Check { .. }); + skip_target_sanity |= matches!(sess.config.cmd, Subcommand::Check { .. }); // Skip target sanity checks when we are doing anything with mir-opt tests or Miri let skipped_paths = [OsStr::new("mir-opt"), OsStr::new("miri")]; - skip_target_sanity |= build.config.paths.iter().any(|path| { + skip_target_sanity |= sess.config.paths.iter().any(|path| { path.components().any(|component| skipped_paths.contains(&component.as_os_str())) }); @@ -102,18 +102,18 @@ pub fn check(build: &mut Build) { let mut cmd_finder = Finder::new(); // If we've got a git directory we're gonna need git to update // submodules and learn about various other aspects. - if build.rust_info().is_managed_git_subrepository() { + if sess.rust_info().is_managed_git_subrepository() { cmd_finder.must_have("git"); } // Ensure that a compatible version of libstdc++ is available on the system when using `llvm.download-ci-llvm`. if cfg!(not(test)) - && !build.config.dry_run() - && !build.host_target.is_msvc() - && build.config.llvm_ci_mode.download_from_ci() + && !sess.config.dry_run() + && !sess.host_target.is_msvc() + && sess.config.llvm_ci_mode.download_from_ci() { - let builder = Builder::new(build); - let libcxx_version = builder.ensure(tool::LibcxxVersionTool { target: build.host_target }); + let builder = Builder::new(sess); + let libcxx_version = builder.ensure(tool::LibcxxVersionTool { target: sess.host_target }); match libcxx_version { tool::LibcxxVersion::Gnu(version) => { @@ -138,11 +138,11 @@ pub fn check(build: &mut Build) { } // We need cmake, but only if we're actually building LLVM or sanitizers. - let building_llvm = !build.config.llvm_ci_mode.download_from_ci() - && !build.config.local_rebuild - && build.hosts.iter().any(|host| { - build.config.llvm_enabled(*host) - && build + let building_llvm = !sess.config.llvm_ci_mode.download_from_ci() + && !sess.config.local_rebuild + && sess.hosts.iter().any(|host| { + sess.config.llvm_enabled(*host) + && sess .config .target_config .get(host) @@ -150,7 +150,7 @@ pub fn check(build: &mut Build) { .unwrap_or(true) }); - let need_cmake = building_llvm || build.config.any_sanitizers_to_build(); + let need_cmake = building_llvm || sess.config.any_sanitizers_to_build(); if need_cmake && cmd_finder.maybe_have("cmake").is_none() { eprintln!( " @@ -164,7 +164,7 @@ than building it. helpers::exit_process(1); } - build.config.python = build + sess.config.python = sess .config .python .take() @@ -174,7 +174,7 @@ than building it. .or_else(|| cmd_finder.maybe_have("python3")) .or_else(|| cmd_finder.maybe_have("python2")); - build.config.nodejs = build + sess.config.nodejs = sess .config .nodejs .take() @@ -182,29 +182,29 @@ than building it. .or_else(|| cmd_finder.maybe_have("node")) .or_else(|| cmd_finder.maybe_have("nodejs")); - build.config.yarn = build + sess.config.yarn = sess .config .yarn .take() .map(|p| cmd_finder.must_have(p)) .or_else(|| cmd_finder.maybe_have("yarn")); - build.config.gdb = build.config.gdb.take().map(|p| match p { + sess.config.gdb = sess.config.gdb.take().map(|p| match p { DebuggerPath::Discover => DebuggerPath::Discover, DebuggerPath::Path(path) => DebuggerPath::Path(cmd_finder.must_have(path)), }); - build.config.reuse = build + sess.config.reuse = sess .config .reuse .take() .map(|p| cmd_finder.must_have(p)) .or_else(|| cmd_finder.maybe_have("reuse")); - let stage0_supported_target_list: HashSet = command(&build.config.initial_rustc) + let stage0_supported_target_list: HashSet = command(&sess.config.initial_rustc) .args(["--print", "target-list"]) .run_in_dry_run() - .run_capture_stdout(&build) + .run_capture_stdout(&sess) .stdout() .lines() .map(|s| s.to_string()) @@ -214,9 +214,9 @@ than building it. // because they are not needed. // // See `cc_detect::find` for more details. - let skip_tools_checks = build.config.dry_run() + let skip_tools_checks = sess.config.dry_run() || matches!( - build.config.cmd, + sess.config.cmd, Subcommand::Clean { .. } | Subcommand::Check { .. } | Subcommand::Format { .. } @@ -225,7 +225,7 @@ than building it. // We're gonna build some custom C code here and there, host triples // also build some C++ shims for LLVM so we need a C++ compiler. - for target in &build.targets { + for target in &sess.targets { // On emscripten we don't actually need the C compiler to just // build the target artifacts, only for testing. For the sake // of easier bot configuration, just skip detection. @@ -243,12 +243,12 @@ than building it. } // skip check for cross-targets - if skip_target_sanity && target != &build.host_target { + if skip_target_sanity && target != &sess.host_target { continue; } // Ignore fake targets that are only used for unit tests in bootstrap. - if cfg!(not(test)) && !skip_target_sanity && !build.local_rebuild { + if cfg!(not(test)) && !skip_target_sanity && !sess.local_rebuild { let mut has_target = false; let target_str = target.to_string(); @@ -301,33 +301,32 @@ than building it. } if !skip_tools_checks { - cmd_finder.must_have(build.cc(*target)); - if let Some(ar) = build.ar(*target) { + cmd_finder.must_have(sess.cc(*target)); + if let Some(ar) = sess.ar(*target) { cmd_finder.must_have(ar); } } } if !skip_tools_checks { - for host in &build.hosts { - cmd_finder.must_have(build.cxx(*host).unwrap()); + for host in &sess.hosts { + cmd_finder.must_have(sess.cxx(*host).unwrap()); } } - for target in &build.targets { - build - .config + for target in &sess.targets { + sess.config .target_config .entry(*target) .or_insert_with(|| Target::from_triple(&target.triple)); // compiler-rt c fallbacks for wasm cannot be built with gcc if target.contains("wasm") - && (*build.config.optimized_compiler_builtins(*target) + && (*sess.config.optimized_compiler_builtins(*target) != CompilerBuiltins::BuildRustOnly - || build.config.rust_std_features.contains("compiler-builtins-c")) + || sess.config.rust_std_features.contains("compiler-builtins-c")) { - let cc_tool = build.cc_tool(*target); + let cc_tool = sess.cc_tool(*target); if !cc_tool.is_like_clang() && !cc_tool.path().ends_with("emcc") { // emcc works as well panic!( @@ -340,19 +339,19 @@ than building it. } if (target.contains("-none-") || target.contains("nvptx")) - && build.no_std(*target) == Some(false) + && sess.no_std(*target) == Some(false) { panic!("All the *-none-* and nvptx* targets are no-std targets") } // skip check for cross-targets - if skip_target_sanity && target != &build.host_target { + if skip_target_sanity && target != &sess.host_target { continue; } // Make sure musl-root is valid. if target.contains("musl") && !target.contains("unikraft") { - match build.musl_libdir(*target) { + match sess.musl_libdir(*target) { Some(libdir) => { if fs::metadata(libdir.join("libc.a")).is_err() { panic!("couldn't find libc.a in musl libdir: {}", libdir.display()); @@ -371,7 +370,7 @@ than building it. // Cygwin. The Cygwin build does not have generators for Visual // Studio, so detect that here and error. let out = - command("cmake").arg("--help").run_in_dry_run().run_capture_stdout(&build).stdout(); + command("cmake").arg("--help").run_in_dry_run().run_capture_stdout(&sess).stdout(); if !out.contains("Visual Studio") { panic!( " @@ -395,15 +394,15 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake // but if it's disabled then double-check it's present on the system. if target.contains("wasip") && !target.contains("wasip1") - && !build.tool_enabled("wasm-component-ld") + && !sess.tool_enabled("wasm-component-ld") { cmd_finder.must_have("wasm-component-ld"); } // aarch64-unknown-linux-pauthtest must use clang if !skip_tools_checks && target.is_pauthtest() { - let cc_tool = build.cc_tool(*target); - let linker_path = build + let cc_tool = sess.cc_tool(*target); + let linker_path = sess .linker(*target) .unwrap_or_else(|| panic!("{} requires an explicit clang linker", target.triple)); @@ -431,7 +430,7 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake } let output = - command(cc_tool.path()).arg("-dumpversion").run_capture_stdout(&build).stdout(); + command(cc_tool.path()).arg("-dumpversion").run_capture_stdout(&sess).stdout(); let version_str = output.trim(); let mut parts = version_str.split('.').map(|s| s.parse::().unwrap_or(0)); let major = parts.next().unwrap_or(0); @@ -448,7 +447,7 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake } } - if let Some(ref s) = build.config.ccache { + if let Some(ref s) = sess.config.ccache { cmd_finder.must_have(s); } } diff --git a/src/bootstrap/src/core/session.rs b/src/bootstrap/src/core/session.rs index 6d0d29dcc1fc4..47814a4e367f3 100644 --- a/src/bootstrap/src/core/session.rs +++ b/src/bootstrap/src/core/session.rs @@ -40,13 +40,8 @@ pub(crate) enum GitRepo { /// This structure transitively contains all configuration for the build system. /// All filesystem-encoded configuration is in `config`, all flags are in /// `flags`, and then parsed or probed information is listed in the keys below. -/// -/// This structure is a parameter of almost all methods in the build system, -/// although most functions are implemented as free functions rather than -/// methods specifically on this structure itself (to make it easier to -/// organize). -pub(crate) struct Build { - /// User-specified configuration from `bootstrap.toml`. +pub(crate) struct Session { + /// User-specified configuration from command-line flags and `bootstrap.toml`. pub(crate) config: Config, // Version information @@ -228,8 +223,8 @@ impl FileType { } macro_rules! forward { - ( $( $fn:ident( $($param:ident: $ty:ty),* ) $( -> $ret:ty)? ),+ $(,)? ) => { - impl Build { + ($( $fn:ident( $($param:ident: $ty:ty),* ) $( -> $ret:ty)? ),+ $(,)? ) => { + impl Session { $( pub(crate) fn $fn(&self, $($param: $ty),* ) $( -> $ret)? { self.config.$fn( $($param),* ) @@ -267,12 +262,12 @@ impl From for TargetAndStage { } } -impl Build { +impl Session { /// Creates a new set of build configuration from the `flags` on the command /// line and the filesystem `config`. /// /// By default all build output will be placed in the current directory. - pub(crate) fn new(mut config: Config) -> Build { + pub(crate) fn new(mut config: Config) -> Session { let src = config.src.clone(); let out = config.out.clone(); @@ -362,7 +357,7 @@ impl Build { config.description = Some("built from a source tarball".to_owned()); } - let mut build = Build { + let mut sess = Session { initial_lld, initial_relative_libdir, initial_rustc: config.initial_rustc.clone(), @@ -411,10 +406,10 @@ impl Build { // If local-rust is the same major.minor as the current version, then force a // local-rebuild - let local_version_verbose = command(&build.initial_rustc) + let local_version_verbose = command(&sess.initial_rustc) .run_in_dry_run() .args(["--version", "--verbose"]) - .run_capture_stdout(&build) + .run_capture_stdout(&sess) .stdout(); let local_release = local_version_verbose .lines() @@ -423,26 +418,26 @@ impl Build { .unwrap() .trim(); if local_release.split('.').take(2).eq(version.split('.').take(2)) { - build.do_if_verbose(|| println!("auto-detected local-rebuild {local_release}")); - build.local_rebuild = true; + sess.do_if_verbose(|| println!("auto-detected local-rebuild {local_release}")); + sess.local_rebuild = true; } - build.do_if_verbose(|| println!("finding compilers")); - crate::utils::cc_detect::fill_compilers(&mut build); + sess.do_if_verbose(|| println!("finding compilers")); + crate::utils::cc_detect::fill_compilers(&mut sess); // When running `setup`, the profile is about to change, so any requirements we have now may // be different on the next invocation. Don't check for them until the next time x.py is // run. This is ok because `setup` never runs any build commands, so it won't fail if commands are missing. // // Similarly, for `setup` we don't actually need submodules or cargo metadata. - if !matches!(build.config.cmd, Subcommand::Setup { .. }) { - build.do_if_verbose(|| println!("running sanity check")); - crate::core::sanity::check(&mut build); + if !matches!(sess.config.cmd, Subcommand::Setup { .. }) { + sess.do_if_verbose(|| println!("running sanity check")); + crate::core::sanity::check(&mut sess); // Make sure we update these before gathering metadata so we don't get an error about missing // Cargo.toml files. let rust_submodules = ["library/backtrace"]; for s in rust_submodules { - build.require_submodule( + sess.require_submodule( s, Some( "The submodule is required for the standard library \ @@ -451,30 +446,30 @@ impl Build { ); } // Now, update all existing submodules. - build.update_existing_submodules(); + sess.update_existing_submodules(); - build.do_if_verbose(|| println!("learning about cargo")); - crate::core::metadata::build(&mut build); + sess.do_if_verbose(|| println!("learning about cargo")); + crate::core::metadata::build(&mut sess); } // Create symbolic link to use host sysroot from a consistent path (e.g., in the rust-analyzer config file). - let build_triple = build.out.join(build.host_target); + let build_triple = sess.out.join(sess.host_target); t!(fs::create_dir_all(&build_triple)); - let host = build.out.join("host"); + let host = sess.out.join("host"); if host.is_symlink() { // Left over from a previous build; overwrite it. - // This matters if `build.build` has changed between invocations. + // This matters if `sess.host_target` has changed between invocations. #[cfg(windows)] t!(fs::remove_dir(&host)); #[cfg(not(windows))] t!(fs::remove_file(&host)); } t!( - symlink_dir(&build.config, &build_triple, &host), + symlink_dir(&sess.config, &build_triple, &host), format!("symlink_dir({} => {}) failed", host.display(), build_triple.display()) ); - build + sess } /// Updates a submodule, and exits with a failure if submodule management @@ -489,10 +484,10 @@ impl Build { feature = "tracing", instrument( level = "trace", - name = "Build::require_submodule", + name = "Session::require_submodule", skip_all, fields(submodule = submodule), - ), + ) )] pub(crate) fn require_submodule(&self, submodule: &str, err_hint: Option<&str>) { if self.rust_info().is_from_tarball() { @@ -567,7 +562,7 @@ impl Build { } /// Executes the entire build, as configured by the flags and configuration. - #[cfg_attr(feature = "tracing", instrument(level = "debug", name = "Build::build", skip_all))] + #[cfg_attr(feature = "tracing", instrument(level = "debug", name = "Session::build", skip_all))] pub(crate) fn build(&mut self) { trace!("setting up job management"); unsafe { @@ -885,7 +880,7 @@ impl Build { /// Return a `Group` guard for a [`Step`] that: /// - Performs `action` - /// - If the action is `Kind::Test`, use [`Build::msg_test`] instead. + /// - If the action is `Kind::Test`, use [`Session::msg_test`] instead. /// - On `what` /// - Where `what` possibly corresponds to a `mode` /// - `action` is performed with/on the given compiler (`target_and_stage`). @@ -908,7 +903,7 @@ impl Build { let action = action.into(); assert!( action != Kind::Test, - "Please use `Build::msg_test` instead of `Build::msg(Kind::Test)`" + "Please use `Session::msg_test` instead of `Session::msg(Kind::Test)`" ); let actual_stage = match mode.into() { @@ -947,7 +942,7 @@ impl Build { } /// Return a `Group` guard for a [`Step`] that tests `what` with the given `stage` and `target`. - /// Use this instead of [`Build::msg`] for test steps, because for them it is not always clear + /// Use this instead of [`Session::msg`] for test steps, because for them it is not always clear /// what exactly is a build compiler. /// /// [`Step`]: crate::core::builder::Step @@ -1868,7 +1863,7 @@ to download LLVM rather than building it. } } -impl AsRef for Build { +impl AsRef for Session { fn as_ref(&self) -> &ExecutionContext { &self.config.exec_ctx } diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs index e753ee71683fd..24308cdadaa4a 100644 --- a/src/bootstrap/src/utils/cc_detect.rs +++ b/src/bootstrap/src/utils/cc_detect.rs @@ -27,29 +27,29 @@ use std::path::{Path, PathBuf}; use crate::core::config::flags::Subcommand; use crate::core::config::{CompressDebuginfo, TargetSelection}; -use crate::core::session::{Build, CLang, GitRepo}; +use crate::core::session::{CLang, GitRepo, Session}; use crate::utils::exec::{BootstrapCommand, command}; /// Creates and configures a new [`cc::Build`] instance for the given target. -fn new_cc_build(build: &Build, target: TargetSelection) -> cc::Build { +fn new_cc_build(sess: &Session, target: TargetSelection) -> cc::Build { let mut cfg = cc::Build::new(); cfg.cargo_metadata(false) .opt_level(2) .warnings(false) .debug(false) // We have to configure out_dir, otherwise flag_if_supported will not work - .out_dir(build.tempdir().join("cc-rs-out-dir")) + .out_dir(sess.tempdir().join("cc-rs-out-dir")) .target(&target.triple) - .host(&build.host_target.triple); + .host(&sess.host_target.triple); - match build.config.compress_debuginfo(target) { + match sess.config.compress_debuginfo(target) { CompressDebuginfo::Zlib => { cfg.flag_if_supported("-gz"); } CompressDebuginfo::Off => {} } - match build.crt_static(target) { + match sess.crt_static(target) { Some(a) => { cfg.static_crt(a); } @@ -62,31 +62,30 @@ fn new_cc_build(build: &Build, target: TargetSelection) -> cc::Build { cfg } -/// Probes for C and C++ compilers and configures the corresponding entries in the [`Build`] +/// Probes for C and C++ compilers and configures the corresponding entries in the [`Session`] /// structure. /// /// This function determines which targets need a C compiler (and, if needed, a C++ compiler) /// by combining the primary build target, host targets, and any additional targets. For /// each target, it calls [`fill_target_compiler`] to configure the necessary compiler tools. -pub fn fill_compilers(build: &mut Build) { - let mut targets: HashSet<_> = match build.config.cmd { +pub(crate) fn fill_compilers(sess: &mut Session) { + let mut targets: HashSet<_> = match sess.config.cmd { // We don't need to check cross targets for these commands. Subcommand::Clean { .. } | Subcommand::Check { .. } | Subcommand::Format { .. } | Subcommand::Setup { .. } => { - build.hosts.iter().cloned().chain(iter::once(build.host_target)).collect() + sess.hosts.iter().cloned().chain(iter::once(sess.host_target)).collect() } _ => { // For all targets we're going to need a C compiler for building some shims // and such as well as for being a linker for Rust code. - build - .targets + sess.targets .iter() - .chain(&build.hosts) + .chain(&sess.hosts) .cloned() - .chain(iter::once(build.host_target)) + .chain(iter::once(sess.host_target)) .collect() } }; @@ -94,12 +93,12 @@ pub fn fill_compilers(build: &mut Build) { // When we intend to build wasm proc macros, we'll need to detect a toolchain for linking those // as well. In the future it would be good to make this a no-op given that we shouldn't need to // build any C/C++ code for wasm... - if build.config.wasm_proc_macros { + if sess.config.wasm_proc_macros { targets.insert(TargetSelection::from_user("wasm32-wasip2")); } for target in targets { - fill_target_compiler(build, target); + fill_target_compiler(sess, target); } } @@ -108,12 +107,12 @@ pub fn fill_compilers(build: &mut Build) { /// This function uses both user-specified configuration (from `bootstrap.toml`) and auto-detection /// logic to determine the correct C/C++ compilers for the target. It also determines the appropriate /// archiver (`ar`) and sets up additional compilation flags (both handled and unhandled). -pub fn fill_target_compiler(build: &mut Build, target: TargetSelection) { - let mut cfg = new_cc_build(build, target); - let config = build.config.target_config.get(&target); +fn fill_target_compiler(sess: &mut Session, target: TargetSelection) { + let mut cfg = new_cc_build(sess, target); + let config = sess.config.target_config.get(&target); if let Some(cc) = config .and_then(|c| c.cc.clone()) - .or_else(|| default_compiler(&cfg, Language::C, target, build)) + .or_else(|| default_compiler(&cfg, Language::C, target, sess)) { cfg.compiler(cc); } @@ -123,17 +122,17 @@ pub fn fill_target_compiler(build: &mut Build, target: TargetSelection) { .and_then(|c| c.ar.clone()) .or_else(|| cfg.try_get_archiver().map(|c| PathBuf::from(c.get_program())).ok()); - build.cc.insert(target, compiler.clone()); - let mut cflags = build.cc_handled_cflags(target, CLang::C); - cflags.extend(build.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::C)); + sess.cc.insert(target, compiler.clone()); + let mut cflags = sess.cc_handled_cflags(target, CLang::C); + cflags.extend(sess.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::C)); // If we use llvm-libunwind, we will need a C++ compiler as well for all targets // We'll need one anyways if the target triple is also a host triple - let mut cfg = new_cc_build(build, target); + let mut cfg = new_cc_build(sess, target); cfg.cpp(true); let cxx_configured = if let Some(cxx) = config .and_then(|c| c.cxx.clone()) - .or_else(|| default_compiler(&cfg, Language::CPlusPlus, target, build)) + .or_else(|| default_compiler(&cfg, Language::CPlusPlus, target, sess)) { cfg.compiler(cxx); true @@ -145,24 +144,24 @@ pub fn fill_target_compiler(build: &mut Build, target: TargetSelection) { // for VxWorks, record CXX compiler which will be used in lib.rs:linker() if cxx_configured || target.contains("vxworks") { let compiler = cfg.get_compiler(); - build.cxx.insert(target, compiler); + sess.cxx.insert(target, compiler); } - build.do_if_verbose(|| println!("CC_{} = {:?}", target.triple, build.cc(target))); - build.do_if_verbose(|| println!("CFLAGS_{} = {cflags:?}", target.triple)); - if let Ok(cxx) = build.cxx(target) { - let mut cxxflags = build.cc_handled_cflags(target, CLang::Cxx); - cxxflags.extend(build.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::Cxx)); - build.do_if_verbose(|| println!("CXX_{} = {cxx:?}", target.triple)); - build.do_if_verbose(|| println!("CXXFLAGS_{} = {cxxflags:?}", target.triple)); + sess.do_if_verbose(|| println!("CC_{} = {:?}", target.triple, sess.cc(target))); + sess.do_if_verbose(|| println!("CFLAGS_{} = {cflags:?}", target.triple)); + if let Ok(cxx) = sess.cxx(target) { + let mut cxxflags = sess.cc_handled_cflags(target, CLang::Cxx); + cxxflags.extend(sess.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::Cxx)); + sess.do_if_verbose(|| println!("CXX_{} = {cxx:?}", target.triple)); + sess.do_if_verbose(|| println!("CXXFLAGS_{} = {cxxflags:?}", target.triple)); } if let Some(ar) = ar { - build.do_if_verbose(|| println!("AR_{} = {ar:?}", target.triple)); - build.ar.insert(target, ar); + sess.do_if_verbose(|| println!("AR_{} = {ar:?}", target.triple)); + sess.ar.insert(target, ar); } if let Some(ranlib) = config.and_then(|c| c.ranlib.clone()) { - build.ranlib.insert(target, ranlib); + sess.ranlib.insert(target, ranlib); } } @@ -172,14 +171,14 @@ fn default_compiler( cfg: &cc::Build, compiler: Language, target: TargetSelection, - build: &Build, + sess: &Session, ) -> Option { match &*target.triple { // When compiling for android we may have the NDK configured in the // bootstrap.toml in which case we look there. Otherwise the default // compiler already takes into account the triple in question. t if t.contains("android") => { - build.config.android_ndk.as_ref().map(|ndk| ndk_compiler(compiler, &target.triple, ndk)) + sess.config.android_ndk.as_ref().map(|ndk| ndk_compiler(compiler, &target.triple, ndk)) } // The default gcc version from OpenBSD may be too old, try using egcc, @@ -192,14 +191,14 @@ fn default_compiler( } let mut cmd = BootstrapCommand::from(c.to_command()); - let output = cmd.arg("--version").run_capture_stdout(build).stdout(); + let output = cmd.arg("--version").run_capture_stdout(sess).stdout(); let i = output.find(" 4.")?; match output[i + 3..].chars().next().unwrap() { '0'..='6' => {} _ => return None, } let alternative = format!("e{gnu_compiler}"); - if command(&alternative).run_capture(build).is_success() { + if command(&alternative).run_capture(sess).is_success() { Some(PathBuf::from(alternative)) } else { None @@ -222,7 +221,7 @@ fn default_compiler( } t if t.contains("musl") && compiler == Language::C => { - if let Some(root) = build.musl_root(target) { + if let Some(root) = sess.musl_root(target) { let guess = root.join("bin/musl-gcc"); if guess.exists() { Some(guess) } else { None } } else { @@ -231,10 +230,10 @@ fn default_compiler( } t if t.contains("-wasi") => { - let root = if let Some(path) = build.wasi_sdk_path.as_ref() { + let root = if let Some(path) = sess.wasi_sdk_path.as_ref() { path } else { - if build.config.is_running_on_ci() { + if sess.config.is_running_on_ci() { panic!("ERROR: WASI_SDK_PATH must be configured for a -wasi target on CI"); } println!("WARNING: WASI_SDK_PATH not set, using default cc/cxx compiler"); diff --git a/src/bootstrap/src/utils/cc_detect/tests.rs b/src/bootstrap/src/utils/cc_detect/tests.rs index 716407cb0cb1c..861c9953b52d1 100644 --- a/src/bootstrap/src/utils/cc_detect/tests.rs +++ b/src/bootstrap/src/utils/cc_detect/tests.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use super::*; use crate::core::config::{Target, TargetSelection}; -use crate::core::session::Build; +use crate::core::session::Session; use crate::utils::tests::TestCtx; #[test] @@ -70,9 +70,9 @@ fn test_language_clang() { #[test] fn test_new_cc_build() { let config = TestCtx::new().config("build").create_config(); - let build = Build::new(config); + let sess = Session::new(config); let target = TargetSelection::from_user("x86_64-unknown-linux-gnu"); - let cfg = new_cc_build(&build, target.clone()); + let cfg = new_cc_build(&sess, target.clone()); let compiler = cfg.get_compiler(); assert!(!compiler.path().to_str().unwrap().is_empty(), "Compiler path should not be empty"); } @@ -80,13 +80,13 @@ fn test_new_cc_build() { #[test] fn test_default_compiler_wasi() { let config = TestCtx::new().config("build").create_config(); - let mut build = Build::new(config); + let mut sess = Session::new(config); let target = TargetSelection::from_user("wasm32-wasi"); let wasi_sdk = PathBuf::from("/wasi-sdk"); - build.wasi_sdk_path = Some(wasi_sdk.clone()); + sess.wasi_sdk_path = Some(wasi_sdk.clone()); let cfg = cc::Build::new(); - if let Some(result) = default_compiler(&cfg, Language::C, target.clone(), &build) { + if let Some(result) = default_compiler(&cfg, Language::C, target.clone(), &sess) { let expected = { let compiler = format!("{}-clang", target.triple); wasi_sdk.join("bin").join(compiler) @@ -102,59 +102,59 @@ fn test_default_compiler_wasi() { #[test] fn test_default_compiler_fallback() { let config = TestCtx::new().config("build").create_config(); - let build = Build::new(config); + let sess = Session::new(config); let target = TargetSelection::from_user("x86_64-unknown-linux-gnu"); let cfg = cc::Build::new(); - let result = default_compiler(&cfg, Language::C, target, &build); + let result = default_compiler(&cfg, Language::C, target, &sess); assert!(result.is_none(), "default_compiler should return None for generic targets"); } #[test] fn test_find_target_with_config() { let config = TestCtx::new().config("build").create_config(); - let mut build = Build::new(config); + let mut sess = Session::new(config); let target = TargetSelection::from_user("x86_64-unknown-linux-gnu"); let mut target_config = Target::default(); target_config.cc = Some(PathBuf::from("dummy-cc")); target_config.cxx = Some(PathBuf::from("dummy-cxx")); target_config.ar = Some(PathBuf::from("dummy-ar")); target_config.ranlib = Some(PathBuf::from("dummy-ranlib")); - build.config.target_config.insert(target.clone(), target_config); - fill_target_compiler(&mut build, target.clone()); - let cc_tool = build.cc.get(&target).unwrap(); + sess.config.target_config.insert(target.clone(), target_config); + fill_target_compiler(&mut sess, target.clone()); + let cc_tool = sess.cc.get(&target).unwrap(); assert_eq!(cc_tool.path(), &PathBuf::from("dummy-cc")); - let cxx_tool = build.cxx.get(&target).unwrap(); + let cxx_tool = sess.cxx.get(&target).unwrap(); assert_eq!(cxx_tool.path(), &PathBuf::from("dummy-cxx")); - let ar = build.ar.get(&target).unwrap(); + let ar = sess.ar.get(&target).unwrap(); assert_eq!(ar, &PathBuf::from("dummy-ar")); - let ranlib = build.ranlib.get(&target).unwrap(); + let ranlib = sess.ranlib.get(&target).unwrap(); assert_eq!(ranlib, &PathBuf::from("dummy-ranlib")); } #[test] fn test_find_target_without_config() { let config = TestCtx::new().config("build").create_config(); - let mut build = Build::new(config); + let mut sess = Session::new(config); let target = TargetSelection::from_user("x86_64-unknown-linux-gnu"); - build.config.target_config.clear(); - fill_target_compiler(&mut build, target.clone()); - assert!(build.cc.contains_key(&target)); + sess.config.target_config.clear(); + fill_target_compiler(&mut sess, target.clone()); + assert!(sess.cc.contains_key(&target)); if !target.triple.contains("vxworks") { - assert!(build.cxx.contains_key(&target)); + assert!(sess.cxx.contains_key(&target)); } - assert!(build.ar.contains_key(&target)); + assert!(sess.ar.contains_key(&target)); } #[test] fn test_find() { let config = TestCtx::new().config("build").create_config(); - let mut build = Build::new(config); + let mut sess = Session::new(config); let target1 = TargetSelection::from_user("x86_64-unknown-linux-gnu"); let target2 = TargetSelection::from_user("x86_64-unknown-openbsd"); - build.targets.push(target1.clone()); - build.hosts.push(target2.clone()); - fill_compilers(&mut build); - for t in build.hosts.iter().chain(build.targets.iter()).chain(iter::once(&build.host_target)) { - assert!(build.cc.contains_key(t), "CC not set for target {}", t.triple); + sess.targets.push(target1.clone()); + sess.hosts.push(target2.clone()); + fill_compilers(&mut sess); + for t in sess.hosts.iter().chain(sess.targets.iter()).chain(iter::once(&sess.host_target)) { + assert!(sess.cc.contains_key(t), "CC not set for target {}", t.triple); } } diff --git a/src/bootstrap/src/utils/channel.rs b/src/bootstrap/src/utils/channel.rs index ebb40edf9b262..0663bd0bf5215 100644 --- a/src/bootstrap/src/utils/channel.rs +++ b/src/bootstrap/src/utils/channel.rs @@ -10,7 +10,7 @@ use std::path::Path; use super::exec::ExecutionContext; use super::helpers; -use crate::core::session::Build; +use crate::core::session::Session; use crate::utils::helpers::t; #[derive(Clone, Default)] @@ -111,8 +111,8 @@ impl GitInfo { self.info().map(|s| &s.commit_date[..]) } - pub fn version(&self, build: &Build, num: &str) -> String { - let mut version = build.release(num); + pub fn version(&self, sess: &Session, num: &str) -> String { + let mut version = sess.release(num); if let Some(inner) = self.info() { version.push_str(" ("); version.push_str(&inner.short_sha); diff --git a/src/bootstrap/src/utils/job.rs b/src/bootstrap/src/utils/job.rs index 942ac6c80e4ee..45cff7c716ba9 100644 --- a/src/bootstrap/src/utils/job.rs +++ b/src/bootstrap/src/utils/job.rs @@ -1,14 +1,13 @@ #[cfg(windows)] -pub use for_windows::*; - -use crate::core::session::Build; +pub(crate) use self::for_windows::setup; +use crate::core::session::Session; #[cfg(any(target_os = "haiku", target_os = "hermit", not(any(unix, windows))))] -pub unsafe fn setup(_build: &mut Build) {} +pub(crate) unsafe fn setup(_sess: &Session) {} #[cfg(all(unix, not(target_os = "haiku")))] -pub unsafe fn setup(build: &mut Build) { - if build.config.low_priority { +pub(crate) unsafe fn setup(sess: &Session) { + if sess.config.low_priority { unsafe { libc::setpriority(libc::PRIO_PGRP as _, 0, 10); } @@ -60,7 +59,7 @@ mod for_windows { use windows::Win32::System::Threading::{BELOW_NORMAL_PRIORITY_CLASS, GetCurrentProcess}; use windows::core::PCWSTR; - pub unsafe fn setup(build: &mut super::Build) { + pub(crate) unsafe fn setup(sess: &super::Session) { // SAFETY: pretty much everything below is unsafe unsafe { // Enable the Windows Error Reporting dialog which msys disables, @@ -77,7 +76,7 @@ mod for_windows { // children will reside in the job by default. let mut info = JOBOBJECT_EXTENDED_LIMIT_INFORMATION::default(); info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; - if build.config.low_priority { + if sess.config.low_priority { info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PRIORITY_CLASS; info.BasicLimitInformation.PriorityClass = BELOW_NORMAL_PRIORITY_CLASS.0; } diff --git a/src/bootstrap/src/utils/metrics.rs b/src/bootstrap/src/utils/metrics.rs index a309b1d53b8e9..79d967452d0c5 100644 --- a/src/bootstrap/src/utils/metrics.rs +++ b/src/bootstrap/src/utils/metrics.rs @@ -17,7 +17,7 @@ use build_helper::metrics::{ use sysinfo::{CpuRefreshKind, RefreshKind, System}; use crate::core::builder::{Builder, Step}; -use crate::core::session::Build; +use crate::core::session::Session; use crate::utils::helpers::t; // Update this number whenever a breaking change is made to the build metrics. @@ -156,11 +156,11 @@ impl BuildMetrics { step.cpu_usage_time_sec += cpu as f64 / 100.0 * elapsed.as_secs_f64(); } - pub(crate) fn persist(&self, build: &Build) { + pub(crate) fn persist(&self, sess: &Session) { let mut state = self.state.borrow_mut(); assert!(state.running_steps.is_empty(), "steps are still executing"); - let dest = build.out.join("metrics.json"); + let dest = sess.out.join("metrics.json"); let mut system = System::new_with_specifics( RefreshKind::nothing().with_cpu(CpuRefreshKind::everything()), @@ -222,7 +222,7 @@ impl BuildMetrics { format_version: CURRENT_FORMAT_VERSION, system_stats, invocations, - ci_metadata: get_ci_metadata(build.config.ci_env), + ci_metadata: get_ci_metadata(sess.config.ci_env), }; t!(std::fs::create_dir_all(dest.parent().unwrap())); diff --git a/src/tools/rust-analyzer/.github/workflows/metrics.yaml b/src/tools/rust-analyzer/.github/workflows/metrics.yaml index a482235105c04..51a12386088ac 100644 --- a/src/tools/rust-analyzer/.github/workflows/metrics.yaml +++ b/src/tools/rust-analyzer/.github/workflows/metrics.yaml @@ -91,35 +91,11 @@ jobs: - name: Checkout repository uses: actions/checkout@v6 - - name: Download build metrics + - name: Download metrics uses: actions/download-artifact@v8 with: - name: build-${{ github.sha }} - - - name: Download self metrics - uses: actions/download-artifact@v8 - with: - name: self-${{ github.sha }} - - - name: Download ripgrep-13.0.0 metrics - uses: actions/download-artifact@v8 - with: - name: ripgrep-13.0.0-${{ github.sha }} - - - name: Download webrender-2022 metrics - uses: actions/download-artifact@v8 - with: - name: webrender-2022-${{ github.sha }} - - - name: Download diesel-1.4.8 metrics - uses: actions/download-artifact@v8 - with: - name: diesel-1.4.8-${{ github.sha }} - - - name: Download hyper-0.14.18 metrics - uses: actions/download-artifact@v8 - with: - name: hyper-0.14.18-${{ github.sha }} + pattern: '*-${{ github.sha }}' + merge-multiple: true - name: Combine json run: | diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml index 50af766db3cbd..7d6e0199abe0b 100644 --- a/src/tools/rust-analyzer/.github/workflows/release.yaml +++ b/src/tools/rust-analyzer/.github/workflows/release.yaml @@ -40,7 +40,6 @@ jobs: - os: ubuntu-latest target: x86_64-unknown-linux-gnu # Use a container with glibc 2.28 - # Zig is not used because it doesn't work with PGO container: quay.io/pypa/manylinux_2_28_x86_64 code-target: linux-x64 allocator: system @@ -200,15 +199,9 @@ jobs: publish: if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event_name == 'workflow_dispatch' }} - name: publish runs-on: ubuntu-latest needs: ["dist", "dist-x86_64-unknown-linux-musl"] steps: - - name: Install Nodejs - uses: actions/setup-node@v6 - with: - node-version: 22 - - name: Checkout repository uses: actions/checkout@v6 with: @@ -237,40 +230,9 @@ jobs: - uses: actions/download-artifact@v8 with: - name: dist-aarch64-apple-darwin - path: dist - - uses: actions/download-artifact@v8 - with: - name: dist-x86_64-apple-darwin - path: dist - - uses: actions/download-artifact@v8 - with: - name: dist-x86_64-unknown-linux-gnu - path: dist - - uses: actions/download-artifact@v8 - with: - name: dist-x86_64-unknown-linux-musl - path: dist - - uses: actions/download-artifact@v8 - with: - name: dist-aarch64-unknown-linux-gnu - path: dist - - uses: actions/download-artifact@v8 - with: - name: dist-arm-unknown-linux-gnueabihf - path: dist - - uses: actions/download-artifact@v8 - with: - name: dist-x86_64-pc-windows-msvc - path: dist - - uses: actions/download-artifact@v8 - with: - name: dist-i686-pc-windows-msvc - path: dist - - uses: actions/download-artifact@v8 - with: - name: dist-aarch64-pc-windows-msvc + pattern: dist-* path: dist + merge-multiple: true - run: ls -al ./dist - name: Publish Release @@ -280,28 +242,41 @@ jobs: name: ${{ env.TAG }} token: ${{ secrets.GITHUB_TOKEN }} - - run: npm ci - working-directory: ./editors/code + publish-extension: + if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event_name == 'workflow_dispatch' }} + name: publish-extension (${{ matrix.cmd }}) + runs-on: ubuntu-latest + needs: publish + strategy: + fail-fast: false + matrix: + include: + - cmd: vsce + pat: MARKETPLACE_TOKEN + - cmd: ovsx + pat: OPENVSX_TOKEN + steps: + - name: Install Nodejs + uses: actions/setup-node@v6 + with: + node-version: 22 - - name: Publish Extension (Code Marketplace, release) - if: github.ref == 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer' - working-directory: ./editors/code - # token from https://dev.azure.com/rust-analyzer/ - run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: ${{ env.FETCH_DEPTH }} - - name: Publish Extension (OpenVSX, release) - if: github.ref == 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer' - working-directory: ./editors/code - run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix - timeout-minutes: 2 + - uses: actions/download-artifact@v8 + with: + pattern: dist-* + path: dist + merge-multiple: true - - name: Publish Extension (Code Marketplace, nightly) - if: github.ref != 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer' + - run: npm ci working-directory: ./editors/code - run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix --pre-release - - name: Publish Extension (OpenVSX, nightly) - if: github.ref != 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer' + - name: Publish Extension + if: github.repository == 'rust-lang/rust-analyzer' working-directory: ./editors/code - run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix + run: npx ${{ matrix.cmd }} publish --skip-duplicate --pat ${{ secrets[matrix.pat] }} --packagePath ../../dist/rust-analyzer-*.vsix ${{ github.ref != 'refs/heads/release' && '--pre-release' || '' }} timeout-minutes: 2 diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index d1432a481a35b..983cfaf992dfe 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -3095,7 +3095,6 @@ name = "tt" version = "0.0.0" dependencies = [ "arrayvec", - "indexmap", "intern", "ra-ap-rustc_lexer", "rustc-hash 2.1.2", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index d043a3aee4f7f..b798e364555df 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -104,7 +104,7 @@ lsp-server = { version = "0.7.9" } anyhow = "1.0.98" arrayvec = "0.7.6" bitflags = "2.9.1" -cargo_metadata = "0.23.0" +cargo_metadata = "0.23.1" camino = "1.2.2" crossbeam-channel = "0.5.15" dissimilar = "1.0.10" diff --git a/src/tools/rust-analyzer/crates/cfg/Cargo.toml b/src/tools/rust-analyzer/crates/cfg/Cargo.toml index 15de1f329385d..7759fc4ae1948 100644 --- a/src/tools/rust-analyzer/crates/cfg/Cargo.toml +++ b/src/tools/rust-analyzer/crates/cfg/Cargo.toml @@ -28,10 +28,9 @@ arbitrary = { version = "1.4.1", features = ["derive"] } # local deps syntax-bridge.workspace = true -syntax.workspace = true -# tt is needed for testing -cfg = { path = ".", default-features = false, features = ["tt"] } +# tt and syntax are needed for testing +cfg = { path = ".", default-features = false, features = ["tt", "syntax"] } [features] default = [] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index 46cdb39c5b46b..7120980dd30cf 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -132,13 +132,23 @@ fn test_env_expand() { #[rustc_builtin_macro] macro_rules! env {() => {}} -fn main() { env!("TEST_ENV_VAR"); } +fn main() { + env!("TEST_ENV_VAR"); + env!("TEST_ENV_VAR",); + env!("TEST_ENV_VAR", "error"); + env!("TEST_ENV_VAR", "error",); +} "#, expect![[r##" #[rustc_builtin_macro] macro_rules! env {() => {}} -fn main() { "UNRESOLVED_ENV_VAR"; } +fn main() { + "UNRESOLVED_ENV_VAR"; + "UNRESOLVED_ENV_VAR"; + "UNRESOLVED_ENV_VAR"; + "UNRESOLVED_ENV_VAR"; +} "##]], ); } @@ -150,13 +160,21 @@ fn test_option_env_expand() { #[rustc_builtin_macro] macro_rules! option_env {() => {}} -fn main() { option_env!("TEST_ENV_VAR"); } +fn main() { + option_env!("TEST_ENV_VAR"); + option_env!("TEST_ENV_VAR",); + option_env!("TEST_ENV_VAR", "invalid"); +} "#, expect![[r#" #[rustc_builtin_macro] macro_rules! option_env {() => {}} -fn main() { $crate::option::Option::None:: < &str>; } +fn main() { + $crate::option::Option::None:: < &str>; + $crate::option::Option::None:: < &str>; + /* error: unexpected input */; +} "#]], ); } 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 b173f44f34ab1..44579304d3646 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 @@ -766,8 +766,32 @@ fn relative_file( } } -fn parse_string(tt: &tt::TopSubtree) -> Result<(Symbol, Span), ExpandError> { - let mut tt = TtElement::Subtree(tt.top_subtree(), tt.iter()); +fn parse_string(tt: &tt::TopSubtree, allow_rest_args: bool) -> Result<(Symbol, Span), ExpandError> { + let expect_literal = |span| ExpandError::other(span, "expected string literal"); + let mut tt = { + let mut tt_iter = tt.iter(); + let extracted = + tt_iter.next().ok_or_else(|| expect_literal(tt.top_subtree().delimiter.close))?; + + match tt_iter.next() { + None => {} + Some(TtElement::Leaf(tt::Leaf::Punct(it))) if it.char == ',' => { + // Tail comma + // FIXME: Ignored like env!("NAME", "compile_error message") + if let Some(tt) = tt_iter.next() + && !allow_rest_args + { + return Err(ExpandError::other(tt.first_span(), "unexpected input")); + } + } + Some(tt) => { + return Err(ExpandError::other(tt.first_span(), "unexpected input")); + } + } + + extracted + }; + (|| { // FIXME: We wrap expression fragments in parentheses which can break this expectation // here @@ -795,7 +819,7 @@ fn parse_string(tt: &tt::TopSubtree) -> Result<(Symbol, Span), ExpandError> { TtElement::Subtree(tt, _) => Err(tt.delimiter.open.cover(tt.delimiter.close)), } })() - .map_err(|span| ExpandError::other(span, "expected string literal")) + .map_err(expect_literal) } fn include_expand( @@ -827,7 +851,7 @@ pub fn include_input_to_file_id( arg_id: MacroCallId, arg: &tt::TopSubtree, ) -> Result { - let (s, span) = parse_string(arg)?; + let (s, span) = parse_string(arg, false)?; relative_file(db, arg_id, s.as_str(), false, span) } @@ -851,7 +875,7 @@ fn include_str_expand( tt: &tt::TopSubtree, call_site: Span, ) -> ExpandResult { - let (path, input_span) = match parse_string(tt) { + let (path, input_span) = match parse_string(tt, false) { Ok(it) => it, Err(e) => { return ExpandResult::new( @@ -889,7 +913,7 @@ fn env_expand( tt: &tt::TopSubtree, span: Span, ) -> ExpandResult { - let (key, span) = match parse_string(tt) { + let (key, span) = match parse_string(tt, true) { Ok(it) => it, Err(e) => { return ExpandResult::new( @@ -927,7 +951,7 @@ fn option_env_expand( tt: &tt::TopSubtree, call_site: Span, ) -> ExpandResult { - let (key, span) = match parse_string(tt) { + let (key, span) = match parse_string(tt, false) { Ok(it) => it, Err(e) => { return ExpandResult::new( diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs index 939104b709163..8781358822064 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs @@ -419,9 +419,11 @@ mod tests { } fn check_subtree_eq(a: &tt::TopSubtree, b: &tt::TopSubtree) -> bool { - let a = a.view().as_token_trees().iter_flat_tokens(); - let b = b.view().as_token_trees().iter_flat_tokens(); - a.len() == b.len() && std::iter::zip(a, b).all(|(a, b)| check_tt_eq(&a, &b)) + let a = a.view().as_token_trees(); + let b = b.view().as_token_trees(); + a.len() == b.len() + && std::iter::zip(a.iter_flat_tokens(), b.iter_flat_tokens()) + .all(|(a, b)| check_tt_eq(&a, &b)) } fn check_tt_eq(a: &tt::TokenTree, b: &tt::TokenTree) -> bool { 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 3f499a9b5a2f0..70539cf83673a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -47,7 +47,7 @@ use hir_def::{ TupleFieldId, TupleId, VariantId, attrs::AttrFlags, expr_store::{Body, ExpressionStore, HygieneId, body::Param, path::Path}, - hir::{BindingId, ExprId, ExprOrPatId, ExprOrPatIdPacked, LabelId, PatId}, + hir::{BindingId, ExprId, ExprOrPatId, ExprOrPatIdPacked, LabelId, PatId, UnaryOp}, lang_item::LangItems, layout::Integer, resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, @@ -435,6 +435,13 @@ pub enum InferenceDiagnostic { expr: ExprId, found: StoredTy, }, + UnaryOperatorCannotBeApplied { + #[type_visitable(ignore)] + expr: ExprId, + #[type_visitable(ignore)] + op: UnaryOp, + found: StoredTy, + }, MutRefInImmRefPat { #[type_visitable(ignore)] pat: PatId, 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 0446978dd996a..f247b517c541f 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 @@ -821,7 +821,7 @@ impl<'db> InferenceContext<'db> { } }; - let diverge = asm.options.contains(AsmOptions::NORETURN); + let mut diverge = asm.options.contains(AsmOptions::NORETURN); asm.operands.iter().for_each(|(_, operand)| match *operand { AsmOperand::In { expr, .. } => check_expr_asm_operand(self, expr, true), AsmOperand::Out { expr: Some(expr), .. } | AsmOperand::InOut { expr, .. } => { @@ -835,11 +835,19 @@ impl<'db> InferenceContext<'db> { } } AsmOperand::Label(expr) => { - self.infer_expr( + let previous_diverges = self.diverges; + // The label blocks should have unit return value or diverge. + let ty = self.infer_expr_inner( expr, &Expectation::HasType(self.types.types.unit), ExprIsRead::No, ); + if !ty.is_never() { + _ = self.demand_suptype(expr.into(), self.types.types.unit, ty); + diverge = false; + } + // We need this to avoid false unreachable warning when a label diverges. + self.diverges = previous_diverges; } AsmOperand::Const(expr) => { self.infer_expr(expr, &Expectation::None, ExprIsRead::No); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs index 5fd4e830fb172..16e62e9dace48 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs @@ -9,7 +9,7 @@ use syntax::ast::{ArithOp, BinaryOp, UnaryOp}; use tracing::debug; use crate::{ - Adjust, Adjustment, AutoBorrow, + Adjust, Adjustment, AutoBorrow, InferenceDiagnostic, infer::{AllowTwoPhase, AutoBorrowMutability, Expectation, InferenceContext, expr::ExprIsRead}, method_resolution::{MethodCallee, TreatNotYetDefinedOpaques}, next_solver::{ @@ -271,7 +271,11 @@ impl<'db> InferenceContext<'db> { method.sig.output() } Err(_errors) => { - // FIXME: Report diagnostic. + self.push_diagnostic(InferenceDiagnostic::UnaryOperatorCannotBeApplied { + expr: ex, + op, + found: operand_ty.store(), + }); self.types.types.error } } 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 7b589efba2f63..8070ed8788977 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 @@ -586,6 +586,7 @@ pub(super) mod resolve_completely { | InferenceDiagnostic::CannotIndexInto { found: ty, .. } | InferenceDiagnostic::ExpectedFunction { found: ty, .. } | InferenceDiagnostic::ExpectedArrayOrSlicePat { found: ty, .. } + | InferenceDiagnostic::UnaryOperatorCannotBeApplied { found: ty, .. } | InferenceDiagnostic::UnresolvedField { receiver: ty, .. } | InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic && ty.as_ref().references_non_lt_error() 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 47a960e300cc7..2ba6408c45f2c 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 @@ -16,6 +16,7 @@ use crate::{ db::HirDatabase, layout::{Layout, LayoutCx, LayoutError, field_ty}, next_solver::StoredGenericArgs, + representability::{Representability, representability}, traits::StoredParamEnvAndCrate, }; @@ -30,6 +31,9 @@ pub fn layout_of_adt_query( let Ok(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable); }; + if representability(db, def) == Representability::Infinite { + return Err(LayoutError::RecursiveTypeWithoutIndirection); + } let dl = target; let cx = LayoutCx::new(dl); let handle_variant = |def: VariantId, var: &VariantFields| { 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 72befc58a1236..5098b38c4380c 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 @@ -275,6 +275,13 @@ fn recursive() { struct BoxLike(*mut T); struct Goal(BoxLike); } + size_and_align! { + struct Foo { + x: *const Foo<[T; 1]>, + y: *const T, + } + struct Goal(Foo); + } check_fail(r#"struct Goal(Goal);"#, LayoutError::RecursiveTypeWithoutIndirection); check_fail( r#" @@ -283,6 +290,18 @@ fn recursive() { "#, LayoutError::RecursiveTypeWithoutIndirection, ); + check_fail( + r#" +struct Foo { + x: Foo<[T; 1]>, + y: T, +} +struct Goal { + x: Foo, +} +"#, + LayoutError::RecursiveTypeWithoutIndirection, + ); } #[test] 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 3cdfe4edcb908..97921e8ab92d5 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 @@ -4162,6 +4162,27 @@ extern "C" fn foo() -> ! { ); } +#[test] +fn asm_label_can_diverge() { + check_no_mismatches( + r#" +//- minicore: asm +fn foo() { + loop { + unsafe { + core::arch::asm!( + "/* {} */", + label { + break; + } + ); + } + } +} + "#, + ); +} + #[test] fn regression_21478() { check_infer( diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 3c268d051a19c..9a2f1fd11d6a4 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -105,6 +105,7 @@ diagnostics![AnyDiagnostic<'db> -> AwaitOutsideOfAsync, BreakOutsideOfLoop, CannotBeDereferenced<'db>, + UnaryOperatorCannotBeApplied<'db>, CannotImplicitlyDerefTraitObject<'db>, CannotIndexInto<'db>, CastToUnsized<'db>, @@ -338,6 +339,13 @@ pub struct CannotBeDereferenced<'db> { pub found: Type<'db>, } +#[derive(Debug)] +pub struct UnaryOperatorCannotBeApplied<'db> { + pub expr: InFile, + pub op: ast::UnaryOp, + pub found: Type<'db>, +} + #[derive(Debug)] pub struct MutRefInImmRefPat { pub pat: InFile, @@ -985,6 +993,10 @@ impl<'db> AnyDiagnostic<'db> { let expr = expr_syntax(*expr)?; CannotBeDereferenced { expr, found: new_ty(found.as_ref()) }.into() } + InferenceDiagnostic::UnaryOperatorCannotBeApplied { expr, op, found } => { + let expr = expr_syntax(*expr)?; + UnaryOperatorCannotBeApplied { expr, op: *op, found: new_ty(found.as_ref()) }.into() + } InferenceDiagnostic::MutRefInImmRefPat { pat } => { let pat = pat_syntax(*pat)?.map(Into::into); MutRefInImmRefPat { pat }.into() 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 209091683a01b..907193fe1d7ff 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -464,7 +464,7 @@ impl<'db> SourceAnalyzer<'db> { db, &self.resolver, self.store()?, - generic_def.into(), + self.resolver.expression_store_owner().unwrap_or_else(|| generic_def.into()), generic_def, &generics, // FIXME: Is this correct here? Anyway that should impact mostly diagnostics, which we don't emit here @@ -1890,7 +1890,7 @@ fn resolve_hir_path_<'db>( db, resolver, store?, - def.into(), + resolver.expression_store_owner().unwrap_or_else(|| def.into()), def, &generics, LifetimeElisionKind::Infer, @@ -2094,7 +2094,7 @@ fn resolve_hir_path_qualifier<'db>( db, resolver, store, - def.into(), + resolver.expression_store_owner().unwrap_or_else(|| def.into()), def, &generics, LifetimeElisionKind::Infer, 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 774e14df48340..fb6a747a64803 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 @@ -118,6 +118,9 @@ pub(crate) fn complete_dot( ctx: dot_access.ctx, }; complete_methods(ctx, &iter, &traits_in_scope, |func| { + if func.name(ctx.db) == hir::sym::into_iter { + return; + } acc.add_method(ctx, &dot_access, func, Some(iter_sym.clone()), None) }); } @@ -1681,7 +1684,6 @@ fn foo() { expect![[r#" me into_iter() (as IntoIterator) fn(self) -> ::IntoIter me into_iter().by_ref() (as Iterator) fn(&mut self) -> &mut Self - me into_iter().into_iter() (as IntoIterator) fn(self) -> ::IntoIter me into_iter().next() (as Iterator) fn(&mut self) -> Option<::Item> me into_iter().nth(…) (as Iterator) fn(&mut self, usize) -> Option<::Item> "#]], @@ -1715,7 +1717,6 @@ fn foo() { me into_iter() (as IntoIterator) fn(self) -> ::IntoIter me iter() fn(&self) -> Iter me iter().by_ref() (as Iterator) fn(&mut self) -> &mut Self - me iter().into_iter() (as IntoIterator) fn(self) -> ::IntoIter me iter().next() (as Iterator) fn(&mut self) -> Option<::Item> me iter().nth(…) (as Iterator) fn(&mut self, usize) -> Option<::Item> "#]], diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs index fb9095e0f4bd2..a577353e27cf0 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs @@ -488,6 +488,7 @@ fn main() { fn legacy_const_generics() { check_diagnostics( r#" +//- minicore: unary_ops, builtin_impls #[rustc_legacy_const_generics(1, 3)] fn mixed( _a: u8, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unary_operator_cannot_be_applied.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unary_operator_cannot_be_applied.rs new file mode 100644 index 0000000000000..1dc779d43ac0f --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unary_operator_cannot_be_applied.rs @@ -0,0 +1,158 @@ +use hir::HirDisplay; +use syntax::ast::UnaryOp; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: unary-operator-cannot-be-applied +// +// This diagnostic is triggered if a unary operator (`!` or `-`) is applied +// to a value whose type does not implement the corresponding trait +// (`Not` or `Neg`). +pub(crate) fn unary_operator_cannot_be_applied( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::UnaryOperatorCannotBeApplied<'_>, +) -> Diagnostic { + let op = match d.op { + UnaryOp::Not => "!", + UnaryOp::Neg => "-", + // `Deref` uses a different diagnostic (`CannotBeDereferenced`). + UnaryOp::Deref => "*", + }; + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0600"), + format!( + "cannot apply unary operator `{op}` to 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 not_on_enum() { + check_diagnostics( + r#" +//- minicore: unary_ops, builtin_impls +enum Question { Yes, No } + +fn f() { + let _ = !Question::Yes; + //^^^^^^^^^^^^^^ error: cannot apply unary operator `!` to type `Question` +} +"#, + ); + } + + #[test] + fn neg_on_struct() { + check_diagnostics( + r#" +//- minicore: unary_ops, builtin_impls +struct S; + +fn f() { + let _ = -S; + //^^ error: cannot apply unary operator `-` to type `S` +} +"#, + ); + } + + #[test] + fn allows_not_on_bool() { + check_diagnostics( + r#" +//- minicore: unary_ops, builtin_impls +fn f() { + let _ = !true; + let _ = !false; +} +"#, + ); + } + + #[test] + fn allows_not_on_integer() { + check_diagnostics( + r#" +//- minicore: unary_ops, builtin_impls +fn f() { + let _ = !0u32; + let _ = !0i32; +} +"#, + ); + } + + #[test] + fn allows_neg_on_numeric() { + check_diagnostics( + r#" +//- minicore: unary_ops, builtin_impls +fn f() { + let _ = -1i32; + let _ = -1.0f64; +} +"#, + ); + } + + #[test] + fn neg_on_unsigned() { + check_diagnostics( + r#" +//- minicore: unary_ops, builtin_impls +fn f() { + let _ = -1u32; + //^^^^^ error: cannot apply unary operator `-` to type `u32` +} +"#, + ); + } + + #[test] + fn allows_not_with_impl() { + check_diagnostics( + r#" +//- minicore: unary_ops, builtin_impls +struct Bar; +struct Foo; + +impl core::ops::Not for Bar { + type Output = Foo; + fn not(self) -> Foo { Foo } +} + +fn f() { + let _ = !Bar; +} +"#, + ); + } + + #[test] + fn allows_neg_with_impl() { + check_diagnostics( + r#" +//- minicore: unary_ops, builtin_impls +struct Bar; +struct Foo; + +impl core::ops::Neg for Bar { + type Output = Foo; + fn neg(self) -> Foo { Foo } +} + +fn f() { + let _ = -Bar; +} +"#, + ); + } +} 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 8ba59edcbc871..5d816a8d41c3c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -85,6 +85,7 @@ mod handlers { pub(crate) mod type_mismatch; pub(crate) mod type_must_be_known; pub(crate) mod typed_hole; + pub(crate) mod unary_operator_cannot_be_applied; pub(crate) mod undeclared_label; pub(crate) mod unimplemented_builtin_macro; pub(crate) mod unimplemented_trait; @@ -437,6 +438,7 @@ pub fn semantic_diagnostics( 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::UnaryOperatorCannotBeApplied(d) => handlers::unary_operator_cannot_be_applied::unary_operator_cannot_be_applied(&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), diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs index d8c15cabb2fb8..8d1858ccf2226 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs @@ -72,18 +72,13 @@ pub(crate) fn single_file(code: &str) -> (ide_db::RootDatabase, FilePosition, Ve let (db, file_id) = ide_db::RootDatabase::with_single_file(code); (db, file_id, RangeOrOffset::Offset(0.into())) }; - let selections; - let position; - match range_or_offset { + + let (position, selections) = match range_or_offset { RangeOrOffset::Range(range) => { - position = FilePosition { file_id, offset: range.start() }; - selections = vec![FileRange { file_id, range }]; - } - RangeOrOffset::Offset(offset) => { - position = FilePosition { file_id, offset }; - selections = vec![]; + (FilePosition { file_id, offset: range.start() }, vec![FileRange { file_id, range }]) } - } + RangeOrOffset::Offset(offset) => (FilePosition { file_id, offset }, vec![]), + }; let mut local_roots = FxHashSet::default(); local_roots.insert(WORKSPACE); LocalRoots::get(&db).set_roots(&mut db).to(local_roots); diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs b/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs index 427a2eff82017..b43a23e69f37b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs @@ -87,6 +87,7 @@ mod tests { ); let refs = super::find_all_methods(&analysis.db, pos.file_id); + #[expect(clippy::single_range_in_vec_init, reason = "this is not a mistake")] check_result(&refs, &[28..=34]); } 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 4fea4468c0725..fad322aa4f5da 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -11948,3 +11948,25 @@ fn main() {} "#]], ); } + +#[test] +fn resolve_array_type_with_anon_const_panic() { + use syntax::AstNode; + let (analysis, position) = crate::fixture::position( + r#" +fn main() { + let x: [u8; 2 + 2$0] = [0; 4]; +} +"#, + ); + let db = &analysis.db; + hir::attach_db(db, || { + let sema = hir::Semantics::new(db); + let file = sema.parse_guess_edition(position.file_id); + let token = file.syntax().token_at_offset(position.offset).right_biased().unwrap(); + let type_node = token.parent_ancestors().find_map(syntax::ast::Type::cast).unwrap(); + + let resolved = sema.resolve_type(&type_node).unwrap(); + let _ = resolved.as_array(db); + }); +} diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol.rs b/src/tools/rust-analyzer/crates/intern/src/symbol.rs index 72d32d1017747..91fc11fa0dae9 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol.rs @@ -217,11 +217,12 @@ impl Symbol { shard.shrink_to(len, |(x, _)| Self::hash(storage, x.as_str())); } } -} -impl Drop for Symbol { + /// # Safety + /// + /// You must know that you have a `Symbol` instance that won't be dropped, so decreasing the refcount is valid. #[inline] - fn drop(&mut self) { + pub unsafe fn decrease_refcount(&mut self) { // SAFETY: We're dropping, we have ownership. let Some(arc) = (unsafe { self.repr.try_as_arc_owned() }) else { return; @@ -237,6 +238,15 @@ impl Drop for Symbol { } } +impl Drop for Symbol { + #[inline] + fn drop(&mut self) { + unsafe { + self.decrease_refcount(); + } + } +} + impl Clone for Symbol { fn clone(&self) -> Self { Self { repr: increase_arc_refcount(self.repr) } diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs index 76fdac097ff71..6de9b4275ce2d 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs @@ -442,7 +442,7 @@ pub fn expect_fragment<'t>( } let res = cursor.crossed(); - tt_iter.flat_advance(res.len()); + tt_iter.flat_advance_to(&cursor); ExpandResult { value: res, err } } 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 ae03be9aa7a26..b9b6247b54fab 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 @@ -54,7 +54,7 @@ pub type SpanDataIndexMap = pub fn serialize_span_data_index_map(map: &SpanDataIndexMap) -> Vec { map.iter() - .flat_map(|span| { + .map(|span| { [ span.anchor.file_id.as_u32(), span.anchor.ast_id.into_raw(), @@ -63,14 +63,16 @@ pub fn serialize_span_data_index_map(map: &SpanDataIndexMap) -> Vec { span.ctx.into_u32(), ] }) - .collect() + .collect::>() + .into_flattened() } pub fn deserialize_span_data_index_map(map: &[u32]) -> SpanDataIndexMap { - debug_assert!(map.len().is_multiple_of(5)); - map.chunks_exact(5) - .map(|span| { - let &[file_id, ast_id, start, end, e] = span else { unreachable!() }; + let (chunks, remainder) = map.as_chunks(); + assert!(remainder.is_empty()); + chunks + .iter() + .map(|&[file_id, ast_id, start, end, e]| { Span { anchor: SpanAnchor { file_id: EditionedFileId::from_raw(file_id), @@ -345,14 +347,13 @@ impl FlatTree { } fn read_vec T, const N: usize>(xs: Vec, f: F) -> Vec { - let mut chunks = xs.chunks_exact(N); - let res = chunks.by_ref().map(|chunk| f(chunk.try_into().unwrap())).collect(); - assert!(chunks.remainder().is_empty()); - res + let (chunks, remainder) = xs.as_chunks(); + assert!(remainder.is_empty()); + chunks.iter().map(|chunk| f(*chunk)).collect() } fn write_vec [u32; N], const N: usize>(xs: Vec, f: F) -> Vec { - xs.into_iter().flat_map(f).collect() + xs.into_iter().map(f).collect::>().into_flattened() } impl SubtreeRepr { 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 a8a409bd4686d..072a01c8298ec 100644 --- a/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs +++ b/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs @@ -30,28 +30,25 @@ impl MemoryUsage { allocated: Bytes(jemalloc_ctl::stats::allocated::read().unwrap() as isize), } } - all(target_os = "linux", target_env = "gnu") => { - memusage_linux() - } + all(target_os = "linux", target_env = "gnu") => memusage_linux(), 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. - use windows_sys::Win32::System::{Threading::*, ProcessStatus::*}; use std::mem::MaybeUninit; + use windows_sys::Win32::System::{ProcessStatus::*, Threading::*}; let proc = unsafe { GetCurrentProcess() }; let mut mem_counters = MaybeUninit::uninit(); let cb = size_of::(); - let ret = unsafe { GetProcessMemoryInfo(proc, mem_counters.as_mut_ptr(), cb as u32) }; + let ret = + unsafe { GetProcessMemoryInfo(proc, mem_counters.as_mut_ptr(), cb as u32) }; assert!(ret != 0); let usage = unsafe { mem_counters.assume_init().PagefileUsage }; MemoryUsage { allocated: Bytes(usage as isize) } } - _ => { - MemoryUsage { allocated: Bytes(0) } - } + _ => MemoryUsage { allocated: Bytes(0) }, } } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index a0a9815d8756f..9d2588380c22c 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -36,6 +36,7 @@ pub struct CargoWorkspace { targets: Arena, workspace_root: AbsPathBuf, target_directory: AbsPathBuf, + build_directory: Option, manifest_path: ManifestPath, is_virtual_workspace: bool, /// Whether this workspace represents the sysroot workspace. @@ -359,6 +360,7 @@ impl CargoWorkspace { let workspace_root = AbsPathBuf::assert(meta.workspace_root); let target_directory = AbsPathBuf::assert(meta.target_directory); + let build_directory = meta.build_directory.map(AbsPathBuf::assert); let mut is_virtual_workspace = true; let mut requires_rustc_private = false; @@ -517,6 +519,7 @@ impl CargoWorkspace { targets, workspace_root, target_directory, + build_directory, manifest_path: ws_manifest_path, is_virtual_workspace, requires_rustc_private, @@ -548,6 +551,10 @@ impl CargoWorkspace { &self.target_directory } + pub fn build_directory(&self) -> Option<&AbsPath> { + self.build_directory.as_deref() + } + pub fn package_flag(&self, package: &PackageData) -> String { if self.is_unique(&package.name) { package.name.clone() 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 99e640a3cecb9..85edb239e3f57 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -226,6 +226,7 @@ impl FlycheckHandle { workspace_root: AbsPathBuf, manifest_path: Option, ws_target_dir: Option, + ws_build_dir: Option, toolchain_version: Option, ) -> FlycheckHandle { let actor = FlycheckActor::new( @@ -238,6 +239,7 @@ impl FlycheckHandle { workspace_root, manifest_path, ws_target_dir, + ws_build_dir, toolchain_version, ); let (sender, receiver) = unbounded::(); @@ -431,6 +433,7 @@ struct FlycheckActor { manifest_path: Option, ws_target_dir: Option, + ws_build_dir: Option, /// Either the workspace root of the workspace we are flychecking, /// or the project root of the project. root: Arc, @@ -533,6 +536,7 @@ impl FlycheckActor { workspace_root: AbsPathBuf, manifest_path: Option, ws_target_dir: Option, + ws_build_dir: Option, toolchain_version: Option, ) -> FlycheckActor { tracing::info!(%id, ?workspace_root, "Spawning flycheck"); @@ -547,6 +551,7 @@ impl FlycheckActor { scope: FlycheckScope::Workspace, manifest_path, ws_target_dir, + ws_build_dir, command_handle: None, command_receiver: None, diagnostics_cleared_for: Default::default(), @@ -633,14 +638,14 @@ impl FlycheckActor { sender, match &self.config { FlycheckConfig::Automatic { cargo_options, .. } => { - let ws_target_dir = - self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path); - let target_dir = - cargo_options.target_dir_config.target_dir(ws_target_dir); + let target_dir = cargo_options + .target_dir_config + .target_dir(self.ws_target_dir.as_deref()); - // If `"rust-analyzer.cargo.targetDir": null`, we should use - // workspace's target dir instead of hard-coded fallback. - let target_dir = target_dir.as_deref().or(ws_target_dir); + let output_dir = target_dir + .as_deref() + .or(self.ws_build_dir.as_deref()) + .or(self.ws_target_dir.as_deref()); Some( // As `CommandHandle::spawn`'s working directory is @@ -648,10 +653,10 @@ impl FlycheckActor { // from the flycheck's working directory, we should canonicalize // the output directory, otherwise we might write it into the // wrong target dir. - // If `target_dir` is an absolute path, it will replace + // If `output_dir` is an absolute path, it will replace // `self.root` and that's an intended behavior. self.root - .join(target_dir.unwrap_or( + .join(output_dir.unwrap_or( Utf8Path::new("target").join("rust-analyzer").as_path(), )) .join(format!("flycheck{}", self.id)) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 86c954b089e4d..039fbeff828e7 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -902,6 +902,7 @@ impl GlobalState { None, None, None, + None, )] } crate::flycheck::InvocationStrategy::PerWorkspace => { @@ -921,6 +922,7 @@ impl GlobalState { cargo.workspace_root(), Some(cargo.manifest_path()), Some(cargo.target_directory()), + cargo.build_directory(), ), ProjectWorkspaceKind::Json(project) => { let config_json = crate::flycheck::FlycheckConfigJson { @@ -932,10 +934,10 @@ impl GlobalState { // in the workspace configuration. match config { _ if config_json.any_configured() => { - (config_json, project.path(), None, None) + (config_json, project.path(), None, None, None) } FlycheckConfig::CustomCommand { .. } => { - (config_json, project.path(), None, None) + (config_json, project.path(), None, None, None) } _ => return None, } @@ -949,7 +951,7 @@ impl GlobalState { .map( |( id, - (config_json, root, manifest_path, target_dir), + (config_json, root, manifest_path, target_dir, build_dir), sysroot_root, toolchain, )| { @@ -963,6 +965,7 @@ impl GlobalState { root.to_path_buf(), manifest_path.map(|it| it.to_path_buf()), target_dir.map(|it| AsRef::::as_ref(it).to_path_buf()), + build_dir.map(|it| AsRef::::as_ref(it).to_path_buf()), toolchain, ) }, 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 7700643f03e75..327fa025f490d 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 @@ -42,6 +42,40 @@ fn main() { ); } +#[test] +fn test_flycheck_output_uses_build_directory() { + if skip_slow_tests() { + return; + } + + let server = Project::with_fixture( + r#" +//- /.cargo/config.toml +[build] +build-dir = "build" + +//- /Cargo.toml +[package] +name = "foo" +version = "0.0.0" + +//- /src/main.rs +fn main() { + let x = 1; +} +"#, + ) + .with_config(serde_json::json!({ + "checkOnSave": true, + })) + .server() + .wait_until_workspace_is_loaded(); + + _ = server.wait_for_diagnostics(); + assert!(server.path().join("build/flycheck0/stdout").exists()); + assert!(server.path().join("build/flycheck0/stderr").exists()); +} + #[test] fn test_flycheck_diagnostic_cleared_after_fix() { if skip_slow_tests() { 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 2e0994a1885d9..0d9bb4f92bdc0 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -34,9 +34,9 @@ //! discriminant: //! drop: sized //! env: option -//! eq: sized +//! eq: sized, unary_ops, builtin_impls //! error: fmt -//! float_consts: +//! float_consts: unary_ops, builtin_impls //! fmt: option, result, transmute, coerce_unsized, copy, clone, derive //! fn: sized, tuple //! from: sized, result @@ -370,11 +370,13 @@ pub mod clone { } } + // region:index impl Clone for [T; 1] { fn clone(&self) -> Self { [self[0].clone()] } } + // endregion:index // endregion:builtin_impls // region:derive @@ -1213,6 +1215,30 @@ pub mod ops { #[must_use = "this returns the result of the operation, without modifying the original"] fn neg(self) -> Self::Output; } + + // region:builtin_impls + macro_rules! not_impl { + ($($t:ty)*) => ($( + impl const Not for $t { + type Output = $t; + fn not(self) -> $t { !self } + } + )*) + } + + not_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } + + macro_rules! neg_impl { + ($($t:ty)*) => ($( + impl const Neg for $t { + type Output = $t; + fn neg(self) -> $t { -self } + } + )*) + } + + neg_impl! { isize i8 i16 i32 i64 i128 f16 f32 f64 f128 } + // endregion:builtin_impls // endregion:unary_ops // region:coroutine diff --git a/src/tools/rust-analyzer/crates/tt/Cargo.toml b/src/tools/rust-analyzer/crates/tt/Cargo.toml index 9a798b592d03c..bd8f740b2f50d 100644 --- a/src/tools/rust-analyzer/crates/tt/Cargo.toml +++ b/src/tools/rust-analyzer/crates/tt/Cargo.toml @@ -16,7 +16,6 @@ doctest = false arrayvec.workspace = true text-size.workspace = true rustc-hash.workspace = true -indexmap.workspace = true span = { path = "../span", version = "0.0", default-features = false } stdx.workspace = true diff --git a/src/tools/rust-analyzer/crates/tt/src/buffer.rs b/src/tools/rust-analyzer/crates/tt/src/buffer.rs index 78cf4b956d0ce..03dcb1b405378 100644 --- a/src/tools/rust-analyzer/crates/tt/src/buffer.rs +++ b/src/tools/rust-analyzer/crates/tt/src/buffer.rs @@ -1,41 +1,51 @@ //! Stateful iteration over token trees. //! //! We use this as the source of tokens for parser. -use crate::{Leaf, Subtree, TokenTree, TokenTreesView, dispatch_ref}; +use crate::{Leaf, Subtree, TokenTree, TokenTreesView, storage::TokenTreesSlice}; pub struct Cursor<'a> { - buffer: TokenTreesView<'a>, - index: usize, - subtrees_stack: Vec, + origin: TokenTreesSlice<'a>, + buffer_before_current: TokenTreesSlice<'a>, + buffer_after_current: TokenTreesSlice<'a>, + /// The number of times we called [`Self::advance()`]. Also the index of [`Self::next`]. + advances_count: usize, + len: usize, + next: Option, + subtrees_stack: Vec<(usize, Subtree)>, } impl<'a> Cursor<'a> { - pub fn new(buffer: TokenTreesView<'a>) -> Self { - Self { buffer, index: 0, subtrees_stack: Vec::new() } + pub fn new(origin: TokenTreesView<'a>) -> Self { + let mut buffer_after_current = origin.slice; + let buffer_before_current = buffer_after_current; + let len = origin.len; + let next = if len >= 1 { buffer_after_current.advance() } else { None }; + Self { + origin: origin.slice, + buffer_after_current, + buffer_before_current, + advances_count: 0, + len, + next, + subtrees_stack: Vec::new(), + } } /// Check whether it is eof pub fn eof(&self) -> bool { - self.index == self.buffer.len() && self.subtrees_stack.is_empty() + self.next.is_none() && self.subtrees_stack.is_empty() } pub fn is_root(&self) -> bool { self.subtrees_stack.is_empty() } - fn at(&self, idx: usize) -> Option { - dispatch_ref! { - match self.buffer.repr => tt => Some(tt.get(idx)?.to_api(self.buffer.span_parts)) - } + fn last_subtree(&self) -> Option<(usize, Subtree)> { + self.subtrees_stack.last().copied() } - fn last_subtree(&self) -> Option<(usize, Subtree)> { - self.subtrees_stack.last().map(|&subtree_idx| { - let Some(TokenTree::Subtree(subtree)) = self.at(subtree_idx) else { - panic!("subtree pointing to non-subtree"); - }; - (subtree_idx, subtree) - }) + pub(crate) fn remaining(&self) -> TokenTreesView<'a> { + TokenTreesView { slice: self.buffer_before_current, len: self.len - self.advances_count } } pub fn end(&mut self) -> Subtree { @@ -44,7 +54,7 @@ impl<'a> Cursor<'a> { // +1 because `Subtree.len` excludes the subtree itself. assert_eq!( last_subtree_idx + last_subtree.usize_len() + 1, - self.index, + self.advances_count, "called `Cursor::end()` without finishing a subtree" ); self.subtrees_stack.pop(); @@ -55,11 +65,24 @@ impl<'a> Cursor<'a> { pub fn token_tree(&self) -> Option { if let Some((last_subtree_idx, last_subtree)) = self.last_subtree() { // +1 because `Subtree.len` excludes the subtree itself. - if last_subtree_idx + last_subtree.usize_len() + 1 == self.index { + if last_subtree_idx + last_subtree.usize_len() + 1 == self.advances_count { return None; } } - self.at(self.index) + self.next.clone() + } + + fn advance(&mut self) { + if self.advances_count >= self.len { + return; + } + if let Some(TokenTree::Subtree(subtree)) = self.next { + self.subtrees_stack.push((self.advances_count, subtree)); + } + self.advances_count += 1; + self.buffer_before_current = self.buffer_after_current; + self.next = + if self.advances_count < self.len { self.buffer_after_current.advance() } else { None }; } /// Bump the cursor, and enters a subtree if it is on one. @@ -68,40 +91,35 @@ impl<'a> Cursor<'a> { // +1 because `Subtree.len` excludes the subtree itself. assert_ne!( last_subtree_idx + last_subtree.usize_len() + 1, - self.index, + self.advances_count, "called `Cursor::bump()` when at the end of a subtree" ); } - if let Some(TokenTree::Subtree(_)) = self.at(self.index) { - self.subtrees_stack.push(self.index); - } - self.index += 1; + self.advance(); } pub fn bump_or_end(&mut self) { - if let Some((last_subtree_idx, last_subtree)) = self.last_subtree() { - // +1 because `Subtree.len` excludes the subtree itself. - if last_subtree_idx + last_subtree.usize_len() + 1 == self.index { - self.subtrees_stack.pop(); - return; - } - } // +1 because `Subtree.len` excludes the subtree itself. - if let Some(TokenTree::Subtree(_)) = self.at(self.index) { - self.subtrees_stack.push(self.index); + if let Some((last_subtree_idx, last_subtree)) = self.last_subtree() + && last_subtree_idx + last_subtree.usize_len() + 1 == self.advances_count + { + self.subtrees_stack.pop(); + return; } - self.index += 1; + self.advance(); } pub fn peek_two_leaves(&self) -> Option<[Leaf; 2]> { if let Some((last_subtree_idx, last_subtree)) = self.last_subtree() { // +1 because `Subtree.len` excludes the subtree itself. let last_end = last_subtree_idx + last_subtree.usize_len() + 1; - if last_end == self.index || last_end == self.index + 1 { + if last_end == self.advances_count || last_end == self.advances_count + 1 { return None; } } - self.at(self.index).zip(self.at(self.index + 1)).and_then(|it| match it { + let mut buffer = self.buffer_after_current; + let next_next = if self.advances_count + 1 < self.len { buffer.advance() } else { None }; + self.next.clone().zip(next_next).and_then(|it| match it { (TokenTree::Leaf(a), TokenTree::Leaf(b)) => Some([a, b]), _ => None, }) @@ -109,9 +127,6 @@ impl<'a> Cursor<'a> { pub fn crossed(&self) -> TokenTreesView<'a> { assert!(self.is_root()); - TokenTreesView { - repr: self.buffer.repr.get(..self.index).unwrap(), - span_parts: self.buffer.span_parts, - } + TokenTreesView { slice: self.origin, len: self.advances_count } } } diff --git a/src/tools/rust-analyzer/crates/tt/src/iter.rs b/src/tools/rust-analyzer/crates/tt/src/iter.rs index 7caacd40dd7e3..9419a9e8534eb 100644 --- a/src/tools/rust-analyzer/crates/tt/src/iter.rs +++ b/src/tools/rust-analyzer/crates/tt/src/iter.rs @@ -8,8 +8,8 @@ use intern::sym; use span::Span; use crate::{ - Ident, Leaf, MAX_GLUED_PUNCT_LEN, Punct, Spacing, Subtree, TokenTree, TokenTreesReprRef, - TokenTreesView, dispatch_ref, + Ident, Leaf, MAX_GLUED_PUNCT_LEN, Punct, Spacing, Subtree, TokenTree, TokenTreesView, + buffer::Cursor, }; #[derive(Clone)] @@ -126,13 +126,13 @@ impl<'a> TtIter<'a> { return Ok(res); } - let (second, third) = match (self.peek_n(0), self.peek_n(1)) { - (Some(TokenTree::Leaf(Leaf::Punct(p2))), Some(TokenTree::Leaf(Leaf::Punct(p3)))) + let (second, third) = match self.peek_two() { + [Some(TokenTree::Leaf(Leaf::Punct(p2))), Some(TokenTree::Leaf(Leaf::Punct(p3)))] if p2.spacing == Spacing::Joint => { (p2, Some(p3)) } - (Some(TokenTree::Leaf(Leaf::Punct(p2))), _) => (p2, None), + [Some(TokenTree::Leaf(Leaf::Punct(p2))), _] => (p2, None), _ => { res.push(first); return Ok(res); @@ -165,20 +165,21 @@ impl<'a> TtIter<'a> { } /// This method won't check for subtrees, so the nth token tree may not be the nth sibling of the current tree. - fn peek_n(&self, n: usize) -> Option { - dispatch_ref! { - match self.inner.repr => tt => Some(tt.get(n)?.to_api(self.inner.span_parts)) - } + fn peek_two(&self) -> [Option; 2] { + let mut iter = self.inner.iter_flat_tokens(); + [iter.next(), iter.next()] } pub fn peek(&self) -> Option> { - match self.peek_n(0)? { + if self.inner.is_empty() { + return None; + } + let mut slice = self.inner.slice; + match slice.advance()? { TokenTree::Leaf(leaf) => Some(TtElement::Leaf(leaf)), TokenTree::Subtree(subtree) => { - let nested_repr = self.inner.repr.get(1..subtree.usize_len() + 1).unwrap(); - let nested_iter = TtIter { - inner: TokenTreesView { repr: nested_repr, span_parts: self.inner.span_parts }, - }; + let nested_iter = + TtIter { inner: TokenTreesView { len: subtree.usize_len(), slice } }; Some(TtElement::Subtree(subtree, nested_iter)) } } @@ -186,7 +187,7 @@ impl<'a> TtIter<'a> { /// Equivalent to `peek().is_none()`, but a bit faster. pub fn is_empty(&self) -> bool { - self.inner.len() == 0 + self.inner.is_empty() } pub fn next_span(&self) -> Option { @@ -197,9 +198,9 @@ impl<'a> TtIter<'a> { self.inner } - /// **Warning**: This advances `skip` **flat** token trees, subtrees account for children+1! - pub fn flat_advance(&mut self, skip: usize) { - self.inner.repr = self.inner.repr.get(skip..).unwrap(); + /// **Warning**: This advances **flat** token trees, subtrees account for children+1! + pub fn flat_advance_to(&mut self, up_to: &Cursor<'a>) { + self.inner = up_to.remaining(); } pub fn savepoint(&self) -> TtIterSavepoint<'a> { @@ -207,34 +208,7 @@ impl<'a> TtIter<'a> { } pub fn from_savepoint(&self, savepoint: TtIterSavepoint<'a>) -> TokenTreesView<'a> { - let len = match (self.inner.repr, savepoint.0.repr) { - ( - TokenTreesReprRef::SpanStorage32(this), - TokenTreesReprRef::SpanStorage32(savepoint), - ) => { - (this.as_ptr() as usize - savepoint.as_ptr() as usize) - / size_of::>() - } - ( - TokenTreesReprRef::SpanStorage64(this), - TokenTreesReprRef::SpanStorage64(savepoint), - ) => { - (this.as_ptr() as usize - savepoint.as_ptr() as usize) - / size_of::>() - } - ( - TokenTreesReprRef::SpanStorage96(this), - TokenTreesReprRef::SpanStorage96(savepoint), - ) => { - (this.as_ptr() as usize - savepoint.as_ptr() as usize) - / size_of::>() - } - _ => panic!("savepoint did not originate from this TtIter"), - }; - TokenTreesView { - repr: savepoint.0.repr.get(..len).unwrap(), - span_parts: savepoint.0.span_parts, - } + TokenTreesView { slice: savepoint.0.slice, len: savepoint.0.len - self.inner.len } } pub fn next_as_view(&mut self) -> Option> { @@ -274,12 +248,20 @@ impl TtElement<'_> { impl<'a> Iterator for TtIter<'a> { type Item = TtElement<'a>; fn next(&mut self) -> Option { - let result = self.peek()?; - let skip = match &result { - TtElement::Leaf(_) => 1, - TtElement::Subtree(subtree, _) => subtree.usize_len() + 1, - }; - self.inner.repr = self.inner.repr.get(skip..).unwrap(); - Some(result) + if self.inner.is_empty() { + return None; + } + self.inner.len -= 1; + let (tt, subtree_slice) = self.inner.slice.advance_skip_subtree()?; + match tt { + TokenTree::Leaf(leaf) => Some(TtElement::Leaf(leaf)), + TokenTree::Subtree(subtree) => { + self.inner.len -= subtree.usize_len(); + let nested_iter = TtIter { + inner: TokenTreesView { len: subtree.usize_len(), slice: subtree_slice }, + }; + Some(TtElement::Subtree(subtree, nested_iter)) + } + } } } diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs index 7b46c33596441..2bc2b64cd4fd0 100644 --- a/src/tools/rust-analyzer/crates/tt/src/lib.rs +++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs @@ -17,7 +17,7 @@ pub mod buffer; pub mod iter; mod storage; -use std::{fmt, slice::SliceIndex}; +use std::fmt; use arrayvec::ArrayString; use buffer::Cursor; @@ -27,7 +27,7 @@ use stdx::{impl_from, itertools::Itertools as _}; pub use span::Span; pub use text_size::{TextRange, TextSize}; -use crate::storage::{CompressedSpanPart, SpanStorage}; +use crate::storage::TokenTreesSlice; pub use self::iter::{TtElement, TtIter}; pub use self::storage::{TopSubtree, TopSubtreeBuilder}; @@ -42,9 +42,11 @@ pub struct Lit { } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[repr(u8)] +// The discriminants are important for `storage.rs` decoding. pub enum IdentIsRaw { - No, - Yes, + No = 0, + Yes = 1, } impl IdentIsRaw { pub fn yes(self) -> bool { @@ -113,6 +115,14 @@ impl Leaf { Leaf::Ident(it) => &it.span, } } + + fn symbol(&self) -> Option<&Symbol> { + match self { + Leaf::Literal(Literal { text_and_suffix: symbol, .. }) + | Leaf::Ident(Ident { sym: symbol, .. }) => Some(symbol), + Leaf::Punct(_) => None, + } + } } impl_from!(Literal, Punct, Ident for Leaf); @@ -129,67 +139,16 @@ impl Subtree { } } -#[rust_analyzer::macro_style(braces)] -macro_rules! dispatch_ref { - ( - match $scrutinee:expr => $tt:ident => $body:expr - ) => { - match $scrutinee { - $crate::TokenTreesReprRef::SpanStorage32($tt) => $body, - $crate::TokenTreesReprRef::SpanStorage64($tt) => $body, - $crate::TokenTreesReprRef::SpanStorage96($tt) => $body, - } - }; -} -use dispatch_ref; - -#[derive(Clone, Copy)] -enum TokenTreesReprRef<'a> { - SpanStorage32(&'a [crate::storage::TokenTree]), - SpanStorage64(&'a [crate::storage::TokenTree]), - SpanStorage96(&'a [crate::storage::TokenTree]), -} - -impl<'a> TokenTreesReprRef<'a> { - #[inline] - fn get(&self, index: I) -> Option - where - I: SliceIndex< - [crate::storage::TokenTree], - Output = [crate::storage::TokenTree], - >, - I: SliceIndex< - [crate::storage::TokenTree], - Output = [crate::storage::TokenTree], - >, - I: SliceIndex< - [crate::storage::TokenTree], - Output = [crate::storage::TokenTree], - >, - { - Some(match self { - TokenTreesReprRef::SpanStorage32(tt) => { - TokenTreesReprRef::SpanStorage32(tt.get(index)?) - } - TokenTreesReprRef::SpanStorage64(tt) => { - TokenTreesReprRef::SpanStorage64(tt.get(index)?) - } - TokenTreesReprRef::SpanStorage96(tt) => { - TokenTreesReprRef::SpanStorage96(tt.get(index)?) - } - }) - } -} - #[derive(Clone, Copy)] pub struct TokenTreesView<'a> { - repr: TokenTreesReprRef<'a>, - span_parts: &'a [CompressedSpanPart], + slice: TokenTreesSlice<'a>, + len: usize, } impl<'a> TokenTreesView<'a> { + #[inline] pub fn empty() -> Self { - Self { repr: TokenTreesReprRef::SpanStorage32(&[]), span_parts: &[] } + Self { slice: TokenTreesSlice::empty(), len: 0 } } pub fn iter(&self) -> TtIter<'a> { @@ -201,9 +160,7 @@ impl<'a> TokenTreesView<'a> { } pub fn len(&self) -> usize { - dispatch_ref! { - match self.repr => tt => tt.len() - } + self.len } pub fn is_empty(&self) -> bool { @@ -211,12 +168,9 @@ impl<'a> TokenTreesView<'a> { } pub fn try_into_subtree(self) -> Option> { - let is_subtree = dispatch_ref! { - match self.repr => tt => matches!( - tt.first(), - Some(crate::storage::TokenTree::Subtree { len, .. }) if (*len as usize) == (tt.len() - 1) - ) - }; + let is_subtree = self.iter_flat_tokens().next().is_some_and( + |it| matches!(it, TokenTree::Subtree(subtree) if subtree.usize_len() == self.len - 1), + ); if is_subtree { Some(SubtreeView(self)) } else { None } } @@ -251,23 +205,29 @@ impl<'a> TokenTreesView<'a> { } pub fn first_span(&self) -> Option { - Some(dispatch_ref! { - match self.repr => tt => tt.first()?.first_span().span(self.span_parts) - }) + self.iter_flat_tokens().next().map(|it| it.first_span()) } + /// Note: this is quite expensive, this needs to decode the whole view, + /// although it "tricks" by skipping subtrees (since we know their byte length). pub fn last_span(&self) -> Option { - Some(dispatch_ref! { - match self.repr => tt => tt.last()?.last_span().span(self.span_parts) - }) + let mut iter = self.iter(); + loop { + match iter.last()? { + TtElement::Leaf(leaf) => return Some(*leaf.span()), + TtElement::Subtree(subtree, tt_iter) => { + if subtree.len == 0 { + return Some(subtree.delimiter.close); + } else { + iter = tt_iter; + } + } + } + } } - pub fn iter_flat_tokens(self) -> impl ExactSizeIterator + use<'a> { - (0..self.len()).map(move |idx| { - dispatch_ref! { - match self.repr => tt => tt[idx].to_api(self.span_parts) - } - }) + pub fn iter_flat_tokens(&self) -> impl Iterator + use<'a> { + self.slice.iter().take(self.len) } } @@ -343,23 +303,10 @@ impl<'a> SubtreeView<'a> { } pub fn top_subtree(&self) -> Subtree { - dispatch_ref! { - match self.0.repr => tt => { - let crate::storage::TokenTree::Subtree { len, delim_kind, open_span, close_span } = - &tt[0] - else { - unreachable!("the first token tree is always the top subtree"); - }; - Subtree { - delimiter: Delimiter { - open: open_span.span(self.0.span_parts), - close: close_span.span(self.0.span_parts), - kind: *delim_kind, - }, - len: *len, - } - } - } + let Some(TokenTree::Subtree(subtree)) = self.0.iter_flat_tokens().next() else { + unreachable!("the first token tree is always the top subtree"); + }; + subtree } pub fn strip_invisible(&self) -> TokenTreesView<'a> { @@ -371,18 +318,10 @@ impl<'a> SubtreeView<'a> { } pub fn token_trees(&self) -> TokenTreesView<'a> { - let repr = match self.0.repr { - TokenTreesReprRef::SpanStorage32(token_trees) => { - TokenTreesReprRef::SpanStorage32(&token_trees[1..]) - } - TokenTreesReprRef::SpanStorage64(token_trees) => { - TokenTreesReprRef::SpanStorage64(&token_trees[1..]) - } - TokenTreesReprRef::SpanStorage96(token_trees) => { - TokenTreesReprRef::SpanStorage96(&token_trees[1..]) - } - }; - TokenTreesView { repr, ..self.0 } + let mut result = self.0; + result.slice.advance(); + result.len -= 1; + result } } @@ -435,11 +374,13 @@ impl Delimiter { } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[repr(u8)] +// The discriminants are important for decoding for `storage.rs`. pub enum DelimiterKind { - Parenthesis, - Brace, - Bracket, - Invisible, + Parenthesis = 0, + Brace = 1, + Bracket = 2, + Invisible = 3, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -555,7 +496,9 @@ pub struct Punct { /// compound token. Used for conversions to `proc_macro::Spacing`. Also used to /// guide pretty-printing, which is where the `JointHidden` value (which isn't /// part of `proc_macro::Spacing`) comes in useful. +// The discriminants are important for decoding for `storage.rs`. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(u8)] pub enum Spacing { /// The token cannot join with the following token to form a compound /// token. @@ -572,7 +515,7 @@ pub enum Spacing { /// /// Converts to `proc_macro::Spacing::Alone`, and /// `proc_macro::Spacing::Alone` converts back to this. - Alone, + Alone = 0, /// The token can join with the following token to form a compound token. /// @@ -586,7 +529,7 @@ pub enum Spacing { /// /// Converts to `proc_macro::Spacing::Joint`, and /// `proc_macro::Spacing::Joint` converts back to this. - Joint, + Joint = 1, /// The token can join with the following token to form a compound token, /// but this will not be visible at the proc macro level. (This is what the @@ -608,7 +551,7 @@ pub enum Spacing { /// pretty-printing of `TokenStream`'s produced by other means (i.e. parsed /// source code, internally constructed token streams, and token streams /// produced by declarative macros). - JointHidden, + JointHidden = 2, } /// Identifier or keyword. @@ -764,36 +707,16 @@ impl Subtree { } pub fn pretty(tkns: TokenTreesView<'_>) -> String { - return dispatch_ref! { - match tkns.repr => tt => pretty_impl(tkns, tt) - }; - - use crate::storage::TokenTree; + return pretty_impl(tkns.iter()); - fn tokentree_to_text( - tkns_view: TokenTreesView<'_>, - tkn: &TokenTree, - tkns: &mut &[TokenTree], - ) -> String { + fn tokentree_to_text(tkn: TtElement<'_>) -> String { match tkn { - TokenTree::Ident { sym, is_raw, .. } => format!("{}{}", is_raw.as_str(), sym), - &TokenTree::Literal { ref text_and_suffix, kind, suffix_len, span } => { - format!( - "{}", - Literal { - text_and_suffix: text_and_suffix.clone(), - span: span.span(tkns_view.span_parts), - kind, - suffix_len - } - ) + TtElement::Leaf(leaf) => { + format!("{}", leaf) } - TokenTree::Punct { char, .. } => format!("{}", char), - TokenTree::Subtree { len, delim_kind, .. } => { - let (subtree_content, rest) = tkns.split_at(*len as usize); - let content = pretty_impl(tkns_view, subtree_content); - *tkns = rest; - let (open, close) = match *delim_kind { + TtElement::Subtree(Subtree { delimiter, .. }, subtree_content) => { + let content = pretty_impl(subtree_content); + let (open, close) = match delimiter.kind { DelimiterKind::Brace => ("{", "}"), DelimiterKind::Bracket => ("[", "]"), DelimiterKind::Parenthesis => ("(", ")"), @@ -804,23 +727,16 @@ pub fn pretty(tkns: TokenTreesView<'_>) -> String { } } - fn pretty_impl( - tkns_view: TokenTreesView<'_>, - mut tkns: &[TokenTree], - ) -> String { + fn pretty_impl(tkns: TtIter<'_>) -> 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(tkns_view, tkn, &mut tkns)].join(if last_to_joint { - "" - } else { - " " - }); + for tkn in tkns { + last = + [last, tokentree_to_text(tkn.clone())].join(if last_to_joint { "" } else { " " }); last_to_joint = false; - if let TokenTree::Punct { spacing, .. } = tkn - && *spacing == Spacing::Joint + if let TtElement::Leaf(Leaf::Punct(Punct { spacing, .. })) = tkn + && spacing == Spacing::Joint { last_to_joint = true; } @@ -847,7 +763,7 @@ impl TransformTtAction<'_> { /// tts view. pub fn transform_tt<'b>( tt: &mut TopSubtree, - mut callback: impl FnMut(TokenTree) -> TransformTtAction<'b>, + mut callback: impl FnMut(&TokenTree) -> TransformTtAction<'b>, ) { let mut tt_vec = tt.as_token_trees().iter_flat_tokens().collect::>(); @@ -867,27 +783,20 @@ pub fn transform_tt<'b>( } } - let current = match &tt_vec[i] { - TokenTree::Leaf(leaf) => TokenTree::Leaf(match leaf { - Leaf::Literal(leaf) => Leaf::Literal(leaf.clone()), - Leaf::Punct(leaf) => Leaf::Punct(*leaf), - Leaf::Ident(leaf) => Leaf::Ident(leaf.clone()), - }), - TokenTree::Subtree(subtree) => TokenTree::Subtree(*subtree), - }; + let current = &tt_vec[i]; let action = callback(current); match action { TransformTtAction::Keep => { // This cannot be shared with the replaced case, because then we may push the same subtree // twice, and will update it twice which will lead to errors. - if let TokenTree::Subtree(_) = &tt_vec[i] { + if let TokenTree::Subtree(_) = current { subtrees_stack.push(i); } i += 1; } TransformTtAction::ReplaceWith(replacement) => { - let old_len = 1 + match &tt_vec[i] { + let old_len = 1 + match current { TokenTree::Leaf(_) => 0, TokenTree::Subtree(subtree) => subtree.usize_len(), }; diff --git a/src/tools/rust-analyzer/crates/tt/src/storage.rs b/src/tools/rust-analyzer/crates/tt/src/storage.rs index 50a1106175ab3..150777cc39e55 100644 --- a/src/tools/rust-analyzer/crates/tt/src/storage.rs +++ b/src/tools/rust-analyzer/crates/tt/src/storage.rs @@ -2,40 +2,32 @@ //! will waste a lot of memory. So instead we implement a clever compression mechanism: //! //! A `TopSubtree` has a list of [`CompressedSpanPart`], which are the parts of a span -//! that tend to be shared between tokens - namely, without the range. The main list -//! of token trees is kept in one of three versions, where we use the smallest version -//! we can for this tree: +//! that tend to be shared between tokens - namely, without the range. //! -//! 1. In the most common version a span is just a `u32`. The bits are divided as follows: -//! there are 4 bits that index into the [`CompressedSpanPart`] list. 20 bits -//! store the range start, and 8 bits store the range length. In experiments, -//! this accounts for 75%-85% of the spans. -//! 2. In the second version a span is 64 bits. 32 bits for the range start, 16 bits -//! for the range length, and 16 bits for the span parts index. This is used in -//! less than 2% of all `TopSubtree`s, but they account for 15%-25% of the spans: -//! those are mostly token tree munchers, that generate a lot of `SyntaxContext`s -//! (because they recurse a lot), which is why they can't fit in the first version, -//! and tend to generate a lot of code. -//! 3. The third version is practically unused; 65,535 bytes for a token and 65,535 -//! unique span parts is more than enough for everybody. However, someone may still -//! create a macro that requires more, therefore we have this version as a backup: -//! it uses 96 bits, 32 for each of the range start, length and span parts index. - -use std::fmt; +//! The main list of token trees is stored in a variable-length encoding as bytes. +//! The encoding is documented in the [`decode()`] function (which decodes one [`TokenTree`]). + +use std::{assert_matches, collections::hash_map, fmt::Debug, hint::cold_path, mem::transmute}; + +#[cfg(all(debug_assertions, not(miri)))] +use std::cell::Cell; + +#[cfg(not(all(debug_assertions, not(miri))))] +use std::mem::MaybeUninit; use intern::Symbol; -use rustc_hash::FxBuildHasher; +use rustc_hash::FxHashMap; use span::{Span, SpanAnchor, SyntaxContext, TextRange, TextSize}; use crate::{ - DelimSpan, DelimiterKind, IdentIsRaw, LitKind, Spacing, SubtreeView, TokenTreesReprRef, - TokenTreesView, TtIter, dispatch_ref, + DelimSpan, Delimiter, DelimiterKind, Ident, IdentIsRaw, Leaf, LitKind, Literal, Punct, Spacing, + Subtree, SubtreeView, TokenTree, TokenTreesView, TtIter, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub(crate) struct CompressedSpanPart { - pub(crate) anchor: SpanAnchor, - pub(crate) ctx: SyntaxContext, +struct CompressedSpanPart { + anchor: SpanAnchor, + ctx: SyntaxContext, } impl CompressedSpanPart { @@ -50,376 +42,1086 @@ impl CompressedSpanPart { } } -pub(crate) trait SpanStorage: Copy { - fn can_hold(text_range: TextRange, span_parts_index: usize) -> bool; - - fn new(text_range: TextRange, span_parts_index: usize) -> Self; - - fn text_range(&self) -> TextRange; +trait Encodable: Sized { + #[cfg(all(debug_assertions, not(miri)))] + fn write(self, buffer: &[Cell]); + #[cfg(all(debug_assertions, not(miri)))] + fn read(buffer: &[u8]) -> Self; +} - fn span_parts_index(&self) -> usize; +impl Encodable for u8 { + #[cfg(all(debug_assertions, not(miri)))] + fn write(self, buffer: &[Cell]) { + buffer[0].set(self); + } + #[cfg(all(debug_assertions, not(miri)))] + fn read(buffer: &[u8]) -> Self { + buffer[0] + } +} - #[inline] - fn span(&self, span_parts: &[CompressedSpanPart]) -> Span { - span_parts[self.span_parts_index()].recombine(self.text_range()) +impl Encodable for u16 { + #[cfg(all(debug_assertions, not(miri)))] + fn write(self, buffer: &[Cell]) { + let value = self.to_ne_bytes(); + let buffer: &[Cell; size_of::()] = buffer.try_into().unwrap(); + for (b, v) in std::iter::zip(buffer, value) { + b.set(v); + } + } + #[cfg(all(debug_assertions, not(miri)))] + fn read(buffer: &[u8]) -> Self { + Self::from_ne_bytes(buffer.try_into().unwrap()) } } -#[inline] -const fn n_bits_mask(n: u32) -> u32 { - (1 << n) - 1 +impl Encodable for u32 { + #[cfg(all(debug_assertions, not(miri)))] + fn write(self, buffer: &[Cell]) { + let value = self.to_ne_bytes(); + let buffer: &[Cell; size_of::()] = buffer.try_into().unwrap(); + for (b, v) in std::iter::zip(buffer, value) { + b.set(v); + } + } + #[cfg(all(debug_assertions, not(miri)))] + fn read(buffer: &[u8]) -> Self { + Self::from_ne_bytes(buffer.try_into().unwrap()) + } } -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub(crate) struct SpanStorage32(u32); +impl Encodable for char { + #[cfg(all(debug_assertions, not(miri)))] + fn write(self, buffer: &[Cell]) { + u32::from(self).write(buffer) + } + #[cfg(all(debug_assertions, not(miri)))] + fn read(buffer: &[u8]) -> Self { + char::from_u32(u32::read(buffer)).unwrap() + } +} -impl SpanStorage32 { - const SPAN_PARTS_BIT: u32 = 4; - const LEN_BITS: u32 = 8; - const OFFSET_BITS: u32 = 20; +struct UninitBuffer { + #[cfg(all(debug_assertions, not(miri)))] + buffer: Box<[u8]>, + #[cfg(not(all(debug_assertions, not(miri))))] + buffer: Box<[MaybeUninit]>, } -const _: () = assert!( - (SpanStorage32::SPAN_PARTS_BIT + SpanStorage32::LEN_BITS + SpanStorage32::OFFSET_BITS) - == u32::BITS -); +impl UninitBuffer { + #[inline] + fn new(capacity: usize) -> Self { + Self { + #[cfg(all(debug_assertions, not(miri)))] + buffer: vec![0; capacity].into_boxed_slice(), + #[cfg(not(all(debug_assertions, not(miri))))] + buffer: Box::new_uninit_slice(capacity), + } + } -impl SpanStorage for SpanStorage32 { #[inline] - fn can_hold(text_range: TextRange, span_parts_index: usize) -> bool { - let offset = u32::from(text_range.start()); - let len = u32::from(text_range.len()); - let span_parts_index = span_parts_index as u32; + fn writer(&mut self) -> BufferWriter<'_> { + BufferWriter { + #[cfg(all(debug_assertions, not(miri)))] + buffer: Cell::from_mut(&mut *self.buffer).as_slice_of_cells(), + #[cfg(not(all(debug_assertions, not(miri))))] + ptr: self.buffer.as_mut_ptr_range().end.cast::(), + #[cfg(not(all(debug_assertions, not(miri))))] + _marker: stdx::variance::PhantomCovariantLifetime::new(), + } + } - offset <= n_bits_mask(Self::OFFSET_BITS) - && len <= n_bits_mask(Self::LEN_BITS) - && span_parts_index <= n_bits_mask(Self::SPAN_PARTS_BIT) + #[cfg(all(debug_assertions, not(miri)))] + unsafe fn finish(self, writer_finish: usize) -> Box<[u8]> { + self.buffer[writer_finish..].into() } + #[cfg(not(all(debug_assertions, not(miri))))] #[inline] - fn new(text_range: TextRange, span_parts_index: usize) -> Self { - let offset = u32::from(text_range.start()); - let len = u32::from(text_range.len()); - let span_parts_index = span_parts_index as u32; - - debug_assert!(offset <= n_bits_mask(Self::OFFSET_BITS)); - debug_assert!(len <= n_bits_mask(Self::LEN_BITS)); - debug_assert!(span_parts_index <= n_bits_mask(Self::SPAN_PARTS_BIT)); - - Self( - (offset << (Self::LEN_BITS + Self::SPAN_PARTS_BIT)) - | (len << Self::SPAN_PARTS_BIT) - | span_parts_index, - ) + unsafe fn finish(mut self, writer_finish: *mut u8) -> Box<[u8]> { + let end = self.buffer.as_mut_ptr_range().end.cast::(); + unsafe { + let bytes_len = end.offset_from_unsigned(writer_finish); + let mut buffer = Box::<[u8]>::new_uninit_slice(bytes_len); + buffer.as_mut_ptr().cast::().copy_from_nonoverlapping(writer_finish, bytes_len); + buffer.assume_init() + } } +} +#[derive(Clone, Copy)] +struct BufferWriter<'a> { + #[cfg(all(debug_assertions, not(miri)))] + buffer: &'a [Cell], + #[cfg(not(all(debug_assertions, not(miri))))] + ptr: *mut u8, + #[cfg(not(all(debug_assertions, not(miri))))] + _marker: stdx::variance::PhantomCovariantLifetime<'a>, +} + +impl<'a> BufferWriter<'a> { #[inline] - fn text_range(&self) -> TextRange { - let offset = TextSize::new(self.0 >> (Self::SPAN_PARTS_BIT + Self::LEN_BITS)); - let len = TextSize::new((self.0 >> Self::SPAN_PARTS_BIT) & n_bits_mask(Self::LEN_BITS)); - TextRange::at(offset, len) + unsafe fn new(buffer: &'a mut [u8]) -> Self { + BufferWriter { + #[cfg(all(debug_assertions, not(miri)))] + buffer: Cell::from_mut(buffer).as_slice_of_cells(), + #[cfg(not(all(debug_assertions, not(miri))))] + ptr: buffer.as_mut_ptr_range().end, + #[cfg(not(all(debug_assertions, not(miri))))] + _marker: stdx::variance::PhantomCovariantLifetime::new(), + } + } + + #[cfg_attr(not(all(debug_assertions, not(miri))), inline(always))] + unsafe fn write(&mut self, value: T) { + #[cfg(all(debug_assertions, not(miri)))] + { + let write_at = self.buffer.split_off(self.buffer.len() - size_of::()..).unwrap(); + value.write(write_at); + } + #[cfg(not(all(debug_assertions, not(miri))))] + unsafe { + self.ptr = self.ptr.sub(size_of::()); + self.ptr.cast::().write_unaligned(value); + } } + fn len_since(self, other: BufferWriter<'_>) -> usize { + #[cfg(all(debug_assertions, not(miri)))] + { + other.buffer.len() - self.buffer.len() + } + #[cfg(not(all(debug_assertions, not(miri))))] + { + other.ptr.addr() - self.ptr.addr() + } + } + + #[cfg(all(debug_assertions, not(miri)))] + fn finish(self) -> usize { + self.buffer.len() + } + + #[cfg(not(all(debug_assertions, not(miri))))] #[inline] - fn span_parts_index(&self) -> usize { - (self.0 & n_bits_mask(Self::SPAN_PARTS_BIT)) as usize + fn finish(self) -> *mut u8 { + self.ptr } } -impl fmt::Debug for SpanStorage32 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SpanStorage32") - .field("text_range", &self.text_range()) - .field("span_parts_index", &self.span_parts_index()) - .finish() - } +#[inline] +const fn n_bits_mask(n: u32) -> u32 { + (1 << n) - 1 } -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub(crate) struct SpanStorage64 { - offset: u32, - len_and_parts: u32, +#[inline] +const fn n_bits_mask_u64(n: u32) -> u64 { + (1 << n) - 1 } -impl SpanStorage64 { - const SPAN_PARTS_BIT: u32 = 16; - const LEN_BITS: u32 = 16; +// Encoding is done in reverse, from the end to the beginning. This is in order +// to be able to tell how many bytes the children of a `Subtree` occupy, and store +// *that* efficiently, with the smallest possible type. + +#[must_use] +unsafe fn encode_span<'a>( + ptr: BufferWriter<'a>, + span: &Span, + span_parts_map: &FxHashMap, + extra_two_bits: u32, + force_heavy_encoding: bool, +) -> BufferWriter<'a> { + let span_parts_index = span_parts_map[&CompressedSpanPart::from_span(span)] as u32; + let offset = u32::from(span.range.start()); + let len = u32::from(span.range.len()); + unsafe { + encode_span_no_map(ptr, span_parts_index, offset, len, extra_two_bits, force_heavy_encoding) + } } -const _: () = assert!((SpanStorage64::SPAN_PARTS_BIT + SpanStorage64::LEN_BITS) == u32::BITS); +#[must_use] +unsafe fn encode_span_no_map( + mut ptr: BufferWriter<'_>, + mut span_parts_index: u32, + mut offset: u32, + mut len: u32, + extra_two_bits: u32, + force_heavy_encoding: bool, +) -> BufferWriter<'_> { + debug_assert!(extra_two_bits & !0b11 == 0); + let mut first_u32 = extra_two_bits; + first_u32 |= (span_parts_index & n_bits_mask(4)) << 2; + span_parts_index >>= 4; + first_u32 |= (len & n_bits_mask(8)) << (2 + 4); + len >>= 8; + first_u32 |= (offset & n_bits_mask(17)) << (2 + 4 + 8 + 1); + offset >>= 17; + + let extends_to_next = span_parts_index != 0 || len != 0 || offset != 0 || force_heavy_encoding; + first_u32 |= u32::from(extends_to_next) << (2 + 4 + 8); + + if extends_to_next { + ptr = unsafe { + encode_extended_span(ptr, span_parts_index, len, offset, force_heavy_encoding) + }; + } + unsafe { ptr.write::(first_u32) }; -impl SpanStorage for SpanStorage64 { - #[inline] - fn can_hold(text_range: TextRange, span_parts_index: usize) -> bool { - let len = u32::from(text_range.len()); - let span_parts_index = span_parts_index as u32; + ptr +} - len <= n_bits_mask(Self::LEN_BITS) && span_parts_index <= n_bits_mask(Self::SPAN_PARTS_BIT) +#[cold] +#[must_use] +unsafe fn encode_extended_span( + mut ptr: BufferWriter<'_>, + span_parts_index: u32, + len: u32, + offset: u32, + force_heavy_encoding: bool, +) -> BufferWriter<'_> { + if span_parts_index <= n_bits_mask(11) + && len <= n_bits_mask(10) + && offset <= n_bits_mask(10) + && !force_heavy_encoding + { + let mut second_u32 = span_parts_index; + second_u32 |= len << 11; + second_u32 |= offset << (11 + 10); + second_u32 <<= 1; + unsafe { ptr.write::(second_u32) }; + } else { + assert!(span_parts_index <= n_bits_mask(24), "too big `span_parts_index`"); + + let mut u64 = u64::from(span_parts_index); + u64 |= u64::from(len) << 24; + u64 |= u64::from(offset) << (24 + 24); + let third_u32 = u64 as u32; + let mut second_u32 = (u64 >> u32::BITS) as u32; + second_u32 <<= 1; + second_u32 |= 0b1; + unsafe { + ptr.write::(third_u32); + ptr.write::(second_u32); + } } - #[inline] - fn new(text_range: TextRange, span_parts_index: usize) -> Self { - let offset = u32::from(text_range.start()); - let len = u32::from(text_range.len()); - let span_parts_index = span_parts_index as u32; + ptr +} + +#[must_use] +unsafe fn encode_symbol<'a>( + mut ptr: BufferWriter<'a>, + symbol: &Symbol, + tag: u32, + symbols_map: &FxHashMap, +) -> BufferWriter<'a> { + let symbol_idx = symbols_map[symbol] as u32; + unsafe { + if symbol_idx <= n_bits_mask(6) { + ptr.write::(((symbol_idx << 2) | tag) as u8); + } else if symbol_idx <= n_bits_mask(13) { + ptr.write::(((symbol_idx >> 6) << 1) as u8); + ptr.write::(((symbol_idx << 2) | 0b10 | tag) as u8); + } else { + ptr.write::((symbol_idx >> 13) as u16); + ptr.write::((((symbol_idx >> 6) << 1) | 0b1) as u8); + ptr.write::(((symbol_idx << 2) | 0b10 | tag) as u8); + } + } + ptr +} - debug_assert!(len <= n_bits_mask(Self::LEN_BITS)); - debug_assert!(span_parts_index <= n_bits_mask(Self::SPAN_PARTS_BIT)); +#[must_use] +unsafe fn encode<'a>( + mut ptr: BufferWriter<'a>, + tt: TokenTree, + span_parts_map: &FxHashMap, + byte_size_after: &mut [u32], + symbols_map: &FxHashMap, +) -> BufferWriter<'a> { + let before_ptr = ptr; + unsafe { + match tt { + TokenTree::Leaf(Leaf::Punct(Punct { char, spacing, span })) => { + if char.is_ascii() { + let spacing = spacing as u8; + let span_extra = 0b1 | (u32::from(spacing) & 0b10); + let char = ((char as u8) << 1) | (spacing & 0b1); + ptr.write::(char); + ptr = encode_span(ptr, &span, span_parts_map, span_extra, false); + } else { + let mut control_byte = 0b110; + control_byte |= (spacing as u8) << 3; + ptr.write::(char); + ptr.write::(control_byte); + ptr = encode_span(ptr, &span, span_parts_map, 0b00, false); + } + } + TokenTree::Leaf(Leaf::Ident(Ident { sym, span, is_raw })) => { + ptr = encode_symbol(ptr, &sym, is_raw as u32, symbols_map); + ptr = encode_span(ptr, &span, span_parts_map, 0b10, false); + } + TokenTree::Leaf(Leaf::Literal(Literal { text_and_suffix, span, kind, suffix_len })) => { + if matches!(kind, LitKind::Str | LitKind::StrRaw(0 | 1) | LitKind::Integer) + && u32::from(suffix_len) <= n_bits_mask(4) + { + // Literal, format 1. + let mut control_byte = match kind { + LitKind::Str => 0b0_011, + LitKind::StrRaw(0) => 0b0_100, + LitKind::StrRaw(1) => 0b1_011, + LitKind::Integer => 0b1_100, + _ => unreachable!(), + }; + control_byte |= suffix_len << 4; + ptr = encode_symbol(ptr, &text_and_suffix, 0, symbols_map); + ptr.write::(control_byte); + } else { + // Literal, format 2. + let mut control_byte = 0b111; + let (kind, raw_count) = match kind { + LitKind::Byte => (0, None), + LitKind::Char => (1, None), + LitKind::Integer => (2, None), + LitKind::Float => (3, None), + LitKind::Str => (4, None), + LitKind::StrRaw(count) => (5, Some(count)), + LitKind::ByteStr => (6, None), + LitKind::ByteStrRaw(count) => (7, Some(count)), + LitKind::CStr => (8, None), + LitKind::CStrRaw(count) => (9, Some(count)), + LitKind::Err(()) => (10, None), + }; + control_byte |= kind << 3; + ptr = encode_symbol(ptr, &text_and_suffix, 0, symbols_map); + ptr.write::(suffix_len); + if let Some(raw_count) = raw_count { + ptr.write::(raw_count); + } + ptr.write::(control_byte); + } + ptr = encode_span(ptr, &span, span_parts_map, 0b00, false); + } + TokenTree::Subtree(Subtree { delimiter, len }) => { + let open_span_parts_index = + span_parts_map[&CompressedSpanPart::from_span(&delimiter.open)] as u32; + let close_span_parts_index = + span_parts_map[&CompressedSpanPart::from_span(&delimiter.close)] as u32; + let close_span_offset_from_open = delimiter + .close + .range + .start() + .checked_sub(delimiter.open.range.start()) + .map_or(u32::MAX, u32::from); + let children_byte_len = byte_size_after[1] - byte_size_after[1 + len as usize]; + if open_span_parts_index == close_span_parts_index + && len <= n_bits_mask(u8::BITS) + && children_byte_len <= n_bits_mask(u8::BITS) + && delimiter.open.range.len() == TextSize::new(1) + && delimiter.close.range.len() == TextSize::new(1) + && close_span_offset_from_open <= n_bits_mask(11) + { + // Subtree, format 1. + let span = Span { + range: TextRange::at( + delimiter.open.range.start(), + TextSize::new(close_span_offset_from_open), + ), + anchor: delimiter.open.anchor, + ctx: delimiter.open.ctx, + }; + let mut control_byte = 0b000; + control_byte |= (delimiter.kind as u8) << 3; + control_byte |= ((close_span_offset_from_open >> 8) << (3 + 2)) as u8; + ptr.write::(children_byte_len as u8); + ptr.write::(len as u8); + ptr.write::(control_byte); + ptr = encode_span(ptr, &span, span_parts_map, 0b00, false); + } else if open_span_parts_index == close_span_parts_index + && len <= n_bits_mask(u8::BITS) + && children_byte_len <= n_bits_mask(u8::BITS) + && delimiter.open.range.len() == delimiter.close.range.len() + && close_span_offset_from_open <= n_bits_mask(3) + { + // Subtree, format 2. + let mut control_byte = 0b001; + control_byte |= (delimiter.kind as u8) << 3; + control_byte |= (close_span_offset_from_open << (3 + 2)) as u8; + ptr.write::(children_byte_len as u8); + ptr.write::(len as u8); + ptr.write::(control_byte); + ptr = encode_span(ptr, &delimiter.open, span_parts_map, 0b00, false); + } else if len <= n_bits_mask(u8::BITS) + && children_byte_len <= n_bits_mask(12) + && delimiter.open.range.len() == delimiter.close.range.len() + && close_span_offset_from_open <= n_bits_mask(8) + && close_span_parts_index <= n_bits_mask(7) + { + // Subtree, format 3. + let mut control_byte = 0b010; + control_byte |= (delimiter.kind as u8) << 3; + control_byte |= (close_span_parts_index << (3 + 2)) as u8; + let mut children_byte_len = children_byte_len << 4; + children_byte_len |= close_span_parts_index >> 3; + ptr.write::(children_byte_len as u16); + ptr.write::(len as u8); + ptr.write::(close_span_offset_from_open as u8); + ptr.write::(control_byte); + ptr = encode_span(ptr, &delimiter.open, span_parts_map, 0b00, false); + } else { + // Subtree, format 4. + let mut control_byte = 0b101; + control_byte |= (delimiter.kind as u8) << 3; + ptr.write::(children_byte_len); + ptr.write::(len); + ptr = encode_span(ptr, &delimiter.close, span_parts_map, 0b00, false); + ptr.write::(control_byte); + ptr = encode_span(ptr, &delimiter.open, span_parts_map, 0b00, false); + } + } + } - Self { offset, len_and_parts: (len << Self::SPAN_PARTS_BIT) | span_parts_index } + let element_byte_size: u32 = ptr.len_since(before_ptr).try_into().unwrap(); + byte_size_after[0] = byte_size_after[1] + element_byte_size; } - #[inline] - fn text_range(&self) -> TextRange { - let offset = TextSize::new(self.offset); - let len = TextSize::new(self.len_and_parts >> Self::SPAN_PARTS_BIT); - TextRange::at(offset, len) + ptr +} + +/// We always encode the top subtree with the heaviest encoding because we sometimes want to change it. +unsafe fn encode_top_subtree<'a>( + mut ptr: BufferWriter<'a>, + top_subtree: Subtree, + open_span_parts_index: u32, + byte_size_after: &[u32], +) -> BufferWriter<'a> { + unsafe { + let Subtree { delimiter, len } = top_subtree; + let children_byte_len = byte_size_after[1] - byte_size_after[1 + len as usize]; + + let mut control_byte = 0b101; + control_byte |= (delimiter.kind as u8) << 3; + ptr.write::(children_byte_len); + ptr.write::(len); + ptr = encode_span_no_map( + ptr, + open_span_parts_index + 1, + delimiter.close.range.start().into(), + delimiter.close.range.len().into(), + 0b00, + true, + ); + ptr.write::(control_byte); + ptr = encode_span_no_map( + ptr, + open_span_parts_index, + delimiter.open.range.start().into(), + delimiter.open.range.len().into(), + 0b00, + true, + ); } + ptr +} - #[inline] - fn span_parts_index(&self) -> usize { - (self.len_and_parts & n_bits_mask(Self::SPAN_PARTS_BIT)) as usize +fn change_root_delimiter(buffer: &mut [u8], new_delim: DelimiterKind) { + // The span is 3*u32 and then the control byte, in which the delimiter comes. + let control_byte_index = 3 * size_of::(); + let mut control_byte = buffer[control_byte_index]; + control_byte &= 0b111; // Remove previous delimiter. + control_byte |= (new_delim as u8) << 3; + buffer[control_byte_index] = control_byte; +} + +unsafe fn change_root_spans( + buffer: &mut [u8], + open_span_parts_index: u32, + close_span_parts_index: u32, + open_range: TextRange, + close_range: TextRange, +) { + // Remember we write in reverse, so we add `3 * size_of::()`. + unsafe { + _ = encode_span_no_map( + BufferWriter::new(&mut buffer[..3 * size_of::()]), + open_span_parts_index, + u32::from(open_range.start()), + u32::from(open_range.len()), + 0b00, + true, + ); + _ = encode_span_no_map( + BufferWriter::new( + &mut buffer[..3 * size_of::() + size_of::() + 3 * size_of::()], + ), + close_span_parts_index, + u32::from(close_range.start()), + u32::from(close_range.len()), + 0b00, + true, + ); } } -impl fmt::Debug for SpanStorage64 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SpanStorage64") - .field("text_range", &self.text_range()) - .field("span_parts_index", &self.span_parts_index()) - .finish() +/// This is subtree in format 4: two spans, each at most 3*u32, a u8 control byte, a u32 length and a u32 bytes length. +const BIGGEST_POSSIBLE_TT_ENCODING: usize = + 2 * 3 * size_of::() + size_of::() + size_of::() + size_of::(); + +fn encode_all( + tts: std::vec::IntoIter, + mut compressed_span_frequencies: FxHashMap, + mut symbol_frequencies: FxHashMap, +) -> TopSubtree { + let tts_len = tts.len(); + let mut token_trees = tts.enumerate(); + let Some((_, TokenTree::Subtree(top_subtree))) = token_trees.next() else { + panic!("must always have a top subtree"); + }; + + let (span_parts, span_parts_map) = { + let mut compressed_spans = compressed_span_frequencies + .keys() + .copied() + .chain([ + CompressedSpanPart::from_span(&top_subtree.delimiter.open), + CompressedSpanPart::from_span(&top_subtree.delimiter.close), + ]) + .collect::>(); + { + // For this purpose, do not consider the top delimiters. They should stay last and not affect the other spans, + // since we might want to change them. + let len = compressed_spans.len(); + let compressed_spans = &mut compressed_spans[..len - 2]; + // No need sort if there is already enough space for everyone to be encoded efficiently. + if compressed_span_frequencies.len() > n_bits_mask(4) as usize { + // We want more used spans to have lower indices, so they can be encoded more efficiently. + compressed_spans.sort_unstable_by_key(|span| { + std::cmp::Reverse(compressed_span_frequencies[span]) + }); + } + for (index, span) in compressed_spans.iter().enumerate() { + *compressed_span_frequencies.get_mut(span).unwrap() = index; + } + } + (compressed_spans, compressed_span_frequencies) + }; + + let (symbols, symbols_map) = { + let mut symbols = symbol_frequencies.keys().cloned().collect::>(); + // No need sort if there is already enough space for everyone to be encoded efficiently. + if symbol_frequencies.len() > n_bits_mask(6) as usize { + // We want more used spans to have lower indices, so they can be encoded more efficiently. + symbols.sort_unstable_by_key(|symbol| std::cmp::Reverse(symbol_frequencies[symbol])); + } + for (index, span) in symbols.iter().enumerate() { + *symbol_frequencies.get_mut(span).unwrap() = index; + } + (symbols, symbol_frequencies) + }; + + // +1 because each `encode()` calls reads the previous value. + let mut byte_size_after = vec![0u32; tts_len + 1]; + let bytes_capacity = tts_len * BIGGEST_POSSIBLE_TT_ENCODING; + unsafe { + let mut temp_buffer = UninitBuffer::new(bytes_capacity); + let mut ptr = temp_buffer.writer(); + for (index, tt) in token_trees.rev() { + ptr = encode(ptr, tt, &span_parts_map, &mut byte_size_after[index..], &symbols_map); + } + ptr = encode_top_subtree(ptr, top_subtree, (span_parts.len() - 2) as u32, &byte_size_after); + + let writer_finish = ptr.finish(); + let buffer = temp_buffer.finish(writer_finish); + + TopSubtree { buffer, span_parts, len: tts_len, symbols } } } -impl From for SpanStorage64 { - #[inline] - fn from(value: SpanStorage32) -> Self { - SpanStorage64::new(value.text_range(), value.span_parts_index()) +fn compute_span_frequencies_and_symbols( + tts: &[TokenTree], +) -> (FxHashMap, FxHashMap) { + let mut span_frequencies = FxHashMap::default(); + let mut symbols = FxHashMap::default(); + for tt in tts { + match tt { + TokenTree::Leaf(leaf) => { + if let Some(symbol) = leaf.symbol() { + *symbols.entry(symbol.clone()).or_insert(0) += 1; + } + + *span_frequencies.entry(CompressedSpanPart::from_span(leaf.span())).or_insert(0) += + 1; + } + TokenTree::Subtree(subtree) => { + *span_frequencies + .entry(CompressedSpanPart::from_span(&subtree.delimiter.open)) + .or_insert(0) += 1; + *span_frequencies + .entry(CompressedSpanPart::from_span(&subtree.delimiter.close)) + .or_insert(0) += 1; + } + } } + (span_frequencies, symbols) } -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub(crate) struct SpanStorage96 { - offset: u32, - len: u32, - parts: u32, +#[derive(Clone, Copy)] +struct BufferReader<'a> { + #[cfg(all(debug_assertions, not(miri)))] + buffer: &'a [u8], + #[cfg(not(all(debug_assertions, not(miri))))] + ptr: *const u8, + #[cfg(not(all(debug_assertions, not(miri))))] + _marker: stdx::variance::PhantomCovariantLifetime<'a>, } -impl SpanStorage for SpanStorage96 { +impl<'a> BufferReader<'a> { #[inline] - fn can_hold(_text_range: TextRange, _span_parts_index: usize) -> bool { - true + fn start_end(slice: &'a [u8]) -> (Self, Self) { + #[cfg(all(debug_assertions, not(miri)))] + { + let start = Self { buffer: slice }; + let end = Self { buffer: &slice[slice.len()..] }; + (start, end) + } + #[cfg(not(all(debug_assertions, not(miri))))] + { + let ptrs = slice.as_ptr_range(); + let start = Self { + ptr: ptrs.start, + #[cfg(not(all(debug_assertions, not(miri))))] + _marker: stdx::variance::PhantomCovariantLifetime::new(), + }; + let end = Self { + ptr: ptrs.end, + #[cfg(not(all(debug_assertions, not(miri))))] + _marker: stdx::variance::PhantomCovariantLifetime::new(), + }; + (start, end) + } } - #[inline] - fn new(text_range: TextRange, span_parts_index: usize) -> Self { - let offset = u32::from(text_range.start()); - let len = u32::from(text_range.len()); - let span_parts_index = span_parts_index as u32; - - Self { offset, len, parts: span_parts_index } + #[cfg_attr(not(all(debug_assertions, not(miri))), inline(always))] + unsafe fn read(&mut self) -> T { + #[cfg(all(debug_assertions, not(miri)))] + { + let read_at = self.buffer.split_off(..size_of::()).unwrap(); + T::read(read_at) + } + #[cfg(not(all(debug_assertions, not(miri))))] + unsafe { + let result = self.ptr.cast::().read_unaligned(); + self.ptr = self.ptr.add(size_of::()); + result + } } - #[inline] - fn text_range(&self) -> TextRange { - let offset = TextSize::new(self.offset); - let len = TextSize::new(self.len); - TextRange::at(offset, len) + #[cfg_attr(not(all(debug_assertions, not(miri))), inline(always))] + unsafe fn skip(&mut self, amount: usize) { + #[cfg(all(debug_assertions, not(miri)))] + { + self.buffer = &self.buffer[amount..]; + } + #[cfg(not(all(debug_assertions, not(miri))))] + unsafe { + self.ptr = self.ptr.add(amount); + } } +} +impl PartialEq for BufferReader<'_> { #[inline] - fn span_parts_index(&self) -> usize { - self.parts as usize + fn eq(&self, other: &Self) -> bool { + #[cfg(all(debug_assertions, not(miri)))] + let self_ptr = self.buffer.as_ptr(); + #[cfg(not(all(debug_assertions, not(miri))))] + let self_ptr = self.ptr; + #[cfg(all(debug_assertions, not(miri)))] + let other_ptr = other.buffer.as_ptr(); + #[cfg(not(all(debug_assertions, not(miri))))] + let other_ptr = other.ptr; + + self_ptr == other_ptr } } -impl fmt::Debug for SpanStorage96 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SpanStorage96") - .field("text_range", &self.text_range()) - .field("span_parts_index", &self.span_parts_index()) - .finish() - } +struct InlineSpanParts { + span_parts_index: usize, + text_range: TextRange, } -impl From for SpanStorage96 { - #[inline] - fn from(value: SpanStorage32) -> Self { - SpanStorage96::new(value.text_range(), value.span_parts_index()) +#[inline] +unsafe fn decode_span( + ptr: BufferReader<'_>, + mut first_u32: u32, +) -> (BufferReader<'_>, InlineSpanParts) { + first_u32 >>= 2; + let span_parts_index = first_u32 & n_bits_mask(4); + let len = (first_u32 >> 4) & n_bits_mask(8); + let extends_to_next = (first_u32 & (1 << (4 + 8))) != 0; + let offset = first_u32 >> (4 + 8 + 1); + if !extends_to_next { + let result = InlineSpanParts { + span_parts_index: span_parts_index as usize, + text_range: TextRange::at(TextSize::new(offset), TextSize::new(len)), + }; + (ptr, result) + } else { + unsafe { decode_extended_span(ptr, span_parts_index, len, offset) } } } -impl From for SpanStorage96 { - #[inline] - fn from(value: SpanStorage64) -> Self { - SpanStorage96::new(value.text_range(), value.span_parts_index()) +#[cold] +unsafe fn decode_extended_span( + mut ptr: BufferReader<'_>, + mut span_parts_index: u32, + mut len: u32, + mut offset: u32, +) -> (BufferReader<'_>, InlineSpanParts) { + unsafe { + let mut second_u32 = ptr.read::(); + let extends_to_next = (second_u32 & 0b1) != 0; + second_u32 >>= 1; + if extends_to_next { + let third_u32 = ptr.read::(); + let u64 = u64::from(third_u32) | (u64::from(second_u32) << u32::BITS); + let rest_span_parts_index = (u64 & n_bits_mask_u64(24)) as u32; + span_parts_index |= rest_span_parts_index << 4; + let rest_len = ((u64 >> 24) & n_bits_mask_u64(24)) as u32; + len |= rest_len << 8; + let rest_offset = (u64 >> (24 + 24)) as u32; + offset |= rest_offset << 17; + } else { + let rest_span_parts_index = second_u32 & n_bits_mask(11); + span_parts_index |= rest_span_parts_index << 4; + let rest_len = (second_u32 >> 11) & n_bits_mask(10); + len |= rest_len << 8; + let rest_offset = second_u32 >> (11 + 10); + offset |= rest_offset << 17; + }; + let result = InlineSpanParts { + span_parts_index: span_parts_index as usize, + text_range: TextRange::at(TextSize::new(offset), TextSize::new(len)), + }; + (ptr, result) } } -// We don't use structs or enum nesting here to save padding. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(crate) enum TokenTree { - Literal { text_and_suffix: Symbol, span: S, kind: LitKind, suffix_len: u8 }, - Punct { char: char, spacing: Spacing, span: S }, - Ident { sym: Symbol, span: S, is_raw: IdentIsRaw }, - Subtree { len: u32, delim_kind: DelimiterKind, open_span: S, close_span: S }, +// FIXME: It'll probably be better to ensure this ourselves via a `#[repr(C, align(4))]` wrapper, even though practically +// this holds for all 32- and 64-bit targets (Rust does not guarantee this). +const _: () = assert!(align_of::<*const *const str>() >= 4); // Needed for the tagging of idents. + +unsafe fn decode_symbol<'a>( + mut ptr: BufferReader<'a>, + first_byte: u8, + symbols: &[Symbol], +) -> (BufferReader<'a>, Symbol) { + let mut symbol_idx = u32::from(first_byte) >> 1; + let extends_to_next = symbol_idx & 0b1 == 0b1; + symbol_idx >>= 1; + if extends_to_next { + let mut second_byte = unsafe { ptr.read::() }; + let rest_bytes = + if second_byte & 0b1 == 0b1 { u32::from(unsafe { ptr.read::() }) } else { 0 }; + second_byte >>= 1; + symbol_idx |= u32::from(second_byte) << 6; + symbol_idx |= rest_bytes << 13; + } + (ptr, symbols[symbol_idx as usize].clone()) } -impl TokenTree { - #[inline] - pub(crate) fn first_span(&self) -> &S { - match self { - TokenTree::Literal { span, .. } => span, - TokenTree::Punct { span, .. } => span, - TokenTree::Ident { span, .. } => span, - TokenTree::Subtree { open_span, .. } => open_span, +/// We need `MaybeUninit` to preserve provenance. +/// +/// The returned `u32` is the length of the children *in bytes*, if we read a subtree. Otherwise it's zero. +unsafe fn decode<'a>( + mut ptr: BufferReader<'a>, + compressed: &[CompressedSpanPart], + symbols: &[Symbol], +) -> (BufferReader<'a>, TokenTree, u32) { + unsafe { + let span_and_extra = ptr.read::(); + let span; + (ptr, span) = decode_span(ptr, span_and_extra); + let span = compressed[span.span_parts_index].recombine(span.text_range); + + if span_and_extra & 0b1 == 0b1 { + // An ASCII punct. + let char_and_half_spacing = ptr.read::(); + + let spacing = (span_and_extra & 0b10) | (u32::from(char_and_half_spacing) & 0b1); + let spacing = transmute::(spacing as u8); + + let char = char::from(char_and_half_spacing >> 1); + + return (ptr, TokenTree::Leaf(Leaf::Punct(Punct { char, spacing, span })), 0); + } else if span_and_extra & 0b10 == 0b10 { + // An ident. + let symbol_first_byte = ptr.read::(); + let is_raw = symbol_first_byte & 0b1; + let symbol; + (ptr, symbol) = decode_symbol(ptr, symbol_first_byte, symbols); + let is_raw = transmute::(is_raw); + + return (ptr, TokenTree::Leaf(Leaf::Ident(Ident { sym: symbol, span, is_raw })), 0); } - } - #[inline] - pub(crate) fn last_span(&self) -> &S { - match self { - TokenTree::Literal { span, .. } => span, - TokenTree::Punct { span, .. } => span, - TokenTree::Ident { span, .. } => span, - TokenTree::Subtree { close_span, .. } => close_span, - } - } + let mut children_byte_len = 0; + + let control_byte = u32::from(ptr.read::()); + let control_byte_extra_data = control_byte >> 3; + let result = match control_byte & 0b111 { + 0b000 => { + // Subtree, format 1: + // - Same span_parts_index for open and close span. + // - Subtree length is a u8. + // - Subtree length in bytes is a u8. + // - The length of both the open and close span is 1 - so we use the already-parsed length for the open span + // for other things (we can only assume it has 8 bits available, the minimum format for a length). + // - The offset between the open span's start and the close span's start is stored in 11 bits. + let kind = transmute::((control_byte_extra_data & 0b11) as u8); + let mut open_span = span; + let mut close_span_offset_from_open = u32::from(span.range.len()); + open_span.range = TextRange::at(open_span.range.start(), TextSize::new(1)); + close_span_offset_from_open |= (control_byte_extra_data >> 2) << 8; + let close_span = Span { + range: open_span.range + TextSize::new(close_span_offset_from_open), + anchor: open_span.anchor, + ctx: open_span.ctx, + }; + let len = u32::from(ptr.read::()); + children_byte_len = u32::from(ptr.read::()); - #[inline] - pub(crate) fn to_api(&self, span_parts: &[CompressedSpanPart]) -> crate::TokenTree { - match self { - TokenTree::Literal { text_and_suffix, span, kind, suffix_len } => { - crate::TokenTree::Leaf(crate::Leaf::Literal(crate::Literal { - text_and_suffix: text_and_suffix.clone(), - span: span.span(span_parts), - kind: *kind, - suffix_len: *suffix_len, - })) + TokenTree::Subtree(Subtree { + delimiter: Delimiter { open: open_span, close: close_span, kind }, + len, + }) } - TokenTree::Punct { char, spacing, span } => { - crate::TokenTree::Leaf(crate::Leaf::Punct(crate::Punct { - char: *char, - spacing: *spacing, - span: span.span(span_parts), - })) + 0b001 => { + // Subtree, format 2: + // - Same span_parts_index for open and close span. + // - Subtree length is a u8. + // - Subtree length in bytes is a u8. + // - The open and close span have the same length. This covers many cases because most cases either give + // both length 1 (the brackets themselves) or the same span (usually, the whole range they encompass). + // - The offset between the open span's start and the close span's start is stored in 3 bits. + let kind = transmute::((control_byte_extra_data & 0b11) as u8); + let open_span = span; + let close_span_offset_from_open = control_byte_extra_data >> 2; + let close_span = Span { + range: open_span.range + TextSize::new(close_span_offset_from_open), + anchor: open_span.anchor, + ctx: open_span.ctx, + }; + let len = u32::from(ptr.read::()); + children_byte_len = u32::from(ptr.read::()); + + TokenTree::Subtree(Subtree { + delimiter: Delimiter { open: open_span, close: close_span, kind }, + len, + }) } - TokenTree::Ident { sym, span, is_raw } => { - crate::TokenTree::Leaf(crate::Leaf::Ident(crate::Ident { - sym: sym.clone(), - span: span.span(span_parts), - is_raw: *is_raw, - })) + 0b010 => { + // Subtree, format 3: + // - Subtree length is a u8. + // - Subtree length in bytes is 12 bits. + // - The open and close span have the same length. + // - The offset between the open span's start and the close span's start is stored in 8 bits. + // - The close span's span_parts_index is stored in 7 bits. + // This is less efficient than formats 1 and 2 (requires two more bytes), but more efficient than the general format. + let kind = transmute::((control_byte_extra_data & 0b11) as u8); + let open_span = span; + let close_span_offset_from_open = u32::from(ptr.read::()); + let len = u32::from(ptr.read::()); + children_byte_len = u32::from(ptr.read::()); + let mut close_span_parts_index = control_byte_extra_data >> 2; + close_span_parts_index |= (children_byte_len & 0b1111) << 3; + children_byte_len >>= 4; + let close_span = compressed[close_span_parts_index as usize] + .recombine(open_span.range + TextSize::new(close_span_offset_from_open)); + + TokenTree::Subtree(Subtree { + delimiter: Delimiter { open: open_span, close: close_span, kind }, + len, + }) } - TokenTree::Subtree { len, delim_kind, open_span, close_span } => { - crate::TokenTree::Subtree(crate::Subtree { - delimiter: crate::Delimiter { - open: open_span.span(span_parts), - close: close_span.span(span_parts), - kind: *delim_kind, - }, - len: *len, + 0b101 => { + cold_path(); + + // Subtree, format 4 - most general format: subtree length is u32, subtree length in bytes is u32, full decoded + // span for close span, delimiter kind is 5 bits (not needed now, will be needed when we have different kinds of + // invisible delimiters). + let kind = transmute::(control_byte_extra_data as u8); + let open_span = span; + let close_span_start = ptr.read::(); + let close_span; + (ptr, close_span) = decode_span(ptr, close_span_start); + let close_span = + compressed[close_span.span_parts_index].recombine(close_span.text_range); + let len = ptr.read::(); + children_byte_len = ptr.read::(); + + TokenTree::Subtree(Subtree { + delimiter: Delimiter { open: open_span, close: close_span, kind }, + len, }) } - } - } + 0b110 => { + cold_path(); - #[inline] - fn convert>(self) -> TokenTree { - match self { - TokenTree::Literal { text_and_suffix, span, kind, suffix_len } => { - TokenTree::Literal { text_and_suffix, span: span.into(), kind, suffix_len } + // Non-ASCII punct. Extremely rare but technically possible. + let spacing = transmute::(control_byte_extra_data as u8); + let char = ptr.read::(); + + TokenTree::Leaf(Leaf::Punct(Punct { char, spacing, span })) } - TokenTree::Punct { char, spacing, span } => { - TokenTree::Punct { char, spacing, span: span.into() } + 0b011 | 0b100 => { + // Literal, format 1: the 6 bits remaining from `control_byte` decide the kind and the suffix len from a constant set + // (of the most common). + let control_byte_extra_data = control_byte >> 2; + let text_and_suffix_first_byte = ptr.read::(); + let text_and_suffix; + (ptr, text_and_suffix) = decode_symbol(ptr, text_and_suffix_first_byte, symbols); + let kind = match control_byte_extra_data & 0b11 { + 0b00 => LitKind::Str, + 0b01 => LitKind::StrRaw(0), + 0b10 => LitKind::StrRaw(1), + 0b11 => LitKind::Integer, + _ => unreachable!(), + }; + let suffix_len = (control_byte_extra_data >> 2) as u8; + + TokenTree::Leaf(Leaf::Literal(Literal { text_and_suffix, span, kind, suffix_len })) } - TokenTree::Ident { sym, span, is_raw } => { - TokenTree::Ident { sym, span: span.into(), is_raw } + 0b111 => { + cold_path(); + + // Literal, format 2: most general format. + let kind = match control_byte_extra_data { + 0 => LitKind::Byte, + 1 => LitKind::Char, + 2 => LitKind::Integer, + 3 => LitKind::Float, + 4 => LitKind::Str, + 5 => LitKind::StrRaw(ptr.read::()), + 6 => LitKind::ByteStr, + 7 => LitKind::ByteStrRaw(ptr.read::()), + 8 => LitKind::CStr, + 9 => LitKind::CStrRaw(ptr.read::()), + 10 => LitKind::Err(()), + _ => unreachable!(), + }; + let suffix_len = ptr.read::(); + let text_and_suffix_first_byte = ptr.read::(); + let text_and_suffix; + (ptr, text_and_suffix) = decode_symbol(ptr, text_and_suffix_first_byte, symbols); + + TokenTree::Leaf(Leaf::Literal(Literal { text_and_suffix, span, kind, suffix_len })) } - TokenTree::Subtree { len, delim_kind, open_span, close_span } => TokenTree::Subtree { - len, - delim_kind, - open_span: open_span.into(), - close_span: close_span.into(), - }, - } + _ => unreachable!(), + }; + (ptr, result, children_byte_len) } } -// This is used a lot, make sure it doesn't grow unintentionally. -const _: () = { - assert!(size_of::>() == 16); - assert!(size_of::>() == 24); - assert!(size_of::>() == 32); -}; +#[derive(Clone, Copy)] +pub(crate) struct TokenTreesSlice<'a> { + current: BufferReader<'a>, + end: BufferReader<'a>, + span_parts: &'a [CompressedSpanPart], + symbols: &'a [Symbol], +} -#[rust_analyzer::macro_style(braces)] -macro_rules! dispatch { - ( - match $scrutinee:expr => $tt:ident => $body:expr - ) => { - match $scrutinee { - TopSubtreeRepr::SpanStorage32($tt) => $body, - TopSubtreeRepr::SpanStorage64($tt) => $body, - TopSubtreeRepr::SpanStorage96($tt) => $body, +unsafe impl Send for TokenTreesSlice<'_> {} +unsafe impl Sync for TokenTreesSlice<'_> {} + +impl<'a> TokenTreesSlice<'a> { + #[inline] + fn new(top_subtree: &'a TopSubtree) -> Self { + let (current, end) = BufferReader::start_end(&top_subtree.buffer); + Self { current, end, span_parts: &top_subtree.span_parts, symbols: &top_subtree.symbols } + } + + #[inline] + pub(crate) fn empty() -> Self { + let (current, end) = BufferReader::start_end(&[]); + Self { current, end, span_parts: &[], symbols: &[] } + } + + pub(crate) fn advance(&mut self) -> Option { + if self.current == self.end { + return None; } - }; -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(crate) enum TopSubtreeRepr { - SpanStorage32(Box<[TokenTree]>), - SpanStorage64(Box<[TokenTree]>), - SpanStorage96(Box<[TokenTree]>), + let (new_current, token_tree, _children_byte_len) = + unsafe { decode(self.current, self.span_parts, self.symbols) }; + self.current = new_current; + Some(token_tree) + } + + /// This is like `advance()`, but when encountering a subtree, it changes `self` to skip it and returns a + /// slice into it (what `advance()` would have done to `self`). + pub(crate) fn advance_skip_subtree(&mut self) -> Option<(TokenTree, TokenTreesSlice<'a>)> { + if self.current == self.end { + return None; + } + + let (new_current, token_tree, children_byte_len) = + unsafe { decode(self.current, self.span_parts, self.symbols) }; + self.current = new_current; + let subtree_slice = *self; + unsafe { self.current.skip(children_byte_len as usize) }; + Some((token_tree, subtree_slice)) + } + + pub(crate) fn iter(mut self) -> impl Iterator { + std::iter::from_fn(move || self.advance()) + } } #[derive(Clone, PartialEq, Eq, Hash)] pub struct TopSubtree { - repr: TopSubtreeRepr, + buffer: Box<[u8]>, + /// The last two are the top subtree's open and close span, in this order. span_parts: Box<[CompressedSpanPart]>, + symbols: Box<[Symbol]>, + len: usize, } impl TopSubtree { pub fn empty(span: DelimSpan) -> Self { - Self { - repr: TopSubtreeRepr::SpanStorage96(Box::new([TokenTree::Subtree { + encode_all( + vec![TokenTree::Subtree(Subtree { + delimiter: Delimiter::invisible_delim_spanned(span), len: 0, - delim_kind: DelimiterKind::Invisible, - open_span: SpanStorage96::new(span.open.range, 0), - close_span: SpanStorage96::new(span.close.range, 1), - }])), - span_parts: Box::new([ - CompressedSpanPart::from_span(&span.open), - CompressedSpanPart::from_span(&span.close), - ]), - } - } - - pub fn invisible_from_leaves( - delim_span: Span, - leaves: [crate::Leaf; N], - ) -> Self { - let mut builder = TopSubtreeBuilder::new(crate::Delimiter::invisible_spanned(delim_span)); - builder.extend(leaves); - builder.build() + })] + .into_iter(), + FxHashMap::default(), + FxHashMap::default(), + ) } - pub fn from_token_trees(delimiter: crate::Delimiter, token_trees: TokenTreesView<'_>) -> Self { + pub fn invisible_from_leaves(delim_span: Span, leaves: [Leaf; N]) -> Self { + Self::from_serialized( + std::iter::chain( + [TokenTree::Subtree(Subtree { + delimiter: Delimiter::invisible_spanned(delim_span), + len: leaves.len() as u32, + })], + leaves.into_iter().map(TokenTree::Leaf), + ) + .collect(), + ) + } + + pub fn from_token_trees(delimiter: Delimiter, token_trees: TokenTreesView<'_>) -> Self { let mut builder = TopSubtreeBuilder::new(delimiter); builder.extend_with_tt(token_trees); builder.build() } - pub fn from_serialized(tt: Vec) -> Self { - let mut tt = tt.into_iter(); - let Some(crate::TokenTree::Subtree(top_subtree)) = tt.next() else { - panic!("first must always come the top subtree") - }; - let mut builder = TopSubtreeBuilder::new(top_subtree.delimiter); - for tt in tt { - builder.push_token_tree(tt); - } - builder.build() + pub fn from_serialized(tts: Vec) -> Self { + let (span_frequencies, symbols) = compute_span_frequencies_and_symbols(&tts[1..]); // Do not include the top subtree. + encode_all(tts.into_iter(), span_frequencies, symbols) } pub fn from_subtree(subtree: SubtreeView<'_>) -> Self { @@ -429,121 +1131,44 @@ impl TopSubtree { } pub fn view(&self) -> SubtreeView<'_> { - let repr = match &self.repr { - TopSubtreeRepr::SpanStorage32(token_trees) => { - TokenTreesReprRef::SpanStorage32(token_trees) - } - TopSubtreeRepr::SpanStorage64(token_trees) => { - TokenTreesReprRef::SpanStorage64(token_trees) - } - TopSubtreeRepr::SpanStorage96(token_trees) => { - TokenTreesReprRef::SpanStorage96(token_trees) - } - }; - SubtreeView(TokenTreesView { repr, span_parts: &self.span_parts }) + let slice = TokenTreesSlice::new(self); + SubtreeView(TokenTreesView { slice, len: self.len }) } pub fn iter(&self) -> TtIter<'_> { self.view().iter() } - pub fn top_subtree(&self) -> crate::Subtree { + pub fn top_subtree(&self) -> Subtree { self.view().top_subtree() } pub fn set_top_subtree_delimiter_kind(&mut self, kind: DelimiterKind) { - dispatch! { - match &mut self.repr => tt => { - let TokenTree::Subtree { delim_kind, .. } = &mut tt[0] else { - unreachable!("the first token tree is always the top subtree"); - }; - *delim_kind = kind; - } - } - } - - fn ensure_can_hold(&mut self, range: TextRange) { - fn can_hold(_: &[TokenTree], range: TextRange) -> bool { - S::can_hold(range, 0) - } - let can_hold = dispatch! { - match &self.repr => tt => can_hold(tt, range) - }; - if can_hold { - return; - } - - // Otherwise, we do something very junky: recreate the entire tree. Hopefully this should be rare. - let mut builder = TopSubtreeBuilder::new(self.top_subtree().delimiter); - builder.extend_with_tt(self.token_trees()); - builder.ensure_can_hold(range, 0); - *self = builder.build(); + change_root_delimiter(&mut self.buffer, kind); } pub fn set_top_subtree_delimiter_span(&mut self, span: DelimSpan) { - self.ensure_can_hold(span.open.range); - self.ensure_can_hold(span.close.range); - fn do_it(tt: &mut [TokenTree], span: DelimSpan) { - let TokenTree::Subtree { open_span, close_span, .. } = &mut tt[0] else { - unreachable!() - }; - *open_span = S::new(span.open.range, 0); - *close_span = S::new(span.close.range, 1); - } - dispatch! { - match &mut self.repr => tt => do_it(tt, span) - } - self.span_parts[0] = CompressedSpanPart::from_span(&span.open); - self.span_parts[1] = CompressedSpanPart::from_span(&span.close); - } - - /// Note: this cannot change spans. - pub fn set_token(&mut self, idx: usize, leaf: crate::Leaf) { - fn do_it( - tt: &mut [TokenTree], - idx: usize, - span_parts: &[CompressedSpanPart], - leaf: crate::Leaf, - ) { - assert!( - !matches!(tt[idx], TokenTree::Subtree { .. }), - "`TopSubtree::set_token()` must be called on a leaf" - ); - let existing_span_compressed = *tt[idx].first_span(); - let existing_span = existing_span_compressed.span(span_parts); - assert_eq!( - *leaf.span(), - existing_span, - "`TopSubtree::set_token()` cannot change spans" + let open_span_idx = self.span_parts.len() - 2; + let close_span_idx = open_span_idx + 1; + unsafe { + change_root_spans( + &mut self.buffer, + open_span_idx as u32, + close_span_idx as u32, + span.open.range, + span.close.range, ); - match leaf { - crate::Leaf::Literal(leaf) => { - tt[idx] = TokenTree::Literal { - text_and_suffix: leaf.text_and_suffix, - span: existing_span_compressed, - kind: leaf.kind, - suffix_len: leaf.suffix_len, - } - } - crate::Leaf::Punct(leaf) => { - tt[idx] = TokenTree::Punct { - char: leaf.char, - spacing: leaf.spacing, - span: existing_span_compressed, - } - } - crate::Leaf::Ident(leaf) => { - tt[idx] = TokenTree::Ident { - sym: leaf.sym, - span: existing_span_compressed, - is_raw: leaf.is_raw, - } - } - } - } - dispatch! { - match &mut self.repr => tt => do_it(tt, idx, &self.span_parts, leaf) } + self.span_parts[open_span_idx] = CompressedSpanPart::from_span(&span.open); + self.span_parts[close_span_idx] = CompressedSpanPart::from_span(&span.close); + } + + /// **Warning**: This is very expensive, this rebuilds the whole tree. Avoid using this if you can. + pub fn set_token(&mut self, idx: usize, leaf: Leaf) { + let mut tts = TokenTreesSlice::new(self).iter().collect::>(); + assert_matches!(tts[idx], TokenTree::Leaf(_), "cannot change a subtree to a leaf"); + tts[idx] = leaf.into(); + *self = TopSubtree::from_serialized(tts); } pub fn token_trees(&self) -> TokenTreesView<'_> { @@ -561,368 +1186,172 @@ impl TopSubtree { } } -#[rust_analyzer::macro_style(braces)] -macro_rules! dispatch_builder { - ( - match $scrutinee:expr => $tt:ident => $body:expr - ) => { - match $scrutinee { - TopSubtreeBuilderRepr::SpanStorage32($tt) => $body, - TopSubtreeBuilderRepr::SpanStorage64($tt) => $body, - TopSubtreeBuilderRepr::SpanStorage96($tt) => $body, - } - }; -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -enum TopSubtreeBuilderRepr { - SpanStorage32(Vec>), - SpanStorage64(Vec>), - SpanStorage96(Vec>), -} - -type FxIndexSet = indexmap::IndexSet; - -/// In any tree, the first two subtree parts are reserved for the top subtree. -/// -/// We do it because `TopSubtree` exposes an API to modify the top subtree, therefore it's more convenient -/// this way, and it's unlikely to affect memory usage. -const RESERVED_SPAN_PARTS_LEN: usize = 2; - #[derive(Debug, Clone)] pub struct TopSubtreeBuilder { unclosed_subtree_indices: Vec, - token_trees: TopSubtreeBuilderRepr, - span_parts: FxIndexSet, + token_trees: Vec, + span_parts_frequencies: FxHashMap, last_closed_subtree: Option, - /// We need to keep those because they are not inside `span_parts`, see [`RESERVED_SPAN_PARTS_LEN`]. - top_subtree_spans: DelimSpan, + symbol_frequencies: FxHashMap, } impl TopSubtreeBuilder { - pub fn new(top_delimiter: crate::Delimiter) -> Self { - let mut result = Self { - unclosed_subtree_indices: Vec::new(), - token_trees: TopSubtreeBuilderRepr::SpanStorage32(Vec::new()), - span_parts: FxIndexSet::default(), - last_closed_subtree: None, - top_subtree_spans: top_delimiter.delim_span(), + fn insert_span(&mut self, span: &Span) { + *self.span_parts_frequencies.entry(CompressedSpanPart::from_span(span)).or_insert(0) += 1; + } + + fn remove_span(span_parts_frequencies: &mut FxHashMap, span: &Span) { + let hash_map::Entry::Occupied(mut entry) = + span_parts_frequencies.entry(CompressedSpanPart::from_span(span)) + else { + panic!("span not present"); }; - result.ensure_can_hold(top_delimiter.open.range, 0); - result.ensure_can_hold(top_delimiter.close.range, 1); - fn push_first(tt: &mut Vec>, top_delimiter: crate::Delimiter) { - tt.push(TokenTree::Subtree { - len: 0, - delim_kind: top_delimiter.kind, - open_span: S::new(top_delimiter.open.range, 0), - close_span: S::new(top_delimiter.close.range, 1), - }); + *entry.get_mut() -= 1; + if *entry.get() == 0 { + entry.remove(); } - dispatch_builder! { - match &mut result.token_trees => tt => push_first(tt, top_delimiter) - } - result } - fn span_part_index(&mut self, part: CompressedSpanPart) -> usize { - self.span_parts.insert_full(part).0 + RESERVED_SPAN_PARTS_LEN + fn insert_symbol(&mut self, symbol: Symbol) { + *self.symbol_frequencies.entry(symbol).or_insert(0) += 1; } - fn switch_repr>(repr: &mut Vec>) -> Vec> { - let repr = std::mem::take(repr); - repr.into_iter().map(|tt| tt.convert()).collect() + fn remove_symbol(symbol_frequencies: &mut FxHashMap, symbol: Symbol) { + let hash_map::Entry::Occupied(mut entry) = symbol_frequencies.entry(symbol) else { + panic!("span not present"); + }; + *entry.get_mut() -= 1; + if *entry.get() == 0 { + entry.remove(); + } } - /// Ensures we have a representation that can hold these values. - fn ensure_can_hold(&mut self, text_range: TextRange, span_parts_index: usize) { - match &mut self.token_trees { - TopSubtreeBuilderRepr::SpanStorage32(token_trees) => { - if SpanStorage32::can_hold(text_range, span_parts_index) { - // Can hold. - } else if SpanStorage64::can_hold(text_range, span_parts_index) { - self.token_trees = - TopSubtreeBuilderRepr::SpanStorage64(Self::switch_repr(token_trees)); - } else { - self.token_trees = - TopSubtreeBuilderRepr::SpanStorage96(Self::switch_repr(token_trees)); - } - } - TopSubtreeBuilderRepr::SpanStorage64(token_trees) => { - if SpanStorage64::can_hold(text_range, span_parts_index) { - // Can hold. - } else { - self.token_trees = - TopSubtreeBuilderRepr::SpanStorage96(Self::switch_repr(token_trees)); - } - } - TopSubtreeBuilderRepr::SpanStorage96(_) => { - // Can hold anything. - } - } + pub fn new(top_delimiter: Delimiter) -> Self { + let mut result = Self { + unclosed_subtree_indices: Vec::new(), + token_trees: Vec::new(), + span_parts_frequencies: FxHashMap::default(), + last_closed_subtree: None, + symbol_frequencies: FxHashMap::default(), + }; + // Do not insert the top delimiters, they have their own place because we sometimes need to change them. + result.token_trees.push(TokenTree::Subtree(Subtree { delimiter: top_delimiter, len: 0 })); + result } /// Not to be exposed, this assumes the subtree's children will be filled in immediately. - fn push_subtree(&mut self, subtree: crate::Subtree) { - let open_span_parts_index = - self.span_part_index(CompressedSpanPart::from_span(&subtree.delimiter.open)); - self.ensure_can_hold(subtree.delimiter.open.range, open_span_parts_index); - let close_span_parts_index = - self.span_part_index(CompressedSpanPart::from_span(&subtree.delimiter.close)); - self.ensure_can_hold(subtree.delimiter.close.range, close_span_parts_index); - fn do_it( - tt: &mut Vec>, - open_span_parts_index: usize, - close_span_parts_index: usize, - subtree: crate::Subtree, - ) { - let open_span = S::new(subtree.delimiter.open.range, open_span_parts_index); - let close_span = S::new(subtree.delimiter.close.range, close_span_parts_index); - tt.push(TokenTree::Subtree { - len: subtree.len, - delim_kind: subtree.delimiter.kind, - open_span, - close_span, - }); - } - dispatch_builder! { - match &mut self.token_trees => tt => do_it(tt, open_span_parts_index, close_span_parts_index, subtree) - } + fn push_subtree(&mut self, subtree: Subtree) { + self.insert_span(&subtree.delimiter.open); + self.insert_span(&subtree.delimiter.close); + self.token_trees.push(subtree.into()); } pub fn open(&mut self, delimiter_kind: DelimiterKind, open_span: Span) { - let span_parts_index = self.span_part_index(CompressedSpanPart::from_span(&open_span)); - self.ensure_can_hold(open_span.range, span_parts_index); - fn do_it( - token_trees: &mut Vec>, - delimiter_kind: DelimiterKind, - range: TextRange, - span_parts_index: usize, - ) -> usize { - let open_span = S::new(range, span_parts_index); - token_trees.push(TokenTree::Subtree { - len: 0, - delim_kind: delimiter_kind, - open_span, - close_span: open_span, // Will be overwritten on close. - }); - token_trees.len() - 1 - } - let subtree_idx = dispatch_builder! { - match &mut self.token_trees => tt => do_it(tt, delimiter_kind, open_span.range, span_parts_index) - }; + self.insert_span(&open_span); + let subtree_idx = self.token_trees.len(); + self.token_trees.push(TokenTree::Subtree(Subtree { + delimiter: Delimiter { open: open_span, close: open_span, kind: delimiter_kind }, + len: 0, // Will be overwritten on close. + })); self.unclosed_subtree_indices.push(subtree_idx); } pub fn close(&mut self, close_span: Span) { - let span_parts_index = self.span_part_index(CompressedSpanPart::from_span(&close_span)); - let range = close_span.range; - self.ensure_can_hold(range, span_parts_index); + self.insert_span(&close_span); let last_unclosed_index = self .unclosed_subtree_indices .pop() .expect("attempt to close a `tt::Subtree` when none is open"); - fn do_it( - token_trees: &mut [TokenTree], - last_unclosed_index: usize, - range: TextRange, - span_parts_index: usize, - ) { - let token_trees_len = token_trees.len(); - let TokenTree::Subtree { len, delim_kind: _, open_span: _, close_span } = - &mut token_trees[last_unclosed_index] - else { - unreachable!("unclosed token tree is always a subtree"); - }; - *len = (token_trees_len - last_unclosed_index - 1) as u32; - *close_span = S::new(range, span_parts_index); - } - dispatch_builder! { - match &mut self.token_trees => tt => do_it(tt, last_unclosed_index, range, span_parts_index) - } + let token_trees_len = self.token_trees.len(); + let TokenTree::Subtree(Subtree { delimiter: Delimiter { open: _, close, kind: _ }, len }) = + &mut self.token_trees[last_unclosed_index] + else { + unreachable!("unclosed token tree is always a subtree"); + }; + *len = (token_trees_len - last_unclosed_index - 1) as u32; + *close = close_span; self.last_closed_subtree = Some(last_unclosed_index); } /// You cannot call this consecutively, it will only work once after close. pub fn remove_last_subtree_if_invisible(&mut self) { let Some(last_subtree_idx) = self.last_closed_subtree else { return }; - fn do_it(tt: &mut Vec>, last_subtree_idx: usize) { - if let TokenTree::Subtree { delim_kind: DelimiterKind::Invisible, .. } = - tt[last_subtree_idx] - { - tt.remove(last_subtree_idx); - } - } - dispatch_builder! { - match &mut self.token_trees => tt => do_it(tt, last_subtree_idx) + if let TokenTree::Subtree(Subtree { + delimiter: Delimiter { kind: DelimiterKind::Invisible, .. }, + .. + }) = self.token_trees[last_subtree_idx] + { + self.token_trees.remove(last_subtree_idx); } self.last_closed_subtree = None; } - fn push_literal(&mut self, leaf: crate::Literal) { - let span_parts_index = self.span_part_index(CompressedSpanPart::from_span(&leaf.span)); - let range = leaf.span.range; - self.ensure_can_hold(range, span_parts_index); - fn do_it( - tt: &mut Vec>, - range: TextRange, - span_parts_index: usize, - leaf: crate::Literal, - ) { - tt.push(TokenTree::Literal { - text_and_suffix: leaf.text_and_suffix, - span: S::new(range, span_parts_index), - kind: leaf.kind, - suffix_len: leaf.suffix_len, - }) - } - dispatch_builder! { - match &mut self.token_trees => tt => do_it(tt, range, span_parts_index, leaf) - } - } - - fn push_punct(&mut self, leaf: crate::Punct) { - let span_parts_index = self.span_part_index(CompressedSpanPart::from_span(&leaf.span)); - let range = leaf.span.range; - self.ensure_can_hold(range, span_parts_index); - fn do_it( - tt: &mut Vec>, - range: TextRange, - span_parts_index: usize, - leaf: crate::Punct, - ) { - tt.push(TokenTree::Punct { - char: leaf.char, - spacing: leaf.spacing, - span: S::new(range, span_parts_index), - }) - } - dispatch_builder! { - match &mut self.token_trees => tt => do_it(tt, range, span_parts_index, leaf) - } - } - - fn push_ident(&mut self, leaf: crate::Ident) { - let span_parts_index = self.span_part_index(CompressedSpanPart::from_span(&leaf.span)); - let range = leaf.span.range; - self.ensure_can_hold(range, span_parts_index); - fn do_it( - tt: &mut Vec>, - range: TextRange, - span_parts_index: usize, - leaf: crate::Ident, - ) { - tt.push(TokenTree::Ident { - sym: leaf.sym, - span: S::new(range, span_parts_index), - is_raw: leaf.is_raw, - }) - } - dispatch_builder! { - match &mut self.token_trees => tt => do_it(tt, range, span_parts_index, leaf) - } - } - - pub fn push(&mut self, leaf: crate::Leaf) { - match leaf { - crate::Leaf::Literal(leaf) => self.push_literal(leaf), - crate::Leaf::Punct(leaf) => self.push_punct(leaf), - crate::Leaf::Ident(leaf) => self.push_ident(leaf), - } - } - - fn push_token_tree(&mut self, tt: crate::TokenTree) { + pub fn push(&mut self, leaf: Leaf) { + self.insert_span(leaf.span()); + if let Some(symbol) = leaf.symbol() { + self.insert_symbol(symbol.clone()); + } + self.token_trees.push(leaf.into()); + } + + fn push_token_tree(&mut self, tt: TokenTree) { match tt { - crate::TokenTree::Leaf(leaf) => self.push(leaf), - crate::TokenTree::Subtree(subtree) => self.push_subtree(subtree), + TokenTree::Leaf(leaf) => self.push(leaf), + TokenTree::Subtree(subtree) => self.push_subtree(subtree), } } - pub fn extend(&mut self, leaves: impl IntoIterator) { + pub fn extend(&mut self, leaves: impl IntoIterator) { leaves.into_iter().for_each(|leaf| self.push(leaf)); } pub fn extend_with_tt(&mut self, tt: TokenTreesView<'_>) { - fn do_it( - this: &mut TopSubtreeBuilder, - tt: &[TokenTree], - span_parts: &[CompressedSpanPart], - ) { - for tt in tt { - this.push_token_tree(tt.to_api(span_parts)); - } - } - dispatch_ref! { - match tt.repr => tt_repr => do_it(self, tt_repr, tt.span_parts) - } + tt.iter_flat_tokens().for_each(|tt| self.push_token_tree(tt)); } /// Like [`Self::extend_with_tt()`], but makes sure the new tokens will never be /// joint with whatever comes after them. pub fn extend_with_tt_alone(&mut self, tt: TokenTreesView<'_>) { self.extend_with_tt(tt); - fn do_it(tt: &mut [TokenTree]) { - if let Some(TokenTree::Punct { spacing, .. }) = tt.last_mut() { - *spacing = Spacing::Alone; - } - } - if !tt.is_empty() { - dispatch_builder! { - match &mut self.token_trees => tt => do_it(tt) - } + if !tt.is_empty() + && let Some(TokenTree::Leaf(Leaf::Punct(Punct { spacing, .. }))) = + self.token_trees.last_mut() + { + *spacing = Spacing::Alone; } } pub fn expected_delimiters(&self) -> impl Iterator { self.unclosed_subtree_indices.iter().rev().map(|&subtree_idx| { - dispatch_builder! { - match &self.token_trees => tt => { - let TokenTree::Subtree { delim_kind, .. } = tt[subtree_idx] else { - unreachable!("unclosed token tree is always a subtree") - }; - delim_kind - } - } + let TokenTree::Subtree(Subtree { delimiter, .. }) = self.token_trees[subtree_idx] + else { + unreachable!("unclosed token tree is always a subtree") + }; + delimiter.kind }) } /// Builds, and remove the top subtree if it has only one subtree child. pub fn build_skip_top_subtree(mut self) -> TopSubtree { - fn remove_first_if_needed( - tt: &mut Vec>, - top_delim_span: &mut DelimSpan, - span_parts: &FxIndexSet, - ) { - let tt_len = tt.len(); - let Some(TokenTree::Subtree { len, open_span, close_span, .. }) = tt.get_mut(1) else { - return; - }; - if (*len as usize) != (tt_len - 2) { - // Subtree does not cover the whole tree (minus 2; itself, and the top span). - return; - } - - // Now we need to adjust the spans, because we assume that the first two spans are always reserved. - let top_open_span = span_parts - .get_index(open_span.span_parts_index() - RESERVED_SPAN_PARTS_LEN) - .unwrap() - .recombine(open_span.text_range()); - let top_close_span = span_parts - .get_index(close_span.span_parts_index() - RESERVED_SPAN_PARTS_LEN) - .unwrap() - .recombine(close_span.text_range()); - *top_delim_span = DelimSpan { open: top_open_span, close: top_close_span }; - // Can't remove the top spans from the map, as maybe they're used by other things as well. - // Now we need to reencode the spans, because their parts index changed: - *open_span = S::new(open_span.text_range(), 0); - *close_span = S::new(close_span.text_range(), 1); - - tt.remove(0); - } - dispatch_builder! { - match &mut self.token_trees => tt => remove_first_if_needed(tt, &mut self.top_subtree_spans, &self.span_parts) + assert!( + self.unclosed_subtree_indices.is_empty(), + "attempt to build an unbalanced `TopSubtreeBuilder`" + ); + let tt_len = self.token_trees.len(); + if let Some(&TokenTree::Subtree(Subtree { len, delimiter })) = self.token_trees.get(1) + && (len as usize) == (tt_len - 2) + { + // The top subtree's delimiters should not be included. + Self::remove_span(&mut self.span_parts_frequencies, &delimiter.open); + Self::remove_span(&mut self.span_parts_frequencies, &delimiter.close); + + let mut token_trees = self.token_trees.into_iter(); + token_trees.next(); // Remove the first subtree. + encode_all(token_trees, self.span_parts_frequencies, self.symbol_frequencies) + } else { + self.build() } - self.build() } pub fn build(mut self) -> TopSubtree { @@ -930,57 +1359,52 @@ impl TopSubtreeBuilder { self.unclosed_subtree_indices.is_empty(), "attempt to build an unbalanced `TopSubtreeBuilder`" ); - fn finish_top_len(tt: &mut [TokenTree]) { - let total_len = tt.len() as u32; - let TokenTree::Subtree { len, .. } = &mut tt[0] else { - unreachable!("first token tree is always a subtree"); - }; - *len = total_len - 1; - } - dispatch_builder! { - match &mut self.token_trees => tt => finish_top_len(tt) - } - - let span_parts = [ - CompressedSpanPart::from_span(&self.top_subtree_spans.open), - CompressedSpanPart::from_span(&self.top_subtree_spans.close), - ] - .into_iter() - .chain(self.span_parts.iter().copied()) - .collect(); - - let repr = match self.token_trees { - TopSubtreeBuilderRepr::SpanStorage32(tt) => { - TopSubtreeRepr::SpanStorage32(tt.into_boxed_slice()) - } - TopSubtreeBuilderRepr::SpanStorage64(tt) => { - TopSubtreeRepr::SpanStorage64(tt.into_boxed_slice()) - } - TopSubtreeBuilderRepr::SpanStorage96(tt) => { - TopSubtreeRepr::SpanStorage96(tt.into_boxed_slice()) - } + let tts_len = self.token_trees.len(); + let TokenTree::Subtree(top_subtree) = &mut self.token_trees[0] else { + panic!("first token tree must be a subtree"); }; - - TopSubtree { repr, span_parts } + top_subtree.len = (tts_len - 1).try_into().unwrap(); + encode_all( + self.token_trees.into_iter(), + self.span_parts_frequencies, + self.symbol_frequencies, + ) } - pub fn restore_point(&self) -> SubtreeBuilderRestorePoint { - let token_trees_len = dispatch_builder! { - match &self.token_trees => tt => tt.len() - }; + pub fn restore_point(&mut self) -> SubtreeBuilderRestorePoint { + // We reset the `last_closed_subtree`, since restoring from a restore point doesn't play well with removing the last subtree. + self.last_closed_subtree = None; SubtreeBuilderRestorePoint { unclosed_subtree_indices_len: self.unclosed_subtree_indices.len(), - token_trees_len, - last_closed_subtree: self.last_closed_subtree, + token_trees_len: self.token_trees.len(), } } pub fn restore(&mut self, restore_point: SubtreeBuilderRestorePoint) { - self.unclosed_subtree_indices.truncate(restore_point.unclosed_subtree_indices_len); - dispatch_builder! { - match &mut self.token_trees => tt => tt.truncate(restore_point.token_trees_len) + if restore_point.token_trees_len >= self.token_trees.len() { + // This means we restored twice, potentially with an earlier restore point first. + return; + } + + for tt in &self.token_trees[restore_point.token_trees_len..] { + match tt { + TokenTree::Leaf(leaf) => { + Self::remove_span(&mut self.span_parts_frequencies, leaf.span()); + + if let Some(symbol) = leaf.symbol() { + Self::remove_symbol(&mut self.symbol_frequencies, symbol.clone()); + } + } + TokenTree::Subtree(subtree) => { + Self::remove_span(&mut self.span_parts_frequencies, &subtree.delimiter.open); + Self::remove_span(&mut self.span_parts_frequencies, &subtree.delimiter.close); + } + } } - self.last_closed_subtree = restore_point.last_closed_subtree; + + self.unclosed_subtree_indices.truncate(restore_point.unclosed_subtree_indices_len); + self.token_trees.truncate(restore_point.token_trees_len); + self.last_closed_subtree = None; } } @@ -988,5 +1412,4 @@ impl TopSubtreeBuilder { pub struct SubtreeBuilderRestorePoint { unclosed_subtree_indices_len: usize, token_trees_len: usize, - last_closed_subtree: Option, } diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index f930573748e28..9ff8b0c27d19c 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -7fb284d9037fa54f6a9b24261c82b394472cbfd7 +f7d782a3be46d6bb4b9792fe69a61db389ba1769 diff --git a/src/tools/rust-analyzer/xtask/src/dist.rs b/src/tools/rust-analyzer/xtask/src/dist.rs index e8bedbe79e56e..0b8b14cb22717 100644 --- a/src/tools/rust-analyzer/xtask/src/dist.rs +++ b/src/tools/rust-analyzer/xtask/src/dist.rs @@ -43,7 +43,6 @@ impl flags::Dist { &format!("{version}-standalone"), &target, allocator, - self.zig, self.pgo, // Profiling requires debug information. self.enable_profiling, @@ -56,7 +55,6 @@ impl flags::Dist { "0.0.0-standalone", &target, allocator, - self.zig, self.pgo, // Profiling requires debug information. self.enable_profiling, @@ -101,7 +99,6 @@ fn dist_server( release: &str, target: &Target, allocator: Malloc, - zig: bool, pgo: Option, dev_rel: bool, ) -> anyhow::Result<()> { @@ -116,33 +113,23 @@ fn dist_server( // * on Linux, this blows up the binary size from 8MB to 43MB, which is unreasonable. // let _e = sh.push_env("CARGO_PROFILE_RELEASE_DEBUG", "1"); - let linux_target = target.is_linux(); - let target_name = match &target.libc_suffix { - Some(libc_suffix) if zig => format!("{}.{libc_suffix}", target.name), - _ => target.name.to_owned(), - }; let features = allocator.to_features(); - let command = if linux_target && zig { "zigbuild" } else { "build" }; + let cmd = build_command(sh, &target.name, features, dev_rel); let pgo_profile = if let Some(train_crate) = pgo { - Some(crate::pgo::gather_pgo_profile( - sh, - crate::pgo::build_command(sh, command, &target_name, features), - &target_name, - train_crate, - )?) + Some(crate::pgo::gather_pgo_profile(sh, cmd, &target.name, train_crate)?) } else { None }; - let mut cmd = build_command(sh, command, &target_name, features, dev_rel); + let mut cmd = build_command(sh, &target.name, features, dev_rel); let mut rustflags = Vec::new(); if let Some(profile) = pgo_profile { rustflags.push(format!("-Cprofile-use={}", profile.to_str().unwrap())); } - if target_name.ends_with("-windows-msvc") { + if target.name.ends_with("-windows-msvc") { // https://github.com/rust-lang/rust-analyzer/issues/20970 rustflags.push("-Ctarget-feature=+crt-static".to_owned()); } @@ -153,7 +140,7 @@ fn dist_server( cmd.run().context("cannot build Rust Analyzer")?; let dst = Path::new("dist").join(&target.artifact_name); - if target_name.contains("-windows-") { + if target.name.contains("-windows-") { zip(&target.server_path, target.symbols_path.as_ref(), &dst.with_extension("zip"))?; } else { gzip(&target.server_path, &dst.with_extension("gz"))?; @@ -164,7 +151,6 @@ fn dist_server( fn build_command<'a>( sh: &'a Shell, - command: &str, target_name: &str, features: &[&str], dev_rel: bool, @@ -172,7 +158,7 @@ fn build_command<'a>( let profile = if dev_rel { "dev-rel" } else { "release" }; cmd!( sh, - "cargo {command} --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --target {target_name} {features...} --profile {profile}" + "cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --target {target_name} {features...} --profile {profile}" ) } @@ -222,7 +208,6 @@ fn zip(src_path: &Path, symbols_path: Option<&PathBuf>, dest_path: &Path) -> any struct Target { name: String, - libc_suffix: Option, server_path: PathBuf, symbols_path: Option, artifact_name: String, @@ -231,10 +216,6 @@ struct Target { impl Target { fn get(project_root: &Path, sh: &Shell) -> Self { let name = detect_target(sh); - let (name, libc_suffix) = match name.split_once('.') { - Some((l, r)) => (l.to_owned(), Some(r.to_owned())), - None => (name, None), - }; let out_path = project_root.join("target").join(&name).join("release"); let (exe_suffix, symbols_path) = if name.contains("-windows-") { (".exe".into(), Some(out_path.join("rust_analyzer.pdb"))) @@ -243,11 +224,7 @@ impl Target { }; let server_path = out_path.join(format!("rust-analyzer{exe_suffix}")); let artifact_name = format!("rust-analyzer-{name}{exe_suffix}"); - Self { name, libc_suffix, server_path, symbols_path, artifact_name } - } - - fn is_linux(&self) -> bool { - self.name.contains("-linux-") + Self { name, server_path, symbols_path, artifact_name } } } diff --git a/src/tools/rust-analyzer/xtask/src/flags.rs b/src/tools/rust-analyzer/xtask/src/flags.rs index e72d8f22e4f0a..ecf38666934a0 100644 --- a/src/tools/rust-analyzer/xtask/src/flags.rs +++ b/src/tools/rust-analyzer/xtask/src/flags.rs @@ -76,8 +76,6 @@ xflags::xflags! { // **Warning:** This will produce a slower build of rust-analyzer, use only for profiling. optional --enable-profiling optional --client-patch-version version: String - /// Use cargo-zigbuild - optional --zig /// Apply PGO optimizations optional --pgo pgo: PgoTrainingCrate } @@ -154,7 +152,6 @@ pub struct Dist { pub jemalloc: bool, pub enable_profiling: bool, pub client_patch_version: Option, - pub zig: bool, pub pgo: Option, } diff --git a/src/tools/rust-analyzer/xtask/src/pgo.rs b/src/tools/rust-analyzer/xtask/src/pgo.rs index ca6dace940b54..9eb41faf26019 100644 --- a/src/tools/rust-analyzer/xtask/src/pgo.rs +++ b/src/tools/rust-analyzer/xtask/src/pgo.rs @@ -96,19 +96,6 @@ fn download_crate_for_training(sh: &Shell, pgo_dir: &Path, repo: &str) -> anyhow Ok(target_path) } -/// Helper function to create a build command for rust-analyzer -pub(crate) fn build_command<'a>( - sh: &'a Shell, - command: &str, - target_name: &str, - features: &[&str], -) -> Cmd<'a> { - cmd!( - sh, - "cargo {command} --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --target {target_name} {features...} --release" - ) -} - pub(crate) fn apply_pgo_to_cmd<'a>(cmd: Cmd<'a>, profile_path: &Path) -> Cmd<'a> { cmd.env("RUSTFLAGS", format!("-Cprofile-use={}", profile_path.to_str().unwrap())) } diff --git a/tests/codegen-llvm/issues/vec-clear-no-branch-45459.rs b/tests/codegen-llvm/issues/vec-clear-no-branch-45459.rs new file mode 100644 index 0000000000000..e6ff4684a8a41 --- /dev/null +++ b/tests/codegen-llvm/issues/vec-clear-no-branch-45459.rs @@ -0,0 +1,19 @@ +// Tests that clearing a `Vec` of a type without drop glue lowers to an +// unconditional store of the new length, without a comparison and branch +// guarding it. +// See . + +//@ compile-flags: -Copt-level=3 + +#![crate_type = "lib"] + +// CHECK-LABEL: @clear_vec( +// CHECK-NOT: icmp +// CHECK-NOT: br {{.*}} +// CHECK: store i{{[0-9]+}} 0 +// CHECK-NOT: br {{.*}} +// CHECK: ret void +#[no_mangle] +pub fn clear_vec(v: &mut Vec) { + v.clear(); +} diff --git a/tests/ui/abi/pass-indirectly-attr.stderr b/tests/ui/abi/pass-indirectly-attr.stderr index 320840c8149f5..efeec0d86982b 100644 --- a/tests/ui/abi/pass-indirectly-attr.stderr +++ b/tests/ui/abi/pass-indirectly-attr.stderr @@ -121,7 +121,7 @@ error: fn_abi_of(extern_rust) = FnAbi { }, }, mode: Cast { - pad_i32: false, + pad_i32_count: 0, cast: CastTarget { prefix: [], rest_offset: None, diff --git a/tests/ui/delegation/auxiliary/inherent-impl.rs b/tests/ui/delegation/auxiliary/inherent-impl.rs new file mode 100644 index 0000000000000..a5d2f15b2ffc6 --- /dev/null +++ b/tests/ui/delegation/auxiliary/inherent-impl.rs @@ -0,0 +1,26 @@ +#![feature(inherent_associated_types)] + +pub struct S; + +impl S { + pub type TYPE = (); + pub const CONST: usize = 0; + + pub fn foo() {} +} + +pub trait Trait { + fn bar() {} +} + +impl Trait for S {} + +pub struct X(T); + +impl X { + pub fn foo() {} +} + +impl X { + pub fn foo() {} +} diff --git a/tests/ui/delegation/inherent-impls-ambig.rs b/tests/ui/delegation/inherent-impls-ambig.rs new file mode 100644 index 0000000000000..1c419034d2446 --- /dev/null +++ b/tests/ui/delegation/inherent-impls-ambig.rs @@ -0,0 +1,62 @@ +#![feature(fn_delegation)] + +mod test_1 { + struct X(T); + + impl X<()> { + fn foo() {} + fn foo_self(self) {} + } + + impl X { + fn foo() {} + fn foo_self(self) {} + } + + reuse X::foo; + //~^ ERROR: cannot find function `foo` in `X` + + reuse X::foo_self; + //~^ ERROR: cannot find function `foo_self` in `X` + + reuse X::<()>::foo as foo1; + //~^ ERROR: cannot find function `foo` in `X` + + reuse X::::foo_self as foo_self1; + //~^ ERROR: cannot find function `foo_self` in `X` +} + +mod test_2 { + struct X(T, U); + trait Marker1 {} + trait Marker2 {} + + impl X { + fn foo() {} + fn foo_self(self) {} + } + + impl X { + fn foo() {} + fn foo_self(self) {} + } + + struct M1; + impl Marker1 for M1 {} + struct M2; + impl Marker2 for M2 {} + + reuse X::foo; + //~^ ERROR: cannot find function `foo` in `X` + + reuse X::foo_self; + //~^ ERROR: cannot find function `foo_self` in `X` + + reuse X::::foo as foo1; + //~^ ERROR: cannot find function `foo` in `X` + + reuse X::::foo_self as foo_self1; + //~^ ERROR: cannot find function `foo_self` in `X` +} + +fn main() {} diff --git a/tests/ui/delegation/inherent-impls-ambig.stderr b/tests/ui/delegation/inherent-impls-ambig.stderr new file mode 100644 index 0000000000000..0be82bcdd6f74 --- /dev/null +++ b/tests/ui/delegation/inherent-impls-ambig.stderr @@ -0,0 +1,99 @@ +error[E0425]: cannot find function `foo` in `X` + --> $DIR/inherent-impls-ambig.rs:16:14 + | +LL | reuse X::foo; + | ^^^ not found in `X` + | +note: function `test_2::foo` exists but is inaccessible + --> $DIR/inherent-impls-ambig.rs:49:5 + | +LL | reuse X::foo; + | ^^^^^^^^^^^^^ not accessible + +error[E0425]: cannot find function `foo_self` in `X` + --> $DIR/inherent-impls-ambig.rs:19:14 + | +LL | reuse X::foo_self; + | ^^^^^^^^ not found in `X` + | +note: function `test_2::foo_self` exists but is inaccessible + --> $DIR/inherent-impls-ambig.rs:52:5 + | +LL | reuse X::foo_self; + | ^^^^^^^^^^^^^^^^^^ not accessible + +error[E0425]: cannot find function `foo` in `X` + --> $DIR/inherent-impls-ambig.rs:22:20 + | +LL | reuse X::<()>::foo as foo1; + | ^^^ not found in `X` + | +note: function `test_2::foo` exists but is inaccessible + --> $DIR/inherent-impls-ambig.rs:49:5 + | +LL | reuse X::foo; + | ^^^^^^^^^^^^^ not accessible + +error[E0425]: cannot find function `foo_self` in `X` + --> $DIR/inherent-impls-ambig.rs:25:23 + | +LL | reuse X::::foo_self as foo_self1; + | ^^^^^^^^ not found in `X` + | +note: function `test_2::foo_self` exists but is inaccessible + --> $DIR/inherent-impls-ambig.rs:52:5 + | +LL | reuse X::foo_self; + | ^^^^^^^^^^^^^^^^^^ not accessible + +error[E0425]: cannot find function `foo` in `X` + --> $DIR/inherent-impls-ambig.rs:49:14 + | +LL | reuse X::foo; + | ^^^ not found in `X` + | +note: function `test_1::foo` exists but is inaccessible + --> $DIR/inherent-impls-ambig.rs:16:5 + | +LL | reuse X::foo; + | ^^^^^^^^^^^^^ not accessible + +error[E0425]: cannot find function `foo_self` in `X` + --> $DIR/inherent-impls-ambig.rs:52:14 + | +LL | reuse X::foo_self; + | ^^^^^^^^ not found in `X` + | +note: function `test_1::foo_self` exists but is inaccessible + --> $DIR/inherent-impls-ambig.rs:19:5 + | +LL | reuse X::foo_self; + | ^^^^^^^^^^^^^^^^^^ not accessible + +error[E0425]: cannot find function `foo` in `X` + --> $DIR/inherent-impls-ambig.rs:55:27 + | +LL | reuse X::::foo as foo1; + | ^^^ not found in `X` + | +note: function `test_1::foo` exists but is inaccessible + --> $DIR/inherent-impls-ambig.rs:16:5 + | +LL | reuse X::foo; + | ^^^^^^^^^^^^^ not accessible + +error[E0425]: cannot find function `foo_self` in `X` + --> $DIR/inherent-impls-ambig.rs:58:28 + | +LL | reuse X::::foo_self as foo_self1; + | ^^^^^^^^ not found in `X` + | +note: function `test_1::foo_self` exists but is inaccessible + --> $DIR/inherent-impls-ambig.rs:19:5 + | +LL | reuse X::foo_self; + | ^^^^^^^^^^^^^^^^^^ not accessible + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/delegation/inherent-impls-enums.rs b/tests/ui/delegation/inherent-impls-enums.rs new file mode 100644 index 0000000000000..301cbe32b6f88 --- /dev/null +++ b/tests/ui/delegation/inherent-impls-enums.rs @@ -0,0 +1,90 @@ +#![feature(fn_delegation)] + +enum S<'a: 'a, A: 'a, const C: usize> { + A(A), + B(&'a [A; C]), +} + +impl<'a, 'b, 'c, A: 'a, const C: usize> S<'a, A, C> { + fn foo_static<'d: 'd, 'e, T, const B: bool>() {} + fn foo_self<'d: 'd, 'e, T, const B: bool>(self) {} +} + +reuse S::<'static, (), 1>::foo_static::<'static, (), true> as foo_static_1; +//~^ ERROR: cannot find function `foo_static` in enum `S` +reuse S::<'static, (), 1>::foo_static as foo_static_3; +//~^ ERROR: cannot find function `foo_static` in enum `S` +reuse S::<'static, (), 1>::foo_static::<'static, _, _> as foo_static_4; +//~^ ERROR: cannot find function `foo_static` in enum `S` + +reuse S::<'static, (), 1>::foo_self::<'static, (), true> as foo_self_1; +//~^ ERROR: cannot find function `foo_self` in enum `S` +reuse S::<'static, (), 1>::foo_self as foo_self_3; +//~^ ERROR: cannot find function `foo_self` in enum `S` +reuse S::<'static, (), 1>::foo_self::<'static, _, _> as foo_self_4; +//~^ ERROR: cannot find function `foo_self` in enum `S` + +trait Trait<'a, AA, BB> +where + Self: Sized, +{ + reuse S::<'static, (), 1>::foo_static::<'static, (), true> as foo_static_1; + //~^ ERROR: cannot find function `foo_static` in enum `S` + reuse S::<'static, (), 1>::foo_static as foo_static_3; + //~^ ERROR: cannot find function `foo_static` in enum `S` + reuse S::<'static, (), 1>::foo_static::<'static, _, _> as foo_static_4; + //~^ ERROR: cannot find function `foo_static` in enum `S` + + fn get_s(self) -> S<'static, (), 1> { + panic!(); + } + + reuse S::<'static, (), 1>::foo_self::<'static, (), true> as foo_self_1 { self.get_s() } + //~^ ERROR: cannot find function `foo_self` in enum `S` + reuse S::<'static, (), 1>::foo_self as foo_self_3 { self.get_s() } + //~^ ERROR: cannot find function `foo_self` in enum `S` + reuse S::<'static, (), 1>::foo_self::<'static, _, _> as foo_self_4; + //~^ ERROR: cannot find function `foo_self` in enum `S` +} + +struct X; + +impl<'a, A, B> Trait<'a, A, B> for X { + reuse S::<'static, (), 1>::foo_static::<'static, (), true> as foo_static_1; + //~^ ERROR: cannot find function `foo_static` in enum `S` + reuse S::<'static, (), 1>::foo_static as foo_static_3; + //~^ ERROR: cannot find function `foo_static` in enum `S` + reuse S::<'static, (), 1>::foo_static::<'static, _, _> as foo_static_4; + //~^ ERROR: cannot find function `foo_static` in enum `S` + + reuse S::<'static, (), 1>::foo_self::<'static, (), true> as foo_self_1 { self.get_s() } + //~^ ERROR: cannot find function `foo_self` in enum `S` + //~| ERROR: delegation's target expression is specified for function with no params + reuse S::<'static, (), 1>::foo_self as foo_self_3 { self.get_s() } + //~^ ERROR: cannot find function `foo_self` in enum `S` + //~| ERROR: delegation's target expression is specified for function with no params + reuse S::<'static, (), 1>::foo_self::<'static, _, _> as foo_self_4; + //~^ ERROR: cannot find function `foo_self` in enum `S` +} + +impl X { + reuse S::<'static, (), 1>::foo_static::<'static, (), true> as foo_static_1; + //~^ ERROR: cannot find function `foo_static` in enum `S` + reuse S::<'static, (), 1>::foo_static as foo_static_3; + //~^ ERROR: cannot find function `foo_static` in enum `S` + reuse S::<'static, (), 1>::foo_static::<'static, _, _> as foo_static_4; + //~^ ERROR: cannot find function `foo_static` in enum `S` + + fn get_s(self) -> S<'static, (), 1> { + panic!(); + } + + reuse S::<'static, (), 1>::foo_self::<'static, (), true> as foo_self_1 { self.get_s() } + //~^ ERROR: cannot find function `foo_self` in enum `S` + reuse S::<'static, (), 1>::foo_self as foo_self_3 { self.get_s() } + //~^ ERROR: cannot find function `foo_self` in enum `S` + reuse S::<'static, (), 1>::foo_self::<'static, _, _> as foo_self_4; + //~^ ERROR: cannot find function `foo_self` in enum `S` +} + +fn main() {} diff --git a/tests/ui/delegation/inherent-impls-enums.stderr b/tests/ui/delegation/inherent-impls-enums.stderr new file mode 100644 index 0000000000000..0016488386017 --- /dev/null +++ b/tests/ui/delegation/inherent-impls-enums.stderr @@ -0,0 +1,159 @@ +error[E0425]: cannot find function `foo_static` in enum `S` + --> $DIR/inherent-impls-enums.rs:13:28 + | +LL | reuse S::<'static, (), 1>::foo_static::<'static, (), true> as foo_static_1; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in enum `S` + --> $DIR/inherent-impls-enums.rs:15:28 + | +LL | reuse S::<'static, (), 1>::foo_static as foo_static_3; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in enum `S` + --> $DIR/inherent-impls-enums.rs:17:28 + | +LL | reuse S::<'static, (), 1>::foo_static::<'static, _, _> as foo_static_4; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in enum `S` + --> $DIR/inherent-impls-enums.rs:20:28 + | +LL | reuse S::<'static, (), 1>::foo_self::<'static, (), true> as foo_self_1; + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in enum `S` + --> $DIR/inherent-impls-enums.rs:22:28 + | +LL | reuse S::<'static, (), 1>::foo_self as foo_self_3; + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in enum `S` + --> $DIR/inherent-impls-enums.rs:24:28 + | +LL | reuse S::<'static, (), 1>::foo_self::<'static, _, _> as foo_self_4; + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in enum `S` + --> $DIR/inherent-impls-enums.rs:31:32 + | +LL | reuse S::<'static, (), 1>::foo_static::<'static, (), true> as foo_static_1; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in enum `S` + --> $DIR/inherent-impls-enums.rs:33:32 + | +LL | reuse S::<'static, (), 1>::foo_static as foo_static_3; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in enum `S` + --> $DIR/inherent-impls-enums.rs:35:32 + | +LL | reuse S::<'static, (), 1>::foo_static::<'static, _, _> as foo_static_4; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in enum `S` + --> $DIR/inherent-impls-enums.rs:42:32 + | +LL | reuse S::<'static, (), 1>::foo_self::<'static, (), true> as foo_self_1 { self.get_s() } + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in enum `S` + --> $DIR/inherent-impls-enums.rs:44:32 + | +LL | reuse S::<'static, (), 1>::foo_self as foo_self_3 { self.get_s() } + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in enum `S` + --> $DIR/inherent-impls-enums.rs:46:32 + | +LL | reuse S::<'static, (), 1>::foo_self::<'static, _, _> as foo_self_4; + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in enum `S` + --> $DIR/inherent-impls-enums.rs:53:32 + | +LL | reuse S::<'static, (), 1>::foo_static::<'static, (), true> as foo_static_1; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in enum `S` + --> $DIR/inherent-impls-enums.rs:55:32 + | +LL | reuse S::<'static, (), 1>::foo_static as foo_static_3; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in enum `S` + --> $DIR/inherent-impls-enums.rs:57:32 + | +LL | reuse S::<'static, (), 1>::foo_static::<'static, _, _> as foo_static_4; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in enum `S` + --> $DIR/inherent-impls-enums.rs:60:32 + | +LL | reuse S::<'static, (), 1>::foo_self::<'static, (), true> as foo_self_1 { self.get_s() } + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in enum `S` + --> $DIR/inherent-impls-enums.rs:63:32 + | +LL | reuse S::<'static, (), 1>::foo_self as foo_self_3 { self.get_s() } + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in enum `S` + --> $DIR/inherent-impls-enums.rs:66:32 + | +LL | reuse S::<'static, (), 1>::foo_self::<'static, _, _> as foo_self_4; + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in enum `S` + --> $DIR/inherent-impls-enums.rs:71:32 + | +LL | reuse S::<'static, (), 1>::foo_static::<'static, (), true> as foo_static_1; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in enum `S` + --> $DIR/inherent-impls-enums.rs:73:32 + | +LL | reuse S::<'static, (), 1>::foo_static as foo_static_3; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in enum `S` + --> $DIR/inherent-impls-enums.rs:75:32 + | +LL | reuse S::<'static, (), 1>::foo_static::<'static, _, _> as foo_static_4; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in enum `S` + --> $DIR/inherent-impls-enums.rs:82:32 + | +LL | reuse S::<'static, (), 1>::foo_self::<'static, (), true> as foo_self_1 { self.get_s() } + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in enum `S` + --> $DIR/inherent-impls-enums.rs:84:32 + | +LL | reuse S::<'static, (), 1>::foo_self as foo_self_3 { self.get_s() } + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in enum `S` + --> $DIR/inherent-impls-enums.rs:86:32 + | +LL | reuse S::<'static, (), 1>::foo_self::<'static, _, _> as foo_self_4; + | ^^^^^^^^ not found in `S` + +error: delegation's target expression is specified for function with no params + --> $DIR/inherent-impls-enums.rs:60:76 + | +LL | reuse S::<'static, (), 1>::foo_self::<'static, (), true> as foo_self_1 { self.get_s() } + | ^^^^^^^^^^^^^^^^ + +error: delegation's target expression is specified for function with no params + --> $DIR/inherent-impls-enums.rs:63:55 + | +LL | reuse S::<'static, (), 1>::foo_self as foo_self_3 { self.get_s() } + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 26 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/delegation/inherent-impls-glob-list.rs b/tests/ui/delegation/inherent-impls-glob-list.rs new file mode 100644 index 0000000000000..51d24e6838df0 --- /dev/null +++ b/tests/ui/delegation/inherent-impls-glob-list.rs @@ -0,0 +1,23 @@ +#![feature(fn_delegation)] + +struct X; + +impl X { + fn foo(&self) {} + fn foo2(&self) {} +} + +struct Y; + +impl Y { + reuse X::{foo, foo2} { X } + //~^ ERROR: cannot find function `foo` in `X` + //~| ERROR: cannot find function `foo2` in `X` +} + +impl Y { + reuse X::*; + //~^ ERROR: expected trait, found struct `X` +} + +fn main() {} diff --git a/tests/ui/delegation/inherent-impls-glob-list.stderr b/tests/ui/delegation/inherent-impls-glob-list.stderr new file mode 100644 index 0000000000000..d2dfce86f860a --- /dev/null +++ b/tests/ui/delegation/inherent-impls-glob-list.stderr @@ -0,0 +1,21 @@ +error: expected trait, found struct `X` + --> $DIR/inherent-impls-glob-list.rs:19:11 + | +LL | reuse X::*; + | ^ not a trait + +error[E0425]: cannot find function `foo` in `X` + --> $DIR/inherent-impls-glob-list.rs:13:15 + | +LL | reuse X::{foo, foo2} { X } + | ^^^ not found in `X` + +error[E0425]: cannot find function `foo2` in `X` + --> $DIR/inherent-impls-glob-list.rs:13:20 + | +LL | reuse X::{foo, foo2} { X } + | ^^^^ not found in `X` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/delegation/inherent-impls-mixed-generics.rs b/tests/ui/delegation/inherent-impls-mixed-generics.rs new file mode 100644 index 0000000000000..027dcace3ab0d --- /dev/null +++ b/tests/ui/delegation/inherent-impls-mixed-generics.rs @@ -0,0 +1,16 @@ +#![feature(fn_delegation)] + +struct S<'a, A, B, const C: usize> { + xd: &'a [(A, B); C], +} + +impl<'a, 'b, 'c, A, const C: usize> S<'static, A, usize, C> { + fn foo_self<'d: 'd, 'e, T, const B: bool>(self) {} +} + +trait Trait<'a, AA, BB> where Self: Sized { + reuse S::foo_self; + //~^ ERROR: cannot find function `foo_self` in `S` +} + +fn main() {} diff --git a/tests/ui/delegation/inherent-impls-mixed-generics.stderr b/tests/ui/delegation/inherent-impls-mixed-generics.stderr new file mode 100644 index 0000000000000..f515b52ca45ab --- /dev/null +++ b/tests/ui/delegation/inherent-impls-mixed-generics.stderr @@ -0,0 +1,9 @@ +error[E0425]: cannot find function `foo_self` in `S` + --> $DIR/inherent-impls-mixed-generics.rs:12:14 + | +LL | reuse S::foo_self; + | ^^^^^^^^ not found in `S` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/delegation/inherent-impls-non-local-crate.rs b/tests/ui/delegation/inherent-impls-non-local-crate.rs new file mode 100644 index 0000000000000..c2f07fd8e7ae7 --- /dev/null +++ b/tests/ui/delegation/inherent-impls-non-local-crate.rs @@ -0,0 +1,25 @@ +//@ aux-crate:inherent_impl=inherent-impl.rs + +#![feature(fn_delegation)] + +reuse inherent_impl::S::foo; +//~^ ERROR: cannot find function `foo` in `inherent_impl::S` + +reuse inherent_impl::S::not_existing; +//~^ ERROR: cannot find function `not_existing` in `inherent_impl::S` + +reuse inherent_impl::S::TYPE; +//~^ ERROR: cannot find function `TYPE` in `inherent_impl::S` + +reuse inherent_impl::S::CONST; +//~^ ERROR: cannot find function `CONST` in `inherent_impl::S` + +reuse inherent_impl::S::bar; +//~^ ERROR: cannot find function `bar` in `inherent_impl::S` + +reuse ::bar as trait_bar; + +reuse inherent_impl::X::foo as x_foo; +//~^ ERROR: cannot find function `foo` in `inherent_impl::X` + +fn main() {} diff --git a/tests/ui/delegation/inherent-impls-non-local-crate.stderr b/tests/ui/delegation/inherent-impls-non-local-crate.stderr new file mode 100644 index 0000000000000..983c8b8f3cb9b --- /dev/null +++ b/tests/ui/delegation/inherent-impls-non-local-crate.stderr @@ -0,0 +1,39 @@ +error[E0425]: cannot find function `foo` in `inherent_impl::S` + --> $DIR/inherent-impls-non-local-crate.rs:5:25 + | +LL | reuse inherent_impl::S::foo; + | ^^^ not found in `inherent_impl::S` + +error[E0425]: cannot find function `not_existing` in `inherent_impl::S` + --> $DIR/inherent-impls-non-local-crate.rs:8:25 + | +LL | reuse inherent_impl::S::not_existing; + | ^^^^^^^^^^^^ not found in `inherent_impl::S` + +error[E0425]: cannot find function `TYPE` in `inherent_impl::S` + --> $DIR/inherent-impls-non-local-crate.rs:11:25 + | +LL | reuse inherent_impl::S::TYPE; + | ^^^^ not found in `inherent_impl::S` + +error[E0425]: cannot find function `CONST` in `inherent_impl::S` + --> $DIR/inherent-impls-non-local-crate.rs:14:25 + | +LL | reuse inherent_impl::S::CONST; + | ^^^^^ not found in `inherent_impl::S` + +error[E0425]: cannot find function `bar` in `inherent_impl::S` + --> $DIR/inherent-impls-non-local-crate.rs:17:25 + | +LL | reuse inherent_impl::S::bar; + | ^^^ not found in `inherent_impl::S` + +error[E0425]: cannot find function `foo` in `inherent_impl::X` + --> $DIR/inherent-impls-non-local-crate.rs:22:25 + | +LL | reuse inherent_impl::X::foo as x_foo; + | ^^^ not found in `inherent_impl::X` + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/delegation/inherent-impls-parent-generics.rs b/tests/ui/delegation/inherent-impls-parent-generics.rs new file mode 100644 index 0000000000000..0f95540643bd7 --- /dev/null +++ b/tests/ui/delegation/inherent-impls-parent-generics.rs @@ -0,0 +1,80 @@ +#![feature(fn_delegation)] +#![allow(late_bound_lifetime_arguments)] + +enum E<'a: 'a, A: 'a, const C: usize> { + A(A), + B(&'a [A; C]), +} + +impl<'a, 'b, 'c, A: 'a, const C: usize> E<'a, A, C> { + fn foo_static<'d: 'd, 'e, T, const B: bool>() {} + fn foo_self<'d: 'd, 'e, T, const B: bool>(self) {} +} + +reuse E::foo_static as e; +//~^ ERROR: cannot find function `foo_static` in enum `E` + +reuse E::foo_self as e1; +//~^ ERROR: cannot find function `foo_self` in enum `E` + +reuse E::foo_static::<'static, (), true> as e2; +//~^ ERROR: cannot find function `foo_static` in enum `E` + +reuse E::foo_self::<'static, (), true> as e3; +//~^ ERROR: cannot find function `foo_self` in enum `E` + +reuse E::<'static, (), 123>::foo_static as e4; +//~^ ERROR: cannot find function `foo_static` in enum `E` +reuse E::<'static, (), 123>::foo_self as e5; +//~^ ERROR: cannot find function `foo_self` in enum `E` + +reuse E::<'static, (), 123>::foo_static::<'static, (), true> as e6; +//~^ ERROR: cannot find function `foo_static` in enum `E` +reuse E::<'static, (), 123>::foo_self::<'static, (), true> as e7; +//~^ ERROR: cannot find function `foo_self` in enum `E` + +reuse E::<'_, (), _>::foo_static as e8; +//~^ ERROR: cannot find function `foo_static` in enum `E` + +reuse E::<'_, _, _>::foo_self as e9; +//~^ ERROR: cannot find function `foo_self` in enum `E` + +struct S { + xd: [A; C], +} + +impl<'a, 'b, 'c, A, const C: usize> S { + fn foo_static<'d: 'd, 'e, T, const B: bool>() {} + fn foo_self<'d: 'd, 'e, T, const B: bool>(self) {} +} + +reuse S::foo_static as s; +//~^ ERROR: cannot find function `foo_static` in `S` + +reuse S::foo_self as s1; +//~^ ERROR: cannot find function `foo_self` in `S` + +reuse S::foo_static::<'static, (), true> as s2; +//~^ ERROR: cannot find function `foo_static` in `S` + +reuse S::foo_self::<'static, (), true> as s3; +//~^ ERROR: cannot find function `foo_self` in `S` + +reuse S::<(), 123>::foo_static as s4; +//~^ ERROR: cannot find function `foo_static` in `S` +reuse S::<(), 123>::foo_self as s5; +//~^ ERROR: cannot find function `foo_self` in `S` + +reuse S::<(), 123>::foo_static::<'static, (), true> as s6; +//~^ ERROR: cannot find function `foo_static` in `S` +reuse S::<(), 123>::foo_self::<'static, (), true> as s7; +//~^ ERROR: cannot find function `foo_self` in `S` + +reuse S::<(), _>::foo_static as s8; +//~^ ERROR: cannot find function `foo_static` in `S` + +reuse S::<_, 123>::foo_self as s9; +//~^ ERROR: cannot find function `foo_self` in `S` + + +fn main() {} diff --git a/tests/ui/delegation/inherent-impls-parent-generics.stderr b/tests/ui/delegation/inherent-impls-parent-generics.stderr new file mode 100644 index 0000000000000..508ca65316457 --- /dev/null +++ b/tests/ui/delegation/inherent-impls-parent-generics.stderr @@ -0,0 +1,123 @@ +error[E0425]: cannot find function `foo_static` in enum `E` + --> $DIR/inherent-impls-parent-generics.rs:14:10 + | +LL | reuse E::foo_static as e; + | ^^^^^^^^^^ not found in `E` + +error[E0425]: cannot find function `foo_self` in enum `E` + --> $DIR/inherent-impls-parent-generics.rs:17:10 + | +LL | reuse E::foo_self as e1; + | ^^^^^^^^ not found in `E` + +error[E0425]: cannot find function `foo_static` in enum `E` + --> $DIR/inherent-impls-parent-generics.rs:20:10 + | +LL | reuse E::foo_static::<'static, (), true> as e2; + | ^^^^^^^^^^ not found in `E` + +error[E0425]: cannot find function `foo_self` in enum `E` + --> $DIR/inherent-impls-parent-generics.rs:23:10 + | +LL | reuse E::foo_self::<'static, (), true> as e3; + | ^^^^^^^^ not found in `E` + +error[E0425]: cannot find function `foo_static` in enum `E` + --> $DIR/inherent-impls-parent-generics.rs:26:30 + | +LL | reuse E::<'static, (), 123>::foo_static as e4; + | ^^^^^^^^^^ not found in `E` + +error[E0425]: cannot find function `foo_self` in enum `E` + --> $DIR/inherent-impls-parent-generics.rs:28:30 + | +LL | reuse E::<'static, (), 123>::foo_self as e5; + | ^^^^^^^^ not found in `E` + +error[E0425]: cannot find function `foo_static` in enum `E` + --> $DIR/inherent-impls-parent-generics.rs:31:30 + | +LL | reuse E::<'static, (), 123>::foo_static::<'static, (), true> as e6; + | ^^^^^^^^^^ not found in `E` + +error[E0425]: cannot find function `foo_self` in enum `E` + --> $DIR/inherent-impls-parent-generics.rs:33:30 + | +LL | reuse E::<'static, (), 123>::foo_self::<'static, (), true> as e7; + | ^^^^^^^^ not found in `E` + +error[E0425]: cannot find function `foo_static` in enum `E` + --> $DIR/inherent-impls-parent-generics.rs:36:23 + | +LL | reuse E::<'_, (), _>::foo_static as e8; + | ^^^^^^^^^^ not found in `E` + +error[E0425]: cannot find function `foo_self` in enum `E` + --> $DIR/inherent-impls-parent-generics.rs:39:22 + | +LL | reuse E::<'_, _, _>::foo_self as e9; + | ^^^^^^^^ not found in `E` + +error[E0425]: cannot find function `foo_static` in `S` + --> $DIR/inherent-impls-parent-generics.rs:51:10 + | +LL | reuse S::foo_static as s; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in `S` + --> $DIR/inherent-impls-parent-generics.rs:54:10 + | +LL | reuse S::foo_self as s1; + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in `S` + --> $DIR/inherent-impls-parent-generics.rs:57:10 + | +LL | reuse S::foo_static::<'static, (), true> as s2; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in `S` + --> $DIR/inherent-impls-parent-generics.rs:60:10 + | +LL | reuse S::foo_self::<'static, (), true> as s3; + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in `S` + --> $DIR/inherent-impls-parent-generics.rs:63:21 + | +LL | reuse S::<(), 123>::foo_static as s4; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in `S` + --> $DIR/inherent-impls-parent-generics.rs:65:21 + | +LL | reuse S::<(), 123>::foo_self as s5; + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in `S` + --> $DIR/inherent-impls-parent-generics.rs:68:21 + | +LL | reuse S::<(), 123>::foo_static::<'static, (), true> as s6; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in `S` + --> $DIR/inherent-impls-parent-generics.rs:70:21 + | +LL | reuse S::<(), 123>::foo_self::<'static, (), true> as s7; + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in `S` + --> $DIR/inherent-impls-parent-generics.rs:73:19 + | +LL | reuse S::<(), _>::foo_static as s8; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in `S` + --> $DIR/inherent-impls-parent-generics.rs:76:20 + | +LL | reuse S::<_, 123>::foo_self as s9; + | ^^^^^^^^ not found in `S` + +error: aborting due to 20 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/delegation/inherent-impls-receiver-mapping.rs b/tests/ui/delegation/inherent-impls-receiver-mapping.rs new file mode 100644 index 0000000000000..05a9c350af746 --- /dev/null +++ b/tests/ui/delegation/inherent-impls-receiver-mapping.rs @@ -0,0 +1,71 @@ +#![feature(fn_delegation)] + +mod receiver_mapping { + struct X; + + impl X { + fn static_f() {} + fn by_value(self) {} + fn by_ref(&self) {} + fn by_mut_ref(&mut self) {} + } + + struct Y; + + impl Y { + fn get_x(&self) -> X { X } + reuse X::{static_f, by_value, by_ref, by_mut_ref} { self.get_x() } + //~^ ERROR: cannot find function `by_mut_ref` in `X` + //~| ERROR: cannot find function `by_ref` in `X` + //~| ERROR: cannot find function `by_value` in `X` + //~| ERROR: cannot find function `static_f` in `X` + } + + fn check() { + let y = Y; + y.by_ref(); + //~^ ERROR: no method named `by_ref` found for struct `Y` in the current scope + y.by_mut_ref(); + //~^ ERROR: no method named `by_mut_ref` found for struct `Y` in the current scope + y.by_value(); + //~^ ERROR: no method named `by_value` found for struct `Y` in the current scope + + let y = &Y; + y.by_value(); + //~^ ERROR: no method named `by_value` found for reference `&Y` in the current scope + y.by_ref(); + //~^ ERROR: no method named `by_ref` found for reference `&Y` in the current scope + y.by_mut_ref(); + //~^ ERROR: no method named `by_mut_ref` found for reference `&Y` in the current scope + + let y = &mut Y; + y.by_value(); + //~^ ERROR: no method named `by_value` found for mutable reference `&mut Y` in the current scope + y.by_ref(); + //~^ ERROR: the method `by_ref` exists for mutable reference `&mut Y`, but its trait bounds were not satisfied + y.by_mut_ref(); + //~^ ERROR: no method named `by_mut_ref` found for mutable reference `&mut Y` in the current scope + } +} + +mod self_type_mapping { + struct X; + impl X { + fn add(self, other: Self) -> Self { + Self + } + } + + struct W(X); + impl W { + reuse X::add { self.0 } + //~^ ERROR: cannot find function `add` in `X` + } + + fn check() { + W(X).add(W(X)); + //~^ ERROR: no method named `add` found for struct `W` in the current scope + } +} + +fn main() {} diff --git a/tests/ui/delegation/inherent-impls-receiver-mapping.stderr b/tests/ui/delegation/inherent-impls-receiver-mapping.stderr new file mode 100644 index 0000000000000..92baa5ff53ed0 --- /dev/null +++ b/tests/ui/delegation/inherent-impls-receiver-mapping.stderr @@ -0,0 +1,256 @@ +error[E0425]: cannot find function `static_f` in `X` + --> $DIR/inherent-impls-receiver-mapping.rs:17:19 + | +LL | reuse X::{static_f, by_value, by_ref, by_mut_ref} { self.get_x() } + | ^^^^^^^^ not found in `X` + +error[E0425]: cannot find function `by_value` in `X` + --> $DIR/inherent-impls-receiver-mapping.rs:17:29 + | +LL | reuse X::{static_f, by_value, by_ref, by_mut_ref} { self.get_x() } + | ^^^^^^^^ not found in `X` + +error[E0425]: cannot find function `by_ref` in `X` + --> $DIR/inherent-impls-receiver-mapping.rs:17:39 + | +LL | reuse X::{static_f, by_value, by_ref, by_mut_ref} { self.get_x() } + | ^^^^^^ not found in `X` + +error[E0425]: cannot find function `by_mut_ref` in `X` + --> $DIR/inherent-impls-receiver-mapping.rs:17:47 + | +LL | reuse X::{static_f, by_value, by_ref, by_mut_ref} { self.get_x() } + | ^^^^^^^^^^ not found in `X` + +error[E0425]: cannot find function `add` in `X` + --> $DIR/inherent-impls-receiver-mapping.rs:61:18 + | +LL | reuse X::add { self.0 } + | ^^^ not found in `X` + +error[E0599]: no method named `by_ref` found for struct `Y` in the current scope + --> $DIR/inherent-impls-receiver-mapping.rs:26:11 + | +LL | struct Y; + | -------- method `by_ref` not found for this struct +... +LL | y.by_ref(); + | ^^^^^^ this is an associated function, not a method + | + = 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 `Y` + --> $DIR/inherent-impls-receiver-mapping.rs:17:39 + | +LL | reuse X::{static_f, by_value, by_ref, by_mut_ref} { self.get_x() } + | ^^^^^^ + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following traits define an item `by_ref`, perhaps you need to implement one of them: + candidate #1: `Iterator` + candidate #2: `std::io::Read` + candidate #3: `std::io::Write` +help: use associated function syntax instead + | +LL - y.by_ref(); +LL + Y::by_ref(); + | + +error[E0599]: no method named `by_mut_ref` found for struct `Y` in the current scope + --> $DIR/inherent-impls-receiver-mapping.rs:28:11 + | +LL | struct Y; + | -------- method `by_mut_ref` not found for this struct +... +LL | y.by_mut_ref(); + | ^^^^^^^^^^ this is an associated function, not a method + | + = 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 `Y` + --> $DIR/inherent-impls-receiver-mapping.rs:17:47 + | +LL | reuse X::{static_f, by_value, by_ref, by_mut_ref} { self.get_x() } + | ^^^^^^^^^^ +help: use associated function syntax instead + | +LL - y.by_mut_ref(); +LL + Y::by_mut_ref(); + | + +error[E0599]: no method named `by_value` found for struct `Y` in the current scope + --> $DIR/inherent-impls-receiver-mapping.rs:30:11 + | +LL | struct Y; + | -------- method `by_value` not found for this struct +... +LL | y.by_value(); + | ^^^^^^^^ this is an associated function, not a method + | + = 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 `Y` + --> $DIR/inherent-impls-receiver-mapping.rs:17:29 + | +LL | reuse X::{static_f, by_value, by_ref, by_mut_ref} { self.get_x() } + | ^^^^^^^^ +help: use associated function syntax instead + | +LL - y.by_value(); +LL + Y::by_value(); + | + +error[E0599]: no method named `by_value` found for reference `&Y` in the current scope + --> $DIR/inherent-impls-receiver-mapping.rs:34:11 + | +LL | y.by_value(); + | ^^^^^^^^ this is an associated function, not a method + | + = 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 `Y` + --> $DIR/inherent-impls-receiver-mapping.rs:17:29 + | +LL | reuse X::{static_f, by_value, by_ref, by_mut_ref} { self.get_x() } + | ^^^^^^^^ +help: use associated function syntax instead + | +LL - y.by_value(); +LL + Y::by_value(); + | + +error[E0599]: no method named `by_ref` found for reference `&Y` in the current scope + --> $DIR/inherent-impls-receiver-mapping.rs:36:11 + | +LL | y.by_ref(); + | ^^^^^^ this is an associated function, not a method + | + = 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 `Y` + --> $DIR/inherent-impls-receiver-mapping.rs:17:39 + | +LL | reuse X::{static_f, by_value, by_ref, by_mut_ref} { self.get_x() } + | ^^^^^^ + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following traits define an item `by_ref`, perhaps you need to implement one of them: + candidate #1: `std::io::Read` + candidate #2: `std::io::Write` + = note: the trait `Iterator` defines an item `by_ref`, but is explicitly unimplemented +help: use associated function syntax instead + | +LL - y.by_ref(); +LL + Y::by_ref(); + | + +error[E0599]: no method named `by_mut_ref` found for reference `&Y` in the current scope + --> $DIR/inherent-impls-receiver-mapping.rs:38:11 + | +LL | y.by_mut_ref(); + | ^^^^^^^^^^ this is an associated function, not a method + | + = 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 `Y` + --> $DIR/inherent-impls-receiver-mapping.rs:17:47 + | +LL | reuse X::{static_f, by_value, by_ref, by_mut_ref} { self.get_x() } + | ^^^^^^^^^^ +help: use associated function syntax instead + | +LL - y.by_mut_ref(); +LL + Y::by_mut_ref(); + | + +error[E0599]: no method named `by_value` found for mutable reference `&mut Y` in the current scope + --> $DIR/inherent-impls-receiver-mapping.rs:42:11 + | +LL | y.by_value(); + | ^^^^^^^^ this is an associated function, not a method + | + = 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 `Y` + --> $DIR/inherent-impls-receiver-mapping.rs:17:29 + | +LL | reuse X::{static_f, by_value, by_ref, by_mut_ref} { self.get_x() } + | ^^^^^^^^ +help: use associated function syntax instead + | +LL - y.by_value(); +LL + Y::by_value(); + | + +error[E0599]: the method `by_ref` exists for mutable reference `&mut Y`, but its trait bounds were not satisfied + --> $DIR/inherent-impls-receiver-mapping.rs:44:11 + | +LL | struct Y; + | -------- doesn't satisfy `Y: Iterator` +... +LL | y.by_ref(); + | ^^^^^^ this is an associated function, not a method + | + = 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 `Y` + --> $DIR/inherent-impls-receiver-mapping.rs:17:39 + | +LL | reuse X::{static_f, by_value, by_ref, by_mut_ref} { self.get_x() } + | ^^^^^^ + = note: the following trait bounds were not satisfied: + `Y: Iterator` + which is required by `&mut Y: Iterator` +note: the trait `Iterator` must be implemented + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following traits define an item `by_ref`, perhaps you need to implement one of them: + candidate #1: `std::io::Read` + candidate #2: `std::io::Write` + = note: the trait `Iterator` defines an item `by_ref`, but is explicitly unimplemented +help: use associated function syntax instead + | +LL - y.by_ref(); +LL + Y::by_ref(); + | + +error[E0599]: no method named `by_mut_ref` found for mutable reference `&mut Y` in the current scope + --> $DIR/inherent-impls-receiver-mapping.rs:46:11 + | +LL | y.by_mut_ref(); + | ^^^^^^^^^^ this is an associated function, not a method + | + = 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 `Y` + --> $DIR/inherent-impls-receiver-mapping.rs:17:47 + | +LL | reuse X::{static_f, by_value, by_ref, by_mut_ref} { self.get_x() } + | ^^^^^^^^^^ +help: use associated function syntax instead + | +LL - y.by_mut_ref(); +LL + Y::by_mut_ref(); + | + +error[E0599]: no method named `add` found for struct `W` in the current scope + --> $DIR/inherent-impls-receiver-mapping.rs:66:14 + | +LL | struct W(X); + | -------- method `add` not found for this struct +... +LL | W(X).add(W(X)); + | ^^^ this is an associated function, not a method + | + = 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 `W` + --> $DIR/inherent-impls-receiver-mapping.rs:61:18 + | +LL | reuse X::add { self.0 } + | ^^^ + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `add`, perhaps you need to implement it: + candidate #1: `Add` +help: use associated function syntax instead + | +LL - W(X).add(W(X)); +LL + W::add(W(X)); + | +help: one of the expressions' fields has a method of the same name + | +LL | W(X).0.add(W(X)); + | ++ + +error: aborting due to 15 previous errors + +Some errors have detailed explanations: E0425, E0599. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/delegation/inherent-impls-recursive-cycle.rs b/tests/ui/delegation/inherent-impls-recursive-cycle.rs new file mode 100644 index 0000000000000..0860ff39f51ee --- /dev/null +++ b/tests/ui/delegation/inherent-impls-recursive-cycle.rs @@ -0,0 +1,52 @@ +#![feature(fn_delegation)] + +trait Trait1 { + reuse trait_foo_reused as foo; +} + +impl Trait1 for () {} + +struct S1(T); +impl S1 { + reuse Trait1::foo { self.0 } + //~^ ERROR: delegation's target expression is specified for function with no params + //~| ERROR: this function takes 0 arguments but 1 argument was supplied +} + +struct S2(S1<()>); +impl S2 { + reuse S1::<()>::foo { self.0 } + //~^ ERROR: cannot find function `foo` in `S1` +} + +reuse S2::foo; +//~^ ERROR: cannot find function `foo` in `S2` + +struct S3; +impl S3 { + reuse foo; +} + +impl Trait1 for S3 { + reuse S2::foo { S2(S1(())) } + //~^ ERROR: delegation's target expression is specified for function with no params + //~| ERROR: cannot find function `foo` in `S2` +} + +trait Trait2 { + reuse ::foo { S3 } + //~^ ERROR: delegation's target expression is specified for function with no params + //~| ERROR: this function takes 0 arguments but 1 argument was supplied +} + +reuse Trait2::foo as trait_foo; + +struct S4; +impl S4 { + reuse trait_foo; +} + +reuse S4::trait_foo as trait_foo_reused; +//~^ ERROR: cannot find function `trait_foo` in `S4` + +fn main() {} diff --git a/tests/ui/delegation/inherent-impls-recursive-cycle.stderr b/tests/ui/delegation/inherent-impls-recursive-cycle.stderr new file mode 100644 index 0000000000000..a68931f8abce7 --- /dev/null +++ b/tests/ui/delegation/inherent-impls-recursive-cycle.stderr @@ -0,0 +1,80 @@ +error[E0425]: cannot find function `foo` in `S1` + --> $DIR/inherent-impls-recursive-cycle.rs:18:21 + | +LL | reuse S1::<()>::foo { self.0 } + | ^^^ not found in `S1` + +error[E0425]: cannot find function `foo` in `S2` + --> $DIR/inherent-impls-recursive-cycle.rs:22:11 + | +LL | reuse S2::foo; + | ^^^ not found in `S2` + +error[E0425]: cannot find function `foo` in `S2` + --> $DIR/inherent-impls-recursive-cycle.rs:31:15 + | +LL | reuse S2::foo { S2(S1(())) } + | ^^^ not found in `S2` + +error[E0425]: cannot find function `trait_foo` in `S4` + --> $DIR/inherent-impls-recursive-cycle.rs:49:11 + | +LL | reuse S4::trait_foo as trait_foo_reused; + | ^^^^^^^^^ not found in `S4` + +error: delegation's target expression is specified for function with no params + --> $DIR/inherent-impls-recursive-cycle.rs:11:23 + | +LL | reuse Trait1::foo { self.0 } + | ^^^^^^^^^^ + +error: delegation's target expression is specified for function with no params + --> $DIR/inherent-impls-recursive-cycle.rs:31:19 + | +LL | reuse S2::foo { S2(S1(())) } + | ^^^^^^^^^^^^^^ + +error: delegation's target expression is specified for function with no params + --> $DIR/inherent-impls-recursive-cycle.rs:37:31 + | +LL | reuse ::foo { S3 } + | ^^^^^^ + +error[E0061]: this function takes 0 arguments but 1 argument was supplied + --> $DIR/inherent-impls-recursive-cycle.rs:11:19 + | +LL | reuse Trait1::foo { self.0 } + | ^^^ ---------- unexpected argument + | +note: associated function defined here + --> $DIR/inherent-impls-recursive-cycle.rs:4:31 + | +LL | reuse trait_foo_reused as foo; + | ^^^ +help: remove the extra argument + | +LL - reuse Trait1::foo { self.0 } +LL + reuse Trait1::fo{ self.0 } + | + +error[E0061]: this function takes 0 arguments but 1 argument was supplied + --> $DIR/inherent-impls-recursive-cycle.rs:37:27 + | +LL | reuse ::foo { S3 } + | ^^^ ------ unexpected argument of type `S3` + | +note: associated function defined here + --> $DIR/inherent-impls-recursive-cycle.rs:4:31 + | +LL | reuse trait_foo_reused as foo; + | ^^^ +help: remove the extra argument + | +LL - reuse ::foo { S3 } +LL + reuse ::fo{ S3 } + | + +error: aborting due to 9 previous errors + +Some errors have detailed explanations: E0061, E0425. +For more information about an error, try `rustc --explain E0061`. diff --git a/tests/ui/delegation/inherent-impls-recursive.rs b/tests/ui/delegation/inherent-impls-recursive.rs new file mode 100644 index 0000000000000..c57ef40642346 --- /dev/null +++ b/tests/ui/delegation/inherent-impls-recursive.rs @@ -0,0 +1,69 @@ +#![feature(fn_delegation)] + +mod test_1 { + struct S1; + impl S1 { + fn foo() {} + } + + struct S2; + impl S2 { + reuse S1::foo; + //~^ ERROR: cannot find function `foo` in `S1` + } + + struct S3; + impl S3 { + reuse S2::foo; + //~^ ERROR: cannot find function `foo` in `S2` + } +} + +mod test_2 { + trait Trait1 { + fn foo(&self) {} + } + + impl Trait1 for () {} + + struct S1(T); + impl S1 { + reuse Trait1::foo { self.0 } + } + + struct S2(S1<()>); + impl S2 { + reuse S1::<()>::foo { self.0 } + //~^ ERROR: cannot find function `foo` in `S1` + } + + reuse S2::foo; + //~^ ERROR: cannot find function `foo` in `S2` + + struct S3; + impl S3 { + reuse foo; + } + + impl Trait1 for S3 { + reuse S2::foo { &S2(S1(())) } + //~^ ERROR: method `foo` has a `&self` declaration in the trait, but not in the impl + //~| ERROR: cannot find function `foo` in `S2` + } + + trait Trait2 { + reuse ::foo { S3 } + } + + reuse Trait2::foo as trait_foo; + + struct S4; + impl S4 { + reuse trait_foo; + } + + reuse S4::trait_foo as trait_foo_reused; + //~^ ERROR: cannot find function `trait_foo` in `S4` +} + +fn main() {} diff --git a/tests/ui/delegation/inherent-impls-recursive.stderr b/tests/ui/delegation/inherent-impls-recursive.stderr new file mode 100644 index 0000000000000..f80d9d3b84097 --- /dev/null +++ b/tests/ui/delegation/inherent-impls-recursive.stderr @@ -0,0 +1,61 @@ +error[E0425]: cannot find function `foo` in `S1` + --> $DIR/inherent-impls-recursive.rs:11:19 + | +LL | reuse S1::foo; + | ^^^ not found in `S1` + | +note: function `test_2::foo` exists but is inaccessible + --> $DIR/inherent-impls-recursive.rs:40:5 + | +LL | reuse S2::foo; + | ^^^^^^^^^^^^^^ not accessible + +error[E0425]: cannot find function `foo` in `S2` + --> $DIR/inherent-impls-recursive.rs:17:19 + | +LL | reuse S2::foo; + | ^^^ not found in `S2` + | +note: function `test_2::foo` exists but is inaccessible + --> $DIR/inherent-impls-recursive.rs:40:5 + | +LL | reuse S2::foo; + | ^^^^^^^^^^^^^^ not accessible + +error[E0425]: cannot find function `foo` in `S1` + --> $DIR/inherent-impls-recursive.rs:36:25 + | +LL | reuse S1::<()>::foo { self.0 } + | ^^^ not found in `S1` + +error[E0425]: cannot find function `foo` in `S2` + --> $DIR/inherent-impls-recursive.rs:40:15 + | +LL | reuse S2::foo; + | ^^^ not found in `S2` + +error[E0425]: cannot find function `foo` in `S2` + --> $DIR/inherent-impls-recursive.rs:49:19 + | +LL | reuse S2::foo { &S2(S1(())) } + | ^^^ not found in `S2` + +error[E0425]: cannot find function `trait_foo` in `S4` + --> $DIR/inherent-impls-recursive.rs:65:15 + | +LL | reuse S4::trait_foo as trait_foo_reused; + | ^^^^^^^^^ not found in `S4` + +error[E0186]: method `foo` has a `&self` declaration in the trait, but not in the impl + --> $DIR/inherent-impls-recursive.rs:49:19 + | +LL | fn foo(&self) {} + | ------------- `&self` used in trait +... +LL | reuse S2::foo { &S2(S1(())) } + | ^^^ expected `&self` in impl + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0186, E0425. +For more information about an error, try `rustc --explain E0186`. diff --git a/tests/ui/delegation/inherent-impls-rename.rs b/tests/ui/delegation/inherent-impls-rename.rs new file mode 100644 index 0000000000000..7b6e1e4b7cddc --- /dev/null +++ b/tests/ui/delegation/inherent-impls-rename.rs @@ -0,0 +1,14 @@ +#![feature(fn_delegation)] + +struct X; + +fn foo() {} + +impl X { + reuse foo as bar; +} + +reuse X::bar; +//~^ ERROR: cannot find function `bar` in `X` + +fn main() {} diff --git a/tests/ui/delegation/inherent-impls-rename.stderr b/tests/ui/delegation/inherent-impls-rename.stderr new file mode 100644 index 0000000000000..e2e8946412e93 --- /dev/null +++ b/tests/ui/delegation/inherent-impls-rename.stderr @@ -0,0 +1,9 @@ +error[E0425]: cannot find function `bar` in `X` + --> $DIR/inherent-impls-rename.rs:11:10 + | +LL | reuse X::bar; + | ^^^ not found in `X` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/delegation/inherent-impls-self-mapping.rs b/tests/ui/delegation/inherent-impls-self-mapping.rs new file mode 100644 index 0000000000000..36aef3a7bc7d8 --- /dev/null +++ b/tests/ui/delegation/inherent-impls-self-mapping.rs @@ -0,0 +1,20 @@ +#![feature(fn_delegation)] + +use std::rc::Rc; + +struct X; +impl X { + fn foo(self: Rc>, other: Box>) -> Option> { + None + } +} + +trait Trait { + reuse X::foo; + //~^ ERROR: cannot find function `foo` in `X` +} + +reuse X::foo; +//~^ ERROR: cannot find function `foo` in `X` + +fn main() {} diff --git a/tests/ui/delegation/inherent-impls-self-mapping.stderr b/tests/ui/delegation/inherent-impls-self-mapping.stderr new file mode 100644 index 0000000000000..9ecf04626d29c --- /dev/null +++ b/tests/ui/delegation/inherent-impls-self-mapping.stderr @@ -0,0 +1,15 @@ +error[E0425]: cannot find function `foo` in `X` + --> $DIR/inherent-impls-self-mapping.rs:13:14 + | +LL | reuse X::foo; + | ^^^ not found in `X` + +error[E0425]: cannot find function `foo` in `X` + --> $DIR/inherent-impls-self-mapping.rs:17:10 + | +LL | reuse X::foo; + | ^^^ not found in `X` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/delegation/inherent-impls-self-replacement.rs b/tests/ui/delegation/inherent-impls-self-replacement.rs new file mode 100644 index 0000000000000..e6aa7458993fd --- /dev/null +++ b/tests/ui/delegation/inherent-impls-self-replacement.rs @@ -0,0 +1,56 @@ +#![feature(fn_delegation)] + +use std::rc::Rc; +use std::pin::Pin; + +struct S { + xd: [A; C], +} + +impl<'a, 'b, 'c, A, const C: usize> S { + fn by_value<'d: 'd, 'e, T, const B: bool>(self) {} + fn by_ref<'d: 'd, 'e, T, const B: bool>(&self) {} + fn by_mut_ref<'d: 'd, 'e, T, const B: bool>(&mut self) {} + fn by_box<'d: 'd, 'e, T, const B: bool>(self: Box) {} + fn by_rc<'d: 'd, 'e, T, const B: bool>(self: Rc) {} + fn by_pin<'d: 'd, 'e, T, const B: bool>(self: Pin>) {} +} + +trait Trait: Sized { + fn get_s(self) -> S<(), 123>; + + reuse S::<(), 123>::by_value { self.get_s() } + //~^ ERROR: cannot find function `by_value` in `S` + + reuse S::<(), 123>::by_ref { self.get_s() } + //~^ ERROR: cannot find function `by_ref` in `S` + + reuse S::<(), 123>::by_mut_ref { self.get_s() } + //~^ ERROR: cannot find function `by_mut_ref` in `S` + + reuse S::<(), 123>::by_box { self.get_s() } + //~^ ERROR: cannot find function `by_box` in `S` + + reuse S::<(), 123>::by_rc { self.get_s() } + //~^ ERROR: cannot find function `by_rc` in `S` + + reuse S::<(), 123>::by_pin { self.get_s() } + //~^ ERROR: cannot find function `by_pin` in `S` +} + +trait Trait2: Sized { + reuse S::<(), 123>::by_value { self.get_s() } + //~^ ERROR: cannot find function `by_value` in `S` + reuse S::<(), 123>::by_ref { self.get_s() } + //~^ ERROR: cannot find function `by_ref` in `S` + reuse S::<(), 123>::by_mut_ref { self.get_s() } + //~^ ERROR: cannot find function `by_mut_ref` in `S` + reuse S::<(), 123>::by_box { self.get_s() } + //~^ ERROR: cannot find function `by_box` in `S` + reuse S::<(), 123>::by_rc { self.get_s() } + //~^ ERROR: cannot find function `by_rc` in `S` + reuse S::<(), 123>::by_pin { self.get_s() } + //~^ ERROR: cannot find function `by_pin` in `S` +} + +fn main() {} diff --git a/tests/ui/delegation/inherent-impls-self-replacement.stderr b/tests/ui/delegation/inherent-impls-self-replacement.stderr new file mode 100644 index 0000000000000..e293635e4a1ea --- /dev/null +++ b/tests/ui/delegation/inherent-impls-self-replacement.stderr @@ -0,0 +1,75 @@ +error[E0425]: cannot find function `by_value` in `S` + --> $DIR/inherent-impls-self-replacement.rs:22:25 + | +LL | reuse S::<(), 123>::by_value { self.get_s() } + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `by_ref` in `S` + --> $DIR/inherent-impls-self-replacement.rs:25:25 + | +LL | reuse S::<(), 123>::by_ref { self.get_s() } + | ^^^^^^ not found in `S` + +error[E0425]: cannot find function `by_mut_ref` in `S` + --> $DIR/inherent-impls-self-replacement.rs:28:25 + | +LL | reuse S::<(), 123>::by_mut_ref { self.get_s() } + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `by_box` in `S` + --> $DIR/inherent-impls-self-replacement.rs:31:25 + | +LL | reuse S::<(), 123>::by_box { self.get_s() } + | ^^^^^^ not found in `S` + +error[E0425]: cannot find function `by_rc` in `S` + --> $DIR/inherent-impls-self-replacement.rs:34:25 + | +LL | reuse S::<(), 123>::by_rc { self.get_s() } + | ^^^^^ not found in `S` + +error[E0425]: cannot find function `by_pin` in `S` + --> $DIR/inherent-impls-self-replacement.rs:37:25 + | +LL | reuse S::<(), 123>::by_pin { self.get_s() } + | ^^^^^^ not found in `S` + +error[E0425]: cannot find function `by_value` in `S` + --> $DIR/inherent-impls-self-replacement.rs:42:25 + | +LL | reuse S::<(), 123>::by_value { self.get_s() } + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `by_ref` in `S` + --> $DIR/inherent-impls-self-replacement.rs:44:25 + | +LL | reuse S::<(), 123>::by_ref { self.get_s() } + | ^^^^^^ not found in `S` + +error[E0425]: cannot find function `by_mut_ref` in `S` + --> $DIR/inherent-impls-self-replacement.rs:46:25 + | +LL | reuse S::<(), 123>::by_mut_ref { self.get_s() } + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `by_box` in `S` + --> $DIR/inherent-impls-self-replacement.rs:48:25 + | +LL | reuse S::<(), 123>::by_box { self.get_s() } + | ^^^^^^ not found in `S` + +error[E0425]: cannot find function `by_rc` in `S` + --> $DIR/inherent-impls-self-replacement.rs:50:25 + | +LL | reuse S::<(), 123>::by_rc { self.get_s() } + | ^^^^^ not found in `S` + +error[E0425]: cannot find function `by_pin` in `S` + --> $DIR/inherent-impls-self-replacement.rs:52:25 + | +LL | reuse S::<(), 123>::by_pin { self.get_s() } + | ^^^^^^ not found in `S` + +error: aborting due to 12 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/delegation/inherent-impls-structs.rs b/tests/ui/delegation/inherent-impls-structs.rs new file mode 100644 index 0000000000000..a5950185b6dad --- /dev/null +++ b/tests/ui/delegation/inherent-impls-structs.rs @@ -0,0 +1,86 @@ +#![feature(fn_delegation)] + +struct S { + xd: [A; C], +} + +impl<'a, 'b, 'c, A, const C: usize> S { + fn foo_static<'d: 'd, 'e, T, const B: bool>() {} + fn foo_self<'d: 'd, 'e, T, const B: bool>(self) {} +} + +reuse S::<(), 1>::foo_static::<'static, (), true> as foo_static_1; +//~^ ERROR: cannot find function `foo_static` in `S` +reuse S::<(), 1>::foo_static as foo_static_3; +//~^ ERROR: cannot find function `foo_static` in `S` +reuse S::::foo_static::<'static, _, _> as foo_static_4; +//~^ ERROR: cannot find function `foo_static` in `S` + +reuse S::<(), 1>::foo_self::<'static, (), true> as foo_self_1; +//~^ ERROR: cannot find function `foo_self` in `S` +reuse S::<(), 1>::foo_self as foo_self_3; +//~^ ERROR: cannot find function `foo_self` in `S` +reuse S::::foo_self::<'static, _, _> as foo_self_4; +//~^ ERROR: cannot find function `foo_self` in `S` + +trait Trait<'a, AA, BB> where Self: Sized { + reuse S::<(), 1>::foo_static::<'static, (), true> as foo_static_1; + //~^ ERROR: cannot find function `foo_static` in `S` + reuse S::<(), 1>::foo_static as foo_static_3; + //~^ ERROR: cannot find function `foo_static` in `S` + reuse S::<(), 1>::foo_static::<'static, _, _> as foo_static_4; + //~^ ERROR: cannot find function `foo_static` in `S` + + fn get_s(self) -> S<(), 1> { + panic!(); + } + + reuse S::<(), 1>::foo_self::<'static, (), true> as foo_self_1 { self.get_s() } + //~^ ERROR: cannot find function `foo_self` in `S` + reuse S::<(), 1>::foo_self as foo_self_3 { self.get_s() } + //~^ ERROR: cannot find function `foo_self` in `S` + reuse S::<(), 1>::foo_self::<'static, _, _> as foo_self_4; + //~^ ERROR: cannot find function `foo_self` in `S` +} + +struct X; + +impl<'a, A, B> Trait<'a, A, B> for X { + reuse S::<(), 1>::foo_static::<'static, (), true> as foo_static_1; + //~^ ERROR: cannot find function `foo_static` in `S` + reuse S::<(), 1>::foo_static as foo_static_3; + //~^ ERROR: cannot find function `foo_static` in `S` + reuse S::<(), 1>::foo_static::<'static, _, _> as foo_static_4; + //~^ ERROR: cannot find function `foo_static` in `S` + + reuse S::<(), 1>::foo_self::<'static, (), true> as foo_self_1 { self.get_s() } + //~^ ERROR: cannot find function `foo_self` in `S` + //~| ERROR: delegation's target expression is specified for function with no params + reuse S::<(), 1>::foo_self as foo_self_3 { self.get_s() } + //~^ ERROR: cannot find function `foo_self` in `S` + //~| ERROR: delegation's target expression is specified for function with no params + reuse S::<(), 1>::foo_self::<'static, _, _> as foo_self_4; + //~^ ERROR: cannot find function `foo_self` in `S` +} + +impl X { + reuse S::<(), 1>::foo_static::<'static, (), true> as foo_static_1; + //~^ ERROR: cannot find function `foo_static` in `S` + reuse S::<(), 1>::foo_static as foo_static_3; + //~^ ERROR: cannot find function `foo_static` in `S` + reuse S::<(), 1>::foo_static::<'static, _, _> as foo_static_4; + //~^ ERROR: cannot find function `foo_static` in `S` + + fn get_s(self) -> S<(), 1> { + panic!(); + } + + reuse S::<(), 1>::foo_self::<'static, (), true> as foo_self_1 { self.get_s() } + //~^ ERROR: cannot find function `foo_self` in `S` + reuse S::<(), 1>::foo_self as foo_self_3 { self.get_s() } + //~^ ERROR: cannot find function `foo_self` in `S` + reuse S::<(), 1>::foo_self::<'static, _, _> as foo_self_4; + //~^ ERROR: cannot find function `foo_self` in `S` +} + +fn main() {} diff --git a/tests/ui/delegation/inherent-impls-structs.stderr b/tests/ui/delegation/inherent-impls-structs.stderr new file mode 100644 index 0000000000000..6a426564a6e3a --- /dev/null +++ b/tests/ui/delegation/inherent-impls-structs.stderr @@ -0,0 +1,159 @@ +error[E0425]: cannot find function `foo_static` in `S` + --> $DIR/inherent-impls-structs.rs:12:19 + | +LL | reuse S::<(), 1>::foo_static::<'static, (), true> as foo_static_1; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in `S` + --> $DIR/inherent-impls-structs.rs:14:19 + | +LL | reuse S::<(), 1>::foo_static as foo_static_3; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in `S` + --> $DIR/inherent-impls-structs.rs:16:22 + | +LL | reuse S::::foo_static::<'static, _, _> as foo_static_4; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in `S` + --> $DIR/inherent-impls-structs.rs:19:19 + | +LL | reuse S::<(), 1>::foo_self::<'static, (), true> as foo_self_1; + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in `S` + --> $DIR/inherent-impls-structs.rs:21:19 + | +LL | reuse S::<(), 1>::foo_self as foo_self_3; + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in `S` + --> $DIR/inherent-impls-structs.rs:23:23 + | +LL | reuse S::::foo_self::<'static, _, _> as foo_self_4; + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in `S` + --> $DIR/inherent-impls-structs.rs:27:23 + | +LL | reuse S::<(), 1>::foo_static::<'static, (), true> as foo_static_1; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in `S` + --> $DIR/inherent-impls-structs.rs:29:23 + | +LL | reuse S::<(), 1>::foo_static as foo_static_3; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in `S` + --> $DIR/inherent-impls-structs.rs:31:23 + | +LL | reuse S::<(), 1>::foo_static::<'static, _, _> as foo_static_4; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in `S` + --> $DIR/inherent-impls-structs.rs:38:23 + | +LL | reuse S::<(), 1>::foo_self::<'static, (), true> as foo_self_1 { self.get_s() } + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in `S` + --> $DIR/inherent-impls-structs.rs:40:23 + | +LL | reuse S::<(), 1>::foo_self as foo_self_3 { self.get_s() } + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in `S` + --> $DIR/inherent-impls-structs.rs:42:23 + | +LL | reuse S::<(), 1>::foo_self::<'static, _, _> as foo_self_4; + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in `S` + --> $DIR/inherent-impls-structs.rs:49:23 + | +LL | reuse S::<(), 1>::foo_static::<'static, (), true> as foo_static_1; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in `S` + --> $DIR/inherent-impls-structs.rs:51:23 + | +LL | reuse S::<(), 1>::foo_static as foo_static_3; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in `S` + --> $DIR/inherent-impls-structs.rs:53:23 + | +LL | reuse S::<(), 1>::foo_static::<'static, _, _> as foo_static_4; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in `S` + --> $DIR/inherent-impls-structs.rs:56:23 + | +LL | reuse S::<(), 1>::foo_self::<'static, (), true> as foo_self_1 { self.get_s() } + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in `S` + --> $DIR/inherent-impls-structs.rs:59:23 + | +LL | reuse S::<(), 1>::foo_self as foo_self_3 { self.get_s() } + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in `S` + --> $DIR/inherent-impls-structs.rs:62:23 + | +LL | reuse S::<(), 1>::foo_self::<'static, _, _> as foo_self_4; + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in `S` + --> $DIR/inherent-impls-structs.rs:67:23 + | +LL | reuse S::<(), 1>::foo_static::<'static, (), true> as foo_static_1; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in `S` + --> $DIR/inherent-impls-structs.rs:69:23 + | +LL | reuse S::<(), 1>::foo_static as foo_static_3; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_static` in `S` + --> $DIR/inherent-impls-structs.rs:71:23 + | +LL | reuse S::<(), 1>::foo_static::<'static, _, _> as foo_static_4; + | ^^^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in `S` + --> $DIR/inherent-impls-structs.rs:78:23 + | +LL | reuse S::<(), 1>::foo_self::<'static, (), true> as foo_self_1 { self.get_s() } + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in `S` + --> $DIR/inherent-impls-structs.rs:80:23 + | +LL | reuse S::<(), 1>::foo_self as foo_self_3 { self.get_s() } + | ^^^^^^^^ not found in `S` + +error[E0425]: cannot find function `foo_self` in `S` + --> $DIR/inherent-impls-structs.rs:82:23 + | +LL | reuse S::<(), 1>::foo_self::<'static, _, _> as foo_self_4; + | ^^^^^^^^ not found in `S` + +error: delegation's target expression is specified for function with no params + --> $DIR/inherent-impls-structs.rs:56:67 + | +LL | reuse S::<(), 1>::foo_self::<'static, (), true> as foo_self_1 { self.get_s() } + | ^^^^^^^^^^^^^^^^ + +error: delegation's target expression is specified for function with no params + --> $DIR/inherent-impls-structs.rs:59:46 + | +LL | reuse S::<(), 1>::foo_self as foo_self_3 { self.get_s() } + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 26 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/delegation/inherent-impls-wrong-header-args-ice.rs b/tests/ui/delegation/inherent-impls-wrong-header-args-ice.rs new file mode 100644 index 0000000000000..8375df5f26587 --- /dev/null +++ b/tests/ui/delegation/inherent-impls-wrong-header-args-ice.rs @@ -0,0 +1,17 @@ +#![feature(fn_delegation)] + +struct S<'a, A, const C: usize> { + xd: &'a [A; C], +} + +impl<'a, 'b, 'c, A, const C: usize> S { +//~^ ERROR: implicit elided lifetime not allowed here + fn foo_self<'d: 'd, 'e, T, const B: bool>(self) {} +} + +trait Trait<'a, AA, BB> where Self: Sized { + reuse S::<(), ()>::foo_self; + //~^ ERROR: cannot find function `foo_self` in `S` +} + +fn main() {} diff --git a/tests/ui/delegation/inherent-impls-wrong-header-args-ice.stderr b/tests/ui/delegation/inherent-impls-wrong-header-args-ice.stderr new file mode 100644 index 0000000000000..bfa377dcc2db6 --- /dev/null +++ b/tests/ui/delegation/inherent-impls-wrong-header-args-ice.stderr @@ -0,0 +1,21 @@ +error[E0726]: implicit elided lifetime not allowed here + --> $DIR/inherent-impls-wrong-header-args-ice.rs:7:37 + | +LL | impl<'a, 'b, 'c, A, const C: usize> S { + | ^^^^^^^ expected lifetime parameter + | +help: indicate the anonymous lifetime + | +LL | impl<'a, 'b, 'c, A, const C: usize> S<'_, A, C> { + | +++ + +error[E0425]: cannot find function `foo_self` in `S` + --> $DIR/inherent-impls-wrong-header-args-ice.rs:13:24 + | +LL | reuse S::<(), ()>::foo_self; + | ^^^^^^^^ not found in `S` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0425, E0726. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/never_type/never-from-impl-is-reserved.rs b/tests/ui/never_type/never-from-impl-is-not-reserved.rs similarity index 53% rename from tests/ui/never_type/never-from-impl-is-reserved.rs rename to tests/ui/never_type/never-from-impl-is-not-reserved.rs index c673462f2962a..22b1c14db0bdf 100644 --- a/tests/ui/never_type/never-from-impl-is-reserved.rs +++ b/tests/ui/never_type/never-from-impl-is-not-reserved.rs @@ -1,5 +1,7 @@ -// check that the `for T: From` impl is reserved +// check that the `for T: From` impl is not reserved anymore +//@ check-pass +// //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver=coherence @@ -10,7 +12,6 @@ pub struct MyFoo; pub trait MyTrait {} impl MyTrait for MyFoo {} -// This will conflict with the first impl if we impl `for T: From`. -impl MyTrait for T where T: From {} //~ ERROR conflicting implementation +impl MyTrait for T where T: From {} fn main() {} diff --git a/tests/ui/never_type/never-from-impl-is-reserved.current.stderr b/tests/ui/never_type/never-from-impl-is-reserved.current.stderr deleted file mode 100644 index 7868206950c34..0000000000000 --- a/tests/ui/never_type/never-from-impl-is-reserved.current.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0119]: conflicting implementations of trait `MyTrait` for type `MyFoo` - --> $DIR/never-from-impl-is-reserved.rs:14:1 - | -LL | impl MyTrait for MyFoo {} - | ---------------------- first implementation here -LL | // This will conflict with the first impl if we impl `for T: From`. -LL | impl MyTrait for T where T: From {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyFoo` - | - = note: permitting this impl would forbid us from adding `impl From for T` later; see rust-lang/rust#64715 for details - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/never_type/never-from-impl-is-reserved.next.stderr b/tests/ui/never_type/never-from-impl-is-reserved.next.stderr deleted file mode 100644 index 7868206950c34..0000000000000 --- a/tests/ui/never_type/never-from-impl-is-reserved.next.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0119]: conflicting implementations of trait `MyTrait` for type `MyFoo` - --> $DIR/never-from-impl-is-reserved.rs:14:1 - | -LL | impl MyTrait for MyFoo {} - | ---------------------- first implementation here -LL | // This will conflict with the first impl if we impl `for T: From`. -LL | impl MyTrait for T where T: From {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyFoo` - | - = note: permitting this impl would forbid us from adding `impl From for T` later; see rust-lang/rust#64715 for details - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0119`.