Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

try all call kinds in generic type node handling #23411

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,8 @@ const
tfGcSafe* = tfThread
tfObjHasKids* = tfEnumHasHoles
tfReturnsNew* = tfInheritable
tfNonConstExpr* = tfExplicitCallConv
## tyFromExpr where the expression shouldn't be evaluated as a static value
skError* = skUnknown

var
Expand Down
9 changes: 8 additions & 1 deletion compiler/semcall.nim
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,13 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
var errors: CandidateErrors = @[] # if efExplain in flags: @[] else: nil
var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
if r.state == csMatch:
if c.inGenericContext > 0 and r.matchedUnresolvedStatic and
r.calleeSym.kind in {skMacro, skTemplate}:
# macros and templates with unresolved statics should not instantiate
# other routines are fine since the static will not be evaluated
result = semGenericStmt(c, n)
result.typ = makeTypeFromExpr(c, result.copyTree)
return
# this may be triggered, when the explain pragma is used
if errors.len > 0:
let (_, candidates) = presentFailedCandidates(c, n, errors)
Expand All @@ -751,7 +758,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
candidates)
result = semResolvedCall(c, r, n, flags, expectedType)
else:
if efDetermineType in flags and c.inGenericContext > 0 and c.matchedConcept == nil:
if c.inGenericContext > 0 and c.matchedConcept == nil:
result = semGenericStmt(c, n)
result.typ = makeTypeFromExpr(c, result.copyTree)
elif efExplain notin flags:
Expand Down
34 changes: 22 additions & 12 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1028,7 +1028,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,

if result != nil:
if result[0].kind != nkSym:
if not (efDetermineType in flags and c.inGenericContext > 0):
if not (c.inGenericContext > 0): # see generic context check in semOverloadedCall
internalError(c.config, "semOverloadedCallAnalyseEffects")
return
let callee = result[0].sym
Expand Down Expand Up @@ -1137,6 +1137,11 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
result.flags.incl nfExplicitCall
for i in 1..<n.len: result.add n[i]
return semExpr(c, result, flags, expectedType)
elif n0.typ.kind == tyFromExpr and c.inGenericContext > 0:
# don't make assumptions, entire expression needs to be tyFromExpr
result = semGenericStmt(c, n)
result.typ = makeTypeFromExpr(c, result.copyTree)
return
else:
n[0] = n0
else:
Expand Down Expand Up @@ -1480,17 +1485,17 @@ proc tryReadingGenericParam(c: PContext, n: PNode, i: PIdent, t: PType): PNode =
of tyTypeParamsHolders:
result = readTypeParameter(c, t, i, n.info)
if result == c.graph.emptyNode:
result = n
n.typ = makeTypeFromExpr(c, n.copyTree)
result = semGenericStmt(c, n)
result.typ = makeTypeFromExpr(c, result.copyTree)
of tyUserTypeClasses:
if t.isResolvedUserTypeClass:
result = readTypeParameter(c, t, i, n.info)
else:
n.typ = makeTypeFromExpr(c, copyTree(n))
result = n
of tyGenericParam, tyAnything:
n.typ = makeTypeFromExpr(c, copyTree(n))
result = n
result = semGenericStmt(c, n)
result.typ = makeTypeFromExpr(c, copyTree(result))
of tyFromExpr, tyGenericParam, tyAnything:
result = semGenericStmt(c, n)
result.typ = makeTypeFromExpr(c, copyTree(result))
else:
result = nil

Expand Down Expand Up @@ -1654,18 +1659,20 @@ proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode =
result.add(newIdentNode(ident, n.info))
for s in n: result.add s

proc semDeref(c: PContext, n: PNode): PNode =
proc semDeref(c: PContext, n: PNode, flags: TExprFlags): PNode =
checkSonsLen(n, 1, c.config)
n[0] = semExprWithType(c, n[0])
let a = getConstExpr(c.module, n[0], c.idgen, c.graph)
if a != nil:
if a.kind == nkNilLit:
if a.kind == nkNilLit and efInTypeof notin flags:
localError(c.config, n.info, "nil dereference is not allowed")
n[0] = a
result = n
var t = skipTypes(n[0].typ, {tyGenericInst, tyVar, tyLent, tyAlias, tySink, tyOwned})
case t.kind
of tyRef, tyPtr: n.typ = t.elementType
of tyMetaTypes, tyFromExpr:
n.typ = makeTypeFromExpr(c, n.copyTree)
else: result = nil
#GlobalError(n[0].info, errCircumNeedsPointer)

