cue/internal/adt: add builtin and import support

- Builtin
- BuiltinValidator
- import

Change-Id: I826ce644cd8d5000ca7998b8eaf6c96ecef6fe02
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6502
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/testdata/basicrewrite/011_list_arithmetic.txtar b/cue/testdata/basicrewrite/011_list_arithmetic.txtar
index c1b52fd..c1d26a8 100644
--- a/cue/testdata/basicrewrite/011_list_arithmetic.txtar
+++ b/cue/testdata/basicrewrite/011_list_arithmetic.txtar
@@ -11,6 +11,7 @@
 mul1_0: list1 * 0
 mul1_1: 1 * list1
 mul1_2: list1 * 2
+add1_2: list + list1
 e:      list * -1
 -- out/def --
 list: [1, 2, 3]
@@ -41,6 +42,7 @@
   mul1_0: (〈0;list1〉 * 0)
   mul1_1: (1 * 〈0;list1〉)
   mul1_2: (〈0;list1〉 * 2)
+  add1_2: (〈0;list〉 + 〈0;list1〉)
   e: (〈0;list〉 * -1)
 }
 -- out/eval --
@@ -78,8 +80,14 @@
     0: (int){ 1 }
     1: (int){ 1 }
   }
+  add1_2: (#list){
+    0: (int){ 1 }
+    1: (int){ 2 }
+    2: (int){ 3 }
+    3: (int){ 1 }
+  }
   e: (_|_){
     // [eval] cannot convert negative number to uint64:
-    //     ./in.cue:9:9
+    //     ./in.cue:10:9
   }
 }
diff --git a/cue/testdata/basicrewrite/014_disjunctions.txtar b/cue/testdata/basicrewrite/014_disjunctions.txtar
index 01e01ae..2172cb7 100644
--- a/cue/testdata/basicrewrite/014_disjunctions.txtar
+++ b/cue/testdata/basicrewrite/014_disjunctions.txtar
@@ -1,5 +1,5 @@
 # DO NOT EDIT; generated by go run testdata/gen.go
-#
+
 #name: disjunctions
 #evalPartial
 -- in.cue --
diff --git a/cue/testdata/basicrewrite/017_null.txtar b/cue/testdata/basicrewrite/017_null.txtar
index 755b9f6..c58a9a5 100644
--- a/cue/testdata/basicrewrite/017_null.txtar
+++ b/cue/testdata/basicrewrite/017_null.txtar
@@ -45,7 +45,7 @@
   eq2: (bool){ false }
   ne1: (bool){ true }
   call: (_|_){
-    // [eval] cannot call non-function *adt.Null (type nil):
+    // [eval] cannot call non-function *adt.Null (type null):
     //     ./in.cue:9:7
   }
 }
diff --git a/cue/testdata/export/020.txtar b/cue/testdata/export/020.txtar
index 660432c..5659d63 100644
--- a/cue/testdata/export/020.txtar
+++ b/cue/testdata/export/020.txtar
@@ -14,3 +14,8 @@
 b: 0
 -- out/json --
 {"a":"","b":0}
+-- out/eval --
+(struct){
+  a: (string){ "" }
+  b: (int){ 0 }
+}
diff --git a/cue/testdata/fulleval/026_dont_convert_incomplete_errors_to_non-incomplete.txtar b/cue/testdata/fulleval/026_dont_convert_incomplete_errors_to_non-incomplete.txtar
index 348ad9a..d41909a 100644
--- a/cue/testdata/fulleval/026_dont_convert_incomplete_errors_to_non-incomplete.txtar
+++ b/cue/testdata/fulleval/026_dont_convert_incomplete_errors_to_non-incomplete.txtar
@@ -45,8 +45,7 @@
 -- out/legacy-debug --
 <0>{n1: <1>{min: <<2>.max, max: ><2>.min}, n2: -<3>.num, num: <4, n3: +<3>.num, n4: (<3>.num + <3>.num), n5: (<3>.num - <3>.num), n6: (<3>.num * <3>.num), n7: (<3>.num / <3>.num), b1: !<3>.is, is: bool, s1: ""+<3>.str+"", str: string, s2: strings.ContainsAny ("dd"), s3: <4>.ContainsAny (<3>.str,"dd")}
 -- out/eval --
