veyron/services/store: Don't put values of the wrong type.

Previously, if the caller tried to put a value of the wrong type into a
top-level field, the value would be placed in the implicit directory of the object,
under the same name as that field.

This fix adds a new failure class to field.Set. If the value has the wrong type,
field.Set will return SetFailedWrongType, and Put() will return an error.

Change-Id: I8a6294c5a717434909e36d51e73b82a64bcb727c
diff --git a/services/store/memstore/field/reflect.go b/services/store/memstore/field/reflect.go
index 5a76e29..891cb31 100644
--- a/services/store/memstore/field/reflect.go
+++ b/services/store/memstore/field/reflect.go
@@ -2,9 +2,9 @@
 
 // reflect.go uses reflection to access the fields in a value.
 //
-//    getField(v, path)
-//    setField(v, path, x)
-//    removeField(v, path)
+//    Get(v, path)
+//    Set(v, path, x)
+//    Remove(v, path)
 //
 // The path is JSON-style, using field names.  For example, consider the
 // following value.
@@ -19,19 +19,19 @@
 // Here are some possible paths:
 //
 //    var x MyValue = ...
-//    getField(x, storage.PathName{"A"}) == x.A
-//    getField(x, storage.PathName{"B", "7"}) == x.B[7]
-//    getField(x, storage.PathName{"C", "a"}) == x.C["a"]
-//    getField(x, storage.PathName{"D", "a", "E"}) == x.D["a"].E
+//    Get(x, storage.PathName{"A"}) == x.A
+//    Get(x, storage.PathName{"B", "7"}) == x.B[7]
+//    Get(x, storage.PathName{"C", "a"}) == x.C["a"]
+//    Get(x, storage.PathName{"D", "a", "E"}) == x.D["a"].E
 //
-//    setField(x, storage.PathName{"A"}, 17)
-//    getField(x, storage.PathName{"A"}) == 17
+//    Set(x, storage.PathName{"A"}, 17)
+//    Get(x, storage.PathName{"A"}) == 17
 //
-//    setField(x, storage.PathName{"D", "a"}, struct{E: 12})
-//    getField(x, storage.PathName{"D", "a", "E"}) == 12
+//    Set(x, storage.PathName{"D", "a"}, struct{E: 12})
+//    Get(x, storage.PathName{"D", "a", "E"}) == 12
 //
