diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 632f138a4c798..bde6614b48548 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1378,15 +1378,6 @@ pub enum UnsafeSource { UserProvided, } -/// Track whether under `feature(min_generic_const_args)` this anon const -/// was explicitly disambiguated as an anon const or not through the use of -/// `const { ... }` syntax. -#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy, Walkable)] -pub enum MgcaDisambiguation { - AnonConst, - Direct, -} - /// A constant (expression) that's not an item or associated item, /// but needs its own `DefId` for type-checking, const-eval, etc. /// These are usually found nested inside types (e.g., array lengths) @@ -1396,7 +1387,6 @@ pub enum MgcaDisambiguation { pub struct AnonConst { pub id: NodeId, pub value: Box, - pub mgca_disambiguation: MgcaDisambiguation, } /// An expression. @@ -1627,6 +1617,7 @@ impl Expr { | ExprKind::UnsafeBinderCast(..) | ExprKind::While(..) | ExprKind::Yield(YieldKind::Postfix(..)) + | ExprKind::DirectConstArg(..) | ExprKind::Err(_) | ExprKind::Dummy => prefix_attrs_precedence(&self.attrs), } @@ -1920,6 +1911,9 @@ pub enum ExprKind { UnsafeBinderCast(UnsafeBinderCastKind, Box, Option>), + /// An mGCA `direct_const_arg!()` expression. + DirectConstArg(Box), + /// Placeholder for an expression that wasn't syntactically well formed in some way. Err(ErrorGuaranteed), @@ -2566,6 +2560,8 @@ pub enum TyKind { FieldOf(Box, Option, Ident), /// A view of a type. `T.{ field_1, field_2 }`. View(Box, #[visitable(ignore)] ThinVec), + /// An mGCA `direct_const_arg!()` expression. + DirectConstArg(Box), /// Sometimes we need a dummy value when no error has occurred. Dummy, /// Placeholder for a kind that has failed to be defined. @@ -3066,6 +3062,9 @@ impl FnDecl { /// Must have the same value as `FnSigKind::NO_SPLATTED_ARG_INDEX` and `FnDeclFlags::NO_SPLATTED_ARG_INDEX`. pub const NO_SPLATTED_ARG_INDEX: u8 = u8::MAX; + /// The maximum valid splatted argument index. + pub const MAX_VALID_SPLATTED_ARG_INDEX: u8 = Self::NO_SPLATTED_ARG_INDEX - 1; + /// Returns a splatted argument index, if any are present. pub fn splatted(&self) -> Option { self.inputs.iter().enumerate().find_map(|(index, arg)| { diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index baad20a86784d..98caac2c88f77 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -22,6 +22,9 @@ use crate::ast_traits::{HasAttrs, HasTokens}; use crate::token::{self, Delimiter, Token, TokenKind}; use crate::{AttrVec, Attribute}; +#[cfg(test)] +mod tests; + /// Part of a `TokenStream`. #[derive(Debug, Clone, PartialEq, Eq, Hash, Encodable, Decodable, StableHash)] pub enum TokenTree { @@ -833,21 +836,18 @@ impl StableHash for TokenStream { } #[derive(Clone)] -pub struct TokenStreamIter<'t> { - stream: &'t TokenStream, - index: usize, -} +pub struct TokenStreamIter<'t>(std::slice::Iter<'t, TokenTree>); impl<'t> TokenStreamIter<'t> { fn new(stream: &'t TokenStream) -> Self { - TokenStreamIter { stream, index: 0 } + TokenStreamIter(stream.0.as_slice().iter()) } // Peeking could be done via `Peekable`, but most iterators need peeking, // and this is simple and avoids the need to use `peekable` and `Peekable` // at all the use sites. pub fn peek(&self) -> Option<&'t TokenTree> { - self.stream.0.get(self.index) + self.0.as_slice().first() } } @@ -855,10 +855,11 @@ impl<'t> Iterator for TokenStreamIter<'t> { type Item = &'t TokenTree; fn next(&mut self) -> Option<&'t TokenTree> { - self.stream.0.get(self.index).map(|tree| { - self.index += 1; - tree - }) + self.0.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() } } diff --git a/compiler/rustc_ast/src/tokenstream/tests.rs b/compiler/rustc_ast/src/tokenstream/tests.rs new file mode 100644 index 0000000000000..6c7e82a97c58e --- /dev/null +++ b/compiler/rustc_ast/src/tokenstream/tests.rs @@ -0,0 +1,13 @@ +use rustc_span::DUMMY_SP; + +use crate::token::TokenKind; +use crate::tokenstream::TokenStream; + +#[test] +fn test_token_stream_iter() { + let ts = TokenStream::token_alone(TokenKind::Eq, DUMMY_SP); + assert_eq!(ts.len(), 1); + + let iter = ts.iter(); + assert_eq!(iter.size_hint(), (1, Some(1))); +} diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index 56f96f9a8a279..0c2218e557f23 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -155,6 +155,7 @@ pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool { | Yeet(..) | Yield(..) | UnsafeBinderCast(..) + | DirectConstArg(..) | Err(..) | Dummy => return false, } @@ -240,6 +241,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option> { | Try(_) | Yeet(None) | UnsafeBinderCast(..) + | DirectConstArg(..) | Err(_) | Dummy => { break None; @@ -301,9 +303,10 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> { | ast::TyKind::CVarArgs | ast::TyKind::Pat(..) | ast::TyKind::FieldOf(..) + | ast::TyKind::View(..) + | ast::TyKind::DirectConstArg(..) | ast::TyKind::Dummy - | ast::TyKind::Err(..) - | ast::TyKind::View(..) => break None, + | ast::TyKind::Err(..) => break None, } } } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index fb4e76321d150..e25c1a0b31937 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -418,7 +418,6 @@ macro_rules! common_visitor_and_walkers { UnsafeBinderCastKind, BinOpKind, BlockCheckMode, - MgcaDisambiguation, BorrowKind, BoundAsyncness, BoundConstness, @@ -1074,6 +1073,8 @@ macro_rules! common_visitor_and_walkers { visit_visitable!($($mut)? vis, bytes), ExprKind::UnsafeBinderCast(kind, expr, ty) => visit_visitable!($($mut)? vis, kind, expr, ty), + ExprKind::DirectConstArg(expr) => + visit_visitable!($($mut)? vis, expr), ExprKind::Err(_guar) => {} ExprKind::Dummy => {} } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index f66d1ac16c907..4ed23e032d234 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -498,6 +498,18 @@ impl<'hir> LoweringContext<'_, 'hir> { } ExprKind::MacCall(_) => panic!("{:?} shouldn't exist here", e.span), + + ExprKind::DirectConstArg(_) => { + let e = self + .tcx + .dcx() + .struct_span_err( + e.span, + "expected expression, found `direct_const_arg!()` constant", + ) + .emit(); + hir::ExprKind::Err(e) + } }; hir::Expr { hir_id: expr_hir_id, kind, span } @@ -599,11 +611,7 @@ impl<'hir> LoweringContext<'_, 'hir> { arg }; - let anon_const = AnonConst { - id: node_id, - value: const_value, - mgca_disambiguation: MgcaDisambiguation::AnonConst, - }; + let anon_const = AnonConst { id: node_id, value: const_value }; generic_args.push(AngleBracketedArg::Arg(GenericArg::Const(anon_const))); } else { real_args.push(arg); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index dc1acade85ed5..8753897cb53d3 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1478,6 +1478,16 @@ impl<'hir> LoweringContext<'_, 'hir> { } } } + TyKind::DirectConstArg(expr) + if self.tcx.features().min_generic_const_args() => + { + let ct = match self.can_lower_expr_to_const_arg_direct(expr) { + Ok(()) => self.lower_expr_to_const_arg_direct(expr, None), + Err(e) => e.emit(self), + }; + let ct = self.arena.alloc(ct); + return GenericArg::Const(ct.try_as_ambig_ct().unwrap()); + } _ => {} } GenericArg::Type(self.lower_ty_alloc(ty, itctx).try_as_ambig_ty().unwrap()) @@ -1750,9 +1760,18 @@ impl<'hir> LoweringContext<'_, 'hir> { ); hir::TyKind::Err(guar) } - TyKind::View(ty, _) => { - // FIXME(scrabsha): lower view types to HIR. - return self.lower_ty(ty, itctx); + TyKind::View(ty, fields) => { + let ty = self.lower_ty_alloc(ty, itctx); + let fields = self.arena.alloc_slice(fields); + hir::TyKind::View(ty, fields) + } + TyKind::DirectConstArg(_) => { + let e = self + .tcx + .dcx() + .struct_span_err(t.span, "expected type, found `direct_const_arg!()` constant") + .emit(); + hir::TyKind::Err(e) } TyKind::Dummy => panic!("`TyKind::Dummy` should never be lowered"), }; @@ -2672,23 +2691,86 @@ impl<'hir> LoweringContext<'_, 'hir> { } #[instrument(level = "debug", skip(self), ret)] - fn lower_expr_to_const_arg_direct(&mut self, expr: &Expr) -> hir::ConstArg<'hir> { - let span = self.lower_span(expr.span); - - let overly_complex_const = |this: &mut Self| { - let msg = "complex const arguments must be placed inside of a `const` block"; - let e = if expr::WillCreateDefIdsVisitor.visit_expr(expr).is_break() { - // FIXME(mgca): make this non-fatal once we have a better way to handle - // nested items in const args - // Issue: https://github.com/rust-lang/rust/issues/154539 - this.dcx().struct_span_fatal(expr.span, msg).emit() - } else { - this.dcx().struct_span_err(expr.span, msg).emit() - }; + fn can_lower_expr_to_const_arg_direct( + &mut self, + expr: &Expr, + ) -> Result<(), UnrepresentableConstArgError> { + let is_mgca = self.tcx.features().min_generic_const_args(); + // Note the only stable case is currently ExprKind::Path. All others have an is_mgca guard. + match &expr.kind { + ExprKind::Call(func, args) + if is_mgca && let ExprKind::Path(_qself, _path) = &func.kind => + { + for arg in args { + self.can_lower_expr_to_const_arg_direct(arg)?; + } + Ok(()) + } + ExprKind::Tup(exprs) if is_mgca => { + for expr in exprs { + self.can_lower_expr_to_const_arg_direct(expr)?; + } + Ok(()) + } + ExprKind::Path(qself, path) + if is_mgca + || path.is_potential_trivial_const_arg() + && matches!( + self.get_partial_res(expr.id) + .and_then(|partial_res| partial_res.full_res()), + Some(Res::Def(DefKind::ConstParam, _)) + ) => + { + Ok(()) + } + ExprKind::Struct(se) if is_mgca => { + for f in &se.fields { + self.can_lower_expr_to_const_arg_direct(&f.expr)?; + } + Ok(()) + } + ExprKind::Array(elements) if is_mgca => { + for element in elements { + self.can_lower_expr_to_const_arg_direct(element)?; + } + Ok(()) + } + ExprKind::Underscore if is_mgca => Ok(()), + ExprKind::Block(block, _) + if is_mgca + && let [stmt] = block.stmts.as_slice() + && let StmtKind::Expr(expr) = &stmt.kind => + { + self.can_lower_expr_to_const_arg_direct(expr) + } + ExprKind::Lit(literal) if is_mgca => Ok(()), + ExprKind::Unary(UnOp::Neg, inner_expr) + if is_mgca && let ExprKind::Lit(_) = &inner_expr.kind => + { + Ok(()) + } + ExprKind::ConstBlock(anon) if is_mgca => Ok(()), + ExprKind::DirectConstArg(expr) if is_mgca => { + // Always report this as able to be represented directly. If it turns out not to be, + // `lower_expr_to_const_arg_direct` will report an error. + Ok(()) + } + _ => Err(UnrepresentableConstArgError::new(expr)), + } + } - ConstArg { hir_id: this.next_id(), kind: hir::ConstArgKind::Error(e), span } - }; + /// It is not allowed to call this function without checking can_lower_expr_to_const_arg_direct + /// first, as we assume all feature gates/etc. have been checked already. + #[instrument(level = "debug", skip(self), ret)] + fn lower_expr_to_const_arg_direct( + &mut self, + expr: &Expr, + id_override: Option, + ) -> hir::ConstArg<'hir> { + debug_assert!(self.can_lower_expr_to_const_arg_direct(expr).is_ok()); + let span = self.lower_span(expr.span); + let node_id = id_override.unwrap_or(expr.id); match &expr.kind { ExprKind::Call(func, args) if let ExprKind::Path(qself, path) = &func.kind => { let qpath = self.lower_qpath( @@ -2702,23 +2784,27 @@ impl<'hir> LoweringContext<'_, 'hir> { ); let lowered_args = self.arena.alloc_from_iter(args.iter().map(|arg| { - let const_arg = self.lower_expr_to_const_arg_direct(arg); + let const_arg = self.lower_expr_to_const_arg_direct(arg, None); &*self.arena.alloc(const_arg) })); ConstArg { - hir_id: self.next_id(), + hir_id: self.lower_node_id(node_id), kind: hir::ConstArgKind::TupleCall(qpath, lowered_args), span, } } ExprKind::Tup(exprs) => { let exprs = self.arena.alloc_from_iter(exprs.iter().map(|expr| { - let expr = self.lower_expr_to_const_arg_direct(&expr); + let expr = self.lower_expr_to_const_arg_direct(expr, None); &*self.arena.alloc(expr) })); - ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Tup(exprs), span } + ConstArg { + hir_id: self.lower_node_id(node_id), + kind: hir::ConstArgKind::Tup(exprs), + span, + } } ExprKind::Path(qself, path) => { let qpath = self.lower_qpath( @@ -2732,7 +2818,11 @@ impl<'hir> LoweringContext<'_, 'hir> { None, ); - ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Path(qpath), span } + ConstArg { + hir_id: self.lower_node_id(node_id), + kind: hir::ConstArgKind::Path(qpath), + span, + } } ExprKind::Struct(se) => { let path = self.lower_qpath( @@ -2754,7 +2844,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // then go unused as the `Target::ExprField` is not actually // corresponding to `Node::ExprField`. self.lower_attrs(hir_id, &f.attrs, f.span, Target::ExprField); - let expr = self.lower_expr_to_const_arg_direct(&f.expr); + let expr = self.lower_expr_to_const_arg_direct(&f.expr, None); &*self.arena.alloc(hir::ConstArgExprField { hir_id, @@ -2765,14 +2855,14 @@ impl<'hir> LoweringContext<'_, 'hir> { })); ConstArg { - hir_id: self.next_id(), + hir_id: self.lower_node_id(node_id), kind: hir::ConstArgKind::Struct(path, fields), span, } } ExprKind::Array(elements) => { let lowered_elems = self.arena.alloc_from_iter(elements.iter().map(|element| { - let const_arg = self.lower_expr_to_const_arg_direct(element); + let const_arg = self.lower_expr_to_const_arg_direct(element, None); &*self.arena.alloc(const_arg) })); let array_expr = self.arena.alloc(hir::ConstArgArrayExpr { @@ -2781,31 +2871,28 @@ impl<'hir> LoweringContext<'_, 'hir> { }); ConstArg { - hir_id: self.next_id(), + hir_id: self.lower_node_id(node_id), kind: hir::ConstArgKind::Array(array_expr), span, } } ExprKind::Underscore => ConstArg { - hir_id: self.lower_node_id(expr.id), + hir_id: self.lower_node_id(node_id), kind: hir::ConstArgKind::Infer(()), span, }, - ExprKind::Block(block, _) => { + ExprKind::Block(block, _) if let [stmt] = block.stmts.as_slice() - && let StmtKind::Expr(expr) = &stmt.kind - { - return self.lower_expr_to_const_arg_direct(expr); - } - - overly_complex_const(self) + && let StmtKind::Expr(expr) = &stmt.kind => + { + return self.lower_expr_to_const_arg_direct(expr, id_override); } ExprKind::Lit(literal) => { let span = self.lower_span(expr.span); let literal = self.lower_lit(literal, span); ConstArg { - hir_id: self.lower_node_id(expr.id), + hir_id: self.lower_node_id(node_id), kind: hir::ConstArgKind::Literal { lit: literal.node, negated: false }, span, } @@ -2816,29 +2903,45 @@ impl<'hir> LoweringContext<'_, 'hir> { let span = self.lower_span(expr.span); let literal = self.lower_lit(literal, span); - if !matches!(literal.node, LitKind::Int(..)) { + let kind = if !matches!(literal.node, LitKind::Int(..)) { let err = self.dcx().struct_span_err(expr.span, "negated literal must be an integer"); - - return ConstArg { - hir_id: self.next_id(), - kind: hir::ConstArgKind::Error(err.emit()), - span, - }; - } - - ConstArg { - hir_id: self.lower_node_id(expr.id), - kind: hir::ConstArgKind::Literal { lit: literal.node, negated: true }, - span, - } + hir::ConstArgKind::Error(err.emit()) + } else { + hir::ConstArgKind::Literal { lit: literal.node, negated: true } + }; + ConstArg { hir_id: self.lower_node_id(node_id), kind, span } } ExprKind::ConstBlock(anon_const) => { + // Do not use lower_anon_const_to_const_arg, as that attempts to represent the body + // directly. Instead, force an anon const. let def_id = self.local_def_id(anon_const.id); assert_eq!(DefKind::InlineConst, self.tcx.def_kind(def_id)); - self.lower_anon_const_to_const_arg(anon_const, span) + let lowered_anon = self.lower_anon_const_to_anon_const(anon_const, span); + ConstArg { + hir_id: self.lower_node_id(node_id), + kind: hir::ConstArgKind::Anon(lowered_anon), + span, + } + } + ExprKind::DirectConstArg(expr) => { + // `can_lower_expr_to_const_arg_direct` always returns success upon encountering a + // ExprKind::DirectConstArg, which effectively forces the expression to be lowered + // as a direct arg. If it actually turns out to not be possible, emit an error + // instead. + match self.can_lower_expr_to_const_arg_direct(expr) { + Ok(()) => self.lower_expr_to_const_arg_direct(expr, id_override), + Err(err) => err.emit(self), + } + } + _ => { + span_bug!( + expr.span, + "lower_expr_to_const_arg_direct encountered an unlowerable expression, either \ + can_lower_expr_to_const_arg_direct returned Ok() on something it shouldn't \ + have, or you forgot to check can_lower_expr_to_const_arg_direct first" + ); } - _ => overly_complex_const(self), } } @@ -2857,67 +2960,23 @@ impl<'hir> LoweringContext<'_, 'hir> { anon: &AnonConst, span: Span, ) -> hir::ConstArg<'hir> { - let tcx = self.tcx; - - // We cannot change parsing depending on feature gates available, - // we can only require feature gates to be active as a delayed check. - // Thus we just parse anon consts generally and make the real decision - // making in ast lowering. - // FIXME(min_generic_const_args): revisit once stable - if tcx.features().min_generic_const_args() { - return match anon.mgca_disambiguation { - MgcaDisambiguation::AnonConst => { - let lowered_anon = self.lower_anon_const_to_anon_const(anon, span); - ConstArg { - hir_id: self.next_id(), - kind: hir::ConstArgKind::Anon(lowered_anon), - span: lowered_anon.span, - } - } - MgcaDisambiguation::Direct => self.lower_expr_to_const_arg_direct(&anon.value), - }; - } - - // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments - // currently have to be wrapped in curly brackets, so it's necessary to special-case. - let expr = if let ExprKind::Block(block, _) = &anon.value.kind - && let [stmt] = block.stmts.as_slice() - && let StmtKind::Expr(expr) = &stmt.kind - && let ExprKind::Path(..) = &expr.kind - { - expr - } else { + // Stable only allows one nesting of blocks for directly represented paths. mGCA allows + // arbitrarily many, and are handled inside lower_expr_to_const_arg_direct for consistency. + let expr = if self.tcx.features().min_generic_const_args() { &anon.value + } else { + anon.value.maybe_unwrap_block() }; - let maybe_res = - self.get_partial_res(expr.id).and_then(|partial_res| partial_res.full_res()); - if let ExprKind::Path(qself, path) = &expr.kind - && path.is_potential_trivial_const_arg() - && matches!(maybe_res, Some(Res::Def(DefKind::ConstParam, _))) - { - let qpath = self.lower_qpath( - expr.id, - qself, - path, - ParamMode::Explicit, - AllowReturnTypeNotation::No, - ImplTraitContext::Disallowed(ImplTraitPosition::Path), - None, - ); - - return ConstArg { - hir_id: self.lower_node_id(anon.id), - kind: hir::ConstArgKind::Path(qpath), - span: self.lower_span(expr.span), - }; + if self.can_lower_expr_to_const_arg_direct(expr).is_ok() { + return self.lower_expr_to_const_arg_direct(expr, Some(anon.id)); } let lowered_anon = self.lower_anon_const_to_anon_const(anon, anon.value.span); ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Anon(lowered_anon), - span: self.lower_span(expr.span), + span: self.lower_span(anon.value.span), } } @@ -3213,3 +3272,36 @@ impl<'hir> GenericArgsCtor<'hir> { this.arena.alloc(ga) } } + +#[derive(Debug)] +struct UnrepresentableConstArgError { + span: Span, + will_create_def_ids: bool, +} + +impl UnrepresentableConstArgError { + fn new(expr: &Expr) -> Self { + Self { + span: expr.span, + will_create_def_ids: expr::WillCreateDefIdsVisitor.visit_expr(expr).is_break(), + } + } + + fn emit<'hir>(self, lowering_context: &mut LoweringContext<'_, 'hir>) -> ConstArg<'hir> { + let msg = "complex const arguments must be placed inside of a `const` block"; + let e = if self.will_create_def_ids { + // FIXME(mgca): make this non-fatal once we have a better way to handle + // nested items in const args + // Issue: https://github.com/rust-lang/rust/issues/154539 + lowering_context.dcx().struct_span_fatal(self.span, msg).emit() + } else { + lowering_context.dcx().struct_span_err(self.span, msg).emit() + }; + + ConstArg { + hir_id: lowering_context.next_id(), + kind: hir::ConstArgKind::Error(e), + span: self.span, + } + } +} diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 06925994b052c..281f417500c55 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -16,6 +16,7 @@ //! constructions produced by proc macros. This pass is only intended for simple checks that do not //! require name resolution or type checking, or other kinds of complex analysis. +use std::collections::BTreeMap; use std::mem; use std::str::FromStr; @@ -34,7 +35,7 @@ use rustc_session::lint::builtin::{ DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN, PATTERNS_IN_FNS_WITHOUT_BODY, UNUSED_VISIBILITIES, }; -use rustc_span::{Ident, Span, kw, sym}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use rustc_target::spec::{AbiMap, AbiMapping}; use crate::diagnostics::{self, TildeConstReason}; @@ -45,6 +46,41 @@ enum SelfSemantic { No, } +/// Is `#[splat]` allowed semantically in a function or closure? +/// Only applies to the function kind and header, the parameters are checked elsewhere. +enum SplatSemantic { + Yes, + NoClosures(Span), + NoAbiCall { span: Span, abi: Symbol }, +} + +impl SplatSemantic { + /// Returns if splatting is semantically allowed for the given `FnKind`, + /// Only checks the function kind and header, not the parameters. + fn from_fn_kind(fk: &FnKind<'_>) -> Self { + match fk { + FnKind::Fn(_, _, f) => Self::from_extern(f.sig.header.ext), + // Splatting closures is banned, because closure arguments are already de-tupled. + FnKind::Closure(_, _, _, expr) => SplatSemantic::NoClosures(expr.span), + } + } + + fn from_extern(ext: Extern) -> Self { + match ext { + Extern::None => SplatSemantic::Yes, + // FIXME(splat): should splatting extern "C" or other ABIs be allowed? + Extern::Implicit(_) => SplatSemantic::Yes, + // For now, splatting rust-call is banned, because it already de-tuples args. + Extern::Explicit(abi_str, span) => match abi_str.symbol_unescaped { + sym::rust_dash_call => { + SplatSemantic::NoAbiCall { span, abi: abi_str.symbol_unescaped } + } + _ => SplatSemantic::Yes, + }, + } + } +} + enum TraitOrImpl { Trait { vis: Span, constness: Const }, TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref_span: Span }, @@ -350,10 +386,15 @@ impl<'a> AstValidator<'a> { }); } - fn check_fn_decl(&self, fn_decl: &FnDecl, self_semantic: SelfSemantic) { + fn check_fn_decl( + &self, + fn_decl: &FnDecl, + self_semantic: SelfSemantic, + splat_semantic: SplatSemantic, + ) { self.check_decl_num_args(fn_decl); let c_variadic_span = self.check_decl_cvariadic_pos(fn_decl); - self.check_decl_splatting(fn_decl, c_variadic_span); + self.check_decl_splatting(fn_decl, c_variadic_span, splat_semantic); self.check_decl_attrs(fn_decl); self.check_decl_self_param(fn_decl, self_semantic); } @@ -399,42 +440,76 @@ impl<'a> AstValidator<'a> { /// Emits an error if a function declaration has more than one splatted argument, with a /// C-variadic parameter, or a splat at an unsupported index (for performance). /// Example: `fn foo(#[splat] x: (), #[splat] y: ())` will emit an error. - fn check_decl_splatting(&self, fn_decl: &FnDecl, c_variadic_span: Option) { - let (splatted_arg_indexes, mut splatted_spans): (Vec, Vec) = fn_decl + fn check_decl_splatting( + &self, + fn_decl: &FnDecl, + c_variadic_span: Option, + splat_semantic: SplatSemantic, + ) { + let mut splatted_arg_spans: BTreeMap> = fn_decl .inputs .iter() .enumerate() .filter_map(|(index, arg)| { - arg.attrs + let splat_arg_spans: Vec = arg + .attrs .iter() - .any(|attr| attr.has_name(sym::splat)) - .then_some((u16::try_from(index).unwrap(), arg.span)) + .filter_map(|attr| attr.has_name(sym::splat).then_some(attr.span)) + .collect(); + if splat_arg_spans.is_empty() { + None + } else { + Some((u16::try_from(index).unwrap(), splat_arg_spans)) + } }) - .unzip(); + .collect(); // A splatted argument greater than or equal to the "no splatted" marker index is not - // supported. - if let (Some(&splatted_arg_index), Some(&splatted_span)) = - (splatted_arg_indexes.last(), splatted_spans.last()) - && splatted_arg_index >= u16::from(FnDecl::NO_SPLATTED_ARG_INDEX) - { - self.dcx().emit_err(diagnostics::InvalidSplattedArg { - splatted_arg_index, - span: splatted_span, + // supported. It is ok to drop these spans after issuing this error, because they are + // always invalid. + let out_of_range_spans = + splatted_arg_spans.split_off(&u16::from(FnDecl::NO_SPLATTED_ARG_INDEX)); + if !out_of_range_spans.is_empty() { + self.dcx().emit_err(diagnostics::InvalidSplattedArgs { + max_valid_splatted_arg_index: u16::from(FnDecl::MAX_VALID_SPLATTED_ARG_INDEX), + first_invalid_splatted_arg_index: *out_of_range_spans.keys().next().unwrap(), + spans: out_of_range_spans.values().flatten().copied().collect(), }); } - // Multiple splatted arguments are invalid: we can't know which arguments go in each splat. - if splatted_arg_indexes.len() > 1 { - self.dcx() - .emit_err(diagnostics::DuplicateSplattedArgs { spans: splatted_spans.clone() }); - } + if !splatted_arg_spans.is_empty() { + let splatted_spans = || splatted_arg_spans.values().flatten().copied().collect(); - if let Some(c_variadic_span) = c_variadic_span - && !splatted_spans.is_empty() - { - splatted_spans.push(c_variadic_span); - self.dcx().emit_err(diagnostics::CVarArgsAndSplat { spans: splatted_spans }); + // Multiple splatted arguments are invalid: we can't know which arguments go in each splat. + if splatted_arg_spans.len() > 1 { + self.dcx().emit_err(diagnostics::DuplicateSplattedArgs { spans: splatted_spans() }); + } + + // C-variadic parameters and splats are not allowed together. + if let Some(c_variadic_span) = c_variadic_span { + let mut splatted_spans = splatted_spans(); + splatted_spans.push(c_variadic_span); + self.dcx().emit_err(diagnostics::CVarArgsAndSplat { spans: splatted_spans }); + } + + // Splatting is not allowed on closures, or some function ABIs. + match splat_semantic { + SplatSemantic::NoClosures(closure_span) => { + let mut splatted_spans = splatted_spans(); + splatted_spans.push(closure_span); + self.dcx() + .emit_err(diagnostics::SplatNotAllowedOnClosures { spans: splatted_spans }); + } + SplatSemantic::NoAbiCall { span, abi } => { + let mut splatted_spans = splatted_spans(); + splatted_spans.push(span); + self.dcx().emit_err(diagnostics::SplatNotAllowedOnAbiCall { + spans: splatted_spans, + abi, + }); + } + SplatSemantic::Yes => {} + } } } @@ -1055,7 +1130,11 @@ impl<'a> AstValidator<'a> { match &ty.kind { TyKind::FnPtr(bfty) => { self.check_fn_ptr_safety(bfty.decl_span, bfty.safety); - self.check_fn_decl(&bfty.decl, SelfSemantic::No); + self.check_fn_decl( + &bfty.decl, + SelfSemantic::No, + SplatSemantic::from_extern(bfty.ext), + ); Self::check_decl_no_pat(&bfty.decl, |span, _, _| { self.dcx().emit_err(diagnostics::PatternFnPointer { span }); }); @@ -1746,7 +1825,8 @@ impl Visitor<'_> for AstValidator<'_> { Some(FnCtxt::Assoc(_)) => SelfSemantic::Yes, _ => SelfSemantic::No, }; - self.check_fn_decl(fk.decl(), self_semantic); + let splat_semantic = SplatSemantic::from_fn_kind(&fk); + self.check_fn_decl(fk.decl(), self_semantic, splat_semantic); if let Some(&FnHeader { safety, .. }) = fk.header() { self.check_item_safety(span, safety); diff --git a/compiler/rustc_ast_passes/src/diagnostics.rs b/compiler/rustc_ast_passes/src/diagnostics.rs index 7a78e8e6213a5..0b610431cadd8 100644 --- a/compiler/rustc_ast_passes/src/diagnostics.rs +++ b/compiler/rustc_ast_passes/src/diagnostics.rs @@ -124,18 +124,22 @@ pub(crate) struct FnParamCVarArgsNotLast { } #[derive(Diagnostic)] -#[diag("`#[splat]` is not supported on argument index {$splatted_arg_index}")] +#[diag( + "`#[splat]` is only supported on argument index {$max_valid_splatted_arg_index} or less, this `#[splat]` is on index {$first_invalid_splatted_arg_index}" +)] #[help("remove `#[splat]`, or use it on an argument closer to the start of the argument list")] -pub(crate) struct InvalidSplattedArg { - pub splatted_arg_index: u16, +pub(crate) struct InvalidSplattedArgs { + pub max_valid_splatted_arg_index: u16, + + pub first_invalid_splatted_arg_index: u16, #[primary_span] #[label("`#[splat]` is not supported here")] - pub span: Span, + pub spans: Vec, } #[derive(Diagnostic)] -#[diag("multiple `#[splat]`s are not allowed in the same function")] +#[diag("multiple `#[splat]`s are not allowed in the same function argument list")] #[help("remove `#[splat]` from all but one argument")] pub(crate) struct DuplicateSplattedArgs { #[primary_span] @@ -143,13 +147,30 @@ pub(crate) struct DuplicateSplattedArgs { } #[derive(Diagnostic)] -#[diag("`...` and `#[splat]` are not allowed in the same function")] +#[diag("`...` and `#[splat]` are not allowed in the same function argument list")] #[help("remove `#[splat]` or remove `...`")] pub(crate) struct CVarArgsAndSplat { #[primary_span] pub spans: Vec, } +#[derive(Diagnostic)] +#[diag("`#[splat]` is not allowed on closure arguments")] +#[help("remove `#[splat]` or turn the closure into a function")] +pub(crate) struct SplatNotAllowedOnClosures { + #[primary_span] + pub spans: Vec, +} + +#[derive(Diagnostic)] +#[diag("`#[splat]` is not allowed in the arguments of functions with the `{$abi}` ABI")] +#[help("remove `#[splat]` or change the ABI")] +pub(crate) struct SplatNotAllowedOnAbiCall { + #[primary_span] + pub spans: Vec, + pub abi: Symbol, +} + #[derive(Diagnostic)] #[diag("documentation comments cannot be applied to function parameters")] pub(crate) struct FnParamDocComment { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 106606877e110..bfca18a42635e 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1459,6 +1459,12 @@ impl<'a> State<'a> { self.print_type(ty); self.print_view(fields); } + ast::TyKind::DirectConstArg(expr) => { + self.word_nbsp("core::direct_const_arg!"); + self.popen(); + self.print_expr(expr, FixupContext::default()); + self.pclose(); + } } self.end(ib); } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 176e91e544ec5..4f3281641359d 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -883,6 +883,12 @@ impl<'a> State<'a> { self.word("/*DUMMY*/"); self.pclose(); } + ast::ExprKind::DirectConstArg(expr) => { + self.word_nbsp("core::direct_const_arg!"); + self.popen(); + self.print_expr(expr, FixupContext::default()); + self.pclose() + } } self.ann.post(self, AnnNode::Expr(expr)); diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index 9edf7103f3990..64ba106b68adc 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -70,7 +70,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> { // Doesn't have any language semantics | StatementKind::Coverage(..) // Does not actually affect borrowck - | StatementKind::StorageLive(..) => {} + | StatementKind::StorageLive(..) + // Does not affect borrowck + | StatementKind::BackwardIncompatibleDropHint { .. } => {} StatementKind::StorageDead(local) => { self.access_place( location, @@ -81,7 +83,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> { } StatementKind::ConstEvalCounter | StatementKind::Nop - | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::SetDiscriminant { .. } => { bug!("Statement not allowed in this MIR phase") } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 64aa92c2c8878..505aa74b04437 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1630,7 +1630,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { }); // Edge case: it's possible that `'from_region` is an unnameable placeholder. - let path = if let Some(unnameable) = due_to_placeholder_outlives + let mut path = if let Some(unnameable) = due_to_placeholder_outlives && unnameable != from_region { // We ignore the extra edges due to unnameable placeholders to get @@ -1799,10 +1799,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { if let ConstraintCategory::ClosureUpvar(f) = p.category { Some(f) } else { None } }) { - OutlivesConstraint { - category: ConstraintCategory::Return(ReturnConstraint::ClosureUpvar(field)), - ..path[best_choice] - } + path[best_choice].category = + ConstraintCategory::Return(ReturnConstraint::ClosureUpvar(field)); + path[best_choice] } else { path[best_choice] }; diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 5a06f54cd2e41..8229be10960f9 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -2,7 +2,6 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_index::bit_set::DenseBitSet; use rustc_index::interval::IntervalSet; use rustc_infer::infer::canonical::QueryRegionConstraints; -use rustc_infer::infer::outlives::for_liveness; use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, HasLocalDecls, Local, Location}; use rustc_middle::traits::query::DropckOutlivesResult; use rustc_middle::ty::relate::Relate; @@ -14,6 +13,7 @@ use rustc_mir_dataflow::{Analysis, ResultsCursor}; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::ObligationCtxt; +use rustc_trait_selection::traits::outlives_for_liveness::FreeRegionsVisitor; use rustc_trait_selection::traits::query::dropck_outlives; use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOpOutput}; use tracing::debug; @@ -611,7 +611,7 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { values::pretty_print_points(location_map, live_at.iter()), ); - value.visit_with(&mut for_liveness::FreeRegionsVisitor { + value.visit_with(&mut FreeRegionsVisitor { tcx: typeck.tcx(), param_env: typeck.infcx.param_env, op: |r| { diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index f15acc154baf3..1bc2bc8342559 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -323,6 +323,7 @@ impl<'cx, 'a> Context<'cx, 'a> { | ExprKind::Yeet(_) | ExprKind::Become(_) | ExprKind::Yield(_) + | ExprKind::DirectConstArg(_) | ExprKind::UnsafeBinderCast(..) => {} } } diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 311a24280cfb7..bd5bf3a687d92 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -16,7 +16,7 @@ mod llvm_enzyme { use rustc_ast::{ self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocItemKind, BindingMode, FnRetTy, FnSig, GenericArg, GenericArgs, GenericParamKind, Generics, ItemKind, - MetaItemInner, MgcaDisambiguation, PatKind, Path, PathSegment, TyKind, Visibility, + MetaItemInner, PatKind, Path, PathSegment, TyKind, Visibility, }; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_hir::attrs::RustcAutodiff; @@ -602,11 +602,7 @@ mod llvm_enzyme { } GenericParamKind::Const { .. } => { let expr = ecx.expr_path(ast::Path::from_ident(p.ident)); - let anon_const = AnonConst { - id: ast::DUMMY_NODE_ID, - value: expr, - mgca_disambiguation: MgcaDisambiguation::Direct, - }; + let anon_const = AnonConst { id: ast::DUMMY_NODE_ID, value: expr }; Some(AngleBracketedArg::Arg(GenericArg::Const(anon_const))) } GenericParamKind::Lifetime { .. } => None, @@ -861,7 +857,6 @@ mod llvm_enzyme { let anon_const = rustc_ast::AnonConst { id: ast::DUMMY_NODE_ID, value: ecx.expr_usize(span, 1 + x.width as usize), - mgca_disambiguation: MgcaDisambiguation::Direct, }; TyKind::Array(ty.clone(), anon_const) }; @@ -876,7 +871,6 @@ mod llvm_enzyme { let anon_const = rustc_ast::AnonConst { id: ast::DUMMY_NODE_ID, value: ecx.expr_usize(span, x.width as usize), - mgca_disambiguation: MgcaDisambiguation::Direct, }; let kind = TyKind::Array(ty.clone(), anon_const); let ty = diff --git a/compiler/rustc_builtin_macros/src/direct_const_arg.rs b/compiler/rustc_builtin_macros/src/direct_const_arg.rs new file mode 100644 index 0000000000000..51c169134e03f --- /dev/null +++ b/compiler/rustc_builtin_macros/src/direct_const_arg.rs @@ -0,0 +1,39 @@ +use rustc_ast::ast; +use rustc_ast::tokenstream::TokenStream; +use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; +use rustc_span::Span; + +use crate::util::get_single_expr_from_tts; + +pub(crate) fn expand<'cx>( + cx: &'cx mut ExtCtxt<'_>, + span: Span, + tts: TokenStream, +) -> MacroExpanderResult<'cx> { + let ExpandResult::Ready(expr) = get_single_expr_from_tts(cx, span, tts, "direct_const_arg!") + else { + return ExpandResult::Retry(()); + }; + let expr = match expr { + Ok(expr) => expr, + Err(err) => return ExpandResult::Ready(DummyResult::any(span, err)), + }; + + let id = ast::DUMMY_NODE_ID; + ExpandResult::Ready(Box::new(base::MacEager { + expr: Some(Box::new(ast::Expr { + id, + kind: ast::ExprKind::DirectConstArg(expr.clone()), + span, + attrs: Default::default(), + tokens: None, + })), + ty: Some(Box::new(ast::Ty { + id, + kind: ast::TyKind::DirectConstArg(expr), + span, + tokens: None, + })), + ..Default::default() + })) +} diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index bd99b269ef5b6..78bf7d97bd7b8 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -34,6 +34,7 @@ mod define_opaque; mod derive; mod deriving; mod diagnostics; +mod direct_const_arg; mod edition_panic; mod eii; mod env; @@ -81,6 +82,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { concat_bytes: concat_bytes::expand_concat_bytes, const_format_args: format::expand_format_args, core_panic: edition_panic::expand_panic, + direct_const_arg: direct_const_arg::expand, env: env::expand_env, file: source_util::expand_file, format_args: format::expand_format_args, diff --git a/compiler/rustc_builtin_macros/src/pattern_type.rs b/compiler/rustc_builtin_macros/src/pattern_type.rs index 53ab3fcd9b34b..065ba9f6a2096 100644 --- a/compiler/rustc_builtin_macros/src/pattern_type.rs +++ b/compiler/rustc_builtin_macros/src/pattern_type.rs @@ -1,5 +1,5 @@ use rustc_ast::tokenstream::TokenStream; -use rustc_ast::{AnonConst, DUMMY_NODE_ID, MgcaDisambiguation, Ty, TyPat, TyPatKind, ast, token}; +use rustc_ast::{AnonConst, DUMMY_NODE_ID, Ty, TyPat, TyPatKind, ast, token}; use rustc_errors::PResult; use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; use rustc_parse::exp; @@ -60,20 +60,8 @@ fn ty_pat(kind: TyPatKind, span: Span) -> TyPat { fn pat_to_ty_pat(cx: &mut ExtCtxt<'_>, pat: ast::Pat) -> TyPat { let kind = match pat.kind { ast::PatKind::Range(start, end, include_end) => TyPatKind::Range( - start.map(|value| { - Box::new(AnonConst { - id: DUMMY_NODE_ID, - value, - mgca_disambiguation: MgcaDisambiguation::Direct, - }) - }), - end.map(|value| { - Box::new(AnonConst { - id: DUMMY_NODE_ID, - value, - mgca_disambiguation: MgcaDisambiguation::Direct, - }) - }), + start.map(|value| Box::new(AnonConst { id: DUMMY_NODE_ID, value })), + end.map(|value| Box::new(AnonConst { id: DUMMY_NODE_ID, value })), include_end, ), ast::PatKind::Or(variants) => { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 3d09b7aab8854..e56bc8ed7e82a 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -30,6 +30,7 @@ use rustc_metadata::{ walk_native_lib_search_dirs, }; use rustc_middle::bug; +use rustc_middle::error::DuplicateEiiImpls; use rustc_middle::lint::emit_lint_base; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Linkage; @@ -76,6 +77,64 @@ pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { } } +fn eii_impl_crate_name(crate_info: &CrateInfo, cnum: CrateNum) -> Symbol { + if cnum == LOCAL_CRATE { crate_info.local_crate_name } else { crate_info.crate_name[&cnum] } +} + +fn check_externally_implementable_item_linkage(sess: &Session, crate_info: &CrateInfo) { + if crate_info.eii_linkage.is_empty() { + return; + } + + // A crate can request multiple linked outputs with overlapping dependency + // formats, so report each underlying conflict once. + let mut emitted = FxHashSet::default(); + + // This needs the dependency formats selected for the final artifact. The + // earlier EII pass still handles missing impls and duplicate explicit impls. + for dependency_formats in crate_info.dependency_formats.values() { + for (eii_index, eii) in crate_info.eii_linkage.iter().enumerate() { + let Some(explicit_impl) = eii.impls.first() else { + continue; + }; + // If the explicit impl is already coming from a dylib, that dylib + // has already resolved the default-vs-explicit choice. + if matches!( + dependency_formats.get(explicit_impl.impl_crate), + Some(Linkage::Dynamic | Linkage::IncludedFromDylib) + ) { + continue; + } + + let Some(default_impl) = &eii.default_impl else { + continue; + }; + if !matches!( + dependency_formats.get(default_impl.impl_crate), + Some(Linkage::Dynamic | Linkage::IncludedFromDylib) + ) { + continue; + } + + if !emitted.insert(eii_index) { + continue; + } + + sess.dcx().emit_err(DuplicateEiiImpls { + name: eii.name, + first_span: explicit_impl.span, + first_crate: eii_impl_crate_name(crate_info, explicit_impl.impl_crate), + second_span: default_impl.span, + second_crate: eii_impl_crate_name(crate_info, default_impl.impl_crate), + help: (), + additional_crates: None, + num_additional_crates: 0, + additional_crate_names: String::new(), + }); + } + } +} + /// Performs the linkage portion of the compilation phase. This will generate all /// of the requested outputs for this compilation session. pub fn link_binary( @@ -91,6 +150,14 @@ pub fn link_binary( let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); let mut tempfiles_for_stdout_output: Vec = Vec::new(); let mut rmeta_link_cache = RmetaLinkCache::default(); + + if outputs.outputs.should_link() { + sess.time("check_externally_implementable_item_linkage", || { + check_externally_implementable_item_linkage(sess, &crate_info); + }); + sess.dcx().abort_if_errors(); + } + for &crate_type in &crate_info.crate_types { // Ignore executable crates if we have -Z no-codegen, as they will error. if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen()) diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 4e979df471318..bd39466508226 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -1,7 +1,7 @@ -use std::cmp; use std::collections::BTreeSet; use std::sync::Arc; use std::time::{Duration, Instant}; +use std::{cmp, iter}; use itertools::Itertools; use rustc_abi::FIRST_VARIANT; @@ -9,17 +9,17 @@ use rustc_ast::expand::allocator::{ ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorMethod, AllocatorMethodInput, AllocatorTy, }; -use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; use rustc_data_structures::unord::UnordMap; -use rustc_hir::attrs::{DebuggerVisualizerType, OptimizeAttr}; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::attrs::{DebuggerVisualizerType, EiiDecl, EiiImpl, OptimizeAttr}; +use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ItemId, Target, find_attr}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; -use rustc_middle::middle::dependency_format::Dependencies; +use rustc_middle::middle::dependency_format::{Dependencies, Linkage}; use rustc_middle::middle::exported_symbols::{self, SymbolExportKind}; use rustc_middle::middle::lang_items; use rustc_middle::mir::BinOp; @@ -50,7 +50,8 @@ use crate::mir::operand::OperandValue; use crate::mir::place::PlaceRef; use crate::traits::*; use crate::{ - CachedModuleCodegen, CodegenLintLevelSpecs, CrateInfo, ModuleCodegen, errors, meth, mir, + CachedModuleCodegen, CodegenLintLevelSpecs, CrateInfo, EiiLinkageImplInfo, EiiLinkageInfo, + ModuleCodegen, errors, meth, mir, }; pub(crate) fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate { @@ -877,12 +878,16 @@ pub fn codegen_crate< /// Returns whether a call from the current crate to the [`Instance`] would produce a call /// from `compiler_builtins` to a symbol the linker must resolve. /// -/// Such calls from `compiler_bultins` are effectively impossible for the linker to handle. Some +/// Such calls from `compiler_builtins` are effectively impossible for the linker to handle. Some /// linkers will optimize such that dead calls to unresolved symbols are not an error, but this is -/// not guaranteed. So we used this function in codegen backends to ensure we do not generate any +/// not guaranteed. So we use this function in codegen backends to ensure we do not generate any /// unlinkable calls. /// /// Note that calls to LLVM intrinsics are uniquely okay because they won't make it to the linker. +/// Note also that calls to foreign items that are actually exported by the local crate are also +/// okay. This situation arises because compiler-builtins calls functions in core that are +/// `#[inline]` wrappers for `extern "C"` declarations in core, which resolve to a symbol exported +/// by compiler-builtins. pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, @@ -895,11 +900,84 @@ pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>( } } + fn is_extern_call_to_local_crate<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> bool { + tcx.is_foreign_item(instance.def_id()) + && tcx.exported_non_generic_symbols(LOCAL_CRATE).iter().any(|(sym, _info)| { + sym.symbol_name_for_local_instance(tcx) == tcx.symbol_name(instance) + }) + } + let def_id = instance.def_id(); !def_id.is_local() && tcx.is_compiler_builtins(LOCAL_CRATE) && !is_llvm_intrinsic(tcx, def_id) && !tcx.should_codegen_locally(instance) + && !is_extern_call_to_local_crate(tcx, instance) +} + +fn collect_eii_linkage(tcx: TyCtxt<'_>) -> Vec { + #[derive(Debug)] + struct FoundImpl { + imp: EiiImpl, + impl_crate: CrateNum, + } + + #[derive(Debug)] + struct FoundEii { + decl: EiiDecl, + impls: FxIndexMap, + } + + let mut eiis = FxIndexMap::::default(); + + for &cnum in tcx.crates(()).iter().chain(iter::once(&LOCAL_CRATE)) { + for (&did, &(decl, ref impls)) in tcx.externally_implementable_items(cnum) { + eiis.entry(did) + .or_insert_with(|| FoundEii { decl, impls: Default::default() }) + .impls + .extend( + impls + .into_iter() + .map(|(&did, &imp)| (did, FoundImpl { imp, impl_crate: cnum })), + ); + } + } + + eiis.into_iter() + .filter_map(|(_, FoundEii { decl, impls })| { + let mut explicit_impls = Vec::new(); + let mut default_impl = None; + + for (impl_did, FoundImpl { imp, impl_crate }) in impls { + let impl_info = EiiLinkageImplInfo { span: tcx.def_span(impl_did), impl_crate }; + if imp.is_default { + default_impl = Some(impl_info); + } else { + explicit_impls.push(impl_info); + } + } + + // Link time check is only needed when there may be a default impl in a dylib. + // Other cases emit an error in `rustc_passes` already. + if let Some(default_impl) = default_impl { + Some(EiiLinkageInfo { + name: decl.name.name, + impls: explicit_impls, + default_impl: Some(default_impl), + }) + } else { + None + } + }) + .collect() +} + +fn eii_linkage_needed(dependency_formats: &Dependencies) -> bool { + dependency_formats.values().any(|formats| { + formats + .iter() + .any(|&linkage| matches!(linkage, Linkage::Dynamic | Linkage::IncludedFromDylib)) + }) } impl CrateInfo { @@ -913,6 +991,12 @@ impl CrateInfo { crate_types.iter().map(|&c| (c, crate::back::linker::linked_symbols(tcx, c))).collect(); let local_crate_name = tcx.crate_name(LOCAL_CRATE); let windows_subsystem = find_attr!(tcx, crate, WindowsSubsystem(kind) => *kind); + let dependency_formats = Arc::clone(tcx.dependency_formats(())); + let eii_linkage = if eii_linkage_needed(&dependency_formats) { + collect_eii_linkage(tcx) + } else { + Vec::new() + }; // This list is used when generating the command line to pass through to // system linker. The linker expects undefined symbols on the left of the @@ -957,7 +1041,8 @@ impl CrateInfo { crate_name: UnordMap::with_capacity(n_crates), used_crates, used_crate_source: UnordMap::with_capacity(n_crates), - dependency_formats: Arc::clone(tcx.dependency_formats(())), + dependency_formats, + eii_linkage, windows_subsystem, natvis_debugger_visualizers: Default::default(), lint_level_specs: CodegenLintLevelSpecs::from_tcx(tcx), diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 200f6aac12502..14c74ab5fb69b 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -40,7 +40,7 @@ use rustc_session::Session; use rustc_session::config::{CrateType, OutputFilenames, OutputType}; use rustc_session::cstore::{self, CrateSource}; use rustc_session::lint::builtin::LINKER_MESSAGES; -use rustc_span::Symbol; +use rustc_span::{Span, Symbol}; pub mod assert_module_sources; pub mod back; @@ -256,6 +256,19 @@ impl SymbolExport { } } +#[derive(Clone, Debug, Encodable, Decodable)] +pub struct EiiLinkageImplInfo { + pub span: Span, + pub impl_crate: CrateNum, +} + +#[derive(Clone, Debug, Encodable, Decodable)] +pub struct EiiLinkageInfo { + pub name: Symbol, + pub impls: Vec, + pub default_impl: Option, +} + /// Misc info we load from metadata to persist beyond the tcx. /// /// Note: though `CrateNum` is only meaningful within the same tcx, information within `CrateInfo` @@ -282,6 +295,9 @@ pub struct CrateInfo { pub used_crate_source: UnordMap>, pub used_crates: Vec, pub dependency_formats: Arc, + /// EII implementations used by the link-time duplicate check, so `-Zno-link` can serialize the data needed by a + /// later `-Zlink-only` invocation. + pub eii_linkage: Vec, pub windows_subsystem: Option, pub natvis_debugger_visualizers: BTreeSet, pub lint_level_specs: CodegenLintLevelSpecs, diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 31353d1eac0fb..b4a6cd33a60f4 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -598,15 +598,26 @@ fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) { match sess.io.input { Input::File(ref path) => { let mut v = Vec::new(); - locator::list_file_metadata( + if let Err(error) = locator::list_file_metadata( &sess.target, path, metadata_loader, &mut v, &sess.opts.unstable_opts.ls, sess.cfg_version, - ) - .unwrap(); + ) { + if path.extension().is_some_and(|extension| extension == "rs") { + let mut err = sess + .dcx() + .struct_fatal("`-Zls` takes a `.rmeta` file as input, not a source file"); + if rustc_session::utils::was_invoked_from_cargo() { + // Give a Cargo-tailored suggestion if we're coming from Cargo + err.note("use `rustc +nightly -Zls=... path/to/file.rmeta` directly, instead of going through Cargo"); + } + err.emit(); + } + sess.dcx().fatal(error.to_string()); + } safe_println!("{}", String::from_utf8(v).unwrap()); } Input::Str { .. } => { diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 01886a97f55a2..f3792d4d45235 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -2,8 +2,8 @@ use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::TokenStream; use rustc_ast::util::literal; use rustc_ast::{ - self as ast, AnonConst, AttrItem, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, - MgcaDisambiguation, PatKind, UnOp, attr, token, tokenstream, + self as ast, AnonConst, AttrItem, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, + UnOp, attr, token, tokenstream, }; use rustc_span::{DUMMY_SP, Ident, Span, Spanned, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; @@ -100,7 +100,6 @@ impl<'a> ExtCtxt<'a> { attrs: AttrVec::new(), tokens: None, }), - mgca_disambiguation: MgcaDisambiguation::Direct, } } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index ee401376a6a5c..236ccca1a517e 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3960,6 +3960,8 @@ pub enum TyKind<'hir, Unambig = ()> { /// /// The optional ident is the variant when an enum is passed `field_of!(Enum, Variant.field)`. FieldOf(&'hir Ty<'hir>, &'hir TyFieldPath), + /// A view of a type. `T.{ field_1, field_2 }`. + View(&'hir Ty<'hir>, &'hir [Ident]), /// `TyKind::Infer` means the type should be inferred instead of it having been /// specified. This can appear anywhere in a type. /// diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 44501b1aa892b..df410d680f533 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1053,6 +1053,12 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v, AmbigArg>) - visit_opt!(visitor, visit_ident, *variant); try_visit!(visitor.visit_ident(*field)); } + TyKind::View(ty, fields) => { + try_visit!(visitor.visit_ty_unambig(ty)); + for field in fields { + try_visit!(visitor.visit_ident(*field)); + } + } } V::Result::output() } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 12dc20de7ff70..81c33c0181cdf 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -13,8 +13,8 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_hir::{AmbigArg, ItemKind, find_attr}; +use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{self, InferCtxt, SubregionOrigin, TyCtxtInferExt}; use rustc_infer::traits::PredicateObligations; use rustc_lint_defs::builtin::SHADOWING_SUPERTRAIT_ITEMS; use rustc_macros::Diagnostic; @@ -30,7 +30,9 @@ use rustc_middle::{bug, span_bug}; use rustc_session::errors::feature_err; use rustc_span::{DUMMY_SP, Span, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; -use rustc_trait_selection::regions::{InferCtxtRegionExt, OutlivesEnvironmentBuildExt}; +use rustc_trait_selection::regions::{ + InferCtxtRegionExt, OutlivesEnvironmentBuildExt, region_known_to_outlive, ty_known_to_outlive, +}; use rustc_trait_selection::traits::misc::{ ConstParamTyImplementationError, type_allowed_to_implement_const_param_ty, }; @@ -690,70 +692,6 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable>>( Some(bounds) } -/// Given a known `param_env` and a set of well formed types, can we prove that -/// `ty` outlives `region`. -fn ty_known_to_outlive<'tcx>( - tcx: TyCtxt<'tcx>, - id: LocalDefId, - param_env: ty::ParamEnv<'tcx>, - wf_tys: &FxIndexSet>, - ty: Ty<'tcx>, - region: ty::Region<'tcx>, -) -> bool { - test_region_obligations(tcx, id, param_env, wf_tys, |infcx| { - infcx.register_type_outlives_constraint_inner(infer::TypeOutlivesConstraint { - sub_region: region, - sup_type: ty, - origin: SubregionOrigin::RelateParamBound(DUMMY_SP, ty, None), - }); - }) -} - -/// Given a known `param_env` and a set of well formed types, can we prove that -/// `region_a` outlives `region_b` -fn region_known_to_outlive<'tcx>( - tcx: TyCtxt<'tcx>, - id: LocalDefId, - param_env: ty::ParamEnv<'tcx>, - wf_tys: &FxIndexSet>, - region_a: ty::Region<'tcx>, - region_b: ty::Region<'tcx>, -) -> bool { - test_region_obligations(tcx, id, param_env, wf_tys, |infcx| { - infcx.sub_regions( - SubregionOrigin::RelateRegionParamBound(DUMMY_SP, None), - region_b, - region_a, - ty::VisibleForLeakCheck::Unreachable, - ); - }) -} - -/// Given a known `param_env` and a set of well formed types, set up an -/// `InferCtxt`, call the passed function (to e.g. set up region constraints -/// to be tested), then resolve region and return errors -fn test_region_obligations<'tcx>( - tcx: TyCtxt<'tcx>, - id: LocalDefId, - param_env: ty::ParamEnv<'tcx>, - wf_tys: &FxIndexSet>, - add_constraints: impl FnOnce(&InferCtxt<'tcx>), -) -> bool { - // Unfortunately, we have to use a new `InferCtxt` each call, because - // region constraints get added and solved there and we need to test each - // call individually. - let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); - - add_constraints(&infcx); - - let errors = infcx.resolve_regions(id, param_env, wf_tys.iter().copied()); - debug!(?errors, "errors"); - - // If we were able to prove that the type outlives the region without - // an error, it must be because of the implied or explicit bounds... - errors.is_empty() -} - /// TypeVisitor that looks for uses of GATs like /// `>::GAT` and adds the arguments `P0..Pm` into /// the two vectors, `regions` and `types` (depending on their kind). For each diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 811ed83e0bf48..d6a5db4c22f24 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -214,6 +214,17 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { "synthetic HIR should have its `generics_of` explicitly fed" ), + Node::ConstArg(..) => { + // These can show up in mGCA when representing "direct" const arguments. The + // DefCollector cannot know whether an anon const will be represented by an actual HIR + // Node::AnonConst, or whether it will be represented directly, so it must generate a + // DefId. If it ends up being direct, this DefId is then attached to the top-level + // ConstArg, which is what we are seeing here. + debug_assert!(tcx.features().min_generic_const_args()); + // Forward to the real parent. + Some(tcx.local_parent(def_id)) + } + _ => span_bug!(tcx.def_span(def_id), "generics_of: unexpected node kind {node:?}"), }; diff --git a/compiler/rustc_hir_analysis/src/delegation.rs b/compiler/rustc_hir_analysis/src/delegation.rs index 58474f0ee2e38..40adfeadc4411 100644 --- a/compiler/rustc_hir_analysis/src/delegation.rs +++ b/compiler/rustc_hir_analysis/src/delegation.rs @@ -4,7 +4,7 @@ use std::debug_assert_matches; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{DelegationSelfTyPropagationKind, PathSegment}; @@ -21,6 +21,7 @@ type RemapTable = FxHashMap; struct ParamIndexRemapper<'tcx> { tcx: TyCtxt<'tcx>, remap_table: RemapTable, + delegation_parent_consts: FxHashSet, } impl<'tcx> TypeFolder> for ParamIndexRemapper<'tcx> { @@ -337,7 +338,7 @@ fn create_generic_args<'tcx>( delegation_id: LocalDefId, mut parent_args: &[ty::GenericArg<'tcx>], mut child_args: &[ty::GenericArg<'tcx>], -) -> Vec> { +) -> (Vec>, &'tcx [ty::GenericArg<'tcx>]) { let delegation_generics = tcx.generics_of(delegation_id); let delegation_args = ty::GenericArgs::identity_for_item(tcx, delegation_id); @@ -389,7 +390,7 @@ fn create_generic_args<'tcx>( let zero_self = zero_self.as_ref().into_iter(); let after_lifetimes_self = after_lifetimes_self.as_ref().into_iter(); - zero_self + let args = zero_self .chain(delegation_parent_args) .chain(parent_args.iter().filter(|a| a.as_region().is_some())) .chain(child_args.iter().filter(|a| a.as_region().is_some())) @@ -398,7 +399,9 @@ fn create_generic_args<'tcx>( .chain(child_args.iter().filter(|a| a.as_region().is_none())) .chain(synth_args) .copied() - .collect::>() + .collect::>(); + + (args, delegation_parent_args) } pub(crate) fn inherit_predicates_for_delegation_item<'tcx>( @@ -434,6 +437,44 @@ pub(crate) fn inherit_predicates_for_delegation_item<'tcx>( continue; } + // If we have a constant in parent or child args that came from delegation + // parent: + // ```rust + // trait Trait { /* .. */} + // impl S { + // reuse Trait::, N>::foo; + // } + // ``` + // Then if we inherit const predicate from `Trait` then we end up with + // two `ConstArgHasType` for `N` constant: + // 1) ConstArgHasType(N/#0, bool) from `Trait` + // 2) ConstArgHasType(N/#0, usize) from delegation parent + // So in case the constant came from delegation parent we will not inherit + // ConstArgHasType from signature. + // The check is so complicated because we build generic args for signature + // and predicates inheritance, for the example above it will be + // `args = [S, N/#0, S, N/#0]`, where + // args[0] - Self type, args[1] - delegation parent const, args[2] - first + // arg of callee path, args[3] - second arg of callee path. + // When processing predicate ConstArgHasType(B/#2, bool) + // from delegation signature (`Trait::foo`), we need to map `B/#2` into some + // arg from `args`. The mapping which is built by `create_mapping` function is: + // `{0: 0, 2: 3, 1: 2}`, so as `B/#2` has index `2` it is mapped into third + // arg from `args` - `N/#0`. After we obtained mapped const param, we check if + // it came from delegation parent, and if so we do not process its `ConstArgHasType` + // predicate. + // (Issue #158675). + if let ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _)) = + pred.0.as_predicate().fold_with(&mut self.folder).kind().skip_binder() + { + let unnorm_const = EarlyBinder::bind(self.tcx, ct).instantiate(self.tcx, args); + if let ty::ConstKind::Param(param) = unnorm_const.skip_norm_wip().kind() + && self.folder.delegation_parent_consts.contains(¶m) + { + continue; + } + } + let new_pred = pred.0.fold_with(&mut self.folder); self.preds.push(( EarlyBinder::bind(self.tcx, new_pred) @@ -497,10 +538,21 @@ fn create_folder_and_args<'tcx>( parent_args: &'tcx [ty::GenericArg<'tcx>], child_args: &'tcx [ty::GenericArg<'tcx>], ) -> (ParamIndexRemapper<'tcx>, Vec>) { - let args = create_generic_args(tcx, sig_id, def_id, parent_args, child_args); + let (args, delegation_parent_args) = + create_generic_args(tcx, sig_id, def_id, parent_args, child_args); + let remap_table = create_mapping(tcx, sig_id, def_id); - (ParamIndexRemapper { tcx, remap_table }, args) + let delegation_parent_consts = delegation_parent_args + .iter() + .filter_map(|a| { + a.as_const().and_then(|c| { + if let ty::ConstKind::Param(param) = c.kind() { Some(param) } else { None } + }) + }) + .collect(); + + (ParamIndexRemapper { tcx, remap_table, delegation_parent_consts }, args) } fn check_constraints<'tcx>( diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index 1bfc495071284..a671b02b5fa66 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -2000,3 +2000,33 @@ impl Diagnostic<'_, G> for UncoveredTyParam<'_> { diag } } + +#[derive(Diagnostic)] +#[diag("field `{$name}` is already part of the view")] +pub(crate) struct ViewedFieldIsAlreadyPartOfTheView { + #[primary_span] + pub span: Span, + pub name: Symbol, + #[label("field `{$name}` is declared as viewed here")] + pub previous_field_span: Span, +} + +#[derive(Diagnostic)] +#[diag("only structs can be viewed")] +pub(crate) struct OnlyStructsCanBeViewedNonAdt<'tcx> { + #[primary_span] + #[label("type `{$ty}` cannot be viewed")] + pub span: Span, + pub ty: Ty<'tcx>, +} + +#[derive(Diagnostic)] +#[diag("only structs can be viewed")] +pub(crate) struct OnlyStructsCanBeViewedAdt<'tcx> { + #[primary_span] + #[label("`{$ty}` is {$article} {$kind}, it cannot be viewed")] + pub span: Span, + pub ty: Ty<'tcx>, + pub article: &'static str, + pub kind: &'static str, +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 2b0219f356c3c..179d7f6f9c56c 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -51,7 +51,7 @@ use rustc_trait_selection::traits::{self, FulfillmentError}; use tracing::{debug, instrument}; use crate::check::check_abi; -use crate::diagnostics::{BadReturnTypeNotation, NoFieldOnType}; +use crate::diagnostics::{self, BadReturnTypeNotation, NoFieldOnType}; use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint}; use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; use crate::middle::resolve_bound_vars as rbv; @@ -2370,7 +2370,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.check_param_uses_if_mcg(ct, tcx.hir_span(path_hir_id), false) } - /// Lower a [`hir::ConstArg`] to a (type-level) [`ty::Const`](Const). + /// Lower a [`hir::ConstArg`] to a (type-level) [`ty::Const`]. #[instrument(skip(self), level = "debug")] pub fn lower_const_arg(&self, const_arg: &hir::ConstArg<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> { let tcx = self.tcx(); @@ -3401,6 +3401,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { *variant, *field, ), + hir::TyKind::View(ty, fields) => { + self.lower_view(self.lower_ty(ty), fields, hir_ty.span) + } + hir::TyKind::Err(guar) => Ty::new_error(tcx, *guar), }; @@ -3812,4 +3816,73 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let adt_ty = Ty::new_adt(tcx, adt_def, args); ty::Const::new_value(tcx, valtree, adt_ty) } + + fn lower_view(&self, inner_ty: Ty<'tcx>, fields: &[Ident], ty_span: Span) -> Ty<'tcx> { + // Step 1: check that every field is unique, and keep a list of field that we know are + // unique. + let mut viewed_fields = Vec::::with_capacity(fields.len()); + + for f in fields { + let f = f.normalize_to_macros_2_0(); + // PERF: this is quadratic, but ~fine since the amount of fields is very low. + if let Some(previous_field_span) = + viewed_fields.iter().find_map(|f_| (*f_ == f).then_some(f_.span)) + { + self.dcx().emit_err(diagnostics::ViewedFieldIsAlreadyPartOfTheView { + name: f.name, + span: f.span, + previous_field_span, + }); + continue; + } + viewed_fields.push(f); + } + + // Step 2: check that the viewed type is a struct. + let variant = match inner_ty.kind() { + ty::Adt(def, _) if def.is_struct() => def.non_enum_variant(), + + ty::Adt(def, _) => { + let guar = self.dcx().emit_err(diagnostics::OnlyStructsCanBeViewedAdt { + ty: inner_ty, + span: ty_span, + article: def.article(), + kind: def.descr(), + }); + return Ty::new_error(self.tcx(), guar); + } + + _ => { + let guar = self.dcx().emit_err(diagnostics::OnlyStructsCanBeViewedNonAdt { + ty: inner_ty, + span: ty_span, + }); + return Ty::new_error(self.tcx(), guar); + } + }; + + // Step 3: check that every viewed field exists. + let mut viewed_indices = Vec::with_capacity(viewed_fields.len()); + let mut error = None; + for field in viewed_fields { + let Some((_, field)) = variant + .fields + .iter_enumerated() + .find(|(_, f)| f.ident(self.tcx()).normalize_to_macros_2_0() == field) + else { + let err = + self.dcx().emit_err(NoFieldOnType { span: field.span, field, ty: inner_ty }); + error = Some(err); + continue; + }; + + viewed_indices.push(field); + } + if let Some(guar) = error { + return Ty::new_error(self.tcx(), guar); + } + + // FIXME(scrabsha): actually lower view types. + inner_ty + } } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 136fe22eea60f..447ad2447b3b9 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -476,6 +476,17 @@ impl<'a> State<'a> { self.print_ident(*field); self.word(")"); } + hir::TyKind::View(ty, fields) => { + self.word("view_type!("); + self.print_type(ty); + self.word(".{"); + if !fields.is_empty() { + self.space(); + self.commasep(Breaks::Inconsistent, fields, |s, f| s.print_ident(*f)); + self.space(); + } + self.word("})"); + } } self.end(ib) } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 1b6f366b9d133..e7b25dec4db59 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -299,7 +299,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { formal_input_tys, provided_args, expected_input_tys, - c_variadic, tuple_arguments, fn_def_id, callee_generic_args, @@ -573,10 +572,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { provided_args: &'tcx [hir::Expr<'tcx>], // The expected input types from the context of the call site mut expected_input_tys: Option>>, - // Whether the function is variadic (e.g. from C) - c_variadic: bool, - // Whether all the arguments have been bundled in a tuple (ex: closures). - // Splatting is handled separately. + // Whether all the arguments have been bundled in a tuple (ex: closures), or one has been splatted tuple_arguments: TupleArgumentsFlag, // The DefId for the function being called, for better error messages fn_def_id: Option, @@ -592,6 +588,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { untupled_expected_input_tys: expected_input_tys, }; }; + let first_tupled_arg_index_usz = usize::from(first_tupled_arg_index); // The argument difference can range from -1 to u16::MAX - 1, so we count the number // of tupled arguments instead. @@ -604,28 +601,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tupled_args_count = (1 + provided_args.len()).checked_sub(formal_input_tys.len()); debug!( ?first_tupled_arg_index, ?is_self_splatted, - ?tupled_args_count, ?tuple_arguments, ?c_variadic, + ?tupled_args_count, ?tuple_arguments, provided_args_len = ?provided_args.len(), formal_input_tys_len = ?formal_input_tys.len() ); // If earlier code has modified the FnSig argument list without adjusting the splatted // argument, indexing into the formal input types will panic. - if first_tupled_arg_index >= formal_input_tys.len() { + if first_tupled_arg_index_usz >= formal_input_tys.len() { span_bug!( call_span, "splatted argument index is out of bounds: {first_tupled_arg_index:?} >= {}, \ is_self_splatted = {is_self_splatted:?}, \ tupled_args_count = {tupled_args_count:?}, {tuple_arguments:?}, \ - c_variadic = {c_variadic:?}, provided_args: {}", + provided_args: {}", formal_input_tys.len(), provided_args.len(), ); } + let formal_input_tupled_ty = formal_input_tys[first_tupled_arg_index_usz]; // Keep the type variable if the argument is splatted, so we can force it to be a tuple later. let tuple_type = if tuple_arguments.is_splatted() { - let callee_tuple_type = - self.resolve_vars_with_obligations(formal_input_tys[first_tupled_arg_index]); + let callee_tuple_type = self.resolve_vars_with_obligations(formal_input_tupled_ty); if callee_tuple_type.is_ty_var() && let Some(tupled_args_count) = tupled_args_count { @@ -674,7 +671,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { callee_tuple_type } } else { - self.structurally_resolve_type(call_span, formal_input_tys[first_tupled_arg_index]) + self.structurally_resolve_type(call_span, formal_input_tupled_ty) }; // We expected a tuple and got a tuple (or made one ourselves). @@ -687,7 +684,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err_code = Some(E0057); } if let Some(ref mut expected_input_tys) = expected_input_tys - && let Some(ty) = expected_input_tys.get(first_tupled_arg_index) + && let Some(ty) = expected_input_tys.get(first_tupled_arg_index_usz) && let ty::Tuple(detup_expected_arg_tys) = ty.kind() { let substitute_tys = if Some(detup_expected_arg_tys.len()) == tupled_args_count { @@ -697,26 +694,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { detup_formal_arg_tys.iter() }; - expected_input_tys - .splice(first_tupled_arg_index..=first_tupled_arg_index, substitute_tys); + expected_input_tys.splice( + first_tupled_arg_index_usz..=first_tupled_arg_index_usz, + substitute_tys, + ); } else { expected_input_tys = None; } - // If splatting, record this call in a side-table, so MIR lowering can tuple the caller's arguments - if tuple_arguments.is_splatted() { - // FIXME(const_trait_impl): does not enforce constness yet - self.write_splatted_call( - call_expr.hir_id, - call_span, - fn_def_id, - callee_generic_args, - first_tupled_arg_index.try_into().unwrap(), - tupled_args_count.unwrap().try_into().unwrap(), - ); - } - formal_input_tys.splice( - first_tupled_arg_index..=first_tupled_arg_index, + first_tupled_arg_index_usz..=first_tupled_arg_index_usz, detup_formal_arg_tys.iter(), ); if let Some(ref expected_input_tys) = expected_input_tys { @@ -724,7 +710,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { formal_input_tys.len(), expected_input_tys.len(), "incorrectly constructed input type tuples, argument counts must match: \ - tuple_arguments: {tuple_arguments:?}", + tuple_arguments: {tuple_arguments:?}, \ + first_tupled_arg_index: {first_tupled_arg_index}", ) } } @@ -751,7 +738,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let spans = if let Some(def_id) = fn_def_id && let Some(hir_node) = self.tcx.hir_get_if_local(def_id) && let Some(fn_decl) = hir_node.fn_decl() - && let Some(arg_ty) = fn_decl.inputs.get(first_tupled_arg_index) + && let Some(arg_ty) = fn_decl.inputs.get(first_tupled_arg_index_usz) { let arg_def_span = arg_ty.span; vec![call_span, arg_def_span] @@ -768,7 +755,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tuple_type.kind(), self.structurally_resolve_type( call_span, - formal_input_tys[first_tupled_arg_index] + formal_input_tys[first_tupled_arg_index_usz] ) .kind(), ) @@ -803,6 +790,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { untupled_expected_input_tys: None, } } else { + // If splatting, record this call in a side-table, so MIR lowering can tuple the caller's arguments + if tuple_arguments.is_splatted() { + // FIXME(const_trait_impl): does not enforce constness yet + self.write_splatted_call( + call_expr.hir_id, + call_span, + fn_def_id, + callee_generic_args, + first_tupled_arg_index, + tupled_args_count.unwrap().try_into().unwrap(), + ); + } + TupledArgCheckOutcome { new_err_code: err_code, untupled_formal_input_tys: formal_input_tys, diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 6b5f084fc3dbe..85fdb9b6fe450 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -666,9 +666,9 @@ impl TupleArgumentsFlag { /// Returns the tupled argument index, and whether the `self` argument is splatted. /// Returns `None` if the arguments are not tupled, or if the `self` argument is splatted. - fn tupled_arg_index(self) -> (Option, bool /* is_self_splatted */) { + fn tupled_arg_index(self) -> (Option, bool /* is_self_splatted */) { match self { - Self::TupleSplattedArg(index) => (Some(usize::from(index)), false), + Self::TupleSplattedArg(index) => (Some(u16::from(index)), false), Self::TupleAllCallArgs => (Some(0), false), Self::TupleSplattedSelfArg => (None, true), Self::DontTupleArguments => (None, false), diff --git a/compiler/rustc_index_macros/src/newtype.rs b/compiler/rustc_index_macros/src/newtype.rs index 14fd147f96780..076dc4c6f81e5 100644 --- a/compiler/rustc_index_macros/src/newtype.rs +++ b/compiler/rustc_index_macros/src/newtype.rs @@ -152,13 +152,15 @@ impl Parse for Newtype { } } impl ::std::cmp::Ord for #name { + #[inline] fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.as_u32().cmp(&other.as_u32()) } } impl ::std::cmp::PartialOrd for #name { + #[inline] fn partial_cmp(&self, other: &Self) -> Option { - self.as_u32().partial_cmp(&other.as_u32()) + Some(self.cmp(other)) } } } diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index 92b47295ade88..76db3830d3962 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -16,7 +16,6 @@ use crate::infer::lexical_region_resolve; use crate::infer::region_constraints::ConstraintKind; pub mod env; -pub mod for_liveness; pub mod obligations; pub mod test_type_match; pub(crate) mod verify; diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index ee6e13250e709..12c577d3a50e2 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -5,10 +5,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::{ - self, InferConst, Term, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, - TypeVisitor, -}; +use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::Span; use tracing::{debug, instrument, warn}; @@ -362,45 +359,6 @@ impl<'tcx> InferCtxt<'tcx> { } } -/// Finds the max universe present -struct MaxUniverse { - max_universe: ty::UniverseIndex, -} - -impl MaxUniverse { - fn new() -> Self { - MaxUniverse { max_universe: ty::UniverseIndex::ROOT } - } - - fn max_universe(self) -> ty::UniverseIndex { - self.max_universe - } -} - -impl<'tcx> TypeVisitor> for MaxUniverse { - fn visit_ty(&mut self, t: Ty<'tcx>) { - if let ty::Placeholder(placeholder) = t.kind() { - self.max_universe = self.max_universe.max(placeholder.universe); - } - - t.super_visit_with(self) - } - - fn visit_const(&mut self, c: ty::Const<'tcx>) { - if let ty::ConstKind::Placeholder(placeholder) = c.kind() { - self.max_universe = self.max_universe.max(placeholder.universe); - } - - c.super_visit_with(self) - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) { - if let ty::RePlaceholder(placeholder) = r.kind() { - self.max_universe = self.max_universe.max(placeholder.universe); - } - } -} - /// The "generalizer" is used when handling inference variables. /// /// The basic strategy for handling a constraint like `?A <: B` is to @@ -511,10 +469,9 @@ impl<'tcx> Generalizer<'_, 'tcx> { if is_nested_alias { return Err(e); } else { - let mut visitor = MaxUniverse::new(); - alias.visit_with(&mut visitor); + let alias_max_universe = ty::max_universe_of_placeholders(self.infcx, alias); let infer_replacement_is_complete = - self.for_universe.can_name(visitor.max_universe()) + self.for_universe.can_name(alias_max_universe) && !alias.has_escaping_bound_vars(); if !infer_replacement_is_complete { warn!("may incompletely handle alias type: {alias:?}"); diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index d38b1cf47bd6f..693737bd89bdd 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -44,7 +44,6 @@ pub mod hardwired { DEPRECATED_WHERE_CLAUSE_LOCATION, DUPLICATE_FEATURES, DUPLICATE_MACRO_ATTRIBUTES, - ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, ELIDED_LIFETIMES_IN_PATHS, EXPLICIT_BUILTIN_CFGS_IN_FLAGS, EXPORTED_PRIVATE_DEPENDENCIES, @@ -4762,48 +4761,6 @@ declare_lint! { "impl trait in impl method signature does not match trait method signature", } -declare_lint! { - /// The `elided_lifetimes_in_associated_constant` lint detects elided lifetimes - /// in associated constants when there are other lifetimes in scope. This was - /// accidentally supported, and this lint was later relaxed to allow eliding - /// lifetimes to `'static` when there are no lifetimes in scope. - /// - /// ### Example - /// - /// ```rust,compile_fail - /// #![deny(elided_lifetimes_in_associated_constant)] - /// - /// struct Foo<'a>(&'a ()); - /// - /// impl<'a> Foo<'a> { - /// const STR: &str = "hello, world"; - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// Previous version of Rust - /// - /// Implicit static-in-const behavior was decided [against] for associated - /// constants because of ambiguity. This, however, regressed and the compiler - /// erroneously treats elided lifetimes in associated constants as lifetime - /// parameters on the impl. - /// - /// This is a [future-incompatible] lint to transition this to a - /// hard error in the future. - /// - /// [against]: https://github.com/rust-lang/rust/issues/38831 - /// [future-incompatible]: ../index.md#future-incompatible-lints - pub ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, - Deny, - "elided lifetimes cannot be used in associated constants in impls", - @future_incompatible = FutureIncompatibleInfo { - reason: fcw!(FutureReleaseError #115010), - }; -} - declare_lint! { /// The `private_macro_use` lint detects private macros that are imported /// with `#[macro_use]`. diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index fe5b8edce4a1d..e4807f10805c9 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -49,6 +49,9 @@ #include "llvm/Transforms/Instrumentation/RealtimeSanitizer.h" #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" #include "llvm/Transforms/Scalar/AnnotationRemarks.h" +#if LLVM_VERSION_GE(23, 0) +#include "llvm/Transforms/Utils/AssignGUID.h" +#endif #include "llvm/Transforms/Utils/CanonicalizeAliases.h" #include "llvm/Transforms/Utils/FunctionImportUtils.h" #include "llvm/Transforms/Utils/NameAnonGlobals.h" @@ -934,6 +937,9 @@ extern "C" LLVMRustResult LLVMRustOptimize( if (NeedThinLTOBufferPasses) { MPM.addPass(CanonicalizeAliasesPass()); MPM.addPass(NameAnonGlobalPass()); +#if LLVM_VERSION_GE(23, 0) + MPM.addPass(AssignGUIDPass()); +#endif } // For `-Copt-level=0`, and the pre-link fat/thin LTO stages. if (ThinLTOBufferRef && *ThinLTOBufferRef == nullptr) { @@ -1475,6 +1481,9 @@ extern "C" LLVMRustBuffer *LLVMRustModuleSerialize(LLVMModuleRef M, PB.registerLoopAnalyses(LAM); PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); ModulePassManager MPM; +#if LLVM_VERSION_GE(23, 0) + MPM.addPass(AssignGUIDPass()); +#endif MPM.addPass(ThinLTOBitcodeWriterPass(OS, nullptr)); MPM.run(*unwrap(M), MAM); } else { diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index f970be089f2ee..83a0977296b30 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -213,7 +213,7 @@ //! metadata::locator or metadata::creader for all the juicy details! use std::borrow::Cow; -use std::io::{self, Result as IoResult, Write}; +use std::io::{self, Error as IoError, ErrorKind as IoErrorKind, Result as IoResult, Write}; use std::ops::Deref; use std::path::{Path, PathBuf}; use std::{cmp, fmt}; @@ -962,7 +962,7 @@ pub fn list_file_metadata( let flavor = get_flavor_from_path(path); match get_metadata_section(target, flavor, path, metadata_loader, cfg_version, None) { Ok(metadata) => metadata.list_crate_metadata(out, ls_kinds), - Err(msg) => write!(out, "{msg}\n"), + Err(msg) => Err(IoError::new(IoErrorKind::Other, msg.to_string())), } } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index dff8a4078da16..798709d69d76e 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -417,6 +417,7 @@ provide! { tcx, def_id, other, cdata, } anon_const_kind => { table } const_of_item => { table } + args_known_to_outlive_alias_params => { table } } pub(in crate::rmeta) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 16a5a6c8877a5..b2abd34d6d6e9 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1108,7 +1108,7 @@ fn should_encode_mir( // instance_mir uses mir_for_ctfe rather than optimized_mir for constructors DefKind::Ctor(_, _) => (true, false), // Constants - DefKind::AnonConst { .. } + DefKind::AnonConst | DefKind::InlineConst | DefKind::AssocConst { .. } | DefKind::Const { .. } => (true, false), @@ -1438,27 +1438,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // for trivial const arguments which are directly lowered to // `ConstArgKind::Path`. We never actually access this `DefId` // anywhere so we don't need to encode it for other crates. + // FIXME(mgca): This probably isn't true, they probably are accessed, but, test case? if def_kind == DefKind::AnonConst - && match tcx.hir_node_by_def_id(local_id) { - hir::Node::ConstArg(hir::ConstArg { kind, .. }) => match kind { - // Skip encoding defs for these as they should not have had a `DefId` created - hir::ConstArgKind::Error(..) - | hir::ConstArgKind::Struct(..) - | hir::ConstArgKind::Array(..) - | hir::ConstArgKind::TupleCall(..) - | hir::ConstArgKind::Tup(..) - | hir::ConstArgKind::Path(..) - | hir::ConstArgKind::Literal { .. } - | hir::ConstArgKind::Infer(..) => true, - hir::ConstArgKind::Anon(..) => false, - }, - _ => false, - } + && matches!(tcx.hir_node_by_def_id(local_id), hir::Node::ConstArg(_)) { - // MGCA doesn't have unnecessary DefIds - if !tcx.features().min_generic_const_args() { - continue; - } + continue; } if def_kind == DefKind::Field @@ -1613,6 +1597,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.tables .type_alias_is_lazy .set(def_id.index, self.tcx.type_alias_is_lazy(def_id)); + if self.tcx.type_alias_is_lazy(def_id) { + record!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); + } } if let DefKind::OpaqueTy = def_kind { self.encode_explicit_item_bounds(def_id); @@ -1623,6 +1610,19 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record_defaulted_array!(self.tables.explicit_implied_const_bounds[def_id] <- tcx.explicit_implied_const_bounds(def_id).skip_binder()); } + record!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); + } + if let DefKind::AssocTy = def_kind { + let assoc_item = tcx.associated_item(def_id); + match assoc_item.container { + ty::AssocContainer::Trait => { + record!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); + } + ty::AssocContainer::InherentImpl => { + record!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); + } + ty::AssocContainer::TraitImpl(_) => {} + } } if let DefKind::AnonConst | DefKind::InlineConst = def_kind { record!(self.tables.anon_const_kind[def_id] <- self.tcx.anon_const_kind(def_id)); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 90721f0f1fc11..cccd613aa8ac1 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -480,6 +480,8 @@ define_tables! { anon_const_kind: Table>, const_of_item: Table>>>, associated_types_for_impl_traits_in_trait_or_impl: Table>>>, + live_args_for_alias_from_outlives_bounds: Table>>>>>, + args_known_to_outlive_alias_params: Table, Vec>)>>>>, } #[derive(TyEncodable, TyDecodable)] diff --git a/compiler/rustc_metadata/src/rmeta/parameterized.rs b/compiler/rustc_metadata/src/rmeta/parameterized.rs index 40f1c85417b04..feb6859eabd5b 100644 --- a/compiler/rustc_metadata/src/rmeta/parameterized.rs +++ b/compiler/rustc_metadata/src/rmeta/parameterized.rs @@ -3,7 +3,7 @@ use std::hash::Hash; use rustc_data_structures::unord::UnordMap; use rustc_hir::def_id::DefIndex; use rustc_index::{Idx, IndexVec}; -use rustc_middle::ty::{Binder, EarlyBinder}; +use rustc_middle::ty::{Binder, EarlyBinder, GenericArg, Region}; use rustc_span::Symbol; use crate::rmeta::{LazyArray, LazyValue}; @@ -48,6 +48,14 @@ impl ParameterizedOverTcx for LazyArray { type Value<'tcx> = LazyArray>; } +impl ParameterizedOverTcx for Region<'static> { + type Value<'tcx> = Region<'tcx>; +} + +impl ParameterizedOverTcx for GenericArg<'static> { + type Value<'tcx> = GenericArg<'tcx>; +} + macro_rules! trivially_parameterized_over_tcx { ($($ty:ty),+ $(,)?) => { $( @@ -61,6 +69,7 @@ macro_rules! trivially_parameterized_over_tcx { trivially_parameterized_over_tcx! { bool, + u32, u64, usize, std::string::String, diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index ae2695987ff13..78bb57745e41f 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -169,3 +169,32 @@ pub(crate) struct IncrementCompilation { pub run_cmd: String, pub dep_node: String, } + +#[derive(Diagnostic)] +#[diag("multiple implementations of `#[{$name}]`")] +pub struct DuplicateEiiImpls { + pub name: Symbol, + + #[primary_span] + #[label("first implemented here in crate `{$first_crate}`")] + pub first_span: Span, + pub first_crate: Symbol, + + #[label("also implemented here in crate `{$second_crate}`")] + pub second_span: Span, + pub second_crate: Symbol, + + #[note("in addition to these two, { $num_additional_crates -> + [one] another implementation was found in crate {$additional_crate_names} + *[other] more implementations were also found in the following crates: {$additional_crate_names} + }")] + pub additional_crates: Option<()>, + + pub num_additional_crates: usize, + pub additional_crate_names: String, + + #[help( + "an \"externally implementable item\" can only have a single implementation in the final artifact. When multiple implementations are found, also in different crates, they conflict" + )] + pub help: (), +} diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index f0557c3d3381a..5d9530b86af15 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -2122,6 +2122,36 @@ rustc_queries! { desc { "listing captured lifetimes for opaque `{}`", tcx.def_path_str(def_id) } } + /// For an opaque type or trait associated type, return the list of potentially live + /// (identity) generic args from the set of outlives bounds on that alias. Callers should + /// instantiate the returned args with the concrete args of the alias. + /// ```ignore (illustrative) + /// // Edition 2024: all args are captured + /// fn foo<'a, 'b, T: 'static>(&'a &'b T) -> impl Sized + 'a {} + /// fn bar<'a, 'b, T: 'static>(&'a &'b T) -> impl Sized + 'static {} + /// fn baz<'a, 'b, T: 'static>(&'a &'b T) -> impl Sized {} + /// ``` + /// + /// In the above: + /// - `foo` outlives `'a`, but we know that `'b: 'a` holds, so `'b` is *also* potentially live + /// (and so is `T`, since `T: 'static` implies `T: 'a`) + /// - `bar` outlives `'static`, so we know that no args are potentially live and we can return an empty set + /// - `baz` has no outlives bound, so return `None` and let the caller decide what to do + query live_args_for_alias_from_outlives_bounds(kind: ty::AliasTyKind<'tcx>) -> &'tcx Option>>> { + arena_cache + desc { "identifying live args for alias `{:?}`", kind } + } + + /// For each region param of an alias, the identity args that are known to + /// outlive it given only the alias's declared where-clauses. Used for liveness: + /// these are the only args whose regions the underlying type of the alias + /// could capture while satisfying an outlives bound on that param. + query args_known_to_outlive_alias_params(def_id: DefId) -> &'tcx ty::EarlyBinder<'tcx, Vec<(ty::Region<'tcx>, Vec>)>> { + arena_cache + desc { "computing the args known to outlive each region param of alias `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } + /// Computes the visibility of the provided `def_id`. /// /// If the item from the `def_id` doesn't have a visibility, it will panic. For example diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 5eedeb4f9ed2e..772caa0b9f505 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -271,6 +271,18 @@ impl<'tcx> QueryKey for ty::Clauses<'tcx> { } } +impl<'tcx> QueryKey for ty::AliasTyKind<'tcx> { + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { + let def_id = match self { + ty::AliasTyKind::Projection { def_id } + | ty::AliasTyKind::Inherent { def_id } + | ty::AliasTyKind::Opaque { def_id } + | ty::AliasTyKind::Free { def_id } => def_id, + }; + tcx.def_span(*def_id) + } +} + impl<'tcx, T: QueryKey> QueryKey for ty::PseudoCanonicalInput<'tcx, T> { fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.value.default_span(tcx) diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index d380b7f522636..a0daa04844e03 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -335,6 +335,17 @@ impl From for DataTypeKind { } } +impl AdtKind { + pub fn article(self) -> &'static str { + match self { + AdtKind::Struct => "a", + // https://english.stackexchange.com/a/266324 + AdtKind::Union => "a", + AdtKind::Enum => "an", + } + } +} + impl AdtDefData { /// Creates a new `AdtDefData`. pub(super) fn new( @@ -455,6 +466,14 @@ impl<'tcx> AdtDef<'tcx> { } } + /// Returns a description of this abstract data type with the article. + pub fn article(self) -> &'static str { + match self.adt_kind() { + AdtKind::Struct | AdtKind::Union => "a", + AdtKind::Enum => "an", + } + } + /// Returns a description of a variant of this abstract data type. #[inline] pub fn variant_descr(self) -> &'static str { diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index c286c0819aea2..650dcf61c66ac 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -1225,26 +1225,50 @@ impl<'tcx> ThirBuildCx<'tcx> { self.typeck_results.splatted_def(expr.hir_id).unwrap_or_else(|| { span_bug!(expr.span, "no splatted def for function or method callee") }); - let def_id = def_id.unwrap_or_else(|| { - span_bug!(expr.span, "no splatted def for function or method callee") - }); - let def_kind = self.tcx.def_kind(def_id); - let user_ty = self.user_args_applied_to_res(expr.hir_id, Res::Def(def_kind, def_id)); - debug!( - "splatted_callee: user_ty={:?} def_kind={:?} def_id={:?} arg_index={:?} arg_count={:?}", - user_ty, def_kind, def_id, arg_index, arg_count - ); - ( + let expr = if let Some(def_id) = def_id { + // We're calling a function via a FnDef, and its possibly generic type + let def_kind = self.tcx.def_kind(def_id); + let user_ty = self.user_args_applied_to_res(expr.hir_id, Res::Def(def_kind, def_id)); + debug!( + "splatted_callee FnDef: user_ty={:?} def_kind={:?} def_id={:?} arg_index={:?} arg_count={:?}", + user_ty, def_kind, def_id, arg_index, arg_count, + ); + Expr { temp_scope_id: expr.hir_id.local_id, + // Create a new FnDef type, representing the splatted function arguments with + // user-supplied generic types applied ty: Ty::new_fn_def(self.tcx, def_id, self.typeck_results.node_args(expr.hir_id)), span, kind: ExprKind::ZstLiteral { user_ty }, - }, - arg_index, - arg_count, - ) + } + } else { + // We're calling a function via a FnPtr and its type + // FIXME(splat): populate the side-tables for FnPtrs, using liberated_fn_sigs if needed + let fn_ty = self.typeck_results.expr_ty_adjusted(expr); + let user_ty = + self.typeck_results.user_provided_types().get(expr.hir_id).copied().map(Box::new); + debug!( + "splatted_callee FnPtr: user_ty={:?} fn_ty={:?} arg_index={:?} arg_count={:?}", + user_ty, fn_ty, arg_index, arg_count, + ); + + if !fn_ty.is_fn() { + span_bug!(expr.span, "splatted FnPtr side-tables are not yet implemented") + } + + Expr { + temp_scope_id: expr.hir_id.local_id, + // Create a new FnPtr FnSig type, representing the splatted function arguments with + // user-supplied generic types applied + ty: Ty::new_fn_ptr(self.tcx, fn_ty.fn_sig(self.tcx)), + span, + kind: ExprKind::ZstLiteral { user_ty }, + } + }; + + (expr, arg_index, arg_count) } /// The callee has a splatted tuple argument. diff --git a/compiler/rustc_next_trait_solver/src/normalize.rs b/compiler/rustc_next_trait_solver/src/normalize.rs index 761b141202d98..959e18071bb57 100644 --- a/compiler/rustc_next_trait_solver/src/normalize.rs +++ b/compiler/rustc_next_trait_solver/src/normalize.rs @@ -3,9 +3,8 @@ use std::fmt::Debug; use rustc_type_ir::data_structures::ensure_sufficient_stack; use rustc_type_ir::inherent::*; use rustc_type_ir::{ - self as ty, AliasTerm, Binder, FallibleTypeFolder, InferConst, InferCtxtLike, InferTy, - Interner, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, - TypeVisitor, UniverseIndex, + self as ty, AliasTerm, Binder, FallibleTypeFolder, InferCtxtLike, Interner, TypeFoldable, + TypeSuperFoldable, TypeVisitableExt, UniverseIndex, }; use tracing::instrument; @@ -37,72 +36,6 @@ pub enum NormalizationWasAmbiguous { No, } -/// Finds the max universe present in infer vars. -struct MaxUniverse<'a, Infcx, I> -where - Infcx: InferCtxtLike, - I: Interner, -{ - infcx: &'a Infcx, - max_universe: ty::UniverseIndex, -} - -impl<'a, Infcx, I> MaxUniverse<'a, Infcx, I> -where - Infcx: InferCtxtLike, - I: Interner, -{ - fn new(infcx: &'a Infcx) -> Self { - MaxUniverse { infcx, max_universe: ty::UniverseIndex::ROOT } - } - - fn max_universe(self) -> ty::UniverseIndex { - self.max_universe - } -} - -impl<'a, Infcx, I> TypeVisitor for MaxUniverse<'a, Infcx, I> -where - Infcx: InferCtxtLike, - I: Interner, -{ - type Result = (); - - fn visit_ty(&mut self, t: I::Ty) { - if !t.has_infer() { - return; - } - - if let ty::Infer(InferTy::TyVar(vid)) = t.kind() { - // We shallow resolved the infer var before. - // So it should be a unresolved infer var with an universe. - self.max_universe = self.max_universe.max(self.infcx.universe_of_ty(vid).unwrap()); - } - - t.super_visit_with(self) - } - - fn visit_const(&mut self, c: I::Const) { - if !c.has_infer() { - return; - } - - if let ty::ConstKind::Infer(InferConst::Var(vid)) = c.kind() { - // We shallow resolved the infer var before. - // So it should be a unresolved infer var with an universe. - self.max_universe = self.max_universe.max(self.infcx.universe_of_ct(vid).unwrap()); - } - - c.super_visit_with(self) - } - - fn visit_region(&mut self, r: I::Region) { - if let ty::ReVar(vid) = r.kind() { - self.max_universe = self.max_universe.max(self.infcx.universe_of_lt(vid).unwrap()); - } - } -} - impl<'a, Infcx, I, F, E> NormalizationFolder<'a, Infcx, I, F> where Infcx: InferCtxtLike, @@ -130,9 +63,7 @@ where if normalization_was_ambiguous == NormalizationWasAmbiguous::Yes && has_escaping == HasEscapingBoundVars::Yes { - let mut visitor = MaxUniverse::new(self.infcx); - normalized.visit_with(&mut visitor); - let max_universe = visitor.max_universe(); + let max_universe = ty::max_universe_of_infer_vars(self.infcx, normalized); if max_universe.can_name(self.universes.first().unwrap().unwrap()) { return Ok(None); } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs index aa92fc163f0ed..a232ab9192444 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs @@ -8,11 +8,11 @@ use rustc_type_ir::outlives::{Component, push_outlives_components}; #[cfg(not(feature = "nightly"))] use rustc_type_ir::region_constraint::TransitiveRelationBuilder; use rustc_type_ir::region_constraint::{ - Assumptions, RegionConstraint, eagerly_handle_placeholders_in_universe, max_universe, + Assumptions, RegionConstraint, eagerly_handle_placeholders_in_universe, }; use rustc_type_ir::{ AliasTy, Binder, ClauseKind, InferCtxtLike, Interner, OutlivesPredicate, TypeVisitable, - TypeVisitableExt, TypeVisitor, UniverseIndex, + TypeVisitableExt, TypeVisitor, UniverseIndex, max_universe, }; use tracing::{debug, instrument}; diff --git a/compiler/rustc_parse/src/parser/asm.rs b/compiler/rustc_parse/src/parser/asm.rs index 3fab234adaad4..11460e775d36d 100644 --- a/compiler/rustc_parse/src/parser/asm.rs +++ b/compiler/rustc_parse/src/parser/asm.rs @@ -1,4 +1,4 @@ -use rustc_ast::{self as ast, AsmMacro, MgcaDisambiguation}; +use rustc_ast::{self as ast, AsmMacro}; use rustc_span::{Span, Symbol, kw}; use super::{ExpKeywordPair, ForceCollect, IdentIsRaw, Trailing, UsePreAttrPos}; @@ -149,7 +149,7 @@ fn parse_asm_operand<'a>( let block = p.parse_block()?; ast::InlineAsmOperand::Label { block } } else if p.eat_keyword(exp!(Const)) { - let anon_const = p.parse_expr_anon_const(|_, _| MgcaDisambiguation::AnonConst)?; + let anon_const = p.parse_expr_anon_const()?; ast::InlineAsmOperand::Const { anon_const } } else if p.eat_keyword(exp!(Sym)) { let expr = p.parse_expr()?; diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 28e2841b9f1fe..a91ee634f3d47 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -7,7 +7,7 @@ use rustc_ast::util::parser::AssocOp; use rustc_ast::{ self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block, BlockCheckMode, Expr, ExprKind, GenericArg, GenericArgs, Generics, Item, ItemKind, - MgcaDisambiguation, Param, Pat, PatKind, Path, PathSegment, QSelf, Recovered, Ty, TyKind, + Param, Pat, PatKind, Path, PathSegment, QSelf, Recovered, Ty, TyKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; @@ -2585,11 +2585,7 @@ impl<'a> Parser<'a> { self.dcx().emit_err(UnexpectedConstParamDeclaration { span: param.span(), sugg }); let value = self.mk_expr_err(param.span(), guar); - Some(GenericArg::Const(AnonConst { - id: ast::DUMMY_NODE_ID, - value, - mgca_disambiguation: MgcaDisambiguation::Direct, - })) + Some(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })) } pub(super) fn recover_const_param_declaration( @@ -2673,11 +2669,7 @@ impl<'a> Parser<'a> { ); let guar = err.emit(); let value = self.mk_expr_err(start.to(expr.span), guar); - return Ok(GenericArg::Const(AnonConst { - id: ast::DUMMY_NODE_ID, - value, - mgca_disambiguation: MgcaDisambiguation::Direct, - })); + return Ok(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })); } else if snapshot.token == token::Colon && expr.span.lo() == snapshot.token.span.hi() && matches!(expr.kind, ExprKind::Path(..)) @@ -2746,11 +2738,7 @@ impl<'a> Parser<'a> { ); let guar = err.emit(); let value = self.mk_expr_err(span, guar); - GenericArg::Const(AnonConst { - id: ast::DUMMY_NODE_ID, - value, - mgca_disambiguation: MgcaDisambiguation::Direct, - }) + GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }) } /// Some special error handling for the "top-level" patterns in a match arm, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index df1877b82cb93..e151899a57228 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -15,8 +15,8 @@ use rustc_ast::visit::{Visitor, walk_expr}; use rustc_ast::{ self as ast, AnonConst, Arm, AssignOp, AssignOpKind, AttrStyle, AttrVec, BinOp, BinOpKind, BlockCheckMode, CaptureBy, ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl, - FnRetTy, Guard, Label, MacCall, MetaItemLit, MgcaDisambiguation, Movability, Param, - RangeLimits, StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind, YieldKind, + FnRetTy, Guard, Label, MacCall, MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, + TyKind, UnOp, UnsafeBinderCastKind, YieldKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -83,15 +83,8 @@ impl<'a> Parser<'a> { ) } - pub fn parse_expr_anon_const( - &mut self, - mgca_disambiguation: impl FnOnce(&Self, &Expr) -> MgcaDisambiguation, - ) -> PResult<'a, AnonConst> { - self.parse_expr().map(|value| AnonConst { - id: DUMMY_NODE_ID, - mgca_disambiguation: mgca_disambiguation(self, &value), - value, - }) + pub fn parse_expr_anon_const(&mut self) -> PResult<'a, AnonConst> { + self.parse_expr().map(|value| AnonConst { id: DUMMY_NODE_ID, value }) } fn parse_expr_catch_underscore( @@ -1672,7 +1665,7 @@ impl<'a> Parser<'a> { let first_expr = self.parse_expr()?; if self.eat(exp!(Semi)) { // Repeating array syntax: `[ 0; 512 ]` - let count = self.parse_expr_anon_const(|_, _| MgcaDisambiguation::Direct)?; + let count = self.parse_expr_anon_const()?; self.expect(close)?; ExprKind::Repeat(first_expr, count) } else if self.eat(exp!(Comma)) { @@ -4508,6 +4501,7 @@ impl MutVisitor for CondChecker<'_> { | ExprKind::IncludedBytes(_) | ExprKind::FormatArgs(_) | ExprKind::Err(_) + | ExprKind::DirectConstArg(_) | ExprKind::Dummy => { // These would forbid any let expressions they contain already. } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 201601d91adfa..815f60c55538e 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1696,9 +1696,9 @@ impl<'a> Parser<'a> { if self.may_recover() { self.parse_where_clause()? } else { WhereClause::default() }; let rhs = match (self.eat(exp!(Eq)), const_arg) { - (true, true) => ConstItemRhsKind::TypeConst { - rhs: Some(self.parse_expr_anon_const(|_, _| MgcaDisambiguation::Direct)?), - }, + (true, true) => { + ConstItemRhsKind::TypeConst { rhs: Some(self.parse_expr_anon_const()?) } + } (true, false) => ConstItemRhsKind::Body { rhs: Some(self.parse_expr()?) }, (false, true) => ConstItemRhsKind::TypeConst { rhs: None }, (false, false) => ConstItemRhsKind::Body { rhs: None }, @@ -1918,11 +1918,8 @@ impl<'a> Parser<'a> { VariantData::Unit(DUMMY_NODE_ID) }; - let disr_expr = if this.eat(exp!(Eq)) { - Some(this.parse_expr_anon_const(|_, _| MgcaDisambiguation::AnonConst)?) - } else { - None - }; + let disr_expr = + if this.eat(exp!(Eq)) { Some(this.parse_expr_anon_const()?) } else { None }; let span = vlo.to(this.prev_token.span); if ident.name == kw::Underscore { @@ -2143,7 +2140,7 @@ impl<'a> Parser<'a> { if p.token == token::Eq { let mut snapshot = p.create_snapshot_for_diagnostic(); snapshot.bump(); - match snapshot.parse_expr_anon_const(|_, _| MgcaDisambiguation::AnonConst) { + match snapshot.parse_expr_anon_const() { Ok(const_expr) => { let sp = ty.span.shrink_to_hi().to(const_expr.value.span); p.psess.gated_spans.gate(sym::default_field_values, sp); @@ -2372,7 +2369,7 @@ impl<'a> Parser<'a> { } let default = if self.token == token::Eq { self.bump(); - let const_expr = self.parse_expr_anon_const(|_, _| MgcaDisambiguation::AnonConst)?; + let const_expr = self.parse_expr_anon_const()?; let sp = ty.span.shrink_to_hi().to(const_expr.value.span); self.psess.gated_spans.gate(sym::default_field_values, sp); Some(const_expr) diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 04a9b1ff212a7..8b57968413d3d 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -36,8 +36,8 @@ use rustc_ast::util::classify; use rustc_ast::{ self as ast, AnonConst, AttrArgs, AttrId, BinOpKind, ByRef, Const, CoroutineKind, DUMMY_NODE_ID, DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, ImplRestriction, - MgcaDisambiguation, MutRestriction, Mutability, Recovered, RestrictionKind, Safety, StrLit, - Visibility, VisibilityKind, + MutRestriction, Mutability, Recovered, RestrictionKind, Safety, StrLit, Visibility, + VisibilityKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; @@ -1298,7 +1298,6 @@ impl<'a> Parser<'a> { let anon_const = AnonConst { id: DUMMY_NODE_ID, value: self.mk_expr(blk.span, ExprKind::Block(blk, None)), - mgca_disambiguation: MgcaDisambiguation::AnonConst, }; let blk_span = anon_const.value.span; let kind = if pat { diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index ea86c08915b31..d3839579d1311 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -4,8 +4,8 @@ use ast::token::IdentIsRaw; use rustc_ast::token::{self, MetaVarKind, Token, TokenKind}; use rustc_ast::{ self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocItemConstraint, - AssocItemConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, MgcaDisambiguation, - ParenthesizedArgs, Path, PathSegment, QSelf, + AssocItemConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs, + Path, PathSegment, QSelf, }; use rustc_errors::{Applicability, Diag, PResult}; use rustc_span::{BytePos, Ident, Span, kw, sym}; @@ -876,13 +876,12 @@ impl<'a> Parser<'a> { /// the caller. pub(super) fn parse_const_arg(&mut self) -> PResult<'a, AnonConst> { // Parse const argument. - let (value, mgca_disambiguation) = if self.token.kind == token::OpenBrace { - let value = self.parse_expr_block(None, self.token.span, BlockCheckMode::Default)?; - (value, MgcaDisambiguation::Direct) + let value = if self.token.kind == token::OpenBrace { + self.parse_expr_block(None, self.token.span, BlockCheckMode::Default)? } else { self.parse_unambiguous_unbraced_const_arg()? }; - Ok(AnonConst { id: ast::DUMMY_NODE_ID, value, mgca_disambiguation }) + Ok(AnonConst { id: ast::DUMMY_NODE_ID, value }) } /// Attempt to parse a const argument that has not been enclosed in braces. @@ -892,9 +891,7 @@ impl<'a> Parser<'a> { /// - Single-segment paths (i.e. standalone generic const parameters). /// All other expressions that can be parsed will emit an error suggesting the expression be /// wrapped in braces. - pub(super) fn parse_unambiguous_unbraced_const_arg( - &mut self, - ) -> PResult<'a, (Box, MgcaDisambiguation)> { + pub(super) fn parse_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, Box> { let start = self.token.span; let attrs = self.parse_outer_attributes()?; let (expr, _) = @@ -915,7 +912,7 @@ impl<'a> Parser<'a> { }); } - Ok((expr, MgcaDisambiguation::Direct)) + Ok(expr) } /// Parse a generic argument in a path segment. @@ -1019,11 +1016,7 @@ impl<'a> Parser<'a> { GenericArg::Type(_) => GenericArg::Type(self.mk_ty(attr_span, TyKind::Err(guar))), GenericArg::Const(_) => { let error_expr = self.mk_expr(attr_span, ExprKind::Err(guar)); - GenericArg::Const(AnonConst { - id: ast::DUMMY_NODE_ID, - value: error_expr, - mgca_disambiguation: MgcaDisambiguation::Direct, - }) + GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value: error_expr }) } GenericArg::Lifetime(lt) => GenericArg::Lifetime(lt), })); diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 349c8d1bea9e8..7c91c15f34034 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -2,9 +2,9 @@ use rustc_ast::token::{self, IdentIsRaw, MetaVarKind, Token, TokenKind}; use rustc_ast::util::case::Case; use rustc_ast::{ self as ast, BoundAsyncness, BoundConstness, BoundPolarity, DUMMY_NODE_ID, FnPtrTy, FnRetTy, - GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MgcaDisambiguation, - MutTy, Mutability, Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, - TraitObjectSyntax, Ty, TyKind, UnsafeBinderTy, + GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, + Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty, + TyKind, UnsafeBinderTy, }; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{Applicability, Diag, E0516, PResult}; @@ -660,7 +660,7 @@ impl<'a> Parser<'a> { }; let ty = if self.eat(exp!(Semi)) { - let mut length = self.parse_expr_anon_const(|_, _| MgcaDisambiguation::Direct)?; + let mut length = self.parse_expr_anon_const()?; if let Err(e) = self.expect(exp!(CloseBracket)) { // Try to recover from `X` when `X::` works @@ -704,7 +704,7 @@ impl<'a> Parser<'a> { // FIXME(mgca): recovery is broken for `const {` args // we first try to parse pattern like `[u8 5]` - let length = match self.parse_expr_anon_const(|_, _| MgcaDisambiguation::Direct) { + let length = match self.parse_expr_anon_const() { Ok(length) => length, Err(e) => { e.cancel(); @@ -794,7 +794,7 @@ impl<'a> Parser<'a> { /// an error type. fn parse_typeof_ty(&mut self, lo: Span) -> PResult<'a, TyKind> { self.expect(exp!(OpenParen))?; - let _expr = self.parse_expr_anon_const(|_, _| MgcaDisambiguation::AnonConst)?; + let _expr = self.parse_expr_anon_const()?; self.expect(exp!(CloseParen))?; let span = lo.to(self.prev_token.span); let guar = self diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 7ae6005e9bc98..863e4d88872c9 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -233,8 +233,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::OnUnimplemented { directive } => { self.check_diagnostic_on_unimplemented(hir_id, directive.as_deref()) } - AttributeKind::OnConst { span, .. } => { - self.check_diagnostic_on_const(*span, hir_id, target, item) + AttributeKind::OnConst { span, directive } => { + self.check_diagnostic_on_const(*span, hir_id, target, item, directive.as_deref()) } AttributeKind::OnMove { directive } => { self.check_diagnostic_on_move(hir_id, directive.as_deref()) @@ -545,10 +545,36 @@ impl<'tcx> CheckAttrVisitor<'tcx> { hir_id: HirId, target: Target, item: Option>, + directive: Option<&Directive>, ) { // We only check the non-constness here. A diagnostic for use // on not-trait impl items is issued during attribute parsing. if target == (Target::Impl { of_trait: true }) { + if let Some(directive) = directive + && let Node::Item(Item { kind: ItemKind::Impl(hir::Impl { generics, .. }), .. }) = + self.tcx.hir_node(hir_id) + { + directive.visit_params(&mut |argument_name, span| { + let has_generic = generics.params.iter().any(|p| { + if !matches!(p.kind, GenericParamKind::Lifetime { .. }) + && let ParamName::Plain(name) = p.name + && name.name == argument_name + { + true + } else { + false + } + }); + if !has_generic { + self.tcx.emit_node_span_lint( + MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + hir_id, + span, + diagnostics::OnConstMalformedFormatLiterals { name: argument_name }, + ) + } + }); + } match item.unwrap() { ItemLike::Item(it) => match it.expect_impl().constness { Constness::Const { .. } => { @@ -566,9 +592,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ItemLike::ForeignItem => {} } } - // FIXME(#155570) Can we do something with generic args here? - // regardless, we don't check the validity of generic args here - // ...whose generics would that be, anyway? The traits' or the impls'? } /// Checks use of generic formatting parameters in `#[diagnostic::on_move]` diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index 92562cc462b87..2589941d7dae1 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -1104,35 +1104,6 @@ pub(crate) struct EiiWithoutImpl { pub help: (), } -#[derive(Diagnostic)] -#[diag("multiple implementations of `#[{$name}]`")] -pub(crate) struct DuplicateEiiImpls { - pub name: Symbol, - - #[primary_span] - #[label("first implemented here in crate `{$first_crate}`")] - pub first_span: Span, - pub first_crate: Symbol, - - #[label("also implemented here in crate `{$second_crate}`")] - pub second_span: Span, - pub second_crate: Symbol, - - #[note("in addition to these two, { $num_additional_crates -> - [one] another implementation was found in crate {$additional_crate_names} - *[other] more implementations were also found in the following crates: {$additional_crate_names} - }")] - pub additional_crates: Option<()>, - - pub num_additional_crates: usize, - pub additional_crate_names: String, - - #[help( - "an \"externally implementable item\" can only have a single implementation in the final artifact. When multiple implementations are found, also in different crates, they conflict" - )] - pub help: (), -} - #[derive(Diagnostic)] #[diag("function doesn't have a default implementation")] pub(crate) struct FunctionNotHaveDefaultImplementation { @@ -1196,6 +1167,13 @@ pub(crate) struct OnMoveMalformedFormatLiterals { pub name: Symbol, } +#[derive(Diagnostic)] +#[diag("unknown parameter `{$name}`")] +#[help(r#"expect either a generic argument name or {"`{Self}`"} as format argument"#)] +pub(crate) struct OnConstMalformedFormatLiterals { + pub name: Symbol, +} + #[derive(Diagnostic)] #[diag("unused target expression is specified for glob or list delegation")] pub(crate) struct GlobOrListDelegationUnusedTargetExpr { diff --git a/compiler/rustc_passes/src/eii.rs b/compiler/rustc_passes/src/eii.rs index c9cfd1e050313..439450fa80bd4 100644 --- a/compiler/rustc_passes/src/eii.rs +++ b/compiler/rustc_passes/src/eii.rs @@ -6,10 +6,11 @@ use std::iter; use rustc_data_structures::fx::FxIndexMap; use rustc_hir::attrs::{EiiDecl, EiiImpl}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_middle::error::DuplicateEiiImpls; use rustc_middle::ty::TyCtxt; use rustc_session::config::CrateType; -use crate::diagnostics::{DuplicateEiiImpls, EiiWithoutImpl}; +use crate::diagnostics::EiiWithoutImpl; #[derive(Clone, Copy, Debug)] enum CheckingMode { diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index d2bcf0359342c..f9b6367d4a9d4 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -411,6 +411,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { Infer, Pat, FieldOf, + View, Err ] ); @@ -660,7 +661,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { If, While, ForLoop, Loop, Match, Closure, Block, Await, Move, Use, TryBlock, Assign, AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret, InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, - Become, IncludedBytes, Gen, UnsafeBinderCast, Err, Dummy + Become, IncludedBytes, Gen, UnsafeBinderCast, Err, Dummy, DirectConstArg ] ); ast_visit::walk_expr(self, e) @@ -688,9 +689,10 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { ImplicitSelf, MacCall, CVarArgs, - Dummy, FieldOf, View, + DirectConstArg, + Dummy, Err ] ); diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index de3f8c380fc44..81c6db6a9d6d7 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -425,26 +425,9 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { fn visit_anon_const(&mut self, constant: &'a AnonConst) { // Note that `visit_anon_const` is skipped for AnonConst nodes wrapped in an // ExprKind::ConstBlock - these are handled in visit_expr, and are DefKind::InlineConst. - - // `MgcaDisambiguation::Direct` is set even when MGCA is disabled, so - // to avoid affecting stable we have to feature gate the not creating - // anon consts - if !self.r.features.min_generic_const_args() { - let parent = self - .create_def(constant.id, None, DefKind::AnonConst, constant.value.span) - .def_id(); - return self.with_parent(parent, |this| visit::walk_anon_const(this, constant)); - } - - match constant.mgca_disambiguation { - MgcaDisambiguation::Direct => visit::walk_anon_const(self, constant), - MgcaDisambiguation::AnonConst => { - let parent = self - .create_def(constant.id, None, DefKind::AnonConst, constant.value.span) - .def_id(); - self.with_parent(parent, |this| visit::walk_anon_const(this, constant)); - } - }; + let parent = + self.create_def(constant.id, None, DefKind::AnonConst, constant.value.span).def_id(); + self.with_parent(parent, |this| visit::walk_anon_const(this, constant)); } #[instrument(level = "debug", skip(self))] diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 2baf423e296d6..22cc6a581f19a 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1661,28 +1661,6 @@ pub(crate) struct UnusedQualifications { pub removal_span: Span, } -#[derive(Diagnostic)] -#[diag( - "{$elided -> - [true] `&` without an explicit lifetime name cannot be used here - *[false] `'_` cannot be used here - }" -)] -pub(crate) struct AssociatedConstElidedLifetime { - #[suggestion( - "use the `'static` lifetime", - style = "verbose", - code = "{code}", - applicability = "machine-applicable" - )] - pub span: Span, - - pub code: &'static str, - pub elided: bool, - #[note("cannot automatically infer `'static` because of other lifetimes in scope")] - pub lifetimes_in_scope: MultiSpan, -} - #[derive(Diagnostic)] #[diag("lifetime parameter `{$ident}` only used once")] pub(crate) struct SingleUseLifetime { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index c94f2f3ffb85d..4ae56ac7c6f3d 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -333,7 +333,11 @@ enum LifetimeRibKind { AnonymousCreateParameter { binder: NodeId, report_in_path: bool }, /// Replace all anonymous lifetimes by provided lifetime. - Elided(LifetimeRes), + Elided { + res: LifetimeRes, + /// Always report those lifetimes as an error if in a path + error_in_path: bool, + }, // -- Barrier ribs that stop lifetime lookup, or continue it but produce an error later. // @@ -342,11 +346,6 @@ enum LifetimeRibKind { /// error on default object bounds (e.g., `Box`). AnonymousReportError, - /// Resolves elided lifetimes to `'static` if there are no other lifetimes in scope, - /// otherwise give a warning that the previous behavior of introducing a new early-bound - /// lifetime is a bug and will be removed (if `emit_lint` is enabled). - StaticIfNoLifetimeInScope { lint_id: NodeId, emit_lint: bool }, - /// Signal we cannot find which should be the anonymous lifetime. ElisionFailure, @@ -369,6 +368,12 @@ enum LifetimeRibKind { /// Lifetimes cannot be elided in `impl Trait` types without `#![feature(anonymous_lifetime_in_impl_trait)]`. ImplTrait, } +impl LifetimeRibKind { + /// Convenience function for creating non-erroring `Elided` variants. + fn elided(res: LifetimeRes) -> LifetimeRibKind { + LifetimeRibKind::Elided { res, error_in_path: false } + } +} #[derive(Copy, Clone, Debug)] enum LifetimeBinderKind { @@ -1162,7 +1167,7 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc this.last_block_rib = None; // Resolve the function body, potentially inside the body of an async closure this.with_lifetime_rib( - LifetimeRibKind::Elided(LifetimeRes::Infer), + LifetimeRibKind::elided(LifetimeRes::Infer), |this| this.visit_block(body), ); @@ -1190,7 +1195,7 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc this.with_lifetime_rib( match binder { ClosureBinder::NotPresent => { - LifetimeRibKind::Elided(LifetimeRes::Infer) + LifetimeRibKind::elided(LifetimeRes::Infer) } ClosureBinder::For { .. } => LifetimeRibKind::AnonymousReportError, }, @@ -1202,7 +1207,7 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc let previous_state = replace(&mut this.in_func_body, true); // Resolve the function body, potentially inside the body of an async closure this.with_lifetime_rib( - LifetimeRibKind::Elided(LifetimeRes::Infer), + LifetimeRibKind::elided(LifetimeRes::Infer), |this| this.visit_expr(body), ); @@ -1380,9 +1385,8 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc } LifetimeRibKind::AnonymousCreateParameter { .. } | LifetimeRibKind::AnonymousReportError - | LifetimeRibKind::StaticIfNoLifetimeInScope { .. } | LifetimeRibKind::ImplTrait - | LifetimeRibKind::Elided(_) + | LifetimeRibKind::Elided { .. } | LifetimeRibKind::ElisionFailure | LifetimeRibKind::ConcreteAnonConst(_) | LifetimeRibKind::ConstParamTy => {} @@ -1786,7 +1790,6 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // lifetime would be illegal. LifetimeRibKind::Item | LifetimeRibKind::AnonymousReportError - | LifetimeRibKind::StaticIfNoLifetimeInScope { .. } | LifetimeRibKind::ElisionFailure => Some(LifetimeUseSet::Many), // An anonymous lifetime is legal here, and bound to the right // place, go ahead. @@ -1800,11 +1803,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { }), // Only report if eliding the lifetime would have the same // semantics. - LifetimeRibKind::Elided(r) => Some(if res == r { - LifetimeUseSet::One { use_span: ident.span, use_ctxt } - } else { - LifetimeUseSet::Many - }), + LifetimeRibKind::Elided { res: r, error_in_path } => { + Some(if res == r && !error_in_path { + LifetimeUseSet::One { use_span: ident.span, use_ctxt } + } else { + LifetimeUseSet::Many + }) + } LifetimeRibKind::Generics { .. } | LifetimeRibKind::ConstParamTy => None, LifetimeRibKind::ConcreteAnonConst(_) => { @@ -1845,12 +1850,11 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { return; } LifetimeRibKind::AnonymousCreateParameter { .. } - | LifetimeRibKind::Elided(_) + | LifetimeRibKind::Elided { .. } | LifetimeRibKind::Generics { .. } | LifetimeRibKind::ElisionFailure | LifetimeRibKind::AnonymousReportError - | LifetimeRibKind::ImplTrait - | LifetimeRibKind::StaticIfNoLifetimeInScope { .. } => {} + | LifetimeRibKind::ImplTrait => {} } } @@ -1889,48 +1893,6 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.record_lifetime_use(lifetime.id, res, elision_candidate); return; } - LifetimeRibKind::StaticIfNoLifetimeInScope { lint_id: node_id, emit_lint } => { - let mut lifetimes_in_scope = vec![]; - for rib in self.lifetime_ribs[..i].iter().rev() { - lifetimes_in_scope.extend(rib.bindings.iter().map(|(ident, _)| ident.span)); - // Consider any anonymous lifetimes, too - if let LifetimeRibKind::AnonymousCreateParameter { binder, .. } = rib.kind - && let Some(extra) = self.r.extra_lifetime_params_map.get(&binder) - { - lifetimes_in_scope.extend(extra.iter().map(|(ident, _, _)| ident.span)); - } - if let LifetimeRibKind::Item = rib.kind { - break; - } - } - if lifetimes_in_scope.is_empty() { - self.record_lifetime_use( - lifetime.id, - LifetimeRes::Static, - elision_candidate, - ); - return; - } else if emit_lint { - let lt_span = if elided { - lifetime.ident.span.shrink_to_hi() - } else { - lifetime.ident.span - }; - let code = if elided { "'static " } else { "'static" }; - - self.r.lint_buffer.buffer_lint( - lint::builtin::ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, - node_id, - lifetime.ident.span, - crate::diagnostics::AssociatedConstElidedLifetime { - elided, - code, - span: lt_span, - lifetimes_in_scope: lifetimes_in_scope.into(), - }, - ); - } - } LifetimeRibKind::AnonymousReportError => { let guar = if elided { let suggestion = if self.diag_metadata.in_assoc_ty_binding { @@ -2047,7 +2009,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.record_lifetime_err(lifetime.id, guar); return; } - LifetimeRibKind::Elided(res) => { + LifetimeRibKind::Elided { res, .. } => { self.record_lifetime_use(lifetime.id, res, elision_candidate); return; } @@ -2317,7 +2279,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // impl Foo for std::cell::Ref // note lack of '_ // async fn foo(_: std::cell::Ref) { ... } LifetimeRibKind::AnonymousCreateParameter { report_in_path: true, .. } - | LifetimeRibKind::StaticIfNoLifetimeInScope { .. } => { + | LifetimeRibKind::Elided { error_in_path: true, .. } => { let sess = self.r.tcx.sess; let subdiag = elided_lifetime_in_path_suggestion( sess.source_map(), @@ -2353,7 +2315,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } break; } - LifetimeRibKind::Elided(res) => { + LifetimeRibKind::Elided { res, error_in_path: false } => { let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime); for id in node_ids { self.record_lifetime_use( @@ -2480,7 +2442,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { if fn_id == this.r.current_owner.id { this.r.current_owner.lifetime_elision_allowed = true; } - LifetimeRibKind::Elided(*res) + LifetimeRibKind::elided(*res) } else { LifetimeRibKind::ElisionFailure }; @@ -2540,7 +2502,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; for (pat, _) in inputs.clone() { debug!("resolving bindings in pat = {pat:?}"); - self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + self.with_lifetime_rib(LifetimeRibKind::elided(LifetimeRes::Infer), |this| { if let Some(pat) = pat { this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings); } @@ -2966,7 +2928,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ident, ty, expr, define_opaque, eii_impls, .. }) => { self.with_static_rib(def_kind, |this| { - this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| { + this.with_lifetime_rib(LifetimeRibKind::elided(LifetimeRes::Static), |this| { this.visit_ty(ty); }); if let Some(expr) = expr { @@ -3004,8 +2966,8 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { this.visit_generics(generics); this.with_lifetime_rib( - LifetimeRibKind::Elided(LifetimeRes::Static), - |this| { + LifetimeRibKind::elided(LifetimeRes::Static), + |this: &mut LateResolutionVisitor<'a, 'ast, 'ra, 'tcx>| { if rhs_kind.is_type_const() && !this.r.features.generic_const_parameter_types() { @@ -3040,7 +3002,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { DUMMY_SP, |this| { this.with_lifetime_rib( - LifetimeRibKind::Elided(LifetimeRes::Infer), + LifetimeRibKind::elided(LifetimeRes::Infer), |this| { this.with_constant_rib( IsRepeatExpr::No, @@ -3401,9 +3363,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { generics.span, |this| { this.with_lifetime_rib( - LifetimeRibKind::StaticIfNoLifetimeInScope { - lint_id: item.id, - emit_lint: false, + LifetimeRibKind::Elided { + res: LifetimeRes::Static, + error_in_path: true, }, |this| { this.visit_generics(generics); @@ -3620,67 +3582,47 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { generics.span, |this| { this.with_lifetime_rib( - // Until these are a hard error, we need to create them within the - // correct binder, Otherwise the lifetimes of this assoc const think - // they are lifetimes of the trait. - LifetimeRibKind::AnonymousCreateParameter { - binder: item.id, - report_in_path: true, + LifetimeRibKind::Elided { + res: LifetimeRes::Static, + error_in_path: true, }, |this| { - this.with_lifetime_rib( - LifetimeRibKind::StaticIfNoLifetimeInScope { - lint_id: item.id, - // In impls, it's not a hard error yet due to backcompat. - emit_lint: true, - }, - |this| { - // If this is a trait impl, ensure the const - // exists in trait - this.check_trait_item( - item.id, - *ident, - *ident, - &item.kind, - ValueNS, - item.span, - seen_trait_items, - |i, s, c| ConstNotMemberOfTrait(i, s, c), - ); + // If this is a trait impl, ensure the const + // exists in trait + this.check_trait_item( + item.id, + *ident, + *ident, + &item.kind, + ValueNS, + item.span, + seen_trait_items, + |i, s, c| ConstNotMemberOfTrait(i, s, c), + ); - this.visit_generics(generics); - if rhs_kind.is_type_const() - && !this - .r - .tcx - .features() - .generic_const_parameter_types() - { - this.with_rib(TypeNS, RibKind::ConstParamTy, |this| { - this.with_rib( - ValueNS, - RibKind::ConstParamTy, - |this| { - this.with_lifetime_rib( - LifetimeRibKind::ConstParamTy, - |this| this.visit_ty(ty), - ) - }, - ) - }); - } else { - this.visit_ty(ty); - } - // We allow arbitrary const expressions inside of associated consts, - // even if they are potentially not const evaluatable. - // - // Type parameters can already be used and as associated consts are - // not used as part of the type system, this is far less surprising. - this.resolve_const_item_rhs(rhs_kind, None); - }, - ) + this.visit_generics(generics); + if rhs_kind.is_type_const() + && !this.r.tcx.features().generic_const_parameter_types() + { + this.with_rib(TypeNS, RibKind::ConstParamTy, |this| { + this.with_rib(ValueNS, RibKind::ConstParamTy, |this| { + this.with_lifetime_rib( + LifetimeRibKind::ConstParamTy, + |this| this.visit_ty(ty), + ) + }) + }); + } else { + this.visit_ty(ty); + } + // We allow arbitrary const expressions inside of associated consts, + // even if they are potentially not const evaluatable. + // + // Type parameters can already be used and as associated consts are + // not used as part of the type system, this is far less surprising. + this.resolve_const_item_rhs(rhs_kind, None); }, - ); + ) }, ); self.resolve_define_opaques(define_opaque); @@ -3902,7 +3844,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } fn resolve_static_body(&mut self, expr: &'ast Expr, item: Option<(Ident, ConstantItemKind)>) { - self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + self.with_lifetime_rib(LifetimeRibKind::elided(LifetimeRes::Infer), |this| { this.with_constant_rib(IsRepeatExpr::No, ConstantHasGenerics::Yes, item, |this| { this.visit_expr(expr) }); @@ -3914,7 +3856,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { rhs_kind: &'ast ConstItemRhsKind, item: Option<(Ident, ConstantItemKind)>, ) { - self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| match rhs_kind { + self.with_lifetime_rib(LifetimeRibKind::elided(LifetimeRes::Infer), |this| match rhs_kind { ConstItemRhsKind::TypeConst { rhs: Some(anon_const) } => { this.resolve_anon_const(anon_const, AnonConstKind::ConstArg(IsRepeatExpr::No)); } @@ -3942,7 +3884,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // Create lifetimes not with `LifetimeRibKind::Generics` but with `LifetimeRibKind::Elided`, // as we are not processing generic params but generic args in a future call (#156342, #156758). - self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + self.with_lifetime_rib(LifetimeRibKind::elided(LifetimeRes::Infer), |this| { if let Some(qself) = &delegation.qself { this.visit_ty(&qself.ty); } @@ -3977,7 +3919,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { //As we lower target_expr_template body to a body of a function we need a label rib (#148889) this.with_label_rib(RibKind::FnOrCoroutine, |this| { - this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + this.with_lifetime_rib(LifetimeRibKind::elided(LifetimeRes::Infer), |this| { this.visit_block(body); }); }); @@ -3986,7 +3928,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { fn resolve_params(&mut self, params: &'ast [Param]) { let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; - self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + self.with_lifetime_rib(LifetimeRibKind::elided(LifetimeRes::Infer), |this| { for Param { pat, .. } in params { this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings); } @@ -5207,7 +5149,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { }; self.with_constant_rib(is_repeat_expr, may_use_generics, None, |this| { - this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + this.with_lifetime_rib(LifetimeRibKind::elided(LifetimeRes::Infer), |this| { resolve_expr(this); }); }); diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index 92bc577f59201..ddf5600f76659 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -641,42 +641,99 @@ pub fn source_span_for_markdown_range_inner( let mut start_bytes = 0; let mut end_bytes = 0; + let span_of_all_fragments: Span = span_of_fragments(fragments)?; + + let mut prev_lines_bytes = 0; 'outer: for (line_no, md_line) in md_lines.enumerate() { loop { let source_line = src_lines.next()?; - match source_line.find(md_line) { - Some(offset) => { - if line_no == starting_line { - start_bytes += offset; + let source_line_len = u32::try_from(source_line.len()).unwrap(); + let source_line_span = + span_of_all_fragments.split_at(prev_lines_bytes).1.split_at(source_line_len).0; + let fragment = fragments + .iter() + // `source_line_span` might contain indentation that `fragment.span` doesn't contain + .find(|fragment| fragment.span.overlaps(source_line_span)); + // Since we're counting bytes, `prev_line_bytes` includes the "\n". + prev_lines_bytes += source_line_len + 1; + if let Some(fragment) = fragment + && let Some(offset) = source_line.find(md_line) + { + if fragment.span.lo() > source_line_span.lo() + && source_line + [..usize::try_from(fragment.span.lo().0 - source_line_span.lo().0).unwrap()] + .chars() + .any(|c| !c.is_whitespace()) + { + // Make sure anything between the start of this line and the fragment itself is just indentation. + // Because source_line is built by splitting the span that covers all fragments, this only finds + // characters *between* doc comments, not characters before or after doc comments. + // + // 1| /** doc */ + // 2| #[inline] /** doc2 */ + // ^^^^^^^^^ + // | this + // + // 3| fn foo() {} + return None; + } + if fragment.span.hi() < source_line_span.hi() + && source_line + [usize::try_from(fragment.span.hi().0 - source_line_span.lo().0).unwrap()..] + .chars() + .any(|c| !c.is_whitespace()) + { + // Make sure anything between the start of this line and the fragment itself is just indentation. + // Because source_line is built by splitting the span that covers all fragments, this only finds + // characters *between* doc comments, not characters before or after doc comments. + // 1| /** doc */ #[inline] + // ^^^^^^^^^ + // | this + // + // 2| /** doc2 */ + // 3| fn foo() {} + return None; + } + if line_no == starting_line { + start_bytes += offset; - if starting_line == ending_line { - break 'outer; - } - } else if line_no == ending_line { - end_bytes += offset; + if starting_line == ending_line { break 'outer; - } else if line_no < starting_line { - start_bytes += source_line.len() - md_line.len(); - } else { - end_bytes += source_line.len() - md_line.len(); } - break; + } else if line_no == ending_line { + end_bytes += offset; + break 'outer; + } else if line_no < starting_line { + start_bytes += source_line.len() - md_line.len(); + } else { + end_bytes += source_line.len() - md_line.len(); } - None => { - // Since this is a source line that doesn't include a markdown line, - // we have to count the newline that we split from earlier. - if line_no <= starting_line { - start_bytes += source_line.len() + 1; - } else { - end_bytes += source_line.len() + 1; - } + break; + } else { + // Since this is a source line that doesn't include a markdown line, + // we have to count it and its newline as non-markdown bytes. + if line_no <= starting_line { + start_bytes += source_line.len() + 1; + } else if source_line.chars().any(|c| !c.is_whitespace()) { + // We're past the first line, but haven't found the last line, + // but we found a non-empty non-markdown line. + // This could be an attribute, and we don't want a diagnostic + // suggesting to delete that attribute, so we return None to be safe. + // 1| /** doc */ + // 2 | #[inline] + // ^^^^^^^^^ + // | this + // 3| /** doc2 */ + // 4| fn foo() {} + return None; + } else { + end_bytes += source_line.len() + 1; } } } } - let span = span_of_fragments(fragments)?; - let src_span = span.from_inner(InnerSpan::new( + let src_span = span_of_all_fragments.from_inner(InnerSpan::new( md_range.start + start_bytes, md_range.end + start_bytes + end_bytes, )); diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 5015741f10c5f..ae01956be9301 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -738,7 +738,7 @@ impl Default for SpanData { impl PartialOrd for Span { fn partial_cmp(&self, rhs: &Self) -> Option { - PartialOrd::partial_cmp(&self.data(), &rhs.data()) + Some(self.cmp(rhs)) } } impl Ord for Span { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3a30a3c266409..9ab27cd1a4918 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -822,6 +822,7 @@ symbols! { diagnostic_on_unmatched_args, dialect, direct, + direct_const_arg, discriminant_kind, discriminant_type, discriminant_value, @@ -1739,6 +1740,7 @@ symbols! { rust_analyzer, rust_begin_unwind, rust_cold_cc, + rust_dash_call: "rust-call", rust_eh_personality, rust_future, rust_logo, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 617ec6c4ac229..b38c933151eb2 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -29,8 +29,8 @@ use rustc_middle::ty::print::{ PrintTraitRefExt as _, with_forced_trimmed_paths, }; use rustc_middle::ty::{ - self, GenericArgKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, - TypeVisitableExt, Unnormalized, Upcast, + self, GenericArgKind, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeVisitableExt, Unnormalized, Upcast, }; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::CrateNum; @@ -916,11 +916,25 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { find_attr!(self.tcx, impl_did, OnConst {directive, ..} => directive.as_deref()) .flatten() { - let (_, format_args) = self.on_unimplemented_components( + let (_, mut format_args) = self.on_unimplemented_components( trait_ref, main_obligation, diag.long_ty_path(), + false, ); + if let ty::Adt(def, args) = trait_ref.self_ty().skip_binder().kind() { + for param in self.tcx.generics_of(def.did()).own_params.iter() { + match param.kind { + GenericParamDefKind::Type { .. } + | GenericParamDefKind::Const { .. } => { + format_args + .generic_args + .push((param.name, args[param.index as usize].to_string())); + } + _ => continue, + } + } + } let CustomDiagnostic { message, label, notes, parent_label: _ } = command.eval(None, &format_args); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 138c53d013ebc..f7e4ec6164c99 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -46,7 +46,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { return CustomDiagnostic::default(); } let (filter_options, format_args) = - self.on_unimplemented_components(trait_pred, obligation, long_ty_path); + self.on_unimplemented_components(trait_pred, obligation, long_ty_path, true); if let Some(command) = find_attr!(self.tcx, trait_pred.def_id(), OnUnimplemented {directive, ..} => directive.as_deref()).flatten() { command.eval( Some(&filter_options), @@ -62,6 +62,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, obligation: &PredicateObligation<'tcx>, long_ty_path: &mut Option, + print_infer_ty_var: bool, ) -> (FilterOptions, FormatArgs) { let (def_id, args) = (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args); let trait_pred = trait_pred.skip_binder(); @@ -244,7 +245,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { if let Some(ty) = trait_pred.trait_ref.args[param.index as usize].as_type() { - self.tcx.short_string(ty, long_ty_path) + if print_infer_ty_var == false + && let ty::Infer(ty::TyVar(_)) = ty.kind() + { + format!("{}", param.name) + } else { + self.tcx.short_string(ty, long_ty_path) + } } else { trait_pred.trait_ref.args[param.index as usize].to_string() } diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index cff2e64a0d1e3..5ff2c904e94c5 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -1,11 +1,14 @@ +use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{InferCtxt, RegionResolutionError}; +use rustc_infer::infer::{ + InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt, TypeOutlivesConstraint, +}; use rustc_macros::extension; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::query::NoSolution; -use rustc_middle::ty::{self, Ty, Unnormalized, elaborate}; -use rustc_span::Span; +use rustc_middle::ty::{self, Ty, TyCtxt, TypingMode, Unnormalized, elaborate}; +use rustc_span::{DUMMY_SP, Span}; use crate::traits::ScrubbedTraitError; use crate::traits::outlives_bounds::InferCtxtExt; @@ -120,3 +123,67 @@ impl<'tcx> InferCtxt<'tcx> { ) } } + +/// Given a known `param_env` and a set of well formed types, can we prove that +/// `ty` outlives `region`. +pub fn ty_known_to_outlive<'tcx>( + tcx: TyCtxt<'tcx>, + id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + wf_tys: &FxIndexSet>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>, +) -> bool { + test_region_obligations(tcx, id, param_env, wf_tys, |infcx| { + infcx.register_type_outlives_constraint_inner(TypeOutlivesConstraint { + sub_region: region, + sup_type: ty, + origin: SubregionOrigin::RelateParamBound(DUMMY_SP, ty, None), + }); + }) +} + +/// Given a known `param_env` and a set of well formed types, can we prove that +/// `region_a` outlives `region_b` +pub fn region_known_to_outlive<'tcx>( + tcx: TyCtxt<'tcx>, + id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + wf_tys: &FxIndexSet>, + region_a: ty::Region<'tcx>, + region_b: ty::Region<'tcx>, +) -> bool { + test_region_obligations(tcx, id, param_env, wf_tys, |infcx| { + infcx.sub_regions( + SubregionOrigin::RelateRegionParamBound(DUMMY_SP, None), + region_b, + region_a, + ty::VisibleForLeakCheck::Unreachable, + ); + }) +} + +/// Given a known `param_env` and a set of well formed types, set up an +/// `InferCtxt`, call the passed function (to e.g. set up region constraints +/// to be tested), then resolve region and return errors +pub fn test_region_obligations<'tcx>( + tcx: TyCtxt<'tcx>, + id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + wf_tys: &FxIndexSet>, + add_constraints: impl FnOnce(&InferCtxt<'tcx>), +) -> bool { + // Unfortunately, we have to use a new `InferCtxt` each call, because + // region constraints get added and solved there and we need to test each + // call individually. + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + + add_constraints(&infcx); + + let errors = infcx.resolve_regions(id, param_env, wf_tys.iter().copied()); + tracing::debug!(?errors, "errors"); + + // If we were able to prove that the type outlives the region without + // an error, it must be because of the implied or explicit bounds... + errors.is_empty() +} diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 72950846ae844..1d3bd53571f72 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -12,6 +12,7 @@ mod fulfill; pub mod misc; pub mod normalize; pub mod outlives_bounds; +pub mod outlives_for_liveness; pub mod project; pub mod query; #[allow(hidden_glob_reexports)] @@ -927,6 +928,10 @@ pub fn provide(providers: &mut Providers) { specialization_enabled_in: specialize::specialization_enabled_in, instantiate_and_check_impossible_predicates, is_impossible_associated_item, + live_args_for_alias_from_outlives_bounds: + outlives_for_liveness::live_args_for_alias_from_outlives_bounds, + args_known_to_outlive_alias_params: + outlives_for_liveness::args_known_to_outlive_alias_params, ..*providers }; } diff --git a/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs b/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs new file mode 100644 index 0000000000000..5f8802310978e --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs @@ -0,0 +1,591 @@ +use rustc_data_structures::fx::FxIndexSet; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::bug; +use rustc_middle::ty::{ + self, Flags, ImplTraitInTraitData, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, +}; + +use crate::infer::outlives::test_type_match; +use crate::infer::region_constraints::VerifyIfEq; +use crate::regions::{region_known_to_outlive, ty_known_to_outlive}; + +/// For a given alias type, this returns the set of (identity) generic args that +/// are relevant for liveness, that can be inferred from outlives bounds on the +/// alias itself, and the explicit and implicit outlives clauses of the alias. +/// Callers should instantiate the returned args with the concrete args of the alias. +/// +/// There are three cases to consider: +/// 1. If there are *no* outlives bounds, then we return None. +/// 2. If there is a `'static` outlives bound, then we know that all args are +/// irrelevant, so we return an empty list. +/// 3. If there are *any* outlives bounds, then we find any args that are known +/// to outlive those bounds, since those are the args whose regions the +/// underlying type could capture. +#[tracing::instrument(level = "debug", skip(tcx), ret)] +pub(crate) fn live_args_for_alias_from_outlives_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + kind: ty::AliasTyKind<'tcx>, +) -> Option>>> { + let def_id = match kind { + ty::AliasTyKind::Projection { def_id } + | ty::AliasTyKind::Inherent { def_id } + | ty::AliasTyKind::Opaque { def_id } + | ty::AliasTyKind::Free { def_id } => def_id, + }; + let self_identity_args = ty::GenericArgs::identity_for_item(tcx, def_id); + + // We first want to collect the outlives bounds of the alias. + let bounds = tcx.item_bounds(def_id).instantiate_identity().skip_norm_wip(); + tracing::debug!(?bounds); + let alias_ty = Ty::new_alias( + tcx, + ty::IsRigid::No, + ty::AliasTy::new_from_args(tcx, kind, self_identity_args), + ); + let outlives_regions: Vec<_> = bounds + .iter() + .filter_map(|clause| { + let outlives = clause.as_type_outlives_clause()?; + if let Some(outlives) = outlives.no_bound_vars() + && outlives.0 == alias_ty + { + Some(outlives.1) + } else { + test_type_match::extract_verify_if_eq( + tcx, + &outlives + .map_bound(|ty::OutlivesPredicate(ty, bound)| VerifyIfEq { ty, bound }), + // FIXME(#155345): Region handling should generally only + // deal with rigid aliases, making sure we do so correctly + // everywhere is effort, so we're just using `No` everywhere + // for now. This should change soon. + alias_ty, + ) + } + }) + .collect(); + tracing::debug!(?outlives_regions); + + // If there are no outlives bounds, then all (non-bivariant) args are potentially live. + if outlives_regions.is_empty() { + return None; + } + + // If any of the outlives bounds are `'static`, then we know the alias + // doesn't capture *any* regions, so we can skip visiting any regions at all. + // + // I was originally a bit concerned about something like `'a: 'static`, and + // whether or not we need to mark `'a` as live. I don't think that we do. + // + // To dig in a bit: Think about the function using this alias. For the alias + // to be well-formed, then it must be proven that the arg (`'a` in this case) + // outlives `'static`. Well, if that is proven *once*, then it must be true + // across the entire function (because `'static` is free). + // + // I think this similarly applies to any other free region, like `'a: 'b` + // where `'b` is *also* free. Though, we of course can't know *here* which + // regions are going to be instantiated with free regions. + if outlives_regions.contains(&tcx.lifetimes.re_static) { + tracing::debug!("alias has a 'static outlives bound, so skipping visiting any regions"); + return Some(ty::EarlyBinder::bind(tcx, vec![])); + } + + // Okay, so we know we have some outlives bounds, and that none of them are `'static`. + // Now, we need to find all other potentially-live args, those that outlive + // an outlives-bound region. `args_known_to_outlive_alias_params` does this + // for us, and in the case of opaques only includes *captured* regions, too. + + let args_known_to_outlive = + tcx.args_known_to_outlive_alias_params(def_id).as_ref().skip_binder(); + tracing::debug!(?args_known_to_outlive); + let mut live_args: Option>> = None; + for outlives_region in outlives_regions { + let Some(outlives_params) = + args_known_to_outlive.iter().find(|(r, _)| *r == outlives_region) + else { + continue; + }; + let new_live_args = outlives_params.1.iter().copied().collect(); + match &mut live_args { + None => live_args = Some(new_live_args), + Some(prev) => *prev = prev.intersection(&new_live_args).copied().collect(), + }; + } + live_args.map(|c| ty::EarlyBinder::bind(tcx, c.into_iter().collect())) +} + +/// For each region param of this alias compute the identity args that are known +/// to outlive it, given only the alias's declared where-clauses. +/// +/// Note: for opaques (including synthetic associated types from RPITITs), +/// the outlives relationships are identified in the context of the *parent*, +/// since bounds and well-formed types are not lowered. +// FIXME: this likely should return a `BitSet` instead of a `Vec>` +#[tracing::instrument(level = "debug", skip(tcx), ret)] +pub(crate) fn args_known_to_outlive_alias_params<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, +) -> ty::EarlyBinder<'tcx, Vec<(ty::Region<'tcx>, Vec>)>> { + match tcx.def_kind(def_id) { + DefKind::OpaqueTy => args_known_to_outlive_opaque_params(tcx, def_id), + DefKind::AssocTy + if let Some(ImplTraitInTraitData::Trait { fn_def_id: _, opaque_def_id }) = + tcx.opt_rpitit_info(def_id.to_def_id()) => + { + args_known_to_outlive_opaque_params(tcx, opaque_def_id.expect_local()) + } + DefKind::AssocTy | DefKind::TyAlias => args_known_to_outlive_non_opaque_params(tcx, def_id), + kind => { + bug!("improper def_kind {kind:?} passed to `live_args_for_alias_from_outlives_bounds`") + } + } +} + +/// For each *captured* region of this alias, compute the *captured* identity +/// args that are known to outlive it, given the definition of the opaque type +/// in the the *parent* context. +/// +/// Some examples: +/// ```ignore (illustrative) +/// // Returns `[('a, ['a]), ('b, ['b])]` +/// fn foo<'a, 'b>() -> impl Sized + use<'a, 'b> {} +/// +/// // Returns `[('a, ['a]), ('b, ['b])]` +/// fn foo<'a: 'a, 'b>() -> impl Sized + use<'a, 'b> {} +/// +/// // Returns `[('a, ['a])]` +/// fn foo<'a, 'b>() -> impl Sized + use<'a> {} +/// +/// // Returns `[('a, ['a, 'b]), ('b, ['b])]` +/// fn foo<'a, 'b: 'a>() -> impl Sized + use<'a, 'b> {} +/// +/// // Returns `[('a, ['a, 'b]), ('b, ['b])]` +/// fn foo<'a, 'b>(_: &'a &'b ()) -> impl Sized + use<'a, 'b> {} +/// ``` +/// +/// Importantly: +/// - *All* captured regions are considered (not just those in outlives bounds) +/// - It doesn't matter if the captured region is early-bound or late-bound +#[tracing::instrument(level = "debug", skip(tcx), ret)] +pub(crate) fn args_known_to_outlive_opaque_params<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, +) -> ty::EarlyBinder<'tcx, Vec<(ty::Region<'tcx>, Vec>)>> { + let self_identity_args = ty::GenericArgs::identity_for_item(tcx, def_id); + + let mut result = Vec::new(); + + // For implied bounds, we need the set of WF types from the parents. + // - For functions, this is all the input and output types. + // - For type alias, there are no implied bounds, so this is empty. + let (parent_def_id, wf_tys) = match tcx.opaque_ty_origin(def_id) { + rustc_hir::OpaqueTyOrigin::FnReturn { parent, .. } + | rustc_hir::OpaqueTyOrigin::AsyncFn { parent, .. } + | rustc_hir::OpaqueTyOrigin::TyAlias { parent, .. } => { + let wf_tys = FxIndexSet::from_iter( + tcx.assumed_wf_types(parent.expect_local()).iter().map(|(ty, _)| *ty), + ); + (parent, wf_tys) + } + }; + let parent_param_env = tcx.param_env(parent_def_id); + tracing::debug!(?parent_param_env); + + // Map the outlives regions to the parent regions. + // If we have `fn foo<'a>() -> impl Sized + 'a`, then this gets lowered as + // ```ignore (illustrative) + // opaque foo_opaque<'a0>: Sized + 'a0; + // fn foo<'a>() -> foo::<'a>::foo_opaque<'a> { ... } + // ``` + // This maps `'a0` to `'a`, because that is what will be used to get the + // explicit and implied outlives relations. + // + // I suppose, an alternative way to do this would be iterate through all the + // *parent* regions and then find those that are captured. This should be + // basically equivalent (except with the added frustration of needing to + // build a `Region` from the opaque region's `LocalDefId`). + let generics = tcx.generics_of(def_id); + let mut parent_outlives_regions = Vec::with_capacity(generics.own_params.len()); + for opaque_arg in self_identity_args[generics.parent_count..].iter() { + let Some(opaque_region) = opaque_arg.as_region() else { + continue; + }; + let region_def_id = match opaque_region.kind() { + ty::ReEarlyParam(ebr) => generics.param_at(ebr.index as usize, tcx).def_id, + _ => panic!("unexpected region `{opaque_region}` in opaque bounds"), + }; + let parent_region = + tcx.map_opaque_lifetime_to_parent_lifetime(region_def_id.expect_local()); + tracing::debug!(?region_def_id, ?parent_region); + parent_outlives_regions.push((parent_region, opaque_region)); + } + tracing::debug!(?parent_outlives_regions); + + // For every captured region, we want to consider outlived args from two sources: + // 1) *Types*: These come from *parent* generics (and are not duplicated to the opaque) + // 2) *Captured Regions* + // + // In both cases, we need to check known outlives for the *parent* region, because that's where the param_env and wf_tys are. + for (parent_outlived_region, opaque_outlived_region) in parent_outlives_regions.iter() { + let mut opaque_outlives_args = Vec::with_capacity(self_identity_args.len()); + for parent_outlives_arg in self_identity_args[..generics.parent_count].iter() { + let type_outlives = match parent_outlives_arg.kind() { + // Consts don't have any non-static regions + ty::GenericArgKind::Const(_) => continue, + // Lifetimes should be captured + ty::GenericArgKind::Lifetime(_) => continue, + ty::GenericArgKind::Type(t) => ty_known_to_outlive( + tcx, + def_id, + parent_param_env, + &wf_tys, + t, + *parent_outlived_region, + ), + }; + if !type_outlives { + continue; + } + + // Types aren't captured, so don't need to map to the opaque + opaque_outlives_args.push(*parent_outlives_arg); + } + + for &(parent_outlives_region, opaque_region) in parent_outlives_regions.iter() { + let region_outlives = parent_outlives_region == *parent_outlived_region + || region_known_to_outlive( + tcx, + def_id, + parent_param_env, + &wf_tys, + parent_outlives_region, + *parent_outlived_region, + ); + if !region_outlives { + continue; + } + + opaque_outlives_args.push(opaque_region.into()); + } + + result.push((*opaque_outlived_region, opaque_outlives_args)); + } + + ty::EarlyBinder::bind(tcx, result) +} + +#[tracing::instrument(level = "debug", skip(tcx), ret)] +pub(crate) fn args_known_to_outlive_non_opaque_params<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, +) -> ty::EarlyBinder<'tcx, Vec<(ty::Region<'tcx>, Vec>)>> { + let self_identity_args = ty::GenericArgs::identity_for_item(tcx, def_id); + let param_env = tcx.param_env(def_id); + tracing::debug!(?param_env); + let wf_tys = tcx.assumed_wf_types(def_id).iter().map(|(ty, _)| *ty).collect::>(); + let mut result = Vec::new(); + for outlived_arg in self_identity_args.iter() { + let Some(outlived_region) = outlived_arg.as_region() else { + continue; + }; + let outliving_args = self_identity_args + .iter() + .filter(|arg| match arg.kind() { + ty::GenericArgKind::Lifetime(r) => { + region_known_to_outlive(tcx, def_id, param_env, &wf_tys, r, outlived_region) + } + ty::GenericArgKind::Type(t) => { + ty_known_to_outlive(tcx, def_id, param_env, &wf_tys, t, outlived_region) + } + ty::GenericArgKind::Const(_) => false, + }) + .collect(); + result.push((outlived_region, outliving_args)); + } + ty::EarlyBinder::bind(tcx, result) +} + +/// For a param-env clause `for<'v..> ::Assoc<..>: 'bound` that +/// applies to `ty` (an alias with `alias_def_id`), returns the set of (identity) args +/// that the underlying type could possibly capture, as restricted by this clause. +/// +/// As an example, let's imagine we had the following associated type definition: +/// ```ignore (illustrative) +/// type Assoc<'a, 'b, 'c: 'a> = (&'a &'c (), &'b ()); +/// ``` +/// +/// the following clause: +/// ```ignore (illustrative) +/// for<'x, 'y> T::Assoc<'x, 'x, 'y>: 'x +/// ``` +/// +/// We know from the clause alone that *given some substitution of `T:Assoc`*, +/// we know that it can capture either the first or the second region. However, +/// the bounds on the associated type itself additionally imply that the +/// third region can *also* be captured, because it outlives the first. +/// +/// Now, let's assume we had this clause: +/// ```ignore (illustrative) +/// for<'x, 'y> T::Assoc<'x, 'y, 'x>: 'x +/// ``` +/// +/// Here, we know that `'a` and `'c` could be captured, but there is no outlives +/// relationship to `'b` for either of those, so the underlying type can't +/// capture any arg containing `'b`. +/// +/// Note: because higher-ranked bounds don't have implications, there will be +/// some cases (like `for<'x, 'y, 'z> T::Assoc<'x, 'y, 'z>: 'x`) that won't +/// be satisfiable today, but the logic here should hold whenever there *is*. +/// +/// Returns `None` if the clause doesn't apply to `ty` or gives us no information. +#[tracing::instrument(level = "debug", skip(tcx), ret)] +fn live_args_for_outlives_clause<'tcx>( + tcx: TyCtxt<'tcx>, + alias_def_id: DefId, + ty: Ty<'tcx>, + outlives: ty::Binder<'tcx, ty::TypeOutlivesPredicate<'tcx>>, +) -> Option>>> { + // N.B. it's okay to skip the binder here (and in the rest of the function), + // because all variables under binders do not escape + let ty::Alias(_, ty::AliasTy { kind: clause_alias_kind, args: clause_args, .. }) = + *outlives.skip_binder().0.kind() + else { + return None; + }; + let clause_def_id = match clause_alias_kind { + ty::AliasTyKind::Projection { def_id } + | ty::AliasTyKind::Inherent { def_id } + | ty::AliasTyKind::Opaque { def_id } + | ty::AliasTyKind::Free { def_id } => def_id, + }; + if clause_def_id != alias_def_id { + return None; + } + + // Here, we're just using this to check if the clause *could apply* to `ty`, + // but importantly we don't want to use the returned region, because that is + // the "last visited" region in `ty` that matches the outlves bound. Actually, + // we want *all* the identity regions in `ty` that match the outlives bound. + test_type_match::extract_verify_if_eq( + tcx, + &outlives.map_bound(|ty::OutlivesPredicate(ty, bound)| VerifyIfEq { ty, bound }), + ty, + )?; + + let outlived_region = outlives.skip_binder().1; + let clause_identity_args = ty::GenericArgs::identity_for_item(tcx, alias_def_id); + match outlived_region.kind() { + // The underlying type must outlive `'static`, so it can't capture any of the args at all. + // + // Of course, you may ask: "what if the function has a `'a: 'static` bound?" See the corresponding + // comment in `live_args_for_alias_from_outlives_bounds` for why we don't need to worry about that. + ty::ReStatic => Some(FxIndexSet::default()), + ty::ReBound(_, br) => { + // The bound is one of the clause's higher-ranked vars. Find the arg + // positions it occupies, then (at the alias's identity level) find + // all args that are known to outlive one of those positions given + // the alias's declared bounds -- only those can be captured by the + // underlying type. + let mut outlived_regions = Vec::new(); + for (clause_arg, identity_arg) in clause_args.iter().zip(clause_identity_args.iter()) { + match clause_arg.kind() { + ty::GenericArgKind::Lifetime(r) => { + if let ty::ReBound(_, arg_br) = r.kind() + && arg_br.var == br.var + { + outlived_regions.push(identity_arg.expect_region()); + } + } + ty::GenericArgKind::Type(_) | ty::GenericArgKind::Const(_) => { + // A bound var inside a type or const arg (e.g. + // `for<'a> >::Output: 'a`) + // can't be reasoned about at the identity-param level, + // so conservatively treat the clause as giving no + // restriction at all. + if clause_arg.has_escaping_bound_vars() { + return None; + } + } + } + } + if outlived_regions.is_empty() { + // The bound var doesn't appear in the args at all, so the clause + // requires the underlying type to outlive *every* region, which + // is equivalent to a `'static` bound. + return Some(FxIndexSet::default()); + } + + // The underlying type can capture any arg that's known to outlive one + // of the bound var's positions (they're all instantiated to the same + // region at any use site this clause applies to). + let args_known_to_outlive = tcx.args_known_to_outlive_alias_params(alias_def_id); + tracing::debug!(?outlived_regions, ?args_known_to_outlive); + let mut capturable_args = FxIndexSet::default(); + for &outlived_region in &outlived_regions { + // There's a bit of a dance here around `Earlybinder::skip_binder` + // and then later a `Earlybinder::bind`. This is because there's + // no real good way today to move the `EarlyBinder` inward + // declaratively without cloning the entire thing. + let (_, outliving_args) = args_known_to_outlive + .as_ref() + .skip_binder() + .iter() + .find(|(region, _)| *region == outlived_region) + .unwrap(); + capturable_args + .extend(outliving_args.iter().copied().map(|a| ty::EarlyBinder::bind(tcx, a))); + } + Some(capturable_args) + } + // A free region (e.g. `for T::Assoc<'a, 'x>: 'x`, where `'x` is free). + // This is effectively the same as `for<'a, 'b> T::Assoc<'a, 'b>: 'b`, + // but that only is sound if we either know that the second substituted + // lifetime equals `'x` or if we *constrain* that lifetime to be `'x`. + // + // In either case, something like this doesn't work today: + // ```ignore (illustrative) + // fn bar<'a, 'b>(a: &'a mut (), b: &'b ()) -> ::Assoc<'a, 'b> { b } + // fn foo<'x>() + // where + // for<'h> ::Assoc<'h, 'x>: 'x, + // { + // let a = &mut (); + // let b: &'x () = &(); + // let val1 = rpit(a, b); + // let val2 = rpit(a, b); + // drop(val1); + // drop(val32); + // } + // ``` + // So, we conservatively treat this as giving no restriction on which args can be captured. + ty::ReEarlyParam(..) => None, + // Don't know that we actually hit this (maybe `ReError`), go ahead and be conservative. + _ => None, + } +} + +/// Visits free regions in the type that are relevant for liveness computation. +/// These regions are passed to `OP`. +/// +/// Specifically, we visit all of the regions of types recursively, except if +/// the type is an alias, we look at the outlives bounds in the param-env and +/// the alias's item bounds. Each such bound restricts which of the alias's +/// args the underlying type could have captured, so only those (capturable) +/// args are visited. If there are no applicable bounds, we walk through the +/// alias's (non-bivariant) args structurally. +pub struct FreeRegionsVisitor<'tcx, OP: FnMut(ty::Region<'tcx>)> { + pub tcx: TyCtxt<'tcx>, + pub param_env: ty::ParamEnv<'tcx>, + pub op: OP, +} + +impl<'tcx, OP> TypeVisitor> for FreeRegionsVisitor<'tcx, OP> +where + OP: FnMut(ty::Region<'tcx>), +{ + fn visit_region(&mut self, r: ty::Region<'tcx>) { + match r.kind() { + // ignore bound regions, keep visiting + ty::ReBound(_, _) => {} + _ => (self.op)(r), + } + } + + #[tracing::instrument(skip(self), level = "debug")] + fn visit_ty(&mut self, ty: Ty<'tcx>) { + // We're only interested in types involving regions + if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) { + return; + } + + match *ty.kind() { + // We can prove that an alias is live two ways: + // 1. All the components are live. + // 2. There is a known outlives bound or where-clause, and that + // region is live. + // + // We search through the item bounds and where clauses for + // either `'static` or a unique outlives region, and if one is + // found, we just need to prove that that region is still live. + // If one is not found, then we continue to walk through the alias. + ty::Alias(_, ty::AliasTy { kind, args, .. }) => { + let tcx = self.tcx; + let param_env = self.param_env; + + // For aliases other than opaques, we have to consider two + // sources of information to identity potentially-live args: + // - Bounds on alias item itself + // - Outlives clauses on the current function that apply to the alias + // + // Each source of information *restricts* the set of potentially-live + // args independently: only the args that can be live for *every* + // source of information can be actually live, so we take the intersection. + let def_id = match kind { + ty::AliasTyKind::Projection { def_id } + | ty::AliasTyKind::Inherent { def_id } + | ty::AliasTyKind::Opaque { def_id } + | ty::AliasTyKind::Free { def_id } => def_id, + }; + let mut capturable: Option< + FxIndexSet>>, + > = None; + let mut restrict = + |capturable_args: FxIndexSet>>| { + match &mut capturable { + None => capturable = Some(capturable_args), + Some(prev) => { + *prev = prev.intersection(&capturable_args).copied().collect() + } + }; + }; + + if let Some(live_args) = tcx.live_args_for_alias_from_outlives_bounds(kind) { + restrict( + live_args + .as_ref() + .skip_binder() + .iter() + .copied() + .map(|a| ty::EarlyBinder::bind(tcx, a)) + .collect(), + ); + } + + for clause in param_env.caller_bounds() { + let Some(outlives) = clause.as_type_outlives_clause() else { + continue; + }; + if let Some(capturable_args) = + live_args_for_outlives_clause(tcx, def_id, ty, outlives) + { + restrict(capturable_args); + } + } + tracing::debug!(?capturable); + + match capturable { + Some(capturable_args) => { + for arg in capturable_args { + let arg = arg.instantiate(tcx, args).skip_norm_wip(); + arg.visit_with(self); + } + } + None => { + // Skip lifetime parameters that are not captured, since they do + // not need to be live. + let variances = tcx.opt_alias_variances(kind); + for (idx, s) in args.iter().enumerate() { + if variances.map(|variances| variances[idx]) != Some(ty::Bivariant) { + s.visit_with(self); + } + } + } + } + } + + _ => ty.super_visit_with(self), + } + } +} diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 0fdfe17b963fa..bef53e422b0d2 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -56,6 +56,7 @@ mod term_kind; mod ty; mod ty_info; mod ty_kind; +mod universe; mod unnormalized; mod upcast; mod visit; @@ -86,6 +87,7 @@ pub use term_kind::*; pub use ty::{Alias, *}; pub use ty_info::*; pub use ty_kind::*; +pub use universe::*; pub use unnormalized::Unnormalized; pub use upcast::*; pub use visit::*; diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 626649027da28..e77e0b9e24d52 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -49,12 +49,11 @@ use crate::data_structures::IndexMap; use crate::fold::TypeSuperFoldable; use crate::inherent::*; use crate::relate::{Relate, RelateResult, TypeRelation, VarianceDiagInfo}; -use crate::visit::TypeSuperVisitable; use crate::{ - AliasTy, Binder, BoundRegion, BoundVar, BoundVariableKind, ConstKind, DebruijnIndex, - FallibleTypeFolder, InferCtxtLike, InferTy, Interner, IsRigid, OutlivesPredicate, RegionKind, - TyKind, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, TypingMode, UniverseIndex, - Variance, VisitorResult, set_aliases_to_non_rigid, + AliasTy, Binder, BoundRegion, BoundVar, BoundVariableKind, DebruijnIndex, FallibleTypeFolder, + InferCtxtLike, Interner, IsRigid, OutlivesPredicate, RegionKind, TyKind, TypeFoldable, + TypeFolder, TypeVisitable, TypeVisitor, TypingMode, UniverseIndex, Variance, VisitorResult, + max_universe, set_aliases_to_non_rigid, }; #[derive_where(Clone, Debug; I: Interner)] @@ -990,84 +989,6 @@ pub fn regions_outlived_by_placeholder( }) } -/// The largest universe a variable or placeholder was from in `t` -pub fn max_universe, I: Interner, T: TypeFoldable>( - infcx: &Infcx, - t: T, -) -> UniverseIndex { - let mut visitor = MaxUniverse::new(infcx); - // `max_universe` is also used while rewriting constraints to lower universes, - // so do not rely on callers having already resolved non-region infer vars. - let t = infcx.resolve_vars_if_possible(t); - t.visit_with(&mut visitor); - visitor.max_universe() -} - -// FIXME(-Zassumptions-on-binders): Share this with the visitor used by generalization. We currently don't -// as generalization does not look at universes of inference variables but we do -struct MaxUniverse<'a, Infcx: InferCtxtLike> { - max_universe: UniverseIndex, - infcx: &'a Infcx, -} - -impl<'a, Infcx: InferCtxtLike> MaxUniverse<'a, Infcx> { - fn new(infcx: &'a Infcx) -> Self { - MaxUniverse { infcx, max_universe: UniverseIndex::ROOT } - } - - fn max_universe(self) -> UniverseIndex { - self.max_universe - } -} - -impl<'a, Infcx: InferCtxtLike, I: Interner> TypeVisitor - for MaxUniverse<'a, Infcx> -{ - type Result = (); - - fn visit_ty(&mut self, t: I::Ty) { - match t.kind() { - TyKind::Placeholder(p) => self.max_universe = self.max_universe.max(p.universe), - TyKind::Infer(InferTy::TyVar(inf)) => { - let u = self.infcx.universe_of_ty(inf).unwrap(); - debug!("var {inf:?} in universe {u:?}"); - self.max_universe = self.max_universe.max(u); - } - _ => t.super_visit_with(self), - } - } - - fn visit_const(&mut self, c: I::Const) { - match c.kind() { - ConstKind::Placeholder(p) => self.max_universe = self.max_universe.max(p.universe), - ConstKind::Infer(rustc_type_ir::InferConst::Var(inf)) => { - let u = self.infcx.universe_of_ct(inf).unwrap(); - debug!("var {inf:?} in universe {u:?}"); - self.max_universe = self.max_universe.max(u); - } - _ => c.super_visit_with(self), - } - } - - fn visit_region(&mut self, r: I::Region) { - match r.kind() { - RegionKind::RePlaceholder(p) => self.max_universe = self.max_universe.max(p.universe), - RegionKind::ReVar(var) => match self.infcx.opportunistic_resolve_lt_var(var).kind() { - RegionKind::RePlaceholder(p) => { - self.max_universe = self.max_universe.max(p.universe) - } - RegionKind::ReVar(var) => { - let u = self.infcx.universe_of_lt(var).unwrap(); - debug!("var {var:?} in universe {u:?}"); - self.max_universe = self.max_universe.max(u); - } - _ => (), - }, - _ => (), - } - } -} - pub struct PlaceholderReplacer { cx: I, existing_var_count: usize, diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 31824b61cfb27..244c81c54af16 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -27,7 +27,7 @@ use crate::{ mod closure; -#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] +#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( feature = "nightly", diff --git a/compiler/rustc_type_ir/src/universe.rs b/compiler/rustc_type_ir/src/universe.rs new file mode 100644 index 0000000000000..9b0990d4d0ff0 --- /dev/null +++ b/compiler/rustc_type_ir/src/universe.rs @@ -0,0 +1,154 @@ +use tracing::{debug, instrument}; + +use crate::inherent::*; +use crate::visit::TypeVisitableExt; +use crate::{ + ConstKind, InferCtxtLike, InferTy, Interner, RegionKind, TyKind, TypeFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitor, UniverseIndex, +}; + +/// The largest universe a variable or placeholder was from in `t` +pub fn max_universe, I: Interner, T: TypeFoldable>( + infcx: &Infcx, + t: T, +) -> UniverseIndex { + max_universe_inner::<_, _, _, true, true>(infcx, t) +} + +/// The largest universe a variable was from in `t` +pub fn max_universe_of_infer_vars< + Infcx: InferCtxtLike, + I: Interner, + T: TypeFoldable, +>( + infcx: &Infcx, + t: T, +) -> UniverseIndex { + max_universe_inner::<_, _, _, false, true>(infcx, t) +} + +/// The largest universe a placeholder was from in `t` +pub fn max_universe_of_placeholders< + Infcx: InferCtxtLike, + I: Interner, + T: TypeFoldable, +>( + infcx: &Infcx, + t: T, +) -> UniverseIndex { + max_universe_inner::<_, _, _, true, false>(infcx, t) +} + +fn max_universe_inner< + Infcx: InferCtxtLike, + I: Interner, + T: TypeFoldable, + const VISIT_PLACEHOLDER: bool, + const VISIT_INFER: bool, +>( + infcx: &Infcx, + t: T, +) -> UniverseIndex { + if !MaxUniverse::::needs_visit(&t) { + return UniverseIndex::ROOT; + } + + let mut visitor = MaxUniverse::<_, VISIT_PLACEHOLDER, VISIT_INFER>::new(infcx); + // FIXME: make this a debug_assert and let callers resolve vars if there's + // perf win here. + let t = infcx.resolve_vars_if_possible(t); + t.visit_with(&mut visitor); + visitor.max_universe() +} + +struct MaxUniverse<'a, Infcx: InferCtxtLike, const VISIT_PLACEHOLDER: bool, const VISIT_INFER: bool> +{ + max_universe: UniverseIndex, + infcx: &'a Infcx, +} + +impl<'a, Infcx: InferCtxtLike, const VISIT_PLACEHOLDER: bool, const VISIT_INFER: bool> + MaxUniverse<'a, Infcx, VISIT_PLACEHOLDER, VISIT_INFER> +{ + fn new(infcx: &'a Infcx) -> Self { + MaxUniverse { infcx, max_universe: UniverseIndex::ROOT } + } + + fn max_universe(self) -> UniverseIndex { + self.max_universe + } + + #[instrument(ret, level = "debug")] + fn needs_visit, I: Interner>(t: &T) -> bool { + (VISIT_PLACEHOLDER && t.has_placeholders()) || (VISIT_INFER && t.has_infer()) + } +} + +impl< + 'a, + Infcx: InferCtxtLike, + I: Interner, + const VISIT_PLACEHOLDER: bool, + const VISIT_INFER: bool, +> TypeVisitor for MaxUniverse<'a, Infcx, VISIT_PLACEHOLDER, VISIT_INFER> +{ + type Result = (); + + fn visit_ty(&mut self, t: I::Ty) { + if !Self::needs_visit(&t) { + return; + } + + match t.kind() { + TyKind::Placeholder(p) if VISIT_PLACEHOLDER => { + self.max_universe = self.max_universe.max(p.universe) + } + TyKind::Infer(InferTy::TyVar(inf)) if VISIT_INFER => { + let u = self.infcx.universe_of_ty(inf).unwrap(); + debug!("var {inf:?} in universe {u:?}"); + self.max_universe = self.max_universe.max(u); + } + _ => t.super_visit_with(self), + } + } + + fn visit_const(&mut self, c: I::Const) { + if !Self::needs_visit(&c) { + return; + } + + match c.kind() { + ConstKind::Placeholder(p) if VISIT_PLACEHOLDER => { + self.max_universe = self.max_universe.max(p.universe) + } + ConstKind::Infer(rustc_type_ir::InferConst::Var(inf)) if VISIT_INFER => { + let u = self.infcx.universe_of_ct(inf).unwrap(); + debug!("var {inf:?} in universe {u:?}"); + self.max_universe = self.max_universe.max(u); + } + _ => c.super_visit_with(self), + } + } + + fn visit_region(&mut self, r: I::Region) { + match r.kind() { + RegionKind::RePlaceholder(p) if VISIT_PLACEHOLDER => { + self.max_universe = self.max_universe.max(p.universe) + } + RegionKind::ReVar(var) if VISIT_INFER => { + match self.infcx.opportunistic_resolve_lt_var(var).kind() { + RegionKind::RePlaceholder(p) if VISIT_PLACEHOLDER => { + self.max_universe = self.max_universe.max(p.universe) + } + RegionKind::ReVar(var) if VISIT_INFER => { + let u = self.infcx.universe_of_lt(var).unwrap(); + debug!("var {var:?} in universe {u:?}"); + self.max_universe = self.max_universe.max(u); + } + _ => (), + } + } + _ => (), + } + } +} diff --git a/library/alloc/src/io/cursor.rs b/library/alloc/src/io/cursor.rs new file mode 100644 index 0000000000000..21f6b8b5b3ffd --- /dev/null +++ b/library/alloc/src/io/cursor.rs @@ -0,0 +1,260 @@ +use crate::alloc::Allocator; +use crate::boxed::Box; +use crate::io::{ + self, Cursor, ErrorKind, IoSlice, WriteThroughCursor, slice_write, slice_write_all, + slice_write_all_vectored, slice_write_vectored, +}; +use crate::vec::Vec; + +/// Reserves the required space, and pads the vec with 0s if necessary. +fn reserve_and_pad( + pos_mut: &mut u64, + vec: &mut Vec, + buf_len: usize, +) -> io::Result { + let pos: usize = (*pos_mut).try_into().map_err(|_| { + io::const_error!( + ErrorKind::InvalidInput, + "cursor position exceeds maximum possible vector length", + ) + })?; + + // For safety reasons, we don't want these numbers to overflow + // otherwise our allocation won't be enough + let desired_cap = pos.saturating_add(buf_len); + if desired_cap > vec.capacity() { + // We want our vec's total capacity + // to have room for (pos+buf_len) bytes. Reserve allocates + // based on additional elements from the length, so we need to + // reserve the difference + cfg_select! { + no_global_oom_handling => { + vec.try_reserve(desired_cap - vec.len())?; + } + _ => { + vec.reserve(desired_cap - vec.len()); + } + } + } + // Pad if pos is above the current len. + if pos > vec.len() { + let diff = pos - vec.len(); + // Unfortunately, `resize()` would suffice but the optimiser does not + // realise the `reserve` it does can be eliminated. So we do it manually + // to eliminate that extra branch + let spare = vec.spare_capacity_mut(); + debug_assert!(spare.len() >= diff); + // Safety: we have allocated enough capacity for this. + // And we are only writing, not reading + unsafe { + spare.get_unchecked_mut(..diff).fill(core::mem::MaybeUninit::new(0)); + vec.set_len(pos); + } + } + + Ok(pos) +} + +/// Writes the slice to the vec without allocating. +/// +/// # Safety +/// +/// `vec` must have `buf.len()` spare capacity. +unsafe fn vec_write_all_unchecked(pos: usize, vec: &mut Vec, buf: &[u8]) -> usize +where + A: Allocator, +{ + debug_assert!(vec.capacity() >= pos + buf.len()); + unsafe { vec.as_mut_ptr().add(pos).copy_from(buf.as_ptr(), buf.len()) }; + pos + buf.len() +} + +/// Resizing `write_all` implementation for [`Cursor`]. +/// +/// Cursor is allowed to have a pre-allocated and initialised +/// vector body, but with a position of 0. This means the [`Write`] +/// will overwrite the contents of the vec. +/// +/// This also allows for the vec body to be empty, but with a position of N. +/// This means that [`Write`] will pad the vec with 0 initially, +/// before writing anything from that point +/// +/// [`Write`]: crate::io::Write +fn vec_write_all(pos_mut: &mut u64, vec: &mut Vec, buf: &[u8]) -> io::Result +where + A: Allocator, +{ + let buf_len = buf.len(); + let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?; + + // Write the buf then progress the vec forward if necessary + // Safety: we have ensured that the capacity is available + // and that all bytes get written up to pos + unsafe { + pos = vec_write_all_unchecked(pos, vec, buf); + if pos > vec.len() { + vec.set_len(pos); + } + }; + + // Bump us forward + *pos_mut += buf_len as u64; + Ok(buf_len) +} + +/// Resizing `write_all_vectored` implementation for [`Cursor`]. +/// +/// Cursor is allowed to have a pre-allocated and initialised +/// vector body, but with a position of 0. This means the [`Write`] +/// will overwrite the contents of the vec. +/// +/// This also allows for the vec body to be empty, but with a position of N. +/// This means that [`Write`] will pad the vec with 0 initially, +/// before writing anything from that point +/// +/// [`Write`]: crate::io::Write +fn vec_write_all_vectored( + pos_mut: &mut u64, + vec: &mut Vec, + bufs: &[IoSlice<'_>], +) -> io::Result +where + A: Allocator, +{ + // For safety reasons, we don't want this sum to overflow ever. + // If this saturates, the reserve should panic to avoid any unsound writing. + let buf_len = bufs.iter().fold(0usize, |a, b| a.saturating_add(b.len())); + let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?; + + // Write the buf then progress the vec forward if necessary + // Safety: we have ensured that the capacity is available + // and that all bytes get written up to the last pos + unsafe { + for buf in bufs { + pos = vec_write_all_unchecked(pos, vec, buf); + } + if pos > vec.len() { + vec.set_len(pos); + } + } + + // Bump us forward + *pos_mut += buf_len as u64; + Ok(buf_len) +} + +#[stable(feature = "cursor_mut_vec", since = "1.25.0")] +impl WriteThroughCursor for &mut Vec +where + A: Allocator, +{ + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + vec_write_all(pos, inner, buf) + } + + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + vec_write_all_vectored(pos, inner, bufs) + } + + #[inline] + fn is_write_vectored(_this: &Cursor) -> bool { + true + } + + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + vec_write_all(pos, inner, buf)?; + Ok(()) + } + + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + vec_write_all_vectored(pos, inner, bufs)?; + Ok(()) + } + + #[inline] + fn flush(_this: &mut Cursor) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl WriteThroughCursor for Vec +where + A: Allocator, +{ + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + vec_write_all(pos, inner, buf) + } + + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + vec_write_all_vectored(pos, inner, bufs) + } + + #[inline] + fn is_write_vectored(_this: &Cursor) -> bool { + true + } + + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + vec_write_all(pos, inner, buf)?; + Ok(()) + } + + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + vec_write_all_vectored(pos, inner, bufs)?; + Ok(()) + } + + #[inline] + fn flush(_this: &mut Cursor) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "cursor_box_slice", since = "1.5.0")] +impl WriteThroughCursor for Box<[u8], A> +where + A: Allocator, +{ + #[inline] + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + slice_write(pos, inner, buf) + } + + #[inline] + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + slice_write_vectored(pos, inner, bufs) + } + + #[inline] + fn is_write_vectored(_this: &Cursor) -> bool { + true + } + + #[inline] + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + slice_write_all(pos, inner, buf) + } + + #[inline] + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + slice_write_all_vectored(pos, inner, bufs) + } + + #[inline] + fn flush(_this: &mut Cursor) -> io::Result<()> { + Ok(()) + } +} diff --git a/library/alloc/src/io/impls.rs b/library/alloc/src/io/impls.rs index 732842bc65df7..1351d3e3ef3a1 100644 --- a/library/alloc/src/io/impls.rs +++ b/library/alloc/src/io/impls.rs @@ -1,7 +1,12 @@ +use crate::alloc::Allocator; use crate::boxed::Box; -use crate::io::{self, Seek, SeekFrom, SizeHint}; +#[cfg(not(no_global_oom_handling))] +use crate::collections::VecDeque; +use crate::fmt; +use crate::io::{self, IoSlice, Seek, SeekFrom, SizeHint, Write}; #[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] use crate::sync::Arc; +use crate::vec::Vec; // ============================================================================= // Forwarding implementations @@ -20,6 +25,43 @@ impl SizeHint for Box { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Box { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + (**self).write(buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + (**self).write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + (**self).is_write_vectored() + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + (**self).flush() + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + (**self).write_all(buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + (**self).write_all_vectored(bufs) + } + + #[inline] + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + (**self).write_fmt(fmt) + } +} #[stable(feature = "rust1", since = "1.0.0")] impl Seek for Box { #[inline] @@ -51,6 +93,148 @@ impl Seek for Box { // ============================================================================= // In-memory buffer implementations +/// Write is implemented for `Vec` by appending to the vector. +/// The vector will grow as needed. +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Vec { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + ::write_all(self, buf)?; + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let len = bufs.iter().map(|b| b.len()).sum(); + cfg_select! { + no_global_oom_handling => { + self.try_reserve(len)?; + } + _ => { + self.reserve(len); + } + } + for buf in bufs { + ::write_all(self, buf)?; + } + Ok(len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + cfg_select! { + no_global_oom_handling => { + self.try_extend_from_slice_of_bytes(buf)?; + } + _ => { + self.extend_from_slice(buf); + } + } + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + self.write_vectored(bufs)?; + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +/// Write is implemented for `VecDeque` by appending to the `VecDeque`, growing it as needed. +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "vecdeque_read_write", since = "1.63.0")] +impl Write for VecDeque { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + self.extend(buf); + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let len = bufs.iter().map(|b| b.len()).sum(); + self.reserve(len); + for buf in bufs { + self.extend(&**buf); + } + Ok(len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.extend(buf); + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + self.write_vectored(bufs)?; + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +#[stable(feature = "io_traits_arc", since = "1.73.0")] +impl Write for Arc +where + for<'a> &'a W: Write, + W: crate::io::IoHandle, +{ + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + (&**self).write(buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + (&**self).write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + (&**self).is_write_vectored() + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + (&**self).flush() + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + (&**self).write_all(buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + (&**self).write_all_vectored(bufs) + } + + #[inline] + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + (&**self).write_fmt(fmt) + } +} #[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] #[stable(feature = "io_traits_arc", since = "1.73.0")] impl Seek for Arc diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 44a1d1b71d164..ef7e95ed5bd7c 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -1,5 +1,6 @@ //! Traits, helpers, and type definitions for core I/O functionality. +mod cursor; mod error; mod impls; @@ -14,8 +15,12 @@ pub use core::io::{BorrowedBuf, BorrowedCursor}; #[unstable(feature = "alloc_io", issue = "154046")] pub use core::io::{ Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, Seek, SeekFrom, - Sink, Take, empty, repeat, sink, + Sink, Take, Write, empty, repeat, sink, }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use core::io::{IoHandle, OsFunctions, SizeHint, chain, stream_len_default, take}; +pub use core::io::{ + IoHandle, OsFunctions, SizeHint, WriteThroughCursor, chain, default_write_vectored, + slice_write, slice_write_all, slice_write_all_vectored, slice_write_vectored, + stream_len_default, take, +}; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 01b4e6f938616..9f82f5be82a68 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -96,6 +96,7 @@ #![feature(async_iterator)] #![feature(bstr)] #![feature(bstr_internals)] +#![feature(can_vector)] #![feature(case_ignorable)] #![feature(cast_maybe_uninit)] #![feature(cell_get_cloned)] @@ -180,6 +181,7 @@ #![feature(unicode_internals)] #![feature(unsize)] #![feature(unwrap_infallible)] +#![feature(write_all_vectored)] #![feature(wtf8_internals)] // tidy-alphabetical-end // diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 7ca6c7b951ffc..78bd6a93e7df3 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2964,8 +2964,26 @@ impl Vec { #[cfg(not(no_global_oom_handling))] #[inline] unsafe fn append_elements(&mut self, other: *const [T]) { + self.reserve(other.len()); + unsafe { + self.append_elements_unreserved(other); + } + } + + /// Appends elements to `self` from other buffer, returning [`TryReserveError`] on OOM. + #[inline] + unsafe fn try_append_elements(&mut self, other: *const [T]) -> Result<(), TryReserveError> { + self.try_reserve(other.len())?; + unsafe { + self.append_elements_unreserved(other); + } + Ok(()) + } + + /// Appends elements to `self` from other buffer without reserving additional capacity. + #[inline] + unsafe fn append_elements_unreserved(&mut self, other: *const [T]) { let count = other.len(); - self.reserve(count); let len = self.len(); if count > 0 { unsafe { @@ -3635,6 +3653,22 @@ impl Vec { } } +impl Vec { + #[cfg_attr( + not(no_global_oom_handling), + expect( + dead_code, + reason = "currently only used in IO module when global OOM handling is disabled" + ) + )] + pub(crate) fn try_extend_from_slice_of_bytes( + &mut self, + other: &[u8], + ) -> Result<(), TryReserveError> { + unsafe { self.try_append_elements(other) } + } +} + impl Vec<[T; N], A> { /// Takes a `Vec<[T; N]>` and flattens it into a `Vec`. /// diff --git a/library/core/src/attribute_docs.rs b/library/core/src/attribute_docs.rs index e3679ecd4d309..94ff7a8fee6d3 100644 --- a/library/core/src/attribute_docs.rs +++ b/library/core/src/attribute_docs.rs @@ -349,3 +349,59 @@ mod deprecated_attribute {} /// [`deny`]: ./attribute.deny.html /// [`forbid`]: ./attribute.forbid.html mod warn_attribute {} + +#[doc(attribute = "no_std")] +// +/// Prevents automatically linking the standard library. +/// +/// Written as an inner attribute at the top of the crate root, the `no_std` attribute stops +/// the compiler from linking [`std`] into the crate, and only links [`core`]. +/// +/// The attribute also swaps which prelude gets inserted, with the [`core`] prelude replacing +/// the [`std`] prelude. Items like [`Option`], [`Result`], and the primitive types remain +/// available from [`core`] without any imports needed: +/// +/// ```rust,ignore (no_std) +/// #![no_std] +/// +/// fn halve(x: u32) -> Option { +/// if x % 2 == 0 { Some(x / 2) } else { None } +/// } +/// ``` +/// +/// Anything that requires heap memory allocations is not part of [`core`]. Linking the [`alloc`] +/// crate explicitly can be used to include these types: +/// +/// ```rust,ignore (no_std + needs a global allocator to link) +/// #![no_std] +/// +/// extern crate alloc; +/// +/// use alloc::vec::Vec; +/// ``` +/// +/// A `no_std` binary also removes the startup routine and default panic handler `std` +/// normally provides. It must define its own `#[panic_handler]`, and typically its own +/// entry point by additionally using `#![no_main]`: +/// +/// ```rust,ignore (no_std + needs more than no_main alone to link) +/// #![no_std] +/// #![no_main] +/// +/// use core::panic::PanicInfo; +/// +/// #[panic_handler] +/// fn on_panic(_info: &PanicInfo) -> ! { +/// loop {} +/// } +/// ``` +/// +/// For more information, see the Reference on [the `no_std` attribute]. +/// +/// [`std`]: ../std/index.html +/// [`core`]: ../core/index.html +/// [`alloc`]: ../alloc/index.html +/// [`Option`]: option::Option +/// [`Result`]: result::Result +/// [the `no_std` attribute]: ../reference/names/preludes.html#the-no_std-attribute +mod no_std_attribute {} diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index d0114c30a6b3c..2af5b4ef46b97 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -1812,6 +1812,28 @@ impl char { ToCasefold(CaseMappingIter::new(conversions::to_casefold(self))) } + /// Returns the code point value as a `u32`. + /// + /// # Examples + /// + /// ``` + /// #![feature(char_to_u32)] + /// + /// let ascii = 'a'; + /// let heart = '❤'; + /// + /// assert_eq!(ascii.to_u32(), 97_u32); + /// assert_eq!(heart.to_u32(), 0x2764_u32); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[unstable(feature = "char_to_u32", issue = "158938")] + #[rustc_const_unstable(feature = "char_to_u32", issue = "158938")] + #[inline(always)] + pub const fn to_u32(self) -> u32 { + self as u32 + } + /// Checks if the value is within the ASCII range. /// /// # Examples diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index bc96c768c0c94..b812b9eb2afc1 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -55,6 +55,7 @@ use crate::ffi::{VaArgSafe, VaList}; use crate::marker::{ConstParamTy, DiscriminantKind, PointeeSized, Tuple}; +use crate::num::imp::libm; use crate::{mem, ptr}; mod bounds; @@ -1083,233 +1084,353 @@ pub fn powif128(a: f128, x: i32) -> f128; /// /// The stabilized version of this intrinsic is /// [`f16::sin`](../../std/primitive.f16.html#method.sin) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn sinf16(x: f16) -> f16; +pub fn sinf16(x: f16) -> f16 { + sinf32(x as f32) as f16 +} /// Returns the sine of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::sin`](../../std/primitive.f32.html#method.sin) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn sinf32(x: f32) -> f32; +pub fn sinf32(x: f32) -> f32 { + cfg_select! { + all(target_env = "msvc", target_arch = "x86") => sinf64(x as f64) as f32, + _ => libm::likely_available::sinf(x), + } +} /// Returns the sine of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::sin`](../../std/primitive.f64.html#method.sin) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn sinf64(x: f64) -> f64; +pub fn sinf64(x: f64) -> f64 { + libm::likely_available::sin(x) +} /// Returns the sine of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::sin`](../../std/primitive.f128.html#method.sin) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn sinf128(x: f128) -> f128; +pub fn sinf128(x: f128) -> f128 { + libm::maybe_available::sinf128(x) +} /// Returns the cosine of an `f16`. /// /// The stabilized version of this intrinsic is /// [`f16::cos`](../../std/primitive.f16.html#method.cos) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn cosf16(x: f16) -> f16; +pub fn cosf16(x: f16) -> f16 { + cosf32(x as f32) as f16 +} /// Returns the cosine of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::cos`](../../std/primitive.f32.html#method.cos) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn cosf32(x: f32) -> f32; +pub fn cosf32(x: f32) -> f32 { + cfg_select! { + all(target_env = "msvc", target_arch = "x86") => cosf64(x as f64) as f32, + _ => libm::likely_available::cosf(x), + } +} /// Returns the cosine of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::cos`](../../std/primitive.f64.html#method.cos) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn cosf64(x: f64) -> f64; +pub fn cosf64(x: f64) -> f64 { + libm::likely_available::cos(x) +} /// Returns the cosine of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::cos`](../../std/primitive.f128.html#method.cos) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn cosf128(x: f128) -> f128; +pub fn cosf128(x: f128) -> f128 { + libm::maybe_available::cosf128(x) +} /// Raises an `f16` to an `f16` power. /// /// The stabilized version of this intrinsic is /// [`f16::powf`](../../std/primitive.f16.html#method.powf) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn powf16(a: f16, x: f16) -> f16; +pub fn powf16(a: f16, x: f16) -> f16 { + powf32(a as f32, x as f32) as f16 +} /// Raises an `f32` to an `f32` power. /// /// The stabilized version of this intrinsic is /// [`f32::powf`](../../std/primitive.f32.html#method.powf) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn powf32(a: f32, x: f32) -> f32; +pub fn powf32(a: f32, x: f32) -> f32 { + cfg_select! { + all(target_env = "msvc", target_arch = "x86") => powf64(a as f64, x as f64) as f32, + _ => libm::likely_available::powf(a, x), + } +} /// Raises an `f64` to an `f64` power. /// /// The stabilized version of this intrinsic is /// [`f64::powf`](../../std/primitive.f64.html#method.powf) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn powf64(a: f64, x: f64) -> f64; +pub fn powf64(a: f64, x: f64) -> f64 { + libm::likely_available::pow(a, x) +} /// Raises an `f128` to an `f128` power. /// /// The stabilized version of this intrinsic is /// [`f128::powf`](../../std/primitive.f128.html#method.powf) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn powf128(a: f128, x: f128) -> f128; +pub fn powf128(a: f128, x: f128) -> f128 { + libm::maybe_available::powf128(a, x) +} /// Returns the exponential of an `f16`. /// /// The stabilized version of this intrinsic is /// [`f16::exp`](../../std/primitive.f16.html#method.exp) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn expf16(x: f16) -> f16; +pub fn expf16(x: f16) -> f16 { + expf32(x as f32) as f16 +} /// Returns the exponential of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::exp`](../../std/primitive.f32.html#method.exp) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn expf32(x: f32) -> f32; +pub fn expf32(x: f32) -> f32 { + cfg_select! { + all(target_env = "msvc", target_arch = "x86") => expf64(x as f64) as f32, + _ => libm::likely_available::expf(x), + } +} /// Returns the exponential of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::exp`](../../std/primitive.f64.html#method.exp) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn expf64(x: f64) -> f64; +pub fn expf64(x: f64) -> f64 { + libm::likely_available::exp(x) +} /// Returns the exponential of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::exp`](../../std/primitive.f128.html#method.exp) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn expf128(x: f128) -> f128; +pub fn expf128(x: f128) -> f128 { + libm::maybe_available::expf128(x) +} /// Returns 2 raised to the power of an `f16`. /// /// The stabilized version of this intrinsic is /// [`f16::exp2`](../../std/primitive.f16.html#method.exp2) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn exp2f16(x: f16) -> f16; +pub fn exp2f16(x: f16) -> f16 { + exp2f32(x as f32) as f16 +} /// Returns 2 raised to the power of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::exp2`](../../std/primitive.f32.html#method.exp2) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn exp2f32(x: f32) -> f32; +pub fn exp2f32(x: f32) -> f32 { + cfg_select! { + all(target_env = "msvc", target_arch = "x86") => exp2f64(x as f64) as f32, + _ => libm::likely_available::exp2f(x), + } +} /// Returns 2 raised to the power of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::exp2`](../../std/primitive.f64.html#method.exp2) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn exp2f64(x: f64) -> f64; +pub fn exp2f64(x: f64) -> f64 { + libm::likely_available::exp2(x) +} /// Returns 2 raised to the power of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::exp2`](../../std/primitive.f128.html#method.exp2) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn exp2f128(x: f128) -> f128; +pub fn exp2f128(x: f128) -> f128 { + libm::maybe_available::exp2f128(x) +} /// Returns the natural logarithm of an `f16`. /// /// The stabilized version of this intrinsic is /// [`f16::ln`](../../std/primitive.f16.html#method.ln) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn logf16(x: f16) -> f16; +pub fn logf16(x: f16) -> f16 { + logf32(x as f32) as f16 +} /// Returns the natural logarithm of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::ln`](../../std/primitive.f32.html#method.ln) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn logf32(x: f32) -> f32; +pub fn logf32(x: f32) -> f32 { + cfg_select! { + all(target_env = "msvc", target_arch = "x86") => logf64(x as f64) as f32, + _ => libm::likely_available::logf(x), + } +} /// Returns the natural logarithm of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::ln`](../../std/primitive.f64.html#method.ln) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn logf64(x: f64) -> f64; +pub fn logf64(x: f64) -> f64 { + libm::likely_available::log(x) +} /// Returns the natural logarithm of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::ln`](../../std/primitive.f128.html#method.ln) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn logf128(x: f128) -> f128; +pub fn logf128(x: f128) -> f128 { + libm::maybe_available::logf128(x) +} /// Returns the base 10 logarithm of an `f16`. /// /// The stabilized version of this intrinsic is /// [`f16::log10`](../../std/primitive.f16.html#method.log10) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn log10f16(x: f16) -> f16; +pub fn log10f16(x: f16) -> f16 { + log10f32(x as f32) as f16 +} /// Returns the base 10 logarithm of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::log10`](../../std/primitive.f32.html#method.log10) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn log10f32(x: f32) -> f32; +pub fn log10f32(x: f32) -> f32 { + cfg_select! { + all(target_env = "msvc", target_arch = "x86") => log10f64(x as f64) as f32, + _ => libm::likely_available::log10f(x), + } +} /// Returns the base 10 logarithm of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::log10`](../../std/primitive.f64.html#method.log10) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn log10f64(x: f64) -> f64; +pub fn log10f64(x: f64) -> f64 { + libm::likely_available::log10(x) +} /// Returns the base 10 logarithm of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::log10`](../../std/primitive.f128.html#method.log10) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn log10f128(x: f128) -> f128; +pub fn log10f128(x: f128) -> f128 { + libm::maybe_available::log10f128(x) +} /// Returns the base 2 logarithm of an `f16`. /// /// The stabilized version of this intrinsic is /// [`f16::log2`](../../std/primitive.f16.html#method.log2) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn log2f16(x: f16) -> f16; +pub fn log2f16(x: f16) -> f16 { + log2f32(x as f32) as f16 +} /// Returns the base 2 logarithm of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::log2`](../../std/primitive.f32.html#method.log2) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn log2f32(x: f32) -> f32; +pub fn log2f32(x: f32) -> f32 { + cfg_select! { + all(target_env = "msvc", target_arch = "x86") => log2f64(x as f64) as f32, + _ => libm::likely_available::log2f(x), + } +} /// Returns the base 2 logarithm of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::log2`](../../std/primitive.f64.html#method.log2) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn log2f64(x: f64) -> f64; +pub fn log2f64(x: f64) -> f64 { + libm::likely_available::log2(x) +} /// Returns the base 2 logarithm of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::log2`](../../std/primitive.f128.html#method.log2) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn log2f128(x: f128) -> f128; +pub fn log2f128(x: f128) -> f128 { + libm::maybe_available::log2f128(x) +} /// Returns `a * b + c` for `f16` values. /// diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index c104b426f48df..fe4def531a84a 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -1,4 +1,5 @@ -use crate::io::{self, ErrorKind, SeekFrom}; +use crate::cmp; +use crate::io::{self, ErrorKind, IoSlice, SeekFrom, Write}; /// A `Cursor` wraps an in-memory buffer and provides it with a /// [`Seek`] implementation. @@ -22,7 +23,7 @@ use crate::io::{self, ErrorKind, SeekFrom}; /// [bytes]: crate::slice "slice" /// [`File`]: ../../std/fs/struct.File.html /// [`Read`]: ../../std/io/trait.Read.html -/// [`Write`]: ../../std/io/trait.Write.html +/// [`Write`]: crate::io::Write /// [`Seek`]: crate::io::Seek /// [Vec]: ../../alloc/vec/struct.Vec.html /// @@ -294,6 +295,144 @@ where } } +/// Non-resizing [`Write::write`] implementation for slices. +/// Exported for `Cursor>`'s implementation of [`Write`]. +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result { + let pos = cmp::min(*pos_mut, slice.len() as u64); + let dst = &mut slice[(pos as usize)..]; + let amt = cmp::min(buf.len(), dst.len()); + dst[..amt].copy_from_slice(&buf[..amt]); + *pos_mut += amt as u64; + Ok(amt) +} + +/// Non-resizing [`Write::write_vectored`] implementation for slices. +/// Exported for `Cursor>`'s implementation of [`Write`]. +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn slice_write_vectored( + pos_mut: &mut u64, + slice: &mut [u8], + bufs: &[IoSlice<'_>], +) -> io::Result { + let mut nwritten = 0; + for buf in bufs { + let n = slice_write(pos_mut, slice, buf)?; + nwritten += n; + if n < buf.len() { + break; + } + } + Ok(nwritten) +} + +/// Non-resizing [`Write::write_all`] implementation for slices. +/// Exported for `Cursor>`'s implementation of [`Write`]. +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn slice_write_all(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<()> { + let n = slice_write(pos_mut, slice, buf)?; + if n < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } +} + +/// Non-resizing [`Write::write_all_vectored`] implementation for slices. +/// Exported for `Cursor>`'s implementation of [`Write`]. +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn slice_write_all_vectored( + pos_mut: &mut u64, + slice: &mut [u8], + bufs: &[IoSlice<'_>], +) -> io::Result<()> { + for buf in bufs { + let n = slice_write(pos_mut, slice, buf)?; + if n < buf.len() { + return Err(io::Error::WRITE_ALL_EOF); + } + } + Ok(()) +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Cursor<&mut [u8]> { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + let (pos, inner) = self.into_parts_mut(); + slice_write(pos, inner, buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = self.into_parts_mut(); + slice_write_vectored(pos, inner, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = self.into_parts_mut(); + slice_write_all(pos, inner, buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = self.into_parts_mut(); + slice_write_all_vectored(pos, inner, bufs) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "cursor_array", since = "1.61.0")] +impl Write for Cursor<[u8; N]> { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + let (pos, inner) = self.into_parts_mut(); + slice_write(pos, inner, buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = self.into_parts_mut(); + slice_write_vectored(pos, inner, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = self.into_parts_mut(); + slice_write_all(pos, inner, buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = self.into_parts_mut(); + slice_write_all_vectored(pos, inner, bufs) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl io::Seek for Cursor where @@ -328,3 +467,53 @@ where Ok(self.position()) } } + +/// Trait used to allow indirect implementation of `Write` for `Cursor`. +/// Since [`Cursor`] is not a foundational type, it is not possible to implement +/// `Write` for `Cursor` if `Write` is defined in `libcore` and `T` is in a +/// downstream crate (e.g., `liballoc` or `libstd`). +/// +/// Methods are identical in purpose and meaning to their `Write` namesakes. +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub trait WriteThroughCursor: Sized { + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result; + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result; + fn is_write_vectored(this: &Cursor) -> bool; + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()>; + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()>; + fn flush(this: &mut Cursor) -> io::Result<()>; +} + +#[doc(hidden)] +impl Write for Cursor { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + WriteThroughCursor::write(self, buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + WriteThroughCursor::write_vectored(self, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + WriteThroughCursor::is_write_vectored(self) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + WriteThroughCursor::write_all(self, buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + WriteThroughCursor::write_all_vectored(self, bufs) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + WriteThroughCursor::flush(self) + } +} diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index 4214a5f87e3ef..eac7396bcfd83 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -79,7 +79,7 @@ pub type Result = result::Result; /// // FIXME(#74481): Hard-links required to link from `core` to `std` /// [Read]: ../../std/io/trait.Read.html -/// [Write]: ../../std/io/trait.Write.html +/// [Write]: crate::io::Write /// [Seek]: crate::io::Seek #[stable(feature = "rust1", since = "1.0.0")] #[rustc_has_incoherent_inherent_impls] @@ -853,7 +853,7 @@ pub enum ErrorKind { /// particular number of bytes but only a smaller number of bytes could be /// written. /// - /// [write]: ../../std/io/trait.Write.html#tymethod.write + /// [write]: crate::io::Write::write /// [`Ok(0)`]: Ok #[stable(feature = "rust1", since = "1.0.0")] WriteZero, diff --git a/library/core/src/io/impls.rs b/library/core/src/io/impls.rs index e8c716f060119..218bd7adc2a55 100644 --- a/library/core/src/io/impls.rs +++ b/library/core/src/io/impls.rs @@ -1,4 +1,5 @@ -use crate::io::{self, Seek, SeekFrom, SizeHint}; +use crate::io::{self, IoSlice, Seek, SeekFrom, SizeHint, Write}; +use crate::{cmp, fmt, mem}; // ============================================================================= // Forwarding implementations @@ -17,6 +18,43 @@ impl SizeHint for &mut T { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for &mut W { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + (**self).write(buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + (**self).write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + (**self).is_write_vectored() + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + (**self).flush() + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + (**self).write_all(buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + (**self).write_all_vectored(bufs) + } + + #[inline] + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + (**self).write_fmt(fmt) + } +} #[stable(feature = "rust1", since = "1.0.0")] impl Seek for &mut S { #[inline] @@ -61,3 +99,110 @@ impl SizeHint for &[u8] { Some(self.len()) } } + +/// Write is implemented for `&mut [u8]` by copying into the slice, overwriting +/// its data. +/// +/// Note that writing updates the slice to point to the yet unwritten part. +/// The slice will be empty when it has been completely overwritten. +/// +/// If the number of bytes to be written exceeds the size of the slice, write operations will +/// return short writes: ultimately, `Ok(0)`; in this situation, `write_all` returns an error of +/// kind `ErrorKind::WriteZero`. +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for &mut [u8] { + #[inline] + fn write(&mut self, data: &[u8]) -> io::Result { + let amt = cmp::min(data.len(), self.len()); + let (a, b) = mem::take(self).split_at_mut(amt); + a.copy_from_slice(&data[..amt]); + *self = b; + Ok(amt) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let mut nwritten = 0; + for buf in bufs { + nwritten += self.write(buf)?; + if self.is_empty() { + break; + } + } + + Ok(nwritten) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, data: &[u8]) -> io::Result<()> { + if self.write(data)? < data.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + for buf in bufs { + if self.write(buf)? < buf.len() { + return Err(io::Error::WRITE_ALL_EOF); + } + } + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[unstable(feature = "read_buf", issue = "78485")] +impl<'a> io::Write for core::io::BorrowedCursor<'a, u8> { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + let amt = cmp::min(buf.len(), self.capacity()); + self.append(&buf[..amt]); + Ok(amt) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let mut nwritten = 0; + for buf in bufs { + let n = self.write(buf)?; + nwritten += n; + if n < buf.len() { + break; + } + } + Ok(nwritten) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + if self.write(buf)? < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + for buf in bufs { + if self.write(buf)? < buf.len() { + return Err(io::Error::WRITE_ALL_EOF); + } + } + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 1c27e42229bb7..0134540ae86c8 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -8,6 +8,7 @@ mod io_slice; mod seek; mod size_hint; mod util; +mod write; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] pub use self::borrowed_buf::{BorrowedBuf, BorrowedCursor}; @@ -24,14 +25,20 @@ pub use self::{ io_slice::{IoSlice, IoSliceMut}, seek::{Seek, SeekFrom}, util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}, + write::Write, }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ + cursor::{ + WriteThroughCursor, slice_write, slice_write_all, slice_write_all_vectored, + slice_write_vectored, + }, error::{Custom, CustomOwner, OsFunctions}, seek::stream_len_default, size_hint::SizeHint, util::{chain, take}, + write::default_write_vectored, }; /// Marks that a type `T` can have IO traits such as [`Seek`], [`Write`], etc. automatically @@ -48,7 +55,7 @@ pub use self::{ // FIXME(#74481): Hard-links required to link from `core` to `std` /// [file]: ../../std/fs/struct.File.html /// [arc]: ../../alloc/sync/struct.Arc.html -/// [`Write`]: ../../std/io/trait.Write.html +/// [`Write`]: crate::io::Write /// [`Seek`]: crate::io::Seek #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] diff --git a/library/core/src/io/util.rs b/library/core/src/io/util.rs index de37690436c80..b022d876eb8f5 100644 --- a/library/core/src/io/util.rs +++ b/library/core/src/io/util.rs @@ -1,10 +1,10 @@ -use crate::io::{ErrorKind, Result, Seek, SeekFrom, SizeHint}; +use crate::io::{self, ErrorKind, IoSlice, Result, Seek, SeekFrom, SizeHint, Write}; use crate::{cmp, fmt}; /// `Empty` ignores any data written via [`Write`], and will always be empty /// (returning zero bytes) when read via [`Read`]. /// -/// [`Write`]: ../../std/io/trait.Write.html +/// [`Write`]: crate::io::Write /// [`Read`]: ../../std/io/trait.Read.html /// /// This struct is generally created by calling [`empty()`]. Please @@ -23,6 +23,84 @@ impl SizeHint for Empty { } } +#[stable(feature = "empty_write", since = "1.73.0")] +impl Write for Empty { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "empty_write", since = "1.73.0")] +impl Write for &Empty { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + #[stable(feature = "empty_seek", since = "1.51.0")] impl Seek for Empty { #[inline] @@ -51,7 +129,7 @@ impl Seek for Empty { /// [`Ok(buf.len())`]: Ok /// [`Ok(0)`]: Ok /// -/// [`write`]: ../../std/io/trait.Write.html#tymethod.write +/// [`write`]: crate::io::Write::write /// [`read`]: ../../std/io/trait.Read.html#tymethod.read /// /// # Examples @@ -142,12 +220,90 @@ impl fmt::Debug for Repeat { #[derive(Copy, Clone, Debug, Default)] pub struct Sink; +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Sink { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "write_mt", since = "1.48.0")] +impl Write for &Sink { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + /// Creates an instance of a writer which will successfully consume all data. /// /// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`] /// and the contents of the buffer will not be inspected. /// -/// [`write`]: ../../std/io/trait.Write.html#tymethod.write +/// [`write`]: crate::io::Write::write /// [`Ok(buf.len())`]: Ok /// /// # Examples diff --git a/library/core/src/io/write.rs b/library/core/src/io/write.rs new file mode 100644 index 0000000000000..cdddd380885f4 --- /dev/null +++ b/library/core/src/io/write.rs @@ -0,0 +1,417 @@ +use crate::fmt; +use crate::io::{Error, IoSlice, Result}; + +/// A trait for objects which are byte-oriented sinks. +/// +/// Implementors of the `Write` trait are sometimes called 'writers'. +/// +/// Writers are defined by two required methods, [`write`] and [`flush`]: +/// +/// * The [`write`] method will attempt to write some data into the object, +/// returning how many bytes were successfully written. +/// +/// * The [`flush`] method is useful for adapters and explicit buffers +/// themselves for ensuring that all buffered data has been pushed out to the +/// 'true sink'. +/// +/// Writers are intended to be composable with one another. Many implementors +/// throughout [`std::io`] take and provide types which implement the `Write` +/// trait. +/// +/// [`write`]: Write::write +/// [`flush`]: Write::flush +/// [`std::io`]: crate::io +/// +/// # Examples +/// +/// ```no_run +/// use std::io::prelude::*; +/// use std::fs::File; +/// +/// fn main() -> std::io::Result<()> { +/// let data = b"some bytes"; +/// +/// let mut pos = 0; +/// let mut buffer = File::create("foo.txt")?; +/// +/// while pos < data.len() { +/// let bytes_written = buffer.write(&data[pos..])?; +/// pos += bytes_written; +/// } +/// Ok(()) +/// } +/// ``` +/// +/// The trait also provides convenience methods like [`write_all`], which calls +/// `write` in a loop until its entire input has been written. +/// +/// [`write_all`]: Write::write_all +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(notable_trait)] +#[cfg_attr(not(test), rustc_diagnostic_item = "IoWrite")] +pub trait Write { + /// Writes a buffer into this writer, returning how many bytes were written. + /// + /// This function will attempt to write the entire contents of `buf`, but + /// the entire write might not succeed, or the write may also generate an + /// error. Typically, a call to `write` represents one attempt to write to + /// any wrapped object. + /// + /// Calls to `write` are not guaranteed to block waiting for data to be + /// written, and a write which would otherwise block can be indicated through + /// an [`Err`] variant. + /// + /// If this method consumed `n > 0` bytes of `buf` it must return [`Ok(n)`]. + /// If the return value is `Ok(n)` then `n` must satisfy `n <= buf.len()`. + /// A return value of `Ok(0)` typically means that the underlying object is + /// no longer able to accept bytes and will likely not be able to in the + /// future as well, or that the buffer provided is empty. + /// + /// # Errors + /// + /// Each call to `write` may generate an I/O error indicating that the + /// operation could not be completed. If an error is returned then no bytes + /// in the buffer were written to this writer. + /// + /// It is **not** considered an error if the entire buffer could not be + /// written to this writer. + /// + /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the + /// write operation should be retried if there is nothing else to do. + /// + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// // Writes some prefix of the byte string, not necessarily all of it. + /// buffer.write(b"some bytes")?; + /// Ok(()) + /// } + /// ``` + /// + /// [`Ok(n)`]: Ok + #[stable(feature = "rust1", since = "1.0.0")] + fn write(&mut self, buf: &[u8]) -> Result; + + /// Like [`write`], except that it writes from a slice of buffers. + /// + /// Data is copied from each buffer in order, with the final buffer + /// read from possibly being only partially consumed. This method must + /// behave as a call to [`write`] with the buffers concatenated would. + /// + /// The default implementation calls [`write`] with either the first nonempty + /// buffer provided, or an empty one if none exists. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::IoSlice; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let data1 = [1; 8]; + /// let data2 = [15; 8]; + /// let io_slice1 = IoSlice::new(&data1); + /// let io_slice2 = IoSlice::new(&data2); + /// + /// let mut buffer = File::create("foo.txt")?; + /// + /// // Writes some prefix of the byte string, not necessarily all of it. + /// buffer.write_vectored(&[io_slice1, io_slice2])?; + /// Ok(()) + /// } + /// ``` + /// + /// [`write`]: Write::write + #[stable(feature = "iovec", since = "1.36.0")] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result { + default_write_vectored(|b| self.write(b), bufs) + } + + /// Determines if this `Write`r has an efficient [`write_vectored`] + /// implementation. + /// + /// If a `Write`r does not override the default [`write_vectored`] + /// implementation, code using it may want to avoid the method all together + /// and coalesce writes into a single buffer for higher performance. + /// + /// The default implementation returns `false`. + /// + /// [`write_vectored`]: Write::write_vectored + #[unstable(feature = "can_vector", issue = "69941")] + fn is_write_vectored(&self) -> bool { + false + } + + /// Flushes this output stream, ensuring that all intermediately buffered + /// contents reach their destination. + /// + /// # Errors + /// + /// It is considered an error if not all bytes could be written due to + /// I/O errors or EOF being reached. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::io::BufWriter; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = BufWriter::new(File::create("foo.txt")?); + /// + /// buffer.write_all(b"some bytes")?; + /// buffer.flush()?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn flush(&mut self) -> Result<()>; + + /// Attempts to write an entire buffer into this writer. + /// + /// This method will continuously call [`write`] until there is no more data + /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is + /// returned. This method will not return until the entire buffer has been + /// successfully written or such an error occurs. The first error that is + /// not of [`ErrorKind::Interrupted`] kind generated from this method will be + /// returned. + /// + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// + /// If the buffer contains no data, this will never call [`write`]. + /// + /// # Errors + /// + /// This function will return the first error of + /// non-[`ErrorKind::Interrupted`] kind that [`write`] returns. + /// + /// [`write`]: Write::write + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// buffer.write_all(b"some bytes")?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn write_all(&mut self, mut buf: &[u8]) -> Result<()> { + while !buf.is_empty() { + match self.write(buf) { + Ok(0) => { + return Err(Error::WRITE_ALL_EOF); + } + Ok(n) => buf = &buf[n..], + Err(ref e) if e.is_interrupted() => {} + Err(e) => return Err(e), + } + } + Ok(()) + } + + /// Attempts to write multiple buffers into this writer. + /// + /// This method will continuously call [`write_vectored`] until there is no + /// more data to be written or an error of non-[`ErrorKind::Interrupted`] + /// kind is returned. This method will not return until all buffers have + /// been successfully written or such an error occurs. The first error that + /// is not of [`ErrorKind::Interrupted`] kind generated from this method + /// will be returned. + /// + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// + /// If the buffer contains no data, this will never call [`write_vectored`]. + /// + /// # Notes + /// + /// Unlike [`write_vectored`], this takes a *mutable* reference to + /// a slice of [`IoSlice`]s, not an immutable one. That's because we need to + /// modify the slice to keep track of the bytes already written. + /// + /// Once this function returns, the contents of `bufs` are unspecified, as + /// this depends on how many calls to [`write_vectored`] were necessary. It is + /// best to understand this function as taking ownership of `bufs` and to + /// not use `bufs` afterwards. The underlying buffers, to which the + /// [`IoSlice`]s point (but not the [`IoSlice`]s themselves), are unchanged and + /// can be reused. + /// + /// [`write_vectored`]: Write::write_vectored + /// + /// # Examples + /// + /// ``` + /// #![feature(write_all_vectored)] + /// # fn main() -> std::io::Result<()> { + /// + /// use std::io::{Write, IoSlice}; + /// + /// let mut writer = Vec::new(); + /// let bufs = &mut [ + /// IoSlice::new(&[1]), + /// IoSlice::new(&[2, 3]), + /// IoSlice::new(&[4, 5, 6]), + /// ]; + /// + /// writer.write_all_vectored(bufs)?; + /// // Note: the contents of `bufs` is now undefined, see the Notes section. + /// + /// assert_eq!(writer, &[1, 2, 3, 4, 5, 6]); + /// # Ok(()) } + /// ``` + #[unstable(feature = "write_all_vectored", issue = "70436")] + fn write_all_vectored(&mut self, mut bufs: &mut [IoSlice<'_>]) -> Result<()> { + // Guarantee that bufs is empty if it contains no data, + // to avoid calling write_vectored if there is no data to be written. + IoSlice::advance_slices(&mut bufs, 0); + while !bufs.is_empty() { + match self.write_vectored(bufs) { + Ok(0) => { + return Err(Error::WRITE_ALL_EOF); + } + Ok(n) => IoSlice::advance_slices(&mut bufs, n), + Err(ref e) if e.is_interrupted() => {} + Err(e) => return Err(e), + } + } + Ok(()) + } + + /// Writes a formatted string into this writer, returning any error + /// encountered. + /// + /// This method is primarily used to interface with the + /// [`format_args!()`] macro, and it is rare that this should + /// explicitly be called. The [`write!()`] macro should be favored to + /// invoke this method instead. + /// + /// This function internally uses the [`write_all`] method on + /// this trait and hence will continuously write data so long as no errors + /// are received. This also means that partial writes are not indicated in + /// this signature. + /// + /// [`write_all`]: Write::write_all + /// + /// # Errors + /// + /// This function will return any I/O error reported while formatting. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// // this call + /// write!(buffer, "{:.*}", 2, 1.234567)?; + /// // turns into this: + /// buffer.write_fmt(format_args!("{:.*}", 2, 1.234567))?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<()> { + if let Some(s) = args.as_statically_known_str() { + self.write_all(s.as_bytes()) + } else { + default_write_fmt(self, args) + } + } + + /// Creates a "by reference" adapter for this instance of `Write`. + /// + /// The returned adapter also implements `Write` and will simply borrow this + /// current writer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::Write; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// let reference = buffer.by_ref(); + /// + /// // we can use reference just like our original buffer + /// reference.write_all(b"some bytes")?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn by_ref(&mut self) -> &mut Self + where + Self: Sized, + { + self + } +} + +/// Default implementation of [`Write::write_vectored`], which is currently used +/// in `libstd` for file system implementations of similar methods. +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn default_write_vectored(write: F, bufs: &[IoSlice<'_>]) -> Result +where + F: FnOnce(&[u8]) -> Result, +{ + let buf = bufs.iter().find(|b| !b.is_empty()).map_or(&[][..], |b| &**b); + write(buf) +} + +fn default_write_fmt(this: &mut W, args: fmt::Arguments<'_>) -> Result<()> { + // Create a shim which translates a `Write` to a `fmt::Write` and saves off + // I/O errors, instead of discarding them. + struct Adapter<'a, T: ?Sized + 'a> { + inner: &'a mut T, + error: Result<()>, + } + + impl fmt::Write for Adapter<'_, T> { + fn write_str(&mut self, s: &str) -> fmt::Result { + match self.inner.write_all(s.as_bytes()) { + Ok(()) => Ok(()), + Err(e) => { + self.error = Err(e); + Err(fmt::Error) + } + } + } + } + + let mut output = Adapter { inner: this, error: Ok(()) }; + match fmt::write(&mut output, args) { + Ok(()) => Ok(()), + Err(..) => { + // Check whether the error came from the underlying `Write`. + if output.error.is_err() { + output.error + } else { + // This shouldn't happen: the underlying stream did not error, + // but somehow the formatter still errored? + panic!( + "a formatting trait implementation returned an error when the underlying stream did not" + ); + } + } + } +} diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 45b8b266a2671..e3785c92c8d0d 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1073,6 +1073,20 @@ pub const trait Destruct: PointeeSized {} #[rustc_dyn_incompatible_trait] pub trait Tuple {} +/// Creates a new style directly represented const argument. +/// ```ignore (cannot test this from within core yet) +/// type const BAR: usize = N; +/// type const FOO: usize = direct!(BAR::); +/// ``` +#[rustc_builtin_macro(direct_const_arg)] +#[unstable(feature = "min_generic_const_args", issue = "132980")] +#[macro_export] +macro_rules! direct_const_arg { + ($($arg:tt)*) => { + /* compiler built-in */ + }; +} + /// A marker for types which can be used as types of `const` generic parameters. /// /// These types must have a proper equivalence relation (`Eq`) and it must be automatically diff --git a/library/core/src/num/imp/libm.rs b/library/core/src/num/imp/libm.rs index aeabb08723095..388f1479b2461 100644 --- a/library/core/src/num/imp/libm.rs +++ b/library/core/src/num/imp/libm.rs @@ -1,11 +1,168 @@ //! Bindings to math functions provided by the system `libm` or by the `libm` crate, exposed //! via `compiler-builtins`. +//! +//! The functions in the root of this module are "guaranteed" to be available; see the +//! `full_availability` module in compiler-builtins for details. // SAFETY: These symbols have standard interfaces in C and are defined by `libm`, or are // provided by `compiler-builtins` on unsupported platforms. +#[allow(dead_code)] // This list reflects what is available rather than what is consumed. unsafe extern "C" { - pub(crate) safe fn cbrt(n: f64) -> f64; + pub(crate) safe fn cbrt(x: f64) -> f64; pub(crate) safe fn cbrtf(n: f32) -> f32; + pub(crate) safe fn ceil(x: f64) -> f64; + pub(crate) safe fn ceilf(x: f32) -> f32; + pub(crate) safe fn ceilf128(x: f128) -> f128; + pub(crate) safe fn ceilf16(x: f16) -> f16; + pub(crate) safe fn copysign(x: f64, y: f64) -> f64; + pub(crate) safe fn copysignf(x: f32, y: f32) -> f32; + pub(crate) safe fn copysignf128(x: f128, y: f128) -> f128; + pub(crate) safe fn copysignf16(x: f16, y: f16) -> f16; + pub(crate) safe fn fabs(x: f64) -> f64; + pub(crate) safe fn fabsf(x: f32) -> f32; + pub(crate) safe fn fabsf128(x: f128) -> f128; + pub(crate) safe fn fabsf16(x: f16) -> f16; pub(crate) safe fn fdim(a: f64, b: f64) -> f64; pub(crate) safe fn fdimf(a: f32, b: f32) -> f32; + pub(crate) safe fn fdimf128(x: f128, y: f128) -> f128; + pub(crate) safe fn fdimf16(x: f16, y: f16) -> f16; + pub(crate) safe fn floor(x: f64) -> f64; + pub(crate) safe fn floorf(x: f32) -> f32; + pub(crate) safe fn floorf128(x: f128) -> f128; + pub(crate) safe fn floorf16(x: f16) -> f16; + pub(crate) safe fn fma(x: f64, y: f64, z: f64) -> f64; + pub(crate) safe fn fmaf(x: f32, y: f32, z: f32) -> f32; + pub(crate) safe fn fmaf128(x: f128, y: f128, z: f128) -> f128; + pub(crate) safe fn fmax(x: f64, y: f64) -> f64; + pub(crate) safe fn fmaxf(x: f32, y: f32) -> f32; + pub(crate) safe fn fmaxf128(x: f128, y: f128) -> f128; + pub(crate) safe fn fmaxf16(x: f16, y: f16) -> f16; + pub(crate) safe fn fmaximum(x: f64, y: f64) -> f64; + pub(crate) safe fn fmaximumf(x: f32, y: f32) -> f32; + pub(crate) safe fn fmaximumf128(x: f128, y: f128) -> f128; + pub(crate) safe fn fmaximumf16(x: f16, y: f16) -> f16; + pub(crate) safe fn fmin(x: f64, y: f64) -> f64; + pub(crate) safe fn fminf(x: f32, y: f32) -> f32; + pub(crate) safe fn fminf128(x: f128, y: f128) -> f128; + pub(crate) safe fn fminf16(x: f16, y: f16) -> f16; + pub(crate) safe fn fminimum(x: f64, y: f64) -> f64; + pub(crate) safe fn fminimumf(x: f32, y: f32) -> f32; + pub(crate) safe fn fminimumf128(x: f128, y: f128) -> f128; + pub(crate) safe fn fminimumf16(x: f16, y: f16) -> f16; + pub(crate) safe fn fmod(x: f64, y: f64) -> f64; + pub(crate) safe fn fmodf(x: f32, y: f32) -> f32; + pub(crate) safe fn fmodf128(x: f128, y: f128) -> f128; + pub(crate) safe fn fmodf16(x: f16, y: f16) -> f16; + pub(crate) safe fn rint(x: f64) -> f64; + pub(crate) safe fn rintf(x: f32) -> f32; + pub(crate) safe fn rintf128(x: f128) -> f128; + pub(crate) safe fn rintf16(x: f16) -> f16; + pub(crate) safe fn round(x: f64) -> f64; + pub(crate) safe fn roundeven(x: f64) -> f64; + pub(crate) safe fn roundevenf(x: f32) -> f32; + pub(crate) safe fn roundevenf128(x: f128) -> f128; + pub(crate) safe fn roundevenf16(x: f16) -> f16; + pub(crate) safe fn roundf(x: f32) -> f32; + pub(crate) safe fn roundf128(x: f128) -> f128; + pub(crate) safe fn roundf16(x: f16) -> f16; + pub(crate) safe fn sqrt(x: f64) -> f64; + pub(crate) safe fn sqrtf(x: f32) -> f32; + pub(crate) safe fn sqrtf128(x: f128) -> f128; + pub(crate) safe fn sqrtf16(x: f16) -> f16; + pub(crate) safe fn trunc(x: f64) -> f64; + pub(crate) safe fn truncf(x: f32) -> f32; + pub(crate) safe fn truncf128(x: f128) -> f128; + pub(crate) safe fn truncf16(x: f16) -> f16; +} + +/// These symbols will be available when `std` is available, and on many no-std platforms. However, +/// since this isn't a guarantee, we cannot rely on them for stable implementations. +pub(crate) mod likely_available { + cfg_select! { + all(target_env = "msvc", target_arch = "x86") => { + /* these symbols are not available on x86 msvc */ + } + _ => { + #[allow(dead_code)] + unsafe extern "C" { + pub(crate) safe fn acosf(n: f32) -> f32; + pub(crate) safe fn asinf(n: f32) -> f32; + pub(crate) safe fn atan2f(a: f32, b: f32) -> f32; + pub(crate) safe fn atanf(n: f32) -> f32; + pub(crate) safe fn cosf(x: f32) -> f32; + pub(crate) safe fn coshf(n: f32) -> f32; + pub(crate) safe fn erfcf(x: f32) -> f32; + pub(crate) safe fn erff(x: f32) -> f32; + pub(crate) safe fn exp2f(x: f32) -> f32; + pub(crate) safe fn expf(x: f32) -> f32; + pub(crate) safe fn expm1f(n: f32) -> f32; + pub(crate) safe fn hypotf(x: f32, y: f32) -> f32; + pub(crate) safe fn ldexpf(f: f32, n: i32) -> f32; + pub(crate) safe fn log10f(x: f32) -> f32; + pub(crate) safe fn log1pf(n: f32) -> f32; + pub(crate) safe fn log2f(x: f32) -> f32; + pub(crate) safe fn logf(x: f32) -> f32; + pub(crate) safe fn powf(x: f32, y: f32) -> f32; + pub(crate) safe fn sinf(x: f32) -> f32; + pub(crate) safe fn sinhf(n: f32) -> f32; + pub(crate) safe fn tanf(n: f32) -> f32; + pub(crate) safe fn tanhf(n: f32) -> f32; + pub(crate) safe fn tgammaf(x: f32) -> f32; + } + } + } + + #[allow(dead_code)] + unsafe extern "C" { + pub(crate) safe fn acos(x: f64) -> f64; + pub(crate) safe fn asin(x: f64) -> f64; + pub(crate) safe fn atan(x: f64) -> f64; + pub(crate) safe fn atan2(x: f64, y: f64) -> f64; + pub(crate) safe fn cos(x: f64) -> f64; + pub(crate) safe fn cosh(x: f64) -> f64; + pub(crate) safe fn erf(x: f64) -> f64; + pub(crate) safe fn erfc(x: f64) -> f64; + pub(crate) safe fn exp(x: f64) -> f64; + pub(crate) safe fn exp2(x: f64) -> f64; + pub(crate) safe fn expm1(x: f64) -> f64; + pub(crate) safe fn hypot(x: f64, y: f64) -> f64; + pub(crate) safe fn ldexp(f: f64, n: i32) -> f64; + pub(crate) safe fn log(x: f64) -> f64; + pub(crate) safe fn log10(x: f64) -> f64; + pub(crate) safe fn log1p(x: f64) -> f64; + pub(crate) safe fn log2(x: f64) -> f64; + pub(crate) safe fn pow(x: f64, y: f64) -> f64; + pub(crate) safe fn sin(x: f64) -> f64; + pub(crate) safe fn sinh(x: f64) -> f64; + pub(crate) safe fn tan(x: f64) -> f64; + pub(crate) safe fn tanh(x: f64) -> f64; + pub(crate) safe fn tgamma(x: f64) -> f64; + } +} + +/// These symbols exist on some platforms but do not have a compiler-builtins fallback. +pub(crate) mod maybe_available { + #[allow(dead_code)] + unsafe extern "C" { + pub(crate) safe fn acosf128(x: f128) -> f128; + pub(crate) safe fn asinf128(x: f128) -> f128; + pub(crate) safe fn atanf128(x: f128) -> f128; + pub(crate) safe fn cbrtf128(x: f128) -> f128; + pub(crate) safe fn cosf128(x: f128) -> f128; + pub(crate) safe fn erff128(x: f128) -> f128; + pub(crate) safe fn expf128(x: f128) -> f128; + pub(crate) safe fn exp2f128(x: f128) -> f128; + pub(crate) safe fn expm1f128(x: f128) -> f128; + pub(crate) safe fn hypotf128(x: f128, y: f128) -> f128; + pub(crate) safe fn ldexpf128(f: f128, n: i32) -> f128; + pub(crate) safe fn log10f128(x: f128) -> f128; + pub(crate) safe fn log1pf128(x: f128) -> f128; + pub(crate) safe fn log2f128(x: f128) -> f128; + pub(crate) safe fn logf128(x: f128) -> f128; + pub(crate) safe fn powf128(x: f128, y: f128) -> f128; + pub(crate) safe fn sinf128(x: f128) -> f128; + pub(crate) safe fn tanf128(x: f128) -> f128; + pub(crate) safe fn tanhf128(x: f128) -> f128; + pub(crate) safe fn tgammaf128(x: f128) -> f128; + } } diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 5399789b4242f..e0b127bef8e2b 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -4,10 +4,8 @@ mod tests; #[stable(feature = "rust1", since = "1.0.0")] pub use core::io::Cursor; -use crate::alloc::Allocator; -use crate::cmp; use crate::io::prelude::*; -use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; +use crate::io::{self, BorrowedCursor, IoSliceMut}; #[stable(feature = "rust1", since = "1.0.0")] impl Read for Cursor @@ -101,414 +99,3 @@ where self.set_position(self.position() + amt as u64); } } - -/// Trait used to allow indirect implementation of `Write` for `Cursor`. -/// Since [`Cursor`] is not a foundational type, it is not possible to implement -/// `Write` for `Cursor` if `Write` is defined in `libcore` and `T` is in a -/// downstream crate (e.g., `liballoc` or `libstd`). -/// -/// Methods are identical in purpose and meaning to their `Write` namesakes. -trait WriteThroughCursor: Sized { - fn write(this: &mut Cursor, buf: &[u8]) -> io::Result; - fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result; - fn is_write_vectored(this: &Cursor) -> bool; - fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()>; - fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()>; - fn flush(this: &mut Cursor) -> io::Result<()>; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Cursor { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - WriteThroughCursor::write(self, buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - WriteThroughCursor::write_vectored(self, bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - WriteThroughCursor::is_write_vectored(self) - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - WriteThroughCursor::write_all(self, buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - WriteThroughCursor::write_all_vectored(self, bufs) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - WriteThroughCursor::flush(self) - } -} - -// Non-resizing write implementation -#[inline] -fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result { - let pos = cmp::min(*pos_mut, slice.len() as u64); - let amt = (&mut slice[(pos as usize)..]).write(buf)?; - *pos_mut += amt as u64; - Ok(amt) -} - -#[inline] -fn slice_write_vectored( - pos_mut: &mut u64, - slice: &mut [u8], - bufs: &[IoSlice<'_>], -) -> io::Result { - let mut nwritten = 0; - for buf in bufs { - let n = slice_write(pos_mut, slice, buf)?; - nwritten += n; - if n < buf.len() { - break; - } - } - Ok(nwritten) -} - -#[inline] -fn slice_write_all(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<()> { - let n = slice_write(pos_mut, slice, buf)?; - if n < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } -} - -#[inline] -fn slice_write_all_vectored( - pos_mut: &mut u64, - slice: &mut [u8], - bufs: &[IoSlice<'_>], -) -> io::Result<()> { - for buf in bufs { - let n = slice_write(pos_mut, slice, buf)?; - if n < buf.len() { - return Err(io::Error::WRITE_ALL_EOF); - } - } - Ok(()) -} - -/// Reserves the required space, and pads the vec with 0s if necessary. -fn reserve_and_pad( - pos_mut: &mut u64, - vec: &mut Vec, - buf_len: usize, -) -> io::Result { - let pos: usize = (*pos_mut).try_into().map_err(|_| { - io::const_error!( - ErrorKind::InvalidInput, - "cursor position exceeds maximum possible vector length", - ) - })?; - - // For safety reasons, we don't want these numbers to overflow - // otherwise our allocation won't be enough - let desired_cap = pos.saturating_add(buf_len); - if desired_cap > vec.capacity() { - // We want our vec's total capacity - // to have room for (pos+buf_len) bytes. Reserve allocates - // based on additional elements from the length, so we need to - // reserve the difference - vec.reserve(desired_cap - vec.len()); - } - // Pad if pos is above the current len. - if pos > vec.len() { - let diff = pos - vec.len(); - // Unfortunately, `resize()` would suffice but the optimiser does not - // realise the `reserve` it does can be eliminated. So we do it manually - // to eliminate that extra branch - let spare = vec.spare_capacity_mut(); - debug_assert!(spare.len() >= diff); - // Safety: we have allocated enough capacity for this. - // And we are only writing, not reading - unsafe { - spare.get_unchecked_mut(..diff).fill(core::mem::MaybeUninit::new(0)); - vec.set_len(pos); - } - } - - Ok(pos) -} - -/// Writes the slice to the vec without allocating. -/// -/// # Safety -/// -/// `vec` must have `buf.len()` spare capacity. -unsafe fn vec_write_all_unchecked(pos: usize, vec: &mut Vec, buf: &[u8]) -> usize -where - A: Allocator, -{ - debug_assert!(vec.capacity() >= pos + buf.len()); - unsafe { vec.as_mut_ptr().add(pos).copy_from(buf.as_ptr(), buf.len()) }; - pos + buf.len() -} - -/// Resizing `write_all` implementation for [`Cursor`]. -/// -/// Cursor is allowed to have a pre-allocated and initialised -/// vector body, but with a position of 0. This means the [`Write`] -/// will overwrite the contents of the vec. -/// -/// This also allows for the vec body to be empty, but with a position of N. -/// This means that [`Write`] will pad the vec with 0 initially, -/// before writing anything from that point -fn vec_write_all(pos_mut: &mut u64, vec: &mut Vec, buf: &[u8]) -> io::Result -where - A: Allocator, -{ - let buf_len = buf.len(); - let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?; - - // Write the buf then progress the vec forward if necessary - // Safety: we have ensured that the capacity is available - // and that all bytes get written up to pos - unsafe { - pos = vec_write_all_unchecked(pos, vec, buf); - if pos > vec.len() { - vec.set_len(pos); - } - }; - - // Bump us forward - *pos_mut += buf_len as u64; - Ok(buf_len) -} - -/// Resizing `write_all_vectored` implementation for [`Cursor`]. -/// -/// Cursor is allowed to have a pre-allocated and initialised -/// vector body, but with a position of 0. This means the [`Write`] -/// will overwrite the contents of the vec. -/// -/// This also allows for the vec body to be empty, but with a position of N. -/// This means that [`Write`] will pad the vec with 0 initially, -/// before writing anything from that point -fn vec_write_all_vectored( - pos_mut: &mut u64, - vec: &mut Vec, - bufs: &[IoSlice<'_>], -) -> io::Result -where - A: Allocator, -{ - // For safety reasons, we don't want this sum to overflow ever. - // If this saturates, the reserve should panic to avoid any unsound writing. - let buf_len = bufs.iter().fold(0usize, |a, b| a.saturating_add(b.len())); - let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?; - - // Write the buf then progress the vec forward if necessary - // Safety: we have ensured that the capacity is available - // and that all bytes get written up to the last pos - unsafe { - for buf in bufs { - pos = vec_write_all_unchecked(pos, vec, buf); - } - if pos > vec.len() { - vec.set_len(pos); - } - } - - // Bump us forward - *pos_mut += buf_len as u64; - Ok(buf_len) -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Cursor<&mut [u8]> { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); - slice_write(pos, inner, buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); - slice_write_vectored(pos, inner, bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); - slice_write_all(pos, inner, buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); - slice_write_all_vectored(pos, inner, bufs) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "cursor_mut_vec", since = "1.25.0")] -impl WriteThroughCursor for &mut Vec -where - A: Allocator, -{ - fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { - let (pos, inner) = this.into_parts_mut(); - vec_write_all(pos, inner, buf) - } - - fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = this.into_parts_mut(); - vec_write_all_vectored(pos, inner, bufs) - } - - #[inline] - fn is_write_vectored(_this: &Cursor) -> bool { - true - } - - fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = this.into_parts_mut(); - vec_write_all(pos, inner, buf)?; - Ok(()) - } - - fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = this.into_parts_mut(); - vec_write_all_vectored(pos, inner, bufs)?; - Ok(()) - } - - #[inline] - fn flush(_this: &mut Cursor) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl WriteThroughCursor for Vec -where - A: Allocator, -{ - fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { - let (pos, inner) = this.into_parts_mut(); - vec_write_all(pos, inner, buf) - } - - fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = this.into_parts_mut(); - vec_write_all_vectored(pos, inner, bufs) - } - - #[inline] - fn is_write_vectored(_this: &Cursor) -> bool { - true - } - - fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = this.into_parts_mut(); - vec_write_all(pos, inner, buf)?; - Ok(()) - } - - fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = this.into_parts_mut(); - vec_write_all_vectored(pos, inner, bufs)?; - Ok(()) - } - - #[inline] - fn flush(_this: &mut Cursor) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "cursor_box_slice", since = "1.5.0")] -impl WriteThroughCursor for Box<[u8], A> -where - A: Allocator, -{ - #[inline] - fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { - let (pos, inner) = this.into_parts_mut(); - slice_write(pos, inner, buf) - } - - #[inline] - fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = this.into_parts_mut(); - slice_write_vectored(pos, inner, bufs) - } - - #[inline] - fn is_write_vectored(_this: &Cursor) -> bool { - true - } - - #[inline] - fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = this.into_parts_mut(); - slice_write_all(pos, inner, buf) - } - - #[inline] - fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = this.into_parts_mut(); - slice_write_all_vectored(pos, inner, bufs) - } - - #[inline] - fn flush(_this: &mut Cursor) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "cursor_array", since = "1.61.0")] -impl Write for Cursor<[u8; N]> { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); - slice_write(pos, inner, buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); - slice_write_vectored(pos, inner, bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); - slice_write_all(pos, inner, buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); - slice_write_all_vectored(pos, inner, bufs) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index 08bb34376b075..61f82828149aa 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -3,9 +3,9 @@ mod tests; use crate::alloc::Allocator; use crate::collections::VecDeque; -use crate::io::{self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Write}; +use crate::io::{self, BorrowedCursor, BufRead, IoSliceMut, Read}; use crate::sync::Arc; -use crate::{cmp, fmt, mem, str}; +use crate::{cmp, str}; // ============================================================================= // Forwarding implementations @@ -53,43 +53,6 @@ impl Read for &mut R { } } #[stable(feature = "rust1", since = "1.0.0")] -impl Write for &mut W { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - (**self).write(buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - (**self).write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - (**self).is_write_vectored() - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - (**self).flush() - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - (**self).write_all(buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - (**self).write_all_vectored(bufs) - } - - #[inline] - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - (**self).write_fmt(fmt) - } -} -#[stable(feature = "rust1", since = "1.0.0")] impl BufRead for &mut B { #[inline] fn fill_buf(&mut self) -> io::Result<&[u8]> { @@ -165,43 +128,6 @@ impl Read for Box { } } #[stable(feature = "rust1", since = "1.0.0")] -impl Write for Box { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - (**self).write(buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - (**self).write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - (**self).is_write_vectored() - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - (**self).flush() - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - (**self).write_all(buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - (**self).write_all_vectored(bufs) - } - - #[inline] - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - (**self).write_fmt(fmt) - } -} -#[stable(feature = "rust1", since = "1.0.0")] impl BufRead for Box { #[inline] fn fill_buf(&mut self) -> io::Result<&[u8]> { @@ -362,108 +288,6 @@ impl BufRead for &[u8] { } } -/// Write is implemented for `&mut [u8]` by copying into the slice, overwriting -/// its data. -/// -/// Note that writing updates the slice to point to the yet unwritten part. -/// The slice will be empty when it has been completely overwritten. -/// -/// If the number of bytes to be written exceeds the size of the slice, write operations will -/// return short writes: ultimately, `Ok(0)`; in this situation, `write_all` returns an error of -/// kind `ErrorKind::WriteZero`. -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for &mut [u8] { - #[inline] - fn write(&mut self, data: &[u8]) -> io::Result { - let amt = cmp::min(data.len(), self.len()); - let (a, b) = mem::take(self).split_at_mut(amt); - a.copy_from_slice(&data[..amt]); - *self = b; - Ok(amt) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let mut nwritten = 0; - for buf in bufs { - nwritten += self.write(buf)?; - if self.is_empty() { - break; - } - } - - Ok(nwritten) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, data: &[u8]) -> io::Result<()> { - if self.write(data)? < data.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - for buf in bufs { - if self.write(buf)? < buf.len() { - return Err(io::Error::WRITE_ALL_EOF); - } - } - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -/// Write is implemented for `Vec` by appending to the vector. -/// The vector will grow as needed. -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Vec { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - self.extend_from_slice(buf); - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let len = bufs.iter().map(|b| b.len()).sum(); - self.reserve(len); - for buf in bufs { - self.extend_from_slice(buf); - } - Ok(len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.extend_from_slice(buf); - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - self.write_vectored(bufs)?; - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - /// Read is implemented for `VecDeque` by consuming bytes from the front of the `VecDeque`. #[stable(feature = "vecdeque_read_write", since = "1.63.0")] impl Read for VecDeque { @@ -573,96 +397,6 @@ impl BufRead for VecDeque { } } -/// Write is implemented for `VecDeque` by appending to the `VecDeque`, growing it as needed. -#[stable(feature = "vecdeque_read_write", since = "1.63.0")] -impl Write for VecDeque { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - self.extend(buf); - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let len = bufs.iter().map(|b| b.len()).sum(); - self.reserve(len); - for buf in bufs { - self.extend(&**buf); - } - Ok(len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.extend(buf); - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - self.write_vectored(bufs)?; - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[unstable(feature = "read_buf", issue = "78485")] -impl<'a> io::Write for core::io::BorrowedCursor<'a, u8> { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - let amt = cmp::min(buf.len(), self.capacity()); - self.append(&buf[..amt]); - Ok(amt) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let mut nwritten = 0; - for buf in bufs { - let n = self.write(buf)?; - nwritten += n; - if n < buf.len() { - break; - } - } - Ok(nwritten) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - if self.write(buf)? < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - for buf in bufs { - if self.write(buf)? < buf.len() { - return Err(io::Error::WRITE_ALL_EOF); - } - } - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - #[stable(feature = "io_traits_arc", since = "1.73.0")] impl Read for Arc where @@ -709,44 +443,3 @@ where (&**self).read_buf_exact(cursor) } } -#[stable(feature = "io_traits_arc", since = "1.73.0")] -impl Write for Arc -where - for<'a> &'a W: Write, - W: crate::io::IoHandle, -{ - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - (&**self).write(buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - (&**self).write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - (&**self).is_write_vectored() - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - (&**self).flush() - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - (&**self).write_all(buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - (&**self).write_all_vectored(bufs) - } - - #[inline] - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - (&**self).write_fmt(fmt) - } -} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 38c8120d66fc6..b1dbcf1f5bd46 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -306,11 +306,14 @@ pub use alloc_crate::io::RawOsError; pub use alloc_crate::io::SimpleMessage; #[unstable(feature = "io_const_error", issue = "133448")] pub use alloc_crate::io::const_error; +#[allow(unused_imports, reason = "only used by certain platforms")] +pub(crate) use alloc_crate::io::default_write_vectored; #[unstable(feature = "read_buf", issue = "78485")] pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::io::{ - Chain, Empty, Error, ErrorKind, Repeat, Result, Seek, SeekFrom, Sink, Take, empty, repeat, sink, + Chain, Empty, Error, ErrorKind, Repeat, Result, Seek, SeekFrom, Sink, Take, Write, empty, + repeat, sink, }; pub(crate) use alloc_crate::io::{IoHandle, stream_len_default}; #[stable(feature = "iovec", since = "1.36.0")] @@ -338,7 +341,7 @@ pub use self::{ stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, }; use crate::mem::MaybeUninit; -use crate::{cmp, fmt, slice, str}; +use crate::{cmp, slice, str}; mod buffered; pub(crate) mod copy; @@ -549,14 +552,6 @@ where read(buf) } -pub(crate) fn default_write_vectored(write: F, bufs: &[IoSlice<'_>]) -> Result -where - F: FnOnce(&[u8]) -> Result, -{ - let buf = bufs.iter().find(|b| !b.is_empty()).map_or(&[][..], |b| &**b); - write(buf) -} - pub(crate) fn default_read_exact(this: &mut R, mut buf: &mut [u8]) -> Result<()> { while !buf.is_empty() { match this.read(buf) { @@ -600,47 +595,6 @@ pub(crate) fn default_read_buf_exact( Ok(()) } -pub(crate) fn default_write_fmt( - this: &mut W, - args: fmt::Arguments<'_>, -) -> Result<()> { - // Create a shim which translates a `Write` to a `fmt::Write` and saves off - // I/O errors, instead of discarding them. - struct Adapter<'a, T: ?Sized + 'a> { - inner: &'a mut T, - error: Result<()>, - } - - impl fmt::Write for Adapter<'_, T> { - fn write_str(&mut self, s: &str) -> fmt::Result { - match self.inner.write_all(s.as_bytes()) { - Ok(()) => Ok(()), - Err(e) => { - self.error = Err(e); - Err(fmt::Error) - } - } - } - } - - let mut output = Adapter { inner: this, error: Ok(()) }; - match fmt::write(&mut output, args) { - Ok(()) => Ok(()), - Err(..) => { - // Check whether the error came from the underlying `Write`. - if output.error.is_err() { - output.error - } else { - // This shouldn't happen: the underlying stream did not error, - // but somehow the formatter still errored? - panic!( - "a formatting trait implementation returned an error when the underlying stream did not" - ); - } - } - } -} - /// The `Read` trait allows for reading bytes from a source. /// /// Implementors of the `Read` trait are called 'readers'. @@ -1403,365 +1357,6 @@ pub fn read_to_string(mut reader: R) -> Result { Ok(buf) } -/// A trait for objects which are byte-oriented sinks. -/// -/// Implementors of the `Write` trait are sometimes called 'writers'. -/// -/// Writers are defined by two required methods, [`write`] and [`flush`]: -/// -/// * The [`write`] method will attempt to write some data into the object, -/// returning how many bytes were successfully written. -/// -/// * The [`flush`] method is useful for adapters and explicit buffers -/// themselves for ensuring that all buffered data has been pushed out to the -/// 'true sink'. -/// -/// Writers are intended to be composable with one another. Many implementors -/// throughout [`std::io`] take and provide types which implement the `Write` -/// trait. -/// -/// [`write`]: Write::write -/// [`flush`]: Write::flush -/// [`std::io`]: self -/// -/// # Examples -/// -/// ```no_run -/// use std::io::prelude::*; -/// use std::fs::File; -/// -/// fn main() -> std::io::Result<()> { -/// let data = b"some bytes"; -/// -/// let mut pos = 0; -/// let mut buffer = File::create("foo.txt")?; -/// -/// while pos < data.len() { -/// let bytes_written = buffer.write(&data[pos..])?; -/// pos += bytes_written; -/// } -/// Ok(()) -/// } -/// ``` -/// -/// The trait also provides convenience methods like [`write_all`], which calls -/// `write` in a loop until its entire input has been written. -/// -/// [`write_all`]: Write::write_all -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(notable_trait)] -#[cfg_attr(not(test), rustc_diagnostic_item = "IoWrite")] -pub trait Write { - /// Writes a buffer into this writer, returning how many bytes were written. - /// - /// This function will attempt to write the entire contents of `buf`, but - /// the entire write might not succeed, or the write may also generate an - /// error. Typically, a call to `write` represents one attempt to write to - /// any wrapped object. - /// - /// Calls to `write` are not guaranteed to block waiting for data to be - /// written, and a write which would otherwise block can be indicated through - /// an [`Err`] variant. - /// - /// If this method consumed `n > 0` bytes of `buf` it must return [`Ok(n)`]. - /// If the return value is `Ok(n)` then `n` must satisfy `n <= buf.len()`. - /// A return value of `Ok(0)` typically means that the underlying object is - /// no longer able to accept bytes and will likely not be able to in the - /// future as well, or that the buffer provided is empty. - /// - /// # Errors - /// - /// Each call to `write` may generate an I/O error indicating that the - /// operation could not be completed. If an error is returned then no bytes - /// in the buffer were written to this writer. - /// - /// It is **not** considered an error if the entire buffer could not be - /// written to this writer. - /// - /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the - /// write operation should be retried if there is nothing else to do. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// // Writes some prefix of the byte string, not necessarily all of it. - /// buffer.write(b"some bytes")?; - /// Ok(()) - /// } - /// ``` - /// - /// [`Ok(n)`]: Ok - #[stable(feature = "rust1", since = "1.0.0")] - fn write(&mut self, buf: &[u8]) -> Result; - - /// Like [`write`], except that it writes from a slice of buffers. - /// - /// Data is copied from each buffer in order, with the final buffer - /// read from possibly being only partially consumed. This method must - /// behave as a call to [`write`] with the buffers concatenated would. - /// - /// The default implementation calls [`write`] with either the first nonempty - /// buffer provided, or an empty one if none exists. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::IoSlice; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let data1 = [1; 8]; - /// let data2 = [15; 8]; - /// let io_slice1 = IoSlice::new(&data1); - /// let io_slice2 = IoSlice::new(&data2); - /// - /// let mut buffer = File::create("foo.txt")?; - /// - /// // Writes some prefix of the byte string, not necessarily all of it. - /// buffer.write_vectored(&[io_slice1, io_slice2])?; - /// Ok(()) - /// } - /// ``` - /// - /// [`write`]: Write::write - #[stable(feature = "iovec", since = "1.36.0")] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result { - default_write_vectored(|b| self.write(b), bufs) - } - - /// Determines if this `Write`r has an efficient [`write_vectored`] - /// implementation. - /// - /// If a `Write`r does not override the default [`write_vectored`] - /// implementation, code using it may want to avoid the method all together - /// and coalesce writes into a single buffer for higher performance. - /// - /// The default implementation returns `false`. - /// - /// [`write_vectored`]: Write::write_vectored - #[unstable(feature = "can_vector", issue = "69941")] - fn is_write_vectored(&self) -> bool { - false - } - - /// Flushes this output stream, ensuring that all intermediately buffered - /// contents reach their destination. - /// - /// # Errors - /// - /// It is considered an error if not all bytes could be written due to - /// I/O errors or EOF being reached. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::prelude::*; - /// use std::io::BufWriter; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = BufWriter::new(File::create("foo.txt")?); - /// - /// buffer.write_all(b"some bytes")?; - /// buffer.flush()?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn flush(&mut self) -> Result<()>; - - /// Attempts to write an entire buffer into this writer. - /// - /// This method will continuously call [`write`] until there is no more data - /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is - /// returned. This method will not return until the entire buffer has been - /// successfully written or such an error occurs. The first error that is - /// not of [`ErrorKind::Interrupted`] kind generated from this method will be - /// returned. - /// - /// If the buffer contains no data, this will never call [`write`]. - /// - /// # Errors - /// - /// This function will return the first error of - /// non-[`ErrorKind::Interrupted`] kind that [`write`] returns. - /// - /// [`write`]: Write::write - /// - /// # Examples - /// - /// ```no_run - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// buffer.write_all(b"some bytes")?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn write_all(&mut self, mut buf: &[u8]) -> Result<()> { - while !buf.is_empty() { - match self.write(buf) { - Ok(0) => { - return Err(Error::WRITE_ALL_EOF); - } - Ok(n) => buf = &buf[n..], - Err(ref e) if e.is_interrupted() => {} - Err(e) => return Err(e), - } - } - Ok(()) - } - - /// Attempts to write multiple buffers into this writer. - /// - /// This method will continuously call [`write_vectored`] until there is no - /// more data to be written or an error of non-[`ErrorKind::Interrupted`] - /// kind is returned. This method will not return until all buffers have - /// been successfully written or such an error occurs. The first error that - /// is not of [`ErrorKind::Interrupted`] kind generated from this method - /// will be returned. - /// - /// If the buffer contains no data, this will never call [`write_vectored`]. - /// - /// # Notes - /// - /// Unlike [`write_vectored`], this takes a *mutable* reference to - /// a slice of [`IoSlice`]s, not an immutable one. That's because we need to - /// modify the slice to keep track of the bytes already written. - /// - /// Once this function returns, the contents of `bufs` are unspecified, as - /// this depends on how many calls to [`write_vectored`] were necessary. It is - /// best to understand this function as taking ownership of `bufs` and to - /// not use `bufs` afterwards. The underlying buffers, to which the - /// [`IoSlice`]s point (but not the [`IoSlice`]s themselves), are unchanged and - /// can be reused. - /// - /// [`write_vectored`]: Write::write_vectored - /// - /// # Examples - /// - /// ``` - /// #![feature(write_all_vectored)] - /// # fn main() -> std::io::Result<()> { - /// - /// use std::io::{Write, IoSlice}; - /// - /// let mut writer = Vec::new(); - /// let bufs = &mut [ - /// IoSlice::new(&[1]), - /// IoSlice::new(&[2, 3]), - /// IoSlice::new(&[4, 5, 6]), - /// ]; - /// - /// writer.write_all_vectored(bufs)?; - /// // Note: the contents of `bufs` is now undefined, see the Notes section. - /// - /// assert_eq!(writer, &[1, 2, 3, 4, 5, 6]); - /// # Ok(()) } - /// ``` - #[unstable(feature = "write_all_vectored", issue = "70436")] - fn write_all_vectored(&mut self, mut bufs: &mut [IoSlice<'_>]) -> Result<()> { - // Guarantee that bufs is empty if it contains no data, - // to avoid calling write_vectored if there is no data to be written. - IoSlice::advance_slices(&mut bufs, 0); - while !bufs.is_empty() { - match self.write_vectored(bufs) { - Ok(0) => { - return Err(Error::WRITE_ALL_EOF); - } - Ok(n) => IoSlice::advance_slices(&mut bufs, n), - Err(ref e) if e.is_interrupted() => {} - Err(e) => return Err(e), - } - } - Ok(()) - } - - /// Writes a formatted string into this writer, returning any error - /// encountered. - /// - /// This method is primarily used to interface with the - /// [`format_args!()`] macro, and it is rare that this should - /// explicitly be called. The [`write!()`] macro should be favored to - /// invoke this method instead. - /// - /// This function internally uses the [`write_all`] method on - /// this trait and hence will continuously write data so long as no errors - /// are received. This also means that partial writes are not indicated in - /// this signature. - /// - /// [`write_all`]: Write::write_all - /// - /// # Errors - /// - /// This function will return any I/O error reported while formatting. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// // this call - /// write!(buffer, "{:.*}", 2, 1.234567)?; - /// // turns into this: - /// buffer.write_fmt(format_args!("{:.*}", 2, 1.234567))?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<()> { - if let Some(s) = args.as_statically_known_str() { - self.write_all(s.as_bytes()) - } else { - default_write_fmt(self, args) - } - } - - /// Creates a "by reference" adapter for this instance of `Write`. - /// - /// The returned adapter also implements `Write` and will simply borrow this - /// current writer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::Write; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// let reference = buffer.by_ref(); - /// - /// // we can use reference just like our original buffer - /// reference.write_all(b"some bytes")?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn by_ref(&mut self) -> &mut Self - where - Self: Sized, - { - self - } -} - fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { let mut read = 0; loop { diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index ce2137f9567c7..ac3e0f84d1e80 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -3,10 +3,7 @@ #[cfg(test)] mod tests; -use crate::fmt; -use crate::io::{ - self, BorrowedCursor, BufRead, Empty, IoSlice, IoSliceMut, Read, Repeat, Sink, Write, -}; +use crate::io::{self, BorrowedCursor, BufRead, Empty, IoSliceMut, Read, Repeat}; #[stable(feature = "rust1", since = "1.0.0")] impl Read for Empty { @@ -83,84 +80,6 @@ impl BufRead for Empty { } } -#[stable(feature = "empty_write", since = "1.73.0")] -impl Write for Empty { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum(); - Ok(total_len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "empty_write", since = "1.73.0")] -impl Write for &Empty { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum(); - Ok(total_len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - #[stable(feature = "rust1", since = "1.0.0")] impl Read for Repeat { #[inline] @@ -213,81 +132,3 @@ impl Read for Repeat { true } } - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Sink { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum(); - Ok(total_len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "write_mt", since = "1.48.0")] -impl Write for &Sink { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum(); - Ok(total_len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index db37df63c5763..7cf576ecd8803 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -322,6 +322,7 @@ #![feature(borrowed_buf_init)] #![feature(bstr)] #![feature(bstr_internals)] +#![feature(can_vector)] #![feature(cast_maybe_uninit)] #![feature(char_internals)] #![feature(clone_to_uninit)] @@ -389,6 +390,7 @@ #![feature(ub_checks)] #![feature(uint_carryless_mul)] #![feature(used_with_arg)] +#![feature(write_all_vectored)] // tidy-alphabetical-end // // Library features (alloc): diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs index ca25c2b132206..c8aff10b6355b 100644 --- a/src/bootstrap/src/bin/rustc.rs +++ b/src/bootstrap/src/bin/rustc.rs @@ -22,8 +22,8 @@ use std::time::Instant; use arg_file_command::ArgFileCommand; use shared_helpers::{ - dylib_path, dylib_path_var, exe, maybe_dump, parse_rustc_stage, parse_rustc_verbose, - parse_value_from_args, + collect_args, dylib_path, dylib_path_var, exe, maybe_dump, parse_rustc_stage, + parse_rustc_verbose, parse_value_from_args, }; #[path = "../utils/shared_helpers.rs"] @@ -36,7 +36,7 @@ mod arg_file_command; mod proc_macro_deps; fn main() { - let orig_args = env::args_os().skip(1).collect::>(); + let orig_args = collect_args(); let mut args = orig_args.clone(); let stage = parse_rustc_stage(); diff --git a/src/bootstrap/src/bin/rustdoc.rs b/src/bootstrap/src/bin/rustdoc.rs index ef2462af952e1..fd048d138e090 100644 --- a/src/bootstrap/src/bin/rustdoc.rs +++ b/src/bootstrap/src/bin/rustdoc.rs @@ -7,7 +7,7 @@ use std::path::PathBuf; use arg_file_command::ArgFileCommand; use shared_helpers::{ - dylib_path, dylib_path_var, maybe_dump, parse_rustc_stage, parse_rustc_verbose, + collect_args, dylib_path, dylib_path_var, maybe_dump, parse_rustc_stage, parse_rustc_verbose, parse_value_from_args, }; @@ -18,7 +18,7 @@ mod shared_helpers; mod arg_file_command; fn main() { - let args = env::args_os().skip(1).collect::>(); + let args = collect_args(); let stage = parse_rustc_stage(); let verbose = parse_rustc_verbose(); diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index db740d8dcf624..b665891c9a206 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -96,7 +96,11 @@ pub fn is_dylib(path: &Path) -> bool { /// Return the path to the containing submodule if available. pub fn submodule_path_of(builder: &Builder<'_>, path: &str) -> Option { - let submodule_paths = builder.submodule_paths(); + submodule_path_of_paths(builder.submodule_paths(), path) +} + +fn submodule_path_of_paths(submodule_paths: &[String], path: &str) -> Option { + let path = Path::new(path); submodule_paths.iter().find_map(|submodule_path| { if path.starts_with(submodule_path) { Some(submodule_path.to_string()) } else { None } }) diff --git a/src/bootstrap/src/utils/helpers/tests.rs b/src/bootstrap/src/utils/helpers/tests.rs index 676fe6cbd5fe5..b051a374a0866 100644 --- a/src/bootstrap/src/utils/helpers/tests.rs +++ b/src/bootstrap/src/utils/helpers/tests.rs @@ -3,7 +3,7 @@ use std::io::Write; use std::path::PathBuf; use crate::utils::helpers::{ - check_cfg_arg, extract_beta_rev, hex_encode, make, set_file_times, submodule_path_of, + check_cfg_arg, extract_beta_rev, hex_encode, make, set_file_times, submodule_path_of_paths, symlink_dir, }; use crate::utils::tests::TestCtx; @@ -101,19 +101,22 @@ fn test_set_file_times_sanity_check() { #[test] fn test_submodule_path_of() { - let config = TestCtx::new().config("build").create_config(); + let submodules = vec!["src/tools/cargo".to_string(), "src/llvm-project".to_string()]; - let build = crate::Build::new(config.clone()); - let builder = crate::core::builder::Builder::new(&build); - assert_eq!(submodule_path_of(&builder, "invalid/path"), None); - assert_eq!(submodule_path_of(&builder, "src/tools/cargo"), Some("src/tools/cargo".to_string())); + assert_eq!(submodule_path_of_paths(&submodules, "invalid/path"), None); assert_eq!( - submodule_path_of(&builder, "src/llvm-project"), + submodule_path_of_paths(&submodules, "src/tools/cargo"), + Some("src/tools/cargo".to_string()) + ); + assert_eq!( + submodule_path_of_paths(&submodules, "src/llvm-project"), Some("src/llvm-project".to_string()) ); // Make sure subdirs are handled properly assert_eq!( - submodule_path_of(&builder, "src/tools/cargo/random-subdir"), + submodule_path_of_paths(&submodules, "src/tools/cargo/random-subdir"), Some("src/tools/cargo".to_string()) ); + // Make sure paths that only share a string prefix with a submodule are not matched. + assert_eq!(submodule_path_of_paths(&submodules, "src/tools/cargo-vendor"), None); } diff --git a/src/bootstrap/src/utils/shared_helpers.rs b/src/bootstrap/src/utils/shared_helpers.rs index d620cc4bbb6b4..78af70ddb0f42 100644 --- a/src/bootstrap/src/utils/shared_helpers.rs +++ b/src/bootstrap/src/utils/shared_helpers.rs @@ -16,7 +16,8 @@ use std::env; use std::ffi::OsString; use std::fs::OpenOptions; -use std::io::Write; +use std::io::{BufRead, Write}; +use std::path::Path; use std::process::Command; use std::str::FromStr; @@ -126,3 +127,30 @@ pub fn parse_value_from_args<'a>(args: &'a [OsString], key: &str) -> Option<&'a None } + +/// Collect all the command line arguments, including the arguments from any `@argfile` +pub fn collect_args() -> Vec { + let mut args = Vec::with_capacity(env::args_os().len()); + for arg in env::args_os().skip(1) { + if let Some(s) = arg.to_str() + && let Some(path) = s.strip_prefix('@') + { + args.extend(args_from_argfile(Path::new(path))); + } else { + args.push(arg) + } + } + args +} + +/// Reads all the arguments from argfile given by `path`. +/// Each argument should be on a line by itself +fn args_from_argfile(path: &Path) -> Vec { + fn collect_lines(path: &Path) -> Result, std::io::Error> { + let file = std::fs::File::open(path)?; + let lines: Result, std::io::Error> = + std::io::BufReader::new(file).lines().map(|r| r.map(OsString::from)).collect(); + lines + } + collect_lines(path).expect("read args from argfile {path:?}") +} diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh b/src/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh index c2a5b308b3284..0ccd364be21bb 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh @@ -15,14 +15,6 @@ if [ -z "${PR_CI_JOB:-}" ]; then else python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri fi -# We re-run the test suite for a chance to find bugs in the intrinsic fallback bodies and in MIR -# optimizations. This can miss UB, so we only run the "pass" tests. We need to enable debug -# assertions as `-O` disables them but some tests rely on them. We also set a cfg flag so tests can -# adjust their expectations if needed. This can change the output of the tests so we ignore that, -# we only ensure that all assertions still pass. -MIRIFLAGS="-Zmiri-force-intrinsic-fallback --cfg force_intrinsic_fallback -O -Zmir-opt-level=4 -Cdebug-assertions=yes" \ - MIRI_SKIP_UI_CHECKS=1 \ - python3 "$X_PY" test --stage 2 src/tools/miri -- tests/pass tests/panic # We natively run this script on x86_64-unknown-linux-gnu and x86_64-pc-windows-msvc. # Also cover some other targets via cross-testing, in particular all tier 1 targets. case $HOST_TARGET in diff --git a/src/doc/unstable-book/src/compiler-flags/ls.md b/src/doc/unstable-book/src/compiler-flags/ls.md new file mode 100644 index 0000000000000..812f2eee1d57f --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/ls.md @@ -0,0 +1,25 @@ +# `ls` + +--- + +Option `-Zls` instructs the compiler to list all metadata from a given metadata file (i.e. files with the `.rmeta` extension). + +This allows for debugging the metadata emitted by an earlier compilation. + +Note that, while `rustc` usually works with `.rs` files, this option is meant purely for analyzing `.rmeta` files, and does not produce any compilation artifact. + +Allowed values are: + +- `root`: Crate info. +- `lang_items`: Language items used and missing, if any. +- `features`: Library features defined via the `#[stable]` and `#[unstable]` internal attributes. +- `items`: All items (such as modules, functions...) in the crate, including attributes like their visibility +- `all`: All of the above + +## Example + +```sh +rustc +nightly -Zls=all target/debug/deps/libmy_crate-*.rmeta +``` + +This lists to stdout all metadata from the given `.rmeta` file diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 3179bda868651..c7387bf157d10 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1953,6 +1953,10 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T TyKind::UnsafeBinder(unsafe_binder_ty) => { UnsafeBinder(Box::new(clean_unsafe_binder_ty(unsafe_binder_ty, cx))) } + TyKind::View(ty, _) => { + // FIXME(scrabsha): propagate view types to `rustdoc`. + clean_ty(ty, cx) + } // Rustdoc handles `TyKind::Err`s by turning them into `Type::Infer`s. TyKind::Infer(()) | TyKind::Err(_) diff --git a/src/librustdoc/passes/lint/redundant_explicit_links.rs b/src/librustdoc/passes/lint/redundant_explicit_links.rs index 8da21f100c6a3..cd7b7caac69ad 100644 --- a/src/librustdoc/passes/lint/redundant_explicit_links.rs +++ b/src/librustdoc/passes/lint/redundant_explicit_links.rs @@ -80,7 +80,7 @@ fn check_redundant_explicit_link<'md>( hir_id: HirId, doc: &'md str, resolutions: &DocLinkResMap, -) -> Option<()> { +) { let mut broken_line_callback = |link: BrokenLink<'md>| Some((link.reference, "".into())); let mut offset_iter = Parser::new_with_broken_link_callback( doc, @@ -115,7 +115,7 @@ fn check_redundant_explicit_link<'md>( } if dest_url.ends_with(resolvable_link) || resolvable_link.ends_with(&*dest_url) { - match link_type { + let check_result = match link_type { LinkType::Inline | LinkType::ReferenceUnknown => { check_inline_or_reference_unknown_redundancy( cx, @@ -127,27 +127,52 @@ fn check_redundant_explicit_link<'md>( dest_url.to_string(), link_data, if link_type == LinkType::Inline { (b'(', b')') } else { (b'[', b']') }, - ); - } - LinkType::Reference => { - check_reference_redundancy( - cx, - item, - hir_id, - doc, - resolutions, - link_range, - &dest_url, - link_data, - ); + ) } - _ => {} + LinkType::Reference => check_reference_redundancy( + cx, + item, + hir_id, + doc, + resolutions, + link_range, + &dest_url, + link_data, + ), + _ => Ok(()), + }; + if let Err(lint) = check_result { + cx.tcx.emit_node_span_lint( + crate::lint::REDUNDANT_EXPLICIT_LINKS, + hir_id, + item.attr_span(cx.tcx), + lint, + ); } } } } +} - None +struct RedundantExplicitLinksWithoutSuggestion { + attr_span: Span, + display_link: String, + dest_link: String, +} + +impl<'a> Diagnostic<'a, ()> for RedundantExplicitLinksWithoutSuggestion { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { attr_span, display_link, dest_link } = self; + + Diag::new(dcx, level, "redundant explicit link target") + .with_span_label( + attr_span, + format!("explicit target `{dest_link}` is redundant because label `{display_link}` resolves to same destination") + ) + .with_note( + "when a link's destination is not specified,\nthe label is used to resolve intra-doc links" + ) + } } /// FIXME(ChAoSUnItY): Too many arguments. @@ -161,7 +186,7 @@ fn check_inline_or_reference_unknown_redundancy( dest: String, link_data: LinkData, (open, close): (u8, u8), -) -> Option<()> { +) -> Result<(), RedundantExplicitLinksWithoutSuggestion> { struct RedundantExplicitLinks { explicit_span: Span, display_span: Span, @@ -196,42 +221,65 @@ fn check_inline_or_reference_unknown_redundancy( } } - let (resolvable_link, resolvable_link_range) = - (&link_data.resolvable_link?, &link_data.resolvable_link_range?); - let (dest_res, display_res) = - (find_resolution(resolutions, &dest)?, find_resolution(resolutions, resolvable_link)?); + let (Some(resolvable_link), Some(resolvable_link_range)) = + (&link_data.resolvable_link, &link_data.resolvable_link_range) + else { + return Ok(()); + }; + let (Some(dest_res), Some(display_res)) = + (find_resolution(resolutions, &dest), find_resolution(resolutions, resolvable_link)) + else { + return Ok(()); + }; if dest_res == display_res { + let attr_span = item.attr_span(cx.tcx); let link_span = match source_span_for_markdown_range(cx.tcx, doc, &link_range, &item.attrs.doc_strings) { Some((sp, from_expansion)) => { if from_expansion { - return None; + return Ok(()); } sp } - None => item.attr_span(cx.tcx), + None => attr_span, }; - let (explicit_span, false) = source_span_for_markdown_range( + let explicit_span = match source_span_for_markdown_range( cx.tcx, doc, &offset_explicit_range(doc, link_range, open, close), &item.attrs.doc_strings, - )? - else { + ) { + Some((explicit_span, false)) => explicit_span, // This `span` comes from macro expansion so skipping it. - return None; + Some((_, true)) => return Ok(()), + // Cannot give a contiguous span for this link. + None => { + return Err(RedundantExplicitLinksWithoutSuggestion { + display_link: resolvable_link.clone(), + dest_link: dest.to_string(), + attr_span, + }); + } }; - let (display_span, false) = source_span_for_markdown_range( + let display_span = match source_span_for_markdown_range( cx.tcx, doc, resolvable_link_range, &item.attrs.doc_strings, - )? - else { + ) { + Some((display_span, false)) => display_span, // This `span` comes from macro expansion so skipping it. - return None; + Some((_, true)) => return Ok(()), + // Cannot give a contiguous span for this link. + None => { + return Err(RedundantExplicitLinksWithoutSuggestion { + display_link: resolvable_link.clone(), + dest_link: dest.to_string(), + attr_span, + }); + } }; cx.tcx.emit_node_span_lint( @@ -247,7 +295,7 @@ fn check_inline_or_reference_unknown_redundancy( ); } - None + Ok(()) } /// FIXME(ChAoSUnItY): Too many arguments. @@ -260,7 +308,7 @@ fn check_reference_redundancy( link_range: Range, dest: &CowStr<'_>, link_data: LinkData, -) -> Option<()> { +) -> Result<(), RedundantExplicitLinksWithoutSuggestion> { struct RedundantExplicitLinkTarget { explicit_span: Span, display_span: Span, @@ -294,49 +342,83 @@ fn check_reference_redundancy( } } - let (resolvable_link, resolvable_link_range) = - (&link_data.resolvable_link?, &link_data.resolvable_link_range?); - let (dest_res, display_res) = - (find_resolution(resolutions, dest)?, find_resolution(resolutions, resolvable_link)?); + let (Some(resolvable_link), Some(resolvable_link_range)) = + (&link_data.resolvable_link, &link_data.resolvable_link_range) + else { + return Ok(()); + }; + let (Some(dest_res), Some(display_res)) = + (find_resolution(resolutions, dest), find_resolution(resolutions, resolvable_link)) + else { + return Ok(()); + }; if dest_res == display_res { + let attr_span = item.attr_span(cx.tcx); let link_span = match source_span_for_markdown_range(cx.tcx, doc, &link_range, &item.attrs.doc_strings) { Some((sp, from_expansion)) => { if from_expansion { - return None; + // This `span` comes from macro expansion so skipping it. + return Ok(()); } sp } - None => item.attr_span(cx.tcx), + None => attr_span, }; - let (explicit_span, false) = source_span_for_markdown_range( + let explicit_span = match source_span_for_markdown_range( cx.tcx, doc, &offset_explicit_range(doc, link_range.clone(), b'[', b']'), &item.attrs.doc_strings, - )? - else { + ) { + Some((explicit_span, false)) => explicit_span, // This `span` comes from macro expansion so skipping it. - return None; + Some((_, true)) => return Ok(()), + // Cannot give a contiguous span for this link. + None => { + return Err(RedundantExplicitLinksWithoutSuggestion { + display_link: resolvable_link.clone(), + dest_link: dest.to_string(), + attr_span, + }); + } }; - let (display_span, false) = source_span_for_markdown_range( + let display_span = match source_span_for_markdown_range( cx.tcx, doc, resolvable_link_range, &item.attrs.doc_strings, - )? - else { + ) { + Some((display_span, false)) => display_span, // This `span` comes from macro expansion so skipping it. - return None; + Some((_, true)) => return Ok(()), + // Cannot give a contiguous span for this link. + None => { + return Err(RedundantExplicitLinksWithoutSuggestion { + display_link: resolvable_link.clone(), + dest_link: dest.to_string(), + attr_span, + }); + } }; - let (def_span, _) = source_span_for_markdown_range( + let def_span = match source_span_for_markdown_range( cx.tcx, doc, &offset_reference_def_range(doc, dest, link_range), &item.attrs.doc_strings, - )?; + ) { + Some((def_span, _)) => def_span, + // Cannot give a contiguous span for this link. + None => { + return Err(RedundantExplicitLinksWithoutSuggestion { + display_link: resolvable_link.clone(), + dest_link: dest.to_string(), + attr_span, + }); + } + }; cx.tcx.emit_node_span_lint( crate::lint::REDUNDANT_EXPLICIT_LINKS, @@ -352,7 +434,7 @@ fn check_reference_redundancy( ); } - None + Ok(()) } fn find_resolution(resolutions: &DocLinkResMap, path: &str) -> Option> { diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index fbff41fe00914..0868ee322a646 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -902,6 +902,10 @@ impl TyCoercionStability { | TyKind::TraitObject(..) | TyKind::InferDelegation(..) | TyKind::Err(_) => Self::Reborrow, + TyKind::View(ty, _) => { + // FIXME(scrabsha): what are the semantics of view types here? + Self::for_hir_ty(ty) + } TyKind::UnsafeBinder(..) => Self::None, }; } diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index c5816cb8b2d63..013fb32bdaf3c 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -1242,7 +1242,8 @@ fn check_doc<'a, Events: Iterator, Range, Range (Pat, Pat) { | TyKind::Pat(..) | TyKind::FieldOf(..) | TyKind::View(..) + | TyKind::DirectConstArg(..) // unused | TyKind::CVarArgs diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 050e43344d0c4..7c3fa51228bc9 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1650,6 +1650,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { TyKind::UnsafeBinder(binder) => { self.hash_ty(binder.inner_ty); }, + TyKind::View(ty, _) => { + self.hash_ty(ty); + // FIXME(scrabsha): probably hash the fields as well? + }, TyKind::Err(_) | TyKind::Infer(()) | TyKind::Never diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index f194103ae2e88..cb0db1ed014fc 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -248,6 +248,7 @@ impl<'a> Sugg<'a> { | ast::ExprKind::Array(..) | ast::ExprKind::While(..) | ast::ExprKind::Await(..) + | ast::ExprKind::DirectConstArg(..) | ast::ExprKind::Err(_) | ast::ExprKind::Dummy | ast::ExprKind::UnsafeBinderCast(..) => Sugg::NonParen(snippet(expr.span)), diff --git a/src/tools/clippy/tests/ui/doc/doc_paragraph_missing_punctuation_split_16169.rs b/src/tools/clippy/tests/ui/doc/doc_paragraph_missing_punctuation_split_16169.rs new file mode 100644 index 0000000000000..c01b0c90b120e --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_paragraph_missing_punctuation_split_16169.rs @@ -0,0 +1,13 @@ +//@ check-pass +// https://github.com/rust-lang/rust-clippy/issues/16169 +#![allow(clippy::mixed_attributes_style)] + +/// +pub fn dont_warn_inner_outer() { + //!w +} +/// +#[inline] +///w +pub fn dont_warn_split_by_attr() { +} diff --git a/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr index 45645c598ab63..28de631f35080 100644 --- a/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr +++ b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr @@ -1,8 +1,7 @@ error: backticks are unbalanced - --> tests/ui/doc/unbalanced_ticks.rs:6:5 + --> tests/ui/doc/unbalanced_ticks.rs:6:1 | -LL | /// This is a doc comment with `unbalanced_tick marks and several words that - | _____^ +LL | / /// This is a doc comment with `unbalanced_tick marks and several words that LL | | LL | | /// should be `encompassed_by` tick marks because they `contain_underscores`. LL | | /// Because of the initial `unbalanced_tick` pair, the error message is diff --git a/src/tools/linkchecker/linkcheck.sh b/src/tools/linkchecker/linkcheck.sh index d230610a6e79d..36c14d31d7310 100755 --- a/src/tools/linkchecker/linkcheck.sh +++ b/src/tools/linkchecker/linkcheck.sh @@ -23,6 +23,31 @@ set -e +# Fetches the contents of a file from GitHub using the REST API. +# Usage: fetch_github_file OWNER REPO PATH OUTPUT_PATH +fetch_github_file() { + owner="$1" + repo="$2" + path="$3" + output="$4" + + # Hack to repurpose the function's positional parameters to conditionally + # pass curl headers. The GITHUB_TOKEN should be set to help avoid rate + # limits. + if [ -n "${GITHUB_TOKEN:-}" ] + then + set -- -H "Authorization: Bearer $GITHUB_TOKEN" + else + set -- + fi + + curl -L -o "$output" \ + -H "Accept: application/vnd.github.raw" \ + "$@" \ + -H "X-GitHub-Api-Version: 2026-03-10" \ + "https://api.github.com/repos/$owner/$repo/contents/$path" +} + html_dir="$(rustc +nightly --print sysroot)/share/doc/rust/html" if [ ! -d "$html_dir" ] @@ -95,12 +120,10 @@ fi if [ ! -e "linkchecker/main.rs" ] || [ "$iterative" = "0" ] then echo "Downloading linkchecker source..." - nightly_hash=$(rustc +nightly -Vv | grep commit-hash | cut -f2 -d" ") - url="https://raw.githubusercontent.com/rust-lang/rust" mkdir linkchecker - curl -o linkchecker/Cargo.lock ${url}/${nightly_hash}/Cargo.lock - curl -o linkchecker/Cargo.toml ${url}/${nightly_hash}/src/tools/linkchecker/Cargo.toml - curl -o linkchecker/main.rs ${url}/${nightly_hash}/src/tools/linkchecker/main.rs + fetch_github_file rust-lang rust Cargo.lock linkchecker/Cargo.lock + fetch_github_file rust-lang rust src/tools/linkchecker/Cargo.toml linkchecker/Cargo.toml + fetch_github_file rust-lang rust src/tools/linkchecker/main.rs linkchecker/main.rs fi echo "Building book \"$book_name\"..." diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index 8a3674bff1ca6..cc7fdaefc8fee 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -486,7 +486,8 @@ pub(crate) fn format_expr( | ast::ExprKind::Type(..) | ast::ExprKind::IncludedBytes(..) | ast::ExprKind::OffsetOf(..) - | ast::ExprKind::UnsafeBinderCast(..) => { + | ast::ExprKind::UnsafeBinderCast(..) + | ast::ExprKind::DirectConstArg(..) => { // These don't normally occur in the AST because macros aren't expanded. However, // rustfmt tries to parse macro arguments when formatting macros, so it's not totally // impossible for rustfmt to come across one of these nodes when formatting a file. diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 9e7bbcc243ccf..d3a1279cfb325 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -1014,7 +1014,6 @@ impl Rewrite for ast::Ty { }) } ast::TyKind::CVarArgs => Ok("...".to_owned()), - ast::TyKind::Dummy | ast::TyKind::Err(_) => Ok(context.snippet(self.span).to_owned()), ast::TyKind::FieldOf(ref ty, ref variant, ref field) => { let ty = ty.rewrite_result(context, shape)?; if let Some(variant) = variant { @@ -1049,14 +1048,14 @@ impl Rewrite for ast::Ty { result.push_str(&rewrite); Ok(result) } - - ast::TyKind::Pat(..) | ast::TyKind::View(..) => { + ast::TyKind::Pat(..) | ast::TyKind::View(..) | ast::TyKind::DirectConstArg(..) => { // These don't normally occur in the AST because macros aren't expanded. However, // rustfmt tries to parse macro arguments when formatting macros, so it's not // totally impossible for rustfmt to come across these nodes when formatting a file. // Also, rustfmt might get passed the output from `-Zunpretty=expanded`. Err(RewriteError::Unknown) } + ast::TyKind::Dummy | ast::TyKind::Err(_) => Ok(context.snippet(self.span).to_owned()), } } } diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index 1fcc59f2a1ea4..3e06f3899d1b3 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -532,9 +532,8 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr | ast::ExprKind::Index(_, ref expr, _) | ast::ExprKind::Unary(_, ref expr) | ast::ExprKind::Try(ref expr) - | ast::ExprKind::Yield(YieldKind::Prefix(Some(ref expr))) => { - is_block_expr(context, expr, repr) - } + | ast::ExprKind::Yield(YieldKind::Prefix(Some(ref expr))) + | ast::ExprKind::DirectConstArg(ref expr) => is_block_expr(context, expr, repr), ast::ExprKind::Closure(ref closure) => is_block_expr(context, &closure.body, repr), // This can only be a string lit ast::ExprKind::Lit(_) => { diff --git a/src/tools/rustfmt/tests/source/direct_const_arg.rs b/src/tools/rustfmt/tests/source/direct_const_arg.rs new file mode 100644 index 0000000000000..f6f9c25d9a0d8 --- /dev/null +++ b/src/tools/rustfmt/tests/source/direct_const_arg.rs @@ -0,0 +1,14 @@ +// direct_const_arg! is a built-in macro relevant to min_generic_const_args; its contents should be +// formatted as if its contents were passed through unchanged (the macro changes the semantics of +// the contained expression, the syntax is unchanged) + +#![feature(min_generic_const_args)] + +trait Trait { + type const TYPE_CONST: usize; +} + +struct S; + +fn parsed_as_expr_kind(_: S<{ core::direct_const_arg!( T :: TYPE_CONST ) }>) {} +fn parsed_as_ty_kind(_: S< core::direct_const_arg!( T :: TYPE_CONST ) >) {} diff --git a/src/tools/rustfmt/tests/target/direct_const_arg.rs b/src/tools/rustfmt/tests/target/direct_const_arg.rs new file mode 100644 index 0000000000000..1ebadae00ff05 --- /dev/null +++ b/src/tools/rustfmt/tests/target/direct_const_arg.rs @@ -0,0 +1,14 @@ +// direct_const_arg! is a built-in macro relevant to min_generic_const_args; its contents should be +// formatted as if its contents were passed through unchanged (the macro changes the semantics of +// the contained expression, the syntax is unchanged) + +#![feature(min_generic_const_args)] + +trait Trait { + type const TYPE_CONST: usize; +} + +struct S; + +fn parsed_as_expr_kind(_: S<{ core::direct_const_arg!(T::TYPE_CONST) }>) {} +fn parsed_as_ty_kind(_: S) {} diff --git a/tests/codegen-llvm/array-cmp.rs b/tests/codegen-llvm/array-cmp.rs index 0106b9c15c11b..5b0a802f4097e 100644 --- a/tests/codegen-llvm/array-cmp.rs +++ b/tests/codegen-llvm/array-cmp.rs @@ -2,6 +2,9 @@ //@ compile-flags: -C opt-level=2 //@ needs-deterministic-layouts (checks depend on tuple layout) +//@ revisions: LLVM22 LLVM23 +//@ [LLVM22] max-llvm-major-version: 22 +//@ [LLVM23] min-llvm-version: 23 #![crate_type = "lib"] @@ -66,7 +69,8 @@ pub fn array_of_tuple_le(a: &[(i16, u16); 2], b: &[(i16, u16); 2]) -> bool { // CHECK-NEXT: br i1 %[[EQ01]], label %{{.+}}, label %[[EXIT_U]] // CHECK: [[DONE]]: - // CHECK: %[[RET:.+]] = phi i1 [ %{{.+}}, %[[EXIT_S]] ], [ %{{.+}}, %[[EXIT_U]] ], [ true, %[[L11]] ] + // LLVM22: %[[RET:.+]] = phi i1 [ %{{.+}}, %[[EXIT_S]] ], [ %{{.+}}, %[[EXIT_U]] ], [ true, %[[L11]] ] + // LLVM23: %[[RET:.+]] = phi i1 [ %{{.+}}, %[[EXIT_U]] ], [ %{{.+}}, %[[EXIT_S]] ], [ true, %[[L11]] ] // CHECK: ret i1 %[[RET]] a <= b diff --git a/tests/codegen-llvm/bpf-allows-unaligned.rs b/tests/codegen-llvm/bpf-allows-unaligned.rs index c7a70d5b2e502..7e95a56d984c9 100644 --- a/tests/codegen-llvm/bpf-allows-unaligned.rs +++ b/tests/codegen-llvm/bpf-allows-unaligned.rs @@ -5,7 +5,7 @@ #[no_mangle] #[target_feature(enable = "allows-misaligned-mem-access")] -// CHECK: define noundef zeroext i8 @foo(i8 noundef returned %arg) unnamed_addr #0 { +// CHECK: define noundef zeroext i8 @foo(i8 noundef returned %arg) unnamed_addr #0 pub unsafe fn foo(arg: u8) -> u8 { arg } diff --git a/tests/codegen-llvm/branch-protection.rs b/tests/codegen-llvm/branch-protection.rs index ed1cb2cd137ea..11847c256d6ba 100644 --- a/tests/codegen-llvm/branch-protection.rs +++ b/tests/codegen-llvm/branch-protection.rs @@ -22,7 +22,7 @@ extern crate minicore; use minicore::*; // A basic test function. -// CHECK: @test(){{.*}} [[ATTR:#[0-9]+]] { +// CHECK: @test(){{.*}} [[ATTR:#[0-9]+]] #[no_mangle] pub fn test() {} diff --git a/tests/codegen-llvm/enum/enum-match.rs b/tests/codegen-llvm/enum/enum-match.rs index 6d8b97328f8e0..a0cba452e123c 100644 --- a/tests/codegen-llvm/enum/enum-match.rs +++ b/tests/codegen-llvm/enum/enum-match.rs @@ -1,5 +1,8 @@ //@ compile-flags: -Copt-level=1 //@ only-64bit +//@ revisions: LLVM22 LLVM23 +//@ [LLVM22] max-llvm-major-version: 22 +//@ [LLVM23] min-llvm-version: 23 #![crate_type = "lib"] #![feature(core_intrinsics)] @@ -18,8 +21,9 @@ pub enum Enum0 { // CHECK-LABEL: define{{( dso_local)?}} noundef{{( range\(i8 [0-9]+, [0-9]+\))?}} i8 @match0(i8{{.+}}%0) // CHECK-NEXT: start: // CHECK-NEXT: %[[IS_B:.+]] = icmp eq i8 %0, 2 -// CHECK-NEXT: %[[TRUNC:.+]] = and i8 %0, 1 -// CHECK-NEXT: %[[R:.+]] = select i1 %[[IS_B]], i8 13, i8 %[[TRUNC]] +// LLVM22-NEXT: %[[TRUNC:.+]] = and i8 %0, 1 +// LLVM22-NEXT: %[[R:.+]] = select i1 %[[IS_B]], i8 13, i8 %[[TRUNC]] +// LLVM23-NEXT: %[[R:.+]] = select i1 %[[IS_B]], i8 13, i8 %0 // CHECK-NEXT: ret i8 %[[R]] #[no_mangle] pub fn match0(e: Enum0) -> u8 { diff --git a/tests/codegen-llvm/frame-pointer-cli-control.rs b/tests/codegen-llvm/frame-pointer-cli-control.rs index 911a5f03cbcda..79cdfc70f1ad7 100644 --- a/tests/codegen-llvm/frame-pointer-cli-control.rs +++ b/tests/codegen-llvm/frame-pointer-cli-control.rs @@ -45,7 +45,7 @@ Specific cases where platforms or tools rely on frame pointers for sound or corr extern crate minicore; -// CHECK: i32 @peach{{.*}}[[PEACH_ATTRS:\#[0-9]+]] { +// CHECK: i32 @peach{{.*}}[[PEACH_ATTRS:\#[0-9]+]] #[no_mangle] pub fn peach(x: u32) -> u32 { x diff --git a/tests/codegen-llvm/frame-pointer.rs b/tests/codegen-llvm/frame-pointer.rs index 1d0dd762826b2..a52d95a23862d 100644 --- a/tests/codegen-llvm/frame-pointer.rs +++ b/tests/codegen-llvm/frame-pointer.rs @@ -18,7 +18,7 @@ extern crate minicore; use minicore::*; -// CHECK: define i32 @peach{{.*}}[[PEACH_ATTRS:\#[0-9]+]] { +// CHECK: define i32 @peach{{.*}}[[PEACH_ATTRS:\#[0-9]+]] #[no_mangle] pub fn peach(x: u32) -> u32 { x diff --git a/tests/codegen-llvm/gpu-convergent.rs b/tests/codegen-llvm/gpu-convergent.rs index bb9271ab69996..376d65a3d4a25 100644 --- a/tests/codegen-llvm/gpu-convergent.rs +++ b/tests/codegen-llvm/gpu-convergent.rs @@ -17,7 +17,7 @@ extern "C" { fn ext(); } -// CHECK: define {{.*}}_kernel void @fun(i32{{.*}}) unnamed_addr #[[ATTR:[0-9]+]] { +// CHECK: define {{.*}}_kernel void @fun(i32{{.*}}) unnamed_addr #[[ATTR:[0-9]+]] // CHECK: declare void @ext() unnamed_addr #[[ATTR]] // CHECK: attributes #[[ATTR]] = {{.*}} convergent #[no_mangle] diff --git a/tests/codegen-llvm/instrument-coverage/testprog.rs b/tests/codegen-llvm/instrument-coverage/testprog.rs index ef61ede6de8ee..67c49c438f9f7 100644 --- a/tests/codegen-llvm/instrument-coverage/testprog.rs +++ b/tests/codegen-llvm/instrument-coverage/testprog.rs @@ -101,7 +101,7 @@ fn main() { // CHECK-SAME: @__llvm_prf_nm // CHECK-SAME: section "llvm.metadata" -// CHECK: define internal { {{.*}} } @_R{{[a-zA-Z0-9_]+}}testprog14will_be_called() unnamed_addr #{{[0-9]+}} { +// CHECK: define internal { {{.*}} } @_R{{[a-zA-Z0-9_]+}}testprog14will_be_called() unnamed_addr #{{[0-9]+}} // CHECK-NEXT: start: // CHECK-NOT: define internal // CHECK: atomicrmw add ptr @@ -109,7 +109,7 @@ fn main() { // CHECK: declare void @llvm.instrprof.increment(ptr, i64, i32, i32) #[[LLVM_INSTRPROF_INCREMENT_ATTR:[0-9]+]] -// WIN: define linkonce_odr hidden i32 @__llvm_profile_runtime_user() #[[LLVM_PROFILE_RUNTIME_USER_ATTR:[0-9]+]] comdat {{.*}}{ +// WIN: define linkonce_odr hidden i32 @__llvm_profile_runtime_user() #[[LLVM_PROFILE_RUNTIME_USER_ATTR:[0-9]+]] comdat {{.*}} // WIN-NEXT: %1 = load i32, ptr @__llvm_profile_runtime // WIN-NEXT: ret i32 %1 // WIN-NEXT: } diff --git a/tests/codegen-llvm/link_section.rs b/tests/codegen-llvm/link_section.rs index f196ea86c447d..61bde683c0a41 100644 --- a/tests/codegen-llvm/link_section.rs +++ b/tests/codegen-llvm/link_section.rs @@ -29,7 +29,7 @@ pub static VAR2: E = E::A(666); #[link_section = "__TEST,three"] pub static VAR3: E = E::B(1.); -// CHECK: define {{(dso_local )?}}void @fn1() {{.*}} section "__TEST,four" { +// CHECK: define {{(dso_local )?}}void @fn1() {{.*}} section "__TEST,four" #[no_mangle] #[link_section = "__TEST,four"] pub fn fn1() {} diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs index 7639ce7b10448..5b1aa97ab3338 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs @@ -7,21 +7,21 @@ pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { // CHECK-LABEL: define{{.*}}foo - // CHECK-SAME: {{.*}}![[TYPE1:[0-9]+]] + // CHECK-SAME: {{.*}}!type ![[TYPE1:[0-9]+]] // CHECK: call i1 @llvm.type.test(ptr {{%f|%0}}, metadata !"_ZTSFu3i32S_E.normalized.generalized") f(arg) } pub fn bar(f: fn(i32, i32) -> i32, arg1: i32, arg2: i32) -> i32 { // CHECK-LABEL: define{{.*}}bar - // CHECK-SAME: {{.*}}![[TYPE2:[0-9]+]] + // CHECK-SAME: {{.*}}!type ![[TYPE2:[0-9]+]] // CHECK: call i1 @llvm.type.test(ptr {{%f|%0}}, metadata !"_ZTSFu3i32S_S_E.normalized.generalized") f(arg1, arg2) } pub fn baz(f: fn(i32, i32, i32) -> i32, arg1: i32, arg2: i32, arg3: i32) -> i32 { // CHECK-LABEL: define{{.*}}baz - // CHECK-SAME: {{.*}}![[TYPE3:[0-9]+]] + // CHECK-SAME: {{.*}}!type ![[TYPE3:[0-9]+]] // CHECK: call i1 @llvm.type.test(ptr {{%f|%0}}, metadata !"_ZTSFu3i32S_S_S_E.normalized.generalized") f(arg1, arg2, arg3) } diff --git a/tests/codegen-llvm/some-non-zero-from-atomic-optimization.rs b/tests/codegen-llvm/some-non-zero-from-atomic-optimization.rs index 35317b0dd39cc..3df2d569f9a40 100644 --- a/tests/codegen-llvm/some-non-zero-from-atomic-optimization.rs +++ b/tests/codegen-llvm/some-non-zero-from-atomic-optimization.rs @@ -72,7 +72,7 @@ pub unsafe fn some_non_zero_from_atomic_get() -> Option { /// /// The way we check that the LLVM IR is correct is by making sure that neither /// `panic` nor `unreachable` is part of the LLVM IR: -// CHECK-LABEL: define {{.*}} i64 @some_non_zero_from_atomic_get2() {{.*}} { +// CHECK-LABEL: define {{.*}} i64 @some_non_zero_from_atomic_get2() {{.*}} // CHECK-NOT: panic // CHECK-NOT: unreachable #[no_mangle] diff --git a/tests/codegen-llvm/target-feature-negative-implication.rs b/tests/codegen-llvm/target-feature-negative-implication.rs index a9cdca4283991..376599738e526 100644 --- a/tests/codegen-llvm/target-feature-negative-implication.rs +++ b/tests/codegen-llvm/target-feature-negative-implication.rs @@ -13,7 +13,7 @@ use minicore::*; #[no_mangle] pub unsafe fn banana() { // CHECK-LABEL: @banana() - // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] { + // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] } // CHECK: attributes [[BANANAATTRS]] diff --git a/tests/codegen-llvm/target-feature-overrides.rs b/tests/codegen-llvm/target-feature-overrides.rs index 2adc8ee6f53bc..3bf05c7977ebf 100644 --- a/tests/codegen-llvm/target-feature-overrides.rs +++ b/tests/codegen-llvm/target-feature-overrides.rs @@ -23,7 +23,7 @@ extern "C" { #[no_mangle] pub unsafe fn apple() -> u32 { // CHECK-LABEL: @apple() - // CHECK-SAME: [[APPLEATTRS:#[0-9]+]] { + // CHECK-SAME: [[APPLEATTRS:#[0-9]+]] // CHECK: {{.*}}call{{.*}}@peach peach() } @@ -32,7 +32,7 @@ pub unsafe fn apple() -> u32 { #[no_mangle] pub unsafe fn banana() -> u32 { // CHECK-LABEL: @banana() - // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] { + // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] // COMPAT: {{.*}}call{{.*}}@peach // INCOMPAT: {{.*}}call{{.*}}@apple apple() // Compatible for inline in COMPAT revision and can't be inlined in INCOMPAT diff --git a/tests/codegen-llvm/unwind-abis/aapcs-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/aapcs-unwind-abi.rs index ecace722e0dbe..279780e3a7aeb 100644 --- a/tests/codegen-llvm/unwind-abis/aapcs-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/aapcs-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `aapcs-unwind` extern functions. `aapcs-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 #[no_mangle] pub extern "aapcs" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 #[no_mangle] pub extern "aapcs-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/c-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/c-unwind-abi.rs index 46c08b5fc4ff4..1b3312839e3e8 100644 --- a/tests/codegen-llvm/unwind-abis/c-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/c-unwind-abi.rs @@ -7,11 +7,11 @@ #![crate_type = "lib"] -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 #[no_mangle] pub extern "C" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 #[no_mangle] pub extern "C-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/cdecl-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/cdecl-unwind-abi.rs index 8e643d6ce4947..6f4eafb353ccb 100644 --- a/tests/codegen-llvm/unwind-abis/cdecl-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/cdecl-unwind-abi.rs @@ -7,11 +7,11 @@ #![crate_type = "lib"] -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 #[no_mangle] pub extern "cdecl" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 #[no_mangle] pub extern "cdecl-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/fastcall-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/fastcall-unwind-abi.rs index 7df46813ed1dd..51c6fd15b9c5c 100644 --- a/tests/codegen-llvm/unwind-abis/fastcall-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/fastcall-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `fastcall-unwind` extern functions. `fastcall-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 #[no_mangle] pub extern "fastcall" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 #[no_mangle] pub extern "fastcall-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/stdcall-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/stdcall-unwind-abi.rs index cc06ee125495a..b5fcea52b4d61 100644 --- a/tests/codegen-llvm/unwind-abis/stdcall-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/stdcall-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // extern functions. `stdcall-unwind` functions MUST NOT have this attribute. We disable // optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 #[no_mangle] pub extern "stdcall" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 #[no_mangle] pub extern "stdcall-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/system-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/system-unwind-abi.rs index 5f9102483464b..15fce95fe285b 100644 --- a/tests/codegen-llvm/unwind-abis/system-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/system-unwind-abi.rs @@ -7,11 +7,11 @@ #![crate_type = "lib"] -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 #[no_mangle] pub extern "system" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 #[no_mangle] pub extern "system-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/sysv64-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/sysv64-unwind-abi.rs index 69bfaf80b4be6..1293e7c0a5f82 100644 --- a/tests/codegen-llvm/unwind-abis/sysv64-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/sysv64-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `sysv64-unwind` extern functions. `sysv64-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 #[no_mangle] pub extern "sysv64" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 #[no_mangle] pub extern "sysv64-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/thiscall-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/thiscall-unwind-abi.rs index 05f6b8b70e171..a9b6c34ee58e2 100644 --- a/tests/codegen-llvm/unwind-abis/thiscall-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/thiscall-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `thiscall-unwind` extern functions. `thiscall-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 #[no_mangle] pub extern "thiscall" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 #[no_mangle] pub extern "thiscall-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/vectorcall-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/vectorcall-unwind-abi.rs index d001a16b32a1c..8cedb55ae1d28 100644 --- a/tests/codegen-llvm/unwind-abis/vectorcall-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/vectorcall-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `vectorcall-unwind` extern functions. `vectorcall-unwind` functions MUST NOT have this attribute. // We disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 #[no_mangle] pub extern "vectorcall" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 #[no_mangle] pub extern "vectorcall-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/win64-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/win64-unwind-abi.rs index 257f00b54e4d8..2a3ad330406e6 100644 --- a/tests/codegen-llvm/unwind-abis/win64-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/win64-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `win64-unwind` extern functions. `win64-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 #[no_mangle] pub extern "win64" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 #[no_mangle] pub extern "win64-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/pretty/direct-const-arg.pp b/tests/pretty/direct-const-arg.pp new file mode 100644 index 0000000000000..a76ed9a480e71 --- /dev/null +++ b/tests/pretty/direct-const-arg.pp @@ -0,0 +1,15 @@ +#![feature(prelude_import)] +#![no_std] +//@ pretty-mode:expanded +//@ pp-exact:direct-const-arg.pp +#![feature(min_generic_const_args)] +extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; + +fn f() {} + +fn main() { + f::(); + f::<{ core::direct_const_arg! (2) }>(); +} diff --git a/tests/pretty/direct-const-arg.rs b/tests/pretty/direct-const-arg.rs new file mode 100644 index 0000000000000..330c1cac024de --- /dev/null +++ b/tests/pretty/direct-const-arg.rs @@ -0,0 +1,10 @@ +//@ pretty-mode:expanded +//@ pp-exact:direct-const-arg.pp +#![feature(min_generic_const_args)] + +fn f() {} + +fn main() { + f::(); + f::<{ core::direct_const_arg!(2) }>(); +} diff --git a/tests/run-make/ls-metadata/rmake.rs b/tests/run-make/ls-metadata/rmake.rs index 42db97a292bed..3fc7c911f6da9 100644 --- a/tests/run-make/ls-metadata/rmake.rs +++ b/tests/run-make/ls-metadata/rmake.rs @@ -10,7 +10,7 @@ use run_make_support::{rfs, rustc}; fn main() { rustc().input("foo.rs").run(); - rustc().arg("-Zls=root").input("foo").run(); + rustc().arg("-Zls=root").input("foo").run_fail(); rfs::create_file("bar"); - rustc().arg("-Zls=root").input("bar").run(); + rustc().arg("-Zls=root").input("bar").run_fail(); } diff --git a/tests/rustdoc-ui/lints/invalid-html-tags-ice-146890.rs b/tests/rustdoc-ui/lints/invalid-html-tags-ice-146890.rs index d7efc201e7e39..53df3e9eb5a80 100644 --- a/tests/rustdoc-ui/lints/invalid-html-tags-ice-146890.rs +++ b/tests/rustdoc-ui/lints/invalid-html-tags-ice-146890.rs @@ -7,17 +7,17 @@ /// /// key +//~^^ ERROR: unclosed HTML tag `TH` /// +//~^^ ERROR: unopened HTML tag `TD` /// value +//~^^ ERROR: unclosed HTML tag `TH` /// +//~^^ ERROR: unopened HTML tag `TD` /// /// | |_____^ | @@ -18,7 +17,6 @@ error: unopened HTML tag `TD` | LL | /// | |_____^ diff --git a/tests/rustdoc-ui/lints/invalid-html-tags.rs b/tests/rustdoc-ui/lints/invalid-html-tags.rs index 8003e5efdd582..f5700e5846e51 100644 --- a/tests/rustdoc-ui/lints/invalid-html-tags.rs +++ b/tests/rustdoc-ui/lints/invalid-html-tags.rs @@ -164,8 +164,8 @@ pub fn r() {} /// >
/// > href="#broken" +//~^^ ERROR incomplete HTML tag `img` pub fn s() {} ///
diff --git a/tests/rustdoc-ui/lints/invalid-html-tags.stderr b/tests/rustdoc-ui/lints/invalid-html-tags.stderr index b6ec22c247901..3256004ddb6ab 100644 --- a/tests/rustdoc-ui/lints/invalid-html-tags.stderr +++ b/tests/rustdoc-ui/lints/invalid-html-tags.stderr @@ -123,7 +123,6 @@ error: incomplete HTML tag `img` | LL | /// > href="#broken" | |____________________^ diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links_split.rs b/tests/rustdoc-ui/lints/redundant_explicit_links_split.rs new file mode 100644 index 0000000000000..eebc6a0aa023c --- /dev/null +++ b/tests/rustdoc-ui/lints/redundant_explicit_links_split.rs @@ -0,0 +1,100 @@ +#![deny(rustdoc::redundant_explicit_links)] +use std::clone::Clone; + +/// [Clone]( +pub fn split_outer_inner() { + //! std::clone::Clone) +//~^^^ ERROR redundant_explicit_links +} + +/// [Clone](std::clone::Clone +pub fn split_outer_inner_b() { + //! ) +//~^^^ ERROR redundant_explicit_links +} + +/// [Clone]( +#[inline] +/// std::clone::Clone) +//~^^^ ERROR redundant_explicit_links +pub fn split_attr() { +} + +/// [Clone](std::clone::Clone +#[inline] +/// ) +//~^^^ ERROR redundant_explicit_links +pub fn split_attr_b() { +} + +/** [Clone]( */ #[inline] /** std::clone::Clone) */ +//~^ ERROR redundant_explicit_links +pub fn split_blockcomment_attr() { +} + +/** [Clone](std::clone::Clone */ #[inline] /** ) */ +//~^ ERROR redundant_explicit_links +pub fn split_blockcomment_attr_b() { +} + +/** [Clone]( */ +#[inline] +/** std::clone::Clone) */ +//~^^^ ERROR redundant_explicit_links +pub fn split_blockcomment_multiline_attr() { +} + +/** [Clone](std::clone::Clone */ +#[inline] +/** ) */ +//~^^^ ERROR redundant_explicit_links +pub fn split_blockcomment_multiline_attr_b() { +} + +/** [Clone]( */ #[inline] +/** std::clone::Clone) */ +//~^^ ERROR redundant_explicit_links +pub fn split_blockcomment_twoline_attr() { +} + +/** [Clone](std::clone::Clone */ #[inline] +/** ) */ +//~^^ ERROR redundant_explicit_links +pub fn split_blockcomment_twoline_attr_b() { +} + +/** [Clone]( */ +#[inline] /** std::clone::Clone) */ +//~^^ ERROR redundant_explicit_links +pub fn split_blockcomment_secondline_attr() { +} + +/** [Clone](std::clone::Clone */ +#[inline] /** ) */ +//~^^ ERROR redundant_explicit_links +pub fn split_blockcomment_secondline_attr_b() { +} + +/** [Clone](std::clone::Clone) */ #[inline] +//~^ ERROR redundant_explicit_links +//~| SUGGESTION [Clone] +pub fn split_blockcomment_singleline_attr() { +} + +/** [Clone](std::clone::Clone) */ #[inline] pub fn split_blockcomment_singleline_attr_b() { } +//~^ ERROR redundant_explicit_links +//~| SUGGESTION [Clone] + +/// [Clone]( +/// std::clone::Clone) +//~^^ ERROR redundant_explicit_links +//~| SUGGESTION [Clone] +pub fn not_split() { +} + +/// [Clone](std::clone::Clone +/// ) +//~^^ ERROR redundant_explicit_links +//~| SUGGESTION [Clone] +pub fn not_split_b() { +} diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links_split.stderr b/tests/rustdoc-ui/lints/redundant_explicit_links_split.stderr new file mode 100644 index 0000000000000..bfab5b751eedf --- /dev/null +++ b/tests/rustdoc-ui/lints/redundant_explicit_links_split.stderr @@ -0,0 +1,201 @@ +error: redundant explicit link target + --> $DIR/redundant_explicit_links_split.rs:4:1 + | +LL | / /// [Clone]( +LL | | pub fn split_outer_inner() { +LL | | //! std::clone::Clone) + | |__________________________^ explicit target `std::clone::Clone` is redundant because label `Clone` resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +note: the lint level is defined here + --> $DIR/redundant_explicit_links_split.rs:1:9 + | +LL | #![deny(rustdoc::redundant_explicit_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: redundant explicit link target + --> $DIR/redundant_explicit_links_split.rs:10:1 + | +LL | / /// [Clone](std::clone::Clone +LL | | pub fn split_outer_inner_b() { +LL | | //! ) + | |_________^ explicit target `std::clone::Clone` is redundant because label `Clone` resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links + +error: redundant explicit link target + --> $DIR/redundant_explicit_links_split.rs:16:1 + | +LL | / /// [Clone]( +LL | | #[inline] +LL | | /// std::clone::Clone) + | |______________________^ explicit target `std::clone::Clone` is redundant because label `Clone` resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links + +error: redundant explicit link target + --> $DIR/redundant_explicit_links_split.rs:23:1 + | +LL | / /// [Clone](std::clone::Clone +LL | | #[inline] +LL | | /// ) + | |_____^ explicit target `std::clone::Clone` is redundant because label `Clone` resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links + +error: redundant explicit link target + --> $DIR/redundant_explicit_links_split.rs:30:1 + | +LL | /** [Clone]( */ #[inline] /** std::clone::Clone) */ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ explicit target `std::clone::Clone` is redundant because label `Clone` resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links + +error: redundant explicit link target + --> $DIR/redundant_explicit_links_split.rs:35:1 + | +LL | /** [Clone](std::clone::Clone */ #[inline] /** ) */ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ explicit target `std::clone::Clone` is redundant because label `Clone` resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links + +error: redundant explicit link target + --> $DIR/redundant_explicit_links_split.rs:40:1 + | +LL | / /** [Clone]( */ +LL | | #[inline] +LL | | /** std::clone::Clone) */ + | |_________________________^ explicit target `std::clone::Clone` is redundant because label `Clone` resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links + +error: redundant explicit link target + --> $DIR/redundant_explicit_links_split.rs:47:1 + | +LL | / /** [Clone](std::clone::Clone */ +LL | | #[inline] +LL | | /** ) */ + | |________^ explicit target `std::clone::Clone` is redundant because label `Clone` resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links + +error: redundant explicit link target + --> $DIR/redundant_explicit_links_split.rs:54:1 + | +LL | / /** [Clone]( */ #[inline] +LL | | /** std::clone::Clone) */ + | |_________________________^ explicit target `std::clone::Clone` is redundant because label `Clone` resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links + +error: redundant explicit link target + --> $DIR/redundant_explicit_links_split.rs:60:1 + | +LL | / /** [Clone](std::clone::Clone */ #[inline] +LL | | /** ) */ + | |________^ explicit target `std::clone::Clone` is redundant because label `Clone` resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links + +error: redundant explicit link target + --> $DIR/redundant_explicit_links_split.rs:66:1 + | +LL | / /** [Clone]( */ +LL | | #[inline] /** std::clone::Clone) */ + | |___________________________________^ explicit target `std::clone::Clone` is redundant because label `Clone` resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links + +error: redundant explicit link target + --> $DIR/redundant_explicit_links_split.rs:72:1 + | +LL | / /** [Clone](std::clone::Clone */ +LL | | #[inline] /** ) */ + | |__________________^ explicit target `std::clone::Clone` is redundant because label `Clone` resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links + +error: redundant explicit link target + --> $DIR/redundant_explicit_links_split.rs:78:13 + | +LL | /** [Clone](std::clone::Clone) */ #[inline] + | ----- ^^^^^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL - /** [Clone](std::clone::Clone) */ #[inline] +LL + /** [Clone] */ #[inline] + | + +error: redundant explicit link target + --> $DIR/redundant_explicit_links_split.rs:84:13 + | +LL | /** [Clone](std::clone::Clone) */ #[inline] pub fn split_blockcomment_singleline_attr_b() { } + | ----- ^^^^^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL - /** [Clone](std::clone::Clone) */ #[inline] pub fn split_blockcomment_singleline_attr_b() { } +LL + /** [Clone] */ #[inline] pub fn split_blockcomment_singleline_attr_b() { } + | + +error: redundant explicit link target + --> $DIR/redundant_explicit_links_split.rs:88:13 + | +LL | /// [Clone]( + | ______-----__^ + | | | + | | because label contains path that resolves to same destination +LL | | /// std::clone::Clone) + | |_____________________^ explicit target is redundant + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL - /// [Clone]( +LL - /// std::clone::Clone) +LL + /// [Clone] + | + +error: redundant explicit link target + --> $DIR/redundant_explicit_links_split.rs:95:13 + | +LL | /// [Clone](std::clone::Clone + | ______-----__^ + | | | + | | because label contains path that resolves to same destination +LL | | /// ) + | |____^ explicit target is redundant + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +help: remove explicit link target + | +LL - /// [Clone](std::clone::Clone +LL - /// ) +LL + /// [Clone] + | + +error: aborting due to 16 previous errors + diff --git a/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-for-swap.edition2015.stderr b/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-for-swap.edition2015.stderr new file mode 100644 index 0000000000000..3c31169a8675d --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-for-swap.edition2015.stderr @@ -0,0 +1,11 @@ +error[E0515]: cannot return value referencing local variable `x` + --> $DIR/gat-hide-lifetime-for-swap.rs:33:5 + | +LL | h.hide_ref(&mut res).swap(h.hide_ref(&mut &x)); + | -- `x` is borrowed here +LL | res + | ^^^ returns a value referencing data owned by the current function + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-for-swap.edition2024.stderr b/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-for-swap.edition2024.stderr new file mode 100644 index 0000000000000..3c31169a8675d --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-for-swap.edition2024.stderr @@ -0,0 +1,11 @@ +error[E0515]: cannot return value referencing local variable `x` + --> $DIR/gat-hide-lifetime-for-swap.rs:33:5 + | +LL | h.hide_ref(&mut res).swap(h.hide_ref(&mut &x)); + | -- `x` is borrowed here +LL | res + | ^^^ returns a value referencing data owned by the current function + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-for-swap.polonius_alpha.stderr b/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-for-swap.polonius_alpha.stderr new file mode 100644 index 0000000000000..3c31169a8675d --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-for-swap.polonius_alpha.stderr @@ -0,0 +1,11 @@ +error[E0515]: cannot return value referencing local variable `x` + --> $DIR/gat-hide-lifetime-for-swap.rs:33:5 + | +LL | h.hide_ref(&mut res).swap(h.hide_ref(&mut &x)); + | -- `x` is borrowed here +LL | res + | ^^^ returns a value referencing data owned by the current function + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-for-swap.rs b/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-for-swap.rs new file mode 100644 index 0000000000000..9cbaa5102f015 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-for-swap.rs @@ -0,0 +1,48 @@ +//@ revisions: edition2015 edition2024 polonius_alpha +//@ ignore-compare-mode-polonius (explicit revisions) +//@ [edition2015] edition: 2015 +//@ [edition2024] edition: 2024 +//@ [polonius_alpha] edition: 2024 +//@ [polonius_alpha] compile-flags: -Zpolonius=next + +// Test to show what happens if we were not careful and allowed invariant +// lifetimes to escape through a GAT. +// +// Specifically we swap a long lived and short lived reference, giving us a +// dangling pointer. + +trait Swap: Sized { + fn swap(self, other: Self); +} +impl Swap for &mut T { + fn swap(self, other: Self) { + std::mem::swap(self, other); + } +} +trait Hider { + type Hidden<'a, 'b, T: 'static>: Swap + 'a + where + Self: 'a, + 'b: 'a; + fn hide_ref<'a, 'b, T: 'static>(&self, x: &'a mut &'b T) -> Self::Hidden<'a, 'b, T>; +} +fn dangle_ref(h: &H) -> &'static [i32; 3] { + let mut res = &[4, 5, 6]; + let x = [1, 2, 3]; + h.hide_ref(&mut res).swap(h.hide_ref(&mut &x)); + res //[edition2015,edition2024,polonius_alpha]~ ERROR cannot return value referencing local variable `x` +} +struct H; +impl Hider for H { + type Hidden<'a, 'b, T: 'static> + = &'a mut &'b T + where + Self: 'a, + 'b: 'a; + fn hide_ref<'a, 'b, T: 'static>(&self, x: &'a mut &'b T) -> Self::Hidden<'a, 'b, T> { + x + } +} +fn main() { + println!("{:?}", dangle_ref(&H)); +} diff --git a/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-in-type-arg-for-swap.edition2015.stderr b/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-in-type-arg-for-swap.edition2015.stderr new file mode 100644 index 0000000000000..ccc8dfbe5fc88 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-in-type-arg-for-swap.edition2015.stderr @@ -0,0 +1,11 @@ +error[E0515]: cannot return value referencing local variable `x` + --> $DIR/gat-hide-lifetime-in-type-arg-for-swap.rs:33:5 + | +LL | h.hide(&mut res).swap(h.hide(&mut &x)); + | -- `x` is borrowed here +LL | res + | ^^^ returns a value referencing data owned by the current function + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-in-type-arg-for-swap.edition2024.stderr b/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-in-type-arg-for-swap.edition2024.stderr new file mode 100644 index 0000000000000..ccc8dfbe5fc88 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-in-type-arg-for-swap.edition2024.stderr @@ -0,0 +1,11 @@ +error[E0515]: cannot return value referencing local variable `x` + --> $DIR/gat-hide-lifetime-in-type-arg-for-swap.rs:33:5 + | +LL | h.hide(&mut res).swap(h.hide(&mut &x)); + | -- `x` is borrowed here +LL | res + | ^^^ returns a value referencing data owned by the current function + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-in-type-arg-for-swap.polonius_alpha.stderr b/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-in-type-arg-for-swap.polonius_alpha.stderr new file mode 100644 index 0000000000000..ccc8dfbe5fc88 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-in-type-arg-for-swap.polonius_alpha.stderr @@ -0,0 +1,11 @@ +error[E0515]: cannot return value referencing local variable `x` + --> $DIR/gat-hide-lifetime-in-type-arg-for-swap.rs:33:5 + | +LL | h.hide(&mut res).swap(h.hide(&mut &x)); + | -- `x` is borrowed here +LL | res + | ^^^ returns a value referencing data owned by the current function + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-in-type-arg-for-swap.rs b/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-in-type-arg-for-swap.rs new file mode 100644 index 0000000000000..3513bd432a9c6 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/gat-hide-lifetime-in-type-arg-for-swap.rs @@ -0,0 +1,48 @@ +//@ revisions: edition2015 edition2024 polonius_alpha +//@ ignore-compare-mode-polonius (explicit revisions) +//@ [edition2015] edition: 2015 +//@ [edition2024] edition: 2024 +//@ [polonius_alpha] edition: 2024 +//@ [polonius_alpha] compile-flags: -Zpolonius=next + +// Like gat-hide-lifetime-for-swap, but the invariant lifetime is hidden +// inside a *type* argument of the GAT rather than being a region argument. +// The underlying type can capture `U` (and thus all of its regions) because +// of the declared `U: 'a` bound, so the regions inside the type argument +// must be considered live too. + +trait Swap: Sized { + fn swap(self, other: Self); +} +impl Swap for &mut T { + fn swap(self, other: Self) { + std::mem::swap(self, other); + } +} +trait Hider { + type Hidden<'a, U>: Swap + 'a + where + Self: 'a, + U: Swap + 'a; + fn hide<'a, U: Swap + 'a>(&'a self, x: U) -> Self::Hidden<'a, U>; +} +fn dangle(h: &H) -> &'static [i32; 3] { + let mut res = &[4, 5, 6]; + let x = [1, 2, 3]; + h.hide(&mut res).swap(h.hide(&mut &x)); + res //[edition2015,edition2024,polonius_alpha]~ ERROR cannot return value referencing local variable `x` +} +struct H; +impl Hider for H { + type Hidden<'a, U> + = U + where + Self: 'a, + U: Swap + 'a; + fn hide<'a, U: Swap + 'a>(&'a self, x: U) -> Self::Hidden<'a, U> { + x + } +} +fn main() { + println!("{:?}", dangle(&H)); +} diff --git a/tests/ui/borrowck/alias-liveness/gat-outlives-env.edition2015.stderr b/tests/ui/borrowck/alias-liveness/gat-outlives-env.edition2015.stderr new file mode 100644 index 0000000000000..9b71acde986f7 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/gat-outlives-env.edition2015.stderr @@ -0,0 +1,13 @@ +error[E0499]: cannot borrow `t` as mutable more than once at a time + --> $DIR/gat-outlives-env.rs:49:13 + | +LL | let a = t.changes(static_unit); + | - first mutable borrow occurs here +LL | let b = t.changes(static_unit); + | ^ second mutable borrow occurs here +LL | } + | - first borrow might be used here, when `a` is dropped and runs the destructor for type `::Changes<'_, '_>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/borrowck/alias-liveness/gat-outlives-env.edition2024.stderr b/tests/ui/borrowck/alias-liveness/gat-outlives-env.edition2024.stderr new file mode 100644 index 0000000000000..9b71acde986f7 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/gat-outlives-env.edition2024.stderr @@ -0,0 +1,13 @@ +error[E0499]: cannot borrow `t` as mutable more than once at a time + --> $DIR/gat-outlives-env.rs:49:13 + | +LL | let a = t.changes(static_unit); + | - first mutable borrow occurs here +LL | let b = t.changes(static_unit); + | ^ second mutable borrow occurs here +LL | } + | - first borrow might be used here, when `a` is dropped and runs the destructor for type `::Changes<'_, '_>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/borrowck/alias-liveness/gat-outlives-env.polonius_alpha.stderr b/tests/ui/borrowck/alias-liveness/gat-outlives-env.polonius_alpha.stderr new file mode 100644 index 0000000000000..9b71acde986f7 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/gat-outlives-env.polonius_alpha.stderr @@ -0,0 +1,13 @@ +error[E0499]: cannot borrow `t` as mutable more than once at a time + --> $DIR/gat-outlives-env.rs:49:13 + | +LL | let a = t.changes(static_unit); + | - first mutable borrow occurs here +LL | let b = t.changes(static_unit); + | ^ second mutable borrow occurs here +LL | } + | - first borrow might be used here, when `a` is dropped and runs the destructor for type `::Changes<'_, '_>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/borrowck/alias-liveness/gat-outlives-env.rs b/tests/ui/borrowck/alias-liveness/gat-outlives-env.rs new file mode 100644 index 0000000000000..1c1b728e486b1 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/gat-outlives-env.rs @@ -0,0 +1,52 @@ +//@ revisions: edition2015 edition2024 polonius_alpha +//@ ignore-compare-mode-polonius (explicit revisions) +//@ [edition2015] edition: 2015 +//@ [edition2024] edition: 2024 +//@ [polonius_alpha] edition: 2024 +//@ [polonius_alpha] compile-flags: -Zpolonius=next + +// Demonstrates that you can use a outlives bound on an associated type in a +// where clause to restrict the lifetimes that may be contained within. + +trait Updater { + type Changes<'a, 'b: 'a> + where + Self: 'a; + fn changes<'a, 'b>(&'a mut self, _value: &'b u8) -> Self::Changes<'a, 'b>; +} + +// Nothing can be captured. +fn overlapping_mut(mut t: T) +where + T: Updater, + for<'a, 'b> T::Changes<'a, 'b>: 'static, +{ + let static_unit: &'static u8 = &0; + let a = t.changes(static_unit); + let b = t.changes(static_unit); +} + +// Only `&mut self` is captured not `&'static u8`, and that borrow is killed on the next call. +fn overlapping_mut2(mut t: T) +where + T: Updater, + for<'a, 'b> T::Changes<'a, 'b>: 'b, +{ + let static_unit: &'static u8 = &0; + let a = t.changes(static_unit); + let b = t.changes(static_unit); +} + +// Both `&mut self` and `&'static u8` are captured, because the `'static` lifetime +// makes the associated type live for the entire function body. +fn overlapping_mut3<'b, T>(mut t: T) +where + T: Updater, + for<'a> T::Changes<'a, 'b>: 'a, +{ + let static_unit: &'static u8 = &0; + let a = t.changes(static_unit); + let b = t.changes(static_unit); //[edition2015,edition2024,polonius_alpha]~ ERROR cannot borrow `t` as mutable more than once at a time +} + +fn main() {} diff --git a/tests/ui/borrowck/alias-liveness/gat-outlives-implied.edition2024.stderr b/tests/ui/borrowck/alias-liveness/gat-outlives-implied.edition2024.stderr new file mode 100644 index 0000000000000..9317846041925 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/gat-outlives-implied.edition2024.stderr @@ -0,0 +1,13 @@ +error[E0506]: cannot assign to `cluster` because it is borrowed + --> $DIR/gat-outlives-implied.rs:19:9 + | +LL | let changes = self.changes(&cluster); + | -------- `cluster` is borrowed here +LL | cluster = 1; + | ^^^^^^^^^^^ `cluster` is assigned to here but it was already borrowed +LL | let _ = changes; + | ------- borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/tests/ui/borrowck/alias-liveness/gat-outlives-implied.polonius_alpha.stderr b/tests/ui/borrowck/alias-liveness/gat-outlives-implied.polonius_alpha.stderr new file mode 100644 index 0000000000000..9317846041925 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/gat-outlives-implied.polonius_alpha.stderr @@ -0,0 +1,13 @@ +error[E0506]: cannot assign to `cluster` because it is borrowed + --> $DIR/gat-outlives-implied.rs:19:9 + | +LL | let changes = self.changes(&cluster); + | -------- `cluster` is borrowed here +LL | cluster = 1; + | ^^^^^^^^^^^ `cluster` is assigned to here but it was already borrowed +LL | let _ = changes; + | ------- borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/tests/ui/borrowck/alias-liveness/gat-outlives-implied.rs b/tests/ui/borrowck/alias-liveness/gat-outlives-implied.rs new file mode 100644 index 0000000000000..a9443fccc76b4 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/gat-outlives-implied.rs @@ -0,0 +1,24 @@ +//@ revisions: edition2024 polonius_alpha +//@ ignore-compare-mode-polonius (explicit revisions) +//@ edition: 2024 +//@ [polonius_alpha] compile-flags: -Zpolonius=next + +// Tests that liveness for regions in associated types considers outlives +// bounds, and the transitive implied outlives bounds from those. + +trait Updater { + // Because `'b` is known to outlive `'a`, then we must consider that `'b` + // may be live in `Self::Changes<'a, 'b>`. + type Changes<'a, 'b: 'a>: 'a + where + Self: 'a; + fn changes<'a, 'b>(&'a self, _value: &'b u8) -> Self::Changes<'a, 'b>; + fn run(&self) { + let mut cluster = 0u8; + let changes = self.changes(&cluster); + cluster = 1; //[edition2024,polonius_alpha]~ ERROR cannot assign to `cluster` because it is borrowed + let _ = changes; + } +} + +fn main() {} diff --git a/tests/ui/borrowck/alias-liveness/gat-outlives.rs b/tests/ui/borrowck/alias-liveness/gat-outlives.rs new file mode 100644 index 0000000000000..ce6beffb19043 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/gat-outlives.rs @@ -0,0 +1,23 @@ +//@ revisions: edition2024 polonius_alpha +//@ ignore-compare-mode-polonius (explicit revisions) +//@ check-pass + +// Tests that liveness for regions in associated types considers outlives bounds. + + +trait Updater { + // Because `'b` isn't known to outlive `'a`, then we know that + // `Self::Changes<'a, 'b>` should not need to consider `'b` to be live. + type Changes<'a, 'b>: 'a + where + Self: 'a; + fn changes<'a, 'b>(&'a self, _value: &'b u8) -> Self::Changes<'a, 'b>; + fn run(&self) { + let mut cluster = 0u8; + let changes = self.changes(&cluster); + cluster = 1; + let _ = changes; + } +} + +fn main() {} diff --git a/tests/ui/borrowck/alias-liveness/nested-rpit.rs b/tests/ui/borrowck/alias-liveness/nested-rpit.rs new file mode 100644 index 0000000000000..f927fe8c5d12a --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/nested-rpit.rs @@ -0,0 +1,77 @@ +//@ edition: 2024 +//@ compile-flags: --crate-type lib +//@ check-pass + +struct ScopeGuard +where + F: FnMut(&mut T), +{ + dropfn: F, + value: T, +} + +impl core::ops::Deref for ScopeGuard +where + F: FnMut(&mut T), +{ + type Target = T; + #[inline] + fn deref(&self) -> &T { + &self.value + } +} + +impl core::ops::DerefMut for ScopeGuard +where + F: FnMut(&mut T), +{ + #[inline] + fn deref_mut(&mut self) -> &mut T { + &mut self.value + } +} + +impl Drop for ScopeGuard +where + F: FnMut(&mut T), +{ + #[inline] + fn drop(&mut self) { + (self.dropfn)(&mut self.value); + } +} + +struct RawTableInner; + +impl RawTableInner { + fn prepare_resize<'a, A>( + &self, + alloc: &'a A, + ) -> Result, ()> + { + Ok(ScopeGuard { + value: RawTableInner, + dropfn: move |self_| {}, + }) + } + + fn resize_inner
( + &mut self, + alloc: &A, + hasher: &dyn Fn(&mut Self, usize) -> u64, + ) -> Result<(), ()> + { + let mut new_table = self.prepare_resize(alloc)?; + + for _ in 0..10 { + hasher(self, 0); + new_table.prepare_insert_index(); + } + + core::mem::swap(self, &mut new_table); + + Ok(()) + } + + fn prepare_insert_index(&mut self) {} +} diff --git a/tests/ui/impl-trait/alias-liveness/rpit-hidden-erased-unsoundness.edition2015.stderr b/tests/ui/borrowck/alias-liveness/rpit-hidden-erased-unsoundness.edition2015.stderr similarity index 100% rename from tests/ui/impl-trait/alias-liveness/rpit-hidden-erased-unsoundness.edition2015.stderr rename to tests/ui/borrowck/alias-liveness/rpit-hidden-erased-unsoundness.edition2015.stderr diff --git a/tests/ui/impl-trait/alias-liveness/rpit-hidden-erased-unsoundness.edition2024.stderr b/tests/ui/borrowck/alias-liveness/rpit-hidden-erased-unsoundness.edition2024.stderr similarity index 100% rename from tests/ui/impl-trait/alias-liveness/rpit-hidden-erased-unsoundness.edition2024.stderr rename to tests/ui/borrowck/alias-liveness/rpit-hidden-erased-unsoundness.edition2024.stderr diff --git a/tests/ui/impl-trait/alias-liveness/rpit-hidden-erased-unsoundness.rs b/tests/ui/borrowck/alias-liveness/rpit-hidden-erased-unsoundness.rs similarity index 100% rename from tests/ui/impl-trait/alias-liveness/rpit-hidden-erased-unsoundness.rs rename to tests/ui/borrowck/alias-liveness/rpit-hidden-erased-unsoundness.rs diff --git a/tests/ui/impl-trait/hidden-lifetimes.edition2015.stderr b/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-for-swap.edition2015.stderr similarity index 94% rename from tests/ui/impl-trait/hidden-lifetimes.edition2015.stderr rename to tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-for-swap.edition2015.stderr index bc545b40c6a0e..ac839b0c5dc48 100644 --- a/tests/ui/impl-trait/hidden-lifetimes.edition2015.stderr +++ b/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-for-swap.edition2015.stderr @@ -1,5 +1,5 @@ error[E0700]: hidden type for `impl Swap + 'a` captures lifetime that does not appear in bounds - --> $DIR/hidden-lifetimes.rs:38:5 + --> $DIR/rpit-hide-lifetime-for-swap.rs:36:5 | LL | fn hide_ref<'a, 'b, T: 'static>(x: &'a mut &'b T) -> impl Swap + 'a { | -- -------------- opaque type defined here @@ -14,7 +14,7 @@ LL | fn hide_ref<'a, 'b, T: 'static>(x: &'a mut &'b T) -> impl Swap + 'a + use<' | ++++++++++++++++ error[E0700]: hidden type for `impl Swap + 'a` captures lifetime that does not appear in bounds - --> $DIR/hidden-lifetimes.rs:55:5 + --> $DIR/rpit-hide-lifetime-for-swap.rs:53:5 | LL | fn hide_rc_refcell<'a, 'b: 'a, T: 'static>(x: Rc>) -> impl Swap + 'a { | -- -------------- opaque type defined here diff --git a/tests/ui/impl-trait/hidden-lifetimes.edition2024.stderr b/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-for-swap.edition2024.stderr similarity index 91% rename from tests/ui/impl-trait/hidden-lifetimes.edition2024.stderr rename to tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-for-swap.edition2024.stderr index 5165e627022c1..17916dac8afd7 100644 --- a/tests/ui/impl-trait/hidden-lifetimes.edition2024.stderr +++ b/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-for-swap.edition2024.stderr @@ -1,5 +1,5 @@ error[E0515]: cannot return value referencing local variable `x` - --> $DIR/hidden-lifetimes.rs:46:5 + --> $DIR/rpit-hide-lifetime-for-swap.rs:44:5 | LL | hide_ref(&mut res).swap(hide_ref(&mut &x)); | -- `x` is borrowed here @@ -7,7 +7,7 @@ LL | res | ^^^ returns a value referencing data owned by the current function error[E0597]: `x` does not live long enough - --> $DIR/hidden-lifetimes.rs:62:38 + --> $DIR/rpit-hide-lifetime-for-swap.rs:60:38 | LL | let x = [1, 2, 3]; | - binding `x` declared here diff --git a/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-for-swap.polonius_alpha.stderr b/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-for-swap.polonius_alpha.stderr new file mode 100644 index 0000000000000..17916dac8afd7 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-for-swap.polonius_alpha.stderr @@ -0,0 +1,26 @@ +error[E0515]: cannot return value referencing local variable `x` + --> $DIR/rpit-hide-lifetime-for-swap.rs:44:5 + | +LL | hide_ref(&mut res).swap(hide_ref(&mut &x)); + | -- `x` is borrowed here +LL | res + | ^^^ returns a value referencing data owned by the current function + +error[E0597]: `x` does not live long enough + --> $DIR/rpit-hide-lifetime-for-swap.rs:60:38 + | +LL | let x = [1, 2, 3]; + | - binding `x` declared here +LL | let short = Rc::new(RefCell::new(&x)); + | ^^ borrowed value does not live long enough +LL | hide_rc_refcell(long.clone()).swap(hide_rc_refcell(short)); +LL | let res: &'static [i32; 3] = *long.borrow(); + | ----------------- type annotation requires that `x` is borrowed for `'static` +LL | res +LL | } + | - `x` dropped here while still borrowed + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0515, E0597. +For more information about an error, try `rustc --explain E0515`. diff --git a/tests/ui/impl-trait/hidden-lifetimes.rs b/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-for-swap.rs similarity index 88% rename from tests/ui/impl-trait/hidden-lifetimes.rs rename to tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-for-swap.rs index d113953c5bbc7..e03d4452dd085 100644 --- a/tests/ui/impl-trait/hidden-lifetimes.rs +++ b/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-for-swap.rs @@ -2,8 +2,6 @@ //@ ignore-compare-mode-polonius (explicit revisions) //@ [edition2015] edition: 2015 //@ [edition2024] edition: 2024 -//@ [polonius_alpha] known-bug: #153215 -//@ [polonius_alpha] check-pass //@ [polonius_alpha] edition: 2024 //@ [polonius_alpha] compile-flags: -Zpolonius=next @@ -43,7 +41,7 @@ fn dangle_ref() -> &'static [i32; 3] { let mut res = &[4, 5, 6]; let x = [1, 2, 3]; hide_ref(&mut res).swap(hide_ref(&mut &x)); - res //[edition2024]~ ERROR cannot return value referencing local variable `x` + res //[edition2024,polonius_alpha]~ ERROR cannot return value referencing local variable `x` } // Here we are hiding `'b` making the caller believe that `Rc>` @@ -59,7 +57,7 @@ fn hide_rc_refcell<'a, 'b: 'a, T: 'static>(x: Rc>) -> impl Swap + fn dangle_rc_refcell() -> &'static [i32; 3] { let long = Rc::new(RefCell::new(&[4, 5, 6])); let x = [1, 2, 3]; - let short = Rc::new(RefCell::new(&x)); //[edition2024]~ ERROR `x` does not live long enough + let short = Rc::new(RefCell::new(&x)); //[edition2024,polonius_alpha]~ ERROR `x` does not live long enough hide_rc_refcell(long.clone()).swap(hide_rc_refcell(short)); let res: &'static [i32; 3] = *long.borrow(); res diff --git a/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-in-type-arg-for-swap.edition2015.stderr b/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-in-type-arg-for-swap.edition2015.stderr new file mode 100644 index 0000000000000..aa7fc52d7d463 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-in-type-arg-for-swap.edition2015.stderr @@ -0,0 +1,11 @@ +error[E0515]: cannot return value referencing local variable `x` + --> $DIR/rpit-hide-lifetime-in-type-arg-for-swap.rs:33:5 + | +LL | hide(&lock, &mut res).swap(hide(&lock, &mut &x)); + | -- `x` is borrowed here +LL | res + | ^^^ returns a value referencing data owned by the current function + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-in-type-arg-for-swap.edition2024.stderr b/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-in-type-arg-for-swap.edition2024.stderr new file mode 100644 index 0000000000000..aa7fc52d7d463 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-in-type-arg-for-swap.edition2024.stderr @@ -0,0 +1,11 @@ +error[E0515]: cannot return value referencing local variable `x` + --> $DIR/rpit-hide-lifetime-in-type-arg-for-swap.rs:33:5 + | +LL | hide(&lock, &mut res).swap(hide(&lock, &mut &x)); + | -- `x` is borrowed here +LL | res + | ^^^ returns a value referencing data owned by the current function + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-in-type-arg-for-swap.polonius_alpha.stderr b/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-in-type-arg-for-swap.polonius_alpha.stderr new file mode 100644 index 0000000000000..aa7fc52d7d463 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-in-type-arg-for-swap.polonius_alpha.stderr @@ -0,0 +1,11 @@ +error[E0515]: cannot return value referencing local variable `x` + --> $DIR/rpit-hide-lifetime-in-type-arg-for-swap.rs:33:5 + | +LL | hide(&lock, &mut res).swap(hide(&lock, &mut &x)); + | -- `x` is borrowed here +LL | res + | ^^^ returns a value referencing data owned by the current function + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-in-type-arg-for-swap.rs b/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-in-type-arg-for-swap.rs new file mode 100644 index 0000000000000..35d51fe791a84 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpit-hide-lifetime-in-type-arg-for-swap.rs @@ -0,0 +1,38 @@ +//@ revisions: edition2015 edition2024 polonius_alpha +//@ ignore-compare-mode-polonius (explicit revisions) +//@ [edition2015] edition: 2015 +//@ [edition2024] edition: 2024 +//@ [polonius_alpha] edition: 2024 +//@ [polonius_alpha] compile-flags: -Zpolonius=next + +// Like rpit-hide-lifetime-for-swap, but the invariant lifetime is hidden +// inside a *type* argument of the opaque rather than being captured as a +// region argument. The hidden type can capture `U` (and thus all of its +// regions) because of the `U: 'a` bound, so the regions inside the type +// argument must be considered live too. + +trait Swap: Sized { + fn swap(self, other: Self); +} + +impl Swap for &mut T { + fn swap(self, other: Self) { + std::mem::swap(self, other); + } +} + +fn hide<'a, U: Swap + 'a>(_: &'a u8, x: U) -> impl Swap + 'a { + x +} + +fn dangle() -> &'static [i32; 3] { + let lock = 0u8; + let mut res = &[4, 5, 6]; + let x = [1, 2, 3]; + hide(&lock, &mut res).swap(hide(&lock, &mut &x)); + res //[edition2015,edition2024,polonius_alpha]~ ERROR cannot return value referencing local variable `x` +} + +fn main() { + println!("{:?}", dangle()); +} diff --git a/tests/ui/borrowck/alias-liveness/rpitit-hide-lifetime-for-swap.edition2015.stderr b/tests/ui/borrowck/alias-liveness/rpitit-hide-lifetime-for-swap.edition2015.stderr new file mode 100644 index 0000000000000..0c2e1d3c7817a --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpitit-hide-lifetime-for-swap.edition2015.stderr @@ -0,0 +1,11 @@ +error[E0515]: cannot return value referencing local variable `x` + --> $DIR/rpitit-hide-lifetime-for-swap.rs:31:5 + | +LL | h.hide_ref(&mut res).swap(h.hide_ref(&mut &x)); + | -- `x` is borrowed here +LL | res + | ^^^ returns a value referencing data owned by the current function + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/borrowck/alias-liveness/rpitit-hide-lifetime-for-swap.edition2024.stderr b/tests/ui/borrowck/alias-liveness/rpitit-hide-lifetime-for-swap.edition2024.stderr new file mode 100644 index 0000000000000..0c2e1d3c7817a --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpitit-hide-lifetime-for-swap.edition2024.stderr @@ -0,0 +1,11 @@ +error[E0515]: cannot return value referencing local variable `x` + --> $DIR/rpitit-hide-lifetime-for-swap.rs:31:5 + | +LL | h.hide_ref(&mut res).swap(h.hide_ref(&mut &x)); + | -- `x` is borrowed here +LL | res + | ^^^ returns a value referencing data owned by the current function + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/borrowck/alias-liveness/rpitit-hide-lifetime-for-swap.polonius_alpha.stderr b/tests/ui/borrowck/alias-liveness/rpitit-hide-lifetime-for-swap.polonius_alpha.stderr new file mode 100644 index 0000000000000..0c2e1d3c7817a --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpitit-hide-lifetime-for-swap.polonius_alpha.stderr @@ -0,0 +1,11 @@ +error[E0515]: cannot return value referencing local variable `x` + --> $DIR/rpitit-hide-lifetime-for-swap.rs:31:5 + | +LL | h.hide_ref(&mut res).swap(h.hide_ref(&mut &x)); + | -- `x` is borrowed here +LL | res + | ^^^ returns a value referencing data owned by the current function + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/borrowck/alias-liveness/rpitit-hide-lifetime-for-swap.rs b/tests/ui/borrowck/alias-liveness/rpitit-hide-lifetime-for-swap.rs new file mode 100644 index 0000000000000..ee20d00ae77cb --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpitit-hide-lifetime-for-swap.rs @@ -0,0 +1,38 @@ +//@ revisions: edition2015 edition2024 polonius_alpha +//@ ignore-compare-mode-polonius (explicit revisions) +//@ [edition2015] edition: 2015 +//@ [edition2024] edition: 2024 +//@ [polonius_alpha] edition: 2024 +//@ [polonius_alpha] compile-flags: -Zpolonius=next + +// Test to show what happens if we were not careful and allowed invariant +// lifetimes to escape though an impl trait. +// +// Specifically we swap a long lived and short lived reference, giving us a +// dangling pointer. + +trait Swap: Sized { + fn swap(self, other: Self); +} +impl Swap for &mut T { + fn swap(self, other: Self) { + std::mem::swap(self, other); + } +} +trait Hider { + fn hide_ref<'a, 'b, T: 'static>(&self, x: &'a mut &'b T) -> impl Swap + 'a { + x + } +} +fn dangle_ref(h: &H) -> &'static [i32; 3] { + let mut res = &[4, 5, 6]; + let x = [1, 2, 3]; + h.hide_ref(&mut res).swap(h.hide_ref(&mut &x)); + res //[edition2015,edition2024,polonius_alpha]~ ERROR cannot return value referencing local variable `x` +} +struct H; +impl Hider for H {} + +fn main() { + println!("{:?}", dangle_ref(&H)); +} diff --git a/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness-2.rs b/tests/ui/borrowck/alias-liveness/tait-hidden-erased-unsoundness-2.rs similarity index 100% rename from tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness-2.rs rename to tests/ui/borrowck/alias-liveness/tait-hidden-erased-unsoundness-2.rs diff --git a/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness-2.stderr b/tests/ui/borrowck/alias-liveness/tait-hidden-erased-unsoundness-2.stderr similarity index 100% rename from tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness-2.stderr rename to tests/ui/borrowck/alias-liveness/tait-hidden-erased-unsoundness-2.stderr diff --git a/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness.rs b/tests/ui/borrowck/alias-liveness/tait-hidden-erased-unsoundness.rs similarity index 100% rename from tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness.rs rename to tests/ui/borrowck/alias-liveness/tait-hidden-erased-unsoundness.rs diff --git a/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness.stderr b/tests/ui/borrowck/alias-liveness/tait-hidden-erased-unsoundness.stderr similarity index 100% rename from tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness.stderr rename to tests/ui/borrowck/alias-liveness/tait-hidden-erased-unsoundness.stderr diff --git a/tests/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr b/tests/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr index 62bebd53b14a6..6cf4e881adae8 100644 --- a/tests/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr +++ b/tests/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr @@ -1,8 +1,8 @@ error: unconstrained generic constant - --> $DIR/doesnt_unify_evaluatable.rs:9:13 + --> $DIR/doesnt_unify_evaluatable.rs:9:11 | LL | bar::<{ T::ASSOC }>(); - | ^^^^^^^^ + | ^^^^^^^^^^^^ | help: try adding a `where` bound | diff --git a/tests/ui/const-generics/mgca/adt_expr_arg_simple.rs b/tests/ui/const-generics/mgca/adt_expr_arg_simple.rs index 8470b933cadd2..b09d17fb11262 100644 --- a/tests/ui/const-generics/mgca/adt_expr_arg_simple.rs +++ b/tests/ui/const-generics/mgca/adt_expr_arg_simple.rs @@ -10,7 +10,6 @@ use Option::Some; fn foo>() {} - trait Trait { type const ASSOC: u32; } @@ -26,7 +25,7 @@ fn bar() { // this on the other hand is not allowed as `N + 1` is not a legal // const argument - foo::<{ Some:: { 0: N + 1 } }>(); + foo::<{ core::direct_const_arg!(Some:: { 0: N + 1 }) }>(); //~^ ERROR: complex const arguments must be placed inside of a `const` block // this also is not allowed as generic parameters cannot be used diff --git a/tests/ui/const-generics/mgca/adt_expr_arg_simple.stderr b/tests/ui/const-generics/mgca/adt_expr_arg_simple.stderr index 0060c94875b5c..8f02ce0f82315 100644 --- a/tests/ui/const-generics/mgca/adt_expr_arg_simple.stderr +++ b/tests/ui/const-generics/mgca/adt_expr_arg_simple.stderr @@ -1,11 +1,11 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/adt_expr_arg_simple.rs:29:30 + --> $DIR/adt_expr_arg_simple.rs:28:54 | -LL | foo::<{ Some:: { 0: N + 1 } }>(); - | ^^^^^ +LL | foo::<{ core::direct_const_arg!(Some:: { 0: N + 1 }) }>(); + | ^^^^^ error: generic parameters may not be used in const operations - --> $DIR/adt_expr_arg_simple.rs:34:38 + --> $DIR/adt_expr_arg_simple.rs:33:38 | LL | foo::<{ Some:: { 0: const { N + 1 } } }>(); | ^ diff --git a/tests/ui/const-generics/mgca/array-expr-complex.r1.stderr b/tests/ui/const-generics/mgca/array-expr-complex.r1.stderr index 0c931af8d8b1c..a226d7ff0c225 100644 --- a/tests/ui/const-generics/mgca/array-expr-complex.r1.stderr +++ b/tests/ui/const-generics/mgca/array-expr-complex.r1.stderr @@ -1,8 +1,8 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/array-expr-complex.rs:11:28 + --> $DIR/array-expr-complex.rs:11:52 | -LL | takes_array::<{ [1, 2, 1 + 2] }>(); - | ^^^^^ +LL | takes_array::<{ core::direct_const_arg!([1, 2, 1 + 2]) }>(); + | ^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/mgca/array-expr-complex.r2.stderr b/tests/ui/const-generics/mgca/array-expr-complex.r2.stderr index 335af9235e0c9..cc1e70c1d9a7a 100644 --- a/tests/ui/const-generics/mgca/array-expr-complex.r2.stderr +++ b/tests/ui/const-generics/mgca/array-expr-complex.r2.stderr @@ -1,8 +1,8 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/array-expr-complex.rs:14:21 + --> $DIR/array-expr-complex.rs:14:45 | -LL | takes_array::<{ [X; 3] }>(); - | ^^^^^^ +LL | takes_array::<{ core::direct_const_arg!([X; 3]) }>(); + | ^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/mgca/array-expr-complex.r3.stderr b/tests/ui/const-generics/mgca/array-expr-complex.r3.stderr index 02d74c1b5d0c5..cc52abe0e8fb8 100644 --- a/tests/ui/const-generics/mgca/array-expr-complex.r3.stderr +++ b/tests/ui/const-generics/mgca/array-expr-complex.r3.stderr @@ -1,8 +1,8 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/array-expr-complex.rs:17:21 + --> $DIR/array-expr-complex.rs:17:45 | -LL | takes_array::<{ [0; Y] }>(); - | ^^^^^^ +LL | takes_array::<{ core::direct_const_arg!([0; Y]) }>(); + | ^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/mgca/array-expr-complex.rs b/tests/ui/const-generics/mgca/array-expr-complex.rs index 26f4700b5885c..7f5a77fdff3df 100644 --- a/tests/ui/const-generics/mgca/array-expr-complex.rs +++ b/tests/ui/const-generics/mgca/array-expr-complex.rs @@ -8,13 +8,13 @@ fn takes_array() {} fn generic_caller() { // not supported yet #[cfg(r1)] - takes_array::<{ [1, 2, 1 + 2] }>(); + takes_array::<{ core::direct_const_arg!([1, 2, 1 + 2]) }>(); //[r1]~^ ERROR: complex const arguments must be placed inside of a `const` block #[cfg(r2)] - takes_array::<{ [X; 3] }>(); + takes_array::<{ core::direct_const_arg!([X; 3]) }>(); //[r2]~^ ERROR: complex const arguments must be placed inside of a `const` block #[cfg(r3)] - takes_array::<{ [0; Y] }>(); + takes_array::<{ core::direct_const_arg!([0; Y]) }>(); //[r3]~^ ERROR: complex const arguments must be placed inside of a `const` block } diff --git a/tests/ui/const-generics/mgca/array_expr_arg_complex.rs b/tests/ui/const-generics/mgca/array_expr_arg_complex.rs index 6d57e4f4b9686..4adb2bf6e429d 100644 --- a/tests/ui/const-generics/mgca/array_expr_arg_complex.rs +++ b/tests/ui/const-generics/mgca/array_expr_arg_complex.rs @@ -9,8 +9,8 @@ fn takes_array() {} fn takes_tuple_with_array() {} fn generic_caller() { - takes_array::<{ [N, N + 1] }>(); //~ ERROR complex const arguments must be placed inside of a `const` block - takes_tuple_with_array::<{ ([N, N + 1], N) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block + takes_array::<{ core::direct_const_arg!([N, N + 1]) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block + takes_tuple_with_array::<{ core::direct_const_arg!(([N, N + 1], N)) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block } fn main() {} diff --git a/tests/ui/const-generics/mgca/array_expr_arg_complex.stderr b/tests/ui/const-generics/mgca/array_expr_arg_complex.stderr index f40d4b5035d86..c7b13351d453c 100644 --- a/tests/ui/const-generics/mgca/array_expr_arg_complex.stderr +++ b/tests/ui/const-generics/mgca/array_expr_arg_complex.stderr @@ -1,14 +1,14 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/array_expr_arg_complex.rs:12:25 + --> $DIR/array_expr_arg_complex.rs:12:49 | -LL | takes_array::<{ [N, N + 1] }>(); - | ^^^^^ +LL | takes_array::<{ core::direct_const_arg!([N, N + 1]) }>(); + | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/array_expr_arg_complex.rs:13:37 + --> $DIR/array_expr_arg_complex.rs:13:61 | -LL | takes_tuple_with_array::<{ ([N, N + 1], N) }>(); - | ^^^^^ +LL | takes_tuple_with_array::<{ core::direct_const_arg!(([N, N + 1], N)) }>(); + | ^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/mgca/auxiliary/anon-const-def-id-on-const-arg-with-anon.rs b/tests/ui/const-generics/mgca/auxiliary/anon-const-def-id-on-const-arg-with-anon.rs new file mode 100644 index 0000000000000..c1ebfc18e0816 --- /dev/null +++ b/tests/ui/const-generics/mgca/auxiliary/anon-const-def-id-on-const-arg-with-anon.rs @@ -0,0 +1,11 @@ +#![feature(min_generic_const_args)] +#![allow(incomplete_features)] +pub struct S; +// this is a directly represented anon const... it's a bit weird. +// imagine `S<{ (2, const { 1 + 1 }) }>`. a directly represented tuple, containing an anon const. +// now, replace `(2, _)` with `_`. it's a directly represented anon const. +// this is different from const argument lowering failing to represent an argument directly and +// falling back to representing it as an anon const instead. +pub fn f() -> S<{ const { 1 + 1 } }> { + S +} diff --git a/tests/ui/const-generics/mgca/bad-const-arg-fn-154539.rs b/tests/ui/const-generics/mgca/bad-const-arg-fn-154539.rs index 7c7ffd9a9bd5f..e3d1f577d4f60 100644 --- a/tests/ui/const-generics/mgca/bad-const-arg-fn-154539.rs +++ b/tests/ui/const-generics/mgca/bad-const-arg-fn-154539.rs @@ -2,10 +2,11 @@ trait Iter< const FN: fn() = { - || { //~ ERROR complex const arguments must be placed inside of a `const` block + core::direct_const_arg!(|| { + //~^ ERROR complex const arguments must be placed inside of a `const` block use std::io::*; write!(_, "") - } + }) }, > { diff --git a/tests/ui/const-generics/mgca/bad-const-arg-fn-154539.stderr b/tests/ui/const-generics/mgca/bad-const-arg-fn-154539.stderr index 60774c4a3efea..96fcea9e906cf 100644 --- a/tests/ui/const-generics/mgca/bad-const-arg-fn-154539.stderr +++ b/tests/ui/const-generics/mgca/bad-const-arg-fn-154539.stderr @@ -1,10 +1,12 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/bad-const-arg-fn-154539.rs:5:9 + --> $DIR/bad-const-arg-fn-154539.rs:5:33 | -LL | / || { +LL | core::direct_const_arg!(|| { + | _________________________________^ +LL | | LL | | use std::io::*; LL | | write!(_, "") -LL | | } +LL | | }) | |_________^ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/mgca/bad-direct-const-arg.rs b/tests/ui/const-generics/mgca/bad-direct-const-arg.rs new file mode 100644 index 0000000000000..451806ca6e1db --- /dev/null +++ b/tests/ui/const-generics/mgca/bad-direct-const-arg.rs @@ -0,0 +1,8 @@ +//! Simple error message test, nothing special here +#![feature(min_generic_const_args)] + +fn main(x: core::direct_const_arg!(2)) { + //~^ ERROR expected type, found `direct_const_arg!()` constant + let _ = core::direct_const_arg!(2); + //~^ ERROR expected expression, found `direct_const_arg!()` constant +} diff --git a/tests/ui/const-generics/mgca/bad-direct-const-arg.stderr b/tests/ui/const-generics/mgca/bad-direct-const-arg.stderr new file mode 100644 index 0000000000000..b77791ad5b3ee --- /dev/null +++ b/tests/ui/const-generics/mgca/bad-direct-const-arg.stderr @@ -0,0 +1,14 @@ +error: expected expression, found `direct_const_arg!()` constant + --> $DIR/bad-direct-const-arg.rs:6:13 + | +LL | let _ = core::direct_const_arg!(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected type, found `direct_const_arg!()` constant + --> $DIR/bad-direct-const-arg.rs:4:12 + | +LL | fn main(x: core::direct_const_arg!(2)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/const-generics/mgca/direct-const-arg-feature-gate.rs b/tests/ui/const-generics/mgca/direct-const-arg-feature-gate.rs new file mode 100644 index 0000000000000..16ba349a29be6 --- /dev/null +++ b/tests/ui/const-generics/mgca/direct-const-arg-feature-gate.rs @@ -0,0 +1,5 @@ +fn foo(_: [(); core::direct_const_arg!(N)]) {} +//~^ ERROR use of unstable library feature `min_generic_const_args` +//~| ERROR expected expression, found `direct_const_arg!()` constant +//~| ERROR generic parameters may not be used in const operations +fn main() {} diff --git a/tests/ui/const-generics/mgca/direct-const-arg-feature-gate.stderr b/tests/ui/const-generics/mgca/direct-const-arg-feature-gate.stderr new file mode 100644 index 0000000000000..f5dc211c2f71a --- /dev/null +++ b/tests/ui/const-generics/mgca/direct-const-arg-feature-gate.stderr @@ -0,0 +1,28 @@ +error[E0658]: use of unstable library feature `min_generic_const_args` + --> $DIR/direct-const-arg-feature-gate.rs:1:32 + | +LL | fn foo(_: [(); core::direct_const_arg!(N)]) {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #132980 for more information + = help: add `#![feature(min_generic_const_args)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: generic parameters may not be used in const operations + --> $DIR/direct-const-arg-feature-gate.rs:1:56 + | +LL | fn foo(_: [(); core::direct_const_arg!(N)]) {} + | ^ cannot perform const operation using `N` + | + = help: const parameters may only be used as standalone arguments here, i.e. `N` + = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions + +error: expected expression, found `direct_const_arg!()` constant + --> $DIR/direct-const-arg-feature-gate.rs:1:32 + | +LL | fn foo(_: [(); core::direct_const_arg!(N)]) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/const-generics/mgca/direct-const-arg-multiple-exprs.rs b/tests/ui/const-generics/mgca/direct-const-arg-multiple-exprs.rs new file mode 100644 index 0000000000000..8c802e563db7c --- /dev/null +++ b/tests/ui/const-generics/mgca/direct-const-arg-multiple-exprs.rs @@ -0,0 +1,5 @@ +#![feature(min_generic_const_args)] +struct S; +fn foo(_: S) {} +//~^ ERROR direct_const_arg! takes 1 argument +fn main() {} diff --git a/tests/ui/const-generics/mgca/direct-const-arg-multiple-exprs.stderr b/tests/ui/const-generics/mgca/direct-const-arg-multiple-exprs.stderr new file mode 100644 index 0000000000000..6f54a563ffbfc --- /dev/null +++ b/tests/ui/const-generics/mgca/direct-const-arg-multiple-exprs.stderr @@ -0,0 +1,8 @@ +error: direct_const_arg! takes 1 argument + --> $DIR/direct-const-arg-multiple-exprs.rs:3:45 + | +LL | fn foo(_: S) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/mgca/double-wrapped-path-not-accidentally-stabilized.rs b/tests/ui/const-generics/mgca/double-wrapped-path-not-accidentally-stabilized.rs new file mode 100644 index 0000000000000..09bce5c7f9aa2 --- /dev/null +++ b/tests/ui/const-generics/mgca/double-wrapped-path-not-accidentally-stabilized.rs @@ -0,0 +1,15 @@ +//! When doing some refactorings in mGCA, it was easy to accidentally stabilize doubly-brace-wrapped +//! paths. Check to make sure we don't accidentally do so. +//! +//! Feel free to delete this test if/when mGCA is stabilized and we support this syntax on stable, +//! it's testing nothing useful beyond that point. + +fn f() {} + +fn g() { + f::<{ N }>(); // ok + f::<{ { N } }>(); + //~^ ERROR: generic parameters may not be used in const operations +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/double-wrapped-path-not-accidentally-stabilized.stderr b/tests/ui/const-generics/mgca/double-wrapped-path-not-accidentally-stabilized.stderr new file mode 100644 index 0000000000000..6168ec242a38c --- /dev/null +++ b/tests/ui/const-generics/mgca/double-wrapped-path-not-accidentally-stabilized.stderr @@ -0,0 +1,11 @@ +error: generic parameters may not be used in const operations + --> $DIR/double-wrapped-path-not-accidentally-stabilized.rs:11:13 + | +LL | f::<{ { N } }>(); + | ^ cannot perform const operation using `N` + | + = help: const parameters may only be used as standalone arguments here, i.e. `N` + = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/mgca/explicit_anon_consts.rs b/tests/ui/const-generics/mgca/explicit_anon_consts.rs index 2b9909b43dfbb..9b642b286fa3e 100644 --- a/tests/ui/const-generics/mgca/explicit_anon_consts.rs +++ b/tests/ui/const-generics/mgca/explicit_anon_consts.rs @@ -10,7 +10,7 @@ type Adt1 = Foo; type Adt2 = Foo<{ N }>; type Adt3 = Foo; //~^ ERROR: generic parameters may not be used in const operations -type Adt4 = Foo<{ 1 + 1 }>; +type Adt4 = Foo; //~^ ERROR: complex const arguments must be placed inside of a `const` block type Adt5 = Foo; @@ -18,7 +18,7 @@ type Arr = [(); N]; type Arr2 = [(); { N }]; type Arr3 = [(); const { N }]; //~^ ERROR: generic parameters may not be used in const operations -type Arr4 = [(); 1 + 1]; +type Arr4 = [(); core::direct_const_arg!(1 + 1)]; //~^ ERROR: complex const arguments must be placed inside of a `const` block type Arr5 = [(); const { 1 + 1 }]; @@ -27,7 +27,7 @@ fn repeats() -> [(); N] { let _2 = [(); { N }]; let _3 = [(); const { N }]; //~^ ERROR: generic parameters may not be used in const operations - let _4 = [(); 1 + 1]; + let _4 = [(); core::direct_const_arg!(1 + 1)]; //~^ ERROR: complex const arguments must be placed inside of a `const` block let _5 = [(); const { 1 + 1 }]; let _6: [(); const { N }] = todo!(); @@ -42,10 +42,10 @@ type const ITEM2: usize = { N }; type const ITEM3: usize = const { N }; //~^ ERROR: generic parameters may not be used in const operations -type const ITEM4: usize = { 1 + 1 }; +type const ITEM4: usize = core::direct_const_arg!(1 + 1); //~^ ERROR: complex const arguments must be placed inside of a `const` block -type const ITEM5: usize = const { 1 + 1}; +type const ITEM5: usize = const { 1 + 1 }; trait Trait { @@ -59,7 +59,7 @@ fn ace_bounds< T2: Trait, T3: Trait, //~^ ERROR: generic parameters may not be used in const operations - T4: Trait, + T4: Trait, //~^ ERROR: complex const arguments must be placed inside of a `const` block T5: Trait, >() {} @@ -68,6 +68,6 @@ struct Default1; struct Default2; struct Default3; //~^ ERROR: generic parameters may not be used in const operations -struct Default4; +struct Default4; //~^ ERROR: complex const arguments must be placed inside of a `const` block -struct Default5; +struct Default5; diff --git a/tests/ui/const-generics/mgca/explicit_anon_consts.stderr b/tests/ui/const-generics/mgca/explicit_anon_consts.stderr index f634ec1cf12e4..9ab6af010bf21 100644 --- a/tests/ui/const-generics/mgca/explicit_anon_consts.stderr +++ b/tests/ui/const-generics/mgca/explicit_anon_consts.stderr @@ -1,38 +1,38 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:13:35 + --> $DIR/explicit_anon_consts.rs:13:57 | -LL | type Adt4 = Foo<{ 1 + 1 }>; - | ^^^^^ +LL | type Adt4 = Foo; + | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:21:34 + --> $DIR/explicit_anon_consts.rs:21:58 | -LL | type Arr4 = [(); 1 + 1]; - | ^^^^^ +LL | type Arr4 = [(); core::direct_const_arg!(1 + 1)]; + | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:30:19 + --> $DIR/explicit_anon_consts.rs:30:43 | -LL | let _4 = [(); 1 + 1]; - | ^^^^^ +LL | let _4 = [(); core::direct_const_arg!(1 + 1)]; + | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:45:45 + --> $DIR/explicit_anon_consts.rs:45:67 | -LL | type const ITEM4: usize = { 1 + 1 }; - | ^^^^^ +LL | type const ITEM4: usize = core::direct_const_arg!(1 + 1); + | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:62:25 + --> $DIR/explicit_anon_consts.rs:62:49 | -LL | T4: Trait, - | ^^^^^ +LL | T4: Trait, + | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:71:52 + --> $DIR/explicit_anon_consts.rs:71:76 | -LL | struct Default4; - | ^^^^^ +LL | struct Default4; + | ^^^^^ error: generic parameters may not be used in const operations --> $DIR/explicit_anon_consts.rs:42:51 diff --git a/tests/ui/const-generics/mgca/mixed-direct-anon-expression-diagnostics.rs b/tests/ui/const-generics/mgca/mixed-direct-anon-expression-diagnostics.rs new file mode 100644 index 0000000000000..1653fd63ed3f8 --- /dev/null +++ b/tests/ui/const-generics/mgca/mixed-direct-anon-expression-diagnostics.rs @@ -0,0 +1,17 @@ +//! Diagnostics for expressions that contain things that must be a mGCA direct expression, *and* +//! things that must be an anon const, are currently less than ideal. This test merely asserts the +//! current (bad) state of diagnostics, so we can track improvements over time. + +#![feature(min_generic_const_args, min_adt_const_params)] +#![allow(incomplete_features)] + +fn f() {} + +fn g() { + f::<{ (N, 1 + 1) }>(); + //~^ ERROR: generic parameters may not be used in const operations + f::<{ core::direct_const_arg!((N, 1 + 1)) }>(); + //~^ ERROR: complex const arguments must be placed inside of a `const` block +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/mixed-direct-anon-expression-diagnostics.stderr b/tests/ui/const-generics/mgca/mixed-direct-anon-expression-diagnostics.stderr new file mode 100644 index 0000000000000..ae297de108493 --- /dev/null +++ b/tests/ui/const-generics/mgca/mixed-direct-anon-expression-diagnostics.stderr @@ -0,0 +1,16 @@ +error: complex const arguments must be placed inside of a `const` block + --> $DIR/mixed-direct-anon-expression-diagnostics.rs:13:39 + | +LL | f::<{ core::direct_const_arg!((N, 1 + 1)) }>(); + | ^^^^^ + +error: generic parameters may not be used in const operations + --> $DIR/mixed-direct-anon-expression-diagnostics.rs:11:12 + | +LL | f::<{ (N, 1 + 1) }>(); + | ^ + | + = help: add `#![feature(generic_const_args)]` to allow generic expressions as the RHS of const items + +error: aborting due to 2 previous errors + diff --git a/tests/ui/const-generics/mgca/serialized-direct-const-with-anon-const-body.rs b/tests/ui/const-generics/mgca/serialized-direct-const-with-anon-const-body.rs new file mode 100644 index 0000000000000..985e14fddb676 --- /dev/null +++ b/tests/ui/const-generics/mgca/serialized-direct-const-with-anon-const-body.rs @@ -0,0 +1,16 @@ +//@ check-pass +//@ aux-build:anon-const-def-id-on-const-arg-with-anon.rs +//! The DefCollector sometimes generates "fake" DefKind::AnonConst DefIds for AST AnonConsts that +//! are not actually lowered to HIR AnonConsts, and so the DefId is placed on a hir::Node::ConstArg +//! (just to place it *somewhere*). These "fake" DefIds should not be serialized. Previously, the +//! logic to skip serializing them was incorrect (we were still serializing fake DefIds for +//! `ConstArg(ConstArgKind::Anon)` if the directly represented expression contained within it +//! *another*, unrelated, anon const). This test checks that case, a directly-represented +//! fake-anon-const directly containing another anon const. + +#![feature(min_generic_const_args)] +#![allow(incomplete_features)] +extern crate anon_const_def_id_on_const_arg_with_anon; +fn main() { + anon_const_def_id_on_const_arg_with_anon::f(); +} diff --git a/tests/ui/const-generics/mgca/tuple_ctor_complex_args.rs b/tests/ui/const-generics/mgca/tuple_ctor_complex_args.rs index 2e39f8952b11d..460c8af840a1c 100644 --- a/tests/ui/const-generics/mgca/tuple_ctor_complex_args.rs +++ b/tests/ui/const-generics/mgca/tuple_ctor_complex_args.rs @@ -9,7 +9,7 @@ struct Point(u32, u32); fn with_point() {} fn test() { - with_point::<{ Point(N + 1, N) }>(); + with_point::<{ core::direct_const_arg!(Point(N + 1, N)) }>(); //~^ ERROR complex const arguments must be placed inside of a `const` block with_point::<{ Point(const { N + 1 }, N) }>(); diff --git a/tests/ui/const-generics/mgca/tuple_ctor_complex_args.stderr b/tests/ui/const-generics/mgca/tuple_ctor_complex_args.stderr index 3a873ec33fb19..a4e7cb94c57c3 100644 --- a/tests/ui/const-generics/mgca/tuple_ctor_complex_args.stderr +++ b/tests/ui/const-generics/mgca/tuple_ctor_complex_args.stderr @@ -1,8 +1,8 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/tuple_ctor_complex_args.rs:12:26 + --> $DIR/tuple_ctor_complex_args.rs:12:50 | -LL | with_point::<{ Point(N + 1, N) }>(); - | ^^^^^ +LL | with_point::<{ core::direct_const_arg!(Point(N + 1, N)) }>(); + | ^^^^^ error: generic parameters may not be used in const operations --> $DIR/tuple_ctor_complex_args.rs:15:34 diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_complex.rs b/tests/ui/const-generics/mgca/tuple_expr_arg_complex.rs index d7cab17bad124..0d99b8ae345d6 100644 --- a/tests/ui/const-generics/mgca/tuple_expr_arg_complex.rs +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_complex.rs @@ -9,11 +9,11 @@ fn takes_tuple() {} fn takes_nested_tuple() {} fn generic_caller() { - takes_tuple::<{ (N, N + 1) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block - takes_tuple::<{ (N, T::ASSOC + 1) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block + takes_tuple::<{ core::direct_const_arg!((N, N + 1)) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block + takes_tuple::<{ core::direct_const_arg!((N, T::ASSOC + 1)) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block - takes_nested_tuple::<{ (N, (N, N + 1)) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block - takes_nested_tuple::<{ (N, (N, const { N + 1 })) }>(); //~ ERROR generic parameters may not be used in const operations + takes_nested_tuple::<{ core::direct_const_arg!((N, (N, N + 1))) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block + takes_nested_tuple::<{ core::direct_const_arg!((N, (N, const { N + 1 }))) }>(); //~ ERROR generic parameters may not be used in const operations } fn main() {} diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_complex.stderr b/tests/ui/const-generics/mgca/tuple_expr_arg_complex.stderr index a9d412964da29..4bed120284c08 100644 --- a/tests/ui/const-generics/mgca/tuple_expr_arg_complex.stderr +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_complex.stderr @@ -1,26 +1,26 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/tuple_expr_arg_complex.rs:12:25 + --> $DIR/tuple_expr_arg_complex.rs:12:49 | -LL | takes_tuple::<{ (N, N + 1) }>(); - | ^^^^^ +LL | takes_tuple::<{ core::direct_const_arg!((N, N + 1)) }>(); + | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/tuple_expr_arg_complex.rs:13:25 + --> $DIR/tuple_expr_arg_complex.rs:13:49 | -LL | takes_tuple::<{ (N, T::ASSOC + 1) }>(); - | ^^^^^^^^^^^^ +LL | takes_tuple::<{ core::direct_const_arg!((N, T::ASSOC + 1)) }>(); + | ^^^^^^^^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/tuple_expr_arg_complex.rs:15:36 + --> $DIR/tuple_expr_arg_complex.rs:15:60 | -LL | takes_nested_tuple::<{ (N, (N, N + 1)) }>(); - | ^^^^^ +LL | takes_nested_tuple::<{ core::direct_const_arg!((N, (N, N + 1))) }>(); + | ^^^^^ error: generic parameters may not be used in const operations - --> $DIR/tuple_expr_arg_complex.rs:16:44 + --> $DIR/tuple_expr_arg_complex.rs:16:68 | -LL | takes_nested_tuple::<{ (N, (N, const { N + 1 })) }>(); - | ^ +LL | takes_nested_tuple::<{ core::direct_const_arg!((N, (N, const { N + 1 }))) }>(); + | ^ | = help: add `#![feature(generic_const_args)]` to allow generic expressions as the RHS of const items diff --git a/tests/ui/const-generics/mgca/type-const-free-anon-const-mismatch.stderr b/tests/ui/const-generics/mgca/type-const-free-anon-const-mismatch.stderr index d0339d09cdc7a..52ef108a84dbf 100644 --- a/tests/ui/const-generics/mgca/type-const-free-anon-const-mismatch.stderr +++ b/tests/ui/const-generics/mgca/type-const-free-anon-const-mismatch.stderr @@ -4,7 +4,7 @@ error[E0284]: type annotations needed LL | type const X: usize = const { N }; | ^^^^^^^^^^^^^^^^^^^ cannot infer the value of the constant `_` | - = note: cannot satisfy `X::{constant#0} == _` + = note: cannot satisfy `X::{constant#0}::{constant#0} == _` error: the constant `"this isn't a usize"` is not of type `usize` --> $DIR/type-const-free-anon-const-mismatch.rs:8:1 diff --git a/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.next.stderr b/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.next.stderr index d96726829ee0b..3ef6ede90d933 100644 --- a/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.next.stderr +++ b/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.next.stderr @@ -10,7 +10,7 @@ error[E0284]: type annotations needed LL | fn f() -> [u8; const { N }] {} | ^^^^^^^^^^^^^^^^^ cannot infer the value of the constant `_` | - = note: cannot satisfy `f::{constant#0} == _` + = note: cannot satisfy `f::{constant#0}::{constant#0} == _` error[E0308]: mismatched types --> $DIR/type-const-free-value-type-mismatch.rs:11:11 diff --git a/tests/ui/const-generics/mgca/type-const-inherent-value-type-mismatch.next.stderr b/tests/ui/const-generics/mgca/type-const-inherent-value-type-mismatch.next.stderr index eeabde2d06320..551a1d496e910 100644 --- a/tests/ui/const-generics/mgca/type-const-inherent-value-type-mismatch.next.stderr +++ b/tests/ui/const-generics/mgca/type-const-inherent-value-type-mismatch.next.stderr @@ -4,7 +4,7 @@ error[E0284]: type annotations needed LL | fn f() -> [u8; const { Struct::N }] {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer the value of the constant `_` | - = note: cannot satisfy `f::{constant#0} == _` + = note: cannot satisfy `f::{constant#0}::{constant#0} == _` error: the constant `"this isn't a usize"` is not of type `usize` --> $DIR/type-const-inherent-value-type-mismatch.rs:13:5 diff --git a/tests/ui/const-generics/mgca/type-const-value-type-mismatch.next.stderr b/tests/ui/const-generics/mgca/type-const-value-type-mismatch.next.stderr index 123c442a3938d..748ffe1294e0a 100644 --- a/tests/ui/const-generics/mgca/type-const-value-type-mismatch.next.stderr +++ b/tests/ui/const-generics/mgca/type-const-value-type-mismatch.next.stderr @@ -16,7 +16,7 @@ error[E0284]: type annotations needed LL | fn arr() -> [u8; const { Self::LEN }] {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer the value of the constant `_` | - = note: cannot satisfy `::arr::{constant#0} == _` + = note: cannot satisfy `::arr::{constant#0}::{constant#0} == _` error[E0308]: mismatched types --> $DIR/type-const-value-type-mismatch.rs:21:17 diff --git a/tests/ui/consts/assoc-const-elided-lifetime.rs b/tests/ui/consts/assoc-const-elided-lifetime.rs index 10cd33a8fed59..3fdd725d32a31 100644 --- a/tests/ui/consts/assoc-const-elided-lifetime.rs +++ b/tests/ui/consts/assoc-const-elided-lifetime.rs @@ -1,4 +1,4 @@ -#![deny(elided_lifetimes_in_associated_constant)] +//@ check-pass use std::marker::PhantomData; @@ -8,12 +8,8 @@ struct Foo<'a> { impl<'a> Foo<'a> { const FOO: Foo<'_> = Foo { x: PhantomData::<&()> }; - //~^ ERROR `'_` cannot be used here - //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! const BAR: &() = &(); - //~^ ERROR `&` without an explicit lifetime name cannot be used here - //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } fn main() {} diff --git a/tests/ui/consts/assoc-const-elided-lifetime.stderr b/tests/ui/consts/assoc-const-elided-lifetime.stderr deleted file mode 100644 index 6277b079bdac7..0000000000000 --- a/tests/ui/consts/assoc-const-elided-lifetime.stderr +++ /dev/null @@ -1,44 +0,0 @@ -error: `'_` cannot be used here - --> $DIR/assoc-const-elided-lifetime.rs:10:20 - | -LL | const FOO: Foo<'_> = Foo { x: PhantomData::<&()> }; - | ^^ - | -note: cannot automatically infer `'static` because of other lifetimes in scope - --> $DIR/assoc-const-elided-lifetime.rs:9:6 - | -LL | impl<'a> Foo<'a> { - | ^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #115010 -note: the lint level is defined here - --> $DIR/assoc-const-elided-lifetime.rs:1:9 - | -LL | #![deny(elided_lifetimes_in_associated_constant)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: use the `'static` lifetime - | -LL - const FOO: Foo<'_> = Foo { x: PhantomData::<&()> }; -LL + const FOO: Foo<'static> = Foo { x: PhantomData::<&()> }; - | - -error: `&` without an explicit lifetime name cannot be used here - --> $DIR/assoc-const-elided-lifetime.rs:14:16 - | -LL | const BAR: &() = &(); - | ^ - | -note: cannot automatically infer `'static` because of other lifetimes in scope - --> $DIR/assoc-const-elided-lifetime.rs:9:6 - | -LL | impl<'a> Foo<'a> { - | ^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #115010 -help: use the `'static` lifetime - | -LL | const BAR: &'static () = &(); - | +++++++ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/consts/static-default-lifetime/elided-lifetime.rs b/tests/ui/consts/static-default-lifetime/elided-lifetime.rs index 989de389180a1..87ca5e082c032 100644 --- a/tests/ui/consts/static-default-lifetime/elided-lifetime.rs +++ b/tests/ui/consts/static-default-lifetime/elided-lifetime.rs @@ -1,11 +1,9 @@ -#![deny(elided_lifetimes_in_associated_constant)] +//@check-pass struct Foo<'a>(&'a ()); impl Foo<'_> { const STATIC: &str = ""; - //~^ ERROR `&` without an explicit lifetime name cannot be used here - //~| WARN this was previously accepted by the compiler but is being phased out } trait Bar { @@ -14,9 +12,6 @@ trait Bar { impl Bar for Foo<'_> { const STATIC: &str = ""; - //~^ ERROR `&` without an explicit lifetime name cannot be used here - //~| WARN this was previously accepted by the compiler but is being phased out - //~| ERROR lifetime parameters or bounds on associated constant `STATIC` do not match the trait declaration } fn main() {} diff --git a/tests/ui/consts/static-default-lifetime/elided-lifetime.stderr b/tests/ui/consts/static-default-lifetime/elided-lifetime.stderr deleted file mode 100644 index 370e6655d8607..0000000000000 --- a/tests/ui/consts/static-default-lifetime/elided-lifetime.stderr +++ /dev/null @@ -1,53 +0,0 @@ -error: `&` without an explicit lifetime name cannot be used here - --> $DIR/elided-lifetime.rs:6:19 - | -LL | const STATIC: &str = ""; - | ^ - | -note: cannot automatically infer `'static` because of other lifetimes in scope - --> $DIR/elided-lifetime.rs:5:10 - | -LL | impl Foo<'_> { - | ^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #115010 -note: the lint level is defined here - --> $DIR/elided-lifetime.rs:1:9 - | -LL | #![deny(elided_lifetimes_in_associated_constant)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: use the `'static` lifetime - | -LL | const STATIC: &'static str = ""; - | +++++++ - -error: `&` without an explicit lifetime name cannot be used here - --> $DIR/elided-lifetime.rs:16:19 - | -LL | const STATIC: &str = ""; - | ^ - | -note: cannot automatically infer `'static` because of other lifetimes in scope - --> $DIR/elided-lifetime.rs:15:18 - | -LL | impl Bar for Foo<'_> { - | ^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #115010 -help: use the `'static` lifetime - | -LL | const STATIC: &'static str = ""; - | +++++++ - -error[E0195]: lifetime parameters or bounds on associated constant `STATIC` do not match the trait declaration - --> $DIR/elided-lifetime.rs:16:17 - | -LL | const STATIC: &str; - | - lifetimes in impl do not match this associated constant in trait -... -LL | const STATIC: &str = ""; - | ^ lifetimes do not match associated constant in trait - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0195`. diff --git a/tests/ui/consts/static-default-lifetime/generic-associated-const.rs b/tests/ui/consts/static-default-lifetime/generic-associated-const.rs index 8fabaa43f5a27..04e7e3d5f7cec 100644 --- a/tests/ui/consts/static-default-lifetime/generic-associated-const.rs +++ b/tests/ui/consts/static-default-lifetime/generic-associated-const.rs @@ -1,18 +1,16 @@ -#![deny(elided_lifetimes_in_associated_constant)] +//@ check-pass + #![feature(generic_const_items)] struct A; impl A { const GAC_TYPE: &str = ""; const GAC_LIFETIME<'a>: &str = ""; - //~^ ERROR `&` without an explicit lifetime name cannot be used here - //~| WARN this was previously accepted by the compiler but is being phased out } trait Trait { const GAC_TYPE: &str = ""; const GAC_LIFETIME<'a>: &str = ""; - //~^ ERROR missing lifetime specifier } fn main() {} diff --git a/tests/ui/consts/static-default-lifetime/generic-associated-const.stderr b/tests/ui/consts/static-default-lifetime/generic-associated-const.stderr deleted file mode 100644 index fe858d685f7fa..0000000000000 --- a/tests/ui/consts/static-default-lifetime/generic-associated-const.stderr +++ /dev/null @@ -1,37 +0,0 @@ -error[E0106]: missing lifetime specifier - --> $DIR/generic-associated-const.rs:14:29 - | -LL | const GAC_LIFETIME<'a>: &str = ""; - | ^ expected named lifetime parameter - | -help: consider using the `'a` lifetime - | -LL | const GAC_LIFETIME<'a>: &'a str = ""; - | ++ - -error: `&` without an explicit lifetime name cannot be used here - --> $DIR/generic-associated-const.rs:7:29 - | -LL | const GAC_LIFETIME<'a>: &str = ""; - | ^ - | -note: cannot automatically infer `'static` because of other lifetimes in scope - --> $DIR/generic-associated-const.rs:7:24 - | -LL | const GAC_LIFETIME<'a>: &str = ""; - | ^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #115010 -note: the lint level is defined here - --> $DIR/generic-associated-const.rs:1:9 - | -LL | #![deny(elided_lifetimes_in_associated_constant)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: use the `'static` lifetime - | -LL | const GAC_LIFETIME<'a>: &'static str = ""; - | +++++++ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0106`. diff --git a/tests/ui/consts/static-default-lifetime/static-trait-impl.rs b/tests/ui/consts/static-default-lifetime/static-trait-impl.rs index ecc163aecbf1a..d146034114b96 100644 --- a/tests/ui/consts/static-default-lifetime/static-trait-impl.rs +++ b/tests/ui/consts/static-default-lifetime/static-trait-impl.rs @@ -1,4 +1,4 @@ -#![deny(elided_lifetimes_in_associated_constant)] +//@ check-pass trait Bar<'a> { const STATIC: &'a str; @@ -7,9 +7,6 @@ trait Bar<'a> { struct A; impl Bar<'_> for A { const STATIC: &str = ""; - //~^ ERROR `&` without an explicit lifetime name cannot be used here - //~| WARN this was previously accepted by the compiler but is being phased out - //~| ERROR lifetime parameters or bounds on associated constant `STATIC` do not match the trait declaration } struct B; @@ -19,12 +16,10 @@ impl Bar<'static> for B { struct C; impl Bar<'_> for C { - // make ^^ not cause const STATIC: &'static str = { struct B; impl Bar<'static> for B { const STATIC: &str = ""; - // ^ to emit a future incompat warning } "" }; diff --git a/tests/ui/consts/static-default-lifetime/static-trait-impl.stderr b/tests/ui/consts/static-default-lifetime/static-trait-impl.stderr deleted file mode 100644 index ab82515162014..0000000000000 --- a/tests/ui/consts/static-default-lifetime/static-trait-impl.stderr +++ /dev/null @@ -1,35 +0,0 @@ -error: `&` without an explicit lifetime name cannot be used here - --> $DIR/static-trait-impl.rs:9:19 - | -LL | const STATIC: &str = ""; - | ^ - | -note: cannot automatically infer `'static` because of other lifetimes in scope - --> $DIR/static-trait-impl.rs:8:10 - | -LL | impl Bar<'_> for A { - | ^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #115010 -note: the lint level is defined here - --> $DIR/static-trait-impl.rs:1:9 - | -LL | #![deny(elided_lifetimes_in_associated_constant)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: use the `'static` lifetime - | -LL | const STATIC: &'static str = ""; - | +++++++ - -error[E0195]: lifetime parameters or bounds on associated constant `STATIC` do not match the trait declaration - --> $DIR/static-trait-impl.rs:9:17 - | -LL | const STATIC: &'a str; - | - lifetimes in impl do not match this associated constant in trait -... -LL | const STATIC: &str = ""; - | ^ lifetimes do not match associated constant in trait - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0195`. diff --git a/tests/ui/delegation/generics/const-predicates-ice-158675.rs b/tests/ui/delegation/generics/const-predicates-ice-158675.rs new file mode 100644 index 0000000000000..2e88ca8135534 --- /dev/null +++ b/tests/ui/delegation/generics/const-predicates-ice-158675.rs @@ -0,0 +1,30 @@ +#![feature(fn_delegation)] + +mod original_ice { + struct S; + + trait Trait { + fn fun(); + } + + impl S { + reuse Trait::, N>::fun; + //~^ ERROR: the constant `N` is not of type `bool` + } +} + +mod with_child_constants { + struct S; + + trait Trait { + fn fun(); + } + + impl S { + reuse Trait::, N>::fun::; + //~^ ERROR: the constant `N` is not of type `bool` + //~| ERROR: the constant `N` is not of type `char` + } +} + +fn main() {} diff --git a/tests/ui/delegation/generics/const-predicates-ice-158675.stderr b/tests/ui/delegation/generics/const-predicates-ice-158675.stderr new file mode 100644 index 0000000000000..6dbf017294822 --- /dev/null +++ b/tests/ui/delegation/generics/const-predicates-ice-158675.stderr @@ -0,0 +1,42 @@ +error: the constant `N` is not of type `bool` + --> $DIR/const-predicates-ice-158675.rs:11:29 + | +LL | reuse Trait::, N>::fun; + | ^ expected `bool`, found `usize` + | +note: required by a const generic parameter in `original_ice::Trait::fun` + --> $DIR/const-predicates-ice-158675.rs:6:20 + | +LL | trait Trait { + | ^^^^^^^^^^^^^ required by this const generic parameter in `Trait::fun` +LL | fn fun(); + | --- required by a bound in this associated function + +error: the constant `N` is not of type `bool` + --> $DIR/const-predicates-ice-158675.rs:24:29 + | +LL | reuse Trait::, N>::fun::; + | ^ expected `bool`, found `usize` + | +note: required by a const generic parameter in `with_child_constants::Trait::fun` + --> $DIR/const-predicates-ice-158675.rs:19:20 + | +LL | trait Trait { + | ^^^^^^^^^^^^^ required by this const generic parameter in `Trait::fun` +LL | fn fun(); + | --- required by a bound in this associated function + +error: the constant `N` is not of type `char` + --> $DIR/const-predicates-ice-158675.rs:24:39 + | +LL | reuse Trait::, N>::fun::; + | ^ expected `char`, found `usize` + | +note: required by a const generic parameter in `with_child_constants::Trait::fun` + --> $DIR/const-predicates-ice-158675.rs:20:16 + | +LL | fn fun(); + | ^^^^^^^^^^^^^ required by this const generic parameter in `Trait::fun` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/delegation/hir-crate-items-before-lowering-ices.ice_155125.stderr b/tests/ui/delegation/hir-crate-items-before-lowering-ices.ice_155125.stderr index d2f0e87ecb593..6675831bddc86 100644 --- a/tests/ui/delegation/hir-crate-items-before-lowering-ices.ice_155125.stderr +++ b/tests/ui/delegation/hir-crate-items-before-lowering-ices.ice_155125.stderr @@ -9,13 +9,14 @@ LL | reuse foo; = note: `foo` must be defined only once in the value namespace of this block error: complex const arguments must be placed inside of a `const` block - --> $DIR/hir-crate-items-before-lowering-ices.rs:10:13 + --> $DIR/hir-crate-items-before-lowering-ices.rs:10:37 | -LL | / { +LL | core::direct_const_arg!({ + | _____________________________________^ LL | | fn foo() {} LL | | reuse foo; LL | | 2 -LL | | }, +LL | | }), | |_____________^ error: aborting due to 2 previous errors diff --git a/tests/ui/delegation/hir-crate-items-before-lowering-ices.ice_155164.stderr b/tests/ui/delegation/hir-crate-items-before-lowering-ices.ice_155164.stderr index 34d1a92ccd225..6164dabe74bff 100644 --- a/tests/ui/delegation/hir-crate-items-before-lowering-ices.ice_155164.stderr +++ b/tests/ui/delegation/hir-crate-items-before-lowering-ices.ice_155164.stderr @@ -1,12 +1,13 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/hir-crate-items-before-lowering-ices.rs:47:13 + --> $DIR/hir-crate-items-before-lowering-ices.rs:47:37 | -LL | / { +LL | core::direct_const_arg!({ + | _____________________________________^ LL | | LL | | struct W; LL | | impl W { ... | -LL | | }, +LL | | }), | |_____________^ error: aborting due to 1 previous error diff --git a/tests/ui/delegation/hir-crate-items-before-lowering-ices.rs b/tests/ui/delegation/hir-crate-items-before-lowering-ices.rs index b9a7a73732cf3..07f5a1ad1712f 100644 --- a/tests/ui/delegation/hir-crate-items-before-lowering-ices.rs +++ b/tests/ui/delegation/hir-crate-items-before-lowering-ices.rs @@ -7,11 +7,11 @@ mod ice_155125 { struct S; impl S< - { //[ice_155125]~ ERROR: complex const arguments must be placed inside of a `const` block + core::direct_const_arg!({ //[ice_155125]~ ERROR: complex const arguments must be placed inside of a `const` block fn foo() {} reuse foo; //[ice_155125]~ ERROR: the name `foo` is defined multiple times 2 - }, + }), > { } @@ -44,13 +44,13 @@ mod ice_155128 { mod ice_155164 { struct X { inner: std::iter::Map< - { + core::direct_const_arg!({ //[ice_155164]~^ ERROR: complex const arguments must be placed inside of a `const` block struct W; impl W { reuse Iterator::fold; } - }, + }), F, >, } diff --git a/tests/ui/delegation/inside-const-body-ice-155300.rs b/tests/ui/delegation/inside-const-body-ice-155300.rs index 06addf7e5412d..2d13007bc807b 100644 --- a/tests/ui/delegation/inside-const-body-ice-155300.rs +++ b/tests/ui/delegation/inside-const-body-ice-155300.rs @@ -5,12 +5,13 @@ pub struct S; impl S< - { //~ ERROR: complex const arguments must be placed inside of a `const` block + core::direct_const_arg!({ + //~^ ERROR: complex const arguments must be placed inside of a `const` block fn foo() {} reuse foo::<> as bar; reuse bar; //~^ ERROR: the name `bar` is defined multiple times - }, + }), > { } diff --git a/tests/ui/delegation/inside-const-body-ice-155300.stderr b/tests/ui/delegation/inside-const-body-ice-155300.stderr index 0d2020c997200..f0cb830109f8a 100644 --- a/tests/ui/delegation/inside-const-body-ice-155300.stderr +++ b/tests/ui/delegation/inside-const-body-ice-155300.stderr @@ -1,5 +1,5 @@ error[E0428]: the name `bar` is defined multiple times - --> $DIR/inside-const-body-ice-155300.rs:11:13 + --> $DIR/inside-const-body-ice-155300.rs:12:13 | LL | reuse foo::<> as bar; | --------------------- previous definition of the value `bar` here @@ -9,14 +9,15 @@ LL | reuse bar; = note: `bar` must be defined only once in the value namespace of this block error: complex const arguments must be placed inside of a `const` block - --> $DIR/inside-const-body-ice-155300.rs:8:9 + --> $DIR/inside-const-body-ice-155300.rs:8:33 | -LL | / { +LL | core::direct_const_arg!({ + | _________________________________^ +LL | | LL | | fn foo() {} LL | | reuse foo::<> as bar; -LL | | reuse bar; -LL | | -LL | | }, +... | +LL | | }), | |_________^ error: aborting due to 2 previous errors diff --git a/tests/ui/diagnostic_namespace/on_const/generic_parameters_handling.current.stderr b/tests/ui/diagnostic_namespace/on_const/generic_parameters_handling.current.stderr new file mode 100644 index 0000000000000..f9e33ff4fac58 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_const/generic_parameters_handling.current.stderr @@ -0,0 +1,11 @@ +error[E0277]: the trait bound `X: const Y<_>` is not satisfied + --> $DIR/generic_parameters_handling.rs:22:6 + | +LL | .blah(); + | ^^^^ + | + = note: Self = X, Z = Z, A = u8, B = &str + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/on_const/generic_parameters_handling.next.stderr b/tests/ui/diagnostic_namespace/on_const/generic_parameters_handling.next.stderr new file mode 100644 index 0000000000000..f9e33ff4fac58 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_const/generic_parameters_handling.next.stderr @@ -0,0 +1,11 @@ +error[E0277]: the trait bound `X: const Y<_>` is not satisfied + --> $DIR/generic_parameters_handling.rs:22:6 + | +LL | .blah(); + | ^^^^ + | + = note: Self = X, Z = Z, A = u8, B = &str + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/on_const/generic_parameters_handling.rs b/tests/ui/diagnostic_namespace/on_const/generic_parameters_handling.rs new file mode 100644 index 0000000000000..32a0caf96988a --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_const/generic_parameters_handling.rs @@ -0,0 +1,25 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +#![crate_type = "lib"] +#![feature(diagnostic_on_const, const_trait_impl)] + +pub struct X { + field: (A, B), +} + +pub const trait Y { + fn blah(&self) {} +} + +#[diagnostic::on_const(note = "Self = {Self}, Z = {Z}, A = {A}, B = {B}")] +impl Y for X {} + +const _: () = { + X { + field: (42_u8, "hello"), + } + .blah(); + //~^ ERROR the trait bound `X: const Y<_>` is not satisfied [E0277] + //~| NOTE Self = X, Z = Z, A = u8, B = &str +}; diff --git a/tests/ui/diagnostic_namespace/on_const/malformed_format_literals.rs b/tests/ui/diagnostic_namespace/on_const/malformed_format_literals.rs new file mode 100644 index 0000000000000..0c36ce25febb5 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_const/malformed_format_literals.rs @@ -0,0 +1,25 @@ +#![crate_type = "lib"] +#![feature(diagnostic_on_const)] +#![feature(const_trait_impl)] + +pub struct X; + +const trait Y { + fn blah(&self) {} +} + +#[diagnostic::on_const( + message = "my message {Foo}", + //~^ WARN unknown parameter `Foo` + label = "my label {Bar}", + //~^ WARN unknown parameter `Bar` + note = "my label {Baz}", + //~^ WARN unknown parameter `Baz` +)] +impl Y for X {} + +const _: () = { + X {}.blah(); + //~^ ERROR my message {Foo} [E0277] + +}; diff --git a/tests/ui/diagnostic_namespace/on_const/malformed_format_literals.stderr b/tests/ui/diagnostic_namespace/on_const/malformed_format_literals.stderr new file mode 100644 index 0000000000000..48bdc2c239a85 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_const/malformed_format_literals.stderr @@ -0,0 +1,36 @@ +warning: unknown parameter `Foo` + --> $DIR/malformed_format_literals.rs:12:28 + | +LL | message = "my message {Foo}", + | ^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + = note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: unknown parameter `Bar` + --> $DIR/malformed_format_literals.rs:14:24 + | +LL | label = "my label {Bar}", + | ^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +warning: unknown parameter `Baz` + --> $DIR/malformed_format_literals.rs:16:23 + | +LL | note = "my label {Baz}", + | ^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +error[E0277]: my message {Foo} + --> $DIR/malformed_format_literals.rs:22:10 + | +LL | X {}.blah(); + | ^^^^ my label {Bar} + | + = note: my label {Baz} + +error: aborting due to 1 previous error; 3 warnings emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/eii/default/auxiliary/decl_with_default.rs b/tests/ui/eii/default/auxiliary/decl_with_default.rs index ba855cb854afd..8d962c19c94d9 100644 --- a/tests/ui/eii/default/auxiliary/decl_with_default.rs +++ b/tests/ui/eii/default/auxiliary/decl_with_default.rs @@ -1,3 +1,4 @@ +//@ no-prefer-dynamic #![crate_type = "rlib"] #![feature(extern_item_impls)] diff --git a/tests/ui/eii/default/auxiliary/impl1.rs b/tests/ui/eii/default/auxiliary/impl1.rs index 84edf24e12816..3510ea1eb3f27 100644 --- a/tests/ui/eii/default/auxiliary/impl1.rs +++ b/tests/ui/eii/default/auxiliary/impl1.rs @@ -1,3 +1,4 @@ +//@ no-prefer-dynamic //@ aux-build: decl_with_default.rs #![crate_type = "rlib"] #![feature(extern_item_impls)] diff --git a/tests/ui/eii/duplicate/auxiliary/dylib_default.rs b/tests/ui/eii/duplicate/auxiliary/dylib_default.rs new file mode 100644 index 0000000000000..d1136b0ebd636 --- /dev/null +++ b/tests/ui/eii/duplicate/auxiliary/dylib_default.rs @@ -0,0 +1,5 @@ +#![crate_type = "dylib"] +#![feature(extern_item_impls)] + +#[eii(eii1)] +fn decl1(x: u64) {} diff --git a/tests/ui/eii/duplicate/dylib_default_duplicate.rs b/tests/ui/eii/duplicate/dylib_default_duplicate.rs new file mode 100644 index 0000000000000..0ac3669715f85 --- /dev/null +++ b/tests/ui/eii/duplicate/dylib_default_duplicate.rs @@ -0,0 +1,20 @@ +//@ aux-build: dylib_default.rs +//@ needs-crate-type: dylib +//@ compile-flags: --emit link +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Regression test for https://github.com/rust-lang/rust/issues/156320. +// A default implementation from an upstream dylib has already been selected and +// must not be overridden by a downstream explicit implementation. +#![feature(extern_item_impls)] + +extern crate dylib_default; + +#[unsafe(dylib_default::eii1)] +fn other(x: u64) { + //~^ ERROR multiple implementations of `#[eii1]` + println!("1{x}"); +} + +fn main() {} diff --git a/tests/ui/eii/duplicate/dylib_default_duplicate.stderr b/tests/ui/eii/duplicate/dylib_default_duplicate.stderr new file mode 100644 index 0000000000000..04c6a13710e28 --- /dev/null +++ b/tests/ui/eii/duplicate/dylib_default_duplicate.stderr @@ -0,0 +1,15 @@ +error: multiple implementations of `#[eii1]` + --> $DIR/dylib_default_duplicate.rs:15:1 + | +LL | fn other(x: u64) { + | ^^^^^^^^^^^^^^^^ first implemented here in crate `dylib_default_duplicate` + | + ::: $DIR/auxiliary/dylib_default.rs:5:1 + | +LL | fn decl1(x: u64) {} + | ---------------- also implemented here in crate `dylib_default` + | + = help: an "externally implementable item" can only have a single implementation in the final artifact. When multiple implementations are found, also in different crates, they conflict + +error: aborting due to 1 previous error + diff --git a/tests/ui/impl-trait/alias-liveness/rpit-hide-lifetime-for-swap.edition2015.stderr b/tests/ui/impl-trait/alias-liveness/rpit-hide-lifetime-for-swap.edition2015.stderr deleted file mode 100644 index 48efb0ca558c0..0000000000000 --- a/tests/ui/impl-trait/alias-liveness/rpit-hide-lifetime-for-swap.edition2015.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error[E0700]: hidden type for `impl Swap + 'a` captures lifetime that does not appear in bounds - --> $DIR/rpit-hide-lifetime-for-swap.rs:26:5 - | -LL | fn hide<'a, 'b: 'a, T: 'static>(x: Rc>) -> impl Swap + 'a { - | -- -------------- opaque type defined here - | | - | hidden type `Rc>` captures the lifetime `'b` as defined here -LL | x - | ^ - | -help: add a `use<...>` bound to explicitly capture `'b` - | -LL | fn hide<'a, 'b: 'a, T: 'static>(x: Rc>) -> impl Swap + 'a + use<'a, 'b, T> { - | ++++++++++++++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/impl-trait/alias-liveness/rpit-hide-lifetime-for-swap.edition2024.stderr b/tests/ui/impl-trait/alias-liveness/rpit-hide-lifetime-for-swap.edition2024.stderr deleted file mode 100644 index e4f5475cdb0a6..0000000000000 --- a/tests/ui/impl-trait/alias-liveness/rpit-hide-lifetime-for-swap.edition2024.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[E0597]: `x` does not live long enough - --> $DIR/rpit-hide-lifetime-for-swap.rs:33:38 - | -LL | let x = [1, 2, 3]; - | - binding `x` declared here -LL | let short = Rc::new(RefCell::new(&x)); - | ^^ borrowed value does not live long enough -... -LL | let res: &'static [i32; 3] = *long.borrow(); - | ----------------- type annotation requires that `x` is borrowed for `'static` -LL | res -LL | } - | - `x` dropped here while still borrowed - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/impl-trait/alias-liveness/rpit-hide-lifetime-for-swap.rs b/tests/ui/impl-trait/alias-liveness/rpit-hide-lifetime-for-swap.rs deleted file mode 100644 index de5335be73dbe..0000000000000 --- a/tests/ui/impl-trait/alias-liveness/rpit-hide-lifetime-for-swap.rs +++ /dev/null @@ -1,42 +0,0 @@ -//@ revisions: edition2015 edition2024 polonius_alpha -//@ ignore-compare-mode-polonius (explicit revisions) -//@ [edition2015] edition: 2015 -//@ [edition2024] edition: 2024 -//@ [polonius_alpha] known-bug: #153215 -//@ [polonius_alpha] check-pass -//@ [polonius_alpha] edition: 2024 -//@ [polonius_alpha] compile-flags: -Zpolonius=next - -// This test should never pass! - -use std::cell::RefCell; -use std::rc::Rc; - -trait Swap: Sized { - fn swap(self, other: Self); -} - -impl Swap for Rc> { - fn swap(self, other: Self) { - >::swap(&self, &other); - } -} - -fn hide<'a, 'b: 'a, T: 'static>(x: Rc>) -> impl Swap + 'a { - x - //[edition2015]~^ ERROR hidden type for `impl Swap + 'a` captures lifetime that does not appear in bounds -} - -fn dangle() -> &'static [i32; 3] { - let long = Rc::new(RefCell::new(&[4, 5, 6])); - let x = [1, 2, 3]; - let short = Rc::new(RefCell::new(&x)); - //[edition2024]~^ ERROR `x` does not live long enough - hide(long.clone()).swap(hide(short)); - let res: &'static [i32; 3] = *long.borrow(); - res -} - -fn main() { - println!("{:?}", dangle()); -} diff --git a/tests/crashes/157568.rs b/tests/ui/nll/polonius/allow-backward-incompatible-drop-hint.rs similarity index 85% rename from tests/crashes/157568.rs rename to tests/ui/nll/polonius/allow-backward-incompatible-drop-hint.rs index 627bfbadf6bc4..b2a9f648809ff 100644 --- a/tests/crashes/157568.rs +++ b/tests/ui/nll/polonius/allow-backward-incompatible-drop-hint.rs @@ -1,5 +1,7 @@ -//@ known-bug: #157568 +// Test for issue #157568 //@ compile-flags: -Zpolonius +//@ check-pass + #![warn(rust_2024_compatibility)] pub struct F; impl std::fmt::Debug for F { @@ -7,4 +9,5 @@ impl std::fmt::Debug for F { f.debug_struct("F").finish_non_exhaustive() } } + fn main() {} diff --git a/tests/ui/splat/arg-count-ice-issue-158482.rs b/tests/ui/splat/arg-count-ice-issue-158482.rs new file mode 100644 index 0000000000000..27f036b8a5906 --- /dev/null +++ b/tests/ui/splat/arg-count-ice-issue-158482.rs @@ -0,0 +1,14 @@ +//! Checks that an incorrect number of arguments to splat doesn't panic. + +#![feature(splat)] +struct Foo {} +trait BarTrait { + fn trait_assoc(w: W, #[splat] _s: (u32, u8)); +} +impl BarTrait for Foo { + fn trait_assoc(_w: W, #[splat] _s: (u32, u8)) {} +} +fn main() { + Foo::trait_assoc() + //~^ ERROR: this splatted function takes 3 arguments, but 0 were provided +} diff --git a/tests/ui/splat/arg-count-ice-issue-158482.stderr b/tests/ui/splat/arg-count-ice-issue-158482.stderr new file mode 100644 index 0000000000000..b7f3a8070f2b2 --- /dev/null +++ b/tests/ui/splat/arg-count-ice-issue-158482.stderr @@ -0,0 +1,9 @@ +error[E0057]: this splatted function takes 3 arguments, but 0 were provided + --> $DIR/arg-count-ice-issue-158482.rs:12:5 + | +LL | Foo::trait_assoc() + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0057`. diff --git a/tests/ui/splat/reject-closure-issue-158605.rs b/tests/ui/splat/reject-closure-issue-158605.rs new file mode 100644 index 0000000000000..7464bab2a77d2 --- /dev/null +++ b/tests/ui/splat/reject-closure-issue-158605.rs @@ -0,0 +1,30 @@ +//! Checks that closures and rust-call functions can't be splatted. +//! This should be rejected until we decide on sensible semantics. + +#![feature(splat, unboxed_closures, tuple_trait)] +#![expect(incomplete_features)] + +use std::marker::Tuple; + +trait Trait: Tuple + Sized { + extern "rust-call" fn method(#[splat] self: Self); + //~^ ERROR: `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI +} + +impl Trait for (i32, i64) { + extern "rust-call" fn method(#[splat] self: Self) { + //~^ ERROR: `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + println!("{self:?}"); + } +} + +fn main() { + (|#[splat] x: i32| { + //~^ ERROR `#[splat]` is not allowed on closure arguments + println!("{x}"); + })(1); + + (1_i32, 2_i64).method(); + Trait::method(3_i32, 4_i64); + //~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument +} diff --git a/tests/ui/splat/reject-closure-issue-158605.stderr b/tests/ui/splat/reject-closure-issue-158605.stderr new file mode 100644 index 0000000000000..c23211459a0db --- /dev/null +++ b/tests/ui/splat/reject-closure-issue-158605.stderr @@ -0,0 +1,36 @@ +error: `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + --> $DIR/reject-closure-issue-158605.rs:10:5 + | +LL | extern "rust-call" fn method(#[splat] self: Self); + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^ + | + = help: remove `#[splat]` or change the ABI + +error: `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + --> $DIR/reject-closure-issue-158605.rs:15:5 + | +LL | extern "rust-call" fn method(#[splat] self: Self) { + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^ + | + = help: remove `#[splat]` or change the ABI + +error: `#[splat]` is not allowed on closure arguments + --> $DIR/reject-closure-issue-158605.rs:22:7 + | +LL | (|#[splat] x: i32| { + | _______^^^^^^^^_________^ +LL | | +LL | | println!("{x}"); +LL | | })(1); + | |_____^ + | + = help: remove `#[splat]` or turn the closure into a function + +error: functions with the "rust-call" ABI must take a single non-self tuple argument + --> $DIR/reject-closure-issue-158605.rs:28:26 + | +LL | Trait::method(3_i32, 4_i64); + | ^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/splat/splat-255-limit-fail.rs b/tests/ui/splat/splat-255-limit-fail.rs index 31767f11d999b..f19ce37bc4566 100644 --- a/tests/ui/splat/splat-255-limit-fail.rs +++ b/tests/ui/splat/splat-255-limit-fail.rs @@ -47,7 +47,7 @@ fn s_255_terminal( _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, - #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 255 + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 255 ) {} #[rustfmt::skip] @@ -68,7 +68,7 @@ fn s_256_terminal( _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, - #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 256 + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 256 ) {} #[rustfmt::skip] @@ -88,7 +88,7 @@ fn s_255_non_terminal( _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, - #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 255 + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 255 _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, ) {} @@ -110,7 +110,7 @@ fn s_256_non_terminal( _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, - #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 256 + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 256 _: A, ) {} diff --git a/tests/ui/splat/splat-255-limit-fail.stderr b/tests/ui/splat/splat-255-limit-fail.stderr index da62da8aa605b..a3a0b9cf3d350 100644 --- a/tests/ui/splat/splat-255-limit-fail.stderr +++ b/tests/ui/splat/splat-255-limit-fail.stderr @@ -1,32 +1,32 @@ -error: `#[splat]` is not supported on argument index 255 +error: `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 255 --> $DIR/splat-255-limit-fail.rs:50:5 | LL | #[splat] (_a, _b): (u32, i8), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | ^^^^^^^^ `#[splat]` is not supported here | = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list -error: `#[splat]` is not supported on argument index 256 +error: `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 256 --> $DIR/splat-255-limit-fail.rs:71:5 | LL | #[splat] (_a, _b): (u32, i8), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | ^^^^^^^^ `#[splat]` is not supported here | = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list -error: `#[splat]` is not supported on argument index 255 +error: `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 255 --> $DIR/splat-255-limit-fail.rs:91:5 | LL | #[splat] (_a, _b): (u32, i8), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | ^^^^^^^^ `#[splat]` is not supported here | = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list -error: `#[splat]` is not supported on argument index 256 +error: `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 256 --> $DIR/splat-255-limit-fail.rs:113:5 | LL | #[splat] (_a, _b): (u32, i8), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | ^^^^^^^^ `#[splat]` is not supported here | = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list diff --git a/tests/ui/splat/splat-async-fn-tuple-fail.rs b/tests/ui/splat/splat-async-fn-tuple-fail.rs index a1f67dfe606d6..308a6c4a2131e 100644 --- a/tests/ui/splat/splat-async-fn-tuple-fail.rs +++ b/tests/ui/splat/splat-async-fn-tuple-fail.rs @@ -8,7 +8,7 @@ async fn async_wrong_type(#[splat] _x: u32) {} //~^ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 async fn async_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} -//~^ ERROR multiple `#[splat]`s are not allowed in the same function +//~^ ERROR multiple `#[splat]`s are not allowed in the same function argument list fn main() { async_wrong_type(1u32); diff --git a/tests/ui/splat/splat-async-fn-tuple-fail.stderr b/tests/ui/splat/splat-async-fn-tuple-fail.stderr index e643b29c88c8a..7c73382d73a5a 100644 --- a/tests/ui/splat/splat-async-fn-tuple-fail.stderr +++ b/tests/ui/splat/splat-async-fn-tuple-fail.stderr @@ -1,8 +1,8 @@ -error: multiple `#[splat]`s are not allowed in the same function +error: multiple `#[splat]`s are not allowed in the same function argument list --> $DIR/splat-async-fn-tuple-fail.rs:10:28 | LL | async fn async_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^ ^^^^^^^^ | = help: remove `#[splat]` from all but one argument diff --git a/tests/ui/splat/splat-fn-ptr-cast.rs b/tests/ui/splat/splat-fn-ptr-cast.rs new file mode 100644 index 0000000000000..918e26c4dd741 --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-cast.rs @@ -0,0 +1,17 @@ +//@ run-fail +//! Test never type casts to splatted and non-splatted functions. + +#![allow(incomplete_features)] +#![feature(splat)] +#![allow(unused_features)] + +fn main() { + // Bug #158603 regression test variants + #[rustfmt::skip] + let _x: fn(#[splat] (f32,)) = None.unwrap(); + // FIXME(splat): causes an ICE until #158603 is fixed + //x(1.0); + + let x: fn((i32,)) = None.unwrap(); + x((1,)); +} diff --git a/tests/ui/splat/splat-fn-ptr-ptr-tuple.rs b/tests/ui/splat/splat-fn-ptr-ptr-tuple.rs new file mode 100644 index 0000000000000..321511f270915 --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-ptr-tuple.rs @@ -0,0 +1,42 @@ +//! Test using `#[splat]` on tuple arguments of pointers to pointers to simple functions. +//! Currently ICEs, but if we fix it, we'll want to know and update this test to pass. + +//@ failure-status: 101 + +//@ normalize-stderr: ".*error:.*compiler/([^:]+):\d{1,}:\d{1,}:(.*)" -> "error: compiler/$1:LL:CC:$2" +//@ normalize-stderr: "thread.*panicked at .*compiler.*" -> "" +//@ normalize-stderr: "note: rustc.*running on.*" -> "note: rustc {version} running on {platform}" +//@ normalize-stderr: "note: compiler flags.*\n\n" -> "" +//@ normalize-stderr: " +\d{1,}: .*\n" -> "" +//@ normalize-stderr: " + at .*\n" -> "" +//@ normalize-stderr: ".*omitted \d{1,} frames?.*\n" -> "" +//@ normalize-stderr: ".*note: Some details are omitted.*\n" -> "" +//@ normalize-stderr: ".*--> .*/splat-fn-ptr-tuple.rs:\d{1,}:\d{1,}.*\n" -> "" + +#![allow(incomplete_features)] +#![feature(splat)] + +fn tuple_args(#[splat] (_a, _b): (u32, i8)) {} + +fn splat_non_terminal_arg(#[splat] (_a, _b): (u32, i8), _c: f64) {} + +fn main() { + // FIXME(splat): not currently supported, can be supported when we no longer require a DefId in + // MIR lowering + // FIXME(rustfmt): the attribute gets deleted by rustfmt + #[rustfmt::skip] + let fn_pp: *const fn(#[splat] (u32, i8)) = tuple_args as *const fn(#[splat] (u32, i8)); + unsafe { + (*fn_pp)(1, 2); //~ ERROR splatted FnPtr side-tables are not yet implemented + // The ICE means that code after this line is not fully checked + (*fn_pp)(1u32, 2i8); + } + + #[rustfmt::skip] + let fn_pp: *const fn(#[splat] (u32, i8), f64) = + splat_non_terminal_arg as *const fn(#[splat] (u32, i8), f64); + unsafe { + (*fn_pp)(1, 2, 3.5); + (*fn_pp)(1u32, 2i8, 3.5f64); + } +} diff --git a/tests/ui/splat/splat-fn-ptr-ptr-tuple.stderr b/tests/ui/splat/splat-fn-ptr-ptr-tuple.stderr new file mode 100644 index 0000000000000..726561b366f80 --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-ptr-tuple.stderr @@ -0,0 +1,24 @@ +error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: splatted FnPtr side-tables are not yet implemented + --> $DIR/splat-fn-ptr-ptr-tuple.rs:30:9 + | +LL | (*fn_pp)(1, 2); + | ^^^^^^^^^^^^^^ + + + +Box +stack backtrace: + +note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md + +note: please make sure that you have updated to the latest nightly + +note: rustc {version} running on {platform} + +query stack during panic: +#0 [thir_body] building THIR for `main` +#1 [check_unsafety] unsafety-checking `main` +#2 [analysis] running analysis passes on crate `splat_fn_ptr_ptr_tuple` +end of query stack +error: aborting due to 1 previous error + diff --git a/tests/ui/splat/splat-fn-ptr-rust-call.rs b/tests/ui/splat/splat-fn-ptr-rust-call.rs new file mode 100644 index 0000000000000..a9fe48a459d8a --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-rust-call.rs @@ -0,0 +1,18 @@ +//! Test using `#[splat]` on tuple arguments of pointers to "rust-call" functions. +//! Currently ICEs at a later stage, but AST validation should catch it earlier. + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(unboxed_closures)] + +extern "rust-call" fn f(#[splat] _: ()) {} //~ ERROR `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + +fn main() { + // FIXME(rustfmt): the attribute gets deleted by rustfmt + #[rustfmt::skip] + let f2: extern "rust-call" fn(#[splat] ()) = f; //~ ERROR `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + // These errors could be confusing, but they're useful if the user meant to use "rust-call" + // instead of #[splat] + f(); //~ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument + f2(); //~ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument +} diff --git a/tests/ui/splat/splat-fn-ptr-rust-call.stderr b/tests/ui/splat/splat-fn-ptr-rust-call.stderr new file mode 100644 index 0000000000000..6eacdfab5faac --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-rust-call.stderr @@ -0,0 +1,30 @@ +error: `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + --> $DIR/splat-fn-ptr-rust-call.rs:8:1 + | +LL | extern "rust-call" fn f(#[splat] _: ()) {} + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^ + | + = help: remove `#[splat]` or change the ABI + +error: `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + --> $DIR/splat-fn-ptr-rust-call.rs:13:13 + | +LL | let f2: extern "rust-call" fn(#[splat] ()) = f; + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^ + | + = help: remove `#[splat]` or change the ABI + +error: functions with the "rust-call" ABI must take a single non-self tuple argument + --> $DIR/splat-fn-ptr-rust-call.rs:16:5 + | +LL | f(); + | ^^^ + +error: functions with the "rust-call" ABI must take a single non-self tuple argument + --> $DIR/splat-fn-ptr-rust-call.rs:17:5 + | +LL | f2(); + | ^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/splat/splat-fn-ptr-tuple-const.rs b/tests/ui/splat/splat-fn-ptr-tuple-const.rs new file mode 100644 index 0000000000000..faf8501cc650a --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-tuple-const.rs @@ -0,0 +1,34 @@ +//! Test using `#[splat]` on tuple arguments of generic function constants. +//! Currently ICEs (#158603), but if we fix it, we'll want to know and update this test to pass. + +//@ failure-status: 101 + +//@ normalize-stderr: ".*error:.*compiler/([^:]+):\d{1,}:\d{1,}:(.*)" -> "error: compiler/$1:LL:CC:$2" +//@ normalize-stderr: "thread.*panicked at .*compiler.*" -> "" +//@ normalize-stderr: "note: rustc.*running on.*" -> "note: rustc {version} running on {platform}" +//@ normalize-stderr: "note: compiler flags.*\n\n" -> "" +//@ normalize-stderr: " +\d{1,}: .*\n" -> "" +//@ normalize-stderr: " + at .*\n" -> "" +//@ normalize-stderr: ".*omitted \d{1,} frames?.*\n" -> "" +//@ normalize-stderr: ".*note: Some details are omitted.*\n" -> "" +//@ normalize-stderr: ".*--> .*/splat-fn-ptr-tuple.rs:\d{1,}:\d{1,}.*\n" -> "" + +#![allow(incomplete_features)] +#![feature(splat, tuple_trait)] + +use std::marker::Tuple; + +fn f(#[splat] args: Args) {} + +fn main() { + // FIXME(splat): not currently supported, can be supported when we no longer require a DefId in + // MIR lowering + // FIXME(rustfmt): the attribute gets deleted by rustfmt + #[rustfmt::skip] + const F2: fn(#[splat] (u8, u32)) = f::<(u8, u32)>; + const R2: () = F2(1, 2); //~ ERROR splatted FnPtr side-tables are not yet implemented + + #[rustfmt::skip] + const F1: fn(#[splat] ((u8, u32),)) = f::<((u8, u32),)>; + const R1: () = F1((1, 2)); //~ ERROR splatted FnPtr side-tables are not yet implemented +} diff --git a/tests/ui/splat/splat-fn-ptr-tuple-const.stderr b/tests/ui/splat/splat-fn-ptr-tuple-const.stderr new file mode 100644 index 0000000000000..1767782a9535e --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-tuple-const.stderr @@ -0,0 +1,52 @@ +error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: splatted FnPtr side-tables are not yet implemented + --> $DIR/splat-fn-ptr-tuple-const.rs:29:20 + | +LL | const R2: () = F2(1, 2); + | ^^^^^^^^ + + + +Box +stack backtrace: + +note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md + +note: please make sure that you have updated to the latest nightly + +note: rustc {version} running on {platform} + +query stack during panic: +#0 [thir_body] building THIR for `main::R2` +#1 [check_match] match-checking `main::R2` +#2 [mir_built] building MIR for `main::R2` +#3 [trivial_const] checking if `main::R2` is a trivial const +#4 [eval_to_const_value_raw] simplifying constant for the type system `main::R2` +#5 [analysis] running analysis passes on crate `splat_fn_ptr_tuple_const` +end of query stack +error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: splatted FnPtr side-tables are not yet implemented + --> $DIR/splat-fn-ptr-tuple-const.rs:33:20 + | +LL | const R1: () = F1((1, 2)); + | ^^^^^^^^^^ + + + +Box +stack backtrace: + +note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md + +note: please make sure that you have updated to the latest nightly + +note: rustc {version} running on {platform} + +query stack during panic: +#0 [thir_body] building THIR for `main::R1` +#1 [check_match] match-checking `main::R1` +#2 [mir_built] building MIR for `main::R1` +#3 [trivial_const] checking if `main::R1` is a trivial const +#4 [eval_to_const_value_raw] simplifying constant for the type system `main::R1` +#5 [analysis] running analysis passes on crate `splat_fn_ptr_tuple_const` +end of query stack +error: aborting due to 2 previous errors + diff --git a/tests/ui/splat/splat-fn-ptr-tuple.rs b/tests/ui/splat/splat-fn-ptr-tuple.rs index 297dbc0457794..395d30baedaaf 100644 --- a/tests/ui/splat/splat-fn-ptr-tuple.rs +++ b/tests/ui/splat/splat-fn-ptr-tuple.rs @@ -1,3 +1,6 @@ +//! Test using `#[splat]` on tuple arguments of pointers to simple functions. +//! Currently ICEs, but if we fix it, we'll want to know and update this test to pass. + //@ failure-status: 101 //@ normalize-stderr: ".*error:.*compiler/([^:]+):\d{1,}:\d{1,}:(.*)" -> "error: compiler/$1:LL:CC:$2" @@ -10,9 +13,6 @@ //@ normalize-stderr: ".*note: Some details are omitted.*\n" -> "" //@ normalize-stderr: ".*--> .*/splat-fn-ptr-tuple.rs:\d{1,}:\d{1,}.*\n" -> "" -//! Test using `#[splat]` on tuple arguments of simple functions. -//! Currently ICEs, but if we fix it, we'll want to know and update this test to pass. - #![allow(incomplete_features)] #![feature(splat)] @@ -24,10 +24,10 @@ fn main() { // FIXME(splat): not currently supported, can be supported when we no longer require a DefId in // MIR lowering // FIXME(rustfmt): the attribute gets deleted by rustfmt - // Functions #[rustfmt::skip] let fn_ptr: fn(#[splat] (u32, i8)) = tuple_args; - fn_ptr(1, 2); //~ ERROR no splatted def for function or method callee + fn_ptr(1, 2); //~ ERROR splatted FnPtr side-tables are not yet implemented + // The ICE means that code after this line is not fully checked fn_ptr(1u32, 2i8); // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? @@ -39,15 +39,8 @@ fn main() { fn_ptr(1, 2, 3.5); fn_ptr(1u32, 2i8, 3.5f64); - // Function pointers - #[rustfmt::skip] - let fn_ptr: *const fn(#[splat] (u32, i8)) = tuple_args as *const fn(#[splat] (u32, i8)); - (*fn_ptr)(1, 2); - (*fn_ptr)(1u32, 2i8); - + // Bug #158603 regression test #[rustfmt::skip] - let fn_ptr: *const fn(#[splat] (u32, i8), f64) = - splat_non_terminal_arg as *const fn(#[splat] (u32, i8), f64); - (*fn_ptr)(1, 2, 3.5); - (*fn_ptr)(1u32, 2i8, 3.5f64); + let x: fn(#[splat] (i32,)) = None.unwrap(); + x(1); } diff --git a/tests/ui/splat/splat-fn-ptr-tuple.stderr b/tests/ui/splat/splat-fn-ptr-tuple.stderr index a69175bd5e886..4cc861cafe968 100644 --- a/tests/ui/splat/splat-fn-ptr-tuple.stderr +++ b/tests/ui/splat/splat-fn-ptr-tuple.stderr @@ -1,4 +1,4 @@ -error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: no splatted def for function or method callee +error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: splatted FnPtr side-tables are not yet implemented | LL | fn_ptr(1, 2); | ^^^^^^^^^^^^ diff --git a/tests/ui/splat/splat-invalid.rs b/tests/ui/splat/splat-invalid.rs index 2c4bf57ef972a..1fe4d131fc584 100644 --- a/tests/ui/splat/splat-invalid.rs +++ b/tests/ui/splat/splat-invalid.rs @@ -4,14 +4,32 @@ #![feature(splat)] #![feature(c_variadic)] -fn multisplat_bad(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} -//~^ ERROR multiple `#[splat]`s are not allowed in the same function +fn multisplat_fn_bad(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} +//~^ ERROR multiple `#[splat]`s are not allowed in the same function argument list + +fn multisplat_arg_bad( + #[splat] + #[splat] + //~^ ERROR multiple `splat` attributes + (_a, _b): (u32, i8), +) { +} + +fn multisplat_arg_fn_bad( + #[splat] + //~^ ERROR multiple `#[splat]`s are not allowed in the same function argument list + #[splat] + //~^ ERROR multiple `splat` attributes + (_a, _b): (u32, i8), + #[splat] (_c, _d): (u32, i8), +) { +} unsafe extern "C" fn splat_variadic(#[splat] (_a, _b): (u32, i8), varargs: ...) {} -//~^ ERROR `...` and `#[splat]` are not allowed in the same function +//~^ ERROR `...` and `#[splat]` are not allowed in the same function argument list unsafe extern "C" fn splat_variadic2(varargs: ..., #[splat] (_a, _b): (u32, i8)) {} -//~^ ERROR `...` and `#[splat]` are not allowed in the same function +//~^ ERROR `...` and `#[splat]` are not allowed in the same function argument list //~| ERROR `...` must be the last argument of a C-variadic function extern "C" { diff --git a/tests/ui/splat/splat-invalid.stderr b/tests/ui/splat/splat-invalid.stderr index 74c54c00cd285..86b1dbbb7e44b 100644 --- a/tests/ui/splat/splat-invalid.stderr +++ b/tests/ui/splat/splat-invalid.stderr @@ -1,35 +1,49 @@ -error: multiple `#[splat]`s are not allowed in the same function - --> $DIR/splat-invalid.rs:7:19 +error: multiple `#[splat]`s are not allowed in the same function argument list + --> $DIR/splat-invalid.rs:7:22 | -LL | fn multisplat_bad(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn multisplat_fn_bad(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} + | ^^^^^^^^ ^^^^^^^^ | = help: remove `#[splat]` from all but one argument -error: `...` and `#[splat]` are not allowed in the same function - --> $DIR/splat-invalid.rs:10:37 +error: multiple `#[splat]`s are not allowed in the same function argument list + --> $DIR/splat-invalid.rs:19:5 + | +LL | #[splat] + | ^^^^^^^^ +LL | +LL | #[splat] + | ^^^^^^^^ +... +LL | #[splat] (_c, _d): (u32, i8), + | ^^^^^^^^ + | + = help: remove `#[splat]` from all but one argument + +error: `...` and `#[splat]` are not allowed in the same function argument list + --> $DIR/splat-invalid.rs:28:37 | LL | unsafe extern "C" fn splat_variadic(#[splat] (_a, _b): (u32, i8), varargs: ...) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^ + | ^^^^^^^^ ^^^^^^^^^^^^ | = help: remove `#[splat]` or remove `...` error: `...` must be the last argument of a C-variadic function - --> $DIR/splat-invalid.rs:13:38 + --> $DIR/splat-invalid.rs:31:38 | LL | unsafe extern "C" fn splat_variadic2(varargs: ..., #[splat] (_a, _b): (u32, i8)) {} | ^^^^^^^^^^^^ -error: `...` and `#[splat]` are not allowed in the same function - --> $DIR/splat-invalid.rs:13:38 +error: `...` and `#[splat]` are not allowed in the same function argument list + --> $DIR/splat-invalid.rs:31:38 | LL | unsafe extern "C" fn splat_variadic2(varargs: ..., #[splat] (_a, _b): (u32, i8)) {} - | ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ ^^^^^^^^ | = help: remove `#[splat]` or remove `...` error: incorrect function inside `extern` block - --> $DIR/splat-invalid.rs:18:8 + --> $DIR/splat-invalid.rs:36:8 | LL | extern "C" { | ---------- `extern` blocks define existing foreign functions and functions inside of them cannot have a body @@ -39,16 +53,16 @@ LL | fn splat_variadic3(#[splat] (_a, _b): (u32, i8), ...) {} = help: you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html -error: `...` and `#[splat]` are not allowed in the same function - --> $DIR/splat-invalid.rs:18:24 +error: `...` and `#[splat]` are not allowed in the same function argument list + --> $DIR/splat-invalid.rs:36:24 | LL | fn splat_variadic3(#[splat] (_a, _b): (u32, i8), ...) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ + | ^^^^^^^^ ^^^ | = help: remove `#[splat]` or remove `...` error: incorrect function inside `extern` block - --> $DIR/splat-invalid.rs:22:8 + --> $DIR/splat-invalid.rs:40:8 | LL | extern "C" { | ---------- `extern` blocks define existing foreign functions and functions inside of them cannot have a body @@ -60,27 +74,51 @@ LL | fn splat_variadic4(..., #[splat] (_a, _b): (u32, i8)) {} = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html error: `...` must be the last argument of a C-variadic function - --> $DIR/splat-invalid.rs:22:24 + --> $DIR/splat-invalid.rs:40:24 | LL | fn splat_variadic4(..., #[splat] (_a, _b): (u32, i8)) {} | ^^^ -error: `...` and `#[splat]` are not allowed in the same function - --> $DIR/splat-invalid.rs:22:24 +error: `...` and `#[splat]` are not allowed in the same function argument list + --> $DIR/splat-invalid.rs:40:24 | LL | fn splat_variadic4(..., #[splat] (_a, _b): (u32, i8)) {} - | ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^ ^^^^^^^^ | = help: remove `#[splat]` or remove `...` +error: multiple `splat` attributes + --> $DIR/splat-invalid.rs:12:5 + | +LL | #[splat] + | ^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/splat-invalid.rs:11:5 + | +LL | #[splat] + | ^^^^^^^^ + +error: multiple `splat` attributes + --> $DIR/splat-invalid.rs:21:5 + | +LL | #[splat] + | ^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/splat-invalid.rs:19:5 + | +LL | #[splat] + | ^^^^^^^^ + error[E0053]: method `has_splat` has an incompatible type for trait - --> $DIR/splat-invalid.rs:42:5 + --> $DIR/splat-invalid.rs:60:5 | LL | fn has_splat(_: ()) {} | ^^^^^^^^^^^^^^^^^^^ expected fn with arg 0 splatted, found fn with no splatted arg | note: type in trait - --> $DIR/splat-invalid.rs:34:5 + --> $DIR/splat-invalid.rs:52:5 | LL | fn has_splat(#[splat] _: ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,19 +126,19 @@ LL | fn has_splat(#[splat] _: ()); found signature `fn(())` error[E0053]: method `no_splat` has an incompatible type for trait - --> $DIR/splat-invalid.rs:44:5 + --> $DIR/splat-invalid.rs:62:5 | LL | fn no_splat(#[splat] _: (u32, f64)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn with no splatted arg, found fn with arg 0 splatted | note: type in trait - --> $DIR/splat-invalid.rs:36:5 + --> $DIR/splat-invalid.rs:54:5 | LL | fn no_splat(_: (u32, f64)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: expected signature `fn((_, _))` found signature `fn(#[splat] (_, _))` -error: aborting due to 11 previous errors +error: aborting due to 14 previous errors For more information about this error, try `rustc --explain E0053`. diff --git a/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs b/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs index 3f29b9d292d22..473aff7891636 100644 --- a/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs +++ b/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs @@ -7,7 +7,7 @@ unsafe fn unsafe_wrong_type(#[splat] _x: u32) {} //~^ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 unsafe fn unsafe_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} -//~^ ERROR multiple `#[splat]`s are not allowed in the same function +//~^ ERROR multiple `#[splat]`s are not allowed in the same function argument list fn main() { unsafe { diff --git a/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr b/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr index 67935e170671a..7ad15f0f960e2 100644 --- a/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr +++ b/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr @@ -1,8 +1,8 @@ -error: multiple `#[splat]`s are not allowed in the same function +error: multiple `#[splat]`s are not allowed in the same function argument list --> $DIR/splat-unsafe-fn-tuple-fail.rs:9:30 | LL | unsafe fn unsafe_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^ ^^^^^^^^ | = help: remove `#[splat]` from all but one argument diff --git a/tests/ui/suggestions/issue-105645.stderr b/tests/ui/suggestions/issue-105645.stderr index 2b9a94d8e0ab2..2342c22cfa80a 100644 --- a/tests/ui/suggestions/issue-105645.stderr +++ b/tests/ui/suggestions/issue-105645.stderr @@ -7,7 +7,7 @@ LL | foo(&mut bref); | required by a bound introduced by this call | help: the trait `std::io::Write` is implemented for `&mut [u8]` - --> $SRC_DIR/std/src/io/impls.rs:LL:COL + --> $SRC_DIR/core/src/io/impls.rs:LL:COL note: required by a bound in `foo` --> $DIR/issue-105645.rs:8:21 | diff --git a/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.rs b/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.rs index a60f0b94587f8..7745ebaceb9aa 100644 --- a/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.rs +++ b/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.rs @@ -9,6 +9,11 @@ trait ZstAssert: Sized { const D: T = T { a: &(), b: &() }; //~ ERROR implicit elided lifetime not allowed here } +const A: &str = ""; +const B: S = S { s: &() }; +const C: &'_ str = ""; +const D: T = T { a: &(), b: &() }; + struct S<'a> { s: &'a (), } diff --git a/tests/ui/view-types/auxiliary/hygiene.rs b/tests/ui/view-types/auxiliary/hygiene.rs new file mode 100644 index 0000000000000..cf842b56e6970 --- /dev/null +++ b/tests/ui/view-types/auxiliary/hygiene.rs @@ -0,0 +1,12 @@ +//@ edition:2015 + +#![feature(view_types, view_type_macro)] + +pub use std::view::view_type; + +#[macro_export] +macro_rules! view_bar { + () => { + $crate::view_type!(Bar.{ async }) + } +} diff --git a/tests/ui/view-types/duplicate_field.rs b/tests/ui/view-types/duplicate_field.rs new file mode 100644 index 0000000000000..b45580510b6cb --- /dev/null +++ b/tests/ui/view-types/duplicate_field.rs @@ -0,0 +1,30 @@ +#![feature(view_types, view_type_macro)] +#![allow(unused)] + +use std::view::view_type; + +struct Foo { + bar: usize, +} + +struct Pair(usize); + +fn f( + _foo: &mut view_type!(Foo.{ bar, bar }), + //~^ ERROR field `bar` is already part of the view + _pair: &mut view_type!(Pair.{ 0, 0 }), + //~^ ERROR field `0` is already part of the view +) { +} + +impl Foo { + fn f(self: &mut view_type!(Self.{ bar, bar })) {} + //~^ ERROR field `bar` is already part of the view +} + +impl Pair { + fn f(self: &mut view_type!(Self.{ 0, 0 })) {} + //~^ ERROR field `0` is already part of the view +} + +fn main() {} diff --git a/tests/ui/view-types/duplicate_field.stderr b/tests/ui/view-types/duplicate_field.stderr new file mode 100644 index 0000000000000..f0162f6e0a67d --- /dev/null +++ b/tests/ui/view-types/duplicate_field.stderr @@ -0,0 +1,34 @@ +error: field `bar` is already part of the view + --> $DIR/duplicate_field.rs:13:38 + | +LL | _foo: &mut view_type!(Foo.{ bar, bar }), + | --- ^^^ + | | + | field `bar` is declared as viewed here + +error: field `0` is already part of the view + --> $DIR/duplicate_field.rs:15:38 + | +LL | _pair: &mut view_type!(Pair.{ 0, 0 }), + | - ^ + | | + | field `0` is declared as viewed here + +error: field `bar` is already part of the view + --> $DIR/duplicate_field.rs:21:44 + | +LL | fn f(self: &mut view_type!(Self.{ bar, bar })) {} + | --- ^^^ + | | + | field `bar` is declared as viewed here + +error: field `0` is already part of the view + --> $DIR/duplicate_field.rs:26:42 + | +LL | fn f(self: &mut view_type!(Self.{ 0, 0 })) {} + | - ^ + | | + | field `0` is declared as viewed here + +error: aborting due to 4 previous errors + diff --git a/tests/ui/view-types/hygiene.rs b/tests/ui/view-types/hygiene.rs new file mode 100644 index 0000000000000..15a9f7f228f90 --- /dev/null +++ b/tests/ui/view-types/hygiene.rs @@ -0,0 +1,32 @@ +//@ run-pass +//@ aux-build: hygiene.rs + +#![feature(view_types, view_type_macro, decl_macro)] +#![allow(unused)] + +extern crate hygiene; + +use std::view::view_type; + +pub macro what($name: ident) { + struct Foo { + bar: usize, + $name: usize, + } + + impl Foo { + fn f(self: &mut view_type!(Self.{ bar, $name })) {} + } +} + +what!(bar); + +struct Bar { + r#async: (), +} + +impl Bar { + fn f(self: hygiene::view_bar!()) {} +} + +fn main() {} diff --git a/tests/ui/view-types/must-be-struct.rs b/tests/ui/view-types/must-be-struct.rs index 43dbae587e4ab..fb30ef1234cc5 100644 --- a/tests/ui/view-types/must-be-struct.rs +++ b/tests/ui/view-types/must-be-struct.rs @@ -1,6 +1,3 @@ -//@ known-bug: unknown -//@ run-pass - #![feature(view_types, view_type_macro)] #![allow(unused)] @@ -11,9 +8,11 @@ enum Foo { Baz, } -// The following types are not structs, we expect errors here. fn f(_: view_type!(Foo.{})) {} +//~^ ERROR only structs can be viewed fn g(_: view_type!(u8.{})) {} +//~^ ERROR only structs can be viewed fn h(_: view_type!(char.{})) {} +//~^ ERROR only structs can be viewed fn main() {} diff --git a/tests/ui/view-types/must-be-struct.stderr b/tests/ui/view-types/must-be-struct.stderr new file mode 100644 index 0000000000000..3d47e4903af6c --- /dev/null +++ b/tests/ui/view-types/must-be-struct.stderr @@ -0,0 +1,20 @@ +error: only structs can be viewed + --> $DIR/must-be-struct.rs:11:9 + | +LL | fn f(_: view_type!(Foo.{})) {} + | ^^^^^^^^^^^^^^^^^^ `Foo` is an enum, it cannot be viewed + +error: only structs can be viewed + --> $DIR/must-be-struct.rs:13:9 + | +LL | fn g(_: view_type!(u8.{})) {} + | ^^^^^^^^^^^^^^^^^ type `u8` cannot be viewed + +error: only structs can be viewed + --> $DIR/must-be-struct.rs:15:9 + | +LL | fn h(_: view_type!(char.{})) {} + | ^^^^^^^^^^^^^^^^^^^ type `char` cannot be viewed + +error: aborting due to 3 previous errors + diff --git a/tests/ui/view-types/must-exist.rs b/tests/ui/view-types/must-exist.rs index 05792b01b3265..efb43eb509e9c 100644 --- a/tests/ui/view-types/must-exist.rs +++ b/tests/ui/view-types/must-exist.rs @@ -1,6 +1,3 @@ -//@ known-bug: unknown -//@ run-pass - #![feature(view_types, view_type_macro)] #![allow(unused)] @@ -10,8 +7,9 @@ struct S { foo: (), } -// We expect errors here, since `S` has no field `bar`. fn f(_: view_type!(S.{ bar })) {} +//~^ ERROR no field `bar` on type `S` fn g(_: view_type!(S.{ foo, bar })) {} +//~^ ERROR no field `bar` on type `S` fn main() {} diff --git a/tests/ui/view-types/must-exist.stderr b/tests/ui/view-types/must-exist.stderr new file mode 100644 index 0000000000000..a775ad84a7edf --- /dev/null +++ b/tests/ui/view-types/must-exist.stderr @@ -0,0 +1,15 @@ +error[E0609]: no field `bar` on type `S` + --> $DIR/must-exist.rs:10:24 + | +LL | fn f(_: view_type!(S.{ bar })) {} + | ^^^ + +error[E0609]: no field `bar` on type `S` + --> $DIR/must-exist.rs:12:29 + | +LL | fn g(_: view_type!(S.{ foo, bar })) {} + | ^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0609`. diff --git a/tests/ui/view-types/syntax-errors.rs b/tests/ui/view-types/syntax-errors.rs index 19f5061cf820a..18ccb859d549e 100644 --- a/tests/ui/view-types/syntax-errors.rs +++ b/tests/ui/view-types/syntax-errors.rs @@ -9,13 +9,17 @@ struct Foo { } impl Foo { - fn not_a_field(self: &mut view_type!(Foo.{ _ }), _: &mut view_type!(Foo.{ _ })) {} - //~^ ERROR expected identifier - //~| ERROR expected identifier + fn not_a_field(self: &mut view_type!(Self.{ _ }), _: &mut view_type!(Foo.{ _ })) {} + //~^ ERROR expected identifier, found reserved identifier + //~| ERROR expected identifier, found reserved identifier + //~| ERROR no field `_` on type `Foo` + //~| ERROR no field `_` on type `Foo` fn keyword(self: &mut view_type!(Foo.{ where }), _: &mut view_type!(Foo.{ for })) {} //~^ ERROR expected identifier //~| ERROR expected identifier + //~| ERROR no field `r#where` on type `Foo` + //~| ERROR no field `r#for` on type `Foo` fn no_comma(self: &mut view_type!(Foo.{ bar baz }), _: &mut view_type!(Foo.{ bar baz })) {} //~^ ERROR expected one of diff --git a/tests/ui/view-types/syntax-errors.stderr b/tests/ui/view-types/syntax-errors.stderr index 86c167ca9a879..1c1e5fdafae1a 100644 --- a/tests/ui/view-types/syntax-errors.stderr +++ b/tests/ui/view-types/syntax-errors.stderr @@ -1,17 +1,17 @@ error: expected identifier, found reserved identifier `_` - --> $DIR/syntax-errors.rs:12:48 + --> $DIR/syntax-errors.rs:12:49 | -LL | fn not_a_field(self: &mut view_type!(Foo.{ _ }), _: &mut view_type!(Foo.{ _ })) {} - | ^ expected identifier, found reserved identifier +LL | fn not_a_field(self: &mut view_type!(Self.{ _ }), _: &mut view_type!(Foo.{ _ })) {} + | ^ expected identifier, found reserved identifier error: expected identifier, found reserved identifier `_` - --> $DIR/syntax-errors.rs:12:79 + --> $DIR/syntax-errors.rs:12:80 | -LL | fn not_a_field(self: &mut view_type!(Foo.{ _ }), _: &mut view_type!(Foo.{ _ })) {} - | ^ expected identifier, found reserved identifier +LL | fn not_a_field(self: &mut view_type!(Self.{ _ }), _: &mut view_type!(Foo.{ _ })) {} + | ^ expected identifier, found reserved identifier error: expected identifier, found keyword `where` - --> $DIR/syntax-errors.rs:16:44 + --> $DIR/syntax-errors.rs:18:44 | LL | fn keyword(self: &mut view_type!(Foo.{ where }), _: &mut view_type!(Foo.{ for })) {} | ^^^^^ expected identifier, found keyword @@ -22,7 +22,7 @@ LL | fn keyword(self: &mut view_type!(Foo.{ r#where }), _: &mut view_type!(F | ++ error: expected identifier, found keyword `for` - --> $DIR/syntax-errors.rs:16:79 + --> $DIR/syntax-errors.rs:18:79 | LL | fn keyword(self: &mut view_type!(Foo.{ where }), _: &mut view_type!(Foo.{ for })) {} | ^^^ expected identifier, found keyword @@ -33,7 +33,7 @@ LL | fn keyword(self: &mut view_type!(Foo.{ where }), _: &mut view_type!(Foo | ++ error: expected one of `,` or `}`, found `baz` - --> $DIR/syntax-errors.rs:20:49 + --> $DIR/syntax-errors.rs:24:49 | LL | fn no_comma(self: &mut view_type!(Foo.{ bar baz }), _: &mut view_type!(Foo.{ bar baz })) {} | -^^^ expected one of `,` or `}` @@ -41,12 +41,37 @@ LL | fn no_comma(self: &mut view_type!(Foo.{ bar baz }), _: &mut view_type!( | help: missing `,` error: expected one of `,` or `}`, found `baz` - --> $DIR/syntax-errors.rs:20:86 + --> $DIR/syntax-errors.rs:24:86 | LL | fn no_comma(self: &mut view_type!(Foo.{ bar baz }), _: &mut view_type!(Foo.{ bar baz })) {} | -^^^ expected one of `,` or `}` | | | help: missing `,` -error: aborting due to 6 previous errors +error[E0609]: no field `_` on type `Foo` + --> $DIR/syntax-errors.rs:12:49 + | +LL | fn not_a_field(self: &mut view_type!(Self.{ _ }), _: &mut view_type!(Foo.{ _ })) {} + | ^ + +error[E0609]: no field `_` on type `Foo` + --> $DIR/syntax-errors.rs:12:80 + | +LL | fn not_a_field(self: &mut view_type!(Self.{ _ }), _: &mut view_type!(Foo.{ _ })) {} + | ^ + +error[E0609]: no field `r#where` on type `Foo` + --> $DIR/syntax-errors.rs:18:44 + | +LL | fn keyword(self: &mut view_type!(Foo.{ where }), _: &mut view_type!(Foo.{ for })) {} + | ^^^^^ + +error[E0609]: no field `r#for` on type `Foo` + --> $DIR/syntax-errors.rs:18:79 + | +LL | fn keyword(self: &mut view_type!(Foo.{ where }), _: &mut view_type!(Foo.{ for })) {} + | ^^^ + +error: aborting due to 10 previous errors +For more information about this error, try `rustc --explain E0609`.