diff options
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/ast.nim | 33 | ||||
-rw-r--r-- | compiler/ccgtypes.nim | 25 | ||||
-rw-r--r-- | compiler/ccgutils.nim | 5 | ||||
-rw-r--r-- | compiler/extccomp.nim | 72 | ||||
-rw-r--r-- | compiler/lambdalifting.nim | 2 | ||||
-rw-r--r-- | compiler/lexer.nim | 10 | ||||
-rw-r--r-- | compiler/lookups.nim | 5 | ||||
-rw-r--r-- | compiler/msgs.nim | 8 | ||||
-rw-r--r-- | compiler/parser.nim | 83 | ||||
-rw-r--r-- | compiler/pragmas.nim | 6 | ||||
-rw-r--r-- | compiler/renderer.nim | 15 | ||||
-rw-r--r-- | compiler/sem.nim | 3 | ||||
-rw-r--r-- | compiler/semcall.nim | 19 | ||||
-rw-r--r-- | compiler/semdata.nim | 23 | ||||
-rw-r--r-- | compiler/semexprs.nim | 84 | ||||
-rw-r--r-- | compiler/seminst.nim | 80 | ||||
-rw-r--r-- | compiler/semstmts.nim | 19 | ||||
-rw-r--r-- | compiler/semtypes.nim | 146 | ||||
-rw-r--r-- | compiler/semtypinst.nim | 33 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 129 | ||||
-rw-r--r-- | compiler/types.nim | 40 | ||||
-rw-r--r-- | compiler/vmgen.nim | 2 | ||||
-rw-r--r-- | compiler/wordrecg.nim | 4 |
23 files changed, 609 insertions, 237 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index ee0d55920..30778c02d 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -188,6 +188,10 @@ type 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 @@ -382,6 +386,10 @@ type # sons[0]: type of containing object or tuple # sons[1]: field type # .n: nkDotExpr storing the field name + +static: + # remind us when TTypeKind stops to fit in a single 64-bit word + assert TTypeKind.high.ord <= 63 const tyPureObject* = tyTuple @@ -394,7 +402,7 @@ const tyUserTypeClass, tyUserTypeClassInst, tyAnd, tyOr, tyNot, tyAnything} - tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyStatic, tyExpr} + tyTypeClasses + tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyExpr} + tyTypeClasses type TTypeKinds* = set[TTypeKind] @@ -414,7 +422,7 @@ type 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 - + TNodeFlags* = set[TNodeFlag] TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 23) tfVarargs, # procedure has C styled varargs @@ -428,8 +436,11 @@ type 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 params: e.g. + 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 + # depends on unresolved static params. 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 @@ -445,9 +456,14 @@ type tfHasMeta, # type contains "wildcard" sub-types such as generic params # or other type classes tfHasGCedMem, # type contains GC'ed memory + tfPacked tfHasStatic tfGenericTypeParam tfImplicitTypeParam + 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. TTypeFlags* = set[TTypeFlag] @@ -693,7 +709,7 @@ type TSym* {.acyclic.} = object of TIdObj # proc and type instantiations are cached in the generic symbol case kind*: TSymKind - of skType: + of skType, skGenericParam: typeInstCache*: seq[PType] typScope*: PScope of routineKinds: @@ -961,7 +977,9 @@ var emptyNode* = newNode(nkEmpty) # There is a single empty node that is shared! Do not overwrite it! proc isMetaType*(t: PType): bool = - return t.kind in tyMetaTypes or tfHasMeta in t.flags + return t.kind in tyMetaTypes or + (t.kind == tyStatic and t.n == nil) or + tfHasMeta in t.flags proc linkTo*(t: PType, s: PSym): PType {.discardable.} = t.sym = s @@ -1305,7 +1323,7 @@ proc propagateToOwner*(owner, elem: PType) = if tfShared in elem.flags: owner.flags.incl tfHasShared - if elem.kind in tyMetaTypes: + if elem.isMetaType: owner.flags.incl tfHasMeta if elem.kind in {tyString, tyRef, tySequence} or @@ -1495,6 +1513,9 @@ proc hasPattern*(s: PSym): bool {.inline.} = iterator items*(n: PNode): PNode = for i in 0.. <n.len: yield n.sons[i] +iterator pairs*(n: PNode): tuple[i: int, n: PNode] = + for i in 0.. <n.len: yield (i, n.sons[i]) + proc isAtom*(n: PNode): bool {.inline.} = result = n.kind >= nkNone and n.kind <= nkNilLit diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 321462044..319d6a5c5 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -464,24 +464,33 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope, check: var TIntSet): PRope = # declare the record: var hasField = false - let aStruct = structOrUnion(typ) + + var attribute: PRope = + if tfPacked in typ.flags: toRope(CC[ccompiler].packedPragma) + else: nil + + result = ropecg(m, CC[ccompiler].structStmtFmt, + [structOrUnion(typ), name, attribute]) + if typ.kind == tyObject: + if typ.sons[0] == nil: if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags: - result = ropecg(m, "$1 $2 {$n", [aStruct, name]) + appcg(m, result, " {$n", []) else: - result = ropecg(m, "$1 $2 {$n#TNimType* m_type;$n", [aStruct, name]) + appcg(m, result, " {$n#TNimType* m_type;$n", [name, attribute]) hasField = true elif gCmd == cmdCompileToCpp: - result = ropecg(m, "$1 $2 : public $3 {$n", - [aStruct, name, getTypeDescAux(m, typ.sons[0], check)]) + appcg(m, result, " : public $1 {$n", + [getTypeDescAux(m, typ.sons[0], check)]) hasField = true else: - result = ropecg(m, "$1 $2 {$n $3 Sup;$n", - [aStruct, name, getTypeDescAux(m, typ.sons[0], check)]) + appcg(m, result, " {$n $1 Sup;$n", + [getTypeDescAux(m, typ.sons[0], check)]) hasField = true else: - result = ropef("$1 $2 {$n", [aStruct, name]) + appf(result, " {$n", [name]) + var desc = getRecordFields(m, typ, check) if (desc == nil) and not hasField: appf(result, "char dummy;$n", []) diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index da1673ca4..1d8f0158b 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -87,9 +87,10 @@ proc getUniqueType*(key: PType): PType = gCanonicalTypes[k] = key result = key of tyTypeDesc, tyTypeClasses, tyGenericParam, - tyFromExpr, tyStatic, tyFieldAccessor: + tyFromExpr, tyFieldAccessor: internalError("GetUniqueType") - of tyGenericInst, tyDistinct, tyOrdinal, tyMutable, tyConst, tyIter: + of tyGenericInst, tyDistinct, tyOrdinal, tyMutable, + tyConst, tyIter, tyStatic: result = getUniqueType(lastSon(key)) of tyArrayConstr, tyGenericInvokation, tyGenericBody, tyOpenArray, tyArray, tySet, tyRange, tyTuple, diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 12761f1d4..f0e5dad11 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -16,7 +16,7 @@ import type TSystemCC* = enum ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc, - ccTcc, ccPcc, ccUcc, ccIcl, ccGpp + ccTcc, ccPcc, ccUcc, ccIcl TInfoCCProp* = enum # properties of the C compiler: hasSwitchRange, # CC allows ranges in switch statements (GNU C) hasComputedGoto, # CC has computed goto (GNU C extension) @@ -33,11 +33,12 @@ type optSpeed: string, # the options for optimization for speed optSize: string, # the options for optimization for size compilerExe: string, # the compiler's executable + cppCompiler: string, # name of the C++ compiler's executable (if supported) compileTmpl: string, # the compile command template buildGui: string, # command to build a GUI application buildDll: string, # command to build a shared library buildLib: string, # command to build a static library - linkerExe: string, # the linker's executable + linkerExe: string, # the linker's executable (if not matching compiler's) linkTmpl: string, # command to link files to produce an exe includeCmd: string, # command to add an include dir linkDirCmd: string, # command to add a lib dir @@ -46,6 +47,8 @@ type pic: string, # command for position independent code # used on some platforms asmStmtFrmt: string, # format of ASM statement + structStmtFmt: string, # Format for struct statement + packedPragma: string, # Attribute/pragma to make struct packed (1-byte aligned) props: TInfoCCProps] # properties of the C compiler @@ -63,11 +66,12 @@ compiler gcc: optSpeed: " -O3 -ffast-math ", optSize: " -Os -ffast-math ", compilerExe: "gcc", + cppCompiler: "g++", compileTmpl: "-c $options $include -o $objfile $file", buildGui: " -mwindows", buildDll: " -shared", buildLib: "ar rcs $libfile $objfiles", - linkerExe: "gcc", + linkerExe: "", linkTmpl: "$buildgui $builddll -o $exefile $objfiles $options", includeCmd: " -I", linkDirCmd: " -L", @@ -75,34 +79,25 @@ compiler gcc: debug: "", pic: "-fPIC", asmStmtFrmt: "asm($1);$n", + structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name + packedPragma: "__attribute__((__packed__))", props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm, hasNakedAttribute}) - -compiler gpp: - result = gcc() - - result.name = "gpp" - result.compilerExe = "g++" - result.linkerExe = "g++" - - result.buildDll = " -mdll" - # XXX: Hmm, I'm keeping this from the previos version, - # but my gcc doesn't even have such an option (is this mingw?) compiler llvmGcc: result = gcc() result.name = "llvm_gcc" result.compilerExe = "llvm-gcc" + result.cppCompiler = "llvm-g++" result.buildLib = "llvm-ar rcs $libfile $objfiles" - result.linkerExe = "llvm-gcc" compiler clang: result = llvmGcc() result.name = "clang" result.compilerExe = "clang" - result.linkerExe = "clang" + result.cppCompiler = "clang++" compiler vcc: result = ( @@ -111,6 +106,7 @@ compiler vcc: optSpeed: " /Ogityb2 /G7 /arch:SSE2 ", optSize: " /O1 /G7 ", compilerExe: "cl", + cppCompiler: "cl", compileTmpl: "/c $options $include /Fo$objfile $file", buildGui: " /link /SUBSYSTEM:WINDOWS ", buildDll: " /LD", @@ -123,6 +119,8 @@ compiler vcc: debug: " /GZ /Zi ", pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", + structStmtFmt: "$3$n$1 $2", + packedPragma: "#pragma pack(1)", props: {hasCpp, hasAssume, hasNakedDeclspec}) compiler icl: @@ -131,7 +129,7 @@ compiler icl: result = vcc() else: result = gcc() - + result.name = "icl" result.compilerExe = "icl" result.linkerExe = "icl" @@ -143,6 +141,7 @@ compiler lcc: optSpeed: " -O -p6 ", optSize: " -O -p6 ", compilerExe: "lcc", + cppCompiler: "", compileTmpl: "$options $include -Fo$objfile $file", buildGui: " -subsystem windows", buildDll: " -dll", @@ -155,6 +154,8 @@ compiler lcc: debug: " -g5 ", pic: "", asmStmtFrmt: "_asm{$n$1$n}$n", + structStmtFmt: "$1 $2", + packedPragma: "", # XXX: not supported yet props: {}) compiler bcc: @@ -164,6 +165,7 @@ compiler bcc: optSpeed: " -O2 -6 ", optSize: " -O1 -6 ", compilerExe: "bcc32", + cppCompiler: "", compileTmpl: "-c $options $include -o$objfile $file", buildGui: " -tW", buildDll: " -tWD", @@ -176,6 +178,8 @@ compiler bcc: debug: "", pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", + structStmtFmt: "$1 $2", + packedPragma: "", # XXX: not supported yet props: {hasCpp}) compiler dmc: @@ -185,6 +189,7 @@ compiler dmc: optSpeed: " -ff -o -6 ", optSize: " -ff -o -6 ", compilerExe: "dmc", + cppCompiler: "", compileTmpl: "-c $options $include -o$objfile $file", buildGui: " -L/exet:nt/su:windows", buildDll: " -WD", @@ -197,6 +202,8 @@ compiler dmc: debug: " -g ", pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", + structStmtFmt: "$3$n$1 $2", + packedPragma: "#pragma pack(1)", props: {hasCpp}) compiler wcc: @@ -206,6 +213,7 @@ compiler wcc: optSpeed: " -ox -on -6 -d0 -fp6 -zW ", optSize: "", compilerExe: "wcl386", + cppCompiler: "", compileTmpl: "-c $options $include -fo=$objfile $file", buildGui: " -bw", buildDll: " -bd", @@ -218,6 +226,8 @@ compiler wcc: debug: " -d2 ", pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", + structStmtFmt: "$1 $2", + packedPragma: "", # XXX: not supported yet props: {hasCpp}) compiler tcc: @@ -227,6 +237,7 @@ compiler tcc: optSpeed: "", optSize: "", compilerExe: "tcc", + cppCompiler: "", compileTmpl: "-c $options $include -o $objfile $file", buildGui: "UNAVAILABLE!", buildDll: " -shared", @@ -239,6 +250,8 @@ compiler tcc: debug: " -g ", pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", + structStmtFmt: "$1 $2", + packedPragma: "", # XXX: not supported yet props: {hasSwitchRange, hasComputedGoto}) compiler pcc: @@ -249,6 +262,7 @@ compiler pcc: optSpeed: " -Ox ", optSize: " -Os ", compilerExe: "cc", + cppCompiler: "", compileTmpl: "-c $options $include -Fo$objfile $file", buildGui: " -SUBSYSTEM:WINDOWS", buildDll: " -DLL", @@ -261,6 +275,8 @@ compiler pcc: debug: " -Zi ", pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", + structStmtFmt: "$1 $2", + packedPragma: "", # XXX: not supported yet props: {}) compiler ucc: @@ -270,6 +286,7 @@ compiler ucc: optSpeed: " -O3 ", optSize: " -O1 ", compilerExe: "cc", + cppCompiler: "", compileTmpl: "-c $options $include -o $objfile $file", buildGui: "", buildDll: " -shared ", @@ -282,6 +299,8 @@ compiler ucc: debug: "", pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", + structStmtFmt: "$1 $2", + packedPragma: "", # XXX: not supported yet props: {}) const @@ -297,8 +316,7 @@ const tcc(), pcc(), ucc(), - icl(), - gpp()] + icl()] const hExt* = ".h" @@ -471,11 +489,21 @@ proc needsExeExt(): bool {.inline.} = result = (optGenScript in gGlobalOptions and targetOS == osWindows) or (platform.hostOS == osWindows) +proc getCompilerExe(compiler: TSystemCC): string = + result = if gCmd == cmdCompileToCpp: CC[compiler].cppCompiler + else: CC[compiler].compilerExe + if result.len == 0: + rawMessage(errCompilerDoesntSupportTarget, CC[compiler].name) + +proc getLinkerExe(compiler: TSystemCC): string = + result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe + else: compiler.getCompilerExe + proc getCompileCFileCmd*(cfilename: string, isExternal = false): string = var c = cCompiler var options = cFileSpecificOptions(cfilename) var exe = getConfigVar(c, ".exe") - if exe.len == 0: exe = CC[c].compilerExe + if exe.len == 0: exe = c.getCompilerExe if needsExeExt(): exe = addFileExt(exe, "exe") if optGenDynLib in gGlobalOptions and @@ -493,7 +521,7 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string = compilePattern = joinPath(ccompilerpath, exe) else: includeCmd = "" - compilePattern = CC[c].compilerExe + compilePattern = c.getCompilerExe var cfile = if noAbsolutePaths(): extractFilename(cfilename) else: cfilename @@ -600,7 +628,7 @@ proc callCCompiler*(projectfile: string) = if optCompileOnly notin gGlobalOptions: execExternalProgram(linkCmd) else: var linkerExe = getConfigVar(c, ".linkerexe") - if len(linkerExe) == 0: linkerExe = CC[c].linkerExe + if len(linkerExe) == 0: linkerExe = c.getLinkerExe if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe") if noAbsolutePaths(): linkCmd = quoteShell(linkerExe) else: linkCmd = quoteShell(joinPath(ccompilerpath, linkerExe)) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 67cd364dc..4bc8eff86 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -518,7 +518,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode) = else: internalError(it.info, "transformOuter") of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, - nkClosure: + nkClosure, nkTypeSection: # don't recurse here: # XXX recurse here and setup 'up' pointers discard diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 9c6c5e22f..217e33675 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -101,6 +101,7 @@ type base10, # base10 is listed as the first element, # so that it is the correct default value base2, base8, base16 + TToken* = object # a Nimrod token tokType*: TTokType # the type of the token indent*: int # the indentation; != -1 if the token has been @@ -121,8 +122,9 @@ type indentAhead*: int # if > 0 an indendation has already been read # this is needed because scanning comments # needs so much look-ahead + currLineIndent*: int strongSpaces*: bool - + var gLinesCompiled*: int # all lines that have been compiled @@ -176,6 +178,7 @@ proc prettyTok*(tok: TToken): string = else: result = tokToStr(tok) proc printTok*(tok: TToken) = + write(stdout, tok.line, ":", tok.col, "\t") write(stdout, TokTypeToStr[tok.tokType]) write(stdout, " ") writeln(stdout, tokToStr(tok)) @@ -206,6 +209,7 @@ proc openLexer(lex: var TLexer, fileIdx: int32, inputstream: PLLStream) = openBaseLexer(lex, inputstream) lex.fileIdx = fileidx lex.indentAhead = - 1 + lex.currLineIndent = 0 inc(lex.lineNumber, inputstream.lineOffset) proc closeLexer(lex: var TLexer) = @@ -709,6 +713,7 @@ proc skip(L: var TLexer, tok: var TToken) = tok.strongSpaceA = 0 if buf[pos] > ' ': tok.indent = indent + L.currLineIndent = indent break else: break # EndOfFile also leaves the loop @@ -718,6 +723,7 @@ proc rawGetTok(L: var TLexer, tok: var TToken) = fillToken(tok) if L.indentAhead >= 0: tok.indent = L.indentAhead + L.currLineIndent = L.indentAhead L.indentAhead = -1 else: tok.indent = -1 @@ -827,5 +833,5 @@ proc rawGetTok(L: var TLexer, tok: var TToken) = tok.tokType = tkInvalid lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')') inc(L.bufpos) - + dummyIdent = getIdent("") diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 8239f2a47..60125177c 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -127,7 +127,10 @@ proc ensureNoMissingOrUnusedSymbols(scope: PScope) = elif {sfUsed, sfExported} * s.flags == {} and optHints in s.options: # BUGFIX: check options in s! if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}: - message(s.info, hintXDeclaredButNotUsed, getSymRepr(s)) + # XXX: implicit type params are currently skTypes + # maybe they can be made skGenericParam as well. + if s.typ != nil and tfImplicitTypeParam notin s.typ.flags: + message(s.info, hintXDeclaredButNotUsed, getSymRepr(s)) s = nextIter(it, scope.symbols) proc wrongRedefinition*(info: TLineInfo, s: string) = diff --git a/compiler/msgs.nim b/compiler/msgs.nim index c75876843..cdafc997b 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -106,6 +106,9 @@ type errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX, errXCannotBeClosure, errXMustBeCompileTime, errCannotInferTypeOfTheLiteral, + errCannotInferReturnType, + errGenericLambdaNotAllowed, + errCompilerDoesntSupportTarget, errUser, warnCannotOpenFile, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, @@ -355,6 +358,11 @@ const errXCannotBeClosure: "'$1' cannot have 'closure' calling convention", errXMustBeCompileTime: "'$1' can only be used in compile-time context", errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1", + errCannotInferReturnType: "cannot infer the return type of the proc", + errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " & + "it is used as an operand to another routine and the types " & + "of the generic paramers can be infered from the expected signature.", + errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target", errUser: "$1", warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", diff --git a/compiler/parser.nim b/compiler/parser.nim index cfba89f4a..d59e013d7 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -67,7 +67,7 @@ proc optPar*(p: var TParser) proc optInd*(p: var TParser, n: PNode) proc indAndComment*(p: var TParser, n: PNode) proc setBaseFlags*(n: PNode, base: TNumericalBase) -proc parseSymbol*(p: var TParser): PNode +proc parseSymbol*(p: var TParser, allowNil = false): PNode proc parseTry(p: var TParser): PNode proc parseCase(p: var TParser): PNode # implementation @@ -273,7 +273,7 @@ proc colcom(p: var TParser, n: PNode) = eat(p, tkColon) skipComment(p, n) -proc parseSymbol(p: var TParser): PNode = +proc parseSymbol(p: var TParser, allowNil = false): PNode = #| symbol = '`' (KEYW|IDENT|operator|'(' ')'|'[' ']'|'{' '}'|'='|literal)+ '`' #| | IDENT case p.tok.tokType @@ -312,9 +312,13 @@ proc parseSymbol(p: var TParser): PNode = break eat(p, tkAccent) else: - parMessage(p, errIdentifierExpected, p.tok) - getTok(p) # BUGFIX: We must consume a token here to prevent endless loops! - result = ast.emptyNode + if allowNil and p.tok.tokType == tkNil: + result = newNodeP(nkNilLit, p) + getTok(p) + else: + parMessage(p, errIdentifierExpected, p.tok) + getTok(p) # BUGFIX: We must consume a token here to prevent endless loops! + result = ast.emptyNode proc indexExpr(p: var TParser): PNode = #| indexExpr = expr @@ -658,7 +662,7 @@ proc namedParams(p: var TParser, callee: PNode, exprColonEqExprListAux(p, endTok, result) proc parseMacroColon(p: var TParser, x: PNode): PNode -proc primarySuffix(p: var TParser, r: PNode): PNode = +proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks? #| | doBlocks #| | '.' optInd ('type' | 'addr' | symbol) generalizedLit? @@ -666,7 +670,8 @@ proc primarySuffix(p: var TParser, r: PNode): PNode = #| | '{' optInd indexExprList optPar '}' #| | &( '`'|IDENT|literal|'cast') expr # command syntax result = r - while p.tok.indent < 0: + while p.tok.indent < 0 or + (p.tok.tokType == tkDot and p.tok.indent >= baseIndent): case p.tok.tokType of tkParLe: if p.strongSpaces and p.tok.strongSpaceA > 0: break @@ -713,9 +718,11 @@ proc primarySuffix(p: var TParser, r: PNode): PNode = break proc primary(p: var TParser, mode: TPrimaryMode): PNode +proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode -proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode = - result = primary(p, mode) +proc parseOperators(p: var TParser, headNode: PNode, + limit: int, mode: TPrimaryMode): PNode = + result = headNode # expand while operators have priorities higher than 'limit' var opPrec = getPrecedence(p.tok, p.strongSpaces) let modeB = if mode == pmTypeDef: pmTypeDesc else: mode @@ -734,6 +741,10 @@ proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode = addSon(a, b) result = a opPrec = getPrecedence(p.tok, p.strongSpaces) + +proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode = + result = primary(p, mode) + result = parseOperators(p, result, limit, mode) proc simpleExpr(p: var TParser, mode = pmNormal): PNode = result = simpleExprAux(p, -1, mode) @@ -957,14 +968,30 @@ proc isExprStart(p: TParser): bool = tkTuple, tkObject, tkType, tkWhen, tkCase, tkShared: result = true else: result = false - -proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, - mode: TPrimaryMode): PNode = + +proc parseSymbolList(p: var TParser, result: PNode, allowNil = false) = + while true: + var s = parseSymbol(p, allowNil) + if s.kind == nkEmpty: break + addSon(result, s) + if p.tok.tokType != tkComma: break + getTok(p) + optInd(p, s) + +proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, + mode: TPrimaryMode): PNode = result = newNodeP(kind, p) getTok(p) optInd(p, result) if not isOperator(p.tok) and isExprStart(p): addSon(result, primary(p, mode)) + if kind == nkDistinctTy and p.tok.tokType in {tkWith, tkWithout}: + let nodeKind = if p.tok.tokType == tkWith: nkWith + else: nkWithout + getTok(p) + let list = newNodeP(nodeKind, p) + result.addSon list + parseSymbolList(p, list, allowNil = true) proc parseExpr(p: var TParser): PNode = #| expr = (ifExpr @@ -981,7 +1008,6 @@ proc parseExpr(p: var TParser): PNode = proc parseEnum(p: var TParser): PNode proc parseObject(p: var TParser): PNode -proc parseDistinct(p: var TParser): PNode proc parseTypeClass(p: var TParser): PNode proc primary(p: var TParser, mode: TPrimaryMode): PNode = @@ -1001,8 +1027,9 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = optInd(p, a) if isSigil: #XXX prefix operators + let baseInd = p.lex.currLineIndent addSon(result, primary(p, pmSkipSuffix)) - result = primarySuffix(p, result) + result = primarySuffix(p, result, baseInd) else: addSon(result, primary(p, pmNormal)) return @@ -1065,9 +1092,10 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = optInd(p, result) addSon(result, primary(p, pmNormal)) else: + let baseInd = p.lex.currLineIndent result = identOrLiteral(p, mode) if mode != pmSkipSuffix: - result = primarySuffix(p, result) + result = primarySuffix(p, result, baseInd) proc parseTypeDesc(p: var TParser): PNode = #| typeDesc = simpleExpr @@ -1501,7 +1529,7 @@ proc parseSection(p: var TParser, kind: TNodeKind, defparser: TDefParser): PNode = #| section(p) = COMMENT? p / (IND{>} (p / COMMENT)^+IND{=} DED) result = newNodeP(kind, p) - getTok(p) + if kind != nkTypeSection: getTok(p) skipComment(p, result) if realInd(p): withInd(p): @@ -1734,13 +1762,6 @@ proc parseTypeClass(p: var TParser): PNode = else: addSon(result, parseStmt(p)) -proc parseDistinct(p: var TParser): PNode = - #| distinct = 'distinct' optInd typeDesc - result = newNodeP(nkDistinctTy, p) - getTok(p) - optInd(p, result) - addSon(result, parseTypeDesc(p)) - proc parseTypeDef(p: var TParser): PNode = #| typeDef = identWithPragma genericParamList? '=' optInd typeDefAux #| indAndComment? @@ -1862,7 +1883,16 @@ proc complexOrSimpleStmt(p: var TParser): PNode = of tkMacro: result = parseRoutine(p, nkMacroDef) of tkTemplate: result = parseRoutine(p, nkTemplateDef) of tkConverter: result = parseRoutine(p, nkConverterDef) - of tkType: result = parseSection(p, nkTypeSection, parseTypeDef) + of tkType: + getTok(p) + if p.tok.tokType == tkParLe: + getTok(p) + result = newNodeP(nkTypeOfExpr, p) + result.addSon(primary(p, pmTypeDesc)) + eat(p, tkParRi) + result = parseOperators(p, result, -1, pmNormal) + else: + result = parseSection(p, nkTypeSection, parseTypeDef) of tkConst: result = parseSection(p, nkConstSection, parseConstant) of tkLet: result = parseSection(p, nkLetSection, parseVariable) of tkWhen: result = parseIfOrWhen(p, nkWhenStmt) @@ -1886,7 +1916,7 @@ proc parseStmt(p: var TParser): PNode = if p.tok.indent < 0 or p.tok.indent == p.currInd: discard else: break else: - if p.tok.indent > p.currInd: + if p.tok.indent > p.currInd and p.tok.tokType != tkDot: parMessage(p, errInvalidIndentation) break if p.tok.tokType in {tkCurlyRi, tkParRi, tkCurlyDotRi, tkBracketRi}: @@ -1913,7 +1943,8 @@ proc parseStmt(p: var TParser): PNode = else: result = newNodeP(nkStmtList, p) while true: - if p.tok.indent >= 0: parMessage(p, errInvalidIndentation) + if p.tok.indent >= 0: + parMessage(p, errInvalidIndentation) let a = simpleStmt(p) if a.kind == nkEmpty: parMessage(p, errExprExpected, p.tok) result.add(a) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index fba4e7657..efd06eef4 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -52,7 +52,7 @@ const typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl, wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow, wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef, - wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion} + wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked} fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, wImportCpp, wImportObjC, wError} varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, @@ -655,6 +655,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, incl(sym.flags, sfThread) incl(sym.flags, sfProcvar) if sym.typ != nil: incl(sym.typ.flags, tfThread) + of wPacked: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfPacked) of wHint: message(it.info, hintUser, expectStrLit(c, it)) of wWarning: message(it.info, warnUser, expectStrLit(c, it)) of wError: diff --git a/compiler/renderer.nim b/compiler/renderer.nim index d68cb91c0..2d2310914 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -424,8 +424,11 @@ proc lsub(n: PNode): int = of nkRefTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ref") of nkPtrTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ptr") of nkVarTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("var") - of nkDistinctTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + - len("Distinct") + of nkDistinctTy: + result = len("distinct") + (if n.len > 0: lsub(n.sons[0])+1 else: 0) + if n.len > 1: + result += (if n[1].kind == nkWith: len("_with_") else: len("_without_")) + result += lcomma(n[1]) of nkStaticTy: result = (if n.len > 0: lsub(n.sons[0]) else: 0) + len("static[]") of nkTypeDef: result = lsons(n) + 3 @@ -1020,9 +1023,15 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = else: put(g, tkVar, "var") of nkDistinctTy: - if sonsLen(n) > 0: + if n.len > 0: putWithSpace(g, tkDistinct, "distinct") gsub(g, n.sons[0]) + if n.len > 1: + if n[1].kind == nkWith: + putWithSpace(g, tkWith, " with") + else: + putWithSpace(g, tkWithout, " without") + gcomma(g, n[1]) else: put(g, tkDistinct, "distinct") of nkTypeDef: diff --git a/compiler/sem.nim b/compiler/sem.nim index 093fc9452..c35cff027 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -66,6 +66,9 @@ proc fitNode(c: PContext, formal: PType, arg: PNode): PNode = result = copyTree(arg) result.typ = formal +proc inferWithMetatype(c: PContext, formal: PType, + arg: PNode, coerceDistincts = false): PNode + var commonTypeBegin = PType(kind: tyExpr) proc commonType*(x, y: PType): PType = diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 5d480bc98..b994a94d7 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -228,12 +228,25 @@ proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode = if m.genericConverter and result != nil: instGenericConvertersArg(c, result, m) -proc convertTo*(c: PContext, f: PType, n: PNode): PNode = +proc inferWithMetatype(c: PContext, formal: PType, + arg: PNode, coerceDistincts = false): PNode = var m: TCandidate - initCandidate(c, m, f) - result = paramTypesMatch(m, f, n.typ, n, nil) + initCandidate(c, m, formal) + m.coerceDistincts = coerceDistincts + result = paramTypesMatch(m, formal, arg.typ, arg, nil) if m.genericConverter and result != nil: instGenericConvertersArg(c, result, m) + if result != nil: + # This almost exactly replicates the steps taken by the compiler during + # param matching. It performs an embarassing ammount of back-and-forth + # type jugling, but it's the price to pay for consistency and correctness + result.typ = generateTypeInstance(c, m.bindings, arg.info, + formal.skipTypes({tyCompositeTypeClass})) + else: + typeMismatch(arg, formal, arg.typ) + # error correction: + result = copyTree(arg) + result.typ = formal proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = assert x.state == csMatch diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 84e017050..5ec66b51c 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -42,7 +42,7 @@ type TExprFlag* = enum efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType, - efAllowDestructor, efWantValue + efAllowDestructor, efWantValue, efOperand TExprFlags* = set[TExprFlag] PContext* = ref TContext @@ -251,6 +251,27 @@ proc makeNotType*(c: PContext, t1: PType): PType = propagateToOwner(result, t1) result.flags.incl(t1.flags * {tfHasStatic}) +proc nMinusOne*(n: PNode): PNode = + result = newNode(nkCall, n.info, @[ + newSymNode(getSysMagic("<", mUnaryLt)), + n]) + +# Remember to fix the procs below this one when you make changes! +proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType = + let intType = getSysType tyInt + result = newTypeS(tyRange, c) + result.sons = @[intType] + result.n = newNode(nkRange, n.info, @[ + newIntTypeNode(nkIntLit, 0, intType), + makeStaticExpr(c, n.nMinusOne)]) + +template rangeHasStaticIf*(t: PType): bool = + # this accepts the ranges's node + t.n[1].kind == nkStaticExpr + +template getStaticTypeFromRange*(t: PType): PType = + t.n[1][0][1].typ + proc newTypeS(kind: TTypeKind, c: PContext): PType = result = newType(kind, getCurrOwner()) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 62a816a50..b3b757640 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -21,7 +21,7 @@ proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # same as 'semExprWithType' but doesn't check for proc vars - result = semExpr(c, n, flags) + result = semExpr(c, n, flags + {efOperand}) if result.kind == nkEmpty: # do not produce another redundant error message: #raiseRecoverableError("") @@ -117,10 +117,12 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = elif s.ast != nil: result = semExpr(c, s.ast) else: - internalError(n.info, "no default for") - result = emptyNode + n.typ = s.typ + return n of skType: markUsed(n, s) + if s.typ.kind == tyStatic and s.typ.n != nil: + return s.typ.n result = newSymNode(s, n.info) result.typ = makeTypeDesc(c, s.typ) else: @@ -191,15 +193,35 @@ proc isCastable(dst, src: PType): bool = proc isSymChoice(n: PNode): bool {.inline.} = result = n.kind in nkSymChoices +proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) = + # XXX: liftParamType started to perform addDecl + # we could do that instead in semTypeNode by snooping for added + # gnrc. params, then it won't be necessary to open a new scope here + openScope(c) + var lifted = liftParamType(c, skType, newNodeI(nkArgList, info), + t, ":anon", info) + closeScope(c) + if lifted != nil: t = lifted + proc semConv(c: PContext, n: PNode): PNode = if sonsLen(n) != 2: localError(n.info, errConvNeedsOneArg) return n + result = newNodeI(nkConv, n.info) - result.typ = semTypeNode(c, n.sons[0], nil).skipTypes({tyGenericInst}) - addSon(result, copyTree(n.sons[0])) - addSon(result, semExprWithType(c, n.sons[1])) - var op = result.sons[1] + var targetType = semTypeNode(c, n.sons[0], nil) + maybeLiftType(targetType, c, n[0].info) + result.addSon copyTree(n.sons[0]) + var op = semExprWithType(c, n.sons[1]) + + if targetType.isMetaType: + let final = inferWithMetatype(c, targetType, op, true) + result.addSon final + result.typ = final.typ + return + + result.typ = targetType + addSon(result, op) if not isSymChoice(op): let status = checkConvertible(c, result.typ, op.typ) @@ -221,7 +243,7 @@ proc semConv(c: PContext, n: PNode): PNode = for i in countup(0, sonsLen(op) - 1): let it = op.sons[i] let status = checkConvertible(c, result.typ, it.typ) - if status == convOK: + if status in {convOK, convNotNeedeed}: markUsed(n, it.sym) markIndirect(c, it.sym) return it @@ -325,9 +347,7 @@ proc isOpImpl(c: PContext, n: PNode): PNode = tfIterator notin t.flags)) else: var t2 = n[2].typ.skipTypes({tyTypeDesc}) - let lifted = liftParamType(c, skType, newNodeI(nkArgList, n.info), - t2, ":anon", n.info) - if lifted != nil: t2 = lifted + maybeLiftType(t2, c, n.info) var m: TCandidate initCandidate(c, m, t2) let match = typeRel(m, t2, t1) != isNone @@ -612,7 +632,19 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = if result.isNil: result = n else: return result result.typ = semfold.getIntervalType(callee.magic, call) - + + block maybeLabelAsStatic: + # XXX: temporary work-around needed for tlateboundstatic. + # This is certainly not correct, but it will get the job + # done until we have a more robust infrastructure for + # implicit statics. + if n.len > 1: + for i in 1 .. <n.len: + if n[i].typ.kind != tyStatic or tfUnresolved notin n[i].typ.flags: + break maybeLabelAsStatic + n.typ = newTypeWithSons(c, tyStatic, @[n.typ]) + n.typ.flags.incl tfUnresolved + # optimization pass: not necessary for correctness of the semantic pass if {sfNoSideEffect, sfCompileTime} * callee.flags != {} and {sfForward, sfImportc} * callee.flags == {}: @@ -899,10 +931,15 @@ proc makeDeref(n: PNode): PNode = addSon(result, a) t = skipTypes(t.sons[0], {tyGenericInst}) -proc readTypeParameter(c: PContext, ty: PType, +const + tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass} + tyDotOpTransparent = {tyVar, tyPtr, tyRef} + +proc readTypeParameter(c: PContext, typ: PType, paramName: PIdent, info: TLineInfo): PNode = - internalAssert ty.kind == tyGenericInst - let ty = ty.skipGenericAlias + let ty = if typ.kind == tyGenericInst: typ.skipGenericAlias + else: (internalAssert(typ.kind == tyCompositeTypeClass); typ.sons[1]) + let tbody = ty.sons[0] for s in countup(0, tbody.len-2): let tParam = tbody.sons[s] @@ -933,6 +970,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result = nil if isTypeExpr(n.sons[0]) or (ty.kind == tyTypeDesc and ty.base.kind != tyNone): if ty.kind == tyTypeDesc: ty = ty.base + ty = ty.skipTypes(tyDotOpTransparent) case ty.kind of tyEnum: # look up if the identifier belongs to the enum: @@ -946,7 +984,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result.typ = ty markUsed(n, f) return - of tyGenericInst: + of tyTypeParamsHolders: return readTypeParameter(c, ty, i, n.info) of tyObject, tyTuple: if ty.n.kind == nkRecList: @@ -996,8 +1034,10 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result = n # we didn't find any field, let's look for a generic param - if result == nil and n.sons[0].typ.kind == tyGenericInst: - result = readTypeParameter(c, n.sons[0].typ, i, n.info) + if result == nil: + let t = n.sons[0].typ.skipTypes(tyDotOpTransparent) + if t.kind in tyTypeParamsHolders: + result = readTypeParameter(c, t, i, n.info) proc dotTransformation(c: PContext, n: PNode): PNode = if isSymChoice(n.sons[1]): @@ -1182,7 +1222,7 @@ proc semAsgn(c: PContext, n: PNode): PNode = if lhsIsResult: {efAllowDestructor} else: {}) if lhsIsResult: n.typ = enforceVoidContext - if resultTypeIsInferrable(lhs.sym.typ): + if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ): if cmpTypes(c, lhs.typ, rhs.typ) == isGeneric: internalAssert c.p.resultSym != nil lhs.typ = rhs.typ @@ -1218,6 +1258,7 @@ proc semReturn(c: PContext, n: PNode): PNode = proc semProcBody(c: PContext, n: PNode): PNode = openScope(c) + result = semExpr(c, n) if c.p.resultSym != nil and not isEmptyType(result.typ): # transform ``expr`` to ``result = expr``, but not if the expr is already @@ -1241,6 +1282,11 @@ proc semProcBody(c: PContext, n: PNode): PNode = result = semAsgn(c, a) else: discardCheck(c, result) + + if c.p.owner.kind notin {skMacro, skTemplate} and + c.p.resultSym != nil and c.p.resultSym.typ.isMetaType: + localError(c.p.resultSym.info, errCannotInferReturnType) + closeScope(c) proc semYieldVarResult(c: PContext, n: PNode, restype: PType) = diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 4bcfa7f15..a5149a842 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -15,8 +15,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, if n.kind != nkGenericParams: internalError(n.info, "instantiateGenericParamList; no generic params") newSeq(entry.concreteTypes, n.len) - for i in countup(0, n.len - 1): - var a = n.sons[i] + for i, a in n.pairs: if a.kind != nkSym: internalError(a.info, "instantiateGenericParamList; no symbol") var q = a.sym @@ -86,19 +85,21 @@ proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) = proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) +proc addProcDecls(c: PContext, fn: PSym) = + # get the proc itself in scope (e.g. for recursion) + addDecl(c, fn) + + for i in 1 .. <fn.typ.n.len: + var param = fn.typ.n.sons[i].sym + param.owner = fn + addParamOrResult(c, param, fn.kind) + + maybeAddResult(c, fn, fn.ast) + proc instantiateBody(c: PContext, n: PNode, result: PSym) = if n.sons[bodyPos].kind != nkEmpty: inc c.inGenericInst # add it here, so that recursive generic procs are possible: - addDecl(c, result) - pushProcCon(c, result) - # add params to scope - for i in 1 .. <result.typ.n.len: - var param = result.typ.n.sons[i].sym - param.owner = result - addParamOrResult(c, param, result.kind) - # debug result.typ.n - maybeAddResult(c, result, n) var b = n.sons[bodyPos] var symMap: TIdTable initIdTable symMap @@ -108,7 +109,6 @@ proc instantiateBody(c: PContext, n: PNode, result: PSym) = n.sons[bodyPos] = transformBody(c.module, b, result) #echo "code instantiated ", result.name.s excl(result.flags, sfForward) - popProcCon(c) dec c.inGenericInst proc fixupInstantiatedSymbols(c: PContext, s: PSym) = @@ -145,11 +145,56 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, proc instGenericContainer(c: PContext, n: PNode, header: PType): PType = result = instGenericContainer(c, n.info, header) +proc instantiateProcType(c: PContext, pt: TIdTable, + prc: PSym, info: TLineInfo) = + # XXX: Instantiates a generic proc signature, while at the same + # time adding the instantiated proc params into the current scope. + # This is necessary, because the instantiation process may refer to + # these params in situations like this: + # proc foo[Container](a: Container, b: a.type.Item): type(b.x) + # + # Alas, doing this here is probably not enough, because another + # proc signature could appear in the params: + # proc foo[T](a: proc (x: T, b: type(x.y)) + # + # The solution would be to move this logic into semtypinst, but + # at this point semtypinst have to become part of sem, because it + # will need to use openScope, addDecl, etc + # + addDecl(c, prc) + + pushInfoContext(info) + var cl = initTypeVars(c, pt, info) + var result = instCopyType(cl, prc.typ) + let originalParams = result.n + result.n = originalParams.shallowCopy + + for i in 1 .. <result.len: + result.sons[i] = replaceTypeVarsT(cl, result.sons[i]) + propagateToOwner(result, result.sons[i]) + let param = replaceTypeVarsN(cl, originalParams[i]) + result.n.sons[i] = param + if param.kind == nkSym: + # XXX: this won't be true for void params + # implement pass-through of void params and + # the "sort by distance to point" container + param.sym.owner = prc + addDecl(c, param.sym) + + result.sons[0] = replaceTypeVarsT(cl, result.sons[0]) + result.n.sons[0] = originalParams[0].copyTree + + eraseVoidParams(result) + skipIntLiteralParams(result) + + prc.typ = result + maybeAddResult(c, prc, prc.ast) + popInfoContext() + proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, info: TLineInfo): PSym = # no need to instantiate generic templates/macros: if fn.kind in {skTemplate, skMacro}: return fn - # generates an instantiated proc if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep") inc(c.instCounter) @@ -173,7 +218,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, var entry = TInstantiation.new entry.sym = result instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[]) - result.typ = generateTypeInstance(c, pt, info, fn.typ) + pushProcCon(c, result) + instantiateProcType(c, pt, result, info) n.sons[genericParamsPos] = ast.emptyNode var oldPrc = genericCacheGet(fn, entry[]) if oldPrc == nil: @@ -183,12 +229,12 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, pragma(c, result, n.sons[pragmasPos], allRoutinePragmas) if isNil(n.sons[bodyPos]): n.sons[bodyPos] = copyTree(fn.getBody) - if fn.kind != skTemplate: - instantiateBody(c, n, result) - sideEffectsCheck(c, result) + instantiateBody(c, n, result) + sideEffectsCheck(c, result) paramsTypeCheck(c, result.typ) else: result = oldPrc + popProcCon(c) popInfoContext() closeScope(c) # close scope for parameters popOwner() diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index edce7c9bd..38854bc8a 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -12,9 +12,6 @@ var enforceVoidContext = PType(kind: tyStmt) -proc semCommand(c: PContext, n: PNode): PNode = - result = semExprNoType(c, n) - proc semDiscard(c: PContext, n: PNode): PNode = result = n checkSonsLen(n, 1) @@ -133,6 +130,7 @@ proc fixNilType(n: PNode) = n.typ = nil proc discardCheck(c: PContext, result: PNode) = + if c.inTypeClass > 0: return if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}: if result.kind == nkNilLit: result.typ = nil @@ -143,11 +141,6 @@ proc discardCheck(c: PContext, result: PNode) = while n.kind in skipForDiscardable: n = n.lastSon n.typ = nil - elif c.inTypeClass > 0: - if result.typ.kind == tyBool: - let verdict = semConstExpr(c, result) - if verdict.intVal == 0: - localError(result.info, "type class predicate failed") elif result.typ.kind != tyError and gCmd != cmdInteractive: if result.typ.kind == tyNil: fixNilType(result) @@ -944,13 +937,15 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s) #if efDetermineType notin flags: # XXX not good enough; see tnamedparamanonproc.nim - if n.sons[genericParamsPos].kind == nkEmpty: + if gp.len == 0 or (gp.len == 1 and tfRetType in gp[0].typ.flags): pushProcCon(c, s) addResult(c, s.typ.sons[0], n.info, skProc) let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) n.sons[bodyPos] = transformBody(c.module, semBody, s) addResultNode(c, n) popProcCon(c) + elif efOperand notin flags: + localError(n.info, errGenericLambdaNotAllowed) sideEffectsCheck(c, s) else: localError(n.info, errImplOfXexpected, s.name.s) @@ -1322,13 +1317,17 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = return else: n.sons[i] = semExpr(c, n.sons[i]) + if c.inTypeClass > 0 and n[i].typ != nil and n[i].typ.kind == tyBool: + let verdict = semConstExpr(c, n[i]) + if verdict.intVal == 0: + localError(result.info, "type class predicate failed") if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]): voidContext = true n.typ = enforceVoidContext if i == last and (length == 1 or efWantValue in flags): n.typ = n.sons[i].typ if not isEmptyType(n.typ): n.kind = nkStmtListExpr - elif i != last or voidContext or c.inTypeClass > 0: + elif i != last or voidContext: discardCheck(c, n.sons[i]) else: n.typ = n.sons[i].typ diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index f91222477..c53dc0f7d 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -139,13 +139,12 @@ proc semVarType(c: PContext, n: PNode, prev: PType): PType = addSonSkipIntLit(result, base) else: result = newConstraint(c, tyVar) - + proc semDistinct(c: PContext, n: PNode, prev: PType): PType = - if sonsLen(n) == 1: - result = newOrPrevType(tyDistinct, prev, c) - addSonSkipIntLit(result, semTypeNode(c, n.sons[0], nil)) - else: - result = newConstraint(c, tyDistinct) + if n.len == 0: return newConstraint(c, tyDistinct) + result = newOrPrevType(tyDistinct, prev, c) + addSonSkipIntLit(result, semTypeNode(c, n.sons[0], nil)) + if n.len > 1: result.n = n[1] proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = assert isRange(n) @@ -198,11 +197,6 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType = localError(n.info, errXExpectsOneTypeParam, "range") result = newOrPrevType(tyError, prev, c) -proc nMinusOne(n: PNode): PNode = - result = newNode(nkCall, n.info, @[ - newSymNode(getSysMagic("<", mUnaryLt)), - n]) - proc semArray(c: PContext, n: PNode, prev: PType): PType = var indx, base: PType result = newOrPrevType(tyArray, prev, c) @@ -212,7 +206,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = else: let e = semExprWithType(c, n.sons[1], {efDetermineType}) if e.typ.kind == tyFromExpr: - indx = e.typ + indx = makeRangeWithStaticExpr(c, e.typ.n) elif e.kind in {nkIntLit..nkUInt64Lit}: indx = makeRangeType(c, 0, e.intVal-1, n.info, e.typ) elif e.kind == nkSym and e.typ.kind == tyStatic: @@ -220,7 +214,8 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = internalAssert c.inGenericContext > 0 if not isOrdinalType(e.typ.lastSon): localError(n[1].info, errOrdinalTypeExpected) - indx = e.typ + indx = makeRangeWithStaticExpr(c, e) + indx.flags.incl tfUnresolved elif e.kind in nkCallKinds and hasGenericArguments(e): if not isOrdinalType(e.typ): localError(n[1].info, errOrdinalTypeExpected) @@ -229,12 +224,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = # We are going to construct a range type that will be # properly filled-out in semtypinst (see how tyStaticExpr # is handled there). - let intType = getSysType(tyInt) - indx = newTypeS(tyRange, c) - indx.sons = @[intType] - indx.n = newNode(nkRange, n.info, @[ - newIntTypeNode(nkIntLit, 0, intType), - makeStaticExpr(c, e.nMinusOne)]) + indx = makeRangeWithStaticExpr(c, e) else: indx = e.typ.skipTypes({tyTypeDesc}) addSonSkipIntLit(result, indx) @@ -283,6 +273,18 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = result = result.typ.sym.copySym result.typ = copyType(result.typ, result.typ.owner, true) result.typ.flags.incl tfUnresolved + + if result.kind == skGenericParam: + if result.typ.kind == tyGenericParam and result.typ.len == 0 and + tfWildcard in result.typ.flags: + # collapse the wild-card param to a type + result.kind = skType + result.typ.flags.excl tfWildcard + return + else: + localError(n.info, errTypeExpected) + return errorSym(c, n) + if result.kind != skType: # this implements the wanted ``var v: V, x: V`` feature ... var ov: TOverloadIter @@ -613,17 +615,28 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = if base == nil and tfInheritable notin result.flags: incl(result.flags, tfFinal) +proc findEnforcedStaticType(t: PType): PType = + # This handles types such as `static[T] and Foo`, + # which are subset of `static[T]`, hence they could + # be treated in the same way + if t.kind == tyStatic: return t + if t.kind == tyAnd: + for s in t.sons: + let t = findEnforcedStaticType(s) + if t != nil: return t + proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = template addDecl(x) = if sfGenSym notin x.flags: addDecl(c, x) if kind == skMacro: - if param.typ.kind == tyTypeDesc: - addDecl(param) - elif param.typ.kind == tyStatic: + let staticType = findEnforcedStaticType(param.typ) + if staticType != nil: var a = copySym(param) - a.typ = param.typ.base + a.typ = staticType.base addDecl(a) + elif param.typ.kind == tyTypeDesc: + addDecl(param) else: # within a macro, every param has the type PNimrodNode! let nn = getSysSym"PNimrodNode" @@ -665,6 +678,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, s.position = genericParams.len genericParams.addSon(newSymNode(s)) result = typeClass + addDecl(c, s) # XXX: There are codegen errors if this is turned into a nested proc template liftingWalk(typ: PType, anonFlag = false): expr = @@ -688,11 +702,13 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, of tyStatic: # proc(a: expr{string}, b: expr{nkLambda}) # overload on compile time values and AST trees + if paramType.n != nil: return # this is a concrete type + if tfUnresolved in paramType.flags: return # already lifted let base = paramType.base.maybeLift if base.isMetaType and procKind == skMacro: localError(info, errMacroBodyDependsOnGenericTypes, paramName) result = addImplicitGeneric(c.newTypeWithSons(tyStatic, @[base])) - result.flags.incl tfHasStatic + result.flags.incl({tfHasStatic, tfUnresolved}) of tyTypeDesc: if tfUnresolved notin paramType.flags: @@ -737,7 +753,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result.kind = tyUserTypeClassInst result.rawAddSon paramType.lastSon return addImplicitGeneric(result) - + result = instGenericContainer(c, paramType.sym.info, result, allowMetaTypes = true) result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result]) @@ -765,7 +781,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result = paramType result.lastSon.shouldHaveMeta - let liftBody = liftingWalk(paramType.lastSon) + let liftBody = liftingWalk(paramType.lastSon, true) if liftBody != nil: result = liftBody result.shouldHaveMeta @@ -777,7 +793,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, let expanded = instGenericContainer(c, info, paramType, allowMetaTypes = true) - result = liftingWalk(expanded) + result = liftingWalk(expanded, true) of tyUserTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot: result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true)) @@ -787,12 +803,11 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result = addImplicitGeneric(newTypeS(tyAnything, c)) of tyGenericParam: - if tfGenericTypeParam in paramType.flags and false: - if paramType.sonsLen > 0: - result = liftingWalk(paramType.lastSon) - else: - result = addImplicitGeneric(newTypeS(tyAnything, c)) - + markUsed(genericParams, paramType.sym) + if tfWildcard in paramType.flags: + paramType.flags.excl tfWildcard + paramType.sym.kind = skType + else: discard # result = liftingWalk(paramType) @@ -804,8 +819,8 @@ proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType = else: result = semTypeNode(c, n, nil) -proc semProcTypeNode(c: PContext, n, genericParams: PNode, - prev: PType, kind: TSymKind): PType = +proc semProcTypeNode(c: PContext, n, genericParams: PNode, + prev: PType, kind: TSymKind): PType = var res: PNode cl: TIntSet @@ -894,6 +909,12 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, result.sons[0] = r res.typ = r + if genericParams != nil: + for n in genericParams: + if tfWildcard in n.sym.typ.flags: + n.sym.kind = skType + n.sym.typ.flags.excl tfWildcard + proc semStmtListType(c: PContext, n: PNode, prev: PType): PType = checkMinSonsLen(n, 1) var length = sonsLen(n) @@ -921,9 +942,18 @@ proc semBlockType(c: PContext, n: PNode, prev: PType): PType = proc semGenericParamInInvokation(c: PContext, n: PNode): PType = result = semTypeNode(c, n, nil) -proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = +proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = + if s.typ == nil: + localError(n.info, "cannot instantiate the '$1' $2" % + [s.name.s, ($s.kind).substr(2).toLower]) + return newOrPrevType(tyError, prev, c) + + var t = s.typ + if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody: + t = t.base + result = newOrPrevType(tyGenericInvokation, prev, c) - addSonSkipIntLit(result, s.typ) + addSonSkipIntLit(result, t) template addToResult(typ) = if typ.isNil: @@ -931,27 +961,24 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = rawAddSon(result, typ) else: addSonSkipIntLit(result, typ) - if s.typ == nil: - localError(n.info, errCannotInstantiateX, s.name.s) - return newOrPrevType(tyError, prev, c) - elif s.typ.kind == tyForward: + if t.kind == tyForward: for i in countup(1, sonsLen(n)-1): var elem = semGenericParamInInvokation(c, n.sons[i]) addToResult(elem) - elif s.typ.kind != tyGenericBody: + return + elif t.kind != tyGenericBody: #we likely got code of the form TypeA[TypeB] where TypeA is #not generic. localError(n.info, errNoGenericParamsAllowedForX, s.name.s) return newOrPrevType(tyError, prev, c) else: - - var m = newCandidate(c, s, n) + var m = newCandidate(c, t) matches(c, n, copyTree(n), m) if m.state != csMatch: - var err = "cannot instantiate " & typeToString(s.typ) & "\n" & + var err = "cannot instantiate " & typeToString(t) & "\n" & "got: (" & describeArgs(c, n) & ")\n" & - "but expected: (" & describeArgs(c, s.typ.n, 0) & ")" + "but expected: (" & describeArgs(c, t.n, 0) & ")" localError(n.info, errGenerated, err) return newOrPrevType(tyError, prev, c) @@ -961,9 +988,10 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = let typ = m.call[i].typ.skipTypes({tyTypeDesc}) if containsGenericType(typ): isConcrete = false addToResult(typ) - + if isConcrete: - if s.ast == nil: + if s.ast == nil and s.typ.kind != tyCompositeTypeClass: + # XXX: What kind of error is this? is it still relevant? localError(n.info, errCannotInstantiateX, s.name.s) result = newOrPrevType(tyError, prev, c) else: @@ -1090,13 +1118,17 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = for i in countup(1, n.len - 1): result.rawAddSon(semTypeNode(c, n.sons[i], nil)) else: result = semGeneric(c, n, s, prev) - of nkIdent, nkDotExpr, nkAccQuoted: - if n.kind == nkDotExpr: - let head = qualifiedLookUp(c, n[0], {checkAmbiguity, checkUndeclared}) - if head.kind in {skType}: - var toBind = initIntSet() - var preprocessed = semGenericStmt(c, n, {}, toBind) - return makeTypeFromExpr(c, preprocessed) + of nkDotExpr: + var typeExpr = semExpr(c, n) + if typeExpr.typ.kind != tyTypeDesc: + localError(n.info, errTypeExpected) + return errorType(c) + result = typeExpr.typ.base + if result.isMetaType: + var toBind = initIntSet() + var preprocessed = semGenericStmt(c, n, {}, toBind) + return makeTypeFromExpr(c, preprocessed) + of nkIdent, nkAccQuoted: var s = semTypeIdent(c, n) if s.typ == nil: if s.kind != skError: localError(n.info, errTypeExpected) @@ -1262,6 +1294,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = if typ == nil: typ = newTypeS(tyGenericParam, c) + if father == nil: typ.flags.incl tfWildcard typ.flags.incl tfGenericTypeParam @@ -1272,8 +1305,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = # type for each generic param. the index # of the parameter will be stored in the # attached symbol. - var s = case finalType.kind - of tyStatic: + var s = if finalType.kind == tyStatic or tfWildcard in typ.flags: newSymG(skGenericParam, a.sons[j], c).linkTo(finalType) else: newSymG(skType, a.sons[j], c).linkTo(finalType) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 80e2aa284..4a8a463f5 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -80,7 +80,7 @@ type proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym -proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode +proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode): PNode template checkMetaInvariants(cl: TReplTypeVars, t: PType) = when false: @@ -96,8 +96,11 @@ proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = checkMetaInvariants(cl, result) proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode = + let t = replaceTypeVarsT(cl, n.typ) + if t != nil and t.kind == tyStatic and t.n != nil: + return t.n result = copyNode(n) - result.typ = replaceTypeVarsT(cl, n.typ) + result.typ = t if result.kind == nkSym: result.sym = replaceTypeVarsS(cl, n.sym) let isCall = result.kind in nkCallKinds for i in 0 .. <n.safeLen: @@ -197,10 +200,10 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym = result = copySym(s, false) incl(result.flags, sfFromGeneric) idTablePut(cl.symMap, s, result) - result.typ = replaceTypeVarsT(cl, s.typ) result.owner = s.owner + result.typ = replaceTypeVarsT(cl, s.typ) result.ast = replaceTypeVarsN(cl, s.ast) - + proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType = result = PType(idTableGet(cl.typeMap, t)) if result == nil: @@ -210,7 +213,7 @@ proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType = elif result.kind == tyGenericParam and not cl.allowMetaTypes: internalError(cl.info, "substitution with generic parameter") -proc instCopyType(cl: var TReplTypeVars, t: PType): PType = +proc instCopyType*(cl: var TReplTypeVars, t: PType): PType = # XXX: relying on allowMetaTypes is a kludge result = copyType(t, t.owner, cl.allowMetaTypes) result.flags.incl tfFromGeneric @@ -281,7 +284,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = rawAddSon(result, newbody) checkPartialConstructedType(cl.info, newbody) -proc eraseVoidParams(t: PType) = +proc eraseVoidParams*(t: PType) = if t.sons[0] != nil and t.sons[0].kind == tyEmpty: t.sons[0] = nil @@ -298,7 +301,7 @@ proc eraseVoidParams(t: PType) = setLen t.n.sons, pos return -proc skipIntLiteralParams(t: PType) = +proc skipIntLiteralParams*(t: PType) = for i in 0 .. <t.sonsLen: let p = t.sons[i] if p == nil: continue @@ -394,21 +397,11 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = propagateToOwner(result, result.sons[i]) result.n = replaceTypeVarsN(cl, result.n) - - # XXX: This is not really needed? - # if result.kind in GenericTypes: - # localError(cl.info, errCannotInstantiateX, typeToString(t, preferName)) - + case result.kind of tyArray: let idx = result.sons[0] - if idx.kind == tyStatic: - if idx.n == nil: - let lookup = lookupTypeVar(cl, idx) - internalAssert lookup != nil - idx.n = lookup.n - - result.sons[0] = makeRangeType(cl.c, 0, idx.n.intVal - 1, idx.n.info) + internalAssert idx.kind != tyStatic of tyObject, tyTuple: propagateFieldFlags(result, result.n) @@ -419,7 +412,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = else: discard -proc initTypeVars(p: PContext, pt: TIdTable, info: TLineInfo): TReplTypeVars = +proc initTypeVars*(p: PContext, pt: TIdTable, info: TLineInfo): TReplTypeVars = initIdTable(result.symMap) copyIdTable(result.typeMap, pt) initIdTable(result.localCache) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 19f10def8..b9fb0c957 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -12,7 +12,7 @@ import intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst, - magicsys, condsyms, idents, lexer, options, parampatterns, strutils + magicsys, condsyms, idents, lexer, options, parampatterns, strutils, trees when not defined(noDocgen): import docgen @@ -40,6 +40,8 @@ type proxyMatch*: bool # to prevent instantiations genericConverter*: bool # true if a generic converter needs to # be instantiated + coerceDistincts*: bool # this is an explicit coercion that can strip away + # a distrinct type typedescMatched: bool inheritancePenalty: int # to prefer closest father object type errors*: seq[string] # additional clarifications to be displayed to the @@ -114,6 +116,9 @@ proc newCandidate*(ctx: PContext, callee: PSym, binding: PNode, calleeScope = -1): TCandidate = initCandidate(ctx, result, callee, binding, calleeScope) +proc newCandidate*(ctx: PContext, callee: PType): TCandidate = + initCandidate(ctx, result, callee) + proc copyCandidate(a: var TCandidate, b: TCandidate) = a.c = b.c a.exactMatches = b.exactMatches @@ -460,7 +465,8 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, if param.kind == nkVarTy: dummyName = param[0] - dummyType = makeVarType(c, a) + dummyType = if a.kind != tyVar: makeVarType(c, a) + else: a else: dummyName = param dummyType = a @@ -470,7 +476,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, dummyParam.typ = dummyType addDecl(c, dummyParam) - var checkedBody = c.semTryExpr(c, copyTree(body.n[3]), bufferErrors = false) + var checkedBody = c.semTryExpr(c, body.n[3].copyTree, bufferErrors = false) m.errors = bufferedMsgs clearBufferedMsgs() if checkedBody == nil: return isNone @@ -485,6 +491,23 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, return isGeneric +proc shouldSkipDistinct(rules: PNode, callIdent: PIdent): bool = + if rules.kind == nkWith: + for r in rules: + if r.considerAcc == callIdent: return true + return false + else: + for r in rules: + if r.considerAcc == callIdent: return false + return true + +proc maybeSkipDistinct(t: PType, callee: PSym): PType = + if t != nil and t.kind == tyDistinct and t.n != nil and + shouldSkipDistinct(t.n, callee.name): + result = t.base + else: + result = t + proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # typeRel can be used to establish various relationships between types: # @@ -512,7 +535,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = assert(aOrig != nil) # var and static arguments match regular modifier-free types - let a = aOrig.skipTypes({tyStatic, tyVar}) + let a = aOrig.skipTypes({tyStatic, tyVar}).maybeSkipDistinct(c.calleeSym) + # XXX: Theoretically, maybeSkipDistinct could be called before we even + # start the param matching process. This could be done in `prepareOperand` + # for example, but unfortunately `prepareOperand` is not called in certain + # situation when nkDotExpr are rotated to nkDotCalls if a.kind == tyGenericInst and skipTypes(f, {tyVar}).kind notin { @@ -522,8 +549,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = template bindingRet(res) = when res == isGeneric: - let bound = aOrig.skipTypes({tyRange}).skipIntLit - put(c.bindings, f, bound) + if doBind: + let bound = aOrig.skipTypes({tyRange}).skipIntLit + if doBind: put(c.bindings, f, bound) return res template considerPreviousT(body: stmt) {.immediate.} = @@ -620,8 +648,24 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = else: fRange = prev result = typeRel(c, f.sons[1], a.sons[1]) - if result < isGeneric: result = isNone - elif lengthOrd(fRange) != lengthOrd(a): result = isNone + if result < isGeneric: + result = isNone + elif tfUnresolved in fRange.flags and + rangeHasStaticIf(fRange): + # This is a range from an array instantiated with a generic + # static param. We must extract the static param here and bind + # it to the size of the currently supplied array. + var + rangeStaticT = fRange.getStaticTypeFromRange + replacementT = newTypeWithSons(c.c, tyStatic, @[tyInt.getSysType]) + inputUpperBound = a.sons[0].n[1].intVal + # we must correct for the off-by-one discrepancy between + # ranges and static params: + replacementT.n = newIntNode(nkIntLit, inputUpperBound + 1) + put(c.bindings, rangeStaticT, replacementT) + result = isGeneric + elif lengthOrd(fRange) != lengthOrd(a): + result = isNone else: discard of tyOpenArray, tyVarargs: case a.kind @@ -683,6 +727,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = isSubtype of tyDistinct: if (a.kind == tyDistinct) and sameDistinctTypes(f, a): result = isEqual + elif c.coerceDistincts: result = typeRel(c, f.base, a) of tySet: if a.kind == tySet: if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): @@ -845,7 +890,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyUserTypeClass, tyUserTypeClassInst: considerPreviousT: - result = matchUserTypeClass(c.c, c, f, a) + result = matchUserTypeClass(c.c, c, f, aOrig) if result == isGeneric: put(c.bindings, f, a) @@ -860,14 +905,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyGenericParam: var x = PType(idTableGet(c.bindings, f)) if x == nil: - if c.calleeSym != nil and c.calleeSym.kind == skType and + if c.callee.kind == tyGenericBody and f.kind == tyGenericParam and not c.typedescMatched: # XXX: The fact that generic types currently use tyGenericParam for # their parameters is really a misnomer. tyGenericParam means "match # any value" and what we need is "match any type", which can be encoded # by a tyTypeDesc params. Unfortunately, this requires more substantial # changes in semtypinst and elsewhere. - if a.kind == tyTypeDesc: + if tfWildcard in a.flags: + result = isGeneric + elif a.kind == tyTypeDesc: if f.sonsLen == 0: result = isGeneric else: @@ -883,11 +930,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = isGeneric if result == isGeneric: - var concrete = concreteType(c, a) - if concrete == nil: - result = isNone + var concrete = a + if tfWildcard in a.flags: + a.sym.kind = skType + a.flags.excl tfWildcard else: - if doBind: put(c.bindings, f, concrete) + concrete = concreteType(c, a) + if concrete == nil: + return isNone + if doBind: + put(c.bindings, f, concrete) elif a.kind == tyEmpty: result = isGeneric elif x.kind == tyGenericParam: @@ -937,8 +989,25 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyProxy: result = isEqual + + of tyFromExpr: + # fix the expression, so it contains the already instantiated types + let instantiated = replaceTypesInBody(c.c, c.bindings, f.n) + let reevaluted = c.c.semExpr(c.c, instantiated) + case reevaluted.typ.kind + of tyTypeDesc: + result = typeRel(c, a, reevaluted.typ.base) + of tyStatic: + result = typeRel(c, a, reevaluted.typ.base) + if result != isNone and reevaluted.typ.n != nil: + if not exprStructuralEquivalent(aOrig.n, reevaluted.typ.n): + result = isNone + else: + localError(f.n.info, errTypeExpected) + result = isNone - else: internalAssert false + else: + internalAssert false proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation = var m: TCandidate @@ -1025,17 +1094,31 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, arg = argSemantized argType = argType c = m.c - + if tfHasStatic in fMaybeStatic.flags: # XXX: When implicit statics are the default # this will be done earlier - we just have to # make sure that static types enter here - var evaluated = c.semTryConstExpr(c, arg) - if evaluated != nil: - arg.typ = newTypeS(tyStatic, c) - arg.typ.sons = @[evaluated.typ] - arg.typ.n = evaluated - argType = arg.typ + + # XXX: weaken tyGenericParam and call it tyGenericPlaceholder + # and finally start using tyTypedesc for generic types properly. + if argType.kind == tyGenericParam and tfWildcard in argType.flags: + argType.assignType(f) + # put(m.bindings, f, argType) + return argSemantized + + if argType.kind == tyStatic: + if m.callee.kind == tyGenericBody: + result = newNodeI(nkType, argOrig.info) + result.typ = makeTypeFromExpr(c, arg) + return + else: + var evaluated = c.semTryConstExpr(c, arg) + if evaluated != nil: + arg.typ = newTypeS(tyStatic, c) + arg.typ.sons = @[evaluated.typ] + arg.typ.n = evaluated + argType = arg.typ var a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc, tyFieldAccessor}) diff --git a/compiler/types.nim b/compiler/types.nim index 8cf91da8b..ae31d24de 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -436,6 +436,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = of tyStatic: internalAssert t.len > 0 result = "static[" & typeToString(t.sons[0]) & "]" + if t.n != nil: result.add "(" & renderTree(t.n) & ")" of tyUserTypeClass: internalAssert t.sym != nil and t.sym.owner != nil return t.sym.owner.name.s @@ -829,6 +830,12 @@ proc sameChildrenAux(a, b: PType, c: var TSameTypeClosure): bool = result = sameTypeOrNilAux(a.sons[i], b.sons[i], c) if not result: return +proc isGenericAlias*(t: PType): bool = + return t.kind == tyGenericInst and t.lastSon.kind == tyGenericInst + +proc skipGenericAlias*(t: PType): PType = + return if t.isGenericAlias: t.lastSon else: t + proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = template cycleCheck() = # believe it or not, the direct check for ``containsOrIncl(c, a, b)`` @@ -859,6 +866,19 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = of dcEqOrDistinctOf: while a.kind == tyDistinct: a = a.sons[0] if a.kind != b.kind: return false + + if x.kind == tyGenericInst: + let + lhs = x.skipGenericAlias + rhs = y.skipGenericAlias + if rhs.kind != tyGenericInst or lhs.base != rhs.base: + return false + for i in 1 .. lhs.len - 2: + let ff = rhs.sons[i] + let aa = lhs.sons[i] + if not sameTypeAux(ff, aa, c): return false + return true + case a.kind of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString, tyInt..tyBigNum, tyStmt, tyExpr: @@ -883,8 +903,6 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = of tyTuple: cycleCheck() result = sameTuple(a, b, c) and sameFlags(a, b) - of tyGenericInst: - result = sameTypeAux(lastSon(a), lastSon(b), c) of tyTypeDesc: if c.cmp == dcEqIgnoreDistinct: result = false elif ExactTypeDescValues in c.flags: @@ -911,6 +929,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = result = sameTypeOrNilAux(a.sons[0], b.sons[0], c) and sameValue(a.n.sons[0], b.n.sons[0]) and sameValue(a.n.sons[1], b.n.sons[1]) + of tyGenericInst: discard of tyNone: result = false proc sameBackendType*(x, y: PType): bool = @@ -1004,12 +1023,6 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]], a = a.sons[i] result = a.kind == last -proc isGenericAlias*(t: PType): bool = - return t.kind == tyGenericInst and t.lastSon.kind == tyGenericInst - -proc skipGenericAlias*(t: PType): PType = - return if t.isGenericAlias: t.lastSon else: t - proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind, flags: TTypeAllowedFlags = {}): bool = assert(kind in {skVar, skLet, skConst, skParam, skResult}) @@ -1255,15 +1268,18 @@ proc getSize(typ: PType): BiggestInt = if result < 0: internalError("getSize: " & $typ.kind) proc containsGenericTypeIter(t: PType, closure: PObject): bool = - if t.kind in GenericTypes + tyTypeClasses + {tyFromExpr}: - return true + if t.kind == tyStatic: + return t.n == nil if t.kind == tyTypeDesc: if t.base.kind == tyNone: return true if containsGenericTypeIter(t.base, closure): return true return false - - return t.kind == tyStatic and t.n == nil + + if t.kind in GenericTypes + tyTypeClasses + {tyFromExpr}: + return true + + return false proc containsGenericType*(t: PType): bool = result = iterOverType(t, containsGenericTypeIter, nil) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 5e51ecf4c..89845c198 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1570,7 +1570,7 @@ proc genProc(c: PCtx; s: PSym): int = c.gABC(body, opcEof, eofInstr.regA) c.optimizeJumps(result) s.offset = c.prc.maxSlots - #if s.name.s == "xmlConstructor": + #if s.name.s == "foo": # echo renderTree(body) # c.echoCode(result) c.prc = oldPrc diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index f4a0c59f4..9fdb3bac6 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -72,7 +72,7 @@ type wPrivate, wProtected, wPublic, wRegister, wReinterpret_cast, wShort, wSigned, wSizeof, wStatic_cast, wStruct, wSwitch, wThis, wThrow, wTrue, wTypedef, wTypeid, wTypename, - wUnion, wUnsigned, wVirtual, wVoid, wVolatile, wWchar_t, + wUnion, wPacked, wUnsigned, wVirtual, wVoid, wVolatile, wWchar_t, wAlignas, wAlignof, wConstexpr, wDecltype, wNullptr, wNoexcept, wThread_local, wStatic_assert, wChar16_t, wChar32_t, @@ -155,7 +155,7 @@ const "private", "protected", "public", "register", "reinterpret_cast", "short", "signed", "sizeof", "static_cast", "struct", "switch", "this", "throw", "true", "typedef", "typeid", - "typename", "union", "unsigned", "virtual", "void", "volatile", + "typename", "union", "packed", "unsigned", "virtual", "void", "volatile", "wchar_t", "alignas", "alignof", "constexpr", "decltype", "nullptr", "noexcept", |