Skip to main content

ExprKind

Enum ExprKind 

Source
pub enum ExprKind {
Show 30 variants Error, Const(ConstId), Str(StrId), Decl(DeclId), Member { base: ExprId, field: u32, }, Subscript { base: ExprId, index: ExprId, }, Call { callee: ExprId, args: ExprList, }, Unary { op: UnaryOp, operand: ExprId, }, Binary { op: BinaryOp, lhs: ExprId, rhs: ExprId, }, Assign { op: Option<BinaryOp>, computation: TypeId, lhs: ExprId, rhs: ExprId, }, Cond { cond: ExprId, then: ExprId, otherwise: ExprId, }, Comma { lhs: ExprId, rhs: ExprId, }, Cast(ExprId), Convert { kind: Conversion, operand: ExprId, }, CompoundLiteral(DeclId), StmtExpr(StmtId), LabelAddr(LabelId), VaArg { list: ExprId, }, VaStart { list: ExprId, }, VaEnd { list: ExprId, }, VaCopy { dst: ExprId, src: ExprId, }, Classify { op: Classify, lhs: ExprId, rhs: Option<ExprId>, }, FpClassify { value: ExprId, answers: ExprList, }, Sign { op: Sign, lhs: ExprId, rhs: Option<ExprId>, }, Abs { operand: ExprId, }, ByteSwap { operand: ExprId, }, BitCount { operand: ExprId, count: BitCount, }, Overflow { op: OverflowOp, at: TypeId, args: ExprList, }, Atomic { op: AtomicOp, order: Ordering, args: ExprList, }, Unreachable,
}
Expand description

What an expression is.

Variants§

§

Error

A node that was already the subject of a diagnostic.

Poisoned, in the sense of spec/06-lexer-and-parser.md section 6.8: nothing is reported about one of these, which is what stops one bad declaration becoming forty bad uses.

§

Const(ConstId)

A constant, in the value table. Every constant that could be folded already has been.

§

Str(StrId)

A string literal, which is an array of characters with static storage duration.

§

Decl(DeclId)

A use of a declared object or function.

§

Member

base.field or, after the pointer has been dereferenced, base->field.

Fields

§base: ExprId

The object the field is in.

§field: u32

Which field, as an index into the record’s field list rather than as a name, since the lookup happened here and nothing after this should repeat it.

§

Subscript

base[index], with the pointer operand first however it was written.

Kept as a subscript rather than rewritten into *(base + index) because the rewriting has exactly one home, which is the walk to the IR, and because a diagnostic about a subscript should talk about a subscript.

Fields

§base: ExprId

The pointer, which has already decayed if it was an array.

§index: ExprId

The integer.

§

Call

callee(args), with the arguments already converted to the parameter types.

Fields

§callee: ExprId

The function, which is a pointer to a function after its decay.

§args: ExprList

The arguments, in order, each converted to what the prototype asks for and each promoted where the prototype does not say.

§

Unary

A prefix or postfix operator on one operand.

Fields

§op: UnaryOp

Which operator.

§operand: ExprId

What it applies to.

§

Binary

A binary operator on two operands of the same type, except for the shifts and the pointer arithmetic, where the two sides legitimately differ.

Fields

§op: BinaryOp

Which operator.

§lhs: ExprId

The left side.

§rhs: ExprId

The right side.

§

Assign

lhs = rhs, or a compound assignment with the operator kept as written.

Fields

§op: Option<BinaryOp>

The operator of a compound assignment, absent for a plain one.

§computation: TypeId

The type the operation is performed in, which is the node’s own type for a plain assignment and for most compound ones.

It is here because a op= b is not a = a op b with the conversions left out, and the difference is not academic: in int i = 5; i /= 0.5; the division happens in double and the answer is ten, and a compiler that converts the right side to int first divides by zero. The left side is an lvalue and cannot carry a conversion node of its own, so the type it is read into is written here instead, which is what clang calls the computation type and for the same reason.

§lhs: ExprId

What is assigned to, which is an lvalue.

§rhs: ExprId

What is assigned.

§

Cond

cond ? then : otherwise, with both arms already converted to the common type.

