Refactor cgen's write{Load,Save}DerivedVar
diff --git a/internal/cgen/func.go b/internal/cgen/func.go
index db48434..5b7c97e 100644
--- a/internal/cgen/func.go
+++ b/internal/cgen/func.go
@@ -384,8 +384,10 @@
 	if g.currFunk.derivedVars != nil {
 		for _, o := range g.currFunk.astFunc.In().Fields() {
 			o := o.AsField()
-			if err := g.writeLoadDerivedVar(b, "", aPrefix, o.Name(), o.XType(), true); err != nil {
-				return err
+			if _, ok := g.currFunk.derivedVars[o.Name()]; ok {
+				if err := g.writeInitialLoadDerivedVar(b, o); err != nil {
+					return err
+				}
 			}
 		}
 		b.writes("\n")
@@ -480,8 +482,10 @@
 	} else if g.currFunk.derivedVars != nil {
 		for _, o := range g.currFunk.astFunc.In().Fields() {
 			o := o.AsField()
-			if err := g.writeSaveDerivedVar(b, "", aPrefix, o.Name(), o.XType()); err != nil {
-				return err
+			if _, ok := g.currFunk.derivedVars[o.Name()]; ok {
+				if err := g.writeFinalSaveDerivedVar(b, o); err != nil {
+					return err
+				}
 			}
 		}
 		b.writes("\n")
diff --git a/internal/cgen/statement.go b/internal/cgen/statement.go
index a16e66a..7ff3efe 100644
--- a/internal/cgen/statement.go
+++ b/internal/cgen/statement.go
@@ -452,8 +452,10 @@
 	if g.currFunk.derivedVars != nil {
 		for _, o := range g.currFunk.astFunc.In().Fields() {
 			o := o.AsField()
-			if err := g.writeSaveDerivedVar(b, "", aPrefix, o.Name(), o.XType()); err != nil {
-				return err
+			if _, ok := g.currFunk.derivedVars[o.Name()]; ok {
+				if err := g.writeFinalSaveDerivedVar(b, o); err != nil {
+					return err
+				}
 			}
 		}
 	}
diff --git a/internal/cgen/var.go b/internal/cgen/var.go
index a06eda9..6106b29 100644
--- a/internal/cgen/var.go
+++ b/internal/cgen/var.go
@@ -69,160 +69,183 @@
 	}
 }
 
-func (g *gen) writeLoadDerivedVar(b *buffer, hack string, prefix string, name t.ID, typ *a.TypeExpr, header bool) error {
-	// TODO: remove this hack. We're picking up the wrong name for "src:r,
-	// dummy:args.src".
-	if name.Str(g.tm) == "dummy" {
-		name = g.tm.ByName("src")
-	}
-	// TODO: also remove this hack.
-	if hack == "w" {
-		b.printf("%s%sw = %sw.data.ptr + %sw.meta.wi;\n", iopPrefix, vPrefix, uPrefix, uPrefix)
-		return nil
-	} else if hack == "r" {
-		b.printf("%s%sr = %sr.data.ptr + %sr.meta.ri;\n", iopPrefix, vPrefix, uPrefix, uPrefix)
-		return nil
-	}
-
-	elem := ""
-	if typ.IsIOType() {
-		if typ.QID()[1] == t.IDIOReader {
-			elem = "const uint8_t"
-		} else {
-			elem = "uint8_t"
+func (g *gen) derivedVarCNames(typ *a.TypeExpr) (elem string, i1 string, i2 string, isWriter bool, retErr error) {
+	if typ.Decorator() == 0 {
+		if qid := typ.QID(); qid[0] == t.IDBase {
+			switch qid[1] {
+			case t.IDIOReader:
+				return "uint8_t", "meta.ri", "meta.wi", false, nil
+			case t.IDIOWriter:
+				return "uint8_t", "meta.wi", "data.len", true, nil
+			case t.IDTokenReader:
+				return "wuffs_base__token", "meta.ri", "meta.wi", false, nil
+			case t.IDTokenWriter:
+				return "wuffs_base__token", "meta.wi", "data.len", true, nil
+			}
 		}
-	} else if typ.IsTokenType() {
-		if typ.QID()[1] == t.IDTokenReader {
-			elem = "const wuffs_base__token"
-		} else {
-			elem = "wuffs_base__token"
-		}
-	} else {
-		return nil
+	}
+	return "", "", "", false, fmt.Errorf("unsupported derivedVarCNames type %q", typ.Str(g.tm))
+}
+
+func (g *gen) writeInitialLoadDerivedVar(b *buffer, n *a.Field) error {
+	elem, i1, i2, isWriter, err := g.derivedVarCNames(n.XType())
+	if err != nil {
+		return err
+	}
+	preName := aPrefix + n.Name().Str(g.tm)
+	c := ""
+	if !isWriter {
+		c = "const "
 	}
 
-	if g.currFunk.derivedVars == nil {
-		return nil
-	}
-	if _, ok := g.currFunk.derivedVars[name]; !ok {
-		return nil
-	}
-
-	preName := prefix + name.Str(g.tm)
-	i1, i2 := "meta.ri", "meta.wi"
-	if q := typ.QID()[1]; (q == t.IDIOWriter) || (q == t.IDTokenWriter) {
-		i1, i2 = "meta.wi", "data.len"
-	}
-
-	if header {
-		b.printf("%s* %s%s = NULL;\n", elem, iopPrefix, preName)
-		b.printf("%s* %s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", elem, io0Prefix, preName)
-		b.printf("%s* %s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", elem, io1Prefix, preName)
-		b.printf("%s* %s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", elem, io2Prefix, preName)
-	}
+	b.printf("%s%s* %s%s = NULL;\n", c, elem, iopPrefix, preName)
+	b.printf("%s%s* %s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", c, elem, io0Prefix, preName)
+	b.printf("%s%s* %s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", c, elem, io1Prefix, preName)
+	b.printf("%s%s* %s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", c, elem, io2Prefix, preName)
 
 	b.printf("if (%s) {\n", preName)
 
-	if header {
-		b.printf("%s%s = %s->data.ptr;\n", io0Prefix, preName, preName)
-		b.printf("%s%s = %s%s + %s->%s;\n", io1Prefix, preName, io0Prefix, preName, preName, i1)
-		b.printf("%s%s = %s%s;\n", iopPrefix, preName, io1Prefix, preName)
-		b.printf("%s%s = %s%s + %s->%s;\n", io2Prefix, preName, io0Prefix, preName, preName, i2)
+	b.printf("%s%s = %s->data.ptr;\n", io0Prefix, preName, preName)
+	b.printf("%s%s = %s%s + %s->%s;\n", io1Prefix, preName, io0Prefix, preName, preName, i1)
+	b.printf("%s%s = %s%s;\n", iopPrefix, preName, io1Prefix, preName)
+	b.printf("%s%s = %s%s + %s->%s;\n", io2Prefix, preName, io0Prefix, preName, preName, i2)
 
-		if q := typ.QID()[1]; (q == t.IDIOWriter) || (q == t.IDTokenWriter) {
-			b.printf("if (%s->meta.closed) {\n", preName)
-			b.printf("%s%s = %s%s;\n", io2Prefix, preName, iopPrefix, preName)
-			b.printf("}\n")
-		}
-	} else {
-		b.printf("%s%s = %s->data.ptr + %s->%s;\n", iopPrefix, preName, preName, preName, i1)
+	if isWriter {
+		b.printf("if (%s->meta.closed) {\n", preName)
+		b.printf("%s%s = %s%s;\n", io2Prefix, preName, iopPrefix, preName)
+		b.printf("}\n")
 	}
 
 	b.printf("}\n")
 	return nil
 }
 
-func (g *gen) writeSaveDerivedVar(b *buffer, hack string, prefix string, name t.ID, typ *a.TypeExpr) error {
-	// TODO: remove this hack. We're picking up the wrong name for "src:r,
-	// dummy:args.src".
-	if name.Str(g.tm) == "dummy" {
-		name = g.tm.ByName("src")
+func (g *gen) writeFinalSaveDerivedVar(b *buffer, n *a.Field) error {
+	_, i1, _, _, err := g.derivedVarCNames(n.XType())
+	if err != nil {
+		return err
 	}
-	// TODO: also remove this hack.
-	if hack == "w" {
-		b.printf("%sw.meta.wi = ((size_t)(%s%sw - %sw.data.ptr));\n", uPrefix, iopPrefix, vPrefix, uPrefix)
-		return nil
-	} else if hack == "r" {
-		b.printf("%sr.meta.ri = ((size_t)(%s%sr - %sr.data.ptr));\n", uPrefix, iopPrefix, vPrefix, uPrefix)
-		return nil
-	}
+	preName := aPrefix + n.Name().Str(g.tm)
 
-	if !typ.IsIOTokenType() {
-		return nil
-	}
-	if g.currFunk.derivedVars == nil {
-		return nil
-	}
-	if _, ok := g.currFunk.derivedVars[name]; !ok {
-		return nil
-	}
-
-	preName := prefix + name.Str(g.tm)
-	index := "ri"
-	if q := typ.QID()[1]; (q == t.IDIOWriter) || (q == t.IDTokenWriter) {
-		index = "wi"
-	}
-
-	b.printf("if (%s) {\n%s->meta.%s = ((size_t)(%s%s - %s->data.ptr));\n}\n",
-		preName, preName, index, iopPrefix, preName, preName)
+	b.printf("if (%s) {\n%s->%s = ((size_t)(%s%s - %s->data.ptr));\n}\n",
+		preName, preName, i1, iopPrefix, preName, preName)
 	return nil
 }
 
-func (g *gen) writeLoadExprDerivedVars(b *buffer, n *a.Expr) error {
-	if g.currFunk.derivedVars == nil {
-		return nil
+func (g *gen) writeLoadDerivedVar(b *buffer, n *a.Expr) error {
+	_, i1, _, _, err := g.derivedVarCNames(n.MType())
+	if err != nil {
+		return err
 	}
-	if n.Operator() != t.IDOpenParen {
+
+	switch n.Operator() {
+	case 0:
+		name := n.Ident().Str(g.tm)
+		b.printf("%s%s%s = %s%s.data.ptr + %s%s.%s;\n",
+			iopPrefix, vPrefix, name, uPrefix, name, uPrefix, name, i1)
 		return nil
-	}
-	for _, o := range n.Args() {
-		o := o.AsArg()
-		prefix := aPrefix
-		// TODO: don't hard-code these.
-		hack := ""
-		if s := o.Value().Str(g.tm); s != "args.dst" && s != "args.src" && s != "w" && s != "r" {
-			continue
-		} else if s == "w" || s == "r" {
-			prefix = vPrefix
-			hack = s
+
+	case t.IDDot:
+		if lhs := n.LHS().AsExpr(); (lhs.Operator() == 0) && (lhs.Ident() == t.IDArgs) {
+			name := n.Ident().Str(g.tm)
+			b.printf("if (%s%s) {\n", aPrefix, name)
+			b.printf("%s%s%s = %s%s->data.ptr + %s%s->%s;\n",
+				iopPrefix, aPrefix, name, aPrefix, name, aPrefix, name, i1)
+			b.printf("}\n")
+			return nil
 		}
-		if err := g.writeLoadDerivedVar(b, hack, prefix, o.Name(), o.Value().MType(), false); err != nil {
-			return err
+	}
+
+	return fmt.Errorf("could not determine derived variables for %q", n.Str(g.tm))
+}
+
+func (g *gen) writeSaveDerivedVar(b *buffer, n *a.Expr) error {
+	_, i1, _, _, err := g.derivedVarCNames(n.MType())
+	if err != nil {
+		return err
+	}
+
+	switch n.Operator() {
+	case 0:
+		name := n.Ident().Str(g.tm)
+		b.printf("%s%s.%s = ((size_t)(%s%s%s - %s%s.data.ptr));\n",
+			uPrefix, name, i1, iopPrefix, vPrefix, name, uPrefix, name)
+		return nil
+
+	case t.IDDot:
+		if lhs := n.LHS().AsExpr(); (lhs.Operator() == 0) && (lhs.Ident() == t.IDArgs) {
+			name := n.Ident().Str(g.tm)
+			b.printf("if (%s%s) {\n", aPrefix, name)
+			b.printf("%s%s->%s = ((size_t)(%s%s%s - %s%s->data.ptr));\n",
+				aPrefix, name, i1, iopPrefix, aPrefix, name, aPrefix, name)
+			b.printf("}\n")
+			return nil
+		}
+	}
+
+	return fmt.Errorf("could not determine derived variables for %q", n.Str(g.tm))
+}
+
+func (g *gen) couldHaveDerivedVar(n *a.Expr) bool {
+	typ := n.MType()
+	if typ.Decorator() != 0 {
+		return false
+	}
+
+	qid := typ.QID()
+	if qid[0] != t.IDBase {
+		return false
+	}
+	switch qid[1] {
+	case t.IDIOReader, t.IDIOWriter, t.IDTokenReader, t.IDTokenWriter:
+		// No-op.
+	default:
+		return false
+	}
+
+	switch n.Operator() {
+	case t.IDDot:
+		if lhs := n.LHS().AsExpr(); (lhs.Operator() == 0) && (lhs.Ident() == t.IDArgs) {
+			if g.currFunk.derivedVars == nil {
+				return false
+			} else if _, ok := g.currFunk.derivedVars[n.Ident()]; !ok {
+				return false
+			}
+		}
+
+	case t.IDOpenParen:
+		switch n.LHS().AsExpr().Ident() {
+		case t.IDEmptyIOReader, t.IDEmptyIOWriter:
+			if n.LHS().AsExpr().LHS().AsExpr().MType().Eq(typeExprUtility) {
+				return false
+			}
+		}
+	}
+
+	return true
+}
+
+func (g *gen) writeLoadExprDerivedVars(b *buffer, n *a.Expr) error {
+	if (g.currFunk.derivedVars != nil) && (n.Operator() == t.IDOpenParen) {
+		for _, o := range n.Args() {
+			if v := o.AsArg().Value(); g.couldHaveDerivedVar(v) {
+				if err := g.writeLoadDerivedVar(b, v); err != nil {
+					return err
+				}
+			}
 		}
 	}
 	return nil
 }
 
 func (g *gen) writeSaveExprDerivedVars(b *buffer, n *a.Expr) error {
-	if g.currFunk.derivedVars == nil {
-		return nil
-	}
-	if n.Operator() != t.IDOpenParen {
-		return nil
-	}
-	for _, o := range n.Args() {
-		o := o.AsArg()
-		prefix := aPrefix
-		// TODO: don't hard-code these.
-		hack := ""
-		if s := o.Value().Str(g.tm); s != "args.dst" && s != "args.src" && s != "w" && s != "r" {
-			continue
-		} else if s == "w" || s == "r" {
-			prefix = vPrefix
-			hack = s
-		}
-		if err := g.writeSaveDerivedVar(b, hack, prefix, o.Name(), o.Value().MType()); err != nil {
-			return err
+	if (g.currFunk.derivedVars != nil) && (n.Operator() == t.IDOpenParen) {
+		for _, o := range n.Args() {
+			if v := o.AsArg().Value(); g.couldHaveDerivedVar(v) {
+				if err := g.writeSaveDerivedVar(b, v); err != nil {
+					return err
+				}
+			}
 		}
 	}
 	return nil