Auto merge of #141506 - matthiaskrgr:rollup-80f1geg, r=matthiaskrgr

Rollup of 6 pull requests

Successful merges:

 - #137323 (Guarantee behavior of transmuting `Option::<T>::None` subject to NPO)
 - #139254 (std: sys: net: uefi: Implement TCP4 connect)
 - #141432 (refactor `CanonicalVarKind`)
 - #141480 (document some -Z flags as living in the rustc-dev-guide)
 - #141486 (rustdoc book: add argument explanation for `html_playground_url`)
 - #141496 (Enable `[issue-links]` and `[no-mentions]` in triagebot)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-05-24 14:20:44 +00:00
commit 038d599eda
28 changed files with 450 additions and 295 deletions

View file

@ -17,8 +17,7 @@ use tracing::debug;
use crate::infer::InferCtxt;
use crate::infer::canonical::{
Canonical, CanonicalQueryInput, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind,
OriginalQueryValues,
Canonical, CanonicalQueryInput, CanonicalTyVarKind, CanonicalVarKind, OriginalQueryValues,
};
impl<'tcx> InferCtxt<'tcx> {
@ -174,10 +173,8 @@ impl CanonicalizeMode for CanonicalizeQueryResponse {
match r.kind() {
ty::ReLateParam(_) | ty::ReErased | ty::ReStatic | ty::ReEarlyParam(..) => r,
ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region(
CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderRegion(placeholder) },
r,
),
ty::RePlaceholder(placeholder) => canonicalizer
.canonical_var_for_region(CanonicalVarKind::PlaceholderRegion(placeholder), r),
ty::ReVar(vid) => {
let universe = infcx
@ -186,10 +183,7 @@ impl CanonicalizeMode for CanonicalizeQueryResponse {
.unwrap_region_constraints()
.probe_value(vid)
.unwrap_err();
canonicalizer.canonical_var_for_region(
CanonicalVarInfo { kind: CanonicalVarKind::Region(universe) },
r,
)
canonicalizer.canonical_var_for_region(CanonicalVarKind::Region(universe), r)
}
_ => {
@ -294,7 +288,7 @@ struct Canonicalizer<'cx, 'tcx> {
/// Set to `None` to disable the resolution of inference variables.
infcx: Option<&'cx InferCtxt<'tcx>>,
tcx: TyCtxt<'tcx>,
variables: SmallVec<[CanonicalVarInfo<'tcx>; 8]>,
variables: SmallVec<[CanonicalVarKind<'tcx>; 8]>,
query_state: &'cx mut OriginalQueryValues<'tcx>,
// Note that indices is only used once `var_values` is big enough to be
// heap-allocated.
@ -368,9 +362,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
ui = ty::UniverseIndex::ROOT;
}
self.canonicalize_ty_var(
CanonicalVarInfo {
kind: CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)),
},
CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)),
t,
)
}
@ -382,10 +374,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
if nt != t {
return self.fold_ty(nt);
} else {
self.canonicalize_ty_var(
CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Int) },
t,
)
self.canonicalize_ty_var(CanonicalVarKind::Ty(CanonicalTyVarKind::Int), t)
}
}
ty::Infer(ty::FloatVar(vid)) => {
@ -393,10 +382,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
if nt != t {
return self.fold_ty(nt);
} else {
self.canonicalize_ty_var(
CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Float) },
t,
)
self.canonicalize_ty_var(CanonicalVarKind::Ty(CanonicalTyVarKind::Float), t)
}
}
@ -408,10 +394,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
if !self.canonicalize_mode.preserve_universes() {
placeholder.universe = ty::UniverseIndex::ROOT;
}
self.canonicalize_ty_var(
CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderTy(placeholder) },
t,
)
self.canonicalize_ty_var(CanonicalVarKind::PlaceholderTy(placeholder), t)
}
ty::Bound(debruijn, _) => {
@ -483,10 +466,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
// FIXME: perf problem described in #55921.
ui = ty::UniverseIndex::ROOT;
}
return self.canonicalize_const_var(
CanonicalVarInfo { kind: CanonicalVarKind::Const(ui) },
ct,
);
return self.canonicalize_const_var(CanonicalVarKind::Const(ui), ct);
}
}
}
@ -501,10 +481,8 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
}
}
ty::ConstKind::Placeholder(placeholder) => {
return self.canonicalize_const_var(
CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderConst(placeholder) },
ct,
);
return self
.canonicalize_const_var(CanonicalVarKind::PlaceholderConst(placeholder), ct);
}
_ => {}
}
@ -595,7 +573,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
debug_assert!(!out_value.has_infer() && !out_value.has_placeholders());
let canonical_variables =
tcx.mk_canonical_var_infos(&canonicalizer.universe_canonicalized_variables());
tcx.mk_canonical_var_kinds(&canonicalizer.universe_canonicalized_variables());
let max_universe = canonical_variables
.iter()
@ -610,18 +588,22 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
/// or returns an existing variable if `kind` has already been
/// seen. `kind` is expected to be an unbound variable (or
/// potentially a free region).
fn canonical_var(&mut self, info: CanonicalVarInfo<'tcx>, kind: GenericArg<'tcx>) -> BoundVar {
fn canonical_var(
&mut self,
var_kind: CanonicalVarKind<'tcx>,
value: GenericArg<'tcx>,
) -> BoundVar {
let Canonicalizer { variables, query_state, indices, .. } = self;
let var_values = &mut query_state.var_values;
let universe = info.universe();
let universe = var_kind.universe();
if universe != ty::UniverseIndex::ROOT {
assert!(self.canonicalize_mode.preserve_universes());
// Insert universe into the universe map. To preserve the order of the
// universes in the value being canonicalized, we don't update the
// universe in `info` until we have finished canonicalizing.
// universe in `var_kind` until we have finished canonicalizing.
match query_state.universe_map.binary_search(&universe) {
Err(idx) => query_state.universe_map.insert(idx, universe),
Ok(_) => {}
@ -636,14 +618,14 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
if !var_values.spilled() {
// `var_values` is stack-allocated. `indices` isn't used yet. Do a
// direct linear search of `var_values`.
if let Some(idx) = var_values.iter().position(|&k| k == kind) {
if let Some(idx) = var_values.iter().position(|&v| v == value) {
// `kind` is already present in `var_values`.
BoundVar::new(idx)
} else {
// `kind` isn't present in `var_values`. Append it. Likewise
// for `info` and `variables`.
variables.push(info);
var_values.push(kind);
// for `var_kind` and `variables`.
variables.push(var_kind);
var_values.push(value);
assert_eq!(variables.len(), var_values.len());
// If `var_values` has become big enough to be heap-allocated,
@ -653,7 +635,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
*indices = var_values
.iter()
.enumerate()
.map(|(i, &kind)| (kind, BoundVar::new(i)))
.map(|(i, &value)| (value, BoundVar::new(i)))
.collect();
}
// The cv is the index of the appended element.
@ -661,9 +643,9 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
}
} else {
// `var_values` is large. Do a hashmap search via `indices`.
*indices.entry(kind).or_insert_with(|| {
variables.push(info);
var_values.push(kind);
*indices.entry(value).or_insert_with(|| {
variables.push(var_kind);
var_values.push(value);
assert_eq!(variables.len(), var_values.len());
BoundVar::new(variables.len() - 1)
})
@ -673,7 +655,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
/// Replaces the universe indexes used in `var_values` with their index in
/// `query_state.universe_map`. This minimizes the maximum universe used in
/// the canonicalized value.
fn universe_canonicalized_variables(self) -> SmallVec<[CanonicalVarInfo<'tcx>; 8]> {
fn universe_canonicalized_variables(self) -> SmallVec<[CanonicalVarKind<'tcx>; 8]> {
if self.query_state.universe_map.len() == 1 {
return self.variables;
}
@ -688,37 +670,33 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
self.variables
.iter()
.map(|v| CanonicalVarInfo {
kind: match v.kind {
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => {
return *v;
}
CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => {
CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u]))
}
CanonicalVarKind::Region(u) => {
CanonicalVarKind::Region(reverse_universe_map[&u])
}
CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]),
CanonicalVarKind::PlaceholderTy(placeholder) => {
CanonicalVarKind::PlaceholderTy(ty::Placeholder {
universe: reverse_universe_map[&placeholder.universe],
..placeholder
})
}
CanonicalVarKind::PlaceholderRegion(placeholder) => {
CanonicalVarKind::PlaceholderRegion(ty::Placeholder {
universe: reverse_universe_map[&placeholder.universe],
..placeholder
})
}
CanonicalVarKind::PlaceholderConst(placeholder) => {
CanonicalVarKind::PlaceholderConst(ty::Placeholder {
universe: reverse_universe_map[&placeholder.universe],
..placeholder
})
}
},
.map(|&kind| match kind {
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => {
return kind;
}
CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => {
CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u]))
}
CanonicalVarKind::Region(u) => CanonicalVarKind::Region(reverse_universe_map[&u]),
CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]),
CanonicalVarKind::PlaceholderTy(placeholder) => {
CanonicalVarKind::PlaceholderTy(ty::Placeholder {
universe: reverse_universe_map[&placeholder.universe],
..placeholder
})
}
CanonicalVarKind::PlaceholderRegion(placeholder) => {
CanonicalVarKind::PlaceholderRegion(ty::Placeholder {
universe: reverse_universe_map[&placeholder.universe],
..placeholder
})
}
CanonicalVarKind::PlaceholderConst(placeholder) => {
CanonicalVarKind::PlaceholderConst(ty::Placeholder {
universe: reverse_universe_map[&placeholder.universe],
..placeholder
})
}
})
.collect()
}
@ -740,20 +718,17 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
&mut self,
r: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
self.canonical_var_for_region(
CanonicalVarInfo { kind: CanonicalVarKind::Region(ty::UniverseIndex::ROOT) },
r,
)
self.canonical_var_for_region(CanonicalVarKind::Region(ty::UniverseIndex::ROOT), r)
}
/// Creates a canonical variable (with the given `info`)
/// representing the region `r`; return a region referencing it.
fn canonical_var_for_region(
&mut self,
info: CanonicalVarInfo<'tcx>,
var_kind: CanonicalVarKind<'tcx>,
r: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
let var = self.canonical_var(info, r.into());
let var = self.canonical_var(var_kind, r.into());
let br = ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon };
ty::Region::new_bound(self.cx(), self.binder_index, br)
}
@ -762,9 +737,13 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
/// if `ty_var` is bound to anything; if so, canonicalize
/// *that*. Otherwise, create a new canonical variable for
/// `ty_var`.
fn canonicalize_ty_var(&mut self, info: CanonicalVarInfo<'tcx>, ty_var: Ty<'tcx>) -> Ty<'tcx> {
fn canonicalize_ty_var(
&mut self,
var_kind: CanonicalVarKind<'tcx>,
ty_var: Ty<'tcx>,
) -> Ty<'tcx> {
debug_assert!(!self.infcx.is_some_and(|infcx| ty_var != infcx.shallow_resolve(ty_var)));
let var = self.canonical_var(info, ty_var.into());
let var = self.canonical_var(var_kind, ty_var.into());
Ty::new_bound(self.tcx, self.binder_index, var.into())
}
@ -774,13 +753,13 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
/// `const_var`.
fn canonicalize_const_var(
&mut self,
info: CanonicalVarInfo<'tcx>,
const_var: ty::Const<'tcx>,
var_kind: CanonicalVarKind<'tcx>,
ct_var: ty::Const<'tcx>,
) -> ty::Const<'tcx> {
debug_assert!(
!self.infcx.is_some_and(|infcx| const_var != infcx.shallow_resolve_const(const_var))
!self.infcx.is_some_and(|infcx| ct_var != infcx.shallow_resolve_const(ct_var))
);
let var = self.canonical_var(info, const_var.into());
let var = self.canonical_var(var_kind, ct_var.into());
ty::Const::new_bound(self.tcx, self.binder_index, var)
}
}

