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 modified: Vec<Modified>,
107 inside: Option<usize>,
110 landings: HashMap<LabelId, Landing>,
112 jumps: Vec<Jump>,
114}
115
116#[derive(Debug, Clone, Copy)]
118struct Modified {
119 name: Option<Symbol>,
121 at: Span,
123 outer: Option<usize>,
125}
126
127#[derive(Debug, Clone, Copy)]
129struct Landing {
130 at: Span,
132 inside: Option<usize>,
134}
135
136#[derive(Debug, Clone, Copy)]
138struct Jump {
139 to: LabelId,
141 at: Span,
143 inside: Option<usize>,
145}
146
147#[derive(Debug, Clone, Copy)]
149pub(in crate::check) struct Enclosing {
150 pub ret: TypeId,
152 pub at: Span,
154 pub variadic: bool,
156 pub last_param: Option<DeclId>,
158 pub params: DeclList,
160 pub name: Option<Symbol>,
162}
163
164impl Enclosing {
165 pub(in crate::check) fn returning(ret: TypeId) -> Enclosing {
168 Enclosing {
169 ret,
170 at: Span::DUMMY,
171 variadic: false,
172 last_param: None,
173 params: DeclList::EMPTY,
174 name: None,
175 }
176 }
177}
178
179#[derive(Debug, Clone, Copy)]
181struct Labelled {
182 id: LabelId,
184 defined: Option<Span>,
186 at: Span,
188}
189
190#[derive(Debug)]
192struct Switch {
193 ty: TypeId,
195 range: Option<IntegerInfo>,
200 cases: Vec<Case>,
202 spans: Vec<Span>,
204 labels: Vec<StmtId>,
207 default: Option<(StmtId, Span)>,
209}
210
211impl Checker<'_> {
212 pub fn check_stmt(&mut self, ret: TypeId, id: ast::StmtId) -> StmtId {
218 let previous = self.open_body(Enclosing::returning(ret));
219 let stmt = self.stmt(id);
220 self.close_body(previous);
221 stmt
222 }
223
224 pub(in crate::check) fn stmt(&mut self, id: ast::StmtId) -> StmtId {
226 let span = self.ast.stmt_span(id);
227 let node = match self.ast[id] {
228 ast::Stmt::Error => Stmt::Error,
229 ast::Stmt::Empty => Stmt::Empty,
230 ast::Stmt::Expr(value) => {
231 let value = self.expr(value);
232 Stmt::Expr(self.value(value))
233 }
234 ast::Stmt::Decl(decl) => {
235 let decls = self.check_decl(decl);
236 self.variably_modified(decls);
237 Stmt::Decls(decls)
238 }
239 ast::Stmt::Compound(body) => Stmt::Block(self.block(body)),
240 ast::Stmt::If { cond, then, otherwise } => {
241 let cond = self.controlling(cond);
242 let then = self.stmt(then);
243 Stmt::If { cond, then, otherwise: otherwise.map(|id| self.stmt(id)) }
244 }
245 ast::Stmt::Switch { scrutinee, body } => self.switch(scrutinee, body),
246 ast::Stmt::While { cond, body } => {
247 let cond = self.controlling(cond);
248 Stmt::While { cond, body: self.loop_body(body) }
249 }
250 ast::Stmt::DoWhile { body, cond } => {
251 let body = self.loop_body(body);
252 Stmt::DoWhile { body, cond: self.controlling(cond) }
253 }
254 ast::Stmt::For { init, cond, step, body } => self.for_loop(init, cond, step, body),
255 ast::Stmt::Goto(name) => Stmt::Goto(self.jump(name, span)),
256 ast::Stmt::GotoExpr(target) => self.computed_goto(target),
257 ast::Stmt::Continue => self.continue_stmt(span),
258 ast::Stmt::Break => self.break_stmt(span),
259 ast::Stmt::Return(value) => self.return_stmt(value, span),
260 ast::Stmt::Label { name, body, .. } => self.labelled(name, body, span),
261 ast::Stmt::Case { lo, hi, body } => self.case(lo, hi, body, span),
262 ast::Stmt::Default { body } => self.default(body, span),
263 ast::Stmt::LocalLabels(names) => {
264 self.local_labels(names, span);
265 Stmt::Empty
266 }
267 ast::Stmt::Asm(asm) => self.asm(asm, span),
268 };
269 let stmt = self.tast.stmt(node, span);
270 if matches!(node, Stmt::Case { .. }) {
274 if let Some(switch) = self.switches() {
275 switch.labels.push(stmt);
276 }
277 }
278 stmt
279 }
280
281 pub(in crate::check) fn stmt_expr(&mut self, id: ast::StmtId, span: Span) -> ExprId {
289 let stmt = self.stmt(id);
290 let ty = match self.tast[stmt] {
291 Stmt::Block(body) => match self.tast[body].last() {
292 Some(&last) => match self.tast[last] {
293 Stmt::Expr(value) => self.tast[value].ty,
294 _ => self.types.void(),
295 },
296 None => self.types.void(),
297 },
298 _ => self.types.void(),
299 };
300 self.tast.expr(Expr::new(ExprKind::StmtExpr(stmt), ty, Category::Rvalue), span)
301 }
302
303 pub(in crate::check) fn label_addr(&mut self, name: Symbol, span: Span) -> ExprId {
308 let label = self.label(name, span);
309 let ty = self.types.pointer(self.types.void());
310 self.tast.expr(Expr::new(ExprKind::LabelAddr(label), ty, Category::Rvalue), span)
311 }
312
313 pub(in crate::check) fn open_body(&mut self, func: Enclosing) -> Option<Body> {
318 let body = Body {
319 ret: func.ret,
320 at: func.at,
321 variadic: func.variadic,
322 last_param: func.last_param,
323 params: func.params,
324 name: func.name,
325 func_name: [None; FUNCTION_NAMES.len()],
326 labels: HashMap::new(),
327 shadowed: Vec::new(),
328 blocks: Vec::new(),
329 switches: Vec::new(),
330 loops: 0,
331 undeclared: HashSet::new(),
332 modified: Vec::new(),
333 inside: None,
334 landings: HashMap::new(),
335 jumps: Vec::new(),
336 };
337 self.body.replace(body)
338 }
339
340 pub(in crate::check) fn in_variadic_function(&self) -> bool {
345 self.body.as_ref().is_some_and(|body| body.variadic)
346 }
347
348 pub(in crate::check) fn last_named_parameter(&self) -> Option<DeclId> {
351 self.body.as_ref().and_then(|body| body.last_param)
352 }
353
354 pub(in crate::check) fn function_name_string(&mut self, which: usize) -> Option<StrId> {
361 let name = self.body.as_ref()?.name?;
362 if let Some(id) = self.body.as_ref().and_then(|body| body.func_name[which]) {
363 return Some(id);
364 }
365 let elements = self.text(name).chars().map(|c| c as u32).collect();
366 let literal =
367 StringLiteral { elements, encoding: Encoding::Plain, remarks: Remarks::default() };
368 let id = self.tast.add_string(literal);
369 if let Some(body) = &mut self.body {
370 body.func_name[which] = Some(id);
371 }
372 Some(id)
373 }
374
375 pub(in crate::check) fn is_parameter(&self, decl: DeclId) -> bool {
379 self.body.as_ref().is_some_and(|body| self.tast[body.params].contains(&decl))
380 }
381
382 pub(in crate::check) fn first_undeclared_use(&mut self, name: Symbol) -> bool {
388 match &mut self.body {
389 Some(body) => body.undeclared.insert(name),
390 None => true,
391 }
392 }
393
394 pub(in crate::check) fn close_body(&mut self, previous: Option<Body>) {
396 let Some(body) = mem::replace(&mut self.body, previous) else {
397 return;
398 };
399 let mut undefined: Vec<Labelled> =
402 body.labels.into_values().filter(|label| label.defined.is_none()).collect();
403 undefined.sort_by_key(|label| label.at.lo);
404 for label in undefined {
405 self.undefined_label(label);
406 }
407
408 for jump in &body.jumps {
411 let Some(landing) = body.landings.get(&jump.to) else { continue };
412 let Some(entered) = landing.inside else { continue };
413 if open_at(&body.modified, jump.inside, entered) {
414 continue;
415 }
416 self.jumped_into_scope(*jump, *landing, body.modified[entered]);
417 }
418 }
419
420 fn jumped_into_scope(&mut self, jump: Jump, landing: Landing, entered: Modified) {
425 let label = self.text(self.tast[jump.to].name).to_owned();
426 let mut diag =
427 Diagnostic::error("jump into scope of identifier with variably modified type", jump.at)
428 .with_code("E0684")
429 .note(format!("label '{label}' defined here"), landing.at);
430 if let Some(name) = entered.name {
431 let spelled = self.text(name).to_owned();
432 diag = diag.note(format!("'{spelled}' declared here"), entered.at);
433 }
434 self.report(diag);
435 }
436
437 pub(in crate::check) fn body_block(&mut self, body: ast::StmtId) -> StmtId {
443 let span = self.ast.stmt_span(body);
444 let ast::Stmt::Compound(list) = self.ast[body] else {
445 return self.stmt(body);
446 };
447 let list = self.statements(list);
448 self.tast.stmt(Stmt::Block(list), span)
449 }
450
451 fn block(&mut self, body: ast::StmtList) -> crate::stmt::StmtList {
453 self.scopes.push();
454 let outer = self.open_scope();
455 let list = self.statements(body);
456 self.close_scope(outer);
457 self.scopes.pop();
458 list
459 }
460
461 fn open_scope(&self) -> Option<usize> {
467 self.body.as_ref().and_then(|state| state.inside)
468 }
469
470 fn close_scope(&mut self, outer: Option<usize>) {
472 if let Some(state) = self.body.as_mut() {
473 state.inside = outer;
474 }
475 }
476
477 fn variably_modified(&mut self, decls: DeclList) {
484 let ids = self.tast[decls].to_vec();
485 for decl in ids {
486 if !self.is_variably_modified(self.tast[decl].ty) {
487 continue;
488 }
489 let name = self.tast[decl].name;
490 let at = self.tast.decl_span(decl);
491 if let Some(state) = self.body.as_mut() {
492 let outer = state.inside;
493 state.modified.push(Modified { name, at, outer });
494 state.inside = Some(state.modified.len() - 1);
495 }
496 }
497 }
498
499 fn statements(&mut self, body: ast::StmtList) -> crate::stmt::StmtList {
501 if let Some(state) = self.body.as_mut() {
502 let mark = state.shadowed.len();
503 state.blocks.push(mark);
504 }
505 let ids = self.ast[body].to_vec();
506 let mut stmts = Vec::with_capacity(ids.len());
507 for id in ids {
508 stmts.push(self.stmt(id));
509 }
510 self.end_block();
511 self.tast.add_stmt_refs(&stmts)
512 }
513
514 fn end_block(&mut self) {
516 let Some(body) = self.body.as_mut() else {
517 return;
518 };
519 let Some(mark) = body.blocks.pop() else {
520 return;
521 };
522 let mut gone = Vec::new();
523 while body.shadowed.len() > mark {
524 let (name, previous) = body.shadowed.pop().expect("a saved binding");
525 let local = match previous {
526 Some(previous) => body.labels.insert(name, previous),
527 None => body.labels.remove(&name),
528 };
529 if let Some(local) = local {
530 if local.defined.is_none() {
531 gone.push(local);
532 }
533 }
534 }
535 gone.sort_by_key(|label| label.at.lo);
536 for label in gone {
537 self.undefined_label(label);
538 }
539 }
540
541 fn loop_body(&mut self, body: ast::StmtId) -> StmtId {
543 if let Some(state) = self.body.as_mut() {
544 state.loops += 1;
545 }
546 let body = self.stmt(body);
547 if let Some(state) = self.body.as_mut() {
548 state.loops -= 1;
549 }
550 body
551 }
552
553 fn for_loop(
555 &mut self,
556 init: ForInit,
557 cond: Option<ast::ExprId>,
558 step: Option<ast::ExprId>,
559 body: ast::StmtId,
560 ) -> Stmt {
561 self.scopes.push();
564 let outer = self.open_scope();
565 let init = match init {
566 ForInit::None => None,
567 ForInit::Expr(value) => {
568 let span = self.ast.expr_span(value);
569 let value = self.expr(value);
570 let value = self.value(value);
571 Some(self.tast.stmt(Stmt::Expr(value), span))
572 }
573 ForInit::Decl(decl) => {
574 let span = self.ast.decl_span(decl);
575 let decls = self.check_decl(decl);
576 self.variably_modified(decls);
577 self.check_loop_declaration(decl);
578 Some(self.tast.stmt(Stmt::Decls(decls), span))
579 }
580 };
581 let cond = cond.map(|cond| self.controlling(cond));
582 let step = step.map(|step| {
583 let step = self.expr(step);
584 self.value(step)
585 });
586 let body = self.loop_body(body);
587 self.close_scope(outer);
588 self.scopes.pop();
589 Stmt::For { init, cond, step, body }
590 }
591
592 fn check_loop_declaration(&mut self, decl: ast::DeclId) {
603 if !self.cx.pedantic {
604 return;
605 }
606 let ast::Decl::Var { specs, declarators } = self.ast[decl] else {
607 return;
608 };
609 let specs = self.ast[specs];
610 let word = match specs.storage {
611 _ if specs.is_typedef() => "non-variable",
612 Some(StorageClass::Static) => "static variable",
613 Some(StorageClass::Extern) => "'extern' variable",
614 _ => return,
615 };
616 let ast = self.ast;
617 for &item in &ast[declarators] {
618 let node = ast[item.declarator];
619 let Some(name) = node.name else { continue };
620 let spelled = self.text(name).to_owned();
621 self.report(
622 Diagnostic::warning(
623 format!("declaration of {word} '{spelled}' in 'for' loop initial declaration"),
624 node.name_span,
625 )
626 .with_code("E0619"),
627 );
628 }
629 }
630
631 fn switch(&mut self, scrutinee: ast::ExprId, body: ast::StmtId) -> Stmt {
633 let at = self.ast.expr_span(scrutinee);
634 let cond = self.expr(scrutinee);
635 let cond = self.value(cond);
636 let range = eval::int_shape(&self.types, self.tast[cond].ty, self.cx.target);
640 let cond = self.conv().promote(cond);
641 let ty = self.tast[cond].ty;
642 let cond = if self.is_poisoned(cond) || is_integer(&self.types, ty) {
643 cond
644 } else {
645 self.report(Diagnostic::error("switch quantity not an integer", at).with_code("E0620"));
646 self.poison(at)
647 };
648 let ty = if is_integer(&self.types, ty) { ty } else { self.int() };
652 if let Some(state) = self.body.as_mut() {
653 state.switches.push(Switch {
654 ty,
655 range,
656 cases: Vec::new(),
657 spans: Vec::new(),
658 labels: Vec::new(),
659 default: None,
660 });
661 }
662 let body = self.stmt(body);
663 let Some(switch) = self.body.as_mut().and_then(|state| state.switches.pop()) else {
664 return Stmt::Error;
665 };
666 let cases = self.tast.add_cases(&switch.cases);
667 for &labelled in &switch.labels {
668 let Stmt::Case { case: entry, body } = self.tast[labelled] else {
669 continue;
670 };
671 let case = cases.iter().nth(entry.index()).expect("a case for every label");
675 self.tast.set_stmt(labelled, Stmt::Case { case, body });
676 }
677 Stmt::Switch { cond, body, cases, default: switch.default.map(|(stmt, _)| stmt) }
678 }
679
680 fn case(
682 &mut self,
683 lo: ast::ExprId,
684 hi: Option<ast::ExprId>,
685 body: Option<ast::StmtId>,
686 span: Span,
687 ) -> Stmt {
688 let entry = self.enter_case(lo, hi, span);
693 let body = self.labelled_body(body, span);
694 let Some(entry) = entry else {
695 return Stmt::Error;
696 };
697 self.switches().expect("a switch").cases[entry].body = body;
698 Stmt::Case { case: rucc_base::Idx::from_usize(entry), body }
703 }
704
705 fn enter_case(
708 &mut self,
709 lo: ast::ExprId,
710 hi: Option<ast::ExprId>,
711 span: Span,
712 ) -> Option<usize> {
713 if self.body.as_ref().is_none_or(|state| state.switches.is_empty()) {
714 self.report(
715 Diagnostic::error("case label not within a switch statement", span)
716 .with_code("E0621"),
717 );
718 return None;
719 }
720 let low = self.case_value(lo, span)?;
721 let high = match hi {
722 Some(hi) => self.case_value(hi, span)?,
723 None => low,
724 };
725 if high < low {
726 self.report(Diagnostic::warning("empty range specified", span).with_code("E0622"));
727 return None;
728 }
729 if let Some(at) = self.overlapping_case(low, high) {
730 self.report(
731 Diagnostic::error("duplicate case value", span)
732 .with_code("E0623")
733 .note("previously used here".to_owned(), at),
734 );
735 return None;
736 }
737 let switch = self.switches().expect("a switch");
738 let entry = switch.cases.len();
739 switch.cases.push(Case { low, high, body: rucc_base::Idx::from_usize(0) });
742 switch.spans.push(span);
743 Some(entry)
744 }
745
746 fn case_value(&mut self, value: ast::ExprId, span: Span) -> Option<i128> {
748 let at = self.ast.expr_span(value);
749 let value = self.expr(value);
750 let value = self.value(value);
751 let folded = match self.eval_integer(value) {
752 Ok(folded) => folded,
753 Err(failed) => {
754 if !failed.poisoned {
755 self.report(
756 Diagnostic::error("case label does not reduce to an integer constant", at)
757 .with_code("E0624"),
758 );
759 }
760 return None;
761 }
762 };
763 let switch = self.switches()?;
764 let (ty, range) = (switch.ty, switch.range);
765 if let Some(range) = range {
766 if eval::overflows(Const::Int(folded), range) {
767 self.report(
768 Diagnostic::warning("case label value exceeds maximum value for type", span)
769 .with_code("E0625"),
770 );
771 }
772 }
773 let info = eval::int_shape(&self.types, ty, self.cx.target)?;
774 Some(eval::narrowed(Const::Int(folded), info))
775 }
776
777 fn overlapping_case(&mut self, low: i128, high: i128) -> Option<Span> {
779 let switch = self.switches()?;
780 switch
781 .cases
782 .iter()
783 .position(|case| case.low <= high && low <= case.high)
784 .map(|index| switch.spans[index])
785 }
786
787 fn default(&mut self, body: Option<ast::StmtId>, span: Span) -> Stmt {
789 let body = self.labelled_body(body, span);
790 if self.body.as_ref().is_none_or(|state| state.switches.is_empty()) {
791 self.report(
792 Diagnostic::error("'default' label not within a switch statement", span)
793 .with_code("E0626"),
794 );
795 return Stmt::Error;
796 }
797 if let Some((_, at)) = self.switches().expect("a switch").default {
798 self.report(
799 Diagnostic::error("multiple default labels in one switch", span)
800 .with_code("E0627")
801 .note("this is the first default label".to_owned(), at),
802 );
803 return Stmt::Error;
804 }
805 self.switches().expect("a switch").default = Some((body, span));
806 Stmt::Default { body }
807 }
808
809 fn labelled(&mut self, name: Symbol, body: Option<ast::StmtId>, span: Span) -> Stmt {
811 let inside = self.open_scope();
814 let body = self.labelled_body(body, span);
815 let label = self.label(name, span);
816 let defined = self.body.as_ref().and_then(|state| state.labels[&name].defined);
817 if let Some(at) = defined {
818 let spelled = self.text(name).to_owned();
819 self.report(
820 Diagnostic::error(format!("duplicate label '{spelled}'"), span)
821 .with_code("E0628")
822 .note(format!("previous definition of '{spelled}' with type 'void'"), at),
823 );
824 return Stmt::Error;
825 }
826 if let Some(state) = self.body.as_mut() {
827 state.labels.entry(name).and_modify(|known| known.defined = Some(span));
828 state.landings.insert(label, Landing { at: span, inside });
829 }
830 self.tast.define_label(label, body);
831 Stmt::Label { label, body }
832 }
833
834 fn labelled_body(&mut self, body: Option<ast::StmtId>, span: Span) -> StmtId {
836 match body {
837 Some(body) => self.stmt(body),
838 None => self.tast.stmt(Stmt::Empty, span),
839 }
840 }
841
842 fn local_labels(&mut self, names: ast::SymbolList, span: Span) {
844 let ast = self.ast;
845 for &name in &ast[names] {
846 let id = self.tast.add_label(Label { name, stmt: None });
847 let local = Labelled { id, defined: None, at: span };
848 if let Some(state) = self.body.as_mut() {
849 let previous = state.labels.insert(name, local);
850 state.shadowed.push((name, previous));
851 }
852 }
853 }
854
855 fn jump(&mut self, name: Symbol, span: Span) -> LabelId {
858 let to = self.label(name, span);
859 if let Some(state) = self.body.as_mut() {
860 state.jumps.push(Jump { to, at: span, inside: state.inside });
861 }
862 to
863 }
864
865 fn label(&mut self, name: Symbol, span: Span) -> LabelId {
867 if let Some(known) = self.body.as_ref().and_then(|state| state.labels.get(&name)) {
868 return known.id;
869 }
870 let id = self.tast.add_label(Label { name, stmt: None });
871 if let Some(state) = self.body.as_mut() {
872 state.labels.insert(name, Labelled { id, defined: None, at: span });
873 }
874 id
875 }
876
877 fn undefined_label(&mut self, label: Labelled) {
884 let name = self.tast[label.id].name;
885 let spelled = self.text(name).to_owned();
886 self.report(
887 Diagnostic::error(format!("label '{spelled}' used but not defined"), label.at)
888 .with_code("E0629"),
889 );
890 }
891
892 fn computed_goto(&mut self, target: ast::ExprId) -> Stmt {
894 let at = self.ast.expr_span(target);
895 let target = self.expr(target);
896 let target = self.value(target);
897 if self.is_poisoned(target) {
898 return Stmt::Error;
899 }
900 let ty = self.tast[target].ty;
901 if !is_pointer(&self.types, ty) && !is_integer(&self.types, ty) {
904 self.report(
905 Diagnostic::error("computed goto must be pointer type", at).with_code("E0630"),
906 );
907 return Stmt::Error;
908 }
909 let void = self.types.pointer(self.types.void());
910 let target = self.conv().to_type(target, void);
911 Stmt::IndirectGoto(target)
912 }
913
914 fn asm(&mut self, id: ast::AsmId, span: Span) -> Stmt {
927 let node = self.ast[id];
928 let outputs = self.asm_operands(node.outputs, 0, true);
929 let first_input = self.ast[node.outputs].len();
930 let inputs = self.asm_operands(node.inputs, first_input, false);
931
932 let mut clobbers = Vec::with_capacity(self.ast[node.clobbers].len());
933 for index in 0..self.ast[node.clobbers].len() {
934 let clobber = self.ast[node.clobbers][index];
935 clobbers.push(self.asm_string(clobber, span));
936 }
937 let clobbers = self.tast.add_str_refs(&clobbers);
938
939 let mut labels = Vec::with_capacity(self.ast[node.labels].len());
940 for index in 0..self.ast[node.labels].len() {
941 let name = self.ast[node.labels][index];
942 labels.push(self.label(name, span));
943 }
944 let labels = self.tast.add_label_refs(&labels);
945 let template = self.asm_template(node.template, outputs, inputs, labels, span);
946
947 let mut quals = node.quals;
951 if self.ast[node.outputs].is_empty() || quals.has(AsmQuals::GOTO) {
952 quals = quals.with(AsmQuals::VOLATILE);
953 }
954 Stmt::Asm(self.tast.add_asm(Asm { template, outputs, inputs, clobbers, labels, quals }))
955 }
956
957 fn asm_operands(
959 &mut self,
960 list: ast::AsmOperandList,
961 first: usize,
962 output: bool,
963 ) -> AsmOperandList {
964 let mut operands = Vec::with_capacity(self.ast[list].len());
965 for index in 0..self.ast[list].len() {
966 let operand = self.ast[list][index];
967 let operand = self.asm_operand(operand, first + index, output);
968 operands.push(operand);
969 }
970 self.tast.add_asm_operands(&operands)
971 }
972
973 fn asm_operand(&mut self, operand: ast::AsmOperand, number: usize, output: bool) -> AsmOperand {
975 let span = operand.span;
976 let constraint = self.asm_string(operand.constraint, span);
977 let text = spelling(&self.tast[constraint]);
978 let value = self.expr(operand.value);
979 let ty = self.tast[value].ty;
980 let lvalue = matches!(self.tast[value].category, Category::Lvalue | Category::Bitfield);
981
982 let record = is_record(&self.types, ty);
986 let memory = memory_only(&text) || record;
987 if record && !memory_only(&text) {
988 self.statement_unsupported("a structure or a union in a register constraint", span);
989 }
990
991 if output {
992 if !text.starts_with(['=', '+']) {
993 self.report(
994 Diagnostic::error("output operand constraint lacks '='", span)
995 .with_code("E0653"),
996 );
997 }
998 if !lvalue {
999 self.report(
1000 Diagnostic::error("lvalue required in 'asm' statement", span)
1001 .with_code("E0654"),
1002 );
1003 } else if self.types.quals(ty).has(Qualifiers::CONST) {
1004 let what = self.read_only(value);
1005 self.report(
1006 Diagnostic::error(format!("read-only {what} used as 'asm' output"), span)
1007 .with_code("E0655"),
1008 );
1009 }
1010 } else {
1011 if let Some(sign) = text.chars().find(|&ch| ch == '=' || ch == '+') {
1012 self.report(
1013 Diagnostic::error(format!("input operand constraint contains '{sign}'"), span)
1014 .with_code("E0656"),
1015 );
1016 }
1017 if memory && !lvalue {
1018 self.report(
1019 Diagnostic::error(
1020 format!("memory input {number} is not directly addressable"),
1021 span,
1022 )
1023 .with_code("E0657"),
1024 );
1025 }
1026 }
1027
1028 let value = if output || memory { value } else { self.value(value) };
1032 AsmOperand { name: operand.name, constraint, value, memory }
1033 }
1034
1035 fn asm_string(&mut self, id: ast::StrId, span: Span) -> StrId {
1037 let literal = self.ast[id].clone();
1038 self.asm_narrow(&literal, span);
1039 self.tast.add_string(literal)
1040 }
1041
1042 fn asm_narrow(&mut self, literal: &StringLiteral, span: Span) {
1044 if !matches!(literal.encoding, Encoding::Plain) {
1045 self.report(Diagnostic::error("wide string literal in 'asm'", span).with_code("E0658"));
1046 }
1047 }
1048
1049 fn asm_template(
1057 &mut self,
1058 id: ast::StrId,
1059 outputs: AsmOperandList,
1060 inputs: AsmOperandList,
1061 labels: LabelList,
1062 span: Span,
1063 ) -> StrId {
1064 let mut names: Vec<(String, usize)> = Vec::new();
1065 let mut number = 0;
1066 for list in [outputs, inputs] {
1067 for index in 0..self.tast[list].len() {
1068 if let Some(name) = self.tast[list][index].name {
1069 names.push((self.text(name).to_owned(), number));
1070 }
1071 number += 1;
1072 }
1073 }
1074 for index in 0..self.tast[labels].len() {
1075 let label = self.tast[labels][index];
1076 let name = self.tast[label].name;
1077 names.push((self.text(name).to_owned(), number));
1078 number += 1;
1079 }
1080 for at in 1..names.len() {
1081 if names[..at].iter().any(|(earlier, _)| *earlier == names[at].0) {
1082 let name = names[at].0.clone();
1083 self.report(
1084 Diagnostic::error(format!("duplicate asm operand name '{name}'"), span)
1085 .with_code("E0659"),
1086 );
1087 }
1088 }
1089
1090 let literal = self.ast[id].clone();
1091 self.asm_narrow(&literal, span);
1092 let text = self.asm_numbers(spelling(&literal), &names, span);
1093 let elements = text.chars().map(|ch| ch as u32).collect();
1094 self.tast.add_string(StringLiteral { elements, ..literal })
1095 }
1096
1097 fn asm_numbers(&mut self, text: String, names: &[(String, usize)], span: Span) -> String {
1104 let chars: Vec<char> = text.chars().collect();
1105 let mut out = String::with_capacity(text.len());
1106 let mut index = 0;
1107 while index < chars.len() {
1108 let ch = chars[index];
1109 out.push(ch);
1110 index += 1;
1111 if ch != '%' {
1112 continue;
1113 }
1114 let letter = chars.get(index).copied();
1115 let open = match letter {
1116 Some('[') => index,
1117 Some(modifier)
1118 if modifier.is_ascii_alphabetic() && chars.get(index + 1) == Some(&'[') =>
1119 {
1120 out.push(modifier);
1121 index += 1;
1122 index
1123 }
1124 Some('%') => {
1126 out.push('%');
1127 index += 1;
1128 continue;
1129 }
1130 _ => continue,
1131 };
1132 let Some(close) = chars[open..].iter().position(|&ch| ch == ']').map(|at| open + at)
1133 else {
1134 continue;
1135 };
1136 let name: String = chars[open + 1..close].iter().collect();
1137 index = close + 1;
1138 match names.iter().find(|(known, _)| *known == name) {
1139 Some(&(_, number)) => out.push_str(&number.to_string()),
1140 None => {
1141 self.report(
1142 Diagnostic::error(format!("undefined named operand '{name}'"), span)
1143 .with_code("E0660"),
1144 );
1145 out.push_str(&chars[open..=close].iter().collect::<String>());
1146 }
1147 }
1148 }
1149 out
1150 }
1151
1152 fn break_stmt(&mut self, span: Span) -> Stmt {
1154 let inside =
1155 self.body.as_ref().is_some_and(|state| state.loops > 0 || !state.switches.is_empty());
1156 if inside {
1157 return Stmt::Break;
1158 }
1159 self.report(
1160 Diagnostic::error("break statement not within loop or switch", span).with_code("E0631"),
1161 );
1162 Stmt::Error
1163 }
1164
1165 fn continue_stmt(&mut self, span: Span) -> Stmt {
1167 if self.body.as_ref().is_some_and(|state| state.loops > 0) {
1168 return Stmt::Continue;
1169 }
1170 self.report(
1171 Diagnostic::error("continue statement not within a loop", span).with_code("E0632"),
1172 );
1173 Stmt::Error
1174 }
1175
1176 fn return_stmt(&mut self, value: Option<ast::ExprId>, span: Span) -> Stmt {
1183 let Some((ret, at)) = self.body.as_ref().map(|state| (state.ret, state.at)) else {
1184 return Stmt::Return(None);
1185 };
1186 let void = is_void(&self.types, ret);
1187 let old = self.cx.std < Std::C99;
1191 let Some(value) = value else {
1192 if !void && !old {
1193 self.report(
1194 Diagnostic::error(
1195 "'return' with no value, in function returning non-void",
1196 span,
1197 )
1198 .with_code("E0633")
1199 .note("declared here".to_owned(), at),
1200 );
1201 }
1202 return Stmt::Return(None);
1203 };
1204 let where_from = self.ast.expr_span(value);
1205 let value = self.expr(value);
1206 let value = self.value(value);
1207 if !void {
1208 return Stmt::Return(Some(self.assign_to(ret, value, where_from, Target::Return)));
1209 }
1210 if !is_void(&self.types, self.tast[value].ty) && !self.is_poisoned(value) {
1213 let said = "'return' with a value, in function returning void";
1214 let diagnostic = if old {
1215 Diagnostic::warning(said, where_from)
1216 } else {
1217 Diagnostic::error(said, where_from)
1218 };
1219 self.report(diagnostic.with_code("E0634").note("declared here".to_owned(), at));
1220 }
1221 let value = self.conv().to_void(value);
1222 Stmt::Return(Some(value))
1223 }
1224
1225 fn controlling(&mut self, cond: ast::ExprId) -> ExprId {
1227 let span = self.ast.expr_span(cond);
1228 let cond = self.expr(cond);
1229 self.condition(cond, span)
1230 }
1231
1232 fn switches(&mut self) -> Option<&mut Switch> {
1234 self.body.as_mut()?.switches.last_mut()
1235 }
1236
1237 fn statement_unsupported(&mut self, what: &str, span: Span) {
1239 self.report(
1240 Diagnostic::error(format!("{what} is not supported yet"), span).with_code("E0519"),
1241 );
1242 }
1243}
1244
1245fn open_at(modified: &[Modified], at: Option<usize>, entered: usize) -> bool {
1252 let mut at = at;
1253 while let Some(index) = at {
1254 if index == entered {
1255 return true;
1256 }
1257 at = modified[index].outer;
1258 }
1259 false
1260}
1261
1262fn spelling(literal: &StringLiteral) -> String {
1268 literal.elements.iter().filter_map(|&element| char::from_u32(element)).collect()
1269}
1270
1271fn memory_only(constraint: &str) -> bool {
1280 let letters: Vec<char> =
1281 constraint.chars().filter(|ch| !"=+&%#*!?, \t".contains(*ch)).collect();
1282 !letters.is_empty() && letters.iter().all(|ch| "moV<>".contains(*ch))
1283}
1284
1285#[cfg(test)]
1286mod tests {
1287 use rucc_ast::{
1288 ArraySize, AttrList, Builtin, BuiltinSet, DeclSpecs, DeclSpecsId, Declarator, DeclaratorId,
1289 Derived, Quals, TypeSpec,
1290 };
1291 use rucc_base::Interner;
1292 use rucc_lex::{IntConstant, IntConstantType, Remarks};
1293 use rucc_session::Std;
1294 use rucc_target::{TargetInfo, Triple};
1295 use rucc_types::IntKind;
1296
1297 use super::*;
1298 use crate::check::Context;
1299 use crate::print::Printer;
1300
1301 struct Fixture {
1307 ast: rucc_ast::Ast,
1308 names: Interner,
1309 target: TargetInfo,
1310 }
1311
1312 impl Fixture {
1313 fn new() -> Fixture {
1314 let target =
1315 TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a triple"));
1316 Fixture { ast: rucc_ast::Ast::new(), names: Interner::new(), target }
1317 }
1318
1319 fn name(&mut self, text: &str) -> Symbol {
1320 self.names.intern(text)
1321 }
1322
1323 fn int(&mut self, value: u128) -> ast::ExprId {
1324 let ty = IntConstantType::Standard(IntKind::Int);
1325 let id = self.ast.add_int(IntConstant { value, ty, remarks: Remarks::default() });
1326 self.ast.expr(ast::Expr::Int(id), Span::DUMMY)
1327 }
1328
1329 fn use_name(&mut self, text: &str) -> ast::ExprId {
1330 let name = self.name(text);
1331 self.ast.expr(ast::Expr::Name(name), Span::DUMMY)
1332 }
1333
1334 fn keywords(&mut self, written: &[BuiltinSet]) -> DeclSpecsId {
1336 let mut builtin = Builtin::NONE;
1337 for &keyword in written {
1338 builtin = builtin.add(keyword).expect("a keyword written once");
1339 }
1340 let mut specs = DeclSpecs::empty(Span::DUMMY);
1341 specs.ty = TypeSpec::Builtin(builtin);
1342 self.ast.add_specs(specs)
1343 }
1344
1345 fn int_specs(&mut self) -> DeclSpecsId {
1347 self.keywords(&[BuiltinSet::INT])
1348 }
1349
1350 fn declarator(&mut self, name: Option<&str>, derived: &[Derived]) -> DeclaratorId {
1351 let name = name.map(|text| self.name(text));
1352 let derived = self.ast.add_derived_list(derived);
1353 self.ast.add_declarator(Declarator {
1354 name,
1355 name_span: Span::DUMMY,
1356 derived,
1357 span: Span::DUMMY,
1358 })
1359 }
1360
1361 fn local(&mut self, specs: DeclSpecsId, name: &str) -> ast::DeclId {
1363 let declarator = self.declarator(Some(name), &[]);
1364 let item = ast::InitDeclarator {
1365 declarator,
1366 init: None,
1367 asm_label: None,
1368 attrs: AttrList::EMPTY,
1369 span: Span::DUMMY,
1370 };
1371 let declarators = self.ast.add_init_declarator_list(&[item]);
1372 self.ast.decl(ast::Decl::Var { specs, declarators }, Span::DUMMY)
1373 }
1374
1375 fn array(&mut self, specs: DeclSpecsId, name: &str, size: ast::ExprId) -> ast::DeclId {
1377 let derived = [Derived::Array {
1378 size: ArraySize::Expr(size),
1379 quals: Quals::NONE,
1380 has_static: false,
1381 }];
1382 let declarator = self.declarator(Some(name), &derived);
1383 let item = ast::InitDeclarator {
1384 declarator,
1385 init: None,
1386 asm_label: None,
1387 attrs: AttrList::EMPTY,
1388 span: Span::DUMMY,
1389 };
1390 let declarators = self.ast.add_init_declarator_list(&[item]);
1391 self.ast.decl(ast::Decl::Var { specs, declarators }, Span::DUMMY)
1392 }
1393
1394 fn cast(&mut self, specs: DeclSpecsId, value: ast::ExprId) -> ast::ExprId {
1396 let declarator = self.declarator(None, &[]);
1397 let ty = self.ast.add_type_name(ast::TypeName { specs, declarator, span: Span::DUMMY });
1398 self.ast.expr(ast::Expr::Cast { ty, operand: value }, Span::DUMMY)
1399 }
1400
1401 fn stmt(&mut self, stmt: ast::Stmt) -> ast::StmtId {
1402 self.ast.stmt(stmt, Span::DUMMY)
1403 }
1404
1405 fn block(&mut self, body: &[ast::StmtId]) -> ast::StmtId {
1407 let body = self.ast.add_stmt_list(body);
1408 self.stmt(ast::Stmt::Compound(body))
1409 }
1410
1411 fn expr_stmt(&mut self, value: ast::ExprId) -> ast::StmtId {
1413 self.stmt(ast::Stmt::Expr(value))
1414 }
1415
1416 fn labelled(&mut self, text: &str, body: Option<ast::StmtId>) -> ast::StmtId {
1418 let name = self.name(text);
1419 self.stmt(ast::Stmt::Label { name, body, attrs: AttrList::EMPTY })
1420 }
1421
1422 fn goto(&mut self, text: &str) -> ast::StmtId {
1424 let name = self.name(text);
1425 self.stmt(ast::Stmt::Goto(name))
1426 }
1427
1428 fn local_labels(&mut self, names: &[&str]) -> ast::StmtId {
1430 let names: Vec<Symbol> = names.iter().map(|text| self.name(text)).collect();
1431 let names = self.ast.add_symbol_list(&names);
1432 self.stmt(ast::Stmt::LocalLabels(names))
1433 }
1434
1435 fn case(&mut self, lo: u128, hi: Option<u128>, body: Option<ast::StmtId>) -> ast::StmtId {
1437 let lo = self.int(lo);
1438 let hi = hi.map(|hi| self.int(hi));
1439 self.stmt(ast::Stmt::Case { lo, hi, body })
1440 }
1441
1442 fn switch(&mut self, scrutinee: ast::ExprId, body: &[ast::StmtId]) -> ast::StmtId {
1444 let body = self.block(body);
1445 self.stmt(ast::Stmt::Switch { scrutinee, body })
1446 }
1447
1448 fn checker(&self) -> Checker<'_> {
1449 Checker::new(&self.ast, Context::new(&self.names, &self.target, Std::C23))
1450 }
1451 }
1452
1453 fn dump(checker: &Checker<'_>, id: StmtId) -> String {
1455 let mut printer = Printer::new(&checker.tast, &checker.types, checker.cx.names);
1456 printer.stmt(id);
1457 printer.finish()
1458 }
1459
1460 fn messages(checker: &Checker<'_>) -> Vec<String> {
1462 checker
1463 .errors
1464 .diagnostics()
1465 .iter()
1466 .flat_map(|d| {
1467 std::iter::once(d.message.clone())
1468 .chain(d.children.iter().map(|n| n.message.clone()))
1469 })
1470 .collect()
1471 }
1472
1473 fn message(checker: &Checker<'_>) -> String {
1475 let mut reported = messages(checker);
1476 assert_eq!(reported.len(), 1, "expected exactly one diagnostic, got {reported:?}");
1477 reported.pop().expect("one message")
1478 }
1479
1480 fn reported(checker: &Checker<'_>) -> Vec<String> {
1484 checker
1485 .errors
1486 .diagnostics()
1487 .iter()
1488 .map(|d| format!("{}: {}", d.severity.as_str(), d.message))
1489 .collect()
1490 }
1491
1492 #[test]
1493 fn a_block_is_a_scope_and_a_name_declared_in_one_is_gone_after_it() {
1494 let mut f = Fixture::new();
1495 let specs = f.int_specs();
1496 let declared = f.local(specs, "x");
1497 let declared = f.stmt(ast::Stmt::Decl(declared));
1498 let inner = f.block(&[declared]);
1499 let use_x = f.use_name("x");
1500 let after = f.expr_stmt(use_x);
1501 let outer = f.block(&[inner, after]);
1502
1503 let mut c = f.checker();
1504 let void = c.types.void();
1505 c.check_stmt(void, outer);
1506
1507 assert_eq!(message(&c), "'x' undeclared (first use in this function)");
1508 }
1509
1510 #[test]
1511 fn a_name_nobody_declared_is_reported_once_per_function_and_not_once_per_use() {
1512 let mut f = Fixture::new();
1516 let first = f.use_name("nope");
1517 let first = f.expr_stmt(first);
1518 let second = f.use_name("nope");
1519 let second = f.expr_stmt(second);
1520 let body = f.block(&[first, second]);
1521
1522 let mut c = f.checker();
1523 let void = c.types.void();
1524 let previous = c.open_body(Enclosing::returning(void));
1525 c.check_stmt(void, body);
1526 c.close_body(previous);
1527
1528 assert_eq!(message(&c), "'nope' undeclared (first use in this function)");
1529 }
1530
1531 #[test]
1532 fn an_expression_statement_holds_the_value_and_not_a_conversion_of_it_to_void() {
1533 let mut f = Fixture::new();
1534 let one = f.int(1);
1535 let stmt = f.expr_stmt(one);
1536
1537 let mut c = f.checker();
1538 let void = c.types.void();
1539 let id = c.check_stmt(void, stmt);
1540
1541 assert_eq!(dump(&c, id), "expr\n const 1 : int\n");
1542 assert!(c.errors.is_empty());
1543 }
1544
1545 #[test]
1546 fn a_statement_expression_has_the_type_of_its_last_statement() {
1547 let mut f = Fixture::new();
1548 let one = f.int(1);
1549 let inner = f.expr_stmt(one);
1550 let body = f.block(&[inner]);
1551 let value = f.ast.expr(ast::Expr::StmtExpr(body), Span::DUMMY);
1552 let stmt = f.expr_stmt(value);
1553
1554 let mut c = f.checker();
1555 let void = c.types.void();
1556 let id = c.check_stmt(void, stmt);
1557
1558 assert_eq!(
1559 dump(&c, id),
1560 "expr\n stmt-expr : int\n block\n expr\n const 1 : int\n"
1561 );
1562 assert!(c.errors.is_empty());
1563 }
1564
1565 #[test]
1566 fn a_statement_expression_that_ends_in_something_else_is_void() {
1567 let mut f = Fixture::new();
1568 let body = f.block(&[]);
1569 let value = f.ast.expr(ast::Expr::StmtExpr(body), Span::DUMMY);
1570 let stmt = f.expr_stmt(value);
1571
1572 let mut c = f.checker();
1573 let void = c.types.void();
1574 let id = c.check_stmt(void, stmt);
1575
1576 assert_eq!(dump(&c, id), "expr\n stmt-expr : void\n block\n");
1577 assert!(c.errors.is_empty());
1578 }
1579
1580 #[test]
1581 fn the_declaration_in_a_for_clause_scopes_to_the_loop_and_not_to_what_follows() {
1582 let mut f = Fixture::new();
1583 let specs = f.int_specs();
1584 let declared = f.local(specs, "i");
1585 let empty = f.stmt(ast::Stmt::Empty);
1586 let loop_stmt = f.stmt(ast::Stmt::For {
1587 init: ForInit::Decl(declared),
1588 cond: None,
1589 step: None,
1590 body: empty,
1591 });
1592 let use_i = f.use_name("i");
1593 let after = f.expr_stmt(use_i);
1594 let outer = f.block(&[loop_stmt, after]);
1595
1596 let mut c = f.checker();
1597 let void = c.types.void();
1598 c.check_stmt(void, outer);
1599
1600 assert_eq!(message(&c), "'i' undeclared (first use in this function)");
1601 }
1602
1603 #[test]
1604 fn a_static_in_a_for_clause_is_accepted_and_only_pedantic_says_anything_about_it() {
1605 let mut f = Fixture::new();
1606 let mut specs = DeclSpecs::empty(Span::DUMMY);
1607 let builtin = Builtin::NONE.add(BuiltinSet::INT).expect("a keyword written once");
1608 specs.ty = TypeSpec::Builtin(builtin);
1609 specs.storage = Some(StorageClass::Static);
1610 let specs = f.ast.add_specs(specs);
1611 let declared = f.local(specs, "i");
1612 let empty = f.stmt(ast::Stmt::Empty);
1613 let loop_stmt = f.stmt(ast::Stmt::For {
1614 init: ForInit::Decl(declared),
1615 cond: None,
1616 step: None,
1617 body: empty,
1618 });
1619
1620 let mut c = f.checker();
1621 let void = c.types.void();
1622 c.check_stmt(void, loop_stmt);
1623 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1624
1625 let mut c = f.checker();
1626 c.cx.pedantic = true;
1627 let void = c.types.void();
1628 c.check_stmt(void, loop_stmt);
1629 assert_eq!(
1630 reported(&c),
1631 ["warning: declaration of static variable 'i' in 'for' loop initial declaration"]
1632 );
1633 }
1634
1635 #[test]
1636 fn continue_needs_a_loop_and_is_not_satisfied_by_a_switch() {
1637 let mut f = Fixture::new();
1638 let one = f.int(1);
1639 let go_on = f.stmt(ast::Stmt::Continue);
1640 let case = f.stmt(ast::Stmt::Case { lo: one, hi: None, body: Some(go_on) });
1641 let scrutinee = f.int(0);
1642 let switch = f.switch(scrutinee, &[case]);
1643
1644 let mut c = f.checker();
1645 let void = c.types.void();
1646 c.check_stmt(void, switch);
1647
1648 assert_eq!(message(&c), "continue statement not within a loop");
1649 }
1650
1651 #[test]
1652 fn break_is_satisfied_by_a_switch_and_reported_where_there_is_neither() {
1653 let mut f = Fixture::new();
1654 let stop = f.stmt(ast::Stmt::Break);
1655 let scrutinee = f.int(0);
1656 let switch = f.switch(scrutinee, &[stop]);
1657 let loose = f.stmt(ast::Stmt::Break);
1658
1659 let mut c = f.checker();
1660 let void = c.types.void();
1661 c.check_stmt(void, switch);
1662 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1663
1664 let mut c = f.checker();
1665 let void = c.types.void();
1666 c.check_stmt(void, loose);
1667 assert_eq!(message(&c), "break statement not within loop or switch");
1668 }
1669
1670 #[test]
1671 fn a_goto_resolves_to_a_label_the_function_defines_further_down() {
1672 let mut f = Fixture::new();
1673 let jump = f.goto("done");
1674 let empty = f.stmt(ast::Stmt::Empty);
1675 let target = f.labelled("done", Some(empty));
1676 let body = f.block(&[jump, target]);
1677
1678 let mut c = f.checker();
1679 let void = c.types.void();
1680 let id = c.check_stmt(void, body);
1681
1682 assert_eq!(dump(&c, id), "block\n goto #0 done\n label #0 done\n empty\n");
1683 assert!(c.errors.is_empty());
1684 }
1685
1686 #[test]
1687 fn a_label_that_is_jumped_to_and_never_defined_is_reported_at_the_jump() {
1688 let mut f = Fixture::new();
1689 let jump = f.goto("away");
1690 let body = f.block(&[jump]);
1691
1692 let mut c = f.checker();
1693 let void = c.types.void();
1694 c.check_stmt(void, body);
1695
1696 assert_eq!(message(&c), "label 'away' used but not defined");
1697 }
1698
1699 #[test]
1700 fn the_address_of_a_label_is_a_use_of_it_and_not_a_definition() {
1701 let mut f = Fixture::new();
1702 let away = f.name("away");
1703 let value = f.ast.expr(ast::Expr::LabelAddr(away), Span::DUMMY);
1704 let stmt = f.expr_stmt(value);
1705
1706 let mut c = f.checker();
1707 let void = c.types.void();
1708 let id = c.check_stmt(void, stmt);
1709
1710 assert_eq!(dump(&c, id), "expr\n label-addr #0 away : void *\n");
1711 assert_eq!(message(&c), "label 'away' used but not defined");
1712 }
1713
1714 #[test]
1715 fn a_goto_into_the_scope_of_a_variable_length_array_is_reported() {
1716 let mut f = Fixture::new();
1719 let specs = f.int_specs();
1720 let length = f.local(specs, "n");
1721 let length = f.stmt(ast::Stmt::Decl(length));
1722 let jump = f.goto("done");
1723 let size = f.use_name("n");
1724 let array = f.array(specs, "a", size);
1725 let array = f.stmt(ast::Stmt::Decl(array));
1726 let empty = f.stmt(ast::Stmt::Empty);
1727 let target = f.labelled("done", Some(empty));
1728 let inner = f.block(&[array, target]);
1729 let body = f.block(&[length, jump, inner]);
1730
1731 let mut c = f.checker();
1732 let void = c.types.void();
1733 c.check_stmt(void, body);
1734
1735 assert_eq!(
1736 messages(&c),
1737 [
1738 "jump into scope of identifier with variably modified type",
1739 "label 'done' defined here",
1740 "'a' declared here",
1741 ]
1742 );
1743 }
1744
1745 #[test]
1746 fn a_goto_out_of_the_scope_of_a_variable_length_array_is_allowed() {
1747 let mut f = Fixture::new();
1750 let specs = f.int_specs();
1751 let length = f.local(specs, "n");
1752 let length = f.stmt(ast::Stmt::Decl(length));
1753 let size = f.use_name("n");
1754 let array = f.array(specs, "a", size);
1755 let array = f.stmt(ast::Stmt::Decl(array));
1756 let jump = f.goto("done");
1757 let inner = f.block(&[array, jump]);
1758 let empty = f.stmt(ast::Stmt::Empty);
1759 let target = f.labelled("done", Some(empty));
1760 let body = f.block(&[length, inner, target]);
1761
1762 let mut c = f.checker();
1763 let void = c.types.void();
1764 c.check_stmt(void, body);
1765
1766 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1767 }
1768
1769 #[test]
1770 fn a_goto_into_the_scope_of_an_array_whose_length_is_a_constant_is_allowed() {
1771 let mut f = Fixture::new();
1774 let specs = f.int_specs();
1775 let jump = f.goto("done");
1776 let size = f.int(4);
1777 let array = f.array(specs, "a", size);
1778 let array = f.stmt(ast::Stmt::Decl(array));
1779 let empty = f.stmt(ast::Stmt::Empty);
1780 let target = f.labelled("done", Some(empty));
1781 let inner = f.block(&[array, target]);
1782 let body = f.block(&[jump, inner]);
1783
1784 let mut c = f.checker();
1785 let void = c.types.void();
1786 c.check_stmt(void, body);
1787
1788 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1789 }
1790
1791 #[test]
1792 fn one_label_defined_twice_is_an_error_that_points_at_the_first() {
1793 let mut f = Fixture::new();
1794 let first = f.labelled("here", None);
1795 let second = f.labelled("here", None);
1796 let body = f.block(&[first, second]);
1797
1798 let mut c = f.checker();
1799 let void = c.types.void();
1800 c.check_stmt(void, body);
1801
1802 assert_eq!(
1803 messages(&c),
1804 ["duplicate label 'here'", "previous definition of 'here' with type 'void'",]
1805 );
1806 }
1807
1808 #[test]
1809 fn a_local_label_is_undone_when_its_block_ends_so_two_blocks_may_declare_one_name() {
1810 let mut f = Fixture::new();
1811 let sibling = |f: &mut Fixture| {
1812 let declared = f.local_labels(&["done"]);
1813 let jump = f.goto("done");
1814 let target = f.labelled("done", None);
1815 f.block(&[declared, jump, target])
1816 };
1817 let first = sibling(&mut f);
1818 let second = sibling(&mut f);
1819 let body = f.block(&[first, second]);
1820
1821 let mut c = f.checker();
1822 let void = c.types.void();
1823 let id = c.check_stmt(void, body);
1824
1825 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1826 assert_eq!(
1827 dump(&c, id),
1828 "block\n block\n empty\n goto #0 done\n label #0 done\n empty\n \
1829 block\n empty\n goto #1 done\n label #1 done\n empty\n"
1830 );
1831 }
1832
1833 #[test]
1834 fn a_local_label_that_nothing_defines_is_reported_when_its_block_ends() {
1835 let mut f = Fixture::new();
1836 let declared = f.local_labels(&["done"]);
1837 let jump = f.goto("done");
1838 let inner = f.block(&[declared, jump]);
1839 let target = f.labelled("done", None);
1840 let body = f.block(&[inner, target]);
1841
1842 let mut c = f.checker();
1843 let void = c.types.void();
1844 c.check_stmt(void, body);
1845
1846 assert_eq!(message(&c), "label 'done' used but not defined");
1847 }
1848
1849 #[test]
1850 fn a_computed_goto_wants_something_that_could_be_an_address() {
1851 let mut f = Fixture::new();
1852 let specs = f.keywords(&[BuiltinSet::DOUBLE]);
1853 let zero = f.int(0);
1854 let target = f.cast(specs, zero);
1855 let stmt = f.stmt(ast::Stmt::GotoExpr(target));
1856
1857 let mut c = f.checker();
1858 let void = c.types.void();
1859 c.check_stmt(void, stmt);
1860
1861 assert_eq!(message(&c), "computed goto must be pointer type");
1862 }
1863
1864 #[test]
1865 fn a_switch_on_something_that_is_not_an_integer_is_an_error() {
1866 let mut f = Fixture::new();
1867 let specs = f.keywords(&[BuiltinSet::DOUBLE]);
1868 let zero = f.int(0);
1869 let scrutinee = f.cast(specs, zero);
1870 let switch = f.switch(scrutinee, &[]);
1871
1872 let mut c = f.checker();
1873 let void = c.types.void();
1874 c.check_stmt(void, switch);
1875
1876 assert_eq!(message(&c), "switch quantity not an integer");
1877 }
1878
1879 #[test]
1880 fn the_cases_of_a_switch_are_one_table_in_the_order_they_were_written() {
1881 let mut f = Fixture::new();
1882 let first = f.case(1, None, None);
1883 let second = f.case(4, Some(6), None);
1884 let default = f.stmt(ast::Stmt::Default { body: None });
1885 let scrutinee = f.int(0);
1886 let switch = f.switch(scrutinee, &[first, second, default]);
1887
1888 let mut c = f.checker();
1889 let void = c.types.void();
1890 let id = c.check_stmt(void, switch);
1891
1892 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1893 assert_eq!(
1894 dump(&c, id),
1895 "switch\n cond\n const 0 : int\n cases\n case #0 1\n case #1 4 ... 6\n \
1896 default\n body\n block\n case #0\n empty\n case #1\n \
1897 empty\n default\n empty\n"
1898 );
1899 }
1900
1901 #[test]
1902 fn two_labels_on_one_statement_are_in_the_table_the_way_round_they_were_written() {
1903 let mut f = Fixture::new();
1906 let inner = f.case(2, None, None);
1907 let outer = f.case(1, None, Some(inner));
1908 let scrutinee = f.int(0);
1909 let switch = f.switch(scrutinee, &[outer]);
1910
1911 let mut c = f.checker();
1912 let void = c.types.void();
1913 let id = c.check_stmt(void, switch);
1914
1915 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1916 assert_eq!(
1917 dump(&c, id),
1918 "switch\n cond\n const 0 : int\n cases\n case #0 1\n case #1 2\n body\n \
1919 block\n case #0\n case #1\n empty\n"
1920 );
1921 }
1922
1923 #[test]
1924 fn a_case_that_covers_a_value_an_earlier_one_covers_is_a_duplicate() {
1925 let mut f = Fixture::new();
1926 let first = f.case(1, Some(3), None);
1927 let second = f.case(2, None, None);
1928 let scrutinee = f.int(0);
1929 let switch = f.switch(scrutinee, &[first, second]);
1930
1931 let mut c = f.checker();
1932 let void = c.types.void();
1933 c.check_stmt(void, switch);
1934
1935 assert_eq!(messages(&c), ["duplicate case value", "previously used here"]);
1936 }
1937
1938 #[test]
1939 fn a_case_outside_a_switch_is_an_error_and_so_is_a_default() {
1940 let mut f = Fixture::new();
1941 let case = f.case(1, None, None);
1942 let default = f.stmt(ast::Stmt::Default { body: None });
1943 let body = f.block(&[case, default]);
1944
1945 let mut c = f.checker();
1946 let void = c.types.void();
1947 c.check_stmt(void, body);
1948
1949 assert_eq!(
1950 messages(&c),
1951 [
1952 "case label not within a switch statement",
1953 "'default' label not within a switch statement",
1954 ]
1955 );
1956 }
1957
1958 #[test]
1959 fn a_case_label_that_is_not_a_constant_is_an_error() {
1960 let mut f = Fixture::new();
1961 let specs = f.int_specs();
1962 let declared = f.local(specs, "n");
1963 let declared = f.stmt(ast::Stmt::Decl(declared));
1964 let use_n = f.use_name("n");
1965 let case = f.stmt(ast::Stmt::Case { lo: use_n, hi: None, body: None });
1966 let scrutinee = f.int(0);
1967 let switch = f.switch(scrutinee, &[case]);
1968 let body = f.block(&[declared, switch]);
1969
1970 let mut c = f.checker();
1971 let void = c.types.void();
1972 c.check_stmt(void, body);
1973
1974 assert_eq!(message(&c), "case label does not reduce to an integer constant");
1975 }
1976
1977 #[test]
1978 fn a_case_range_that_runs_backwards_is_empty() {
1979 let mut f = Fixture::new();
1980 let case = f.case(6, Some(4), None);
1981 let scrutinee = f.int(0);
1982 let switch = f.switch(scrutinee, &[case]);
1983
1984 let mut c = f.checker();
1985 let void = c.types.void();
1986 c.check_stmt(void, switch);
1987
1988 assert_eq!(reported(&c), ["warning: empty range specified"]);
1989 }
1990
1991 #[test]
1992 fn a_case_is_measured_against_the_type_that_was_written_and_not_the_promoted_one() {
1993 let mut f = Fixture::new();
1994 let specs = f.keywords(&[BuiltinSet::CHAR]);
1995 let zero = f.int(0);
1996 let scrutinee = f.cast(specs, zero);
1997 let case = f.case(300, None, None);
1998 let switch = f.switch(scrutinee, &[case]);
1999
2000 let mut c = f.checker();
2001 let void = c.types.void();
2002 c.check_stmt(void, switch);
2003
2004 assert_eq!(reported(&c), ["warning: case label value exceeds maximum value for type"]);
2005 }
2006
2007 #[test]
2008 fn two_defaults_in_one_switch_are_an_error_that_points_at_the_first() {
2009 let mut f = Fixture::new();
2010 let first = f.stmt(ast::Stmt::Default { body: None });
2011 let second = f.stmt(ast::Stmt::Default { body: None });
2012 let scrutinee = f.int(0);
2013 let switch = f.switch(scrutinee, &[first, second]);
2014
2015 let mut c = f.checker();
2016 let void = c.types.void();
2017 c.check_stmt(void, switch);
2018
2019 assert_eq!(
2020 messages(&c),
2021 ["multiple default labels in one switch", "this is the first default label"]
2022 );
2023 }
2024
2025 #[test]
2026 fn a_nested_switch_keeps_its_cases_to_itself() {
2027 let mut f = Fixture::new();
2028 let inner_case = f.case(1, None, None);
2029 let inner_scrutinee = f.int(0);
2030 let inner = f.switch(inner_scrutinee, &[inner_case]);
2031 let outer_case = f.case(1, None, Some(inner));
2032 let outer_scrutinee = f.int(0);
2033 let outer = f.switch(outer_scrutinee, &[outer_case]);
2034
2035 let mut c = f.checker();
2036 let void = c.types.void();
2037 let id = c.check_stmt(void, outer);
2038
2039 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
2040 assert_eq!(
2041 dump(&c, id),
2042 "switch\n cond\n const 0 : int\n cases\n case #1 1\n body\n block\n \
2043 case #1\n switch\n cond\n const 0 : int\n \
2044 cases\n case #0 1\n body\n block\n case \
2045 #0\n empty\n"
2046 );
2047 }
2048
2049 #[test]
2050 fn a_bare_return_from_a_function_that_promised_a_value_is_an_error() {
2051 let mut f = Fixture::new();
2052 let stmt = f.stmt(ast::Stmt::Return(None));
2053
2054 let mut c = f.checker();
2055 let int = c.int();
2056 c.check_stmt(int, stmt);
2057
2058 assert_eq!(reported(&c), ["error: 'return' with no value, in function returning non-void"]);
2059 assert_eq!(messages(&c).len(), 2, "the note is attached to it");
2060 }
2061
2062 #[test]
2063 fn a_value_returned_from_a_function_returning_void_is_an_error() {
2064 let mut f = Fixture::new();
2065 let one = f.int(1);
2066 let stmt = f.stmt(ast::Stmt::Return(Some(one)));
2067
2068 let mut c = f.checker();
2069 let void = c.types.void();
2070 c.check_stmt(void, stmt);
2071
2072 assert_eq!(reported(&c), ["error: 'return' with a value, in function returning void"]);
2073 }
2074
2075 #[test]
2076 fn a_void_value_returned_from_a_function_returning_void_is_what_a_wrapper_writes() {
2077 let mut f = Fixture::new();
2078 let specs = f.keywords(&[BuiltinSet::VOID]);
2079 let one = f.int(1);
2080 let value = f.cast(specs, one);
2081 let stmt = f.stmt(ast::Stmt::Return(Some(value)));
2082
2083 let mut c = f.checker();
2084 let void = c.types.void();
2085 c.check_stmt(void, stmt);
2086
2087 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
2088 }
2089
2090 #[test]
2091 fn a_returned_value_is_converted_to_the_return_type() {
2092 let mut f = Fixture::new();
2093 let one = f.int(1);
2094 let stmt = f.stmt(ast::Stmt::Return(Some(one)));
2095
2096 let mut c = f.checker();
2097 let long = c.types.int(IntKind::Long);
2098 let id = c.check_stmt(long, stmt);
2099
2100 assert_eq!(dump(&c, id), "return\n convert arithmetic : long\n const 1 : int\n");
2101 assert!(c.errors.is_empty());
2102 }
2103}