mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-29 01:14:41 +00:00
fixes #16376
The way the compiler handled generic proc instantiations in calls (like
`foo[int](...)`) up to this point was to instantiate `foo[int]`, create
a symbol for the instantiated proc (or a symchoice for multiple procs
excluding ones with mismatching generic param counts), then perform
overload resolution on this symbol/symchoice. The exception to this was
when the called symbol was already a symchoice node, in which case it
wasn't instantiated and overloading was called directly ([these
lines](b7b1313d21/compiler/semexprs.nim (L3366-L3371))).
This has several problems:
* Templates and macros can't create instantiated symbols, so they
couldn't participate in overloaded explicit generic instantiations,
causing the issue #16376.
* Every single proc that can be instantiated with the given generic
params is fully instantiated including the body. #9997 is about this but
isn't fixed here since the instantiation isn't in a call.
The way overload resolution handles explicit instantiations by itself is
also buggy:
* It doesn't check constraints.
* It allows only partially providing the generic parameters, which makes
sense for implicit generics, but can cause ambiguity in overloading.
Here is how this PR deals with these problems:
* Overload resolution now always handles explicit generic instantiations
in calls, in `initCandidate`, as long as the symbol resolves to a
routine symbol.
* Overload resolution now checks the generic params for constraints and
correct parameter count (ignoring implicit params). If these don't
match, the entire overload is considered as not matching and not
instantiated.
* Special error messages are added for mismatching/missing/extra generic
params. This is almost all of the diff in `semcall`.
* Procs with matching generic parameters now instantiate only the type
of the signature in overload resolution, not the proc itself, which also
works for templates and macros.
Unfortunately we can't entirely remove instantiations because overload
resolution can't handle some cases with uninstantiated types even though
it's resolved in the binding (see the last 2 blocks in
`texplicitgenerics`). There are also some instantiation issues with
default params that #24005 didn't fix but I didn't want this to become
the 3rd huge generics PR in a row so I didn't dive too deep into trying
to fix them. There is still a minor instantiation fix in `semtypinst`
though for subscripts in calls.
Additional changes:
* Overloading of `[]` wasn't documented properly, it somewhat is now
because we need to mention the limitation that it can't be done for
generic procs/types.
* Tests can now enable the new type mismatch errors with just
`-d:testsConciseTypeMismatch` in the command.
Package PRs:
- using fork for now:
[combparser](https://github.com/PMunch/combparser/pull/7) (partial
generic instantiation)
- merged: [cligen](https://github.com/c-blake/cligen/pull/233) (partial
generic instantiation but non-overloaded + template)
- merged: [neo](https://github.com/andreaferretti/neo/pull/56) (trying
to instantiate template with no generic param)
636 lines
24 KiB
Nim
636 lines
24 KiB
Nim
#
|
|
#
|
|
# The Nim Compiler
|
|
# (c) Copyright 2017 Andreas Rumpf
|
|
#
|
|
# See the file "copying.txt", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
## This module contains the data structures for the semantic checking phase.
|
|
|
|
import std/[tables, intsets, sets]
|
|
|
|
when defined(nimPreviewSlimSystem):
|
|
import std/assertions
|
|
|
|
import
|
|
options, ast, astalgo, msgs, idents, renderer,
|
|
magicsys, vmdef, modulegraphs, lineinfos, pathutils
|
|
|
|
import ic / ic
|
|
|
|
type
|
|
TOptionEntry* = object # entries to put on a stack for pragma parsing
|
|
options*: TOptions
|
|
defaultCC*: TCallingConvention
|
|
dynlib*: PLib
|
|
notes*: TNoteKinds
|
|
features*: set[Feature]
|
|
otherPragmas*: PNode # every pragma can be pushed
|
|
warningAsErrors*: TNoteKinds
|
|
|
|
POptionEntry* = ref TOptionEntry
|
|
PProcCon* = ref TProcCon
|
|
TProcCon* {.acyclic.} = object # procedure context; also used for top-level
|
|
# statements
|
|
owner*: PSym # the symbol this context belongs to
|
|
resultSym*: PSym # the result symbol (if we are in a proc)
|
|
nestedLoopCounter*: int # whether we are in a loop or not
|
|
nestedBlockCounter*: int # whether we are in a block or not
|
|
breakInLoop*: bool # whether we are in a loop without block
|
|
next*: PProcCon # used for stacking procedure contexts
|
|
mappingExists*: bool
|
|
mapping*: Table[ItemId, PSym]
|
|
caseContext*: seq[tuple[n: PNode, idx: int]]
|
|
localBindStmts*: seq[PNode]
|
|
|
|
TMatchedConcept* = object
|
|
candidateType*: PType
|
|
prev*: ptr TMatchedConcept
|
|
depth*: int
|
|
|
|
TInstantiationPair* = object
|
|
genericSym*: PSym
|
|
inst*: PInstantiation
|
|
|
|
TExprFlag* = enum
|
|
efLValue, efWantIterator, efWantIterable, efInTypeof,
|
|
efNeedStatic,
|
|
# Use this in contexts where a static value is mandatory
|
|
efPreferStatic,
|
|
# Use this in contexts where a static value could bring more
|
|
# information, but it's not strictly mandatory. This may become
|
|
# the default with implicit statics in the future.
|
|
efPreferNilResult,
|
|
# Use this if you want a certain result (e.g. static value),
|
|
# but you don't want to trigger a hard error. For example,
|
|
# you may be in position to supply a better error message
|
|
# to the user.
|
|
efWantStmt, efAllowStmt, efDetermineType, efExplain,
|
|
efWantValue, efOperand, efNoSemCheck,
|
|
efNoEvaluateGeneric, efInCall, efFromHlo, efNoSem2Check,
|
|
efNoUndeclared, efIsDotCall, efCannotBeDotCall,
|
|
# Use this if undeclared identifiers should not raise an error during
|
|
# overload resolution.
|
|
efNoDiagnostics,
|
|
efTypeAllowed # typeAllowed will be called after
|
|
efWantNoDefaults
|
|
efAllowSymChoice # symchoice node should not be resolved
|
|
|
|
TExprFlags* = set[TExprFlag]
|
|
|
|
ImportMode* = enum
|
|
importAll, importSet, importExcept
|
|
ImportedModule* = object
|
|
m*: PSym
|
|
case mode*: ImportMode
|
|
of importAll: discard
|
|
of importSet:
|
|
imported*: IntSet # of PIdent.id
|
|
of importExcept:
|
|
exceptSet*: IntSet # of PIdent.id
|
|
|
|
PContext* = ref TContext
|
|
TContext* = object of TPassContext # a context represents the module
|
|
# that is currently being compiled
|
|
enforceVoidContext*: PType
|
|
# for `if cond: stmt else: foo`, `foo` will be evaluated under
|
|
# enforceVoidContext != nil
|
|
voidType*: PType # for typeof(stmt)
|
|
module*: PSym # the module sym belonging to the context
|
|
currentScope*: PScope # current scope
|
|
moduleScope*: PScope # scope for modules
|
|
imports*: seq[ImportedModule] # scope for all imported symbols
|
|
topLevelScope*: PScope # scope for all top-level symbols
|
|
p*: PProcCon # procedure context
|
|
intTypeCache*: array[-5..32, PType] # cache some common integer types
|
|
# to avoid type allocations
|
|
nilTypeCache*: PType
|
|
matchedConcept*: ptr TMatchedConcept # the current concept being matched
|
|
friendModules*: seq[PSym] # friend modules; may access private data;
|
|
# this is used so that generic instantiations
|
|
# can access private object fields
|
|
instCounter*: int # to prevent endless instantiations
|
|
templInstCounter*: ref int # gives every template instantiation a unique id
|
|
inGenericContext*: int # > 0 if we are in a generic type
|
|
inStaticContext*: int # > 0 if we are inside a static: block
|
|
inUnrolledContext*: int # > 0 if we are unrolling a loop
|
|
compilesContextId*: int # > 0 if we are in a ``compiles`` magic
|
|
compilesContextIdGenerator*: int
|
|
inGenericInst*: int # > 0 if we are instantiating a generic
|
|
converters*: seq[PSym]
|
|
patterns*: seq[PSym] # sequence of pattern matchers
|
|
optionStack*: seq[POptionEntry]
|
|
libs*: seq[PLib] # all libs used by this module
|
|
semConstExpr*: proc (c: PContext, n: PNode; expectedType: PType = nil): PNode {.nimcall.} # for the pragmas
|
|
semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode {.nimcall.}
|
|
semExprWithType*: proc (c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode {.nimcall.}
|
|
semTryExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
|
|
semTryConstExpr*: proc (c: PContext, n: PNode; expectedType: PType = nil): PNode {.nimcall.}
|
|
computeRequiresInit*: proc (c: PContext, t: PType): bool {.nimcall.}
|
|
hasUnresolvedArgs*: proc (c: PContext, n: PNode): bool
|
|
|
|
semOperand*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
|
|
semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet
|
|
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
|
|
filter: TSymKinds, flags: TExprFlags, expectedType: PType = nil): PNode {.nimcall.}
|
|
semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.}
|
|
semInferredLambda*: proc(c: PContext, pt: Table[ItemId, PType], n: PNode): PNode
|
|
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: Table[ItemId, PType],
|
|
info: TLineInfo): PSym
|
|
instantiateOnlyProcType*: proc (c: PContext, pt: TypeMapping,
|
|
prc: PSym, info: TLineInfo): PType
|
|
# used by sigmatch for explicit generic instantiations
|
|
includedFiles*: IntSet # used to detect recursive include files
|
|
pureEnumFields*: TStrTable # pure enum fields that can be used unambiguously
|
|
userPragmas*: TStrTable
|
|
evalContext*: PEvalContext
|
|
unknownIdents*: IntSet # ids of all unknown identifiers to prevent
|
|
# naming it multiple times
|
|
generics*: seq[TInstantiationPair] # pending list of instantiated generics to compile
|
|
topStmts*: int # counts the number of encountered top level statements
|
|
lastGenericIdx*: int # used for the generics stack
|
|
hloLoopDetector*: int # used to prevent endless loops in the HLO
|
|
inParallelStmt*: int
|
|
instTypeBoundOp*: proc (c: PContext; dc: PSym; t: PType; info: TLineInfo;
|
|
op: TTypeAttachedOp; col: int): PSym {.nimcall.}
|
|
cache*: IdentCache
|
|
graph*: ModuleGraph
|
|
signatures*: TStrTable
|
|
recursiveDep*: string
|
|
suggestionsMade*: bool
|
|
isAmbiguous*: bool # little hack
|
|
features*: set[Feature]
|
|
inTypeContext*, inConceptDecl*: int
|
|
unusedImports*: seq[(PSym, TLineInfo)]
|
|
exportIndirections*: HashSet[(int, int)] # (module.id, symbol.id)
|
|
importModuleMap*: Table[int, int] # (module.id, module.id)
|
|
lastTLineInfo*: TLineInfo
|
|
sideEffects*: Table[int, seq[(TLineInfo, PSym)]] # symbol.id index
|
|
inUncheckedAssignSection*: int
|
|
importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id])
|
|
skipTypes*: seq[PNode] # used to skip types between passes in type section. So far only used for inheritance, sets and generic bodies.
|
|
inTypeofContext*: int
|
|
TBorrowState* = enum
|
|
bsNone, bsReturnNotMatch, bsNoDistinct, bsGeneric, bsNotSupported, bsMatch
|
|
|
|
template config*(c: PContext): ConfigRef = c.graph.config
|
|
|
|
proc getIntLitType*(c: PContext; literal: PNode): PType =
|
|
# we cache some common integer literal types for performance:
|
|
let value = literal.intVal
|
|
if value >= low(c.intTypeCache) and value <= high(c.intTypeCache):
|
|
result = c.intTypeCache[value.int]
|
|
if result == nil:
|
|
let ti = getSysType(c.graph, literal.info, tyInt)
|
|
result = copyType(ti, c.idgen, ti.owner)
|
|
result.n = literal
|
|
c.intTypeCache[value.int] = result
|
|
else:
|
|
let ti = getSysType(c.graph, literal.info, tyInt)
|
|
result = copyType(ti, c.idgen, ti.owner)
|
|
result.n = literal
|
|
|
|
proc setIntLitType*(c: PContext; result: PNode) =
|
|
let i = result.intVal
|
|
case c.config.target.intSize
|
|
of 8: result.typ = getIntLitType(c, result)
|
|
of 4:
|
|
if i >= low(int32) and i <= high(int32):
|
|
result.typ = getIntLitType(c, result)
|
|
else:
|
|
result.typ = getSysType(c.graph, result.info, tyInt64)
|
|
of 2:
|
|
if i >= low(int16) and i <= high(int16):
|
|
result.typ = getIntLitType(c, result)
|
|
elif i >= low(int32) and i <= high(int32):
|
|
result.typ = getSysType(c.graph, result.info, tyInt32)
|
|
else:
|
|
result.typ = getSysType(c.graph, result.info, tyInt64)
|
|
of 1:
|
|
# 8 bit CPUs are insane ...
|
|
if i >= low(int8) and i <= high(int8):
|
|
result.typ = getIntLitType(c, result)
|
|
elif i >= low(int16) and i <= high(int16):
|
|
result.typ = getSysType(c.graph, result.info, tyInt16)
|
|
elif i >= low(int32) and i <= high(int32):
|
|
result.typ = getSysType(c.graph, result.info, tyInt32)
|
|
else:
|
|
result.typ = getSysType(c.graph, result.info, tyInt64)
|
|
else:
|
|
internalError(c.config, result.info, "invalid int size")
|
|
|
|
proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
|
|
result = TInstantiationPair(genericSym: s, inst: inst)
|
|
|
|
proc filename*(c: PContext): string =
|
|
# the module's filename
|
|
result = toFilename(c.config, FileIndex c.module.position)
|
|
|
|
proc scopeDepth*(c: PContext): int {.inline.} =
|
|
result = if c.currentScope != nil: c.currentScope.depthLevel
|
|
else: 0
|
|
|
|
proc getCurrOwner*(c: PContext): PSym =
|
|
# owner stack (used for initializing the
|
|
# owner field of syms)
|
|
# the documentation comment always gets
|
|
# assigned to the current owner
|
|
result = c.graph.owners[^1]
|
|
|
|
proc pushOwner*(c: PContext; owner: PSym) =
|
|
c.graph.owners.add(owner)
|
|
|
|
proc popOwner*(c: PContext) =
|
|
if c.graph.owners.len > 0: setLen(c.graph.owners, c.graph.owners.len - 1)
|
|
else: internalError(c.config, "popOwner")
|
|
|
|
proc lastOptionEntry*(c: PContext): POptionEntry =
|
|
result = c.optionStack[^1]
|
|
|
|
proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next
|
|
|
|
proc put*(p: PProcCon; key, val: PSym) =
|
|
if not p.mappingExists:
|
|
p.mapping = initTable[ItemId, PSym]()
|
|
p.mappingExists = true
|
|
#echo "put into table ", key.info
|
|
p.mapping[key.itemId] = val
|
|
|
|
proc get*(p: PProcCon; key: PSym): PSym =
|
|
if not p.mappingExists: return nil
|
|
result = p.mapping.getOrDefault(key.itemId)
|
|
|
|
proc getGenSym*(c: PContext; s: PSym): PSym =
|
|
if sfGenSym notin s.flags: return s
|
|
var it = c.p
|
|
while it != nil:
|
|
result = get(it, s)
|
|
if result != nil:
|
|
#echo "got from table ", result.name.s, " ", result.info
|
|
return result
|
|
it = it.next
|
|
result = s
|
|
|
|
proc considerGenSyms*(c: PContext; n: PNode) =
|
|
if n == nil:
|
|
discard "can happen for nkFormalParams/nkArgList"
|
|
elif n.kind == nkSym:
|
|
let s = getGenSym(c, n.sym)
|
|
if n.sym != s:
|
|
n.sym = s
|
|
else:
|
|
for i in 0..<n.safeLen:
|
|
considerGenSyms(c, n[i])
|
|
|
|
proc newOptionEntry*(conf: ConfigRef): POptionEntry =
|
|
new(result)
|
|
result.options = conf.options
|
|
result.defaultCC = ccNimCall
|
|
result.dynlib = nil
|
|
result.notes = conf.notes
|
|
result.warningAsErrors = conf.warningAsErrors
|
|
|
|
proc pushOptionEntry*(c: PContext): POptionEntry =
|
|
new(result)
|
|
var prev = c.optionStack[^1]
|
|
result.options = c.config.options
|
|
result.defaultCC = prev.defaultCC
|
|
result.dynlib = prev.dynlib
|
|
result.notes = c.config.notes
|
|
result.warningAsErrors = c.config.warningAsErrors
|
|
result.features = c.features
|
|
c.optionStack.add(result)
|
|
|
|
proc popOptionEntry*(c: PContext) =
|
|
c.config.options = c.optionStack[^1].options
|
|
c.config.notes = c.optionStack[^1].notes
|
|
c.config.warningAsErrors = c.optionStack[^1].warningAsErrors
|
|
c.features = c.optionStack[^1].features
|
|
c.optionStack.setLen(c.optionStack.len - 1)
|
|
|
|
proc newContext*(graph: ModuleGraph; module: PSym): PContext =
|
|
new(result)
|
|
result.optionStack = @[newOptionEntry(graph.config)]
|
|
result.libs = @[]
|
|
result.module = module
|
|
result.friendModules = @[module]
|
|
result.converters = @[]
|
|
result.patterns = @[]
|
|
result.includedFiles = initIntSet()
|
|
result.pureEnumFields = initStrTable()
|
|
result.userPragmas = initStrTable()
|
|
result.generics = @[]
|
|
result.unknownIdents = initIntSet()
|
|
result.cache = graph.cache
|
|
result.graph = graph
|
|
result.signatures = initStrTable()
|
|
result.features = graph.config.features
|
|
if graph.config.symbolFiles != disabledSf:
|
|
let id = module.position
|
|
if graph.config.cmd != cmdM:
|
|
assert graph.packed[id].status in {undefined, outdated}
|
|
graph.packed[id].status = storing
|
|
graph.packed[id].module = module
|
|
initEncoder graph, module
|
|
|
|
template packedRepr*(c): untyped = c.graph.packed[c.module.position].fromDisk
|
|
template encoder*(c): untyped = c.graph.encoders[c.module.position]
|
|
|
|
proc addIncludeFileDep*(c: PContext; f: FileIndex) =
|
|
if c.config.symbolFiles != disabledSf:
|
|
addIncludeFileDep(c.encoder, c.packedRepr, f)
|
|
|
|
proc addImportFileDep*(c: PContext; f: FileIndex) =
|
|
if c.config.symbolFiles != disabledSf:
|
|
addImportFileDep(c.encoder, c.packedRepr, f)
|
|
|
|
proc addPragmaComputation*(c: PContext; n: PNode) =
|
|
if c.config.symbolFiles != disabledSf:
|
|
addPragmaComputation(c.encoder, c.packedRepr, n)
|
|
|
|
proc inclSym(sq: var seq[PSym], s: PSym): bool =
|
|
for i in 0..<sq.len:
|
|
if sq[i].id == s.id: return false
|
|
sq.add s
|
|
result = true
|
|
|
|
proc addConverter*(c: PContext, conv: LazySym) =
|
|
assert conv.sym != nil
|
|
if inclSym(c.converters, conv.sym):
|
|
add(c.graph.ifaces[c.module.position].converters, conv)
|
|
|
|
proc addConverterDef*(c: PContext, conv: LazySym) =
|
|
addConverter(c, conv)
|
|
if c.config.symbolFiles != disabledSf:
|
|
addConverter(c.encoder, c.packedRepr, conv.sym)
|
|
|
|
proc addPureEnum*(c: PContext, e: LazySym) =
|
|
assert e.sym != nil
|
|
add(c.graph.ifaces[c.module.position].pureEnums, e)
|
|
if c.config.symbolFiles != disabledSf:
|
|
addPureEnum(c.encoder, c.packedRepr, e.sym)
|
|
|
|
proc addPattern*(c: PContext, p: LazySym) =
|
|
assert p.sym != nil
|
|
if inclSym(c.patterns, p.sym):
|
|
add(c.graph.ifaces[c.module.position].patterns, p)
|
|
if c.config.symbolFiles != disabledSf:
|
|
addTrmacro(c.encoder, c.packedRepr, p.sym)
|
|
|
|
proc exportSym*(c: PContext; s: PSym) =
|
|
strTableAdds(c.graph, c.module, s)
|
|
if c.config.symbolFiles != disabledSf:
|
|
addExported(c.encoder, c.packedRepr, s)
|
|
|
|
proc reexportSym*(c: PContext; s: PSym) =
|
|
strTableAdds(c.graph, c.module, s)
|
|
if c.config.symbolFiles != disabledSf:
|
|
addReexport(c.encoder, c.packedRepr, s)
|
|
|
|
proc newLib*(kind: TLibKind): PLib =
|
|
new(result)
|
|
result.kind = kind #result.syms = initObjectSet()
|
|
|
|
proc addToLib*(lib: PLib, sym: PSym) =
|
|
#if sym.annex != nil and not isGenericRoutine(sym):
|
|
# LocalError(sym.info, errInvalidPragma)
|
|
sym.annex = lib
|
|
|
|
proc newTypeS*(kind: TTypeKind; c: PContext; son: sink PType = nil): PType =
|
|
result = newType(kind, c.idgen, getCurrOwner(c), son = son)
|
|
|
|
proc makePtrType*(owner: PSym, baseType: PType; idgen: IdGenerator): PType =
|
|
result = newType(tyPtr, idgen, owner, skipIntLit(baseType, idgen))
|
|
|
|
proc makePtrType*(c: PContext, baseType: PType): PType =
|
|
makePtrType(getCurrOwner(c), baseType, c.idgen)
|
|
|
|
proc makeTypeWithModifier*(c: PContext,
|
|
modifier: TTypeKind,
|
|
baseType: PType): PType =
|
|
assert modifier in {tyVar, tyLent, tyPtr, tyRef, tyStatic, tyTypeDesc}
|
|
|
|
if modifier in {tyVar, tyLent, tyTypeDesc} and baseType.kind == modifier:
|
|
result = baseType
|
|
else:
|
|
result = newTypeS(modifier, c, skipIntLit(baseType, c.idgen))
|
|
|
|
proc makeVarType*(c: PContext, baseType: PType; kind = tyVar): PType =
|
|
if baseType.kind == kind:
|
|
result = baseType
|
|
else:
|
|
result = newTypeS(kind, c, skipIntLit(baseType, c.idgen))
|
|
|
|
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
|
|
let typedesc = newTypeS(tyTypeDesc, c)
|
|
incl typedesc.flags, tfCheckedForDestructor
|
|
internalAssert(c.config, typ != nil)
|
|
typedesc.addSonSkipIntLit(typ, c.idgen)
|
|
let sym = newSym(skType, c.cache.idAnon, c.idgen, getCurrOwner(c), info,
|
|
c.config.options).linkTo(typedesc)
|
|
result = newSymNode(sym, info)
|
|
|
|
proc makeTypeFromExpr*(c: PContext, n: PNode): PType =
|
|
result = newTypeS(tyFromExpr, c)
|
|
assert n != nil
|
|
result.n = n
|
|
|
|
when false:
|
|
proc newTypeWithSons*(owner: PSym, kind: TTypeKind, sons: seq[PType];
|
|
idgen: IdGenerator): PType =
|
|
result = newType(kind, idgen, owner, sons = sons)
|
|
|
|
proc newTypeWithSons*(c: PContext, kind: TTypeKind,
|
|
sons: seq[PType]): PType =
|
|
result = newType(kind, c.idgen, getCurrOwner(c), sons = sons)
|
|
|
|
proc makeStaticExpr*(c: PContext, n: PNode): PNode =
|
|
result = newNodeI(nkStaticExpr, n.info)
|
|
result.sons = @[n]
|
|
result.typ = if n.typ != nil and n.typ.kind == tyStatic: n.typ
|
|
else: newTypeS(tyStatic, c, n.typ)
|
|
|
|
proc makeAndType*(c: PContext, t1, t2: PType): PType =
|
|
result = newTypeS(tyAnd, c)
|
|
result.rawAddSon t1
|
|
result.rawAddSon t2
|
|
propagateToOwner(result, t1)
|
|
propagateToOwner(result, t2)
|
|
result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
|
|
result.flags.incl tfHasMeta
|
|
|
|
proc makeOrType*(c: PContext, t1, t2: PType): PType =
|
|
if t1.kind != tyOr and t2.kind != tyOr:
|
|
result = newTypeS(tyOr, c)
|
|
result.rawAddSon t1
|
|
result.rawAddSon t2
|
|
else:
|
|
result = newTypeS(tyOr, c)
|
|
template addOr(t1) =
|
|
if t1.kind == tyOr:
|
|
for x in t1.kids: result.rawAddSon x
|
|
else:
|
|
result.rawAddSon t1
|
|
addOr(t1)
|
|
addOr(t2)
|
|
propagateToOwner(result, t1)
|
|
propagateToOwner(result, t2)
|
|
result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
|
|
result.flags.incl tfHasMeta
|
|
|
|
proc makeNotType*(c: PContext, t1: PType): PType =
|
|
result = newTypeS(tyNot, c, son = t1)
|
|
propagateToOwner(result, t1)
|
|
result.flags.incl(t1.flags * {tfHasStatic})
|
|
result.flags.incl tfHasMeta
|
|
|
|
proc nMinusOne(c: PContext; n: PNode): PNode =
|
|
result = newTreeI(nkCall, n.info, newSymNode(getSysMagic(c.graph, n.info, "pred", mPred)), n)
|
|
|
|
# Remember to fix the procs below this one when you make changes!
|
|
proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType =
|
|
let intType = getSysType(c.graph, n.info, tyInt)
|
|
result = newTypeS(tyRange, c, son = intType)
|
|
if n.typ != nil and n.typ.n == nil:
|
|
result.flags.incl tfUnresolved
|
|
result.n = newTreeI(nkRange, n.info, newIntTypeNode(0, intType),
|
|
makeStaticExpr(c, nMinusOne(c, n)))
|
|
|
|
template rangeHasUnresolvedStatic*(t: PType): bool =
|
|
tfUnresolved in t.flags
|
|
|
|
proc errorType*(c: PContext): PType =
|
|
## creates a type representing an error state
|
|
result = newTypeS(tyError, c)
|
|
result.flags.incl tfCheckedForDestructor
|
|
|
|
proc errorNode*(c: PContext, n: PNode): PNode =
|
|
result = newNodeI(nkEmpty, n.info)
|
|
result.typ = errorType(c)
|
|
|
|
# These mimic localError
|
|
template localErrorNode*(c: PContext, n: PNode, info: TLineInfo, msg: TMsgKind, arg: string): PNode =
|
|
liMessage(c.config, info, msg, arg, doNothing, instLoc())
|
|
errorNode(c, n)
|
|
|
|
template localErrorNode*(c: PContext, n: PNode, info: TLineInfo, arg: string): PNode =
|
|
liMessage(c.config, info, errGenerated, arg, doNothing, instLoc())
|
|
errorNode(c, n)
|
|
|
|
template localErrorNode*(c: PContext, n: PNode, msg: TMsgKind, arg: string): PNode =
|
|
let n2 = n
|
|
liMessage(c.config, n2.info, msg, arg, doNothing, instLoc())
|
|
errorNode(c, n2)
|
|
|
|
template localErrorNode*(c: PContext, n: PNode, arg: string): PNode =
|
|
let n2 = n
|
|
liMessage(c.config, n2.info, errGenerated, arg, doNothing, instLoc())
|
|
errorNode(c, n2)
|
|
|
|
proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) =
|
|
dest.kind = kind
|
|
dest.owner = getCurrOwner(c)
|
|
dest.size = - 1
|
|
|
|
proc makeRangeType*(c: PContext; first, last: BiggestInt;
|
|
info: TLineInfo; intType: PType = nil): PType =
|
|
let intType = if intType != nil: intType else: getSysType(c.graph, info, tyInt)
|
|
var n = newNodeI(nkRange, info)
|
|
n.add newIntTypeNode(first, intType)
|
|
n.add newIntTypeNode(last, intType)
|
|
result = newTypeS(tyRange, c)
|
|
result.n = n
|
|
addSonSkipIntLit(result, intType, c.idgen) # basetype of range
|
|
|
|
proc isSelf*(t: PType): bool {.inline.} =
|
|
## Is this the magical 'Self' type from concepts?
|
|
t.kind == tyTypeDesc and tfPacked in t.flags
|
|
|
|
proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
|
if typ.kind == tyTypeDesc and not isSelf(typ):
|
|
result = typ
|
|
else:
|
|
result = newTypeS(tyTypeDesc, c, skipIntLit(typ, c.idgen))
|
|
incl result.flags, tfCheckedForDestructor
|
|
|
|
proc symFromType*(c: PContext; t: PType, info: TLineInfo): PSym =
|
|
if t.sym != nil: return t.sym
|
|
result = newSym(skType, getIdent(c.cache, "AnonType"), c.idgen, t.owner, info)
|
|
result.flags.incl sfAnon
|
|
result.typ = t
|
|
|
|
proc symNodeFromType*(c: PContext, t: PType, info: TLineInfo): PNode =
|
|
result = newSymNode(symFromType(c, t, info), info)
|
|
result.typ = makeTypeDesc(c, t)
|
|
|
|
proc markIndirect*(c: PContext, s: PSym) {.inline.} =
|
|
if s.kind in {skProc, skFunc, skConverter, skMethod, skIterator}:
|
|
incl(s.flags, sfAddrTaken)
|
|
# XXX add to 'c' for global analysis
|
|
|
|
proc illFormedAst*(n: PNode; conf: ConfigRef) =
|
|
globalError(conf, n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
|
|
|
|
proc illFormedAstLocal*(n: PNode; conf: ConfigRef) =
|
|
localError(conf, n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
|
|
|
|
proc checkSonsLen*(n: PNode, length: int; conf: ConfigRef) =
|
|
if n.len != length: illFormedAst(n, conf)
|
|
|
|
proc checkMinSonsLen*(n: PNode, length: int; conf: ConfigRef) =
|
|
if n.len < length: illFormedAst(n, conf)
|
|
|
|
proc isTopLevel*(c: PContext): bool {.inline.} =
|
|
result = c.currentScope.depthLevel <= 2
|
|
|
|
proc isTopLevelInsideDeclaration*(c: PContext, sym: PSym): bool {.inline.} =
|
|
# for routeKinds the scope isn't closed yet:
|
|
c.currentScope.depthLevel <= 2 + ord(sym.kind in routineKinds)
|
|
|
|
proc pushCaseContext*(c: PContext, caseNode: PNode) =
|
|
c.p.caseContext.add((caseNode, 0))
|
|
|
|
proc popCaseContext*(c: PContext) =
|
|
discard pop(c.p.caseContext)
|
|
|
|
proc setCaseContextIdx*(c: PContext, idx: int) =
|
|
c.p.caseContext[^1].idx = idx
|
|
|
|
template addExport*(c: PContext; s: PSym) =
|
|
## convenience to export a symbol from the current module
|
|
addExport(c.graph, c.module, s)
|
|
|
|
proc storeRodNode*(c: PContext, n: PNode) =
|
|
if c.config.symbolFiles != disabledSf:
|
|
toPackedNodeTopLevel(n, c.encoder, c.packedRepr)
|
|
|
|
proc addToGenericProcCache*(c: PContext; s: PSym; inst: PInstantiation) =
|
|
c.graph.procInstCache.mgetOrPut(s.itemId, @[]).add LazyInstantiation(module: c.module.position, inst: inst)
|
|
if c.config.symbolFiles != disabledSf:
|
|
storeInstantiation(c.encoder, c.packedRepr, s, inst)
|
|
|
|
proc addToGenericCache*(c: PContext; s: PSym; inst: PType) =
|
|
c.graph.typeInstCache.mgetOrPut(s.itemId, @[]).add LazyType(typ: inst)
|
|
if c.config.symbolFiles != disabledSf:
|
|
storeTypeInst(c.encoder, c.packedRepr, s, inst)
|
|
|
|
proc sealRodFile*(c: PContext) =
|
|
if c.config.symbolFiles != disabledSf:
|
|
if c.graph.vm != nil:
|
|
for (m, n) in PCtx(c.graph.vm).vmstateDiff:
|
|
if m == c.module:
|
|
addPragmaComputation(c, n)
|
|
c.idgen.sealed = true # no further additions are allowed
|
|
|
|
proc rememberExpansion*(c: PContext; info: TLineInfo; expandedSym: PSym) =
|
|
## Templates and macros are very special in Nim; these have
|
|
## inlining semantics so after semantic checking they leave no trace
|
|
## in the sem'checked AST. This is very bad for IDE-like tooling
|
|
## ("find all usages of this template" would not work). We need special
|
|
## logic to remember macro/template expansions. This is done here and
|
|
## delegated to the "rod" file mechanism.
|
|
if c.config.symbolFiles != disabledSf:
|
|
storeExpansion(c.encoder, c.packedRepr, info, expandedSym)
|