use super::PureAnalyzer;
use crate::arch;
use crate::data::{hir::*, lex::ComparisonToken, *};
use crate::intern::InternedStr;
impl PureAnalyzer {
pub fn expr(&mut self, expr: ast::Expr) -> Expr {
use ast::ExprType::*;
let _guard = self.recursion_check();
let _guard2 = self.recursion_check();
match expr.data {
Literal(lit) => literal(lit, expr.location),
Id(id) => self.parse_id(id, expr.location),
Cast(ctype, inner) => {
let ctype = self.parse_typename(ctype, expr.location);
self.explicit_cast(*inner, ctype)
}
Shift(left, right, direction) => {
let op = if direction {
BinaryOp::Shl
} else {
BinaryOp::Shr
};
self.binary_helper(left, right, op, Self::parse_integer_op)
}
BitwiseAnd(left, right) => {
self.binary_helper(left, right, BinaryOp::BitwiseAnd, Self::parse_integer_op)
}
BitwiseOr(left, right) => {
self.binary_helper(left, right, BinaryOp::BitwiseOr, Self::parse_integer_op)
}
Xor(left, right) => {
self.binary_helper(left, right, BinaryOp::Xor, Self::parse_integer_op)
}
Compare(left, right, token) => self.relational_expr(*left, *right, token),
Mul(left, right) => self.binary_helper(left, right, BinaryOp::Mul, Self::mul),
Div(left, right) => self.binary_helper(left, right, BinaryOp::Div, Self::mul),
Mod(left, right) => self.binary_helper(left, right, BinaryOp::Mod, Self::mul),
Assign(lval, rval, token) => {
let lval = self.expr(*lval);
let rval = self.expr(*rval);
self.assignment_expr(lval, rval, token, expr.location)
}
Add(left, right) => self.binary_helper(left, right, BinaryOp::Add, Self::add),
Sub(left, right) => self.binary_helper(left, right, BinaryOp::Sub, Self::add),
FuncCall(func, args) => self.func_call(*func, args),
Member(struct_, id) => {
let struct_ = self.expr(*struct_);
self.struct_member(struct_, id, expr.location)
}
DerefMember(inner, id) => {
let inner = self.expr(*inner);
let struct_type = match &inner.ctype {
Type::Pointer(ctype, _) => match &**ctype {
Type::Union(_) | Type::Struct(_) => (**ctype).clone(),
other => {
self.err(SemanticError::NotAStruct(other.clone()), inner.location);
return inner;
}
},
other => {
self.err(SemanticError::NotAPointer(other.clone()), inner.location);
return inner;
}
};
let deref = inner.indirection(false, struct_type);
self.struct_member(deref, id, expr.location)
}
Deref(inner) => {
let inner = self.expr(*inner);
match &inner.ctype {
Type::Array(t, _) | Type::Pointer(t, _) => {
let ctype = (**t).clone();
inner.indirection(true, ctype)
}
_ => {
self.err(
SemanticError::NotAPointer(inner.ctype.clone()),
expr.location,
);
inner
}
}
}
AddressOf(inner) => {
let inner = self.expr(*inner);
match inner.expr {
ExprType::Deref(double_inner) => *double_inner,
ExprType::Id(ref sym) if sym.get().storage_class == StorageClass::Register => {
self.err(
SemanticError::InvalidAddressOf("variable declared with `register`"),
expr.location,
);
inner
}
_ if inner.lval => Expr {
lval: false,
location: expr.location,
ctype: Type::Pointer(Box::new(inner.ctype.clone()), Qualifiers::default()),
expr: inner.expr,
},
_ => {
self.err(SemanticError::InvalidAddressOf("value"), expr.location);
inner
}
}
}
PreIncrement(inner, increment) => {
self.increment_op(true, increment, *inner, expr.location)
}
PostIncrement(inner, increment) => {
self.increment_op(false, increment, *inner, expr.location)
}
Index(left, right) => self.index(*left, *right, expr.location),
AlignofType(type_name) => {
let ctype = self.parse_typename(type_name, expr.location);
self.align(ctype, expr.location)
}
AlignofExpr(inner) => {
let inner = self.expr(*inner);
self.align(inner.ctype, expr.location)
}
SizeofType(type_name) => {
let ctype = self.parse_typename(type_name, expr.location);
self.sizeof(ctype, expr.location)
}
SizeofExpr(inner) => {
let inner = self.expr(*inner);
self.sizeof(inner.ctype, expr.location)
}
BitwiseNot(inner) => self.bitwise_not(*inner),
UnaryPlus(inner) => self.unary_add(*inner, true, expr.location),
Negate(inner) => self.unary_add(*inner, false, expr.location),
LogicalNot(inner) => self.logical_not(*inner),
LogicalAnd(left, right) => {
self.binary_helper(left, right, BinaryOp::LogicalAnd, Self::logical_bin_op)
}
LogicalOr(left, right) => {
self.binary_helper(left, right, BinaryOp::LogicalOr, Self::logical_bin_op)
}
Comma(left, right) => {
let left = self.expr(*left);
let right = self.expr(*right).rval();
Expr {
ctype: right.ctype.clone(),
lval: false,
expr: ExprType::Comma(Box::new(left), Box::new(right)),
location: expr.location,
}
}
Ternary(condition, then, otherwise) => {
self.ternary(*condition, *then, *otherwise, expr.location)
}
}
}
#[allow(clippy::boxed_local)]
fn binary_helper<F>(
&mut self,
left: Box<ast::Expr>,
right: Box<ast::Expr>,
op: BinaryOp,
expr_checker: F,
) -> Expr
where
F: FnOnce(&mut Self, Expr, Expr, BinaryOp) -> Expr,
{
let left = self.expr(*left);
let right = self.expr(*right);
expr_checker(self, left, right, op)
}
fn parse_integer_op(&mut self, left: Expr, right: Expr, op: BinaryOp) -> Expr {
let non_scalar = if !left.ctype.is_integral() {
Some(&left.ctype)
} else if !right.ctype.is_integral() {
Some(&right.ctype)
} else {
None
};
let location = left.location.merge(right.location);
if let Some(ctype) = non_scalar {
self.err(SemanticError::NonIntegralExpr(ctype.clone()), location);
}
let (promoted_expr, next) = Expr::binary_promote(left, right, &mut self.error_handler);
Expr {
ctype: next.ctype.clone(),
expr: ExprType::Binary(op, Box::new(promoted_expr), Box::new(next)),
lval: false,
location,
}
}
fn parse_id(&mut self, name: InternedStr, location: Location) -> Expr {
let mut pretend_zero = Expr::zero(location);
pretend_zero.ctype = Type::Error;
match self.scope.get(&name) {
None => {
self.err(SemanticError::UndeclaredVar(name), location);
pretend_zero
}
Some(&symbol) => {
let meta = symbol.get();
if meta.storage_class == StorageClass::Typedef {
self.err(SemanticError::TypedefInExpressionContext, location);
return pretend_zero;
}
if let Type::Enum(ident, members) = &meta.ctype {
let mapper = |(member, value): &(InternedStr, i64)| {
if name == *member {
Some(*value)
} else {
None
}
};
let enumerator = members.iter().find_map(mapper);
if let Some(e) = enumerator {
return Expr {
ctype: Type::Enum(*ident, members.clone()),
location,
lval: false,
expr: ExprType::Literal(Literal::Int(e)),
};
}
}
Expr::id(symbol, location)
}
}
}
fn relational_expr(
&mut self,
left: ast::Expr,
right: ast::Expr,
token: ComparisonToken,
) -> Expr {
let location = left.location.merge(right.location);
let mut left = self.expr(left);
let mut right = self.expr(right);
if left.ctype.is_arithmetic() && right.ctype.is_arithmetic() {
let tmp = Expr::binary_promote(left, right, &mut self.error_handler);
left = tmp.0;
right = tmp.1;
} else {
let (left_expr, right_expr) = (left.rval(), right.rval());
if !((left_expr.ctype.is_pointer() && left_expr.ctype == right_expr.ctype)
|| ((token == ComparisonToken::EqualEqual || token == ComparisonToken::NotEqual)
&& ((left_expr.ctype.is_pointer() && right_expr.ctype.is_void_pointer())
|| (left_expr.ctype.is_void_pointer() && right_expr.ctype.is_pointer())
|| (left_expr.is_null() && right_expr.ctype.is_pointer())
|| (left_expr.ctype.is_pointer() && right_expr.is_null()))))
{
self.err(
SemanticError::InvalidRelationalType(
token,
left_expr.ctype.clone(),
right_expr.ctype.clone(),
),
location,
);
}
left = left_expr;
right = right_expr;
}
assert!(!left.lval && !right.lval);
Expr {
lval: false,
location,
ctype: Type::Bool,
expr: ExprType::Binary(BinaryOp::Compare(token), Box::new(left), Box::new(right)),
}
}
fn mul(&mut self, left: Expr, right: Expr, op: BinaryOp) -> Expr {
let location = left.location.merge(right.location);
if op == BinaryOp::Mod && !(left.ctype.is_integral() && right.ctype.is_integral()) {
self.err(
SemanticError::from(format!(
"expected integers for both operators of %, got '{}' and '{}'",
left.ctype, right.ctype
)),
location,
);
} else if !(left.ctype.is_arithmetic() && right.ctype.is_arithmetic()) {
self.err(
SemanticError::from(format!(
"expected float or integer types for both operands of {}, got '{}' and '{}'",
op, left.ctype, right.ctype
)),
location,
);
}
let (left, right) = Expr::binary_promote(left, right, &mut self.error_handler);
Expr {
ctype: left.ctype.clone(),
location,
lval: false,
expr: ExprType::Binary(op, Box::new(left), Box::new(right)),
}
}
fn add(&mut self, mut left: Expr, mut right: Expr, op: BinaryOp) -> Expr {
let is_add = op == BinaryOp::Add;
let location = left.location.merge(right.location);
match (&left.ctype, &right.ctype) {
(Type::Pointer(to, _), i)
| (Type::Array(to, _), i) if i.is_integral() && to.is_complete() => {
let to = to.clone();
let (left, right) = (left.rval(), right.rval());
return self.pointer_arithmetic(left, right, &*to, location);
}
(i, Type::Pointer(to, _))
| (i, Type::Array(to, _)) if i.is_integral() && is_add && to.is_complete() => {
let to = to.clone();
let (left, right) = (left.rval(), right.rval());
return self.pointer_arithmetic(right, left, &*to, location);
}
_ => {}
};
let (ctype, lval) = if left.ctype.is_arithmetic() && right.ctype.is_arithmetic() {
let tmp = Expr::binary_promote(left, right, &mut self.error_handler);
left = tmp.0;
right = tmp.1;
(left.ctype.clone(), false)
} else if !is_add && left.ctype.is_pointer_to_complete_object() && left.ctype == right.ctype
{
(left.ctype.clone(), true)
} else {
self.err(
SemanticError::InvalidAdd(op, left.ctype.clone(), right.ctype.clone()),
location,
);
(left.ctype.clone(), false)
};
Expr {
ctype,
lval,
location,
expr: ExprType::Binary(op, Box::new(left), Box::new(right)),
}
}
fn explicit_cast(&mut self, expr: ast::Expr, ctype: Type) -> Expr {
let location = expr.location;
let expr = self.expr(expr).rval();
if ctype == Type::Void {
return Expr {
lval: false,
ctype,
expr: ExprType::Cast(Box::new(expr)),
location,
};
}
if !ctype.is_scalar() {
self.err(SemanticError::NonScalarCast(ctype.clone()), location);
} else if expr.ctype.is_floating() && ctype.is_pointer()
|| expr.ctype.is_pointer() && ctype.is_floating()
{
self.err(SemanticError::FloatPointerCast(ctype.clone()), location);
} else if expr.ctype.is_struct() {
self.err(SemanticError::StructCast, location);
} else if expr.ctype == Type::Void {
self.err(SemanticError::VoidCast, location);
}
Expr {
lval: false,
expr: ExprType::Cast(Box::new(expr)),
ctype,
location,
}
}
fn pointer_arithmetic(
&mut self,
base: Expr,
index: Expr,
pointee: &Type,
location: Location,
) -> Expr {
let offset = Expr {
lval: false,
location: index.location,
expr: ExprType::Cast(Box::new(index)),
ctype: base.ctype.clone(),
}
.rval();
let size = match pointee.sizeof() {
Ok(s) => s,
Err(_) => {
self.err(
SemanticError::PointerAddUnknownSize(base.ctype.clone()),
location,
);
1
}
};
let size_literal = literal(Literal::UnsignedInt(size), offset.location);
let size_cast = Expr {
lval: false,
location: offset.location,
ctype: offset.ctype.clone(),
expr: ExprType::Cast(Box::new(size_literal)),
};
let offset = Expr {
lval: false,
location: offset.location,
ctype: offset.ctype.clone(),
expr: ExprType::Binary(BinaryOp::Mul, Box::new(size_cast), Box::new(offset)),
};
Expr {
lval: false,
location,
ctype: base.ctype.clone(),
expr: ExprType::Binary(BinaryOp::Add, Box::new(base), Box::new(offset)),
}
}
fn func_call(&mut self, func: ast::Expr, args: Vec<ast::Expr>) -> Expr {
let mut func = self.expr(func);
match &func.ctype {
Type::Pointer(pointee, _) if pointee.is_function() => {
func = Expr {
lval: false,
location: func.location,
ctype: (**pointee).clone(),
expr: ExprType::Deref(Box::new(func)),
}
}
_ => {}
};
let functype = match &func.ctype {
Type::Function(functype) => functype,
Type::Error => return func,
other => {
self.err(SemanticError::NotAFunction(other.clone()), func.location);
return func;
}
};
let mut expected = functype.params.len();
if expected == 1 && functype.params[0].get().ctype == Type::Void {
expected = 0;
}
if !functype.params.is_empty()
&& (args.len() < expected || args.len() > expected && !functype.varargs)
{
self.err(
SemanticError::WrongArgumentNumber(args.len(), expected),
func.location,
);
}
let mut promoted_args = vec![];
for (i, arg) in args.into_iter().enumerate() {
let arg = self.expr(arg);
let promoted = match functype.params.get(i) {
Some(expected) => arg
.rval()
.implicit_cast(&expected.get().ctype, &mut self.error_handler),
None => self.default_promote(arg),
};
promoted_args.push(promoted);
}
Expr {
location: func.location,
lval: false,
ctype: *functype.return_type.clone(),
expr: ExprType::FuncCall(Box::new(func), promoted_args),
}
}
fn default_promote(&mut self, expr: Expr) -> Expr {
let expr = expr.rval();
let ctype = expr.ctype.clone().default_promote();
expr.implicit_cast(&ctype, &mut self.error_handler)
}
fn struct_member(&mut self, expr: Expr, id: InternedStr, location: Location) -> Expr {
match &expr.ctype {
Type::Struct(stype) | Type::Union(stype) => {
let members = stype.members();
if members.is_empty() {
self.err(
SemanticError::IncompleteDefinitionUsed(expr.ctype.clone()),
location,
);
expr
} else if let Some(member) = members.iter().find(|member| member.id == id) {
Expr {
ctype: member.ctype.clone(),
lval: true,
location,
expr: ExprType::Member(Box::new(expr), id),
}
} else {
self.err(SemanticError::NotAMember(id, expr.ctype.clone()), location);
expr
}
}
_ => {
self.err(SemanticError::NotAStruct(expr.ctype.clone()), location);
expr
}
}
}
fn increment_op(
&mut self,
prefix: bool,
increment: bool,
expr: ast::Expr,
location: Location,
) -> Expr {
use crate::data::lex::AssignmentToken;
let expr = self.expr(expr);
if let Err(err) = expr.modifiable_lval() {
self.err(err, location);
} else if !(expr.ctype.is_arithmetic() || expr.ctype.is_pointer()) {
self.err(
SemanticError::InvalidIncrement(expr.ctype.clone()),
expr.location,
);
}
if prefix {
let rval = Expr {
lval: false,
ctype: expr.ctype.clone(),
location,
expr: ExprType::Cast(Box::new(literal(Literal::Int(1), location))),
};
let op = if increment {
AssignmentToken::AddEqual
} else {
AssignmentToken::SubEqual
};
self.assignment_expr(expr, rval, op, location)
} else {
Expr {
lval: false,
ctype: expr.ctype.clone(),
expr: ExprType::PostIncrement(Box::new(expr), increment),
location,
}
}
}
fn index(&mut self, left: ast::Expr, right: ast::Expr, location: Location) -> Expr {
let left = self.expr(left).rval();
let right = self.expr(right).rval();
let (target_type, array, index) = match (&left.ctype, &right.ctype) {
(Type::Pointer(target, _), _) => ((**target).clone(), left, right),
(_, Type::Pointer(target, _)) => ((**target).clone(), right, left),
(l, _) => {
self.err(SemanticError::NotAPointer(l.clone()), location);
return left;
}
};
let mut addr = self.pointer_arithmetic(array, index, &target_type, location);
addr.ctype = target_type;
addr.lval = true;
addr
}
fn align(&mut self, ctype: Type, location: Location) -> Expr {
let align = ctype.alignof().unwrap_or_else(|err| {
self.err(err.into(), location);
1
});
literal(Literal::UnsignedInt(align), location)
}
fn sizeof(&mut self, ctype: Type, location: Location) -> Expr {
let align = ctype.sizeof().unwrap_or_else(|err| {
self.err(err.into(), location);
1
});
literal(Literal::UnsignedInt(align), location)
}
fn bitwise_not(&mut self, expr: ast::Expr) -> Expr {
let expr = self.expr(expr);
if !expr.ctype.is_integral() {
self.err(
SemanticError::NonIntegralExpr(expr.ctype.clone()),
expr.location,
);
expr
} else {
let expr = expr.integer_promote(&mut self.error_handler);
Expr {
lval: false,
ctype: expr.ctype.clone(),
location: expr.location,
expr: ExprType::BitwiseNot(Box::new(expr)),
}
}
}
fn unary_add(&mut self, expr: ast::Expr, add: bool, location: Location) -> Expr {
let expr = self.expr(expr);
if !expr.ctype.is_arithmetic() {
self.err(SemanticError::NotArithmetic(expr.ctype.clone()), location);
return expr;
}
let expr = expr.integer_promote(&mut self.error_handler);
if add {
Expr {
lval: false,
location,
..expr
}
} else {
Expr {
lval: false,
ctype: expr.ctype.clone(),
location,
expr: ExprType::Negate(Box::new(expr)),
}
}
}
fn logical_not(&mut self, expr: ast::Expr) -> Expr {
let expr = self.expr(expr);
let boolean = expr.truthy(&mut self.error_handler);
debug_assert_eq!(boolean.ctype, Type::Bool);
let zero = Expr::zero(boolean.location).implicit_cast(&Type::Bool, &mut self.error_handler);
Expr {
lval: false,
location: boolean.location,
ctype: Type::Bool,
expr: ExprType::Binary(
BinaryOp::Compare(ComparisonToken::EqualEqual),
Box::new(boolean),
Box::new(zero),
),
}
}
fn logical_bin_op(&mut self, a: Expr, b: Expr, op: BinaryOp) -> Expr {
let a = a.implicit_cast(&Type::Bool, &mut self.error_handler);
let b = b.implicit_cast(&Type::Bool, &mut self.error_handler);
Expr {
lval: false,
ctype: Type::Bool,
location: a.location,
expr: ExprType::Binary(op, Box::new(a), Box::new(b)),
}
}
fn ternary(
&mut self,
condition: ast::Expr,
then: ast::Expr,
otherwise: ast::Expr,
location: Location,
) -> Expr {
let condition = self.expr(condition).truthy(&mut self.error_handler);
let mut then = self.expr(then).rval();
let mut otherwise = self.expr(otherwise).rval();
if then.ctype.is_arithmetic() && otherwise.ctype.is_arithmetic() {
let (tmp1, tmp2) = Expr::binary_promote(then, otherwise, &mut self.error_handler);
then = tmp1;
otherwise = tmp2;
} else if !pointer_promote(&mut then, &mut otherwise) {
self.err(
SemanticError::IncompatibleTypes(then.ctype.clone(), otherwise.ctype.clone()),
location,
);
}
Expr {
ctype: then.ctype.clone(),
lval: false,
location,
expr: ExprType::Ternary(Box::new(condition), Box::new(then), Box::new(otherwise)),
}
}
fn assignment_expr(
&mut self,
lval: Expr,
rval: Expr,
token: lex::AssignmentToken,
location: Location,
) -> Expr {
if let Err(err) = lval.modifiable_lval() {
self.err(err, location);
}
if let lex::AssignmentToken::Equal = token {
let mut rval = rval.rval();
if rval.ctype != lval.ctype {
rval = rval.implicit_cast(&lval.ctype, &mut self.error_handler);
}
return Expr {
ctype: lval.ctype.clone(),
lval: false,
location,
expr: ExprType::Binary(BinaryOp::Assign, Box::new(lval), Box::new(rval)),
};
}
self.scope.enter();
let tmp_name = "tmp".into();
let ctype = lval.ctype.clone();
let ptr_type = Type::Pointer(Box::new(ctype.clone()), Qualifiers::default());
let meta = Variable {
id: tmp_name,
ctype: ptr_type.clone(),
qualifiers: Qualifiers::NONE,
storage_class: StorageClass::Register,
};
let tmp_var = self.declare(meta, true, location);
let init = Some(Initializer::Scalar(Box::new(lval)));
let decl = Declaration {
symbol: tmp_var,
init,
};
self.decl_side_channel.push(Locatable::new(decl, location));
self.scope.exit();
let tmp = Expr {
expr: ExprType::Id(tmp_var),
ctype: ptr_type,
lval: true,
location,
}
.rval();
let lval_as_rval = Expr {
ctype: ctype.clone(),
lval: false,
location,
expr: ExprType::Deref(Box::new(tmp.clone())),
};
let target = tmp.indirection(true, ctype.clone());
let new_val = self
.desugar_op(lval_as_rval, rval.rval(), token)
.implicit_cast(&target.ctype, &mut self.error_handler);
Expr {
ctype,
lval: false,
location,
expr: ExprType::Binary(BinaryOp::Assign, Box::new(target), Box::new(new_val)),
}
}
fn desugar_op(&mut self, left: Expr, right: Expr, token: lex::AssignmentToken) -> Expr {
use lex::AssignmentToken::*;
match token {
Equal => unreachable!(),
OrEqual => self.parse_integer_op(left, right, BinaryOp::BitwiseOr),
AndEqual => self.parse_integer_op(left, right, BinaryOp::BitwiseAnd),
XorEqual => self.parse_integer_op(left, right, BinaryOp::Xor),
ShlEqual => self.parse_integer_op(left, right, BinaryOp::Shl),
ShrEqual => self.parse_integer_op(left, right, BinaryOp::Shr),
MulEqual => self.mul(left, right, BinaryOp::Mul),
DivEqual => self.mul(left, right, BinaryOp::Div),
ModEqual => self.mul(left, right, BinaryOp::Mod),
AddEqual => self.add(left, right, BinaryOp::Add),
SubEqual => self.add(left, right, BinaryOp::Sub),
}
}
}
pub(super) fn literal(literal: Literal, location: Location) -> Expr {
use crate::data::types::ArrayType;
let ctype = match &literal {
Literal::Char(_) => Type::Char(true),
Literal::Int(_) => Type::Long(true),
Literal::UnsignedInt(_) => Type::Long(false),
Literal::Float(_) => Type::Double,
Literal::Str(s) => {
let len = s.len() as arch::SIZE_T;
Type::Array(Box::new(Type::Char(true)), ArrayType::Fixed(len))
}
};
Expr {
lval: false,
ctype,
location,
expr: ExprType::Literal(literal),
}
}
fn pointer_promote(left: &mut Expr, right: &mut Expr) -> bool {
let is_convertible_to_any_pointer = |expr: &Expr| {
expr.ctype.is_void_pointer() || expr.ctype.is_char_pointer() || expr.is_null()
};
if left.ctype == right.ctype {
true
} else if is_convertible_to_any_pointer(left) && right.ctype.is_pointer() {
left.ctype = right.ctype.clone();
true
} else if is_convertible_to_any_pointer(right) && left.ctype.is_pointer() {
right.ctype = left.ctype.clone();
true
} else {
false
}
}
impl Type {
#[inline]
fn is_void_pointer(&self) -> bool {
match self {
Type::Pointer(t, _) => **t == Type::Void,
_ => false,
}
}
#[inline]
fn is_char_pointer(&self) -> bool {
match self {
Type::Pointer(t, _) => match **t {
Type::Char(_) => true,
_ => false,
},
_ => false,
}
}
#[inline]
fn is_pointer_to_complete_object(&self) -> bool {
match self {
Type::Pointer(ctype, _) => ctype.is_complete() && !ctype.is_function(),
Type::Array(_, _) => true,
_ => false,
}
}
fn sign(&self) -> Result<bool, ()> {
use Type::*;
match self {
Char(sign) | Short(sign) | Int(sign) | Long(sign) => Ok(*sign),
Bool => Ok(false),
Enum(_, _) => Ok(true),
_ => Err(()),
}
}
fn rank(&self) -> usize {
use Type::*;
match self {
Bool => 0,
Char(_) => 1,
Short(_) => 2,
Int(_) => 3,
Long(_) => 4,
_ => std::usize::MAX,
}
}
fn integer_promote(self) -> Type {
if self.rank() <= Type::Int(true).rank() {
if Type::Int(true).can_represent(&self) {
Type::Int(true)
} else {
Type::Int(false)
}
} else {
self
}
}
fn binary_promote(mut left: Type, mut right: Type) -> Result<Type, Type> {
use Type::*;
if left == Double || right == Double {
return Ok(Double);
} else if left == Float || right == Float {
return Ok(Float);
}
left = left.integer_promote();
right = right.integer_promote();
let signs = (
left.sign().map_err(|_| left.clone())?,
right.sign().map_err(|_| right.clone())?,
);
if signs.0 == signs.1 {
return Ok(if left.rank() >= right.rank() {
left
} else {
right
});
};
let (signed, unsigned) = if signs.0 {
(left, right)
} else {
(right, left)
};
if signed.can_represent(&unsigned) {
Ok(signed)
} else {
Ok(unsigned)
}
}
fn default_promote(self) -> Type {
if self.is_integral() {
self.integer_promote()
} else if self == Type::Float {
Type::Double
} else {
self
}
}
fn is_struct(&self) -> bool {
match self {
Type::Struct(_) | Type::Union(_) => true,
_ => false,
}
}
fn is_complete(&self) -> bool {
match self {
Type::Void | Type::Function(_) | Type::Array(_, types::ArrayType::Unbounded) => false,
_ => true,
}
}
}
impl Expr {
pub(super) fn zero(location: Location) -> Expr {
Expr {
ctype: Type::Int(true),
expr: ExprType::Literal(Literal::Int(0)),
lval: false,
location,
}
}
fn is_null(&self) -> bool {
if let ExprType::Literal(token) = &self.expr {
match token {
Literal::Int(0) | Literal::UnsignedInt(0) | Literal::Char(0) => true,
_ => false,
}
} else {
false
}
}
fn id(symbol: Symbol, location: Location) -> Self {
Self {
expr: ExprType::Id(symbol),
ctype: symbol.get().ctype.clone(),
lval: true,
location,
}
}
pub(crate) fn truthy(mut self, error_handler: &mut ErrorHandler) -> Expr {
self = self.rval();
if self.ctype == Type::Bool {
return self;
}
if !self.ctype.is_scalar() {
error_handler.error(
SemanticError::Generic(format!(
"expression of type '{}' cannot be converted to bool",
self.ctype
)),
self.location,
);
self.ctype = Type::Error;
}
let zero = Expr::zero(self.location).implicit_cast(&self.ctype, error_handler);
Expr {
lval: false,
location: self.location,
ctype: Type::Bool,
expr: ExprType::Binary(
BinaryOp::Compare(ComparisonToken::NotEqual),
Box::new(self),
Box::new(zero),
),
}
}
fn integer_promote(self, error_handler: &mut ErrorHandler) -> Expr {
let expr = self.rval();
let ctype = expr.ctype.clone().integer_promote();
expr.implicit_cast(&ctype, error_handler)
}
fn binary_promote(left: Expr, right: Expr, error_handler: &mut ErrorHandler) -> (Expr, Expr) {
let (left, right) = (left.rval(), right.rval());
let ctype = Type::binary_promote(left.ctype.clone(), right.ctype.clone());
match ctype {
Ok(promoted) => (
left.implicit_cast(&promoted, error_handler),
right.implicit_cast(&promoted, error_handler),
),
Err(non_int) => {
error_handler.error(SemanticError::NonIntegralExpr(non_int), right.location);
(left, right)
}
}
}
pub(super) fn rval(self) -> Expr {
match self.ctype {
Type::Array(to, _) => Expr {
lval: false,
ctype: Type::Pointer(to, Qualifiers::default()),
..self
},
Type::Function(_) => Expr {
lval: false,
ctype: Type::Pointer(
Box::new(self.ctype),
Qualifiers {
c_const: true,
..Qualifiers::default()
},
),
..self
},
Type::Struct(_) | Type::Union(_) if self.lval => Expr {
lval: false,
..self
},
_ if self.lval => Expr {
ctype: self.ctype.clone(),
lval: false,
location: self.location,
expr: ExprType::Deref(Box::new(self)),
},
_ => self,
}
}
fn indirection(self, lval: bool, ctype: Type) -> Self {
Expr {
location: self.location,
ctype,
lval,
expr: ExprType::Noop(Box::new(self.rval())),
}
}
pub(super) fn implicit_cast(self, ctype: &Type, error_handler: &mut ErrorHandler) -> Expr {
let mut expr = self.rval();
if &expr.ctype == ctype {
expr
} else if expr.ctype.is_arithmetic() && ctype.is_arithmetic()
|| expr.is_null() && ctype.is_pointer()
|| expr.ctype.is_pointer() && ctype.is_bool()
|| expr.ctype.is_pointer() && ctype.is_void_pointer()
|| expr.ctype.is_pointer() && ctype.is_char_pointer()
{
Expr {
location: expr.location,
expr: ExprType::Cast(Box::new(expr)),
lval: false,
ctype: ctype.clone(),
}
} else if ctype.is_pointer()
&& (expr.is_null() || expr.ctype.is_void_pointer() || expr.ctype.is_char_pointer())
{
expr.ctype = ctype.clone();
expr
} else if expr.ctype == Type::Error {
expr
} else {
if let (Type::Pointer(a, from), Type::Pointer(b, to)) = (&expr.ctype, ctype) {
if *a == *b && from.contains_all(*to) {
expr.ctype = ctype.clone();
return expr;
}
}
error_handler.error(
SemanticError::InvalidCast(expr.ctype.clone(), ctype.clone()),
expr.location,
);
expr
}
}
fn modifiable_lval(&self) -> Result<(), SemanticError> {
let err = |e| Err(SemanticError::NotAssignable(e));
if !self.lval {
return err("rvalue".to_string());
}
if !self.ctype.is_complete() {
return err(format!("expression with incomplete type '{}'", self.ctype));
}
if let ExprType::Id(sym) = &self.expr {
let meta = sym.get();
if meta.qualifiers.c_const {
return err(format!("variable '{}' with `const` qualifier", meta.id));
}
}
match &self.ctype {
Type::Array(_, _) => err("array".to_string()),
Type::Struct(stype) | Type::Union(stype) => {
if stype
.members()
.iter()
.map(|sym| sym.qualifiers.c_const)
.any(|x| x)
{
err("struct or union with `const` qualified member".to_string())
} else {
Ok(())
}
}
_ => Ok(()),
}
}
}
impl Qualifiers {
fn contains_all(self, other: Self) -> bool {
(self.c_const || !other.c_const) && (self.volatile || !other.volatile)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::analyze::test::analyze;
use crate::analyze::*;
pub(crate) fn expr(input: &str) -> CompileResult<Expr> {
analyze(input, Parser::expr, PureAnalyzer::expr)
}
fn get_location(r: &CompileResult<Expr>) -> Location {
match r {
Ok(expr) => expr.location,
Err(err) => err.location(),
}
}
fn assert_literal(token: Literal) {
let parsed = expr(&token.to_string());
let location = get_location(&parsed);
assert_eq!(parsed.unwrap(), literal(token, location));
}
fn expr_with_scope<'a>(input: &'a str, variables: &[Symbol]) -> CompileResult<Expr> {
analyze(input, Parser::expr, |a, expr| {
for &meta in variables {
let id = meta.get().id;
a.scope.insert(id, meta);
}
a.expr(expr)
})
}
fn assert_type(input: &str, ctype: Type) {
match expr(input) {
Ok(expr) => assert_eq!(expr.ctype, ctype),
Err(err) => panic!("error: {}", err.data),
};
}
#[test]
fn test_primaries() {
assert_literal(Literal::Int(141));
let parsed = expr("\"hi there\"");
assert_eq!(
parsed,
Ok(literal(
Literal::Str("hi there\0".into()),
get_location(&parsed)
)),
);
assert_literal(Literal::Float(1.5));
let parsed = expr("(1)");
assert_eq!(parsed, Ok(literal(Literal::Int(1), get_location(&parsed))));
let x = Variable {
ctype: Type::Int(true),
id: InternedStr::get_or_intern("x"),
qualifiers: Default::default(),
storage_class: Default::default(),
}
.insert();
let parsed = expr_with_scope("x", &[x]);
assert_eq!(
parsed,
Ok(Expr {
location: get_location(&parsed),
ctype: Type::Int(true),
lval: true,
expr: ExprType::Id(x)
})
);
}
#[test]
fn test_mul() {
assert_type("1*1.0", Type::Double);
assert_type("1*2.0 / 1.3", Type::Double);
assert_type("3%2", Type::Long(true));
}
#[test]
fn test_funcall() {
let f = Variable {
id: InternedStr::get_or_intern("f"),
qualifiers: Default::default(),
storage_class: Default::default(),
ctype: Type::Function(types::FunctionType {
params: vec![Variable {
ctype: Type::Void,
id: Default::default(),
qualifiers: Default::default(),
storage_class: StorageClass::Auto,
}
.insert()],
return_type: Box::new(Type::Int(true)),
varargs: false,
}),
}
.insert();
assert!(expr_with_scope("f(1,2,3)", &[f]).is_err());
let parsed = expr_with_scope("f()", &[f]);
assert!(match parsed {
Ok(Expr {
expr: ExprType::FuncCall(_, _),
..
}) => true,
_ => false,
},);
}
#[test]
fn test_type_errors() {
assert!(expr("1 % 2.0").is_err());
assert!(expr("0 ? \"error message\" : 0.0").is_err());
}
#[test]
fn test_explicit_casts() {
assert_type("(int)4.2", Type::Int(true));
assert_type("(unsigned int)4.2", Type::Int(false));
assert_type("(float)4.2", Type::Float);
assert_type("(double)4.2", Type::Double);
assert!(expr("(int*)4.2").is_err());
assert_type(
"(int*)(int)4.2",
Type::Pointer(Box::new(Type::Int(true)), Qualifiers::default()),
);
}
}