pub struct Conv<'a> {
pub tast: &'a mut Tast,
pub types: &'a mut Types,
pub target: &'a TargetInfo,
}Expand description
Everything a conversion needs: the tree to write the node into and the table to ask.
Three references rather than a pass-wide context, because the conversions are the part of semantic analysis with no state of its own and nothing else here should be able to reach the scopes or the diagnostics through them.
Fields§
§tast: &'a mut TastThe tree the nodes are written into.
types: &'a mut TypesThe types, which conversions extend.
target: &'a TargetInfoWhat the target’s integers are, which is what the promotions are decided by.
Implementations§
Source§impl Conv<'_>
impl Conv<'_>
Sourcepub fn value(&mut self, expr: ExprId) -> ExprId
pub fn value(&mut self, expr: ExprId) -> ExprId
The value of an expression: 6.3.2.1, with the decays that replace it.
An array becomes a pointer to its first element, a function becomes a pointer to itself, and everything else that is an lvalue is read. An expression that is already a value is its own answer, so this can be called on any operand without asking what it is first.
Sourcepub fn promote(&mut self, expr: ExprId) -> ExprId
pub fn promote(&mut self, expr: ExprId) -> ExprId
The value of an expression with the integer promotions applied, 6.3.1.1.
Anything narrower than int becomes int, or unsigned int where int cannot hold
every value it had. A floating type, a pointer and a _BitInt are each their own
answer, the last because C23 6.3.1.1p2 says so and because that is the point of the
type: it is the one integer type in C that does what it says.
Sourcepub fn promote_bits(&mut self, expr: ExprId, width: u32) -> ExprId
pub fn promote_bits(&mut self, expr: ExprId, width: u32) -> ExprId
The value of a bit-field with the integer promotions applied to its width.
A bit-field is narrower than the type it was declared with, and it is the width that decides. The caller passes the width because the tree holds the field index and the width is a fact about the record, not about the expression.
Sourcepub fn usual_arithmetic(
&mut self,
lhs: ExprId,
rhs: ExprId,
) -> Option<(ExprId, ExprId)>
pub fn usual_arithmetic( &mut self, lhs: ExprId, rhs: ExprId, ) -> Option<(ExprId, ExprId)>
The usual arithmetic conversions, 6.3.1.8: both operands converted to one type.
None where either operand is not arithmetic, which is not a failure of this rule but
a question it does not answer, since p + 1 is pointer arithmetic and never reaches it.
Sourcepub fn to_bool(&mut self, expr: ExprId) -> ExprId
pub fn to_bool(&mut self, expr: ExprId) -> ExprId
A scalar as a condition, which is a comparison against zero and not a truncation.
That is why it is Conversion::Bool rather than Conversion::Arithmetic: (bool) 256 is true and (char) 256 is zero, and a compiler that treats the two the same is
wrong about one of them.
Sourcepub fn to_void(&mut self, expr: ExprId) -> ExprId
pub fn to_void(&mut self, expr: ExprId) -> ExprId
A value discarded, which is what a cast to void does.
An expression statement does not write one of these, even though it discards a value too. The statement is what does the discarding, and a statement expression’s value is the last statement’s, so an expression statement that had thrown its type away would have nothing left to give.
Sourcepub fn to_type(&mut self, expr: ExprId, ty: TypeId) -> ExprId
pub fn to_type(&mut self, expr: ExprId, ty: TypeId) -> ExprId
A value converted to a given type, with the kind of conversion worked out from the two.
This is what an assignment, an argument, a return and an initializer all do. It writes
the conversion the pair calls for and does not judge whether the pair is allowed: the
caller has the span and the wording, and a conversion that should have been diagnosed is
a diagnostic the caller owes rather than a node this refuses to write.
Sourcepub fn is_null_pointer_constant(&self, expr: ExprId) -> bool
pub fn is_null_pointer_constant(&self, expr: ExprId) -> bool
Whether an expression is a null pointer constant, 6.3.2.3p3.
An integer constant expression with the value zero, or such an expression cast to void *. The casts and the conversions are looked through because (void *)0 is one and so
is (long)0, and stopping at the first node would see a cast rather than a zero.