Skip to content

Commit

Permalink
delay resolved procvar check for proc params + acknowledge unresolved…
Browse files Browse the repository at this point in the history
… 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.
  • Loading branch information
metagn authored Jan 11, 2024
1 parent e20a2b1 commit e8092a5
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 23 deletions.
35 changes: 24 additions & 11 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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, efAllowSymChoice})
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)
Expand Down Expand Up @@ -1013,6 +1002,30 @@ proc bracketedMacro(n: PNode): PSym =
else:
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)
Expand Down
9 changes: 9 additions & 0 deletions compiler/seminst.nim
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,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)
Expand Down Expand Up @@ -377,10 +383,13 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
# see ttypeor.nim test.
var i = 0
newSeq(entry.concreteTypes, fn.typ.paramsLen+gp.len)
# 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 _, param in paramTypes(result.typ):
Expand Down
6 changes: 0 additions & 6 deletions compiler/sigmatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2704,12 +2704,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()
Expand Down
4 changes: 2 additions & 2 deletions tests/errmsgs/t5167_5.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 155 additions & 0 deletions tests/generics/t23186.nim
Original file line number Diff line number Diff line change
@@ -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)
4 changes: 2 additions & 2 deletions tests/generics/t6137.nim
Original file line number Diff line number Diff line change
@@ -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
Expand Down
3 changes: 2 additions & 1 deletion tests/generics/timplicit_and_explicit.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion tests/misc/t8545.nim
Original file line number Diff line number Diff line change
@@ -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
Expand Down

0 comments on commit e8092a5

Please sign in to comment.