Expand Down Expand Up @@ -1696,8 +1703,11 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
## checking of assignments
result = nil
if n.len == 1:
let x = semDeref(c, n)
let x = semDeref(c, n, flags)
if x == nil: return nil
if x.typ.kind == tyFromExpr:
# depends on generic type
return x
result = newNodeIT(nkDerefExpr, x.info, x.typ)
result.add(x[0])
return
Expand Down Expand Up @@ -3392,7 +3402,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
result = semArrayConstr(c, n, flags, expectedType)
of nkObjConstr: result = semObjConstr(c, n, flags, expectedType)
of nkLambdaKinds: result = semProcAux(c, n, skProc, lambdaPragmas, flags)
of nkDerefExpr: result = semDeref(c, n)
of nkDerefExpr: result = semDeref(c, n, flags)
of nkAddr:
result = n
checkSonsLen(n, 1, c.config)
Expand Down
20 changes: 20 additions & 0 deletions compiler/semgnrc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,16 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
if s.typ != nil and s.typ.kind == tyStatic:
if s.typ.n != nil:
result = s.typ.n
elif c.inGenericContext > 0 and withinConcept notin flags:
# fine to give a symbol node a generic type here since
# we are in a generic context and `prepareNode` will be called
result = newSymNodeTypeDesc(s, c.idgen, n.info)
if canOpenSym(result.sym):
if genericsOpenSym in c.features:
result = newOpenSym(result)
else:
result.flags.incl nfDisabledOpenSym
result.typ = nil
else:
result = n
else:
Expand All @@ -128,6 +138,16 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
else:
result.flags.incl nfDisabledOpenSym
result.typ = nil
elif c.inGenericContext > 0 and withinConcept notin flags:
# fine to give a symbol node a generic type here since
# we are in a generic context and `prepareNode` will be called
result = newSymNodeTypeDesc(s, c.idgen, n.info)
if canOpenSym(result.sym):
if genericsOpenSym in c.features:
result = newOpenSym(result)
else:
result.flags.incl nfDisabledOpenSym
result.typ = nil
else:
result = n
onUse(n.info, s)
Expand Down
8 changes: 3 additions & 5 deletions compiler/semtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1684,6 +1684,8 @@ proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
# unnecessary new type creation
let alias = maybeAliasType(c, result, prev)
if alias != nil: result = alias
elif n.typ.kind == tyFromExpr and c.inGenericContext > 0:
result = n.typ
else:
localError(c.config, n.info, "expected type, but got: " & n.renderTree)
result = errorType(c)
Expand Down Expand Up @@ -2049,11 +2051,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
elif op.s == "owned" and optOwnedRefs notin c.config.globalOptions and n.len == 2:
result = semTypeExpr(c, n[1], prev)
else:
if c.inGenericContext > 0 and n.kind == nkCall:
let n = semGenericStmt(c, n)
result = makeTypeFromExpr(c, n.copyTree)
else:
result = semTypeExpr(c, n, prev)
result = semTypeExpr(c, n, prev)
of nkWhenStmt:
var whenResult = semWhen(c, n, false)
if whenResult.kind == nkStmtList: whenResult.transitionSonsKind(nkStmtListType)
Expand Down
72 changes: 62 additions & 10 deletions compiler/semtypinst.nim
Original file line number Diff line number Diff line change
Expand Up @@ -131,15 +131,59 @@ proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
replaceTypeVarsS(cl, n.sym, result.typ)
else:
replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ))
let isCall = result.kind in nkCallKinds
# don't try to instantiate symchoice symbols, they can be
# generic procs which the compiler will think are uninstantiated
# because their type will contain uninstantiated params
let isSymChoice = result.kind in nkSymChoices
for i in 0..<n.safeLen:
# XXX HACK: ``f(a, b)``, avoid to instantiate `f`
if isSymChoice or (isCall and i == 0): result.add(n[i])
else: result.add(prepareNode(cl, n[i]))
case n.kind
of nkSymChoices:
# don't try to instantiate symchoice symbols, they can be
# generic procs which the compiler will think are uninstantiated
# because their type will contain uninstantiated params
for i in 0..<n.len:
result.add(n[i])
of nkCallKinds:
# don't try to instantiate call names since they may be generic proc syms
# also bracket expressions can turn into calls with symchoice [] and
# we need to not instantiate the Generic in Generic[int]
# exception exists for the call name being a dot expression since
# dot expressions need their LHS instantiated
assert n.len != 0
let ignoreFirst = n[0].kind != nkDotExpr
let name = n[0].getPIdent
let ignoreSecond = name != nil and name.s == "[]" and n.len > 1 and
(n[1].typ != nil and n[1].typ.kind == tyTypeDesc)
if ignoreFirst:
result.add(n[0])
else:
result.add(prepareNode(cl, n[0]))
if n.len > 1:
if ignoreSecond:
result.add(n[1])
else:
result.add(prepareNode(cl, n[1]))
for i in 2..<n.len:
result.add(prepareNode(cl, n[i]))
of nkBracketExpr:
# don't instantiate Generic body type in expression like Generic[T]
# exception exists for the call name being a dot expression since
# dot expressions need their LHS instantiated
assert n.len != 0
let ignoreFirst = n[0].kind != nkDotExpr and
n[0].typ != nil and n[0].typ.kind == tyTypeDesc
if ignoreFirst:
result.add(n[0])
else:
result.add(prepareNode(cl, n[0]))
for i in 1..<n.len:
result.add(prepareNode(cl, n[i]))
of nkDotExpr:
# don't try to instantiate RHS of dot expression, it can outright be
# undeclared, but definitely instantiate LHS
assert n.len >= 2
result.add(prepareNode(cl, n[0]))
result.add(n[1])
for i in 2..<n.len:
result.add(prepareNode(cl, n[i]))
else:
for i in 0..<n.safeLen:
result.add(prepareNode(cl, n[i]))