-(_|_){
-  // [eval]
+(struct){
   n1: (struct){
     min: (_|_){
       // [cycle] cycle error
@@ -88,11 +87,11 @@
     //     ./in.cue:13:5
   }
   s2: (_|_){
-    // [eval] cannot call non-function *adt.SelectorExpr (type nil):
+    // [incomplete] incomplete value *adt.ImportReference:
     //     ./in.cue:14:5
   }
   s3: (_|_){
-    // [eval] cannot call non-function *adt.SelectorExpr (type nil):
+    // [incomplete] incomplete value *adt.ImportReference:
     //     ./in.cue:15:5
   }
   str: (string){ string }
diff --git a/cue/testdata/fulleval/027_len_of_incomplete_types.txtar b/cue/testdata/fulleval/027_len_of_incomplete_types.txtar
index 5fe7a7c..bc9111c 100644
--- a/cue/testdata/fulleval/027_len_of_incomplete_types.txtar
+++ b/cue/testdata/fulleval/027_len_of_incomplete_types.txtar
@@ -32,3 +32,27 @@
 v10: 0
 -- out/legacy-debug --
 <0>{args: [], v1: 0, v2: 0, v3: 0, v4: 1, v5: len ((<1>{a: 3} | <2>{a: 4})), v6: len (('sf' | 'dd')), v7: 2, v8: len (([2] | [1,2])), v9: 4, v10: 0}
+-- out/eval --
+(struct){
+  args: (#list){
+  }
+  v1: (int){ 0 }
+  v2: (int){ 0 }
+  v3: (int){ 0 }
+  v4: (int){ 1 }
+  v5: (_|_){
+    // [incomplete] unresolved disjunction *adt.Disjunction (type struct):
+    //     ./in.cue:6:7
+  }
+  v6: (_|_){
+    // [incomplete] unresolved disjunction *adt.Disjunction (type bytes):
+    //     ./in.cue:7:7
+  }
+  v7: (int){ 2 }
+  v8: (_|_){
+    // [incomplete] unresolved disjunction *adt.Disjunction (type list):
+    //     ./in.cue:9:7
+  }
+  v9: (int){ 4 }
+  v10: (int){ 0 }
+}
diff --git a/cue/testdata/fulleval/032_or_builtin_should_not_fail_on_non-concrete_empty_list.txtar b/cue/testdata/fulleval/032_or_builtin_should_not_fail_on_non-concrete_empty_list.txtar
index b3e4875..1a44031 100644
--- a/cue/testdata/fulleval/032_or_builtin_should_not_fail_on_non-concrete_empty_list.txtar
+++ b/cue/testdata/fulleval/032_or_builtin_should_not_fail_on_non-concrete_empty_list.txtar
@@ -41,3 +41,21 @@
 {"foo":{"jobs":{"foo":{}}}}
 -- out/legacy-debug --
 <0>{#Workflow: <1>C{jobs: <2>{[]: <3>(jobID: string)-><4>C{}, }, #JobID: or ([ <5>for k, _ in <6>.jobs yield <5>.k ])}, foo: <7>C{jobs: <8>{[]: <9>(jobID: string)-><10>C{}, foo: <11>C{}}, #JobID: "foo"}}
+-- out/eval --
+(struct){
+  #Workflow: (#struct){
+    jobs: (#struct){
+    }
+    #JobID: (_|_){
+      // [incomplete] empty list in call to or:
+      //     ./in.cue:6:10
+    }
+  }
+  foo: (#struct){
+    jobs: (#struct){
+      foo: (#struct){
+      }
+    }
+    #JobID: (string){ "foo" }
+  }
+}
diff --git "a/cue/testdata/fulleval/044_Issue_\043178.txtar" "b/cue/testdata/fulleval/044_Issue_\043178.txtar"
index b34015d..4ad3745 100644
--- "a/cue/testdata/fulleval/044_Issue_\043178.txtar"
+++ "b/cue/testdata/fulleval/044_Issue_\043178.txtar"
@@ -25,16 +25,15 @@
 -- out/legacy-debug --
 <0>{foo: <1>.Decode (<2>.data), data: bytes, len: int, bar: <3>.EncodedLen (<2>.len)}
 -- out/eval --
-(_|_){
-  // [eval]
+(struct){
   foo: (_|_){
-    // [eval] cannot call non-function *adt.SelectorExpr (type nil):
+    // [incomplete] incomplete value *adt.ImportReference:
     //     ./in.cue:5:7
   }
   data: (bytes){ bytes }
   len: (int){ int }
   bar: (_|_){
-    // [eval] cannot call non-function *adt.SelectorExpr (type nil):
+    // [incomplete] incomplete value *adt.ImportReference:
     //     ./in.cue:9:6
   }
 }
diff --git a/cue/testdata/fulleval/049_alias_reuse_in_nested_scope.txtar b/cue/testdata/fulleval/049_alias_reuse_in_nested_scope.txtar
index cf00d94..240a8b2 100644
--- a/cue/testdata/fulleval/049_alias_reuse_in_nested_scope.txtar
+++ b/cue/testdata/fulleval/049_alias_reuse_in_nested_scope.txtar
@@ -64,3 +64,31 @@
 {"b":{"foo":"key","a":{"foo":{}}}}
 -- out/legacy-debug --
 <0>{#Foo: <1>C{connection: <2>C{[or ([ <3>for k, _ in <4>{} yield <3>.k ])]: <5>(_: string)->or ([ <3>for k, _ in <4>{} yield <3>.k ]), }}, #A: <6>C{foo: "key", a: <7>C{foo: <8>C{["key"]: <9>(_: string)-><10>.foo, }}}, #B: <11>C{foo: string, a: <12>C{foo: <13>C{[string]: <14>(_: string)-><15>.foo, }}}, b: <16>C{foo: "key", a: <17>C{foo: <18>C{["key"]: <19>(_: string)-><20>.foo, }}}}
+-- out/eval --
+(struct){
+  #Foo: (#struct){
+    connection: (#struct){
+    }
+  }
+  #A: (#struct){
+    foo: (string){ "key" }
+    a: (#struct){
+      foo: (#struct){
+      }
+    }
+  }
+  #B: (#struct){
+    foo: (string){ string }
+    a: (#struct){
+      foo: (#struct){
+      }
+    }
+  }
+  b: (#struct){
+    foo: (string){ "key" }
+    a: (#struct){
+      foo: (#struct){
+      }
+    }
+  }
+}
diff --git a/cue/testdata/references/labels.txtar b/cue/testdata/references/labels.txtar
new file mode 100644
index 0000000..829c79d
--- /dev/null
+++ b/cue/testdata/references/labels.txtar
@@ -0,0 +1,179 @@
+-- in.cue --
+// Label aliases
+
+// direct
+a: [X=string]: X
+a: bar: {}
+
+// in struct
+b: [X=string]: {X}
+b: bar: {}
+
+// in structs
+c: [X=string]: X
+c: [Y=string]: {{{Y}}}
+c: bar: {}
+
+// in sub field
+d: [X=string]: name: X
+d: bar: {}
+
+// nested
+e: [X=string]: [Y=string]: X + Y
+e: foo: bar: {}
+
+// Field aliases
+
+bar: 3
+f1: C="foo\(bar)": {
+    name: "xx"
+    foo: C.name
+}
+
+// nested
+f1: D="foo\(bar)": E="foo\(bar)baz": {
+    name: "xx"
+    a: D["foo\(bar)baz"].name
+    b: E.name
+}
+
+// Combo
+
+c1: C="foo\(bar)": [D=string]: {
+    name: D
+    foo: C.x.name
+}
+c1: foo3: x: _
+
+
+// TODO: support. Also not yet supported in old implementation.
+// c10: {
+// 	C=[string]: {
+// 		name: "x"
+// 		foo: C.name
+// 	}
+// }
+// c2: c1 & { x: _ }
+
+-- out/eval --
+(struct){
+  a: (struct){
+    bar: (string){ "bar" }
+  }
+  b: (struct){
+    bar: (string){ "bar" }
+  }
+  c: (struct){
+    bar: (string){ "bar" }
+  }
+  d: (struct){
+    bar: (struct){
+      name: (string){ "bar" }
+    }
+  }
+  e: (struct){
+    foo: (struct){
+      bar: (string){ "foobar" }
+    }
+  }
+  bar: (int){ 3 }
+  f1: (struct){
+    foo3: (struct){
+      name: (string){ "xx" }
+      foo: (string){ "xx" }
+      foo3baz: (struct){
+        name: (string){ "xx" }
+        a: (string){ "xx" }
+        b: (string){ "xx" }
+      }
+    }
+  }
+  c1: (struct){
+    foo3: (struct){
+      x: (struct){
+        name: (string){ "x" }
+        foo: (string){ "x" }
+      }
+    }
+  }
+}
+-- out/compile --
+--- in.cue
+{
+  a: {
+    [string]: 〈0;-〉
+  }
+  a: {
+    bar: {}
+  }
+  b: {
+    [string]: {
+      〈1;-〉
+    }
+  }
+  b: {
+    bar: {}
+  }
+  c: {
+    [string]: 〈0;-〉
+  }
+  c: {
+    [string]: {
+      {
+        {
+          〈3;-〉
+        }
+      }
+    }
+  }
+  c: {
+    bar: {}
+  }
+  d: {
+    [string]: {
+      name: 〈1;-〉
+    }
+  }
+  d: {
+    bar: {}
+  }
+  e: {
+    [string]: {
+      [string]: (〈1;-〉 + 〈0;-〉)
+    }
+  }
+  e: {
+    foo: {
+      bar: {}
+    }
+  }
+  bar: 3
+  f1: {
+    "foo\(〈1;bar〉)": {
+      name: "xx"
+      foo: 〈1;("foo\(〈1;bar〉)")〉.name
+    }
+  }
+  f1: {
+    "foo\(〈1;bar〉)": {
+      "foo\(〈2;bar〉)baz": {
+        name: "xx"
+        a: 〈2;("foo\(〈1;bar〉)")〉["foo\(〈3;bar〉)baz"].name
+        b: 〈1;("foo\(〈2;bar〉)baz")〉.name
+      }
+    }
+  }
+  c1: {
+    "foo\(〈1;bar〉)": {
+      [string]: {
+        name: 〈1;-〉
+        foo: 〈2;("foo\(〈1;bar〉)")〉.x.name
+      }
+    }
+  }
+  c1: {
+    foo3: {
+      x: _
+    }
+  }
+}
diff --git a/cue/testdata/references/labelstop.txtar b/cue/testdata/references/labelstop.txtar
new file mode 100644
index 0000000..b936ab0
--- /dev/null
+++ b/cue/testdata/references/labelstop.txtar
@@ -0,0 +1,21 @@
+TODO: add maching when bulk optional fields are allowed
+alongside other fields.
+-- in.cue --
+{[X=string]: baz: X}
+bar: {}
+-- out/eval --
+(struct){
+  bar: (struct){
+    baz: (string){ "bar" }
+  }
+}
+-- out/compile --
+--- in.cue
+{
+  {
+    [string]: {
+      baz: 〈1;-〉
+    }
+  }
+  bar: {}
+}
diff --git a/cue/testdata/resolve/048_builtins.txtar b/cue/testdata/resolve/048_builtins.txtar
index 45da3b7..bcb6fab 100644
--- a/cue/testdata/resolve/048_builtins.txtar
+++ b/cue/testdata/resolve/048_builtins.txtar
@@ -44,3 +44,42 @@
 }
 -- out/legacy-debug --
 <0>{a1: <1>{a: (=~"oo" & =~"fo"), b: =~"oo", c: =~"fo"}, a2: <2>{a: "foo", b: =~"oo", c: =~"fo"}, a3: <3>{a: _|_((=~"oo" & "bar"):invalid value "bar" (does not match =~"oo")), b: =~"oo", c: =~"fo"}, o1: <4>{a: string, b: string, c: "bar"}, o2: <5>{a: "foo", b: string, c: "bar"}, o3: <6>{a: _|_(("baz" & "foo"):empty disjunction: conflicting values "baz" and "foo";("bar" & "foo"):empty disjunction: conflicting values "bar" and "foo"), b: "baz", c: "bar"}}
