diff options
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_codegen_llvm/src/back/lto.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_codegen_llvm/src/back/write.rs | 31 | ||||
| -rw-r--r-- | compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 15 | ||||
| -rw-r--r-- | compiler/rustc_codegen_llvm/src/llvm_util.rs | 5 | ||||
| -rw-r--r-- | compiler/rustc_interface/src/tests.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 97 | ||||
| -rw-r--r-- | compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 1 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/pass_manager.rs | 21 | ||||
| -rw-r--r-- | compiler/rustc_session/src/options.rs | 19 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/astconv/mod.rs | 92 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/collect.rs | 152 | 
11 files changed, 353 insertions, 83 deletions
| diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 7a747a9cdee..3f5957bdb6e 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -625,7 +625,7 @@ pub(crate) fn run_pass_manager( if thin { llvm::LLVMRustPassManagerBuilderPopulateThinLTOPassManager(b, pm); } else { - llvm::LLVMPassManagerBuilderPopulateLTOPassManager( + llvm::LLVMRustPassManagerBuilderPopulateLTOPassManager( b, pm, /* Internalize = */ False, /* RunInliner = */ True, ); } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 7ef3b12cd08..99e30531c22 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -523,6 +523,12 @@ pub(crate) unsafe fn optimize( let module_name = module.name.clone(); let module_name = Some(&module_name[..]); + if let Some(false) = config.new_llvm_pass_manager && llvm_util::get_version() >= (15, 0, 0) { + diag_handler.warn( + "ignoring `-Z new-llvm-pass-manager=no`, which is no longer supported with LLVM 15", + ); + } + if config.emit_no_opt_bc { let out = cgcx.output_filenames.temp_path_ext("no-opt.bc", module_name); let out = path_to_c_string(&out); @@ -628,8 +634,8 @@ pub(crate) unsafe fn optimize( extra_passes.as_ptr(), extra_passes.len() as size_t, ); - llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(b, fpm); - llvm::LLVMPassManagerBuilderPopulateModulePassManager(b, mpm); + llvm::LLVMRustPassManagerBuilderPopulateFunctionPassManager(b, fpm); + llvm::LLVMRustPassManagerBuilderPopulateModulePassManager(b, mpm); }); have_name_anon_globals_pass = have_name_anon_globals_pass || prepare_for_thin_lto; @@ -1085,7 +1091,7 @@ pub unsafe fn with_llvm_pmb( // Create the PassManagerBuilder for LLVM. We configure it with // reasonable defaults and prepare it to actually populate the pass // manager. - let builder = llvm::LLVMPassManagerBuilderCreate(); + let builder = llvm::LLVMRustPassManagerBuilderCreate(); let opt_size = config.opt_size.map_or(llvm::CodeGenOptSizeNone, |x| to_llvm_opt_settings(x).1); let inline_threshold = config.inline_threshold; let pgo_gen_path = get_pgo_gen_path(config); @@ -1102,14 +1108,9 @@ pub unsafe fn with_llvm_pmb( pgo_gen_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()), pgo_use_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()), pgo_sample_use_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()), + opt_size as c_int, ); - llvm::LLVMPassManagerBuilderSetSizeLevel(builder, opt_size as u32); - - if opt_size != llvm::CodeGenOptSizeNone { - llvm::LLVMPassManagerBuilderSetDisableUnrollLoops(builder, 1); - } - llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, config.no_builtins); // Here we match what clang does (kinda). For O0 we only inline @@ -1118,16 +1119,16 @@ pub unsafe fn with_llvm_pmb( // thresholds copied from clang. match (opt_level, opt_size, inline_threshold) { (.., Some(t)) => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, t); + llvm::LLVMRustPassManagerBuilderUseInlinerWithThreshold(builder, t); } (llvm::CodeGenOptLevel::Aggressive, ..) => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 275); + llvm::LLVMRustPassManagerBuilderUseInlinerWithThreshold(builder, 275); } (_, llvm::CodeGenOptSizeDefault, _) => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 75); + llvm::LLVMRustPassManagerBuilderUseInlinerWithThreshold(builder, 75); } (_, llvm::CodeGenOptSizeAggressive, _) => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 25); + llvm::LLVMRustPassManagerBuilderUseInlinerWithThreshold(builder, 25); } (llvm::CodeGenOptLevel::None, ..) => { llvm::LLVMRustAddAlwaysInlinePass(builder, config.emit_lifetime_markers); @@ -1136,12 +1137,12 @@ pub unsafe fn with_llvm_pmb( llvm::LLVMRustAddAlwaysInlinePass(builder, config.emit_lifetime_markers); } (llvm::CodeGenOptLevel::Default, ..) => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 225); + llvm::LLVMRustPassManagerBuilderUseInlinerWithThreshold(builder, 225); } } f(builder); - llvm::LLVMPassManagerBuilderDispose(builder); + llvm::LLVMRustPassManagerBuilderDispose(builder); } // Create a `__imp_<symbol> = &symbol` global for every public static `symbol`. diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 7f533b0552a..13baaddccd4 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1825,24 +1825,22 @@ extern "C" { pub fn LLVMAddAnalysisPasses<'a>(T: &'a TargetMachine, PM: &PassManager<'a>); - pub fn LLVMPassManagerBuilderCreate() -> &'static mut PassManagerBuilder; - pub fn LLVMPassManagerBuilderDispose(PMB: &'static mut PassManagerBuilder); - pub fn LLVMPassManagerBuilderSetSizeLevel(PMB: &PassManagerBuilder, Value: Bool); - pub fn LLVMPassManagerBuilderSetDisableUnrollLoops(PMB: &PassManagerBuilder, Value: Bool); - pub fn LLVMPassManagerBuilderUseInlinerWithThreshold( + pub fn LLVMRustPassManagerBuilderCreate() -> &'static mut PassManagerBuilder; + pub fn LLVMRustPassManagerBuilderDispose(PMB: &'static mut PassManagerBuilder); + pub fn LLVMRustPassManagerBuilderUseInlinerWithThreshold( PMB: &PassManagerBuilder, threshold: c_uint, ); - pub fn LLVMPassManagerBuilderPopulateModulePassManager( + pub fn LLVMRustPassManagerBuilderPopulateModulePassManager( PMB: &PassManagerBuilder, PM: &PassManager<'_>, ); - pub fn LLVMPassManagerBuilderPopulateFunctionPassManager( + pub fn LLVMRustPassManagerBuilderPopulateFunctionPassManager( PMB: &PassManagerBuilder, PM: &PassManager<'_>, ); - pub fn LLVMPassManagerBuilderPopulateLTOPassManager( + pub fn LLVMRustPassManagerBuilderPopulateLTOPassManager( PMB: &PassManagerBuilder, PM: &PassManager<'_>, Internalize: Bool, @@ -2308,6 +2306,7 @@ extern "C" { PGOGenPath: *const c_char, PGOUsePath: *const c_char, PGOSampleUsePath: *const c_char, + SizeLevel: c_int, ); pub fn LLVMRustAddLibraryInfo<'a>( PM: &PassManager<'a>, diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index c24e369ae72..7b407c94e7b 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -542,6 +542,11 @@ pub(crate) fn should_use_new_llvm_pass_manager(user_opt: &Option<bool>, target_a // The new pass manager is enabled by default for LLVM >= 13. // This matches Clang, which also enables it since Clang 13. + // Since LLVM 15, the legacy pass manager is no longer supported. + if llvm_util::get_version() >= (15, 0, 0) { + return true; + } + // There are some perf issues with the new pass manager when targeting // s390x with LLVM 13, so enable the new pass manager only with LLVM 14. // See https://github.com/rust-lang/rust/issues/89609. diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 6caa1032ba3..1327bf6fcd4 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -752,6 +752,7 @@ fn test_debugging_options_tracking_hash() { tracked!(location_detail, LocationDetail { file: true, line: false, column: false }); tracked!(merge_functions, Some(MergeFunctions::Disabled)); tracked!(mir_emit_retag, true); + tracked!(mir_enable_passes, vec![("DestProp".to_string(), false)]); tracked!(mir_opt_level, Some(4)); tracked!(move_size_limit, Some(4096)); tracked!(mutable_noalias, Some(true)); diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 71f21dc6666..38fddbdba54 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -107,6 +107,7 @@ static LLVMRustPassKind toRust(PassKind Kind) { } extern "C" LLVMPassRef LLVMRustFindAndCreatePass(const char *PassName) { +#if LLVM_VERSION_LT(15, 0) StringRef SR(PassName); PassRegistry *PR = PassRegistry::getPassRegistry(); @@ -115,36 +116,59 @@ extern "C" LLVMPassRef LLVMRustFindAndCreatePass(const char *PassName) { return wrap(PI->createPass()); } return nullptr; +#else + report_fatal_error("Legacy PM not supported with LLVM 15"); +#endif } extern "C" LLVMPassRef LLVMRustCreateAddressSanitizerFunctionPass(bool Recover) { +#if LLVM_VERSION_LT(15, 0) const bool CompileKernel = false; const bool UseAfterScope = true; return wrap(createAddressSanitizerFunctionPass(CompileKernel, Recover, UseAfterScope)); +#else + report_fatal_error("Legacy PM not supported with LLVM 15"); +#endif } extern "C" LLVMPassRef LLVMRustCreateModuleAddressSanitizerPass(bool Recover) { +#if LLVM_VERSION_LT(15, 0) const bool CompileKernel = false; return wrap(createModuleAddressSanitizerLegacyPassPass(CompileKernel, Recover)); +#else + report_fatal_error("Legacy PM not supported with LLVM 15"); +#endif } extern "C" LLVMPassRef LLVMRustCreateMemorySanitizerPass(int TrackOrigins, bool Recover) { +#if LLVM_VERSION_LT(15, 0) const bool CompileKernel = false; return wrap(createMemorySanitizerLegacyPassPass( MemorySanitizerOptions{TrackOrigins, Recover, CompileKernel})); +#else + report_fatal_error("Legacy PM not supported with LLVM 15"); +#endif } extern "C" LLVMPassRef LLVMRustCreateThreadSanitizerPass() { +#if LLVM_VERSION_LT(15, 0) return wrap(createThreadSanitizerLegacyPassPass()); +#else + report_fatal_error("Legacy PM not supported with LLVM 15"); +#endif } extern "C" LLVMPassRef LLVMRustCreateHWAddressSanitizerPass(bool Recover) { +#if LLVM_VERSION_LT(15, 0) const bool CompileKernel = false; return wrap(createHWAddressSanitizerLegacyPassPass(CompileKernel, Recover)); +#else + report_fatal_error("Legacy PM not supported with LLVM 15"); +#endif } extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) { @@ -154,10 +178,57 @@ extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) { } extern "C" void LLVMRustAddPass(LLVMPassManagerRef PMR, LLVMPassRef RustPass) { +#if LLVM_VERSION_LT(15, 0) assert(RustPass); Pass *Pass = unwrap(RustPass); PassManagerBase *PMB = unwrap(PMR); PMB->add(Pass); +#else + report_fatal_error("Legacy PM not supported with LLVM 15"); +#endif +} + +extern "C" LLVMPassManagerBuilderRef LLVMRustPassManagerBuilderCreate() { +#if LLVM_VERSION_LT(15, 0) + return LLVMPassManagerBuilderCreate(); +#else + report_fatal_error("Legacy PM not supported with LLVM 15"); +#endif +} + +extern "C" void LLVMRustPassManagerBuilderDispose(LLVMPassManagerBuilderRef PMB) { +#if LLVM_VERSION_LT(15, 0) + LLVMPassManagerBuilderDispose(PMB); +#else + report_fatal_error("Legacy PM not supported with LLVM 15"); +#endif +} + +extern "C" void LLVMRustPassManagerBuilderPopulateFunctionPassManager( + LLVMPassManagerBuilderRef PMB, LLVMPassManagerRef PM) { +#if LLVM_VERSION_LT(15, 0) + LLVMPassManagerBuilderPopulateFunctionPassManager(PMB, PM); +#else + report_fatal_error("Legacy PM not supported with LLVM 15"); +#endif +} + +extern "C" void LLVMRustPassManagerBuilderPopulateModulePassManager( + LLVMPassManagerBuilderRef PMB, LLVMPassManagerRef PM) { +#if LLVM_VERSION_LT(15, 0) + LLVMPassManagerBuilderPopulateModulePassManager(PMB, PM); +#else + report_fatal_error("Legacy PM not supported with LLVM 15"); +#endif +} + +extern "C" void LLVMRustPassManagerBuilderPopulateLTOPassManager( + LLVMPassManagerBuilderRef PMB, LLVMPassManagerRef PM, bool Internalize, bool RunInliner) { +#if LLVM_VERSION_LT(15, 0) + LLVMPassManagerBuilderPopulateLTOPassManager(PMB, PM, Internalize, RunInliner); +#else + report_fatal_error("Legacy PM not supported with LLVM 15"); +#endif } extern "C" @@ -165,12 +236,26 @@ void LLVMRustPassManagerBuilderPopulateThinLTOPassManager( LLVMPassManagerBuilderRef PMBR, LLVMPassManagerRef PMR ) { +#if LLVM_VERSION_LT(15, 0) unwrap(PMBR)->populateThinLTOPassManager(*unwrap(PMR)); +#else + report_fatal_error("Legacy PM not supported with LLVM 15"); +#endif +} + +extern "C" void LLVMRustPassManagerBuilderUseInlinerWithThreshold( + LLVMPassManagerBuilderRef PMB, unsigned Threshold) { +#if LLVM_VERSION_LT(15, 0) + LLVMPassManagerBuilderUseInlinerWithThreshold(PMB, Threshold); +#else + report_fatal_error("Legacy PM not supported with LLVM 15"); +#endif } extern "C" void LLVMRustAddLastExtensionPasses( LLVMPassManagerBuilderRef PMBR, LLVMPassRef *Passes, size_t NumPasses) { +#if LLVM_VERSION_LT(15, 0) auto AddExtensionPasses = [Passes, NumPasses]( const PassManagerBuilder &Builder, PassManagerBase &PM) { for (size_t I = 0; I < NumPasses; I++) { @@ -183,6 +268,9 @@ void LLVMRustAddLastExtensionPasses( AddExtensionPasses); unwrap(PMBR)->addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0, AddExtensionPasses); +#else + report_fatal_error("Legacy PM not supported with LLVM 15"); +#endif } #ifdef LLVM_COMPONENT_X86 @@ -533,12 +621,16 @@ extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) { extern "C" void LLVMRustConfigurePassManagerBuilder( LLVMPassManagerBuilderRef PMBR, LLVMRustCodeGenOptLevel OptLevel, bool MergeFunctions, bool SLPVectorize, bool LoopVectorize, bool PrepareForThinLTO, - const char* PGOGenPath, const char* PGOUsePath, const char* PGOSampleUsePath) { + const char* PGOGenPath, const char* PGOUsePath, const char* PGOSampleUsePath, + int SizeLevel) { +#if LLVM_VERSION_LT(15, 0) unwrap(PMBR)->MergeFunctions = MergeFunctions; unwrap(PMBR)->SLPVectorize = SLPVectorize; unwrap(PMBR)->OptLevel = fromRust(OptLevel); unwrap(PMBR)->LoopVectorize = LoopVectorize; unwrap(PMBR)->PrepareForThinLTO = PrepareForThinLTO; + unwrap(PMBR)->SizeLevel = SizeLevel; + unwrap(PMBR)->DisableUnrollLoops = SizeLevel != 0; if (PGOGenPath) { assert(!PGOUsePath && !PGOSampleUsePath); @@ -550,6 +642,9 @@ extern "C" void LLVMRustConfigurePassManagerBuilder( } else if (PGOSampleUsePath) { unwrap(PMBR)->PGOSampleUse = PGOSampleUsePath; } +#else + report_fatal_error("Legacy PM not supported with LLVM 15"); +#endif } // Unfortunately, the LLVM C API doesn't provide a way to set the `LibraryInfo` diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 3ed4396d1e9..850b80e4280 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -6,6 +6,7 @@ #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Intrinsics.h" +#include "llvm/IR/Mangler.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFFImportFile.h" #include "llvm/Object/ObjectFile.h" diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 740a2168b41..2380391d09a 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -77,17 +77,30 @@ pub fn run_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn let mut cnt = 0; let validate = tcx.sess.opts.debugging_opts.validate_mir; + let overridden_passes = &tcx.sess.opts.debugging_opts.mir_enable_passes; + trace!(?overridden_passes); if validate { validate_body(tcx, body, format!("start of phase transition from {:?}", start_phase)); } for pass in passes { - if !pass.is_enabled(&tcx.sess) { - continue; - } - let name = pass.name(); + + if let Some((_, polarity)) = overridden_passes.iter().rev().find(|(s, _)| s == &*name) { + trace!( + pass = %name, + "{} as requested by flag", + if *polarity { "Running" } else { "Not running" }, + ); + if !polarity { + continue; + } + } else { + if !pass.is_enabled(&tcx.sess) { + continue; + } + } let dump_enabled = pass.is_mir_dump_enabled(); if dump_enabled { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index df65409a8a0..96f50e57ac4 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -368,6 +368,8 @@ mod desc { pub const parse_opt_langid: &str = "a language identifier"; pub const parse_opt_pathbuf: &str = "a path"; pub const parse_list: &str = "a space-separated list of strings"; + pub const parse_list_with_polarity: &str = + "a comma-separated list of strings, with elements beginning with + or -"; pub const parse_opt_comma_list: &str = "a comma-separated list of strings"; pub const parse_number: &str = "a number"; pub const parse_opt_number: &str = parse_number; @@ -529,6 +531,19 @@ mod parse { } } + crate fn parse_list_with_polarity(slot: &mut Vec<(String, bool)>, v: Option<&str>) -> bool { + match v { + Some(s) => { + for s in s.split(",") { + let Some(pass_name) = s.strip_prefix(&['+', '-'][..]) else { return false }; + slot.push((pass_name.to_string(), &s[..1] == "+")); + } + true + } + None => false, + } + } + crate fn parse_location_detail(ld: &mut LocationDetail, v: Option<&str>) -> bool { if let Some(v) = v { ld.line = false; @@ -1318,6 +1333,10 @@ options! { mir_emit_retag: bool = (false, parse_bool, [TRACKED], "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \ (default: no)"), + mir_enable_passes: Vec<(String, bool)> = (Vec::new(), parse_list_with_polarity, [TRACKED], + "use like `-Zmir-enable-passes=+DestProp,-InstCombine`. Forces the specified passes to be \ + enabled, overriding all other checks. Passes that are not specified are enabled or \ + disabled by other flags as usual."), mir_opt_level: Option<usize> = (None, parse_opt_number, [TRACKED], "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"), move_size_limit: Option<usize> = (None, parse_opt_number, [TRACKED], diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 6bae0f2eac9..1cd0ace8adb 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -2563,40 +2563,77 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // We proactively collect all the inferred type params to emit a single error per fn def. let mut visitor = HirPlaceholderCollector::default(); - for ty in decl.inputs { - visitor.visit_ty(ty); - } + let mut infer_replacements = vec![]; + walk_generics(&mut visitor, generics); - let input_tys = decl.inputs.iter().map(|a| self.ty_of_arg(a, None)); + let input_tys: Vec<_> = decl + .inputs + .iter() + .enumerate() + .map(|(i, a)| { + if let hir::TyKind::Infer = a.kind && !self.allow_ty_infer() { + if let Some(suggested_ty) = + self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i)) + { + infer_replacements.push((a.span, suggested_ty.to_string())); + return suggested_ty; + } + } + + // Only visit the type looking for `_` if we didn't fix the type above + visitor.visit_ty(a); + self.ty_of_arg(a, None) + }) + .collect(); + let output_ty = match decl.output { hir::FnRetTy::Return(output) => { - visitor.visit_ty(output); - self.ast_ty_to_ty(output) + if let hir::TyKind::Infer = output.kind + && !self.allow_ty_infer() + && let Some(suggested_ty) = + self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, None) + { + infer_replacements.push((output.span, suggested_ty.to_string())); + suggested_ty + } else { + visitor.visit_ty(output); + self.ast_ty_to_ty(output) + } } hir::FnRetTy::DefaultReturn(..) => tcx.mk_unit(), }; debug!("ty_of_fn: output_ty={:?}", output_ty); - let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, unsafety, abi); + let fn_ty = tcx.mk_fn_sig(input_tys.into_iter(), output_ty, decl.c_variadic, unsafety, abi); let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars); - if !self.allow_ty_infer() { + if !self.allow_ty_infer() && !(visitor.0.is_empty() && infer_replacements.is_empty()) { // We always collect the spans for placeholder types when evaluating `fn`s, but we // only want to emit an error complaining about them if infer types (`_`) are not // allowed. `allow_ty_infer` gates this behavior. We check for the presence of // `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`. - crate::collect::placeholder_type_error( + let mut diag = crate::collect::placeholder_type_error_diag( tcx, ident_span.map(|sp| sp.shrink_to_hi()), generics.params, visitor.0, + infer_replacements.iter().map(|(s, _)| *s).collect(), true, hir_ty, "function", ); + + if !infer_replacements.is_empty() { + diag.multipart_suggestion(&format!( + "try replacing `_` with the type{} in the corresponding trait method signature", + rustc_errors::pluralize!(infer_replacements.len()), + ), infer_replacements, Applicability::MachineApplicable); + } + + diag.emit(); } // Find any late-bound regions declared in return type that do @@ -2624,6 +2661,43 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { bare_fn_ty } + /// Given a fn_hir_id for a impl function, suggest the type that is found on the + /// corresponding function in the trait that the impl implements, if it exists. + /// If arg_idx is Some, then it corresponds to an input type index, otherwise it + /// corresponds to the return type. + fn suggest_trait_fn_ty_for_impl_fn_infer( + &self, + fn_hir_id: hir::HirId, + arg_idx: Option<usize>, + ) -> Option<Ty<'tcx>> { + let tcx = self.tcx(); + let hir = tcx.hir(); + + let hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), ident, .. }) = + hir.get(fn_hir_id) else { return None }; + let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(i), .. }) = + hir.get(hir.get_parent_node(fn_hir_id)) else { bug!("ImplItem should have Impl parent") }; + + let trait_ref = + self.instantiate_mono_trait_ref(i.of_trait.as_ref()?, self.ast_ty_to_ty(i.self_ty)); + + let x: &ty::AssocItem = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind( + tcx, + *ident, + ty::AssocKind::Fn, + trait_ref.def_id, + )?; + + let fn_sig = tcx.fn_sig(x.def_id).subst( + tcx, + trait_ref.substs.extend_to(tcx, x.def_id, |param, _| tcx.mk_param_from_def(param)), + ); + + let ty = if let Some(arg_idx) = arg_idx { fn_sig.input(arg_idx) } else { fn_sig.output() }; + + Some(tcx.erase_late_bound_regions(ty)) + } + fn validate_late_bound_regions( &self, constrained_regions: FxHashSet<ty::BoundRegionKind>, diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 153ab8d95fd..be77bdb0bf5 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -160,6 +160,33 @@ crate fn placeholder_type_error<'tcx>( return; } + placeholder_type_error_diag( + tcx, + span, + generics, + placeholder_types, + vec![], + suggest, + hir_ty, + kind, + ) + .emit(); +} + +crate fn placeholder_type_error_diag<'tcx>( + tcx: TyCtxt<'tcx>, + span: Option<Span>, + generics: &[hir::GenericParam<'_>], + placeholder_types: Vec<Span>, + additional_spans: Vec<Span>, + suggest: bool, + hir_ty: Option<&hir::Ty<'_>>, + kind: &'static str, +) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + if placeholder_types.is_empty() { + return bad_placeholder(tcx, additional_spans, kind); + } + let type_name = generics.next_type_param_name(None); let mut sugg: Vec<_> = placeholder_types.iter().map(|sp| (*sp, (*type_name).to_string())).collect(); @@ -182,7 +209,8 @@ crate fn placeholder_type_error<'tcx>( sugg.push((span, format!(", {}", type_name))); } - let mut err = bad_placeholder(tcx, placeholder_types, kind); + let mut err = + bad_placeholder(tcx, placeholder_types.into_iter().chain(additional_spans).collect(), kind); // Suggest, but only if it is not a function in const or static if suggest { @@ -218,7 +246,8 @@ crate fn placeholder_type_error<'tcx>( ); } } - err.emit(); + + err } fn reject_placeholder_type_signatures_in_item<'tcx>( @@ -1868,50 +1897,17 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { generics, .. }) - | ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), ident, generics, .. }) | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), ident, .. }) => { - match get_infer_ret_ty(&sig.decl.output) { - Some(ty) => { - let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id]; - // Typeck doesn't expect erased regions to be returned from `type_of`. - let fn_sig = tcx.fold_regions(fn_sig, &mut false, |r, _| match *r { - ty::ReErased => tcx.lifetimes.re_static, - _ => r, - }); - let fn_sig = ty::Binder::dummy(fn_sig); - - let mut visitor = HirPlaceholderCollector::default(); - visitor.visit_ty(ty); - let mut diag = bad_placeholder(tcx, visitor.0, "return type"); - let ret_ty = fn_sig.skip_binder().output(); - if !ret_ty.references_error() { - if !ret_ty.is_closure() { - let ret_ty_str = match ret_ty.kind() { - // Suggest a function pointer return type instead of a unique function definition - // (e.g. `fn() -> i32` instead of `fn() -> i32 { f }`, the latter of which is invalid - // syntax) - ty::FnDef(..) => ret_ty.fn_sig(tcx).to_string(), - _ => ret_ty.to_string(), - }; - diag.span_suggestion( - ty.span, - "replace with the correct return type", - ret_ty_str, - Applicability::MaybeIncorrect, - ); - } else { - // We're dealing with a closure, so we should suggest using `impl Fn` or trait bounds - // to prevent the user from getting a papercut while trying to use the unique closure - // syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`). - diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound"); - diag.note("for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html"); - } - } - diag.emit(); + infer_return_ty_for_fn_sig(tcx, sig, *ident, generics, def_id, &icx) + } - fn_sig - } - None => <dyn AstConv<'_>>::ty_of_fn( + ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), ident, generics, .. }) => { + // Do not try to inference the return type for a impl method coming from a trait + if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) = + tcx.hir().get(tcx.hir().get_parent_node(hir_id)) + && i.of_trait.is_some() + { + <dyn AstConv<'_>>::ty_of_fn( &icx, hir_id, sig.header.unsafety, @@ -1920,7 +1916,9 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { generics, Some(ident.span), None, - ), + ) + } else { + infer_return_ty_for_fn_sig(tcx, sig, *ident, generics, def_id, &icx) } } @@ -1982,6 +1980,70 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { } } +fn infer_return_ty_for_fn_sig<'tcx>( + tcx: TyCtxt<'tcx>, + sig: &hir::FnSig<'_>, + ident: Ident, + generics: &hir::Generics<'_>, + def_id: LocalDefId, + icx: &ItemCtxt<'tcx>, +) -> ty::PolyFnSig<'tcx> { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + + match get_infer_ret_ty(&sig.decl.output) { + Some(ty) => { + let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id]; + // Typeck doesn't expect erased regions to be returned from `type_of`. + let fn_sig = tcx.fold_regions(fn_sig, &mut false, |r, _| match *r { + ty::ReErased => tcx.lifetimes.re_static, + _ => r, + }); + let fn_sig = ty::Binder::dummy(fn_sig); + + let mut visitor = HirPlaceholderCollector::default(); + visitor.visit_ty(ty); + let mut diag = bad_placeholder(tcx, visitor.0, "return type"); + let ret_ty = fn_sig.skip_binder().output(); + if !ret_ty.references_error() { + if !ret_ty.is_closure() { + let ret_ty_str = match ret_ty.kind() { + // Suggest a function pointer return type instead of a unique function definition + // (e.g. `fn() -> i32` instead of `fn() -> i32 { f }`, the latter of which is invalid + // syntax) + ty::FnDef(..) => ret_ty.fn_sig(tcx).to_string(), + _ => ret_ty.to_string(), + }; + diag.span_suggestion( + ty.span, + "replace with the correct return type", + ret_ty_str, + Applicability::MaybeIncorrect, + ); + } else { + // We're dealing with a closure, so we should suggest using `impl Fn` or trait bounds + // to prevent the user from getting a papercut while trying to use the unique closure + // syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`). + diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound"); + diag.note("for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html"); + } + } + diag.emit(); + + fn_sig + } + None => <dyn AstConv<'_>>::ty_of_fn( + icx, + hir_id, + sig.header.unsafety, + sig.header.abi, + sig.decl, + generics, + Some(ident.span), + None, + ), + } +} + fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::TraitRef<'_>> { let icx = ItemCtxt::new(tcx, def_id); match tcx.hir().expect_item(def_id.expect_local()).kind { | 
