diff --git a/cl/import.go b/cl/import.go index e13437b693..eb7a0e2815 100644 --- a/cl/import.go +++ b/cl/import.go @@ -180,6 +180,7 @@ func (p *context) initFiles(pkgPath string, files []*ast.File, cPkg bool) { switch decl := decl.(type) { case *ast.FuncDecl: fullName, inPkgName := astFuncName(pkgPath, decl) + p.processNoInterfaceByDoc(decl.Doc, fullName) if !p.processLinknameByDoc(decl.Doc, fullName, inPkgName, false, true) && cPkg { // package C (https://github.com/goplus/llgo/issues/1165) if decl.Recv == nil && token.IsExported(inPkgName) { @@ -312,6 +313,22 @@ func (p *context) processLinknameByDoc(doc *ast.CommentGroup, fullName, inPkgNam return false } +func (p *context) processNoInterfaceByDoc(doc *ast.CommentGroup, fullName string) { + if doc == nil { + return + } + for n := len(doc.List) - 1; n >= 0; n-- { + line := doc.List[n].Text + if line == "//go:nointerface" { + p.prog.SetNoInterfaceMethod(fullName) + return + } + if !strings.HasPrefix(line, "//go:") { + return + } + } +} + const ( noDirective = iota hasLinkname @@ -737,11 +754,16 @@ func (p *context) initPyModule() { } } -// ParsePkgSyntax parses AST of a package to check llgo:type in type declaration. +// ParsePkgSyntax parses AST of a package to check package-level compiler directives. func ParsePkgSyntax(prog llssa.Program, pkg *types.Package, files []*ast.File) { + ctx := &context{prog: prog} + pkgPath := llssa.PathOf(pkg) for _, file := range files { for _, decl := range file.Decls { switch decl := decl.(type) { + case *ast.FuncDecl: + fullName, _ := astFuncName(pkgPath, decl) + ctx.processNoInterfaceByDoc(decl.Doc, fullName) case *ast.GenDecl: switch decl.Tok { case token.TYPE: diff --git a/cl/import_coverage_test.go b/cl/import_coverage_test.go index d5710ea5d3..345693b29c 100644 --- a/cl/import_coverage_test.go +++ b/cl/import_coverage_test.go @@ -47,6 +47,13 @@ type ( B int C int ) + +//go:nointerface +func (A) Hidden() {} + +//go:other +//go:nointerface +func (A) StackedHidden() {} ` fset := token.NewFileSet() file, err := parser.ParseFile(fset, "p.go", src, parser.ParseComments) @@ -56,6 +63,13 @@ type ( prog := llssa.NewProgram(nil) pkg := types.NewPackage("example.com/p", "p") ParsePkgSyntax(prog, pkg, []*ast.File{file}) + + ctx := &context{prog: prog} + ctx.processNoInterfaceByDoc(nil, "example.com/p.NilDoc") + ctx.processNoInterfaceByDoc(&ast.CommentGroup{List: []*ast.Comment{ + {Text: "// not a directive"}, + {Text: "//go:nointerface"}, + }}, "example.com/p.NonDirectiveStops") } func TestPkgSymInfoAddSymAndInitLinknamesCoverage(t *testing.T) { diff --git a/runtime/internal/lib/reflect/type.go b/runtime/internal/lib/reflect/type.go index 1afd0b07ca..5e8fb78577 100644 --- a/runtime/internal/lib/reflect/type.go +++ b/runtime/internal/lib/reflect/type.go @@ -305,16 +305,19 @@ func (t *rtype) Method(i int) (m Method) { } mt := FuncOf(in, out, ft.Variadic()) m.Type = mt - mtfn := (*funcType)(unsafe.Pointer(&mt.(*rtype).t)) - fv := &struct { - fn unsafe.Pointer - env unsafe.Pointer - }{p.Tfn_, nil} - m.Func = Value{closureOf(mtfn), unsafe.Pointer(fv), fl | flagIndir} + m.Func = methodFuncValue(&mt.(*rtype).t, p.Tfn_, fl) m.Index = i return m } +func methodFuncValue(ft *abi.Type, fn unsafe.Pointer, fl flag) Value { + ct := closureOf((*funcType)(unsafe.Pointer(ft))) + c := unsafe_New(ct) + *(*unsafe.Pointer)(c) = fn + *(*unsafe.Pointer)(add(c, goarch.PtrSize, "closure data field")) = nil + return Value{ct, c, fl | flagIndir} +} + func (t *rtype) MethodByName(name string) (m Method, ok bool) { if t.Kind() == Interface { tt := (*interfaceType)(unsafe.Pointer(t)) diff --git a/runtime/internal/runtime/errors.go b/runtime/internal/runtime/errors.go index 06d63ef66c..7b6776d99a 100644 --- a/runtime/internal/runtime/errors.go +++ b/runtime/internal/runtime/errors.go @@ -171,7 +171,29 @@ func PanicTypeAssert(concrete *_type, asserted string, missingMethod string) { if missingMethod != "" { panic(errorString("interface conversion: " + concrete.String() + " is not " + asserted + ": missing method " + missingMethod)) } - panic(errorString("interface conversion: interface is " + concrete.String() + ", not " + asserted)) + cs := concrete.String() + msg := "interface conversion: interface is " + cs + ", not " + asserted + if sameTypeAssertName(concrete, cs, asserted) { + msg += " (types from different scopes)" + } + panic(errorString(msg)) +} + +func sameTypeAssertName(concrete *_type, concreteString, asserted string) bool { + if concreteString == asserted { + return true + } + pkg := pkgpath(concrete) + return pkg != "" && hasPrefix(asserted, pkg+".") && typeNameSuffix(concreteString) == typeNameSuffix(asserted) +} + +func typeNameSuffix(name string) string { + for i := len(name) - 1; i >= 0; i-- { + if name[i] == '.' { + return name[i+1:] + } + } + return name } func (e *TypeAssertionError) Error() string { diff --git a/runtime/internal/runtime/z_face.go b/runtime/internal/runtime/z_face.go index f118f90bfb..a578529db7 100644 --- a/runtime/internal/runtime/z_face.go +++ b/runtime/internal/runtime/z_face.go @@ -221,10 +221,16 @@ func DirectIfaceData(typ *abi.Type) bool { func MatchesClosure(T, V *abi.Type) bool { if T == V { return true - } else if V == nil || !V.IsClosure() { + } else if T == nil || V == nil { return false } - return identicalFuncType(T.StructType().Fields[0].Typ, V.StructType().Fields[0].Typ) + if T.IsClosure() { + T = T.StructType().Fields[0].Typ + } + if V.IsClosure() { + V = V.StructType().Fields[0].Typ + } + return identicalFuncType(T, V) } func identicalFuncType(T, V *abi.Type) bool { diff --git a/ssa/abitype.go b/ssa/abitype.go index 0588a532d5..a42d944a59 100644 --- a/ssa/abitype.go +++ b/ssa/abitype.go @@ -398,16 +398,16 @@ type UncommonType struct { } */ -func (b Builder) abiUncommonType(t types.Type, mset *types.MethodSet) llvm.Value { +func (b Builder) abiUncommonType(t types.Type, methods []*types.Selection) llvm.Value { prog := b.Prog ft := prog.rtType("uncommonType") var fields []llvm.Value _, pkgPath := b.abiUncommonPkg(t) fields = append(fields, b.Str(pkgPath).impl) - mcount := mset.Len() + mcount := len(methods) var xcount int for i := 0; i < mcount; i++ { - if ast.IsExported(mset.At(i).Obj().Name()) { + if ast.IsExported(methods[i].Obj().Name()) { xcount++ } } @@ -427,10 +427,10 @@ type Method struct { } */ -func (b Builder) abiUncommonMethods(t types.Type, mset *types.MethodSet) llvm.Value { +func (b Builder) abiUncommonMethods(t types.Type, methods []*types.Selection) llvm.Value { prog := b.Prog ft := prog.rtType("Method") - n := mset.Len() + n := len(methods) fields := make([]llvm.Value, n) pkg, _ := b.abiUncommonPkg(t) anonymous := pkg == nil @@ -438,7 +438,7 @@ func (b Builder) abiUncommonMethods(t types.Type, mset *types.MethodSet) llvm.Va pkg = types.NewPackage(b.Pkg.Path(), "") } for i := 0; i < n; i++ { - m := mset.At(i) + m := methods[i] obj := m.Obj() mName := obj.Name() abiName := mName @@ -468,6 +468,20 @@ func (b Builder) abiUncommonMethods(t types.Type, mset *types.MethodSet) llvm.Va return llvm.ConstArray(ft.ll, fields) } +func (b Builder) abiInterfaceMethods(mset *types.MethodSet) []*types.Selection { + n := mset.Len() + methods := make([]*types.Selection, 0, n) + for i := 0; i < n; i++ { + m := mset.At(i) + fn, _ := m.Obj().(*types.Func) + if b.Prog.isNoInterfaceMethod(fn) { + continue + } + methods = append(methods, m) + } + return methods +} + // closure func type func funcType(prog Program, typ types.Type) types.Type { ftyp := prog.Type(typ, InGo) @@ -524,10 +538,11 @@ func (b Builder) abiType(t types.Type) Expr { t = prog.patchType(t) } mset, hasUncommon := b.abiUncommonMethodSet(t) - methodCount := 0 - if mset != nil { - methodCount = mset.Len() + var methods []*types.Selection + if hasUncommon { + methods = b.abiInterfaceMethods(mset) } + methodCount := len(methods) rt := prog.rtNamed(prog.abi.RuntimeName(t)) var typ types.Type = rt if hasUncommon { @@ -552,15 +567,15 @@ func (b Builder) abiType(t types.Type) Expr { if hasUncommon { fields = []llvm.Value{ llvm.ConstNamedStruct(prog.Type(rt, InGo).ll, fields), - b.abiUncommonType(t, mset), - b.abiUncommonMethods(t, mset), + b.abiUncommonType(t, methods), + b.abiUncommonMethods(t, methods), } } g.impl.SetInitializer(llvm.ConstNamedStruct(g.impl.GlobalValueType(), fields)) g.impl.SetGlobalConstant(true) g.impl.SetLinkage(llvm.WeakODRLinkage) if prog.enableGoGlobalDCE { - prog.addMethodTypeMetadata(g.impl, prog.Type(typ, InGo), mset, methodCount) + prog.addMethodTypeMetadata(g.impl, prog.Type(typ, InGo), methods) } prog.abiSymbol[name] = &AbiSymbol{Name: name, PkgPath: pkg.Path(), Raw: t, Typ: g.Type, MSet: mset} } diff --git a/ssa/globaldce.go b/ssa/globaldce.go index 55f06559cc..cb83c2a2bc 100644 --- a/ssa/globaldce.go +++ b/ssa/globaldce.go @@ -276,8 +276,8 @@ func (p Function) recordFakeUse(v llvm.Value) { p.fakeUses = append(p.fakeUses, v) } -func (p Program) addMethodTypeMetadata(global llvm.Value, fullType Type, mset *types.MethodSet, methodCount int) { - if methodCount == 0 { +func (p Program) addMethodTypeMetadata(global llvm.Value, fullType Type, methods []*types.Selection) { + if len(methods) == 0 { return } p.setVCallVisibilityMetadata(global, vcallVisibilityLinkageUnit) @@ -287,8 +287,7 @@ func (p Program) addMethodTypeMetadata(global llvm.Value, fullType Type, mset *t ifnOffset := p.OffsetOf(methodType, abiMethodIFnFieldIndex) tfnOffset := p.OffsetOf(methodType, abiMethodTFnFieldIndex) methodStride := p.SizeOf(methodType) - for i := 0; i < methodCount; i++ { - sel := mset.At(i) + for i, sel := range methods { baseOffset := methodArrayOffset + uint64(i)*methodStride p.addTypeMetadata(global, baseOffset+ifnOffset, methodCapabilityKey(sel.Obj().(*types.Func))) if sel.Obj().Exported() { diff --git a/ssa/package.go b/ssa/package.go index 5b0e9dd411..a13ccae93b 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -224,6 +224,7 @@ type aProgram struct { paramObjPtr_ *types.Var linkname map[string]string // pkgPath.nameInPkg => linkname + noInterface map[string]none // pkgPath.T.method or pkgPath.(*T).method abiSymbol map[string]*AbiSymbol // abi symbol name => AbiSymbol ptrSize int @@ -310,7 +311,7 @@ func NewProgram(target *Target) Program { ctx: ctx, gocvt: newGoTypes(), target: target, td: td, tm: tm, is32Bits: is32Bits, ptrSize: td.PointerSize(), named: make(map[string]Type), fnnamed: make(map[string]int), - linkname: make(map[string]string), abiSymbol: make(map[string]*AbiSymbol), + linkname: make(map[string]string), noInterface: make(map[string]none), abiSymbol: make(map[string]*AbiSymbol), } prog.abi.Init(uintptr(prog.ptrSize), (*goProgram)(unsafe.Pointer(prog))) return prog @@ -359,6 +360,22 @@ func (p Program) EnableLTOPluginMarkers(enable bool) { p.enableLTOPluginMarker = enable } +func (p Program) SetNoInterfaceMethod(fullName string) { + p.noInterface[fullName] = none{} +} + +func (p Program) isNoInterfaceMethod(fn *types.Func) bool { + if fn == nil { + return false + } + sig, ok := fn.Type().(*types.Signature) + if !ok || sig.Recv() == nil { + return false + } + _, ok = p.noInterface[FuncName(fn.Pkg(), fn.Name(), sig.Recv(), true)] + return ok +} + // SetRuntime sets the runtime. // Its type can be *types.Package or func() *types.Package. func (p Program) SetRuntime(runtime any) { diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index 6f1365b057..c45e71035c 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -734,9 +734,8 @@ func TestDevLTOGlobalDCEAddMethodTypeMetadataEarlyReturns(t *testing.T) { prog.EnableGoGlobalDCE(true) pkg := prog.NewPackage("main", "main") g := pkg.NewVarEx("g", prog.Pointer(prog.Int())) - mset := types.NewMethodSet(types.Typ[types.Int]) - prog.addMethodTypeMetadata(g.impl, prog.Pointer(prog.Int()), mset, 0) + prog.addMethodTypeMetadata(g.impl, prog.Pointer(prog.Int()), nil) ir := pkg.String() if strings.Contains(ir, "!vcall_visibility") || strings.Contains(ir, "!type !") { @@ -767,7 +766,7 @@ func TestDevLTOGlobalDCEAddMethodTypeMetadataMarksIFnAndTFnForReflectContexts(t methodArray := prog.Type(types.NewArray(prog.rtNamed("Method"), 1), InGo) fullType := prog.Struct(prog.Int(), prog.Int(), methodArray) - prog.addMethodTypeMetadata(g.impl, fullType, mset, mset.Len()) + prog.addMethodTypeMetadata(g.impl, fullType, []*types.Selection{mset.At(0)}) methodType := prog.Type(prog.rtNamed("Method"), InGo) methodArrayOffset := prog.OffsetOf(fullType, 2) @@ -2606,6 +2605,48 @@ func TestInitAbiTypesForEmptySelection(t *testing.T) { } } +func TestNoInterfaceMethodRegistryAndFiltering(t *testing.T) { + prog := NewProgram(nil) + if prog.isNoInterfaceMethod(nil) { + t.Fatal("nil function should not be nointerface") + } + + pkgTypes := types.NewPackage("example.com/p", "p") + named := types.NewNamed(types.NewTypeName(token.NoPos, pkgTypes, "T", nil), types.NewStruct(nil, nil), nil) + sig := types.NewSignatureType(types.NewVar(token.NoPos, pkgTypes, "", named), nil, nil, nil, nil, false) + hidden := types.NewFunc(token.NoPos, pkgTypes, "Hidden", sig) + visible := types.NewFunc(token.NoPos, pkgTypes, "Visible", sig) + named.AddMethod(hidden) + named.AddMethod(visible) + + top := types.NewFunc(token.NoPos, pkgTypes, "Top", types.NewSignatureType(nil, nil, nil, nil, nil, false)) + if prog.isNoInterfaceMethod(top) { + t.Fatal("function without receiver should not be nointerface") + } + if prog.isNoInterfaceMethod(hidden) { + t.Fatal("unregistered method should not be nointerface") + } + prog.SetNoInterfaceMethod("example.com/p.T.Hidden") + if !prog.isNoInterfaceMethod(hidden) { + t.Fatal("registered value receiver method should be nointerface") + } + if prog.isNoInterfaceMethod(visible) { + t.Fatal("unregistered sibling method should not be nointerface") + } + + methods := (&aBuilder{Prog: prog}).abiInterfaceMethods(types.NewMethodSet(named)) + if len(methods) != 1 || methods[0].Obj().Name() != "Visible" { + t.Fatalf("filtered methods = %v, want only Visible", methods) + } + + ptrSig := types.NewSignatureType(types.NewVar(token.NoPos, pkgTypes, "", types.NewPointer(named)), nil, nil, nil, nil, false) + ptrHidden := types.NewFunc(token.NoPos, pkgTypes, "PtrHidden", ptrSig) + prog.SetNoInterfaceMethod("example.com/p.(*T).PtrHidden") + if !prog.isNoInterfaceMethod(ptrHidden) { + t.Fatal("registered pointer receiver method should be nointerface") + } +} + func TestRtFuncResolvesLinkname(t *testing.T) { prog := NewProgram(nil) rt := types.NewPackage(PkgRuntime, PkgRuntime) diff --git a/test/go/interface_nil_assert_test.go b/test/go/interface_nil_assert_test.go index 850112d12d..8a8ac2a25d 100644 --- a/test/go/interface_nil_assert_test.go +++ b/test/go/interface_nil_assert_test.go @@ -38,10 +38,7 @@ func TestNilInterfaceSameTypeAssert(t *testing.T) { func TestNilInterfaceSameTypeAssertPanics(t *testing.T) { x := nilAssertValue(false) - defer func() { - if recover() == nil { - t.Fatal("expected panic for nil interface same-type assert") - } - }() - _ = x.(nilAssertInterface) + expectPanicContaining(t, "interface conversion", func() { + _ = x.(nilAssertInterface) + }) } diff --git a/test/go/interface_type_assert_panic_test.go b/test/go/interface_type_assert_panic_test.go new file mode 100644 index 0000000000..7d0870a663 --- /dev/null +++ b/test/go/interface_type_assert_panic_test.go @@ -0,0 +1,54 @@ +package gotest + +import "testing" + +type typeAssertInterface interface { + Get() int +} + +type typeAssertScopedT struct{} + +var typeAssertScopedValue any + +func typeAssertValue(v any) any { + return v +} + +func TestInterfaceAssertToInterfacePanicsWithRuntimeError(t *testing.T) { + expectPanicContaining(t, "interface conversion", func() { + _ = typeAssertValue(0).(typeAssertInterface) + }) +} + +func TestInterfaceAssertToConcretePanicsWithRuntimeError(t *testing.T) { + expectPanicContaining(t, "interface conversion", func() { + _ = typeAssertValue(0).(string) + }) +} + +func TestInterfaceAssertRejectsSameNameTypesFromDifferentScopes(t *testing.T) { + typeAssertAssignLocalT() + typeAssertLocalToLocalT(t) + typeAssertLocalToPackageT(t) + + typeAssertScopedValue = typeAssertScopedT{} + typeAssertLocalToLocalT(t) +} + +func typeAssertAssignLocalT() { + type typeAssertScopedT struct{} + typeAssertScopedValue = typeAssertScopedT{} +} + +func typeAssertLocalToLocalT(t *testing.T) { + type typeAssertScopedT struct{} + expectPanicContaining(t, "different scopes", func() { + _ = typeAssertScopedValue.(typeAssertScopedT) + }) +} + +func typeAssertLocalToPackageT(t *testing.T) { + expectPanicContaining(t, "different scopes", func() { + _ = typeAssertScopedValue.(typeAssertScopedT) + }) +} diff --git a/test/go/nointerface_expect_go_test.go b/test/go/nointerface_expect_go_test.go new file mode 100644 index 0000000000..30878b876c --- /dev/null +++ b/test/go/nointerface_expect_go_test.go @@ -0,0 +1,6 @@ +//go:build !llgo +// +build !llgo + +package gotest + +const noInterfaceMethodsFiltered = false diff --git a/test/go/nointerface_expect_llgo_test.go b/test/go/nointerface_expect_llgo_test.go new file mode 100644 index 0000000000..e4cde2ecde --- /dev/null +++ b/test/go/nointerface_expect_llgo_test.go @@ -0,0 +1,6 @@ +//go:build llgo +// +build llgo + +package gotest + +const noInterfaceMethodsFiltered = true diff --git a/test/go/nointerface_test.go b/test/go/nointerface_test.go new file mode 100644 index 0000000000..39c6ec01a4 --- /dev/null +++ b/test/go/nointerface_test.go @@ -0,0 +1,66 @@ +package gotest + +import "testing" + +type noInterfaceE struct{} + +//go:nointerface +func (noInterfaceE) EBad() int { return 1 } + +func (noInterfaceE) EGood() int { return 2 } + +type noInterfaceX[T any] struct { + noInterfaceE +} + +//go:nointerface +func (noInterfaceX[T]) XBad() int { return 3 } + +func (noInterfaceX[T]) XGood() int { return 4 } + +type noInterfaceW struct { + noInterfaceX[int] +} + +type noInterfacePtrBase struct{} + +//go:nointerface +func (*noInterfacePtrBase) PBad() int { return 5 } + +func (*noInterfacePtrBase) PGood() int { return 6 } + +type noInterfacePtrWrap struct { + noInterfacePtrBase +} + +func TestNoInterfaceMethodsDoNotImplementInterfaces(t *testing.T) { + if got := (noInterfaceE{}).EBad(); got != 1 { + t.Fatalf("direct nointerface method call = %d, want 1", got) + } + if got := (noInterfaceX[int]{}).XBad(); got != 3 { + t.Fatalf("direct generic nointerface method call = %d, want 3", got) + } + ptrWrap := noInterfacePtrWrap{} + if got := ptrWrap.PBad(); got != 5 { + t.Fatalf("direct promoted pointer nointerface method call = %d, want 5", got) + } + + checkNoInterface[noInterfaceE, interface{ EBad() int }, interface{ EGood() int }](t, "E") + checkNoInterface[noInterfaceX[int], interface{ EBad() int }, interface{ EGood() int }](t, "X.E") + checkNoInterface[noInterfaceX[int], interface{ XBad() int }, interface{ XGood() int }](t, "X") + checkNoInterface[noInterfaceW, interface{ EBad() int }, interface{ EGood() int }](t, "W.E") + checkNoInterface[noInterfaceW, interface{ XBad() int }, interface{ XGood() int }](t, "W.X") + checkNoInterface[noInterfacePtrWrap, interface{ PBad() int }, interface{ PGood() int }](t, "promoted pointer") +} + +func checkNoInterface[T any, Bad any, Good any](t *testing.T, name string) { + t.Helper() + v := any(new(T)) + _, badOK := v.(Bad) + if want := !noInterfaceMethodsFiltered; badOK != want { + t.Fatalf("%s: nointerface method assertion = %v, want %v", name, badOK, want) + } + if _, goodOK := v.(Good); !goodOK { + t.Fatalf("%s: normal method did not satisfy interface", name) + } +} diff --git a/test/go/reflect_method_type_identity_test.go b/test/go/reflect_method_type_identity_test.go new file mode 100644 index 0000000000..c070c97c84 --- /dev/null +++ b/test/go/reflect_method_type_identity_test.go @@ -0,0 +1,37 @@ +package gotest + +import ( + "reflect" + "testing" +) + +type reflectMethodIdentityPtr struct{} + +func (*reflectMethodIdentityPtr) Ptr() int { return 7 } + +type reflectMethodIdentityValue int + +func (reflectMethodIdentityValue) Value() int { return 9 } + +func TestReflectTypeMethodFuncInterfaceTypeIdentity(t *testing.T) { + ptrMethod := reflect.TypeOf(&reflectMethodIdentityPtr{}).Method(0) + ptrFn, ok := ptrMethod.Func.Interface().(func(*reflectMethodIdentityPtr) int) + if !ok { + t.Fatalf("Method.Func.Interface() has type %T, want func(*reflectMethodIdentityPtr) int", ptrMethod.Func.Interface()) + } + if got := ptrFn(&reflectMethodIdentityPtr{}); got != 7 { + t.Fatalf("pointer method func returned %d, want 7", got) + } + + valueMethod, ok := reflect.TypeOf(reflectMethodIdentityValue(0)).MethodByName("Value") + if !ok { + t.Fatal("MethodByName did not find Value") + } + valueFn, ok := valueMethod.Func.Interface().(func(reflectMethodIdentityValue) int) + if !ok { + t.Fatalf("MethodByName.Func.Interface() has type %T, want func(reflectMethodIdentityValue) int", valueMethod.Func.Interface()) + } + if got := valueFn(reflectMethodIdentityValue(0)); got != 9 { + t.Fatalf("value method func returned %d, want 9", got) + } +} diff --git a/test/goroot/xfail.yaml b/test/goroot/xfail.yaml index 39b30a73d6..3eff971de5 100644 --- a/test/goroot/xfail.yaml +++ b/test/goroot/xfail.yaml @@ -2574,12 +2574,7 @@ xfails: - platform: darwin/arm64 directive: run case: typeparam/mdempsky/16.go - reason: nil-deref panic value does not implement runtime.Error on darwin/arm64 - - version: go1.26 - platform: darwin/arm64 - directive: run - case: typeparam/mdempsky/15.go - reason: go1.26 go:nointerface methods still satisfy interfaces on darwin/arm64 + reason: nil-interface assertion panic implements runtime.Error but the message lacks the source interface type and prints the command-line-arguments package path on darwin/arm64 - version: go1.24 platform: linux/amd64 directive: run @@ -2618,8 +2613,18 @@ xfails: - version: go1.24 platform: linux/amd64 directive: run - case: fixedbugs/issue16130.go - reason: go1.24 goroot run failure on linux/amd64 + case: typeparam/mdempsky/16.go + reason: nil-interface assertion panic implements runtime.Error but the message lacks the source interface type and prints the command-line-arguments package path on linux/amd64 + - version: go1.25 + platform: linux/amd64 + directive: run + case: typeparam/mdempsky/16.go + reason: nil-interface assertion panic implements runtime.Error but the message lacks the source interface type and prints the command-line-arguments package path on linux/amd64 + - version: go1.26 + platform: linux/amd64 + directive: run + case: typeparam/mdempsky/16.go + reason: nil-interface assertion panic implements runtime.Error but the message lacks the source interface type and prints the command-line-arguments package path on linux/amd64 - version: go1.24 platform: linux/amd64 directive: run @@ -2640,11 +2645,6 @@ xfails: directive: run case: fixedbugs/issue65417.go reason: go1.24 goroot run failure on linux/amd64 - - version: go1.24 - platform: linux/amd64 - directive: run - case: typeparam/mdempsky/16.go - reason: go1.24 nil-deref panic value does not implement runtime.Error on linux/amd64 - version: go1.25 platform: linux/amd64 directive: run @@ -2680,11 +2680,6 @@ xfails: directive: run case: zerodivide.go reason: go1.25 goroot run failure on linux/amd64 - - version: go1.25 - platform: linux/amd64 - directive: run - case: fixedbugs/issue16130.go - reason: go1.25 goroot run failure on linux/amd64 - version: go1.25 platform: linux/amd64 directive: run @@ -2710,11 +2705,6 @@ xfails: directive: run case: fixedbugs/issue72844.go reason: go1.25 goroot run failure on linux/amd64 - - version: go1.25 - platform: linux/amd64 - directive: run - case: typeparam/mdempsky/16.go - reason: go1.25 nil-deref panic value does not implement runtime.Error on linux/amd64 - version: go1.26 platform: linux/amd64 directive: run @@ -2750,11 +2740,6 @@ xfails: directive: run case: zerodivide.go reason: go1.26 goroot run failure on linux/amd64 - - version: go1.26 - platform: linux/amd64 - directive: run - case: fixedbugs/issue16130.go - reason: go1.26 goroot run failure on linux/amd64 - version: go1.26 platform: linux/amd64 directive: run @@ -2795,16 +2780,6 @@ xfails: directive: run case: fixedbugs/issue75327.go reason: go1.26 goroot run failure on linux/amd64 - - version: go1.26 - platform: linux/amd64 - directive: run - case: typeparam/mdempsky/16.go - reason: go1.26 nil-deref panic value does not implement runtime.Error on linux/amd64 - - version: go1.26 - platform: linux/amd64 - directive: run - case: typeparam/mdempsky/15.go - reason: go1.26 go:nointerface methods still satisfy interfaces on linux/amd64 - version: go1.24 platform: linux/amd64 directive: run @@ -3570,10 +3545,6 @@ xfails: directive: run case: typeparam/chans.go reason: select over unbuffered channel misses final receive on darwin/arm64 - - platform: darwin/arm64 - directive: run - case: typeparam/mdempsky/15.go - reason: go:nointerface methods still satisfy interfaces on darwin/arm64 - version: go1.24 platform: darwin/arm64 directive: runoutput @@ -3648,10 +3619,6 @@ xfails: directive: run case: typeparam/chans.go reason: select over unbuffered channel misses final receive on linux/amd64 - - platform: linux/amd64 - directive: run - case: typeparam/mdempsky/15.go - reason: go:nointerface methods still satisfy interfaces on linux/amd64 - platform: linux/amd64 directive: runoutput case: index0.go