anchor.diff:              member x.Var = v
   
           type UnaryExpression(op : UnaryOperation, e : Expr) =
  -            inherit DerivedExpression(ExpressionType.Unary, e.Type, [e])
  +            inherit DerivedExpression(ExpressionType.Unary, unaryOpResultType op e.Type, [e])
               member x.Operation = op
               member x.Operand = e
           
  
positive.code:                     return id
          }
  
      let getLabelId (l : Label) =
          spirv {
              let! id = tryFindLabelId l
              match id with
                  | Some id ->
                      return id
                  | None ->
                      let! id = newId
                      do! setLabelId l id
                      return id
          }
      
  
      let rec compile (self : Expr) =
          spirv {
              match self with
                  | Return(UnitVal) -> 
                      yield OpReturn
                      return None
  
                  | Return e ->
                      let! e = compile e
                      match e with
                          | Some e ->
                              yield OpReturnValue(e)
                              return None
                          | None ->
                              yield OpReturn
                              return None
  
                  | Binary(op, l, r) ->
                      let! tid = getTypeId self.Type
                      let! lid = compile l
                      let! rid = compile r
                      let! id = newId
  
                      match lid, rid with
                          | Some lid, Some rid ->
                              match l.Type with
                                  | Type.Integral ->
                                      match op with
                                          | Add -> yield OpIAdd(tid, id, lid, rid)
                                          | Sub -> yield OpISub(tid, id, lid, rid)
                                          | Mul -> yield OpIMul(tid, id, lid, rid)
                                          | Div -> 
                                              match self.Type with
                                                  | Type.Signed -> yield OpSDiv(tid, id, lid, rid)
                                                  | _ -> yield OpUDiv(tid, id, lid, rid)
                                          | Mod ->
                                              match self.Type with
                                                  | Type.Signed -> yield OpSMod(tid, id, lid, rid)
                                                  | _ -> yield OpUMod(tid, id, lid, rid)
  
                                          | Greater ->
                                              match self.Type with
                                                  | Type.Signed -> yield OpSGreaterThan(tid, id, lid, rid)
                                                  | _ -> yield OpUGreaterThan(tid, id, lid, rid)
  
                                          | GreaterOrEqual ->
                                              match self.Type with
                                                  | Type.Signed -> yield OpSGreaterThanEqual(tid, id, lid, rid)
                                                  | _ -> yield OpUGreaterThanEqual(tid, id, lid, rid)
  
                                          | Smaller ->
                                              match self.Type with
                                                  | Type.Signed -> yield OpSLessThan(tid, id, lid, rid)
                                                  | _ -> yield OpULessThan(tid, id, lid, rid)
  
                                          | SmallerOrEqual ->
                                              match self.Type with
                                                  | Type.Signed -> yield OpSLessThanEqual(tid, id, lid, rid)
                                                  | _ -> yield OpULessThanEqual(tid, id, lid, rid)
                                          | Equal ->
                                              yield OpIEqual(tid, id, lid, rid)
  
                                          | NotEqual ->
                                              yield OpINotEqual(tid, id, lid, rid)
                                          
                                  | Type.Fractional ->
                                      match op with
                                          | Add -> yield OpFAdd(tid, id, lid, rid)
                                          | Sub -> yield OpFSub(tid, id, lid, rid)
                                          | Mul -> yield OpFMul(tid, id, lid, rid)
                                          | Div -> yield OpFDiv(tid, id, lid, rid)
                                          | Mod -> yield OpFMod(tid, id, lid, rid)
  
                                          | Greater -> yield OpFOrdGreaterThan(tid, id, lid, rid)
                                          | GreaterOrEqual -> yield OpFOrdGreaterThanEqual(tid, id, lid, rid)
                                          | Smaller -> yield OpFOrdLessThan(tid, id, lid, rid)
                                          | SmallerOrEqual -> yield OpFOrdLessThanEqual(tid, id, lid, rid)
                                          | Equal -> yield OpFOrdEqual(tid, id, lid, rid)
                                          | NotEqual -> yield OpFOrdNotEqual(tid, id, lid, rid)
  
                                  | _ ->
                                      failwithf "cannot add type: %A" self.Type
  
                              return Some id
                          | _ ->
                              return failwithf "cannot add non-value expressions: %A %A" l r
  
                  | Unary(op, e) ->
                      let! e = compile e
  
                      match e with
                          | Some e ->
                              let! id = newId
                              let! tid = getTypeId self.Type
                              match self.Type with
                                  | Type.Signed -> yield OpSNegate(tid, id, e)
                                  | _ -> yield OpFNegate(tid, id, e)
  
                              return Some id
                          | None ->
                              return failwithf "non-value expression found %A" e
  
                  | Block(statements) ->
                      for s in statements do
                          let! r = compile s
                          match r with
                              | None -> ()
                              | Some v -> Log.warn "unused value: %A" v
  
                      return None
  
                  | NewObject(t, values) ->
                      
                      let! values = values |> List.mapSpv compile
  
                      let values =
                          if values |> List.forall Option.isSome then
                              values |> List.map (fun v -> v.Value) |> List.toArray
                          else
                              failwith "cannot create composite type from non-value expressions"
  
                      let! id = newId
                      let! tid = getTypeId t
                      yield OpCompositeConstruct(tid, id, values)
  
                      return Some id
  
                  | Var(v) ->
                      let! id = getVarId v
                      return Some id
  
                  | Value(t, v) ->
                      let! t = getTypeId t
                      let! id = newId
  
                      let bytes =
                          match v with
                              | :? int32 as v -> System.BitConverter.GetBytes(v)
                              | :? uint32 as v -> System.BitConverter.GetBytes(v)
                              | :? int64 as v -> System.BitConverter.GetBytes(v)
                              | :? uint64 as v -> System.BitConverter.GetBytes(v)
                              | :? float32 as v -> System.BitConverter.GetBytes(v)
                              | :? float as v -> System.BitConverter.GetBytes(v)
                              | _ -> failwithf "unsupported constant-value: %A" v
  
                      let words =
                          Array.init ((bytes.Length + 3 &&& ~~~3) / 4) (fun i ->
                              System.BitConverter.ToUInt32(bytes, 4 * i)
                          )
  
                      yield OpConstant(t, id, words)
                      return Some id
  
                  | Label(l) ->
                      let! id = getLabelId l
                      yield OpLabel(id)
                      return None
  
                  | Let(v,e,b) ->
                      let! e = compile e
                      match e with
                          | Some e ->
                              let! tid = getTypeId v.Type
                              let! vid = getVarId v
                              
                              yield OpVariable(tid, vid, StorageClass.Private, Some e)
                              yield OpName(vid, v.Name)
  
                              let! b = compile b
                              return b
  
                          | None ->
                              return failwith "cannot let non-value expression"
  
                  | IfThenElse(c,i,e) ->
                      let! c = compile c
  
                      match c with
                          | Some c ->
                              
                              let! trueLabel = getLabelId <| Label()
                              let! falseLabel = getLabelId <| Label()
                              let! endLabel = getLabelId <| Label()
  
                              yield OpBranchConditional(c, trueLabel, falseLabel, [||])
  
                              yield OpLabel(trueLabel)
                              let! _ = compile i
                              yield OpBranch(endLabel)
  
                              yield OpLabel(falseLabel)
                              let! _ = compile e
                              yield OpLabel(endLabel)
  
                              return None
                          | None ->
                              return failwith "non-value condition in ifthenelse"
  
  
                  | _ ->  
                      return None
          }
  
  
  
negative.code:     abstract member Substitute : (Var -> Option<Var>) -> Expr
  
  
  type Function = FunctionDefinition of arguments : list<Var> * body : Expr
  
  
  [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
  module Expr =
  
  