Skip to content
Closed
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
25 changes: 22 additions & 3 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use super::compare_impl_item::check_type_bounds;
use super::*;
use crate::check::wfcheck::{
check_associated_item, check_trait_item, check_variances_for_type_defn, check_where_clauses,
enter_wf_checking_ctxt,
enter_wf_checking_ctxt, enter_wf_checking_ctxt_without_checking_global_bounds,
};

fn add_abi_diag_help<T: EmissionGuarantee>(abi: ExternAbi, diag: &mut Diag<'_, T>) {
Expand Down Expand Up @@ -937,10 +937,11 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
tcx.ensure_ok().type_of(def_id);
tcx.ensure_ok().predicates_of(def_id);
check_type_alias_type_params_are_used(tcx, def_id);

let ty = tcx.type_of(def_id).instantiate_identity();
let span = tcx.def_span(def_id);
if tcx.type_alias_is_lazy(def_id) {
res = res.and(enter_wf_checking_ctxt(tcx, def_id, |wfcx| {
let ty = tcx.type_of(def_id).instantiate_identity();
let span = tcx.def_span(def_id);
let item_ty = wfcx.deeply_normalize(span, Some(WellFormedLoc::Ty(def_id)), ty);
wfcx.register_wf_obligation(
span,
Expand All @@ -951,6 +952,24 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
Ok(())
}));
check_variances_for_type_defn(tcx, def_id);
} else {
res = res.and(enter_wf_checking_ctxt_without_checking_global_bounds(
tcx,
def_id,
|wfcx| {
// HACK: We sometimes incidentally check that const arguments
// have the correct type as a side effect of the anon const
// desugaring. To make this "consistent" for users we explicitly
// check `ConstArgHasType` clauses so that const args that don't
// go through an anon const still have their types checked.
//
// We use the unnormalized type as this mirrors the behaviour
// that we previously would have had when all const arguments
// were anon consts.
wfcx.register_const_arg_has_type_obligations_from_wf(span, ty);
Ok(())
},
));
}

// Only `Node::Item` and `Node::ForeignItem` still have HIR based
Expand Down
48 changes: 47 additions & 1 deletion compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,59 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
ty::ClauseKind::WellFormed(term),
));
}

pub(super) fn register_const_arg_has_type_obligations_from_wf(&self, span: Span, ty: Ty<'tcx>) {
let Some(wf_obligations) = traits::wf::unnormalized_obligations(
self.ocx.infcx,
self.param_env,
ty.into(),
span,
self.body_def_id,
) else {
return;
};

let const_arg_has_type_clauses =
wf_obligations.into_iter().filter(|o| match o.predicate.kind().skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _))
if matches!(ct.kind(), ty::ConstKind::Param(..)) =>
{
true
}
_ => false,
});
self.ocx.register_obligations(const_arg_has_type_clauses);
}
}

pub(super) fn enter_wf_checking_ctxt<'tcx, F>(
tcx: TyCtxt<'tcx>,
body_def_id: LocalDefId,
f: F,
) -> Result<(), ErrorGuaranteed>
where
F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>) -> Result<(), ErrorGuaranteed>,
{
enter_wf_checking_ctxt_inner(tcx, body_def_id, !tcx.features().trivial_bounds(), f)
}

pub(super) fn enter_wf_checking_ctxt_without_checking_global_bounds<'tcx, F>(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems suboptimal to me that we implicitly check things about the where clauses of an item just from creating a wfcx. ideally this would happen as part of actually checking the where clauses or something?

tcx: TyCtxt<'tcx>,
body_def_id: LocalDefId,
f: F,
) -> Result<(), ErrorGuaranteed>
where
F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>) -> Result<(), ErrorGuaranteed>,
{
enter_wf_checking_ctxt_inner(tcx, body_def_id, false, f)
}

