fix #17630: Implement cycle detection for recursive concepts (#25353)

fixes #17630

## Recursive Concept Cycle Detection

- Track (conceptId, typeId) pairs during matching to detect cycles
- Changed marker from IntSet to HashSet[ConceptTypePair]
- Removed unused depthCount field
- Added recursive concepts documentation to manual
- Added tests for recursive concepts, distinct chains, and co-dependent
concepts

## Fix Flaky `tasyncclosestall` Test

The macOS ARM64 CI jobs were failing due to a flaky async socket test
(unrelated to concepts).

The test only accepted `EBADF` as a valid error code when closing a
socket with pending writes. However, depending on timing, the kernel may
report `ECONNRESET` or `EPIPE` instead:

- **EBADF**: Socket was closed locally before kernel detected remote
state
- **ECONNRESET**: Remote peer sent RST packet (detected first)  
- **EPIPE**: Socket is no longer connected (broken pipe)

All three are valid disconnection errors. The fix accepts any of them,
making the test reliable across platforms.

---------

Co-authored-by: Andreas Rumpf <araq4k@proton.me>
This commit is contained in:
elijahr
2025-12-20 01:56:10 -06:00
committed by GitHub
parent 548b1c6ef8
commit 1324183c38
4 changed files with 216 additions and 13 deletions

View File

@@ -3025,6 +3025,44 @@ If neither of them are subsets of one another, then the disambiguation proceeds
and the concept with the most definitions wins, if any. No definite winner is an ambiguity error at
compile time.
Recursive concepts
------------------
Concepts can reference themselves in their definitions, enabling recursive type constraints.
This is useful for matching `distinct` types that should inherit traits from their base type:
```nim
import std/typetraits
type
PrimitiveBase = SomeNumber | bool | ptr | pointer | enum
# Matches PrimitiveBase directly, or any distinct type whose base is Primitive
Primitive = concept x
x is PrimitiveBase or distinctBase(x) is Primitive
# Application: a handle type that should be treated like a primitive
Handle = distinct int
SpecialHandle = distinct Handle
assert int is Primitive
assert Handle is Primitive
assert SpecialHandle is Primitive # works through 2 levels
assert not (string is Primitive)
```
Concepts can also be mutually recursive (co-dependent):
```nim
type
Serializable = concept
proc serialize(s: Self; writer: var Writer)
Writer = concept
proc write(w: var Self; data: Serializable)
```
The compiler uses cycle detection to handle these cases without infinite recursion.
Statements and expressions
==========================