mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-12 14:23:45 +00:00
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>
(cherry picked from commit 1324183c38)
133 lines
3.5 KiB
Nim
133 lines
3.5 KiB
Nim
discard """
|
|
action: "run"
|
|
output: '''
|
|
int is Primitive: true
|
|
Handle is Primitive: true
|
|
SpecialHandle is Primitive: true
|
|
FileDescriptor is Primitive: true
|
|
float is Primitive: false
|
|
string is Primitive: false
|
|
char is PrimitiveBase: true
|
|
ptr int is PrimitiveBase: true
|
|
'''
|
|
"""
|
|
|
|
# Test recursive concepts with cycle detection
|
|
# This tests concepts that reference themselves via distinctBase
|
|
|
|
import std/typetraits
|
|
|
|
block: # Basic recursive concept with distinctBase
|
|
type
|
|
PrimitiveBase = SomeInteger | bool | char | ptr | pointer
|
|
|
|
# Recursive concept: matches PrimitiveBase or any distinct type whose base is Primitive
|
|
Primitive = concept x
|
|
x is PrimitiveBase or distinctBase(x) is Primitive
|
|
|
|
# Real-world example: handle types that wrap integers
|
|
Handle = distinct int
|
|
SpecialHandle = distinct Handle
|
|
FileDescriptor = distinct SpecialHandle
|
|
|
|
# Direct base types
|
|
echo "int is Primitive: ", int is Primitive
|
|
|
|
# Single-level distinct (like a simple handle type)
|
|
echo "Handle is Primitive: ", Handle is Primitive
|
|
|
|
# Two-level distinct
|
|
echo "SpecialHandle is Primitive: ", SpecialHandle is Primitive
|
|
|
|
# Three-level distinct
|
|
echo "FileDescriptor is Primitive: ", FileDescriptor is Primitive
|
|
|
|
# Non-primitive types should NOT match
|
|
echo "float is Primitive: ", float is Primitive
|
|
echo "string is Primitive: ", string is Primitive
|
|
|
|
block: # Ensure base type matching still works
|
|
type
|
|
PrimitiveBase = SomeInteger | bool | char | ptr | pointer
|
|
|
|
echo "char is PrimitiveBase: ", char is PrimitiveBase
|
|
echo "ptr int is PrimitiveBase: ", (ptr int) is PrimitiveBase
|
|
|
|
block: # Test that cycle detection doesn't break normal concept matching
|
|
type
|
|
Addable = concept x, y
|
|
x + y is typeof(x)
|
|
|
|
doAssert int is Addable
|
|
doAssert float is Addable
|
|
|
|
block: # Test non-matching recursive case
|
|
type
|
|
IntegerBase = SomeInteger
|
|
|
|
IntegerLike = concept x
|
|
x is IntegerBase or distinctBase(x) is IntegerLike
|
|
|
|
Percentage = distinct float # float base, not integer
|
|
|
|
doAssert int is IntegerLike
|
|
doAssert not(float is IntegerLike)
|
|
doAssert not(Percentage is IntegerLike) # float base doesn't match
|
|
|
|
block: # Test deep distinct chains (5+ levels) - e.g., layered ID types
|
|
type
|
|
IdBase = SomeInteger
|
|
|
|
IdLike = concept x
|
|
x is IdBase or distinctBase(x) is IdLike
|
|
|
|
EntityId = distinct int
|
|
UserId = distinct EntityId
|
|
AdminId = distinct UserId
|
|
SuperAdminId = distinct AdminId
|
|
RootId = distinct SuperAdminId
|
|
|
|
doAssert int is IdLike
|
|
doAssert EntityId is IdLike
|
|
doAssert UserId is IdLike
|
|
doAssert AdminId is IdLike
|
|
doAssert SuperAdminId is IdLike
|
|
doAssert RootId is IdLike
|
|
doAssert not(float is IdLike)
|
|
|
|
block: # Test 3-way mutual recursion (co-dependent concepts)
|
|
# This tests that cycle detection properly handles A -> B -> C -> A cycles
|
|
type
|
|
Serializable = concept
|
|
proc serialize(x: Self): Bytes
|
|
|
|
Bytes = concept
|
|
proc compress(x: Self): Compressed
|
|
|
|
Compressed = concept
|
|
proc decompress(x: Self): Serializable
|
|
|
|
Data = object
|
|
value: int
|
|
|
|
proc serialize(x: Data): Data = x
|
|
proc compress(x: Data): Data = x
|
|
proc decompress(x: Data): Data = x
|
|
|
|
# Data should satisfy all three mutually recursive concepts
|
|
doAssert Data is Serializable
|
|
doAssert Data is Bytes
|
|
doAssert Data is Compressed
|
|
|
|
block: # Test concept with method returning same type
|
|
type
|
|
Cloneable = concept
|
|
proc clone(x: Self): Self
|
|
|
|
Document = object
|
|
content: string
|
|
|
|
proc clone(x: Document): Document = x
|
|
|
|
doAssert Document is Cloneable
|