mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
2010 lines
75 KiB
Nim
2010 lines
75 KiB
Nim
#
|
|
#
|
|
# The Nim Compiler
|
|
# (c) Copyright 2015 Andreas Rumpf
|
|
#
|
|
# See the file "copying.txt", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
# abstract syntax tree + symbol table
|
|
|
|
import
|
|
lineinfos, hashes, options, ropes, idents, int128
|
|
from strutils import toLowerAscii
|
|
|
|
export int128
|
|
|
|
type
|
|
TCallingConvention* = enum
|
|
ccNimCall = "nimcall" # nimcall, also the default
|
|
ccStdCall = "stdcall" # procedure is stdcall
|
|
ccCDecl = "cdecl" # cdecl
|
|
ccSafeCall = "safecall" # safecall
|
|
ccSysCall = "syscall" # system call
|
|
ccInline = "inline" # proc should be inlined
|
|
ccNoInline = "noinline" # proc should not be inlined
|
|
ccFastCall = "fastcall" # fastcall (pass parameters in registers)
|
|
ccThisCall = "thiscall" # thiscall (parameters are pushed right-to-left)
|
|
ccClosure = "closure" # proc has a closure
|
|
ccNoConvention = "noconv" # needed for generating proper C procs sometimes
|
|
|
|
type
|
|
TNodeKind* = enum # order is extremely important, because ranges are used
|
|
# to check whether a node belongs to a certain class
|
|
nkNone, # unknown node kind: indicates an error
|
|
# Expressions:
|
|
# Atoms:
|
|
nkEmpty, # the node is empty
|
|
nkIdent, # node is an identifier
|
|
nkSym, # node is a symbol
|
|
nkType, # node is used for its typ field
|
|
|
|
nkCharLit, # a character literal ''
|
|
nkIntLit, # an integer literal
|
|
nkInt8Lit,
|
|
nkInt16Lit,
|
|
nkInt32Lit,
|
|
nkInt64Lit,
|
|
nkUIntLit, # an unsigned integer literal
|
|
nkUInt8Lit,
|
|
nkUInt16Lit,
|
|
nkUInt32Lit,
|
|
nkUInt64Lit,
|
|
nkFloatLit, # a floating point literal
|
|
nkFloat32Lit,
|
|
nkFloat64Lit,
|
|
nkFloat128Lit,
|
|
nkStrLit, # a string literal ""
|
|
nkRStrLit, # a raw string literal r""
|
|
nkTripleStrLit, # a triple string literal """
|
|
nkNilLit, # the nil literal
|
|
# end of atoms
|
|
nkComesFrom, # "comes from" template/macro information for
|
|
# better stack trace generation
|
|
nkDotCall, # used to temporarily flag a nkCall node;
|
|
# this is used
|
|
# for transforming ``s.len`` to ``len(s)``
|
|
|
|
nkCommand, # a call like ``p 2, 4`` without parenthesis
|
|
nkCall, # a call like p(x, y) or an operation like +(a, b)
|
|
nkCallStrLit, # a call with a string literal
|
|
# x"abc" has two sons: nkIdent, nkRStrLit
|
|
# x"""abc""" has two sons: nkIdent, nkTripleStrLit
|
|
nkInfix, # a call like (a + b)
|
|
nkPrefix, # a call like !a
|
|
nkPostfix, # something like a! (also used for visibility)
|
|
nkHiddenCallConv, # an implicit type conversion via a type converter
|
|
|
|
nkExprEqExpr, # a named parameter with equals: ''expr = expr''
|
|
nkExprColonExpr, # a named parameter with colon: ''expr: expr''
|
|
nkIdentDefs, # a definition like `a, b: typeDesc = expr`
|
|
# either typeDesc or expr may be nil; used in
|
|
# formal parameters, var statements, etc.
|
|
nkVarTuple, # a ``var (a, b) = expr`` construct
|
|
nkPar, # syntactic (); may be a tuple constructor
|
|
nkObjConstr, # object constructor: T(a: 1, b: 2)
|
|
nkCurly, # syntactic {}
|
|
nkCurlyExpr, # an expression like a{i}
|
|
nkBracket, # syntactic []
|
|
nkBracketExpr, # an expression like a[i..j, k]
|
|
nkPragmaExpr, # an expression like a{.pragmas.}
|
|
nkRange, # an expression like i..j
|
|
nkDotExpr, # a.b
|
|
nkCheckedFieldExpr, # a.b, but b is a field that needs to be checked
|
|
nkDerefExpr, # a^
|
|
nkIfExpr, # if as an expression
|
|
nkElifExpr,
|
|
nkElseExpr,
|
|
nkLambda, # lambda expression
|
|
nkDo, # lambda block appering as trailing proc param
|
|
nkAccQuoted, # `a` as a node
|
|
|
|
nkTableConstr, # a table constructor {expr: expr}
|
|
nkBind, # ``bind expr`` node
|
|
nkClosedSymChoice, # symbol choice node; a list of nkSyms (closed)
|
|
nkOpenSymChoice, # symbol choice node; a list of nkSyms (open)
|
|
nkHiddenStdConv, # an implicit standard type conversion
|
|
nkHiddenSubConv, # an implicit type conversion from a subtype
|
|
# to a supertype
|
|
nkConv, # a type conversion
|
|
nkCast, # a type cast
|
|
nkStaticExpr, # a static expr
|
|
nkAddr, # a addr expression
|
|
nkHiddenAddr, # implicit address operator
|
|
nkHiddenDeref, # implicit ^ operator
|
|
nkObjDownConv, # down conversion between object types
|
|
nkObjUpConv, # up conversion between object types
|
|
nkChckRangeF, # range check for floats
|
|
nkChckRange64, # range check for 64 bit ints
|
|
nkChckRange, # range check for ints
|
|
nkStringToCString, # string to cstring
|
|
nkCStringToString, # cstring to string
|
|
# end of expressions
|
|
|
|
nkAsgn, # a = b
|
|
nkFastAsgn, # internal node for a fast ``a = b``
|
|
# (no string copy)
|
|
nkGenericParams, # generic parameters
|
|
nkFormalParams, # formal parameters
|
|
nkOfInherit, # inherited from symbol
|
|
|
|
nkImportAs, # a 'as' b in an import statement
|
|
nkProcDef, # a proc
|
|
nkMethodDef, # a method
|
|
nkConverterDef, # a converter
|
|
nkMacroDef, # a macro
|
|
nkTemplateDef, # a template
|
|
nkIteratorDef, # an iterator
|
|
|
|
nkOfBranch, # used inside case statements
|
|
# for (cond, action)-pairs
|
|
nkElifBranch, # used in if statements
|
|
nkExceptBranch, # an except section
|
|
nkElse, # an else part
|
|
nkAsmStmt, # an assembler block
|
|
nkPragma, # a pragma statement
|
|
nkPragmaBlock, # a pragma with a block
|
|
nkIfStmt, # an if statement
|
|
nkWhenStmt, # a when expression or statement
|
|
nkForStmt, # a for statement
|
|
nkParForStmt, # a parallel for statement
|
|
nkWhileStmt, # a while statement
|
|
nkCaseStmt, # a case statement
|
|
nkTypeSection, # a type section (consists of type definitions)
|
|
nkVarSection, # a var section
|
|
nkLetSection, # a let section
|
|
nkConstSection, # a const section
|
|
nkConstDef, # a const definition
|
|
nkTypeDef, # a type definition
|
|
nkYieldStmt, # the yield statement as a tree
|
|
nkDefer, # the 'defer' statement
|
|
nkTryStmt, # a try statement
|
|
nkFinally, # a finally section
|
|
nkRaiseStmt, # a raise statement
|
|
nkReturnStmt, # a return statement
|
|
nkBreakStmt, # a break statement
|
|
nkContinueStmt, # a continue statement
|
|
nkBlockStmt, # a block statement
|
|
nkStaticStmt, # a static statement
|
|
nkDiscardStmt, # a discard statement
|
|
nkStmtList, # a list of statements
|
|
nkImportStmt, # an import statement
|
|
nkImportExceptStmt, # an import x except a statement
|
|
nkExportStmt, # an export statement
|
|
nkExportExceptStmt, # an 'export except' statement
|
|
nkFromStmt, # a from * import statement
|
|
nkIncludeStmt, # an include statement
|
|
nkBindStmt, # a bind statement
|
|
nkMixinStmt, # a mixin statement
|
|
nkUsingStmt, # an using statement
|
|
nkCommentStmt, # a comment statement
|
|
nkStmtListExpr, # a statement list followed by an expr; this is used
|
|
# to allow powerful multi-line templates
|
|
nkBlockExpr, # a statement block ending in an expr; this is used
|
|
# to allow powerful multi-line templates that open a
|
|
# temporary scope
|
|
nkStmtListType, # a statement list ending in a type; for macros
|
|
nkBlockType, # a statement block ending in a type; for macros
|
|
# types as syntactic trees:
|
|
|
|
nkWith, # distinct with `foo`
|
|
nkWithout, # distinct without `foo`
|
|
|
|
nkTypeOfExpr, # type(1+2)
|
|
nkObjectTy, # object body
|
|
nkTupleTy, # tuple body
|
|
nkTupleClassTy, # tuple type class
|
|
nkTypeClassTy, # user-defined type class
|
|
nkStaticTy, # ``static[T]``
|
|
nkRecList, # list of object parts
|
|
nkRecCase, # case section of object
|
|
nkRecWhen, # when section of object
|
|
nkRefTy, # ``ref T``
|
|
nkPtrTy, # ``ptr T``
|
|
nkVarTy, # ``var T``
|
|
nkConstTy, # ``const T``
|
|
nkMutableTy, # ``mutable T``
|
|
nkDistinctTy, # distinct type
|
|
nkProcTy, # proc type
|
|
nkIteratorTy, # iterator type
|
|
nkSharedTy, # 'shared T'
|
|
# we use 'nkPostFix' for the 'not nil' addition
|
|
nkEnumTy, # enum body
|
|
nkEnumFieldDef, # `ident = expr` in an enumeration
|
|
nkArgList, # argument list
|
|
nkPattern, # a special pattern; used for matching
|
|
nkHiddenTryStmt, # a hidden try statement
|
|
nkClosure, # (prc, env)-pair (internally used for code gen)
|
|
nkGotoState, # used for the state machine (for iterators)
|
|
nkState, # give a label to a code section (for iterators)
|
|
nkBreakState, # special break statement for easier code generation
|
|
nkFuncDef, # a func
|
|
nkTupleConstr # a tuple constructor
|
|
nkError # erroneous AST node
|
|
nkModuleRef # for .rod file support: A (moduleId, itemId) pair
|
|
nkReplayAction # for .rod file support: A replay action
|
|
nkNilRodNode # for .rod file support: a 'nil' PNode
|
|
|
|
TNodeKinds* = set[TNodeKind]
|
|
|
|
type
|
|
TSymFlag* = enum # 46 flags!
|
|
sfUsed, # read access of sym (for warnings) or simply used
|
|
sfExported, # symbol is exported from module
|
|
sfFromGeneric, # symbol is instantiation of a generic; this is needed
|
|
# for symbol file generation; such symbols should always
|
|
# be written into the ROD file
|
|
sfGlobal, # symbol is at global scope
|
|
|
|
sfForward, # symbol is forward declared
|
|
sfWasForwarded, # symbol had a forward declaration
|
|
# (implies it's too dangerous to patch its type signature)
|
|
sfImportc, # symbol is external; imported
|
|
sfExportc, # symbol is exported (under a specified name)
|
|
sfMangleCpp, # mangle as cpp (combines with `sfExportc`)
|
|
sfVolatile, # variable is volatile
|
|
sfRegister, # variable should be placed in a register
|
|
sfPure, # object is "pure" that means it has no type-information
|
|
# enum is "pure", its values need qualified access
|
|
# variable is "pure"; it's an explicit "global"
|
|
sfNoSideEffect, # proc has no side effects
|
|
sfSideEffect, # proc may have side effects; cannot prove it has none
|
|
sfMainModule, # module is the main module
|
|
sfSystemModule, # module is the system module
|
|
sfNoReturn, # proc never returns (an exit proc)
|
|
sfAddrTaken, # the variable's address is taken (ex- or implicitly);
|
|
# *OR*: a proc is indirectly called (used as first class)
|
|
sfCompilerProc, # proc is a compiler proc, that is a C proc that is
|
|
# needed for the code generator
|
|
sfProcvar, # proc can be passed to a proc var
|
|
sfDiscriminant, # field is a discriminant in a record/object
|
|
sfRequiresInit, # field must be initialized during construction
|
|
sfDeprecated, # symbol is deprecated
|
|
sfExplain, # provide more diagnostics when this symbol is used
|
|
sfError, # usage of symbol should trigger a compile-time error
|
|
sfShadowed, # a symbol that was shadowed in some inner scope
|
|
sfThread, # proc will run as a thread
|
|
# variable is a thread variable
|
|
sfCppNonPod, # tells compiler to treat such types as non-pod's, so that
|
|
# `thread_local` is used instead of `__thread` for
|
|
# {.threadvar.} + `--threads`. Only makes sense for importcpp types.
|
|
# This has a performance impact so isn't set by default.
|
|
sfCompileTime, # proc can be evaluated at compile time
|
|
sfConstructor, # proc is a C++ constructor
|
|
sfDispatcher, # copied method symbol is the dispatcher
|
|
# deprecated and unused, except for the con
|
|
sfBorrow, # proc is borrowed
|
|
sfInfixCall, # symbol needs infix call syntax in target language;
|
|
# for interfacing with C++, JS
|
|
sfNamedParamCall, # symbol needs named parameter call syntax in target
|
|
# language; for interfacing with Objective C
|
|
sfDiscardable, # returned value may be discarded implicitly
|
|
sfOverriden, # proc is overridden
|
|
sfCallsite # A flag for template symbols to tell the
|
|
# compiler it should use line information from
|
|
# the calling side of the macro, not from the
|
|
# implementation.
|
|
sfGenSym # symbol is 'gensym'ed; do not add to symbol table
|
|
sfNonReloadable # symbol will be left as-is when hot code reloading is on -
|
|
# meaning that it won't be renamed and/or changed in any way
|
|
sfGeneratedOp # proc is a generated '='; do not inject destructors in it
|
|
# variable is generated closure environment; requires early
|
|
# destruction for --newruntime.
|
|
sfTemplateParam # symbol is a template parameter
|
|
sfCursor # variable/field is a cursor, see RFC 177 for details
|
|
sfInjectDestructors # whether the proc needs the 'injectdestructors' transformation
|
|
sfNeverRaises # proc can never raise an exception, not even OverflowDefect
|
|
# or out-of-memory
|
|
sfUsedInFinallyOrExcept # symbol is used inside an 'except' or 'finally'
|
|
sfSingleUsedTemp # For temporaries that we know will only be used once
|
|
sfNoalias # 'noalias' annotation, means C's 'restrict'
|
|
|
|
TSymFlags* = set[TSymFlag]
|
|
|
|
const
|
|
sfNoInit* = sfMainModule # don't generate code to init the variable
|
|
|
|
sfAllUntyped* = sfVolatile # macro or template is immediately expanded \
|
|
# in a generic context
|
|
|
|
sfDirty* = sfPure
|
|
# template is not hygienic (old styled template)
|
|
# module, compiled from a dirty-buffer
|
|
|
|
sfAnon* = sfDiscardable
|
|
# symbol name that was generated by the compiler
|
|
# the compiler will avoid printing such names
|
|
# in user messages.
|
|
|
|
sfNoForward* = sfRegister
|
|
# forward declarations are not required (per module)
|
|
sfReorder* = sfForward
|
|
# reordering pass is enabled
|
|
|
|
sfCompileToCpp* = sfInfixCall # compile the module as C++ code
|
|
sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code
|
|
sfExperimental* = sfOverriden # module uses the .experimental switch
|
|
sfGoto* = sfOverriden # var is used for 'goto' code generation
|
|
sfWrittenTo* = sfBorrow # param is assigned to
|
|
sfEscapes* = sfProcvar # param escapes
|
|
sfBase* = sfDiscriminant
|
|
sfIsSelf* = sfOverriden # param is 'self'
|
|
sfCustomPragma* = sfRegister # symbol is custom pragma template
|
|
|
|
const
|
|
# getting ready for the future expr/stmt merge
|
|
nkWhen* = nkWhenStmt
|
|
nkWhenExpr* = nkWhenStmt
|
|
nkEffectList* = nkArgList
|
|
# hacks ahead: an nkEffectList is a node with 4 children:
|
|
exceptionEffects* = 0 # exceptions at position 0
|
|
requiresEffects* = 1 # 'requires' annotation
|
|
ensuresEffects* = 2 # 'ensures' annotation
|
|
tagEffects* = 3 # user defined tag ('gc', 'time' etc.)
|
|
pragmasEffects* = 4 # not an effect, but a slot for pragmas in proc type
|
|
effectListLen* = 5 # list of effects list
|
|
nkLastBlockStmts* = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
|
|
# these must be last statements in a block
|
|
|
|
type
|
|
TTypeKind* = enum # order is important!
|
|
# Don't forget to change hti.nim if you make a change here
|
|
# XXX put this into an include file to avoid this issue!
|
|
# several types are no longer used (guess which), but a
|
|
# spot in the sequence is kept for backwards compatibility
|
|
# (apparently something with bootstrapping)
|
|
# if you need to add a type, they can apparently be reused
|
|
tyNone, tyBool, tyChar,
|
|
tyEmpty, tyAlias, tyNil, tyUntyped, tyTyped, tyTypeDesc,
|
|
tyGenericInvocation, # ``T[a, b]`` for types to invoke
|
|
tyGenericBody, # ``T[a, b, body]`` last parameter is the body
|
|
tyGenericInst, # ``T[a, b, realInstance]`` instantiated generic type
|
|
# realInstance will be a concrete type like tyObject
|
|
# unless this is an instance of a generic alias type.
|
|
# then realInstance will be the tyGenericInst of the
|
|
# completely (recursively) resolved alias.
|
|
|
|
tyGenericParam, # ``a`` in the above patterns
|
|
tyDistinct,
|
|
tyEnum,
|
|
tyOrdinal, # integer types (including enums and boolean)
|
|
tyArray,
|
|
tyObject,
|
|
tyTuple,
|
|
tySet,
|
|
tyRange,
|
|
tyPtr, tyRef,
|
|
tyVar,
|
|
tySequence,
|
|
tyProc,
|
|
tyPointer, tyOpenArray,
|
|
tyString, tyCstring, tyForward,
|
|
tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers
|
|
tyFloat, tyFloat32, tyFloat64, tyFloat128,
|
|
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64,
|
|
tyOwned, tySink, tyLent,
|
|
tyVarargs,
|
|
tyUncheckedArray
|
|
# An array with boundaries [0,+∞]
|
|
|
|
tyProxy # used as errornous type (for idetools)
|
|
|
|
tyBuiltInTypeClass
|
|
# Type such as the catch-all object, tuple, seq, etc
|
|
|
|
tyUserTypeClass
|
|
# the body of a user-defined type class
|
|
|
|
tyUserTypeClassInst
|
|
# Instance of a parametric user-defined type class.
|
|
# Structured similarly to tyGenericInst.
|
|
# tyGenericInst represents concrete types, while
|
|
# this is still a "generic param" that will bind types
|
|
# and resolves them during sigmatch and instantiation.
|
|
|
|
tyCompositeTypeClass
|
|
# Type such as seq[Number]
|
|
# The notes for tyUserTypeClassInst apply here as well
|
|
# sons[0]: the original expression used by the user.
|
|
# sons[1]: fully expanded and instantiated meta type
|
|
# (potentially following aliases)
|
|
|
|
tyInferred
|
|
# In the initial state `base` stores a type class constraining
|
|
# the types that can be inferred. After a candidate type is
|
|
# selected, it's stored in `lastSon`. Between `base` and `lastSon`
|
|
# there may be 0, 2 or more types that were also considered as
|
|
# possible candidates in the inference process (i.e. lastSon will
|
|
# be updated to store a type best conforming to all candidates)
|
|
|
|
tyAnd, tyOr, tyNot
|
|
# boolean type classes such as `string|int`,`not seq`,
|
|
# `Sortable and Enumable`, etc
|
|
|
|
tyAnything
|
|
# a type class matching any type
|
|
|
|
tyStatic
|
|
# a value known at compile type (the underlying type is .base)
|
|
|
|
tyFromExpr
|
|
# This is a type representing an expression that depends
|
|
# on generic parameters (the expression is stored in t.n)
|
|
# It will be converted to a real type only during generic
|
|
# instantiation and prior to this it has the potential to
|
|
# be any type.
|
|
|
|
tyConcept
|
|
# new style concept.
|
|
|
|
tyVoid
|
|
# now different from tyEmpty, hurray!
|
|
tyIterable
|
|
|
|
static:
|
|
# remind us when TTypeKind stops to fit in a single 64-bit word
|
|
# assert TTypeKind.high.ord <= 63
|
|
discard
|
|
|
|
const
|
|
tyPureObject* = tyTuple
|
|
GcTypeKinds* = {tyRef, tySequence, tyString}
|
|
tyError* = tyProxy # as an errornous node should match everything
|
|
tyUnknown* = tyFromExpr
|
|
|
|
tyUnknownTypes* = {tyError, tyFromExpr}
|
|
|
|
tyTypeClasses* = {tyBuiltInTypeClass, tyCompositeTypeClass,
|
|
tyUserTypeClass, tyUserTypeClassInst,
|
|
tyAnd, tyOr, tyNot, tyAnything}
|
|
|
|
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyUntyped} + tyTypeClasses
|
|
tyUserTypeClasses* = {tyUserTypeClass, tyUserTypeClassInst}
|
|
# consider renaming as `tyAbstractVarRange`
|
|
abstractVarRange* = {tyGenericInst, tyRange, tyVar, tyDistinct, tyOrdinal,
|
|
tyTypeDesc, tyAlias, tyInferred, tySink, tyOwned}
|
|
|
|
type
|
|
TTypeKinds* = set[TTypeKind]
|
|
|
|
TNodeFlag* = enum
|
|
nfNone,
|
|
nfBase2, # nfBase10 is default, so not needed
|
|
nfBase8,
|
|
nfBase16,
|
|
nfAllConst, # used to mark complex expressions constant; easy to get rid of
|
|
# but unfortunately it has measurable impact for compilation
|
|
# efficiency
|
|
nfTransf, # node has been transformed
|
|
nfNoRewrite # node should not be transformed anymore
|
|
nfSem # node has been checked for semantics
|
|
nfLL # node has gone through lambda lifting
|
|
nfDotField # the call can use a dot operator
|
|
nfDotSetter # the call can use a setter dot operarator
|
|
nfExplicitCall # x.y() was used instead of x.y
|
|
nfExprCall # this is an attempt to call a regular expression
|
|
nfIsRef # this node is a 'ref' node; used for the VM
|
|
nfIsPtr # this node is a 'ptr' node; used for the VM
|
|
nfPreventCg # this node should be ignored by the codegen
|
|
nfBlockArg # this a stmtlist appearing in a call (e.g. a do block)
|
|
nfFromTemplate # a top-level node returned from a template
|
|
nfDefaultParam # an automatically inserter default parameter
|
|
nfDefaultRefsParam # a default param value references another parameter
|
|
# the flag is applied to proc default values and to calls
|
|
nfExecuteOnReload # A top-level statement that will be executed during reloads
|
|
nfLastRead # this node is a last read
|
|
nfFirstWrite# this node is a first write
|
|
|
|
TNodeFlags* = set[TNodeFlag]
|
|
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: ~40)
|
|
tfVarargs, # procedure has C styled varargs
|
|
# tyArray type represeting a varargs list
|
|
tfNoSideEffect, # procedure type does not allow side effects
|
|
tfFinal, # is the object final?
|
|
tfInheritable, # is the object inheritable?
|
|
tfHasOwned, # type contains an 'owned' type and must be moved
|
|
tfEnumHasHoles, # enum cannot be mapped into a range
|
|
tfShallow, # type can be shallow copied on assignment
|
|
tfThread, # proc type is marked as ``thread``; alias for ``gcsafe``
|
|
tfFromGeneric, # type is an instantiation of a generic; this is needed
|
|
# because for instantiations of objects, structural
|
|
# type equality has to be used
|
|
tfUnresolved, # marks unresolved typedesc/static params: e.g.
|
|
# proc foo(T: typedesc, list: seq[T]): var T
|
|
# proc foo(L: static[int]): array[L, int]
|
|
# can be attached to ranges to indicate that the range
|
|
# can be attached to generic procs with free standing
|
|
# type parameters: e.g. proc foo[T]()
|
|
# depends on unresolved static params.
|
|
tfResolved # marks a user type class, after it has been bound to a
|
|
# concrete type (lastSon becomes the concrete type)
|
|
tfRetType, # marks return types in proc (used to detect type classes
|
|
# used as return types for return type inference)
|
|
tfCapturesEnv, # whether proc really captures some environment
|
|
tfByCopy, # pass object/tuple by copy (C backend)
|
|
tfByRef, # pass object/tuple by reference (C backend)
|
|
tfIterator, # type is really an iterator, not a tyProc
|
|
tfPartial, # type is declared as 'partial'
|
|
tfNotNil, # type cannot be 'nil'
|
|
tfRequiresInit, # type constains a "not nil" constraint somewhere or
|
|
# a `requiresInit` field, so the default zero init
|
|
# is not appropriate
|
|
tfNeedsFullInit, # object type marked with {.requiresInit.}
|
|
# all fields must be initialized
|
|
tfVarIsPtr, # 'var' type is translated like 'ptr' even in C++ mode
|
|
tfHasMeta, # type contains "wildcard" sub-types such as generic params
|
|
# or other type classes
|
|
tfHasGCedMem, # type contains GC'ed memory
|
|
tfPacked
|
|
tfHasStatic
|
|
tfGenericTypeParam
|
|
tfImplicitTypeParam
|
|
tfInferrableStatic
|
|
tfConceptMatchedTypeSym
|
|
tfExplicit # for typedescs, marks types explicitly prefixed with the
|
|
# `type` operator (e.g. type int)
|
|
tfWildcard # consider a proc like foo[T, I](x: Type[T, I])
|
|
# T and I here can bind to both typedesc and static types
|
|
# before this is determined, we'll consider them to be a
|
|
# wildcard type.
|
|
tfHasAsgn # type has overloaded assignment operator
|
|
tfBorrowDot # distinct type borrows '.'
|
|
tfTriggersCompileTime # uses the NimNode type which make the proc
|
|
# implicitly '.compiletime'
|
|
tfRefsAnonObj # used for 'ref object' and 'ptr object'
|
|
tfCovariant # covariant generic param mimicking a ptr type
|
|
tfWeakCovariant # covariant generic param mimicking a seq/array type
|
|
tfContravariant # contravariant generic param
|
|
tfCheckedForDestructor # type was checked for having a destructor.
|
|
# If it has one, t.destructor is not nil.
|
|
tfAcyclic # object type was annotated as .acyclic
|
|
tfIncompleteStruct # treat this type as if it had sizeof(pointer)
|
|
tfCompleteStruct
|
|
# (for importc types); type is fully specified, allowing to compute
|
|
# sizeof, alignof, offsetof at CT
|
|
tfExplicitCallConv
|
|
|
|
TTypeFlags* = set[TTypeFlag]
|
|
|
|
TSymKind* = enum # the different symbols (start with the prefix sk);
|
|
# order is important for the documentation generator!
|
|
skUnknown, # unknown symbol: used for parsing assembler blocks
|
|
# and first phase symbol lookup in generics
|
|
skConditional, # symbol for the preprocessor (may become obsolete)
|
|
skDynLib, # symbol represents a dynamic library; this is used
|
|
# internally; it does not exist in Nim code
|
|
skParam, # a parameter
|
|
skGenericParam, # a generic parameter; eq in ``proc x[eq=`==`]()``
|
|
skTemp, # a temporary variable (introduced by compiler)
|
|
skModule, # module identifier
|
|
skType, # a type
|
|
skVar, # a variable
|
|
skLet, # a 'let' symbol
|
|
skConst, # a constant
|
|
skResult, # special 'result' variable
|
|
skProc, # a proc
|
|
skFunc, # a func
|
|
skMethod, # a method
|
|
skIterator, # an iterator
|
|
skConverter, # a type converter
|
|
skMacro, # a macro
|
|
skTemplate, # a template; currently also misused for user-defined
|
|
# pragmas
|
|
skField, # a field in a record or object
|
|
skEnumField, # an identifier in an enum
|
|
skForVar, # a for loop variable
|
|
skLabel, # a label (for block statement)
|
|
skStub, # symbol is a stub and not yet loaded from the ROD
|
|
# file (it is loaded on demand, which may
|
|
# mean: never)
|
|
skPackage, # symbol is a package (used for canonicalization)
|
|
skAlias # an alias (needs to be resolved immediately)
|
|
TSymKinds* = set[TSymKind]
|
|
|
|
const
|
|
routineKinds* = {skProc, skFunc, skMethod, skIterator,
|
|
skConverter, skMacro, skTemplate}
|
|
ExportableSymKinds* = {skVar, skLet, skConst, skType, skEnumField, skStub, skAlias} + routineKinds
|
|
|
|
tfUnion* = tfNoSideEffect
|
|
tfGcSafe* = tfThread
|
|
tfObjHasKids* = tfEnumHasHoles
|
|
tfReturnsNew* = tfInheritable
|
|
skError* = skUnknown
|
|
|
|
var
|
|
eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect}
|
|
## type flags that are essential for type equality.
|
|
## This is now a variable because for emulation of version:1.0 we
|
|
## might exclude {tfGcSafe, tfNoSideEffect}.
|
|
|
|
type
|
|
TMagic* = enum # symbols that require compiler magic:
|
|
mNone,
|
|
mDefined, mDeclared, mDeclaredInScope, mCompiles, mArrGet, mArrPut, mAsgn,
|
|
mLow, mHigh, mSizeOf, mAlignOf, mOffsetOf, mTypeTrait,
|
|
mIs, mOf, mAddr, mType, mTypeOf,
|
|
mPlugin, mEcho, mShallowCopy, mSlurp, mStaticExec, mStatic,
|
|
mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
|
|
mInc, mDec, mOrd,
|
|
mNew, mNewFinalize, mNewSeq, mNewSeqOfCap,
|
|
mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq,
|
|
mIncl, mExcl, mCard, mChr,
|
|
mGCref, mGCunref,
|
|
mAddI, mSubI, mMulI, mDivI, mModI,
|
|
mSucc, mPred,
|
|
mAddF64, mSubF64, mMulF64, mDivF64,
|
|
mShrI, mShlI, mAshrI, mBitandI, mBitorI, mBitxorI,
|
|
mMinI, mMaxI,
|
|
mAddU, mSubU, mMulU, mDivU, mModU,
|
|
mEqI, mLeI, mLtI,
|
|
mEqF64, mLeF64, mLtF64,
|
|
mLeU, mLtU,
|
|
mEqEnum, mLeEnum, mLtEnum,
|
|
mEqCh, mLeCh, mLtCh,
|
|
mEqB, mLeB, mLtB,
|
|
mEqRef, mLePtr, mLtPtr,
|
|
mXor, mEqCString, mEqProc,
|
|
mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot,
|
|
mUnaryPlusI, mBitnotI,
|
|
mUnaryPlusF64, mUnaryMinusF64,
|
|
mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr,
|
|
mStrToStr, mEnumToStr,
|
|
mAnd, mOr,
|
|
mImplies, mIff, mExists, mForall, mOld,
|
|
mEqStr, mLeStr, mLtStr,
|
|
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
|
|
mConStrStr, mSlice,
|
|
mDotDot, # this one is only necessary to give nice compile time warnings
|
|
mFields, mFieldPairs, mOmpParFor,
|
|
mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
|
mInSet, mRepr, mExit,
|
|
mSetLengthStr, mSetLengthSeq,
|
|
mIsPartOf, mAstToStr, mParallel,
|
|
mSwap, mIsNil, mArrToSeq,
|
|
mNewString, mNewStringOfCap, mParseBiggestFloat,
|
|
mMove, mWasMoved, mDestroy,
|
|
mDefault, mUnown, mIsolate, mAccessEnv, mReset,
|
|
mArray, mOpenArray, mRange, mSet, mSeq, mVarargs,
|
|
mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
|
|
mOrdinal, mIterableType,
|
|
mInt, mInt8, mInt16, mInt32, mInt64,
|
|
mUInt, mUInt8, mUInt16, mUInt32, mUInt64,
|
|
mFloat, mFloat32, mFloat64, mFloat128,
|
|
mBool, mChar, mString, mCstring,
|
|
mPointer, mNil, mExpr, mStmt, mTypeDesc,
|
|
mVoidType, mPNimrodNode, mSpawn, mDeepCopy,
|
|
mIsMainModule, mCompileDate, mCompileTime, mProcCall,
|
|
mCpuEndian, mHostOS, mHostCPU, mBuildOS, mBuildCPU, mAppType,
|
|
mCompileOption, mCompileOptionArg,
|
|
mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel,
|
|
mNKind, mNSymKind,
|
|
|
|
mNccValue, mNccInc, mNcsAdd, mNcsIncl, mNcsLen, mNcsAt,
|
|
mNctPut, mNctLen, mNctGet, mNctHasNext, mNctNext,
|
|
|
|
mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal,
|
|
mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo,
|
|
mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mNSigHash, mNSizeOf,
|
|
mNBindSym, mNCallSite,
|
|
mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNGenSym,
|
|
mNHint, mNWarning, mNError,
|
|
mInstantiationInfo, mGetTypeInfo, mGetTypeInfoV2,
|
|
mNimvm, mIntDefine, mStrDefine, mBoolDefine, mRunnableExamples,
|
|
mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf,
|
|
mSymIsInstantiationOf, mNodeId, mPrivateAccess
|
|
|
|
|
|
# things that we can evaluate safely at compile time, even if not asked for it:
|
|
const
|
|
ctfeWhitelist* = {mNone, mSucc,
|
|
mPred, mInc, mDec, mOrd, mLengthOpenArray,
|
|
mLengthStr, mLengthArray, mLengthSeq,
|
|
mArrGet, mArrPut, mAsgn, mDestroy,
|
|
mIncl, mExcl, mCard, mChr,
|
|
mAddI, mSubI, mMulI, mDivI, mModI,
|
|
mAddF64, mSubF64, mMulF64, mDivF64,
|
|
mShrI, mShlI, mBitandI, mBitorI, mBitxorI,
|
|
mMinI, mMaxI,
|
|
mAddU, mSubU, mMulU, mDivU, mModU,
|
|
mEqI, mLeI, mLtI,
|
|
mEqF64, mLeF64, mLtF64,
|
|
mLeU, mLtU,
|
|
mEqEnum, mLeEnum, mLtEnum,
|
|
mEqCh, mLeCh, mLtCh,
|
|
mEqB, mLeB, mLtB,
|
|
mEqRef, mEqProc, mLePtr, mLtPtr, mEqCString, mXor,
|
|
mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI,
|
|
mUnaryPlusF64, mUnaryMinusF64,
|
|
mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr,
|
|
mStrToStr, mEnumToStr,
|
|
mAnd, mOr,
|
|
mEqStr, mLeStr, mLtStr,
|
|
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
|
|
mConStrStr, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
|
mInSet, mRepr}
|
|
|
|
type
|
|
ItemId* = object
|
|
module*: int32
|
|
item*: int32
|
|
|
|
proc `==`*(a, b: ItemId): bool {.inline.} =
|
|
a.item == b.item and a.module == b.module
|
|
|
|
proc hash*(x: ItemId): Hash =
|
|
var h: Hash = hash(x.module)
|
|
h = h !& hash(x.item)
|
|
result = !$h
|
|
|
|
|
|
type
|
|
TIdObj* = object of RootObj
|
|
itemId*: ItemId
|
|
PIdObj* = ref TIdObj
|
|
|
|
PNode* = ref TNode
|
|
TNodeSeq* = seq[PNode]
|
|
PType* = ref TType
|
|
PSym* = ref TSym
|
|
TNode*{.final, acyclic.} = object # on a 32bit machine, this takes 32 bytes
|
|
when defined(useNodeIds):
|
|
id*: int
|
|
typ*: PType
|
|
info*: TLineInfo
|
|
flags*: TNodeFlags
|
|
case kind*: TNodeKind
|
|
of nkCharLit..nkUInt64Lit:
|
|
intVal*: BiggestInt
|
|
of nkFloatLit..nkFloat128Lit:
|
|
floatVal*: BiggestFloat
|
|
of nkStrLit..nkTripleStrLit:
|
|
strVal*: string
|
|
of nkSym:
|
|
sym*: PSym
|
|
of nkIdent:
|
|
ident*: PIdent
|
|
else:
|
|
sons*: TNodeSeq
|
|
comment*: string
|
|
|
|
TStrTable* = object # a table[PIdent] of PSym
|
|
counter*: int
|
|
data*: seq[PSym]
|
|
|
|
# -------------- backend information -------------------------------
|
|
TLocKind* = enum
|
|
locNone, # no location
|
|
locTemp, # temporary location
|
|
locLocalVar, # location is a local variable
|
|
locGlobalVar, # location is a global variable
|
|
locParam, # location is a parameter
|
|
locField, # location is a record field
|
|
locExpr, # "location" is really an expression
|
|
locProc, # location is a proc (an address of a procedure)
|
|
locData, # location is a constant
|
|
locCall, # location is a call expression
|
|
locOther # location is something other
|
|
TLocFlag* = enum
|
|
lfIndirect, # backend introduced a pointer
|
|
lfFullExternalName, # only used when 'conf.cmd == cmdNimfix': Indicates
|
|
# that the symbol has been imported via 'importc: "fullname"' and
|
|
# no format string.
|
|
lfNoDeepCopy, # no need for a deep copy
|
|
lfNoDecl, # do not declare it in C
|
|
lfDynamicLib, # link symbol to dynamic library
|
|
lfExportLib, # export symbol for dynamic library generation
|
|
lfHeader, # include header file for symbol
|
|
lfImportCompilerProc, # ``importc`` of a compilerproc
|
|
lfSingleUse # no location yet and will only be used once
|
|
lfEnforceDeref # a copyMem is required to dereference if this a
|
|
# ptr array due to C array limitations.
|
|
# See #1181, #6422, #11171
|
|
lfPrepareForMutation # string location is about to be mutated (V2)
|
|
TStorageLoc* = enum
|
|
OnUnknown, # location is unknown (stack, heap or static)
|
|
OnStatic, # in a static section
|
|
OnStack, # location is on hardware stack
|
|
OnHeap # location is on heap or global
|
|
# (reference counting needed)
|
|
TLocFlags* = set[TLocFlag]
|
|
TLoc* = object
|
|
k*: TLocKind # kind of location
|
|
storage*: TStorageLoc
|
|
flags*: TLocFlags # location's flags
|
|
lode*: PNode # Node where the location came from; can be faked
|
|
r*: Rope # rope value of location (code generators)
|
|
|
|
# ---------------- end of backend information ------------------------------
|
|
|
|
TLibKind* = enum
|
|
libHeader, libDynamic
|
|
|
|
TLib* = object # also misused for headers!
|
|
# keep in sync with PackedLib
|
|
kind*: TLibKind
|
|
generated*: bool # needed for the backends:
|
|
isOverriden*: bool
|
|
name*: Rope
|
|
path*: PNode # can be a string literal!
|
|
|
|
|
|
CompilesId* = int ## id that is used for the caching logic within
|
|
## ``system.compiles``. See the seminst module.
|
|
TInstantiation* = object
|
|
sym*: PSym
|
|
concreteTypes*: seq[PType]
|
|
compilesId*: CompilesId
|
|
|
|
PInstantiation* = ref TInstantiation
|
|
|
|
TScope* = object
|
|
depthLevel*: int
|
|
symbols*: TStrTable
|
|
parent*: PScope
|
|
allowPrivateAccess*: seq[PSym] # # enable access to private fields
|
|
|
|
PScope* = ref TScope
|
|
|
|
PLib* = ref TLib
|
|
TSym* {.acyclic.} = object of TIdObj # Keep in sync with PackedSym
|
|
# proc and type instantiations are cached in the generic symbol
|
|
case kind*: TSymKind
|
|
of routineKinds:
|
|
#procInstCache*: seq[PInstantiation]
|
|
gcUnsafetyReason*: PSym # for better error messages wrt gcsafe
|
|
transformedBody*: PNode # cached body after transf pass
|
|
of skLet, skVar, skField, skForVar:
|
|
guard*: PSym
|
|
bitsize*: int
|
|
alignment*: int # for alignment
|
|
else: nil
|
|
magic*: TMagic
|
|
typ*: PType
|
|
name*: PIdent
|
|
info*: TLineInfo
|
|
owner*: PSym
|
|
flags*: TSymFlags
|
|
ast*: PNode # syntax tree of proc, iterator, etc.:
|
|
# the whole proc including header; this is used
|
|
# for easy generation of proper error messages
|
|
# for variant record fields the discriminant
|
|
# expression
|
|
# for modules, it's a placeholder for compiler
|
|
# generated code that will be appended to the
|
|
# module after the sem pass (see appendToModule)
|
|
options*: TOptions
|
|
position*: int # used for many different things:
|
|
# for enum fields its position;
|
|
# for fields its offset
|
|
# for parameters its position (starting with 0)
|
|
# for a conditional:
|
|
# 1 iff the symbol is defined, else 0
|
|
# (or not in symbol table)
|
|
# for modules, an unique index corresponding
|
|
# to the module's fileIdx
|
|
# for variables a slot index for the evaluator
|
|
offset*: int # offset of record field
|
|
loc*: TLoc
|
|
annex*: PLib # additional fields (seldom used, so we use a
|
|
# reference to another object to save space)
|
|
when hasFFI:
|
|
cname*: string # resolved C declaration name in importc decl, e.g.:
|
|
# proc fun() {.importc: "$1aux".} => cname = funaux
|
|
constraint*: PNode # additional constraints like 'lit|result'; also
|
|
# misused for the codegenDecl pragma in the hope
|
|
# it won't cause problems
|
|
# for skModule the string literal to output for
|
|
# deprecated modules.
|
|
when defined(nimsuggest):
|
|
allUsages*: seq[TLineInfo]
|
|
|
|
TTypeSeq* = seq[PType]
|
|
TLockLevel* = distinct int16
|
|
|
|
TTypeAttachedOp* = enum ## as usual, order is important here
|
|
attachedDestructor,
|
|
attachedAsgn,
|
|
attachedSink,
|
|
attachedTrace,
|
|
attachedDispose,
|
|
attachedDeepCopy
|
|
|
|
TType* {.acyclic.} = object of TIdObj # \
|
|
# types are identical iff they have the
|
|
# same id; there may be multiple copies of a type
|
|
# in memory!
|
|
# Keep in sync with PackedType
|
|
kind*: TTypeKind # kind of type
|
|
callConv*: TCallingConvention # for procs
|
|
flags*: TTypeFlags # flags of the type
|
|
sons*: TTypeSeq # base types, etc.
|
|
n*: PNode # node for types:
|
|
# for range types a nkRange node
|
|
# for record types a nkRecord node
|
|
# for enum types a list of symbols
|
|
# if kind == tyInt: it is an 'int literal(x)' type
|
|
# for procs and tyGenericBody, it's the
|
|
# formal param list
|
|
# for concepts, the concept body
|
|
# else: unused
|
|
owner*: PSym # the 'owner' of the type
|
|
sym*: PSym # types have the sym associated with them
|
|
# it is used for converting types to strings
|
|
size*: BiggestInt # the size of the type in bytes
|
|
# -1 means that the size is unkwown
|
|
align*: int16 # the type's alignment requirements
|
|
paddingAtEnd*: int16 #
|
|
lockLevel*: TLockLevel # lock level as required for deadlock checking
|
|
loc*: TLoc
|
|
typeInst*: PType # for generic instantiations the tyGenericInst that led to this
|
|
# type.
|
|
uniqueId*: ItemId # due to a design mistake, we need to keep the real ID here as it
|
|
# is required by the --incremental:on mode.
|
|
|
|
TPair* = object
|
|
key*, val*: RootRef
|
|
|
|
TPairSeq* = seq[TPair]
|
|
|
|
TIdPair* = object
|
|
key*: PIdObj
|
|
val*: RootRef
|
|
|
|
TIdPairSeq* = seq[TIdPair]
|
|
TIdTable* = object # the same as table[PIdent] of PObject
|
|
counter*: int
|
|
data*: TIdPairSeq
|
|
|
|
TIdNodePair* = object
|
|
key*: PIdObj
|
|
val*: PNode
|
|
|
|
TIdNodePairSeq* = seq[TIdNodePair]
|
|
TIdNodeTable* = object # the same as table[PIdObj] of PNode
|
|
counter*: int
|
|
data*: TIdNodePairSeq
|
|
|
|
TNodePair* = object
|
|
h*: Hash # because it is expensive to compute!
|
|
key*: PNode
|
|
val*: int
|
|
|
|
TNodePairSeq* = seq[TNodePair]
|
|
TNodeTable* = object # the same as table[PNode] of int;
|
|
# nodes are compared by structure!
|
|
counter*: int
|
|
data*: TNodePairSeq
|
|
|
|
TObjectSeq* = seq[RootRef]
|
|
TObjectSet* = object
|
|
counter*: int
|
|
data*: TObjectSeq
|
|
|
|
TImplication* = enum
|
|
impUnknown, impNo, impYes
|
|
|
|
# BUGFIX: a module is overloadable so that a proc can have the
|
|
# same name as an imported module. This is necessary because of
|
|
# the poor naming choices in the standard library.
|
|
|
|
const
|
|
OverloadableSyms* = {skProc, skFunc, skMethod, skIterator,
|
|
skConverter, skModule, skTemplate, skMacro}
|
|
|
|
GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody,
|
|
tyGenericParam}
|
|
|
|
StructuralEquivTypes*: TTypeKinds = {tyNil, tyTuple, tyArray,
|
|
tySet, tyRange, tyPtr, tyRef, tyVar, tyLent, tySequence, tyProc, tyOpenArray,
|
|
tyVarargs}
|
|
|
|
ConcreteTypes*: TTypeKinds = { # types of the expr that may occur in::
|
|
# var x = expr
|
|
tyBool, tyChar, tyEnum, tyArray, tyObject,
|
|
tySet, tyTuple, tyRange, tyPtr, tyRef, tyVar, tyLent, tySequence, tyProc,
|
|
tyPointer,
|
|
tyOpenArray, tyString, tyCstring, tyInt..tyInt64, tyFloat..tyFloat128,
|
|
tyUInt..tyUInt64}
|
|
IntegralTypes* = {tyBool, tyChar, tyEnum, tyInt..tyInt64,
|
|
tyFloat..tyFloat128, tyUInt..tyUInt64} # weird name because it contains tyFloat
|
|
ConstantDataTypes*: TTypeKinds = {tyArray, tySet,
|
|
tyTuple, tySequence}
|
|
NilableTypes*: TTypeKinds = {tyPointer, tyCstring, tyRef, tyPtr,
|
|
tyProc, tyError} # TODO
|
|
PtrLikeKinds*: TTypeKinds = {tyPointer, tyPtr} # for VM
|
|
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
|
|
nfDotSetter, nfDotField,
|
|
nfIsRef, nfIsPtr, nfPreventCg, nfLL,
|
|
nfFromTemplate, nfDefaultRefsParam,
|
|
nfExecuteOnReload, nfLastRead, nfFirstWrite}
|
|
namePos* = 0
|
|
patternPos* = 1 # empty except for term rewriting macros
|
|
genericParamsPos* = 2
|
|
paramsPos* = 3
|
|
pragmasPos* = 4
|
|
miscPos* = 5 # used for undocumented and hacky stuff
|
|
bodyPos* = 6 # position of body; use rodread.getBody() instead!
|
|
resultPos* = 7
|
|
dispatcherPos* = 8
|
|
|
|
nfAllFieldsSet* = nfBase2
|
|
|
|
nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
|
|
nkCommand, nkCallStrLit, nkHiddenCallConv}
|
|
nkIdentKinds* = {nkIdent, nkSym, nkAccQuoted, nkOpenSymChoice,
|
|
nkClosedSymChoice}
|
|
|
|
nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit}
|
|
nkLiterals* = {nkCharLit..nkTripleStrLit}
|
|
nkFloatLiterals* = {nkFloatLit..nkFloat128Lit}
|
|
nkLambdaKinds* = {nkLambda, nkDo}
|
|
declarativeDefs* = {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef}
|
|
routineDefs* = declarativeDefs + {nkMacroDef, nkTemplateDef}
|
|
procDefs* = nkLambdaKinds + declarativeDefs
|
|
callableDefs* = nkLambdaKinds + routineDefs
|
|
|
|
nkSymChoices* = {nkClosedSymChoice, nkOpenSymChoice}
|
|
nkStrKinds* = {nkStrLit..nkTripleStrLit}
|
|
|
|
skLocalVars* = {skVar, skLet, skForVar, skParam, skResult}
|
|
skProcKinds* = {skProc, skFunc, skTemplate, skMacro, skIterator,
|
|
skMethod, skConverter}
|
|
|
|
defaultSize = -1
|
|
defaultAlignment = -1
|
|
defaultOffset* = -1
|
|
|
|
proc getPIdent*(a: PNode): PIdent {.inline.} =
|
|
## Returns underlying `PIdent` for `{nkSym, nkIdent}`, or `nil`.
|
|
# xxx consider whether also returning the 1st ident for {nkOpenSymChoice, nkClosedSymChoice}
|
|
# which may simplify code.
|
|
case a.kind
|
|
of nkSym: a.sym.name
|
|
of nkIdent: a.ident
|
|
else: nil
|
|
|
|
proc getnimblePkg*(a: PSym): PSym =
|
|
result = a
|
|
while result != nil:
|
|
case result.kind
|
|
of skModule:
|
|
result = result.owner
|
|
assert result.kind == skPackage
|
|
of skPackage:
|
|
if result.owner == nil:
|
|
break
|
|
else:
|
|
result = result.owner
|
|
else:
|
|
assert false, $result.kind
|
|
|
|
const
|
|
moduleShift = when defined(cpu32): 20 else: 24
|
|
|
|
template id*(a: PIdObj): int =
|
|
let x = a
|
|
(x.itemId.module.int shl moduleShift) + x.itemId.item.int
|
|
|
|
type
|
|
IdGenerator* = ref object # unfortunately, we really need the 'shared mutable' aspect here.
|
|
module*: int32
|
|
symId*: int32
|
|
typeId*: int32
|
|
sealed*: bool
|
|
|
|
const
|
|
PackageModuleId* = -3'i32
|
|
|
|
proc idGeneratorFromModule*(m: PSym): IdGenerator =
|
|
assert m.kind == skModule
|
|
result = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0)
|
|
|
|
proc nextSymId*(x: IdGenerator): ItemId {.inline.} =
|
|
assert(not x.sealed)
|
|
inc x.symId
|
|
result = ItemId(module: x.module, item: x.symId)
|
|
|
|
proc nextTypeId*(x: IdGenerator): ItemId {.inline.} =
|
|
assert(not x.sealed)
|
|
inc x.typeId
|
|
result = ItemId(module: x.module, item: x.typeId)
|
|
|
|
when false:
|
|
proc nextId*(x: IdGenerator): ItemId {.inline.} =
|
|
inc x.item
|
|
result = x[]
|
|
|
|
when false:
|
|
proc storeBack*(dest: var IdGenerator; src: IdGenerator) {.inline.} =
|
|
assert dest.ItemId.module == src.ItemId.module
|
|
if dest.ItemId.item > src.ItemId.item:
|
|
echo dest.ItemId.item, " ", src.ItemId.item, " ", src.ItemId.module
|
|
assert dest.ItemId.item <= src.ItemId.item
|
|
dest = src
|
|
|
|
proc getnimblePkgId*(a: PSym): int =
|
|
let b = a.getnimblePkg
|
|
result = if b == nil: -1 else: b.id
|
|
|
|
var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things
|
|
#var
|
|
# gMainPackageId*: int
|
|
|
|
proc isCallExpr*(n: PNode): bool =
|
|
result = n.kind in nkCallKinds
|
|
|
|
proc discardSons*(father: PNode)
|
|
|
|
type Indexable = PNode | PType
|
|
|
|
proc len*(n: Indexable): int {.inline.} =
|
|
result = n.sons.len
|
|
|
|
proc safeLen*(n: PNode): int {.inline.} =
|
|
## works even for leaves.
|
|
if n.kind in {nkNone..nkNilLit}: result = 0
|
|
else: result = n.len
|
|
|
|
proc safeArrLen*(n: PNode): int {.inline.} =
|
|
## works for array-like objects (strings passed as openArray in VM).
|
|
if n.kind in {nkStrLit..nkTripleStrLit}: result = n.strVal.len
|
|
elif n.kind in {nkNone..nkFloat128Lit}: result = 0
|
|
else: result = n.len
|
|
|
|
proc add*(father, son: Indexable) =
|
|
assert son != nil
|
|
father.sons.add(son)
|
|
|
|
proc addAllowNil*(father, son: Indexable) {.inline.} =
|
|
father.sons.add(son)
|
|
|
|
template `[]`*(n: Indexable, i: int): Indexable = n.sons[i]
|
|
template `[]=`*(n: Indexable, i: int; x: Indexable) = n.sons[i] = x
|
|
|
|
template `[]`*(n: Indexable, i: BackwardsIndex): Indexable = n[n.len - i.int]
|
|
template `[]=`*(n: Indexable, i: BackwardsIndex; x: Indexable) = n[n.len - i.int] = x
|
|
|
|
when defined(useNodeIds):
|
|
const nodeIdToDebug* = -1 # 2322968
|
|
var gNodeId: int
|
|
|
|
proc newNode*(kind: TNodeKind): PNode =
|
|
## new node with unknown line info, no type, and no children
|
|
result = PNode(kind: kind, info: unknownLineInfo)
|
|
when defined(useNodeIds):
|
|
result.id = gNodeId
|
|
if result.id == nodeIdToDebug:
|
|
echo "KIND ", result.kind
|
|
writeStackTrace()
|
|
inc gNodeId
|
|
|
|
proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode =
|
|
## new node with line info, no type, and no children
|
|
result = PNode(kind: kind, info: info)
|
|
when defined(useNodeIds):
|
|
result.id = gNodeId
|
|
if result.id == nodeIdToDebug:
|
|
echo "KIND ", result.kind
|
|
writeStackTrace()
|
|
inc gNodeId
|
|
|
|
proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode =
|
|
## new node with line info, type, and children
|
|
result = PNode(kind: kind, info: info)
|
|
if children > 0:
|
|
newSeq(result.sons, children)
|
|
when defined(useNodeIds):
|
|
result.id = gNodeId
|
|
if result.id == nodeIdToDebug:
|
|
echo "KIND ", result.kind
|
|
writeStackTrace()
|
|
inc gNodeId
|
|
|
|
proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
|
|
## new node with line info, type, and no children
|
|
result = newNode(kind)
|
|
result.info = info
|
|
result.typ = typ
|
|
|
|
proc newTree*(kind: TNodeKind; children: varargs[PNode]): PNode =
|
|
result = newNode(kind)
|
|
if children.len > 0:
|
|
result.info = children[0].info
|
|
result.sons = @children
|
|
|
|
proc newTreeI*(kind: TNodeKind; info: TLineInfo; children: varargs[PNode]): PNode =
|
|
result = newNodeI(kind, info)
|
|
if children.len > 0:
|
|
result.info = children[0].info
|
|
result.sons = @children
|
|
|
|
proc newTreeIT*(kind: TNodeKind; info: TLineInfo; typ: PType; children: varargs[PNode]): PNode =
|
|
result = newNodeIT(kind, info, typ)
|
|
if children.len > 0:
|
|
result.info = children[0].info
|
|
result.sons = @children
|
|
|
|
template previouslyInferred*(t: PType): PType =
|
|
if t.sons.len > 1: t.lastSon else: nil
|
|
|
|
when false:
|
|
import tables, strutils
|
|
var x: CountTable[string]
|
|
|
|
addQuitProc proc () {.noconv.} =
|
|
for k, v in pairs(x):
|
|
echo k
|
|
echo v
|
|
|
|
proc newSym*(symKind: TSymKind, name: PIdent, id: ItemId, owner: PSym,
|
|
info: TLineInfo; options: TOptions = {}): PSym =
|
|
# generates a symbol and initializes the hash field too
|
|
result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id,
|
|
options: options, owner: owner, offset: defaultOffset)
|
|
when false:
|
|
if id.module == 48 and id.item == 39:
|
|
writeStackTrace()
|
|
echo "kind ", symKind, " ", name.s
|
|
if owner != nil: echo owner.name.s
|
|
|
|
proc astdef*(s: PSym): PNode =
|
|
# get only the definition (initializer) portion of the ast
|
|
if s.ast != nil and s.ast.kind == nkIdentDefs:
|
|
s.ast[2]
|
|
else:
|
|
s.ast
|
|
|
|
proc isMetaType*(t: PType): bool =
|
|
return t.kind in tyMetaTypes or
|
|
(t.kind == tyStatic and t.n == nil) or
|
|
tfHasMeta in t.flags
|
|
|
|
proc isUnresolvedStatic*(t: PType): bool =
|
|
return t.kind == tyStatic and t.n == nil
|
|
|
|
proc linkTo*(t: PType, s: PSym): PType {.discardable.} =
|
|
t.sym = s
|
|
s.typ = t
|
|
result = t
|
|
|
|
proc linkTo*(s: PSym, t: PType): PSym {.discardable.} =
|
|
t.sym = s
|
|
s.typ = t
|
|
result = s
|
|
|
|
template fileIdx*(c: PSym): FileIndex =
|
|
# XXX: this should be used only on module symbols
|
|
c.position.FileIndex
|
|
|
|
template filename*(c: PSym): string =
|
|
# XXX: this should be used only on module symbols
|
|
c.position.FileIndex.toFilename
|
|
|
|
proc appendToModule*(m: PSym, n: PNode) =
|
|
## The compiler will use this internally to add nodes that will be
|
|
## appended to the module after the sem pass
|
|
if m.ast == nil:
|
|
m.ast = newNode(nkStmtList)
|
|
m.ast.sons = @[n]
|
|
else:
|
|
assert m.ast.kind == nkStmtList
|
|
m.ast.sons.add(n)
|
|
|
|
const # for all kind of hash tables:
|
|
GrowthFactor* = 2 # must be power of 2, > 0
|
|
StartSize* = 8 # must be power of 2, > 0
|
|
|
|
proc copyStrTable*(dest: var TStrTable, src: TStrTable) =
|
|
dest.counter = src.counter
|
|
setLen(dest.data, src.data.len)
|
|
for i in 0..high(src.data): dest.data[i] = src.data[i]
|
|
|
|
proc copyIdTable*(dest: var TIdTable, src: TIdTable) =
|
|
dest.counter = src.counter
|
|
newSeq(dest.data, src.data.len)
|
|
for i in 0..high(src.data): dest.data[i] = src.data[i]
|
|
|
|
proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) =
|
|
dest.counter = src.counter
|
|
setLen(dest.data, src.data.len)
|
|
for i in 0..high(src.data): dest.data[i] = src.data[i]
|
|
|
|
proc discardSons*(father: PNode) =
|
|
father.sons = @[]
|
|
|
|
proc withInfo*(n: PNode, info: TLineInfo): PNode =
|
|
n.info = info
|
|
return n
|
|
|
|
proc newIdentNode*(ident: PIdent, info: TLineInfo): PNode =
|
|
result = newNode(nkIdent)
|
|
result.ident = ident
|
|
result.info = info
|
|
|
|
proc newSymNode*(sym: PSym): PNode =
|
|
result = newNode(nkSym)
|
|
result.sym = sym
|
|
result.typ = sym.typ
|
|
result.info = sym.info
|
|
|
|
proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
|
|
result = newNode(nkSym)
|
|
result.sym = sym
|
|
result.typ = sym.typ
|
|
result.info = info
|
|
|
|
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
|
|
result = newNode(kind)
|
|
result.intVal = intVal
|
|
|
|
proc newIntNode*(kind: TNodeKind, intVal: Int128): PNode =
|
|
result = newNode(kind)
|
|
result.intVal = castToInt64(intVal)
|
|
|
|
proc lastSon*(n: Indexable): Indexable = n.sons[^1]
|
|
|
|
proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
|
|
## Used throughout the compiler code to test whether a type tree contains or
|
|
## doesn't contain a specific type/types - it is often the case that only the
|
|
## last child nodes of a type tree need to be searched. This is a really hot
|
|
## path within the compiler!
|
|
result = t
|
|
while result.kind in kinds: result = lastSon(result)
|
|
|
|
proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode =
|
|
let kind = skipTypes(typ, abstractVarRange).kind
|
|
case kind
|
|
of tyInt: result = newNode(nkIntLit)
|
|
of tyInt8: result = newNode(nkInt8Lit)
|
|
of tyInt16: result = newNode(nkInt16Lit)
|
|
of tyInt32: result = newNode(nkInt32Lit)
|
|
of tyInt64: result = newNode(nkInt64Lit)
|
|
of tyChar: result = newNode(nkCharLit)
|
|
of tyUInt: result = newNode(nkUIntLit)
|
|
of tyUInt8: result = newNode(nkUInt8Lit)
|
|
of tyUInt16: result = newNode(nkUInt16Lit)
|
|
of tyUInt32: result = newNode(nkUInt32Lit)
|
|
of tyUInt64: result = newNode(nkUInt64Lit)
|
|
of tyBool, tyEnum:
|
|
# XXX: does this really need to be the kind nkIntLit?
|
|
result = newNode(nkIntLit)
|
|
of tyStatic: # that's a pre-existing bug, will fix in another PR
|
|
result = newNode(nkIntLit)
|
|
else: doAssert false, $kind
|
|
result.intVal = intVal
|
|
result.typ = typ
|
|
|
|
proc newIntTypeNode*(intVal: Int128, typ: PType): PNode =
|
|
# XXX: introduce range check
|
|
newIntTypeNode(castToInt64(intVal), typ)
|
|
|
|
proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode =
|
|
result = newNode(kind)
|
|
result.floatVal = floatVal
|
|
|
|
proc newStrNode*(kind: TNodeKind, strVal: string): PNode =
|
|
result = newNode(kind)
|
|
result.strVal = strVal
|
|
|
|
proc newStrNode*(strVal: string; info: TLineInfo): PNode =
|
|
result = newNodeI(nkStrLit, info)
|
|
result.strVal = strVal
|
|
|
|
proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode,
|
|
params,
|
|
name, pattern, genericParams,
|
|
pragmas, exceptions: PNode): PNode =
|
|
result = newNodeI(kind, info)
|
|
result.sons = @[name, pattern, genericParams, params,
|
|
pragmas, exceptions, body]
|
|
|
|
const
|
|
UnspecifiedLockLevel* = TLockLevel(-1'i16)
|
|
MaxLockLevel* = 1000'i16
|
|
UnknownLockLevel* = TLockLevel(1001'i16)
|
|
AttachedOpToStr*: array[TTypeAttachedOp, string] = [
|
|
"=destroy", "=copy", "=sink", "=trace", "=dispose", "=deepcopy"]
|
|
|
|
proc `$`*(x: TLockLevel): string =
|
|
if x.ord == UnspecifiedLockLevel.ord: result = "<unspecified>"
|
|
elif x.ord == UnknownLockLevel.ord: result = "<unknown>"
|
|
else: result = $int16(x)
|
|
|
|
proc `$`*(s: PSym): string =
|
|
if s != nil:
|
|
result = s.name.s & "@" & $s.id
|
|
else:
|
|
result = "<nil>"
|
|
|
|
proc newType*(kind: TTypeKind, id: ItemId; owner: PSym): PType =
|
|
result = PType(kind: kind, owner: owner, size: defaultSize,
|
|
align: defaultAlignment, itemId: id,
|
|
lockLevel: UnspecifiedLockLevel,
|
|
uniqueId: id)
|
|
when false:
|
|
if result.itemId.module == 55 and result.itemId.item == 2:
|
|
echo "KNID ", kind
|
|
writeStackTrace()
|
|
|
|
|
|
proc mergeLoc(a: var TLoc, b: TLoc) =
|
|
if a.k == low(typeof(a.k)): a.k = b.k
|
|
if a.storage == low(typeof(a.storage)): a.storage = b.storage
|
|
a.flags.incl b.flags
|
|
if a.lode == nil: a.lode = b.lode
|
|
if a.r == nil: a.r = b.r
|
|
|
|
proc newSons*(father: Indexable, length: int) =
|
|
setLen(father.sons, length)
|
|
|
|
proc assignType*(dest, src: PType) =
|
|
dest.kind = src.kind
|
|
dest.flags = src.flags
|
|
dest.callConv = src.callConv
|
|
dest.n = src.n
|
|
dest.size = src.size
|
|
dest.align = src.align
|
|
dest.lockLevel = src.lockLevel
|
|
# this fixes 'type TLock = TSysLock':
|
|
if src.sym != nil:
|
|
if dest.sym != nil:
|
|
dest.sym.flags.incl src.sym.flags-{sfExported}
|
|
if dest.sym.annex == nil: dest.sym.annex = src.sym.annex
|
|
mergeLoc(dest.sym.loc, src.sym.loc)
|
|
else:
|
|
dest.sym = src.sym
|
|
newSons(dest, src.len)
|
|
for i in 0..<src.len: dest[i] = src[i]
|
|
|
|
proc copyType*(t: PType, id: ItemId, owner: PSym): PType =
|
|
result = newType(t.kind, id, owner)
|
|
assignType(result, t)
|
|
result.sym = t.sym # backend-info should not be copied
|
|
|
|
proc exactReplica*(t: PType): PType =
|
|
result = copyType(t, t.itemId, t.owner)
|
|
|
|
proc copySym*(s: PSym; id: ItemId): PSym =
|
|
result = newSym(s.kind, s.name, id, s.owner, s.info, s.options)
|
|
#result.ast = nil # BUGFIX; was: s.ast which made problems
|
|
result.typ = s.typ
|
|
result.flags = s.flags
|
|
result.magic = s.magic
|
|
result.options = s.options
|
|
result.position = s.position
|
|
result.loc = s.loc
|
|
result.annex = s.annex # BUGFIX
|
|
result.constraint = s.constraint
|
|
if result.kind in {skVar, skLet, skField}:
|
|
result.guard = s.guard
|
|
result.bitsize = s.bitsize
|
|
result.alignment = s.alignment
|
|
|
|
proc createModuleAlias*(s: PSym, id: ItemId, newIdent: PIdent, info: TLineInfo;
|
|
options: TOptions): PSym =
|
|
result = newSym(s.kind, newIdent, id, s.owner, info, options)
|
|
# keep ID!
|
|
result.ast = s.ast
|
|
#result.id = s.id # XXX figure out what to do with the ID.
|
|
result.flags = s.flags
|
|
result.options = s.options
|
|
result.position = s.position
|
|
result.loc = s.loc
|
|
result.annex = s.annex
|
|
|
|
proc initStrTable*(x: var TStrTable) =
|
|
x.counter = 0
|
|
newSeq(x.data, StartSize)
|
|
|
|
proc newStrTable*: TStrTable =
|
|
initStrTable(result)
|
|
|
|
proc initIdTable*(x: var TIdTable) =
|
|
x.counter = 0
|
|
newSeq(x.data, StartSize)
|
|
|
|
proc newIdTable*: TIdTable =
|
|
initIdTable(result)
|
|
|
|
proc resetIdTable*(x: var TIdTable) =
|
|
x.counter = 0
|
|
# clear and set to old initial size:
|
|
setLen(x.data, 0)
|
|
setLen(x.data, StartSize)
|
|
|
|
proc initObjectSet*(x: var TObjectSet) =
|
|
x.counter = 0
|
|
newSeq(x.data, StartSize)
|
|
|
|
proc initIdNodeTable*(x: var TIdNodeTable) =
|
|
x.counter = 0
|
|
newSeq(x.data, StartSize)
|
|
|
|
proc initNodeTable*(x: var TNodeTable) =
|
|
x.counter = 0
|
|
newSeq(x.data, StartSize)
|
|
|
|
proc skipTypes*(t: PType, kinds: TTypeKinds; maxIters: int): PType =
|
|
result = t
|
|
var i = maxIters
|
|
while result.kind in kinds:
|
|
result = lastSon(result)
|
|
dec i
|
|
if i == 0: return nil
|
|
|
|
proc skipTypesOrNil*(t: PType, kinds: TTypeKinds): PType =
|
|
## same as skipTypes but handles 'nil'
|
|
result = t
|
|
while result != nil and result.kind in kinds:
|
|
if result.len == 0: return nil
|
|
result = lastSon(result)
|
|
|
|
proc isGCedMem*(t: PType): bool {.inline.} =
|
|
result = t.kind in {tyString, tyRef, tySequence} or
|
|
t.kind == tyProc and t.callConv == ccClosure
|
|
|
|
proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) =
|
|
owner.flags.incl elem.flags * {tfHasMeta, tfTriggersCompileTime}
|
|
if tfNotNil in elem.flags:
|
|
if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvocation}:
|
|
owner.flags.incl tfNotNil
|
|
|
|
if elem.isMetaType:
|
|
owner.flags.incl tfHasMeta
|
|
|
|
let mask = elem.flags * {tfHasAsgn, tfHasOwned}
|
|
if mask != {} and propagateHasAsgn:
|
|
let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink})
|
|
if o2.kind in {tyTuple, tyObject, tyArray,
|
|
tySequence, tySet, tyDistinct, tyOpenArray, tyVarargs}:
|
|
o2.flags.incl mask
|
|
owner.flags.incl mask
|
|
|
|
if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
|
|
tyGenericInvocation, tyPtr}:
|
|
let elemB = elem.skipTypes({tyGenericInst, tyAlias, tySink})
|
|
if elemB.isGCedMem or tfHasGCedMem in elemB.flags:
|
|
# for simplicity, we propagate this flag even to generics. We then
|
|
# ensure this doesn't bite us in sempass2.
|
|
owner.flags.incl tfHasGCedMem
|
|
|
|
proc rawAddSon*(father, son: PType; propagateHasAsgn = true) =
|
|
father.sons.add(son)
|
|
if not son.isNil: propagateToOwner(father, son, propagateHasAsgn)
|
|
|
|
proc rawAddSonNoPropagationOfTypeFlags*(father, son: PType) =
|
|
father.sons.add(son)
|
|
|
|
proc addSonNilAllowed*(father, son: PNode) =
|
|
father.sons.add(son)
|
|
|
|
proc delSon*(father: PNode, idx: int) =
|
|
if father.len == 0: return
|
|
for i in idx..<father.len - 1: father[i] = father[i + 1]
|
|
father.sons.setLen(father.len - 1)
|
|
|
|
proc copyNode*(src: PNode): PNode =
|
|
# does not copy its sons!
|
|
if src == nil:
|
|
return nil
|
|
result = newNode(src.kind)
|
|
result.info = src.info
|
|
result.typ = src.typ
|
|
result.flags = src.flags * PersistentNodeFlags
|
|
result.comment = src.comment
|
|
when defined(useNodeIds):
|
|
if result.id == nodeIdToDebug:
|
|
echo "COMES FROM ", src.id
|
|
case src.kind
|
|
of nkCharLit..nkUInt64Lit: result.intVal = src.intVal
|
|
of nkFloatLiterals: result.floatVal = src.floatVal
|
|
of nkSym: result.sym = src.sym
|
|
of nkIdent: result.ident = src.ident
|
|
of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
|
|
else: discard
|
|
|
|
template transitionNodeKindCommon(k: TNodeKind) =
|
|
let obj {.inject.} = n[]
|
|
n[] = TNode(kind: k, typ: obj.typ, info: obj.info, flags: obj.flags,
|
|
comment: obj.comment)
|
|
when defined(useNodeIds):
|
|
n.id = obj.id
|
|
|
|
proc transitionSonsKind*(n: PNode, kind: range[nkComesFrom..nkTupleConstr]) =
|
|
transitionNodeKindCommon(kind)
|
|
n.sons = obj.sons
|
|
|
|
proc transitionIntKind*(n: PNode, kind: range[nkCharLit..nkUInt64Lit]) =
|
|
transitionNodeKindCommon(kind)
|
|
n.intVal = obj.intVal
|
|
|
|
proc transitionNoneToSym*(n: PNode) =
|
|
transitionNodeKindCommon(nkSym)
|
|
|
|
template transitionSymKindCommon*(k: TSymKind) =
|
|
let obj {.inject.} = s[]
|
|
s[] = TSym(kind: k, itemId: obj.itemId, magic: obj.magic, typ: obj.typ, name: obj.name,
|
|
info: obj.info, owner: obj.owner, flags: obj.flags, ast: obj.ast,
|
|
options: obj.options, position: obj.position, offset: obj.offset,
|
|
loc: obj.loc, annex: obj.annex, constraint: obj.constraint)
|
|
when hasFFI:
|
|
s.cname = obj.cname
|
|
when defined(nimsuggest):
|
|
s.allUsages = obj.allUsages
|
|
|
|
proc transitionGenericParamToType*(s: PSym) =
|
|
transitionSymKindCommon(skType)
|
|
|
|
proc transitionRoutineSymKind*(s: PSym, kind: range[skProc..skTemplate]) =
|
|
transitionSymKindCommon(kind)
|
|
s.gcUnsafetyReason = obj.gcUnsafetyReason
|
|
s.transformedBody = obj.transformedBody
|
|
|
|
proc transitionToLet*(s: PSym) =
|
|
transitionSymKindCommon(skLet)
|
|
s.guard = obj.guard
|
|
s.bitsize = obj.bitsize
|
|
s.alignment = obj.alignment
|
|
|
|
template copyNodeImpl(dst, src, processSonsStmt) =
|
|
if src == nil: return
|
|
dst = newNode(src.kind)
|
|
dst.info = src.info
|
|
dst.typ = src.typ
|
|
dst.flags = src.flags * PersistentNodeFlags
|
|
dst.comment = src.comment
|
|
when defined(useNodeIds):
|
|
if dst.id == nodeIdToDebug:
|
|
echo "COMES FROM ", src.id
|
|
case src.kind
|
|
of nkCharLit..nkUInt64Lit: dst.intVal = src.intVal
|
|
of nkFloatLiterals: dst.floatVal = src.floatVal
|
|
of nkSym: dst.sym = src.sym
|
|
of nkIdent: dst.ident = src.ident
|
|
of nkStrLit..nkTripleStrLit: dst.strVal = src.strVal
|
|
else: processSonsStmt
|
|
|
|
proc shallowCopy*(src: PNode): PNode =
|
|
# does not copy its sons, but provides space for them:
|
|
copyNodeImpl(result, src):
|
|
newSeq(result.sons, src.len)
|
|
|
|
proc copyTree*(src: PNode): PNode =
|
|
# copy a whole syntax tree; performs deep copying
|
|
copyNodeImpl(result, src):
|
|
newSeq(result.sons, src.len)
|
|
for i in 0..<src.len:
|
|
result[i] = copyTree(src[i])
|
|
|
|
proc copyTreeWithoutNode*(src, skippedNode: PNode): PNode =
|
|
copyNodeImpl(result, src):
|
|
result.sons = newSeqOfCap[PNode](src.len)
|
|
for n in src.sons:
|
|
if n != skippedNode:
|
|
result.sons.add copyTreeWithoutNode(n, skippedNode)
|
|
|
|
proc hasSonWith*(n: PNode, kind: TNodeKind): bool =
|
|
for i in 0..<n.len:
|
|
if n[i].kind == kind:
|
|
return true
|
|
result = false
|
|
|
|
proc hasNilSon*(n: PNode): bool =
|
|
for i in 0..<n.safeLen:
|
|
if n[i] == nil:
|
|
return true
|
|
elif hasNilSon(n[i]):
|
|
return true
|
|
result = false
|
|
|
|
proc containsNode*(n: PNode, kinds: TNodeKinds): bool =
|
|
if n == nil: return
|
|
case n.kind
|
|
of nkEmpty..nkNilLit: result = n.kind in kinds
|
|
else:
|
|
for i in 0..<n.len:
|
|
if n.kind in kinds or containsNode(n[i], kinds): return true
|
|
|
|
proc hasSubnodeWith*(n: PNode, kind: TNodeKind): bool =
|
|
case n.kind
|
|
of nkEmpty..nkNilLit: result = n.kind == kind
|
|
else:
|
|
for i in 0..<n.len:
|
|
if (n[i].kind == kind) or hasSubnodeWith(n[i], kind):
|
|
return true
|
|
result = false
|
|
|
|
proc getInt*(a: PNode): Int128 =
|
|
case a.kind
|
|
of nkCharLit, nkUIntLit..nkUInt64Lit:
|
|
result = toInt128(cast[uint64](a.intVal))
|
|
of nkInt8Lit..nkInt64Lit:
|
|
result = toInt128(a.intVal)
|
|
of nkIntLit:
|
|
# XXX: enable this assert
|
|
# assert a.typ.kind notin {tyChar, tyUint..tyUInt64}
|
|
result = toInt128(a.intVal)
|
|
else:
|
|
raiseRecoverableError("cannot extract number from invalid AST node")
|
|
|
|
proc getInt64*(a: PNode): int64 {.deprecated: "use getInt".} =
|
|
case a.kind
|
|
of nkCharLit, nkUIntLit..nkUInt64Lit, nkIntLit..nkInt64Lit:
|
|
result = a.intVal
|
|
else:
|
|
raiseRecoverableError("cannot extract number from invalid AST node")
|
|
|
|
proc getFloat*(a: PNode): BiggestFloat =
|
|
case a.kind
|
|
of nkFloatLiterals: result = a.floatVal
|
|
of nkCharLit, nkUIntLit..nkUInt64Lit, nkIntLit..nkInt64Lit:
|
|
result = BiggestFloat a.intVal
|
|
else:
|
|
raiseRecoverableError("cannot extract number from invalid AST node")
|
|
#doAssert false, "getFloat"
|
|
#internalError(a.info, "getFloat")
|
|
#result = 0.0
|
|
|
|
proc getStr*(a: PNode): string =
|
|
case a.kind
|
|
of nkStrLit..nkTripleStrLit: result = a.strVal
|
|
of nkNilLit:
|
|
# let's hope this fixes more problems than it creates:
|
|
result = ""
|
|
else:
|
|
raiseRecoverableError("cannot extract string from invalid AST node")
|
|
#doAssert false, "getStr"
|
|
#internalError(a.info, "getStr")
|
|
#result = ""
|
|
|
|
proc getStrOrChar*(a: PNode): string =
|
|
case a.kind
|
|
of nkStrLit..nkTripleStrLit: result = a.strVal
|
|
of nkCharLit..nkUInt64Lit: result = $chr(int(a.intVal))
|
|
else:
|
|
raiseRecoverableError("cannot extract string from invalid AST node")
|
|
#doAssert false, "getStrOrChar"
|
|
#internalError(a.info, "getStrOrChar")
|
|
#result = ""
|
|
|
|
proc isGenericParams*(n: PNode): bool {.inline.} =
|
|
## used to judge whether a node is generic params.
|
|
n != nil and n.kind == nkGenericParams
|
|
|
|
proc isGenericRoutine*(n: PNode): bool {.inline.} =
|
|
n != nil and n.kind in callableDefs and n[genericParamsPos].isGenericParams
|
|
|
|
proc isGenericRoutineStrict*(s: PSym): bool {.inline.} =
|
|
## determines if this symbol represents a generic routine
|
|
## the unusual name is so it doesn't collide and eventually replaces
|
|
## `isGenericRoutine`
|
|
s.kind in skProcKinds and s.ast.isGenericRoutine
|
|
|
|
proc isGenericRoutine*(s: PSym): bool {.inline.} =
|
|
## determines if this symbol represents a generic routine or an instance of
|
|
## one. This should be renamed accordingly and `isGenericRoutineStrict`
|
|
## should take this name instead.
|
|
##
|
|
## Warning/XXX: Unfortunately, it considers a proc kind symbol flagged with
|
|
## sfFromGeneric as a generic routine. Instead this should likely not be the
|
|
## case and the concepts should be teased apart:
|
|
## - generic definition
|
|
## - generic instance
|
|
## - either generic definition or instance
|
|
s.kind in skProcKinds and (sfFromGeneric in s.flags or
|
|
s.ast.isGenericRoutine)
|
|
|
|
proc skipGenericOwner*(s: PSym): PSym =
|
|
## Generic instantiations are owned by their originating generic
|
|
## symbol. This proc skips such owners and goes straight to the owner
|
|
## of the generic itself (the module or the enclosing proc).
|
|
result = if s.kind in skProcKinds and sfFromGeneric in s.flags:
|
|
s.owner.owner
|
|
else:
|
|
s.owner
|
|
|
|
proc originatingModule*(s: PSym): PSym =
|
|
result = s.owner
|
|
while result.kind != skModule: result = result.owner
|
|
|
|
proc isRoutine*(s: PSym): bool {.inline.} =
|
|
result = s.kind in skProcKinds
|
|
|
|
proc isCompileTimeProc*(s: PSym): bool {.inline.} =
|
|
result = s.kind == skMacro or
|
|
s.kind in {skProc, skFunc} and sfCompileTime in s.flags
|
|
|
|
proc isRunnableExamples*(n: PNode): bool =
|
|
# Templates and generics don't perform symbol lookups.
|
|
result = n.kind == nkSym and n.sym.magic == mRunnableExamples or
|
|
n.kind == nkIdent and n.ident.s == "runnableExamples"
|
|
|
|
proc requiredParams*(s: PSym): int =
|
|
# Returns the number of required params (without default values)
|
|
# XXX: Perhaps we can store this in the `offset` field of the
|
|
# symbol instead?
|
|
for i in 1..<s.typ.len:
|
|
if s.typ.n[i].sym.ast != nil:
|
|
return i - 1
|
|
return s.typ.len - 1
|
|
|
|
proc hasPattern*(s: PSym): bool {.inline.} =
|
|
result = isRoutine(s) and s.ast[patternPos].kind != nkEmpty
|
|
|
|
iterator items*(n: PNode): PNode =
|
|
for i in 0..<n.safeLen: yield n[i]
|
|
|
|
iterator pairs*(n: PNode): tuple[i: int, n: PNode] =
|
|
for i in 0..<n.safeLen: yield (i, n[i])
|
|
|
|
proc isAtom*(n: PNode): bool {.inline.} =
|
|
result = n.kind >= nkNone and n.kind <= nkNilLit
|
|
|
|
proc isEmptyType*(t: PType): bool {.inline.} =
|
|
## 'void' and 'typed' types are often equivalent to 'nil' these days:
|
|
result = t == nil or t.kind in {tyVoid, tyTyped}
|
|
|
|
proc makeStmtList*(n: PNode): PNode =
|
|
if n.kind == nkStmtList:
|
|
result = n
|
|
else:
|
|
result = newNodeI(nkStmtList, n.info)
|
|
result.add n
|
|
|
|
proc skipStmtList*(n: PNode): PNode =
|
|
if n.kind in {nkStmtList, nkStmtListExpr}:
|
|
for i in 0..<n.len-1:
|
|
if n[i].kind notin {nkEmpty, nkCommentStmt}: return n
|
|
result = n.lastSon
|
|
else:
|
|
result = n
|
|
|
|
proc toVar*(typ: PType; kind: TTypeKind; idgen: IdGenerator): PType =
|
|
## If ``typ`` is not a tyVar then it is converted into a `var <typ>` and
|
|
## returned. Otherwise ``typ`` is simply returned as-is.
|
|
result = typ
|
|
if typ.kind != kind:
|
|
result = newType(kind, nextTypeId(idgen), typ.owner)
|
|
rawAddSon(result, typ)
|
|
|
|
proc toRef*(typ: PType; idgen: IdGenerator): PType =
|
|
## If ``typ`` is a tyObject then it is converted into a `ref <typ>` and
|
|
## returned. Otherwise ``typ`` is simply returned as-is.
|
|
result = typ
|
|
if typ.skipTypes({tyAlias, tyGenericInst}).kind == tyObject:
|
|
result = newType(tyRef, nextTypeId(idgen), typ.owner)
|
|
rawAddSon(result, typ)
|
|
|
|
proc toObject*(typ: PType): PType =
|
|
## If ``typ`` is a tyRef then its immediate son is returned (which in many
|
|
## cases should be a ``tyObject``).
|
|
## Otherwise ``typ`` is simply returned as-is.
|
|
let t = typ.skipTypes({tyAlias, tyGenericInst})
|
|
if t.kind == tyRef: t.lastSon
|
|
else: typ
|
|
|
|
proc isImportedException*(t: PType; conf: ConfigRef): bool =
|
|
assert t != nil
|
|
|
|
if conf.exc != excCpp:
|
|
return false
|
|
|
|
let base = t.skipTypes({tyAlias, tyPtr, tyDistinct, tyGenericInst})
|
|
|
|
if base.sym != nil and {sfCompileToCpp, sfImportc} * base.sym.flags != {}:
|
|
result = true
|
|
|
|
proc isInfixAs*(n: PNode): bool =
|
|
return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "as"
|
|
|
|
proc skipColon*(n: PNode): PNode =
|
|
result = n
|
|
if n.kind == nkExprColonExpr:
|
|
result = n[1]
|
|
|
|
proc findUnresolvedStatic*(n: PNode): PNode =
|
|
# n.typ == nil: see issue #14802
|
|
if n.kind == nkSym and n.typ != nil and n.typ.kind == tyStatic and n.typ.n == nil:
|
|
return n
|
|
|
|
for son in n:
|
|
let n = son.findUnresolvedStatic
|
|
if n != nil: return n
|
|
|
|
return nil
|
|
|
|
when false:
|
|
proc containsNil*(n: PNode): bool =
|
|
# only for debugging
|
|
if n.isNil: return true
|
|
for i in 0..<n.safeLen:
|
|
if n[i].containsNil: return true
|
|
|
|
|
|
template hasDestructor*(t: PType): bool = {tfHasAsgn, tfHasOwned} * t.flags != {}
|
|
|
|
template incompleteType*(t: PType): bool =
|
|
t.sym != nil and {sfForward, sfNoForward} * t.sym.flags == {sfForward}
|
|
|
|
template typeCompleted*(s: PSym) =
|
|
incl s.flags, sfNoForward
|
|
|
|
template detailedInfo*(sym: PSym): string =
|
|
sym.name.s
|
|
|
|
proc isInlineIterator*(typ: PType): bool {.inline.} =
|
|
typ.kind == tyProc and tfIterator in typ.flags and typ.callConv != ccClosure
|
|
|
|
proc isClosureIterator*(typ: PType): bool {.inline.} =
|
|
typ.kind == tyProc and tfIterator in typ.flags and typ.callConv == ccClosure
|
|
|
|
proc isClosure*(typ: PType): bool {.inline.} =
|
|
typ.kind == tyProc and typ.callConv == ccClosure
|
|
|
|
proc isSinkParam*(s: PSym): bool {.inline.} =
|
|
s.kind == skParam and (s.typ.kind == tySink or tfHasOwned in s.typ.flags)
|
|
|
|
proc isSinkType*(t: PType): bool {.inline.} =
|
|
t.kind == tySink or tfHasOwned in t.flags
|
|
|
|
proc newProcType*(info: TLineInfo; id: ItemId; owner: PSym): PType =
|
|
result = newType(tyProc, id, owner)
|
|
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 addParam*(procType: PType; param: PSym) =
|
|
param.position = procType.len-1
|
|
procType.n.add newSymNode(param)
|
|
rawAddSon(procType, param.typ)
|
|
|
|
const magicsThatCanRaise = {
|
|
mNone, mSlurp, mStaticExec, mParseExprToAst, mParseStmtToAst, mEcho}
|
|
|
|
proc canRaiseConservative*(fn: PNode): bool =
|
|
if fn.kind == nkSym and fn.sym.magic notin magicsThatCanRaise:
|
|
result = false
|
|
else:
|
|
result = true
|
|
|
|
proc canRaise*(fn: PNode): bool =
|
|
if fn.kind == nkSym and (fn.sym.magic notin magicsThatCanRaise or
|
|
{sfImportc, sfInfixCall} * fn.sym.flags == {sfImportc} or
|
|
sfGeneratedOp in fn.sym.flags):
|
|
result = false
|
|
elif fn.kind == nkSym and fn.sym.magic == mEcho:
|
|
result = true
|
|
else:
|
|
# TODO check for n having sons? or just return false for now if not
|
|
if fn.typ != nil and fn.typ.n != nil and fn.typ.n[0].kind == nkSym:
|
|
result = false
|
|
else:
|
|
result = fn.typ != nil and fn.typ.n != nil and ((fn.typ.n[0].len < effectListLen) or
|
|
(fn.typ.n[0][exceptionEffects] != nil and
|
|
fn.typ.n[0][exceptionEffects].safeLen > 0))
|
|
|
|
proc toHumanStrImpl[T](kind: T, num: static int): string =
|
|
result = $kind
|
|
result = result[num..^1]
|
|
result[0] = result[0].toLowerAscii
|
|
|
|
proc toHumanStr*(kind: TSymKind): string =
|
|
## strips leading `sk`
|
|
result = toHumanStrImpl(kind, 2)
|
|
|
|
proc toHumanStr*(kind: TTypeKind): string =
|
|
## strips leading `tk`
|
|
result = toHumanStrImpl(kind, 2)
|
|
|
|
proc skipAddr*(n: PNode): PNode {.inline.} =
|
|
(if n.kind == nkHiddenAddr: n[0] else: n)
|
|
|
|
proc isNewStyleConcept*(n: PNode): bool {.inline.} =
|
|
assert n.kind == nkTypeClassTy
|
|
result = n[0].kind == nkEmpty
|