Skip to content

[beta] backports #138525

New issue

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

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

Already on GitHub? # to your account

Merged
merged 10 commits into from
Mar 16, 2025
56 changes: 44 additions & 12 deletions compiler/rustc_codegen_ssa/src/target_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use rustc_middle::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::parse::feature_err;
use rustc_span::{Span, Symbol, sym};
use rustc_target::target_features;
use rustc_target::target_features::{self, Stability};

use crate::errors;

Expand Down Expand Up @@ -87,12 +87,17 @@ pub(crate) fn from_target_feature_attr(
// But ensure the ABI does not forbid enabling this.
// Here we do assume that LLVM doesn't add even more implied features
// we don't know about, at least no features that would have ABI effects!
if abi_feature_constraints.incompatible.contains(&name.as_str()) {
tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
span: item.span(),
feature: name.as_str(),
reason: "this feature is incompatible with the target ABI",
});
// We skip this logic in rustdoc, where we want to allow all target features of
// all targets, so we can't check their ABI compatibility and anyway we are not
// generating code so "it's fine".
if !tcx.sess.opts.actually_rustdoc {
if abi_feature_constraints.incompatible.contains(&name.as_str()) {
tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
span: item.span(),
feature: name.as_str(),
reason: "this feature is incompatible with the target ABI",
});
}
}
target_features.push(TargetFeature { name, implied: name != feature_sym })
}
Expand Down Expand Up @@ -142,11 +147,38 @@ pub(crate) fn provide(providers: &mut Providers) {
rust_target_features: |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
if tcx.sess.opts.actually_rustdoc {
// rustdoc needs to be able to document functions that use all the features, so
// whitelist them all
rustc_target::target_features::all_rust_features()
.map(|(a, b)| (a.to_string(), b))
.collect()
// HACK: rustdoc would like to pretend that we have all the target features, so we
// have to merge all the lists into one. To ensure an unstable target never prevents
// a stable one from working, we merge the stability info of all instances of the
// same target feature name, with the "most stable" taking precedence. And then we
// hope that this doesn't cause issues anywhere else in the compiler...
let mut result: UnordMap<String, Stability> = Default::default();
for (name, stability) in rustc_target::target_features::all_rust_features() {
use std::collections::hash_map::Entry;
match result.entry(name.to_owned()) {
Entry::Vacant(vacant_entry) => {
vacant_entry.insert(stability);
}
Entry::Occupied(mut occupied_entry) => {
// Merge the two stabilities, "more stable" taking precedence.
match (occupied_entry.get(), stability) {
(Stability::Stable, _)
| (
Stability::Unstable { .. },
Stability::Unstable { .. } | Stability::Forbidden { .. },
)
| (Stability::Forbidden { .. }, Stability::Forbidden { .. }) => {
// The stability in the entry is at least as good as the new one, just keep it.
}
_ => {
// Overwrite stabilite.
occupied_entry.insert(stability);
}
}
}
}
}
result
} else {
tcx.sess
.target
Expand Down
35 changes: 2 additions & 33 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1110,7 +1110,6 @@ fn check_associated_item(
let ty = tcx.type_of(item.def_id).instantiate_identity();
let ty = wfcx.normalize(span, Some(WellFormedLoc::Ty(item_id)), ty);
wfcx.register_wf_obligation(span, loc, ty.into());
check_sized_if_body(wfcx, item.def_id.expect_local(), ty, Some(span));
Ok(())
}
ty::AssocKind::Fn => {
Expand Down Expand Up @@ -1235,7 +1234,7 @@ fn check_type_defn<'tcx>(
),
wfcx.param_env,
ty,
tcx.require_lang_item(LangItem::Sized, Some(hir_ty.span)),
tcx.require_lang_item(LangItem::Sized, None),
);
}

Expand Down Expand Up @@ -1360,7 +1359,7 @@ fn check_item_type(
),
wfcx.param_env,
item_ty,
tcx.require_lang_item(LangItem::Sized, Some(ty_span)),
tcx.require_lang_item(LangItem::Sized, None),
);
}

Expand Down Expand Up @@ -1690,36 +1689,6 @@ fn check_fn_or_method<'tcx>(
);
}
}

// If the function has a body, additionally require that the return type is sized.
check_sized_if_body(
wfcx,
def_id,
sig.output(),
match hir_decl.output {
hir::FnRetTy::Return(ty) => Some(ty.span),
hir::FnRetTy::DefaultReturn(_) => None,
},
);
}

fn check_sized_if_body<'tcx>(
wfcx: &WfCheckingCtxt<'_, 'tcx>,
def_id: LocalDefId,
ty: Ty<'tcx>,
maybe_span: Option<Span>,
) {
let tcx = wfcx.tcx();
if let Some(body) = tcx.hir().maybe_body_owned_by(def_id) {
let span = maybe_span.unwrap_or(body.value.span);

wfcx.register_bound(
ObligationCause::new(span, def_id, traits::ObligationCauseCode::SizedReturnType),
wfcx.param_env,
ty,
tcx.require_lang_item(LangItem::Sized, Some(span)),
);
}
}

/// The `arbitrary_self_types_pointers` feature implies `arbitrary_self_types`.
Expand Down
19 changes: 12 additions & 7 deletions compiler/rustc_hir_typeck/src/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,17 +117,22 @@ pub(super) fn check_fn<'a, 'tcx>(

fcx.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig);

