mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-11 22:08:54 +00:00
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.}
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
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 = 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:
|
||||
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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -453,28 +453,25 @@ Assuming `foo` is a macro or a template, this is roughly equivalent to:
|
||||
```
|
||||
|
||||
|
||||
Symbols as template/macro calls
|
||||
===============================
|
||||
Symbols as template/macro calls (alias syntax)
|
||||
==============================================
|
||||
|
||||
Templates and macros that take no arguments can be called as lone symbols,
|
||||
i.e. without parentheses. This is useful for repeated uses of complex
|
||||
expressions that cannot conveniently be represented as runtime values.
|
||||
Templates and macros that have no generic parameters and no required arguments
|
||||
can be called as lone symbols, i.e. without parentheses. This is useful for
|
||||
repeated uses of complex expressions that cannot conveniently be represented
|
||||
as runtime values.
|
||||
|
||||
```nim
|
||||
type Foo = object
|
||||
bar: int
|
||||
|
||||
var foo = Foo(bar: 10)
|
||||
template bar: untyped = foo.bar
|
||||
template bar: int = foo.bar
|
||||
assert bar == 10
|
||||
bar = 15
|
||||
assert bar == 15
|
||||
```
|
||||
|
||||
In the future, this may require more specific information on template or macro
|
||||
signatures to be used. Specializations for some applications of this may also
|
||||
be introduced to guarantee consistency and circumvent bugs.
|
||||
|
||||
|
||||
Not nil annotation
|
||||
==================
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
discard """
|
||||
cmd: "nim check --mm:refc $file"
|
||||
errormsg: "'t' has unspecified generic parameters"
|
||||
nimout: '''
|
||||
t5167_5.nim(10, 16) Error: expression 'system' has no type (or is ambiguous)
|
||||
t5167_5.nim(21, 9) Error: 't' has unspecified generic parameters
|
||||
'''
|
||||
"""
|
||||
# issue #11942
|
||||
discard newSeq[system]()
|
||||
discard newSeq[system]() #[tt.Error
|
||||
^ expression 'system' has no type (or is ambiguous)]#
|
||||
|
||||
# issue #5167
|
||||
template t[B]() =
|
||||
@@ -18,8 +14,12 @@ macro m[T]: untyped = nil
|
||||
proc bar(x: proc (x: int)) =
|
||||
echo "bar"
|
||||
|
||||
let x = t
|
||||
bar t
|
||||
let x = t #[tt.Error
|
||||
^ 't' has unspecified generic parameters]#
|
||||
bar t #[tt.Error
|
||||
^ 't' has unspecified generic parameters]#
|
||||
|
||||
let y = m
|
||||
bar m
|
||||
let y = m #[tt.Error
|
||||
^ 'm' has unspecified generic parameters]#
|
||||
bar m #[tt.Error
|
||||
^ 'm' has unspecified generic parameters]#
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
discard """
|
||||
action: compile
|
||||
"""
|
||||
|
||||
template test: bool = true
|
||||
|
||||
# compiles:
|
||||
if not test:
|
||||
echo "wtf"
|
||||
|
||||
# does not compile:
|
||||
template x =
|
||||
if not test:
|
||||
echo "wtf"
|
||||
|
||||
x
|
||||
63
tests/template/taliassyntax.nim
Normal file
63
tests/template/taliassyntax.nim
Normal file
@@ -0,0 +1,63 @@
|
||||
type Foo = object
|
||||
bar: int
|
||||
|
||||
var foo = Foo(bar: 10)
|
||||
template bar: int = foo.bar
|
||||
doAssert bar == 10
|
||||
bar = 15
|
||||
doAssert bar == 15
|
||||
var foo2 = Foo(bar: -10)
|
||||
doAssert bar == 15
|
||||
# works in generics
|
||||
proc genericProc[T](x: T): string =
|
||||
$(x, bar)
|
||||
doAssert genericProc(true) == "(true, 15)"
|
||||
# redefine
|
||||
template bar: int {.redefine.} = foo2.bar
|
||||
doAssert bar == -10
|
||||
|
||||
block: # subscript
|
||||
var bazVal = @[1, 2, 3]
|
||||
template baz: seq[int] = bazVal
|
||||
doAssert baz[1] == 2
|
||||
proc genericProc2[T](x: T): string =
|
||||
result = $(x, baz[1])
|
||||
baz[1] = 7
|
||||
doAssert genericProc2(true) == "(true, 2)"
|
||||
doAssert baz[1] == 7
|
||||
baz[1] = 14
|
||||
doAssert baz[1] == 14
|
||||
|
||||
block: # type alias
|
||||
template Int2: untyped = int
|
||||
let x: Int2 = 123
|
||||
proc generic[T](): string =
|
||||
template U: untyped = T
|
||||
var x: U
|
||||
result = $typeof(x)
|
||||
doAssert result == $U
|
||||
doAssert result == $T
|
||||
doAssert generic[int]() == "int"
|
||||
doAssert generic[Int2]() == "int"
|
||||
doAssert generic[string]() == "string"
|
||||
doAssert generic[seq[int]]() == "seq[int]"
|
||||
doAssert generic[seq[Int2]]() == "seq[int]"
|
||||
discard generic[123]()
|
||||
proc genericStatic[X; T: static[X]](): string =
|
||||
template U: untyped = T
|
||||
result = $U
|
||||
doAssert result == $T
|
||||
doAssert genericStatic[int, 123]() == "123"
|
||||
doAssert genericStatic[Int2, 123]() == "123"
|
||||
doAssert genericStatic[(string, bool), ("a", true)]() == "(\"a\", true)"
|
||||
|
||||
block: # issue #13515
|
||||
template test: bool = true
|
||||
# compiles:
|
||||
if not test:
|
||||
doAssert false
|
||||
# does not compile:
|
||||
template x =
|
||||
if not test:
|
||||
doAssert false
|
||||
x
|
||||
28
tests/template/taliassyntaxerrors.nim
Normal file
28
tests/template/taliassyntaxerrors.nim
Normal file
@@ -0,0 +1,28 @@
|
||||
discard """
|
||||
cmd: "nim check --hints:off $file"
|
||||
"""
|
||||
|
||||
block: # with params
|
||||
type Foo = object
|
||||
bar: int
|
||||
|
||||
var foo = Foo(bar: 10)
|
||||
template bar(x: int): int = x + foo.bar
|
||||
let a = bar #[tt.Error
|
||||
^ invalid type: 'template (x: int): int' for let. Did you mean to call the template with '()'?]#
|
||||
bar = 15 #[tt.Error
|
||||
^ 'bar' cannot be assigned to]#
|
||||
|
||||
block: # generic template
|
||||
type Foo = object
|
||||
bar: int
|
||||
|
||||
var foo = Foo(bar: 10)
|
||||
template bar[T]: T = T(foo.bar)
|
||||
let a = bar #[tt.Error
|
||||
^ invalid type: 'template (): T' for let. Did you mean to call the template with '()'?; tt.Error
|
||||
^ 'bar' has unspecified generic parameters]#
|
||||
let b = bar[float]()
|
||||
doAssert b == 10.0
|
||||
bar = 15 #[tt.Error
|
||||
^ 'bar' cannot be assigned to]#
|
||||
Reference in New Issue
Block a user