[go] go/types, types2: check for direct cycles as a separate phase

22 views
Skip to first unread message

Mark Freeman (Gerrit)

unread,
Oct 24, 2025, 4:28:07 PMOct 24
to goph...@pubsubhelper.golang.org, Robert Griesemer, Go LUCI, golang-co...@googlegroups.com
Attention needed from Robert Griesemer

Mark Freeman voted Commit-Queue+1

Commit-Queue+1
Open in Gerrit

Related details

Attention is currently required from:
  • Robert Griesemer
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
Gerrit-Change-Number: 714760
Gerrit-PatchSet: 2
Gerrit-Owner: Mark Freeman <markf...@google.com>
Gerrit-Reviewer: Mark Freeman <markf...@google.com>
Gerrit-Reviewer: Robert Griesemer <g...@google.com>
Gerrit-Attention: Robert Griesemer <g...@google.com>
Gerrit-Comment-Date: Fri, 24 Oct 2025 20:28:03 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
unsatisfied_requirement
satisfied_requirement
open
diffy

Mark Freeman (Gerrit)

unread,
Oct 27, 2025, 3:03:43 PMOct 27
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
Attention needed from Robert Griesemer

Mark Freeman uploaded new patchset

Mark Freeman uploaded patch set #3 to this change.
Following approvals got outdated and were removed:
  • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI
Open in Gerrit

Related details

