as is very complicated and do a lot of different things. See https://doc.rust-lang.org/stable/reference/expressions/operator-expr.html#type-cast-expressions.

This doc goes through the ways in which as casts are implemented from source (where they all look the same) to codegen (where they are all resolved).

The motivation is finding the proper way to fix https://github.com/rust-lang/rust/pull/113262, possibly revealing ways in which casts could be improved and made less confusing.

AST/HIR

as casts are parsed into the AST, where they are of course still represented as a simple ExprKind::Cast. HIR is the same, as as is dependent on the types.

Typeck

The first interesting interaction is typeck. Typeck is responsible for ensuring that all casts are well-formed. This happens in the cast module: https://github.com/rust-lang/rust/blob/e4cd1610067501fa4d347eba7b18f77137dbbf48/compiler/rustc_hir_typeck/src/cast.rs#L3.

It first tries to resolve the cast with a coercion. If that works, the cast is marked as a coercion cast for later. If it doesn't, then the complicated casting machinery is called to make sure that the cast is valid. Importantly, the result of this analysis is simply discarded. Additionally, the coercion code also stores the necessary adjustments required for the coercion.

THIR

THIR still has a mostly unified Cast expression, except for some pointer related coercions, for which it uses ExprKind::PointerCoercion which contains some casts that come from coercions. The enum inside is shared with the coercion typeck code. Additionally it also contains ExprKind::NeverToAny for that coercion.

THIR lowering first loweres the HIR expression directly and then applies adjustments. When lowering the cast itself, it checks whether typeck deemed the cast to be a coercion cast (using TypeckResults::is_coercion_cast). If it is one, it is not treated as an ExprKind::Cast anymore but simple a Use.

There is a curious special case with ExprKind::Pointer(PointerCast::ArrayToPointer). If the source is a reference (and it's not a coercion cast), then this expr is constructed.

It also does some special casing for enum variant casts to prevent cycles but that's not too important here. All non-coercion casts are turned into ExprKind::Cast. This means that even ptr->ptr casts end up as ExprKind::Cast instead of ExprKind::Pointer! Other than the special case above, only pointer related casts from adjustments are turned into that THIR expr.

MIR

MIR knows the Rvalue::Cast. But unlike the previous "does anything" casts, MIR's cast is fully resolved. As we've seen at the start, there are many different kinds of casts, but they're now exhaustively listed. MIR building for Cast computes the mir::CastKind based on the types.

MIR casts are a little weirdly organized due to the sharing of the PointerCoercion enum with adjustments.

codegen

Codegen looks at the MIR casts and deals with them.