// We checked the root's ret ty during wfcheck, but not the child.
if fcx.tcx.is_typeck_child(fn_def_id.to_def_id()) {
let return_or_body_span = match decl.output {
hir::FnRetTy::DefaultReturn(_) => body.value.span,
hir::FnRetTy::Return(ty) => ty.span,
};
let return_or_body_span = match decl.output {
hir::FnRetTy::DefaultReturn(_) => body.value.span,
hir::FnRetTy::Return(ty) => ty.span,
};

fcx.require_type_is_sized(
declared_ret_ty,
return_or_body_span,
ObligationCauseCode::SizedReturnType,
);
// We checked the root's signature during wfcheck, but not the child.
if fcx.tcx.is_typeck_child(fn_def_id.to_def_id()) {
fcx.require_type_is_sized(
declared_ret_ty,
return_or_body_span,
ObligationCauseCode::SizedReturnType,
ObligationCauseCode::WellFormed(None),
);
}

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir_typeck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ fn typeck_with_inspect<'tcx>(
let wf_code = ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(def_id)));
fcx.register_wf_obligation(expected_type.into(), body.value.span, wf_code);

fcx.require_type_is_sized(expected_type, body.value.span, ObligationCauseCode::ConstSized);

// Gather locals in statics (because of block expressions).
GatherLocalsVisitor::new(&fcx).visit_body(body);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
};

self.note_obligation_cause(&mut err, &obligation);
self.point_at_returns_when_relevant(&mut err, &obligation);
err.emit()
}
}
Expand Down Expand Up @@ -809,6 +810,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
"Async",
);
self.note_obligation_cause(&mut err, &obligation);
self.point_at_returns_when_relevant(&mut err, &obligation);
return Some(err.emit());
}
}
Expand Down Expand Up @@ -854,6 +856,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
"",
);
self.note_obligation_cause(&mut err, &obligation);
self.point_at_returns_when_relevant(&mut err, &obligation);
return Some(err.emit());
}

Expand All @@ -869,6 +872,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
kind: expected_kind.as_str(),
});
self.note_obligation_cause(&mut err, &obligation);
self.point_at_returns_when_relevant(&mut err, &obligation);
return Some(err.emit());
}
}
Expand Down Expand Up @@ -2829,6 +2833,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
err.span_note(self.tcx.def_span(def_id), "opaque type is declared here");

self.note_obligation_cause(&mut err, &obligation);
self.point_at_returns_when_relevant(&mut err, &obligation);
self.dcx().try_steal_replace_and_emit_err(self.tcx.def_span(def_id), StashKey::Cycle, err)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
suggest_increasing_limit,
);
self.note_obligation_cause(&mut err, &obligation);
self.point_at_returns_when_relevant(&mut err, &obligation);
err.emit()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1765,7 +1765,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
};

err.code(E0746);
err.primary_message("return type cannot be a trait object without pointer indirection");
err.primary_message("return type cannot have an unboxed trait object");
err.children.clear();

let span = obligation.cause.span;
Expand All @@ -1781,13 +1781,25 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
} else {
("dyn ", span.shrink_to_lo())
};

err.span_suggestion_verbose(
impl_span,
"consider returning an `impl Trait` instead of a `dyn Trait`",
"impl ",
Applicability::MaybeIncorrect,
);
let alternatively = if visitor
.returns
.iter()
.map(|expr| self.typeck_results.as_ref().unwrap().expr_ty_adjusted_opt(expr))
.collect::<FxHashSet<_>>()
.len()
<= 1
{
err.span_suggestion_verbose(
impl_span,
"consider returning an `impl Trait` instead of a `dyn Trait`",
"impl ",
Applicability::MaybeIncorrect,
);
"alternatively, "
} else {
err.help("if there were a single returned type, you could use `impl Trait` instead");
""
};

let mut sugg = vec![
(span.shrink_to_lo(), format!("Box<{pre}")),
Expand Down Expand Up @@ -1819,7 +1831,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {

err.multipart_suggestion(
format!(
"alternatively, box the return type, and wrap all of the returned values in \
"{alternatively}box the return type, and wrap all of the returned values in \
`Box::new`",
),
sugg,
Expand All @@ -1829,6 +1841,41 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
true
}

pub(super) fn point_at_returns_when_relevant(
&self,
err: &mut Diag<'_>,
obligation: &PredicateObligation<'tcx>,
) {
match obligation.cause.code().peel_derives() {
ObligationCauseCode::SizedReturnType => {}
_ => return,
}

let hir = self.tcx.hir();
let node = self.tcx.hir_node_by_def_id(obligation.cause.body_id);
if let hir::Node::Item(hir::Item {
kind: hir::ItemKind::Fn { body: body_id, .. }, ..
}) = node
{
let body = hir.body(*body_id);
// Point at all the `return`s in the function as they have failed trait bounds.
let mut visitor = ReturnsVisitor::default();
visitor.visit_body(body);
let typeck_results = self.typeck_results.as_ref().unwrap();
for expr in &visitor.returns {
if let Some(returned_ty) = typeck_results.node_type_opt(expr.hir_id) {
let ty = self.resolve_vars_if_possible(returned_ty);
if ty.references_error() {
// don't print out the [type error] here
err.downgrade_to_delayed_bug();
} else {
err.span_label(expr.span, format!("this returned value is of type `{ty}`"));
}
}
}
}
}

pub(super) fn report_closure_arg_mismatch(
&self,
span: Span,
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2425,7 +2425,7 @@ pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
/// # Platform-specific behavior
///
/// This function currently corresponds to the `rename` function on Unix
/// and the `SetFileInformationByHandle` function on Windows.
/// and the `MoveFileExW` or `SetFileInformationByHandle` function on Windows.
///
/// Because of this, the behavior when both `from` and `to` exist differs. On
/// Unix, if `from` is a directory, `to` must also be an (empty) directory. If
Expand Down
Loading
Loading