Fields

§cond: ExprId

The condition, converted to bool.

§then: ExprId

The arm taken when it is true. GNU’s cond ?: otherwise has this equal to the condition before its conversion, so the value is computed once.

§otherwise: ExprId

The arm taken when it is false.

§

Comma

lhs, rhs, whose value is the right side and whose left side is evaluated and dropped.

Fields

§lhs: ExprId

Evaluated first, for its effects.

§rhs: ExprId

The value.

§

Cast(ExprId)

A cast the program wrote. The type is the node’s type.

§

Convert

A conversion the language performed. The type is the node’s type.

Fields

§kind: Conversion

Which conversion, so that a reader and the verifier can both tell what happened rather than comparing the two types and guessing.

§operand: ExprId

What was converted.

§

CompoundLiteral(DeclId)

(T){ ... }, which is an unnamed object with an initializer and not a conversion.

§

StmtExpr(StmtId)

({ ... }), GNU’s statement expression, whose value is its last expression statement.

§

LabelAddr(LabelId)

&&label, GNU’s label address.

§

VaArg

va_arg(list, T), which reads the next argument and moves the list on.

The type it fetches is the node’s own type, so there is nothing else to hold. It is a node rather than a call because what it becomes is the target’s own sequence of loads and not a function anything links against.

Fields

§list: ExprId

The address of the list, which is what this reads through and moves on.

§

VaStart

va_start(list, last), which sets a list to the first argument past the named ones.

What the source wrote as the second argument is not here. It names where the named arguments stopped, which the enclosing function’s own type already says, and it is not evaluated: gcc rewrites va_start(ap, last) to a call with a zero in that place and C23 lets the program leave it out altogether.

Fields

§list: ExprId

The address of the list, which this writes.

§

VaEnd

va_end(list), which is the end of the reading and is nothing at all on most targets.

Fields

§list: ExprId

The address of the list.

§

VaCopy

va_copy(dst, src), which makes a second list standing where the first one stands.

Fields

§dst: ExprId

The address of the list being written.

§src: ExprId

The address of the list being read, which stays where it is.

§

Classify

One of the floating point classification builtins, which asks about a value rather than computing one.

A node rather than a call because there is nothing to call: isnan and the rest are macros in math.h that expand to exactly these, so the name has no function under it on any platform. What each becomes is a comparison, and the four of the family that C already has an operator for are ExprKind::Binary instead. See check/builtin/classify.rs for which are here and why.

Fields

§op: Classify

Which question is being asked.

§lhs: ExprId

The value asked about, converted to the type the question is asked in.

§rhs: Option<ExprId>

The value it is asked against, for the two questions that are about a pair of them.

§

FpClassify

__builtin_fpclassify(nan, inf, normal, subnormal, zero, x), which answers with whichever of the five the value is.

A node of its own rather than one of ExprKind::Classify because it has five operands besides the value, and a node rather than the chain of conditionals it turns into because the value is asked about four times and a program that writes __builtin_fpclassify(.., f()) calls f once.

Fields

§value: ExprId

The value asked about.

§answers: ExprList

The five answers, in the order the call writes them: a NaN, an infinity, a normal number, a subnormal and a zero. gcc requires each to be an integer constant expression and so does this.

§

Sign

__builtin_fabs or __builtin_copysign, which set the sign bit of a value from somewhere and leave every other bit of it alone.

A node rather than a call because the call would be to the math library, which is not on the link line of a program that never asked for it, and because neither one needs anything the library has: both are a mask and an or over the bits. See check/builtin/sign.rs.

Fields

§op: Sign

Where the sign of the answer comes from.

§lhs: ExprId

The value whose magnitude the answer has.

§rhs: Option<ExprId>

The value whose sign the answer has, for copysign, which is the only one that reads a sign from anywhere other than nowhere.

§

Abs

abs, labs and llabs, which are the magnitude of an integer.

A node rather than a call because the names are the C library’s and the compiler is allowed to know what they do, which is what lets a program define one of them and still get the magnitude. See check/builtin/abs.rs for when a call becomes one of these and when it stays a call.

