shithub: mc

Download patch

ref: 7ec5af48e7c7fa40c494b5572fcfa04d3a998cba
parent: e2c679b435ce2e9a71a4247acaf0c9177cd33c0b
parent: ac6c06f906d356b0004ed450558dd890c7057fbf
author: Ori Bernstein <ori@eigenstate.org>
date: Sun Jan 14 15:06:34 EST 2018

Merge branch 'trait-syntax'

--- a/6/gengas.c
+++ b/6/gengas.c
@@ -9,6 +9,7 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <errno.h>
 
 #include "util.h"
 #include "parse.h"
@@ -471,7 +472,8 @@
 	}
 	popstab();
 
-	getcwd(dir, sizeof dir);
+	if (!getcwd(dir, sizeof dir))
+		die("could not get cwd: %s\n", strerror(errno));
 	for (i = 0; i < file->file.nfiles; i++) {
 		path = file->file.files[i];
 		fprintf(fd, ".file %zd \"%s/%s\"\n", i + 1, dir, path);
--- a/doc/lang.txt
+++ b/doc/lang.txt
@@ -190,7 +190,8 @@
 
     3.3. Declarations:
 
-            decl:       attrs ("var" | "const" | "generic")  decllist
+            decl:       attrs declkind  decllist [traitspec]
+	    declkind:   ("var" | "const" | "generic") 
             attrs:      ("extern" | "pkglocal" | "$noret")*
             decllist:   declbody ("," declbody)*
             declbody:   declcore ["=" expr]
@@ -530,9 +531,7 @@
 
     4.3. Generic types:
 
-            typaram:        "@" ident ["::" paramlist]
-            paramlist:      ident | "(" ident ("," ident)* ")"
-            
+            typaram:        "@" ident
 
         A nametype refers to a (potentially parameterized) named type, as
         defined in section 4.5.
@@ -591,7 +590,7 @@
 
         4.4.2. Impls:
 
-                implstmt:        "impl" ident impltypes "=" implbody
+                implstmt:        "impl" ident impltypes traitspec "=" implbody
                 impltypes:       type ["->" type ("," type)*]
                 implbody:        (name [":" type] "=" expr)*
 
@@ -637,8 +636,8 @@
                 specific type that represents them.
                 - Types are freshened. Freshening is the process of replacing
                 unbound type parameters with type variables, such that
-                '@a::(integral,numeric)' is replaced with the type variable
-                '$n::(integral,numeric)'.
+                '@a :: integral @a, numeric @a' is replaced with the type variable
+                '$n :: integral $n, numeric $n'.
                 - Union tags are registered for delayed unification, with the type
                 for unions being the declaration type of the variable.
 
@@ -782,8 +781,8 @@
             a single pass suffices.
 
             At this point, default types are applied. An unconstrained type
-            with type $t::(numeric,integral) is replaced with int. An
-            unconstrained type with $t::(numeric,floating) is replaced with
+            with type $t :: numeric $t, integral $t is replaced with int. An
+            unconstrained type with $t :: numeric $t, floating $t is replaced with
             flt64.
 
     4.6. Built In Traits:
@@ -950,7 +949,7 @@
 
                     e.g. 0x123_fff, 0b1111, 0o777, 1234
 
-                They have the type `@a::(numeric,integral)
+                They have the type `@a :: numeric @a, integral @a
 
             5.1.1.4: Boolean Literals:
 
@@ -980,7 +979,7 @@
 
                     e.g. 123.456, 10.0e7, 1_000.
 
-                They have type `@a::(numeric,floating)`
+                They have type `@a :: numeric @a, floating @a`
 
 
         5.1.2. Sequence and Tuple Literals:
@@ -1043,7 +1042,8 @@
 
         5.1.3. Function Literals:
 
-                funclit:        "{" arglist ["->" rettype] "\n" blockbody "}"
+                funclit:        "{" arglist ["->" rettype] [traitspec] "\n"
+		                blockbody "}"
                 arglist:        (ident [":" type])*
 
             Function literals describe a function. They begin with a '{',
@@ -1461,8 +1461,7 @@
 
             Type:
 
-                ( e1 : @a OP e2 : @a ) : bool
-                where @a :: numeric
+                ( e1 : @a OP e2 : @a ) : bool :: numeric @a
 
 
         5.2.11. Union Constructors:
@@ -1488,8 +1487,7 @@
 
             Type:
 
-                (e1 : @a OP e2:@a) : @a
-                where @a :: integral
+                (e1 : @a OP e2:@a) : @a :: integral @a
 
         5.2.13. Addition:
         
@@ -1503,8 +1501,7 @@
 
             Type:
 
-                ( e1 : @a OP e2 : @a ) : bool
-                where @a :: numeric
+                ( e1 : @a OP e2 : @a ) : bool :: numeric @a
 
         
         5.2.14. Multiplication and Division
@@ -1516,8 +1513,7 @@
 
             Type:
 
-                ( e1 : @a OP e2 : @a ) : bool
-                where @a :: numeric
+                ( e1 : @a OP e2 : @a ) : bool :: numeric @a
 
         5.2.15. Modulo:
         
@@ -1528,8 +1524,7 @@
 
             Type:
 
-                ( e1 : @a OP e2 : @a ) : bool
-                where @a :: (numeric,integral)
+                ( e1 : @a OP e2 : @a ) : bool :: numeric @a, integral @a
 
         5.2.16. Shift:
 
@@ -1545,8 +1540,7 @@
 
             Type:
 
-                (e1 : @a OP e2:@a) : @a
-                where @a :: integral
+                (e1 : @a OP e2:@a) : @a :: integral @a
 
         5.2.17: Postincrement, Postdecrement:
 
@@ -1570,7 +1564,7 @@
 
                 (e1++ : @a) : @a
                 (e1-- : @a) : @a
-                where @a :: integral
+                :: integral @a
 
         5.2.18: Address:
 
@@ -1636,7 +1630,7 @@
 
                 (expr : <aggregate>).name : @a
                 (expr : <seq>).len : @idx
-                where @idx :: (integral,numeric)
+                :: integral @a, numeric @a
 
         5.2.22: Index:
 
@@ -1653,7 +1647,7 @@
 
                 (expr : @a[N])[(idx : @idx)] : @a
                 (expr : @a[:])[(idx : @idx)] : @a
-                where @idx :: (integral,numeric)
+                :: integral, numeric @idx
 
         5.2.23: Slice:
 
@@ -1675,8 +1669,7 @@
                 (expr : @a[N])[(lo : @lo) : (hi : @hi)] : @a[:]
                 (expr : @a[:])[(lo : @lo) : (hi : @hi)] : @a[:]
                 (expr : @#)[(lo : @lo) : (hi : @hi)] : @a[:]
-                where @lo :: (integral,numeric)
-                and   @hi :: (integral,numeric)
+                :: integral, numeric @lo, integral, numeric @hi
 
         5.2.24: Call:
 
@@ -1960,7 +1953,7 @@
     pkgbody:   (decl | attrs tydef | traitdef | impldef)*
 
     /* declarations */
-    decl:       attrs ("var" | "const" | "generic") decllist
+    decl:       attrs ("var" | "const" | "generic") decllist 
     attrs:      ("$noret" | "extern" | "pkglocal")*
     decllist:   declbody ("," declbody)+
     declbody:   declcore ["=" expr]
@@ -1967,14 +1960,16 @@
     declcore:   ident [":" type]
 
     /* traits */
-    traitdef:   "trait" ident typaram [auxtypes] ("\n" | "=" traitbody ";;")
+    traitdef:   "trait" ident typaram [auxtypes] [traitspec] ("\n" | "=" traitbody ";;")
     auxtypes:   "->" type ("," type)*
     traitbody:  "\n"* (ident ":" type "\n"*)*
-    impldef:    "impl" ident type [auxtypes] ("\n" | "=" implbody ";;")
+    impldef:    "impl" ident type [auxtypes] [traitspec] ("\n" | "=" implbody ";;")
     implbody:   "\n"* (ident [":" type] "=" expr "\n"*)*
 
     /* types */
-    tydef:      "type" typeid "=" type
+    traitspec:  "::" traitvar+
+    traitvar:   name+ type ["->" type]
+    tydef:      "type" typeid [traitspec] "=" type
     typeid:     ident | ident "(" typarams ")"
     typarams:   typaram ("," typaram)*
     type:       structdef | uniondef | tupledef |
@@ -1984,8 +1979,7 @@
     uniondef:   "union" unionbody ";;"
     unionbody:	("`" ident [type])*
     tupledef:   "(" type ("," type)* ")"
-    generic:    typaram ["::" traitlist]
-    traitlist:  name | "(" name ("," name)
+    generic:    typaram
     constructed: functype | sicetype | arraytype | ptrtype | void | name
     functype:   "(" arglist "->" type ")"
     arglist:    [arg ("," arg)*]
@@ -2024,7 +2018,7 @@
 
     /* literals */
     literal:    funclit | seqlit | tuplit | simplelit
-    funclit:    "{" [funcargs] ";" blockbody "}"
+    funclit:    "{" [funcargs] [traitspec] "\n" blockbody "}"
     funcargs:   ident [ ":" type] ("," ident [ ":" type])*
     seqlit:     "[" structelts | [arrayelts] "]"
     arrayelts:  arrayelt ("," arrayelt)*
--- a/lib/bio/bio.myr
+++ b/lib/bio/bio.myr
@@ -283,7 +283,7 @@
   writes a single integer-like value to the output stream, in
   little endian format
 */
-generic putle = {f, v : @a::(numeric,integral)
+generic putle = {f, v : @a :: numeric,integral @a
 	for var i = 0; i < sizeof(@a); i++
 		putb(f, (v & 0xff : byte))
 		v >>= 8
@@ -295,7 +295,7 @@
   writes a single integer-like value to the output stream, in
   big endian format
 */
-generic putbe = {f, v : @a::(numeric,integral)
+generic putbe = {f, v : @a :: numeric,integral @a
 	for var i = sizeof(@a); i != 0; i--
 		putb(f, ((v >> ((i-1)*8)) & 0xff : byte))
 	;;
--- a/lib/bio/geti.myr
+++ b/lib/bio/geti.myr
@@ -5,16 +5,16 @@
 
 pkg bio =
 	/* unsigned big endian */
-	generic getbe8	: (f : file# -> std.result(@a::(numeric,integral), err))
-	generic getbe16	: (f : file# -> std.result(@a::(numeric,integral), err))
-	generic getbe32	: (f : file# -> std.result(@a::(numeric,integral), err))
-	generic getbe64	: (f : file# -> std.result(@a::(numeric,integral), err))
+	generic getbe8	: (f : file# -> std.result(@a, err)) :: numeric,integral @a
+	generic getbe16	: (f : file# -> std.result(@a, err)) :: numeric,integral @a
+	generic getbe32	: (f : file# -> std.result(@a, err)) :: numeric,integral @a
+	generic getbe64	: (f : file# -> std.result(@a, err)) :: numeric,integral @a
 
 	/* signed big endian */
-	generic getle8	: (f : file# -> std.result(@a::(numeric,integral), err))
-	generic getle16	: (f : file# -> std.result(@a::(numeric,integral), err))
-	generic getle32	: (f : file# -> std.result(@a::(numeric,integral), err))
-	generic getle64	: (f : file# -> std.result(@a::(numeric,integral), err))
+	generic getle8	: (f : file# -> std.result(@a, err)) :: numeric,integral @a
+	generic getle16	: (f : file# -> std.result(@a, err)) :: numeric,integral @a
+	generic getle32	: (f : file# -> std.result(@a, err)) :: numeric,integral @a
+	generic getle64	: (f : file# -> std.result(@a, err)) :: numeric,integral @a
 ;;
 
 /*
@@ -21,7 +21,7 @@
   reads a single integer-like value to the output stream, in
   little endian format
 */
-generic getle = {f, n -> std.result(@a::(numeric,integral), err)
+generic getle = {f, n -> std.result(@a, err) :: numeric,integral @a
 	match ensureread(f, n)
 	| `std.Err e :	-> `std.Err e
 	| `std.Ok _:
@@ -29,7 +29,7 @@
 		for var i = 0; i < n; i++
 			v |= (f.rbuf[f.rstart++] : uint64) << (8*(i : uint64))
 		;;
-		-> `std.Ok (v : @a::(numeric,integral))
+		-> `std.Ok (v : @a)
 	;;
 }
 
@@ -37,7 +37,7 @@
   reads a single integer-like value to the output stream, in
   big endian format
 */
-generic getbe = {f, n -> std.result(@a::(numeric,integral), err)
+generic getbe = {f, n -> std.result(@a, err) :: numeric,integral @a
 	match ensureread(f, n)
 	| `std.Err e :	-> `std.Err e
 	| `std.Ok _:
@@ -46,7 +46,7 @@
 			v <<= 8
 			v |= (f.rbuf[f.rstart++] : uint64)
 		;;
-		-> `std.Ok (v : @a::(numeric,integral))
+		-> `std.Ok (v : @a)
 	;;
 }
 
--- a/lib/bio/puti.myr
+++ b/lib/bio/puti.myr
@@ -5,16 +5,16 @@
 
 pkg bio =
 	/* unsigned big endian */
-	generic putbe8	: (f : file#, v : @a::(numeric,integral) -> std.result(std.size, err))
-	generic putbe16	: (f : file#, v : @a::(numeric,integral) -> std.result(std.size, err))
-	generic putbe32	: (f : file#, v : @a::(numeric,integral) -> std.result(std.size, err))
-	generic putbe64	: (f : file#, v : @a::(numeric,integral) -> std.result(std.size, err))
+	generic putbe8	: (f : file#, v : @a -> std.result(std.size, err)) :: numeric,integral @a
+	generic putbe16	: (f : file#, v : @a -> std.result(std.size, err)) :: numeric,integral @a
+	generic putbe32	: (f : file#, v : @a -> std.result(std.size, err)) :: numeric,integral @a
+	generic putbe64	: (f : file#, v : @a -> std.result(std.size, err)) :: numeric,integral @a
 
 	/* unsigned little endian */
-	generic putle8	: (f : file#, v : @a::(numeric,integral) -> std.result(std.size, err))
-	generic putle16	: (f : file#, v : @a::(numeric,integral) -> std.result(std.size, err))
-	generic putle32	: (f : file#, v : @a::(numeric,integral) -> std.result(std.size, err))
-	generic putle64	: (f : file#, v : @a::(numeric,integral) -> std.result(std.size, err))
+	generic putle8	: (f : file#, v : @a -> std.result(std.size, err)) :: numeric,integral @a
+	generic putle16	: (f : file#, v : @a -> std.result(std.size, err)) :: numeric,integral @a
+	generic putle32	: (f : file#, v : @a -> std.result(std.size, err)) :: numeric,integral @a
+	generic putle64	: (f : file#, v : @a -> std.result(std.size, err)) :: numeric,integral @a
 ;;
 
 generic putbe8  = {f, v; -> putbe(f, (v : uint64), 1)}
--- a/lib/crypto/rand.myr
+++ b/lib/crypto/rand.myr
@@ -8,8 +8,8 @@
 pkg crypto =
 	/* designed to mirror std.rand() */
 	const randbytes	: (buf : byte[:] -> void)
-	generic rand	: (lo : @a::(integral,numeric), hi : @a::(integral,numeric) -> @a::(numeric,integral))
-	generic randnum	: (-> @a::(numeric,integral))
+	generic rand	: (lo : @a, hi : @a -> @a) ::numeric,integral @a
+	generic randnum	: (-> @a) :: numeric,integral @a
 ;;
 
 const Stirinterval = 16*std.MiB
--- a/lib/date/parse.myr
+++ b/lib/date/parse.myr
@@ -239,9 +239,8 @@
 		-> s
 	;;
 }
-generic intval = {dst : @a::(numeric,integral)#, s : byte[:], \
-		min : @a::(numeric,integral), max : @a::(numeric,integral), \
-		err : std.option(parsefail)# -> byte[:]
+
+generic intval = {dst : @a#, s : byte[:], min : @a, max : @a, err : std.option(parsefail)# -> byte[:] :: numeric,integral @a
 	var i, c, num
 
 	num = s
--- a/lib/iter/bld.sub
+++ b/lib/iter/bld.sub
@@ -9,3 +9,9 @@
 	lib ../sys:sys
 	lib ../std:std
 ;;
+
+testdeps =
+	../testr:testr
+	../sys:sys
+	../std:std
+;;
--- a/lib/std/bigint.myr
+++ b/lib/std/bigint.myr
@@ -22,7 +22,7 @@
 	;;
 
 	/* administrivia */
-	generic mkbigint	: (v : @a::(numeric,integral) -> bigint#)
+	generic mkbigint	: (v : @a -> bigint#) :: numeric,integral @a
 	const bigfrombytes	: (isneg : bool, v : byte[:] -> bigint#)
 	const bigfree	: (a : bigint# -> void)
 	const bigdup	: (a : bigint# -> bigint#)
@@ -40,7 +40,7 @@
 	const bigiszero	: (a : bigint# -> bool)
 	const bigiseven	: (a : bigint# -> bool)
 	const bigcmp	: (a : bigint#, b : bigint# -> order)
-	generic bigcmpi	: (a : bigint#, b : @a::(numeric,integral) -> order)
+	generic bigcmpi	: (a : bigint#, b : @a -> order) :: numeric,integral @a
 
 	/* shorthand for comparisons */
 	const bigeq	: (a : bigint#, b : bigint# -> bool)
@@ -48,11 +48,11 @@
 	const bigle	: (a : bigint#, b : bigint# -> bool)
 	const biggt	: (a : bigint#, b : bigint# -> bool)
 	const bigge	: (a : bigint#, b : bigint# -> bool)
-	generic bigeqi	: (a : bigint#, b : @a::(numeric,integral) -> bool)
-	generic biglti	: (a : bigint#, b : @a::(numeric,integral) -> bool)
-	generic biglei	: (a : bigint#, b : @a::(numeric,integral) -> bool)
-	generic biggti	: (a : bigint#, b : @a::(numeric,integral) -> bool)
-	generic biggei	: (a : bigint#, b : @a::(numeric,integral) -> bool)
+	generic bigeqi	: (a : bigint#, b : @a -> bool) :: numeric,integral @a
+	generic biglti	: (a : bigint#, b : @a -> bool) :: numeric,integral @a
+	generic biglei	: (a : bigint#, b : @a -> bool) :: numeric,integral @a
+	generic biggti	: (a : bigint#, b : @a -> bool) :: numeric,integral @a
+	generic biggei	: (a : bigint#, b : @a -> bool) :: numeric,integral @a
 
 	/* bigint*bigint -> bigint ops */
 	const bigadd	: (a : bigint#, b : bigint# -> bigint#)
@@ -71,15 +71,15 @@
 
 
 	/* bigint*int -> bigint ops */
-	generic bigaddi	: (a : bigint#, b : @a::(integral,numeric) -> bigint#)
-	generic bigsubi	: (a : bigint#, b : @a::(integral,numeric) -> bigint#)
-	generic bigmuli	: (a : bigint#, b : @a::(integral,numeric) -> bigint#)
-	generic bigdivi	: (a : bigint#, b : @a::(integral,numeric) -> bigint#)
-	generic bigmodi	: (a : bigint#, b : @a::(integral,numeric) -> bigint#)
-	generic bigshli	: (a : bigint#, b : @a::(integral,numeric) -> bigint#)
-	generic bigshri	: (a : bigint#, b : @a::(integral,numeric) -> bigint#)
-	generic bigandi	: (a : bigint#, b : @a::(integral,numeric) -> bigint#)
-	generic bigori	: (a : bigint#, b : @a::(integral,numeric) -> bigint#)
+	generic bigaddi	: (a : bigint#, b : @a -> bigint#) :: integral,numeric @a
+	generic bigsubi	: (a : bigint#, b : @a -> bigint#) :: integral,numeric @a
+	generic bigmuli	: (a : bigint#, b : @a -> bigint#) :: integral,numeric @a
+	generic bigdivi	: (a : bigint#, b : @a -> bigint#) :: integral,numeric @a
+	generic bigmodi	: (a : bigint#, b : @a -> bigint#) :: integral,numeric @a
+	generic bigshli	: (a : bigint#, b : @a -> bigint#) :: integral,numeric @a
+	generic bigshri	: (a : bigint#, b : @a -> bigint#) :: integral,numeric @a
+	generic bigandi	: (a : bigint#, b : @a -> bigint#) :: integral,numeric @a
+	generic bigori	: (a : bigint#, b : @a -> bigint#) :: integral,numeric @a
 
 	//const bigpowi	: (a : bigint#, b : uint64 -> bigint#)
 	//const bigmodpowi	: (b : bigint#, e : bigint#, m : bigint# -> bigint#)
@@ -93,7 +93,7 @@
 
 const Base = 0x100000000ul
 
-generic mkbigint = {v : @a::(integral,numeric)
+generic mkbigint = {v : @a :: integral,numeric @a
 	var a
 	var val
 
@@ -771,7 +771,7 @@
 	-> a
 }
 
-generic bigsubi = {a, b : @a::(numeric,integral)
+generic bigsubi = {a, b : @a :: numeric,integral @a
 	var bigb : bigint
 	var dig : uint32[2]
 
@@ -811,7 +811,7 @@
   a << s, with integer arg.
   logical left shift. any other type would be illogical.
  */
-generic bigshli = {a, s : @a::(numeric,integral)
+generic bigshli = {a, s : @a :: numeric,integral @a
 	var off, shift
 	var t, carry
 
--- a/lib/std/bitset.myr
+++ b/lib/std/bitset.myr
@@ -22,9 +22,9 @@
 	const bsmax	: (a : bitset# -> size)
 	const bscount	: (a : bitset# -> size)
 
-	generic bsput	: (bs : bitset#, v : @a::(integral,numeric) -> bool)
-	generic bsdel	: (bs : bitset#, v : @a::(integral,numeric) -> bool)
-	generic bshas	: (bs : bitset#, v : @a::(integral,numeric) -> bool)
+	generic bsput	: (bs : bitset#, v : @a -> bool) :: integral,numeric @a
+	generic bsdel	: (bs : bitset#, v : @a -> bool) :: integral,numeric @a
+	generic bshas	: (bs : bitset#, v : @a -> bool) :: integral,numeric @a
 
 	const bsdiff	: (a : bitset#, b : bitset# -> void)
 	const bsintersect	: (a : bitset#, b : bitset# -> void)
--- a/lib/std/chartype.myr
+++ b/lib/std/chartype.myr
@@ -25,7 +25,7 @@
 	const toupper	: (c : char -> char)
 	const totitle	: (c : char -> char)
 
-	generic charval : (c : char, base : int -> @a::(integral,numeric))
+	generic charval : (c : char, base : int -> @a) :: integral,numeric @a
 
 	const cellwidth	: (c : char -> int)
 ;;
@@ -1670,18 +1670,18 @@
 	-> c
 }
 
-generic charval = {c, base -> @a::(numeric,integral)
+generic charval = {c, base -> @a :: numeric,integral @a
 	var v = -1
 
 	if c >= '0' && c <= '9'
-		v =  (c - '0' : @a::(integral,numeric))
+		v =  (c - '0' : @a)
 	elif c >= 'a' && c <= 'z'
-		v =  (c - 'a' + 10 : @a::(integral,numeric))
+		v =  (c - 'a' + 10 : @a)
 	elif c >= 'A' && c <= 'Z'
-		v =  (c - 'A' + 10 : @a::(integral,numeric))
+		v =  (c - 'A' + 10 : @a)
 	;;
 
-	if v < 0 || v >= (base : @a::(integral,numeric))
+	if v < 0 || v >= (base : @a)
 		-> -1
 	;;
 	-> v
--- a/lib/std/endian.myr
+++ b/lib/std/endian.myr
@@ -1,16 +1,16 @@
 pkg std =
-	generic hosttonet	: (v : @a::(integral,numeric) -> @a::(integral,numeric))
-	generic nettohost	: (v : @a::(integral,numeric) -> @a::(integral,numeric))
+	generic hosttonet	: (v : @a -> @a) :: integral,numeric @a
+	generic nettohost	: (v : @a -> @a) :: integral,numeric @a
 ;;
 
 /* FIXME: we only support little endian platforms right now,
    so we assume a little endian machine. FIX THIS. */
-generic hosttonet = {v : @a::(integral,numeric)
+generic hosttonet = {v : @a :: integral,numeric @a
 	var i
 	var ret
 
 	ret = 0
-	for i = 0; i < sizeof(@a::(integral,numeric)); i++
+	for i = 0; i < sizeof(@a); i++
 		ret <<= 8
 		ret |= v & 0xff 
 		v >>= 8
@@ -18,12 +18,12 @@
 	-> ret
 }
 
-generic nettohost = {v : @a::(integral,numeric)
+generic nettohost = {v : @a :: integral,numeric @a
 	var i
 	var ret
 
 	ret = 0
-	for i = 0; i < sizeof(@a::(integral,numeric)); i++
+	for i = 0; i < sizeof(@a); i++
 		ret <<= 8
 		ret |= v & 0xff 
 		v >>= 8
--- a/lib/std/extremum.myr
+++ b/lib/std/extremum.myr
@@ -1,8 +1,8 @@
 pkg std =
-	generic min	: (a : @a::numeric, b : @a::numeric  -> @a::numeric)
-	generic max	: (a : @a::numeric, b : @a::numeric  -> @a::numeric)
-	generic clamp	: (a : @a::numeric, min : @a::numeric, max : @a::numeric -> @a::numeric)
-	generic abs	: (a : @a::numeric -> @a::numeric)
+	generic min	: (a : @a, b : @a -> @a)		:: numeric @a
+	generic max	: (a : @a, b : @a -> @a)		:: numeric @a
+	generic clamp	: (a : @a, min : @a, max : @a -> @a)	:: numeric @a
+	generic abs	: (a : @a -> @a)			:: numeric @a
 ;;
 
 generic min = {a, b
@@ -31,8 +31,8 @@
 	;;
 }
 
-generic abs = {a : @a::numeric
-	if a < (0 : @a::numeric)
+generic abs = {a : @a :: numeric @a
+	if a < (0 : @a)
 		-> -a
 	else
 		-> a
--- a/lib/std/fltbits.myr
+++ b/lib/std/fltbits.myr
@@ -6,7 +6,7 @@
 	const flt32inf	: (-> flt32)
 	const flt32nan	: (-> flt32)
 
-	generic isnan	: (f : @a::floating -> bool)
+	generic isnan		: (f : @a -> bool) ::floating @a
 	const flt64frombits	: (bits : uint64 -> flt64)
 	const flt32frombits	: (bits : uint32 -> flt32)
 	const flt64explode	: (flt : flt64 -> (bool, int64, int64))
--- a/lib/std/fmt.myr
+++ b/lib/std/fmt.myr
@@ -537,7 +537,7 @@
 	'5','6','7','8','9',
 	'a','b','c','d','e','f'
 ]
-generic intfmt = {sb, opts, signed, bits : @a::(integral,numeric)
+generic intfmt = {sb, opts, signed, bits : @a :: integral,numeric @a
 	var isneg
 	var sval, val
 	var b : char[32]
@@ -557,7 +557,7 @@
 		;;
 	else
 		val = (bits : uint64)
-		val &= ~0 >> (8*(sizeof(uint64)-sizeof(@a::(integral,numeric))))
+		val &= ~0 >> (8*(sizeof(uint64)-sizeof(@a)))
 		isneg = false
 	;;
 
--- a/lib/std/fndup.myr
+++ b/lib/std/fndup.myr
@@ -5,10 +5,10 @@
 use "types"
 
 pkg std =
-	generic fnenvsz	: (fn : @fn::function -> size)
-	generic fndup	: (fn : @fn::function -> @fn::function)
-	generic fnbdup	: (fn : @fn::function, buf : byte[:] -> @fn::function)
-	generic fnfree	: (fn : @fn::function -> void)
+	generic fnenvsz	: (fn : @fn -> size)	:: function @fn
+	generic fndup	: (fn : @fn -> @fn)	:: function @fn
+	generic fnbdup	: (fn : @fn, buf : byte[:] -> @fn)	:: function @fn
+	generic fnfree	: (fn : @fn -> void)	:: function @fn
 ;;
 
 generic fndup = {fn
@@ -27,7 +27,7 @@
 
 extern const put : (fmt : byte[:], args : ... -> int64)
 
-generic fnbdup = {fn, buf
+generic fnbdup = {fn : @fn, buf	:: function @fn
 	var repr : intptr[2]
 	var env
 
@@ -35,7 +35,7 @@
 	env = envslice(repr[0])
 	slcp(buf[:env.len], env)
 	repr[0] = (buf : intptr)
-	-> (&repr : @fn::function#)#
+	-> (&repr : @fn#)#
 }
 
 generic fnfree = {fn
--- a/lib/std/getint.myr
+++ b/lib/std/getint.myr
@@ -2,95 +2,95 @@
 use "memops"
 
 pkg std =
-	generic gethost64	: (buf : byte[:]	-> @a::(numeric,integral))
-	generic getle64		: (buf : byte[:]	-> @a::(numeric,integral))
-	generic getbe64		: (buf : byte[:]	-> @a::(numeric,integral))
-	generic gethost32	: (buf : byte[:]	-> @a::(numeric,integral))
-	generic getle32		: (buf : byte[:]	-> @a::(numeric,integral))
-	generic getbe32		: (buf : byte[:]	-> @a::(numeric,integral))
-	generic gethost16	: (buf : byte[:]	-> @a::(numeric,integral))
-	generic getle16		: (buf : byte[:]	-> @a::(numeric,integral))
-	generic getbe16		: (buf : byte[:]	-> @a::(numeric,integral))
-	generic gethost8	: (buf : byte[:]	-> @a::(numeric,integral))
-	generic getle8		: (buf : byte[:]	-> @a::(numeric,integral))
-	generic getbe8		: (buf : byte[:]	-> @a::(numeric,integral))
+	generic gethost64	: (buf : byte[:]	-> @a) :: numeric,integral @a
+	generic getle64		: (buf : byte[:]	-> @a) :: numeric,integral @a
+	generic getbe64		: (buf : byte[:]	-> @a) :: numeric,integral @a
+	generic gethost32	: (buf : byte[:]	-> @a) :: numeric,integral @a
+	generic getle32		: (buf : byte[:]	-> @a) :: numeric,integral @a
+	generic getbe32		: (buf : byte[:]	-> @a) :: numeric,integral @a
+	generic gethost16	: (buf : byte[:]	-> @a) :: numeric,integral @a
+	generic getle16		: (buf : byte[:]	-> @a) :: numeric,integral @a
+	generic getbe16		: (buf : byte[:]	-> @a) :: numeric,integral @a
+	generic gethost8	: (buf : byte[:]	-> @a) :: numeric,integral @a
+	generic getle8		: (buf : byte[:]	-> @a) :: numeric,integral @a
+	generic getbe8		: (buf : byte[:]	-> @a) :: numeric,integral @a
 ;;
 
-generic gethost64 = {buf -> @a::(numeric,integral)
+generic gethost64 = {buf -> @a :: numeric,integral @a
 	var val : int64
 	iassert(buf.len >= 8, "gethost64: index out of bounds")
 	memblit((&val : byte#), (buf : byte#), 8)
-	-> (val : @a::(numeric,integral))
+	-> (val : @a)
 }
 
-generic getbe64 = {buf -> @a::(numeric,integral)
-	-> ((buf[0] : @a::(numeric,integral)) << 56) | \
-		((buf[1] : @a::(numeric,integral)) << 48) | \
-		((buf[2] : @a::(numeric,integral)) << 40) | \
-		((buf[3] : @a::(numeric,integral)) << 32) | \
-		((buf[4] : @a::(numeric,integral)) << 24) | \
-		((buf[5] : @a::(numeric,integral)) << 16) | \
-		((buf[6] : @a::(numeric,integral)) << 8) | \
-		((buf[7] : @a::(numeric,integral)) << 0)
+generic getbe64 = {buf -> @a :: numeric,integral @a
+	->	((buf[0] : @a) << 56) | \
+		((buf[1] : @a) << 48) | \
+		((buf[2] : @a) << 40) | \
+		((buf[3] : @a) << 32) | \
+		((buf[4] : @a) << 24) | \
+		((buf[5] : @a) << 16) | \
+		((buf[6] : @a) << 8) | \
+		((buf[7] : @a) << 0)
 }
 
 generic getle64 = {buf
-	-> ((buf[0] : @a::(numeric,integral))  << 0) | \
-		((buf[1] : @a::(numeric,integral))  << 8) | \
-		((buf[2] : @a::(numeric,integral))  << 16) | \
-		((buf[3] : @a::(numeric,integral))  << 24) | \
-		((buf[4] : @a::(numeric,integral))  << 32) | \
-		((buf[5] : @a::(numeric,integral))  << 40) | \
-		((buf[6] : @a::(numeric,integral))  << 48) | \
-		((buf[7] : @a::(numeric,integral))  << 56)
+	->	((buf[0] : @a)  << 0) | \
+		((buf[1] : @a)  << 8) | \
+		((buf[2] : @a)  << 16) | \
+		((buf[3] : @a)  << 24) | \
+		((buf[4] : @a)  << 32) | \
+		((buf[5] : @a)  << 40) | \
+		((buf[6] : @a)  << 48) | \
+		((buf[7] : @a)  << 56)
 }
 
-generic gethost32 = {buf -> @a::(numeric,integral)
+generic gethost32 = {buf -> @a :: numeric,integral @a
 	var val : int32
 	iassert(buf.len >= 4, "gethost32: index out of bounds")
 	memblit((&val : byte#), (buf : byte#), 4)
-	-> (val : @a::(numeric,integral))
+	-> (val : @a)
 }
 
-generic getbe32 = {buf
-	-> ((buf[0] : @a::(numeric,integral)) << 24) | \
-		((buf[1] : @a::(numeric,integral)) << 16) | \
-		((buf[2] : @a::(numeric,integral)) << 8) | \
-		((buf[3] : @a::(numeric,integral)) << 0)
+generic getbe32 = {buf -> @a :: numeric, integral @a
+	->	((buf[0] : @a) << 24) | \
+		((buf[1] : @a) << 16) | \
+		((buf[2] : @a) << 8) | \
+		((buf[3] : @a) << 0)
 }
 
-generic getle32 = {buf
-	-> ((buf[0] : @a::(numeric,integral)) << 0) | \
-		((buf[1] : @a::(numeric,integral)) << 8) | \
-		((buf[2] : @a::(numeric,integral)) << 16) | \
-		((buf[3] : @a::(numeric,integral)) << 24)
+generic getle32 = {buf -> @a :: numeric, integral @a
+	-> ((buf[0] : @a) << 0) | \
+		((buf[1] : @a) << 8) | \
+		((buf[2] : @a) << 16) | \
+		((buf[3] : @a) << 24)
 }
 
-generic gethost16 = {buf -> @a::(numeric,integral)
+generic gethost16 = {buf -> @a :: numeric,integral @a
 	var val : int16
 	iassert(buf.len >= 2, "gethost16: index out of bounds")
 	memblit((&val : byte#), (buf : byte#), 4)
-	-> (val : @a::(numeric,integral))
+	-> (val : @a)
 }
 
-generic getbe16 = {buf
-	-> ((buf[0] : @a::(numeric,integral)) << 8) | \
-		((buf[1] : @a::(numeric,integral)) << 0)
+generic getbe16 = {buf -> @a :: numeric,integral @a
+	-> ((buf[0] : @a) << 8) | \
+		((buf[1] : @a) << 0)
 }
 
-generic getle16 = {buf
-	-> ((buf[0] : @a::(numeric,integral)) << 0) | \
-		((buf[1] : @a::(numeric,integral)) << 8)
+generic getle16 = {buf -> @a :: numeric,integral @a
+	-> ((buf[0] : @a) << 0) | \
+		((buf[1] : @a) << 8)
 }
 
-generic gethost8 = {buf
-	-> (buf[0] : @a::(numeric,integral)) << 0
+generic gethost8 = {buf -> @a :: numeric,integral @a
+	-> (buf[0] : @a) << 0
 }
 
-generic getbe8 = {buf
-	-> (buf[0] : @a::(numeric,integral)) << 0
+generic getbe8 = {buf -> @a :: numeric,integral @a
+	-> (buf[0] : @a) << 0
 }
 
-generic getle8 = {buf
-	-> (buf[0] : @a::(numeric,integral)) << 0
+generic getle8 = {buf -> @a :: numeric,integral @a
+	-> (buf[0] : @a) << 0
 }
--- a/lib/std/hashfuncs.myr
+++ b/lib/std/hashfuncs.myr
@@ -23,13 +23,13 @@
 		}
 	;;
 
-	impl equatable @a::(integral,numeric) =
+	impl equatable @a :: integral,numeric @a =
 		eq = {a, b
 			-> a == b
 		}
 	;;
 
-	impl hashable @a::(integral,numeric) =
+	impl hashable @a :: integral,numeric @a =
 		hash = {a
 			-> siphash24((&a : byte#)[:sizeof(@a)], Seed)
 		}
--- a/lib/std/intparse.myr
+++ b/lib/std/intparse.myr
@@ -7,8 +7,8 @@
 use "utf"
 
 pkg std =
-	generic intparsebase	: (s : byte[:], base : int -> option(@a::(integral,numeric)))
-	generic intparse	: (s : byte[:]	-> option(@a::(integral,numeric)))
+	generic intparsebase	: (s : byte[:], base : int -> option(@a)) :: integral,numeric @a
+	generic intparse	: (s : byte[:]	-> option(@a)) ::  integral,numeric @a
 ;;
 
 generic intparse = {s
@@ -47,7 +47,7 @@
 	-> doparse(s, isneg, base)
 }
 
-generic doparse = {s, isneg, base ->  option(@a::(integral,numeric))
+generic doparse = {s, isneg, base ->  option(@a) :: integral,numeric @a
 	var v
 	var cv : int32
 	
@@ -58,8 +58,8 @@
 		;;
 		cv = charval(c, base)
 		if cv >= 0
-			v *= (base : @a::(integral,numeric))
-			v += (cv : @a::(integral,numeric))
+			v *= (base : @a)
+			v += (cv : @a)
 		else
 			-> `None
 		;;
--- a/lib/std/ipparse.myr
+++ b/lib/std/ipparse.myr
@@ -116,7 +116,7 @@
 	;;
 }
 
-generic num = {ip, lo, hi, base, sep, ok -> (@a::(numeric,integral), byte[:], bool)
+generic num = {ip, lo, hi, base, sep, ok -> (@a, byte[:], bool) :: numeric,integral @a
 	var len
 
 	if !ok
@@ -133,7 +133,7 @@
 		if v < lo || v > hi
 			-> (0, "", false)
 		;;
-		-> ((v : @a::(numeric,integral)), ip[len:], true)
+		-> ((v : @a), ip[len:], true)
 	| `std.None:
 		-> (0, "", false)
 	;;
--- a/lib/std/putint.myr
+++ b/lib/std/putint.myr
@@ -3,21 +3,21 @@
 use "types"
 
 pkg std =
-	generic puthost64	: (buf : byte[:], v :  @a::(numeric,integral) -> size)
-	generic putle64		: (buf : byte[:], v :  @a::(numeric,integral) -> size)
-	generic putbe64		: (buf : byte[:], v :  @a::(numeric,integral) -> size)
-	generic puthost32	: (buf : byte[:], v :  @a::(numeric,integral) -> size)
-	generic putle32		: (buf : byte[:], v :  @a::(numeric,integral) -> size)
-	generic putbe32		: (buf : byte[:], v :  @a::(numeric,integral) -> size)
-	generic puthost16	: (buf : byte[:], v :  @a::(numeric,integral) -> size)
-	generic putle16		: (buf : byte[:], v :  @a::(numeric,integral) -> size)
-	generic putbe16		: (buf : byte[:], v :  @a::(numeric,integral) -> size)
-	generic puthost8	: (buf : byte[:], v :  @a::(numeric,integral) -> size)
-	generic putle8		: (buf : byte[:], v :  @a::(numeric,integral) -> size)
-	generic putbe8		: (buf : byte[:], v :  @a::(numeric,integral) -> size)
+	generic puthost64	: (buf : byte[:], v :  @a -> size) :: numeric,integral @a
+	generic putle64		: (buf : byte[:], v :  @a -> size) :: numeric,integral @a
+	generic putbe64		: (buf : byte[:], v :  @a -> size) :: numeric,integral @a
+	generic puthost32	: (buf : byte[:], v :  @a -> size) :: numeric,integral @a
+	generic putle32		: (buf : byte[:], v :  @a -> size) :: numeric,integral @a
+	generic putbe32		: (buf : byte[:], v :  @a -> size) :: numeric,integral @a
+	generic puthost16	: (buf : byte[:], v :  @a -> size) :: numeric,integral @a
+	generic putle16		: (buf : byte[:], v :  @a -> size) :: numeric,integral @a
+	generic putbe16		: (buf : byte[:], v :  @a -> size) :: numeric,integral @a
+	generic puthost8	: (buf : byte[:], v :  @a -> size) :: numeric,integral @a
+	generic putle8		: (buf : byte[:], v :  @a -> size) :: numeric,integral @a
+	generic putbe8		: (buf : byte[:], v :  @a -> size) :: numeric,integral @a
 ;;
 
-generic puthost = {buf, val : @a::(integral, numeric)
+generic puthost = {buf, val : @a :: integral, numeric @a
 	iassert(buf.len >= sizeof(@a), "buffer too small")
 	memblit((buf : byte#), (&val : byte#), sizeof(@a))
 	-> sizeof(@a)
--- a/lib/std/rand.myr
+++ b/lib/std/rand.myr
@@ -13,12 +13,12 @@
 	const mksrng	: (seed : uint32 -> rng#)
 	const freerng	: (rng : rng# -> void)
 
-	generic rand	: (lo : @a::(numeric,integral), hi : @a::(numeric,integral) -> @a::(numeric,integral))
-	generic randnum	: (-> @a::(numeric,integral))
+	generic rand	: (lo : @a, hi : @a -> @a) :: numeric,integral @a
+	generic randnum	: (-> @a) :: numeric,integral @a
 	const randbytes	: (buf : byte[:] -> void)
 
-	generic rngrand	: (rng : rng#, lo : @a::(numeric,integral), hi : @a::(numeric,integral) -> @a::(numeric,integral))
-	generic rngrandnum	: (rng : rng# -> @a::(numeric,integral))
+	generic rngrand	: (rng : rng#, lo : @a, hi : @a -> @a) ::numeric,integral @a
+	generic rngrandnum	: (rng : rng# -> @a) :: numeric,integral @a
 	const rngrandbytes	: (rng : rng#, buf : byte[:] -> void)
 ;;
 
@@ -90,7 +90,7 @@
 
 See http://xoroshiro.di.unimi.it/ for details.
 */
-generic rngrandnum = {rng -> @a::(numeric,integral)
+generic rngrandnum = {rng -> @a :: numeric,integral @a
 	var s0, s1, r
 
 	s0 = rng.s0
@@ -101,5 +101,5 @@
 
 	rng.s0 = (s0 << 55 | s0 >> 9) ^ s1 ^ (s1 << 14)
 	rng.s1 = (s1 << 36 | s1 >> 28)
-	-> (r : @a::(numeric,integral))
+	-> (r : @a)
 }
--- a/lib/std/search.myr
+++ b/lib/std/search.myr
@@ -3,8 +3,8 @@
 use "types"
 
 pkg std =
-	generic lsearch	: (sl : @t[:], key : @k, cmp : (v : @t, k : @k -> order) -> option(@idx::(integral,numeric)))
-	generic bsearch	: (sl : @t[:], key : @k, cmp : (v : @t, k : @k -> order) -> option(@idx::(integral,numeric)))
+	generic lsearch	: (sl : @t[:], key : @k, cmp : (v : @t, k : @k -> order) -> option(@idx)) :: integral,numeric @idx
+	generic bsearch	: (sl : @t[:], key : @k, cmp : (v : @t, k : @k -> order) -> option(@idx)) :: integral,numeric @idx
 ;;
 
 /* linear search over a list of values */
--- a/lib/std/syswrap+plan9.myr
+++ b/lib/std/syswrap+plan9.myr
@@ -261,7 +261,7 @@
 }
 */
 
-generic check = {e : @a::(integral, numeric) -> result(@b, errno)
+generic check = {e : @a -> result(@b, errno) :: integral, numeric @a
 	if e < 0
 		-> `Err lasterr()
 	else
@@ -270,25 +270,25 @@
 }
 
 /* duplicated code to break dependency cycle */
-generic _getle16 = {buf -> @a::(numeric,integral)
-	-> ((buf[0] : @a::(numeric,integral)) << 0) | \
-		((buf[1] : @a::(numeric,integral)) << 8)
+generic _getle16 = {buf -> @a :: numeric,integral @a
+	-> ((buf[0] : @a) << 0) | \
+		((buf[1] : @a) << 8)
 }
 
-generic _getle32 = {buf -> @a::(numeric,integral)
-	-> ((buf[0] : @a::(numeric,integral)) << 0) | \
-		((buf[1] : @a::(numeric,integral)) << 8) | \
-		((buf[2] : @a::(numeric,integral)) << 16) | \
-		((buf[3] : @a::(numeric,integral)) << 24)
+generic _getle32 = {buf -> @a :: numeric,integral @a
+	-> ((buf[0] : @a) << 0) | \
+		((buf[1] : @a) << 8) | \
+		((buf[2] : @a) << 16) | \
+		((buf[3] : @a) << 24)
 }
 
-generic _getle64 = {buf -> @a::(numeric,integral)
-	-> ((buf[0] : @a::(numeric,integral))  << 0) | \
-		((buf[1] : @a::(numeric,integral))  << 8) | \
-		((buf[2] : @a::(numeric,integral))  << 16) | \
-		((buf[3] : @a::(numeric,integral))  << 24) | \
-		((buf[4] : @a::(numeric,integral))  << 32) | \
-		((buf[5] : @a::(numeric,integral))  << 40) | \
-		((buf[6] : @a::(numeric,integral))  << 48) | \
-		((buf[7] : @a::(numeric,integral))  << 56)
+generic _getle64 = {buf -> @a :: numeric,integral @a
+	-> ((buf[0] : @a)  << 0) | \
+		((buf[1] : @a)  << 8) | \
+		((buf[2] : @a)  << 16) | \
+		((buf[3] : @a)  << 24) | \
+		((buf[4] : @a)  << 32) | \
+		((buf[5] : @a)  << 40) | \
+		((buf[6] : @a)  << 48) | \
+		((buf[7] : @a)  << 56)
 }
--- a/lib/std/syswrap+posixy.myr
+++ b/lib/std/syswrap+posixy.myr
@@ -179,7 +179,7 @@
 	;;
 }
 
-generic check = {e : @a::(integral, numeric) -> result(@b, errno)
+generic check = {e : @a -> result(@b, errno) :: integral,numeric @a
 	if e < 0
 		-> `Err (e : errno)
 	else
--- a/lib/std/units.myr
+++ b/lib/std/units.myr
@@ -2,14 +2,14 @@
 
 pkg std =
 	/* JEDEC 100B.1 memory sizes */
-	generic KiB	: @a::(integral,numeric)	= 1024 
-	generic MiB	: @a::(integral,numeric)	= KiB*1024
-	generic GiB	: @a::(integral,numeric)	= MiB*1024
-	generic TiB	: @a::(integral,numeric)	= GiB*1024
-	generic PiB	: @a::(integral,numeric)	= TiB*1024
-	generic EiB	: @a::(integral,numeric)	= PiB*1024
-	generic ZiB	: @a::(integral,numeric)	= EiB*1024
-	generic YiB	: @a::(integral,numeric)	= ZiB*1024
+	generic KiB	: @a	= 1024 		:: integral,numeric @a
+	generic MiB	: @a	= KiB*1024      :: integral,numeric @a
+	generic GiB	: @a	= MiB*1024      :: integral,numeric @a
+	generic TiB	: @a	= GiB*1024      :: integral,numeric @a
+	generic PiB	: @a	= TiB*1024      :: integral,numeric @a
+	generic EiB	: @a	= PiB*1024      :: integral,numeric @a
+	generic ZiB	: @a	= EiB*1024      :: integral,numeric @a
+	generic YiB	: @a	= ZiB*1024      :: integral,numeric @a
 
 	generic Sec	: time	= 1_000_000
 	generic Msec	: time	= 1_000
--- a/lib/thread/atomic.myr
+++ b/lib/thread/atomic.myr
@@ -1,7 +1,7 @@
 use std
 
 pkg thread =
-	trait atomic @a::(integral,numeric) =
+	trait atomic @a :: integral,numeric @a =
 		xget	: (p : @a# -> @a)
 		xset	: (p : @a#, v : @a -> void)
 		xadd	: (p : @a#, v : @a -> @a)
--- a/mbld/deps.myr
+++ b/mbld/deps.myr
@@ -19,8 +19,6 @@
 	`Ldep	byte[:]
 ;;
 
-const Abiversion = 13
-
 var usepat	: regex.regex#
 var cflagpat	: regex.regex#
 var clibpat	: regex.regex#
--- a/mbld/libs.myr
+++ b/mbld/libs.myr
@@ -22,7 +22,7 @@
 		incs : byte[:][:] -> void)
 ;;
 
-const Abiversion = 14
+const Abiversion = 15
 
 const builtlib = {b, mt, dep, dyndep
 	var ldep, l, u
--- a/parse/export.c
+++ b/parse/export.c
@@ -71,11 +71,15 @@
 {
 	size_t i;
 
-	if (t->vis != Visintern)
+	if (!t || t->vis != Visintern)
 		return;
 	t->vis = Vishidden;
 	for (i = 0; i < t->nsub; i++)
 		tagtype(st, t->sub[i], ingeneric, hidelocal);
+	for (i = 0; i < t->nspec; i++) {
+		tagtype(st, t->spec[i]->param, ingeneric, hidelocal);
+		tagtype(st, t->spec[i]->aux, ingeneric, hidelocal);
+	}
 	switch (t->type) {
 	case Tystruct:
 		for (i = 0; i < t->nmemb; i++)
@@ -259,7 +263,6 @@
 	free(k);
 
 	/* tag the traits */
-	tr = NULL;
 	for (i = 0; i < ntraittab; i++) {
 		tr = traittab[i];
 		if (tr->vis != Visexport)
@@ -266,9 +269,12 @@
 			continue;
 		if (hidelocal && tr->ishidden)
 			tr->vis = Vishidden;
+		tagtype(st, tr->param, 0, hidelocal);
 		tr->param->vis = tr->vis;
-		for (j = 0; j < tr->naux; j++)
+		for (j = 0; j < tr->naux; j++) {
+			tagtype(st, tr->aux[j], 0, hidelocal);
 			tr->aux[j]->vis = tr->vis;
+		}
 		for (j = 0; j < tr->nproto; j++) {
 			tr->proto[j]->decl.vis = tr->vis;
 			tagnode(st, tr->proto[j], 0, hidelocal);
--- a/parse/gram.y
+++ b/parse/gram.y
@@ -18,10 +18,11 @@
 #include "parse.h"
 
 
-Stab *curscope;
 #define LBLSTKSZ 64
 static Node **lbls[LBLSTKSZ];
 static size_t nlbls[LBLSTKSZ];
+Stab *curscope;
+
 /* the first time we see a label, we increment to 0 */
 static int lbldepth = -1;
 
@@ -32,6 +33,7 @@
 static Node *mkpseudodecl(Srcloc l, Type *t);
 static void installucons(Stab *st, Type *t);
 static void setattrs(Node *dcl, char **attrs, size_t nattrs);
+static void setwith(Type *ty, Traitspec **spec, size_t nspec);
 static void setupinit(Node *n);
 
 %}
@@ -132,7 +134,9 @@
 %type <ty> type structdef uniondef tupledef compoundtype functype funcsig
 %type <ty> generictype
 %type <tylist> typelist typarams optauxtypes
-%type <nodelist> typaramlist
+%type <traitspecs> traitspec traits
+%type <traitspec> traitvar
+%type <nodelist> traitlist
 
 %type <tok> asnop cmpop addop mulop shiftop optident obrace
 
@@ -207,6 +211,11 @@
 		Type **params;
 		size_t nparams;
 	} tydef;
+	struct {
+		Traitspec **spec;
+		size_t nspec;
+	} traitspecs;
+	Traitspec *traitspec;
 	Trait *trait;
 	Node *node;
 	Tok  *tok;
@@ -254,7 +263,7 @@
 	| /* empty */
 	;
 
-decl	: attrs Tvar decllist {
+decl	: attrs Tvar decllist traitspec {
 		size_t i;
 
 		for (i = 0; i < $3.nn; i++)
@@ -261,7 +270,7 @@
 			setattrs($3.nl[i], $1.str, $1.nstr);
 		$$ = $3;
 	}
-	| attrs Tconst decllist {
+	| attrs Tconst decllist traitspec {
 		size_t i;
 		for (i = 0; i < $3.nn; i++) {
 			setattrs($3.nl[i], $1.str, $1.nstr);
@@ -269,11 +278,12 @@
 		}
 		$$ = $3;
 	}
-	| attrs Tgeneric decllist {
+	| attrs Tgeneric decllist traitspec {
 		size_t i;
 
 		for (i = 0; i < $3.nn; i++) {
 			setattrs($3.nl[i], $1.str, $1.nstr);
+			setwith($3.nl[i]->decl.type, $4.spec, $4.nspec);
 			$3.nl[i]->decl.isconst = 1;
 			$3.nl[i]->decl.isgeneric = 1;
 		}
@@ -288,6 +298,50 @@
 	}
 	;
 
+traitspec
+	: Twith traits	{$$ = $2;}
+	| /* nothing */ {$$.nspec = 0;}
+	;
+
+traits	: traitvar {
+       		$$.spec = NULL;
+       		$$.nspec = 0;
+		lappend(&$$.spec, &$$.nspec, $1);
+	}
+	| traits listsep traitvar {
+		$$ = $1;
+		lappend(&$$.spec, &$$.nspec, $3);
+	}
+	;
+
+traitvar
+	: traitlist generictype {
+		$$ = calloc(sizeof(Traitspec), 1);
+		$$->trait = $1.nl;
+		$$->ntrait = $1.nn;
+		$$->param = $2;
+		$$->aux = NULL;
+	}
+	| traitlist generictype Tret type {
+		$$ = calloc(sizeof(Traitspec), 1);
+		$$->trait = $1.nl;
+		$$->ntrait = $1.nn;
+		$$->param = $2;
+		$$->aux = $4;
+	}
+	;
+
+traitlist
+	: name {
+		$$.nl = 0;
+		$$.nn = 0;
+		lappend(&$$.nl, &$$.nn, $1);
+	}
+	| traitlist listsep name {
+		lappend(&$$.nl, &$$.nn, $3);
+	}
+	;
+
 decllist: declbody {
 		$$.loc = $1->loc; $$.nl = NULL; $$.nn = 0;
 		lappend(&$$.nl, &$$.nn, $1);
@@ -388,12 +442,22 @@
 	;
 
 implstmt
-	: Timpl name type optauxtypes {
+	: Timpl name type optauxtypes traitspec {
+		size_t i;
+
 		$$ = mkimplstmt($1->loc, $2, $3, $4.types, $4.ntypes, NULL, 0);
 		$$->impl.isproto = 1;
+		setwith($3, $5.spec, $5.nspec);
+		for (i = 0; i < $4.ntypes; i++)
+			setwith($4.types[i], $5.spec, $5.nspec);
 	}
-	| Timpl name type optauxtypes Tasn Tendln implbody Tendblk {
-		$$ = mkimplstmt($1->loc, $2, $3, $4.types, $4.ntypes, $7.nl, $7.nn);
+	| Timpl name type optauxtypes traitspec Tasn Tendln implbody Tendblk {
+		size_t i;
+
+		$$ = mkimplstmt($1->loc, $2, $3, $4.types, $4.ntypes, $8.nl, $8.nn);
+		setwith($3, $5.spec, $5.nspec);
+		for (i = 0; i < $4.ntypes; i++)
+			setwith($4.types[i], $5.spec, $5.nspec);
 	}
 	;
 
@@ -411,25 +475,32 @@
 	;
 
 traitdef
-	: Ttrait Tident generictype optauxtypes { /* trait prototype */
+	: Ttrait Tident generictype optauxtypes traitspec { /* trait prototype */
+		size_t i;
 		$$ = mktrait($1->loc,
 			mkname($2->loc, $2->id), $3,
 			$4.types, $4.ntypes,
 			NULL, 0,
 			1);
+		setwith($3, $5.spec, $5.nspec);
+		for (i = 0; i < $4.ntypes; i++)
+			setwith($4.types[i], $5.spec, $5.nspec);
 	}
-	| Ttrait Tident generictype optauxtypes Tasn traitbody Tendblk /* trait definition */ {
+	| Ttrait Tident generictype optauxtypes traitspec Tasn traitbody Tendblk /* trait definition */ {
 		size_t i;
 		$$ = mktrait($1->loc,
 			mkname($2->loc, $2->id), $3,
 			$4.types, $4.ntypes,
-			$6.nl, $6.nn,
+			$7.nl, $7.nn,
 			0);
-		for (i = 0; i < $6.nn; i++) {
-			$6.nl[i]->decl.trait = $$;
-			$6.nl[i]->decl.impls = mkht(tyhash, tyeq);
-			$6.nl[i]->decl.isgeneric = 1;
+		for (i = 0; i < $7.nn; i++) {
+			$7.nl[i]->decl.trait = $$;
+			$7.nl[i]->decl.impls = mkht(tyhash, tyeq);
+			$7.nl[i]->decl.isgeneric = 1;
 		}
+		setwith($3, $5.spec, $5.nspec);
+		for (i = 0; i < $4.ntypes; i++)
+			setwith($4.types[i], $5.spec, $5.nspec);
 	}
 	;
 
@@ -452,14 +523,14 @@
 	;
 
 
-tydef	: Ttype typeid {$$ = $2;}
-	| Ttype typeid Tasn type {
+tydef	: Ttype typeid traitspec {$$ = $2;}
+	| Ttype typeid traitspec Tasn type {
 		$$ = $2;
-		if ($$.nparams == 0) {
-			$$.type = mktyname($2.loc, mkname($2.loc, $2.name), $4);
-		} else {
-			$$.type = mktygeneric($2.loc, mkname($2.loc, $2.name), $2.params, $2.nparams, $4);
-		}
+		if ($$.nparams == 0)
+			$$.type = mktyname($2.loc, mkname($2.loc, $2.name), $5);
+		else
+			$$.type = mktygeneric($2.loc, mkname($2.loc, $2.name), $2.params, $2.nparams, $5);
+		setwith($$.type, $3.spec, $3.nspec);
 	}
 	;
 
@@ -498,35 +569,17 @@
 
 generictype
 	: Ttyparam {$$ = mktyparam($1->loc, $1->id);}
-	| Ttyparam Twith name {
-		$$ = mktyparam($1->loc, $1->id);
-		lappend(&$$->traits, &$$->ntraits, $3);
-	}
-	| Ttyparam Twith Toparen typaramlist Tcparen {
-		size_t i;
-		$$ = mktyparam($1->loc, $1->id);
-		for (i = 0; i < $4.nn; i++)
-			lappend(&$$->traits, &$$->ntraits, $4.nl[i]);
-	}
 	;
 
-typaramlist
-	: name {
-		$$.nl = NULL; $$.nn = 0;
-		lappend(&$$.nl, &$$.nn, $1);
-	}
-	| typaramlist listsep name {lappend(&$$.nl, &$$.nn, $3);}
-	;
-
 compoundtype
-	: functype   {$$ = $1;}
-	| type Tosqbrac Tcolon Tcsqbrac {$$ = mktyslice($2->loc, $1);}
-	| type Tosqbrac expr Tcsqbrac {$$ = mktyarray($2->loc, $1, $3);}
-	| type Tosqbrac Tellipsis Tcsqbrac {$$ = mktyarray($2->loc, $1, NULL);}
-	| name Toparen typelist Tcparen {$$ = mktyunres($1->loc, $1, $3.types, $3.ntypes);}
-	| type Tderef	{$$ = mktyptr($2->loc, $1);}
-	| Tvoidlit	{$$ = mktyunres($1->loc, mkname($1->loc, $1->id), NULL, 0);}
-	| name	{$$ = mktyunres($1->loc, $1, NULL, 0);}
+	: functype   				{$$ = $1;}
+	| type Tosqbrac Tcolon Tcsqbrac		{$$ = mktyslice($2->loc, $1);}
+	| type Tosqbrac expr Tcsqbrac		{$$ = mktyarray($2->loc, $1, $3);}
+	| type Tosqbrac Tellipsis Tcsqbrac 	{$$ = mktyarray($2->loc, $1, NULL);}
+	| name Toparen typelist Tcparen 	{$$ = mktyunres($1->loc, $1, $3.types, $3.ntypes);}
+	| type Tderef				{$$ = mktyptr($2->loc, $1);}
+	| Tvoidlit				{$$ = mktyunres($1->loc, mkname($1->loc, $1->id), NULL, 0);}
+	| name					{$$ = mktyunres($1->loc, $1, NULL, 0);}
 	;
 
 functype: Toparen funcsig Tcparen {$$ = $2;}
@@ -826,11 +879,13 @@
 	}
 	;
 
-funclit : obrace params Tendln blkbody Tcbrace {
+funclit : obrace params traitspec Tendln blkbody Tcbrace {
 		size_t i;
 		Node *fn, *lit;
 
-		$$ = mkfunc($1->loc, $2.nl, $2.nn, mktyvar($3->loc), $4);
+		for (i = 0; i < $2.nn; i++)
+			setwith($2.nl[i]->decl.type, $3.spec, $3.nspec);
+		$$ = mkfunc($1->loc, $2.nl, $2.nn, mktyvar($4->loc), $5);
 		fn = $$->lit.fnval;
 		for (i = 0; i < nlbls[lbldepth]; i++) {
 			lit = lbls[lbldepth][i]->expr.args[0];
@@ -840,11 +895,14 @@
 		assert(lbldepth >= 0);
 		lbldepth--;
 	}
-	| obrace params Tret type Tendln blkbody Tcbrace {
+	| obrace params Tret type traitspec Tendln blkbody Tcbrace {
 		size_t i;
 		Node *fn, *lit;
 
-		$$ = mkfunc($1->loc, $2.nl, $2.nn, $4, $6);
+		setwith($4, $5.spec, $5.nspec);
+		for (i = 0; i < $2.nn; i++)
+			setwith($2.nl[i]->decl.type, $5.spec, $5.nspec);
+		$$ = mkfunc($1->loc, $2.nl, $2.nn, $4, $7);
 		fn = $$->lit.fnval;
 		for (i = 0; i < nlbls[lbldepth]; i++) {
 			lit = lbls[lbldepth][i]->expr.args[0];
@@ -1056,7 +1114,8 @@
 
 %%
 
-static void setupinit(Node *n)
+static void
+setupinit(Node *n)
 {
 	char name[1024];
 	char *p, *s;
@@ -1078,7 +1137,8 @@
 	n->decl.name->name.name = strdup(s);
 }
 
-static Node *mkpseudodecl(Srcloc l, Type *t)
+static Node *
+mkpseudodecl(Srcloc l, Type *t)
 {
 	static int nextpseudoid;
 	char buf[128];
@@ -1087,7 +1147,8 @@
 	return mkdecl(l, mkname(l, buf), t);
 }
 
-static void setattrs(Node *dcl, char **attrs, size_t nattrs)
+static void
+setattrs(Node *dcl, char **attrs, size_t nattrs)
 {
 	size_t i;
 
@@ -1101,8 +1162,52 @@
 	}
 }
 
-static void installucons(Stab *st, Type *t)
+static void
+setwith(Type *ty, Traitspec **ts, size_t nts)
 {
+	size_t i, j;
+
+	if (!ty)
+		return;
+	for (i = 0; i < nts; i++) {
+		switch (ty->type) {
+		case Typaram:
+			if (tyeq(ty, ts[i]->param))
+				lappend(&ty->spec, &ty->nspec, ts[i]);
+
+			break;
+		case Tyname:
+		case Tyunres:
+			for (j = 0; j < ty->ngparam; j++)
+				setwith(ty->gparam[j], ts, nts);
+			for (j = 0; j < ty->narg; j++)
+				setwith(ty->arg[j], ts, nts);
+			break;
+		case Tystruct:
+			for (j = 0; j < ty->nmemb; j++)
+				setwith(ty->sdecls[j]->decl.type, ts, nts);
+			break;
+		case Tyunion:
+			for (j = 0; j < ty->nmemb; j++)
+				setwith(ty->udecls[j]->etype, ts, nts);
+			break;
+		case Typtr:
+		case Tyarray:
+		case Tyslice:
+		case Tyfunc:
+		case Tytuple:
+			for (j = 0; j < ty->nsub; j++)
+				setwith(ty->sub[j], ts, nts);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+static void
+installucons(Stab *st, Type *t)
+{
 	Type *b;
 	size_t i;
 
@@ -1127,7 +1232,8 @@
 }
 
 
-static Op binop(int tt)
+static Op
+binop(int tt)
 {
 	Op o;
 
--- a/parse/infer.c
+++ b/parse/infer.c
@@ -35,7 +35,9 @@
 static void inferdecl(Node *n);
 static int tryconstrain(Type *ty, Trait *tr, int update);
 
+static Type *tyfreshen(Tysubst *subst, Type *orig);
 static Type *tf(Type *t);
+static Type *basetype(Type *a);
 
 static Type *unify(Node *ctx, Type *a, Type *b);
 static Type *tyfix(Node *ctx, Type *orig, int noerr);
@@ -66,7 +68,6 @@
 static size_t nspecializations;
 static Stab **specializationscope;
 static size_t nspecializationscope;
-static Htab *seqbase;
 static Traitmap *traitmap;
 
 static void
@@ -279,6 +280,7 @@
 	ty = exprtype(n->iterstmt.seq);
 	if (ty->type == Tyslice || ty->type == Tyarray || ty->type == Typtr)
 		return;
+	ty = tyfreshen(NULL, ty);
 	for (i = 0; i < tr->nproto; i++) {
 		ty = exprtype(n->iterstmt.seq);
 		if (hthas(tr->proto[i]->decl.impls, ty))
@@ -462,7 +464,7 @@
 static Type *
 tyfreshen(Tysubst *subst, Type *orig)
 {
-	Type *t;
+	Type *ty, *base;
 
 	if (!needfreshen(orig))
 		return orig;
@@ -469,13 +471,18 @@
 	pushenv(orig->env);
 	if (!subst) {
 		subst = mksubst();
-		t = tyspecialize(orig, subst, delayed, seqbase);
+		ty = tyspecialize(orig, subst, delayed, seqbase);
 		substfree(subst);
 	} else {
-		t = tyspecialize(orig, subst, delayed, seqbase);
+		ty = tyspecialize(orig, subst, delayed, seqbase);
 	}
+	ty->spec = orig->spec;
+	ty->nspec = orig->nspec;
+	base = basetype(ty);
+	if (base)
+		htput(seqbase, ty, base);
 	popenv(orig->env);
-	return t;
+	return ty;
 }
 
 /* Resolves a type and all its subtypes recursively. */
@@ -482,7 +489,7 @@
 static void
 tyresolve(Type *t)
 {
-	size_t i;
+	size_t i, j;
 	Trait *tr;
 
 	if (t->resolved)
@@ -526,13 +533,16 @@
 		break;
 	}
 
-	for (i = 0; i < t->ntraits; i++) {
-		tr = gettrait(curstab(), t->traits[i]);
-		if (!tr)
-			lfatal(t->loc, "trait %s does not exist", ctxstr(t->traits[i]));
-		if (!t->trneed)
-			t->trneed = mkbs();
-		bsput(t->trneed, tr->uid);
+	for (i = 0; i < t->nspec; i++) {
+		for (j = 0; j < t->spec[i]->ntrait; j++) {
+			tr = gettrait(curstab(), t->spec[i]->trait[j]);
+			if (!tr)
+				lfatal(t->loc, "trait %s does not exist", ctxstr(t->spec[i]->trait[j]));
+			if (!t->trneed)
+				t->trneed = mkbs();
+			bsput(t->trneed, tr->uid);
+			htput(seqbase, t, t->spec[i]->aux);
+		}
 	}
 
 	for (i = 0; i < t->nsub; i++) {
@@ -597,9 +607,8 @@
 {
 	size_t i;
 
-	for (i = 0; i < t->ngparam; i++) {
+	for (i = 0; i < t->ngparam; i++)
 		substput(subst, t->gparam[i], tf(orig->arg[i]));
-	}
 	t = tyfreshen(subst, t);
 	return t;
 }
@@ -628,7 +637,9 @@
 	t = tylookup(orig);
 	isgeneric = t->type == Tygeneric;
 	ingeneric += isgeneric;
+	pushenv(orig->env);
 	tyresolve(t);
+	popenv(orig->env);
 	/* If this is an instantiation of a generic type, we want the params to
 	 * match the instantiation */
 	if (orig->type == Tyunres && t->type == Tygeneric) {
@@ -1919,7 +1930,7 @@
 		fatal(n, "%s incompatibly specialized with %zd types instead of %zd types",
 			namestr(n->impl.traitname), n->impl.naux, tr->naux);
 	n->impl.type = tf(n->impl.type);
-	pushenv(n->impl.type->env);
+	pushenv(n->impl.env);
 	for (i = 0; i < n->impl.naux; i++)
 		n->impl.aux[i] = tf(n->impl.aux[i]);
 	for (i = 0; i < n->impl.ndecls; i++) {
@@ -1986,7 +1997,7 @@
 		if (generic)
 			ingeneric--;
 	}
-	popenv(n->impl.type->env);
+	popenv(n->impl.env);
 }
 
 static void
@@ -2049,7 +2060,7 @@
 {
 	size_t i, nbound;
 	Node **bound, *n, *pat;
-	Type *t, *b;
+	Type *t, *b, *e;
 
 	n = *np;
 	if (!n)
@@ -2115,12 +2126,15 @@
 		infernode(&n->iterstmt.seq, NULL, sawret);
 		infernode(&n->iterstmt.body, ret, sawret);
 
-		b = mktyvar(n->loc);
-		t = mktyvar(n->loc);
-		htput(seqbase, t, b);
-		constrain(n, type(n->iterstmt.seq), traittab[Tciter]);
-		unify(n, type(n->iterstmt.seq), t);
-		unify(n, type(n->iterstmt.elt), b);
+		e = type(n->iterstmt.elt);
+		t = type(n->iterstmt.seq);
+		constrain(n, t, traittab[Tciter]);
+		b = basetype(t);
+		if (b)
+			unify(n, e, b);
+		else
+			htput(seqbase, t, e);
+		delayedcheck(n, curstab());
 		break;
 	case Nmatchstmt:
 		infernode(&n->matchstmt.val, NULL, sawret);
@@ -2365,6 +2379,43 @@
 }
 
 static void
+fixiter(Node *n, Type *ty, Type *base)
+{
+	size_t i, bestidx;
+	int r, bestrank;
+	Type *b, *t, *orig;
+	Tysubst *ts;
+	Node *impl;
+
+	ty = tysearch(ty);
+	b = htget(seqbase, ty);
+	if (!b)
+		return;
+	bestrank = -1;
+	bestidx = 0;
+	for (i = 0; i < nimpltab; i++) {
+		if (impltab[i]->impl.trait != traittab[Tciter])
+			continue;
+		r = tymatchrank(impltab[i]->impl.type, ty);
+		if (r > bestrank) {
+			bestrank = r;
+			bestidx = i;
+		}
+	}
+	if (bestrank >= 0) {
+		impl = impltab[bestidx];
+		orig = impl->impl.type;
+		t = tf(impl->impl.aux[0]);
+		ts = mksubst();
+		for (i = 0; i < ty->narg; i++)
+			substput(ts, tf(orig->arg[i]), ty->arg[i]);
+		t = tyfreshen(ts, t);
+		substfree(ts);
+		unify(n, t, base);
+	}
+}
+
+static void
 postcheckpass(Node ***rem, size_t *nrem, Stab ***remscope, size_t *nremscope)
 {
 	size_t i;
@@ -2373,12 +2424,16 @@
 	for (i = 0; i < npostcheck; i++) {
 		n = postcheck[i];
 		pushstab(postcheckscope[i]);
-		switch (exprop(n)) {
-		case Omemb:	infercompn(n, rem, nrem, remscope, nremscope);	break;
-		case Ocast:	checkcast(n, rem, nrem, remscope, nremscope);	break;
-		case Ostruct:	checkstruct(n, rem, nrem, remscope, nremscope);	break;
-		case Ovar:	checkvar(n, rem, nrem, remscope, nremscope);	break;
-		default:	die("should not see %s in postcheck\n", opstr[exprop(n)]);
+		if (n->type == Nexpr) {
+			switch (exprop(n)) {
+			case Omemb:	infercompn(n, rem, nrem, remscope, nremscope);	break;
+			case Ocast:	checkcast(n, rem, nrem, remscope, nremscope);	break;
+			case Ostruct:	checkstruct(n, rem, nrem, remscope, nremscope);	break;
+			case Ovar:	checkvar(n, rem, nrem, remscope, nremscope);	break;
+			default:	die("should not see %s in postcheck\n", opstr[exprop(n)]);
+			}
+		} else if (n->type == Niterstmt) {
+			fixiter(n, type(n->iterstmt.seq), type(n->iterstmt.elt));
 		}
 		popstab();
 	}
@@ -2405,7 +2460,6 @@
 		postcheckscope = remscope;
 		npostcheckscope = nremscope;
 	}
-	postcheckpass(NULL, NULL, NULL, NULL);
 }
 
 /* After inference, replace all
@@ -2588,7 +2642,8 @@
 		typesub(n->iterstmt.elt, noerr);
 		typesub(n->iterstmt.seq, noerr);
 		typesub(n->iterstmt.body, noerr);
-		additerspecialization(n, curstab());
+		if (!ingeneric)
+			additerspecialization(n, curstab());
 		break;
 	case Nmatchstmt:
 		typesub(n->matchstmt.val, noerr);
@@ -2687,6 +2742,8 @@
 			tr = traittab[Tciter];
 			assert(tr->nproto == 2);
 			ty = exprtype(n->iterstmt.seq);
+			if (ty->type == Typaram)
+				continue;
 
 			it = itertype(n->iterstmt.seq, mktype(n->loc, Tybool));
 			d = specializedcl(tr->proto[0], ty, it, &name);
@@ -2782,10 +2839,10 @@
 			tr = gettrait(ns, n);
 		if (!tr)
 			fatal(impl, "trait %s does not exist near %s",
-					namestr(impl->impl.traitname), ctxstr(impl));
+			    namestr(impl->impl.traitname), ctxstr(impl));
 		if (tr->naux != impl->impl.naux)
 			fatal(impl, "incompatible implementation of %s: mismatched aux types",
-					namestr(impl->impl.traitname), ctxstr(impl));
+			    namestr(impl->impl.traitname), ctxstr(impl));
 	}
 	return tr;
 }
@@ -2849,6 +2906,7 @@
 	Type *ty;
 
 	pushstab(file->file.globls);
+	seqbase = mkht(tyhash, tyeq);
 	traitmap = zalloc(sizeof(Traitmap));
 	builtintraits();
 	for (i = 0; i < nimpltab; i++) {
@@ -2898,7 +2956,6 @@
 infer(void)
 {
 	delayed = mkht(tyhash, tyeq);
-	seqbase = mkht(tyhash, tyeq);
 	loaduses();
 	initimpl();
 
--- a/parse/node.c
+++ b/parse/node.c
@@ -264,6 +264,9 @@
 		n->impl.env = mkenv();
 		bindtype(n->impl.env, t);
 	}
+	for (i = 0; i < naux; i++)
+		if (hasparams(aux[i]))
+			bindtype(n->impl.env, aux[i]);
 	return n;
 }
 
--- a/parse/parse.h
+++ b/parse/parse.h
@@ -1,7 +1,8 @@
-#define Abiversion 14
+#define Abiversion 15
 
 typedef struct Srcloc Srcloc;
 typedef struct Tysubst Tysubst;
+typedef struct Traitspec Traitspec;
 
 typedef struct Tok Tok;
 typedef struct Node Node;
@@ -118,6 +119,13 @@
 	Htab *tab;
 };
 
+struct Traitspec {
+	Node **trait;
+	size_t ntrait;
+	Type *param;
+	Type *aux;
+};
+
 struct Type {
 	Ty type;
 	uint32_t tid;
@@ -125,8 +133,10 @@
 	Vis vis;
 
 
-	Node **traits;		/* trait list */
-	size_t ntraits;		/* trait list size */
+	Traitspec **spec;
+	size_t nspec;
+	//Node **traits;		/* trait list */
+	//size_t ntraits;		/* trait list size */
 
 	Type **gparam;		/* Tygeneric: type parameters that match the type args */
 	size_t ngparam;		/* Tygeneric: count of type parameters */
@@ -367,6 +377,7 @@
 extern size_t ndecls;
 extern Node **exportimpls;
 extern size_t nexportimpls;
+extern Htab *seqbase;
 
 /* property tables */
 extern int opispure[];
--- a/parse/specialize.c
+++ b/parse/specialize.c
@@ -14,6 +14,7 @@
 #include "parse.h"
 
 static Node *specializenode(Node *g, Tysubst *tsmap);
+static void fillsubst(Tysubst *tsmap, Type *to, Type *from);
 
 static void
 substpush(Tysubst *subst)
@@ -84,8 +85,9 @@
 tyspecialize(Type *orig, Tysubst *tsmap, Htab *delayed, Htab *trbase)
 {
 	Type *t, *ret, *tmp, *var, *base;
-	Type **arg;
+	Traitspec *ts;
 	size_t i, narg;
+	Type **arg;
 
 	t = tysearch(orig);
 	tmp = substget(tsmap, t);
@@ -98,6 +100,20 @@
 		ret = mktyvar(t->loc);
 		ret->trneed = bsdup(t->trneed);
 		substput(tsmap, t, ret);
+		for (i = 0; i < t->nspec; i++) {
+			ts = zalloc(sizeof(Traitspec));
+			ts->trait = t->spec[i]->trait;
+			ts->ntrait = t->spec[i]->ntrait;
+			ts->param = tyspecialize(t->spec[i]->param, tsmap, delayed, trbase);
+			if (t->spec[i]->aux)
+				ts->aux = tyspecialize(t->spec[i]->aux, tsmap, delayed, trbase);
+			lappend(&ret->spec, &ret->nspec, ts);
+		}
+		tmp = htget(seqbase, t);
+		if (tmp) {
+			tmp = tyspecialize(tmp, tsmap, delayed, trbase);
+			htput(trbase, ret, tmp);
+		}
 		break;
 	case Tygeneric:
 		var = mktyvar(t->loc);
@@ -186,6 +202,26 @@
 		return t;
 }
 
+static void
+substputspec(Tysubst *tsmap, Type *from, Type *to)
+{
+	size_t ai, aj, bi, bj;
+
+	for (ai = 0; ai < from->nspec; ai++) {
+		for (aj = 0; aj < from->spec[ai]->ntrait; aj++) {
+			for (bi = 0; bi < to->nspec; bi++) {
+				for (bj = 0; bj < to->spec[bi]->ntrait; bj++) {
+					if (nameeq(from->spec[ai]->trait[aj], to->spec[bi]->trait[bj]))
+						fillsubst(tsmap, to->spec[ai]->aux, from->spec[bi]->aux);
+				}
+			}
+			if (nameeq(from->spec[ai]->trait[aj], traittab[Tciter]->name))
+				if (to->type == Tyslice || to->type == Tyarray)
+					fillsubst(tsmap, to->sub[0], from->spec[ai]->aux);
+		}
+	}
+}
+
 /*
  * Fills the substitution map with a mapping from
  * the type parameter 'from' to it's substititon 'to'
@@ -199,6 +235,7 @@
 		if (debugopt['S'])
 			printf("mapping %s => %s\n", tystr(from), tystr(to));
 		substput(tsmap, from, to);
+		substputspec(tsmap, from, to);
 		return;
 	}
 	assert(to->nsub == from->nsub);
--- a/parse/stab.c
+++ b/parse/stab.c
@@ -671,6 +671,9 @@
 			tytab[t->tid] = tt;
 		else if (!boundtype(t))
 			htput(e->tab, t, t);
+		for (i = 0; i < t->nspec; i++)
+			if (t->spec[i]->aux)
+				bindtype_rec(e, t->spec[i]->aux, visited);
 		break;
 	case Tygeneric:
 		for (i = 0; i < t->ngparam; i++)
--- a/parse/type.c
+++ b/parse/type.c
@@ -24,6 +24,7 @@
 Node **impltab;
 size_t nimpltab;
 Htab *eqcache;
+Htab *seqbase;
 
 struct Typair {
 	uint32_t atid;
--- a/parse/use.c
+++ b/parse/use.c
@@ -218,7 +218,7 @@
 static void
 typickle(FILE *fd, Type *ty)
 {
-	size_t i;
+	size_t i, j;
 
 	if (!ty) {
 		die("trying to pickle null type\n");
@@ -226,6 +226,16 @@
 	}
 	wrbyte(fd, ty->type);
 	wrbyte(fd, ty->vis);
+	wrint(fd, ty->nspec);
+	for (i = 0; i < ty->nspec; i++) {
+		wrint(fd, ty->spec[i]->ntrait);
+		for (j = 0; j < ty->spec[i]->ntrait; j++)
+			pickle(fd, ty->spec[i]->trait[j]);
+		wrtype(fd, ty->spec[i]->param);
+		wrbool(fd, ty->spec[i]->aux != NULL);
+		if (ty->spec[i]->aux)
+			wrtype(fd, ty->spec[i]->aux);
+	}
 	wrint(fd, ty->nsub);
 	switch (ty->type) {
 	case Tyunres:
@@ -350,7 +360,7 @@
 static Type *
 tyunpickle(FILE *fd)
 {
-	size_t i, n;
+	size_t i, j, n;
 	Type *ty;
 	Ty t;
 
@@ -359,6 +369,19 @@
 	ty->isimport = 1;
 	if (rdbyte(fd) == Vishidden)
 		ty->ishidden = 1;
+	ty->nspec = rdint(fd);
+	ty->spec = malloc(ty->nspec * sizeof(Traitspec*));
+	for (i = 0; i < ty->nspec; i++) {
+		ty->spec[i] = zalloc(sizeof(Traitspec));
+		n = rdint(fd);
+		ty->spec[i]->ntrait = n;
+		ty->spec[i]->trait = malloc(n * sizeof(Node*));
+		for (j = 0; j < ty->spec[i]->ntrait; j++)
+			ty->spec[i]->trait[j] = unpickle(fd);
+		rdtype(fd, &ty->spec[i]->param);
+		if (rdbool(fd))
+			rdtype(fd, &ty->spec[i]->aux);
+	}
 	ty->nsub = rdint(fd);
 	if (ty->nsub > 0)
 		ty->sub = zalloc(ty->nsub * sizeof(Type *));
--- a/test/emptytrait.myr
+++ b/test/emptytrait.myr
@@ -6,7 +6,7 @@
 impl fooable int =
 ;;
 
-generic foo = {x : @a::fooable
+generic foo = {x : @a::fooable @a
 	-> x
 }
 
--- a/test/generictype.myr
+++ b/test/generictype.myr
@@ -1,8 +1,8 @@
 use std
 
 /* checks that parameterized types work. exits with 0. */
-type option(@a::(integral,numeric)) = union
-	`Some @a::(integral,numeric)
+type option(@a) :: integral,numeric @a = union
+	`Some @a
 	`None
 ;;
 
--- a/test/genericval.myr
+++ b/test/genericval.myr
@@ -1,6 +1,6 @@
 use std
 
-generic Foo : @a::(integral,numeric) = 42
+generic Foo : @a = 42 :: integral,numeric @a
 
 const main = {
 	std.exit(Foo)
--- a/test/gtrait.myr
+++ b/test/gtrait.myr
@@ -4,7 +4,7 @@
 	cmp	: (a : @a, b : @a -> std.order)
 ;;
 
-impl comparable @a::numeric =
+impl comparable @a :: numeric @a =
 	cmp = {a, b
 		-> std.numcmp(a, b)
 	}
--- a/test/recgeneric.myr
+++ b/test/recgeneric.myr
@@ -1,6 +1,6 @@
 use std
 
-type o(@a::integral) = union
+type o(@a) :: integral @a = union
 	`S @a
 ;;
 
--- a/test/trait-builtin.myr
+++ b/test/trait-builtin.myr
@@ -7,7 +7,7 @@
 exits with 42.
 */
 
-generic max = {a:@a::numeric, b:@a::numeric
+generic max = {a : @a, b : @a :: numeric @a
 	if a > b
 		-> a
 	else
@@ -15,7 +15,7 @@
 	;;
 }
 
-generic intlike_is42 = {a : @a::(numeric,integral)
+generic intlike_is42 = {a : @a :: numeric,integral @a
 	-> a == 42
 }
 
--- a/test/traitimpl.myr
+++ b/test/traitimpl.myr
@@ -22,7 +22,7 @@
 	}
 ;;
 
-generic foo = {x : @a::frobable
+generic foo = {x : @a :: frobable @a
 	-> frob(x)
 }