+-- out/eval --
+(_|_){
+  // [eval]
+  a1: (struct){
+    a: (string){ &(=~"oo", =~"fo") }
+    b: (string){ =~"oo" }
+    c: (string){ =~"fo" }
+  }
+  a2: (struct){
+    a: (string){ "foo" }
+    b: (string){ =~"oo" }
+    c: (string){ =~"fo" }
+  }
+  a3: (_|_){
+    // [eval]
+    a: (_|_){
+      // [eval] invalid value *adt.Vertex (out of bound *adt.BoundValue)
+    }
+    b: (string){ =~"oo" }
+    c: (string){ =~"fo" }
+  }
+  o1: (struct){
+    a: (string){ |((string){ string }, (string){ "bar" }) }
+    b: (string){ string }
+    c: (string){ "bar" }
+  }
+  o2: (struct){
+    a: (string){ "foo" }
+    b: (string){ string }
+    c: (string){ "bar" }
+  }
+  o3: (struct){
+    a: (_|_){
+      // [incomplete] empty disjunction
+    }
+    b: (string){ "baz" }
+    c: (string){ "bar" }
+  }
+}
diff --git a/internal/core/adt/adt.go b/internal/core/adt/adt.go
index 8ae9338..120aee9 100644
--- a/internal/core/adt/adt.go
+++ b/internal/core/adt/adt.go
@@ -90,12 +90,16 @@
 	return x.Value.Concreteness()
 }
 
