1package main
2
3type Thing struct {
4	name string
5}
6
7type ThingOption func(*Thing)
8
9func WithName(name string) ThingOption {
10	return func(t *Thing) {
11		t.name = name
12	}
13}
14
15func NewThing(opts ...ThingOption) *Thing {
16	t := &Thing{}
17	for _, opt := range opts {
18		opt(t)
19	}
20	return t
21}
22
23func (t Thing) String() string {
24	return t.name
25}
26
27func (t Thing) Print(arg string) {
28	println("Thing.Print:", t.name, "arg:", arg)
29}
30
31type Printer interface {
32	Print(string)
33}
34
35func main() {
36	thing := &Thing{"foo"}
37
38	// function pointers
39	runFunc(hello, 5) // must be indirect to avoid obvious inlining
40
41	// deferred functions
42	testDefer()
43
44	// defers in loop
45	testDeferLoop()
46
47	//defer func variable call
48	testDeferFuncVar()
49
50	//More complicated func variable call
51	testMultiFuncVar()
52
53	// Take a bound method and use it as a function pointer.
54	// This function pointer needs a context pointer.
55	testBound(thing.String)
56
57	// closures
58	func() {
59		println("thing inside closure:", thing.String())
60	}()
61	runFunc(func(i int) {
62		println("inside fp closure:", thing.String(), i)
63	}, 3)
64
65	// functional arguments
66	thingFunctionalArgs1 := NewThing()
67	thingFunctionalArgs1.Print("functional args 1")
68	thingFunctionalArgs2 := NewThing(WithName("named thing"))
69	thingFunctionalArgs2.Print("functional args 2")
70
71	// regression testing
72	regression1033()
73
74	//Test deferred builtins
75	testDeferBuiltin()
76}
77
78func runFunc(f func(int), arg int) {
79	f(arg)
80}
81
82func hello(n int) {
83	println("hello from function pointer:", n)
84}
85
86func testDefer() {
87	defer exportedDefer()
88
89	i := 1
90	defer deferred("...run as defer", i)
91	i++
92	defer func() {
93		println("...run closure deferred:", i)
94	}()
95	i++
96	defer deferred("...run as defer", i)
97	i++
98
99	var t Printer = &Thing{"foo"}
100	defer t.Print("bar")
101
102	println("deferring...")
103	d := dumb{}
104	defer d.Value(0)
105}
106
107func testDeferLoop() {
108	for j := 0; j < 4; j++ {
109		defer deferred("loop", j)
110	}
111}
112
113func testDeferFuncVar() {
114	dummy, f := deferFunc()
115	dummy++
116	defer f(1)
117}
118
119func testMultiFuncVar() {
120	f := multiFuncDefer()
121	defer f(1)
122}
123
124func testDeferBuiltin() {
125	i := make(chan int)
126	defer close(i)
127}
128
129type dumb struct {
130
131}
132
133func (*dumb) Value(key interface{}) interface{} {
134	return nil
135}
136
137func deferred(msg string, i int) {
138	println(msg, i)
139}
140
141//export __exportedDefer
142func exportedDefer() {
143	println("...exported defer")
144}
145
146func deferFunc() (int, func(int)) {
147	return 0, func(i int){println("...extracted defer func ", i)}
148}
149
150func multiFuncDefer() func(int) {
151	i := 0
152
153	if i > 0 {
154		return func(i int){println("Should not have gotten here. i = ", i)}
155	}
156
157	return func(i int){println("Called the correct function. i = ", i)}
158}
159
160func testBound(f func() string) {
161	println("bound method:", f())
162}
163
164// regression1033 is a regression test for https://github.com/tinygo-org/tinygo/issues/1033.
165// In previous versions of the compiler, a deferred call to an interface would create an instruction that did not dominate its uses.
166func regression1033() {
167	foo(&Bar{})
168}
169
170type Bar struct {
171	empty bool
172}
173
174func (b *Bar) Close() error {
175	return nil
176}
177
178type Closer interface {
179	Close() error
180}
181
182func foo(bar *Bar) error {
183	var a int
184	if !bar.empty {
185		a = 10
186		if a != 5 {
187			return nil
188		}
189	}
190
191	var c Closer = bar
192	defer c.Close()
193
194	return nil
195}
196