cly_impl/
converter.rs

1// SPDX-License-Identifier: GPL-3.0-or-later
2use crate::ast::{
3    BinaryExprType, BuiltinExpr, DeclarationType, ExprType, Index, IndexType, OffsetofType, Span,
4    TypeExprType, UnaryExprType,
5};
6use crate::{ast, to_span, S};
7use anyhow::{anyhow, Result};
8use repc_impl::layout::{
9    Annotation, Array, FieldLayout, Layout, Record, RecordField, Type, TypeLayout, TypeVariant,
10};
11use repc_impl::target::Target;
12use repc_impl::util::BITS_PER_BYTE;
13use std::collections::{HashMap, HashSet};
14use std::convert::TryInto;
15use std::ops::Not;
16
17#[derive(Eq, PartialEq)]
18pub struct ConversionResult {
19    pub types: HashMap<String, Type<TypeLayout>>,
20    pub consts: HashMap<String, i128>,
21}
22
23pub fn extract_layouts(input: &str, d: &[ast::Declaration]) -> Result<ConversionResult> {
24    struct Converter<'a>(&'a str);
25    impl<'a> Convert for Converter<'a> {
26        type Src = TypeLayout;
27        const USE_EVALUATED_EXPR: bool = true;
28
29        fn convert(&self, ty: Type<Self::Src>) -> Result<Type<TypeLayout>> {
30            Ok(ty)
31        }
32
33        fn extract_type(&self, t: &ast::Type) -> Result<Self::Src> {
34            match t.layout {
35                None => Err(anyhow!(
36                    "At {}: Missing type layout",
37                    to_span(self.0, Span(t.lo, t.lo))
38                )),
39                Some(l) => Ok(l),
40            }
41        }
42
43        fn extract_field(&self, t: &ast::RecordField, _pos: usize) -> Result<FieldLayout> {
44            match t.layout {
45                None => Err(anyhow!(
46                    "At {}: Missing field layout",
47                    to_span(self.0, Span(t.lo, t.lo))
48                )),
49                Some(l) => Ok(l),
50            }
51        }
52    }
53    Computer::new(input, d, Converter(input))?.compute_layouts()
54}
55
56pub fn compute_layouts(
57    input: &str,
58    d: &[ast::Declaration],
59    target: Target,
60) -> Result<ConversionResult> {
61    struct Converter(Target);
62    impl Convert for Converter {
63        type Src = ();
64
65        fn convert(&self, ty: Type<Self::Src>) -> Result<Type<TypeLayout>> {
66            Ok(repc_impl::builder::compute_layout(self.0, &ty)?)
67        }
68
69        fn extract_type(&self, _: &ast::Type) -> Result<Self::Src> {
70            Ok(())
71        }
72
73        fn extract_field(&self, _: &ast::RecordField, _: usize) -> Result<()> {
74            Ok(())
75        }
76    }
77    Computer::new(input, d, Converter(target))?.compute_layouts()
78}
79
80pub trait Convert {
81    type Src: Layout<OpaqueLayout = TypeLayout>;
82    const USE_EVALUATED_EXPR: bool = false;
83
84    fn convert(&self, ty: Type<Self::Src>) -> Result<Type<TypeLayout>>;
85    fn extract_type(&self, ty: &ast::Type) -> Result<<Self::Src as Layout>::TypeLayout>;
86    fn extract_field(
87        &self,
88        field: &ast::RecordField,
89        pos: usize,
90    ) -> Result<<Self::Src as Layout>::FieldLayout>;
91}
92
93pub struct Computer<'a, C> {
94    input: &'a str,
95    d: &'a [ast::Declaration],
96    declarations: HashMap<&'a str, &'a ast::Declaration>,
97    type_layouts: HashMap<String, Type<TypeLayout>>,
98    constants: HashMap<String, i128>,
99    converting: HashSet<&'a str>,
100    converter: C,
101}
102
103impl<'a, C: Convert> Computer<'a, C> {
104    pub fn new(input: &'a str, d: &'a [ast::Declaration], converter: C) -> Result<Self> {
105        let mut declarations = HashMap::new();
106        for d in d {
107            if let Some(old) = declarations.insert(&*d.name, d) {
108                return Err(anyhow!(
109                    "At {}: Type {} is declared multiple times. Previous declaration at {}",
110                    to_span(input, d.span),
111                    d.name,
112                    to_span(input, old.span)
113                ));
114            }
115        }
116        Ok(Computer {
117            input,
118            d,
119            declarations,
120            converter,
121            type_layouts: Default::default(),
122            converting: Default::default(),
123            constants: Default::default(),
124        })
125    }
126
127    pub fn compute_layouts(mut self) -> Result<ConversionResult> {
128        for d in self.d {
129            match d.ty {
130                DeclarationType::Type(_) => {
131                    self.compute_decl_ty_layout(d, d.span)?;
132                }
133                DeclarationType::Const(_) => {
134                    self.compute_decl_const(d, d.span)?;
135                }
136            }
137        }
138        Ok(ConversionResult {
139            types: self.type_layouts,
140            consts: self.constants,
141        })
142    }
143
144    fn span(&self, span: Span) -> S {
145        to_span(self.input, span)
146    }
147
148    fn compute_decl_const(&mut self, d: &'a ast::Declaration, site: Span) -> Result<i128> {
149        if let Some(value) = self.constants.get(&d.name) {
150            return Ok(*value);
151        }
152        if self.converting.insert(&d.name).not() {
153            return Err(anyhow!(
154                "At {}: The value of {} depends on itself",
155                self.span(d.span),
156                d.name
157            ));
158        }
159        let e = match &d.ty {
160            DeclarationType::Type(_) => {
161                return Err(anyhow!(
162                    "At {}: {} is declared as a type but must be a constant at {}",
163                    self.span(d.span),
164                    d.name,
165                    self.span(site),
166                ));
167            }
168            DeclarationType::Const(e) => e,
169        };
170        let res = self.eval_expr(e);
171        self.converting.remove(&*d.name);
172        let res = res?;
173        self.constants.insert(d.name.clone(), res);
174        Ok(res)
175    }
176
177    fn compute_decl_ty_layout(
178        &mut self,
179        d: &'a ast::Declaration,
180        site: Span,
181    ) -> Result<TypeLayout> {
182        if let Some(layout) = self.type_layouts.get(&d.name) {
183            return Ok(layout.layout);
184        }
185        let ty = match &d.ty {
186            DeclarationType::Type(ty) => ty,
187            DeclarationType::Const(_) => {
188                return Err(anyhow!(
189                    "At {}: {} is declared as a constant but must be a type at {}",
190                    self.span(d.span),
191                    d.name,
192                    self.span(site),
193                ));
194            }
195        };
196        if self.converting.insert(&d.name).not() {
197            return Err(anyhow!(
198                "At {}: The layout of {} depends on itself",
199                self.span(d.span),
200                d.name
201            ));
202        }
203        let res = self.compute_type_layout(ty);
204        self.converting.remove(&*d.name);
205        let res = res?;
206        let layout = res.layout;
207        self.type_layouts.insert(d.name.clone(), res);
208        Ok(layout)
209    }
210
211    fn compute_type_layout(&mut self, t: &'a ast::Type) -> Result<Type<TypeLayout>> {
212        let t = self.convert_type(t)?;
213        Ok(self.converter.convert(t)?)
214    }
215
216    fn convert_type(&mut self, t: &'a ast::Type) -> Result<Type<C::Src>> {
217        let variant = match &t.variant {
218            ast::TypeVariant::Opaque(l) => {
219                let layout = if C::USE_EVALUATED_EXPR && t.layout.is_some() {
220                    t.layout.unwrap()
221                } else {
222                    TypeLayout {
223                        size_bits: self.eval_u64_expr(&l.size_bits)?,
224                        pointer_alignment_bits: self.eval_u64_expr(&l.pointer_alignment_bits)?,
225                        field_alignment_bits: self.eval_u64_expr(&l.field_alignment_bits)?,
226                        required_alignment_bits: self.eval_u64_expr(&l.required_alignment_bits)?,
227                    }
228                };
229                TypeVariant::Opaque(layout)
230            }
231            ast::TypeVariant::Builtin(bi) => TypeVariant::Builtin(*bi),
232            ast::TypeVariant::Record(r) => TypeVariant::Record(self.convert_record(r)?),
233            ast::TypeVariant::Array(a) => TypeVariant::Array(self.convert_array(a)?),
234            ast::TypeVariant::Name(n, span) => match self.declarations.get(&**n) {
235                None => {
236                    return Err(anyhow!(
237                        "At {}: The referenced type {} is not declared",
238                        self.span(*span),
239                        n
240                    ))
241                }
242                Some(&d) => TypeVariant::Opaque(self.compute_decl_ty_layout(d, *span)?),
243            },
244            ast::TypeVariant::Typedef(td) => TypeVariant::Typedef(Box::new(self.convert_type(td)?)),
245            ast::TypeVariant::Enum(e) => {
246                let mut res = vec![];
247                for e in e {
248                    res.push(self.eval_expr(e)?);
249                }
250                TypeVariant::Enum(res)
251            }
252        };
253        Ok(Type {
254            layout: self.converter.extract_type(t)?,
255            annotations: self.convert_annotations(&t.annotations)?,
256            variant,
257        })
258    }
259
260    fn convert_record(&mut self, r: &'a ast::Record) -> Result<Record<C::Src>> {
261        let mut fields = vec![];
262        for f in &r.fields {
263            fields.push(self.convert_record_field(f)?);
264        }
265        Ok(Record {
266            kind: r.kind,
267            fields,
268        })
269    }
270
271    fn convert_record_field(&mut self, f: &'a ast::RecordField) -> Result<RecordField<C::Src>> {
272        Ok(RecordField {
273            layout: match f.pos {
274                Some(p) => Some(self.converter.extract_field(f, p)?),
275                _ => None,
276            },
277            annotations: self.convert_annotations(&f.annotations)?,
278            named: f.name.is_some(),
279            bit_width: f
280                .bit_width
281                .as_ref()
282                .map(|w| self.eval_u64_expr(w))
283                .transpose()?,
284            ty: self.convert_type(&f.ty)?,
285        })
286    }
287
288    fn convert_annotations(&mut self, a: &'a [ast::Annotation]) -> Result<Vec<Annotation>> {
289        let mut res = vec![];
290        for a in a {
291            res.push(match a {
292                ast::Annotation::PragmaPack(e) => {
293                    Annotation::PragmaPack(BITS_PER_BYTE * self.eval_u64_expr(e)?)
294                }
295                ast::Annotation::AttrPacked => Annotation::AttrPacked,
296                ast::Annotation::Aligned(None) => Annotation::Align(None),
297                ast::Annotation::Aligned(Some(e)) => {
298                    Annotation::Align(Some(BITS_PER_BYTE * self.eval_u64_expr(e)?))
299                }
300            });
301        }
302        Ok(res)
303    }
304
305    fn eval_u64_expr(&mut self, e: &'a ast::Expr) -> Result<u64> {
306        let v = self.eval_expr(e)?.try_into().map_err(|_| {
307            anyhow!(
308                "At {}: Expression value does not fit into u64",
309                self.span(e.span)
310            )
311        })?;
312        Ok(v)
313    }
314
315    fn eval_expr(&mut self, e: &'a ast::Expr) -> Result<i128> {
316        if C::USE_EVALUATED_EXPR {
317            if let Some(e) = e.value {
318                return Ok(e);
319            }
320        }
321        match &e.ty {
322            ExprType::Lit(n) => Ok(*n),
323            ExprType::Builtin(b) => match b {
324                BuiltinExpr::BitsPerByte => Ok(BITS_PER_BYTE as i128),
325            },
326            ExprType::Unary(k, v) => {
327                let v = self.eval_expr(v)?;
328                match *k {
329                    UnaryExprType::Neg => v
330                        .checked_neg()
331                        .ok_or_else(|| anyhow!("At {}: Expression overflow", self.span(e.span))),
332                    UnaryExprType::Not => Ok(if v != 0 { 0 } else { 1 }),
333                }
334            }
335            ExprType::Binary(k, le, re) => {
336                use BinaryExprType::*;
337                let l = self.eval_expr(le)?;
338                let r = self.eval_expr(re)?;
339                match *k {
340                    Add | Sub | Mul => match *k {
341                        Add => l.checked_add(r),
342                        Sub => l.checked_sub(r),
343                        Mul => l.checked_mul(r),
344                        _ => unreachable!(),
345                    }
346                    .ok_or_else(|| anyhow!("At {}: Expression overflow", self.span(e.span))),
347                    Div | Mod => {
348                        if r == 0 {
349                            return Err(anyhow!("At {}: Division by zero", self.span(re.span)));
350                        }
351                        Ok(match *k {
352                            Div => l / r,
353                            Mod => l % r,
354                            _ => unreachable!(),
355                        })
356                    }
357                    LogicalAnd | LogicalOr | Eq | NotEq | Lt | Le | Gt | Ge => {
358                        let ll = l != 0;
359                        let rr = r != 0;
360                        Ok(match *k {
361                            LogicalAnd => ll && rr,
362                            LogicalOr => ll || rr,
363                            Eq => l == r,
364                            NotEq => l != r,
365                            Lt => l < r,
366                            Le => l <= r,
367                            Gt => l > r,
368                            Ge => l >= r,
369                            _ => unreachable!(),
370                        } as i128)
371                    }
372                }
373            }
374            ExprType::TypeExpr(k, t) => {
375                let layout = self.compute_type_layout(t)?.layout;
376                Ok(match k {
377                    TypeExprType::Sizeof => (layout.size_bits / BITS_PER_BYTE) as i128,
378                    TypeExprType::SizeofBits => layout.size_bits as i128,
379                })
380            }
381            ExprType::Name(n) => match self.declarations.get(&**n) {
382                None => Err(anyhow!(
383                    "At {}: The referenced constant {} is not declared",
384                    self.span(e.span),
385                    n
386                )),
387                Some(&d) => self.compute_decl_const(d, e.span),
388            },
389            ExprType::Offsetof(k, aty, p) => {
390                let ty = self.compute_type_layout(aty)?;
391                let val = self.eval_offsetof(*k, aty, &ty, &p[0], &p[1..])?;
392                match k {
393                    OffsetofType::Bytes => Ok((val / BITS_PER_BYTE) as i128),
394                    OffsetofType::Bits => Ok(val as i128),
395                }
396            }
397        }
398    }
399
400    fn eval_offsetof(
401        &mut self,
402        k: OffsetofType,
403        aty: &'a ast::Type,
404        ty: &Type<TypeLayout>,
405        head: &'a Index,
406        rest: &'a [Index],
407    ) -> Result<u64> {
408        let (aty, ty, base) = match (&aty.variant, &ty.variant, &head.ty) {
409            (ast::TypeVariant::Record(ar), TypeVariant::Record(r), IndexType::Field(name)) => {
410                let af = match ar.fields.iter().find(|f| f.name.as_ref() == Some(name)) {
411                    Some(f) => f,
412                    None => {
413                        return Err(anyhow!(
414                            "At {}: Type has no field {}",
415                            self.span(head.span),
416                            name
417                        ))
418                    }
419                };
420                let pos = ar
421                    .fields
422                    .iter()
423                    .position(|f| f.name.as_ref() == Some(name))
424                    .unwrap();
425                let f = &r.fields[pos];
426                if f.bit_width.is_some() && k == OffsetofType::Bytes {
427                    return Err(anyhow!(
428                        "At {}: Cannot compute bytewise offset of bit field",
429                        self.span(head.span)
430                    ));
431                }
432                (&af.ty, &f.ty, f.layout.unwrap().offset_bits)
433            }
434            (ast::TypeVariant::Array(aa), TypeVariant::Array(a), IndexType::Array(pos)) => {
435                let pos = self.eval_u64_expr(pos)?;
436                match a.num_elements {
437                    Some(n) if pos > n => {
438                        return Err(anyhow!("At {}: Out of bounds", self.span(head.span)));
439                    }
440                    _ => {}
441                }
442                match a.element_type.layout.size_bits.checked_mul(pos) {
443                    None => {
444                        return Err(anyhow!("At {}: Offset overflow", self.span(head.span)));
445                    }
446                    Some(b) => (&*aa.element_type, &*a.element_type, b),
447                }
448            }
449            (ast::TypeVariant::Name(n, span), _, _) => {
450                let d = match self.declarations.get(&**n) {
451                    None => {
452                        return Err(anyhow!(
453                            "At {}: The referenced type {} is not declared",
454                            self.span(*span),
455                            n
456                        ))
457                    }
458                    Some(d) => *d,
459                };
460                self.compute_decl_ty_layout(d, head.span)?;
461                let aty = match &d.ty {
462                    DeclarationType::Type(aty) => aty,
463                    DeclarationType::Const(_) => unreachable!(),
464                };
465                let ty = self.type_layouts.get(n).unwrap().clone();
466                return self.eval_offsetof(k, aty, &ty, head, rest);
467            }
468            (_, _, IndexType::Field(_)) => {
469                return Err(anyhow!("At {}: Type is not a record", self.span(head.span)));
470            }
471            (_, _, IndexType::Array(_)) => {
472                return Err(anyhow!("At {}: Type is not an array", self.span(head.span)));
473            }
474        };
475        Ok(base
476            + match rest {
477                [head, rest @ ..] => self.eval_offsetof(k, aty, ty, head, rest)?,
478                _ => 0,
479            })
480    }
481
482    fn convert_array(&mut self, a: &'a ast::Array) -> Result<Array<C::Src>> {
483        Ok(Array {
484            element_type: Box::new(self.convert_type(&a.element_type)?),
485            num_elements: match &a.num_elements {
486                None => None,
487                Some(n) => Some(self.eval_u64_expr(n)?),
488            },
489        })
490    }
491}