Commit type: Compute the type for meta too (#2185)

This changes to compute the type for the meta field in a similar
way to how we compute the type for the value field.

Fixes #2179
This commit is contained in:
Erik Arvidsson
2016-07-28 17:14:35 -07:00
committed by GitHub
parent a995d5dafc
commit ff88ff2a65
5 changed files with 56 additions and 44 deletions

View File

@@ -15,30 +15,29 @@ const (
MetaField = "meta"
)
var valueCommitType = makeCommitType(types.ValueType)
var valueCommitType = makeCommitType(types.ValueType, nil, types.EmptyStructType, nil)
// NewCommit creates a new commit object. The type of Commit is computed based on the type of the value and the type of the parents.
// It also includes a Meta field whose type is always the empty struct
// NewCommit creates a new commit object. The type of Commit is computed based on the type of the value, the type of the meta info as well as the type of the parents.
//
// For the first commit we get:
//
// ```
// struct Commit {
// meta: struct {},
// meta: M,
// parents: Set<Ref<Cycle<0>>>,
// value: T,
// }
// ```
//
// As long as we continue to commit values with type T that type stays the same.
// As long as we continue to commit values with type T and meta of type M that type stays the same.
//
// When we later commits a value of type U we get:
// When we later do a commit with value of type U and meta of type N we get:
//
// ```
// struct Commit {
// meta: struct {},
// meta: N,
// parents: Set<Ref<struct Commit {
// meta: struct {},
// meta: M | N,
// parents: Set<Ref<Cycle<0>>>,
// value: T | U
// }>>,
@@ -46,32 +45,39 @@ var valueCommitType = makeCommitType(types.ValueType)
// }
// ```
//
// The new type gets combined as a union type for the value of the inner commit struct.
// Similarly if we do a commit with a different type for the meta info.
//
// The new type gets combined as a union type for the value/meta of the inner commit struct.
func NewCommit(value types.Value, parents types.Set, meta types.Struct) types.Struct {
t := makeCommitType(value.Type(), valueTypesFromParents(parents)...)
t := makeCommitType(value.Type(), valueTypesFromParents(parents, ValueField), meta.Type(), valueTypesFromParents(parents, MetaField))
return types.NewStructWithType(t, types.ValueSlice{meta, parents, value})
}
func makeCommitType(valueType *types.Type, parentsValueTypes ...*types.Type) *types.Type {
tmp := make([]*types.Type, len(parentsValueTypes)+1)
func makeCommitType(valueType *types.Type, parentsValueTypes []*types.Type, metaType *types.Type, parentsMetaTypes []*types.Type) *types.Type {
tmp := make([]*types.Type, len(parentsValueTypes), len(parentsValueTypes)+1)
copy(tmp, parentsValueTypes)
tmp[len(tmp)-1] = valueType
tmp = append(tmp, valueType)
parentsValueUnionType := types.MakeUnionType(tmp...)
tmp2 := make([]*types.Type, len(parentsMetaTypes), len(parentsMetaTypes)+1)
copy(tmp2, parentsMetaTypes)
tmp2 = append(tmp2, metaType)
parentsMetaUnionType := types.MakeUnionType(tmp2...)
fieldNames := []string{MetaField, ParentsField, ValueField}
var parentsType *types.Type
if parentsValueUnionType.Equals(valueType) {
if parentsValueUnionType.Equals(valueType) && parentsMetaUnionType.Equals(metaType) {
parentsType = types.MakeSetType(types.MakeRefType(types.MakeCycleType(0)))
} else {
parentsType = types.MakeSetType(types.MakeRefType(
types.MakeStructType("Commit", fieldNames, []*types.Type{
types.EmptyStructType,
parentsMetaUnionType,
types.MakeSetType(types.MakeRefType(types.MakeCycleType(0))),
parentsValueUnionType,
})))
}
fieldTypes := []*types.Type{
types.EmptyStructType,
metaType,
parentsType,
valueType,
}
@@ -79,17 +85,17 @@ func makeCommitType(valueType *types.Type, parentsValueTypes ...*types.Type) *ty
return types.MakeStructType("Commit", fieldNames, fieldTypes)
}
func valueTypesFromParents(parents types.Set) []*types.Type {
func valueTypesFromParents(parents types.Set, fieldName string) []*types.Type {
elemType := getSetElementType(parents.Type())
switch elemType.Kind() {
case types.UnionKind:
ts := []*types.Type{}
for _, rt := range elemType.Desc.(types.CompoundDesc).ElemTypes {
ts = append(ts, valueFromRefOfCommit(rt))
ts = append(ts, fieldTypeFromRefOfCommit(rt, fieldName))
}
return ts
default:
return []*types.Type{valueFromRefOfCommit(elemType)}
return []*types.Type{fieldTypeFromRefOfCommit(elemType, fieldName)}
}
}
@@ -98,8 +104,8 @@ func getSetElementType(t *types.Type) *types.Type {
return t.Desc.(types.CompoundDesc).ElemTypes[0]
}
func valueFromRefOfCommit(t *types.Type) *types.Type {
return valueTypeFromCommit(getRefElementType(t))
func fieldTypeFromRefOfCommit(t *types.Type, fieldName string) *types.Type {
return fieldTypeFromCommit(getRefElementType(t), fieldName)
}
func getRefElementType(t *types.Type) *types.Type {
@@ -107,9 +113,9 @@ func getRefElementType(t *types.Type) *types.Type {
return t.Desc.(types.CompoundDesc).ElemTypes[0]
}
func valueTypeFromCommit(t *types.Type) *types.Type {
func fieldTypeFromCommit(t *types.Type, fieldName string) *types.Type {
d.Chk.True(t.Kind() == types.StructKind && t.Desc.(types.StructDesc).Name == "Commit")
return t.Desc.(types.StructDesc).Field(ValueField)
return t.Desc.(types.StructDesc).Field(fieldName)
}
func IsCommitType(t *types.Type) bool {

View File

@@ -51,12 +51,14 @@ func TestNewCommit(t *testing.T) {
// Now commit a String with MetaInfo
meta := types.NewStruct("Meta", map[string]types.Value{"date": types.String("some date"), "number": types.Number(9)})
metaType := types.MakeStructType("Meta", []string{"date", "number"}, []*types.Type{types.StringType, types.NumberType})
assertTypeEquals(metaType, meta.Type())
commit4 := NewCommit(types.String("Hi"), types.NewSet(types.NewRef(commit2)), meta)
at4 := commit4.Type()
et4 := types.MakeStructType("Commit", commitFieldNames, []*types.Type{
types.EmptyStructType,
metaType,
types.MakeSetType(types.MakeRefType(types.MakeStructType("Commit", commitFieldNames, []*types.Type{
types.EmptyStructType,
types.MakeUnionType(types.EmptyStructType, metaType),
types.MakeSetType(types.MakeRefType(types.MakeCycleType(0))),
types.MakeUnionType(types.NumberType, types.StringType),
}))),

View File

@@ -1,7 +1,7 @@
{
"name": "@attic/noms",
"license": "Apache-2.0",
"version": "55.0.0",
"version": "55.0.1",
"description": "Noms JS SDK",
"repository": "https://github.com/attic-labs/noms",
"main": "dist/commonjs/noms.js",

View File

@@ -61,12 +61,13 @@ suite('commit.js', () => {
// Now commit a String with MetaInfo
const meta = newStruct('Meta', {date: 'some date', number: 9});
const metaType = makeStructType('Meta', ['date', 'number'], [stringType, numberType]);
const commit4 = new Commit('Hi', new Set([new Ref(commit2)]), meta);
const at4 = commit4.type;
const et4 = makeStructType('Commit', commitFieldNames, [
emptyStructType,
metaType,
makeSetType(makeRefType(makeStructType('Commit', commitFieldNames, [
emptyStructType,
makeUnionType([emptyStructType, metaType]),
makeSetType(makeRefType(makeCycleType(0))),
makeUnionType([numberType, stringType]),
]))),

View File

@@ -33,7 +33,8 @@ const valueIndex = 2;
export default class Commit<T: Value> extends Struct {
constructor(value: T, parents: Set<Ref<Commit>> = new Set(), meta: Struct = getEmptyStruct()) {
const t = makeCommitType(getTypeOfValue(value), valueTypesFromParents(parents));
const t = makeCommitType(getTypeOfValue(value), valueTypesFromParents(parents, 'value'),
getTypeOfValue(meta), valueTypesFromParents(parents, 'meta'));
super(t, [meta, parents, value]);
}
@@ -73,34 +74,36 @@ export default class Commit<T: Value> extends Struct {
}
}
function makeCommitType(valueType: Type<*>, parentsValueTypes: Type<*>[]): Type<StructDesc> {
// ../../go/datas/commit.go for the motivation for how this is computed.
function makeCommitType(valueType: Type<*>, parentsValueTypes: Type<*>[],
metaType: Type<*>, parentsMetaTypes: Type<*>[]): Type<StructDesc> {
const fieldNames = ['meta', 'parents', 'value'];
const tmp = parentsValueTypes.concat(valueType);
const parentsValueUnionType = makeUnionType(tmp);
const parentsValueUnionType = makeUnionType(parentsValueTypes.concat(valueType));
const parentsMetaUnionType = makeUnionType(parentsMetaTypes.concat(metaType));
let parentsType;
if (equals(parentsValueUnionType, valueType)) {
if (equals(parentsValueUnionType, valueType) && equals(parentsMetaUnionType, metaType)) {
parentsType = makeSetType(makeRefType(makeCycleType(0)));
} else {
parentsType = makeSetType(makeRefType(makeStructType('Commit', fieldNames, [
getEmptyStruct().type,
parentsMetaUnionType,
makeSetType(makeRefType(makeCycleType(0))),
parentsValueUnionType,
])));
}
return makeStructType('Commit', fieldNames, [
getEmptyStruct().type,
metaType,
parentsType,
valueType,
]);
}
function valueTypesFromParents(parents: Set): Type<*>[] {
function valueTypesFromParents(parents: Set, fieldName: string): Type<*>[] {
const elemType = getSetElementType(parents.type);
switch (elemType.kind) {
case Kind.Union:
return elemType.desc.elemTypes.map(valueFromRefOfCommit);
return elemType.desc.elemTypes.map(t => fieldTypeFromRefOfCommit(t, fieldName));
default:
return [valueFromRefOfCommit(elemType)];
return [fieldTypeFromRefOfCommit(elemType, fieldName)];
}
}
@@ -109,8 +112,8 @@ function getSetElementType(t: Type<CompoundDesc>): Type<*> {
return t.desc.elemTypes[0];
}
function valueFromRefOfCommit(t: Type<CompoundDesc>): Type<*> {
return valueTypeFromCommit(getRefElementType(t));
function fieldTypeFromRefOfCommit(t: Type<CompoundDesc>, fieldName: string): Type<*> {
return fieldTypeFromCommit(getRefElementType(t), fieldName);
}
function getRefElementType(t: Type<CompoundDesc>): Type<*> {
@@ -118,16 +121,16 @@ function getRefElementType(t: Type<CompoundDesc>): Type<*> {
return t.desc.elemTypes[0];
}
function valueTypeFromCommit(t: Type<StructDesc>): Type<*> {
function fieldTypeFromCommit(t: Type<StructDesc>, fieldName: string): Type<*> {
invariant(t.desc.name === 'Commit');
return notNull(t.desc.getField('value'));
return notNull(t.desc.getField(fieldName));
}
// Work around npm cyclic dependencies.
let valueCommitType;
function getValueCommitType() {
if (!valueCommitType) {
valueCommitType = makeCommitType(valueType, []);
valueCommitType = makeCommitType(valueType, [], getEmptyStruct().type, []);
}
return valueCommitType;
}