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