use rucc_ast::Ast;
use rucc_base::{Interner, Symbol};
use rucc_diag::{DEFAULT_ERROR_LIMIT, Diagnostic, Errors, Span};
use rucc_session::Std;
use rucc_target::TargetInfo;
use rucc_types::{ArrayLen, IntKind, TypeId, TypeKind, Types, int_width};
use crate::convert::Conv;
use crate::decl::{Decl, DeclId, DeclKind, DeclList, Definition, Linkage, StorageDuration};
use crate::eval::{Eval, NotConstant};
use crate::expr::{Category, Expr, ExprId, ExprKind};
use crate::scope::Scopes;
use crate::tast::{Const, Tast};
mod decl;
mod expr;
mod init;
mod stmt;
mod ty;
#[derive(Debug, Clone, Copy)]
pub struct Context<'a> {
pub names: &'a Interner,
pub target: &'a TargetInfo,
pub std: Std,
pub gnu: bool,
pub pedantic: bool,
pub error_limit: usize,
}
impl<'a> Context<'a> {
#[must_use]
pub fn new(names: &'a Interner, target: &'a TargetInfo, std: Std) -> Context<'a> {
Context { names, target, std, gnu: true, pedantic: false, error_limit: DEFAULT_ERROR_LIMIT }
}
}
#[derive(Debug)]
pub struct Checked {
pub tast: Tast,
pub types: Types,
pub diagnostics: Vec<Diagnostic>,
}
impl Checked {
#[must_use]
pub fn failed(&self) -> bool {
self.diagnostics.iter().any(|d| d.severity.is_fatal())
}
}
#[derive(Debug)]
pub struct Checker<'a> {
pub(crate) ast: &'a Ast,
pub(crate) tast: Tast,
pub(crate) types: Types,
pub(crate) scopes: Scopes,
pub(crate) errors: Errors,
pub(crate) cx: Context<'a>,
pub(crate) built: ty::Built,
pub(in crate::check) body: Option<stmt::Body>,
pub(in crate::check) underspecified: Vec<DeclId>,
}
impl<'a> Checker<'a> {
#[must_use]
pub fn new(ast: &'a Ast, cx: Context<'a>) -> Checker<'a> {
Checker {
ast,
tast: Tast::new(),
types: Types::new(),
scopes: Scopes::new(),
errors: Errors::new(cx.error_limit),
cx,
built: ty::Built::default(),
body: None,
underspecified: Vec::new(),
}
}
pub fn check_unit(&mut self) {
let ast = self.ast;
for &decl in ast.top_level() {
self.check_decl(decl);
}
}
pub fn check_expr(&mut self, id: rucc_ast::ExprId) -> ExprId {
self.expr(id)
}
pub fn eval_constant(&mut self, expr: ExprId) -> Result<Const, NotConstant> {
let mut eval = self.eval();
let value = eval.constant(expr);
self.absorb(eval.finish());
value
}
pub fn eval_integer(&mut self, expr: ExprId) -> Result<i128, NotConstant> {
let mut eval = self.eval();
let value = eval.integer(expr);
self.absorb(eval.finish());
value
}
#[must_use]
pub fn finish(self) -> Checked {
Checked { tast: self.tast, types: self.types, diagnostics: self.errors.finish() }
}
pub fn declare_object(&mut self, name: Symbol, ty: TypeId, span: Span) -> DeclId {
let kind = if rucc_types::is_function(&self.types, ty) {
DeclKind::Function
} else {
DeclKind::Object
};
let decl = self.tast.decl(
Decl {
name: Some(name),
ty,
kind,
linkage: Linkage::None,
duration: StorageDuration::Automatic,
state: Definition::Defined,
alignment: None,
init: None,
params: DeclList::EMPTY,
body: None,
},
span,
);
self.scopes.declare(name, crate::scope::Binding::Decl(decl));
decl
}
pub(crate) fn conv(&mut self) -> Conv<'_> {
let target = self.cx.target;
Conv { tast: &mut self.tast, types: &mut self.types, target }
}
pub(crate) fn eval(&self) -> Eval<'_> {
Eval::new(&self.tast, &self.types, self.cx.target, self.cx.names)
}
pub(crate) fn report(&mut self, diagnostic: Diagnostic) {
self.errors.push(diagnostic);
}
pub(crate) fn absorb(&mut self, diagnostics: Vec<Diagnostic>) {
for diagnostic in diagnostics {
self.errors.push(diagnostic);
}
}
pub(crate) fn is_poisoned(&self, id: ExprId) -> bool {
matches!(self.tast[id].kind, ExprKind::Error)
}
pub(crate) fn poison(&mut self, span: Span) -> ExprId {
let int = self.types.int(IntKind::Int);
self.tast.expr(Expr::new(ExprKind::Error, int, Category::Rvalue), span)
}
pub(crate) fn spell(&self, ty: TypeId) -> String {
rucc_types::spell(&self.types, self.cx.names, ty)
}
pub(crate) fn text(&self, name: Symbol) -> &str {
self.cx.names.resolve(name)
}
pub(crate) fn int(&self) -> TypeId {
self.types.int(IntKind::Int)
}
pub(crate) fn size_type(&self) -> TypeId {
let width = self.cx.target.pointer_width;
for kind in [IntKind::UInt, IntKind::ULong, IntKind::ULongLong] {
if int_width(kind, self.cx.target) >= width {
return self.types.int(kind);
}
}
self.types.int(IntKind::ULongLong)
}
pub(crate) fn is_variable_length(&self, ty: TypeId) -> bool {
match self.types.kind(self.types.canonical(ty)) {
TypeKind::Array { elem, len } => {
matches!(len, ArrayLen::Variable(_)) || self.is_variable_length(elem)
}
_ => false,
}
}
pub(crate) fn ptrdiff(&self) -> TypeId {
let width = self.cx.target.pointer_width;
for kind in [IntKind::Int, IntKind::Long, IntKind::LongLong] {
if int_width(kind, self.cx.target) >= width {
return self.types.int(kind);
}
}
self.types.int(IntKind::LongLong)
}
}