Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use rustc_ast::BoundPolarity;
use rustc_hir::{self as hir, AmbigArg};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_macros::{LintDiagnostic, Subdiagnostic};
Expand Down Expand Up @@ -66,6 +67,33 @@ declare_lint! {

declare_lint_pass!(OpaqueHiddenInferredBound => [OPAQUE_HIDDEN_INFERRED_BOUND]);

/// Check if an opaque type has an explicit `?Sized` bound by examining its HIR bounds.
fn opaque_has_maybe_sized_bound<'tcx>(
cx: &LateContext<'tcx>,
opaque_def_id: rustc_span::def_id::DefId,
) -> bool {
let Some(local_def_id) = opaque_def_id.as_local() else {
return false;
};

let opaque_ty = match cx.tcx.hir_node_by_def_id(local_def_id) {
hir::Node::OpaqueTy(opaque) => opaque,
_ => return false,
};

// Check if any of the bounds is a `?Sized` bound
for bound in opaque_ty.bounds {
if let hir::GenericBound::Trait(poly_trait_ref) = bound
&& matches!(poly_trait_ref.modifiers.polarity, BoundPolarity::Maybe(_))
&& cx.tcx.is_lang_item(poly_trait_ref.trait_ref.path.res.def_id(), hir::LangItem::Sized)
{
return true;
}
}

false
}

impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
let hir::TyKind::OpaqueDef(opaque) = &ty.kind else {
Expand Down Expand Up @@ -98,12 +126,14 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
let Some(proj_term) = proj.term.as_type() else { return };

// HACK: `impl Trait<Assoc = impl Trait2>` from an RPIT is "ok"...
// unless the nested opaque has an explicit `?Sized` bound.
if let ty::Alias(ty::Opaque, opaque_ty) = *proj_term.kind()
&& cx.tcx.parent(opaque_ty.def_id) == def_id
&& matches!(
opaque.origin,
hir::OpaqueTyOrigin::FnReturn { .. } | hir::OpaqueTyOrigin::AsyncFn { .. }
)
&& !opaque_has_maybe_sized_bound(cx, opaque_ty.def_id)
{
return;
}
Expand Down
13 changes: 13 additions & 0 deletions tests/ui/async-await/async-fn-unsized-return-type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//@ edition: 2021
#![deny(opaque_hidden_inferred_bound)]
// Test that async functions cannot return unsized types via `impl Trait + ?Sized`
// Issue #149438

use std::fmt::Debug;

async fn unsized_async() -> impl Debug + ?Sized {
//~^ ERROR opaque type `impl Future<Output = impl Debug + ?Sized>` does not satisfy its associated type bounds
123
}

fn main() {}
21 changes: 21 additions & 0 deletions tests/ui/async-await/async-fn-unsized-return-type.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error: opaque type `impl Future<Output = impl Debug + ?Sized>` does not satisfy its associated type bounds
--> $DIR/async-fn-unsized-return-type.rs:8:29
|
LL | async fn unsized_async() -> impl Debug + ?Sized {
| ^^^^^^^^^^^^^^^^^^^
|
--> $SRC_DIR/core/src/future/future.rs:LL:COL
|
= note: this associated type bound is unsatisfied for `impl Debug + ?Sized`
note: the lint level is defined here
--> $DIR/async-fn-unsized-return-type.rs:2:9
|
LL | #![deny(opaque_hidden_inferred_bound)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: add this bound
|
LL | async fn unsized_async() -> impl Debug + ?Sized + Sized {
| +++++++

error: aborting due to 1 previous error

Loading