From 568eccd7f8dc44405184e281e7510852d590ae8f Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 6 May 2026 14:44:09 +0800 Subject: [PATCH] fixes #25617; handle backend type aliasing in procParamTypeRel (#25692) fixes #25617 This pull request introduces a stricter check for parameter type relations in the `procParamTypeRel` procedure. Specifically, it ensures that two types are not only structurally equal but also have the same backend type, taking type aliases into account. Type relation checks: * [`compiler/sigmatch.nim`](diffhunk://#diff-251afcd01d239369019495096c187998dd6695b6457528953237a7e4a10f7138R787-R789): In `procParamTypeRel`, added a check to ensure that if two types are considered equal (`isEqual`), they must also have the same backend type (using `sameBackendTypePickyAliases`). If not, the result is set to `isNone`, preventing false positives when type aliases differ. --- changelog.md | 4 +++ compiler/options.nim | 3 ++ compiler/sigmatch.nim | 11 ++++++++ compiler/types.nim | 2 +- doc/manual.md | 7 +++++ tests/concepts/tconcepts_issues.nim | 36 +++++++++++++++++++++++ tests/proc/tbackendtypealias.nim | 44 +++++++++++++++++++++++++++++ 7 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 tests/proc/tbackendtypealias.nim diff --git a/changelog.md b/changelog.md index aa0485975e..f87dadae63 100644 --- a/changelog.md +++ b/changelog.md @@ -35,6 +35,10 @@ errors. - Adds a new warning `--warning:ImplicitRangeConversion` that detects downsizing implicit conversions to range types (e.g., `int -> range[0..255]` or `range[1..256] -> range[0..255]`) that could cause runtime panics. Safe conversions like `range[0..255] -> range[0..65535]` and explicit casts do not trigger warnings. `int` to `Natural` and `Positive` conversions do not trigger warnings, which can be enabled with `--warning:systemRangeConversion`. +- Procedure compatibility also checks the backend representation of the +parameter and result types, not just their source-level shape. Use +`--legacy:procParamTypeBackendAliases` to restore the older behavior. + ## Standard library additions and changes [//]: # "Additions:" diff --git a/compiler/options.nim b/compiler/options.nim index bd84e0ee7b..7a28b1dc6f 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -259,6 +259,9 @@ type ## Old transformation for closures in JS backend noPanicOnExcept ## don't panic on bare except + procParamTypeBackendAliases + ## Keep the old proc type compatibility rules that ignore backend + ## c type aliases. SymbolFilesOption* = enum disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 55fcc43bd9..bf9c2d2050 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -784,6 +784,17 @@ proc procParamTypeRel(c: var TCandidate; f, a: PType): TTypeRelation = # if f is metatype. result = typeRel(c, f, a) + if result == isEqual and + procParamTypeBackendAliases notin c.c.config.legacyFeatures: + # Ensure types that are semantically equal also match at the backend level. + # E.g. reject assigning proc(csize_t) to proc(uint) since these map to + # different C types (size_t vs unsigned long long). + let fCheck = concreteType(c, f) + let aCheck = concreteType(c, a) + if fCheck != nil and aCheck != nil and + not sameBackendTypePickyAliases(fCheck, aCheck): + result = isNone + if result <= isSubrange or inconsistentVarTypes(f, a): result = isNone diff --git a/compiler/types.nim b/compiler/types.nim index 62831624b9..de24471e7d 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -897,7 +897,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = c.flags = oldFlags if x == y: return true - let aliasSkipSet = maybeSkipRange({tyAlias}) + let aliasSkipSet = maybeSkipRange({tyAlias, tyInferred}) var a = skipTypes(x, aliasSkipSet) while a.kind == tyUserTypeClass and tfResolved in a.flags: a = skipTypes(a.last, aliasSkipSet) diff --git a/doc/manual.md b/doc/manual.md index ab06f7aa4b..40897ac108 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -1024,6 +1024,9 @@ These are the major type classes: * procedural type * generic type +The compiler's internal type zoo is richer than this summary suggests: +some types that are structurally equal still differ in backend representation. + Ordinal types ------------- @@ -2174,6 +2177,10 @@ Procedural type A procedural type is internally a pointer to a procedure. `nil` is an allowed value for a variable of a procedural type. +Procedure compatibility also checks the backend representation of the +parameter and result types, not just their source-level shape. Use +`--legacy:procParamTypeBackendAliases` to restore the older behavior. + Examples: ```nim diff --git a/tests/concepts/tconcepts_issues.nim b/tests/concepts/tconcepts_issues.nim index c6d0267c5c..02d9d25bf1 100644 --- a/tests/concepts/tconcepts_issues.nim +++ b/tests/concepts/tconcepts_issues.nim @@ -176,6 +176,42 @@ block t6462: var s = SeqGen[int](fil: FilterMixin[int](test: nil, trans: nil)) doAssert s.test() == nil +block concept_with_cint: + # Generic proc matching through concepts with cint should still work + type + FilterMixin[T] = ref object + test: (T) -> bool + trans: (T) -> T + + SeqGen[T] = ref object + fil: FilterMixin[T] + + WithFilter[T] = concept a + a.fil is FilterMixin[T] + + proc test[T](a: WithFilter[T]): (T) -> bool = + a.fil.test + + var s = SeqGen[cint](fil: FilterMixin[cint](test: nil, trans: nil)) + doAssert s.test() == nil + +block concept_with_int: + type + FilterMixin[T] = ref object + test: (T) -> bool + trans: (T) -> T + + SeqGen[T] = ref object + fil: FilterMixin[T] + + WithFilter[T] = concept a + a.fil is FilterMixin[T] + + proc test[T](a: WithFilter[T]): (T) -> bool = + a.fil.test + + var s = SeqGen[int](fil: FilterMixin[int](test: nil, trans: nil)) + doAssert s.test() == nil block t6770: diff --git a/tests/proc/tbackendtypealias.nim b/tests/proc/tbackendtypealias.nim new file mode 100644 index 0000000000..eaae0c2917 --- /dev/null +++ b/tests/proc/tbackendtypealias.nim @@ -0,0 +1,44 @@ +# bug #25617 +# Ensure that proc types with backend type alias mismatches +# (e.g. uint vs csize_t) are rejected at the Nim level rather +# than producing invalid C code. + +discard """ + cmd: "nim check --hints:off --warnings:off --errorMax:0 $file" + action: "reject" + nimout: ''' +tbackendtypealias.nim(21, 7) Error: type mismatch: got but expected 'proc (len: uint){.closure.}' +tbackendtypealias.nim(28, 7) Error: type mismatch: got but expected 'proc (len: csize_t){.closure.}' +''' +""" + +block direct_assignment: + # Direct proc variable assignment with backend type alias mismatch + var + a: proc (len: uint) + b: proc (len: csize_t) + c = a + c = b + +block direct_assignment_reverse: + var + a: proc (len: csize_t) + b: proc (len: uint) + c = a + c = b + +block same_backend_type: + # Same backend type should still work + var + a: proc (len: uint) + b: proc (len: uint) + c = a + c = b + +block cint_same_type: + # cint to cint should work + var + a: proc (len: cint) + b: proc (len: cint) + c = a + c = b