0c3571cfa63a — Leonard Ritter 5 years ago
* added collectors as counterpieces to generators
M lib/scopes/Array.sc +8 -8
@@ 27,19 27,19 @@ fn iLeftChild (i)
 fn iRightChild (i)
     2:i64 * i + 2:i64
 
+inline array-generator (self)
+    Generator
+        inline () 0:usize
+        inline (i) (i < self._count)
+        inline (i) (self @ i)
+        inline (i) (i + 1:usize)
+
 typedef Array < CStruct
 
     @@ box-cast
     fn __as (cls T self)
         if (T == Generator)
-            spice-quote
-                Generator
-                    inline (fdone i)
-                        if (i >= self._count)
-                            fdone;
-                        else
-                            _ (i + 1:usize) (self @ i)
-                    0:usize
+            `(array-generator self)
         else
             compiler-error! "unsupported type"
 

          
M lib/scopes/core.sc +135 -79
@@ 816,6 816,10 @@ sc_typename_type_set_super usize integer
 let Generator = (sc_typename_type "Generator")
 'set-plain-storage Generator ('storageof Closure)
 
+# collector type
+let Collector = (sc_typename_type "Collector")
+'set-plain-storage Collector ('storageof Closure)
+
 # syntax macro type
 let SugarMacro = (sc_typename_type "SugarMacro")
 let SugarMacroFunction =

          
@@ 2139,6 2143,18 @@ let Closure->Generator =
             let self = (bitcast self Generator)
             Value self
 
+let Closure->Collector =
+    spice-macro
+        fn "Closure->Collector" (args)
+            let argc = ('argcount args)
+            verify-count argc 1 1
+            let self = ('getarg args 0)
+            if (not ('constant? self))
+                compiler-error! "Closure must be constant"
+            let self = (as self Closure)
+            let self = (bitcast self Collector)
+            Value self
+
 # (define name expr ...)
 fn expand-define (expr)
     raises-compile-error;

          
@@ 2323,6 2339,28 @@ let va-option =
                 va
 
 #---------------------------------------------------------------------------
+# collector
+#---------------------------------------------------------------------------
+
+'set-symbols Collector
+    __typecall =
+        inline "Collector-new" (cls init valid? at collect)
+            Closure->Collector
+                inline "get-init" ()
+                    _ init valid? at collect
+    __call =
+        spice-macro
+            fn (args)
+                let argc = ('argcount args)
+                verify-count argc 1 1
+                let self = ('getarg args 0)
+                if (not ('constant? self))
+                    compiler-error! "Generator must be constant"
+                let self = (self as Collector)
+                let self = (bitcast self Closure)
+                `(self)
+
+#---------------------------------------------------------------------------
 # for iterator
 #---------------------------------------------------------------------------
 

          
@@ 2927,7 2965,7 @@ define-sugar-macro define-sugar-block-sc
         inline "symbols" (self)
             Generator
                 inline () (sc_type_next self unnamed)
-                inline (key value) (key == unnamed)
+                inline (key value) (key != unnamed)
                 inline (key value) (_ key value)
                 inline (key value) (sc_type_next self key)
     elements =

          
@@ 2939,35 2977,84 @@ define-sugar-macro define-sugar-block-sc
                 inline (i) ('element@ self i)
                 inline (i) (i + 1)
 
-inline scope-generator (self)
-    Generator
-        inline () (sc_scope_next self unnamed)
-        inline (key value) (key == unnamed)
-        inline (key value) (_ key value)
-        inline (key value) (sc_scope_next self key)
-
-inline list-generator (self)
-    Generator
-        inline () self
-        inline (cell) (not (empty? cell))
-        inline (cell) (sc_list_at cell)
-        inline (cell) (sc_list_next cell)
-
-'set-symbols Scope
-    __as =
-        box-cast
-            fn "scope-as" (vT T expr)
-                if (T == Generator)
-                    return `(scope-generator expr)
-                compiler-error! "unsupported type"
-
-'set-symbols list
-    __as =
-        box-cast
-            fn "list-as" (vT T expr)
-                if (T == Generator)
-                    return `(list-generator expr)
-                compiler-error! "unsupported type"
+do
+    inline scope-generator (self)
+        Generator
+            inline () (sc_scope_next self unnamed)
+            inline (key value) (key != unnamed)
+            inline (key value) (_ key value)
+            inline (key value)
+                sc_scope_next self key
+
+    'set-symbols Scope
+        __as =
+            box-cast
+                fn "scope-as" (vT T expr)
+                    if (T == Generator)
+                        return `(scope-generator expr)
+                    compiler-error! "unsupported type"
+
+do
+    inline string-generator (self)
+        let buf sz = ('buffer self)
+        Generator
+            inline () 0:usize
+            inline (i) (i < sz)
+            inline (i) (load (getelementptr buf i))
+            inline (i) (i + 1:usize)
+
+    fn i8->string(c)
+        let ptr = (alloca i8)
+        store c ptr
+        sc_string_new ptr 1
+
+    'set-symbols string
+        __ras =
+            box-cast
+                fn "string-as" (vT T expr)
+                    if (vT == i8)
+                        return `(i8->string expr)
+                    compiler-error! "unsupported type"
+        __as =
+            box-cast
+                fn "string-as" (vT T expr)
+                    if (T == Generator)
+                        return `(string-generator expr)
+                    compiler-error! "unsupported type"
+
+do
+    inline list-generator (self)
+        Generator
+            inline () self
+            inline (cell) (not (empty? cell))
+            inline (cell) (sc_list_at cell)
+            inline (cell) (sc_list_next cell)
+
+    inline list-collector (self)
+        Collector
+            inline () self
+            inline (it) true
+            inline (it) ('reverse it)
+            inline (src it)
+                cons (src) it
+
+    'set-symbols list
+        cons-collector =
+            inline "list-collector" (self)
+                Collector
+                    inline () self
+                    inline (it) true
+                    inline (it) it
+                    inline (src it)
+                        cons (src) it
+        __as =
+            box-cast
+                fn "list-as" (vT T expr)
+                    if (T == Generator)
+                        return `(list-generator expr)
+                    if (T == Collector)
+                        return `(list-collector expr)
+                    compiler-error! "unsupported type"
 
 'set-symbols Value
     args =

          
@@ 2978,6 3065,16 @@ inline list-generator (self)
                 inline (x) (x < argc)
                 inline (x) ('getarg self x)
                 inline (x) (x + 1)
+    arg-appender =
+        inline "Value-args" (self)
+            let argc = ('argcount self)
+            Collector
+                inline () self
+                inline (self) true
+                inline (self) self
+                inline (src self)
+                    sc_argument_list_append self (src)
+                    self
 
 inline range (a b c)
     let num-type = (typeof a)

          
@@ 4201,6 4298,8 @@ sugar from (src 'let params...)
                 cons load-from src
                     quotify params...
 
+define zip (spice-macro (fn (args) (ltr-multiop args (Value zip))))
+
 run-stage;
 
 define-sugar-block-scope-macro static-if

          
@@ 4453,49 4552,6 @@ sugar unlet ((name as Symbol) names...)
 # fold iteration
 #-------------------------------------------------------------------------------
 
-# for <name> ... in <generator> body ...
-#define for
-    sugar-block-scope-macro
-        fn "expand-for" (expr next-expr scope)
-            let head args = (decons expr)
-            let it params =
-                loop (it params = args '())
-                    if (empty? it)
-                        compiler-error! "'in' expected"
-                    let sxat it = (decons it)
-                    let at = (sxat as Symbol)
-                    if (at == 'in)
-                        break it params
-                    _ it (cons sxat params)
-            let generator-expr body = (decons it)
-            let subscope = (Scope scope)
-            spice-quote
-                let start valid? at next =
-                    (as [(sc_expand generator-expr '() subscope)] Generator);
-            return
-                cons
-                    spice-quote start valid? at next # order expressions
-                        loop (it... = (start))
-                            if (valid? it...)
-                                let args... = (at it...)
-                                inline continue ()
-                                    repeat (next it...)
-                                spice-unquote
-                                    let expr =
-                                        loop (params expr = params (list '= args...))
-                                            if (empty? params)
-                                                break expr
-                                            let param next = (decons params)
-                                            _ next (cons param expr)
-                                    let expr = (cons let expr)
-                                    'set-symbol subscope 'continue continue
-                                    sc_expand (cons do expr body) '() subscope
-                                continue;
-                            else
-                                break;
-                    next-expr
-                scope
-
 # fold (<name> ... = <init> ...) for <name> ... in <expr>
 sugar fold ((binding...) 'for expr...)
     fn rjoin-list (lside rside)

          
@@ 4535,15 4591,15 @@ sugar fold ((binding...) 'for expr...)
                 if (valid? it...)
                     inline continue ()
                         repeat (va-append-va state (next it...))
-                    let args... = (va-append-va (inline () (at it...)) (state))
+                    let at... = (at it...)
+                    let state... = (state)
                     let newstate... =
                         spice-unquote
-                            let expr =
-                                cons let
-                                    rjoin-list foldparams
-                                        rjoin-list itparams (list '= args...)
+                            let expr1 expr2 =
+                                cons let (rjoin-list itparams (list '= at...))
+                                cons let (rjoin-list foldparams (list '= state...))
                             'set-symbol subscope 'continue continue
-                            let result = (sc_expand (cons do expr body) '() subscope)
+                            let result = (sc_expand (cons do expr1 expr2 body) '() subscope)
                             result
                     repeat (va-append-va (inline () newstate...) (next it...))
                 else

          
M lib/scopes/glm.sc +8 -12
@@ 423,12 423,10 @@ typedef vec-type < immutable
             let count = ('element-count ST)
             spice-quote
                 Generator
-                    inline (fdone index)
-                        if (index == count)
-                            fdone;
-                        else
-                            _ (index + 1) (extractelement self index)
-                    0
+                    inline () 0
+                    inline (i) (i < count)
+                    inline (i) (extractelement self i)
+                    inline (i) (i + 1)
         else
             compiler-error! "unsupported type"
 

          
@@ 631,12 629,10 @@ typedef mat-type < immutable
             let count = ('element-count ST)
             spice-quote
                 Generator
-                    inline (fdone index)
-                        if (index == count)
-                            fdone;
-                        else
-                            _ (index + 1) (extractvalue self index)
-                    0
+                    inline () 0
+                    inline (i) (i < count)
+                    inline (i) (extractvalue self i)
+                    inline (i) (i + 1)
         else
             compiler-error! "unsupported type"
 

          
M lib/scopes/itertools.sc +287 -30
@@ 1,5 1,8 @@ 
+#---------------------------------------------------------------------------
+# generators
+#---------------------------------------------------------------------------
 
-# for each element in generator a, repeat generator b
+# for each element in generator a, repeat generator b and return both states
 inline span (a b)
     let start-a valid-a at-a next-a = ((a as Generator))
     let start-b valid-b at-b next-b = ((b as Generator))

          
@@ 25,41 28,295 @@ inline span (a b)
             else
                 va-append-va (inline () start-b...) (next-a (it-a))
 
-inline map (gen f)
+spice unpack-dim (n size...)
+    let args = (sc_argument_list_new (active-anchor))
+    fold (expr = n) for S in ('args size...)
+        let arg = `(expr % S)
+        sc_argument_list_append args arg
+        _ `(expr // S)
+    args
+
+spice unpack-bitdim (n size...)
+    let args = (sc_argument_list_new (active-anchor))
+    let _1 = `(1 as (typeof n))
+    fold (expr = n) for S in ('args size...)
+        let arg = `(expr & ((_1 << S) - _1))
+        sc_argument_list_append args arg
+        _ `(expr >> S)
+    args
+
+# a branchless generator that iterates multidimensional coordinates
+@@ spice-quote
+inline dim (x n...)
+    let T = (typeof x)
+    let size = (* x n...)
+    let start = (0 as T)
+    let _1 = (1 as T)
+    Generator
+        inline () start
+        inline (n) (n < size)
+        inline (n) (unpack-dim n x n...)
+        inline (n) (n + _1)
+
+# a variant of dim optimized for power of two sizes; the dimensions are
+    specified as exponents of 2
+@@ spice-quote
+inline bitdim (x n...)
+    let T = (typeof x)
+    let start = (0 as T)
+    let _1 = (1 as T)
+    let size = (_1 << (+ x n...))
+    Generator
+        inline () start
+        inline (n) (n < size)
+        inline (n) (unpack-bitdim n x n...)
+        inline (n) (n + _1)
+
+inline imap (gen f)
     let start valid at next = ((gen as Generator))
     Generator start valid
         inline (it...)
             f (at it...)
         next
 
-inline filter (gen f)
-    let start valid at next = ((gen as Generator))
-    # skip to first valid iterator
-    let start... =
-        loop (it... = (start))
-            if (valid it...)
-                if (f (at it...))
-                    break it...
-                else
-                    repeat (next it...)
-            else
-                break it...
-    Generator
-        inline () start...
-        valid
-        at
-        inline (it...)
-            loop (it... = (next it...))
-                if (valid it...)
-                    if (f (at it...))
-                        break it...
-                    else
-                        repeat (next it...)
-                else
-                    break it...
-        next
-
-define zip (spice-macro (fn (args) (rtl-multiop args (Value zip))))
+define zip (spice-macro (fn (args) (ltr-multiop args (Value zip))))
 define span (spice-macro (fn (args) (rtl-multiop args (Value span))))
 
+#---------------------------------------------------------------------------
+# collectors
+#---------------------------------------------------------------------------
+
+inline collect (coll)
+    """"run collector until full and return the result
+    let start valid? at collect = ((coll as Collector))
+    let start... = (start)
+    loop (state... = start...)
+        if (valid? state...)
+            repeat (collect (inline () ()) state...)
+        else
+            break (at state...)
+
+inline each (generator collector)
+    """"fold output from generator into collector
+    inline _each (collector)
+        let c-init c-valid? c-at c-collect = ((collector as Collector))
+        let g-start g-valid? g-at g-next = ((generator as Generator))
+        let init... = (c-init)
+        let start... = (g-start)
+        let lsize = (va-countof start...)
+        Collector
+            inline () (va-append-va (inline () init...) start...)
+            inline (args...)
+                let it state = (va-split lsize args...)
+                let it... = (it)
+                let state... = (state)
+                (g-valid? it...) & (c-valid? state...)
+            inline (args...)
+                let it state = (va-split lsize args...)
+                let it... = (it)
+                let state... = (state)
+                c-at state...
+            inline (src args...)
+                let it state = (va-split lsize args...)
+                let it... = (it)
+                let state... = (state)
+                let src... = (va-append-va src (g-at it...))
+                let newstate... = (c-collect (inline () src...) state...)
+                va-append-va (inline () newstate...) (g-next it...)
+    static-if (none? collector) _each
+    else (_each collector)
+
+inline comp (collector...)
+    inline (coll)
+        va-rfold coll
+            inline (key value coll)
+                value coll
+            collector...
+
+inline ->> (generator collector...)
+    collect
+        each generator
+            va-rfold none
+                inline (key value coll)
+                    static-if (none? coll) value
+                    else
+                        value coll
+                collector...
+
+inline flatten (coll)
+    """"collect variadic input as individual single items
+    inline _flatten (coll)
+        let init valid? at collect = ((coll as Collector))
+        Collector init valid? at
+            inline (src args...)
+                call
+                    va-lfold (inline () args...)
+                        inline (key value it)
+                            inline ()
+                                collect (inline () value) (it)
+                        src;
+    static-if (none? coll) _flatten
+    else (_flatten coll)
+
+inline map (f coll)
+    inline _map (coll)
+        let init valid? at collect = ((coll as Collector))
+        Collector init valid? at
+            inline (src args...)
+                collect (inline () (f (src))) args...
+    static-if (none? coll) _map
+    else (_map coll)
+
+inline reduce (init f)
+    Collector
+        inline () init
+        inline (it) true
+        inline (it) it
+        inline (src it)
+            f it (src)
+
+let drain =
+    Collector
+        inline () ()
+        inline () true
+        inline () ()
+        inline (src) (src) ()
+
+inline limit (f coll)
+    inline _limit (coll)
+        let init valid? at collect = ((coll as Collector))
+        Collector
+            inline ()
+                _ true (init)
+            inline (ok? it...)
+                ok? & (valid? it...)
+            inline (ok? it...)
+                at it...
+            inline (src ok? it...)
+                let src... = (src)
+                let outit... = (collect (inline () src...) it...)
+                _ (f src...) outit...
+    static-if (none? coll) _limit
+    else (_limit coll)
+
+inline gate (f a b)
+    """"if f is true, collect input in a, otherwise collect in b
+        when both are full, output both
+        until then, new input for full containers is discarded
+    inline _gate (b)
+        let a-init a-valid? a-at a-collect = ((a as Collector))
+        let b-init b-valid? b-at b-collect = ((b as Collector))
+        let a-start... = (a-init)
+        let b-start... = (b-init)
+        let lsize = (va-countof a-start...)
+        let start... =
+            va-append-va (inline () b-start...) a-start...
+        Collector
+            inline () start...
+            inline (it...)
+                let a-it b-it = (va-split lsize it...)
+                (a-valid? (a-it)) | (b-valid? (b-it))
+            inline (it...)
+                let a-it b-it = (va-split lsize it...)
+                let a... = (a-at (a-it))
+                let b... = (b-at (b-it))
+                va-append-va (inline () b...) a...
+            inline (src it...)
+                let a-it b-it = (va-split lsize it...)
+                let src... = (src)
+                if (f src...)
+                    let a-it... = (a-it)
+                    if (a-valid? a-it...)
+                        let a-it... =  (a-collect (inline () src...) a-it...)
+                        return
+                            va-append-va b-it a-it...
+                else
+                    let b-it... = (b-it)
+                    if (b-valid? b-it...)
+                        let b-it... =  (b-collect (inline () src...) (b-it))
+                        return
+                            va-append-va (inline () b-it...) (a-it)
+                it...
+    static-if (none? b) _gate
+    else (_gate b)
+
+inline filter (f coll)
+    inline _filter (coll)
+        let init valid? at collect = ((coll as Collector))
+        Collector init valid? at
+            inline (src rest...)
+                let src... = (src)
+                if (f src...)
+                    collect (inline () src...) rest...
+                else
+                    rest...
+    static-if (none? coll) _filter
+    else (_filter coll)
+
+inline take (n coll)
+    """"limit collector to output n items
+    inline _take (coll)
+        let init valid? at collect = ((coll as Collector))
+        Collector
+            inline () (_ 0 (init))
+            inline (it rest...)
+                (valid? rest...) & (it < n)
+            inline (it rest...) (at rest...)
+            inline (src it rest...)
+                _ (it + 1) (collect src rest...)
+    static-if (none? coll) _take
+    else (_take coll)
+
+inline cascade1 (a b)
+    inline _cascade (b)
+        let start-a valid-a at-a collect-a = ((a as Collector))
+        let start-b valid-b at-b collect-b = ((b as Collector))
+        let start-a... = (start-a)
+        let start-b... = (start-b)
+        let lsize = (va-countof start-a...)
+        let start... = (va-append-va (inline () start-b...) true start-a...)
+        # if a is empty, nothing will ever be produced
+        let a-items? = (valid-a start-a...)
+        Collector
+            inline () start...
+            inline (first? it...)
+                let it-a it-b = (va-split lsize it...)
+                a-items? & (valid-b (it-b))
+            inline (first? it...)
+                let it-a it-b = (va-split lsize it...)
+                let it-b... = (it-b)
+                at-b
+                    if first?
+                        it-b...
+                    else
+                        collect-b (inline () (at-a (it-a))) it-b...
+            inline (src first? it...)
+                let it-a it-b = (va-split lsize it...)
+                let collect-a... = (collect-a src (it-a))
+                if (valid-a collect-a...)
+                    va-append-va it-b false collect-a...
+                else
+                    va-append-va
+                        inline ()
+                            collect-b (inline () (at-a collect-a...)) (it-b)
+                        \ true start-a...
+    static-if (none? b) _cascade
+    else (_cascade b)
+
+inline cascade (collector...)
+    """"two collectors:
+        every time a is full, b collects a and a is reset
+        when b ends, the remainder of a is collected
+    inline (coll)
+        cascade1
+            va-rfold none
+                inline (key value coll)
+                    static-if (none? coll) value
+                    else
+                        value coll
+                collector...
+            coll
+
+unlet cascade1
+
 locals;

          
M src/error.cpp +5 -2
@@ 208,10 208,13 @@ SCOPES_RESULT(void) error_plain_not_stor
     SCOPES_LOCATION_ERROR(ss.str());
 }
 
-SCOPES_RESULT(void) error_value_not_unique(TypedValue *value) {
+SCOPES_RESULT(void) error_value_not_unique(TypedValue *value, const char *by) {
     SCOPES_RESULT_TYPE(void);
     StyledString ss;
-    ss.out << "value of type " << value->get_type() << " is not unique";
+    ss.out << by << " value of type " << value->get_type() << " is not unique";
+    if (!is_plain(value->get_type())) {
+        ss.out << ", but type is unique" << std::endl;
+    }
     SCOPES_LOCATION_ERROR(ss.str());
 }
 

          
M src/error.hpp +1 -1
@@ 113,7 113,7 @@ SCOPES_RESULT(void) error_cannot_deref_n
 SCOPES_RESULT(void) error_cannot_view_moved(TypedValue *value);
 SCOPES_RESULT(void) error_cannot_access_moved(const Type *T, const char *by);
 SCOPES_RESULT(void) error_cannot_return_view(TypedValue *value);
-SCOPES_RESULT(void) error_value_not_unique(TypedValue *value);
+SCOPES_RESULT(void) error_value_not_unique(TypedValue *value, const char *by);
 SCOPES_RESULT(void) error_value_not_plain(TypedValue *value);
 SCOPES_RESULT(void) error_altering_parent_scope_in_pass(const Type *valuetype);
 SCOPES_RESULT(void) error_altering_parent_scope_in_loop(const Type *valuetype);

          
M src/gen_llvm.cpp +17 -0
@@ 1178,6 1178,17 @@ struct LLVMIRGenerator {
         return type_to_llvm_type(SCOPES_GET_RESULT(extract_type_constant(node)));
     }
 
+    LLVMValueRef fix_named_struct_store(LLVMValueRef val, LLVMValueRef ptr) {
+        // fix struct vs named struct
+        auto ET = LLVMGetElementType(LLVMTypeOf(ptr));
+        auto ST = LLVMTypeOf(val);
+        if (ET != ST) {
+            assert(LLVMGetTypeKind(ET) == LLVMStructTypeKind);
+            ptr = LLVMBuildBitCast(builder, ptr, LLVMPointerType(ST, 0), "");
+        }
+        return ptr;
+    }
+
     SCOPES_RESULT(LLVMValueRef) translate_builtin(Builtin builtin, const TypedValues &args) {
         SCOPES_RESULT_TYPE(LLVMValueRef);
         size_t argcount = args.size();

          
@@ 1334,6 1345,9 @@ struct LLVMIRGenerator {
         case FN_Assign: {
             READ_VALUE(lhs);
             READ_VALUE(rhs);
+
+            rhs = fix_named_struct_store(lhs, rhs);
+
             LLVMValueRef retvalue = LLVMBuildStore(builder, lhs, rhs);
             return retvalue;
         } break;

          
@@ 1353,6 1367,9 @@ struct LLVMIRGenerator {
         } break;
         case FN_VolatileStore:
         case FN_Store: { READ_VALUE(val); READ_VALUE(ptr);
+
+            ptr = fix_named_struct_store(val, ptr);
+
             LLVMValueRef retvalue = LLVMBuildStore(builder, val, ptr);
             if (builtin.value() == FN_VolatileStore) { LLVMSetVolatile(retvalue, true); }
             return retvalue;

          
M src/prover.cpp +12 -13
@@ 1546,15 1546,13 @@ static SCOPES_RESULT(void) build_deref_m
     if (rq) {
         SCOPES_CHECK_RESULT(verify_readable(rq, T));
         auto retT = strip_qualifier<ReferQualifier>(T);
-        auto uq = try_unique(T);
-        if (!uq) {
-            SCOPES_EXPECT_ERROR(error_value_not_unique(val));
-        }
         auto call = Call::from(anchor, unique_result_type(ctx, retT), g_deref, { val });
         ctx.append(call);
-        ctx.move(uq->id);
+        auto uq = try_unique(T);
+        if (uq) {
+            ctx.move(uq->id);
+        }
         val = call;
-        return {};
     }
     return {};
 }

          
@@ 1945,7 1943,7 @@ repeat:
             READ_NODEREF_TYPEOF(X);
             auto uq = try_unique(X);
             if (!uq) {
-                SCOPES_CHECK_RESULT(error_value_not_unique(_X));
+                SCOPES_CHECK_RESULT(error_value_not_unique(_X, "move"));
             }
             build_move(ctx, call->anchor(), _X);
             return _X;

          
@@ 1972,7 1970,7 @@ repeat:
             READ_NODEREF_TYPEOF(X);
             auto uq = try_unique(X);
             if (!uq) {
-                SCOPES_CHECK_RESULT(error_value_not_unique(_X));
+                SCOPES_CHECK_RESULT(error_value_not_unique(_X, "lose"));
             }
 
             const Type *DestT = SCOPES_GET_RESULT(storage_type(X));

          
@@ 2541,15 2539,16 @@ repeat:
             if (rq) {
                 SCOPES_CHECK_RESULT(verify_writable(rq, typeof_DestT));
             }
+            typeof_DestT = strip_qualifier<ReferQualifier>(typeof_DestT);
             //strip_qualifiers(ElemT);
             //strip_qualifiers(DestT);
-            SCOPES_CHECK_RESULT(
-                verify(ElemT, DestT));
+
+            SCOPES_CHECK_RESULT(verify(ElemT, DestT));
 
             if (!is_plain(typeof_ElemT)) {
                 auto uq = try_unique(typeof_ElemT);
                 if (!uq) {
-                    SCOPES_CHECK_RESULT(error_value_not_unique(_ElemT));
+                    SCOPES_CHECK_RESULT(error_value_not_unique(_ElemT, "assign"));
                 }
                 ctx.move(uq->id);
             }

          
@@ 2600,7 2599,7 @@ repeat:
             if (!is_plain(typeof_DestT)) {
                 auto uq = try_unique(typeof_ElemT);
                 if (!uq) {
-                    SCOPES_CHECK_RESULT(error_value_not_unique(_ElemT));
+                    SCOPES_CHECK_RESULT(error_value_not_unique(_ElemT, "store"));
                 }
                 ctx.move(uq->id);
             }

          
@@ 2641,7 2640,7 @@ repeat:
             if (!is_plain(T)) {
                 auto uq = try_unique(T);
                 if (!uq) {
-                    SCOPES_CHECK_RESULT(error_value_not_unique(_T));
+                    SCOPES_CHECK_RESULT(error_value_not_unique(_T, "free"));
                 }
                 SCOPES_CHECK_RESULT(build_drop(ctx, call->anchor(), _T));
                 ctx.move(uq->id);

          
A => testing/test_itertools.sc +105 -0
@@ 0,0 1,105 @@ 
+
+using import itertools
+
+for x y z in (dim 1 2 3)
+    print x y z
+print;
+for x y z in (bitdim 2 1 1)
+    print x y z
+
+let binary-range = (range 2)
+let gen =
+    zip
+        imap
+            infinite-range
+            inline (x) (x * 2)
+        span
+            arrayof string "yes" "this" "is" "dog?"
+            binary-range
+            binary-range
+
+for x... in gen
+    print x...
+
+let gen =
+    imap (range 22)
+        inline (x)
+            let ph = (radians ((f32 x) * 16.0))
+            _ (sin ph) (cos ph)
+
+fold (x y z = 0.0 0.0 0.0) for s t in gen
+    print x y z s t
+    _ (x + s) y z
+
+print
+    ->>
+        range 12
+        cascade
+            gate
+                inline (x)
+                    (x & 1) == 0
+                take 2 '()
+                take 2 '()
+        flatten
+        '()
+
+inline string-buffer-sink (maxsize)
+    let buf = (alloca-array i8 maxsize)
+    Collector
+        inline () 0
+        inline (n) (n < maxsize)
+        inline (n)
+            sc_string_new buf (n as usize)
+        inline (src n)
+            store (src) (getelementptr buf n)
+            n + 1
+
+print
+    ->>
+        "the quick brown fox jumped over the lazy dog"
+        cascade
+            limit (inline (x) (x != 32))
+            string-buffer-sink 256
+        '()
+
+assert
+    ==
+        ->>
+            range 26
+            filter
+                inline (x) (x != 10)
+            cascade
+                take 3
+                'cons-collector '()
+            cascade
+                take 3
+                'cons-collector '()
+            'cons-collector '()
+        '(((25) (24 23 22) (21 20 19))
+        ((18 17 16) (15 14 13) (12 11 9))
+        ((8 7 6) (5 4 3) (2 1 0)))
+
+spice va-map (f args...)
+    ->>
+        'args args...
+        map
+            inline (arg) `(f arg)
+        'arg-appender (sc_argument_list_new (active-anchor))
+
+run-stage;
+
+let x y z w =
+    va-map
+        inline (x) (x * 2)
+        \ 1 2 3 4
+
+assert ((+ x y z w) == 20)
+
+#print
+    collect
+        fold-for
+            limit 3
+                list-builder;
+            range 16
+
+

          
M testing/test_spirv_loop.sc +1 -1
@@ 1,6 1,6 @@ 
 
 fn main ()
-    for i in (range 16)
+    for i j in (zip (range 16) (range 16))
     return;