View file

@ -81,14 +81,14 @@ impl<'tcx> InferCtxt<'tcx> {
fn instantiate_canonical_vars(
&self,
span: Span,
variables: &List<CanonicalVarInfo<'tcx>>,
variables: &List<CanonicalVarKind<'tcx>>,
universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
) -> CanonicalVarValues<'tcx> {
CanonicalVarValues {
var_values: self.tcx.mk_args_from_iter(
variables
.iter()
.map(|info| self.instantiate_canonical_var(span, info, &universe_map)),
.map(|kind| self.instantiate_canonical_var(span, kind, &universe_map)),
),
}
}
@ -104,10 +104,10 @@ impl<'tcx> InferCtxt<'tcx> {
pub fn instantiate_canonical_var(
&self,
span: Span,
cv_info: CanonicalVarInfo<'tcx>,
kind: CanonicalVarKind<'tcx>,
universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
) -> GenericArg<'tcx> {
match cv_info.kind {
match kind {
CanonicalVarKind::Ty(ty_kind) => {
let ty = match ty_kind {
CanonicalTyVarKind::General(ui) => {

View file

@ -445,17 +445,17 @@ impl<'tcx> InferCtxt<'tcx> {
// a fresh inference variable.
let result_args = CanonicalVarValues {
var_values: self.tcx.mk_args_from_iter(
query_response.variables.iter().enumerate().map(|(index, info)| {
if info.universe() != ty::UniverseIndex::ROOT {
query_response.variables.iter().enumerate().map(|(index, var_kind)| {
if var_kind.universe() != ty::UniverseIndex::ROOT {
// A variable from inside a binder of the query. While ideally these shouldn't
// exist at all, we have to deal with them for now.
self.instantiate_canonical_var(cause.span, info, |u| {
self.instantiate_canonical_var(cause.span, var_kind, |u| {
universe_map[u.as_usize()]
})
} else if info.is_existential() {
} else if var_kind.is_existential() {
match opt_values[BoundVar::new(index)] {
Some(k) => k,
None => self.instantiate_canonical_var(cause.span, info, |u| {
None => self.instantiate_canonical_var(cause.span, var_kind, |u| {
universe_map[u.as_usize()]
}),
}

View file

@ -27,7 +27,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lock;
use rustc_macros::{HashStable, TypeFoldable, TypeVisitable};
pub use rustc_type_ir as ir;
pub use rustc_type_ir::{CanonicalTyVarKind, CanonicalVarKind};
pub use rustc_type_ir::CanonicalTyVarKind;
use smallvec::SmallVec;
use crate::mir::ConstraintCategory;
@ -35,9 +35,9 @@ use crate::ty::{self, GenericArg, List, Ty, TyCtxt, TypeFlags, TypeVisitableExt}
pub type CanonicalQueryInput<'tcx, V> = ir::CanonicalQueryInput<TyCtxt<'tcx>, V>;
pub type Canonical<'tcx, V> = ir::Canonical<TyCtxt<'tcx>, V>;
pub type CanonicalVarInfo<'tcx> = ir::CanonicalVarInfo<TyCtxt<'tcx>>;
pub type CanonicalVarKind<'tcx> = ir::CanonicalVarKind<TyCtxt<'tcx>>;
pub type CanonicalVarValues<'tcx> = ir::CanonicalVarValues<TyCtxt<'tcx>>;
pub type CanonicalVarInfos<'tcx> = &'tcx List<CanonicalVarInfo<'tcx>>;
pub type CanonicalVarKinds<'tcx> = &'tcx List<CanonicalVarKind<'tcx>>;
/// When we canonicalize a value to form a query, we wind up replacing
/// various parts of it with canonical variables. This struct stores

View file

@ -18,7 +18,7 @@ use rustc_span::source_map::Spanned;
use rustc_span::{Span, SpanDecoder, SpanEncoder};
use crate::arena::ArenaAllocatable;
use crate::infer::canonical::{CanonicalVarInfo, CanonicalVarInfos};
use crate::infer::canonical::{CanonicalVarKind, CanonicalVarKinds};
use crate::mir::interpret::{AllocId, ConstAllocation, CtfeProvenance};
use crate::mir::mono::MonoItem;
use crate::mir::{self};
@ -310,11 +310,11 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for ty::Region<'tcx> {
}
}
impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for CanonicalVarInfos<'tcx> {
impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for CanonicalVarKinds<'tcx> {
fn decode(decoder: &mut D) -> Self {
let len = decoder.read_usize();
decoder.interner().mk_canonical_var_infos_from_iter(
(0..len).map::<CanonicalVarInfo<'tcx>, _>(|_| Decodable::decode(decoder)),
(0..len).map::<CanonicalVarKind<'tcx>, _>(|_| Decodable::decode(decoder)),
)
}
}

View file

@ -60,7 +60,7 @@ use tracing::{debug, instrument};
use crate::arena::Arena;
use crate::dep_graph::{DepGraph, DepKindStruct};
use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarInfo, CanonicalVarInfos};
use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarKind, CanonicalVarKinds};
use crate::lint::lint_level;
use crate::metadata::ModChild;
use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature};
@ -107,9 +107,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
self.mk_predefined_opaques_in_body(data)
}
type LocalDefIds = &'tcx ty::List<LocalDefId>;
type CanonicalVars = CanonicalVarInfos<'tcx>;
fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo<Self>]) -> Self::CanonicalVars {
self.mk_canonical_var_infos(infos)
type CanonicalVarKinds = CanonicalVarKinds<'tcx>;
fn mk_canonical_var_kinds(
self,
kinds: &[ty::CanonicalVarKind<Self>],
) -> Self::CanonicalVarKinds {
self.mk_canonical_var_kinds(kinds)
}
type ExternalConstraints = ExternalConstraints<'tcx>;
@ -833,7 +836,7 @@ pub struct CtxtInterners<'tcx> {
const_lists: InternedSet<'tcx, List<ty::Const<'tcx>>>,
args: InternedSet<'tcx, GenericArgs<'tcx>>,
type_lists: InternedSet<'tcx, List<Ty<'tcx>>>,
canonical_var_infos: InternedSet<'tcx, List<CanonicalVarInfo<'tcx>>>,
canonical_var_kinds: InternedSet<'tcx, List<CanonicalVarKind<'tcx>>>,
region: InternedSet<'tcx, RegionKind<'tcx>>,
poly_existential_predicates: InternedSet<'tcx, List<PolyExistentialPredicate<'tcx>>>,
predicate: InternedSet<'tcx, WithCachedTypeInfo<ty::Binder<'tcx, PredicateKind<'tcx>>>>,
@ -872,7 +875,7 @@ impl<'tcx> CtxtInterners<'tcx> {
type_lists: InternedSet::with_capacity(N * 4),
region: InternedSet::with_capacity(N * 4),
poly_existential_predicates: InternedSet::with_capacity(N / 4),
canonical_var_infos: InternedSet::with_capacity(N / 2),
canonical_var_kinds: InternedSet::with_capacity(N / 2),
predicate: InternedSet::with_capacity(N),
clauses: InternedSet::with_capacity(N),
projs: InternedSet::with_capacity(N * 4),
@ -2675,7 +2678,7 @@ slice_interners!(
const_lists: pub mk_const_list(Const<'tcx>),
args: pub mk_args(GenericArg<'tcx>),
type_lists: pub mk_type_list(Ty<'tcx>),
canonical_var_infos: pub mk_canonical_var_infos(CanonicalVarInfo<'tcx>),
canonical_var_kinds: pub mk_canonical_var_kinds(CanonicalVarKind<'tcx>),
poly_existential_predicates: intern_poly_existential_predicates(PolyExistentialPredicate<'tcx>),
projs: pub mk_projs(ProjectionKind),
place_elems: pub mk_place_elems(PlaceElem<'tcx>),
@ -3055,9 +3058,9 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn mk_canonical_var_infos_from_iter<I, T>(self, iter: I) -> T::Output
where
I: Iterator<Item = T>,
T: CollectAndApply<CanonicalVarInfo<'tcx>, &'tcx List<CanonicalVarInfo<'tcx>>>,
T: CollectAndApply<CanonicalVarKind<'tcx>, &'tcx List<CanonicalVarKind<'tcx>>>,
{
T::collect_and_apply(iter, |xs| self.mk_canonical_var_infos(xs))
T::collect_and_apply(iter, |xs| self.mk_canonical_var_kinds(xs))
}
pub fn mk_place_elems_from_iter<I, T>(self, iter: I) -> T::Output

View file

@ -4,8 +4,8 @@ use rustc_type_ir::data_structures::{HashMap, ensure_sufficient_stack};
use rustc_type_ir::inherent::*;
use rustc_type_ir::solve::{Goal, QueryInput};
use rustc_type_ir::{
self as ty, Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, InferCtxtLike,
Interner, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
self as ty, Canonical, CanonicalTyVarKind, CanonicalVarKind, InferCtxtLike, Interner,
TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
};
use crate::delegate::SolverDelegate;
@ -50,7 +50,7 @@ pub struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner> {
// Mutable fields.
variables: &'a mut Vec<I::GenericArg>,
primitive_var_infos: Vec<CanonicalVarInfo<I>>,
var_kinds: Vec<CanonicalVarKind<I>>,
variable_lookup_table: HashMap<I::GenericArg, usize>,
binder_index: ty::DebruijnIndex,
@ -73,7 +73,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
variables,
variable_lookup_table: Default::default(),
primitive_var_infos: Vec::new(),
var_kinds: Vec::new(),
binder_index: ty::INNERMOST,
cache: Default::default(),
@ -106,7 +106,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
variables,
variable_lookup_table: Default::default(),
primitive_var_infos: Vec::new(),
var_kinds: Vec::new(),
binder_index: ty::INNERMOST,
cache: Default::default(),
@ -123,7 +123,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
// We're able to reuse the `variable_lookup_table` as whether or not
// it already contains an entry for `'static` does not matter.
variable_lookup_table: env_canonicalizer.variable_lookup_table,
primitive_var_infos: env_canonicalizer.primitive_var_infos,
var_kinds: env_canonicalizer.var_kinds,
binder_index: ty::INNERMOST,
// We do not reuse the cache as it may contain entries whose canonicalized
@ -149,7 +149,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
fn get_or_insert_bound_var(
&mut self,
arg: impl Into<I::GenericArg>,
canonical_var_info: CanonicalVarInfo<I>,
kind: CanonicalVarKind<I>,
) -> ty::BoundVar {
// FIXME: 16 is made up and arbitrary. We should look at some
// perf data here.
@ -162,14 +162,14 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
*self.variable_lookup_table.entry(arg).or_insert_with(|| {
let var = self.variables.len();
self.variables.push(arg);
self.primitive_var_infos.push(canonical_var_info);
self.var_kinds.push(kind);
var
})
} else {
self.variables.iter().position(|&v| v == arg).unwrap_or_else(|| {
let var = self.variables.len();
self.variables.push(arg);
self.primitive_var_infos.push(canonical_var_info);
self.var_kinds.push(kind);
var
})
};
@ -177,8 +177,8 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
ty::BoundVar::from(idx)
}
fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVars) {
let mut var_infos = self.primitive_var_infos;
fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVarKinds) {
let mut var_kinds = self.var_kinds;
// See the rustc-dev-guide section about how we deal with universes
// during canonicalization in the new solver.
match self.canonicalize_mode {
@ -192,25 +192,25 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
// information for placeholders and inference variables created inside
// of the query.
CanonicalizeMode::Response { max_input_universe } => {
for var in var_infos.iter_mut() {
for var in var_kinds.iter_mut() {
let uv = var.universe();
let new_uv = ty::UniverseIndex::from(
uv.index().saturating_sub(max_input_universe.index()),
);
*var = var.with_updated_universe(new_uv);
}
let max_universe = var_infos
let max_universe = var_kinds
.iter()
.map(|info| info.universe())
.map(|kind| kind.universe())
.max()
.unwrap_or(ty::UniverseIndex::ROOT);
let var_infos = self.delegate.cx().mk_canonical_var_infos(&var_infos);
return (max_universe, var_infos);
let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
return (max_universe, var_kinds);
}
}
// Given a `var_infos` with existentials `En` and universals `Un` in
// Given a `var_kinds` with existentials `En` and universals `Un` in
// universes `n`, this algorithm compresses them in place so that:
//
// - the new universe indices are as small as possible
@ -219,12 +219,12 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
// 2. put a placeholder in the same universe as an existential which cannot name it
//
// Let's walk through an example:
// - var_infos: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 0
// - var_infos: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 1
// - var_infos: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 1, next_orig_uv: 2
// - var_infos: [E0, U1, E5, U1, E1, E6, U6], curr_compressed_uv: 1, next_orig_uv: 5
// - var_infos: [E0, U1, E2, U1, E1, E6, U6], curr_compressed_uv: 2, next_orig_uv: 6
// - var_infos: [E0, U1, E1, U1, E1, E3, U3], curr_compressed_uv: 2, next_orig_uv: -
// - var_kinds: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 0
// - var_kinds: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 1
// - var_kinds: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 1, next_orig_uv: 2
// - var_kinds: [E0, U1, E5, U1, E1, E6, U6], curr_compressed_uv: 1, next_orig_uv: 5
// - var_kinds: [E0, U1, E2, U1, E1, E6, U6], curr_compressed_uv: 2, next_orig_uv: 6
// - var_kinds: [E0, U1, E1, U1, E1, E3, U3], curr_compressed_uv: 2, next_orig_uv: -
//
// This algorithm runs in `O(mn)` where `n` is the number of different universes and
// `m` the number of variables. This should be fine as both are expected to be small.
@ -232,7 +232,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
let mut existential_in_new_uv = None;
let mut next_orig_uv = Some(ty::UniverseIndex::ROOT);
while let Some(orig_uv) = next_orig_uv.take() {
let mut update_uv = |var: &mut CanonicalVarInfo<I>, orig_uv, is_existential| {
let mut update_uv = |var: &mut CanonicalVarKind<I>, orig_uv, is_existential| {
let uv = var.universe();
match uv.cmp(&orig_uv) {
Ordering::Less => (), // Already updated
@ -284,7 +284,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
// Whenever we compress the universe of a placeholder, no existential with
// an already compressed universe can name that placeholder.
for is_existential in [false, true] {
for var in var_infos.iter_mut() {
for var in var_kinds.iter_mut() {
// We simply put all regions from the input into the highest
// compressed universe, so we only deal with them at the end.
if !var.is_region() {
@ -298,7 +298,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
// We put all regions into a separate universe.
let mut first_region = true;
for var in var_infos.iter_mut() {
for var in var_kinds.iter_mut() {
if var.is_region() {
if first_region {
first_region = false;
@ -309,8 +309,8 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
}
}
let var_infos = self.delegate.cx().mk_canonical_var_infos(&var_infos);
(curr_compressed_uv, var_infos)
let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
(curr_compressed_uv, var_kinds)
}
fn cached_fold_ty(&mut self, t: I::Ty) -> I::Ty {
@ -391,7 +391,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
}
};
let var = self.get_or_insert_bound_var(t, CanonicalVarInfo { kind });
let var = self.get_or_insert_bound_var(t, kind);
Ty::new_anon_bound(self.cx(), self.binder_index, var)
}
@ -475,7 +475,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
}
};
let var = self.get_or_insert_bound_var(r, CanonicalVarInfo { kind });
let var = self.get_or_insert_bound_var(r, kind);
Region::new_anon_bound(self.cx(), self.binder_index, var)
}
@ -525,7 +525,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
| ty::ConstKind::Expr(_) => return c.super_fold_with(self),
};
let var = self.get_or_insert_bound_var(c, CanonicalVarInfo { kind });
let var = self.get_or_insert_bound_var(c, kind);
Const::new_anon_bound(self.cx(), self.binder_index, var)
}

View file

@ -61,7 +61,7 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
fn instantiate_canonical_var_with_infer(
&self,
cv_info: ty::CanonicalVarInfo<Self::Interner>,
kind: ty::CanonicalVarKind<Self::Interner>,
span: <Self::Interner as Interner>::Span,
universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
) -> <Self::Interner as Interner>::GenericArg;

View file

@ -360,15 +360,15 @@ where
}
let var_values = delegate.cx().mk_args_from_iter(
response.variables.iter().enumerate().map(|(index, info)| {
if info.universe() != ty::UniverseIndex::ROOT {
response.variables.iter().enumerate().map(|(index, var_kind)| {
if var_kind.universe() != ty::UniverseIndex::ROOT {
// A variable from inside a binder of the query. While ideally these shouldn't
// exist at all (see the FIXME at the start of this method), we have to deal with
// them for now.
delegate.instantiate_canonical_var_with_infer(info, span, |idx| {
delegate.instantiate_canonical_var_with_infer(var_kind, span, |idx| {
prev_universe + idx.index()
})
} else if info.is_existential() {
} else if var_kind.is_existential() {
// As an optimization we sometimes avoid creating a new inference variable here.
//
// All new inference variables we create start out in the current universe of the caller.
@ -379,12 +379,13 @@ where
if let Some(v) = opt_values[ty::BoundVar::from_usize(index)] {
v
} else {
delegate.instantiate_canonical_var_with_infer(info, span, |_| prev_universe)
delegate
.instantiate_canonical_var_with_infer(var_kind, span, |_| prev_universe)
}
} else {
// For placeholders which were already part of the input, we simply map this
// universal bound variable back the placeholder of the input.
original_values[info.expect_placeholder_index()]
original_values[var_kind.expect_placeholder_index()]
}
}),
);

View file

@ -91,7 +91,7 @@ where
/// The variable info for the `var_values`, only used to make an ambiguous response
/// with no constraints.
variables: I::CanonicalVars,
variables: I::CanonicalVarKinds,
/// What kind of goal we're currently computing, see the enum definition
/// for more info.

View file

@ -354,7 +354,7 @@ where
fn response_no_constraints_raw<I: Interner>(
cx: I,
max_universe: ty::UniverseIndex,
variables: I::CanonicalVars,
variables: I::CanonicalVarKinds,
certainty: Certainty,
) -> CanonicalResponse<I> {
ty::Canonical {

View file

@ -5,7 +5,7 @@ use rustc_hir::LangItem;
use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
use rustc_infer::infer::canonical::{
Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarInfo, CanonicalVarValues,
Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarKind, CanonicalVarValues,
};
use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt};
use rustc_infer::traits::solve::Goal;
@ -190,11 +190,11 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
fn instantiate_canonical_var_with_infer(
&self,
cv_info: CanonicalVarInfo<'tcx>,
kind: CanonicalVarKind<'tcx>,
span: Span,
universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
) -> ty::GenericArg<'tcx> {
self.0.instantiate_canonical_var(span, cv_info, universe_map)
self.0.instantiate_canonical_var(span, kind, universe_map)
}
fn add_item_bounds_for_hidden_type(

View file

@ -41,7 +41,7 @@ pub struct CanonicalQueryInput<I: Interner, V> {
pub struct Canonical<I: Interner, V> {
pub value: V,
pub max_universe: UniverseIndex,
pub variables: I::CanonicalVars,
pub variables: I::CanonicalVarKinds,
}
impl<I: Interner, V> Canonical<I, V> {
@ -89,63 +89,6 @@ impl<I: Interner, V: fmt::Display> fmt::Display for Canonical<I, V> {
/// a copy of the canonical value in some other inference context,
/// with fresh inference variables replacing the canonical values.
#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)]
#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
#[cfg_attr(
feature = "nightly",
derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext)
)]
pub struct CanonicalVarInfo<I: Interner> {
pub kind: CanonicalVarKind<I>,
}
impl<I: Interner> CanonicalVarInfo<I> {
pub fn universe(self) -> UniverseIndex {
self.kind.universe()
}
#[must_use]
pub fn with_updated_universe(self, ui: UniverseIndex) -> CanonicalVarInfo<I> {
CanonicalVarInfo { kind: self.kind.with_updated_universe(ui) }
}
pub fn is_existential(&self) -> bool {
match self.kind {
CanonicalVarKind::Ty(_) => true,
CanonicalVarKind::PlaceholderTy(_) => false,
CanonicalVarKind::Region(_) => true,
CanonicalVarKind::PlaceholderRegion(..) => false,
CanonicalVarKind::Const(_) => true,
CanonicalVarKind::PlaceholderConst(_) => false,
}
}
pub fn is_region(&self) -> bool {
match self.kind {
CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => true,
CanonicalVarKind::Ty(_)
| CanonicalVarKind::PlaceholderTy(_)
| CanonicalVarKind::Const(_)
| CanonicalVarKind::PlaceholderConst(_) => false,
}
}
pub fn expect_placeholder_index(self) -> usize {
match self.kind {
CanonicalVarKind::Ty(_) | CanonicalVarKind::Region(_) | CanonicalVarKind::Const(_) => {
panic!("expected placeholder: {self:?}")
}
CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.var().as_usize(),
CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.var().as_usize(),
CanonicalVarKind::PlaceholderConst(placeholder) => placeholder.var().as_usize(),
}
}
}
/// Describes the "kind" of the canonical variable. This is a "kind"
/// in the type-theory sense of the term -- i.e., a "meta" type system
/// that analyzes type-like values.
#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)]
#[cfg_attr(
feature = "nightly",
derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext)
@ -214,6 +157,39 @@ impl<I: Interner> CanonicalVarKind<I> {
}
}
}
pub fn is_existential(self) -> bool {
match self {
CanonicalVarKind::Ty(_) => true,
CanonicalVarKind::PlaceholderTy(_) => false,
CanonicalVarKind::Region(_) => true,
CanonicalVarKind::PlaceholderRegion(..) => false,
CanonicalVarKind::Const(_) => true,
CanonicalVarKind::PlaceholderConst(_) => false,
}
}
pub fn is_region(self) -> bool {
match self {
CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => true,
CanonicalVarKind::Ty(_)
| CanonicalVarKind::PlaceholderTy(_)
| CanonicalVarKind::Const(_)
| CanonicalVarKind::PlaceholderConst(_) => false,
}
}
pub fn expect_placeholder_index(self) -> usize {
match self {
CanonicalVarKind::Ty(_) | CanonicalVarKind::Region(_) | CanonicalVarKind::Const(_) => {
panic!("expected placeholder: {self:?}")
}
CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.var().as_usize(),
CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.var().as_usize(),
CanonicalVarKind::PlaceholderConst(placeholder) => placeholder.var().as_usize(),
}
}
}
/// Rust actually has more than one category of type variables;
@ -306,11 +282,11 @@ impl<I: Interner> CanonicalVarValues<I> {
// Given a list of canonical variables, construct a set of values which are
// the identity response.
pub fn make_identity(cx: I, infos: I::CanonicalVars) -> CanonicalVarValues<I> {
pub fn make_identity(cx: I, infos: I::CanonicalVarKinds) -> CanonicalVarValues<I> {
CanonicalVarValues {
var_values: cx.mk_args_from_iter(infos.iter().enumerate().map(
|(i, info)| -> I::GenericArg {
match info.kind {
|(i, kind)| -> I::GenericArg {
match kind {
CanonicalVarKind::Ty(_) | CanonicalVarKind::PlaceholderTy(_) => {
Ty::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i))
.into()

View file

@ -64,13 +64,16 @@ pub trait Interner:
+ TypeVisitable<Self>
+ SliceLike<Item = Self::LocalDefId>;
type CanonicalVars: Copy
type CanonicalVarKinds: Copy
+ Debug
+ Hash
+ Eq
+ SliceLike<Item = ty::CanonicalVarInfo<Self>>
+ SliceLike<Item = ty::CanonicalVarKind<Self>>
+ Default;
fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo<Self>]) -> Self::CanonicalVars;
fn mk_canonical_var_kinds(
self,
kinds: &[ty::CanonicalVarKind<Self>],
) -> Self::CanonicalVarKinds;
type ExternalConstraints: Copy
+ Debug

View file

@ -120,20 +120,22 @@
//!
//! Rust guarantees to optimize the following types `T` such that
//! [`Option<T>`] has the same size, alignment, and [function call ABI] as `T`. In some
//! of these cases, Rust further guarantees that
//! `transmute::<_, Option<T>>([0u8; size_of::<T>()])` is sound and
//! produces `Option::<T>::None`. These cases are identified by the
//! second column:
//! of these cases, Rust further guarantees the following:
//! - `transmute::<_, Option<T>>([0u8; size_of::<T>()])` is sound and produces
//! `Option::<T>::None`
//! - `transmute::<_, [u8; size_of::<T>()]>(Option::<T>::None)` is sound and produces
//! `[0u8; size_of::<T>()]`
//! These cases are identified by the second column:
//!
//! | `T` | `transmute::<_, Option<T>>([0u8; size_of::<T>()])` sound? |
//! |---------------------------------------------------------------------|----------------------------------------------------------------------|
//! | [`Box<U>`] (specifically, only `Box<U, Global>`) | when `U: Sized` |
//! | `&U` | when `U: Sized` |
//! | `&mut U` | when `U: Sized` |
//! | `fn`, `extern "C" fn`[^extern_fn] | always |
//! | [`num::NonZero*`] | always |
//! | [`ptr::NonNull<U>`] | when `U: Sized` |
//! | `#[repr(transparent)]` struct around one of the types in this list. | when it holds for the inner type |
//! | `T` | Transmuting between `[0u8; size_of::<T>()]` and `Option::<T>::None` sound? |
//! |---------------------------------------------------------------------|----------------------------------------------------------------------------|
//! | [`Box<U>`] (specifically, only `Box<U, Global>`) | when `U: Sized` |
//! | `&U` | when `U: Sized` |
//! | `&mut U` | when `U: Sized` |
//! | `fn`, `extern "C" fn`[^extern_fn] | always |
//! | [`num::NonZero*`] | always |
//! | [`ptr::NonNull<U>`] | when `U: Sized` |
//! | `#[repr(transparent)]` struct around one of the types in this list. | when it holds for the inner type |
//!
//! [^extern_fn]: this remains true for any argument/return types and any other ABI: `extern "abi" fn` (_e.g._, `extern "system" fn`)
//!

View file

@ -4,11 +4,14 @@ use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
use crate::sys::unsupported;
use crate::time::Duration;
pub struct TcpStream(!);
mod tcp;
pub(crate) mod tcp4;
pub struct TcpStream(#[expect(dead_code)] tcp::Tcp);
impl TcpStream {
pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
unsupported()
pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
tcp::Tcp::connect(addr?).map(Self)
}
pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<TcpStream> {
@ -16,105 +19,105 @@ impl TcpStream {
}
pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
self.0
unsupported()
}
pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
self.0
unsupported()
}
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
self.0
unsupported()
}
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
self.0
unsupported()
}
pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
self.0
unsupported()
}
pub fn read(&self, _: &mut [u8]) -> io::Result<usize> {
self.0
unsupported()
}
pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> {
self.0
unsupported()
}
pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.0
unsupported()
}
pub fn is_read_vectored(&self) -> bool {
self.0
false
}
pub fn write(&self, _: &[u8]) -> io::Result<usize> {
self.0
unsupported()
}
pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> {
self.0
unsupported()
}
pub fn is_write_vectored(&self) -> bool {
self.0
false
}
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
self.0
unsupported()
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
self.0
unsupported()
}
pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
self.0
unsupported()
}
pub fn duplicate(&self) -> io::Result<TcpStream> {
self.0
unsupported()
}
pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
self.0
unsupported()
}
pub fn linger(&self) -> io::Result<Option<Duration>> {
self.0
unsupported()
}
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
self.0
unsupported()
}
pub fn nodelay(&self) -> io::Result<bool> {
self.0
unsupported()
}
pub fn set_ttl(&self, _: u32) -> io::Result<()> {
self.0
unsupported()
}
pub fn ttl(&self) -> io::Result<u32> {
self.0
unsupported()
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
self.0
unsupported()
}
pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
self.0
unsupported()
}
}
impl fmt::Debug for TcpStream {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0
todo!()
}
}

View file

@ -0,0 +1,21 @@
use super::tcp4;
use crate::io;
use crate::net::SocketAddr;
pub(crate) enum Tcp {
V4(#[expect(dead_code)] tcp4::Tcp4),
}
impl Tcp {
pub(crate) fn connect(addr: &SocketAddr) -> io::Result<Self> {
match addr {
SocketAddr::V4(x) => {
let temp = tcp4::Tcp4::new()?;
temp.configure(true, Some(x), None)?;
temp.connect()?;
Ok(Tcp::V4(temp))
}
SocketAddr::V6(_) => todo!(),
}
}
}

View file

@ -0,0 +1,118 @@
use r_efi::efi::{self, Status};
use r_efi::protocols::tcp4;
use crate::io;
use crate::net::SocketAddrV4;
use crate::ptr::NonNull;
use crate::sync::atomic::{AtomicBool, Ordering};
use crate::sys::pal::helpers;
const TYPE_OF_SERVICE: u8 = 8;
const TIME_TO_LIVE: u8 = 255;
pub(crate) struct Tcp4 {
protocol: NonNull<tcp4::Protocol>,
flag: AtomicBool,
#[expect(dead_code)]
service_binding: helpers::ServiceProtocol,
}
const DEFAULT_ADDR: efi::Ipv4Address = efi::Ipv4Address { addr: [0u8; 4] };
impl Tcp4 {
pub(crate) fn new() -> io::Result<Self> {
let service_binding = helpers::ServiceProtocol::open(tcp4::SERVICE_BINDING_PROTOCOL_GUID)?;
let protocol = helpers::open_protocol(service_binding.child_handle(), tcp4::PROTOCOL_GUID)?;
Ok(Self { service_binding, protocol, flag: AtomicBool::new(false) })
}
pub(crate) fn configure(
&self,
active: bool,
remote_address: Option<&SocketAddrV4>,
station_address: Option<&SocketAddrV4>,
) -> io::Result<()> {
let protocol = self.protocol.as_ptr();
let (remote_address, remote_port) = if let Some(x) = remote_address {
(helpers::ipv4_to_r_efi(*x.ip()), x.port())
} else {
(DEFAULT_ADDR, 0)
};
// FIXME: Remove when passive connections with proper subnet handling are added
assert!(station_address.is_none());
let use_default_address = efi::Boolean::TRUE;
let (station_address, station_port) = (DEFAULT_ADDR, 0);
let subnet_mask = helpers::ipv4_to_r_efi(crate::net::Ipv4Addr::new(0, 0, 0, 0));
let mut config_data = tcp4::ConfigData {
type_of_service: TYPE_OF_SERVICE,
time_to_live: TIME_TO_LIVE,
access_point: tcp4::AccessPoint {
use_default_address,
remote_address,
remote_port,
active_flag: active.into(),
station_address,
station_port,
subnet_mask,
},
control_option: crate::ptr::null_mut(),
};
let r = unsafe { ((*protocol).configure)(protocol, &mut config_data) };
if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) }
}
pub(crate) fn connect(&self) -> io::Result<()> {
let evt = unsafe { self.create_evt() }?;
let completion_token =
tcp4::CompletionToken { event: evt.as_ptr(), status: Status::SUCCESS };
let protocol = self.protocol.as_ptr();
let mut conn_token = tcp4::ConnectionToken { completion_token };
let r = unsafe { ((*protocol).connect)(protocol, &mut conn_token) };
if r.is_error() {
return Err(io::Error::from_raw_os_error(r.as_usize()));
}
self.wait_for_flag();
if completion_token.status.is_error() {
Err(io::Error::from_raw_os_error(completion_token.status.as_usize()))
} else {
Ok(())
}
}
unsafe fn create_evt(&self) -> io::Result<helpers::OwnedEvent> {
self.flag.store(false, Ordering::Relaxed);
helpers::OwnedEvent::new(
efi::EVT_NOTIFY_SIGNAL,
efi::TPL_CALLBACK,
Some(toggle_atomic_flag),
Some(unsafe { NonNull::new_unchecked(self.flag.as_ptr().cast()) }),
)
}
fn wait_for_flag(&self) {
while !self.flag.load(Ordering::Relaxed) {
let _ = self.poll();
}
}
fn poll(&self) -> io::Result<()> {
let protocol = self.protocol.as_ptr();
let r = unsafe { ((*protocol).poll)(protocol) };
if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) }
}
}
extern "efiapi" fn toggle_atomic_flag(_: r_efi::efi::Event, ctx: *mut crate::ffi::c_void) {
let flag = unsafe { AtomicBool::from_ptr(ctx.cast()) };
flag.store(true, Ordering::Relaxed);
}

View file

@ -653,7 +653,6 @@ pub(crate) struct ServiceProtocol {
}
impl ServiceProtocol {
#[expect(dead_code)]
pub(crate) fn open(service_guid: r_efi::efi::Guid) -> io::Result<Self> {
let handles = locate_handles(service_guid)?;
@ -670,7 +669,6 @@ impl ServiceProtocol {
Err(io::const_error!(io::ErrorKind::NotFound, "no service binding protocol found"))
}
#[expect(dead_code)]
pub(crate) fn child_handle(&self) -> NonNull<crate::ffi::c_void> {
self.child_handle
}
@ -732,6 +730,10 @@ impl OwnedEvent {
}
}
pub(crate) fn as_ptr(&self) -> efi::Event {
self.0.as_ptr()
}
pub(crate) fn into_raw(self) -> *mut crate::ffi::c_void {
let r = self.0.as_ptr();
crate::mem::forget(self);
@ -755,3 +757,7 @@ impl Drop for OwnedEvent {
}
}
}
pub(crate) const fn ipv4_to_r_efi(addr: crate::net::Ipv4Addr) -> efi::Ipv4Address {
efi::Ipv4Address { addr: addr.octets() }
}

View file

@ -88,8 +88,10 @@ on your documentation examples make requests to.
```
Now, when you press "run", the button will make a request to this domain. The request
URL will contain 2 query parameters: `code` and `edition` for the code in the documentation
and the Rust edition respectively.
URL will contain 3 query parameters:
1. `code` for the code in the documentation
2. `version` for the Rust channel, e.g. nightly, which is decided by whether `code` contain unstable features
3. `edition` for the Rust edition, e.g. 2024
If you don't use this attribute, there will be no run buttons.

View file

@ -0,0 +1,12 @@
# `eagerly-emit-delayed-bugs`
This feature is perma-unstable and has no tracking issue.
------------------------
This flag converts all [`span_delayed_bug()`] calls to [`bug!`] calls, exiting the compiler immediately and allowing you to generate a backtrace of where the delayed bug occurred.
For full documentation, see [the rustc-dev-guide][dev-guide-delayed].
[`bug!`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/macro.bug.html
[`span_delayed_bug()`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.DiagCtxtHandle.html#method.span_delayed_bug
[dev-guide-delayed]: https://rustc-dev-guide.rust-lang.org/compiler-debugging.html#debugging-delayed-bugs

View file

@ -0,0 +1,11 @@
# `track-diagnostics`
This feature is perma-unstable and has no tracking issue.
------------------------
This flag prints the source code span in the compiler where a diagnostic was generated, respecting [`#[track_caller]`][track_caller]. Note that this may be different from the place it was emitted.
For full documentation, see [the rustc-dev-guide][dev-guide-track-diagnostics].
[track_caller]: https://doc.rust-lang.org/reference/attributes/codegen.html#the-track_caller-attribute
[dev-guide-track-diagnostics]: https://rustc-dev-guide.rust-lang.org/compiler-debugging.html#getting-the-error-creation-location

View file

@ -0,0 +1,13 @@
# `treat-err-as-bug`
This feature is perma-unstable and has no tracking issue.
------------------------
This flag converts the selected error to a [`bug!`] call, exiting the compiler immediately and allowing you to generate a backtrace of where the error occurred.
For full documentation, see [the rustc-dev-guide][dev-guide-backtrace].
Note that the compiler automatically sets `RUST_BACKTRACE=1` for itself, and so you do not need to set it yourself when using this flag.
[`bug!`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/macro.bug.html
[dev-guide-backtrace]: https://rustc-dev-guide.rust-lang.org/compiler-debugging.html#getting-a-backtrace-for-errors

View file

@ -1,34 +1,34 @@
// MIR for `address_of_reborrow` after SimplifyCfg-initial
| User Type Annotations
| 0: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:8:10: 8:18, inferred_ty: *const [i32; 10]
| 1: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:10:10: 10:25, inferred_ty: *const dyn std::marker::Send
| 2: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10]
| 3: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10]
| 0: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:8:10: 8:18, inferred_ty: *const [i32; 10]
| 1: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:10:10: 10:25, inferred_ty: *const dyn std::marker::Send
| 2: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10]
| 3: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10]
| 4: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10]
| 5: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10]
| 6: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send
| 7: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send
| 6: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send
| 7: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send
| 8: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32]
| 9: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32]
| 10: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:19:10: 19:18, inferred_ty: *const [i32; 10]
| 11: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:21:10: 21:25, inferred_ty: *const dyn std::marker::Send
| 12: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10]
| 13: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10]
| 10: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:19:10: 19:18, inferred_ty: *const [i32; 10]
| 11: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:21:10: 21:25, inferred_ty: *const dyn std::marker::Send
| 12: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10]
| 13: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10]
| 14: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10]
| 15: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10]
| 16: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send
| 17: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send
| 16: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send
| 17: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send
| 18: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32]
| 19: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32]
| 20: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:29:10: 29:16, inferred_ty: *mut [i32; 10]
| 21: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:31:10: 31:23, inferred_ty: *mut dyn std::marker::Send
| 22: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10]
| 23: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10]
| 20: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:29:10: 29:16, inferred_ty: *mut [i32; 10]
| 21: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:31:10: 31:23, inferred_ty: *mut dyn std::marker::Send
| 22: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10]
| 23: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10]
| 24: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10]
| 25: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10]
| 26: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send
| 27: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send
| 26: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send
| 27: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send
| 28: user_ty: Canonical { value: Ty(*mut [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:37:12: 37:22, inferred_ty: *mut [i32]
| 29: user_ty: Canonical { value: Ty(*mut [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:37:12: 37:22, inferred_ty: *mut [i32]
|

View file

@ -3,8 +3,8 @@
| User Type Annotations
| 0: user_ty: Canonical { value: Ty(*mut Test), max_universe: U0, variables: [] }, span: $DIR/receiver_ptr_mutability.rs:15:14: 15:23, inferred_ty: *mut Test
| 1: user_ty: Canonical { value: Ty(*mut Test), max_universe: U0, variables: [] }, span: $DIR/receiver_ptr_mutability.rs:15:14: 15:23, inferred_ty: *mut Test
| 2: user_ty: Canonical { value: Ty(&&&&*mut Test), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/receiver_ptr_mutability.rs:19:18: 19:31, inferred_ty: &&&&*mut Test
| 3: user_ty: Canonical { value: Ty(&&&&*mut Test), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/receiver_ptr_mutability.rs:19:18: 19:31, inferred_ty: &&&&*mut Test
| 2: user_ty: Canonical { value: Ty(&&&&*mut Test), max_universe: U0, variables: [Region(U0), Region(U0), Region(U0), Region(U0)] }, span: $DIR/receiver_ptr_mutability.rs:19:18: 19:31, inferred_ty: &&&&*mut Test
| 3: user_ty: Canonical { value: Ty(&&&&*mut Test), max_universe: U0, variables: [Region(U0), Region(U0), Region(U0), Region(U0)] }, span: $DIR/receiver_ptr_mutability.rs:19:18: 19:31, inferred_ty: &&&&*mut Test
|
fn main() -> () {
let mut _0: ();

View file

@ -18,7 +18,7 @@ pub trait Interner: Sized {
type Binder<T: TypeVisitable<Self>>: BoundVars<Self> + TypeSuperVisitable<Self>;
type BoundVars: IntoIterator<Item = Self::BoundVar>;
type BoundVar;
type CanonicalVars: Copy + Debug + Hash + Eq + IntoIterator<Item = CanonicalVarInfo<Self>>;
type CanonicalVarKinds: Copy + Debug + Hash + Eq + IntoIterator<Item = CanonicalVarKind<Self>>;
type Ty: Copy
+ DebugWithInfcx<Self>
+ Hash
@ -77,7 +77,7 @@ pub trait Interner: Sized {
type ClosureKind: Copy + Debug + Hash + Eq;
// Required method
fn mk_canonical_var_infos(self, infos: &[CanonicalVarInfo<Self>]) -> Self::CanonicalVars;
fn mk_canonical_var_kinds(self, kinds: &[CanonicalVarKind<Self>]) -> Self::CanonicalVarKinds;
}
pub trait DebugWithInfcx<I: Interner>: Debug {
@ -104,10 +104,6 @@ pub trait TypeSuperVisitable<I: Interner>: TypeVisitable<I> {
fn super_visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result;
}
pub struct CanonicalVarInfo<I: Interner> {
pub kind: CanonicalVarKind<I>,
}
pub struct CanonicalVarKind<I>(std::marker::PhantomData<I>);
pub struct TyKind<I>(std::marker::PhantomData<I>);

View file

@ -3,13 +3,13 @@
const EXPECTED = [
{
'query': 'canonicalvarinfo, intoiterator -> intoiterator',
'query': 'CanonicalVarKind, intoiterator -> intoiterator',
'others': [],
},
{
'query': '[canonicalvarinfo], interner<tys=intoiterator> -> intoiterator',
'query': '[CanonicalVarKind], interner<tys=intoiterator> -> intoiterator',
'others': [
{ 'path': 'looks_like_rustc_interner::Interner', 'name': 'mk_canonical_var_infos' },
{ 'path': 'looks_like_rustc_interner::Interner', 'name': 'mk_canonical_var_kinds' },
],
},
];

View file

@ -1423,3 +1423,12 @@ compiletest = [
[behind-upstream]
days-threshold = 14
# Canonicalize issue numbers to avoid closing the wrong issue
# when commits are included in subtrees, as well as warning links in commits.
# Documentation at: https://forge.rust-lang.org/triagebot/issue-links.html
[issue-links]
# Prevents mentions in commits to avoid users being spammed
# Documentation at: https://forge.rust-lang.org/triagebot/no-mentions.html
[no-mentions]