mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
delay resolved procvar check for proc params + acknowledge unresolved statics (#23188)
fixes #23186
As explained in #23186, generics can transform `genericProc[int]` into a
call `` `[]`(genericProc, int) `` which causes a problem when
`genericProc` is resemmed, since it is not a resolved generic proc. `[]`
needs unresolved generic procs since `mArrGet` also handles explicit
generic instantiations, so delay the resolved generic proc check to
`semFinishOperands` which is intentionally not called for `mArrGet`.
The root issue for
[t6137](https://github.com/nim-lang/Nim/blob/devel/tests/generics/t6137.nim)
is also fixed (because this change breaks it otherwise), the compiler
doesn't consider the possibility that an assigned generic param can be
an unresolved static value (note the line `if t.kind == tyStatic: s.ast
= t.n` below the change in sigmatch), now it properly errors that it
couldn't instantiate it as it would for a type param. ~~The change in
semtypinst is just for symmetry with the code above it which also gives
a `cannot instantiate` error, it may or may not be necessary/correct.~~
Now removed, I don't think it was correct.
Still possible that this has unintended consequences.
(cherry picked from commit e8092a5470)
This commit is contained in:
@@ -54,17 +54,6 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
# same as 'semExprWithType' but doesn't check for proc vars
|
||||
result = semExpr(c, n, flags + {efOperand})
|
||||
if result.typ != nil:
|
||||
# XXX tyGenericInst here?
|
||||
if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}):
|
||||
#and tfUnresolved in result.typ.flags:
|
||||
let owner = result.typ.owner
|
||||
let err =
|
||||
# consistent error message with evaltempl/semMacroExpr
|
||||
if owner != nil and owner.kind in {skTemplate, skMacro}:
|
||||
errMissingGenericParamsForTemplate % n.renderTree
|
||||
else:
|
||||
errProcHasNoConcreteType % n.renderTree
|
||||
localError(c.config, n.info, err)
|
||||
if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
|
||||
elif {efWantStmt, efAllowStmt} * flags != {}:
|
||||
result.typ = newTypeS(tyVoid, c)
|
||||
@@ -1005,6 +994,30 @@ proc bracketedMacro(n: PNode): PSym =
|
||||
if result.kind notin {skMacro, skTemplate}:
|
||||
result = nil
|
||||
|
||||
proc finishOperand(c: PContext, a: PNode): PNode =
|
||||
if a.typ.isNil:
|
||||
result = c.semOperand(c, a, {efDetermineType})
|
||||
else:
|
||||
result = a
|
||||
# XXX tyGenericInst here?
|
||||
if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}):
|
||||
#and tfUnresolved in result.typ.flags:
|
||||
let owner = result.typ.owner
|
||||
let err =
|
||||
# consistent error message with evaltempl/semMacroExpr
|
||||
if owner != nil and owner.kind in {skTemplate, skMacro}:
|
||||
errMissingGenericParamsForTemplate % a.renderTree
|
||||
else:
|
||||
errProcHasNoConcreteType % a.renderTree
|
||||
localError(c.config, a.info, err)
|
||||
considerGenSyms(c, result)
|
||||
|
||||
proc semFinishOperands(c: PContext; n: PNode) =
|
||||
# this needs to be called to ensure that after overloading resolution every
|
||||
# argument has been sem'checked:
|
||||
for i in 1..<n.len:
|
||||
n[i] = finishOperand(c, n[i])
|
||||
|
||||
proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||
if efNoSemCheck notin flags and n.typ != nil and n.typ.kind == tyError:
|
||||
return errorNode(c, n)
|
||||
|
||||
@@ -60,6 +60,12 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym
|
||||
elif t.kind in {tyGenericParam, tyConcept}:
|
||||
localError(c.config, a.info, errCannotInstantiateX % q.name.s)
|
||||
t = errorType(c)
|
||||
elif isUnresolvedStatic(t) and c.inGenericContext == 0 and
|
||||
c.matchedConcept == nil:
|
||||
# generic/concept type bodies will try to instantiate static values but
|
||||
# won't actually use them
|
||||
localError(c.config, a.info, errCannotInstantiateX % q.name.s)
|
||||
t = errorType(c)
|
||||
elif t.kind == tyGenericInvocation:
|
||||
#t = instGenericContainer(c, a, t)
|
||||
t = generateTypeInstance(c, pt, a, t)
|
||||
@@ -364,10 +370,13 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
# see ttypeor.nim test.
|
||||
var i = 0
|
||||
newSeq(entry.concreteTypes, fn.typ.len+gp.len-1)
|
||||
# let param instantiation know we are in a concept for unresolved statics:
|
||||
c.matchedConcept = oldMatchedConcept
|
||||
for s in instantiateGenericParamList(c, gp, pt):
|
||||
addDecl(c, s)
|
||||
entry.concreteTypes[i] = s.typ
|
||||
inc i
|
||||
c.matchedConcept = nil
|
||||
pushProcCon(c, result)
|
||||
instantiateProcType(c, pt, result, info)
|
||||
for j in 1..<result.typ.len:
|
||||
|
||||
@@ -2683,12 +2683,6 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
|
||||
m.firstMismatch.arg = a
|
||||
m.firstMismatch.formal = formal
|
||||
|
||||
proc semFinishOperands*(c: PContext, n: PNode) =
|
||||
# this needs to be called to ensure that after overloading resolution every
|
||||
# argument has been sem'checked:
|
||||
for i in 1..<n.len:
|
||||
n[i] = prepareOperand(c, n[i])
|
||||
|
||||
proc partialMatch*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
|
||||
# for 'suggest' support:
|
||||
var marker = initIntSet()
|
||||
|
||||
@@ -17,9 +17,9 @@ proc bar(x: proc (x: int)) =
|
||||
let x = t #[tt.Error
|
||||
^ 't' has unspecified generic parameters]#
|
||||
bar t #[tt.Error
|
||||
^ 't' has unspecified generic parameters]#
|
||||
^ type mismatch: got <template [*missing parameters*]()>]#
|
||||
|
||||
let y = m #[tt.Error
|
||||
^ 'm' has unspecified generic parameters]#
|
||||
bar m #[tt.Error
|
||||
^ 'm' has unspecified generic parameters]#
|
||||
^ type mismatch: got <macro [*missing parameters*](): untyped{.noSideEffect, gcsafe.}>]#
|
||||
|
||||
155
tests/generics/t23186.nim
Normal file
155
tests/generics/t23186.nim
Normal file
@@ -0,0 +1,155 @@
|
||||
# issue #23186
|
||||
|
||||
block: # simplified
|
||||
template typedTempl(x: int, body): untyped =
|
||||
body
|
||||
proc generic1[T]() =
|
||||
discard
|
||||
proc generic2[T]() =
|
||||
typedTempl(1):
|
||||
let x = generic1[T]
|
||||
generic2[int]()
|
||||
|
||||
import std/macros
|
||||
|
||||
when not compiles(len((1, 2))):
|
||||
import std/typetraits
|
||||
|
||||
func len(x: tuple): int =
|
||||
arity(type(x))
|
||||
|
||||
block: # full issue example
|
||||
type FieldDescription = object
|
||||
name: NimNode
|
||||
func isTuple(t: NimNode): bool =
|
||||
t.kind == nnkBracketExpr and t[0].kind == nnkSym and eqIdent(t[0], "tuple")
|
||||
proc collectFieldsFromRecList(result: var seq[FieldDescription],
|
||||
n: NimNode,
|
||||
parentCaseField: NimNode = nil,
|
||||
parentCaseBranch: NimNode = nil,
|
||||
isDiscriminator = false) =
|
||||
case n.kind
|
||||
of nnkRecList:
|
||||
for entry in n:
|
||||
collectFieldsFromRecList result, entry,
|
||||
parentCaseField, parentCaseBranch
|
||||
of nnkIdentDefs:
|
||||
for i in 0 ..< n.len - 2:
|
||||
var field: FieldDescription
|
||||
field.name = n[i]
|
||||
if field.name.kind == nnkPragmaExpr:
|
||||
field.name = field.name[0]
|
||||
if field.name.kind == nnkPostfix:
|
||||
field.name = field.name[1]
|
||||
result.add field
|
||||
of nnkNilLit, nnkDiscardStmt, nnkCommentStmt, nnkEmpty:
|
||||
discard
|
||||
else:
|
||||
doAssert false, "Unexpected nodes in recordFields:\n" & n.treeRepr
|
||||
proc collectFieldsInHierarchy(result: var seq[FieldDescription],
|
||||
objectType: NimNode) =
|
||||
var objectType = objectType
|
||||
if objectType.kind == nnkRefTy:
|
||||
objectType = objectType[0]
|
||||
let recList = objectType[2]
|
||||
collectFieldsFromRecList result, recList
|
||||
proc recordFields(typeImpl: NimNode): seq[FieldDescription] =
|
||||
let objectType = case typeImpl.kind
|
||||
of nnkObjectTy: typeImpl
|
||||
of nnkTypeDef: typeImpl[2]
|
||||
else:
|
||||
macros.error("object type expected", typeImpl)
|
||||
return
|
||||
collectFieldsInHierarchy(result, objectType)
|
||||
proc skipPragma(n: NimNode): NimNode =
|
||||
if n.kind == nnkPragmaExpr: n[0]
|
||||
else: n
|
||||
func declval(T: type): T =
|
||||
doAssert false,
|
||||
"declval should be used only in `typeof` expressions and concepts"
|
||||
default(ptr T)[]
|
||||
macro enumAllSerializedFieldsImpl(T: type, body: untyped): untyped =
|
||||
var typeAst = getType(T)[1]
|
||||
var typeImpl: NimNode
|
||||
let isSymbol = not typeAst.isTuple
|
||||
if not isSymbol:
|
||||
typeImpl = typeAst
|
||||
else:
|
||||
typeImpl = getImpl(typeAst)
|
||||
result = newStmtList()
|
||||
var i = 0
|
||||
for field in recordFields(typeImpl):
|
||||
let
|
||||
fieldIdent = field.name
|
||||
realFieldName = newLit($fieldIdent.skipPragma)
|
||||
fieldName = realFieldName
|
||||
fieldIndex = newLit(i)
|
||||
let fieldNameDefs =
|
||||
if isSymbol:
|
||||
quote:
|
||||
const fieldName {.inject, used.} = `fieldName`
|
||||
const realFieldName {.inject, used.} = `realFieldName`
|
||||
else:
|
||||
quote:
|
||||
const fieldName {.inject, used.} = $`fieldIndex`
|
||||
const realFieldName {.inject, used.} = $`fieldIndex`
|
||||
# we can't access .Fieldn, so our helper knows
|
||||
# to parseInt this
|
||||
let field =
|
||||
if isSymbol:
|
||||
quote do: declval(`T`).`fieldIdent`
|
||||
else:
|
||||
quote do: declval(`T`)[`fieldIndex`]
|
||||
result.add quote do:
|
||||
block:
|
||||
`fieldNameDefs`
|
||||
type FieldType {.inject, used.} = type(`field`)
|
||||
`body`
|
||||
i += 1
|
||||
template enumAllSerializedFields(T: type, body): untyped =
|
||||
when T is ref|ptr:
|
||||
type TT = type(default(T)[])
|
||||
enumAllSerializedFieldsImpl(TT, body)
|
||||
else:
|
||||
enumAllSerializedFieldsImpl(T, body)
|
||||
type
|
||||
MemRange = object
|
||||
startAddr: ptr byte
|
||||
length: int
|
||||
SszNavigator[T] = object
|
||||
m: MemRange
|
||||
func sszMount(data: openArray[byte], T: type): SszNavigator[T] =
|
||||
let startAddr = unsafeAddr data[0]
|
||||
SszNavigator[T](m: MemRange(startAddr: startAddr, length: data.len))
|
||||
func sszMount(data: openArray[char], T: type): SszNavigator[T] =
|
||||
let startAddr = cast[ptr byte](unsafeAddr data[0])
|
||||
SszNavigator[T](m: MemRange(startAddr: startAddr, length: data.len))
|
||||
template sszMount(data: MemRange, T: type): SszNavigator[T] =
|
||||
SszNavigator[T](m: data)
|
||||
func navigateToField[T](
|
||||
n: SszNavigator[T],
|
||||
FieldType: type): SszNavigator[FieldType] =
|
||||
default(SszNavigator[FieldType])
|
||||
type
|
||||
FieldInfo = ref object
|
||||
navigator: proc (m: MemRange): MemRange {.
|
||||
gcsafe, noSideEffect, raises: [IOError] .}
|
||||
func fieldNavigatorImpl[RecordType; FieldType; fieldName: static string](
|
||||
m: MemRange): MemRange =
|
||||
var typedNavigator = sszMount(m, RecordType)
|
||||
discard navigateToField(typedNavigator, FieldType)
|
||||
default(MemRange)
|
||||
func genTypeInfo(T: type) =
|
||||
when T is object:
|
||||
enumAllSerializedFields(T):
|
||||
discard FieldInfo(navigator: fieldNavigatorImpl[T, FieldType, fieldName])
|
||||
type
|
||||
Foo = object
|
||||
bar: Bar
|
||||
BarList = seq[uint64]
|
||||
Bar = object
|
||||
b: BarList
|
||||
baz: Baz
|
||||
Baz = object
|
||||
i: uint64
|
||||
genTypeInfo(Foo)
|
||||
@@ -1,6 +1,6 @@
|
||||
discard """
|
||||
errormsg: "\'vectFunc\' doesn't have a concrete type, due to unspecified generic parameters."
|
||||
line: 28
|
||||
errormsg: "cannot instantiate: 'T'"
|
||||
line: 19
|
||||
"""
|
||||
|
||||
type
|
||||
|
||||
@@ -34,7 +34,8 @@ block: #15622
|
||||
proc test1[T](a: T, b: static[string] = "") = discard
|
||||
test1[int64](123)
|
||||
proc test2[T](a: T, b: static[string] = "") = discard
|
||||
test2[int64, static[string]](123)
|
||||
doAssert not (compiles do:
|
||||
test2[int64, static[string]](123))
|
||||
|
||||
block: #4688
|
||||
proc convertTo[T](v: int or float): T = (T)(v)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
discard """
|
||||
targets: "c cpp js"
|
||||
# just tests that this doesn't crash the compiler
|
||||
errormsg: "cannot instantiate: 'a:type'"
|
||||
"""
|
||||
|
||||
# bug #8545
|
||||
|
||||
Reference in New Issue
Block a user