Ban multi-trait objects via trait aliases.

This commit is contained in:
Alexander Regueiro 2019-03-26 17:34:32 +00:00
parent 16ef295770
commit 9f800457dd
13 changed files with 211 additions and 129 deletions

View File

@ -123,12 +123,12 @@ pub use core::slice::{RChunks, RChunksMut, RChunksExact, RChunksExactMut};
////////////////////////////////////////////////////////////////////////////////
// HACK(japaric) needed for the implementation of `vec!` macro during testing
// NB see the hack module in this file for more details
// N.B., see the `hack` module in this file for more details.
#[cfg(test)]
pub use hack::into_vec;
// HACK(japaric) needed for the implementation of `Vec::clone` during testing
// NB see the hack module in this file for more details
// N.B., see the `hack` module in this file for more details.
#[cfg(test)]
pub use hack::to_vec;
@ -376,7 +376,7 @@ impl<T> [T] {
pub fn to_vec(&self) -> Vec<T>
where T: Clone
{
// NB see hack module in this file
// N.B., see the `hack` module in this file for more details.
hack::to_vec(self)
}
@ -397,7 +397,7 @@ impl<T> [T] {
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn into_vec(self: Box<Self>) -> Vec<T> {
// NB see hack module in this file
// N.B., see the `hack` module in this file for more details.
hack::into_vec(self)
}

View File

@ -957,7 +957,7 @@ impl<'hir> Map<'hir> {
}
}
/// Returns the name associated with the given NodeId's AST.
/// Returns the name associated with the given `NodeId`'s AST.
pub fn name(&self, id: NodeId) -> Name {
let hir_id = self.node_to_hir_id(id);
self.name_by_hir_id(hir_id)

View File

@ -2143,11 +2143,11 @@ pub enum UseKind {
ListStem,
}
/// TraitRef's appear in impls.
/// `TraitRef` are references to traits in impls.
///
/// resolve maps each TraitRef's ref_id to its defining trait; that's all
/// that the ref_id is for. Note that ref_id's value is not the NodeId of the
/// trait being referred to but just a unique NodeId that serves as a key
/// `resolve` maps each `TraitRef`'s `ref_id` to its defining trait; that's all
/// that the `ref_id` is for. Note that `ref_id`'s value is not the `NodeId` of the
/// trait being referred to but just a unique `NodeId` that serves as a key
/// within the resolution map.
#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable)]
pub struct TraitRef {

View File

@ -141,9 +141,9 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> {
}
fn recursive_type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
let mut bounds = ty.walk_shallow()
let mut bounds: Vec<_> = ty.walk_shallow()
.map(|subty| self.type_bound(subty))
.collect::<Vec<_>>();
.collect();
let mut regions = smallvec![];
ty.push_regions(&mut regions);

View File

@ -62,6 +62,7 @@ pub use self::engine::{TraitEngine, TraitEngineExt};
pub use self::util::{elaborate_predicates, elaborate_trait_ref, elaborate_trait_refs};
pub use self::util::{supertraits, supertrait_def_ids, transitive_bounds,
Supertraits, SupertraitDefIds};
pub use self::util::{expand_trait_refs, TraitRefExpander};
pub use self::chalk_fulfill::{
CanonicalGoal as ChalkCanonicalGoal,
@ -1043,7 +1044,7 @@ fn vtable_methods<'a, 'tcx>(
)
}
impl<'tcx,O> Obligation<'tcx,O> {
impl<'tcx, O> Obligation<'tcx,O> {
pub fn new(cause: ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
predicate: O)

View File

@ -1772,7 +1772,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
bounds
);
let matching_bound = util::elaborate_predicates(self.tcx(), bounds.predicates)
let elaborated_predicates = util::elaborate_predicates(self.tcx(), bounds.predicates);
let matching_bound = elaborated_predicates
.filter_to_traits()
.find(|bound| {
self.infcx.probe(|_| {

View File

@ -1,3 +1,5 @@
use syntax_pos::Span;
use crate::hir;
use crate::hir::def_id::DefId;
use crate::traits::specialize::specialization_graph::NodeItem;
@ -41,7 +43,6 @@ fn anonymize_predicate<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
}
}
struct PredicateSet<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
set: FxHashSet<ty::Predicate<'tcx>>,
@ -73,12 +74,11 @@ impl<'a, 'gcx, 'tcx> PredicateSet<'a, 'gcx, 'tcx> {
/// "Elaboration" is the process of identifying all the predicates that
/// are implied by a source predicate. Currently this basically means
/// walking the "supertraits" and other similar assumptions. For
/// example, if we know that `T : Ord`, the elaborator would deduce
/// that `T : PartialOrd` holds as well. Similarly, if we have `trait
/// Foo : 'static`, and we know that `T : Foo`, then we know that `T :
/// 'static`.
pub struct Elaborator<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
/// walking the "supertraits" and other similar assumptions. For example,
/// if we know that `T: Ord`, the elaborator would deduce that `T: PartialOrd`
/// holds as well. Similarly, if we have `trait Foo: 'static`, and we know that
/// `T: Foo`, then we know that `T: 'static`.
pub struct Elaborator<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
stack: Vec<ty::Predicate<'tcx>>,
visited: PredicateSet<'a, 'gcx, 'tcx>,
}
@ -96,8 +96,7 @@ pub fn elaborate_trait_refs<'cx, 'gcx, 'tcx>(
trait_refs: impl Iterator<Item = ty::PolyTraitRef<'tcx>>)
-> Elaborator<'cx, 'gcx, 'tcx>
{
let predicates = trait_refs.map(|trait_ref| trait_ref.to_predicate())
.collect();
let predicates = trait_refs.map(|trait_ref| trait_ref.to_predicate()).collect();
elaborate_predicates(tcx, predicates)
}
@ -120,7 +119,7 @@ impl<'cx, 'gcx, 'tcx> Elaborator<'cx, 'gcx, 'tcx> {
let tcx = self.visited.tcx;
match *predicate {
ty::Predicate::Trait(ref data) => {
// Predicates declared on the trait.
// Get predicates declared on the trait.
let predicates = tcx.super_predicates_of(data.def_id());
let mut predicates: Vec<_> = predicates.predicates
@ -130,12 +129,11 @@ impl<'cx, 'gcx, 'tcx> Elaborator<'cx, 'gcx, 'tcx> {
debug!("super_predicates: data={:?} predicates={:?}",
data, predicates);
// Only keep those bounds that we haven't already
// seen. This is necessary to prevent infinite
// recursion in some cases. One common case is when
// people define `trait Sized: Sized { }` rather than `trait
// Sized { }`.
predicates.retain(|r| self.visited.insert(r));
// Only keep those bounds that we haven't already seen.
// This is necessary to prevent infinite recursion in some
// cases. One common case is when people define
// `trait Sized: Sized { }` rather than `trait Sized { }`.
predicates.retain(|p| self.visited.insert(p));
self.stack.extend(predicates);
}
@ -161,11 +159,9 @@ impl<'cx, 'gcx, 'tcx> Elaborator<'cx, 'gcx, 'tcx> {
// Currently, we do not elaborate const-evaluatable
// predicates.
}
ty::Predicate::RegionOutlives(..) => {
// Nothing to elaborate from `'a: 'b`.
}
ty::Predicate::TypeOutlives(ref data) => {
// We know that `T: 'a` for some type `T`. We can
// often elaborate this. For example, if we know that
@ -192,34 +188,35 @@ impl<'cx, 'gcx, 'tcx> Elaborator<'cx, 'gcx, 'tcx> {
tcx.push_outlives_components(ty_max, &mut components);
self.stack.extend(
components
.into_iter()
.filter_map(|component| match component {
Component::Region(r) => if r.is_late_bound() {
None
} else {
Some(ty::Predicate::RegionOutlives(
ty::Binder::dummy(ty::OutlivesPredicate(r, r_min))))
},
.into_iter()
.filter_map(|component| match component {
Component::Region(r) => if r.is_late_bound() {
None
} else {
Some(ty::Predicate::RegionOutlives(
ty::Binder::dummy(ty::OutlivesPredicate(r, r_min))))
}
Component::Param(p) => {
let ty = tcx.mk_ty_param(p.index, p.name);
Some(ty::Predicate::TypeOutlives(
ty::Binder::dummy(ty::OutlivesPredicate(ty, r_min))))
},
Component::Param(p) => {
let ty = tcx.mk_ty_param(p.index, p.name);
Some(ty::Predicate::TypeOutlives(
ty::Binder::dummy(ty::OutlivesPredicate(ty, r_min))))
}
Component::UnresolvedInferenceVariable(_) => {
None
},
Component::UnresolvedInferenceVariable(_) => {
None
}
Component::Projection(_) |
Component::EscapingProjection(_) => {
// We can probably do more here. This
// corresponds to a case like `<T as
// Foo<'a>>::U: 'b`.
None
},
})
.filter(|p| visited.insert(p)));
Component::Projection(_) |
Component::EscapingProjection(_) => {
// We can probably do more here. This
// corresponds to a case like `<T as
// Foo<'a>>::U: 'b`.
None
}
})
.filter(|p| visited.insert(p))
);
}
}
}
@ -233,16 +230,10 @@ impl<'cx, 'gcx, 'tcx> Iterator for Elaborator<'cx, 'gcx, 'tcx> {
}
fn next(&mut self) -> Option<ty::Predicate<'tcx>> {
// Extract next item from top-most stack frame, if any.
let next_predicate = match self.stack.pop() {
Some(predicate) => predicate,
None => {
// No more stack frames. Done.
return None;
}
};
self.push(&next_predicate);
return Some(next_predicate);
self.stack.pop().map(|item| {
self.push(&item);
item
})
}
}
@ -254,20 +245,124 @@ pub type Supertraits<'cx, 'gcx, 'tcx> = FilterToTraits<Elaborator<'cx, 'gcx, 'tc
pub fn supertraits<'cx, 'gcx, 'tcx>(tcx: TyCtxt<'cx, 'gcx, 'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>)
-> Supertraits<'cx, 'gcx, 'tcx>
{
-> Supertraits<'cx, 'gcx, 'tcx> {
elaborate_trait_ref(tcx, trait_ref).filter_to_traits()
}
pub fn transitive_bounds<'cx, 'gcx, 'tcx>(tcx: TyCtxt<'cx, 'gcx, 'tcx>,
bounds: impl Iterator<Item = ty::PolyTraitRef<'tcx>>)
-> Supertraits<'cx, 'gcx, 'tcx>
{
-> Supertraits<'cx, 'gcx, 'tcx> {
elaborate_trait_refs(tcx, bounds).filter_to_traits()
}
///////////////////////////////////////////////////////////////////////////
// `TraitRefExpander` iterator
///////////////////////////////////////////////////////////////////////////
/// "Trait reference expansion" is the process of expanding a sequence of trait
/// references into another sequence by transitively following all trait
/// aliases. e.g. If you have bounds like `Foo + Send`, a trait alias
/// `trait Foo = Bar + Sync;`, and another trait alias
/// `trait Bar = Read + Write`, then the bounds would expand to
/// `Read + Write + Sync + Send`.
pub struct TraitRefExpander<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
stack: Vec<TraitRefExpansionInfo<'tcx>>,
visited: PredicateSet<'a, 'gcx, 'tcx>,
}
#[derive(Debug, Clone)]
pub struct TraitRefExpansionInfo<'tcx> {
pub top_level_trait_ref: ty::PolyTraitRef<'tcx>,
pub top_level_span: Span,
pub trait_ref: ty::PolyTraitRef<'tcx>,
pub span: Span,
}
pub fn expand_trait_refs<'cx, 'gcx, 'tcx>(
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
trait_refs: impl IntoIterator<Item = (ty::PolyTraitRef<'tcx>, Span)>
) -> TraitRefExpander<'cx, 'gcx, 'tcx> {
let mut visited = PredicateSet::new(tcx);
let mut items: Vec<_> =
trait_refs
.into_iter()
.map(|(tr, sp)| TraitRefExpansionInfo {
top_level_trait_ref: tr.clone(),
top_level_span: sp,
trait_ref: tr,
span: sp,
})
.collect();
items.retain(|item| visited.insert(&item.trait_ref.to_predicate()));
TraitRefExpander { stack: items, visited: visited, }
}
impl<'cx, 'gcx, 'tcx> TraitRefExpander<'cx, 'gcx, 'tcx> {
// Returns `true` if `item` refers to a trait.
fn push(&mut self, item: &TraitRefExpansionInfo<'tcx>) -> bool {
let tcx = self.visited.tcx;
if !tcx.is_trait_alias(item.trait_ref.def_id()) {
return true;
}
// Get predicates declared on the trait.
let predicates = tcx.super_predicates_of(item.trait_ref.def_id());
let mut items: Vec<_> = predicates.predicates
.iter()
.rev()
.filter_map(|(pred, sp)| {
pred.subst_supertrait(tcx, &item.trait_ref)
.to_opt_poly_trait_ref()
.map(|trait_ref|
TraitRefExpansionInfo {
trait_ref,
span: *sp,
..*item
}
)
})
.collect();
debug!("expand_trait_refs: trait_ref={:?} items={:?}",
item.trait_ref, items);
// Only keep those items that we haven't already seen.
items.retain(|i| self.visited.insert(&i.trait_ref.to_predicate()));
self.stack.extend(items);
false
}
}
impl<'cx, 'gcx, 'tcx> Iterator for TraitRefExpander<'cx, 'gcx, 'tcx> {
type Item = TraitRefExpansionInfo<'tcx>;
fn size_hint(&self) -> (usize, Option<usize>) {
(self.stack.len(), None)
}
fn next(&mut self) -> Option<TraitRefExpansionInfo<'tcx>> {
loop {
let item = self.stack.pop();
match item {
Some(item) => {
if self.push(&item) {
return Some(item);
}
}
None => {
return None;
}
}
}
}
}
///////////////////////////////////////////////////////////////////////////
// Iterator over def-ids of supertraits
///////////////////////////////////////////////////////////////////////////
pub struct SupertraitDefIds<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,

