stash1-createFunctionTC.patch raw
1 diff --git a/compiler/calls.go b/compiler/calls.go
2 index df0cff15..ba847f71 100644
3 --- a/compiler/calls.go
4 +++ b/compiler/calls.go
5 @@ -79,6 +79,17 @@ func (b *builder) createCall(fnType llvm.Type, fn llvm.Value, args []llvm.Value,
6 fragments := b.expandFormalParam(arg)
7 expanded = append(expanded, fragments...)
8 }
9 + // Coerce named↔anonymous struct mismatches (TC path may produce
10 + // anonymous struct types where the callee expects named ones).
11 + if paramTypes := fnType.ParamTypes(); len(paramTypes) == len(expanded) {
12 + for i, pt := range paramTypes {
13 + if expanded[i].Type() != pt &&
14 + pt.TypeKind() == llvm.StructTypeKind &&
15 + expanded[i].Type().TypeKind() == llvm.StructTypeKind {
16 + expanded[i] = b.coerceStructType(expanded[i], pt)
17 + }
18 + }
19 + }
20 call := b.CreateCall(fnType, fn, expanded, name)
21 if !fn.IsAFunction().IsNil() {
22 if cc := fn.FunctionCallConv(); cc != llvm.CCallConv {
23 diff --git a/compiler/compiler.go b/compiler/compiler.go
24 index f34d5cc8..41424880 100644
25 --- a/compiler/compiler.go
26 +++ b/compiler/compiler.go
27 @@ -1379,6 +1379,25 @@ func (b *builder) createFunctionStart(intrinsic bool) {
28 // because runtime.trackPointer is replaced by an alloca store.
29 b.stackChainAlloca = b.CreateAlloca(b.ctx.Int8Type(), "stackalloc")
30 }
31 +
32 + // Bridge ssa locals → mxssa locals so getValueTC works for intrinsics
33 + // and any other path that calls createFunctionStart directly.
34 + if b.mxFn != nil {
35 + for i, ssaParam := range b.fn.Params {
36 + if i < len(b.mxFn.Params) {
37 + if val, ok := b.locals[ssaParam]; ok {
38 + b.mxLocals[b.mxFn.Params[i]] = val
39 + }
40 + }
41 + }
42 + for i, ssaFV := range b.fn.FreeVars {
43 + if i < len(b.mxFn.FreeVars) {
44 + if val, ok := b.locals[ssaFV]; ok {
45 + b.mxLocals[b.mxFn.FreeVars[i]] = val
46 + }
47 + }
48 + }
49 + }
50 }
51
52 // createFunction builds the LLVM IR implementation for this function. The
53 @@ -1492,27 +1511,17 @@ func (b *builder) createFunction() {
54 }
55
56 func (b *builder) createFunctionTC() {
57 + if len(b.mxFn.Blocks) == 0 {
58 + b.createFunction()
59 + return
60 + }
61 + if mxFnHasInvalidTypes(b.mxFn) {
62 + b.createFunction()
63 + return
64 + }
65 b.createFunctionStart(false)
66 b.checkMoxieRestrictions()
67
68 - // Bridge parameters: ssa locals → mxssa locals.
69 - if b.mxFn != nil {
70 - for i, ssaParam := range b.fn.Params {
71 - if i < len(b.mxFn.Params) {
72 - if val, ok := b.locals[ssaParam]; ok {
73 - b.mxLocals[b.mxFn.Params[i]] = val
74 - }
75 - }
76 - }
77 - for i, ssaFV := range b.fn.FreeVars {
78 - if i < len(b.mxFn.FreeVars) {
79 - if val, ok := b.locals[ssaFV]; ok {
80 - b.mxLocals[b.mxFn.FreeVars[i]] = val
81 - }
82 - }
83 - }
84 - }
85 -
86 for _, block := range b.fn.DomPreorder() {
87 if b.DumpSSA {
88 fmt.Printf("%d: %s:\n", block.Index, block.Comment)
89 @@ -1784,7 +1793,9 @@ func (b *builder) createInstructionTC(instr mxssa.Instruction) {
90 if len(instr.Results) == 0 {
91 b.CreateRetVoid()
92 } else if len(instr.Results) == 1 {
93 - b.CreateRet(b.getValueTC(instr.Results[0], instr.Pos()))
94 + val := b.getValueTC(instr.Results[0], instr.Pos())
95 + val = b.fixReturnType(val)
96 + b.CreateRet(val)
97 } else {
98 retVal := llvm.ConstNull(b.llvmFn.GlobalValueType().ReturnType())
99 for i, result := range instr.Results {
100 @@ -4938,9 +4949,8 @@ func (b *builder) createUnOpTC(unop *mxssa.UnOp) (llvm.Value, error) {
101 valueType := b.getLLVMTypeTC(unop.X.Type().Underlying().(*typecheck.Pointer).Elem())
102 if b.targetData.TypeAllocSize(valueType) == 0 {
103 return llvm.ConstNull(valueType), nil
104 - } else if strings.HasSuffix(unop.X.String(), "$funcaddr") {
105 - // CGo function pointer — Moxie has no CGo, so this is dead code.
106 - name := strings.TrimSuffix(unop.X.(*mxssa.Global).Name(), "$funcaddr")
107 + } else if g, ok := unop.X.(*mxssa.Global); ok && strings.HasSuffix(g.Name(), "$funcaddr") {
108 + name := strings.TrimSuffix(g.Name(), "$funcaddr")
109 pkg := b.mxFn.Pkg
110 fn := pkg.Members[name].(*mxssa.Function)
111 goFn := b.mxTranslator.ReverseFunction(fn)
112 diff --git a/compiler/tccompat.go b/compiler/tccompat.go
113 index 05724261..5c8988cc 100644
114 --- a/compiler/tccompat.go
115 +++ b/compiler/tccompat.go
116 @@ -122,16 +122,26 @@ func (c *compilerContext) getLLVMFunctionTypeTC(typ *typecheck.Signature) llvm.T
117
118 var paramTypes []llvm.Type
119 if recv := typ.Recv(); recv != nil {
120 - paramTypes = append(paramTypes, c.getLLVMTypeTC(recv.Type()))
121 + recv := c.getLLVMTypeTC(recv.Type())
122 + if recv.StructName() == "runtime._interface" {
123 + recv = c.dataPtrType
124 + }
125 + for _, info := range c.expandFormalParamType(recv, "", nil) {
126 + paramTypes = append(paramTypes, info.llvmType)
127 + }
128 }
129 params := typ.Params()
130 if params != nil {
131 for i := 0; i < params.Len(); i++ {
132 - paramTypes = append(paramTypes, c.getLLVMTypeTC(params.At(i).Type()))
133 + subType := c.getLLVMTypeTC(params.At(i).Type())
134 + for _, info := range c.expandFormalParamType(subType, "", nil) {
135 + paramTypes = append(paramTypes, info.llvmType)
136 + }
137 }
138 }
139 + paramTypes = append(paramTypes, c.dataPtrType) // context
140
141 - return llvm.FunctionType(returnType, paramTypes, typ.Variadic())
142 + return llvm.FunctionType(returnType, paramTypes, false)
143 }
144
145 // isPointerTC checks whether a typecheck.Type is a pointer type.
146 @@ -316,15 +326,31 @@ func (b *builder) getValueTC(expr mxssa.Value, pos token.Pos) llvm.Value {
147 }
148 }
149 return b.createConstTC(expr, pos)
150 + case *mxssa.Function:
151 + goFn := b.mxTranslator.ReverseFunction(expr)
152 + if goFn == nil {
153 + b.addError(expr.Pos(), "cannot reverse-bridge function: "+expr.Name())
154 + return llvm.Undef(b.getLLVMTypeTC(expr.Type()))
155 + }
156 + if b.getFunctionInfo(goFn).exported {
157 + b.addError(expr.Pos(), "cannot use an exported function as value: "+expr.String())
158 + return llvm.Undef(b.getLLVMTypeTC(expr.Type()))
159 + }
160 + _, fn := b.getFunction(goFn)
161 + return b.createFuncValue(fn, llvm.Undef(b.dataPtrType), goFn.Signature)
162 case *mxssa.Global:
163 - // Look up via the old path: find the corresponding ssa.Global and delegate.
164 - if b.fn != nil {
165 - for _, m := range b.fn.Package().Members {
166 - if g, ok := m.(*ssa.Global); ok && g.Name() == expr.Name() {
167 - value := b.getGlobal(g)
168 - if !value.IsNil() {
169 - return value
170 - }
171 + pkgPath := ""
172 + if expr.Package() != nil {
173 + pkgPath = expr.Package().Pkg.Path()
174 + }
175 + for _, p := range b.fn.Prog.AllPackages() {
176 + if pkgPath != "" && p.Pkg.Path() != pkgPath {
177 + continue
178 + }
179 + if g, ok := p.Members[expr.Name()].(*ssa.Global); ok {
180 + value := b.getGlobal(g)
181 + if !value.IsNil() {
182 + return value
183 }
184 }
185 }
186 @@ -343,6 +369,9 @@ func (c *compilerContext) createConstTC(expr *mxssa.Const, pos token.Pos) llvm.V
187 switch typ := expr.Type().Underlying().(type) {
188 case *typecheck.Basic:
189 llvmType := c.getLLVMTypeTC(typ)
190 + if expr.Value() == nil {
191 + return llvm.ConstNull(llvmType)
192 + }
193 if typ.Info()&typecheck.IsBoolean != 0 {
194 n := uint64(0)
195 if constant.BoolVal(expr.Value()) {
196 @@ -390,6 +419,10 @@ func (c *compilerContext) createConstTC(expr *mxssa.Const, pos token.Pos) llvm.V
197 *typecheck.Pointer, *typecheck.Array, *typecheck.Slice,
198 *typecheck.Struct, *typecheck.Map:
199 if !expr.IsNil() {
200 + switch typ.(type) {
201 + case *typecheck.Struct, *typecheck.Array:
202 + return llvm.ConstNull(c.getLLVMTypeTC(expr.Type()))
203 + }
204 panic("tccompat: expected nil constant for " + typ.String())
205 }
206 if _, ok := typ.(*typecheck.Interface); ok {
207 @@ -405,6 +438,76 @@ func (c *compilerContext) createConstTC(expr *mxssa.Const, pos token.Pos) llvm.V
208 }
209 }
210
211 +func isInvalidTC(t typecheck.Type) bool {
212 + if t == nil {
213 + return false
214 + }
215 + u := t.Underlying()
216 + if b, ok := u.(*typecheck.Basic); ok && b.Kind() == typecheck.Invalid {
217 + return true
218 + }
219 + return false
220 +}
221 +
222 +func mxFnHasInvalidTypes(fn *mxssa.Function) bool {
223 + if fn.Signature != nil {
224 + if fn.Signature.Params() != nil {
225 + for i := 0; i < fn.Signature.Params().Len(); i++ {
226 + if isInvalidTC(fn.Signature.Params().At(i).Type()) {
227 + return true
228 + }
229 + }
230 + }
231 + if fn.Signature.Results() != nil {
232 + for i := 0; i < fn.Signature.Results().Len(); i++ {
233 + if isInvalidTC(fn.Signature.Results().At(i).Type()) {
234 + return true
235 + }
236 + }
237 + }
238 + }
239 + for _, b := range fn.Blocks {
240 + for _, instr := range b.Instrs {
241 + if v, ok := instr.(mxssa.Value); ok {
242 + if isInvalidTC(v.Type()) {
243 + return true
244 + }
245 + }
246 + switch instr.(type) {
247 + case *mxssa.Defer, *mxssa.Go:
248 + return true
249 + }
250 + }
251 + }
252 + return false
253 +}
254 +
255 +// fixReturnType ensures a return value matches the function's declared return type.
256 +// TC path may produce anonymous struct types where the function expects named types.
257 +func (b *builder) fixReturnType(val llvm.Value) llvm.Value {
258 + expected := b.llvmFn.GlobalValueType().ReturnType()
259 + return b.coerceStructType(val, expected)
260 +}
261 +
262 +func (b *builder) coerceStructType(val llvm.Value, expected llvm.Type) llvm.Value {
263 + got := val.Type()
264 + if expected == got {
265 + return val
266 + }
267 + if expected.TypeKind() == llvm.StructTypeKind && got.TypeKind() == llvm.StructTypeKind {
268 + n := expected.StructElementTypesCount()
269 + if n == got.StructElementTypesCount() {
270 + result := llvm.ConstNull(expected)
271 + for i := 0; i < n; i++ {
272 + field := b.CreateExtractValue(val, i, "")
273 + result = b.CreateInsertValue(result, field, i, "")
274 + }
275 + return result
276 + }
277 + }
278 + return val
279 +}
280 +
281 // getPosTC extracts token.Pos from an mxssa value.
282 func getPosTC(v mxssa.Value) token.Pos {
283 if v == nil {
284 diff --git a/jsruntime/dom.mjs b/jsruntime/dom.mjs
285 index f1bbbcfd..e11389e8 100644
286 --- a/jsruntime/dom.mjs
287 +++ b/jsruntime/dom.mjs
288 @@ -870,21 +870,23 @@ export function OnPasteImage(elId, fn) {
289 export function OnDropImage(elId, fn) {
290 const el = _elements.get(elId);
291 if (!el) return;
292 - el.addEventListener('dragover', function(e) { e.preventDefault(); });
293 + el.addEventListener('dragenter', function(e) { e.preventDefault(); e.stopPropagation(); });
294 + el.addEventListener('dragover', function(e) { e.preventDefault(); e.stopPropagation(); });
295 el.addEventListener('drop', function(e) {
296 e.preventDefault();
297 + e.stopPropagation();
298 const files = e.dataTransfer && e.dataTransfer.files;
299 - if (!files) return;
300 + if (!files || files.length === 0) return;
301 for (let i = 0; i < files.length; i++) {
302 - if (files[i].type.indexOf('image/') === 0) {
303 - const mime = files[i].type;
304 - const reader = new FileReader();
305 - reader.onload = function() {
306 - const b64 = reader.result.split(',')[1] || '';
307 - fn(b64, mime);
308 - };
309 - reader.readAsDataURL(files[i]);
310 - }
311 + const f = files[i];
312 + const mime = f.type || 'application/octet-stream';
313 + if (mime.indexOf('image/') !== 0) continue;
314 + const reader = new FileReader();
315 + reader.onload = function() {
316 + const b64 = reader.result.split(',')[1] || '';
317 + fn(b64, mime);
318 + };
319 + reader.readAsDataURL(f);
320 }
321 });
322 }
323 diff --git a/ssabridge/ssabridge.go b/ssabridge/ssabridge.go
324 index c6f30f73..8bdadfd5 100644
325 --- a/ssabridge/ssabridge.go
326 +++ b/ssabridge/ssabridge.go
327 @@ -104,6 +104,14 @@ func (t *Translator) typ(gt types.Type) typecheck.Type {
328 tc := tcbridge.TranslateType(t.imp, gt)
329 if tc != nil && gt != nil {
330 t.revTypes[tc] = gt
331 + if u := tc.Underlying(); u != nil && u != tc {
332 + if _, exists := t.revTypes[u]; !exists {
333 + gu := gt.Underlying()
334 + if gu != nil {
335 + t.revTypes[u] = gu
336 + }
337 + }
338 + }
339 }
340 return tc
341 }
342 @@ -124,6 +132,16 @@ func (t *Translator) translateFunctionShell(f *ssa.Function, pkg *mxssa.Package)
343 }
344 fn := mxssa.NewFunction(f.Name(), sig, f.Synthetic, pkg, t.prog)
345 fn.SetPos(f.Pos())
346 + for _, p := range f.Params {
347 + mp := mxssa.NewParameter(p.Name(), t.typ(p.Type()), fn)
348 + mp.SetPos(p.Pos())
349 + fn.Params = append(fn.Params, mp)
350 + }
351 + for _, fv := range f.FreeVars {
352 + mfv := mxssa.NewFreeVar(fv.Name(), t.typ(fv.Type()), fn)
353 + mfv.SetPos(fv.Pos())
354 + fn.FreeVars = append(fn.FreeVars, mfv)
355 + }
356 t.funcs[f] = fn
357 t.revFuncs[fn] = f
358 return fn
359 @@ -153,20 +171,6 @@ func (t *Translator) translateFunctionBody(sf *ssa.Function, mf *mxssa.Function)
360 return // external function, no body
361 }
362
363 - // Parameters.
364 - for _, p := range sf.Params {
365 - mp := mxssa.NewParameter(p.Name(), t.typ(p.Type()), mf)
366 - mp.SetPos(p.Pos())
367 - mf.Params = append(mf.Params, mp)
368 - }
369 -
370 - // Free variables.
371 - for _, fv := range sf.FreeVars {
372 - mfv := mxssa.NewFreeVar(fv.Name(), t.typ(fv.Type()), mf)
373 - mfv.SetPos(fv.Pos())
374 - mf.FreeVars = append(mf.FreeVars, mfv)
375 - }
376 -
377 // Create blocks first (instructions reference blocks by index).
378 blockMap := map[*ssa.BasicBlock]*mxssa.BasicBlock{}
379 for _, b := range sf.Blocks {
380 @@ -206,14 +210,31 @@ func (t *Translator) translateFunctionBody(sf *ssa.Function, mf *mxssa.Function)
381 _ = g
382 }
383
384 - // Translate instructions.
385 - for _, b := range sf.Blocks {
386 + // Translate instructions in dominator-tree preorder so that definitions
387 + // are visited before their uses (SSA dominance property).
388 + // Phi edges are deferred because they can reference values from predecessor
389 + // blocks that aren't dominators (e.g. loop back-edges).
390 + type deferredPhi struct {
391 + phi *mxssa.Phi
392 + edges []ssa.Value
393 + }
394 + var deferred []deferredPhi
395 + for _, b := range sf.DomPreorder() {
396 mb := blockMap[b]
397 for _, instr := range b.Instrs {
398 + if phiInstr, ok := instr.(*ssa.Phi); ok {
399 + edges := make([]mxssa.Value, len(phiInstr.Edges))
400 + p := mxssa.NewPhi(t.typ(phiInstr.Type()), phiInstr.Comment, edges, mb)
401 + p.SetPos(phiInstr.Pos())
402 + p.SetName(phiInstr.Name())
403 + mb.Instrs = append(mb.Instrs, p)
404 + valMap[phiInstr] = p
405 + deferred = append(deferred, deferredPhi{p, phiInstr.Edges})
406 + continue
407 + }
408 mi := t.translateInstruction(instr, mb, blockMap, valMap)
409 if mi != nil {
410 mb.Instrs = append(mb.Instrs, mi)
411 - // If the instruction defines a value, register it.
412 if v, ok := instr.(ssa.Value); ok {
413 if mv, ok := mi.(mxssa.Value); ok {
414 valMap[v] = mv
415 @@ -222,6 +243,11 @@ func (t *Translator) translateFunctionBody(sf *ssa.Function, mf *mxssa.Function)
416 }
417 }
418 }
419 + for _, dp := range deferred {
420 + for j, e := range dp.edges {
421 + dp.phi.Edges[j] = t.value(e, valMap)
422 + }
423 + }
424
425 // Anonymous functions.
426 for _, af := range sf.AnonFuncs {
427 @@ -287,14 +313,7 @@ func (t *Translator) translateInstruction(
428 return a
429
430 case *ssa.Phi:
431 - edges := make([]mxssa.Value, len(i.Edges))
432 - for j, e := range i.Edges {
433 - edges[j] = v(e)
434 - }
435 - p := mxssa.NewPhi(t.typ(i.Type()), i.Comment, edges, mb)
436 - p.SetPos(i.Pos())
437 - p.SetName(i.Name())
438 - return p
439 + return nil // handled in deferred pass
440
441 case *ssa.Call:
442 cc := t.translateCallCommon(&i.Call, valMap)
443 diff --git a/typecheck/bridge/bridge.go b/typecheck/bridge/bridge.go
444 index fa496ce0..c69d561e 100644
445 --- a/typecheck/bridge/bridge.go
446 +++ b/typecheck/bridge/bridge.go
447 @@ -228,6 +228,9 @@ func translateType(t types.Type, cache *typeCache, imp *Importer) typecheck.Type
448
449 case *types.Alias:
450 return translateType(types.Unalias(t), cache, imp)
451 +
452 + case *types.Tuple:
453 + return translateTuple(t, cache, imp)
454 }
455 return nil
456 }
457