proc isTypeParam(n: PNode): bool =
# XXX: generic params should use skGenericParam instead of skType
Expand Down Expand Up @@ -222,6 +266,9 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PT
if n == nil: return
result = copyNode(n)
if n.typ != nil:
if n.typ.kind == tyFromExpr:
# type of node should not be evaluated as a static value
n.typ.flags.incl tfNonConstExpr
result.typ = replaceTypeVarsT(cl, n.typ)
checkMetaInvariants(cl, result.typ)
case n.kind
Expand Down Expand Up @@ -591,13 +638,18 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
assert t.n.typ != t
var n = prepareNode(cl, t.n)
if n.kind != nkEmpty:
n = cl.c.semConstExpr(cl.c, n)
if tfNonConstExpr in t.flags:
n = cl.c.semExprWithType(cl.c, n, flags = {efInTypeof})
else:
n = cl.c.semConstExpr(cl.c, n)
if n.typ.kind == tyTypeDesc:
# XXX: sometimes, chained typedescs enter here.
# It may be worth investigating why this is happening,
# because it may cause other bugs elsewhere.
result = n.typ.skipTypes({tyTypeDesc})
# result = n.typ.base
elif tfNonConstExpr in t.flags:
result = n.typ
else:
if n.typ.kind != tyStatic and n.kind != nkType:
# XXX: In the future, semConstExpr should
Expand Down
7 changes: 5 additions & 2 deletions compiler/sigmatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ type
inheritancePenalty: int
firstMismatch*: MismatchInfo # mismatch info for better error messages
diagnosticsEnabled*: bool
matchedUnresolvedStatic*: bool

TTypeRelFlag* = enum
trDontBind
Expand Down Expand Up @@ -1216,6 +1217,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
return isGeneric
else: discard

if aOrig.kind == tyStatic and aOrig.n == nil:
c.matchedUnresolvedStatic = true

case f.kind
of tyEnum:
if a.kind == f.kind and sameEnumTypes(f, a): result = isEqual
Expand Down Expand Up @@ -1913,8 +1917,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
# proc foo(T: typedesc, x: T)
# when `f` is an unresolved typedesc, `a` could be any
# type, so we should not perform this check earlier
if c.c.inGenericContext > 0 and
a.skipTypes({tyTypeDesc}).kind == tyGenericParam:
if c.c.inGenericContext > 0 and a.containsGenericType:
# generic type bodies can sometimes compile call expressions
# prevent unresolved generic parameters from being passed to procs as
# typedesc parameters
Expand Down
70 changes: 70 additions & 0 deletions tests/generics/t23854.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# issue #23854, not entirely fixed

import std/bitops

const WordBitWidth = sizeof(pointer) * 8

func wordsRequired*(bits: int): int {.inline.} =
const divShiftor = fastLog2(uint32(WordBitWidth))
result = (bits + WordBitWidth - 1) shr divShiftor

type
Algebra* = enum
BLS12_381

BigInt*[bits: static int] = object
limbs*: array[wordsRequired(bits), uint]

Fr*[Name: static Algebra] = object
residue_form*: BigInt[255]

Fp*[Name: static Algebra] = object
residue_form*: BigInt[381]

FF*[Name: static Algebra] = Fp[Name] or Fr[Name]

template getBigInt*[Name: static Algebra](T: type FF[Name]): untyped =
## Get the underlying BigInt type.
typeof(default(T).residue_form)

type
EC_ShortW_Aff*[F] = object
## Elliptic curve point for a curve in Short Weierstrass form
## y² = x³ + a x + b
##
## over a field F
x*, y*: F

type FieldKind* = enum
kBaseField
kScalarField

func bits*[Name: static Algebra](T: type FF[Name]): static int =
T.getBigInt().bits

template getScalarField*(EC: type EC_ShortW_Aff): untyped =
Fr[EC.F.Name]

# ------------------------------------------------------------------------------

type
ECFFT_Descriptor*[EC] = object
## Metadata for FFT on Elliptic Curve
order*: int
rootsOfUnity1*: ptr UncheckedArray[BigInt[EC.getScalarField().bits()]] # Error: in expression 'EC.getScalarField()': identifier expected, but found 'EC.getScalarField'
rootsOfUnity2*: ptr UncheckedArray[BigInt[getScalarField(EC).bits()]] # Compiler SIGSEGV: Illegal Storage Access

func new*(T: type ECFFT_Descriptor): T =
discard

# ------------------------------------------------------------------------------

template getBits[bits: static int](x: ptr UncheckedArray[BigInt[bits]]): int = bits

proc main() =
let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new()
when false: echo getBits(ctx.rootsOfUnity2) # doesn't work yet?
doAssert ctx.rootsOfUnity1[0].limbs.len == wordsRequired(255)
doAssert ctx.rootsOfUnity2[0].limbs.len == wordsRequired(255)

main()
Loading
Loading