View File

@ -1234,7 +1234,7 @@ impl<'tcx> TraitPredicate<'tcx> {
self.trait_ref.def_id
}
pub fn input_types<'a>(&'a self) -> impl DoubleEndedIterator<Item=Ty<'tcx>> + 'a {
pub fn input_types<'a>(&'a self) -> impl DoubleEndedIterator<Item = Ty<'tcx>> + 'a {
self.trait_ref.input_types()
}
@ -2400,7 +2400,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
pub fn discriminants(
&'a self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
) -> impl Iterator<Item=(VariantIdx, Discr<'tcx>)> + Captures<'gcx> + 'a {
) -> impl Iterator<Item = (VariantIdx, Discr<'tcx>)> + Captures<'gcx> + 'a {
let repr_type = self.repr.discr_type();
let initial = repr_type.initial_discriminant(tcx.global_tcx());
let mut prev_discr = None::<Discr<'tcx>>;

View File

@ -1177,13 +1177,13 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
);
}
ast::ImplItemKind::Type(ref ty) => {
// FIXME uses of the assoc type should ideally point to this
// FIXME: uses of the assoc type should ideally point to this
// 'def' and the name here should be a ref to the def in the
// trait.
self.visit_ty(ty)
}
ast::ImplItemKind::Existential(ref bounds) => {
// FIXME uses of the assoc type should ideally point to this
// FIXME: uses of the assoc type should ideally point to this
// 'def' and the name here should be a ref to the def in the
// trait.
for bound in bounds.iter() {
@ -1216,7 +1216,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
let hir_id = self.tcx.hir().node_to_hir_id(id);
let access = access_from!(self.save_ctxt, root_item, hir_id);
// The parent def id of a given use tree is always the enclosing item.
// The parent def-ID of a given use tree is always the enclosing item.
let parent = self.save_ctxt.tcx.hir().opt_local_def_id(id)
.and_then(|id| self.save_ctxt.tcx.parent(id))
.map(id_from_def_id);

View File

@ -975,12 +975,13 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
if trait_bounds.is_empty() {
span_err!(tcx.sess, span, E0224,
"at least one non-builtin trait is required for an object type");
"at least one non-builtin trait is required for an object type");
return tcx.types.err;
}
let mut projection_bounds = Vec::new();
let dummy_self = self.tcx().types.trait_object_dummy_self;
let mut bound_trait_refs = Vec::with_capacity(trait_bounds.len());
let (principal, potential_assoc_types) = self.instantiate_poly_trait_ref(
&trait_bounds[0],
dummy_self,
@ -988,22 +989,29 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
);
debug!("principal: {:?}", principal);
for trait_bound in trait_bounds[1..].iter() {
// sanity check for non-principal trait bounds
self.instantiate_poly_trait_ref(trait_bound,
dummy_self,
&mut vec![]);
for trait_bound in trait_bounds[1..].iter().rev() {
// Sanity check for non-principal trait bounds.
let (tr, _) = self.instantiate_poly_trait_ref(
trait_bound,
dummy_self,
&mut Vec::new()
);
bound_trait_refs.push((tr, trait_bound.span));
}
bound_trait_refs.push((principal, trait_bounds[0].span));
let (mut auto_traits, trait_bounds) = split_auto_traits(tcx, &trait_bounds[1..]);
if !trait_bounds.is_empty() {
let b = &trait_bounds[0];
let span = b.trait_ref.path.span;
struct_span_err!(self.tcx().sess, span, E0225,
"only auto traits can be used as additional traits in a trait object")
.span_label(span, "non-auto additional trait")
.emit();
let expanded_traits = traits::expand_trait_refs(tcx, bound_trait_refs);
let (auto_traits, regular_traits): (Vec<_>, Vec<_>) =
expanded_traits.partition(|i| tcx.trait_is_auto(i.trait_ref.def_id()));
if regular_traits.len() > 1 {
let extra_trait = &regular_traits[1];
let mut err = struct_span_err!(tcx.sess, extra_trait.top_level_span, E0225,
"only auto traits can be used as additional traits in a trait object");
err.span_label(extra_trait.span, "non-auto additional trait");
if extra_trait.span != extra_trait.top_level_span {
err.span_label(extra_trait.top_level_span, "expanded from this alias");
}
err.emit();
}
// Check that there are no gross object safety violations;
@ -1024,9 +1032,10 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
debug!("conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", tr);
match tr {
ty::Predicate::Trait(pred) => {
associated_types.extend(tcx.associated_items(pred.def_id())
.filter(|item| item.kind == ty::AssociatedKind::Type)
.map(|item| item.def_id));
associated_types
.extend(tcx.associated_items(pred.def_id())
.filter(|item| item.kind == ty::AssociatedKind::Type)
.map(|item| item.def_id));
}
ty::Predicate::Projection(pred) => {
// A `Self` within the original bound will be substituted with a
@ -1145,11 +1154,15 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
})
});
// Dedup auto traits so that `dyn Trait + Send + Send` is the same as `dyn Trait + Send`.
// De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as
// `dyn Trait + Send`.
let mut auto_traits: Vec<_> =
auto_traits.into_iter().map(|i| i.trait_ref.def_id()).collect();
auto_traits.sort();
auto_traits.dedup();
debug!("auto_traits: {:?}", auto_traits);
// Calling `skip_binder` is okay, because the predicates are re-bound.
// Calling `skip_binder` is okay because the predicates are re-bound.
let principal = if tcx.trait_is_auto(existential_principal.def_id()) {
ty::ExistentialPredicate::AutoTrait(existential_principal.def_id())
} else {
@ -1175,14 +1188,13 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
} else {
self.re_infer(span, None).unwrap_or_else(|| {
span_err!(tcx.sess, span, E0228,
"the lifetime bound for this object type cannot be deduced \
from context; please supply an explicit bound");
"the lifetime bound for this object type cannot be deduced \
from context; please supply an explicit bound");
tcx.lifetimes.re_static
})
}
})
};
debug!("region_bound: {:?}", region_bound);
let ty = tcx.mk_dynamic(existential_predicates, region_bound);
@ -2097,33 +2109,6 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
}
}
/// Divides a list of general trait bounds into two groups: auto traits (e.g., Sync and Send) and
/// the remaining general trait bounds.
fn split_auto_traits<'a, 'b, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
trait_bounds: &'b [hir::PolyTraitRef])
-> (Vec<DefId>, Vec<&'b hir::PolyTraitRef>)
{
let (auto_traits, trait_bounds): (Vec<_>, _) = trait_bounds.iter().partition(|bound| {
// Checks whether `trait_did` is an auto trait and adds it to `auto_traits` if so.
match bound.trait_ref.path.res {
Res::Def(DefKind::Trait, trait_did) if tcx.trait_is_auto(trait_did) => {
true
}
_ => false
}
});
let auto_traits = auto_traits.into_iter().map(|tr| {
if let Res::Def(DefKind::Trait, trait_did) = tr.trait_ref.path.res {
trait_did
} else {
unreachable!()
}
}).collect::<Vec<_>>();
(auto_traits, trait_bounds)
}
// A helper struct for conveniently grouping a set of bounds which we pass to
// and return from functions in multiple places.
#[derive(PartialEq, Eq, Clone, Debug)]

View File

@ -758,7 +758,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
}
fn assemble_inherent_candidates_from_param(&mut self, param_ty: ty::ParamTy) {
// FIXME -- Do we want to commit to this behavior for param bounds?
// FIXME: do we want to commit to this behavior for param bounds?
let bounds = self.param_env
.caller_bounds

View File

@ -1019,7 +1019,7 @@ fn check_false_global_bounds<'a, 'gcx, 'tcx>(
.iter()
.map(|(p, _)| *p)
.collect();
// Check elaborated bounds
// Check elaborated bounds.
let implied_obligations = traits::elaborate_predicates(fcx.tcx, predicates);
for pred in implied_obligations {

View File

@ -2133,10 +2133,10 @@ pub struct TraitRef {
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub struct PolyTraitRef {
/// The `'a` in `<'a> Foo<&'a T>`
/// The `'a` in `<'a> Foo<&'a T>`.
pub bound_generic_params: Vec<GenericParam>,
/// The `Foo<&'a T>` in `<'a> Foo<&'a T>`
/// The `Foo<&'a T>` in `<'a> Foo<&'a T>`.
pub trait_ref: TraitRef,
pub span: Span,