Skip to main content

TypeRef

Struct TypeRef 

Source
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 TypeRef was classified by the model. Flat classified 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 fieldspub(super) — a public field is a constructor, so restricting only the composers would block nothing
borrowed / optional / scalarpub(crate)
namedpub(super)flat alone
Flat::classifypub(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

Source

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.

Source

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

pub fn location(&self) -> &SourceLocation

Where the type was written, for diagnostics. A composed type is placeless — [SourceLocation::has_position] gates what is printed.

Source§

impl TypeRef

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn sequence_elem(&self) -> Option<&TypeRef>

The element of a run of values (Vec<T>, [T]), else None.

Source

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.

Source

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.

Source

pub fn optional(&self) -> TypeRef

An optional of this type — Option<T> from T. Location as Self::borrowed.

Source

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.

Source

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.

Source

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:

  • kind decides what the destination sees — the surface type and the wire. Box<Option<String>> and Option<String> are one optional string to every destination language, which is why the wrapper is erased.
  • syntax decides 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.

Source

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:

Spellinghereon 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.

Source

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, because Box<Option<T>> and Option<T> genuinely need different converter bodies — one has to put a Box back 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 a Payload to Kotlin, so it must find Payload’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.

Source

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.

Source

pub fn fallible_parts(&self) -> Option<(&TypeRef, &TypeRef)>

The Ok and Err sides when this is a Result, else None.

Source

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.

Source

pub fn array_extent(&self) -> Option<&ArrayExtent>

The extent of this type when it is an array, else None.

Source

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.

Source

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 Clone for TypeRef

Source§

fn clone(&self) -> TypeRef

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for TypeRef

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for TypeRef

Source§

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 spellingTypeKey, 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§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.