xref: /aosp_15_r20/external/starlark-go/starlark/testdata/function.star (revision 4947cdc739c985f6d86941e22894f5cefe7c9e9a)
1*4947cdc7SCole Faust# Tests of Starlark 'function'
2*4947cdc7SCole Faust# option:set
3*4947cdc7SCole Faust
4*4947cdc7SCole Faust# TODO(adonovan):
5*4947cdc7SCole Faust# - add some introspection functions for looking at function values
6*4947cdc7SCole Faust#   and test that functions have correct position, free vars, names of locals, etc.
7*4947cdc7SCole Faust# - move the hard-coded tests of parameter passing from eval_test.go to here.
8*4947cdc7SCole Faust
9*4947cdc7SCole Faustload("assert.star", "assert", "freeze")
10*4947cdc7SCole Faust
11*4947cdc7SCole Faust# Test lexical scope and closures:
12*4947cdc7SCole Faustdef outer(x):
13*4947cdc7SCole Faust   def inner(y):
14*4947cdc7SCole Faust     return x + x + y # multiple occurrences of x should create only 1 freevar
15*4947cdc7SCole Faust   return inner
16*4947cdc7SCole Faust
17*4947cdc7SCole Faustz = outer(3)
18*4947cdc7SCole Faustassert.eq(z(5), 11)
19*4947cdc7SCole Faustassert.eq(z(7), 13)
20*4947cdc7SCole Faustz2 = outer(4)
21*4947cdc7SCole Faustassert.eq(z2(5), 13)
22*4947cdc7SCole Faustassert.eq(z2(7), 15)
23*4947cdc7SCole Faustassert.eq(z(5), 11)
24*4947cdc7SCole Faustassert.eq(z(7), 13)
25*4947cdc7SCole Faust
26*4947cdc7SCole Faust# Function name
27*4947cdc7SCole Faustassert.eq(str(outer), '<function outer>')
28*4947cdc7SCole Faustassert.eq(str(z), '<function inner>')
29*4947cdc7SCole Faustassert.eq(str(str), '<built-in function str>')
30*4947cdc7SCole Faustassert.eq(str("".startswith), '<built-in method startswith of string value>')
31*4947cdc7SCole Faust
32*4947cdc7SCole Faust# Stateful closure
33*4947cdc7SCole Faustdef squares():
34*4947cdc7SCole Faust    x = [0]
35*4947cdc7SCole Faust    def f():
36*4947cdc7SCole Faust      x[0] += 1
37*4947cdc7SCole Faust      return x[0] * x[0]
38*4947cdc7SCole Faust    return f
39*4947cdc7SCole Faust
40*4947cdc7SCole Faustsq = squares()
41*4947cdc7SCole Faustassert.eq(sq(), 1)
42*4947cdc7SCole Faustassert.eq(sq(), 4)
43*4947cdc7SCole Faustassert.eq(sq(), 9)
44*4947cdc7SCole Faustassert.eq(sq(), 16)
45*4947cdc7SCole Faust
46*4947cdc7SCole Faust# Freezing a closure
47*4947cdc7SCole Faustsq2 = freeze(sq)
48*4947cdc7SCole Faustassert.fails(sq2, "frozen list")
49*4947cdc7SCole Faust
50*4947cdc7SCole Faust# recursion detection, simple
51*4947cdc7SCole Faustdef fib(x):
52*4947cdc7SCole Faust  if x < 2:
53*4947cdc7SCole Faust    return x
54*4947cdc7SCole Faust  return fib(x-2) + fib(x-1)
55*4947cdc7SCole Faustassert.fails(lambda: fib(10), "function fib called recursively")
56*4947cdc7SCole Faust
57*4947cdc7SCole Faust# recursion detection, advanced
58*4947cdc7SCole Faust#
59*4947cdc7SCole Faust# A simplistic recursion check that looks for repeated calls to the
60*4947cdc7SCole Faust# same function value will not detect recursion using the Y
61*4947cdc7SCole Faust# combinator, which creates a new closure at each step of the
62*4947cdc7SCole Faust# recursion.  To truly prohibit recursion, the dynamic check must look
63*4947cdc7SCole Faust# for repeated calls of the same syntactic function body.
64*4947cdc7SCole FaustY = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))
65*4947cdc7SCole Faustfibgen = lambda fib: lambda x: (x if x<2 else fib(x-1)+fib(x-2))
66*4947cdc7SCole Faustfib2 = Y(fibgen)
67*4947cdc7SCole Faustassert.fails(lambda: [fib2(x) for x in range(10)], "function lambda called recursively")
68*4947cdc7SCole Faust
69*4947cdc7SCole Faust# However, this stricter check outlaws many useful programs
70*4947cdc7SCole Faust# that are still bounded, and creates a hazard because
71*4947cdc7SCole Faust# helper functions such as map below cannot be used to
72*4947cdc7SCole Faust# call functions that themselves use map:
73*4947cdc7SCole Faustdef map(f, seq): return [f(x) for x in seq]
74*4947cdc7SCole Faustdef double(x): return x+x
75*4947cdc7SCole Faustassert.eq(map(double, [1, 2, 3]), [2, 4, 6])
76*4947cdc7SCole Faustassert.eq(map(double, ["a", "b", "c"]), ["aa", "bb", "cc"])
77*4947cdc7SCole Faustdef mapdouble(x): return map(double, x)
78*4947cdc7SCole Faustassert.fails(lambda: map(mapdouble, ([1, 2, 3], ["a", "b", "c"])),
79*4947cdc7SCole Faust             'function map called recursively')
80*4947cdc7SCole Faust# With the -recursion option it would yield [[2, 4, 6], ["aa", "bb", "cc"]].
81*4947cdc7SCole Faust
82*4947cdc7SCole Faust# call of function not through its name
83*4947cdc7SCole Faust# (regression test for parsing suffixes of primary expressions)
84*4947cdc7SCole Fausthf = hasfields()
85*4947cdc7SCole Fausthf.x = [len]
86*4947cdc7SCole Faustassert.eq(hf.x[0]("abc"), 3)
87*4947cdc7SCole Faustdef f():
88*4947cdc7SCole Faust   return lambda: 1
89*4947cdc7SCole Faustassert.eq(f()(), 1)
90*4947cdc7SCole Faustassert.eq(["abc"][0][0].upper(), "A")
91*4947cdc7SCole Faust
92*4947cdc7SCole Faust# functions may be recursively defined,
93*4947cdc7SCole Faust# so long as they don't dynamically recur.
94*4947cdc7SCole Faustcalls = []
95*4947cdc7SCole Faustdef yin(x):
96*4947cdc7SCole Faust  calls.append("yin")
97*4947cdc7SCole Faust  if x:
98*4947cdc7SCole Faust    yang(False)
99*4947cdc7SCole Faust
100*4947cdc7SCole Faustdef yang(x):
101*4947cdc7SCole Faust  calls.append("yang")
102*4947cdc7SCole Faust  if x:
103*4947cdc7SCole Faust    yin(False)
104*4947cdc7SCole Faust
105*4947cdc7SCole Faustyin(True)
106*4947cdc7SCole Faustassert.eq(calls, ["yin", "yang"])
107*4947cdc7SCole Faust
108*4947cdc7SCole Faustcalls.clear()
109*4947cdc7SCole Faustyang(True)
110*4947cdc7SCole Faustassert.eq(calls, ["yang", "yin"])
111*4947cdc7SCole Faust
112*4947cdc7SCole Faust
113*4947cdc7SCole Faust# builtin_function_or_method use identity equivalence.
114*4947cdc7SCole Faustclosures = set(["".count for _ in range(10)])
115*4947cdc7SCole Faustassert.eq(len(closures), 10)
116*4947cdc7SCole Faust
117*4947cdc7SCole Faust---
118*4947cdc7SCole Faust# Default values of function parameters are mutable.
119*4947cdc7SCole Faustload("assert.star", "assert", "freeze")
120*4947cdc7SCole Faust
121*4947cdc7SCole Faustdef f(x=[0]):
122*4947cdc7SCole Faust  return x
123*4947cdc7SCole Faust
124*4947cdc7SCole Faustassert.eq(f(), [0])
125*4947cdc7SCole Faust
126*4947cdc7SCole Faustf().append(1)
127*4947cdc7SCole Faustassert.eq(f(), [0, 1])
128*4947cdc7SCole Faust
129*4947cdc7SCole Faust# Freezing a function value freezes its parameter defaults.
130*4947cdc7SCole Faustfreeze(f)
131*4947cdc7SCole Faustassert.fails(lambda: f().append(2), "cannot append to frozen list")
132*4947cdc7SCole Faust
133*4947cdc7SCole Faust---
134*4947cdc7SCole Faust# This is a well known corner case of parsing in Python.
135*4947cdc7SCole Faustload("assert.star", "assert")
136*4947cdc7SCole Faust
137*4947cdc7SCole Faustf = lambda x: 1 if x else 0
138*4947cdc7SCole Faustassert.eq(f(True), 1)
139*4947cdc7SCole Faustassert.eq(f(False), 0)
140*4947cdc7SCole Faust
141*4947cdc7SCole Faustx = True
142*4947cdc7SCole Faustf2 = (lambda x: 1) if x else 0
143*4947cdc7SCole Faustassert.eq(f2(123), 1)
144*4947cdc7SCole Faust
145*4947cdc7SCole Fausttf = lambda: True, lambda: False
146*4947cdc7SCole Faustassert.true(tf[0]())
147*4947cdc7SCole Faustassert.true(not tf[1]())
148*4947cdc7SCole Faust
149*4947cdc7SCole Faust---
150*4947cdc7SCole Faust# Missing parameters are correctly reported
151*4947cdc7SCole Faust# in functions of more than 64 parameters.
152*4947cdc7SCole Faust# (This tests a corner case of the implementation:
153*4947cdc7SCole Faust# we avoid a map allocation for <64 parameters)
154*4947cdc7SCole Faust
155*4947cdc7SCole Faustload("assert.star", "assert")
156*4947cdc7SCole Faust
157*4947cdc7SCole Faustdef f(a, b, c, d, e, f, g, h,
158*4947cdc7SCole Faust      i, j, k, l, m, n, o, p,
159*4947cdc7SCole Faust      q, r, s, t, u, v, w, x,
160*4947cdc7SCole Faust      y, z, A, B, C, D, E, F,
161*4947cdc7SCole Faust      G, H, I, J, K, L, M, N,
162*4947cdc7SCole Faust      O, P, Q, R, S, T, U, V,
163*4947cdc7SCole Faust      W, X, Y, Z, aa, bb, cc, dd,
164*4947cdc7SCole Faust      ee, ff, gg, hh, ii, jj, kk, ll,
165*4947cdc7SCole Faust      mm):
166*4947cdc7SCole Faust  pass
167*4947cdc7SCole Faust
168*4947cdc7SCole Faustassert.fails(lambda: f(
169*4947cdc7SCole Faust    1, 2, 3, 4, 5, 6, 7, 8,
170*4947cdc7SCole Faust    9, 10, 11, 12, 13, 14, 15, 16,
171*4947cdc7SCole Faust    17, 18, 19, 20, 21, 22, 23, 24,
172*4947cdc7SCole Faust    25, 26, 27, 28, 29, 30, 31, 32,
173*4947cdc7SCole Faust    33, 34, 35, 36, 37, 38, 39, 40,
174*4947cdc7SCole Faust    41, 42, 43, 44, 45, 46, 47, 48,
175*4947cdc7SCole Faust    49, 50, 51, 52, 53, 54, 55, 56,
176*4947cdc7SCole Faust    57, 58, 59, 60, 61, 62, 63, 64), "missing 1 argument \\(mm\\)")
177*4947cdc7SCole Faust
178*4947cdc7SCole Faustassert.fails(lambda: f(
179*4947cdc7SCole Faust    1, 2, 3, 4, 5, 6, 7, 8,
180*4947cdc7SCole Faust    9, 10, 11, 12, 13, 14, 15, 16,
181*4947cdc7SCole Faust    17, 18, 19, 20, 21, 22, 23, 24,
182*4947cdc7SCole Faust    25, 26, 27, 28, 29, 30, 31, 32,
183*4947cdc7SCole Faust    33, 34, 35, 36, 37, 38, 39, 40,
184*4947cdc7SCole Faust    41, 42, 43, 44, 45, 46, 47, 48,
185*4947cdc7SCole Faust    49, 50, 51, 52, 53, 54, 55, 56,
186*4947cdc7SCole Faust    57, 58, 59, 60, 61, 62, 63, 64, 65,
187*4947cdc7SCole Faust    mm = 100), 'multiple values for parameter "mm"')
188*4947cdc7SCole Faust
189*4947cdc7SCole Faust---
190*4947cdc7SCole Faust# Regression test for github.com/google/starlark-go/issues/21,
191*4947cdc7SCole Faust# which concerns dynamic checks.
192*4947cdc7SCole Faust# Related: https://github.com/bazelbuild/starlark/issues/21,
193*4947cdc7SCole Faust# which concerns static checks.
194*4947cdc7SCole Faust
195*4947cdc7SCole Faustload("assert.star", "assert")
196*4947cdc7SCole Faust
197*4947cdc7SCole Faustdef f(*args, **kwargs):
198*4947cdc7SCole Faust  return args, kwargs
199*4947cdc7SCole Faust
200*4947cdc7SCole Faustassert.eq(f(x=1, y=2), ((), {"x": 1, "y": 2}))
201*4947cdc7SCole Faustassert.fails(lambda: f(x=1, **dict(x=2)), 'multiple values for parameter "x"')
202*4947cdc7SCole Faust
203*4947cdc7SCole Faustdef g(x, y):
204*4947cdc7SCole Faust  return x, y
205*4947cdc7SCole Faust
206*4947cdc7SCole Faustassert.eq(g(1, y=2), (1, 2))
207*4947cdc7SCole Faustassert.fails(lambda: g(1, y=2, **{'y': 3}), 'multiple values for parameter "y"')
208*4947cdc7SCole Faust
209*4947cdc7SCole Faust---
210*4947cdc7SCole Faust# Regression test for a bug in CALL_VAR_KW.
211*4947cdc7SCole Faust
212*4947cdc7SCole Faustload("assert.star", "assert")
213*4947cdc7SCole Faust
214*4947cdc7SCole Faustdef f(a, b, x, y):
215*4947cdc7SCole Faust  return a+b+x+y
216*4947cdc7SCole Faust
217*4947cdc7SCole Faustassert.eq(f(*("a", "b"), **dict(y="y", x="x")) + ".", 'abxy.')
218*4947cdc7SCole Faust---
219*4947cdc7SCole Faust# Order of evaluation of function arguments.
220*4947cdc7SCole Faust# Regression test for github.com/google/skylark/issues/135.
221*4947cdc7SCole Faustload("assert.star", "assert")
222*4947cdc7SCole Faust
223*4947cdc7SCole Faustr = []
224*4947cdc7SCole Faust
225*4947cdc7SCole Faustdef id(x):
226*4947cdc7SCole Faust  r.append(x)
227*4947cdc7SCole Faust  return x
228*4947cdc7SCole Faust
229*4947cdc7SCole Faustdef f(*args, **kwargs):
230*4947cdc7SCole Faust  return (args, kwargs)
231*4947cdc7SCole Faust
232*4947cdc7SCole Fausty = f(id(1), id(2), x=id(3), *[id(4)], **dict(z=id(5)))
233*4947cdc7SCole Faustassert.eq(y, ((1, 2, 4), dict(x=3, z=5)))
234*4947cdc7SCole Faust
235*4947cdc7SCole Faust# This matches Python2 and Starlark-in-Java, but not Python3 [1 2 4 3 6].
236*4947cdc7SCole Faust# *args and *kwargs are evaluated last.
237*4947cdc7SCole Faust# (Python[23] also allows keyword arguments after *args.)
238*4947cdc7SCole Faust# See github.com/bazelbuild/starlark#13 for spec change.
239*4947cdc7SCole Faustassert.eq(r, [1, 2, 3, 4, 5])
240*4947cdc7SCole Faust
241*4947cdc7SCole Faust---
242*4947cdc7SCole Faust# option:recursion
243*4947cdc7SCole Faust# See github.com/bazelbuild/starlark#170
244*4947cdc7SCole Faustload("assert.star", "assert")
245*4947cdc7SCole Faust
246*4947cdc7SCole Faustdef a():
247*4947cdc7SCole Faust    list = []
248*4947cdc7SCole Faust    def b(n):
249*4947cdc7SCole Faust        list.append(n)
250*4947cdc7SCole Faust        if n > 0:
251*4947cdc7SCole Faust            b(n - 1) # recursive reference to b
252*4947cdc7SCole Faust
253*4947cdc7SCole Faust    b(3)
254*4947cdc7SCole Faust    return list
255*4947cdc7SCole Faust
256*4947cdc7SCole Faustassert.eq(a(), [3, 2, 1, 0])
257*4947cdc7SCole Faust
258*4947cdc7SCole Faustdef c():
259*4947cdc7SCole Faust    list = []
260*4947cdc7SCole Faust    x = 1
261*4947cdc7SCole Faust    def d():
262*4947cdc7SCole Faust      list.append(x) # this use of x observes both assignments
263*4947cdc7SCole Faust    d()
264*4947cdc7SCole Faust    x = 2
265*4947cdc7SCole Faust    d()
266*4947cdc7SCole Faust    return list
267*4947cdc7SCole Faust
268*4947cdc7SCole Faustassert.eq(c(), [1, 2])
269*4947cdc7SCole Faust
270*4947cdc7SCole Faustdef e():
271*4947cdc7SCole Faust    def f():
272*4947cdc7SCole Faust      return x # forward reference ok: x is a closure cell
273*4947cdc7SCole Faust    x = 1
274*4947cdc7SCole Faust    return f()
275*4947cdc7SCole Faust
276*4947cdc7SCole Faustassert.eq(e(), 1)
277*4947cdc7SCole Faust
278*4947cdc7SCole Faust---
279*4947cdc7SCole Faustload("assert.star", "assert")
280*4947cdc7SCole Faust
281*4947cdc7SCole Faustdef e():
282*4947cdc7SCole Faust    x = 1
283*4947cdc7SCole Faust    def f():
284*4947cdc7SCole Faust      print(x) # this reference to x fails
285*4947cdc7SCole Faust      x = 3    # because this assignment makes x local to f
286*4947cdc7SCole Faust    f()
287*4947cdc7SCole Faust
288*4947cdc7SCole Faustassert.fails(e, "local variable x referenced before assignment")
289*4947cdc7SCole Faust
290*4947cdc7SCole Faustdef f():
291*4947cdc7SCole Faust    def inner():
292*4947cdc7SCole Faust        return x
293*4947cdc7SCole Faust    if False:
294*4947cdc7SCole Faust        x = 0
295*4947cdc7SCole Faust    return x # fails (x is an uninitialized cell of this function)
296*4947cdc7SCole Faust
297*4947cdc7SCole Faustassert.fails(f, "local variable x referenced before assignment")
298*4947cdc7SCole Faust
299*4947cdc7SCole Faustdef g():
300*4947cdc7SCole Faust    def inner():
301*4947cdc7SCole Faust        return x # fails (x is an uninitialized cell of the enclosing function)
302*4947cdc7SCole Faust    if False:
303*4947cdc7SCole Faust        x = 0
304*4947cdc7SCole Faust    return inner()
305*4947cdc7SCole Faust
306*4947cdc7SCole Faustassert.fails(g, "local variable x referenced before assignment")
307*4947cdc7SCole Faust
308*4947cdc7SCole Faust---
309*4947cdc7SCole Faust# A trailing comma is allowed in any function definition or call.
310*4947cdc7SCole Faust# This reduces the need to edit neighboring lines when editing defs
311*4947cdc7SCole Faust# or calls splayed across multiple lines.
312*4947cdc7SCole Faust
313*4947cdc7SCole Faustdef a(x,): pass
314*4947cdc7SCole Faustdef b(x, y=None, ): pass
315*4947cdc7SCole Faustdef c(x, y=None, *args, ): pass
316*4947cdc7SCole Faustdef d(x, y=None, *args, z=None, ): pass
317*4947cdc7SCole Faustdef e(x, y=None, *args, z=None, **kwargs, ): pass
318*4947cdc7SCole Faust
319*4947cdc7SCole Fausta(1,)
320*4947cdc7SCole Faustb(1, y=2, )
321*4947cdc7SCole Faust#c(1, *[], )
322*4947cdc7SCole Faust#d(1, *[], z=None, )
323*4947cdc7SCole Faust#e(1, *[], z=None, *{}, )
324