diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 4c0c496584eb8..053468ff936af 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -26,7 +26,7 @@ use rustc_span::{sym, Span, Symbol, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; use std::borrow::Cow; -use std::{cmp, fmt, iter, mem}; +use std::{cmp, fmt, iter}; /// When the main Rust parser encounters a syntax-extension invocation, it /// parses the arguments to the invocation as a token tree. This is a very @@ -81,14 +81,6 @@ impl TokenTree { } } - /// Modify the `TokenTree`'s span in-place. - pub fn set_span(&mut self, span: Span) { - match self { - TokenTree::Token(token, _) => token.span = span, - TokenTree::Delimited(dspan, ..) => *dspan = DelimSpan::from_single(span), - } - } - /// Create a `TokenTree::Token` with alone spacing. pub fn token_alone(kind: TokenKind, span: Span) -> TokenTree { TokenTree::Token(Token::new(kind, span), Spacing::Alone) @@ -461,19 +453,6 @@ impl TokenStream { t1.next().is_none() && t2.next().is_none() } - /// Applies the supplied function to each `TokenTree` and its index in `self`, returning a new `TokenStream` - /// - /// It is equivalent to `TokenStream::new(self.trees().cloned().enumerate().map(|(i, tt)| f(i, tt)).collect())`. - pub fn map_enumerated_owned( - mut self, - mut f: impl FnMut(usize, TokenTree) -> TokenTree, - ) -> TokenStream { - let owned = Lrc::make_mut(&mut self.0); // clone if necessary - // rely on vec's in-place optimizations to avoid another allocation - *owned = mem::take(owned).into_iter().enumerate().map(|(i, tree)| f(i, tree)).collect(); - self - } - /// Create a token stream containing a single token with alone spacing. The /// spacing used for the final token in a constructed stream doesn't matter /// because it's never used. In practice we arbitrarily use diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index c7c77bf56b7b0..fb59770d48a2b 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1434,19 +1434,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let (bounds, lifetime_bound) = self.with_dyn_type_scope(true, |this| { let bounds = this.arena.alloc_from_iter(bounds.iter().filter_map(|bound| match bound { - GenericBound::Trait( - ty, - TraitBoundModifiers { - polarity: BoundPolarity::Positive | BoundPolarity::Negative(_), - constness, - }, - ) => Some(this.lower_poly_trait_ref(ty, itctx, *constness)), - // We can safely ignore constness here, since AST validation - // will take care of invalid modifier combinations. - GenericBound::Trait( - _, - TraitBoundModifiers { polarity: BoundPolarity::Maybe(_), .. }, - ) => None, + // We can safely ignore constness here since AST validation + // takes care of rejecting invalid modifier combinations and + // const trait bounds in trait object types. + GenericBound::Trait(ty, modifiers) => match modifiers.polarity { + BoundPolarity::Positive | BoundPolarity::Negative(_) => { + Some(this.lower_poly_trait_ref( + ty, + itctx, + // Still, don't pass along the constness here; we don't want to + // synthesize any host effect args, it'd only cause problems. + ast::BoundConstness::Never, + )) + } + BoundPolarity::Maybe(_) => None, + }, GenericBound::Outlives(lifetime) => { if lifetime_bound.is_none() { lifetime_bound = Some(this.lower_lifetime(lifetime)); diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 4ff497f2fdd3f..215649f33ff1d 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1186,15 +1186,22 @@ mod win { } } -fn add_sanitizer_libraries(sess: &Session, crate_type: CrateType, linker: &mut dyn Linker) { - // On macOS the runtimes are distributed as dylibs which should be linked to - // both executables and dynamic shared objects. Everywhere else the runtimes - // are currently distributed as static libraries which should be linked to - // executables only. +fn add_sanitizer_libraries( + sess: &Session, + flavor: LinkerFlavor, + crate_type: CrateType, + linker: &mut dyn Linker, +) { + // On macOS and Windows using MSVC the runtimes are distributed as dylibs + // which should be linked to both executables and dynamic libraries. + // Everywhere else the runtimes are currently distributed as static + // libraries which should be linked to executables only. let needs_runtime = !sess.target.is_like_android && match crate_type { CrateType::Executable => true, - CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro => sess.target.is_like_osx, + CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro => { + sess.target.is_like_osx || sess.target.is_like_msvc + } CrateType::Rlib | CrateType::Staticlib => false, }; @@ -1204,26 +1211,31 @@ fn add_sanitizer_libraries(sess: &Session, crate_type: CrateType, linker: &mut d let sanitizer = sess.opts.unstable_opts.sanitizer; if sanitizer.contains(SanitizerSet::ADDRESS) { - link_sanitizer_runtime(sess, linker, "asan"); + link_sanitizer_runtime(sess, flavor, linker, "asan"); } if sanitizer.contains(SanitizerSet::LEAK) { - link_sanitizer_runtime(sess, linker, "lsan"); + link_sanitizer_runtime(sess, flavor, linker, "lsan"); } if sanitizer.contains(SanitizerSet::MEMORY) { - link_sanitizer_runtime(sess, linker, "msan"); + link_sanitizer_runtime(sess, flavor, linker, "msan"); } if sanitizer.contains(SanitizerSet::THREAD) { - link_sanitizer_runtime(sess, linker, "tsan"); + link_sanitizer_runtime(sess, flavor, linker, "tsan"); } if sanitizer.contains(SanitizerSet::HWADDRESS) { - link_sanitizer_runtime(sess, linker, "hwasan"); + link_sanitizer_runtime(sess, flavor, linker, "hwasan"); } if sanitizer.contains(SanitizerSet::SAFESTACK) { - link_sanitizer_runtime(sess, linker, "safestack"); + link_sanitizer_runtime(sess, flavor, linker, "safestack"); } } -fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) { +fn link_sanitizer_runtime( + sess: &Session, + flavor: LinkerFlavor, + linker: &mut dyn Linker, + name: &str, +) { fn find_sanitizer_runtime(sess: &Session, filename: &str) -> PathBuf { let session_tlib = filesearch::make_target_lib_path(&sess.sysroot, sess.opts.target_triple.triple()); @@ -1254,6 +1266,10 @@ fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) { let rpath = path.to_str().expect("non-utf8 component in path"); linker.args(&["-Wl,-rpath", "-Xlinker", rpath]); linker.link_dylib(&filename, false, true); + } else if sess.target.is_like_msvc && flavor == LinkerFlavor::Msvc(Lld::No) && name == "asan" { + // MSVC provides the `/INFERASANLIBS` argument to automatically find the + // compatible ASAN library. + linker.arg("/INFERASANLIBS"); } else { let filename = format!("librustc{channel}_rt.{name}.a"); let path = find_sanitizer_runtime(sess, &filename).join(&filename); @@ -2076,7 +2092,7 @@ fn linker_with_args<'a>( ); // Sanitizer libraries. - add_sanitizer_libraries(sess, crate_type, cmd); + add_sanitizer_libraries(sess, flavor, crate_type, cmd); // Object code from the current crate. // Take careful note of the ordering of the arguments we pass to the linker diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index e9507dcfed7e4..6707e17e90f4a 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1698,7 +1698,7 @@ pub enum Level { /// internal overflows, some file operation errors. /// /// Its `EmissionGuarantee` is `FatalAbort`, except in the non-aborting "almost fatal" case - /// that is occasionaly used, where it is `FatalError`. + /// that is occasionally used, where it is `FatalError`. Fatal, /// An error in the code being compiled, which prevents compilation from finishing. This is the diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index e9736d6f2c8ae..e9797abcbdf24 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -10,7 +10,7 @@ use crate::mbe::transcribe::transcribe; use rustc_ast as ast; use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind, TokenKind::*}; -use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; +use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::{NodeId, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_attr::{self as attr, TransparencyError}; @@ -213,7 +213,7 @@ fn expand_macro<'cx>( let arm_span = rhses[i].span(); // rhs has holes ( `$id` and `$(...)` that need filled) - let mut tts = match transcribe(cx, &named_matches, rhs, rhs_span, transparency) { + let tts = match transcribe(cx, &named_matches, rhs, rhs_span, transparency) { Ok(tts) => tts, Err(mut err) => { err.emit(); @@ -221,37 +221,6 @@ fn expand_macro<'cx>( } }; - // Replace all the tokens for the corresponding positions in the macro, to maintain - // proper positions in error reporting, while maintaining the macro_backtrace. - if tts.len() == rhs.tts.len() { - tts = tts.map_enumerated_owned(|i, mut tt| { - let rhs_tt = &rhs.tts[i]; - let ctxt = tt.span().ctxt(); - match (&mut tt, rhs_tt) { - // preserve the delim spans if able - ( - TokenTree::Delimited(target_sp, ..), - mbe::TokenTree::Delimited(source_sp, ..), - ) => { - target_sp.open = source_sp.open.with_ctxt(ctxt); - target_sp.close = source_sp.close.with_ctxt(ctxt); - } - ( - TokenTree::Delimited(target_sp, ..), - mbe::TokenTree::MetaVar(source_sp, ..), - ) => { - target_sp.open = source_sp.with_ctxt(ctxt); - target_sp.close = source_sp.with_ctxt(ctxt).shrink_to_hi(); - } - _ => { - let sp = rhs_tt.span().with_ctxt(ctxt); - tt.set_span(sp); - } - } - tt - }); - } - if cx.trace_macros() { let msg = format!("to `{}`", pprust::tts_to_string(&tts)); trace_macros_note(&mut cx.expansions, sp, msg); diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index f2a9875ffd28a..c969ca7ef89b3 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -4,7 +4,7 @@ use crate::errors::{ NoSyntaxVarsExprRepeat, VarStillRepeating, }; use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, MatchedTokenTree, NamedMatch}; -use crate::mbe::{self, MetaVarExpr}; +use crate::mbe::{self, KleeneOp, MetaVarExpr}; use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; @@ -42,6 +42,7 @@ enum Frame<'a> { tts: &'a [mbe::TokenTree], idx: usize, sep: Option, + kleene_op: KleeneOp, }, } @@ -207,7 +208,7 @@ pub(super) fn transcribe<'a>( // Is the repetition empty? if len == 0 { - if seq.kleene.op == mbe::KleeneOp::OneOrMore { + if seq.kleene.op == KleeneOp::OneOrMore { // FIXME: this really ought to be caught at macro definition // time... It happens when the Kleene operator in the matcher and // the body for the same meta-variable do not match. @@ -227,6 +228,7 @@ pub(super) fn transcribe<'a>( idx: 0, sep: seq.separator.clone(), tts: &delimited.tts, + kleene_op: seq.kleene.op, }); } } @@ -243,7 +245,7 @@ pub(super) fn transcribe<'a>( MatchedTokenTree(tt) => { // `tt`s are emitted into the output stream directly as "raw tokens", // without wrapping them into groups. - result.push(tt.clone()); + result.push(maybe_use_metavar_location(cx, &stack, sp, tt)); } MatchedNonterminal(nt) => { // Other variables are emitted into the output stream as groups with @@ -308,6 +310,62 @@ pub(super) fn transcribe<'a>( } } +/// Usually metavariables `$var` produce interpolated tokens, which have an additional place for +/// keeping both the original span and the metavariable span. For `tt` metavariables that's not the +/// case however, and there's no place for keeping a second span. So we try to give the single +/// produced span a location that would be most useful in practice (the hygiene part of the span +/// must not be changed). +/// +/// Different locations are useful for different purposes: +/// - The original location is useful when we need to report a diagnostic for the original token in +/// isolation, without combining it with any surrounding tokens. This case occurs, but it is not +/// very common in practice. +/// - The metavariable location is useful when we need to somehow combine the token span with spans +/// of its surrounding tokens. This is the most common way to use token spans. +/// +/// So this function replaces the original location with the metavariable location in all cases +/// except these two: +/// - The metavariable is an element of undelimited sequence `$($tt)*`. +/// These are typically used for passing larger amounts of code, and tokens in that code usually +/// combine with each other and not with tokens outside of the sequence. +/// - The metavariable span comes from a different crate, then we prefer the more local span. +/// +/// FIXME: Find a way to keep both original and metavariable spans for all tokens without +/// regressing compilation time too much. Several experiments for adding such spans were made in +/// the past (PR #95580, #118517, #118671) and all showed some regressions. +fn maybe_use_metavar_location( + cx: &ExtCtxt<'_>, + stack: &[Frame<'_>], + metavar_span: Span, + orig_tt: &TokenTree, +) -> TokenTree { + let undelimited_seq = matches!( + stack.last(), + Some(Frame::Sequence { + tts: [_], + sep: None, + kleene_op: KleeneOp::ZeroOrMore | KleeneOp::OneOrMore, + .. + }) + ); + if undelimited_seq || cx.source_map().is_imported(metavar_span) { + return orig_tt.clone(); + } + + match orig_tt { + TokenTree::Token(Token { kind, span }, spacing) => { + let span = metavar_span.with_ctxt(span.ctxt()); + TokenTree::Token(Token { kind: kind.clone(), span }, *spacing) + } + TokenTree::Delimited(dspan, dspacing, delimiter, tts) => { + let open = metavar_span.shrink_to_lo().with_ctxt(dspan.open.ctxt()); + let close = metavar_span.shrink_to_hi().with_ctxt(dspan.close.ctxt()); + let dspan = DelimSpan::from_pair(open, close); + TokenTree::Delimited(dspan, *dspacing, *delimiter, tts.clone()) + } + } +} + /// Lookup the meta-var named `ident` and return the matched token tree from the invocation using /// the set of matches `interpolations`. /// diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 2a408ac255c44..a852c3d2be375 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -28,10 +28,10 @@ use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; pub(super) fn check_fn<'a, 'tcx>( fcx: &mut FnCtxt<'a, 'tcx>, fn_sig: ty::FnSig<'tcx>, + coroutine_types: Option>, decl: &'tcx hir::FnDecl<'tcx>, fn_def_id: LocalDefId, body: &'tcx hir::Body<'tcx>, - closure_kind: Option, params_can_be_unsized: bool, ) -> Option> { let fn_id = fcx.tcx.local_def_id_to_hir_id(fn_def_id); @@ -49,54 +49,13 @@ pub(super) fn check_fn<'a, 'tcx>( fcx.param_env, )); + fcx.coroutine_types = coroutine_types; fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty))); let span = body.value.span; forbid_intrinsic_abi(tcx, span, fn_sig.abi); - if let Some(hir::ClosureKind::Coroutine(kind)) = closure_kind { - let yield_ty = match kind { - hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _) - | hir::CoroutineKind::Coroutine(_) => { - let yield_ty = fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }); - fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType); - yield_ty - } - // HACK(-Ztrait-solver=next): In the *old* trait solver, we must eagerly - // guide inference on the yield type so that we can handle `AsyncIterator` - // in this block in projection correctly. In the new trait solver, it is - // not a problem. - hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _) => { - let yield_ty = fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }); - fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType); - - Ty::new_adt( - tcx, - tcx.adt_def(tcx.require_lang_item(hir::LangItem::Poll, Some(span))), - tcx.mk_args(&[Ty::new_adt( - tcx, - tcx.adt_def(tcx.require_lang_item(hir::LangItem::Option, Some(span))), - tcx.mk_args(&[yield_ty.into()]), - ) - .into()]), - ) - } - hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _) => Ty::new_unit(tcx), - }; - - // Resume type defaults to `()` if the coroutine has no argument. - let resume_ty = fn_sig.inputs().get(0).copied().unwrap_or_else(|| Ty::new_unit(tcx)); - - fcx.resume_yield_tys = Some((resume_ty, yield_ty)); - } - GatherLocalsVisitor::new(fcx).visit_body(body); // C-variadic fns also have a `VaList` input that's not listed in `fn_sig` @@ -147,25 +106,6 @@ pub(super) fn check_fn<'a, 'tcx>( fcx.require_type_is_sized(declared_ret_ty, return_or_body_span, traits::SizedReturnType); fcx.check_return_expr(body.value, false); - // We insert the deferred_coroutine_interiors entry after visiting the body. - // This ensures that all nested coroutines appear before the entry of this coroutine. - // resolve_coroutine_interiors relies on this property. - let coroutine_ty = if let Some(hir::ClosureKind::Coroutine(coroutine_kind)) = closure_kind { - let interior = fcx - .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }); - fcx.deferred_coroutine_interiors.borrow_mut().push(( - fn_def_id, - body.id(), - interior, - coroutine_kind, - )); - - let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap(); - Some(CoroutineTypes { resume_ty, yield_ty, interior }) - } else { - None - }; - // Finalize the return check by taking the LUB of the return types // we saw and assigning it to the expected return type. This isn't // really expected to fail, since the coercions would have failed @@ -201,7 +141,7 @@ pub(super) fn check_fn<'a, 'tcx>( check_lang_start_fn(tcx, fn_sig, fn_def_id); } - coroutine_ty + fcx.coroutine_types } fn check_panic_info_fn(tcx: TyCtxt<'_>, fn_id: LocalDefId, fn_sig: ty::FnSig<'_>) { diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index bf6fda20df83e..7edb5912dd530 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -72,7 +72,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { opt_kind: Option, expected_sig: Option>, ) -> Ty<'tcx> { - let body = self.tcx.hir().body(closure.body); + let tcx = self.tcx; + let body = tcx.hir().body(closure.body); trace!("decl = {:#?}", closure.fn_decl); let expr_def_id = closure.def_id; @@ -83,81 +84,151 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!(?bound_sig, ?liberated_sig); + // FIXME: We could probably actually just unify this further -- + // instead of having a `FnSig` and a `Option`, + // we can have a `ClosureSignature { Coroutine { .. }, Closure { .. } }`, + // similar to how `ty::GenSig` is a distinct data structure. + let coroutine_types = match closure.kind { + hir::ClosureKind::Closure => None, + hir::ClosureKind::Coroutine(kind) => { + let yield_ty = match kind { + hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _) + | hir::CoroutineKind::Coroutine(_) => { + let yield_ty = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span: expr_span, + }); + self.require_type_is_sized(yield_ty, expr_span, traits::SizedYieldType); + yield_ty + } + // HACK(-Ztrait-solver=next): In the *old* trait solver, we must eagerly + // guide inference on the yield type so that we can handle `AsyncIterator` + // in this block in projection correctly. In the new trait solver, it is + // not a problem. + hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _) => { + let yield_ty = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span: expr_span, + }); + self.require_type_is_sized(yield_ty, expr_span, traits::SizedYieldType); + + Ty::new_adt( + tcx, + tcx.adt_def( + tcx.require_lang_item(hir::LangItem::Poll, Some(expr_span)), + ), + tcx.mk_args(&[Ty::new_adt( + tcx, + tcx.adt_def( + tcx.require_lang_item(hir::LangItem::Option, Some(expr_span)), + ), + tcx.mk_args(&[yield_ty.into()]), + ) + .into()]), + ) + } + hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _) => { + tcx.types.unit + } + }; + + // Resume type defaults to `()` if the coroutine has no argument. + let resume_ty = liberated_sig.inputs().get(0).copied().unwrap_or(tcx.types.unit); + + Some(CoroutineTypes { resume_ty, yield_ty }) + } + }; + let mut fcx = FnCtxt::new(self, self.param_env, closure.def_id); - let coroutine_types = check_fn( + check_fn( &mut fcx, liberated_sig, + coroutine_types, closure.fn_decl, expr_def_id, body, - Some(closure.kind), // Closure "rust-call" ABI doesn't support unsized params false, ); - let parent_args = GenericArgs::identity_for_item( - self.tcx, - self.tcx.typeck_root_def_id(expr_def_id.to_def_id()), - ); + let parent_args = + GenericArgs::identity_for_item(tcx, tcx.typeck_root_def_id(expr_def_id.to_def_id())); let tupled_upvars_ty = self.next_root_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::ClosureSynthetic, - span: self.tcx.def_span(expr_def_id), - }); - - if let Some(CoroutineTypes { resume_ty, yield_ty, interior }) = coroutine_types { - let coroutine_args = ty::CoroutineArgs::new( - self.tcx, - ty::CoroutineArgsParts { - parent_args, - resume_ty, - yield_ty, - return_ty: liberated_sig.output(), - witness: interior, - tupled_upvars_ty, - }, - ); - - return Ty::new_coroutine(self.tcx, expr_def_id.to_def_id(), coroutine_args.args); - } - - // Tuple up the arguments and insert the resulting function type into - // the `closures` table. - let sig = bound_sig.map_bound(|sig| { - self.tcx.mk_fn_sig( - [Ty::new_tup(self.tcx, sig.inputs())], - sig.output(), - sig.c_variadic, - sig.unsafety, - sig.abi, - ) + span: expr_span, }); - debug!(?sig, ?opt_kind); - - let closure_kind_ty = match opt_kind { - Some(kind) => Ty::from_closure_kind(self.tcx, kind), + match closure.kind { + hir::ClosureKind::Closure => { + assert_eq!(coroutine_types, None); + // Tuple up the arguments and insert the resulting function type into + // the `closures` table. + let sig = bound_sig.map_bound(|sig| { + tcx.mk_fn_sig( + [Ty::new_tup(tcx, sig.inputs())], + sig.output(), + sig.c_variadic, + sig.unsafety, + sig.abi, + ) + }); - // Create a type variable (for now) to represent the closure kind. - // It will be unified during the upvar inference phase (`upvar.rs`) - None => self.next_root_ty_var(TypeVariableOrigin { - // FIXME(eddyb) distinguish closure kind inference variables from the rest. - kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr_span, - }), - }; + debug!(?sig, ?opt_kind); + + let closure_kind_ty = match opt_kind { + Some(kind) => Ty::from_closure_kind(tcx, kind), + + // Create a type variable (for now) to represent the closure kind. + // It will be unified during the upvar inference phase (`upvar.rs`) + None => self.next_root_ty_var(TypeVariableOrigin { + // FIXME(eddyb) distinguish closure kind inference variables from the rest. + kind: TypeVariableOriginKind::ClosureSynthetic, + span: expr_span, + }), + }; + + let closure_args = ty::ClosureArgs::new( + tcx, + ty::ClosureArgsParts { + parent_args, + closure_kind_ty, + closure_sig_as_fn_ptr_ty: Ty::new_fn_ptr(tcx, sig), + tupled_upvars_ty, + }, + ); - let closure_args = ty::ClosureArgs::new( - self.tcx, - ty::ClosureArgsParts { - parent_args, - closure_kind_ty, - closure_sig_as_fn_ptr_ty: Ty::new_fn_ptr(self.tcx, sig), - tupled_upvars_ty, - }, - ); + Ty::new_closure(tcx, expr_def_id.to_def_id(), closure_args.args) + } + hir::ClosureKind::Coroutine(_) => { + let Some(CoroutineTypes { resume_ty, yield_ty }) = coroutine_types else { + bug!("expected coroutine to have yield/resume types"); + }; + let interior = fcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: body.value.span, + }); + fcx.deferred_coroutine_interiors.borrow_mut().push(( + expr_def_id, + body.id(), + interior, + )); + + let coroutine_args = ty::CoroutineArgs::new( + tcx, + ty::CoroutineArgsParts { + parent_args, + resume_ty, + yield_ty, + return_ty: liberated_sig.output(), + witness: interior, + tupled_upvars_ty, + }, + ); - Ty::new_closure(self.tcx, expr_def_id.to_def_id(), closure_args.args) + Ty::new_coroutine(tcx, expr_def_id.to_def_id(), coroutine_args.args) + } + } } /// Given the expected type, figures out what it can about this closure we diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 7425d6f89012a..44d9f1ed81846 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -15,6 +15,7 @@ use crate::errors::{ use crate::fatally_break_rust; use crate::method::SelfSource; use crate::type_error_struct; +use crate::CoroutineTypes; use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; use crate::{ report_unexpected_variant_res, BreakableCtxt, Diverges, FnCtxt, Needs, @@ -3163,8 +3164,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { value: &'tcx hir::Expr<'tcx>, expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { - match self.resume_yield_tys { - Some((resume_ty, yield_ty)) => { + match self.coroutine_types { + Some(CoroutineTypes { resume_ty, yield_ty }) => { self.check_expr_coercible_to_type(value, yield_ty, None); resume_ty diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 023bd70be174e..aa8bbad1d1246 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -85,7 +85,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { return false; } - // not setting the `fallback_has_occured` field here because + // not setting the `fallback_has_occurred` field here because // that field is only used for type fallback diagnostics. for effect in unsolved_effects { let expected = self.tcx.consts.true_; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 994f11b57d195..cb109a2e02428 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -534,7 +534,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let coroutines = std::mem::take(&mut *self.deferred_coroutine_interiors.borrow_mut()); debug!(?coroutines); - for &(expr_def_id, body_id, interior, _) in coroutines.iter() { + for &(expr_def_id, body_id, interior) in coroutines.iter() { debug!(?expr_def_id); // Create the `CoroutineWitness` type that we will unify with `interior`. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 635284c5f7323..fde3d41faecf0 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -5,7 +5,7 @@ mod checks; mod suggestions; use crate::coercion::DynamicCoerceMany; -use crate::{Diverges, EnclosingBreakables, Inherited}; +use crate::{CoroutineTypes, Diverges, EnclosingBreakables, Inherited}; use rustc_errors::{DiagCtxt, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -68,7 +68,7 @@ pub struct FnCtxt<'a, 'tcx> { /// First span of a return site that we find. Used in error messages. pub(super) ret_coercion_span: Cell>, - pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>, + pub(super) coroutine_types: Option>, /// Whether the last checked node generates a divergence (e.g., /// `return` will set this to `Always`). In general, when entering @@ -122,7 +122,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err_count_on_creation: inh.tcx.dcx().err_count(), ret_coercion: None, ret_coercion_span: Cell::new(None), - resume_yield_tys: None, + coroutine_types: None, diverges: Cell::new(Diverges::Maybe), enclosing_breakables: RefCell::new(EnclosingBreakables { stack: Vec::new(), diff --git a/compiler/rustc_hir_typeck/src/inherited.rs b/compiler/rustc_hir_typeck/src/inherited.rs index 7a6a2b2a01029..4ad46845f0ba8 100644 --- a/compiler/rustc_hir_typeck/src/inherited.rs +++ b/compiler/rustc_hir_typeck/src/inherited.rs @@ -55,8 +55,7 @@ pub struct Inherited<'tcx> { pub(super) deferred_asm_checks: RefCell, hir::HirId)>>, - pub(super) deferred_coroutine_interiors: - RefCell, hir::CoroutineKind)>>, + pub(super) deferred_coroutine_interiors: RefCell)>>, /// Whenever we introduce an adjustment from `!` into a type variable, /// we record that type variable here. This is later used to inform diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index da9a2bde783b8..6044b1fdd40af 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -193,7 +193,7 @@ fn typeck_with_fallback<'tcx>( let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); let fn_sig = fcx.normalize(body.value.span, fn_sig); - check_fn(&mut fcx, fn_sig, decl, def_id, body, None, tcx.features().unsized_fn_params); + check_fn(&mut fcx, fn_sig, None, decl, def_id, body, tcx.features().unsized_fn_params); } else { let expected_type = if let Some(&hir::Ty { kind: hir::TyKind::Infer, span, .. }) = body_ty { Some(fcx.next_ty_var(TypeVariableOrigin { @@ -295,15 +295,13 @@ fn typeck_with_fallback<'tcx>( /// When `check_fn` is invoked on a coroutine (i.e., a body that /// includes yield), it returns back some information about the yield /// points. +#[derive(Debug, PartialEq, Copy, Clone)] struct CoroutineTypes<'tcx> { /// Type of coroutine argument / values returned by `yield`. resume_ty: Ty<'tcx>, /// Type of value that is yielded. yield_ty: Ty<'tcx>, - - /// Types that are captured (see `CoroutineInterior` for more). - interior: Ty<'tcx>, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 47fdd64796e9d..8a179c5a440e8 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2252,6 +2252,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, err: &mut Diagnostic, errors: Vec>, + suggest_derive: bool, ) { let all_local_types_needing_impls = errors.iter().all(|e| match e.obligation.predicate.kind().skip_binder() { @@ -2322,10 +2323,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .map(|e| (e.obligation.predicate, None, Some(e.obligation.cause.clone()))) .collect(); - self.suggest_derive(err, &preds); + if suggest_derive { + self.suggest_derive(err, &preds); + } else { + // The predicate comes from a binop where the lhs and rhs have different types. + let _ = self.note_predicate_source_and_get_derives(err, &preds); + } } - pub fn suggest_derive( + fn note_predicate_source_and_get_derives( &self, err: &mut Diagnostic, unsatisfied_predicates: &[( @@ -2333,7 +2339,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Option>, Option>, )], - ) { + ) -> Vec<(String, Span, Symbol)> { let mut derives = Vec::<(String, Span, Symbol)>::new(); let mut traits = Vec::new(); for (pred, _, _) in unsatisfied_predicates { @@ -2382,21 +2388,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { traits.sort(); traits.dedup(); - derives.sort(); - derives.dedup(); - - let mut derives_grouped = Vec::<(String, Span, String)>::new(); - for (self_name, self_span, trait_name) in derives.into_iter() { - if let Some((last_self_name, _, ref mut last_trait_names)) = derives_grouped.last_mut() - { - if last_self_name == &self_name { - last_trait_names.push_str(format!(", {trait_name}").as_str()); - continue; - } - } - derives_grouped.push((self_name, self_span, trait_name.to_string())); - } - let len = traits.len(); if len > 0 { let span = @@ -2419,6 +2410,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } + derives + } + + pub(crate) fn suggest_derive( + &self, + err: &mut Diagnostic, + unsatisfied_predicates: &[( + ty::Predicate<'tcx>, + Option>, + Option>, + )], + ) { + let mut derives = self.note_predicate_source_and_get_derives(err, unsatisfied_predicates); + derives.sort(); + derives.dedup(); + + let mut derives_grouped = Vec::<(String, Span, String)>::new(); + for (self_name, self_span, trait_name) in derives.into_iter() { + if let Some((last_self_name, _, ref mut last_trait_names)) = derives_grouped.last_mut() + { + if last_self_name == &self_name { + last_trait_names.push_str(format!(", {trait_name}").as_str()); + continue; + } + } + derives_grouped.push((self_name, self_span, trait_name.to_string())); + } + for (self_name, self_span, traits) in &derives_grouped { err.span_suggestion_verbose( self_span.shrink_to_lo(), diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index de3d5f498d5b7..7b49a7cc009db 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -318,7 +318,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_expr.span, format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty), ); - self.note_unmet_impls_on_type(&mut err, errors); + self.note_unmet_impls_on_type(&mut err, errors, false); (err, None) } IsAssign::No => { @@ -375,7 +375,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(lhs_expr.span, lhs_ty.to_string()); err.span_label(rhs_expr.span, rhs_ty.to_string()); } - self.note_unmet_impls_on_type(&mut err, errors); + let suggest_derive = self.can_eq(self.param_env, lhs_ty, rhs_ty); + self.note_unmet_impls_on_type(&mut err, errors, suggest_derive); (err, output_def_id) } }; @@ -852,7 +853,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Str | Never | Char | Tuple(_) | Array(_, _) => {} Ref(_, lty, _) if *lty.kind() == Str => {} _ => { - self.note_unmet_impls_on_type(&mut err, errors); + self.note_unmet_impls_on_type(&mut err, errors, true); } } } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 8afb5f2d32ebf..8d2f2aaca5574 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -2734,10 +2734,13 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels { #[allow(rustc::diagnostic_outside_of_impl)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { if let hir::Expr { - kind: hir::ExprKind::InlineAsm(hir::InlineAsm { template_strs, .. }), + kind: hir::ExprKind::InlineAsm(hir::InlineAsm { template_strs, options, .. }), .. } = expr { + // asm with `options(raw)` does not do replacement with `{` and `}`. + let raw = options.contains(InlineAsmOptions::RAW); + for (template_sym, template_snippet, template_span) in template_strs.iter() { let template_str = template_sym.as_str(); let find_label_span = |needle: &str| -> Option { @@ -2763,24 +2766,57 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels { for statement in statements { // If there's a comment, trim it from the statement let statement = statement.find("//").map_or(statement, |idx| &statement[..idx]); + + // In this loop, if there is ever a non-label, no labels can come after it. let mut start_idx = 0; - for (idx, _) in statement.match_indices(':') { + 'label_loop: for (idx, _) in statement.match_indices(':') { let possible_label = statement[start_idx..idx].trim(); let mut chars = possible_label.chars(); - let Some(c) = chars.next() else { - // Empty string means a leading ':' in this section, which is not a label - break; + + let Some(start) = chars.next() else { + // Empty string means a leading ':' in this section, which is not a label. + break 'label_loop; }; - // A label starts with an alphabetic character or . or _ and continues with alphanumeric characters, _, or $ - if (c.is_alphabetic() || matches!(c, '.' | '_')) - && chars.all(|c| c.is_alphanumeric() || matches!(c, '_' | '$')) - { - found_labels.push(possible_label); - } else { - // If we encounter a non-label, there cannot be any further labels, so stop checking - break; + + // Whether a { bracket has been seen and its } hasn't been found yet. + let mut in_bracket = false; + + // A label starts with an ASCII alphabetic character or . or _ + // A label can also start with a format arg, if it's not a raw asm block. + if !raw && start == '{' { + in_bracket = true; + } else if !(start.is_ascii_alphabetic() || matches!(start, '.' | '_')) { + break 'label_loop; + } + + // Labels continue with ASCII alphanumeric characters, _, or $ + for c in chars { + // Inside a template format arg, any character is permitted for the puproses of label detection + // because we assume that it can be replaced with some other valid label string later. + // `options(raw)` asm blocks cannot have format args, so they are excluded from this special case. + if !raw && in_bracket { + if c == '{' { + // Nested brackets are not allowed in format args, this cannot be a label. + break 'label_loop; + } + + if c == '}' { + // The end of the format arg. + in_bracket = false; + } + } else if !raw && c == '{' { + // Start of a format arg. + in_bracket = true; + } else { + if !(c.is_ascii_alphanumeric() || matches!(c, '_' | '$')) { + // The potential label had an invalid character inside it, it cannot be a label. + break 'label_loop; + } + } } + // If all characters passed the label checks, this is likely a label. + found_labels.push(possible_label); start_idx = idx + 1; } } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index c6bddbfacd6ec..aa8c8538b5408 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -767,6 +767,9 @@ parse_unexpected_if_with_if = unexpected `if` in the condition expression parse_unexpected_lifetime_in_pattern = unexpected lifetime `{$symbol}` in pattern .suggestion = remove the lifetime +parse_unexpected_paren_in_range_pat = range pattern bounds cannot have parentheses +parse_unexpected_paren_in_range_pat_sugg = remove these parentheses + parse_unexpected_parentheses_in_for_head = unexpected parentheses surrounding `for` loop head .suggestion = remove parentheses in `for` loop diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index e276b34ca37af..d67ae14934429 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2378,6 +2378,27 @@ pub(crate) struct ExpectedCommaAfterPatternField { pub span: Span, } +#[derive(Diagnostic)] +#[diag(parse_unexpected_paren_in_range_pat)] +pub(crate) struct UnexpectedParenInRangePat { + #[primary_span] + pub span: Vec, + #[subdiagnostic] + pub sugg: UnexpectedParenInRangePatSugg, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + parse_unexpected_paren_in_range_pat_sugg, + applicability = "machine-applicable" +)] +pub(crate) struct UnexpectedParenInRangePatSugg { + #[suggestion_part(code = "")] + pub start_span: Span, + #[suggestion_part(code = "")] + pub end_span: Span, +} + #[derive(Diagnostic)] #[diag(parse_return_types_use_thin_arrow)] pub(crate) struct ReturnTypesUseThinArrow { diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index afbc253757816..7d17b1d4c4d67 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -6,7 +6,8 @@ use crate::errors::{ InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern, PatternOnWrongSideOfAt, RefMutOrderIncorrect, RemoveLet, RepeatedMutInPattern, SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, - TrailingVertNotAllowed, UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam, + TrailingVertNotAllowed, UnexpectedLifetimeInPattern, UnexpectedParenInRangePat, + UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, }; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; @@ -579,6 +580,8 @@ impl<'a> Parser<'a> { /// Parse a tuple or parenthesis pattern. fn parse_pat_tuple_or_parens(&mut self) -> PResult<'a, PatKind> { + let open_paren = self.token.span; + let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| { p.parse_pat_allow_top_alt( None, @@ -591,7 +594,29 @@ impl<'a> Parser<'a> { // Here, `(pat,)` is a tuple pattern. // For backward compatibility, `(..)` is a tuple pattern as well. Ok(if fields.len() == 1 && !(trailing_comma || fields[0].is_rest()) { - PatKind::Paren(fields.into_iter().next().unwrap()) + let pat = fields.into_iter().next().unwrap(); + let close_paren = self.prev_token.span; + + match &pat.kind { + // recover ranges with parentheses around the `(start)..` + PatKind::Lit(begin) + if self.may_recover() + && let Some(form) = self.parse_range_end() => + { + self.dcx().emit_err(UnexpectedParenInRangePat { + span: vec![open_paren, close_paren], + sugg: UnexpectedParenInRangePatSugg { + start_span: open_paren, + end_span: close_paren, + }, + }); + + self.parse_pat_range_begin_with(begin.clone(), form)? + } + + // (pat) with optional parentheses + _ => PatKind::Paren(pat), + } } else { PatKind::Tuple(fields) }) @@ -794,11 +819,21 @@ impl<'a> Parser<'a> { || t.can_begin_literal_maybe_minus() // e.g. `42`. || t.is_whole_expr() || t.is_lifetime() // recover `'a` instead of `'a'` + || (self.may_recover() // recover leading `(` + && t.kind == token::OpenDelim(Delimiter::Parenthesis) + && self.look_ahead(dist + 1, |t| t.kind != token::OpenDelim(Delimiter::Parenthesis)) + && self.is_pat_range_end_start(dist + 1)) }) } + /// Parse a range pattern end bound fn parse_pat_range_end(&mut self) -> PResult<'a, P> { - if self.check_inline_const(0) { + // recover leading `(` + let open_paren = (self.may_recover() + && self.eat_noexpect(&token::OpenDelim(Delimiter::Parenthesis))) + .then_some(self.prev_token.span); + + let bound = if self.check_inline_const(0) { self.parse_const_block(self.token.span, true) } else if self.check_path() { let lo = self.token.span; @@ -814,7 +849,22 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(lo.to(hi), ExprKind::Path(qself, path))) } else { self.parse_literal_maybe_minus() + }?; + + // recover trailing `)` + if let Some(open_paren) = open_paren { + self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; + + self.dcx().emit_err(UnexpectedParenInRangePat { + span: vec![open_paren, self.prev_token.span], + sugg: UnexpectedParenInRangePatSugg { + start_span: open_paren, + end_span: self.prev_token.span, + }, + }); } + + Ok(bound) } /// Is this the start of a pattern beginning with a path? diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 9ee7625e5bfeb..c80990402a980 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1274,7 +1274,10 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } // Cannot enable crt-static with sanitizers on Linux - if sess.crt_static(None) && !sess.opts.unstable_opts.sanitizer.is_empty() { + if sess.crt_static(None) + && !sess.opts.unstable_opts.sanitizer.is_empty() + && !sess.target.is_like_msvc + { sess.dcx().emit_err(errors::CannotEnableCrtStaticLinux); } diff --git a/compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs index ba80c23196e1d..5abc3017bf80c 100644 --- a/compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs @@ -1,9 +1,10 @@ -use crate::spec::{base, LinkerFlavor, Lld, Target}; +use crate::spec::{base, LinkerFlavor, Lld, SanitizerSet, Target}; pub fn target() -> Target { let mut base = base::windows_msvc::opts(); base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); + base.supported_sanitizers = SanitizerSet::ADDRESS; base.add_pre_link_args( LinkerFlavor::Msvc(Lld::No), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs index 7d6276a0c2d57..3a4da91c2443f 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs @@ -1,10 +1,11 @@ -use crate::spec::{base, Target}; +use crate::spec::{base, SanitizerSet, Target}; pub fn target() -> Target { let mut base = base::windows_msvc::opts(); base.cpu = "x86-64".into(); base.plt_by_default = false; base.max_atomic_width = Some(64); + base.supported_sanitizers = SanitizerSet::ADDRESS; Target { llvm_target: "x86_64-pc-windows-msvc".into(), diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index 5be8aebc70fd5..1b70b669c7790 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -73,8 +73,18 @@ impl UnixListener { unsafe { let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; let (addr, len) = sockaddr_un(path.as_ref())?; - const backlog: libc::c_int = - if cfg!(any(target_os = "linux", target_os = "freebsd")) { -1 } else { 128 }; + #[cfg(any(target_os = "windows", target_os = "redox"))] + const backlog: libc::c_int = 128; + #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))] + const backlog: libc::c_int = -1; + #[cfg(not(any( + target_os = "windows", + target_os = "redox", + target_os = "linux", + target_os = "freebsd", + target_os = "openbsd" + )))] + const backlog: libc::c_int = libc::SOMAXCONN; cvt(libc::bind(inner.as_inner().as_raw_fd(), &addr as *const _ as *const _, len as _))?; cvt(libc::listen(inner.as_inner().as_raw_fd(), backlog))?; diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index dbb64583d561c..d699c4fe536c5 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -274,7 +274,7 @@ fn copy_third_party_objects( ) -> Vec<(PathBuf, DependencyType)> { let mut target_deps = vec![]; - if builder.config.sanitizers_enabled(target) && compiler.stage != 0 { + if builder.config.needs_sanitizer_runtime_built(target) && compiler.stage != 0 { // The sanitizers are only copied in stage1 or above, // to avoid creating dependency on LLVM. target_deps.extend( diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 92140b00da843..3ae3af38bf85b 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1937,6 +1937,29 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the } } + // Special setup to enable running with sanitizers on MSVC. + if !builder.config.dry_run() + && target.contains("msvc") + && builder.config.sanitizers_enabled(target) + { + // Ignore interception failures: not all dlls in the process will have been built with + // address sanitizer enabled (e.g., ntdll.dll). + cmd.env("ASAN_WIN_CONTINUE_ON_INTERCEPTION_FAILURE", "1"); + // Add the address sanitizer runtime to the PATH - it is located next to cl.exe. + let asan_runtime_path = + builder.cc.borrow()[&target].path().parent().unwrap().to_path_buf(); + let old_path = cmd + .get_envs() + .find_map(|(k, v)| (k == "PATH").then_some(v)) + .flatten() + .map_or_else(|| env::var_os("PATH").unwrap_or_default(), |v| v.to_owned()); + let new_path = env::join_paths( + env::split_paths(&old_path).chain(std::iter::once(asan_runtime_path)), + ) + .expect("Could not add ASAN runtime path to PATH"); + cmd.env("PATH", new_path); + } + // Some UI tests trigger behavior in rustc where it reads $CARGO and changes behavior if it exists. // To make the tests work that rely on it not being set, make sure it is not set. cmd.env_remove("CARGO"); diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index c6bf71c883785..3ac3e54563148 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -2180,8 +2180,15 @@ impl Config { self.target_config.get(&target).map(|t| t.sanitizers).flatten().unwrap_or(self.sanitizers) } - pub fn any_sanitizers_enabled(&self) -> bool { - self.target_config.values().any(|t| t.sanitizers == Some(true)) || self.sanitizers + pub fn needs_sanitizer_runtime_built(&self, target: TargetSelection) -> bool { + // MSVC uses the Microsoft-provided sanitizer runtime, but all other runtimes we build. + !target.is_msvc() && self.sanitizers_enabled(target) + } + + pub fn any_sanitizers_to_build(&self) -> bool { + self.target_config + .iter() + .any(|(ts, t)| !ts.is_msvc() && t.sanitizers.unwrap_or(self.sanitizers)) } pub fn profiler_path(&self, target: TargetSelection) -> Option<&str> { diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 9101d94ea881e..82755f418000d 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -96,7 +96,7 @@ pub fn check(build: &mut Build) { }) .any(|build_llvm_ourselves| build_llvm_ourselves); - let need_cmake = building_llvm || build.config.any_sanitizers_enabled(); + let need_cmake = building_llvm || build.config.any_sanitizers_to_build(); if need_cmake && cmd_finder.maybe_have("cmake").is_none() { eprintln!( " diff --git a/src/librustdoc/passes/lint/redundant_explicit_links.rs b/src/librustdoc/passes/lint/redundant_explicit_links.rs index 4491d20b478d0..73f6a42636064 100644 --- a/src/librustdoc/passes/lint/redundant_explicit_links.rs +++ b/src/librustdoc/passes/lint/redundant_explicit_links.rs @@ -244,7 +244,7 @@ fn find_resolution(resolutions: &DocLinkResMap, path: &str) -> Option) -> LinkData { let mut resolvable_link = None; let mut resolvable_link_range = None; diff --git a/tests/codegen/maybeuninit-rvo.rs b/tests/codegen/maybeuninit-rvo.rs new file mode 100644 index 0000000000000..feed513e1fbf5 --- /dev/null +++ b/tests/codegen/maybeuninit-rvo.rs @@ -0,0 +1,33 @@ +// compile-flags: -O +#![feature(c_unwind)] +#![crate_type = "lib"] + +pub struct Foo([u8; 1000]); + +extern "C" { + fn init(p: *mut Foo); +} + +pub fn new_from_uninit() -> Foo { + // CHECK-LABEL: new_from_uninit + // CHECK-NOT: call void @llvm.memcpy. + let mut x = std::mem::MaybeUninit::uninit(); + unsafe { + init(x.as_mut_ptr()); + x.assume_init() + } +} + +extern "C-unwind" { + fn init_unwind(p: *mut Foo); +} + +pub fn new_from_uninit_unwind() -> Foo { + // CHECK-LABEL: new_from_uninit + // CHECK: call void @llvm.memcpy. + let mut x = std::mem::MaybeUninit::uninit(); + unsafe { + init_unwind(x.as_mut_ptr()); + x.assume_init() + } +} diff --git a/tests/coverage/macro_name_span.cov-map b/tests/coverage/macro_name_span.cov-map index b84628fc788ce..a18e5f1486147 100644 --- a/tests/coverage/macro_name_span.cov-map +++ b/tests/coverage/macro_name_span.cov-map @@ -1,15 +1,15 @@ Function name: macro_name_span::affected_function -Raw bytes (9): 0x[01, 01, 00, 01, 01, 06, 1b, 00, 20] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 16, 1c, 02, 06] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 6, 27) to (start + 0, 32) +- Code(Counter(0)) at (prev + 22, 28) to (start + 2, 6) Function name: macro_name_span::main -Raw bytes (9): 0x[01, 02, 00, 01, 01, 0b, 01, 02, 02] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 0b, 01, 02, 02] Number of files: 1 -- file 0 => global file 2 +- file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 11, 1) to (start + 2, 2) diff --git a/tests/coverage/macro_name_span.coverage b/tests/coverage/macro_name_span.coverage index cadf7024657e3..28c88b1defa83 100644 --- a/tests/coverage/macro_name_span.coverage +++ b/tests/coverage/macro_name_span.coverage @@ -1,16 +1,3 @@ -$DIR/auxiliary/macro_name_span_helper.rs: - LL| |// edition: 2021 - LL| | - LL| |#[macro_export] - LL| |macro_rules! macro_that_defines_a_function { - LL| | (fn $name:ident () $body:tt) => { - LL| 1| fn $name () -> () $body - LL| | } - LL| |} - LL| | - LL| |// Non-executable comment. - -$DIR/macro_name_span.rs: LL| |// edition: 2021 LL| | LL| |// Regression test for . @@ -32,8 +19,8 @@ $DIR/macro_name_span.rs: LL| |} LL| | LL| |macro_name_span_helper::macro_that_defines_a_function! { - LL| | fn affected_function() { - LL| | macro_with_an_unreasonably_and_egregiously_long_name!(); - LL| | } + LL| 1| fn affected_function() { + LL| 1| macro_with_an_unreasonably_and_egregiously_long_name!(); + LL| 1| } LL| |} diff --git a/tests/run-make/sanitizer-cdylib-link/Makefile b/tests/run-make/sanitizer-cdylib-link/Makefile index 691585268bfe9..10d94afc39ed4 100644 --- a/tests/run-make/sanitizer-cdylib-link/Makefile +++ b/tests/run-make/sanitizer-cdylib-link/Makefile @@ -12,5 +12,5 @@ LOG := $(TMPDIR)/log.txt all: $(RUSTC) -g -Z sanitizer=address --crate-type cdylib --target $(TARGET) library.rs - $(RUSTC) -g -Z sanitizer=address --crate-type bin --target $(TARGET) -llibrary program.rs + $(RUSTC) -g -Z sanitizer=address --crate-type bin --target $(TARGET) program.rs LD_LIBRARY_PATH=$(TMPDIR) $(TMPDIR)/program 2>&1 | $(CGREP) stack-buffer-overflow diff --git a/tests/run-make/sanitizer-cdylib-link/program.rs b/tests/run-make/sanitizer-cdylib-link/program.rs index ef053aa2e7a3a..1026c7f89ba9f 100644 --- a/tests/run-make/sanitizer-cdylib-link/program.rs +++ b/tests/run-make/sanitizer-cdylib-link/program.rs @@ -1,3 +1,5 @@ +#[cfg_attr(windows, link(name = "library.dll.lib", modifiers = "+verbatim"))] +#[cfg_attr(not(windows), link(name = "library"))] extern "C" { fn overflow(); } diff --git a/tests/run-make/sanitizer-dylib-link/Makefile b/tests/run-make/sanitizer-dylib-link/Makefile index b0a91e5b197d7..c5a698db3a034 100644 --- a/tests/run-make/sanitizer-dylib-link/Makefile +++ b/tests/run-make/sanitizer-dylib-link/Makefile @@ -12,5 +12,5 @@ LOG := $(TMPDIR)/log.txt all: $(RUSTC) -g -Z sanitizer=address --crate-type dylib --target $(TARGET) library.rs - $(RUSTC) -g -Z sanitizer=address --crate-type bin --target $(TARGET) -llibrary program.rs + $(RUSTC) -g -Z sanitizer=address --crate-type bin --target $(TARGET) program.rs LD_LIBRARY_PATH=$(TMPDIR) $(TMPDIR)/program 2>&1 | $(CGREP) stack-buffer-overflow diff --git a/tests/run-make/sanitizer-dylib-link/program.rs b/tests/run-make/sanitizer-dylib-link/program.rs index ef053aa2e7a3a..1026c7f89ba9f 100644 --- a/tests/run-make/sanitizer-dylib-link/program.rs +++ b/tests/run-make/sanitizer-dylib-link/program.rs @@ -1,3 +1,5 @@ +#[cfg_attr(windows, link(name = "library.dll.lib", modifiers = "+verbatim"))] +#[cfg_attr(not(windows), link(name = "library"))] extern "C" { fn overflow(); } diff --git a/tests/run-make/sanitizer-staticlib-link/program.rs b/tests/run-make/sanitizer-staticlib-link/program.rs index ec59bdb11c889..5fac0e739669e 100644 --- a/tests/run-make/sanitizer-staticlib-link/program.rs +++ b/tests/run-make/sanitizer-staticlib-link/program.rs @@ -1,4 +1,4 @@ -#[link(name = "library")] +#[link(name = "library", kind = "static")] extern "C" { fn overflow(); } diff --git a/tests/ui/asm/named-asm-labels.rs b/tests/ui/asm/named-asm-labels.rs index 160dbf617c4f6..24586b39aacc0 100644 --- a/tests/ui/asm/named-asm-labels.rs +++ b/tests/ui/asm/named-asm-labels.rs @@ -120,6 +120,27 @@ fn main() { // is there an example that is valid x86 for this test? asm!(":bbb nop"); + // non-ascii characters are not allowed in labels, so should not trigger the lint + asm!("Ù: nop"); + asm!("testÙ: nop"); + asm!("_Ù_: nop"); + + // Format arguments should be conservatively assumed to be valid characters in labels + // Would emit `test_rax:` or similar + #[allow(asm_sub_register)] + { + asm!("test_{}: nop", in(reg) 10); //~ ERROR avoid using named labels + } + asm!("test_{}: nop", const 10); //~ ERROR avoid using named labels + asm!("test_{}: nop", sym main); //~ ERROR avoid using named labels + asm!("{}_test: nop", const 10); //~ ERROR avoid using named labels + asm!("test_{}_test: nop", const 10); //~ ERROR avoid using named labels + asm!("{}: nop", const 10); //~ ERROR avoid using named labels + + asm!("{uwu}: nop", uwu = const 10); //~ ERROR avoid using named labels + asm!("{0}: nop", const 10); //~ ERROR avoid using named labels + asm!("{1}: nop", "/* {0} */", const 10, const 20); //~ ERROR avoid using named labels + // Test include_str in asm asm!(include_str!("named-asm-labels.s")); //~ ERROR avoid using named labels diff --git a/tests/ui/asm/named-asm-labels.stderr b/tests/ui/asm/named-asm-labels.stderr index c8380629e12ea..89c058499675c 100644 --- a/tests/ui/asm/named-asm-labels.stderr +++ b/tests/ui/asm/named-asm-labels.stderr @@ -245,7 +245,88 @@ LL | ab: nop // ab: does foo = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:124:14 + --> $DIR/named-asm-labels.rs:132:19 + | +LL | asm!("test_{}: nop", in(reg) 10); + | ^^^^^^^ + | + = help: only local labels of the form `:` should be used in inline asm + = note: see the asm section of Rust By Example for more information + +error: avoid using named labels in inline assembly + --> $DIR/named-asm-labels.rs:134:15 + | +LL | asm!("test_{}: nop", const 10); + | ^^^^^^^ + | + = help: only local labels of the form `:` should be used in inline asm + = note: see the asm section of Rust By Example for more information + +error: avoid using named labels in inline assembly + --> $DIR/named-asm-labels.rs:135:15 + | +LL | asm!("test_{}: nop", sym main); + | ^^^^^^^ + | + = help: only local labels of the form `:` should be used in inline asm + = note: see the asm section of Rust By Example for more information + +error: avoid using named labels in inline assembly + --> $DIR/named-asm-labels.rs:136:15 + | +LL | asm!("{}_test: nop", const 10); + | ^^^^^^^ + | + = help: only local labels of the form `:` should be used in inline asm + = note: see the asm section of Rust By Example for more information + +error: avoid using named labels in inline assembly + --> $DIR/named-asm-labels.rs:137:15 + | +LL | asm!("test_{}_test: nop", const 10); + | ^^^^^^^^^^^^ + | + = help: only local labels of the form `:` should be used in inline asm + = note: see the asm section of Rust By Example for more information + +error: avoid using named labels in inline assembly + --> $DIR/named-asm-labels.rs:138:15 + | +LL | asm!("{}: nop", const 10); + | ^^ + | + = help: only local labels of the form `:` should be used in inline asm + = note: see the asm section of Rust By Example for more information + +error: avoid using named labels in inline assembly + --> $DIR/named-asm-labels.rs:140:15 + | +LL | asm!("{uwu}: nop", uwu = const 10); + | ^^^^^ + | + = help: only local labels of the form `:` should be used in inline asm + = note: see the asm section of Rust By Example for more information + +error: avoid using named labels in inline assembly + --> $DIR/named-asm-labels.rs:141:15 + | +LL | asm!("{0}: nop", const 10); + | ^^^ + | + = help: only local labels of the form `:` should be used in inline asm + = note: see the asm section of Rust By Example for more information + +error: avoid using named labels in inline assembly + --> $DIR/named-asm-labels.rs:142:15 + | +LL | asm!("{1}: nop", "/* {0} */", const 10, const 20); + | ^^^ + | + = help: only local labels of the form `:` should be used in inline asm + = note: see the asm section of Rust By Example for more information + +error: avoid using named labels in inline assembly + --> $DIR/named-asm-labels.rs:145:14 | LL | asm!(include_str!("named-asm-labels.s")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -254,7 +335,7 @@ LL | asm!(include_str!("named-asm-labels.s")); = note: see the asm section of Rust By Example for more information warning: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:134:19 + --> $DIR/named-asm-labels.rs:155:19 | LL | asm!("warned: nop"); | ^^^^^^ @@ -262,13 +343,13 @@ LL | asm!("warned: nop"); = help: only local labels of the form `:` should be used in inline asm = note: see the asm section of Rust By Example for more information note: the lint level is defined here - --> $DIR/named-asm-labels.rs:132:16 + --> $DIR/named-asm-labels.rs:153:16 | LL | #[warn(named_asm_labels)] | ^^^^^^^^^^^^^^^^ error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:143:20 + --> $DIR/named-asm-labels.rs:164:20 | LL | unsafe { asm!(".Lfoo: mov rax, {}; ret;", "nop", const 1, options(noreturn)) } | ^^^^^ @@ -277,7 +358,7 @@ LL | unsafe { asm!(".Lfoo: mov rax, {}; ret;", "nop", const 1, options(noret = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:149:20 + --> $DIR/named-asm-labels.rs:170:20 | LL | unsafe { asm!(".Lbar: mov rax, {}; ret;", "nop", const 1, options(noreturn)) } | ^^^^^ @@ -286,7 +367,7 @@ LL | unsafe { asm!(".Lbar: mov rax, {}; ret;", "nop", const 1, options(noret = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:157:20 + --> $DIR/named-asm-labels.rs:178:20 | LL | unsafe { asm!(".Laaa: nop; ret;", options(noreturn)) } | ^^^^^ @@ -295,7 +376,7 @@ LL | unsafe { asm!(".Laaa: nop; ret;", options(noreturn)) } = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:167:24 + --> $DIR/named-asm-labels.rs:188:24 | LL | unsafe { asm!(".Lbbb: nop; ret;", options(noreturn)) } | ^^^^^ @@ -304,7 +385,7 @@ LL | unsafe { asm!(".Lbbb: nop; ret;", options(noreturn)) } = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:176:15 + --> $DIR/named-asm-labels.rs:197:15 | LL | asm!("closure1: nop"); | ^^^^^^^^ @@ -313,7 +394,7 @@ LL | asm!("closure1: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:180:15 + --> $DIR/named-asm-labels.rs:201:15 | LL | asm!("closure2: nop"); | ^^^^^^^^ @@ -322,7 +403,7 @@ LL | asm!("closure2: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:190:19 + --> $DIR/named-asm-labels.rs:211:19 | LL | asm!("closure3: nop"); | ^^^^^^^^ @@ -330,5 +411,5 @@ LL | asm!("closure3: nop"); = help: only local labels of the form `:` should be used in inline asm = note: see the asm section of Rust By Example for more information -error: aborting due to 35 previous errors; 1 warning emitted +error: aborting due to 44 previous errors; 1 warning emitted diff --git a/tests/ui/binop/binary-op-suggest-deref.stderr b/tests/ui/binop/binary-op-suggest-deref.stderr index f5de64e3ab1ae..68b5a24bf974d 100644 --- a/tests/ui/binop/binary-op-suggest-deref.stderr +++ b/tests/ui/binop/binary-op-suggest-deref.stderr @@ -270,11 +270,6 @@ note: an implementation of `PartialEq<&&{integer}>` might be missing for `Foo` | LL | struct Foo; | ^^^^^^^^^^ must implement `PartialEq<&&{integer}>` -help: consider annotating `Foo` with `#[derive(PartialEq)]` - | -LL + #[derive(PartialEq)] -LL | struct Foo; - | error[E0277]: can't compare `&String` with `str` --> $DIR/binary-op-suggest-deref.rs:69:20 diff --git a/tests/ui/coroutine/gen_block.e2024.stderr b/tests/ui/coroutine/gen_block.e2024.stderr index f250e2f79c77d..e32f80dafa0c1 100644 --- a/tests/ui/coroutine/gen_block.e2024.stderr +++ b/tests/ui/coroutine/gen_block.e2024.stderr @@ -8,10 +8,10 @@ LL | let _ = || yield true; = help: add `#![feature(coroutines)]` to the crate attributes to enable error[E0282]: type annotations needed - --> $DIR/gen_block.rs:6:17 + --> $DIR/gen_block.rs:6:13 | LL | let x = gen {}; - | ^^ cannot infer type + | ^^^^^^ cannot infer type error: aborting due to 2 previous errors diff --git a/tests/ui/coroutine/sized-yield.stderr b/tests/ui/coroutine/sized-yield.stderr index 40663ac12de46..bbecaffa95a12 100644 --- a/tests/ui/coroutine/sized-yield.stderr +++ b/tests/ui/coroutine/sized-yield.stderr @@ -1,8 +1,8 @@ error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/sized-yield.rs:8:27 + --> $DIR/sized-yield.rs:8:19 | LL | let mut gen = move || { - | ___________________________^ + | ___________________^ LL | | LL | | yield s[..]; LL | | }; diff --git a/tests/ui/feature-gates/feature-gate-gen_blocks.e2024.stderr b/tests/ui/feature-gates/feature-gate-gen_blocks.e2024.stderr index c582ca7ba3d58..526354f6cfbdf 100644 --- a/tests/ui/feature-gates/feature-gate-gen_blocks.e2024.stderr +++ b/tests/ui/feature-gates/feature-gate-gen_blocks.e2024.stderr @@ -35,16 +35,16 @@ LL | async gen {}; = help: add `#![feature(gen_blocks)]` to the crate attributes to enable error[E0282]: type annotations needed - --> $DIR/feature-gate-gen_blocks.rs:5:9 + --> $DIR/feature-gate-gen_blocks.rs:5:5 | LL | gen {}; - | ^^ cannot infer type + | ^^^^^^ cannot infer type error[E0282]: type annotations needed - --> $DIR/feature-gate-gen_blocks.rs:12:15 + --> $DIR/feature-gate-gen_blocks.rs:12:5 | LL | async gen {}; - | ^^ cannot infer type + | ^^^^^^^^^^^^ cannot infer type error: aborting due to 6 previous errors diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions2.rs b/tests/ui/half-open-range-patterns/range_pat_interactions2.rs index 4615ebd688a9c..0e96cfe785857 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions2.rs +++ b/tests/ui/half-open-range-patterns/range_pat_interactions2.rs @@ -8,8 +8,7 @@ fn main() { for x in -9 + 1..=(9 - 2) { match x as i32 { 0..=(5+1) => errors_only.push(x), - //~^ error: inclusive range with no end - //~| error: expected one of `=>`, `if`, or `|`, found `(` + //~^ error: expected `)`, found `+` 1 | -3..0 => first_or.push(x), y @ (0..5 | 6) => or_two.push(y), y @ 0..const { 5 + 1 } => assert_eq!(y, 5), diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr index 13a5542a4741f..a54f29a3b3263 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr +++ b/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr @@ -1,17 +1,8 @@ -error[E0586]: inclusive range with no end - --> $DIR/range_pat_interactions2.rs:10:14 +error: expected `)`, found `+` + --> $DIR/range_pat_interactions2.rs:10:19 | LL | 0..=(5+1) => errors_only.push(x), - | ^^^ help: use `..` instead - | - = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`) - -error: expected one of `=>`, `if`, or `|`, found `(` - --> $DIR/range_pat_interactions2.rs:10:17 - | -LL | 0..=(5+1) => errors_only.push(x), - | ^ expected one of `=>`, `if`, or `|` + | ^ expected `)` -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0586`. diff --git a/tests/ui/issues/issue-62375.stderr b/tests/ui/issues/issue-62375.stderr index 8750fbcf4cf75..faca94a03f018 100644 --- a/tests/ui/issues/issue-62375.stderr +++ b/tests/ui/issues/issue-62375.stderr @@ -11,11 +11,6 @@ note: an implementation of `PartialEq A {A::Value}>` might be missing | LL | enum A { | ^^^^^^ must implement `PartialEq A {A::Value}>` -help: consider annotating `A` with `#[derive(PartialEq)]` - | -LL + #[derive(PartialEq)] -LL | enum A { - | help: use parentheses to construct this tuple variant | LL | a == A::Value(/* () */); diff --git a/tests/ui/macros/issue-118786.stderr b/tests/ui/macros/issue-118786.stderr index ca3a40f31c1f5..1a8ac9340daa8 100644 --- a/tests/ui/macros/issue-118786.stderr +++ b/tests/ui/macros/issue-118786.stderr @@ -6,8 +6,8 @@ LL | macro_rules! $macro_name { | help: change the delimiters to curly braces | -LL | macro_rules! {} { - | ~ + +LL | macro_rules! {$macro_name} { + | + + help: add a semicolon | LL | macro_rules! $macro_name; { diff --git a/tests/ui/parser/issues/issue-68091-unicode-ident-after-if.stderr b/tests/ui/parser/issues/issue-68091-unicode-ident-after-if.stderr index 2423a7526be13..8e125864b8bce 100644 --- a/tests/ui/parser/issues/issue-68091-unicode-ident-after-if.stderr +++ b/tests/ui/parser/issues/issue-68091-unicode-ident-after-if.stderr @@ -1,10 +1,10 @@ error: missing condition for `if` expression - --> $DIR/issue-68091-unicode-ident-after-if.rs:3:14 + --> $DIR/issue-68091-unicode-ident-after-if.rs:3:13 | LL | $($c)ö* {} - | ^ - if this block is the condition of the `if` expression, then it must be followed by another block - | | - | expected condition here + | ^ - if this block is the condition of the `if` expression, then it must be followed by another block + | | + | expected condition here error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-68092-unicode-ident-after-incomplete-expr.stderr b/tests/ui/parser/issues/issue-68092-unicode-ident-after-incomplete-expr.stderr index 43047ff88020e..15aa62e08108a 100644 --- a/tests/ui/parser/issues/issue-68092-unicode-ident-after-incomplete-expr.stderr +++ b/tests/ui/parser/issues/issue-68092-unicode-ident-after-incomplete-expr.stderr @@ -1,8 +1,8 @@ error: macro expansion ends with an incomplete expression: expected expression - --> $DIR/issue-68092-unicode-ident-after-incomplete-expr.rs:3:14 + --> $DIR/issue-68092-unicode-ident-after-incomplete-expr.rs:3:13 | LL | $($c)ö* - | ^ expected expression + | ^ expected expression error: aborting due to 1 previous error diff --git a/tests/ui/parser/pat-recover-ranges.rs b/tests/ui/parser/pat-recover-ranges.rs new file mode 100644 index 0000000000000..65a6fc6fe21f3 --- /dev/null +++ b/tests/ui/parser/pat-recover-ranges.rs @@ -0,0 +1,19 @@ +fn main() { + match -1 { + 0..=1 => (), + 0..=(1) => (), + //~^ error: range pattern bounds cannot have parentheses + (-12)..=4 => (), + //~^ error: range pattern bounds cannot have parentheses + (0)..=(-4) => (), + //~^ error: range pattern bounds cannot have parentheses + //~| error: range pattern bounds cannot have parentheses + }; +} + +macro_rules! m { + ($pat:pat) => {}; + (($s:literal)..($e:literal)) => {}; +} + +m!((7)..(7)); diff --git a/tests/ui/parser/pat-recover-ranges.stderr b/tests/ui/parser/pat-recover-ranges.stderr new file mode 100644 index 0000000000000..0d722b5aa95c8 --- /dev/null +++ b/tests/ui/parser/pat-recover-ranges.stderr @@ -0,0 +1,50 @@ +error: range pattern bounds cannot have parentheses + --> $DIR/pat-recover-ranges.rs:4:13 + | +LL | 0..=(1) => (), + | ^ ^ + | +help: remove these parentheses + | +LL - 0..=(1) => (), +LL + 0..=1 => (), + | + +error: range pattern bounds cannot have parentheses + --> $DIR/pat-recover-ranges.rs:6:9 + | +LL | (-12)..=4 => (), + | ^ ^ + | +help: remove these parentheses + | +LL - (-12)..=4 => (), +LL + -12..=4 => (), + | + +error: range pattern bounds cannot have parentheses + --> $DIR/pat-recover-ranges.rs:8:9 + | +LL | (0)..=(-4) => (), + | ^ ^ + | +help: remove these parentheses + | +LL - (0)..=(-4) => (), +LL + 0..=(-4) => (), + | + +error: range pattern bounds cannot have parentheses + --> $DIR/pat-recover-ranges.rs:8:15 + | +LL | (0)..=(-4) => (), + | ^ ^ + | +help: remove these parentheses + | +LL - (0)..=(-4) => (), +LL + (0)..=-4 => (), + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/proc-macro/capture-macro-rules-invoke.stdout b/tests/ui/proc-macro/capture-macro-rules-invoke.stdout index 71e34119ba7ee..bbab08bca499e 100644 --- a/tests/ui/proc-macro/capture-macro-rules-invoke.stdout +++ b/tests/ui/proc-macro/capture-macro-rules-invoke.stdout @@ -271,7 +271,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ span: $DIR/capture-macro-rules-invoke.rs:47:19: 47:20 (#0), }, ], - span: $DIR/capture-macro-rules-invoke.rs:47:13: 47:22 (#0), + span: $DIR/capture-macro-rules-invoke.rs:15:60: 15:63 (#0), }, Punct { ch: ',', diff --git a/tests/ui/proc-macro/expand-expr.rs b/tests/ui/proc-macro/expand-expr.rs index 700aac41c449a..89cd1d767a5d8 100644 --- a/tests/ui/proc-macro/expand-expr.rs +++ b/tests/ui/proc-macro/expand-expr.rs @@ -37,7 +37,7 @@ expand_expr_is!("hello", stringify!(hello)); expand_expr_is!("10 + 20", stringify!(10 + 20)); macro_rules! echo_tts { - ($($t:tt)*) => { $($t)* }; //~ ERROR: expected expression, found `$` + ($($t:tt)*) => { $($t)* }; } macro_rules! echo_lit { @@ -109,7 +109,7 @@ expand_expr_fail!("string"; hello); //~ ERROR: expected one of `.`, `?`, or an o // Invalid expressions produce errors in addition to returning `Err(())`. expand_expr_fail!($); //~ ERROR: expected expression, found `$` -expand_expr_fail!(echo_tts!($)); +expand_expr_fail!(echo_tts!($)); //~ ERROR: expected expression, found `$` expand_expr_fail!(echo_pm!($)); //~ ERROR: expected expression, found `$` // We get errors reported and recover during macro expansion if the macro diff --git a/tests/ui/proc-macro/expand-expr.stderr b/tests/ui/proc-macro/expand-expr.stderr index df61e9972896b..2b92472e5ab75 100644 --- a/tests/ui/proc-macro/expand-expr.stderr +++ b/tests/ui/proc-macro/expand-expr.stderr @@ -11,10 +11,10 @@ LL | expand_expr_fail!($); | ^ expected expression error: expected expression, found `$` - --> $DIR/expand-expr.rs:40:23 + --> $DIR/expand-expr.rs:112:29 | -LL | ($($t:tt)*) => { $($t)* }; - | ^^^^ expected expression +LL | expand_expr_fail!(echo_tts!($)); + | ^ expected expression error: expected expression, found `$` --> $DIR/expand-expr.rs:113:28 diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-trait-bounds-trait-objects.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-trait-bounds-trait-objects.rs index 8670667131695..a00a6d481050d 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-trait-bounds-trait-objects.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-trait-bounds-trait-objects.rs @@ -1,4 +1,4 @@ -#![feature(const_trait_impl)] +#![feature(const_trait_impl, effects)] // edition: 2021 #[const_trait] @@ -6,4 +6,12 @@ trait Trait {} fn main() { let _: &dyn const Trait; //~ ERROR const trait bounds are not allowed in trait object types + let _: &dyn ~const Trait; //~ ERROR `~const` is not allowed here } + +// Regression test for issue #119525. +trait NonConst {} +const fn handle(_: &dyn const NonConst) {} +//~^ ERROR const trait bounds are not allowed in trait object types +const fn take(_: &dyn ~const NonConst) {} +//~^ ERROR `~const` is not allowed here diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-trait-bounds-trait-objects.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-trait-bounds-trait-objects.stderr index 8b9ba94d099d5..04c2dc2e2e0fd 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-trait-bounds-trait-objects.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-trait-bounds-trait-objects.stderr @@ -4,5 +4,27 @@ error: const trait bounds are not allowed in trait object types LL | let _: &dyn const Trait; | ^^^^^^^^^^^ -error: aborting due to 1 previous error +error: `~const` is not allowed here + --> $DIR/const-trait-bounds-trait-objects.rs:9:17 + | +LL | let _: &dyn ~const Trait; + | ^^^^^^ + | + = note: trait objects cannot have `~const` trait bounds + +error: const trait bounds are not allowed in trait object types + --> $DIR/const-trait-bounds-trait-objects.rs:14:25 + | +LL | const fn handle(_: &dyn const NonConst) {} + | ^^^^^^^^^^^^^^ + +error: `~const` is not allowed here + --> $DIR/const-trait-bounds-trait-objects.rs:16:23 + | +LL | const fn take(_: &dyn ~const NonConst) {} + | ^^^^^^ + | + = note: trait objects cannot have `~const` trait bounds + +error: aborting due to 4 previous errors diff --git a/tests/ui/sanitize/badfree.rs b/tests/ui/sanitize/badfree.rs index c8d1ce7dff25d..4a230e11d9579 100644 --- a/tests/ui/sanitize/badfree.rs +++ b/tests/ui/sanitize/badfree.rs @@ -5,7 +5,7 @@ // compile-flags: -Z sanitizer=address -O // // run-fail -// error-pattern: AddressSanitizer: SEGV +// regex-error-pattern: AddressSanitizer: (SEGV|attempting free on address which was not malloc) use std::ffi::c_void;