+func (x *NodeLink) Concreteness() Concreteness     { return Concrete }
 func (x *ListMarker) Concreteness() Concreteness   { return Concrete }
 func (x *StructMarker) Concreteness() Concreteness { return Concrete }
 
-func (*Conjunction) Concreteness() Concreteness      { return Constraint }
-func (*Disjunction) Concreteness() Concreteness      { return Constraint }
-func (*BoundValue) Concreteness() Concreteness       { return Constraint }
+func (*Conjunction) Concreteness() Concreteness { return Constraint }
+func (*Disjunction) Concreteness() Concreteness { return Constraint }
+func (*BoundValue) Concreteness() Concreteness  { return Constraint }
+
+// Constraint only applies if Builtin is used as constraint.
+func (*Builtin) Concreteness() Concreteness          { return Constraint }
 func (*BuiltinValidator) Concreteness() Concreteness { return Constraint }
 
 // Value and Expr
@@ -132,9 +136,11 @@
 func (*Disjunction) expr()      {}
 func (*BoundValue) expr()       {}
 func (*BuiltinValidator) expr() {}
+func (*Builtin) expr()          {}
 
 // Expr and Resolver
 
+func (*NodeLink) expr()         {}
 func (*FieldReference) expr()   {}
 func (*LabelReference) expr()   {}
 func (*DynamicReference) expr() {}
@@ -207,6 +213,8 @@
 func (*BoundValue) elemNode()       {}
 func (*BuiltinValidator) declNode() {}
 func (*BuiltinValidator) elemNode() {}
+func (*NodeLink) declNode()         {}
+func (*NodeLink) elemNode()         {}
 func (*FieldReference) declNode()   {}
 func (*FieldReference) elemNode()   {}
 func (*LabelReference) declNode()   {}
@@ -231,6 +239,8 @@
 func (*BinaryExpr) elemNode()       {}
 func (*CallExpr) declNode()         {}
 func (*CallExpr) elemNode()         {}
