Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Composite refs #386

Merged
merged 2 commits into from
Jul 31, 2017
Merged

Conversation

mmussomele
Copy link
Contributor

@mmussomele mmussomele commented Jul 18, 2017

Fixes #239

@mmussomele mmussomele force-pushed the composite-refs branch 5 times, most recently from 577290c to dd04aca Compare July 20, 2017 00:39
Copy link
Member

@tsandall tsandall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this looks really good. Left a few comments but I'll have to come back and do another pass.

ast/check.go Outdated
@@ -487,6 +487,9 @@ func (rc *refChecker) checkRef(curr *TypeEnv, node *typeTreeNode, ref Ref, idx i
return newRefErrInvalid(ref[0].Location, ref, idx, exist, types.S, getOneOfForNode(node))
}

case Array, Object, *Set:
rc.env.tree.PutOne(value, rc.env.Get(value))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be a type error. At this point, the ref operand doesn't refer to a leaf, so the key must be a string? You'll need to think about the content of the ref error details to report here. Look at the other ref errors for examples.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea I think you're right, this case just shouldn't exist. I'll just let it hit default

ast/check.go Outdated
@@ -487,6 +487,9 @@ func (rc *refChecker) checkRef(curr *TypeEnv, node *typeTreeNode, ref Ref, idx i
return newRefErrInvalid(ref[0].Location, ref, idx, exist, types.S, getOneOfForNode(node))
}

case Array, Object, *Set:
rc.env.tree.PutOne(value, rc.env.Get(value))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I suspect this is not being exercised by any test cases.

ast/check.go Outdated
@@ -534,6 +538,19 @@ func (rc *refChecker) checkRefLeaf(tpe types.Type, ref Ref, idx int) *Error {
}
}

case Array, Object, *Set:
tpe := rc.env.Get(ref[:idx])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain what's happening here?

checkRefLeaf is provided with the type of the value referred to by the ref up to (but not including) the index. checkRefLeaf should not have to lookup the type here?

ast/check.go Outdated
return nil
}

unify1(rc.env, SetTerm(head), exist, false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you consider calling unify1(rc.env, head, keys, false) instead here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yea that would be much better

storage/path.go Outdated
@@ -52,6 +52,8 @@ func NewPathForRef(ref ast.Ref) (path Path, err error) {
Code: NotFoundErr,
Message: fmt.Sprintf("%v: does not exist", ref),
}
case ast.Array, ast.Object, *ast.Set:
return nil, fmt.Errorf("composites may not be used as ref operands for base documents: %v", ref)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I'd try to make this error message more concise.

{"object", `data.test.r[{"foo": "bar"}]`, `{"foo": "bar"}`},
{"set", `data.test.r[{1, 2}]`, "[1, 2]"},

{"unify array", `data.test.r[[1, x]]`, "[1, 2]"},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if this is the best way to test refs with var operands (or non-ground composite operands). There may be more than one query result--but the test case can also assert on one of them.

{"complete doc unify", `data.test.s[[x, y]]`, `[1, 2]`},

// These should compile, but be undefined.
{"array doc", `data.test.a[[1, 2]]`, ``},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate on why this should compile? I'd expect the type checker to return an error w/ invalid ref details here.

@mmussomele
Copy link
Contributor Author

mmussomele commented Jul 20, 2017

All the issues should be fixed now. The commit IDs have changes due to resolving a merge conflict in ast/parser.go.

@tsandall
Copy link
Member

This is close, but I think more testing is needed. Specifically, anywhere that was branching based on ref operand type needs to be reviewed. E.g., what happens if you write...

p[[x,y]] { ... }

And then query p[[foo.bar.baz[_], "qux"]]?

I imagine we need to have special handling for nested refs?

What about comprehensions nested inside the composites?

@mmussomele
Copy link
Contributor Author

Ready for another look, fixed up up some checking issues and corrected eval to handle refs nested within composite operands. Comprehensions also work inside the composites.

Copy link
Member

@tsandall tsandall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only two comments (which are related) on the type checking changes.

ast/check.go Outdated
if !unifies(tpe, types.NewSet(types.A)) {
return newRefErrInvalid(ref[0].Location, ref, idx, tpe, types.NewSet(types.A), nil)
}
unify1(rc.env, head, keys, false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should check if the head unified w/ the keys.

if !unify1(...) { // type error }

@@ -738,6 +743,14 @@ func TestCheckRefErrInvalid(t *testing.T) {
want: types.S,
oneOf: []Value{String("p"), String("q")},
},
{
note: "composite ref into non-set",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about type errors with terms embedded within composite operands? This is related to the comment from above.

For example...

p = {[1],[2],[3]}

And then...p[["foo"]] or p[{"a": b"}].

@mmussomele
Copy link
Contributor Author

Comments addressed.

if err := evalRefRuleResultRec(t, e.Value, tail, path, iter); err != nil {
return err
}
path = path[:len(path)-1]
t.Unbind(undo)
}
return nil
case ast.Array, ast.Object, *ast.Set:
Copy link
Member

@tsandall tsandall Jul 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This entire function can be simplified to unify the set elements with the next element in the ref. Something like...

	head, tail := ref[0], ref[1:]
	k := head.Value
	for _, e := range *set {
		undo, err := evalEqUnify(t, e.Value, k, nil, func(t *Topdown) error {
			return evalRefRuleResultRec(t, e.Value, tail, path.Append(e), iter)
		})
		if err != nil {
			return err
		}
		t.Unbind(undo)
	}
	return nil

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0.o it's like magic

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Member

@tsandall tsandall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Please squash your commits and then we can merge this.

@mmussomele
Copy link
Contributor Author

Done

@tsandall tsandall merged commit 140599f into open-policy-agent:master Jul 31, 2017
@mmussomele mmussomele deleted the composite-refs branch July 31, 2017 16:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants