pub struct TypeRef { /* private fields */ }Expand description
A type as the language accepted it, plus the exact syntax it came from.
The retained slice is what generated Rust spells, through
spell. It is not
where facts go to survive a lossy classification any more — kind keeps the
lifetime, the wrapper and the argument it used to drop, and rebuilding the
syntax from it proves so. Keeping the slice anyway is cheap, exact
(nothing has to reconstruct token for token what the source already wrote),
and it is what makes the proof possible at all.
§The invariant
Every
TypeRefwas classified by the model.Flatclassified it from source syntax, or the registry pipeline composed it by layering over something already classified.
Historically enforced by visibility, now by convention. Before the
registry pipeline moved to the separate prebindgen-registry crate, the
boundary was api::core and was drawn by visibility at four places, each
checked by the compiler on every build:
the kind and origin fields | pub(super) — a public field is a constructor, so restricting only the composers would block nothing |
borrowed / optional / scalar | pub(crate) |
named | pub(super) — flat alone |
Flat::classify | pub(crate) |
A module-path seal can no longer express “the registry pipeline, and
nothing else” once that pipeline is a different crate — there is no path
inside this crate to name it — so borrowed / optional / scalar and
Flat::classify are now plain pub, and the fields stay pub(super)
(nothing outside flat ever needed them). The intent is unchanged and
documented here, but no longer compiler-enforced against a destination
adapter (prebindgen-c, prebindgen-jni): restoring that would need a real
API, e.g. a sealed capability token minted only by prebindgen-registry.
Where one needs a type the model already declares, the declaration
answers: see Variant::type_ref, which is what
the SumTag selector uses instead of composing a reading from an ident.
The invariant is unconditional — no phase, no lifetime, no direction — so it
holds for a stored value. That is the point: a TypeRef lives in
UnfoldLeaf::out_ty and FoldLeaf::ty, inside plans the registry itself
stores, so a borrow-carrying token would make the registry self-referential.
It deliberately does not claim the type’s converters exist. That is
false by design for stored readings — unrequire_output leaves a cell whose
converter genuinely cannot resolve, and a SumTag leaf never has one — so
converter existence stays a lookup that answers Option.
It does not claim a registry cell either, and the two are separate
questions. Holding a TypeRef means the model classified the type; whether
it is in a type table is the registry’s business, and the registry states it
in three parts — a cell (the type entered the pipeline), a root (the
binding asked for it directly), an entry (a converter resolved). A
SumTag leaf’s type makes the first and not the second, deliberately
(#282); see Registry::reference_output in the registry layer above.
Implementations§
Source§impl TypeRef
impl TypeRef
Sourcepub fn kind(&self) -> &TypeKind
pub fn kind(&self) -> &TypeKind
What the type means. Classify off this, never off the spelling.
The seal, as a compiled assertion. An out-of-crate consumer cannot
assemble a reading, because the fields it would have to name are private
(E0451):
let forged = TypeRef { kind: TypeKind::Unit, origin: todo!() };…nor, historically, through a composer (E0624) — no longer true:
borrowed / optional / scalar are pub now that the registry
pipeline that composes with them is the separate prebindgen-registry
crate rather than code inside this one:
let composed = TypeRef::scalar(ScalarKind::Bool);The struct-literal case above still proves the crate boundary. The
stronger claim this crate used to enforce by visibility — that nothing
above api::core can mint one either — no longer has a module path to
be checked against once the registry pipeline is the separate
prebindgen-registry crate; see the type-level doc’s “The invariant”
section for what replaced it.
Sourcepub fn spell(&self) -> TokenStream
pub fn spell(&self) -> TokenStream
The tokens generated Rust must spell. Spell off this, never off
kind — re-deriving a spelling from the classification is how
Box<Option<T>> becomes an E0308.
Tokens, not a syn::Type: a spelling is for spelling. What the type
is has an answer in kind and in the readings beside
it; the node itself never leaves the model.
Source§impl TypeRef
impl TypeRef
Sourcepub fn layer_stack(&self) -> (Shape, &TypeRef)
pub fn layer_stack(&self) -> (Shape, &TypeRef)
The arity layers over this type, and what they wrap.
Option<Vec<T>> is Optional(Iterable(Base)) over T. The stack is the
same Shape the expansion and decomposition plans are built from — so a
consumer that needs a plan shape has it, rather than rebuilding one from
flags that were derived from this type moments earlier.
A borrow is not a layer. Optional and Iterable change arity — none
or one, none or many — while &T is the same single value held
differently. That is ownership, and it stays on the returned core, where
borrow_target reads it.
A layer out of position is not a layer: Vec<Option<T>> is
Iterable(Base) over Option<T>, because the optional is inside the run.
The stack is what wraps the payload, in order, and nothing is reordered to
make it fit a shape a caller hoped for.
Returning the stack rather than a set of flags is what lets a caller
decline: a consumer that can only build Base and Optional(Base)
matches those and falls through on anything else, instead of silently
consuming a layer it cannot honour.
Sourcepub fn layer_types(&self) -> Vec<&TypeRef>
pub fn layer_types(&self) -> Vec<&TypeRef>
Every type on the way down through the arity layers, outermost first and
ending at the core layer_stack returns.
What a registration walks, which is a different question from what crosses: a value delivered layer-by-layer needs each of these un-required, and none of them has a converter of its own.
Sourcepub fn unwrapped(&self) -> &TypeRef
pub fn unwrapped(&self) -> &TypeRef
This type with every transparent wrapper peeled
off — Box<Cow<'_, [T]>> → the [T] node, an unwrapped type → itself.
The fold, made explicit. kind is the syntax the source
wrote, wrappers and all; a consumer that does not care which of them stand
over a type says so here, at its own call site, and the ones that must put
them back in generated Rust ask erased_wrappers
instead. That split is why the wrapper is no longer erased during
lowering: the model reports, the consumer decides.
Per layer, and only this one: a wrapper under a borrow or inside an
Option belongs to that inner node, which answers for itself.
Sourcepub fn optional_inner(&self) -> Option<&TypeRef>
pub fn optional_inner(&self) -> Option<&TypeRef>
What an Option<T> wraps, else None.
One layer, named. layer_stack reads the whole
arity stack; these three read exactly the layer a caller asks for, which
is what a consumer wants when it can only represent some of them.
Read through unwrapped, like every layer accessor
here: Box<Option<T>> is an optional to a destination language, and the
Box is still on the node for whoever has to spell it.
Sourcepub fn sequence_elem(&self) -> Option<&TypeRef>
pub fn sequence_elem(&self) -> Option<&TypeRef>
The element of a run of values (Vec<T>, [T]), else None.
Sourcepub fn borrow_target(&self) -> Option<&TypeRef>
pub fn borrow_target(&self) -> Option<&TypeRef>
What a borrow points at, else None.
Through an out-parameter’s Uninit: &mut MaybeUninit<T> points at a T’s storage, and the slot is not a type
anything converts, registers or crosses with. A consumer that needs to
tell the two borrows apart reads the kind, where the
MaybeUninit the source wrote is still standing.
Sourcepub fn borrowed(&self) -> TypeRef
pub fn borrowed(&self) -> TypeRef
A borrow of this type — &T from T.
Keeps this type’s location: the borrow exists because of this value, so a diagnostic about it should point where the value came from.
Sourcepub fn optional(&self) -> TypeRef
pub fn optional(&self) -> TypeRef
An optional of this type — Option<T> from T. Location as
Self::borrowed.
Sourcepub fn scalar(kind: ScalarKind) -> TypeRef
pub fn scalar(kind: ScalarKind) -> TypeRef
A scalar the binding invented — a presence flag, a selector.
Placeless, and deliberately: no file wrote it, so claiming a location
would make a fabricated one indistinguishable from a real one.
Flat::classify does exactly this for a
composed spelling, and ensure_entry gives adapter-authored cells the
same treatment — has_position already gates what a diagnostic prints.
Sourcepub fn key(&self) -> TypeKey
pub fn key(&self) -> TypeKey
This type’s identity as a table key.
The canonical spelling is what a key is (#113), and reading it is
legitimate — but it should be the model’s answer rather than every caller
reaching for the spelling itself, since a caller that reaches
into origin to reason is the thing this model exists to stop.
Sourcepub fn erased_wrapper(&self) -> Option<&'static str>
pub fn erased_wrapper(&self) -> Option<&'static str>
The transparent wrapper this type’s spelling
adds over its classification, if any — Box<Option<T>> → Some("Box"),
Option<T> → None.
This exists because kind and spell
answer different questions, and only one of them is about the
destination:
kinddecides what the destination sees — the surface type and the wire.Box<Option<String>>andOption<String>are one optional string to every destination language, which is why the wrapper is erased.syntaxdecides how the value is converted — and Rust does tell them apart. A converter that rebuilds a value must produce the type the source actually spelled.
So a consumer that classifies should never consult this; a consumer
that reconstructs a Rust value must, because rebuilding from the
classification alone yields the stripped type and handing that to a
parameter spelled Box<..> is an E0308 in the generated crate.
Only the outermost wrapper is named. That is enough to decide whether
a spelling was erased — which is the question a refusal asks — but a
consumer that rebuilds a nested Box<Cow<'_, T>> needs every layer, and
asks erased_wrappers for the whole list, and
stripped_key for what sits under them.
Erased says nothing about rebuildable: Box reconstructs as
Box::new(v), while Cow’s Owned/Borrowed choice is not determined
by any fact the model holds. Which wrappers an emitter can rebuild is
that emitter’s policy; this only stops the wrapper from being invisible.
Sourcepub fn erased_wrappers(&self) -> Vec<&'static str>
pub fn erased_wrappers(&self) -> Vec<&'static str>
Every transparent wrapper this type’s spelling
adds over its classification, outermost first — Box<Box<T>> →
["Box", "Box"], Box<Cow<'_, T>> → ["Box", "Cow"], an unwrapped
spelling → [].
The list erased_wrapper names the head of. A
consumer deciding whether to refuse needs only that head; one that
rebuilds needs all of them, because it has to apply an operation per
layer — and Box<Cow<'_, T>> is two different operations, not one
repeated.
§This answers for one layer’s spelling
An erasure sits outside the layer it wraps, so this is a question that has to be asked on the way down, at every layer, and never once at the top:
| Spelling | here | on borrow_target |
|---|---|---|
Box<&Vec<T>> | ["Box"] | [] — kind is Ref, and peeling it first drops the Box |
&Box<Vec<T>> | [] — a syn::Type::Reference cannot be peeled | ["Box"] |
A rebuild therefore collects wrappers as it descends: by the time it
reaches the leaf they are gone from kind, which is precisely the thing
they are missing from.
Sourcepub fn stripped_key(&self) -> TypeKey
pub fn stripped_key(&self) -> TypeKey
This type’s identity as a table key with every transparent wrapper removed.
What key answers for a spelling, this answers for the
type. The two are different questions and both are legitimate:
- a conversion is keyed by
key, becauseBox<Option<T>>andOption<T>genuinely need different converter bodies — one has to put aBoxback and the other must not; - a declaration is keyed by this, because a declaration says what a
type is to the destination language, and a wrapper the model erases
cannot change that. A
Box<Payload>parameter is aPayloadto Kotlin, so it must findPayload’s data-class declaration — keying it by spelling finds nothing and silently costs the parameter its lowering.
Use this wherever the lookup is against declarations the binding author
wrote, and key wherever it is against something derived per spelling.
Sourcepub fn is_exclusive_borrow(&self) -> bool
pub fn is_exclusive_borrow(&self) -> bool
True when this is &mut T over a value — not &mut MaybeUninit<T>.
The one distinction an out-parameter’s form makes to a converter: an
exclusive borrow may be read before it is written and an out-parameter
may not, so the two cannot share a conversion. Everything else about the
slot — that it points at a T, that the T is what crosses — is
borrow_target’s answer.
Sourcepub fn fallible_parts(&self) -> Option<(&TypeRef, &TypeRef)>
pub fn fallible_parts(&self) -> Option<(&TypeRef, &TypeRef)>
The Ok and Err sides when this is a Result, else None.
Sourcepub fn callback_args(&self) -> Option<&[TypeRef]>
pub fn callback_args(&self) -> Option<&[TypeRef]>
The argument types when this is a callback, else None — the reading
counterpart of
extract_fn_trait_args.
The two are the same question asked of different things, and that is the
whole difference. extract_fn_trait_args takes an
impl Fn(..) + Send + Sync + 'static apart: it walks the bounds,
checks the three markers, and refuses a written return type — it is a
classifier, and the one the model itself runs to build
TypeKind::Callback. This reads the result of that classification,
already made. A consumer holding a reading has no reason to redo the
walk, and every reason not to: a Vec<syn::Type> of arguments has lost
which of them the model accepted and how, while each TypeRef here
carries its own classification and its own spelling.
Consequently this answers None for a type that merely looks like a
callback but was refused (a missing Send, an impl Fn() -> u8): the
acceptance already happened, and asking again is how the two drift.
Sourcepub fn array_extent(&self) -> Option<&ArrayExtent>
pub fn array_extent(&self) -> Option<&ArrayExtent>
The extent of this type when it is an array, else None.
Sourcepub fn extents(&self) -> Vec<&ArrayExtent>
pub fn extents(&self) -> Vec<&ArrayExtent>
Every extent reachable from this type, outermost first — so a nested
[[u8; A]; B] yields B then A.
Used to find which consts an emitted C type may name, and therefore which
must reach the header as a #define.
Sourcepub fn walk(&self) -> Vec<&TypeRef>
pub fn walk(&self) -> Vec<&TypeRef>
This type and every type reachable inside it, outermost first.
The nested positions are real TypeRefs carrying their own spelling and
origin, so a consumer that indexes types finds Foo from Vec<Foo> with
the classification already made rather than a sub-path to re-read.
A Named’s generic arguments are not among them:
TypeId keeps a name and nothing else, so MyBox<Foo> reaches no Foo
here. The full spelling is Self::spell’s answer for whoever needs it.
Trait Implementations§
Source§impl Display for TypeRef
impl Display for TypeRef
Source§fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>
The type as the source wrote it, for a message.
Diagnostics are not emission: a panic naming an unsupported type is
decision code reporting why it decided, and it must not need the
Emit capability to say so. So this is
ungated where spell is not.
The identity, not the spelling — TypeKey, which is
canonical_type rendered. Delegating to spell() would have handed the
captured spelling back out through format!("{ty}"), so
syn::parse_str(&ty.to_string()) reconstructed it exactly and the
capability was a suggestion. Rendering the canonical form keeps
diagnostics readable while making the round trip land on a normalized
type rather than the source’s own tokens.
Auto Trait Implementations§
impl !Send for TypeRef
impl !Sync for TypeRef
impl Freeze for TypeRef
impl RefUnwindSafe for TypeRef
impl Unpin for TypeRef
impl UnsafeUnpin for TypeRef
impl UnwindSafe for TypeRef
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more