diff options
492 files changed, 8535 insertions, 3517 deletions
diff --git a/Cargo.lock b/Cargo.lock index 17856e1f52a..b1bdaef81ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3670,6 +3670,7 @@ dependencies = [ "rustc_metadata", "rustc_middle", "rustc_query_system", + "rustc_sanitizers", "rustc_session", "rustc_span", "rustc_symbol_mangling", @@ -4559,6 +4560,21 @@ dependencies = [ ] [[package]] +name = "rustc_sanitizers" +version = "0.0.0" +dependencies = [ + "bitflags 2.5.0", + "rustc_data_structures", + "rustc_hir", + "rustc_middle", + "rustc_span", + "rustc_target", + "rustc_trait_selection", + "tracing", + "twox-hash", +] + +[[package]] name = "rustc_serialize" version = "0.0.0" dependencies = [ @@ -4633,7 +4649,6 @@ dependencies = [ name = "rustc_symbol_mangling" version = "0.0.0" dependencies = [ - "bitflags 2.5.0", "punycode", "rustc-demangle", "rustc_data_structures", @@ -4643,9 +4658,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "rustc_trait_selection", "tracing", - "twox-hash", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e12c968e205..bbf4ecfe61d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,6 +96,7 @@ adler.debug = 0 gimli.debug = 0 miniz_oxide.debug = 0 object.debug = 0 +rustc-demangle.debug = 0 # These are very thin wrappers around executing lld with the right binary name. # Basically nothing within them can go wrong without having been explicitly logged anyway. diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index e29ef591bcb..63ce6685e43 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2152,6 +2152,9 @@ pub enum TyKind { MacCall(P<MacCall>), /// Placeholder for a `va_list`. CVarArgs, + /// Pattern types like `pattern_type!(u32 is 1..=)`, which is the same as `NonZeroU32`, + /// just as part of the type system. + Pat(P<Ty>, P<Pat>), /// Sometimes we need a dummy value when no error has occurred. Dummy, /// Placeholder for a kind that has failed to be defined. diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 7337b969242..da57def263d 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -502,6 +502,10 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) { } TyKind::Tup(tys) => visit_thin_vec(tys, |ty| vis.visit_ty(ty)), TyKind::Paren(ty) => vis.visit_ty(ty), + TyKind::Pat(ty, pat) => { + vis.visit_ty(ty); + vis.visit_pat(pat); + } TyKind::Path(qself, path) => { vis.visit_qself(qself); vis.visit_path(path); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 18986fb7504..9e9ae52962d 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -446,6 +446,10 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result { } try_visit!(visitor.visit_path(path, typ.id)); } + TyKind::Pat(ty, pat) => { + try_visit!(visitor.visit_ty(ty)); + try_visit!(visitor.visit_pat(pat)); + } TyKind::Array(ty, length) => { try_visit!(visitor.visit_ty(ty)); try_visit!(visitor.visit_anon_const(length)); diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 1c34fd0afbb..4c552289a81 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -381,4 +381,8 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { ArrayLen::Body(..) => intravisit::walk_array_len(self, len), } } + + fn visit_pattern_type_pattern(&mut self, p: &'hir hir::Pat<'hir>) { + self.visit_pat(p) + } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 8cf347bfa96..5005c22d4cc 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1463,7 +1463,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } } - TyKind::MacCall(_) => panic!("`TyKind::MacCall` should have been expanded by now"), + TyKind::Pat(ty, pat) => hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_pat(pat)), + TyKind::MacCall(_) => { + span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now") + } TyKind::CVarArgs => { let guar = self.dcx().span_delayed_bug( t.span, diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 5912dd3f931..d7cd3efe408 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -332,6 +332,9 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { ast::TyKind::Never => { gate!(&self, never_type, ty.span, "the `!` type is experimental"); } + ast::TyKind::Pat(..) => { + gate!(&self, pattern_types, ty.span, "pattern types are unstable"); + } _ => {} } visit::walk_ty(self, ty) diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 3ea182c5867..51ccfe89fbd 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1188,6 +1188,11 @@ impl<'a> State<'a> { ast::TyKind::CVarArgs => { self.word("..."); } + ast::TyKind::Pat(ty, pat) => { + self.print_type(ty); + self.word(" is "); + self.print_pat(pat); + } } self.end(); } diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs index 31307ef1410..64726eacca7 100644 --- a/compiler/rustc_borrowck/src/consumers.rs +++ b/compiler/rustc_borrowck/src/consumers.rs @@ -4,7 +4,6 @@ use rustc_hir::def_id::LocalDefId; use rustc_index::{IndexSlice, IndexVec}; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::{Body, Promoted}; -use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::TyCtxt; use std::rc::Rc; @@ -106,7 +105,7 @@ pub fn get_body_with_borrowck_facts( options: ConsumerOptions, ) -> BodyWithBorrowckFacts<'_> { let (input_body, promoted) = tcx.mir_promoted(def); - let infcx = tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::bind(tcx, def)).build(); + let infcx = tcx.infer_ctxt().with_opaque_type_inference(def).build(); let input_body: &Body<'_> = &input_body.borrow(); let promoted: &IndexSlice<_, _> = &promoted.borrow(); *super::do_mir_borrowck(&infcx, input_body, promoted, Some(options)).1.unwrap() diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 4a5ba441878..7fbf4c47ec8 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -32,7 +32,6 @@ use rustc_infer::infer::{ use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::*; use rustc_middle::query::Providers; -use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt}; use rustc_session::lint::builtin::UNUSED_MUT; use rustc_span::{Span, Symbol}; @@ -126,7 +125,7 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> { return tcx.arena.alloc(result); } - let infcx = tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::bind(tcx, def)).build(); + let infcx = tcx.infer_ctxt().with_opaque_type_inference(def).build(); let promoted: &IndexSlice<_, _> = &promoted.borrow(); let opt_closure_req = do_mir_borrowck(&infcx, input_body, promoted, None).0; debug!("mir_borrowck done"); @@ -1606,6 +1605,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | ty::Foreign(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) | ty::FnDef(_, _) | ty::FnPtr(_) @@ -1648,6 +1648,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | ty::Foreign(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 63b80445817..9f0e54febe4 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -7,7 +7,6 @@ use rustc_infer::infer::TyCtxtInferExt as _; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_macros::extension; -use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable}; use rustc_middle::ty::{GenericArgKind, GenericArgs}; @@ -133,6 +132,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { let ty = infcx.infer_opaque_definition_from_instantiation(opaque_type_key, concrete_type); + + // Sometimes, when the hidden type is an inference variable, it can happen that + // the hidden type becomes the opaque type itself. In this case, this was an opaque + // usage of the opaque type and we can ignore it. This check is mirrored in typeck's + // writeback. + // FIXME(-Znext-solver): This should be unnecessary with the new solver. + if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() + && alias_ty.def_id == opaque_type_key.def_id.to_def_id() + && alias_ty.args == opaque_type_key.args + { + continue; + } // Sometimes two opaque types are the same only after we remap the generic parameters // back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to `(X, Y)` // and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we only know that @@ -321,13 +332,13 @@ fn check_opaque_type_well_formed<'tcx>( parent_def_id = tcx.local_parent(parent_def_id); } - // FIXME(-Znext-solver): We probably should use `DefiningAnchor::Bind(&[])` + // FIXME(-Znext-solver): We probably should use `&[]` instead of // and prepopulate this `InferCtxt` with known opaque values, rather than - // using the `Bind` anchor here. For now it's fine. + // allowing opaque types to be defined and checking them after the fact. let infcx = tcx .infer_ctxt() .with_next_trait_solver(next_trait_solver) - .with_opaque_type_inference(DefiningAnchor::bind(tcx, parent_def_id)) + .with_opaque_type_inference(parent_def_id) .build(); let ocx = ObligationCtxt::new(&infcx); let identity_args = GenericArgs::identity_for_item(tcx, def_id); diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index f28b786e4f7..a950f10787b 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -39,6 +39,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let TypeOpOutput { output, constraints, error_info } = op.fully_perform(self.infcx, locations.span(self.body))?; + if cfg!(debug_assertions) { + let data = self.infcx.take_and_reset_region_constraints(); + if !data.is_empty() { + panic!("leftover region constraints: {data:#?}"); + } + } debug!(?output, ?constraints); diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 554dac0852f..1b4c6041294 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -46,6 +46,7 @@ mod format; mod format_foreign; mod global_allocator; mod log_syntax; +mod pattern_type; mod source_util; mod test; mod trace_macros; @@ -95,6 +96,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { log_syntax: log_syntax::expand_log_syntax, module_path: source_util::expand_mod, option_env: env::expand_option_env, + pattern_type: pattern_type::expand, std_panic: edition_panic::expand_panic, stringify: source_util::expand_stringify, trace_macros: trace_macros::expand_trace_macros, diff --git a/compiler/rustc_builtin_macros/src/pattern_type.rs b/compiler/rustc_builtin_macros/src/pattern_type.rs new file mode 100644 index 00000000000..54039c2c538 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/pattern_type.rs @@ -0,0 +1,29 @@ +use rustc_ast::{ast, ptr::P, tokenstream::TokenStream, Pat, Ty}; +use rustc_errors::PResult; +use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; +use rustc_span::{sym, Span}; + +pub fn expand<'cx>( + cx: &'cx mut ExtCtxt<'_>, + sp: Span, + tts: TokenStream, +) -> MacroExpanderResult<'cx> { + let (ty, pat) = match parse_pat_ty(cx, tts) { + Ok(parsed) => parsed, + Err(err) => { + return ExpandResult::Ready(DummyResult::any(sp, err.emit())); + } + }; + + ExpandResult::Ready(base::MacEager::ty(cx.ty(sp, ast::TyKind::Pat(ty, pat)))) +} + +fn parse_pat_ty<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P<Ty>, P<Pat>)> { + let mut parser = cx.new_parser_from_tts(stream); + + let ty = parser.parse_ty()?; + parser.eat_keyword(sym::is); + let pat = parser.parse_pat_no_top_alt(None, None)?; + + Ok((ty, pat)) +} diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 1307a62a60d..771e5b21958 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -267,7 +267,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { .generic_activity("codegen prelude") .run(|| crate::abi::codegen_fn_prelude(fx, start_block)); - for (bb, bb_data) in fx.mir.basic_blocks.iter_enumerated() { + for (bb, bb_data) in traversal::mono_reachable(fx.mir, fx.tcx, fx.instance) { let block = fx.get_block(bb); fx.bcx.switch_to_block(block); diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 75268341a4f..e8c96486041 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -341,6 +341,8 @@ fn emit_cgu( object: Some(global_asm_object_file), dwarf_object: None, bytecode: None, + assembly: None, + llvm_ir: None, }), existing_work_product: None, }) @@ -378,7 +380,15 @@ fn emit_module( prof.artifact_size("object_file", &*name, file.metadata().unwrap().len()); - Ok(CompiledModule { name, kind, object: Some(tmp_file), dwarf_object: None, bytecode: None }) + Ok(CompiledModule { + name, + kind, + object: Some(tmp_file), + dwarf_object: None, + bytecode: None, + assembly: None, + llvm_ir: None, + }) } fn reuse_workproduct_for_cgu( @@ -426,6 +436,8 @@ fn reuse_workproduct_for_cgu( object: Some(obj_out_regular), dwarf_object: None, bytecode: None, + assembly: None, + llvm_ir: None, }, module_global_asm: has_global_asm.then(|| CompiledModule { name: cgu.name().to_string(), @@ -433,6 +445,8 @@ fn reuse_workproduct_for_cgu( object: Some(obj_out_global_asm), dwarf_object: None, bytecode: None, + assembly: None, + llvm_ir: None, }), existing_work_product: Some((cgu.work_product_id(), work_product)), }) @@ -678,6 +692,8 @@ pub(crate) fn run_aot( object: Some(tmp_file), dwarf_object: None, bytecode: None, + assembly: None, + llvm_ir: None, }) } else { None diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs index 76a619a1af7..3ea5be1ee56 100644 --- a/compiler/rustc_codegen_gcc/src/back/write.rs +++ b/compiler/rustc_codegen_gcc/src/back/write.rs @@ -158,6 +158,8 @@ pub(crate) unsafe fn codegen( config.emit_obj != EmitObj::None, cgcx.target_can_use_split_dwarf && cgcx.split_debuginfo == SplitDebuginfo::Unpacked, config.emit_bc, + config.emit_asm, + config.emit_ir, &cgcx.output_filenames, )) } diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 3fda59e8b52..bb5045ec872 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -28,6 +28,7 @@ rustc_macros = { path = "../rustc_macros" } rustc_metadata = { path = "../rustc_metadata" } rustc_middle = { path = "../rustc_middle" } rustc_query_system = { path = "../rustc_query_system" } +rustc_sanitizers = { path = "../rustc_sanitizers" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index d2828669d43..f918facc86d 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -150,7 +150,10 @@ impl LlvmType for CastTarget { // Simplify to a single unit or an array if there's no prefix. // This produces the same layout, but using a simpler type. if self.prefix.iter().all(|x| x.is_none()) { - if rest_count == 1 { + // We can't do this if is_consecutive is set and the unit would get + // split on the target. Currently, this is only relevant for i128 + // registers. + if rest_count == 1 && (!self.rest.is_consecutive || self.rest.unit != Reg::i128()) { return rest_ll_unit; } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 68ba8cbf7b7..49f9d7ddab6 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -880,6 +880,8 @@ pub(crate) unsafe fn codegen( config.emit_obj != EmitObj::None, dwarf_object_emitted, config.emit_bc, + config.emit_asm, + config.emit_ir, &cgcx.output_filenames, )) } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 1a32958d362..b7235972204 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -20,12 +20,9 @@ use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout, }; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_sanitizers::{cfi, kcfi}; use rustc_session::config::OptLevel; use rustc_span::Span; -use rustc_symbol_mangling::typeid::{ - kcfi_typeid_for_fnabi, kcfi_typeid_for_instance, typeid_for_fnabi, typeid_for_instance, - TypeIdOptions, -}; use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange}; use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target}; use smallvec::SmallVec; @@ -1632,18 +1629,18 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { return; } - let mut options = TypeIdOptions::empty(); + let mut options = cfi::TypeIdOptions::empty(); if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() { - options.insert(TypeIdOptions::GENERALIZE_POINTERS); + options.insert(cfi::TypeIdOptions::GENERALIZE_POINTERS); } if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() { - options.insert(TypeIdOptions::NORMALIZE_INTEGERS); + options.insert(cfi::TypeIdOptions::NORMALIZE_INTEGERS); } let typeid = if let Some(instance) = instance { - typeid_for_instance(self.tcx, instance, options) + cfi::typeid_for_instance(self.tcx, instance, options) } else { - typeid_for_fnabi(self.tcx, fn_abi, options) + cfi::typeid_for_fnabi(self.tcx, fn_abi, options) }; let typeid_metadata = self.cx.typeid_metadata(typeid).unwrap(); @@ -1680,18 +1677,18 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { return None; } - let mut options = TypeIdOptions::empty(); + let mut options = kcfi::TypeIdOptions::empty(); if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() { - options.insert(TypeIdOptions::GENERALIZE_POINTERS); + options.insert(kcfi::TypeIdOptions::GENERALIZE_POINTERS); } if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() { - options.insert(TypeIdOptions::NORMALIZE_INTEGERS); + options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS); } let kcfi_typeid = if let Some(instance) = instance { - kcfi_typeid_for_instance(self.tcx, instance, options) + kcfi::typeid_for_instance(self.tcx, instance, options) } else { - kcfi_typeid_for_fnabi(self.tcx, fn_abi, options) + kcfi::typeid_for_fnabi(self.tcx, fn_abi, options) }; Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)])) diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index f86cdcaa6f7..7117c4a0ed9 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -22,10 +22,7 @@ use itertools::Itertools; use rustc_codegen_ssa::traits::TypeMembershipMethods; use rustc_data_structures::fx::FxIndexSet; use rustc_middle::ty::{Instance, Ty}; -use rustc_symbol_mangling::typeid::{ - kcfi_typeid_for_fnabi, kcfi_typeid_for_instance, typeid_for_fnabi, typeid_for_instance, - TypeIdOptions, -}; +use rustc_sanitizers::{cfi, kcfi}; use smallvec::SmallVec; /// Declare a function. @@ -145,27 +142,29 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { if let Some(instance) = instance { let mut typeids = FxIndexSet::default(); for options in [ - TypeIdOptions::GENERALIZE_POINTERS, - TypeIdOptions::NORMALIZE_INTEGERS, - TypeIdOptions::USE_CONCRETE_SELF, + cfi::TypeIdOptions::GENERALIZE_POINTERS, + cfi::TypeIdOptions::NORMALIZE_INTEGERS, + cfi::TypeIdOptions::USE_CONCRETE_SELF, ] .into_iter() .powerset() - .map(TypeIdOptions::from_iter) + .map(cfi::TypeIdOptions::from_iter) { - let typeid = typeid_for_instance(self.tcx, instance, options); + let typeid = cfi::typeid_for_instance(self.tcx, instance, options); if typeids.insert(typeid.clone()) { self.add_type_metadata(llfn, typeid); } } } else { - for options in - [TypeIdOptions::GENERALIZE_POINTERS, TypeIdOptions::NORMALIZE_INTEGERS] - .into_iter() - .powerset() - .map(TypeIdOptions::from_iter) + for options in [ + cfi::TypeIdOptions::GENERALIZE_POINTERS, + cfi::TypeIdOptions::NORMALIZE_INTEGERS, + ] + .into_iter() + .powerset() + .map(cfi::TypeIdOptions::from_iter) { - let typeid = typeid_for_fnabi(self.tcx, fn_abi, options); + let typeid = cfi::typeid_for_fnabi(self.tcx, fn_abi, options); self.add_type_metadata(llfn, typeid); } } @@ -173,19 +172,19 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { if self.tcx.sess.is_sanitizer_kcfi_enabled() { // LLVM KCFI does not support multiple !kcfi_type attachments - let mut options = TypeIdOptions::empty(); + let mut options = kcfi::TypeIdOptions::empty(); if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() { - options.insert(TypeIdOptions::GENERALIZE_POINTERS); + options.insert(kcfi::TypeIdOptions::GENERALIZE_POINTERS); } if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() { - options.insert(TypeIdOptions::NORMALIZE_INTEGERS); + options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS); } if let Some(instance) = instance { - let kcfi_typeid = kcfi_typeid_for_instance(self.tcx, instance, options); + let kcfi_typeid = kcfi::typeid_for_instance(self.tcx, instance, options); self.set_kcfi_type_metadata(llfn, kcfi_typeid); } else { - let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi, options); + let kcfi_typeid = kcfi::typeid_for_fnabi(self.tcx, fn_abi, options); self.set_kcfi_type_metadata(llfn, kcfi_typeid); } } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index b7bcaac3b18..e7f692144ff 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -528,12 +528,20 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( for module in compiled_modules.modules.iter().filter(|m| m.kind == ModuleKind::Regular) { let mut files = Vec::new(); if let Some(object_file_path) = &module.object { - files.push(("o", object_file_path.as_path())); + files.push((OutputType::Object.extension(), object_file_path.as_path())); } if let Some(dwarf_object_file_path) = &module.dwarf_object { files.push(("dwo", dwarf_object_file_path.as_path())); } - + if let Some(path) = &module.assembly { + files.push((OutputType::Assembly.extension(), path.as_path())); + } + if let Some(path) = &module.llvm_ir { + files.push((OutputType::LlvmAssembly.extension(), path.as_path())); + } + if let Some(path) = &module.bytecode { + files.push((OutputType::Bitcode.extension(), path.as_path())); + } if let Some((id, product)) = copy_cgu_workproduct_to_incr_comp_cache_dir(sess, &module.name, files.as_slice()) { @@ -937,12 +945,28 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>( load_from_incr_comp_dir(dwarf_obj_out, saved_dwarf_object_file) }); + let load_from_incr_cache = |perform, output_type: OutputType| { + if perform { + let saved_file = module.source.saved_files.get(output_type.extension())?; + let output_path = cgcx.output_filenames.temp_path(output_type, Some(&module.name)); + load_from_incr_comp_dir(output_path, &saved_file) + } else { + None + } + }; + + let assembly = load_from_incr_cache(module_config.emit_asm, OutputType::Assembly); + let llvm_ir = load_from_incr_cache(module_config.emit_ir, OutputType::LlvmAssembly); + let bytecode = load_from_incr_cache(module_config.emit_bc, OutputType::Bitcode); + WorkItemResult::Finished(CompiledModule { name: module.name, kind: ModuleKind::Regular, object, dwarf_object, - bytecode: None, + bytecode, + assembly, + llvm_ir, }) } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 410b5d27c57..c4c16ee7311 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -656,6 +656,8 @@ pub fn codegen_crate<B: ExtraBackendMethods>( object: Some(file_name), dwarf_object: None, bytecode: None, + assembly: None, + llvm_ir: None, } }) }); diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 64448441acb..5f0dcf9510f 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -202,6 +202,16 @@ fn push_debuginfo_type_name<'tcx>( } } } + ty::Pat(inner_type, pat) => { + if cpp_like_debuginfo { + output.push_str("pat$<"); + push_debuginfo_type_name(tcx, inner_type, true, output, visited); + // FIXME(wg-debugging): implement CPP like printing for patterns. + write!(output, ",{:?}>", pat).unwrap(); + } else { + write!(output, "{:?}", t).unwrap(); + } + } ty::Slice(inner_type) => { if cpp_like_debuginfo { output.push_str("slice2$<"); diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 9be8dcf166d..80fe7e0bb78 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -79,13 +79,26 @@ impl<M> ModuleCodegen<M> { emit_obj: bool, emit_dwarf_obj: bool, emit_bc: bool, + emit_asm: bool, + emit_ir: bool, outputs: &OutputFilenames, ) -> CompiledModule { let object = emit_obj.then(|| outputs.temp_path(OutputType::Object, Some(&self.name))); let dwarf_object = emit_dwarf_obj.then(|| outputs.temp_path_dwo(Some(&self.name))); let bytecode = emit_bc.then(|| outputs.temp_path(OutputType::Bitcode, Some(&self.name))); + let assembly = emit_asm.then(|| outputs.temp_path(OutputType::Assembly, Some(&self.name))); + let llvm_ir = + emit_ir.then(|| outputs.temp_path(OutputType::LlvmAssembly, Some(&self.name))); - CompiledModule { name: self.name.clone(), kind: self.kind, object, dwarf_object, bytecode } + CompiledModule { + name: self.name.clone(), + kind: self.kind, + object, + dwarf_object, + bytecode, + assembly, + llvm_ir, + } } } @@ -96,6 +109,8 @@ pub struct CompiledModule { pub object: Option<PathBuf>, pub dwarf_object: Option<PathBuf>, pub bytecode: Option<PathBuf>, + pub assembly: Option<PathBuf>, // --emit=asm + pub llvm_ir: Option<PathBuf>, // --emit=llvm-ir, llvm-bc is in bytecode } pub struct CachedModuleCodegen { diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 387a5366b20..1f4473d2ec4 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -257,20 +257,19 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // Apply debuginfo to the newly allocated locals. fx.debug_introduce_locals(&mut start_bx); - let reachable_blocks = mir.reachable_blocks_in_mono(cx.tcx(), instance); - // The builders will be created separately for each basic block at `codegen_block`. // So drop the builder of `start_llbb` to avoid having two at the same time. drop(start_bx); + let reachable_blocks = traversal::mono_reachable_as_bitset(mir, cx.tcx(), instance); + // Codegen the body of each block using reverse postorder for (bb, _) in traversal::reverse_postorder(mir) { if reachable_blocks.contains(bb) { fx.codegen_block(bb); } else { - // This may have references to things we didn't monomorphize, so we - // don't actually codegen the body. We still create the block so - // terminators in other blocks can reference it without worry. + // We want to skip this block, because it's not reachable. But we still create + // the block so terminators in other blocks can reference it. fx.codegen_block_as_unreachable(bb); } } diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index d91ad3fcab1..dcfce4e35e0 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -1,3 +1,4 @@ +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_middle::mir; use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId}; use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout}; @@ -98,6 +99,16 @@ fn const_to_valtree_inner<'tcx>( Ok(ty::ValTree::Leaf(val.assert_int())) } + ty::Pat(base, ..) => { + let mut place = place.clone(); + // The valtree of the base type is the same as the valtree of the pattern type. + // Since the returned valtree does not contain the type or layout, we can just + // switch to the base type. + place.layout = ecx.layout_of(*base).unwrap(); + ensure_sufficient_stack(|| const_to_valtree_inner(ecx, &place, num_nodes)) + }, + + ty::RawPtr(_, _) => { // Not all raw pointers are allowed, as we cannot properly test them for // equality at compile-time (see `ptr_guaranteed_cmp`). @@ -273,7 +284,7 @@ pub fn valtree_to_const_value<'tcx>( let (param_env, ty) = param_env_ty.into_parts(); - match ty.kind() { + match *ty.kind() { ty::FnDef(..) => { assert!(valtree.unwrap_branch().is_empty()); mir::ConstValue::ZeroSized @@ -286,10 +297,11 @@ pub fn valtree_to_const_value<'tcx>( ), } } + ty::Pat(ty, _) => valtree_to_const_value(tcx, param_env.and(ty), valtree), ty::Ref(_, inner_ty, _) => { let mut ecx = mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No); - let imm = valtree_to_ref(&mut ecx, valtree, *inner_ty); + let imm = valtree_to_ref(&mut ecx, valtree, inner_ty); let imm = ImmTy::from_immediate(imm, tcx.layout_of(param_env_ty).unwrap()); op_to_const(&ecx, &imm.into(), /* for diagnostics */ false) } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 3283bcc4c45..62d169db628 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -1060,6 +1060,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ty::Tuple(tys) => tys.last().iter().all(|ty| is_very_trivially_sized(**ty)), + ty::Pat(ty, ..) => is_very_trivially_sized(*ty), + // We don't want to do any queries, so there is not much we can do with ADTs. ty::Adt(..) => false, diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index a8478f721c7..63c709d8aed 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -69,6 +69,10 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>( ty::Alias(..) | ty::Param(_) | ty::Placeholder(_) | ty::Infer(_) => { throw_inval!(TooGeneric) } + ty::Pat(_, pat) => match **pat { + ty::PatternKind::Range { .. } => ConstValue::from_target_usize(0u64, &tcx), + // Future pattern kinds may have more variants + }, ty::Bound(_, _) => bug!("bound ty during ctfe"), ty::Bool | ty::Char diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index d18600ce7d7..9911c59d4b8 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -640,6 +640,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' | ty::Str | ty::Dynamic(..) | ty::Closure(..) + | ty::Pat(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) => Ok(false), // Some types only occur during typechecking, they have no layout. diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index f3db7d4cd42..e474b952938 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -31,6 +31,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { | ty::Uint(_) | ty::Float(_) | ty::Str + | ty::Pat(_, _) | ty::Array(_, _) | ty::Slice(_) | ty::RawPtr(_, _) diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl index 1b69a6e2fbe..62391daecd0 100644 --- a/compiler/rustc_driver_impl/messages.ftl +++ b/compiler/rustc_driver_impl/messages.ftl @@ -2,7 +2,7 @@ driver_impl_ice = the compiler unexpectedly panicked. this is a bug. driver_impl_ice_bug_report = we would appreciate a bug report: {$bug_report_url} driver_impl_ice_bug_report_internal_feature = using internal features is not supported and expected to cause internal compiler errors when used incorrectly driver_impl_ice_bug_report_outdated = - it seems that this compiler `{$version}` is outdated, a newer nightly should have been released in the mean time + it seems that this compiler `{$version}` is outdated, a newer nightly should have been released in the meantime .update = please consider running `rustup update nightly` to update the nightly channel and check if this problem still persists .url = if the problem still persists, we would appreciate a bug report: {$bug_report_url} driver_impl_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden diff --git a/compiler/rustc_error_codes/src/error_codes/E0699.md b/compiler/rustc_error_codes/src/error_codes/E0699.md index 454d2507e5e..1094ebf4b8f 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0699.md +++ b/compiler/rustc_error_codes/src/error_codes/E0699.md @@ -1,8 +1,10 @@ +#### Note: this error code is no longer emitted by the compiler. + A method was called on a raw pointer whose inner type wasn't completely known. Erroneous code example: -```compile_fail,edition2018,E0699 +```compile_fail,edition2018 # #![deny(warnings)] # fn main() { let foo = &1; diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index da688e385aa..f4a33a05c1b 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -441,7 +441,7 @@ E0695: 0695, E0696: 0696, E0697: 0697, E0698: 0698, -E0699: 0699, +E0699: 0699, // REMOVED: merged into generic inference var error E0700: 0700, E0701: 0701, E0703: 0703, diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index a83f9f56beb..1eee11604ce 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -355,6 +355,8 @@ declare_features! ( (accepted, type_alias_enum_variants, "1.37.0", Some(49683)), /// Allows macros to appear in the type position. (accepted, type_macros, "1.13.0", Some(27245)), + /// Allows using type privacy lints (`private_interfaces`, `private_bounds`, `unnameable_types`). + (accepted, type_privacy_lints, "CURRENT_RUSTC_VERSION", Some(48054)), /// Allows `const _: TYPE = VALUE`. (accepted, underscore_const_names, "1.37.0", Some(54912)), /// Allows `use path as _;` and `extern crate c as _;`. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 6fe51c62936..e6b19817de3 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -215,6 +215,8 @@ declare_features! ( (internal, omit_gdb_pretty_printer_section, "1.5.0", None), /// Set the maximum pattern complexity allowed (not limited by default). (internal, pattern_complexity, "1.78.0", None), + /// Allows using pattern types. + (internal, pattern_types, "CURRENT_RUSTC_VERSION", Some(123646)), /// Allows using `#[prelude_import]` on glob `use` items. (internal, prelude_import, "1.2.0", None), /// Used to identify crates that contain the profiler runtime. @@ -615,8 +617,6 @@ declare_features! ( /// Allows creation of instances of a struct by moving fields that have /// not changed from prior instances of the same struct (RFC #2528) (unstable, type_changing_struct_update, "1.58.0", Some(86555)), - /// Allows using type privacy lints (`private_interfaces`, `private_bounds`, `unnameable_types`). - (unstable, type_privacy_lints, "1.72.0", Some(48054)), /// Enables rustc to generate code that instructs libstd to NOT ignore SIGPIPE. (unstable, unix_sigpipe, "1.65.0", Some(97889)), /// Allows unnamed fields of struct and union type diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index f21cd653f96..c6e3ad31f01 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2624,6 +2624,8 @@ pub enum TyKind<'hir> { Infer, /// Placeholder for a type that has failed to be defined. Err(rustc_span::ErrorGuaranteed), + /// Pattern types (`pattern_type!(u32 is 1..)`) + Pat(&'hir Ty<'hir>, &'hir Pat<'hir>), } #[derive(Debug, Clone, Copy, HashStable_Generic)] diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 8c44f21a57b..5da9d4444da 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -356,6 +356,11 @@ pub trait Visitor<'v>: Sized { fn visit_ty(&mut self, t: &'v Ty<'v>) -> Self::Result { walk_ty(self, t) } + fn visit_pattern_type_pattern(&mut self, _p: &'v Pat<'v>) { + // Do nothing. Only a few visitors need to know the details of the pattern type, + // and they opt into it. All other visitors will just choke on our fake patterns + // because they aren't in a body. + } fn visit_generic_param(&mut self, p: &'v GenericParam<'v>) -> Self::Result { walk_generic_param(self, p) } @@ -882,6 +887,10 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul TyKind::AnonAdt(item_id) => { try_visit!(visitor.visit_nested_item(item_id)); } + TyKind::Pat(ty, pat) => { + try_visit!(visitor.visit_ty(ty)); + try_visit!(visitor.visit_pattern_type_pattern(pat)); + } } V::Result::output() } diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index d8a90d62dac..e66a834ab9e 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -349,6 +349,9 @@ hir_analysis_pass_to_variadic_function = can't pass `{$ty}` to variadic function .suggestion = cast the value to `{$cast_ty}` .help = cast the value to `{$cast_ty}` +hir_analysis_pattern_type_non_const_range = "range patterns must have constant range start and end" +hir_analysis_pattern_type_wild_pat = "wildcard patterns are not permitted for pattern types" + .label = "this type is the same as the inner type without a pattern" hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind} .label = not allowed in type signatures diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 739a7086992..8d4ae10d4bf 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -13,7 +13,7 @@ use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, TraitEngineExt as _}; use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS; use rustc_middle::middle::stability::EvalResult; -use rustc_middle::traits::{DefiningAnchor, ObligationCauseCode}; +use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; use rustc_middle::ty::util::{Discr, InspectCoroutineFields, IntTypeExt}; @@ -345,10 +345,7 @@ fn check_opaque_meets_bounds<'tcx>( }; let param_env = tcx.param_env(defining_use_anchor); - let infcx = tcx - .infer_ctxt() - .with_opaque_type_inference(DefiningAnchor::bind(tcx, defining_use_anchor)) - .build(); + let infcx = tcx.infer_ctxt().with_opaque_type_inference(defining_use_anchor).build(); let ocx = ObligationCtxt::new(&infcx); let args = match *origin { @@ -1567,7 +1564,7 @@ pub(super) fn check_coroutine_obligations( .ignoring_regions() // Bind opaque types to type checking root, as they should have been checked by borrowck, // but may show up in some cases, like when (root) obligations are stalled in the new solver. - .with_opaque_type_inference(DefiningAnchor::bind(tcx, typeck.hir_owner.def_id)) + .with_opaque_type_inference(typeck.hir_owner.def_id) .build(); let mut fulfillment_cx = <dyn TraitEngine<'_>>::new(&infcx); diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index 067878091a7..4a85e9983f4 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -144,7 +144,12 @@ impl<'tcx> InherentCollect<'tcx> { let id = id.owner_id.def_id; let item_span = self.tcx.def_span(id); let self_ty = self.tcx.type_of(id).instantiate_identity(); - let self_ty = self.tcx.peel_off_weak_alias_tys(self_ty); + let mut self_ty = self.tcx.peel_off_weak_alias_tys(self_ty); + // We allow impls on pattern types exactly when we allow impls on the base type. + // FIXME(pattern_types): Figure out the exact coherence rules we want here. + while let ty::Pat(base, _) = *self_ty.kind() { + self_ty = base; + } match *self_ty.kind() { ty::Adt(def, _) => self.check_def_id(id, self_ty, def.did()), ty::Foreign(did) => self.check_def_id(id, self_ty, did), @@ -154,6 +159,7 @@ impl<'tcx> InherentCollect<'tcx> { ty::Dynamic(..) => { Err(self.tcx.dcx().emit_err(errors::InherentDyn { span: item_span })) } + ty::Pat(_, _) => unreachable!(), ty::Bool | ty::Char | ty::Int(_) diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 1770f7b4e91..5585d2e069c 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -206,6 +206,11 @@ pub(crate) fn orphan_check_impl( (LocalImpl::Disallow { problematic_kind }, NonlocalImpl::DisallowOther) } + ty::Pat(..) => ( + LocalImpl::Disallow { problematic_kind: "pattern type" }, + NonlocalImpl::DisallowOther, + ), + ty::Bool | ty::Char | ty::Int(..) diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 2d4742fa1dc..d129614e0e1 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -7,6 +7,8 @@ use rustc_errors::{ use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::Ty; use rustc_span::{symbol::Ident, Span, Symbol}; +mod pattern_types; +pub use pattern_types::*; #[derive(Diagnostic)] #[diag(hir_analysis_ambiguous_assoc_item)] @@ -1629,3 +1631,10 @@ pub struct OpaqueCapturesHigherRankedLifetime { pub decl_span: Span, pub bad_place: &'static str, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_pattern_type_non_const_range)] +pub struct NonConstRange { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_hir_analysis/src/errors/pattern_types.rs b/compiler/rustc_hir_analysis/src/errors/pattern_types.rs new file mode 100644 index 00000000000..008d2698989 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/errors/pattern_types.rs @@ -0,0 +1,9 @@ +use rustc_macros::Diagnostic; +use rustc_span::Span; + +#[derive(Diagnostic)] +#[diag(hir_analysis_pattern_type_wild_pat)] +pub struct WildPatTy { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index f726f2a7b89..59f0fac5aa7 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -21,7 +21,7 @@ mod object_safety; use crate::bounds::Bounds; use crate::collect::HirPlaceholderCollector; -use crate::errors::AmbiguousLifetimeBound; +use crate::errors::{AmbiguousLifetimeBound, WildPatTy}; use crate::hir_ty_lowering::errors::{prohibit_assoc_item_binding, GenericsArgsErrExtend}; use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; use crate::middle::resolve_bound_vars as rbv; @@ -39,6 +39,7 @@ use rustc_hir::{GenericArg, GenericArgs}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; use rustc_middle::middle::stability::AllowUnstable; +use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; use rustc_middle::ty::{ self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, ParamEnv, Ty, TyCtxt, TypeVisitableExt, @@ -2195,6 +2196,68 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // handled specially and will not descend into this routine. self.ty_infer(None, hir_ty.span) } + hir::TyKind::Pat(ty, pat) => { + let ty = self.lower_ty(ty); + let pat_ty = match pat.kind { + hir::PatKind::Wild => { + let err = tcx.dcx().emit_err(WildPatTy { span: pat.span }); + Ty::new_error(tcx, err) + } + hir::PatKind::Range(start, end, include_end) => { + let expr_to_const = |expr: &'tcx hir::Expr<'tcx>| -> ty::Const<'tcx> { + let (expr, neg) = match expr.kind { + hir::ExprKind::Unary(hir::UnOp::Neg, negated) => { + (negated, Some((expr.hir_id, expr.span))) + } + _ => (expr, None), + }; + let c = match &expr.kind { + hir::ExprKind::Lit(lit) => { + let lit_input = + LitToConstInput { lit: &lit.node, ty, neg: neg.is_some() }; + match tcx.lit_to_const(lit_input) { + Ok(c) => c, + Err(LitToConstError::Reported(err)) => { + ty::Const::new_error(tcx, err, ty) + } + Err(LitToConstError::TypeError) => todo!(), + } + } + _ => { + let err = tcx + .dcx() + .emit_err(crate::errors::NonConstRange { span: expr.span }); + ty::Const::new_error(tcx, err, ty) + } + }; + self.record_ty(expr.hir_id, c.ty(), expr.span); + if let Some((id, span)) = neg { + self.record_ty(id, c.ty(), span); + } + c + }; + + let start = start.map(expr_to_const); + let end = end.map(expr_to_const); + + let include_end = match include_end { + hir::RangeEnd::Included => true, + hir::RangeEnd::Excluded => false, + }; + + let pat = tcx.mk_pat(ty::PatternKind::Range { start, end, include_end }); + Ty::new_pat(tcx, ty, pat) + } + hir::PatKind::Err(e) => Ty::new_error(tcx, e), + _ => Ty::new_error_with_message( + tcx, + pat.span, + format!("unsupported pattern for pattern type: {pat:#?}"), + ), + }; + self.record_ty(pat.hir_id, ty, pat.span); + pat_ty + } hir::TyKind::Err(guar) => Ty::new_error(tcx, *guar), }; diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index 28c86d8019e..20e4110e137 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -249,6 +249,20 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_ty(current, typ, variance); } + ty::Pat(typ, pat) => { + match *pat { + ty::PatternKind::Range { start, end, include_end: _ } => { + if let Some(start) = start { + self.add_constraints_from_const(current, start, variance); + } + if let Some(end) = end { + self.add_constraints_from_const(current, end, variance); + } + } + } + self.add_constraints_from_ty(current, typ, variance); + } + ty::Slice(typ) => { self.add_constraints_from_ty(current, typ, variance); } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index bd528432e70..a590c648d20 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -330,6 +330,11 @@ impl<'a> State<'a> { self.word("_"); } hir::TyKind::AnonAdt(..) => self.word("/* anonymous adt */"), + hir::TyKind::Pat(ty, pat) => { + self.print_type(ty); + self.word(" is "); + self.print_pat(pat); + } } self.end() } diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 1d51101c940..18d9d739dd6 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -93,9 +93,6 @@ hir_typeck_lossy_provenance_ptr2int = .suggestion = use `.addr()` to obtain the address of a pointer .help = if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead -hir_typeck_method_call_on_unknown_raw_pointee = - cannot call a method on a raw pointer with an unknown pointee type - hir_typeck_missing_parentheses_in_range = can't call method `{$method_name}` on type `{$ty_str}` hir_typeck_no_associated_item = no {$item_kind} named `{$item_name}` found for {$ty_prefix} `{$ty_str}`{$trait_missing_method -> diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index c948b6343b7..92f74281ab9 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -40,17 +40,19 @@ use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::cast::{CastKind, CastTy}; use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitableExt, VariantDef}; use rustc_session::lint; use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::DUMMY_SP; use rustc_trait_selection::infer::InferCtxtExt; /// Reifies a cast check to be checked once we have full type information for /// a function context. #[derive(Debug)] -pub struct CastCheck<'tcx> { +pub(crate) struct CastCheck<'tcx> { /// The expression whose value is being casted expr: &'tcx hir::Expr<'tcx>, /// The source type for the cast expression @@ -60,8 +62,6 @@ pub struct CastCheck<'tcx> { cast_ty: Ty<'tcx>, cast_span: Span, span: Span, - /// whether the cast is made in a const context or not. - pub constness: hir::Constness, } /// The kind of pointer and associated metadata (thin, length or vtable) - we @@ -130,6 +130,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::CoroutineWitness(..) | ty::RawPtr(_, _) | ty::Ref(..) + | ty::Pat(..) | ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) @@ -194,18 +195,45 @@ fn make_invalid_casting_error<'a, 'tcx>( ) } +/// If a cast from `from_ty` to `to_ty` is valid, returns a `Some` containing the kind +/// of the cast. +/// +/// This is a helper used from clippy. +pub fn check_cast<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + e: &'tcx hir::Expr<'tcx>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, +) -> Option<CastKind> { + let hir_id = e.hir_id; + let local_def_id = hir_id.owner.def_id; + + let root_ctxt = crate::TypeckRootCtxt::new(tcx, local_def_id); + let fn_ctxt = FnCtxt::new(&root_ctxt, param_env, local_def_id); + + if let Ok(check) = CastCheck::new( + &fn_ctxt, e, from_ty, to_ty, + // We won't show any errors to the user, so the span is irrelevant here. + DUMMY_SP, DUMMY_SP, + ) { + check.do_check(&fn_ctxt).ok() + } else { + None + } +} + impl<'a, 'tcx> CastCheck<'tcx> { - pub fn new( + pub(crate) fn new( fcx: &FnCtxt<'a, 'tcx>, expr: &'tcx hir::Expr<'tcx>, expr_ty: Ty<'tcx>, cast_ty: Ty<'tcx>, cast_span: Span, span: Span, - constness: hir::Constness, ) -> Result<CastCheck<'tcx>, ErrorGuaranteed> { let expr_span = expr.span.find_ancestor_inside(span).unwrap_or(expr.span); - let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span, constness }; + let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span }; // For better error messages, check for some obviously unsized // cases now. We do a more thorough check at the end, once @@ -644,7 +672,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { /// Checks a cast, and report an error if one exists. In some cases, this /// can return Ok and create type errors in the fcx rather than returning /// directly. coercion-cast is handled in check instead of here. - pub fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<CastKind, CastError> { + fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<CastKind, CastError> { use rustc_middle::ty::cast::CastTy::*; use rustc_middle::ty::cast::IntTy::*; diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 44b19318d5d..51d01afc4eb 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1318,6 +1318,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } +/// Check whether `ty` can be coerced to `output_ty`. +/// Used from clippy. +pub fn can_coerce<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: LocalDefId, + ty: Ty<'tcx>, + output_ty: Ty<'tcx>, +) -> bool { + let root_ctxt = crate::typeck_root_ctxt::TypeckRootCtxt::new(tcx, body_id); + let fn_ctxt = FnCtxt::new(&root_ctxt, param_env, body_id); + fn_ctxt.can_coerce(ty, output_ty) +} + /// CoerceMany encapsulates the pattern you should use when you have /// many expressions that are all getting coerced to a common /// type. This arises, for example, when you have a match (the result diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index f9b2ec69730..d399730bf3d 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -77,13 +77,6 @@ pub struct StructExprNonExhaustive { } #[derive(Diagnostic)] -#[diag(hir_typeck_method_call_on_unknown_raw_pointee, code = E0699)] -pub struct MethodCallOnUnknownRawPointee { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] #[diag(hir_typeck_functional_record_update_on_non_struct, code = E0436)] pub struct FunctionalRecordUpdateOnNonStruct { #[primary_span] diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 4da45303d12..d3df3dd3885 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1390,15 +1390,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { // Defer other checks until we're done type checking. let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); - match cast::CastCheck::new( - self, - e, - t_expr, - t_cast, - t.span, - expr.span, - hir::Constness::NotConst, - ) { + match cast::CastCheck::new(self, e, t_expr, t_cast, t.span, expr.span) { Ok(cast_check) => { debug!( "check_expr_cast: deferring cast from {:?} to {:?}: {:?}", diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 85d04f7d1c4..f1e82543a99 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -730,8 +730,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for ty in ret_ty.walk() { if let ty::GenericArgKind::Type(ty) = ty.unpack() && let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *ty.kind() - && let Some(def_id) = def_id.as_local() - && self.opaque_type_origin(def_id).is_some() + && self.can_define_opaque_ty(def_id) { return None; } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 05e7c5b2b41..0b69c7a2431 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -38,7 +38,7 @@ use std::ops::Deref; /// /// [`ItemCtxt`]: rustc_hir_analysis::collect::ItemCtxt /// [`InferCtxt`]: infer::InferCtxt -pub struct FnCtxt<'a, 'tcx> { +pub(crate) struct FnCtxt<'a, 'tcx> { pub(super) body_id: LocalDefId, /// The parameter environment used for proving trait obligations diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 80fd4be53e1..700dde184f2 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -42,8 +42,9 @@ mod typeck_root_ctxt; mod upvar; mod writeback; -pub use fn_ctxt::FnCtxt; -pub use typeck_root_ctxt::TypeckRootCtxt; +pub use coercion::can_coerce; +use fn_ctxt::FnCtxt; +use typeck_root_ctxt::TypeckRootCtxt; use crate::check::check_fn; use crate::coercion::DynamicCoerceMany; diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 4e63600dbdf..28e17e1de36 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -3,7 +3,6 @@ use super::CandidateSource; use super::MethodError; use super::NoMatchData; -use crate::errors::MethodCallOnUnknownRawPointee; use crate::FnCtxt; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -430,21 +429,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if is_suggestion.0 { // Ambiguity was encountered during a suggestion. Just keep going. debug!("ProbeContext: encountered ambiguity in suggestion"); - } else if bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types { + } else if bad_ty.reached_raw_pointer + && !self.tcx.features().arbitrary_self_types + && !self.tcx.sess.at_least_rust_2018() + { // this case used to be allowed by the compiler, // so we do a future-compat lint here for the 2015 edition // (see https://github.com/rust-lang/rust/issues/46906) - if self.tcx.sess.at_least_rust_2018() { - self.dcx().emit_err(MethodCallOnUnknownRawPointee { span }); - } else { - self.tcx.node_span_lint( - lint::builtin::TYVAR_BEHIND_RAW_POINTER, - scope_expr_id, - span, - "type annotations needed", - |_| {}, - ); - } + self.tcx.node_span_lint( + lint::builtin::TYVAR_BEHIND_RAW_POINTER, + scope_expr_id, + span, + "type annotations needed", + |_| {}, + ); } else { // Ended up encountering a type variable when doing autoderef, // but it may not be a type variable after processing obligations @@ -455,10 +453,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty)); let ty = self.resolve_vars_if_possible(ty.value); let guar = match *ty.kind() { - ty::Infer(ty::TyVar(_)) => self - .err_ctxt() - .emit_inference_failure_err(self.body_id, span, ty.into(), E0282, true) - .emit(), + ty::Infer(ty::TyVar(_)) => { + let raw_ptr_call = + bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types; + let mut err = self.err_ctxt().emit_inference_failure_err( + self.body_id, + span, + ty.into(), + E0282, + !raw_ptr_call, + ); + if raw_ptr_call { + err.span_label(span, "cannot call a method on a raw pointer with an unknown pointee type"); + } + err.emit() + } ty::Error(guar) => guar, _ => bug!("unexpected bad final type in method autoderef"), }; diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs index e493e6a0a7e..694ddd0e3e8 100644 --- a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs +++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs @@ -5,7 +5,6 @@ use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::HirIdMap; use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; -use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::LocalDefIdMap; @@ -27,7 +26,7 @@ use std::ops::Deref; /// `bar()` will each have their own `FnCtxt`, but they will /// share the inference context, will process obligations together, /// can access each other's local types (scoping permitted), etc. -pub struct TypeckRootCtxt<'tcx> { +pub(crate) struct TypeckRootCtxt<'tcx> { pub(super) infcx: InferCtxt<'tcx>, pub(super) typeck_results: RefCell<ty::TypeckResults<'tcx>>, @@ -78,11 +77,7 @@ impl<'tcx> TypeckRootCtxt<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { let hir_owner = tcx.local_def_id_to_hir_id(def_id).owner; - let infcx = tcx - .infer_ctxt() - .ignoring_regions() - .with_opaque_type_inference(DefiningAnchor::bind(tcx, def_id)) - .build(); + let infcx = tcx.infer_ctxt().ignoring_regions().with_opaque_type_inference(def_id).build(); let typeck_results = RefCell::new(ty::TypeckResults::new(hir_owner)); TypeckRootCtxt { diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 94088216c02..f2222eec76a 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -75,7 +75,7 @@ impl<'tcx> InferCtxt<'tcx> { pub fn fork_with_intercrate(&self, intercrate: bool) -> Self { Self { tcx: self.tcx, - defining_use_anchor: self.defining_use_anchor, + defining_opaque_types: self.defining_opaque_types, considering_regions: self.considering_regions, skip_leak_check: self.skip_leak_check, inner: self.inner.clone(), diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 9f70fee993d..825c3bf82fc 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -42,7 +42,7 @@ impl<'tcx> InferCtxt<'tcx> { V: TypeFoldable<TyCtxt<'tcx>>, { let (param_env, value) = value.into_parts(); - let param_env = self.tcx.canonical_param_env_cache.get_or_insert( + let mut param_env = self.tcx.canonical_param_env_cache.get_or_insert( self.tcx, param_env, query_state, @@ -59,6 +59,8 @@ impl<'tcx> InferCtxt<'tcx> { }, ); + param_env.defining_opaque_types = self.defining_opaque_types; + Canonicalizer::canonicalize_with_base( param_env, value, @@ -440,6 +442,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> { | ty::Tuple(..) | ty::Alias(..) | ty::Foreign(..) + | ty::Pat(..) | ty::Param(..) => { if t.flags().intersects(self.needs_canonical_flags) { t.super_fold_with(self) @@ -540,6 +543,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { max_universe: ty::UniverseIndex::ROOT, variables: List::empty(), value: (), + defining_opaque_types: infcx.map(|i| i.defining_opaque_types).unwrap_or_default(), }; Canonicalizer::canonicalize_with_base( base, @@ -609,7 +613,15 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { .max() .unwrap_or(ty::UniverseIndex::ROOT); - Canonical { max_universe, variables: canonical_variables, value: (base.value, out_value) } + assert!( + !infcx.is_some_and(|infcx| infcx.defining_opaque_types != base.defining_opaque_types) + ); + Canonical { + max_universe, + variables: canonical_variables, + value: (base.value, out_value), + defining_opaque_types: base.defining_opaque_types, + } } /// Creates a canonical variable replacing `kind` from the input, diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 9d2e065afa3..b948067e750 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -505,12 +505,9 @@ impl<'tcx> InferCtxt<'tcx> { let b = instantiate_value(self.tcx, &result_args, b); debug!(?a, ?b, "constrain opaque type"); // We use equate here instead of, for example, just registering the - // opaque type's hidden value directly, because we may be instantiating - // a query response that was canonicalized in an InferCtxt that had - // a different defining anchor. In that case, we may have inferred - // `NonLocalOpaque := LocalOpaque` but can only instantiate it in - // the other direction as `LocalOpaque := NonLocalOpaque`. Using eq - // here allows us to try both directions (in `InferCtxt::handle_opaque_type`). + // opaque type's hidden value directly, because the hidden type may have been an inference + // variable that got constrained to the opaque type itself. In that case we want to equate + // the generic args of the opaque with the generic params of its hidden type version. obligations.extend( self.at(cause, param_env) .eq( diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 6e5ed0a31cb..0b8061104ab 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -34,7 +34,7 @@ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKin use rustc_middle::infer::unify_key::{ConstVidKey, EffectVidKey}; use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult}; use rustc_middle::mir::ConstraintCategory; -use rustc_middle::traits::{select, DefiningAnchor}; +use rustc_middle::traits::select; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::BoundVarReplacerDelegate; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; @@ -243,18 +243,8 @@ impl<'tcx> InferCtxtInner<'tcx> { pub struct InferCtxt<'tcx> { pub tcx: TyCtxt<'tcx>, - /// The `DefId` of the item in whose context we are performing inference or typeck. - /// It is used to check whether an opaque type use is a defining use. - /// - /// If it is `DefiningAnchor::Bubble`, we can't resolve opaque types here and need to bubble up - /// the obligation. This frequently happens for - /// short lived InferCtxt within queries. The opaque type obligations are forwarded - /// to the outside until the end up in an `InferCtxt` for typeck or borrowck. - /// - /// Its default value is `DefiningAnchor::Bind(&[])`, which means no opaque types may be defined. - /// This way it is easier to catch errors that - /// might come up during inference or typeck. - pub defining_use_anchor: DefiningAnchor<'tcx>, + /// The `DefIds` of the opaque types that may have their hidden types constrained. + defining_opaque_types: &'tcx ty::List<LocalDefId>, /// Whether this inference context should care about region obligations in /// the root universe. Most notably, this is used during hir typeck as region @@ -401,6 +391,10 @@ impl<'tcx> ty::InferCtxtLike for InferCtxt<'tcx> { fn probe_ct_var(&self, vid: ConstVid) -> Option<ty::Const<'tcx>> { self.probe_const_var(vid).ok() } + + fn defining_opaque_types(&self) -> &'tcx ty::List<LocalDefId> { + self.defining_opaque_types + } } /// See the `error_reporting` module for more details. @@ -615,7 +609,7 @@ impl fmt::Display for FixupError { /// Used to configure inference contexts before their creation. pub struct InferCtxtBuilder<'tcx> { tcx: TyCtxt<'tcx>, - defining_use_anchor: DefiningAnchor<'tcx>, + defining_opaque_types: &'tcx ty::List<LocalDefId>, considering_regions: bool, skip_leak_check: bool, /// Whether we are in coherence mode. @@ -630,7 +624,7 @@ impl<'tcx> TyCtxt<'tcx> { fn infer_ctxt(self) -> InferCtxtBuilder<'tcx> { InferCtxtBuilder { tcx: self, - defining_use_anchor: DefiningAnchor::Bind(ty::List::empty()), + defining_opaque_types: ty::List::empty(), considering_regions: true, skip_leak_check: false, intercrate: false, @@ -646,8 +640,16 @@ impl<'tcx> InferCtxtBuilder<'tcx> { /// It is only meant to be called in two places, for typeck /// (via `Inherited::build`) and for the inference context used /// in mir borrowck. - pub fn with_opaque_type_inference(mut self, defining_use_anchor: DefiningAnchor<'tcx>) -> Self { - self.defining_use_anchor = defining_use_anchor; + pub fn with_opaque_type_inference(mut self, defining_anchor: LocalDefId) -> Self { + self.defining_opaque_types = self.tcx.opaque_types_defined_by(defining_anchor); + self + } + + pub fn with_defining_opaque_types( + mut self, + defining_opaque_types: &'tcx ty::List<LocalDefId>, + ) -> Self { + self.defining_opaque_types = defining_opaque_types; self } @@ -679,14 +681,14 @@ impl<'tcx> InferCtxtBuilder<'tcx> { /// the bound values in `C` to their instantiated values in `V` /// (in other words, `S(C) = V`). pub fn build_with_canonical<T>( - &mut self, + self, span: Span, canonical: &Canonical<'tcx, T>, ) -> (InferCtxt<'tcx>, T, CanonicalVarValues<'tcx>) where T: TypeFoldable<TyCtxt<'tcx>>, { - let infcx = self.build(); + let infcx = self.with_defining_opaque_types(canonical.defining_opaque_types).build(); let (value, args) = infcx.instantiate_canonical(span, canonical); (infcx, value, args) } @@ -694,7 +696,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { pub fn build(&mut self) -> InferCtxt<'tcx> { let InferCtxtBuilder { tcx, - defining_use_anchor, + defining_opaque_types, considering_regions, skip_leak_check, intercrate, @@ -702,7 +704,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { } = *self; InferCtxt { tcx, - defining_use_anchor, + defining_opaque_types, considering_regions, skip_leak_check, inner: RefCell::new(InferCtxtInner::new()), @@ -1230,6 +1232,12 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow().opaque_type_storage.opaque_types.clone() } + #[inline(always)] + pub fn can_define_opaque_ty(&self, id: impl Into<DefId>) -> bool { + let Some(id) = id.into().as_local() else { return false }; + self.defining_opaque_types.contains(&id) + } + pub fn ty_to_string(&self, t: Ty<'tcx>) -> String { self.resolve_vars_if_possible(t).to_string() } diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index 01430e830e5..d32515425c4 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -4,11 +4,10 @@ use crate::errors::OpaqueHiddenTypeDiag; use crate::infer::{InferCtxt, InferOk}; use crate::traits::{self, PredicateObligation}; use hir::def_id::{DefId, LocalDefId}; -use hir::OpaqueTyOrigin; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; -use rustc_middle::traits::{DefiningAnchor, ObligationCause}; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::GenericArgKind; @@ -54,16 +53,13 @@ impl<'tcx> InferCtxt<'tcx> { } let mut obligations = vec![]; - let replace_opaque_type = |def_id: DefId| { - def_id.as_local().is_some_and(|def_id| self.opaque_type_origin(def_id).is_some()) - }; let value = value.fold_with(&mut BottomUpFolder { tcx: self.tcx, lt_op: |lt| lt, ct_op: |ct| ct, ty_op: |ty| match *ty.kind() { ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) - if replace_opaque_type(def_id) && !ty.has_escaping_bound_vars() => + if self.can_define_opaque_ty(def_id) && !ty.has_escaping_bound_vars() => { let def_span = self.tcx.def_span(def_id); let span = if span.contains(def_span) { def_span } else { span }; @@ -106,56 +102,52 @@ impl<'tcx> InferCtxt<'tcx> { b, )); } - match self.defining_use_anchor { - DefiningAnchor::Bind(_) => { - // Check that this is `impl Trait` type is - // declared by `parent_def_id` -- i.e., one whose - // value we are inferring. At present, this is - // always true during the first phase of - // type-check, but not always true later on during - // NLL. Once we support named opaque types more fully, - // this same scenario will be able to arise during all phases. - // - // Here is an example using type alias `impl Trait` - // that indicates the distinction we are checking for: - // - // ```rust - // mod a { - // pub type Foo = impl Iterator; - // pub fn make_foo() -> Foo { .. } - // } - // - // mod b { - // fn foo() -> a::Foo { a::make_foo() } - // } - // ``` - // - // Here, the return type of `foo` references an - // `Opaque` indeed, but not one whose value is - // presently being inferred. You can get into a - // similar situation with closure return types - // today: - // - // ```rust - // fn foo() -> impl Iterator { .. } - // fn bar() { - // let x = || foo(); // returns the Opaque assoc with `foo` - // } - // ``` - if self.opaque_type_origin(def_id).is_none() { - return None; - } - } - DefiningAnchor::Bubble => {} + // Check that this is `impl Trait` type is + // declared by `parent_def_id` -- i.e., one whose + // value we are inferring. At present, this is + // always true during the first phase of + // type-check, but not always true later on during + // NLL. Once we support named opaque types more fully, + // this same scenario will be able to arise during all phases. + // + // Here is an example using type alias `impl Trait` + // that indicates the distinction we are checking for: + // + // ```rust + // mod a { + // pub type Foo = impl Iterator; + // pub fn make_foo() -> Foo { .. } + // } + // + // mod b { + // fn foo() -> a::Foo { a::make_foo() } + // } + // ``` + // + // Here, the return type of `foo` references an + // `Opaque` indeed, but not one whose value is + // presently being inferred. You can get into a + // similar situation with closure return types + // today: + // + // ```rust + // fn foo() -> impl Iterator { .. } + // fn bar() { + // let x = || foo(); // returns the Opaque assoc with `foo` + // } + // ``` + if !self.can_define_opaque_ty(def_id) { + return None; } + if let ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }) = *b.kind() { // We could accept this, but there are various ways to handle this situation, and we don't // want to make a decision on it right now. Likely this case is so super rare anyway, that // no one encounters it in practice. // It does occur however in `fn fut() -> impl Future<Output = i32> { async { 42 } }`, // where it is of no concern, so we only check for TAITs. - if let Some(OpaqueTyOrigin::TyAlias { .. }) = - b_def_id.as_local().and_then(|b_def_id| self.opaque_type_origin(b_def_id)) + if self.can_define_opaque_ty(b_def_id) + && self.tcx.is_type_alias_impl_trait(b_def_id) { self.tcx.dcx().emit_err(OpaqueHiddenTypeDiag { span: cause.span, @@ -367,20 +359,6 @@ impl<'tcx> InferCtxt<'tcx> { op: |r| self.member_constraint(opaque_type_key, span, concrete_ty, r, &choice_regions), }); } - - /// Returns the origin of the opaque type `def_id` if we're currently - /// in its defining scope. - #[instrument(skip(self), level = "trace", ret)] - pub fn opaque_type_origin(&self, def_id: LocalDefId) -> Option<OpaqueTyOrigin> { - let defined_opaque_types = match self.defining_use_anchor { - DefiningAnchor::Bubble => return None, - DefiningAnchor::Bind(bind) => bind, - }; - - let origin = self.tcx.opaque_type_origin(def_id); - - defined_opaque_types.contains(&def_id).then_some(origin) - } } /// Visitor that requires that (almost) all regions in the type visited outlive diff --git a/compiler/rustc_infer/src/infer/outlives/components.rs b/compiler/rustc_infer/src/infer/outlives/components.rs index 7dd1ec32542..6bab3ad6ba3 100644 --- a/compiler/rustc_infer/src/infer/outlives/components.rs +++ b/compiler/rustc_infer/src/infer/outlives/components.rs @@ -93,6 +93,7 @@ fn compute_components<'tcx>( } } + ty::Pat(element, _) | ty::Array(element, _) => { // Don't look into the len const as it doesn't affect regions compute_components(tcx, element, out, visited); diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 797c0df4d73..82b90e1660a 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -299,6 +299,9 @@ lint_improper_ctypes_only_phantomdata = composed only of `PhantomData` lint_improper_ctypes_opaque = opaque types have no C equivalent +lint_improper_ctypes_pat_help = consider using the base type instead + +lint_improper_ctypes_pat_reason = pattern types have no C equivalent lint_improper_ctypes_slice_help = consider using a raw pointer instead lint_improper_ctypes_slice_reason = slices have no C equivalent diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 70c7aff3f20..8b974a461d4 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1337,8 +1337,9 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller { } declare_lint! { - /// The `unreachable_pub` lint triggers for `pub` items not reachable from - /// the crate root. + /// The `unreachable_pub` lint triggers for `pub` items not reachable from other crates - that + /// means neither directly accessible, nor reexported, nor leaked through things like return + /// types. /// /// ### Example /// diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index 1e9c60a540d..870e198d70a 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -157,6 +157,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { | TyKind::Never | TyKind::Tup(_) | TyKind::Path(_) + | TyKind::Pat(..) | TyKind::AnonAdt(_) | TyKind::OpaqueDef(_, _, _) | TyKind::Typeof(_) diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index cf825be7a55..ec7954536be 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -20,7 +20,7 @@ declare_lint! { /// This functionality was removed in #97346, but then rolled back in #99860 /// because it caused regressions. /// - /// We plan on reintroducing this as a hard error, but in the mean time, + /// We plan on reintroducing this as a hard error, but in the meantime, /// this lint serves to warn and suggest fixes for any use-cases which rely /// on this behavior. /// diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 534eb60eeb0..e982842f536 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1379,6 +1379,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { help: Some(fluent::lint_improper_ctypes_char_help), }, + ty::Pat(..) => FfiUnsafe { + ty, + reason: fluent::lint_improper_ctypes_pat_reason, + help: Some(fluent::lint_improper_ctypes_pat_help), + }, + ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => { FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_128bit, help: None } } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 30522628f46..53b5273803c 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4311,7 +4311,6 @@ declare_lint! { /// ### Example /// /// ```rust,compile_fail - /// # #![feature(type_privacy_lints)] /// # #![allow(unused)] /// #![deny(unnameable_types)] /// mod m { @@ -4328,10 +4327,14 @@ declare_lint! { /// /// It is often expected that if you can obtain an object of type `T`, then /// you can name the type `T` as well, this lint attempts to enforce this rule. + /// The recommended action is to either reexport the type properly to make it nameable, + /// or document that users are not supposed to be able to name it for one reason or another. + /// + /// Besides types, this lint applies to traits because traits can also leak through signatures, + /// and you may obtain objects of their `dyn Trait` or `impl Trait` types. pub UNNAMEABLE_TYPES, Allow, "effective visibility of a type is larger than the area in which it can be named", - @feature_gate = sym::type_privacy_lints; } declare_lint! { diff --git a/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp index 40723ff9f5e..64e6c18092f 100644 --- a/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp @@ -147,7 +147,7 @@ LLVMRustArchiveChildName(LLVMRustArchiveChildConstRef Child, size_t *Size) { Expected<StringRef> NameOrErr = Child->getName(); if (!NameOrErr) { // rustc_codegen_llvm currently doesn't use this error string, but it might be - // useful in the future, and in the mean time this tells LLVM that the + // useful in the future, and in the meantime this tells LLVM that the // error was not ignored and that it shouldn't abort the process. LLVMRustSetLastError(toString(NameOrErr.takeError()).c_str()); return nullptr; diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 8ec1f5a99e7..a6894a7e089 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1524,8 +1524,8 @@ extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty, LLVM } extern "C" LLVMValueRef LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M) { - return wrap(llvm::Intrinsic::getDeclaration(unwrap(M), - (llvm::Intrinsic::ID)llvm::Intrinsic::instrprof_increment)); + return wrap(llvm::Intrinsic::getDeclaration( + unwrap(M), llvm::Intrinsic::instrprof_increment)); } extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B, diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index b69a295f010..2935d5b8f63 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -21,7 +21,7 @@ use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::util::Providers; use rustc_session::cstore::{CrateStore, ExternCrate}; use rustc_session::{Session, StableCrateId}; -use rustc_span::hygiene::{ExpnHash, ExpnId}; +use rustc_span::hygiene::ExpnId; use rustc_span::symbol::{kw, Symbol}; use rustc_span::Span; @@ -378,6 +378,7 @@ provide! { tcx, def_id, other, cdata, } pub(in crate::rmeta) fn provide(providers: &mut Providers) { + provide_cstore_hooks(providers); // FIXME(#44234) - almost all of these queries have no sub-queries and // therefore no actual inputs, they're just reading tables calculated in // resolve! Does this work? Unsure! That's what the issue is about @@ -649,26 +650,27 @@ impl CrateStore for CStore { fn def_path_hash(&self, def: DefId) -> DefPathHash { self.get_crate_data(def.krate).def_path_hash(def.index) } +} - fn def_path_hash_to_def_id(&self, cnum: CrateNum, hash: DefPathHash) -> DefId { - let def_index = self.get_crate_data(cnum).def_path_hash_to_def_index(hash); +fn provide_cstore_hooks(providers: &mut Providers) { + providers.hooks.def_path_hash_to_def_id_extern = |tcx, hash, stable_crate_id| { + // If this is a DefPathHash from an upstream crate, let the CrateStore map + // it to a DefId. + let cstore = CStore::from_tcx(tcx.tcx); + let cnum = cstore.stable_crate_id_to_crate_num(stable_crate_id); + let def_index = cstore.get_crate_data(cnum).def_path_hash_to_def_index(hash); DefId { krate: cnum, index: def_index } - } - - fn expn_hash_to_expn_id( - &self, - sess: &Session, - cnum: CrateNum, - index_guess: u32, - hash: ExpnHash, - ) -> ExpnId { - self.get_crate_data(cnum).expn_hash_to_expn_id(sess, index_guess, hash) - } + }; - fn import_source_files(&self, sess: &Session, cnum: CrateNum) { - let cdata = self.get_crate_data(cnum); + providers.hooks.expn_hash_to_expn_id = |tcx, cnum, index_guess, hash| { + let cstore = CStore::from_tcx(tcx.tcx); + cstore.get_crate_data(cnum).expn_hash_to_expn_id(tcx.sess, index_guess, hash) + }; + providers.hooks.import_source_files = |tcx, cnum| { + let cstore = CStore::from_tcx(tcx.tcx); + let cdata = cstore.get_crate_data(cnum); for file_index in 0..cdata.root.source_map.size() { - cdata.imported_source_file(file_index as u32, sess); + cdata.imported_source_file(file_index as u32, tcx.sess); } - } + }; } diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index bd11b3eb04c..13719268737 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -90,6 +90,7 @@ macro_rules! arena_types { [decode] attribute: rustc_ast::Attribute, [] name_set: rustc_data_structures::unord::UnordSet<rustc_span::symbol::Symbol>, [] ordered_name_set: rustc_data_structures::fx::FxIndexSet<rustc_span::symbol::Symbol>, + [] pats: rustc_middle::ty::PatternKind<'tcx>, // Note that this deliberately duplicates items in the `rustc_hir::arena`, // since we need to allocate this type on both the `rustc_hir` arena diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index aa2cddad093..f7ce15d0a8d 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -6,8 +6,10 @@ use crate::mir; use crate::query::TyCtxtAt; use crate::ty::{Ty, TyCtxt}; -use rustc_span::def_id::LocalDefId; -use rustc_span::DUMMY_SP; +use rustc_hir::def_id::{DefId, DefPathHash}; +use rustc_session::StableCrateId; +use rustc_span::def_id::{CrateNum, LocalDefId}; +use rustc_span::{ExpnHash, ExpnId, DUMMY_SP}; macro_rules! declare_hooks { ($($(#[$attr:meta])*hook $name:ident($($arg:ident: $K:ty),*) -> $V:ty;)*) => { @@ -16,7 +18,6 @@ macro_rules! declare_hooks { $( $(#[$attr])* #[inline(always)] - #[must_use] pub fn $name(self, $($arg: $K,)*) -> $V { self.at(DUMMY_SP).$name($($arg,)*) @@ -28,7 +29,6 @@ macro_rules! declare_hooks { $( $(#[$attr])* #[inline(always)] - #[must_use] #[instrument(level = "debug", skip(self), ret)] pub fn $name(self, $($arg: $K,)*) -> $V { @@ -83,4 +83,23 @@ declare_hooks! { /// You do not want to call this yourself, instead use the cached version /// via `mir_built` hook build_mir(key: LocalDefId) -> mir::Body<'tcx>; + + + /// Imports all `SourceFile`s from the given crate into the current session. + /// This normally happens automatically when we decode a `Span` from + /// that crate's metadata - however, the incr comp cache needs + /// to trigger this manually when decoding a foreign `Span` + hook import_source_files(key: CrateNum) -> (); + + hook expn_hash_to_expn_id( + cnum: CrateNum, + index_guess: u32, + hash: ExpnHash + ) -> ExpnId; + + /// Converts a `DefPathHash` to its corresponding `DefId` in the current compilation + /// session, if it still exists. This is used during incremental compilation to + /// turn a deserialized `DefPathHash` into its current `DefId`. + /// Will fetch a DefId from a DefPathHash for a foreign crate. + hook def_path_hash_to_def_id_extern(hash: DefPathHash, stable_crate_id: StableCrateId) -> DefId; } diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index acea89e4aab..62d7b7c28d1 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -153,11 +153,6 @@ pub struct QueryResponse<'tcx, R> { pub var_values: CanonicalVarValues<'tcx>, pub region_constraints: QueryRegionConstraints<'tcx>, pub certainty: Certainty, - /// List of opaque types which we tried to compare to another type. - /// Inside the query we don't know yet whether the opaque type actually - /// should get its hidden type inferred. So we bubble the opaque type - /// and the type it was compared against upwards and let the query caller - /// handle it. pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>, pub value: R, } @@ -330,6 +325,7 @@ impl<'tcx> CanonicalParamEnvCache<'tcx> { max_universe: ty::UniverseIndex::ROOT, variables: List::empty(), value: key, + defining_opaque_types: ty::List::empty(), }; } @@ -340,6 +336,14 @@ impl<'tcx> CanonicalParamEnvCache<'tcx> { match self.map.borrow().entry(key) { Entry::Occupied(e) => { let (canonical, var_values) = e.get(); + if cfg!(debug_assertions) { + let mut state = state.clone(); + let rerun_canonical = canonicalize_op(tcx, key, &mut state); + assert_eq!(rerun_canonical, *canonical); + let OriginalQueryValues { var_values: rerun_var_values, universe_map } = state; + assert_eq!(universe_map.len(), 1); + assert_eq!(**var_values, *rerun_var_values); + } state.var_values.extend_from_slice(var_values); *canonical } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 7ecac0c0e78..601bfc770f4 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -30,7 +30,6 @@ pub use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::graph::dominators::Dominators; -use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_index::bit_set::BitSet; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_serialize::{Decodable, Encodable}; @@ -687,57 +686,6 @@ impl<'tcx> Body<'tcx> { self.injection_phase.is_some() } - /// Finds which basic blocks are actually reachable for a specific - /// monomorphization of this body. - /// - /// This is allowed to have false positives; just because this says a block - /// is reachable doesn't mean that's necessarily true. It's thus always - /// legal for this to return a filled set. - /// - /// Regardless, the [`BitSet::domain_size`] of the returned set will always - /// exactly match the number of blocks in the body so that `contains` - /// checks can be done without worrying about panicking. - /// - /// This is mostly useful because it lets us skip lowering the `false` side - /// of `if <T as Trait>::CONST`, as well as `intrinsics::debug_assertions`. - pub fn reachable_blocks_in_mono( - &self, - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - ) -> BitSet<BasicBlock> { - let mut set = BitSet::new_empty(self.basic_blocks.len()); - self.reachable_blocks_in_mono_from(tcx, instance, &mut set, START_BLOCK); - set - } - - fn reachable_blocks_in_mono_from( - &self, - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - set: &mut BitSet<BasicBlock>, - bb: BasicBlock, - ) { - if !set.insert(bb) { - return; - } - - let data = &self.basic_blocks[bb]; - - if let Some((bits, targets)) = Self::try_const_mono_switchint(tcx, instance, data) { - let target = targets.target_for_value(bits); - ensure_sufficient_stack(|| { - self.reachable_blocks_in_mono_from(tcx, instance, set, target) - }); - return; - } - - for target in data.terminator().successors() { - ensure_sufficient_stack(|| { - self.reachable_blocks_in_mono_from(tcx, instance, set, target) - }); - } - } - /// If this basic block ends with a [`TerminatorKind::SwitchInt`] for which we can evaluate the /// dimscriminant in monomorphization, we return the discriminant bits and the /// [`SwitchTargets`], just so the caller doesn't also have to match on the terminator. diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index f116347cc2b..58a27c1f9ef 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -85,6 +85,12 @@ impl SwitchTargets { self.values.push(value); self.targets.insert(self.targets.len() - 1, bb); } + + /// Returns true if all targets (including the fallback target) are distinct. + #[inline] + pub fn is_distinct(&self) -> bool { + self.targets.iter().collect::<FxHashSet<_>>().len() == self.targets.len() + } } pub struct SwitchTargetsIter<'a> { diff --git a/compiler/rustc_middle/src/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs index 0a938bcd315..245e9096bad 100644 --- a/compiler/rustc_middle/src/mir/traversal.rs +++ b/compiler/rustc_middle/src/mir/traversal.rs @@ -245,7 +245,7 @@ pub fn reachable<'a, 'tcx>( /// Returns a `BitSet` containing all basic blocks reachable from the `START_BLOCK`. pub fn reachable_as_bitset(body: &Body<'_>) -> BitSet<BasicBlock> { let mut iter = preorder(body); - iter.by_ref().for_each(drop); + while let Some(_) = iter.next() {} iter.visited } @@ -279,3 +279,97 @@ pub fn reverse_postorder<'a, 'tcx>( { body.basic_blocks.reverse_postorder().iter().map(|&bb| (bb, &body.basic_blocks[bb])) } + +/// Traversal of a [`Body`] that tries to avoid unreachable blocks in a monomorphized [`Instance`]. +/// +/// This is allowed to have false positives; blocks may be visited even if they are not actually +/// reachable. +/// +/// Such a traversal is mostly useful because it lets us skip lowering the `false` side +/// of `if <T as Trait>::CONST`, as well as [`NullOp::UbChecks`]. +/// +/// [`NullOp::UbChecks`]: rustc_middle::mir::NullOp::UbChecks +pub fn mono_reachable<'a, 'tcx>( + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, +) -> MonoReachable<'a, 'tcx> { + MonoReachable::new(body, tcx, instance) +} + +/// [`MonoReachable`] internally accumulates a [`BitSet`] of visited blocks. This is just a +/// convenience function to run that traversal then extract its set of reached blocks. +pub fn mono_reachable_as_bitset<'a, 'tcx>( + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, +) -> BitSet<BasicBlock> { + let mut iter = mono_reachable(body, tcx, instance); + while let Some(_) = iter.next() {} + iter.visited +} + +pub struct MonoReachable<'a, 'tcx> { + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + visited: BitSet<BasicBlock>, + // Other traversers track their worklist in a Vec. But we don't care about order, so we can + // store ours in a BitSet and thus save allocations because BitSet has a small size + // optimization. + worklist: BitSet<BasicBlock>, +} + +impl<'a, 'tcx> MonoReachable<'a, 'tcx> { + pub fn new( + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + ) -> MonoReachable<'a, 'tcx> { + let mut worklist = BitSet::new_empty(body.basic_blocks.len()); + worklist.insert(START_BLOCK); + MonoReachable { + body, + tcx, + instance, + visited: BitSet::new_empty(body.basic_blocks.len()), + worklist, + } + } + + fn add_work(&mut self, blocks: impl IntoIterator<Item = BasicBlock>) { + for block in blocks.into_iter() { + if !self.visited.contains(block) { + self.worklist.insert(block); + } + } + } +} + +impl<'a, 'tcx> Iterator for MonoReachable<'a, 'tcx> { + type Item = (BasicBlock, &'a BasicBlockData<'tcx>); + + fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> { + while let Some(idx) = self.worklist.iter().next() { + self.worklist.remove(idx); + if !self.visited.insert(idx) { + continue; + } + + let data = &self.body[idx]; + + if let Some((bits, targets)) = + Body::try_const_mono_switchint(self.tcx, self.instance, data) + { + let target = targets.target_for_value(bits); + self.add_work([target]); + } else { + self.add_work(data.terminator().successors()); + } + + return Some((idx, data)); + } + + None + } +} diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 8f02b3121ac..db1b5a74f0a 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -492,9 +492,7 @@ impl<'a, 'tcx> CacheDecoder<'a, 'tcx> { // expansion, so we use `import_source_files` to ensure that the foreign // source files are actually imported before we call `source_file_by_stable_id`. if source_file_cnum != LOCAL_CRATE { - self.tcx - .cstore_untracked() - .import_source_files(self.tcx.sess, source_file_cnum); + self.tcx.import_source_files(source_file_cnum); } source_map @@ -634,12 +632,7 @@ impl<'a, 'tcx> SpanDecoder for CacheDecoder<'a, 'tcx> { expn_id } else { let index_guess = self.foreign_expn_data[&hash]; - self.tcx.cstore_untracked().expn_hash_to_expn_id( - self.tcx.sess, - krate, - index_guess, - hash, - ) + self.tcx.expn_hash_to_expn_id(krate, index_guess, hash) }; debug_assert_eq!(expn_id.krate, krate); diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 8cb4ee7bd41..6555a687152 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -338,10 +338,11 @@ macro_rules! define_callbacks { pub type Storage<'tcx> = <$($K)* as keys::Key>::Cache<Erase<$V>>; - // Ensure that keys grow no larger than 64 bytes + // Ensure that keys grow no larger than 72 bytes by accident. + // Increase this limit if necessary, but do try to keep the size low if possible #[cfg(all(any(target_arch = "x86_64", target_arch="aarch64"), target_pointer_width = "64"))] const _: () = { - if mem::size_of::<Key<'static>>() > 64 { + if mem::size_of::<Key<'static>>() > 72 { panic!("{}", concat!( "the query `", stringify!($name), @@ -352,7 +353,8 @@ macro_rules! define_callbacks { } }; - // Ensure that values grow no larger than 64 bytes + // Ensure that values grow no larger than 64 bytes by accident. + // Increase this limit if necessary, but do try to keep the size low if possible #[cfg(all(any(target_arch = "x86_64", target_arch="aarch64"), target_pointer_width = "64"))] const _: () = { if mem::size_of::<Value<'static>>() > 64 { diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index ee816791919..d51e86c909c 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -12,8 +12,8 @@ pub mod util; use crate::infer::canonical::Canonical; use crate::mir::ConstraintCategory; use crate::ty::abstract_const::NotConstEvaluatable; +use crate::ty::GenericArgsRef; use crate::ty::{self, AdtKind, Ty}; -use crate::ty::{GenericArgsRef, TyCtxt}; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, Diag, EmissionGuarantee}; @@ -997,33 +997,3 @@ pub enum CodegenObligationError { Unimplemented, FulfillmentError, } - -/// Defines the treatment of opaque types in a given inference context. -/// -/// This affects both what opaques are allowed to be defined, but also whether -/// opaques are replaced with inference vars eagerly in the old solver (e.g. -/// in projection, and in the signature during function type-checking). -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)] -pub enum DefiningAnchor<'tcx> { - /// Define opaques which are in-scope of the current item being analyzed. - /// Also, eagerly replace these opaque types in `replace_opaque_types_with_inference_vars`. - /// - /// If the list is empty, do not allow any opaques to be defined. This is used to catch type mismatch - /// errors when handling opaque types, and also should be used when we would - /// otherwise reveal opaques (such as [`Reveal::All`] reveal mode). - Bind(&'tcx ty::List<LocalDefId>), - /// In contexts where we don't currently know what opaques are allowed to be - /// defined, such as (old solver) canonical queries, we will simply allow - /// opaques to be defined, but "bubble" them up in the canonical response or - /// otherwise treat them to be handled later. - /// - /// We do not eagerly replace opaque types in `replace_opaque_types_with_inference_vars`, - /// which may affect what predicates pass and fail in the old trait solver. - Bubble, -} - -impl<'tcx> DefiningAnchor<'tcx> { - pub fn bind(tcx: TyCtxt<'tcx>, item: LocalDefId) -> Self { - Self::Bind(tcx.opaque_types_defined_by(item)) - } -} diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index 6dcea2aaff1..13504c6ae93 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -4,7 +4,7 @@ use rustc_span::def_id::DefId; use crate::infer::canonical::{CanonicalVarValues, QueryRegionConstraints}; use crate::traits::query::NoSolution; -use crate::traits::{Canonical, DefiningAnchor}; +use crate::traits::Canonical; use crate::ty::{ self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, @@ -114,7 +114,6 @@ impl MaybeCause { #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)] pub struct QueryInput<'tcx, T> { pub goal: Goal<'tcx, T>, - pub anchor: DefiningAnchor<'tcx>, pub predefined_opaques_in_body: PredefinedOpaques<'tcx>, } diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 7db64504f85..95d1e08b58b 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -45,7 +45,7 @@ impl UpvarId { /// Information describing the capture of an upvar. This is computed /// during `typeck`, specifically by `regionck`. -#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)] +#[derive(Eq, PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable, Hash)] #[derive(TypeFoldable, TypeVisitable)] pub enum UpvarCapture { /// Upvar is captured by value. This is always true when the @@ -73,7 +73,7 @@ pub type RootVariableMinCaptureList<'tcx> = FxIndexMap<hir::HirId, MinCaptureLis pub type MinCaptureList<'tcx> = Vec<CapturedPlace<'tcx>>; /// A composite describing a `Place` that is captured by a closure. -#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +#[derive(Eq, PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable, Hash)] #[derive(TypeFoldable, TypeVisitable)] pub struct CapturedPlace<'tcx> { /// Name and span where the binding happens. @@ -192,7 +192,7 @@ impl<'tcx> CapturedPlace<'tcx> { #[derive(Copy, Clone, Debug, HashStable)] pub struct ClosureTypeInfo<'tcx> { user_provided_sig: ty::CanonicalPolyFnSig<'tcx>, - captures: &'tcx [&'tcx ty::CapturedPlace<'tcx>], + captures: &'tcx ty::List<&'tcx ty::CapturedPlace<'tcx>>, kind_origin: Option<&'tcx (Span, HirPlace<'tcx>)>, } @@ -201,7 +201,7 @@ fn closure_typeinfo<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ClosureTypeInfo let typeck_results = tcx.typeck(def); let user_provided_sig = typeck_results.user_provided_sigs[&def]; let captures = typeck_results.closure_min_captures_flattened(def); - let captures = tcx.arena.alloc_from_iter(captures); + let captures = tcx.mk_captures_from_iter(captures); let hir_id = tcx.local_def_id_to_hir_id(def); let kind_origin = typeck_results.closure_kind_origins().get(hir_id); ClosureTypeInfo { user_provided_sig, captures, kind_origin } @@ -253,7 +253,7 @@ pub fn is_ancestor_or_same_capture( /// Part of `MinCaptureInformationMap`; describes the capture kind (&, &mut, move) /// for a particular capture as well as identifying the part of the source code /// that triggered this capture to occur. -#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)] +#[derive(Eq, PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable, Hash)] #[derive(TypeFoldable, TypeVisitable)] pub struct CaptureInfo { /// Expr Id pointing to use that resulted in selecting the current capture kind @@ -332,7 +332,7 @@ pub fn place_to_string_for_capture<'tcx>(tcx: TyCtxt<'tcx>, place: &HirPlace<'tc curr_string } -#[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, Copy, HashStable)] +#[derive(Eq, Clone, PartialEq, Debug, TyEncodable, TyDecodable, Copy, HashStable, Hash)] #[derive(TypeFoldable, TypeVisitable)] pub enum BorrowKind { /// Data must be immutable and is aliasable. diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 9068961d736..c0effe9804c 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -148,6 +148,12 @@ impl<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>> Encodable<E> for ty::Const<'tcx> { } } +impl<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>> Encodable<E> for ty::Pattern<'tcx> { + fn encode(&self, e: &mut E) { + self.0.0.encode(e); + } +} + impl<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>> Encodable<E> for ConstAllocation<'tcx> { fn encode(&self, e: &mut E) { self.inner().encode(e) @@ -364,6 +370,12 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for ty::Const<'tcx> { } } +impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for ty::Pattern<'tcx> { + fn decode(decoder: &mut D) -> Self { + decoder.interner().mk_pat(Decodable::decode(decoder)) + } +} + impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for [ty::ValTree<'tcx>] { fn decode(decoder: &mut D) -> &'tcx Self { decoder @@ -443,6 +455,12 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for ty::List<Lo } } +impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for &'tcx ty::List<LocalDefId> { + fn decode(d: &mut D) -> Self { + RefDecodable::decode(d) + } +} + impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for ty::List<(VariantIdx, FieldIdx)> { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 1db9bce73a6..0daf83162db 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -26,9 +26,10 @@ use crate::traits::solve::{ }; use crate::ty::{ self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, ConstData, - GenericParamDefKind, ImplPolarity, List, ListWithCachedTypeInfo, ParamConst, ParamTy, - PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, PredicatePolarity, Region, - RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, TypeVisitable, Visibility, + GenericParamDefKind, ImplPolarity, List, ListWithCachedTypeInfo, ParamConst, ParamTy, Pattern, + PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, PredicatePolarity, + Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, TypeVisitable, + Visibility, }; use crate::ty::{GenericArg, GenericArgs, GenericArgsRef}; use rustc_ast::{self as ast, attr}; @@ -84,6 +85,7 @@ use std::ops::{Bound, Deref}; #[allow(rustc::usage_of_ty_tykind)] impl<'tcx> Interner for TyCtxt<'tcx> { type DefId = DefId; + type DefiningOpaqueTypes = &'tcx ty::List<LocalDefId>; type AdtDef = ty::AdtDef<'tcx>; type GenericArgs = ty::GenericArgsRef<'tcx>; type GenericArg = ty::GenericArg<'tcx>; @@ -95,6 +97,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type CanonicalVars = CanonicalVarInfos<'tcx>; type Ty = Ty<'tcx>; + type Pat = Pattern<'tcx>; type Tys = &'tcx List<Ty<'tcx>>; type AliasTy = ty::AliasTy<'tcx>; type ParamTy = ParamTy; @@ -157,6 +160,7 @@ pub struct CtxtInterners<'tcx> { projs: InternedSet<'tcx, List<ProjectionKind>>, place_elems: InternedSet<'tcx, List<PlaceElem<'tcx>>>, const_: InternedSet<'tcx, WithCachedTypeInfo<ConstData<'tcx>>>, + pat: InternedSet<'tcx, PatternKind<'tcx>>, const_allocation: InternedSet<'tcx, Allocation>, bound_variable_kinds: InternedSet<'tcx, List<ty::BoundVariableKind>>, layout: InternedSet<'tcx, LayoutS<FieldIdx, VariantIdx>>, @@ -165,6 +169,7 @@ pub struct CtxtInterners<'tcx> { predefined_opaques_in_body: InternedSet<'tcx, PredefinedOpaquesData<'tcx>>, fields: InternedSet<'tcx, List<FieldIdx>>, local_def_ids: InternedSet<'tcx, List<LocalDefId>>, + captures: InternedSet<'tcx, List<&'tcx ty::CapturedPlace<'tcx>>>, offset_of: InternedSet<'tcx, List<(VariantIdx, FieldIdx)>>, } @@ -184,6 +189,7 @@ impl<'tcx> CtxtInterners<'tcx> { projs: Default::default(), place_elems: Default::default(), const_: Default::default(), + pat: Default::default(), const_allocation: Default::default(), bound_variable_kinds: Default::default(), layout: Default::default(), @@ -192,6 +198,7 @@ impl<'tcx> CtxtInterners<'tcx> { predefined_opaques_in_body: Default::default(), fields: Default::default(), local_def_ids: Default::default(), + captures: Default::default(), offset_of: Default::default(), } } @@ -1158,11 +1165,7 @@ impl<'tcx> TyCtxt<'tcx> { .local_def_path_hash_to_def_id(hash, err_msg) .to_def_id() } else { - // If this is a DefPathHash from an upstream crate, let the CrateStore map - // it to a DefId. - let cstore = &*self.cstore_untracked(); - let cnum = cstore.stable_crate_id_to_crate_num(stable_crate_id); - cstore.def_path_hash_to_def_id(cnum, hash) + self.def_path_hash_to_def_id_extern(hash, stable_crate_id) } } @@ -1578,6 +1581,7 @@ macro_rules! nop_list_lift { nop_lift! {type_; Ty<'a> => Ty<'tcx>} nop_lift! {region; Region<'a> => Region<'tcx>} nop_lift! {const_; Const<'a> => Const<'tcx>} +nop_lift! {pat; Pattern<'a> => Pattern<'tcx>} nop_lift! {const_allocation; ConstAllocation<'a> => ConstAllocation<'tcx>} nop_lift! {predicate; Predicate<'a> => Predicate<'tcx>} nop_lift! {predicate; Clause<'a> => Clause<'tcx>} @@ -1715,6 +1719,7 @@ impl<'tcx> TyCtxt<'tcx> { Param, Infer, Alias, + Pat, Foreign )?; @@ -1866,6 +1871,7 @@ macro_rules! direct_interners { // crate only, and have a corresponding `mk_` function. direct_interners! { region: pub(crate) intern_region(RegionKind<'tcx>): Region -> Region<'tcx>, + pat: pub mk_pat(PatternKind<'tcx>): Pattern -> Pattern<'tcx>, const_allocation: pub mk_const_alloc(Allocation): ConstAllocation -> ConstAllocation<'tcx>, layout: pub mk_layout(LayoutS<FieldIdx, VariantIdx>): Layout -> Layout<'tcx>, adt_def: pub mk_adt_def_from_data(AdtDefData): AdtDef -> AdtDef<'tcx>, @@ -1905,6 +1911,7 @@ slice_interners!( bound_variable_kinds: pub mk_bound_variable_kinds(ty::BoundVariableKind), fields: pub mk_fields(FieldIdx), local_def_ids: intern_local_def_ids(LocalDefId), + captures: intern_captures(&'tcx ty::CapturedPlace<'tcx>), offset_of: pub mk_offset_of((VariantIdx, FieldIdx)), ); @@ -2226,6 +2233,17 @@ impl<'tcx> TyCtxt<'tcx> { T::collect_and_apply(iter, |xs| self.mk_local_def_ids(xs)) } + pub fn mk_captures_from_iter<I, T>(self, iter: I) -> T::Output + where + I: Iterator<Item = T>, + T: CollectAndApply< + &'tcx ty::CapturedPlace<'tcx>, + &'tcx List<&'tcx ty::CapturedPlace<'tcx>>, + >, + { + T::collect_and_apply(iter, |xs| self.intern_captures(xs)) + } + pub fn mk_const_list_from_iter<I, T>(self, iter: I) -> T::Output where I: Iterator<Item = T>, diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 09586a95f1c..ce85c28ece8 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -285,6 +285,7 @@ impl<'tcx> Ty<'tcx> { ty::Adt(def, _) => def.descr().into(), ty::Foreign(_) => "extern type".into(), ty::Array(..) => "array".into(), + ty::Pat(..) => "pattern type".into(), ty::Slice(_) => "slice".into(), ty::RawPtr(_, _) => "raw pointer".into(), ty::Ref(.., mutbl) => match mutbl { diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs index 5b257cdfd86..7c925d5fbb6 100644 --- a/compiler/rustc_middle/src/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -120,6 +120,7 @@ pub fn simplify_type<'tcx>( ty::Str => Some(SimplifiedType::Str), ty::Array(..) => Some(SimplifiedType::Array), ty::Slice(..) => Some(SimplifiedType::Slice), + ty::Pat(ty, ..) => simplify_type(tcx, ty, treat_params), ty::RawPtr(_, mutbl) => Some(SimplifiedType::Ptr(mutbl)), ty::Dynamic(trait_info, ..) => match trait_info.principal_def_id() { Some(principal_def_id) if !tcx.trait_is_auto(principal_def_id) => { @@ -231,6 +232,7 @@ impl DeepRejectCtxt { | ty::Slice(..) | ty::RawPtr(..) | ty::Dynamic(..) + | ty::Pat(..) | ty::Ref(..) | ty::Never | ty::Tuple(..) @@ -269,6 +271,10 @@ impl DeepRejectCtxt { } _ => false, }, + ty::Pat(obl_ty, _) => { + // FIXME(pattern_types): take pattern into account + matches!(k, &ty::Pat(impl_ty, _) if self.types_may_unify(obl_ty, impl_ty)) + } ty::Slice(obl_ty) => { matches!(k, &ty::Slice(impl_ty) if self.types_may_unify(obl_ty, impl_ty)) } diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 5feb6ef76d5..0dc835671d5 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -218,6 +218,20 @@ impl FlagComputation { self.add_const(len); } + &ty::Pat(ty, pat) => { + self.add_ty(ty); + match *pat { + ty::PatternKind::Range { start, end, include_end: _ } => { + if let Some(start) = start { + self.add_const(start) + } + if let Some(end) = end { + self.add_const(end) + } + } + } + } + &ty::Slice(tt) => self.add_ty(tt), &ty::RawPtr(ty, _) => { diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 2630b96869b..e984f543701 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -316,11 +316,11 @@ impl<'tcx> Generics { /// of this item, excluding `Self`. /// /// **This should only be used for diagnostics purposes.** - pub fn own_args_no_defaults( + pub fn own_args_no_defaults<'a>( &'tcx self, tcx: TyCtxt<'tcx>, - args: &'tcx [ty::GenericArg<'tcx>], - ) -> &'tcx [ty::GenericArg<'tcx>] { + args: &'a [ty::GenericArg<'tcx>], + ) -> &'a [ty::GenericArg<'tcx>] { let mut own_params = self.parent_count..self.count(); if self.has_self && self.parent.is_none() { own_params.start = 1; diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 4a7720b38f8..f8f59fbeab4 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -520,7 +520,10 @@ impl<'tcx> Instance<'tcx> { // Reify `Trait::method` implementations if KCFI is enabled // FIXME(maurer) only reify it if it is a vtable-safe function _ if tcx.sess.is_sanitizer_kcfi_enabled() - && tcx.associated_item(def_id).trait_item_def_id.is_some() => + && tcx + .opt_associated_item(def_id) + .and_then(|assoc| assoc.trait_item_def_id) + .is_some() => { // If this function could also go in a vtable, we need to `ReifyShim` it with // KCFI because it can only attach one type per function. diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 66078663098..50e68bfdbe7 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -742,6 +742,7 @@ where | ty::FnDef(..) | ty::CoroutineWitness(..) | ty::Foreign(..) + | ty::Pat(_, _) | ty::Dynamic(_, _, ty::Dyn) => { bug!("TyAndLayout::field({:?}): not applicable", this) } diff --git a/compiler/rustc_middle/src/ty/list.rs b/compiler/rustc_middle/src/ty/list.rs index 04e165c8a73..0db541899d2 100644 --- a/compiler/rustc_middle/src/ty/list.rs +++ b/compiler/rustc_middle/src/ty/list.rs @@ -55,6 +55,12 @@ struct ListSkeleton<H, T> { data: [T; 0], } +impl<T> Default for &List<T> { + fn default() -> Self { + List::empty() + } +} + extern "C" { /// A dummy type used to force `List` to be unsized while not requiring /// references to it be wide pointers. diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 9bf5205edaa..ee4dc9744ac 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -91,6 +91,7 @@ pub use self::context::{ pub use self::instance::{Instance, InstanceDef, ReifyReason, ShortInstance, UnusedGenericParams}; pub use self::list::{List, ListWithCachedTypeInfo}; pub use self::parameterized::ParameterizedOverTcx; +pub use self::pattern::{Pattern, PatternKind}; pub use self::predicate::{ Clause, ClauseKind, CoercePredicate, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, NormalizesTo, OutlivesPredicate, PolyCoercePredicate, @@ -130,6 +131,7 @@ pub mod fold; pub mod inhabitedness; pub mod layout; pub mod normalize_erasing_regions; +pub mod pattern; pub mod print; pub mod relate; pub mod trait_def; diff --git a/compiler/rustc_middle/src/ty/pattern.rs b/compiler/rustc_middle/src/ty/pattern.rs new file mode 100644 index 00000000000..8a41ba257ec --- /dev/null +++ b/compiler/rustc_middle/src/ty/pattern.rs @@ -0,0 +1,48 @@ +use std::fmt; + +use crate::ty; +use rustc_data_structures::intern::Interned; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable)] +#[rustc_pass_by_value] +pub struct Pattern<'tcx>(pub Interned<'tcx, PatternKind<'tcx>>); + +impl<'tcx> std::ops::Deref for Pattern<'tcx> { + type Target = PatternKind<'tcx>; + + fn deref(&self) -> &Self::Target { + &*self.0 + } +} + +impl<'tcx> fmt::Debug for Pattern<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", **self) + } +} + +impl<'tcx> fmt::Debug for PatternKind<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + PatternKind::Range { start, end, include_end } => { + if let Some(start) = start { + write!(f, "{start}")?; + } + write!(f, "..")?; + if include_end { + write!(f, "=")?; + } + if let Some(end) = end { + write!(f, "{end}")?; + } + Ok(()) + } + } + } +} + +#[derive(Clone, PartialEq, Eq, Hash)] +#[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)] +pub enum PatternKind<'tcx> { + Range { start: Option<ty::Const<'tcx>>, end: Option<ty::Const<'tcx>>, include_end: bool }, +} diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index d9aa7f9e5c4..9d0e1123e43 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -259,7 +259,7 @@ fn characteristic_def_id_of_type_cached<'a>( ty::Dynamic(data, ..) => data.principal_def_id(), - ty::Array(subty, _) | ty::Slice(subty) => { + ty::Pat(subty, _) | ty::Array(subty, _) | ty::Slice(subty) => { characteristic_def_id_of_type_cached(subty, visited) } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 2a898430ce9..20ebd87c3d4 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -667,6 +667,9 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::Int(t) => p!(write("{}", t.name_str())), ty::Uint(t) => p!(write("{}", t.name_str())), ty::Float(t) => p!(write("{}", t.name_str())), + ty::Pat(ty, pat) => { + p!("(", print(ty), ") is ", write("{pat:?}")) + } ty::RawPtr(ty, mutbl) => { p!(write( "*{} ", diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index cf7caafcebb..3c1dea1d9f2 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -13,6 +13,8 @@ use rustc_hir::def_id::DefId; use rustc_target::spec::abi; use std::iter; +use super::Pattern; + pub type RelateResult<'tcx, T> = Result<T, TypeError<'tcx>>; pub trait TypeRelation<'tcx>: Sized { @@ -351,6 +353,36 @@ impl<'tcx> Relate<'tcx> for Ty<'tcx> { } } +impl<'tcx> Relate<'tcx> for Pattern<'tcx> { + #[inline] + fn relate<R: TypeRelation<'tcx>>( + relation: &mut R, + a: Self, + b: Self, + ) -> RelateResult<'tcx, Self> { + match (&*a, &*b) { + ( + &ty::PatternKind::Range { start: start_a, end: end_a, include_end: inc_a }, + &ty::PatternKind::Range { start: start_b, end: end_b, include_end: inc_b }, + ) => { + // FIXME(pattern_types): make equal patterns equal (`0..=` is the same as `..=`). + let mut relate_opt_const = |a, b| match (a, b) { + (None, None) => Ok(None), + (Some(a), Some(b)) => relation.relate(a, b).map(Some), + // FIXME(pattern_types): report a better error + _ => Err(TypeError::Mismatch), + }; + let start = relate_opt_const(start_a, start_b)?; + let end = relate_opt_const(end_a, end_b)?; + if inc_a != inc_b { + todo!() + } + Ok(relation.tcx().mk_pat(ty::PatternKind::Range { start, end, include_end: inc_a })) + } + } + } +} + /// Relates `a` and `b` structurally, calling the relation for all nested values. /// Any semantic equality, e.g. of projections, and inference variables have to be /// handled by the caller. @@ -533,6 +565,12 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>( Ok(Ty::new_alias(tcx, a_kind, alias_ty)) } + (&ty::Pat(a_ty, a_pat), &ty::Pat(b_ty, b_pat)) => { + let ty = relation.relate(a_ty, b_ty)?; + let pat = relation.relate(a_pat, b_pat)?; + Ok(Ty::new_pat(tcx, ty, pat)) + } + _ => Err(TypeError::Sorts(expected_found(a, b))), } } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 8231c0214cb..90c68e7ddfc 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -20,6 +20,8 @@ use std::fmt::{self, Debug}; use super::print::PrettyPrinter; use super::{GenericArg, GenericArgKind, Region}; +use super::Pattern; + impl fmt::Debug for ty::TraitDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ty::tls::with(|tcx| { @@ -210,6 +212,22 @@ impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for AliasTy<'tcx> { } } +impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for Pattern<'tcx> { + fn fmt<Infcx: InferCtxtLike<Interner = TyCtxt<'tcx>>>( + this: WithInfcx<'_, Infcx, &Self>, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + match &**this.data { + ty::PatternKind::Range { start, end, include_end } => f + .debug_struct("Pattern::Range") + .field("start", start) + .field("end", end) + .field("include_end", include_end) + .finish(), + } + } +} + impl<'tcx> fmt::Debug for ty::consts::Expr<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { WithInfcx::with_no_infcx(self).fmt(f) @@ -541,6 +559,22 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for &'tcx ty::List<ty::Const<'tcx>> { } } +impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for Pattern<'tcx> { + fn try_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + let pat = (*self).clone().try_fold_with(folder)?; + Ok(if pat == *self { self } else { folder.interner().mk_pat(pat) }) + } +} + +impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for Pattern<'tcx> { + fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result { + (**self).visit_with(visitor) + } +} + impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for Ty<'tcx> { fn try_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>( self, @@ -586,6 +620,7 @@ impl<'tcx> TypeSuperFoldable<TyCtxt<'tcx>> for Ty<'tcx> { ty::CoroutineClosure(did, args.try_fold_with(folder)?) } ty::Alias(kind, data) => ty::Alias(kind, data.try_fold_with(folder)?), + ty::Pat(ty, pat) => ty::Pat(ty.try_fold_with(folder)?, pat.try_fold_with(folder)?), ty::Bool | ty::Char @@ -633,6 +668,11 @@ impl<'tcx> TypeSuperVisitable<TyCtxt<'tcx>> for Ty<'tcx> { ty::CoroutineClosure(_did, ref args) => args.visit_with(visitor), ty::Alias(_, ref data) => data.visit_with(visitor), + ty::Pat(ty, pat) => { + try_visit!(ty.visit_with(visitor)); + pat.visit_with(visitor) + } + ty::Bool | ty::Char | ty::Str diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 2ab63f01e7c..d9e99bf07af 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1523,6 +1523,11 @@ impl<'tcx> Ty<'tcx> { } #[inline] + pub fn new_pat(tcx: TyCtxt<'tcx>, base: Ty<'tcx>, pat: ty::Pattern<'tcx>) -> Ty<'tcx> { + Ty::new(tcx, Pat(base, pat)) + } + + #[inline] pub fn new_opaque(tcx: TyCtxt<'tcx>, def_id: DefId, args: GenericArgsRef<'tcx>) -> Ty<'tcx> { Ty::new_alias(tcx, ty::Opaque, AliasTy::new(tcx, def_id, args)) } @@ -2278,6 +2283,8 @@ impl<'tcx> Ty<'tcx> { Ty::new_projection(tcx, assoc_items[0], tcx.mk_args(&[self.into()])) } + ty::Pat(ty, _) => ty.discriminant_ty(tcx), + ty::Bool | ty::Char | ty::Int(_) @@ -2359,6 +2366,7 @@ impl<'tcx> Ty<'tcx> { ty::Param(_) | ty::Alias(..) => Err(tail), ty::Infer(ty::TyVar(_)) + | ty::Pat(..) | ty::Bound(..) | ty::Placeholder(..) | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!( @@ -2495,6 +2503,7 @@ impl<'tcx> Ty<'tcx> { | ty::Coroutine(..) | ty::CoroutineWitness(..) | ty::Array(..) + | ty::Pat(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never @@ -2549,6 +2558,8 @@ impl<'tcx> Ty<'tcx> { field_tys.len() <= 3 && field_tys.iter().all(Self::is_trivially_pure_clone_copy) } + ty::Pat(ty, _) => ty.is_trivially_pure_clone_copy(), + // Sometimes traits aren't implemented for every ABI or arity, // because we can't be generic over everything yet. ty::FnPtr(..) => false, @@ -2630,6 +2641,7 @@ impl<'tcx> Ty<'tcx> { | Foreign(_) | Str | Array(_, _) + | Pat(_, _) | Slice(_) | RawPtr(_, _) | Ref(_, _, _) diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index cef15b29a85..e422fb0d020 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -245,6 +245,11 @@ impl<'tcx> TyCtxt<'tcx> { ty::Tuple(_) => break, + ty::Pat(inner, _) => { + f(); + ty = inner; + } + ty::Alias(..) => { let normalized = normalize(ty); if ty == normalized { @@ -1242,7 +1247,7 @@ impl<'tcx> Ty<'tcx> { | ty::Error(_) | ty::FnPtr(_) => true, ty::Tuple(fields) => fields.iter().all(Self::is_trivially_freeze), - ty::Slice(elem_ty) | ty::Array(elem_ty, _) => elem_ty.is_trivially_freeze(), + ty::Pat(ty, _) | ty::Slice(ty) | ty::Array(ty, _) => ty.is_trivially_freeze(), ty::Adt(..) | ty::Bound(..) | ty::Closure(..) @@ -1282,7 +1287,7 @@ impl<'tcx> Ty<'tcx> { | ty::Error(_) | ty::FnPtr(_) => true, ty::Tuple(fields) => fields.iter().all(Self::is_trivially_unpin), - ty::Slice(elem_ty) | ty::Array(elem_ty, _) => elem_ty.is_trivially_unpin(), + ty::Pat(ty, _) | ty::Slice(ty) | ty::Array(ty, _) => ty.is_trivially_unpin(), ty::Adt(..) | ty::Bound(..) | ty::Closure(..) @@ -1398,7 +1403,7 @@ impl<'tcx> Ty<'tcx> { // // Because this function is "shallow", we return `true` for these composites regardless // of the type(s) contained within. - ty::Ref(..) | ty::Array(..) | ty::Slice(_) | ty::Tuple(..) => true, + ty::Pat(..) | ty::Ref(..) | ty::Array(..) | ty::Slice(_) | ty::Tuple(..) => true, // Raw pointers use bitwise comparison. ty::RawPtr(_, _) | ty::FnPtr(_) => true, @@ -1528,7 +1533,7 @@ pub fn needs_drop_components<'tcx>( ty::Dynamic(..) | ty::Error(_) => Err(AlwaysRequiresDrop), - ty::Slice(ty) => needs_drop_components(tcx, ty), + ty::Pat(ty, _) | ty::Slice(ty) => needs_drop_components(tcx, ty), ty::Array(elem_ty, size) => { match needs_drop_components(tcx, elem_ty) { Ok(v) if v.is_empty() => Ok(v), @@ -1597,7 +1602,7 @@ pub fn is_trivially_const_drop(ty: Ty<'_>) -> bool { | ty::CoroutineWitness(..) | ty::Adt(..) => false, - ty::Array(ty, _) | ty::Slice(ty) => is_trivially_const_drop(ty), + ty::Array(ty, _) | ty::Slice(ty) | ty::Pat(ty, _) => is_trivially_const_drop(ty), ty::Tuple(tys) => tys.iter().all(|ty| is_trivially_const_drop(ty)), } diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs index 9e7bf980237..7069bdcbcb9 100644 --- a/compiler/rustc_middle/src/ty/walk.rs +++ b/compiler/rustc_middle/src/ty/walk.rs @@ -151,6 +151,15 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) | ty::Bound(..) | ty::Foreign(..) => {} + ty::Pat(ty, pat) => { + match *pat { + ty::PatternKind::Range { start, end, include_end: _ } => { + stack.extend(end.map(Into::into)); + stack.extend(start.map(Into::into)); + } + } + stack.push(ty.into()); + } ty::Array(ty, len) => { stack.push(len.into()); stack.push(ty.into()); diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index ce39aff69cb..65c53be8ddd 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -457,7 +457,7 @@ impl<'tcx> ConstToPat<'tcx> { PatKind::Constant { value: mir::Const::Ty(ty::Const::new_value(tcx, cv, ty)) } } } - ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => { + ty::Pat(..) | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => { // The raw pointers we see here have been "vetted" by valtree construction to be // just integers, so we simply allow them. PatKind::Constant { value: mir::Const::Ty(ty::Const::new_value(tcx, cv, ty)) } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index e73d945e0bb..d7477309400 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -153,6 +153,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { | ty::Foreign(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) | ty::FnDef(_, _) | ty::FnPtr(_) @@ -193,6 +194,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { | ty::Foreign(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index de43f9faff9..320d8fd3977 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -58,16 +58,24 @@ //! borrowing from the outer closure, and we simply peel off a `deref` projection //! from them. This second body is stored alongside the first body, and optimized //! with it in lockstep. When we need to resolve a body for `FnOnce` or `AsyncFnOnce`, -//! we use this "by move" body instead. - -use itertools::Itertools; +//! we use this "by-move" body instead. +//! +//! ## How does this work? +//! +//! This pass essentially remaps the body of the (child) closure of the coroutine-closure +//! to take the set of upvars of the parent closure by value. This at least requires +//! changing a by-ref upvar to be by-value in the case that the outer coroutine-closure +//! captures something by value; however, it may also require renumbering field indices +//! in case precise captures (edition 2021 closure capture rules) caused the inner coroutine +//! to split one field capture into two. -use rustc_data_structures::unord::UnordSet; +use rustc_data_structures::unord::UnordMap; use rustc_hir as hir; +use rustc_middle::hir::place::{PlaceBase, Projection, ProjectionKind}; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::{self, dump_mir, MirPass}; use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt, TypeVisitableExt}; -use rustc_target::abi::FieldIdx; +use rustc_target::abi::{FieldIdx, VariantIdx}; pub struct ByMoveBody; @@ -116,32 +124,116 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { .tuple_fields() .len(); - let mut by_ref_fields = UnordSet::default(); - for (idx, (coroutine_capture, parent_capture)) in tcx + let mut field_remapping = UnordMap::default(); + + // One parent capture may correspond to several child captures if we end up + // refining the set of captures via edition-2021 precise captures. We want to + // match up any number of child captures with one parent capture, so we keep + // peeking off this `Peekable` until the child doesn't match anymore. + let mut parent_captures = + tcx.closure_captures(parent_def_id).iter().copied().enumerate().peekable(); + // Make sure we use every field at least once, b/c why are we capturing something + // if it's not used in the inner coroutine. + let mut field_used_at_least_once = false; + + for (child_field_idx, child_capture) in tcx .closure_captures(coroutine_def_id) .iter() + .copied() // By construction we capture all the args first. .skip(num_args) - .zip_eq(tcx.closure_captures(parent_def_id)) .enumerate() { - // This upvar is captured by-move from the parent closure, but by-ref - // from the inner async block. That means that it's being borrowed from - // the outer closure body -- we need to change the coroutine to take the - // upvar by value. - if coroutine_capture.is_by_ref() && !parent_capture.is_by_ref() { - assert_ne!( - coroutine_kind, - ty::ClosureKind::FnOnce, - "`FnOnce` coroutine-closures return coroutines that capture from \ - their body; it will always result in a borrowck error!" + loop { + let Some(&(parent_field_idx, parent_capture)) = parent_captures.peek() else { + bug!("we ran out of parent captures!") + }; + + let PlaceBase::Upvar(parent_base) = parent_capture.place.base else { + bug!("expected capture to be an upvar"); + }; + let PlaceBase::Upvar(child_base) = child_capture.place.base else { + bug!("expected capture to be an upvar"); + }; + + assert!( + child_capture.place.projections.len() >= parent_capture.place.projections.len() ); - by_ref_fields.insert(FieldIdx::from_usize(num_args + idx)); + // A parent matches a child they share the same prefix of projections. + // The child may have more, if it is capturing sub-fields out of + // something that is captured by-move in the parent closure. + if parent_base.var_path.hir_id != child_base.var_path.hir_id + || !std::iter::zip( + &child_capture.place.projections, + &parent_capture.place.projections, + ) + .all(|(child, parent)| child.kind == parent.kind) + { + // Make sure the field was used at least once. + assert!( + field_used_at_least_once, + "we captured {parent_capture:#?} but it was not used in the child coroutine?" + ); + field_used_at_least_once = false; + // Skip this field. + let _ = parent_captures.next().unwrap(); + continue; + } + + // Store this set of additional projections (fields and derefs). + // We need to re-apply them later. + let child_precise_captures = + &child_capture.place.projections[parent_capture.place.projections.len()..]; + + // If the parent captures by-move, and the child captures by-ref, then we + // need to peel an additional `deref` off of the body of the child. + let needs_deref = child_capture.is_by_ref() && !parent_capture.is_by_ref(); + if needs_deref { + assert_ne!( + coroutine_kind, + ty::ClosureKind::FnOnce, + "`FnOnce` coroutine-closures return coroutines that capture from \ + their body; it will always result in a borrowck error!" + ); + } + + // Finally, store the type of the parent's captured place. We need + // this when building the field projection in the MIR body later on. + let mut parent_capture_ty = parent_capture.place.ty(); + parent_capture_ty = match parent_capture.info.capture_kind { + ty::UpvarCapture::ByValue => parent_capture_ty, + ty::UpvarCapture::ByRef(kind) => Ty::new_ref( + tcx, + tcx.lifetimes.re_erased, + parent_capture_ty, + kind.to_mutbl_lossy(), + ), + }; + + field_remapping.insert( + FieldIdx::from_usize(child_field_idx + num_args), + ( + FieldIdx::from_usize(parent_field_idx + num_args), + parent_capture_ty, + needs_deref, + child_precise_captures, + ), + ); + + field_used_at_least_once = true; + break; } + } + + // Pop the last parent capture + if field_used_at_least_once { + let _ = parent_captures.next().unwrap(); + } + assert_eq!(parent_captures.next(), None, "leftover parent captures?"); - // Make sure we're actually talking about the same capture. - // FIXME(async_closures): We could look at the `hir::Upvar` instead? - assert_eq!(coroutine_capture.place.ty(), parent_capture.place.ty()); + if coroutine_kind == ty::ClosureKind::FnOnce { + assert_eq!(field_remapping.len(), tcx.closure_captures(parent_def_id).len()); + return; } let by_move_coroutine_ty = tcx @@ -157,7 +249,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { ); let mut by_move_body = body.clone(); - MakeByMoveBody { tcx, by_ref_fields, by_move_coroutine_ty }.visit_body(&mut by_move_body); + MakeByMoveBody { tcx, field_remapping, by_move_coroutine_ty }.visit_body(&mut by_move_body); dump_mir(tcx, false, "coroutine_by_move", &0, &by_move_body, |_, _| Ok(())); by_move_body.source = mir::MirSource::from_instance(InstanceDef::CoroutineKindShim { coroutine_def_id: coroutine_def_id.to_def_id(), @@ -168,7 +260,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { struct MakeByMoveBody<'tcx> { tcx: TyCtxt<'tcx>, - by_ref_fields: UnordSet<FieldIdx>, + field_remapping: UnordMap<FieldIdx, (FieldIdx, Ty<'tcx>, bool, &'tcx [Projection<'tcx>])>, by_move_coroutine_ty: Ty<'tcx>, } @@ -183,24 +275,59 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> { context: mir::visit::PlaceContext, location: mir::Location, ) { + // Initializing an upvar local always starts with `CAPTURE_STRUCT_LOCAL` and a + // field projection. If this is in `field_remapping`, then it must not be an + // arg from calling the closure, but instead an upvar. if place.local == ty::CAPTURE_STRUCT_LOCAL - && let Some((&mir::ProjectionElem::Field(idx, ty), projection)) = + && let Some((&mir::ProjectionElem::Field(idx, _), projection)) = place.projection.split_first() - && self.by_ref_fields.contains(&idx) + && let Some(&(remapped_idx, remapped_ty, needs_deref, additional_projections)) = + self.field_remapping.get(&idx) { - let (begin, end) = projection.split_first().unwrap(); - // FIXME(async_closures): I'm actually a bit surprised to see that we always - // initially deref the by-ref upvars. If this is not actually true, then we - // will at least get an ICE that explains why this isn't true :^) - assert_eq!(*begin, mir::ProjectionElem::Deref); - // Peel one ref off of the ty. - let peeled_ty = ty.builtin_deref(true).unwrap().ty; + // As noted before, if the parent closure captures a field by value, and + // the child captures a field by ref, then for the by-move body we're + // generating, we also are taking that field by value. Peel off a deref, + // since a layer of reffing has now become redundant. + let final_deref = if needs_deref { + let Some((mir::ProjectionElem::Deref, projection)) = projection.split_first() + else { + bug!( + "There should be at least a single deref for an upvar local initialization, found {projection:#?}" + ); + }; + // There may be more derefs, since we may also implicitly reborrow + // a captured mut pointer. + projection + } else { + projection + }; + + // The only thing that should be left is a deref, if the parent captured + // an upvar by-ref. + std::assert_matches::assert_matches!(final_deref, [] | [mir::ProjectionElem::Deref]); + + // For all of the additional projections that come out of precise capturing, + // re-apply these projections. + let additional_projections = + additional_projections.iter().map(|elem| match elem.kind { + ProjectionKind::Deref => mir::ProjectionElem::Deref, + ProjectionKind::Field(idx, VariantIdx::ZERO) => { + mir::ProjectionElem::Field(idx, elem.ty) + } + _ => unreachable!("precise captures only through fields and derefs"), + }); + + // We start out with an adjusted field index (and ty), representing the + // upvar that we get from our parent closure. We apply any of the additional + // projections to make sure that to the rest of the body of the closure, the + // place looks the same, and then apply that final deref if necessary. *place = mir::Place { local: place.local, projection: self.tcx.mk_place_elems_from_iter( - [mir::ProjectionElem::Field(idx, peeled_ty)] + [mir::ProjectionElem::Field(remapped_idx, remapped_ty)] .into_iter() - .chain(end.iter().copied()), + .chain(additional_projections) + .chain(final_deref.iter().copied()), ), }; } diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 3e9c1459f1c..d0f6ec8f21f 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -684,6 +684,7 @@ fn try_write_constant<'tcx>( // Unsupported for now. ty::Array(_, _) + | ty::Pat(_, _) // Do not attempt to support indirection in constants. | ty::Ref(..) | ty::RawPtr(..) | ty::FnPtr(..) | ty::Str | ty::Slice(_) diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index 0d600f0f937..9edb8bcee6e 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -1,6 +1,6 @@ use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{Ty, TyCtxt}; use std::fmt::Debug; use super::simplify::simplify_cfg; @@ -11,6 +11,7 @@ use super::simplify::simplify_cfg; /// let y: Option<()>; /// match (x,y) { /// (Some(_), Some(_)) => {0}, +/// (None, None) => {2}, /// _ => {1} /// } /// ``` @@ -23,10 +24,10 @@ use super::simplify::simplify_cfg; /// if discriminant_x == discriminant_y { /// match x { /// Some(_) => 0, -/// _ => 1, // <---- -/// } // | Actually the same bb -/// } else { // | -/// 1 // <-------------- +/// None => 2, +/// } +/// } else { +/// 1 /// } /// ``` /// @@ -47,18 +48,18 @@ use super::simplify::simplify_cfg; /// | | | /// ================= | | | /// | BBU | <-| | | ============================ -/// |---------------| | \-------> | BBD | -/// |---------------| | | |--------------------------| -/// | unreachable | | | | _dl = discriminant(P) | -/// ================= | | |--------------------------| -/// | | | switchInt(_dl) | -/// ================= | | | d | ---> BBD.2 +/// |---------------| \-------> | BBD | +/// |---------------| | |--------------------------| +/// | unreachable | | | _dl = discriminant(P) | +/// ================= | |--------------------------| +/// | | switchInt(_dl) | +/// ================= | | d | ---> BBD.2 /// | BB9 | <--------------- | otherwise | /// |---------------| ============================ /// | ... | /// ================= /// ``` -/// Where the `otherwise` branch on `BB1` is permitted to either go to `BBU` or to `BB9`. In the +/// Where the `otherwise` branch on `BB1` is permitted to either go to `BBU`. In the /// code: /// - `BB1` is `parent` and `BBC, BBD` are children /// - `P` is `child_place` @@ -78,7 +79,7 @@ use super::simplify::simplify_cfg; /// |---------------------| | | switchInt(Q) | /// | switchInt(_t) | | | c | ---> BBC.2 /// | false | --------/ | d | ---> BBD.2 -/// | otherwise | ---------------- | otherwise | +/// | otherwise | /--------- | otherwise | /// ======================= | ============================ /// | /// ================= | @@ -87,16 +88,11 @@ use super::simplify::simplify_cfg; /// | ... | /// ================= /// ``` -/// -/// This is only correct for some `P`, since `P` is now computed outside the original `switchInt`. -/// The filter on which `P` are allowed (together with discussion of its correctness) is found in -/// `may_hoist`. pub struct EarlyOtherwiseBranch; impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - // unsound: https://github.com/rust-lang/rust/issues/95162 - sess.mir_opt_level() >= 3 && sess.opts.unstable_opts.unsound_mir_opts + sess.mir_opt_level() >= 2 } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { @@ -172,7 +168,8 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { }; (value, targets.target_for_value(value)) }); - let eq_targets = SwitchTargets::new(eq_new_targets, opt_data.destination); + // The otherwise either is the same target branch or an unreachable. + let eq_targets = SwitchTargets::new(eq_new_targets, parent_targets.otherwise()); // Create `bbEq` in example above let eq_switch = BasicBlockData::new(Some(Terminator { @@ -217,85 +214,6 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { } } -/// Returns true if computing the discriminant of `place` may be hoisted out of the branch -fn may_hoist<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, place: Place<'tcx>) -> bool { - // FIXME(JakobDegen): This is unsound. Someone could write code like this: - // ```rust - // let Q = val; - // if discriminant(P) == otherwise { - // let ptr = &mut Q as *mut _ as *mut u8; - // unsafe { *ptr = 10; } // Any invalid value for the type - // } - // - // match P { - // A => match Q { - // A => { - // // code - // } - // _ => { - // // don't use Q - // } - // } - // _ => { - // // don't use Q - // } - // }; - // ``` - // - // Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant of an - // invalid value, which is UB. - // - // In order to fix this, we would either need to show that the discriminant computation of - // `place` is computed in all branches, including the `otherwise` branch, or we would need - // another analysis pass to determine that the place is fully initialized. It might even be best - // to have the hoisting be performed in a different pass and just do the CFG changing in this - // pass. - for (place, proj) in place.iter_projections() { - match proj { - // Dereferencing in the computation of `place` might cause issues from one of two - // categories. First, the referent might be invalid. We protect against this by - // dereferencing references only (not pointers). Second, the use of a reference may - // invalidate other references that are used later (for aliasing reasons). Consider - // where such an invalidated reference may appear: - // - In `Q`: Not possible since `Q` is used as the operand of a `SwitchInt` and so - // cannot contain referenced data. - // - In `BBU`: Not possible since that block contains only the `unreachable` terminator - // - In `BBC.2, BBD.2`: Not possible, since `discriminant(P)` was computed prior to - // reaching that block in the input to our transformation, and so any data - // invalidated by that computation could not have been used there. - // - In `BB9`: Not possible since control flow might have reached `BB9` via the - // `otherwise` branch in `BBC, BBD` in the input to our transformation, which would - // have invalidated the data when computing `discriminant(P)` - // So dereferencing here is correct. - ProjectionElem::Deref => match place.ty(body.local_decls(), tcx).ty.kind() { - ty::Ref(..) => {} - _ => return false, - }, - // Field projections are always valid - ProjectionElem::Field(..) => {} - // We cannot allow - // downcasts either, since the correctness of the downcast may depend on the parent - // branch being taken. An easy example of this is - // ``` - // Q = discriminant(_3) - // P = (_3 as Variant) - // ``` - // However, checking if the child and parent place are the same and only erroring then - // is not sufficient either, since the `discriminant(_3) == 1` (or whatever) check may - // be replaced by another optimization pass with any other condition that can be proven - // equivalent. - ProjectionElem::Downcast(..) => { - return false; - } - // We cannot allow indexing since the index may be out of bounds. - _ => { - return false; - } - } - } - true -} - #[derive(Debug)] struct OptimizationData<'tcx> { destination: BasicBlock, @@ -315,18 +233,40 @@ fn evaluate_candidate<'tcx>( return None; }; let parent_ty = parent_discr.ty(body.local_decls(), tcx); - let parent_dest = { - let poss = targets.otherwise(); - // If the fallthrough on the parent is trivially unreachable, we can let the - // children choose the destination - if bbs[poss].statements.len() == 0 - && bbs[poss].terminator().kind == TerminatorKind::Unreachable - { - None - } else { - Some(poss) - } - }; + if !bbs[targets.otherwise()].is_empty_unreachable() { + // Someone could write code like this: + // ```rust + // let Q = val; + // if discriminant(P) == otherwise { + // let ptr = &mut Q as *mut _ as *mut u8; + // // It may be difficult for us to effectively determine whether values are valid. + // // Invalid values can come from all sorts of corners. + // unsafe { *ptr = 10; } + // } + // + // match P { + // A => match Q { + // A => { + // // code + // } + // _ => { + // // don't use Q + // } + // } + // _ => { + // // don't use Q + // } + // }; + // ``` + // + // Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant of an + // invalid value, which is UB. + // In order to fix this, **we would either need to show that the discriminant computation of + // `place` is computed in all branches**. + // FIXME(#95162) For the moment, we adopt a conservative approach and + // consider only the `otherwise` branch has no statements and an unreachable terminator. + return None; + } let (_, child) = targets.iter().next()?; let child_terminator = &bbs[child].terminator(); let TerminatorKind::SwitchInt { targets: child_targets, discr: child_discr } = @@ -344,13 +284,7 @@ fn evaluate_candidate<'tcx>( let (_, Rvalue::Discriminant(child_place)) = &**boxed else { return None; }; - let destination = parent_dest.unwrap_or(child_targets.otherwise()); - - // Verify that the optimization is legal in general - // We can hoist evaluating the child discriminant out of the branch - if !may_hoist(tcx, body, *child_place) { - return None; - } + let destination = child_targets.otherwise(); // Verify that the optimization is legal for each branch for (value, child) in targets.iter() { @@ -411,5 +345,5 @@ fn verify_candidate_branch<'tcx>( if let Some(_) = iter.next() { return false; } - return true; + true } diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 6d4332793af..4d9a198eeb2 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -1,11 +1,125 @@ +use rustc_index::IndexSlice; +use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{ParamEnv, ScalarInt, Ty, TyCtxt}; +use rustc_target::abi::Size; use std::iter; use super::simplify::simplify_cfg; pub struct MatchBranchSimplification; +impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { + fn is_enabled(&self, sess: &rustc_session::Session) -> bool { + sess.mir_opt_level() >= 1 + } + + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let def_id = body.source.def_id(); + let param_env = tcx.param_env_reveal_all_normalized(def_id); + + let mut should_cleanup = false; + for i in 0..body.basic_blocks.len() { + let bbs = &*body.basic_blocks; + let bb_idx = BasicBlock::from_usize(i); + if !tcx.consider_optimizing(|| format!("MatchBranchSimplification {def_id:?} ")) { + continue; + } + + match bbs[bb_idx].terminator().kind { + TerminatorKind::SwitchInt { + discr: ref _discr @ (Operand::Copy(_) | Operand::Move(_)), + ref targets, + .. + // We require that the possible target blocks don't contain this block. + } if !targets.all_targets().contains(&bb_idx) => {} + // Only optimize switch int statements + _ => continue, + }; + + if SimplifyToIf.simplify(tcx, body, bb_idx, param_env).is_some() { + should_cleanup = true; + continue; + } + if SimplifyToExp::default().simplify(tcx, body, bb_idx, param_env).is_some() { + should_cleanup = true; + continue; + } + } + + if should_cleanup { + simplify_cfg(body); + } + } +} + +trait SimplifyMatch<'tcx> { + /// Simplifies a match statement, returning true if the simplification succeeds, false otherwise. + /// Generic code is written here, and we generally don't need a custom implementation. + fn simplify( + &mut self, + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + switch_bb_idx: BasicBlock, + param_env: ParamEnv<'tcx>, + ) -> Option<()> { + let bbs = &body.basic_blocks; + let (discr, targets) = match bbs[switch_bb_idx].terminator().kind { + TerminatorKind::SwitchInt { ref discr, ref targets, .. } => (discr, targets), + _ => unreachable!(), + }; + + let discr_ty = discr.ty(body.local_decls(), tcx); + self.can_simplify(tcx, targets, param_env, bbs, discr_ty)?; + + let mut patch = MirPatch::new(body); + + // Take ownership of items now that we know we can optimize. + let discr = discr.clone(); + + // Introduce a temporary for the discriminant value. + let source_info = bbs[switch_bb_idx].terminator().source_info; + let discr_local = patch.new_temp(discr_ty, source_info.span); + + let (_, first) = targets.iter().next().unwrap(); + let statement_index = bbs[switch_bb_idx].statements.len(); + let parent_end = Location { block: switch_bb_idx, statement_index }; + patch.add_statement(parent_end, StatementKind::StorageLive(discr_local)); + patch.add_assign(parent_end, Place::from(discr_local), Rvalue::Use(discr)); + self.new_stmts(tcx, targets, param_env, &mut patch, parent_end, bbs, discr_local, discr_ty); + patch.add_statement(parent_end, StatementKind::StorageDead(discr_local)); + patch.patch_terminator(switch_bb_idx, bbs[first].terminator().kind.clone()); + patch.apply(body); + Some(()) + } + + /// Check that the BBs to be simplified satisfies all distinct and + /// that the terminator are the same. + /// There are also conditions for different ways of simplification. + fn can_simplify( + &mut self, + tcx: TyCtxt<'tcx>, + targets: &SwitchTargets, + param_env: ParamEnv<'tcx>, + bbs: &IndexSlice<BasicBlock, BasicBlockData<'tcx>>, + discr_ty: Ty<'tcx>, + ) -> Option<()>; + + fn new_stmts( + &self, + tcx: TyCtxt<'tcx>, + targets: &SwitchTargets, + param_env: ParamEnv<'tcx>, + patch: &mut MirPatch<'tcx>, + parent_end: Location, + bbs: &IndexSlice<BasicBlock, BasicBlockData<'tcx>>, + discr_local: Local, + discr_ty: Ty<'tcx>, + ); +} + +struct SimplifyToIf; + /// If a source block is found that switches between two blocks that are exactly /// the same modulo const bool assignments (e.g., one assigns true another false /// to the same place), merge a target block statements into the source block, @@ -37,144 +151,350 @@ pub struct MatchBranchSimplification; /// goto -> bb3; /// } /// ``` +impl<'tcx> SimplifyMatch<'tcx> for SimplifyToIf { + fn can_simplify( + &mut self, + tcx: TyCtxt<'tcx>, + targets: &SwitchTargets, + param_env: ParamEnv<'tcx>, + bbs: &IndexSlice<BasicBlock, BasicBlockData<'tcx>>, + _discr_ty: Ty<'tcx>, + ) -> Option<()> { + if targets.iter().len() != 1 { + return None; + } + // We require that the possible target blocks all be distinct. + let (_, first) = targets.iter().next().unwrap(); + let second = targets.otherwise(); + if first == second { + return None; + } + // Check that destinations are identical, and if not, then don't optimize this block + if bbs[first].terminator().kind != bbs[second].terminator().kind { + return None; + } -impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { - fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.mir_opt_level() >= 1 - } + // Check that blocks are assignments of consts to the same place or same statement, + // and match up 1-1, if not don't optimize this block. + let first_stmts = &bbs[first].statements; + let second_stmts = &bbs[second].statements; + if first_stmts.len() != second_stmts.len() { + return None; + } + for (f, s) in iter::zip(first_stmts, second_stmts) { + match (&f.kind, &s.kind) { + // If two statements are exactly the same, we can optimize. + (f_s, s_s) if f_s == s_s => {} - fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let def_id = body.source.def_id(); - let param_env = tcx.param_env_reveal_all_normalized(def_id); + // If two statements are const bool assignments to the same place, we can optimize. + ( + StatementKind::Assign(box (lhs_f, Rvalue::Use(Operand::Constant(f_c)))), + StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), + ) if lhs_f == lhs_s + && f_c.const_.ty().is_bool() + && s_c.const_.ty().is_bool() + && f_c.const_.try_eval_bool(tcx, param_env).is_some() + && s_c.const_.try_eval_bool(tcx, param_env).is_some() => {} - let bbs = body.basic_blocks.as_mut(); - let mut should_cleanup = false; - 'outer: for bb_idx in bbs.indices() { - if !tcx.consider_optimizing(|| format!("MatchBranchSimplification {def_id:?} ")) { - continue; + // Otherwise we cannot optimize. Try another block. + _ => return None, } + } + Some(()) + } - let (discr, val, first, second) = match bbs[bb_idx].terminator().kind { - TerminatorKind::SwitchInt { - discr: ref discr @ (Operand::Copy(_) | Operand::Move(_)), - ref targets, - .. - } if targets.iter().len() == 1 => { - let (value, target) = targets.iter().next().unwrap(); - // We require that this block and the two possible target blocks all be - // distinct. - if target == targets.otherwise() - || bb_idx == target - || bb_idx == targets.otherwise() - { - continue; + fn new_stmts( + &self, + tcx: TyCtxt<'tcx>, + targets: &SwitchTargets, + param_env: ParamEnv<'tcx>, + patch: &mut MirPatch<'tcx>, + parent_end: Location, + bbs: &IndexSlice<BasicBlock, BasicBlockData<'tcx>>, + discr_local: Local, + discr_ty: Ty<'tcx>, + ) { + let (val, first) = targets.iter().next().unwrap(); + let second = targets.otherwise(); + // We already checked that first and second are different blocks, + // and bb_idx has a different terminator from both of them. + let first = &bbs[first]; + let second = &bbs[second]; + for (f, s) in iter::zip(&first.statements, &second.statements) { + match (&f.kind, &s.kind) { + (f_s, s_s) if f_s == s_s => { + patch.add_statement(parent_end, f.kind.clone()); + } + + ( + StatementKind::Assign(box (lhs, Rvalue::Use(Operand::Constant(f_c)))), + StatementKind::Assign(box (_, Rvalue::Use(Operand::Constant(s_c)))), + ) => { + // From earlier loop we know that we are dealing with bool constants only: + let f_b = f_c.const_.try_eval_bool(tcx, param_env).unwrap(); + let s_b = s_c.const_.try_eval_bool(tcx, param_env).unwrap(); + if f_b == s_b { + // Same value in both blocks. Use statement as is. + patch.add_statement(parent_end, f.kind.clone()); + } else { + // Different value between blocks. Make value conditional on switch condition. + let size = tcx.layout_of(param_env.and(discr_ty)).unwrap().size; + let const_cmp = Operand::const_from_scalar( + tcx, + discr_ty, + rustc_const_eval::interpret::Scalar::from_uint(val, size), + rustc_span::DUMMY_SP, + ); + let op = if f_b { BinOp::Eq } else { BinOp::Ne }; + let rhs = Rvalue::BinaryOp( + op, + Box::new((Operand::Copy(Place::from(discr_local)), const_cmp)), + ); + patch.add_assign(parent_end, *lhs, rhs); } - (discr, value, target, targets.otherwise()) } - // Only optimize switch int statements - _ => continue, - }; - // Check that destinations are identical, and if not, then don't optimize this block - if bbs[first].terminator().kind != bbs[second].terminator().kind { - continue; + _ => unreachable!(), } + } + } +} - // Check that blocks are assignments of consts to the same place or same statement, - // and match up 1-1, if not don't optimize this block. - let first_stmts = &bbs[first].statements; - let scnd_stmts = &bbs[second].statements; - if first_stmts.len() != scnd_stmts.len() { - continue; - } - for (f, s) in iter::zip(first_stmts, scnd_stmts) { - match (&f.kind, &s.kind) { - // If two statements are exactly the same, we can optimize. - (f_s, s_s) if f_s == s_s => {} +#[derive(Default)] +struct SimplifyToExp { + transfrom_types: Vec<TransfromType>, +} - // If two statements are const bool assignments to the same place, we can optimize. - ( - StatementKind::Assign(box (lhs_f, Rvalue::Use(Operand::Constant(f_c)))), - StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), - ) if lhs_f == lhs_s - && f_c.const_.ty().is_bool() - && s_c.const_.ty().is_bool() - && f_c.const_.try_eval_bool(tcx, param_env).is_some() - && s_c.const_.try_eval_bool(tcx, param_env).is_some() => {} +#[derive(Clone, Copy)] +enum CompareType<'tcx, 'a> { + /// Identical statements. + Same(&'a StatementKind<'tcx>), + /// Assignment statements have the same value. + Eq(&'a Place<'tcx>, Ty<'tcx>, ScalarInt), + /// Enum variant comparison type. + Discr { place: &'a Place<'tcx>, ty: Ty<'tcx>, is_signed: bool }, +} - // Otherwise we cannot optimize. Try another block. - _ => continue 'outer, - } - } - // Take ownership of items now that we know we can optimize. - let discr = discr.clone(); - let discr_ty = discr.ty(&body.local_decls, tcx); +enum TransfromType { + Same, + Eq, + Discr, +} + +impl From<CompareType<'_, '_>> for TransfromType { + fn from(compare_type: CompareType<'_, '_>) -> Self { + match compare_type { + CompareType::Same(_) => TransfromType::Same, + CompareType::Eq(_, _, _) => TransfromType::Eq, + CompareType::Discr { .. } => TransfromType::Discr, + } + } +} + +/// If we find that the value of match is the same as the assignment, +/// merge a target block statements into the source block, +/// using cast to transform different integer types. +/// +/// For example: +/// +/// ```ignore (MIR) +/// bb0: { +/// switchInt(_1) -> [1: bb2, 2: bb3, 3: bb4, otherwise: bb1]; +/// } +/// +/// bb1: { +/// unreachable; +/// } +/// +/// bb2: { +/// _0 = const 1_i16; +/// goto -> bb5; +/// } +/// +/// bb3: { +/// _0 = const 2_i16; +/// goto -> bb5; +/// } +/// +/// bb4: { +/// _0 = const 3_i16; +/// goto -> bb5; +/// } +/// ``` +/// +/// into: +/// +/// ```ignore (MIR) +/// bb0: { +/// _0 = _3 as i16 (IntToInt); +/// goto -> bb5; +/// } +/// ``` +impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp { + fn can_simplify( + &mut self, + tcx: TyCtxt<'tcx>, + targets: &SwitchTargets, + param_env: ParamEnv<'tcx>, + bbs: &IndexSlice<BasicBlock, BasicBlockData<'tcx>>, + discr_ty: Ty<'tcx>, + ) -> Option<()> { + if targets.iter().len() < 2 || targets.iter().len() > 64 { + return None; + } + // We require that the possible target blocks all be distinct. + if !targets.is_distinct() { + return None; + } + if !bbs[targets.otherwise()].is_empty_unreachable() { + return None; + } + let mut target_iter = targets.iter(); + let (first_val, first_target) = target_iter.next().unwrap(); + let first_terminator_kind = &bbs[first_target].terminator().kind; + // Check that destinations are identical, and if not, then don't optimize this block + if !targets + .iter() + .all(|(_, other_target)| first_terminator_kind == &bbs[other_target].terminator().kind) + { + return None; + } - // Introduce a temporary for the discriminant value. - let source_info = bbs[bb_idx].terminator().source_info; - let discr_local = body.local_decls.push(LocalDecl::new(discr_ty, source_info.span)); + let discr_size = tcx.layout_of(param_env.and(discr_ty)).unwrap().size; + let first_stmts = &bbs[first_target].statements; + let (second_val, second_target) = target_iter.next().unwrap(); + let second_stmts = &bbs[second_target].statements; + if first_stmts.len() != second_stmts.len() { + return None; + } - // We already checked that first and second are different blocks, - // and bb_idx has a different terminator from both of them. - let (from, first, second) = bbs.pick3_mut(bb_idx, first, second); + fn int_equal(l: ScalarInt, r: impl Into<u128>, size: Size) -> bool { + l.try_to_int(l.size()).unwrap() + == ScalarInt::try_from_uint(r, size).unwrap().try_to_int(size).unwrap() + } - let new_stmts = iter::zip(&first.statements, &second.statements).map(|(f, s)| { - match (&f.kind, &s.kind) { - (f_s, s_s) if f_s == s_s => (*f).clone(), + // We first compare the two branches, and then the other branches need to fulfill the same conditions. + let mut compare_types = Vec::new(); + for (f, s) in iter::zip(first_stmts, second_stmts) { + let compare_type = match (&f.kind, &s.kind) { + // If two statements are exactly the same, we can optimize. + (f_s, s_s) if f_s == s_s => CompareType::Same(f_s), - ( - StatementKind::Assign(box (lhs, Rvalue::Use(Operand::Constant(f_c)))), - StatementKind::Assign(box (_, Rvalue::Use(Operand::Constant(s_c)))), - ) => { - // From earlier loop we know that we are dealing with bool constants only: - let f_b = f_c.const_.try_eval_bool(tcx, param_env).unwrap(); - let s_b = s_c.const_.try_eval_bool(tcx, param_env).unwrap(); - if f_b == s_b { - // Same value in both blocks. Use statement as is. - (*f).clone() - } else { - // Different value between blocks. Make value conditional on switch condition. - let size = tcx.layout_of(param_env.and(discr_ty)).unwrap().size; - let const_cmp = Operand::const_from_scalar( - tcx, - discr_ty, - rustc_const_eval::interpret::Scalar::from_uint(val, size), - rustc_span::DUMMY_SP, - ); - let op = if f_b { BinOp::Eq } else { BinOp::Ne }; - let rhs = Rvalue::BinaryOp( - op, - Box::new((Operand::Copy(Place::from(discr_local)), const_cmp)), - ); - Statement { - source_info: f.source_info, - kind: StatementKind::Assign(Box::new((*lhs, rhs))), + // If two statements are assignments with the match values to the same place, we can optimize. + ( + StatementKind::Assign(box (lhs_f, Rvalue::Use(Operand::Constant(f_c)))), + StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), + ) if lhs_f == lhs_s + && f_c.const_.ty() == s_c.const_.ty() + && f_c.const_.ty().is_integral() => + { + match ( + f_c.const_.try_eval_scalar_int(tcx, param_env), + s_c.const_.try_eval_scalar_int(tcx, param_env), + ) { + (Some(f), Some(s)) if f == s => CompareType::Eq(lhs_f, f_c.const_.ty(), f), + // Enum variants can also be simplified to an assignment statement if their values are equal. + // We need to consider both unsigned and signed scenarios here. + (Some(f), Some(s)) + if ((f_c.const_.ty().is_signed() || discr_ty.is_signed()) + && int_equal(f, first_val, discr_size) + && int_equal(s, second_val, discr_size)) + || (Some(f) == ScalarInt::try_from_uint(first_val, f.size()) + && Some(s) + == ScalarInt::try_from_uint(second_val, s.size())) => + { + CompareType::Discr { + place: lhs_f, + ty: f_c.const_.ty(), + is_signed: f_c.const_.ty().is_signed() || discr_ty.is_signed(), } } + _ => { + return None; + } } + } + + // Otherwise we cannot optimize. Try another block. + _ => return None, + }; + compare_types.push(compare_type); + } - _ => unreachable!(), + // All remaining BBs need to fulfill the same pattern as the two BBs from the previous step. + for (other_val, other_target) in target_iter { + let other_stmts = &bbs[other_target].statements; + if compare_types.len() != other_stmts.len() { + return None; + } + for (f, s) in iter::zip(&compare_types, other_stmts) { + match (*f, &s.kind) { + (CompareType::Same(f_s), s_s) if f_s == s_s => {} + ( + CompareType::Eq(lhs_f, f_ty, val), + StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), + ) if lhs_f == lhs_s + && s_c.const_.ty() == f_ty + && s_c.const_.try_eval_scalar_int(tcx, param_env) == Some(val) => {} + ( + CompareType::Discr { place: lhs_f, ty: f_ty, is_signed }, + StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), + ) if lhs_f == lhs_s && s_c.const_.ty() == f_ty => { + let Some(f) = s_c.const_.try_eval_scalar_int(tcx, param_env) else { + return None; + }; + if is_signed + && s_c.const_.ty().is_signed() + && int_equal(f, other_val, discr_size) + { + continue; + } + if Some(f) == ScalarInt::try_from_uint(other_val, f.size()) { + continue; + } + return None; + } + _ => return None, } - }); - - from.statements - .push(Statement { source_info, kind: StatementKind::StorageLive(discr_local) }); - from.statements.push(Statement { - source_info, - kind: StatementKind::Assign(Box::new(( - Place::from(discr_local), - Rvalue::Use(discr), - ))), - }); - from.statements.extend(new_stmts); - from.statements - .push(Statement { source_info, kind: StatementKind::StorageDead(discr_local) }); - from.terminator_mut().kind = first.terminator().kind.clone(); - should_cleanup = true; + } } + self.transfrom_types = compare_types.into_iter().map(|c| c.into()).collect(); + Some(()) + } - if should_cleanup { - simplify_cfg(body); + fn new_stmts( + &self, + _tcx: TyCtxt<'tcx>, + targets: &SwitchTargets, + _param_env: ParamEnv<'tcx>, + patch: &mut MirPatch<'tcx>, + parent_end: Location, + bbs: &IndexSlice<BasicBlock, BasicBlockData<'tcx>>, + discr_local: Local, + discr_ty: Ty<'tcx>, + ) { + let (_, first) = targets.iter().next().unwrap(); + let first = &bbs[first]; + + for (t, s) in iter::zip(&self.transfrom_types, &first.statements) { + match (t, &s.kind) { + (TransfromType::Same, _) | (TransfromType::Eq, _) => { + patch.add_statement(parent_end, s.kind.clone()); + } + ( + TransfromType::Discr, + StatementKind::Assign(box (lhs, Rvalue::Use(Operand::Constant(f_c)))), + ) => { + let operand = Operand::Copy(Place::from(discr_local)); + let r_val = if f_c.const_.ty() == discr_ty { + Rvalue::Use(operand) + } else { + Rvalue::Cast(CastKind::IntToInt, operand, f_c.const_.ty()) + }; + patch.add_assign(parent_end, *lhs, r_val); + } + _ => unreachable!(), + } } } } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 0c35f9838ed..ee6c154e6e8 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -214,6 +214,7 @@ use rustc_hir::lang_items::LangItem; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::{AllocId, ErrorHandled, GlobalAlloc, Scalar}; use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; +use rustc_middle::mir::traversal; use rustc_middle::mir::visit::Visitor as MirVisitor; use rustc_middle::mir::{self, Location, MentionedItem}; use rustc_middle::query::TyCtxtAt; @@ -1414,15 +1415,16 @@ fn collect_items_of_instance<'tcx>( }; if mode == CollectionMode::UsedItems { - // Visit everything. Here we rely on the visitor also visiting `required_consts`, so that we - // evaluate them and abort compilation if any of them errors. - collector.visit_body(body); - } else { - // We only need to evaluate all constants, but can ignore the rest of the MIR. - for const_op in &body.required_consts { - if let Some(val) = collector.eval_constant(const_op) { - collect_const_value(tcx, val, mentioned_items); - } + for (bb, data) in traversal::mono_reachable(body, tcx, instance) { + collector.visit_basic_block_data(bb, data) + } + } + + // Always visit all `required_consts`, so that we evaluate them and abort compilation if any of + // them errors. + for const_op in &body.required_consts { + if let Some(val) = collector.eval_constant(const_op) { + collect_const_value(tcx, val, mentioned_items); } } diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 1899517c0e2..6600b92fb31 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -69,7 +69,8 @@ impl<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> Canonicalizer<'a, Infc let (max_universe, variables) = canonicalizer.finalize(); - Canonical { max_universe, variables, value } + let defining_opaque_types = infcx.defining_opaque_types(); + Canonical { defining_opaque_types, max_universe, variables, value } } fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVars) { @@ -349,6 +350,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I> | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) + | ty::Pat(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) | ty::Dynamic(_, _, _) diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 69b48bf0aff..353c3f41ed8 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -34,8 +34,7 @@ use unescape_error_reporting::{emit_unescape_error, escaped_char}; rustc_data_structures::static_assert_size!(rustc_lexer::Token, 12); #[derive(Clone, Debug)] -pub struct UnmatchedDelim { - pub expected_delim: Delimiter, +pub(crate) struct UnmatchedDelim { pub found_delim: Option<Delimiter>, pub found_span: Span, pub unclosed_span: Option<Span>, diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index a506f98bf3a..b5a5a2a90ee 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -77,7 +77,6 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { for &(_, sp) in &self.diag_info.open_braces { err.span_label(sp, "unclosed delimiter"); self.diag_info.unmatched_delims.push(UnmatchedDelim { - expected_delim: Delimiter::Brace, found_delim: None, found_span: self.token.span, unclosed_span: Some(sp), @@ -163,9 +162,8 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { candidate = Some(*brace_span); } } - let (tok, _) = self.diag_info.open_braces.pop().unwrap(); + let (_, _) = self.diag_info.open_braces.pop().unwrap(); self.diag_info.unmatched_delims.push(UnmatchedDelim { - expected_delim: tok, found_delim: Some(close_delim), found_span: self.token.span, unclosed_span: unclosed_delimiter, diff --git a/compiler/rustc_passes/src/hir_id_validator.rs b/compiler/rustc_passes/src/hir_id_validator.rs index dd6c1166957..51a69809c7a 100644 --- a/compiler/rustc_passes/src/hir_id_validator.rs +++ b/compiler/rustc_passes/src/hir_id_validator.rs @@ -161,4 +161,8 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> { let mut inner_visitor = self.new_visitor(self.tcx); inner_visitor.check(i.owner_id, |this| intravisit::walk_impl_item(this, i)); } + + fn visit_pattern_type_pattern(&mut self, p: &'hir hir::Pat<'hir>) { + self.visit_pat(p) + } } diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index e6e52648d6f..72c6a714e4d 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -352,6 +352,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { TraitObject, Typeof, Infer, + Pat, Err ] ); @@ -611,6 +612,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { AnonStruct, AnonUnion, Path, + Pat, TraitObject, ImplTrait, Paren, diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 467f09e4c29..548a7b43005 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -399,6 +399,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { | ty::RawPtr(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) + | ty::Pat(_, _) | ty::Dynamic(_, _, _) | ty::Closure(..) | ty::CoroutineClosure(..) diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 41d63407418..2039e994aaa 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -276,6 +276,7 @@ where | ty::Tuple(..) | ty::RawPtr(..) | ty::Ref(..) + | ty::Pat(..) | ty::FnPtr(..) | ty::Param(..) | ty::Bound(..) diff --git a/compiler/rustc_sanitizers/Cargo.toml b/compiler/rustc_sanitizers/Cargo.toml new file mode 100644 index 00000000000..aea2f7dda7f --- /dev/null +++ b/compiler/rustc_sanitizers/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "rustc_sanitizers" +version = "0.0.0" +edition = "2021" + +[dependencies] +bitflags = "2.5.0" +tracing = "0.1" +twox-hash = "1.6.3" +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_hir = { path = "../rustc_hir" } +rustc_middle = { path = "../rustc_middle" } +rustc_span = { path = "../rustc_span" } +rustc_target = { path = "../rustc_target" } +rustc_trait_selection = { path = "../rustc_trait_selection" } diff --git a/compiler/rustc_sanitizers/README.md b/compiler/rustc_sanitizers/README.md new file mode 100644 index 00000000000..d2e8f5d3a97 --- /dev/null +++ b/compiler/rustc_sanitizers/README.md @@ -0,0 +1,2 @@ +The `rustc_sanitizers` crate contains the source code for providing support for +the [sanitizers](https://github.com/google/sanitizers) to the Rust compiler. diff --git a/compiler/rustc_sanitizers/src/cfi/mod.rs b/compiler/rustc_sanitizers/src/cfi/mod.rs new file mode 100644 index 00000000000..90dab5e0333 --- /dev/null +++ b/compiler/rustc_sanitizers/src/cfi/mod.rs @@ -0,0 +1,6 @@ +//! LLVM Control Flow Integrity (CFI) and cross-language LLVM CFI support for the Rust compiler. +//! +//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, +//! see design document in the tracking issue #89653. +pub mod typeid; +pub use crate::cfi::typeid::{typeid_for_fnabi, typeid_for_instance, TypeIdOptions}; diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index a3a18fb768f..ed7cd8c2da7 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -1,76 +1,46 @@ -/// Type metadata identifiers (using Itanium C++ ABI mangling for encoding) for LLVM Control Flow -/// Integrity (CFI) and cross-language LLVM CFI support. -/// -/// Encodes type metadata identifiers for LLVM CFI and cross-language LLVM CFI support using Itanium -/// C++ ABI mangling for encoding with vendor extended type qualifiers and types for Rust types that -/// are not used across the FFI boundary. -/// -/// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, -/// see design document in the tracking issue #89653. +//! Encodes type metadata identifiers for LLVM CFI and cross-language LLVM CFI support using Itanium +//! C++ ABI mangling for encoding with vendor extended type qualifiers and types for Rust types that +//! are not used across the FFI boundary. +//! +//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, +//! see design document in the tracking issue #89653. use rustc_data_structures::base_n; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; -use rustc_hir::lang_items::LangItem; -use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; +use rustc_middle::bug; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ - self, Const, ExistentialPredicate, FloatTy, FnSig, Instance, IntTy, List, Region, RegionKind, - TermKind, Ty, TyCtxt, UintTy, + self, Const, ExistentialPredicate, FloatTy, FnSig, GenericArg, GenericArgKind, GenericArgsRef, + IntTy, List, Region, RegionKind, TermKind, Ty, TyCtxt, TypeFoldable, UintTy, }; -use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef}; -use rustc_middle::ty::{TypeFoldable, TypeVisitableExt}; use rustc_span::def_id::DefId; use rustc_span::sym; -use rustc_target::abi::call::{Conv, FnAbi, PassMode}; use rustc_target::abi::Integer; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::traits; use std::fmt::Write as _; -use std::iter; +use tracing::instrument; -use crate::typeid::TypeIdOptions; +use crate::cfi::typeid::itanium_cxx_abi::transform::{TransformTy, TransformTyOptions}; +use crate::cfi::typeid::TypeIdOptions; -/// Type and extended type qualifiers. -#[derive(Eq, Hash, PartialEq)] -enum TyQ { - None, - Const, - Mut, -} +/// Options for encode_ty. +pub type EncodeTyOptions = TypeIdOptions; /// Substitution dictionary key. #[derive(Eq, Hash, PartialEq)] -enum DictKey<'tcx> { +pub enum DictKey<'tcx> { Ty(Ty<'tcx>, TyQ), Region(Region<'tcx>), Const(Const<'tcx>), Predicate(ExistentialPredicate<'tcx>), } -/// Options for encode_ty. -type EncodeTyOptions = TypeIdOptions; - -/// Options for transform_ty. -type TransformTyOptions = TypeIdOptions; - -/// Converts a number to a disambiguator (see -/// <https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html>). -fn to_disambiguator(num: u64) -> String { - if let Some(num) = num.checked_sub(1) { - format!("s{}_", base_n::encode(num as u128, 62)) - } else { - "s_".to_string() - } -} - -/// Converts a number to a sequence number (see -/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id>). -fn to_seq_id(num: usize) -> String { - if let Some(num) = num.checked_sub(1) { - base_n::encode(num as u128, 36).to_uppercase() - } else { - "".to_string() - } +/// Type and extended type qualifiers. +#[derive(Eq, Hash, PartialEq)] +pub enum TyQ { + None, + Const, + Mut, } /// Substitutes a component if found in the substitution dictionary (see @@ -91,6 +61,37 @@ fn compress<'tcx>( } } +/// Encodes args using the Itanium C++ ABI with vendor extended type qualifiers and types for Rust +/// types that are not used at the FFI boundary. +fn encode_args<'tcx>( + tcx: TyCtxt<'tcx>, + args: GenericArgsRef<'tcx>, + dict: &mut FxHashMap<DictKey<'tcx>, usize>, + options: EncodeTyOptions, +) -> String { + // [I<subst1..substN>E] as part of vendor extended type + let mut s = String::new(); + let args: Vec<GenericArg<'_>> = args.iter().collect(); + if !args.is_empty() { + s.push('I'); + for arg in args { + match arg.unpack() { + GenericArgKind::Lifetime(region) => { + s.push_str(&encode_region(region, dict)); + } + GenericArgKind::Type(ty) => { + s.push_str(&encode_ty(tcx, ty, dict, options)); + } + GenericArgKind::Const(c) => { + s.push_str(&encode_const(tcx, c, dict, options)); + } + } + } + s.push('E'); + } + s +} + /// Encodes a const using the Itanium C++ ABI as a literal argument (see /// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling.literal>). fn encode_const<'tcx>( @@ -159,7 +160,6 @@ fn encode_const<'tcx>( /// Encodes a FnSig using the Itanium C++ ABI with vendor extended type qualifiers and types for /// Rust types that are not used at the FFI boundary. -#[instrument(level = "trace", skip(tcx, dict))] fn encode_fnsig<'tcx>( tcx: TyCtxt<'tcx>, fn_sig: &FnSig<'tcx>, @@ -299,137 +299,10 @@ fn encode_region<'tcx>(region: Region<'tcx>, dict: &mut FxHashMap<DictKey<'tcx>, s } -/// Encodes args using the Itanium C++ ABI with vendor extended type qualifiers and types for Rust -/// types that are not used at the FFI boundary. -fn encode_args<'tcx>( - tcx: TyCtxt<'tcx>, - args: GenericArgsRef<'tcx>, - dict: &mut FxHashMap<DictKey<'tcx>, usize>, - options: EncodeTyOptions, -) -> String { - // [I<subst1..substN>E] as part of vendor extended type - let mut s = String::new(); - let args: Vec<GenericArg<'_>> = args.iter().collect(); - if !args.is_empty() { - s.push('I'); - for arg in args { - match arg.unpack() { - GenericArgKind::Lifetime(region) => { - s.push_str(&encode_region(region, dict)); - } - GenericArgKind::Type(ty) => { - s.push_str(&encode_ty(tcx, ty, dict, options)); - } - GenericArgKind::Const(c) => { - s.push_str(&encode_const(tcx, c, dict, options)); - } - } - } - s.push('E'); - } - s -} - -/// Encodes a ty:Ty name, including its crate and path disambiguators and names. -fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String { - // Encode <name> for use in u<length><name>[I<element-type1..element-typeN>E], where - // <element-type> is <subst>, using v0's <path> without v0's extended form of paths: - // - // N<namespace-tagN>..N<namespace-tag1> - // C<crate-disambiguator><crate-name> - // <path-disambiguator1><path-name1>..<path-disambiguatorN><path-nameN> - // - // With additional tags for DefPathData::Impl and DefPathData::ForeignMod. For instance: - // - // pub type Type1 = impl Send; - // let _: Type1 = <Struct1<i32>>::foo; - // fn foo1(_: Type1) { } - // - // pub type Type2 = impl Send; - // let _: Type2 = <Trait1<i32>>::foo; - // fn foo2(_: Type2) { } - // - // pub type Type3 = impl Send; - // let _: Type3 = <i32 as Trait1<i32>>::foo; - // fn foo3(_: Type3) { } - // - // pub type Type4 = impl Send; - // let _: Type4 = <Struct1<i32> as Trait1<i32>>::foo; - // fn foo3(_: Type4) { } - // - // Are encoded as: - // - // _ZTSFvu29NvNIC1234_5crate8{{impl}}3fooIu3i32EE - // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3dynIu21NtC1234_5crate6Trait1Iu3i32Eu6regionES_EE - // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3i32S_EE - // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu22NtC1234_5crate7Struct1Iu3i32ES_EE - // - // The reason for not using v0's extended form of paths is to use a consistent and simpler - // encoding, as the reasoning for using it isn't relevant for type metadata identifiers (i.e., - // keep symbol names close to how methods are represented in error messages). See - // https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html#methods. - let mut s = String::new(); - - // Start and namespace tags - let mut def_path = tcx.def_path(def_id); - def_path.data.reverse(); - for disambiguated_data in &def_path.data { - s.push('N'); - s.push_str(match disambiguated_data.data { - hir::definitions::DefPathData::Impl => "I", // Not specified in v0's <namespace> - hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's <namespace> - hir::definitions::DefPathData::TypeNs(..) => "t", - hir::definitions::DefPathData::ValueNs(..) => "v", - hir::definitions::DefPathData::Closure => "C", - hir::definitions::DefPathData::Ctor => "c", - hir::definitions::DefPathData::AnonConst => "k", - hir::definitions::DefPathData::OpaqueTy => "i", - hir::definitions::DefPathData::CrateRoot - | hir::definitions::DefPathData::Use - | hir::definitions::DefPathData::GlobalAsm - | hir::definitions::DefPathData::MacroNs(..) - | hir::definitions::DefPathData::LifetimeNs(..) - | hir::definitions::DefPathData::AnonAdt => { - bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data); - } - }); - } - - // Crate disambiguator and name - s.push('C'); - s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).as_u64())); - let crate_name = tcx.crate_name(def_path.krate).to_string(); - let _ = write!(s, "{}{}", crate_name.len(), &crate_name); - - // Disambiguators and names - def_path.data.reverse(); - for disambiguated_data in &def_path.data { - let num = disambiguated_data.disambiguator as u64; - if num > 0 { - s.push_str(&to_disambiguator(num)); - } - - let name = disambiguated_data.data.to_string(); - let _ = write!(s, "{}", name.len()); - - // Prepend a '_' if name starts with a digit or '_' - if let Some(first) = name.as_bytes().first() { - if first.is_ascii_digit() || *first == b'_' { - s.push('_'); - } - } else { - bug!("encode_ty_name: invalid name `{:?}`", name); - } - - s.push_str(&name); - } - - s -} - /// Encodes a ty:Ty using the Itanium C++ ABI with vendor extended type qualifiers and types for /// Rust types that are not used at the FFI boundary. -fn encode_ty<'tcx>( +#[instrument(level = "trace", skip(tcx, dict))] +pub fn encode_ty<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, dict: &mut FxHashMap<DictKey<'tcx>, usize>, @@ -533,6 +406,16 @@ fn encode_ty<'tcx>( typeid.push_str(&s); } + ty::Pat(ty0, pat) => { + // u3patI<element-type><pattern>E as vendor extended type + let mut s = String::from("u3patI"); + s.push_str(&encode_ty(tcx, *ty0, dict, options)); + write!(s, "{:?}", **pat).unwrap(); + s.push('E'); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + ty::Slice(ty0) => { // u5sliceI<element-type>E as vendor extended type let mut s = String::from("u5sliceI"); @@ -752,485 +635,119 @@ fn encode_ty<'tcx>( typeid } -struct TransformTy<'tcx> { - tcx: TyCtxt<'tcx>, - options: TransformTyOptions, - parents: Vec<Ty<'tcx>>, -} - -impl<'tcx> TransformTy<'tcx> { - fn new(tcx: TyCtxt<'tcx>, options: TransformTyOptions) -> Self { - TransformTy { tcx, options, parents: Vec::new() } - } -} - -impl<'tcx> TypeFolder<TyCtxt<'tcx>> for TransformTy<'tcx> { - // Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms - // all c_void types into unit types unconditionally, generalizes pointers if - // TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if - // TransformTyOptions::NORMALIZE_INTEGERS option is set. - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match t.kind() { - ty::Array(..) - | ty::Closure(..) - | ty::Coroutine(..) - | ty::CoroutineClosure(..) - | ty::CoroutineWitness(..) - | ty::Dynamic(..) - | ty::Float(..) - | ty::FnDef(..) - | ty::Foreign(..) - | ty::Never - | ty::Slice(..) - | ty::Str - | ty::Tuple(..) => t.super_fold_with(self), - - ty::Bool => { - if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { - // Note: on all platforms that Rust's currently supports, its size and alignment - // are 1, and its ABI class is INTEGER - see Rust Layout and ABIs. - // - // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.) - // - // Clang represents bool as an 8-bit unsigned integer. - self.tcx.types.u8 - } else { - t - } - } - - ty::Char => { - if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { - // Since #118032, char is guaranteed to have the same size, alignment, and - // function call ABI as u32 on all platforms. - self.tcx.types.u32 - } else { - t - } - } - - ty::Int(..) | ty::Uint(..) => { - if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { - // Note: C99 7.18.2.4 requires uintptr_t and intptr_t to be at least 16-bit - // wide. All platforms we currently support have a C platform, and as a - // consequence, isize/usize are at least 16-bit wide for all of them. - // - // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize.) - match t.kind() { - ty::Int(IntTy::Isize) => match self.tcx.sess.target.pointer_width { - 16 => self.tcx.types.i16, - 32 => self.tcx.types.i32, - 64 => self.tcx.types.i64, - 128 => self.tcx.types.i128, - _ => bug!( - "fold_ty: unexpected pointer width `{}`", - self.tcx.sess.target.pointer_width - ), - }, - ty::Uint(UintTy::Usize) => match self.tcx.sess.target.pointer_width { - 16 => self.tcx.types.u16, - 32 => self.tcx.types.u32, - 64 => self.tcx.types.u64, - 128 => self.tcx.types.u128, - _ => bug!( - "fold_ty: unexpected pointer width `{}`", - self.tcx.sess.target.pointer_width - ), - }, - _ => t, - } - } else { - t - } - } - - ty::Adt(..) if t.is_c_void(self.tcx) => self.tcx.types.unit, - - ty::Adt(adt_def, args) => { - if adt_def.repr().transparent() && adt_def.is_struct() && !self.parents.contains(&t) - { - // Don't transform repr(transparent) types with an user-defined CFI encoding to - // preserve the user-defined CFI encoding. - if let Some(_) = self.tcx.get_attr(adt_def.did(), sym::cfi_encoding) { - return t; - } - let variant = adt_def.non_enum_variant(); - let param_env = self.tcx.param_env(variant.def_id); - let field = variant.fields.iter().find(|field| { - let ty = self.tcx.type_of(field.did).instantiate_identity(); - let is_zst = self - .tcx - .layout_of(param_env.and(ty)) - .is_ok_and(|layout| layout.is_zst()); - !is_zst - }); - if let Some(field) = field { - let ty0 = self.tcx.type_of(field.did).instantiate(self.tcx, args); - // Generalize any repr(transparent) user-defined type that is either a - // pointer or reference, and either references itself or any other type that - // contains or references itself, to avoid a reference cycle. - - // If the self reference is not through a pointer, for example, due - // to using `PhantomData`, need to skip normalizing it if we hit it again. - self.parents.push(t); - let ty = if ty0.is_any_ptr() && ty0.contains(t) { - let options = self.options; - self.options |= TransformTyOptions::GENERALIZE_POINTERS; - let ty = ty0.fold_with(self); - self.options = options; - ty - } else { - ty0.fold_with(self) - }; - self.parents.pop(); - ty - } else { - // Transform repr(transparent) types without non-ZST field into () - self.tcx.types.unit - } - } else { - t.super_fold_with(self) - } - } - - ty::Ref(..) => { - if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { - if t.is_mutable_ptr() { - Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit) - } else { - Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit) - } - } else { - t.super_fold_with(self) - } - } - - ty::RawPtr(..) => { - if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { - if t.is_mutable_ptr() { - Ty::new_mut_ptr(self.tcx, self.tcx.types.unit) - } else { - Ty::new_imm_ptr(self.tcx, self.tcx.types.unit) - } - } else { - t.super_fold_with(self) - } - } - - ty::FnPtr(..) => { - if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { - Ty::new_imm_ptr(self.tcx, self.tcx.types.unit) - } else { - t.super_fold_with(self) - } - } - - ty::Alias(..) => { - self.fold_ty(self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), t)) - } +/// Encodes a ty:Ty name, including its crate and path disambiguators and names. +fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String { + // Encode <name> for use in u<length><name>[I<element-type1..element-typeN>E], where + // <element-type> is <subst>, using v0's <path> without v0's extended form of paths: + // + // N<namespace-tagN>..N<namespace-tag1> + // C<crate-disambiguator><crate-name> + // <path-disambiguator1><path-name1>..<path-disambiguatorN><path-nameN> + // + // With additional tags for DefPathData::Impl and DefPathData::ForeignMod. For instance: + // + // pub type Type1 = impl Send; + // let _: Type1 = <Struct1<i32>>::foo; + // fn foo1(_: Type1) { } + // + // pub type Type2 = impl Send; + // let _: Type2 = <Trait1<i32>>::foo; + // fn foo2(_: Type2) { } + // + // pub type Type3 = impl Send; + // let _: Type3 = <i32 as Trait1<i32>>::foo; + // fn foo3(_: Type3) { } + // + // pub type Type4 = impl Send; + // let _: Type4 = <Struct1<i32> as Trait1<i32>>::foo; + // fn foo3(_: Type4) { } + // + // Are encoded as: + // + // _ZTSFvu29NvNIC1234_5crate8{{impl}}3fooIu3i32EE + // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3dynIu21NtC1234_5crate6Trait1Iu3i32Eu6regionES_EE + // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3i32S_EE + // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu22NtC1234_5crate7Struct1Iu3i32ES_EE + // + // The reason for not using v0's extended form of paths is to use a consistent and simpler + // encoding, as the reasoning for using it isn't relevant for type metadata identifiers (i.e., + // keep symbol names close to how methods are represented in error messages). See + // https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html#methods. + let mut s = String::new(); - ty::Bound(..) | ty::Error(..) | ty::Infer(..) | ty::Param(..) | ty::Placeholder(..) => { - bug!("fold_ty: unexpected `{:?}`", t.kind()); + // Start and namespace tags + let mut def_path = tcx.def_path(def_id); + def_path.data.reverse(); + for disambiguated_data in &def_path.data { + s.push('N'); + s.push_str(match disambiguated_data.data { + hir::definitions::DefPathData::Impl => "I", // Not specified in v0's <namespace> + hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's <namespace> + hir::definitions::DefPathData::TypeNs(..) => "t", + hir::definitions::DefPathData::ValueNs(..) => "v", + hir::definitions::DefPathData::Closure => "C", + hir::definitions::DefPathData::Ctor => "c", + hir::definitions::DefPathData::AnonConst => "k", + hir::definitions::DefPathData::OpaqueTy => "i", + hir::definitions::DefPathData::CrateRoot + | hir::definitions::DefPathData::Use + | hir::definitions::DefPathData::GlobalAsm + | hir::definitions::DefPathData::MacroNs(..) + | hir::definitions::DefPathData::LifetimeNs(..) + | hir::definitions::DefPathData::AnonAdt => { + bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data); } - } - } - - fn interner(&self) -> TyCtxt<'tcx> { - self.tcx + }); } -} -/// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor -/// extended type qualifiers and types for Rust types that are not used at the FFI boundary. -#[instrument(level = "trace", skip(tcx))] -pub fn typeid_for_fnabi<'tcx>( - tcx: TyCtxt<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - options: TypeIdOptions, -) -> String { - // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions - // its type. - let mut typeid = String::from("_Z"); - - // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type - // metadata identifiers for function pointers. The typeinfo name encoding is a two-character - // code (i.e., 'TS') prefixed to the type encoding for the function. - typeid.push_str("TS"); - - // Function types are delimited by an "F..E" pair - typeid.push('F'); - - // A dictionary of substitution candidates used for compression (see - // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression). - let mut dict: FxHashMap<DictKey<'tcx>, usize> = FxHashMap::default(); + // Crate disambiguator and name + s.push('C'); + s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).as_u64())); + let crate_name = tcx.crate_name(def_path.krate).to_string(); + let _ = write!(s, "{}{}", crate_name.len(), &crate_name); - let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits()) - .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); - match fn_abi.conv { - Conv::C => { - encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C); - } - _ => { - encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C); + // Disambiguators and names + def_path.data.reverse(); + for disambiguated_data in &def_path.data { + let num = disambiguated_data.disambiguator as u64; + if num > 0 { + s.push_str(&to_disambiguator(num)); } - } - // Encode the return type - let transform_ty_options = TransformTyOptions::from_bits(options.bits()) - .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); - let mut type_folder = TransformTy::new(tcx, transform_ty_options); - let ty = fn_abi.ret.layout.ty.fold_with(&mut type_folder); - typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); - - // Encode the parameter types + let name = disambiguated_data.data.to_string(); + let _ = write!(s, "{}", name.len()); - // We erase ZSTs as we go if the argument is skipped. This is an implementation detail of how - // MIR is currently treated by rustc, and subject to change in the future. Specifically, MIR - // interpretation today will allow skipped arguments to simply not be passed at a call-site. - if !fn_abi.c_variadic { - let mut pushed_arg = false; - for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) { - pushed_arg = true; - let ty = arg.layout.ty.fold_with(&mut type_folder); - typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); - } - if !pushed_arg { - // Empty parameter lists, whether declared as () or conventionally as (void), are - // encoded with a void parameter specifier "v". - typeid.push('v'); - } - } else { - for n in 0..fn_abi.fixed_count as usize { - if fn_abi.args[n].mode == PassMode::Ignore { - continue; + // Prepend a '_' if name starts with a digit or '_' + if let Some(first) = name.as_bytes().first() { + if first.is_ascii_digit() || *first == b'_' { + s.push('_'); } - let ty = fn_abi.args[n].layout.ty.fold_with(&mut type_folder); - typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + } else { + bug!("encode_ty_name: invalid name `{:?}`", name); } - typeid.push('z'); - } - - // Close the "F..E" pair - typeid.push('E'); - - // Add encoding suffixes - if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { - typeid.push_str(".normalized"); - } - - if options.contains(EncodeTyOptions::GENERALIZE_POINTERS) { - typeid.push_str(".generalized"); + s.push_str(&name); } - typeid + s } -/// Returns a type metadata identifier for the specified Instance using the Itanium C++ ABI with -/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary. -pub fn typeid_for_instance<'tcx>( - tcx: TyCtxt<'tcx>, - mut instance: Instance<'tcx>, - options: TypeIdOptions, -) -> String { - if (matches!(instance.def, ty::InstanceDef::Virtual(..)) - && Some(instance.def_id()) == tcx.lang_items().drop_in_place_fn()) - || matches!(instance.def, ty::InstanceDef::DropGlue(..)) - { - // Adjust the type ids of DropGlues - // - // DropGlues may have indirect calls to one or more given types drop function. Rust allows - // for types to be erased to any trait object and retains the drop function for the original - // type, which means at the indirect call sites in DropGlues, when typeid_for_fnabi is - // called a second time, it only has information after type erasure and it could be a call - // on any arbitrary trait object. Normalize them to a synthesized Drop trait object, both on - // declaration/definition, and during code generation at call sites so they have the same - // type id and match. - // - // FIXME(rcvalle): This allows a drop call on any trait object to call the drop function of - // any other type. - // - let def_id = tcx - .lang_items() - .drop_trait() - .unwrap_or_else(|| bug!("typeid_for_instance: couldn't get drop_trait lang item")); - let predicate = ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { - def_id: def_id, - args: List::empty(), - }); - let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]); - let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn); - instance.args = tcx.mk_args_trait(self_ty, List::empty()); - } else if let ty::InstanceDef::Virtual(def_id, _) = instance.def { - let upcast_ty = match tcx.trait_of_item(def_id) { - Some(trait_id) => trait_object_ty( - tcx, - ty::Binder::dummy(ty::TraitRef::from_method(tcx, trait_id, instance.args)), - ), - // drop_in_place won't have a defining trait, skip the upcast - None => instance.args.type_at(0), - }; - let stripped_ty = strip_receiver_auto(tcx, upcast_ty); - instance.args = tcx.mk_args_trait(stripped_ty, instance.args.into_iter().skip(1)); - } else if let ty::InstanceDef::VTableShim(def_id) = instance.def - && let Some(trait_id) = tcx.trait_of_item(def_id) - { - // VTableShims may have a trait method, but a concrete Self. This is not suitable for a vtable, - // as the caller will not know the concrete Self. - let trait_ref = ty::TraitRef::new(tcx, trait_id, instance.args); - let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); - instance.args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); - } - - if !options.contains(EncodeTyOptions::USE_CONCRETE_SELF) { - if let Some(impl_id) = tcx.impl_of_method(instance.def_id()) - && let Some(trait_ref) = tcx.impl_trait_ref(impl_id) - { - let impl_method = tcx.associated_item(instance.def_id()); - let method_id = impl_method - .trait_item_def_id - .expect("Part of a trait implementation, but not linked to the def_id?"); - let trait_method = tcx.associated_item(method_id); - let trait_id = trait_ref.skip_binder().def_id; - if traits::is_vtable_safe_method(tcx, trait_id, trait_method) - && tcx.object_safety_violations(trait_id).is_empty() - { - // Trait methods will have a Self polymorphic parameter, where the concreteized - // implementatation will not. We need to walk back to the more general trait method - let trait_ref = tcx.instantiate_and_normalize_erasing_regions( - instance.args, - ty::ParamEnv::reveal_all(), - trait_ref, - ); - let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); - - // At the call site, any call to this concrete function through a vtable will be - // `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the - // original method id, and we've recovered the trait arguments, we can make the callee - // instance we're computing the alias set for match the caller instance. - // - // Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder. - // If we ever *do* start encoding the vtable index, we will need to generate an alias set - // based on which vtables we are putting this method into, as there will be more than one - // index value when supertraits are involved. - instance.def = ty::InstanceDef::Virtual(method_id, 0); - let abstract_trait_args = - tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); - instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args); - } - } else if tcx.is_closure_like(instance.def_id()) { - // We're either a closure or a coroutine. Our goal is to find the trait we're defined on, - // instantiate it, and take the type of its only method as our own. - let closure_ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); - let (trait_id, inputs) = match closure_ty.kind() { - ty::Closure(..) => { - let closure_args = instance.args.as_closure(); - let trait_id = tcx.fn_trait_kind_to_def_id(closure_args.kind()).unwrap(); - let tuple_args = - tcx.instantiate_bound_regions_with_erased(closure_args.sig()).inputs()[0]; - (trait_id, Some(tuple_args)) - } - ty::Coroutine(..) => match tcx.coroutine_kind(instance.def_id()).unwrap() { - hir::CoroutineKind::Coroutine(..) => ( - tcx.require_lang_item(LangItem::Coroutine, None), - Some(instance.args.as_coroutine().resume_ty()), - ), - hir::CoroutineKind::Desugared(desugaring, _) => { - let lang_item = match desugaring { - hir::CoroutineDesugaring::Async => LangItem::Future, - hir::CoroutineDesugaring::AsyncGen => LangItem::AsyncIterator, - hir::CoroutineDesugaring::Gen => LangItem::Iterator, - }; - (tcx.require_lang_item(lang_item, None), None) - } - }, - ty::CoroutineClosure(..) => ( - tcx.require_lang_item(LangItem::FnOnce, None), - Some( - tcx.instantiate_bound_regions_with_erased( - instance.args.as_coroutine_closure().coroutine_closure_sig(), - ) - .tupled_inputs_ty, - ), - ), - x => bug!("Unexpected type kind for closure-like: {x:?}"), - }; - let concrete_args = tcx.mk_args_trait(closure_ty, inputs.map(Into::into)); - let trait_ref = ty::TraitRef::new(tcx, trait_id, concrete_args); - let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); - let abstract_args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); - // There should be exactly one method on this trait, and it should be the one we're - // defining. - let call = tcx - .associated_items(trait_id) - .in_definition_order() - .find(|it| it.kind == ty::AssocKind::Fn) - .expect("No call-family function on closure-like Fn trait?") - .def_id; - - instance.def = ty::InstanceDef::Virtual(call, 0); - instance.args = abstract_args; - } +/// Converts a number to a disambiguator (see +/// <https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html>). +fn to_disambiguator(num: u64) -> String { + if let Some(num) = num.checked_sub(1) { + format!("s{}_", base_n::encode(num as u128, 62)) + } else { + "s_".to_string() } - - let fn_abi = tcx - .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((instance, ty::List::empty()))) - .unwrap_or_else(|error| { - bug!("typeid_for_instance: couldn't get fn_abi of instance {instance:?}: {error:?}") - }); - - typeid_for_fnabi(tcx, fn_abi, options) } -fn strip_receiver_auto<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - let ty::Dynamic(preds, lifetime, kind) = ty.kind() else { - bug!("Tried to strip auto traits from non-dynamic type {ty}"); - }; - if preds.principal().is_some() { - let filtered_preds = - tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| { - !matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..)) - })); - Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind) +/// Converts a number to a sequence number (see +/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id>). +fn to_seq_id(num: usize) -> String { + if let Some(num) = num.checked_sub(1) { + base_n::encode(num as u128, 36).to_uppercase() } else { - // If there's no principal type, re-encode it as a unit, since we don't know anything - // about it. This technically discards the knowledge that it was a type that was made - // into a trait object at some point, but that's not a lot. - tcx.types.unit + "".to_string() } } - -#[instrument(skip(tcx), ret)] -fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> { - assert!(!poly_trait_ref.has_non_region_param()); - let principal_pred = poly_trait_ref.map_bound(|trait_ref| { - ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)) - }); - let mut assoc_preds: Vec<_> = traits::supertraits(tcx, poly_trait_ref) - .flat_map(|super_poly_trait_ref| { - tcx.associated_items(super_poly_trait_ref.def_id()) - .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Type) - .map(move |assoc_ty| { - super_poly_trait_ref.map_bound(|super_trait_ref| { - let alias_ty = ty::AliasTy::new(tcx, assoc_ty.def_id, super_trait_ref.args); - let resolved = tcx.normalize_erasing_regions( - ty::ParamEnv::reveal_all(), - alias_ty.to_ty(tcx), - ); - debug!("Resolved {:?} -> {resolved}", alias_ty.to_ty(tcx)); - ty::ExistentialPredicate::Projection(ty::ExistentialProjection { - def_id: assoc_ty.def_id, - args: ty::ExistentialTraitRef::erase_self_ty(tcx, super_trait_ref).args, - term: resolved.into(), - }) - }) - }) - }) - .collect(); - assoc_preds.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); - let preds = tcx.mk_poly_existential_predicates_from_iter( - iter::once(principal_pred).chain(assoc_preds.into_iter()), - ); - Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased, ty::Dyn) -} diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs new file mode 100644 index 00000000000..b6182dc4e63 --- /dev/null +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs @@ -0,0 +1,123 @@ +//! Type metadata identifiers (using Itanium C++ ABI mangling for encoding) for LLVM Control Flow +//! Integrity (CFI) and cross-language LLVM CFI support. +//! +//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, +//! see design document in the tracking issue #89653. +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::bug; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable}; +use rustc_target::abi::call::{Conv, FnAbi, PassMode}; +use tracing::instrument; + +mod encode; +mod transform; +use crate::cfi::typeid::itanium_cxx_abi::encode::{encode_ty, DictKey, EncodeTyOptions}; +use crate::cfi::typeid::itanium_cxx_abi::transform::{ + transform_instance, TransformTy, TransformTyOptions, +}; +use crate::cfi::typeid::TypeIdOptions; + +/// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor +/// extended type qualifiers and types for Rust types that are not used at the FFI boundary. +#[instrument(level = "trace", skip(tcx))] +pub fn typeid_for_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + options: TypeIdOptions, +) -> String { + // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions + // its type. + let mut typeid = String::from("_Z"); + + // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type + // metadata identifiers for function pointers. The typeinfo name encoding is a two-character + // code (i.e., 'TS') prefixed to the type encoding for the function. + typeid.push_str("TS"); + + // Function types are delimited by an "F..E" pair + typeid.push('F'); + + // A dictionary of substitution candidates used for compression (see + // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression). + let mut dict: FxHashMap<DictKey<'tcx>, usize> = FxHashMap::default(); + + let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits()) + .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); + match fn_abi.conv { + Conv::C => { + encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C); + } + _ => { + encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C); + } + } + + // Encode the return type + let transform_ty_options = TransformTyOptions::from_bits(options.bits()) + .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); + let mut type_folder = TransformTy::new(tcx, transform_ty_options); + let ty = fn_abi.ret.layout.ty.fold_with(&mut type_folder); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + + // Encode the parameter types + + // We erase ZSTs as we go if the argument is skipped. This is an implementation detail of how + // MIR is currently treated by rustc, and subject to change in the future. Specifically, MIR + // interpretation today will allow skipped arguments to simply not be passed at a call-site. + if !fn_abi.c_variadic { + let mut pushed_arg = false; + for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) { + pushed_arg = true; + let ty = arg.layout.ty.fold_with(&mut type_folder); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + } + if !pushed_arg { + // Empty parameter lists, whether declared as () or conventionally as (void), are + // encoded with a void parameter specifier "v". + typeid.push('v'); + } + } else { + for n in 0..fn_abi.fixed_count as usize { + if fn_abi.args[n].mode == PassMode::Ignore { + continue; + } + let ty = fn_abi.args[n].layout.ty.fold_with(&mut type_folder); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + } + + typeid.push('z'); + } + + // Close the "F..E" pair + typeid.push('E'); + + // Add encoding suffixes + if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { + typeid.push_str(".normalized"); + } + + if options.contains(EncodeTyOptions::GENERALIZE_POINTERS) { + typeid.push_str(".generalized"); + } + + typeid +} + +/// Returns a type metadata identifier for the specified Instance using the Itanium C++ ABI with +/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary. +#[instrument(level = "trace", skip(tcx))] +pub fn typeid_for_instance<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + options: TypeIdOptions, +) -> String { + let transform_ty_options = TransformTyOptions::from_bits(options.bits()) + .unwrap_or_else(|| bug!("typeid_for_instance: invalid option(s) `{:?}`", options.bits())); + let instance = transform_instance(tcx, instance, transform_ty_options); + let fn_abi = tcx + .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((instance, ty::List::empty()))) + .unwrap_or_else(|error| { + bug!("typeid_for_instance: couldn't get fn_abi of instance {instance:?}: {error:?}") + }); + typeid_for_fnabi(tcx, fn_abi, options) +} diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs new file mode 100644 index 00000000000..7141c6c9bb0 --- /dev/null +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -0,0 +1,450 @@ +//! Transforms instances and types for LLVM CFI and cross-language LLVM CFI support using Itanium +//! C++ ABI mangling. +//! +//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, +//! see design document in the tracking issue #89653. +use rustc_hir as hir; +use rustc_hir::LangItem; +use rustc_middle::bug; +use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::{ + self, Instance, IntTy, List, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, UintTy, +}; +use rustc_span::sym; +use rustc_trait_selection::traits; +use std::iter; +use tracing::{debug, instrument}; + +use crate::cfi::typeid::itanium_cxx_abi::encode::EncodeTyOptions; +use crate::cfi::typeid::TypeIdOptions; + +/// Options for transform_ty. +pub type TransformTyOptions = TypeIdOptions; + +pub struct TransformTy<'tcx> { + tcx: TyCtxt<'tcx>, + options: TransformTyOptions, + parents: Vec<Ty<'tcx>>, +} + +impl<'tcx> TransformTy<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, options: TransformTyOptions) -> Self { + TransformTy { tcx, options, parents: Vec::new() } + } +} + +/// Transforms a ty:Ty for being encoded and used in the substitution dictionary. +/// +/// * Transforms all c_void types into unit types. +/// * Generalizes pointers if TransformTyOptions::GENERALIZE_POINTERS option is set. +/// * Normalizes integers if TransformTyOptions::NORMALIZE_INTEGERS option is set. +/// * Generalizes any repr(transparent) user-defined type that is either a pointer or reference, and +/// either references itself or any other type that contains or references itself, to avoid a +/// reference cycle. +/// * Transforms repr(transparent) types without non-ZST field into (). +/// +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for TransformTy<'tcx> { + // Transforms a ty:Ty for being encoded and used in the substitution dictionary. + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match t.kind() { + ty::Array(..) + | ty::Closure(..) + | ty::Coroutine(..) + | ty::CoroutineClosure(..) + | ty::CoroutineWitness(..) + | ty::Dynamic(..) + | ty::Float(..) + | ty::FnDef(..) + | ty::Foreign(..) + | ty::Never + | ty::Pat(..) + | ty::Slice(..) + | ty::Str + | ty::Tuple(..) => t.super_fold_with(self), + + ty::Bool => { + if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { + // Note: on all platforms that Rust's currently supports, its size and alignment + // are 1, and its ABI class is INTEGER - see Rust Layout and ABIs. + // + // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.) + // + // Clang represents bool as an 8-bit unsigned integer. + self.tcx.types.u8 + } else { + t + } + } + + ty::Char => { + if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { + // Since #118032, char is guaranteed to have the same size, alignment, and + // function call ABI as u32 on all platforms. + self.tcx.types.u32 + } else { + t + } + } + + ty::Int(..) | ty::Uint(..) => { + if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { + // Note: C99 7.18.2.4 requires uintptr_t and intptr_t to be at least 16-bit + // wide. All platforms we currently support have a C platform, and as a + // consequence, isize/usize are at least 16-bit wide for all of them. + // + // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize.) + match t.kind() { + ty::Int(IntTy::Isize) => match self.tcx.sess.target.pointer_width { + 16 => self.tcx.types.i16, + 32 => self.tcx.types.i32, + 64 => self.tcx.types.i64, + 128 => self.tcx.types.i128, + _ => bug!( + "fold_ty: unexpected pointer width `{}`", + self.tcx.sess.target.pointer_width + ), + }, + ty::Uint(UintTy::Usize) => match self.tcx.sess.target.pointer_width { + 16 => self.tcx.types.u16, + 32 => self.tcx.types.u32, + 64 => self.tcx.types.u64, + 128 => self.tcx.types.u128, + _ => bug!( + "fold_ty: unexpected pointer width `{}`", + self.tcx.sess.target.pointer_width + ), + }, + _ => t, + } + } else { + t + } + } + + ty::Adt(..) if t.is_c_void(self.tcx) => self.tcx.types.unit, + + ty::Adt(adt_def, args) => { + if adt_def.repr().transparent() && adt_def.is_struct() && !self.parents.contains(&t) + { + // Don't transform repr(transparent) types with an user-defined CFI encoding to + // preserve the user-defined CFI encoding. + if let Some(_) = self.tcx.get_attr(adt_def.did(), sym::cfi_encoding) { + return t; + } + let variant = adt_def.non_enum_variant(); + let param_env = self.tcx.param_env(variant.def_id); + let field = variant.fields.iter().find(|field| { + let ty = self.tcx.type_of(field.did).instantiate_identity(); + let is_zst = self + .tcx + .layout_of(param_env.and(ty)) + .is_ok_and(|layout| layout.is_zst()); + !is_zst + }); + if let Some(field) = field { + let ty0 = self.tcx.type_of(field.did).instantiate(self.tcx, args); + // Generalize any repr(transparent) user-defined type that is either a + // pointer or reference, and either references itself or any other type that + // contains or references itself, to avoid a reference cycle. + + // If the self reference is not through a pointer, for example, due + // to using `PhantomData`, need to skip normalizing it if we hit it again. + self.parents.push(t); + let ty = if ty0.is_any_ptr() && ty0.contains(t) { + let options = self.options; + self.options |= TransformTyOptions::GENERALIZE_POINTERS; + let ty = ty0.fold_with(self); + self.options = options; + ty + } else { + ty0.fold_with(self) + }; + self.parents.pop(); + ty + } else { + // Transform repr(transparent) types without non-ZST field into () + self.tcx.types.unit + } + } else { + t.super_fold_with(self) + } + } + + ty::Ref(..) => { + if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + if t.is_mutable_ptr() { + Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit) + } else { + Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit) + } + } else { + t.super_fold_with(self) + } + } + + ty::RawPtr(..) => { + if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + if t.is_mutable_ptr() { + Ty::new_mut_ptr(self.tcx, self.tcx.types.unit) + } else { + Ty::new_imm_ptr(self.tcx, self.tcx.types.unit) + } + } else { + t.super_fold_with(self) + } + } + + ty::FnPtr(..) => { + if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + Ty::new_imm_ptr(self.tcx, self.tcx.types.unit) + } else { + t.super_fold_with(self) + } + } + + ty::Alias(..) => { + self.fold_ty(self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), t)) + } + + ty::Bound(..) | ty::Error(..) | ty::Infer(..) | ty::Param(..) | ty::Placeholder(..) => { + bug!("fold_ty: unexpected `{:?}`", t.kind()); + } + } + } + + fn interner(&self) -> TyCtxt<'tcx> { + self.tcx + } +} + +#[instrument(skip(tcx), ret)] +fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> { + assert!(!poly_trait_ref.has_non_region_param()); + let principal_pred = poly_trait_ref.map_bound(|trait_ref| { + ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)) + }); + let mut assoc_preds: Vec<_> = traits::supertraits(tcx, poly_trait_ref) + .flat_map(|super_poly_trait_ref| { + tcx.associated_items(super_poly_trait_ref.def_id()) + .in_definition_order() + .filter(|item| item.kind == ty::AssocKind::Type) + .map(move |assoc_ty| { + super_poly_trait_ref.map_bound(|super_trait_ref| { + let alias_ty = ty::AliasTy::new(tcx, assoc_ty.def_id, super_trait_ref.args); + let resolved = tcx.normalize_erasing_regions( + ty::ParamEnv::reveal_all(), + alias_ty.to_ty(tcx), + ); + debug!("Resolved {:?} -> {resolved}", alias_ty.to_ty(tcx)); + ty::ExistentialPredicate::Projection(ty::ExistentialProjection { + def_id: assoc_ty.def_id, + args: ty::ExistentialTraitRef::erase_self_ty(tcx, super_trait_ref).args, + term: resolved.into(), + }) + }) + }) + }) + .collect(); + assoc_preds.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); + let preds = tcx.mk_poly_existential_predicates_from_iter( + iter::once(principal_pred).chain(assoc_preds.into_iter()), + ); + Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased, ty::Dyn) +} + +/// Transforms an instance for LLVM CFI and cross-language LLVM CFI support using Itanium C++ ABI +/// mangling. +/// +/// typeid_for_instance is called at two locations, initially when declaring/defining functions and +/// methods, and later during code generation at call sites, after type erasure might have ocurred. +/// +/// In the first call (i.e., when declaring/defining functions and methods), it encodes type ids for +/// an FnAbi or Instance, and these type ids are attached to functions and methods. (These type ids +/// are used later by the LowerTypeTests LLVM pass to aggregate functions in groups derived from +/// these type ids.) +/// +/// In the second call (i.e., during code generation at call sites), it encodes a type id for an +/// FnAbi or Instance, after type erasure might have occured, and this type id is used for testing +/// if a function is member of the group derived from this type id. Therefore, in the first call to +/// typeid_for_fnabi (when type ids are attached to functions and methods), it can only include at +/// most as much information that would be available in the second call (i.e., during code +/// generation at call sites); otherwise, the type ids would not not match. +/// +/// For this, it: +/// +/// * Adjust the type ids of DropGlues (see below). +/// * Adjusts the type ids of VTableShims to the type id expected in the call sites for the +/// entry in the vtable (i.e., by using the signature of the closure passed as an argument to the +/// shim, or by just removing self). +/// * Performs type erasure for calls on trait objects by transforming self into a trait object of +/// the trait that defines the method. +/// * Performs type erasure for closures call methods by transforming self into a trait object of +/// the Fn trait that defines the method (for being attached as a secondary type id). +/// +#[instrument(level = "trace", skip(tcx))] +pub fn transform_instance<'tcx>( + tcx: TyCtxt<'tcx>, + mut instance: Instance<'tcx>, + options: TransformTyOptions, +) -> Instance<'tcx> { + if (matches!(instance.def, ty::InstanceDef::Virtual(..)) + && Some(instance.def_id()) == tcx.lang_items().drop_in_place_fn()) + || matches!(instance.def, ty::InstanceDef::DropGlue(..)) + { + // Adjust the type ids of DropGlues + // + // DropGlues may have indirect calls to one or more given types drop function. Rust allows + // for types to be erased to any trait object and retains the drop function for the original + // type, which means at the indirect call sites in DropGlues, when typeid_for_fnabi is + // called a second time, it only has information after type erasure and it could be a call + // on any arbitrary trait object. Normalize them to a synthesized Drop trait object, both on + // declaration/definition, and during code generation at call sites so they have the same + // type id and match. + // + // FIXME(rcvalle): This allows a drop call on any trait object to call the drop function of + // any other type. + // + let def_id = tcx + .lang_items() + .drop_trait() + .unwrap_or_else(|| bug!("typeid_for_instance: couldn't get drop_trait lang item")); + let predicate = ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { + def_id: def_id, + args: List::empty(), + }); + let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]); + let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn); + instance.args = tcx.mk_args_trait(self_ty, List::empty()); + } else if let ty::InstanceDef::Virtual(def_id, _) = instance.def { + // Transform self into a trait object of the trait that defines the method for virtual + // functions to match the type erasure done below. + let upcast_ty = match tcx.trait_of_item(def_id) { + Some(trait_id) => trait_object_ty( + tcx, + ty::Binder::dummy(ty::TraitRef::from_method(tcx, trait_id, instance.args)), + ), + // drop_in_place won't have a defining trait, skip the upcast + None => instance.args.type_at(0), + }; + let ty::Dynamic(preds, lifetime, kind) = upcast_ty.kind() else { + bug!("Tried to remove autotraits from non-dynamic type {upcast_ty}"); + }; + let self_ty = if preds.principal().is_some() { + let filtered_preds = + tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| { + !matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..)) + })); + Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind) + } else { + // If there's no principal type, re-encode it as a unit, since we don't know anything + // about it. This technically discards the knowledge that it was a type that was made + // into a trait object at some point, but that's not a lot. + tcx.types.unit + }; + instance.args = tcx.mk_args_trait(self_ty, instance.args.into_iter().skip(1)); + } else if let ty::InstanceDef::VTableShim(def_id) = instance.def + && let Some(trait_id) = tcx.trait_of_item(def_id) + { + // Adjust the type ids of VTableShims to the type id expected in the call sites for the + // entry in the vtable (i.e., by using the signature of the closure passed as an argument + // to the shim, or by just removing self). + let trait_ref = ty::TraitRef::new(tcx, trait_id, instance.args); + let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); + instance.args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); + } + + if !options.contains(TransformTyOptions::USE_CONCRETE_SELF) { + // Perform type erasure for calls on trait objects by transforming self into a trait object + // of the trait that defines the method. + if let Some(impl_id) = tcx.impl_of_method(instance.def_id()) + && let Some(trait_ref) = tcx.impl_trait_ref(impl_id) + { + let impl_method = tcx.associated_item(instance.def_id()); + let method_id = impl_method + .trait_item_def_id + .expect("Part of a trait implementation, but not linked to the def_id?"); + let trait_method = tcx.associated_item(method_id); + let trait_id = trait_ref.skip_binder().def_id; + if traits::is_vtable_safe_method(tcx, trait_id, trait_method) + && tcx.object_safety_violations(trait_id).is_empty() + { + // Trait methods will have a Self polymorphic parameter, where the concreteized + // implementatation will not. We need to walk back to the more general trait method + let trait_ref = tcx.instantiate_and_normalize_erasing_regions( + instance.args, + ty::ParamEnv::reveal_all(), + trait_ref, + ); + let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); + + // At the call site, any call to this concrete function through a vtable will be + // `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the + // original method id, and we've recovered the trait arguments, we can make the callee + // instance we're computing the alias set for match the caller instance. + // + // Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder. + // If we ever *do* start encoding the vtable index, we will need to generate an alias set + // based on which vtables we are putting this method into, as there will be more than one + // index value when supertraits are involved. + instance.def = ty::InstanceDef::Virtual(method_id, 0); + let abstract_trait_args = + tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); + instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args); + } + } else if tcx.is_closure_like(instance.def_id()) { + // We're either a closure or a coroutine. Our goal is to find the trait we're defined on, + // instantiate it, and take the type of its only method as our own. + let closure_ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); + let (trait_id, inputs) = match closure_ty.kind() { + ty::Closure(..) => { + let closure_args = instance.args.as_closure(); + let trait_id = tcx.fn_trait_kind_to_def_id(closure_args.kind()).unwrap(); + let tuple_args = + tcx.instantiate_bound_regions_with_erased(closure_args.sig()).inputs()[0]; + (trait_id, Some(tuple_args)) + } + ty::Coroutine(..) => match tcx.coroutine_kind(instance.def_id()).unwrap() { + hir::CoroutineKind::Coroutine(..) => ( + tcx.require_lang_item(LangItem::Coroutine, None), + Some(instance.args.as_coroutine().resume_ty()), + ), + hir::CoroutineKind::Desugared(desugaring, _) => { + let lang_item = match desugaring { + hir::CoroutineDesugaring::Async => LangItem::Future, + hir::CoroutineDesugaring::AsyncGen => LangItem::AsyncIterator, + hir::CoroutineDesugaring::Gen => LangItem::Iterator, + }; + (tcx.require_lang_item(lang_item, None), None) + } + }, + ty::CoroutineClosure(..) => ( + tcx.require_lang_item(LangItem::FnOnce, None), + Some( + tcx.instantiate_bound_regions_with_erased( + instance.args.as_coroutine_closure().coroutine_closure_sig(), + ) + .tupled_inputs_ty, + ), + ), + x => bug!("Unexpected type kind for closure-like: {x:?}"), + }; + let concrete_args = tcx.mk_args_trait(closure_ty, inputs.map(Into::into)); + let trait_ref = ty::TraitRef::new(tcx, trait_id, concrete_args); + let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); + let abstract_args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); + // There should be exactly one method on this trait, and it should be the one we're + // defining. + let call = tcx + .associated_items(trait_id) + .in_definition_order() + .find(|it| it.kind == ty::AssocKind::Fn) + .expect("No call-family function on closure-like Fn trait?") + .def_id; + + instance.def = ty::InstanceDef::Virtual(call, 0); + instance.args = abstract_args; + } + } + + instance +} diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/mod.rs b/compiler/rustc_sanitizers/src/cfi/typeid/mod.rs new file mode 100644 index 00000000000..ad8b1804439 --- /dev/null +++ b/compiler/rustc_sanitizers/src/cfi/typeid/mod.rs @@ -0,0 +1,54 @@ +//! Type metadata identifiers for LLVM Control Flow Integrity (CFI) and cross-language LLVM CFI +//! support for the Rust compiler. +//! +//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, +//! see design document in the tracking issue #89653. +use bitflags::bitflags; +use rustc_middle::ty::{Instance, Ty, TyCtxt}; +use rustc_target::abi::call::FnAbi; + +bitflags! { + /// Options for typeid_for_fnabi. + #[derive(Clone, Copy, Debug)] + pub struct TypeIdOptions: u32 { + /// Generalizes pointers for compatibility with Clang + /// `-fsanitize-cfi-icall-generalize-pointers` option for cross-language LLVM CFI and KCFI + /// support. + const GENERALIZE_POINTERS = 1; + /// Generalizes repr(C) user-defined type for extern function types with the "C" calling + /// convention (or extern types) for cross-language LLVM CFI and KCFI support. + const GENERALIZE_REPR_C = 2; + /// Normalizes integers for compatibility with Clang + /// `-fsanitize-cfi-icall-experimental-normalize-integers` option for cross-language LLVM + /// CFI and KCFI support. + const NORMALIZE_INTEGERS = 4; + /// Do not perform self type erasure for attaching a secondary type id to methods with their + /// concrete self so they can be used as function pointers. + /// + /// (This applies to typeid_for_instance only and should be used to attach a secondary type + /// id to methods during their declaration/definition so they match the type ids returned by + /// either typeid_for_instance or typeid_for_fnabi at call sites during code generation for + /// type membership tests when methods are used as function pointers.) + const USE_CONCRETE_SELF = 8; + } +} + +pub mod itanium_cxx_abi; + +/// Returns a type metadata identifier for the specified FnAbi. +pub fn typeid_for_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + options: TypeIdOptions, +) -> String { + itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options) +} + +/// Returns a type metadata identifier for the specified Instance. +pub fn typeid_for_instance<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + options: TypeIdOptions, +) -> String { + itanium_cxx_abi::typeid_for_instance(tcx, instance, options) +} diff --git a/compiler/rustc_sanitizers/src/kcfi/mod.rs b/compiler/rustc_sanitizers/src/kcfi/mod.rs new file mode 100644 index 00000000000..a8b632940b0 --- /dev/null +++ b/compiler/rustc_sanitizers/src/kcfi/mod.rs @@ -0,0 +1,7 @@ +//! LLVM Kernel Control Flow Integrity (KCFI) and cross-language LLVM KCFI support for the Rust +//! compiler. +//! +//! For more information about LLVM KCFI and cross-language LLVM KCFI support for the Rust compiler, +//! see the tracking issue #123479. +pub mod typeid; +pub use crate::kcfi::typeid::{typeid_for_fnabi, typeid_for_instance, TypeIdOptions}; diff --git a/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs b/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs new file mode 100644 index 00000000000..436c398e39b --- /dev/null +++ b/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs @@ -0,0 +1,55 @@ +//! Type metadata identifiers for LLVM Kernel Control Flow Integrity (KCFI) and cross-language LLVM +//! KCFI support for the Rust compiler. +//! +//! For more information about LLVM KCFI and cross-language LLVM KCFI support for the Rust compiler, +//! see the tracking issue #123479. +use rustc_middle::ty::{Instance, InstanceDef, ReifyReason, Ty, TyCtxt}; +use rustc_target::abi::call::FnAbi; +use std::hash::Hasher; +use twox_hash::XxHash64; + +pub use crate::cfi::typeid::{itanium_cxx_abi, TypeIdOptions}; + +/// Returns a KCFI type metadata identifier for the specified FnAbi. +pub fn typeid_for_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + options: TypeIdOptions, +) -> u32 { + // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the + // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) + let mut hash: XxHash64 = Default::default(); + hash.write(itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options).as_bytes()); + hash.finish() as u32 +} + +/// Returns a KCFI type metadata identifier for the specified Instance. +pub fn typeid_for_instance<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + mut options: TypeIdOptions, +) -> u32 { + // KCFI support for Rust shares most of its implementation with the CFI support, with some key + // differences: + // + // 1. KCFI performs type tests differently and are implemented as different LLVM passes than CFI + // to not require LTO. + // 2. KCFI has the limitation that a function or method may have one type id assigned only. + // + // Because of the limitation listed above (2), the current KCFI implementation (not CFI) does + // reifying of types (i.e., adds shims/trampolines for indirect calls in these cases) for: + // + // * Supporting casting between function items, closures, and Fn trait objects. + // * Supporting methods being cast as function pointers. + // + // This was implemented for KCFI support in #123106 and #123052 (which introduced the + // ReifyReason). The tracking issue for KCFI support for Rust is #123479. + if matches!(instance.def, InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr))) { + options.insert(TypeIdOptions::USE_CONCRETE_SELF); + } + // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the + // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) + let mut hash: XxHash64 = Default::default(); + hash.write(itanium_cxx_abi::typeid_for_instance(tcx, instance, options).as_bytes()); + hash.finish() as u32 +} diff --git a/compiler/rustc_sanitizers/src/lib.rs b/compiler/rustc_sanitizers/src/lib.rs new file mode 100644 index 00000000000..1f73e255490 --- /dev/null +++ b/compiler/rustc_sanitizers/src/lib.rs @@ -0,0 +1,7 @@ +#![feature(let_chains)] +//! Sanitizers support for the Rust compiler. +//! +//! This crate contains the source code for providing support for the sanitizers to the Rust +//! compiler. +pub mod cfi; +pub mod kcfi; diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs index a0f5eb59b6a..cb6656bae06 100644 --- a/compiler/rustc_session/src/cstore.rs +++ b/compiler/rustc_session/src/cstore.rs @@ -4,12 +4,10 @@ use crate::search_paths::PathKind; use crate::utils::NativeLibKind; -use crate::Session; use rustc_ast as ast; use rustc_data_structures::sync::{self, AppendOnlyIndexVec, FreezeLock}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, StableCrateId, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash, Definitions}; -use rustc_span::hygiene::{ExpnHash, ExpnId}; use rustc_span::symbol::Symbol; use rustc_span::Span; use rustc_target::spec::abi::Abi; @@ -220,22 +218,6 @@ pub trait CrateStore: std::fmt::Debug { fn crate_name(&self, cnum: CrateNum) -> Symbol; fn stable_crate_id(&self, cnum: CrateNum) -> StableCrateId; fn stable_crate_id_to_crate_num(&self, stable_crate_id: StableCrateId) -> CrateNum; - - /// Fetch a DefId from a DefPathHash for a foreign crate. - fn def_path_hash_to_def_id(&self, cnum: CrateNum, hash: DefPathHash) -> DefId; - fn expn_hash_to_expn_id( - &self, - sess: &Session, - cnum: CrateNum, - index_guess: u32, - hash: ExpnHash, - ) -> ExpnId; - - /// Imports all `SourceFile`s from the given crate into the current session. - /// This normally happens automatically when we decode a `Span` from - /// that crate's metadata - however, the incr comp cache needs - /// to trigger this manually when decoding a foreign `Span` - fn import_source_files(&self, sess: &Session, cnum: CrateNum); } pub type CrateStoreDyn = dyn CrateStore + sync::DynSync + sync::DynSend; diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index e8cc41cc886..0893bc31bfa 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -10,12 +10,12 @@ use rustc_span::Symbol; use stable_mir::abi::Layout; use stable_mir::mir::alloc::AllocId; use stable_mir::mir::mono::{Instance, MonoItem, StaticDef}; -use stable_mir::mir::{Mutability, Place, ProjectionElem, Safety}; +use stable_mir::mir::{BinOp, Mutability, Place, ProjectionElem, 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, + GenericArgKind, GenericArgs, IndexedVal, IntTy, Movability, Pattern, Region, RigidTy, Span, + TermKind, TraitRef, Ty, UintTy, VariantDef, VariantIdx, }; use stable_mir::{CrateItem, CrateNum, DefId}; @@ -76,6 +76,19 @@ impl RustcInternal for Ty { } } +impl RustcInternal for Pattern { + type T<'tcx> = rustc_ty::Pattern<'tcx>; + fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { + tcx.mk_pat(match self { + Pattern::Range { start, end, include_end } => rustc_ty::PatternKind::Range { + start: start.as_ref().map(|c| ty_const(c, tables, tcx)), + end: end.as_ref().map(|c| ty_const(c, tables, tcx)), + include_end: *include_end, + }, + }) + } +} + impl RustcInternal for RigidTy { type T<'tcx> = rustc_ty::TyKind<'tcx>; @@ -90,6 +103,9 @@ impl RustcInternal for RigidTy { RigidTy::Array(ty, cnst) => { rustc_ty::TyKind::Array(ty.internal(tables, tcx), ty_const(cnst, tables, tcx)) } + RigidTy::Pat(ty, pat) => { + rustc_ty::TyKind::Pat(ty.internal(tables, tcx), pat.internal(tables, tcx)) + } RigidTy::Adt(def, args) => { rustc_ty::TyKind::Adt(def.internal(tables, tcx), args.internal(tables, tcx)) } @@ -535,6 +551,38 @@ impl RustcInternal for ProjectionElem { } } +impl RustcInternal for BinOp { + type T<'tcx> = rustc_middle::mir::BinOp; + + fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { + match self { + BinOp::Add => rustc_middle::mir::BinOp::Add, + BinOp::AddUnchecked => rustc_middle::mir::BinOp::AddUnchecked, + BinOp::Sub => rustc_middle::mir::BinOp::Sub, + BinOp::SubUnchecked => rustc_middle::mir::BinOp::SubUnchecked, + BinOp::Mul => rustc_middle::mir::BinOp::Mul, + BinOp::MulUnchecked => rustc_middle::mir::BinOp::MulUnchecked, + BinOp::Div => rustc_middle::mir::BinOp::Div, + BinOp::Rem => rustc_middle::mir::BinOp::Rem, + BinOp::BitXor => rustc_middle::mir::BinOp::BitXor, + BinOp::BitAnd => rustc_middle::mir::BinOp::BitAnd, + BinOp::BitOr => rustc_middle::mir::BinOp::BitOr, + BinOp::Shl => rustc_middle::mir::BinOp::Shl, + BinOp::ShlUnchecked => rustc_middle::mir::BinOp::ShlUnchecked, + BinOp::Shr => rustc_middle::mir::BinOp::Shr, + BinOp::ShrUnchecked => rustc_middle::mir::BinOp::ShrUnchecked, + BinOp::Eq => rustc_middle::mir::BinOp::Eq, + BinOp::Lt => rustc_middle::mir::BinOp::Lt, + BinOp::Le => rustc_middle::mir::BinOp::Le, + BinOp::Ne => rustc_middle::mir::BinOp::Ne, + BinOp::Ge => rustc_middle::mir::BinOp::Ge, + BinOp::Gt => rustc_middle::mir::BinOp::Gt, + BinOp::Cmp => rustc_middle::mir::BinOp::Cmp, + BinOp::Offset => rustc_middle::mir::BinOp::Offset, + } + } +} + impl<T> RustcInternal for &T where T: RustcInternal, diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 7c12168b809..61bbedf9eec 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -19,7 +19,7 @@ use stable_mir::abi::{FnAbi, Layout, LayoutShape}; use stable_mir::compiler_interface::Context; use stable_mir::mir::alloc::GlobalAlloc; use stable_mir::mir::mono::{InstanceDef, StaticDef}; -use stable_mir::mir::{Body, Place}; +use stable_mir::mir::{BinOp, Body, Place}; use stable_mir::target::{MachineInfo, MachineSize}; use stable_mir::ty::{ AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef, @@ -668,6 +668,15 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let tcx = tables.tcx; format!("{:?}", place.internal(&mut *tables, tcx)) } + + fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let rhs_internal = rhs.internal(&mut *tables, tcx); + let lhs_internal = lhs.internal(&mut *tables, tcx); + let ty = bin_op.internal(&mut *tables, tcx).ty(tcx, rhs_internal, lhs_internal); + ty.stable(&mut *tables) + } } pub struct TablesWrapper<'tcx>(pub RefCell<Tables<'tcx>>); diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index 2ad8f350f10..112e44f674e 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -330,6 +330,9 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> { ty::Array(ty, constant) => { TyKind::RigidTy(RigidTy::Array(ty.stable(tables), constant.stable(tables))) } + ty::Pat(ty, pat) => { + TyKind::RigidTy(RigidTy::Pat(ty.stable(tables), pat.stable(tables))) + } ty::Slice(ty) => TyKind::RigidTy(RigidTy::Slice(ty.stable(tables))), ty::RawPtr(ty, mutbl) => { TyKind::RigidTy(RigidTy::RawPtr(ty.stable(tables), mutbl.stable(tables))) @@ -385,6 +388,20 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> { } } +impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> { + type T = stable_mir::ty::Pattern; + + fn stable(&self, tables: &mut Tables<'_>) -> Self::T { + match **self { + ty::PatternKind::Range { start, end, include_end } => stable_mir::ty::Pattern::Range { + start: start.stable(tables), + end: end.stable(tables), + include_end, + }, + } + } +} + impl<'tcx> Stable<'tcx> for ty::Const<'tcx> { type T = stable_mir::ty::Const; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 7a5647e979a..bfd0f77c237 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1009,6 +1009,7 @@ symbols! { io_stderr, io_stdout, irrefutable_let_patterns, + is, is_val_statically_known, isa_attribute, isize, @@ -1347,6 +1348,8 @@ symbols! { path, pattern_complexity, pattern_parentheses, + pattern_type, + pattern_types, phantom_data, pic, pie, diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml index 1c8f1d03670..65aa9e40c8b 100644 --- a/compiler/rustc_symbol_mangling/Cargo.toml +++ b/compiler/rustc_symbol_mangling/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -bitflags = "2.4.1" punycode = "0.4.0" rustc-demangle = "0.1.21" rustc_data_structures = { path = "../rustc_data_structures" } @@ -15,7 +14,5 @@ rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } -rustc_trait_selection = { path = "../rustc_trait_selection" } tracing = "0.1" -twox-hash = "1.6.3" # tidy-alphabetical-end diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 0588af9bda7..b9509478702 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -114,7 +114,6 @@ mod v0; pub mod errors; pub mod test; -pub mod typeid; /// This function computes the symbol name for the given `instance` and the /// given instantiating crate. That is, if you know that instance X is diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs deleted file mode 100644 index 7bd998294dd..00000000000 --- a/compiler/rustc_symbol_mangling/src/typeid.rs +++ /dev/null @@ -1,100 +0,0 @@ -/// Type metadata identifiers for LLVM Control Flow Integrity (CFI) and cross-language LLVM CFI -/// support. -/// -/// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, -/// see design document in the tracking issue #89653. -use bitflags::bitflags; -use rustc_middle::ty::{Instance, InstanceDef, ReifyReason, Ty, TyCtxt}; -use rustc_target::abi::call::FnAbi; -use std::hash::Hasher; -use twox_hash::XxHash64; - -bitflags! { - /// Options for typeid_for_fnabi. - #[derive(Clone, Copy, Debug)] - pub struct TypeIdOptions: u32 { - /// Generalizes pointers for compatibility with Clang - /// `-fsanitize-cfi-icall-generalize-pointers` option for cross-language LLVM CFI and KCFI - /// support. - const GENERALIZE_POINTERS = 1; - /// Generalizes repr(C) user-defined type for extern function types with the "C" calling - /// convention (or extern types) for cross-language LLVM CFI and KCFI support. - const GENERALIZE_REPR_C = 2; - /// Normalizes integers for compatibility with Clang - /// `-fsanitize-cfi-icall-experimental-normalize-integers` option for cross-language LLVM - /// CFI and KCFI support. - const NORMALIZE_INTEGERS = 4; - /// Do not perform self type erasure for attaching a secondary type id to methods with their - /// concrete self so they can be used as function pointers. - /// - /// (This applies to typeid_for_instance only and should be used to attach a secondary type - /// id to methods during their declaration/definition so they match the type ids returned by - /// either typeid_for_instance or typeid_for_fnabi at call sites during code generation for - /// type membership tests when methods are used as function pointers.) - const USE_CONCRETE_SELF = 8; - } -} - -mod typeid_itanium_cxx_abi; - -/// Returns a type metadata identifier for the specified FnAbi. -pub fn typeid_for_fnabi<'tcx>( - tcx: TyCtxt<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - options: TypeIdOptions, -) -> String { - typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options) -} - -/// Returns a type metadata identifier for the specified Instance. -pub fn typeid_for_instance<'tcx>( - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - options: TypeIdOptions, -) -> String { - typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options) -} - -/// Returns a KCFI type metadata identifier for the specified FnAbi. -pub fn kcfi_typeid_for_fnabi<'tcx>( - tcx: TyCtxt<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - options: TypeIdOptions, -) -> u32 { - // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the - // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) - let mut hash: XxHash64 = Default::default(); - hash.write(typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options).as_bytes()); - hash.finish() as u32 -} - -/// Returns a KCFI type metadata identifier for the specified Instance. -pub fn kcfi_typeid_for_instance<'tcx>( - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - mut options: TypeIdOptions, -) -> u32 { - // KCFI support for Rust shares most of its implementation with the CFI support, with some key - // differences: - // - // 1. KCFI performs type tests differently and are implemented as different LLVM passes than CFI - // to not require LTO. - // 2. KCFI has the limitation that a function or method may have one type id assigned only. - // - // Because of the limitation listed above (2), the current KCFI implementation (not CFI) does - // reifying of types (i.e., adds shims/trampolines for indirect calls in these cases) for: - // - // * Supporting casting between function items, closures, and Fn trait objects. - // * Supporting methods being cast as function pointers. - // - // This was implemented for KCFI support in #123106 and #123052 (which introduced the - // ReifyReason). The tracking issue for KCFI support for Rust is #123479. - if matches!(instance.def, InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr))) { - options.insert(TypeIdOptions::USE_CONCRETE_SELF); - } - // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the - // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) - let mut hash: XxHash64 = Default::default(); - hash.write(typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options).as_bytes()); - hash.finish() as u32 -} diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 8cb5370bb4a..cb255fabfe2 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -45,8 +45,8 @@ pub(super) fn mangle<'tcx>( ty::InstanceDef::ThreadLocalShim(_) => Some("tls"), ty::InstanceDef::VTableShim(_) => Some("vtable"), ty::InstanceDef::ReifyShim(_, None) => Some("reify"), - ty::InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr)) => Some("reify-fnptr"), - ty::InstanceDef::ReifyShim(_, Some(ReifyReason::Vtable)) => Some("reify-vtable"), + ty::InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr)) => Some("reify_fnptr"), + ty::InstanceDef::ReifyShim(_, Some(ReifyReason::Vtable)) => Some("reify_vtable"), ty::InstanceDef::ConstructCoroutineInClosureShim { .. } | ty::InstanceDef::CoroutineKindShim { .. } => Some("fn_once"), @@ -371,6 +371,25 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { ty.print(self)?; } + ty::Pat(ty, pat) => match *pat { + ty::PatternKind::Range { start, end, include_end } => { + let consts = [ + start.unwrap_or(self.tcx.consts.unit), + end.unwrap_or(self.tcx.consts.unit), + ty::Const::from_bool(self.tcx, include_end).into(), + ]; + // HACK: Represent as tuple until we have something better. + // HACK: constants are used in arrays, even if the types don't match. + self.push("T"); + ty.print(self)?; + for ct in consts { + Ty::new_array_with_const_len(self.tcx, self.tcx.types.unit, ct) + .print(self)?; + } + self.push("E"); + } + }, + ty::Array(ty, len) => { self.push("A"); ty.print(self)?; diff --git a/compiler/rustc_target/src/abi/call/aarch64.rs b/compiler/rustc_target/src/abi/call/aarch64.rs index f99f6a3b721..04020d13f22 100644 --- a/compiler/rustc_target/src/abi/call/aarch64.rs +++ b/compiler/rustc_target/src/abi/call/aarch64.rs @@ -31,7 +31,7 @@ where RegKind::Vector => size.bits() == 64 || size.bits() == 128, }; - valid_unit.then_some(Uniform { unit, total: size }) + valid_unit.then_some(Uniform::consecutive(unit, size)) }) } @@ -60,7 +60,7 @@ where let size = ret.layout.size; let bits = size.bits(); if bits <= 128 { - ret.cast_to(Uniform { unit: Reg::i64(), total: size }); + ret.cast_to(Uniform::new(Reg::i64(), size)); return; } ret.make_indirect(); @@ -100,9 +100,9 @@ where }; if size.bits() <= 128 { if align.bits() == 128 { - arg.cast_to(Uniform { unit: Reg::i128(), total: size }); + arg.cast_to(Uniform::new(Reg::i128(), size)); } else { - arg.cast_to(Uniform { unit: Reg::i64(), total: size }); + arg.cast_to(Uniform::new(Reg::i64(), size)); } return; } diff --git a/compiler/rustc_target/src/abi/call/arm.rs b/compiler/rustc_target/src/abi/call/arm.rs index 95f6691d42a..9371e1b3958 100644 --- a/compiler/rustc_target/src/abi/call/arm.rs +++ b/compiler/rustc_target/src/abi/call/arm.rs @@ -21,7 +21,7 @@ where RegKind::Vector => size.bits() == 64 || size.bits() == 128, }; - valid_unit.then_some(Uniform { unit, total: size }) + valid_unit.then_some(Uniform::consecutive(unit, size)) }) } @@ -49,7 +49,7 @@ where let size = ret.layout.size; let bits = size.bits(); if bits <= 32 { - ret.cast_to(Uniform { unit: Reg::i32(), total: size }); + ret.cast_to(Uniform::new(Reg::i32(), size)); return; } ret.make_indirect(); @@ -78,7 +78,7 @@ where let align = arg.layout.align.abi.bytes(); let total = arg.layout.size; - arg.cast_to(Uniform { unit: if align <= 4 { Reg::i32() } else { Reg::i64() }, total }); + arg.cast_to(Uniform::consecutive(if align <= 4 { Reg::i32() } else { Reg::i64() }, total)); } pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) diff --git a/compiler/rustc_target/src/abi/call/csky.rs b/compiler/rustc_target/src/abi/call/csky.rs index 8b4328db52e..7951f28beea 100644 --- a/compiler/rustc_target/src/abi/call/csky.rs +++ b/compiler/rustc_target/src/abi/call/csky.rs @@ -18,7 +18,7 @@ fn classify_ret<Ty>(arg: &mut ArgAbi<'_, Ty>) { if total.bits() > 64 { arg.make_indirect(); } else if total.bits() > 32 { - arg.cast_to(Uniform { unit: Reg::i32(), total }); + arg.cast_to(Uniform::new(Reg::i32(), total)); } else { arg.cast_to(Reg::i32()); } @@ -38,7 +38,7 @@ fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) { if arg.layout.is_aggregate() { let total = arg.layout.size; if total.bits() > 32 { - arg.cast_to(Uniform { unit: Reg::i32(), total }); + arg.cast_to(Uniform::new(Reg::i32(), total)); } else { arg.cast_to(Reg::i32()); } diff --git a/compiler/rustc_target/src/abi/call/loongarch.rs b/compiler/rustc_target/src/abi/call/loongarch.rs index 35d4b331cb4..943b12a9fbf 100644 --- a/compiler/rustc_target/src/abi/call/loongarch.rs +++ b/compiler/rustc_target/src/abi/call/loongarch.rs @@ -195,7 +195,7 @@ where if total.bits() <= xlen { arg.cast_to(xlen_reg); } else { - arg.cast_to(Uniform { unit: xlen_reg, total: Size::from_bits(xlen * 2) }); + arg.cast_to(Uniform::new(xlen_reg, Size::from_bits(xlen * 2))); } return false; } @@ -278,10 +278,10 @@ fn classify_arg<'a, Ty, C>( if total.bits() > xlen { let align_regs = align > xlen; if is_loongarch_aggregate(arg) { - arg.cast_to(Uniform { - unit: if align_regs { double_xlen_reg } else { xlen_reg }, - total: Size::from_bits(xlen * 2), - }); + arg.cast_to(Uniform::new( + if align_regs { double_xlen_reg } else { xlen_reg }, + Size::from_bits(xlen * 2), + )); } if align_regs && is_vararg { *avail_gprs -= *avail_gprs % 2; diff --git a/compiler/rustc_target/src/abi/call/mips.rs b/compiler/rustc_target/src/abi/call/mips.rs index 57ccfe2152b..0e5a7f37a09 100644 --- a/compiler/rustc_target/src/abi/call/mips.rs +++ b/compiler/rustc_target/src/abi/call/mips.rs @@ -27,7 +27,7 @@ where if arg.layout.is_aggregate() { let pad_i32 = !offset.is_aligned(align); - arg.cast_to_and_pad_i32(Uniform { unit: Reg::i32(), total: size }, pad_i32); + arg.cast_to_and_pad_i32(Uniform::new(Reg::i32(), size), pad_i32); } else { arg.extend_integer_width_to(32); } diff --git a/compiler/rustc_target/src/abi/call/mips64.rs b/compiler/rustc_target/src/abi/call/mips64.rs index 2700f67b209..b2a2c34b980 100644 --- a/compiler/rustc_target/src/abi/call/mips64.rs +++ b/compiler/rustc_target/src/abi/call/mips64.rs @@ -68,7 +68,7 @@ where } // Cast to a uniform int structure - ret.cast_to(Uniform { unit: Reg::i64(), total: size }); + ret.cast_to(Uniform::new(Reg::i64(), size)); } else { ret.make_indirect(); } @@ -139,7 +139,7 @@ where let rest_size = size - Size::from_bytes(8) * prefix_index as u64; arg.cast_to(CastTarget { prefix, - rest: Uniform { unit: Reg::i64(), total: rest_size }, + rest: Uniform::new(Reg::i64(), rest_size), attrs: ArgAttributes { regular: ArgAttribute::default(), arg_ext: ArgExtension::None, diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index cdd3f0afd79..4502df339d1 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -255,11 +255,16 @@ pub struct Uniform { /// for 64-bit integers with a total size of 20 bytes. When the argument is actually passed, /// this size will be rounded up to the nearest multiple of `unit.size`. pub total: Size, + + /// Indicate that the argument is consecutive, in the sense that either all values need to be + /// passed in register, or all on the stack. If they are passed on the stack, there should be + /// no additional padding between elements. + pub is_consecutive: bool, } impl From<Reg> for Uniform { fn from(unit: Reg) -> Uniform { - Uniform { unit, total: unit.size } + Uniform { unit, total: unit.size, is_consecutive: false } } } @@ -267,6 +272,18 @@ impl Uniform { pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align { self.unit.align(cx) } + + /// Pass using one or more values of the given type, without requiring them to be consecutive. + /// That is, some values may be passed in register and some on the stack. + pub fn new(unit: Reg, total: Size) -> Self { + Uniform { unit, total, is_consecutive: false } + } + + /// Pass using one or more consecutive values of the given type. Either all values will be + /// passed in registers, or all on the stack. + pub fn consecutive(unit: Reg, total: Size) -> Self { + Uniform { unit, total, is_consecutive: true } + } } /// Describes the type used for `PassMode::Cast`. diff --git a/compiler/rustc_target/src/abi/call/nvptx64.rs b/compiler/rustc_target/src/abi/call/nvptx64.rs index 5c040ce9c3b..f85fa2419f0 100644 --- a/compiler/rustc_target/src/abi/call/nvptx64.rs +++ b/compiler/rustc_target/src/abi/call/nvptx64.rs @@ -35,7 +35,7 @@ where 16 => Reg::i128(), _ => unreachable!("Align is given as power of 2 no larger than 16 bytes"), }; - arg.cast_to(Uniform { unit, total: Size::from_bytes(2 * align_bytes) }); + arg.cast_to(Uniform::new(unit, Size::from_bytes(2 * align_bytes))); } else { // FIXME: find a better way to do this. See https://github.com/rust-lang/rust/issues/117271. arg.make_direct_deprecated(); diff --git a/compiler/rustc_target/src/abi/call/powerpc64.rs b/compiler/rustc_target/src/abi/call/powerpc64.rs index 2d41f77e50e..11a6cb52bab 100644 --- a/compiler/rustc_target/src/abi/call/powerpc64.rs +++ b/compiler/rustc_target/src/abi/call/powerpc64.rs @@ -2,7 +2,7 @@ // Alignment of 128 bit types is not currently handled, this will // need to be fixed when PowerPC vector support is added. -use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind, Uniform}; +use crate::abi::call::{Align, ArgAbi, FnAbi, Reg, RegKind, Uniform}; use crate::abi::{Endian, HasDataLayout, TyAbiInterface}; use crate::spec::HasTargetSpec; @@ -37,7 +37,7 @@ where RegKind::Vector => arg.layout.size.bits() == 128, }; - valid_unit.then_some(Uniform { unit, total: arg.layout.size }) + valid_unit.then_some(Uniform::consecutive(unit, arg.layout.size)) }) } @@ -81,7 +81,7 @@ where Reg::i64() }; - ret.cast_to(Uniform { unit, total: size }); + ret.cast_to(Uniform::new(unit, size)); return; } @@ -108,18 +108,20 @@ where } let size = arg.layout.size; - let (unit, total) = if size.bits() <= 64 { + if size.bits() <= 64 { // Aggregates smaller than a doubleword should appear in // the least-significant bits of the parameter doubleword. - (Reg { kind: RegKind::Integer, size }, size) + arg.cast_to(Reg { kind: RegKind::Integer, size }) } else { - // Aggregates larger than a doubleword should be padded - // at the tail to fill out a whole number of doublewords. - let reg_i64 = Reg::i64(); - (reg_i64, size.align_to(reg_i64.align(cx))) + // Aggregates larger than i64 should be padded at the tail to fill out a whole number + // of i64s or i128s, depending on the aggregate alignment. Always use an array for + // this, even if there is only a single element. + let reg = if arg.layout.align.abi.bytes() > 8 { Reg::i128() } else { Reg::i64() }; + arg.cast_to(Uniform::consecutive( + reg, + size.align_to(Align::from_bytes(reg.size.bytes()).unwrap()), + )) }; - - arg.cast_to(Uniform { unit, total }); } pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) diff --git a/compiler/rustc_target/src/abi/call/riscv.rs b/compiler/rustc_target/src/abi/call/riscv.rs index 6a38496dc57..5d4b3a9d245 100644 --- a/compiler/rustc_target/src/abi/call/riscv.rs +++ b/compiler/rustc_target/src/abi/call/riscv.rs @@ -201,7 +201,7 @@ where if total.bits() <= xlen { arg.cast_to(xlen_reg); } else { - arg.cast_to(Uniform { unit: xlen_reg, total: Size::from_bits(xlen * 2) }); + arg.cast_to(Uniform::new(xlen_reg, Size::from_bits(xlen * 2))); } return false; } @@ -284,10 +284,10 @@ fn classify_arg<'a, Ty, C>( if total.bits() > xlen { let align_regs = align > xlen; if is_riscv_aggregate(arg) { - arg.cast_to(Uniform { - unit: if align_regs { double_xlen_reg } else { xlen_reg }, - total: Size::from_bits(xlen * 2), - }); + arg.cast_to(Uniform::new( + if align_regs { double_xlen_reg } else { xlen_reg }, + Size::from_bits(xlen * 2), + )); } if align_regs && is_vararg { *avail_gprs -= *avail_gprs % 2; diff --git a/compiler/rustc_target/src/abi/call/sparc.rs b/compiler/rustc_target/src/abi/call/sparc.rs index 57ccfe2152b..0e5a7f37a09 100644 --- a/compiler/rustc_target/src/abi/call/sparc.rs +++ b/compiler/rustc_target/src/abi/call/sparc.rs @@ -27,7 +27,7 @@ where if arg.layout.is_aggregate() { let pad_i32 = !offset.is_aligned(align); - arg.cast_to_and_pad_i32(Uniform { unit: Reg::i32(), total: size }, pad_i32); + arg.cast_to_and_pad_i32(Uniform::new(Reg::i32(), size), pad_i32); } else { arg.extend_integer_width_to(32); } diff --git a/compiler/rustc_target/src/abi/call/sparc64.rs b/compiler/rustc_target/src/abi/call/sparc64.rs index cbed5b4afc1..acdcd5cc0d4 100644 --- a/compiler/rustc_target/src/abi/call/sparc64.rs +++ b/compiler/rustc_target/src/abi/call/sparc64.rs @@ -192,7 +192,7 @@ where arg.cast_to(CastTarget { prefix: data.prefix, - rest: Uniform { unit: Reg::i64(), total: rest_size }, + rest: Uniform::new(Reg::i64(), rest_size), attrs: ArgAttributes { regular: data.arg_attribute, arg_ext: ArgExtension::None, @@ -205,7 +205,7 @@ where } } - arg.cast_to(Uniform { unit: Reg::i64(), total }); + arg.cast_to(Uniform::new(Reg::i64(), total)); } pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) diff --git a/compiler/rustc_target/src/abi/call/wasm.rs b/compiler/rustc_target/src/abi/call/wasm.rs index a7a2b314a94..a773fb1e814 100644 --- a/compiler/rustc_target/src/abi/call/wasm.rs +++ b/compiler/rustc_target/src/abi/call/wasm.rs @@ -1,4 +1,4 @@ -use crate::abi::call::{ArgAbi, FnAbi, Uniform}; +use crate::abi::call::{ArgAbi, FnAbi}; use crate::abi::{HasDataLayout, TyAbiInterface}; fn unwrap_trivial_aggregate<'a, Ty, C>(cx: &C, val: &mut ArgAbi<'a, Ty>) -> bool @@ -10,7 +10,7 @@ where if let Some(unit) = val.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()) { let size = val.layout.size; if unit.size == size { - val.cast_to(Uniform { unit, total: size }); + val.cast_to(unit); return true; } } diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs index 96da0b6fd1f..021457b145f 100644 --- a/compiler/rustc_target/src/spec/base/apple/mod.rs +++ b/compiler/rustc_target/src/spec/base/apple/mod.rs @@ -368,6 +368,11 @@ fn watchos_deployment_target() -> (u32, u32) { from_set_deployment_target("WATCHOS_DEPLOYMENT_TARGET").unwrap_or((5, 0)) } +pub fn watchos_llvm_target(arch: Arch) -> String { + let (major, minor) = watchos_deployment_target(); + format!("{}-apple-watchos{}.{}.0", arch.target_name(), major, minor) +} + pub fn watchos_sim_llvm_target(arch: Arch) -> String { let (major, minor) = watchos_deployment_target(); format!("{}-apple-watchos{}.{}.0-simulator", arch.target_name(), major, minor) diff --git a/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs b/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs index 9bf09b4376d..f842a834c05 100644 --- a/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs +++ b/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs @@ -1,10 +1,11 @@ -use crate::spec::base::apple::{opts, Arch}; +use crate::spec::base::apple::{opts, watchos_llvm_target, Arch}; use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { - let base = opts("watchos", Arch::Arm64_32); + let arch = Arch::Arm64_32; + let base = opts("watchos", arch); Target { - llvm_target: "arm64_32-apple-watchos".into(), + llvm_target: watchos_llvm_target(arch).into(), metadata: crate::spec::TargetMetadata { description: None, tier: None, diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 7056288e758..0595e82b39e 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -1,5 +1,5 @@ use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -use crate::traits::{self, DefiningAnchor, ObligationCtxt, SelectionContext}; +use crate::traits::{self, ObligationCtxt, SelectionContext}; use crate::traits::TraitEngineExt as _; use rustc_hir::def_id::DefId; @@ -132,9 +132,8 @@ impl<'tcx> InferCtxtBuilder<'tcx> { R: Debug + TypeFoldable<TyCtxt<'tcx>>, Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>, { - let (infcx, key, canonical_inference_vars) = self - .with_opaque_type_inference(DefiningAnchor::Bubble) - .build_with_canonical(DUMMY_SP, canonical_key); + let (infcx, key, canonical_inference_vars) = + self.build_with_canonical(DUMMY_SP, canonical_key); let ocx = ObligationCtxt::new(&infcx); let value = operation(&ocx, key)?; ocx.make_canonicalized_query_response(canonical_inference_vars, value) diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 35f7d1d7151..8b5c029428c 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -363,6 +363,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | ty::Foreign(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) @@ -596,6 +597,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | ty::Foreign(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) @@ -684,6 +686,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | ty::Foreign(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs index 00cd4b48797..8a96d810134 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -50,7 +50,9 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( Ok(vec![ty::Binder::dummy(element_ty)]) } - ty::Array(element_ty, _) | ty::Slice(element_ty) => Ok(vec![ty::Binder::dummy(element_ty)]), + ty::Pat(element_ty, _) | ty::Array(element_ty, _) | ty::Slice(element_ty) => { + Ok(vec![ty::Binder::dummy(element_ty)]) + } ty::Tuple(tys) => { // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet @@ -114,6 +116,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( | ty::Coroutine(..) | ty::CoroutineWitness(..) | ty::Array(..) + | ty::Pat(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never @@ -177,6 +180,10 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( | ty::Ref(_, _, Mutability::Not) | ty::Array(..) => Err(NoSolution), + // Cannot implement in core, as we can't be generic over patterns yet, + // so we'd have to list all patterns and type combinations. + ty::Pat(ty, ..) => Ok(vec![ty::Binder::dummy(ty)]), + ty::Dynamic(..) | ty::Str | ty::Slice(_) @@ -347,6 +354,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( | ty::CoroutineWitness(..) | ty::Never | ty::Tuple(_) + | ty::Pat(_, _) | ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) @@ -526,6 +534,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc | ty::Foreign(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 4a4efb6884f..0b37163d597 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -68,7 +68,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &mut orig_values, QueryInput { goal, - anchor: self.infcx.defining_use_anchor, predefined_opaques_in_body: self .tcx() .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }), diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs index 1739bd70e7b..0154aff12b6 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs @@ -1,5 +1,5 @@ use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::DefId; use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -230,7 +230,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { .infer_ctxt() .intercrate(intercrate) .with_next_trait_solver(true) - .with_opaque_type_inference(canonical_input.value.anchor) .build_with_canonical(DUMMY_SP, &canonical_input); let mut ecx = EvalCtxt { @@ -936,8 +935,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } - pub(super) fn can_define_opaque_ty(&self, def_id: LocalDefId) -> bool { - self.infcx.opaque_type_origin(def_id).is_some() + pub(super) fn can_define_opaque_ty(&self, def_id: impl Into<DefId>) -> bool { + self.infcx.can_define_opaque_ty(def_id) } pub(super) fn insert_hidden_type( diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 8294a8a67b1..da5cd15a10d 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -316,5 +316,6 @@ fn response_no_constraints_raw<'tcx>( external_constraints: tcx.mk_external_constraints(ExternalConstraintsData::default()), certainty, }, + defining_opaque_types: Default::default(), } } diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index fb296d55100..befde8f768a 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -533,6 +533,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { | ty::Uint(..) | ty::Float(..) | ty::Array(..) + | ty::Pat(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnDef(..) @@ -768,6 +769,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { | ty::Uint(..) | ty::Float(..) | ty::Array(..) + | ty::Pat(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnDef(..) diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index eb3ad0aa782..e522339358a 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -155,10 +155,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { if let ty::Alias(ty::Opaque, opaque_ty) = goal.predicate.self_ty().kind() { if matches!(goal.param_env.reveal(), Reveal::All) || matches!(ecx.solver_mode(), SolverMode::Coherence) - || opaque_ty - .def_id - .as_local() - .is_some_and(|def_id| ecx.can_define_opaque_ty(def_id)) + || ecx.can_define_opaque_ty(opaque_ty.def_id) { return Err(NoSolution); } @@ -1051,6 +1048,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | ty::Float(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 8625ad378f7..77eaa4fd03e 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -883,6 +883,7 @@ where | ty::Float(..) | ty::Str | ty::FnDef(..) + | ty::Pat(..) | ty::FnPtr(_) | ty::Array(..) | ty::Slice(..) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 10c03387a5b..837b784f272 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -40,7 +40,7 @@ pub struct ImplCandidate<'tcx> { enum GetSafeTransmuteErrorAndReason { Silent, - Error { err_msg: String, safe_transmute_explanation: String }, + Error { err_msg: String, safe_transmute_explanation: Option<String> }, } struct UnsatisfiedConst(pub bool); 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 144971b63c0..1b8b09ddda1 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 @@ -558,7 +558,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation, - } => (err_msg, Some(safe_transmute_explanation)), + } => (err_msg, safe_transmute_explanation), } } else { (err_msg, None) @@ -1804,6 +1804,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ty::Foreign(..) => Some(19), ty::CoroutineWitness(..) => Some(20), ty::CoroutineClosure(..) => Some(21), + ty::Pat(..) => Some(22), ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None, } } @@ -3067,28 +3068,33 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { return GetSafeTransmuteErrorAndReason::Silent; }; + let dst = trait_ref.args.type_at(0); + let src = trait_ref.args.type_at(1); + let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`"); + match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable( obligation.cause, src_and_dst, assume, ) { Answer::No(reason) => { - let dst = trait_ref.args.type_at(0); - let src = trait_ref.args.type_at(1); - let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`"); let safe_transmute_explanation = match reason { rustc_transmute::Reason::SrcIsNotYetSupported => { - format!("analyzing the transmutability of `{src}` is not yet supported.") + format!("analyzing the transmutability of `{src}` is not yet supported") } rustc_transmute::Reason::DstIsNotYetSupported => { - format!("analyzing the transmutability of `{dst}` is not yet supported.") + format!("analyzing the transmutability of `{dst}` is not yet supported") } rustc_transmute::Reason::DstIsBitIncompatible => { format!("at least one value of `{src}` isn't a bit-valid value of `{dst}`") } + rustc_transmute::Reason::DstUninhabited => { + format!("`{dst}` is uninhabited") + } + rustc_transmute::Reason::DstMayHaveSafetyInvariants => { format!("`{dst}` may carry safety invariants") } @@ -3134,14 +3140,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { format!("`{dst}` has an unknown layout") } }; - GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation } + GetSafeTransmuteErrorAndReason::Error { + err_msg, + safe_transmute_explanation: Some(safe_transmute_explanation), + } } // Should never get a Yes at this point! We already ran it before, and did not get a Yes. Answer::Yes => span_bug!( span, "Inconsistent rustc_transmute::is_transmutable(...) result, got Yes", ), - other => span_bug!(span, "Unsupported rustc_transmute::Answer variant: `{other:?}`"), + // Reached when a different obligation (namely `Freeze`) causes the + // transmutability analysis to fail. In this case, silence the + // transmutability error message in favor of that more specific + // error. + Answer::If(_) => { + GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation: None } + } } } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 2c8116b779b..98d5b466cd0 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -119,7 +119,9 @@ pub fn predicates_for_generics<'tcx>( /// Determines whether the type `ty` is known to meet `bound` and /// returns true if so. Returns false if `ty` either does not meet -/// `bound` or is not known to meet bound. +/// `bound` or is not known to meet bound (note that this is +/// conservative towards *no impl*, which is the opposite of the +/// `evaluate` methods). pub fn type_known_to_meet_bound_modulo_regions<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -127,8 +129,50 @@ pub fn type_known_to_meet_bound_modulo_regions<'tcx>( def_id: DefId, ) -> bool { let trait_ref = ty::TraitRef::new(infcx.tcx, def_id, [ty]); - let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, trait_ref); - infcx.predicate_must_hold_modulo_regions(&obligation) + pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref) +} + +/// FIXME(@lcnr): this function doesn't seem right and shouldn't exist? +/// +/// Ping me on zulip if you want to use this method and need help with finding +/// an appropriate replacement. +#[instrument(level = "debug", skip(infcx, param_env, pred), ret)] +fn pred_known_to_hold_modulo_regions<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + pred: impl ToPredicate<'tcx>, +) -> bool { + let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, pred); + + let result = infcx.evaluate_obligation_no_overflow(&obligation); + debug!(?result); + + if result.must_apply_modulo_regions() { + true + } else if result.may_apply() { + // Sometimes obligations are ambiguous because the recursive evaluator + // is not smart enough, so we fall back to fulfillment when we're not certain + // that an obligation holds or not. Even still, we must make sure that + // the we do no inference in the process of checking this obligation. + let goal = infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env)); + infcx.probe(|_| { + let ocx = ObligationCtxt::new(infcx); + ocx.register_obligation(obligation); + + let errors = ocx.select_all_or_error(); + match errors.as_slice() { + // Only known to hold if we did no inference. + [] => infcx.shallow_resolve(goal) == goal, + + errors => { + debug!(?errors); + false + } + } + }) + } else { + false + } } #[instrument(level = "debug", skip(tcx, elaborated_env))] diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 56b25b0fe6c..8d04fd45940 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1048,6 +1048,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Foreign(_) | ty::Str | ty::Array(..) + | ty::Pat(..) | ty::Slice(_) | ty::RawPtr(..) | ty::Ref(..) @@ -1099,6 +1100,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Float(_) | ty::Str | ty::Array(..) + | ty::Pat(..) | ty::Slice(_) | ty::RawPtr(..) | ty::Ref(..) diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index aaa38d14d6e..326c68e01db 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -42,8 +42,8 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { | ty::Foreign(..) | ty::Error(_) => true, - // [T; N] and [T] have same properties as T. - ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, *ty), + // `T is PAT`, `[T; N]`, and `[T]` have same properties as T. + ty::Pat(ty, _) | ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, *ty), // (T1..Tn) and closures have same properties as T1..Tn -- // check if *all* of them are trivial. @@ -222,7 +222,7 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( // these types never have a destructor } - ty::Array(ety, _) | ty::Slice(ety) => { + ty::Pat(ety, _) | ty::Array(ety, _) | ty::Slice(ety) => { // single-element containers, behave like their element rustc_data_structures::stack::ensure_sufficient_stack(|| { dtorck_constraint_for_ty_inner(tcx, param_env, span, depth + 1, *ety, constraints) diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index faf218131b8..ae4cdb9258e 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -159,53 +159,70 @@ where .0); } + let mut error_info = None; let mut region_constraints = QueryRegionConstraints::default(); - let (output, error_info, mut obligations, _) = - Q::fully_perform_into(self, infcx, &mut region_constraints, span).map_err(|_| { - infcx.dcx().span_delayed_bug(span, format!("error performing {self:?}")) - })?; - - // Typically, instantiating NLL query results does not - // create obligations. However, in some cases there - // are unresolved type variables, and unify them *can* - // create obligations. In that case, we have to go - // fulfill them. We do this via a (recursive) query. - while !obligations.is_empty() { - trace!("{:#?}", obligations); - let mut progress = false; - for obligation in std::mem::take(&mut obligations) { - let obligation = infcx.resolve_vars_if_possible(obligation); - match ProvePredicate::fully_perform_into( - obligation.param_env.and(ProvePredicate::new(obligation.predicate)), - infcx, - &mut region_constraints, - span, - ) { - Ok(((), _, new, certainty)) => { - obligations.extend(new); - progress = true; - if let Certainty::Ambiguous = certainty { - obligations.push(obligation); + + // HACK(type_alias_impl_trait): When moving an opaque type to hidden type mapping from the query to the current inferctxt, + // we sometimes end up with `Opaque<'a> = Opaque<'b>` instead of an actual hidden type. In that case we don't register a + // hidden type but just equate the lifetimes. Thus we need to scrape the region constraints even though we're also manually + // collecting region constraints via `region_constraints`. + let (mut output, _) = scrape_region_constraints( + infcx, + |_ocx| { + let (output, ei, mut obligations, _) = + Q::fully_perform_into(self, infcx, &mut region_constraints, span)?; + error_info = ei; + + // Typically, instantiating NLL query results does not + // create obligations. However, in some cases there + // are unresolved type variables, and unify them *can* + // create obligations. In that case, we have to go + // fulfill them. We do this via a (recursive) query. + while !obligations.is_empty() { + trace!("{:#?}", obligations); + let mut progress = false; + for obligation in std::mem::take(&mut obligations) { + let obligation = infcx.resolve_vars_if_possible(obligation); + match ProvePredicate::fully_perform_into( + obligation.param_env.and(ProvePredicate::new(obligation.predicate)), + infcx, + &mut region_constraints, + span, + ) { + Ok(((), _, new, certainty)) => { + obligations.extend(new); + progress = true; + if let Certainty::Ambiguous = certainty { + obligations.push(obligation); + } + } + Err(_) => obligations.push(obligation), } } - Err(_) => obligations.push(obligation), + if !progress { + infcx.dcx().span_bug( + span, + format!("ambiguity processing {obligations:?} from {self:?}"), + ); + } } - } - if !progress { - infcx - .dcx() - .span_bug(span, format!("ambiguity processing {obligations:?} from {self:?}")); - } - } - - Ok(TypeOpOutput { - output, - constraints: if region_constraints.is_empty() { - None - } else { - Some(infcx.tcx.arena.alloc(region_constraints)) + Ok(output) }, - error_info, - }) + "fully_perform", + span, + )?; + output.error_info = error_info; + if let Some(constraints) = output.constraints { + region_constraints + .member_constraints + .extend(constraints.member_constraints.iter().cloned()); + region_constraints.outlives.extend(constraints.outlives.iter().cloned()); + } + output.constraints = if region_constraints.is_empty() { + None + } else { + Some(infcx.tcx.arena.alloc(region_constraints)) + }; + Ok(output) } } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index c6ea596b819..c1f340dfc7c 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -670,6 +670,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Foreign(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) @@ -803,6 +804,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Float(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) | ty::Adt(..) | ty::RawPtr(_, _) @@ -1193,6 +1195,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Never | ty::Foreign(_) | ty::Array(..) + | ty::Pat(..) | ty::Slice(_) | ty::Closure(..) | ty::CoroutineClosure(..) @@ -1270,6 +1273,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) + | ty::Pat(_, _) | ty::FnPtr(_) | ty::Dynamic(_, _, _) | ty::Closure(..) @@ -1329,6 +1333,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Foreign(..) | ty::Str | ty::Array(..) + | ty::Pat(..) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(..) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 0459246553b..25ba985397e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -314,12 +314,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .flat_map(|cond| flatten_answer_tree(tcx, obligation, predicate, cond)) .collect(), Condition::IfTransmutable { src, dst } => { - let trait_def_id = obligation.predicate.def_id(); + let transmute_trait = obligation.predicate.def_id(); let assume_const = predicate.trait_ref.args.const_at(2); - let make_obl = |from_ty, to_ty| { - let trait_ref1 = ty::TraitRef::new( + let make_transmute_obl = |from_ty, to_ty| { + let trait_ref = ty::TraitRef::new( tcx, - trait_def_id, + transmute_trait, [ ty::GenericArg::from(to_ty), ty::GenericArg::from(from_ty), @@ -331,17 +331,45 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.cause.clone(), obligation.recursion_depth + 1, obligation.param_env, - trait_ref1, + trait_ref, ) }; + let make_freeze_obl = |ty| { + let trait_ref = ty::TraitRef::new( + tcx, + tcx.lang_items().freeze_trait().unwrap(), + [ty::GenericArg::from(ty)], + ); + Obligation::with_depth( + tcx, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + trait_ref, + ) + }; + + let mut obls = vec![]; + + // If the source is a shared reference, it must be `Freeze`; + // otherwise, transmuting could lead to data races. + if src.mutability == Mutability::Not { + obls.extend([make_freeze_obl(src.ty), make_freeze_obl(dst.ty)]) + } + // If Dst is mutable, check bidirectionally. // For example, transmuting bool -> u8 is OK as long as you can't update that u8 // to be > 1, because you could later transmute the u8 back to a bool and get UB. match dst.mutability { - Mutability::Not => vec![make_obl(src.ty, dst.ty)], - Mutability::Mut => vec![make_obl(src.ty, dst.ty), make_obl(dst.ty, src.ty)], + Mutability::Not => obls.push(make_transmute_obl(src.ty, dst.ty)), + Mutability::Mut => obls.extend([ + make_transmute_obl(src.ty, dst.ty), + make_transmute_obl(dst.ty, src.ty), + ]), } + + obls } } } @@ -1417,7 +1445,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // These types are built-in, so we can fast-track by registering // nested predicates for their constituent type(s) - ty::Array(ty, _) | ty::Slice(ty) => { + ty::Array(ty, _) | ty::Slice(ty) | ty::Pat(ty, _) => { stack.push(ty); } ty::Tuple(tys) => { @@ -1469,7 +1497,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // If we have any other type (e.g. an ADT), just register a nested obligation // since it's either not `const Drop` (and we raise an error during selection), // or it's an ADT (and we need to check for a custom impl during selection) - _ => { + ty::Error(_) + | ty::Dynamic(..) + | ty::CoroutineClosure(..) + | ty::Param(_) + | ty::Bound(..) + | ty::Adt(..) + | ty::Alias(ty::Opaque | ty::Weak, _) + | ty::Infer(_) + | ty::Placeholder(_) => { let predicate = self_ty.rebind(ty::TraitPredicate { trait_ref: ty::TraitRef::from_lang_item( self.tcx(), diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 5e9a28b5cce..e363119393a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2142,6 +2142,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> { obligation.predicate.rebind(tys.last().map_or_else(Vec::new, |&last| vec![last])), ), + ty::Pat(ty, _) => Where(obligation.predicate.rebind(vec![*ty])), + ty::Adt(def, args) => { if let Some(sized_crit) = def.sized_constraint(self.tcx()) { // (*) binder moved here @@ -2202,6 +2204,11 @@ impl<'tcx> SelectionContext<'_, 'tcx> { Where(obligation.predicate.rebind(tys.iter().collect())) } + ty::Pat(ty, _) => { + // (*) binder moved here + Where(obligation.predicate.rebind(vec![ty])) + } + ty::Coroutine(coroutine_def_id, args) => { match self.tcx().coroutine_movability(coroutine_def_id) { hir::Movability::Static => None, @@ -2340,7 +2347,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { ty::RawPtr(element_ty, _) | ty::Ref(_, element_ty, _) => t.rebind(vec![element_ty]), - ty::Array(element_ty, _) | ty::Slice(element_ty) => t.rebind(vec![element_ty]), + ty::Pat(ty, _) | ty::Array(ty, _) | ty::Slice(ty) => t.rebind(vec![ty]), ty::Tuple(tys) => { // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index b89406ca023..6778ac81aea 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -126,7 +126,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Search<'tcx> { return ControlFlow::Continue(()); } - ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => { + ty::Pat(..) | ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => { // First check all contained types and then tell the caller to continue searching. return ty.super_visit_with(self); } diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index a44a5ae0e6b..5553490542b 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -681,6 +681,10 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> { // Note that we handle the len is implicitly checked while walking `arg`. } + ty::Pat(subty, _) => { + self.require_sized(subty, traits::MiscObligation); + } + ty::Tuple(tys) => { if let Some((_last, rest)) = tys.split_last() { for &elem in rest { diff --git a/compiler/rustc_transmute/src/layout/dfa.rs b/compiler/rustc_transmute/src/layout/dfa.rs index b8922696e30..77d5a48f158 100644 --- a/compiler/rustc_transmute/src/layout/dfa.rs +++ b/compiler/rustc_transmute/src/layout/dfa.rs @@ -35,6 +35,7 @@ impl<R> Transitions<R> where R: Ref, { + #[allow(dead_code)] fn insert(&mut self, transition: Transition<R>, state: State) { match transition { Transition::Byte(b) => { @@ -82,6 +83,7 @@ impl<R> Dfa<R> where R: Ref, { + #[allow(dead_code)] pub(crate) fn unit() -> Self { let transitions: Map<State, Transitions<R>> = Map::default(); let start = State::new(); diff --git a/compiler/rustc_transmute/src/layout/nfa.rs b/compiler/rustc_transmute/src/layout/nfa.rs index 78fcceb5f2c..3c5963202c6 100644 --- a/compiler/rustc_transmute/src/layout/nfa.rs +++ b/compiler/rustc_transmute/src/layout/nfa.rs @@ -160,6 +160,7 @@ where Self { transitions, start, accepting } } + #[allow(dead_code)] pub(crate) fn edges_from(&self, start: State) -> Option<&Map<Transition<R>, Set<State>>> { self.transitions.get(&start) } diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index f6bc224c7e7..12c984f1603 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -173,16 +173,20 @@ pub(crate) mod rustc { use super::Tree; use crate::layout::rustc::{Def, Ref}; + use rustc_middle::ty::layout::HasTyCtxt; + use rustc_middle::ty::layout::LayoutCx; use rustc_middle::ty::layout::LayoutError; + use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::AdtDef; - use rustc_middle::ty::GenericArgsRef; - use rustc_middle::ty::ParamEnv; + use rustc_middle::ty::AdtKind; + use rustc_middle::ty::List; use rustc_middle::ty::ScalarInt; - use rustc_middle::ty::VariantDef; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::ErrorGuaranteed; - use rustc_target::abi::Align; - use std::alloc; + use rustc_target::abi::FieldsShape; + use rustc_target::abi::Size; + use rustc_target::abi::TyAndLayout; + use rustc_target::abi::Variants; #[derive(Debug, Copy, Clone)] pub(crate) enum Err { @@ -206,176 +210,64 @@ pub(crate) mod rustc { } } - trait LayoutExt { - fn clamp_align(&self, min_align: Align, max_align: Align) -> Self; - } - - impl LayoutExt for alloc::Layout { - fn clamp_align(&self, min_align: Align, max_align: Align) -> Self { - let min_align = min_align.bytes().try_into().unwrap(); - let max_align = max_align.bytes().try_into().unwrap(); - Self::from_size_align(self.size(), self.align().clamp(min_align, max_align)).unwrap() - } - } - - struct LayoutSummary { - total_align: Align, - total_size: usize, - discriminant_size: usize, - discriminant_align: Align, - } - - impl LayoutSummary { - fn from_ty<'tcx>(ty: Ty<'tcx>, ctx: TyCtxt<'tcx>) -> Result<Self, &'tcx LayoutError<'tcx>> { - use rustc_middle::ty::ParamEnvAnd; - use rustc_target::abi::{TyAndLayout, Variants}; - - let param_env = ParamEnv::reveal_all(); - let param_env_and_type = ParamEnvAnd { param_env, value: ty }; - let TyAndLayout { layout, .. } = ctx.layout_of(param_env_and_type)?; - - let total_size: usize = layout.size().bytes_usize(); - let total_align: Align = layout.align().abi; - let discriminant_align: Align; - let discriminant_size: usize; - - if let Variants::Multiple { tag, .. } = layout.variants() { - discriminant_align = tag.align(&ctx).abi; - discriminant_size = tag.size(&ctx).bytes_usize(); - } else { - discriminant_align = Align::ONE; - discriminant_size = 0; - }; - - Ok(Self { total_align, total_size, discriminant_align, discriminant_size }) - } - - fn into(&self) -> alloc::Layout { - alloc::Layout::from_size_align( - self.total_size, - self.total_align.bytes().try_into().unwrap(), - ) - .unwrap() - } - } - impl<'tcx> Tree<Def<'tcx>, Ref<'tcx>> { - pub fn from_ty(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Result<Self, Err> { - use rustc_middle::ty::FloatTy::*; - use rustc_middle::ty::IntTy::*; - use rustc_middle::ty::UintTy::*; + pub fn from_ty( + ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>, + cx: LayoutCx<'tcx, TyCtxt<'tcx>>, + ) -> Result<Self, Err> { use rustc_target::abi::HasDataLayout; - if let Err(e) = ty.error_reported() { + if let Err(e) = ty_and_layout.ty.error_reported() { return Err(Err::TypeError(e)); } - let target = tcx.data_layout(); + let target = cx.tcx.data_layout(); + let pointer_size = target.pointer_size; - match ty.kind() { + match ty_and_layout.ty.kind() { ty::Bool => Ok(Self::bool()), - ty::Int(I8) | ty::Uint(U8) => Ok(Self::u8()), - ty::Int(I16) | ty::Uint(U16) => Ok(Self::number(2)), - ty::Int(I32) | ty::Uint(U32) | ty::Float(F32) => Ok(Self::number(4)), - ty::Int(I64) | ty::Uint(U64) | ty::Float(F64) => Ok(Self::number(8)), - ty::Int(I128) | ty::Uint(U128) => Ok(Self::number(16)), - ty::Int(Isize) | ty::Uint(Usize) => { - Ok(Self::number(target.pointer_size.bytes_usize())) + ty::Float(nty) => { + let width = nty.bit_width() / 8; + Ok(Self::number(width as _)) } - ty::Tuple(members) => { - if members.len() == 0 { - Ok(Tree::unit()) - } else { - Err(Err::NotYetSupported) - } + ty::Int(nty) => { + let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8; + Ok(Self::number(width as _)) } - ty::Array(ty, len) => { - let len = len - .try_eval_target_usize(tcx, ParamEnv::reveal_all()) - .ok_or(Err::NotYetSupported)?; - let elt = Tree::from_ty(*ty, tcx)?; - Ok(std::iter::repeat(elt) - .take(len as usize) - .fold(Tree::unit(), |tree, elt| tree.then(elt))) + ty::Uint(nty) => { + let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8; + Ok(Self::number(width as _)) } - ty::Adt(adt_def, args_ref) => { - use rustc_middle::ty::AdtKind; + ty::Tuple(members) => Self::from_tuple(ty_and_layout, members, cx), - // If the layout is ill-specified, halt. - if !(adt_def.repr().c() || adt_def.repr().int.is_some()) { + ty::Array(inner_ty, len) => { + let FieldsShape::Array { stride, count } = &ty_and_layout.fields else { return Err(Err::NotYetSupported); - } + }; + let inner_ty_and_layout = cx.layout_of(*inner_ty)?; + assert_eq!(*stride, inner_ty_and_layout.size); + let elt = Tree::from_ty(inner_ty_and_layout, cx)?; + Ok(std::iter::repeat(elt) + .take(*count as usize) + .fold(Tree::unit(), |tree, elt| tree.then(elt))) + } - // Compute a summary of the type's layout. - let layout_summary = LayoutSummary::from_ty(ty, tcx)?; - - // The layout begins with this adt's visibility. - let vis = Self::def(Def::Adt(*adt_def)); - - // And is followed the layout(s) of its variants - Ok(vis.then(match adt_def.adt_kind() { - AdtKind::Struct => Self::from_repr_c_variant( - ty, - *adt_def, - args_ref, - &layout_summary, - None, - adt_def.non_enum_variant(), - tcx, - )?, - AdtKind::Enum => { - trace!(?adt_def, "treeifying enum"); - let mut tree = Tree::uninhabited(); - - for (idx, variant) in adt_def.variants().iter_enumerated() { - let tag = tcx.tag_for_variant((ty, idx)); - tree = tree.or(Self::from_repr_c_variant( - ty, - *adt_def, - args_ref, - &layout_summary, - tag, - variant, - tcx, - )?); - } - - tree - } - AdtKind::Union => { - // is the layout well-defined? - if !adt_def.repr().c() { - return Err(Err::NotYetSupported); - } - - let ty_layout = layout_of(tcx, ty)?; - - let mut tree = Tree::uninhabited(); - - for field in adt_def.all_fields() { - let variant_ty = field.ty(tcx, args_ref); - let variant_layout = layout_of(tcx, variant_ty)?; - let padding_needed = ty_layout.size() - variant_layout.size(); - let variant = Self::def(Def::Field(field)) - .then(Self::from_ty(variant_ty, tcx)?) - .then(Self::padding(padding_needed)); - - tree = tree.or(variant); - } - - tree - } - })) + ty::Adt(adt_def, _args_ref) if !ty_and_layout.ty.is_box() => { + match adt_def.adt_kind() { + AdtKind::Struct => Self::from_struct(ty_and_layout, *adt_def, cx), + AdtKind::Enum => Self::from_enum(ty_and_layout, *adt_def, cx), + AdtKind::Union => Self::from_union(ty_and_layout, *adt_def, cx), + } } ty::Ref(lifetime, ty, mutability) => { - let layout = layout_of(tcx, *ty)?; - let align = layout.align(); - let size = layout.size(); + let ty_and_layout = cx.layout_of(*ty)?; + let align = ty_and_layout.align.abi.bytes() as usize; + let size = ty_and_layout.size.bytes_usize(); Ok(Tree::Ref(Ref { lifetime: *lifetime, ty: *ty, @@ -389,80 +281,143 @@ pub(crate) mod rustc { } } - fn from_repr_c_variant( - ty: Ty<'tcx>, - adt_def: AdtDef<'tcx>, - args_ref: GenericArgsRef<'tcx>, - layout_summary: &LayoutSummary, - tag: Option<ScalarInt>, - variant_def: &'tcx VariantDef, - tcx: TyCtxt<'tcx>, + /// Constructs a `Tree` from a tuple. + fn from_tuple( + ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>, + members: &'tcx List<Ty<'tcx>>, + cx: LayoutCx<'tcx, TyCtxt<'tcx>>, ) -> Result<Self, Err> { - let mut tree = Tree::unit(); - - let repr = adt_def.repr(); - let min_align = repr.align.unwrap_or(Align::ONE); - let max_align = repr.pack.unwrap_or(Align::MAX); - - let variant_span = trace_span!( - "treeifying variant", - min_align = ?min_align, - max_align = ?max_align, - ) - .entered(); - - let mut variant_layout = alloc::Layout::from_size_align( - 0, - layout_summary.total_align.bytes().try_into().unwrap(), - ) - .unwrap(); - - // The layout of the variant is prefixed by the tag, if any. - if let Some(tag) = tag { - let tag_layout = - alloc::Layout::from_size_align(tag.size().bytes_usize(), 1).unwrap(); - tree = tree.then(Self::from_tag(tag, tcx)); - variant_layout = variant_layout.extend(tag_layout).unwrap().0; + match &ty_and_layout.fields { + FieldsShape::Primitive => { + assert_eq!(members.len(), 1); + let inner_ty = members[0]; + let inner_ty_and_layout = cx.layout_of(inner_ty)?; + assert_eq!(ty_and_layout.layout, inner_ty_and_layout.layout); + Self::from_ty(inner_ty_and_layout, cx) + } + FieldsShape::Arbitrary { offsets, .. } => { + assert_eq!(offsets.len(), members.len()); + Self::from_variant(Def::Primitive, None, ty_and_layout, ty_and_layout.size, cx) + } + FieldsShape::Array { .. } | FieldsShape::Union(_) => Err(Err::NotYetSupported), + } + } + + /// Constructs a `Tree` from a struct. + /// + /// # Panics + /// + /// Panics if `def` is not a struct definition. + fn from_struct( + ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>, + def: AdtDef<'tcx>, + cx: LayoutCx<'tcx, TyCtxt<'tcx>>, + ) -> Result<Self, Err> { + assert!(def.is_struct()); + let def = Def::Adt(def); + Self::from_variant(def, None, ty_and_layout, ty_and_layout.size, cx) + } + + /// Constructs a `Tree` from an enum. + /// + /// # Panics + /// + /// Panics if `def` is not an enum definition. + fn from_enum( + ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>, + def: AdtDef<'tcx>, + cx: LayoutCx<'tcx, TyCtxt<'tcx>>, + ) -> Result<Self, Err> { + assert!(def.is_enum()); + let layout = ty_and_layout.layout; + + if let Variants::Multiple { tag_field, .. } = layout.variants() { + // For enums (but not coroutines), the tag field is + // currently always the first field of the layout. + assert_eq!(*tag_field, 0); } - // Next come fields. - let fields_span = trace_span!("treeifying fields").entered(); - for field_def in variant_def.fields.iter() { - let field_ty = field_def.ty(tcx, args_ref); - let _span = trace_span!("treeifying field", field = ?field_ty).entered(); + let variants = def.discriminants(cx.tcx()).try_fold( + Self::uninhabited(), + |variants, (idx, ref discriminant)| { + let tag = cx.tcx.tag_for_variant((ty_and_layout.ty, idx)); + let variant_def = Def::Variant(def.variant(idx)); + let variant_ty_and_layout = ty_and_layout.for_variant(&cx, idx); + let variant = Self::from_variant( + variant_def, + tag, + variant_ty_and_layout, + layout.size, + cx, + )?; + Result::<Self, Err>::Ok(variants.or(variant)) + }, + )?; - // begin with the field's visibility - tree = tree.then(Self::def(Def::Field(field_def))); + return Ok(Self::def(Def::Adt(def)).then(variants)); + } - // compute the field's layout characteristics - let field_layout = layout_of(tcx, field_ty)?.clamp_align(min_align, max_align); + /// Constructs a `Tree` from a 'variant-like' layout. + /// + /// A 'variant-like' layout includes those of structs and, of course, + /// enum variants. Pragmatically speaking, this method supports anything + /// with `FieldsShape::Arbitrary`. + /// + /// Note: This routine assumes that the optional `tag` is the first + /// field, and enum callers should check that `tag_field` is, in fact, + /// `0`. + fn from_variant( + def: Def<'tcx>, + tag: Option<ScalarInt>, + ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>, + total_size: Size, + cx: LayoutCx<'tcx, TyCtxt<'tcx>>, + ) -> Result<Self, Err> { + // This constructor does not support non-`FieldsShape::Arbitrary` + // layouts. + let FieldsShape::Arbitrary { offsets, memory_index } = ty_and_layout.layout.fields() + else { + return Err(Err::NotYetSupported); + }; - // next comes the field's padding - let padding_needed = variant_layout.padding_needed_for(field_layout.align()); - if padding_needed > 0 { - tree = tree.then(Self::padding(padding_needed)); - } + // When this function is invoked with enum variants, + // `ty_and_layout.size` does not encompass the entire size of the + // enum. We rely on `total_size` for this. + assert!(ty_and_layout.size <= total_size); - // finally, the field's layout - tree = tree.then(Self::from_ty(field_ty, tcx)?); + let mut size = Size::ZERO; + let mut struct_tree = Self::def(def); - // extend the variant layout with the field layout - variant_layout = variant_layout.extend(field_layout).unwrap().0; + // If a `tag` is provided, place it at the start of the layout. + if let Some(tag) = tag { + size += tag.size(); + struct_tree = struct_tree.then(Self::from_tag(tag, cx.tcx)); } - drop(fields_span); - // finally: padding - let padding_span = trace_span!("adding trailing padding").entered(); - if layout_summary.total_size > variant_layout.size() { - let padding_needed = layout_summary.total_size - variant_layout.size(); - tree = tree.then(Self::padding(padding_needed)); - }; - drop(padding_span); - drop(variant_span); - Ok(tree) + // Append the fields, in memory order, to the layout. + let inverse_memory_index = memory_index.invert_bijective_mapping(); + for (memory_idx, field_idx) in inverse_memory_index.iter_enumerated() { + // Add interfield padding. + let padding_needed = offsets[*field_idx] - size; + let padding = Self::padding(padding_needed.bytes_usize()); + + let field_ty_and_layout = ty_and_layout.field(&cx, field_idx.as_usize()); + let field_tree = Self::from_ty(field_ty_and_layout, cx)?; + + struct_tree = struct_tree.then(padding).then(field_tree); + + size += padding_needed + field_ty_and_layout.size; + } + + // Add trailing padding. + let padding_needed = total_size - size; + let trailing_padding = Self::padding(padding_needed.bytes_usize()); + + Ok(struct_tree.then(trailing_padding)) } - pub fn from_tag(tag: ScalarInt, tcx: TyCtxt<'tcx>) -> Self { + /// Constructs a `Tree` representing the value of a enum tag. + fn from_tag(tag: ScalarInt, tcx: TyCtxt<'tcx>) -> Self { use rustc_target::abi::Endian; let size = tag.size(); let bits = tag.to_bits(size).unwrap(); @@ -479,24 +434,42 @@ pub(crate) mod rustc { }; Self::Seq(bytes.iter().map(|&b| Self::from_bits(b)).collect()) } - } - fn layout_of<'tcx>( - ctx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - ) -> Result<alloc::Layout, &'tcx LayoutError<'tcx>> { - use rustc_middle::ty::ParamEnvAnd; - use rustc_target::abi::TyAndLayout; - - let param_env = ParamEnv::reveal_all(); - let param_env_and_type = ParamEnvAnd { param_env, value: ty }; - let TyAndLayout { layout, .. } = ctx.layout_of(param_env_and_type)?; - let layout = alloc::Layout::from_size_align( - layout.size().bytes_usize(), - layout.align().abi.bytes().try_into().unwrap(), - ) - .unwrap(); - trace!(?ty, ?layout, "computed layout for type"); - Ok(layout) + /// Constructs a `Tree` from a union. + /// + /// # Panics + /// + /// Panics if `def` is not a union definition. + fn from_union( + ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>, + def: AdtDef<'tcx>, + cx: LayoutCx<'tcx, TyCtxt<'tcx>>, + ) -> Result<Self, Err> { + assert!(def.is_union()); + + let union_layout = ty_and_layout.layout; + + // This constructor does not support non-`FieldsShape::Union` + // layouts. Fields of this shape are all placed at offset 0. + let FieldsShape::Union(fields) = union_layout.fields() else { + return Err(Err::NotYetSupported); + }; + + let fields = &def.non_enum_variant().fields; + let fields = fields.iter_enumerated().try_fold( + Self::uninhabited(), + |fields, (idx, ref field_def)| { + let field_def = Def::Field(field_def); + let field_ty_and_layout = ty_and_layout.field(&cx, idx.as_usize()); + let field = Self::from_ty(field_ty_and_layout, cx)?; + let trailing_padding_needed = union_layout.size - field_ty_and_layout.size; + let trailing_padding = Self::padding(trailing_padding_needed.bytes_usize()); + let field_and_padding = field.then(trailing_padding); + Result::<Self, Err>::Ok(fields.or(field_and_padding)) + }, + )?; + + Ok(Self::def(Def::Adt(def)).then(fields)) + } } } diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index b6f937ebe47..12312271646 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -1,6 +1,6 @@ #![feature(alloc_layout_extra)] #![feature(never_type)] -#![allow(dead_code, unused_variables)] +#![allow(unused_variables)] #[macro_use] extern crate tracing; @@ -49,6 +49,8 @@ pub enum Reason<T> { DstIsNotYetSupported, /// The layout of the destination type is bit-incompatible with the source type. DstIsBitIncompatible, + /// The destination type is uninhabited. + DstUninhabited, /// The destination type may carry safety invariants. DstMayHaveSafetyInvariants, /// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized. diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs index 16d15580a05..2789fe8f6b1 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs @@ -33,6 +33,9 @@ mod rustc { use super::*; use crate::layout::tree::rustc::Err; + use rustc_middle::ty::layout::LayoutCx; + use rustc_middle::ty::layout::LayoutOf; + use rustc_middle::ty::ParamEnv; use rustc_middle::ty::Ty; use rustc_middle::ty::TyCtxt; @@ -43,12 +46,20 @@ mod rustc { pub fn answer(self) -> Answer<<TyCtxt<'tcx> as QueryContext>::Ref> { let Self { src, dst, assume, context } = self; + let layout_cx = LayoutCx { tcx: context, param_env: ParamEnv::reveal_all() }; + let layout_of = |ty| { + layout_cx + .layout_of(ty) + .map_err(|_| Err::NotYetSupported) + .and_then(|tl| Tree::from_ty(tl, layout_cx)) + }; + // Convert `src` and `dst` from their rustc representations, to `Tree`-based // representations. If these conversions fail, conclude that the transmutation is // unacceptable; the layouts of both the source and destination types must be // well-defined. - let src = Tree::from_ty(src, context); - let dst = Tree::from_ty(dst, context); + let src = layout_of(src); + let dst = layout_of(dst); match (src, dst) { (Err(Err::TypeError(_)), _) | (_, Err(Err::TypeError(_))) => { @@ -86,6 +97,10 @@ where // references. let src = src.prune(&|def| false); + if src.is_inhabited() && !dst.is_inhabited() { + return Answer::No(Reason::DstUninhabited); + } + trace!(?src, "pruned src"); // Remove all `Def` nodes from `dst`, additionally... diff --git a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs index 54ed03d44e6..1ccb6f36c8e 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs @@ -5,8 +5,6 @@ pub(crate) trait QueryContext { type Def: layout::Def; type Ref: layout::Ref; type Scope: Copy; - - fn min_align(&self, reference: Self::Ref) -> usize; } #[cfg(test)] @@ -31,10 +29,6 @@ pub(crate) mod test { type Def = Def; type Ref = !; type Scope = (); - - fn min_align(&self, reference: !) -> usize { - unimplemented!() - } } } @@ -48,9 +42,5 @@ mod rustc { type Ref = layout::rustc::Ref<'tcx>; type Scope = Ty<'tcx>; - - fn min_align(&self, reference: Self::Ref) -> usize { - unimplemented!() - } } } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 509727cdeab..77cd4662ae5 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -126,6 +126,39 @@ fn layout_of_uncached<'tcx>( debug_assert!(!ty.has_non_region_infer()); Ok(match *ty.kind() { + ty::Pat(ty, pat) => { + let layout = cx.layout_of(ty)?.layout; + let mut layout = LayoutS::clone(&layout.0); + match *pat { + ty::PatternKind::Range { start, end, include_end } => { + if let Abi::Scalar(scalar) | Abi::ScalarPair(scalar, _) = &mut layout.abi { + if let Some(start) = start { + scalar.valid_range_mut().start = start.eval_bits(tcx, param_env); + } + if let Some(end) = end { + let mut end = end.eval_bits(tcx, param_env); + if !include_end { + end = end.wrapping_sub(1); + } + scalar.valid_range_mut().end = end; + } + + let niche = Niche { + offset: Size::ZERO, + value: scalar.primitive(), + valid_range: scalar.valid_range(cx), + }; + + layout.largest_niche = Some(niche); + + tcx.mk_layout(layout) + } else { + bug!("pattern type with range but not scalar layout: {ty:?}, {layout:?}") + } + } + } + } + // Basic scalars. ty::Bool => tcx.mk_layout(LayoutS::scalar( cx, diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index 48339e6120a..ee930a78e77 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -221,6 +221,7 @@ where | ty::Ref(..) | ty::RawPtr(..) | ty::FnDef(..) + | ty::Pat(..) | ty::FnPtr(..) | ty::Tuple(_) | ty::Bound(..) diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 547a3cb5a8c..f33234122c9 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -36,6 +36,8 @@ fn sized_constraint_for_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Ty<' // these are never sized Str | Slice(..) | Dynamic(_, _, ty::Dyn) | Foreign(..) => Some(ty), + Pat(ty, _) => sized_constraint_for_ty(tcx, *ty), + Tuple(tys) => tys.last().and_then(|&ty| sized_constraint_for_ty(tcx, ty)), // recursive case diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index ad18ef24984..06a5051956a 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -16,6 +16,8 @@ use crate::{Interner, PlaceholderLike, UniverseIndex}; pub struct Canonical<I: Interner, V> { pub value: V, pub max_universe: UniverseIndex, + // FIXME(lcnr, oli-obk): try moving this into the query inputs instead + pub defining_opaque_types: I::DefiningOpaqueTypes, pub variables: I::CanonicalVars, } @@ -44,8 +46,8 @@ impl<I: Interner, V> Canonical<I, V> { /// let b: Canonical<I, (T, Ty<I>)> = a.unchecked_map(|v| (v, ty)); /// ``` pub fn unchecked_map<W>(self, map_op: impl FnOnce(V) -> W) -> Canonical<I, W> { - let Canonical { max_universe, variables, value } = self; - Canonical { max_universe, variables, value: map_op(value) } + let Canonical { defining_opaque_types, max_universe, variables, value } = self; + Canonical { defining_opaque_types, max_universe, variables, value: map_op(value) } } /// Allows you to map the `value` of a canonical while keeping the same set of @@ -54,8 +56,8 @@ impl<I: Interner, V> Canonical<I, V> { /// **WARNING:** This function is very easy to mis-use, hence the name! See /// the comment of [Canonical::unchecked_map] for more details. pub fn unchecked_rebind<W>(self, value: W) -> Canonical<I, W> { - let Canonical { max_universe, variables, value: _ } = self; - Canonical { max_universe, variables, value } + let Canonical { defining_opaque_types, max_universe, variables, value: _ } = self; + Canonical { defining_opaque_types, max_universe, variables, value } } } @@ -63,28 +65,32 @@ impl<I: Interner, V: Eq> Eq for Canonical<I, V> {} impl<I: Interner, V: PartialEq> PartialEq for Canonical<I, V> { fn eq(&self, other: &Self) -> bool { - self.value == other.value - && self.max_universe == other.max_universe - && self.variables == other.variables + let Self { value, max_universe, variables, defining_opaque_types } = self; + *value == other.value + && *max_universe == other.max_universe + && *variables == other.variables + && *defining_opaque_types == other.defining_opaque_types } } impl<I: Interner, V: fmt::Display> fmt::Display for Canonical<I, V> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { value, max_universe, variables, defining_opaque_types } = self; write!( f, - "Canonical {{ value: {}, max_universe: {:?}, variables: {:?} }}", - self.value, self.max_universe, self.variables + "Canonical {{ value: {value}, max_universe: {max_universe:?}, variables: {variables:?}, defining_opaque_types: {defining_opaque_types:?} }}", ) } } impl<I: Interner, V: fmt::Debug> fmt::Debug for Canonical<I, V> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { value, max_universe, variables, defining_opaque_types } = self; f.debug_struct("Canonical") - .field("value", &self.value) - .field("max_universe", &self.max_universe) - .field("variables", &self.variables) + .field("value", &value) + .field("max_universe", &max_universe) + .field("variables", &variables) + .field("defining_opaque_types", &defining_opaque_types) .finish() } } @@ -100,6 +106,7 @@ where value: self.value.try_fold_with(folder)?, max_universe: self.max_universe.try_fold_with(folder)?, variables: self.variables.try_fold_with(folder)?, + defining_opaque_types: self.defining_opaque_types, }) } } @@ -109,9 +116,11 @@ where I::CanonicalVars: TypeVisitable<I>, { fn visit_with<F: TypeVisitor<I>>(&self, folder: &mut F) -> F::Result { - try_visit!(self.value.visit_with(folder)); - try_visit!(self.max_universe.visit_with(folder)); - self.variables.visit_with(folder) + let Self { value, max_universe, variables, defining_opaque_types } = self; + try_visit!(value.visit_with(folder)); + try_visit!(max_universe.visit_with(folder)); + try_visit!(defining_opaque_types.visit_with(folder)); + variables.visit_with(folder) } } diff --git a/compiler/rustc_type_ir/src/debug.rs b/compiler/rustc_type_ir/src/debug.rs index 9c8e45b4338..9298360f749 100644 --- a/compiler/rustc_type_ir/src/debug.rs +++ b/compiler/rustc_type_ir/src/debug.rs @@ -43,6 +43,10 @@ impl<I: Interner> InferCtxtLike for NoInfcx<I> { fn probe_ct_var(&self, _vid: ConstVid) -> Option<I::Const> { None } + + fn defining_opaque_types(&self) -> <Self::Interner as Interner>::DefiningOpaqueTypes { + Default::default() + } } pub trait DebugWithInfcx<I: Interner>: fmt::Debug { diff --git a/compiler/rustc_type_ir/src/infcx.rs b/compiler/rustc_type_ir/src/infcx.rs index 28b71f0ea13..a53287c1987 100644 --- a/compiler/rustc_type_ir/src/infcx.rs +++ b/compiler/rustc_type_ir/src/infcx.rs @@ -37,4 +37,6 @@ pub trait InferCtxtLike { /// Resolve `ConstVid` to its inferred type, if it has been equated with a non-infer type. fn probe_ct_var(&self, vid: ConstVid) -> Option<<Self::Interner as Interner>::Const>; + + fn defining_opaque_types(&self) -> <Self::Interner as Interner>::DefiningOpaqueTypes; } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 7d6862726f0..70f7e58be19 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -11,6 +11,7 @@ use crate::{ pub trait Interner: Sized + Copy { type DefId: Copy + Debug + Hash + Eq; + type DefiningOpaqueTypes: Copy + Debug + Hash + Default + Eq + TypeVisitable<Self>; type AdtDef: Copy + Debug + Hash + Eq; type GenericArgs: Copy @@ -49,6 +50,7 @@ pub trait Interner: Sized + Copy { type BoundExistentialPredicates: Copy + DebugWithInfcx<Self> + Hash + Eq; type PolyFnSig: Copy + DebugWithInfcx<Self> + Hash + Eq; type AllocId: Copy + Debug + Hash + Eq; + type Pat: Copy + Debug + Hash + Eq + DebugWithInfcx<Self>; // Kinds of consts type Const: Copy diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index fad67fe3cbb..8813955f2af 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -100,6 +100,13 @@ pub enum TyKind<I: Interner> { /// An array with the given length. Written as `[T; N]`. Array(I::Ty, I::Const), + /// A pattern newtype. Takes any type and restricts its valid values to its pattern. + /// This will also change the layout to take advantage of this restriction. + /// Only `Copy` and `Clone` will automatically get implemented for pattern types. + /// Auto-traits treat this as if it were an aggregate with a single nested type. + /// Only supports integer range patterns for now. + Pat(I::Ty, I::Pat), + /// The pointee of an array slice. Written as `[T]`. Slice(I::Ty), @@ -273,12 +280,13 @@ const fn tykind_discriminant<I: Interner>(value: &TyKind<I>) -> usize { CoroutineWitness(_, _) => 18, Never => 19, Tuple(_) => 20, - Alias(_, _) => 21, - Param(_) => 22, - Bound(_, _) => 23, - Placeholder(_) => 24, - Infer(_) => 25, - Error(_) => 26, + Pat(_, _) => 21, + Alias(_, _) => 22, + Param(_) => 23, + Bound(_, _) => 24, + Placeholder(_) => 25, + Infer(_) => 26, + Error(_) => 27, } } @@ -299,6 +307,7 @@ impl<I: Interner> PartialEq for TyKind<I> { (Adt(a_d, a_s), Adt(b_d, b_s)) => a_d == b_d && a_s == b_s, (Foreign(a_d), Foreign(b_d)) => a_d == b_d, (Array(a_t, a_c), Array(b_t, b_c)) => a_t == b_t && a_c == b_c, + (Pat(a_t, a_c), Pat(b_t, b_c)) => a_t == b_t && a_c == b_c, (Slice(a_t), Slice(b_t)) => a_t == b_t, (RawPtr(a_t, a_m), RawPtr(b_t, b_m)) => a_t == b_t && a_m == b_m, (Ref(a_r, a_t, a_m), Ref(b_r, b_t, b_m)) => a_r == b_r && a_t == b_t && a_m == b_m, @@ -322,7 +331,7 @@ impl<I: Interner> PartialEq for TyKind<I> { _ => { debug_assert!( tykind_discriminant(self) != tykind_discriminant(other), - "This branch must be unreachable, maybe the match is missing an arm? self = self = {self:?}, other = {other:?}" + "This branch must be unreachable, maybe the match is missing an arm? self = {self:?}, other = {other:?}" ); false } @@ -362,6 +371,7 @@ impl<I: Interner> DebugWithInfcx<I> for TyKind<I> { Foreign(d) => f.debug_tuple("Foreign").field(d).finish(), Str => write!(f, "str"), Array(t, c) => write!(f, "[{:?}; {:?}]", &this.wrap(t), &this.wrap(c)), + Pat(t, p) => write!(f, "pattern_type!({:?} is {:?})", &this.wrap(t), &this.wrap(p)), Slice(t) => write!(f, "[{:?}]", &this.wrap(t)), RawPtr(ty, mutbl) => { match mutbl { diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs index 8ed34fab54d..94c552199bc 100644 --- a/compiler/stable_mir/src/compiler_interface.rs +++ b/compiler/stable_mir/src/compiler_interface.rs @@ -8,7 +8,7 @@ use std::cell::Cell; use crate::abi::{FnAbi, Layout, LayoutShape}; use crate::mir::alloc::{AllocId, GlobalAlloc}; use crate::mir::mono::{Instance, InstanceDef, StaticDef}; -use crate::mir::{Body, Place}; +use crate::mir::{BinOp, Body, Place}; use crate::target::MachineInfo; use crate::ty::{ AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef, @@ -211,6 +211,9 @@ pub trait Context { /// Get a debug string representation of a place. fn place_pretty(&self, place: &Place) -> String; + + /// Get the resulting type of binary operation. + fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty; } // A thread local variable that stores a pointer to the tables mapping between TyCtxt diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index 593b1868f26..1ad05633d62 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -1,3 +1,4 @@ +use crate::compiler_interface::with; use crate::mir::pretty::function_body; use crate::ty::{ AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability, Region, RigidTy, Ty, TyKind, @@ -337,42 +338,7 @@ impl BinOp { /// Return the type of this operation for the given input Ty. /// This function does not perform type checking, and it currently doesn't handle SIMD. pub fn ty(&self, lhs_ty: Ty, rhs_ty: Ty) -> Ty { - match self { - BinOp::Add - | BinOp::AddUnchecked - | BinOp::Sub - | BinOp::SubUnchecked - | BinOp::Mul - | BinOp::MulUnchecked - | BinOp::Div - | BinOp::Rem - | BinOp::BitXor - | BinOp::BitAnd - | BinOp::BitOr => { - assert_eq!(lhs_ty, rhs_ty); - assert!(lhs_ty.kind().is_primitive()); - lhs_ty - } - BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => { - assert!(lhs_ty.kind().is_primitive()); - assert!(rhs_ty.kind().is_primitive()); - lhs_ty - } - BinOp::Offset => { - assert!(lhs_ty.kind().is_raw_ptr()); - assert!(rhs_ty.kind().is_integral()); - lhs_ty - } - BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => { - assert_eq!(lhs_ty, rhs_ty); - let lhs_kind = lhs_ty.kind(); - assert!(lhs_kind.is_primitive() || lhs_kind.is_raw_ptr() || lhs_kind.is_fn_ptr()); - Ty::bool_ty() - } - BinOp::Cmp => { - unimplemented!("Should cmp::Ordering be a RigidTy?"); - } - } + with(|ctx| ctx.binop_ty(*self, lhs_ty, rhs_ty)) } } diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index 3e8d186b97e..bc6fb34493a 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -99,6 +99,12 @@ impl Ty { } } +/// Represents a pattern in the type system +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Pattern { + Range { start: Option<Const>, end: Option<Const>, include_end: bool }, +} + /// Represents a constant in MIR or from the Type system. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Const { @@ -481,6 +487,7 @@ pub enum RigidTy { Foreign(ForeignDef), Str, Array(Ty, Const), + Pat(Ty, Pattern), Slice(Ty), RawPtr(Ty, Mutability), Ref(Region, Ty, Mutability), diff --git a/compiler/stable_mir/src/visitor.rs b/compiler/stable_mir/src/visitor.rs index 65e42879d61..2d7159f87fe 100644 --- a/compiler/stable_mir/src/visitor.rs +++ b/compiler/stable_mir/src/visitor.rs @@ -139,6 +139,7 @@ impl Visitable for RigidTy { t.visit(visitor)?; c.visit(visitor) } + RigidTy::Pat(t, _p) => t.visit(visitor), RigidTy::Slice(inner) => inner.visit(visitor), RigidTy::RawPtr(ty, _) => ty.visit(visitor), RigidTy::Ref(reg, ty, _) => { diff --git a/library/alloc/benches/lib.rs b/library/alloc/benches/lib.rs index 638f343fb24..0561f49c967 100644 --- a/library/alloc/benches/lib.rs +++ b/library/alloc/benches/lib.rs @@ -1,6 +1,8 @@ // Disabling on android for the time being // See https://github.com/rust-lang/rust/issues/73535#event-3477699747 #![cfg(not(target_os = "android"))] +// Disabling in Miri as these would take too long. +#![cfg(not(miri))] #![feature(btree_extract_if)] #![feature(iter_next_chunk)] #![feature(repr_simd)] diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index 0421a12b3a9..8b145b67bf1 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -4,10 +4,14 @@ use crate::alloc::{self, Layout, LayoutError}; use core::error::Error; use core::fmt::{self, Debug, Display, Formatter}; +#[cfg(not(no_global_oom_handling))] +use core::intrinsics::const_allocate; use core::marker::PhantomData; #[cfg(not(no_global_oom_handling))] use core::marker::Unsize; -use core::mem::{self, SizedTypeProperties}; +use core::mem; +#[cfg(not(no_global_oom_handling))] +use core::mem::SizedTypeProperties; use core::ops::{Deref, DerefMut}; use core::ptr::Pointee; use core::ptr::{self, NonNull}; @@ -109,9 +113,14 @@ impl<Dyn: ?Sized> ThinBox<Dyn> { where T: Unsize<Dyn>, { - let meta = ptr::metadata(&value as &Dyn); - let ptr = WithOpaqueHeader::new(meta, value); - ThinBox { ptr, _marker: PhantomData } + if mem::size_of::<T>() == 0 { + let ptr = WithOpaqueHeader::new_unsize_zst::<Dyn, T>(value); + ThinBox { ptr, _marker: PhantomData } + } else { + let meta = ptr::metadata(&value as &Dyn); + let ptr = WithOpaqueHeader::new(meta, value); + ThinBox { ptr, _marker: PhantomData } + } } } @@ -200,6 +209,16 @@ impl WithOpaqueHeader { Self(ptr.0) } + #[cfg(not(no_global_oom_handling))] + fn new_unsize_zst<Dyn, T>(value: T) -> Self + where + Dyn: ?Sized, + T: Unsize<Dyn>, + { + let ptr = WithHeader::<<Dyn as Pointee>::Metadata>::new_unsize_zst::<Dyn, T>(value); + Self(ptr.0) + } + fn try_new<H, T>(header: H, value: T) -> Result<Self, core::alloc::AllocError> { WithHeader::try_new(header, value).map(|ptr| Self(ptr.0)) } @@ -288,6 +307,58 @@ impl<H> WithHeader<H> { } } + // `Dyn` is `?Sized` type like `[u32]`, and `T` is ZST type like `[u32; 0]`. + #[cfg(not(no_global_oom_handling))] + fn new_unsize_zst<Dyn, T>(value: T) -> WithHeader<H> + where + Dyn: Pointee<Metadata = H> + ?Sized, + T: Unsize<Dyn>, + { + assert!(mem::size_of::<T>() == 0); + + const fn max(a: usize, b: usize) -> usize { + if a > b { a } else { b } + } + + // Compute a pointer to the right metadata. This will point to the beginning + // of the header, past the padding, so the assigned type makes sense. + // It also ensures that the address at the end of the header is sufficiently + // aligned for T. + let alloc: &<Dyn as Pointee>::Metadata = const { + // FIXME: just call `WithHeader::alloc_layout` with size reset to 0. + // Currently that's blocked on `Layout::extend` not being `const fn`. + + let alloc_align = + max(mem::align_of::<T>(), mem::align_of::<<Dyn as Pointee>::Metadata>()); + + let alloc_size = + max(mem::align_of::<T>(), mem::size_of::<<Dyn as Pointee>::Metadata>()); + + unsafe { + // SAFETY: align is power of two because it is the maximum of two alignments. + let alloc: *mut u8 = const_allocate(alloc_size, alloc_align); + + let metadata_offset = + alloc_size.checked_sub(mem::size_of::<<Dyn as Pointee>::Metadata>()).unwrap(); + // SAFETY: adding offset within the allocation. + let metadata_ptr: *mut <Dyn as Pointee>::Metadata = + alloc.add(metadata_offset).cast(); + // SAFETY: `*metadata_ptr` is within the allocation. + metadata_ptr.write(ptr::metadata::<Dyn>(ptr::dangling::<T>() as *const Dyn)); + + // SAFETY: we have just written the metadata. + &*(metadata_ptr) + } + }; + + // SAFETY: `alloc` points to `<Dyn as Pointee>::Metadata`, so addition stays in-bounds. + let value_ptr = + unsafe { (alloc as *const <Dyn as Pointee>::Metadata).add(1) }.cast::<T>().cast_mut(); + debug_assert!(value_ptr.is_aligned()); + mem::forget(value); + WithHeader(NonNull::new(value_ptr.cast()).unwrap(), PhantomData) + } + // Safety: // - Assumes that either `value` can be dereferenced, or is the // `NonNull::dangling()` we use when both `T` and `H` are ZSTs. @@ -300,20 +371,19 @@ impl<H> WithHeader<H> { impl<H> Drop for DropGuard<H> { fn drop(&mut self) { + // All ZST are allocated statically. + if self.value_layout.size() == 0 { + return; + } + unsafe { // SAFETY: Layout must have been computable if we're in drop let (layout, value_offset) = WithHeader::<H>::alloc_layout(self.value_layout).unwrap_unchecked(); - // Note: Don't deallocate if the layout size is zero, because the pointer - // didn't come from the allocator. - if layout.size() != 0 { - alloc::dealloc(self.ptr.as_ptr().sub(value_offset), layout); - } else { - debug_assert!( - value_offset == 0 && H::IS_ZST && self.value_layout.size() == 0 - ); - } + // Since we only allocate for non-ZSTs, the layout size cannot be zero. + debug_assert!(layout.size() != 0); + alloc::dealloc(self.ptr.as_ptr().sub(value_offset), layout); } } } diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index e2fc320f280..693ecb0b6b4 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1614,7 +1614,10 @@ impl<T, A: Allocator> VecDeque<T, A> { let old_head = self.head; self.head = self.to_physical_idx(1); self.len -= 1; - Some(unsafe { self.buffer_read(old_head) }) + unsafe { + core::hint::assert_unchecked(self.len < self.capacity()); + Some(self.buffer_read(old_head)) + } } } @@ -1638,7 +1641,10 @@ impl<T, A: Allocator> VecDeque<T, A> { None } else { self.len -= 1; - Some(unsafe { self.buffer_read(self.to_physical_idx(self.len)) }) + unsafe { + core::hint::assert_unchecked(self.len < self.capacity()); + Some(self.buffer_read(self.to_physical_idx(self.len))) + } } } diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index e21479450c2..f638c5cf8c7 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -108,8 +108,10 @@ #![feature(const_box)] #![feature(const_cow_is_borrowed)] #![feature(const_eval_select)] +#![feature(const_heap)] #![feature(const_maybe_uninit_as_mut_ptr)] #![feature(const_maybe_uninit_write)] +#![feature(const_option)] #![feature(const_pin)] #![feature(const_refs_to_cell)] #![feature(const_size_of_val)] diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 4dea27221b7..6ae52cc7827 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -1062,7 +1062,9 @@ impl<T, A: Allocator> Arc<T, A> { /// /// // Create a long list and clone it /// let mut x = LinkedList::new(); - /// for i in 0..100000 { + /// let size = 100000; + /// # let size = if cfg!(miri) { 100 } else { size }; + /// for i in 0..size { /// x.push(i); // Adds i to the front of x /// } /// let y = x.clone(); diff --git a/library/core/benches/lib.rs b/library/core/benches/lib.rs index 4d14b930e41..32d15c386cb 100644 --- a/library/core/benches/lib.rs +++ b/library/core/benches/lib.rs @@ -1,5 +1,7 @@ // wasm32 does not support benches (no time). #![cfg(not(target_arch = "wasm32"))] +// Disabling in Miri as these would take too long. +#![cfg(not(miri))] #![feature(flt2dec)] #![feature(test)] #![feature(trusted_random_access)] diff --git a/library/core/src/iter/adapters/step_by.rs b/library/core/src/iter/adapters/step_by.rs index b8b96417d13..616dd0afc51 100644 --- a/library/core/src/iter/adapters/step_by.rs +++ b/library/core/src/iter/adapters/step_by.rs @@ -1,6 +1,7 @@ use crate::{ intrinsics, iter::{from_fn, TrustedLen, TrustedRandomAccess}, + num::NonZeroUsize, ops::{Range, Try}, }; @@ -22,7 +23,11 @@ pub struct StepBy<I> { /// Additionally this type-dependent preprocessing means specialized implementations /// cannot be used interchangeably. iter: I, - step: usize, + /// This field is `step - 1`, aka the correct amount to pass to `nth` when iterating. + /// It MUST NOT be `usize::MAX`, as `unsafe` code depends on being able to add one + /// without the risk of overflow. (This is important so that length calculations + /// don't need to check for division-by-zero, for example.) + step_minus_one: usize, first_take: bool, } @@ -31,7 +36,16 @@ impl<I> StepBy<I> { pub(in crate::iter) fn new(iter: I, step: usize) -> StepBy<I> { assert!(step != 0); let iter = <I as SpecRangeSetup<I>>::setup(iter, step); - StepBy { iter, step: step - 1, first_take: true } + StepBy { iter, step_minus_one: step - 1, first_take: true } + } + + /// The `step` that was originally passed to `Iterator::step_by(step)`, + /// aka `self.step_minus_one + 1`. + #[inline] + fn original_step(&self) -> NonZeroUsize { + // SAFETY: By type invariant, `step_minus_one` cannot be `MAX`, which + // means the addition cannot overflow and the result cannot be zero. + unsafe { NonZeroUsize::new_unchecked(intrinsics::unchecked_add(self.step_minus_one, 1)) } } } @@ -81,8 +95,8 @@ where // The zero-based index starting from the end of the iterator of the // last element. Used in the `DoubleEndedIterator` implementation. fn next_back_index(&self) -> usize { - let rem = self.iter.len() % (self.step + 1); - if self.first_take { if rem == 0 { self.step } else { rem - 1 } } else { rem } + let rem = self.iter.len() % self.original_step(); + if self.first_take { if rem == 0 { self.step_minus_one } else { rem - 1 } } else { rem } } } @@ -209,7 +223,7 @@ unsafe impl<I: Iterator> StepByImpl<I> for StepBy<I> { #[inline] default fn spec_next(&mut self) -> Option<I::Item> { - let step_size = if self.first_take { 0 } else { self.step }; + let step_size = if self.first_take { 0 } else { self.step_minus_one }; self.first_take = false; self.iter.nth(step_size) } @@ -217,22 +231,22 @@ unsafe impl<I: Iterator> StepByImpl<I> for StepBy<I> { #[inline] default fn spec_size_hint(&self) -> (usize, Option<usize>) { #[inline] - fn first_size(step: usize) -> impl Fn(usize) -> usize { - move |n| if n == 0 { 0 } else { 1 + (n - 1) / (step + 1) } + fn first_size(step: NonZeroUsize) -> impl Fn(usize) -> usize { + move |n| if n == 0 { 0 } else { 1 + (n - 1) / step } } #[inline] - fn other_size(step: usize) -> impl Fn(usize) -> usize { - move |n| n / (step + 1) + fn other_size(step: NonZeroUsize) -> impl Fn(usize) -> usize { + move |n| n / step } let (low, high) = self.iter.size_hint(); if self.first_take { - let f = first_size(self.step); + let f = first_size(self.original_step()); (f(low), high.map(f)) } else { - let f = other_size(self.step); + let f = other_size(self.original_step()); (f(low), high.map(f)) } } @@ -247,10 +261,9 @@ unsafe impl<I: Iterator> StepByImpl<I> for StepBy<I> { } n -= 1; } - // n and self.step are indices, we need to add 1 to get the amount of elements + // n and self.step_minus_one are indices, we need to add 1 to get the amount of elements // When calling `.nth`, we need to subtract 1 again to convert back to an index - // step + 1 can't overflow because `.step_by` sets `self.step` to `step - 1` - let mut step = self.step + 1; + let mut step = self.original_step().get(); // n + 1 could overflow // thus, if n is usize::MAX, instead of adding one, we call .nth(step) if n == usize::MAX { @@ -288,8 +301,11 @@ unsafe impl<I: Iterator> StepByImpl<I> for StepBy<I> { R: Try<Output = Acc>, { #[inline] - fn nth<I: Iterator>(iter: &mut I, step: usize) -> impl FnMut() -> Option<I::Item> + '_ { - move || iter.nth(step) + fn nth<I: Iterator>( + iter: &mut I, + step_minus_one: usize, + ) -> impl FnMut() -> Option<I::Item> + '_ { + move || iter.nth(step_minus_one) } if self.first_take { @@ -299,7 +315,7 @@ unsafe impl<I: Iterator> StepByImpl<I> for StepBy<I> { Some(x) => acc = f(acc, x)?, } } - from_fn(nth(&mut self.iter, self.step)).try_fold(acc, f) + from_fn(nth(&mut self.iter, self.step_minus_one)).try_fold(acc, f) } default fn spec_fold<Acc, F>(mut self, mut acc: Acc, mut f: F) -> Acc @@ -307,8 +323,11 @@ unsafe impl<I: Iterator> StepByImpl<I> for StepBy<I> { F: FnMut(Acc, Self::Item) -> Acc, { #[inline] - fn nth<I: Iterator>(iter: &mut I, step: usize) -> impl FnMut() -> Option<I::Item> + '_ { - move || iter.nth(step) + fn nth<I: Iterator>( + iter: &mut I, + step_minus_one: usize, + ) -> impl FnMut() -> Option<I::Item> + '_ { + move || iter.nth(step_minus_one) } if self.first_take { @@ -318,7 +337,7 @@ unsafe impl<I: Iterator> StepByImpl<I> for StepBy<I> { Some(x) => acc = f(acc, x), } } - from_fn(nth(&mut self.iter, self.step)).fold(acc, f) + from_fn(nth(&mut self.iter, self.step_minus_one)).fold(acc, f) } } @@ -336,7 +355,7 @@ unsafe impl<I: DoubleEndedIterator + ExactSizeIterator> StepByBackImpl<I> for St // is out of bounds because the length of `self.iter` does not exceed // `usize::MAX` (because `I: ExactSizeIterator`) and `nth_back` is // zero-indexed - let n = n.saturating_mul(self.step + 1).saturating_add(self.next_back_index()); + let n = n.saturating_mul(self.original_step().get()).saturating_add(self.next_back_index()); self.iter.nth_back(n) } @@ -348,16 +367,16 @@ unsafe impl<I: DoubleEndedIterator + ExactSizeIterator> StepByBackImpl<I> for St #[inline] fn nth_back<I: DoubleEndedIterator>( iter: &mut I, - step: usize, + step_minus_one: usize, ) -> impl FnMut() -> Option<I::Item> + '_ { - move || iter.nth_back(step) + move || iter.nth_back(step_minus_one) } match self.next_back() { None => try { init }, Some(x) => { let acc = f(init, x)?; - from_fn(nth_back(&mut self.iter, self.step)).try_fold(acc, f) + from_fn(nth_back(&mut self.iter, self.step_minus_one)).try_fold(acc, f) } } } @@ -371,16 +390,16 @@ unsafe impl<I: DoubleEndedIterator + ExactSizeIterator> StepByBackImpl<I> for St #[inline] fn nth_back<I: DoubleEndedIterator>( iter: &mut I, - step: usize, + step_minus_one: usize, ) -> impl FnMut() -> Option<I::Item> + '_ { - move || iter.nth_back(step) + move || iter.nth_back(step_minus_one) } match self.next_back() { None => init, Some(x) => { let acc = f(init, x); - from_fn(nth_back(&mut self.iter, self.step)).fold(acc, f) + from_fn(nth_back(&mut self.iter, self.step_minus_one)).fold(acc, f) } } } @@ -424,8 +443,7 @@ macro_rules! spec_int_ranges { fn spec_next(&mut self) -> Option<$t> { // if a step size larger than the type has been specified fall back to // t::MAX, in which case remaining will be at most 1. - // The `+ 1` can't overflow since the constructor substracted 1 from the original value. - let step = <$t>::try_from(self.step + 1).unwrap_or(<$t>::MAX); + let step = <$t>::try_from(self.original_step().get()).unwrap_or(<$t>::MAX); let remaining = self.iter.end; if remaining > 0 { let val = self.iter.start; @@ -474,7 +492,7 @@ macro_rules! spec_int_ranges { { // if a step size larger than the type has been specified fall back to // t::MAX, in which case remaining will be at most 1. - let step = <$t>::try_from(self.step + 1).unwrap_or(<$t>::MAX); + let step = <$t>::try_from(self.original_step().get()).unwrap_or(<$t>::MAX); let remaining = self.iter.end; let mut acc = init; let mut val = self.iter.start; @@ -500,7 +518,7 @@ macro_rules! spec_int_ranges_r { fn spec_next_back(&mut self) -> Option<Self::Item> where Range<$t>: DoubleEndedIterator + ExactSizeIterator, { - let step = (self.step + 1) as $t; + let step = self.original_step().get() as $t; let remaining = self.iter.end; if remaining > 0 { let start = self.iter.start; diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index d2c9e1554b4..95c03043e3e 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -1572,7 +1572,7 @@ pub trait Iterator { /// /// The returned iterator implements [`FusedIterator`], because once `self` /// returns `None`, even if it returns a `Some(T)` again in the next iterations, - /// we cannot put it into a contigious array buffer, and thus the returned iterator + /// we cannot put it into a contiguous array buffer, and thus the returned iterator /// should be fused. /// /// [`slice::windows()`]: slice::windows diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 1e52d7ac412..0ba3a557a03 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -396,6 +396,9 @@ pub mod net; pub mod option; pub mod panic; pub mod panicking; +#[cfg(not(bootstrap))] +#[unstable(feature = "core_pattern_types", issue = "none")] +pub mod pat; pub mod pin; pub mod result; pub mod sync; diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index 3f8c8efd416..483f55b2070 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -310,7 +310,7 @@ pub trait FromResidual<R = <Self as Try>::Residual> { /// This should be implemented consistently with the `branch` method such /// that applying the `?` operator will get back an equivalent residual: /// `FromResidual::from_residual(r).branch() --> ControlFlow::Break(r)`. - /// (It must not be an *identical* residual when interconversion is involved.) + /// (The residual is not mandated to be *identical* when interconversion is involved.) /// /// # Examples /// diff --git a/library/core/src/pat.rs b/library/core/src/pat.rs new file mode 100644 index 00000000000..a10c4593342 --- /dev/null +++ b/library/core/src/pat.rs @@ -0,0 +1,14 @@ +//! Helper module for exporting the `pattern_type` macro + +/// Creates a pattern type. +/// ```ignore (cannot test this from within core yet) +/// type Positive = std::pat::pattern_type!(i32 is 1..); +/// ``` +#[macro_export] +#[rustc_builtin_macro(pattern_type)] +#[unstable(feature = "core_pattern_type", issue = "none")] +macro_rules! pattern_type { + ($($arg:tt)*) => { + /* compiler built-in */ + }; +} diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index 672af752149..ba2d6f64496 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -1,6 +1,7 @@ //! Trait implementations for `str`. use crate::cmp::Ordering; +use crate::intrinsics::unchecked_sub; use crate::ops; use crate::ptr; use crate::slice::SliceIndex; @@ -210,9 +211,10 @@ unsafe impl SliceIndex<str> for ops::Range<usize> { // SAFETY: the caller guarantees that `self` is in bounds of `slice` // which satisfies all the conditions for `add`. - let ptr = unsafe { slice.as_ptr().add(self.start) }; - let len = self.end - self.start; - ptr::slice_from_raw_parts(ptr, len) as *const str + unsafe { + let new_len = unchecked_sub(self.end, self.start); + ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), new_len) as *const str + } } #[inline] unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { @@ -229,9 +231,10 @@ unsafe impl SliceIndex<str> for ops::Range<usize> { ); // SAFETY: see comments for `get_unchecked`. - let ptr = unsafe { slice.as_mut_ptr().add(self.start) }; - let len = self.end - self.start; - ptr::slice_from_raw_parts_mut(ptr, len) as *mut str + unsafe { + let new_len = unchecked_sub(self.end, self.start); + ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), new_len) as *mut str + } } #[inline] fn index(self, slice: &str) -> &Self::Output { diff --git a/library/std/benches/lib.rs b/library/std/benches/lib.rs index 4d1cf7fab7b..1b21c230a0b 100644 --- a/library/std/benches/lib.rs +++ b/library/std/benches/lib.rs @@ -1,3 +1,5 @@ +// Disabling in Miri as these would take too long. +#![cfg(not(miri))] #![feature(test)] extern crate test; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index b509503ce4d..c713eefc72c 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -576,6 +576,9 @@ pub mod net; pub mod num; pub mod os; pub mod panic; +#[cfg(not(bootstrap))] +#[unstable(feature = "core_pattern_types", issue = "none")] +pub mod pat; pub mod path; pub mod process; pub mod sync; diff --git a/library/std/src/pat.rs b/library/std/src/pat.rs new file mode 100644 index 00000000000..aeddd84c2cb --- /dev/null +++ b/library/std/src/pat.rs @@ -0,0 +1,3 @@ +//! Helper module for exporting the `pattern_type` macro + +pub use core::pattern_type; diff --git a/library/std/src/sys/pal/hermit/fd.rs b/library/std/src/sys/pal/hermit/fd.rs index 5eb828fea1f..962577bb1ed 100644 --- a/library/std/src/sys/pal/hermit/fd.rs +++ b/library/std/src/sys/pal/hermit/fd.rs @@ -48,6 +48,11 @@ impl FileDesc { pub fn set_nonblocking(&self, _nonblocking: bool) -> io::Result<()> { unsupported() } + + pub fn fstat(&self, stat: *mut abi::stat) -> io::Result<()> { + cvt(unsafe { abi::fstat(self.fd.as_raw_fd(), stat) })?; + Ok(()) + } } impl<'a> Read for &'a FileDesc { diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs index d4da53fd3df..6519cc22f1f 100644 --- a/library/std/src/sys/pal/hermit/fs.rs +++ b/library/std/src/sys/pal/hermit/fs.rs @@ -1,12 +1,17 @@ -use super::abi::{self, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY}; +use super::abi::{ + self, dirent64, stat as stat_struct, DT_DIR, DT_LNK, DT_REG, DT_UNKNOWN, O_APPEND, O_CREAT, + O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, +}; use super::fd::FileDesc; -use crate::ffi::{CStr, OsString}; +use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt; -use crate::hash::{Hash, Hasher}; use crate::io::{self, Error, ErrorKind}; use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::mem; +use crate::os::hermit::ffi::OsStringExt; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::{Path, PathBuf}; +use crate::sync::Arc; use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::cvt; use crate::sys::time::SystemTime; @@ -14,16 +19,53 @@ use crate::sys::unsupported; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; pub use crate::sys_common::fs::{copy, try_exists}; -//pub use crate::sys_common::fs::remove_dir_all; #[derive(Debug)] pub struct File(FileDesc); +#[derive(Clone)] +pub struct FileAttr { + stat_val: stat_struct, +} -pub struct FileAttr(!); +impl FileAttr { + fn from_stat(stat_val: stat_struct) -> Self { + Self { stat_val } + } +} -pub struct ReadDir(!); +// all DirEntry's will have a reference to this struct +struct InnerReadDir { + root: PathBuf, + dir: Vec<u8>, +} -pub struct DirEntry(!); +impl InnerReadDir { + pub fn new(root: PathBuf, dir: Vec<u8>) -> Self { + Self { root, dir } + } +} + +pub struct ReadDir { + inner: Arc<InnerReadDir>, + pos: i64, +} + +impl ReadDir { + fn new(inner: InnerReadDir) -> Self { + Self { inner: Arc::new(inner), pos: 0 } + } +} + +pub struct DirEntry { + /// path to the entry + root: PathBuf, + /// 64-bit inode number + ino: u64, + /// File type + type_: u32, + /// name of the entry + name: OsString, +} #[derive(Clone, Debug)] pub struct OpenOptions { @@ -41,72 +83,87 @@ pub struct OpenOptions { #[derive(Copy, Clone, Debug, Default)] pub struct FileTimes {} -pub struct FilePermissions(!); - -pub struct FileType(!); +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { + mode: u32, +} -#[derive(Debug)] -pub struct DirBuilder {} +#[derive(Copy, Clone, Eq, Debug)] +pub struct FileType { + mode: u32, +} -impl FileAttr { - pub fn size(&self) -> u64 { - self.0 +impl PartialEq for FileType { + fn eq(&self, other: &Self) -> bool { + self.mode == other.mode } +} - pub fn perm(&self) -> FilePermissions { - self.0 +impl core::hash::Hash for FileType { + fn hash<H: core::hash::Hasher>(&self, state: &mut H) { + self.mode.hash(state); } +} - pub fn file_type(&self) -> FileType { - self.0 - } +#[derive(Debug)] +pub struct DirBuilder { + mode: u32, +} +impl FileAttr { pub fn modified(&self) -> io::Result<SystemTime> { - self.0 + Ok(SystemTime::new( + self.stat_val.st_mtime.try_into().unwrap(), + self.stat_val.st_mtime_nsec.try_into().unwrap(), + )) } pub fn accessed(&self) -> io::Result<SystemTime> { - self.0 + Ok(SystemTime::new( + self.stat_val.st_atime.try_into().unwrap(), + self.stat_val.st_atime_nsec.try_into().unwrap(), + )) } pub fn created(&self) -> io::Result<SystemTime> { - self.0 + Ok(SystemTime::new( + self.stat_val.st_ctime.try_into().unwrap(), + self.stat_val.st_ctime_nsec.try_into().unwrap(), + )) } -} -impl Clone for FileAttr { - fn clone(&self) -> FileAttr { - self.0 + pub fn size(&self) -> u64 { + self.stat_val.st_size as u64 } -} - -impl FilePermissions { - pub fn readonly(&self) -> bool { - self.0 + pub fn perm(&self) -> FilePermissions { + FilePermissions { mode: (self.stat_val.st_mode) } } - pub fn set_readonly(&mut self, _readonly: bool) { - self.0 + pub fn file_type(&self) -> FileType { + let masked_mode = self.stat_val.st_mode & S_IFMT; + let mode = match masked_mode { + S_IFDIR => DT_DIR, + S_IFLNK => DT_LNK, + S_IFREG => DT_REG, + _ => DT_UNKNOWN, + }; + FileType { mode: mode } } } -impl Clone for FilePermissions { - fn clone(&self) -> FilePermissions { - self.0 +impl FilePermissions { + pub fn readonly(&self) -> bool { + // check if any class (owner, group, others) has write permission + self.mode & 0o222 == 0 } -} -impl PartialEq for FilePermissions { - fn eq(&self, _other: &FilePermissions) -> bool { - self.0 + pub fn set_readonly(&mut self, _readonly: bool) { + unimplemented!() } -} - -impl Eq for FilePermissions {} -impl fmt::Debug for FilePermissions { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 + #[allow(dead_code)] + pub fn mode(&self) -> u32 { + self.mode as u32 } } @@ -117,49 +174,21 @@ impl FileTimes { impl FileType { pub fn is_dir(&self) -> bool { - self.0 + self.mode == DT_DIR } - pub fn is_file(&self) -> bool { - self.0 + self.mode == DT_REG } - pub fn is_symlink(&self) -> bool { - self.0 - } -} - -impl Clone for FileType { - fn clone(&self) -> FileType { - self.0 - } -} - -impl Copy for FileType {} - -impl PartialEq for FileType { - fn eq(&self, _other: &FileType) -> bool { - self.0 - } -} - -impl Eq for FileType {} - -impl Hash for FileType { - fn hash<H: Hasher>(&self, _h: &mut H) { - self.0 - } -} - -impl fmt::Debug for FileType { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 + self.mode == DT_LNK } } impl fmt::Debug for ReadDir { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. + // Thus the result will be e.g. 'ReadDir("/home")' + fmt::Debug::fmt(&*self.inner.root, f) } } @@ -167,25 +196,78 @@ impl Iterator for ReadDir { type Item = io::Result<DirEntry>; fn next(&mut self) -> Option<io::Result<DirEntry>> { - self.0 + let mut counter: usize = 0; + let mut offset: i64 = 0; + + // loop over all directory entries and search the entry for the current position + loop { + // leave function, if the loop reaches the of the buffer (with all entries) + if offset >= self.inner.dir.len().try_into().unwrap() { + return None; + } + + let dir = unsafe { + &*(self.inner.dir.as_ptr().offset(offset.try_into().unwrap()) as *const dirent64) + }; + + if counter == self.pos.try_into().unwrap() { + self.pos += 1; + + // After dirent64, the file name is stored. d_reclen represents the length of the dirent64 + // plus the length of the file name. Consequently, file name has a size of d_reclen minus + // the size of dirent64. The file name is always a C string and terminated by `\0`. + // Consequently, we are able to ignore the last byte. + let name_bytes = unsafe { + core::slice::from_raw_parts( + &dir.d_name as *const _ as *const u8, + dir.d_reclen as usize - core::mem::size_of::<dirent64>() - 1, + ) + .to_vec() + }; + let entry = DirEntry { + root: self.inner.root.clone(), + ino: dir.d_ino, + type_: dir.d_type as u32, + name: OsString::from_vec(name_bytes), + }; + + return Some(Ok(entry)); + } + + counter += 1; + + // move to the next dirent64, which is directly stored after the previous one + offset = offset + dir.d_off; + } } } impl DirEntry { pub fn path(&self) -> PathBuf { - self.0 + self.root.join(self.file_name_os_str()) } pub fn file_name(&self) -> OsString { - self.0 + self.file_name_os_str().to_os_string() } pub fn metadata(&self) -> io::Result<FileAttr> { - self.0 + let mut path = self.path(); + path.set_file_name(self.file_name_os_str()); + lstat(&path) } pub fn file_type(&self) -> io::Result<FileType> { - self.0 + Ok(FileType { mode: self.type_ as u32 }) + } + + #[allow(dead_code)] + pub fn ino(&self) -> u64 { + self.ino + } + + pub fn file_name_os_str(&self) -> &OsStr { + self.name.as_os_str() } } @@ -288,7 +370,9 @@ impl File { } pub fn file_attr(&self) -> io::Result<FileAttr> { - Err(Error::from_raw_os_error(22)) + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + self.0.fstat(&mut stat_val)?; + Ok(FileAttr::from_stat(stat_val)) } pub fn fsync(&self) -> io::Result<()> { @@ -357,11 +441,18 @@ impl File { impl DirBuilder { pub fn new() -> DirBuilder { - DirBuilder {} + DirBuilder { mode: 0o777 } } - pub fn mkdir(&self, _p: &Path) -> io::Result<()> { - unsupported() + pub fn mkdir(&self, path: &Path) -> io::Result<()> { + run_path_with_cstr(path, &|path| { + cvt(unsafe { abi::mkdir(path.as_ptr(), self.mode) }).map(|_| ()) + }) + } + + #[allow(dead_code)] + pub fn set_mode(&mut self, mode: u32) { + self.mode = mode as u32; } } @@ -416,8 +507,43 @@ impl FromRawFd for File { } } -pub fn readdir(_p: &Path) -> io::Result<ReadDir> { - unsupported() +pub fn readdir(path: &Path) -> io::Result<ReadDir> { + let fd_raw = run_path_with_cstr(path, &|path| cvt(unsafe { abi::opendir(path.as_ptr()) }))?; + let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) }; + let root = path.to_path_buf(); + + // read all director entries + let mut vec: Vec<u8> = Vec::new(); + let mut sz = 512; + loop { + // reserve memory to receive all directory entries + vec.resize(sz, 0); + + let readlen = + unsafe { abi::getdents64(fd.as_raw_fd(), vec.as_mut_ptr() as *mut dirent64, sz) }; + if readlen > 0 { + // shrink down to the minimal size + vec.resize(readlen.try_into().unwrap(), 0); + break; + } + + // if the buffer is too small, getdents64 returns EINVAL + // otherwise, getdents64 returns an error number + if readlen != (-abi::errno::EINVAL).into() { + return Err(Error::from_raw_os_error(readlen.try_into().unwrap())); + } + + // we don't have enough memory => try to increase the vector size + sz = sz * 2; + + // 1 MB for directory entries should be enough + // stop here to avoid an endless loop + if sz > 0x100000 { + return Err(Error::from(ErrorKind::Uncategorized)); + } + } + + Ok(ReadDir::new(InnerReadDir::new(root, vec))) } pub fn unlink(path: &Path) -> io::Result<()> { @@ -428,17 +554,16 @@ pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { unsupported() } -pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { - match perm.0 {} +pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { + Err(Error::from_raw_os_error(22)) } -pub fn rmdir(_p: &Path) -> io::Result<()> { - unsupported() +pub fn rmdir(path: &Path) -> io::Result<()> { + run_path_with_cstr(path, &|path| cvt(unsafe { abi::rmdir(path.as_ptr()) }).map(|_| ())) } pub fn remove_dir_all(_path: &Path) -> io::Result<()> { - //unsupported() - Ok(()) + unsupported() } pub fn readlink(_p: &Path) -> io::Result<PathBuf> { @@ -453,12 +578,20 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { unsupported() } -pub fn stat(_p: &Path) -> io::Result<FileAttr> { - unsupported() +pub fn stat(path: &Path) -> io::Result<FileAttr> { + run_path_with_cstr(path, &|path| { + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + cvt(unsafe { abi::stat(path.as_ptr(), &mut stat_val) })?; + Ok(FileAttr::from_stat(stat_val)) + }) } -pub fn lstat(_p: &Path) -> io::Result<FileAttr> { - unsupported() +pub fn lstat(path: &Path) -> io::Result<FileAttr> { + run_path_with_cstr(path, &|path| { + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + cvt(unsafe { abi::lstat(path.as_ptr(), &mut stat_val) })?; + Ok(FileAttr::from_stat(stat_val)) + }) } pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> { diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs index 319b835a768..d6f9e4c1476 100644 --- a/library/std/src/sys/pal/hermit/time.rs +++ b/library/std/src/sys/pal/hermit/time.rs @@ -18,6 +18,12 @@ impl Timespec { Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } } } + const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { + assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64); + // SAFETY: The assert above checks tv_nsec is within the valid range + Timespec { t: timespec { tv_sec: tv_sec, tv_nsec: tv_nsec } } + } + fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> { if self >= other { Ok(if self.t.tv_nsec >= other.t.tv_nsec { @@ -195,6 +201,10 @@ pub struct SystemTime(Timespec); pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero()); impl SystemTime { + pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { + SystemTime(Timespec::new(tv_sec, tv_nsec)) + } + pub fn now() -> SystemTime { let mut time: Timespec = Timespec::zero(); let _ = unsafe { abi::clock_gettime(CLOCK_REALTIME, core::ptr::addr_of_mut!(time.t)) }; diff --git a/library/std/src/sys_common/thread_local_key.rs b/library/std/src/sys_common/thread_local_key.rs index 7dcc1141099..a9cd26389cd 100644 --- a/library/std/src/sys_common/thread_local_key.rs +++ b/library/std/src/sys_common/thread_local_key.rs @@ -1,4 +1,4 @@ -//! OS-based thread local storage +//! OS-based thread local storage for non-Windows systems //! //! This module provides an implementation of OS-based thread local storage, //! using the native OS-provided facilities (think `TlsAlloc` or @@ -11,6 +11,9 @@ //! the OS-TLS key. The other is a type which does implement `Drop` and hence //! has a safe interface. //! +//! Windows doesn't use this module at all; `sys::pal::windows::thread_local_key` +//! gets imported in its stead. +//! //! # Usage //! //! This module should likely not be used directly unless other primitives are diff --git a/library/std/tests/process_spawning.rs b/library/std/tests/process_spawning.rs index 59f67f9901f..c56c111c37d 100644 --- a/library/std/tests/process_spawning.rs +++ b/library/std/tests/process_spawning.rs @@ -8,6 +8,7 @@ use std::str; mod common; #[test] +#[cfg_attr(miri, ignore)] // Process spawning not supported by Miri fn issue_15149() { // If we're the parent, copy our own binary to a new directory. let my_path = env::current_exe().unwrap(); diff --git a/library/std/tests/switch-stdout.rs b/library/std/tests/switch-stdout.rs index 27f3e8a9b96..0afe18088fa 100644 --- a/library/std/tests/switch-stdout.rs +++ b/library/std/tests/switch-stdout.rs @@ -51,6 +51,7 @@ fn switch_stdout_to(file: OwnedHandle) -> OwnedHandle { } #[test] +#[cfg_attr(miri, ignore)] // dup/SetStdHandle not supported by Miri fn switch_stdout() { let temp = common::tmpdir(); let path = temp.join("switch-stdout-output"); diff --git a/library/std/tests/thread.rs b/library/std/tests/thread.rs index 4ce81f2846e..79a981d0b0d 100644 --- a/library/std/tests/thread.rs +++ b/library/std/tests/thread.rs @@ -5,7 +5,8 @@ use std::time::Duration; #[test] #[cfg_attr(target_os = "emscripten", ignore)] -fn sleep() { +#[cfg_attr(miri, ignore)] // Miri does not like the thread leak +fn sleep_very_long() { let finished = Arc::new(Mutex::new(false)); let t_finished = finished.clone(); thread::spawn(move || { diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index 0b67079917c..d6e60d52d63 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -52,6 +52,36 @@ check-aux: src/tools/cargo \ src/tools/cargotest \ $(BOOTSTRAP_ARGS) + # Run standard library tests in Miri. + # We use a 64bit little-endian and a 32bit big-endian target for max coverage. + $(Q)BOOTSTRAP_SKIP_TARGET_SANITY=1 \ + $(BOOTSTRAP) miri --stage 2 \ + --target x86_64-unknown-linux-gnu,mips-unknown-linux-gnu \ + library/core \ + library/alloc \ + --no-doc + # Some doctests have intentional memory leaks. + # Also, they work only on the host. + $(Q)MIRIFLAGS="-Zmiri-ignore-leaks -Zmiri-disable-isolation" \ + $(BOOTSTRAP) miri --stage 2 \ + library/core \ + library/alloc \ + --doc + # In `std` we cannot test everything. + $(Q)MIRIFLAGS="-Zmiri-disable-isolation" BOOTSTRAP_SKIP_TARGET_SANITY=1 \ + $(BOOTSTRAP) miri --stage 2 library/std \ + --no-doc -- \ + --skip fs:: --skip net:: --skip process:: --skip sys::pal:: + $(Q)MIRIFLAGS="-Zmiri-ignore-leaks -Zmiri-disable-isolation" BOOTSTRAP_SKIP_TARGET_SANITY=1 \ + $(BOOTSTRAP) miri --stage 2 library/std \ + --doc -- \ + --skip fs:: --skip net:: --skip process:: --skip sys::pal:: + # Also test some very target-specific modules on other targets. + $(Q)MIRIFLAGS="-Zmiri-disable-isolation" BOOTSTRAP_SKIP_TARGET_SANITY=1 \ + $(BOOTSTRAP) miri --stage 2 library/std \ + --target aarch64-apple-darwin,i686-pc-windows-gnu \ + --no-doc -- \ + time:: sync:: thread:: env:: dist: $(Q)$(BOOTSTRAP) dist $(BOOTSTRAP_ARGS) distcheck: diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 028e0f6d05f..1e68f8d276a 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2689,16 +2689,27 @@ impl Step for Crate { match mode { Mode::Std => { - compile::std_cargo(builder, target, compiler.stage, &mut cargo); - // `std_cargo` actually does the wrong thing: it passes `--sysroot build/host/stage2`, - // but we want to use the force-recompile std we just built in `build/host/stage2-test-sysroot`. - // Override it. - if builder.download_rustc() && compiler.stage > 0 { - let sysroot = builder - .out - .join(compiler.host.triple) - .join(format!("stage{}-test-sysroot", compiler.stage)); - cargo.env("RUSTC_SYSROOT", sysroot); + if builder.kind == Kind::Miri { + // We can't use `std_cargo` as that uses `optimized-compiler-builtins` which + // needs host tools for the given target. This is similar to what `compile::Std` + // does when `is_for_mir_opt_tests` is true. There's probably a chance for + // de-duplication here... `std_cargo` should support a mode that avoids needing + // host tools. + cargo + .arg("--manifest-path") + .arg(builder.src.join("library/sysroot/Cargo.toml")); + } else { + compile::std_cargo(builder, target, compiler.stage, &mut cargo); + // `std_cargo` actually does the wrong thing: it passes `--sysroot build/host/stage2`, + // but we want to use the force-recompile std we just built in `build/host/stage2-test-sysroot`. + // Override it. + if builder.download_rustc() && compiler.stage > 0 { + let sysroot = builder + .out + .join(compiler.host.triple) + .join(format!("stage{}-test-sysroot", compiler.stage)); + cargo.env("RUSTC_SYSROOT", sysroot); + } } } Mode::Rustc => { diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index e8977a4b173..e3b27600c5e 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -645,27 +645,6 @@ pub enum Kind { } impl Kind { - pub fn parse(string: &str) -> Option<Kind> { - // these strings, including the one-letter aliases, must match the x.py help text - Some(match string { - "build" | "b" => Kind::Build, - "check" | "c" => Kind::Check, - "clippy" => Kind::Clippy, - "fix" => Kind::Fix, - "fmt" => Kind::Format, - "test" | "t" => Kind::Test, - "bench" => Kind::Bench, - "doc" | "d" => Kind::Doc, - "clean" => Kind::Clean, - "dist" => Kind::Dist, - "install" => Kind::Install, - "run" | "r" => Kind::Run, - "setup" => Kind::Setup, - "suggest" => Kind::Suggest, - _ => return None, - }) - } - pub fn as_str(&self) -> &'static str { match self { Kind::Build => "build", diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index bcb8260b15a..1a8322c0dfd 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1911,15 +1911,6 @@ impl Compiler { pub fn is_snapshot(&self, build: &Build) -> bool { self.stage == 0 && self.host == build.build } - - /// Returns if this compiler should be treated as a final stage one in the - /// current build session. - /// This takes into account whether we're performing a full bootstrap or - /// not; don't directly compare the stage with `2`! - pub fn is_final_stage(&self, build: &Build) -> bool { - let final_stage = if build.config.full_bootstrap { 2 } else { 1 }; - self.stage >= final_stage - } } fn envify(s: &str) -> String { diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile index e08c4e1e8b7..a74db2250fc 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile @@ -25,5 +25,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh +# Miri is just too slow with full assertions +ENV NO_DEBUG_ASSERTIONS=1 +ENV NO_OVERFLOW_CHECKS=1 + ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu ENV RUST_CHECK_TARGET check-aux diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile index 6f720569898..a3e8f6176a3 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile @@ -78,16 +78,6 @@ ENV PATH="$NODE_FOLDER:${PATH}" COPY host-x86_64/x86_64-gnu-tools/browser-ui-test.version /tmp/ -# For now, we need to use `--unsafe-perm=true` to go around an issue when npm tries -# to create a new folder. For reference: -# https://github.com/puppeteer/puppeteer/issues/375 -# -# We also specify the version in case we need to update it to go around cache limitations. -# -# The `browser-ui-test.version` file is also used by bootstrap to emit warnings in case -# the local version of the package is different than the one used by the CI. -RUN npm install -g browser-ui-test@$(head -n 1 /tmp/browser-ui-test.version) --unsafe-perm=true - ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ --save-toolstates=/tmp/toolstate/toolstates.json \ @@ -100,6 +90,14 @@ COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ RUN /scripts/build-gccjit.sh /scripts +# For now, we need to use `--unsafe-perm=true` to go around an issue when npm tries +# to create a new folder. For reference: +# https://github.com/puppeteer/puppeteer/issues/375 +# +# We also specify the version in case we need to update it to go around cache limitations. +# +# The `browser-ui-test.version` file is also used by bootstrap to emit warnings in case +# the local version of the package is different than the one used by the CI. ENV SCRIPT /tmp/checktools.sh ../x.py && \ - NODE_PATH=`npm root -g` python3 ../x.py test tests/rustdoc-gui --stage 2 \ - --test-args "'--no-sandbox --jobs 1'" + npm install browser-ui-test@$(head -n 1 /tmp/browser-ui-test.version) --unsafe-perm=true && \ + python3 ../x.py test tests/rustdoc-gui --stage 2 --test-args "'--no-sandbox --jobs 1'" diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version index 14a8c245756..50c2e5e29f0 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version @@ -1 +1 @@ -0.17.1 \ No newline at end of file +0.17.2 \ No newline at end of file diff --git a/src/doc/book b/src/doc/book -Subproject 19c40bfd2d57641d962f3119a1c343355f1b3c5 +Subproject 3131aa4642c627a24f523c82566b94a7d920f68 diff --git a/src/doc/edition-guide b/src/doc/edition-guide -Subproject 98b33e9a441457b0a491fe1be90e7de64eafc3e +Subproject eb3eb80e106d03250c1fb7c5666b1c8c5967286 diff --git a/src/doc/embedded-book b/src/doc/embedded-book -Subproject 2e95fc2fd31d669947e993aa07ef10dc9828bee +Subproject aa7d4b0b4653ddb47cb1de2036d090ec2ba9dab diff --git a/src/doc/nomicon b/src/doc/nomicon -Subproject 6bc2415218d4dd0cb01433d8320f5ccf79c343a +Subproject 0d5f88475fe285affa6dbbc806e9e44d730797c diff --git a/src/doc/reference b/src/doc/reference -Subproject 984b36eca4b9293df04d5ba4eb5c4f77db0f51d +Subproject 55694913b1301cc809f9bf4a1ad1b3d6920efbd diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example -Subproject 7601e0c5ad29d5bd3b518700ea63fddfff5915a +Subproject 60d34b5fd33db1346f9aabfc0c9d0bda6c8e42b diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide -Subproject ffa246b7fd95a96e1cd54883e613aed42c32547 +Subproject b77a34bd46399687b4ce6a17198e9f316c98879 diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 217f6bb550b..daf63998461 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -168,7 +168,7 @@ fn clean_param_env<'tcx>( // FIXME(#111101): Incorporate the explicit predicates of the item here... let item_predicates: FxIndexSet<_> = - tcx.predicates_of(item_def_id).predicates.iter().map(|(pred, _)| pred).collect(); + tcx.param_env(item_def_id).caller_bounds().iter().collect(); let where_predicates = param_env .caller_bounds() .iter() diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 72d4cc7c465..8ed6ee014f3 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -1,141 +1,130 @@ -use crate::rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_hir as hir; use rustc_infer::infer::{DefineOpaqueTypes, InferOk, TyCtxtInferExt}; use rustc_infer::traits; -use rustc_middle::ty::ToPredicate; +use rustc_middle::ty::{self, ToPredicate}; +use rustc_span::def_id::DefId; use rustc_span::DUMMY_SP; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; -use super::*; +use thin_vec::ThinVec; -pub(crate) struct BlanketImplFinder<'a, 'tcx> { - pub(crate) cx: &'a mut core::DocContext<'tcx>, -} +use crate::clean; +use crate::clean::{ + clean_middle_assoc_item, clean_middle_ty, clean_trait_ref_with_bindings, clean_ty_generics, +}; +use crate::core::DocContext; + +#[instrument(level = "debug", skip(cx))] +pub(crate) fn synthesize_blanket_impls( + cx: &mut DocContext<'_>, + item_def_id: DefId, +) -> Vec<clean::Item> { + let tcx = cx.tcx; + let ty = tcx.type_of(item_def_id); -impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { - pub(crate) fn get_blanket_impls(&mut self, item_def_id: DefId) -> Vec<Item> { - let cx = &mut self.cx; - let ty = cx.tcx.type_of(item_def_id); + let mut blanket_impls = Vec::new(); + for trait_def_id in tcx.all_traits() { + if !cx.cache.effective_visibilities.is_reachable(tcx, trait_def_id) + || cx.generated_synthetics.get(&(ty.skip_binder(), trait_def_id)).is_some() + { + continue; + } + // NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls + let trait_impls = tcx.trait_impls_of(trait_def_id); + 'blanket_impls: for &impl_def_id in trait_impls.blanket_impls() { + trace!("considering impl `{impl_def_id:?}` for trait `{trait_def_id:?}`"); - trace!("get_blanket_impls({ty:?})"); - let mut impls = Vec::new(); - for trait_def_id in cx.tcx.all_traits() { - if !cx.cache.effective_visibilities.is_reachable(cx.tcx, trait_def_id) - || cx.generated_synthetics.get(&(ty.skip_binder(), trait_def_id)).is_some() - { + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); + if !matches!(trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) { continue; } - // NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls - let trait_impls = cx.tcx.trait_impls_of(trait_def_id); - 'blanket_impls: for &impl_def_id in trait_impls.blanket_impls() { - trace!( - "get_blanket_impls: Considering impl for trait '{:?}' {:?}", - trait_def_id, - impl_def_id - ); - let trait_ref = cx.tcx.impl_trait_ref(impl_def_id).unwrap(); - if !matches!(trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) { - continue; - } - let infcx = cx.tcx.infer_ctxt().build(); - let args = infcx.fresh_args_for_item(DUMMY_SP, item_def_id); - let impl_ty = ty.instantiate(infcx.tcx, args); - let param_env = ty::ParamEnv::empty(); + let infcx = tcx.infer_ctxt().build(); + let args = infcx.fresh_args_for_item(DUMMY_SP, item_def_id); + let impl_ty = ty.instantiate(tcx, args); + let param_env = ty::ParamEnv::empty(); - let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id); - let impl_trait_ref = trait_ref.instantiate(infcx.tcx, impl_args); + let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id); + let impl_trait_ref = trait_ref.instantiate(tcx, impl_args); - // Require the type the impl is implemented on to match - // our type, and ignore the impl if there was a mismatch. - let Ok(eq_result) = infcx.at(&traits::ObligationCause::dummy(), param_env).eq( - DefineOpaqueTypes::Yes, - impl_trait_ref.self_ty(), - impl_ty, - ) else { - continue; - }; - let InferOk { value: (), obligations } = eq_result; - // FIXME(eddyb) ignoring `obligations` might cause false positives. - drop(obligations); + // Require the type the impl is implemented on to match + // our type, and ignore the impl if there was a mismatch. + let Ok(eq_result) = infcx.at(&traits::ObligationCause::dummy(), param_env).eq( + DefineOpaqueTypes::Yes, + impl_trait_ref.self_ty(), + impl_ty, + ) else { + continue; + }; + let InferOk { value: (), obligations } = eq_result; + // FIXME(eddyb) ignoring `obligations` might cause false positives. + drop(obligations); - trace!( - "invoking predicate_may_hold: param_env={:?}, impl_trait_ref={:?}, impl_ty={:?}", + let predicates = tcx + .predicates_of(impl_def_id) + .instantiate(tcx, impl_args) + .predicates + .into_iter() + .chain(Some(ty::Binder::dummy(impl_trait_ref).to_predicate(tcx))); + for predicate in predicates { + let obligation = traits::Obligation::new( + tcx, + traits::ObligationCause::dummy(), param_env, - impl_trait_ref, - impl_ty + predicate, ); - let predicates = cx - .tcx - .predicates_of(impl_def_id) - .instantiate(cx.tcx, impl_args) - .predicates - .into_iter() - .chain(Some(ty::Binder::dummy(impl_trait_ref).to_predicate(infcx.tcx))); - for predicate in predicates { - debug!("testing predicate {predicate:?}"); - let obligation = traits::Obligation::new( - infcx.tcx, - traits::ObligationCause::dummy(), - param_env, - predicate, - ); - match infcx.evaluate_obligation(&obligation) { - Ok(eval_result) if eval_result.may_apply() => {} - Err(traits::OverflowError::Canonical) => {} - _ => continue 'blanket_impls, - } + match infcx.evaluate_obligation(&obligation) { + Ok(eval_result) if eval_result.may_apply() => {} + Err(traits::OverflowError::Canonical) => {} + _ => continue 'blanket_impls, } - debug!( - "get_blanket_impls: found applicable impl for trait_ref={:?}, ty={:?}", - trait_ref, ty - ); + } + debug!("found applicable impl for trait ref {trait_ref:?}"); - cx.generated_synthetics.insert((ty.skip_binder(), trait_def_id)); + cx.generated_synthetics.insert((ty.skip_binder(), trait_def_id)); - impls.push(Item { - name: None, - attrs: Default::default(), - item_id: ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, - kind: Box::new(ImplItem(Box::new(Impl { - unsafety: hir::Unsafety::Normal, - generics: clean_ty_generics( - cx, - cx.tcx.generics_of(impl_def_id), - cx.tcx.explicit_predicates_of(impl_def_id), - ), - // FIXME(eddyb) compute both `trait_` and `for_` from - // the post-inference `trait_ref`, as it's more accurate. - trait_: Some(clean_trait_ref_with_bindings( - cx, - ty::Binder::dummy(trait_ref.instantiate_identity()), - ThinVec::new(), - )), - for_: clean_middle_ty( - ty::Binder::dummy(ty.instantiate_identity()), - cx, - None, - None, - ), - items: cx - .tcx - .associated_items(impl_def_id) - .in_definition_order() - .filter(|item| !item.is_impl_trait_in_trait()) - .map(|item| clean_middle_assoc_item(item, cx)) - .collect::<Vec<_>>(), - polarity: ty::ImplPolarity::Positive, - kind: ImplKind::Blanket(Box::new(clean_middle_ty( - ty::Binder::dummy(trait_ref.instantiate_identity().self_ty()), - cx, - None, - None, - ))), - }))), - cfg: None, - inline_stmt_id: None, - }); - } + blanket_impls.push(clean::Item { + name: None, + attrs: Default::default(), + item_id: clean::ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, + kind: Box::new(clean::ImplItem(Box::new(clean::Impl { + unsafety: hir::Unsafety::Normal, + generics: clean_ty_generics( + cx, + tcx.generics_of(impl_def_id), + tcx.explicit_predicates_of(impl_def_id), + ), + // FIXME(eddyb) compute both `trait_` and `for_` from + // the post-inference `trait_ref`, as it's more accurate. + trait_: Some(clean_trait_ref_with_bindings( + cx, + ty::Binder::dummy(trait_ref.instantiate_identity()), + ThinVec::new(), + )), + for_: clean_middle_ty( + ty::Binder::dummy(ty.instantiate_identity()), + cx, + None, + None, + ), + items: tcx + .associated_items(impl_def_id) + .in_definition_order() + .filter(|item| !item.is_impl_trait_in_trait()) + .map(|item| clean_middle_assoc_item(item, cx)) + .collect(), + polarity: ty::ImplPolarity::Positive, + kind: clean::ImplKind::Blanket(Box::new(clean_middle_ty( + ty::Binder::dummy(trait_ref.instantiate_identity().self_ty()), + cx, + None, + None, + ))), + }))), + cfg: None, + inline_stmt_id: None, + }); } - - impls } + + blanket_impls } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index a25a506d9c5..12f45fe4979 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -29,7 +29,7 @@ use rustc_middle::ty::{self, AdtKind, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::hygiene::{AstPass, MacroKind}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{self, ExpnKind}; +use rustc_span::ExpnKind; use rustc_trait_selection::traits::wf::object_region_bounds; use std::borrow::Cow; @@ -37,14 +37,14 @@ use std::collections::BTreeMap; use std::mem; use thin_vec::ThinVec; -use crate::core::{self, DocContext}; +use crate::core::DocContext; use crate::formats::item_type::ItemType; use crate::visit_ast::Module as DocModule; use utils::*; pub(crate) use self::types::*; -pub(crate) use self::utils::{get_auto_trait_and_blanket_impls, krate, register_res}; +pub(crate) use self::utils::{krate, register_res, synthesize_auto_trait_and_blanket_impls}; pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<'tcx>) -> Item { let mut items: Vec<Item> = vec![]; @@ -1782,6 +1782,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T BorrowedRef { lifetime, mutability: m.mutbl, type_: Box::new(clean_ty(m.ty, cx)) } } TyKind::Slice(ty) => Slice(Box::new(clean_ty(ty, cx))), + TyKind::Pat(ty, pat) => Type::Pat(Box::new(clean_ty(ty, cx)), format!("{pat:?}").into()), TyKind::Array(ty, ref length) => { let length = match length { hir::ArrayLen::Infer(..) => "_".to_string(), @@ -2008,6 +2009,10 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Float(float_ty) => Primitive(float_ty.into()), ty::Str => Primitive(PrimitiveType::Str), ty::Slice(ty) => Slice(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None))), + ty::Pat(ty, pat) => Type::Pat( + Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), + format!("{pat:?}").into_boxed_str(), + ), ty::Array(ty, mut n) => { n = n.normalize(cx.tcx, ty::ParamEnv::reveal_all()); let n = print_const(cx, n); diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 6793ea9f485..b592bd76e4c 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -23,6 +23,7 @@ use rustc_hir::{BodyId, Mutability}; use rustc_hir_analysis::check::intrinsic::intrinsic_operation_unsafety; use rustc_index::IndexVec; use rustc_metadata::rendered_const; +use rustc_middle::span_bug; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, TyCtxt, Visibility}; use rustc_resolve::rustdoc::{ @@ -266,8 +267,15 @@ impl ExternalCrate { let as_primitive = |res: Res<!>| { let Res::Def(DefKind::Mod, def_id) = res else { return None }; tcx.get_attrs(def_id, sym::rustc_doc_primitive).find_map(|attr| { - // FIXME: should warn on unknown primitives? - Some((def_id, PrimitiveType::from_symbol(attr.value_str()?)?)) + let attr_value = attr.value_str().expect("syntax should already be validated"); + let Some(prim) = PrimitiveType::from_symbol(attr_value) else { + span_bug!( + attr.span, + "primitive `{attr_value}` is not a member of `PrimitiveType`" + ); + }; + + Some((def_id, prim)) }) }; @@ -1475,7 +1483,9 @@ pub(crate) enum Type { /// /// This is mostly Rustdoc's version of [`hir::Path`]. /// It has to be different because Rustdoc's [`PathSegment`] can contain cleaned generics. - Path { path: Path }, + Path { + path: Path, + }, /// A `dyn Trait` object: `dyn for<'a> Trait<'a> + Send + 'static` DynTrait(Vec<PolyTrait>, Option<Lifetime>), /// A type parameter. @@ -1492,10 +1502,15 @@ pub(crate) enum Type { /// /// The `String` field is a stringified version of the array's length parameter. Array(Box<Type>, Box<str>), + Pat(Box<Type>, Box<str>), /// A raw pointer type: `*const i32`, `*mut i32` RawPointer(Mutability, Box<Type>), /// A reference type: `&i32`, `&'a mut Foo` - BorrowedRef { lifetime: Option<Lifetime>, mutability: Mutability, type_: Box<Type> }, + BorrowedRef { + lifetime: Option<Lifetime>, + mutability: Mutability, + type_: Box<Type>, + }, /// A qualified path to an associated item: `<Type as Trait>::Name` QPath(Box<QPathData>), @@ -1692,6 +1707,7 @@ impl Type { BareFunction(..) => PrimitiveType::Fn, Slice(..) => PrimitiveType::Slice, Array(..) => PrimitiveType::Array, + Type::Pat(..) => PrimitiveType::Pat, RawPointer(..) => PrimitiveType::RawPointer, QPath(box QPathData { ref self_type, .. }) => return self_type.inner_def_id(cache), Generic(_) | Infer | ImplTrait(_) => return None, @@ -1745,6 +1761,7 @@ pub(crate) enum PrimitiveType { Str, Slice, Array, + Pat, Tuple, Unit, RawPointer, @@ -1797,8 +1814,10 @@ impl PrimitiveType { sym::bool => Some(PrimitiveType::Bool), sym::char => Some(PrimitiveType::Char), sym::str => Some(PrimitiveType::Str), + sym::f16 => Some(PrimitiveType::F16), sym::f32 => Some(PrimitiveType::F32), sym::f64 => Some(PrimitiveType::F64), + sym::f128 => Some(PrimitiveType::F128), sym::array => Some(PrimitiveType::Array), sym::slice => Some(PrimitiveType::Slice), sym::tuple => Some(PrimitiveType::Tuple), @@ -1831,8 +1850,10 @@ impl PrimitiveType { U32 => single(SimplifiedType::Uint(UintTy::U32)), U64 => single(SimplifiedType::Uint(UintTy::U64)), U128 => single(SimplifiedType::Uint(UintTy::U128)), + F16 => single(SimplifiedType::Float(FloatTy::F16)), F32 => single(SimplifiedType::Float(FloatTy::F32)), F64 => single(SimplifiedType::Float(FloatTy::F64)), + F128 => single(SimplifiedType::Float(FloatTy::F128)), Str => single(SimplifiedType::Str), Bool => single(SimplifiedType::Bool), Char => single(SimplifiedType::Char), @@ -1895,6 +1916,7 @@ impl PrimitiveType { Bool => sym::bool, Char => sym::char, Array => sym::array, + Pat => sym::pat, Slice => sym::slice, Tuple => sym::tuple, Unit => sym::unit, diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index d5e0e83696f..dc62fbb5edb 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -1,5 +1,5 @@ use crate::clean::auto_trait::synthesize_auto_trait_impls; -use crate::clean::blanket_impl::BlanketImplFinder; +use crate::clean::blanket_impl::synthesize_blanket_impls; use crate::clean::render_macro_matchers::render_macro_matcher; use crate::clean::{ clean_doc_module, clean_middle_const, clean_middle_region, clean_middle_ty, inline, Crate, @@ -477,8 +477,7 @@ pub(crate) fn resolve_type(cx: &mut DocContext<'_>, path: Path) -> Type { } } -// FIXME(fmease): Update the `get_*` terminology to the `synthesize_` one. -pub(crate) fn get_auto_trait_and_blanket_impls( +pub(crate) fn synthesize_auto_trait_and_blanket_impls( cx: &mut DocContext<'_>, item_def_id: DefId, ) -> impl Iterator<Item = Item> { @@ -490,8 +489,8 @@ pub(crate) fn get_auto_trait_and_blanket_impls( let blanket_impls = cx .sess() .prof - .generic_activity("get_blanket_impls") - .run(|| BlanketImplFinder { cx }.get_blanket_impls(item_def_id)); + .generic_activity("synthesize_blanket_impls") + .run(|| synthesize_blanket_impls(cx, item_def_id)); auto_impls.into_iter().chain(blanket_impls) } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 312765d3e6d..f82b89fdd5f 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1071,6 +1071,10 @@ fn fmt_type<'cx>( write!(f, "]") } }, + clean::Type::Pat(ref t, ref pat) => { + fmt::Display::fmt(&t.print(cx), f)?; + write!(f, " is {pat}") + } clean::Array(ref t, ref n) => match **t { clean::Generic(name) if !f.alternate() => primitive_link( f, diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 51f90e45500..7083999de68 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -668,7 +668,7 @@ fn get_index_type_id( } } // Not supported yet - clean::Generic(_) | clean::ImplTrait(_) | clean::Infer => None, + clean::Type::Pat(..) | clean::Generic(_) | clean::ImplTrait(_) | clean::Infer => None, } } diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index cb50a27326f..35b99ab46f0 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -573,6 +573,10 @@ impl FromWithTcx<clean::Type> for Type { Tuple(t) => Type::Tuple(t.into_tcx(tcx)), Slice(t) => Type::Slice(Box::new((*t).into_tcx(tcx))), Array(t, s) => Type::Array { type_: Box::new((*t).into_tcx(tcx)), len: s.to_string() }, + clean::Type::Pat(t, p) => Type::Pat { + type_: Box::new((*t).into_tcx(tcx)), + __pat_unstable_do_not_use: p.to_string(), + }, ImplTrait(g) => Type::ImplTrait(g.into_tcx(tcx)), Infer => Type::Infer, RawPointer(mutability, type_) => Type::RawPointer { diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index a5c26429013..1ba4e6cbdf0 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -491,6 +491,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { ty::Str => Res::Primitive(Str), ty::Tuple(tys) if tys.is_empty() => Res::Primitive(Unit), ty::Tuple(_) => Res::Primitive(Tuple), + ty::Pat(..) => Res::Primitive(Pat), ty::Array(..) => Res::Primitive(Array), ty::Slice(_) => Res::Primitive(Slice), ty::RawPtr(_, _) => Res::Primitive(RawPointer), @@ -536,8 +537,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { I64 => tcx.types.i64, I128 => tcx.types.i128, Isize => tcx.types.isize, + F16 => tcx.types.f16, F32 => tcx.types.f32, F64 => tcx.types.f64, + F128 => tcx.types.f128, U8 => tcx.types.u8, U16 => tcx.types.u16, U32 => tcx.types.u32, @@ -2196,8 +2199,10 @@ fn resolve_primitive(path_str: &str, ns: Namespace) -> Option<Res> { "u32" => U32, "u64" => U64, "u128" => U128, + "f16" => F16, "f32" => F32, "f64" => F64, + "f128" => F128, "char" => Char, "bool" | "true" | "false" => Bool, "str" | "&str" => Str, diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index fbbb6152cfe..c92cf9d3e80 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -122,7 +122,7 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> _ => true, } }) { - let impls = get_auto_trait_and_blanket_impls(cx, def_id); + let impls = synthesize_auto_trait_and_blanket_impls(cx, def_id); new_items_external.extend(impls.filter(|i| cx.inlined.insert(i.item_id))); } } @@ -230,8 +230,10 @@ impl<'a, 'tcx> DocVisitor for SyntheticImplCollector<'a, 'tcx> { if i.is_struct() || i.is_enum() || i.is_union() { // FIXME(eddyb) is this `doc(hidden)` check needed? if !self.cx.tcx.is_doc_hidden(i.item_id.expect_def_id()) { - self.impls - .extend(get_auto_trait_and_blanket_impls(self.cx, i.item_id.expect_def_id())); + self.impls.extend(synthesize_auto_trait_and_blanket_impls( + self.cx, + i.item_id.expect_def_id(), + )); } } diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 164f88faa31..89d6f8d67f1 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use std::path::PathBuf; /// rustdoc format-version. -pub const FORMAT_VERSION: u32 = 28; +pub const FORMAT_VERSION: u32 = 29; /// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information /// about the language items in the local crate, as well as info about external items to allow @@ -562,6 +562,13 @@ pub enum Type { type_: Box<Type>, len: String, }, + /// `u32 is 1..` + Pat { + #[serde(rename = "type")] + type_: Box<Type>, + #[doc(hidden)] + __pat_unstable_do_not_use: String, + }, /// `impl TraitA + TraitB + ...` ImplTrait(Vec<GenericBound>), /// `_` diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 560b2acc1c7..f83fb1b9019 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -821,6 +821,7 @@ impl TyCoercionStability { | TyKind::Array(..) | TyKind::Ptr(_) | TyKind::BareFn(_) + | TyKind::Pat(..) | TyKind::Never | TyKind::Tup(_) | TyKind::Path(_) => Self::Deref, @@ -869,6 +870,7 @@ impl TyCoercionStability { | ty::Int(_) | ty::Uint(_) | ty::Array(..) + | ty::Pat(..) | ty::Float(_) | ty::RawPtr(..) | ty::FnPtr(_) diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 23fc323446e..d3347466be9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -12,7 +12,6 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node}; -use rustc_hir_typeck::{FnCtxt, TypeckRootCtxt}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; @@ -437,9 +436,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< Node::Item(item) => { if let ItemKind::Fn(_, _, body_id) = &item.kind && let output_ty = return_ty(cx, item.owner_id) - && let root_ctxt = TypeckRootCtxt::new(cx.tcx, item.owner_id.def_id) - && let fn_ctxt = FnCtxt::new(&root_ctxt, cx.param_env, item.owner_id.def_id) - && fn_ctxt.can_coerce(ty, output_ty) + && rustc_hir_typeck::can_coerce(cx.tcx, cx.param_env, item.owner_id.def_id, ty, output_ty) { if has_lifetime(output_ty) && has_lifetime(ty) { return false; diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index f5ce8dd29b1..3cf054e7207 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -66,7 +66,7 @@ declare_clippy_lint! { /// /// ### Known problems /// The lint does not work properly with desugaring and - /// macro, it has been allowed in the mean time. + /// macro, it has been allowed in the meantime. /// /// ### Example /// ```no_run diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index 6f5ac625e35..a6a6e9a3bac 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -1,4 +1,4 @@ -use super::utils::check_cast; +use rustc_hir_typeck::cast::check_cast; use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>( ) -> bool { use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; let mut app = Applicability::MachineApplicable; - let mut sugg = match check_cast(cx, e, from_ty, to_ty) { + let mut sugg = match check_cast(cx.tcx, cx.param_env, e, from_ty, to_ty) { Some(FnPtrAddrCast | PtrAddrCast) if const_context => return false, Some(PtrPtrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) => { Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app) diff --git a/src/tools/clippy/clippy_lints/src/transmute/utils.rs b/src/tools/clippy/clippy_lints/src/transmute/utils.rs index 15f1890aa39..e8ccd35b4da 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/utils.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/utils.rs @@ -1,10 +1,5 @@ -use rustc_hir as hir; -use rustc_hir::Expr; -use rustc_hir_typeck::{cast, FnCtxt, TypeckRootCtxt}; use rustc_lint::LateContext; -use rustc_middle::ty::cast::CastKind; use rustc_middle::ty::Ty; -use rustc_span::DUMMY_SP; // check if the component types of the transmuted collection and the result have different ABI, // size or alignment @@ -20,35 +15,3 @@ pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx false } } - -/// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of -/// the cast. In certain cases, including some invalid casts from array references -/// to pointers, this may cause additional errors to be emitted and/or ICE error -/// messages. This function will panic if that occurs. -pub(super) fn check_cast<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, -) -> Option<CastKind> { - let hir_id = e.hir_id; - let local_def_id = hir_id.owner.def_id; - - let root_ctxt = TypeckRootCtxt::new(cx.tcx, local_def_id); - let fn_ctxt = FnCtxt::new(&root_ctxt, cx.param_env, local_def_id); - - if let Ok(check) = cast::CastCheck::new( - &fn_ctxt, - e, - from_ty, - to_ty, - // We won't show any error to the user, so we don't care what the span is here. - DUMMY_SP, - DUMMY_SP, - hir::Constness::NotConst, - ) { - check.do_check(&fn_ctxt).ok() - } else { - None - } -} diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index f8bbe997774..6c3d9329932 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1068,6 +1068,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_ty(ty); self.hash_array_length(len); }, + TyKind::Pat(ty, pat) => { + self.hash_ty(ty); + self.hash_pat(pat); + }, TyKind::Ptr(ref mut_ty) => { self.hash_ty(mut_ty.ty); mut_ty.mutbl.hash(&mut self.s); diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr index b84f4e87e07..763ce59ba1d 100644 --- a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr +++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr @@ -4,7 +4,7 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace error: the compiler unexpectedly panicked. this is a bug. -note: it seems that this compiler <version> is outdated, a newer nightly should have been released in the mean time +note: it seems that this compiler <version> is outdated, a newer nightly should have been released in the meantime | = note: please consider running `rustup update nightly` to update the nightly channel and check if this problem still persists = note: if the problem still persists, we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 689fdc5dfeb..327e34ea36b 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -752,6 +752,19 @@ impl<'test> TestCx<'test> { Lazy::new(|| Regex::new(r"(?m:^)(?<prefix>(?: \|)+ Branch \()[0-9]+:").unwrap()); let coverage = BRANCH_LINE_NUMBER_RE.replace_all(&coverage, "${prefix}LL:"); + // ` |---> MC/DC Decision Region (1:30) to (2:` => ` |---> MC/DC Decision Region (LL:30) to (LL:` + static MCDC_DECISION_LINE_NUMBER_RE: Lazy<Regex> = Lazy::new(|| { + Regex::new(r"(?m:^)(?<prefix>(?: \|)+---> MC/DC Decision Region \()[0-9]+:(?<middle>[0-9]+\) to \()[0-9]+:").unwrap() + }); + let coverage = + MCDC_DECISION_LINE_NUMBER_RE.replace_all(&coverage, "${prefix}LL:${middle}LL:"); + + // ` | Condition C1 --> (1:` => ` | Condition C1 --> (LL:` + static MCDC_CONDITION_LINE_NUMBER_RE: Lazy<Regex> = Lazy::new(|| { + Regex::new(r"(?m:^)(?<prefix>(?: \|)+ Condition C[0-9]+ --> \()[0-9]+:").unwrap() + }); + let coverage = MCDC_CONDITION_LINE_NUMBER_RE.replace_all(&coverage, "${prefix}LL:"); + coverage.into_owned() } @@ -2354,6 +2367,11 @@ impl<'test> TestCx<'test> { "ignore-directory-in-diagnostics-source-blocks={}", home::cargo_home().expect("failed to find cargo home").to_str().unwrap() )); + // Similarly, vendored sources shouldn't be shown when running from a dist tarball. + rustc.arg("-Z").arg(format!( + "ignore-directory-in-diagnostics-source-blocks={}", + self.config.find_rust_src_root().unwrap().join("vendor").display(), + )); // Optionally prevent default --sysroot if specified in test compile-flags. if !self.props.compile_flags.iter().any(|flag| flag.starts_with("--sysroot")) @@ -4252,7 +4270,7 @@ impl<'test> TestCx<'test> { if self.props.run_rustfix && self.config.compare_mode.is_none() { // And finally, compile the fixed code and make sure it both // succeeds and has no diagnostics. - let rustc = self.make_compile_args( + let mut rustc = self.make_compile_args( &self.expected_output_path(UI_FIXED), TargetLocation::ThisFile(self.make_exe_name()), emit_metadata, @@ -4260,6 +4278,26 @@ impl<'test> TestCx<'test> { LinkToAux::Yes, Vec::new(), ); + + // If a test is revisioned, it's fixed source file can be named "a.foo.fixed", which, + // well, "a.foo" isn't a valid crate name. So we explicitly mangle the test name + // (including the revision) here to avoid the test writer having to manually specify a + // `#![crate_name = "..."]` as a workaround. This is okay since we're only checking if + // the fixed code is compilable. + if self.revision.is_some() { + let crate_name = + self.testpaths.file.file_stem().expect("test must have a file stem"); + // crate name must be alphanumeric or `_`. + let crate_name = + crate_name.to_str().expect("crate name implies file name must be valid UTF-8"); + // replace `a.foo` -> `a__foo` for crate name purposes. + // replace `revision-name-with-dashes` -> `revision_name_with_underscore` + let crate_name = crate_name.replace(".", "__"); + let crate_name = crate_name.replace("-", "_"); + rustc.arg("--crate-name"); + rustc.arg(crate_name); + } + let res = self.compose_and_run_compiler(rustc, None); if !res.status.success() { self.fatal_proc_rec("failed to compile fixed code", &res); diff --git a/src/tools/compiletest/src/runtest/tests.rs b/src/tools/compiletest/src/runtest/tests.rs index ee42243e83d..817b56109a5 100644 --- a/src/tools/compiletest/src/runtest/tests.rs +++ b/src/tools/compiletest/src/runtest/tests.rs @@ -50,72 +50,68 @@ fn normalize_platform_differences() { } /// Test for anonymizing line numbers in coverage reports, especially for -/// branch regions. +/// MC/DC regions. /// -/// FIXME(#119681): This test can be removed when we have examples of branch +/// FIXME(#123409): This test can be removed when we have examples of MC/DC /// coverage in the actual coverage test suite. #[test] fn anonymize_coverage_line_numbers() { let anon = |coverage| TestCx::anonymize_coverage_line_numbers(coverage); let input = r#" - 6| 3|fn print_size<T>() { - 7| 3| if std::mem::size_of::<T>() > 4 { + 7| 2|fn mcdc_check_neither(a: bool, b: bool) { + 8| 2| if a && b { + ^0 ------------------ - | Branch (7:8): [True: 0, False: 1] - | Branch (7:8): [True: 0, False: 1] - | Branch (7:8): [True: 1, False: 0] + |---> MC/DC Decision Region (8:8) to (8:14) + | + | Number of Conditions: 2 + | Condition C1 --> (8:8) + | Condition C2 --> (8:13) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, - = F } + | + | C1-Pair: not covered + | C2-Pair: not covered + | MC/DC Coverage for Decision: 0.00% + | ------------------ - 8| 1| println!("size > 4"); + 9| 0| say("a and b"); + 10| 2| } else { + 11| 2| say("not both"); + 12| 2| } + 13| 2|} "#; let expected = r#" - LL| 3|fn print_size<T>() { - LL| 3| if std::mem::size_of::<T>() > 4 { + LL| 2|fn mcdc_check_neither(a: bool, b: bool) { + LL| 2| if a && b { + ^0 ------------------ - | Branch (LL:8): [True: 0, False: 1] - | Branch (LL:8): [True: 0, False: 1] - | Branch (LL:8): [True: 1, False: 0] - ------------------ - LL| 1| println!("size > 4"); -"#; - - assert_eq!(anon(input), expected); - - ////////// - - let input = r#" - 12| 3|} - ------------------ - | branch_generics::print_size::<()>: - | 6| 1|fn print_size<T>() { - | 7| 1| if std::mem::size_of::<T>() > 4 { - | ------------------ - | | Branch (7:8): [True: 0, False: 1] - | ------------------ - | 8| 0| println!("size > 4"); - | 9| 1| } else { - | 10| 1| println!("size <= 4"); - | 11| 1| } - | 12| 1|} - ------------------ -"#; - - let expected = r#" - LL| 3|} - ------------------ - | branch_generics::print_size::<()>: - | LL| 1|fn print_size<T>() { - | LL| 1| if std::mem::size_of::<T>() > 4 { - | ------------------ - | | Branch (LL:8): [True: 0, False: 1] - | ------------------ - | LL| 0| println!("size > 4"); - | LL| 1| } else { - | LL| 1| println!("size <= 4"); - | LL| 1| } - | LL| 1|} + |---> MC/DC Decision Region (LL:8) to (LL:14) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:8) + | Condition C2 --> (LL:13) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, - = F } + | + | C1-Pair: not covered + | C2-Pair: not covered + | MC/DC Coverage for Decision: 0.00% + | ------------------ + LL| 0| say("a and b"); + LL| 2| } else { + LL| 2| say("not both"); + LL| 2| } + LL| 2|} "#; assert_eq!(anon(input), expected); diff --git a/src/tools/coverage-dump/src/covfun.rs b/src/tools/coverage-dump/src/covfun.rs index 49e3a6ed583..b308c8de14f 100644 --- a/src/tools/coverage-dump/src/covfun.rs +++ b/src/tools/coverage-dump/src/covfun.rs @@ -70,7 +70,8 @@ pub(crate) fn dump_covfun_mappings( } // If the mapping is a branch region, print both of its arms // in resolved form (even if they aren't expressions). - MappingKind::Branch { r#true, r#false } => { + MappingKind::Branch { r#true, r#false } + | MappingKind::MCDCBranch { r#true, r#false, .. } => { println!(" true = {}", expression_resolver.format_term(r#true)); println!(" false = {}", expression_resolver.format_term(r#false)); } @@ -164,6 +165,26 @@ impl<'a> Parser<'a> { let r#false = self.read_simple_term()?; Ok(MappingKind::Branch { r#true, r#false }) } + 5 => { + let bitmap_idx = self.read_uleb128_u32()?; + let conditions_num = self.read_uleb128_u32()?; + Ok(MappingKind::MCDCDecision { bitmap_idx, conditions_num }) + } + 6 => { + let r#true = self.read_simple_term()?; + let r#false = self.read_simple_term()?; + let condition_id = self.read_uleb128_u32()?; + let true_next_id = self.read_uleb128_u32()?; + let false_next_id = self.read_uleb128_u32()?; + Ok(MappingKind::MCDCBranch { + r#true, + r#false, + condition_id, + true_next_id, + false_next_id, + }) + } + _ => Err(anyhow!("unknown mapping kind: {raw_mapping_kind:#x}")), } } @@ -224,7 +245,28 @@ enum MappingKind { // Using raw identifiers here makes the dump output a little bit nicer // (via the derived Debug), at the expense of making this tool's source // code a little bit uglier. - Branch { r#true: CovTerm, r#false: CovTerm }, + Branch { + r#true: CovTerm, + r#false: CovTerm, + }, + MCDCBranch { + r#true: CovTerm, + r#false: CovTerm, + // These attributes are printed in Debug but not used directly. + #[allow(dead_code)] + condition_id: u32, + #[allow(dead_code)] + true_next_id: u32, + #[allow(dead_code)] + false_next_id: u32, + }, + MCDCDecision { + // These attributes are printed in Debug but not used directly. + #[allow(dead_code)] + bitmap_idx: u32, + #[allow(dead_code)] + conditions_num: u32, + }, } struct MappingRegion { diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index 592e97310a4..9e08f7e5f9b 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -262,6 +262,7 @@ impl<'a> Validator<'a> { Type::DynTrait(dyn_trait) => self.check_dyn_trait(dyn_trait), Type::Generic(_) => {} Type::Primitive(_) => {} + Type::Pat { type_, __pat_unstable_do_not_use: _ } => self.check_type(type_), Type::FunctionPointer(fp) => self.check_function_pointer(&**fp), Type::Tuple(tys) => tys.iter().for_each(|ty| self.check_type(ty)), Type::Slice(inner) => self.check_type(&**inner), diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs index 50ff0d26bbb..217da36ccc7 100644 --- a/src/tools/run-make-support/src/rustc.rs +++ b/src/tools/run-make-support/src/rustc.rs @@ -1,5 +1,5 @@ use std::env; -use std::ffi::OsStr; +use std::ffi::{OsStr, OsString}; use std::path::Path; use std::process::{Command, Output}; @@ -86,6 +86,33 @@ impl Rustc { self } + /// Specify number of codegen units + pub fn codegen_units(&mut self, units: usize) -> &mut Self { + self.cmd.arg(format!("-Ccodegen-units={units}")); + self + } + + /// Specify directory path used for incremental cache + pub fn incremental<P: AsRef<Path>>(&mut self, path: P) -> &mut Self { + let mut arg = OsString::new(); + arg.push("-Cincremental="); + arg.push(path.as_ref()); + self.cmd.arg(&arg); + self + } + + /// Specify error format to use + pub fn error_format(&mut self, format: &str) -> &mut Self { + self.cmd.arg(format!("--error-format={format}")); + self + } + + /// Specify json messages printed by the compiler + pub fn json(&mut self, items: &str) -> &mut Self { + self.cmd.arg(format!("--json={items}")); + self + } + /// Specify target triple. pub fn target(&mut self, target: &str) -> &mut Self { assert!(!target.contains(char::is_whitespace), "target triple cannot contain spaces"); @@ -94,13 +121,7 @@ impl Rustc { } /// Generic command argument provider. Use `.arg("-Zname")` over `.arg("-Z").arg("arg")`. - /// This method will panic if a plain `-Z` or `-C` is passed, or if `-Z <name>` or `-C <name>` - /// is passed (note the space). - pub fn arg(&mut self, arg: &str) -> &mut Self { - assert!( - !(["-Z", "-C"].contains(&arg) || arg.starts_with("-Z ") || arg.starts_with("-C ")), - "use `-Zarg` or `-Carg` over split `-Z` `arg` or `-C` `arg`" - ); + pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self { self.cmd.arg(arg); self } @@ -120,16 +141,7 @@ impl Rustc { } /// Generic command arguments provider. Use `.arg("-Zname")` over `.arg("-Z").arg("arg")`. - /// This method will panic if a plain `-Z` or `-C` is passed, or if `-Z <name>` or `-C <name>` - /// is passed (note the space). - pub fn args(&mut self, args: &[&str]) -> &mut Self { - for arg in args { - assert!( - !(["-Z", "-C"].contains(&arg) || arg.starts_with("-Z ") || arg.starts_with("-C ")), - "use `-Zarg` or `-Carg` over split `-Z` `arg` or `-C` `arg`" - ); - } - + pub fn args<S: AsRef<OsStr>>(&mut self, args: &[S]) -> &mut Self { self.cmd.args(args); self } diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 7f220a456a8..10a87f6e698 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -867,6 +867,11 @@ impl Rewrite for ast::Ty { self.span, shape, ), + ast::TyKind::Pat(ref ty, ref pat) => { + let ty = ty.rewrite(context, shape)?; + let pat = pat.rewrite(context, shape)?; + Some(format!("{ty} is {pat}")) + } } } } diff --git a/src/tools/tidy/src/error_codes.rs b/src/tools/tidy/src/error_codes.rs index 6fc65e56901..39f7e70b693 100644 --- a/src/tools/tidy/src/error_codes.rs +++ b/src/tools/tidy/src/error_codes.rs @@ -71,10 +71,12 @@ fn extract_error_codes(root_path: &Path, errors: &mut Vec<String>) -> Vec<String let path = root_path.join(Path::new(ERROR_CODES_PATH)); let file = fs::read_to_string(&path).unwrap_or_else(|e| panic!("failed to read `{path:?}`: {e}")); + let path = path.display(); let mut error_codes = Vec::new(); - for line in file.lines() { + for (line_index, line) in file.lines().enumerate() { + let line_index = line_index + 1; let line = line.trim(); if line.starts_with('E') { @@ -82,39 +84,54 @@ fn extract_error_codes(root_path: &Path, errors: &mut Vec<String>) -> Vec<String // Extract the error code from the line. Emit a fatal error if it is not in the correct // format. - let err_code = if let Some(err_code) = split_line { - err_code.0.to_owned() - } else { + let Some(split_line) = split_line else { errors.push(format!( - "Expected a line with the format `Eabcd: abcd, \ + "{path}:{line_index}: Expected a line with the format `Eabcd: abcd, \ but got \"{}\" without a `:` delimiter", line, )); continue; }; + let err_code = split_line.0.to_owned(); + // If this is a duplicate of another error code, emit a fatal error. if error_codes.contains(&err_code) { - errors.push(format!("Found duplicate error code: `{}`", err_code)); + errors.push(format!( + "{path}:{line_index}: Found duplicate error code: `{}`", + err_code + )); continue; } let mut chars = err_code.chars(); - chars.next(); + assert_eq!(chars.next(), Some('E')); let error_num_as_str = chars.as_str(); // Ensure that the line references the correct markdown file. - let expected_filename = format!(" {},", error_num_as_str); - if expected_filename != split_line.unwrap().1 { + let rest = split_line.1.split_once(','); + let Some(rest) = rest else { + errors.push(format!( + "{path}:{line_index}: Expected a line with the format `Eabcd: abcd, \ + but got \"{}\" without a `,` delimiter", + line, + )); + continue; + }; + if error_num_as_str != rest.0.trim() { errors.push(format!( - "`{}:` should be followed by `{}` but instead found `{}` in \ + "{path}:{line_index}: `{}:` should be followed by `{},` but instead found `{}` in \ `compiler/rustc_error_codes/src/lib.rs`", err_code, - expected_filename, - split_line.unwrap().1, + error_num_as_str, + split_line.1, )); continue; } + if !rest.1.trim().is_empty() && !rest.1.trim().starts_with("//") { + errors.push(format!("{path}:{line_index}: should only have one error per line")); + continue; + } error_codes.push(err_code); } @@ -146,14 +163,14 @@ fn check_error_codes_docs( return; } - // Make sure that the file is referenced in `error_codes.rs` + // Make sure that the file is referenced in `rustc_error_codes/src/lib.rs` let filename = path.file_name().unwrap().to_str().unwrap().split_once('.'); let err_code = filename.unwrap().0; // `unwrap` is ok because we know the filename is in the correct format. if error_codes.iter().all(|e| e != err_code) { errors.push(format!( "Found valid file `{}` in error code docs directory without corresponding \ - entry in `error_code.rs`", + entry in `rustc_error_codes/src/lib.rs`", path.display() )); return; diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 9be10528a64..e14932ad99d 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -1380,18 +1380,12 @@ ui/issues/auxiliary/issue-11224.rs ui/issues/auxiliary/issue-11508.rs ui/issues/auxiliary/issue-11529.rs ui/issues/auxiliary/issue-11680.rs -ui/issues/auxiliary/issue-12133-dylib.rs -ui/issues/auxiliary/issue-12133-dylib2.rs -ui/issues/auxiliary/issue-12133-rlib.rs ui/issues/auxiliary/issue-12612-1.rs ui/issues/auxiliary/issue-12612-2.rs ui/issues/auxiliary/issue-12660-aux.rs ui/issues/auxiliary/issue-13507.rs ui/issues/auxiliary/issue-13620-1.rs ui/issues/auxiliary/issue-13620-2.rs -ui/issues/auxiliary/issue-13872-1.rs -ui/issues/auxiliary/issue-13872-2.rs -ui/issues/auxiliary/issue-13872-3.rs ui/issues/auxiliary/issue-14344-1.rs ui/issues/auxiliary/issue-14344-2.rs ui/issues/auxiliary/issue-14421.rs @@ -1510,9 +1504,6 @@ ui/issues/issue-11958.rs ui/issues/issue-12033.rs ui/issues/issue-12041.rs ui/issues/issue-12127.rs -ui/issues/issue-12133-1.rs -ui/issues/issue-12133-2.rs -ui/issues/issue-12133-3.rs ui/issues/issue-12187-1.rs ui/issues/issue-12187-2.rs ui/issues/issue-12285.rs @@ -1556,7 +1547,6 @@ ui/issues/issue-13775.rs ui/issues/issue-13808.rs ui/issues/issue-13847.rs ui/issues/issue-13867.rs -ui/issues/issue-13872.rs ui/issues/issue-14082.rs ui/issues/issue-14091-2.rs ui/issues/issue-14091.rs @@ -1726,10 +1716,6 @@ ui/issues/issue-19127.rs ui/issues/issue-19129-1.rs ui/issues/issue-19129-2.rs ui/issues/issue-19135.rs -ui/issues/issue-1920-absolute-paths/auxiliary/issue-1920.rs -ui/issues/issue-1920-absolute-paths/issue-1920-1.rs -ui/issues/issue-1920-absolute-paths/issue-1920-2.rs -ui/issues/issue-1920-absolute-paths/issue-1920-3.rs ui/issues/issue-19244-1.rs ui/issues/issue-19244-2.rs ui/issues/issue-19293.rs @@ -1878,9 +1864,6 @@ ui/issues/issue-23217.rs ui/issues/issue-23253.rs ui/issues/issue-23261.rs ui/issues/issue-23281.rs -ui/issues/issue-23302-enum-infinite-recursion/issue-23302-1.rs -ui/issues/issue-23302-enum-infinite-recursion/issue-23302-2.rs -ui/issues/issue-23302-enum-infinite-recursion/issue-23302-3.rs ui/issues/issue-23304-1.rs ui/issues/issue-23304-2.rs ui/issues/issue-23311.rs @@ -1897,9 +1880,6 @@ ui/issues/issue-23543.rs ui/issues/issue-23544.rs ui/issues/issue-23550.rs ui/issues/issue-23589.rs -ui/issues/issue-23649-1.rs -ui/issues/issue-23649-2.rs -ui/issues/issue-23649-3.rs ui/issues/issue-23699.rs ui/issues/issue-2380-b.rs ui/issues/issue-23808.rs @@ -1970,7 +1950,6 @@ ui/issues/issue-25901.rs ui/issues/issue-26056.rs ui/issues/issue-26093.rs ui/issues/issue-26095.rs -ui/issues/issue-2611-3.rs ui/issues/issue-26127.rs ui/issues/issue-26186.rs ui/issues/issue-26205.rs @@ -2268,10 +2247,6 @@ ui/issues/issue-40350.rs ui/issues/issue-40402-ref-hints/issue-40402-1.rs ui/issues/issue-40402-ref-hints/issue-40402-2.rs ui/issues/issue-40408.rs -ui/issues/issue-40510-captured-variable-return/issue-40510-1.rs -ui/issues/issue-40510-captured-variable-return/issue-40510-2.rs -ui/issues/issue-40510-captured-variable-return/issue-40510-3.rs -ui/issues/issue-40510-captured-variable-return/issue-40510-4.rs ui/issues/issue-40610.rs ui/issues/issue-40749.rs ui/issues/issue-40782.rs @@ -2764,8 +2739,14 @@ ui/limits/issue-55878.rs ui/limits/issue-56762.rs ui/limits/issue-69485-var-size-diffs-too-large.rs ui/limits/issue-75158-64.rs +ui/linkage-attr/auxiliary/issue-12133-dylib.rs +ui/linkage-attr/auxiliary/issue-12133-dylib2.rs +ui/linkage-attr/auxiliary/issue-12133-rlib.rs ui/linkage-attr/issue-10755.rs ui/linkage-attr/issue-109144.rs +ui/linkage-attr/issue-12133-1.rs +ui/linkage-attr/issue-12133-2.rs +ui/linkage-attr/issue-12133-3.rs ui/lint/dead-code/issue-41883.rs ui/lint/dead-code/issue-59003.rs ui/lint/dead-code/issue-68408-false-positive.rs @@ -3051,7 +3032,15 @@ ui/mismatched_types/issue-84976.rs ui/missing-trait-bounds/auxiliary/issue-69725.rs ui/missing-trait-bounds/issue-35677.rs ui/missing-trait-bounds/issue-69725.rs +ui/modules/auxiliary/issue-13872-1.rs +ui/modules/auxiliary/issue-13872-2.rs +ui/modules/auxiliary/issue-13872-3.rs +ui/modules/auxiliary/issue-1920.rs ui/modules/issue-107649.rs +ui/modules/issue-13872.rs +ui/modules/issue-1920-1.rs +ui/modules/issue-1920-2.rs +ui/modules/issue-1920-3.rs ui/modules/issue-56411-aux.rs ui/modules/issue-56411.rs ui/moves/issue-22536-copy-mustnt-zero.rs @@ -3092,6 +3081,10 @@ ui/nll/issue-30438-b.rs ui/nll/issue-30438-c.rs ui/nll/issue-31567.rs ui/nll/issue-32382-index-assoc-type-with-lifetime.rs +ui/nll/issue-40510-1.rs +ui/nll/issue-40510-2.rs +ui/nll/issue-40510-3.rs +ui/nll/issue-40510-4.rs ui/nll/issue-42574-diagnostic-in-nested-closure.rs ui/nll/issue-43058.rs ui/nll/issue-45157.rs @@ -3621,6 +3614,9 @@ ui/reachable/issue-11225-1.rs ui/reachable/issue-11225-2.rs ui/reachable/issue-11225-3.rs ui/reachable/issue-948.rs +ui/recursion/issue-23302-1.rs +ui/recursion/issue-23302-2.rs +ui/recursion/issue-23302-3.rs ui/recursion/issue-26548-recursion-via-normalize.rs ui/recursion/issue-38591-non-regular-dropck-recursion.rs ui/recursion/issue-83150.rs @@ -4039,6 +4035,7 @@ ui/traits/issue-23003-overflow.rs ui/traits/issue-23003.rs ui/traits/issue-23825.rs ui/traits/issue-24010.rs +ui/traits/issue-2611-3.rs ui/traits/issue-26339.rs ui/traits/issue-28576.rs ui/traits/issue-32963.rs @@ -4135,7 +4132,6 @@ ui/type-alias-impl-trait/issue-52843.rs ui/type-alias-impl-trait/issue-53092-2.rs ui/type-alias-impl-trait/issue-53092.rs ui/type-alias-impl-trait/issue-53096.rs -ui/type-alias-impl-trait/issue-53398-cyclic-types.rs ui/type-alias-impl-trait/issue-53598.rs ui/type-alias-impl-trait/issue-53678-coroutine-and-const-fn.rs ui/type-alias-impl-trait/issue-55099-lifetime-inference.rs @@ -4350,6 +4346,9 @@ ui/unsized-locals/issue-50940.rs ui/unsized-locals/issue-67981.rs ui/unsized/issue-115203.rs ui/unsized/issue-115809.rs +ui/unsized/issue-23649-1.rs +ui/unsized/issue-23649-2.rs +ui/unsized/issue-23649-3.rs ui/unsized/issue-30355.rs ui/unsized/issue-40231-1.rs ui/unsized/issue-40231-2.rs diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 65ea685b37c..4fae7572ffb 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -17,7 +17,7 @@ use std::path::{Path, PathBuf}; const ENTRY_LIMIT: usize = 900; // FIXME: The following limits should be reduced eventually. -const ISSUES_ENTRY_LIMIT: usize = 1733; +const ISSUES_ENTRY_LIMIT: usize = 1722; const ROOT_ENTRY_LIMIT: usize = 861; const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ diff --git a/tests/codegen/enum/enum-early-otherwise-branch.rs b/tests/codegen/enum/enum-early-otherwise-branch.rs new file mode 100644 index 00000000000..6c7548912da --- /dev/null +++ b/tests/codegen/enum/enum-early-otherwise-branch.rs @@ -0,0 +1,26 @@ +//@ compile-flags: -O +//@ min-llvm-version: 18 + +#![crate_type = "lib"] + +pub enum Enum { + A(u32), + B(u32), + C(u32), +} + +#[no_mangle] +pub fn foo(lhs: &Enum, rhs: &Enum) -> bool { + // CHECK-LABEL: define{{.*}}i1 @foo( + // CHECK-NOT: switch + // CHECK-NOT: br + // CHECK: [[SELECT:%.*]] = select + // CHECK-NEXT: ret i1 [[SELECT]] + // CHECK-NEXT: } + match (lhs, rhs) { + (Enum::A(lhs), Enum::A(rhs)) => lhs == rhs, + (Enum::B(lhs), Enum::B(rhs)) => lhs == rhs, + (Enum::C(lhs), Enum::C(rhs)) => lhs == rhs, + _ => false, + } +} diff --git a/tests/codegen/issues/issue-122805.rs b/tests/codegen/issues/issue-122805.rs index 3bfffcd4a96..6a5be39238f 100644 --- a/tests/codegen/issues/issue-122805.rs +++ b/tests/codegen/issues/issue-122805.rs @@ -20,6 +20,7 @@ // CHECK-NEXT: store <8 x i16> // CHECK-NEXT: ret void #[no_mangle] +#[cfg(target_endian = "little")] pub fn convert(value: [u16; 8]) -> [u8; 16] { let addr16 = [ value[0].to_be(), diff --git a/tests/codegen/match-optimized.rs b/tests/codegen/match-optimized.rs index 09907edf8f2..5cecafb9f29 100644 --- a/tests/codegen/match-optimized.rs +++ b/tests/codegen/match-optimized.rs @@ -26,12 +26,12 @@ pub fn exhaustive_match(e: E) -> u8 { // CHECK-NEXT: store i8 1, ptr %_0, align 1 // CHECK-NEXT: br label %[[EXIT]] // CHECK: [[C]]: -// CHECK-NEXT: store i8 2, ptr %_0, align 1 +// CHECK-NEXT: store i8 3, ptr %_0, align 1 // CHECK-NEXT: br label %[[EXIT]] match e { E::A => 0, E::B => 1, - E::C => 2, + E::C => 3, } } diff --git a/tests/codegen/pattern_type_symbols.rs b/tests/codegen/pattern_type_symbols.rs new file mode 100644 index 00000000000..dfe83443348 --- /dev/null +++ b/tests/codegen/pattern_type_symbols.rs @@ -0,0 +1,23 @@ +//! Check that symbol names with pattern types in them are +//! different from the same symbol with the base type + +//@ compile-flags: -Csymbol-mangling-version=v0 -Copt-level=0 --crate-type=lib + +#![feature(pattern_types)] +#![feature(core_pattern_types)] +#![feature(core_pattern_type)] + +use std::pat::pattern_type; + +type NanoU32 = crate::pattern_type!(u32 is 0..=999_999_999); + +fn foo<T>() {} + +pub fn bar() { + // CHECK: call pattern_type_symbols::foo::<u32> + // CHECK: call void @_RINvCs3QvG2ESzx2Q_20pattern_type_symbols3foomEB2_ + foo::<u32>(); + // CHECK: call pattern_type_symbols::foo::<(u32, [(); 0], [(); 999999999], [(); true])> + // CHECK: call void @_RINvCs3QvG2ESzx2Q_20pattern_type_symbols3fooTmAum0_Aum3b9ac9ff_Aub1_EEB2_ + foo::<NanoU32>(); +} diff --git a/tests/codegen/powerpc64le-struct-align-128.rs b/tests/codegen/powerpc64le-struct-align-128.rs new file mode 100644 index 00000000000..0096c6d3138 --- /dev/null +++ b/tests/codegen/powerpc64le-struct-align-128.rs @@ -0,0 +1,93 @@ +// Test that structs aligned to 128 bits are passed with the correct ABI on powerpc64le. +// This is similar to aarch64-struct-align-128.rs, but for ppc. + +//@ compile-flags: --target powerpc64le-unknown-linux-gnu +//@ needs-llvm-components: powerpc + +#![feature(no_core, lang_items)] +#![crate_type = "lib"] +#![no_core] + +#[lang="sized"] +trait Sized { } +#[lang="freeze"] +trait Freeze { } +#[lang="copy"] +trait Copy { } + +#[repr(C)] +pub struct Align8 { + pub a: u64, + pub b: u64, +} + +#[repr(transparent)] +pub struct Transparent8 { + a: Align8 +} + +#[repr(C)] +pub struct Wrapped8 { + a: Align8, +} + +extern "C" { + // CHECK: declare void @test_8([2 x i64], [2 x i64], [2 x i64]) + fn test_8(a: Align8, b: Transparent8, c: Wrapped8); +} + +#[repr(C)] +#[repr(align(16))] +pub struct Align16 { + pub a: u64, + pub b: u64, +} + +#[repr(transparent)] +pub struct Transparent16 { + a: Align16 +} + +#[repr(C)] +pub struct Wrapped16 { + pub a: Align16, +} + +extern "C" { + // It's important that this produces [1 x i128] rather than just i128! + // CHECK: declare void @test_16([1 x i128], [1 x i128], [1 x i128]) + fn test_16(a: Align16, b: Transparent16, c: Wrapped16); +} + +#[repr(C)] +#[repr(align(32))] +pub struct Align32 { + pub a: u64, + pub b: u64, + pub c: u64, +} + +#[repr(transparent)] +pub struct Transparent32 { + a: Align32 +} + +#[repr(C)] +pub struct Wrapped32 { + pub a: Align32, +} + +extern "C" { + // CHECK: declare void @test_32([2 x i128], [2 x i128], [2 x i128]) + fn test_32(a: Align32, b: Transparent32, c: Wrapped32); +} + +pub unsafe fn main( + a1: Align8, a2: Transparent8, a3: Wrapped8, + b1: Align16, b2: Transparent16, b3: Wrapped16, + c1: Align32, c2: Transparent32, c3: Wrapped32, +) { + test_8(a1, a2, a3); + test_16(b1, b2, b3); + test_32(c1, c2, c3); +} diff --git a/tests/codegen/slice-indexing.rs b/tests/codegen/slice-indexing.rs index ecce9201071..3d284148db2 100644 --- a/tests/codegen/slice-indexing.rs +++ b/tests/codegen/slice-indexing.rs @@ -32,3 +32,31 @@ pub unsafe fn get_unchecked_mut_by_range(x: &mut [i32], r: Range<usize>) -> &mut // CHECK: sub nuw i64 x.get_unchecked_mut(r) } + +// CHECK-LABEL: @str_index_by_range( +#[no_mangle] +pub fn str_index_by_range(x: &str, r: Range<usize>) -> &str { + // CHECK: sub nuw i64 + &x[r] +} + +// CHECK-LABEL: @str_get_unchecked_by_range( +#[no_mangle] +pub unsafe fn str_get_unchecked_by_range(x: &str, r: Range<usize>) -> &str { + // CHECK: sub nuw i64 + x.get_unchecked(r) +} + +// CHECK-LABEL: @str_index_mut_by_range( +#[no_mangle] +pub fn str_index_mut_by_range(x: &mut str, r: Range<usize>) -> &mut str { + // CHECK: sub nuw i64 + &mut x[r] +} + +// CHECK-LABEL: @str_get_unchecked_mut_by_range( +#[no_mangle] +pub unsafe fn str_get_unchecked_mut_by_range(x: &mut str, r: Range<usize>) -> &mut str { + // CHECK: sub nuw i64 + x.get_unchecked_mut(r) +} diff --git a/tests/codegen/step_by-overflow-checks.rs b/tests/codegen/step_by-overflow-checks.rs new file mode 100644 index 00000000000..43e8514a8b7 --- /dev/null +++ b/tests/codegen/step_by-overflow-checks.rs @@ -0,0 +1,26 @@ +//@ compile-flags: -O + +#![crate_type = "lib"] + +use std::iter::StepBy; +use std::slice::Iter; + +// The constructor for `StepBy` ensures we can never end up needing to do zero +// checks on denominators, so check that the code isn't emitting panic paths. + +// CHECK-LABEL: @step_by_len_std +#[no_mangle] +pub fn step_by_len_std(x: &StepBy<Iter<i32>>) -> usize { + // CHECK-NOT: div_by_zero + // CHECK: udiv + // CHECK-NOT: div_by_zero + x.len() +} + +// CHECK-LABEL: @step_by_len_naive +#[no_mangle] +pub fn step_by_len_naive(x: Iter<i32>, step_minus_one: usize) -> usize { + // CHECK: udiv + // CHECK: call{{.+}}div_by_zero + x.len() / (step_minus_one + 1) +} diff --git a/tests/codegen/vecdeque_pop_push.rs b/tests/codegen/vecdeque_pop_push.rs new file mode 100644 index 00000000000..040d5a279dc --- /dev/null +++ b/tests/codegen/vecdeque_pop_push.rs @@ -0,0 +1,67 @@ +//@ compile-flags: -O + +#![crate_type = "lib"] + +use std::collections::VecDeque; + +#[no_mangle] +// CHECK-LABEL: @noop_back( +pub fn noop_back(v: &mut VecDeque<u8>) { + // CHECK-NOT: grow + // CHECK: tail call void @llvm.assume + // CHECK-NOT: grow + // CHECK: ret + if let Some(x) = v.pop_back() { + v.push_back(x); + } +} + +#[no_mangle] +// CHECK-LABEL: @noop_front( +pub fn noop_front(v: &mut VecDeque<u8>) { + // CHECK-NOT: grow + // CHECK: tail call void @llvm.assume + // CHECK-NOT: grow + // CHECK: ret + if let Some(x) = v.pop_front() { + v.push_front(x); + } +} + +#[no_mangle] +// CHECK-LABEL: @move_byte_front_to_back( +pub fn move_byte_front_to_back(v: &mut VecDeque<u8>) { + // CHECK-NOT: grow + // CHECK: tail call void @llvm.assume + // CHECK-NOT: grow + // CHECK: ret + if let Some(x) = v.pop_front() { + v.push_back(x); + } +} + +#[no_mangle] +// CHECK-LABEL: @move_byte_back_to_front( +pub fn move_byte_back_to_front(v: &mut VecDeque<u8>) { + // CHECK-NOT: grow + // CHECK: tail call void @llvm.assume + // CHECK-NOT: grow + // CHECK: ret + if let Some(x) = v.pop_back() { + v.push_front(x); + } +} + +#[no_mangle] +// CHECK-LABEL: @push_back_byte( +pub fn push_back_byte(v: &mut VecDeque<u8>) { + // CHECK: call {{.*}}grow + v.push_back(3); +} + +#[no_mangle] +// CHECK-LABEL: @push_front_byte( +pub fn push_front_byte(v: &mut VecDeque<u8>) { + // CHECK: call {{.*}}grow + v.push_front(3); +} diff --git a/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir b/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir index 76938c14e1e..cb72ad3d253 100644 --- a/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir @@ -1,36 +1,36 @@ // MIR for `address_of_reborrow` after SimplifyCfg-initial | User Type Annotations -| 0: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:8:5: 8:18, inferred_ty: *const [i32; 10] -| 1: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:10:5: 10:25, inferred_ty: *const dyn std::marker::Send -| 2: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] -| 3: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] -| 4: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] -| 5: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] -| 6: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send -| 7: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send -| 8: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] -| 9: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] -| 10: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:19:5: 19:18, inferred_ty: *const [i32; 10] -| 11: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:21:5: 21:25, inferred_ty: *const dyn std::marker::Send -| 12: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] -| 13: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] -| 14: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] -| 15: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] -| 16: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send -| 17: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send -| 18: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] -| 19: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] -| 20: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:29:5: 29:16, inferred_ty: *mut [i32; 10] -| 21: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:31:5: 31:23, inferred_ty: *mut dyn std::marker::Send -| 22: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] -| 23: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] -| 24: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] -| 25: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] -| 26: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send -| 27: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send -| 28: user_ty: Canonical { value: Ty(*mut [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:37:12: 37:22, inferred_ty: *mut [i32] -| 29: user_ty: Canonical { value: Ty(*mut [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:37:12: 37:22, inferred_ty: *mut [i32] +| 0: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:8:5: 8:18, inferred_ty: *const [i32; 10] +| 1: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:10:5: 10:25, inferred_ty: *const dyn std::marker::Send +| 2: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] +| 3: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] +| 4: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] +| 5: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] +| 6: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send +| 7: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send +| 8: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] +| 9: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] +| 10: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:19:5: 19:18, inferred_ty: *const [i32; 10] +| 11: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:21:5: 21:25, inferred_ty: *const dyn std::marker::Send +| 12: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] +| 13: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] +| 14: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] +| 15: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] +| 16: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send +| 17: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send +| 18: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] +| 19: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] +| 20: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:29:5: 29:16, inferred_ty: *mut [i32; 10] +| 21: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:31:5: 31:23, inferred_ty: *mut dyn std::marker::Send +| 22: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] +| 23: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] +| 24: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] +| 25: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] +| 26: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send +| 27: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send +| 28: user_ty: Canonical { value: Ty(*mut [i32]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:37:12: 37:22, inferred_ty: *mut [i32] +| 29: user_ty: Canonical { value: Ty(*mut [i32]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:37:12: 37:22, inferred_ty: *mut [i32] | fn address_of_reborrow() -> () { let mut _0: (); diff --git a/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir b/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir index 5df6633880e..5c0d1e9b93f 100644 --- a/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir @@ -1,8 +1,8 @@ // MIR for `main` after SimplifyCfg-initial | User Type Annotations -| 0: user_ty: Canonical { value: Ty(std::option::Option<std::boxed::Box<u32>>), max_universe: U0, variables: [] }, span: $DIR/basic_assignment.rs:38:17: 38:33, inferred_ty: std::option::Option<std::boxed::Box<u32>> -| 1: user_ty: Canonical { value: Ty(std::option::Option<std::boxed::Box<u32>>), max_universe: U0, variables: [] }, span: $DIR/basic_assignment.rs:38:17: 38:33, inferred_ty: std::option::Option<std::boxed::Box<u32>> +| 0: user_ty: Canonical { value: Ty(std::option::Option<std::boxed::Box<u32>>), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/basic_assignment.rs:38:17: 38:33, inferred_ty: std::option::Option<std::boxed::Box<u32>> +| 1: user_ty: Canonical { value: Ty(std::option::Option<std::boxed::Box<u32>>), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/basic_assignment.rs:38:17: 38:33, inferred_ty: std::option::Option<std::boxed::Box<u32>> | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/building/issue_101867.main.built.after.mir b/tests/mir-opt/building/issue_101867.main.built.after.mir index 19a777cb03b..0f7917dbb5c 100644 --- a/tests/mir-opt/building/issue_101867.main.built.after.mir +++ b/tests/mir-opt/building/issue_101867.main.built.after.mir @@ -1,8 +1,8 @@ // MIR for `main` after built | User Type Annotations -| 0: user_ty: Canonical { value: Ty(std::option::Option<u8>), max_universe: U0, variables: [] }, span: $DIR/issue_101867.rs:4:12: 4:22, inferred_ty: std::option::Option<u8> -| 1: user_ty: Canonical { value: Ty(std::option::Option<u8>), max_universe: U0, variables: [] }, span: $DIR/issue_101867.rs:4:12: 4:22, inferred_ty: std::option::Option<u8> +| 0: user_ty: Canonical { value: Ty(std::option::Option<u8>), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_101867.rs:4:12: 4:22, inferred_ty: std::option::Option<u8> +| 1: user_ty: Canonical { value: Ty(std::option::Option<u8>), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_101867.rs:4:12: 4:22, inferred_ty: std::option::Option<u8> | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/building/receiver_ptr_mutability.main.built.after.mir b/tests/mir-opt/building/receiver_ptr_mutability.main.built.after.mir index e07c2b6fa9d..1855bb0787d 100644 --- a/tests/mir-opt/building/receiver_ptr_mutability.main.built.after.mir +++ b/tests/mir-opt/building/receiver_ptr_mutability.main.built.after.mir @@ -1,10 +1,10 @@ // MIR for `main` after built | User Type Annotations -| 0: user_ty: Canonical { value: Ty(*mut Test), max_universe: U0, variables: [] }, span: $DIR/receiver_ptr_mutability.rs:15:14: 15:23, inferred_ty: *mut Test -| 1: user_ty: Canonical { value: Ty(*mut Test), max_universe: U0, variables: [] }, span: $DIR/receiver_ptr_mutability.rs:15:14: 15:23, inferred_ty: *mut Test -| 2: user_ty: Canonical { value: Ty(&&&&*mut Test), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/receiver_ptr_mutability.rs:19:18: 19:31, inferred_ty: &&&&*mut Test -| 3: user_ty: Canonical { value: Ty(&&&&*mut Test), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/receiver_ptr_mutability.rs:19:18: 19:31, inferred_ty: &&&&*mut Test +| 0: user_ty: Canonical { value: Ty(*mut Test), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/receiver_ptr_mutability.rs:15:14: 15:23, inferred_ty: *mut Test +| 1: user_ty: Canonical { value: Ty(*mut Test), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/receiver_ptr_mutability.rs:15:14: 15:23, inferred_ty: *mut Test +| 2: user_ty: Canonical { value: Ty(&&&&*mut Test), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/receiver_ptr_mutability.rs:19:18: 19:31, inferred_ty: &&&&*mut Test +| 3: user_ty: Canonical { value: Ty(&&&&*mut Test), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/receiver_ptr_mutability.rs:19:18: 19:31, inferred_ty: &&&&*mut Test | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/early_otherwise_branch.opt1.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch.opt1.EarlyOtherwiseBranch.diff index 7a374c5675a..4af3ed3e1d1 100644 --- a/tests/mir-opt/early_otherwise_branch.opt1.EarlyOtherwiseBranch.diff +++ b/tests/mir-opt/early_otherwise_branch.opt1.EarlyOtherwiseBranch.diff @@ -12,8 +12,6 @@ let mut _7: isize; let _8: u32; let _9: u32; -+ let mut _10: isize; -+ let mut _11: bool; scope 1 { debug a => _8; debug b => _9; @@ -29,28 +27,20 @@ StorageDead(_5); StorageDead(_4); _7 = discriminant((_3.0: std::option::Option<u32>)); -- switchInt(move _7) -> [1: bb2, otherwise: bb1]; -+ StorageLive(_10); -+ _10 = discriminant((_3.1: std::option::Option<u32>)); -+ StorageLive(_11); -+ _11 = Ne(_7, move _10); -+ StorageDead(_10); -+ switchInt(move _11) -> [0: bb4, otherwise: bb1]; + switchInt(move _7) -> [1: bb2, 0: bb1, otherwise: bb5]; } bb1: { -+ StorageDead(_11); _0 = const 1_u32; -- goto -> bb4; -+ goto -> bb3; + goto -> bb4; } bb2: { -- _6 = discriminant((_3.1: std::option::Option<u32>)); -- switchInt(move _6) -> [1: bb3, otherwise: bb1]; -- } -- -- bb3: { + _6 = discriminant((_3.1: std::option::Option<u32>)); + switchInt(move _6) -> [1: bb3, 0: bb1, otherwise: bb5]; + } + + bb3: { StorageLive(_8); _8 = (((_3.0: std::option::Option<u32>) as Some).0: u32); StorageLive(_9); @@ -58,19 +48,16 @@ _0 = const 0_u32; StorageDead(_9); StorageDead(_8); -- goto -> bb4; -+ goto -> bb3; + goto -> bb4; } -- bb4: { -+ bb3: { + bb4: { StorageDead(_3); return; -+ } -+ -+ bb4: { -+ StorageDead(_11); -+ switchInt(_7) -> [1: bb2, otherwise: bb1]; + } + + bb5: { + unreachable; } } diff --git a/tests/mir-opt/early_otherwise_branch.opt2.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch.opt2.EarlyOtherwiseBranch.diff index 1348bdd739a..7776ff0fde7 100644 --- a/tests/mir-opt/early_otherwise_branch.opt2.EarlyOtherwiseBranch.diff +++ b/tests/mir-opt/early_otherwise_branch.opt2.EarlyOtherwiseBranch.diff @@ -13,8 +13,6 @@ let mut _8: isize; let _9: u32; let _10: u32; -+ let mut _11: isize; -+ let mut _12: bool; scope 1 { debug a => _9; debug b => _10; @@ -30,33 +28,25 @@ StorageDead(_5); StorageDead(_4); _8 = discriminant((_3.0: std::option::Option<u32>)); -- switchInt(move _8) -> [0: bb3, 1: bb2, otherwise: bb1]; -+ StorageLive(_11); -+ _11 = discriminant((_3.1: std::option::Option<u32>)); -+ StorageLive(_12); -+ _12 = Ne(_8, move _11); -+ StorageDead(_11); -+ switchInt(move _12) -> [0: bb5, otherwise: bb1]; + switchInt(move _8) -> [0: bb3, 1: bb2, otherwise: bb7]; } bb1: { -+ StorageDead(_12); _0 = const 1_u32; -- goto -> bb6; -+ goto -> bb4; + goto -> bb6; } bb2: { -- _6 = discriminant((_3.1: std::option::Option<u32>)); -- switchInt(move _6) -> [1: bb4, otherwise: bb1]; -- } -- -- bb3: { -- _7 = discriminant((_3.1: std::option::Option<u32>)); -- switchInt(move _7) -> [0: bb5, otherwise: bb1]; -- } -- -- bb4: { + _6 = discriminant((_3.1: std::option::Option<u32>)); + switchInt(move _6) -> [1: bb4, 0: bb1, otherwise: bb7]; + } + + bb3: { + _7 = discriminant((_3.1: std::option::Option<u32>)); + switchInt(move _7) -> [0: bb5, 1: bb1, otherwise: bb7]; + } + + bb4: { StorageLive(_9); _9 = (((_3.0: std::option::Option<u32>) as Some).0: u32); StorageLive(_10); @@ -64,26 +54,21 @@ _0 = const 0_u32; StorageDead(_10); StorageDead(_9); -- goto -> bb6; -+ goto -> bb4; + goto -> bb6; } -- bb5: { -+ bb3: { - _0 = const 0_u32; -- goto -> bb6; -+ goto -> bb4; + bb5: { + _0 = const 2_u32; + goto -> bb6; } -- bb6: { -+ bb4: { + bb6: { StorageDead(_3); return; -+ } -+ -+ bb5: { -+ StorageDead(_12); -+ switchInt(_8) -> [0: bb3, 1: bb2, otherwise: bb1]; + } + + bb7: { + unreachable; } } diff --git a/tests/mir-opt/early_otherwise_branch.opt3.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch.opt3.EarlyOtherwiseBranch.diff index e058c409cb5..b41e952d80f 100644 --- a/tests/mir-opt/early_otherwise_branch.opt3.EarlyOtherwiseBranch.diff +++ b/tests/mir-opt/early_otherwise_branch.opt3.EarlyOtherwiseBranch.diff @@ -1,76 +1,107 @@ - // MIR for `opt3` before EarlyOtherwiseBranch + // MIR for `opt3` after EarlyOtherwiseBranch - fn opt3(_1: Option<u32>, _2: Option<bool>) -> u32 { + fn opt3(_1: Option2<u32>, _2: Option2<bool>) -> u32 { debug x => _1; debug y => _2; let mut _0: u32; - let mut _3: (std::option::Option<u32>, std::option::Option<bool>); - let mut _4: std::option::Option<u32>; - let mut _5: std::option::Option<bool>; + let mut _3: (Option2<u32>, Option2<bool>); + let mut _4: Option2<u32>; + let mut _5: Option2<bool>; let mut _6: isize; let mut _7: isize; - let _8: u32; - let _9: bool; -+ let mut _10: isize; -+ let mut _11: bool; + let mut _8: isize; + let mut _9: isize; + let _10: u32; + let _11: bool; ++ let mut _12: isize; ++ let mut _13: bool; scope 1 { - debug a => _8; - debug b => _9; + debug a => _10; + debug b => _11; } bb0: { StorageLive(_3); StorageLive(_4); - _4 = _1; + _4 = move _1; StorageLive(_5); - _5 = _2; + _5 = move _2; _3 = (move _4, move _5); StorageDead(_5); StorageDead(_4); - _7 = discriminant((_3.0: std::option::Option<u32>)); -- switchInt(move _7) -> [1: bb2, otherwise: bb1]; -+ StorageLive(_10); -+ _10 = discriminant((_3.1: std::option::Option<bool>)); -+ StorageLive(_11); -+ _11 = Ne(_7, move _10); -+ StorageDead(_10); -+ switchInt(move _11) -> [0: bb4, otherwise: bb1]; + _9 = discriminant((_3.0: Option2<u32>)); +- switchInt(move _9) -> [0: bb2, 1: bb3, 2: bb4, otherwise: bb9]; ++ StorageLive(_12); ++ _12 = discriminant((_3.1: Option2<bool>)); ++ StorageLive(_13); ++ _13 = Ne(_9, move _12); ++ StorageDead(_12); ++ switchInt(move _13) -> [0: bb7, otherwise: bb1]; } bb1: { -+ StorageDead(_11); ++ StorageDead(_13); _0 = const 1_u32; -- goto -> bb4; -+ goto -> bb3; +- goto -> bb8; ++ goto -> bb5; } bb2: { -- _6 = discriminant((_3.1: std::option::Option<bool>)); -- switchInt(move _6) -> [1: bb3, otherwise: bb1]; +- _6 = discriminant((_3.1: Option2<bool>)); +- switchInt(move _6) -> [0: bb5, otherwise: bb1]; - } - - bb3: { - StorageLive(_8); - _8 = (((_3.0: std::option::Option<u32>) as Some).0: u32); - StorageLive(_9); - _9 = (((_3.1: std::option::Option<bool>) as Some).0: bool); +- _7 = discriminant((_3.1: Option2<bool>)); +- switchInt(move _7) -> [1: bb6, otherwise: bb1]; +- } +- +- bb4: { +- _8 = discriminant((_3.1: Option2<bool>)); +- switchInt(move _8) -> [2: bb7, otherwise: bb1]; +- } +- +- bb5: { + StorageLive(_10); + _10 = (((_3.0: Option2<u32>) as Some).0: u32); + StorageLive(_11); + _11 = (((_3.1: Option2<bool>) as Some).0: bool); _0 = const 0_u32; - StorageDead(_9); - StorageDead(_8); -- goto -> bb4; -+ goto -> bb3; + StorageDead(_11); + StorageDead(_10); +- goto -> bb8; ++ goto -> bb5; } -- bb4: { +- bb6: { + bb3: { + _0 = const 2_u32; +- goto -> bb8; ++ goto -> bb5; + } + +- bb7: { ++ bb4: { + _0 = const 3_u32; +- goto -> bb8; ++ goto -> bb5; + } + +- bb8: { ++ bb5: { StorageDead(_3); return; + } + +- bb9: { ++ bb6: { + unreachable; + } + -+ bb4: { -+ StorageDead(_11); -+ switchInt(_7) -> [1: bb2, otherwise: bb1]; ++ bb7: { ++ StorageDead(_13); ++ switchInt(_9) -> [0: bb2, 1: bb3, 2: bb4, otherwise: bb6]; } } diff --git a/tests/mir-opt/early_otherwise_branch.opt4.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch.opt4.EarlyOtherwiseBranch.diff new file mode 100644 index 00000000000..18dea56f430 --- /dev/null +++ b/tests/mir-opt/early_otherwise_branch.opt4.EarlyOtherwiseBranch.diff @@ -0,0 +1,107 @@ +- // MIR for `opt4` before EarlyOtherwiseBranch ++ // MIR for `opt4` after EarlyOtherwiseBranch + + fn opt4(_1: Option2<u32>, _2: Option2<u32>) -> u32 { + debug x => _1; + debug y => _2; + let mut _0: u32; + let mut _3: (Option2<u32>, Option2<u32>); + let mut _4: Option2<u32>; + let mut _5: Option2<u32>; + let mut _6: isize; + let mut _7: isize; + let mut _8: isize; + let mut _9: isize; + let _10: u32; + let _11: u32; ++ let mut _12: isize; ++ let mut _13: bool; + scope 1 { + debug a => _10; + debug b => _11; + } + + bb0: { + StorageLive(_3); + StorageLive(_4); + _4 = move _1; + StorageLive(_5); + _5 = move _2; + _3 = (move _4, move _5); + StorageDead(_5); + StorageDead(_4); + _9 = discriminant((_3.0: Option2<u32>)); +- switchInt(move _9) -> [0: bb2, 1: bb3, 2: bb4, otherwise: bb9]; ++ StorageLive(_12); ++ _12 = discriminant((_3.1: Option2<u32>)); ++ StorageLive(_13); ++ _13 = Ne(_9, move _12); ++ StorageDead(_12); ++ switchInt(move _13) -> [0: bb7, otherwise: bb1]; + } + + bb1: { ++ StorageDead(_13); + _0 = const 1_u32; +- goto -> bb8; ++ goto -> bb5; + } + + bb2: { +- _6 = discriminant((_3.1: Option2<u32>)); +- switchInt(move _6) -> [0: bb5, otherwise: bb1]; +- } +- +- bb3: { +- _7 = discriminant((_3.1: Option2<u32>)); +- switchInt(move _7) -> [1: bb6, otherwise: bb1]; +- } +- +- bb4: { +- _8 = discriminant((_3.1: Option2<u32>)); +- switchInt(move _8) -> [2: bb7, otherwise: bb1]; +- } +- +- bb5: { + StorageLive(_10); + _10 = (((_3.0: Option2<u32>) as Some).0: u32); + StorageLive(_11); + _11 = (((_3.1: Option2<u32>) as Some).0: u32); + _0 = const 0_u32; + StorageDead(_11); + StorageDead(_10); +- goto -> bb8; ++ goto -> bb5; + } + +- bb6: { ++ bb3: { + _0 = const 2_u32; +- goto -> bb8; ++ goto -> bb5; + } + +- bb7: { ++ bb4: { + _0 = const 3_u32; +- goto -> bb8; ++ goto -> bb5; + } + +- bb8: { ++ bb5: { + StorageDead(_3); + return; + } + +- bb9: { ++ bb6: { + unreachable; ++ } ++ ++ bb7: { ++ StorageDead(_13); ++ switchInt(_9) -> [0: bb2, 1: bb3, 2: bb4, otherwise: bb6]; + } + } + diff --git a/tests/mir-opt/early_otherwise_branch.rs b/tests/mir-opt/early_otherwise_branch.rs index c984c271ccd..bfeb1f7bbc6 100644 --- a/tests/mir-opt/early_otherwise_branch.rs +++ b/tests/mir-opt/early_otherwise_branch.rs @@ -1,27 +1,79 @@ -// skip-filecheck //@ unit-test: EarlyOtherwiseBranch +//@ compile-flags: -Zmir-enable-passes=+UnreachableEnumBranching + +enum Option2<T> { + Some(T), + None, + Other, +} + +// We can't optimize it because y may be an invalid value. // EMIT_MIR early_otherwise_branch.opt1.EarlyOtherwiseBranch.diff fn opt1(x: Option<u32>, y: Option<u32>) -> u32 { + // CHECK-LABEL: fn opt1( + // CHECK: bb0: { + // CHECK: [[LOCAL1:_.*]] = discriminant({{.*}}); + // CHECK-NOT: Ne + // CHECK-NOT: discriminant + // CHECK: switchInt(move [[LOCAL1]]) -> [ + // CHECK-NEXT: } match (x, y) { (Some(a), Some(b)) => 0, _ => 1, } } +// FIXME: `switchInt` will have three targets after `UnreachableEnumBranching`, +// otherwise is unreachable. We can consume the UB fact to transform back to if else pattern. // EMIT_MIR early_otherwise_branch.opt2.EarlyOtherwiseBranch.diff fn opt2(x: Option<u32>, y: Option<u32>) -> u32 { + // CHECK-LABEL: fn opt2( + // CHECK: bb0: { + // CHECK: [[LOCAL1:_.*]] = discriminant({{.*}}); + // CHECK-NOT: Ne + // CHECK-NOT: discriminant + // CHECK: switchInt(move [[LOCAL1]]) -> [ + // CHECK-NEXT: } match (x, y) { (Some(a), Some(b)) => 0, - (None, None) => 0, + (None, None) => 2, _ => 1, } } // optimize despite different types // EMIT_MIR early_otherwise_branch.opt3.EarlyOtherwiseBranch.diff -fn opt3(x: Option<u32>, y: Option<bool>) -> u32 { +fn opt3(x: Option2<u32>, y: Option2<bool>) -> u32 { + // CHECK-LABEL: fn opt3( + // CHECK: let mut [[CMP_LOCAL:_.*]]: bool; + // CHECK: bb0: { + // CHECK: [[LOCAL1:_.*]] = discriminant({{.*}}); + // CHECK: [[LOCAL2:_.*]] = discriminant({{.*}}); + // CHECK: [[CMP_LOCAL]] = Ne([[LOCAL1]], move [[LOCAL2]]); + // CHECK: switchInt(move [[CMP_LOCAL]]) -> [ + // CHECK-NEXT: } match (x, y) { - (Some(a), Some(b)) => 0, + (Option2::Some(a), Option2::Some(b)) => 0, + (Option2::None, Option2::None) => 2, + (Option2::Other, Option2::Other) => 3, + _ => 1, + } +} + +// EMIT_MIR early_otherwise_branch.opt4.EarlyOtherwiseBranch.diff +fn opt4(x: Option2<u32>, y: Option2<u32>) -> u32 { + // CHECK-LABEL: fn opt4( + // CHECK: let mut [[CMP_LOCAL:_.*]]: bool; + // CHECK: bb0: { + // CHECK: [[LOCAL1:_.*]] = discriminant({{.*}}); + // CHECK: [[LOCAL2:_.*]] = discriminant({{.*}}); + // CHECK: [[CMP_LOCAL]] = Ne([[LOCAL1]], move [[LOCAL2]]); + // CHECK: switchInt(move [[CMP_LOCAL]]) -> [ + // CHECK-NEXT: } + match (x, y) { + (Option2::Some(a), Option2::Some(b)) => 0, + (Option2::None, Option2::None) => 2, + (Option2::Other, Option2::Other) => 3, _ => 1, } } @@ -29,5 +81,6 @@ fn opt3(x: Option<u32>, y: Option<bool>) -> u32 { fn main() { opt1(None, Some(0)); opt2(None, Some(0)); - opt3(None, Some(false)); + opt3(Option2::None, Option2::Some(false)); + opt4(Option2::None, Option2::Some(0)); } diff --git a/tests/mir-opt/early_otherwise_branch_3_element_tuple.opt1.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch_3_element_tuple.opt1.EarlyOtherwiseBranch.diff index f98d68e6ffc..4c3c717b522 100644 --- a/tests/mir-opt/early_otherwise_branch_3_element_tuple.opt1.EarlyOtherwiseBranch.diff +++ b/tests/mir-opt/early_otherwise_branch_3_element_tuple.opt1.EarlyOtherwiseBranch.diff @@ -13,17 +13,15 @@ let mut _8: isize; let mut _9: isize; let mut _10: isize; - let _11: u32; - let _12: u32; + let mut _11: isize; + let mut _12: isize; let _13: u32; -+ let mut _14: isize; -+ let mut _15: bool; -+ let mut _16: isize; -+ let mut _17: bool; + let _14: u32; + let _15: u32; scope 1 { - debug a => _11; - debug b => _12; - debug c => _13; + debug a => _13; + debug b => _14; + debug c => _15; } bb0: { @@ -38,60 +36,61 @@ StorageDead(_7); StorageDead(_6); StorageDead(_5); - _10 = discriminant((_4.0: std::option::Option<u32>)); -- switchInt(move _10) -> [1: bb2, otherwise: bb1]; -+ StorageLive(_14); -+ _14 = discriminant((_4.1: std::option::Option<u32>)); -+ StorageLive(_15); -+ _15 = Ne(_10, move _14); -+ StorageDead(_14); -+ switchInt(move _15) -> [0: bb5, otherwise: bb1]; + _12 = discriminant((_4.0: std::option::Option<u32>)); + switchInt(move _12) -> [0: bb4, 1: bb2, otherwise: bb9]; } bb1: { -+ StorageDead(_17); -+ StorageDead(_15); _0 = const 1_u32; -- goto -> bb5; -+ goto -> bb4; + goto -> bb8; } bb2: { -- _9 = discriminant((_4.1: std::option::Option<u32>)); -- switchInt(move _9) -> [1: bb3, otherwise: bb1]; -- } -- -- bb3: { + _9 = discriminant((_4.1: std::option::Option<u32>)); + switchInt(move _9) -> [1: bb3, 0: bb1, otherwise: bb9]; + } + + bb3: { _8 = discriminant((_4.2: std::option::Option<u32>)); -- switchInt(move _8) -> [1: bb4, otherwise: bb1]; -+ switchInt(move _8) -> [1: bb3, otherwise: bb1]; + switchInt(move _8) -> [1: bb6, 0: bb1, otherwise: bb9]; + } + + bb4: { + _11 = discriminant((_4.1: std::option::Option<u32>)); + switchInt(move _11) -> [0: bb5, 1: bb1, otherwise: bb9]; + } + + bb5: { + _10 = discriminant((_4.2: std::option::Option<u32>)); + switchInt(move _10) -> [0: bb7, 1: bb1, otherwise: bb9]; } -- bb4: { -+ bb3: { - StorageLive(_11); - _11 = (((_4.0: std::option::Option<u32>) as Some).0: u32); - StorageLive(_12); - _12 = (((_4.1: std::option::Option<u32>) as Some).0: u32); + bb6: { StorageLive(_13); - _13 = (((_4.2: std::option::Option<u32>) as Some).0: u32); + _13 = (((_4.0: std::option::Option<u32>) as Some).0: u32); + StorageLive(_14); + _14 = (((_4.1: std::option::Option<u32>) as Some).0: u32); + StorageLive(_15); + _15 = (((_4.2: std::option::Option<u32>) as Some).0: u32); _0 = const 0_u32; + StorageDead(_15); + StorageDead(_14); StorageDead(_13); - StorageDead(_12); - StorageDead(_11); -- goto -> bb5; -+ goto -> bb4; + goto -> bb8; + } + + bb7: { + _0 = const 2_u32; + goto -> bb8; } -- bb5: { -+ bb4: { + bb8: { StorageDead(_4); return; -+ } -+ -+ bb5: { -+ StorageDead(_15); -+ switchInt(_10) -> [1: bb2, otherwise: bb1]; + } + + bb9: { + unreachable; } } diff --git a/tests/mir-opt/early_otherwise_branch_3_element_tuple.opt2.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch_3_element_tuple.opt2.EarlyOtherwiseBranch.diff new file mode 100644 index 00000000000..0ea7a10baaa --- /dev/null +++ b/tests/mir-opt/early_otherwise_branch_3_element_tuple.opt2.EarlyOtherwiseBranch.diff @@ -0,0 +1,141 @@ +- // MIR for `opt2` before EarlyOtherwiseBranch ++ // MIR for `opt2` after EarlyOtherwiseBranch + + fn opt2(_1: Option2<u32>, _2: Option2<u32>, _3: Option2<u32>) -> u32 { + debug x => _1; + debug y => _2; + debug z => _3; + let mut _0: u32; + let mut _4: (Option2<u32>, Option2<u32>, Option2<u32>); + let mut _5: Option2<u32>; + let mut _6: Option2<u32>; + let mut _7: Option2<u32>; + let mut _8: isize; + let mut _9: isize; + let mut _10: isize; + let mut _11: isize; + let mut _12: isize; + let mut _13: isize; + let mut _14: isize; + let _15: u32; + let _16: u32; + let _17: u32; ++ let mut _18: isize; ++ let mut _19: bool; + scope 1 { + debug a => _15; + debug b => _16; + debug c => _17; + } + + bb0: { + StorageLive(_4); + StorageLive(_5); + _5 = move _1; + StorageLive(_6); + _6 = move _2; + StorageLive(_7); + _7 = move _3; + _4 = (move _5, move _6, move _7); + StorageDead(_7); + StorageDead(_6); + StorageDead(_5); + _14 = discriminant((_4.0: Option2<u32>)); +- switchInt(move _14) -> [0: bb2, 1: bb4, 2: bb6, otherwise: bb12]; ++ StorageLive(_18); ++ _18 = discriminant((_4.1: Option2<u32>)); ++ StorageLive(_19); ++ _19 = Ne(_14, move _18); ++ StorageDead(_18); ++ switchInt(move _19) -> [0: bb10, otherwise: bb1]; + } + + bb1: { ++ StorageDead(_19); + _0 = const 1_u32; +- goto -> bb11; ++ goto -> bb8; + } + + bb2: { +- _9 = discriminant((_4.1: Option2<u32>)); +- switchInt(move _9) -> [0: bb3, otherwise: bb1]; +- } +- +- bb3: { + _8 = discriminant((_4.2: Option2<u32>)); +- switchInt(move _8) -> [0: bb8, otherwise: bb1]; ++ switchInt(move _8) -> [0: bb5, otherwise: bb1]; + } + +- bb4: { +- _11 = discriminant((_4.1: Option2<u32>)); +- switchInt(move _11) -> [1: bb5, otherwise: bb1]; +- } +- +- bb5: { ++ bb3: { + _10 = discriminant((_4.2: Option2<u32>)); +- switchInt(move _10) -> [1: bb9, otherwise: bb1]; ++ switchInt(move _10) -> [1: bb6, otherwise: bb1]; + } + +- bb6: { +- _13 = discriminant((_4.1: Option2<u32>)); +- switchInt(move _13) -> [2: bb7, otherwise: bb1]; +- } +- +- bb7: { ++ bb4: { + _12 = discriminant((_4.2: Option2<u32>)); +- switchInt(move _12) -> [2: bb10, otherwise: bb1]; ++ switchInt(move _12) -> [2: bb7, otherwise: bb1]; + } + +- bb8: { ++ bb5: { + StorageLive(_15); + _15 = (((_4.0: Option2<u32>) as Some).0: u32); + StorageLive(_16); + _16 = (((_4.1: Option2<u32>) as Some).0: u32); + StorageLive(_17); + _17 = (((_4.2: Option2<u32>) as Some).0: u32); + _0 = const 0_u32; + StorageDead(_17); + StorageDead(_16); + StorageDead(_15); +- goto -> bb11; ++ goto -> bb8; + } + +- bb9: { ++ bb6: { + _0 = const 2_u32; +- goto -> bb11; ++ goto -> bb8; + } + +- bb10: { ++ bb7: { + _0 = const 3_u32; +- goto -> bb11; ++ goto -> bb8; + } + +- bb11: { ++ bb8: { + StorageDead(_4); + return; + } + +- bb12: { ++ bb9: { + unreachable; ++ } ++ ++ bb10: { ++ StorageDead(_19); ++ switchInt(_14) -> [0: bb2, 1: bb3, 2: bb4, otherwise: bb9]; + } + } + diff --git a/tests/mir-opt/early_otherwise_branch_3_element_tuple.rs b/tests/mir-opt/early_otherwise_branch_3_element_tuple.rs index 32081347558..2d215621bbd 100644 --- a/tests/mir-opt/early_otherwise_branch_3_element_tuple.rs +++ b/tests/mir-opt/early_otherwise_branch_3_element_tuple.rs @@ -1,14 +1,49 @@ -// skip-filecheck //@ unit-test: EarlyOtherwiseBranch +//@ compile-flags: -Zmir-enable-passes=+UnreachableEnumBranching +enum Option2<T> { + Some(T), + None, + Other, +} + +// FIXME: `switchInt` will have three targets after `UnreachableEnumBranching`, +// otherwise is unreachable. We can consume the UB fact to transform back to if else pattern. // EMIT_MIR early_otherwise_branch_3_element_tuple.opt1.EarlyOtherwiseBranch.diff fn opt1(x: Option<u32>, y: Option<u32>, z: Option<u32>) -> u32 { + // CHECK-LABEL: fn opt1( + // CHECK: bb0: { + // CHECK: [[LOCAL1:_.*]] = discriminant({{.*}}); + // CHECK-NOT: Ne + // CHECK-NOT: discriminant + // CHECK: switchInt(move [[LOCAL1]]) -> [ + // CHECK-NEXT: } match (x, y, z) { (Some(a), Some(b), Some(c)) => 0, + (None, None, None) => 2, + _ => 1, + } +} + +// EMIT_MIR early_otherwise_branch_3_element_tuple.opt2.EarlyOtherwiseBranch.diff +fn opt2(x: Option2<u32>, y: Option2<u32>, z: Option2<u32>) -> u32 { + // CHECK-LABEL: fn opt2( + // CHECK: let mut [[CMP_LOCAL:_.*]]: bool; + // CHECK: bb0: { + // CHECK: [[LOCAL1:_.*]] = discriminant({{.*}}); + // CHECK: [[LOCAL2:_.*]] = discriminant({{.*}}); + // CHECK: [[CMP_LOCAL]] = Ne([[LOCAL1]], move [[LOCAL2]]); + // CHECK: switchInt(move [[CMP_LOCAL]]) -> [ + // CHECK-NEXT: } + match (x, y, z) { + (Option2::Some(a), Option2::Some(b), Option2::Some(c)) => 0, + (Option2::None, Option2::None, Option2::None) => 2, + (Option2::Other, Option2::Other, Option2::Other) => 3, _ => 1, } } fn main() { opt1(None, Some(0), None); + opt2(Option2::None, Option2::Some(0), Option2::None); } diff --git a/tests/mir-opt/early_otherwise_branch_68867.rs b/tests/mir-opt/early_otherwise_branch_68867.rs index 805d21533c5..59bc19ceecc 100644 --- a/tests/mir-opt/early_otherwise_branch_68867.rs +++ b/tests/mir-opt/early_otherwise_branch_68867.rs @@ -1,5 +1,5 @@ -// skip-filecheck //@ unit-test: EarlyOtherwiseBranch +//@ compile-flags: -Zmir-enable-passes=+UnreachableEnumBranching // FIXME: This test was broken by the derefer change. @@ -19,6 +19,13 @@ pub extern "C" fn try_sum( x: &ViewportPercentageLength, other: &ViewportPercentageLength, ) -> Result<ViewportPercentageLength, ()> { + // CHECK-LABEL: fn try_sum( + // CHECK: bb0: { + // CHECK: [[LOCAL1:_.*]] = discriminant({{.*}}); + // CHECK-NOT: Ne + // CHECK-NOT: discriminant + // CHECK: switchInt(move [[LOCAL1]]) -> [ + // CHECK-NEXT: } use self::ViewportPercentageLength::*; Ok(match (x, other) { (&Vw(one), &Vw(other)) => Vw(one + other), diff --git a/tests/mir-opt/early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.diff index a5b5659a31a..de12fe8f120 100644 --- a/tests/mir-opt/early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.diff +++ b/tests/mir-opt/early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.diff @@ -78,7 +78,7 @@ StorageDead(_5); _34 = deref_copy (_4.0: &ViewportPercentageLength); _11 = discriminant((*_34)); - switchInt(move _11) -> [0: bb2, 1: bb3, 2: bb4, 3: bb5, otherwise: bb1]; + switchInt(move _11) -> [0: bb2, 1: bb3, 2: bb4, 3: bb5, otherwise: bb12]; } bb1: { @@ -213,5 +213,9 @@ bb11: { return; } + + bb12: { + unreachable; + } } diff --git a/tests/mir-opt/early_otherwise_branch_noopt.rs b/tests/mir-opt/early_otherwise_branch_noopt.rs index 648089e2df1..6b48393e6b9 100644 --- a/tests/mir-opt/early_otherwise_branch_noopt.rs +++ b/tests/mir-opt/early_otherwise_branch_noopt.rs @@ -1,11 +1,18 @@ -// skip-filecheck //@ unit-test: EarlyOtherwiseBranch +//@ compile-flags: -Zmir-enable-passes=+UnreachableEnumBranching // must not optimize as it does not follow the pattern of // left and right hand side being the same variant // EMIT_MIR early_otherwise_branch_noopt.noopt1.EarlyOtherwiseBranch.diff fn noopt1(x: Option<u32>, y: Option<u32>) -> u32 { + // CHECK-LABEL: fn noopt1( + // CHECK: bb0: { + // CHECK: [[LOCAL1:_.*]] = discriminant({{.*}}); + // CHECK-NOT: Ne + // CHECK-NOT: discriminant + // CHECK: switchInt(move [[LOCAL1]]) -> [ + // CHECK-NEXT: } match (x, y) { (Some(a), Some(b)) => 0, (Some(a), None) => 1, diff --git a/tests/mir-opt/early_otherwise_branch_soundness.no_deref_ptr.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch_soundness.no_deref_ptr.EarlyOtherwiseBranch.diff index b24ff6ec74b..8eab59823f4 100644 --- a/tests/mir-opt/early_otherwise_branch_soundness.no_deref_ptr.EarlyOtherwiseBranch.diff +++ b/tests/mir-opt/early_otherwise_branch_soundness.no_deref_ptr.EarlyOtherwiseBranch.diff @@ -14,7 +14,7 @@ bb0: { _3 = discriminant(_1); - switchInt(move _3) -> [1: bb2, otherwise: bb1]; + switchInt(move _3) -> [1: bb2, 0: bb1, otherwise: bb6]; } bb1: { @@ -24,7 +24,7 @@ bb2: { _4 = discriminant((*_2)); - switchInt(move _4) -> [1: bb4, otherwise: bb3]; + switchInt(move _4) -> [1: bb4, 0: bb3, otherwise: bb6]; } bb3: { @@ -43,5 +43,9 @@ bb5: { return; } + + bb6: { + unreachable; + } } diff --git a/tests/mir-opt/early_otherwise_branch_soundness.no_downcast.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch_soundness.no_downcast.EarlyOtherwiseBranch.diff index c3ea975ce03..6a4c947b882 100644 --- a/tests/mir-opt/early_otherwise_branch_soundness.no_downcast.EarlyOtherwiseBranch.diff +++ b/tests/mir-opt/early_otherwise_branch_soundness.no_downcast.EarlyOtherwiseBranch.diff @@ -12,13 +12,13 @@ bb0: { _3 = discriminant((*_1)); - switchInt(move _3) -> [1: bb1, otherwise: bb3]; + switchInt(move _3) -> [1: bb1, 0: bb3, otherwise: bb5]; } bb1: { _4 = deref_copy (((*_1) as Some).0: &E<'_>); _2 = discriminant((*_4)); - switchInt(move _2) -> [1: bb2, otherwise: bb3]; + switchInt(move _2) -> [1: bb2, 0: bb3, otherwise: bb5]; } bb2: { @@ -34,5 +34,9 @@ bb4: { return; } + + bb5: { + unreachable; + } } diff --git a/tests/mir-opt/early_otherwise_branch_soundness.rs b/tests/mir-opt/early_otherwise_branch_soundness.rs index b4f5821c420..74a2af884c0 100644 --- a/tests/mir-opt/early_otherwise_branch_soundness.rs +++ b/tests/mir-opt/early_otherwise_branch_soundness.rs @@ -1,5 +1,5 @@ -// skip-filecheck //@ unit-test: EarlyOtherwiseBranch +//@ compile-flags: -Zmir-enable-passes=+UnreachableEnumBranching // Tests various cases that the `early_otherwise_branch` opt should *not* optimize @@ -11,12 +11,26 @@ enum E<'a> { // EMIT_MIR early_otherwise_branch_soundness.no_downcast.EarlyOtherwiseBranch.diff fn no_downcast(e: &E) -> u32 { + // CHECK-LABEL: fn no_downcast( + // CHECK: bb0: { + // CHECK: [[LOCAL1:_.*]] = discriminant({{.*}}); + // CHECK-NOT: Ne + // CHECK-NOT: discriminant + // CHECK: switchInt(move [[LOCAL1]]) -> [ + // CHECK-NEXT: } if let E::Some(E::Some(_)) = e { 1 } else { 2 } } // SAFETY: if `a` is `Some`, `b` must point to a valid, initialized value // EMIT_MIR early_otherwise_branch_soundness.no_deref_ptr.EarlyOtherwiseBranch.diff unsafe fn no_deref_ptr(a: Option<i32>, b: *const Option<i32>) -> i32 { + // CHECK-LABEL: fn no_deref_ptr( + // CHECK: bb0: { + // CHECK: [[LOCAL1:_.*]] = discriminant({{.*}}); + // CHECK-NOT: Ne + // CHECK-NOT: discriminant + // CHECK: switchInt(move [[LOCAL1]]) -> [ + // CHECK-NEXT: } match a { // `*b` being correct depends on `a == Some(_)` Some(_) => match *b { diff --git a/tests/mir-opt/issue_72181_1.main.built.after.mir b/tests/mir-opt/issue_72181_1.main.built.after.mir index ae0dc9a0b6a..e8ad5cd8d16 100644 --- a/tests/mir-opt/issue_72181_1.main.built.after.mir +++ b/tests/mir-opt/issue_72181_1.main.built.after.mir @@ -1,8 +1,8 @@ // MIR for `main` after built | User Type Annotations -| 0: user_ty: Canonical { value: Ty(Void), max_universe: U0, variables: [] }, span: $DIR/issue_72181_1.rs:17:12: 17:16, inferred_ty: Void -| 1: user_ty: Canonical { value: Ty(Void), max_universe: U0, variables: [] }, span: $DIR/issue_72181_1.rs:17:12: 17:16, inferred_ty: Void +| 0: user_ty: Canonical { value: Ty(Void), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_72181_1.rs:17:12: 17:16, inferred_ty: Void +| 1: user_ty: Canonical { value: Ty(Void), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_72181_1.rs:17:12: 17:16, inferred_ty: Void | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/issue_99325.main.built.after.32bit.mir b/tests/mir-opt/issue_99325.main.built.after.32bit.mir index c8039dc4735..0b0bac73e9f 100644 --- a/tests/mir-opt/issue_99325.main.built.after.32bit.mir +++ b/tests/mir-opt/issue_99325.main.built.after.32bit.mir @@ -1,8 +1,8 @@ // MIR for `main` after built | User Type Annotations -| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} -| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}), args: [] }: &'static [u8; 4_usize]], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}), args: [] }: &'static [u8; 4_usize]], user_self_ty: None }), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/issue_99325.main.built.after.64bit.mir b/tests/mir-opt/issue_99325.main.built.after.64bit.mir index c8039dc4735..0b0bac73e9f 100644 --- a/tests/mir-opt/issue_99325.main.built.after.64bit.mir +++ b/tests/mir-opt/issue_99325.main.built.after.64bit.mir @@ -1,8 +1,8 @@ // MIR for `main` after built | User Type Annotations -| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} -| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}), args: [] }: &'static [u8; 4_usize]], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}), args: [] }: &'static [u8; 4_usize]], user_self_ty: None }), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/matches_reduce_branches.match_i128_u128.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_i128_u128.MatchBranchSimplification.diff new file mode 100644 index 00000000000..31ce51dc6de --- /dev/null +++ b/tests/mir-opt/matches_reduce_branches.match_i128_u128.MatchBranchSimplification.diff @@ -0,0 +1,47 @@ +- // MIR for `match_i128_u128` before MatchBranchSimplification ++ // MIR for `match_i128_u128` after MatchBranchSimplification + + fn match_i128_u128(_1: EnumAi128) -> u128 { + debug i => _1; + let mut _0: u128; + let mut _2: i128; ++ let mut _3: i128; + + bb0: { + _2 = discriminant(_1); +- switchInt(move _2) -> [1: bb3, 2: bb4, 3: bb5, 340282366920938463463374607431768211455: bb2, otherwise: bb1]; +- } +- +- bb1: { +- unreachable; +- } +- +- bb2: { +- _0 = const core::num::<impl u128>::MAX; +- goto -> bb6; +- } +- +- bb3: { +- _0 = const 1_u128; +- goto -> bb6; +- } +- +- bb4: { +- _0 = const 2_u128; +- goto -> bb6; +- } +- +- bb5: { +- _0 = const 3_u128; +- goto -> bb6; +- } +- +- bb6: { ++ StorageLive(_3); ++ _3 = move _2; ++ _0 = _3 as u128 (IntToInt); ++ StorageDead(_3); + return; + } + } + diff --git a/tests/mir-opt/matches_reduce_branches.match_i16_i8.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_i16_i8.MatchBranchSimplification.diff new file mode 100644 index 00000000000..e1b537b1b71 --- /dev/null +++ b/tests/mir-opt/matches_reduce_branches.match_i16_i8.MatchBranchSimplification.diff @@ -0,0 +1,42 @@ +- // MIR for `match_i16_i8` before MatchBranchSimplification ++ // MIR for `match_i16_i8` after MatchBranchSimplification + + fn match_i16_i8(_1: EnumAi16) -> i8 { + debug i => _1; + let mut _0: i8; + let mut _2: i16; ++ let mut _3: i16; + + bb0: { + _2 = discriminant(_1); +- switchInt(move _2) -> [65535: bb3, 2: bb4, 65533: bb2, otherwise: bb1]; +- } +- +- bb1: { +- unreachable; +- } +- +- bb2: { +- _0 = const -3_i8; +- goto -> bb5; +- } +- +- bb3: { +- _0 = const -1_i8; +- goto -> bb5; +- } +- +- bb4: { +- _0 = const 2_i8; +- goto -> bb5; +- } +- +- bb5: { ++ StorageLive(_3); ++ _3 = move _2; ++ _0 = _3 as i8 (IntToInt); ++ StorageDead(_3); + return; + } + } + diff --git a/tests/mir-opt/matches_reduce_branches.match_i8_i16.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_i8_i16.MatchBranchSimplification.diff new file mode 100644 index 00000000000..cabc5a44cd8 --- /dev/null +++ b/tests/mir-opt/matches_reduce_branches.match_i8_i16.MatchBranchSimplification.diff @@ -0,0 +1,42 @@ +- // MIR for `match_i8_i16` before MatchBranchSimplification ++ // MIR for `match_i8_i16` after MatchBranchSimplification + + fn match_i8_i16(_1: EnumAi8) -> i16 { + debug i => _1; + let mut _0: i16; + let mut _2: i8; ++ let mut _3: i8; + + bb0: { + _2 = discriminant(_1); +- switchInt(move _2) -> [255: bb3, 2: bb4, 253: bb2, otherwise: bb1]; +- } +- +- bb1: { +- unreachable; +- } +- +- bb2: { +- _0 = const -3_i16; +- goto -> bb5; +- } +- +- bb3: { +- _0 = const -1_i16; +- goto -> bb5; +- } +- +- bb4: { +- _0 = const 2_i16; +- goto -> bb5; +- } +- +- bb5: { ++ StorageLive(_3); ++ _3 = move _2; ++ _0 = _3 as i16 (IntToInt); ++ StorageDead(_3); + return; + } + } + diff --git a/tests/mir-opt/matches_reduce_branches.match_i8_i16_failed.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_i8_i16_failed.MatchBranchSimplification.diff new file mode 100644 index 00000000000..b0217792294 --- /dev/null +++ b/tests/mir-opt/matches_reduce_branches.match_i8_i16_failed.MatchBranchSimplification.diff @@ -0,0 +1,37 @@ +- // MIR for `match_i8_i16_failed` before MatchBranchSimplification ++ // MIR for `match_i8_i16_failed` after MatchBranchSimplification + + fn match_i8_i16_failed(_1: EnumAi8) -> i16 { + debug i => _1; + let mut _0: i16; + let mut _2: i8; + + bb0: { + _2 = discriminant(_1); + switchInt(move _2) -> [255: bb3, 2: bb4, 253: bb2, otherwise: bb1]; + } + + bb1: { + unreachable; + } + + bb2: { + _0 = const 3_i16; + goto -> bb5; + } + + bb3: { + _0 = const -1_i16; + goto -> bb5; + } + + bb4: { + _0 = const 2_i16; + goto -> bb5; + } + + bb5: { + return; + } + } + diff --git a/tests/mir-opt/matches_reduce_branches.match_u8_i16.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_u8_i16.MatchBranchSimplification.diff new file mode 100644 index 00000000000..9ee01a87a91 --- /dev/null +++ b/tests/mir-opt/matches_reduce_branches.match_u8_i16.MatchBranchSimplification.diff @@ -0,0 +1,37 @@ +- // MIR for `match_u8_i16` before MatchBranchSimplification ++ // MIR for `match_u8_i16` after MatchBranchSimplification + + fn match_u8_i16(_1: EnumAu8) -> i16 { + debug i => _1; + let mut _0: i16; + let mut _2: u8; ++ let mut _3: u8; + + bb0: { + _2 = discriminant(_1); +- switchInt(move _2) -> [1: bb3, 2: bb2, otherwise: bb1]; +- } +- +- bb1: { +- unreachable; +- } +- +- bb2: { +- _0 = const 2_i16; +- goto -> bb4; +- } +- +- bb3: { +- _0 = const 1_i16; +- goto -> bb4; +- } +- +- bb4: { ++ StorageLive(_3); ++ _3 = move _2; ++ _0 = _3 as i16 (IntToInt); ++ StorageDead(_3); + return; + } + } + diff --git a/tests/mir-opt/matches_reduce_branches.match_u8_i16_2.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_u8_i16_2.MatchBranchSimplification.diff new file mode 100644 index 00000000000..3333cd765a8 --- /dev/null +++ b/tests/mir-opt/matches_reduce_branches.match_u8_i16_2.MatchBranchSimplification.diff @@ -0,0 +1,26 @@ +- // MIR for `match_u8_i16_2` before MatchBranchSimplification ++ // MIR for `match_u8_i16_2` after MatchBranchSimplification + + fn match_u8_i16_2(_1: EnumAu8) -> i16 { + let mut _0: i16; + let mut _2: u8; + + bb0: { + _2 = discriminant(_1); + switchInt(_2) -> [1: bb3, 2: bb1, otherwise: bb2]; + } + + bb1: { + _0 = const 2_i16; + goto -> bb3; + } + + bb2: { + unreachable; + } + + bb3: { + return; + } + } + diff --git a/tests/mir-opt/matches_reduce_branches.match_u8_i16_failed.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_u8_i16_failed.MatchBranchSimplification.diff new file mode 100644 index 00000000000..6da19e46dab --- /dev/null +++ b/tests/mir-opt/matches_reduce_branches.match_u8_i16_failed.MatchBranchSimplification.diff @@ -0,0 +1,32 @@ +- // MIR for `match_u8_i16_failed` before MatchBranchSimplification ++ // MIR for `match_u8_i16_failed` after MatchBranchSimplification + + fn match_u8_i16_failed(_1: EnumAu8) -> i16 { + debug i => _1; + let mut _0: i16; + let mut _2: u8; + + bb0: { + _2 = discriminant(_1); + switchInt(move _2) -> [1: bb3, 2: bb2, otherwise: bb1]; + } + + bb1: { + unreachable; + } + + bb2: { + _0 = const 3_i16; + goto -> bb4; + } + + bb3: { + _0 = const 1_i16; + goto -> bb4; + } + + bb4: { + return; + } + } + diff --git a/tests/mir-opt/matches_reduce_branches.match_u8_i16_fallback.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_u8_i16_fallback.MatchBranchSimplification.diff new file mode 100644 index 00000000000..8fa497fe890 --- /dev/null +++ b/tests/mir-opt/matches_reduce_branches.match_u8_i16_fallback.MatchBranchSimplification.diff @@ -0,0 +1,31 @@ +- // MIR for `match_u8_i16_fallback` before MatchBranchSimplification ++ // MIR for `match_u8_i16_fallback` after MatchBranchSimplification + + fn match_u8_i16_fallback(_1: u8) -> i16 { + debug i => _1; + let mut _0: i16; + + bb0: { + switchInt(_1) -> [1: bb2, 2: bb3, otherwise: bb1]; + } + + bb1: { + _0 = const 3_i16; + goto -> bb4; + } + + bb2: { + _0 = const 1_i16; + goto -> bb4; + } + + bb3: { + _0 = const 2_i16; + goto -> bb4; + } + + bb4: { + return; + } + } + diff --git a/tests/mir-opt/matches_reduce_branches.match_u8_u16.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_u8_u16.MatchBranchSimplification.diff new file mode 100644 index 00000000000..aa9fcc60a3e --- /dev/null +++ b/tests/mir-opt/matches_reduce_branches.match_u8_u16.MatchBranchSimplification.diff @@ -0,0 +1,42 @@ +- // MIR for `match_u8_u16` before MatchBranchSimplification ++ // MIR for `match_u8_u16` after MatchBranchSimplification + + fn match_u8_u16(_1: EnumBu8) -> u16 { + debug i => _1; + let mut _0: u16; + let mut _2: u8; ++ let mut _3: u8; + + bb0: { + _2 = discriminant(_1); +- switchInt(move _2) -> [1: bb3, 2: bb4, 5: bb2, otherwise: bb1]; +- } +- +- bb1: { +- unreachable; +- } +- +- bb2: { +- _0 = const 5_u16; +- goto -> bb5; +- } +- +- bb3: { +- _0 = const 1_u16; +- goto -> bb5; +- } +- +- bb4: { +- _0 = const 2_u16; +- goto -> bb5; +- } +- +- bb5: { ++ StorageLive(_3); ++ _3 = move _2; ++ _0 = _3 as u16 (IntToInt); ++ StorageDead(_3); + return; + } + } + diff --git a/tests/mir-opt/matches_reduce_branches.match_u8_u16_2.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_u8_u16_2.MatchBranchSimplification.diff new file mode 100644 index 00000000000..b47de6a52b7 --- /dev/null +++ b/tests/mir-opt/matches_reduce_branches.match_u8_u16_2.MatchBranchSimplification.diff @@ -0,0 +1,37 @@ +- // MIR for `match_u8_u16_2` before MatchBranchSimplification ++ // MIR for `match_u8_u16_2` after MatchBranchSimplification + + fn match_u8_u16_2(_1: EnumBu8) -> i16 { + let mut _0: i16; + let mut _2: u8; + + bb0: { + _2 = discriminant(_1); + switchInt(_2) -> [1: bb1, 2: bb2, 5: bb3, otherwise: bb4]; + } + + bb1: { + _0 = const 1_i16; + goto -> bb5; + } + + bb2: { + _0 = const 2_i16; + goto -> bb5; + } + + bb3: { + _0 = const 5_i16; + _0 = const 5_i16; + goto -> bb5; + } + + bb4: { + unreachable; + } + + bb5: { + return; + } + } + diff --git a/tests/mir-opt/matches_reduce_branches.rs b/tests/mir-opt/matches_reduce_branches.rs index 4bf14e5a7bd..ca3e5f747d1 100644 --- a/tests/mir-opt/matches_reduce_branches.rs +++ b/tests/mir-opt/matches_reduce_branches.rs @@ -1,18 +1,28 @@ -// skip-filecheck //@ unit-test: MatchBranchSimplification +#![feature(repr128)] +#![feature(core_intrinsics)] +#![feature(custom_mir)] -// EMIT_MIR matches_reduce_branches.foo.MatchBranchSimplification.diff -// EMIT_MIR matches_reduce_branches.bar.MatchBranchSimplification.diff -// EMIT_MIR matches_reduce_branches.match_nested_if.MatchBranchSimplification.diff +use std::intrinsics::mir::*; +// EMIT_MIR matches_reduce_branches.foo.MatchBranchSimplification.diff fn foo(bar: Option<()>) { + // CHECK-LABEL: fn foo( + // CHECK: = Eq( + // CHECK: switchInt + // CHECK-NOT: switchInt if matches!(bar, None) { () } } +// EMIT_MIR matches_reduce_branches.bar.MatchBranchSimplification.diff fn bar(i: i32) -> (bool, bool, bool, bool) { + // CHECK-LABEL: fn bar( + // CHECK: = Ne( + // CHECK: = Eq( + // CHECK-NOT: switchInt let a; let b; let c; @@ -38,7 +48,10 @@ fn bar(i: i32) -> (bool, bool, bool, bool) { (a, b, c, d) } +// EMIT_MIR matches_reduce_branches.match_nested_if.MatchBranchSimplification.diff fn match_nested_if() -> bool { + // CHECK-LABEL: fn match_nested_if( + // CHECK-NOT: switchInt let val = match () { () if if if if true { true } else { false } { true } else { false } { true @@ -53,9 +66,221 @@ fn match_nested_if() -> bool { val } +#[repr(u8)] +enum EnumAu8 { + A = 1, + B = 2, +} + +// EMIT_MIR matches_reduce_branches.match_u8_i16.MatchBranchSimplification.diff +fn match_u8_i16(i: EnumAu8) -> i16 { + // CHECK-LABEL: fn match_u8_i16( + // CHECK-NOT: switchInt + // CHECK: _0 = _3 as i16 (IntToInt); + // CHECH: return + match i { + EnumAu8::A => 1, + EnumAu8::B => 2, + } +} + +// EMIT_MIR matches_reduce_branches.match_u8_i16_2.MatchBranchSimplification.diff +// Check for different instruction lengths +#[custom_mir(dialect = "built")] +fn match_u8_i16_2(i: EnumAu8) -> i16 { + // CHECK-LABEL: fn match_u8_i16_2( + // CHECK: switchInt + mir!( + { + let a = Discriminant(i); + match a { + 1 => bb1, + 2 => bb2, + _ => unreachable_bb, + } + } + bb1 = { + Goto(ret) + } + bb2 = { + RET = 2; + Goto(ret) + } + unreachable_bb = { + Unreachable() + } + ret = { + Return() + } + ) +} + +// EMIT_MIR matches_reduce_branches.match_u8_i16_failed.MatchBranchSimplification.diff +fn match_u8_i16_failed(i: EnumAu8) -> i16 { + // CHECK-LABEL: fn match_u8_i16_failed( + // CHECK: switchInt + match i { + EnumAu8::A => 1, + EnumAu8::B => 3, + } +} + +// EMIT_MIR matches_reduce_branches.match_u8_i16_fallback.MatchBranchSimplification.diff +fn match_u8_i16_fallback(i: u8) -> i16 { + // CHECK-LABEL: fn match_u8_i16_fallback( + // CHECK: switchInt + match i { + 1 => 1, + 2 => 2, + _ => 3, + } +} + +#[repr(u8)] +enum EnumBu8 { + A = 1, + B = 2, + C = 5, +} + +// EMIT_MIR matches_reduce_branches.match_u8_u16.MatchBranchSimplification.diff +fn match_u8_u16(i: EnumBu8) -> u16 { + // CHECK-LABEL: fn match_u8_u16( + // CHECK-NOT: switchInt + // CHECK: _0 = _3 as u16 (IntToInt); + // CHECH: return + match i { + EnumBu8::A => 1, + EnumBu8::B => 2, + EnumBu8::C => 5, + } +} + +// EMIT_MIR matches_reduce_branches.match_u8_u16_2.MatchBranchSimplification.diff +// Check for different instruction lengths +#[custom_mir(dialect = "built")] +fn match_u8_u16_2(i: EnumBu8) -> i16 { + // CHECK-LABEL: fn match_u8_u16_2( + // CHECK: switchInt + mir!( + { + let a = Discriminant(i); + match a { + 1 => bb1, + 2 => bb2, + 5 => bb5, + _ => unreachable_bb, + } + } + bb1 = { + RET = 1; + Goto(ret) + } + bb2 = { + RET = 2; + Goto(ret) + } + bb5 = { + RET = 5; + RET = 5; + Goto(ret) + } + unreachable_bb = { + Unreachable() + } + ret = { + Return() + } + ) +} + +#[repr(i8)] +enum EnumAi8 { + A = -1, + B = 2, + C = -3, +} + +// EMIT_MIR matches_reduce_branches.match_i8_i16.MatchBranchSimplification.diff +fn match_i8_i16(i: EnumAi8) -> i16 { + // CHECK-LABEL: fn match_i8_i16( + // CHECK-NOT: switchInt + // CHECK: _0 = _3 as i16 (IntToInt); + // CHECH: return + match i { + EnumAi8::A => -1, + EnumAi8::B => 2, + EnumAi8::C => -3, + } +} + +// EMIT_MIR matches_reduce_branches.match_i8_i16_failed.MatchBranchSimplification.diff +fn match_i8_i16_failed(i: EnumAi8) -> i16 { + // CHECK-LABEL: fn match_i8_i16_failed( + // CHECK: switchInt + match i { + EnumAi8::A => -1, + EnumAi8::B => 2, + EnumAi8::C => 3, + } +} + +#[repr(i16)] +enum EnumAi16 { + A = -1, + B = 2, + C = -3, +} + +// EMIT_MIR matches_reduce_branches.match_i16_i8.MatchBranchSimplification.diff +fn match_i16_i8(i: EnumAi16) -> i8 { + // CHECK-LABEL: fn match_i16_i8( + // CHECK-NOT: switchInt + // CHECK: _0 = _3 as i8 (IntToInt); + // CHECH: return + match i { + EnumAi16::A => -1, + EnumAi16::B => 2, + EnumAi16::C => -3, + } +} + +#[repr(i128)] +enum EnumAi128 { + A = 1, + B = 2, + C = 3, + D = -1, +} + +// EMIT_MIR matches_reduce_branches.match_i128_u128.MatchBranchSimplification.diff +fn match_i128_u128(i: EnumAi128) -> u128 { + // CHECK-LABEL: fn match_i128_u128( + // CHECK-NOT: switchInt + // CHECK: _0 = _3 as u128 (IntToInt); + // CHECH: return + match i { + EnumAi128::A => 1, + EnumAi128::B => 2, + EnumAi128::C => 3, + EnumAi128::D => u128::MAX, + } +} + fn main() { let _ = foo(None); let _ = foo(Some(())); let _ = bar(0); let _ = match_nested_if(); + let _ = match_u8_i16(EnumAu8::A); + let _ = match_u8_i16_2(EnumAu8::A); + let _ = match_u8_i16_failed(EnumAu8::A); + let _ = match_u8_i16_fallback(1); + let _ = match_u8_u16(EnumBu8::A); + let _ = match_u8_u16_2(EnumBu8::A); + let _ = match_i8_i16(EnumAi8::A); + let _ = match_i8_i16_failed(EnumAi8::A); + let _ = match_i8_i16(EnumAi8::A); + let _ = match_i16_i8(EnumAi16::A); + let _ = match_i128_u128(EnumAi128::A); } diff --git a/tests/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff b/tests/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff index 157f9c98353..11a18f58e3a 100644 --- a/tests/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff +++ b/tests/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff @@ -5,27 +5,32 @@ debug e => _1; let mut _0: u8; let mut _2: isize; ++ let mut _3: isize; bb0: { _2 = discriminant(_1); - switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1]; - } - - bb1: { - unreachable; - } - - bb2: { - _0 = const 1_u8; - goto -> bb4; - } - - bb3: { - _0 = const 0_u8; - goto -> bb4; - } - - bb4: { +- switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1]; +- } +- +- bb1: { +- unreachable; +- } +- +- bb2: { +- _0 = const 1_u8; +- goto -> bb4; +- } +- +- bb3: { +- _0 = const 0_u8; +- goto -> bb4; +- } +- +- bb4: { ++ StorageLive(_3); ++ _3 = move _2; ++ _0 = _3 as u8 (IntToInt); ++ StorageDead(_3); return; } } diff --git a/tests/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff b/tests/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff index 19083771fd9..809badc41ba 100644 --- a/tests/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff +++ b/tests/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff @@ -5,27 +5,32 @@ debug e => _1; let mut _0: i8; let mut _2: isize; ++ let mut _3: isize; bb0: { _2 = discriminant(_1); - switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1]; - } - - bb1: { - unreachable; - } - - bb2: { - _0 = const 1_i8; - goto -> bb4; - } - - bb3: { - _0 = const 0_i8; - goto -> bb4; - } - - bb4: { +- switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1]; +- } +- +- bb1: { +- unreachable; +- } +- +- bb2: { +- _0 = const 1_i8; +- goto -> bb4; +- } +- +- bb3: { +- _0 = const 0_i8; +- goto -> bb4; +- } +- +- bb4: { ++ StorageLive(_3); ++ _3 = move _2; ++ _0 = _3 as i8 (IntToInt); ++ StorageDead(_3); return; } } diff --git a/tests/run-make/artifact-incr-cache/lib.rs b/tests/run-make/artifact-incr-cache/lib.rs new file mode 100644 index 00000000000..fa4048594e3 --- /dev/null +++ b/tests/run-make/artifact-incr-cache/lib.rs @@ -0,0 +1,6 @@ +#![crate_name = "foo"] + +#[inline(never)] +pub fn add(a: u32, b: u32) -> u32 { + a + b +} diff --git a/tests/run-make/artifact-incr-cache/rmake.rs b/tests/run-make/artifact-incr-cache/rmake.rs new file mode 100644 index 00000000000..bb651368081 --- /dev/null +++ b/tests/run-make/artifact-incr-cache/rmake.rs @@ -0,0 +1,25 @@ +// rustc should be able to emit required files (asm, llvm-*, etc) during incremental +// compilation on the first pass by running the code gen as well as on subsequent runs - +// extracting them from the cache +// +// Fixes: rust-lang/rust#89149 +// Fixes: rust-lang/rust#88829 +// Also see discussion at +// <https://internals.rust-lang.org/t/interaction-between-incremental-compilation-and-emit/20551> + +extern crate run_make_support; + +use run_make_support::{rustc, tmp_dir}; + +fn main() { + let inc_dir = tmp_dir(); + + for _ in 0..=1 { + rustc() + .input("lib.rs") + .crate_type("lib") + .emit("obj,asm,dep-info,link,mir,llvm-ir,llvm-bc") + .incremental(&inc_dir) + .run(); + } +} diff --git a/tests/rustdoc-gui/docblock-code-block-line-number.goml b/tests/rustdoc-gui/docblock-code-block-line-number.goml index fc80932caba..348ce0c992f 100644 --- a/tests/rustdoc-gui/docblock-code-block-line-number.goml +++ b/tests/rustdoc-gui/docblock-code-block-line-number.goml @@ -2,23 +2,25 @@ include: "utils.goml" go-to: "file://" + |DOC_PATH| + "/test_docs/fn.foo.html" -// Otherwise, we can't check text color -show-text: true - // We check that without this setting, there is no line number displayed. assert-false: "pre.example-line-numbers" +// We set the setting to show the line numbers on code examples. +set-local-storage: {"rustdoc-line-numbers": "true"} +reload: +// We wait for the line numbers to be added into the DOM by the JS... +wait-for: "pre.example-line-numbers" + +// Otherwise, we can't check text color +show-text: true + // Let's now check some CSS properties... define-function: ( "check-colors", [theme, color], block { - // We now set the setting to show the line numbers on code examples. - set-local-storage: {"rustdoc-line-numbers": "true"} // Page will be reloaded in "switch-theme". call-function: ("switch-theme", {"theme": |theme|}) - // We wait for the line numbers to be added into the DOM by the JS... - wait-for: "pre.example-line-numbers" // If the test didn't fail, it means that it was found! assert-css: ( "pre.example-line-numbers", diff --git a/tests/rustdoc-gui/scrape-examples-toggle.goml b/tests/rustdoc-gui/scrape-examples-toggle.goml index a9d37048188..441895a7c0e 100644 --- a/tests/rustdoc-gui/scrape-examples-toggle.goml +++ b/tests/rustdoc-gui/scrape-examples-toggle.goml @@ -9,6 +9,7 @@ define-function: ( [theme, toggle_line_color, toggle_line_hover_color], block { call-function: ("switch-theme", {"theme": |theme|}) + reload: // Clicking "More examples..." will open additional examples assert-attribute-false: (".more-examples-toggle", {"open": ""}) @@ -21,6 +22,8 @@ define-function: ( ".toggle-line:hover .toggle-line-inner", {"background-color": |toggle_line_hover_color|}, ) + // We put the toggle in the original state. + click: ".more-examples-toggle" // Moving cursor away from the toggle line to prevent disrupting next test. move-cursor-to: ".search-input" }, diff --git a/tests/rustdoc-gui/search-result-color.goml b/tests/rustdoc-gui/search-result-color.goml index fd0b86af3ea..9825f92b453 100644 --- a/tests/rustdoc-gui/search-result-color.goml +++ b/tests/rustdoc-gui/search-result-color.goml @@ -210,24 +210,21 @@ call-function: ("check-search-color", { // Check the alias. go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -// If the text isn't displayed, the browser doesn't compute color style correctly... -show-text: true + +write-into: (".search-input", "thisisanalias") +// To be SURE that the search will be run. +press-key: 'Enter' +// Waiting for the search results to appear... +wait-for: "#search-tabs" define-function: ( "check-alias", [theme, alias, grey], block { call-function: ("switch-theme", {"theme": |theme|}) - write-into: (".search-input", "thisisanalias") - // To be SURE that the search will be run. - press-key: 'Enter' - // Waiting for the search results to appear... - wait-for: "#search-tabs" // Checking that the colors for the alias element are the ones expected. assert-css: (".result-name .path .alias", {"color": |alias|}) assert-css: (".result-name .path .alias > .grey", {"color": |grey|}) - // Leave the search results to prevent reloading with an already filled search input. - press-key: "Escape" }, ) diff --git a/tests/rustdoc-gui/settings.goml b/tests/rustdoc-gui/settings.goml index 56d0f8624e8..0011e44ca59 100644 --- a/tests/rustdoc-gui/settings.goml +++ b/tests/rustdoc-gui/settings.goml @@ -36,7 +36,12 @@ wait-for: "#alternative-display #search" assert: "#main-content.hidden" // Now let's check the content of the settings menu. -call-function: ("switch-theme", {"theme": "dark"}) +// If we are on the settings page, the menu doesn't work the same so we set +// the theme manually. +set-local-storage: {"rustdoc-theme": "dark", "rustdoc-use-system-theme": "false"} +// We reload the page so the local storage settings are being used. +reload: + click: "#settings-menu" wait-for: "#settings" diff --git a/tests/rustdoc-gui/utils.goml b/tests/rustdoc-gui/utils.goml index d9f8726ec53..844dc98a537 100644 --- a/tests/rustdoc-gui/utils.goml +++ b/tests/rustdoc-gui/utils.goml @@ -4,8 +4,15 @@ define-function: ( [theme], block { // Set the theme. - set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} - // We reload the page so the local storage settings are being used. - reload: + // Open the settings menu. + click: "#settings-menu" + // Wait for the popover to appear... + wait-for: "#settings" + // Change the setting. + click: "#theme-"+ |theme| + // Close the popover. + click: "#settings-menu" + // Ensure that the local storage was correctly updated. + assert-local-storage: {"rustdoc-theme": |theme|} }, ) diff --git a/tests/rustdoc/primitive/primitive.rs b/tests/rustdoc/primitive/primitive.rs index 32af2636c18..4b89fd9dfb7 100644 --- a/tests/rustdoc/primitive/primitive.rs +++ b/tests/rustdoc/primitive/primitive.rs @@ -1,6 +1,8 @@ #![crate_name = "foo"] #![feature(rustc_attrs)] +#![feature(f16)] +#![feature(f128)] // @has foo/index.html '//h2[@id="primitives"]' 'Primitive Types' // @has foo/index.html '//a[@href="primitive.i32.html"]' 'i32' @@ -13,9 +15,19 @@ // @!has foo/index.html '//span' '🔒' #[rustc_doc_primitive = "i32"] /// this is a test! -mod i32{} +mod i32 {} // @has foo/primitive.bool.html '//section[@id="main-content"]//div[@class="docblock"]//p' 'hello' #[rustc_doc_primitive = "bool"] /// hello mod bool {} + +// @has foo/primitive.f16.html '//section[@id="main-content"]//div[@class="docblock"]//p' 'hello' +#[rustc_doc_primitive = "f16"] +/// hello +mod f16 {} + +// @has foo/primitive.f128.html '//section[@id="main-content"]//div[@class="docblock"]//p' 'hello' +#[rustc_doc_primitive = "f128"] +/// hello +mod f128 {} diff --git a/tests/rustdoc/synthetic_auto/supertrait-bounds.rs b/tests/rustdoc/synthetic_auto/supertrait-bounds.rs new file mode 100644 index 00000000000..503e65d0f4f --- /dev/null +++ b/tests/rustdoc/synthetic_auto/supertrait-bounds.rs @@ -0,0 +1,14 @@ +// Check that we don't add bounds to synthetic auto trait impls that are +// already implied by the item (like supertrait bounds). + +// In this case we don't want to add the bounds `T: Copy` and `T: 'static` +// to the auto trait impl because they're implied by the bound `T: Bound` +// on the implementor `Type`. + +pub struct Type<T: Bound>(T); + +// @has supertrait_bounds/struct.Type.html +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \ +// "impl<T> Send for Type<T>where T: Send," + +pub trait Bound: Copy + 'static {} diff --git a/tests/ui-fulldeps/internal-lints/ty_tykind_usage.rs b/tests/ui-fulldeps/internal-lints/ty_tykind_usage.rs index 3152bf23ca5..f77b318039d 100644 --- a/tests/ui-fulldeps/internal-lints/ty_tykind_usage.rs +++ b/tests/ui-fulldeps/internal-lints/ty_tykind_usage.rs @@ -22,6 +22,7 @@ fn main() { TyKind::Foreign(..) => (), //~ ERROR usage of `ty::TyKind::<kind>` TyKind::Str => (), //~ ERROR usage of `ty::TyKind::<kind>` TyKind::Array(..) => (), //~ ERROR usage of `ty::TyKind::<kind>` + TyKind::Pat(..) => (), //~ ERROR usage of `ty::TyKind::<kind>` TyKind::Slice(..) => (), //~ ERROR usage of `ty::TyKind::<kind>` TyKind::RawPtr(..) => (), //~ ERROR usage of `ty::TyKind::<kind>` TyKind::Ref(..) => (), //~ ERROR usage of `ty::TyKind::<kind>` diff --git a/tests/ui-fulldeps/internal-lints/ty_tykind_usage.stderr b/tests/ui-fulldeps/internal-lints/ty_tykind_usage.stderr index 2ff5aad95dd..53bf5cb1a82 100644 --- a/tests/ui-fulldeps/internal-lints/ty_tykind_usage.stderr +++ b/tests/ui-fulldeps/internal-lints/ty_tykind_usage.stderr @@ -67,119 +67,125 @@ LL | TyKind::Array(..) => (), error: usage of `ty::TyKind::<kind>` --> $DIR/ty_tykind_usage.rs:25:9 | -LL | TyKind::Slice(..) => (), +LL | TyKind::Pat(..) => (), | ^^^^^^ help: try using `ty::<kind>` directly: `ty` error: usage of `ty::TyKind::<kind>` --> $DIR/ty_tykind_usage.rs:26:9 | -LL | TyKind::RawPtr(..) => (), +LL | TyKind::Slice(..) => (), | ^^^^^^ help: try using `ty::<kind>` directly: `ty` error: usage of `ty::TyKind::<kind>` --> $DIR/ty_tykind_usage.rs:27:9 | -LL | TyKind::Ref(..) => (), +LL | TyKind::RawPtr(..) => (), | ^^^^^^ help: try using `ty::<kind>` directly: `ty` error: usage of `ty::TyKind::<kind>` --> $DIR/ty_tykind_usage.rs:28:9 | -LL | TyKind::FnDef(..) => (), +LL | TyKind::Ref(..) => (), | ^^^^^^ help: try using `ty::<kind>` directly: `ty` error: usage of `ty::TyKind::<kind>` --> $DIR/ty_tykind_usage.rs:29:9 | -LL | TyKind::FnPtr(..) => (), +LL | TyKind::FnDef(..) => (), | ^^^^^^ help: try using `ty::<kind>` directly: `ty` error: usage of `ty::TyKind::<kind>` --> $DIR/ty_tykind_usage.rs:30:9 | -LL | TyKind::Dynamic(..) => (), +LL | TyKind::FnPtr(..) => (), | ^^^^^^ help: try using `ty::<kind>` directly: `ty` error: usage of `ty::TyKind::<kind>` --> $DIR/ty_tykind_usage.rs:31:9 | -LL | TyKind::Closure(..) => (), +LL | TyKind::Dynamic(..) => (), | ^^^^^^ help: try using `ty::<kind>` directly: `ty` error: usage of `ty::TyKind::<kind>` --> $DIR/ty_tykind_usage.rs:32:9 | -LL | TyKind::CoroutineClosure(..) => (), +LL | TyKind::Closure(..) => (), | ^^^^^^ help: try using `ty::<kind>` directly: `ty` error: usage of `ty::TyKind::<kind>` --> $DIR/ty_tykind_usage.rs:33:9 | -LL | TyKind::Coroutine(..) => (), +LL | TyKind::CoroutineClosure(..) => (), | ^^^^^^ help: try using `ty::<kind>` directly: `ty` error: usage of `ty::TyKind::<kind>` --> $DIR/ty_tykind_usage.rs:34:9 | -LL | TyKind::CoroutineWitness(..) => (), +LL | TyKind::Coroutine(..) => (), | ^^^^^^ help: try using `ty::<kind>` directly: `ty` error: usage of `ty::TyKind::<kind>` --> $DIR/ty_tykind_usage.rs:35:9 | -LL | TyKind::Never => (), +LL | TyKind::CoroutineWitness(..) => (), | ^^^^^^ help: try using `ty::<kind>` directly: `ty` error: usage of `ty::TyKind::<kind>` --> $DIR/ty_tykind_usage.rs:36:9 | -LL | TyKind::Tuple(..) => (), +LL | TyKind::Never => (), | ^^^^^^ help: try using `ty::<kind>` directly: `ty` error: usage of `ty::TyKind::<kind>` --> $DIR/ty_tykind_usage.rs:37:9 | -LL | TyKind::Alias(..) => (), +LL | TyKind::Tuple(..) => (), | ^^^^^^ help: try using `ty::<kind>` directly: `ty` error: usage of `ty::TyKind::<kind>` --> $DIR/ty_tykind_usage.rs:38:9 | -LL | TyKind::Param(..) => (), +LL | TyKind::Alias(..) => (), | ^^^^^^ help: try using `ty::<kind>` directly: `ty` error: usage of `ty::TyKind::<kind>` --> $DIR/ty_tykind_usage.rs:39:9 | -LL | TyKind::Bound(..) => (), +LL | TyKind::Param(..) => (), | ^^^^^^ help: try using `ty::<kind>` directly: `ty` error: usage of `ty::TyKind::<kind>` --> $DIR/ty_tykind_usage.rs:40:9 | -LL | TyKind::Placeholder(..) => (), +LL | TyKind::Bound(..) => (), | ^^^^^^ help: try using `ty::<kind>` directly: `ty` error: usage of `ty::TyKind::<kind>` --> $DIR/ty_tykind_usage.rs:41:9 | -LL | TyKind::Infer(..) => (), +LL | TyKind::Placeholder(..) => (), | ^^^^^^ help: try using `ty::<kind>` directly: `ty` error: usage of `ty::TyKind::<kind>` --> $DIR/ty_tykind_usage.rs:42:9 | +LL | TyKind::Infer(..) => (), + | ^^^^^^ help: try using `ty::<kind>` directly: `ty` + +error: usage of `ty::TyKind::<kind>` + --> $DIR/ty_tykind_usage.rs:43:9 + | LL | TyKind::Error(_) => (), | ^^^^^^ help: try using `ty::<kind>` directly: `ty` error: usage of `ty::TyKind::<kind>` - --> $DIR/ty_tykind_usage.rs:47:12 + --> $DIR/ty_tykind_usage.rs:48:12 | LL | if let TyKind::Int(int_ty) = kind {} | ^^^^^^ help: try using `ty::<kind>` directly: `ty` error: usage of `ty::TyKind` - --> $DIR/ty_tykind_usage.rs:49:24 + --> $DIR/ty_tykind_usage.rs:50:24 | LL | fn ty_kind(ty_bad: TyKind<'_>, ty_good: Ty<'_>) {} | ^^^^^^^^^^ @@ -187,7 +193,7 @@ LL | fn ty_kind(ty_bad: TyKind<'_>, ty_good: Ty<'_>) {} = help: try using `Ty` instead error: usage of `ty::TyKind` - --> $DIR/ty_tykind_usage.rs:51:37 + --> $DIR/ty_tykind_usage.rs:52:37 | LL | fn ir_ty_kind<I: Interner>(bad: IrTyKind<I>) -> IrTyKind<I> { | ^^^^^^^^^^^ @@ -195,7 +201,7 @@ LL | fn ir_ty_kind<I: Interner>(bad: IrTyKind<I>) -> IrTyKind<I> { = help: try using `Ty` instead error: usage of `ty::TyKind` - --> $DIR/ty_tykind_usage.rs:51:53 + --> $DIR/ty_tykind_usage.rs:52:53 | LL | fn ir_ty_kind<I: Interner>(bad: IrTyKind<I>) -> IrTyKind<I> { | ^^^^^^^^^^^ @@ -203,12 +209,12 @@ LL | fn ir_ty_kind<I: Interner>(bad: IrTyKind<I>) -> IrTyKind<I> { = help: try using `Ty` instead error: usage of `ty::TyKind::<kind>` - --> $DIR/ty_tykind_usage.rs:54:9 + --> $DIR/ty_tykind_usage.rs:55:9 | LL | IrTyKind::Bool | --------^^^^^^ | | | help: try using `ty::<kind>` directly: `ty` -error: aborting due to 33 previous errors +error: aborting due to 34 previous errors diff --git a/tests/ui-fulldeps/stable-mir/check_binop.rs b/tests/ui-fulldeps/stable-mir/check_binop.rs new file mode 100644 index 00000000000..3b52d88de3c --- /dev/null +++ b/tests/ui-fulldeps/stable-mir/check_binop.rs @@ -0,0 +1,147 @@ +//@ run-pass +//! Test information regarding binary operations. + +//@ ignore-stage1 +//@ ignore-cross-compile +//@ ignore-remote +//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 + +#![feature(rustc_private)] + +extern crate rustc_hir; +#[macro_use] +extern crate rustc_smir; +extern crate rustc_driver; +extern crate rustc_interface; +extern crate stable_mir; + +use rustc_smir::rustc_internal; +use stable_mir::mir::mono::Instance; +use stable_mir::mir::visit::{Location, MirVisitor}; +use stable_mir::mir::{LocalDecl, Rvalue, Statement, StatementKind, Terminator, TerminatorKind}; +use stable_mir::ty::{RigidTy, TyKind}; +use std::collections::HashSet; +use std::convert::TryFrom; +use std::io::Write; +use std::ops::ControlFlow; + +/// This function tests that we can correctly get type information from binary operations. +fn test_binops() -> ControlFlow<()> { + // Find items in the local crate. + let items = stable_mir::all_local_items(); + let mut instances = + items.into_iter().map(|item| Instance::try_from(item).unwrap()).collect::<Vec<_>>(); + while let Some(instance) = instances.pop() { + // The test below shouldn't have recursion in it. + let Some(body) = instance.body() else { + continue; + }; + let mut visitor = Visitor { locals: body.locals(), calls: Default::default() }; + visitor.visit_body(&body); + instances.extend(visitor.calls.into_iter()); + } + ControlFlow::Continue(()) +} + +struct Visitor<'a> { + locals: &'a [LocalDecl], + calls: HashSet<Instance>, +} + +impl<'a> MirVisitor for Visitor<'a> { + fn visit_statement(&mut self, stmt: &Statement, _loc: Location) { + match &stmt.kind { + StatementKind::Assign(place, Rvalue::BinaryOp(op, rhs, lhs)) => { + let ret_ty = place.ty(self.locals).unwrap(); + let op_ty = op.ty(rhs.ty(self.locals).unwrap(), lhs.ty(self.locals).unwrap()); + assert_eq!(ret_ty, op_ty, "Operation type should match the assigned place type"); + } + _ => {} + } + } + + fn visit_terminator(&mut self, term: &Terminator, _loc: Location) { + match &term.kind { + TerminatorKind::Call { func, .. } => { + let TyKind::RigidTy(RigidTy::FnDef(def, args)) = + func.ty(self.locals).unwrap().kind() + else { + return; + }; + self.calls.insert(Instance::resolve(def, &args).unwrap()); + } + _ => {} + } + } +} + +/// 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 = "binop_input.rs"; + generate_input(&path).unwrap(); + let args = vec!["rustc".to_string(), "--crate-type=lib".to_string(), path.to_string()]; + run!(args, test_binops).unwrap(); +} + +fn generate_input(path: &str) -> std::io::Result<()> { + let mut file = std::fs::File::create(path)?; + write!( + file, + r#" + macro_rules! binop_int {{ + ($fn:ident, $typ:ty) => {{ + pub fn $fn(lhs: $typ, rhs: $typ) {{ + let eq = lhs == rhs; + let lt = lhs < rhs; + let le = lhs <= rhs; + + let sum = lhs + rhs; + let mult = lhs * sum; + let shift = mult << 2; + let bit_or = shift | rhs; + let cmp = lhs.cmp(&bit_or); + + // Try to avoid the results above being pruned + std::hint::black_box(((eq, lt, le), cmp)); + }} + }} + }} + + binop_int!(binop_u8, u8); + binop_int!(binop_i64, i64); + + pub fn binop_bool(lhs: bool, rhs: bool) {{ + let eq = lhs == rhs; + let or = lhs | eq; + let lt = lhs < or; + let cmp = lhs.cmp(&rhs); + + // Try to avoid the results above being pruned + std::hint::black_box((lt, cmp)); + }} + + pub fn binop_char(lhs: char, rhs: char) {{ + let eq = lhs == rhs; + let lt = lhs < rhs; + let cmp = lhs.cmp(&rhs); + + // Try to avoid the results above being pruned + std::hint::black_box(([eq, lt], cmp)); + }} + + pub fn binop_ptr(lhs: *const char, rhs: *const char) {{ + let eq = lhs == rhs; + let lt = lhs < rhs; + let cmp = lhs.cmp(&rhs); + let off = unsafe {{ lhs.offset(2) }}; + + // Try to avoid the results above being pruned + std::hint::black_box(([eq, lt], cmp, off)); + }} + "# + )?; + Ok(()) +} diff --git a/tests/ui/asm/inline-syntax.rs b/tests/ui/asm/inline-syntax.rs index 6da1b89ed67..4a98d37aca0 100644 --- a/tests/ui/asm/inline-syntax.rs +++ b/tests/ui/asm/inline-syntax.rs @@ -16,7 +16,7 @@ #![feature(no_core, lang_items, rustc_attrs)] #![crate_type = "rlib"] #![no_core] -#![cfg_attr(x86_64_allowed, allow(bad_asm_style))] + #[rustc_builtin_macro] macro_rules! asm { diff --git a/tests/ui/async-await/async-closures/mut-ref-reborrow.rs b/tests/ui/async-await/async-closures/mut-ref-reborrow.rs new file mode 100644 index 00000000000..9f2cbd7ce1c --- /dev/null +++ b/tests/ui/async-await/async-closures/mut-ref-reborrow.rs @@ -0,0 +1,27 @@ +//@ aux-build:block-on.rs +//@ run-pass +//@ check-run-results +//@ revisions: e2021 e2018 +//@[e2018] edition:2018 +//@[e2021] edition:2021 + +#![feature(async_closure)] + +extern crate block_on; + +async fn call_once(f: impl async FnOnce()) { f().await; } + +pub async fn async_closure(x: &mut i32) { + let c = async move || { + *x += 1; + }; + call_once(c).await; +} + +fn main() { + block_on::block_on(async { + let mut x = 0; + async_closure(&mut x).await; + assert_eq!(x, 1); + }); +} diff --git a/tests/ui/async-await/async-closures/overlapping-projs.rs b/tests/ui/async-await/async-closures/overlapping-projs.rs new file mode 100644 index 00000000000..6dd00b16103 --- /dev/null +++ b/tests/ui/async-await/async-closures/overlapping-projs.rs @@ -0,0 +1,27 @@ +//@ aux-build:block-on.rs +//@ edition:2021 +//@ run-pass +//@ check-run-results + +#![feature(async_closure)] + +extern crate block_on; + +async fn call_once(f: impl async FnOnce()) { + f().await; +} + +async fn async_main() { + let x = &mut 0; + let y = &mut 0; + let c = async || { + *x = 1; + *y = 2; + }; + call_once(c).await; + println!("{x} {y}"); +} + +fn main() { + block_on::block_on(async_main()); +} diff --git a/tests/ui/async-await/async-closures/overlapping-projs.run.stdout b/tests/ui/async-await/async-closures/overlapping-projs.run.stdout new file mode 100644 index 00000000000..8d04f961a03 --- /dev/null +++ b/tests/ui/async-await/async-closures/overlapping-projs.run.stdout @@ -0,0 +1 @@ +1 2 diff --git a/tests/ui/async-await/async-closures/precise-captures.call.run.stdout b/tests/ui/async-await/async-closures/precise-captures.call.run.stdout new file mode 100644 index 00000000000..6062556837c --- /dev/null +++ b/tests/ui/async-await/async-closures/precise-captures.call.run.stdout @@ -0,0 +1,29 @@ +after call +after await +fixed +uncaptured + +after call +after await +fixed +uncaptured + +after call +after await +fixed +uncaptured + +after call +after await +fixed +untouched + +after call +drop first +after await +uncaptured + +after call +drop first +after await +uncaptured diff --git a/tests/ui/async-await/async-closures/precise-captures.call_once.run.stdout b/tests/ui/async-await/async-closures/precise-captures.call_once.run.stdout new file mode 100644 index 00000000000..ddb02d47600 --- /dev/null +++ b/tests/ui/async-await/async-closures/precise-captures.call_once.run.stdout @@ -0,0 +1,29 @@ +after call +after await +fixed +uncaptured + +after call +after await +fixed +uncaptured + +after call +fixed +after await +uncaptured + +after call +after await +fixed +untouched + +after call +drop first +after await +uncaptured + +after call +drop first +after await +uncaptured diff --git a/tests/ui/async-await/async-closures/precise-captures.force_once.run.stdout b/tests/ui/async-await/async-closures/precise-captures.force_once.run.stdout new file mode 100644 index 00000000000..ddb02d47600 --- /dev/null +++ b/tests/ui/async-await/async-closures/precise-captures.force_once.run.stdout @@ -0,0 +1,29 @@ +after call +after await +fixed +uncaptured + +after call +after await +fixed +uncaptured + +after call +fixed +after await +uncaptured + +after call +after await +fixed +untouched + +after call +drop first +after await +uncaptured + +after call +drop first +after await +uncaptured diff --git a/tests/ui/async-await/async-closures/precise-captures.rs b/tests/ui/async-await/async-closures/precise-captures.rs new file mode 100644 index 00000000000..e82dd1dbaf0 --- /dev/null +++ b/tests/ui/async-await/async-closures/precise-captures.rs @@ -0,0 +1,157 @@ +//@ aux-build:block-on.rs +//@ edition:2021 +//@ run-pass +//@ check-run-results +//@ revisions: call call_once force_once + +// call - Call the closure regularly. +// call_once - Call the closure w/ `async FnOnce`, so exercising the by_move shim. +// force_once - Force the closure mode to `FnOnce`, so exercising what was fixed +// in <https://github.com/rust-lang/rust/pull/123350>. + +#![feature(async_closure)] +#![allow(unused_mut)] + +extern crate block_on; + +#[cfg(any(call, force_once))] +macro_rules! call { + ($c:expr) => { ($c)() } +} + +#[cfg(call_once)] +async fn call_once(f: impl async FnOnce()) { + f().await +} + +#[cfg(call_once)] +macro_rules! call { + ($c:expr) => { call_once($c) } +} + +#[cfg(not(force_once))] +macro_rules! guidance { + ($c:expr) => { $c } +} + +#[cfg(force_once)] +fn infer_fnonce(c: impl async FnOnce()) -> impl async FnOnce() { c } + +#[cfg(force_once)] +macro_rules! guidance { + ($c:expr) => { infer_fnonce($c) } +} + +#[derive(Debug)] +struct Drop(&'static str); + +impl std::ops::Drop for Drop { + fn drop(&mut self) { + println!("{}", self.0); + } +} + +struct S { + a: i32, + b: Drop, + c: Drop, +} + +async fn async_main() { + // Precise capture struct + { + let mut s = S { a: 1, b: Drop("fix me up"), c: Drop("untouched") }; + let mut c = guidance!(async || { + s.a = 2; + let w = &mut s.b; + w.0 = "fixed"; + }); + s.c.0 = "uncaptured"; + let fut = call!(c); + println!("after call"); + fut.await; + println!("after await"); + } + println!(); + + // Precise capture &mut struct + { + let s = &mut S { a: 1, b: Drop("fix me up"), c: Drop("untouched") }; + let mut c = guidance!(async || { + s.a = 2; + let w = &mut s.b; + w.0 = "fixed"; + }); + s.c.0 = "uncaptured"; + let fut = call!(c); + println!("after call"); + fut.await; + println!("after await"); + } + println!(); + + // Precise capture struct by move + { + let mut s = S { a: 1, b: Drop("fix me up"), c: Drop("untouched") }; + let mut c = guidance!(async move || { + s.a = 2; + let w = &mut s.b; + w.0 = "fixed"; + }); + s.c.0 = "uncaptured"; + let fut = call!(c); + println!("after call"); + fut.await; + println!("after await"); + } + println!(); + + // Precise capture &mut struct by move + { + let s = &mut S { a: 1, b: Drop("fix me up"), c: Drop("untouched") }; + let mut c = guidance!(async move || { + s.a = 2; + let w = &mut s.b; + w.0 = "fixed"; + }); + // `s` is still captured fully as `&mut S`. + let fut = call!(c); + println!("after call"); + fut.await; + println!("after await"); + } + println!(); + + // Precise capture struct, consume field + { + let mut s = S { a: 1, b: Drop("drop first"), c: Drop("untouched") }; + let c = guidance!(async move || { + // s.a = 2; // FIXME(async_closures): Figure out why this fails + drop(s.b); + }); + s.c.0 = "uncaptured"; + let fut = call!(c); + println!("after call"); + fut.await; + println!("after await"); + } + println!(); + + // Precise capture struct by move, consume field + { + let mut s = S { a: 1, b: Drop("drop first"), c: Drop("untouched") }; + let c = guidance!(async move || { + // s.a = 2; // FIXME(async_closures): Figure out why this fails + drop(s.b); + }); + s.c.0 = "uncaptured"; + let fut = call!(c); + println!("after call"); + fut.await; + println!("after await"); + } +} + +fn main() { + block_on::block_on(async_main()); +} diff --git a/tests/ui/cfg/cfg-attr-cfg.rs b/tests/ui/cfg/cfg-attr-cfg.rs index 5b49966d544..67d97e760d7 100644 --- a/tests/ui/cfg/cfg-attr-cfg.rs +++ b/tests/ui/cfg/cfg-attr-cfg.rs @@ -4,5 +4,5 @@ //@ pretty-expanded FIXME #23616 -#[cfg_attr(foo, cfg(bar))] +#[cfg_attr(FALSE, cfg(bar))] fn main() { } diff --git a/tests/ui/cfg/cfg-attr-crate.rs b/tests/ui/cfg/cfg-attr-crate.rs index 7868b006e27..444704d132a 100644 --- a/tests/ui/cfg/cfg-attr-crate.rs +++ b/tests/ui/cfg/cfg-attr-crate.rs @@ -3,6 +3,6 @@ //@ pretty-expanded FIXME #23616 -#![cfg_attr(not_used, no_core)] +#![cfg_attr(FALSE, no_core)] fn main() { } diff --git a/tests/ui/cfg/cfg-macros-notfoo.rs b/tests/ui/cfg/cfg-macros-notfoo.rs index c47f4332aa3..9feb06be73e 100644 --- a/tests/ui/cfg/cfg-macros-notfoo.rs +++ b/tests/ui/cfg/cfg-macros-notfoo.rs @@ -1,11 +1,9 @@ //@ run-pass -//@ compile-flags: // check that cfg correctly chooses between the macro impls (see also // cfg-macros-foo.rs) - -#[cfg(foo)] +#[cfg(FALSE)] #[macro_use] mod foo { macro_rules! bar { @@ -13,7 +11,7 @@ mod foo { } } -#[cfg(not(foo))] +#[cfg(not(FALSE))] #[macro_use] mod foo { macro_rules! bar { diff --git a/tests/ui/cfg/cfg-match-arm.rs b/tests/ui/cfg/cfg-match-arm.rs index a41337a19a3..e646a63b8fb 100644 --- a/tests/ui/cfg/cfg-match-arm.rs +++ b/tests/ui/cfg/cfg-match-arm.rs @@ -10,9 +10,9 @@ enum Foo { fn foo(f: Foo) { match f { Foo::Bar => {}, - #[cfg(not(asdfa))] + #[cfg(not(FALSE))] Foo::Baz => {}, - #[cfg(afsd)] + #[cfg(FALSE)] Basdfwe => {} } } diff --git a/tests/ui/cfg/cfg-panic-abort.rs b/tests/ui/cfg/cfg-panic-abort.rs index 49adfd55c68..448fde21086 100644 --- a/tests/ui/cfg/cfg-panic-abort.rs +++ b/tests/ui/cfg/cfg-panic-abort.rs @@ -2,15 +2,11 @@ //@ compile-flags: -C panic=abort //@ no-prefer-dynamic - #[cfg(panic = "unwind")] pub fn bad() -> i32 { } #[cfg(not(panic = "abort"))] pub fn bad() -> i32 { } -#[cfg(panic = "some_imaginary_future_panic_handler")] -pub fn bad() -> i32 { } - #[cfg(panic = "abort")] pub fn main() { } diff --git a/tests/ui/cfg/cfg-panic.rs b/tests/ui/cfg/cfg-panic.rs index 0f1f539ebe3..4e3ed0cd9c2 100644 --- a/tests/ui/cfg/cfg-panic.rs +++ b/tests/ui/cfg/cfg-panic.rs @@ -2,15 +2,11 @@ //@ compile-flags: -C panic=unwind //@ needs-unwind - #[cfg(panic = "abort")] pub fn bad() -> i32 { } #[cfg(not(panic = "unwind"))] pub fn bad() -> i32 { } -#[cfg(panic = "some_imaginary_future_panic_handler")] -pub fn bad() -> i32 { } - #[cfg(panic = "unwind")] pub fn main() { } diff --git a/tests/ui/cfg/cfg_stmt_expr.rs b/tests/ui/cfg/cfg_stmt_expr.rs index 6de5eb5c4c6..9245f6d9757 100644 --- a/tests/ui/cfg/cfg_stmt_expr.rs +++ b/tests/ui/cfg/cfg_stmt_expr.rs @@ -7,49 +7,49 @@ fn main() { let a = 413; - #[cfg(unset)] + #[cfg(FALSE)] let a = (); assert_eq!(a, 413); let mut b = 612; - #[cfg(unset)] + #[cfg(FALSE)] { b = 1111; } assert_eq!(b, 612); - #[cfg(unset)] + #[cfg(FALSE)] undefined_fn(); - #[cfg(unset)] + #[cfg(FALSE)] undefined_macro!(); - #[cfg(unset)] + #[cfg(FALSE)] undefined_macro![]; - #[cfg(unset)] + #[cfg(FALSE)] undefined_macro!{}; // pretty printer bug... - // #[cfg(unset)] + // #[cfg(FALSE)] // undefined_macro!{} - let () = (#[cfg(unset)] 341,); // Should this also work on parens? - let t = (1, #[cfg(unset)] 3, 4); + let () = (#[cfg(FALSE)] 341,); // Should this also work on parens? + let t = (1, #[cfg(FALSE)] 3, 4); assert_eq!(t, (1, 4)); let f = |_: u32, _: u32| (); - f(2, 1, #[cfg(unset)] 6); + f(2, 1, #[cfg(FALSE)] 6); - let _: u32 = a.clone(#[cfg(unset)] undefined); + let _: u32 = a.clone(#[cfg(FALSE)] undefined); - let _: [(); 0] = [#[cfg(unset)] 126]; - let t = [#[cfg(unset)] 1, 2, 6]; + let _: [(); 0] = [#[cfg(FALSE)] 126]; + let t = [#[cfg(FALSE)] 1, 2, 6]; assert_eq!(t, [2, 6]); { let r; - #[cfg(unset)] + #[cfg(FALSE)] (r = 5); - #[cfg(not(unset))] + #[cfg(not(FALSE))] (r = 10); assert_eq!(r, 10); } @@ -69,13 +69,13 @@ fn main() { } } - let n = if_cfg!(unset? { + let n = if_cfg!(FALSE? { 413 } else { 612 }); - assert_eq!((#[cfg(unset)] 1, #[cfg(not(unset))] 2), (2,)); + assert_eq!((#[cfg(FALSE)] 1, #[cfg(not(FALSE))] 2), (2,)); assert_eq!(n, 612); // check that lints work diff --git a/tests/ui/cfg/conditional-compile.rs b/tests/ui/cfg/conditional-compile.rs index f39663adda2..a4f334dd696 100644 --- a/tests/ui/cfg/conditional-compile.rs +++ b/tests/ui/cfg/conditional-compile.rs @@ -6,31 +6,31 @@ // Crate use statements -#[cfg(bogus)] +#[cfg(FALSE)] use flippity; -#[cfg(bogus)] +#[cfg(FALSE)] static b: bool = false; static b: bool = true; mod rustrt { - #[cfg(bogus)] + #[cfg(FALSE)] extern "C" { // This symbol doesn't exist and would be a link error if this // module was codegened - pub fn bogus(); + pub fn FALSE(); } extern "C" {} } -#[cfg(bogus)] +#[cfg(FALSE)] type t = isize; type t = bool; -#[cfg(bogus)] +#[cfg(FALSE)] enum tg { foo, } @@ -39,12 +39,12 @@ enum tg { bar, } -#[cfg(bogus)] +#[cfg(FALSE)] struct r { i: isize, } -#[cfg(bogus)] +#[cfg(FALSE)] fn r(i: isize) -> r { r { i: i } } @@ -57,11 +57,11 @@ fn r(i: isize) -> r { r { i: i } } -#[cfg(bogus)] +#[cfg(FALSE)] mod m { // This needs to parse but would fail in typeck. Since it's not in // the current config it should not be typechecked. - pub fn bogus() { + pub fn FALSE() { return 0; } } @@ -69,22 +69,22 @@ mod m { mod m { // Submodules have slightly different code paths than the top-level // module, so let's make sure this jazz works here as well - #[cfg(bogus)] + #[cfg(FALSE)] pub fn f() {} pub fn f() {} } -// Since the bogus configuration isn't defined main will just be +// Since the FALSE configuration isn't defined main will just be // parsed, but nothing further will be done with it -#[cfg(bogus)] +#[cfg(FALSE)] pub fn main() { panic!() } pub fn main() { // Exercise some of the configured items in ways that wouldn't be possible - // if they had the bogus definition + // if they had the FALSE definition assert!((b)); let _x: t = true; let _y: tg = tg::bar; @@ -93,14 +93,14 @@ pub fn main() { } fn test_in_fn_ctxt() { - #[cfg(bogus)] + #[cfg(FALSE)] fn f() { panic!() } fn f() {} f(); - #[cfg(bogus)] + #[cfg(FALSE)] static i: isize = 0; static i: isize = 1; assert_eq!(i, 1); @@ -109,7 +109,7 @@ fn test_in_fn_ctxt() { mod test_foreign_items { pub mod rustrt { extern "C" { - #[cfg(bogus)] + #[cfg(FALSE)] pub fn write() -> String; pub fn write() -> String; } @@ -117,7 +117,7 @@ mod test_foreign_items { } mod test_use_statements { - #[cfg(bogus)] + #[cfg(FALSE)] use flippity_foo; } @@ -127,24 +127,24 @@ mod test_methods { } impl Fooable for Foo { - #[cfg(bogus)] + #[cfg(FALSE)] fn what(&self) {} fn what(&self) {} - #[cfg(bogus)] + #[cfg(FALSE)] fn the(&self) {} fn the(&self) {} } trait Fooable { - #[cfg(bogus)] + #[cfg(FALSE)] fn what(&self); fn what(&self); - #[cfg(bogus)] + #[cfg(FALSE)] fn the(&self); fn the(&self); diff --git a/tests/ui/cfg/diagnostics-reexport.rs b/tests/ui/cfg/diagnostics-reexport.rs index b9548e91183..9b3208cb87c 100644 --- a/tests/ui/cfg/diagnostics-reexport.rs +++ b/tests/ui/cfg/diagnostics-reexport.rs @@ -14,7 +14,7 @@ pub use a::x; //~| NOTE no `x` in `a` mod a { - #[cfg(no)] + #[cfg(FALSE)] pub fn x() {} //~^ NOTE found an item that was configured out } @@ -25,10 +25,10 @@ pub use b::{x, y}; //~| NOTE no `y` in `b` mod b { - #[cfg(no)] + #[cfg(FALSE)] pub fn x() {} //~^ NOTE found an item that was configured out - #[cfg(no)] + #[cfg(FALSE)] pub fn y() {} //~^ NOTE found an item that was configured out } diff --git a/tests/ui/compiletest-self-test/run-rustfix-revisions.foo.fixed b/tests/ui/compiletest-self-test/run-rustfix-revisions.foo.fixed new file mode 100644 index 00000000000..0fa6ac1dd99 --- /dev/null +++ b/tests/ui/compiletest-self-test/run-rustfix-revisions.foo.fixed @@ -0,0 +1,9 @@ +// Check that revisioned `run-rustfix` does not fail with issues related to `.` in crate name. +//@ revisions: foo +//@[foo] run-rustfix +#![deny(unused_variables)] + +fn main() { + let _x = 0usize; + //~^ ERROR unused variable: `x` +} diff --git a/tests/ui/compiletest-self-test/run-rustfix-revisions.foo.stderr b/tests/ui/compiletest-self-test/run-rustfix-revisions.foo.stderr new file mode 100644 index 00000000000..74384ef24af --- /dev/null +++ b/tests/ui/compiletest-self-test/run-rustfix-revisions.foo.stderr @@ -0,0 +1,14 @@ +error: unused variable: `x` + --> $DIR/run-rustfix-revisions.rs:7:9 + | +LL | let x = 0usize; + | ^ help: if this is intentional, prefix it with an underscore: `_x` + | +note: the lint level is defined here + --> $DIR/run-rustfix-revisions.rs:4:9 + | +LL | #![deny(unused_variables)] + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/compiletest-self-test/run-rustfix-revisions.rs b/tests/ui/compiletest-self-test/run-rustfix-revisions.rs new file mode 100644 index 00000000000..84c5b7a2d0a --- /dev/null +++ b/tests/ui/compiletest-self-test/run-rustfix-revisions.rs @@ -0,0 +1,9 @@ +// Check that revisioned `run-rustfix` does not fail with issues related to `.` in crate name. +//@ revisions: foo +//@[foo] run-rustfix +#![deny(unused_variables)] + +fn main() { + let x = 0usize; + //~^ ERROR unused variable: `x` +} diff --git a/tests/ui/conditional-compilation/cfg-attr-empty-is-unused.rs b/tests/ui/conditional-compilation/cfg-attr-empty-is-unused.rs index 72b0db5da84..bca29f0da2b 100644 --- a/tests/ui/conditional-compilation/cfg-attr-empty-is-unused.rs +++ b/tests/ui/conditional-compilation/cfg-attr-empty-is-unused.rs @@ -1,13 +1,11 @@ // Check that `#[cfg_attr($PREDICATE,)]` triggers the `unused_attribute` lint. -//@ compile-flags: --cfg TRUE - #![deny(unused)] #[cfg_attr(FALSE,)] //~ ERROR `#[cfg_attr]` does not expand to any attributes fn _f() {} -#[cfg_attr(TRUE,)] //~ ERROR `#[cfg_attr]` does not expand to any attributes +#[cfg_attr(not(FALSE),)] //~ ERROR `#[cfg_attr]` does not expand to any attributes fn _g() {} fn main() {} diff --git a/tests/ui/conditional-compilation/cfg-attr-empty-is-unused.stderr b/tests/ui/conditional-compilation/cfg-attr-empty-is-unused.stderr index 87b69881353..a858afe2f2b 100644 --- a/tests/ui/conditional-compilation/cfg-attr-empty-is-unused.stderr +++ b/tests/ui/conditional-compilation/cfg-attr-empty-is-unused.stderr @@ -1,21 +1,21 @@ error: `#[cfg_attr]` does not expand to any attributes - --> $DIR/cfg-attr-empty-is-unused.rs:7:1 + --> $DIR/cfg-attr-empty-is-unused.rs:5:1 | LL | #[cfg_attr(FALSE,)] | ^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/cfg-attr-empty-is-unused.rs:5:9 + --> $DIR/cfg-attr-empty-is-unused.rs:3:9 | LL | #![deny(unused)] | ^^^^^^ = note: `#[deny(unused_attributes)]` implied by `#[deny(unused)]` error: `#[cfg_attr]` does not expand to any attributes - --> $DIR/cfg-attr-empty-is-unused.rs:10:1 + --> $DIR/cfg-attr-empty-is-unused.rs:8:1 | -LL | #[cfg_attr(TRUE,)] - | ^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(not(FALSE),)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/conditional-compilation/cfg-in-crate-1.rs b/tests/ui/conditional-compilation/cfg-in-crate-1.rs index 59be27a065e..ecd3722bf4c 100644 --- a/tests/ui/conditional-compilation/cfg-in-crate-1.rs +++ b/tests/ui/conditional-compilation/cfg-in-crate-1.rs @@ -1,3 +1,3 @@ //@ error-pattern: `main` function not found -#![cfg(bar)] +#![cfg(FALSE)] diff --git a/tests/ui/conditional-compilation/cfg-in-crate-1.stderr b/tests/ui/conditional-compilation/cfg-in-crate-1.stderr index 98be6d01f1b..6067a3a921c 100644 --- a/tests/ui/conditional-compilation/cfg-in-crate-1.stderr +++ b/tests/ui/conditional-compilation/cfg-in-crate-1.stderr @@ -1,8 +1,8 @@ error[E0601]: `main` function not found in crate `cfg_in_crate_1` - --> $DIR/cfg-in-crate-1.rs:3:13 + --> $DIR/cfg-in-crate-1.rs:3:15 | -LL | #![cfg(bar)] - | ^ consider adding a `main` function to `$DIR/cfg-in-crate-1.rs` +LL | #![cfg(FALSE)] + | ^ consider adding a `main` function to `$DIR/cfg-in-crate-1.rs` error: aborting due to 1 previous error diff --git a/tests/ui/conditional-compilation/cfg-non-opt-expr.rs b/tests/ui/conditional-compilation/cfg-non-opt-expr.rs index 0ddbd8a156d..ae85f38e645 100644 --- a/tests/ui/conditional-compilation/cfg-non-opt-expr.rs +++ b/tests/ui/conditional-compilation/cfg-non-opt-expr.rs @@ -2,10 +2,10 @@ #![feature(custom_test_frameworks)] fn main() { - let _ = #[cfg(unset)] (); + let _ = #[cfg(FALSE)] (); //~^ ERROR removing an expression is not supported in this position - let _ = 1 + 2 + #[cfg(unset)] 3; + let _ = 1 + 2 + #[cfg(FALSE)] 3; //~^ ERROR removing an expression is not supported in this position - let _ = [1, 2, 3][#[cfg(unset)] 1]; + let _ = [1, 2, 3][#[cfg(FALSE)] 1]; //~^ ERROR removing an expression is not supported in this position } diff --git a/tests/ui/conditional-compilation/cfg-non-opt-expr.stderr b/tests/ui/conditional-compilation/cfg-non-opt-expr.stderr index 933b7dc184a..06eaa59efdd 100644 --- a/tests/ui/conditional-compilation/cfg-non-opt-expr.stderr +++ b/tests/ui/conditional-compilation/cfg-non-opt-expr.stderr @@ -1,19 +1,19 @@ error: removing an expression is not supported in this position --> $DIR/cfg-non-opt-expr.rs:5:13 | -LL | let _ = #[cfg(unset)] (); +LL | let _ = #[cfg(FALSE)] (); | ^^^^^^^^^^^^^ error: removing an expression is not supported in this position --> $DIR/cfg-non-opt-expr.rs:7:21 | -LL | let _ = 1 + 2 + #[cfg(unset)] 3; +LL | let _ = 1 + 2 + #[cfg(FALSE)] 3; | ^^^^^^^^^^^^^ error: removing an expression is not supported in this position --> $DIR/cfg-non-opt-expr.rs:9:23 | -LL | let _ = [1, 2, 3][#[cfg(unset)] 1]; +LL | let _ = [1, 2, 3][#[cfg(FALSE)] 1]; | ^^^^^^^^^^^^^ error: aborting due to 3 previous errors diff --git a/tests/ui/const-generics/generic_const_exprs/unknown-alias-defkind-anonconst-ice-116710.rs b/tests/ui/const-generics/generic_const_exprs/unknown-alias-defkind-anonconst-ice-116710.rs new file mode 100644 index 00000000000..dd0b1e8c9f7 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/unknown-alias-defkind-anonconst-ice-116710.rs @@ -0,0 +1,15 @@ +// ICE unknown alias DefKind: AnonConst +// issue: rust-lang/rust#116710 +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +struct A<const N: u32 = 1, const M: u32 = u8>; +//~^ ERROR expected value, found builtin type `u8` + +trait Trait {} +impl<const N: u32> Trait for A<N> {} + +impl<const N: u32> Trait for A<N> {} +//~^ ERROR conflicting implementations of trait `Trait` for type `A<_>` + +pub fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/unknown-alias-defkind-anonconst-ice-116710.stderr b/tests/ui/const-generics/generic_const_exprs/unknown-alias-defkind-anonconst-ice-116710.stderr new file mode 100644 index 00000000000..80ac96d4870 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/unknown-alias-defkind-anonconst-ice-116710.stderr @@ -0,0 +1,19 @@ +error[E0423]: expected value, found builtin type `u8` + --> $DIR/unknown-alias-defkind-anonconst-ice-116710.rs:6:43 + | +LL | struct A<const N: u32 = 1, const M: u32 = u8>; + | ^^ not a value + +error[E0119]: conflicting implementations of trait `Trait` for type `A<_>` + --> $DIR/unknown-alias-defkind-anonconst-ice-116710.rs:12:1 + | +LL | impl<const N: u32> Trait for A<N> {} + | --------------------------------- first implementation here +LL | +LL | impl<const N: u32> Trait for A<N> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `A<_>` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0119, E0423. +For more information about an error, try `rustc --explain E0119`. diff --git a/tests/ui/editions/edition-raw-pointer-method-2018.rs b/tests/ui/editions/edition-raw-pointer-method-2018.rs index b346953e187..ad47553d5b7 100644 --- a/tests/ui/editions/edition-raw-pointer-method-2018.rs +++ b/tests/ui/editions/edition-raw-pointer-method-2018.rs @@ -6,6 +6,6 @@ fn main() { let x = 0; let y = &x as *const _; + //~^ error: type annotations needed let _ = y.is_null(); - //~^ error: cannot call a method on a raw pointer with an unknown pointee type [E0699] } diff --git a/tests/ui/editions/edition-raw-pointer-method-2018.stderr b/tests/ui/editions/edition-raw-pointer-method-2018.stderr index 663843ad7bc..2792d1e7400 100644 --- a/tests/ui/editions/edition-raw-pointer-method-2018.stderr +++ b/tests/ui/editions/edition-raw-pointer-method-2018.stderr @@ -1,9 +1,17 @@ -error[E0699]: cannot call a method on a raw pointer with an unknown pointee type - --> $DIR/edition-raw-pointer-method-2018.rs:9:15 +error[E0282]: type annotations needed for `*const _` + --> $DIR/edition-raw-pointer-method-2018.rs:8:9 | +LL | let y = &x as *const _; + | ^ +LL | LL | let _ = y.is_null(); - | ^^^^^^^ + | ------- cannot call a method on a raw pointer with an unknown pointee type + | +help: consider giving `y` an explicit type, where the placeholders `_` are specified + | +LL | let y: *const _ = &x as *const _; + | ++++++++++ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0699`. +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/expr/if/attrs/cfg-false-if-attr.rs b/tests/ui/expr/if/attrs/cfg-false-if-attr.rs index 72d83215ade..c139b347d99 100644 --- a/tests/ui/expr/if/attrs/cfg-false-if-attr.rs +++ b/tests/ui/expr/if/attrs/cfg-false-if-attr.rs @@ -25,7 +25,7 @@ fn bar() { let x: () = true; // Should not error due to the #[cfg(FALSE)] } - #[cfg_attr(not(unset_attr), cfg(FALSE))] + #[cfg_attr(not(FALSE), cfg(FALSE))] if true { let a: () = true; // Should not error due to the applied #[cfg(FALSE)] } diff --git a/tests/ui/feature-gates/feature-gate-type_privacy_lints.rs b/tests/ui/feature-gates/feature-gate-type_privacy_lints.rs deleted file mode 100644 index c537fc419f6..00000000000 --- a/tests/ui/feature-gates/feature-gate-type_privacy_lints.rs +++ /dev/null @@ -1,4 +0,0 @@ -//@ check-pass - -#![warn(unnameable_types)] //~ WARN unknown lint -fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-type_privacy_lints.stderr b/tests/ui/feature-gates/feature-gate-type_privacy_lints.stderr deleted file mode 100644 index 72ac3792fff..00000000000 --- a/tests/ui/feature-gates/feature-gate-type_privacy_lints.stderr +++ /dev/null @@ -1,14 +0,0 @@ -warning: unknown lint: `unnameable_types` - --> $DIR/feature-gate-type_privacy_lints.rs:3:1 - | -LL | #![warn(unnameable_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: the `unnameable_types` lint is unstable - = note: see issue #48054 <https://github.com/rust-lang/rust/issues/48054> for more information - = help: add `#![feature(type_privacy_lints)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `#[warn(unknown_lints)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/filter-block-view-items.rs b/tests/ui/filter-block-view-items.rs index edb9ce38006..f582c51a3a6 100644 --- a/tests/ui/filter-block-view-items.rs +++ b/tests/ui/filter-block-view-items.rs @@ -4,5 +4,5 @@ pub fn main() { // Make sure that this view item is filtered out because otherwise it would // trigger a compilation error - #[cfg(not_present)] use bar as foo; + #[cfg(FALSE)] use bar as foo; } diff --git a/tests/ui/generic-const-items/assoc-const-no-infer-ice-115806.rs b/tests/ui/generic-const-items/assoc-const-no-infer-ice-115806.rs new file mode 100644 index 00000000000..2607013ec63 --- /dev/null +++ b/tests/ui/generic-const-items/assoc-const-no-infer-ice-115806.rs @@ -0,0 +1,19 @@ +// ICE: assertion failed: !value.has_infer() +// issue: rust-lang/rust#115806 +#![feature(associated_const_equality)] +#![allow(incomplete_features)] + +pub struct NoPin; + +impl<TA> Pins<TA> for NoPin {} + +pub trait PinA<PER> { + const A: &'static () = &(); +} + +pub trait Pins<USART> {} + +impl<USART, T> Pins<USART> for T where T: PinA<USART, A = { &() }> {} +//~^ ERROR conflicting implementations of trait `Pins<_>` for type `NoPin` + +pub fn main() {} diff --git a/tests/ui/generic-const-items/assoc-const-no-infer-ice-115806.stderr b/tests/ui/generic-const-items/assoc-const-no-infer-ice-115806.stderr new file mode 100644 index 00000000000..9a9baaddcba --- /dev/null +++ b/tests/ui/generic-const-items/assoc-const-no-infer-ice-115806.stderr @@ -0,0 +1,14 @@ +error[E0119]: conflicting implementations of trait `Pins<_>` for type `NoPin` + --> $DIR/assoc-const-no-infer-ice-115806.rs:16:1 + | +LL | impl<TA> Pins<TA> for NoPin {} + | --------------------------- first implementation here +... +LL | impl<USART, T> Pins<USART> for T where T: PinA<USART, A = { &() }> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `NoPin` + | + = note: downstream crates may implement trait `PinA<_>` for type `NoPin` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/impl-trait/different_where_bounds.rs b/tests/ui/impl-trait/different_where_bounds.rs new file mode 100644 index 00000000000..87ae6db2822 --- /dev/null +++ b/tests/ui/impl-trait/different_where_bounds.rs @@ -0,0 +1,27 @@ +//! This test checks that the param env canonicalization cache +//! does not end up with inconsistent values. + +//@ check-pass + +pub fn poison1() -> impl Sized +where + (): 'static, +{ +} +pub fn poison2() -> impl Sized +where + (): 'static, +{ + define_by_query((poison2, ())); +} +pub fn poison3() -> impl Sized +where + (): 'static, +{ +} + +trait Query {} +impl<Out, F: Fn() -> Out> Query for (F, Out) {} +fn define_by_query(_: impl Query) {} + +fn main() {} diff --git a/tests/ui/impl-trait/equality-in-canonical-query.clone.stderr b/tests/ui/impl-trait/equality-in-canonical-query.clone.stderr deleted file mode 100644 index e4c8aec3973..00000000000 --- a/tests/ui/impl-trait/equality-in-canonical-query.clone.stderr +++ /dev/null @@ -1,23 +0,0 @@ -note: no errors encountered even though delayed bugs were created - -note: those delayed bugs will now be shown as internal compiler errors - -error: internal compiler error: {OpaqueTypeKey { def_id: DefId(rpit::{opaque#0}), args: [] }: OpaqueTypeDecl { hidden_type: OpaqueHiddenType { span: no-location (#0), ty: Alias(Opaque, AliasTy { args: [], def_id: DefId(foo::{opaque#0}) }) } }} - | - = - - -error: internal compiler error: error performing ParamEnvAnd { param_env: ParamEnv { caller_bounds: [], reveal: UserFacing }, value: ProvePredicate { predicate: Binder { value: ProjectionPredicate(AliasTy { args: [FnDef(DefId(rpit), []), ()], def_id: DefId(ops::function::FnOnce::Output) }, Term::Ty(Alias(Opaque, AliasTy { args: [], def_id: DefId(foo::{opaque#0}) }))), bound_vars: [] } } } - --> $DIR/equality-in-canonical-query.rs:21:5 - | -LL | same_output(foo, rpit); - | ^^^^^^^^^^^^^^^^^^^^^^ - | - - --> $DIR/equality-in-canonical-query.rs:21:5 - | -LL | same_output(foo, rpit); - | ^^^^^^^^^^^^^^^^^^^^^^ - -query stack during panic: -end of query stack diff --git a/tests/ui/impl-trait/equality-in-canonical-query.rs b/tests/ui/impl-trait/equality-in-canonical-query.rs index 6a32f4bec76..2b8f6ce1b07 100644 --- a/tests/ui/impl-trait/equality-in-canonical-query.rs +++ b/tests/ui/impl-trait/equality-in-canonical-query.rs @@ -1,15 +1,6 @@ // issue: #116877 //@ revisions: sized clone -//@[sized] check-pass -//@[clone] known-bug: #108498 -//@[clone] failure-status: 101 -//@[clone] normalize-stderr-test: "DefId\(.*?\]::" -> "DefId(" -//@[clone] normalize-stderr-test: "(?m)note: we would appreciate a bug report.*\n\n" -> "" -//@[clone] normalize-stderr-test: "(?m)note: rustc.*running on.*\n\n" -> "" -//@[clone] normalize-stderr-test: "(?m)note: compiler flags.*\n\n" -> "" -//@[clone] normalize-stderr-test: "(?m)note: delayed at.*$" -> "" -//@[clone] normalize-stderr-test: "(?m)^ *\d+: .*\n" -> "" -//@[clone] normalize-stderr-test: "(?m)^ *at .*\n" -> "" +//@ check-pass #[cfg(sized)] fn rpit() -> impl Sized {} #[cfg(clone)] fn rpit() -> impl Clone {} diff --git a/tests/ui/impl-trait/failed-to-resolve-instance-ice-105488.rs b/tests/ui/impl-trait/failed-to-resolve-instance-ice-105488.rs new file mode 100644 index 00000000000..994a5073947 --- /dev/null +++ b/tests/ui/impl-trait/failed-to-resolve-instance-ice-105488.rs @@ -0,0 +1,42 @@ +// ICE failed to resolve instance for <fn() -> impl MyFnOnce ... +// issue: rust-lang/rust#105488 +//@ build-fail +//~^^^ ERROR overflow evaluating the requirement `fn() -> impl MyFnOnce + +pub trait MyFnOnce { + type Output; + + fn call_my_fn_once(self) -> Self::Output; +} + +pub struct WrapFnOnce<F>(F); + +impl<F: FnOnce() -> D, D: MyFnOnce> MyFnOnce for WrapFnOnce<F> { + type Output = D::Output; + + fn call_my_fn_once(self) -> Self::Output { + D::call_my_fn_once(self.0()) + } +} + +impl<F: FnOnce() -> D, D: MyFnOnce> MyFnOnce for F { + type Output = D::Output; + + fn call_my_fn_once(self) -> Self::Output { + D::call_my_fn_once(self()) + } +} + +pub fn my_fn_1() -> impl MyFnOnce { + my_fn_2 +} + +pub fn my_fn_2() -> impl MyFnOnce { + WrapFnOnce(my_fn_1) +} + +fn main() { + let v = my_fn_1(); + + let _ = v.call_my_fn_once(); +} diff --git a/tests/ui/impl-trait/failed-to-resolve-instance-ice-105488.stderr b/tests/ui/impl-trait/failed-to-resolve-instance-ice-105488.stderr new file mode 100644 index 00000000000..c2782b79d90 --- /dev/null +++ b/tests/ui/impl-trait/failed-to-resolve-instance-ice-105488.stderr @@ -0,0 +1,16 @@ +error[E0275]: overflow evaluating the requirement `fn() -> impl MyFnOnce {my_fn_2}: MyFnOnce` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`failed_to_resolve_instance_ice_105488`) +note: required for `WrapFnOnce<fn() -> impl MyFnOnce {my_fn_1}>` to implement `MyFnOnce` + --> $DIR/failed-to-resolve-instance-ice-105488.rs:14:37 + | +LL | impl<F: FnOnce() -> D, D: MyFnOnce> MyFnOnce for WrapFnOnce<F> { + | -------- ^^^^^^^^ ^^^^^^^^^^^^^ + | | + | unsatisfied trait bound introduced here + = note: 126 redundant requirements hidden + = note: required for `WrapFnOnce<fn() -> impl MyFnOnce {my_fn_1}>` to implement `MyFnOnce` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/impl-trait/failed-to-resolve-instance-ice-123145.rs b/tests/ui/impl-trait/failed-to-resolve-instance-ice-123145.rs new file mode 100644 index 00000000000..977827f3b55 --- /dev/null +++ b/tests/ui/impl-trait/failed-to-resolve-instance-ice-123145.rs @@ -0,0 +1,20 @@ +// ICE failed to resolve instance for ... +// issue: rust-lang/rust#123145 +//@ build-fail +//~^^^ ERROR overflow evaluating the requirement `(fn() -> impl Handler + +trait Handler { + fn handle(&self) {} +} + +impl<H: Handler, F: Fn() -> H> Handler for F {} + +impl<L: Handler> Handler for (L,) {} + +fn one() -> impl Handler { + (one,) +} + +fn main() { + one.handle(); +} diff --git a/tests/ui/impl-trait/failed-to-resolve-instance-ice-123145.stderr b/tests/ui/impl-trait/failed-to-resolve-instance-ice-123145.stderr new file mode 100644 index 00000000000..f61e8c2f8df --- /dev/null +++ b/tests/ui/impl-trait/failed-to-resolve-instance-ice-123145.stderr @@ -0,0 +1,15 @@ +error[E0275]: overflow evaluating the requirement `(fn() -> impl Handler {one},): Handler` + | +note: required for `fn() -> impl Handler {one}` to implement `Handler` + --> $DIR/failed-to-resolve-instance-ice-123145.rs:10:32 + | +LL | impl<H: Handler, F: Fn() -> H> Handler for F {} + | ------- ^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: 2 redundant requirements hidden + = note: required for `fn() -> impl Handler {one}` to implement `Handler` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/impl-trait/nested-hkl-lifetime.rs b/tests/ui/impl-trait/nested-hkl-lifetime.rs new file mode 100644 index 00000000000..089ceca6777 --- /dev/null +++ b/tests/ui/impl-trait/nested-hkl-lifetime.rs @@ -0,0 +1,32 @@ +//@ check-pass + +use std::iter::FromIterator; + +struct DynamicAlt<P>(P); + +impl<P> FromIterator<P> for DynamicAlt<P> { + fn from_iter<T: IntoIterator<Item = P>>(_iter: T) -> Self { + loop {} + } +} + +fn owned_context<I, F>(_: F) -> impl FnMut(I) -> I { + |i| i +} + +trait Parser<I> {} + +impl<T, I> Parser<I> for T where T: FnMut(I) -> I {} + +fn alt<I, P: Parser<I>>(_: DynamicAlt<P>) -> impl FnMut(I) -> I { + |i| i +} + +fn rule_to_parser<'c>() -> impl Parser<&'c str> { + move |input| { + let v: Vec<()> = vec![]; + alt(v.iter().map(|()| owned_context(rule_to_parser())).collect::<DynamicAlt<_>>())(input) + } +} + +fn main() {} diff --git a/tests/ui/impl-trait/nested-rpit-hrtb.rs b/tests/ui/impl-trait/nested-rpit-hrtb.rs index a696e1710f0..9b18aceb4a7 100644 --- a/tests/ui/impl-trait/nested-rpit-hrtb.rs +++ b/tests/ui/impl-trait/nested-rpit-hrtb.rs @@ -44,7 +44,7 @@ fn one_hrtb_mention_fn_outlives<'b>() -> impl for<'a> Foo<'a, Assoc = impl Sized // This should resolve. fn one_hrtb_mention_fn_trait_param_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Qux<'b>> {} -//~^ ERROR type annotations needed: cannot satisfy `for<'a> &'a (): Qux<'b>` +//~^ ERROR the trait bound `for<'a> &'a (): Qux<'b>` is not satisfied // This should resolve. fn one_hrtb_mention_fn_outlives_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'b> {} diff --git a/tests/ui/impl-trait/nested-rpit-hrtb.stderr b/tests/ui/impl-trait/nested-rpit-hrtb.stderr index 64f801ea685..2fa036f35fa 100644 --- a/tests/ui/impl-trait/nested-rpit-hrtb.stderr +++ b/tests/ui/impl-trait/nested-rpit-hrtb.stderr @@ -86,13 +86,12 @@ note: lifetime declared here LL | fn one_hrtb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl Qux<'a>> {} | ^^ -error[E0283]: type annotations needed: cannot satisfy `for<'a> &'a (): Qux<'b>` +error[E0277]: the trait bound `for<'a> &'a (): Qux<'b>` is not satisfied --> $DIR/nested-rpit-hrtb.rs:46:79 | LL | fn one_hrtb_mention_fn_trait_param_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Qux<'b>> {} - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ the trait `for<'a> Qux<'b>` is not implemented for `&'a ()` | - = note: cannot satisfy `for<'a> &'a (): Qux<'b>` = help: the trait `Qux<'_>` is implemented for `()` = help: for that trait implementation, expected `()`, found `&'a ()` @@ -125,5 +124,5 @@ LL | fn two_htrb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Si error: aborting due to 11 previous errors -Some errors have detailed explanations: E0261, E0277, E0283, E0657. +Some errors have detailed explanations: E0261, E0277, E0657. For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/imports/unused-import-issue-87973.rs b/tests/ui/imports/unused-import-issue-87973.rs index b04bec07d18..1b016ff814c 100644 --- a/tests/ui/imports/unused-import-issue-87973.rs +++ b/tests/ui/imports/unused-import-issue-87973.rs @@ -4,7 +4,7 @@ // Check that attributes get removed too. See #87973. #[deprecated] #[allow(unsafe_code)] -#[cfg(not(foo))] +#[cfg(not(FALSE))] use std::fs; //~^ ERROR unused import diff --git a/tests/ui/inference/issue-80409.no-compat.stderr b/tests/ui/inference/issue-80409.no-compat.stderr index 523ca229b06..c772225be75 100644 --- a/tests/ui/inference/issue-80409.no-compat.stderr +++ b/tests/ui/inference/issue-80409.no-compat.stderr @@ -1,4 +1,4 @@ -error: internal compiler error: error performing ParamEnvAnd { param_env: ParamEnv { caller_bounds: [], reveal: UserFacing }, value: ImpliedOutlivesBounds { ty: &'?2 mut StateContext<'?3, usize> } } +error: internal compiler error: error performing operation: fully_perform --> $DIR/issue-80409.rs:49:30 | LL | builder.state().on_entry(|_| {}); diff --git a/tests/ui/inner-attrs-on-impl.rs b/tests/ui/inner-attrs-on-impl.rs index 1c94169a2e5..75f406232e2 100644 --- a/tests/ui/inner-attrs-on-impl.rs +++ b/tests/ui/inner-attrs-on-impl.rs @@ -3,16 +3,16 @@ struct Foo; impl Foo { - #![cfg(cfg_that_surely_doesnt_exist)] + #![cfg(FALSE)] fn method(&self) -> bool { false } } impl Foo { - #![cfg(not(cfg_that_surely_doesnt_exist))] + #![cfg(not(FALSE))] // check that we don't eat attributes too eagerly. - #[cfg(cfg_that_surely_doesnt_exist)] + #[cfg(FALSE)] fn method(&self) -> bool { false } fn method(&self) -> bool { true } diff --git a/tests/ui/instrument-coverage/off-values.rs b/tests/ui/instrument-coverage/off-values.rs index bd13e5d7495..60222d43b23 100644 --- a/tests/ui/instrument-coverage/off-values.rs +++ b/tests/ui/instrument-coverage/off-values.rs @@ -1,9 +1,9 @@ //@ check-pass -//@ revisions: n no off false zero +//@ revisions: n no off _false zero //@ [n] compile-flags: -Cinstrument-coverage=n //@ [no] compile-flags: -Cinstrument-coverage=no //@ [off] compile-flags: -Cinstrument-coverage=off -//@ [false] compile-flags: -Cinstrument-coverage=false +//@ [_false] compile-flags: -Cinstrument-coverage=false //@ [zero] compile-flags: -Cinstrument-coverage=0 fn main() {} diff --git a/tests/ui/issues/issue-11004.rs b/tests/ui/issues/issue-11004.rs index 10ef1f5e3b5..09d5476dbe6 100644 --- a/tests/ui/issues/issue-11004.rs +++ b/tests/ui/issues/issue-11004.rs @@ -2,14 +2,14 @@ use std::mem; struct A { x: i32, y: f64 } -#[cfg(not(works))] +#[cfg(not(FALSE))] unsafe fn access(n:*mut A) -> (i32, f64) { let x : i32 = n.x; //~ no field `x` on type `*mut A` let y : f64 = n.y; //~ no field `y` on type `*mut A` (x, y) } -#[cfg(works)] +#[cfg(FALSE)] unsafe fn access(n:*mut A) -> (i32, f64) { let x : i32 = (*n).x; let y : f64 = (*n).y; diff --git a/tests/ui/issues/issue-11085.rs b/tests/ui/issues/issue-11085.rs index c4a9f5f69bd..300be10226c 100644 --- a/tests/ui/issues/issue-11085.rs +++ b/tests/ui/issues/issue-11085.rs @@ -5,7 +5,7 @@ //@ pretty-expanded FIXME #23616 struct Foo { - #[cfg(fail)] + #[cfg(FALSE)] bar: baz, foo: isize, } @@ -17,18 +17,18 @@ struct Foo2 { enum Bar1 { Bar1_1, - #[cfg(fail)] + #[cfg(FALSE)] Bar1_2(NotAType), } enum Bar2 { - #[cfg(fail)] + #[cfg(FALSE)] Bar2_1(NotAType), } enum Bar3 { Bar3_1 { - #[cfg(fail)] + #[cfg(FALSE)] foo: isize, bar: isize, } diff --git a/tests/ui/issues/issue-16819.rs b/tests/ui/issues/issue-16819.rs index 320695118d5..e2b10909177 100644 --- a/tests/ui/issues/issue-16819.rs +++ b/tests/ui/issues/issue-16819.rs @@ -3,7 +3,7 @@ // `#[cfg]` on struct field permits empty unusable struct struct S { - #[cfg(untrue)] + #[cfg(FALSE)] a: int, } diff --git a/tests/ui/issues/issue-21763.rs b/tests/ui/issues/issue-21763.rs index a349253063c..1d0a0705cbb 100644 --- a/tests/ui/issues/issue-21763.rs +++ b/tests/ui/issues/issue-21763.rs @@ -1,6 +1,6 @@ // Regression test for HashMap only impl'ing Send/Sync if its contents do -//@ normalize-stderr-test: "\S+hashbrown-\S+" -> "$$HASHBROWN_SRC_LOCATION" +//@ normalize-stderr-test: "\S+[\\/]hashbrown\S+" -> "$$HASHBROWN_SRC_LOCATION" use std::collections::HashMap; use std::rc::Rc; diff --git a/tests/ui/layout/base-layout-is-sized-ice-123078.rs b/tests/ui/layout/base-layout-is-sized-ice-123078.rs new file mode 100644 index 00000000000..b1c33e15075 --- /dev/null +++ b/tests/ui/layout/base-layout-is-sized-ice-123078.rs @@ -0,0 +1,17 @@ +// ICE !base.layout().is_sized() +// issue: rust-lang/rust#123078 + +struct S { + a: [u8], + //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time + b: (), +} + +const C: S = unsafe { std::mem::transmute(()) }; +//~^ ERROR cannot transmute between types of different sizes, or dependently-sized types +const _: [(); { + C; + 0 +}] = []; + +pub fn main() {} diff --git a/tests/ui/layout/base-layout-is-sized-ice-123078.stderr b/tests/ui/layout/base-layout-is-sized-ice-123078.stderr new file mode 100644 index 00000000000..7e0a41a4367 --- /dev/null +++ b/tests/ui/layout/base-layout-is-sized-ice-123078.stderr @@ -0,0 +1,31 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/base-layout-is-sized-ice-123078.rs:5:8 + | +LL | a: [u8], + | ^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: only the last field of a struct may have a dynamically sized type + = help: change the field's type to have a statically known size +help: borrowed types always have a statically known size + | +LL | a: &[u8], + | + +help: the `Box` type always has a statically known size and allocates its contents in the heap + | +LL | a: Box<[u8]>, + | ++++ + + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/base-layout-is-sized-ice-123078.rs:10:23 + | +LL | const C: S = unsafe { std::mem::transmute(()) }; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `()` (0 bits) + = note: target type: `S` (this type does not have a fixed size) + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0277, E0512. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.rs b/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.rs new file mode 100644 index 00000000000..96c993035ef --- /dev/null +++ b/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.rs @@ -0,0 +1,10 @@ +// ICE Unexpected unsized type tail: &ReStatic [u8] +// issue: rust-lang/rust#122488 +use std::ops::Deref; + +struct ArenaSet<U: Deref, V: ?Sized = <U as Deref>::Target>(V, U); +//~^ ERROR the size for values of type `V` cannot be known at compilation time + +const DATA: *const ArenaSet<Vec<u8>> = std::ptr::null_mut(); + +pub fn main() {} diff --git a/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.stderr b/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.stderr new file mode 100644 index 00000000000..f39cb29868a --- /dev/null +++ b/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.stderr @@ -0,0 +1,27 @@ +error[E0277]: the size for values of type `V` cannot be known at compilation time + --> $DIR/issue-unsized-tail-restatic-ice-122488.rs:5:61 + | +LL | struct ArenaSet<U: Deref, V: ?Sized = <U as Deref>::Target>(V, U); + | -------------------------------- ^ doesn't have a size known at compile-time + | | + | this type parameter needs to be `Sized` + | + = note: only the last field of a struct may have a dynamically sized type + = help: change the field's type to have a statically known size +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - struct ArenaSet<U: Deref, V: ?Sized = <U as Deref>::Target>(V, U); +LL + struct ArenaSet<U: Deref, V = <U as Deref>::Target>(V, U); + | +help: borrowed types always have a statically known size + | +LL | struct ArenaSet<U: Deref, V: ?Sized = <U as Deref>::Target>(&V, U); + | + +help: the `Box` type always has a statically known size and allocates its contents in the heap + | +LL | struct ArenaSet<U: Deref, V: ?Sized = <U as Deref>::Target>(Box<V>, U); + | ++++ + + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/issues/auxiliary/issue-12133-dylib.rs b/tests/ui/linkage-attr/auxiliary/issue-12133-dylib.rs index 8bd2b3353b8..8bd2b3353b8 100644 --- a/tests/ui/issues/auxiliary/issue-12133-dylib.rs +++ b/tests/ui/linkage-attr/auxiliary/issue-12133-dylib.rs diff --git a/tests/ui/issues/auxiliary/issue-12133-dylib2.rs b/tests/ui/linkage-attr/auxiliary/issue-12133-dylib2.rs index 42e13ad6908..42e13ad6908 100644 --- a/tests/ui/issues/auxiliary/issue-12133-dylib2.rs +++ b/tests/ui/linkage-attr/auxiliary/issue-12133-dylib2.rs diff --git a/tests/ui/issues/auxiliary/issue-12133-rlib.rs b/tests/ui/linkage-attr/auxiliary/issue-12133-rlib.rs index 1adaf2b0379..1adaf2b0379 100644 --- a/tests/ui/issues/auxiliary/issue-12133-rlib.rs +++ b/tests/ui/linkage-attr/auxiliary/issue-12133-rlib.rs diff --git a/tests/ui/linkage-attr/framework.omit.stderr b/tests/ui/linkage-attr/framework.omit.stderr index 5cb4d391437..23e017cb012 100644 --- a/tests/ui/linkage-attr/framework.omit.stderr +++ b/tests/ui/linkage-attr/framework.omit.stderr @@ -1,4 +1,4 @@ -error: linking with `cc` failed: exit status: 1 +error: linking with `LINKER` failed: exit status: 1 | ld: Undefined symbols: _CFRunLoopGetTypeID, referenced from: diff --git a/tests/ui/linkage-attr/framework.rs b/tests/ui/linkage-attr/framework.rs index 662ef4c429d..824adf62206 100644 --- a/tests/ui/linkage-attr/framework.rs +++ b/tests/ui/linkage-attr/framework.rs @@ -6,8 +6,10 @@ //@ [weak]run-pass //@ [both]run-pass -// The linker's exact error output changes between Xcode versions. +// The linker's exact error output changes between Xcode versions, depends on +// linker invocation details, and the linker sometimes outputs more warnings. //@ compare-output-lines-by-subset +//@ normalize-stderr-test: "linking with `.*` failed" -> "linking with `LINKER` failed" //@ normalize-stderr-test: "Undefined symbols for architecture .*" -> "ld: Undefined symbols:" //@ normalize-stderr-test: "._CFRunLoopGetTypeID.," -> "_CFRunLoopGetTypeID," diff --git a/tests/ui/issues/issue-12133-1.rs b/tests/ui/linkage-attr/issue-12133-1.rs index dc3f7f33da1..dc3f7f33da1 100644 --- a/tests/ui/issues/issue-12133-1.rs +++ b/tests/ui/linkage-attr/issue-12133-1.rs diff --git a/tests/ui/issues/issue-12133-2.rs b/tests/ui/linkage-attr/issue-12133-2.rs index 55742a1b383..55742a1b383 100644 --- a/tests/ui/issues/issue-12133-2.rs +++ b/tests/ui/linkage-attr/issue-12133-2.rs diff --git a/tests/ui/issues/issue-12133-3.rs b/tests/ui/linkage-attr/issue-12133-3.rs index a34c075d64d..a34c075d64d 100644 --- a/tests/ui/issues/issue-12133-3.rs +++ b/tests/ui/linkage-attr/issue-12133-3.rs diff --git a/tests/ui/lint/unused_braces_macro.rs b/tests/ui/lint/unused_braces_macro.rs index d0b42a12ff5..f9da4ed4e49 100644 --- a/tests/ui/lint/unused_braces_macro.rs +++ b/tests/ui/lint/unused_braces_macro.rs @@ -2,5 +2,5 @@ pub fn foo<const BAR: bool> () {} fn main() { - foo::<{cfg!(feature = "foo")}>(); + foo::<{cfg!(FALSE)}>(); } diff --git a/tests/ui/macros/macro-attributes.rs b/tests/ui/macros/macro-attributes.rs index 57ba0d3bf56..83290790766 100644 --- a/tests/ui/macros/macro-attributes.rs +++ b/tests/ui/macros/macro-attributes.rs @@ -9,7 +9,7 @@ macro_rules! compiles_fine { // check that the attributes are recognised by requiring this // to be removed to avoid a compile error - #[cfg(always_remove)] + #[cfg(FALSE)] static MISTYPED: () = "foo"; } } diff --git a/tests/ui/macros/macro-error.rs b/tests/ui/macros/macro-error.rs index 59ed79e91f0..4984b92911e 100644 --- a/tests/ui/macros/macro-error.rs +++ b/tests/ui/macros/macro-error.rs @@ -5,5 +5,5 @@ macro_rules! foo { fn main() { foo!(0); // Check that we report errors at macro definition, not expansion. - let _: cfg!(foo) = (); //~ ERROR non-type macro in type position + let _: cfg!(FALSE) = (); //~ ERROR non-type macro in type position } diff --git a/tests/ui/macros/macro-error.stderr b/tests/ui/macros/macro-error.stderr index 2539a6d5156..fcf8d922d65 100644 --- a/tests/ui/macros/macro-error.stderr +++ b/tests/ui/macros/macro-error.stderr @@ -7,8 +7,8 @@ LL | ($a:expr) => a; error: non-type macro in type position: cfg --> $DIR/macro-error.rs:8:12 | -LL | let _: cfg!(foo) = (); - | ^^^^^^^^^ +LL | let _: cfg!(FALSE) = (); + | ^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/macros/macro-inner-attributes.rs b/tests/ui/macros/macro-inner-attributes.rs index a8cda23075b..6dbfce21359 100644 --- a/tests/ui/macros/macro-inner-attributes.rs +++ b/tests/ui/macros/macro-inner-attributes.rs @@ -5,11 +5,11 @@ macro_rules! test { ($nm:ident, $i:item) => (mod $nm { #![$a] $i }); } test!(a, - #[cfg(qux)], + #[cfg(FALSE)], pub fn bar() { }); test!(b, - #[cfg(not(qux))], + #[cfg(not(FALSE))], pub fn bar() { }); #[rustc_dummy] diff --git a/tests/ui/macros/macro-outer-attributes.rs b/tests/ui/macros/macro-outer-attributes.rs index 0752f7e3153..8c79683f49a 100644 --- a/tests/ui/macros/macro-outer-attributes.rs +++ b/tests/ui/macros/macro-outer-attributes.rs @@ -5,11 +5,11 @@ macro_rules! test { ($nm:ident, $i:item) => (mod $nm { #[$a] $i }); } test!(a, - #[cfg(qux)], + #[cfg(FALSE)], pub fn bar() { }); test!(b, - #[cfg(not(qux))], + #[cfg(not(FALSE))], pub fn bar() { }); // test1!(#[bar]) diff --git a/tests/ui/macros/macro-with-attrs2.rs b/tests/ui/macros/macro-with-attrs2.rs index 3ff26025206..37188e45ad3 100644 --- a/tests/ui/macros/macro-with-attrs2.rs +++ b/tests/ui/macros/macro-with-attrs2.rs @@ -1,9 +1,9 @@ //@ run-pass -#[cfg(foo)] +#[cfg(FALSE)] macro_rules! foo { () => (1) } -#[cfg(not(foo))] +#[cfg(not(FALSE))] macro_rules! foo { () => (2) } pub fn main() { diff --git a/tests/ui/methods/call_method_unknown_pointee.rs b/tests/ui/methods/call_method_unknown_pointee.rs index 1643b6bfa18..a144e855ae3 100644 --- a/tests/ui/methods/call_method_unknown_pointee.rs +++ b/tests/ui/methods/call_method_unknown_pointee.rs @@ -8,10 +8,10 @@ fn main() { let ptr = &val as *const u32; unsafe { let _a: i32 = (ptr as *const _).read(); - //~^ ERROR cannot call a method on a raw pointer with an unknown pointee type [E0699] + //~^ ERROR type annotations needed let b = ptr as *const _; + //~^ ERROR type annotations needed let _b: u8 = b.read(); - //~^ ERROR cannot call a method on a raw pointer with an unknown pointee type [E0699] let _c = (ptr as *const u8).read(); // we know the type here } @@ -19,10 +19,10 @@ fn main() { let ptr = &mut val as *mut u32; unsafe { let _a: i32 = (ptr as *mut _).read(); - //~^ ERROR cannot call a method on a raw pointer with an unknown pointee type [E0699] + //~^ ERROR type annotations needed let b = ptr as *mut _; + //~^ ERROR type annotations needed b.write(10); - //~^ ERROR cannot call a method on a raw pointer with an unknown pointee type [E0699] (ptr as *mut i32).write(1000); // we know the type here } } diff --git a/tests/ui/methods/call_method_unknown_pointee.stderr b/tests/ui/methods/call_method_unknown_pointee.stderr index 84ecf046e7a..9d0f38cf6b5 100644 --- a/tests/ui/methods/call_method_unknown_pointee.stderr +++ b/tests/ui/methods/call_method_unknown_pointee.stderr @@ -1,27 +1,49 @@ -error[E0699]: cannot call a method on a raw pointer with an unknown pointee type +error[E0282]: type annotations needed --> $DIR/call_method_unknown_pointee.rs:10:41 | LL | let _a: i32 = (ptr as *const _).read(); | ^^^^ + | | + | cannot infer type + | cannot call a method on a raw pointer with an unknown pointee type -error[E0699]: cannot call a method on a raw pointer with an unknown pointee type - --> $DIR/call_method_unknown_pointee.rs:13:24 +error[E0282]: type annotations needed for `*const _` + --> $DIR/call_method_unknown_pointee.rs:12:13 | +LL | let b = ptr as *const _; + | ^ +LL | LL | let _b: u8 = b.read(); - | ^^^^ + | ---- cannot call a method on a raw pointer with an unknown pointee type + | +help: consider giving `b` an explicit type, where the placeholders `_` are specified + | +LL | let b: *const _ = ptr as *const _; + | ++++++++++ -error[E0699]: cannot call a method on a raw pointer with an unknown pointee type +error[E0282]: type annotations needed --> $DIR/call_method_unknown_pointee.rs:21:39 | LL | let _a: i32 = (ptr as *mut _).read(); | ^^^^ + | | + | cannot infer type + | cannot call a method on a raw pointer with an unknown pointee type -error[E0699]: cannot call a method on a raw pointer with an unknown pointee type - --> $DIR/call_method_unknown_pointee.rs:24:11 +error[E0282]: type annotations needed for `*mut _` + --> $DIR/call_method_unknown_pointee.rs:23:13 | +LL | let b = ptr as *mut _; + | ^ +LL | LL | b.write(10); - | ^^^^^ + | ----- cannot call a method on a raw pointer with an unknown pointee type + | +help: consider giving `b` an explicit type, where the placeholders `_` are specified + | +LL | let b: *mut _ = ptr as *mut _; + | ++++++++ error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0699`. +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/issues/auxiliary/issue-13872-1.rs b/tests/ui/modules/auxiliary/issue-13872-1.rs index fa9258834c7..fa9258834c7 100644 --- a/tests/ui/issues/auxiliary/issue-13872-1.rs +++ b/tests/ui/modules/auxiliary/issue-13872-1.rs diff --git a/tests/ui/issues/auxiliary/issue-13872-2.rs b/tests/ui/modules/auxiliary/issue-13872-2.rs index 8c64f16e3f9..8c64f16e3f9 100644 --- a/tests/ui/issues/auxiliary/issue-13872-2.rs +++ b/tests/ui/modules/auxiliary/issue-13872-2.rs diff --git a/tests/ui/issues/auxiliary/issue-13872-3.rs b/tests/ui/modules/auxiliary/issue-13872-3.rs index d31d52eb847..d31d52eb847 100644 --- a/tests/ui/issues/auxiliary/issue-13872-3.rs +++ b/tests/ui/modules/auxiliary/issue-13872-3.rs diff --git a/tests/ui/issues/issue-1920-absolute-paths/auxiliary/issue-1920.rs b/tests/ui/modules/auxiliary/issue-1920.rs index 1548cb99563..1548cb99563 100644 --- a/tests/ui/issues/issue-1920-absolute-paths/auxiliary/issue-1920.rs +++ b/tests/ui/modules/auxiliary/issue-1920.rs diff --git a/tests/ui/issues/issue-13872.rs b/tests/ui/modules/issue-13872.rs index 5589d2d4f68..5589d2d4f68 100644 --- a/tests/ui/issues/issue-13872.rs +++ b/tests/ui/modules/issue-13872.rs diff --git a/tests/ui/issues/issue-1920-absolute-paths/issue-1920-1.rs b/tests/ui/modules/issue-1920-1.rs index 763d07db2cd..763d07db2cd 100644 --- a/tests/ui/issues/issue-1920-absolute-paths/issue-1920-1.rs +++ b/tests/ui/modules/issue-1920-1.rs diff --git a/tests/ui/issues/issue-1920-absolute-paths/issue-1920-1.stderr b/tests/ui/modules/issue-1920-1.stderr index b7c7da00672..b7c7da00672 100644 --- a/tests/ui/issues/issue-1920-absolute-paths/issue-1920-1.stderr +++ b/tests/ui/modules/issue-1920-1.stderr diff --git a/tests/ui/issues/issue-1920-absolute-paths/issue-1920-2.rs b/tests/ui/modules/issue-1920-2.rs index b5a90b2c8e8..b5a90b2c8e8 100644 --- a/tests/ui/issues/issue-1920-absolute-paths/issue-1920-2.rs +++ b/tests/ui/modules/issue-1920-2.rs diff --git a/tests/ui/issues/issue-1920-absolute-paths/issue-1920-2.stderr b/tests/ui/modules/issue-1920-2.stderr index 844cb0ff199..844cb0ff199 100644 --- a/tests/ui/issues/issue-1920-absolute-paths/issue-1920-2.stderr +++ b/tests/ui/modules/issue-1920-2.stderr diff --git a/tests/ui/issues/issue-1920-absolute-paths/issue-1920-3.rs b/tests/ui/modules/issue-1920-3.rs index 372c8b1511c..372c8b1511c 100644 --- a/tests/ui/issues/issue-1920-absolute-paths/issue-1920-3.rs +++ b/tests/ui/modules/issue-1920-3.rs diff --git a/tests/ui/issues/issue-1920-absolute-paths/issue-1920-3.stderr b/tests/ui/modules/issue-1920-3.stderr index 525ca4685bb..525ca4685bb 100644 --- a/tests/ui/issues/issue-1920-absolute-paths/issue-1920-3.stderr +++ b/tests/ui/modules/issue-1920-3.stderr diff --git a/tests/ui/nested-cfg-attrs.rs b/tests/ui/nested-cfg-attrs.rs index c988d423373..0af28fc3d8e 100644 --- a/tests/ui/nested-cfg-attrs.rs +++ b/tests/ui/nested-cfg-attrs.rs @@ -1,4 +1,4 @@ -#[cfg_attr(all(), cfg_attr(all(), cfg(foo)))] +#[cfg_attr(all(), cfg_attr(all(), cfg(FALSE)))] fn f() {} fn main() { f() } //~ ERROR cannot find function `f` in this scope diff --git a/tests/ui/issues/issue-40510-captured-variable-return/issue-40510-1.rs b/tests/ui/nll/issue-40510-1.rs index ca53dcd9b41..ca53dcd9b41 100644 --- a/tests/ui/issues/issue-40510-captured-variable-return/issue-40510-1.rs +++ b/tests/ui/nll/issue-40510-1.rs diff --git a/tests/ui/issues/issue-40510-captured-variable-return/issue-40510-1.stderr b/tests/ui/nll/issue-40510-1.stderr index 81fed1305cb..81fed1305cb 100644 --- a/tests/ui/issues/issue-40510-captured-variable-return/issue-40510-1.stderr +++ b/tests/ui/nll/issue-40510-1.stderr diff --git a/tests/ui/issues/issue-40510-captured-variable-return/issue-40510-2.rs b/tests/ui/nll/issue-40510-2.rs index 9ce54862265..9ce54862265 100644 --- a/tests/ui/issues/issue-40510-captured-variable-return/issue-40510-2.rs +++ b/tests/ui/nll/issue-40510-2.rs diff --git a/tests/ui/issues/issue-40510-captured-variable-return/issue-40510-3.rs b/tests/ui/nll/issue-40510-3.rs index 181263adcbf..181263adcbf 100644 --- a/tests/ui/issues/issue-40510-captured-variable-return/issue-40510-3.rs +++ b/tests/ui/nll/issue-40510-3.rs diff --git a/tests/ui/issues/issue-40510-captured-variable-return/issue-40510-3.stderr b/tests/ui/nll/issue-40510-3.stderr index 43e8a73b819..43e8a73b819 100644 --- a/tests/ui/issues/issue-40510-captured-variable-return/issue-40510-3.stderr +++ b/tests/ui/nll/issue-40510-3.stderr diff --git a/tests/ui/issues/issue-40510-captured-variable-return/issue-40510-4.rs b/tests/ui/nll/issue-40510-4.rs index 771502894f1..771502894f1 100644 --- a/tests/ui/issues/issue-40510-captured-variable-return/issue-40510-4.rs +++ b/tests/ui/nll/issue-40510-4.rs diff --git a/tests/ui/object-safety/bare-trait-dont-suggest-dyn.new.fixed b/tests/ui/object-safety/bare-trait-dont-suggest-dyn.new.fixed index aee05f5e512..4f5310082e1 100644 --- a/tests/ui/object-safety/bare-trait-dont-suggest-dyn.new.fixed +++ b/tests/ui/object-safety/bare-trait-dont-suggest-dyn.new.fixed @@ -2,8 +2,6 @@ //@[old] edition:2015 //@[new] edition:2021 //@[new] run-rustfix -// FIXME: the test suite tries to create a crate called `bare_trait_dont_suggest_dyn.new` -#![crate_name="bare_trait_dont_suggest_dyn"] #![deny(bare_trait_objects)] fn ord_prefer_dot(s: String) -> impl Ord { //~^ ERROR the trait `Ord` cannot be made into an object diff --git a/tests/ui/object-safety/bare-trait-dont-suggest-dyn.new.stderr b/tests/ui/object-safety/bare-trait-dont-suggest-dyn.new.stderr index 52db31d620c..efddab6dff6 100644 --- a/tests/ui/object-safety/bare-trait-dont-suggest-dyn.new.stderr +++ b/tests/ui/object-safety/bare-trait-dont-suggest-dyn.new.stderr @@ -1,5 +1,5 @@ error[E0038]: the trait `Ord` cannot be made into an object - --> $DIR/bare-trait-dont-suggest-dyn.rs:8:33 + --> $DIR/bare-trait-dont-suggest-dyn.rs:6:33 | LL | fn ord_prefer_dot(s: String) -> Ord { | ^^^ `Ord` cannot be made into an object diff --git a/tests/ui/object-safety/bare-trait-dont-suggest-dyn.old.stderr b/tests/ui/object-safety/bare-trait-dont-suggest-dyn.old.stderr index f795e910d21..0545a1afcc1 100644 --- a/tests/ui/object-safety/bare-trait-dont-suggest-dyn.old.stderr +++ b/tests/ui/object-safety/bare-trait-dont-suggest-dyn.old.stderr @@ -1,5 +1,5 @@ error: trait objects without an explicit `dyn` are deprecated - --> $DIR/bare-trait-dont-suggest-dyn.rs:8:33 + --> $DIR/bare-trait-dont-suggest-dyn.rs:6:33 | LL | fn ord_prefer_dot(s: String) -> Ord { | ^^^ @@ -7,7 +7,7 @@ LL | fn ord_prefer_dot(s: String) -> Ord { = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html> note: the lint level is defined here - --> $DIR/bare-trait-dont-suggest-dyn.rs:7:9 + --> $DIR/bare-trait-dont-suggest-dyn.rs:5:9 | LL | #![deny(bare_trait_objects)] | ^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | fn ord_prefer_dot(s: String) -> dyn Ord { | +++ error[E0038]: the trait `Ord` cannot be made into an object - --> $DIR/bare-trait-dont-suggest-dyn.rs:8:33 + --> $DIR/bare-trait-dont-suggest-dyn.rs:6:33 | LL | fn ord_prefer_dot(s: String) -> Ord { | ^^^ `Ord` cannot be made into an object diff --git a/tests/ui/object-safety/bare-trait-dont-suggest-dyn.rs b/tests/ui/object-safety/bare-trait-dont-suggest-dyn.rs index e927b510b9d..cb5a305eab0 100644 --- a/tests/ui/object-safety/bare-trait-dont-suggest-dyn.rs +++ b/tests/ui/object-safety/bare-trait-dont-suggest-dyn.rs @@ -2,8 +2,6 @@ //@[old] edition:2015 //@[new] edition:2021 //@[new] run-rustfix -// FIXME: the test suite tries to create a crate called `bare_trait_dont_suggest_dyn.new` -#![crate_name="bare_trait_dont_suggest_dyn"] #![deny(bare_trait_objects)] fn ord_prefer_dot(s: String) -> Ord { //~^ ERROR the trait `Ord` cannot be made into an object diff --git a/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.rs b/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.rs index d97f24a3d29..33671df9492 100644 --- a/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.rs +++ b/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.rs @@ -1,9 +1,9 @@ #![feature(stmt_expr_attributes)] fn foo() -> String { - #[cfg(feature = "validation")] + #[cfg(FALSE)] [1, 2, 3].iter().map(|c| c.to_string()).collect::<String>() //~ ERROR expected `;`, found `#` - #[cfg(not(feature = "validation"))] + #[cfg(not(FALSE))] String::new() } diff --git a/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.stderr b/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.stderr index a71253a5e42..6266718162f 100644 --- a/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.stderr +++ b/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.stderr @@ -1,11 +1,11 @@ error: expected `;`, found `#` --> $DIR/multiple-tail-expr-behind-cfg.rs:5:64 | -LL | #[cfg(feature = "validation")] - | ------------------------------ only `;` terminated statements or tail expressions are allowed after this attribute +LL | #[cfg(FALSE)] + | ------------- only `;` terminated statements or tail expressions are allowed after this attribute LL | [1, 2, 3].iter().map(|c| c.to_string()).collect::<String>() | ^ expected `;` here -LL | #[cfg(not(feature = "validation"))] +LL | #[cfg(not(FALSE))] | - unexpected token | help: add `;` here @@ -18,9 +18,9 @@ LL | { [1, 2, 3].iter().map(|c| c.to_string()).collect::<String>() } | + + help: it seems like you are trying to provide different expressions depending on `cfg`, consider using `if cfg!(..)` | -LL ~ if cfg!(feature = "validation") { +LL ~ if cfg!(FALSE) { LL ~ [1, 2, 3].iter().map(|c| c.to_string()).collect::<String>() -LL ~ } else if cfg!(not(feature = "validation")) { +LL ~ } else if cfg!(not(FALSE)) { LL ~ String::new() LL + } | diff --git a/tests/ui/parser/attribute/properly-recover-from-trailing-outer-attribute-in-body-2.rs b/tests/ui/parser/attribute/properly-recover-from-trailing-outer-attribute-in-body-2.rs index ad9e7ad707b..e2a62922bcc 100644 --- a/tests/ui/parser/attribute/properly-recover-from-trailing-outer-attribute-in-body-2.rs +++ b/tests/ui/parser/attribute/properly-recover-from-trailing-outer-attribute-in-body-2.rs @@ -5,7 +5,7 @@ macro_rules! the_macro { #[cfg()] $foo //~ ERROR expected `;`, found `#` - #[cfg(bar)] + #[cfg(FALSE)] $bar }; } diff --git a/tests/ui/parser/attribute/properly-recover-from-trailing-outer-attribute-in-body-2.stderr b/tests/ui/parser/attribute/properly-recover-from-trailing-outer-attribute-in-body-2.stderr index 7b9b8319674..fa4409f73fa 100644 --- a/tests/ui/parser/attribute/properly-recover-from-trailing-outer-attribute-in-body-2.stderr +++ b/tests/ui/parser/attribute/properly-recover-from-trailing-outer-attribute-in-body-2.stderr @@ -6,7 +6,7 @@ LL | #[cfg()] LL | $foo | ^ expected `;` here LL | -LL | #[cfg(bar)] +LL | #[cfg(FALSE)] | - unexpected token ... LL | the_macro!( (); (); ); diff --git a/tests/ui/parser/issues/issue-103381.fixed b/tests/ui/parser/issues/issue-103381.fixed index 9b63bf206a0..87c308789a1 100644 --- a/tests/ui/parser/issues/issue-103381.fixed +++ b/tests/ui/parser/issues/issue-103381.fixed @@ -37,21 +37,6 @@ fn should_ok_3() { if true && if true { true } else { false } {} } -fn shoule_match_ok() { - #[cfg(feature = "full")] - { - let a = 1; - let b = 2; - if match a { - 1 if b == 1 => true, - _ => false, - } && if a > 1 { true } else { false } - { - true - } - } -} - fn should_ok_in_nested() { if true && if true { true } else { false } { true } else { false }; } diff --git a/tests/ui/parser/issues/issue-103381.rs b/tests/ui/parser/issues/issue-103381.rs index a44a7410aaf..ccbc40e5d02 100644 --- a/tests/ui/parser/issues/issue-103381.rs +++ b/tests/ui/parser/issues/issue-103381.rs @@ -37,21 +37,6 @@ fn should_ok_3() { if true && if true { true } else { false } {} } -fn shoule_match_ok() { - #[cfg(feature = "full")] - { - let a = 1; - let b = 2; - if match a { - 1 if b == 1 => true, - _ => false, - } && if a > 1 { true } else { false } - { - true - } - } -} - fn should_ok_in_nested() { if true && if true { true } else { false } { true } else { false }; } diff --git a/tests/ui/privacy/unnameable_types.rs b/tests/ui/privacy/unnameable_types.rs index c6c5561c3c4..e82bfd0477f 100644 --- a/tests/ui/privacy/unnameable_types.rs +++ b/tests/ui/privacy/unnameable_types.rs @@ -1,4 +1,3 @@ -#![feature(type_privacy_lints)] #![deny(unnameable_types)] mod m { diff --git a/tests/ui/privacy/unnameable_types.stderr b/tests/ui/privacy/unnameable_types.stderr index d68a11c9728..a1bc60ef9c2 100644 --- a/tests/ui/privacy/unnameable_types.stderr +++ b/tests/ui/privacy/unnameable_types.stderr @@ -1,23 +1,23 @@ error: struct `PubStruct` is reachable but cannot be named - --> $DIR/unnameable_types.rs:5:5 + --> $DIR/unnameable_types.rs:4:5 | LL | pub struct PubStruct(pub i32); | ^^^^^^^^^^^^^^^^^^^^ reachable at visibility `pub`, but can only be named at visibility `pub(crate)` | note: the lint level is defined here - --> $DIR/unnameable_types.rs:2:9 + --> $DIR/unnameable_types.rs:1:9 | LL | #![deny(unnameable_types)] | ^^^^^^^^^^^^^^^^ error: enum `PubE` is reachable but cannot be named - --> $DIR/unnameable_types.rs:7:5 + --> $DIR/unnameable_types.rs:6:5 | LL | pub enum PubE { | ^^^^^^^^^^^^^ reachable at visibility `pub`, but can only be named at visibility `pub(crate)` error: trait `PubTr` is reachable but cannot be named - --> $DIR/unnameable_types.rs:11:5 + --> $DIR/unnameable_types.rs:10:5 | LL | pub trait PubTr { | ^^^^^^^^^^^^^^^ reachable at visibility `pub`, but can only be named at visibility `pub(crate)` diff --git a/tests/ui/proc-macro/modify-ast.rs b/tests/ui/proc-macro/modify-ast.rs index 86a7d6a7772..4c125c1c6e8 100644 --- a/tests/ui/proc-macro/modify-ast.rs +++ b/tests/ui/proc-macro/modify-ast.rs @@ -7,7 +7,7 @@ use modify_ast::*; #[derive(Foo)] pub struct MyStructc { - #[cfg_attr(my_cfg, foo)] + #[cfg_attr(FALSE, foo)] _a: i32, } diff --git a/tests/ui/issues/issue-23302-enum-infinite-recursion/issue-23302-1.rs b/tests/ui/recursion/issue-23302-1.rs index 24e79dc5811..24e79dc5811 100644 --- a/tests/ui/issues/issue-23302-enum-infinite-recursion/issue-23302-1.rs +++ b/tests/ui/recursion/issue-23302-1.rs diff --git a/tests/ui/issues/issue-23302-enum-infinite-recursion/issue-23302-1.stderr b/tests/ui/recursion/issue-23302-1.stderr index 234060ab5c8..234060ab5c8 100644 --- a/tests/ui/issues/issue-23302-enum-infinite-recursion/issue-23302-1.stderr +++ b/tests/ui/recursion/issue-23302-1.stderr diff --git a/tests/ui/issues/issue-23302-enum-infinite-recursion/issue-23302-2.rs b/tests/ui/recursion/issue-23302-2.rs index e89c7eab503..e89c7eab503 100644 --- a/tests/ui/issues/issue-23302-enum-infinite-recursion/issue-23302-2.rs +++ b/tests/ui/recursion/issue-23302-2.rs diff --git a/tests/ui/issues/issue-23302-enum-infinite-recursion/issue-23302-2.stderr b/tests/ui/recursion/issue-23302-2.stderr index 9bd95239c83..9bd95239c83 100644 --- a/tests/ui/issues/issue-23302-enum-infinite-recursion/issue-23302-2.stderr +++ b/tests/ui/recursion/issue-23302-2.stderr diff --git a/tests/ui/issues/issue-23302-enum-infinite-recursion/issue-23302-3.rs b/tests/ui/recursion/issue-23302-3.rs index da75f330798..da75f330798 100644 --- a/tests/ui/issues/issue-23302-enum-infinite-recursion/issue-23302-3.rs +++ b/tests/ui/recursion/issue-23302-3.rs diff --git a/tests/ui/issues/issue-23302-enum-infinite-recursion/issue-23302-3.stderr b/tests/ui/recursion/issue-23302-3.stderr index 8a152f58966..8a152f58966 100644 --- a/tests/ui/issues/issue-23302-enum-infinite-recursion/issue-23302-3.stderr +++ b/tests/ui/recursion/issue-23302-3.stderr diff --git a/tests/ui/sanitizer/cfg-kasan.rs b/tests/ui/sanitizer/cfg-kasan.rs index 394bf216581..491eaf3acc1 100644 --- a/tests/ui/sanitizer/cfg-kasan.rs +++ b/tests/ui/sanitizer/cfg-kasan.rs @@ -2,7 +2,7 @@ // the `#[cfg(sanitize = "address")]` attribute is configured. //@ check-pass -//@ compile-flags: -Zsanitizer=kernel-address --cfg kernel_address +//@ compile-flags: -Zsanitizer=kernel-address //@ revisions: aarch64 riscv64imac riscv64gc x86_64 //@[aarch64] compile-flags: --target aarch64-unknown-none //@[aarch64] needs-llvm-components: aarch64 @@ -22,5 +22,5 @@ trait Sized {} const _: fn() -> () = main; -#[cfg(all(sanitize = "address", kernel_address))] +#[cfg(sanitize = "address")] fn main() {} diff --git a/tests/ui/sanitizer/cfi-method-fn-ptr-cast.rs b/tests/ui/sanitizer/cfi-fn-ptr.rs index 8f79de11748..505b4b8e7f0 100644 --- a/tests/ui/sanitizer/cfi-method-fn-ptr-cast.rs +++ b/tests/ui/sanitizer/cfi-fn-ptr.rs @@ -1,4 +1,4 @@ -// Verifies that casting a method to a function pointer works. +// Verifies that casting to a function pointer works. //@ revisions: cfi kcfi // FIXME(#122848) Remove only-linux once OSX CFI binaries work @@ -46,6 +46,8 @@ impl Trait1 for Type1 { fn foo(&self) {} } +fn foo<T>(_: &T) {} + fn main() { let type1 = Type1 {}; let f = <Type1 as Trait1>::foo; @@ -53,5 +55,7 @@ fn main() { // Check again with different optimization barriers S2 { f: <S as Foo>::foo }.foo(&S); // Check mismatched #[track_caller] - S2 { f: <S as Foo>::bar }.foo(&S) + S2 { f: <S as Foo>::bar }.foo(&S); + // Check non-method functions + S2 { f: foo }.foo(&S) } diff --git a/tests/ui/sanitizer/kcfi-mangling.rs b/tests/ui/sanitizer/kcfi-mangling.rs new file mode 100644 index 00000000000..fde7b5451b6 --- /dev/null +++ b/tests/ui/sanitizer/kcfi-mangling.rs @@ -0,0 +1,30 @@ +// Check KCFI extra mangling works correctly on v0 + +//@ needs-sanitizer-kcfi +//@ no-prefer-dynamic +//@ compile-flags: -C panic=abort -Zsanitizer=kcfi -C symbol-mangling-version=v0 +//@ build-pass + +trait Foo { + fn foo(&self); +} + +struct Bar; +impl Foo for Bar { + fn foo(&self) {} +} + +struct Baz; +impl Foo for Baz { + #[track_caller] + fn foo(&self) {} +} + +fn main() { + // Produces `ReifyShim(_, ReifyReason::FnPtr)` + let f: fn(&Bar) = Bar::foo; + f(&Bar); + // Produces `ReifyShim(_, ReifyReason::Vtable)` + let v: &dyn Foo = &Baz as _; + v.foo(); +} diff --git a/tests/ui/stmt_expr_attrs_no_feature.rs b/tests/ui/stmt_expr_attrs_no_feature.rs index 627c97da008..a160a9bb082 100644 --- a/tests/ui/stmt_expr_attrs_no_feature.rs +++ b/tests/ui/stmt_expr_attrs_no_feature.rs @@ -25,25 +25,25 @@ fn main() { // Check that cfg works right -#[cfg(unset)] +#[cfg(FALSE)] fn c() { #[rustc_dummy] 5; } -#[cfg(not(unset))] +#[cfg(not(FALSE))] fn j() { #[rustc_dummy] 5; } -#[cfg_attr(not(unset), cfg(unset))] +#[cfg_attr(not(FALSE), cfg(FALSE))] fn d() { #[rustc_dummy] 8; } -#[cfg_attr(not(unset), cfg(not(unset)))] +#[cfg_attr(not(FALSE), cfg(not(FALSE)))] fn i() { #[rustc_dummy] 8; @@ -57,25 +57,25 @@ macro_rules! item_mac { #[rustc_dummy] 42; - #[cfg(unset)] + #[cfg(FALSE)] fn f() { #[rustc_dummy] 5; } - #[cfg(not(unset))] + #[cfg(not(FALSE))] fn k() { #[rustc_dummy] 5; } - #[cfg_attr(not(unset), cfg(unset))] + #[cfg_attr(not(FALSE), cfg(FALSE))] fn g() { #[rustc_dummy] 8; } - #[cfg_attr(not(unset), cfg(not(unset)))] + #[cfg_attr(not(FALSE), cfg(not(FALSE)))] fn h() { #[rustc_dummy] 8; @@ -90,42 +90,42 @@ item_mac!(e); // check that the gate visitor works right: extern "C" { - #[cfg(unset)] + #[cfg(FALSE)] fn x(a: [u8; #[rustc_dummy] 5]); fn y(a: [u8; #[rustc_dummy] 5]); //~ ERROR attributes on expressions are experimental } struct Foo; impl Foo { - #[cfg(unset)] + #[cfg(FALSE)] const X: u8 = #[rustc_dummy] 5; const Y: u8 = #[rustc_dummy] 5; //~ ERROR attributes on expressions are experimental } trait Bar { - #[cfg(unset)] + #[cfg(FALSE)] const X: [u8; #[rustc_dummy] 5]; const Y: [u8; #[rustc_dummy] 5]; //~ ERROR attributes on expressions are experimental } struct Joyce { - #[cfg(unset)] + #[cfg(FALSE)] field: [u8; #[rustc_dummy] 5], field2: [u8; #[rustc_dummy] 5] //~ ERROR attributes on expressions are experimental } struct Walky( - #[cfg(unset)] [u8; #[rustc_dummy] 5], + #[cfg(FALSE)] [u8; #[rustc_dummy] 5], [u8; #[rustc_dummy] 5] //~ ERROR attributes on expressions are experimental ); enum Mike { Happy( - #[cfg(unset)] [u8; #[rustc_dummy] 5], + #[cfg(FALSE)] [u8; #[rustc_dummy] 5], [u8; #[rustc_dummy] 5] //~ ERROR attributes on expressions are experimental ), Angry { - #[cfg(unset)] + #[cfg(FALSE)] field: [u8; #[rustc_dummy] 5], field2: [u8; #[rustc_dummy] 5] //~ ERROR attributes on expressions are experimental } @@ -133,7 +133,7 @@ enum Mike { fn pat() { match 5 { - #[cfg(unset)] + #[cfg(FALSE)] 5 => #[rustc_dummy] (), 6 => #[rustc_dummy] (), //~ ERROR attributes on expressions are experimental _ => (), diff --git a/tests/ui/suggestions/option-to-bool.rs b/tests/ui/suggestions/option-to-bool.rs index 2a1823b15f5..bbc5d1d71cb 100644 --- a/tests/ui/suggestions/option-to-bool.rs +++ b/tests/ui/suggestions/option-to-bool.rs @@ -1,5 +1,3 @@ -#![cfg_attr(let_chains, feature(let_chains))] - fn foo(x: Option<i32>) { if true && x {} //~^ ERROR mismatched types diff --git a/tests/ui/suggestions/option-to-bool.stderr b/tests/ui/suggestions/option-to-bool.stderr index e16d829ca7a..ab97eae6c5c 100644 --- a/tests/ui/suggestions/option-to-bool.stderr +++ b/tests/ui/suggestions/option-to-bool.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/option-to-bool.rs:4:16 + --> $DIR/option-to-bool.rs:2:16 | LL | if true && x {} | ---- ^ expected `bool`, found `Option<i32>` diff --git a/tests/ui/symbol-names/basic.legacy.stderr b/tests/ui/symbol-names/basic.legacy.stderr index c1cbefac828..6ce0ae09195 100644 --- a/tests/ui/symbol-names/basic.legacy.stderr +++ b/tests/ui/symbol-names/basic.legacy.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_ZN5basic4main17h6fc0c8d27b1a289fE) +error: symbol-name(_ZN5basic4main17had874e876c8b1028E) --> $DIR/basic.rs:8:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(basic::main::h6fc0c8d27b1a289f) +error: demangling(basic::main::had874e876c8b1028) --> $DIR/basic.rs:8:1 | LL | #[rustc_symbol_name] diff --git a/tests/ui/symbol-names/issue-60925.legacy.stderr b/tests/ui/symbol-names/issue-60925.legacy.stderr index 7dd68e6e3a8..cc4eec470fb 100644 --- a/tests/ui/symbol-names/issue-60925.legacy.stderr +++ b/tests/ui/symbol-names/issue-60925.legacy.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17hab58a402db4ebf3aE) +error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17haf0d0ad2255e29c6E) --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(issue_60925::foo::Foo<issue_60925::llvm::Foo>::foo::hab58a402db4ebf3a) +error: demangling(issue_60925::foo::Foo<issue_60925::llvm::Foo>::foo::haf0d0ad2255e29c6) --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_symbol_name] diff --git a/tests/ui/issues/issue-2611-3.rs b/tests/ui/traits/issue-2611-3.rs index c95ebae33fa..c95ebae33fa 100644 --- a/tests/ui/issues/issue-2611-3.rs +++ b/tests/ui/traits/issue-2611-3.rs diff --git a/tests/ui/traits/pred-known-to-hold-modulo-regions-unsized-tail.rs b/tests/ui/traits/pred-known-to-hold-modulo-regions-unsized-tail.rs new file mode 100644 index 00000000000..4e8c19d600d --- /dev/null +++ b/tests/ui/traits/pred-known-to-hold-modulo-regions-unsized-tail.rs @@ -0,0 +1,244 @@ +// This is a non-regression test for issues #108721 and its duplicate #123275 (hopefully, because +// the test is still convoluted and the ICE is fiddly). +// +// `pred_known_to_hold_modulo_regions` prevented "unexpected unsized tail" ICEs with warp/hyper but +// was unknowingly removed in #120463. + +//@ build-pass: the ICE happened in codegen + +use std::future::Future; +trait TryFuture: Future { + type Ok; +} +impl<F, T> TryFuture for F +where + F: ?Sized + Future<Output = Option<T>>, +{ + type Ok = T; +} +trait Executor {} +struct Exec {} +trait HttpBody { + type Data; +} +trait ConnStreamExec<F> {} +impl<F> ConnStreamExec<F> for Exec where H2Stream<F>: Send {} +impl<E, F> ConnStreamExec<F> for E where E: Executor {} +struct H2Stream<F> { + _fut: F, +} +trait NewSvcExec<S, E, W: Watcher<S, E>> { + fn execute_new_svc(&mut self, _fut: NewSvcTask<S, E, W>) { + unimplemented!() + } +} +impl<S, E, W> NewSvcExec<S, E, W> for Exec where W: Watcher<S, E> {} +trait Watcher<S, E> { + type Future; +} +struct NoopWatcher; +impl<S, E> Watcher<S, E> for NoopWatcher +where + S: HttpService, + E: ConnStreamExec<S::Future>, +{ + type Future = Option<<<S as HttpService>::ResBody as HttpBody>::Data>; +} +trait Service<Request> { + type Response; + type Future; +} +trait HttpService { + type ResBody: HttpBody; + type Future; +} +struct Body {} +impl HttpBody for Body { + type Data = String; +} +impl<S> HttpService for S +where + S: Service<(), Response = ()>, +{ + type ResBody = Body; + type Future = S::Future; +} +trait MakeServiceRef<Target> { + type ResBody; + type Service: HttpService<ResBody = Self::ResBody>; +} +impl<T, Target, S, F> MakeServiceRef<Target> for T +where + T: for<'a> Service<&'a Target, Response = S, Future = F>, + S: HttpService, +{ + type Service = S; + type ResBody = S::ResBody; +} +fn make_service_fn<F, Target, Ret>(_f: F) -> MakeServiceFn<F> +where + F: FnMut(&Target) -> Ret, + Ret: Future, +{ + unimplemented!() +} +struct MakeServiceFn<F> { + _func: F, +} +impl<'t, F, Ret, Target, Svc> Service<&'t Target> for MakeServiceFn<F> +where + F: FnMut(&Target) -> Ret, + Ret: Future<Output = Option<Svc>>, +{ + type Response = Svc; + type Future = Option<()>; +} +struct AddrIncoming {} +struct Server<I, S, E> { + _incoming: I, + _make_service: S, + _protocol: E, +} +impl<I, S, E, B> Server<I, S, E> +where + S: MakeServiceRef<(), ResBody = B>, + B: HttpBody, + E: ConnStreamExec<<S::Service as HttpService>::Future>, + E: NewSvcExec<S::Service, E, NoopWatcher>, +{ + fn serve(&mut self) { + let fut = NewSvcTask::new(); + self._protocol.execute_new_svc(fut); + } +} +fn serve<S>(_make_service: S) -> Server<AddrIncoming, S, Exec> { + unimplemented!() +} +struct NewSvcTask<S, E, W: Watcher<S, E>> { + _state: State<S, E, W>, +} +struct State<S, E, W: Watcher<S, E>> { + _fut: W::Future, +} +impl<S, E, W: Watcher<S, E>> NewSvcTask<S, E, W> { + fn new() -> Self { + unimplemented!() + } +} +trait Filter { + type Extract; + type Future; + fn map<F>(self, _fun: F) -> MapFilter<Self, F> + where + Self: Sized, + { + unimplemented!() + } + fn wrap_with<W>(self, _wrapper: W) -> W::Wrapped + where + Self: Sized, + W: Wrap<Self>, + { + unimplemented!() + } +} +fn service<F>(_filter: F) -> FilteredService<F> +where + F: Filter, +{ + unimplemented!() +} +struct FilteredService<F> { + _filter: F, +} +impl<F> Service<()> for FilteredService<F> +where + F: Filter, +{ + type Response = (); + type Future = FilteredFuture<F::Future>; +} +struct FilteredFuture<F> { + _fut: F, +} +struct MapFilter<T, F> { + _filter: T, + _func: F, +} +impl<T, F> Filter for MapFilter<T, F> +where + T: Filter, + F: Func<T::Extract>, +{ + type Extract = F::Output; + type Future = MapFilterFuture<T, F>; +} +struct MapFilterFuture<T: Filter, F> { + _extract: T::Future, + _func: F, +} +trait Wrap<F> { + type Wrapped; +} +fn make_filter_fn<F, U>(_func: F) -> FilterFn<F> +where + F: Fn() -> U, +{ + unimplemented!() +} +struct FilterFn<F> { + _func: F, +} +impl<F, U> Filter for FilterFn<F> +where + F: Fn() -> U, + U: TryFuture, + U::Ok: Send, +{ + type Extract = U::Ok; + type Future = Option<U>; +} +fn trace<F>(_func: F) -> Trace<F> +where + F: Fn(), +{ + unimplemented!() +} +struct Trace<F> { + _func: F, +} +impl<FN, F> Wrap<F> for Trace<FN> { + type Wrapped = WithTrace<FN, F>; +} +struct WithTrace<FN, F> { + _filter: F, + _trace: FN, +} +impl<FN, F> Filter for WithTrace<FN, F> +where + F: Filter, +{ + type Extract = (); + type Future = (F::Future, fn(F::Extract)); +} +trait Func<Args> { + type Output; +} +impl<F, R> Func<()> for F +where + F: Fn() -> R, +{ + type Output = R; +} +fn main() { + let make_service = make_service_fn(|_| { + let tracer = trace(|| unimplemented!()); + let filter = make_filter_fn(|| std::future::ready(Some(()))) + .map(|| "Hello, world") + .wrap_with(tracer); + let svc = service(filter); + std::future::ready(Some(svc)) + }); + let mut server = serve(make_service); + server.serve(); +} diff --git a/tests/ui/transmutability/arrays/huge-len.stderr b/tests/ui/transmutability/arrays/huge-len.stderr index 37160c5c959..3fc652f47c1 100644 --- a/tests/ui/transmutability/arrays/huge-len.stderr +++ b/tests/ui/transmutability/arrays/huge-len.stderr @@ -2,7 +2,7 @@ error[E0277]: `()` cannot be safely transmuted into `ExplicitlyPadded` --> $DIR/huge-len.rs:21:41 | LL | assert::is_maybe_transmutable::<(), ExplicitlyPadded>(); - | ^^^^^^^^^^^^^^^^ values of the type `ExplicitlyPadded` are too big for the current architecture + | ^^^^^^^^^^^^^^^^ analyzing the transmutability of `ExplicitlyPadded` is not yet supported | note: required by a bound in `is_maybe_transmutable` --> $DIR/huge-len.rs:8:14 @@ -17,7 +17,7 @@ error[E0277]: `ExplicitlyPadded` cannot be safely transmuted into `()` --> $DIR/huge-len.rs:24:55 | LL | assert::is_maybe_transmutable::<ExplicitlyPadded, ()>(); - | ^^ values of the type `ExplicitlyPadded` are too big for the current architecture + | ^^ analyzing the transmutability of `ExplicitlyPadded` is not yet supported | note: required by a bound in `is_maybe_transmutable` --> $DIR/huge-len.rs:8:14 diff --git a/tests/ui/transmutability/arrays/should_require_well_defined_layout.stderr b/tests/ui/transmutability/arrays/should_require_well_defined_layout.stderr index e486928a445..b4cd70142c4 100644 --- a/tests/ui/transmutability/arrays/should_require_well_defined_layout.stderr +++ b/tests/ui/transmutability/arrays/should_require_well_defined_layout.stderr @@ -2,7 +2,7 @@ error[E0277]: `[String; 0]` cannot be safely transmuted into `()` --> $DIR/should_require_well_defined_layout.rs:25:52 | LL | assert::is_maybe_transmutable::<repr_rust, ()>(); - | ^^ analyzing the transmutability of `[String; 0]` is not yet supported. + | ^^ analyzing the transmutability of `[String; 0]` is not yet supported | note: required by a bound in `is_maybe_transmutable` --> $DIR/should_require_well_defined_layout.rs:12:14 @@ -23,7 +23,7 @@ error[E0277]: `u128` cannot be safely transmuted into `[String; 0]` --> $DIR/should_require_well_defined_layout.rs:26:47 | LL | assert::is_maybe_transmutable::<u128, repr_rust>(); - | ^^^^^^^^^ analyzing the transmutability of `[String; 0]` is not yet supported. + | ^^^^^^^^^ analyzing the transmutability of `[String; 0]` is not yet supported | note: required by a bound in `is_maybe_transmutable` --> $DIR/should_require_well_defined_layout.rs:12:14 @@ -44,7 +44,7 @@ error[E0277]: `[String; 1]` cannot be safely transmuted into `()` --> $DIR/should_require_well_defined_layout.rs:31:52 | LL | assert::is_maybe_transmutable::<repr_rust, ()>(); - | ^^ analyzing the transmutability of `[String; 1]` is not yet supported. + | ^^ analyzing the transmutability of `[String; 1]` is not yet supported | note: required by a bound in `is_maybe_transmutable` --> $DIR/should_require_well_defined_layout.rs:12:14 @@ -65,7 +65,7 @@ error[E0277]: `u128` cannot be safely transmuted into `[String; 1]` --> $DIR/should_require_well_defined_layout.rs:32:47 | LL | assert::is_maybe_transmutable::<u128, repr_rust>(); - | ^^^^^^^^^ analyzing the transmutability of `[String; 1]` is not yet supported. + | ^^^^^^^^^ analyzing the transmutability of `[String; 1]` is not yet supported | note: required by a bound in `is_maybe_transmutable` --> $DIR/should_require_well_defined_layout.rs:12:14 @@ -86,7 +86,7 @@ error[E0277]: `[String; 2]` cannot be safely transmuted into `()` --> $DIR/should_require_well_defined_layout.rs:37:52 | LL | assert::is_maybe_transmutable::<repr_rust, ()>(); - | ^^ analyzing the transmutability of `[String; 2]` is not yet supported. + | ^^ analyzing the transmutability of `[String; 2]` is not yet supported | note: required by a bound in `is_maybe_transmutable` --> $DIR/should_require_well_defined_layout.rs:12:14 @@ -107,7 +107,7 @@ error[E0277]: `u128` cannot be safely transmuted into `[String; 2]` --> $DIR/should_require_well_defined_layout.rs:38:47 | LL | assert::is_maybe_transmutable::<u128, repr_rust>(); - | ^^^^^^^^^ analyzing the transmutability of `[String; 2]` is not yet supported. + | ^^^^^^^^^ analyzing the transmutability of `[String; 2]` is not yet supported | note: required by a bound in `is_maybe_transmutable` --> $DIR/should_require_well_defined_layout.rs:12:14 diff --git a/tests/ui/transmutability/enums/niche_optimization.rs b/tests/ui/transmutability/enums/niche_optimization.rs new file mode 100644 index 00000000000..23f57ecad75 --- /dev/null +++ b/tests/ui/transmutability/enums/niche_optimization.rs @@ -0,0 +1,156 @@ +//@ check-pass +//! Checks that niche optimizations are encoded correctly. +#![crate_type = "lib"] +#![feature(transmutability)] +#![allow(dead_code, incomplete_features, non_camel_case_types)] + +mod assert { + use std::mem::{Assume, BikeshedIntrinsicFrom}; + + pub fn is_transmutable<Src, Dst>() + where + Dst: BikeshedIntrinsicFrom<Src, { + Assume { + alignment: false, + lifetimes: false, + safety: true, + validity: false, + } + }> + {} + + pub fn is_maybe_transmutable<Src, Dst>() + where + Dst: BikeshedIntrinsicFrom<Src, { + Assume { + alignment: false, + lifetimes: false, + safety: true, + validity: true, + } + }> + {} +} + +#[repr(u8)] enum V0 { V = 0 } +#[repr(u8)] enum V1 { V = 1 } +#[repr(u8)] enum V2 { V = 2 } +#[repr(u8)] enum V253 { V = 253 } +#[repr(u8)] enum V254 { V = 254 } +#[repr(u8)] enum V255 { V = 255 } + +fn bool() { + enum OptionLike { + A(bool), + B, + } + + const _: () = { + assert!(std::mem::size_of::<OptionLike>() == 1); + }; + + assert::is_transmutable::<OptionLike, u8>(); + + assert::is_transmutable::<bool, OptionLike>(); + assert::is_transmutable::<V0, OptionLike>(); + assert::is_transmutable::<V1, OptionLike>(); + assert::is_transmutable::<V2, OptionLike>(); +} + +fn one_niche() { + #[repr(u8)] + enum N1 { + S = 0, + E = 255 - 1, + } + + enum OptionLike { + A(N1), + B, + } + + const _: () = { + assert!(std::mem::size_of::<OptionLike>() == 1); + }; + + assert::is_transmutable::<OptionLike, u8>(); + assert::is_transmutable::<V0, OptionLike>(); + assert::is_transmutable::<V254, OptionLike>(); + assert::is_transmutable::<V255, OptionLike>(); +} + +fn one_niche_alt() { + #[repr(u8)] + enum N1 { + S = 1, + E = 255 - 1, + } + + enum OptionLike { + A(N1), + B, + C, + } + + const _: () = { + assert!(std::mem::size_of::<OptionLike>() == 1); + }; + + assert::is_transmutable::<OptionLike, u8>(); + assert::is_transmutable::<V0, OptionLike>(); + assert::is_transmutable::<V254, OptionLike>(); + assert::is_transmutable::<V255, OptionLike>(); +} + +fn two_niche() { + #[repr(u8)] + enum Niche { + S = 0, + E = 255 - 2, + } + + enum OptionLike { + A(Niche), + B, + C, + } + + const _: () = { + assert!(std::mem::size_of::<OptionLike>() == 1); + }; + + assert::is_transmutable::<OptionLike, u8>(); + assert::is_transmutable::<V0, OptionLike>(); + assert::is_transmutable::<V253, OptionLike>(); + assert::is_transmutable::<V254, OptionLike>(); + assert::is_transmutable::<V255, OptionLike>(); +} + +fn no_niche() { + use std::mem::MaybeUninit; + + #[repr(u8)] + enum Niche { + S = 0, + E = 255 - 1, + } + + enum OptionLike { + A(Niche), + B, + C, + } + + const _: () = { + assert!(std::mem::size_of::<OptionLike>() == 2); + }; + + #[repr(C)] + struct Pair<T, U>(T, U); + + assert::is_transmutable::<V0, Niche>(); + assert::is_transmutable::<V254, Niche>(); + assert::is_transmutable::<Pair<V0, Niche>, OptionLike>(); + assert::is_transmutable::<Pair<V1, MaybeUninit<u8>>, OptionLike>(); + assert::is_transmutable::<Pair<V2, MaybeUninit<u8>>, OptionLike>(); +} diff --git a/tests/ui/transmutability/enums/repr/padding_differences.rs b/tests/ui/transmutability/enums/repr/padding_differences.rs new file mode 100644 index 00000000000..d0e1502b5e2 --- /dev/null +++ b/tests/ui/transmutability/enums/repr/padding_differences.rs @@ -0,0 +1,80 @@ +//@ check-pass +//! Adapted from https://rust-lang.github.io/unsafe-code-guidelines/layout/enums.html#explicit-repr-annotation-without-c-compatibility +#![crate_type = "lib"] +#![feature(transmutability)] +#![allow(dead_code, incomplete_features, non_camel_case_types)] + +use std::mem::MaybeUninit; + +mod assert { + use std::mem::{Assume, BikeshedIntrinsicFrom}; + + pub fn is_transmutable<Src, Dst>() + where + Dst: BikeshedIntrinsicFrom<Src, { + Assume { + alignment: false, + lifetimes: false, + safety: true, + validity: false, + } + }> + {} + + pub fn is_maybe_transmutable<Src, Dst>() + where + Dst: BikeshedIntrinsicFrom<Src, { + Assume { + alignment: false, + lifetimes: false, + safety: true, + validity: true, + } + }> + {} +} + +#[repr(u8)] enum V0 { V = 0 } +#[repr(u8)] enum V1 { V = 1 } + +fn repr_u8() { + #[repr(u8)] + enum TwoCases { + A(u8, u16), // 0x00 INIT INIT INIT + B(u16), // 0x01 PADD INIT INIT + } + + const _: () = { + assert!(std::mem::size_of::<TwoCases>() == 4); + }; + + #[repr(C)] struct TwoCasesA(V0, u8, u8, u8); + #[repr(C)] struct TwoCasesB(V1, MaybeUninit<u8>, u8, u8); + + assert::is_transmutable::<TwoCasesA, TwoCases>(); + assert::is_transmutable::<TwoCasesB, TwoCases>(); + + assert::is_maybe_transmutable::<TwoCases, TwoCasesA>(); + assert::is_maybe_transmutable::<TwoCases, TwoCasesB>(); +} + +fn repr_c_u8() { + #[repr(C, u8)] + enum TwoCases { + A(u8, u16), // 0x00 PADD INIT PADD INIT INIT + B(u16), // 0x01 PADD INIT INIT PADD PADD + } + + const _: () = { + assert!(std::mem::size_of::<TwoCases>() == 6); + }; + + #[repr(C)] struct TwoCasesA(V0, MaybeUninit<u8>, u8, MaybeUninit<u8>, u8, u8); + #[repr(C)] struct TwoCasesB(V1, MaybeUninit<u8>, u8, u8, MaybeUninit<u8>, MaybeUninit<u8>); + + assert::is_transmutable::<TwoCasesA, TwoCases>(); + assert::is_transmutable::<TwoCasesB, TwoCases>(); + + assert::is_maybe_transmutable::<TwoCases, TwoCasesA>(); + assert::is_maybe_transmutable::<TwoCases, TwoCasesB>(); +} diff --git a/tests/ui/transmutability/enums/repr/should_require_well_defined_layout.rs b/tests/ui/transmutability/enums/repr/should_handle_all.rs index 630e662b926..a8ec86fa40d 100644 --- a/tests/ui/transmutability/enums/repr/should_require_well_defined_layout.rs +++ b/tests/ui/transmutability/enums/repr/should_handle_all.rs @@ -1,5 +1,4 @@ -//! An enum must have a well-defined layout to participate in a transmutation. - +//@ check-pass #![crate_type = "lib"] #![feature(repr128)] #![feature(transmutability)] @@ -21,23 +20,17 @@ mod assert { {} } -fn should_reject_repr_rust() { - fn void() { - enum repr_rust {} - assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted - assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted - } - +fn should_accept_repr_rust() { fn singleton() { enum repr_rust { V } - assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted - assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted + assert::is_maybe_transmutable::<repr_rust, ()>(); + assert::is_maybe_transmutable::<u128, repr_rust>(); } fn duplex() { enum repr_rust { A, B } - assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted - assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted + assert::is_maybe_transmutable::<repr_rust, ()>(); + assert::is_maybe_transmutable::<u128, repr_rust>(); } } @@ -116,7 +109,7 @@ fn should_accept_primitive_reprs() } } -fn should_accept_repr_C() { +fn should_accept_repr_c() { #[repr(C)] enum repr_c { V } assert::is_maybe_transmutable::<repr_c, ()>(); assert::is_maybe_transmutable::<i128, repr_c>(); diff --git a/tests/ui/transmutability/enums/repr/should_require_well_defined_layout.stderr b/tests/ui/transmutability/enums/repr/should_require_well_defined_layout.stderr deleted file mode 100644 index 2a683de6a65..00000000000 --- a/tests/ui/transmutability/enums/repr/should_require_well_defined_layout.stderr +++ /dev/null @@ -1,135 +0,0 @@ -error[E0277]: `void::repr_rust` cannot be safely transmuted into `()` - --> $DIR/should_require_well_defined_layout.rs:27:52 - | -LL | assert::is_maybe_transmutable::<repr_rust, ()>(); - | ^^ analyzing the transmutability of `void::repr_rust` is not yet supported. - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/should_require_well_defined_layout.rs:13:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom<Src, { - | ______________^ -LL | | Assume { -LL | | alignment: true, -LL | | lifetimes: true, -... | -LL | | } -LL | | }> - | |__________^ required by this bound in `is_maybe_transmutable` - -error[E0277]: `u128` cannot be safely transmuted into `void::repr_rust` - --> $DIR/should_require_well_defined_layout.rs:28:47 - | -LL | assert::is_maybe_transmutable::<u128, repr_rust>(); - | ^^^^^^^^^ analyzing the transmutability of `void::repr_rust` is not yet supported. - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/should_require_well_defined_layout.rs:13:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom<Src, { - | ______________^ -LL | | Assume { -LL | | alignment: true, -LL | | lifetimes: true, -... | -LL | | } -LL | | }> - | |__________^ required by this bound in `is_maybe_transmutable` - -error[E0277]: `singleton::repr_rust` cannot be safely transmuted into `()` - --> $DIR/should_require_well_defined_layout.rs:33:52 - | -LL | assert::is_maybe_transmutable::<repr_rust, ()>(); - | ^^ analyzing the transmutability of `singleton::repr_rust` is not yet supported. - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/should_require_well_defined_layout.rs:13:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom<Src, { - | ______________^ -LL | | Assume { -LL | | alignment: true, -LL | | lifetimes: true, -... | -LL | | } -LL | | }> - | |__________^ required by this bound in `is_maybe_transmutable` - -error[E0277]: `u128` cannot be safely transmuted into `singleton::repr_rust` - --> $DIR/should_require_well_defined_layout.rs:34:47 - | -LL | assert::is_maybe_transmutable::<u128, repr_rust>(); - | ^^^^^^^^^ analyzing the transmutability of `singleton::repr_rust` is not yet supported. - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/should_require_well_defined_layout.rs:13:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom<Src, { - | ______________^ -LL | | Assume { -LL | | alignment: true, -LL | | lifetimes: true, -... | -LL | | } -LL | | }> - | |__________^ required by this bound in `is_maybe_transmutable` - -error[E0277]: `duplex::repr_rust` cannot be safely transmuted into `()` - --> $DIR/should_require_well_defined_layout.rs:39:52 - | -LL | assert::is_maybe_transmutable::<repr_rust, ()>(); - | ^^ analyzing the transmutability of `duplex::repr_rust` is not yet supported. - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/should_require_well_defined_layout.rs:13:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom<Src, { - | ______________^ -LL | | Assume { -LL | | alignment: true, -LL | | lifetimes: true, -... | -LL | | } -LL | | }> - | |__________^ required by this bound in `is_maybe_transmutable` - -error[E0277]: `u128` cannot be safely transmuted into `duplex::repr_rust` - --> $DIR/should_require_well_defined_layout.rs:40:47 - | -LL | assert::is_maybe_transmutable::<u128, repr_rust>(); - | ^^^^^^^^^ analyzing the transmutability of `duplex::repr_rust` is not yet supported. - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/should_require_well_defined_layout.rs:13:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom<Src, { - | ______________^ -LL | | Assume { -LL | | alignment: true, -LL | | lifetimes: true, -... | -LL | | } -LL | | }> - | |__________^ required by this bound in `is_maybe_transmutable` - -error: aborting due to 6 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/malformed-program-gracefulness/unknown_src_field.stderr b/tests/ui/transmutability/malformed-program-gracefulness/unknown_src_field.stderr index eeed8a62a2a..cabc7bcfef7 100644 --- a/tests/ui/transmutability/malformed-program-gracefulness/unknown_src_field.stderr +++ b/tests/ui/transmutability/malformed-program-gracefulness/unknown_src_field.stderr @@ -8,7 +8,7 @@ error[E0277]: `Src` cannot be safely transmuted into `Dst` --> $DIR/unknown_src_field.rs:19:36 | LL | assert::is_transmutable::<Src, Dst>(); - | ^^^ `Dst` has an unknown layout + | ^^^ analyzing the transmutability of `Dst` is not yet supported | note: required by a bound in `is_transmutable` --> $DIR/unknown_src_field.rs:12:14 diff --git a/tests/ui/transmutability/maybeuninit.rs b/tests/ui/transmutability/maybeuninit.rs new file mode 100644 index 00000000000..77c3381c774 --- /dev/null +++ b/tests/ui/transmutability/maybeuninit.rs @@ -0,0 +1,43 @@ +#![crate_type = "lib"] +#![feature(transmutability)] +#![allow(dead_code, incomplete_features, non_camel_case_types)] + +use std::mem::MaybeUninit; + +mod assert { + use std::mem::{Assume, BikeshedIntrinsicFrom}; + + pub fn is_maybe_transmutable<Src, Dst>() + where + Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }> + {} +} + +fn validity() { + // An initialized byte is a valid uninitialized byte. + assert::is_maybe_transmutable::<u8, MaybeUninit<u8>>(); + + // An uninitialized byte is never a valid initialized byte. + assert::is_maybe_transmutable::<MaybeUninit<u8>, u8>(); //~ ERROR: cannot be safely transmuted +} + +fn padding() { + #[repr(align(8))] + struct Align8; + + #[repr(u8)] + enum ImplicitlyPadded { + A(Align8), + } + + #[repr(u8)] + enum V0 { + V0 = 0, + } + + #[repr(C)] + struct ExplicitlyPadded(V0, MaybeUninit<[u8; 7]>); + + assert::is_maybe_transmutable::<ExplicitlyPadded, ImplicitlyPadded>(); + assert::is_maybe_transmutable::<ImplicitlyPadded, ExplicitlyPadded>(); +} diff --git a/tests/ui/transmutability/maybeuninit.stderr b/tests/ui/transmutability/maybeuninit.stderr new file mode 100644 index 00000000000..be7dcaf35ea --- /dev/null +++ b/tests/ui/transmutability/maybeuninit.stderr @@ -0,0 +1,18 @@ +error[E0277]: `MaybeUninit<u8>` cannot be safely transmuted into `u8` + --> $DIR/maybeuninit.rs:21:54 + | +LL | assert::is_maybe_transmutable::<MaybeUninit<u8>, u8>(); + | ^^ at least one value of `MaybeUninit<u8>` isn't a bit-valid value of `u8` + | +note: required by a bound in `is_maybe_transmutable` + --> $DIR/maybeuninit.rs:12:14 + | +LL | pub fn is_maybe_transmutable<Src, Dst>() + | --------------------- required by a bound in this function +LL | where +LL | Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }> + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/references/unsafecell.rs b/tests/ui/transmutability/references/unsafecell.rs new file mode 100644 index 00000000000..a8a1f969fb4 --- /dev/null +++ b/tests/ui/transmutability/references/unsafecell.rs @@ -0,0 +1,47 @@ +#![crate_type = "lib"] +#![feature(transmutability)] +#![allow(dead_code, incomplete_features, non_camel_case_types)] + +use std::cell::UnsafeCell; + +mod assert { + use std::mem::{Assume, BikeshedIntrinsicFrom}; + + pub fn is_maybe_transmutable<Src, Dst>() + where + Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }> + {} +} + +fn value_to_value() { + // We accept value-to-value transmutations of `UnsafeCell`-containing types, + // because owning a value implies exclusive access. + assert::is_maybe_transmutable::<UnsafeCell<u8>, u8>(); + assert::is_maybe_transmutable::<u8, UnsafeCell<u8>>(); + assert::is_maybe_transmutable::<UnsafeCell<u8>, UnsafeCell<u8>>(); +} + +fn ref_to_ref() { + // We forbid `UnsafeCell`-containing ref-to-ref transmutations, because the + // two types may use different, incompatible synchronization strategies. + assert::is_maybe_transmutable::<&'static u8, &'static UnsafeCell<u8>>(); //~ ERROR: cannot be safely transmuted + + assert::is_maybe_transmutable::<&'static UnsafeCell<u8>, &'static UnsafeCell<u8>>(); //~ ERROR: cannot be safely transmuted +} + +fn mut_to_mut() { + // `UnsafeCell` does't matter for `&mut T` to `&mut U`, since exclusive + // borrows can't be used for shared access. + assert::is_maybe_transmutable::<&'static mut u8, &'static mut UnsafeCell<u8>>(); + assert::is_maybe_transmutable::<&'static mut UnsafeCell<u8>, &'static mut u8>(); + assert::is_maybe_transmutable::<&'static mut UnsafeCell<u8>, &'static mut UnsafeCell<u8>>(); +} + +fn mut_to_ref() { + // We don't care about `UnsafeCell` for transmutations in the form `&mut T + // -> &U`, because downgrading a `&mut T` to a `&U` deactivates `&mut T` for + // the lifetime of `&U`. + assert::is_maybe_transmutable::<&'static mut u8, &'static UnsafeCell<u8>>(); + assert::is_maybe_transmutable::<&'static mut UnsafeCell<u8>, &'static u8>(); + assert::is_maybe_transmutable::<&'static mut UnsafeCell<u8>, &'static UnsafeCell<u8>>(); +} diff --git a/tests/ui/transmutability/references/unsafecell.stderr b/tests/ui/transmutability/references/unsafecell.stderr new file mode 100644 index 00000000000..651eb8ceb26 --- /dev/null +++ b/tests/ui/transmutability/references/unsafecell.stderr @@ -0,0 +1,33 @@ +error[E0277]: `&u8` cannot be safely transmuted into `&UnsafeCell<u8>` + --> $DIR/unsafecell.rs:27:50 + | +LL | assert::is_maybe_transmutable::<&'static u8, &'static UnsafeCell<u8>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Freeze` is not implemented for `&'static UnsafeCell<u8>` + | +note: required by a bound in `is_maybe_transmutable` + --> $DIR/unsafecell.rs:12:14 + | +LL | pub fn is_maybe_transmutable<Src, Dst>() + | --------------------- required by a bound in this function +LL | where +LL | Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }> + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable` + +error[E0277]: `&UnsafeCell<u8>` cannot be safely transmuted into `&UnsafeCell<u8>` + --> $DIR/unsafecell.rs:29:62 + | +LL | assert::is_maybe_transmutable::<&'static UnsafeCell<u8>, &'static UnsafeCell<u8>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Freeze` is not implemented for `&'static UnsafeCell<u8>` + | +note: required by a bound in `is_maybe_transmutable` + --> $DIR/unsafecell.rs:12:14 + | +LL | pub fn is_maybe_transmutable<Src, Dst>() + | --------------------- required by a bound in this function +LL | where +LL | Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }> + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/structs/repr/should_require_well_defined_layout.rs b/tests/ui/transmutability/structs/repr/should_handle_all.rs index 2e673601baf..52c24eecf12 100644 --- a/tests/ui/transmutability/structs/repr/should_require_well_defined_layout.rs +++ b/tests/ui/transmutability/structs/repr/should_handle_all.rs @@ -1,3 +1,4 @@ +//@ check-pass //! A struct must have a well-defined layout to participate in a transmutation. #![crate_type = "lib"] @@ -20,47 +21,47 @@ mod assert { {} } -fn should_reject_repr_rust() +fn should_accept_repr_rust() { fn unit() { struct repr_rust; - assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted - assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted + assert::is_maybe_transmutable::<repr_rust, ()>(); + assert::is_maybe_transmutable::<u128, repr_rust>(); } fn tuple() { struct repr_rust(); - assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted - assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted + assert::is_maybe_transmutable::<repr_rust, ()>(); + assert::is_maybe_transmutable::<u128, repr_rust>(); } fn braces() { struct repr_rust{} - assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted - assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted + assert::is_maybe_transmutable::<repr_rust, ()>(); + assert::is_maybe_transmutable::<u128, repr_rust>(); } fn aligned() { #[repr(align(1))] struct repr_rust{} - assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted - assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted + assert::is_maybe_transmutable::<repr_rust, ()>(); + assert::is_maybe_transmutable::<u128, repr_rust>(); } fn packed() { #[repr(packed)] struct repr_rust{} - assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted - assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted + assert::is_maybe_transmutable::<repr_rust, ()>(); + assert::is_maybe_transmutable::<u128, repr_rust>(); } fn nested() { struct repr_rust; #[repr(C)] struct repr_c(repr_rust); - assert::is_maybe_transmutable::<repr_c, ()>(); //~ ERROR cannot be safely transmuted - assert::is_maybe_transmutable::<u128, repr_c>(); //~ ERROR cannot be safely transmuted + assert::is_maybe_transmutable::<repr_c, ()>(); + assert::is_maybe_transmutable::<u128, repr_c>(); } } -fn should_accept_repr_C() +fn should_accept_repr_c() { fn unit() { #[repr(C)] struct repr_c; diff --git a/tests/ui/transmutability/structs/repr/should_require_well_defined_layout.stderr b/tests/ui/transmutability/structs/repr/should_require_well_defined_layout.stderr deleted file mode 100644 index 77788f72c21..00000000000 --- a/tests/ui/transmutability/structs/repr/should_require_well_defined_layout.stderr +++ /dev/null @@ -1,267 +0,0 @@ -error[E0277]: `should_reject_repr_rust::unit::repr_rust` cannot be safely transmuted into `()` - --> $DIR/should_require_well_defined_layout.rs:27:52 - | -LL | assert::is_maybe_transmutable::<repr_rust, ()>(); - | ^^ analyzing the transmutability of `should_reject_repr_rust::unit::repr_rust` is not yet supported. - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/should_require_well_defined_layout.rs:12:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom<Src, { - | ______________^ -LL | | Assume { -LL | | alignment: true, -LL | | lifetimes: true, -... | -LL | | } -LL | | }> - | |__________^ required by this bound in `is_maybe_transmutable` - -error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::unit::repr_rust` - --> $DIR/should_require_well_defined_layout.rs:28:47 - | -LL | assert::is_maybe_transmutable::<u128, repr_rust>(); - | ^^^^^^^^^ analyzing the transmutability of `should_reject_repr_rust::unit::repr_rust` is not yet supported. - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/should_require_well_defined_layout.rs:12:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom<Src, { - | ______________^ -LL | | Assume { -LL | | alignment: true, -LL | | lifetimes: true, -... | -LL | | } -LL | | }> - | |__________^ required by this bound in `is_maybe_transmutable` - -error[E0277]: `should_reject_repr_rust::tuple::repr_rust` cannot be safely transmuted into `()` - --> $DIR/should_require_well_defined_layout.rs:33:52 - | -LL | assert::is_maybe_transmutable::<repr_rust, ()>(); - | ^^ analyzing the transmutability of `should_reject_repr_rust::tuple::repr_rust` is not yet supported. - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/should_require_well_defined_layout.rs:12:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom<Src, { - | ______________^ -LL | | Assume { -LL | | alignment: true, -LL | | lifetimes: true, -... | -LL | | } -LL | | }> - | |__________^ required by this bound in `is_maybe_transmutable` - -error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::tuple::repr_rust` - --> $DIR/should_require_well_defined_layout.rs:34:47 - | -LL | assert::is_maybe_transmutable::<u128, repr_rust>(); - | ^^^^^^^^^ analyzing the transmutability of `should_reject_repr_rust::tuple::repr_rust` is not yet supported. - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/should_require_well_defined_layout.rs:12:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom<Src, { - | ______________^ -LL | | Assume { -LL | | alignment: true, -LL | | lifetimes: true, -... | -LL | | } -LL | | }> - | |__________^ required by this bound in `is_maybe_transmutable` - -error[E0277]: `should_reject_repr_rust::braces::repr_rust` cannot be safely transmuted into `()` - --> $DIR/should_require_well_defined_layout.rs:39:52 - | -LL | assert::is_maybe_transmutable::<repr_rust, ()>(); - | ^^ analyzing the transmutability of `should_reject_repr_rust::braces::repr_rust` is not yet supported. - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/should_require_well_defined_layout.rs:12:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom<Src, { - | ______________^ -LL | | Assume { -LL | | alignment: true, -LL | | lifetimes: true, -... | -LL | | } -LL | | }> - | |__________^ required by this bound in `is_maybe_transmutable` - -error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::braces::repr_rust` - --> $DIR/should_require_well_defined_layout.rs:40:47 - | -LL | assert::is_maybe_transmutable::<u128, repr_rust>(); - | ^^^^^^^^^ analyzing the transmutability of `should_reject_repr_rust::braces::repr_rust` is not yet supported. - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/should_require_well_defined_layout.rs:12:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom<Src, { - | ______________^ -LL | | Assume { -LL | | alignment: true, -LL | | lifetimes: true, -... | -LL | | } -LL | | }> - | |__________^ required by this bound in `is_maybe_transmutable` - -error[E0277]: `aligned::repr_rust` cannot be safely transmuted into `()` - --> $DIR/should_require_well_defined_layout.rs:45:52 - | -LL | assert::is_maybe_transmutable::<repr_rust, ()>(); - | ^^ analyzing the transmutability of `aligned::repr_rust` is not yet supported. - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/should_require_well_defined_layout.rs:12:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom<Src, { - | ______________^ -LL | | Assume { -LL | | alignment: true, -LL | | lifetimes: true, -... | -LL | | } -LL | | }> - | |__________^ required by this bound in `is_maybe_transmutable` - -error[E0277]: `u128` cannot be safely transmuted into `aligned::repr_rust` - --> $DIR/should_require_well_defined_layout.rs:46:47 - | -LL | assert::is_maybe_transmutable::<u128, repr_rust>(); - | ^^^^^^^^^ analyzing the transmutability of `aligned::repr_rust` is not yet supported. - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/should_require_well_defined_layout.rs:12:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom<Src, { - | ______________^ -LL | | Assume { -LL | | alignment: true, -LL | | lifetimes: true, -... | -LL | | } -LL | | }> - | |__________^ required by this bound in `is_maybe_transmutable` - -error[E0277]: `packed::repr_rust` cannot be safely transmuted into `()` - --> $DIR/should_require_well_defined_layout.rs:51:52 - | -LL | assert::is_maybe_transmutable::<repr_rust, ()>(); - | ^^ analyzing the transmutability of `packed::repr_rust` is not yet supported. - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/should_require_well_defined_layout.rs:12:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom<Src, { - | ______________^ -LL | | Assume { -LL | | alignment: true, -LL | | lifetimes: true, -... | -LL | | } -LL | | }> - | |__________^ required by this bound in `is_maybe_transmutable` - -error[E0277]: `u128` cannot be safely transmuted into `packed::repr_rust` - --> $DIR/should_require_well_defined_layout.rs:52:47 - | -LL | assert::is_maybe_transmutable::<u128, repr_rust>(); - | ^^^^^^^^^ analyzing the transmutability of `packed::repr_rust` is not yet supported. - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/should_require_well_defined_layout.rs:12:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom<Src, { - | ______________^ -LL | | Assume { -LL | | alignment: true, -LL | | lifetimes: true, -... | -LL | | } -LL | | }> - | |__________^ required by this bound in `is_maybe_transmutable` - -error[E0277]: `nested::repr_c` cannot be safely transmuted into `()` - --> $DIR/should_require_well_defined_layout.rs:58:49 - | -LL | assert::is_maybe_transmutable::<repr_c, ()>(); - | ^^ analyzing the transmutability of `nested::repr_c` is not yet supported. - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/should_require_well_defined_layout.rs:12:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom<Src, { - | ______________^ -LL | | Assume { -LL | | alignment: true, -LL | | lifetimes: true, -... | -LL | | } -LL | | }> - | |__________^ required by this bound in `is_maybe_transmutable` - -error[E0277]: `u128` cannot be safely transmuted into `nested::repr_c` - --> $DIR/should_require_well_defined_layout.rs:59:47 - | -LL | assert::is_maybe_transmutable::<u128, repr_c>(); - | ^^^^^^ analyzing the transmutability of `nested::repr_c` is not yet supported. - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/should_require_well_defined_layout.rs:12:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom<Src, { - | ______________^ -LL | | Assume { -LL | | alignment: true, -LL | | lifetimes: true, -... | -LL | | } -LL | | }> - | |__________^ required by this bound in `is_maybe_transmutable` - -error: aborting due to 12 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.rs b/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.rs index 4c285a616b3..64110753832 100644 --- a/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.rs +++ b/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.rs @@ -22,4 +22,5 @@ fn should_pad_explicitly_packed_field() { //~^ ERROR: recursive type assert::is_maybe_transmutable::<ExplicitlyPadded, ()>(); + //~^ ERROR: cannot be safely transmuted } diff --git a/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.stderr b/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.stderr index 7fb051f6625..ebfb5361143 100644 --- a/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.stderr +++ b/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.stderr @@ -15,7 +15,22 @@ error[E0391]: cycle detected when computing layout of `should_pad_explicitly_pac = note: cycle used when evaluating trait selection obligation `(): core::mem::transmutability::BikeshedIntrinsicFrom<should_pad_explicitly_packed_field::ExplicitlyPadded, core::mem::transmutability::Assume { alignment: false, lifetimes: false, safety: false, validity: false }>` = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error: aborting due to 2 previous errors +error[E0277]: `ExplicitlyPadded` cannot be safely transmuted into `()` + --> $DIR/transmute_infinitely_recursive_type.rs:24:55 + | +LL | assert::is_maybe_transmutable::<ExplicitlyPadded, ()>(); + | ^^ analyzing the transmutability of `ExplicitlyPadded` is not yet supported + | +note: required by a bound in `is_maybe_transmutable` + --> $DIR/transmute_infinitely_recursive_type.rs:14:14 + | +LL | pub fn is_maybe_transmutable<Src, Dst>() + | --------------------- required by a bound in this function +LL | where +LL | Dst: BikeshedIntrinsicFrom<Src>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable` + +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0072, E0391. +Some errors have detailed explanations: E0072, E0277, E0391. For more information about an error, try `rustc --explain E0072`. diff --git a/tests/ui/transmutability/transmute-padding-ice.rs b/tests/ui/transmutability/transmute-padding-ice.rs index 3f3f75bc086..f5935a0009e 100644 --- a/tests/ui/transmutability/transmute-padding-ice.rs +++ b/tests/ui/transmutability/transmute-padding-ice.rs @@ -1,7 +1,13 @@ +//@ check-pass +//! This UI test was introduced as check-fail by a buggy bug-fix for an ICE. In +//! fact, this transmutation should be valid. + #![crate_type = "lib"] #![feature(transmutability)] #![allow(dead_code)] +use std::mem::size_of; + mod assert { use std::mem::{Assume, BikeshedIntrinsicFrom}; @@ -22,6 +28,7 @@ fn test() { #[repr(C)] struct B(u8, u8); + assert_eq!(size_of::<A>(), size_of::<B>()); + assert::is_maybe_transmutable::<B, A>(); - //~^ ERROR cannot be safely transmuted } diff --git a/tests/ui/transmutability/transmute-padding-ice.stderr b/tests/ui/transmutability/transmute-padding-ice.stderr deleted file mode 100644 index 4c121d463c6..00000000000 --- a/tests/ui/transmutability/transmute-padding-ice.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error[E0277]: `B` cannot be safely transmuted into `A` - --> $DIR/transmute-padding-ice.rs:25:40 - | -LL | assert::is_maybe_transmutable::<B, A>(); - | ^ the size of `B` is smaller than the size of `A` - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/transmute-padding-ice.rs:10:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom< - | ______________^ -LL | | Src, -LL | | { Assume { alignment: true, lifetimes: true, safety: true, validity: true } }, -LL | | >, - | |_________^ required by this bound in `is_maybe_transmutable` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/uninhabited.rs b/tests/ui/transmutability/uninhabited.rs new file mode 100644 index 00000000000..b61b110f6a1 --- /dev/null +++ b/tests/ui/transmutability/uninhabited.rs @@ -0,0 +1,71 @@ +#![crate_type = "lib"] +#![feature(transmutability)] +#![allow(dead_code, incomplete_features, non_camel_case_types)] + +mod assert { + use std::mem::{Assume, BikeshedIntrinsicFrom}; + + pub fn is_maybe_transmutable<Src, Dst>() + where + Dst: BikeshedIntrinsicFrom<Src, { + Assume { + alignment: true, + lifetimes: true, + safety: true, + validity: true, + } + }> + {} +} + +fn void() { + enum Void {} + + // This transmutation is vacuously acceptable; since one cannot construct a + // `Void`, unsoundness cannot directly arise from transmuting a void into + // anything else. + assert::is_maybe_transmutable::<Void, u128>(); + + assert::is_maybe_transmutable::<(), Void>(); //~ ERROR: cannot be safely transmuted +} + +// Non-ZST uninhabited types are, nonetheless, uninhabited. +fn yawning_void() { + enum Void {} + + struct YawningVoid(Void, u128); + + const _: () = { + assert!(std::mem::size_of::<YawningVoid>() == std::mem::size_of::<u128>()); + // Just to be sure the above constant actually evaluated: + assert!(false); //~ ERROR: evaluation of constant value failed + }; + + // This transmutation is vacuously acceptable; since one cannot construct a + // `Void`, unsoundness cannot directly arise from transmuting a void into + // anything else. + assert::is_maybe_transmutable::<YawningVoid, u128>(); + + assert::is_maybe_transmutable::<(), Void>(); //~ ERROR: cannot be safely transmuted +} + +// References to uninhabited types are, logically, uninhabited, but for layout +// purposes are not ZSTs, and aren't treated as uninhabited when they appear in +// enum variants. +fn distant_void() { + enum Void {} + + enum DistantVoid { + A(&'static Void) + } + + const _: () = { + assert!(std::mem::size_of::<DistantVoid>() == std::mem::size_of::<usize>()); + // Just to be sure the above constant actually evaluated: + assert!(false); //~ ERROR: evaluation of constant value failed + }; + + assert::is_maybe_transmutable::<DistantVoid, ()>(); + assert::is_maybe_transmutable::<DistantVoid, &'static Void>(); + assert::is_maybe_transmutable::<u128, DistantVoid>(); //~ ERROR: cannot be safely transmuted +} diff --git a/tests/ui/transmutability/uninhabited.stderr b/tests/ui/transmutability/uninhabited.stderr new file mode 100644 index 00000000000..60219b0f263 --- /dev/null +++ b/tests/ui/transmutability/uninhabited.stderr @@ -0,0 +1,86 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/uninhabited.rs:41:9 + | +LL | assert!(false); + | ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:41:9 + | + = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0080]: evaluation of constant value failed + --> $DIR/uninhabited.rs:65:9 + | +LL | assert!(false); + | ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:65:9 + | + = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: `()` cannot be safely transmuted into `void::Void` + --> $DIR/uninhabited.rs:29:41 + | +LL | assert::is_maybe_transmutable::<(), Void>(); + | ^^^^ `void::Void` is uninhabited + | +note: required by a bound in `is_maybe_transmutable` + --> $DIR/uninhabited.rs:10:14 + | +LL | pub fn is_maybe_transmutable<Src, Dst>() + | --------------------- required by a bound in this function +LL | where +LL | Dst: BikeshedIntrinsicFrom<Src, { + | ______________^ +LL | | Assume { +LL | | alignment: true, +LL | | lifetimes: true, +... | +LL | | } +LL | | }> + | |__________^ required by this bound in `is_maybe_transmutable` + +error[E0277]: `()` cannot be safely transmuted into `yawning_void::Void` + --> $DIR/uninhabited.rs:49:41 + | +LL | assert::is_maybe_transmutable::<(), Void>(); + | ^^^^ `yawning_void::Void` is uninhabited + | +note: required by a bound in `is_maybe_transmutable` + --> $DIR/uninhabited.rs:10:14 + | +LL | pub fn is_maybe_transmutable<Src, Dst>() + | --------------------- required by a bound in this function +LL | where +LL | Dst: BikeshedIntrinsicFrom<Src, { + | ______________^ +LL | | Assume { +LL | | alignment: true, +LL | | lifetimes: true, +... | +LL | | } +LL | | }> + | |__________^ required by this bound in `is_maybe_transmutable` + +error[E0277]: `u128` cannot be safely transmuted into `DistantVoid` + --> $DIR/uninhabited.rs:70:43 + | +LL | assert::is_maybe_transmutable::<u128, DistantVoid>(); + | ^^^^^^^^^^^ at least one value of `u128` isn't a bit-valid value of `DistantVoid` + | +note: required by a bound in `is_maybe_transmutable` + --> $DIR/uninhabited.rs:10:14 + | +LL | pub fn is_maybe_transmutable<Src, Dst>() + | --------------------- required by a bound in this function +LL | where +LL | Dst: BikeshedIntrinsicFrom<Src, { + | ______________^ +LL | | Assume { +LL | | alignment: true, +LL | | lifetimes: true, +... | +LL | | } +LL | | }> + | |__________^ required by this bound in `is_maybe_transmutable` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0080, E0277. +For more information about an error, try `rustc --explain E0080`. diff --git a/tests/ui/transmutability/unions/repr/should_handle_align.rs b/tests/ui/transmutability/unions/repr/should_handle_align.rs index 8668cca3cb5..ba4e904e161 100644 --- a/tests/ui/transmutability/unions/repr/should_handle_align.rs +++ b/tests/ui/transmutability/unions/repr/should_handle_align.rs @@ -25,13 +25,13 @@ fn should_pad_explicitly_aligned_field() { #[derive(Clone, Copy)] #[repr(u8)] enum V0u8 { V = 0 } #[derive(Clone, Copy)] #[repr(u8)] enum V1u8 { V = 1 } - #[repr(C)] + #[repr(align(1))] pub union Uninit { a: (), b: V1u8, } - #[repr(C, align(2))] + #[repr(align(2))] pub union align_2 { a: V0u8, } diff --git a/tests/ui/transmutability/unions/repr/should_require_well_defined_layout.rs b/tests/ui/transmutability/unions/repr/should_handle_all.rs index 8495b0ea88f..85d48dd9b7f 100644 --- a/tests/ui/transmutability/unions/repr/should_require_well_defined_layout.rs +++ b/tests/ui/transmutability/unions/repr/should_handle_all.rs @@ -1,7 +1,7 @@ -//! A struct must have a well-defined layout to participate in a transmutation. +//@ check-pass #![crate_type = "lib"] -#![feature(transmutability)] +#![feature(transmutability, transparent_unions)] #![allow(dead_code, incomplete_features, non_camel_case_types)] mod assert { @@ -20,17 +20,17 @@ mod assert { {} } -fn should_reject_repr_rust() +fn should_accept_repr_rust() { union repr_rust { a: u8 } - assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted - assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted + assert::is_maybe_transmutable::<repr_rust, ()>(); + assert::is_maybe_transmutable::<u128, repr_rust>(); } -fn should_accept_repr_C() +fn should_accept_repr_c() { #[repr(C)] union repr_c { @@ -41,3 +41,15 @@ fn should_accept_repr_C() assert::is_maybe_transmutable::<repr_c, ()>(); assert::is_maybe_transmutable::<u128, repr_c>(); } + + +fn should_accept_transparent() +{ + #[repr(transparent)] + union repr_transparent { + a: u8 + } + + assert::is_maybe_transmutable::<repr_transparent, ()>(); + assert::is_maybe_transmutable::<u128, repr_transparent>(); +} diff --git a/tests/ui/transmutability/unions/repr/should_handle_packed.rs b/tests/ui/transmutability/unions/repr/should_handle_packed.rs index 4af6c1d3a61..fc06eba4353 100644 --- a/tests/ui/transmutability/unions/repr/should_handle_packed.rs +++ b/tests/ui/transmutability/unions/repr/should_handle_packed.rs @@ -27,7 +27,6 @@ fn should_pad_explicitly_packed_field() { #[derive(Clone, Copy)] #[repr(u8)] enum V2u8 { V = 2 } #[derive(Clone, Copy)] #[repr(u32)] enum V3u32 { V = 3 } - #[repr(C)] pub union Uninit { a: (), b: V1u8, diff --git a/tests/ui/transmutability/unions/repr/should_require_well_defined_layout.stderr b/tests/ui/transmutability/unions/repr/should_require_well_defined_layout.stderr deleted file mode 100644 index bec07f13103..00000000000 --- a/tests/ui/transmutability/unions/repr/should_require_well_defined_layout.stderr +++ /dev/null @@ -1,47 +0,0 @@ -error[E0277]: `should_reject_repr_rust::repr_rust` cannot be safely transmuted into `()` - --> $DIR/should_require_well_defined_layout.rs:29:48 - | -LL | assert::is_maybe_transmutable::<repr_rust, ()>(); - | ^^ analyzing the transmutability of `should_reject_repr_rust::repr_rust` is not yet supported. - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/should_require_well_defined_layout.rs:12:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom<Src, { - | ______________^ -LL | | Assume { -LL | | alignment: true, -LL | | lifetimes: true, -... | -LL | | } -LL | | }> - | |__________^ required by this bound in `is_maybe_transmutable` - -error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::repr_rust` - --> $DIR/should_require_well_defined_layout.rs:30:43 - | -LL | assert::is_maybe_transmutable::<u128, repr_rust>(); - | ^^^^^^^^^ analyzing the transmutability of `should_reject_repr_rust::repr_rust` is not yet supported. - | -note: required by a bound in `is_maybe_transmutable` - --> $DIR/should_require_well_defined_layout.rs:12:14 - | -LL | pub fn is_maybe_transmutable<Src, Dst>() - | --------------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom<Src, { - | ______________^ -LL | | Assume { -LL | | alignment: true, -LL | | lifetimes: true, -... | -LL | | } -LL | | }> - | |__________^ required by this bound in `is_maybe_transmutable` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/type-alias-impl-trait/issue-53398-cyclic-types.rs b/tests/ui/type-alias-impl-trait/issue-53398-cyclic-types.rs deleted file mode 100644 index ae463b6ef5b..00000000000 --- a/tests/ui/type-alias-impl-trait/issue-53398-cyclic-types.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![feature(type_alias_impl_trait)] - -//@ check-pass - -type Foo = impl Fn() -> Foo; - -fn foo() -> Foo { - foo -} - -fn main() {} diff --git a/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.current.stderr b/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.current.stderr deleted file mode 100644 index 1c36fda4ae1..00000000000 --- a/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.current.stderr +++ /dev/null @@ -1,23 +0,0 @@ -note: no errors encountered even though delayed bugs were created - -note: those delayed bugs will now be shown as internal compiler errors - -error: internal compiler error: {OpaqueTypeKey { def_id: DefId(get_rpit::{opaque#0}), args: [] }: OpaqueTypeDecl { hidden_type: OpaqueHiddenType { span: no-location (#0), ty: Alias(Opaque, AliasTy { args: [], def_id: DefId(Opaque::{opaque#0}) }) } }} - | - = - - -error: internal compiler error: error performing ParamEnvAnd { param_env: ParamEnv { caller_bounds: [], reveal: UserFacing }, value: ProvePredicate { predicate: Binder { value: ProjectionPredicate(AliasTy { args: [FnDef(DefId(get_rpit), []), ()], def_id: DefId(ops::function::FnOnce::Output) }, Term::Ty(Alias(Opaque, AliasTy { args: [], def_id: DefId(Opaque::{opaque#0}) }))), bound_vars: [] } } } - --> $DIR/rpit_tait_equality_in_canonical_query.rs:32:5 - | -LL | query(get_rpit); - | ^^^^^^^^^^^^^^^ - | - - --> $DIR/rpit_tait_equality_in_canonical_query.rs:32:5 - | -LL | query(get_rpit); - | ^^^^^^^^^^^^^^^ - -query stack during panic: -end of query stack diff --git a/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.rs b/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.rs index 7524cebf9e6..6f50703aca2 100644 --- a/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.rs +++ b/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.rs @@ -8,17 +8,7 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] check-pass - -//@[current] known-bug: #108498 -//@[current] failure-status: 101 -//@[current] normalize-stderr-test: "DefId\(.*?\]::" -> "DefId(" -//@[current] normalize-stderr-test: "(?m)note: we would appreciate a bug report.*\n\n" -> "" -//@[current] normalize-stderr-test: "(?m)note: rustc.*running on.*\n\n" -> "" -//@[current] normalize-stderr-test: "(?m)note: compiler flags.*\n\n" -> "" -//@[current] normalize-stderr-test: "(?m)note: delayed at.*$" -> "" -//@[current] normalize-stderr-test: "(?m)^ *\d+: .*\n" -> "" -//@[current] normalize-stderr-test: "(?m)^ *at .*\n" -> "" +//@ check-pass #![feature(type_alias_impl_trait)] diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.rs b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-1.rs index e5e7fb677ed..19986247d40 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.rs +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-1.rs @@ -1,12 +1,10 @@ #![feature(type_alias_impl_trait)] +//@ known-bug: #109268 type Foo = impl Fn() -> Foo; -//~^ ERROR: unconstrained opaque type fn crash(x: Foo) -> Foo { x } -fn main() { - -} +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-1.stderr b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-1.stderr new file mode 100644 index 00000000000..ee8922b673e --- /dev/null +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-1.stderr @@ -0,0 +1,9 @@ +error[E0275]: overflow evaluating the requirement `<Foo as FnOnce<()>>::Output == Foo` + --> $DIR/type-alias-impl-trait-with-cycle-error-1.rs:6:21 + | +LL | fn crash(x: Foo) -> Foo { + | ^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.rs b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-2.rs index 7c7a1b405bc..761cc83af51 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.rs +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-2.rs @@ -1,11 +1,11 @@ #![feature(type_alias_impl_trait)] +//@ known-bug: #109268 pub trait Bar<T> { type Item; } type Foo = impl Bar<Foo, Item = Foo>; -//~^ ERROR: unconstrained opaque type fn crash(x: Foo) -> Foo { x diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-2.stderr b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-2.stderr new file mode 100644 index 00000000000..3d0f1d30ca2 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-2.stderr @@ -0,0 +1,9 @@ +error[E0275]: overflow evaluating the requirement `<Foo as Bar<Foo>>::Item == Foo` + --> $DIR/type-alias-impl-trait-with-cycle-error-2.rs:10:21 + | +LL | fn crash(x: Foo) -> Foo { + | ^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-3.rs b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-3.rs new file mode 100644 index 00000000000..52942afd639 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-3.rs @@ -0,0 +1,10 @@ +#![feature(type_alias_impl_trait)] +//@ known-bug: #109268 + +type Foo<'a> = impl Fn() -> Foo<'a>; + +fn crash<'a>(_: &'a (), x: Foo<'a>) -> Foo<'a> { + x +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-3.stderr b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-3.stderr new file mode 100644 index 00000000000..675689bac42 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-3.stderr @@ -0,0 +1,9 @@ +error[E0275]: overflow evaluating the requirement `<Foo<'_> as FnOnce<()>>::Output == Foo<'a>` + --> $DIR/type-alias-impl-trait-with-cycle-error-3.rs:6:40 + | +LL | fn crash<'a>(_: &'a (), x: Foo<'a>) -> Foo<'a> { + | ^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-4.rs b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-4.rs new file mode 100644 index 00000000000..e866b45a8e6 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-4.rs @@ -0,0 +1,16 @@ +#![feature(type_alias_impl_trait)] +//@ known-bug: trait-system-refactor-initiative#43 + +trait Id { + type Assoc; +} +impl<T> Id for T { + type Assoc = T; +} + +type Ty +where + Ty: Id<Assoc = Ty>, += impl Sized; +fn define() -> Ty {} +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-4.stderr b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-4.stderr new file mode 100644 index 00000000000..aedb78bf5e7 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-4.stderr @@ -0,0 +1,39 @@ +error[E0275]: overflow evaluating the requirement `Ty: Id` + --> $DIR/type-alias-impl-trait-with-cycle-error-4.rs:11:1 + | +LL | type Ty + | ^^^^^^^ + | +note: required by a bound on the type alias `Ty` + --> $DIR/type-alias-impl-trait-with-cycle-error-4.rs:13:9 + | +LL | Ty: Id<Assoc = Ty>, + | ^^^^^^^^^^^^^^ required by this bound + +error[E0275]: overflow evaluating the requirement `Ty: Id` + --> $DIR/type-alias-impl-trait-with-cycle-error-4.rs:15:19 + | +LL | fn define() -> Ty {} + | ^^ + | +note: required by a bound on the type alias `Ty` + --> $DIR/type-alias-impl-trait-with-cycle-error-4.rs:13:9 + | +LL | Ty: Id<Assoc = Ty>, + | ^^^^^^^^^^^^^^ required by this bound + +error[E0275]: overflow evaluating the requirement `Ty: Id` + --> $DIR/type-alias-impl-trait-with-cycle-error-4.rs:15:16 + | +LL | fn define() -> Ty {} + | ^^ + | +note: required by a bound on the type alias `Ty` + --> $DIR/type-alias-impl-trait-with-cycle-error-4.rs:13:9 + | +LL | Ty: Id<Assoc = Ty>, + | ^^^^^^^^^^^^^^ required by this bound + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.stderr b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.stderr deleted file mode 100644 index 3d43fbe0dbc..00000000000 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: unconstrained opaque type - --> $DIR/type-alias-impl-trait-with-cycle-error.rs:3:12 - | -LL | type Foo = impl Fn() -> Foo; - | ^^^^^^^^^^^^^^^^ - | - = note: `Foo` must be used in combination with a concrete type within the same module - -error: aborting due to 1 previous error - diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.stderr b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.stderr deleted file mode 100644 index e2dc887989b..00000000000 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: unconstrained opaque type - --> $DIR/type-alias-impl-trait-with-cycle-error2.rs:7:12 - | -LL | type Foo = impl Bar<Foo, Item = Foo>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `Foo` must be used in combination with a concrete type within the same module - -error: aborting due to 1 previous error - diff --git a/tests/ui/type/pattern_types/bad_pat.rs b/tests/ui/type/pattern_types/bad_pat.rs new file mode 100644 index 00000000000..8ad042eeba6 --- /dev/null +++ b/tests/ui/type/pattern_types/bad_pat.rs @@ -0,0 +1,14 @@ +#![feature(pattern_types)] +#![feature(core_pattern_types)] +#![feature(core_pattern_type)] + +use std::pat::pattern_type; + +type NonNullU32_2 = pattern_type!(u32 is 1..=); +//~^ ERROR: inclusive range with no end +type Positive2 = pattern_type!(i32 is 0..=); +//~^ ERROR: inclusive range with no end +type Wild = pattern_type!(() is _); +//~^ ERROR: wildcard patterns are not permitted for pattern types + +fn main() {} diff --git a/tests/ui/type/pattern_types/bad_pat.stderr b/tests/ui/type/pattern_types/bad_pat.stderr new file mode 100644 index 00000000000..4f0f0bc9742 --- /dev/null +++ b/tests/ui/type/pattern_types/bad_pat.stderr @@ -0,0 +1,25 @@ +error[E0586]: inclusive range with no end + --> $DIR/bad_pat.rs:7:43 + | +LL | type NonNullU32_2 = pattern_type!(u32 is 1..=); + | ^^^ help: use `..` instead + | + = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`) + +error[E0586]: inclusive range with no end + --> $DIR/bad_pat.rs:9:40 + | +LL | type Positive2 = pattern_type!(i32 is 0..=); + | ^^^ help: use `..` instead + | + = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`) + +error: "wildcard patterns are not permitted for pattern types" + --> $DIR/bad_pat.rs:11:33 + | +LL | type Wild = pattern_type!(() is _); + | ^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0586`. diff --git a/tests/ui/type/pattern_types/derives.noimpl.stderr b/tests/ui/type/pattern_types/derives.noimpl.stderr new file mode 100644 index 00000000000..9450e575344 --- /dev/null +++ b/tests/ui/type/pattern_types/derives.noimpl.stderr @@ -0,0 +1,14 @@ +error[E0369]: binary operation `==` cannot be applied to type `(i32) is 0..=999999999` + --> $DIR/derives.rs:14:20 + | +LL | #[derive(Clone, Copy, PartialEq)] + | --------- in this derive macro expansion +LL | #[repr(transparent)] +LL | struct Nanoseconds(NanoI32); + | ^^^^^^^ + | + = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0369`. diff --git a/tests/ui/type/pattern_types/derives.rs b/tests/ui/type/pattern_types/derives.rs new file mode 100644 index 00000000000..b8b53e61102 --- /dev/null +++ b/tests/ui/type/pattern_types/derives.rs @@ -0,0 +1,20 @@ +//! Check that pattern types don't implement traits of their base automatically + +#![feature(pattern_types)] +#![feature(core_pattern_types)] +#![feature(core_pattern_type)] + +use std::pat::pattern_type; + +#[derive(Clone, Copy, PartialEq)] +#[repr(transparent)] +struct Nanoseconds(NanoI32); +//~^ ERROR: binary operation `==` cannot be applied to type `(i32) is 0..=999999999` + +type NanoI32 = crate::pattern_type!(i32 is 0..=999_999_999); + +fn main() { + let x = Nanoseconds(unsafe { std::mem::transmute(42) }); + let y = x.clone(); + if y == x {} +} diff --git a/tests/ui/type/pattern_types/derives.stderr b/tests/ui/type/pattern_types/derives.stderr new file mode 100644 index 00000000000..1d4d3fc55c3 --- /dev/null +++ b/tests/ui/type/pattern_types/derives.stderr @@ -0,0 +1,14 @@ +error[E0369]: binary operation `==` cannot be applied to type `(i32) is 0..=999999999` + --> $DIR/derives.rs:11:20 + | +LL | #[derive(Clone, Copy, PartialEq)] + | --------- in this derive macro expansion +LL | #[repr(transparent)] +LL | struct Nanoseconds(NanoI32); + | ^^^^^^^ + | + = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0369`. diff --git a/tests/ui/type/pattern_types/feature-gate-pattern_types.rs b/tests/ui/type/pattern_types/feature-gate-pattern_types.rs new file mode 100644 index 00000000000..3c507a9669d --- /dev/null +++ b/tests/ui/type/pattern_types/feature-gate-pattern_types.rs @@ -0,0 +1,14 @@ +//@ compile-flags: -Zno-analysis + +use std::pat::pattern_type; + +type NonNullU32 = pattern_type!(u32 is 1..); +//~^ use of unstable library feature 'core_pattern_type' +type Percent = pattern_type!(u32 is 0..=100); +//~^ use of unstable library feature 'core_pattern_type' +type Negative = pattern_type!(i32 is ..=0); +//~^ use of unstable library feature 'core_pattern_type' +type Positive = pattern_type!(i32 is 0..); +//~^ use of unstable library feature 'core_pattern_type' +type Always = pattern_type!(Option<u32> is Some(_)); +//~^ use of unstable library feature 'core_pattern_type' diff --git a/tests/ui/type/pattern_types/feature-gate-pattern_types.stderr b/tests/ui/type/pattern_types/feature-gate-pattern_types.stderr new file mode 100644 index 00000000000..6f74b89d224 --- /dev/null +++ b/tests/ui/type/pattern_types/feature-gate-pattern_types.stderr @@ -0,0 +1,48 @@ +error[E0658]: use of unstable library feature 'core_pattern_type' + --> $DIR/feature-gate-pattern_types.rs:5:19 + | +LL | type NonNullU32 = pattern_type!(u32 is 1..); + | ^^^^^^^^^^^^ + | + = help: add `#![feature(core_pattern_type)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature 'core_pattern_type' + --> $DIR/feature-gate-pattern_types.rs:7:16 + | +LL | type Percent = pattern_type!(u32 is 0..=100); + | ^^^^^^^^^^^^ + | + = help: add `#![feature(core_pattern_type)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature 'core_pattern_type' + --> $DIR/feature-gate-pattern_types.rs:9:17 + | +LL | type Negative = pattern_type!(i32 is ..=0); + | ^^^^^^^^^^^^ + | + = help: add `#![feature(core_pattern_type)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature 'core_pattern_type' + --> $DIR/feature-gate-pattern_types.rs:11:17 + | +LL | type Positive = pattern_type!(i32 is 0..); + | ^^^^^^^^^^^^ + | + = help: add `#![feature(core_pattern_type)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature 'core_pattern_type' + --> $DIR/feature-gate-pattern_types.rs:13:15 + | +LL | type Always = pattern_type!(Option<u32> is Some(_)); + | ^^^^^^^^^^^^ + | + = help: add `#![feature(core_pattern_type)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/type/pattern_types/feature-gate-pattern_types2.rs b/tests/ui/type/pattern_types/feature-gate-pattern_types2.rs new file mode 100644 index 00000000000..d7b3ea58e02 --- /dev/null +++ b/tests/ui/type/pattern_types/feature-gate-pattern_types2.rs @@ -0,0 +1,12 @@ +//@ compile-flags: -Zno-analysis +//@ check-pass + +#![feature(core_pattern_type)] + +use std::pat::pattern_type; + +type NonNullU32 = pattern_type!(u32 is 1..); +type Percent = pattern_type!(u32 is 0..=100); +type Negative = pattern_type!(i32 is ..=0); +type Positive = pattern_type!(i32 is 0..); +type Always = pattern_type!(Option<u32> is Some(_)); diff --git a/tests/ui/type/pattern_types/macros.active.stderr b/tests/ui/type/pattern_types/macros.active.stderr new file mode 100644 index 00000000000..002e944fe2e --- /dev/null +++ b/tests/ui/type/pattern_types/macros.active.stderr @@ -0,0 +1,10 @@ +error: `$t:ty` is followed by `is`, which is not allowed for `ty` fragments + --> $DIR/macros.rs:10:12 + | +LL | ($t:ty is $p:pat) => {}; + | ^^ not allowed after `ty` fragments + | + = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` + +error: aborting due to 1 previous error + diff --git a/tests/ui/type/pattern_types/macros.gated.stderr b/tests/ui/type/pattern_types/macros.gated.stderr new file mode 100644 index 00000000000..002e944fe2e --- /dev/null +++ b/tests/ui/type/pattern_types/macros.gated.stderr @@ -0,0 +1,10 @@ +error: `$t:ty` is followed by `is`, which is not allowed for `ty` fragments + --> $DIR/macros.rs:10:12 + | +LL | ($t:ty is $p:pat) => {}; + | ^^ not allowed after `ty` fragments + | + = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where` + +error: aborting due to 1 previous error + diff --git a/tests/ui/type/pattern_types/macros.rs b/tests/ui/type/pattern_types/macros.rs new file mode 100644 index 00000000000..fd5fe257e8e --- /dev/null +++ b/tests/ui/type/pattern_types/macros.rs @@ -0,0 +1,15 @@ +//@ revisions: gated active + +#![cfg_attr(active, feature(pattern_types))] +#![allow(incomplete_features)] + +// Check that pattern types do not affect existing macros. +// They don't, because pattern types don't have surface syntax. + +macro_rules! foo { + ($t:ty is $p:pat) => {}; //~ ERROR `$t:ty` is followed by `is`, which is not allowed for `ty` fragments +} + +fn main() { + foo!(u32 is 1..) +} diff --git a/tests/ui/type/pattern_types/range_patterns.rs b/tests/ui/type/pattern_types/range_patterns.rs new file mode 100644 index 00000000000..d1fd055dbab --- /dev/null +++ b/tests/ui/type/pattern_types/range_patterns.rs @@ -0,0 +1,23 @@ +#![feature(pattern_types, rustc_attrs)] +#![feature(core_pattern_type)] +#![feature(core_pattern_types)] +#![allow(incomplete_features)] + +//@ normalize-stderr-test "pref: Align\([1-8] bytes\)" -> "pref: $$SOME_ALIGN" + +use std::pat::pattern_type; + +#[rustc_layout(debug)] +type X = std::num::NonZeroU32; //~ ERROR layout_of +#[rustc_layout(debug)] +type Y = pattern_type!(u32 is 1..); //~ ERROR layout_of +#[rustc_layout(debug)] +type Z = Option<pattern_type!(u32 is 1..)>; //~ ERROR layout_of +#[rustc_layout(debug)] +type A = Option<std::num::NonZeroU32>; //~ ERROR layout_of +#[rustc_layout(debug)] +struct NonZeroU32New(pattern_type!(u32 is 1..)); //~ ERROR layout_of + +fn main() { + let x: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(42_u32) }; +} diff --git a/tests/ui/type/pattern_types/range_patterns.stderr b/tests/ui/type/pattern_types/range_patterns.stderr new file mode 100644 index 00000000000..8465e1b7ff2 --- /dev/null +++ b/tests/ui/type/pattern_types/range_patterns.stderr @@ -0,0 +1,343 @@ +error: layout_of(NonZero<u32>) = Layout { + size: Size(4 bytes), + align: AbiAndPrefAlign { + abi: Align(4 bytes), + pref: $SOME_ALIGN, + }, + abi: Scalar( + Initialized { + value: Int( + I32, + false, + ), + valid_range: 1..=4294967295, + }, + ), + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, + largest_niche: Some( + Niche { + offset: Size(0 bytes), + value: Int( + I32, + false, + ), + valid_range: 1..=4294967295, + }, + ), + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(4 bytes), + } + --> $DIR/range_patterns.rs:11:1 + | +LL | type X = std::num::NonZeroU32; + | ^^^^^^ + +error: layout_of((u32) is 1..=) = Layout { + size: Size(4 bytes), + align: AbiAndPrefAlign { + abi: Align(4 bytes), + pref: $SOME_ALIGN, + }, + abi: Scalar( + Initialized { + value: Int( + I32, + false, + ), + valid_range: 1..=4294967295, + }, + ), + fields: Primitive, + largest_niche: Some( + Niche { + offset: Size(0 bytes), + value: Int( + I32, + false, + ), + valid_range: 1..=4294967295, + }, + ), + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(4 bytes), + } + --> $DIR/range_patterns.rs:13:1 + | +LL | type Y = pattern_type!(u32 is 1..); + | ^^^^^^ + +error: layout_of(Option<(u32) is 1..=>) = Layout { + size: Size(4 bytes), + align: AbiAndPrefAlign { + abi: Align(4 bytes), + pref: $SOME_ALIGN, + }, + abi: Scalar( + Initialized { + value: Int( + I32, + false, + ), + valid_range: (..=0) | (1..), + }, + ), + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, + largest_niche: None, + variants: Multiple { + tag: Initialized { + value: Int( + I32, + false, + ), + valid_range: (..=0) | (1..), + }, + tag_encoding: Niche { + untagged_variant: 1, + niche_variants: 0..=0, + niche_start: 0, + }, + tag_field: 0, + variants: [ + Layout { + size: Size(0 bytes), + align: AbiAndPrefAlign { + abi: Align(1 bytes), + pref: $SOME_ALIGN, + }, + abi: Aggregate { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + }, + Layout { + size: Size(4 bytes), + align: AbiAndPrefAlign { + abi: Align(4 bytes), + pref: $SOME_ALIGN, + }, + abi: Scalar( + Initialized { + value: Int( + I32, + false, + ), + valid_range: 1..=4294967295, + }, + ), + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, + largest_niche: Some( + Niche { + offset: Size(0 bytes), + value: Int( + I32, + false, + ), + valid_range: 1..=4294967295, + }, + ), + variants: Single { + index: 1, + }, + max_repr_align: None, + unadjusted_abi_align: Align(4 bytes), + }, + ], + }, + max_repr_align: None, + unadjusted_abi_align: Align(4 bytes), + } + --> $DIR/range_patterns.rs:15:1 + | +LL | type Z = Option<pattern_type!(u32 is 1..)>; + | ^^^^^^ + +error: layout_of(Option<NonZero<u32>>) = Layout { + size: Size(4 bytes), + align: AbiAndPrefAlign { + abi: Align(4 bytes), + pref: $SOME_ALIGN, + }, + abi: Scalar( + Initialized { + value: Int( + I32, + false, + ), + valid_range: (..=0) | (1..), + }, + ), + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, + largest_niche: None, + variants: Multiple { + tag: Initialized { + value: Int( + I32, + false, + ), + valid_range: (..=0) | (1..), + }, + tag_encoding: Niche { + untagged_variant: 1, + niche_variants: 0..=0, + niche_start: 0, + }, + tag_field: 0, + variants: [ + Layout { + size: Size(0 bytes), + align: AbiAndPrefAlign { + abi: Align(1 bytes), + pref: $SOME_ALIGN, + }, + abi: Aggregate { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + }, + Layout { + size: Size(4 bytes), + align: AbiAndPrefAlign { + abi: Align(4 bytes), + pref: $SOME_ALIGN, + }, + abi: Scalar( + Initialized { + value: Int( + I32, + false, + ), + valid_range: 1..=4294967295, + }, + ), + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, + largest_niche: Some( + Niche { + offset: Size(0 bytes), + value: Int( + I32, + false, + ), + valid_range: 1..=4294967295, + }, + ), + variants: Single { + index: 1, + }, + max_repr_align: None, + unadjusted_abi_align: Align(4 bytes), + }, + ], + }, + max_repr_align: None, + unadjusted_abi_align: Align(4 bytes), + } + --> $DIR/range_patterns.rs:17:1 + | +LL | type A = Option<std::num::NonZeroU32>; + | ^^^^^^ + +error: layout_of(NonZeroU32New) = Layout { + size: Size(4 bytes), + align: AbiAndPrefAlign { + abi: Align(4 bytes), + pref: $SOME_ALIGN, + }, + abi: Scalar( + Initialized { + value: Int( + I32, + false, + ), + valid_range: 1..=4294967295, + }, + ), + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, + largest_niche: Some( + Niche { + offset: Size(0 bytes), + value: Int( + I32, + false, + ), + valid_range: 1..=4294967295, + }, + ), + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(4 bytes), + } + --> $DIR/range_patterns.rs:19:1 + | +LL | struct NonZeroU32New(pattern_type!(u32 is 1..)); + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/type/pattern_types/range_patterns_inherent_impls.rs b/tests/ui/type/pattern_types/range_patterns_inherent_impls.rs new file mode 100644 index 00000000000..9653a744c41 --- /dev/null +++ b/tests/ui/type/pattern_types/range_patterns_inherent_impls.rs @@ -0,0 +1,30 @@ +#![feature(pattern_types, rustc_attrs)] +#![feature(core_pattern_type)] +#![feature(core_pattern_types)] +#![allow(incomplete_features)] + +//! check that pattern types can have traits implemented for them if +//! their base type is a local type. + +use std::pat::pattern_type; + +type Y = pattern_type!(u32 is 1..); + +impl Y { + //~^ ERROR cannot define inherent `impl` + fn foo() {} +} + +struct MyStruct<T>(T); + +impl MyStruct<Y> { + fn foo() {} +} + +struct Wrapper(Y); + +impl Wrapper { + fn foo() {} +} + +fn main() {} diff --git a/tests/ui/type/pattern_types/range_patterns_inherent_impls.stderr b/tests/ui/type/pattern_types/range_patterns_inherent_impls.stderr new file mode 100644 index 00000000000..784ebb0c9f0 --- /dev/null +++ b/tests/ui/type/pattern_types/range_patterns_inherent_impls.stderr @@ -0,0 +1,16 @@ +error[E0390]: cannot define inherent `impl` for primitive types outside of `core` + --> $DIR/range_patterns_inherent_impls.rs:13:1 + | +LL | impl Y { + | ^^^^^^ + | + = help: consider moving this inherent impl into `core` if possible +help: alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items + --> $DIR/range_patterns_inherent_impls.rs:15:5 + | +LL | fn foo() {} + | ^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0390`. diff --git a/tests/ui/type/pattern_types/range_patterns_trait_impls.rs b/tests/ui/type/pattern_types/range_patterns_trait_impls.rs new file mode 100644 index 00000000000..f8c9af86281 --- /dev/null +++ b/tests/ui/type/pattern_types/range_patterns_trait_impls.rs @@ -0,0 +1,19 @@ +#![feature(pattern_types, rustc_attrs)] +#![feature(core_pattern_type)] +#![feature(core_pattern_types)] +#![allow(incomplete_features)] + +//! check that pattern types can have local traits +//! implemented for them. + +//@ check-pass + +use std::pat::pattern_type; + +type Y = pattern_type!(u32 is 1..); + +trait Trait {} + +impl Trait for Y {} + +fn main() {} diff --git a/tests/ui/type/pattern_types/range_patterns_trait_impls2.rs b/tests/ui/type/pattern_types/range_patterns_trait_impls2.rs new file mode 100644 index 00000000000..acde4580c1b --- /dev/null +++ b/tests/ui/type/pattern_types/range_patterns_trait_impls2.rs @@ -0,0 +1,16 @@ +#![feature(pattern_types, rustc_attrs)] +#![feature(core_pattern_type)] +#![feature(core_pattern_types)] +#![allow(incomplete_features)] + +//! check that pattern types can have local traits +//! implemented for them. + +use std::pat::pattern_type; + +type Y = pattern_type!(u32 is 1..); + +impl Eq for Y {} +//~^ ERROR: only traits defined in the current crate can be implemented for arbitrary types + +fn main() {} diff --git a/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr b/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr new file mode 100644 index 00000000000..41f42455bde --- /dev/null +++ b/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr @@ -0,0 +1,14 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/range_patterns_trait_impls2.rs:13:1 + | +LL | impl Eq for Y {} + | ^^^^^^^^^^^^- + | | | + | | `(u32) is 1..=` is not defined in the current crate + | impl doesn't use only types from inside the current crate + | + = note: define and implement a trait or new type instead + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0117`. diff --git a/tests/ui/type/pattern_types/range_patterns_unusable.rs b/tests/ui/type/pattern_types/range_patterns_unusable.rs new file mode 100644 index 00000000000..7cde44f4133 --- /dev/null +++ b/tests/ui/type/pattern_types/range_patterns_unusable.rs @@ -0,0 +1,15 @@ +#![feature(pattern_types, rustc_attrs)] +#![feature(core_pattern_type)] +#![feature(core_pattern_types)] +#![allow(incomplete_features)] + +//! Some practical niche checks. + +use std::pat::pattern_type; + +type Z = Option<pattern_type!(u32 is 1..)>; + +fn main() { + let z: Z = Some(unsafe { std::mem::transmute(42_u32) }); + let _: Option<u32> = unsafe { std::mem::transmute(z) }; //~ ERROR: different sizes +} diff --git a/tests/ui/type/pattern_types/range_patterns_unusable.stderr b/tests/ui/type/pattern_types/range_patterns_unusable.stderr new file mode 100644 index 00000000000..aa0e592684b --- /dev/null +++ b/tests/ui/type/pattern_types/range_patterns_unusable.stderr @@ -0,0 +1,12 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/range_patterns_unusable.rs:14:35 + | +LL | let _: Option<u32> = unsafe { std::mem::transmute(z) }; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `Option<(u32) is 1..=>` (32 bits) + = note: target type: `Option<u32>` (64 bits) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/type/pattern_types/range_patterns_unusable_math.rs b/tests/ui/type/pattern_types/range_patterns_unusable_math.rs new file mode 100644 index 00000000000..bc1ab75429d --- /dev/null +++ b/tests/ui/type/pattern_types/range_patterns_unusable_math.rs @@ -0,0 +1,16 @@ +#![feature(pattern_types, rustc_attrs)] +#![feature(core_pattern_type)] +#![feature(core_pattern_types)] +#![allow(incomplete_features)] + +//! check that pattern types don't have an `Add` impl. + +use std::pat::pattern_type; + +type Y = pattern_type!(u32 is 1..); +type Z = Option<pattern_type!(u32 is 1..)>; + +fn main() { + let x: Y = unsafe { std::mem::transmute(42_u32) }; + let x = x + 1_u32; //~ ERROR cannot add `u32` to `(u32) is 1..=` +} diff --git a/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr b/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr new file mode 100644 index 00000000000..e52d899a703 --- /dev/null +++ b/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr @@ -0,0 +1,11 @@ +error[E0369]: cannot add `u32` to `(u32) is 1..=` + --> $DIR/range_patterns_unusable_math.rs:15:15 + | +LL | let x = x + 1_u32; + | - ^ ----- u32 + | | + | (u32) is 1..= + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0369`. diff --git a/tests/ui/type/pattern_types/range_patterns_usage.rs b/tests/ui/type/pattern_types/range_patterns_usage.rs new file mode 100644 index 00000000000..7fe50423c51 --- /dev/null +++ b/tests/ui/type/pattern_types/range_patterns_usage.rs @@ -0,0 +1,26 @@ +#![feature(pattern_types, rustc_attrs)] +#![feature(core_pattern_type)] +#![feature(core_pattern_types)] +#![allow(incomplete_features)] +//@ check-pass + +//! check that pattern types can actually be part of code that compiles successfully. + +use std::pat::pattern_type; + +type X = std::num::NonZeroU32; +type Y = pattern_type!(u32 is 1..); +type Z = Option<pattern_type!(u32 is 1..)>; +struct NonZeroU32New(pattern_type!(u32 is 1..)); + +fn main() { + let x: Y = unsafe { std::mem::transmute(42_u32) }; + let z: Z = Some(unsafe { std::mem::transmute(42_u32) }); + match z { + Some(y) => { + let _: Y = y; + } + None => {} + } + let x: X = unsafe { std::mem::transmute(x) }; +} diff --git a/tests/ui/type/pattern_types/unimplemented_pat.rs b/tests/ui/type/pattern_types/unimplemented_pat.rs new file mode 100644 index 00000000000..c02160ff58a --- /dev/null +++ b/tests/ui/type/pattern_types/unimplemented_pat.rs @@ -0,0 +1,15 @@ +//! This test ensures we do not ICE for unimplemented +//! patterns unless the feature gate is enabled. + +#![feature(core_pattern_type)] +#![feature(core_pattern_types)] + +use std::pat::pattern_type; + +type Always = pattern_type!(Option<u32> is Some(_)); +//~^ ERROR: pattern types are unstable + +type Binding = pattern_type!(Option<u32> is x); +//~^ ERROR: pattern types are unstable + +fn main() {} diff --git a/tests/ui/type/pattern_types/unimplemented_pat.stderr b/tests/ui/type/pattern_types/unimplemented_pat.stderr new file mode 100644 index 00000000000..481c6017dcc --- /dev/null +++ b/tests/ui/type/pattern_types/unimplemented_pat.stderr @@ -0,0 +1,23 @@ +error[E0658]: pattern types are unstable + --> $DIR/unimplemented_pat.rs:9:15 + | +LL | type Always = pattern_type!(Option<u32> is Some(_)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #123646 <https://github.com/rust-lang/rust/issues/123646> for more information + = help: add `#![feature(pattern_types)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: pattern types are unstable + --> $DIR/unimplemented_pat.rs:12:16 + | +LL | type Binding = pattern_type!(Option<u32> is x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #123646 <https://github.com/rust-lang/rust/issues/123646> for more information + = help: add `#![feature(pattern_types)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/issues/issue-23649-1.rs b/tests/ui/unsized/issue-23649-1.rs index 1c4252b40c6..1c4252b40c6 100644 --- a/tests/ui/issues/issue-23649-1.rs +++ b/tests/ui/unsized/issue-23649-1.rs diff --git a/tests/ui/issues/issue-23649-2.rs b/tests/ui/unsized/issue-23649-2.rs index 4a953de1768..4a953de1768 100644 --- a/tests/ui/issues/issue-23649-2.rs +++ b/tests/ui/unsized/issue-23649-2.rs diff --git a/tests/ui/issues/issue-23649-3.rs b/tests/ui/unsized/issue-23649-3.rs index 524055d99b4..524055d99b4 100644 --- a/tests/ui/issues/issue-23649-3.rs +++ b/tests/ui/unsized/issue-23649-3.rs |
