diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 1e4d3ba47f472..3ed342ce48b43 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2976,7 +2976,7 @@ pub enum ItemKind { } impl ItemKind { - pub fn article(&self) -> &str { + pub fn article(&self) -> &'static str { use ItemKind::*; match self { Use(..) | Static(..) | Const(..) | Fn(..) | Mod(..) | GlobalAsm(..) | TyAlias(..) @@ -2985,7 +2985,7 @@ impl ItemKind { } } - pub fn descr(&self) -> &str { + pub fn descr(&self) -> &'static str { match self { ItemKind::ExternCrate(..) => "extern crate", ItemKind::Use(..) => "`use` import", diff --git a/compiler/rustc_infer/src/traits/project.rs b/compiler/rustc_infer/src/traits/project.rs index 8d0af738dd115..e375d611936ea 100644 --- a/compiler/rustc_infer/src/traits/project.rs +++ b/compiler/rustc_infer/src/traits/project.rs @@ -20,7 +20,7 @@ pub struct MismatchedProjectionTypes<'tcx> { pub err: ty::error::TypeError<'tcx>, } -#[derive(Clone, TypeFoldable, TypeVisitable)] +#[derive(Clone)] pub struct Normalized<'tcx, T> { pub value: T, pub obligations: Vec>, diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index a6ba742201a3b..4d0a35368b35d 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1250,17 +1250,45 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - fn check_foreign_fn(&mut self, def_id: LocalDefId, decl: &hir::FnDecl<'_>) { + /// Check if a function's argument types and result type are "ffi-safe". + /// + /// Argument types and the result type are checked for functions with external ABIs. + /// For functions with internal ABIs, argument types and the result type are walked to find + /// fn-ptr types that have external ABIs, as these still need checked. + fn check_maybe_foreign_fn( + &mut self, + abi: SpecAbi, + def_id: LocalDefId, + decl: &'tcx hir::FnDecl<'_>, + ) { let sig = self.cx.tcx.fn_sig(def_id).subst_identity(); let sig = self.cx.tcx.erase_late_bound_regions(sig); + let is_internal_abi = self.is_internal_abi(abi); + let check_ty = |this: &mut ImproperCTypesVisitor<'a, 'tcx>, + hir_ty: &'tcx hir::Ty<'_>, + ty: Ty<'tcx>, + is_return_type: bool| { + // If this function has an external ABI, then its arguments and return type should be + // checked.. + if !is_internal_abi { + this.check_type_for_ffi_and_report_errors(hir_ty.span, ty, false, is_return_type); + return; + } + + // ..but if this function has an internal ABI, then search the argument or return type + // for any fn-ptr types with external ABI, which should be checked.. + for (fn_ptr_ty, span) in this.find_fn_ptr_ty_with_external_abi(hir_ty, ty) { + this.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, is_return_type); + } + }; + for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { - self.check_type_for_ffi_and_report_errors(input_hir.span, *input_ty, false, false); + check_ty(self, input_hir, *input_ty, false); } if let hir::FnRetTy::Return(ref ret_hir) = decl.output { - let ret_ty = sig.output(); - self.check_type_for_ffi_and_report_errors(ret_hir.span, ret_ty, false, true); + check_ty(self, ret_hir, sig.output(), true); } } @@ -1275,28 +1303,134 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { SpecAbi::Rust | SpecAbi::RustCall | SpecAbi::RustIntrinsic | SpecAbi::PlatformIntrinsic ) } + + /// Find any fn-ptr types with external ABIs in `ty`. + /// + /// For example, `Option` returns `extern "C" fn()` + fn find_fn_ptr_ty_with_external_abi( + &self, + hir_ty: &hir::Ty<'tcx>, + ty: Ty<'tcx>, + ) -> Vec<(Ty<'tcx>, Span)> { + struct FnPtrFinder<'parent, 'a, 'tcx> { + visitor: &'parent ImproperCTypesVisitor<'a, 'tcx>, + spans: Vec, + tys: Vec>, + } + + impl<'parent, 'a, 'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'parent, 'a, 'tcx> { + fn visit_ty(&mut self, ty: &'_ hir::Ty<'_>) { + debug!(?ty); + if let hir::TyKind::BareFn(hir::BareFnTy { abi, .. }) = ty.kind + && !self.visitor.is_internal_abi(*abi) + { + self.spans.push(ty.span); + } + + hir::intravisit::walk_ty(self, ty) + } + } + + impl<'vis, 'a, 'tcx> ty::visit::TypeVisitor> for FnPtrFinder<'vis, 'a, 'tcx> { + type BreakTy = Ty<'tcx>; + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { + if let ty::FnPtr(sig) = ty.kind() && !self.visitor.is_internal_abi(sig.abi()) { + self.tys.push(ty); + } + + ty.super_visit_with(self) + } + } + + let mut visitor = FnPtrFinder { visitor: &*self, spans: Vec::new(), tys: Vec::new() }; + self.cx + .tcx + .try_normalize_erasing_regions(self.cx.param_env, ty) + .unwrap_or(ty) + .visit_with(&mut visitor); + hir::intravisit::Visitor::visit_ty(&mut visitor, hir_ty); + + iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)).collect() + } } impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations { - fn check_foreign_item(&mut self, cx: &LateContext<'_>, it: &hir::ForeignItem<'_>) { + fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) { let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Declaration }; let abi = cx.tcx.hir().get_foreign_abi(it.hir_id()); - if !vis.is_internal_abi(abi) { - match it.kind { - hir::ForeignItemKind::Fn(ref decl, _, _) => { - vis.check_foreign_fn(it.owner_id.def_id, decl); - } - hir::ForeignItemKind::Static(ref ty, _) => { - vis.check_foreign_static(it.owner_id, ty.span); - } - hir::ForeignItemKind::Type => (), + match it.kind { + hir::ForeignItemKind::Fn(ref decl, _, _) => { + vis.check_maybe_foreign_fn(abi, it.owner_id.def_id, decl); } + hir::ForeignItemKind::Static(ref ty, _) if !vis.is_internal_abi(abi) => { + vis.check_foreign_static(it.owner_id, ty.span); + } + hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (), } } } +impl ImproperCTypesDefinitions { + fn check_ty_maybe_containing_foreign_fnptr<'tcx>( + &mut self, + cx: &LateContext<'tcx>, + hir_ty: &'tcx hir::Ty<'_>, + ty: Ty<'tcx>, + ) { + let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition }; + for (fn_ptr_ty, span) in vis.find_fn_ptr_ty_with_external_abi(hir_ty, ty) { + vis.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, true, false); + } + } +} + +/// `ImproperCTypesDefinitions` checks items outside of foreign items (e.g. stuff that isn't in +/// `extern "C" { }` blocks): +/// +/// - `extern "" fn` definitions are checked in the same way as the +/// `ImproperCtypesDeclarations` visitor checks functions if `` is external (e.g. "C"). +/// - All other items which contain types (e.g. other functions, struct definitions, etc) are +/// checked for extern fn-ptrs with external ABIs. impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { + match item.kind { + hir::ItemKind::Static(ty, ..) + | hir::ItemKind::Const(ty, ..) + | hir::ItemKind::TyAlias(ty, ..) => { + self.check_ty_maybe_containing_foreign_fnptr( + cx, + ty, + cx.tcx.type_of(item.owner_id).subst_identity(), + ); + } + // See `check_fn`.. + hir::ItemKind::Fn(..) => {} + // See `check_field_def`.. + hir::ItemKind::Union(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) => {} + // Doesn't define something that can contain a external type to be checked. + hir::ItemKind::Impl(..) + | hir::ItemKind::TraitAlias(..) + | hir::ItemKind::Trait(..) + | hir::ItemKind::OpaqueTy(..) + | hir::ItemKind::GlobalAsm(..) + | hir::ItemKind::ForeignMod { .. } + | hir::ItemKind::Mod(..) + | hir::ItemKind::Macro(..) + | hir::ItemKind::Use(..) + | hir::ItemKind::ExternCrate(..) => {} + } + } + + fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) { + self.check_ty_maybe_containing_foreign_fnptr( + cx, + field.ty, + cx.tcx.type_of(field.def_id).subst_identity(), + ); + } + fn check_fn( &mut self, cx: &LateContext<'tcx>, @@ -1315,9 +1449,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { }; let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition }; - if !vis.is_internal_abi(abi) { - vis.check_foreign_fn(id, decl); - } + vis.check_maybe_foreign_fn(abi, id, decl); } } diff --git a/compiler/rustc_middle/src/hir/place.rs b/compiler/rustc_middle/src/hir/place.rs index 80b4c964ce402..8a22de931c35b 100644 --- a/compiler/rustc_middle/src/hir/place.rs +++ b/compiler/rustc_middle/src/hir/place.rs @@ -66,7 +66,6 @@ pub struct Place<'tcx> { /// /// This is an HIR version of [`rustc_middle::mir::Place`]. #[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] -#[derive(TypeFoldable, TypeVisitable)] pub struct PlaceWithHirId<'tcx> { /// `HirId` of the expression or pattern producing this value. pub hir_id: HirId, diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 039194284b80f..2490b17aac09b 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -9,7 +9,7 @@ use crate::mir::visit::MirVisitable; use crate::ty::codec::{TyDecoder, TyEncoder}; use crate::ty::fold::{FallibleTypeFolder, TypeFoldable}; use crate::ty::print::{FmtPrinter, Printer}; -use crate::ty::visit::{TypeVisitable, TypeVisitableExt, TypeVisitor}; +use crate::ty::visit::TypeVisitableExt; use crate::ty::{self, List, Ty, TyCtxt}; use crate::ty::{AdtDef, InstanceDef, ScalarInt, UserTypeAnnotationIndex}; use crate::ty::{GenericArg, InternalSubsts, SubstsRef}; @@ -36,7 +36,7 @@ use either::Either; use std::borrow::Cow; use std::fmt::{self, Debug, Display, Formatter, Write}; -use std::ops::{ControlFlow, Index, IndexMut}; +use std::ops::{Index, IndexMut}; use std::{iter, mem}; pub use self::query::*; @@ -2722,6 +2722,7 @@ impl<'tcx> UserTypeProjections { /// `field[0]` (aka `.0`), indicating that the type of `s` is /// determined by finding the type of the `.0` field from `T`. #[derive(Clone, Debug, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)] +#[derive(TypeFoldable, TypeVisitable)] pub struct UserTypeProjection { pub base: UserTypeAnnotationIndex, pub projs: Vec, @@ -2765,28 +2766,6 @@ impl UserTypeProjection { } } -impl<'tcx> TypeFoldable> for UserTypeProjection { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - Ok(UserTypeProjection { - base: self.base.try_fold_with(folder)?, - projs: self.projs.try_fold_with(folder)?, - }) - } -} - -impl<'tcx> TypeVisitable> for UserTypeProjection { - fn visit_with>>( - &self, - visitor: &mut Vs, - ) -> ControlFlow { - self.base.visit_with(visitor) - // Note: there's nothing in `self.proj` to visit. - } -} - rustc_index::newtype_index! { #[derive(HashStable)] #[debug_format = "promoted[{}]"] diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index da86cfd477229..813e109c41e14 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -234,7 +234,6 @@ pub enum StmtKind<'tcx> { } #[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)] -#[derive(TypeFoldable, TypeVisitable)] pub struct LocalVarId(pub hir::HirId); /// A THIR expression. diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 6a8ae525069c0..02433026266a3 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -569,7 +569,7 @@ pub struct DerivedObligationCause<'tcx> { pub parent_code: InternedObligationCauseCode<'tcx>, } -#[derive(Clone, Debug, TypeFoldable, TypeVisitable, Lift)] +#[derive(Clone, Debug, TypeVisitable, Lift)] pub enum SelectionError<'tcx> { /// The trait is not implemented. Unimplemented, diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 1cc9fd526b44f..f2dda003b99c5 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -103,7 +103,7 @@ pub type EvaluationCache<'tcx> = Cache< /// required for associated types to work in default impls, as the bounds /// are visible both as projection bounds and as where-clauses from the /// parameter environment. -#[derive(PartialEq, Eq, Debug, Clone, TypeFoldable, TypeVisitable)] +#[derive(PartialEq, Eq, Debug, Clone, TypeVisitable)] pub enum SelectionCandidate<'tcx> { /// A builtin implementation for some specific traits, used in cases /// where we cannot rely an ordinary library implementations. diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index fef2be133e81f..6b7b910a59bf9 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -120,7 +120,7 @@ impl<'tcx> std::ops::Deref for ExternalConstraints<'tcx> { } /// Additional constraints returned on success. -#[derive(Debug, PartialEq, Eq, Clone, Hash, Default, TypeFoldable, TypeVisitable)] +#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)] pub struct ExternalConstraintsData<'tcx> { // FIXME: implement this. pub region_constraints: QueryRegionConstraints<'tcx>, diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index aff6c77e039af..1be61e16dbed8 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -28,7 +28,7 @@ impl ExpectedFound { } // Data structures used in type unification -#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, Lift, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, TypeVisitable, Lift, PartialEq, Eq)] #[rustc_pass_by_value] pub enum TypeError<'tcx> { Mismatch, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 1c1432ecd5a05..2aced27f7bbdc 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2690,7 +2690,7 @@ impl<'tcx> ty::PolyTraitPredicate<'tcx> { } } -#[derive(Debug, Copy, Clone, TypeFoldable, TypeVisitable, Lift)] +#[derive(Debug, Copy, Clone, Lift)] pub struct PrintClosureAsImpl<'tcx> { pub closure: ty::ClosureSubsts<'tcx>, } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index b35b514d795dc..29a3bc8bb9756 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -4,7 +4,6 @@ //! to help with the tedium. use crate::mir::interpret; -use crate::mir::ProjectionKind; use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}; use crate::ty::print::{with_no_trimmed_paths, FmtPrinter, Printer}; use crate::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; @@ -373,16 +372,6 @@ impl<'a, 'tcx> Lift<'tcx> for ty::ParamEnv<'a> { /////////////////////////////////////////////////////////////////////////// // Traversal implementations. -/// AdtDefs are basically the same as a DefId. -impl<'tcx> TypeFoldable> for ty::AdtDef<'tcx> { - fn try_fold_with>>( - self, - _folder: &mut F, - ) -> Result { - Ok(self) - } -} - impl<'tcx> TypeVisitable> for ty::AdtDef<'tcx> { fn visit_with>>( &self, @@ -445,15 +434,6 @@ impl<'tcx> TypeFoldable> for &'tcx ty::List> { } } -impl<'tcx> TypeFoldable> for &'tcx ty::List { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - ty::util::fold_list(self, folder, |tcx, v| tcx.mk_projs(v)) - } -} - impl<'tcx> TypeFoldable> for Ty<'tcx> { fn try_fold_with>>( self, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 82dec7d98ad5f..7bda20ffe9a32 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -631,7 +631,7 @@ impl<'tcx> UpvarSubsts<'tcx> { /// type of the constant. The reason that `R` is represented as an extra type parameter /// is the same reason that [`ClosureSubsts`] have `CS` and `U` as type parameters: /// inline const can reference lifetimes that are internal to the creating function. -#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable)] +#[derive(Copy, Clone, Debug)] pub struct InlineConstSubsts<'tcx> { /// Generic parameters from the enclosing item, /// concatenated with the inferred type of the constant. diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index d45fa90a11b08..9c4fac84fc294 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -772,3 +772,75 @@ parse_const_bounds_missing_tilde = const bounds must start with `~` .suggestion = add `~` parse_underscore_literal_suffix = underscore literal suffix is not allowed + +parse_expect_label_found_ident = expected a label, found an identifier + .suggestion = labels start with a tick + +parse_inappropriate_default = {$article} {$descr} cannot be `default` + .label = `default` because of this + .note = only associated `fn`, `const`, and `type` items can be `default` + +parse_recover_import_as_use = expected item, found {$token_name} + .suggestion = items are imported using the `use` keyword + +parse_single_colon_import_path = expected `::`, found `:` + .suggestion = use double colon + .note = import paths are delimited using `::` + +parse_bad_item_kind = {$descr} is not supported in {$ctx} + .help = consider moving the {$descr} out to a nearby module scope + +parse_single_colon_struct_type = found single colon in a struct field type path + .suggestion = write a path separator here + +parse_equals_struct_default = default values on `struct` fields aren't supported + .suggestion = remove this unsupported default value + +parse_macro_rules_missing_bang = expected `!` after `macro_rules` + .suggestion = add a `!` + +parse_macro_name_remove_bang = macro names aren't followed by a `!` + .suggestion = remove the `!` + +parse_macro_rules_visibility = can't qualify macro_rules invocation with `{$vis}` + .suggestion = try exporting the macro + +parse_macro_invocation_visibility = can't qualify macro invocation with `pub` + .suggestion = remove the visibility + .help = try adjusting the macro to put `{$vis}` inside the invocation + +parse_nested_adt = `{$kw_str}` definition cannot be nested inside `{$keyword}` + .suggestion = consider creating a new `{$kw_str}` definition instead of nesting + +parse_function_body_equals_expr = function body cannot be `= expression;` + .suggestion = surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;` + +parse_box_not_pat = expected pattern, found {$descr} + .note = `box` is a reserved keyword + .suggestion = escape `box` to use it as an identifier + +parse_unmatched_angle = unmatched angle {$plural -> + [true] brackets + *[false] bracket + } + .suggestion = remove extra angle {$plural -> + [true] brackets + *[false] bracket + } + +parse_missing_plus_in_bounds = expected `+` between lifetime and {$sym} + .suggestion = add `+` + +parse_incorrect_braces_trait_bounds = incorrect braces around trait bounds + .suggestion = remove the parentheses + +parse_kw_bad_case = keyword `{$kw}` is written in the wrong case + .suggestion = write it in the correct case + +parse_meta_bad_delim = wrong meta list delimiters +parse_cfg_attr_bad_delim = wrong `cfg_attr` delimiters +parse_meta_bad_delim_suggestion = the delimiters should be `(` and `)` + +parse_malformed_cfg_attr = malformed `cfg_attr` attribute input + .suggestion = missing condition and attribute + .note = for more information, visit diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index b0e1189851a1b..f286707a9c0df 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2421,3 +2421,227 @@ pub(crate) struct UnderscoreLiteralSuffix { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(parse_expect_label_found_ident)] +pub(crate) struct ExpectedLabelFoundIdent { + #[primary_span] + pub span: Span, + #[suggestion(code = "'", applicability = "machine-applicable", style = "short")] + pub start: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_inappropriate_default)] +#[note] +pub(crate) struct InappropriateDefault { + #[primary_span] + #[label] + pub span: Span, + pub article: &'static str, + pub descr: &'static str, +} + +#[derive(Diagnostic)] +#[diag(parse_recover_import_as_use)] +pub(crate) struct RecoverImportAsUse { + #[primary_span] + #[suggestion(code = "use", applicability = "machine-applicable", style = "short")] + pub span: Span, + pub token_name: String, +} + +#[derive(Diagnostic)] +#[diag(parse_single_colon_import_path)] +#[note] +pub(crate) struct SingleColonImportPath { + #[primary_span] + #[suggestion(code = "::", applicability = "machine-applicable", style = "short")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_bad_item_kind)] +#[help] +pub(crate) struct BadItemKind { + #[primary_span] + pub span: Span, + pub descr: &'static str, + pub ctx: &'static str, +} + +#[derive(Diagnostic)] +#[diag(parse_single_colon_struct_type)] +pub(crate) struct SingleColonStructType { + #[primary_span] + #[suggestion(code = "::", applicability = "maybe-incorrect", style = "verbose")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_equals_struct_default)] +pub(crate) struct EqualsStructDefault { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_macro_rules_missing_bang)] +pub(crate) struct MacroRulesMissingBang { + #[primary_span] + pub span: Span, + #[suggestion(code = "!", applicability = "machine-applicable", style = "verbose")] + pub hi: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_macro_name_remove_bang)] +pub(crate) struct MacroNameRemoveBang { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_macro_rules_visibility)] +pub(crate) struct MacroRulesVisibility<'a> { + #[primary_span] + #[suggestion(code = "#[macro_export]", applicability = "maybe-incorrect")] + pub span: Span, + pub vis: &'a str, +} + +#[derive(Diagnostic)] +#[diag(parse_macro_invocation_visibility)] +#[help] +pub(crate) struct MacroInvocationVisibility<'a> { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub span: Span, + pub vis: &'a str, +} + +#[derive(Diagnostic)] +#[diag(parse_nested_adt)] +pub(crate) struct NestedAdt<'a> { + #[primary_span] + pub span: Span, + #[suggestion(code = "", applicability = "maybe-incorrect")] + pub item: Span, + pub keyword: &'a str, + pub kw_str: Cow<'a, str>, +} + +#[derive(Diagnostic)] +#[diag(parse_function_body_equals_expr)] +pub(crate) struct FunctionBodyEqualsExpr { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub sugg: FunctionBodyEqualsExprSugg, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +pub(crate) struct FunctionBodyEqualsExprSugg { + #[suggestion_part(code = "{{")] + pub eq: Span, + #[suggestion_part(code = " }}")] + pub semi: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_box_not_pat)] +pub(crate) struct BoxNotPat { + #[primary_span] + pub span: Span, + #[note] + pub kw: Span, + #[suggestion(code = "r#", applicability = "maybe-incorrect", style = "verbose")] + pub lo: Span, + pub descr: String, +} + +#[derive(Diagnostic)] +#[diag(parse_unmatched_angle)] +pub(crate) struct UnmatchedAngle { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub span: Span, + pub plural: bool, +} + +#[derive(Diagnostic)] +#[diag(parse_missing_plus_in_bounds)] +pub(crate) struct MissingPlusBounds { + #[primary_span] + pub span: Span, + #[suggestion(code = " +", applicability = "maybe-incorrect", style = "verbose")] + pub hi: Span, + pub sym: Symbol, +} + +#[derive(Diagnostic)] +#[diag(parse_incorrect_braces_trait_bounds)] +pub(crate) struct IncorrectBracesTraitBounds { + #[primary_span] + pub span: Vec, + #[subdiagnostic] + pub sugg: IncorrectBracesTraitBoundsSugg, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +pub(crate) struct IncorrectBracesTraitBoundsSugg { + #[suggestion_part(code = " ")] + pub l: Span, + #[suggestion_part(code = "")] + pub r: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_kw_bad_case)] +pub(crate) struct KwBadCase<'a> { + #[primary_span] + #[suggestion(code = "{kw}", applicability = "machine-applicable")] + pub span: Span, + pub kw: &'a str, +} + +#[derive(Diagnostic)] +#[diag(parse_meta_bad_delim)] +pub(crate) struct MetaBadDelim { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub sugg: MetaBadDelimSugg, +} + +#[derive(Diagnostic)] +#[diag(parse_cfg_attr_bad_delim)] +pub(crate) struct CfgAttrBadDelim { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub sugg: MetaBadDelimSugg, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(parse_meta_bad_delim_suggestion, applicability = "machine-applicable")] +pub(crate) struct MetaBadDelimSugg { + #[suggestion_part(code = "(")] + pub open: Span, + #[suggestion_part(code = ")")] + pub close: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_malformed_cfg_attr)] +#[note] +pub(crate) struct MalformedCfgAttr { + #[primary_span] + #[suggestion(code = "{sugg}")] + pub span: Span, + pub sugg: &'static str, +} diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 507f6e4182eec..61a1cdeb540e9 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -18,7 +18,7 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AttrItem, Attribute, MetaItem}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; -use rustc_errors::{Applicability, Diagnostic, FatalError, Level, PResult}; +use rustc_errors::{Diagnostic, FatalError, Level, PResult}; use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage}; use rustc_fluent_macro::fluent_messages; use rustc_session::parse::ParseSess; @@ -243,8 +243,7 @@ pub fn parse_cfg_attr( ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, ref tokens }) if !tokens.is_empty() => { - let msg = "wrong `cfg_attr` delimiters"; - crate::validate_attr::check_meta_bad_delim(parse_sess, dspan, delim, msg); + crate::validate_attr::check_cfg_attr_bad_delim(parse_sess, dspan, delim); match parse_in(parse_sess, tokens.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) { Ok(r) => return Some(r), Err(mut e) => { @@ -265,15 +264,5 @@ const CFG_ATTR_NOTE_REF: &str = "for more information, visit \ #the-cfg_attr-attribute>"; fn error_malformed_cfg_attr_missing(span: Span, parse_sess: &ParseSess) { - parse_sess - .span_diagnostic - .struct_span_err(span, "malformed `cfg_attr` attribute input") - .span_suggestion( - span, - "missing condition and attribute", - CFG_ATTR_GRAMMAR_HELP, - Applicability::HasPlaceholders, - ) - .note(CFG_ATTR_NOTE_REF) - .emit(); + parse_sess.emit_err(errors::MalformedCfgAttr { span, sugg: CFG_ATTR_GRAMMAR_HELP }); } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 03c82fbd329fc..27de9bd72685f 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3151,14 +3151,10 @@ impl<'a> Parser<'a> { let label = format!("'{}", ident.name); let ident = Ident { name: Symbol::intern(&label), span: ident.span }; - self.struct_span_err(ident.span, "expected a label, found an identifier") - .span_suggestion( - ident.span, - "labels start with a tick", - label, - Applicability::MachineApplicable, - ) - .emit(); + self.sess.emit_err(errors::ExpectedLabelFoundIdent { + span: ident.span, + start: ident.span.shrink_to_lo(), + }); Label { ident } } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index f5fef6ad019eb..9e003bfc09747 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -181,11 +181,11 @@ impl<'a> Parser<'a> { /// Error in-case `default` was parsed in an in-appropriate context. fn error_on_unconsumed_default(&self, def: Defaultness, kind: &ItemKind) { if let Defaultness::Default(span) = def { - let msg = format!("{} {} cannot be `default`", kind.article(), kind.descr()); - self.struct_span_err(span, &msg) - .span_label(span, "`default` because of this") - .note("only associated `fn`, `const`, and `type` items can be `default`") - .emit(); + self.sess.emit_err(errors::InappropriateDefault { + span, + article: kind.article(), + descr: kind.descr(), + }); } } @@ -310,14 +310,7 @@ impl<'a> Parser<'a> { self.bump(); match self.parse_use_item() { Ok(u) => { - self.struct_span_err(span, format!("expected item, found {token_name}")) - .span_suggestion_short( - span, - "items are imported using the `use` keyword", - "use", - Applicability::MachineApplicable, - ) - .emit(); + self.sess.emit_err(errors::RecoverImportAsUse { span, token_name }); Ok(Some(u)) } Err(e) => { @@ -963,15 +956,8 @@ impl<'a> Parser<'a> { } else { // Recover from using a colon as path separator. while self.eat_noexpect(&token::Colon) { - self.struct_span_err(self.prev_token.span, "expected `::`, found `:`") - .span_suggestion_short( - self.prev_token.span, - "use double colon", - "::", - Applicability::MachineApplicable, - ) - .note_once("import paths are delimited using `::`") - .emit(); + self.sess + .emit_err(errors::SingleColonImportPath { span: self.prev_token.span }); // We parse the rest of the path and append it to the original prefix. self.parse_path_segments(&mut prefix.segments, PathStyle::Mod, None)?; @@ -1134,13 +1120,11 @@ impl<'a> Parser<'a> { )) } - fn error_bad_item_kind(&self, span: Span, kind: &ItemKind, ctx: &str) -> Option { + fn error_bad_item_kind(&self, span: Span, kind: &ItemKind, ctx: &'static str) -> Option { // FIXME(#100717): needs variant for each `ItemKind` (instead of using `ItemKind::descr()`) let span = self.sess.source_map().guess_head_span(span); let descr = kind.descr(); - self.struct_span_err(span, &format!("{descr} is not supported in {ctx}")) - .help(&format!("consider moving the {descr} out to a nearby module scope")) - .emit(); + self.sess.emit_err(errors::BadItemKind { span, descr, ctx }); None } @@ -1713,27 +1697,13 @@ impl<'a> Parser<'a> { self.expect_field_ty_separator()?; let ty = self.parse_ty()?; if self.token.kind == token::Colon && self.look_ahead(1, |tok| tok.kind != token::Colon) { - self.struct_span_err(self.token.span, "found single colon in a struct field type path") - .span_suggestion_verbose( - self.token.span, - "write a path separator here", - "::", - Applicability::MaybeIncorrect, - ) - .emit(); + self.sess.emit_err(errors::SingleColonStructType { span: self.token.span }); } if self.token.kind == token::Eq { self.bump(); let const_expr = self.parse_expr_anon_const()?; let sp = ty.span.shrink_to_hi().to(const_expr.value.span); - self.struct_span_err(sp, "default values on `struct` fields aren't supported") - .span_suggestion( - sp, - "remove this unsupported default value", - "", - Applicability::MachineApplicable, - ) - .emit(); + self.sess.emit_err(errors::EqualsStructDefault { span: sp }); } Ok(FieldDef { span: lo.to(self.prev_token.span), @@ -1871,14 +1841,10 @@ impl<'a> Parser<'a> { return IsMacroRulesItem::Yes { has_bang: true }; } else if self.look_ahead(1, |t| (t.is_ident())) { // macro_rules foo - self.struct_span_err(macro_rules_span, "expected `!` after `macro_rules`") - .span_suggestion( - macro_rules_span, - "add a `!`", - "macro_rules!", - Applicability::MachineApplicable, - ) - .emit(); + self.sess.emit_err(errors::MacroRulesMissingBang { + span: macro_rules_span, + hi: macro_rules_span.shrink_to_hi(), + }); return IsMacroRulesItem::Yes { has_bang: false }; } @@ -1903,9 +1869,7 @@ impl<'a> Parser<'a> { if self.eat(&token::Not) { // Handle macro_rules! foo! let span = self.prev_token.span; - self.struct_span_err(span, "macro names aren't followed by a `!`") - .span_suggestion(span, "remove the `!`", "", Applicability::MachineApplicable) - .emit(); + self.sess.emit_err(errors::MacroNameRemoveBang { span }); } let body = self.parse_delim_args()?; @@ -1925,25 +1889,9 @@ impl<'a> Parser<'a> { let vstr = pprust::vis_to_string(vis); let vstr = vstr.trim_end(); if macro_rules { - let msg = format!("can't qualify macro_rules invocation with `{vstr}`"); - self.struct_span_err(vis.span, &msg) - .span_suggestion( - vis.span, - "try exporting the macro", - "#[macro_export]", - Applicability::MaybeIncorrect, // speculative - ) - .emit(); + self.sess.emit_err(errors::MacroRulesVisibility { span: vis.span, vis: vstr }); } else { - self.struct_span_err(vis.span, "can't qualify macro invocation with `pub`") - .span_suggestion( - vis.span, - "remove the visibility", - "", - Applicability::MachineApplicable, - ) - .help(&format!("try adjusting the macro to put `{vstr}` inside the invocation")) - .emit(); + self.sess.emit_err(errors::MacroInvocationVisibility { span: vis.span, vis: vstr }); } } @@ -1989,18 +1937,12 @@ impl<'a> Parser<'a> { let kw_token = self.token.clone(); let kw_str = pprust::token_to_string(&kw_token); let item = self.parse_item(ForceCollect::No)?; - - self.struct_span_err( - kw_token.span, - &format!("`{kw_str}` definition cannot be nested inside `{keyword}`"), - ) - .span_suggestion( - item.unwrap().span, - &format!("consider creating a new `{kw_str}` definition instead of nesting"), - "", - Applicability::MaybeIncorrect, - ) - .emit(); + self.sess.emit_err(errors::NestedAdt { + span: kw_token.span, + item: item.unwrap().span, + kw_str, + keyword: keyword.as_str(), + }); // We successfully parsed the item but we must inform the caller about nested problem. return Ok(false); } @@ -2139,13 +2081,10 @@ impl<'a> Parser<'a> { let _ = self.parse_expr()?; self.expect_semi()?; // `;` let span = eq_sp.to(self.prev_token.span); - self.struct_span_err(span, "function body cannot be `= expression;`") - .multipart_suggestion( - "surround the expression with `{` and `}` instead of `=` and `;`", - vec![(eq_sp, "{".to_string()), (self.prev_token.span, " }".to_string())], - Applicability::MachineApplicable, - ) - .emit(); + self.sess.emit_err(errors::FunctionBodyEqualsExpr { + span, + sugg: errors::FunctionBodyEqualsExprSugg { eq: eq_sp, semi: self.prev_token.span }, + }); (AttrVec::new(), Some(self.mk_block_err(span))) } else { let expected = if req_body { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index aa57b804779b0..1c34e491f210e 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -43,7 +43,7 @@ use thin_vec::ThinVec; use tracing::debug; use crate::errors::{ - IncorrectVisibilityRestriction, MismatchedClosingDelimiter, NonStringAbiLiteral, + self, IncorrectVisibilityRestriction, MismatchedClosingDelimiter, NonStringAbiLiteral, }; bitflags::bitflags! { @@ -663,15 +663,10 @@ impl<'a> Parser<'a> { if case == Case::Insensitive && let Some((ident, /* is_raw */ false)) = self.token.ident() && ident.as_str().to_lowercase() == kw.as_str().to_lowercase() { - self - .struct_span_err(ident.span, format!("keyword `{kw}` is written in a wrong case")) - .span_suggestion( - ident.span, - "write it in the correct case", - kw, - Applicability::MachineApplicable - ).emit(); - + self.sess.emit_err(errors::KwBadCase { + span: ident.span, + kw: kw.as_str() + }); self.bump(); return true; } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 2246002f5d32a..f2422fe307c5f 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1,6 +1,6 @@ use super::{ForceCollect, Parser, PathStyle, TrailingToken}; use crate::errors::{ - AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed, + self, AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed, DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt, ExpectedCommaAfterPatternField, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern, PatternOnWrongSideOfAt, RefMutOrderIncorrect, @@ -908,18 +908,13 @@ impl<'a> Parser<'a> { let box_span = self.prev_token.span; if self.isnt_pattern_start() { - self.struct_span_err( - self.token.span, - format!("expected pattern, found {}", super::token_descr(&self.token)), - ) - .span_note(box_span, "`box` is a reserved keyword") - .span_suggestion_verbose( - box_span.shrink_to_lo(), - "escape `box` to use it as an identifier", - "r#", - Applicability::MaybeIncorrect, - ) - .emit(); + let descr = super::token_descr(&self.token); + self.sess.emit_err(errors::BoxNotPat { + span: self.token.span, + kw: box_span, + lo: box_span.shrink_to_lo(), + descr, + }); // We cannot use `parse_pat_ident()` since it will complain `box` // is not an identifier. diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 6cceb47ff8384..ae73760bd8cab 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -8,7 +8,7 @@ use rustc_ast::{ AssocConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs, Path, PathSegment, QSelf, }; -use rustc_errors::{pluralize, Applicability, PResult}; +use rustc_errors::{Applicability, PResult}; use rustc_span::source_map::{BytePos, Span}; use rustc_span::symbol::{kw, sym, Ident}; use std::mem; @@ -464,23 +464,10 @@ impl<'a> Parser<'a> { // i.e. no multibyte characters, in this range. let span = lo.with_hi(lo.lo() + BytePos(snapshot.unmatched_angle_bracket_count)); - self.struct_span_err( + self.sess.emit_err(errors::UnmatchedAngle { span, - &format!( - "unmatched angle bracket{}", - pluralize!(snapshot.unmatched_angle_bracket_count) - ), - ) - .span_suggestion( - span, - &format!( - "remove extra angle bracket{}", - pluralize!(snapshot.unmatched_angle_bracket_count) - ), - "", - Applicability::MachineApplicable, - ) - .emit(); + plural: snapshot.unmatched_angle_bracket_count > 1, + }); // Try again without unmatched angle bracket characters. self.parse_angle_args(ty_generics) diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index f5f6788362ba9..3ceb3a2bef16e 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -588,20 +588,14 @@ impl<'a> Parser<'a> { // Always parse bounds greedily for better error recovery. if self.token.is_lifetime() { self.look_ahead(1, |t| { - if let token::Ident(symname, _) = t.kind { + if let token::Ident(sym, _) = t.kind { // parse pattern with "'a Sized" we're supposed to give suggestion like // "'a + Sized" - self.struct_span_err( - self.token.span, - &format!("expected `+` between lifetime and {}", symname), - ) - .span_suggestion_verbose( - self.token.span.shrink_to_hi(), - "add `+`", - " +", - Applicability::MaybeIncorrect, - ) - .emit(); + self.sess.emit_err(errors::MissingPlusBounds { + span: self.token.span, + hi: self.token.span.shrink_to_hi(), + sym, + }); } }) } @@ -926,14 +920,10 @@ impl<'a> Parser<'a> { self.parse_remaining_bounds(bounds, true)?; self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; let sp = vec![lo, self.prev_token.span]; - let sugg = vec![(lo, String::from(" ")), (self.prev_token.span, String::new())]; - self.struct_span_err(sp, "incorrect braces around trait bounds") - .multipart_suggestion( - "remove the parentheses", - sugg, - Applicability::MachineApplicable, - ) - .emit(); + self.sess.emit_err(errors::IncorrectBracesTraitBounds { + span: sp, + sugg: errors::IncorrectBracesTraitBoundsSugg { l: lo, r: self.prev_token.span }, + }); } else { self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; } diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 2f397e303e508..815b7c8567918 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -1,6 +1,6 @@ //! Meta-syntax validation logic of attributes for post-expansion. -use crate::parse_in; +use crate::{errors, parse_in}; use rustc_ast::tokenstream::DelimSpan; use rustc_ast::MetaItemKind; @@ -45,7 +45,7 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta kind: match &item.args { AttrArgs::Empty => MetaItemKind::Word, AttrArgs::Delimited(DelimArgs { dspan, delim, tokens }) => { - check_meta_bad_delim(sess, *dspan, *delim, "wrong meta list delimiters"); + check_meta_bad_delim(sess, *dspan, *delim); let nmis = parse_in(sess, tokens.clone(), "meta list", |p| p.parse_meta_seq_top())?; MetaItemKind::List(nmis) } @@ -84,19 +84,24 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta }) } -pub fn check_meta_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimiter, msg: &str) { +pub fn check_meta_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimiter) { if let ast::MacDelimiter::Parenthesis = delim { return; } + sess.emit_err(errors::MetaBadDelim { + span: span.entire(), + sugg: errors::MetaBadDelimSugg { open: span.open, close: span.close }, + }); +} - sess.span_diagnostic - .struct_span_err(span.entire(), msg) - .multipart_suggestion( - "the delimiters should be `(` and `)`", - vec![(span.open, "(".to_string()), (span.close, ")".to_string())], - Applicability::MachineApplicable, - ) - .emit(); +pub fn check_cfg_attr_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimiter) { + if let ast::MacDelimiter::Parenthesis = delim { + return; + } + sess.emit_err(errors::CfgAttrBadDelim { + span: span.entire(), + sugg: errors::MetaBadDelimSugg { open: span.open, close: span.close }, + }); } /// Checks that the given meta-item is compatible with this `AttributeTemplate`. diff --git a/compiler/rustc_target/src/abi/call/x86_64.rs b/compiler/rustc_target/src/abi/call/x86_64.rs index 9427f27d1b7bb..2470cdfebe225 100644 --- a/compiler/rustc_target/src/abi/call/x86_64.rs +++ b/compiler/rustc_target/src/abi/call/x86_64.rs @@ -153,9 +153,9 @@ fn reg_component(cls: &[Option], i: &mut usize, size: Size) -> Option], size: Size) -> CastTarget { +fn cast_target(cls: &[Option], size: Size) -> Option { let mut i = 0; - let lo = reg_component(cls, &mut i, size).unwrap(); + let lo = reg_component(cls, &mut i, size)?; let offset = Size::from_bytes(8) * (i as u64); let mut target = CastTarget::from(lo); if size > offset { @@ -164,7 +164,7 @@ fn cast_target(cls: &[Option], size: Size) -> CastTarget { } } assert_eq!(reg_component(cls, &mut i, Size::ZERO), None); - target + Some(target) } const MAX_INT_REGS: usize = 6; // RDI, RSI, RDX, RCX, R8, R9 @@ -227,7 +227,9 @@ where // split into sized chunks passed individually if arg.layout.is_aggregate() { let size = arg.layout.size; - arg.cast_to(cast_target(cls, size)) + if let Some(cast_target) = cast_target(cls, size) { + arg.cast_to(cast_target); + } } else { arg.extend_integer_width_to(32); } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index bd52957d162f2..63a73f8d50d93 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -3,7 +3,8 @@ use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{ - DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, TyCtxtInferExt, + DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, RegionVariableOrigin, + TyCtxtInferExt, }; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::ObligationCause; @@ -223,18 +224,20 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { { debug!("rerunning goal to check result is stable"); let (_orig_values, canonical_goal) = self.canonicalize_goal(goal); - let canonical_response = + let new_canonical_response = EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?; - if !canonical_response.value.var_values.is_identity() { + if !new_canonical_response.value.var_values.is_identity() { bug!( "unstable result: re-canonicalized goal={canonical_goal:#?} \ - response={canonical_response:#?}" + first_response={canonical_response:#?} \ + second_response={new_canonical_response:#?}" ); } - if certainty != canonical_response.value.certainty { + if certainty != new_canonical_response.value.certainty { bug!( "unstable certainty: {certainty:#?} re-canonicalized goal={canonical_goal:#?} \ - response={canonical_response:#?}" + first_response={canonical_response:#?} \ + second_response={new_canonical_response:#?}" ); } } @@ -434,6 +437,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }) } + pub(super) fn next_region_infer(&self) -> ty::Region<'tcx> { + self.infcx.next_region_var(RegionVariableOrigin::MiscVariable(DUMMY_SP)) + } + pub(super) fn next_const_infer(&self, ty: Ty<'tcx>) -> ty::Const<'tcx> { self.infcx.next_const_var( ty, diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 226d29687e3a8..67ad7fb4bd21d 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -16,7 +16,7 @@ use rustc_infer::infer::canonical::query_response::make_query_region_constraints use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints}; use rustc_middle::traits::query::NoSolution; -use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData}; +use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData, MaybeCause}; use rustc_middle::ty::{self, BoundVar, GenericArgKind}; use rustc_span::DUMMY_SP; use std::iter; @@ -60,9 +60,27 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let certainty = certainty.unify_with(goals_certainty); - let external_constraints = self.compute_external_query_constraints()?; + let response = match certainty { + Certainty::Yes | Certainty::Maybe(MaybeCause::Ambiguity) => { + let external_constraints = self.compute_external_query_constraints()?; + Response { var_values: self.var_values, external_constraints, certainty } + } + Certainty::Maybe(MaybeCause::Overflow) => { + // If we have overflow, it's probable that we're substituting a type + // into itself infinitely and any partial substitutions in the query + // response are probably not useful anyways, so just return an empty + // query response. + // + // This may prevent us from potentially useful inference, e.g. + // 2 candidates, one ambiguous and one overflow, which both + // have the same inference constraints. + // + // Changing this to retain some constraints in the future + // won't be a breaking change, so this is good enough for now. + return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow)); + } + }; - let response = Response { var_values: self.var_values, external_constraints, certainty }; let canonical = Canonicalizer::canonicalize( self.infcx, CanonicalizeMode::Response { max_input_universe: self.max_input_universe }, @@ -72,6 +90,40 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Ok(canonical) } + /// Constructs a totally unconstrained, ambiguous response to a goal. + /// + /// Take care when using this, since often it's useful to respond with + /// ambiguity but return constrained variables to guide inference. + pub(in crate::solve) fn make_ambiguous_response_no_constraints( + &self, + maybe_cause: MaybeCause, + ) -> CanonicalResponse<'tcx> { + let unconstrained_response = Response { + var_values: CanonicalVarValues { + var_values: self.tcx().mk_substs_from_iter(self.var_values.var_values.iter().map( + |arg| -> ty::GenericArg<'tcx> { + match arg.unpack() { + GenericArgKind::Lifetime(_) => self.next_region_infer().into(), + GenericArgKind::Type(_) => self.next_ty_infer().into(), + GenericArgKind::Const(ct) => self.next_const_infer(ct.ty()).into(), + } + }, + )), + }, + external_constraints: self + .tcx() + .mk_external_constraints(ExternalConstraintsData::default()), + certainty: Certainty::Maybe(maybe_cause), + }; + + Canonicalizer::canonicalize( + self.infcx, + CanonicalizeMode::Response { max_input_universe: self.max_input_universe }, + &mut Default::default(), + unconstrained_response, + ) + } + #[instrument(level = "debug", skip(self), ret)] fn compute_external_query_constraints(&self) -> Result, NoSolution> { // Cannot use `take_registered_region_obligations` as we may compute the response diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 19bcbd461447d..d94679fef2833 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -340,17 +340,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { if responses.is_empty() { return Err(NoSolution); } - let certainty = responses.iter().fold(Certainty::AMBIGUOUS, |certainty, response| { - certainty.unify_with(response.value.certainty) - }); - - let response = self.evaluate_added_goals_and_make_canonical_response(certainty); - if let Ok(response) = response { - assert!(response.has_no_inference_or_external_constraints()); - Ok(response) - } else { - bug!("failed to make floundered response: {responses:?}"); - } + + let Certainty::Maybe(maybe_cause) = responses.iter().fold( + Certainty::AMBIGUOUS, + |certainty, response| { + certainty.unify_with(response.value.certainty) + }, + ) else { + bug!("expected flounder response to be ambiguous") + }; + + Ok(self.make_ambiguous_response_no_constraints(maybe_cause)) } } diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index 8be02c1d9888a..77c0526e3aabe 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -62,7 +62,7 @@ mod rustc { use rustc_hir::lang_items::LangItem; use rustc_infer::infer::InferCtxt; - use rustc_macros::{TypeFoldable, TypeVisitable}; + use rustc_macros::TypeVisitable; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::Const; use rustc_middle::ty::ParamEnv; @@ -70,7 +70,7 @@ mod rustc { use rustc_middle::ty::TyCtxt; /// The source and destination types of a transmutation. - #[derive(TypeFoldable, TypeVisitable, Debug, Clone, Copy)] + #[derive(TypeVisitable, Debug, Clone, Copy)] pub struct Types<'tcx> { /// The source type. pub src: Ty<'tcx>, diff --git a/compiler/rustc_type_ir/src/structural_impls.rs b/compiler/rustc_type_ir/src/structural_impls.rs index 54c1ab5a275ef..c9675f93f9564 100644 --- a/compiler/rustc_type_ir/src/structural_impls.rs +++ b/compiler/rustc_type_ir/src/structural_impls.rs @@ -6,11 +6,10 @@ use crate::fold::{FallibleTypeFolder, TypeFoldable}; use crate::visit::{TypeVisitable, TypeVisitor}; use crate::Interner; use rustc_data_structures::functor::IdFunctor; +use rustc_data_structures::sync::Lrc; use rustc_index::{Idx, IndexVec}; use std::ops::ControlFlow; -use std::rc::Rc; -use std::sync::Arc; /////////////////////////////////////////////////////////////////////////// // Atomic structs @@ -106,25 +105,13 @@ impl, E: TypeVisitable> TypeVisitable for } } -impl> TypeFoldable for Rc { +impl> TypeFoldable for Lrc { fn try_fold_with>(self, folder: &mut F) -> Result { self.try_map_id(|value| value.try_fold_with(folder)) } } -impl> TypeVisitable for Rc { - fn visit_with>(&self, visitor: &mut V) -> ControlFlow { - (**self).visit_with(visitor) - } -} - -impl> TypeFoldable for Arc { - fn try_fold_with>(self, folder: &mut F) -> Result { - self.try_map_id(|value| value.try_fold_with(folder)) - } -} - -impl> TypeVisitable for Arc { +impl> TypeVisitable for Lrc { fn visit_with>(&self, visitor: &mut V) -> ControlFlow { (**self).visit_with(visitor) } @@ -154,19 +141,11 @@ impl> TypeVisitable for Vec { } } -impl> TypeVisitable for &[T] { - fn visit_with>(&self, visitor: &mut V) -> ControlFlow { - self.iter().try_for_each(|t| t.visit_with(visitor)) - } -} - -impl> TypeFoldable for Box<[T]> { - fn try_fold_with>(self, folder: &mut F) -> Result { - self.try_map_id(|t| t.try_fold_with(folder)) - } -} +// `TypeFoldable` isn't impl'd for `&[T]`. It doesn't make sense in the general +// case, because we can't return a new slice. But note that there are a couple +// of trivial impls of `TypeFoldable` for specific slice types elsewhere. -impl> TypeVisitable for Box<[T]> { +impl> TypeVisitable for &[T] { fn visit_with>(&self, visitor: &mut V) -> ControlFlow { self.iter().try_for_each(|t| t.visit_with(visitor)) } diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index c5a5991cc81c4..ec774e62debbf 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -93,7 +93,7 @@ pub use alloc_crate::alloc::*; /// /// ```rust /// use std::alloc::{System, GlobalAlloc, Layout}; -/// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; +/// use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; /// /// struct Counter; /// @@ -103,14 +103,14 @@ pub use alloc_crate::alloc::*; /// unsafe fn alloc(&self, layout: Layout) -> *mut u8 { /// let ret = System.alloc(layout); /// if !ret.is_null() { -/// ALLOCATED.fetch_add(layout.size(), SeqCst); +/// ALLOCATED.fetch_add(layout.size(), Relaxed); /// } /// ret /// } /// /// unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { /// System.dealloc(ptr, layout); -/// ALLOCATED.fetch_sub(layout.size(), SeqCst); +/// ALLOCATED.fetch_sub(layout.size(), Relaxed); /// } /// } /// @@ -118,7 +118,7 @@ pub use alloc_crate::alloc::*; /// static A: Counter = Counter; /// /// fn main() { -/// println!("allocated bytes before main: {}", ALLOCATED.load(SeqCst)); +/// println!("allocated bytes before main: {}", ALLOCATED.load(Relaxed)); /// } /// ``` /// diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 42a68496fc491..30e553f285b98 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -2284,6 +2284,11 @@ pub fn remove_dir>(path: P) -> io::Result<()> { /// /// See [`fs::remove_file`] and [`fs::remove_dir`]. /// +/// `remove_dir_all` will fail if `remove_dir` or `remove_file` fail on any constituent paths, including the root path. +/// As a result, the directory you are deleting must exist, meaning that this function is not idempotent. +/// +/// Consider ignoring the error if validating the removal is not required for your use case. +/// /// [`fs::remove_file`]: remove_file /// [`fs::remove_dir`]: remove_dir /// diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index f6597c729387e..f5b34f8395205 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2332,7 +2332,7 @@ impl<'test> TestCx<'test> { // No extra flags needed. } - Some(_) => self.fatal("unknown 'assembly-output' header"), + Some(header) => self.fatal(&format!("unknown 'assembly-output' header: {header}")), None => self.fatal("missing 'assembly-output' header"), } diff --git a/tests/ui/abi/foreign/foreign-fn-with-byval.rs b/tests/ui/abi/foreign/foreign-fn-with-byval.rs index f366b6ee1bdd5..e20ee0da45d9d 100644 --- a/tests/ui/abi/foreign/foreign-fn-with-byval.rs +++ b/tests/ui/abi/foreign/foreign-fn-with-byval.rs @@ -1,5 +1,5 @@ // run-pass -#![allow(improper_ctypes)] +#![allow(improper_ctypes, improper_ctypes_definitions)] // ignore-wasm32-bare no libc to test ffi with diff --git a/tests/ui/abi/issue-94223.rs b/tests/ui/abi/issue-94223.rs new file mode 100644 index 0000000000000..79d6b94031bc2 --- /dev/null +++ b/tests/ui/abi/issue-94223.rs @@ -0,0 +1,8 @@ +// check-pass +#![allow(improper_ctypes_definitions)] +#![crate_type = "lib"] + +// Check that computing the fn abi for `bad`, with a external ABI fn ptr that is not FFI-safe, does +// not ICE. + +pub fn bad(f: extern "C" fn([u8])) {} diff --git a/tests/ui/coherence/coherence-overlap-negative-impls.rs b/tests/ui/coherence/coherence-overlap-negative-impls.rs new file mode 100644 index 0000000000000..cd1df53a52889 --- /dev/null +++ b/tests/ui/coherence/coherence-overlap-negative-impls.rs @@ -0,0 +1,41 @@ +// check-pass +// known-bug: #74629 + +// Should fail. The `0` and `1` impls overlap, violating coherence. Eg, with +// `T = Test, F = ()`, all bounds are true, making both impls applicable. +// `Test: Fold`, `Test: Fold<()>` are true because of `2`. +// `Is: NotNil` is true because of `auto trait` and lack of negative impl. + +#![feature(negative_impls)] +#![feature(auto_traits)] + +struct Nil; +struct Cons(H); +struct Test; + +trait Fold {} + +impl Fold for Cons // 0 +where + T: Fold, +{} + +impl Fold for Cons // 1 +where + T: Fold, + private::Is: private::NotNil, +{} + +impl Fold for Test {} // 2 + +mod private { + use crate::Nil; + + pub struct Is(T); + pub auto trait NotNil {} + + #[allow(suspicious_auto_trait_impls)] + impl !NotNil for Is {} +} + +fn main() {} diff --git a/tests/ui/hashmap/hashmap-memory.rs b/tests/ui/hashmap/hashmap-memory.rs index 87f8b6ad5730b..bd364b349e263 100644 --- a/tests/ui/hashmap/hashmap-memory.rs +++ b/tests/ui/hashmap/hashmap-memory.rs @@ -1,5 +1,6 @@ // run-pass +#![allow(improper_ctypes_definitions)] #![allow(non_camel_case_types)] #![allow(dead_code)] #![allow(unused_mut)] diff --git a/tests/ui/lint/lint-ctypes-94223.rs b/tests/ui/lint/lint-ctypes-94223.rs new file mode 100644 index 0000000000000..70dd2a71f2675 --- /dev/null +++ b/tests/ui/lint/lint-ctypes-94223.rs @@ -0,0 +1,42 @@ +#![crate_type = "lib"] +#![deny(improper_ctypes_definitions)] + +pub fn bad(f: extern "C" fn([u8])) {} +//~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe + +pub fn bad_twice(f: Result) {} +//~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe +//~^^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe + +struct BadStruct(extern "C" fn([u8])); +//~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe + +enum BadEnum { + A(extern "C" fn([u8])), + //~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe +} + +enum BadUnion { + A(extern "C" fn([u8])), + //~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe +} + +type Foo = extern "C" fn([u8]); +//~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe + +pub struct FfiUnsafe; + +#[allow(improper_ctypes_definitions)] +extern "C" fn f(_: FfiUnsafe) { + unimplemented!() +} + +pub static BAD: extern "C" fn(FfiUnsafe) = f; +//~^ ERROR `extern` fn uses type `FfiUnsafe`, which is not FFI-safe + +pub static BAD_TWICE: Result = Ok(f); +//~^ ERROR `extern` fn uses type `FfiUnsafe`, which is not FFI-safe +//~^^ ERROR `extern` fn uses type `FfiUnsafe`, which is not FFI-safe + +pub const BAD_CONST: extern "C" fn(FfiUnsafe) = f; +//~^ ERROR `extern` fn uses type `FfiUnsafe`, which is not FFI-safe diff --git a/tests/ui/lint/lint-ctypes-94223.stderr b/tests/ui/lint/lint-ctypes-94223.stderr new file mode 100644 index 0000000000000..49e64ed5140c2 --- /dev/null +++ b/tests/ui/lint/lint-ctypes-94223.stderr @@ -0,0 +1,126 @@ +error: `extern` fn uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:4:15 + | +LL | pub fn bad(f: extern "C" fn([u8])) {} + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer instead + = note: slices have no C equivalent +note: the lint level is defined here + --> $DIR/lint-ctypes-94223.rs:2:9 + | +LL | #![deny(improper_ctypes_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:7:28 + | +LL | pub fn bad_twice(f: Result) {} + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer instead + = note: slices have no C equivalent + +error: `extern` fn uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:7:49 + | +LL | pub fn bad_twice(f: Result) {} + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer instead + = note: slices have no C equivalent + +error: `extern` fn uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:11:18 + | +LL | struct BadStruct(extern "C" fn([u8])); + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer instead + = note: slices have no C equivalent + +error: `extern` fn uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:15:7 + | +LL | A(extern "C" fn([u8])), + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer instead + = note: slices have no C equivalent + +error: `extern` fn uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:20:7 + | +LL | A(extern "C" fn([u8])), + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer instead + = note: slices have no C equivalent + +error: `extern` fn uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:24:12 + | +LL | type Foo = extern "C" fn([u8]); + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer instead + = note: slices have no C equivalent + +error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:34:17 + | +LL | pub static BAD: extern "C" fn(FfiUnsafe) = f; + | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the type is defined here + --> $DIR/lint-ctypes-94223.rs:27:1 + | +LL | pub struct FfiUnsafe; + | ^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:37:30 + | +LL | pub static BAD_TWICE: Result = Ok(f); + | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the type is defined here + --> $DIR/lint-ctypes-94223.rs:27:1 + | +LL | pub struct FfiUnsafe; + | ^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:37:56 + | +LL | pub static BAD_TWICE: Result = Ok(f); + | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the type is defined here + --> $DIR/lint-ctypes-94223.rs:27:1 + | +LL | pub struct FfiUnsafe; + | ^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:41:22 + | +LL | pub const BAD_CONST: extern "C" fn(FfiUnsafe) = f; + | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the type is defined here + --> $DIR/lint-ctypes-94223.rs:27:1 + | +LL | pub struct FfiUnsafe; + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 11 previous errors + diff --git a/tests/ui/macros/missing-bang-in-decl.stderr b/tests/ui/macros/missing-bang-in-decl.stderr index dfabafb0a7ab8..aa78c9a690643 100644 --- a/tests/ui/macros/missing-bang-in-decl.stderr +++ b/tests/ui/macros/missing-bang-in-decl.stderr @@ -2,13 +2,23 @@ error: expected `!` after `macro_rules` --> $DIR/missing-bang-in-decl.rs:5:1 | LL | macro_rules foo { - | ^^^^^^^^^^^ help: add a `!`: `macro_rules!` + | ^^^^^^^^^^^ + | +help: add a `!` + | +LL | macro_rules! foo { + | + error: expected `!` after `macro_rules` --> $DIR/missing-bang-in-decl.rs:10:1 | LL | macro_rules bar! { - | ^^^^^^^^^^^ help: add a `!`: `macro_rules!` + | ^^^^^^^^^^^ + | +help: add a `!` + | +LL | macro_rules! bar! { + | + error: macro names aren't followed by a `!` --> $DIR/missing-bang-in-decl.rs:10:16 diff --git a/tests/ui/parser/item-kw-case-mismatch.fixed b/tests/ui/parser/item-kw-case-mismatch.fixed index 1794268f260e1..4b99537fbf7f4 100644 --- a/tests/ui/parser/item-kw-case-mismatch.fixed +++ b/tests/ui/parser/item-kw-case-mismatch.fixed @@ -4,31 +4,31 @@ fn main() {} -use std::ptr::read; //~ ERROR keyword `use` is written in a wrong case -use std::ptr::write; //~ ERROR keyword `use` is written in a wrong case +use std::ptr::read; //~ ERROR keyword `use` is written in the wrong case +use std::ptr::write; //~ ERROR keyword `use` is written in the wrong case async fn _a() {} -//~^ ERROR keyword `fn` is written in a wrong case +//~^ ERROR keyword `fn` is written in the wrong case fn _b() {} -//~^ ERROR keyword `fn` is written in a wrong case +//~^ ERROR keyword `fn` is written in the wrong case async fn _c() {} -//~^ ERROR keyword `async` is written in a wrong case -//~| ERROR keyword `fn` is written in a wrong case +//~^ ERROR keyword `async` is written in the wrong case +//~| ERROR keyword `fn` is written in the wrong case async fn _d() {} -//~^ ERROR keyword `async` is written in a wrong case +//~^ ERROR keyword `async` is written in the wrong case const unsafe fn _e() {} -//~^ ERROR keyword `const` is written in a wrong case -//~| ERROR keyword `unsafe` is written in a wrong case -//~| ERROR keyword `fn` is written in a wrong case +//~^ ERROR keyword `const` is written in the wrong case +//~| ERROR keyword `unsafe` is written in the wrong case +//~| ERROR keyword `fn` is written in the wrong case unsafe extern fn _f() {} -//~^ ERROR keyword `unsafe` is written in a wrong case -//~| ERROR keyword `extern` is written in a wrong case +//~^ ERROR keyword `unsafe` is written in the wrong case +//~| ERROR keyword `extern` is written in the wrong case extern "C" fn _g() {} -//~^ ERROR keyword `extern` is written in a wrong case -//~| ERROR keyword `fn` is written in a wrong case +//~^ ERROR keyword `extern` is written in the wrong case +//~| ERROR keyword `fn` is written in the wrong case diff --git a/tests/ui/parser/item-kw-case-mismatch.rs b/tests/ui/parser/item-kw-case-mismatch.rs index ac8390efdb9a3..b11ec93754fc5 100644 --- a/tests/ui/parser/item-kw-case-mismatch.rs +++ b/tests/ui/parser/item-kw-case-mismatch.rs @@ -4,31 +4,31 @@ fn main() {} -Use std::ptr::read; //~ ERROR keyword `use` is written in a wrong case -USE std::ptr::write; //~ ERROR keyword `use` is written in a wrong case +Use std::ptr::read; //~ ERROR keyword `use` is written in the wrong case +USE std::ptr::write; //~ ERROR keyword `use` is written in the wrong case async Fn _a() {} -//~^ ERROR keyword `fn` is written in a wrong case +//~^ ERROR keyword `fn` is written in the wrong case Fn _b() {} -//~^ ERROR keyword `fn` is written in a wrong case +//~^ ERROR keyword `fn` is written in the wrong case aSYNC fN _c() {} -//~^ ERROR keyword `async` is written in a wrong case -//~| ERROR keyword `fn` is written in a wrong case +//~^ ERROR keyword `async` is written in the wrong case +//~| ERROR keyword `fn` is written in the wrong case Async fn _d() {} -//~^ ERROR keyword `async` is written in a wrong case +//~^ ERROR keyword `async` is written in the wrong case CONST UNSAFE FN _e() {} -//~^ ERROR keyword `const` is written in a wrong case -//~| ERROR keyword `unsafe` is written in a wrong case -//~| ERROR keyword `fn` is written in a wrong case +//~^ ERROR keyword `const` is written in the wrong case +//~| ERROR keyword `unsafe` is written in the wrong case +//~| ERROR keyword `fn` is written in the wrong case unSAFE EXTern fn _f() {} -//~^ ERROR keyword `unsafe` is written in a wrong case -//~| ERROR keyword `extern` is written in a wrong case +//~^ ERROR keyword `unsafe` is written in the wrong case +//~| ERROR keyword `extern` is written in the wrong case EXTERN "C" FN _g() {} -//~^ ERROR keyword `extern` is written in a wrong case -//~| ERROR keyword `fn` is written in a wrong case +//~^ ERROR keyword `extern` is written in the wrong case +//~| ERROR keyword `fn` is written in the wrong case diff --git a/tests/ui/parser/item-kw-case-mismatch.stderr b/tests/ui/parser/item-kw-case-mismatch.stderr index e66dae825f9c4..ba59ea8536338 100644 --- a/tests/ui/parser/item-kw-case-mismatch.stderr +++ b/tests/ui/parser/item-kw-case-mismatch.stderr @@ -1,82 +1,82 @@ -error: keyword `use` is written in a wrong case +error: keyword `use` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:7:1 | LL | Use std::ptr::read; | ^^^ help: write it in the correct case (notice the capitalization): `use` -error: keyword `use` is written in a wrong case +error: keyword `use` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:8:1 | LL | USE std::ptr::write; | ^^^ help: write it in the correct case: `use` -error: keyword `fn` is written in a wrong case +error: keyword `fn` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:10:7 | LL | async Fn _a() {} | ^^ help: write it in the correct case (notice the capitalization): `fn` -error: keyword `fn` is written in a wrong case +error: keyword `fn` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:13:1 | LL | Fn _b() {} | ^^ help: write it in the correct case (notice the capitalization): `fn` -error: keyword `async` is written in a wrong case +error: keyword `async` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:16:1 | LL | aSYNC fN _c() {} | ^^^^^ help: write it in the correct case: `async` -error: keyword `fn` is written in a wrong case +error: keyword `fn` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:16:7 | LL | aSYNC fN _c() {} | ^^ help: write it in the correct case: `fn` -error: keyword `async` is written in a wrong case +error: keyword `async` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:20:1 | LL | Async fn _d() {} | ^^^^^ help: write it in the correct case: `async` -error: keyword `const` is written in a wrong case +error: keyword `const` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:23:1 | LL | CONST UNSAFE FN _e() {} | ^^^^^ help: write it in the correct case: `const` -error: keyword `unsafe` is written in a wrong case +error: keyword `unsafe` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:23:7 | LL | CONST UNSAFE FN _e() {} | ^^^^^^ help: write it in the correct case: `unsafe` -error: keyword `fn` is written in a wrong case +error: keyword `fn` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:23:14 | LL | CONST UNSAFE FN _e() {} | ^^ help: write it in the correct case: `fn` -error: keyword `unsafe` is written in a wrong case +error: keyword `unsafe` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:28:1 | LL | unSAFE EXTern fn _f() {} | ^^^^^^ help: write it in the correct case: `unsafe` -error: keyword `extern` is written in a wrong case +error: keyword `extern` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:28:8 | LL | unSAFE EXTern fn _f() {} | ^^^^^^ help: write it in the correct case: `extern` -error: keyword `extern` is written in a wrong case +error: keyword `extern` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:32:1 | LL | EXTERN "C" FN _g() {} | ^^^^^^ help: write it in the correct case: `extern` -error: keyword `fn` is written in a wrong case +error: keyword `fn` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:32:12 | LL | EXTERN "C" FN _g() {} diff --git a/tests/ui/parser/recover-unticked-labels.stderr b/tests/ui/parser/recover-unticked-labels.stderr index c115dffb10e9c..fbd108ca613c7 100644 --- a/tests/ui/parser/recover-unticked-labels.stderr +++ b/tests/ui/parser/recover-unticked-labels.stderr @@ -2,13 +2,17 @@ error: expected a label, found an identifier --> $DIR/recover-unticked-labels.rs:5:26 | LL | 'label: loop { break label 0 }; - | ^^^^^ help: labels start with a tick: `'label` + | -^^^^ + | | + | help: labels start with a tick error: expected a label, found an identifier --> $DIR/recover-unticked-labels.rs:6:29 | LL | 'label: loop { continue label }; - | ^^^^^ help: labels start with a tick: `'label` + | -^^^^ + | | + | help: labels start with a tick error[E0425]: cannot find value `label` in this scope --> $DIR/recover-unticked-labels.rs:4:26 diff --git a/tests/ui/parser/use-colon-as-mod-sep.stderr b/tests/ui/parser/use-colon-as-mod-sep.stderr index e825dfed11192..bfc5374ef9d2d 100644 --- a/tests/ui/parser/use-colon-as-mod-sep.stderr +++ b/tests/ui/parser/use-colon-as-mod-sep.stderr @@ -11,18 +11,24 @@ error: expected `::`, found `:` | LL | use std:fs::File; | ^ help: use double colon + | + = note: import paths are delimited using `::` error: expected `::`, found `:` --> $DIR/use-colon-as-mod-sep.rs:7:8 | LL | use std:collections:HashMap; | ^ help: use double colon + | + = note: import paths are delimited using `::` error: expected `::`, found `:` --> $DIR/use-colon-as-mod-sep.rs:7:20 | LL | use std:collections:HashMap; | ^ help: use double colon + | + = note: import paths are delimited using `::` error: aborting due to 4 previous errors diff --git a/tests/ui/specialization/issue-40582.rs b/tests/ui/specialization/issue-40582.rs new file mode 100644 index 0000000000000..9805933553dd7 --- /dev/null +++ b/tests/ui/specialization/issue-40582.rs @@ -0,0 +1,35 @@ +// check-pass +// known-bug: #40582 + +// Should fail. Should not be possible to implement `make_static`. + +#![feature(specialization)] +#![allow(incomplete_features)] + +trait FromRef<'a, T: ?Sized> { + fn from_ref(r: &'a T) -> Self; +} + +impl<'a, T: ?Sized> FromRef<'a, T> for &'a T { + fn from_ref(r: &'a T) -> Self { + r + } +} + +impl<'a, T: ?Sized, R> FromRef<'a, T> for R { + default fn from_ref(_: &'a T) -> Self { + unimplemented!() + } +} + +fn make_static(data: &T) -> &'static T { + fn helper(data: &T) -> R { + R::from_ref(data) + } + helper(data) +} + +fn main() { + let s = "specialization".to_owned(); + println!("{:?}", make_static(s.as_str())); +} diff --git a/tests/ui/specialization/specialization-default-items-drop-coherence.rs b/tests/ui/specialization/specialization-default-items-drop-coherence.rs new file mode 100644 index 0000000000000..16ad942d5ab95 --- /dev/null +++ b/tests/ui/specialization/specialization-default-items-drop-coherence.rs @@ -0,0 +1,30 @@ +// check-pass +// known-bug: #105782 + +// Should fail. Default items completely drop candidates instead of ambiguity, +// which is unsound during coherence, since coherence requires completeness. + +#![feature(specialization)] +#![allow(incomplete_features)] + +trait Default { + type Id; +} + +impl Default for T { + default type Id = T; +} + +trait Overlap { + type Assoc; +} + +impl Overlap for u32 { + type Assoc = usize; +} + +impl Overlap for ::Id { + type Assoc = Box; +} + +fn main() {} diff --git a/tests/ui/thread-local/thread-local-static-ref-use-after-free.rs b/tests/ui/thread-local/thread-local-static-ref-use-after-free.rs new file mode 100644 index 0000000000000..c282e2185bca2 --- /dev/null +++ b/tests/ui/thread-local/thread-local-static-ref-use-after-free.rs @@ -0,0 +1,46 @@ +// check-pass +// known-bug: #49682 +// edition:2021 + +// Should fail. Keeping references to thread local statics can result in a +// use-after-free. + +#![feature(thread_local)] + +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::thread; + +#[allow(dead_code)] +#[thread_local] +static FOO: AtomicUsize = AtomicUsize::new(0); + +#[allow(dead_code)] +async fn bar() {} + +#[allow(dead_code)] +async fn foo() { + let r = &FOO; + bar().await; + r.load(Ordering::SeqCst); +} + +fn main() { + // &FOO = 0x7fd1e9cbf6d0 + _ = thread::spawn(|| { + let g = foo(); + println!("&FOO = {:p}", &FOO); + g + }) + .join() + .unwrap(); + + // &FOO = 0x7fd1e9cc0f50 + println!("&FOO = {:p}", &FOO); + + // &FOO = 0x7fd1e9cbf6d0 + thread::spawn(move || { + println!("&FOO = {:p}", &FOO); + }) + .join() + .unwrap(); +} diff --git a/tests/ui/traits/new-solver/exponential-trait-goals.rs b/tests/ui/traits/new-solver/exponential-trait-goals.rs new file mode 100644 index 0000000000000..b37f09ee185e9 --- /dev/null +++ b/tests/ui/traits/new-solver/exponential-trait-goals.rs @@ -0,0 +1,20 @@ +// compile-flags: -Ztrait-solver=next + +trait Trait {} + +struct W(T); + +impl Trait for W<(W, W)> +where + W: Trait, + W: Trait, +{ +} + +fn impls() {} + +fn main() { + impls::>(); + //~^ ERROR type annotations needed + //~| ERROR overflow evaluating the requirement `W<_>: Trait` +} diff --git a/tests/ui/traits/new-solver/exponential-trait-goals.stderr b/tests/ui/traits/new-solver/exponential-trait-goals.stderr new file mode 100644 index 0000000000000..28a99cbbca6a1 --- /dev/null +++ b/tests/ui/traits/new-solver/exponential-trait-goals.stderr @@ -0,0 +1,23 @@ +error[E0282]: type annotations needed + --> $DIR/exponential-trait-goals.rs:17:5 + | +LL | impls::>(); + | ^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `impls` + +error[E0275]: overflow evaluating the requirement `W<_>: Trait` + --> $DIR/exponential-trait-goals.rs:17:5 + | +LL | impls::>(); + | ^^^^^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`exponential_trait_goals`) +note: required by a bound in `impls` + --> $DIR/exponential-trait-goals.rs:14:13 + | +LL | fn impls() {} + | ^^^^^ required by this bound in `impls` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0275, E0282. +For more information about an error, try `rustc --explain E0275`.