diff options
author | metagn <metagngn@gmail.com> | 2023-04-22 10:11:56 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-22 09:11:56 +0200 |
commit | 63d29ddd6980ee9f89673c454c15da52e2984283 (patch) | |
tree | 86af58c42f415e6e7dc4d1c10de9d6f633127477 /compiler | |
parent | c136ebf1ed0812f019895acc5aeeda8fde75ed00 (diff) | |
download | Nim-63d29ddd6980ee9f89673c454c15da52e2984283.tar.gz |
alias syntax fixes, improvements and tests (#21671)
* alias syntax fixes, improvements and tests * even better, cannot use alias syntax with generics * more type tests, improve comment * fix again * consistent error message + make t5167_5 work * more comments, remove {.noalias.}
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/ast.nim | 11 | ||||
-rw-r--r-- | compiler/semexprs.nim | 20 | ||||
-rw-r--r-- | compiler/semgnrc.nim | 47 | ||||
-rw-r--r-- | compiler/semstmts.nim | 15 | ||||
-rw-r--r-- | compiler/semtempl.nim | 8 | ||||
-rw-r--r-- | compiler/semtypes.nim | 129 |
6 files changed, 118 insertions, 112 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index b5306c423..8aa4ca6b1 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -303,6 +303,8 @@ type 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' + # for templates and macros, means cannot be called + # as a lone symbol (cannot use alias syntax) sfEffectsDelayed # an 'effectsDelayed' parameter sfGeneratedType # A anonymous generic type that is generated by the compiler for # objects that do not have generic parameters in case one of the @@ -1920,15 +1922,6 @@ proc isRunnableExamples*(n: PNode): bool = 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 diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index e5d5498a8..46d83c0e7 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -57,7 +57,14 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # XXX tyGenericInst here? if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}): #and tfUnresolved in result.typ.flags: - localError(c.config, n.info, errProcHasNoConcreteType % n.renderTree) + let owner = result.typ.owner + let err = + # consistent error message with evaltempl/semMacroExpr + if owner != nil and owner.kind in {skTemplate, skMacro}: + errMissingGenericParamsForTemplate % n.renderTree + else: + errProcHasNoConcreteType % n.renderTree + localError(c.config, n.info, err) if result.typ.kind in {tyVar, tyLent}: result = newDeref(result) elif {efWantStmt, efAllowStmt} * flags != {}: result.typ = newTypeS(tyVoid, c) @@ -1259,6 +1266,7 @@ proc readTypeParameter(c: PContext, typ: PType, return nil proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = + assert n.kind in nkIdentKinds + {nkDotExpr} let s = getGenSym(c, sym) case s.kind of skConst: @@ -1293,9 +1301,8 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = else: result = newSymNode(s, n.info) of skMacro, skTemplate: - if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or - (n.kind notin nkCallKinds and s.requiredParams > 0) or - sfCustomPragma in sym.flags: + # check if we cannot use alias syntax (no required args or generic params) + if sfNoalias in s.flags: let info = getCallLineInfo(n) markUsed(c, info, s) onUse(info, s) @@ -1588,9 +1595,8 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = result.add(x[0]) return checkMinSonsLen(n, 2, c.config) - # make sure we don't evaluate generic macros/templates - n[0] = semExprWithType(c, n[0], - {efNoEvaluateGeneric}) + # signal that generic parameters may be applied after + n[0] = semExprWithType(c, n[0], {efNoEvaluateGeneric}) var arr = skipTypes(n[0].typ, {tyGenericInst, tyUserTypeClassInst, tyOwned, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink}) if arr.kind == tyStatic: diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index fa37af850..695f8a01d 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -50,13 +50,6 @@ proc semGenericStmtScope(c: PContext, n: PNode, result = semGenericStmt(c, n, flags, ctx) closeScope(c) -template macroToExpand(s): untyped = - s.kind in {skMacro, skTemplate} and (s.typ.len == 1 or sfAllUntyped in s.flags) - -template macroToExpandSym(s): untyped = - sfCustomPragma notin s.flags and s.kind in {skMacro, skTemplate} and - (s.typ.len == 1) and not fromDotExpr - template isMixedIn(sym): bool = let s = sym s.name.id in ctx.toMixin or (withinConcept in flags and @@ -74,19 +67,14 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, result = n of skProc, skFunc, skMethod, skIterator, skConverter, skModule: result = symChoice(c, n, s, scOpen) - of skTemplate: - if macroToExpandSym(s): + of skTemplate, skMacro: + # alias syntax, see semSym for skTemplate, skMacro + if sfNoalias notin s.flags and not fromDotExpr: onUse(n.info, s) - result = semTemplateExpr(c, n, s, {efNoSemCheck}) - c.friendModules.add(s.owner.getModule) - result = semGenericStmt(c, result, {}, ctx) - discard c.friendModules.pop() - else: - result = symChoice(c, n, s, scOpen) - of skMacro: - if macroToExpandSym(s): - onUse(n.info, s) - result = semMacroExpr(c, n, n, s, {efNoSemCheck}) + case s.kind + of skTemplate: result = semTemplateExpr(c, n, s, {efNoSemCheck}) + of skMacro: result = semMacroExpr(c, n, n, s, {efNoSemCheck}) + else: discard # unreachable c.friendModules.add(s.owner.getModule) result = semGenericStmt(c, result, {}, ctx) discard c.friendModules.pop() @@ -245,21 +233,14 @@ proc semGenericStmt(c: PContext, n: PNode, else: scOpen let sc = symChoice(c, fn, s, whichChoice) case s.kind - of skMacro: - if macroToExpand(s) and sc.safeLen <= 1: - onUse(fn.info, s) - result = semMacroExpr(c, n, n, s, {efNoSemCheck}) - c.friendModules.add(s.owner.getModule) - result = semGenericStmt(c, result, flags, ctx) - discard c.friendModules.pop() - else: - n[0] = sc - result = n - mixinContext = true - of skTemplate: - if macroToExpand(s) and sc.safeLen <= 1: + of skMacro, skTemplate: + # unambiguous macros/templates are expanded if all params are untyped + if sfAllUntyped in s.flags and sc.safeLen <= 1: onUse(fn.info, s) - result = semTemplateExpr(c, n, s, {efNoSemCheck}) + case s.kind + of skMacro: result = semMacroExpr(c, n, n, s, {efNoSemCheck}) + of skTemplate: result = semTemplateExpr(c, n, s, {efNoSemCheck}) + else: discard # unreachable c.friendModules.add(s.owner.getModule) result = semGenericStmt(c, result, flags, ctx) discard c.friendModules.pop() diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 3701e09f5..20fb12d71 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -682,7 +682,14 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = localError(c.config, def.info, errCannotInferTypeOfTheLiteral % typ.kind.toHumanStr) elif typ.kind == tyProc and def.kind == nkSym and isGenericRoutine(def.sym.ast): # tfUnresolved in typ.flags: - localError(c.config, def.info, errProcHasNoConcreteType % def.renderTree) + let owner = typ.owner + let err = + # consistent error message with evaltempl/semMacroExpr + if owner != nil and owner.kind in {skTemplate, skMacro}: + errMissingGenericParamsForTemplate % def.renderTree + else: + errProcHasNoConcreteType % def.renderTree + localError(c.config, def.info, err) when false: # XXX This typing rule is neither documented nor complete enough to # justify it. Instead use the newer 'unowned x' until we figured out @@ -2328,10 +2335,16 @@ proc semMacroDef(c: PContext, n: PNode): PNode = var s = result[namePos].sym var t = s.typ var allUntyped = true + var requiresParams = false for i in 1..<t.n.len: let param = t.n[i].sym if param.typ.kind != tyUntyped: allUntyped = false + # no default value, parameters required in call + if param.ast == nil: requiresParams = true if allUntyped: incl(s.flags, sfAllUntyped) + if requiresParams or n[genericParamsPos].kind != nkEmpty: + # macro cannot be called with alias syntax + incl(s.flags, sfNoalias) if n[bodyPos].kind == nkEmpty: localError(c.config, n.info, errImplOfXexpected % s.name.s) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 3b2f2dd9e..6c01be7ea 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -635,6 +635,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = setGenericParamsMisc(c, n) # process parameters: var allUntyped = true + var requiresParams = false if n[paramsPos].kind != nkEmpty: semParamList(c, n[paramsPos], n[genericParamsPos], s) # a template's parameters are not gensym'ed even if that was originally the @@ -646,6 +647,8 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = param.flags.incl sfTemplateParam param.flags.excl sfGenSym if param.typ.kind != tyUntyped: allUntyped = false + # no default value, parameters required in call + if param.ast == nil: requiresParams = true else: s.typ = newTypeS(tyProc, c) # XXX why do we need tyTyped as a return type again? @@ -657,6 +660,11 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = n[genericParamsPos] = n[miscPos][1] n[miscPos] = c.graph.emptyNode if allUntyped: incl(s.flags, sfAllUntyped) + if requiresParams or + n[bodyPos].kind == nkEmpty or + n[genericParamsPos].kind != nkEmpty: + # template cannot be called with alias syntax + incl(s.flags, sfNoalias) if n[patternPos].kind != nkEmpty: n[patternPos] = semPattern(c, n[patternPos], s) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index cd742c90a..8f34da72d 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -416,68 +416,6 @@ proc semOrdinal(c: PContext, n: PNode, prev: PType): PType = localError(c.config, n.info, errXExpectsOneTypeParam % "ordinal") result = newOrPrevType(tyError, prev, c) -proc semTypeIdent(c: PContext, n: PNode): PSym = - if n.kind == nkSym: - result = getGenSym(c, n.sym) - else: - result = pickSym(c, n, {skType, skGenericParam, skParam}) - if result.isNil: - result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared}) - if result != nil: - markUsed(c, n.info, result) - onUse(n.info, result) - - if result.kind == skParam and result.typ.kind == tyTypeDesc: - # This is a typedesc param. is it already bound? - # it's not bound when it's used multiple times in the - # proc signature for example - if c.inGenericInst > 0: - let bound = result.typ[0].sym - if bound != nil: return bound - return result - if result.typ.sym == nil: - localError(c.config, n.info, errTypeExpected) - return errorSym(c, n) - result = result.typ.sym.copySym(nextSymId c.idgen) - result.typ = exactReplica(result.typ) - result.typ.flags.incl tfUnresolved - - if result.kind == skGenericParam: - if result.typ.kind == tyGenericParam and result.typ.len == 0 and - tfWildcard in result.typ.flags: - # collapse the wild-card param to a type - result.transitionGenericParamToType() - result.typ.flags.excl tfWildcard - return - else: - localError(c.config, n.info, errTypeExpected) - return errorSym(c, n) - if result.kind != skType and result.magic notin {mStatic, mType, mTypeOf}: - # this implements the wanted ``var v: V, x: V`` feature ... - var ov: TOverloadIter - var amb = initOverloadIter(ov, c, n) - while amb != nil and amb.kind != skType: - amb = nextOverloadIter(ov, c, n) - if amb != nil: result = amb - else: - if result.kind != skError: localError(c.config, n.info, errTypeExpected) - return errorSym(c, n) - if result.typ.kind != tyGenericParam: - # XXX get rid of this hack! - var oldInfo = n.info - when defined(useNodeIds): - let oldId = n.id - reset(n[]) - when defined(useNodeIds): - n.id = oldId - n.transitionNoneToSym() - n.sym = result - n.info = oldInfo - n.typ = result.typ - else: - localError(c.config, n.info, "identifier expected") - result = errorSym(c, n) - proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType = if n.len == 0: localError(c.config, n.info, errTypeExpected) @@ -1801,6 +1739,73 @@ proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType = fixupTypeOf(c, prev, t) result = t.typ +proc semTypeIdent(c: PContext, n: PNode): PSym = + if n.kind == nkSym: + result = getGenSym(c, n.sym) + else: + result = pickSym(c, n, {skType, skGenericParam, skParam}) + if result.isNil: + result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared}) + if result != nil: + markUsed(c, n.info, result) + onUse(n.info, result) + + # alias syntax, see semSym for skTemplate, skMacro + if result.kind in {skTemplate, skMacro} and sfNoalias notin result.flags: + let t = semTypeExpr(c, n, nil) + result = symFromType(c, t, n.info) + + if result.kind == skParam and result.typ.kind == tyTypeDesc: + # This is a typedesc param. is it already bound? + # it's not bound when it's used multiple times in the + # proc signature for example + if c.inGenericInst > 0: + let bound = result.typ[0].sym + if bound != nil: return bound + return result + if result.typ.sym == nil: + localError(c.config, n.info, errTypeExpected) + return errorSym(c, n) + result = result.typ.sym.copySym(nextSymId c.idgen) + result.typ = exactReplica(result.typ) + result.typ.flags.incl tfUnresolved + + if result.kind == skGenericParam: + if result.typ.kind == tyGenericParam and result.typ.len == 0 and + tfWildcard in result.typ.flags: + # collapse the wild-card param to a type + result.transitionGenericParamToType() + result.typ.flags.excl tfWildcard + return + else: + localError(c.config, n.info, errTypeExpected) + return errorSym(c, n) + if result.kind != skType and result.magic notin {mStatic, mType, mTypeOf}: + # this implements the wanted ``var v: V, x: V`` feature ... + var ov: TOverloadIter + var amb = initOverloadIter(ov, c, n) + while amb != nil and amb.kind != skType: + amb = nextOverloadIter(ov, c, n) + if amb != nil: result = amb + else: + if result.kind != skError: localError(c.config, n.info, errTypeExpected) + return errorSym(c, n) + if result.typ.kind != tyGenericParam: + # XXX get rid of this hack! + var oldInfo = n.info + when defined(useNodeIds): + let oldId = n.id + reset(n[]) + when defined(useNodeIds): + n.id = oldId + n.transitionNoneToSym() + n.sym = result + n.info = oldInfo + n.typ = result.typ + else: + localError(c.config, n.info, "identifier expected") + result = errorSym(c, n) + proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = nil inc c.inTypeContext |