+func (*Builtin) declNode()          {}
+func (*Builtin) elemNode()          {}
 func (*DisjunctionExpr) declNode()  {}
 func (*DisjunctionExpr) elemNode()  {}
 
@@ -249,6 +259,7 @@
 func (*Conjunction) node()       {}
 func (*Disjunction) node()       {}
 func (*BoundValue) node()        {}
+func (*Builtin) node()           {}
 func (*BuiltinValidator) node()  {}
 func (*Bottom) node()            {}
 func (*Null) node()              {}
@@ -261,6 +272,7 @@
 func (*StructLit) node()         {}
 func (*ListLit) node()           {}
 func (*BoundExpr) node()         {}
+func (*NodeLink) node()          {}
 func (*FieldReference) node()    {}
 func (*LabelReference) node()    {}
 func (*DynamicReference) node()  {}
diff --git a/internal/core/adt/context.go b/internal/core/adt/context.go
index 8f9e07e..3718304 100644
--- a/internal/core/adt/context.go
+++ b/internal/core/adt/context.go
@@ -60,6 +60,10 @@
 	// StringIndexer allows for converting string labels to and from a
 	// canonical numeric representation.
 	StringIndexer
+
+	// LoadImport loads a unique Vertex associated with a given import path. It
+	// returns an error if no import for this package could be found.
+	LoadImport(importPath string) (*Vertex, errors.Error)
 }
 
 type Config struct {
diff --git a/internal/core/adt/expr.go b/internal/core/adt/expr.go
index 2346519..4836405 100644
--- a/internal/core/adt/expr.go
+++ b/internal/core/adt/expr.go
@@ -17,6 +17,7 @@
 import (
 	"bytes"
 	"fmt"
+	"io"
 	"regexp"
 
 	"github.com/cockroachdb/apd/v2"
@@ -396,6 +397,20 @@
 	}
 }
 
+// A NodeLink is used during computation to refer to an existing Vertex.
+// It is used to signal a potential cycle or reference.
+// Note that a NodeLink may be used as a value. This should be taken into
+// account.
+type NodeLink struct {
+	Node *Vertex
+}
+
+func (x *NodeLink) Kind() Kind {
+	return x.Node.Kind()
+}
+func (x *NodeLink) Source() ast.Node             { return x.Node.Source() }
+func (x *NodeLink) resolve(c *OpContext) *Vertex { return x.Node }
+
 // A FieldReference represents a lexical reference to a field.
 //
 //    a
@@ -504,18 +519,11 @@
 	return x.Src
 }
 
-// TODO: imports
-// func (x *ImportReference) resolve(c *context, e *environment) (arc, *Bottom) {
-// 	return c.r.lookupImport(e, x.importPath)
-// }
-
-// func (x *ImportReference) eval(c *context, e *environment) envVal {
-// 	arc, err := lookup(e, e.node, x.label)
-// 	if err != nil {
-// 		return err
-// 	}
-// 	return envVal{e, arc.eval()}
-// }
+func (x *ImportReference) resolve(ctx *OpContext) *Vertex {
+	path := x.ImportPath.StringValue(ctx)
+	v, _ := ctx.Runtime.LoadImport(path)
+	return v
+}
 
 // A LetReference evaluates a let expression in its original environment.
 //
@@ -878,20 +886,122 @@
 }
 
 func (x *CallExpr) evaluate(c *OpContext) Value {
-	c.addErrf(0, pos(x), "cannot call non-function %s (type %s)",
-		x.Fun, "nil")
-	return nil
+	fun := c.value(x.Fun)
+	args := []Value{}
+	for _, a := range x.Args {
+		expr := c.value(a)
+		args = append(args, expr)
+	}
+	if c.HasErr() {
+		return nil
+	}
+	b, _ := fun.(*Builtin)
+	if b == nil {
+		c.addErrf(0, pos(x.Fun), "cannot call non-function %s (type %s)",
+			c.Str(x.Fun), kind(fun))
+		return nil
+	}
+	result := b.call(c, x.Src, args)
+	if result == nil {
+		return nil
+	}
+	return c.eval(result)
 }
 
