Skip to content

elaborate: avoid projections with unconstrained bound regions #136303

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

Closed
wants to merge 3 commits into from
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
2 changes: 2 additions & 0 deletions compiler/rustc_ast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ pub mod token;
pub mod tokenstream;
pub mod visit;

pub use rustc_ast_ir::Limit;

pub use self::ast::*;
pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasTokens};

Expand Down
51 changes: 51 additions & 0 deletions compiler/rustc_ast_ir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
#![warn(unreachable_pub)]
// tidy-alphabetical-end

use std::fmt;
use std::ops::{Div, Mul};

#[cfg(feature = "nightly")]
use rustc_macros::{Decodable, Encodable, HashStable_NoContext};

Expand Down Expand Up @@ -86,3 +89,51 @@ pub enum Pinnedness {
Not,
Pinned,
}

/// New-type wrapper around `usize` for representing limits. Ensures that comparisons against
/// limits are consistent throughout the compiler.
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))]
pub struct Limit(pub usize);

impl Limit {
/// Create a new limit from a `usize`.
pub fn new(value: usize) -> Self {
Limit(value)
}

/// Check that `value` is within the limit. Ensures that the same comparisons are used
/// throughout the compiler, as mismatches can cause ICEs, see #72540.
#[inline]
pub fn value_within_limit(&self, value: usize) -> bool {
value <= self.0
}
}

impl From<usize> for Limit {
fn from(value: usize) -> Self {
Self::new(value)
}
}

impl fmt::Display for Limit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}

impl Div<usize> for Limit {
type Output = Limit;

fn div(self, rhs: usize) -> Self::Output {
Limit::new(self.0 / rhs)
}
}

impl Mul<usize> for Limit {
type Output = Limit;

fn mul(self, rhs: usize) -> Self::Output {
Limit::new(self.0 * rhs)
}
}
7 changes: 7 additions & 0 deletions compiler/rustc_errors/src/diagnostic_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::path::{Path, PathBuf};
use std::process::ExitStatus;

use rustc_abi::TargetDataLayoutErrors;
use rustc_ast::Limit;
use rustc_ast::util::parser::ExprPrecedence;
use rustc_ast_pretty::pprust;
use rustc_macros::Subdiagnostic;
Expand Down Expand Up @@ -190,6 +191,12 @@ impl IntoDiagArg for PathBuf {
}
}

impl IntoDiagArg for Limit {
fn into_diag_arg(self) -> rustc_errors::DiagArgValue {
self.to_string().into_diag_arg()
}
}

impl IntoDiagArg for PanicStrategy {
fn into_diag_arg(self) -> DiagArgValue {
DiagArgValue::Str(Cow::Owned(self.desc().to_string()))
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::ObligationCtxt;
use rustc_type_ir::visit::collect_referenced_late_bound_regions;
use tracing::{debug, instrument};

use crate::check::intrinsic::intrinsic_operation_unsafety;
Expand Down Expand Up @@ -635,11 +636,10 @@ fn get_new_lifetime_name<'tcx>(
poly_trait_ref: ty::PolyTraitRef<'tcx>,
generics: &hir::Generics<'tcx>,
) -> String {
let existing_lifetimes = tcx
.collect_referenced_late_bound_regions(poly_trait_ref)
let existing_lifetimes = collect_referenced_late_bound_regions(tcx, poly_trait_ref)
.into_iter()
.filter_map(|lt| {
if let ty::BoundRegionKind::Named(_, name) = lt {
if let ty::BoundRegionKind::Named(_, name) = lt.kind {
Some(name.as_str().to_string())
} else {
None
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir_analysis/src/constrained_generic_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use rustc_middle::bug;
use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitor};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::Span;
use rustc_type_ir::fold::TypeFoldable;
use rustc_type_ir::fold::{TypeFoldable, expand_weak_alias_tys};
use tracing::debug;

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
Expand Down Expand Up @@ -51,7 +51,7 @@ pub(crate) fn parameters_for<'tcx>(
include_nonconstraining: bool,
) -> Vec<Parameter> {
let mut collector = ParameterCollector { parameters: vec![], include_nonconstraining };
let value = if !include_nonconstraining { tcx.expand_weak_alias_tys(value) } else { value };
let value = if !include_nonconstraining { expand_weak_alias_tys(tcx, value) } else { value };
value.visit_with(&mut collector);
collector.parameters
}
Expand Down
9 changes: 6 additions & 3 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ use rustc_middle::bug;
use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt};
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
use rustc_trait_selection::traits;
use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
use rustc_type_ir::visit::{
TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
collect_constrained_late_bound_regions, collect_referenced_late_bound_regions,
};
use smallvec::SmallVec;
use tracing::{debug, instrument};

Expand Down Expand Up @@ -358,9 +361,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
// for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
let late_bound_in_projection_ty =
tcx.collect_constrained_late_bound_regions(projection_term);
collect_constrained_late_bound_regions(tcx, projection_term);
let late_bound_in_term =
tcx.collect_referenced_late_bound_regions(trait_ref.rebind(term));
collect_referenced_late_bound_regions(tcx, trait_ref.rebind(term));
debug!(?late_bound_in_projection_ty);
debug!(?late_bound_in_term);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility;
use rustc_trait_selection::traits::{self, hir_ty_lowering_dyn_compatibility_violations};
use rustc_type_ir::elaborate::ClauseWithSupertraitSpan;
use rustc_type_ir::visit::{
collect_constrained_late_bound_regions, collect_referenced_late_bound_regions,
};
use smallvec::{SmallVec, smallvec};
use tracing::{debug, instrument};

Expand Down Expand Up @@ -362,10 +365,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
//
// for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
// for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
let late_bound_in_projection_term =
tcx.collect_constrained_late_bound_regions(pred.map_bound(|pred| pred.projection_term));
let late_bound_in_projection_term = collect_constrained_late_bound_regions(
tcx,
pred.map_bound(|pred| pred.projection_term),
);
let late_bound_in_term =
tcx.collect_referenced_late_bound_regions(pred.map_bound(|pred| pred.term));
collect_referenced_late_bound_regions(tcx, pred.map_bound(|pred| pred.term));
debug!(?late_bound_in_projection_term);
debug!(?late_bound_in_term);

Expand Down
15 changes: 9 additions & 6 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::wf::object_region_bounds;
use rustc_trait_selection::traits::{self, ObligationCtxt};
use rustc_type_ir::visit::{
collect_constrained_late_bound_regions, collect_referenced_late_bound_regions,
};
use tracing::{debug, instrument};

use crate::bounds::Bounds;
Expand Down Expand Up @@ -2606,9 +2609,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// for<'a> fn(&'a String) -> &'a str <-- 'a is ok
let inputs = bare_fn_ty.inputs();
let late_bound_in_args =
tcx.collect_constrained_late_bound_regions(inputs.map_bound(|i| i.to_owned()));
collect_constrained_late_bound_regions(tcx, inputs.map_bound(|i| i.to_owned()));
let output = bare_fn_ty.output();
let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(output);
let late_bound_in_ret = collect_referenced_late_bound_regions(tcx, output);

self.validate_late_bound_regions(late_bound_in_args, late_bound_in_ret, |br_name| {
struct_span_code_err!(
Expand Down Expand Up @@ -2665,12 +2668,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
#[instrument(level = "trace", skip(self, generate_err))]
fn validate_late_bound_regions<'cx>(
&'cx self,
constrained_regions: FxIndexSet<ty::BoundRegionKind>,
referenced_regions: FxIndexSet<ty::BoundRegionKind>,
constrained_regions: FxIndexSet<ty::BoundRegion>,
referenced_regions: FxIndexSet<ty::BoundRegion>,
generate_err: impl Fn(&str) -> Diag<'cx>,
) {
for br in referenced_regions.difference(&constrained_regions) {
let br_name = match *br {
let br_name = match br.kind {
ty::BoundRegionKind::Named(_, kw::UnderscoreLifetime)
| ty::BoundRegionKind::Anon
| ty::BoundRegionKind::ClosureEnv => "an anonymous lifetime".to_string(),
Expand All @@ -2680,7 +2683,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let mut err = generate_err(&br_name);

if let ty::BoundRegionKind::Named(_, kw::UnderscoreLifetime)
| ty::BoundRegionKind::Anon = *br
| ty::BoundRegionKind::Anon = br.kind
{
// The only way for an anonymous lifetime to wind up
// in the return type but **also** be unconstrained is
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
self.parent(def_id)
}

fn recursion_limit(self) -> usize {
self.recursion_limit().0
fn recursion_limit(self) -> Limit {
self.recursion_limit()
}

type Features = &'tcx rustc_feature::Features;
Expand Down
61 changes: 0 additions & 61 deletions compiler/rustc_middle/src/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use rustc_abi::{ExternAbi, Float, Integer, IntegerType, Size};
use rustc_apfloat::Float as _;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res};
Expand Down Expand Up @@ -895,30 +894,6 @@ impl<'tcx> TyCtxt<'tcx> {
|| self.extern_crate(key).is_some_and(|e| e.is_direct())
}

/// Expand any [weak alias types][weak] contained within the given `value`.
///
/// This should be used over other normalization routines in situations where
/// it's important not to normalize other alias types and where the predicates
/// on the corresponding type alias shouldn't be taken into consideration.
///
/// Whenever possible **prefer not to use this function**! Instead, use standard
/// normalization routines or if feasible don't normalize at all.
///
/// This function comes in handy if you want to mimic the behavior of eager
/// type alias expansion in a localized manner.
///
/// <div class="warning">
/// This delays a bug on overflow! Therefore you need to be certain that the
/// contained types get fully normalized at a later stage. Note that even on
/// overflow all well-behaved weak alias types get expanded correctly, so the
/// result is still useful.
/// </div>
///
/// [weak]: ty::Weak
pub fn expand_weak_alias_tys<T: TypeFoldable<TyCtxt<'tcx>>>(self, value: T) -> T {
value.fold_with(&mut WeakAliasTypeExpander { tcx: self, depth: 0 })
}

/// Peel off all [weak alias types] in this type until there are none left.
///
/// This only expands weak alias types in “head” / outermost positions. It can
Expand Down Expand Up @@ -1088,42 +1063,6 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for OpaqueTypeExpander<'tcx> {
}
}

struct WeakAliasTypeExpander<'tcx> {
tcx: TyCtxt<'tcx>,
depth: usize,
}

impl<'tcx> TypeFolder<TyCtxt<'tcx>> for WeakAliasTypeExpander<'tcx> {
fn cx(&self) -> TyCtxt<'tcx> {
self.tcx
}

fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
if !ty.has_type_flags(ty::TypeFlags::HAS_TY_WEAK) {
return ty;
}
let ty::Alias(ty::Weak, alias) = ty.kind() else {
return ty.super_fold_with(self);
};
if !self.tcx.recursion_limit().value_within_limit(self.depth) {
let guar = self.tcx.dcx().delayed_bug("overflow expanding weak alias type");
return Ty::new_error(self.tcx, guar);
}

self.depth += 1;
ensure_sufficient_stack(|| {
self.tcx.type_of(alias.def_id).instantiate(self.tcx, alias.args).fold_with(self)
})
}

fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
if !ct.has_type_flags(ty::TypeFlags::HAS_TY_WEAK) {
return ct;
}
ct.super_fold_with(self)
}
}

/// Indicates the form of `AsyncDestruct::Destructor`. Used to simplify async
/// drop glue for types not using async drop.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
Expand Down
Loading
Loading