The operand has already been converted to the type of the answer, which is the type the declaration gave the parameter, so nothing downstream has to widen it.

Fields

§operand: ExprId

The value whose magnitude this is.

§

ByteSwap

__builtin_bswap16, __builtin_bswap32 and __builtin_bswap64, which are the bytes of a value in the other order.

A node rather than a call because no object file defines one of these, and because a byte order swap is arithmetic: every machine can do it and most have an instruction for it. See check/builtin/bswap.rs.

The operand has already been converted to the unsigned type the declaration gave the parameter, which is also the type of the answer, so the width the bytes are reversed in is the width of the node and nothing downstream has to work it out.

Fields

§operand: ExprId

The value whose bytes these are.

§

BitCount

The bit counting builtins, which are five questions about which bits of a value are set.

__builtin_clz, __builtin_ctz, __builtin_popcount, __builtin_parity and __builtin_ffs, each in the plain, l and ll widths. Nodes rather than calls for the reason ExprKind::ByteSwap is one: no object file defines any of them, and every machine can answer them with instructions it already has. See check/builtin/count.rs.

The operand keeps the width its declaration gave it, because that width is the question. The type of the whole node is int whatever that width is, which is the one place this differs from the byte swaps, so the walk to the IR counts at the operand’s width and then narrows the answer.

Fields

§operand: ExprId

The value whose bits are being counted.

§count: BitCount

Which of the five questions this asks.

§

Overflow

The overflow checking builtins, which do the arithmetic exactly and say whether it fit.

__builtin_add_overflow, __builtin_sub_overflow and __builtin_mul_overflow. A node rather than a call for the reason ExprKind::ByteSwap is one, and for a second reason besides: the answer is two things, a value and a bit, and a call in C can only give back one. See check/builtin/overflow.rs.

The operands keep the types they were written with, because the arithmetic is defined as happening in infinite precision and then being put somewhere. What stands in for infinite precision is at, a type wide enough to hold every value all three of the written types can hold, and the walk to the IR converts both operands to it before doing anything.

The three operands are a run rather than three fields, because three of them and a type would be the widest variant here and every expression in the program is the size of the widest one. See Tast for the same trade made about a declaration.

Fields

§op: OverflowOp

Which of the three operations this is.

§at: TypeId

The type the arithmetic is done at, which represents every value of both operand types and of what the third operand points at. Working it out is the whole of the type checking here.

§args: ExprList

The two operands in the types they were written with, and then the pointer the exact result is written through whether or not it fit. Always exactly three.

§

Atomic

The atomic accesses and the barriers, which carry a memory ordering.

__atomic_load_n, __atomic_store_n, __atomic_thread_fence, __atomic_signal_fence and __sync_synchronize. A node rather than a call because none of them is a function anywhere: what they are is an access with an ordering on it, and an ordering is a thing the IR says about an access rather than an argument something is passed. See check/builtin/atomic.rs.

The order is a value here rather than an operand, because it was a constant in the source and the ordering of an access has to be known when the access is built. A call that wrote a value the compiler cannot fold gets the strongest ordering, which is what gcc does and is the only safe reading of a question that has to be answered before the program runs.

Fields

§op: AtomicOp

Which of the three shapes this is.

§order: Ordering

How strongly it is ordered, after the source’s number has been read and checked.

§args: ExprList

The address for a load, the address and then the value for a store, and nothing at all for a barrier.

§

Unreachable

__builtin_unreachable(), which is the program promising control does not get here.

It has no operands and no value, and it is a node rather than a call for the reason ExprKind::VaArg is one: there is no function of the name for a call to reach. What it carries is the promise itself, which the optimizer is where it will pay, and until then what it costs to honour is nothing at all. See check/builtin/unreachable.rs.

Trait Implementations§

Source§

impl Clone for ExprKind

Source§

fn clone(&self) -> ExprKind

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 Copy for ExprKind

Source§

impl Debug for ExprKind

Source§

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

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

impl Eq for ExprKind

Source§

impl PartialEq for ExprKind

Source§

fn eq(&self, other: &ExprKind) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for ExprKind

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> 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, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

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

fn try_from(value: U) -> Result<T, !>

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.