+// A Builtin is a value representing a native function call.
+type Builtin struct {
+	// TODO:  make these values for better type checking.
+	Params []Kind
+	Result Kind
+	Func   func(c *OpContext, args []Value) Expr
+
+	Package Feature
+	Name    string
+	// REMOVE: for legacy
+	Const string
+}
+
+func (x *Builtin) WriteName(w io.Writer, c *OpContext) {
+	_, _ = fmt.Fprintf(w, "%s.%s", x.Package.StringValue(c), x.Name)
+}
+
+// Kind here represents the case where Builtin is used as a Validator.
+func (x *Builtin) Kind() Kind {
+	if len(x.Params) == 0 {
+		return BottomKind
+	}
+	return x.Params[0]
+}
+
+func (x *Builtin) validate(c *OpContext, v Value) *Bottom {
+	if x.Result != BoolKind {
+		return c.NewErrf(
+			"invalid validator %s: not a bool return", x.Name)
+	}
+	if len(x.Params) != 1 {
+		return c.NewErrf(
+			"invalid validator %s: may only have one validator to be used without call", x.Name)
+	}
+	return validateWithBuiltin(c, nil, x, []Value{v})
+}
+
+func bottom(v Value) *Bottom {
+	if x, ok := v.(*Vertex); ok {
+		v = x.Value
+	}
+	b, _ := v.(*Bottom)
+	return b
+}
+
+func (x *Builtin) call(c *OpContext, call *ast.CallExpr, args []Value) Expr {
+	if len(x.Params)-1 == len(args) && x.Result == BoolKind {
+		// We have a custom builtin
+		return &BuiltinValidator{call, x, args}
+	}
+	switch {
+	case len(x.Params) < len(args):
+		c.addErrf(0, call.Rparen,
+			"too many arguments in call to %s (have %d, want %d)",
+			call.Fun, len(args), len(x.Params))
+		return nil
+	case len(x.Params) > len(args):
+		c.addErrf(0, call.Rparen,
+			"not enough arguments in call to %s (have %d, want %d)",
+			call.Fun, len(args), len(x.Params))
+		return nil
+	}
+	for i, a := range args {
+		if x.Params[i] != BottomKind {
+			if b := bottom(a); b != nil {
+				return b
+			}
+			if k := kind(a); x.Params[i]&k == BottomKind {
+				code := EvalError
+				b, _ := args[i].(*Bottom)
+				if b != nil {
+					code = b.Code
+				}
+				c.addErrf(code, pos(a),
+					"cannot use %s (type %s) as %s in argument %d to %s",
+					a, k, x.Params[i], i+1, call.Fun)
+				return nil
+			}
+		}
+	}
+	return x.Func(c, args)
+}
+
+func (x *Builtin) Source() ast.Node { return nil }
+
 // A BuiltinValidator is a Value that results from evaluation a partial call
 // to a builtin (using CallExpr).
 //
 //    strings.MinRunes(4)
 //
 type BuiltinValidator struct {
-	Src  *ast.CallExpr
-	Fun  Expr
-	Args []Value // any but the first value
+	Src     *ast.CallExpr
+	Builtin *Builtin
+	Args    []Value // any but the first value
 }
 
 func (x *BuiltinValidator) Source() ast.Node {
@@ -900,10 +1010,50 @@
 	}
 	return x.Src
 }
-func (x *BuiltinValidator) Kind() Kind { return TopKind }
+
+func (x *BuiltinValidator) Kind() Kind {
+	return x.Builtin.Params[0]
+}
 
 func (x *BuiltinValidator) validate(c *OpContext, v Value) *Bottom {
-	return nil
+	args := make([]Value, len(x.Args)+1)
+	args[0] = v
+	copy(args[1:], x.Args)
+	return validateWithBuiltin(c, x.Src, x.Builtin, args)
+}
+
+func validateWithBuiltin(c *OpContext, src *ast.CallExpr, b *Builtin, args []Value) *Bottom {
+	res := b.call(c, src, args)
+	switch v := res.(type) {
+	case nil:
+		return nil
+
+	case *Bottom:
+		return v
+
+	case *Bool:
+		if v.B {
+			return nil
+		}
+
+	default:
+		return c.NewErrf("invalid validator %s.%s", b.Package.StringValue(c), b.Name)
+	}
+
+	// failed:
+	var buf bytes.Buffer
+	b.WriteName(&buf, c)
+	if len(args) > 1 {
+		buf.WriteString("(")
+		for i, a := range args[1:] {
+			if i > 0 {
+				_, _ = buf.WriteString(", ")
+			}
+			buf.WriteString(c.Str(a))
+		}
+		buf.WriteString(")")
+	}
+	return c.NewErrf("invalid value %s (does not satisfy %s)", c.Str(args[0]), buf.String())
 }
 
 // A Disjunction represents a disjunction, where each disjunct may or may not
