Files
Nim/compiler/semtypes.nim
Ryan McConnell dfab30734b new-style concepts adjusments (#24697)
Yet another one of these. Multiple changes piled up in this one. I've
only minimally cleaned it for now (debug code is still here etc). Just
want to start putting this up so I might get feedback. I know this is a
lot and you all are busy with bigger things. As per my last PR, this
might just contain changes that are not ready.

### concept instantiation uniqueness
It has already been said that concepts like `ArrayLike[int]` is not
unique for each matching type of that concept. Likewise the compiler
needs to instantiate a new proc for each unique *bound* type not each
unique invocation of `ArrayLike`

### generic parameter bindings
Couple of things here. The code in sigmatch has to give it's bindings to
the code in concepts, else the information is lost in that step. The
code that prepares the generic variables bound in concepts was also
changed slightly. Net effect is that it works better.
I did choose to use the `LayedIdTable` instead of the `seq`s in
`concepts.nim`. This was mostly to avoid confusing myself. It also
avoids some unnecessary movings around. I wouldn't doubt this is
slightly less performant, but not much in the grand scheme of things and
I would prefer to keep things as easy to understand as possible for as
long as possible because this stuff can get confusing.

### various fixes in the matching logic
Certain forms of modifiers like `var` and generic types like
`tyGenericInst` and `tyGenericInvocation` have logic adjustments based
on my testing and usage

### signature matching method adjustment
This is the weird one, like my last PR. I thought a lot about the
feedback from my last attempt and this is what I came up with. Perhaps
unfortunately I am preoccupied with a slight grey area. consider the
follwing:
```nim
type
  C1 = concept
    proc p[T](s: Self; x: T)
  C2[T] = concept
    proc p(s: Self; x: T)
```
It would be temping to say that these are the same, but I don't think
they are. `C2` makes each invocation distinct, and this has important
implications in the type system. eg `C2[int]` is not the same type as
`C2[string]` and this means that signatures are meant to accept a type
that only matches `p` for a single type per unique binding. For `C1` all
are the same and the binding `p` accepts multiple types. There are
multiple variations of this type classes, `tyAnything` and the like.

The make things more complicated, an implementation might match:
```nim
type
  A = object
  C3 = concept
    proc p(s: Self; x:  A)
```
if the implementation defines:
```nim
proc p(x: Impl; y: object)
```

while a concept that fits `C2` may be satisfied by something like:
```nim
proc p(x: Impl; y: int)
proc spring[T](x: C2[T])
```
it just depends. None of this is really a problem, it just seems to
provoke some more logic in `concepts.nim` that makes all of this (appear
to?) work. The logic checks for both kinds of matches with a couple of
caveats. The fist is that some unbind-able arrangements may be matched
during overload resolution. I don't think this is avoidable and I
actually think this is a good way to get a failed compilation. So, first
note imo is that failing during binding is preferred to forcing the
programming to write annoying stub procs and putting insane gymnastics
in the compiler. Second thing is: I think this logic is way to accepting
for some parts of overload resolutions. Particularly in `checkGeneric`
when disambiguation is happening. Things get hard to understand for me
here. ~~I made it so the implicit bindings to not count during
disambiguation~~. I still need to test this more, but the thought is
that it would help curb excessive ambiguity errors.

Again, I'm sorry for this being so many changes. It's probably
inconvenient.

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
2025-03-07 18:14:00 +01:00

2526 lines
98 KiB
Nim

#
#
# The Nim Compiler
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# this module does the semantic checking of type declarations
# included from sem.nim
const
errStringOrIdentNodeExpected = "string or ident node expected"
errStringLiteralExpected = "string literal expected"
errIntLiteralExpected = "integer literal expected"
errWrongNumberOfVariables = "wrong number of variables"
errDuplicateAliasInEnumX = "duplicate value in enum '$1'"
errOverflowInEnumX = "The enum '$1' exceeds its maximum value ($2)"
errOrdinalTypeExpected = "ordinal type expected; given: $1"
errSetTooBig = "set is too large; use `std/sets` for ordinal types with more than 2^16 elements"
errBaseTypeMustBeOrdinal = "base type of a set must be an ordinal"
errInheritanceOnlyWithNonFinalObjects = "inheritance only works with non-final objects"
errXExpectsOneTypeParam = "'$1' expects one type parameter"
errArrayExpectsTwoTypeParams = "array expects two type parameters"
errInvalidVisibilityX = "invalid visibility: '$1'"
errXCannotBeAssignedTo = "'$1' cannot be assigned to"
errIteratorNotAllowed = "iterators can only be defined at the module's top level"
errXNeedsReturnType = "$1 needs a return type"
errNoReturnTypeDeclared = "no return type declared"
errTIsNotAConcreteType = "'$1' is not a concrete type"
errTypeExpected = "type expected"
errXOnlyAtModuleScope = "'$1' is only allowed at top level"
errDuplicateCaseLabel = "duplicate case label"
errMacroBodyDependsOnGenericTypes = "the macro body cannot be compiled, " &
"because the parameter '$1' has a generic type"
errIllegalRecursionInTypeX = "illegal recursion in type '$1'"
errNoGenericParamsAllowedForX = "no generic parameters allowed for $1"
errInOutFlagNotExtern = "the '$1' modifier can be used only with imported types"
proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext, son: sink PType): PType =
if prev == nil or prev.kind == tyGenericBody:
result = newTypeS(kind, c, son)
else:
result = prev
result.setSon(son)
if result.kind == tyForward: result.kind = kind
#if kind == tyError: result.flags.incl tfCheckedForDestructor
proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
if prev == nil or prev.kind == tyGenericBody:
result = newTypeS(kind, c)
else:
result = prev
if result.kind == tyForward: result.kind = kind
proc newConstraint(c: PContext, k: TTypeKind): PType =
result = newTypeS(tyBuiltInTypeClass, c)
result.flags.incl tfCheckedForDestructor
result.addSonSkipIntLit(newTypeS(k, c), c.idgen)
proc semEnum(c: PContext, n: PNode, prev: PType): PType =
if n.len == 0: return newConstraint(c, tyEnum)
elif n.len == 1:
# don't create an empty tyEnum; fixes #3052
return errorType(c)
var
counter, x: BiggestInt = 0
e: PSym = nil
base: PType = nil
identToReplace: ptr PNode = nil
counterSet = initPackedSet[BiggestInt]()
counter = 0
base = nil
result = newOrPrevType(tyEnum, prev, c)
result.n = newNodeI(nkEnumTy, n.info)
checkMinSonsLen(n, 1, c.config)
if n[0].kind != nkEmpty:
base = semTypeNode(c, n[0][0], nil)
if base.kind != tyEnum:
localError(c.config, n[0].info, "inheritance only works with an enum")
counter = toInt64(lastOrd(c.config, base)) + 1
rawAddSon(result, base)
let isPure = result.sym != nil and sfPure in result.sym.flags
var symbols: TStrTable = initStrTable()
var hasNull = false
var needsReorder = false
for i in 1..<n.len:
if n[i].kind == nkEmpty: continue
var useAutoCounter = false
case n[i].kind
of nkEnumFieldDef:
if n[i][0].kind == nkPragmaExpr:
e = newSymS(skEnumField, n[i][0][0], c)
identToReplace = addr n[i][0][0]
pragma(c, e, n[i][0][1], enumFieldPragmas)
else:
e = newSymS(skEnumField, n[i][0], c)
identToReplace = addr n[i][0]
var v = semConstExpr(c, n[i][1])
var strVal: PNode = nil
case skipTypes(v.typ, abstractInst-{tyTypeDesc}).kind
of tyTuple:
if v.len == 2:
strVal = v[1] # second tuple part is the string value
if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCstring}:
if not isOrdinalType(v[0].typ, allowEnumWithHoles=true):
localError(c.config, v[0].info, errOrdinalTypeExpected % typeToString(v[0].typ, preferDesc))
x = toInt64(getOrdValue(v[0])) # first tuple part is the ordinal
n[i][1][0] = newIntTypeNode(x, getSysType(c.graph, unknownLineInfo, tyInt))
else:
localError(c.config, strVal.info, errStringLiteralExpected)
else:
localError(c.config, v.info, errWrongNumberOfVariables)
of tyString, tyCstring:
strVal = v
x = counter
useAutoCounter = true
else:
if isOrdinalType(v.typ, allowEnumWithHoles=true):
x = toInt64(getOrdValue(v))
n[i][1] = newIntTypeNode(x, getSysType(c.graph, unknownLineInfo, tyInt))
else:
localError(c.config, v.info, errOrdinalTypeExpected % typeToString(v.typ, preferDesc))
if i != 1:
if x != counter:
needsReorder = true
incl(result.flags, tfEnumHasHoles)
e.ast = strVal # might be nil
counter = x
of nkSym:
e = n[i].sym
useAutoCounter = true
of nkIdent, nkAccQuoted:
e = newSymS(skEnumField, n[i], c)
identToReplace = addr n[i]
useAutoCounter = true
of nkPragmaExpr:
e = newSymS(skEnumField, n[i][0], c)
pragma(c, e, n[i][1], enumFieldPragmas)
identToReplace = addr n[i][0]
useAutoCounter = true
else:
illFormedAst(n[i], c.config)
if useAutoCounter:
while counter in counterSet and counter != high(typeof(counter)):
inc counter
counterSet.incl counter
elif counterSet.containsOrIncl(counter):
localError(c.config, n[i].info, errDuplicateAliasInEnumX % e.name.s)
e.typ = result
e.position = int(counter)
let symNode = newSymNode(e)
if identToReplace != nil and c.config.cmd notin cmdDocLike:
# A hack to produce documentation for enum fields.
identToReplace[] = symNode
if e.position == 0: hasNull = true
if result.sym != nil and sfExported in result.sym.flags:
e.flags.incl {sfUsed, sfExported}
result.n.add symNode
styleCheckDef(c, e)
onDef(e.info, e)
suggestSym(c.graph, e.info, e, c.graph.usageSym)
if sfGenSym notin e.flags:
if not isPure:
addInterfaceOverloadableSymAt(c, c.currentScope, e)
else:
declarePureEnumField(c, e)
if (let conflict = strTableInclReportConflict(symbols, e); conflict != nil):
wrongRedefinition(c, e.info, e.name.s, conflict.info)
if counter == high(typeof(counter)):
if i > 1 and result.n[i-2].sym.position == high(int):
localError(c.config, n[i].info, errOverflowInEnumX % [e.name.s, $high(typeof(counter))])
else:
inc(counter)
if needsReorder:
result.n.sons.sort(
proc (x, y: PNode): int =
result = cmp(x.sym.position, y.sym.position)
)
if isPure and sfExported in result.sym.flags:
addPureEnum(c, LazySym(sym: result.sym))
if tfNotNil in e.typ.flags and not hasNull:
result.flags.incl tfRequiresInit
setToStringProc(c.graph, result, genEnumToStrProc(result, n.info, c.graph, c.idgen))
proc semSet(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tySet, prev, c)
if n.len == 2 and n[1].kind != nkEmpty:
var base = semTypeNode(c, n[1], nil)
addSonSkipIntLit(result, base, c.idgen)
if base.kind in {tyGenericInst, tyAlias, tySink}: base = skipModifier(base)
if base.kind notin {tyGenericParam, tyGenericInvocation}:
if base.kind == tyForward:
c.skipTypes.add n
elif not isOrdinalType(base, allowEnumWithHoles = true):
localError(c.config, n.info, errOrdinalTypeExpected % typeToString(base, preferDesc))
elif lengthOrd(c.config, base) > MaxSetElements:
localError(c.config, n.info, errSetTooBig)
else:
localError(c.config, n.info, errXExpectsOneTypeParam % "set")
addSonSkipIntLit(result, errorType(c), c.idgen)
proc semContainerArg(c: PContext; n: PNode, kindStr: string; result: PType) =
if n.len == 2:
var base = semTypeNode(c, n[1], nil)
if base.kind == tyVoid:
localError(c.config, n.info, errTIsNotAConcreteType % typeToString(base))
addSonSkipIntLit(result, base, c.idgen)
else:
localError(c.config, n.info, errXExpectsOneTypeParam % kindStr)
addSonSkipIntLit(result, errorType(c), c.idgen)
proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string,
prev: PType): PType =
result = newOrPrevType(kind, prev, c)
semContainerArg(c, n, kindStr, result)
proc semVarargs(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tyVarargs, prev, c)
if n.len == 2 or n.len == 3:
var base = semTypeNode(c, n[1], nil)
addSonSkipIntLit(result, base, c.idgen)
if n.len == 3:
result.n = newIdentNode(considerQuotedIdent(c, n[2]), n[2].info)
else:
localError(c.config, n.info, errXExpectsOneTypeParam % "varargs")
addSonSkipIntLit(result, errorType(c), c.idgen)
proc semVarOutType(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType =
if n.len == 1:
result = newOrPrevType(tyVar, prev, c)
result.flags = flags
var base = semTypeNode(c, n[0], nil)
if base.kind == tyTypeDesc and not isSelf(base):
base = base[0]
if base.kind == tyVar:
localError(c.config, n.info, "type 'var var' is not allowed")
base = base[0]
addSonSkipIntLit(result, base, c.idgen)
else:
result = newConstraint(c, tyVar)
proc isRecursiveType(t: PType, cycleDetector: var IntSet): bool =
if t == nil:
return false
if cycleDetector.containsOrIncl(t.id):
return true
case t.kind
of tyAlias, tyGenericInst, tyDistinct:
return isRecursiveType(t.skipModifier, cycleDetector)
else:
return false
proc annotateClosureConv(n: PNode) =
case n.kind
of {nkNone..nkNilLit}:
discard
of nkTupleConstr:
if n.typ.kind == tyProc and n.typ.callConv == ccClosure and
n[0].typ.kind == tyProc and n[0].typ.callConv != ccClosure:
# restores `transf.generateThunk`
n[0] = newTreeIT(nkHiddenSubConv, n[0].info, n.typ,
newNodeI(nkEmpty, n[0].info), n[0])
n.transitionSonsKind(nkClosure)
n.flags.incl nfTransf
else:
for i in 0..<n.len:
annotateClosureConv(n[i])
proc fitDefaultNode(c: PContext, n: var PNode, expectedType: PType) =
inc c.inStaticContext
n = semConstExpr(c, n, expectedType = expectedType)
let oldType = n.typ
n.flags.incl nfSem
if expectedType != nil and oldType != expectedType:
n = fitNodeConsiderViewType(c, expectedType, n, n.info)
changeType(c, n, expectedType, true) # infer types for default fields value
# bug #22926; be cautious that it uses `semConstExpr` to
# evaulate the default fields; it's only natural to use
# `changeType` to infer types for constant values
# that's also the reason why we don't use `semExpr` to check
# the type since two overlapping error messages might be produced
annotateClosureConv(n)
# xxx any troubles related to defaults fields, consult `semConst` for a potential answer
if n.kind != nkNilLit:
typeAllowedCheck(c, n.info, n.typ, skConst, {taProcContextIsNotMacro, taIsDefaultField})
dec c.inStaticContext
proc isRecursiveType*(t: PType): bool =
# handle simple recusive types before typeFinalPass
var cycleDetector = initIntSet()
isRecursiveType(t, cycleDetector)
proc addSonSkipIntLitChecked(c: PContext; father, son: PType; it: PNode, id: IdGenerator) =
let s = son.skipIntLit(id)
father.add(s)
if isRecursiveType(s):
localError(c.config, it.info, "illegal recursion in type '" & typeToString(s) & "'")
else:
propagateToOwner(father, s)
proc semDistinct(c: PContext, n: PNode, prev: PType): PType =
if n.len == 0: return newConstraint(c, tyDistinct)
result = newOrPrevType(tyDistinct, prev, c)
addSonSkipIntLitChecked(c, result, semTypeNode(c, n[0], nil), n[0], c.idgen)
if n.len > 1: result.n = n[1]
proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
assert isRange(n)
checkSonsLen(n, 3, c.config)
result = newOrPrevType(tyRange, prev, c)
result.n = newNodeI(nkRange, n.info)
# always create a 'valid' range type, but overwrite it later
# because 'semExprWithType' can raise an exception. See bug #6895.
addSonSkipIntLit(result, errorType(c), c.idgen)
if (n[1].kind == nkEmpty) or (n[2].kind == nkEmpty):
localError(c.config, n.info, "range is empty")
var range: array[2, PNode]
# XXX this is still a hard compilation in a generic context, this can
# result in unresolved generic parameters being treated like real types
range[0] = semExprWithType(c, n[1], {efDetermineType})
range[1] = semExprWithType(c, n[2], {efDetermineType})
var rangeT: array[2, PType] = default(array[2, PType])
for i in 0..1:
rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit(c.idgen)
let hasUnknownTypes = c.inGenericContext > 0 and
(rangeT[0].kind == tyFromExpr or rangeT[1].kind == tyFromExpr)
if not hasUnknownTypes:
if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})):
typeMismatch(c.config, n.info, rangeT[0], rangeT[1], n)
elif not isOrdinalType(rangeT[0]) and rangeT[0].kind notin {tyFloat..tyFloat128} or
rangeT[0].kind == tyBool:
localError(c.config, n.info, "ordinal or float type expected, but got " & typeToString(rangeT[0]))
elif enumHasHoles(rangeT[0]):
localError(c.config, n.info, "enum '$1' has holes" % typeToString(rangeT[0]))
for i in 0..1:
if hasUnresolvedArgs(c, range[i]):
result.n.add makeStaticExpr(c, range[i])
result.flags.incl tfUnresolved
else:
result.n.add semConstExpr(c, range[i])
if result.n[i].kind in {nkFloatLit..nkFloat64Lit} and result.n[i].floatVal.isNaN:
localError(c.config, n.info, "NaN is not a valid range " & (if i == 0: "start" else: "end"))
if weakLeValue(result.n[0], result.n[1]) == impNo:
localError(c.config, n.info, "range is empty")
result[0] = rangeT[0]
proc semRange(c: PContext, n: PNode, prev: PType): PType =
result = nil
if n.len == 2:
if isRange(n[1]):
result = semRangeAux(c, n[1], prev)
if not isDefined(c.config, "nimPreviewRangeDefault"):
let n = result.n
if n[0].kind in {nkCharLit..nkUInt64Lit} and n[0].intVal > 0:
incl(result.flags, tfRequiresInit)
elif n[1].kind in {nkCharLit..nkUInt64Lit} and n[1].intVal < 0:
incl(result.flags, tfRequiresInit)
elif n[0].kind in {nkFloatLit..nkFloat64Lit} and
n[0].floatVal > 0.0:
incl(result.flags, tfRequiresInit)
elif n[1].kind in {nkFloatLit..nkFloat64Lit} and
n[1].floatVal < 0.0:
incl(result.flags, tfRequiresInit)
else:
if n[1].kind == nkInfix and considerQuotedIdent(c, n[1][0]).s == "..<":
localError(c.config, n[0].info, "range types need to be constructed with '..', '..<' is not supported")
else:
localError(c.config, n[0].info, "expected range")
result = newOrPrevType(tyError, prev, c)
else:
localError(c.config, n.info, errXExpectsOneTypeParam % "range")
result = newOrPrevType(tyError, prev, c)
proc semArrayIndexConst(c: PContext, e: PNode, info: TLineInfo): PType =
let x = semConstExpr(c, e)
if x.kind in {nkIntLit..nkUInt64Lit}:
result = makeRangeType(c, 0, x.intVal-1, info,
x.typ.skipTypes({tyTypeDesc}))
else:
result = x.typ.skipTypes({tyTypeDesc})
proc semArrayIndex(c: PContext, n: PNode): PType =
if isRange(n):
result = semRangeAux(c, n, nil)
elif n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "..<":
result = errorType(c)
else:
# XXX this is still a hard compilation in a generic context, this can
# result in unresolved generic parameters being treated like real types
let e = semExprWithType(c, n, {efDetermineType})
if e.typ.kind == tyFromExpr:
result = makeRangeWithStaticExpr(c, e.typ.n)
elif e.kind in {nkIntLit..nkUInt64Lit}:
if e.intVal < 0:
if e.kind in {nkIntLit..nkInt64Lit}:
localError(c.config, n.info,
"Array length can't be negative, but was " & $e.intVal)
else:
localError(c.config, n.info,
"Array length can't exceed its maximum value (9223372036854775807), but was " & $cast[BiggestUInt](e.intVal))
result = makeRangeType(c, 0, e.intVal-1, n.info, e.typ)
elif e.kind == nkSym and (e.typ.kind == tyStatic or e.typ.kind == tyTypeDesc):
if e.typ.kind == tyStatic:
if e.sym.ast != nil:
return semArrayIndex(c, e.sym.ast)
if e.typ.skipModifier.kind != tyGenericParam and not isOrdinalType(e.typ.skipModifier):
let info = if n.safeLen > 1: n[1].info else: n.info
localError(c.config, info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc))
result = makeRangeWithStaticExpr(c, e)
if c.inGenericContext > 0: result.flags.incl tfUnresolved
else:
result = e.typ.skipTypes({tyTypeDesc})
result.flags.incl tfImplicitStatic
elif e.kind in (nkCallKinds + {nkBracketExpr}) and hasUnresolvedArgs(c, e):
if not isOrdinalType(e.typ.skipTypes({tyStatic, tyAlias, tyGenericInst, tySink})):
localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc))
# This is an int returning call, depending on an
# yet unknown generic param (see tuninstantiatedgenericcalls).
# We are going to construct a range type that will be
# properly filled-out in semtypinst (see how tyStaticExpr
# is handled there).
result = makeRangeWithStaticExpr(c, e)
elif e.kind == nkIdent:
result = e.typ.skipTypes({tyTypeDesc})
else:
result = semArrayIndexConst(c, e, n.info)
#localError(c.config, n[1].info, errConstExprExpected)
proc semArray(c: PContext, n: PNode, prev: PType): PType =
var base: PType
if n.len == 3:
# 3 = length(array indx base)
let indx = semArrayIndex(c, n[1])
var indxB = indx
if indxB.kind in {tyGenericInst, tyAlias, tySink}: indxB = skipModifier(indxB)
if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr} and
tfUnresolved notin indxB.flags:
if not isOrdinalType(indxB):
localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(indxB, preferDesc))
elif enumHasHoles(indxB):
localError(c.config, n[1].info, "enum '$1' has holes" %
typeToString(indxB.skipTypes({tyRange})))
elif indxB.kind != tyRange and
lengthOrd(c.config, indxB) > high(uint16).int:
# assume range type is intentional
localError(c.config, n[1].info,
"index type '$1' for array is too large" % typeToString(indxB))
base = semTypeNode(c, n[2], nil)
# ensure we only construct a tyArray when there was no error (bug #3048):
# bug #6682: Do not propagate initialization requirements etc for the
# index type:
result = newOrPrevType(tyArray, prev, c, indx)
addSonSkipIntLit(result, base, c.idgen)
else:
localError(c.config, n.info, errArrayExpectsTwoTypeParams)
result = newOrPrevType(tyError, prev, c)
proc semIterableType(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tyIterable, prev, c)
if n.len == 2:
let base = semTypeNode(c, n[1], nil)
addSonSkipIntLit(result, base, c.idgen)
else:
localError(c.config, n.info, errXExpectsOneTypeParam % "iterable")
result = newOrPrevType(tyError, prev, c)
proc semOrdinal(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tyOrdinal, prev, c)
if n.len == 2:
var base = semTypeNode(c, n[1], nil)
if base.kind != tyGenericParam:
if not isOrdinalType(base):
localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(base, preferDesc))
addSonSkipIntLit(result, base, c.idgen)
else:
localError(c.config, n.info, errXExpectsOneTypeParam % "ordinal")
result = newOrPrevType(tyError, prev, c)
proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType =
if n.len == 0:
localError(c.config, n.info, errTypeExpected)
result = newOrPrevType(tyTuple, prev, c)
for it in n:
let t = semTypeNode(c, it, nil)
addSonSkipIntLitChecked(c, result, t, it, c.idgen)
proc firstRange(config: ConfigRef, t: PType): PNode =
if t.skipModifier().kind in tyFloat..tyFloat64:
result = newFloatNode(nkFloatLit, firstFloat(t))
else:
result = newIntNode(nkIntLit, firstOrd(config, t))
result.typ() = t
proc semTuple(c: PContext, n: PNode, prev: PType): PType =
var typ: PType
result = newOrPrevType(tyTuple, prev, c)
result.n = newNodeI(nkRecList, n.info)
var check = initIntSet()
var counter = 0
for i in ord(n.kind == nkBracketExpr)..<n.len:
var a = n[i]
if (a.kind != nkIdentDefs): illFormedAst(a, c.config)
checkMinSonsLen(a, 3, c.config)
var hasDefaultField = a[^1].kind != nkEmpty
if hasDefaultField:
typ = if a[^2].kind != nkEmpty: semTypeNode(c, a[^2], nil) else: nil
if c.inGenericContext > 0:
a[^1] = semExprWithType(c, a[^1], {efDetermineType, efAllowSymChoice}, typ)
if typ == nil:
typ = a[^1].typ
else:
fitDefaultNode(c, a[^1], typ)
typ = a[^1].typ
elif a[^2].kind != nkEmpty:
typ = semTypeNode(c, a[^2], nil)
if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
a[^1] = firstRange(c.config, typ)
hasDefaultField = true
else:
localError(c.config, a.info, errTypeExpected)
typ = errorType(c)
for j in 0..<a.len - 2:
var field = newSymG(skField, a[j], c)
field.typ = typ
field.position = counter
inc(counter)
if containsOrIncl(check, field.name.id):
localError(c.config, a[j].info, "attempt to redefine: '" & field.name.s & "'")
else:
let fSym = newSymNode(field)
if hasDefaultField:
fSym.sym.ast = a[^1]
fSym.sym.ast.flags.incl nfSkipFieldChecking
result.n.add fSym
addSonSkipIntLit(result, typ, c.idgen)
styleCheckDef(c, a[j].info, field)
onDef(field.info, field)
if result.n.len == 0: result.n = nil
if isTupleRecursive(result):
localError(c.config, n.info, errIllegalRecursionInTypeX % typeToString(result))
proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags): PSym =
# identifier with visibility
if n.kind == nkPostfix:
if n.len == 2:
# for gensym'ed identifiers the identifier may already have been
# transformed to a symbol and we need to use that here:
result = newSymG(kind, n[1], c)
var v = considerQuotedIdent(c, n[0])
if sfExported in allowed and v.id == ord(wStar):
incl(result.flags, sfExported)
else:
if not (sfExported in allowed):
localError(c.config, n[0].info, errXOnlyAtModuleScope % "export")
else:
localError(c.config, n[0].info, errInvalidVisibilityX % renderTree(n[0]))
else:
result = nil
illFormedAst(n, c.config)
else:
result = newSymG(kind, n, c)
proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags, fromTopLevel = false): PSym =
if n.kind == nkPragmaExpr:
checkSonsLen(n, 2, c.config)
result = semIdentVis(c, kind, n[0], allowed)
case kind
of skType:
# process pragmas later, because result.typ has not been set yet
discard
of skField: pragma(c, result, n[1], fieldPragmas)
of skVar: pragma(c, result, n[1], varPragmas)
of skLet: pragma(c, result, n[1], letPragmas)
of skConst: pragma(c, result, n[1], constPragmas)
else: discard
else:
result = semIdentVis(c, kind, n, allowed)
let invalidPragmasForPush = if fromTopLevel and sfWasGenSym notin result.flags:
{}
else:
{wExportc, wExportCpp, wDynlib}
case kind
of skField: implicitPragmas(c, result, n.info, fieldPragmas)
of skVar: implicitPragmas(c, result, n.info, varPragmas-invalidPragmasForPush)
of skLet: implicitPragmas(c, result, n.info, letPragmas-invalidPragmasForPush)
of skConst: implicitPragmas(c, result, n.info, constPragmas-invalidPragmasForPush)
else: discard
proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) =
let ex = t[branchIndex][currentEx].skipConv
for i in 1..branchIndex:
for j in 0..<t[i].len - 1:
if i == branchIndex and j == currentEx: break
if overlap(t[i][j].skipConv, ex):
localError(c.config, ex.info, errDuplicateCaseLabel)
proc semBranchRange(c: PContext, n, a, b: PNode, covered: var Int128): PNode =
checkMinSonsLen(n, 1, c.config)
let ac = semConstExpr(c, a)
let bc = semConstExpr(c, b)
if ac.kind in {nkStrLit..nkTripleStrLit} or bc.kind in {nkStrLit..nkTripleStrLit}:
localError(c.config, b.info, "range of string is invalid")
var at = fitNode(c, n[0].typ, ac, ac.info).skipConvTakeType
var bt = fitNode(c, n[0].typ, bc, bc.info).skipConvTakeType
# the calls to fitNode may introduce calls to converters
# mirrored with semCaseBranch for single elements
if at.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
at = semConstExpr(c, at)
if bt.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
bt = semConstExpr(c, bt)
result = newNodeI(nkRange, a.info)
result.add(at)
result.add(bt)
if emptyRange(ac, bc): localError(c.config, b.info, "range is empty")
else: covered = covered + getOrdValue(bc) + 1 - getOrdValue(ac)
proc semCaseBranchRange(c: PContext, t, b: PNode,
covered: var Int128): PNode =
checkSonsLen(b, 3, c.config)
result = semBranchRange(c, t, b[1], b[2], covered)
proc semCaseBranchSetElem(c: PContext, n, b: PNode,
covered: var Int128): PNode =
if isRange(b):
checkSonsLen(b, 3, c.config)
result = semBranchRange(c, n, b[1], b[2], covered)
elif b.kind == nkRange:
checkSonsLen(b, 2, c.config)
result = semBranchRange(c, n, b[0], b[1], covered)
else:
result = fitNode(c, n[0].typ, b, b.info)
inc(covered)
proc semCaseBranch(c: PContext, n, branch: PNode, branchIndex: int,
covered: var Int128) =
let lastIndex = branch.len - 2
for i in 0..lastIndex:
var b = branch[i]
if b.kind == nkRange:
branch[i] = b
# same check as in semBranchRange for exhaustiveness
covered = covered + getOrdValue(b[1]) + 1 - getOrdValue(b[0])
elif isRange(b):
branch[i] = semCaseBranchRange(c, n, b, covered)
else:
# constant sets and arrays are allowed:
# set expected type to selector type for type inference
# even if it can be a different type like a set or array
var r = semConstExpr(c, b, expectedType = n[0].typ)
if r.kind in {nkCurly, nkBracket} and r.len == 0 and branch.len == 2:
# discarding ``{}`` and ``[]`` branches silently
delSon(branch, 0)
return
elif r.kind notin {nkCurly, nkBracket} or r.len == 0:
checkMinSonsLen(n, 1, c.config)
var tmp = fitNode(c, n[0].typ, r, r.info)
# the call to fitNode may introduce a call to a converter
# mirrored with semBranchRange
if tmp.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
tmp = semConstExpr(c, tmp)
branch[i] = skipConv(tmp)
inc(covered)
else:
if r.kind == nkCurly:
r = deduplicate(c.config, r)
# first element is special and will overwrite: branch[i]:
branch[i] = semCaseBranchSetElem(c, n, r[0], covered)
# other elements have to be added to ``branch``
for j in 1..<r.len:
branch.add(semCaseBranchSetElem(c, n, r[j], covered))
# caution! last son of branch must be the actions to execute:
swap(branch[^2], branch[^1])
checkForOverlap(c, n, i, branchIndex)
# Elements added above needs to be checked for overlaps.
for i in lastIndex.succ..<branch.len - 1:
checkForOverlap(c, n, i, branchIndex)
proc toCover(c: PContext, t: PType): Int128 =
let t2 = skipTypes(t, abstractVarRange-{tyTypeDesc})
if t2.kind == tyEnum and enumHasHoles(t2):
result = toInt128(t2.n.len)
else:
# <----
let t = skipTypes(t, abstractVar-{tyTypeDesc})
# XXX: hack incoming. lengthOrd is incorrect for 64bit integer
# types because it doesn't uset Int128 yet. This entire branching
# should be removed as soon as lengthOrd uses int128.
if t.kind in {tyInt64, tyUInt64}:
result = toInt128(1) shl 64
elif t.kind in {tyInt, tyUInt}:
result = toInt128(1) shl (c.config.target.intSize * 8)
else:
result = lengthOrd(c.config, t)
proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
father: PNode, rectype: PType, hasCaseFields = false)
proc getIntSetOfType(c: PContext, t: PType): IntSet =
result = initIntSet()
if t.enumHasHoles:
let t = t.skipTypes(abstractRange)
for field in t.n.sons:
result.incl(field.sym.position)
else:
assert(lengthOrd(c.config, t) <= BiggestInt(MaxSetElements))
for i in toInt64(firstOrd(c.config, t))..toInt64(lastOrd(c.config, t)):
result.incl(i.int)
iterator processBranchVals(b: PNode): int =
assert b.kind in {nkOfBranch, nkElifBranch, nkElse}
if b.kind == nkOfBranch:
for i in 0..<b.len-1:
if b[i].kind in {nkIntLit, nkCharLit}:
yield b[i].intVal.int
elif b[i].kind == nkRange:
for i in b[i][0].intVal..b[i][1].intVal:
yield i.int
proc renderAsType(vals: IntSet, t: PType): string =
result = "{"
let t = t.skipTypes(abstractRange)
var enumSymOffset = 0
var i = 0
for val in vals:
if result.len > 1:
result &= ", "
case t.kind:
of tyEnum, tyBool:
while t.n[enumSymOffset].sym.position < val: inc(enumSymOffset)
result &= t.n[enumSymOffset].sym.name.s
of tyChar:
result.addQuoted(char(val))
else:
if i == 64:
result &= "omitted $1 values..." % $(vals.len - i)
break
else:
result &= $val
inc(i)
result &= "}"
proc formatMissingEnums(c: PContext, n: PNode): string =
var coveredCases = initIntSet()
for i in 1..<n.len:
for val in processBranchVals(n[i]):
coveredCases.incl val
result = (c.getIntSetOfType(n[0].typ) - coveredCases).renderAsType(n[0].typ)
proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
father: PNode, rectype: PType) =
var a = copyNode(n)
checkMinSonsLen(n, 2, c.config)
semRecordNodeAux(c, n[0], check, pos, a, rectype, hasCaseFields = true)
if a[0].kind != nkSym:
internalError(c.config, "semRecordCase: discriminant is no symbol")
return
incl(a[0].sym.flags, sfDiscriminant)
var covered = toInt128(0)
var chckCovered = false
var typ = skipTypes(a[0].typ, abstractVar-{tyTypeDesc})
const shouldChckCovered = {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool}
case typ.kind
of shouldChckCovered:
chckCovered = true
of tyFloat..tyFloat128, tyError:
discard
of tyRange:
if skipTypes(typ.elementType, abstractInst).kind in shouldChckCovered:
chckCovered = true
of tyForward:
errorUndeclaredIdentifier(c, n[0].info, typ.sym.name.s)
elif not isOrdinalType(typ):
localError(c.config, n[0].info, "selector must be of an ordinal type, float")
if firstOrd(c.config, typ) != 0:
localError(c.config, n.info, "low(" & $a[0].sym.name.s &
") must be 0 for discriminant")
elif lengthOrd(c.config, typ) > 0x00007FFF:
localError(c.config, n.info, "len($1) must be less than 32768" % a[0].sym.name.s)
for i in 1..<n.len:
var b = copyTree(n[i])
a.add b
case n[i].kind
of nkOfBranch:
checkMinSonsLen(b, 2, c.config)
semCaseBranch(c, a, b, i, covered)
of nkElse:
checkSonsLen(b, 1, c.config)
if chckCovered and covered == toCover(c, a[0].typ):
message(c.config, b.info, warnUnreachableElse)
chckCovered = false
else: illFormedAst(n, c.config)
delSon(b, b.len - 1)
semRecordNodeAux(c, lastSon(n[i]), check, pos, b, rectype, hasCaseFields = true)
if chckCovered and covered != toCover(c, a[0].typ):
if a[0].typ.skipTypes(abstractRange).kind == tyEnum:
localError(c.config, a.info, "not all cases are covered; missing: $1" %
formatMissingEnums(c, a))
else:
localError(c.config, a.info, "not all cases are covered")
father.add a
proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
father: PNode, rectype: PType, hasCaseFields: bool) =
if n == nil: return
case n.kind
of nkRecWhen:
var a = copyTree(n)
var branch: PNode = nil # the branch to take
var cannotResolve = false # no branch should be taken
for i in 0..<a.len:
var it = a[i]
if it == nil: illFormedAst(n, c.config)
var idx = 1
case it.kind
of nkElifBranch:
checkSonsLen(it, 2, c.config)
if c.inGenericContext == 0:
var e = semConstBoolExpr(c, it[0])
if e.kind != nkIntLit: discard "don't report followup error"
elif e.intVal != 0 and branch == nil: branch = it[1]
else:
# XXX this is still a hard compilation in a generic context, this can
# result in unresolved generic parameters being treated like real types
let e = semExprWithType(c, it[0], {efDetermineType})
if e.typ.kind == tyFromExpr:
it[0] = makeStaticExpr(c, e)
cannotResolve = true
else:
it[0] = forceBool(c, e)
let val = getConstExpr(c.module, it[0], c.idgen, c.graph)
if val == nil or val.kind != nkIntLit:
cannotResolve = true
elif not cannotResolve and val.intVal != 0 and branch == nil:
branch = it[1]
of nkElse:
checkSonsLen(it, 1, c.config)
if branch == nil and not cannotResolve: branch = it[0]
idx = 0
else: illFormedAst(n, c.config)
if c.inGenericContext > 0 and cannotResolve:
# use a new check intset here for each branch:
var newCheck: IntSet = check
var newPos = pos
var newf = newNodeI(nkRecList, n.info)
semRecordNodeAux(c, it[idx], newCheck, newPos, newf, rectype, hasCaseFields)
it[idx] = if newf.len == 1: newf[0] else: newf
if branch != nil:
semRecordNodeAux(c, branch, check, pos, father, rectype, hasCaseFields)
elif cannotResolve:
father.add a
elif father.kind in {nkElse, nkOfBranch}:
father.add newNodeI(nkRecList, n.info)
of nkRecCase:
semRecordCase(c, n, check, pos, father, rectype)
of nkNilLit:
if father.kind != nkRecList: father.add newNodeI(nkRecList, n.info)
of nkRecList:
# attempt to keep the nesting at a sane level:
var a = if father.kind == nkRecList: father else: copyNode(n)
for i in 0..<n.len:
semRecordNodeAux(c, n[i], check, pos, a, rectype, hasCaseFields)
if a != father: father.add a
of nkIdentDefs:
checkMinSonsLen(n, 3, c.config)
var a: PNode
if father.kind != nkRecList and n.len >= 4: a = newNodeI(nkRecList, n.info)
else: a = newNodeI(nkEmpty, n.info)
var typ: PType
var hasDefaultField = n[^1].kind != nkEmpty
if hasDefaultField:
typ = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
if c.inGenericContext > 0:
n[^1] = semExprWithType(c, n[^1], {efDetermineType, efAllowSymChoice}, typ)
if typ == nil:
typ = n[^1].typ
else:
fitDefaultNode(c, n[^1], typ)
typ = n[^1].typ
propagateToOwner(rectype, typ)
elif n[^2].kind == nkEmpty:
localError(c.config, n.info, errTypeExpected)
typ = errorType(c)
else:
typ = semTypeNode(c, n[^2], nil)
if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
n[^1] = firstRange(c.config, typ)
hasDefaultField = true
propagateToOwner(rectype, typ)
var fieldOwner = if c.inGenericContext > 0: c.getCurrOwner
else: rectype.sym
for i in 0..<n.len-2:
var f = semIdentWithPragma(c, skField, n[i], {sfExported})
let info = if n[i].kind == nkPostfix:
n[i][1].info
else:
n[i].info
suggestSym(c.graph, info, f, c.graph.usageSym)
f.typ = typ
f.position = pos
f.options = c.config.options
if fieldOwner != nil and
{sfImportc, sfExportc} * fieldOwner.flags != {} and
not hasCaseFields and f.loc.snippet == "":
f.loc.snippet = rope(f.name.s)
f.flags.incl {sfImportc, sfExportc} * fieldOwner.flags
inc(pos)
if containsOrIncl(check, f.name.id):
localError(c.config, info, "attempt to redefine: '" & f.name.s & "'")
let fSym = newSymNode(f)
if hasDefaultField:
fSym.sym.ast = n[^1]
fSym.sym.ast.flags.incl nfSkipFieldChecking
if a.kind == nkEmpty: father.add fSym
else: a.add fSym
styleCheckDef(c, f)
onDef(f.info, f)
if a.kind != nkEmpty: father.add a
of nkSym:
# This branch only valid during generic object
# inherited from generic/partial specialized parent second check.
# There is no branch validity check here
if containsOrIncl(check, n.sym.name.id):
localError(c.config, n.info, "attempt to redefine: '" & n.sym.name.s & "'")
father.add n
of nkEmpty:
if father.kind in {nkElse, nkOfBranch}:
father.add n
else: illFormedAst(n, c.config)
proc addInheritedFieldsAux(c: PContext, check: var IntSet, pos: var int,
n: PNode) =
case n.kind
of nkRecCase:
if (n[0].kind != nkSym): internalError(c.config, n.info, "addInheritedFieldsAux")
addInheritedFieldsAux(c, check, pos, n[0])
for i in 1..<n.len:
case n[i].kind
of nkOfBranch, nkElse:
addInheritedFieldsAux(c, check, pos, lastSon(n[i]))
else: internalError(c.config, n.info, "addInheritedFieldsAux(record case branch)")
of nkRecList, nkRecWhen, nkElifBranch, nkElse:
for i in int(n.kind == nkElifBranch)..<n.len:
addInheritedFieldsAux(c, check, pos, n[i])
of nkSym:
incl(check, n.sym.name.id)
inc(pos)
else: internalError(c.config, n.info, "addInheritedFieldsAux()")
proc skipGenericInvocation(t: PType): PType {.inline.} =
result = t
if result.kind == tyGenericInvocation:
result = result[0]
while result.kind in {tyGenericInst, tyGenericBody, tyRef, tyPtr, tyAlias, tySink, tyOwned}:
result = skipModifier(result)
proc tryAddInheritedFields(c: PContext, check: var IntSet, pos: var int,
obj: PType, n: PNode, isPartial = false, innerObj: PType = nil): bool =
if ((not isPartial) and (obj.kind notin {tyObject, tyGenericParam} or tfFinal in obj.flags)) or
(innerObj != nil and obj.sym.id == innerObj.sym.id):
localError(c.config, n.info, "Cannot inherit from: '" & $obj & "'")
result = false
elif obj.kind == tyObject:
result = true
if (obj.len > 0) and (obj[0] != nil):
result = result and tryAddInheritedFields(c, check, pos, obj[0].skipGenericInvocation, n, false, obj)
addInheritedFieldsAux(c, check, pos, obj.n)
else:
result = true
proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType =
result = nil
if n.len == 0:
return newConstraint(c, tyObject)
var check = initIntSet()
var pos = 0
var base, realBase: PType = nil
# n[0] contains the pragmas (if any). We process these later...
checkSonsLen(n, 3, c.config)
if n[1].kind != nkEmpty:
realBase = semTypeNode(c, n[1][0], nil)
base = skipTypesOrNil(realBase, skipPtrs)
if base.isNil:
localError(c.config, n.info, "cannot inherit from a type that is not an object type")
else:
var concreteBase = skipGenericInvocation(base)
if concreteBase.kind in {tyObject, tyGenericParam,
tyGenericInvocation} and tfFinal notin concreteBase.flags:
# we only check fields duplication of object inherited from
# concrete object. If inheriting from generic object or partial
# specialized object, there will be second check after instantiation
# located in semGeneric.
if concreteBase.kind == tyObject:
if concreteBase.sym != nil and concreteBase.sym.magic == mException and
sfSystemModule notin c.module.flags:
message(c.config, n.info, warnInheritFromException, "")
if not tryAddInheritedFields(c, check, pos, concreteBase, n):
return newType(tyError, c.idgen, result.owner)
elif concreteBase.kind == tyForward:
c.skipTypes.add n #we retry in the final pass
else:
if concreteBase.kind != tyError:
localError(c.config, n[1].info, "inheritance only works with non-final objects; " &
"for " & typeToString(realBase) & " to be inheritable it must be " &
"'object of RootObj' instead of 'object'")
base = nil
realBase = nil
if n.kind != nkObjectTy: internalError(c.config, n.info, "semObjectNode")
result = newOrPrevType(tyObject, prev, c)
rawAddSon(result, realBase)
if realBase == nil and tfInheritable in flags:
result.flags.incl tfInheritable
if tfAcyclic in flags: result.flags.incl tfAcyclic
if result.n.isNil:
result.n = newNodeI(nkRecList, n.info)
else:
# partial object so add things to the check
if not tryAddInheritedFields(c, check, pos, result, n, isPartial = true):
return newType(tyError, c.idgen, result.owner)
semRecordNodeAux(c, n[2], check, pos, result.n, result)
if n[0].kind != nkEmpty:
# dummy symbol for `pragma`:
var s = newSymS(skType, newIdentNode(getIdent(c.cache, "dummy"), n.info), c)
s.typ = result
pragma(c, s, n[0], typePragmas)
if base == nil and tfInheritable notin result.flags:
incl(result.flags, tfFinal)
if c.inGenericContext == 0 and computeRequiresInit(c, result):
result.flags.incl tfRequiresInit
proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
if n.len < 1:
result = newConstraint(c, kind)
else:
let isCall = int ord(n.kind in nkCallKinds+{nkBracketExpr})
let n = if n[0].kind == nkBracket: n[0] else: n
checkMinSonsLen(n, 1, c.config)
let body = n.lastSon
var t = if prev != nil and prev.kind != tyGenericBody and body.kind == nkObjectTy:
semObjectNode(c, body, nil, prev.flags)
else:
semTypeNode(c, body, nil)
if t.kind == tyTypeDesc and tfUnresolved notin t.flags:
t = t.base
if t.kind == tyVoid:
localError(c.config, n.info, "type '$1 void' is not allowed" % kind.toHumanStr)
result = newOrPrevType(kind, prev, c)
var isNilable = false
var wrapperKind = tyNone
# check every except the last is an object:
for i in isCall..<n.len-1:
let ni = n[i]
# echo "semAnyRef ", "n: ", n, "i: ", i, "ni: ", ni
if ni.kind == nkNilLit:
isNilable = true
else:
let region = semTypeNode(c, ni, nil)
if region.kind in {tyOwned, tySink}:
wrapperKind = region.kind
elif region.skipTypes({tyGenericInst, tyAlias, tySink}).kind notin {
tyError, tyObject}:
message c.config, n[i].info, errGenerated, "region needs to be an object type"
addSonSkipIntLit(result, region, c.idgen)
else:
message(c.config, n.info, warnDeprecated, "region for pointer types is deprecated")
addSonSkipIntLit(result, region, c.idgen)
addSonSkipIntLit(result, t, c.idgen)
if tfPartial in result.flags:
if result.elementType.kind == tyObject: incl(result.elementType.flags, tfPartial)
# if not isNilable: result.flags.incl tfNotNil
case wrapperKind
of tyOwned:
if optOwnedRefs in c.config.globalOptions:
let t = newTypeS(tyOwned, c, result)
t.flags.incl tfHasOwned
result = t
of tySink:
let t = newTypeS(tySink, c, result)
result = t
else: discard
if result.kind == tyRef and c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
result.flags.incl tfHasAsgn
proc findEnforcedStaticType(t: PType): PType =
# This handles types such as `static[T] and Foo`,
# which are subset of `static[T]`, hence they could
# be treated in the same way
result = nil
if t == nil: return nil
if t.kind == tyStatic: return t
if t.kind == tyAnd:
for s in t.kids:
let t = findEnforcedStaticType(s)
if t != nil: return t
proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
if kind == skMacro:
let staticType = findEnforcedStaticType(param.typ)
if staticType != nil:
var a = copySym(param, c.idgen)
a.typ = staticType.base
addDecl(c, a)
#elif param.typ != nil and param.typ.kind == tyTypeDesc:
# addDecl(c, param)
else:
# within a macro, every param has the type NimNode!
let nn = getSysSym(c.graph, param.info, "NimNode")
var a = copySym(param, c.idgen)
a.typ = nn.typ
addDecl(c, a)
else:
if sfGenSym in param.flags:
# bug #XXX, fix the gensym'ed parameters owner:
if param.owner == nil:
setOwner(param, getCurrOwner(c))
else: addDecl(c, param)
template shouldHaveMeta(t) =
internalAssert c.config, tfHasMeta in t.flags
# result.lastSon.flags.incl tfHasMeta
proc addImplicitGeneric(c: PContext; typeClass: PType, typId: PIdent;
info: TLineInfo; genericParams: PNode;
paramName: string): PType =
if genericParams == nil:
# This happens with anonymous proc types appearing in signatures
# XXX: we need to lift these earlier
return
let finalTypId = if typId != nil: typId
else: getIdent(c.cache, paramName & ":type")
# is this a bindOnce type class already present in the param list?
for i in 0..<genericParams.len:
if genericParams[i].sym.name.id == finalTypId.id:
return genericParams[i].typ
let owner = if typeClass.sym != nil: typeClass.sym
else: getCurrOwner(c)
var s = newSym(skType, finalTypId, c.idgen, owner, info)
if sfExplain in owner.flags: s.flags.incl sfExplain
if typId == nil: s.flags.incl(sfAnon)
s.linkTo(typeClass)
typeClass.flags.incl tfImplicitTypeParam
s.position = genericParams.len
genericParams.add newSymNode(s)
result = typeClass
addDecl(c, s)
proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
paramType: PType, paramName: string,
info: TLineInfo, anon = false): PType =
if paramType == nil: return # (e.g. proc return type)
template recurse(typ: PType, anonFlag = false): untyped =
liftParamType(c, procKind, genericParams, typ, paramName, info, anonFlag)
var paramTypId = if not anon and paramType.sym != nil: paramType.sym.name
else: nil
case paramType.kind
of tyAnything:
result = addImplicitGeneric(c, newTypeS(tyGenericParam, c), nil, info, genericParams, paramName)
of tyStatic:
if paramType.base.kind != tyNone and paramType.n != nil:
# this is a concrete static value
return
if tfUnresolved in paramType.flags: return # already lifted
let lifted = recurse(paramType.base)
let base = (if lifted != nil: lifted else: paramType.base)
if base.isMetaType and procKind == skMacro:
localError(c.config, info, errMacroBodyDependsOnGenericTypes % paramName)
result = addImplicitGeneric(c, newTypeS(tyStatic, c, base),
paramTypId, info, genericParams, paramName)
if result != nil: result.flags.incl({tfHasStatic, tfUnresolved})
of tyTypeDesc:
if tfUnresolved notin paramType.flags:
# naked typedescs are not bindOnce types
if paramType.base.kind == tyNone and paramTypId != nil and
(paramTypId.id == getIdent(c.cache, "typedesc").id or
paramTypId.id == getIdent(c.cache, "type").id):
# XXX Why doesn't this check for tyTypeDesc instead?
paramTypId = nil
let t = newTypeS(tyTypeDesc, c, paramType.base)
incl t.flags, tfCheckedForDestructor
result = addImplicitGeneric(c, t, paramTypId, info, genericParams, paramName)
else:
result = nil
of tyDistinct:
if paramType.len == 1:
# disable the bindOnce behavior for the type class
result = recurse(paramType.base, true)
else:
result = nil
of tyTuple:
result = nil
for i in 0..<paramType.len:
let t = recurse(paramType[i])
if t != nil:
paramType[i] = t
result = paramType
of tyAlias, tyOwned:
result = recurse(paramType.base)
of tySequence, tySet, tyArray, tyOpenArray,
tyVar, tyLent, tyPtr, tyRef, tyProc, tySink:
# XXX: this is a bit strange, but proc(s: seq)
# produces tySequence(tyGenericParam, tyNone).
# This also seems to be true when creating aliases
# like: type myseq = distinct seq.
# Maybe there is another better place to associate
# the seq type class with the seq identifier.
if paramType.kind == tySequence and paramType.elementType.kind == tyNone:
let typ = newTypeS(tyBuiltInTypeClass, c,
newTypeS(paramType.kind, c))
result = addImplicitGeneric(c, typ, paramTypId, info, genericParams, paramName)
else:
result = nil
for i in 0..<paramType.len:
if paramType[i] == paramType:
globalError(c.config, info, errIllegalRecursionInTypeX % typeToString(paramType))
var lifted = recurse(paramType[i])
if lifted != nil:
paramType[i] = lifted
result = paramType
of tyGenericBody:
result = newTypeS(tyGenericInvocation, c)
result.rawAddSon(paramType)
for i in 0..<paramType.len - 1:
if paramType[i].kind == tyStatic:
var staticCopy = paramType[i].exactReplica
staticCopy.flags.incl tfInferrableStatic
result.rawAddSon staticCopy
else:
result.rawAddSon newTypeS(tyAnything, c)
if paramType.typeBodyImpl.kind == tyUserTypeClass:
result.kind = tyUserTypeClassInst
result.rawAddSon paramType.typeBodyImpl
return addImplicitGeneric(c, result, paramTypId, info, genericParams, paramName)
let x = instGenericContainer(c, paramType.sym.info, result,
allowMetaTypes = true)
result = newTypeS(tyCompositeTypeClass, c)
result.rawAddSon paramType
result.rawAddSon x
result = addImplicitGeneric(c, result, paramTypId, info, genericParams, paramName)
of tyGenericInst:
result = nil
if paramType.skipModifier.kind == tyUserTypeClass:
var cp = copyType(paramType, c.idgen, getCurrOwner(c))
copyTypeProps(c.graph, c.idgen.module, cp, paramType)
cp.kind = tyUserTypeClassInst
return addImplicitGeneric(c, cp, paramTypId, info, genericParams, paramName)
for i in 1..<paramType.len-1:
var lifted = recurse(paramType[i])
if lifted != nil:
paramType[i] = lifted
result = paramType
result.last.shouldHaveMeta
if paramType.isConcept:
return addImplicitGeneric(c, paramType, paramTypId, info, genericParams, paramName)
else:
let liftBody = recurse(paramType.skipModifier, true)
if liftBody != nil:
result = liftBody
result.flags.incl tfHasMeta
#result.shouldHaveMeta
of tyGenericInvocation:
result = nil
for i in 1..<paramType.len:
#if paramType[i].kind != tyTypeDesc:
let lifted = recurse(paramType[i])
if lifted != nil: paramType[i] = lifted
let body = paramType.base
if body.kind in {tyForward, tyError}:
# this may happen for proc type appearing in a type section
# before one of its param types
return
if body.last.kind == tyUserTypeClass:
let expanded = instGenericContainer(c, info, paramType,
allowMetaTypes = true)
result = recurse(expanded, true)
of tyUserTypeClasses, tyBuiltInTypeClass, tyCompositeTypeClass,
tyAnd, tyOr, tyNot, tyConcept:
result = addImplicitGeneric(c,
copyType(paramType, c.idgen, getCurrOwner(c)), paramTypId,
info, genericParams, paramName)
of tyGenericParam:
result = nil
markUsed(c, paramType.sym.info, paramType.sym)
onUse(paramType.sym.info, paramType.sym)
if tfWildcard in paramType.flags:
paramType.flags.excl tfWildcard
paramType.sym.transitionGenericParamToType()
else: result = nil
proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType =
## Semchecks the type of parameters.
if n.kind == nkCurlyExpr:
result = semTypeNode(c, n[0], nil)
constraint = semNodeKindConstraints(n, c.config, 1)
elif n.kind == nkCall and
n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice, nkOpenSym} and
considerQuotedIdent(c, n[0]).s == "{}":
result = semTypeNode(c, n[1], nil)
constraint = semNodeKindConstraints(n, c.config, 2)
else:
result = semTypeNode(c, n, nil)
proc newProcType(c: PContext; info: TLineInfo; prev: PType = nil): PType =
result = newOrPrevType(tyProc, prev, c)
result.callConv = lastOptionEntry(c).defaultCC
result.n = newNodeI(nkFormalParams, info)
rawAddSon(result, nil) # return type
# result.n[0] used to be `nkType`, but now it's `nkEffectList` because
# the effects are now stored in there too ... this is a bit hacky, but as
# usual we desperately try to save memory:
result.n.add newNodeI(nkEffectList, info)
proc isMagic(sym: PSym): bool =
if sym.ast == nil: return false
let nPragmas = sym.ast[pragmasPos]
return hasPragma(nPragmas, wMagic)
proc semProcTypeNode(c: PContext, n, genericParams: PNode,
prev: PType, kind: TSymKind; isType=false): PType =
# for historical reasons (code grows) this is invoked for parameter
# lists too and then 'isType' is false.
checkMinSonsLen(n, 1, c.config)
result = newProcType(c, n.info, prev)
var check = initIntSet()
var counter = 0
template isCurrentlyGeneric: bool =
# genericParams might update as implicit generic params are added
genericParams != nil and genericParams.len > 0
for i in 1..<n.len:
var a = n[i]
if a.kind != nkIdentDefs:
# for some generic instantiations the passed ':env' parameter
# for closures has already been produced (see bug #898). We simply
# skip this parameter here. It'll then be re-generated in another LL
# pass over this instantiation:
if a.kind == nkSym and sfFromGeneric in a.sym.flags: continue
illFormedAst(a, c.config)
checkMinSonsLen(a, 3, c.config)
var
typ: PType = nil
def: PNode = nil
constraint: PNode = nil
hasType = a[^2].kind != nkEmpty
hasDefault = a[^1].kind != nkEmpty
if hasType:
let isGeneric = isCurrentlyGeneric()
inc c.inGenericContext, ord(isGeneric)
typ = semParamType(c, a[^2], constraint)
dec c.inGenericContext, ord(isGeneric)
# TODO: Disallow typed/untyped in procs in the compiler/stdlib
if kind in {skProc, skFunc} and (typ.kind == tyTyped or typ.kind == tyUntyped):
if not isMagic(getCurrOwner(c)):
localError(c.config, a[^2].info, "'" & typ.sym.name.s & "' is only allowed in templates and macros or magic procs")
if hasDefault:
def = a[^1]
if a.len > 3:
var msg = ""
for j in 0 ..< a.len - 2:
if msg.len != 0: msg.add(", ")
msg.add($a[j])
msg.add(" all have default value '")
msg.add(def.renderTree)
msg.add("', this may be unintentional, " &
"either use ';' (semicolon) or explicitly write each default value")
message(c.config, a.info, warnImplicitDefaultValue, msg)
block determineType:
var canBeVoid = false
if kind == skTemplate:
if typ != nil and typ.kind == tyUntyped:
# don't do any typechecking or assign a type for
# `untyped` parameter default value
break determineType
elif hasUnresolvedArgs(c, def):
# template default value depends on other parameter
# don't do any typechecking
def.typ() = makeTypeFromExpr(c, def.copyTree)
break determineType
elif typ != nil and typ.kind == tyTyped:
canBeVoid = true
let isGeneric = isCurrentlyGeneric()
inc c.inGenericContext, ord(isGeneric)
if canBeVoid:
def = semExpr(c, def, {efDetermineType, efAllowSymChoice}, typ)
else:
def = semExprWithType(c, def, {efDetermineType, efAllowSymChoice}, typ)
dec c.inGenericContext, ord(isGeneric)
if def.referencesAnotherParam(getCurrOwner(c)):
def.flags.incl nfDefaultRefsParam
if typ == nil:
typ = def.typ
if isEmptyContainer(typ):
localError(c.config, a.info, "cannot infer the type of parameter '" & $a[0] & "'")
if typ.kind == tyTypeDesc:
# consider a proc such as:
# proc takesType(T = int)
# a naive analysis may conclude that the proc type is type[int]
# which will prevent other types from matching - clearly a very
# surprising behavior. We must instead fix the expected type of
# the proc to be the unbound typedesc type:
typ = newTypeS(tyTypeDesc, c, newTypeS(tyNone, c))
typ.flags.incl tfCheckedForDestructor
elif def.typ != nil and def.typ.kind != tyFromExpr: # def.typ can be void
# if def.typ != nil and def.typ.kind != tyNone:
# example code that triggers it:
# proc sort[T](cmp: proc(a, b: T): int = cmp)
if not containsGenericType(typ):
# check type compatibility between def.typ and typ:
def = fitNode(c, typ, def, def.info)
elif typ.kind == tyStatic:
def = semConstExpr(c, def)
def = fitNode(c, typ, def, def.info)
if not hasType and not hasDefault:
if isType: localError(c.config, a.info, "':' expected")
if kind in {skTemplate, skMacro}:
typ = newTypeS(tyUntyped, c)
elif skipTypes(typ, {tyGenericInst, tyAlias, tySink}).kind == tyVoid:
continue
for j in 0..<a.len-2:
var arg = newSymG(skParam, if a[j].kind == nkPragmaExpr: a[j][0] else: a[j], c)
if arg.name.id == ord(wUnderscore):
arg.flags.incl(sfGenSym)
elif containsOrIncl(check, arg.name.id):
localError(c.config, a[j].info, "attempt to redefine: '" & arg.name.s & "'")
if a[j].kind == nkPragmaExpr:
pragma(c, arg, a[j][1], paramPragmas)
if not hasType and not hasDefault and kind notin {skTemplate, skMacro}:
let param = strTableGet(c.signatures, arg.name)
if param != nil: typ = param.typ
else:
localError(c.config, a.info, "parameter '$1' requires a type" % arg.name.s)
typ = errorType(c)
var nameForLift = arg.name.s
if sfGenSym in arg.flags:
nameForLift.add("`gensym" & $arg.id)
let lifted = liftParamType(c, kind, genericParams, typ,
nameForLift, arg.info)
let finalType = if lifted != nil: lifted else: typ.skipIntLit(c.idgen)
arg.typ = finalType
arg.position = counter
if constraint != nil:
#only replace the constraint when it has been set as arg could contain codegenDecl
arg.constraint = constraint
inc(counter)
if def != nil and def.kind != nkEmpty:
arg.ast = copyTree(def)
result.n.add newSymNode(arg)
rawAddSon(result, finalType)
addParamOrResult(c, arg, kind)
styleCheckDef(c, a[j].info, arg)
onDef(a[j].info, arg)
if a[j].kind == nkPragmaExpr:
a[j][0] = newSymNode(arg)
else:
a[j] = newSymNode(arg)
var r: PType = nil
if n[0].kind != nkEmpty:
let isGeneric = isCurrentlyGeneric()
inc c.inGenericContext, ord(isGeneric)
r = semTypeNode(c, n[0], nil)
dec c.inGenericContext, ord(isGeneric)
if r != nil and kind in {skMacro, skTemplate} and r.kind == tyTyped:
# XXX: To implement the proposed change in the warning, just
# delete this entire if block. The rest is (at least at time of
# writing this comment) already implemented.
let info = n[0].info
const msg = "`typed` will change its meaning in future versions of Nim. " &
"`void` or no return type declaration at all has the same " &
"meaning as the current meaning of `typed` as return type " &
"declaration."
message(c.config, info, warnDeprecated, msg)
r = nil
if r != nil:
# turn explicit 'void' return type into 'nil' because the rest of the
# compiler only checks for 'nil':
if skipTypes(r, {tyGenericInst, tyAlias, tySink}).kind != tyVoid:
if kind notin {skMacro, skTemplate} and r.kind in {tyTyped, tyUntyped}:
localError(c.config, n[0].info, "return type '" & typeToString(r) &
"' is only valid for macros and templates")
# 'auto' as a return type does not imply a generic:
elif r.kind == tyAnything:
r = copyType(r, c.idgen, r.owner)
r.flags.incl tfRetType
elif r.kind == tyStatic:
# type allowed should forbid this type
discard
else:
if r.sym == nil or sfAnon notin r.sym.flags:
let lifted = liftParamType(c, kind, genericParams, r, "result",
n[0].info)
if lifted != nil:
r = lifted
#if r.kind != tyGenericParam:
#echo "came here for ", typeToString(r)
r.flags.incl tfRetType
r = skipIntLit(r, c.idgen)
if kind == skIterator:
# see tchainediterators
# in cases like iterator foo(it: iterator): typeof(it)
# we don't need to change the return type to iter[T]
result.flags.incl tfIterator
# XXX Would be nice if we could get rid of this
result[0] = r
let oldFlags = result.flags
propagateToOwner(result, r)
if oldFlags != result.flags:
# XXX This rather hacky way keeps 'tflatmap' compiling:
if tfHasMeta notin oldFlags:
result.flags.excl tfHasMeta
result.n.typ() = r
if isCurrentlyGeneric():
for n in genericParams:
if {sfUsed, sfAnon} * n.sym.flags == {}:
result.flags.incl tfUnresolved
if tfWildcard in n.sym.typ.flags:
n.sym.transitionGenericParamToType()
n.sym.typ.flags.excl tfWildcard
proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
checkMinSonsLen(n, 1, c.config)
for i in 0..<n.len - 1:
n[i] = semStmt(c, n[i], {})
if n.len > 0:
result = semTypeNode(c, n[^1], prev)
n.typ() = result
n[^1].typ() = result
else:
result = nil
proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
inc(c.p.nestedBlockCounter)
let oldBreakInLoop = c.p.breakInLoop
c.p.breakInLoop = false
checkSonsLen(n, 2, c.config)
openScope(c)
if n[0].kind notin {nkEmpty, nkSym}:
addDecl(c, newSymS(skLabel, n[0], c))
result = semStmtListType(c, n[1], prev)
n[1].typ() = result
n.typ() = result
closeScope(c)
c.p.breakInLoop = oldBreakInLoop
dec(c.p.nestedBlockCounter)
proc semGenericParamInInvocation(c: PContext, n: PNode): PType =
result = semTypeNode(c, n, nil)
n.typ() = makeTypeDesc(c, result)
proc trySemObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType): bool =
var
check = initIntSet()
pos = 0
let
realBase = t.baseClass
base = skipTypesOrNil(realBase, skipPtrs)
result = true
if base.isNil:
localError(c.config, n.info, errIllegalRecursionInTypeX % "object")
else:
let concreteBase = skipGenericInvocation(base)
if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags:
if not tryAddInheritedFields(c, check, pos, concreteBase, n):
return false
else:
if concreteBase.kind != tyError:
localError(c.config, n.info, errInheritanceOnlyWithNonFinalObjects)
var newf = newNodeI(nkRecList, n.info)
semRecordNodeAux(c, t.n, check, pos, newf, t)
proc containsGenericInvocationWithForward(n: PNode): bool =
if n.kind == nkSym and n.sym.ast != nil and n.sym.ast.len > 1 and n.sym.ast[2].kind == nkObjectTy:
for p in n.sym.ast[2][^1]:
if p.kind == nkIdentDefs and p[1].typ != nil and p[1].typ.kind == tyGenericInvocation and
p[1][0].kind == nkSym and p[1][0].typ.kind == tyForward:
return true
return false
proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
if s.typ == nil:
localError(c.config, n.info, "cannot instantiate the '$1' $2" %
[s.name.s, s.kind.toHumanStr])
return newOrPrevType(tyError, prev, c)
var t = s.typ.skipTypes({tyAlias})
if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody:
t = t.base
result = newOrPrevType(tyGenericInvocation, prev, c)
addSonSkipIntLit(result, t, c.idgen)
template addToResult(typ, skip) =
if typ.isNil:
internalAssert c.config, false
rawAddSon(result, typ)
else:
if skip:
addSonSkipIntLit(result, typ, c.idgen)
else:
rawAddSon(result, makeRangeWithStaticExpr(c, typ.n))
if t.kind == tyForward:
for i in 1..<n.len:
var elem = semGenericParamInInvocation(c, n[i])
addToResult(elem, true)
return
elif t.kind != tyGenericBody:
# we likely got code of the form TypeA[TypeB] where TypeA is
# not generic.
localError(c.config, n.info, errNoGenericParamsAllowedForX % s.name.s)
return newOrPrevType(tyError, prev, c)
else:
var m = newCandidate(c, t)
m.isNoCall = true
matches(c, n, copyTree(n), m)
if m.state != csMatch:
var err = "cannot instantiate "
err.addTypeHeader(c.config, t)
err.add "\ngot: <$1>\nbut expected: <$2>" % [describeArgs(c, n), describeArgs(c, t.n, 0)]
if m.firstMismatch.kind == kTypeMismatch and m.firstMismatch.arg < n.len:
let nArg = n[m.firstMismatch.arg]
if nArg.kind in nkSymChoices:
err.add "\n"
err.add ambiguousIdentifierMsg(nArg)
localError(c.config, n.info, errGenerated, err)
return newOrPrevType(tyError, prev, c)
var isConcrete = true
let rType = m.call[0].typ
let mIndex = if rType != nil: rType.len - 1 else: -1
for i in 1..<m.call.len:
var typ = m.call[i].typ
# is this a 'typedesc' *parameter*? If so, use the typedesc type,
# unstripped.
if m.call[i].kind == nkSym and m.call[i].sym.kind == skParam and
typ.kind == tyTypeDesc and containsGenericType(typ):
isConcrete = false
addToResult(typ, true)
else:
typ = typ.skipTypes({tyTypeDesc})
if containsGenericType(typ): isConcrete = false
var skip = true
if mIndex >= i - 1 and tfImplicitStatic in rType[i - 1].flags and isIntLit(typ):
skip = false
addToResult(typ, skip)
if isConcrete:
if s.ast == nil and s.typ.kind != tyCompositeTypeClass:
# XXX: What kind of error is this? is it still relevant?
localError(c.config, n.info, errCannotInstantiateX % s.name.s)
result = newOrPrevType(tyError, prev, c)
elif containsGenericInvocationWithForward(n[0]):
c.skipTypes.add n #fixes 1500
else:
result = instGenericContainer(c, n.info, result,
allowMetaTypes = false)
# special check for generic object with
# generic/partial specialized parent
let tx = result.skipTypes(abstractPtrs, 50)
if tx.isNil or isTupleRecursive(tx):
localError(c.config, n.info, "illegal recursion in type '$1'" % typeToString(result[0]))
return errorType(c)
if tx != result and tx.kind == tyObject:
if tx[0] != nil:
if not trySemObjectTypeForInheritedGenericInst(c, n, tx):
return newOrPrevType(tyError, prev, c)
var position = 0
recomputeFieldPositions(tx, tx.n, position)
proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
if prev != nil and (prev.kind == tyGenericBody or
typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward, tyGenericBody}):
result = newTypeS(tyAlias, c)
result.rawAddSon typeExpr
result.sym = prev.sym
if prev.kind != tyGenericBody:
assignType(prev, result)
else:
result = nil
proc fixupTypeOf(c: PContext, prev: PType, typ: PType) =
if prev != nil:
let result = newTypeS(tyAlias, c)
result.rawAddSon typ
result.sym = prev.sym
if prev.kind != tyGenericBody:
assignType(prev, result)
proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
var n = semExprWithType(c, n, {efDetermineType})
if n.typ.kind == tyTypeDesc:
result = n.typ.base
# fix types constructed by macros/template:
if prev != nil and prev.kind != tyGenericBody and prev.sym != nil:
if result.sym.isNil:
# Behold! you're witnessing enormous power yielded
# by macros. Only macros can summon unnamed types
# and cast spell upon AST. Here we need to give
# it a name taken from left hand side's node
result.sym = prev.sym
result.sym.typ = result
else:
# Less powerful routine like template do not have
# the ability to produce unnamed types. But still
# it has wild power to push a type a bit too far.
# So we need to hold it back using alias and prevent
# 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)
proc freshType(c: PContext; res, prev: PType): PType {.inline.} =
if prev.isNil or prev.kind == tyGenericBody:
result = copyType(res, c.idgen, res.owner)
copyTypeProps(c.graph, c.idgen.module, result, res)
else:
result = res
template modifierTypeKindOfNode(n: PNode): TTypeKind =
case n.kind
of nkVarTy: tyVar
of nkRefTy: tyRef
of nkPtrTy: tyPtr
of nkStaticTy: tyStatic
of nkTypeOfExpr: tyTypeDesc
else: tyNone
proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
# if n.len == 0: return newConstraint(c, tyTypeClass)
if isNewStyleConcept(n):
result = newOrPrevType(tyConcept, prev, c)
result.flags.incl tfCheckedForDestructor
result.n = semConceptDeclaration(c, n)
return result
let
pragmas = n[1]
inherited = n[2]
var owner = getCurrOwner(c)
var candidateTypeSlot = newTypeS(tyAlias, c, c.errorType)
result = newOrPrevType(tyUserTypeClass, prev, c, son = candidateTypeSlot)
result.flags.incl tfCheckedForDestructor
result.n = n
if inherited.kind != nkEmpty:
for n in inherited.sons:
let typ = semTypeNode(c, n, nil)
result.add(typ)
openScope(c)
for param in n[0]:
var
dummyName: PNode
dummyType: PType
let modifier = param.modifierTypeKindOfNode
if modifier != tyNone:
dummyName = param[0]
dummyType = c.makeTypeWithModifier(modifier, candidateTypeSlot)
# if modifier == tyRef:
# dummyType.flags.incl tfNotNil
if modifier == tyTypeDesc:
dummyType.flags.incl tfConceptMatchedTypeSym
dummyType.flags.incl tfCheckedForDestructor
else:
dummyName = param
dummyType = candidateTypeSlot
# this can be true for 'nim check' on incomplete concepts,
# see bug #8230
if dummyName.kind == nkEmpty: continue
internalAssert c.config, dummyName.kind == nkIdent
var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar,
dummyName.ident, c.idgen, owner, param.info)
dummyParam.typ = dummyType
incl dummyParam.flags, sfUsed
addDecl(c, dummyParam)
result.n[3] = semConceptBody(c, n[3])
closeScope(c)
proc applyTypeSectionPragmas(c: PContext; pragmas, operand: PNode): PNode =
result = nil
for p in pragmas:
let key = if p.kind in nkPragmaCallKinds and p.len >= 1: p[0] else: p
if p.kind == nkEmpty or whichPragma(p) != wInvalid:
discard "builtin pragma"
else:
trySuggestPragmas(c, key)
let ident =
if key.kind in nkIdentKinds:
considerQuotedIdent(c, key)
else:
nil
if ident != nil and strTableGet(c.userPragmas, ident) != nil:
discard "User-defined pragma"
else:
let sym = qualifiedLookUp(c, key, {})
# XXX: What to do here if amb is true?
if sym != nil and sfCustomPragma in sym.flags:
discard "Custom user pragma"
else:
# we transform ``(arg1, arg2: T) {.m, rest.}`` into ``m((arg1, arg2: T) {.rest.})`` and
# let the semantic checker deal with it:
var x = newNodeI(nkCall, key.info)
x.add(key)
if p.kind in nkPragmaCallKinds and p.len > 1:
# pass pragma arguments to the macro too:
for i in 1 ..< p.len:
x.add(p[i])
# Also pass the node the pragma has been applied to
x.add(operand.copyTreeWithoutNode(p))
# recursion assures that this works for multiple macro annotations too:
var r = semOverloadedCall(c, x, x, {skMacro, skTemplate}, {efNoUndeclared})
if r != nil and (r.typ == nil or r.typ.kind != tyFromExpr):
doAssert r[0].kind == nkSym
let m = r[0].sym
case m.kind
of skMacro: return semMacroExpr(c, r, r, m, {efNoSemCheck})
of skTemplate: return semTemplateExpr(c, r, m, {efNoSemCheck})
else: doAssert(false, "cannot happen")
proc semProcTypeWithScope(c: PContext, n: PNode,
prev: PType, kind: TSymKind): PType =
checkSonsLen(n, 2, c.config)
if n[1].kind != nkEmpty and n[1].len > 0:
let macroEval = applyTypeSectionPragmas(c, n[1], n)
if macroEval != nil:
return semTypeNode(c, macroEval, prev)
openScope(c)
result = semProcTypeNode(c, n[0], nil, prev, kind, isType=true)
# start with 'ccClosure', but of course pragmas can overwrite this:
result.callConv = ccClosure
# dummy symbol for `pragma`:
var s = newSymS(kind, newIdentNode(getIdent(c.cache, "dummy"), n.info), c)
s.typ = result
if n[1].kind != nkEmpty and n[1].len > 0:
pragma(c, s, n[1], procTypePragmas)
when useEffectSystem: setEffectsForProcType(c.graph, result, n[1])
elif c.optionStack.len > 0:
# we construct a fake 'nkProcDef' for the 'mergePragmas' inside 'implicitPragmas'...
s.ast = newTree(nkProcDef, newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info),
newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info))
implicitPragmas(c, s, n.info, {wTags, wRaises})
when useEffectSystem: setEffectsForProcType(c.graph, result, s.ast[pragmasPos])
closeScope(c)
proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym =
if n.kind == nkType:
result = symFromType(c, n.typ, n.info)
else:
localError(c.config, n.info, errTypeExpected)
result = errorSym(c, n)
proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType =
result = newOrPrevType(tyStatic, prev, c)
var base = semTypeNode(c, childNode, nil).skipTypes({tyTypeDesc, tyAlias})
result.rawAddSon(base)
result.flags.incl tfHasStatic
proc semTypeOf(c: PContext; n: PNode; prev: PType): PType =
openScope(c)
inc c.inTypeofContext
defer: dec c.inTypeofContext # compiles can raise an exception
let ex = semExprWithType(c, n, {efInTypeof})
closeScope(c)
result = ex.typ
if result.kind == tyFromExpr:
result.flags.incl tfNonConstExpr
elif result.kind == tyStatic:
let base = result.skipTypes({tyStatic})
if c.inGenericContext > 0 and base.containsGenericType:
result = makeTypeFromExpr(c, copyTree(ex))
result.flags.incl tfNonConstExpr
else:
result = base
fixupTypeOf(c, prev, result)
proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
openScope(c)
var m = BiggestInt 1 # typeOfIter
if n.len == 3:
let mode = semConstExpr(c, n[2])
if mode.kind != nkIntLit:
localError(c.config, n.info, "typeof: cannot evaluate 'mode' parameter at compile-time")
else:
m = mode.intVal
inc c.inTypeofContext
defer: dec c.inTypeofContext # compiles can raise an exception
let ex = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
closeScope(c)
result = ex.typ
if result.kind == tyFromExpr:
result.flags.incl tfNonConstExpr
elif result.kind == tyStatic:
let base = result.skipTypes({tyStatic})
if c.inGenericContext > 0 and base.containsGenericType:
result = makeTypeFromExpr(c, copyTree(ex))
result.flags.incl tfNonConstExpr
else:
result = base
fixupTypeOf(c, prev, result)
proc semTypeIdent(c: PContext, n: PNode): PSym =
if n.kind == nkSym:
result = getGenSym(c, n.sym)
else:
result = pickSym(c, n, {skType, skGenericParam, skParam})
if result.isNil:
result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared})
if result != nil:
markUsed(c, n.info, result)
onUse(n.info, result)
# alias syntax, see semSym for skTemplate, skMacro
if result.kind in {skTemplate, skMacro} and sfNoalias notin result.flags:
let t = semTypeExpr(c, n, nil)
result = symFromType(c, t, n.info)
if result.kind == skParam and result.typ.kind == tyTypeDesc:
# This is a typedesc param. is it already bound?
# it's not bound when it's used multiple times in the
# proc signature for example
if c.inGenericInst > 0:
let bound = result.typ.elementType.sym
if bound != nil: return bound
return result
if result.typ.sym == nil:
localError(c.config, n.info, errTypeExpected)
return errorSym(c, n)
result = result.typ.sym.copySym(c.idgen)
result.typ = exactReplica(result.typ)
result.typ.flags.incl tfUnresolved
if result.kind == skGenericParam:
if result.typ.kind == tyGenericParam and result.typ.len == 0 and
tfWildcard in result.typ.flags:
# collapse the wild-card param to a type
result.transitionGenericParamToType()
result.typ.flags.excl tfWildcard
return
else:
localError(c.config, n.info, errTypeExpected)
return errorSym(c, n)
if result.kind != skType and result.magic notin {mStatic, mType, mTypeOf}:
# this implements the wanted ``var v: V, x: V`` feature ...
var ov: TOverloadIter = default(TOverloadIter)
var amb = initOverloadIter(ov, c, n)
while amb != nil and amb.kind != skType:
amb = nextOverloadIter(ov, c, n)
if amb != nil: result = amb
else:
if result.kind != skError: localError(c.config, n.info, errTypeExpected)
return errorSym(c, n)
if result.typ.kind != tyGenericParam:
# XXX get rid of this hack!
var oldInfo = n.info
when defined(useNodeIds):
let oldId = n.id
reset(n[])
when defined(useNodeIds):
n.id = oldId
n.transitionNoneToSym()
n.sym = result
n.info = oldInfo
n.typ() = result.typ
else:
localError(c.config, n.info, "identifier expected")
result = errorSym(c, n)
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = nil
inc c.inTypeContext
if c.config.cmd == cmdIdeTools: suggestExpr(c, n)
case n.kind
of nkEmpty: result = n.typ
of nkTypeOfExpr:
# for ``typeof(countup(1,3))``, see ``tests/ttoseq``.
checkSonsLen(n, 1, c.config)
result = semTypeOf(c, n[0], prev)
if result.kind == tyTypeDesc: result.flags.incl tfExplicit
of nkPar:
if n.len == 1: result = semTypeNode(c, n[0], prev)
else:
result = semAnonTuple(c, n, prev)
of nkTupleConstr: result = semAnonTuple(c, n, prev)
of nkCallKinds:
let x = n[0]
let ident = x.getPIdent
if ident != nil and ident.s == "[]":
let b = newNodeI(nkBracketExpr, n.info)
for i in 1..<n.len: b.add(n[i])
result = semTypeNode(c, b, prev)
elif ident != nil and ident.id == ord(wDotDot):
result = semRangeAux(c, n, prev)
elif n[0].kind == nkNilLit and n.len == 2:
result = semTypeNode(c, n[1], prev)
if result.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).kind in NilableTypes+GenericTypes:
if tfNotNil in result.flags:
result = freshType(c, result, prev)
result.flags.excl(tfNotNil)
else:
localError(c.config, n.info, errGenerated, "invalid type")
elif n[0].kind notin nkIdentKinds:
result = semTypeExpr(c, n, prev)
else:
let op = considerQuotedIdent(c, n[0])
if op.id == ord(wAnd) or op.id == ord(wOr) or op.s == "|":
checkSonsLen(n, 3, c.config)
var
t1 = semTypeNode(c, n[1], nil)
t2 = semTypeNode(c, n[2], nil)
if t1 == nil:
localError(c.config, n[1].info, errTypeExpected)
result = newOrPrevType(tyError, prev, c)
elif t2 == nil:
localError(c.config, n[2].info, errTypeExpected)
result = newOrPrevType(tyError, prev, c)
else:
result = if op.id == ord(wAnd): makeAndType(c, t1, t2)
else: makeOrType(c, t1, t2)
elif op.id == ord(wNot):
case n.len
of 3:
result = semTypeNode(c, n[1], prev)
if result.kind == tyTypeDesc and tfUnresolved notin result.flags:
result = result.base
if n[2].kind != nkNilLit:
localError(c.config, n.info,
"Invalid syntax. When used with a type, 'not' can be followed only by 'nil'")
if notnil notin c.features and strictNotNil notin c.features:
localError(c.config, n.info,
"enable the 'not nil' annotation with {.experimental: \"notnil\".} or " &
" the `strict not nil` annotation with {.experimental: \"strictNotNil\".} " &
" the \"notnil\" one is going to be deprecated, so please use \"strictNotNil\"")
let resolvedType = result.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned})
case resolvedType.kind
of tyGenericParam, tyTypeDesc, tyFromExpr:
# XXX: This is a really inappropraite hack, but it solves
# https://github.com/nim-lang/Nim/issues/4907 for now.
#
# A proper solution is to introduce a new type kind such
# as `tyNotNil[tyRef[SomeGenericParam]]`. This will allow
# semtypinst to replace the generic param correctly in
# situations like the following:
#
# type Foo[T] = object
# bar: ref T not nil
# baz: ref T
#
# The root of the problem is that `T` here must have a specific
# ID that is bound to a concrete type during instantiation.
# The use of `freshType` below breaks this. Another hack would
# be to reuse the same ID for the not nil type, but this will
# fail if the `T` parameter is referenced multiple times as in
# the example above.
#
# I suggest revisiting this once the language decides on whether
# `not nil` should be the default. We can then map nilable refs
# to other types such as `Option[T]`.
result = makeTypeFromExpr(c, newTree(nkStmtListType, n.copyTree))
of NilableTypes + {tyGenericInvocation, tyForward}:
result = freshType(c, result, prev)
result.flags.incl(tfNotNil)
else:
localError(c.config, n.info, errGenerated, "invalid type")
of 2:
let negated = semTypeNode(c, n[1], prev)
result = makeNotType(c, negated)
else:
localError(c.config, n.info, errGenerated, "invalid type")
elif op.id == ord(wPtr):
result = semAnyRef(c, n, tyPtr, prev)
elif op.id == ord(wRef):
result = semAnyRef(c, n, tyRef, prev)
elif op.id == ord(wType):
checkSonsLen(n, 2, c.config)
result = semTypeOf(c, n[1], prev)
elif op.s == "typeof" and (
(n[0].kind == nkSym and n[0].sym.magic == mTypeOf) or
(n[0].kind == nkOpenSym and n[0][0].sym.magic == mTypeOf)):
result = semTypeOf2(c, n, prev)
elif op.s == "owned" and optOwnedRefs notin c.config.globalOptions and n.len == 2:
result = semTypeExpr(c, n[1], prev)
else:
result = semTypeExpr(c, n, prev)
of nkWhenStmt:
var whenResult = semWhen(c, n, false)
if whenResult.kind == nkStmtList: whenResult.transitionSonsKind(nkStmtListType)
if whenResult.kind == nkWhenStmt:
result = whenResult.typ
else:
result = semTypeNode(c, whenResult, prev)
of nkBracketExpr:
checkMinSonsLen(n, 2, c.config)
var head = n[0]
var s = if head.kind notin nkCallKinds: semTypeIdent(c, head)
else: symFromExpectedTypeNode(c, semExpr(c, head))
case s.magic
of mArray: result = semArray(c, n, prev)
of mOpenArray: result = semContainer(c, n, tyOpenArray, "openarray", prev)
of mUncheckedArray: result = semContainer(c, n, tyUncheckedArray, "UncheckedArray", prev)
of mRange: result = semRange(c, n, prev)
of mSet: result = semSet(c, n, prev)
of mOrdinal: result = semOrdinal(c, n, prev)
of mIterableType: result = semIterableType(c, n, prev)
of mSeq:
result = semContainer(c, n, tySequence, "seq", prev)
if optSeqDestructors in c.config.globalOptions:
incl result.flags, tfHasAsgn
of mVarargs: result = semVarargs(c, n, prev)
of mTypeDesc, mType, mTypeOf:
result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
result.flags.incl tfExplicit
of mStatic:
result = semStaticType(c, n[1], prev)
of mExpr:
result = semTypeNode(c, n[0], nil)
if result != nil:
let old = result
result = copyType(result, c.idgen, getCurrOwner(c))
copyTypeProps(c.graph, c.idgen.module, result, old)
for i in 1..<n.len:
result.rawAddSon(semTypeNode(c, n[i], nil))
of mDistinct:
result = newOrPrevType(tyDistinct, prev, c)
addSonSkipIntLit(result, semTypeNode(c, n[1], nil), c.idgen)
of mVar:
result = newOrPrevType(tyVar, prev, c)
var base = semTypeNode(c, n[1], nil)
if base.kind in {tyVar, tyLent}:
localError(c.config, n.info, "type 'var var' is not allowed")
base = base[0]
addSonSkipIntLit(result, base, c.idgen)
of mRef: result = semAnyRef(c, n, tyRef, prev)
of mPtr: result = semAnyRef(c, n, tyPtr, prev)
of mTuple: result = semTuple(c, n, prev)
of mBuiltinType:
case s.name.s
of "lent": result = semAnyRef(c, n, tyLent, prev)
of "sink": result = semAnyRef(c, n, tySink, prev)
of "owned": result = semAnyRef(c, n, tyOwned, prev)
else: result = semGeneric(c, n, s, prev)
else: result = semGeneric(c, n, s, prev)
of nkDotExpr:
let typeExpr = semExpr(c, n)
if typeExpr.typ.isNil:
localError(c.config, n.info, "object constructor needs an object type;" &
" for named arguments use '=' instead of ':'")
result = errorType(c)
elif typeExpr.typ.kind == tyFromExpr:
result = typeExpr.typ
elif typeExpr.typ.kind != tyTypeDesc:
localError(c.config, n.info, errTypeExpected)
result = errorType(c)
else:
result = typeExpr.typ.base
if result.isMetaType and
result.kind != tyUserTypeClass:
# the dot expression may refer to a concept type in
# a different module. allow a normal alias then.
let preprocessed = semGenericStmt(c, n)
result = makeTypeFromExpr(c, preprocessed.copyTree)
else:
let alias = maybeAliasType(c, result, prev)
if alias != nil: result = alias
of nkIdent, nkAccQuoted:
var s = semTypeIdent(c, n)
if s.typ == nil:
if s.kind != skError: localError(c.config, n.info, errTypeExpected)
result = newOrPrevType(tyError, prev, c)
elif s.kind == skParam and s.typ.kind == tyTypeDesc:
internalAssert c.config, s.typ.base.kind != tyNone
result = s.typ.base
elif prev == nil:
result = s.typ
else:
let alias = maybeAliasType(c, s.typ, prev)
if alias != nil:
result = alias
elif prev.kind == tyGenericBody:
result = s.typ
else:
assignType(prev, s.typ)
# bugfix: keep the fresh id for aliases to integral types:
if s.typ.kind notin {tyBool, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
tyUInt..tyUInt64}:
prev.itemId = s.typ.itemId
result = prev
of nkSym:
let s = getGenSym(c, n.sym)
if s.typ != nil and (s.kind == skType or s.typ.kind == tyTypeDesc):
var t =
if s.kind == skType:
s.typ
else:
internalAssert c.config, s.typ.base.kind != tyNone
s.typ.base
let alias = maybeAliasType(c, t, prev)
if alias != nil:
result = alias
elif prev == nil or prev.kind == tyGenericBody:
result = t
else:
assignType(prev, t)
result = prev
markUsed(c, n.info, n.sym)
onUse(n.info, n.sym)
else:
if s.kind != skError:
if s.typ == nil:
localError(c.config, n.info, "type expected, but symbol '$1' has no type." % [s.name.s])
else:
localError(c.config, n.info, "type expected, but got symbol '$1' of kind '$2'" %
[s.name.s, s.kind.toHumanStr])
result = newOrPrevType(tyError, prev, c)
of nkObjectTy: result = semObjectNode(c, n, prev, {})
of nkTupleTy: result = semTuple(c, n, prev)
of nkTupleClassTy: result = newConstraint(c, tyTuple)
of nkTypeClassTy: result = semTypeClass(c, n, prev)
of nkRefTy: result = semAnyRef(c, n, tyRef, prev)
of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev)
of nkVarTy: result = semVarOutType(c, n, prev, {})
of nkOutTy: result = semVarOutType(c, n, prev, {tfIsOutParam})
of nkDistinctTy: result = semDistinct(c, n, prev)
of nkStaticTy: result = semStaticType(c, n[0], prev)
of nkProcTy, nkIteratorTy:
if n.len == 0 or n[0].kind == nkEmpty:
# 0 length or empty param list with possible pragmas imply typeclass
result = newTypeS(tyBuiltInTypeClass, c)
let child = newTypeS(tyProc, c)
if n.kind == nkIteratorTy:
child.flags.incl tfIterator
if n.len > 0 and n[1].kind != nkEmpty and n[1].len > 0:
# typeclass with pragma
let symKind = if n.kind == nkIteratorTy: skIterator else: skProc
# dummy symbol for `pragma`:
var s = newSymS(symKind, newIdentNode(getIdent(c.cache, "dummy"), n.info), c)
s.typ = child
# for now only call convention pragmas supported in proc typeclass
pragma(c, s, n[1], {FirstCallConv..LastCallConv})
result.addSonSkipIntLit(child, c.idgen)
else:
let symKind = if n.kind == nkIteratorTy: skIterator else: skProc
result = semProcTypeWithScope(c, n, prev, symKind)
if result == nil:
localError(c.config, n.info, "type expected, but got: " & renderTree(n))
result = newOrPrevType(tyError, prev, c)
if n.kind == nkIteratorTy and result.kind == tyProc:
result.flags.incl(tfIterator)
if result.callConv == ccClosure and c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
result.flags.incl tfHasAsgn
of nkEnumTy: result = semEnum(c, n, prev)
of nkType: result = n.typ
of nkStmtListType: result = semStmtListType(c, n, prev)
of nkBlockType: result = semBlockType(c, n, prev)
of nkOpenSym: result = semTypeNode(c, n[0], prev)
else:
result = semTypeExpr(c, n, prev)
when false:
localError(c.config, n.info, "type expected, but got: " & renderTree(n))
result = newOrPrevType(tyError, prev, c)
n.typ() = result
dec c.inTypeContext
proc setMagicType(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) =
# source : https://en.wikipedia.org/wiki/Data_structure_alignment#x86
m.typ.kind = kind
m.typ.size = size
# this usually works for most basic types
# Assuming that since ARM, ARM64 don't support unaligned access
# data is aligned to type size
m.typ.align = size.int16
# FIXME: proper support for clongdouble should be added.
# long double size can be 8, 10, 12, 16 bytes depending on platform & compiler
if kind in {tyFloat64, tyFloat, tyInt, tyUInt, tyInt64, tyUInt64} and size == 8:
m.typ.align = int16(conf.floatInt64Align)
proc setMagicIntegral(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) =
setMagicType(conf, m, kind, size)
incl m.typ.flags, tfCheckedForDestructor
proc processMagicType(c: PContext, m: PSym) =
case m.magic
of mInt: setMagicIntegral(c.config, m, tyInt, c.config.target.intSize)
of mInt8: setMagicIntegral(c.config, m, tyInt8, 1)
of mInt16: setMagicIntegral(c.config, m, tyInt16, 2)
of mInt32: setMagicIntegral(c.config, m, tyInt32, 4)
of mInt64: setMagicIntegral(c.config, m, tyInt64, 8)
of mUInt: setMagicIntegral(c.config, m, tyUInt, c.config.target.intSize)
of mUInt8: setMagicIntegral(c.config, m, tyUInt8, 1)
of mUInt16: setMagicIntegral(c.config, m, tyUInt16, 2)
of mUInt32: setMagicIntegral(c.config, m, tyUInt32, 4)
of mUInt64: setMagicIntegral(c.config, m, tyUInt64, 8)
of mFloat: setMagicIntegral(c.config, m, tyFloat, c.config.target.floatSize)
of mFloat32: setMagicIntegral(c.config, m, tyFloat32, 4)
of mFloat64: setMagicIntegral(c.config, m, tyFloat64, 8)
of mFloat128: setMagicIntegral(c.config, m, tyFloat128, 16)
of mBool: setMagicIntegral(c.config, m, tyBool, 1)
of mChar: setMagicIntegral(c.config, m, tyChar, 1)
of mString:
setMagicType(c.config, m, tyString, szUncomputedSize)
rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
if optSeqDestructors in c.config.globalOptions:
incl m.typ.flags, tfHasAsgn
of mCstring:
setMagicIntegral(c.config, m, tyCstring, c.config.target.ptrSize)
rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
of mPointer: setMagicIntegral(c.config, m, tyPointer, c.config.target.ptrSize)
of mNil: setMagicType(c.config, m, tyNil, c.config.target.ptrSize)
of mExpr:
if m.name.s == "auto":
setMagicIntegral(c.config, m, tyAnything, 0)
else:
setMagicIntegral(c.config, m, tyUntyped, 0)
of mStmt:
setMagicIntegral(c.config, m, tyTyped, 0)
of mTypeDesc, mType:
setMagicIntegral(c.config, m, tyTypeDesc, 0)
rawAddSon(m.typ, newTypeS(tyNone, c))
of mStatic:
setMagicType(c.config, m, tyStatic, 0)
rawAddSon(m.typ, newTypeS(tyNone, c))
of mVoidType:
setMagicIntegral(c.config, m, tyVoid, 0)
of mArray:
setMagicType(c.config, m, tyArray, szUncomputedSize)
of mOpenArray:
setMagicType(c.config, m, tyOpenArray, szUncomputedSize)
of mVarargs:
setMagicType(c.config, m, tyVarargs, szUncomputedSize)
of mRange:
setMagicIntegral(c.config, m, tyRange, szUncomputedSize)
rawAddSon(m.typ, newTypeS(tyNone, c))
of mSet:
setMagicIntegral(c.config, m, tySet, szUncomputedSize)
of mUncheckedArray:
setMagicIntegral(c.config, m, tyUncheckedArray, szUncomputedSize)
of mSeq:
setMagicType(c.config, m, tySequence, szUncomputedSize)
if optSeqDestructors in c.config.globalOptions:
incl m.typ.flags, tfHasAsgn
if defined(nimsuggest) or c.config.cmd == cmdCheck: # bug #18985
discard
else:
assert c.graph.sysTypes[tySequence] == nil
c.graph.sysTypes[tySequence] = m.typ
of mOrdinal:
setMagicIntegral(c.config, m, tyOrdinal, szUncomputedSize)
rawAddSon(m.typ, newTypeS(tyNone, c))
of mIterableType:
setMagicIntegral(c.config, m, tyIterable, 0)
rawAddSon(m.typ, newTypeS(tyNone, c))
of mPNimrodNode:
incl m.typ.flags, tfTriggersCompileTime
incl m.typ.flags, tfCheckedForDestructor
of mException: discard
of mBuiltinType:
case m.name.s
of "lent": setMagicType(c.config, m, tyLent, c.config.target.ptrSize)
of "sink": setMagicType(c.config, m, tySink, szUncomputedSize)
of "owned":
setMagicType(c.config, m, tyOwned, c.config.target.ptrSize)
incl m.typ.flags, tfHasOwned
else: localError(c.config, m.info, errTypeExpected)
else: localError(c.config, m.info, errTypeExpected)
proc semGenericConstraints(c: PContext, x: PType): PType =
result = newTypeS(tyGenericParam, c, x)
proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
template addSym(result: PNode, s: PSym): untyped =
if father != nil: addSonSkipIntLit(father, s.typ, c.idgen)
if sfGenSym notin s.flags: addDecl(c, s)
result.add newSymNode(s)
result = copyNode(n)
if n.kind != nkGenericParams:
illFormedAst(n, c.config)
return
for i in 0..<n.len:
var a = n[i]
case a.kind
of nkSym: result.addSym(a.sym)
of nkIdentDefs:
var def = a[^1]
let constraint = a[^2]
var typ: PType = nil
if constraint.kind != nkEmpty:
typ = semTypeNode(c, constraint, nil)
if typ.kind != tyStatic or typ.len == 0:
if typ.kind == tyTypeDesc:
if typ.elementType.kind == tyNone:
typ = newTypeS(tyTypeDesc, c, newTypeS(tyNone, c))
incl typ.flags, tfCheckedForDestructor
else:
typ = semGenericConstraints(c, typ)
if def.kind != nkEmpty:
def = semConstExpr(c, def)
if typ == nil:
if def.typ.kind != tyTypeDesc:
typ = newTypeS(tyStatic, c, def.typ)
else:
# the following line fixes ``TV2*[T:SomeNumber=TR] = array[0..1, T]``
# from manyloc/named_argument_bug/triengine:
def.typ() = def.typ.skipTypes({tyTypeDesc})
if not containsGenericType(def.typ):
def = fitNode(c, typ, def, def.info)
if typ == nil:
typ = newTypeS(tyGenericParam, c)
if father == nil: typ.flags.incl tfWildcard
typ.flags.incl tfGenericTypeParam
for j in 0..<a.len-2:
var finalType: PType
if j == 0:
finalType = typ
else:
finalType = copyType(typ, c.idgen, typ.owner)
copyTypeProps(c.graph, c.idgen.module, finalType, typ)
# it's important the we create an unique
# type for each generic param. the index
# of the parameter will be stored in the
# attached symbol.
var paramName = a[j]
var covarianceFlag = tfUnresolved
if paramName.safeLen == 2:
if not nimEnableCovariance or paramName[0].ident.s == "in":
if father == nil or sfImportc notin father.sym.flags:
localError(c.config, paramName.info, errInOutFlagNotExtern % $paramName[0])
covarianceFlag = if paramName[0].ident.s == "in": tfContravariant
else: tfCovariant
if father != nil: father.flags.incl tfCovariant
paramName = paramName[1]
var s = if finalType.kind == tyStatic or tfWildcard in typ.flags:
newSymG(skGenericParam, paramName, c).linkTo(finalType)
else:
newSymG(skType, paramName, c).linkTo(finalType)
if covarianceFlag != tfUnresolved: s.typ.flags.incl(covarianceFlag)
if def.kind != nkEmpty: s.ast = def
s.position = result.len
result.addSym(s)
else:
illFormedAst(n, c.config)