/// The position at which an expression is, used while lowering #[derive(Clone, Copy, PartialEq, Eq, Debug)] pubenum ExprPos { /// The expression is in the left hand side of an assignment
Lhs, /// The expression is in the right hand side of an assignment
Rhs, /// The expression is an array being indexed, needed to allow constant /// arrays to be dynamically indexed
AccessBase { /// The index is a constant
constant_index: bool,
},
}
impl ExprPos { /// Returns an lhs position if the current position is lhs otherwise AccessBase constfn maybe_access_base(&self, constant_index: bool) -> Self { match *self {
ExprPos::Lhs
| ExprPos::AccessBase {
constant_index: false,
} => *self,
_ => ExprPos::AccessBase { constant_index },
}
}
}
/// The [`FunctionArgument`]s for the final [`crate::Function`]. /// /// Parameters with the `out` and `inout` qualifiers have [`Pointer`] types /// here. For example, an `inout vec2 a` argument would be a [`Pointer`] to /// a [`Vector`]. /// /// [`Pointer`]: crate::TypeInner::Pointer /// [`Vector`]: crate::TypeInner::Vector pub arguments: Vec<FunctionArgument>,
/// The parameter types given in the source code. /// /// The `out` and `inout` qualifiers don't affect the types that appear /// here. For example, an `inout vec2 a` argument would simply be a /// [`Vector`], not a pointer to one. /// /// [`Vector`]: crate::TypeInner::Vector pub parameters: Vec<Handle<Type>>, pub parameters_info: Vec<ParameterInfo>,
let var = VariableReference {
expr,
load,
mutable,
constant,
entry_arg,
};
self.symbol_table.add(name.into(), var);
Ok(())
}
/// Starts the expression emitter /// /// # Panics /// /// - If called twice in a row without calling [`emit_end`][Self::emit_end]. #[inline] pubfn emit_start(&mutself) { self.emitter.start(&self.expressions)
}
/// Emits all the expressions captured by the emitter to the current body /// /// # Panics /// /// - If called before calling [`emit_start`]. /// - If called twice in a row without calling [`emit_start`]. /// /// [`emit_start`]: Self::emit_start pubfn emit_end(&mutself) { self.body.extend(self.emitter.finish(&self.expressions))
}
/// Emits all the expressions captured by the emitter to the current body /// and starts the emitter again /// /// # Panics /// /// - If called before calling [`emit_start`][Self::emit_start]. pubfn emit_restart(&mutself) { self.emit_end(); self.emit_start()
}
/// Add variable to current scope /// /// Returns a variable if a variable with the same name was already defined, /// otherwise returns `None` pubfn add_local_var(
&mutself,
name: String,
expr: Handle<Expression>,
mutable: bool,
) -> Option<VariableReference> { let var = VariableReference {
expr,
load: true,
mutable,
constant: None,
entry_arg: None,
};
self.symbol_table.add(name, var)
}
/// Add function argument to current scope pubfn add_function_arg(
&mutself,
name_meta: Option<(String, Span)>,
ty: Handle<Type>,
qualifier: ParameterQualifier,
) -> Result<()> { let index = self.arguments.len(); letmut arg = FunctionArgument {
name: name_meta.as_ref().map(|&(ref name, _)| name.clone()),
ty,
binding: None,
}; self.parameters.push(ty);
/// Returns a [`StmtContext`] to be used in parsing and lowering /// /// # Panics /// /// - If more than one [`StmtContext`] are active at the same time or if the /// previous call didn't use it in lowering. #[must_use] pubfn stmt_ctx(&mutself) -> StmtContext { self.stmt_ctx.take().unwrap()
}
/// Lowers a [`HirExpr`] which might produce a [`Expression`]. /// /// consumes a [`StmtContext`] returning it to the context so that it can be /// used again later. pubfn lower(
&mutself, mut stmt: StmtContext,
frontend: &mut Frontend,
expr: Handle<HirExpr>,
pos: ExprPos,
) -> Result<(Option<Handle<Expression>>, Span)> { let res = self.lower_inner(&stmt, frontend, expr, pos);
/// Similar to [`lower`](Self::lower) but returns an error if the expression /// returns void (ie. doesn't produce a [`Expression`]). /// /// consumes a [`StmtContext`] returning it to the context so that it can be /// used again later. pubfn lower_expect(
&mutself, mut stmt: StmtContext,
frontend: &mut Frontend,
expr: Handle<HirExpr>,
pos: ExprPos,
) -> Result<(Handle<Expression>, Span)> { let res = self.lower_expect_inner(&stmt, frontend, expr, pos);
/// internal implementation of [`lower_expect`](Self::lower_expect) /// /// this method is only public because it's used in /// [`function_call`](Frontend::function_call), unless you know what /// you're doing use [`lower_expect`](Self::lower_expect) pubfn lower_expect_inner(
&mutself,
stmt: &StmtContext,
frontend: &mut Frontend,
expr: Handle<HirExpr>,
pos: ExprPos,
) -> Result<(Handle<Expression>, Span)> { let (maybe_expr, meta) = self.lower_inner(stmt, frontend, expr, pos)?;
let expr = match maybe_expr {
Some(e) => e,
None => { return Err(Error {
kind: ErrorKind::SemanticError("Expression returns void".into()),
meta,
})
}
};
Ok((expr, meta))
}
fn lower_store(
&mutself,
pointer: Handle<Expression>,
value: Handle<Expression>,
meta: Span,
) -> Result<()> { iflet Expression::Swizzle {
size, mut vector,
pattern,
} = self.expressions[pointer]
{ // Stores to swizzled values are not directly supported, // lower them as series of per-component stores. let size = match size {
VectorSize::Bi => 2,
VectorSize::Tri => 3,
VectorSize::Quad => 4,
};
let handle = match *kind {
HirExprKind::Access { base, index } => { let (index, _) = self.lower_expect_inner(stmt, frontend, index, ExprPos::Rhs)?; let maybe_constant_index = match pos { // Don't try to generate `AccessIndex` if in a LHS position, since it // wouldn't produce a pointer.
ExprPos::Lhs => None,
_ => self
.module
.to_ctx()
.eval_expr_to_u32_from(index, &self.expressions)
.ok(),
};
let base = self
.lower_expect_inner(
stmt,
frontend,
base,
pos.maybe_access_base(maybe_constant_index.is_some()),
)?
.0;
let pointer = maybe_constant_index
.map(|index| self.add_expression(Expression::AccessIndex { base, index }, meta))
.unwrap_or_else(|| { self.add_expression(Expression::Access { base, index }, meta)
})?;
if ExprPos::Rhs == pos { let resolved = self.resolve_type(pointer, meta)?; if resolved.pointer_space().is_some() { return Ok((
Some(self.add_expression(Expression::Load { pointer }, meta)?),
meta,
));
}
}
pointer
}
HirExprKind::Select { base, ref field } => { let base = self.lower_expect_inner(stmt, frontend, base, pos)?.0;
frontend.field_selection(self, pos, base, field, meta)?
}
HirExprKind::Literal(literal) if pos != ExprPos::Lhs => { self.add_expression(Expression::Literal(literal), meta)?
}
HirExprKind::Binary { left, op, right } if pos != ExprPos::Lhs => { let (mut left, left_meta) = self.lower_expect_inner(stmt, frontend, left, ExprPos::Rhs)?; let (mut right, right_meta) = self.lower_expect_inner(stmt, frontend, right, ExprPos::Rhs)?;
// Check that the two arguments have the same dimensions if !dimensions_ok || left_scalar != right_scalar {
frontend.errors.push(Error {
kind: ErrorKind::SemanticError(
format!( "Cannot apply operation to {:?} and {:?}",
left_inner, right_inner
)
.into(),
),
meta,
})
}
match op {
BinaryOperator::Divide => { // Naga IR doesn't support matrix division so we need to // divide the columns individually and reassemble the matrix letmut components = Vec::with_capacity(left_columns as usize);
for index in0..left_columns as u32 { // Get the column vectors let left_vector = self.add_expression(
Expression::AccessIndex { base: left, index },
meta,
)?; let right_vector = self.add_expression(
Expression::AccessIndex { base: right, index },
meta,
)?;
// Divide the vectors let column = self.add_expression(
Expression::Binary {
op,
left: left_vector,
right: right_vector,
},
meta,
)?;
components.push(column)
}
let ty = self.module.types.insert( Type {
name: None,
inner: TypeInner::Matrix {
columns: left_columns,
rows: left_rows,
scalar: left_scalar,
},
},
Span::default(),
);
// Rebuild the matrix from the divided vectors self.add_expression(Expression::Compose { ty, components }, meta)?
}
BinaryOperator::Equal | BinaryOperator::NotEqual => { // Naga IR doesn't support matrix comparisons so we need to // compare the columns individually and then fold them together // // The folding is done using a logical and for equality and // a logical or for inequality let equals = op == BinaryOperator::Equal;
for index in0..left_columns as u32 { // Get the column vectors let left_vector = self.add_expression(
Expression::AccessIndex { base: left, index },
meta,
)?; let right_vector = self.add_expression(
Expression::AccessIndex { base: right, index },
meta,
)?;
// The result of comparing two vectors is a boolean vector // so use a relational function like all to get a single // boolean value let compare = self.add_expression(
Expression::Relational { fun, argument },
meta,
)?;
self.add_expression(
Expression::Binary {
op,
left: scalar_vector,
right,
},
meta,
)?
}
_ => self.add_expression(Expression::Binary { left, op, right }, meta)?,
},
(
&TypeInner::Scalar(left_scalar),
&TypeInner::Matrix {
rows,
columns,
scalar: right_scalar,
},
) => { // Check that the two arguments have the same scalar type if left_scalar != right_scalar {
frontend.errors.push(Error {
kind: ErrorKind::SemanticError(
format!( "Cannot apply operation to {:?} and {:?}",
left_inner, right_inner
)
.into(),
),
meta,
})
}
match op {
BinaryOperator::Divide
| BinaryOperator::Add
| BinaryOperator::Subtract => { // Naga IR doesn't support all matrix by scalar operations so // we need for some to turn the scalar into a vector by // splatting it and then for each column vector apply the // operation and finally reconstruct the matrix let scalar_vector = self.add_expression(
Expression::Splat {
size: rows,
value: left,
},
meta,
)?;
letmut components = Vec::with_capacity(columns as usize);
for index in0..columns as u32 { // Get the column vector let matrix_column = self.add_expression(
Expression::AccessIndex { base: right, index },
meta,
)?;
// Apply the operation to the splatted vector and // the column vector let column = self.add_expression(
Expression::Binary {
op,
left: scalar_vector,
right: matrix_column,
},
meta,
)?;
components.push(column)
}
let ty = self.module.types.insert( Type {
name: None,
inner: TypeInner::Matrix {
columns,
rows,
scalar: left_scalar,
},
},
Span::default(),
);
// Rebuild the matrix from the operation result vectors self.add_expression(Expression::Compose { ty, components }, meta)?
}
_ => { self.add_expression(Expression::Binary { left, op, right }, meta)?
}
}
}
(
&TypeInner::Matrix {
rows,
columns,
scalar: left_scalar,
},
&TypeInner::Scalar(right_scalar),
) => { // Check that the two arguments have the same scalar type if left_scalar != right_scalar {
frontend.errors.push(Error {
kind: ErrorKind::SemanticError(
format!( "Cannot apply operation to {:?} and {:?}",
left_inner, right_inner
)
.into(),
),
meta,
})
}
match op {
BinaryOperator::Divide
| BinaryOperator::Add
| BinaryOperator::Subtract => { // Naga IR doesn't support all matrix by scalar operations so // we need for some to turn the scalar into a vector by // splatting it and then for each column vector apply the // operation and finally reconstruct the matrix
letmut components = Vec::with_capacity(columns as usize);
for index in0..columns as u32 { // Get the column vector let matrix_column = self.add_expression(
Expression::AccessIndex { base: left, index },
meta,
)?;
// Apply the operation to the splatted vector and // the column vector let column = self.add_expression(
Expression::Binary {
op,
left: matrix_column,
right: scalar_vector,
},
meta,
)?;
components.push(column)
}
let ty = self.module.types.insert( Type {
name: None,
inner: TypeInner::Matrix {
columns,
rows,
scalar: left_scalar,
},
},
Span::default(),
);
// Rebuild the matrix from the operation result vectors self.add_expression(Expression::Compose { ty, components }, meta)?
}
_ => { self.add_expression(Expression::Binary { left, op, right }, meta)?
}
}
}
_ => self.add_expression(Expression::Binary { left, op, right }, meta)?,
}
}
HirExprKind::Unary { op, expr } if pos != ExprPos::Lhs => { let expr = self
.lower_expect_inner(stmt, frontend, expr, ExprPos::Rhs)?
.0;
self.add_expression(Expression::Unary { op, expr }, meta)?
}
HirExprKind::Variable(ref var) => match pos {
ExprPos::Lhs => { if !var.mutable {
frontend.errors.push(Error {
kind: ErrorKind::SemanticError( "Variable cannot be used in LHS position".into(),
),
meta,
})
}
var.expr
}
ExprPos::AccessBase { constant_index } => { // If the index isn't constant all accesses backed by a constant base need // to be done through a proxy local variable, since constants have a non // pointer type which is required for dynamic indexing if !constant_index { iflet Some((constant, ty)) = var.constant { let init = self
.add_expression(Expression::Constant(constant), Span::default())?; let local = self.locals.append(
LocalVariable {
name: None,
ty,
init: Some(init),
},
Span::default(),
);
self.add_expression(Expression::LocalVariable(local), Span::default())?
} else {
var.expr
}
} else {
var.expr
}
}
_ if var.load => { self.add_expression(Expression::Load { pointer: var.expr }, meta)?
}
ExprPos::Rhs => { iflet Some((constant, _)) = self.is_const.then_some(var.constant).flatten() { self.add_expression(Expression::Constant(constant), meta)?
} else {
var.expr
}
}
},
HirExprKind::Call(ref call) if pos != ExprPos::Lhs => { let maybe_expr = frontend.function_or_constructor_call( self,
stmt,
call.kind.clone(),
&call.args,
meta,
)?; return Ok((maybe_expr, meta));
} // `HirExprKind::Conditional` represents the ternary operator in glsl (`:?`) // // The ternary operator is defined to only evaluate one of the two possible // expressions which means that it's behavior is that of an `if` statement, // and it's merely syntactic sugar for it.
HirExprKind::Conditional {
condition,
accept,
reject,
} if ExprPos::Lhs != pos => { // Given an expression `a ? b : c`, we need to produce a Naga // statement roughly like: // // var temp; // if a { // temp = convert(b); // } else { // temp = convert(c); // } // // where `convert` stands for type conversions to bring `b` and `c` to // the same type, and then use `temp` to represent the value of the whole // conditional expression in subsequent code.
// Lower the condition first to the current bodyy let condition = self
.lower_expect_inner(stmt, frontend, condition, ExprPos::Rhs)?
.0;
let (mut accept_body, (mut accept, accept_meta)) = self.new_body_with_ret(|ctx| { // Lower the `true` branch
ctx.lower_expect_inner(stmt, frontend, accept, pos)
})?;
let (mut reject_body, (mut reject, reject_meta)) = self.new_body_with_ret(|ctx| { // Lower the `false` branch
ctx.lower_expect_inner(stmt, frontend, reject, pos)
})?;
// We need to do some custom implicit conversions since the two target expressions // are in different bodies iflet (Some((accept_power, accept_scalar)), Some((reject_power, reject_scalar))) = ( // Get the components of both branches and calculate the type power self.expr_scalar_components(accept, accept_meta)?
.and_then(|scalar| Some((type_power(scalar)?, scalar))), self.expr_scalar_components(reject, reject_meta)?
.and_then(|scalar| Some((type_power(scalar)?, scalar))),
) { match accept_power.cmp(&reject_power) {
std::cmp::Ordering::Less => {
accept_body = self.with_body(accept_body, |ctx| {
ctx.conversion(&mut accept, accept_meta, reject_scalar)?;
Ok(())
})?;
}
std::cmp::Ordering::Equal => {}
std::cmp::Ordering::Greater => {
reject_body = self.with_body(reject_body, |ctx| {
ctx.conversion(&mut reject, reject_meta, accept_scalar)?;
Ok(())
})?;
}
}
}
// We need to get the type of the resulting expression to create the local, // this must be done after implicit conversions to ensure both branches have // the same type. let ty = self.resolve_type_handle(accept, accept_meta)?;
// Add the local that will hold the result of our conditional let local = self.locals.append(
LocalVariable {
name: None,
ty,
init: None,
},
meta,
);
let local_expr = self.add_expression(Expression::LocalVariable(local), meta)?;
// Add to each the store to the result variable
accept_body.push(
Statement::Store {
pointer: local_expr,
value: accept,
},
accept_meta,
);
reject_body.push(
Statement::Store {
pointer: local_expr,
value: reject,
},
reject_meta,
);
// Finally add the `If` to the main body with the `condition` we lowered // earlier and the branches we prepared. self.body.push(
Statement::If {
condition,
accept: accept_body,
reject: reject_body,
},
meta,
);
// Note: `Expression::Load` must be emitted before it's used so make // sure the emitter is active here. self.add_expression(
Expression::Load {
pointer: local_expr,
},
meta,
)?
}
HirExprKind::Assign { tgt, value } if ExprPos::Lhs != pos => { let (pointer, ptr_meta) = self.lower_expect_inner(stmt, frontend, tgt, ExprPos::Lhs)?; let (mut value, value_meta) = self.lower_expect_inner(stmt, frontend, value, ExprPos::Rhs)?;
let ty = match *self.resolve_type(pointer, ptr_meta)? {
TypeInner::Pointer { base, .. } => &self.module.types[base].inner, ref ty => ty,
};
value
}
HirExprKind::PrePostfix { op, postfix, expr } if ExprPos::Lhs != pos => { let (pointer, _) = self.lower_expect_inner(stmt, frontend, expr, ExprPos::Lhs)?; let left = iflet Expression::Swizzle { .. } = self.expressions[pointer] {
pointer
} else { self.add_expression(Expression::Load { pointer }, meta)?
};
let res = match *self.resolve_type(left, meta)? {
TypeInner::Scalar(scalar) => { let ty = TypeInner::Scalar(scalar);
Literal::one(scalar).map(|i| (ty, i, None, None))
}
TypeInner::Vector { size, scalar } => { let ty = TypeInner::Vector { size, scalar };
Literal::one(scalar).map(|i| (ty, i, Some(size), None))
}
TypeInner::Matrix {
columns,
rows,
scalar,
} => { let ty = TypeInner::Matrix {
columns,
rows,
scalar,
};
Literal::one(scalar).map(|i| (ty, i, Some(rows), Some(columns)))
}
_ => None,
}; let (ty_inner, literal, rows, columns) = match res {
Some(res) => res,
None => {
frontend.errors.push(Error {
kind: ErrorKind::SemanticError( "Increment/decrement only works on scalar/vector/matrix".into(),
),
meta,
}); return Ok((Some(left), meta));
}
};
letmut right = self.add_expression(Expression::Literal(literal), meta)?;
// Glsl allows pre/postfixes operations on vectors and matrices, so if the // target is either of them change the right side of the addition to be splatted // to the same size as the target, furthermore if the target is a matrix // use a composed matrix using the splatted value. iflet Some(size) = rows {
right = self.add_expression(Expression::Splat { size, value: right }, meta)?;
iflet Some(cols) = columns { let ty = self.module.types.insert( Type {
name: None,
inner: ty_inner,
},
meta,
);
right = self.add_expression(
Expression::Compose {
ty,
components: std::iter::repeat(right).take(cols as usize).collect(),
},
meta,
)?;
}
}
let value = self.add_expression(Expression::Binary { op, left, right }, meta)?;
self.lower_store(pointer, value, meta)?;
if postfix {
left
} else {
value
}
}
HirExprKind::Method {
expr: object, ref name, ref args,
} if ExprPos::Lhs != pos => { let args = args
.iter()
.map(|e| self.lower_expect_inner(stmt, frontend, *e, ExprPos::Rhs))
.collect::<Result<Vec<_>>>()?; match name.as_ref() { "length" => { if !args.is_empty() {
frontend.errors.push(Error {
kind: ErrorKind::SemanticError( ".length() doesn't take any arguments".into(),
),
meta,
});
} let lowered_array = self.lower_expect_inner(stmt, frontend, object, pos)?.0; let array_type = self.resolve_type(lowered_array, meta)?;
match *array_type {
TypeInner::Array {
size: crate::ArraySize::Constant(size),
..
} => { letmut array_length = self.add_expression(
Expression::Literal(Literal::U32(size.get())),
meta,
)?; self.forced_conversion(&mut array_length, meta, Scalar::I32)?;
array_length
} // let the error be handled in type checking if it's not a dynamic array
_ => { letmut array_length = self
.add_expression(Expression::ArrayLength(lowered_array), meta)?; self.conversion(&mut array_length, meta, Scalar::I32)?;
array_length
}
}
}
_ => { return Err(Error {
kind: ErrorKind::SemanticError(
format!("unknown method '{name}'").into(),
),
meta,
});
}
}
}
_ => { return Err(Error {
kind: ErrorKind::SemanticError(
format!("{:?} cannot be in the left hand side", stmt.hir_exprs[expr])
.into(),
),
meta,
})
}
};
/// Helper struct passed when parsing expressions /// /// This struct should only be obtained through [`stmt_ctx`](Context::stmt_ctx) /// and only one of these may be active at any time per context. #[derive(Debug)] pubstruct StmtContext { /// A arena of high level expressions which can be lowered through a /// [`Context`] to Naga's [`Expression`]s pub hir_exprs: Arena<HirExpr>,
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.23Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-19)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.