diff --git a/internal/core/compile/builtin.go b/internal/core/compile/builtin.go
new file mode 100644
index 0000000..8934019
--- /dev/null
+++ b/internal/core/compile/builtin.go
@@ -0,0 +1,137 @@
+// Copyright 2020 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compile
+
+import (
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/internal/core/adt"
+)
+
+// This file contains predeclared builtins.
+
+const supportedByLen = adt.StructKind | adt.BytesKind | adt.StringKind | adt.ListKind
+
+var lenBuiltin = &adt.Builtin{
+	Name:   "len",
+	Params: []adt.Kind{supportedByLen},
+	Result: adt.IntKind,
+	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
+		v := args[0]
+		if x, ok := v.(*adt.Vertex); ok {
+			switch x.Value.(type) {
+			case nil:
+				// This should not happen, but be defensive.
+				return c.NewErrf("unevaluated vertex")
+			case *adt.ListMarker:
+				return c.NewInt64(int64(len(x.Elems())), v)
+
+			case *adt.StructMarker:
+				n := 0
+				v, _ := v.(*adt.Vertex)
+				for _, a := range v.Arcs {
+					if a.Label.IsRegular() {
+						n++
+					}
+				}
+				return c.NewInt64(int64(n), v)
+
+			default:
+				v = x.Value
+			}
+		}
+
+		switch x := v.(type) {
+		case *adt.Bytes:
+			return c.NewInt64(int64(len(x.B)), v)
+		case *adt.String:
+			return c.NewInt64(int64(len(x.Str)), v)
+		default:
+			k := x.Kind()
+			if k&supportedByLen == adt.BottomKind {
+				return c.NewErrf("invalid argument type %v", k)
+			}
+			b := c.NewErrf("incomplete argument %s (type %v)", c.Str(v), k)
+			b.Code = adt.IncompleteError
+			return b
+		}
+	},
+}
+
+var closeBuiltin = &adt.Builtin{
+	Name:   "close",
+	Params: []adt.Kind{adt.StructKind},
+	Result: adt.StructKind,
+	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
+		s, ok := args[0].(*adt.Vertex)
+		if !ok {
+			return c.NewErrf("struct argument must be concrete")
+		}
+		if s.IsClosed(c) {
+			return s
+		}
+		v := *s
+		v.Value = &adt.StructMarker{NeedClose: true}
+		return &v
+	},
+}
+
+var andBuiltin = &adt.Builtin{
+	Name:   "and",
+	Params: []adt.Kind{adt.ListKind},
+	Result: adt.IntKind,
+	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
+		list := c.Elems(args[0])
+		if len(list) == 0 {
+			return &adt.Top{}
+		}
+		a := []adt.Value{}
+		for _, c := range list {
+			a = append(a, c.Value)
+		}
+		return &adt.Conjunction{Values: a}
+	},
+}
+
+var orBuiltin = &adt.Builtin{
+	Name:   "or",
+	Params: []adt.Kind{adt.ListKind},
+	Result: adt.IntKind,
+	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
+		d := []adt.Disjunct{}
+		for _, c := range c.Elems(args[0]) {
+			d = append(d, adt.Disjunct{Val: c, Default: false})
+		}
+		if len(d) == 0 {
+			// TODO(manifest): This should not be unconditionally incomplete,
+			// but it requires results from comprehensions and all to have
+			// some special status. Maybe this can be solved by having results
+			// of list comprehensions be open if they result from iterating over
+			// an open list or struct. This would actually be exactly what
+			// that means. The error here could then only add an incomplete
+			// status if the source is open.
+			return &adt.Bottom{
+				Code: adt.IncompleteError,
+				Err:  errors.Newf(c.Pos(), "empty list in call to or"),
+			}
+		}
+		v := &adt.Vertex{}
+		// TODO: make a Disjunction.
+		v.AddConjunct(adt.MakeConjunct(nil,
+			&adt.DisjunctionExpr{Values: d, HasDefaults: false},
+		))
+		c.Unify(c, v, adt.Finalized)
+		return v
+	},
+}
diff --git a/internal/core/compile/compile.go b/internal/core/compile/compile.go
index eacd993..f965240 100644
--- a/internal/core/compile/compile.go
+++ b/internal/core/compile/compile.go
@@ -37,7 +37,7 @@
 //
 // Files may return a completed parse even if it has errors.
 func Files(cfg *Config, r adt.Runtime, files ...*ast.File) (*adt.Vertex, errors.Error) {
-	c := &compiler{index: r}
+	c := newCompiler(cfg, r)
 
 	v := c.compileFiles(files)
 
@@ -47,7 +47,19 @@
 	return v, nil
 }
 
+func newCompiler(cfg *Config, r adt.Runtime) *compiler {
+	c := &compiler{
+		index: r,
+	}
+	if cfg != nil {
+		c.Config = *cfg
+	}
+	return c
+}
+
 type compiler struct {
+	Config
+
 	index adt.StringIndexer
 
 	stack      []frame
@@ -192,6 +204,10 @@
 	return res
 }
 
+func (c *compiler) compileExpr(x ast.Expr) adt.Expr {
+	return c.expr(x)
+}
+
 // resolve assumes that all existing resolutions are legal. Validation should
 // be done in a separate step if required.
 //
diff --git a/internal/core/compile/predeclared.go b/internal/core/compile/predeclared.go
index 6c54d85..28d6b59 100644
--- a/internal/core/compile/predeclared.go
+++ b/internal/core/compile/predeclared.go
@@ -42,14 +42,14 @@
 	case "number", "__number":
 		return &adt.BasicType{Src: n, K: adt.NumKind}
 