Attention is currently required from:
  • Robert Griesemer
Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: newpatchset
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
    Gerrit-Change-Number: 714760
    Gerrit-PatchSet: 3
    unsatisfied_requirement
    satisfied_requirement
    open
    diffy

    Mark Freeman (Gerrit)

    unread,
    Oct 27, 2025, 3:30:55 PMOct 27
    to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
    Attention needed from Robert Griesemer

    Mark Freeman uploaded new patchset

    Mark Freeman uploaded patch set #5 to this change.
    Open in Gerrit

    Related details

    Attention is currently required from:
    • Robert Griesemer
    Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: newpatchset
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
    Gerrit-Change-Number: 714760
    Gerrit-PatchSet: 5
    unsatisfied_requirement
    satisfied_requirement
    open
    diffy

    Mark Freeman (Gerrit)

    unread,
    Oct 27, 2025, 3:34:09 PMOct 27
    to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
    Attention needed from Robert Griesemer

    Mark Freeman uploaded new patchset

    Mark Freeman uploaded patch set #6 to this change.
    Open in Gerrit

    Related details

    Attention is currently required from:
    • Robert Griesemer
    Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: newpatchset
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
    Gerrit-Change-Number: 714760
    Gerrit-PatchSet: 6
    unsatisfied_requirement
    satisfied_requirement
    open
    diffy

    Robert Griesemer (Gerrit)

    unread,
    Oct 27, 2025, 3:58:21 PMOct 27
    to Mark Freeman, goph...@pubsubhelper.golang.org, Go LUCI, golang-co...@googlegroups.com
    Attention needed from Mark Freeman

    Robert Griesemer added 3 comments

    Patchset-level comments
    File-level comment, Patchset 6 (Latest):
    Robert Griesemer . resolved

    Some initial drive-by comments.

    File src/cmd/compile/internal/types2/cycles.go
    Line 18, Patchset 6 (Latest): c.objSeen = make(map[Object]int)
    Robert Griesemer . unresolved

    Rather than storing objSeen - which appears to be used in this code only - in Checker, in this case I would pass it to directCycle explicitly.
    Then it will also automatically disappear from the root set when you return from this function (no need to nil out the field).

    Line 21, Patchset 6 (Latest): // any order is fine
    Robert Griesemer . unresolved

    For reproduceability we probably need to go in a deterministic order.
    We may have test cases that produce different results depending on order.
    Or we may beed it for debuggability (in that case, we could enable the deterministic order when debug is enabled).

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Mark Freeman
    Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 6
      Gerrit-Owner: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
      Gerrit-Attention: Mark Freeman <markf...@google.com>
      Gerrit-Comment-Date: Mon, 27 Oct 2025 19:58:14 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 27, 2025, 4:22:47 PMOct 27
      to goph...@pubsubhelper.golang.org, Robert Griesemer, Go LUCI, golang-co...@googlegroups.com
      Attention needed from Robert Griesemer

      Mark Freeman added 2 comments

      File src/cmd/compile/internal/types2/cycles.go
      Line 18, Patchset 6 (Latest): c.objSeen = make(map[Object]int)
      Robert Griesemer . unresolved

      Rather than storing objSeen - which appears to be used in this code only - in Checker, in this case I would pass it to directCycle explicitly.
      Then it will also automatically disappear from the root set when you return from this function (no need to nil out the field).

      Mark Freeman

      Fair — I had meant for this to be an alternative to the `grey` color used in the main type checking pass. Recall that the value of the grey color is the index in the object path, which might be a bit clever.

      Do you think it's worth replacing that?

      Alternatively, we could also do grey color encoding here, but then most objects will go from grey back to white which seems odd.

      Line 21, Patchset 6 (Latest): // any order is fine
      Robert Griesemer . unresolved

      For reproduceability we probably need to go in a deterministic order.
      We may have test cases that produce different results depending on order.
      Or we may beed it for debuggability (in that case, we could enable the deterministic order when debug is enabled).

      Mark Freeman

      Agree, it seems a bit less subtle if we go in deterministic order. Although we do end up doing this sort in a few places, which might not be the best performance wise.

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Robert Griesemer
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 6
      Gerrit-Owner: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
      Gerrit-Attention: Robert Griesemer <g...@google.com>
      Gerrit-Comment-Date: Mon, 27 Oct 2025 20:22:43 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      Comment-In-Reply-To: Robert Griesemer <g...@google.com>
      unsatisfied_requirement
      open
      diffy

      Robert Griesemer (Gerrit)

      unread,
      Oct 27, 2025, 4:33:28 PMOct 27
      to Mark Freeman, goph...@pubsubhelper.golang.org, Go LUCI, golang-co...@googlegroups.com
      Attention needed from Mark Freeman

      Robert Griesemer added 2 comments

      File src/cmd/compile/internal/types2/cycles.go
      Line 18, Patchset 6 (Latest): c.objSeen = make(map[Object]int)
      Robert Griesemer . unresolved

      Rather than storing objSeen - which appears to be used in this code only - in Checker, in this case I would pass it to directCycle explicitly.
      Then it will also automatically disappear from the root set when you return from this function (no need to nil out the field).

      Mark Freeman

      Fair — I had meant for this to be an alternative to the `grey` color used in the main type checking pass. Recall that the value of the grey color is the index in the object path, which might be a bit clever.

      Do you think it's worth replacing that?

      Alternatively, we could also do grey color encoding here, but then most objects will go from grey back to white which seems odd.

      Robert Griesemer

      I agree that the grey color scheme is subtle and the machinery spread. Would be nice to replace that. But is this code sufficient? Don't we need to run through all kinds of RHS expressions etc.? I am probably missing something (haven't had time to look it through in detail yet).

      Line 21, Patchset 6 (Latest): // any order is fine
      Robert Griesemer . unresolved

      For reproduceability we probably need to go in a deterministic order.
      We may have test cases that produce different results depending on order.
      Or we may beed it for debuggability (in that case, we could enable the deterministic order when debug is enabled).

      Mark Freeman

      Agree, it seems a bit less subtle if we go in deterministic order. Although we do end up doing this sort in a few places, which might not be the best performance wise.

      Robert Griesemer

      Understood, but in the past we used map random iteration orders and we endep up fixing all of those for reproducability.

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Mark Freeman
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 6
      Gerrit-Owner: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
      Gerrit-Attention: Mark Freeman <markf...@google.com>
      Gerrit-Comment-Date: Mon, 27 Oct 2025 20:33:22 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      Comment-In-Reply-To: Robert Griesemer <g...@google.com>
      Comment-In-Reply-To: Mark Freeman <markf...@google.com>
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 27, 2025, 4:46:52 PMOct 27
      to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
      Attention needed from Mark Freeman

      Mark Freeman uploaded new patchset

      Mark Freeman uploaded patch set #7 to this change.
      Open in Gerrit

      Related details

      Attention is currently required from:
      • Mark Freeman
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: newpatchset
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 7
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 27, 2025, 4:56:32 PMOct 27
      to goph...@pubsubhelper.golang.org, Robert Griesemer, Go LUCI, golang-co...@googlegroups.com
      Attention needed from Robert Griesemer

      Mark Freeman added 2 comments

      File src/cmd/compile/internal/types2/cycles.go
      Line 18, Patchset 6: c.objSeen = make(map[Object]int)
      Robert Griesemer . unresolved

      Rather than storing objSeen - which appears to be used in this code only - in Checker, in this case I would pass it to directCycle explicitly.
      Then it will also automatically disappear from the root set when you return from this function (no need to nil out the field).

      Mark Freeman

      Fair — I had meant for this to be an alternative to the `grey` color used in the main type checking pass. Recall that the value of the grey color is the index in the object path, which might be a bit clever.

      Do you think it's worth replacing that?

      Alternatively, we could also do grey color encoding here, but then most objects will go from grey back to white which seems odd.

      Robert Griesemer

      I agree that the grey color scheme is subtle and the machinery spread. Would be nice to replace that. But is this code sufficient? Don't we need to run through all kinds of RHS expressions etc.? I am probably missing something (haven't had time to look it through in detail yet).

      Mark Freeman

      I believe the mechanism (a map) is sufficient to replicate the grey color encoding, but the code here does not eliminate use of grey color encoding. We would need to go through `decl.go` in a separate CL.

      I'm just pointing out that by adding `Checker.objSeen` in this CL, it makes follow-up work (marginally) easier.

      Line 21, Patchset 6: // any order is fine
      Robert Griesemer . unresolved

      For reproduceability we probably need to go in a deterministic order.
      We may have test cases that produce different results depending on order.
      Or we may beed it for debuggability (in that case, we could enable the deterministic order when debug is enabled).

      Mark Freeman

      Agree, it seems a bit less subtle if we go in deterministic order. Although we do end up doing this sort in a few places, which might not be the best performance wise.

      Robert Griesemer

      Understood, but in the past we used map random iteration orders and we endep up fixing all of those for reproducability.

      Mark Freeman

      I'm in agreement, it seems like the right thing to do. I've extracted the relevant logic in CL 715480 so we don't have to redo it here.

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Robert Griesemer
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 8
      Gerrit-Owner: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
      Gerrit-Attention: Robert Griesemer <g...@google.com>
      Gerrit-Comment-Date: Mon, 27 Oct 2025 20:56:27 +0000
      unsatisfied_requirement
      open
      diffy

      Robert Griesemer (Gerrit)

      unread,
      Oct 27, 2025, 6:11:41 PMOct 27
      to Mark Freeman, goph...@pubsubhelper.golang.org, Go LUCI, golang-co...@googlegroups.com
      Attention needed from Mark Freeman

      Robert Griesemer added 12 comments

      Patchset-level comments
      File-level comment, Patchset 8 (Latest):
      Robert Griesemer . resolved

      I

      File src/cmd/compile/internal/types2/cycles.go
      Line 15, Patchset 8 (Latest):// While seemingly trivial, freedom from direct cycles guarantees that the type
      Robert Griesemer . unresolved

      Just say: Freedom from direct cycles guarantees that ...

      Line 18, Patchset 6: c.objSeen = make(map[Object]int)
      Robert Griesemer . resolved

      Rather than storing objSeen - which appears to be used in this code only - in Checker, in this case I would pass it to directCycle explicitly.
      Then it will also automatically disappear from the root set when you return from this function (no need to nil out the field).

      Mark Freeman

      Fair — I had meant for this to be an alternative to the `grey` color used in the main type checking pass. Recall that the value of the grey color is the index in the object path, which might be a bit clever.

      Do you think it's worth replacing that?

      Alternatively, we could also do grey color encoding here, but then most objects will go from grey back to white which seems odd.

      Robert Griesemer

      I agree that the grey color scheme is subtle and the machinery spread. Would be nice to replace that. But is this code sufficient? Don't we need to run through all kinds of RHS expressions etc.? I am probably missing something (haven't had time to look it through in detail yet).

      Mark Freeman

      I believe the mechanism (a map) is sufficient to replicate the grey color encoding, but the code here does not eliminate use of grey color encoding. We would need to go through `decl.go` in a separate CL.

      I'm just pointing out that by adding `Checker.objSeen` in this CL, it makes follow-up work (marginally) easier.

      Robert Griesemer

      Acknowledged

      Line 19, Patchset 8 (Latest): defer (func() { c.objSeen = nil })()
      Robert Griesemer . unresolved

      again: pass objSeen down to directCyle instead of storing in Checker?

      Line 21, Patchset 6: // any order is fine
      Robert Griesemer . resolved

      For reproduceability we probably need to go in a deterministic order.
      We may have test cases that produce different results depending on order.
      Or we may beed it for debuggability (in that case, we could enable the deterministic order when debug is enabled).

      Mark Freeman

      Agree, it seems a bit less subtle if we go in deterministic order. Although we do end up doing this sort in a few places, which might not be the best performance wise.

      Robert Griesemer

      Understood, but in the past we used map random iteration orders and we endep up fixing all of those for reproducability.

      Mark Freeman

      I'm in agreement, it seems like the right thing to do. I've extracted the relevant logic in CL 715480 so we don't have to redo it here.

      Robert Griesemer

      Acknowledged

      Line 22, Patchset 8 (Latest): if t, ok := obj.(*TypeName); ok {
      Robert Griesemer . unresolved

      the convention in the type checker (when we don't _need_ and ok) is to avoid it:

      ```
      if tname, _ := obj.(*TypeName); tname != nil {
      ```

      Line 29, Patchset 8 (Latest):func (c *Checker) directCycle(t *TypeName) (err bool) {
      Robert Griesemer . unresolved

      s/err/ok/ and swap the default: this way the default is "not ok", and we are also not using the name "err" typically used for a variable of error type in an unusual way.

      Line 45, Patchset 8 (Latest): return false
      Robert Griesemer . unresolved

      if you change the result to (ok bool), the error case can just be "return"

      Line 60, Patchset 8 (Latest): defer (func() { delete(c.objSeen, c.pop()) })()
      Robert Griesemer . unresolved

      no need for ()'s around the func literal

      Line 62, Patchset 8 (Latest): if n, ok := c.objMap[t].tdecl.Type.(*syntax.Name); ok {
      Robert Griesemer . unresolved

      s/ok/n != nil/

      Line 68, Patchset 8 (Latest): // direct cycles can only exist through package-level type names
      Robert Griesemer . unresolved

      this comment seems a bit out of place

      File src/cmd/compile/internal/types2/named.go
      Line 621, Patchset 8 (Latest): var ts []*Named
      Robert Griesemer . unresolved

      s/ts/path/

      ts leaves one guessing. Path seems clearer.

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Mark Freeman
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 8
      Gerrit-Owner: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
      Gerrit-Attention: Mark Freeman <markf...@google.com>
      Gerrit-Comment-Date: Mon, 27 Oct 2025 22:11:36 +0000
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 28, 2025, 11:30:20 AMOct 28
      to goph...@pubsubhelper.golang.org, Robert Griesemer, Go LUCI, golang-co...@googlegroups.com
      Attention needed from Robert Griesemer

      Mark Freeman added 3 comments

      File src/cmd/compile/internal/types2/cycles.go
      Line 19, Patchset 8: defer (func() { c.objSeen = nil })()
      Robert Griesemer . unresolved

      again: pass objSeen down to directCyle instead of storing in Checker?

      Mark Freeman

      I'm planning to follow this with replacement of the `grey` color encoding with `objSeen`, in which case I think I need it at the `Checker` level, no?

      Though I don't explicitly need it in this CL if we don't want it at the Checker level until then.

      Line 22, Patchset 8: if t, ok := obj.(*TypeName); ok {
      Robert Griesemer . unresolved

      the convention in the type checker (when we don't _need_ and ok) is to avoid it:

      ```
      if tname, _ := obj.(*TypeName); tname != nil {
      ```

      Mark Freeman

      I'm not opposed to following the convention, but I am curious why this is the convention.

      IMO, I just need whether the assertion succeeded (without a panic) and don't want to know the appropriate default value to compare with `tname`.

      It also seemingly invites a shortening to `if tname :=` which panics.

      Line 68, Patchset 8: // direct cycles can only exist through package-level type names
      Robert Griesemer . unresolved

      this comment seems a bit out of place

      Mark Freeman

      It's meant to suggest "we don't need to check anything else" — otherwise it's easy to wonder whether we missed consideration of imports, types in the universe scope, etc.

      Perhaps a more direct wording of that?

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Robert Griesemer
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 10
      Gerrit-Owner: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
      Gerrit-Attention: Robert Griesemer <g...@google.com>
      Gerrit-Comment-Date: Tue, 28 Oct 2025 15:30:15 +0000
      unsatisfied_requirement
      open
      diffy

      Robert Griesemer (Gerrit)

      unread,
      Oct 28, 2025, 11:40:54 AMOct 28
      to Mark Freeman, goph...@pubsubhelper.golang.org, Go LUCI, golang-co...@googlegroups.com
      Attention needed from Mark Freeman

      Robert Griesemer added 2 comments

      File src/cmd/compile/internal/types2/cycles.go
      Line 19, Patchset 8: defer (func() { c.objSeen = nil })()
      Robert Griesemer . resolved

      again: pass objSeen down to directCyle instead of storing in Checker?

      Mark Freeman

      I'm planning to follow this with replacement of the `grey` color encoding with `objSeen`, in which case I think I need it at the `Checker` level, no?

      Though I don't explicitly need it in this CL if we don't want it at the Checker level until then.

      Robert Griesemer

      Acknowledged

      Line 22, Patchset 8: if t, ok := obj.(*TypeName); ok {
      Robert Griesemer . unresolved

      the convention in the type checker (when we don't _need_ and ok) is to avoid it:

      ```
      if tname, _ := obj.(*TypeName); tname != nil {
      ```

      Mark Freeman

      I'm not opposed to following the convention, but I am curious why this is the convention.

      IMO, I just need whether the assertion succeeded (without a panic) and don't want to know the appropriate default value to compare with `tname`.

      It also seemingly invites a shortening to `if tname :=` which panics.

      Robert Griesemer

      With t, ok, you can have ok == true but t == nil, leading to a panic. With tname != nil we check both ok and tname != nil at the same time. Also, it removes a name (ok) from the scope.

      Admittedly, tname != nil may hide an error elsewhere, when tname shouldn't be nil in the first place. But it seemed that not panicking was more important at some point.

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Mark Freeman
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 10
      Gerrit-Owner: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
      Gerrit-Attention: Mark Freeman <markf...@google.com>
      Gerrit-Comment-Date: Tue, 28 Oct 2025 15:40:48 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      Comment-In-Reply-To: Robert Griesemer <g...@google.com>
      Comment-In-Reply-To: Mark Freeman <markf...@google.com>
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 28, 2025, 2:29:35 PMOct 28
      to goph...@pubsubhelper.golang.org, Robert Griesemer, Go LUCI, golang-co...@googlegroups.com
      Attention needed from Robert Griesemer

      Mark Freeman added 1 comment

      File src/cmd/compile/internal/types2/cycles.go
      Line 22, Patchset 8: if t, ok := obj.(*TypeName); ok {
      Robert Griesemer . unresolved

      the convention in the type checker (when we don't _need_ and ok) is to avoid it:

      ```
      if tname, _ := obj.(*TypeName); tname != nil {
      ```

      Mark Freeman

      I'm not opposed to following the convention, but I am curious why this is the convention.

      IMO, I just need whether the assertion succeeded (without a panic) and don't want to know the appropriate default value to compare with `tname`.

      It also seemingly invites a shortening to `if tname :=` which panics.

      Robert Griesemer

      With t, ok, you can have ok == true but t == nil, leading to a panic. With tname != nil we check both ok and tname != nil at the same time. Also, it removes a name (ok) from the scope.

      Admittedly, tname != nil may hide an error elsewhere, when tname shouldn't be nil in the first place. But it seemed that not panicking was more important at some point.

      Mark Freeman

      Got it — I figured we didn't want to pollute the scope, but didn't know we were taking a cautious approach to nilness.

      In this case, I don't expect `objLst` to hold `nil` and would like to panic if that invariant is violated (even if only in debug mode). IMO, making assumptions about those invariants clearer seems quite useful.

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Robert Griesemer
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 10
      Gerrit-Owner: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
      Gerrit-Attention: Robert Griesemer <g...@google.com>
      Gerrit-Comment-Date: Tue, 28 Oct 2025 18:29:29 +0000
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 28, 2025, 3:19:07 PMOct 28
      to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
      Attention needed from Robert Griesemer

      Mark Freeman uploaded new patchset

      Mark Freeman uploaded patch set #11 to this change.
      Open in Gerrit

      Related details

      Attention is currently required from:
      • Robert Griesemer
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: newpatchset
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 11
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 28, 2025, 4:08:07 PMOct 28
      to goph...@pubsubhelper.golang.org, Robert Griesemer, Go LUCI, golang-co...@googlegroups.com
      Attention needed from Robert Griesemer

      Mark Freeman added 4 comments

      File src/cmd/compile/internal/types2/cycles.go
      Line 15, Patchset 8:// While seemingly trivial, freedom from direct cycles guarantees that the type
      Robert Griesemer . resolved

      Just say: Freedom from direct cycles guarantees that ...

      Mark Freeman

      Done

      Line 29, Patchset 8:func (c *Checker) directCycle(t *TypeName) (err bool) {
      Robert Griesemer . resolved

      s/err/ok/ and swap the default: this way the default is "not ok", and we are also not using the name "err" typically used for a variable of error type in an unusual way.

      Mark Freeman

      Done

      Line 45, Patchset 8: return false
      Robert Griesemer . resolved

      if you change the result to (ok bool), the error case can just be "return"

      Mark Freeman

      Let's avoid the naked return there, it's a bit tricky that the `ok` (whether the assertion succeeded) is being returned.

      Line 60, Patchset 8: defer (func() { delete(c.objSeen, c.pop()) })()
      Robert Griesemer . resolved

      no need for ()'s around the func literal

      Mark Freeman

      Done

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Robert Griesemer
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 11
      Gerrit-Owner: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
      Gerrit-Attention: Robert Griesemer <g...@google.com>
      Gerrit-Comment-Date: Tue, 28 Oct 2025 20:08:04 +0000
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 28, 2025, 4:14:56 PMOct 28
      to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
      Attention needed from Robert Griesemer

      Mark Freeman uploaded new patchset

      Mark Freeman uploaded patch set #12 to this change.
      Open in Gerrit

      Related details

      Attention is currently required from:
      • Robert Griesemer
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: newpatchset
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 12
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 28, 2025, 4:16:26 PMOct 28
      to goph...@pubsubhelper.golang.org, Robert Griesemer, Go LUCI, golang-co...@googlegroups.com
      Attention needed from Robert Griesemer

      Mark Freeman added 1 comment

      File src/cmd/compile/internal/types2/named.go
      Line 621, Patchset 8: var ts []*Named
      Robert Griesemer . resolved

      s/ts/path/

      ts leaves one guessing. Path seems clearer.

      Mark Freeman

      Done

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Robert Griesemer
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 12
      Gerrit-Owner: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
      Gerrit-Attention: Robert Griesemer <g...@google.com>
      Gerrit-Comment-Date: Tue, 28 Oct 2025 20:16:23 +0000
      unsatisfied_requirement
      open
      diffy

      Robert Griesemer (Gerrit)

      unread,
      Oct 28, 2025, 4:28:14 PMOct 28
      to Mark Freeman, goph...@pubsubhelper.golang.org, Go LUCI, golang-co...@googlegroups.com
      Attention needed from Mark Freeman

      Robert Griesemer added 4 comments

      File src/cmd/compile/internal/types2/cycles.go
      Line 22, Patchset 8: if t, ok := obj.(*TypeName); ok {
      Robert Griesemer . resolved

      the convention in the type checker (when we don't _need_ and ok) is to avoid it:

      ```
      if tname, _ := obj.(*TypeName); tname != nil {
      ```

      Mark Freeman

      I'm not opposed to following the convention, but I am curious why this is the convention.

      IMO, I just need whether the assertion succeeded (without a panic) and don't want to know the appropriate default value to compare with `tname`.

      It also seemingly invites a shortening to `if tname :=` which panics.

      Robert Griesemer

      With t, ok, you can have ok == true but t == nil, leading to a panic. With tname != nil we check both ok and tname != nil at the same time. Also, it removes a name (ok) from the scope.

      Admittedly, tname != nil may hide an error elsewhere, when tname shouldn't be nil in the first place. But it seemed that not panicking was more important at some point.

      Mark Freeman

      Got it — I figured we didn't want to pollute the scope, but didn't know we were taking a cautious approach to nilness.

      In this case, I don't expect `objLst` to hold `nil` and would like to panic if that invariant is violated (even if only in debug mode). IMO, making assumptions about those invariants clearer seems quite useful.

      Robert Griesemer

      Acknowledged

      Line 62, Patchset 8: if n, ok := c.objMap[t].tdecl.Type.(*syntax.Name); ok {
      Robert Griesemer . resolved

      s/ok/n != nil/

      Robert Griesemer

      Acknowledged

      Line 68, Patchset 8: // direct cycles can only exist through package-level type names
      Robert Griesemer . resolved

      this comment seems a bit out of place

      Mark Freeman

      It's meant to suggest "we don't need to check anything else" — otherwise it's easy to wonder whether we missed consideration of imports, types in the universe scope, etc.

      Perhaps a more direct wording of that?

      Robert Griesemer

      Acknowledged

      File src/cmd/compile/internal/types2/named.go
      Line 633, Patchset 12 (Parent): if i, ok := seen[t]; ok {
      Robert Griesemer . unresolved

      I wonder if it would make sense to leave (some of) this in debug mode: we panic if there's a cycle. That might be useful in case we run into unexpected cycles (not that I can think of one at the moment).

      I'd declare the seen variable, and in debug mode, allocate it eagerly (so you don't need lines 652-654), and then have a simple check:

      ```
      if debug {
      assert(!seen[t])
      seen[t] = true
      }
      ```
      Open in Gerrit

      Related details

      Attention is currently required from:
      • Mark Freeman
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 12
      Gerrit-Owner: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
      Gerrit-Attention: Mark Freeman <markf...@google.com>
      Gerrit-Comment-Date: Tue, 28 Oct 2025 20:28:10 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      Comment-In-Reply-To: Robert Griesemer <g...@google.com>
      Comment-In-Reply-To: Mark Freeman <markf...@google.com>
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 28, 2025, 5:13:15 PMOct 28
      to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
      Attention needed from Mark Freeman

      Mark Freeman uploaded new patchset

      Mark Freeman uploaded patch set #13 to this change.
      Open in Gerrit

      Related details

      Attention is currently required from:
      • Mark Freeman
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: newpatchset
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 13
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 28, 2025, 5:16:06 PMOct 28
      to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
      Attention needed from Mark Freeman

      Mark Freeman uploaded new patchset

      Mark Freeman uploaded patch set #14 to this change.
      Open in Gerrit

      Related details

      Attention is currently required from:
      • Mark Freeman
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: newpatchset
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 14
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 28, 2025, 5:16:47 PMOct 28
      to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
      Attention needed from Mark Freeman

      Mark Freeman uploaded new patchset

      Mark Freeman uploaded patch set #15 to this change.
      Open in Gerrit

      Related details

      Attention is currently required from:
      • Mark Freeman
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: newpatchset
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 15
      unsatisfied_requirement
      open
      diffy

      Robert Griesemer (Gerrit)

      unread,
      Oct 28, 2025, 9:11:14 PMOct 28
      to Mark Freeman, goph...@pubsubhelper.golang.org, Go LUCI, golang-co...@googlegroups.com
      Attention needed from Mark Freeman

      Robert Griesemer added 8 comments

      Patchset-level comments
      File-level comment, Patchset 15 (Latest):
      Robert Griesemer . resolved

      Now that I spent some more time on this, I think this code needs more work.
      Apologies for not noticing sooner.

      File src/cmd/compile/internal/types2/check.go
      Line 178, Patchset 15 (Latest): objPath []Object // path of object dependencies during type-checking (for cycle reporting)
      Robert Griesemer . unresolved

      not sure what changed on this line

      Line 501, Patchset 15 (Latest): check.directCycles(make(map[Object]int))
      Robert Griesemer . unresolved

      I don't see a reason for passing this map in as an argument.
      Why not allocate it locally, in directCycles?

      File src/cmd/compile/internal/types2/cycles.go
      Line 17, Patchset 15 (Latest):func (c *Checker) directCycles(seen map[Object]int) {
      Robert Griesemer . unresolved

      s/c/check/ for consistency with the rest of the code.

      I don't object to renaming, but let's do it consistently across all files.
      The consistency simply makes it easier to read the code.

      Line 25, Patchset 15 (Latest):// directCycle drives [Checker.directCycles] for a given type.
      Robert Griesemer . unresolved

      This doc string is not super helpful, also, the name is not quite right.
      Better to explain the result.
      But see below.

      Line 40, Patchset 15 (Latest): // if we reach an already-reported type, don't report it again
      Robert Griesemer . unresolved

      // If we reach a previously reported type, don't report it again.

      (Note that we may not have reported anything before. Also, for comments that are proper sentences, please use capitalization and periods. For others its ok to keep it all lowercase without punctuation.)

      Line 61, Patchset 15 (Latest): return c.directCycle(t, seen)
      Robert Griesemer . unresolved

      I think we should avoid the recursion here. There is really no need, it's basically a tail recursion. The primary benefit is that tracing would show the entire path, but it's nested, which is not great either.

      Just have a loop. Then you also don't need to return a result, actually.
      And the seen map is always empty at the end, so I wonder if we can be smarter about this whole thing.

      Let's say we represent `type A B` as A -> B, and use . to mark a literal (end of path). We may have a situation like this:

      A -> B -> C -> D -> E -> .
      F -> C -> D -> E -> .
      G -> H -> D -> E -> .

      Even though we know that after starting with A there is no cycle, we iterate (recurse!) through much of the path again and again for B, C, etc. That's potentially quadratic. We only stop early if we find a black object, and we only find one if we have a cycle.

      We should absolutely avoid repeated traversals. In normal programs it won't be an issue, but it would be easy to generate a bad program that would consume a lot of time to check.

      This needs some more work.

      Line 65, Patchset 15 (Latest): // direct cycles can only exist through package-level type names,
      Robert Griesemer . unresolved

      make a proper sentence, please

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Mark Freeman
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 15
      Gerrit-Owner: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
      Gerrit-Attention: Mark Freeman <markf...@google.com>
      Gerrit-Comment-Date: Wed, 29 Oct 2025 01:11:08 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 29, 2025, 12:23:37 PMOct 29
      to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
      Attention needed from Mark Freeman

      Mark Freeman uploaded new patchset

      Mark Freeman uploaded patch set #16 to this change.
      Open in Gerrit

      Related details

      Attention is currently required from:
      • Mark Freeman
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: newpatchset
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 16
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 29, 2025, 12:28:50 PMOct 29
      to goph...@pubsubhelper.golang.org, Robert Griesemer, Go LUCI, golang-co...@googlegroups.com

      Mark Freeman added 5 comments

      File src/cmd/compile/internal/types2/check.go
      Line 178, Patchset 15: objPath []Object // path of object dependencies during type-checking (for cycle reporting)
      Robert Griesemer . resolved

      not sure what changed on this line

      Mark Freeman

      s/type inference/type-checking

      I believe inference refers to something else

      Line 501, Patchset 15: check.directCycles(make(map[Object]int))
      Robert Griesemer . resolved

      I don't see a reason for passing this map in as an argument.
      Why not allocate it locally, in directCycles?

      Mark Freeman

      My mistake — I misread your comment on "passing it in to directCycle" as passing it to "directCycles" (note the "s"); have put that back.

      File src/cmd/compile/internal/types2/cycles.go
      Line 17, Patchset 15:func (c *Checker) directCycles(seen map[Object]int) {
      Robert Griesemer . resolved

      s/c/check/ for consistency with the rest of the code.

      I don't object to renaming, but let's do it consistently across all files.
      The consistency simply makes it easier to read the code.

      Mark Freeman

      Done

      Line 40, Patchset 15: // if we reach an already-reported type, don't report it again
      Robert Griesemer . resolved

      // If we reach a previously reported type, don't report it again.

      (Note that we may not have reported anything before. Also, for comments that are proper sentences, please use capitalization and periods. For others its ok to keep it all lowercase without punctuation.)

      Mark Freeman

      Done

      Line 65, Patchset 15: // direct cycles can only exist through package-level type names,
      Robert Griesemer . resolved

      make a proper sentence, please

      Mark Freeman

      Done

      Open in Gerrit

      Related details

      Attention set is empty
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 16
      Gerrit-Owner: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
      Gerrit-Comment-Date: Wed, 29 Oct 2025 16:28:45 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      Comment-In-Reply-To: Robert Griesemer <g...@google.com>
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 29, 2025, 1:03:38 PMOct 29
      to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

      Mark Freeman uploaded new patchset

      Mark Freeman uploaded patch set #17 to this change.
      Open in Gerrit

      Related details

      Attention set is empty
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: newpatchset
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 17
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 29, 2025, 1:06:07 PMOct 29
      to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

      Mark Freeman uploaded new patchset

      Mark Freeman uploaded patch set #18 to this change.
      Open in Gerrit

      Related details

      Attention set is empty
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: newpatchset
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 18
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 29, 2025, 1:44:18 PMOct 29
      to goph...@pubsubhelper.golang.org, Robert Griesemer, Go LUCI, golang-co...@googlegroups.com

      Mark Freeman voted Commit-Queue+1

      Commit-Queue+1
      Open in Gerrit

      Related details

      Attention set is empty
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 18
      Gerrit-Owner: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
      Gerrit-Comment-Date: Wed, 29 Oct 2025 17:44:14 +0000
      Gerrit-HasComments: No
      Gerrit-Has-Labels: Yes
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 29, 2025, 5:16:36 PMOct 29
      to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

      Mark Freeman uploaded new patchset

      Mark Freeman uploaded patch set #19 to this change.
      Following approvals got outdated and were removed:
      • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI
      Open in Gerrit

      Related details

      Attention set is empty
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: newpatchset
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 19
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 29, 2025, 5:22:00 PMOct 29
      to goph...@pubsubhelper.golang.org, Go LUCI, Robert Griesemer, golang-co...@googlegroups.com

      Mark Freeman voted Commit-Queue+1

      Commit-Queue+1
      Open in Gerrit

      Related details

      Attention set is empty
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 19
      Gerrit-Owner: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
      Gerrit-Comment-Date: Wed, 29 Oct 2025 21:21:56 +0000
      Gerrit-HasComments: No
      Gerrit-Has-Labels: Yes
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 29, 2025, 6:21:54 PMOct 29
      to goph...@pubsubhelper.golang.org, Go LUCI, Robert Griesemer, golang-co...@googlegroups.com
      Attention needed from Robert Griesemer

      Mark Freeman added 1 comment

      File src/cmd/compile/internal/types2/cycles.go
      Line 61, Patchset 15: return c.directCycle(t, seen)
      Robert Griesemer . resolved

      I think we should avoid the recursion here. There is really no need, it's basically a tail recursion. The primary benefit is that tracing would show the entire path, but it's nested, which is not great either.

      Just have a loop. Then you also don't need to return a result, actually.
      And the seen map is always empty at the end, so I wonder if we can be smarter about this whole thing.

      Let's say we represent `type A B` as A -> B, and use . to mark a literal (end of path). We may have a situation like this:

      A -> B -> C -> D -> E -> .
      F -> C -> D -> E -> .
      G -> H -> D -> E -> .

      Even though we know that after starting with A there is no cycle, we iterate (recurse!) through much of the path again and again for B, C, etc. That's potentially quadratic. We only stop early if we find a black object, and we only find one if we have a cycle.

      We should absolutely avoid repeated traversals. In normal programs it won't be an issue, but it would be easy to generate a bad program that would consume a lot of time to check.

      This needs some more work.

      Mark Freeman

      Overall agree — I've changed this to an iterative approach that marks visited types using a `done` map. This should guard against the quadratic case. Unfortunately, tracing becomes a bit less obvious, but I don't think its critical to have.

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Robert Griesemer
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 20
      Gerrit-Owner: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
      Gerrit-Attention: Robert Griesemer <g...@google.com>
      Gerrit-Comment-Date: Wed, 29 Oct 2025 22:21:50 +0000
      unsatisfied_requirement
      open
      diffy

      Mark Freeman (Gerrit)

      unread,
      Oct 31, 2025, 10:51:34 AM (13 days ago) Oct 31
      to goph...@pubsubhelper.golang.org, Go LUCI, Robert Griesemer, golang-co...@googlegroups.com
      Attention needed from Robert Griesemer

      Mark Freeman voted Commit-Queue+1

      Commit-Queue+1
      Open in Gerrit

      Related details

      Attention is currently required from:
      • Robert Griesemer
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
      Gerrit-Change-Number: 714760
      Gerrit-PatchSet: 20
      Gerrit-Owner: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Mark Freeman <markf...@google.com>
      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
      Gerrit-Attention: Robert Griesemer <g...@google.com>
      Gerrit-Comment-Date: Fri, 31 Oct 2025 14:51:30 +0000
      Gerrit-HasComments: No
      Gerrit-Has-Labels: Yes
      unsatisfied_requirement
      open
      diffy

      Robert Griesemer (Gerrit)

      unread,
      Nov 3, 2025, 1:43:33 PM (10 days ago) Nov 3
      to Mark Freeman, goph...@pubsubhelper.golang.org, Go LUCI, golang-co...@googlegroups.com
      Attention needed from Mark Freeman

      Robert Griesemer added 4 comments

      Patchset-level comments
      File-level comment, Patchset 20 (Latest):
      Robert Griesemer . resolved

      Apologies for the delay. Some more feedback.
      It seems ok, but there ought to be a simpler, easier to understand way to do this.
      I haven't completely convinced myself that this is correct.

      File src/cmd/compile/internal/types2/cycles.go
      Line 28, Patchset 20 (Latest):func (check *Checker) directCycle(t0 *TypeName, seen map[Object]int, done map[Object]bool) {
      Robert Griesemer . unresolved

      s/t0/t/

      You need a t1 below, but only briefly. Ok to use t, t1 (similar to say t, and t' as one might us in math); this keeps the primary identifier short.

      Line 45, Patchset 20 (Latest): // If no cycle has been found, keep walking.
      Robert Griesemer . unresolved

      This comment is confusing in front of the next two lines. I'd put it on line 44 and then have an empty line.

      And then, here, I'd explain what happens. It is super subtle because push/pop affects the objPath. And the defer clears from the seen map and object path if there's no cycle, but _after_ we mark the path as done.

      Line 50, Patchset 20 (Latest): if t1, ok := check.pkg.scope.Lookup(n.Value).(*TypeName); ok {
      Robert Griesemer . unresolved

      This could use a comment explaining that we follow the RHS type whether it's an alias or not.

      Also, I think if t0 is not a type, that tdecl.Type might be nil. I think you can probably have a test case for that.

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Mark Freeman
      Submit Requirements:
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: go
        Gerrit-Branch: master
        Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
        Gerrit-Change-Number: 714760
        Gerrit-PatchSet: 20
        Gerrit-Owner: Mark Freeman <markf...@google.com>
        Gerrit-Reviewer: Mark Freeman <markf...@google.com>
        Gerrit-Reviewer: Robert Griesemer <g...@google.com>
        Gerrit-Attention: Mark Freeman <markf...@google.com>
        Gerrit-Comment-Date: Mon, 03 Nov 2025 18:43:28 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        unsatisfied_requirement
        satisfied_requirement
        open
        diffy

        Mark Freeman (Gerrit)

        unread,
        Nov 3, 2025, 3:14:45 PM (9 days ago) Nov 3
        to goph...@pubsubhelper.golang.org, Go LUCI, Robert Griesemer, golang-co...@googlegroups.com
        Attention needed from Robert Griesemer

        Mark Freeman added 5 comments

        File src/cmd/compile/internal/types2/cycles.go
        Line 25, Patchset 15:// directCycle drives [Checker.directCycles] for a given type.
        Robert Griesemer . resolved

        This doc string is not super helpful, also, the name is not quite right.
        Better to explain the result.
        But see below.

        Mark Freeman

        I don't think we can explain much without restating what's in `Checker.directCycles`. It might be best to leave this a bit terse and simply link to the other method with more exposition.

        Line 28, Patchset 20 (Latest):func (check *Checker) directCycle(t0 *TypeName, seen map[Object]int, done map[Object]bool) {
        Robert Griesemer . resolved

        s/t0/t/

        You need a t1 below, but only briefly. Ok to use t, t1 (similar to say t, and t' as one might us in math); this keeps the primary identifier short.

        Mark Freeman

        Makes sense, done

        Line 45, Patchset 20 (Latest): // If no cycle has been found, keep walking.
        Robert Griesemer . resolved

        This comment is confusing in front of the next two lines. I'd put it on line 44 and then have an empty line.

        And then, here, I'd explain what happens. It is super subtle because push/pop affects the objPath. And the defer clears from the seen map and object path if there's no cycle, but _after_ we mark the path as done.

        Mark Freeman

        Agree — `check.pop()` being deferred is crucial because we still need the object path when we go to mark the path as done. I've added a comment explaining this.

        Line 50, Patchset 20 (Latest): if t1, ok := check.pkg.scope.Lookup(n.Value).(*TypeName); ok {
        Robert Griesemer . resolved

        This could use a comment explaining that we follow the RHS type whether it's an alias or not.

        Also, I think if t0 is not a type, that tdecl.Type might be nil. I think you can probably have a test case for that.

        Mark Freeman

        Added the comment.

        By traversal, `t0` is a package-level type declared in the current package. I think it's an invariant that `tdecl.Type` is populated for type declarations, which `Checker.resolveBaseTypeName` also relies on.

        If we want to explicitly enforce that invariant, maybe we check it upstream in debug mode. Here, it seems simplest to use the invariant.

        File src/cmd/compile/internal/types2/named.go
        Line 633, Patchset 12 (Parent): if i, ok := seen[t]; ok {
        Robert Griesemer . resolved

        I wonder if it would make sense to leave (some of) this in debug mode: we panic if there's a cycle. That might be useful in case we run into unexpected cycles (not that I can think of one at the moment).

        I'd declare the seen variable, and in debug mode, allocate it eagerly (so you don't need lines 652-654), and then have a simple check:

        ```
        if debug {
        assert(!seen[t])
        seen[t] = true
        }
        ```
        Mark Freeman

        Pardon my delay, I had to think about this one a bit. While it seems fine here, it could be tricky for more advanced cycles and defeat some of the complexity savings.

        In any case, we would hang if such unexpected cycles slipped by and could rely on traces to observe where it's hanging.

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Robert Griesemer
        Submit Requirements:
          • requirement is not satisfiedCode-Review
          • requirement satisfiedNo-Unresolved-Comments
          • requirement is not satisfiedReview-Enforcement
          • requirement satisfiedTryBots-Pass
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: comment
          Gerrit-Project: go
          Gerrit-Branch: master
          Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
          Gerrit-Change-Number: 714760
          Gerrit-PatchSet: 20
          Gerrit-Owner: Mark Freeman <markf...@google.com>
          Gerrit-Reviewer: Mark Freeman <markf...@google.com>
          Gerrit-Reviewer: Robert Griesemer <g...@google.com>
          Gerrit-Attention: Robert Griesemer <g...@google.com>
          Gerrit-Comment-Date: Mon, 03 Nov 2025 20:14:41 +0000
          Gerrit-HasComments: Yes
          Gerrit-Has-Labels: No
          Comment-In-Reply-To: Robert Griesemer <g...@google.com>
          unsatisfied_requirement
          satisfied_requirement
          open
          diffy

          Robert Griesemer (Gerrit)

          unread,
          Nov 3, 2025, 7:55:51 PM (9 days ago) Nov 3
          to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

          Robert Griesemer has uploaded the change for review

          Commit message

          go/types, types2: check for direct cycles as a separate phase

          Alternative to CL 714760. To be discussed.
          Change-Id: I3044c383278deb6acb8767c498d8cb68099ba8ef

          Change diff

          diff --git a/src/cmd/compile/internal/types2/check.go b/src/cmd/compile/internal/types2/check.go
          index 8b27d9d..0716ee8 100644
          --- a/src/cmd/compile/internal/types2/check.go
          +++ b/src/cmd/compile/internal/types2/check.go
          @@ -497,6 +497,9 @@
          print("== sortObjects ==")
          check.sortObjects()

          + print("== directCycles ==")
          + check.directCycles()
          +
          print("== packageObjects ==")
          check.packageObjects()

          diff --git a/src/cmd/compile/internal/types2/cycles.go b/src/cmd/compile/internal/types2/cycles.go
          new file mode 100644
          index 0000000..b76dcf3
          --- /dev/null
          +++ b/src/cmd/compile/internal/types2/cycles.go
          @@ -0,0 +1,122 @@
          +// Copyright 2025 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.
          +
          +package types2
          +
          +import "cmd/compile/internal/syntax"
          +
          +// directCycles searches for direct cycles among package level type declarations.
          +// See directCycle for details.
          +func (check *Checker) directCycles() {
          + seen := make(map[*TypeName]bool)
          + for _, obj := range check.objList {
          + if tname, _ := obj.(*TypeName); tname != nil {
          + check.directCycle(tname, seen)
          + }
          + }
          +}
          +
          +// directCycle checks if the declaration of the type given by tname is free of direct cycles.
          +// A direct cycle exists if the path from tname's declaration's RHS leads from type name to
          +// type name and eventually ends up on the path again, via regular or alias declarations; in
          +// other words if there are no type literals (or basic types) on the path.
          +// If a cycle is detected, a cycle error is reported and all types on the path are marked
          +// as invalid.
          +//
          +// The seen map tracks which type names have been processed already. An entry can be in one
          +// one of 3 states to represent a typical 3-state (white-grey-black) graph marking algorithm
          +// for cycle detection:
          +//
          +// - entry doesn't exist: tname has not been seen before ("white")
          +// - value is false : tname has been seen but is not done ("grey")
          +// - value is true : tname has been seen and is done ("black")
          +//
          +// When directCycle returns, the seen entries for all type names on the path that starts
          +// at tname are true ("black"), irrespective of whether there was a cycle or not.
          +// This ensures that a type name is never iterated over multiple times during the search.
          +func (check *Checker) directCycle(tname *TypeName, seen map[*TypeName]bool) {
          + if debug && check.conf.Trace {
          + check.trace(tname.Pos(), "-- path for %s", tname)
          + }
          +
          + var path []*TypeName
          + for {
          + done, found := seen[tname]
          + if done {
          + // We have seen tname and it was done before ("black" type name).
          + break
          + }
          +
          + if found {
          + // We have seen tname before but it is not done yet, so we must
          + // have a cycle ("grey" type name).
          + // TODO(gri) consider marking all types on the path as invalid
          + tname.setType(Typ[Invalid])
          + tname.setColor(black)
          +
          + // Determine the start of the cycle on the path.
          + // This is the error case, so we don't care about the extra
          + // path traversal.
          + start := -1 // start of cycle
          + for i, tn := range path {
          + // Because the seen map contained no "grey" entries upon call of
          + // this function, the only grey entries that are in seen now are
          + // entries that were added during this iteration. This ensures
          + // that we're going to fine tname in the path somewhere.
          + if tname == tn {
          + start = i
          + break
          + }
          + }
          + assert(start >= 0)
          +
          + // Convert cycle path into an object path.
          + var cycle []Object
          + for _, tname := range path[start:] {
          + cycle = append(cycle, tname)
          + }
          +
          + check.cycleError(cycle, firstInSrc(cycle))
          + break
          + }
          +
          + // The type name was never seen before ("white" type name).
          + // Mark it accordingly ("grey") and add it to the path.
          + seen[tname] = false
          + path = append(path, tname)
          +
          + // tname is a type name, so it must have a corresponding type declaration and
          + // associated RHS type of that declaration.
          + // For direct cycle detection, we don't care about whether we have an alias or not.
          + // If the associated type is not a name, we're at the end of the path and we're done.
          + rhs, _ := check.objMap[tname].tdecl.Type.(*syntax.Name)
          + if rhs == nil {
          + break
          + }
          +
          + // Determine the RHS type. If it's not found, we have an error but it will
          + // be reported later, and we can stop. If it's not a type name, we also
          + // cannot have a direct cycle and thus can stop.
          + tn, _ := check.pkg.scope.Lookup(rhs.Value).(*TypeName)
          + if tn == nil {
          + break
          + }
          +
          + // Otherwise, continue with RHS.
          + tname = tn
          + }
          +
          + // Mark all type names as done.
          + // This ensures that seen doesn't contain any seen but not done entries
          + // upon return from this function.
          + for _, tname := range path {
          + seen[tname] = true
          + }
          +
          + if debug {
          + for _, done := range seen {
          + assert(done)
          + }
          + }
          +}
          diff --git a/src/cmd/compile/internal/types2/named.go b/src/cmd/compile/internal/types2/named.go
          index cece304..b5c8ed1 100644
          --- a/src/cmd/compile/internal/types2/named.go
          +++ b/src/cmd/compile/internal/types2/named.go
          @@ -613,13 +613,18 @@
          // type set to T. Aliases are skipped because their underlying type is
          // not memoized.
          //
          -// This method also checks for cycles among alias and named types, which will
          -// yield no underlying type. If such a cycle is found, the underlying type is
          -// set to Typ[Invalid] and a cycle is reported.
          +// resolveUnderlying assumes that there are no direct cycles; if there were
          +// any, they were broken (by setting the respective types to invalid) during
          +// the directCycles check phase.
          func (n *Named) resolveUnderlying() {
          assert(n.stateHas(unpacked))

          - var seen map[*Named]int // allocated lazily
          + var seen map[*Named]bool // for debugging only
          + if debug {
          + seen = make(map[*Named]bool)
          + }
          +
          + var path []*Named
          var u Type
          for rhs := Type(n); u == nil; {
          switch t := rhs.(type) {
          @@ -630,17 +635,9 @@
          rhs = unalias(t)

          case *Named:
          - if i, ok := seen[t]; ok {
          - // compute cycle path
          - path := make([]Object, len(seen))
          - for t, j := range seen {
          - path[j] = t.obj
          - }
          - path = path[i:]
          - // only called during type checking, hence n.check != nil
          - n.check.cycleError(path, firstInSrc(path))
          - u = Typ[Invalid]
          - break
          + if debug {
          + assert(!seen[t])
          + seen[t] = true
          }

          // don't recalculate the underlying
          @@ -649,10 +646,10 @@
          break
          }

          - if seen == nil {
          - seen = make(map[*Named]int)
          + if debug {
          + seen[t] = true
          }
          - seen[t] = len(seen)
          + path = append(path, t)

          t.unpack()
          assert(t.rhs() != nil || t.allowNilRHS)
          @@ -663,7 +660,7 @@
          }
          }

          - for t := range seen {
          + for _, t := range path {
          func() {
          t.mu.Lock()
          defer t.mu.Unlock()
          diff --git a/src/go/types/check.go b/src/go/types/check.go
          index d163012..44d3ae5 100644
          --- a/src/go/types/check.go
          +++ b/src/go/types/check.go
          @@ -522,6 +522,9 @@
          print("== sortObjects ==")
          check.sortObjects()

          + print("== directCycles ==")
          + check.directCycles()
          +
          print("== packageObjects ==")
          check.packageObjects()

          diff --git a/src/go/types/cycles.go b/src/go/types/cycles.go
          new file mode 100644
          index 0000000..5445900
          --- /dev/null
          +++ b/src/go/types/cycles.go
          @@ -0,0 +1,122 @@
          +// Copyright 2025 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.
          +
          +package types
          +
          +import "go/ast"
          +
          +// directCycles searches for direct cycles among package level type declarations.
          +// See directCycle for details.
          +func (check *Checker) directCycles() {
          + seen := make(map[*TypeName]bool)
          + for _, obj := range check.objList {
          + if tname, _ := obj.(*TypeName); tname != nil {
          + check.directCycle(tname, seen)
          + }
          + }
          +}
          +
          +// directCycle checks if the declaration of the type given by tname is free of direct cycles.
          +// A direct cycle exists if the path from tname's declaration's RHS leads from type name to
          +// type name and eventually ends up on the path again, via regular or alias declarations; in
          +// other words if there are no type literals (or basic types) on the path.
          +// If a cycle is detected, a cycle error is reported and all types on the path are marked
          +// as invalid.
          +//
          +// The seen map tracks which type names have been processed already. An entry can be in one
          +// one of 3 states to represent a typical 3-state (white-grey-black) graph marking algorithm
          +// for cycle detection:
          +//
          +// - entry doesn't exist: tname has not been seen before ("white")
          +// - value is false : tname has been seen but is not done ("grey")
          +// - value is true : tname has been seen and is done ("black")
          +//
          +// When directCycle returns, the seen entries for all type names on the path that starts
          +// at tname are true ("black"), irrespective of whether there was a cycle or not.
          +// This ensures that a type name is never iterated over multiple times during the search.
          +func (check *Checker) directCycle(tname *TypeName, seen map[*TypeName]bool) {
          + if debug && check.conf._Trace {
          + check.trace(tname.Pos(), "-- path for %s", tname)
          + }
          +
          + var path []*TypeName
          + for {
          + done, found := seen[tname]
          + if done {
          + // We have seen tname and it was done before ("black" type name).
          + break
          + }
          +
          + if found {
          + // We have seen tname before but it is not done yet, so we must
          + // have a cycle ("grey" type name).
          + // TODO(gri) consider marking all types on the path as invalid
          + tname.setType(Typ[Invalid])
          + tname.setColor(black)
          +
          + // Determine the start of the cycle on the path.
          + // This is the error case, so we don't care about the extra
          + // path traversal.
          + start := -1 // start of cycle
          + for i, tn := range path {
          + // Because the seen map contained no "grey" entries upon call of
          + // this function, the only grey entries that are in seen now are
          + // entries that were added during this iteration. This ensures
          + // that we're going to fine tname in the path somewhere.
          + if tname == tn {
          + start = i
          + break
          + }
          + }
          + assert(start >= 0)
          +
          + // Convert cycle path into an object path.
          + var cycle []Object
          + for _, tname := range path[start:] {
          + cycle = append(cycle, tname)
          + }
          +
          + check.cycleError(cycle, firstInSrc(cycle))
          + break
          + }
          +
          + // The type name was never seen before ("white" type name).
          + // Mark it accordingly ("grey") and add it to the path.
          + seen[tname] = false
          + path = append(path, tname)
          +
          + // tname is a type name, so it must have a corresponding type declaration and
          + // associated RHS type of that declaration.
          + // For direct cycle detection, we don't care about whether we have an alias or not.
          + // If the associated type is not a name, we're at the end of the path and we're done.
          + rhs, _ := check.objMap[tname].tdecl.Type.(*ast.Ident)
          + if rhs == nil {
          + break
          + }
          +
          + // Determine the RHS type. If it's not found, we have an error but it will
          + // be reported later, and we can stop. If it's not a type name, we also
          + // cannot have a direct cycle and thus can stop.
          + tn, _ := check.pkg.scope.Lookup(rhs.Name).(*TypeName)
          + if tn == nil {
          + break
          + }
          +
          + // Otherwise, continue with RHS.
          + tname = tn
          + }
          +
          + // Mark all type names as done.
          + // This ensures that seen doesn't contain any seen but not done entries
          + // upon return from this function.
          + for _, tname := range path {
          + seen[tname] = true
          + }
          +
          + if debug {
          + for _, done := range seen {
          + assert(done)
          + }
          + }
          +}
          diff --git a/src/go/types/named.go b/src/go/types/named.go
          index 1f9e247..b106d7a 100644
          --- a/src/go/types/named.go
          +++ b/src/go/types/named.go
          @@ -616,13 +616,18 @@
          // type set to T. Aliases are skipped because their underlying type is
          // not memoized.
          //
          -// This method also checks for cycles among alias and named types, which will
          -// yield no underlying type. If such a cycle is found, the underlying type is
          -// set to Typ[Invalid] and a cycle is reported.
          +// resolveUnderlying assumes that there are no direct cycles; if there were
          +// any, they were broken (by setting the respective types to invalid) during
          +// the directCycles check phase.
          func (n *Named) resolveUnderlying() {
          assert(n.stateHas(unpacked))

          - var seen map[*Named]int // allocated lazily
          + var seen map[*Named]bool // for debugging only
          + if debug {
          + seen = make(map[*Named]bool)
          + }
          +
          + var path []*Named
          var u Type
          for rhs := Type(n); u == nil; {
          switch t := rhs.(type) {
          @@ -633,17 +638,9 @@
          rhs = unalias(t)

          case *Named:
          - if i, ok := seen[t]; ok {
          - // compute cycle path
          - path := make([]Object, len(seen))
          - for t, j := range seen {
          - path[j] = t.obj
          - }
          - path = path[i:]
          - // only called during type checking, hence n.check != nil
          - n.check.cycleError(path, firstInSrc(path))
          - u = Typ[Invalid]
          - break
          + if debug {
          + assert(!seen[t])
          + seen[t] = true
          }

          // don't recalculate the underlying
          @@ -652,10 +649,10 @@
          break
          }

          - if seen == nil {
          - seen = make(map[*Named]int)
          + if debug {
          + seen[t] = true
          }
          - seen[t] = len(seen)
          + path = append(path, t)

          t.unpack()
          assert(t.rhs() != nil || t.allowNilRHS)
          @@ -666,7 +663,7 @@
          }
          }

          - for t := range seen {
          + for _, t := range path {
          func() {
          t.mu.Lock()
          defer t.mu.Unlock()

          Change information

          Files:
          • M src/cmd/compile/internal/types2/check.go
          • A src/cmd/compile/internal/types2/cycles.go
          • M src/cmd/compile/internal/types2/named.go
          • M src/go/types/check.go
          • A src/go/types/cycles.go
          • M src/go/types/named.go
          Change size: L
          Delta: 6 files changed, 282 insertions(+), 38 deletions(-)
          Open in Gerrit

          Related details

          Attention set is empty
          Submit Requirements:
            • requirement is not satisfiedCode-Review
            • requirement satisfiedNo-Unresolved-Comments
            • requirement is not satisfiedReview-Enforcement
            • requirement is not satisfiedTryBots-Pass
            Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
            Gerrit-MessageType: newchange
            Gerrit-Project: go
            Gerrit-Branch: master
            Gerrit-Change-Id: I3044c383278deb6acb8767c498d8cb68099ba8ef
            Gerrit-Change-Number: 717343
            Gerrit-PatchSet: 1
            Gerrit-Owner: Robert Griesemer <g...@google.com>
            unsatisfied_requirement
            satisfied_requirement
            open
            diffy

            Robert Griesemer (Gerrit)

            unread,
            Nov 3, 2025, 8:08:18 PM (9 days ago) Nov 3
            to goph...@pubsubhelper.golang.org, Mark Freeman, Robert Griesemer, Gopher Robot, golang-co...@googlegroups.com
            Attention needed from Mark Freeman and Robert Griesemer

            Robert Griesemer added 1 comment

            Patchset-level comments
            File-level comment, Patchset 1 (Latest):
            Robert Griesemer . resolved

            CL to be discussed. Please have a look.

            Open in Gerrit

            Related details

            Attention is currently required from:
            • Mark Freeman
            • Robert Griesemer
            Submit Requirements:
            • requirement is not satisfiedCode-Review
            • requirement satisfiedNo-Unresolved-Comments
            • requirement is not satisfiedReview-Enforcement
            • requirement is not satisfiedTryBots-Pass
            Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
            Gerrit-MessageType: comment
            Gerrit-Project: go
            Gerrit-Branch: master
            Gerrit-Change-Id: I3044c383278deb6acb8767c498d8cb68099ba8ef
            Gerrit-Change-Number: 717343
            Gerrit-PatchSet: 1
            Gerrit-Owner: Robert Griesemer <g...@google.com>
            Gerrit-Reviewer: Mark Freeman <markf...@google.com>
            Gerrit-Reviewer: Robert Griesemer <g...@golang.org>
            Gerrit-CC: Gopher Robot <go...@golang.org>
            Gerrit-Attention: Robert Griesemer <g...@golang.org>
            Gerrit-Attention: Mark Freeman <markf...@google.com>
            Gerrit-Comment-Date: Tue, 04 Nov 2025 01:08:14 +0000
            Gerrit-HasComments: Yes
            Gerrit-Has-Labels: No
            unsatisfied_requirement
            satisfied_requirement
            open
            diffy

            Robert Griesemer (Gerrit)

            unread,
            Nov 3, 2025, 8:13:17 PM (9 days ago) Nov 3
            to goph...@pubsubhelper.golang.org, Mark Freeman, Robert Griesemer, Gopher Robot, golang-co...@googlegroups.com
            Attention needed from Mark Freeman and Robert Griesemer

            Robert Griesemer voted

            Commit-Queue+1
            Hold+1
            Open in Gerrit

            Related details

            Attention is currently required from:
            • Mark Freeman
            • Robert Griesemer
            Submit Requirements:
              • requirement is not satisfiedCode-Review
              • requirement is not satisfiedNo-Holds
              • requirement satisfiedNo-Unresolved-Comments
              • requirement is not satisfiedReview-Enforcement
              • requirement is not satisfiedTryBots-Pass
              Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
              Gerrit-MessageType: comment
              Gerrit-Project: go
              Gerrit-Branch: master
              Gerrit-Change-Id: I3044c383278deb6acb8767c498d8cb68099ba8ef
              Gerrit-Change-Number: 717343
              Gerrit-PatchSet: 1
              Gerrit-Owner: Robert Griesemer <g...@google.com>
              Gerrit-Reviewer: Mark Freeman <markf...@google.com>
              Gerrit-Reviewer: Robert Griesemer <g...@golang.org>
              Gerrit-Reviewer: Robert Griesemer <g...@google.com>
              Gerrit-CC: Gopher Robot <go...@golang.org>
              Gerrit-Attention: Robert Griesemer <g...@golang.org>
              Gerrit-Attention: Mark Freeman <markf...@google.com>
              Gerrit-Comment-Date: Tue, 04 Nov 2025 01:13:12 +0000
              Gerrit-HasComments: No
              Gerrit-Has-Labels: Yes
              unsatisfied_requirement
              satisfied_requirement
              open
              diffy

              Mark Freeman (Gerrit)

              unread,
              Nov 4, 2025, 1:29:17 PM (9 days ago) Nov 4
              to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
              Attention needed from Robert Griesemer

              Mark Freeman uploaded new patchset

              Mark Freeman uploaded patch set #21 to this change.
              Following approvals got outdated and were removed:
              • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI
              Open in Gerrit

              Related details

              Attention is currently required from:
              • Robert Griesemer
              Submit Requirements:
                • requirement is not satisfiedCode-Review
                • requirement satisfiedNo-Unresolved-Comments
                • requirement is not satisfiedReview-Enforcement
                • requirement is not satisfiedTryBots-Pass
                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                Gerrit-MessageType: newpatchset
                Gerrit-Project: go
                Gerrit-Branch: master
                Gerrit-Change-Id: I90edfe2b0c75426fa1022b454b1e888c304178e1
                Gerrit-Change-Number: 714760
                Gerrit-PatchSet: 21
                Gerrit-Owner: Mark Freeman <markf...@google.com>
                Gerrit-Reviewer: Mark Freeman <markf...@google.com>
                unsatisfied_requirement
                satisfied_requirement
                open
                diffy

                Robert Griesemer (Gerrit)

                unread,
                Nov 11, 2025, 6:09:01 PM (2 days ago) Nov 11
                to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
                Attention needed from Mark Freeman and Robert Griesemer

                Robert Griesemer uploaded new patchset

                Robert Griesemer uploaded patch set #3 to this change.
                Open in Gerrit

                Related details

                Attention is currently required from:
                • Mark Freeman
                • Robert Griesemer
                Submit Requirements:
                  • requirement is not satisfiedCode-Review
                  • requirement is not satisfiedNo-Holds
                  • requirement satisfiedNo-Unresolved-Comments
                  • requirement is not satisfiedReview-Enforcement
                  • requirement is not satisfiedTryBots-Pass
                  Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                  Gerrit-MessageType: newpatchset
                  Gerrit-Project: go
                  Gerrit-Branch: master
                  Gerrit-Change-Id: I3044c383278deb6acb8767c498d8cb68099ba8ef
                  Gerrit-Change-Number: 717343
                  Gerrit-PatchSet: 3
                  Gerrit-Owner: Robert Griesemer <g...@google.com>
                  Gerrit-Reviewer: Mark Freeman <markf...@google.com>
                  unsatisfied_requirement
                  satisfied_requirement
                  open
                  diffy

                  Robert Griesemer (Gerrit)

                  unread,
                  Nov 11, 2025, 6:09:51 PM (2 days ago) Nov 11
                  to goph...@pubsubhelper.golang.org, Go LUCI, Mark Freeman, Robert Griesemer, Gopher Robot, golang-co...@googlegroups.com
                  Attention needed from Mark Freeman and Robert Griesemer

                  Robert Griesemer voted and added 1 comment

                  Votes added by Robert Griesemer

                  Auto-Submit+1
                  Code-Review+1
                  Commit-Queue+1
                  Hold+0

                  1 comment

                  Patchset-level comments
                  File-level comment, Patchset 3 (Latest):
                  Robert Griesemer . resolved

                  Please have a look, thanks.

                  Open in Gerrit

                  Related details

                  Attention is currently required from:
                  • Mark Freeman
                  • Robert Griesemer
                  Submit Requirements:
                    • requirement is not satisfiedCode-Review
                    • requirement satisfiedNo-Unresolved-Comments
                    • requirement is not satisfiedReview-Enforcement
                    • requirement is not satisfiedTryBots-Pass
                    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                    Gerrit-MessageType: comment
                    Gerrit-Project: go
                    Gerrit-Branch: master
                    Gerrit-Change-Id: I3044c383278deb6acb8767c498d8cb68099ba8ef
                    Gerrit-Change-Number: 717343
                    Gerrit-PatchSet: 3
                    Gerrit-Owner: Robert Griesemer <g...@google.com>
                    Gerrit-Reviewer: Mark Freeman <markf...@google.com>
                    Gerrit-Reviewer: Robert Griesemer <g...@golang.org>
                    Gerrit-Reviewer: Robert Griesemer <g...@google.com>
                    Gerrit-CC: Gopher Robot <go...@golang.org>
                    Gerrit-Attention: Robert Griesemer <g...@golang.org>
                    Gerrit-Attention: Mark Freeman <markf...@google.com>
                    Gerrit-Comment-Date: Tue, 11 Nov 2025 23:09:45 +0000
                    Gerrit-HasComments: Yes
                    Gerrit-Has-Labels: Yes
                    unsatisfied_requirement
                    satisfied_requirement
                    open
                    diffy

                    Robert Griesemer (Gerrit)

                    unread,
                    Nov 11, 2025, 6:30:22 PM (2 days ago) Nov 11
                    to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
                    Attention needed from Mark Freeman, Robert Griesemer and Robert Griesemer

                    Robert Griesemer uploaded new patchset

                    Robert Griesemer uploaded patch set #4 to this change.
                    Following approvals got outdated and were removed:
                    • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI
                    Open in Gerrit

                    Related details

                    Attention is currently required from:
                    • Mark Freeman
                    • Robert Griesemer
                    • Robert Griesemer
                    Submit Requirements:
                    • requirement is not satisfiedCode-Review
                    • requirement satisfiedNo-Unresolved-Comments
                    • requirement is not satisfiedReview-Enforcement
                    • requirement is not satisfiedTryBots-Pass
                    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                    Gerrit-MessageType: newpatchset
                    Gerrit-Project: go
                    Gerrit-Branch: master
                    Gerrit-Change-Id: I3044c383278deb6acb8767c498d8cb68099ba8ef
                    Gerrit-Change-Number: 717343
                    Gerrit-PatchSet: 4
                    Gerrit-Owner: Robert Griesemer <g...@google.com>
                    Gerrit-Reviewer: Mark Freeman <markf...@google.com>
                    Gerrit-Reviewer: Robert Griesemer <g...@golang.org>
                    Gerrit-Reviewer: Robert Griesemer <g...@google.com>
                    Gerrit-CC: Gopher Robot <go...@golang.org>
                    Gerrit-Attention: Robert Griesemer <g...@google.com>
                    unsatisfied_requirement
                    satisfied_requirement
                    open
                    diffy

                    Robert Griesemer (Gerrit)

                    unread,
                    Nov 11, 2025, 6:30:39 PM (2 days ago) Nov 11
                    to goph...@pubsubhelper.golang.org, Go LUCI, Mark Freeman, Robert Griesemer, Gopher Robot, golang-co...@googlegroups.com
                    Attention needed from Mark Freeman and Robert Griesemer

                    Robert Griesemer voted

                    Auto-Submit+1
                    Code-Review+1
                    Commit-Queue+1

                    Related details

                    Attention is currently required from:
                    • Mark Freeman
                    • Robert Griesemer
                    Submit Requirements:
                    • requirement is not satisfiedCode-Review
                    • requirement satisfiedNo-Unresolved-Comments
                    • requirement is not satisfiedReview-Enforcement
                    • requirement is not satisfiedTryBots-Pass
                    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                    Gerrit-MessageType: comment
                    Gerrit-Project: go
                    Gerrit-Branch: master
                    Gerrit-Change-Id: I3044c383278deb6acb8767c498d8cb68099ba8ef
                    Gerrit-Change-Number: 717343
                    Gerrit-PatchSet: 4
                    Gerrit-Owner: Robert Griesemer <g...@google.com>
                    Gerrit-Reviewer: Mark Freeman <markf...@google.com>
                    Gerrit-Reviewer: Robert Griesemer <g...@golang.org>
                    Gerrit-Reviewer: Robert Griesemer <g...@google.com>
                    Gerrit-CC: Gopher Robot <go...@golang.org>
                    Gerrit-Attention: Robert Griesemer <g...@golang.org>
                    Gerrit-Attention: Mark Freeman <markf...@google.com>
                    Gerrit-Comment-Date: Tue, 11 Nov 2025 23:30:34 +0000
                    Gerrit-HasComments: No
                    Gerrit-Has-Labels: Yes
                    unsatisfied_requirement
                    satisfied_requirement
                    open
                    diffy

                    Mark Freeman (Gerrit)

                    unread,
                    Nov 12, 2025, 4:59:38 PM (10 hours ago) Nov 12
                    to Robert Griesemer, goph...@pubsubhelper.golang.org, Go LUCI, Robert Griesemer, Gopher Robot, golang-co...@googlegroups.com
                    Attention needed from Robert Griesemer and Robert Griesemer

                    Mark Freeman added 29 comments

                    Patchset-level comments
                    File-level comment, Patchset 4 (Latest):
                    Mark Freeman . resolved

                    Quite nice and easy to read — left a first round of minor comments.

                    File src/cmd/compile/internal/types2/cycles.go
                    Line 14, Patchset 4 (Latest): if tname, _ := obj.(*TypeName); tname != nil {
                    Mark Freeman . unresolved

                    ditto

                    Line 20, Patchset 4 (Latest):// directCycle checks if the declaration of the type given by tname is free of direct cycles.
                    Mark Freeman . unresolved

                    A type can only have at most 1 direct cycle.

                    Perhaps "given by tname contains a direct cycle"? (?)

                    Line 21, Patchset 4 (Latest):// A direct cycle exists if the path from tname's declaration's RHS leads from type name to

                    // type name and eventually ends up on the path again, via regular or alias declarations; in
                    Mark Freeman . resolved

                    As an aside, I wonder if it would help to define some notion of reachability. If we had that, we could say something like:

                    "A direct cycle exists if a TypeName tn is reachable from tn by traversing only TypeNames".

                    Line 24, Patchset 4 (Latest):// If a cycle is detected, a cycle error is reported and all types on the path are marked
                    Mark Freeman . unresolved

                    Seems this should use either a newline or start on the previous line, though I see this is done often in this CL.

                    Line 27, Patchset 4 (Latest):// The pathIdx map tracks which type names have been processed already. An entry can be in
                    Mark Freeman . unresolved

                    s/already/ (?)

                    Line 28, Patchset 4 (Latest):// one of 3 states as used in a typical 3-state (white-grey-black) graph marking algorithm
                    Mark Freeman . unresolved

                    white / grey / black (?)

                    Line 28, Patchset 4 (Latest):// one of 3 states as used in a typical 3-state (white-grey-black) graph marking algorithm
                    Mark Freeman . unresolved

                    s/one of 3/1 of 3

                    Line 31, Patchset 4 (Latest):// - entry not found: tname has not been seen before ("white")
                    Mark Freeman . resolved

                    If we define these shorthands, I think we should make more use of them below.

                    Line 36, Patchset 4 (Latest):// at tname are set to a value < 0 ("black"), irrespective of whether there was a cycle or not.
                    Mark Freeman . unresolved

                    s/irrespective/regardless (?)

                    Line 36, Patchset 4 (Latest):// at tname are set to a value < 0 ("black"), irrespective of whether there was a cycle or not.
                    Mark Freeman . unresolved

                    s/set to a value < 0 ("black")/marked black

                    Though see my other comment regarding marking only the back-edge.

                    Line 36, Patchset 4 (Latest):// at tname are set to a value < 0 ("black"), irrespective of whether there was a cycle or not.
                    Mark Freeman . unresolved

                    s/or not/

                    Line 37, Patchset 4 (Latest):// This ensures that a type name is never iterated over multiple times during the search.
                    Mark Freeman . unresolved

                    s/that a type name is never iterated over multiple times/a type name is traversed only once

                    Line 39, Patchset 4 (Latest): if debug && check.conf.Trace {

                    check.trace(tname.Pos(), "-- path for %s", tname)
                    }
                    Mark Freeman . unresolved

                    Is this meant to print the path? I don't see how. Also, do we usually gate tracing by debug?

                    Line 47, Patchset 4 (Latest): // We have seen tname and it was done before ("black" type name).
                    // (start can only be < 0 if it was found in the first place)
                    Mark Freeman . unresolved

                    Perhaps:

                    It's marked black, do not traverse again.

                    Line 49, Patchset 4 (Latest): assert(found)
                    Mark Freeman . unresolved

                    This feels a bit odd — if `!found`, then `start` takes the default value for an int. The only way this would fail is if that map invariant changed.

                    Line 54, Patchset 4 (Latest): // We have seen tname before but it is not done yet, so we must
                    // have a cycle ("grey" type name). start is the index where the
                    // cycle begins on the path.
                    Mark Freeman . unresolved

                    Perhaps:
                    // It's marked grey, so we have a cycle on the path beginning at start.

                    Line 58, Patchset 4 (Latest): // Mark all types on the path as invalid and collect type names on cycle.
                    Mark Freeman . unresolved

                    It feels more correct to only handle the back-edge. Consider:

                    ```
                    type B A
                    type A A
                    type C A
                    ```

                    Since we go in source order, we'll process B first. This will mark both B and A invalid. When we visit C, we observe that A is done and break out _without marking C_.

                    I think either all of them should be marked as invalid or only the back-edge. This is a mix depending on whichever comes before the back-edge in source, which seems odd.

                    Line 72, Patchset 4 (Latest): // The type name was never seen before ("white" type name).

                    // Mark it accordingly ("grey") and add it to the path.
                    Mark Freeman . unresolved

                    Perhaps:

                    // It's marked white; mark it grey and add it to the path.

                    Line 77, Patchset 4 (Latest): // tname is a type name, so it must have a corresponding type declaration and

                    // associated RHS type of that declaration.
                    Mark Freeman . unresolved

                    Maybe we leave this away, I would consider these well-known invariants that might not need restatement (?)

                    Line 82, Patchset 4 (Latest): if rhs == nil {
                    Mark Freeman . unresolved

                    s/rhs == nil/!ok

                    I understand this is convention; still, I would prefer not to potentially mask an error here since we are not expecting RHS to be `nil` unless `!ok`.

                    Line 86, Patchset 4 (Latest): // Determine the RHS type. If it's not found, we have an error but it will

                    // be reported later, and we can stop. If it's not a type name, we also
                    // cannot have a direct cycle and thus can stop.
                    Mark Freeman . unresolved

                    It's not necessarily an error. For example, it could refer to something in the universe, but we know nothing in the universe can lead to a direct cycle.

                    Line 90, Patchset 4 (Latest): if tn == nil {
                    Mark Freeman . unresolved

                    ditto

                    Line 94, Patchset 4 (Latest): // Otherwise, continue with RHS.
                    Mark Freeman . unresolved

                    s/RHS/the RHS.

                    Line 95, Patchset 4 (Latest): tname = tn
                    Mark Freeman . unresolved

                    If tname is used more frequently than tn, I think the more concise should go to the more frequently used (all else being equal).

                    Perhaps:
                    s/tname/tn
                    s/rhs/n
                    s/tn/rhs

                    Or alternatively:
                    s/tname/tn
                    s/tn/tn1

                    Line 98, Patchset 4 (Latest): // Mark all type names as done.
                    Mark Freeman . unresolved

                    s/all type names/all traversed type names
                    s/as/

                    Line 99, Patchset 4 (Latest): // This ensures that seen doesn't contain any seen but not done entries
                    Mark Freeman . unresolved

                    s/that seen doesn't contain any seen but not done entries/pathIdx contains no grey entries

                    Line 100, Patchset 4 (Latest): // upon return from this function.
                    Mark Freeman . unresolved

                    s/return/returning
                    s/from this function/

                    Line 106, Patchset 4 (Latest): for _, index := range pathIdx {
                    Mark Freeman . unresolved

                    s/index/idx

                    Open in Gerrit

                    Related details

                    Attention is currently required from:
                    • Robert Griesemer
                    • Robert Griesemer
                    Submit Requirements:
                      • requirement is not satisfiedCode-Review
                      • requirement is not satisfiedNo-Unresolved-Comments
                      • requirement is not satisfiedReview-Enforcement
                      • requirement satisfiedTryBots-Pass
                      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                      Gerrit-MessageType: comment
                      Gerrit-Project: go
                      Gerrit-Branch: master
                      Gerrit-Change-Id: I3044c383278deb6acb8767c498d8cb68099ba8ef
                      Gerrit-Change-Number: 717343
                      Gerrit-PatchSet: 4
                      Gerrit-Owner: Robert Griesemer <g...@google.com>
                      Gerrit-Reviewer: Mark Freeman <markf...@google.com>
                      Gerrit-Reviewer: Robert Griesemer <g...@golang.org>
                      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
                      Gerrit-CC: Gopher Robot <go...@golang.org>
                      Gerrit-Attention: Robert Griesemer <g...@google.com>
                      Gerrit-Attention: Robert Griesemer <g...@golang.org>
                      Gerrit-Comment-Date: Wed, 12 Nov 2025 21:59:34 +0000
                      Gerrit-HasComments: Yes
                      Gerrit-Has-Labels: No
                      unsatisfied_requirement
                      satisfied_requirement
                      open
                      diffy

                      Robert Griesemer (Gerrit)

                      unread,
                      Nov 12, 2025, 6:40:34 PM (8 hours ago) Nov 12
                      to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
                      Attention needed from Robert Griesemer and Robert Griesemer

                      Robert Griesemer uploaded new patchset

                      Robert Griesemer uploaded patch set #5 to this change.
                      Following approvals got outdated and were removed:
                      • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI

                      Related details

                      Attention is currently required from:
                      • Robert Griesemer
                      • Robert Griesemer
                      Submit Requirements:
                        • requirement is not satisfiedCode-Review
                        • requirement is not satisfiedNo-Unresolved-Comments
                        • requirement is not satisfiedReview-Enforcement
                        • requirement is not satisfiedTryBots-Pass
                        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                        Gerrit-MessageType: newpatchset
                        Gerrit-Project: go
                        Gerrit-Branch: master
                        Gerrit-Change-Id: I3044c383278deb6acb8767c498d8cb68099ba8ef
                        Gerrit-Change-Number: 717343
                        Gerrit-PatchSet: 5
                        unsatisfied_requirement
                        open
                        diffy

                        Robert Griesemer (Gerrit)

                        unread,
                        Nov 12, 2025, 6:41:08 PM (8 hours ago) Nov 12
                        to goph...@pubsubhelper.golang.org, Go LUCI, Mark Freeman, Robert Griesemer, Gopher Robot, golang-co...@googlegroups.com
                        Attention needed from Mark Freeman and Robert Griesemer

                        Robert Griesemer voted and added 28 comments

                        Votes added by Robert Griesemer

                        Auto-Submit+1
                        Code-Review+1
                        Commit-Queue+1

                        28 comments

                        Patchset-level comments
                        File-level comment, Patchset 5 (Latest):
                        Robert Griesemer . resolved

                        Thanks for the careful review. PTAL.

                        File src/cmd/compile/internal/types2/cycles.go
                        Line 14, Patchset 4: if tname, _ := obj.(*TypeName); tname != nil {
                        Mark Freeman . resolved

                        ditto

                        Robert Griesemer

                        Done

                        Line 20, Patchset 4:// directCycle checks if the declaration of the type given by tname is free of direct cycles.
                        Mark Freeman . resolved

                        A type can only have at most 1 direct cycle.

                        Perhaps "given by tname contains a direct cycle"? (?)

                        Robert Griesemer

                        Done

                        Line 21, Patchset 4:// A direct cycle exists if the path from tname's declaration's RHS leads from type name to

                        // type name and eventually ends up on the path again, via regular or alias declarations; in
                        Mark Freeman . resolved

                        As an aside, I wonder if it would help to define some notion of reachability. If we had that, we could say something like:

                        "A direct cycle exists if a TypeName tn is reachable from tn by traversing only TypeNames".

                        Robert Griesemer

                        Ok to introduce a notion of reachability. For now I think this is fine.

                        Line 24, Patchset 4:// If a cycle is detected, a cycle error is reported and all types on the path are marked
                        Mark Freeman . resolved

                        Seems this should use either a newline or start on the previous line, though I see this is done often in this CL.

                        Robert Griesemer

                        not sure what this refers to

                        Line 27, Patchset 4:// The pathIdx map tracks which type names have been processed already. An entry can be in
                        Mark Freeman . resolved

                        s/already/ (?)

                        Robert Griesemer

                        Done

                        Line 28, Patchset 4:// one of 3 states as used in a typical 3-state (white-grey-black) graph marking algorithm
                        Mark Freeman . resolved

                        white / grey / black (?)

                        Robert Griesemer

                        Done

                        Line 28, Patchset 4:// one of 3 states as used in a typical 3-state (white-grey-black) graph marking algorithm
                        Mark Freeman . resolved

                        s/one of 3/1 of 3

                        Robert Griesemer

                        Done

                        Line 36, Patchset 4:// at tname are set to a value < 0 ("black"), irrespective of whether there was a cycle or not.
                        Mark Freeman . resolved

                        s/set to a value < 0 ("black")/marked black

                        Though see my other comment regarding marking only the back-edge.

                        Robert Griesemer

                        Done

                        Line 36, Patchset 4:// at tname are set to a value < 0 ("black"), irrespective of whether there was a cycle or not.
                        Mark Freeman . resolved

                        s/or not/

                        Robert Griesemer

                        Done

                        Line 36, Patchset 4:// at tname are set to a value < 0 ("black"), irrespective of whether there was a cycle or not.
                        Mark Freeman . resolved

                        s/irrespective/regardless (?)

                        Robert Griesemer

                        Done

                        Line 37, Patchset 4:// This ensures that a type name is never iterated over multiple times during the search.
                        Mark Freeman . resolved

                        s/that a type name is never iterated over multiple times/a type name is traversed only once

                        Robert Griesemer

                        Done

                        Line 39, Patchset 4: if debug && check.conf.Trace {

                        check.trace(tname.Pos(), "-- path for %s", tname)
                        }
                        Mark Freeman . resolved

                        Is this meant to print the path? I don't see how. Also, do we usually gate tracing by debug?

                        Robert Griesemer

                        It's gated by debug because in normal tracing we don't want to see this level of detail. Changed the text.

                        Line 47, Patchset 4: // We have seen tname and it was done before ("black" type name).

                        // (start can only be < 0 if it was found in the first place)
                        Mark Freeman . resolved

                        Perhaps:

                        It's marked black, do not traverse again.

                        Robert Griesemer

                        Done

                        Line 49, Patchset 4: assert(found)
                        Mark Freeman . resolved

                        This feels a bit odd — if `!found`, then `start` takes the default value for an int. The only way this would fail is if that map invariant changed.

                        Robert Griesemer

                        removed (was a leftover because I added the comment in parentheses)

                        Line 54, Patchset 4: // We have seen tname before but it is not done yet, so we must

                        // have a cycle ("grey" type name). start is the index where the
                        // cycle begins on the path.
                        Mark Freeman . resolved

                        Perhaps:
                        // It's marked grey, so we have a cycle on the path beginning at start.

                        Robert Griesemer

                        Done

                        Line 58, Patchset 4: // Mark all types on the path as invalid and collect type names on cycle.
                        Mark Freeman . resolved

                        It feels more correct to only handle the back-edge. Consider:

                        ```
                        type B A
                        type A A
                        type C A
                        ```

                        Since we go in source order, we'll process B first. This will mark both B and A invalid. When we visit C, we observe that A is done and break out _without marking C_.

                        I think either all of them should be marked as invalid or only the back-edge. This is a mix depending on whichever comes before the back-edge in source, which seems odd.

                        Robert Griesemer

                        Excellent point.
                        Will mark the backedge (actually the start of the cycle) only.

                        Line 72, Patchset 4: // The type name was never seen before ("white" type name).

                        // Mark it accordingly ("grey") and add it to the path.
                        Mark Freeman . resolved

                        Perhaps:

                        // It's marked white; mark it grey and add it to the path.

                        Robert Griesemer

                        Done

                        Line 77, Patchset 4: // tname is a type name, so it must have a corresponding type declaration and

                        // associated RHS type of that declaration.
                        Mark Freeman . resolved

                        Maybe we leave this away, I would consider these well-known invariants that might not need restatement (?)

                        Robert Griesemer

                        Done

                        Line 82, Patchset 4: if rhs == nil {
                        Mark Freeman . resolved

                        s/rhs == nil/!ok

                        I understand this is convention; still, I would prefer not to potentially mask an error here since we are not expecting RHS to be `nil` unless `!ok`.

                        Robert Griesemer

                        Done

                        Line 86, Patchset 4: // Determine the RHS type. If it's not found, we have an error but it will

                        // be reported later, and we can stop. If it's not a type name, we also
                        // cannot have a direct cycle and thus can stop.
                        Mark Freeman . resolved

                        It's not necessarily an error. For example, it could refer to something in the universe, but we know nothing in the universe can lead to a direct cycle.

                        Robert Griesemer

                        adjusted (using check.lookup now, no need for a subtle optimization here)

                        Line 90, Patchset 4: if tn == nil {
                        Mark Freeman . resolved

                        ditto

                        Robert Griesemer

                        Done

                        Line 94, Patchset 4: // Otherwise, continue with RHS.
                        Mark Freeman . resolved

                        s/RHS/the RHS.

                        Robert Griesemer

                        Done

                        Line 95, Patchset 4: tname = tn
                        Mark Freeman . resolved

                        If tname is used more frequently than tn, I think the more concise should go to the more frequently used (all else being equal).

                        Perhaps:
                        s/tname/tn
                        s/rhs/n
                        s/tn/rhs

                        Or alternatively:
                        s/tname/tn
                        s/tn/tn1

                        Robert Griesemer

                        tname is used widely elsewhere in the code. renamed tn to tname1.

                        Line 98, Patchset 4: // Mark all type names as done.
                        Mark Freeman . resolved

                        s/all type names/all traversed type names
                        s/as/

                        Robert Griesemer

                        Done

                        Line 99, Patchset 4: // This ensures that seen doesn't contain any seen but not done entries
                        Mark Freeman . resolved

                        s/that seen doesn't contain any seen but not done entries/pathIdx contains no grey entries

                        Robert Griesemer

                        Done

                        Line 100, Patchset 4: // upon return from this function.
                        Mark Freeman . resolved

                        s/return/returning
                        s/from this function/

                        Robert Griesemer

                        Done

                        Line 106, Patchset 4: for _, index := range pathIdx {
                        Mark Freeman . resolved

                        s/index/idx

                        Robert Griesemer

                        changed to i - this is debug code after all

                        Open in Gerrit

                        Related details

                        Attention is currently required from:
                        • Mark Freeman
                        • Robert Griesemer
                        Submit Requirements:
                          • requirement is not satisfiedCode-Review
                          • requirement satisfiedNo-Unresolved-Comments
                          • requirement is not satisfiedReview-Enforcement
                          • requirement is not satisfiedTryBots-Pass
                          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                          Gerrit-MessageType: comment
                          Gerrit-Project: go
                          Gerrit-Branch: master
                          Gerrit-Change-Id: I3044c383278deb6acb8767c498d8cb68099ba8ef
                          Gerrit-Change-Number: 717343
                          Gerrit-PatchSet: 5
                          Gerrit-Owner: Robert Griesemer <g...@google.com>
                          Gerrit-Reviewer: Mark Freeman <markf...@google.com>
                          Gerrit-Reviewer: Robert Griesemer <g...@golang.org>
                          Gerrit-Reviewer: Robert Griesemer <g...@google.com>
                          Gerrit-CC: Gopher Robot <go...@golang.org>
                          Gerrit-Attention: Robert Griesemer <g...@golang.org>
                          Gerrit-Attention: Mark Freeman <markf...@google.com>
                          Gerrit-Comment-Date: Wed, 12 Nov 2025 23:41:00 +0000
                          Gerrit-HasComments: Yes
                          Gerrit-Has-Labels: Yes
                          Comment-In-Reply-To: Mark Freeman <markf...@google.com>
                          unsatisfied_requirement
                          satisfied_requirement
                          open
                          diffy
                          Reply all
                          Reply to author
                          Forward
                          0 new messages