pub(super) fn enter_wf_checking_ctxt_inner<'tcx, F>(
tcx: TyCtxt<'tcx>,
body_def_id: LocalDefId,
check_false_global_bounds: bool,
f: F,
) -> Result<(), ErrorGuaranteed>
where
F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>) -> Result<(), ErrorGuaranteed>,
{
Expand All @@ -139,7 +185,7 @@ where

let mut wfcx = WfCheckingCtxt { ocx, body_def_id, param_env };

if !tcx.features().trivial_bounds() {
if check_false_global_bounds {
wfcx.check_false_global_bounds()
}
f(&mut wfcx)?;
Expand Down
177 changes: 108 additions & 69 deletions compiler/rustc_trait_selection/src/traits/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
//! well formed is performed elsewhere (e.g. during type checking or item well formedness
//! checking).

use core::ops::ControlFlow;
use std::iter;

use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::lang_items::LangItem;
use rustc_infer::traits::{ObligationCauseCode, PredicateObligations};
use rustc_infer::traits::{ObligationCauseCode, PredicateObligation, PredicateObligations};
use rustc_middle::bug;
use rustc_middle::ty::{
self, GenericArgsRef, Term, TermKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
Expand Down Expand Up @@ -562,7 +563,6 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
debug!(?self.out);
}

#[instrument(level = "debug", skip(self))]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dont yeet this

fn nominal_obligations(
&mut self,
def_id: DefId,
Expand Down Expand Up @@ -604,69 +604,29 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
.collect()
}

fn add_wf_preds_for_dyn_ty(
fn nominal_obligations_not_referencing_self(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instantiate_identity the preds then walk for Self#0 then wrap in an early binder and instantiate a second time?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or why am i even filtering to stuff not involving Self i dont remember, should just be able to use the object type to instantiate Self ...

&mut self,
ty: Ty<'tcx>,
data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
region: ty::Region<'tcx>,
) {
// Imagine a type like this:
//
// trait Foo { }
// trait Bar<'c> : 'c { }
//
// &'b (Foo+'c+Bar<'d>)
// ^
//
// In this case, the following relationships must hold:
//
// 'b <= 'c
// 'd <= 'c
//
// The first conditions is due to the normal region pointer
// rules, which say that a reference cannot outlive its
// referent.
//
// The final condition may be a bit surprising. In particular,
// you may expect that it would have been `'c <= 'd`, since
// usually lifetimes of outer things are conservative
// approximations for inner things. However, it works somewhat
// differently with trait objects: here the idea is that if the
// user specifies a region bound (`'c`, in this case) it is the
// "master bound" that *implies* that bounds from other traits are
// all met. (Remember that *all bounds* in a type like
// `Foo+Bar+Zed` must be met, not just one, hence if we write
// `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and
// 'y.)
//
// Note: in fact we only permit builtin traits, not `Bar<'d>`, I
// am looking forward to the future here.
if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() {
let implicit_bounds = object_region_bounds(self.tcx(), data);

let explicit_bound = region;

self.out.reserve(implicit_bounds.len());
for implicit_bound in implicit_bounds {
let cause = self.cause(ObligationCauseCode::ObjectTypeBound(ty, explicit_bound));
let outlives =
ty::Binder::dummy(ty::OutlivesPredicate(explicit_bound, implicit_bound));
self.out.push(traits::Obligation::with_depth(
self.tcx(),
cause,
self.recursion_depth,
self.param_env,
outlives,
));
trait_id_and_args: ty::Binder<'tcx, ty::ExistentialTraitRef<'tcx>>,
) -> impl Iterator<Item = PredicateObligation<'tcx>> + use<'tcx> {
let tcx = self.tcx();
let def_id = trait_id_and_args.skip_binder().def_id;
let bogus_self_ty: Ty<'tcx> =
Ty::new_param(tcx, u32::MAX, rustc_span::sym::RustaceansAreAwesome);
let args = trait_id_and_args.skip_binder().with_self_ty(tcx, bogus_self_ty).args;

struct HasBogusSelfTy<'tcx>(Ty<'tcx>);
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasBogusSelfTy<'tcx> {
type Result = ControlFlow<()>;

fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<()> {
// FIXME: should this check be post normalization rather than quite so syntactic
if ty == self.0 { ControlFlow::Break(()) } else { ty.super_visit_with(self) }
}

// We don't add any wf predicates corresponding to the trait ref's generic arguments
// which allows code like this to compile:
// ```rust
// trait Trait<T: Sized> {}
// fn foo(_: &dyn Trait<[u32]>) {}
// ```
}

self.nominal_obligations(def_id, args).into_iter().filter(move |o| {
o.predicate.visit_with(&mut HasBogusSelfTy(bogus_self_ty)).is_continue()
})
}

fn add_wf_preds_for_pat_ty(&mut self, base_ty: Ty<'tcx>, pat: ty::Pattern<'tcx>) {
Expand Down Expand Up @@ -932,24 +892,103 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
// We recurse into the binder below.
}

ty::Dynamic(data, r) => {
// WfObject
ty::Dynamic(data, region) => {
// Imagine a type like this:
//
// trait Foo { }
// trait Bar<'c> : 'c { }
//
// &'b (Foo+'c+Bar<'d>)
// ^
//
// In this case, the following relationships must hold:
//
// 'b <= 'c
// 'd <= 'c
//
// The first conditions is due to the normal region pointer
// rules, which say that a reference cannot outlive its
// referent.
//
// Here, we defer WF checking due to higher-ranked
// regions. This is perhaps not ideal.
self.add_wf_preds_for_dyn_ty(t, data, r);
// The final condition may be a bit surprising. In particular,
// you may expect that it would have been `'c <= 'd`, since
// usually lifetimes of outer things are conservative
// approximations for inner things. However, it works somewhat
// differently with trait objects: here the idea is that if the
// user specifies a region bound (`'c`, in this case) it is the
// "master bound" that *implies* that bounds from other traits are
// all met. (Remember that *all bounds* in a type like
// `Foo+Bar+Zed` must be met, not just one, hence if we write
// `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and
// 'y.)
//
// Note: in fact we only permit builtin traits, not `Bar<'d>`, I
// am looking forward to the future here.
if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() {
let implicit_bounds = object_region_bounds(self.tcx(), data);

let explicit_bound = region;

self.out.reserve(implicit_bounds.len());
for implicit_bound in implicit_bounds {
let cause =
self.cause(ObligationCauseCode::ObjectTypeBound(t, explicit_bound));
let outlives = ty::Binder::dummy(ty::OutlivesPredicate(
explicit_bound,
implicit_bound,
));
self.out.push(traits::Obligation::with_depth(
self.tcx(),
cause,
self.recursion_depth,
self.param_env,
outlives,
));
}
}

// FIXME(#27579) RFC also considers adding trait
// obligations that don't refer to Self and
// checking those
if let Some(principal) = data.principal_def_id() {
if let Some(principal) = data.principal() {
let principal_def_id = principal.skip_binder().def_id;
self.out.push(traits::Obligation::with_depth(
tcx,
self.cause(ObligationCauseCode::WellFormed(None)),
self.recursion_depth,
self.param_env,
ty::Binder::dummy(ty::PredicateKind::DynCompatible(principal)),
ty::Binder::dummy(ty::PredicateKind::DynCompatible(principal_def_id)),
));

// For the most part we don't add wf predicates corresponding to
// the trait ref's generic arguments which allows code like this
// to compile:
// ```rust
// trait Trait<T: Sized> {}
// fn foo(_: &dyn Trait<[u32]>) {}
// ```
//
// However, we sometimes incidentally check that const arguments
// have the correct type as a side effect of the anon const
// desugaring. To make this "consistent" for users we explicitly
// check `ConstArgHasType` clauses so that const args that don't
// go through an anon const still have their types checked.
let wf_obligations = self.nominal_obligations_not_referencing_self(principal);
let const_arg_has_type_clauses =
wf_obligations.filter(|o| match o.predicate.kind().skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _))
if matches!(ct.kind(), ty::ConstKind::Param(..)) =>
{
debug!("skipped o: {:?}", o);
true
}
_ => false,
});

let collected = const_arg_has_type_clauses.collect::<Vec<_>>();
debug!("{:?}", collected);

self.out.extend(collected);
}
}

Expand Down
Loading
Loading