-		// case "len", "__len":
-		// 	return lenBuiltin
-		// case "close", "__close":
-		// 	return closeBuiltin
-		// case "and", "__and":
-		// 	return andBuiltin
-		// case "or", "__or":
-		// 	return orBuiltin
+	case "len", "__len":
+		return lenBuiltin
+	case "close", "__close":
+		return closeBuiltin
+	case "and", "__and":
+		return andBuiltin
+	case "or", "__or":
+		return orBuiltin
 	}
 
 	if r, ok := predefinedRanges[n.Name]; ok {
diff --git a/internal/core/debug/compact.go b/internal/core/debug/compact.go
index 67a4da9..722a480 100644
--- a/internal/core/debug/compact.go
+++ b/internal/core/debug/compact.go
@@ -169,6 +169,16 @@
 		fmt.Fprint(w, x.Op)
 		w.node(x.Value)
 
+	case *adt.NodeLink:
+		w.string(openTuple)
+		for i, f := range x.Node.Path() {
+			if i > 0 {
+				w.string(".")
+			}
+			w.label(f)
+		}
+		w.string(closeTuple)
+
 	case *adt.FieldReference:
 		w.label(x.Label)
 
@@ -253,8 +263,15 @@
 		}
 		w.string(")")
 
+	case *adt.Builtin:
+		if x.Package != 0 {
+			w.label(x.Package)
+			w.string(".")
+		}
+		w.string(x.Name)
+
 	case *adt.BuiltinValidator:
-		w.node(x.Fun)
+		w.node(x.Builtin)
 		w.string("(")
 		for i, a := range x.Args {
 			if i > 0 {
diff --git a/internal/core/debug/debug.go b/internal/core/debug/debug.go
index 6209b83..b61d58f 100644
--- a/internal/core/debug/debug.go
+++ b/internal/core/debug/debug.go
@@ -67,6 +67,12 @@
 	index  adt.StringIndexer
 	indent string
 	cfg    *Config
+
+	// modes:
+	// - show vertex
+	// - show original conjuncts
+	// - show unevaluated
+	// - auto
 }
 
 func (w *printer) string(s string) {
@@ -298,6 +304,16 @@
 		fmt.Fprint(w, x.Op)
 		w.node(x.Value)
 
+	case *adt.NodeLink:
+		w.string(openTuple)
+		for i, f := range x.Node.Path() {
+			if i > 0 {
+				w.string(".")
+			}
+			w.label(f)
+		}
+		w.string(closeTuple)
+
 	case *adt.FieldReference:
 		w.string(openTuple)
 		w.string(strconv.Itoa(int(x.UpCount)))
@@ -396,8 +412,15 @@
 		}
 		w.string(")")
 
+	case *adt.Builtin:
+		if x.Package != 0 {
+			w.label(x.Package)
+			w.string(".")
+		}
+		w.string(x.Name)
+
 	case *adt.BuiltinValidator:
-		w.node(x.Fun)
+		w.node(x.Builtin)
 		w.string("(")
 		for i, a := range x.Args {
 			if i > 0 {
diff --git a/internal/core/eval/eval_test.go b/internal/core/eval/eval_test.go
index 9dea9a5..b8cb2bf 100644
--- a/internal/core/eval/eval_test.go
+++ b/internal/core/eval/eval_test.go
@@ -92,16 +92,8 @@
 
 	"cycle/025_cannot_resolve_references_that_would_be_ambiguous": "cycle",
 
-	"export/020":                  "builtin",
-	"resolve/034_closing_structs": "builtin",
-	"resolve/048_builtins":        "builtin",
-
-	"fulleval/027_len_of_incomplete_types": "builtin",
-
-	"fulleval/032_or_builtin_should_not_fail_on_non-concrete_empty_list": "builtin",
-
-	"fulleval/049_alias_reuse_in_nested_scope": "builtin",
-	"fulleval/053_issue312":                    "builtin",
+	"resolve/034_closing_structs": "close()",
+	"fulleval/053_issue312":       "close()",
 }
 
 // TestX is for debugging. Do not delete.
diff --git a/internal/core/runtime/index.go b/internal/core/runtime/index.go
index 4907368..569e8ad 100644
--- a/internal/core/runtime/index.go
+++ b/internal/core/runtime/index.go
@@ -32,14 +32,6 @@
 	typeCache sync.Map // map[reflect.Type]evaluated
 }
 
-// work around golang-ci linter bug: fields are used.
-func init() {
-	var i index
-	i.mutex.Lock()
-	i.mutex.Unlock()
-	i.typeCache.Load(1)
-}
-
 // sharedIndex is used for indexing builtins and any other labels common to
 // all instances.
 var sharedIndex = newSharedIndex()
diff --git a/internal/core/runtime/runtime.go b/internal/core/runtime/runtime.go
index ecdaa0a..c2f7d83 100644
--- a/internal/core/runtime/runtime.go
+++ b/internal/core/runtime/runtime.go
@@ -14,6 +14,11 @@
 
 package runtime
 
+import (
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/internal/core/adt"
+)
+
 // A Runtime maintains data structures for indexing and resuse for evaluation.
 type Runtime struct {
 	*index
@@ -25,3 +30,7 @@
 		index: newIndex(sharedIndex),
 	}
 }
+
+func (x *Runtime) LoadImport(importPath string) (*adt.Vertex, errors.Error) {
+	return nil, nil
+}