compiler: implement interface assertions
This is a lot harder than 'regular' type assertions as the actual methods need to be checked.
Этот коммит содержится в:
		
							родитель
							
								
									30ac6ec281
								
							
						
					
					
						коммит
						43b8c24226
					
				
					 5 изменённых файлов: 290 добавлений и 59 удалений
				
			
		
							
								
								
									
										141
									
								
								compiler.go
									
										
									
									
									
								
							
							
						
						
									
										141
									
								
								compiler.go
									
										
									
									
									
								
							|  | @ -426,7 +426,12 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error { | |||
| 		} | ||||
| 		rangeValue := llvm.ConstNamedStruct(rangeType, rangeValues) | ||||
| 		ranges = append(ranges, rangeValue) | ||||
| 		methods := make([]*types.Selection, 0, len(meta.Methods)) | ||||
| 		for _, method := range meta.Methods { | ||||
| 			methods = append(methods, method) | ||||
| 		} | ||||
| 		c.ir.SortMethods(methods) | ||||
| 		for _, method := range methods { | ||||
| 			f := c.ir.GetFunction(program.MethodValue(method)) | ||||
| 			if f.llvmFn.IsNil() { | ||||
| 				return errors.New("cannot find function: " + f.LinkName()) | ||||
|  | @ -440,6 +445,25 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error { | |||
| 		startIndex += len(meta.Methods) | ||||
| 	} | ||||
| 
 | ||||
| 	interfaceTypes := c.ir.AllInterfaces() | ||||
| 	interfaceLengths := make([]llvm.Value, len(interfaceTypes)) | ||||
| 	interfaceMethods := make([]llvm.Value, 0) | ||||
| 	for i, itfType := range interfaceTypes { | ||||
| 		if itfType.Type.NumMethods() > 0xff { | ||||
| 			return errors.New("too many methods for interface " + itfType.Type.String()) | ||||
| 		} | ||||
| 		interfaceLengths[i] = llvm.ConstInt(llvm.Int8Type(), uint64(itfType.Type.NumMethods()), false) | ||||
| 		funcs := make([]*types.Func, itfType.Type.NumMethods()) | ||||
| 		for i := range funcs { | ||||
| 			funcs[i] = itfType.Type.Method(i) | ||||
| 		} | ||||
| 		c.ir.SortFuncs(funcs) | ||||
| 		for _, f := range funcs { | ||||
| 			id := llvm.ConstInt(llvm.Int16Type(), uint64(c.ir.MethodNum(f)), false) | ||||
| 			interfaceMethods = append(interfaceMethods, id) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if len(ranges) >= 1<<16 { | ||||
| 		return errors.New("method call numbers do not fit in a 16-bit integer") | ||||
| 	} | ||||
|  | @ -469,8 +493,24 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error { | |||
| 	signatureArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(signatureArrayNewGlobal, signatureArrayOldGlobal.Type())) | ||||
| 	signatureArrayOldGlobal.EraseFromParentAsGlobal() | ||||
| 	signatureArrayNewGlobal.SetName("runtime.methodSetSignatures") | ||||
| 	interfaceLengthsArray := llvm.ConstArray(llvm.Int8Type(), interfaceLengths) | ||||
| 	interfaceLengthsArrayNewGlobal := llvm.AddGlobal(c.mod, interfaceLengthsArray.Type(), "runtime.interfaceLengths.tmp") | ||||
| 	interfaceLengthsArrayNewGlobal.SetInitializer(interfaceLengthsArray) | ||||
| 	interfaceLengthsArrayNewGlobal.SetLinkage(llvm.InternalLinkage) | ||||
| 	interfaceLengthsArrayOldGlobal := c.mod.NamedGlobal("runtime.interfaceLengths") | ||||
| 	interfaceLengthsArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(interfaceLengthsArrayNewGlobal, interfaceLengthsArrayOldGlobal.Type())) | ||||
| 	interfaceLengthsArrayOldGlobal.EraseFromParentAsGlobal() | ||||
| 	interfaceLengthsArrayNewGlobal.SetName("runtime.interfaceLengths") | ||||
| 	interfaceMethodsArray := llvm.ConstArray(llvm.Int16Type(), interfaceMethods) | ||||
| 	interfaceMethodsArrayNewGlobal := llvm.AddGlobal(c.mod, interfaceMethodsArray.Type(), "runtime.interfaceMethods.tmp") | ||||
| 	interfaceMethodsArrayNewGlobal.SetInitializer(interfaceMethodsArray) | ||||
| 	interfaceMethodsArrayNewGlobal.SetLinkage(llvm.InternalLinkage) | ||||
| 	interfaceMethodsArrayOldGlobal := c.mod.NamedGlobal("runtime.interfaceMethods") | ||||
| 	interfaceMethodsArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(interfaceMethodsArrayNewGlobal, interfaceMethodsArrayOldGlobal.Type())) | ||||
| 	interfaceMethodsArrayOldGlobal.EraseFromParentAsGlobal() | ||||
| 	interfaceMethodsArrayNewGlobal.SetName("runtime.interfaceMethods") | ||||
| 
 | ||||
| 	c.mod.NamedGlobal("runtime.firstInterfaceNum").SetInitializer(llvm.ConstInt(llvm.Int16Type(), uint64(c.ir.FirstDynamicType()), false)) | ||||
| 	c.mod.NamedGlobal("runtime.firstTypeWithMethods").SetInitializer(llvm.ConstInt(llvm.Int16Type(), uint64(c.ir.FirstDynamicType()), false)) | ||||
| 
 | ||||
| 	// see: https://reviews.llvm.org/D18355 | ||||
| 	c.mod.AddNamedMetadataOperand("llvm.module.flags", | ||||
|  | @ -2238,25 +2278,43 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { | |||
| 		if err != nil { | ||||
| 			return llvm.Value{}, err | ||||
| 		} | ||||
| 		if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok { | ||||
| 			// TODO: check whether the type implements the interface. | ||||
| 			return llvm.Value{}, errors.New("todo: assert on interface") | ||||
| 		} | ||||
| 
 | ||||
| 		assertedType, err := c.getLLVMType(expr.AssertedType) | ||||
| 		if err != nil { | ||||
| 			return llvm.Value{}, err | ||||
| 		} | ||||
| 		assertedTypeNum, typeExists := c.ir.TypeNum(expr.AssertedType) | ||||
| 		if !typeExists { | ||||
| 			// Static analysis has determined this type assert will never apply. | ||||
| 			return llvm.ConstStruct([]llvm.Value{llvm.Undef(assertedType), llvm.ConstInt(llvm.Int1Type(), 0, false)}, false), nil | ||||
| 		valueNil, err := getZeroValue(assertedType) | ||||
| 		if err != nil { | ||||
| 			return llvm.Value{}, err | ||||
| 		} | ||||
| 		if assertedTypeNum >= 1<<16 { | ||||
| 			return llvm.Value{}, errors.New("interface typecodes do not fit in a 16-bit integer") | ||||
| 		} | ||||
| 		actualTypeNum := c.builder.CreateExtractValue(itf, 0, "interface.type") | ||||
| 
 | ||||
| 		commaOk := c.builder.CreateICmp(llvm.IntEQ, llvm.ConstInt(llvm.Int16Type(), uint64(assertedTypeNum), false), actualTypeNum, "") | ||||
| 		actualTypeNum := c.builder.CreateExtractValue(itf, 0, "interface.type") | ||||
| 		commaOk := llvm.Value{} | ||||
| 		if itf, ok := expr.AssertedType.Underlying().(*types.Interface); ok { | ||||
| 			// Type assert on interface type. | ||||
| 			// This is slightly non-trivial: at runtime the list of methods | ||||
| 			// needs to be checked to see whether it implements the interface. | ||||
| 			// At the same time, the interface value itself is unchanged. | ||||
| 			itfTypeNum := c.ir.InterfaceNum(itf) | ||||
| 			itfTypeNumValue := llvm.ConstInt(llvm.Int16Type(), uint64(itfTypeNum), false) | ||||
| 			fn := c.mod.NamedFunction("runtime.interfaceImplements") | ||||
| 			commaOk = c.builder.CreateCall(fn, []llvm.Value{actualTypeNum, itfTypeNumValue}, "") | ||||
| 
 | ||||
| 		} else { | ||||
| 			// Type assert on concrete type. | ||||
| 			// This is easy: just compare the type number. | ||||
| 			assertedTypeNum, typeExists := c.ir.TypeNum(expr.AssertedType) | ||||
| 			if !typeExists { | ||||
| 				// Static analysis has determined this type assert will never apply. | ||||
| 				return llvm.ConstStruct([]llvm.Value{valueNil, llvm.ConstInt(llvm.Int1Type(), 0, false)}, false), nil | ||||
| 			} | ||||
| 			if assertedTypeNum >= 1<<16 { | ||||
| 				return llvm.Value{}, errors.New("interface typecodes do not fit in a 16-bit integer") | ||||
| 			} | ||||
| 
 | ||||
| 			assertedTypeNumValue := llvm.ConstInt(llvm.Int16Type(), uint64(assertedTypeNum), false) | ||||
| 			commaOk = c.builder.CreateICmp(llvm.IntEQ, assertedTypeNumValue, actualTypeNum, "") | ||||
| 		} | ||||
| 
 | ||||
| 		// Add 2 new basic blocks (that should get optimized away): one for the | ||||
| 		// 'ok' case and one for all instructions following this type assert. | ||||
|  | @ -2269,11 +2327,6 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { | |||
| 		// typeassert should return a zero value, not an incorrectly casted | ||||
| 		// value. | ||||
| 
 | ||||
| 		valueNil, err := getZeroValue(assertedType) | ||||
| 		if err != nil { | ||||
| 			return llvm.Value{}, err | ||||
| 		} | ||||
| 
 | ||||
| 		prevBlock := c.builder.GetInsertBlock() | ||||
| 		okBlock := c.ctx.AddBasicBlock(frame.fn.llvmFn, "typeassert.ok") | ||||
| 		nextBlock := c.ctx.AddBasicBlock(frame.fn.llvmFn, "typeassert.next") | ||||
|  | @ -2282,29 +2335,37 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { | |||
| 		// Retrieve the value from the interface if the type assert was | ||||
| 		// successful. | ||||
| 		c.builder.SetInsertPointAtEnd(okBlock) | ||||
| 		valuePtr := c.builder.CreateExtractValue(itf, 1, "typeassert.value.ptr") | ||||
| 		var valueOk llvm.Value | ||||
| 		if c.targetData.TypeAllocSize(assertedType) > c.targetData.TypeAllocSize(c.i8ptrType) { | ||||
| 			// Value was stored in an allocated buffer, load it from there. | ||||
| 			valuePtrCast := c.builder.CreateBitCast(valuePtr, llvm.PointerType(assertedType, 0), "") | ||||
| 			valueOk = c.builder.CreateLoad(valuePtrCast, "typeassert.value.ok") | ||||
| 		if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok { | ||||
| 			// Type assert on interface type. Easy: just return the same | ||||
| 			// interface value. | ||||
| 			valueOk = itf | ||||
| 		} else { | ||||
| 			// Value was stored directly in the interface. | ||||
| 			switch assertedType.TypeKind() { | ||||
| 			case llvm.IntegerTypeKind: | ||||
| 				valueOk = c.builder.CreatePtrToInt(valuePtr, assertedType, "typeassert.value.ok") | ||||
| 			case llvm.PointerTypeKind: | ||||
| 				valueOk = c.builder.CreateBitCast(valuePtr, assertedType, "typeassert.value.ok") | ||||
| 			case llvm.StructTypeKind: | ||||
| 				// A bitcast would be useful here, but bitcast doesn't allow | ||||
| 				// aggregate types. So we'll bitcast it using an alloca. | ||||
| 				// Hopefully this will get optimized away. | ||||
| 				mem := c.builder.CreateAlloca(c.i8ptrType, "") | ||||
| 				c.builder.CreateStore(valuePtr, mem) | ||||
| 				memStructPtr := c.builder.CreateBitCast(mem, llvm.PointerType(assertedType, 0), "") | ||||
| 				valueOk = c.builder.CreateLoad(memStructPtr, "typeassert.value.ok") | ||||
| 			default: | ||||
| 				return llvm.Value{}, errors.New("todo: typeassert: bitcast small types") | ||||
| 			// Type assert on concrete type. Extract the underlying type from | ||||
| 			// the interface (but only after checking it matches). | ||||
| 			valuePtr := c.builder.CreateExtractValue(itf, 1, "typeassert.value.ptr") | ||||
| 			if c.targetData.TypeAllocSize(assertedType) > c.targetData.TypeAllocSize(c.i8ptrType) { | ||||
| 				// Value was stored in an allocated buffer, load it from there. | ||||
| 				valuePtrCast := c.builder.CreateBitCast(valuePtr, llvm.PointerType(assertedType, 0), "") | ||||
| 				valueOk = c.builder.CreateLoad(valuePtrCast, "typeassert.value.ok") | ||||
| 			} else { | ||||
| 				// Value was stored directly in the interface. | ||||
| 				switch assertedType.TypeKind() { | ||||
| 				case llvm.IntegerTypeKind: | ||||
| 					valueOk = c.builder.CreatePtrToInt(valuePtr, assertedType, "typeassert.value.ok") | ||||
| 				case llvm.PointerTypeKind: | ||||
| 					valueOk = c.builder.CreateBitCast(valuePtr, assertedType, "typeassert.value.ok") | ||||
| 				case llvm.StructTypeKind: | ||||
| 					// A bitcast would be useful here, but bitcast doesn't allow | ||||
| 					// aggregate types. So we'll bitcast it using an alloca. | ||||
| 					// Hopefully this will get optimized away. | ||||
| 					mem := c.builder.CreateAlloca(c.i8ptrType, "") | ||||
| 					c.builder.CreateStore(valuePtr, mem) | ||||
| 					memStructPtr := c.builder.CreateBitCast(mem, llvm.PointerType(assertedType, 0), "") | ||||
| 					valueOk = c.builder.CreateLoad(memStructPtr, "typeassert.value.ok") | ||||
| 				default: | ||||
| 					return llvm.Value{}, errors.New("todo: typeassert: bitcast small types") | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		c.builder.CreateBr(nextBlock) | ||||
|  |  | |||
							
								
								
									
										71
									
								
								ir.go
									
										
									
									
									
								
							
							
						
						
									
										71
									
								
								ir.go
									
										
									
									
									
								
							|  | @ -25,10 +25,11 @@ type Program struct { | |||
| 	NamedTypes           []*NamedType | ||||
| 	needsScheduler       bool | ||||
| 	goCalls              []*ssa.Go | ||||
| 	typesWithMethods     map[string]*InterfaceType // see AnalyseInterfaceConversions | ||||
| 	typesWithoutMethods  map[string]int            // see AnalyseInterfaceConversions | ||||
| 	methodSignatureNames map[string]int | ||||
| 	fpWithContext        map[string]struct{} // see AnalyseFunctionPointers | ||||
| 	typesWithMethods     map[string]*TypeWithMethods // see AnalyseInterfaceConversions | ||||
| 	typesWithoutMethods  map[string]int              // see AnalyseInterfaceConversions | ||||
| 	methodSignatureNames map[string]int              // see MethodNum | ||||
| 	interfaces           map[string]*Interface       // see AnalyseInterfaceConversions | ||||
| 	fpWithContext        map[string]struct{}         // see AnalyseFunctionPointers | ||||
| } | ||||
| 
 | ||||
| // Function or method. | ||||
|  | @ -60,12 +61,19 @@ type NamedType struct { | |||
| } | ||||
| 
 | ||||
| // Type that is at some point put in an interface. | ||||
| type InterfaceType struct { | ||||
| type TypeWithMethods struct { | ||||
| 	t       types.Type | ||||
| 	Num     int | ||||
| 	Methods map[string]*types.Selection | ||||
| } | ||||
| 
 | ||||
| // Interface type that is at some point used in a type assert (to check whether | ||||
| // it implements another interface). | ||||
| type Interface struct { | ||||
| 	Num  int | ||||
| 	Type *types.Interface | ||||
| } | ||||
| 
 | ||||
| // Create and intialize a new *Program from a *ssa.Program. | ||||
| func NewProgram(program *ssa.Program, mainPath string) *Program { | ||||
| 	return &Program{ | ||||
|  | @ -74,6 +82,7 @@ func NewProgram(program *ssa.Program, mainPath string) *Program { | |||
| 		functionMap:          make(map[*ssa.Function]*Function), | ||||
| 		globalMap:            make(map[*ssa.Global]*Global), | ||||
| 		methodSignatureNames: make(map[string]int), | ||||
| 		interfaces:           make(map[string]*Interface), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -148,6 +157,18 @@ func (p *Program) GetGlobal(ssaGlobal *ssa.Global) *Global { | |||
| 	return p.globalMap[ssaGlobal] | ||||
| } | ||||
| 
 | ||||
| // SortMethods sorts the list of methods by method ID. | ||||
| func (p *Program) SortMethods(methods []*types.Selection) { | ||||
| 	m := &methodList{methods: methods, program: p} | ||||
| 	sort.Sort(m) | ||||
| } | ||||
| 
 | ||||
| // SortFuncs sorts the list of functions by method ID. | ||||
| func (p *Program) SortFuncs(funcs []*types.Func) { | ||||
| 	m := &funcList{funcs: funcs, program: p} | ||||
| 	sort.Sort(m) | ||||
| } | ||||
| 
 | ||||
| // Parse compiler directives in the preceding comments. | ||||
| func (f *Function) parsePragmas() { | ||||
| 	if f.fn.Syntax() == nil { | ||||
|  | @ -236,3 +257,43 @@ func (g *Global) LinkName() string { | |||
| func (g *Global) IsExtern() bool { | ||||
| 	return strings.HasPrefix(g.g.Name(), "_extern_") | ||||
| } | ||||
| 
 | ||||
| // Wrapper type to implement sort.Interface for []*types.Selection. | ||||
| type methodList struct { | ||||
| 	methods []*types.Selection | ||||
| 	program *Program | ||||
| } | ||||
| 
 | ||||
| func (m *methodList) Len() int { | ||||
| 	return len(m.methods) | ||||
| } | ||||
| 
 | ||||
| func (m *methodList) Less(i, j int) bool { | ||||
| 	iid := m.program.MethodNum(m.methods[i].Obj().(*types.Func)) | ||||
| 	jid := m.program.MethodNum(m.methods[j].Obj().(*types.Func)) | ||||
| 	return iid < jid | ||||
| } | ||||
| 
 | ||||
| func (m *methodList) Swap(i, j int) { | ||||
| 	m.methods[i], m.methods[j] = m.methods[j], m.methods[i] | ||||
| } | ||||
| 
 | ||||
| // Wrapper type to implement sort.Interface for []*types.Func. | ||||
| type funcList struct { | ||||
| 	funcs   []*types.Func | ||||
| 	program *Program | ||||
| } | ||||
| 
 | ||||
| func (fl *funcList) Len() int { | ||||
| 	return len(fl.funcs) | ||||
| } | ||||
| 
 | ||||
| func (fl *funcList) Less(i, j int) bool { | ||||
| 	iid := fl.program.MethodNum(fl.funcs[i]) | ||||
| 	jid := fl.program.MethodNum(fl.funcs[j]) | ||||
| 	return iid < jid | ||||
| } | ||||
| 
 | ||||
| func (fl *funcList) Swap(i, j int) { | ||||
| 	fl.funcs[i], fl.funcs[j] = fl.funcs[j], fl.funcs[i] | ||||
| } | ||||
|  |  | |||
							
								
								
									
										53
									
								
								passes.go
									
										
									
									
									
								
							
							
						
						
									
										53
									
								
								passes.go
									
										
									
									
									
								
							|  | @ -2,6 +2,9 @@ package main | |||
| 
 | ||||
| import ( | ||||
| 	"go/types" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"golang.org/x/tools/go/ssa" | ||||
| ) | ||||
| 
 | ||||
|  | @ -56,6 +59,18 @@ func Signature(sig *types.Signature) string { | |||
| 	return s | ||||
| } | ||||
| 
 | ||||
| // Convert an interface type to a string of all method strings, separated by | ||||
| // "; ". For example: "Read([]byte) (int, error); Close() error" | ||||
| func InterfaceKey(itf *types.Interface) string { | ||||
| 	methodStrings := []string{} | ||||
| 	for i := 0; i < itf.NumMethods(); i++ { | ||||
| 		method := itf.Method(i) | ||||
| 		methodStrings = append(methodStrings, MethodSignature(method)) | ||||
| 	} | ||||
| 	sort.Strings(methodStrings) | ||||
| 	return strings.Join(methodStrings, ";") | ||||
| } | ||||
| 
 | ||||
| // Fill in parents of all functions. | ||||
| // | ||||
| // All packages need to be added before this pass can run, or it will produce | ||||
|  | @ -104,7 +119,7 @@ func (p *Program) AnalyseCallgraph() { | |||
| func (p *Program) AnalyseInterfaceConversions() { | ||||
| 	// Clear, if AnalyseTypes has been called before. | ||||
| 	p.typesWithoutMethods = map[string]int{"nil": 0} | ||||
| 	p.typesWithMethods = map[string]*InterfaceType{} | ||||
| 	p.typesWithMethods = map[string]*TypeWithMethods{} | ||||
| 
 | ||||
| 	for _, f := range p.Functions { | ||||
| 		for _, block := range f.fn.Blocks { | ||||
|  | @ -114,7 +129,7 @@ func (p *Program) AnalyseInterfaceConversions() { | |||
| 					methods := getAllMethods(f.fn.Prog, instr.X.Type()) | ||||
| 					name := instr.X.Type().String() | ||||
| 					if _, ok := p.typesWithMethods[name]; !ok && len(methods) > 0 { | ||||
| 						t := &InterfaceType{ | ||||
| 						t := &TypeWithMethods{ | ||||
| 							t:       instr.X.Type(), | ||||
| 							Num:     len(p.typesWithMethods), | ||||
| 							Methods: make(map[string]*types.Selection), | ||||
|  | @ -271,7 +286,13 @@ func (p *Program) SimpleDCE() { | |||
| 			for _, instr := range block.Instrs { | ||||
| 				if instr, ok := instr.(*ssa.MakeInterface); ok { | ||||
| 					for _, sel := range getAllMethods(p.program, instr.X.Type()) { | ||||
| 						callee := p.GetFunction(p.program.MethodValue(sel)) | ||||
| 						fn := p.program.MethodValue(sel) | ||||
| 						callee := p.GetFunction(fn) | ||||
| 						if callee == nil { | ||||
| 							// TODO: why is this necessary? | ||||
| 							p.addFunction(fn) | ||||
| 							callee = p.GetFunction(fn) | ||||
| 						} | ||||
| 						if !callee.flag { | ||||
| 							callee.flag = true | ||||
| 							worklist = append(worklist, callee.fn) | ||||
|  | @ -361,6 +382,19 @@ func (p *Program) TypeNum(typ types.Type) (int, bool) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // InterfaceNum returns the numeric interface ID of this type, for use in type | ||||
| // asserts. | ||||
| func (p *Program) InterfaceNum(itfType *types.Interface) int { | ||||
| 	key := InterfaceKey(itfType) | ||||
| 	if itf, ok := p.interfaces[key]; !ok { | ||||
| 		num := len(p.interfaces) | ||||
| 		p.interfaces[key] = &Interface{Num: num, Type: itfType} | ||||
| 		return num | ||||
| 	} else { | ||||
| 		return itf.Num | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // MethodNum returns the numeric ID of this method, to be used in method lookups | ||||
| // on interfaces for example. | ||||
| func (p *Program) MethodNum(method *types.Func) int { | ||||
|  | @ -381,14 +415,23 @@ func (p *Program) FirstDynamicType() int { | |||
| } | ||||
| 
 | ||||
| // Return all types with methods, sorted by type ID. | ||||
| func (p *Program) AllDynamicTypes() []*InterfaceType { | ||||
| 	l := make([]*InterfaceType, len(p.typesWithMethods)) | ||||
| func (p *Program) AllDynamicTypes() []*TypeWithMethods { | ||||
| 	l := make([]*TypeWithMethods, len(p.typesWithMethods)) | ||||
| 	for _, m := range p.typesWithMethods { | ||||
| 		l[m.Num] = m | ||||
| 	} | ||||
| 	return l | ||||
| } | ||||
| 
 | ||||
| // Return all interface types, sorted by interface ID. | ||||
| func (p *Program) AllInterfaces() []*Interface { | ||||
| 	l := make([]*Interface, len(p.interfaces)) | ||||
| 	for _, itf := range p.interfaces { | ||||
| 		l[itf.Num] = itf | ||||
| 	} | ||||
| 	return l | ||||
| } | ||||
| 
 | ||||
| func (p *Program) FunctionNeedsContext(f *Function) bool { | ||||
| 	if !f.addressTaken { | ||||
| 		return false | ||||
|  |  | |||
|  | @ -16,6 +16,18 @@ type Stringer interface { | |||
| 	String() string | ||||
| } | ||||
| 
 | ||||
| type Foo int | ||||
| 
 | ||||
| type Number int | ||||
| 
 | ||||
| func (n Number) Double() int { | ||||
| 	return int(n) * 2 | ||||
| } | ||||
| 
 | ||||
| type Doubler interface { | ||||
| 	Double() int | ||||
| } | ||||
| 
 | ||||
| const SIX = 6 | ||||
| 
 | ||||
| var testmap = map[string]int{"data": 3} | ||||
|  | @ -54,6 +66,7 @@ func main() { | |||
| 	printItf(*thing) | ||||
| 	printItf(thing) | ||||
| 	printItf(Stringer(thing)) | ||||
| 	printItf(Number(3)) | ||||
| 	s := Stringer(thing) | ||||
| 	println("Stringer.String():", s.String()) | ||||
| 
 | ||||
|  | @ -107,6 +120,8 @@ func strlen(s string) int { | |||
| 
 | ||||
| func printItf(val interface{}) { | ||||
| 	switch val := val.(type) { | ||||
| 	case Doubler: | ||||
| 		println("is Doubler:", val.Double()) | ||||
| 	case int: | ||||
| 		println("is int:", val) | ||||
| 	case byte: | ||||
|  | @ -117,6 +132,8 @@ func printItf(val interface{}) { | |||
| 		println("is Thing:", val.String()) | ||||
| 	case *Thing: | ||||
| 		println("is *Thing:", val.String()) | ||||
| 	case Foo: | ||||
| 		println("is Foo:", val) | ||||
| 	default: | ||||
| 		println("is ?") | ||||
| 	} | ||||
|  |  | |||
|  | @ -10,10 +10,10 @@ package runtime | |||
| // signatures as interned strings at compile time. | ||||
| // | ||||
| // The typecode is a small number unique for the Go type. All typecodes < | ||||
| // firstInterfaceNum do not have any methods and typecodes >= firstInterfaceNum | ||||
| // all have at least one method. This means that methodSetRanges does not need | ||||
| // to contain types without methods and is thus indexed starting at a typecode | ||||
| // with number firstInterfaceNum. | ||||
| // firstTypeWithMethods do not have any methods and typecodes >= | ||||
| // firstTypeWithMethods all have at least one method. This means that | ||||
| // methodSetRanges does not need to contain types without methods and is thus | ||||
| // indexed starting at a typecode with number firstTypeWithMethods. | ||||
| // | ||||
| // To further conserve some space, the methodSetRange (as the name indicates) | ||||
| // doesn't contain a list of methods and function pointers directly, but instead | ||||
|  | @ -36,10 +36,13 @@ type methodSetRange struct { | |||
| // which is a dummy value, but will be bigger after the compiler has filled them | ||||
| // in. | ||||
| var ( | ||||
| 	firstInterfaceNum   uint16            // the lowest typecode that has at least one method | ||||
| 	methodSetRanges     [0]methodSetRange // indexes into methodSetSignatures and methodSetFunctions | ||||
| 	methodSetSignatures [0]uint16         // uniqued method ID | ||||
| 	methodSetFunctions  [0]*uint8         // function pointer of method | ||||
| 	firstTypeWithMethods uint16            // the lowest typecode that has at least one method | ||||
| 	methodSetRanges      [0]methodSetRange // indexes into methodSetSignatures and methodSetFunctions | ||||
| 	methodSetSignatures  [0]uint16         // uniqued method ID | ||||
| 	methodSetFunctions   [0]*uint8         // function pointer of method | ||||
| 	interfaceIndex       [0]uint16         // mapping from interface ID to an index in interfaceMethods | ||||
| 	interfaceLengths     [0]uint8          // mapping from interface ID to the number of methods it has | ||||
| 	interfaceMethods     [0]uint16         // the method an interface implements (list of method IDs) | ||||
| ) | ||||
| 
 | ||||
| // Get the function pointer for the method on the interface. | ||||
|  | @ -50,7 +53,7 @@ func interfaceMethod(itf _interface, method uint16) *uint8 { | |||
| 	// in the list of signatures. The compiler will only emit | ||||
| 	// runtime.interfaceMethod calls when the method actually exists on this | ||||
| 	// interface (proven by the typechecker). | ||||
| 	i := methodSetRanges[itf.typecode-firstInterfaceNum].index | ||||
| 	i := methodSetRanges[itf.typecode-firstTypeWithMethods].index | ||||
| 	for { | ||||
| 		if methodSetSignatures[i] == method { | ||||
| 			return methodSetFunctions[i] | ||||
|  | @ -72,3 +75,49 @@ func interfaceEqual(x, y _interface) bool { | |||
| 	// TODO: depends on reflection. | ||||
| 	panic("unimplemented: interface equality") | ||||
| } | ||||
| 
 | ||||
| // Return true iff the type implements all methods needed by the interface. This | ||||
| // means the type satisfies the interface. | ||||
| // This is a compiler intrinsic. | ||||
| //go:nobounds | ||||
| func interfaceImplements(typecode, interfaceNum uint16) bool { | ||||
| 	// method set indexes of the concrete type | ||||
| 	methodSet := methodSetRanges[typecode-firstTypeWithMethods] | ||||
| 	methodIndex := methodSet.index | ||||
| 	methodIndexEnd := methodSet.index + methodSet.length | ||||
| 
 | ||||
| 	// method set indexes of the interface | ||||
| 	itfIndex := interfaceIndex[interfaceNum] | ||||
| 	itfIndexEnd := itfIndex + uint16(interfaceLengths[interfaceNum]) | ||||
| 
 | ||||
| 	// Iterate over all methods of the interface: | ||||
| 	for itfIndex < itfIndexEnd { | ||||
| 		methodId := interfaceMethods[itfIndex] | ||||
| 		if methodIndex >= methodIndexEnd { | ||||
| 			// Reached the end of the list of methods, so interface doesn't | ||||
| 			// implement this type. | ||||
| 			return false | ||||
| 		} | ||||
| 		if methodId == methodSetSignatures[methodIndex] { | ||||
| 			// Found a matching method, continue to the next method. | ||||
| 			itfIndex++ | ||||
| 			methodIndex++ | ||||
| 			continue | ||||
| 		} else if methodId > methodSetSignatures[methodIndex] { | ||||
| 			// The method didn't match, but method ID of the concrete type was | ||||
| 			// lower than that of the interface, so probably it has a method the | ||||
| 			// interface doesn't implement. | ||||
| 			// Move on to the next method of the concrete type. | ||||
| 			methodIndex++ | ||||
| 			continue | ||||
| 		} else { | ||||
| 			// The concrete type is missing a method. This means the type assert | ||||
| 			// fails. | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Found a method for each expected method in the interface. This type | ||||
| 	// assert is successful. | ||||
| 	return true | ||||
| } | ||||
|  |  | |||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Ayke van Laethem
						Ayke van Laethem