1use std::collections::{HashMap, HashSet};
41use std::mem;
42
43use rucc_ast::{self as ast, AsmQuals, ForInit, StorageClass};
44use rucc_base::Symbol;
45use rucc_diag::{Diagnostic, Span};
46use rucc_lex::{Encoding, Remarks, StringLiteral};
47use rucc_session::Std;
48use rucc_types::{IntegerInfo, Qualifiers, TypeId, is_integer, is_pointer, is_record, is_void};
49
50use crate::asm::{Asm, AsmOperand, AsmOperandList, LabelList};
51use crate::check::Checker;
52use crate::check::expr::Target;
53use crate::decl::{DeclId, DeclList};
54use crate::eval;
55use crate::expr::{Category, Expr, ExprId, ExprKind};
56use crate::stmt::{Case, Stmt, StmtId};
57use crate::tast::{Const, Label, LabelId, StrId};
58
59pub(in crate::check) const FUNCTION_NAMES: [&str; 3] =
63 ["__func__", "__FUNCTION__", "__PRETTY_FUNCTION__"];
64
65#[derive(Debug)]
67pub(in crate::check) struct Body {
68 ret: TypeId,
70 at: Span,
73 variadic: bool,
76 last_param: Option<DeclId>,
78 params: DeclList,
82 name: Option<Symbol>,
84 func_name: [Option<StrId>; FUNCTION_NAMES.len()],
89 labels: HashMap<Symbol, Labelled>,
91 shadowed: Vec<(Symbol, Option<Labelled>)>,
94 blocks: Vec<usize>,
96 switches: Vec<Switch>,
98 loops: usize,
100 undeclared: HashSet<Symbol>,
104}
105
106#[derive(Debug, Clone, Copy)]
108pub(in crate::check) struct Enclosing {
109 pub ret: TypeId,
111 pub at: Span,
113 pub variadic: bool,
115 pub last_param: Option<DeclId>,
117 pub params: DeclList,
119 pub name: Option<Symbol>,
121}
122
123impl Enclosing {
124 pub(in crate::check) fn returning(ret: TypeId) -> Enclosing {
127 Enclosing {
128 ret,
129 at: Span::DUMMY,
130 variadic: false,
131 last_param: None,
132 params: DeclList::EMPTY,
133 name: None,
134 }
135 }
136}
137
138#[derive(Debug, Clone, Copy)]
140struct Labelled {
141 id: LabelId,
143 defined: Option<Span>,
145 at: Span,
147}
148
149#[derive(Debug)]
151struct Switch {
152 ty: TypeId,
154 range: Option<IntegerInfo>,
159 cases: Vec<Case>,
161 spans: Vec<Span>,
163 labels: Vec<StmtId>,
166 default: Option<(StmtId, Span)>,
168}
169
170impl Checker<'_> {
171 pub fn check_stmt(&mut self, ret: TypeId, id: ast::StmtId) -> StmtId {
177 let previous = self.open_body(Enclosing::returning(ret));
178 let stmt = self.stmt(id);
179 self.close_body(previous);
180 stmt
181 }
182
183 pub(in crate::check) fn stmt(&mut self, id: ast::StmtId) -> StmtId {
185 let span = self.ast.stmt_span(id);
186 let node = match self.ast[id] {
187 ast::Stmt::Error => Stmt::Error,
188 ast::Stmt::Empty => Stmt::Empty,
189 ast::Stmt::Expr(value) => {
190 let value = self.expr(value);
191 Stmt::Expr(self.value(value))
192 }
193 ast::Stmt::Decl(decl) => Stmt::Decls(self.check_decl(decl)),
194 ast::Stmt::Compound(body) => Stmt::Block(self.block(body)),
195 ast::Stmt::If { cond, then, otherwise } => {
196 let cond = self.controlling(cond);
197 let then = self.stmt(then);
198 Stmt::If { cond, then, otherwise: otherwise.map(|id| self.stmt(id)) }
199 }
200 ast::Stmt::Switch { scrutinee, body } => self.switch(scrutinee, body),
201 ast::Stmt::While { cond, body } => {
202 let cond = self.controlling(cond);
203 Stmt::While { cond, body: self.loop_body(body) }
204 }
205 ast::Stmt::DoWhile { body, cond } => {
206 let body = self.loop_body(body);
207 Stmt::DoWhile { body, cond: self.controlling(cond) }
208 }
209 ast::Stmt::For { init, cond, step, body } => self.for_loop(init, cond, step, body),
210 ast::Stmt::Goto(name) => Stmt::Goto(self.label(name, span)),
211 ast::Stmt::GotoExpr(target) => self.computed_goto(target),
212 ast::Stmt::Continue => self.continue_stmt(span),
213 ast::Stmt::Break => self.break_stmt(span),
214 ast::Stmt::Return(value) => self.return_stmt(value, span),
215 ast::Stmt::Label { name, body, .. } => self.labelled(name, body, span),
216 ast::Stmt::Case { lo, hi, body } => self.case(lo, hi, body, span),
217 ast::Stmt::Default { body } => self.default(body, span),
218 ast::Stmt::LocalLabels(names) => {
219 self.local_labels(names, span);
220 Stmt::Empty
221 }
222 ast::Stmt::Asm(asm) => self.asm(asm, span),
223 };
224 let stmt = self.tast.stmt(node, span);
225 if matches!(node, Stmt::Case { .. }) {
229 if let Some(switch) = self.switches() {
230 switch.labels.push(stmt);
231 }
232 }
233 stmt
234 }
235
236 pub(in crate::check) fn stmt_expr(&mut self, id: ast::StmtId, span: Span) -> ExprId {
244 let stmt = self.stmt(id);
245 let ty = match self.tast[stmt] {
246 Stmt::Block(body) => match self.tast[body].last() {
247 Some(&last) => match self.tast[last] {
248 Stmt::Expr(value) => self.tast[value].ty,
249 _ => self.types.void(),
250 },
251 None => self.types.void(),
252 },
253 _ => self.types.void(),
254 };
255 self.tast.expr(Expr::new(ExprKind::StmtExpr(stmt), ty, Category::Rvalue), span)
256 }
257
258 pub(in crate::check) fn label_addr(&mut self, name: Symbol, span: Span) -> ExprId {
263 let label = self.label(name, span);
264 let ty = self.types.pointer(self.types.void());
265 self.tast.expr(Expr::new(ExprKind::LabelAddr(label), ty, Category::Rvalue), span)
266 }
267
268 pub(in crate::check) fn open_body(&mut self, func: Enclosing) -> Option<Body> {
273 let body = Body {
274 ret: func.ret,
275 at: func.at,
276 variadic: func.variadic,
277 last_param: func.last_param,
278 params: func.params,
279 name: func.name,
280 func_name: [None; FUNCTION_NAMES.len()],
281 labels: HashMap::new(),
282 shadowed: Vec::new(),
283 blocks: Vec::new(),
284 switches: Vec::new(),
285 loops: 0,
286 undeclared: HashSet::new(),
287 };
288 self.body.replace(body)
289 }
290
291 pub(in crate::check) fn in_variadic_function(&self) -> bool {
296 self.body.as_ref().is_some_and(|body| body.variadic)
297 }
298
299 pub(in crate::check) fn last_named_parameter(&self) -> Option<DeclId> {
302 self.body.as_ref().and_then(|body| body.last_param)
303 }
304
305 pub(in crate::check) fn function_name_string(&mut self, which: usize) -> Option<StrId> {
312 let name = self.body.as_ref()?.name?;
313 if let Some(id) = self.body.as_ref().and_then(|body| body.func_name[which]) {
314 return Some(id);
315 }
316 let elements = self.text(name).chars().map(|c| c as u32).collect();
317 let literal =
318 StringLiteral { elements, encoding: Encoding::Plain, remarks: Remarks::default() };
319 let id = self.tast.add_string(literal);
320 if let Some(body) = &mut self.body {
321 body.func_name[which] = Some(id);
322 }
323 Some(id)
324 }
325
326 pub(in crate::check) fn is_parameter(&self, decl: DeclId) -> bool {
330 self.body.as_ref().is_some_and(|body| self.tast[body.params].contains(&decl))
331 }
332
333 pub(in crate::check) fn first_undeclared_use(&mut self, name: Symbol) -> bool {
339 match &mut self.body {
340 Some(body) => body.undeclared.insert(name),
341 None => true,
342 }
343 }
344
345 pub(in crate::check) fn close_body(&mut self, previous: Option<Body>) {
347 let Some(body) = mem::replace(&mut self.body, previous) else {
348 return;
349 };
350 let mut undefined: Vec<Labelled> =
353 body.labels.into_values().filter(|label| label.defined.is_none()).collect();
354 undefined.sort_by_key(|label| label.at.lo);
355 for label in undefined {
356 self.undefined_label(label);
357 }
358 }
359
360 pub(in crate::check) fn body_block(&mut self, body: ast::StmtId) -> StmtId {
366 let span = self.ast.stmt_span(body);
367 let ast::Stmt::Compound(list) = self.ast[body] else {
368 return self.stmt(body);
369 };
370 let list = self.statements(list);
371 self.tast.stmt(Stmt::Block(list), span)
372 }
373
374 fn block(&mut self, body: ast::StmtList) -> crate::stmt::StmtList {
376 self.scopes.push();
377 let list = self.statements(body);
378 self.scopes.pop();
379 list
380 }
381
382 fn statements(&mut self, body: ast::StmtList) -> crate::stmt::StmtList {
384 if let Some(state) = self.body.as_mut() {
385 let mark = state.shadowed.len();
386 state.blocks.push(mark);
387 }
388 let ids = self.ast[body].to_vec();
389 let mut stmts = Vec::with_capacity(ids.len());
390 for id in ids {
391 stmts.push(self.stmt(id));
392 }
393 self.end_block();
394 self.tast.add_stmt_refs(&stmts)
395 }
396
397 fn end_block(&mut self) {
399 let Some(body) = self.body.as_mut() else {
400 return;
401 };
402 let Some(mark) = body.blocks.pop() else {
403 return;
404 };
405 let mut gone = Vec::new();
406 while body.shadowed.len() > mark {
407 let (name, previous) = body.shadowed.pop().expect("a saved binding");
408 let local = match previous {
409 Some(previous) => body.labels.insert(name, previous),
410 None => body.labels.remove(&name),
411 };
412 if let Some(local) = local {
413 if local.defined.is_none() {
414 gone.push(local);
415 }
416 }
417 }
418 gone.sort_by_key(|label| label.at.lo);
419 for label in gone {
420 self.undefined_label(label);
421 }
422 }
423
424 fn loop_body(&mut self, body: ast::StmtId) -> StmtId {
426 if let Some(state) = self.body.as_mut() {
427 state.loops += 1;
428 }
429 let body = self.stmt(body);
430 if let Some(state) = self.body.as_mut() {
431 state.loops -= 1;
432 }
433 body
434 }
435
436 fn for_loop(
438 &mut self,
439 init: ForInit,
440 cond: Option<ast::ExprId>,
441 step: Option<ast::ExprId>,
442 body: ast::StmtId,
443 ) -> Stmt {
444 self.scopes.push();
447 let init = match init {
448 ForInit::None => None,
449 ForInit::Expr(value) => {
450 let span = self.ast.expr_span(value);
451 let value = self.expr(value);
452 let value = self.value(value);
453 Some(self.tast.stmt(Stmt::Expr(value), span))
454 }
455 ForInit::Decl(decl) => {
456 let span = self.ast.decl_span(decl);
457 let decls = self.check_decl(decl);
458 self.check_loop_declaration(decl);
459 Some(self.tast.stmt(Stmt::Decls(decls), span))
460 }
461 };
462 let cond = cond.map(|cond| self.controlling(cond));
463 let step = step.map(|step| {
464 let step = self.expr(step);
465 self.value(step)
466 });
467 let body = self.loop_body(body);
468 self.scopes.pop();
469 Stmt::For { init, cond, step, body }
470 }
471
472 fn check_loop_declaration(&mut self, decl: ast::DeclId) {
483 if !self.cx.pedantic {
484 return;
485 }
486 let ast::Decl::Var { specs, declarators } = self.ast[decl] else {
487 return;
488 };
489 let specs = self.ast[specs];
490 let word = match specs.storage {
491 _ if specs.is_typedef() => "non-variable",
492 Some(StorageClass::Static) => "static variable",
493 Some(StorageClass::Extern) => "'extern' variable",
494 _ => return,
495 };
496 let ast = self.ast;
497 for &item in &ast[declarators] {
498 let node = ast[item.declarator];
499 let Some(name) = node.name else { continue };
500 let spelled = self.text(name).to_owned();
501 self.report(
502 Diagnostic::warning(
503 format!("declaration of {word} '{spelled}' in 'for' loop initial declaration"),
504 node.name_span,
505 )
506 .with_code("E0619"),
507 );
508 }
509 }
510
511 fn switch(&mut self, scrutinee: ast::ExprId, body: ast::StmtId) -> Stmt {
513 let at = self.ast.expr_span(scrutinee);
514 let cond = self.expr(scrutinee);
515 let cond = self.value(cond);
516 let range = eval::int_shape(&self.types, self.tast[cond].ty, self.cx.target);
520 let cond = self.conv().promote(cond);
521 let ty = self.tast[cond].ty;
522 let cond = if self.is_poisoned(cond) || is_integer(&self.types, ty) {
523 cond
524 } else {
525 self.report(Diagnostic::error("switch quantity not an integer", at).with_code("E0620"));
526 self.poison(at)
527 };
528 let ty = if is_integer(&self.types, ty) { ty } else { self.int() };
532 if let Some(state) = self.body.as_mut() {
533 state.switches.push(Switch {
534 ty,
535 range,
536 cases: Vec::new(),
537 spans: Vec::new(),
538 labels: Vec::new(),
539 default: None,
540 });
541 }
542 let body = self.stmt(body);
543 let Some(switch) = self.body.as_mut().and_then(|state| state.switches.pop()) else {
544 return Stmt::Error;
545 };
546 let cases = self.tast.add_cases(&switch.cases);
547 for &labelled in &switch.labels {
548 let Stmt::Case { case: entry, body } = self.tast[labelled] else {
549 continue;
550 };
551 let case = cases.iter().nth(entry.index()).expect("a case for every label");
555 self.tast.set_stmt(labelled, Stmt::Case { case, body });
556 }
557 Stmt::Switch { cond, body, cases, default: switch.default.map(|(stmt, _)| stmt) }
558 }
559
560 fn case(
562 &mut self,
563 lo: ast::ExprId,
564 hi: Option<ast::ExprId>,
565 body: Option<ast::StmtId>,
566 span: Span,
567 ) -> Stmt {
568 let entry = self.enter_case(lo, hi, span);
573 let body = self.labelled_body(body, span);
574 let Some(entry) = entry else {
575 return Stmt::Error;
576 };
577 self.switches().expect("a switch").cases[entry].body = body;
578 Stmt::Case { case: rucc_base::Idx::from_usize(entry), body }
583 }
584
585 fn enter_case(
588 &mut self,
589 lo: ast::ExprId,
590 hi: Option<ast::ExprId>,
591 span: Span,
592 ) -> Option<usize> {
593 if self.body.as_ref().is_none_or(|state| state.switches.is_empty()) {
594 self.report(
595 Diagnostic::error("case label not within a switch statement", span)
596 .with_code("E0621"),
597 );
598 return None;
599 }
600 let low = self.case_value(lo, span)?;
601 let high = match hi {
602 Some(hi) => self.case_value(hi, span)?,
603 None => low,
604 };
605 if high < low {
606 self.report(Diagnostic::warning("empty range specified", span).with_code("E0622"));
607 return None;
608 }
609 if let Some(at) = self.overlapping_case(low, high) {
610 self.report(
611 Diagnostic::error("duplicate case value", span)
612 .with_code("E0623")
613 .note("previously used here".to_owned(), at),
614 );
615 return None;
616 }
617 let switch = self.switches().expect("a switch");
618 let entry = switch.cases.len();
619 switch.cases.push(Case { low, high, body: rucc_base::Idx::from_usize(0) });
622 switch.spans.push(span);
623 Some(entry)
624 }
625
626 fn case_value(&mut self, value: ast::ExprId, span: Span) -> Option<i128> {
628 let at = self.ast.expr_span(value);
629 let value = self.expr(value);
630 let value = self.value(value);
631 let folded = match self.eval_integer(value) {
632 Ok(folded) => folded,
633 Err(failed) => {
634 if !failed.poisoned {
635 self.report(
636 Diagnostic::error("case label does not reduce to an integer constant", at)
637 .with_code("E0624"),
638 );
639 }
640 return None;
641 }
642 };
643 let switch = self.switches()?;
644 let (ty, range) = (switch.ty, switch.range);
645 if let Some(range) = range {
646 if eval::overflows(Const::Int(folded), range) {
647 self.report(
648 Diagnostic::warning("case label value exceeds maximum value for type", span)
649 .with_code("E0625"),
650 );
651 }
652 }
653 let info = eval::int_shape(&self.types, ty, self.cx.target)?;
654 Some(eval::narrowed(Const::Int(folded), info))
655 }
656
657 fn overlapping_case(&mut self, low: i128, high: i128) -> Option<Span> {
659 let switch = self.switches()?;
660 switch
661 .cases
662 .iter()
663 .position(|case| case.low <= high && low <= case.high)
664 .map(|index| switch.spans[index])
665 }
666
667 fn default(&mut self, body: Option<ast::StmtId>, span: Span) -> Stmt {
669 let body = self.labelled_body(body, span);
670 if self.body.as_ref().is_none_or(|state| state.switches.is_empty()) {
671 self.report(
672 Diagnostic::error("'default' label not within a switch statement", span)
673 .with_code("E0626"),
674 );
675 return Stmt::Error;
676 }
677 if let Some((_, at)) = self.switches().expect("a switch").default {
678 self.report(
679 Diagnostic::error("multiple default labels in one switch", span)
680 .with_code("E0627")
681 .note("this is the first default label".to_owned(), at),
682 );
683 return Stmt::Error;
684 }
685 self.switches().expect("a switch").default = Some((body, span));
686 Stmt::Default { body }
687 }
688
689 fn labelled(&mut self, name: Symbol, body: Option<ast::StmtId>, span: Span) -> Stmt {
691 let body = self.labelled_body(body, span);
692 let label = self.label(name, span);
693 let defined = self.body.as_ref().and_then(|state| state.labels[&name].defined);
694 if let Some(at) = defined {
695 let spelled = self.text(name).to_owned();
696 self.report(
697 Diagnostic::error(format!("duplicate label '{spelled}'"), span)
698 .with_code("E0628")
699 .note(format!("previous definition of '{spelled}' with type 'void'"), at),
700 );
701 return Stmt::Error;
702 }
703 if let Some(state) = self.body.as_mut() {
704 state.labels.entry(name).and_modify(|known| known.defined = Some(span));
705 }
706 self.tast.define_label(label, body);
707 Stmt::Label { label, body }
708 }
709
710 fn labelled_body(&mut self, body: Option<ast::StmtId>, span: Span) -> StmtId {
712 match body {
713 Some(body) => self.stmt(body),
714 None => self.tast.stmt(Stmt::Empty, span),
715 }
716 }
717
718 fn local_labels(&mut self, names: ast::SymbolList, span: Span) {
720 let ast = self.ast;
721 for &name in &ast[names] {
722 let id = self.tast.add_label(Label { name, stmt: None });
723 let local = Labelled { id, defined: None, at: span };
724 if let Some(state) = self.body.as_mut() {
725 let previous = state.labels.insert(name, local);
726 state.shadowed.push((name, previous));
727 }
728 }
729 }
730
731 fn label(&mut self, name: Symbol, span: Span) -> LabelId {
733 if let Some(known) = self.body.as_ref().and_then(|state| state.labels.get(&name)) {
734 return known.id;
735 }
736 let id = self.tast.add_label(Label { name, stmt: None });
737 if let Some(state) = self.body.as_mut() {
738 state.labels.insert(name, Labelled { id, defined: None, at: span });
739 }
740 id
741 }
742
743 fn undefined_label(&mut self, label: Labelled) {
750 let name = self.tast[label.id].name;
751 let spelled = self.text(name).to_owned();
752 self.report(
753 Diagnostic::error(format!("label '{spelled}' used but not defined"), label.at)
754 .with_code("E0629"),
755 );
756 }
757
758 fn computed_goto(&mut self, target: ast::ExprId) -> Stmt {
760 let at = self.ast.expr_span(target);
761 let target = self.expr(target);
762 let target = self.value(target);
763 if self.is_poisoned(target) {
764 return Stmt::Error;
765 }
766 let ty = self.tast[target].ty;
767 if !is_pointer(&self.types, ty) && !is_integer(&self.types, ty) {
770 self.report(
771 Diagnostic::error("computed goto must be pointer type", at).with_code("E0630"),
772 );
773 return Stmt::Error;
774 }
775 let void = self.types.pointer(self.types.void());
776 let target = self.conv().to_type(target, void);
777 Stmt::IndirectGoto(target)
778 }
779
780 fn asm(&mut self, id: ast::AsmId, span: Span) -> Stmt {
793 let node = self.ast[id];
794 let outputs = self.asm_operands(node.outputs, 0, true);
795 let first_input = self.ast[node.outputs].len();
796 let inputs = self.asm_operands(node.inputs, first_input, false);
797
798 let mut clobbers = Vec::with_capacity(self.ast[node.clobbers].len());
799 for index in 0..self.ast[node.clobbers].len() {
800 let clobber = self.ast[node.clobbers][index];
801 clobbers.push(self.asm_string(clobber, span));
802 }
803 let clobbers = self.tast.add_str_refs(&clobbers);
804
805 let mut labels = Vec::with_capacity(self.ast[node.labels].len());
806 for index in 0..self.ast[node.labels].len() {
807 let name = self.ast[node.labels][index];
808 labels.push(self.label(name, span));
809 }
810 let labels = self.tast.add_label_refs(&labels);
811 let template = self.asm_template(node.template, outputs, inputs, labels, span);
812
813 let mut quals = node.quals;
817 if self.ast[node.outputs].is_empty() || quals.has(AsmQuals::GOTO) {
818 quals = quals.with(AsmQuals::VOLATILE);
819 }
820 Stmt::Asm(self.tast.add_asm(Asm { template, outputs, inputs, clobbers, labels, quals }))
821 }
822
823 fn asm_operands(
825 &mut self,
826 list: ast::AsmOperandList,
827 first: usize,
828 output: bool,
829 ) -> AsmOperandList {
830 let mut operands = Vec::with_capacity(self.ast[list].len());
831 for index in 0..self.ast[list].len() {
832 let operand = self.ast[list][index];
833 let operand = self.asm_operand(operand, first + index, output);
834 operands.push(operand);
835 }
836 self.tast.add_asm_operands(&operands)
837 }
838
839 fn asm_operand(&mut self, operand: ast::AsmOperand, number: usize, output: bool) -> AsmOperand {
841 let span = operand.span;
842 let constraint = self.asm_string(operand.constraint, span);
843 let text = spelling(&self.tast[constraint]);
844 let value = self.expr(operand.value);
845 let ty = self.tast[value].ty;
846 let lvalue = matches!(self.tast[value].category, Category::Lvalue | Category::Bitfield);
847
848 let record = is_record(&self.types, ty);
852 let memory = memory_only(&text) || record;
853 if record && !memory_only(&text) {
854 self.statement_unsupported("a structure or a union in a register constraint", span);
855 }
856
857 if output {
858 if !text.starts_with(['=', '+']) {
859 self.report(
860 Diagnostic::error("output operand constraint lacks '='", span)
861 .with_code("E0653"),
862 );
863 }
864 if !lvalue {
865 self.report(
866 Diagnostic::error("lvalue required in 'asm' statement", span)
867 .with_code("E0654"),
868 );
869 } else if self.types.quals(ty).has(Qualifiers::CONST) {
870 let what = self.read_only(value);
871 self.report(
872 Diagnostic::error(format!("read-only {what} used as 'asm' output"), span)
873 .with_code("E0655"),
874 );
875 }
876 } else {
877 if let Some(sign) = text.chars().find(|&ch| ch == '=' || ch == '+') {
878 self.report(
879 Diagnostic::error(format!("input operand constraint contains '{sign}'"), span)
880 .with_code("E0656"),
881 );
882 }
883 if memory && !lvalue {
884 self.report(
885 Diagnostic::error(
886 format!("memory input {number} is not directly addressable"),
887 span,
888 )
889 .with_code("E0657"),
890 );
891 }
892 }
893
894 let value = if output || memory { value } else { self.value(value) };
898 AsmOperand { name: operand.name, constraint, value, memory }
899 }
900
901 fn asm_string(&mut self, id: ast::StrId, span: Span) -> StrId {
903 let literal = self.ast[id].clone();
904 self.asm_narrow(&literal, span);
905 self.tast.add_string(literal)
906 }
907
908 fn asm_narrow(&mut self, literal: &StringLiteral, span: Span) {
910 if !matches!(literal.encoding, Encoding::Plain) {
911 self.report(Diagnostic::error("wide string literal in 'asm'", span).with_code("E0658"));
912 }
913 }
914
915 fn asm_template(
923 &mut self,
924 id: ast::StrId,
925 outputs: AsmOperandList,
926 inputs: AsmOperandList,
927 labels: LabelList,
928 span: Span,
929 ) -> StrId {
930 let mut names: Vec<(String, usize)> = Vec::new();
931 let mut number = 0;
932 for list in [outputs, inputs] {
933 for index in 0..self.tast[list].len() {
934 if let Some(name) = self.tast[list][index].name {
935 names.push((self.text(name).to_owned(), number));
936 }
937 number += 1;
938 }
939 }
940 for index in 0..self.tast[labels].len() {
941 let label = self.tast[labels][index];
942 let name = self.tast[label].name;
943 names.push((self.text(name).to_owned(), number));
944 number += 1;
945 }
946 for at in 1..names.len() {
947 if names[..at].iter().any(|(earlier, _)| *earlier == names[at].0) {
948 let name = names[at].0.clone();
949 self.report(
950 Diagnostic::error(format!("duplicate asm operand name '{name}'"), span)
951 .with_code("E0659"),
952 );
953 }
954 }
955
956 let literal = self.ast[id].clone();
957 self.asm_narrow(&literal, span);
958 let text = self.asm_numbers(spelling(&literal), &names, span);
959 let elements = text.chars().map(|ch| ch as u32).collect();
960 self.tast.add_string(StringLiteral { elements, ..literal })
961 }
962
963 fn asm_numbers(&mut self, text: String, names: &[(String, usize)], span: Span) -> String {
970 let chars: Vec<char> = text.chars().collect();
971 let mut out = String::with_capacity(text.len());
972 let mut index = 0;
973 while index < chars.len() {
974 let ch = chars[index];
975 out.push(ch);
976 index += 1;
977 if ch != '%' {
978 continue;
979 }
980 let letter = chars.get(index).copied();
981 let open = match letter {
982 Some('[') => index,
983 Some(modifier)
984 if modifier.is_ascii_alphabetic() && chars.get(index + 1) == Some(&'[') =>
985 {
986 out.push(modifier);
987 index += 1;
988 index
989 }
990 Some('%') => {
992 out.push('%');
993 index += 1;
994 continue;
995 }
996 _ => continue,
997 };
998 let Some(close) = chars[open..].iter().position(|&ch| ch == ']').map(|at| open + at)
999 else {
1000 continue;
1001 };
1002 let name: String = chars[open + 1..close].iter().collect();
1003 index = close + 1;
1004 match names.iter().find(|(known, _)| *known == name) {
1005 Some(&(_, number)) => out.push_str(&number.to_string()),
1006 None => {
1007 self.report(
1008 Diagnostic::error(format!("undefined named operand '{name}'"), span)
1009 .with_code("E0660"),
1010 );
1011 out.push_str(&chars[open..=close].iter().collect::<String>());
1012 }
1013 }
1014 }
1015 out
1016 }
1017
1018 fn break_stmt(&mut self, span: Span) -> Stmt {
1020 let inside =
1021 self.body.as_ref().is_some_and(|state| state.loops > 0 || !state.switches.is_empty());
1022 if inside {
1023 return Stmt::Break;
1024 }
1025 self.report(
1026 Diagnostic::error("break statement not within loop or switch", span).with_code("E0631"),
1027 );
1028 Stmt::Error
1029 }
1030
1031 fn continue_stmt(&mut self, span: Span) -> Stmt {
1033 if self.body.as_ref().is_some_and(|state| state.loops > 0) {
1034 return Stmt::Continue;
1035 }
1036 self.report(
1037 Diagnostic::error("continue statement not within a loop", span).with_code("E0632"),
1038 );
1039 Stmt::Error
1040 }
1041
1042 fn return_stmt(&mut self, value: Option<ast::ExprId>, span: Span) -> Stmt {
1049 let Some((ret, at)) = self.body.as_ref().map(|state| (state.ret, state.at)) else {
1050 return Stmt::Return(None);
1051 };
1052 let void = is_void(&self.types, ret);
1053 let old = self.cx.std < Std::C99;
1057 let Some(value) = value else {
1058 if !void && !old {
1059 self.report(
1060 Diagnostic::error(
1061 "'return' with no value, in function returning non-void",
1062 span,
1063 )
1064 .with_code("E0633")
1065 .note("declared here".to_owned(), at),
1066 );
1067 }
1068 return Stmt::Return(None);
1069 };
1070 let where_from = self.ast.expr_span(value);
1071 let value = self.expr(value);
1072 let value = self.value(value);
1073 if !void {
1074 return Stmt::Return(Some(self.assign_to(ret, value, where_from, Target::Return)));
1075 }
1076 if !is_void(&self.types, self.tast[value].ty) && !self.is_poisoned(value) {
1079 let said = "'return' with a value, in function returning void";
1080 let diagnostic = if old {
1081 Diagnostic::warning(said, where_from)
1082 } else {
1083 Diagnostic::error(said, where_from)
1084 };
1085 self.report(diagnostic.with_code("E0634").note("declared here".to_owned(), at));
1086 }
1087 let value = self.conv().to_void(value);
1088 Stmt::Return(Some(value))
1089 }
1090
1091 fn controlling(&mut self, cond: ast::ExprId) -> ExprId {
1093 let span = self.ast.expr_span(cond);
1094 let cond = self.expr(cond);
1095 self.condition(cond, span)
1096 }
1097
1098 fn switches(&mut self) -> Option<&mut Switch> {
1100 self.body.as_mut()?.switches.last_mut()
1101 }
1102
1103 fn statement_unsupported(&mut self, what: &str, span: Span) {
1105 self.report(
1106 Diagnostic::error(format!("{what} is not supported yet"), span).with_code("E0519"),
1107 );
1108 }
1109}
1110
1111fn spelling(literal: &StringLiteral) -> String {
1117 literal.elements.iter().filter_map(|&element| char::from_u32(element)).collect()
1118}
1119
1120fn memory_only(constraint: &str) -> bool {
1129 let letters: Vec<char> =
1130 constraint.chars().filter(|ch| !"=+&%#*!?, \t".contains(*ch)).collect();
1131 !letters.is_empty() && letters.iter().all(|ch| "moV<>".contains(*ch))
1132}
1133
1134#[cfg(test)]
1135mod tests {
1136 use rucc_ast::{
1137 AttrList, Builtin, BuiltinSet, DeclSpecs, DeclSpecsId, Declarator, DeclaratorId, Derived,
1138 TypeSpec,
1139 };
1140 use rucc_base::Interner;
1141 use rucc_lex::{IntConstant, IntConstantType, Remarks};
1142 use rucc_session::Std;
1143 use rucc_target::{TargetInfo, Triple};
1144 use rucc_types::IntKind;
1145
1146 use super::*;
1147 use crate::check::Context;
1148 use crate::print::Printer;
1149
1150 struct Fixture {
1156 ast: rucc_ast::Ast,
1157 names: Interner,
1158 target: TargetInfo,
1159 }
1160
1161 impl Fixture {
1162 fn new() -> Fixture {
1163 let target =
1164 TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a triple"));
1165 Fixture { ast: rucc_ast::Ast::new(), names: Interner::new(), target }
1166 }
1167
1168 fn name(&mut self, text: &str) -> Symbol {
1169 self.names.intern(text)
1170 }
1171
1172 fn int(&mut self, value: u128) -> ast::ExprId {
1173 let ty = IntConstantType::Standard(IntKind::Int);
1174 let id = self.ast.add_int(IntConstant { value, ty, remarks: Remarks::default() });
1175 self.ast.expr(ast::Expr::Int(id), Span::DUMMY)
1176 }
1177
1178 fn use_name(&mut self, text: &str) -> ast::ExprId {
1179 let name = self.name(text);
1180 self.ast.expr(ast::Expr::Name(name), Span::DUMMY)
1181 }
1182
1183 fn keywords(&mut self, written: &[BuiltinSet]) -> DeclSpecsId {
1185 let mut builtin = Builtin::NONE;
1186 for &keyword in written {
1187 builtin = builtin.add(keyword).expect("a keyword written once");
1188 }
1189 let mut specs = DeclSpecs::empty(Span::DUMMY);
1190 specs.ty = TypeSpec::Builtin(builtin);
1191 self.ast.add_specs(specs)
1192 }
1193
1194 fn int_specs(&mut self) -> DeclSpecsId {
1196 self.keywords(&[BuiltinSet::INT])
1197 }
1198
1199 fn declarator(&mut self, name: Option<&str>, derived: &[Derived]) -> DeclaratorId {
1200 let name = name.map(|text| self.name(text));
1201 let derived = self.ast.add_derived_list(derived);
1202 self.ast.add_declarator(Declarator {
1203 name,
1204 name_span: Span::DUMMY,
1205 derived,
1206 span: Span::DUMMY,
1207 })
1208 }
1209
1210 fn local(&mut self, specs: DeclSpecsId, name: &str) -> ast::DeclId {
1212 let declarator = self.declarator(Some(name), &[]);
1213 let item = ast::InitDeclarator {
1214 declarator,
1215 init: None,
1216 asm_label: None,
1217 attrs: AttrList::EMPTY,
1218 span: Span::DUMMY,
1219 };
1220 let declarators = self.ast.add_init_declarator_list(&[item]);
1221 self.ast.decl(ast::Decl::Var { specs, declarators }, Span::DUMMY)
1222 }
1223
1224 fn cast(&mut self, specs: DeclSpecsId, value: ast::ExprId) -> ast::ExprId {
1226 let declarator = self.declarator(None, &[]);
1227 let ty = self.ast.add_type_name(ast::TypeName { specs, declarator, span: Span::DUMMY });
1228 self.ast.expr(ast::Expr::Cast { ty, operand: value }, Span::DUMMY)
1229 }
1230
1231 fn stmt(&mut self, stmt: ast::Stmt) -> ast::StmtId {
1232 self.ast.stmt(stmt, Span::DUMMY)
1233 }
1234
1235 fn block(&mut self, body: &[ast::StmtId]) -> ast::StmtId {
1237 let body = self.ast.add_stmt_list(body);
1238 self.stmt(ast::Stmt::Compound(body))
1239 }
1240
1241 fn expr_stmt(&mut self, value: ast::ExprId) -> ast::StmtId {
1243 self.stmt(ast::Stmt::Expr(value))
1244 }
1245
1246 fn labelled(&mut self, text: &str, body: Option<ast::StmtId>) -> ast::StmtId {
1248 let name = self.name(text);
1249 self.stmt(ast::Stmt::Label { name, body, attrs: AttrList::EMPTY })
1250 }
1251
1252 fn goto(&mut self, text: &str) -> ast::StmtId {
1254 let name = self.name(text);
1255 self.stmt(ast::Stmt::Goto(name))
1256 }
1257
1258 fn local_labels(&mut self, names: &[&str]) -> ast::StmtId {
1260 let names: Vec<Symbol> = names.iter().map(|text| self.name(text)).collect();
1261 let names = self.ast.add_symbol_list(&names);
1262 self.stmt(ast::Stmt::LocalLabels(names))
1263 }
1264
1265 fn case(&mut self, lo: u128, hi: Option<u128>, body: Option<ast::StmtId>) -> ast::StmtId {
1267 let lo = self.int(lo);
1268 let hi = hi.map(|hi| self.int(hi));
1269 self.stmt(ast::Stmt::Case { lo, hi, body })
1270 }
1271
1272 fn switch(&mut self, scrutinee: ast::ExprId, body: &[ast::StmtId]) -> ast::StmtId {
1274 let body = self.block(body);
1275 self.stmt(ast::Stmt::Switch { scrutinee, body })
1276 }
1277
1278 fn checker(&self) -> Checker<'_> {
1279 Checker::new(&self.ast, Context::new(&self.names, &self.target, Std::C23))
1280 }
1281 }
1282
1283 fn dump(checker: &Checker<'_>, id: StmtId) -> String {
1285 let mut printer = Printer::new(&checker.tast, &checker.types, checker.cx.names);
1286 printer.stmt(id);
1287 printer.finish()
1288 }
1289
1290 fn messages(checker: &Checker<'_>) -> Vec<String> {
1292 checker
1293 .errors
1294 .diagnostics()
1295 .iter()
1296 .flat_map(|d| {
1297 std::iter::once(d.message.clone())
1298 .chain(d.children.iter().map(|n| n.message.clone()))
1299 })
1300 .collect()
1301 }
1302
1303 fn message(checker: &Checker<'_>) -> String {
1305 let mut reported = messages(checker);
1306 assert_eq!(reported.len(), 1, "expected exactly one diagnostic, got {reported:?}");
1307 reported.pop().expect("one message")
1308 }
1309
1310 fn reported(checker: &Checker<'_>) -> Vec<String> {
1314 checker
1315 .errors
1316 .diagnostics()
1317 .iter()
1318 .map(|d| format!("{}: {}", d.severity.as_str(), d.message))
1319 .collect()
1320 }
1321
1322 #[test]
1323 fn a_block_is_a_scope_and_a_name_declared_in_one_is_gone_after_it() {
1324 let mut f = Fixture::new();
1325 let specs = f.int_specs();
1326 let declared = f.local(specs, "x");
1327 let declared = f.stmt(ast::Stmt::Decl(declared));
1328 let inner = f.block(&[declared]);
1329 let use_x = f.use_name("x");
1330 let after = f.expr_stmt(use_x);
1331 let outer = f.block(&[inner, after]);
1332
1333 let mut c = f.checker();
1334 let void = c.types.void();
1335 c.check_stmt(void, outer);
1336
1337 assert_eq!(message(&c), "'x' undeclared (first use in this function)");
1338 }
1339
1340 #[test]
1341 fn a_name_nobody_declared_is_reported_once_per_function_and_not_once_per_use() {
1342 let mut f = Fixture::new();
1346 let first = f.use_name("nope");
1347 let first = f.expr_stmt(first);
1348 let second = f.use_name("nope");
1349 let second = f.expr_stmt(second);
1350 let body = f.block(&[first, second]);
1351
1352 let mut c = f.checker();
1353 let void = c.types.void();
1354 let previous = c.open_body(Enclosing::returning(void));
1355 c.check_stmt(void, body);
1356 c.close_body(previous);
1357
1358 assert_eq!(message(&c), "'nope' undeclared (first use in this function)");
1359 }
1360
1361 #[test]
1362 fn an_expression_statement_holds_the_value_and_not_a_conversion_of_it_to_void() {
1363 let mut f = Fixture::new();
1364 let one = f.int(1);
1365 let stmt = f.expr_stmt(one);
1366
1367 let mut c = f.checker();
1368 let void = c.types.void();
1369 let id = c.check_stmt(void, stmt);
1370
1371 assert_eq!(dump(&c, id), "expr\n const 1 : int\n");
1372 assert!(c.errors.is_empty());
1373 }
1374
1375 #[test]
1376 fn a_statement_expression_has_the_type_of_its_last_statement() {
1377 let mut f = Fixture::new();
1378 let one = f.int(1);
1379 let inner = f.expr_stmt(one);
1380 let body = f.block(&[inner]);
1381 let value = f.ast.expr(ast::Expr::StmtExpr(body), Span::DUMMY);
1382 let stmt = f.expr_stmt(value);
1383
1384 let mut c = f.checker();
1385 let void = c.types.void();
1386 let id = c.check_stmt(void, stmt);
1387
1388 assert_eq!(
1389 dump(&c, id),
1390 "expr\n stmt-expr : int\n block\n expr\n const 1 : int\n"
1391 );
1392 assert!(c.errors.is_empty());
1393 }
1394
1395 #[test]
1396 fn a_statement_expression_that_ends_in_something_else_is_void() {
1397 let mut f = Fixture::new();
1398 let body = f.block(&[]);
1399 let value = f.ast.expr(ast::Expr::StmtExpr(body), Span::DUMMY);
1400 let stmt = f.expr_stmt(value);
1401
1402 let mut c = f.checker();
1403 let void = c.types.void();
1404 let id = c.check_stmt(void, stmt);
1405
1406 assert_eq!(dump(&c, id), "expr\n stmt-expr : void\n block\n");
1407 assert!(c.errors.is_empty());
1408 }
1409
1410 #[test]
1411 fn the_declaration_in_a_for_clause_scopes_to_the_loop_and_not_to_what_follows() {
1412 let mut f = Fixture::new();
1413 let specs = f.int_specs();
1414 let declared = f.local(specs, "i");
1415 let empty = f.stmt(ast::Stmt::Empty);
1416 let loop_stmt = f.stmt(ast::Stmt::For {
1417 init: ForInit::Decl(declared),
1418 cond: None,
1419 step: None,
1420 body: empty,
1421 });
1422 let use_i = f.use_name("i");
1423 let after = f.expr_stmt(use_i);
1424 let outer = f.block(&[loop_stmt, after]);
1425
1426 let mut c = f.checker();
1427 let void = c.types.void();
1428 c.check_stmt(void, outer);
1429
1430 assert_eq!(message(&c), "'i' undeclared (first use in this function)");
1431 }
1432
1433 #[test]
1434 fn a_static_in_a_for_clause_is_accepted_and_only_pedantic_says_anything_about_it() {
1435 let mut f = Fixture::new();
1436 let mut specs = DeclSpecs::empty(Span::DUMMY);
1437 let builtin = Builtin::NONE.add(BuiltinSet::INT).expect("a keyword written once");
1438 specs.ty = TypeSpec::Builtin(builtin);
1439 specs.storage = Some(StorageClass::Static);
1440 let specs = f.ast.add_specs(specs);
1441 let declared = f.local(specs, "i");
1442 let empty = f.stmt(ast::Stmt::Empty);
1443 let loop_stmt = f.stmt(ast::Stmt::For {
1444 init: ForInit::Decl(declared),
1445 cond: None,
1446 step: None,
1447 body: empty,
1448 });
1449
1450 let mut c = f.checker();
1451 let void = c.types.void();
1452 c.check_stmt(void, loop_stmt);
1453 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1454
1455 let mut c = f.checker();
1456 c.cx.pedantic = true;
1457 let void = c.types.void();
1458 c.check_stmt(void, loop_stmt);
1459 assert_eq!(
1460 reported(&c),
1461 ["warning: declaration of static variable 'i' in 'for' loop initial declaration"]
1462 );
1463 }
1464
1465 #[test]
1466 fn continue_needs_a_loop_and_is_not_satisfied_by_a_switch() {
1467 let mut f = Fixture::new();
1468 let one = f.int(1);
1469 let go_on = f.stmt(ast::Stmt::Continue);
1470 let case = f.stmt(ast::Stmt::Case { lo: one, hi: None, body: Some(go_on) });
1471 let scrutinee = f.int(0);
1472 let switch = f.switch(scrutinee, &[case]);
1473
1474 let mut c = f.checker();
1475 let void = c.types.void();
1476 c.check_stmt(void, switch);
1477
1478 assert_eq!(message(&c), "continue statement not within a loop");
1479 }
1480
1481 #[test]
1482 fn break_is_satisfied_by_a_switch_and_reported_where_there_is_neither() {
1483 let mut f = Fixture::new();
1484 let stop = f.stmt(ast::Stmt::Break);
1485 let scrutinee = f.int(0);
1486 let switch = f.switch(scrutinee, &[stop]);
1487 let loose = f.stmt(ast::Stmt::Break);
1488
1489 let mut c = f.checker();
1490 let void = c.types.void();
1491 c.check_stmt(void, switch);
1492 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1493
1494 let mut c = f.checker();
1495 let void = c.types.void();
1496 c.check_stmt(void, loose);
1497 assert_eq!(message(&c), "break statement not within loop or switch");
1498 }
1499
1500 #[test]
1501 fn a_goto_resolves_to_a_label_the_function_defines_further_down() {
1502 let mut f = Fixture::new();
1503 let jump = f.goto("done");
1504 let empty = f.stmt(ast::Stmt::Empty);
1505 let target = f.labelled("done", Some(empty));
1506 let body = f.block(&[jump, target]);
1507
1508 let mut c = f.checker();
1509 let void = c.types.void();
1510 let id = c.check_stmt(void, body);
1511
1512 assert_eq!(dump(&c, id), "block\n goto #0 done\n label #0 done\n empty\n");
1513 assert!(c.errors.is_empty());
1514 }
1515
1516 #[test]
1517 fn a_label_that_is_jumped_to_and_never_defined_is_reported_at_the_jump() {
1518 let mut f = Fixture::new();
1519 let jump = f.goto("away");
1520 let body = f.block(&[jump]);
1521
1522 let mut c = f.checker();
1523 let void = c.types.void();
1524 c.check_stmt(void, body);
1525
1526 assert_eq!(message(&c), "label 'away' used but not defined");
1527 }
1528
1529 #[test]
1530 fn the_address_of_a_label_is_a_use_of_it_and_not_a_definition() {
1531 let mut f = Fixture::new();
1532 let away = f.name("away");
1533 let value = f.ast.expr(ast::Expr::LabelAddr(away), Span::DUMMY);
1534 let stmt = f.expr_stmt(value);
1535
1536 let mut c = f.checker();
1537 let void = c.types.void();
1538 let id = c.check_stmt(void, stmt);
1539
1540 assert_eq!(dump(&c, id), "expr\n label-addr #0 away : void *\n");
1541 assert_eq!(message(&c), "label 'away' used but not defined");
1542 }
1543
1544 #[test]
1545 fn one_label_defined_twice_is_an_error_that_points_at_the_first() {
1546 let mut f = Fixture::new();
1547 let first = f.labelled("here", None);
1548 let second = f.labelled("here", None);
1549 let body = f.block(&[first, second]);
1550
1551 let mut c = f.checker();
1552 let void = c.types.void();
1553 c.check_stmt(void, body);
1554
1555 assert_eq!(
1556 messages(&c),
1557 ["duplicate label 'here'", "previous definition of 'here' with type 'void'",]
1558 );
1559 }
1560
1561 #[test]
1562 fn a_local_label_is_undone_when_its_block_ends_so_two_blocks_may_declare_one_name() {
1563 let mut f = Fixture::new();
1564 let sibling = |f: &mut Fixture| {
1565 let declared = f.local_labels(&["done"]);
1566 let jump = f.goto("done");
1567 let target = f.labelled("done", None);
1568 f.block(&[declared, jump, target])
1569 };
1570 let first = sibling(&mut f);
1571 let second = sibling(&mut f);
1572 let body = f.block(&[first, second]);
1573
1574 let mut c = f.checker();
1575 let void = c.types.void();
1576 let id = c.check_stmt(void, body);
1577
1578 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1579 assert_eq!(
1580 dump(&c, id),
1581 "block\n block\n empty\n goto #0 done\n label #0 done\n empty\n \
1582 block\n empty\n goto #1 done\n label #1 done\n empty\n"
1583 );
1584 }
1585
1586 #[test]
1587 fn a_local_label_that_nothing_defines_is_reported_when_its_block_ends() {
1588 let mut f = Fixture::new();
1589 let declared = f.local_labels(&["done"]);
1590 let jump = f.goto("done");
1591 let inner = f.block(&[declared, jump]);
1592 let target = f.labelled("done", None);
1593 let body = f.block(&[inner, target]);
1594
1595 let mut c = f.checker();
1596 let void = c.types.void();
1597 c.check_stmt(void, body);
1598
1599 assert_eq!(message(&c), "label 'done' used but not defined");
1600 }
1601
1602 #[test]
1603 fn a_computed_goto_wants_something_that_could_be_an_address() {
1604 let mut f = Fixture::new();
1605 let specs = f.keywords(&[BuiltinSet::DOUBLE]);
1606 let zero = f.int(0);
1607 let target = f.cast(specs, zero);
1608 let stmt = f.stmt(ast::Stmt::GotoExpr(target));
1609
1610 let mut c = f.checker();
1611 let void = c.types.void();
1612 c.check_stmt(void, stmt);
1613
1614 assert_eq!(message(&c), "computed goto must be pointer type");
1615 }
1616
1617 #[test]
1618 fn a_switch_on_something_that_is_not_an_integer_is_an_error() {
1619 let mut f = Fixture::new();
1620 let specs = f.keywords(&[BuiltinSet::DOUBLE]);
1621 let zero = f.int(0);
1622 let scrutinee = f.cast(specs, zero);
1623 let switch = f.switch(scrutinee, &[]);
1624
1625 let mut c = f.checker();
1626 let void = c.types.void();
1627 c.check_stmt(void, switch);
1628
1629 assert_eq!(message(&c), "switch quantity not an integer");
1630 }
1631
1632 #[test]
1633 fn the_cases_of_a_switch_are_one_table_in_the_order_they_were_written() {
1634 let mut f = Fixture::new();
1635 let first = f.case(1, None, None);
1636 let second = f.case(4, Some(6), None);
1637 let default = f.stmt(ast::Stmt::Default { body: None });
1638 let scrutinee = f.int(0);
1639 let switch = f.switch(scrutinee, &[first, second, default]);
1640
1641 let mut c = f.checker();
1642 let void = c.types.void();
1643 let id = c.check_stmt(void, switch);
1644
1645 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1646 assert_eq!(
1647 dump(&c, id),
1648 "switch\n cond\n const 0 : int\n cases\n case #0 1\n case #1 4 ... 6\n \
1649 default\n body\n block\n case #0\n empty\n case #1\n \
1650 empty\n default\n empty\n"
1651 );
1652 }
1653
1654 #[test]
1655 fn two_labels_on_one_statement_are_in_the_table_the_way_round_they_were_written() {
1656 let mut f = Fixture::new();
1659 let inner = f.case(2, None, None);
1660 let outer = f.case(1, None, Some(inner));
1661 let scrutinee = f.int(0);
1662 let switch = f.switch(scrutinee, &[outer]);
1663
1664 let mut c = f.checker();
1665 let void = c.types.void();
1666 let id = c.check_stmt(void, switch);
1667
1668 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1669 assert_eq!(
1670 dump(&c, id),
1671 "switch\n cond\n const 0 : int\n cases\n case #0 1\n case #1 2\n body\n \
1672 block\n case #0\n case #1\n empty\n"
1673 );
1674 }
1675
1676 #[test]
1677 fn a_case_that_covers_a_value_an_earlier_one_covers_is_a_duplicate() {
1678 let mut f = Fixture::new();
1679 let first = f.case(1, Some(3), None);
1680 let second = f.case(2, None, None);
1681 let scrutinee = f.int(0);
1682 let switch = f.switch(scrutinee, &[first, second]);
1683
1684 let mut c = f.checker();
1685 let void = c.types.void();
1686 c.check_stmt(void, switch);
1687
1688 assert_eq!(messages(&c), ["duplicate case value", "previously used here"]);
1689 }
1690
1691 #[test]
1692 fn a_case_outside_a_switch_is_an_error_and_so_is_a_default() {
1693 let mut f = Fixture::new();
1694 let case = f.case(1, None, None);
1695 let default = f.stmt(ast::Stmt::Default { body: None });
1696 let body = f.block(&[case, default]);
1697
1698 let mut c = f.checker();
1699 let void = c.types.void();
1700 c.check_stmt(void, body);
1701
1702 assert_eq!(
1703 messages(&c),
1704 [
1705 "case label not within a switch statement",
1706 "'default' label not within a switch statement",
1707 ]
1708 );
1709 }
1710
1711 #[test]
1712 fn a_case_label_that_is_not_a_constant_is_an_error() {
1713 let mut f = Fixture::new();
1714 let specs = f.int_specs();
1715 let declared = f.local(specs, "n");
1716 let declared = f.stmt(ast::Stmt::Decl(declared));
1717 let use_n = f.use_name("n");
1718 let case = f.stmt(ast::Stmt::Case { lo: use_n, hi: None, body: None });
1719 let scrutinee = f.int(0);
1720 let switch = f.switch(scrutinee, &[case]);
1721 let body = f.block(&[declared, switch]);
1722
1723 let mut c = f.checker();
1724 let void = c.types.void();
1725 c.check_stmt(void, body);
1726
1727 assert_eq!(message(&c), "case label does not reduce to an integer constant");
1728 }
1729
1730 #[test]
1731 fn a_case_range_that_runs_backwards_is_empty() {
1732 let mut f = Fixture::new();
1733 let case = f.case(6, Some(4), None);
1734 let scrutinee = f.int(0);
1735 let switch = f.switch(scrutinee, &[case]);
1736
1737 let mut c = f.checker();
1738 let void = c.types.void();
1739 c.check_stmt(void, switch);
1740
1741 assert_eq!(reported(&c), ["warning: empty range specified"]);
1742 }
1743
1744 #[test]
1745 fn a_case_is_measured_against_the_type_that_was_written_and_not_the_promoted_one() {
1746 let mut f = Fixture::new();
1747 let specs = f.keywords(&[BuiltinSet::CHAR]);
1748 let zero = f.int(0);
1749 let scrutinee = f.cast(specs, zero);
1750 let case = f.case(300, None, None);
1751 let switch = f.switch(scrutinee, &[case]);
1752
1753 let mut c = f.checker();
1754 let void = c.types.void();
1755 c.check_stmt(void, switch);
1756
1757 assert_eq!(reported(&c), ["warning: case label value exceeds maximum value for type"]);
1758 }
1759
1760 #[test]
1761 fn two_defaults_in_one_switch_are_an_error_that_points_at_the_first() {
1762 let mut f = Fixture::new();
1763 let first = f.stmt(ast::Stmt::Default { body: None });
1764 let second = f.stmt(ast::Stmt::Default { body: None });
1765 let scrutinee = f.int(0);
1766 let switch = f.switch(scrutinee, &[first, second]);
1767
1768 let mut c = f.checker();
1769 let void = c.types.void();
1770 c.check_stmt(void, switch);
1771
1772 assert_eq!(
1773 messages(&c),
1774 ["multiple default labels in one switch", "this is the first default label"]
1775 );
1776 }
1777
1778 #[test]
1779 fn a_nested_switch_keeps_its_cases_to_itself() {
1780 let mut f = Fixture::new();
1781 let inner_case = f.case(1, None, None);
1782 let inner_scrutinee = f.int(0);
1783 let inner = f.switch(inner_scrutinee, &[inner_case]);
1784 let outer_case = f.case(1, None, Some(inner));
1785 let outer_scrutinee = f.int(0);
1786 let outer = f.switch(outer_scrutinee, &[outer_case]);
1787
1788 let mut c = f.checker();
1789 let void = c.types.void();
1790 let id = c.check_stmt(void, outer);
1791
1792 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1793 assert_eq!(
1794 dump(&c, id),
1795 "switch\n cond\n const 0 : int\n cases\n case #1 1\n body\n block\n \
1796 case #1\n switch\n cond\n const 0 : int\n \
1797 cases\n case #0 1\n body\n block\n case \
1798 #0\n empty\n"
1799 );
1800 }
1801
1802 #[test]
1803 fn a_bare_return_from_a_function_that_promised_a_value_is_an_error() {
1804 let mut f = Fixture::new();
1805 let stmt = f.stmt(ast::Stmt::Return(None));
1806
1807 let mut c = f.checker();
1808 let int = c.int();
1809 c.check_stmt(int, stmt);
1810
1811 assert_eq!(reported(&c), ["error: 'return' with no value, in function returning non-void"]);
1812 assert_eq!(messages(&c).len(), 2, "the note is attached to it");
1813 }
1814
1815 #[test]
1816 fn a_value_returned_from_a_function_returning_void_is_an_error() {
1817 let mut f = Fixture::new();
1818 let one = f.int(1);
1819 let stmt = f.stmt(ast::Stmt::Return(Some(one)));
1820
1821 let mut c = f.checker();
1822 let void = c.types.void();
1823 c.check_stmt(void, stmt);
1824
1825 assert_eq!(reported(&c), ["error: 'return' with a value, in function returning void"]);
1826 }
1827
1828 #[test]
1829 fn a_void_value_returned_from_a_function_returning_void_is_what_a_wrapper_writes() {
1830 let mut f = Fixture::new();
1831 let specs = f.keywords(&[BuiltinSet::VOID]);
1832 let one = f.int(1);
1833 let value = f.cast(specs, one);
1834 let stmt = f.stmt(ast::Stmt::Return(Some(value)));
1835
1836 let mut c = f.checker();
1837 let void = c.types.void();
1838 c.check_stmt(void, stmt);
1839
1840 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1841 }
1842
1843 #[test]
1844 fn a_returned_value_is_converted_to_the_return_type() {
1845 let mut f = Fixture::new();
1846 let one = f.int(1);
1847 let stmt = f.stmt(ast::Stmt::Return(Some(one)));
1848
1849 let mut c = f.checker();
1850 let long = c.types.int(IntKind::Long);
1851 let id = c.check_stmt(long, stmt);
1852
1853 assert_eq!(dump(&c, id), "return\n convert arithmetic : long\n const 1 : int\n");
1854 assert!(c.errors.is_empty());
1855 }
1856}