Holy cats I think if statements work!
2 files changed, 150 insertions(+), 4 deletions(-)

M src/compile/mod.rs
M src/compile/tests.rs
M src/compile/mod.rs +65 -4
@@ 45,6 45,17 @@ impl CContext {
         b.set_version(1, 0);
         b.capability(spirv::Capability::Shader);
         b.memory_model(spirv::AddressingModel::Logical, spirv::MemoryModel::Simple);
+        /*
+        TODO: This is more trouble than it's worth right now!
+        My poor unit tests.  ;_;
+                let filename_word = b.string("unknownnfile.cm");
+                b.source(
+                    spirv::SourceLanguage::Unknown,
+                    1,
+                    Some(filename_word),
+                    Some("TODO: contents of unknownfile.cm"),
+                );
+        */
         let typetable = FnvHashMap::default();
         let consts = FnvHashMap::default();
         // Global symbol table for functions, consts, etc...

          
@@ 344,7 355,58 @@ impl CContext {
                 let (w, t) = self.compile_expr(e, ctx)?;
                 self.compile_inferred_uniop(*op, t, w)
             }
-            ast::Expr::If(_cond, _ifpart, _elsepart) => unimplemented!(),
+            ast::Expr::If(cond, ifpart, elsepart) => {
+                // Okay, it looks like we just do:
+                // %condition = ...
+                // OpSelectionMerge %endlabel None
+                // OpBranchConditional %condition %iflabel %elselabel
+                // %iflabel = OpLabel
+                // ...
+                // %ifvalue = ...
+                // OpBranch %endlabel
+                // %elselabel = OpLabel
+                // ...
+                // %elsevalue = ...
+                // OpBranch %endlabel
+                // %endlabel = OpLabel
+                // %result = OpPhi %resulttype %ifvalue %iflabel %elsevalue %elselabel
+                let iflabel_word = self.b.id();
+                let elselabel_word = self.b.id();
+                let endlabel_word = self.b.id();
+
+                // Header
+                let (cond_word, _cond_type) = self.compile_expr(cond, ctx)?;
+                self.b
+                    .selection_merge(endlabel_word, spirv::SelectionControl::NONE)?;
+                self.b
+                    .branch_conditional(cond_word, iflabel_word, elselabel_word, [])?;
+
+                // If part
+                self.b.begin_basic_block(Some(iflabel_word))?;
+                // The turning into a block and to_vec() is a little silly, but effective.
+                let (ifvalue_word, iftype_word) =
+                    self.compile_expr(&ast::Expr::Block(ifpart.to_vec()), ctx)?;
+                self.b.branch(endlabel_word)?;
+
+                // Else part
+                self.b.begin_basic_block(Some(elselabel_word))?;
+                let (elsevalue_word, elsetype_word) =
+                    self.compile_expr(&ast::Expr::Block(elsepart.to_vec()), ctx)?;
+                self.b.branch(endlabel_word)?;
+
+                // Merge block
+                self.b.begin_basic_block(Some(endlabel_word))?;
+                assert_eq!(iftype_word, elsetype_word);
+                let res_word = self.b.phi(
+                    iftype_word,
+                    None,
+                    [
+                        (ifvalue_word, iflabel_word),
+                        (elsevalue_word, elselabel_word),
+                    ],
+                )?;
+                (res_word, iftype_word)
+            }
             ast::Expr::Structure(_name, _vals) => {
                 // Okay I'm going to put this off until I can handle struct layout
                 // stuff.

          
@@ 612,9 674,8 @@ pub fn compile(ctx: &verify::VContext) -
     }
     cc.mongle_entry_points(ctx)?;
     // TODO:
-    // OpSource
-    // OpName, at least for functions
+    // OpSource, though it will heck up the unit tests narsty
+    // OpName for types?
     // OpMemberName for struct's
-    // OpCapability
     Ok(cc)
 }

          
M src/compile/tests.rs +85 -0
@@ 1,3 1,7 @@ 
+// TODO: Uniops
+// Test each side of if arms are the same
+// test each side of bin ops are the same
+// test types for if's
 use crate::ast::{self, *};
 use crate::compile;
 use crate::verify;

          
@@ 25,6 29,8 @@ fn validate_module_header(asm: &str) -> 
     assert!(lines.next().unwrap().contains("OpEntryPoint Vertex"));
     assert!(lines.next().unwrap().contains("OpEntryPoint Fragment"));
     assert!(lines.next().unwrap().contains("OpExecutionMode"));
+    //assert!(lines.next().unwrap().contains("OpString"));
+    //assert!(lines.next().unwrap().contains("OpSource"));
     // Skip OpName debug-info lines
     let mut p = lines.peekable();
     while p.peek().unwrap().contains("OpName ") {

          
@@ 542,3 548,82 @@ OpFunctionEnd"#,
     );
     spirv_val(&m);
 }
+
+#[test]
+fn test_compile_if() {
+    let vert = Decl::Function(FunctionDecl {
+        name: String::from("vertex"),
+        params: vec![Param {
+            name: "i".into(),
+            typ: Type("F32".into()),
+        }],
+        returns: Type("F32".into()),
+        body: vec![Expr::Var("i".into())],
+    });
+    let frag = ast::Decl::Function(ast::FunctionDecl {
+        name: String::from("fragment"),
+        params: vec![ast::Param {
+            name: "input".into(),
+            typ: ast::Type("F32".into()),
+        }],
+        returns: ast::Type("F32".into()),
+        body: vec![Expr::If(
+            Box::new(Expr::Literal(Lit::Bool(true))),
+            vec![Expr::BinOp(
+                Op::Add,
+                Box::new(Expr::Literal(Lit::F32(5.0))),
+                Box::new(Expr::Var("input".into())),
+            )],
+            vec![Expr::BinOp(
+                Op::Mul,
+                Box::new(Expr::Literal(Lit::F32(50.0))),
+                Box::new(Expr::Var("input".into())),
+            )],
+        )],
+    });
+    let program = vec![vert, frag];
+    let ctx = verify::verify(program).unwrap();
+    let c = compile::compile(&ctx).unwrap();
+
+    let m = c.b.module();
+    let dis = m.disassemble();
+    println!("{}", dis);
+    let rest = validate_module_header(&dis);
+    spirv_val(&m);
+    validate_lines(
+        rest,
+        r#"%15 = OpConstantTrue  %4
+%16 = OpConstant  %1  5.0
+%18 = OpConstant  %1  50.0
+%21 = OpTypeFunction %5
+%6 = OpFunction  %1  DontInline|Pure|Const %3
+%7 = OpFunctionParameter  %1
+%8 = OpLabel
+OpReturnValue %7
+OpFunctionEnd
+%9 = OpFunction  %1  DontInline|Pure|Const %3
+%10 = OpFunctionParameter  %1
+%11 = OpLabel
+OpSelectionMerge %14 None
+OpBranchConditional %15 %12 %13
+%12 = OpLabel
+%17 = OpFAdd  %1  %16 %10
+OpBranch %14
+%13 = OpLabel
+%19 = OpFMul  %1  %18 %10
+OpBranch %14
+%14 = OpLabel
+%20 = OpPhi  %1  %17 %12 %19 %13
+OpReturnValue %20
+OpFunctionEnd
+%22 = OpFunction  %5  DontInline|Const %21
+%23 = OpLabel
+OpReturn
+OpFunctionEnd
+%24 = OpFunction  %5  DontInline|Const %21
+%25 = OpLabel
+OpReturn
+OpFunctionEnd"#,
+    );
+    spirv_val(&m);
+}