-//    removeField(x, storage.PathName{"D", "a"}
-//    getField(x, storage.PathName{"D", "a", "E"}) fails
+//    Remove(x, storage.PathName{"D", "a"}
+//    Get(x, storage.PathName{"D", "a", "E"}) fails
 
 import (
 	"reflect"
@@ -43,7 +43,8 @@
 type SetResult uint32
 
 const (
-	SetFailed SetResult = iota
+	SetFailedNotFound SetResult = iota
+	SetFailedWrongType
 	SetAsValue
 	SetAsID
 )
@@ -135,26 +136,27 @@
 //
 // Here are the possible cases:
 //
-// 1. SetFailed if the operation failed because the value has the wrong type
-//    or the path doesn't exist.
+// 1. SetFailedNotFound if the operation failed because the path doesn't exist.
 //
-// 2. SetAsValue if the operation was successful, and the value xval was
+// 2. SetFailedWrongType if the operation failed because the value has the wrong type.
+//
+// 3. SetAsValue if the operation was successful, and the value xval was
 //    stored.  The returned storage.ID is null.
 //
-// 3. SetAsId if the operation was successful, but the type of the field is
+// 4. SetAsId if the operation was successful, but the type of the field is
 //    storage.ID and xval does not have type storage.ID.  In this case, the value
 //    xval is not stored; the storage.ID is returned instead.  If the field does
 //    not already exist, a new storage.ID is created (and returned).
 //
-// The setFieldAsID case means that the value xval is to be stored as a separate
+// The setAsID case means that the value xval is to be stored as a separate
 // value in the store, not as a subfield of the current value.
 //
 // As a special case, if the field type is storage.ID, and xval has type storage.ID,
-// then it is case #2, setFieldAsValue.  The returned storage.ID is zero.
+// then it is case #2, setAsValue.  The returned storage.ID is zero.
 func Set(v reflect.Value, name string, xval interface{}) (SetResult, storage.ID) {
 	v = followPointers(v)
 	if !v.IsValid() {
-		return SetFailed, nullID
+		return SetFailedNotFound, nullID
 	}
 	switch v.Type().Kind() {
 	case reflect.Map:
@@ -164,7 +166,7 @@
 	case reflect.Struct:
 		return setStructField(v, name, xval)
 	default:
-		return SetFailed, nullID
+		return SetFailedNotFound, nullID
 	}
 }
 
@@ -172,12 +174,12 @@
 	tyV := v.Type()
 	tyKey := tyV.Key()
 	if tyKey.Kind() != reflect.String {
-		return SetFailed, nullID
+		return SetFailedNotFound, nullID
 	}
 	key := reflect.ValueOf(name).Convert(tyKey)
 	r, x, id := coerceValue(tyV.Elem(), v.MapIndex(key), xval)
-	if r == SetFailed {
-		return SetFailed, nullID
+	if r == SetFailedWrongType {
+		return SetFailedWrongType, nullID
 	}
 	v.SetMapIndex(key, x)
 	return r, id
@@ -186,8 +188,8 @@
 func setSliceField(v reflect.Value, field string, xval interface{}) (SetResult, storage.ID) {
 	if field == SliceAppendSuffix {
 		r, x, id := coerceValue(v.Type().Elem(), nullValue, xval)
-		if r == SetFailed {
-			return SetFailed, nullID
+		if r == SetFailedWrongType {
+			return SetFailedWrongType, nullID
 		}
 		// This can panic if v is not settable. It is a requirement that users of this method
 		// ensure that it is settable.
@@ -197,11 +199,11 @@
 	l := v.Len()
 	i, err := strconv.Atoi(field)
 	if err != nil || i < 0 || i >= l {
-		return SetFailed, nullID
+		return SetFailedNotFound, nullID
 	}
 	r, x, id := coerceValue(v.Type().Elem(), v.Index(i), xval)
-	if r == SetFailed {
-		return SetFailed, nullID
+	if r == SetFailedWrongType {
+		return SetFailedWrongType, nullID
 	}
 	v.Index(i).Set(x)
 	return r, id
@@ -210,12 +212,12 @@
 func setStructField(v reflect.Value, name string, xval interface{}) (SetResult, storage.ID) {
 	field, found := v.Type().FieldByName(name)
 	if !found {
-		return SetFailed, nullID
+		return SetFailedNotFound, nullID
 	}
 	fieldVal := v.FieldByName(name)
 	r, x, id := coerceValue(field.Type, fieldVal, xval)
-	if r == SetFailed {
-		return SetFailed, nullID
+	if r == SetFailedWrongType {
+		return SetFailedWrongType, nullID
 	}
 	fieldVal.Set(x)
 	return r, id
@@ -232,7 +234,7 @@
 		if prev.IsValid() {
 			var ok bool
 			if id, ok = prev.Interface().(storage.ID); !ok {
-				return SetFailed, nullValue, nullID
+				return SetFailedWrongType, nullValue, nullID
 			}
 		} else {
 			id = storage.NewID()
@@ -243,7 +245,7 @@
 	case x.Type().ConvertibleTo(ty):
 		return SetAsValue, x.Convert(ty), nullID
 	default:
-		return SetFailed, nullValue, nullID
+		return SetFailedWrongType, nullValue, nullID
 	}
 }
 
diff --git a/services/store/memstore/state/mutable_snapshot.go b/services/store/memstore/state/mutable_snapshot.go
index 0845fcd..f640eb1 100644
--- a/services/store/memstore/state/mutable_snapshot.go
+++ b/services/store/memstore/state/mutable_snapshot.go
@@ -105,6 +105,7 @@
 var (
 	errBadPath              = verror.BadArgf("malformed path")
 	errBadRef               = verror.BadArgf("value has dangling references")
+	errBadValue             = verror.BadArgf("value has the wrong type")
 	errDuplicatePutMutation = verror.BadArgf("duplicate calls to PutMutation for the same ID")
 	errNotFound             = verror.NotFoundf("not found")
 	errNotTagList           = verror.BadArgf("not a TagList")
@@ -381,7 +382,7 @@
 	name := path[len(path)-1]
 	result, id := field.Set(p, name, v)
 	switch result {
-	case field.SetFailed:
+	case field.SetFailedNotFound:
 		if len(suffix) != 0 {
 			return nil, errNotFound
 		}
@@ -389,6 +390,8 @@
 			return sn.putTags(checker, c, v)
 		}
 		return sn.putDirEntry(checker, c, name, v)
+	case field.SetFailedWrongType:
+		return nil, errBadValue
 	case field.SetAsID:
 		nc, err := sn.add(checker, id, v)
 		if err != nil {
@@ -417,8 +420,10 @@
 	name := path[len(path)-1]
 	result, id := field.Set(p, name, v)
 	switch result {
-	case field.SetFailed:
+	case field.SetFailedNotFound:
 		return nil, errNotFound
+	case field.SetFailedWrongType:
+		return nil, errBadValue
 	case field.SetAsID:
 		nc, err := sn.add(checker, id, v)
 		if err != nil {
diff --git a/services/store/memstore/state/mutable_snapshot_test.go b/services/store/memstore/state/mutable_snapshot_test.go
index c65794c..34366c6 100644
--- a/services/store/memstore/state/mutable_snapshot_test.go
+++ b/services/store/memstore/state/mutable_snapshot_test.go
@@ -21,6 +21,11 @@
 	X int
 }
 
+// Nest is a struct that nests a Value.
+type Nest struct {
+	V Value
+}
+
 var (
 	root     = &Dir{}
 	rootPath = storage.ParsePath("/")
@@ -335,3 +340,44 @@
 		t.Errorf("Expected 3, got %v", v)
 	}
 }
+
+// Put a value of the wrong type to a subfield.
+func TestPutWrongType(t *testing.T) {
+	sn := newMutableSnapshot(rootPublicID)
+
+	_, err := sn.Put(rootPublicID, rootPath, Value{7})
+	if err != nil {
+		t.Errorf("/: %v", err)
+	}
+	_, err = sn.Put(rootPublicID, storage.ParsePath("/X"), "string")
+	if !verror.Is(err, verror.BadArg) {
+		t.Errorf("/X: %v", err)
+	}
+	v, err := sn.Get(rootPublicID, storage.ParsePath("/X"))
+	if err != nil {
+		t.Errorf("/X: %v", err)
+	}
+	if v.Value != 7 {
+		t.Errorf("Expected 7, got %v", v.Value)
+	}
+
+	_, err = sn.Put(rootPublicID, storage.ParsePath("/a"), Nest{Value{42}})
+	if err != nil {
+		t.Errorf("/a: %v", err)
+	}
+	_, err = sn.Put(rootPublicID, storage.ParsePath("/a/V"), "string")
+	if !verror.Is(err, verror.BadArg) {
+		t.Errorf("/a/V: %v", err)
+	}
+	_, err = sn.Put(rootPublicID, storage.ParsePath("/a/V/X"), "string")
+	if !verror.Is(err, verror.BadArg) {
+		t.Errorf("/a/V/X: %v", err)
+	}
+	v, err = sn.Get(rootPublicID, storage.ParsePath("/a/V/X"))
+	if err != nil {
+		t.Errorf("/a/V/X: %v", err)
+	}
+	if v.Value != 42 {
+		t.Errorf("Expected 42, got %v", v.Value)
+	}
+}