Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rollup of 3 pull requests #112530

Merged
merged 8 commits into from
Jun 11, 2023
8 changes: 8 additions & 0 deletions compiler/rustc_hir_typeck/src/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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| {
Expand Down
113 changes: 4 additions & 109 deletions compiler/rustc_hir_typeck/src/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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 _;
Expand Down Expand Up @@ -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
Expand All @@ -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),
Expand Down Expand Up @@ -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,
Expand All @@ -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);
Expand Down Expand Up @@ -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.
Expand Down
20 changes: 19 additions & 1 deletion compiler/rustc_infer/src/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
7 changes: 1 addition & 6 deletions src/bootstrap/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down
86 changes: 72 additions & 14 deletions src/bootstrap/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand All @@ -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",
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/bootstrap/defaults/config.tools.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
8 changes: 8 additions & 0 deletions src/bootstrap/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
9 changes: 9 additions & 0 deletions tests/ui/impl-trait/dont-suggest-box-on-empty-else-arm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
fn test() -> impl std::fmt::Debug {
if true {
"boo2"
} else {
//~^ ERROR `if` and `else` have incompatible types
}
}

fn main() {}
Loading