Skip to content
Merged
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
2 changes: 1 addition & 1 deletion compiler/semcall.nim
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,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
24 changes: 24 additions & 0 deletions compiler/semgnrc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,18 @@ 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:
# don't leave generic param as identifier node in generic type,
# sigmatch will try to instantiate generic type AST without all params
# 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 +140,18 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
else:
result.flags.incl nfDisabledOpenSym
result.typ = nil
elif c.inGenericContext > 0 and withinConcept notin flags:
# don't leave generic param as identifier node in generic type,
# sigmatch will try to instantiate generic type AST without all params
# 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
10 changes: 5 additions & 5 deletions compiler/semtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1684,6 +1684,10 @@ 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:
# sometimes not possible to distinguish type from value in generic body,
# for example `T.Foo`, so both are handled under `tyFromExpr`
result = n.typ
else:
localError(c.config, n.info, "expected type, but got: " & n.renderTree)
result = errorType(c)
Expand Down Expand Up @@ -2049,11 +2053,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
74 changes: 64 additions & 10 deletions compiler/semtypinst.nim
Original file line number Diff line number Diff line change
Expand Up @@ -131,15 +131,61 @@ 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]))
# we need to avoid trying to instantiate nodes that can have uninstantiated
# types, like generic proc symbols or raw generic type symbols
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 +268,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 +640,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
10 changes: 7 additions & 3 deletions compiler/sigmatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1858,7 +1858,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
let prev = idTableGet(c.bindings, f)
if prev == nil:
if aOrig.kind == tyStatic:
if f.base.kind notin {tyNone, tyGenericParam}:
if c.c.inGenericContext > 0 and aOrig.n == nil and not c.isNoCall:
# don't match unresolved static value to static param to avoid
# faulty instantiations in calls in generic bodies
# but not for generic invocations as they only check constraints
result = isNone
elif f.base.kind notin {tyNone, tyGenericParam}:
result = typeRel(c, f.base, a, flags)
if result != isNone and f.n != nil:
if not exprStructuralEquivalent(f.n, aOrig.n):
Expand Down Expand Up @@ -1913,8 +1918,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