Skip to content

Commit 87ca9c0

Browse files
mpvlcueckoo
authored andcommitted
internal/core/compile: add version check for builtins
Right now we only do this for predeclared identifiers. Note, however, that by taking the Runtime, instead of an Indexer, we can lookup stdlib packages and implement the same for stdlib calls. Issue #291 Issue #943 Signed-off-by: Marcel van Lohuizen <[email protected]> Change-Id: I667b713fbb0808ced0cbc76445083b097edfa6da Dispatch-Trailer: {"type":"trybot","CL":1217324,"patchset":2,"ref":"refs/changes/24/1217324/2","targetBranch":"master"}
1 parent 0984f67 commit 87ca9c0

File tree

6 files changed

+67
-3
lines changed

6 files changed

+67
-3
lines changed

cue/testdata/builtins/error.txtar

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
-- cue.mod/module.cue --
2+
module: "cuelang.org/error"
3+
language: version: "v0.14.0"
14
-- in.cue --
25
selection: {
36
dropError: 1 | error("drop me")
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
-- cue.mod/module.cue --
2+
module: "cuelang.org/go/internal/core/adt/eval_test"
3+
language: version: "v0.8.0"
4+
-- in.cue --
5+
// error is not yet available in v0.8.0.
6+
a: error("msg")
7+
-- out/eval --
8+
a: builtin "error" is not available in version v0.8.0; it was added in version "v0.14.0":
9+
./in.cue:2:4
10+
-- out/compile --
11+
a: builtin "error" is not available in version v0.8.0; it was added in version "v0.14.0":
12+
./in.cue:2:4
13+
--- in.cue
14+
{
15+
a: _|_(builtin "error" is not available in version v0.8.0; it was added in version "v0.14.0")("msg")
16+
}

internal/core/adt/expr.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1669,6 +1669,9 @@ type Builtin struct {
16691669
// TODO: consider merging Func and RawFunc into a single field again.
16701670
RawFunc func(call *CallContext) Value
16711671

1672+
// Added indicates as of which language version this builtin can be used.
1673+
Added string
1674+
16721675
Package Feature
16731676
Name string
16741677
}

internal/core/compile/builtin.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ var (
3838
// message. If the argument is an interpolation, it will be evaluated and if it
3939
// results in an error, the argument will be inserted as an expression.
4040
var errorBuiltin = &adt.Builtin{
41-
Name: "error",
41+
Name: "error",
42+
Added: "v0.14.0",
43+
4244
Params: []adt.Param{stringParam},
4345
Result: adt.BottomKind,
4446
RawFunc: func(call *adt.CallContext) adt.Value {

internal/core/compile/compile.go

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"cuelang.org/go/internal/astinternal"
2626
"cuelang.org/go/internal/core/adt"
2727
"cuelang.org/go/internal/cueexperiment"
28+
"cuelang.org/go/internal/mod/semver"
2829
)
2930

3031
// A Scope represents a nested scope of Vertices.
@@ -333,6 +334,34 @@ func (c *compiler) compileExpr(x ast.Expr) adt.Conjunct {
333334
return adt.MakeRootConjunct(env, expr)
334335
}
335336

337+
// verifyVersion checks whether n is a Builtin and then checks whether the
338+
// Added version is compatible with the file version registered in c.
339+
func (c *compiler) verifyVersion(src ast.Node, n adt.Expr) adt.Expr {
340+
b, ok := n.(*adt.Builtin)
341+
if !ok {
342+
return n
343+
}
344+
if b.Added == "" {
345+
// No version check needed.
346+
return n
347+
}
348+
v := c.experiments.LanguageVersion()
349+
if v == "" {
350+
// We assume "latest" if the file is not associated with a version.
351+
return n
352+
}
353+
354+
if semver.Compare(b.Added, v) <= 0 {
355+
// The feature is available in the file version.
356+
return n
357+
}
358+
359+
// The feature is not available in the file version.
360+
// NonConcrete builtins are not allowed in older versions.
361+
return c.errf(src, "builtin %q is not available in version %v; "+
362+
"it was added in version %q", b.Name, v, b.Added)
363+
}
364+
336365
// resolve assumes that all existing resolutions are legal. Validation should
337366
// be done in a separate step if required.
338367
//
@@ -396,7 +425,7 @@ func (c *compiler) resolve(n *ast.Ident) adt.Expr {
396425
}
397426

398427
if p := predeclared(n); p != nil {
399-
return p
428+
return c.verifyVersion(n, p)
400429
}
401430

402431
return c.errf(n, "reference %q not found", n.Name)
@@ -902,9 +931,14 @@ func (c *compiler) expr(expr ast.Expr) adt.Expr {
902931

903932
case *ast.SelectorExpr:
904933
c.inSelector++
934+
x := c.expr(n.X)
935+
// TODO: check if x is an ImportReference, and if so, check if it a
936+
// standard library, look up the builtin, and check its version. The
937+
// index of standard libraries is available in c.index, which is really
938+
// an adt.Runtime under the hood.
905939
ret := &adt.SelectorExpr{
906940
Src: n,
907-
X: c.expr(n.X),
941+
X: x,
908942
Sel: c.label(n.Sel)}
909943
c.inSelector--
910944
return ret

internal/cueexperiment/file.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ type File struct {
5353
StructCmp bool `experiment:"since:v0.14.0"`
5454
}
5555

56+
// LanguageVersion returns the language version of the file or "" if no language
57+
// version is associated with it.
58+
func (f *File) LanguageVersion() string {
59+
return f.version
60+
}
61+
5662
// NewFile parses the given comma-separated list of experiments for
5763
// the given version and returns a PerFile struct with the experiments enabled.
5864
// A empty version indicates the default version.

0 commit comments

Comments
 (0)