From c7d8c65c1a3ca9f1d5cb13c5ea4db4243ea975bf Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Fri, 3 Nov 2023 21:45:19 -0500 Subject: [PATCH 01/13] docs: clarify explicitly freeing heap allocated memory --- library/alloc/src/boxed.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 25c63b425ce59..fdf5e134f4d4c 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1038,10 +1038,18 @@ impl Box { /// use std::ptr; /// /// let x = Box::new(String::from("Hello")); - /// let p = Box::into_raw(x); + /// let ptr = Box::into_raw(x); + /// unsafe { + /// ptr::drop_in_place(ptr); + /// dealloc(ptr as *mut u8, Layout::new::()); + /// } + /// ``` + /// Note: This is equivalent to the following: + /// ``` + /// let x = Box::new(String::from("Hello")); + /// let ptr = Box::into_raw(x); /// unsafe { - /// ptr::drop_in_place(p); - /// dealloc(p as *mut u8, Layout::new::()); + /// drop(Box::from_raw(ptr)); /// } /// ``` /// From efaf4258ba65348f607a3604aaf9a9ffd01e95a9 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Thu, 30 Nov 2023 20:22:20 -0800 Subject: [PATCH 02/13] Add Variant and a few more APIs to stable_mir --- .../rustc_smir/src/rustc_internal/internal.rs | 22 ++++-- compiler/rustc_smir/src/rustc_smir/context.rs | 12 +++- .../rustc_smir/src/rustc_smir/convert/mir.rs | 2 +- .../rustc_smir/src/rustc_smir/convert/mod.rs | 22 +++--- compiler/stable_mir/src/compiler_interface.rs | 8 ++- compiler/stable_mir/src/mir/body.rs | 55 ++++++++------ compiler/stable_mir/src/ty.rs | 71 +++++++++++++++++++ 7 files changed, 153 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index 202ca1b156aa7..d56f299e194b0 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -9,11 +9,7 @@ use rustc_middle::ty::{self as rustc_ty, Ty as InternalTy}; use rustc_span::Symbol; use stable_mir::mir::alloc::AllocId; use stable_mir::mir::mono::{Instance, MonoItem, StaticDef}; -use stable_mir::ty::{ - AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, Const, - ExistentialTraitRef, FloatTy, GenericArgKind, GenericArgs, IntTy, Region, RigidTy, Span, - TraitRef, Ty, UintTy, -}; +use stable_mir::ty::{AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, Const, ExistentialTraitRef, FloatTy, GenericArgKind, GenericArgs, IndexedVal, IntTy, Region, RigidTy, Span, TraitRef, Ty, UintTy, VariantDef, VariantIdx}; use stable_mir::{CrateItem, DefId}; use super::RustcInternal; @@ -141,6 +137,22 @@ impl<'tcx> RustcInternal<'tcx> for FloatTy { } } +impl<'tcx> RustcInternal<'tcx> for VariantIdx { + type T = rustc_target::abi::VariantIdx; + + fn internal(&self, _tables: &mut Tables<'tcx>) -> Self::T { + rustc_target::abi::VariantIdx::from(self.to_index()) + } +} + +impl<'tcx> RustcInternal<'tcx> for VariantDef { + type T = &'tcx rustc_ty::VariantDef; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + self.adt_def.internal(tables).variant(self.idx.internal(tables)) + } +} + fn ty_const<'tcx>(constant: &Const, tables: &mut Tables<'tcx>) -> rustc_ty::Const<'tcx> { match constant.internal(tables) { rustc_middle::mir::Const::Ty(c) => c, diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 0bd640ee1e3b0..fbdc1101f3684 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -13,7 +13,7 @@ use stable_mir::mir::mono::{InstanceDef, StaticDef}; use stable_mir::mir::Body; use stable_mir::ty::{ AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FnDef, GenericArgs, LineInfo, - PolyFnSig, RigidTy, Span, TyKind, + PolyFnSig, RigidTy, Span, TyKind, VariantDef, }; use stable_mir::{self, Crate, CrateItem, DefId, Error, Filename, ItemKind, Symbol}; use std::cell::RefCell; @@ -209,6 +209,16 @@ impl<'tcx> Context for TablesWrapper<'tcx> { sig.stable(&mut *tables) } + fn adt_variants_len(&self, def: AdtDef) -> usize { + let mut tables = self.0.borrow_mut(); + def.internal(&mut *tables).variants().len() + } + + fn variant_name(&self, def: VariantDef) -> Symbol { + let mut tables = self.0.borrow_mut(); + def.internal(&mut *tables).name.to_string() + } + fn eval_target_usize(&self, cnst: &Const) -> Result { let mut tables = self.0.borrow_mut(); let mir_const = cnst.internal(&mut *tables); diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index 165b8c50e70f9..0cea3fcc7f7ac 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -517,7 +517,7 @@ impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> { mir::AggregateKind::Adt(def_id, var_idx, generic_arg, user_ty_index, field_idx) => { stable_mir::mir::AggregateKind::Adt( tables.adt_def(*def_id), - var_idx.index(), + var_idx.stable(tables), generic_arg.stable(tables), user_ty_index.map(|idx| idx.index()), field_idx.map(|idx| idx.index()), diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs index edb32df305c8e..6dca8cfdbb07c 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs @@ -1,7 +1,7 @@ //! Conversion of internal Rust compiler items to stable ones. use rustc_target::abi::FieldIdx; -use stable_mir::mir::VariantIdx; +use stable_mir::ty::{IndexedVal, VariantIdx}; use crate::rustc_smir::{Stable, Tables}; @@ -25,17 +25,10 @@ impl<'tcx> Stable<'tcx> for FieldIdx { } } -impl<'tcx> Stable<'tcx> for (rustc_target::abi::VariantIdx, FieldIdx) { - type T = (usize, usize); - fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { - (self.0.as_usize(), self.1.as_usize()) - } -} - impl<'tcx> Stable<'tcx> for rustc_target::abi::VariantIdx { type T = VariantIdx; fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { - self.as_usize() + VariantIdx::to_val(self.as_usize()) } } @@ -74,3 +67,14 @@ impl<'tcx> Stable<'tcx> for rustc_span::Span { tables.create_span(*self) } } + +impl<'tcx, T, U> Stable<'tcx> for (T, U) +where + T: Stable<'tcx>, + U: Stable<'tcx>, +{ + type T = (T::T, U::T); + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + (self.0.stable(tables), self.1.stable(tables)) + } +} diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs index daf4465963edd..33e301f697359 100644 --- a/compiler/stable_mir/src/compiler_interface.rs +++ b/compiler/stable_mir/src/compiler_interface.rs @@ -11,7 +11,7 @@ use crate::mir::Body; use crate::ty::{ AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FnDef, GenericArgs, GenericPredicates, Generics, ImplDef, ImplTrait, LineInfo, PolyFnSig, RigidTy, Span, TraitDecl, - TraitDef, Ty, TyKind, + TraitDef, Ty, TyKind, VariantDef, }; use crate::{ mir, Crate, CrateItem, CrateItems, DefId, Error, Filename, ImplTraitDecls, ItemKind, Symbol, @@ -71,6 +71,12 @@ pub trait Context { /// Retrieve the function signature for the given generic arguments. fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig; + /// The number of variants in this ADT. + fn adt_variants_len(&self, def: AdtDef) -> usize; + + /// The name of a variant. + fn variant_name(&self, def: VariantDef) -> Symbol; + /// Evaluate constant as a target usize. fn eval_target_usize(&self, cnst: &Const) -> Result; diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index 3a4f42835622b..8ad9306fe747a 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -1,6 +1,7 @@ use crate::mir::pretty::{function_body, pretty_statement, pretty_terminator}; use crate::ty::{ AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability, Region, RigidTy, Ty, TyKind, + VariantIdx, }; use crate::{Error, Opaque, Span, Symbol}; use std::io; @@ -9,17 +10,17 @@ use std::io; pub struct Body { pub blocks: Vec, - // Declarations of locals within the function. - // - // The first local is the return value pointer, followed by `arg_count` - // locals for the function arguments, followed by any user-declared - // variables and temporaries. + /// Declarations of locals within the function. + /// + /// The first local is the return value pointer, followed by `arg_count` + /// locals for the function arguments, followed by any user-declared + /// variables and temporaries. pub(super) locals: LocalDecls, - // The number of arguments this function takes. + /// The number of arguments this function takes. pub(super) arg_count: usize, - // Debug information pertaining to user variables, including captures. + /// Debug information pertaining to user variables, including captures. pub(super) var_debug_info: Vec, } @@ -69,6 +70,11 @@ impl Body { &self.locals } + /// Get the local declaration for this local. + pub fn local_decl(&self, local: Local) -> Option<&LocalDecl> { + self.locals.get(local) + } + pub fn dump(&self, w: &mut W) -> io::Result<()> { writeln!(w, "{}", function_body(self))?; self.blocks @@ -492,12 +498,32 @@ pub struct Place { pub projection: Vec, } +impl From for Place { + fn from(local: Local) -> Self { + Place { local, projection: vec![] } + } +} + +/// Debug information pertaining to a user variable. #[derive(Clone, Debug, Eq, PartialEq)] pub struct VarDebugInfo { + /// The variable name. pub name: Symbol, + + /// Source info of the user variable, including the scope + /// within which the variable is visible (to debuginfo) pub source_info: SourceInfo, + + /// The user variable's data is split across several fragments, + /// each described by a `VarDebugInfoFragment`. pub composite: Option, + + /// Where the data for this user variable is to be found. pub value: VarDebugInfoContents, + + /// When present, indicates what argument number this variable is in the function that it + /// originated from (starting from 1). Note, if MIR inlining is enabled, then this is the + /// argument number in the original function before it was inlined. pub argument_index: Option, } @@ -634,21 +660,6 @@ pub const RETURN_LOCAL: Local = 0; /// `g`'s `FieldIdx` is `2`. type FieldIdx = usize; -/// The source-order index of a variant in a type. -/// -/// For example, in the following types, -/// ```ignore(illustrative) -/// enum Demo1 { -/// Variant0 { a: bool, b: i32 }, -/// Variant1 { c: u8, d: u64 }, -/// } -/// struct Demo2 { e: u8, f: u16, g: u8 } -/// ``` -/// `a` is in the variant with the `VariantIdx` of `0`, -/// `c` is in the variant with the `VariantIdx` of `1`, and -/// `g` is in the variant with the `VariantIdx` of `0`. -pub type VariantIdx = usize; - type UserTypeAnnotationIndex = usize; #[derive(Clone, Debug, Eq, PartialEq)] diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index 6c4fb4a775352..ebf43821fb51f 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -30,6 +30,16 @@ impl Ty { pub fn try_new_array(elem_ty: Ty, size: u64) -> Result { Ok(Ty::from_rigid_kind(RigidTy::Array(elem_ty, Const::try_from_target_usize(size)?))) } + + /// Create a new pointer type. + pub fn new_ptr(pointee_ty: Ty, mutability: Mutability) -> Ty { + Ty::from_rigid_kind(RigidTy::RawPtr(pointee_ty, mutability)) + } + + /// Create a type representing `usize`. + pub fn usize_ty() -> Ty { + Ty::from_rigid_kind(RigidTy::Uint(UintTy::Usize)) + } } impl Ty { @@ -369,6 +379,49 @@ impl AdtDef { pub fn is_box(&self) -> bool { with(|cx| cx.adt_is_box(*self)) } + + /// The number of variants in this ADT. + pub fn num_variants(&self) -> usize { + with(|cx| cx.adt_variants_len(*self)) + } + + /// Retrieve the variants in this ADT. + pub fn variants(&self) -> Vec { + self.variants_iter().collect() + } + + /// Iterate over the variants in this ADT. + pub fn variants_iter(&self) -> impl Iterator + '_ { + (0..self.num_variants()) + .map(|idx| VariantDef { idx: VariantIdx::to_val(idx), adt_def: *self }) + } + + pub fn variant(&self, idx: VariantIdx) -> Option { + self.variants().get(idx.to_index()).copied() + } +} + +/// Definition of a variant, which can be either a struct / union field or an enum variant. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct VariantDef { + /// The variant index. + /// + /// ## Warning + /// Do not access this field directly! + pub idx: VariantIdx, + /// The data type where this variant comes from. + /// For now, we use this to retrieve information about the variant itself so we don't need to + /// cache more information. + /// + /// ## Warning + /// Do not access this field directly! + pub adt_def: AdtDef, +} + +impl VariantDef { + pub fn name(&self) -> Symbol { + with(|cx| cx.variant_name(*self)) + } } impl Display for AdtKind { @@ -906,3 +959,21 @@ macro_rules! index_impl { index_impl!(ConstId); index_impl!(Ty); index_impl!(Span); + +/// The source-order index of a variant in a type. +/// +/// For example, in the following types, +/// ```ignore(illustrative) +/// enum Demo1 { +/// Variant0 { a: bool, b: i32 }, +/// Variant1 { c: u8, d: u64 }, +/// } +/// struct Demo2 { e: u8, f: u16, g: u8 } +/// ``` +/// `a` is in the variant with the `VariantIdx` of `0`, +/// `c` is in the variant with the `VariantIdx` of `1`, and +/// `g` is in the variant with the `VariantIdx` of `0`. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct VariantIdx(usize); + +index_impl!(VariantIdx); From e19c7cd159c7449600f9feb2e63501e9951c4aac Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Fri, 1 Dec 2023 11:38:42 -0800 Subject: [PATCH 03/13] Finish implementing `RustcInternal` for `TyKind` This will allow us to provide methods to create `Ty` inside the stable MIR, which can be helpful while handling pointers and other stuff. --- .../rustc_smir/src/rustc_internal/internal.rs | 197 ++++++++++++++++-- compiler/stable_mir/src/mir/body.rs | 2 +- 2 files changed, 186 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index d56f299e194b0..632072003539f 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -9,7 +9,13 @@ use rustc_middle::ty::{self as rustc_ty, Ty as InternalTy}; use rustc_span::Symbol; use stable_mir::mir::alloc::AllocId; use stable_mir::mir::mono::{Instance, MonoItem, StaticDef}; -use stable_mir::ty::{AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, Const, ExistentialTraitRef, FloatTy, GenericArgKind, GenericArgs, IndexedVal, IntTy, Region, RigidTy, Span, TraitRef, Ty, UintTy, VariantDef, VariantIdx}; +use stable_mir::mir::{Mutability, Safety}; +use stable_mir::ty::{ + Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, Const, + DynKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig, + GenericArgKind, GenericArgs, IndexedVal, IntTy, Movability, Region, RigidTy, Span, TermKind, + TraitRef, Ty, UintTy, VariantDef, VariantIdx, +}; use stable_mir::{CrateItem, DefId}; use super::RustcInternal; @@ -80,17 +86,38 @@ impl<'tcx> RustcInternal<'tcx> for RigidTy { } RigidTy::Str => rustc_ty::TyKind::Str, RigidTy::Slice(ty) => rustc_ty::TyKind::Slice(ty.internal(tables)), - RigidTy::RawPtr(..) - | RigidTy::Ref(..) - | RigidTy::Foreign(_) - | RigidTy::FnDef(_, _) - | RigidTy::FnPtr(_) - | RigidTy::Closure(..) - | RigidTy::Coroutine(..) - | RigidTy::CoroutineWitness(..) - | RigidTy::Dynamic(..) - | RigidTy::Tuple(..) => { - todo!() + RigidTy::RawPtr(ty, mutability) => rustc_ty::TyKind::RawPtr(rustc_ty::TypeAndMut { + ty: ty.internal(tables), + mutbl: mutability.internal(tables), + }), + RigidTy::Ref(region, ty, mutability) => rustc_ty::TyKind::Ref( + region.internal(tables), + ty.internal(tables), + mutability.internal(tables), + ), + RigidTy::Foreign(def) => rustc_ty::TyKind::Foreign(def.0.internal(tables)), + RigidTy::FnDef(def, args) => { + rustc_ty::TyKind::FnDef(def.0.internal(tables), args.internal(tables)) + } + RigidTy::FnPtr(sig) => rustc_ty::TyKind::FnPtr(sig.internal(tables)), + RigidTy::Closure(def, args) => { + rustc_ty::TyKind::Closure(def.0.internal(tables), args.internal(tables)) + } + RigidTy::Coroutine(def, args, mov) => rustc_ty::TyKind::Coroutine( + def.0.internal(tables), + args.internal(tables), + mov.internal(tables), + ), + RigidTy::CoroutineWitness(def, args) => { + rustc_ty::TyKind::CoroutineWitness(def.0.internal(tables), args.internal(tables)) + } + RigidTy::Dynamic(predicate, region, dyn_kind) => rustc_ty::TyKind::Dynamic( + tables.tcx.mk_poly_existential_predicates(&predicate.internal(tables)), + region.internal(tables), + dyn_kind.internal(tables), + ), + RigidTy::Tuple(tys) => { + rustc_ty::TyKind::Tuple(tables.tcx.mk_type_list(&tys.internal(tables))) } } } @@ -137,6 +164,41 @@ impl<'tcx> RustcInternal<'tcx> for FloatTy { } } +impl<'tcx> RustcInternal<'tcx> for Mutability { + type T = rustc_ty::Mutability; + + fn internal(&self, _tables: &mut Tables<'tcx>) -> Self::T { + match self { + Mutability::Not => rustc_ty::Mutability::Not, + Mutability::Mut => rustc_ty::Mutability::Mut, + } + } +} + +impl<'tcx> RustcInternal<'tcx> for Movability { + type T = rustc_ty::Movability; + + fn internal(&self, _tables: &mut Tables<'tcx>) -> Self::T { + match self { + Movability::Static => rustc_ty::Movability::Static, + Movability::Movable => rustc_ty::Movability::Movable, + } + } +} + +impl<'tcx> RustcInternal<'tcx> for FnSig { + type T = rustc_ty::FnSig<'tcx>; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + rustc_ty::FnSig { + inputs_and_output: tables.tcx.mk_type_list(&self.inputs_and_output.internal(tables)), + c_variadic: self.c_variadic, + unsafety: self.unsafety.internal(tables), + abi: self.abi.internal(tables), + } + } +} + impl<'tcx> RustcInternal<'tcx> for VariantIdx { type T = rustc_target::abi::VariantIdx; @@ -242,6 +304,58 @@ impl<'tcx> RustcInternal<'tcx> for BoundVariableKind { } } +impl<'tcx> RustcInternal<'tcx> for DynKind { + type T = rustc_ty::DynKind; + + fn internal(&self, _tables: &mut Tables<'tcx>) -> Self::T { + match self { + DynKind::Dyn => rustc_ty::DynKind::Dyn, + DynKind::DynStar => rustc_ty::DynKind::DynStar, + } + } +} + +impl<'tcx> RustcInternal<'tcx> for ExistentialPredicate { + type T = rustc_ty::ExistentialPredicate<'tcx>; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + match self { + ExistentialPredicate::Trait(trait_ref) => { + rustc_ty::ExistentialPredicate::Trait(trait_ref.internal(tables)) + } + ExistentialPredicate::Projection(proj) => { + rustc_ty::ExistentialPredicate::Projection(proj.internal(tables)) + } + ExistentialPredicate::AutoTrait(trait_def) => { + rustc_ty::ExistentialPredicate::AutoTrait(trait_def.0.internal(tables)) + } + } + } +} + +impl<'tcx> RustcInternal<'tcx> for ExistentialProjection { + type T = rustc_ty::ExistentialProjection<'tcx>; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + rustc_ty::ExistentialProjection { + def_id: self.def_id.0.internal(tables), + args: self.generic_args.internal(tables), + term: self.term.internal(tables), + } + } +} + +impl<'tcx> RustcInternal<'tcx> for TermKind { + type T = rustc_ty::Term<'tcx>; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + match self { + TermKind::Type(ty) => ty.internal(tables).into(), + TermKind::Const(const_) => ty_const(const_, tables).into(), + } + } +} + impl<'tcx> RustcInternal<'tcx> for ExistentialTraitRef { type T = rustc_ty::ExistentialTraitRef<'tcx>; @@ -291,6 +405,53 @@ impl<'tcx> RustcInternal<'tcx> for AdtDef { } } +impl<'tcx> RustcInternal<'tcx> for Abi { + type T = rustc_target::spec::abi::Abi; + + fn internal(&self, _tables: &mut Tables<'tcx>) -> Self::T { + match *self { + Abi::Rust => rustc_target::spec::abi::Abi::Rust, + Abi::C { unwind } => rustc_target::spec::abi::Abi::C { unwind }, + Abi::Cdecl { unwind } => rustc_target::spec::abi::Abi::Cdecl { unwind }, + Abi::Stdcall { unwind } => rustc_target::spec::abi::Abi::Stdcall { unwind }, + Abi::Fastcall { unwind } => rustc_target::spec::abi::Abi::Fastcall { unwind }, + Abi::Vectorcall { unwind } => rustc_target::spec::abi::Abi::Vectorcall { unwind }, + Abi::Thiscall { unwind } => rustc_target::spec::abi::Abi::Thiscall { unwind }, + Abi::Aapcs { unwind } => rustc_target::spec::abi::Abi::Aapcs { unwind }, + Abi::Win64 { unwind } => rustc_target::spec::abi::Abi::Win64 { unwind }, + Abi::SysV64 { unwind } => rustc_target::spec::abi::Abi::SysV64 { unwind }, + Abi::PtxKernel => rustc_target::spec::abi::Abi::PtxKernel, + Abi::Msp430Interrupt => rustc_target::spec::abi::Abi::Msp430Interrupt, + Abi::X86Interrupt => rustc_target::spec::abi::Abi::X86Interrupt, + Abi::AmdGpuKernel => rustc_target::spec::abi::Abi::AmdGpuKernel, + Abi::EfiApi => rustc_target::spec::abi::Abi::EfiApi, + Abi::AvrInterrupt => rustc_target::spec::abi::Abi::AvrInterrupt, + Abi::AvrNonBlockingInterrupt => rustc_target::spec::abi::Abi::AvrNonBlockingInterrupt, + Abi::CCmseNonSecureCall => rustc_target::spec::abi::Abi::CCmseNonSecureCall, + Abi::Wasm => rustc_target::spec::abi::Abi::Wasm, + Abi::System { unwind } => rustc_target::spec::abi::Abi::System { unwind }, + Abi::RustIntrinsic => rustc_target::spec::abi::Abi::RustIntrinsic, + Abi::RustCall => rustc_target::spec::abi::Abi::RustCall, + Abi::PlatformIntrinsic => rustc_target::spec::abi::Abi::PlatformIntrinsic, + Abi::Unadjusted => rustc_target::spec::abi::Abi::Unadjusted, + Abi::RustCold => rustc_target::spec::abi::Abi::RustCold, + Abi::RiscvInterruptM => rustc_target::spec::abi::Abi::RiscvInterruptM, + Abi::RiscvInterruptS => rustc_target::spec::abi::Abi::RiscvInterruptS, + } + } +} + +impl<'tcx> RustcInternal<'tcx> for Safety { + type T = rustc_hir::Unsafety; + + fn internal(&self, _tables: &mut Tables<'tcx>) -> Self::T { + match self { + Safety::Unsafe => rustc_hir::Unsafety::Unsafe, + Safety::Normal => rustc_hir::Unsafety::Normal, + } + } +} + impl<'tcx> RustcInternal<'tcx> for Span { type T = rustc_span::Span; @@ -309,6 +470,7 @@ where (*self).internal(tables) } } + impl<'tcx, T> RustcInternal<'tcx> for Option where T: RustcInternal<'tcx>, @@ -319,3 +481,14 @@ where self.as_ref().map(|inner| inner.internal(tables)) } } + +impl<'tcx, T> RustcInternal<'tcx> for Vec +where + T: RustcInternal<'tcx>, +{ + type T = Vec; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + self.iter().map(|e| e.internal(tables)).collect() + } +} diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index 8ad9306fe747a..3bd42d703c35d 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -511,7 +511,7 @@ pub struct VarDebugInfo { pub name: Symbol, /// Source info of the user variable, including the scope - /// within which the variable is visible (to debuginfo) + /// within which the variable is visible (to debuginfo). pub source_info: SourceInfo, /// The user variable's data is split across several fragments, From 1720b108f7ce17aac804fe21d2d647714938cb57 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Mon, 4 Dec 2023 20:08:25 -0800 Subject: [PATCH 04/13] Add FieldDef to StableMIR and methods to get type --- compiler/rustc_smir/src/rustc_smir/context.rs | 21 +++++++++- .../rustc_smir/src/rustc_smir/convert/mod.rs | 18 ++++++++ .../rustc_smir/src/rustc_smir/convert/ty.rs | 11 +++++ compiler/stable_mir/src/compiler_interface.rs | 6 ++- compiler/stable_mir/src/mir/body.rs | 2 +- compiler/stable_mir/src/ty.rs | 42 ++++++++++++++++++- 6 files changed, 95 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index fbdc1101f3684..8ddd3d4853965 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -12,8 +12,8 @@ use stable_mir::mir::alloc::GlobalAlloc; use stable_mir::mir::mono::{InstanceDef, StaticDef}; use stable_mir::mir::Body; use stable_mir::ty::{ - AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FnDef, GenericArgs, LineInfo, - PolyFnSig, RigidTy, Span, TyKind, VariantDef, + AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, GenericArgs, + LineInfo, PolyFnSig, RigidTy, Span, TyKind, VariantDef, }; use stable_mir::{self, Crate, CrateItem, DefId, Error, Filename, ItemKind, Symbol}; use std::cell::RefCell; @@ -219,6 +219,11 @@ impl<'tcx> Context for TablesWrapper<'tcx> { def.internal(&mut *tables).name.to_string() } + fn variant_fields(&self, def: VariantDef) -> Vec { + let mut tables = self.0.borrow_mut(); + def.internal(&mut *tables).fields.iter().map(|f| f.stable(&mut *tables)).collect() + } + fn eval_target_usize(&self, cnst: &Const) -> Result { let mut tables = self.0.borrow_mut(); let mir_const = cnst.internal(&mut *tables); @@ -250,6 +255,18 @@ impl<'tcx> Context for TablesWrapper<'tcx> { tables.tcx.type_of(item.internal(&mut *tables)).instantiate_identity().stable(&mut *tables) } + fn def_ty_with_args( + &self, + item: stable_mir::DefId, + args: &GenericArgs, + ) -> Result { + let mut tables = self.0.borrow_mut(); + let args = args.internal(&mut *tables); + let def_ty = tables.tcx.type_of(item.internal(&mut *tables)); + // FIXME(celinval): use try_fold instead to avoid crashing. + Ok(def_ty.instantiate(tables.tcx, args).stable(&mut *tables)) + } + fn const_literal(&self, cnst: &stable_mir::ty::Const) -> String { internal(cnst).to_string() } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs index 6dca8cfdbb07c..c3ee0a60f4d35 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs @@ -60,6 +60,14 @@ impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineKind { } } +impl<'tcx> Stable<'tcx> for rustc_span::Symbol { + type T = stable_mir::Symbol; + + fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T { + self.to_string() + } +} + impl<'tcx> Stable<'tcx> for rustc_span::Span { type T = stable_mir::ty::Span; @@ -68,6 +76,16 @@ impl<'tcx> Stable<'tcx> for rustc_span::Span { } } +impl<'tcx, T> Stable<'tcx> for &[T] +where + T: Stable<'tcx>, +{ + type T = Vec; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + self.iter().map(|e| e.stable(tables)).collect() + } +} + impl<'tcx, T, U> Stable<'tcx> for (T, U) where T: Stable<'tcx>, diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index f837f28e11ed2..4fe847c291c87 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -137,6 +137,17 @@ impl<'tcx> Stable<'tcx> for ty::AdtKind { } } +impl<'tcx> Stable<'tcx> for ty::FieldDef { + type T = stable_mir::ty::FieldDef; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + stable_mir::ty::FieldDef { + def: tables.create_def_id(self.did), + name: self.name.stable(tables), + } + } +} + impl<'tcx> Stable<'tcx> for ty::GenericArgs<'tcx> { type T = stable_mir::ty::GenericArgs; fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs index 33e301f697359..8f0c5f737965c 100644 --- a/compiler/stable_mir/src/compiler_interface.rs +++ b/compiler/stable_mir/src/compiler_interface.rs @@ -9,7 +9,7 @@ use crate::mir::alloc::{AllocId, GlobalAlloc}; use crate::mir::mono::{Instance, InstanceDef, StaticDef}; use crate::mir::Body; use crate::ty::{ - AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FnDef, GenericArgs, + AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, GenericArgs, GenericPredicates, Generics, ImplDef, ImplTrait, LineInfo, PolyFnSig, RigidTy, Span, TraitDecl, TraitDef, Ty, TyKind, VariantDef, }; @@ -76,6 +76,7 @@ pub trait Context { /// The name of a variant. fn variant_name(&self, def: VariantDef) -> Symbol; + fn variant_fields(&self, def: VariantDef) -> Vec; /// Evaluate constant as a target usize. fn eval_target_usize(&self, cnst: &Const) -> Result; @@ -89,6 +90,9 @@ pub trait Context { /// Returns the type of given crate item. fn def_ty(&self, item: DefId) -> Ty; + /// Returns the type of given definition instantiated with the given arguments. + fn def_ty_with_args(&self, item: DefId, args: &GenericArgs) -> Result; + /// Returns literal value of a const as a string. fn const_literal(&self, cnst: &Const) -> String; diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index 3bd42d703c35d..6f131cc4f0334 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -658,7 +658,7 @@ pub const RETURN_LOCAL: Local = 0; /// `b`'s `FieldIdx` is `1`, /// `c`'s `FieldIdx` is `0`, and /// `g`'s `FieldIdx` is `2`. -type FieldIdx = usize; +pub type FieldIdx = usize; type UserTypeAnnotationIndex = usize; diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index ebf43821fb51f..2724b1fe0a61f 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -376,6 +376,16 @@ impl AdtDef { with(|cx| cx.adt_kind(*self)) } + /// Retrieve the type of this Adt. + pub fn ty(&self) -> Ty { + with(|cx| cx.def_ty(self.0)) + } + + /// Retrieve the type of this Adt instantiating the type with the given arguments. + pub fn ty_with_args(&self, args: &GenericArgs) -> Result { + with(|cx| cx.def_ty_with_args(self.0, args)) + } + pub fn is_box(&self) -> bool { with(|cx| cx.adt_is_box(*self)) } @@ -397,7 +407,7 @@ impl AdtDef { } pub fn variant(&self, idx: VariantIdx) -> Option { - self.variants().get(idx.to_index()).copied() + (idx.to_index() < self.num_variants()).then_some(VariantDef { idx, adt_def: *self }) } } @@ -422,6 +432,36 @@ impl VariantDef { pub fn name(&self) -> Symbol { with(|cx| cx.variant_name(*self)) } + + /// Retrieve all the fields in this variant. + // We expect user to cache this and use it directly since today it is expensive to generate all + // fields name. + pub fn fields(&self) -> Vec { + with(|cx| cx.variant_fields(*self)) + } +} + +pub struct FieldDef { + /// The field definition. + /// + /// ## Warning + /// Do not access this field directly! This is public for the compiler to have access to it. + pub def: DefId, + + /// The field name. + pub name: Symbol, +} + +impl FieldDef { + /// Retrieve the type of this field instantiating the type with the given arguments. + pub fn ty_with_args(&self, args: &GenericArgs) -> Result { + with(|cx| cx.def_ty_with_args(self.def, args)) + } + + /// Retrieve the type of this field. + pub fn ty(&self) -> Ty { + with(|cx| cx.def_ty(self.def)) + } } impl Display for AdtKind { From 1a7b610da35f23a758498942a3a71e34ab0d32d2 Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Mon, 13 Nov 2023 11:31:39 +0000 Subject: [PATCH 05/13] Add riscv32 imafc bare metal target - riscv32imac-unknown-none-elf - Add platform support docs for rv32 --- compiler/rustc_target/src/spec/mod.rs | 1 + .../targets/riscv32imafc_unknown_none_elf.rs | 24 +++++++++++++ .../host-x86_64/dist-various-1/Dockerfile | 1 + src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/platform-support.md | 9 ++--- .../riscv32i-unknown-none-elf.md | 1 + .../riscv32im-unknown-none-elf.md | 1 + .../riscv32imac-unknown-none-elf.md | 34 +++++++++++++++++++ .../riscv32imafc-unknown-none-elf.md | 1 + .../riscv32imc-unknown-none-elf.md | 1 + src/tools/build-manifest/src/main.rs | 1 + 11 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_none_elf.rs create mode 100644 src/doc/rustc/src/platform-support/riscv32i-unknown-none-elf.md create mode 100644 src/doc/rustc/src/platform-support/riscv32im-unknown-none-elf.md create mode 100644 src/doc/rustc/src/platform-support/riscv32imac-unknown-none-elf.md create mode 100644 src/doc/rustc/src/platform-support/riscv32imafc-unknown-none-elf.md create mode 100644 src/doc/rustc/src/platform-support/riscv32imc-unknown-none-elf.md diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index dbce2f30f9397..63002aa4aa404 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1637,6 +1637,7 @@ supported_targets! { ("riscv32imc-esp-espidf", riscv32imc_esp_espidf), ("riscv32imac-esp-espidf", riscv32imac_esp_espidf), ("riscv32imac-unknown-none-elf", riscv32imac_unknown_none_elf), + ("riscv32imafc-unknown-none-elf", riscv32imafc_unknown_none_elf), ("riscv32imac-unknown-xous-elf", riscv32imac_unknown_xous_elf), ("riscv32gc-unknown-linux-gnu", riscv32gc_unknown_linux_gnu), ("riscv32gc-unknown-linux-musl", riscv32gc_unknown_linux_musl), diff --git a/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_none_elf.rs new file mode 100644 index 0000000000000..9fb68874ac8ea --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_none_elf.rs @@ -0,0 +1,24 @@ +use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(), + llvm_target: "riscv32".into(), + pointer_width: 32, + arch: "riscv32".into(), + + options: TargetOptions { + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + cpu: "generic-rv32".into(), + max_atomic_width: Some(32), + llvm_abiname: "ilp32f".into(), + features: "+m,+a,+c,+f".into(), + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + eh_frame_header: false, + ..Default::default() + }, + } +} diff --git a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile index 341e2de223aa5..adf3c67479b08 100644 --- a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile @@ -95,6 +95,7 @@ ENV TARGETS=$TARGETS,thumbv8m.main-none-eabihf ENV TARGETS=$TARGETS,riscv32i-unknown-none-elf ENV TARGETS=$TARGETS,riscv32imc-unknown-none-elf ENV TARGETS=$TARGETS,riscv32imac-unknown-none-elf +ENV TARGETS=$TARGETS,riscv32imafc-unknown-none-elf ENV TARGETS=$TARGETS,riscv64imac-unknown-none-elf ENV TARGETS=$TARGETS,riscv64gc-unknown-none-elf ENV TARGETS=$TARGETS,armebv7r-none-eabi diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 3bd90f7062dc2..ad33538951441 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -45,6 +45,7 @@ - [nvptx64-nvidia-cuda](platform-support/nvptx64-nvidia-cuda.md) - [powerpc64-ibm-aix](platform-support/aix.md) - [riscv32imac-unknown-xous-elf](platform-support/riscv32imac-unknown-xous-elf.md) + - [riscv32*-unknown-none-elf](platform-support/riscv32imac-unknown-none-elf.md) - [sparc-unknown-none-elf](./platform-support/sparc-unknown-none-elf.md) - [*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md) - [\*-nto-qnx-\*](platform-support/nto-qnx.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 3671fdd3fd2be..457e8d90a433d 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -159,9 +159,9 @@ target | std | notes [`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | | LoongArch64 Bare-metal (LP64D ABI) [`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | | LoongArch64 Bare-metal (LP64S ABI) [`nvptx64-nvidia-cuda`](platform-support/nvptx64-nvidia-cuda.md) | * | --emit=asm generates PTX code that [runs on NVIDIA GPUs] -`riscv32i-unknown-none-elf` | * | Bare RISC-V (RV32I ISA) -`riscv32imac-unknown-none-elf` | * | Bare RISC-V (RV32IMAC ISA) -`riscv32imc-unknown-none-elf` | * | Bare RISC-V (RV32IMC ISA) +[`riscv32imac-unknown-none-elf`](platform-support/riscv32imac-unknown-none-elf.md) | * | Bare RISC-V (RV32IMAC ISA) +[`riscv32i-unknown-none-elf`](platform-support/riscv32imac-unknown-none-elf.md) | * | Bare RISC-V (RV32I ISA) +[`riscv32imc-unknown-none-elf`](platform-support/riscv32imac-unknown-none-elf.md) | * | Bare RISC-V (RV32IMC ISA) `riscv64gc-unknown-none-elf` | * | Bare RISC-V (RV64IMAFDC ISA) `riscv64imac-unknown-none-elf` | * | Bare RISC-V (RV64IMAC ISA) `sparc64-unknown-linux-gnu` | ✓ | SPARC Linux (kernel 4.4, glibc 2.23) @@ -314,7 +314,8 @@ target | std | host | notes [`powerpc64-ibm-aix`](platform-support/aix.md) | ? | | 64-bit AIX (7.2 and newer) `riscv32gc-unknown-linux-gnu` | | | RISC-V Linux (kernel 5.4, glibc 2.33) `riscv32gc-unknown-linux-musl` | | | RISC-V Linux (kernel 5.4, musl + RISCV32 support patches) -`riscv32im-unknown-none-elf` | * | | Bare RISC-V (RV32IM ISA) +[`riscv32imafc-unknown-none-elf`](platform-support/riscv32imac-unknown-none-elf.md) | * | Bare RISC-V (RV32IMAFC ISA) +[`riscv32im-unknown-none-elf`](platform-support/riscv32imac-unknown-none-elf.md) | * | | Bare RISC-V (RV32IM ISA) [`riscv32imac-unknown-xous-elf`](platform-support/riscv32imac-unknown-xous-elf.md) | ? | | RISC-V Xous (RV32IMAC ISA) [`riscv32imc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF [`riscv32imac-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF diff --git a/src/doc/rustc/src/platform-support/riscv32i-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32i-unknown-none-elf.md new file mode 100644 index 0000000000000..edfe07fc0537b --- /dev/null +++ b/src/doc/rustc/src/platform-support/riscv32i-unknown-none-elf.md @@ -0,0 +1 @@ +riscv32imac-unknown-none-elf.md diff --git a/src/doc/rustc/src/platform-support/riscv32im-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32im-unknown-none-elf.md new file mode 100644 index 0000000000000..edfe07fc0537b --- /dev/null +++ b/src/doc/rustc/src/platform-support/riscv32im-unknown-none-elf.md @@ -0,0 +1 @@ +riscv32imac-unknown-none-elf.md diff --git a/src/doc/rustc/src/platform-support/riscv32imac-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32imac-unknown-none-elf.md new file mode 100644 index 0000000000000..a069f3d3aa9c3 --- /dev/null +++ b/src/doc/rustc/src/platform-support/riscv32imac-unknown-none-elf.md @@ -0,0 +1,34 @@ +# `riscv32{i,im,imc,imac,imafc}-unknown-none-elf` + +**Tier: 2/3** + +Bare-metal target for RISC-V CPUs with the RV32I, RV32IM, RV32IMC, RV32IMAFC and RV32IMAC ISAs. + +## Target maintainers + +* Rust Embedded Working Group, [RISC-V team](https://github.com/rust-embedded/wg#the-risc-v-team) + +## Requirements + +The target is cross-compiled, and uses static linking. No external toolchain +is required and the default `rust-lld` linker works, but you must specify +a linker script. The [`riscv-rt`] crate provides a suitable one. The +[`riscv-rust-quickstart`] repository gives an example of an RV32 project. + +[`riscv-rt`]: https://crates.io/crates/riscv-rt +[`riscv-rust-quickstart`]: https://github.com/riscv-rust/riscv-rust-quickstart + +## Building the target + +This target is included in Rust and can be installed via `rustup`. + +## Testing + +This is a cross-compiled no-std target, which must be run either in a simulator +or by programming them onto suitable hardware. It is not possible to run the +Rust testsuite on this target. + +## Cross-compilation toolchains and C code + +This target supports C code. If interlinking with C or C++, you may need to use +riscv64-unknown-elf-gcc as a linker instead of rust-lld. diff --git a/src/doc/rustc/src/platform-support/riscv32imafc-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32imafc-unknown-none-elf.md new file mode 100644 index 0000000000000..edfe07fc0537b --- /dev/null +++ b/src/doc/rustc/src/platform-support/riscv32imafc-unknown-none-elf.md @@ -0,0 +1 @@ +riscv32imac-unknown-none-elf.md diff --git a/src/doc/rustc/src/platform-support/riscv32imc-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32imc-unknown-none-elf.md new file mode 100644 index 0000000000000..edfe07fc0537b --- /dev/null +++ b/src/doc/rustc/src/platform-support/riscv32imc-unknown-none-elf.md @@ -0,0 +1 @@ +riscv32imac-unknown-none-elf.md diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 83c7ccc9edeb7..9ac97236f19c8 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -124,6 +124,7 @@ static TARGETS: &[&str] = &[ "riscv32im-unknown-none-elf", "riscv32imc-unknown-none-elf", "riscv32imac-unknown-none-elf", + "riscv32imafc-unknown-none-elf", "riscv32gc-unknown-linux-gnu", "riscv64imac-unknown-none-elf", "riscv64gc-unknown-hermit", From 68ea62100bfa8859c07682e3409685e41bd9014f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 5 Dec 2023 19:23:33 +0100 Subject: [PATCH 06/13] add comment about keeping flags in sync between bootstrap.py and bootstrap.rs --- src/bootstrap/bootstrap.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 4691fb3ad6f9f..2328dd822afd2 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -946,6 +946,8 @@ def build_bootstrap_cmd(self, env): target_linker = self.get_toml("linker", build_section) if target_linker is not None: env["RUSTFLAGS"] += " -C linker=" + target_linker + # When changing this list, also update the corresponding list in `Builder::cargo` + # in `src/bootstrap/src/core/builder.rs`. env["RUSTFLAGS"] += " -Wrust_2018_idioms -Wunused_lifetimes" if self.warnings == "default": deny_warnings = self.get_toml("deny-warnings", "rust") != "false" From 326fea0fb86e148adc8168ac70a5ceddef3ad9a3 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Tue, 5 Dec 2023 12:01:09 -0800 Subject: [PATCH 07/13] Change ty_with_args to return Ty instead of Result Although, we would like to avoid crashes whenever possible, and that's why I wanted to make this API fallible. It's looking pretty hard to do proper validation. I think many of our APIs will unfortunately depend on the user doing the correct thing since at the MIR level we are working on, we expect types to have been checked already. --- compiler/rustc_smir/src/rustc_smir/context.rs | 9 +- .../rustc_smir/src/rustc_smir/convert/mod.rs | 21 ---- compiler/rustc_smir/src/rustc_smir/mod.rs | 21 ++++ compiler/stable_mir/src/compiler_interface.rs | 2 +- compiler/stable_mir/src/error.rs | 2 +- compiler/stable_mir/src/mir/visit.rs | 2 +- compiler/stable_mir/src/ty.rs | 9 +- tests/ui-fulldeps/stable-mir/check_ty_fold.rs | 115 ++++++++++++++++++ 8 files changed, 148 insertions(+), 33 deletions(-) create mode 100644 tests/ui-fulldeps/stable-mir/check_ty_fold.rs diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 8ddd3d4853965..93d49038fc1c7 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -255,16 +255,11 @@ impl<'tcx> Context for TablesWrapper<'tcx> { tables.tcx.type_of(item.internal(&mut *tables)).instantiate_identity().stable(&mut *tables) } - fn def_ty_with_args( - &self, - item: stable_mir::DefId, - args: &GenericArgs, - ) -> Result { + fn def_ty_with_args(&self, item: stable_mir::DefId, args: &GenericArgs) -> stable_mir::ty::Ty { let mut tables = self.0.borrow_mut(); let args = args.internal(&mut *tables); let def_ty = tables.tcx.type_of(item.internal(&mut *tables)); - // FIXME(celinval): use try_fold instead to avoid crashing. - Ok(def_ty.instantiate(tables.tcx, args).stable(&mut *tables)) + def_ty.instantiate(tables.tcx, args).stable(&mut *tables) } fn const_literal(&self, cnst: &stable_mir::ty::Const) -> String { diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs index c3ee0a60f4d35..9c0b2b29bca71 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs @@ -75,24 +75,3 @@ impl<'tcx> Stable<'tcx> for rustc_span::Span { tables.create_span(*self) } } - -impl<'tcx, T> Stable<'tcx> for &[T] -where - T: Stable<'tcx>, -{ - type T = Vec; - fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { - self.iter().map(|e| e.stable(tables)).collect() - } -} - -impl<'tcx, T, U> Stable<'tcx> for (T, U) -where - T: Stable<'tcx>, - U: Stable<'tcx>, -{ - type T = (T::T, U::T); - fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { - (self.0.stable(tables), self.1.stable(tables)) - } -} diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index eee587f3b2ab1..4cb48a12c96b5 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -141,3 +141,24 @@ where } } } + +impl<'tcx, T> Stable<'tcx> for &[T] +where + T: Stable<'tcx>, +{ + type T = Vec; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + self.iter().map(|e| e.stable(tables)).collect() + } +} + +impl<'tcx, T, U> Stable<'tcx> for (T, U) +where + T: Stable<'tcx>, + U: Stable<'tcx>, +{ + type T = (T::T, U::T); + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + (self.0.stable(tables), self.1.stable(tables)) + } +} diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs index 8f0c5f737965c..d8a3d4eda4be3 100644 --- a/compiler/stable_mir/src/compiler_interface.rs +++ b/compiler/stable_mir/src/compiler_interface.rs @@ -91,7 +91,7 @@ pub trait Context { fn def_ty(&self, item: DefId) -> Ty; /// Returns the type of given definition instantiated with the given arguments. - fn def_ty_with_args(&self, item: DefId, args: &GenericArgs) -> Result; + fn def_ty_with_args(&self, item: DefId, args: &GenericArgs) -> Ty; /// Returns literal value of a const as a string. fn const_literal(&self, cnst: &Const) -> String; diff --git a/compiler/stable_mir/src/error.rs b/compiler/stable_mir/src/error.rs index 1ff65717e87db..bb5e1a34180ba 100644 --- a/compiler/stable_mir/src/error.rs +++ b/compiler/stable_mir/src/error.rs @@ -28,7 +28,7 @@ pub enum CompilerError { } /// A generic error to represent an API request that cannot be fulfilled. -#[derive(Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Error(pub(crate) String); impl Error { diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs index 0c44781c46394..d46caad9a016b 100644 --- a/compiler/stable_mir/src/mir/visit.rs +++ b/compiler/stable_mir/src/mir/visit.rs @@ -452,7 +452,7 @@ impl Location { } /// Information about a place's usage. -#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct PlaceContext { /// Whether the access is mutable or not. Keep this private so we can increment the type in a /// backward compatible manner. diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index 2724b1fe0a61f..f64b1f5f5a355 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -382,7 +382,9 @@ impl AdtDef { } /// Retrieve the type of this Adt instantiating the type with the given arguments. - pub fn ty_with_args(&self, args: &GenericArgs) -> Result { + /// + /// This will assume the type can be instantiated with these arguments. + pub fn ty_with_args(&self, args: &GenericArgs) -> Ty { with(|cx| cx.def_ty_with_args(self.0, args)) } @@ -441,6 +443,7 @@ impl VariantDef { } } +#[derive(Clone, Debug, Eq, PartialEq)] pub struct FieldDef { /// The field definition. /// @@ -454,7 +457,9 @@ pub struct FieldDef { impl FieldDef { /// Retrieve the type of this field instantiating the type with the given arguments. - pub fn ty_with_args(&self, args: &GenericArgs) -> Result { + /// + /// This will assume the type can be instantiated with these arguments. + pub fn ty_with_args(&self, args: &GenericArgs) -> Ty { with(|cx| cx.def_ty_with_args(self.def, args)) } diff --git a/tests/ui-fulldeps/stable-mir/check_ty_fold.rs b/tests/ui-fulldeps/stable-mir/check_ty_fold.rs new file mode 100644 index 0000000000000..b90d47d4540ba --- /dev/null +++ b/tests/ui-fulldeps/stable-mir/check_ty_fold.rs @@ -0,0 +1,115 @@ +// run-pass +//! Test that users are able to use stable mir APIs to retrieve monomorphized types, and that +//! we have an error handling for trying to instantiate types with incorrect arguments. + +// ignore-stage1 +// ignore-cross-compile +// ignore-remote +// ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 +// edition: 2021 + +#![feature(rustc_private)] +#![feature(assert_matches)] +#![feature(control_flow_enum)] + +extern crate rustc_middle; +#[macro_use] +extern crate rustc_smir; +extern crate rustc_driver; +extern crate rustc_interface; +extern crate stable_mir; + +use rustc_middle::ty::TyCtxt; +use rustc_smir::rustc_internal; +use stable_mir::ty::{RigidTy, TyKind, Ty, }; +use stable_mir::mir::{Body, MirVisitor, FieldIdx, Place, ProjectionElem, visit::{Location, + PlaceContext}}; +use std::io::Write; +use std::ops::ControlFlow; + +const CRATE_NAME: &str = "input"; + +/// This function uses the Stable MIR APIs to get information about the test crate. +fn test_stable_mir(_tcx: TyCtxt<'_>) -> ControlFlow<()> { + let main_fn = stable_mir::entry_fn(); + let body = main_fn.unwrap().body(); + let mut visitor = PlaceVisitor{ body: &body, tested: false}; + visitor.visit_body(&body); + assert!(visitor.tested); + ControlFlow::Continue(()) +} + +struct PlaceVisitor<'a> { + body: &'a Body, + /// Used to ensure that the test was reachable. Otherwise this test would vacuously succeed. + tested: bool, +} + +/// Check that `wrapper.inner` place projection can be correctly interpreted. +/// Ensure that instantiation is correct. +fn check_tys(local_ty: Ty, idx: FieldIdx, expected_ty: Ty) { + let TyKind::RigidTy(RigidTy::Adt(def, args)) = local_ty.kind() else { unreachable!() }; + assert_eq!(def.ty_with_args(&args), local_ty); + + let field_def = &def.variants_iter().next().unwrap().fields()[idx]; + let field_ty = field_def.ty_with_args(&args); + assert_eq!(field_ty, expected_ty); + + // Check that the generic version is different than the instantiated one. + let field_ty_gen = field_def.ty(); + assert_ne!(field_ty_gen, field_ty); +} + +impl<'a> MirVisitor for PlaceVisitor<'a> { + fn visit_place(&mut self, place: &Place, _ptx: PlaceContext, _loc: Location) { + let start_ty = self.body.locals()[place.local].ty; + match place.projection.as_slice() { + [ProjectionElem::Field(idx, ty)] => { + check_tys(start_ty, *idx, *ty); + self.tested = true; + } + _ => {} + } + } +} + +/// This test will generate and analyze a dummy crate using the stable mir. +/// For that, it will first write the dummy crate into a file. +/// Then it will create a `StableMir` using custom arguments and then +/// it will run the compiler. +fn main() { + let path = "ty_fold_input.rs"; + generate_input(&path).unwrap(); + let args = vec![ + "rustc".to_string(), + "-Cpanic=abort".to_string(), + "--crate-name".to_string(), + CRATE_NAME.to_string(), + path.to_string(), + ]; + run!(args, tcx, test_stable_mir(tcx)).unwrap(); +} + +fn generate_input(path: &str) -> std::io::Result<()> { + let mut file = std::fs::File::create(path)?; + write!( + file, + r#" + struct Wrapper {{ + pub inner: T + }} + + impl Wrapper {{ + pub fn new() -> Wrapper {{ + Wrapper {{ inner: T::default() }} + }} + }} + + fn main() {{ + let wrapper = Wrapper::::new(); + let _inner = wrapper.inner; + }} + "# + )?; + Ok(()) +} From 6c3879d1f154bb6f18562d29aa30fbc03239c66f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 6 Oct 2023 19:07:05 +0000 Subject: [PATCH 08/13] Provide context when `?` can't be called because of `Result<_, E>` When a method chain ending in `?` causes an E0277 because the expression's `Result::Err` variant doesn't have a type that can be converted to the `Result<_, E>` type parameter in the return type, provide additional context of which parts of the chain can and can't support the `?` operator. ``` error[E0277]: `?` couldn't convert the error to `String` --> $DIR/question-mark-result-err-mismatch.rs:28:25 | LL | fn bar() -> Result<(), String> { | ------------------ expected `String` because of this LL | let x = foo(); | ----- this can be annotated with `?` because it has type `Result` LL | let one = x LL | .map(|s| ()) | ----------- this can be annotated with `?` because it has type `Result<(), String>` LL | .map_err(|_| ())?; | ---------------^ the trait `From<()>` is not implemented for `String` | | | this can't be annotated with `?` because it has type `Result<(), ()>` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait = help: the following other types implement trait `From`: > >> >> > > > = note: required for `Result<(), String>` to implement `FromResidual>` ``` Fix #72124. --- .../error_reporting/type_err_ctxt_ext.rs | 144 +++++++++++++++++- .../question-mark-result-err-mismatch.rs | 59 +++++++ .../question-mark-result-err-mismatch.stderr | 83 ++++++++++ tests/ui/try-block/try-block-bad-type.stderr | 4 +- tests/ui/try-trait/bad-interconversion.stderr | 4 +- tests/ui/try-trait/issue-32709.stderr | 4 +- 6 files changed, 294 insertions(+), 4 deletions(-) create mode 100644 tests/ui/traits/question-mark-result-err-mismatch.rs create mode 100644 tests/ui/traits/question-mark-result-err-mismatch.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index b3910a2770b3a..bad74588d0cc8 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -3,6 +3,7 @@ use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch}; use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::infer::InferCtxtExt as _; use crate::infer::{self, InferCtxt}; use crate::traits::error_reporting::infer_ctxt_ext::InferCtxtExt; use crate::traits::error_reporting::{ambiguity, ambiguity::Ambiguity::*}; @@ -40,7 +41,7 @@ use rustc_session::config::{DumpSolverProofTree, TraitSolver}; use rustc_session::Limit; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::sym; -use rustc_span::{ExpnKind, Span, DUMMY_SP}; +use rustc_span::{BytePos, ExpnKind, Span, DUMMY_SP}; use std::borrow::Cow; use std::fmt; use std::iter; @@ -106,6 +107,13 @@ pub trait TypeErrCtxtExt<'tcx> { fn fn_arg_obligation(&self, obligation: &PredicateObligation<'tcx>) -> bool; + fn try_conversion_context( + &self, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + err: &mut Diagnostic, + ); + fn report_const_param_not_wf( &self, ty: Ty<'tcx>, @@ -509,6 +517,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut err = struct_span_err!(self.tcx.sess, span, E0277, "{}", err_msg); + if is_try_conversion { + self.try_conversion_context(&obligation, trait_ref.skip_binder(), &mut err); + } + if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) { err.span_label( ret_span, @@ -982,6 +994,136 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { false } + /// When the `E` of the resulting `Result` in an expression `foo().bar().baz()?`, + /// identify thoe method chain sub-expressions that could or could not have been annotated + /// with `?`. + fn try_conversion_context( + &self, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + err: &mut Diagnostic, + ) { + let span = obligation.cause.span; + struct V<'v> { + search_span: Span, + found: Option<&'v hir::Expr<'v>>, + } + impl<'v> Visitor<'v> for V<'v> { + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + if let hir::ExprKind::Match(expr, _arms, hir::MatchSource::TryDesugar(_)) = ex.kind + { + if ex.span.with_lo(ex.span.hi() - BytePos(1)).source_equal(self.search_span) { + if let hir::ExprKind::Call(_, [expr, ..]) = expr.kind { + self.found = Some(expr); + return; + } + } + } + hir::intravisit::walk_expr(self, ex); + } + } + let hir_id = self.tcx.local_def_id_to_hir_id(obligation.cause.body_id); + let body_id = match self.tcx.hir().find(hir_id) { + Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) => { + body_id + } + _ => return, + }; + let mut v = V { search_span: span, found: None }; + v.visit_body(self.tcx.hir().body(*body_id)); + let Some(expr) = v.found else { + return; + }; + let Some(typeck) = &self.typeck_results else { + return; + }; + let Some((ObligationCauseCode::QuestionMark, Some(y))) = obligation.cause.code().parent() + else { + return; + }; + if !self.tcx.is_diagnostic_item(sym::FromResidual, y.def_id()) { + return; + } + let self_ty = trait_ref.self_ty(); + + let mut prev_ty = self.resolve_vars_if_possible( + typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), + ); + let mut annotate_expr = |span: Span, prev_ty: Ty<'tcx>, self_ty: Ty<'tcx>| -> bool { + // We always look at the `E` type, because that's the only one affected by `?`. If the + // incorrect `Result` is because of the `T`, we'll get an E0308 on the whole + // expression, after the `?` has "unwrapped" the `T`. + let ty::Adt(def, args) = prev_ty.kind() else { + return false; + }; + let Some(arg) = args.get(1) else { + return false; + }; + if !self.tcx.is_diagnostic_item(sym::Result, def.did()) { + return false; + } + let can = if self + .infcx + .type_implements_trait( + self.tcx.get_diagnostic_item(sym::From).unwrap(), + [self_ty.into(), *arg], + obligation.param_env, + ) + .must_apply_modulo_regions() + { + "can" + } else { + "can't" + }; + err.span_label( + span, + format!("this {can} be annotated with `?` because it has type `{prev_ty}`"), + ); + true + }; + + // The following logic is simlar to `point_at_chain`, but that's focused on associated types + let mut expr = expr; + while let hir::ExprKind::MethodCall(_path_segment, rcvr_expr, _args, span) = expr.kind { + // Point at every method call in the chain with the `Result` type. + // let foo = bar.iter().map(mapper)?; + // ------ ----------- + expr = rcvr_expr; + if !annotate_expr(span, prev_ty, self_ty) { + break; + } + + prev_ty = self.resolve_vars_if_possible( + typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), + ); + + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind + && let hir::Path { res: hir::def::Res::Local(hir_id), .. } = path + && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(*hir_id) + && let Some(parent) = self.tcx.hir().find_parent(binding.hir_id) + { + // We've reached the root of the method call chain... + if let hir::Node::Local(local) = parent + && let Some(binding_expr) = local.init + { + // ...and it is a binding. Get the binding creation and continue the chain. + expr = binding_expr; + } + if let hir::Node::Param(_param) = parent { + // ...and it is a an fn argument. + break; + } + } + } + // `expr` is now the "root" expression of the method call chain, which can be any + // expression kind, like a method call or a path. If this expression is `Result` as + // well, then we also point at it. + prev_ty = self.resolve_vars_if_possible( + typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), + ); + annotate_expr(expr.span, prev_ty, self_ty); + } + fn report_const_param_not_wf( &self, ty: Ty<'tcx>, diff --git a/tests/ui/traits/question-mark-result-err-mismatch.rs b/tests/ui/traits/question-mark-result-err-mismatch.rs new file mode 100644 index 0000000000000..7b364580858be --- /dev/null +++ b/tests/ui/traits/question-mark-result-err-mismatch.rs @@ -0,0 +1,59 @@ +fn foo() -> Result { //~ NOTE expected `String` because of this + let test = String::from("one,two"); + let x = test + .split_whitespace() + .next() + .ok_or_else(|| { //~ NOTE this can be annotated with `?` because it has type `Result<&str, &str>` + "Couldn't split the test string" + }); + let one = x + .map(|s| ()) //~ NOTE this can be annotated with `?` because it has type `Result<(), &str>` + .map_err(|_| ()) //~ NOTE this can't be annotated with `?` because it has type `Result<(), ()>` + .map(|()| "")?; //~ ERROR `?` couldn't convert the error to `String` + //~^ NOTE in this expansion of desugaring of operator `?` + //~| NOTE in this expansion of desugaring of operator `?` + //~| NOTE in this expansion of desugaring of operator `?` + //~| NOTE in this expansion of desugaring of operator `?` + //~| NOTE this can't be annotated with `?` because it has type `Result<&str, ()>` + //~| NOTE the trait `From<()>` is not implemented for `String` + //~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait + //~| NOTE required for `Result` to implement `FromResidual>` + Ok(one.to_string()) +} + +fn bar() -> Result<(), String> { //~ NOTE expected `String` because of this + let x = foo(); //~ NOTE this can be annotated with `?` because it has type `Result` + let one = x + .map(|s| ()) //~ NOTE this can be annotated with `?` because it has type `Result<(), String>` + .map_err(|_| ())?; //~ ERROR `?` couldn't convert the error to `String` + //~^ NOTE in this expansion of desugaring of operator `?` + //~| NOTE in this expansion of desugaring of operator `?` + //~| NOTE in this expansion of desugaring of operator `?` + //~| NOTE in this expansion of desugaring of operator `?` + //~| NOTE this can't be annotated with `?` because it has type `Result<(), ()>` + //~| NOTE the trait `From<()>` is not implemented for `String` + //~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait + //~| NOTE required for `Result<(), String>` to implement `FromResidual>` + Ok(one) +} + +fn baz() -> Result { //~ NOTE expected `String` because of this + let test = String::from("one,two"); + let one = test + .split_whitespace() + .next() + .ok_or_else(|| { //~ NOTE this can't be annotated with `?` because it has type `Result<&str, ()>` + "Couldn't split the test string"; + })?; + //~^ ERROR `?` couldn't convert the error to `String` + //~| NOTE in this expansion of desugaring of operator `?` + //~| NOTE in this expansion of desugaring of operator `?` + //~| NOTE in this expansion of desugaring of operator `?` + //~| NOTE in this expansion of desugaring of operator `?` + //~| NOTE the trait `From<()>` is not implemented for `String` + //~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait + //~| NOTE required for `Result` to implement `FromResidual>` + Ok(one.to_string()) +} + +fn main() {} diff --git a/tests/ui/traits/question-mark-result-err-mismatch.stderr b/tests/ui/traits/question-mark-result-err-mismatch.stderr new file mode 100644 index 0000000000000..f6acbc6dd0785 --- /dev/null +++ b/tests/ui/traits/question-mark-result-err-mismatch.stderr @@ -0,0 +1,83 @@ +error[E0277]: `?` couldn't convert the error to `String` + --> $DIR/question-mark-result-err-mismatch.rs:12:22 + | +LL | fn foo() -> Result { + | ---------------------- expected `String` because of this +... +LL | .ok_or_else(|| { + | __________- +LL | | "Couldn't split the test string" +LL | | }); + | |__________- this can be annotated with `?` because it has type `Result<&str, &str>` +LL | let one = x +LL | .map(|s| ()) + | ----------- this can be annotated with `?` because it has type `Result<(), &str>` +LL | .map_err(|_| ()) + | --------------- this can't be annotated with `?` because it has type `Result<(), ()>` +LL | .map(|()| "")?; + | ------------^ the trait `From<()>` is not implemented for `String` + | | + | this can't be annotated with `?` because it has type `Result<&str, ()>` + | + = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait + = help: the following other types implement trait `From`: + > + >> + >> + > + > + > + = note: required for `Result` to implement `FromResidual>` + +error[E0277]: `?` couldn't convert the error to `String` + --> $DIR/question-mark-result-err-mismatch.rs:28:25 + | +LL | fn bar() -> Result<(), String> { + | ------------------ expected `String` because of this +LL | let x = foo(); + | ----- this can be annotated with `?` because it has type `Result` +LL | let one = x +LL | .map(|s| ()) + | ----------- this can be annotated with `?` because it has type `Result<(), String>` +LL | .map_err(|_| ())?; + | ---------------^ the trait `From<()>` is not implemented for `String` + | | + | this can't be annotated with `?` because it has type `Result<(), ()>` + | + = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait + = help: the following other types implement trait `From`: + > + >> + >> + > + > + > + = note: required for `Result<(), String>` to implement `FromResidual>` + +error[E0277]: `?` couldn't convert the error to `String` + --> $DIR/question-mark-result-err-mismatch.rs:47:11 + | +LL | fn baz() -> Result { + | ---------------------- expected `String` because of this +... +LL | .ok_or_else(|| { + | __________- +LL | | "Couldn't split the test string"; +LL | | })?; + | | -^ the trait `From<()>` is not implemented for `String` + | |__________| + | this can't be annotated with `?` because it has type `Result<&str, ()>` + | + = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait + = help: the following other types implement trait `From`: + > + >> + >> + > + > + > + = note: required for `Result` to implement `FromResidual>` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/try-block/try-block-bad-type.stderr b/tests/ui/try-block/try-block-bad-type.stderr index b41bf86d3d911..d58a011ff5501 100644 --- a/tests/ui/try-block/try-block-bad-type.stderr +++ b/tests/ui/try-block/try-block-bad-type.stderr @@ -2,7 +2,9 @@ error[E0277]: `?` couldn't convert the error to `TryFromSliceError` --> $DIR/try-block-bad-type.rs:7:16 | LL | Err("")?; - | ^ the trait `From<&str>` is not implemented for `TryFromSliceError` + | -------^ the trait `From<&str>` is not implemented for `TryFromSliceError` + | | + | this can't be annotated with `?` because it has type `Result<_, &str>` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait = help: the trait `From` is implemented for `TryFromSliceError` diff --git a/tests/ui/try-trait/bad-interconversion.stderr b/tests/ui/try-trait/bad-interconversion.stderr index d8b9431becc6b..97fbbdbf8f8a8 100644 --- a/tests/ui/try-trait/bad-interconversion.stderr +++ b/tests/ui/try-trait/bad-interconversion.stderr @@ -4,7 +4,9 @@ error[E0277]: `?` couldn't convert the error to `u8` LL | fn result_to_result() -> Result { | --------------- expected `u8` because of this LL | Ok(Err(123_i32)?) - | ^ the trait `From` is not implemented for `u8` + | ------------^ the trait `From` is not implemented for `u8` + | | + | this can't be annotated with `?` because it has type `Result<_, i32>` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait = help: the following other types implement trait `From`: diff --git a/tests/ui/try-trait/issue-32709.stderr b/tests/ui/try-trait/issue-32709.stderr index 46798f5dcfda8..b155b3ff66313 100644 --- a/tests/ui/try-trait/issue-32709.stderr +++ b/tests/ui/try-trait/issue-32709.stderr @@ -4,7 +4,9 @@ error[E0277]: `?` couldn't convert the error to `()` LL | fn a() -> Result { | --------------- expected `()` because of this LL | Err(5)?; - | ^ the trait `From<{integer}>` is not implemented for `()` + | ------^ the trait `From<{integer}>` is not implemented for `()` + | | + | this can't be annotated with `?` because it has type `Result<_, {integer}>` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait = help: the following other types implement trait `From`: From 53817963ed8796b35f802d87866aea8de5d4caf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 7 Oct 2023 01:14:43 +0000 Subject: [PATCH 09/13] Point at fewer methods in the chain, only those that change the E type --- .../error_reporting/type_err_ctxt_ext.rs | 76 ++++++++++++------- .../question-mark-result-err-mismatch.rs | 15 ++-- .../question-mark-result-err-mismatch.stderr | 26 +++---- 3 files changed, 64 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index bad74588d0cc8..58ea7a0edd521 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -1049,39 +1049,25 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut prev_ty = self.resolve_vars_if_possible( typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), ); - let mut annotate_expr = |span: Span, prev_ty: Ty<'tcx>, self_ty: Ty<'tcx>| -> bool { - // We always look at the `E` type, because that's the only one affected by `?`. If the - // incorrect `Result` is because of the `T`, we'll get an E0308 on the whole - // expression, after the `?` has "unwrapped" the `T`. + + // We always look at the `E` type, because that's the only one affected by `?`. If the + // incorrect `Result` is because of the `T`, we'll get an E0308 on the whole + // expression, after the `?` has "unwrapped" the `T`. + let get_e_type = |prev_ty: Ty<'tcx>| -> Option> { let ty::Adt(def, args) = prev_ty.kind() else { - return false; + return None; }; let Some(arg) = args.get(1) else { - return false; + return None; }; if !self.tcx.is_diagnostic_item(sym::Result, def.did()) { - return false; + return None; } - let can = if self - .infcx - .type_implements_trait( - self.tcx.get_diagnostic_item(sym::From).unwrap(), - [self_ty.into(), *arg], - obligation.param_env, - ) - .must_apply_modulo_regions() - { - "can" - } else { - "can't" - }; - err.span_label( - span, - format!("this {can} be annotated with `?` because it has type `{prev_ty}`"), - ); - true + Some(arg.as_type()?) }; + let mut chain = vec![]; + // The following logic is simlar to `point_at_chain`, but that's focused on associated types let mut expr = expr; while let hir::ExprKind::MethodCall(_path_segment, rcvr_expr, _args, span) = expr.kind { @@ -1089,9 +1075,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // let foo = bar.iter().map(mapper)?; // ------ ----------- expr = rcvr_expr; - if !annotate_expr(span, prev_ty, self_ty) { - break; - } + chain.push((span, prev_ty)); prev_ty = self.resolve_vars_if_possible( typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), @@ -1121,7 +1105,41 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { prev_ty = self.resolve_vars_if_possible( typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), ); - annotate_expr(expr.span, prev_ty, self_ty); + chain.push((expr.span, prev_ty)); + + let mut prev = None; + for (span, err_ty) in chain.into_iter().rev() { + let err_ty = get_e_type(err_ty); + let err_ty = match (err_ty, prev) { + (Some(err_ty), Some(prev)) if !self.can_eq(obligation.param_env, err_ty, prev) => { + err_ty + } + (Some(err_ty), None) => err_ty, + _ => { + prev = err_ty; + continue; + } + }; + if self + .infcx + .type_implements_trait( + self.tcx.get_diagnostic_item(sym::From).unwrap(), + [self_ty, err_ty], + obligation.param_env, + ) + .must_apply_modulo_regions() + { + err.span_label(span, format!("this has type `Result<_, {err_ty}>`")); + } else { + err.span_label( + span, + format!( + "this can't be annotated with `?` because it has type `Result<_, {err_ty}>`", + ), + ); + } + prev = Some(err_ty); + } } fn report_const_param_not_wf( diff --git a/tests/ui/traits/question-mark-result-err-mismatch.rs b/tests/ui/traits/question-mark-result-err-mismatch.rs index 7b364580858be..e5ccca2e5f7c7 100644 --- a/tests/ui/traits/question-mark-result-err-mismatch.rs +++ b/tests/ui/traits/question-mark-result-err-mismatch.rs @@ -3,18 +3,17 @@ fn foo() -> Result { //~ NOTE expected `String` because of this let x = test .split_whitespace() .next() - .ok_or_else(|| { //~ NOTE this can be annotated with `?` because it has type `Result<&str, &str>` + .ok_or_else(|| { //~ NOTE this has type `Result<_, &str>` "Couldn't split the test string" }); let one = x - .map(|s| ()) //~ NOTE this can be annotated with `?` because it has type `Result<(), &str>` - .map_err(|_| ()) //~ NOTE this can't be annotated with `?` because it has type `Result<(), ()>` + .map(|s| ()) + .map_err(|_| ()) //~ NOTE this can't be annotated with `?` because it has type `Result<_, ()>` .map(|()| "")?; //~ ERROR `?` couldn't convert the error to `String` //~^ NOTE in this expansion of desugaring of operator `?` //~| NOTE in this expansion of desugaring of operator `?` //~| NOTE in this expansion of desugaring of operator `?` //~| NOTE in this expansion of desugaring of operator `?` - //~| NOTE this can't be annotated with `?` because it has type `Result<&str, ()>` //~| NOTE the trait `From<()>` is not implemented for `String` //~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait //~| NOTE required for `Result` to implement `FromResidual>` @@ -22,15 +21,15 @@ fn foo() -> Result { //~ NOTE expected `String` because of this } fn bar() -> Result<(), String> { //~ NOTE expected `String` because of this - let x = foo(); //~ NOTE this can be annotated with `?` because it has type `Result` + let x = foo(); //~ NOTE this has type `Result<_, String>` let one = x - .map(|s| ()) //~ NOTE this can be annotated with `?` because it has type `Result<(), String>` + .map(|s| ()) .map_err(|_| ())?; //~ ERROR `?` couldn't convert the error to `String` //~^ NOTE in this expansion of desugaring of operator `?` //~| NOTE in this expansion of desugaring of operator `?` //~| NOTE in this expansion of desugaring of operator `?` //~| NOTE in this expansion of desugaring of operator `?` - //~| NOTE this can't be annotated with `?` because it has type `Result<(), ()>` + //~| NOTE this can't be annotated with `?` because it has type `Result<_, ()>` //~| NOTE the trait `From<()>` is not implemented for `String` //~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait //~| NOTE required for `Result<(), String>` to implement `FromResidual>` @@ -42,7 +41,7 @@ fn baz() -> Result { //~ NOTE expected `String` because of this let one = test .split_whitespace() .next() - .ok_or_else(|| { //~ NOTE this can't be annotated with `?` because it has type `Result<&str, ()>` + .ok_or_else(|| { //~ NOTE this can't be annotated with `?` because it has type `Result<_, ()>` "Couldn't split the test string"; })?; //~^ ERROR `?` couldn't convert the error to `String` diff --git a/tests/ui/traits/question-mark-result-err-mismatch.stderr b/tests/ui/traits/question-mark-result-err-mismatch.stderr index f6acbc6dd0785..fc3b2e6b46b38 100644 --- a/tests/ui/traits/question-mark-result-err-mismatch.stderr +++ b/tests/ui/traits/question-mark-result-err-mismatch.stderr @@ -8,16 +8,12 @@ LL | .ok_or_else(|| { | __________- LL | | "Couldn't split the test string" LL | | }); - | |__________- this can be annotated with `?` because it has type `Result<&str, &str>` -LL | let one = x -LL | .map(|s| ()) - | ----------- this can be annotated with `?` because it has type `Result<(), &str>` + | |__________- this has type `Result<_, &str>` +... LL | .map_err(|_| ()) - | --------------- this can't be annotated with `?` because it has type `Result<(), ()>` + | --------------- this can't be annotated with `?` because it has type `Result<_, ()>` LL | .map(|()| "")?; - | ------------^ the trait `From<()>` is not implemented for `String` - | | - | this can't be annotated with `?` because it has type `Result<&str, ()>` + | ^ the trait `From<()>` is not implemented for `String` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait = help: the following other types implement trait `From`: @@ -30,19 +26,17 @@ LL | .map(|()| "")?; = note: required for `Result` to implement `FromResidual>` error[E0277]: `?` couldn't convert the error to `String` - --> $DIR/question-mark-result-err-mismatch.rs:28:25 + --> $DIR/question-mark-result-err-mismatch.rs:27:25 | LL | fn bar() -> Result<(), String> { | ------------------ expected `String` because of this LL | let x = foo(); - | ----- this can be annotated with `?` because it has type `Result` -LL | let one = x -LL | .map(|s| ()) - | ----------- this can be annotated with `?` because it has type `Result<(), String>` + | ----- this has type `Result<_, String>` +... LL | .map_err(|_| ())?; | ---------------^ the trait `From<()>` is not implemented for `String` | | - | this can't be annotated with `?` because it has type `Result<(), ()>` + | this can't be annotated with `?` because it has type `Result<_, ()>` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait = help: the following other types implement trait `From`: @@ -55,7 +49,7 @@ LL | .map_err(|_| ())?; = note: required for `Result<(), String>` to implement `FromResidual>` error[E0277]: `?` couldn't convert the error to `String` - --> $DIR/question-mark-result-err-mismatch.rs:47:11 + --> $DIR/question-mark-result-err-mismatch.rs:46:11 | LL | fn baz() -> Result { | ---------------------- expected `String` because of this @@ -66,7 +60,7 @@ LL | | "Couldn't split the test string"; LL | | })?; | | -^ the trait `From<()>` is not implemented for `String` | |__________| - | this can't be annotated with `?` because it has type `Result<&str, ()>` + | this can't be annotated with `?` because it has type `Result<_, ()>` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait = help: the following other types implement trait `From`: From 98e5317173679763bd6f25e3d925d63dd644baee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 7 Oct 2023 03:47:02 +0000 Subject: [PATCH 10/13] Detect incorrect `;` in `Option::ok_or_else` and `Result::map_err` Fix #72124. --- compiler/rustc_span/src/symbol.rs | 2 + .../error_reporting/type_err_ctxt_ext.rs | 70 ++++++++++++++++++- .../question-mark-result-err-mismatch.rs | 9 ++- .../question-mark-result-err-mismatch.stderr | 15 ++-- 4 files changed, 86 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d7e822382ef92..07fe3a2314768 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -973,6 +973,7 @@ symbols! { managed_boxes, manually_drop, map, + map_err, marker, marker_trait_attr, masked, @@ -1137,6 +1138,7 @@ symbols! { offset, offset_of, offset_of_enum, + ok_or_else, omit_gdb_pretty_printer_section, on, on_unimplemented, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index 58ea7a0edd521..5f522fc29eb28 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -41,7 +41,7 @@ use rustc_session::config::{DumpSolverProofTree, TraitSolver}; use rustc_session::Limit; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::sym; -use rustc_span::{BytePos, ExpnKind, Span, DUMMY_SP}; +use rustc_span::{BytePos, ExpnKind, Span, Symbol, DUMMY_SP}; use std::borrow::Cow; use std::fmt; use std::iter; @@ -1045,6 +1045,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { return; } let self_ty = trait_ref.self_ty(); + let found_ty = trait_ref.args.get(1).and_then(|a| a.as_type()); let mut prev_ty = self.resolve_vars_if_possible( typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), @@ -1070,17 +1071,80 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // The following logic is simlar to `point_at_chain`, but that's focused on associated types let mut expr = expr; - while let hir::ExprKind::MethodCall(_path_segment, rcvr_expr, _args, span) = expr.kind { + while let hir::ExprKind::MethodCall(path_segment, rcvr_expr, args, span) = expr.kind { // Point at every method call in the chain with the `Result` type. // let foo = bar.iter().map(mapper)?; // ------ ----------- expr = rcvr_expr; chain.push((span, prev_ty)); - prev_ty = self.resolve_vars_if_possible( + let next_ty = self.resolve_vars_if_possible( typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), ); + let is_diagnostic_item = |symbol: Symbol, ty: Ty<'tcx>| { + let ty::Adt(def, _) = ty.kind() else { + return false; + }; + self.tcx.is_diagnostic_item(symbol, def.did()) + }; + // For each method in the chain, see if this is `Result::map_err` or + // `Option::ok_or_else` and if it is, see if the closure passed to it has an incorrect + // trailing `;`. + if let Some(ty) = get_e_type(prev_ty) + && let Some(found_ty) = found_ty + // Ideally we would instead use `FnCtxt::lookup_method_for_diagnostic` for 100% + // accurate check, but we are in the wrong stage to do that and looking for + // `Result::map_err` by checking the Self type and the path segment is enough. + // sym::ok_or_else + && ( + ( // Result::map_err + path_segment.ident.name == sym::map_err + && is_diagnostic_item(sym::Result, next_ty) + ) || ( // Option::ok_or_else + path_segment.ident.name == sym::ok_or_else + && is_diagnostic_item(sym::Option, next_ty) + ) + ) + // Found `Result<_, ()>?` + && let ty::Tuple(tys) = found_ty.kind() + && tys.is_empty() + // The current method call returns `Result<_, ()>` + && self.can_eq(obligation.param_env, ty, found_ty) + // There's a single argument in the method call and it is a closure + && args.len() == 1 + && let Some(arg) = args.get(0) + && let hir::ExprKind::Closure(closure) = arg.kind + // The closure has a block for its body with no tail expression + && let body = self.tcx.hir().body(closure.body) + && let hir::ExprKind::Block(block, _) = body.value.kind + && let None = block.expr + // The last statement is of a type that can be converted to the return error type + && let [.., stmt] = block.stmts + && let hir::StmtKind::Semi(expr) = stmt.kind + && let expr_ty = self.resolve_vars_if_possible( + typeck.expr_ty_adjusted_opt(expr) + .unwrap_or(Ty::new_misc_error(self.tcx)), + ) + && self + .infcx + .type_implements_trait( + self.tcx.get_diagnostic_item(sym::From).unwrap(), + [self_ty, expr_ty], + obligation.param_env, + ) + .must_apply_modulo_regions() + { + err.span_suggestion_short( + stmt.span.with_lo(expr.span.hi()), + "remove this semicolon", + String::new(), + Applicability::MachineApplicable, + ); + } + + prev_ty = next_ty; + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind && let hir::Path { res: hir::def::Res::Local(hir_id), .. } = path && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(*hir_id) diff --git a/tests/ui/traits/question-mark-result-err-mismatch.rs b/tests/ui/traits/question-mark-result-err-mismatch.rs index e5ccca2e5f7c7..317029e004613 100644 --- a/tests/ui/traits/question-mark-result-err-mismatch.rs +++ b/tests/ui/traits/question-mark-result-err-mismatch.rs @@ -8,7 +8,9 @@ fn foo() -> Result { //~ NOTE expected `String` because of this }); let one = x .map(|s| ()) - .map_err(|_| ()) //~ NOTE this can't be annotated with `?` because it has type `Result<_, ()>` + .map_err(|e| { //~ NOTE this can't be annotated with `?` because it has type `Result<_, ()>` + e; //~ HELP remove this semicolon + }) .map(|()| "")?; //~ ERROR `?` couldn't convert the error to `String` //~^ NOTE in this expansion of desugaring of operator `?` //~| NOTE in this expansion of desugaring of operator `?` @@ -17,6 +19,7 @@ fn foo() -> Result { //~ NOTE expected `String` because of this //~| NOTE the trait `From<()>` is not implemented for `String` //~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait //~| NOTE required for `Result` to implement `FromResidual>` + //~| HELP the following other types implement trait `From`: Ok(one.to_string()) } @@ -33,6 +36,7 @@ fn bar() -> Result<(), String> { //~ NOTE expected `String` because of this //~| NOTE the trait `From<()>` is not implemented for `String` //~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait //~| NOTE required for `Result<(), String>` to implement `FromResidual>` + //~| HELP the following other types implement trait `From`: Ok(one) } @@ -42,7 +46,7 @@ fn baz() -> Result { //~ NOTE expected `String` because of this .split_whitespace() .next() .ok_or_else(|| { //~ NOTE this can't be annotated with `?` because it has type `Result<_, ()>` - "Couldn't split the test string"; + "Couldn't split the test string"; //~ HELP remove this semicolon })?; //~^ ERROR `?` couldn't convert the error to `String` //~| NOTE in this expansion of desugaring of operator `?` @@ -52,6 +56,7 @@ fn baz() -> Result { //~ NOTE expected `String` because of this //~| NOTE the trait `From<()>` is not implemented for `String` //~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait //~| NOTE required for `Result` to implement `FromResidual>` + //~| HELP the following other types implement trait `From`: Ok(one.to_string()) } diff --git a/tests/ui/traits/question-mark-result-err-mismatch.stderr b/tests/ui/traits/question-mark-result-err-mismatch.stderr index fc3b2e6b46b38..1f9495a505ac3 100644 --- a/tests/ui/traits/question-mark-result-err-mismatch.stderr +++ b/tests/ui/traits/question-mark-result-err-mismatch.stderr @@ -1,5 +1,5 @@ error[E0277]: `?` couldn't convert the error to `String` - --> $DIR/question-mark-result-err-mismatch.rs:12:22 + --> $DIR/question-mark-result-err-mismatch.rs:14:22 | LL | fn foo() -> Result { | ---------------------- expected `String` because of this @@ -10,8 +10,12 @@ LL | | "Couldn't split the test string" LL | | }); | |__________- this has type `Result<_, &str>` ... -LL | .map_err(|_| ()) - | --------------- this can't be annotated with `?` because it has type `Result<_, ()>` +LL | .map_err(|e| { + | __________- +LL | | e; + | | - help: remove this semicolon +LL | | }) + | |__________- this can't be annotated with `?` because it has type `Result<_, ()>` LL | .map(|()| "")?; | ^ the trait `From<()>` is not implemented for `String` | @@ -26,7 +30,7 @@ LL | .map(|()| "")?; = note: required for `Result` to implement `FromResidual>` error[E0277]: `?` couldn't convert the error to `String` - --> $DIR/question-mark-result-err-mismatch.rs:27:25 + --> $DIR/question-mark-result-err-mismatch.rs:30:25 | LL | fn bar() -> Result<(), String> { | ------------------ expected `String` because of this @@ -49,7 +53,7 @@ LL | .map_err(|_| ())?; = note: required for `Result<(), String>` to implement `FromResidual>` error[E0277]: `?` couldn't convert the error to `String` - --> $DIR/question-mark-result-err-mismatch.rs:46:11 + --> $DIR/question-mark-result-err-mismatch.rs:50:11 | LL | fn baz() -> Result { | ---------------------- expected `String` because of this @@ -57,6 +61,7 @@ LL | fn baz() -> Result { LL | .ok_or_else(|| { | __________- LL | | "Couldn't split the test string"; + | | - help: remove this semicolon LL | | })?; | | -^ the trait `From<()>` is not implemented for `String` | |__________| From 70fe624b3d82aee2d32908f3fe2216002a207846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 7 Oct 2023 03:58:45 +0000 Subject: [PATCH 11/13] Reduce verbosity of error --- .../error_reporting/type_err_ctxt_ext.rs | 37 +++++++++++-------- .../question-mark-result-err-mismatch.rs | 6 +-- .../question-mark-result-err-mismatch.stderr | 24 +----------- 3 files changed, 24 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index 5f522fc29eb28..a779d69943ddf 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -112,7 +112,7 @@ pub trait TypeErrCtxtExt<'tcx> { obligation: &PredicateObligation<'tcx>, trait_ref: ty::TraitRef<'tcx>, err: &mut Diagnostic, - ); + ) -> bool; fn report_const_param_not_wf( &self, @@ -517,8 +517,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut err = struct_span_err!(self.tcx.sess, span, E0277, "{}", err_msg); + let mut suggested = false; if is_try_conversion { - self.try_conversion_context(&obligation, trait_ref.skip_binder(), &mut err); + suggested = self.try_conversion_context(&obligation, trait_ref.skip_binder(), &mut err); } if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) { @@ -621,8 +622,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref); self.suggest_dereferencing_index(&obligation, &mut err, trait_predicate); - let mut suggested = - self.suggest_dereferences(&obligation, &mut err, trait_predicate); + suggested |= self.suggest_dereferences(&obligation, &mut err, trait_predicate); suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate); let impl_candidates = self.find_similar_impl_candidates(trait_predicate); suggested = if let &[cand] = &impl_candidates[..] { @@ -1002,7 +1002,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { obligation: &PredicateObligation<'tcx>, trait_ref: ty::TraitRef<'tcx>, err: &mut Diagnostic, - ) { + ) -> bool { let span = obligation.cause.span; struct V<'v> { search_span: Span, @@ -1027,22 +1027,22 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) => { body_id } - _ => return, + _ => return false, }; let mut v = V { search_span: span, found: None }; v.visit_body(self.tcx.hir().body(*body_id)); let Some(expr) = v.found else { - return; + return false; }; let Some(typeck) = &self.typeck_results else { - return; + return false; }; let Some((ObligationCauseCode::QuestionMark, Some(y))) = obligation.cause.code().parent() else { - return; + return false; }; if !self.tcx.is_diagnostic_item(sym::FromResidual, y.def_id()) { - return; + return false; } let self_ty = trait_ref.self_ty(); let found_ty = trait_ref.args.get(1).and_then(|a| a.as_type()); @@ -1067,6 +1067,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { Some(arg.as_type()?) }; + let mut suggested = false; let mut chain = vec![]; // The following logic is simlar to `point_at_chain`, but that's focused on associated types @@ -1135,6 +1136,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) .must_apply_modulo_regions() { + suggested = true; err.span_suggestion_short( stmt.span.with_lo(expr.span.hi()), "remove this semicolon", @@ -1193,17 +1195,20 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) .must_apply_modulo_regions() { - err.span_label(span, format!("this has type `Result<_, {err_ty}>`")); + if !suggested { + err.span_label(span, format!("this has type `Result<_, {err_ty}>`")); + } } else { err.span_label( - span, - format!( - "this can't be annotated with `?` because it has type `Result<_, {err_ty}>`", - ), - ); + span, + format!( + "this can't be annotated with `?` because it has type `Result<_, {err_ty}>`", + ), + ); } prev = Some(err_ty); } + suggested } fn report_const_param_not_wf( diff --git a/tests/ui/traits/question-mark-result-err-mismatch.rs b/tests/ui/traits/question-mark-result-err-mismatch.rs index 317029e004613..0ca18b5b0ddce 100644 --- a/tests/ui/traits/question-mark-result-err-mismatch.rs +++ b/tests/ui/traits/question-mark-result-err-mismatch.rs @@ -3,7 +3,7 @@ fn foo() -> Result { //~ NOTE expected `String` because of this let x = test .split_whitespace() .next() - .ok_or_else(|| { //~ NOTE this has type `Result<_, &str>` + .ok_or_else(|| { "Couldn't split the test string" }); let one = x @@ -15,11 +15,9 @@ fn foo() -> Result { //~ NOTE expected `String` because of this //~^ NOTE in this expansion of desugaring of operator `?` //~| NOTE in this expansion of desugaring of operator `?` //~| NOTE in this expansion of desugaring of operator `?` - //~| NOTE in this expansion of desugaring of operator `?` //~| NOTE the trait `From<()>` is not implemented for `String` //~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait //~| NOTE required for `Result` to implement `FromResidual>` - //~| HELP the following other types implement trait `From`: Ok(one.to_string()) } @@ -52,11 +50,9 @@ fn baz() -> Result { //~ NOTE expected `String` because of this //~| NOTE in this expansion of desugaring of operator `?` //~| NOTE in this expansion of desugaring of operator `?` //~| NOTE in this expansion of desugaring of operator `?` - //~| NOTE in this expansion of desugaring of operator `?` //~| NOTE the trait `From<()>` is not implemented for `String` //~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait //~| NOTE required for `Result` to implement `FromResidual>` - //~| HELP the following other types implement trait `From`: Ok(one.to_string()) } diff --git a/tests/ui/traits/question-mark-result-err-mismatch.stderr b/tests/ui/traits/question-mark-result-err-mismatch.stderr index 1f9495a505ac3..3059e0beca3e4 100644 --- a/tests/ui/traits/question-mark-result-err-mismatch.stderr +++ b/tests/ui/traits/question-mark-result-err-mismatch.stderr @@ -4,12 +4,6 @@ error[E0277]: `?` couldn't convert the error to `String` LL | fn foo() -> Result { | ---------------------- expected `String` because of this ... -LL | .ok_or_else(|| { - | __________- -LL | | "Couldn't split the test string" -LL | | }); - | |__________- this has type `Result<_, &str>` -... LL | .map_err(|e| { | __________- LL | | e; @@ -20,17 +14,10 @@ LL | .map(|()| "")?; | ^ the trait `From<()>` is not implemented for `String` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait - = help: the following other types implement trait `From`: - > - >> - >> - > - > - > = note: required for `Result` to implement `FromResidual>` error[E0277]: `?` couldn't convert the error to `String` - --> $DIR/question-mark-result-err-mismatch.rs:30:25 + --> $DIR/question-mark-result-err-mismatch.rs:28:25 | LL | fn bar() -> Result<(), String> { | ------------------ expected `String` because of this @@ -53,7 +40,7 @@ LL | .map_err(|_| ())?; = note: required for `Result<(), String>` to implement `FromResidual>` error[E0277]: `?` couldn't convert the error to `String` - --> $DIR/question-mark-result-err-mismatch.rs:50:11 + --> $DIR/question-mark-result-err-mismatch.rs:48:11 | LL | fn baz() -> Result { | ---------------------- expected `String` because of this @@ -68,13 +55,6 @@ LL | | })?; | this can't be annotated with `?` because it has type `Result<_, ()>` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait - = help: the following other types implement trait `From`: - > - >> - >> - > - > - > = note: required for `Result` to implement `FromResidual>` error: aborting due to 3 previous errors From dc0f7a1208ad3e86b00cd8ee6aabffd8fd1d52b0 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 5 Dec 2023 22:48:06 -0700 Subject: [PATCH 12/13] docs: remove #110800 from release notes It's not stable yet, and shouldn't be mentioned here. --- RELEASES.md | 1 - 1 file changed, 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 5bc302305c71c..9eb8755dad99a 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -89,7 +89,6 @@ Rustdoc ------- - [Add warning block support in rustdoc](https://github.com/rust-lang/rust/pull/106561/) -- [Accept additional user-defined syntax classes in fenced code blocks](https://github.com/rust-lang/rust/pull/110800/) - [rustdoc-search: add support for type parameters](https://github.com/rust-lang/rust/pull/112725/) - [rustdoc: show inner enum and struct in type definition for concrete type](https://github.com/rust-lang/rust/pull/114855/) From 7ff9648808921746dcd08c050c7ff8918c3551b9 Mon Sep 17 00:00:00 2001 From: klensy Date: Wed, 6 Dec 2023 13:45:46 +0300 Subject: [PATCH 13/13] library: fix comment about const assert in win api --- library/std/src/sys/windows/api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/windows/api.rs b/library/std/src/sys/windows/api.rs index 72ecb9503410e..a7ea59e85f786 100644 --- a/library/std/src/sys/windows/api.rs +++ b/library/std/src/sys/windows/api.rs @@ -48,7 +48,7 @@ use super::c; /// converted to a `u32`. Clippy would warn about this but, alas, it's not run /// on the standard library. const fn win32_size_of() -> u32 { - // Const assert that the size is less than u32::MAX. + // Const assert that the size does not exceed u32::MAX. // Uses a trait to workaround restriction on using generic types in inner items. trait Win32SizeOf: Sized { const WIN32_SIZE_OF: u32 = {