Auto merge of #112530 - matthiaskrgr:rollup-qee1kc1, r=matthiaskrgr

Rollup of 3 pull requests

Successful merges:

 - #112487 (Update documentation for `tools` defaults)
 - #112513 (Dont compute `opt_suggest_box_span` span for TAIT)
 - #112528 (bootstrap: Don't override `debuginfo-level = 1` to mean `line-tables-only`)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-06-11 17:33:51 +00:00
commit 37998ab508
9 changed files with 139 additions and 130 deletions

View file

@ -510,6 +510,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
..
} = self.type_var_origin(expected)? else { return None; };
let Some(rpit_local_def_id) = rpit_def_id.as_local() else { return None; };
if !matches!(
self.tcx.hir().expect_item(rpit_local_def_id).expect_opaque_ty().origin,
hir::OpaqueTyOrigin::FnReturn(..)
) {
return None;
}
let sig = self.body_fn_sig()?;
let substs = sig.output().walk().find_map(|arg| {

View file

@ -36,9 +36,7 @@
//! ```
use crate::FnCtxt;
use rustc_errors::{
struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
};
use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{self, Visitor};
@ -58,7 +56,7 @@ use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::{self, Ty, TypeAndMut};
use rustc_session::parse::feature_err;
use rustc_span::symbol::sym;
use rustc_span::{self, BytePos, DesugaringKind, Span};
use rustc_span::{self, DesugaringKind};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
@ -1702,9 +1700,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
let mut err = fcx.err_ctxt().report_mismatched_types(cause, expected, found, ty_err);
let mut pointing_at_return_type = false;
let mut fn_output = None;
let parent_id = fcx.tcx.hir().parent_id(id);
let parent = fcx.tcx.hir().get(parent_id);
if let Some(expr) = expression
@ -1717,7 +1712,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
// label pointing out the cause for the type coercion will be wrong
// as prior return coercions would not be relevant (#57664).
let fn_decl = if let (Some(expr), Some(blk_id)) = (expression, blk_id) {
pointing_at_return_type =
let pointing_at_return_type =
fcx.suggest_mismatched_types_on_tail(&mut err, expr, expected, found, blk_id);
if let (Some(cond_expr), true, false) = (
fcx.tcx.hir().get_if_cause(expr.hir_id),
@ -1749,7 +1744,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
if let Some((fn_id, fn_decl, can_suggest)) = fn_decl {
if blk_id.is_none() {
pointing_at_return_type |= fcx.suggest_missing_return_type(
fcx.suggest_missing_return_type(
&mut err,
&fn_decl,
expected,
@ -1758,9 +1753,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
fn_id,
);
}
if !pointing_at_return_type {
fn_output = Some(&fn_decl.output); // `impl Trait` return type
}
}
let parent_id = fcx.tcx.hir().get_parent_item(id);
@ -1795,106 +1787,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
);
}
if let (Some(sp), Some(fn_output)) = (ret_coercion_span, fn_output) {
self.add_impl_trait_explanation(&mut err, cause, fcx, expected, sp, fn_output);
}
err
}
fn add_impl_trait_explanation<'a>(
&self,
err: &mut Diagnostic,
cause: &ObligationCause<'tcx>,
fcx: &FnCtxt<'a, 'tcx>,
expected: Ty<'tcx>,
sp: Span,
fn_output: &hir::FnRetTy<'_>,
) {
let return_sp = fn_output.span();
err.span_label(return_sp, "expected because this return type...");
err.span_label(
sp,
format!("...is found to be `{}` here", fcx.resolve_vars_with_obligations(expected)),
);
let impl_trait_msg = "for information on `impl Trait`, see \
<https://doc.rust-lang.org/book/ch10-02-traits.html\
#returning-types-that-implement-traits>";
let trait_obj_msg = "for information on trait objects, see \
<https://doc.rust-lang.org/book/ch17-02-trait-objects.html\
#using-trait-objects-that-allow-for-values-of-different-types>";
err.note("to return `impl Trait`, all returned values must be of the same type");
err.note(impl_trait_msg);
let snippet = fcx
.tcx
.sess
.source_map()
.span_to_snippet(return_sp)
.unwrap_or_else(|_| "dyn Trait".to_string());
let mut snippet_iter = snippet.split_whitespace();
let has_impl = snippet_iter.next().is_some_and(|s| s == "impl");
// Only suggest `Box<dyn Trait>` if `Trait` in `impl Trait` is object safe.
let mut is_object_safe = false;
if let hir::FnRetTy::Return(ty) = fn_output
// Get the return type.
&& let hir::TyKind::OpaqueDef(..) = ty.kind
{
let ty = fcx.astconv().ast_ty_to_ty( ty);
// Get the `impl Trait`'s `DefId`.
if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = ty.kind()
// Get the `impl Trait`'s `Item` so that we can get its trait bounds and
// get the `Trait`'s `DefId`.
&& let hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, .. }) =
fcx.tcx.hir().expect_item(def_id.expect_local()).kind
{
// Are of this `impl Trait`'s traits object safe?
is_object_safe = bounds.iter().all(|bound| {
bound
.trait_ref()
.and_then(|t| t.trait_def_id())
.is_some_and(|def_id| {
fcx.tcx.check_is_object_safe(def_id)
})
})
}
};
if has_impl {
if is_object_safe {
err.multipart_suggestion(
"you could change the return type to be a boxed trait object",
vec![
(return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()),
(return_sp.shrink_to_hi(), ">".to_string()),
],
Applicability::MachineApplicable,
);
let sugg = [sp, cause.span]
.into_iter()
.flat_map(|sp| {
[
(sp.shrink_to_lo(), "Box::new(".to_string()),
(sp.shrink_to_hi(), ")".to_string()),
]
.into_iter()
})
.collect::<Vec<_>>();
err.multipart_suggestion(
"if you change the return type to expect trait objects, box the returned \
expressions",
sugg,
Applicability::MaybeIncorrect,
);
} else {
err.help(format!(
"if the trait `{}` were object safe, you could return a boxed trait object",
&snippet[5..]
));
}
err.note(trait_obj_msg);
}
err.help("you could instead create a new `enum` with a variant for each returned type");
}
/// Checks whether the return type is unsized via an obligation, which makes
/// sure we consider `dyn Trait: Sized` where clauses, which are trivially
/// false but technically valid for typeck.

