Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
0b4d5f8
[release-branch.go1.26] cmd/link: use bfd ld 2.36+ on linux/arm64 ins…
xnox Jan 29, 2026
f4e425d
[release-branch.go1.26] fix incorrect loop trip counts
mrkfrmn Apr 14, 2026
ba4554f
[release-branch.go1.26] cmd/go: specify full path to go command when …
matloob Mar 17, 2026
efdc0fb
[release-branch.go1.26] cmd/compile: handle min integer step in loop
cuonglm Apr 13, 2026
710f29a
[release-branch.go1.26] runtime: add sysUnreserve to undo sysReserve
prattmic Apr 2, 2026
ec5ebec
[release-branch.go1.26] all: update x/net to 705de46f
mrkfrmn Apr 17, 2026
be12fe1
[release-branch.go1.26] runtime: use uname version check for 64-bit t…
bradfitz Mar 24, 2026
9b01c04
[release-branch.go1.26] html/template: fix escaper bypass by treating…
thatnealpatel Apr 27, 2026
9547066
[release-branch.go1.26] net/http/httputil: reencode queries with many…
neild Apr 24, 2026
cb994d8
[release-branch.go1.26] cmd/fix: change -diff to exit 1 if diffs exist
yqyyqq Mar 3, 2026
c971287
[release-branch.go1.26] crypto/internal/fips140/drbg: build tag out e…
cherrymui Mar 23, 2026
e137885
[release-branch.go1.26] cmd/compile/internal/devirtualize: use pointe…
mateusz834 Mar 27, 2026
50856a1
[release-branch.go1.26] cmd/go: use MkdirTemp to create temp director…
neild Apr 8, 2026
73f417a
[release-branch.go1.26] go/types, types2: handle unconstrained type p…
griesemer Mar 12, 2026
e9df527
[release-branch.go1.26] crypto/tls: wrap ML-KEM hybrids in fips140.Wi…
FiloSottile Mar 25, 2026
19d2ce3
[release-branch.go1.26] runtime: fix timespec definition on 32bits sy…
Jorropo Mar 4, 2026
f43caf8
[release-branch.go1.26] lib/fips140: update inprocess to v1.26.0
FiloSottile Apr 27, 2026
2992086
[release-branch.go1.26] lib/fips140: add certified pointing to v1.0.0…
FiloSottile Apr 27, 2026
e0f5c05
[release-branch.go1.26] net: avoid double-free of cgo pointer when ha…
neild Mar 26, 2026
18fa391
[release-branch.go1.26] crypto/fips140: add package docs
FiloSottile Apr 28, 2026
dd29b59
[release-branch.go1.26] cmd/compile: fix loopvar version detection wi…
xieyuschen Jan 21, 2026
d9389d3
[release-branch.go1.26] net/mail: fix quadratic complexity in consume…
mohammadmseet-hue Apr 4, 2026
40fa774
[release-branch.go1.26] cmd/pack: refuse to extract files with direct…
neild Apr 15, 2026
c9f97f5
[release-branch.go1.26] html/template: fix escaping of URLs in meta c…
thatnealpatel Apr 22, 2026
3ae315a
[release-branch.go1.26] os: avoid panic when RemoveAll fails to remov…
neild Apr 6, 2026
722b68c
[release-branch.go1.26] cmd/compile: keep blank nodes alive in b.loop
JunyangShao Mar 12, 2026
7136366
[release-branch.go1.26] cmd/go: invalidate test cache when -coverpkg …
ryancurrah Apr 9, 2026
0bec633
[release-branch.go1.26] net/mail: fix quadratic consumePhrase behavior
thatnealpatel Apr 28, 2026
3baf3ee
[release-branch.go1.26] all: avoid unsafe StringToUTF16Ptr on Windows
neild Apr 24, 2026
8282c62
[release-branch.go1.26] cmd/go: reject sumdb response lacking module …
neild Apr 30, 2026
2dc996f
[release-branch.go1.26] go1.26.3
gopherbot May 7, 2026
e877d97
Merge branch 'release-branch.go1.26' of https://go.googlesource.com/g…
bradfitz May 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions VERSION
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
go1.26.2
time 2026-03-27T21:58:29Z
go1.26.3
time 2026-05-04T20:36:18Z
1 change: 1 addition & 0 deletions lib/fips140/certified.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v1.0.0-c2097c7c
2 changes: 1 addition & 1 deletion lib/fips140/inprocess.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.0.0-c2097c7c
v1.26.0
41 changes: 40 additions & 1 deletion src/cmd/compile/internal/bloop/bloop.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,16 @@ func preserveStmt(curFn *ir.Func, stmt ir.Node) (ret ir.Node) {
ret = stmt
switch n := stmt.(type) {
case *ir.AssignStmt:
// If the left hand side is blank, we need to assign it to a temp
// so that it can be kept alive.
if ir.IsBlank(n.X) {
tmp := typecheck.TempAt(n.Pos(), curFn, n.Y.Type())
n.X = tmp
n.Def = true
n.PtrInit().Append(typecheck.Stmt(ir.NewDecl(n.Pos(), ir.ODCL, tmp)))
stmt = typecheck.AssignExpr(n)
n = stmt.(*ir.AssignStmt)
}
// Peel down struct and slice indexing to get the names
name := getAddressableNameFromNode(n.X)
if name != nil {
Expand All @@ -154,7 +164,30 @@ func preserveStmt(curFn *ir.Func, stmt ir.Node) (ret ir.Node) {
}
case *ir.AssignListStmt:
ns := []ir.Node{}
for _, lhs := range n.Lhs {
hasBlank := false
for i, lhs := range n.Lhs {
if ir.IsBlank(lhs) {
// If the left hand side has blanks, we need to assign them to temps
// so that they can be kept alive.
var typ *types.Type
// AssignListStmt can have tuple or a list of expressions on the right hand side.
if len(n.Rhs) == 1 && n.Rhs[0].Type() != nil &&
n.Rhs[0].Type().IsTuple() &&
len(n.Lhs) == n.Rhs[0].Type().NumFields() {
typ = n.Rhs[0].Type().Field(i).Type
} else if len(n.Rhs) == len(n.Lhs) {
typ = n.Rhs[i].Type()
} else {
// Unrecognized shapes, skip?
base.WarnfAt(n.Pos(), "unrecognized shape for assign list stmt for blank assignment")
continue
}
tmp := typecheck.TempAt(n.Pos(), curFn, typ)
n.Lhs[i] = tmp
n.PtrInit().Append(typecheck.Stmt(ir.NewDecl(n.Pos(), ir.ODCL, tmp)))
hasBlank = true
lhs = tmp
}
name := getAddressableNameFromNode(lhs)
if name != nil {
debugName(name, n.Pos())
Expand All @@ -168,6 +201,12 @@ func preserveStmt(curFn *ir.Func, stmt ir.Node) (ret ir.Node) {
base.WarnfAt(n.Pos(), "expr is unknown to bloop pass")
}
}
if hasBlank {
// blank nodes are rewritten to temps, we need to typecheck the node again.
n.Def = true
stmt = typecheck.AssignExpr(n)
n = stmt.(*ir.AssignListStmt)
}
ret = keepAliveAt(ns, n)
case *ir.AssignOpStmt:
name := getAddressableNameFromNode(n.X)
Expand Down
17 changes: 15 additions & 2 deletions src/cmd/compile/internal/devirtualize/devirtualize.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,22 @@ func concreteType1(s *State, n ir.Node, seen map[*ir.Name]struct{}) (outT *types
continue
}
}
if t == nil || (typ != nil && !types.Identical(typ, t)) {
return nil
if t == nil {
return nil // unknown concrete type
}

// Methods are only declared on named types, and each named type
// is represented by a unique [*types.Type], thus pointer comparison
// is fine here.
//
// The only scenario where [types.IdenticalStrict] could help here is with
// unnamed struct types that embed another type (e.g. foo = struct { Impl }{}).
// However, such patterns are uncommon and not worth the additional complexity
// in the devirtualizer.
if typ != nil && typ != t {
return nil // assigned with a different type
}

typ = t
}

Expand Down
60 changes: 60 additions & 0 deletions src/cmd/compile/internal/loopvar/loopvar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package loopvar_test

import (
"internal/testenv"
"os"
"os/exec"
"path/filepath"
"regexp"
Expand Down Expand Up @@ -381,3 +382,62 @@ func TestLoopVarVersionDisableGoBuild(t *testing.T) {
t.Errorf("err=%v == nil", err)
}
}

// TestLoopVarLineDirective tests that loopvar version detection works correctly
// with line directives. This is a regression test for a bug where FileBase() was
// used instead of Base(), causing incorrect version lookup when line directives
// were present.
func TestLoopVarLineDirective(t *testing.T) {
switch runtime.GOOS {
case "linux", "darwin":
default:
t.Skipf("Slow test, usually avoid it, os=%s not linux or darwin", runtime.GOOS)
}
switch runtime.GOARCH {
case "amd64", "arm64":
default:
t.Skipf("Slow test, usually avoid it, arch=%s not amd64 or arm64", runtime.GOARCH)
}

testenv.MustHaveGoBuild(t)
gocmd := testenv.GoToolPath(t)
tmpdir := t.TempDir()
output := filepath.Join(tmpdir, "foo.exe")

// Create a go.mod file with Go 1.21 to test compatibility behavior.
// When building with a higher Go compiler, the loopvar should be created per-loop.
gomodPath := filepath.Join(tmpdir, "go.mod")
if err := os.WriteFile(gomodPath, []byte("module test\n\ngo 1.21\n"), 0644); err != nil {
t.Fatal(err)
}

// Copy the test file (with line directive) to the temporary module
testFile := "range_esc_closure_linedir.go"
srcPath := filepath.Join("testdata", testFile)
dstPath := filepath.Join(tmpdir, testFile)
src, err := os.ReadFile(srcPath)
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(dstPath, src, 0644); err != nil {
t.Fatal(err)
}

// Build the module (not as a single file, so go.mod is respected)
cmd := testenv.Command(t, gocmd, "build", "-o", output, ".")
cmd.Dir = tmpdir
b, err := cmd.CombinedOutput()
if err != nil {
t.Logf("build output: %s", b)
t.Fatal(err)
}
t.Logf("build output: %s", b)

cmd = testenv.Command(t, output)
b, err = cmd.CombinedOutput()
t.Logf("run output: %s", b)

if err != nil {
t.Errorf("expected success (exit code 0), got: %v", err)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2026 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//line range_esc_closure_linedir.go:5
package main

import "fmt"

var is []func() int

func main() {
var ints = []int{0, 0, 0}
for i := range ints {
is = append(is, func() int { return i })
}

for _, f := range is {
fmt.Println(f())
if f() != 2 {
panic("loop variable i: expected shared per-loop, but got distinct per-iteration")
}
}
}
2 changes: 1 addition & 1 deletion src/cmd/compile/internal/noder/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -1557,7 +1557,7 @@ func (w *writer) forStmt(stmt *syntax.ForStmt) {

func (w *writer) distinctVars(stmt *syntax.ForStmt) bool {
lv := base.Debug.LoopVar
fileVersion := w.p.info.FileVersions[stmt.Pos().Base()]
fileVersion := w.p.info.FileVersions[stmt.Pos().FileBase()]
is122 := fileVersion == "" || version.Compare(fileVersion, "go1.22") >= 0

// Turning off loopvar for 1.22 is only possible with loopvarhash=qn
Expand Down
6 changes: 6 additions & 0 deletions src/cmd/compile/internal/ssa/loopbce.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ func findIndVar(f *Func) []indVar {
if step == 0 {
continue
}
// step == minInt64 cannot be safely negated below, because -step
// overflows back to minInt64. The later underflow checks need a
// positive magnitude, so reject this case here.
if step == minSignedValue(ind.Type) {
continue
}

// startBody is the edge that eventually returns to the loop header.
var startBody Edge
Expand Down
118 changes: 0 additions & 118 deletions src/cmd/compile/internal/ssa/prove.go
Original file line number Diff line number Diff line change
Expand Up @@ -1517,124 +1517,6 @@ func prove(f *Func) {
// Since this induction variable is not used for anything but counting the iterations,
// no point in putting it into the facts table.
}

// try to rewrite to a downward counting loop checking against start if the
// loop body does not depend on ind or nxt and end is known before the loop.
// This reduces pressure on the register allocator because this does not need
// to use end on each iteration anymore. We compare against the start constant instead.
// That means this code:
//
// loop:
// ind = (Phi (Const [x]) nxt),
// if ind < end
// then goto enter_loop
// else goto exit_loop
//
// enter_loop:
// do something without using ind nor nxt
// nxt = inc + ind
// goto loop
//
// exit_loop:
//
// is rewritten to:
//
// loop:
// ind = (Phi end nxt)
// if (Const [x]) < ind
// then goto enter_loop
// else goto exit_loop
//
// enter_loop:
// do something without using ind nor nxt
// nxt = ind - inc
// goto loop
//
// exit_loop:
//
// this is better because it only requires to keep ind then nxt alive while looping,
// while the original form keeps ind then nxt and end alive
start, end := v.min, v.max
if v.flags&indVarCountDown != 0 {
start, end = end, start
}

if !start.isGenericIntConst() {
// if start is not a constant we would be winning nothing from inverting the loop
continue
}
if end.isGenericIntConst() {
// TODO: if both start and end are constants we should rewrite such that the comparison
// is against zero and nxt is ++ or -- operation
// That means:
// for i := 2; i < 11; i += 2 {
// should be rewritten to:
// for i := 5; 0 < i; i-- {
continue
}

if end.Block == ind.Block {
// we can't rewrite loops where the condition depends on the loop body
// this simple check is forced to work because if this is true a Phi in ind.Block must exist
continue
}

check := ind.Block.Controls[0]
// invert the check
check.Args[0], check.Args[1] = check.Args[1], check.Args[0]

// swap start and end in the loop
for i, v := range check.Args {
if v != end {
continue
}

check.SetArg(i, start)
goto replacedEnd
}
panic(fmt.Sprintf("unreachable, ind: %v, start: %v, end: %v", ind, start, end))
replacedEnd:

for i, v := range ind.Args {
if v != start {
continue
}

ind.SetArg(i, end)
goto replacedStart
}
panic(fmt.Sprintf("unreachable, ind: %v, start: %v, end: %v", ind, start, end))
replacedStart:

if nxt.Args[0] != ind {
// unlike additions subtractions are not commutative so be sure we get it right
nxt.Args[0], nxt.Args[1] = nxt.Args[1], nxt.Args[0]
}

switch nxt.Op {
case OpAdd8:
nxt.Op = OpSub8
case OpAdd16:
nxt.Op = OpSub16
case OpAdd32:
nxt.Op = OpSub32
case OpAdd64:
nxt.Op = OpSub64
case OpSub8:
nxt.Op = OpAdd8
case OpSub16:
nxt.Op = OpAdd16
case OpSub32:
nxt.Op = OpAdd32
case OpSub64:
nxt.Op = OpAdd64
default:
panic("unreachable")
}

if f.pass.debug > 0 {
f.Warnl(ind.Pos, "Inverted loop iteration")
}
}

ft := newFactsTable(f)
Expand Down
4 changes: 2 additions & 2 deletions src/cmd/compile/internal/types2/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
for _, u := range typeset(y.typ) {
if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
// typeset ⊇ {[]byte}
} else if isString(u) {
} else if u != nil && isString(u) {
// typeset ⊇ {string}
hasString = true
} else {
Expand Down Expand Up @@ -375,7 +375,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
for _, u := range typeset(y.typ) {
if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
// typeset ⊇ {[]byte}
} else if isString(u) {
} else if u != nil && isString(u) {
// typeset ⊇ {string}
} else {
special = false
Expand Down
6 changes: 5 additions & 1 deletion src/cmd/compile/internal/types2/issues_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -631,9 +631,13 @@ func TestIssue55030(t *testing.T) {
makeSig := func(typ Type, valid bool) {
if !valid {
defer func() {
if recover() == nil {
r := recover()
if r == nil {
panic("NewSignatureType panic expected")
}
if _, ok := r.(string); !ok {
panic("NewSignatureType string panic expected")
}
}()
}
par := NewParam(nopos, nil, "", typ)
Expand Down
3 changes: 3 additions & 0 deletions src/cmd/compile/internal/types2/signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params
last := params.At(n - 1).typ
var S *Slice
for t := range typeset(last) {
if t == nil {
break
}
var s *Slice
if isString(t) {
s = NewSlice(universeByte)
Expand Down
6 changes: 3 additions & 3 deletions src/cmd/compile/internal/types2/under.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ func all(t Type, f func(t, u Type) bool) bool {
// typeset is an iterator over the (type/underlying type) pairs of the
// specific type terms of the type set implied by t.
// If t is a type parameter, the implied type set is the type set of t's constraint.
// In that case, if there are no specific terms, typeset calls yield with (nil, nil).
// In this case, if there are no specific terms, the iterator produces (nil, nil).
// If t is not a type parameter, the implied type set consists of just t.
// In any case, typeset is guaranteed to call yield at least once.
// In any case, typeset is guaranteed to produce at least one set of results.
func typeset(t Type) iter.Seq2[Type, Type] {
return func(yield func(t, u Type) bool) {
_ = all(t, yield)
all(t, yield)
}
}

Expand Down
Loading
Loading