View file

@ -847,7 +847,25 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
) {
err.subdiagnostic(subdiag);
}
if let Some(ret_sp) = opt_suggest_box_span {
// don't suggest wrapping either blocks in `if .. {} else {}`
let is_empty_arm = |id| {
let hir::Node::Block(blk) = self.tcx.hir().get(id)
else {
return false;
};
if blk.expr.is_some() || !blk.stmts.is_empty() {
return false;
}
let Some((_, hir::Node::Expr(expr))) = self.tcx.hir().parent_iter(id).nth(1)
else {
return false;
};
matches!(expr.kind, hir::ExprKind::If(..))
};
if let Some(ret_sp) = opt_suggest_box_span
&& !is_empty_arm(then_id)
&& !is_empty_arm(else_id)
{
self.suggest_boxing_for_return_impl_trait(
err,
ret_sp,

View file

@ -1649,12 +1649,7 @@ impl<'a> Builder<'a> {
self.config.rust_debuginfo_level_tools
}
};
if debuginfo_level == 1 {
// Use less debuginfo than the default to save on disk space.
cargo.env(profile_var("DEBUG"), "line-tables-only");
} else {
cargo.env(profile_var("DEBUG"), debuginfo_level.to_string());
};
cargo.env(profile_var("DEBUG"), debuginfo_level.to_string());
if self.cc[&target].args().iter().any(|arg| arg == "-gz") {
rustflags.arg("-Clink-arg=-gz");
}

View file

@ -10,7 +10,7 @@ use std::cell::{Cell, RefCell};
use std::cmp;
use std::collections::{HashMap, HashSet};
use std::env;
use std::fmt;
use std::fmt::{self, Display};
use std::fs;
use std::io::IsTerminal;
use std::path::{Path, PathBuf};
@ -50,6 +50,57 @@ pub enum DryRun {
UserSelected,
}
#[derive(Copy, Clone, Default)]
pub enum DebuginfoLevel {
#[default]
None,
LineTablesOnly,
Limited,
Full,
}
// NOTE: can't derive(Deserialize) because the intermediate trip through toml::Value only
// deserializes i64, and derive() only generates visit_u64
impl<'de> Deserialize<'de> for DebuginfoLevel {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error;
Ok(match Deserialize::deserialize(deserializer)? {
StringOrInt::String("none") | StringOrInt::Int(0) => DebuginfoLevel::None,
StringOrInt::String("line-tables-only") => DebuginfoLevel::LineTablesOnly,
StringOrInt::String("limited") | StringOrInt::Int(1) => DebuginfoLevel::Limited,
StringOrInt::String("full") | StringOrInt::Int(2) => DebuginfoLevel::Full,
StringOrInt::Int(n) => {
let other = serde::de::Unexpected::Signed(n);
return Err(D::Error::invalid_value(other, &"expected 0, 1, or 2"));
}
StringOrInt::String(s) => {
let other = serde::de::Unexpected::Str(s);
return Err(D::Error::invalid_value(
other,
&"expected none, line-tables-only, limited, or full",
));
}
})
}
}
/// Suitable for passing to `-C debuginfo`
impl Display for DebuginfoLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use DebuginfoLevel::*;
f.write_str(match self {
None => "0",
LineTablesOnly => "line-tables-only",
Limited => "1",
Full => "2",
})
}
}
/// Global configuration for the entire build and/or bootstrap.
///
/// This structure is parsed from `config.toml`, and some of the fields are inferred from `git` or build-time parameters.
@ -159,10 +210,10 @@ pub struct Config {
pub rust_overflow_checks: bool,
pub rust_overflow_checks_std: bool,
pub rust_debug_logging: bool,
pub rust_debuginfo_level_rustc: u32,
pub rust_debuginfo_level_std: u32,
pub rust_debuginfo_level_tools: u32,
pub rust_debuginfo_level_tests: u32,
pub rust_debuginfo_level_rustc: DebuginfoLevel,
pub rust_debuginfo_level_std: DebuginfoLevel,
pub rust_debuginfo_level_tools: DebuginfoLevel,
pub rust_debuginfo_level_tests: DebuginfoLevel,
pub rust_split_debuginfo: SplitDebuginfo,
pub rust_rpath: bool,
pub rustc_parallel: bool,
@ -810,6 +861,13 @@ impl Default for StringOrBool {
}
}
#[derive(Deserialize)]
#[serde(untagged)]
enum StringOrInt<'a> {
String(&'a str),
Int(i64),
}
define_config! {
/// TOML representation of how the Rust build is configured.
struct Rust {
@ -822,11 +880,11 @@ define_config! {
overflow_checks: Option<bool> = "overflow-checks",
overflow_checks_std: Option<bool> = "overflow-checks-std",
debug_logging: Option<bool> = "debug-logging",
debuginfo_level: Option<u32> = "debuginfo-level",
debuginfo_level_rustc: Option<u32> = "debuginfo-level-rustc",
debuginfo_level_std: Option<u32> = "debuginfo-level-std",
debuginfo_level_tools: Option<u32> = "debuginfo-level-tools",
debuginfo_level_tests: Option<u32> = "debuginfo-level-tests",
debuginfo_level: Option<DebuginfoLevel> = "debuginfo-level",
debuginfo_level_rustc: Option<DebuginfoLevel> = "debuginfo-level-rustc",
debuginfo_level_std: Option<DebuginfoLevel> = "debuginfo-level-std",
debuginfo_level_tools: Option<DebuginfoLevel> = "debuginfo-level-tools",
debuginfo_level_tests: Option<DebuginfoLevel> = "debuginfo-level-tests",
split_debuginfo: Option<String> = "split-debuginfo",
run_dsymutil: Option<bool> = "run-dsymutil",
backtrace: Option<bool> = "backtrace",
@ -1478,17 +1536,17 @@ impl Config {
config.rust_debug_logging = debug_logging.unwrap_or(config.rust_debug_assertions);
let with_defaults = |debuginfo_level_specific: Option<u32>| {
let with_defaults = |debuginfo_level_specific: Option<_>| {
debuginfo_level_specific.or(debuginfo_level).unwrap_or(if debug == Some(true) {
1
DebuginfoLevel::Limited
} else {
0
DebuginfoLevel::None
})
};
config.rust_debuginfo_level_rustc = with_defaults(debuginfo_level_rustc);
config.rust_debuginfo_level_std = with_defaults(debuginfo_level_std);
config.rust_debuginfo_level_tools = with_defaults(debuginfo_level_tools);
config.rust_debuginfo_level_tests = debuginfo_level_tests.unwrap_or(0);
config.rust_debuginfo_level_tests = debuginfo_level_tests.unwrap_or(DebuginfoLevel::None);
let download_rustc = config.download_rustc_commit.is_some();
// See https://github.com/rust-lang/compiler-team/issues/326

View file

@ -9,6 +9,8 @@ debug-logging = true
incremental = true
# Download rustc from CI instead of building it from source.
# This cuts compile times by almost 60x, but means you can't modify the compiler.
# Using these defaults will download the stage2 compiler (see `download-rustc`
# setting) and the stage2 toolchain should therefore be used for these defaults.
download-rustc = "if-unchanged"
[build]

View file

@ -176,6 +176,14 @@ pub fn setup(config: &Config, profile: Profile) {
);
}
if profile == Profile::Tools {
eprintln!();
eprintln!(
"note: the `tools` profile sets up the `stage2` toolchain (use \
`rustup toolchain link 'name' host/build/stage2` to use rustc)"
)
}
let path = &config.config.clone().unwrap_or(PathBuf::from("config.toml"));
setup_config_toml(path, profile, config);
}

View file

@ -0,0 +1,9 @@
fn test() -> impl std::fmt::Debug {
if true {
"boo2"
} else {
//~^ ERROR `if` and `else` have incompatible types
}
}
fn main() {}

View file

@ -0,0 +1,16 @@
error[E0308]: `if` and `else` have incompatible types
--> $DIR/dont-suggest-box-on-empty-else-arm.rs:4:12
|
LL | if true {
| ------- `if` and `else` have incompatible types
LL | "boo2"
| ------ expected because of this
LL | } else {
| ____________^
LL | |
LL | | }
| |_____^ expected `&str`, found `()`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.